ed25519_zebra/
signing_key.rs

1#[cfg(feature = "pkcs8")]
2const OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.101.112"); // RFC 8410
3#[cfg(feature = "pkcs8")]
4const ALGORITHM_ID: AlgorithmIdentifierRef = AlgorithmIdentifierRef {
5    oid: OID,
6    parameters: None,
7};
8
9use crate::Error;
10#[cfg(all(feature = "pem", feature = "pkcs8"))]
11use alloc::string::String;
12use core::convert::TryFrom;
13#[cfg(feature = "pkcs8")]
14use core::convert::TryInto;
15use curve25519_dalek::{constants, digest::Update, scalar::Scalar};
16use rand_core::{CryptoRng, RngCore};
17use sha2::{Digest, Sha512};
18use subtle::ConstantTimeEq;
19use zeroize::Zeroize;
20
21use ed25519::{signature::Signer, Signature};
22
23#[cfg(feature = "pkcs8")]
24use ed25519::KeypairBytes;
25#[cfg(feature = "pem")]
26use ed25519::PublicKeyBytes;
27
28#[cfg(all(feature = "pem", feature = "pkcs8"))]
29use der::pem::LineEnding;
30#[cfg(feature = "pkcs8")]
31use pkcs8::der::SecretDocument;
32#[cfg(feature = "pkcs8")]
33use pkcs8::{
34    spki::AlgorithmIdentifierRef, DecodePrivateKey, DecodePublicKey, Document, EncodePrivateKey,
35    EncodePublicKey, ObjectIdentifier, PrivateKeyInfo,
36};
37#[cfg(all(feature = "pem", feature = "pkcs8"))]
38use zeroize::Zeroizing;
39
40#[cfg(all(feature = "pem", feature = "pkcs8"))]
41use pkcs8::der::pem::PemLabel;
42
43use crate::{VerificationKey, VerificationKeyBytes};
44
45/// The length of a ed25519 `SecretKey`, in bytes.
46pub const SECRET_KEY_LENGTH: usize = 32;
47
48/// ed25519 secret key as defined in [RFC8032 § 5.1.5]:
49///
50/// > The private key is 32 octets (256 bits, corresponding to b) of
51/// > cryptographically secure random data.
52///
53/// [RFC8032 § 5.1.5]: https://www.rfc-editor.org/rfc/rfc8032#section-5.1.5
54pub type SecretKey = [u8; SECRET_KEY_LENGTH];
55
56/// An Ed25519 signing key.
57///
58/// This is also called a secret key by other implementations.
59#[derive(Copy, Clone, Zeroize)]
60#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
61#[cfg_attr(feature = "serde", serde(from = "SerdeHelper"))]
62#[cfg_attr(feature = "serde", serde(into = "SerdeHelper"))]
63pub struct SigningKey {
64    seed: SecretKey,
65    s: Scalar,
66    prefix: [u8; 32],
67    vk: VerificationKey,
68}
69
70impl core::fmt::Debug for SigningKey {
71    fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
72        fmt.debug_struct("SigningKey")
73            .field("vk", &self.vk)
74            .finish()
75    }
76}
77
78impl<'a> From<&'a SigningKey> for VerificationKey {
79    fn from(sk: &'a SigningKey) -> VerificationKey {
80        sk.vk
81    }
82}
83
84impl<'a> From<&'a SigningKey> for VerificationKeyBytes {
85    fn from(sk: &'a SigningKey) -> VerificationKeyBytes {
86        sk.vk.into()
87    }
88}
89
90impl AsRef<[u8]> for SigningKey {
91    fn as_ref(&self) -> &[u8] {
92        &self.seed[..]
93    }
94}
95
96impl From<SigningKey> for SecretKey {
97    fn from(sk: SigningKey) -> SecretKey {
98        sk.seed
99    }
100}
101
102impl TryFrom<&[u8]> for SigningKey {
103    type Error = Error;
104    fn try_from(slice: &[u8]) -> Result<SigningKey, Self::Error> {
105        if slice.len() == 32 {
106            let mut bytes = [0u8; 32];
107            bytes[..].copy_from_slice(slice);
108            Ok(bytes.into())
109        } else {
110            Err(Self::Error::InvalidSliceLength)
111        }
112    }
113}
114
115impl From<SecretKey> for SigningKey {
116    #[allow(non_snake_case)]
117    fn from(seed: [u8; 32]) -> SigningKey {
118        // Expand the seed to a 64-byte array with SHA512.
119        let h = Sha512::digest(&seed[..]);
120
121        // Convert the low half to a scalar with Ed25519 "clamping"
122        let s = {
123            let mut scalar_bytes = [0u8; 32];
124            scalar_bytes[..].copy_from_slice(&h.as_slice()[0..32]);
125            scalar_bytes[0] &= 248;
126            scalar_bytes[31] &= 127;
127            scalar_bytes[31] |= 64;
128            Scalar::from_bytes_mod_order(scalar_bytes)
129        };
130
131        // Extract and cache the high half.
132        let prefix = {
133            let mut prefix = [0u8; 32];
134            prefix[..].copy_from_slice(&h.as_slice()[32..64]);
135            prefix
136        };
137
138        // Compute the public key as A = [s]B.
139        let A = &s * constants::ED25519_BASEPOINT_TABLE;
140
141        SigningKey {
142            seed,
143            s,
144            prefix,
145            vk: VerificationKey {
146                minus_A: -A,
147                A_bytes: VerificationKeyBytes(A.compress().to_bytes()),
148            },
149        }
150    }
151}
152
153impl ConstantTimeEq for SigningKey {
154    fn ct_eq(&self, other: &Self) -> subtle::Choice {
155        self.seed.ct_eq(&other.seed)
156    }
157}
158
159impl PartialEq for SigningKey {
160    fn eq(&self, other: &Self) -> bool {
161        self.ct_eq(other).into()
162    }
163}
164
165impl Eq for SigningKey {}
166
167#[cfg(feature = "pkcs8")]
168impl<'a> TryFrom<PrivateKeyInfo<'a>> for SigningKey {
169    type Error = Error;
170    fn try_from(pki: PrivateKeyInfo) -> Result<Self, Self::Error> {
171        if pki.algorithm == ALGORITHM_ID {
172            SigningKey::try_from(pki.private_key)
173        } else {
174            Err(Self::Error::MalformedSecretKey)
175        }
176    }
177}
178
179#[cfg(feature = "pkcs8")]
180impl EncodePublicKey for SigningKey {
181    /// Serialize the public key for a [`SigningKey`] to an ASN.1 DER-encoded document.
182    fn to_public_key_der(&self) -> pkcs8::spki::Result<Document> {
183        self.vk.to_public_key_der()
184    }
185}
186
187impl Signer<Signature> for SigningKey {
188    /// Generate a [`Signature`] using a given [`SigningKey`].
189    fn try_sign(&self, message: &[u8]) -> Result<Signature, ed25519::signature::Error> {
190        Ok(self.sign(message))
191    }
192}
193
194#[cfg(feature = "pkcs8")]
195impl TryFrom<KeypairBytes> for SigningKey {
196    type Error = pkcs8::Error;
197
198    fn try_from(pkcs8_key: KeypairBytes) -> pkcs8::Result<Self> {
199        SigningKey::try_from(&pkcs8_key)
200    }
201}
202
203#[cfg(feature = "pkcs8")]
204impl TryFrom<&KeypairBytes> for SigningKey {
205    type Error = pkcs8::Error;
206
207    fn try_from(pkcs8_key: &KeypairBytes) -> pkcs8::Result<Self> {
208        let signing_key = SigningKey::from_der(&pkcs8_key.secret_key);
209
210        // Validate the public key in the PKCS#8 document if present
211        if let Some(public_bytes) = &pkcs8_key.public_key {
212            let expected_verifying_key =
213                VerificationKey::from_public_key_der(public_bytes.as_ref())
214                    .map_err(|_| pkcs8::Error::KeyMalformed)?;
215
216            if VerificationKey::try_from(&signing_key.unwrap())
217                .unwrap()
218                .A_bytes
219                != expected_verifying_key.into()
220            {
221                return Err(pkcs8::Error::KeyMalformed);
222            }
223        }
224
225        signing_key
226    }
227}
228
229#[cfg(feature = "pem")]
230impl From<SigningKey> for KeypairBytes {
231    fn from(signing_key: SigningKey) -> KeypairBytes {
232        KeypairBytes::from(&signing_key)
233    }
234}
235
236#[cfg(feature = "pem")]
237impl From<&SigningKey> for KeypairBytes {
238    fn from(signing_key: &SigningKey) -> KeypairBytes {
239        KeypairBytes {
240            secret_key: signing_key.s.to_bytes(),
241            public_key: Some(PublicKeyBytes(signing_key.vk.try_into().unwrap())),
242        }
243    }
244}
245
246#[cfg(feature = "pkcs8")]
247impl EncodePrivateKey for SigningKey {
248    /// Serialize [`SigningKey`] to an ASN.1 DER-encoded secret document. Note that this
249    /// will generate a v2 (RFC 5958) DER encoding with a public key.
250    fn to_pkcs8_der(&self) -> pkcs8::Result<SecretDocument> {
251        // In RFC 8410, the octet string containing the private key is encapsulated by
252        // another octet string. Just add octet string bytes to the key when building
253        // the document.
254        let mut final_key = [0u8; 34];
255        final_key[..2].copy_from_slice(&[0x04, 0x20]);
256        final_key[2..].copy_from_slice(&self.seed);
257        SecretDocument::try_from(PrivateKeyInfo {
258            algorithm: ALGORITHM_ID,
259            private_key: &final_key,
260            public_key: Some(self.vk.A_bytes.0.as_slice()),
261        })
262    }
263}
264
265#[cfg(feature = "pkcs8")]
266impl DecodePrivateKey for SigningKey {
267    /// Create a [`SigningKey`] from an ASN.1 DER-encoded bytes. The bytes may include an
268    /// accompanying public key, as defined in RFC 5958 (v1 and v2), but the call will
269    /// fail if the public key doesn't match the private key's true accompanying public
270    /// key.
271    fn from_pkcs8_der(bytes: &[u8]) -> pkcs8::Result<Self> {
272        let keypair = KeypairBytes::from_pkcs8_der(bytes).unwrap();
273        let sk = SigningKey::try_from(keypair.secret_key).unwrap();
274        match keypair.public_key {
275            Some(vk2) => {
276                if sk.vk.A_bytes.0 == vk2.to_bytes() {
277                    Ok(sk)
278                } else {
279                    Err(pkcs8::Error::KeyMalformed)
280                }
281            }
282            None => Ok(sk),
283        }
284    }
285}
286
287#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
288struct SerdeHelper([u8; 32]);
289
290impl From<SerdeHelper> for SigningKey {
291    fn from(helper: SerdeHelper) -> SigningKey {
292        helper.0.into()
293    }
294}
295
296impl From<SigningKey> for SerdeHelper {
297    fn from(sk: SigningKey) -> Self {
298        Self(sk.into())
299    }
300}
301
302impl SigningKey {
303    /// Construct a [`SigningKey`] from a [`SecretKey`]
304    ///
305    #[inline]
306    pub fn from_bytes(secret_key: &SecretKey) -> Self {
307        (*secret_key).into()
308    }
309
310    /// Convert this [`SigningKey`] into a [`SecretKey`]
311    #[inline]
312    pub fn to_bytes(&self) -> SecretKey {
313        (*self).into()
314    }
315
316    /// Convert this [`SigningKey`] into a [`SecretKey`] reference
317    #[inline]
318    pub fn as_bytes(&self) -> &SecretKey {
319        &self.seed
320    }
321
322    /// Generate a new signing key.
323    pub fn new<R: RngCore + CryptoRng>(mut rng: R) -> SigningKey {
324        let mut bytes = [0u8; 32];
325        rng.fill_bytes(&mut bytes[..]);
326        bytes.into()
327    }
328
329    /// Get the [`VerificationKey`] for this [`SigningKey`].
330    pub fn verification_key(&self) -> VerificationKey {
331        self.into()
332    }
333
334    /// Create a signature on `msg` using this key.
335    #[allow(non_snake_case)]
336    pub fn sign(&self, msg: &[u8]) -> Signature {
337        let r = Scalar::from_hash(Sha512::default().chain(&self.prefix[..]).chain(msg));
338
339        let R_bytes = (&r * constants::ED25519_BASEPOINT_TABLE)
340            .compress()
341            .to_bytes();
342
343        let k = Scalar::from_hash(
344            Sha512::default()
345                .chain(&R_bytes[..])
346                .chain(&self.vk.A_bytes.0[..])
347                .chain(msg),
348        );
349
350        let s_bytes = (r + k * self.s).to_bytes();
351
352        Signature::from_components(R_bytes, s_bytes)
353    }
354
355    /// Parse [`SigningKey`] from ASN.1 DER bytes.
356    #[cfg(feature = "pkcs8")]
357    pub fn from_der(bytes: &[u8]) -> pkcs8::Result<Self> {
358        bytes
359            .try_into()
360            .map_err(|_| pkcs8::Error::ParametersMalformed)
361    }
362
363    /// Serialize [`SigningKey`] to an ASN.1 DER-encoded secret document. Note that this
364    /// will generate a v1 (RFC 5958) DER encoding without a public key.
365    #[cfg(feature = "pkcs8")]
366    pub fn to_pkcs8_der_v1(&self) -> pkcs8::Result<SecretDocument> {
367        // In RFC 8410, the octet string containing the private key is encapsulated by
368        // another octet string. Just add octet string bytes to the key when building
369        // the document.
370        let mut final_key = [0u8; 34];
371        final_key[..2].copy_from_slice(&[0x04, 0x20]);
372        final_key[2..].copy_from_slice(&self.seed);
373        SecretDocument::try_from(PrivateKeyInfo::new(ALGORITHM_ID, &final_key))
374    }
375
376    /// Serialize [`SigningKey`] as a PEM-encoded PKCS#8 string. Note that this
377    /// will generate a v1 (RFC 5958) PEM encoding without a public key.
378    #[cfg(all(feature = "pem", feature = "pkcs8"))]
379    pub fn to_pkcs8_pem_v1(
380        &self,
381        line_ending: LineEnding,
382    ) -> Result<Zeroizing<String>, pkcs8::Error> {
383        let doc = self.to_pkcs8_der_v1()?;
384        Ok(doc.to_pem(PrivateKeyInfo::PEM_LABEL, line_ending)?)
385    }
386}