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 |
49 | pub fn generate_pkcs8( |
50 | rng: &dyn rand::SecureRandom, |
51 | ) -> Result<pkcs8::Document, error::Unspecified> { |
52 | let cpu_features = cpu::features(); |
53 | let seed: [u8; SEED_LEN] = rand::generate(rng)?.expose(); |
54 | let key_pair = Self::from_seed_(&seed, cpu_features); |
55 | Ok(pkcs8::wrap_key( |
56 | &PKCS8_TEMPLATE, |
57 | &seed[..], |
58 | key_pair.public_key().as_ref(), |
59 | )) |
60 | } |
61 | |
62 | /// Constructs an Ed25519 key pair by parsing an unencrypted PKCS#8 v2 |
63 | /// Ed25519 private key. |
64 | /// |
65 | /// `openssl genpkey -algorithm ED25519` generates PKCS# v1 keys, which |
66 | /// require the use of `Ed25519KeyPair::from_pkcs8_maybe_unchecked()` |
67 | /// instead of `Ed25519KeyPair::from_pkcs8()`. |
68 | /// |
69 | /// The input must be in PKCS#8 v2 format, and in particular it must contain |
70 | /// the public key in addition to the private key. `from_pkcs8()` will |
71 | /// verify that the public key and the private key are consistent with each |
72 | /// other. |
73 | /// |
74 | /// Some early implementations of PKCS#8 v2, including earlier versions of |
75 | /// *ring* and other implementations, wrapped the public key in the wrong |
76 | /// ASN.1 tags. Both that incorrect form and the standardized form are |
77 | /// accepted. |
78 | /// |
79 | /// If you need to parse PKCS#8 v1 files (without the public key) then use |
80 | /// `Ed25519KeyPair::from_pkcs8_maybe_unchecked()` instead. |
81 | pub fn from_pkcs8(pkcs8: &[u8]) -> Result<Self, error::KeyRejected> { |
82 | let version = pkcs8::Version::V2Only(pkcs8::PublicKeyOptions { |
83 | accept_legacy_ed25519_public_key_tag: true, |
84 | }); |
85 | let (seed, public_key) = unwrap_pkcs8(version, untrusted::Input::from(pkcs8))?; |
86 | Self::from_seed_and_public_key( |
87 | seed.as_slice_less_safe(), |
88 | public_key.unwrap().as_slice_less_safe(), |
89 | ) |
90 | } |
91 | |
92 | /// Constructs an Ed25519 key pair by parsing an unencrypted PKCS#8 v1 or v2 |
93 | /// Ed25519 private key. |
94 | /// |
95 | /// `openssl genpkey -algorithm ED25519` generates PKCS# v1 keys. |
96 | /// |
97 | /// It is recommended to use `Ed25519KeyPair::from_pkcs8()`, which accepts |
98 | /// only PKCS#8 v2 files that contain the public key. |
99 | /// `from_pkcs8_maybe_unchecked()` parses PKCS#2 files exactly like |
100 | /// `from_pkcs8()`. It also accepts v1 files. PKCS#8 v1 files do not contain |
101 | /// the public key, so when a v1 file is parsed the public key will be |
102 | /// computed from the private key, and there will be no consistency check |
103 | /// between the public key and the private key. |
104 | /// |
105 | /// Some early implementations of PKCS#8 v2, including earlier versions of |
106 | /// *ring* and other implementations, wrapped the public key in the wrong |
107 | /// ASN.1 tags. Both that incorrect form and the standardized form are |
108 | /// accepted. |
109 | /// |
110 | /// PKCS#8 v2 files are parsed exactly like `Ed25519KeyPair::from_pkcs8()`. |
111 | pub fn from_pkcs8_maybe_unchecked(pkcs8: &[u8]) -> Result<Self, error::KeyRejected> { |
112 | let version = pkcs8::Version::V1OrV2(pkcs8::PublicKeyOptions { |
113 | accept_legacy_ed25519_public_key_tag: true, |
114 | }); |
115 | let (seed, public_key) = unwrap_pkcs8(version, untrusted::Input::from(pkcs8))?; |
116 | if let Some(public_key) = public_key { |
117 | Self::from_seed_and_public_key( |
118 | seed.as_slice_less_safe(), |
119 | public_key.as_slice_less_safe(), |
120 | ) |
121 | } else { |
122 | Self::from_seed_unchecked(seed.as_slice_less_safe()) |
123 | } |
124 | } |
125 | |
126 | /// Constructs an Ed25519 key pair from the private key seed `seed` and its |
127 | /// public key `public_key`. |
128 | /// |
129 | /// It is recommended to use `Ed25519KeyPair::from_pkcs8()` instead. |
130 | /// |
131 | /// The private and public keys will be verified to be consistent with each |
132 | /// other. This helps avoid misuse of the key (e.g. accidentally swapping |
133 | /// the private key and public key, or using the wrong private key for the |
134 | /// public key). This also detects any corruption of the public or private |
135 | /// key. |
136 | pub fn from_seed_and_public_key( |
137 | seed: &[u8], |
138 | public_key: &[u8], |
139 | ) -> Result<Self, error::KeyRejected> { |
140 | let pair = Self::from_seed_unchecked(seed)?; |
141 | |
142 | // This implicitly verifies that `public_key` is the right length. |
143 | // XXX: This rejects ~18 keys when they are partially reduced, though |
144 | // those keys are virtually impossible to find. |
145 | if public_key != pair.public_key.as_ref() { |
146 | let err = if public_key.len() != pair.public_key.as_ref().len() { |
147 | error::KeyRejected::invalid_encoding() |
148 | } else { |
149 | error::KeyRejected::inconsistent_components() |
150 | }; |
151 | return Err(err); |
152 | } |
153 | |
154 | Ok(pair) |
155 | } |
156 | |
157 | /// Constructs a Ed25519 key pair from the private key seed `seed`. |
158 | /// |
159 | /// It is recommended to use `Ed25519KeyPair::from_pkcs8()` instead. When |
160 | /// that is not practical, it is recommended to use |
161 | /// `Ed25519KeyPair::from_seed_and_public_key()` instead. |
162 | /// |
163 | /// Since the public key is not given, the public key will be computed from |
164 | /// the private key. It is not possible to detect misuse or corruption of |
165 | /// the private key since the public key isn't given as input. |
166 | pub fn from_seed_unchecked(seed: &[u8]) -> Result<Self, error::KeyRejected> { |
167 | let seed = seed |
168 | .try_into() |
169 | .map_err(|_| error::KeyRejected::invalid_encoding())?; |
170 | Ok(Self::from_seed_(seed, cpu::features())) |
171 | } |
172 | |
173 | fn from_seed_(seed: &Seed, cpu_features: cpu::Features) -> Self { |
174 | let h = digest::digest(&digest::SHA512, seed); |
175 | let (private_scalar, private_prefix) = h.as_ref().split_at(SCALAR_LEN); |
176 | |
177 | let private_scalar = |
178 | MaskedScalar::from_bytes_masked(private_scalar.try_into().unwrap()).into(); |
179 | |
180 | let a = ExtPoint::from_scalarmult_base_consttime(&private_scalar, cpu_features); |
181 | |
182 | Self { |
183 | private_scalar, |
184 | private_prefix: private_prefix.try_into().unwrap(), |
185 | public_key: PublicKey(a.into_encoded_point(cpu_features)), |
186 | } |
187 | } |
188 | |
189 | /// Returns the signature of the message `msg`. |
190 | pub fn sign(&self, msg: &[u8]) -> signature::Signature { |
191 | let cpu_features = cpu::features(); |
192 | signature::Signature::new(|signature_bytes| { |
193 | prefixed_extern! { |
194 | fn x25519_sc_muladd( |
195 | s: &mut [u8; SCALAR_LEN], |
196 | a: &Scalar, |
197 | b: &Scalar, |
198 | c: &Scalar, |
199 | ); |
200 | } |
201 | |
202 | let (signature_bytes, _unused) = signature_bytes.split_at_mut(ELEM_LEN + SCALAR_LEN); |
203 | let (signature_r, signature_s) = signature_bytes.split_at_mut(ELEM_LEN); |
204 | let nonce = { |
205 | let mut ctx = digest::Context::new(&digest::SHA512); |
206 | ctx.update(&self.private_prefix); |
207 | ctx.update(msg); |
208 | ctx.finish() |
209 | }; |
210 | let nonce = Scalar::from_sha512_digest_reduced(nonce); |
211 | |
212 | let r = ExtPoint::from_scalarmult_base_consttime(&nonce, cpu_features); |
213 | signature_r.copy_from_slice(&r.into_encoded_point(cpu_features)); |
214 | let hram_digest = eddsa_digest(signature_r, self.public_key.as_ref(), msg); |
215 | let hram = Scalar::from_sha512_digest_reduced(hram_digest); |
216 | unsafe { |
217 | x25519_sc_muladd( |
218 | signature_s.try_into().unwrap(), |
219 | &hram, |
220 | &self.private_scalar, |
221 | &nonce, |
222 | ); |
223 | } |
224 | |
225 | SIGNATURE_LEN |
226 | }) |
227 | } |
228 | } |
229 | |
230 | impl signature::KeyPair for Ed25519KeyPair { |
231 | type PublicKey = PublicKey; |
232 | |
233 | fn public_key(&self) -> &Self::PublicKey { |
234 | &self.public_key |
235 | } |
236 | } |
237 | |
238 | #[derive (Clone, Copy)] |
239 | pub struct PublicKey([u8; ED25519_PUBLIC_KEY_LEN]); |
240 | |
241 | impl AsRef<[u8]> for PublicKey { |
242 | fn as_ref(&self) -> &[u8] { |
243 | self.0.as_ref() |
244 | } |
245 | } |
246 | |
247 | derive_debug_self_as_ref_hex_bytes!(PublicKey); |
248 | |
249 | fn unwrap_pkcs8( |
250 | version: pkcs8::Version, |
251 | input: untrusted::Input, |
252 | ) -> Result<(untrusted::Input, Option<untrusted::Input>), error::KeyRejected> { |
253 | let (private_key: Input<'_>, public_key: Option>) = pkcs8::unwrap_key(&PKCS8_TEMPLATE, version, input)?; |
254 | let private_key: Input<'_> = private_key |
255 | .read_all(error::Unspecified, |input| { |
256 | der::expect_tag_and_get_value(input, der::Tag::OctetString) |
257 | }) |
258 | .map_err(|error::Unspecified| error::KeyRejected::invalid_encoding())?; |
259 | Ok((private_key, public_key)) |
260 | } |
261 | |
262 | type Prefix = [u8; PREFIX_LEN]; |
263 | const PREFIX_LEN: usize = digest::SHA512_OUTPUT_LEN - SCALAR_LEN; |
264 | |
265 | const SIGNATURE_LEN: usize = ELEM_LEN + SCALAR_LEN; |
266 | |
267 | type Seed = [u8; SEED_LEN]; |
268 | const SEED_LEN: usize = 32; |
269 | |
270 | static PKCS8_TEMPLATE: pkcs8::Template = pkcs8::Template { |
271 | bytes: include_bytes!("ed25519_pkcs8_v2_template.der" ), |
272 | alg_id_range: core::ops::Range { start: 7, end: 12 }, |
273 | curve_id_index: 0, |
274 | private_key_index: 0x10, |
275 | }; |
276 | |