[bssl] Support EC P-384 key in libbssl_avf_nostd
The support of EC P-384 key is required for the verification the
signatures of DiceChainEntry in the service VM.
Test: atest libbssl_avf_nostd.test
Change-Id: I1817a913c6b9be96eed4bc2c86d032f24441386c
diff --git a/libs/bssl/src/ec_key.rs b/libs/bssl/src/ec_key.rs
index a187259..511b133 100644
--- a/libs/bssl/src/ec_key.rs
+++ b/libs/bssl/src/ec_key.rs
@@ -23,14 +23,14 @@
use bssl_avf_error::{ApiName, Error, Result};
use bssl_ffi::{
BN_bin2bn, BN_bn2bin_padded, BN_clear_free, BN_new, CBB_flush, CBB_len, ECDSA_sign, ECDSA_size,
- ECDSA_verify, EC_GROUP_new_by_curve_name, EC_KEY_check_key, EC_KEY_free, EC_KEY_generate_key,
- EC_KEY_get0_group, EC_KEY_get0_public_key, EC_KEY_marshal_private_key,
- EC_KEY_new_by_curve_name, EC_KEY_parse_private_key, EC_KEY_set_public_key_affine_coordinates,
- EC_POINT_get_affine_coordinates, NID_X9_62_prime256v1, BIGNUM, EC_GROUP, EC_KEY, EC_POINT,
+ ECDSA_verify, EC_GROUP_get_curve_name, EC_GROUP_new_by_curve_name, EC_KEY_check_key,
+ EC_KEY_free, EC_KEY_generate_key, EC_KEY_get0_group, EC_KEY_get0_public_key,
+ EC_KEY_marshal_private_key, EC_KEY_new_by_curve_name, EC_KEY_parse_private_key,
+ EC_KEY_set_public_key_affine_coordinates, EC_POINT_get_affine_coordinates,
+ NID_X9_62_prime256v1, NID_secp384r1, BIGNUM, EC_GROUP, EC_KEY, EC_POINT,
};
use ciborium::Value;
use core::ptr::{self, NonNull};
-use core::result;
use coset::{
iana::{self, EnumI64},
CborSerializable, CoseKey, CoseKeyBuilder, Label,
@@ -40,9 +40,9 @@
const ES256_ALGO: iana::Algorithm = iana::Algorithm::ES256;
const P256_CURVE: iana::EllipticCurve = iana::EllipticCurve::P_256;
+const P384_CURVE: iana::EllipticCurve = iana::EllipticCurve::P_384;
const P256_AFFINE_COORDINATE_SIZE: usize = 32;
-
-type Coordinate = [u8; P256_AFFINE_COORDINATE_SIZE];
+const P384_AFFINE_COORDINATE_SIZE: usize = 48;
/// Wrapper of an `EC_KEY` object, representing a public or private EC key.
pub struct EcKey(pub(crate) NonNull<EC_KEY>);
@@ -67,35 +67,45 @@
.ok_or(to_call_failed_error(ApiName::EC_KEY_new_by_curve_name))
}
+ /// Creates a new EC P-384 key pair.
+ pub fn new_p384() -> Result<Self> {
+ // SAFETY: The returned pointer is checked below.
+ let ec_key = unsafe {
+ EC_KEY_new_by_curve_name(NID_secp384r1) // EC P-384 CURVE Nid
+ };
+ NonNull::new(ec_key)
+ .map(Self)
+ .ok_or(to_call_failed_error(ApiName::EC_KEY_new_by_curve_name))
+ }
+
/// Constructs an `EcKey` instance from the provided COSE_Key encoded public key slice.
pub fn from_cose_public_key(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
})?;
- if cose_key.alg != Some(coset::Algorithm::Assigned(ES256_ALGO)) {
- error!(
- "Only ES256 algorithm is supported. Algo type in the COSE Key: {:?}",
- cose_key.alg
- );
- return Err(Error::Unimplemented);
- }
- let crv = get_label_value(&cose_key, Label::Int(iana::Ec2KeyParameter::Crv.to_i64()))?;
- if &Value::from(P256_CURVE.to_i64()) != crv {
- error!("Only EC P-256 curve is supported. Curve type in the COSE Key: {crv:?}");
- return Err(Error::Unimplemented);
- }
-
+ let ec_key =
+ 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 => {
+ error!(
+ "Only EC P-256 and P-384 curves are supported. \
+ Curve type in the COSE Key: {crv:?}"
+ );
+ 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()))?;
- check_p256_affine_coordinate_size(x)?;
- check_p256_affine_coordinate_size(y)?;
+ let group = ec_key.ec_group()?;
+ group.check_affine_coordinate_size(x)?;
+ group.check_affine_coordinate_size(y)?;
let x = BigNum::from_slice(x)?;
let y = BigNum::from_slice(y)?;
- let ec_key = EcKey::new_p256()?;
// SAFETY: All the parameters are checked non-null and initialized.
// The function only reads the coordinates x and y within their bounds.
let ret = unsafe {
@@ -193,14 +203,13 @@
/// Returns the `CoseKey` for the public key.
pub fn cose_public_key(&self) -> Result<CoseKey> {
let (x, y) = self.public_key_coordinates()?;
- let key = CoseKeyBuilder::new_ec2_pub_key(P256_CURVE, x.to_vec(), y.to_vec())
- .algorithm(ES256_ALGO)
- .build();
+ let curve = self.ec_group()?.coset_curve()?;
+ let key = CoseKeyBuilder::new_ec2_pub_key(curve, x, y).algorithm(ES256_ALGO).build();
Ok(key)
}
/// Returns the x and y coordinates of the public key.
- fn public_key_coordinates(&self) -> Result<(Coordinate, Coordinate)> {
+ fn public_key_coordinates(&self) -> Result<(Vec<u8>, Vec<u8>)> {
let ec_group = self.ec_group()?;
let ec_point = self.public_key_ec_point()?;
let mut x = BigNum::new()?;
@@ -209,10 +218,17 @@
// SAFETY: All the parameters are checked non-null and initialized when needed.
// The last parameter `ctx` is generated when needed inside the function.
let ret = unsafe {
- EC_POINT_get_affine_coordinates(ec_group, ec_point, x.as_mut_ptr(), y.as_mut_ptr(), ctx)
+ EC_POINT_get_affine_coordinates(
+ ec_group.as_ref(),
+ ec_point,
+ x.as_mut_ptr(),
+ y.as_mut_ptr(),
+ ctx,
+ )
};
check_int_result(ret, ApiName::EC_POINT_get_affine_coordinates)?;
- Ok((x.try_into()?, y.try_into()?))
+ let len = ec_group.affine_coordinate_size()?;
+ Ok((x.to_padded_vec(len)?, y.to_padded_vec(len)?))
}
/// Returns a pointer to the public key point inside `EC_KEY`. The memory region pointed
@@ -231,7 +247,7 @@
/// Returns a pointer to the `EC_GROUP` object inside `EC_KEY`. The memory region pointed
/// by the pointer is owned by the `EC_KEY`.
- fn ec_group(&self) -> Result<*const EC_GROUP> {
+ fn ec_group(&self) -> Result<EcGroup<'_>> {
let group =
// SAFETY: It is safe since the key pair has been generated and stored in the
// `EC_KEY` pointer.
@@ -239,7 +255,9 @@
if group.is_null() {
Err(to_call_failed_error(ApiName::EC_KEY_get0_group))
} else {
- Ok(group)
+ // SAFETY: The pointer should be valid and points to an initialized `EC_GROUP`
+ // since it is read from a valid `EC_KEY`.
+ Ok(EcGroup(unsafe { &*group }))
}
}
@@ -302,16 +320,59 @@
Ok(&key.params.iter().find(|(k, _)| k == &label).ok_or(Error::CoseKeyDecodingFailed)?.1)
}
-fn check_p256_affine_coordinate_size(coordinate: &[u8]) -> Result<()> {
- if P256_AFFINE_COORDINATE_SIZE == coordinate.len() {
- Ok(())
- } else {
- error!(
- "The size of the affine coordinate '{}' does not match the expected size '{}'",
- coordinate.len(),
- P256_AFFINE_COORDINATE_SIZE
- );
- Err(Error::CoseKeyDecodingFailed)
+/// Wrapper of an `EC_GROUP` reference.
+struct EcGroup<'a>(&'a EC_GROUP);
+
+impl<'a> EcGroup<'a> {
+ /// Returns the NID that identifies the EC group of the key.
+ fn curve_nid(&self) -> i32 {
+ // SAFETY: It is safe since the inner pointer is valid and points to an initialized
+ // instance of `EC_GROUP`.
+ unsafe { EC_GROUP_get_curve_name(self.as_ref()) }
+ }
+
+ fn coset_curve(&self) -> Result<iana::EllipticCurve> {
+ #[allow(non_upper_case_globals)]
+ match self.curve_nid() {
+ NID_X9_62_prime256v1 => Ok(P256_CURVE),
+ NID_secp384r1 => Ok(P384_CURVE),
+ name => {
+ error!("Unsupported curve NID: {}", name);
+ Err(Error::Unimplemented)
+ }
+ }
+ }
+
+ fn affine_coordinate_size(&self) -> Result<usize> {
+ #[allow(non_upper_case_globals)]
+ match self.curve_nid() {
+ NID_X9_62_prime256v1 => Ok(P256_AFFINE_COORDINATE_SIZE),
+ NID_secp384r1 => Ok(P384_AFFINE_COORDINATE_SIZE),
+ name => {
+ error!("Unsupported curve NID: {}", name);
+ Err(Error::Unimplemented)
+ }
+ }
+ }
+
+ fn check_affine_coordinate_size(&self, coordinate: &[u8]) -> Result<()> {
+ let expected_len = self.affine_coordinate_size()?;
+ if expected_len == coordinate.len() {
+ Ok(())
+ } else {
+ error!(
+ "The size of the affine coordinate '{}' does not match the expected size '{}'",
+ coordinate.len(),
+ expected_len
+ );
+ Err(Error::CoseKeyDecodingFailed)
+ }
+ }
+}
+
+impl<'a> AsRef<EC_GROUP> for EcGroup<'a> {
+ fn as_ref(&self) -> &EC_GROUP {
+ self.0
}
}
@@ -355,6 +416,16 @@
NonNull::new(bn).map(Self).ok_or(to_call_failed_error(ApiName::BN_new))
}
+ /// Converts the `BigNum` to a big-endian integer. The integer is padded with leading zeros up
+ /// to size `len`. The conversion fails if `len` is smaller than the size of the integer.
+ fn to_padded_vec(&self, len: usize) -> Result<Vec<u8>> {
+ let mut num = vec![0u8; len];
+ // SAFETY: The `BIGNUM` pointer has been created with `BN_new`.
+ let ret = unsafe { BN_bn2bin_padded(num.as_mut_ptr(), num.len(), self.0.as_ptr()) };
+ check_int_result(ret, ApiName::BN_bn2bin_padded)?;
+ Ok(num)
+ }
+
fn as_mut_ptr(&mut self) -> *mut BIGNUM {
self.0.as_ptr()
}
@@ -367,19 +438,3 @@
unsafe { self.0.as_ref() }
}
}
-
-/// Converts the `BigNum` to a big-endian integer. The integer is padded with leading zeros up to
-/// size `N`. The conversion fails if `N` is smaller thanthe size of the integer.
-impl<const N: usize> TryFrom<BigNum> for [u8; N] {
- type Error = Error;
-
- fn try_from(bn: BigNum) -> result::Result<Self, Self::Error> {
- let mut num = [0u8; N];
- // SAFETY: The `BIGNUM` pointer has been created with `BN_new`.
- let ret = unsafe { BN_bn2bin_padded(num.as_mut_ptr(), num.len(), bn.0.as_ptr()) };
- check_int_result(ret, ApiName::BN_bn2bin_padded)?;
- Ok(num)
- }
-}
-
-// TODO(b/301068421): Unit tests the EcKey.
diff --git a/libs/bssl/tests/eckey_test.rs b/libs/bssl/tests/eckey_test.rs
index 968af63..2eb908a 100644
--- a/libs/bssl/tests/eckey_test.rs
+++ b/libs/bssl/tests/eckey_test.rs
@@ -57,8 +57,18 @@
}
#[test]
-fn cose_public_key_serialization() -> Result<()> {
+fn p256_cose_public_key_serialization() -> Result<()> {
let mut ec_key = EcKey::new_p256()?;
+ check_cose_public_key_serialization(&mut ec_key)
+}
+
+#[test]
+fn p384_cose_public_key_serialization() -> Result<()> {
+ let mut ec_key = EcKey::new_p384()?;
+ check_cose_public_key_serialization(&mut ec_key)
+}
+
+fn check_cose_public_key_serialization(ec_key: &mut EcKey) -> Result<()> {
ec_key.generate_key()?;
let cose_key = ec_key.cose_public_key()?;
let cose_key_data = cose_key.clone().to_vec().unwrap();