1 // Copyright 2015-2016 Brian Smith.
2 //
3 // Permission to use, copy, modify, and/or distribute this software for any
4 // purpose with or without fee is hereby granted, provided that the above
5 // copyright notice and this permission notice appear in all copies.
6 //
7 // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
8 // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
10 // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12 // OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13 // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14 
15 //! EdDSA Signatures.
16 
17 use super::{super::ops::*, eddsa_digest, ED25519_PUBLIC_KEY_LEN};
18 use crate::{
19     cpu, digest, error,
20     io::der,
21     pkcs8, rand,
22     signature::{self, KeyPair as SigningKeyPair},
23 };
24 
25 /// An Ed25519 key pair, for signing.
26 pub struct Ed25519KeyPair {
27     // RFC 8032 Section 5.1.6 calls this *s*.
28     private_scalar: Scalar,
29 
30     // RFC 8032 Section 5.1.6 calls this *prefix*.
31     private_prefix: Prefix,
32 
33     // RFC 8032 Section 5.1.5 calls this *A*.
34     public_key: PublicKey,
35 }
36 
37 derive_debug_via_field!(Ed25519KeyPair, stringify!(Ed25519KeyPair), public_key);
38 
39 impl Ed25519KeyPair {
40     /// Generates a new key pair and returns the key pair serialized as a
41     /// PKCS#8 document.
42     ///
43     /// The PKCS#8 document will be a v2 `OneAsymmetricKey` with the public key,
44     /// as described in [RFC 5958 Section 2]; see [RFC 8410 Section 10.3] for an
45     /// example.
46     ///
47     /// [RFC 5958 Section 2]: https://tools.ietf.org/html/rfc5958#section-2
48     /// [RFC 8410 Section 10.3]: https://tools.ietf.org/html/rfc8410#section-10.3
generate_pkcs8( rng: &dyn rand::SecureRandom, ) -> Result<pkcs8::Document, error::Unspecified>49     pub fn generate_pkcs8(
50         rng: &dyn rand::SecureRandom,
51     ) -> Result<pkcs8::Document, error::Unspecified> {
52         let seed: [u8; SEED_LEN] = rand::generate(rng)?.expose();
53         let key_pair = Self::from_seed_(&seed);
54         Ok(pkcs8::wrap_key(
55             &PKCS8_TEMPLATE,
56             &seed[..],
57             key_pair.public_key().as_ref(),
58         ))
59     }
60 
61     /// Constructs an Ed25519 key pair by parsing an unencrypted PKCS#8 v2
62     /// Ed25519 private key.
63     ///
64     /// `openssl genpkey -algorithm ED25519` generates PKCS# v1 keys, which
65     /// require the use of `Ed25519KeyPair::from_pkcs8_maybe_unchecked()`
66     /// instead of `Ed25519KeyPair::from_pkcs8()`.
67     ///
68     /// The input must be in PKCS#8 v2 format, and in particular it must contain
69     /// the public key in addition to the private key. `from_pkcs8()` will
70     /// verify that the public key and the private key are consistent with each
71     /// other.
72     ///
73     /// Some early implementations of PKCS#8 v2, including earlier versions of
74     /// *ring* and other implementations, wrapped the public key in the wrong
75     /// ASN.1 tags. Both that incorrect form and the standardized form are
76     /// accepted.
77     ///
78     /// If you need to parse PKCS#8 v1 files (without the public key) then use
79     /// `Ed25519KeyPair::from_pkcs8_maybe_unchecked()` instead.
from_pkcs8(pkcs8: &[u8]) -> Result<Self, error::KeyRejected>80     pub fn from_pkcs8(pkcs8: &[u8]) -> Result<Self, error::KeyRejected> {
81         let version = pkcs8::Version::V2Only(pkcs8::PublicKeyOptions {
82             accept_legacy_ed25519_public_key_tag: true,
83         });
84         let (seed, public_key) = unwrap_pkcs8(version, untrusted::Input::from(pkcs8))?;
85         Self::from_seed_and_public_key(
86             seed.as_slice_less_safe(),
87             public_key.unwrap().as_slice_less_safe(),
88         )
89     }
90 
91     /// Constructs an Ed25519 key pair by parsing an unencrypted PKCS#8 v1 or v2
92     /// Ed25519 private key.
93     ///
94     /// `openssl genpkey -algorithm ED25519` generates PKCS# v1 keys.
95     ///
96     /// It is recommended to use `Ed25519KeyPair::from_pkcs8()`, which accepts
97     /// only PKCS#8 v2 files that contain the public key.
98     /// `from_pkcs8_maybe_unchecked()` parses PKCS#2 files exactly like
99     /// `from_pkcs8()`. It also accepts v1 files. PKCS#8 v1 files do not contain
100     /// the public key, so when a v1 file is parsed the public key will be
101     /// computed from the private key, and there will be no consistency check
102     /// between the public key and the private key.
103     ///
104     /// Some early implementations of PKCS#8 v2, including earlier versions of
105     /// *ring* and other implementations, wrapped the public key in the wrong
106     /// ASN.1 tags. Both that incorrect form and the standardized form are
107     /// accepted.
108     ///
109     /// PKCS#8 v2 files are parsed exactly like `Ed25519KeyPair::from_pkcs8()`.
from_pkcs8_maybe_unchecked(pkcs8: &[u8]) -> Result<Self, error::KeyRejected>110     pub fn from_pkcs8_maybe_unchecked(pkcs8: &[u8]) -> Result<Self, error::KeyRejected> {
111         let version = pkcs8::Version::V1OrV2(pkcs8::PublicKeyOptions {
112             accept_legacy_ed25519_public_key_tag: true,
113         });
114         let (seed, public_key) = unwrap_pkcs8(version, untrusted::Input::from(pkcs8))?;
115         if let Some(public_key) = public_key {
116             Self::from_seed_and_public_key(
117                 seed.as_slice_less_safe(),
118                 public_key.as_slice_less_safe(),
119             )
120         } else {
121             Self::from_seed_unchecked(seed.as_slice_less_safe())
122         }
123     }
124 
125     /// Constructs an Ed25519 key pair from the private key seed `seed` and its
126     /// public key `public_key`.
127     ///
128     /// It is recommended to use `Ed25519KeyPair::from_pkcs8()` instead.
129     ///
130     /// The private and public keys will be verified to be consistent with each
131     /// other. This helps avoid misuse of the key (e.g. accidentally swapping
132     /// the private key and public key, or using the wrong private key for the
133     /// public key). This also detects any corruption of the public or private
134     /// key.
from_seed_and_public_key( seed: &[u8], public_key: &[u8], ) -> Result<Self, error::KeyRejected>135     pub fn from_seed_and_public_key(
136         seed: &[u8],
137         public_key: &[u8],
138     ) -> Result<Self, error::KeyRejected> {
139         let pair = Self::from_seed_unchecked(seed)?;
140 
141         // This implicitly verifies that `public_key` is the right length.
142         // XXX: This rejects ~18 keys when they are partially reduced, though
143         // those keys are virtually impossible to find.
144         if public_key != pair.public_key.as_ref() {
145             let err = if public_key.len() != pair.public_key.as_ref().len() {
146                 error::KeyRejected::invalid_encoding()
147             } else {
148                 error::KeyRejected::inconsistent_components()
149             };
150             return Err(err);
151         }
152 
153         Ok(pair)
154     }
155 
156     /// Constructs a Ed25519 key pair from the private key seed `seed`.
157     ///
158     /// It is recommended to use `Ed25519KeyPair::from_pkcs8()` instead. When
159     /// that is not practical, it is recommended to use
160     /// `Ed25519KeyPair::from_seed_and_public_key()` instead.
161     ///
162     /// Since the public key is not given, the public key will be computed from
163     /// the private key. It is not possible to detect misuse or corruption of
164     /// the private key since the public key isn't given as input.
from_seed_unchecked(seed: &[u8]) -> Result<Self, error::KeyRejected>165     pub fn from_seed_unchecked(seed: &[u8]) -> Result<Self, error::KeyRejected> {
166         let seed = seed
167             .try_into()
168             .map_err(|_| error::KeyRejected::invalid_encoding())?;
169         Ok(Self::from_seed_(seed))
170     }
171 
from_seed_(seed: &Seed) -> Self172     fn from_seed_(seed: &Seed) -> Self {
173         let h = digest::digest(&digest::SHA512, seed);
174         let (private_scalar, private_prefix) = h.as_ref().split_at(SCALAR_LEN);
175 
176         let private_scalar =
177             MaskedScalar::from_bytes_masked(private_scalar.try_into().unwrap()).into();
178 
179         let a = ExtPoint::from_scalarmult_base_consttime(&private_scalar, cpu::features());
180 
181         Self {
182             private_scalar,
183             private_prefix: private_prefix.try_into().unwrap(),
184             public_key: PublicKey(a.into_encoded_point()),
185         }
186     }
187 
188     /// Returns the signature of the message `msg`.
sign(&self, msg: &[u8]) -> signature::Signature189     pub fn sign(&self, msg: &[u8]) -> signature::Signature {
190         signature::Signature::new(|signature_bytes| {
191             prefixed_extern! {
192                 fn x25519_sc_muladd(
193                     s: &mut [u8; SCALAR_LEN],
194                     a: &Scalar,
195                     b: &Scalar,
196                     c: &Scalar,
197                 );
198             }
199 
200             let (signature_bytes, _unused) = signature_bytes.split_at_mut(ELEM_LEN + SCALAR_LEN);
201             let (signature_r, signature_s) = signature_bytes.split_at_mut(ELEM_LEN);
202             let nonce = {
203                 let mut ctx = digest::Context::new(&digest::SHA512);
204                 ctx.update(&self.private_prefix);
205                 ctx.update(msg);
206                 ctx.finish()
207             };
208             let nonce = Scalar::from_sha512_digest_reduced(nonce);
209 
210             let r = ExtPoint::from_scalarmult_base_consttime(&nonce, cpu::features());
211             signature_r.copy_from_slice(&r.into_encoded_point());
212             let hram_digest = eddsa_digest(signature_r, self.public_key.as_ref(), msg);
213             let hram = Scalar::from_sha512_digest_reduced(hram_digest);
214             unsafe {
215                 x25519_sc_muladd(
216                     signature_s.try_into().unwrap(),
217                     &hram,
218                     &self.private_scalar,
219                     &nonce,
220                 );
221             }
222 
223             SIGNATURE_LEN
224         })
225     }
226 }
227 
228 impl signature::KeyPair for Ed25519KeyPair {
229     type PublicKey = PublicKey;
230 
public_key(&self) -> &Self::PublicKey231     fn public_key(&self) -> &Self::PublicKey {
232         &self.public_key
233     }
234 }
235 
236 #[derive(Clone, Copy)]
237 pub struct PublicKey([u8; ED25519_PUBLIC_KEY_LEN]);
238 
239 impl AsRef<[u8]> for PublicKey {
as_ref(&self) -> &[u8]240     fn as_ref(&self) -> &[u8] {
241         self.0.as_ref()
242     }
243 }
244 
245 derive_debug_self_as_ref_hex_bytes!(PublicKey);
246 
unwrap_pkcs8( version: pkcs8::Version, input: untrusted::Input, ) -> Result<(untrusted::Input, Option<untrusted::Input>), error::KeyRejected>247 fn unwrap_pkcs8(
248     version: pkcs8::Version,
249     input: untrusted::Input,
250 ) -> Result<(untrusted::Input, Option<untrusted::Input>), error::KeyRejected> {
251     let (private_key, public_key) = pkcs8::unwrap_key(&PKCS8_TEMPLATE, version, input)?;
252     let private_key = private_key
253         .read_all(error::Unspecified, |input| {
254             der::expect_tag_and_get_value(input, der::Tag::OctetString)
255         })
256         .map_err(|error::Unspecified| error::KeyRejected::invalid_encoding())?;
257     Ok((private_key, public_key))
258 }
259 
260 type Prefix = [u8; PREFIX_LEN];
261 const PREFIX_LEN: usize = digest::SHA512_OUTPUT_LEN - SCALAR_LEN;
262 
263 const SIGNATURE_LEN: usize = ELEM_LEN + SCALAR_LEN;
264 
265 type Seed = [u8; SEED_LEN];
266 const SEED_LEN: usize = 32;
267 
268 static PKCS8_TEMPLATE: pkcs8::Template = pkcs8::Template {
269     bytes: include_bytes!("ed25519_pkcs8_v2_template.der"),
270     alg_id_range: core::ops::Range { start: 7, end: 12 },
271     curve_id_index: 0,
272     private_key_index: 0x10,
273 };
274