• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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