Merge "[attestation] Validate DICE chain signatures and CSR signature" into main
diff --git a/libs/bssl/error/src/lib.rs b/libs/bssl/error/src/lib.rs
index 7f01c6c..82a2d5e 100644
--- a/libs/bssl/error/src/lib.rs
+++ b/libs/bssl/error/src/lib.rs
@@ -91,6 +91,7 @@
ECDSA_sign,
ECDSA_size,
ECDSA_verify,
+ ED25519_verify,
EVP_AEAD_CTX_new,
EVP_AEAD_CTX_open,
EVP_AEAD_CTX_seal,
diff --git a/libs/bssl/src/curve25519.rs b/libs/bssl/src/curve25519.rs
new file mode 100644
index 0000000..499a3d0
--- /dev/null
+++ b/libs/bssl/src/curve25519.rs
@@ -0,0 +1,39 @@
+// 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 Curve25519 related functions in BoringSSL curve25519.h.
+
+use crate::util::check_int_result;
+use bssl_avf_error::{ApiName, Result};
+
+const ED25519_PUBLIC_KEY_LEN: usize = bssl_ffi::ED25519_PUBLIC_KEY_LEN as usize;
+const ED25519_SIGNATURE_LEN: usize = bssl_ffi::ED25519_SIGNATURE_LEN as usize;
+
+/// Verifies the signature of a message with the given ED25519 public key.
+pub fn ed25519_verify(
+ message: &[u8],
+ signature: &[u8; ED25519_SIGNATURE_LEN],
+ public_key: &[u8; ED25519_PUBLIC_KEY_LEN],
+) -> Result<()> {
+ // SAFETY: The function only reads the parameters within their bounds.
+ let ret = unsafe {
+ bssl_ffi::ED25519_verify(
+ message.as_ptr(),
+ message.len(),
+ signature.as_ptr(),
+ public_key.as_ptr(),
+ )
+ };
+ check_int_result(ret, ApiName::ED25519_verify)
+}
diff --git a/libs/bssl/src/lib.rs b/libs/bssl/src/lib.rs
index a420168..ad51b61 100644
--- a/libs/bssl/src/lib.rs
+++ b/libs/bssl/src/lib.rs
@@ -21,6 +21,7 @@
mod aead;
mod cbb;
mod cbs;
+mod curve25519;
mod digest;
mod ec_key;
mod err;
@@ -36,6 +37,7 @@
pub use aead::{Aead, AeadContext, AES_GCM_NONCE_LENGTH};
pub use cbb::CbbFixed;
pub use cbs::Cbs;
+pub use curve25519::ed25519_verify;
pub use digest::Digester;
pub use ec_key::{EcKey, ZVec};
pub use evp::{PKey, PKeyType};
diff --git a/service_vm/requests/src/client_vm.rs b/service_vm/requests/src/client_vm.rs
index cfdac2d..871b5f1 100644
--- a/service_vm/requests/src/client_vm.rs
+++ b/service_vm/requests/src/client_vm.rs
@@ -30,6 +30,7 @@
type Result<T> = result::Result<T, RequestProcessingError>;
+const DICE_CDI_LEAF_SIGNATURE_INDEX: usize = 0;
const ATTESTATION_KEY_SIGNATURE_INDEX: usize = 1;
pub(super) fn request_attestation(
@@ -58,8 +59,9 @@
let aad = &[];
// Verifies the first signature with the leaf private key in the DICE chain.
- // TODO(b/310931749): Verify the first signature with CDI_Leaf_Pub of
- // the DICE chain in `cose_sign`.
+ cose_sign.verify_signature(DICE_CDI_LEAF_SIGNATURE_INDEX, aad, |signature, message| {
+ client_vm_dice_chain.microdroid_payload().subject_public_key.verify(signature, message)
+ })?;
// Verifies the second signature with the public key in the CSR payload.
let ec_public_key = EcKey::from_cose_public_key_slice(&csr_payload.public_key)?;
diff --git a/service_vm/requests/src/dice.rs b/service_vm/requests/src/dice.rs
index 557b678..6f67049 100644
--- a/service_vm/requests/src/dice.rs
+++ b/service_vm/requests/src/dice.rs
@@ -16,15 +16,19 @@
use alloc::string::String;
use alloc::vec::Vec;
+use bssl_avf::{ed25519_verify, Digester, EcKey};
use cbor_util::{
- cbor_value_type, value_to_array, value_to_byte_array, value_to_bytes, value_to_map,
- value_to_num, value_to_text,
+ cbor_value_type, get_label_value, get_label_value_as_bytes, value_to_array,
+ value_to_byte_array, value_to_bytes, value_to_map, value_to_num, value_to_text,
};
use ciborium::value::Value;
use core::cell::OnceCell;
use core::result;
use coset::{
- self, iana, AsCborValue, CborSerializable, CoseError, CoseKey, CoseSign1, KeyOperation,
+ self,
+ iana::{self, EnumI64},
+ Algorithm, AsCborValue, CborSerializable, CoseError, CoseKey, CoseSign1, KeyOperation, KeyType,
+ Label,
};
use diced_open_dice::{DiceMode, HASH_SIZE};
use log::error;
@@ -58,7 +62,7 @@
/// ]
#[derive(Debug, Clone)]
pub(crate) struct ClientVmDiceChain {
- pub(crate) payloads: Vec<DiceChainEntryPayload>,
+ payloads: Vec<DiceChainEntryPayload>,
}
impl ClientVmDiceChain {
@@ -130,7 +134,7 @@
&self.payloads[self.payloads.len() - 2]
}
- fn microdroid_payload(&self) -> &DiceChainEntryPayload {
+ pub(crate) fn microdroid_payload(&self) -> &DiceChainEntryPayload {
&self.payloads[self.payloads.len() - 1]
}
@@ -198,15 +202,69 @@
}
}
+impl PublicKey {
+ /// Verifies the signature of the provided message with the public key.
+ ///
+ /// This function supports the following key/algorithm types as specified in
+ /// hardware/interfaces/security/rkp/aidl/android/hardware/security/keymint/
+ /// generateCertificateRequestV2.cddl:
+ ///
+ /// PubKeyEd25519 / PubKeyECDSA256 / PubKeyECDSA384
+ pub(crate) fn verify(&self, signature: &[u8], message: &[u8]) -> Result<()> {
+ match &self.0.kty {
+ KeyType::Assigned(iana::KeyType::EC2) => {
+ let public_key = EcKey::from_cose_public_key(&self.0)?;
+ let Some(Algorithm::Assigned(alg)) = self.0.alg else {
+ error!("Invalid algorithm in COSE key {:?}", self.0.alg);
+ return Err(RequestProcessingError::InvalidDiceChain);
+ };
+ let digester = match alg {
+ iana::Algorithm::ES256 => Digester::sha256(),
+ iana::Algorithm::ES384 => Digester::sha384(),
+ _ => {
+ error!("Unsupported algorithm in EC2 key: {:?}", alg);
+ return Err(RequestProcessingError::InvalidDiceChain);
+ }
+ };
+ let digest = digester.digest(message)?;
+ Ok(public_key.ecdsa_verify(signature, &digest)?)
+ }
+ KeyType::Assigned(iana::KeyType::OKP) => {
+ let curve_type =
+ get_label_value(&self.0, Label::Int(iana::OkpKeyParameter::Crv.to_i64()))?;
+ if curve_type != &Value::from(iana::EllipticCurve::Ed25519.to_i64()) {
+ error!("Unsupported curve type in OKP COSE key: {:?}", curve_type);
+ return Err(RequestProcessingError::OperationUnimplemented);
+ }
+ let x = get_label_value_as_bytes(
+ &self.0,
+ Label::Int(iana::OkpKeyParameter::X.to_i64()),
+ )?;
+ let public_key = x.try_into().map_err(|_| {
+ error!("Invalid ED25519 public key size: {}", x.len());
+ RequestProcessingError::InvalidDiceChain
+ })?;
+ let signature = signature.try_into().map_err(|_| {
+ error!("Invalid ED25519 signature size: {}", signature.len());
+ RequestProcessingError::InvalidDiceChain
+ })?;
+ Ok(ed25519_verify(message, signature, public_key)?)
+ }
+ kty => {
+ error!("Unsupported key type in COSE key: {:?}", kty);
+ Err(RequestProcessingError::OperationUnimplemented)
+ }
+ }
+ }
+}
+
/// Represents a partially decoded `DiceChainEntryPayload`. The whole payload is defined in:
///
/// hardware/interfaces/security/rkp/aidl/android/hardware/security/keymint/
/// generateCertificateRequestV2.cddl
#[derive(Debug, Clone)]
pub(crate) struct DiceChainEntryPayload {
- /// TODO(b/310931749): Verify the DICE chain entry using the subject public key.
- #[allow(dead_code)]
- subject_public_key: PublicKey,
+ pub(crate) subject_public_key: PublicKey,
mode: DiceMode,
/// TODO(b/271275206): Verify Microdroid kernel authority and code hashes.
#[allow(dead_code)]
@@ -221,10 +279,13 @@
/// extracts payload from the value.
fn validate_cose_signature_and_extract_payload(
value: Value,
- _authority_public_key: &PublicKey,
+ authority_public_key: &PublicKey,
) -> Result<Self> {
let cose_sign1 = CoseSign1::from_cbor_value(value)?;
- // TODO(b/310931749): Verify the DICE chain entry using `authority_public_key`.
+ let aad = &[]; // AAD is not used in DICE chain entry.
+ cose_sign1.verify_signature(aad, |signature, message| {
+ authority_public_key.verify(signature, message)
+ })?;
let payload = cose_sign1.payload.ok_or_else(|| {
error!("No payload found in the DICE chain entry");