1#![warn(rust_2018_idioms)]
2#![warn(future_incompatible)]
3#![forbid(missing_docs)]
4#![forbid(unsafe_code)]
5
6use curve25519_dalek::{EdwardsPoint, Scalar, edwards::CompressedEdwardsY};
9use ed25519_dalek::pkcs8::{DecodePrivateKey, DecodePublicKey, EncodePrivateKey, EncodePublicKey};
10use ed25519_dalek::{Digest, Sha512};
11use ed25519_dalek::{Signer, SigningKey, VerifyingKey};
12use hex_literal::hex;
13use thiserror::Error;
14use zeroize::ZeroizeOnDrop;
15
16pub use ic_principal::Principal as CanisterId;
17
18pub const SIGNATURE_BYTES: usize = 64;
20
21#[derive(Clone, Debug, Error)]
23pub enum PrivateKeyDecodingError {
24 #[error("The outer PEM encoding is invalid: {0}")]
26 InvalidPemEncoding(String),
27 #[error("The PEM label was not the expected value: {0}")]
29 UnexpectedPemLabel(String),
30 #[error("The private key seems invalid in some way: {0}")]
32 InvalidKeyEncoding(String),
33}
34
35#[derive(Clone, Eq, PartialEq, ZeroizeOnDrop)]
37pub struct PrivateKey {
38 sk: SigningKey,
39}
40
41impl std::fmt::Debug for PrivateKey {
42 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43 f.debug_struct("PrivateKey")
44 .field("public_key", &self.public_key().serialize_raw())
45 .finish_non_exhaustive() }
47}
48
49const BUGGY_RING_V2_DER_PREFIX: [u8; 16] = [
57 48, 83, 2, 1, 1, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32, ];
65
66const BUGGY_RING_V2_DER_PK_PREFIX: [u8; 5] = [
67 161,
68 35, 3, 33, 0, ];
72
73const BUGGY_RING_V2_LEN: usize = BUGGY_RING_V2_DER_PREFIX.len()
74 + PrivateKey::BYTES
75 + BUGGY_RING_V2_DER_PK_PREFIX.len()
76 + PublicKey::BYTES;
77
78#[derive(Copy, Clone, Eq, PartialEq, Debug)]
80pub enum PrivateKeyFormat {
81 Pkcs8v1,
83 Pkcs8v2,
89 Pkcs8v2WithRingBug,
98}
99
100impl PrivateKey {
101 pub const BYTES: usize = 32;
103
104 #[cfg(feature = "rand")]
106 pub fn generate() -> Self {
107 let mut rng = rand::thread_rng();
108 Self::generate_using_rng(&mut rng)
109 }
110
111 #[cfg(feature = "rand")]
113 pub fn generate_using_rng<R: rand::CryptoRng + rand::Rng>(rng: &mut R) -> Self {
114 let sk = SigningKey::generate(rng);
115 Self { sk }
116 }
117
118 pub fn generate_from_seed(seed: &[u8]) -> Self {
125 let digest: [u8; 32] = {
126 let mut sha2 = Sha512::new();
127 sha2.update(seed);
128 let digest: [u8; 64] = sha2.finalize().into();
129 let mut truncated = [0u8; 32];
130 truncated.copy_from_slice(&digest[..32]);
131 truncated
132 };
133
134 Self {
135 sk: SigningKey::from_bytes(&digest),
136 }
137 }
138
139 pub fn sign_message(&self, msg: &[u8]) -> [u8; SIGNATURE_BYTES] {
143 self.sk.sign(msg).into()
144 }
145
146 pub fn public_key(&self) -> PublicKey {
148 PublicKey::new(self.sk.verifying_key())
149 }
150
151 pub fn serialize_raw(&self) -> [u8; Self::BYTES] {
156 self.sk.to_bytes()
157 }
158
159 pub fn deserialize_raw(bytes: &[u8]) -> Result<Self, PrivateKeyDecodingError> {
166 let bytes = <[u8; Self::BYTES]>::try_from(bytes).map_err(|_| {
167 PrivateKeyDecodingError::InvalidKeyEncoding(format!(
168 "Expected key of exactly {} bytes, got {}",
169 Self::BYTES,
170 bytes.len()
171 ))
172 })?;
173
174 Ok(Self::deserialize_raw_32(&bytes))
175 }
176
177 pub fn deserialize_raw_32(bytes: &[u8; 32]) -> Self {
184 let sk = SigningKey::from_bytes(bytes);
185 Self { sk }
186 }
187
188 pub fn serialize_pkcs8(&self, format: PrivateKeyFormat) -> Vec<u8> {
192 let sk_bytes = self.serialize_raw();
193 let pk_bytes = self.public_key().serialize_raw();
194
195 fn to_pkcs8<T: EncodePrivateKey>(v: &T) -> Vec<u8> {
196 let pkcs8 = v.to_pkcs8_der();
197
198 pkcs8.expect("PKCS8 encoding failed").to_bytes().to_vec()
207 }
208
209 match format {
210 PrivateKeyFormat::Pkcs8v1 => {
211 let kp = ed25519_dalek::pkcs8::KeypairBytes {
212 secret_key: sk_bytes,
213 public_key: None,
214 };
215
216 to_pkcs8(&kp)
217 }
218 PrivateKeyFormat::Pkcs8v2 => {
219 let kp = ed25519_dalek::pkcs8::KeypairBytes {
220 secret_key: sk_bytes,
221 public_key: Some(ed25519_dalek::pkcs8::PublicKeyBytes(pk_bytes)),
222 };
223
224 to_pkcs8(&kp)
225 }
226 PrivateKeyFormat::Pkcs8v2WithRingBug => {
227 let mut ringv2 = Vec::with_capacity(BUGGY_RING_V2_LEN);
228
229 ringv2.extend_from_slice(&BUGGY_RING_V2_DER_PREFIX);
230 ringv2.extend_from_slice(&sk_bytes);
231 ringv2.extend_from_slice(&BUGGY_RING_V2_DER_PK_PREFIX);
232 ringv2.extend_from_slice(&pk_bytes);
233
234 ringv2
235 }
236 }
237 }
238
239 pub fn deserialize_pkcs8(bytes: &[u8]) -> Result<Self, PrivateKeyDecodingError> {
247 if bytes.len() == BUGGY_RING_V2_LEN && bytes.starts_with(&BUGGY_RING_V2_DER_PREFIX) {
248 let sk_offset = BUGGY_RING_V2_DER_PREFIX.len();
249 Self::deserialize_raw(&bytes[sk_offset..sk_offset + Self::BYTES])
250 } else {
251 let sk = SigningKey::from_pkcs8_der(bytes)
252 .map_err(|e| PrivateKeyDecodingError::InvalidKeyEncoding(format!("{e:?}")))?;
253 Ok(Self { sk })
254 }
255 }
256
257 pub fn serialize_pkcs8_pem(&self, format: PrivateKeyFormat) -> String {
261 let pkcs8 = self.serialize_pkcs8(format);
262
263 pem::encode(&pem::Pem::new("PRIVATE KEY", pkcs8))
264 }
265
266 pub fn deserialize_pkcs8_pem(pem: &str) -> Result<Self, PrivateKeyDecodingError> {
272 let der = pem::parse(pem)
273 .map_err(|e| PrivateKeyDecodingError::InvalidPemEncoding(format!("{e:?}")))?;
274 if der.tag() != "PRIVATE KEY" {
275 return Err(PrivateKeyDecodingError::UnexpectedPemLabel(
276 der.tag().to_string(),
277 ));
278 }
279
280 Self::deserialize_pkcs8(der.contents())
281 }
282
283 pub fn derive_subkey(&self, derivation_path: &DerivationPath) -> (DerivedPrivateKey, [u8; 32]) {
294 let chain_code = [0u8; 32];
295 self.derive_subkey_with_chain_code(derivation_path, &chain_code)
296 }
297
298 pub fn derive_subkey_with_chain_code(
310 &self,
311 derivation_path: &DerivationPath,
312 chain_code: &[u8; 32],
313 ) -> (DerivedPrivateKey, [u8; 32]) {
314 let sk_scalar = self.sk.to_scalar();
315 let pt = EdwardsPoint::mul_base(&sk_scalar);
316
317 let (pt, sum, chain_code) = derivation_path.derive_offset(pt, chain_code);
318
319 let derived_scalar = sk_scalar + sum;
320
321 let derived_hash_prefix = {
322 let mut sha2 = Sha512::new();
325 sha2.update(derived_scalar.to_bytes());
326 sha2.update(chain_code);
327 let hash: [u8; 64] = sha2.finalize().into();
328 let mut truncated = [0u8; 32];
329 truncated.copy_from_slice(&hash[..32]);
330 truncated
331 };
332
333 let dpk = DerivedPrivateKey::new(derived_scalar, derived_hash_prefix, pt);
334 (dpk, chain_code)
335 }
336}
337
338pub struct DerivedPrivateKey {
344 esk: ed25519_dalek::hazmat::ExpandedSecretKey,
346 vk: ed25519_dalek::VerifyingKey,
347}
348
349impl DerivedPrivateKey {
350 fn new(scalar: Scalar, hash_prefix: [u8; 32], pk: EdwardsPoint) -> Self {
351 let esk = ed25519_dalek::hazmat::ExpandedSecretKey {
352 scalar,
353 hash_prefix,
354 };
355
356 let vk = ed25519_dalek::VerifyingKey::from(pk);
357
358 Self { esk, vk }
359 }
360
361 pub fn sign_message(&self, msg: &[u8]) -> [u8; SIGNATURE_BYTES] {
365 ed25519_dalek::hazmat::raw_sign::<Sha512>(&self.esk, msg, &self.vk).to_bytes()
366 }
367
368 pub fn public_key(&self) -> PublicKey {
370 PublicKey::new(self.vk)
371 }
372
373 pub fn derive_subkey(&self, derivation_path: &DerivationPath) -> (DerivedPrivateKey, [u8; 32]) {
384 let chain_code = [0u8; 32];
385 self.derive_subkey_with_chain_code(derivation_path, &chain_code)
386 }
387
388 pub fn derive_subkey_with_chain_code(
400 &self,
401 derivation_path: &DerivationPath,
402 chain_code: &[u8; 32],
403 ) -> (DerivedPrivateKey, [u8; 32]) {
404 let sk_scalar = self.esk.scalar;
405 let pt = EdwardsPoint::mul_base(&sk_scalar);
406
407 let (pt, sum, chain_code) = derivation_path.derive_offset(pt, chain_code);
408
409 let derived_scalar = sk_scalar + sum;
410
411 let derived_hash_prefix = {
412 let mut sha2 = Sha512::new();
415 sha2.update(derived_scalar.to_bytes());
416 sha2.update(chain_code);
417 let hash: [u8; 64] = sha2.finalize().into();
418 let mut truncated = [0u8; 32];
419 truncated.copy_from_slice(&hash[..32]);
420 truncated
421 };
422
423 let dpk = DerivedPrivateKey::new(derived_scalar, derived_hash_prefix, pt);
424 (dpk, chain_code)
425 }
426}
427
428#[derive(Clone, Debug, Error)]
430pub enum PublicKeyDecodingError {
431 #[error("The outer PEM encoding is invalid: {0}")]
433 InvalidPemEncoding(String),
434 #[error("The PEM label was not the expected value: {0}")]
436 UnexpectedPemLabel(String),
437 #[error("The encoding of the public key is invalid: {0}")]
439 InvalidKeyEncoding(String),
440}
441
442struct Signature {
444 r: EdwardsPoint,
445 r_bytes: [u8; 32], s: Scalar,
447}
448
449impl Signature {
450 fn from_slice(signature: &[u8]) -> Result<Self, SignatureError> {
451 if signature.len() != SIGNATURE_BYTES {
452 return Err(SignatureError::InvalidLength);
453 }
454
455 let (r, r_bytes) = {
456 let mut r_bytes = [0u8; 32];
457 r_bytes.copy_from_slice(&signature[..32]);
458 let r = CompressedEdwardsY(r_bytes)
459 .decompress()
460 .ok_or(SignatureError::InvalidSignature)?;
461
462 (r, r_bytes)
463 };
464
465 let s = {
466 let mut s_bytes = [0u8; 32];
467 s_bytes.copy_from_slice(&signature[32..]);
468 Option::<Scalar>::from(Scalar::from_canonical_bytes(s_bytes))
469 .ok_or(SignatureError::InvalidSignature)?
470 };
471
472 Ok(Self { r, r_bytes, s })
473 }
474
475 pub fn r_bytes(&self) -> &[u8; 32] {
476 &self.r_bytes
477 }
478
479 pub fn r(&self) -> &EdwardsPoint {
480 &self.r
481 }
482
483 pub fn s(&self) -> &Scalar {
484 &self.s
485 }
486}
487
488#[derive(Copy, Clone, Eq, PartialEq, Debug)]
490pub enum MasterPublicKeyId {
491 Key1,
493 TestKey1,
495}
496
497#[derive(Copy, Clone, Eq, PartialEq, Debug)]
499pub enum PocketIcMasterPublicKeyId {
500 Key1,
502 TestKey1,
504 DfxTestKey,
506}
507
508#[derive(Copy, Clone, Eq, PartialEq, Debug)]
510pub struct PublicKey {
511 pk: VerifyingKey,
512}
513
514#[derive(Copy, Clone, Debug, Error, Eq, PartialEq)]
516pub enum SignatureError {
517 #[error("The signature had an invalid length, and cannot possibly be valid")]
519 InvalidLength,
520 #[error(
523 "The batch was invalid (e.g., due to length mismatch between number of
524 messages and number of signatures)"
525 )]
526 InvalidBatch,
527 #[error("A signature was invalid")]
529 InvalidSignature,
530}
531
532impl PublicKey {
533 pub const BYTES: usize = 32;
535
536 fn new(pk: VerifyingKey) -> Self {
544 Self { pk }
545 }
546
547 pub fn is_torsion_free(&self) -> bool {
550 self.pk.to_edwards().is_torsion_free()
554 }
555
556 pub fn is_canonical(&self) -> bool {
558 self.pk.to_bytes() == self.pk.to_edwards().compress().0
559 }
560
561 pub fn convert_raw_to_der(raw: &[u8]) -> Result<Vec<u8>, PublicKeyDecodingError> {
570 let raw32: [u8; 32] = raw.try_into().map_err(|_| {
571 PublicKeyDecodingError::InvalidKeyEncoding(format!(
572 "Expected key of exactly {} bytes, got {}",
573 Self::BYTES,
574 raw.len()
575 ))
576 })?;
577
578 Ok(Self::convert_raw32_to_der(raw32))
579 }
580
581 pub fn convert_raw32_to_der(raw: [u8; 32]) -> Vec<u8> {
589 const DER_PREFIX: [u8; 12] = [
590 48, 42, 48, 5, 6, 3, 43, 101, 112, 3, 33, 0, ];
596
597 let mut der_enc = Vec::with_capacity(DER_PREFIX.len() + Self::BYTES);
598 der_enc.extend_from_slice(&DER_PREFIX);
599 der_enc.extend_from_slice(&raw);
600 der_enc
601 }
602
603 pub fn serialize_raw(&self) -> [u8; Self::BYTES] {
607 *self.pk.as_bytes()
608 }
609
610 pub fn deserialize_raw(bytes: &[u8]) -> Result<Self, PublicKeyDecodingError> {
621 let bytes = <[u8; Self::BYTES]>::try_from(bytes).map_err(|_| {
622 PublicKeyDecodingError::InvalidKeyEncoding(format!(
623 "Expected key of exactly {} bytes, got {}",
624 Self::BYTES,
625 bytes.len()
626 ))
627 })?;
628 let pk = VerifyingKey::from_bytes(&bytes)
629 .map_err(|e| PublicKeyDecodingError::InvalidKeyEncoding(format!("{e:?}")))?;
630
631 Ok(Self::new(pk))
632 }
633
634 pub fn serialize_rfc8410_der(&self) -> Vec<u8> {
638 let der = self.pk.to_public_key_der();
639
640 der.expect("Encoding public key as DER failed")
642 .as_bytes()
643 .to_vec()
644 }
645
646 pub fn serialize_rfc8410_pem(&self) -> Vec<u8> {
652 let der = self.serialize_rfc8410_der();
653 pem::encode(&pem::Pem::new("PUBLIC KEY", der)).into()
654 }
655
656 pub fn deserialize_rfc8410_der(bytes: &[u8]) -> Result<Self, PublicKeyDecodingError> {
667 let pk = VerifyingKey::from_public_key_der(bytes)
668 .map_err(|e| PublicKeyDecodingError::InvalidKeyEncoding(format!("{e:?}")))?;
669 Ok(Self::new(pk))
670 }
671
672 pub fn deserialize_rfc8410_pem(pem: &str) -> Result<Self, PublicKeyDecodingError> {
683 let der = pem::parse(pem)
684 .map_err(|e| PublicKeyDecodingError::InvalidPemEncoding(format!("{e:?}")))?;
685 if der.tag() != "PUBLIC KEY" {
686 return Err(PublicKeyDecodingError::UnexpectedPemLabel(
687 der.tag().to_string(),
688 ));
689 }
690
691 Self::deserialize_rfc8410_der(der.contents())
692 }
693
694 fn compute_challenge(sig: &Signature, pk: &Self, msg: &[u8]) -> Scalar {
696 let mut sha512 = Sha512::new();
697 sha512.update(sig.r_bytes());
698 sha512.update(pk.pk.as_bytes());
701 sha512.update(msg);
702 Scalar::from_hash(sha512)
703 }
704
705 pub fn verify_signature(&self, msg: &[u8], signature: &[u8]) -> Result<(), SignatureError> {
711 let signature = Signature::from_slice(signature)?;
712
713 let k = Self::compute_challenge(&signature, self, msg);
714 let minus_a = -self.pk.to_edwards();
715 let recomputed_r =
716 EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &minus_a, signature.s());
717
718 use curve25519_dalek::traits::IsIdentity;
719
720 if (recomputed_r - signature.r())
721 .mul_by_cofactor()
722 .is_identity()
723 {
724 Ok(())
725 } else {
726 Err(SignatureError::InvalidSignature)
727 }
728 }
729
730 #[cfg(feature = "rand")]
740 pub fn batch_verify<R: rand::CryptoRng + rand::Rng>(
741 messages: &[&[u8]],
742 signatures: &[&[u8]],
743 keys: &[Self],
744 rng: &mut R,
745 ) -> Result<(), SignatureError> {
746 if messages.len() != signatures.len() || signatures.len() != keys.len() {
747 return Err(SignatureError::InvalidBatch);
748 }
749
750 use curve25519_dalek::{
751 constants::ED25519_BASEPOINT_POINT, traits::IsIdentity, traits::VartimeMultiscalarMul,
752 };
753 use std::iter::once;
754
755 let signatures = signatures
756 .iter()
757 .map(|s| Signature::from_slice(s))
758 .collect::<Result<Vec<_>, _>>()?;
759
760 let n = signatures.len();
761
762 let hrams = (0..n)
763 .map(|i| Self::compute_challenge(&signatures[i], &keys[i], messages[i]))
764 .collect::<Vec<_>>();
765
766 let zs: Vec<Scalar> = (0..n).map(|_| Scalar::from(rng.r#gen::<u128>())).collect();
768
769 let b_coefficient: Scalar = signatures
770 .iter()
771 .zip(zs.iter())
772 .map(|(sig, z)| sig.s() * z)
773 .sum();
774
775 let zhrams = hrams.iter().zip(zs.iter()).map(|(hram, z)| hram * z);
776
777 let r = signatures.iter().map(|sig| *sig.r());
778 let pk = keys.iter().map(|pk| pk.pk.to_edwards());
779
780 let id = EdwardsPoint::vartime_multiscalar_mul(
781 once(-b_coefficient).chain(zs.iter().cloned()).chain(zhrams),
782 once(ED25519_BASEPOINT_POINT).chain(r).chain(pk),
783 )
784 .mul_by_cofactor();
785
786 if id.is_identity() {
787 Ok(())
788 } else {
789 Err(SignatureError::InvalidSignature)
790 }
791 }
792
793 pub fn mainnet_key(key_id: MasterPublicKeyId) -> Self {
795 match key_id {
796 MasterPublicKeyId::Key1 => Self::deserialize_raw(&hex!(
797 "476374d9df3a8af28d3164dc2422cff894482eadd1295290b6d9ad92b2eeaa5c"
798 ))
799 .expect("Hardcoded master key was rejected"),
800 MasterPublicKeyId::TestKey1 => Self::deserialize_raw(&hex!(
801 "6c0824beb37621bcca6eecc237ed1bc4e64c9c59dcb85344aa7f9cc8278ee31f"
802 ))
803 .expect("Hardcoded master key was rejected"),
804 }
805 }
806
807 pub fn pocketic_key(key_id: PocketIcMasterPublicKeyId) -> Self {
812 match key_id {
813 PocketIcMasterPublicKeyId::Key1 => Self::deserialize_raw(&hex!(
814 "db415b8eb85bd5127b0984723e0448054042cf40e7a9c262ed0cc87ecea98349"
815 ))
816 .expect("Hardcoded master key was rejected"),
817 PocketIcMasterPublicKeyId::TestKey1 => Self::deserialize_raw(&hex!(
818 "6ed9121ecf701b9e301fce17d8a65214888984e8211225691b089d6b219ec144"
819 ))
820 .expect("Hardcoded master key was rejected"),
821 PocketIcMasterPublicKeyId::DfxTestKey => Self::deserialize_raw(&hex!(
822 "7124afcb1be5927cac0397a7447b9c3cda2a4099af62d9bc0a2c2fe42d33efe1"
823 ))
824 .expect("Hardcoded master key was rejected"),
825 }
826 }
827
828 pub fn derive_mainnet_key(
832 key_id: MasterPublicKeyId,
833 canister_id: &CanisterId,
834 derivation_path: &[Vec<u8>],
835 ) -> (Self, [u8; 32]) {
836 let mk = PublicKey::mainnet_key(key_id);
837 mk.derive_subkey(&DerivationPath::from_canister_id_and_path(
838 canister_id.as_slice(),
839 derivation_path,
840 ))
841 }
842
843 pub fn derive_pocketic_key(
848 key_id: PocketIcMasterPublicKeyId,
849 canister_id: &CanisterId,
850 derivation_path: &[Vec<u8>],
851 ) -> (Self, [u8; 32]) {
852 let mk = PublicKey::pocketic_key(key_id);
853 mk.derive_subkey(&DerivationPath::from_canister_id_and_path(
854 canister_id.as_slice(),
855 derivation_path,
856 ))
857 }
858
859 pub fn derive_subkey(&self, derivation_path: &DerivationPath) -> (Self, [u8; 32]) {
864 let chain_code = [0u8; 32];
865 self.derive_subkey_with_chain_code(derivation_path, &chain_code)
866 }
867
868 pub fn derive_subkey_with_chain_code(
874 &self,
875 derivation_path: &DerivationPath,
876 chain_code: &[u8; 32],
877 ) -> (Self, [u8; 32]) {
878 let pt = self.pk.to_edwards();
879
880 let (pt, _sum, chain_code) = derivation_path.derive_offset(pt, chain_code);
881
882 let key = Self::new(VerifyingKey::from(pt));
883
884 (key, chain_code)
885 }
886}
887
888#[derive(Clone, Debug)]
890pub struct DerivationIndex(pub Vec<u8>);
891
892#[derive(Clone, Debug)]
896pub struct DerivationPath {
897 path: Vec<DerivationIndex>,
898}
899
900impl DerivationPath {
901 pub fn new_bip32(bip32: &[u32]) -> Self {
903 let mut path = Vec::with_capacity(bip32.len());
904 for n in bip32 {
905 path.push(DerivationIndex(n.to_be_bytes().to_vec()));
906 }
907 Self::new(path)
908 }
909
910 pub fn new(path: Vec<DerivationIndex>) -> Self {
912 Self { path }
913 }
914
915 pub fn from_canister_id_and_path(canister_id: &[u8], path: &[Vec<u8>]) -> Self {
917 let mut vpath = Vec::with_capacity(1 + path.len());
918 vpath.push(DerivationIndex(canister_id.to_vec()));
919
920 for n in path {
921 vpath.push(DerivationIndex(n.to_vec()));
922 }
923 Self::new(vpath)
924 }
925
926 pub fn len(&self) -> usize {
928 self.path.len()
929 }
930
931 pub fn is_empty(&self) -> bool {
933 self.len() == 0
934 }
935
936 pub fn path(&self) -> &[DerivationIndex] {
938 &self.path
939 }
940
941 fn derive_offset(
942 &self,
943 mut pt: EdwardsPoint,
944 chain_code: &[u8; 32],
945 ) -> (EdwardsPoint, Scalar, [u8; 32]) {
946 let mut chain_code = *chain_code;
947 let mut sum = Scalar::ZERO;
948
949 for idx in self.path() {
950 let mut ikm = Vec::with_capacity(PublicKey::BYTES + idx.0.len());
951 ikm.extend_from_slice(&pt.compress().0);
952 ikm.extend_from_slice(&idx.0);
953
954 let hkdf = hkdf::Hkdf::<Sha512>::new(Some(&chain_code), &ikm);
955
956 let mut okm = [0u8; 96];
957 hkdf.expand(b"Ed25519", &mut okm)
958 .expect("96 is a valid length for HKDF-SHA-512");
959
960 let mut offset = [0u8; 64];
961 offset.copy_from_slice(&okm[0..64]);
962 offset.reverse(); let offset = Scalar::from_bytes_mod_order_wide(&offset);
964
965 pt += EdwardsPoint::mul_base(&offset);
966 sum += offset;
967 chain_code.copy_from_slice(&okm[64..]);
968 }
969
970 (pt, sum, chain_code)
971 }
972}