• 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 //! Entry point to the AuthMgr-Common crate used by both AuthMgr-FE and AuthMgr-BE.
18 
19 #![no_std]
20 extern crate alloc;
21 
22 use alloc::string::String;
23 use authgraph_core::key::{CertChain, DiceChainEntry, Policy};
24 use coset::{CborSerializable, CoseError};
25 use dice_policy::{DicePolicy, DICE_POLICY_VERSION};
26 
27 pub mod signed_connection_request;
28 
29 /// The command byte to indicate the intent to connect to the `IAuthMgrAuthorization` service via
30 /// RPC binder
31 pub const CMD_RPC: u8 = 0u8;
32 /// The command byte to indicate the intent to establish a raw connection
33 pub const CMD_RAW: u8 = 1u8;
34 
35 /// Token length is 32 bytes
36 pub const TOKEN_LENGTH: usize = 32;
37 /// Type alias for a cryptographic random token (sent from AuthMgrFE to BE) of 32 bytes
38 pub type Token = [u8; TOKEN_LENGTH];
39 
40 /// AuthMgr common error type
41 #[derive(Debug, PartialEq)]
42 pub struct Error(pub ErrorCode, pub String);
43 
44 /// AuthMgr common error codes
45 #[derive(Debug, PartialEq)]
46 pub enum ErrorCode {
47     /// CBOR encoding failed
48     CborEncodingFailed,
49     /// CBOR decoding failed
50     CborDecodingFailed,
51     /// Signing failed
52     SigningFailed,
53     /// Signature verification failed
54     SignatureVerificationFailed,
55     /// Failed to create a DICE policy
56     DicePolicyCreationFailed,
57     /// Failed to match DICE policy
58     DicePolicyMatchingFailed,
59     /// Other error
60     UnknownError,
61 }
62 
63 /// AuthMgr common result type
64 pub type Result<T, E = Error> = core::result::Result<T, E>;
65 
66 impl core::convert::From<CoseError> for Error {
from(e: CoseError) -> Self67     fn from(e: CoseError) -> Self {
68         match e {
69             CoseError::DecodeFailed(_) => amc_err!(CborDecodingFailed, "{}", e),
70             CoseError::EncodeFailed => amc_err!(CborEncodingFailed, "{}", e),
71             CoseError::UnexpectedItem(_, _) => amc_err!(CborDecodingFailed, "{}", e),
72             _ => amc_err!(UnknownError, "{}", e),
73         }
74     }
75 }
76 
77 /// Macro to build an [`Error`] instance.
78 /// E.g. use: `amc_err!(SigningFailed, "some {} format", arg)`.
79 #[macro_export]
80 macro_rules! amc_err {
81     { $error_code:ident, $($arg:tt)+ } => {
82         Error(ErrorCode::$error_code,
83               alloc::format!("{}:{}: {}", file!(), line!(), format_args!($($arg)+))) };
84 }
85 
86 /// Wrapper function around the dice_policy library API, to match a DICE chain with a DICE policy,
87 /// to be used in AuthMgr protocol.
match_dice_chain_with_policy(cert_chain: &CertChain, policy: &Policy) -> Result<bool>88 pub fn match_dice_chain_with_policy(cert_chain: &CertChain, policy: &Policy) -> Result<bool> {
89     let cert_chain = cert_chain.clone().to_vec()?;
90     let result = dice_policy::chain_matches_policy(&cert_chain, &policy.0);
91     Ok(result.is_ok())
92 }
93 
94 /// Wrapper function around the dice_policy library API, to match a DICE certificate with a DICE
95 /// policy, to be used in AuthMgr protocol. A DICE policy created for a single DICE certificate is
96 /// expected to have up to only one nodeConstraintList, as per the DICE policy specification.
match_dice_cert_with_policy(dice_cert: &DiceChainEntry, policy: &Policy) -> Result<bool>97 pub fn match_dice_cert_with_policy(dice_cert: &DiceChainEntry, policy: &Policy) -> Result<bool> {
98     let dice_policy = DicePolicy::from_slice(&policy.0)?;
99     if dice_policy.node_constraints_list.is_empty() {
100         return Ok(true);
101     }
102     let dice_node = dice_cert
103         .payload
104         .full_map
105         .as_ref()
106         .ok_or(amc_err!(UnknownError, "no certificate payload found"))?;
107     let result =
108         dice_policy::check_constraints_on_node(&dice_policy.node_constraints_list[0], dice_node)
109             .map_err(|_e| {
110                 amc_err!(DicePolicyMatchingFailed, "failed to match constraints on DICE cert")
111             });
112     Ok(result.is_ok())
113 }
114 
115 /// Get a copy of this DICE policy extended with the given DICE policy. This is an AuthMgr
116 /// specific requirement where we create a DICE policy for a child DICE certificate and later
117 /// need to combine it with the policy for the parent DICE cert chain
extend_dice_policy_with(parent_policy: &Policy, child_policy: &Policy) -> Result<Policy>118 pub fn extend_dice_policy_with(parent_policy: &Policy, child_policy: &Policy) -> Result<Policy> {
119     let parent_policy = DicePolicy::from_slice(&parent_policy.0)?;
120     let child_policy = DicePolicy::from_slice(&child_policy.0)?;
121     // Match the version of the two policies
122     if parent_policy.version != child_policy.version {
123         return Err(amc_err!(DicePolicyCreationFailed, "policy versions do not match"));
124     }
125     let mut parent_node_constraints = parent_policy.node_constraints_list.to_vec();
126     parent_node_constraints.extend_from_slice(&child_policy.node_constraints_list);
127     let extended_policy = DicePolicy {
128         version: DICE_POLICY_VERSION,
129         node_constraints_list: parent_node_constraints.into_boxed_slice(),
130     };
131     Ok(Policy(extended_policy.to_vec()?))
132 }
133 
134 #[cfg(test)]
135 mod tests {
136     use super::*;
137     use alloc::string::ToString;
138     use alloc::{vec, vec::Vec};
139     use authgraph_boringssl::ec::BoringEcDsa;
140     use authgraph_core::key::{
141         AUTHORITY_HASH, CONFIG_DESC, GUEST_OS_COMPONENT_NAME, INSTANCE_HASH, MODE, SECURITY_VERSION,
142     };
143     use authgraph_core_test::{
144         create_dice_cert_chain_for_guest_os, create_dice_leaf_cert, SAMPLE_INSTANCE_HASH,
145     };
146     use authmgr_common_util::{
147         get_constraint_spec_for_static_trusty_ta, get_constraints_spec_for_trusty_vm,
148         policy_for_dice_node,
149     };
150     use dice_policy_builder::{ConstraintSpec, ConstraintType, MissingAction, TargetEntry};
151 
152     #[test]
test_dice_policy_extend()153     fn test_dice_policy_extend() {
154         // Create DICE chain 1 for a pvm instance
155         let (_sign_key_1, cdi_values_1, cert_chain_bytes_1) =
156             create_dice_cert_chain_for_guest_os(Some(SAMPLE_INSTANCE_HASH), 1);
157         let cert_chain_1 =
158             CertChain::from_slice(&cert_chain_bytes_1).expect("failed to decode cert_chain 1");
159         // Create constraints spec 1
160         let constraint_spec_1 = get_constraints_spec_for_trusty_vm();
161         // Create policy 1 given constraints spec 1 and DICE chain 1
162         let policy_1 = Policy(
163             dice_policy_builder::policy_for_dice_chain(
164                 &cert_chain_bytes_1,
165                 constraint_spec_1.clone(),
166             )
167             .expect("failed to building policy 1")
168             .to_vec()
169             .expect("failed to encode policy 1"),
170         );
171         // Match DICE chain 1 to policy 1 and expect a match
172         let result1 = match_dice_chain_with_policy(&cert_chain_1, &policy_1);
173         assert!(result1.unwrap());
174         // Create (child) DICE cert entry for DICE chain 1
175         let leaf_cert_bytes_1 = create_dice_leaf_cert(cdi_values_1, "keymint", 1);
176         let leaf_cert_1 =
177             DiceChainEntry::from_slice(&leaf_cert_bytes_1).expect("failed to decode leaf cert 1");
178         // Create a constraints spec for client 1
179         let client_constraint_spec_km = get_constraint_spec_for_static_trusty_ta();
180         // Create the client policy given the constraints spec and DICE cert entry
181         let client_policy_1 = Policy(
182             policy_for_dice_node(&leaf_cert_1, client_constraint_spec_km)
183                 .expect("failed to create dice policy for keymint")
184                 .to_vec()
185                 .expect("failed to encode policy for client TA 1"),
186         );
187         // Match the client's DICE cert entry and policy and expect a match
188         let result_1a = match_dice_cert_with_policy(&leaf_cert_1, &client_policy_1);
189         assert!(result_1a.unwrap());
190 
191         // Extend DICE chain 1 with leaf DICE cert 1
192         let ecdsa = BoringEcDsa;
193         let extended_dice_chain_1 =
194             cert_chain_1.extend_with(&leaf_cert_1, &ecdsa).expect("failed to extend DICE chain 1");
195         // Extend DICE policy 1 with client 1's DICE policy
196         let extended_policy_1 = extend_dice_policy_with(&policy_1, &client_policy_1)
197             .expect("failed to extend DICE policy 1");
198 
199         // Match the extended DICE chain 1 with the extended DICE policy 1 and expect a match
200         let result1 = match_dice_chain_with_policy(&extended_dice_chain_1, &extended_policy_1);
201         assert!(result1.unwrap());
202     }
203 
204     // This test simulates the DICE chain to policy matching that takes place in the full AuthMgr
205     // protocol, using:
206     //     1) two different DICE chains representing two pvm instances,
207     //     2) DICE policies for the two pvm instances,
208     //     3) two individual DICE certificates (which are extension of the aforementioned two DICE
209     //        chains) representing the client TAs in the aforementioned two pvm instances,
210     //     4) DICE policies created for the client TAs.
211     // We do the following checks:
212     //     1) Matching of the DICE cert chain and the policy of a given pvm instance succeeds.
213     //     2) Matching of the DICE cert chain of one pvm instance with the policy of the other pvm
214     //        instance fails.
215     //     3) DICE policies of the two instances do not pass the equality match
216     //     4) Matching of the DICE certificate and the policy of a given client TA in a pvm instance
217     //        succeeds
218     //     5) Matching of the DICE certificate of one client TA and the policy of the other client
219     //        TA fails.
220     //     6) Matching of the extended DICE chain created for a client TA in a pvm and the extended
221     //        DICE policy created for the same client TA succeeds.
222     //     7) The extended DICE policies created for the two client TAs do not pass the equality
223     //        check.
224     //     8) Matching of the extended DICE chain created for one client and the extended DICE
225     //        policy created for the other client fails.
226     //     9) Rollback protection for pvm instance 1 with a new DICE chain created with a higher
227     //        security version and an updated policy.
228     #[test]
simulate_authmgr_dice_verification()229     fn simulate_authmgr_dice_verification() {
230         // Create DICE chain 1 for a pvm instance
231         let (_sign_key_1, cdi_values_1, cert_chain_bytes_1) =
232             create_dice_cert_chain_for_guest_os(Some(SAMPLE_INSTANCE_HASH), 1);
233         let cert_chain_1 =
234             CertChain::from_slice(&cert_chain_bytes_1).expect("failed to decode cert_chain 1");
235         // Create constraints spec 1
236         let constraint_spec_1 = get_constraints_spec_for_trusty_vm();
237         // Create policy 1 given constraints spec 1 and DICE chain 1
238         let policy_1 = Policy(
239             dice_policy_builder::policy_for_dice_chain(
240                 &cert_chain_bytes_1,
241                 constraint_spec_1.clone(),
242             )
243             .expect("failed to building policy 1")
244             .to_vec()
245             .expect("failed to encode policy 1"),
246         );
247         // Match DICE chain 1 to policy 1 and expect a match
248         let result1 = match_dice_chain_with_policy(&cert_chain_1, &policy_1);
249         assert_eq!(result1, Ok(true));
250 
251         // Create DICE chain 2 for a pvm instance (with different instance hash and security
252         // version)
253         let instance_hash: [u8; 64] = [
254             0x1a, 0x3f, 0xc9, 0x6b, 0xe3, 0x95, 0x59, 0x40, 0x21, 0x09, 0x9d, 0xf3, 0xcd, 0xc7,
255             0xa4, 0x2a, 0x7d, 0x7e, 0xf5, 0x8e, 0xe6, 0x4d, 0x84, 0x25, 0x1a, 0x51, 0x27, 0x9d,
256             0x55, 0x8a, 0xe9, 0x90, 0xf5, 0x8e, 0xd6, 0x4d, 0x84, 0x25, 0x1a, 0x51, 0x86, 0x9d,
257             0x5b, 0x3f, 0xc9, 0x6b, 0xe3, 0x95, 0x59, 0x40, 0x21, 0x09, 0x9d, 0xf3, 0xcd, 0xc7,
258             0xa4, 0x2a, 0x7d, 0x7e, 0xf5, 0x8e, 0xf5, 0x3f,
259         ];
260         let (_sign_key_2, cdi_values_2, cert_chain_bytes_2) =
261             create_dice_cert_chain_for_guest_os(Some(instance_hash), 2);
262         let cert_chain_2 =
263             CertChain::from_slice(&cert_chain_bytes_2).expect("failed to decode cert_chain 2");
264         // Create (different) constraints spec 2
265         let constraint_spec_2 = get_relaxed_constraints_spec_for_trusty_vm();
266         // Create policy 2 given constraints spec 2 and DICE chain 2
267         let policy_2 = Policy(
268             dice_policy_builder::policy_for_dice_chain(&cert_chain_bytes_2, constraint_spec_2)
269                 .expect("failed to building policy 2")
270                 .to_vec()
271                 .expect("failed to encode policy 2"),
272         );
273         // Match DICE chain 2 with policy 2 and expect a match
274         let result2 = match_dice_chain_with_policy(&cert_chain_2, &policy_2);
275         assert_eq!(result2, Ok(true));
276 
277         // Compare policy 1 and policy 2 and expect a non-match
278         assert_ne!(policy_1, policy_2);
279         // Match DICE chain 1 with policy 2 and expect a non-match
280         let result_12 = match_dice_chain_with_policy(&cert_chain_1, &policy_2);
281         assert_eq!(result_12, Ok(false));
282         // Match DICE chain 2 with policy 1 and expect a non-match
283         let result_21 = match_dice_chain_with_policy(&cert_chain_2, &policy_1);
284         assert_eq!(result_21, Ok(false));
285 
286         // Create (child) DICE cert entry for DICE chain 1
287         let leaf_cert_bytes_1 = create_dice_leaf_cert(cdi_values_1, "keymint", 1);
288         let leaf_cert_1 =
289             DiceChainEntry::from_slice(&leaf_cert_bytes_1).expect("failed to decode leaf cert 1");
290         // Create a constraints spec for client 1
291         let client_constraint_spec_km = get_constraint_spec_for_static_trusty_ta();
292         // Create the client policy given the constraints spec and DICE cert entry
293         let client_policy_1 = Policy(
294             policy_for_dice_node(&leaf_cert_1, client_constraint_spec_km)
295                 .expect("failed to create dice policy for keymint")
296                 .to_vec()
297                 .expect("failed to encode policy for client TA 1"),
298         );
299         // Match the client's DICE cert entry and policy and expect a match
300         let result_1a = match_dice_cert_with_policy(&leaf_cert_1, &client_policy_1);
301         assert_eq!(result_1a, Ok(true));
302 
303         // Create (child) DICE cert entry for DICE chain 2
304         let leaf_cert_bytes_2 = create_dice_leaf_cert(cdi_values_2, "widevine", 1);
305         let leaf_cert_2 =
306             DiceChainEntry::from_slice(&leaf_cert_bytes_2).expect("failed to decode leaf cert 2");
307         // Create constraints spec for client 2
308         let client_constraint_spec_wv = get_constraint_spec_for_static_trusty_ta();
309         // Create client policy giiven the constraints spec and DICE cert entry
310         let client_policy_2 = Policy(
311             policy_for_dice_node(&leaf_cert_2, client_constraint_spec_wv)
312                 .expect("failed to create dice policy for widevine")
313                 .to_vec()
314                 .expect("failed to encode policy for client TA 2"),
315         );
316         // Match the client's DICE cert entry and policy and expect a match
317         let result_2a = match_dice_cert_with_policy(&leaf_cert_2, &client_policy_2);
318         assert_eq!(result_2a, Ok(true));
319 
320         // Compare client 1's policy and client 2's policy and expect a non-match
321         assert_ne!(client_policy_1, client_policy_2);
322         // Match client 1's DICE cert entry with client 2's policy and expect a non-match
323         let result_12 = match_dice_cert_with_policy(&leaf_cert_1, &client_policy_2);
324         assert_eq!(result_12, Ok(false));
325         // Match client 2's DICE cert entry with client 1's policy and expect a non-match
326         let result_21 = match_dice_cert_with_policy(&leaf_cert_2, &client_policy_1);
327         assert_eq!(result_21, Ok(false));
328 
329         // Extend DICE chain 1 with leaf DICE cert 1
330         let ecdsa = BoringEcDsa;
331         let extended_dice_chain_1 =
332             cert_chain_1.extend_with(&leaf_cert_1, &ecdsa).expect("failed to extend DICE chain 1");
333         // Extend DICE policy 1 with client 1's DICE policy
334         let extended_dice_policy_1 = extend_dice_policy_with(&policy_1, &client_policy_1)
335             .expect("failed to extend DICE policy 1");
336         // Match the extended DICE chain 1 with the extended DICE policy 1 and expect a match
337         let result1 = match_dice_chain_with_policy(&extended_dice_chain_1, &extended_dice_policy_1);
338         assert_eq!(result1, Ok(true));
339 
340         // Extend DICE chain 2 with DICE cert entry 2
341         let extended_dice_chain_2 =
342             cert_chain_2.extend_with(&leaf_cert_2, &ecdsa).expect("failed to extend DICE chain 2");
343         // Extend DICE policy 2 with client 2's DICE policy
344         let extended_dice_policy_2 = extend_dice_policy_with(&policy_2, &client_policy_2)
345             .expect("failed to extend DICE policy 2");
346         // Match the extended DICE chain 2 with the extended DICE policy 2 and expect a match
347         let result2 = match_dice_chain_with_policy(&extended_dice_chain_2, &extended_dice_policy_2);
348         assert_eq!(result2, Ok(true));
349 
350         // Compare the extended policy 1 and the extended policy 2 and expect a non-match
351         assert_ne!(extended_dice_policy_1, extended_dice_policy_2);
352         // Match the extended DICE chain 1 with the extended policy 2 and expect a non-match
353         let result12 =
354             match_dice_chain_with_policy(&extended_dice_chain_1, &extended_dice_policy_2);
355         assert_eq!(result12, Ok(false));
356         // Match the extended DICE chain 2 with the extended policy 1 and expect a non-match
357         let result21 =
358             match_dice_chain_with_policy(&extended_dice_chain_2, &extended_dice_policy_1);
359         assert_eq!(result21, Ok(false));
360 
361         // Test rollback protection via DICE policy:
362         // Create a DICE chain 3 with the same instance hash as DICE chain 1, but with a higher
363         // security version.
364         let (_sign_key_3, _cdi_values_3, cert_chain_bytes_3) =
365             create_dice_cert_chain_for_guest_os(Some(SAMPLE_INSTANCE_HASH), 3);
366         let cert_chain_3 =
367             CertChain::from_slice(&cert_chain_bytes_3).expect("failed to decode cert_chain 3");
368         // Create policy 3 given constraint spec 1 and DICE chain 3
369         let policy_3 = Policy(
370             dice_policy_builder::policy_for_dice_chain(&cert_chain_bytes_3, constraint_spec_1)
371                 .expect("failed to building policy 3")
372                 .to_vec()
373                 .expect("failed to encode policy 3"),
374         );
375         // Match DICE chain 3 to policy 3 and expect a match
376         let result3 = match_dice_chain_with_policy(&cert_chain_3, &policy_3);
377         assert_eq!(result3, Ok(true));
378         // Match DICE chain 3 to policy 1 and expect a match
379         let result31 = match_dice_chain_with_policy(&cert_chain_3, &policy_1);
380         assert_eq!(result31, Ok(true));
381         // Match DICE chain 1 to policy 3 and expect a non-match
382         let result13 = match_dice_chain_with_policy(&cert_chain_1, &policy_3);
383         assert_eq!(result13, Ok(false));
384     }
385 
386     // Returns a constraint spec as above, but without the security version check for
387     // the node except the "vm_entry" node
get_relaxed_constraints_spec_for_trusty_vm() -> Vec<ConstraintSpec>388     fn get_relaxed_constraints_spec_for_trusty_vm() -> Vec<ConstraintSpec> {
389         vec![
390             ConstraintSpec::new(
391                 ConstraintType::ExactMatch,
392                 vec![AUTHORITY_HASH],
393                 MissingAction::Fail,
394                 TargetEntry::All,
395             ),
396             ConstraintSpec::new(
397                 ConstraintType::ExactMatch,
398                 vec![MODE],
399                 MissingAction::Fail,
400                 TargetEntry::All,
401             ),
402             ConstraintSpec::new(
403                 ConstraintType::ExactMatch,
404                 vec![CONFIG_DESC, INSTANCE_HASH],
405                 MissingAction::Fail,
406                 TargetEntry::ByName(GUEST_OS_COMPONENT_NAME.to_string()),
407             ),
408             ConstraintSpec::new(
409                 ConstraintType::GreaterOrEqual,
410                 vec![CONFIG_DESC, SECURITY_VERSION],
411                 MissingAction::Fail,
412                 TargetEntry::ByName(GUEST_OS_COMPONENT_NAME.to_string()),
413             ),
414         ]
415     }
416 }
417