Merge "[rkp] Introduce a new request type for attestation request" into main
diff --git a/service_vm/comm/src/client_vm_csr.cddl b/service_vm/comm/src/client_vm_csr.cddl
new file mode 100644
index 0000000..bbc709a
--- /dev/null
+++ b/service_vm/comm/src/client_vm_csr.cddl
@@ -0,0 +1,62 @@
+; CDDL for the CSR sent from the client VM to the RKP VM for pVM remote attestation.
+
+Csr = [
+ DiceCertChain, ; The DICE chain containing measurement of the client VM. See
+ ; keymint/generateCertificateRequestV2.cddl for the DiceCertChain
+ ; definition.
+ SignedData,
+]
+
+; COSE_Sign [RFC9052 s4.1]
+SignedData = [
+ protected: {}, ; The signing algorithms are specified in each signature
+ ; separately.
+ unprotected: {},
+ payload: bstr .cbor CsrPayload,
+ Signatures,
+]
+
+CsrPayload = [ ; CBOR Array defining the payload for CSR
+ challenge: bstr .size (0..64), ; The challenge is provided by the client server.
+ ; It will be included in the certificate chain in the
+ ; attestation result, serving as proof of the freshness
+ ; of the result.
+ PublicKey, ; COSE_Key encoded EC P-256 public key [ RFC9053 s7.1.1 ]
+ ; to be attested. See keymint/PublicKey.cddl for the
+ ; definition, the test flag `-70000` is never used.
+]
+
+Signatures = [
+ dice_cdi_leaf_signature: COSE_Signature_Dice_Cdi_Leaf,
+ attestation_key_signature: COSE_Signature_Attestation_Key,
+]
+
+; COSE_Signature [RFC9052 s4.1]
+COSE_Signature_Dice_Cdi_Leaf = [
+ protected: bstr .cbor { 1: AlgorithmEdDSA },
+ unprotected: {},
+ signature: bstr, ; Ed25519(CDI_Leaf_Priv, SigStruct)
+]
+
+; COSE_Signature [RFC9052 s4.1]
+COSE_Signature_Attestation_Key = [
+ protected: bstr .cbor { 1: AlgorithmES256 },
+ unprotected: {},
+ signature: bstr, ; ECDSA(PrivateKey, SigStruct)
+]
+
+; Sig_structure for SignedData [ RFC9052 s4.4 ]
+SigStruct = {
+ context: "Signature",
+ external_aad: bstr .size 0,
+ payload: bstr .cbor CsrPayload,
+}
+
+; ASN.1 DER-encoded EC P-256 ECPrivateKey [ RFC 5915 s3 ]:
+; ECPrivateKey ::= SEQUENCE {
+; version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1),
+; privateKey OCTET STRING,
+; parameters [0] ECParameters {{ NamedCurve }} OPTIONAL,
+; publicKey [1] BIT STRING OPTIONAL
+;}
+PrivateKey = bstr
diff --git a/service_vm/comm/src/csr.rs b/service_vm/comm/src/csr.rs
index 5e1cbad..e913bb5 100644
--- a/service_vm/comm/src/csr.rs
+++ b/service_vm/comm/src/csr.rs
@@ -21,6 +21,8 @@
use coset::{self, CborSerializable, CoseError};
/// Represents a CSR sent from the client VM to the service VM for attestation.
+///
+/// See client_vm_csr.cddl for the definition of the CSR.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Csr {
/// The DICE certificate chain of the client VM.
diff --git a/service_vm/comm/src/lib.rs b/service_vm/comm/src/lib.rs
index 0818f24..bb85a26 100644
--- a/service_vm/comm/src/lib.rs
+++ b/service_vm/comm/src/lib.rs
@@ -25,7 +25,7 @@
pub use csr::{Csr, CsrPayload};
pub use message::{
- EcdsaP256KeyPair, GenerateCertificateRequestParams, Request, RequestProcessingError, Response,
- ServiceVmRequest,
+ ClientVmAttestationParams, EcdsaP256KeyPair, GenerateCertificateRequestParams, Request,
+ RequestProcessingError, Response, ServiceVmRequest,
};
pub use vsock::VmType;
diff --git a/service_vm/comm/src/message.rs b/service_vm/comm/src/message.rs
index f8d7420..6dd0ccd 100644
--- a/service_vm/comm/src/message.rs
+++ b/service_vm/comm/src/message.rs
@@ -50,6 +50,22 @@
/// Creates a certificate signing request to be sent to the
/// provisioning server.
GenerateCertificateRequest(GenerateCertificateRequestParams),
+
+ /// Requests the service VM to attest the client VM and issue a certificate
+ /// if the attestation succeeds.
+ RequestClientVmAttestation(ClientVmAttestationParams),
+}
+
+/// Represents the params passed to `Request::RequestClientVmAttestation`.
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct ClientVmAttestationParams {
+ /// The CBOR-encoded CSR signed by the CDI_Leaf_Priv of the client VM's DICE chain
+ /// and the private key to be attested.
+ /// See client_vm_csr.cddl for the definition of the CSR.
+ pub csr: Vec<u8>,
+
+ /// The key blob retrieved from RKPD by virtualizationservice.
+ pub remotely_provisioned_key_blob: Vec<u8>,
}
/// Represents a response to a request sent to the service VM.
@@ -66,6 +82,11 @@
/// Returns a CBOR Certificate Signing Request (Csr) serialized into a byte array.
GenerateCertificateRequest(Vec<u8>),
+ /// Returns a certificate covering the public key to be attested in the provided CSR.
+ /// The certificate is signed by the remotely provisioned private key and also
+ /// includes an extension that describes the attested client VM.
+ RequestClientVmAttestation(Vec<u8>),
+
/// Encountered an error during the request processing.
Err(RequestProcessingError),
}
@@ -93,6 +114,12 @@
/// The DICE chain of the service VM is missing.
MissingDiceChain,
+
+ /// Failed to decrypt the remotely provisioned key blob.
+ FailedToDecryptKeyBlob,
+
+ /// The requested operation has not been implemented.
+ OperationUnimplemented,
}
impl fmt::Display for RequestProcessingError {
@@ -109,6 +136,12 @@
write!(f, "An error happened when serializing to/from a CBOR Value.")
}
Self::MissingDiceChain => write!(f, "The DICE chain of the service VM is missing"),
+ Self::FailedToDecryptKeyBlob => {
+ write!(f, "Failed to decrypt the remotely provisioned key blob")
+ }
+ Self::OperationUnimplemented => {
+ write!(f, "The requested operation has not been implemented")
+ }
}
}
}
diff --git a/service_vm/requests/src/api.rs b/service_vm/requests/src/api.rs
index eae0370..315d2af 100644
--- a/service_vm/requests/src/api.rs
+++ b/service_vm/requests/src/api.rs
@@ -14,6 +14,7 @@
//! This module contains the main API for the request processing module.
+use crate::client_vm;
use crate::rkp;
use alloc::vec::Vec;
use diced_open_dice::DiceArtifacts;
@@ -31,6 +32,8 @@
rkp::generate_certificate_request(p, dice_artifacts)
.map_or_else(Response::Err, Response::GenerateCertificateRequest)
}
+ Request::RequestClientVmAttestation(p) => client_vm::request_attestation(p, dice_artifacts)
+ .map_or_else(Response::Err, Response::RequestClientVmAttestation),
}
}
diff --git a/service_vm/requests/src/client_vm.rs b/service_vm/requests/src/client_vm.rs
new file mode 100644
index 0000000..1081f3a
--- /dev/null
+++ b/service_vm/requests/src/client_vm.rs
@@ -0,0 +1,46 @@
+// 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.
+
+//! This module contains functions related to the attestation of the
+//! client VM.
+
+use crate::keyblob::decrypt_private_key;
+use alloc::vec::Vec;
+use core::result;
+use diced_open_dice::DiceArtifacts;
+use log::error;
+use service_vm_comm::{ClientVmAttestationParams, RequestProcessingError};
+
+type Result<T> = result::Result<T, RequestProcessingError>;
+
+pub(super) fn request_attestation(
+ params: ClientVmAttestationParams,
+ dice_artifacts: &dyn DiceArtifacts,
+) -> Result<Vec<u8>> {
+ // TODO(b/309440321): Verify the signatures in the csr.
+
+ // TODO(b/278717513): Compare client VM's DICE chain up to pvmfw cert with
+ // RKP VM's DICE chain.
+
+ let _private_key =
+ decrypt_private_key(¶ms.remotely_provisioned_key_blob, dice_artifacts.cdi_seal())
+ .map_err(|e| {
+ error!("Failed to decrypt the remotely provisioned key blob: {e}");
+ RequestProcessingError::FailedToDecryptKeyBlob
+ })?;
+
+ // TODO(b/309441500): Build a new certificate signed with the remotely provisioned
+ // private key.
+ Err(RequestProcessingError::OperationUnimplemented)
+}
diff --git a/service_vm/requests/src/keyblob.rs b/service_vm/requests/src/keyblob.rs
index 456c879..1fb7a67 100644
--- a/service_vm/requests/src/keyblob.rs
+++ b/service_vm/requests/src/keyblob.rs
@@ -20,8 +20,6 @@
use core::result;
use serde::{Deserialize, Serialize};
use service_vm_comm::RequestProcessingError;
-// TODO(b/241428146): This will be used once the retrieval mechanism is available.
-#[cfg(test)]
use zeroize::Zeroizing;
type Result<T> = result::Result<T, RequestProcessingError>;
@@ -61,9 +59,6 @@
EncryptedKeyBlobV1::new(private_key, kek_secret).map(Self::V1)
}
- // TODO(b/241428146): Use this function to decrypt the retrieved keyblob once the retrieval
- // mechanism is available.
- #[cfg(test)]
pub(crate) fn decrypt_private_key(&self, kek_secret: &[u8]) -> Result<Zeroizing<Vec<u8>>> {
match self {
Self::V1(blob) => blob.decrypt_private_key(kek_secret),
@@ -85,7 +80,6 @@
Ok(Self { kek_salt, encrypted_private_key: ciphertext.to_vec() })
}
- #[cfg(test)]
fn decrypt_private_key(&self, kek_secret: &[u8]) -> Result<Zeroizing<Vec<u8>>> {
let kek = hkdf::<32>(kek_secret, &self.kek_salt, KEK_INFO, Digester::sha512())?;
let mut out = Zeroizing::new(vec![0u8; self.encrypted_private_key.len()]);
@@ -101,6 +95,15 @@
}
}
+pub(crate) fn decrypt_private_key(
+ encrypted_key_blob: &[u8],
+ kek_secret: &[u8],
+) -> Result<Zeroizing<Vec<u8>>> {
+ let key_blob: EncryptedKeyBlob = cbor_util::deserialize(encrypted_key_blob)?;
+ let private_key = key_blob.decrypt_private_key(kek_secret)?;
+ Ok(private_key)
+}
+
#[cfg(test)]
mod tests {
use super::*;
@@ -127,8 +130,7 @@
fn decrypting_keyblob_succeeds_with_the_same_kek() -> Result<()> {
let encrypted_key_blob =
cbor_util::serialize(&EncryptedKeyBlob::new(&TEST_KEY, &TEST_SECRET1)?)?;
- let encrypted_key_blob: EncryptedKeyBlob = cbor_util::deserialize(&encrypted_key_blob)?;
- let decrypted_key = encrypted_key_blob.decrypt_private_key(&TEST_SECRET1)?;
+ let decrypted_key = decrypt_private_key(&encrypted_key_blob, &TEST_SECRET1)?;
assert_eq!(TEST_KEY, decrypted_key.as_slice());
Ok(())
@@ -138,8 +140,7 @@
fn decrypting_keyblob_fails_with_a_different_kek() -> Result<()> {
let encrypted_key_blob =
cbor_util::serialize(&EncryptedKeyBlob::new(&TEST_KEY, &TEST_SECRET1)?)?;
- let encrypted_key_blob: EncryptedKeyBlob = cbor_util::deserialize(&encrypted_key_blob)?;
- let err = encrypted_key_blob.decrypt_private_key(&TEST_SECRET2).unwrap_err();
+ let err = decrypt_private_key(&encrypted_key_blob, &TEST_SECRET2).unwrap_err();
let expected_err: RequestProcessingError =
Error::CallFailed(ApiName::EVP_AEAD_CTX_open, CipherError::BadDecrypt.into()).into();
diff --git a/service_vm/requests/src/lib.rs b/service_vm/requests/src/lib.rs
index e3c5794..b2db298 100644
--- a/service_vm/requests/src/lib.rs
+++ b/service_vm/requests/src/lib.rs
@@ -19,6 +19,7 @@
extern crate alloc;
mod api;
+mod client_vm;
mod keyblob;
mod pub_key;
mod rkp;