1 // Copyright 2025 Google LLC 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 //////////////////////////////////////////////////////////////////////////////// 16 17 //! The unit tests module 18 #![no_std] 19 extern crate alloc; 20 21 #[cfg(test)] 22 mod tests { 23 use super::*; 24 use alloc::string::ToString; 25 use alloc::vec; 26 use authgraph_boringssl::BoringEcDsa; 27 use authgraph_core::key::{ 28 CertChain, DiceChainEntry, DiceChainEntryPayload, DiceChainEntryPayloadPartiallyDecoded, 29 Identity, 30 }; 31 use authgraph_core_test::{ 32 create_dice_cert_chain_for_guest_os, create_dice_leaf_cert, SAMPLE_INSTANCE_HASH, 33 }; 34 use coset::CborSerializable; 35 36 #[test] test_legacy_open_dice_payload()37 fn test_legacy_open_dice_payload() { 38 // Some legacy devices have an open-DICE format config descriptor (b/261647022) rather than 39 // the format used in the Android RKP HAL. Ensure that they still parse. 40 let data = hex::decode(concat!( 41 "a8", // 8-map 42 "01", // Issuer: 43 "7828", // 40-tstr 44 "32336462613837333030633932323934663836333566323738316464346633366362313934383835", 45 "02", // Subject: 46 "7828", // 40-tstr 47 "33376165616366396230333465643064376166383665306634653431656163356335383134343966", 48 "3a00474450", // Code Hash(-4670545): 49 "5840", // 64-bstr 50 "3c9aa93a6766f16f5fbd3dfc7e5059b39cdc8aa0cf546cc878d588a69cfcd654", 51 "2fa509bd6cc14b7160a6bf34545ffdd840f0e91e35b274a7a952b5b0efcff1b0", 52 "3a00474453", // Configuration Descriptor (-4670548): 53 "5840", // 64-bstr 54 // The RKP HAL expects the following data to match schema: 55 // 56 // { ? -70002 : tstr, ? -70003 : int / tstr, ? -70004 : null, 57 // ? -70005 : uint, ? -70006 : null, } 58 // 59 // However, the open-DICE spec had: 60 // If the configuration input is a hash this field contains the original 61 // configuration data that was hashed. If it is not a hash, this field contains the 62 // exact 64-byte configuration input value used to compute CDI values." 63 "e2000000000001508609939b5a4f0f0800000000000000000101000000000000", 64 "0000000000000000000000000000000000000000000000000000000000000000", 65 "3a00474454", // Authority Hash (-4670549): 66 "5840", // 64-bstr 67 "4d00da66eabbb2b684641a57e96c8e64d76df1e31ea203bbbb9f439372c1a8ec", 68 "aa550000aa550000aa550000aa550000aa550000aa550000aa550000aa550000", 69 "3a00474456", // Mode (-4670551): 70 "4101", // 1-bstr value 0x01 71 "3a00474457", // Subject Public Key (-4670552): 72 "5871", // 113-bstr 73 "a601020338220481022002215830694a8fa269c3375b770ef61d06dec5a78595", 74 "2ee96db3602b57c50d8fa67f97e874fbd3f5b42e66ac8ead3f3eb3b130f42258", 75 "301b5574256be9f4770c3325422e53981b1a969387068a51aea68fe98f779be5", 76 "75ecb077a60106852af654377e56d446a6", 77 "3a00474458", // Key Usage (-4670553): 78 "4120" // 1-bstr value 0x20 79 )) 80 .unwrap(); 81 82 assert!(DiceChainEntryPayloadPartiallyDecoded::from_slice(&data).is_ok()); 83 assert!(DiceChainEntryPayload::from_slice(&data).is_ok()); 84 } 85 86 /// Test instance hash extraction API method with test data (from a device) that has 87 /// an instance hash 88 #[test] test_instance_hash_extraction()89 fn test_instance_hash_extraction() { 90 // Read the DICE chain bytes from the file 91 let dice_chain_bytes: &[u8] = include_bytes!("../testdata/bcc"); 92 // Create an explicit key DICE chain out of it 93 let explicit_key_dice_chain = CertChain::from_non_explicit_key_cert_chain(dice_chain_bytes) 94 .expect("error converting DICE chain to an explicit key DICE chain"); 95 // Extract the instance hash 96 let instance_hash = explicit_key_dice_chain 97 .extract_instance_identifier_in_guest_os_entry() 98 .expect("error in extracting the instance id") 99 .expect("no instance id found"); 100 let expected_instance_hash = vec![ 101 68, 32, 41, 225, 228, 67, 229, 107, 207, 212, 74, 74, 191, 25, 211, 133, 57, 166, 35, 102 146, 86, 89, 182, 52, 183, 255, 215, 204, 5, 183, 254, 79, 129, 240, 197, 252, 238, 69, 103 124, 44, 164, 214, 205, 87, 194, 226, 124, 249, 158, 219, 188, 127, 55, 143, 232, 142, 104 119, 174, 202, 160, 234, 179, 205, 30, 105 ]; 106 assert_eq!(instance_hash, expected_instance_hash); 107 } 108 109 /// Test instance hash extraction API method with test data (from a device) that does not 110 /// have an instance hash 111 #[test] test_instance_hash_extraction_negative()112 fn test_instance_hash_extraction_negative() { 113 let mut hex_data = 114 std::str::from_utf8(include_bytes!("../../tests/testdata/sample_identity.hex")) 115 .unwrap() 116 .to_string(); 117 hex_data.retain(|c| !c.is_whitespace()); 118 let data = hex::decode(hex_data).unwrap(); 119 let identity = Identity::from_slice(&data).expect("identity data did not decode"); 120 // Extract the instance hash 121 let instance_hash = identity 122 .cert_chain 123 .extract_instance_identifier_in_guest_os_entry() 124 .expect("error in extracting the instance id"); 125 assert_eq!(instance_hash, None); 126 } 127 128 /// Test instance hash extraction with a programmatically created DICE cert chain that has 129 /// instance hash 130 #[test] test_instance_hash_extraction_with_code_generated_dice_chain()131 fn test_instance_hash_extraction_with_code_generated_dice_chain() { 132 let (_, _, dice_chain_bytes) = 133 create_dice_cert_chain_for_guest_os(Some(SAMPLE_INSTANCE_HASH), 1); 134 let dice_cert_chain = 135 CertChain::from_slice(&dice_chain_bytes).expect("error decoding the DICE chain"); 136 // Extract the instance hash 137 let instance_hash = dice_cert_chain 138 .extract_instance_identifier_in_guest_os_entry() 139 .expect("error in extracting the instance id") 140 .expect("no instance id found"); 141 assert_eq!(instance_hash, SAMPLE_INSTANCE_HASH); 142 } 143 144 /// Test instance hash extraction with a programmatically created DICE cert chain that does not 145 /// has instance hash 146 #[test] test_instance_hash_extraction_with_code_generated_dice_chain_negative()147 fn test_instance_hash_extraction_with_code_generated_dice_chain_negative() { 148 let (_, _, dice_chain_bytes) = create_dice_cert_chain_for_guest_os(None, 0); 149 let dice_cert_chain = 150 CertChain::from_slice(&dice_chain_bytes).expect("error decoding the DICE chain"); 151 // Extract the instance id 152 let instance_hash = dice_cert_chain 153 .extract_instance_identifier_in_guest_os_entry() 154 .expect("error in extracting the instance id"); 155 assert_eq!(instance_hash, None); 156 } 157 158 /// Test extending a DICE chain with a giveen DICE certificate 159 #[test] test_dice_chain_extend()160 fn test_dice_chain_extend() { 161 let ecdsa = BoringEcDsa; 162 // Create a DICE chain for a pvm instance and a leaf cert for keymint TA 163 let (_sign_key, cdi_values, cert_chain_bytes) = 164 create_dice_cert_chain_for_guest_os(Some(SAMPLE_INSTANCE_HASH), 1); 165 let leaf_cert_bytes = create_dice_leaf_cert(cdi_values, "keymint", 1); 166 let dice_chain = 167 CertChain::from_slice(&cert_chain_bytes).expect("failed to decode dice chain"); 168 let leaf_cert = 169 DiceChainEntry::from_slice(&leaf_cert_bytes).expect("failed to decode the leaf cert"); 170 // Extend the pvm's DICE chain with the leaf cert 171 let extended_dice_chain = 172 dice_chain.extend_with(&leaf_cert, &ecdsa).expect("failed to extend the dice chain"); 173 // Verify that the original DICE chain has extended as expected 174 assert_eq!( 175 dice_chain.dice_cert_chain.as_ref().unwrap().len() + 1, 176 extended_dice_chain.dice_cert_chain.as_ref().unwrap().len() 177 ); 178 assert!(extended_dice_chain.is_current_leaf(&leaf_cert)); 179 assert!(!dice_chain.is_current_leaf(&leaf_cert)); 180 181 assert_eq!( 182 &leaf_cert, 183 extended_dice_chain.dice_cert_chain.as_ref().unwrap().last().unwrap() 184 ); 185 186 // Create a secondary DICE chain, try to extend it with the previous leaf cert and expect 187 // error 188 let (_, _, cert_chain_bytes_2) = 189 create_dice_cert_chain_for_guest_os(Some(SAMPLE_INSTANCE_HASH), 2); 190 let dice_chain_2 = 191 CertChain::from_slice(&cert_chain_bytes_2).expect("failed to decode dice chain 2"); 192 assert!(dice_chain_2.extend_with(&leaf_cert, &ecdsa).is_err()); 193 } 194 } 195