[x509] Encode EC_KEY as SubjectPublicKeyInfo
This cl adds bssl wrappers that enable the encoding of bssl::EC_KEY
objects as SubjectPublicKeyInfo. This will facilitate the econding
of the public key in the client VM's CSR in the x509 certificate
later.
Test: atest libbssl_avf_nostd.test
Bug: 309441500
Change-Id: I374a4a9d7e9ff5408cd176dfca09b670da507e11
diff --git a/libs/bssl/Android.bp b/libs/bssl/Android.bp
index ff45af9..e1f4ffd 100644
--- a/libs/bssl/Android.bp
+++ b/libs/bssl/Android.bp
@@ -46,5 +46,6 @@
rustlibs: [
"libbssl_avf_nostd",
"libcoset_nostd",
+ "libspki_nostd",
],
}
diff --git a/libs/bssl/error/src/lib.rs b/libs/bssl/error/src/lib.rs
index 89865d4..c0dca2e 100644
--- a/libs/bssl/error/src/lib.rs
+++ b/libs/bssl/error/src/lib.rs
@@ -81,6 +81,9 @@
EVP_AEAD_CTX_new,
EVP_AEAD_CTX_open,
EVP_AEAD_CTX_seal,
+ EVP_PKEY_new,
+ EVP_PKEY_set1_EC_KEY,
+ EVP_marshal_public_key,
HKDF,
HMAC,
RAND_bytes,
diff --git a/libs/bssl/src/ec_key.rs b/libs/bssl/src/ec_key.rs
index 7e677c4..6436be3 100644
--- a/libs/bssl/src/ec_key.rs
+++ b/libs/bssl/src/ec_key.rs
@@ -45,7 +45,7 @@
type Coordinate = [u8; P256_AFFINE_COORDINATE_SIZE];
/// Wrapper of an `EC_KEY` object, representing a public or private EC key.
-pub struct EcKey(NonNull<EC_KEY>);
+pub struct EcKey(pub(crate) NonNull<EC_KEY>);
impl Drop for EcKey {
fn drop(&mut self) {
diff --git a/libs/bssl/src/evp.rs b/libs/bssl/src/evp.rs
new file mode 100644
index 0000000..30bfc21
--- /dev/null
+++ b/libs/bssl/src/evp.rs
@@ -0,0 +1,90 @@
+// Copyright 2023, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Wrappers of the EVP functions in BoringSSL evp.h.
+
+use crate::cbb::CbbFixed;
+use crate::ec_key::EcKey;
+use crate::util::{check_int_result, to_call_failed_error};
+use alloc::vec::Vec;
+use bssl_avf_error::{ApiName, Result};
+use bssl_ffi::{
+ CBB_flush, CBB_len, EVP_PKEY_free, EVP_PKEY_new, EVP_PKEY_set1_EC_KEY, EVP_marshal_public_key,
+ EVP_PKEY,
+};
+use core::ptr::NonNull;
+
+/// Wrapper of an `EVP_PKEY` object, representing a public or private key.
+pub struct EvpPKey {
+ pkey: NonNull<EVP_PKEY>,
+ /// Since this struct owns the inner key, the inner key remains valid as
+ /// long as the pointer to `EVP_PKEY` is valid.
+ _inner_key: EcKey,
+}
+
+impl Drop for EvpPKey {
+ fn drop(&mut self) {
+ // SAFETY: It is safe because `EVP_PKEY` has been allocated by BoringSSL and isn't
+ // used after this.
+ unsafe { EVP_PKEY_free(self.pkey.as_ptr()) }
+ }
+}
+
+/// Creates a new empty `EVP_PKEY`.
+fn new_pkey() -> Result<NonNull<EVP_PKEY>> {
+ // SAFETY: The returned pointer is checked below.
+ let key = unsafe { EVP_PKEY_new() };
+ NonNull::new(key).ok_or(to_call_failed_error(ApiName::EVP_PKEY_new))
+}
+
+impl TryFrom<EcKey> for EvpPKey {
+ type Error = bssl_avf_error::Error;
+
+ fn try_from(key: EcKey) -> Result<Self> {
+ let pkey = new_pkey()?;
+ // SAFETY: The function only sets the inner key of the initialized and
+ // non-null `EVP_PKEY` to point to the given `EC_KEY`. It only reads from
+ // and writes to the initialized `EVP_PKEY`.
+ // Since this struct owns the inner key, the inner key remains valid as
+ // long as `EVP_PKEY` is valid.
+ let ret = unsafe { EVP_PKEY_set1_EC_KEY(pkey.as_ptr(), key.0.as_ptr()) };
+ check_int_result(ret, ApiName::EVP_PKEY_set1_EC_KEY)?;
+ Ok(Self { pkey, _inner_key: key })
+ }
+}
+
+impl EvpPKey {
+ /// Returns a DER-encoded SubjectPublicKeyInfo structure as specified
+ /// in RFC 5280 s4.1.2.7:
+ ///
+ /// https://www.rfc-editor.org/rfc/rfc5280.html#section-4.1.2.7
+ pub fn subject_public_key_info(&self) -> Result<Vec<u8>> {
+ const CAPACITY: usize = 256;
+ let mut buf = [0u8; CAPACITY];
+ let mut cbb = CbbFixed::new(buf.as_mut());
+ // SAFETY: The function only write bytes to the buffer managed by the valid `CBB`.
+ // The inner key in `EVP_PKEY` was set to a valid key when the object was created.
+ // As this struct owns the inner key, the inner key is guaranteed to be valid
+ // throughout the execution of the function.
+ let ret = unsafe { EVP_marshal_public_key(cbb.as_mut(), self.pkey.as_ptr()) };
+ check_int_result(ret, ApiName::EVP_marshal_public_key)?;
+ // SAFETY: This is safe because the CBB pointer is a valid pointer initialized with
+ // `CBB_init_fixed()`.
+ check_int_result(unsafe { CBB_flush(cbb.as_mut()) }, ApiName::CBB_flush)?;
+ // SAFETY: This is safe because the CBB pointer is initialized with `CBB_init_fixed()`,
+ // and it has been flushed, thus it has no active children.
+ let len = unsafe { CBB_len(cbb.as_ref()) };
+ Ok(buf.get(0..len).ok_or(to_call_failed_error(ApiName::CBB_len))?.to_vec())
+ }
+}
diff --git a/libs/bssl/src/lib.rs b/libs/bssl/src/lib.rs
index 8e3abcf..e378386 100644
--- a/libs/bssl/src/lib.rs
+++ b/libs/bssl/src/lib.rs
@@ -24,6 +24,7 @@
mod digest;
mod ec_key;
mod err;
+mod evp;
mod hkdf;
mod hmac;
mod rand;
@@ -37,6 +38,7 @@
pub use cbs::Cbs;
pub use digest::Digester;
pub use ec_key::{EcKey, ZVec};
+pub use evp::EvpPKey;
pub use hkdf::hkdf;
pub use hmac::hmac_sha256;
pub use rand::rand_bytes;
diff --git a/libs/bssl/tests/eckey_test.rs b/libs/bssl/tests/eckey_test.rs
index 3dd243c..968af63 100644
--- a/libs/bssl/tests/eckey_test.rs
+++ b/libs/bssl/tests/eckey_test.rs
@@ -12,8 +12,18 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-use bssl_avf::{sha256, ApiName, EcKey, EcdsaError, Error, Result};
+use bssl_avf::{sha256, ApiName, EcKey, EcdsaError, Error, EvpPKey, Result};
use coset::CborSerializable;
+use spki::{
+ der::{AnyRef, Decode},
+ AlgorithmIdentifier, ObjectIdentifier, SubjectPublicKeyInfo,
+};
+
+/// OID value for general-use NIST EC keys held in PKCS#8 and X.509; see RFC 5480 s2.1.1.
+const X509_NIST_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.2.1");
+
+/// OID value in `AlgorithmIdentifier.parameters` for P-256; see RFC 5480 s2.1.1.1.
+const ALGO_PARAM_P256_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.3.1.7");
const MESSAGE1: &[u8] = b"test message 1";
const MESSAGE2: &[u8] = b"test message 2";
@@ -30,6 +40,23 @@
}
#[test]
+fn subject_public_key_info_serialization() -> Result<()> {
+ let mut ec_key = EcKey::new_p256()?;
+ ec_key.generate_key()?;
+ let pkey: EvpPKey = ec_key.try_into()?;
+ let subject_public_key_info = pkey.subject_public_key_info()?;
+
+ let subject_public_key_info = SubjectPublicKeyInfo::from_der(&subject_public_key_info).unwrap();
+ let expected_algorithm = AlgorithmIdentifier {
+ oid: X509_NIST_OID,
+ parameters: Some(AnyRef::from(&ALGO_PARAM_P256_OID)),
+ };
+ assert_eq!(expected_algorithm, subject_public_key_info.algorithm);
+ assert!(!subject_public_key_info.subject_public_key.to_vec().is_empty());
+ Ok(())
+}
+
+#[test]
fn cose_public_key_serialization() -> Result<()> {
let mut ec_key = EcKey::new_p256()?;
ec_key.generate_key()?;