• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Functionality related to elliptic curve support.
2 
3 use super::{CurveType, KeyMaterial, OpaqueOr};
4 use crate::{km_err, try_to_vec, Error, FallibleAllocExt};
5 use alloc::vec::Vec;
6 use der::{AnyRef, Decode};
7 use kmr_wire::{coset, keymint::EcCurve, rpc, KeySizeInBits};
8 use spki::{AlgorithmIdentifier, SubjectPublicKeyInfo};
9 use zeroize::ZeroizeOnDrop;
10 
11 /// Size (in bytes) of a curve 25519 private key.
12 pub const CURVE25519_PRIV_KEY_LEN: usize = 32;
13 
14 /// Maximum message size for Ed25519 Signing operations.
15 pub const MAX_ED25519_MSG_SIZE: usize = 16 * 1024;
16 
17 /// Marker value used to indicate that a public key is for RKP test mode.
18 pub const RKP_TEST_KEY_CBOR_MARKER: i64 = -70000;
19 
20 /// Initial byte of SEC1 public key encoding that indicates an uncompressed point.
21 pub const SEC1_UNCOMPRESSED_PREFIX: u8 = 0x04;
22 
23 /// OID value for general-use NIST EC keys held in PKCS#8 and X.509; see RFC 5480 s2.1.1.
24 pub const X509_NIST_OID: pkcs8::ObjectIdentifier =
25     pkcs8::ObjectIdentifier::new_unwrap("1.2.840.10045.2.1");
26 
27 /// OID value for Ed25519 keys held in PKCS#8 and X.509; see RFC 8410 s3.
28 pub const X509_ED25519_OID: pkcs8::ObjectIdentifier =
29     pkcs8::ObjectIdentifier::new_unwrap("1.3.101.112");
30 
31 /// OID value for X25519 keys held in PKCS#8 and X.509; see RFC 8410 s3.
32 pub const X509_X25519_OID: pkcs8::ObjectIdentifier =
33     pkcs8::ObjectIdentifier::new_unwrap("1.3.101.110");
34 
35 /// OID value for PKCS#1 signature with SHA-256 and ECDSA, see RFC 5758 s3.2.
36 pub const ECDSA_SHA256_SIGNATURE_OID: pkcs8::ObjectIdentifier =
37     pkcs8::ObjectIdentifier::new_unwrap("1.2.840.10045.4.3.2");
38 
39 /// OID value in `AlgorithmIdentifier.parameters` for P-224; see RFC 5480 s2.1.1.1.
40 pub const ALGO_PARAM_P224_OID: pkcs8::ObjectIdentifier =
41     pkcs8::ObjectIdentifier::new_unwrap("1.3.132.0.33");
42 
43 /// OID value in `AlgorithmIdentifier.parameters` for P-256; see RFC 5480 s2.1.1.1.
44 pub const ALGO_PARAM_P256_OID: pkcs8::ObjectIdentifier =
45     pkcs8::ObjectIdentifier::new_unwrap("1.2.840.10045.3.1.7");
46 
47 /// OID value in `AlgorithmIdentifier.parameters` for P-384; see RFC 5480 s2.1.1.1.
48 pub const ALGO_PARAM_P384_OID: pkcs8::ObjectIdentifier =
49     pkcs8::ObjectIdentifier::new_unwrap("1.3.132.0.34");
50 
51 /// OID value in `AlgorithmIdentifier.parameters` for P-521; see RFC 5480 s2.1.1.1.
52 pub const ALGO_PARAM_P521_OID: pkcs8::ObjectIdentifier =
53     pkcs8::ObjectIdentifier::new_unwrap("1.3.132.0.35");
54 
55 /// Subset of `EcCurve` values that are NIST curves.
56 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
57 #[repr(i32)]
58 pub enum NistCurve {
59     P224 = 0,
60     P256 = 1,
61     P384 = 2,
62     P521 = 3,
63 }
64 
65 impl NistCurve {
66     // Curve coordinate size in bytes.
coord_len(&self) -> usize67     pub fn coord_len(&self) -> usize {
68         match self {
69             NistCurve::P224 => 28,
70             NistCurve::P256 => 32,
71             NistCurve::P384 => 48,
72             NistCurve::P521 => 66,
73         }
74     }
75 }
76 
77 impl From<NistCurve> for EcCurve {
from(nist: NistCurve) -> EcCurve78     fn from(nist: NistCurve) -> EcCurve {
79         match nist {
80             NistCurve::P224 => EcCurve::P224,
81             NistCurve::P256 => EcCurve::P256,
82             NistCurve::P384 => EcCurve::P384,
83             NistCurve::P521 => EcCurve::P521,
84         }
85     }
86 }
87 
88 impl TryFrom<EcCurve> for NistCurve {
89     type Error = Error;
try_from(curve: EcCurve) -> Result<NistCurve, Error>90     fn try_from(curve: EcCurve) -> Result<NistCurve, Error> {
91         match curve {
92             EcCurve::P224 => Ok(NistCurve::P224),
93             EcCurve::P256 => Ok(NistCurve::P256),
94             EcCurve::P384 => Ok(NistCurve::P384),
95             EcCurve::P521 => Ok(NistCurve::P521),
96             EcCurve::Curve25519 => Err(km_err!(InvalidArgument, "curve 25519 is not a NIST curve")),
97         }
98     }
99 }
100 
101 impl OpaqueOr<Key> {
102     /// Encode into `buf` the public key information as an ASN.1 DER encodable
103     /// `SubjectPublicKeyInfo`, as described in RFC 5280 section 4.1.
104     ///
105     /// ```asn1
106     /// SubjectPublicKeyInfo  ::=  SEQUENCE  {
107     ///    algorithm            AlgorithmIdentifier,
108     ///    subjectPublicKey     BIT STRING  }
109     ///
110     /// AlgorithmIdentifier  ::=  SEQUENCE  {
111     ///    algorithm               OBJECT IDENTIFIER,
112     ///    parameters              ANY DEFINED BY algorithm OPTIONAL  }
113     /// ```
114     ///
115     /// For NIST curve EC keys, the contents are described in RFC 5480 section 2.1.
116     /// - The `AlgorithmIdentifier` has an `algorithm` OID of 1.2.840.10045.2.1.
117     /// - The `AlgorithmIdentifier` has `parameters` that hold an OID identifying the curve, here
118     ///   one of:
119     ///    - P-224: 1.3.132.0.33
120     ///    - P-256: 1.2.840.10045.3.1.7
121     ///    - P-384: 1.3.132.0.34
122     ///    - P-521: 1.3.132.0.35
123     /// - The `subjectPublicKey` bit string holds an ASN.1 DER-encoded `OCTET STRING` that contains
124     ///   a SEC-1 encoded public key.  The first byte indicates the format:
125     ///    - 0x04: uncompressed, followed by x || y coordinates
126     ///    - 0x03: compressed, followed by x coordinate (and with a odd y coordinate)
127     ///    - 0x02: compressed, followed by x coordinate (and with a even y coordinate)
128     ///
129     /// For Ed25519 keys, the contents of the `AlgorithmIdentifier` are described in RFC 8410
130     /// section 3.
131     /// - The `algorithm` has an OID of 1.3.101.112.
132     /// - The `parameters` are absent.
133     ///
134     /// The `subjectPublicKey` holds the raw key bytes.
135     ///
136     /// For X25519 keys, the contents of the `AlgorithmIdentifier` are described in RFC 8410
137     /// section 3.
138     /// - The `algorithm` has an OID of 1.3.101.110.
139     /// - The `parameters` are absent.
140     ///
141     /// The `subjectPublicKey` holds the raw key bytes.
subject_public_key_info<'a>( &'a self, buf: &'a mut Vec<u8>, ec: &dyn super::Ec, curve: &EcCurve, curve_type: &CurveType, ) -> Result<SubjectPublicKeyInfo<'a>, Error>142     pub fn subject_public_key_info<'a>(
143         &'a self,
144         buf: &'a mut Vec<u8>,
145         ec: &dyn super::Ec,
146         curve: &EcCurve,
147         curve_type: &CurveType,
148     ) -> Result<SubjectPublicKeyInfo<'a>, Error> {
149         buf.try_extend_from_slice(&ec.subject_public_key(self)?)?;
150         let (oid, parameters) = match curve_type {
151             CurveType::Nist => {
152                 let nist_curve: NistCurve = (*curve).try_into()?;
153                 let params_oid = match nist_curve {
154                     NistCurve::P224 => &ALGO_PARAM_P224_OID,
155                     NistCurve::P256 => &ALGO_PARAM_P256_OID,
156                     NistCurve::P384 => &ALGO_PARAM_P384_OID,
157                     NistCurve::P521 => &ALGO_PARAM_P521_OID,
158                 };
159                 (X509_NIST_OID, Some(AnyRef::from(params_oid)))
160             }
161             CurveType::EdDsa => (X509_ED25519_OID, None),
162             CurveType::Xdh => (X509_X25519_OID, None),
163         };
164         Ok(SubjectPublicKeyInfo {
165             algorithm: AlgorithmIdentifier { oid, parameters },
166             subject_public_key: buf,
167         })
168     }
169 
public_cose_key( &self, ec: &dyn super::Ec, curve: EcCurve, curve_type: CurveType, purpose: CoseKeyPurpose, key_id: Option<Vec<u8>>, test_mode: rpc::TestMode, ) -> Result<coset::CoseKey, Error>170     pub fn public_cose_key(
171         &self,
172         ec: &dyn super::Ec,
173         curve: EcCurve,
174         curve_type: CurveType,
175         purpose: CoseKeyPurpose,
176         key_id: Option<Vec<u8>>,
177         test_mode: rpc::TestMode,
178     ) -> Result<coset::CoseKey, Error> {
179         let nist_algo = match purpose {
180             CoseKeyPurpose::Agree => coset::iana::Algorithm::ECDH_ES_HKDF_256,
181             CoseKeyPurpose::Sign => coset::iana::Algorithm::ES256,
182         };
183 
184         let pub_key = ec.subject_public_key(self)?;
185         let mut builder = match curve_type {
186             CurveType::Nist => {
187                 let nist_curve: NistCurve = curve.try_into()?;
188                 let (x, y) = coordinates_from_pub_key(pub_key, nist_curve)?;
189                 let cose_nist_curve = match nist_curve {
190                     NistCurve::P224 => {
191                         // P-224 is not supported by COSE: there is no value in the COSE Elliptic
192                         // Curve registry for it.
193                         return Err(km_err!(Unimplemented, "no COSE support for P-224"));
194                     }
195                     NistCurve::P256 => coset::iana::EllipticCurve::P_256,
196                     NistCurve::P384 => coset::iana::EllipticCurve::P_384,
197                     NistCurve::P521 => coset::iana::EllipticCurve::P_521,
198                 };
199                 coset::CoseKeyBuilder::new_ec2_pub_key(cose_nist_curve, x, y).algorithm(nist_algo)
200             }
201             CurveType::EdDsa => coset::CoseKeyBuilder::new_okp_key()
202                 .param(
203                     coset::iana::OkpKeyParameter::Crv as i64,
204                     coset::cbor::value::Value::from(coset::iana::EllipticCurve::Ed25519 as u64),
205                 )
206                 .param(
207                     coset::iana::OkpKeyParameter::X as i64,
208                     coset::cbor::value::Value::from(pub_key),
209                 )
210                 .algorithm(coset::iana::Algorithm::EdDSA),
211             CurveType::Xdh => coset::CoseKeyBuilder::new_okp_key()
212                 .param(
213                     coset::iana::OkpKeyParameter::Crv as i64,
214                     coset::cbor::value::Value::from(coset::iana::EllipticCurve::X25519 as u64),
215                 )
216                 .param(
217                     coset::iana::OkpKeyParameter::X as i64,
218                     coset::cbor::value::Value::from(pub_key),
219                 )
220                 .algorithm(coset::iana::Algorithm::ECDH_ES_HKDF_256),
221         };
222 
223         if let Some(key_id) = key_id {
224             builder = builder.key_id(key_id);
225         }
226         if test_mode == rpc::TestMode(true) {
227             builder = builder.param(RKP_TEST_KEY_CBOR_MARKER, coset::cbor::value::Value::Null);
228         }
229         Ok(builder.build())
230     }
231 }
232 
233 /// Elliptic curve private key material.
234 #[derive(Clone, PartialEq, Eq)]
235 pub enum Key {
236     P224(NistKey),
237     P256(NistKey),
238     P384(NistKey),
239     P521(NistKey),
240     Ed25519(Ed25519Key),
241     X25519(X25519Key),
242 }
243 
244 /// Indication of the purpose for a COSE key.
245 pub enum CoseKeyPurpose {
246     Agree,
247     Sign,
248 }
249 
250 impl Key {
251     /// Return the private key material.
private_key_bytes(&self) -> &[u8]252     pub fn private_key_bytes(&self) -> &[u8] {
253         match self {
254             Key::P224(key) => &key.0,
255             Key::P256(key) => &key.0,
256             Key::P384(key) => &key.0,
257             Key::P521(key) => &key.0,
258             Key::Ed25519(key) => &key.0,
259             Key::X25519(key) => &key.0,
260         }
261     }
262 
263     /// Return the type of curve.
curve_type(&self) -> CurveType264     pub fn curve_type(&self) -> CurveType {
265         match self {
266             Key::P224(_) | Key::P256(_) | Key::P384(_) | Key::P521(_) => CurveType::Nist,
267             Key::Ed25519(_) => CurveType::EdDsa,
268             Key::X25519(_) => CurveType::Xdh,
269         }
270     }
271 
272     /// Return the curve.
curve(&self) -> EcCurve273     pub fn curve(&self) -> EcCurve {
274         match self {
275             Key::P224(_) => EcCurve::P224,
276             Key::P256(_) => EcCurve::P256,
277             Key::P384(_) => EcCurve::P384,
278             Key::P521(_) => EcCurve::P521,
279             Key::Ed25519(_) => EcCurve::Curve25519,
280             Key::X25519(_) => EcCurve::Curve25519,
281         }
282     }
283 }
284 
285 /// A NIST EC key, in the form of an ASN.1 DER encoding of a `ECPrivateKey` structure,
286 /// as specified by RFC 5915 section 3:
287 ///
288 /// ```asn1
289 /// ECPrivateKey ::= SEQUENCE {
290 ///    version        INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1),
291 ///    privateKey     OCTET STRING,
292 ///    parameters [0] ECParameters {{ NamedCurve }} OPTIONAL,
293 ///    publicKey  [1] BIT STRING OPTIONAL
294 /// }
295 /// ```
296 #[derive(Clone, PartialEq, Eq, ZeroizeOnDrop)]
297 pub struct NistKey(pub Vec<u8>);
298 
299 // Helper function to return the (x,y) coordinates, given the public key as a SEC-1 encoded
300 // uncompressed point. 0x04: uncompressed, followed by x || y coordinates.
coordinates_from_pub_key( pub_key: Vec<u8>, curve: NistCurve, ) -> Result<(Vec<u8>, Vec<u8>), Error>301 pub fn coordinates_from_pub_key(
302     pub_key: Vec<u8>,
303     curve: NistCurve,
304 ) -> Result<(Vec<u8>, Vec<u8>), Error> {
305     let coord_len = curve.coord_len();
306     if pub_key.len() != (1 + 2 * coord_len) {
307         return Err(km_err!(
308             UnknownError,
309             "unexpected SEC1 pubkey len of {} for {:?}",
310             pub_key.len(),
311             curve
312         ));
313     }
314     if pub_key[0] != SEC1_UNCOMPRESSED_PREFIX {
315         return Err(km_err!(
316             UnknownError,
317             "unexpected SEC1 pubkey initial byte {} for {:?}",
318             pub_key[0],
319             curve
320         ));
321     }
322     Ok((try_to_vec(&pub_key[1..1 + coord_len])?, try_to_vec(&pub_key[1 + coord_len..])?))
323 }
324 
325 /// An Ed25519 private key.
326 #[derive(Clone, PartialEq, Eq, ZeroizeOnDrop)]
327 pub struct Ed25519Key(pub [u8; CURVE25519_PRIV_KEY_LEN]);
328 
329 /// An X25519 private key.
330 #[derive(Clone, PartialEq, Eq, ZeroizeOnDrop)]
331 pub struct X25519Key(pub [u8; CURVE25519_PRIV_KEY_LEN]);
332 
333 /// Return the OID used in an `AlgorithmIdentifier` for signatures produced by this curve.
curve_to_signing_oid(curve: EcCurve) -> pkcs8::ObjectIdentifier334 pub fn curve_to_signing_oid(curve: EcCurve) -> pkcs8::ObjectIdentifier {
335     match curve {
336         EcCurve::P224 | EcCurve::P256 | EcCurve::P384 | EcCurve::P521 => ECDSA_SHA256_SIGNATURE_OID,
337         EcCurve::Curve25519 => X509_ED25519_OID,
338     }
339 }
340 
341 /// Return the key size for a curve.
curve_to_key_size(curve: EcCurve) -> KeySizeInBits342 pub fn curve_to_key_size(curve: EcCurve) -> KeySizeInBits {
343     KeySizeInBits(match curve {
344         EcCurve::P224 => 224,
345         EcCurve::P256 => 256,
346         EcCurve::P384 => 384,
347         EcCurve::P521 => 521,
348         EcCurve::Curve25519 => 256,
349     })
350 }
351 
352 /// Import an NIST EC key in SEC1 ECPrivateKey format.
import_sec1_private_key(data: &[u8]) -> Result<KeyMaterial, Error>353 pub fn import_sec1_private_key(data: &[u8]) -> Result<KeyMaterial, Error> {
354     let ec_key = sec1::EcPrivateKey::from_der(data)?;
355     let ec_parameters = ec_key.parameters.ok_or(km_err!(
356         InvalidArgument,
357         "sec1 formatted EC private key didn't have a parameters field"
358     ))?;
359     let parameters_oid = ec_parameters.named_curve().ok_or(km_err!(
360         InvalidArgument,
361         "couldn't retrieve parameters oid from sec1 ECPrivateKey formatted ec key parameters"
362     ))?;
363     let algorithm =
364         AlgorithmIdentifier { oid: X509_NIST_OID, parameters: Some(AnyRef::from(&parameters_oid)) };
365     let pkcs8_key = pkcs8::PrivateKeyInfo::new(algorithm, data);
366     import_pkcs8_key_impl(&pkcs8_key)
367 }
368 
369 /// Import an EC key in PKCS#8 format.
import_pkcs8_key(data: &[u8]) -> Result<KeyMaterial, Error>370 pub fn import_pkcs8_key(data: &[u8]) -> Result<KeyMaterial, Error> {
371     let key_info = pkcs8::PrivateKeyInfo::try_from(data)
372         .map_err(|_| km_err!(InvalidArgument, "failed to parse PKCS#8 EC key"))?;
373     import_pkcs8_key_impl(&key_info)
374 }
375 
376 /// Import a `pkcs8::PrivateKeyInfo` EC key.
import_pkcs8_key_impl(key_info: &pkcs8::PrivateKeyInfo) -> Result<KeyMaterial, Error>377 fn import_pkcs8_key_impl(key_info: &pkcs8::PrivateKeyInfo) -> Result<KeyMaterial, Error> {
378     let algo_params = key_info.algorithm.parameters;
379     match key_info.algorithm.oid {
380         X509_NIST_OID => {
381             let algo_params = algo_params.ok_or_else(|| {
382                 km_err!(
383                     InvalidArgument,
384                     "missing PKCS#8 parameters for NIST curve import under OID {:?}",
385                     key_info.algorithm.oid
386                 )
387             })?;
388             let curve_oid = algo_params
389                 .oid()
390                 .map_err(|_e| km_err!(InvalidArgument, "imported key has no OID parameter"))?;
391             let (curve, key) = match curve_oid {
392                 ALGO_PARAM_P224_OID => {
393                     (EcCurve::P224, Key::P224(NistKey(try_to_vec(key_info.private_key)?)))
394                 }
395                 ALGO_PARAM_P256_OID => {
396                     (EcCurve::P256, Key::P256(NistKey(try_to_vec(key_info.private_key)?)))
397                 }
398                 ALGO_PARAM_P384_OID => {
399                     (EcCurve::P384, Key::P384(NistKey(try_to_vec(key_info.private_key)?)))
400                 }
401                 ALGO_PARAM_P521_OID => {
402                     (EcCurve::P521, Key::P521(NistKey(try_to_vec(key_info.private_key)?)))
403                 }
404                 oid => {
405                     return Err(km_err!(
406                         ImportParameterMismatch,
407                         "imported key has unknown OID {:?}",
408                         oid,
409                     ))
410                 }
411             };
412             Ok(KeyMaterial::Ec(curve, CurveType::Nist, key.into()))
413         }
414         X509_ED25519_OID => {
415             if algo_params.is_some() {
416                 Err(km_err!(InvalidArgument, "unexpected PKCS#8 parameters for Ed25519 import"))
417             } else {
418                 // For Ed25519 the PKCS#8 `privateKey` field holds a `CurvePrivateKey`
419                 // (RFC 8410 s7) that is an OCTET STRING holding the raw key.  As this is DER,
420                 // this is just a 2 byte prefix (0x04 = OCTET STRING, 0x20 = length of raw key).
421                 if key_info.private_key.len() != 2 + CURVE25519_PRIV_KEY_LEN
422                     || key_info.private_key[0] != 0x04
423                     || key_info.private_key[1] != 0x20
424                 {
425                     return Err(km_err!(InvalidArgument, "unexpected CurvePrivateKey contents"));
426                 }
427                 import_raw_ed25519_key(&key_info.private_key[2..])
428             }
429         }
430         X509_X25519_OID => {
431             if algo_params.is_some() {
432                 Err(km_err!(InvalidArgument, "unexpected PKCS#8 parameters for X25519 import",))
433             } else {
434                 // For X25519 the PKCS#8 `privateKey` field holds a `CurvePrivateKey`
435                 // (RFC 8410 s7) that is an OCTET STRING holding the raw key.  As this is DER,
436                 // this is just a 2 byte prefix (0x04 = OCTET STRING, 0x20 = length of raw key).
437                 if key_info.private_key.len() != 2 + CURVE25519_PRIV_KEY_LEN
438                     || key_info.private_key[0] != 0x04
439                     || key_info.private_key[1] != 0x20
440                 {
441                     return Err(km_err!(InvalidArgument, "unexpected CurvePrivateKey contents"));
442                 }
443                 import_raw_x25519_key(&key_info.private_key[2..])
444             }
445         }
446         _ => Err(km_err!(
447             InvalidArgument,
448             "unexpected OID {:?} for PKCS#8 EC key import",
449             key_info.algorithm.oid,
450         )),
451     }
452 }
453 
454 /// Import a 32-byte raw Ed25519 key.
import_raw_ed25519_key(data: &[u8]) -> Result<KeyMaterial, Error>455 pub fn import_raw_ed25519_key(data: &[u8]) -> Result<KeyMaterial, Error> {
456     let key = data.try_into().map_err(|_e| {
457         km_err!(InvalidInputLength, "import Ed25519 key of incorrect len {}", data.len())
458     })?;
459     Ok(KeyMaterial::Ec(EcCurve::Curve25519, CurveType::EdDsa, Key::Ed25519(Ed25519Key(key)).into()))
460 }
461 
462 /// Import a 32-byte raw X25519 key.
import_raw_x25519_key(data: &[u8]) -> Result<KeyMaterial, Error>463 pub fn import_raw_x25519_key(data: &[u8]) -> Result<KeyMaterial, Error> {
464     let key = data.try_into().map_err(|_e| {
465         km_err!(InvalidInputLength, "import X25519 key of incorrect len {}", data.len())
466     })?;
467     Ok(KeyMaterial::Ec(EcCurve::Curve25519, CurveType::Xdh, Key::X25519(X25519Key(key)).into()))
468 }
469