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 digest, error,
20 io::der,
21 pkcs8, rand,
22 signature::{self, KeyPair as SigningKeyPair},
23 };
24 use core::convert::TryInto;
25
26 /// An Ed25519 key pair, for signing.
27 pub struct Ed25519KeyPair {
28 // RFC 8032 Section 5.1.6 calls this *s*.
29 private_scalar: Scalar,
30
31 // RFC 8032 Section 5.1.6 calls this *prefix*.
32 private_prefix: Prefix,
33
34 // RFC 8032 Section 5.1.5 calls this *A*.
35 public_key: PublicKey,
36 }
37
38 derive_debug_via_field!(Ed25519KeyPair, stringify!(Ed25519KeyPair), public_key);
39
40 impl Ed25519KeyPair {
41 /// Generates a new key pair and returns the key pair serialized as a
42 /// PKCS#8 document.
43 ///
44 /// The PKCS#8 document will be a v2 `OneAsymmetricKey` with the public key,
45 /// as described in [RFC 5958 Section 2]; see [RFC 8410 Section 10.3] for an
46 /// example.
47 ///
48 /// [RFC 5958 Section 2]: https://tools.ietf.org/html/rfc5958#section-2
49 /// [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>50 pub fn generate_pkcs8(
51 rng: &dyn rand::SecureRandom,
52 ) -> Result<pkcs8::Document, error::Unspecified> {
53 let seed: [u8; SEED_LEN] = rand::generate(rng)?.expose();
54 let key_pair = Self::from_seed_(&seed);
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 /// If you need to parse PKCS#8 v1 files (without the public key) then use
75 /// `Ed25519KeyPair::from_pkcs8_maybe_unchecked()` instead.
from_pkcs8(pkcs8: &[u8]) -> Result<Self, error::KeyRejected>76 pub fn from_pkcs8(pkcs8: &[u8]) -> Result<Self, error::KeyRejected> {
77 let (seed, public_key) =
78 unwrap_pkcs8(pkcs8::Version::V2Only, untrusted::Input::from(pkcs8))?;
79 Self::from_seed_and_public_key(
80 seed.as_slice_less_safe(),
81 public_key.unwrap().as_slice_less_safe(),
82 )
83 }
84
85 /// Constructs an Ed25519 key pair by parsing an unencrypted PKCS#8 v1 or v2
86 /// Ed25519 private key.
87 ///
88 /// `openssl genpkey -algorithm ED25519` generates PKCS# v1 keys.
89 ///
90 /// It is recommended to use `Ed25519KeyPair::from_pkcs8()`, which accepts
91 /// only PKCS#8 v2 files that contain the public key.
92 /// `from_pkcs8_maybe_unchecked()` parses PKCS#2 files exactly like
93 /// `from_pkcs8()`. It also accepts v1 files. PKCS#8 v1 files do not contain
94 /// the public key, so when a v1 file is parsed the public key will be
95 /// computed from the private key, and there will be no consistency check
96 /// between the public key and the private key.
97 ///
98 /// PKCS#8 v2 files are parsed exactly like `Ed25519KeyPair::from_pkcs8()`.
from_pkcs8_maybe_unchecked(pkcs8: &[u8]) -> Result<Self, error::KeyRejected>99 pub fn from_pkcs8_maybe_unchecked(pkcs8: &[u8]) -> Result<Self, error::KeyRejected> {
100 let (seed, public_key) =
101 unwrap_pkcs8(pkcs8::Version::V1OrV2, untrusted::Input::from(pkcs8))?;
102 if let Some(public_key) = public_key {
103 Self::from_seed_and_public_key(
104 seed.as_slice_less_safe(),
105 public_key.as_slice_less_safe(),
106 )
107 } else {
108 Self::from_seed_unchecked(seed.as_slice_less_safe())
109 }
110 }
111
112 /// Constructs an Ed25519 key pair from the private key seed `seed` and its
113 /// public key `public_key`.
114 ///
115 /// It is recommended to use `Ed25519KeyPair::from_pkcs8()` instead.
116 ///
117 /// The private and public keys will be verified to be consistent with each
118 /// other. This helps avoid misuse of the key (e.g. accidentally swapping
119 /// the private key and public key, or using the wrong private key for the
120 /// public key). This also detects any corruption of the public or private
121 /// key.
from_seed_and_public_key( seed: &[u8], public_key: &[u8], ) -> Result<Self, error::KeyRejected>122 pub fn from_seed_and_public_key(
123 seed: &[u8],
124 public_key: &[u8],
125 ) -> Result<Self, error::KeyRejected> {
126 let pair = Self::from_seed_unchecked(seed)?;
127
128 // This implicitly verifies that `public_key` is the right length.
129 // XXX: This rejects ~18 keys when they are partially reduced, though
130 // those keys are virtually impossible to find.
131 if public_key != pair.public_key.as_ref() {
132 let err = if public_key.len() != pair.public_key.as_ref().len() {
133 error::KeyRejected::invalid_encoding()
134 } else {
135 error::KeyRejected::inconsistent_components()
136 };
137 return Err(err);
138 }
139
140 Ok(pair)
141 }
142
143 /// Constructs a Ed25519 key pair from the private key seed `seed`.
144 ///
145 /// It is recommended to use `Ed25519KeyPair::from_pkcs8()` instead. When
146 /// that is not practical, it is recommended to use
147 /// `Ed25519KeyPair::from_seed_and_public_key()` instead.
148 ///
149 /// Since the public key is not given, the public key will be computed from
150 /// the private key. It is not possible to detect misuse or corruption of
151 /// the private key since the public key isn't given as input.
from_seed_unchecked(seed: &[u8]) -> Result<Self, error::KeyRejected>152 pub fn from_seed_unchecked(seed: &[u8]) -> Result<Self, error::KeyRejected> {
153 let seed = seed
154 .try_into()
155 .map_err(|_| error::KeyRejected::invalid_encoding())?;
156 Ok(Self::from_seed_(seed))
157 }
158
from_seed_(seed: &Seed) -> Self159 fn from_seed_(seed: &Seed) -> Self {
160 let h = digest::digest(&digest::SHA512, seed);
161 let (private_scalar, private_prefix) = h.as_ref().split_at(SCALAR_LEN);
162
163 let private_scalar =
164 MaskedScalar::from_bytes_masked(private_scalar.try_into().unwrap()).into();
165
166 let mut a = ExtPoint::new_at_infinity();
167 unsafe {
168 GFp_x25519_ge_scalarmult_base(&mut a, &private_scalar);
169 }
170
171 Self {
172 private_scalar,
173 private_prefix: private_prefix.try_into().unwrap(),
174 public_key: PublicKey(a.into_encoded_point()),
175 }
176 }
177
178 /// Returns the signature of the message `msg`.
sign(&self, msg: &[u8]) -> signature::Signature179 pub fn sign(&self, msg: &[u8]) -> signature::Signature {
180 signature::Signature::new(|signature_bytes| {
181 extern "C" {
182 fn GFp_x25519_sc_muladd(
183 s: &mut [u8; SCALAR_LEN],
184 a: &Scalar,
185 b: &Scalar,
186 c: &Scalar,
187 );
188 }
189
190 let (signature_bytes, _unused) = signature_bytes.split_at_mut(ELEM_LEN + SCALAR_LEN);
191 let (signature_r, signature_s) = signature_bytes.split_at_mut(ELEM_LEN);
192 let nonce = {
193 let mut ctx = digest::Context::new(&digest::SHA512);
194 ctx.update(&self.private_prefix);
195 ctx.update(msg);
196 ctx.finish()
197 };
198 let nonce = Scalar::from_sha512_digest_reduced(nonce);
199
200 let mut r = ExtPoint::new_at_infinity();
201 unsafe {
202 GFp_x25519_ge_scalarmult_base(&mut r, &nonce);
203 }
204 signature_r.copy_from_slice(&r.into_encoded_point());
205 let hram_digest = eddsa_digest(signature_r, &self.public_key.as_ref(), msg);
206 let hram = Scalar::from_sha512_digest_reduced(hram_digest);
207 unsafe {
208 GFp_x25519_sc_muladd(
209 signature_s.try_into().unwrap(),
210 &hram,
211 &self.private_scalar,
212 &nonce,
213 );
214 }
215
216 SIGNATURE_LEN
217 })
218 }
219 }
220
221 impl signature::KeyPair for Ed25519KeyPair {
222 type PublicKey = PublicKey;
223
public_key(&self) -> &Self::PublicKey224 fn public_key(&self) -> &Self::PublicKey {
225 &self.public_key
226 }
227 }
228
229 #[derive(Clone, Copy)]
230 pub struct PublicKey([u8; ED25519_PUBLIC_KEY_LEN]);
231
232 impl AsRef<[u8]> for PublicKey {
as_ref(&self) -> &[u8]233 fn as_ref(&self) -> &[u8] {
234 self.0.as_ref()
235 }
236 }
237
238 derive_debug_self_as_ref_hex_bytes!(PublicKey);
239
unwrap_pkcs8( version: pkcs8::Version, input: untrusted::Input, ) -> Result<(untrusted::Input, Option<untrusted::Input>), error::KeyRejected>240 fn unwrap_pkcs8(
241 version: pkcs8::Version,
242 input: untrusted::Input,
243 ) -> Result<(untrusted::Input, Option<untrusted::Input>), error::KeyRejected> {
244 let (private_key, public_key) = pkcs8::unwrap_key(&PKCS8_TEMPLATE, version, input)?;
245 let private_key = private_key
246 .read_all(error::Unspecified, |input| {
247 der::expect_tag_and_get_value(input, der::Tag::OctetString)
248 })
249 .map_err(|error::Unspecified| error::KeyRejected::invalid_encoding())?;
250 Ok((private_key, public_key))
251 }
252
253 extern "C" {
GFp_x25519_ge_scalarmult_base(h: &mut ExtPoint, a: &Scalar)254 fn GFp_x25519_ge_scalarmult_base(h: &mut ExtPoint, a: &Scalar);
255 }
256
257 type Prefix = [u8; PREFIX_LEN];
258 const PREFIX_LEN: usize = digest::SHA512_OUTPUT_LEN - SCALAR_LEN;
259
260 const SIGNATURE_LEN: usize = ELEM_LEN + SCALAR_LEN;
261
262 type Seed = [u8; SEED_LEN];
263 const SEED_LEN: usize = 32;
264
265 static PKCS8_TEMPLATE: pkcs8::Template = pkcs8::Template {
266 bytes: include_bytes!("ed25519_pkcs8_v2_template.der"),
267 alg_id_range: core::ops::Range { start: 7, end: 12 },
268 curve_id_index: 0,
269 private_key_index: 0x10,
270 };
271