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
18#[derive(Clone, Debug, Error)]
20pub enum PrivateKeyDecodingError {
21 #[error("The outer PEM encoding is invalid: {0}")]
23 InvalidPemEncoding(String),
24 #[error("The PEM label was not the expected value: {0}")]
26 UnexpectedPemLabel(String),
27 #[error("The private key seems invalid in some way: {0}")]
29 InvalidKeyEncoding(String),
30}
31
32#[derive(Clone, Eq, PartialEq, ZeroizeOnDrop)]
34pub struct PrivateKey {
35 sk: SigningKey,
36}
37
38impl std::fmt::Debug for PrivateKey {
39 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40 f.debug_struct("PrivateKey")
41 .field("public_key", &self.public_key().serialize_raw())
42 .finish_non_exhaustive() }
44}
45
46const BUGGY_RING_V2_DER_PREFIX: [u8; 16] = [
54 48, 83, 2, 1, 1, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32, ];
62
63const BUGGY_RING_V2_DER_PK_PREFIX: [u8; 5] = [
64 161,
65 35, 3, 33, 0, ];
69
70const BUGGY_RING_V2_LEN: usize = BUGGY_RING_V2_DER_PREFIX.len()
71 + PrivateKey::BYTES
72 + BUGGY_RING_V2_DER_PK_PREFIX.len()
73 + PublicKey::BYTES;
74
75#[derive(Copy, Clone, Eq, PartialEq, Debug)]
77pub enum PrivateKeyFormat {
78 Pkcs8v1,
80 Pkcs8v2,
86 Pkcs8v2WithRingBug,
95}
96
97impl PrivateKey {
98 pub const BYTES: usize = 32;
100
101 #[cfg(feature = "rand")]
103 pub fn generate() -> Self {
104 let mut rng = rand::thread_rng();
105 Self::generate_using_rng(&mut rng)
106 }
107
108 #[cfg(feature = "rand")]
110 pub fn generate_using_rng<R: rand::CryptoRng + rand::Rng>(rng: &mut R) -> Self {
111 let sk = SigningKey::generate(rng);
112 Self { sk }
113 }
114
115 pub fn generate_from_seed(seed: &[u8]) -> Self {
122 let digest: [u8; 32] = {
123 let mut sha2 = Sha512::new();
124 sha2.update(seed);
125 let digest: [u8; 64] = sha2.finalize().into();
126 let mut truncated = [0u8; 32];
127 truncated.copy_from_slice(&digest[..32]);
128 truncated
129 };
130
131 Self {
132 sk: SigningKey::from_bytes(&digest),
133 }
134 }
135
136 pub fn sign_message(&self, msg: &[u8]) -> [u8; 64] {
140 self.sk.sign(msg).into()
141 }
142
143 pub fn public_key(&self) -> PublicKey {
145 PublicKey::new(self.sk.verifying_key())
146 }
147
148 pub fn serialize_raw(&self) -> [u8; Self::BYTES] {
153 self.sk.to_bytes()
154 }
155
156 pub fn deserialize_raw(bytes: &[u8]) -> Result<Self, PrivateKeyDecodingError> {
163 let bytes = <[u8; Self::BYTES]>::try_from(bytes).map_err(|_| {
164 PrivateKeyDecodingError::InvalidKeyEncoding(format!(
165 "Expected key of exactly {} bytes, got {}",
166 Self::BYTES,
167 bytes.len()
168 ))
169 })?;
170
171 Ok(Self::deserialize_raw_32(&bytes))
172 }
173
174 pub fn deserialize_raw_32(bytes: &[u8; 32]) -> Self {
181 let sk = SigningKey::from_bytes(bytes);
182 Self { sk }
183 }
184
185 pub fn serialize_pkcs8(&self, format: PrivateKeyFormat) -> Vec<u8> {
189 let sk_bytes = self.serialize_raw();
190 let pk_bytes = self.public_key().serialize_raw();
191
192 fn to_pkcs8<T: EncodePrivateKey>(v: &T) -> Vec<u8> {
193 let pkcs8 = v.to_pkcs8_der();
194
195 pkcs8.expect("PKCS8 encoding failed").to_bytes().to_vec()
204 }
205
206 match format {
207 PrivateKeyFormat::Pkcs8v1 => {
208 let kp = ed25519_dalek::pkcs8::KeypairBytes {
209 secret_key: sk_bytes,
210 public_key: None,
211 };
212
213 to_pkcs8(&kp)
214 }
215 PrivateKeyFormat::Pkcs8v2 => {
216 let kp = ed25519_dalek::pkcs8::KeypairBytes {
217 secret_key: sk_bytes,
218 public_key: Some(ed25519_dalek::pkcs8::PublicKeyBytes(pk_bytes)),
219 };
220
221 to_pkcs8(&kp)
222 }
223 PrivateKeyFormat::Pkcs8v2WithRingBug => {
224 let mut ringv2 = Vec::with_capacity(BUGGY_RING_V2_LEN);
225
226 ringv2.extend_from_slice(&BUGGY_RING_V2_DER_PREFIX);
227 ringv2.extend_from_slice(&sk_bytes);
228 ringv2.extend_from_slice(&BUGGY_RING_V2_DER_PK_PREFIX);
229 ringv2.extend_from_slice(&pk_bytes);
230
231 ringv2
232 }
233 }
234 }
235
236 pub fn deserialize_pkcs8(bytes: &[u8]) -> Result<Self, PrivateKeyDecodingError> {
244 if bytes.len() == BUGGY_RING_V2_LEN && bytes.starts_with(&BUGGY_RING_V2_DER_PREFIX) {
245 let sk_offset = BUGGY_RING_V2_DER_PREFIX.len();
246 Self::deserialize_raw(&bytes[sk_offset..sk_offset + Self::BYTES])
247 } else {
248 let sk = SigningKey::from_pkcs8_der(bytes)
249 .map_err(|e| PrivateKeyDecodingError::InvalidKeyEncoding(format!("{e:?}")))?;
250 Ok(Self { sk })
251 }
252 }
253
254 pub fn serialize_pkcs8_pem(&self, format: PrivateKeyFormat) -> String {
258 let pkcs8 = self.serialize_pkcs8(format);
259
260 pem::encode(&pem::Pem::new("PRIVATE KEY", pkcs8))
261 }
262
263 pub fn deserialize_pkcs8_pem(pem: &str) -> Result<Self, PrivateKeyDecodingError> {
269 let der = pem::parse(pem)
270 .map_err(|e| PrivateKeyDecodingError::InvalidPemEncoding(format!("{e:?}")))?;
271 if der.tag() != "PRIVATE KEY" {
272 return Err(PrivateKeyDecodingError::UnexpectedPemLabel(
273 der.tag().to_string(),
274 ));
275 }
276
277 Self::deserialize_pkcs8(der.contents())
278 }
279
280 pub fn derive_subkey(&self, derivation_path: &DerivationPath) -> (DerivedPrivateKey, [u8; 32]) {
291 let chain_code = [0u8; 32];
292 self.derive_subkey_with_chain_code(derivation_path, &chain_code)
293 }
294
295 pub fn derive_subkey_with_chain_code(
307 &self,
308 derivation_path: &DerivationPath,
309 chain_code: &[u8; 32],
310 ) -> (DerivedPrivateKey, [u8; 32]) {
311 let sk_scalar = self.sk.to_scalar();
312 let pt = EdwardsPoint::mul_base(&sk_scalar);
313
314 let (pt, sum, chain_code) = derivation_path.derive_offset(pt, chain_code);
315
316 let derived_scalar = sk_scalar + sum;
317
318 let derived_hash_prefix = {
319 let mut sha2 = Sha512::new();
322 sha2.update(derived_scalar.to_bytes());
323 sha2.update(chain_code);
324 let hash: [u8; 64] = sha2.finalize().into();
325 let mut truncated = [0u8; 32];
326 truncated.copy_from_slice(&hash[..32]);
327 truncated
328 };
329
330 let dpk = DerivedPrivateKey::new(derived_scalar, derived_hash_prefix, pt);
331 (dpk, chain_code)
332 }
333}
334
335pub struct DerivedPrivateKey {
341 esk: ed25519_dalek::hazmat::ExpandedSecretKey,
343 vk: ed25519_dalek::VerifyingKey,
344}
345
346impl DerivedPrivateKey {
347 fn new(scalar: Scalar, hash_prefix: [u8; 32], pk: EdwardsPoint) -> Self {
348 let esk = ed25519_dalek::hazmat::ExpandedSecretKey {
349 scalar,
350 hash_prefix,
351 };
352
353 let vk = ed25519_dalek::VerifyingKey::from(pk);
354
355 Self { esk, vk }
356 }
357
358 pub fn sign_message(&self, msg: &[u8]) -> [u8; 64] {
362 ed25519_dalek::hazmat::raw_sign::<Sha512>(&self.esk, msg, &self.vk).to_bytes()
363 }
364
365 pub fn public_key(&self) -> PublicKey {
367 PublicKey::new(self.vk)
368 }
369
370 pub fn derive_subkey(&self, derivation_path: &DerivationPath) -> (DerivedPrivateKey, [u8; 32]) {
381 let chain_code = [0u8; 32];
382 self.derive_subkey_with_chain_code(derivation_path, &chain_code)
383 }
384
385 pub fn derive_subkey_with_chain_code(
397 &self,
398 derivation_path: &DerivationPath,
399 chain_code: &[u8; 32],
400 ) -> (DerivedPrivateKey, [u8; 32]) {
401 let sk_scalar = self.esk.scalar;
402 let pt = EdwardsPoint::mul_base(&sk_scalar);
403
404 let (pt, sum, chain_code) = derivation_path.derive_offset(pt, chain_code);
405
406 let derived_scalar = sk_scalar + sum;
407
408 let derived_hash_prefix = {
409 let mut sha2 = Sha512::new();
412 sha2.update(derived_scalar.to_bytes());
413 sha2.update(chain_code);
414 let hash: [u8; 64] = sha2.finalize().into();
415 let mut truncated = [0u8; 32];
416 truncated.copy_from_slice(&hash[..32]);
417 truncated
418 };
419
420 let dpk = DerivedPrivateKey::new(derived_scalar, derived_hash_prefix, pt);
421 (dpk, chain_code)
422 }
423}
424
425#[derive(Clone, Debug, Error)]
427pub enum PublicKeyDecodingError {
428 #[error("The outer PEM encoding is invalid: {0}")]
430 InvalidPemEncoding(String),
431 #[error("The PEM label was not the expected value: {0}")]
433 UnexpectedPemLabel(String),
434 #[error("The encoding of the public key is invalid: {0}")]
436 InvalidKeyEncoding(String),
437}
438
439struct Signature {
441 r: EdwardsPoint,
442 r_bytes: [u8; 32], s: Scalar,
444}
445
446impl Signature {
447 fn from_slice(signature: &[u8]) -> Result<Self, SignatureError> {
448 if signature.len() != 64 {
449 return Err(SignatureError::InvalidLength);
450 }
451
452 let (r, r_bytes) = {
453 let mut r_bytes = [0u8; 32];
454 r_bytes.copy_from_slice(&signature[..32]);
455 let r = CompressedEdwardsY(r_bytes)
456 .decompress()
457 .ok_or(SignatureError::InvalidSignature)?;
458
459 (r, r_bytes)
460 };
461
462 let s = {
463 let mut s_bytes = [0u8; 32];
464 s_bytes.copy_from_slice(&signature[32..]);
465 Option::<Scalar>::from(Scalar::from_canonical_bytes(s_bytes))
466 .ok_or(SignatureError::InvalidSignature)?
467 };
468
469 Ok(Self { r, r_bytes, s })
470 }
471
472 pub fn r_bytes(&self) -> &[u8; 32] {
473 &self.r_bytes
474 }
475
476 pub fn r(&self) -> &EdwardsPoint {
477 &self.r
478 }
479
480 pub fn s(&self) -> &Scalar {
481 &self.s
482 }
483}
484
485#[derive(Copy, Clone, Eq, PartialEq, Debug)]
487pub enum MasterPublicKeyId {
488 Key1,
490 TestKey1,
492}
493
494#[derive(Copy, Clone, Eq, PartialEq, Debug)]
496pub enum PocketIcMasterPublicKeyId {
497 Key1,
499 TestKey1,
501 DfxTestKey,
503}
504
505#[derive(Copy, Clone, Eq, PartialEq, Debug)]
507pub struct PublicKey {
508 pk: VerifyingKey,
509}
510
511#[derive(Copy, Clone, Debug, Error, Eq, PartialEq)]
513pub enum SignatureError {
514 #[error("The signature had an invalid length, and cannot possibly be valid")]
516 InvalidLength,
517 #[error(
520 "The batch was invalid (e.g., due to length mismatch between number of
521 messages and number of signatures)"
522 )]
523 InvalidBatch,
524 #[error("A signature was invalid")]
526 InvalidSignature,
527}
528
529impl PublicKey {
530 pub const BYTES: usize = 32;
532
533 fn new(pk: VerifyingKey) -> Self {
541 Self { pk }
542 }
543
544 pub fn is_torsion_free(&self) -> bool {
547 self.pk.to_edwards().is_torsion_free()
551 }
552
553 pub fn is_canonical(&self) -> bool {
555 self.pk.to_bytes() == self.pk.to_edwards().compress().0
556 }
557
558 pub fn convert_raw_to_der(raw: &[u8]) -> Result<Vec<u8>, PublicKeyDecodingError> {
567 if raw.len() != Self::BYTES {
570 return Err(PublicKeyDecodingError::InvalidKeyEncoding(format!(
571 "Expected key of exactly {} bytes, got {}",
572 Self::BYTES,
573 raw.len()
574 )));
575 };
576
577 const DER_PREFIX: [u8; 12] = [
578 48, 42, 48, 5, 6, 3, 43, 101, 112, 3, 33, 0, ];
584
585 let mut der_enc = Vec::with_capacity(DER_PREFIX.len() + Self::BYTES);
586 der_enc.extend_from_slice(&DER_PREFIX);
587 der_enc.extend_from_slice(raw);
588 Ok(der_enc)
589 }
590
591 pub fn serialize_raw(&self) -> [u8; Self::BYTES] {
595 *self.pk.as_bytes()
596 }
597
598 pub fn deserialize_raw(bytes: &[u8]) -> Result<Self, PublicKeyDecodingError> {
609 let bytes = <[u8; Self::BYTES]>::try_from(bytes).map_err(|_| {
610 PublicKeyDecodingError::InvalidKeyEncoding(format!(
611 "Expected key of exactly {} bytes, got {}",
612 Self::BYTES,
613 bytes.len()
614 ))
615 })?;
616 let pk = VerifyingKey::from_bytes(&bytes)
617 .map_err(|e| PublicKeyDecodingError::InvalidKeyEncoding(format!("{e:?}")))?;
618
619 Ok(Self::new(pk))
620 }
621
622 pub fn serialize_rfc8410_der(&self) -> Vec<u8> {
626 let der = self.pk.to_public_key_der();
627
628 der.expect("Encoding public key as DER failed")
630 .as_bytes()
631 .to_vec()
632 }
633
634 pub fn serialize_rfc8410_pem(&self) -> Vec<u8> {
640 let der = self.serialize_rfc8410_der();
641 pem::encode(&pem::Pem::new("PUBLIC KEY", der)).into()
642 }
643
644 pub fn deserialize_rfc8410_der(bytes: &[u8]) -> Result<Self, PublicKeyDecodingError> {
655 let pk = VerifyingKey::from_public_key_der(bytes)
656 .map_err(|e| PublicKeyDecodingError::InvalidKeyEncoding(format!("{e:?}")))?;
657 Ok(Self::new(pk))
658 }
659
660 pub fn deserialize_rfc8410_pem(pem: &str) -> Result<Self, PublicKeyDecodingError> {
671 let der = pem::parse(pem)
672 .map_err(|e| PublicKeyDecodingError::InvalidPemEncoding(format!("{e:?}")))?;
673 if der.tag() != "PUBLIC KEY" {
674 return Err(PublicKeyDecodingError::UnexpectedPemLabel(
675 der.tag().to_string(),
676 ));
677 }
678
679 Self::deserialize_rfc8410_der(der.contents())
680 }
681
682 fn compute_challenge(sig: &Signature, pk: &Self, msg: &[u8]) -> Scalar {
684 let mut sha512 = Sha512::new();
685 sha512.update(sig.r_bytes());
686 sha512.update(pk.pk.as_bytes());
689 sha512.update(msg);
690 Scalar::from_hash(sha512)
691 }
692
693 pub fn verify_signature(&self, msg: &[u8], signature: &[u8]) -> Result<(), SignatureError> {
699 let signature = Signature::from_slice(signature)?;
700
701 let k = Self::compute_challenge(&signature, self, msg);
702 let minus_a = -self.pk.to_edwards();
703 let recomputed_r =
704 EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &minus_a, signature.s());
705
706 use curve25519_dalek::traits::IsIdentity;
707
708 if (recomputed_r - signature.r())
709 .mul_by_cofactor()
710 .is_identity()
711 {
712 Ok(())
713 } else {
714 Err(SignatureError::InvalidSignature)
715 }
716 }
717
718 #[cfg(feature = "rand")]
728 pub fn batch_verify<R: rand::CryptoRng + rand::Rng>(
729 messages: &[&[u8]],
730 signatures: &[&[u8]],
731 keys: &[Self],
732 rng: &mut R,
733 ) -> Result<(), SignatureError> {
734 if messages.len() != signatures.len() || signatures.len() != keys.len() {
735 return Err(SignatureError::InvalidBatch);
736 }
737
738 use curve25519_dalek::{
739 constants::ED25519_BASEPOINT_POINT, traits::IsIdentity, traits::VartimeMultiscalarMul,
740 };
741 use std::iter::once;
742
743 let signatures = signatures
744 .iter()
745 .map(|s| Signature::from_slice(s))
746 .collect::<Result<Vec<_>, _>>()?;
747
748 let n = signatures.len();
749
750 let hrams = (0..n)
751 .map(|i| Self::compute_challenge(&signatures[i], &keys[i], messages[i]))
752 .collect::<Vec<_>>();
753
754 let zs: Vec<Scalar> = (0..n).map(|_| Scalar::from(rng.r#gen::<u128>())).collect();
756
757 let b_coefficient: Scalar = signatures
758 .iter()
759 .zip(zs.iter())
760 .map(|(sig, z)| sig.s() * z)
761 .sum();
762
763 let zhrams = hrams.iter().zip(zs.iter()).map(|(hram, z)| hram * z);
764
765 let r = signatures.iter().map(|sig| *sig.r());
766 let pk = keys.iter().map(|pk| pk.pk.to_edwards());
767
768 let id = EdwardsPoint::vartime_multiscalar_mul(
769 once(-b_coefficient).chain(zs.iter().cloned()).chain(zhrams),
770 once(ED25519_BASEPOINT_POINT).chain(r).chain(pk),
771 )
772 .mul_by_cofactor();
773
774 if id.is_identity() {
775 Ok(())
776 } else {
777 Err(SignatureError::InvalidSignature)
778 }
779 }
780
781 pub fn mainnet_key(key_id: MasterPublicKeyId) -> Self {
783 match key_id {
784 MasterPublicKeyId::Key1 => Self::deserialize_raw(&hex!(
785 "476374d9df3a8af28d3164dc2422cff894482eadd1295290b6d9ad92b2eeaa5c"
786 ))
787 .expect("Hardcoded master key was rejected"),
788 MasterPublicKeyId::TestKey1 => Self::deserialize_raw(&hex!(
789 "6c0824beb37621bcca6eecc237ed1bc4e64c9c59dcb85344aa7f9cc8278ee31f"
790 ))
791 .expect("Hardcoded master key was rejected"),
792 }
793 }
794
795 pub fn pocketic_key(key_id: PocketIcMasterPublicKeyId) -> Self {
800 match key_id {
801 PocketIcMasterPublicKeyId::Key1 => Self::deserialize_raw(&hex!(
802 "db415b8eb85bd5127b0984723e0448054042cf40e7a9c262ed0cc87ecea98349"
803 ))
804 .expect("Hardcoded master key was rejected"),
805 PocketIcMasterPublicKeyId::TestKey1 => Self::deserialize_raw(&hex!(
806 "6ed9121ecf701b9e301fce17d8a65214888984e8211225691b089d6b219ec144"
807 ))
808 .expect("Hardcoded master key was rejected"),
809 PocketIcMasterPublicKeyId::DfxTestKey => Self::deserialize_raw(&hex!(
810 "7124afcb1be5927cac0397a7447b9c3cda2a4099af62d9bc0a2c2fe42d33efe1"
811 ))
812 .expect("Hardcoded master key was rejected"),
813 }
814 }
815
816 pub fn derive_mainnet_key(
820 key_id: MasterPublicKeyId,
821 canister_id: &CanisterId,
822 derivation_path: &[Vec<u8>],
823 ) -> (Self, [u8; 32]) {
824 let mk = PublicKey::mainnet_key(key_id);
825 mk.derive_subkey(&DerivationPath::from_canister_id_and_path(
826 canister_id.as_slice(),
827 derivation_path,
828 ))
829 }
830
831 pub fn derive_pocketic_key(
836 key_id: PocketIcMasterPublicKeyId,
837 canister_id: &CanisterId,
838 derivation_path: &[Vec<u8>],
839 ) -> (Self, [u8; 32]) {
840 let mk = PublicKey::pocketic_key(key_id);
841 mk.derive_subkey(&DerivationPath::from_canister_id_and_path(
842 canister_id.as_slice(),
843 derivation_path,
844 ))
845 }
846
847 pub fn derive_subkey(&self, derivation_path: &DerivationPath) -> (Self, [u8; 32]) {
852 let chain_code = [0u8; 32];
853 self.derive_subkey_with_chain_code(derivation_path, &chain_code)
854 }
855
856 pub fn derive_subkey_with_chain_code(
862 &self,
863 derivation_path: &DerivationPath,
864 chain_code: &[u8; 32],
865 ) -> (Self, [u8; 32]) {
866 let pt = CompressedEdwardsY(self.pk.to_bytes()).decompress().unwrap();
869
870 let (pt, _sum, chain_code) = derivation_path.derive_offset(pt, chain_code);
871
872 let key = Self::new(VerifyingKey::from(pt));
873
874 (key, chain_code)
875 }
876}
877
878#[derive(Clone, Debug)]
880pub struct DerivationIndex(pub Vec<u8>);
881
882#[derive(Clone, Debug)]
886pub struct DerivationPath {
887 path: Vec<DerivationIndex>,
888}
889
890impl DerivationPath {
891 pub fn new_bip32(bip32: &[u32]) -> Self {
893 let mut path = Vec::with_capacity(bip32.len());
894 for n in bip32 {
895 path.push(DerivationIndex(n.to_be_bytes().to_vec()));
896 }
897 Self::new(path)
898 }
899
900 pub fn new(path: Vec<DerivationIndex>) -> Self {
902 Self { path }
903 }
904
905 pub fn from_canister_id_and_path(canister_id: &[u8], path: &[Vec<u8>]) -> Self {
907 let mut vpath = Vec::with_capacity(1 + path.len());
908 vpath.push(DerivationIndex(canister_id.to_vec()));
909
910 for n in path {
911 vpath.push(DerivationIndex(n.to_vec()));
912 }
913 Self::new(vpath)
914 }
915
916 pub fn len(&self) -> usize {
918 self.path.len()
919 }
920
921 pub fn is_empty(&self) -> bool {
923 self.len() == 0
924 }
925
926 pub fn path(&self) -> &[DerivationIndex] {
928 &self.path
929 }
930
931 fn derive_offset(
932 &self,
933 mut pt: EdwardsPoint,
934 chain_code: &[u8; 32],
935 ) -> (EdwardsPoint, Scalar, [u8; 32]) {
936 let mut chain_code = *chain_code;
937 let mut sum = Scalar::ZERO;
938
939 for idx in self.path() {
940 let mut ikm = Vec::with_capacity(PublicKey::BYTES + idx.0.len());
941 ikm.extend_from_slice(&pt.compress().0);
942 ikm.extend_from_slice(&idx.0);
943
944 let hkdf = hkdf::Hkdf::<Sha512>::new(Some(&chain_code), &ikm);
945
946 let mut okm = [0u8; 96];
947 hkdf.expand(b"Ed25519", &mut okm)
948 .expect("96 is a valid length for HKDF-SHA-512");
949
950 let mut offset = [0u8; 64];
951 offset.copy_from_slice(&okm[0..64]);
952 offset.reverse(); let offset = Scalar::from_bytes_mod_order_wide(&offset);
954
955 pt += EdwardsPoint::mul_base(&offset);
956 sum += offset;
957 chain_code.copy_from_slice(&okm[64..]);
958 }
959
960 (pt, sum, chain_code)
961 }
962}