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
17use super::{super::ops::*, eddsa_digest, ED25519_PUBLIC_KEY_LEN};
18use 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.
26pub 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
37derive_debug_via_field!(Ed25519KeyPair, stringify!(Ed25519KeyPair), public_key);
38
39impl 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
228impl 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)]
237pub struct PublicKey([u8; ED25519_PUBLIC_KEY_LEN]);
238
239impl AsRef<[u8]> for PublicKey {
240 fn as_ref(&self) -> &[u8] {
241 self.0.as_ref()
242 }
243}
244
245derive_debug_self_as_ref_hex_bytes!(PublicKey);
246
247fn 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
260type Prefix = [u8; PREFIX_LEN];
261const PREFIX_LEN: usize = digest::SHA512_OUTPUT_LEN - SCALAR_LEN;
262
263const SIGNATURE_LEN: usize = ELEM_LEN + SCALAR_LEN;
264
265type Seed = [u8; SEED_LEN];
266const SEED_LEN: usize = 32;
267
268static 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