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 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. |
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()`. |
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. |
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. |
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 | |
172 | 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`. |
189 | 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 | |
231 | 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 { |
240 | fn as_ref(&self) -> &[u8] { |
241 | self.0.as_ref() |
242 | } |
243 | } |
244 | |
245 | derive_debug_self_as_ref_hex_bytes!(PublicKey); |
246 | |
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: Input<'_>, public_key: Option>) = pkcs8::unwrap_key(&PKCS8_TEMPLATE, version, input)?; |
252 | let private_key: Input<'_> = 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 | |