ic_ed25519/
lib.rs

1#![warn(rust_2018_idioms)]
2#![warn(future_incompatible)]
3#![forbid(missing_docs)]
4#![forbid(unsafe_code)]
5
6//! A crate for creating and verifying Ed25519 signatures
7
8use 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/// An error if a private key cannot be decoded
19#[derive(Clone, Debug, Error)]
20pub enum PrivateKeyDecodingError {
21    /// The outer PEM encoding is invalid
22    #[error("The outer PEM encoding is invalid: {0}")]
23    InvalidPemEncoding(String),
24    /// The PEM label was not the expected value
25    #[error("The PEM label was not the expected value: {0}")]
26    UnexpectedPemLabel(String),
27    /// The private key seems invalid in some way; the string contains details
28    #[error("The private key seems invalid in some way: {0}")]
29    InvalidKeyEncoding(String),
30}
31
32/// An Ed25519 secret key
33#[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() // avoids printing secret information
43    }
44}
45
46/*
47The ring crate, in versions prior to 0.17 has an unfortunate bug that
48it both requires that Ed25519 private keys be conveyed using the PKCS8 V2
49encoding AND it has a bug such that it does not accept the actual (correct)
50PKCS8 V2 encoding.
51*/
52
53const BUGGY_RING_V2_DER_PREFIX: [u8; 16] = [
54    48, 83, // A sequence of 83 bytes follows.
55    2, 1, // An integer denoting version
56    1, // 0 if secret key only, 1 if public key is also present
57    48, 5, // An element of 5 bytes follows
58    6, 3, 43, 101, 112, // The OID
59    4, 34, // An octet string of 34 bytes follows.
60    4, 32, // An octet string of 32 bytes follows.
61];
62
63const BUGGY_RING_V2_DER_PK_PREFIX: [u8; 5] = [
64    161,
65    35, // An explicitly tagged with 35 bytes. (This is the bug; this should be an implicit tag)
66    3, 33, // A bitstring of 33 bytes follows.
67    0,  // The bitstring (32 bytes) is divisible by 8
68];
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/// Specifies a private key encoding format
76#[derive(Copy, Clone, Eq, PartialEq, Debug)]
77pub enum PrivateKeyFormat {
78    /// PKCS #8 v1: most common version, implemented by for example OpenSSL
79    Pkcs8v1,
80    /// PKCS #8 v2: newer format which includes the public key.
81    ///
82    /// # Warning
83    ///
84    /// Many libraries including OpenSSL cannot parse PKCS8 v2 formatting
85    Pkcs8v2,
86    /// PKCS #8 v2 emulating a bug that makes it compatible with
87    /// versions of the ring cryptography library prior to 0.17
88    ///
89    /// # Warning
90    ///
91    /// The only libraries that can parse this format are ring,
92    /// or libraries (such as this crate) that go out of their way
93    /// to be compatible with ring's buggy format.
94    Pkcs8v2WithRingBug,
95}
96
97impl PrivateKey {
98    /// The length in bytes of the raw private key
99    pub const BYTES: usize = 32;
100
101    /// Create a new random secret Ed25519 key
102    #[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    /// Create a new random secret Ed25519 key using specified RNG
109    #[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    /// Generate a key using an input seed
116    ///
117    /// # Warning
118    ///
119    /// For security the seed should be at least 256 bits and
120    /// randomly generated
121    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    /// Sign a message and return a signature
137    ///
138    /// This is the non-prehashed variant of Ed25519
139    pub fn sign_message(&self, msg: &[u8]) -> [u8; 64] {
140        self.sk.sign(msg).into()
141    }
142
143    /// Return the public key associated with this secret key
144    pub fn public_key(&self) -> PublicKey {
145        PublicKey::new(self.sk.verifying_key())
146    }
147
148    /// Serialize the Ed25519 secret key
149    ///
150    /// This returns the 32-byte encoding of the seed value
151    /// which is used to derive the secret scalar
152    pub fn serialize_raw(&self) -> [u8; Self::BYTES] {
153        self.sk.to_bytes()
154    }
155
156    /// Deserialize an Ed25519 private key from raw format
157    ///
158    /// This is just the plain 32 byte random seed from which the
159    /// internal key material is derived
160    ///
161    /// This corresponds with the format used by PrivateKey::serialize_raw
162    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    /// Deserialize an Ed25519 private key from raw format
175    ///
176    /// This is just the plain 32 byte random seed from which the
177    /// internal key material is derived
178    ///
179    /// This corresponds with the format used by PrivateKey::serialize_raw
180    pub fn deserialize_raw_32(bytes: &[u8; 32]) -> Self {
181        let sk = SigningKey::from_bytes(bytes);
182        Self { sk }
183    }
184
185    /// Serialize the Ed25519 secret key in PKCS8 format
186    ///
187    /// The details of the formatting are specified using the argument
188    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            // Key encoding with the pkcs8 crate can fail, largely to allow for
196            // fallible encoding on the part of the algorithm specific code.  But
197            // logically speaking, as long as the key is valid (which we've already
198            // checked) then there is no reason for encoding to ever fail outside of
199            // memory allocation errors. None of the error types that to_pkcs8_der
200            // can return have any relevance to encoding. And the dalek encoding
201            // functions themselves do not have any error cases.
202
203            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    /// Deserialize an Ed25519 private key from PKCS8 format
237    ///
238    /// Both v1 and v2 PKCS8 encodings are accepted. The only difference is
239    /// that v2 includes the public key as well. This also accepts the buggy
240    /// format used by ring 0.16.
241    ///
242    /// This corresponds with the format used by PrivateKey::serialize_pkcs8
243    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    /// Serialize the Ed25519 secret key in PKCS8 v2 format with PEM encoding
255    ///
256    /// The details of the formatting are specified using the argument
257    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    /// Deserialize an Ed25519 private key from PKCS8 PEM format
264    ///
265    /// Both v1 and v2 PKCS8 encodings are accepted
266    ///
267    /// This corresponds with the format used by PrivateKey::serialize_pkcs8_pem
268    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    /// Derive a private key from this private key using a derivation path
281    ///
282    /// This is the same derivation system used by the Internet Computer when
283    /// deriving subkeys for threshold Ed25519
284    ///
285    /// Note that this function returns a DerivedPrivateKey rather than Self,
286    /// and that DerivedPrivateKey can sign messages but cannot be serialized.
287    /// This is due to the definition of Ed25519 private keys, which is
288    /// incompatible with additive derivation.
289    ///
290    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    /// Derive a private key from this private key using a derivation path
296    /// and chain code
297    ///
298    /// This is the same derivation system used by the Internet Computer when
299    /// deriving subkeys for threshold Ed25519
300    ///
301    /// Note that this function returns a DerivedPrivateKey rather than Self,
302    /// and that DerivedPrivateKey can sign messages but cannot be serialized.
303    /// This is due to the definition of Ed25519 private keys, which is
304    /// incompatible with additive derivation.
305    ///
306    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            // Hash the new derived key and chain code with SHA-512 to derive
320            // the new hash prefix
321            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
335/// A private key derived via the IC's derivation mechanism
336///
337/// Due to oddities in Ed25519's secret key format, a derived private
338/// key cannot be treated the same way as an ordinary private key.
339/// In particular, it cannot be serialized.
340pub struct DerivedPrivateKey {
341    // ExpandedSecretKey has a Drop impl which will zeroize the key
342    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    /// Sign a message and return a signature
359    ///
360    /// This is the non-prehashed variant of Ed25519
361    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    /// Return the public key associated with this private key
366    pub fn public_key(&self) -> PublicKey {
367        PublicKey::new(self.vk)
368    }
369
370    /// Derive a private key from this private key using a derivation path
371    ///
372    /// This is the same derivation system used by the Internet Computer when
373    /// deriving subkeys for threshold Ed25519
374    ///
375    /// Note that this function returns a DerivedPrivateKey rather than Self,
376    /// and that DerivedPrivateKey can sign messages but cannot be serialized.
377    /// This is due to the definition of Ed25519 private keys, which is
378    /// incompatible with additive derivation.
379    ///
380    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    /// Derive a private key from this private key using a derivation path
386    /// and chain code
387    ///
388    /// This is the same derivation system used by the Internet Computer when
389    /// deriving subkeys for threshold Ed25519
390    ///
391    /// Note that this function returns a DerivedPrivateKey rather than Self,
392    /// and that DerivedPrivateKey can sign messages but cannot be serialized.
393    /// This is due to the definition of Ed25519 private keys, which is
394    /// incompatible with additive derivation.
395    ///
396    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            // Hash the new derived key and chain code with SHA-512 to derive
410            // the new hash prefix
411            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/// An invalid key was encountered
426#[derive(Clone, Debug, Error)]
427pub enum PublicKeyDecodingError {
428    /// The outer PEM encoding is invalid
429    #[error("The outer PEM encoding is invalid: {0}")]
430    InvalidPemEncoding(String),
431    /// The PEM label was not the expected value
432    #[error("The PEM label was not the expected value: {0}")]
433    UnexpectedPemLabel(String),
434    /// The encoding of the public key is invalid, the string contains details
435    #[error("The encoding of the public key is invalid: {0}")]
436    InvalidKeyEncoding(String),
437}
438
439/// An Ed25519 signature (not a public API)
440struct Signature {
441    r: EdwardsPoint,
442    r_bytes: [u8; 32], // potentially non-canonical
443    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/// An identifier for the mainnet production key
486#[derive(Copy, Clone, Eq, PartialEq, Debug)]
487pub enum MasterPublicKeyId {
488    /// The production master key
489    Key1,
490    /// The test master key
491    TestKey1,
492}
493
494/// An identifier for the hardcoded keys used in PocketIC
495#[derive(Copy, Clone, Eq, PartialEq, Debug)]
496pub enum PocketIcMasterPublicKeyId {
497    /// The PocketIC hardcoded key "key_1"
498    Key1,
499    /// The PocketIC hardcoded key "test_key_1"
500    TestKey1,
501    /// The PocketIC hardcoded key "dfx_test_key"
502    DfxTestKey,
503}
504
505/// An Ed25519 public key
506#[derive(Copy, Clone, Eq, PartialEq, Debug)]
507pub struct PublicKey {
508    pk: VerifyingKey,
509}
510
511/// An error that occurs when verifying signatures or batches of signatures
512#[derive(Copy, Clone, Debug, Error, Eq, PartialEq)]
513pub enum SignatureError {
514    /// The signature had an invalid length, and cannot possibly be valid
515    #[error("The signature had an invalid length, and cannot possibly be valid")]
516    InvalidLength,
517    /// The batch was invalid (e.g., due to length mismatch between number of
518    /// messages and number of signatures)
519    #[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    /// A signature was invalid
525    #[error("A signature was invalid")]
526    InvalidSignature,
527}
528
529impl PublicKey {
530    /// The number of bytes in the raw public key
531    pub const BYTES: usize = 32;
532
533    /// Internal constructor
534    ///
535    /// # Warning
536    ///
537    /// This does not verify that the key is within the prime order
538    /// subgroup, or that the public key is canonical. To check these
539    /// properties, use is_torsion_free and is_canonical
540    fn new(pk: VerifyingKey) -> Self {
541        Self { pk }
542    }
543
544    /// Return true if and only if the key is contained within the prime
545    /// order subgroup
546    pub fn is_torsion_free(&self) -> bool {
547        // We don't need to call is_weak here since that is subsumed by the
548        // test that the point is torsion free - is_weak just checks if the
549        // point is within the size-8 cofactor group.
550        self.pk.to_edwards().is_torsion_free()
551    }
552
553    /// Return true if and only if the public key uses a canonical encoding
554    pub fn is_canonical(&self) -> bool {
555        self.pk.to_bytes() == self.pk.to_edwards().compress().0
556    }
557
558    /// Convert a raw Ed25519 public key (32 bytes) to the DER encoding
559    ///
560    /// # Warning
561    ///
562    /// This performs no validity check on the public key aside from verifying
563    /// that it is exactly 32 bytes long. If you pass an invalid key (ie a
564    /// encoding of a point not in the prime order subgroup), then the DER
565    /// encoding of that invalid key will be returned.
566    pub fn convert_raw_to_der(raw: &[u8]) -> Result<Vec<u8>, PublicKeyDecodingError> {
567        // We continue to check the length, since otherwise the DER
568        // encoding itself would be invalid and unparsable.
569        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, // A sequence of 42 bytes follows
579            48, 5, // An sequence of 5 bytes follows
580            6, 3, 43, 101, 112, // The OID (1.3.101.112)
581            3, 33, // A bitstring of 33 bytes follows
582            0,  // The bitstring has no unused bits
583        ];
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    /// Serialize this public key in raw format
592    ///
593    /// This is just the 32 byte encoding of the public point
594    pub fn serialize_raw(&self) -> [u8; Self::BYTES] {
595        *self.pk.as_bytes()
596    }
597
598    /// Deserialize a public key in raw format
599    ///
600    /// This is just the 32 byte encoding of the public point,
601    /// cooresponding to Self::serialize_raw
602    ///
603    /// # Warning
604    ///
605    /// This does not verify that the key is within the prime order
606    /// subgroup, or that the public key is canonical. To check these
607    /// properties, use is_torsion_free and is_canonical
608    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    /// Serialize this public key as a DER encoded structure
623    ///
624    /// See RFC 8410 for details on the format
625    pub fn serialize_rfc8410_der(&self) -> Vec<u8> {
626        let der = self.pk.to_public_key_der();
627
628        // See comment in serialize_pkcs8 regarding this expect
629        der.expect("Encoding public key as DER failed")
630            .as_bytes()
631            .to_vec()
632    }
633
634    /// Serialize this public key as a PEM encoded structure
635    ///
636    /// See RFC 8410 for details on the format
637    ///
638    /// This returns a Vec<u8> instead of a String for accidental/historical reasons
639    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    /// Deserialize the DER encoded public key
645    ///
646    /// See RFC 8410 for details on the format. This cooresponds to
647    /// Self::serialize_rfc8410_der
648    ///
649    /// # Warning
650    ///
651    /// This does not verify that the key is within the prime order
652    /// subgroup, or that the public key is canonical. To check these
653    /// properties, use is_torsion_free and is_canonical
654    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    /// Deserialize the PEM encoded public key
661    ///
662    /// See RFC 8410 for details on the format. This cooresponds to
663    /// Self::serialize_rfc8410_pem
664    ///
665    /// # Warning
666    ///
667    /// This does not verify that the key is within the prime order
668    /// subgroup, or that the public key is canonical. To check these
669    /// properties, use is_torsion_free and is_canonical
670    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    /// Helper function for computing H(R || A || M)
683    fn compute_challenge(sig: &Signature, pk: &Self, msg: &[u8]) -> Scalar {
684        let mut sha512 = Sha512::new();
685        sha512.update(sig.r_bytes());
686        // VerifyingKey::as_bytes returns the original encoding which may be non-canonical;
687        // this is exactly what we need under ZIP215
688        sha512.update(pk.pk.as_bytes());
689        sha512.update(msg);
690        Scalar::from_hash(sha512)
691    }
692
693    /// Verify a Ed25519 signature
694    ///
695    /// Returns Ok if the signature is valid, or Err otherwise
696    ///
697    /// This verification follows ZIP215 validation rules
698    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    /// Verify a batch of signatures
719    ///
720    /// Returns Ok if the signatures are all valid, or Err otherwise
721    ///
722    /// Note that this does not indicate which of the signature(s) are invalid;
723    /// if batch verification fails you must then test serially to find the
724    /// valid signatures (if any).
725    ///
726    /// This verification follows ZIP215 validation rules
727    #[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        // Select a random Scalar for each signature.
755        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    /// Return the public master keys used in the production mainnet
782    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    /// Return the public master keys used by PocketIC
796    ///
797    /// Note that the secret keys for these public keys are known, and these keys
798    /// should only be used for offline testing with PocketIC
799    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    /// Derive a public key from the mainnet parameters
817    ///
818    /// This is an offline equivalent to the `schnorr_public_key` management canister call
819    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    /// Derive a public key as is done on PocketIC
832    ///
833    /// This is an offline equivalent to the `schnorr_public_key` management canister call
834    /// when running on PocketIC
835    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    /// Derive a public key from this public key using a derivation path
848    ///
849    /// This is the same derivation system used by the Internet Computer when
850    /// deriving subkeys for Ed25519
851    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    /// Derive a public key from this public key using a derivation path
857    /// and chain code
858    ///
859    /// This is the same derivation system used by the Internet Computer when
860    /// deriving subkeys for Ed25519
861    pub fn derive_subkey_with_chain_code(
862        &self,
863        derivation_path: &DerivationPath,
864        chain_code: &[u8; 32],
865    ) -> (Self, [u8; 32]) {
866        // TODO(CRP-2412) Use VerifyingKey::to_edwards once available
867
868        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/// A component of a derivation path
879#[derive(Clone, Debug)]
880pub struct DerivationIndex(pub Vec<u8>);
881
882/// Derivation Path
883///
884/// A derivation path is simply a sequence of DerivationIndex
885#[derive(Clone, Debug)]
886pub struct DerivationPath {
887    path: Vec<DerivationIndex>,
888}
889
890impl DerivationPath {
891    /// Create a BIP32-style derivation path
892    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    /// Create a free-form derivation path
901    pub fn new(path: Vec<DerivationIndex>) -> Self {
902        Self { path }
903    }
904
905    /// Create a path from a canister ID and a user provided path
906    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    /// Return the length of this path
917    pub fn len(&self) -> usize {
918        self.path.len()
919    }
920
921    /// Return if this path is empty
922    pub fn is_empty(&self) -> bool {
923        self.len() == 0
924    }
925
926    /// Return the components of the derivation path
927    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(); // dalek uses little endian
953            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}