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(¶meters_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