1 // Copyright 2022, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 //! Functionality related to elliptic curve support.
16
17 use super::{CurveType, KeyMaterial, OpaqueOr};
18 use crate::{der_err, km_err, try_to_vec, vec_try, Error, FallibleAllocExt};
19 use alloc::vec::Vec;
20 use der::{asn1::BitStringRef, AnyRef, Decode, Encode, Sequence};
21 use kmr_wire::{coset, keymint::EcCurve, rpc, KeySizeInBits};
22 use spki::{AlgorithmIdentifier, SubjectPublicKeyInfo, SubjectPublicKeyInfoRef};
23 use zeroize::ZeroizeOnDrop;
24
25 /// Size (in bytes) of a curve 25519 private key.
26 pub const CURVE25519_PRIV_KEY_LEN: usize = 32;
27
28 /// Maximum message size for Ed25519 Signing operations.
29 pub const MAX_ED25519_MSG_SIZE: usize = 16 * 1024;
30
31 /// Marker value used to indicate that a public key is for RKP test mode.
32 pub const RKP_TEST_KEY_CBOR_MARKER: i64 = -70000;
33
34 /// Initial byte of SEC1 public key encoding that indicates an uncompressed point.
35 pub const SEC1_UNCOMPRESSED_PREFIX: u8 = 0x04;
36
37 /// OID value for general-use NIST EC keys held in PKCS#8 and X.509; see RFC 5480 s2.1.1.
38 pub const X509_NIST_OID: pkcs8::ObjectIdentifier =
39 pkcs8::ObjectIdentifier::new_unwrap("1.2.840.10045.2.1");
40
41 /// OID value for Ed25519 keys held in PKCS#8 and X.509; see RFC 8410 s3.
42 pub const X509_ED25519_OID: pkcs8::ObjectIdentifier =
43 pkcs8::ObjectIdentifier::new_unwrap("1.3.101.112");
44
45 /// OID value for X25519 keys held in PKCS#8 and X.509; see RFC 8410 s3.
46 pub const X509_X25519_OID: pkcs8::ObjectIdentifier =
47 pkcs8::ObjectIdentifier::new_unwrap("1.3.101.110");
48
49 /// OID value for PKCS#1 signature with SHA-256 and ECDSA, see RFC 5758 s3.2.
50 pub const ECDSA_SHA256_SIGNATURE_OID: pkcs8::ObjectIdentifier =
51 pkcs8::ObjectIdentifier::new_unwrap("1.2.840.10045.4.3.2");
52
53 /// OID value in `AlgorithmIdentifier.parameters` for P-224; see RFC 5480 s2.1.1.1.
54 pub const ALGO_PARAM_P224_OID: pkcs8::ObjectIdentifier =
55 pkcs8::ObjectIdentifier::new_unwrap("1.3.132.0.33");
56
57 /// OID value in `AlgorithmIdentifier.parameters` for P-256; see RFC 5480 s2.1.1.1.
58 pub const ALGO_PARAM_P256_OID: pkcs8::ObjectIdentifier =
59 pkcs8::ObjectIdentifier::new_unwrap("1.2.840.10045.3.1.7");
60
61 /// OID value in `AlgorithmIdentifier.parameters` for P-384; see RFC 5480 s2.1.1.1.
62 pub const ALGO_PARAM_P384_OID: pkcs8::ObjectIdentifier =
63 pkcs8::ObjectIdentifier::new_unwrap("1.3.132.0.34");
64
65 /// OID value in `AlgorithmIdentifier.parameters` for P-521; see RFC 5480 s2.1.1.1.
66 pub const ALGO_PARAM_P521_OID: pkcs8::ObjectIdentifier =
67 pkcs8::ObjectIdentifier::new_unwrap("1.3.132.0.35");
68
69 /// Subset of `EcCurve` values that are NIST curves.
70 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
71 #[repr(i32)]
72 pub enum NistCurve {
73 /// P-224
74 P224 = 0,
75 /// P-256
76 P256 = 1,
77 /// P-384
78 P384 = 2,
79 /// P-521
80 P521 = 3,
81 }
82
83 impl NistCurve {
84 /// Curve coordinate size in bytes.
coord_len(&self) -> usize85 pub fn coord_len(&self) -> usize {
86 match self {
87 NistCurve::P224 => 28,
88 NistCurve::P256 => 32,
89 NistCurve::P384 => 48,
90 NistCurve::P521 => 66,
91 }
92 }
93 }
94
95 impl From<NistCurve> for EcCurve {
from(nist: NistCurve) -> EcCurve96 fn from(nist: NistCurve) -> EcCurve {
97 match nist {
98 NistCurve::P224 => EcCurve::P224,
99 NistCurve::P256 => EcCurve::P256,
100 NistCurve::P384 => EcCurve::P384,
101 NistCurve::P521 => EcCurve::P521,
102 }
103 }
104 }
105
106 impl TryFrom<EcCurve> for NistCurve {
107 type Error = Error;
try_from(curve: EcCurve) -> Result<NistCurve, Error>108 fn try_from(curve: EcCurve) -> Result<NistCurve, Error> {
109 match curve {
110 EcCurve::P224 => Ok(NistCurve::P224),
111 EcCurve::P256 => Ok(NistCurve::P256),
112 EcCurve::P384 => Ok(NistCurve::P384),
113 EcCurve::P521 => Ok(NistCurve::P521),
114 EcCurve::Curve25519 => Err(km_err!(InvalidArgument, "curve 25519 is not a NIST curve")),
115 }
116 }
117 }
118
119 impl OpaqueOr<Key> {
120 /// Encode into `buf` the public key information as an ASN.1 DER encodable
121 /// `SubjectPublicKeyInfo`, as described in RFC 5280 section 4.1.
122 ///
123 /// ```asn1
124 /// SubjectPublicKeyInfo ::= SEQUENCE {
125 /// algorithm AlgorithmIdentifier,
126 /// subjectPublicKey BIT STRING }
127 ///
128 /// AlgorithmIdentifier ::= SEQUENCE {
129 /// algorithm OBJECT IDENTIFIER,
130 /// parameters ANY DEFINED BY algorithm OPTIONAL }
131 /// ```
132 ///
133 /// For NIST curve EC keys, the contents are described in RFC 5480 section 2.1.
134 /// - The `AlgorithmIdentifier` has an `algorithm` OID of 1.2.840.10045.2.1.
135 /// - The `AlgorithmIdentifier` has `parameters` that hold an OID identifying the curve, here
136 /// one of:
137 /// - P-224: 1.3.132.0.33
138 /// - P-256: 1.2.840.10045.3.1.7
139 /// - P-384: 1.3.132.0.34
140 /// - P-521: 1.3.132.0.35
141 /// - The `subjectPublicKey` bit string holds an ASN.1 DER-encoded `OCTET STRING` that contains
142 /// a SEC-1 encoded public key. The first byte indicates the format:
143 /// - 0x04: uncompressed, followed by x || y coordinates
144 /// - 0x03: compressed, followed by x coordinate (and with a odd y coordinate)
145 /// - 0x02: compressed, followed by x coordinate (and with a even y coordinate)
146 ///
147 /// For Ed25519 keys, the contents of the `AlgorithmIdentifier` are described in RFC 8410
148 /// section 3.
149 /// - The `algorithm` has an OID of 1.3.101.112.
150 /// - The `parameters` are absent.
151 ///
152 /// The `subjectPublicKey` holds the raw key bytes.
153 ///
154 /// For X25519 keys, the contents of the `AlgorithmIdentifier` are described in RFC 8410
155 /// section 3.
156 /// - The `algorithm` has an OID of 1.3.101.110.
157 /// - The `parameters` are absent.
158 ///
159 /// 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<SubjectPublicKeyInfoRef<'a>, Error>160 pub fn subject_public_key_info<'a>(
161 &'a self,
162 buf: &'a mut Vec<u8>,
163 ec: &dyn super::Ec,
164 curve: &EcCurve,
165 curve_type: &CurveType,
166 ) -> Result<SubjectPublicKeyInfoRef<'a>, Error> {
167 buf.try_extend_from_slice(&ec.subject_public_key(self)?)?;
168 let (oid, parameters) = match curve_type {
169 CurveType::Nist => {
170 let nist_curve: NistCurve = (*curve).try_into()?;
171 let params_oid = match nist_curve {
172 NistCurve::P224 => &ALGO_PARAM_P224_OID,
173 NistCurve::P256 => &ALGO_PARAM_P256_OID,
174 NistCurve::P384 => &ALGO_PARAM_P384_OID,
175 NistCurve::P521 => &ALGO_PARAM_P521_OID,
176 };
177 (X509_NIST_OID, Some(AnyRef::from(params_oid)))
178 }
179 CurveType::EdDsa => (X509_ED25519_OID, None),
180 CurveType::Xdh => (X509_X25519_OID, None),
181 };
182 Ok(SubjectPublicKeyInfo {
183 algorithm: AlgorithmIdentifier { oid, parameters },
184 subject_public_key: BitStringRef::from_bytes(buf).unwrap(),
185 })
186 }
187
188 /// Generate a `COSE_Key` for the public key.
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>189 pub fn public_cose_key(
190 &self,
191 ec: &dyn super::Ec,
192 curve: EcCurve,
193 curve_type: CurveType,
194 purpose: CoseKeyPurpose,
195 key_id: Option<Vec<u8>>,
196 test_mode: rpc::TestMode,
197 ) -> Result<coset::CoseKey, Error> {
198 let nist_algo = match purpose {
199 CoseKeyPurpose::Agree => coset::iana::Algorithm::ECDH_ES_HKDF_256,
200 CoseKeyPurpose::Sign => coset::iana::Algorithm::ES256,
201 };
202
203 let pub_key = ec.subject_public_key(self)?;
204 let mut builder = match curve_type {
205 CurveType::Nist => {
206 let nist_curve: NistCurve = curve.try_into()?;
207 let (x, y) = coordinates_from_pub_key(pub_key, nist_curve)?;
208 let cose_nist_curve = match nist_curve {
209 NistCurve::P224 => {
210 // P-224 is not supported by COSE: there is no value in the COSE Elliptic
211 // Curve registry for it.
212 return Err(km_err!(Unimplemented, "no COSE support for P-224"));
213 }
214 NistCurve::P256 => coset::iana::EllipticCurve::P_256,
215 NistCurve::P384 => coset::iana::EllipticCurve::P_384,
216 NistCurve::P521 => coset::iana::EllipticCurve::P_521,
217 };
218 coset::CoseKeyBuilder::new_ec2_pub_key(cose_nist_curve, x, y).algorithm(nist_algo)
219 }
220 CurveType::EdDsa => coset::CoseKeyBuilder::new_okp_key()
221 .param(
222 coset::iana::OkpKeyParameter::Crv as i64,
223 coset::cbor::value::Value::from(coset::iana::EllipticCurve::Ed25519 as u64),
224 )
225 .param(
226 coset::iana::OkpKeyParameter::X as i64,
227 coset::cbor::value::Value::from(pub_key),
228 )
229 .algorithm(coset::iana::Algorithm::EdDSA),
230 CurveType::Xdh => coset::CoseKeyBuilder::new_okp_key()
231 .param(
232 coset::iana::OkpKeyParameter::Crv as i64,
233 coset::cbor::value::Value::from(coset::iana::EllipticCurve::X25519 as u64),
234 )
235 .param(
236 coset::iana::OkpKeyParameter::X as i64,
237 coset::cbor::value::Value::from(pub_key),
238 )
239 .algorithm(coset::iana::Algorithm::ECDH_ES_HKDF_256),
240 };
241
242 if let Some(key_id) = key_id {
243 builder = builder.key_id(key_id);
244 }
245 if test_mode == rpc::TestMode(true) {
246 builder = builder.param(RKP_TEST_KEY_CBOR_MARKER, coset::cbor::value::Value::Null);
247 }
248 Ok(builder.build())
249 }
250 }
251
252 /// Elliptic curve private key material.
253 #[derive(Clone, PartialEq, Eq)]
254 pub enum Key {
255 /// P-224 private key.
256 P224(NistKey),
257 /// P-256 private key.
258 P256(NistKey),
259 /// P-384 private key.
260 P384(NistKey),
261 /// P-521 private key.
262 P521(NistKey),
263 /// Ed25519 private key.
264 Ed25519(Ed25519Key),
265 /// X25519 private key.
266 X25519(X25519Key),
267 }
268
269 /// Indication of the purpose for a COSE key.
270 pub enum CoseKeyPurpose {
271 /// ECDH key agreement.
272 Agree,
273 /// ECDSA signature generation.
274 Sign,
275 }
276
277 impl Key {
278 /// Return the private key material.
private_key_bytes(&self) -> &[u8]279 pub fn private_key_bytes(&self) -> &[u8] {
280 match self {
281 Key::P224(key) => &key.0,
282 Key::P256(key) => &key.0,
283 Key::P384(key) => &key.0,
284 Key::P521(key) => &key.0,
285 Key::Ed25519(key) => &key.0,
286 Key::X25519(key) => &key.0,
287 }
288 }
289
290 /// Return the type of curve.
curve_type(&self) -> CurveType291 pub fn curve_type(&self) -> CurveType {
292 match self {
293 Key::P224(_) | Key::P256(_) | Key::P384(_) | Key::P521(_) => CurveType::Nist,
294 Key::Ed25519(_) => CurveType::EdDsa,
295 Key::X25519(_) => CurveType::Xdh,
296 }
297 }
298
299 /// Return the curve.
curve(&self) -> EcCurve300 pub fn curve(&self) -> EcCurve {
301 match self {
302 Key::P224(_) => EcCurve::P224,
303 Key::P256(_) => EcCurve::P256,
304 Key::P384(_) => EcCurve::P384,
305 Key::P521(_) => EcCurve::P521,
306 Key::Ed25519(_) => EcCurve::Curve25519,
307 Key::X25519(_) => EcCurve::Curve25519,
308 }
309 }
310 }
311
312 /// A NIST EC key, in the form of an ASN.1 DER encoding of a `ECPrivateKey` structure,
313 /// as specified by RFC 5915 section 3:
314 ///
315 /// ```asn1
316 /// ECPrivateKey ::= SEQUENCE {
317 /// version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1),
318 /// privateKey OCTET STRING,
319 /// parameters [0] ECParameters {{ NamedCurve }} OPTIONAL,
320 /// publicKey [1] BIT STRING OPTIONAL
321 /// }
322 /// ```
323 #[derive(Clone, PartialEq, Eq, ZeroizeOnDrop)]
324 pub struct NistKey(pub Vec<u8>);
325
326 /// Helper function to return the (x,y) coordinates, given the public key as a SEC-1 encoded
327 /// 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>328 pub fn coordinates_from_pub_key(
329 pub_key: Vec<u8>,
330 curve: NistCurve,
331 ) -> Result<(Vec<u8>, Vec<u8>), Error> {
332 let coord_len = curve.coord_len();
333 if pub_key.len() != (1 + 2 * coord_len) {
334 return Err(km_err!(
335 UnsupportedKeySize,
336 "unexpected SEC1 pubkey len of {} for {:?}",
337 pub_key.len(),
338 curve
339 ));
340 }
341 if pub_key[0] != SEC1_UNCOMPRESSED_PREFIX {
342 return Err(km_err!(
343 UnsupportedKeySize,
344 "unexpected SEC1 pubkey initial byte {} for {:?}",
345 pub_key[0],
346 curve
347 ));
348 }
349 Ok((try_to_vec(&pub_key[1..1 + coord_len])?, try_to_vec(&pub_key[1 + coord_len..])?))
350 }
351
352 /// An Ed25519 private key.
353 #[derive(Clone, PartialEq, Eq, ZeroizeOnDrop)]
354 pub struct Ed25519Key(pub [u8; CURVE25519_PRIV_KEY_LEN]);
355
356 /// An X25519 private key.
357 #[derive(Clone, PartialEq, Eq, ZeroizeOnDrop)]
358 pub struct X25519Key(pub [u8; CURVE25519_PRIV_KEY_LEN]);
359
360 /// Return the OID used in an `AlgorithmIdentifier` for signatures produced by this curve.
curve_to_signing_oid(curve: EcCurve) -> pkcs8::ObjectIdentifier361 pub fn curve_to_signing_oid(curve: EcCurve) -> pkcs8::ObjectIdentifier {
362 match curve {
363 EcCurve::P224 | EcCurve::P256 | EcCurve::P384 | EcCurve::P521 => ECDSA_SHA256_SIGNATURE_OID,
364 EcCurve::Curve25519 => X509_ED25519_OID,
365 }
366 }
367
368 /// Return the key size for a curve.
curve_to_key_size(curve: EcCurve) -> KeySizeInBits369 pub fn curve_to_key_size(curve: EcCurve) -> KeySizeInBits {
370 KeySizeInBits(match curve {
371 EcCurve::P224 => 224,
372 EcCurve::P256 => 256,
373 EcCurve::P384 => 384,
374 EcCurve::P521 => 521,
375 EcCurve::Curve25519 => 256,
376 })
377 }
378
379 /// Import an NIST EC key in SEC1 ECPrivateKey format.
import_sec1_private_key(data: &[u8]) -> Result<KeyMaterial, Error>380 pub fn import_sec1_private_key(data: &[u8]) -> Result<KeyMaterial, Error> {
381 let ec_key = sec1::EcPrivateKey::from_der(data)
382 .map_err(|e| der_err!(e, "failed to parse ECPrivateKey"))?;
383 let ec_parameters = ec_key.parameters.ok_or_else(|| {
384 km_err!(InvalidArgument, "sec1 formatted EC private key didn't have a parameters field")
385 })?;
386 let parameters_oid = ec_parameters.named_curve().ok_or_else(|| {
387 km_err!(
388 InvalidArgument,
389 "couldn't retrieve parameters oid from sec1 ECPrivateKey formatted ec key parameters"
390 )
391 })?;
392 let algorithm =
393 AlgorithmIdentifier { oid: X509_NIST_OID, parameters: Some(AnyRef::from(¶meters_oid)) };
394 let pkcs8_key = pkcs8::PrivateKeyInfo::new(algorithm, data);
395 import_pkcs8_key_impl(&pkcs8_key)
396 }
397
398 /// Import an EC key in PKCS#8 format.
import_pkcs8_key(data: &[u8]) -> Result<KeyMaterial, Error>399 pub fn import_pkcs8_key(data: &[u8]) -> Result<KeyMaterial, Error> {
400 let key_info = pkcs8::PrivateKeyInfo::try_from(data)
401 .map_err(|_| km_err!(InvalidArgument, "failed to parse PKCS#8 EC key"))?;
402 import_pkcs8_key_impl(&key_info)
403 }
404
405 /// Import a `pkcs8::PrivateKeyInfo` EC key.
import_pkcs8_key_impl(key_info: &pkcs8::PrivateKeyInfo) -> Result<KeyMaterial, Error>406 fn import_pkcs8_key_impl(key_info: &pkcs8::PrivateKeyInfo) -> Result<KeyMaterial, Error> {
407 let algo_params = key_info.algorithm.parameters;
408 match key_info.algorithm.oid {
409 X509_NIST_OID => {
410 let algo_params = algo_params.ok_or_else(|| {
411 km_err!(
412 InvalidArgument,
413 "missing PKCS#8 parameters for NIST curve import under OID {:?}",
414 key_info.algorithm.oid
415 )
416 })?;
417 let curve_oid = algo_params
418 .decode_as()
419 .map_err(|_e| km_err!(InvalidArgument, "imported key has no OID parameter"))?;
420 let (curve, key) = match curve_oid {
421 ALGO_PARAM_P224_OID => {
422 (EcCurve::P224, Key::P224(NistKey(try_to_vec(key_info.private_key)?)))
423 }
424 ALGO_PARAM_P256_OID => {
425 (EcCurve::P256, Key::P256(NistKey(try_to_vec(key_info.private_key)?)))
426 }
427 ALGO_PARAM_P384_OID => {
428 (EcCurve::P384, Key::P384(NistKey(try_to_vec(key_info.private_key)?)))
429 }
430 ALGO_PARAM_P521_OID => {
431 (EcCurve::P521, Key::P521(NistKey(try_to_vec(key_info.private_key)?)))
432 }
433 oid => {
434 return Err(km_err!(
435 ImportParameterMismatch,
436 "imported key has unknown OID {:?}",
437 oid,
438 ))
439 }
440 };
441 Ok(KeyMaterial::Ec(curve, CurveType::Nist, key.into()))
442 }
443 X509_ED25519_OID => {
444 if algo_params.is_some() {
445 Err(km_err!(InvalidArgument, "unexpected PKCS#8 parameters for Ed25519 import"))
446 } else {
447 // For Ed25519 the PKCS#8 `privateKey` field holds a `CurvePrivateKey`
448 // (RFC 8410 s7) that is an OCTET STRING holding the raw key. As this is DER,
449 // this is just a 2 byte prefix (0x04 = OCTET STRING, 0x20 = length of raw key).
450 if key_info.private_key.len() != 2 + CURVE25519_PRIV_KEY_LEN
451 || key_info.private_key[0] != 0x04
452 || key_info.private_key[1] != 0x20
453 {
454 return Err(km_err!(InvalidArgument, "unexpected CurvePrivateKey contents"));
455 }
456 import_raw_ed25519_key(&key_info.private_key[2..])
457 }
458 }
459 X509_X25519_OID => {
460 if algo_params.is_some() {
461 Err(km_err!(InvalidArgument, "unexpected PKCS#8 parameters for X25519 import",))
462 } else {
463 // For X25519 the PKCS#8 `privateKey` field holds a `CurvePrivateKey`
464 // (RFC 8410 s7) that is an OCTET STRING holding the raw key. As this is DER,
465 // this is just a 2 byte prefix (0x04 = OCTET STRING, 0x20 = length of raw key).
466 if key_info.private_key.len() != 2 + CURVE25519_PRIV_KEY_LEN
467 || key_info.private_key[0] != 0x04
468 || key_info.private_key[1] != 0x20
469 {
470 return Err(km_err!(InvalidArgument, "unexpected CurvePrivateKey contents"));
471 }
472 import_raw_x25519_key(&key_info.private_key[2..])
473 }
474 }
475 _ => Err(km_err!(
476 InvalidArgument,
477 "unexpected OID {:?} for PKCS#8 EC key import",
478 key_info.algorithm.oid,
479 )),
480 }
481 }
482
483 /// Import a 32-byte raw Ed25519 key.
import_raw_ed25519_key(data: &[u8]) -> Result<KeyMaterial, Error>484 pub fn import_raw_ed25519_key(data: &[u8]) -> Result<KeyMaterial, Error> {
485 let key = data.try_into().map_err(|_e| {
486 km_err!(InvalidInputLength, "import Ed25519 key of incorrect len {}", data.len())
487 })?;
488 Ok(KeyMaterial::Ec(EcCurve::Curve25519, CurveType::EdDsa, Key::Ed25519(Ed25519Key(key)).into()))
489 }
490
491 /// Import a 32-byte raw X25519 key.
import_raw_x25519_key(data: &[u8]) -> Result<KeyMaterial, Error>492 pub fn import_raw_x25519_key(data: &[u8]) -> Result<KeyMaterial, Error> {
493 let key = data.try_into().map_err(|_e| {
494 km_err!(InvalidInputLength, "import X25519 key of incorrect len {}", data.len())
495 })?;
496 Ok(KeyMaterial::Ec(EcCurve::Curve25519, CurveType::Xdh, Key::X25519(X25519Key(key)).into()))
497 }
498
499 /// Convert a signature as emitted from the `Ec` trait into the form needed for
500 /// a `COSE_Sign1`.
to_cose_signature(curve: EcCurve, sig: Vec<u8>) -> Result<Vec<u8>, Error>501 pub fn to_cose_signature(curve: EcCurve, sig: Vec<u8>) -> Result<Vec<u8>, Error> {
502 match curve {
503 EcCurve::P224 | EcCurve::P256 | EcCurve::P384 | EcCurve::P521 => {
504 // NIST curve signatures are emitted as a DER-encoded `SEQUENCE`.
505 let der_sig = NistSignature::from_der(&sig)
506 .map_err(|e| km_err!(EncodingError, "failed to parse DER signature: {:?}", e))?;
507 // COSE expects signature of (r||s) with each value left-padded with zeros to coordinate
508 // size.
509 let nist_curve = NistCurve::try_from(curve)?;
510 let l = nist_curve.coord_len();
511 let mut sig = vec_try![0; 2 * l]?;
512 let r = der_sig.r.as_bytes();
513 let s = der_sig.s.as_bytes();
514 let r_offset = l - r.len();
515 let s_offset = l + l - s.len();
516 sig[r_offset..r_offset + r.len()].copy_from_slice(r);
517 sig[s_offset..s_offset + s.len()].copy_from_slice(s);
518 Ok(sig)
519 }
520 EcCurve::Curve25519 => {
521 // Ed25519 signatures can be used as-is (RFC 8410 section 6)
522 Ok(sig)
523 }
524 }
525 }
526
527 /// Convert a signature as used in a `COSE_Sign1` into the form needed for the `Ec` trait.
from_cose_signature(curve: EcCurve, sig: &[u8]) -> Result<Vec<u8>, Error>528 pub fn from_cose_signature(curve: EcCurve, sig: &[u8]) -> Result<Vec<u8>, Error> {
529 match curve {
530 EcCurve::P224 | EcCurve::P256 | EcCurve::P384 | EcCurve::P521 => {
531 // COSE signatures are (r||s) with each value left-padded with zeros to coordinate size.
532 let nist_curve = NistCurve::try_from(curve)?;
533 let l = nist_curve.coord_len();
534 if sig.len() != 2 * l {
535 return Err(km_err!(
536 EncodingError,
537 "unexpected len {} for {:?} COSE signature value",
538 sig.len(),
539 nist_curve
540 ));
541 }
542
543 // NIST curve signatures need to be emitted as a DER-encoded `SEQUENCE`.
544 let der_sig = NistSignature {
545 r: der::asn1::UintRef::new(&sig[..l])
546 .map_err(|e| km_err!(EncodingError, "failed to build INTEGER: {:?}", e))?,
547 s: der::asn1::UintRef::new(&sig[l..])
548 .map_err(|e| km_err!(EncodingError, "failed to build INTEGER: {:?}", e))?,
549 };
550 der_sig
551 .to_der()
552 .map_err(|e| km_err!(EncodingError, "failed to encode signature SEQUENCE: {:?}", e))
553 }
554 EcCurve::Curve25519 => {
555 // Ed25519 signatures can be used as-is (RFC 8410 section 6)
556 try_to_vec(sig)
557 }
558 }
559 }
560
561 /// DER-encoded signature from a NIST curve (RFC 3279 section 2.2.3):
562 /// ```asn1
563 /// Ecdsa-Sig-Value ::= SEQUENCE {
564 /// r INTEGER,
565 /// s INTEGER
566 /// }
567 /// ```
568 #[derive(Sequence)]
569 struct NistSignature<'a> {
570 r: der::asn1::UintRef<'a>,
571 s: der::asn1::UintRef<'a>,
572 }
573
574 #[cfg(test)]
575 mod tests {
576 use super::*;
577 #[test]
test_sig_decode()578 fn test_sig_decode() {
579 let sig_data = hex::decode("3045022001b309d5eeffa5d550bde27630f9fc7f08492e4617bc158da08b913414cf675b022100fcdca2e77d036c33fa78f4a892b98569358d83c047a7d8a74ce6fe12fbf919c6").unwrap();
580 let sig = NistSignature::from_der(&sig_data).expect("sequence should decode");
581 assert_eq!(
582 hex::encode(sig.r.as_bytes()),
583 "01b309d5eeffa5d550bde27630f9fc7f08492e4617bc158da08b913414cf675b"
584 );
585 assert_eq!(
586 hex::encode(sig.s.as_bytes()),
587 "fcdca2e77d036c33fa78f4a892b98569358d83c047a7d8a74ce6fe12fbf919c6"
588 );
589 }
590
591 #[test]
test_longer_sig_transmute()592 fn test_longer_sig_transmute() {
593 let nist_sig_data = hex::decode(concat!(
594 "30", // SEQUENCE
595 "45", // len
596 "02", // INTEGER
597 "20", // len = 32
598 "01b309d5eeffa5d550bde27630f9fc7f08492e4617bc158da08b913414cf675b",
599 "02", // INTEGER
600 "21", // len = 33 (high bit set so leading zero needed)
601 "00fcdca2e77d036c33fa78f4a892b98569358d83c047a7d8a74ce6fe12fbf919c6"
602 ))
603 .unwrap();
604 let cose_sig_data = to_cose_signature(EcCurve::P256, nist_sig_data.clone()).unwrap();
605 assert_eq!(
606 concat!(
607 "01b309d5eeffa5d550bde27630f9fc7f08492e4617bc158da08b913414cf675b",
608 "fcdca2e77d036c33fa78f4a892b98569358d83c047a7d8a74ce6fe12fbf919c6"
609 ),
610 hex::encode(&cose_sig_data),
611 );
612 let got_nist_sig = from_cose_signature(EcCurve::P256, &cose_sig_data).unwrap();
613 assert_eq!(got_nist_sig, nist_sig_data);
614 }
615
616 #[test]
test_short_sig_transmute()617 fn test_short_sig_transmute() {
618 let nist_sig_data = hex::decode(concat!(
619 "30", // SEQUENCE
620 "43", // len x44
621 "02", // INTEGER
622 "1e", // len = 30
623 "09d5eeffa5d550bde27630f9fc7f08492e4617bc158da08b913414cf675b",
624 "02", // INTEGER
625 "21", // len = 33 (high bit set so leading zero needed)
626 "00fcdca2e77d036c33fa78f4a892b98569358d83c047a7d8a74ce6fe12fbf919c6"
627 ))
628 .unwrap();
629 let cose_sig_data = to_cose_signature(EcCurve::P256, nist_sig_data.clone()).unwrap();
630 assert_eq!(
631 concat!(
632 "000009d5eeffa5d550bde27630f9fc7f08492e4617bc158da08b913414cf675b",
633 "fcdca2e77d036c33fa78f4a892b98569358d83c047a7d8a74ce6fe12fbf919c6"
634 ),
635 hex::encode(&cose_sig_data),
636 );
637 let got_nist_sig = from_cose_signature(EcCurve::P256, &cose_sig_data).unwrap();
638 assert_eq!(got_nist_sig, nist_sig_data);
639 }
640
641 #[test]
test_sec1_ec_import()642 fn test_sec1_ec_import() {
643 // Key data created with:
644 // ```
645 // openssl ecparam -name prime256v1 -genkey -noout -out private-key.pem
646 // ```
647 let key_data = hex::decode(concat!(
648 "3077", // SEQUENCE len x77 (ECPrivateKey)
649 "020101", // INTEGER 1 = (ecPrivkeyVer1)
650 "0420", // OCTET STRING len x20 (privateKey)
651 "a6a30ca3dc87b58763736400e7e86260",
652 "9e8311f41e6b89888c33753218168517",
653 "a00a", // [0] len x0a (parameters)
654 "0608", // OBJECT IDENTIFIER len 8 (NamedCurve)
655 "2a8648ce3d030107", // 1.2.840.10045.3.1.7=secp256r1
656 "a144", // [1] len x44 (publicKey)
657 "0342", // BIT STRING len x42
658 "00", // no pad bits
659 "0481e4ce20d8be3dd40b940b3a3ba3e8",
660 "cf5a3f2156eceb4debb8fce83cbe4a48",
661 "bd576a03eebf77d329a438fcdc509f37",
662 "1f092cad41e2ecf9f25cd82f31500f33",
663 "8e"
664 ))
665 .unwrap();
666 let key = import_sec1_private_key(&key_data).expect("SEC1 parse failed");
667 if let KeyMaterial::Ec(curve, curve_type, _key) = key {
668 assert_eq!(curve, EcCurve::P256);
669 assert_eq!(curve_type, CurveType::Nist);
670 } else {
671 panic!("unexpected key type");
672 }
673 }
674 }
675