• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! This module contains functions related to the attestation of the
16 //! client VM.
17 
18 use crate::cert;
19 use crate::dice::{ClientVmDiceChain, DiceChainEntryPayload};
20 use crate::keyblob::decrypt_private_key;
21 use alloc::vec::Vec;
22 use bssl_avf::{rand_bytes, sha256, Digester, EcKey, PKey};
23 use cbor_util::parse_value_array;
24 use ciborium::value::Value;
25 use core::result;
26 use coset::{AsCborValue, CborSerializable, CoseSign, CoseSign1};
27 use der::{Decode, Encode};
28 use diced_open_dice::{DiceArtifacts, HASH_SIZE};
29 use log::{debug, error, info};
30 use microdroid_kernel_hashes::{HASH_SIZE as KERNEL_HASH_SIZE, OS_HASHES};
31 use service_vm_comm::{ClientVmAttestationParams, Csr, CsrPayload, RequestProcessingError};
32 use x509_cert::{certificate::Certificate, name::Name};
33 
34 type Result<T> = result::Result<T, RequestProcessingError>;
35 
36 const DICE_CDI_LEAF_SIGNATURE_INDEX: usize = 0;
37 const ATTESTATION_KEY_SIGNATURE_INDEX: usize = 1;
38 
request_attestation( params: ClientVmAttestationParams, dice_artifacts: &dyn DiceArtifacts, vendor_hashtree_root_digest_from_dt: Option<&[u8]>, ) -> Result<Vec<u8>>39 pub(super) fn request_attestation(
40     params: ClientVmAttestationParams,
41     dice_artifacts: &dyn DiceArtifacts,
42     vendor_hashtree_root_digest_from_dt: Option<&[u8]>,
43 ) -> Result<Vec<u8>> {
44     let csr = Csr::from_cbor_slice(&params.csr)?;
45     let cose_sign = CoseSign::from_slice(&csr.signed_csr_payload)?;
46     let csr_payload = cose_sign.payload.as_ref().ok_or_else(|| {
47         error!("No CsrPayload found in the CSR");
48         RequestProcessingError::InternalError
49     })?;
50     let csr_payload = CsrPayload::from_cbor_slice(csr_payload)?;
51 
52     let client_vm_dice_chain = validate_client_vm_dice_chain(
53         &csr.dice_cert_chain,
54         dice_artifacts.bcc().ok_or(RequestProcessingError::MissingDiceChain)?,
55         vendor_hashtree_root_digest_from_dt,
56     )?;
57 
58     // AAD is empty as defined in service_vm/comm/client_vm_csr.cddl.
59     let aad = &[];
60 
61     // Verifies the first signature with the leaf private key in the DICE chain.
62     cose_sign.verify_signature(DICE_CDI_LEAF_SIGNATURE_INDEX, aad, |signature, message| {
63         client_vm_dice_chain.microdroid_payload().subject_public_key.verify(signature, message)
64     })?;
65 
66     // Verifies the second signature with the public key in the CSR payload.
67     let ec_public_key = EcKey::from_cose_public_key_slice(&csr_payload.public_key)?;
68     cose_sign.verify_signature(ATTESTATION_KEY_SIGNATURE_INDEX, aad, |signature, message| {
69         ecdsa_verify_cose(&ec_public_key, signature, message)
70     })?;
71 
72     let subject_public_key_info = PKey::try_from(ec_public_key)?.subject_public_key_info()?;
73 
74     // Builds the TBSCertificate.
75     // The serial number can be up to 20 bytes according to RFC5280 s4.1.2.2.
76     // In this case, a serial number with a length of 16 bytes is used to ensure that each
77     // certificate signed by RKP VM has a unique serial number.
78     // Attention: Do not use 20 bytes here as when the MSB is 1, a leading 0 byte can be
79     // added during the encoding to make the serial number length exceed 20 bytes.
80     let mut serial_number = [0u8; 16];
81     rand_bytes(&mut serial_number)?;
82     let subject = Name::encode_from_string("CN=Android Protected Virtual Machine Key")?;
83     let rkp_cert = Certificate::from_der(&params.remotely_provisioned_cert)?;
84     let vm_components = client_vm_dice_chain.microdroid_payload_components()?;
85     let vm_components =
86         vm_components.iter().map(cert::VmComponent::new).collect::<der::Result<Vec<_>>>()?;
87 
88     info!("The client VM DICE chain validation succeeded. Beginning to generate the certificate.");
89     let attestation_ext = cert::AttestationExtension::new(
90         &csr_payload.challenge,
91         client_vm_dice_chain.all_entries_are_secure(),
92         vm_components,
93     )
94     .to_der()?;
95     let tbs_cert = cert::build_tbs_certificate(
96         &serial_number,
97         rkp_cert.tbs_certificate.subject,
98         Name::from_der(&subject)?,
99         rkp_cert.tbs_certificate.validity,
100         &subject_public_key_info,
101         &attestation_ext,
102     )?;
103 
104     // Signs the TBSCertificate and builds the Certificate.
105     // The two private key structs below will be zeroed out on drop.
106     let private_key =
107         decrypt_private_key(&params.remotely_provisioned_key_blob, dice_artifacts.cdi_seal())
108             .map_err(|e| {
109                 error!("Failed to decrypt the remotely provisioned key blob: {e}");
110                 RequestProcessingError::FailedToDecryptKeyBlob
111             })?;
112     let ec_private_key = EcKey::from_ec_private_key(private_key.as_slice())?;
113     let signature = ecdsa_sign_der(&ec_private_key, &tbs_cert.to_der()?)?;
114     let certificate = cert::build_certificate(tbs_cert, &signature)?;
115     Ok(certificate.to_der()?)
116 }
117 
ecdsa_verify_cose(key: &EcKey, signature: &[u8], message: &[u8]) -> bssl_avf::Result<()>118 fn ecdsa_verify_cose(key: &EcKey, signature: &[u8], message: &[u8]) -> bssl_avf::Result<()> {
119     // The message was signed with ECDSA with curve P-256 and SHA-256 at the signature generation.
120     let digest = sha256(message)?;
121     key.ecdsa_verify_cose(signature, &digest)
122 }
123 
ecdsa_sign_der(key: &EcKey, message: &[u8]) -> bssl_avf::Result<Vec<u8>>124 fn ecdsa_sign_der(key: &EcKey, message: &[u8]) -> bssl_avf::Result<Vec<u8>> {
125     let digest = sha256(message)?;
126     key.ecdsa_sign_der(&digest)
127 }
128 
validate_service_vm_dice_chain_length(service_vm_dice_chain: &[Value]) -> Result<()>129 fn validate_service_vm_dice_chain_length(service_vm_dice_chain: &[Value]) -> Result<()> {
130     if service_vm_dice_chain.len() < 3 {
131         // The service VM's DICE chain must contain the root key and at least two other entries
132         // that describe:
133         //   - pvmfw
134         //   - Service VM kernel
135         error!(
136             "The service VM DICE chain must contain at least three entries. Got '{}' entries",
137             service_vm_dice_chain.len()
138         );
139         return Err(RequestProcessingError::InternalError);
140     }
141     Ok(())
142 }
143 
144 /// Validates the client VM DICE chain against the reference service VM DICE chain and
145 /// the reference `vendor_hashtree_root_digest`.
146 ///
147 /// Returns the valid `ClientVmDiceChain` if the validation succeeds.
validate_client_vm_dice_chain( client_vm_dice_chain: &[u8], service_vm_dice_chain: &[u8], vendor_hashtree_root_digest: Option<&[u8]>, ) -> Result<ClientVmDiceChain>148 fn validate_client_vm_dice_chain(
149     client_vm_dice_chain: &[u8],
150     service_vm_dice_chain: &[u8],
151     vendor_hashtree_root_digest: Option<&[u8]>,
152 ) -> Result<ClientVmDiceChain> {
153     let service_vm_dice_chain = parse_value_array(service_vm_dice_chain, "service_vm_dice_chain")?;
154     validate_service_vm_dice_chain_length(&service_vm_dice_chain)?;
155 
156     let client_vm_dice_chain = parse_value_array(client_vm_dice_chain, "client_vm_dice_chain")?;
157     validate_client_vm_dice_chain_prefix_match(&client_vm_dice_chain, &service_vm_dice_chain)?;
158 
159     // Validates the signatures in the Client VM DICE chain and extracts the partially decoded
160     // DiceChainEntryPayloads.
161     let client_vm_dice_chain = ClientVmDiceChain::validate_signatures_and_parse_dice_chain(
162         client_vm_dice_chain,
163         service_vm_dice_chain.len(),
164     )?;
165     validate_vendor_partition_code_hash_if_exists(
166         &client_vm_dice_chain,
167         vendor_hashtree_root_digest,
168     )?;
169 
170     // The last entry in the service VM DICE chain describes the service VM, which should
171     // be signed with the same key as the kernel image.
172     let service_vm_entry = service_vm_dice_chain.last().unwrap();
173     validate_kernel_authority_hash(client_vm_dice_chain.microdroid_kernel(), service_vm_entry)?;
174     validate_kernel_code_hash(&client_vm_dice_chain)?;
175 
176     info!("The client VM DICE chain validation succeeded");
177     Ok(client_vm_dice_chain)
178 }
179 
validate_vendor_partition_code_hash_if_exists( client_vm_dice_chain: &ClientVmDiceChain, vendor_hashtree_root_digest: Option<&[u8]>, ) -> Result<()>180 fn validate_vendor_partition_code_hash_if_exists(
181     client_vm_dice_chain: &ClientVmDiceChain,
182     vendor_hashtree_root_digest: Option<&[u8]>,
183 ) -> Result<()> {
184     let Some(vendor_partition) = client_vm_dice_chain.vendor_partition() else {
185         debug!("The vendor partition is not present in the Client VM DICE chain");
186         return Ok(());
187     };
188     let Some(expected_root_digest) = vendor_hashtree_root_digest else {
189         error!(
190             "The vendor partition is present in the DICE chain, \
191              but the vendor_hashtree_root_digest is not provided in the DT"
192         );
193         return Err(RequestProcessingError::NoVendorHashTreeRootDigestInDT);
194     };
195     if Digester::sha512().digest(expected_root_digest)? == vendor_partition.code_hash {
196         Ok(())
197     } else {
198         error!(
199             "The vendor partition code hash in the Client VM DICE chain does \
200              not match the expected value from the DT"
201         );
202         Err(RequestProcessingError::InvalidVendorPartition)
203     }
204 }
205 
206 /// Validates that the authority hash of the Microdroid kernel in the Client VM DICE chain
207 /// matches the authority hash of the service VM entry in the service VM DICE chain, because
208 /// the Microdroid kernel is signed with the same key as the one used for the service VM.
validate_kernel_authority_hash( kernel: &DiceChainEntryPayload, service_vm_entry: &Value, ) -> Result<()>209 fn validate_kernel_authority_hash(
210     kernel: &DiceChainEntryPayload,
211     service_vm_entry: &Value,
212 ) -> Result<()> {
213     if expected_kernel_authority_hash(service_vm_entry)? == kernel.authority_hash {
214         Ok(())
215     } else {
216         error!("The authority hash of the Microdroid kernel does not match the expected value");
217         Err(RequestProcessingError::InvalidDiceChain)
218     }
219 }
220 
221 /// Validates that the kernel code hash in the Client VM DICE chain matches the code hashes
222 /// embedded during the build time.
validate_kernel_code_hash(dice_chain: &ClientVmDiceChain) -> Result<()>223 fn validate_kernel_code_hash(dice_chain: &ClientVmDiceChain) -> Result<()> {
224     let kernel = dice_chain.microdroid_kernel();
225     if matches_any_kernel_code_hash(&kernel.code_hash, /* is_debug= */ false)? {
226         return Ok(());
227     }
228     if matches_any_kernel_code_hash(&kernel.code_hash, /* is_debug= */ true)? {
229         if dice_chain.all_entries_are_secure() {
230             error!("The Microdroid kernel has debug initrd but the DICE chain is secure");
231             return Err(RequestProcessingError::InvalidDiceChain);
232         }
233         return Ok(());
234     }
235     error!("The kernel code hash in the Client VM DICE chain does not match any expected values");
236     Err(RequestProcessingError::InvalidDiceChain)
237 }
238 
matches_any_kernel_code_hash(actual_code_hash: &[u8], is_debug: bool) -> bssl_avf::Result<bool>239 fn matches_any_kernel_code_hash(actual_code_hash: &[u8], is_debug: bool) -> bssl_avf::Result<bool> {
240     for os_hash in OS_HASHES {
241         let mut code_hash = [0u8; KERNEL_HASH_SIZE * 2];
242         code_hash[0..KERNEL_HASH_SIZE].copy_from_slice(&os_hash.kernel);
243         if is_debug {
244             code_hash[KERNEL_HASH_SIZE..].copy_from_slice(&os_hash.initrd_debug);
245         } else {
246             code_hash[KERNEL_HASH_SIZE..].copy_from_slice(&os_hash.initrd_normal);
247         }
248         if Digester::sha512().digest(&code_hash)? == actual_code_hash {
249             return Ok(true);
250         }
251     }
252     Ok(false)
253 }
254 
expected_kernel_authority_hash(service_vm_entry: &Value) -> Result<[u8; HASH_SIZE]>255 fn expected_kernel_authority_hash(service_vm_entry: &Value) -> Result<[u8; HASH_SIZE]> {
256     let cose_sign1 = CoseSign1::from_cbor_value(service_vm_entry.clone())?;
257     let payload = cose_sign1.payload.ok_or_else(|| {
258         error!("No payload found in the service VM DICE chain entry");
259         RequestProcessingError::InternalError
260     })?;
261     let service_vm = DiceChainEntryPayload::from_slice(&payload)?;
262     Ok(service_vm.authority_hash)
263 }
264 
validate_client_vm_dice_chain_prefix_match( client_vm_dice_chain: &[Value], service_vm_dice_chain: &[Value], ) -> Result<()>265 fn validate_client_vm_dice_chain_prefix_match(
266     client_vm_dice_chain: &[Value],
267     service_vm_dice_chain: &[Value],
268 ) -> Result<()> {
269     // Ignores the last entry that describes service VM
270     let entries_up_to_pvmfw = &service_vm_dice_chain[0..(service_vm_dice_chain.len() - 1)];
271     if client_vm_dice_chain.get(0..entries_up_to_pvmfw.len()) == Some(entries_up_to_pvmfw) {
272         Ok(())
273     } else {
274         error!(
275             "The client VM's DICE chain does not match service VM's DICE chain up to \
276              the pvmfw entry"
277         );
278         Err(RequestProcessingError::InvalidDiceChain)
279     }
280 }
281