[bssl] Support conversion from COSE_Key to EVP_PKEY
The subject public key in DICE chain will be encoded as EVP_PKEY
in a subsequent cl to verify the DICE chain signatures.
Bug: 314266221
Test: atest libbssl_avf_nostd.test
Change-Id: I489876025d431a794f08cb12999b34e4920d3507
diff --git a/libs/bssl/src/ec_key.rs b/libs/bssl/src/ec_key.rs
index 511b133..0c944cd 100644
--- a/libs/bssl/src/ec_key.rs
+++ b/libs/bssl/src/ec_key.rs
@@ -17,7 +17,9 @@
use crate::cbb::CbbFixed;
use crate::cbs::Cbs;
-use crate::util::{check_int_result, to_call_failed_error};
+use crate::util::{
+ check_int_result, get_label_value, get_label_value_as_bytes, to_call_failed_error,
+};
use alloc::vec;
use alloc::vec::Vec;
use bssl_avf_error::{ApiName, Error, Result};
@@ -33,7 +35,7 @@
use core::ptr::{self, NonNull};
use coset::{
iana::{self, EnumI64},
- CborSerializable, CoseKey, CoseKeyBuilder, Label,
+ CborSerializable, CoseKey, CoseKeyBuilder, KeyType, Label,
};
use log::error;
use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
@@ -79,13 +81,27 @@
}
/// Constructs an `EcKey` instance from the provided COSE_Key encoded public key slice.
- pub fn from_cose_public_key(cose_key: &[u8]) -> Result<Self> {
+ pub fn from_cose_public_key_slice(cose_key: &[u8]) -> Result<Self> {
let cose_key = CoseKey::from_slice(cose_key).map_err(|e| {
error!("Failed to deserialize COSE_Key: {e:?}");
Error::CoseKeyDecodingFailed
})?;
+ Self::from_cose_public_key(&cose_key)
+ }
+
+ /// Constructs an `EcKey` instance from the provided `COSE_Key`.
+ ///
+ /// The lifetime of the returned `EcKey` is not tied to the lifetime of the `cose_key`,
+ /// because the affine coordinates stored in the `cose_key` are copied into the `EcKey`.
+ ///
+ /// Currently, only the EC P-256 and P-384 curves are supported.
+ pub fn from_cose_public_key(cose_key: &CoseKey) -> Result<Self> {
+ if cose_key.kty != KeyType::Assigned(iana::KeyType::EC2) {
+ error!("Only EC2 keys are supported. Key type in the COSE Key: {:?}", cose_key.kty);
+ return Err(Error::Unimplemented);
+ }
let ec_key =
- match get_label_value(&cose_key, Label::Int(iana::Ec2KeyParameter::Crv.to_i64()))? {
+ match get_label_value(cose_key, Label::Int(iana::Ec2KeyParameter::Crv.to_i64()))? {
crv if crv == &Value::from(P256_CURVE.to_i64()) => EcKey::new_p256()?,
crv if crv == &Value::from(P384_CURVE.to_i64()) => EcKey::new_p384()?,
crv => {
@@ -96,8 +112,8 @@
return Err(Error::Unimplemented);
}
};
- let x = get_label_value_as_bytes(&cose_key, Label::Int(iana::Ec2KeyParameter::X.to_i64()))?;
- let y = get_label_value_as_bytes(&cose_key, Label::Int(iana::Ec2KeyParameter::Y.to_i64()))?;
+ let x = get_label_value_as_bytes(cose_key, Label::Int(iana::Ec2KeyParameter::X.to_i64()))?;
+ let y = get_label_value_as_bytes(cose_key, Label::Int(iana::Ec2KeyParameter::Y.to_i64()))?;
let group = ec_key.ec_group()?;
group.check_affine_coordinate_size(x)?;
@@ -309,17 +325,6 @@
}
}
-fn get_label_value_as_bytes(key: &CoseKey, label: Label) -> Result<&[u8]> {
- Ok(get_label_value(key, label)?.as_bytes().ok_or_else(|| {
- error!("Value not a bstr.");
- Error::CoseKeyDecodingFailed
- })?)
-}
-
-fn get_label_value(key: &CoseKey, label: Label) -> Result<&Value> {
- Ok(&key.params.iter().find(|(k, _)| k == &label).ok_or(Error::CoseKeyDecodingFailed)?.1)
-}
-
/// Wrapper of an `EC_GROUP` reference.
struct EcGroup<'a>(&'a EC_GROUP);
diff --git a/libs/bssl/src/evp.rs b/libs/bssl/src/evp.rs
index 330f317..86f99a8 100644
--- a/libs/bssl/src/evp.rs
+++ b/libs/bssl/src/evp.rs
@@ -17,15 +17,23 @@
use crate::cbb::CbbFixed;
use crate::digest::{Digester, DigesterContext};
use crate::ec_key::EcKey;
-use crate::util::{check_int_result, to_call_failed_error};
+use crate::util::{
+ check_int_result, get_label_value, get_label_value_as_bytes, to_call_failed_error,
+};
use alloc::vec::Vec;
-use bssl_avf_error::{ApiName, Result};
+use bssl_avf_error::{ApiName, Error, Result};
use bssl_ffi::{
CBB_flush, CBB_len, EVP_DigestVerify, EVP_DigestVerifyInit, EVP_PKEY_free, EVP_PKEY_new,
EVP_PKEY_new_raw_public_key, EVP_PKEY_set1_EC_KEY, EVP_marshal_public_key, EVP_PKEY,
EVP_PKEY_ED25519, EVP_PKEY_X25519,
};
+use ciborium::Value;
use core::ptr::{self, NonNull};
+use coset::{
+ iana::{self, EnumI64},
+ CoseKey, KeyType, Label,
+};
+use log::error;
/// Wrapper of an `EVP_PKEY` object, representing a public or private key.
pub struct PKey {
@@ -116,6 +124,43 @@
Ok(Self { pkey, _inner_ec_key: None })
}
+ /// Creates a `PKey` from the given `cose_key`.
+ ///
+ /// The lifetime of the returned instance is not tied to the lifetime of the `cose_key` as the
+ /// data of `cose_key` is copied into the `EVP_PKEY` or `EC_KEY` object.
+ pub fn from_cose_public_key(cose_key: &CoseKey) -> Result<Self> {
+ match &cose_key.kty {
+ KeyType::Assigned(iana::KeyType::EC2) => {
+ EcKey::from_cose_public_key(cose_key)?.try_into()
+ }
+ KeyType::Assigned(iana::KeyType::OKP) => {
+ let curve_type =
+ get_label_value(cose_key, Label::Int(iana::OkpKeyParameter::Crv.to_i64()))?;
+ let curve_type = match curve_type {
+ crv if crv == &Value::from(iana::EllipticCurve::Ed25519.to_i64()) => {
+ PKeyType::ED25519
+ }
+ crv if crv == &Value::from(iana::EllipticCurve::X25519.to_i64()) => {
+ PKeyType::X25519
+ }
+ crv => {
+ error!("Unsupported curve type in OKP COSE key: {:?}", crv);
+ return Err(Error::Unimplemented);
+ }
+ };
+ let x = get_label_value_as_bytes(
+ cose_key,
+ Label::Int(iana::OkpKeyParameter::X.to_i64()),
+ )?;
+ Self::new_raw_public_key(x, curve_type)
+ }
+ kty => {
+ error!("Unsupported key type in COSE key: {:?}", kty);
+ Err(Error::Unimplemented)
+ }
+ }
+ }
+
/// Verifies the given `signature` of the `message` using the current public key.
///
/// The `message` will be hashed using the given `digester` before verification.
diff --git a/libs/bssl/src/util.rs b/libs/bssl/src/util.rs
index 880c85b..7473fe1 100644
--- a/libs/bssl/src/util.rs
+++ b/libs/bssl/src/util.rs
@@ -16,6 +16,8 @@
use crate::err::get_error_reason_code;
use bssl_avf_error::{ApiName, Error, Result};
+use ciborium::Value;
+use coset::{CoseKey, Label};
use log::error;
pub(crate) fn check_int_result(ret: i32, api_name: ApiName) -> Result<()> {
@@ -35,3 +37,14 @@
pub(crate) fn to_call_failed_error(api_name: ApiName) -> Error {
Error::CallFailed(api_name, get_error_reason_code())
}
+
+pub(crate) fn get_label_value_as_bytes(key: &CoseKey, label: Label) -> Result<&[u8]> {
+ Ok(get_label_value(key, label)?.as_bytes().ok_or_else(|| {
+ error!("Value not a bstr.");
+ Error::CoseKeyDecodingFailed
+ })?)
+}
+
+pub(crate) fn get_label_value(key: &CoseKey, label: Label) -> Result<&Value> {
+ Ok(&key.params.iter().find(|(k, _)| k == &label).ok_or(Error::CoseKeyDecodingFailed)?.1)
+}
diff --git a/libs/bssl/tests/eckey_test.rs b/libs/bssl/tests/eckey_test.rs
index 265dee7..9c7eb4f 100644
--- a/libs/bssl/tests/eckey_test.rs
+++ b/libs/bssl/tests/eckey_test.rs
@@ -72,7 +72,7 @@
ec_key.generate_key()?;
let cose_key = ec_key.cose_public_key()?;
let cose_key_data = cose_key.clone().to_vec().unwrap();
- let deserialized_ec_key = EcKey::from_cose_public_key(&cose_key_data)?;
+ let deserialized_ec_key = EcKey::from_cose_public_key_slice(&cose_key_data)?;
assert_eq!(cose_key, deserialized_ec_key.cose_public_key()?);
Ok(())
@@ -88,7 +88,9 @@
let signature = ec_key.ecdsa_sign(&digest)?;
ec_key.ecdsa_verify(&signature, &digest)?;
- let pkey: PKey = ec_key.try_into()?;
+ // Building a `PKey` from a temporary `CoseKey` should work as the lifetime
+ // of the `PKey` is not tied to the lifetime of the `CoseKey`.
+ let pkey = PKey::from_cose_public_key(&ec_key.cose_public_key()?)?;
pkey.verify(&signature, MESSAGE1, Some(digester))
}
@@ -101,7 +103,7 @@
let signature = ec_key.ecdsa_sign(&digest)?;
ec_key.ecdsa_verify(&signature, &digest)?;
- let pkey: PKey = ec_key.try_into()?;
+ let pkey = PKey::from_cose_public_key(&ec_key.cose_public_key()?)?;
pkey.verify(&signature, MESSAGE1, Some(digester))
}
diff --git a/rialto/tests/test.rs b/rialto/tests/test.rs
index c1a8394..029895f 100644
--- a/rialto/tests/test.rs
+++ b/rialto/tests/test.rs
@@ -226,7 +226,8 @@
parent_certificate: &X509Certificate,
) -> Result<()> {
let cose_mac = CoseMac0::from_slice(maced_public_key)?;
- let authority_public_key = EcKey::from_cose_public_key(&cose_mac.payload.unwrap()).unwrap();
+ let authority_public_key =
+ EcKey::from_cose_public_key_slice(&cose_mac.payload.unwrap()).unwrap();
let (remaining, cert) = X509Certificate::from_der(certificate)?;
assert!(remaining.is_empty());
@@ -244,7 +245,7 @@
let cose_sign = CoseSign::from_slice(&csr.signed_csr_payload)?;
let csr_payload =
cose_sign.payload.as_ref().and_then(|v| CsrPayload::from_cbor_slice(v).ok()).unwrap();
- let subject_public_key = EcKey::from_cose_public_key(&csr_payload.public_key).unwrap();
+ let subject_public_key = EcKey::from_cose_public_key_slice(&csr_payload.public_key).unwrap();
let expected_spki_data =
PKey::try_from(subject_public_key).unwrap().subject_public_key_info().unwrap();
let (remaining, expected_spki) = SubjectPublicKeyInfo::from_der(&expected_spki_data)?;
diff --git a/service_vm/requests/src/client_vm.rs b/service_vm/requests/src/client_vm.rs
index ddf230b..4e87136 100644
--- a/service_vm/requests/src/client_vm.rs
+++ b/service_vm/requests/src/client_vm.rs
@@ -62,7 +62,7 @@
// the DICE chain in `cose_sign`.
// Verifies the second signature with the public key in the CSR payload.
- let ec_public_key = EcKey::from_cose_public_key(&csr_payload.public_key)?;
+ let ec_public_key = EcKey::from_cose_public_key_slice(&csr_payload.public_key)?;
cose_sign.verify_signature(ATTESTATION_KEY_SIGNATURE_INDEX, aad, |signature, message| {
ecdsa_verify(&ec_public_key, signature, message)
})?;