[rkp] Generate EC P-256 key pair for the RKP HAL
This cl generates the EC P-256 key pair needed by the RKP HAL.
The MAC of the public key and wrapping of the private key will be
handled in subsequent cls.
Bug: 299055662
Test: atest rialto_test
Change-Id: I253130e62bbf70cb5a7abe1a056fc4f77d30aa6e
diff --git a/libs/service_vm_comm/src/lib.rs b/libs/service_vm_comm/src/lib.rs
index d8f7bd7..7bcb9cd 100644
--- a/libs/service_vm_comm/src/lib.rs
+++ b/libs/service_vm_comm/src/lib.rs
@@ -23,7 +23,7 @@
mod vsock;
pub use message::{
- EcdsaP256KeyPair, GenerateCertificateRequestParams, Request, RequestProcessingError, Response,
- ServiceVmRequest,
+ BoringSSLApiName, EcdsaP256KeyPair, GenerateCertificateRequestParams, Request,
+ RequestProcessingError, Response, ServiceVmRequest,
};
pub use vsock::VmType;
diff --git a/libs/service_vm_comm/src/message.rs b/libs/service_vm_comm/src/message.rs
index 407c5e5..4a4bb4d 100644
--- a/libs/service_vm_comm/src/message.rs
+++ b/libs/service_vm_comm/src/message.rs
@@ -15,7 +15,6 @@
//! This module contains the requests and responses definitions exchanged
//! between the host and the service VM.
-use alloc::string::String;
use alloc::vec::Vec;
use core::fmt;
use log::error;
@@ -71,11 +70,24 @@
Err(RequestProcessingError),
}
+/// BoringSSL API names.
+#[allow(missing_docs)]
+#[allow(non_camel_case_types)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
+pub enum BoringSSLApiName {
+ CBB_flush,
+ CBB_len,
+ EC_KEY_check_key,
+ EC_KEY_generate_key,
+ EC_KEY_marshal_private_key,
+ EC_KEY_new_by_curve_name,
+}
+
/// Errors related to request processing.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum RequestProcessingError {
/// Failed to invoke a BoringSSL API.
- BoringSSLCallFailed(String),
+ BoringSSLCallFailed(BoringSSLApiName),
/// An error happened during the interaction with coset.
CosetError,
@@ -88,7 +100,7 @@
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::BoringSSLCallFailed(api_name) => {
- write!(f, "Failed to invoke a BoringSSL API: {api_name}")
+ write!(f, "Failed to invoke a BoringSSL API: {api_name:?}")
}
Self::CosetError => write!(f, "Encountered an error with coset"),
Self::InvalidMac => write!(f, "A key to sign lacks a valid MAC."),
diff --git a/rialto/Android.bp b/rialto/Android.bp
index 05775c2..cb3e477 100644
--- a/rialto/Android.bp
+++ b/rialto/Android.bp
@@ -9,6 +9,7 @@
defaults: ["vmbase_ffi_defaults"],
rustlibs: [
"libaarch64_paging",
+ "libbssl_ffi_nostd",
"libciborium_io_nostd",
"libciborium_nostd",
"libdiced_open_dice_nostd",
@@ -21,6 +22,10 @@
"libtinyvec_nostd",
"libvirtio_drivers",
"libvmbase",
+ "libzeroize_nostd",
+ ],
+ static_libs: [
+ "libcrypto_baremetal",
],
}
diff --git a/rialto/src/main.rs b/rialto/src/main.rs
index 8b73130..43215a0 100644
--- a/rialto/src/main.rs
+++ b/rialto/src/main.rs
@@ -29,6 +29,7 @@
use crate::error::{Error, Result};
use crate::fdt::read_dice_range_from;
use alloc::boxed::Box;
+use bssl_ffi::CRYPTO_library_init;
use ciborium_io::Write;
use core::num::NonZeroUsize;
use core::slice;
@@ -134,6 +135,13 @@
e
})?;
}
+
+ // Initializes the crypto library before any crypto operations and after the heap is
+ // initialized.
+ // SAFETY: It is safe to call this function multiple times and concurrently.
+ unsafe {
+ CRYPTO_library_init();
+ }
let bcc_handover: Box<dyn DiceArtifacts> = match vm_type() {
VmType::ProtectedVm => {
let dice_range = read_dice_range_from(fdt)?;
diff --git a/rialto/src/requests/ec_key.rs b/rialto/src/requests/ec_key.rs
new file mode 100644
index 0000000..2f3fe90
--- /dev/null
+++ b/rialto/src/requests/ec_key.rs
@@ -0,0 +1,131 @@
+// 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.
+
+//! Contains struct and functions that wraps the API related to EC_KEY in
+//! BoringSSL.
+
+use alloc::vec::Vec;
+use bssl_ffi::CBB_flush;
+use bssl_ffi::CBB_init_fixed;
+use bssl_ffi::CBB_len;
+use bssl_ffi::EC_KEY_free;
+use bssl_ffi::EC_KEY_generate_key;
+use bssl_ffi::EC_KEY_marshal_private_key;
+use bssl_ffi::EC_KEY_new_by_curve_name;
+use bssl_ffi::NID_X9_62_prime256v1; // EC P-256 CURVE Nid
+use bssl_ffi::EC_KEY;
+use core::mem::MaybeUninit;
+use core::ptr::NonNull;
+use core::result;
+use service_vm_comm::{BoringSSLApiName, RequestProcessingError};
+use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
+
+type Result<T> = result::Result<T, RequestProcessingError>;
+
+/// Wrapper of an `EC_KEY` object, representing a public or private EC key.
+pub struct EcKey(NonNull<EC_KEY>);
+
+impl Drop for EcKey {
+ fn drop(&mut self) {
+ // SAFETY: It is safe because the key has been allocated by BoringSSL and isn't
+ // used after this.
+ unsafe { EC_KEY_free(self.0.as_ptr()) }
+ }
+}
+
+impl EcKey {
+ /// Creates a new EC P-256 key pair.
+ pub fn new_p256() -> Result<Self> {
+ // SAFETY: The returned pointer is checked below.
+ let ec_key = unsafe { EC_KEY_new_by_curve_name(NID_X9_62_prime256v1) };
+ let mut ec_key = NonNull::new(ec_key).map(Self).ok_or(
+ RequestProcessingError::BoringSSLCallFailed(BoringSSLApiName::EC_KEY_new_by_curve_name),
+ )?;
+ ec_key.generate_key()?;
+ Ok(ec_key)
+ }
+
+ /// Generates a random, private key, calculates the corresponding public key and stores both
+ /// in the `EC_KEY`.
+ fn generate_key(&mut self) -> Result<()> {
+ // SAFETY: The non-null pointer is created with `EC_KEY_new_by_curve_name` and should
+ // point to a valid `EC_KEY`.
+ // The randomness is provided by `getentropy()` in `vmbase`.
+ let ret = unsafe { EC_KEY_generate_key(self.0.as_ptr()) };
+ check_int_result(ret, BoringSSLApiName::EC_KEY_generate_key)
+ }
+
+ // TODO(b/300068317): Returns the CoseKey for the public key.
+
+ /// Returns the DER-encoded ECPrivateKey structure described in RFC 5915 Section 3:
+ ///
+ /// https://datatracker.ietf.org/doc/html/rfc5915#section-3
+ pub fn private_key(&self) -> Result<ZVec> {
+ const CAPACITY: usize = 256;
+ let mut buf = Zeroizing::new([0u8; CAPACITY]);
+ // SAFETY: `CBB_init_fixed()` is infallible and always returns one.
+ // The `buf` is never moved and remains valid during the lifetime of `cbb`.
+ let mut cbb = unsafe {
+ let mut cbb = MaybeUninit::uninit();
+ CBB_init_fixed(cbb.as_mut_ptr(), buf.as_mut_ptr(), buf.len());
+ cbb.assume_init()
+ };
+ let enc_flags = 0;
+ let ret =
+ // SAFETY: The function only write bytes to the buffer managed by the valid `CBB`
+ // object, and the key has been allocated by BoringSSL.
+ unsafe { EC_KEY_marshal_private_key(&mut cbb, self.0.as_ptr(), enc_flags) };
+
+ check_int_result(ret, BoringSSLApiName::EC_KEY_marshal_private_key)?;
+ // SAFETY: This is safe because the CBB pointer is a valid pointer initialized with
+ // `CBB_init_fixed()`.
+ check_int_result(unsafe { CBB_flush(&mut cbb) }, BoringSSLApiName::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) };
+ Ok(buf
+ .get(0..len)
+ .ok_or(RequestProcessingError::BoringSSLCallFailed(BoringSSLApiName::CBB_len))?
+ .to_vec()
+ .into())
+ }
+}
+
+/// A u8 vector that is zeroed when dropped.
+#[derive(Zeroize, ZeroizeOnDrop)]
+pub struct ZVec(Vec<u8>);
+
+impl ZVec {
+ /// Extracts a slice containing the entire vector.
+ pub fn as_slice(&self) -> &[u8] {
+ &self.0[..]
+ }
+}
+
+impl From<Vec<u8>> for ZVec {
+ fn from(v: Vec<u8>) -> Self {
+ Self(v)
+ }
+}
+
+fn check_int_result(ret: i32, api_name: BoringSSLApiName) -> Result<()> {
+ if ret == 1 {
+ Ok(())
+ } else {
+ assert_eq!(ret, 0, "Unexpected return value {ret} for {api_name:?}");
+ Err(RequestProcessingError::BoringSSLCallFailed(api_name))
+ }
+}
+
+// TODO(b/301068421): Unit tests the EcKey.
diff --git a/rialto/src/requests/mod.rs b/rialto/src/requests/mod.rs
index 2ed568c..d70791f 100644
--- a/rialto/src/requests/mod.rs
+++ b/rialto/src/requests/mod.rs
@@ -15,6 +15,7 @@
//! This module contains functions for the request processing.
mod api;
+mod ec_key;
mod rkp;
pub use api::process_request;
diff --git a/rialto/src/requests/rkp.rs b/rialto/src/requests/rkp.rs
index a73b9f4..66d3603 100644
--- a/rialto/src/requests/rkp.rs
+++ b/rialto/src/requests/rkp.rs
@@ -13,8 +13,9 @@
// limitations under the License.
//! This module contains functions related to the attestation of the
-//! service VM via the RKP (Remote Key Provisionning) server.
+//! service VM via the RKP (Remote Key Provisioning) server.
+use super::ec_key::EcKey;
use alloc::vec::Vec;
use core::result;
use diced_open_dice::DiceArtifacts;
@@ -25,7 +26,13 @@
pub(super) fn generate_ecdsa_p256_key_pair(
_dice_artifacts: &dyn DiceArtifacts,
) -> Result<EcdsaP256KeyPair> {
- // TODO(b/299055662): Generate the key pair.
+ let ec_key = EcKey::new_p256()?;
+
+ // TODO(b/279425980): Encrypt the private key in a key blob.
+ // Remove the printing of the private key.
+ log::debug!("Private key: {:?}", ec_key.private_key()?.as_slice());
+
+ // TODO(b/300068317): Build MACed public key.
let key_pair = EcdsaP256KeyPair { maced_public_key: Vec::new(), key_blob: Vec::new() };
Ok(key_pair)
}