Skip to main content

islet_rmm/rsi/attestation/
mod.rs

1pub mod claims;
2
3use alloc::{boxed::Box, string::String, vec, vec::Vec};
4use ciborium::{ser, Value};
5use coset::{CoseSign1Builder, HeaderBuilder, TaggedCborSerializable};
6use ecdsa::signature::Signer;
7use tinyvec::ArrayVec;
8
9use crate::{
10    measurement::Measurement,
11    rmi::{HASH_ALGO_SHA256, HASH_ALGO_SHA512},
12};
13
14use self::claims::RealmClaims;
15use crate::rmm_el3::{plat_token, realm_attest_key};
16
17// Arbitrary number.
18pub const MAX_CCA_TOKEN_SIZE: usize = 4096;
19// This mostly depends on amount of measurement slots used.
20// In the maximum it could take around 6kB.
21// Currently platform token size is a little over 1kB.
22pub const MAX_PLATFORM_TOKEN_SIZE: usize = 2048;
23pub const MAX_CHALLENGE_SIZE: usize = 64;
24
25const CCA_TOKEN_COLLECTION: u64 = 399;
26const CCA_PLATFORM_TOKEN: u64 = 44234;
27const CCA_REALM_DELEGATED_TOKEN: u64 = 44241;
28
29// Hardcoded RAK private key for use in fuzzing, where HES is absent.
30#[cfg(fuzzing)]
31const RAK_PRIV_KEY: [u8; 48] = [
32    60, 140, 180, 183, 23, 21, 46, 76, 243, 33, 155, 38, 242, 82, 86, 0, 57, 97, 140, 67, 71, 234,
33    13, 22, 87, 140, 136, 172, 120, 226, 162, 115, 202, 242, 116, 10, 141, 245, 16, 119, 255, 31,
34    1, 3, 10, 113, 218, 134,
35];
36
37type PlatformToken = ArrayVec<[u8; MAX_PLATFORM_TOKEN_SIZE]>;
38// 48B - the length of EC-P384 private key
39type RAKPriv = ArrayVec<[u8; 48]>;
40
41#[derive(Debug, Default)]
42pub struct Attestation {
43    platform_token: PlatformToken,
44    rak_priv: RAKPriv,
45}
46
47// TODO: Can we keep this context anywhere?
48impl Attestation {
49    pub fn new(platform_token: &[u8], rak_priv: &[u8]) -> Self {
50        let mut at = Self::default();
51        at.set_platform_token(platform_token);
52        at.set_rak_priv(rak_priv);
53        at
54    }
55
56    fn set_platform_token(&mut self, token: &[u8]) {
57        self.platform_token = token.iter().cloned().collect();
58    }
59
60    fn set_rak_priv(&mut self, key_priv: &[u8]) {
61        self.rak_priv = key_priv.iter().cloned().collect();
62    }
63
64    // TODO: Consider returning errors.
65    // Though all errors in here are programmer errors
66    // or a result of incorrect data passed from HES.
67    pub fn create_attestation_token(
68        &self,
69        challenge: &[u8],
70        measurements: &[Measurement],
71        personalization_value: &[u8],
72        hash_algo: u8,
73    ) -> Vec<u8> {
74        let mut cca_token = Vec::new();
75
76        let realm_token =
77            self.create_realm_token(challenge, measurements, personalization_value, hash_algo);
78
79        let realm_token_entry = (
80            Value::Integer(CCA_REALM_DELEGATED_TOKEN.into()),
81            Value::Bytes(realm_token),
82        );
83
84        let platform_token_entry = (
85            Value::Integer(CCA_PLATFORM_TOKEN.into()),
86            Value::Bytes(self.platform_token.to_vec()),
87        );
88
89        let token_map: Vec<(Value, Value)> = vec![platform_token_entry, realm_token_entry];
90
91        ser::into_writer(
92            &Value::Tag(CCA_TOKEN_COLLECTION, Box::new(Value::Map(token_map))),
93            &mut cca_token,
94        )
95        .expect("Failed to serialize CCA token");
96
97        cca_token
98    }
99
100    fn create_realm_token(
101        &self,
102        challenge: &[u8],
103        measurements: &[Measurement],
104        personalization_value: &[u8],
105        hash_algo: u8,
106    ) -> Vec<u8> {
107        let hash_algo_id = match hash_algo {
108            HASH_ALGO_SHA256 => String::from("sha-256"),
109            HASH_ALGO_SHA512 => String::from("sha-512"),
110            _ => panic!("Unrecognized hash algorithm {}", hash_algo),
111        };
112
113        let secret_key =
114            p384::SecretKey::from_slice(&self.rak_priv).expect("Failed to import private RAK.");
115
116        let public_key = secret_key.public_key().to_sec1_bytes().to_vec();
117
118        let claims = RealmClaims::init(
119            challenge,
120            personalization_value,
121            measurements,
122            hash_algo_id,
123            &public_key,
124            // TODO: should this value be stored somewhere else?
125            String::from("sha-256"),
126        );
127
128        let claims_map: Vec<(Value, Value)> = vec![
129            claims.challenge.into(),
130            claims.profile.into(),
131            claims.personalization_value.into(),
132            claims.rim.into(),
133            claims.rems.into(),
134            claims.measurement_hash_algo.into(),
135            claims.rak_pub.into(),
136            claims.rak_pub_hash_algo.into(),
137        ];
138
139        let mut realm_token = Vec::new();
140        ser::into_writer(&Value::Map(claims_map), &mut realm_token)
141            .expect("Failed to serialize realm token");
142
143        let protected = HeaderBuilder::new()
144            .algorithm(coset::iana::Algorithm::ES384)
145            .build();
146
147        let sign1 = CoseSign1Builder::new()
148            .protected(protected)
149            .payload(realm_token)
150            .create_signature(b"", |payload| Self::sign(secret_key, payload))
151            .build();
152
153        sign1
154            .to_tagged_vec()
155            .expect("Failed to create tagged signed token")
156    }
157
158    fn sign(secret_key: p384::SecretKey, data: &[u8]) -> Vec<u8> {
159        let signing_key = p384::ecdsa::SigningKey::from_bytes(&secret_key.to_bytes())
160            .expect("Failed to generate signing key");
161
162        let signature: p384::ecdsa::Signature = signing_key
163            .try_sign(data)
164            .expect("Failed to create P384 signature");
165        signature.to_vec()
166    }
167}
168
169pub fn get_token(
170    challenge: &[u8],
171    measurements: &[Measurement],
172    personalization_value: &[u8],
173    hash_algo: u8,
174) -> Vec<u8> {
175    // TODO: consider storing attestation object somewhere,
176    // as RAK and token do not change during rmm lifetime.
177    #[cfg(fuzzing)]
178    let realm_attest_key = &RAK_PRIV_KEY;
179    #[cfg(not(fuzzing))]
180    let realm_attest_key = &realm_attest_key();
181
182    Attestation::new(&plat_token(), realm_attest_key).create_attestation_token(
183        challenge,
184        measurements,
185        personalization_value,
186        hash_algo,
187    )
188}