Skip to main content

islet_hes/attestation/
mod.rs

1use core::mem;
2
3use alloc::borrow::ToOwned;
4use alloc::string::{String, ToString};
5use alloc::vec::Vec;
6use ciborium::{ser, Value};
7use coset::{iana, AsCborValue, CoseKeyBuilder, CoseSign1, CoseSign1Builder, HeaderBuilder};
8use ecdsa::{elliptic_curve::sec1::ToEncodedPoint, signature::Signer};
9use key_derivation::{derive_p256_key, derive_p384_key, generate_seed};
10use p384::ecdsa::Signature as P384Signature;
11use sha2::{Digest, Sha256, Sha384, Sha512};
12use tinyvec::ArrayVec;
13
14use crate::utils::token_tag;
15use crate::{utils, HWHash, HWSymmetricKey, Measurement, MeasurementType};
16
17/// Supported ecc family types.
18#[derive(Copy, Clone, Debug)]
19pub enum ECCFamily {
20    /// The only one compliant with CCA Security Model 1.0
21    SecpR1,
22}
23
24/// Supported ecc key bit size.
25#[derive(Copy, Clone, Debug)]
26pub enum KeyBits {
27    Bits256,
28    Bits384,
29    // Not Supported
30    Bits521,
31}
32
33/// Supported public dak hash algorithms.
34#[derive(Copy, Clone, Debug)]
35pub enum HashAlgo {
36    Sha256,
37    Sha384,
38    Sha512,
39}
40
41impl HashAlgo {
42    /// Converts the given `HashAlgo` into [`usize`] length.
43    pub fn len(self) -> usize {
44        match self {
45            HashAlgo::Sha256 => 32,
46            HashAlgo::Sha384 => 48,
47            HashAlgo::Sha512 => 64,
48        }
49    }
50}
51
52impl Into<String> for HashAlgo {
53    /// Converts the given `HashAlgo` into [`String`] description.
54    fn into(self) -> String {
55        match self {
56            HashAlgo::Sha256 => "sha-256".to_string(),
57            HashAlgo::Sha384 => "sha-384".to_string(),
58            HashAlgo::Sha512 => "sha-512".to_string(),
59        }
60    }
61}
62
63impl Into<String> for MeasurementType {
64    /// Converts the given `MeasurementType` into [`String`].
65    fn into(self) -> String {
66        match self {
67            MeasurementType::Sha256 => "sha-256".to_string(),
68            MeasurementType::Sha384 => "sha-384".to_string(),
69            MeasurementType::Sha512 => "sha-512".to_string(),
70        }
71    }
72}
73
74/// Error kinds returned by AttestationMgr
75#[derive(Debug, PartialEq)]
76pub enum AttestationError {
77    /// Some parameter or combination of parameters are recognised as invalid:
78    /// - challenge size is not allowed
79    /// - challenge object is unavailable
80    /// - token buffer is unavailable
81    InvalidArgument,
82    /// An error occurred that does not correspond to any defined
83    /// failure cause.
84    GenericError,
85    /// The requested operation or a parameter is not supported
86    /// by this implementation.
87    NotSupported,
88}
89
90/// Represents data required for key derivation (CPAK/DAK).
91#[derive(Clone, Debug)]
92pub struct KeyMaterialData {
93    /// Bootloader image singed hash.
94    pub hash: HWHash,
95    /// Group unique 256bit symmetric key.
96    pub guk: HWSymmetricKey,
97}
98
99/// Hardware provisioned claims.
100#[derive(Clone, Debug)]
101pub struct HWClaims {
102    pub implementation_id: [u8; 32],
103    pub security_lifecycle: u32,
104    pub profile_definition: Option<String>,
105    pub verification_service_url: Option<String>,
106    pub platform_config: ArrayVec<[u8; 32]>,
107}
108
109/// Represents DAK specific data.
110pub struct DAKInfo {
111    /// Hash algorithm as will be passed during token creation.
112    hash_algo: HashAlgo,
113    /// Key bit size
114    key_bits: KeyBits,
115    /// Generated DAK private key.
116    key: Vec<u8>,
117}
118
119type InstanceId = ArrayVec<[u8; 33]>;
120
121/// Represents CPAK specific data.
122struct CPAK {
123    // TODO: SigningKey is defacto SecretKey. Maybe change key derivation
124    // to return SigningKey instead of the SecretKey
125    /// CPAK private key.
126    key: p384::SecretKey,
127    /// CPAK sha256, used for creating instance id.
128    //RSS uses sha256 for instance id
129    instance_id: InstanceId,
130}
131
132/// Attestation manager implementing `get_delegated_key` and `get_platform_token` functinality.
133pub struct AttestationMgr {
134    /// CPAK data
135    cpak: CPAK,
136    /// Key derivation material for DAK and CPAK generation
137    derivation_material: KeyMaterialData,
138    /// Claims from HW
139    claims: HWClaims,
140    /// When DAK is generated keeps data required for DAK verification and signing
141    dak: Option<DAKInfo>,
142}
143
144/// Convert SEC1 encoded EC2 public `key` to COSE/CBOR
145pub fn ec_public_key_sec1_to_cose(key: &[u8]) -> Vec<u8> {
146    let p256_sec1_len = 1 + 2 * 32;
147    let p384_sec1_len = 1 + 2 * 48;
148    let p521_sec1_len = 1 + 2 * 66;
149
150    let key_cbor_value = match key.len() {
151        n if n == p256_sec1_len => {
152            let pk = p256::PublicKey::from_sec1_bytes(key).expect("Failed to load p256 sec1 key");
153            let ep = pk.to_encoded_point(false);
154            let x = ep.x().unwrap().to_owned().to_vec();
155            let y = ep.y().unwrap().to_owned().to_vec();
156            let key = CoseKeyBuilder::new_ec2_pub_key(iana::EllipticCurve::P_256, x, y).build();
157            key.to_cbor_value().expect("Failed to encode p256 as CBOR")
158        }
159        n if n == p384_sec1_len => {
160            let pk = p384::PublicKey::from_sec1_bytes(key).expect("Failed to load p384 sec1 key");
161            let ep = pk.to_encoded_point(false);
162            let x = ep.x().unwrap().to_owned().to_vec();
163            let y = ep.y().unwrap().to_owned().to_vec();
164            let key = CoseKeyBuilder::new_ec2_pub_key(iana::EllipticCurve::P_384, x, y).build();
165            key.to_cbor_value().expect("Failed to encode p384 as CBOR")
166        }
167        n if n == p521_sec1_len => {
168            let pk = p521::PublicKey::from_sec1_bytes(key).expect("Failed to load p521 sec1 key");
169            let ep = pk.to_encoded_point(false);
170            let x = ep.x().unwrap().to_owned().to_vec();
171            let y = ep.y().unwrap().to_owned().to_vec();
172            let key = CoseKeyBuilder::new_ec2_pub_key(iana::EllipticCurve::P_521, x, y).build();
173            key.to_cbor_value().expect("Failed to encode p521 as CBOR")
174        }
175        _ => panic!("Wrong sec1 key length"),
176    };
177
178    let mut key_cbor_bytes = Vec::new();
179    ser::into_writer(&key_cbor_value, &mut key_cbor_bytes).expect("Failed to serialize CBOR value");
180    key_cbor_bytes
181}
182
183/// Calculate hash for given `key` with chosen [`HashAlgo`]
184pub fn calculate_public_key_hash(public_key: &[u8], hash_algo: HashAlgo) -> Vec<u8> {
185    match hash_algo {
186        HashAlgo::Sha256 => Sha256::digest(public_key).to_vec(),
187        HashAlgo::Sha384 => Sha384::digest(public_key).to_vec(),
188        HashAlgo::Sha512 => Sha512::digest(public_key).to_vec(),
189    }
190}
191
192impl AttestationMgr {
193    const CPAK_SEED_LABEL: &'static [u8] = b"BL1_CPAK_SEED_DERIVATION";
194    const DAK_SEED_LABEL: &'static [u8] = b"BL1_DAK_SEED_DERIVATION";
195
196    pub fn calculate_cpak_hash(&self) -> Vec<u8> {
197        calculate_public_key_hash(
198            &self.cpak.key.public_key().to_sec1_bytes(),
199            HashAlgo::Sha256,
200        )
201    }
202
203    // This function calculates a hash of a public key but not in sec1 format as
204    // was done previously but encoded as COSE/CBOR. I find it strange that HES
205    // module is supposed to return a raw key through PSA, but when asked for
206    // token be forced to encode that key as COSE. This is how TF-RMM does it
207    // now though. See here:
208    // https://github.com/TF-RMM/tf-rmm/commit/3aa34974bd21449e0be82f99e3e0c1888e67dfd6
209    pub fn calculate_dak_hash(&self, hash_algo: HashAlgo) -> Vec<u8> {
210        let dak_info = self.dak.as_ref().unwrap();
211        let key_public_sec1 = match dak_info.key_bits {
212            KeyBits::Bits256 => p256::SecretKey::from_slice(&dak_info.key)
213                .unwrap()
214                .public_key()
215                .to_sec1_bytes(),
216            KeyBits::Bits384 => p384::SecretKey::from_slice(&dak_info.key)
217                .unwrap()
218                .public_key()
219                .to_sec1_bytes(),
220            KeyBits::Bits521 => p521::SecretKey::from_slice(&dak_info.key)
221                .unwrap()
222                .public_key()
223                .to_sec1_bytes(),
224        };
225        let key_public_cose = ec_public_key_sec1_to_cose(&key_public_sec1);
226        calculate_public_key_hash(&key_public_cose, hash_algo)
227    }
228
229    fn generate_instance_id(cpak: &p384::SecretKey) -> InstanceId {
230        let hash = calculate_public_key_hash(&cpak.public_key().to_sec1_bytes(), HashAlgo::Sha256);
231
232        let mut instance_id: InstanceId = ArrayVec::new();
233        instance_id.push(0x01);
234        for item in hash {
235            instance_id.push(item)
236        }
237        instance_id
238    }
239
240    /// Initialize AttestationMgr using [`KeyMaterialData`] and [`HWClaims`].
241    /// Generates a CPAK using `key_material_data`.
242    pub fn init(key_derivation_material: KeyMaterialData, claims: HWClaims) -> Self {
243        let lcs: u32 = 3;
244        let reprovisioning_bits: u32 = 0;
245        let input = key_derivation_material.hash.as_slice();
246
247        let mut context = Vec::with_capacity(input.len() + mem::size_of::<u32>() * 2);
248        context.extend(input);
249        context.extend(&lcs.to_ne_bytes());
250        context.extend(&reprovisioning_bits.to_ne_bytes());
251
252        let seed = generate_seed(
253            &context,
254            &key_derivation_material.guk,
255            Self::CPAK_SEED_LABEL,
256        );
257        let cpak = derive_p384_key(&seed, None);
258
259        Self {
260            cpak: CPAK {
261                instance_id: Self::generate_instance_id(&cpak).into_iter().collect(),
262                key: cpak,
263            },
264            derivation_material: key_derivation_material,
265            claims,
266            dak: None,
267        }
268    }
269
270    /// Unmark DAK as created
271    pub fn reset(&mut self) {
272        self.dak = None;
273    }
274
275    /// Generates DAK with [`ECCFamily`] and uses `measurements` ([`Measurement`])
276    /// as salt in the process.
277    /// Returns bytes of a scalar primitive, which can be used to recreate DAK Private Key.
278    /// [`HashAlgo`] is used for verification process, when `get_platform_token` is called.
279    /// Returns [`AttestationError::GenericError`], when CBOR or crypto operation fails.
280    pub fn get_delegated_key(
281        &mut self,
282        _ecc_family: ECCFamily,
283        key_bits: KeyBits,
284        hash_algo: HashAlgo,
285        measurements: &[Measurement],
286    ) -> Result<Vec<u8>, AttestationError> {
287        let mut context = Vec::new();
288        context.extend(&self.derivation_material.hash);
289        context.extend(&self.claims.security_lifecycle.to_ne_bytes());
290        context.extend(&0u32.to_ne_bytes());
291
292        let seed = generate_seed(
293            &context,
294            &self.derivation_material.guk,
295            &Self::DAK_SEED_LABEL,
296        );
297
298        let salt = utils::encode_measurements(measurements);
299        let mut salt_bytes: Vec<u8> = Vec::new();
300        ser::into_writer(&salt, &mut salt_bytes).map_err(|_| AttestationError::GenericError)?;
301
302        let dak = match key_bits {
303            KeyBits::Bits256 => derive_p256_key(&seed, Some(&salt_bytes))
304                .to_bytes()
305                .to_vec(),
306            KeyBits::Bits384 => derive_p384_key(&seed, Some(&salt_bytes))
307                .to_bytes()
308                .to_vec(),
309            KeyBits::Bits521 => return Err(AttestationError::NotSupported),
310        };
311
312        self.dak = Some(DAKInfo {
313            hash_algo,
314            key_bits,
315            key: dak.clone(),
316        });
317
318        Ok(dak)
319    }
320
321    fn encode_claims(&self, dak_hash: &[u8], measurements: &[Measurement]) -> Value {
322        let mut map = Vec::with_capacity(9);
323
324        map.push((
325            Value::Integer(token_tag::CCA_PLAT_CHALLENGE.into()),
326            Value::Bytes(dak_hash.to_vec()),
327        ));
328        map.push((
329            Value::Integer(token_tag::CCA_PLAT_INSTANCE_ID.into()),
330            Value::Bytes(self.cpak.instance_id.to_vec()),
331        ));
332        if let Some(profile_definition) = &self.claims.profile_definition {
333            map.push((
334                Value::Integer(token_tag::CCA_PLAT_PROFILE.into()),
335                Value::Text(profile_definition.clone()),
336            ));
337        }
338
339        map.push((
340            Value::Integer(token_tag::CCA_PLAT_SECURITY_LIFECYCLE.into()),
341            Value::Integer((self.claims.security_lifecycle as u32).into()),
342        ));
343        map.push((
344            Value::Integer(token_tag::CCA_PLAT_IMPLEMENTATION_ID.into()),
345            Value::Bytes(self.claims.implementation_id.to_vec()),
346        ));
347        map.push((
348            Value::Integer(token_tag::CCA_PLAT_SW_COMPONENTS.into()),
349            utils::encode_measurements(measurements),
350        ));
351        if let Some(verification_service_url) = &self.claims.verification_service_url {
352            map.push((
353                Value::Integer(token_tag::CCA_PLAT_VERIFICATION_SERVICE.into()),
354                Value::Text(verification_service_url.clone()),
355            ));
356        }
357        map.push((
358            Value::Integer(token_tag::CCA_PLAT_CONFIGURATION.into()),
359            Value::Bytes(self.claims.platform_config.to_vec()),
360        ));
361        map.push((
362            Value::Integer(token_tag::CCA_PLAT_HASH_ALGO_DESC.into()),
363            Value::Text(self.dak.as_ref().unwrap().hash_algo.into()),
364        ));
365        Value::Map(map)
366    }
367
368    fn sign(&self, data: &[u8]) -> Result<Vec<u8>, AttestationError> {
369        let signing_key = p384::ecdsa::SigningKey::from_bytes(&self.cpak.key.to_bytes())
370            .map_err(|_| AttestationError::GenericError)?;
371        let signature: P384Signature = signing_key
372            .try_sign(data)
373            .map_err(|_| AttestationError::GenericError)?;
374        Ok(signature.to_vec())
375    }
376
377    fn verify_dak_hash(&self, dak_pub_hash: &[u8]) -> bool {
378        let DAKInfo { hash_algo, .. } = self.dak.as_ref().unwrap();
379        let calculated_hash = self.calculate_dak_hash(*hash_algo);
380        dak_pub_hash == &calculated_hash[..]
381    }
382
383    /// Creates a tagged [`CoseSign1`] of the platform token.
384    /// `dak_pub_hash` must be a valid hash of DAK Public Key using [`HashAlgo`] passed
385    /// in [`AttestationMgr::get_delegated_key`].
386    /// Returns [`AttestationError::GenericError`], when CBOR or crypto operation fails.
387    /// Returns [`AttestationError::InvalidArgument`], when DAK was not requsted before
388    /// this operation, or `dak_pub_hash` is not a valid hash of DAK Public Key.
389    pub fn get_platform_token(
390        &mut self,
391        dak_pub_hash: &[u8],
392        measurements: &[Measurement],
393    ) -> Result<CoseSign1, AttestationError> {
394        if self.dak.is_none() {
395            return Err(AttestationError::InvalidArgument);
396        }
397
398        if !self.verify_dak_hash(dak_pub_hash) {
399            return Err(AttestationError::InvalidArgument);
400        }
401
402        let encoded_claims = self.encode_claims(dak_pub_hash, measurements);
403        let mut token = Vec::new();
404        ser::into_writer(&encoded_claims, &mut token).expect("Unable to encode token");
405
406        let protected = HeaderBuilder::new()
407            .algorithm(coset::iana::Algorithm::ES384)
408            .build();
409
410        let mut singning_error = None;
411
412        let sign1 = CoseSign1Builder::new()
413            .protected(protected)
414            .payload(token)
415            .create_signature(b"", |payload| match self.sign(payload) {
416                Ok(sign) => sign,
417                Err(e) => {
418                    singning_error = Some(e);
419                    Vec::new()
420                }
421            })
422            .build();
423
424        match singning_error {
425            None => Ok(sign1),
426            Some(e) => Err(e),
427        }
428    }
429}
430
431#[cfg(test)]
432mod tests {
433    use core::str::from_utf8;
434
435    use alloc::vec;
436    use ciborium::de;
437
438    use crate::{MeasurementMetaData, SWType, SWVersion, SignerHash, ValueHash};
439
440    use super::*;
441
442    fn key_derivation_material() -> KeyMaterialData {
443        KeyMaterialData {
444            hash: [
445                0xf1, 0x5f, 0x95, 0x3b, 0xe5, 0x0d, 0xad, 0x92, 0xc3, 0xb2, 0xaa, 0x32, 0x97, 0xe6,
446                0xa4, 0xa8, 0xd6, 0x6d, 0x33, 0x63, 0x84, 0x49, 0xec, 0x19, 0x22, 0xb4, 0xa7, 0x92,
447                0x4a, 0x7b, 0x30, 0x22,
448            ]
449            .iter()
450            .cloned()
451            .collect(),
452            guk: [
453                0x01, 0x23, 0x45, 0x67, 0x89, 0x01, 0x23, 0x45, 0x67, 0x89, 0x01, 0x23, 0x45, 0x67,
454                0x89, 0x01, 0x23, 0x45, 0x67, 0x89, 0x01, 0x23, 0x45, 0x67, 0x89, 0x01, 0x23, 0x45,
455                0x67, 0x89, 0x01, 0x23,
456            ]
457            .into(),
458        }
459    }
460
461    fn hw_claims() -> HWClaims {
462        HWClaims {
463            implementation_id: [
464                0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB,
465                0xBB, 0xBB, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xDD, 0xDD, 0xDD, 0xDD,
466                0xDD, 0xDD, 0xDD, 0xDD,
467            ],
468            security_lifecycle: 0x4000,
469            profile_definition: Some("tag:arm.com,2023:cca_platform#1.0.0".to_string()),
470            verification_service_url: Some("http://whatever.com".to_string()),
471            platform_config: 0xDEADBEEFu32.to_ne_bytes().iter().cloned().collect(),
472        }
473    }
474
475    fn measurements() -> Vec<Measurement> {
476        let signer_id: SignerHash = ArrayVec::from([
477            0x01, 0x05, 0x01, 0xEF, 0x68, 0x07, 0x88, 0xCC, 0x33, 0x06, 0x54, 0xAB, 0x09, 0x01,
478            0x74, 0x77, 0x49, 0x08, 0x93, 0xA8, 0x01, 0x07, 0xEF, 0x01, 0x83, 0x09, 0x22, 0xCD,
479            0x09, 0x61, 0xB6, 0xFF, 0x01, 0x05, 0x01, 0xEF, 0x68, 0x07, 0x88, 0xCC, 0x33, 0x06,
480            0x54, 0xAB, 0x09, 0x01, 0x74, 0x77, 0x49, 0x08, 0x93, 0xA8, 0x01, 0x07, 0xEF, 0x01,
481            0x83, 0x09, 0x22, 0xCD, 0x09, 0x61, 0xB6, 0xFF,
482        ]);
483
484        let sw_version: SWVersion = from_utf8(&[
485            0x32, 0x35, 0x35, 0x2E, 0x32, 0x35, 0x35, 0x2E, 0x36, 0x35, 0x35, 0x33, 0x35, 0x0,
486        ])
487        .unwrap()
488        .to_string();
489
490        let sw_type: SWType = from_utf8(&[
491            0x4D, 0x45, 0x41, 0x53, 0x55, 0x52, 0x45, 0x44, 0x5F, 0x42, 0x4F, 0x4F, 0x54, 0x5F,
492            0x54, 0x45, 0x53, 0x54, 0x53, 0x0,
493        ])
494        .unwrap()
495        .to_string();
496
497        vec![Measurement {
498            metadata: MeasurementMetaData {
499                signer_id,
500                sw_version,
501                algorithm: MeasurementType::Sha512,
502                sw_type,
503            },
504            value: ValueHash::from([
505                0x8a, 0x66, 0x01, 0xf6, 0x70, 0x74, 0x8b, 0xe2, 0x33, 0xff, 0x5d, 0x75, 0xd7, 0xea,
506                0x89, 0xa8, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x01, 0x05, 0x01, 0xEF,
507                0x68, 0x07, 0x88, 0xCC, 0x83, 0x09, 0x22, 0xCD, 0x09, 0x61, 0xB6, 0xFF, 0xbb, 0xbb,
508                0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x56, 0x46, 0x58, 0x49, 0x99, 0x31, 0xcf, 0x59,
509                0x7d, 0xbc, 0x3a, 0x4e, 0x68, 0x79, 0x8a, 0x1c,
510            ]),
511        }]
512    }
513
514    #[test]
515    fn token_before_dak_error() {
516        let mut mgr = AttestationMgr::init(key_derivation_material(), hw_claims());
517
518        assert_eq!(
519            mgr.get_platform_token(&[], &[]).unwrap_err(),
520            AttestationError::InvalidArgument
521        );
522    }
523
524    #[test]
525    fn token_bad_dak_hash_value() {
526        let mut mgr = AttestationMgr::init(key_derivation_material(), hw_claims());
527
528        let _dak = mgr
529            .get_delegated_key(ECCFamily::SecpR1, KeyBits::Bits384, HashAlgo::Sha256, &[])
530            .unwrap();
531
532        assert_eq!(
533            mgr.get_platform_token(&[], &[]).unwrap_err(),
534            AttestationError::InvalidArgument
535        );
536    }
537
538    #[test]
539    fn token_bad_dak_hash_algo() {
540        let mut mgr = AttestationMgr::init(key_derivation_material(), hw_claims());
541
542        let dak = mgr
543            .get_delegated_key(ECCFamily::SecpR1, KeyBits::Bits384, HashAlgo::Sha256, &[])
544            .unwrap();
545
546        let key_public_sec1 = p384::SecretKey::from_slice(&dak)
547            .unwrap()
548            .public_key()
549            .to_sec1_bytes();
550        let key_public_cose = ec_public_key_sec1_to_cose(&key_public_sec1);
551        let hash = calculate_public_key_hash(&key_public_cose, HashAlgo::Sha512);
552
553        assert_eq!(
554            mgr.get_platform_token(&hash, &[]).unwrap_err(),
555            AttestationError::InvalidArgument
556        );
557    }
558
559    #[test]
560    fn token_sign1_verify() {
561        let key_derivation_material = key_derivation_material();
562        let claims = hw_claims();
563        let boot_measurements = measurements();
564        let hash_algo = HashAlgo::Sha256;
565
566        let mut mgr = AttestationMgr::init(key_derivation_material.clone(), claims.clone());
567        let dak = mgr
568            .get_delegated_key(
569                ECCFamily::SecpR1,
570                KeyBits::Bits384,
571                hash_algo,
572                &boot_measurements,
573            )
574            .unwrap();
575
576        let key_public_sec1 = p384::SecretKey::from_slice(&dak)
577            .unwrap()
578            .public_key()
579            .to_sec1_bytes();
580        let key_public_cose = ec_public_key_sec1_to_cose(&key_public_sec1);
581        let hash = calculate_public_key_hash(&key_public_cose, hash_algo);
582
583        let token_sign1 = mgr.get_platform_token(&hash, &boot_measurements).unwrap();
584
585        assert!(token_sign1
586            .verify_signature(&[], |sig, data| {
587                (mgr.sign(data).unwrap() == sig).then_some(()).ok_or(())
588            })
589            .is_ok());
590    }
591
592    fn claims_occurence_vector() -> Vec<(u32, bool)> {
593        let occurence_vec = vec![
594            (token_tag::CCA_PLAT_CHALLENGE, false),
595            (token_tag::CCA_PLAT_INSTANCE_ID, false),
596            (token_tag::CCA_PLAT_SECURITY_LIFECYCLE, false),
597            (token_tag::CCA_PLAT_IMPLEMENTATION_ID, false),
598            (token_tag::CCA_PLAT_SW_COMPONENTS, false),
599            (token_tag::CCA_PLAT_PROFILE, false),
600            (token_tag::CCA_PLAT_VERIFICATION_SERVICE, false),
601            (token_tag::CCA_PLAT_CONFIGURATION, false),
602            (token_tag::CCA_PLAT_HASH_ALGO_DESC, false),
603        ];
604
605        assert_eq!(occurence_vec.len(), 9);
606
607        occurence_vec
608    }
609
610    fn mark_occurence(vec: &mut Vec<(u32, bool)>, occured_tag: u32) {
611        let (_, tag_occurence) = vec
612            .iter_mut()
613            .find(|&&mut (tag, _)| tag == occured_tag)
614            .unwrap();
615        *tag_occurence = true;
616    }
617
618    fn verify_occurence(vec: &Vec<(u32, bool)>) -> bool {
619        !vec.iter()
620            .find(|&&(_, occurence)| occurence == false)
621            .is_some()
622    }
623
624    #[test]
625    fn token_decode_verify() {
626        let key_derivation_material = key_derivation_material();
627        let claims = hw_claims();
628        let measurements = measurements();
629        let hash_algo = HashAlgo::Sha256;
630
631        let mut mgr = AttestationMgr::init(key_derivation_material.clone(), claims.clone());
632        let dak = mgr
633            .get_delegated_key(
634                ECCFamily::SecpR1,
635                KeyBits::Bits384,
636                hash_algo,
637                &measurements,
638            )
639            .unwrap();
640
641        let key_public_sec1 = p384::SecretKey::from_slice(&dak)
642            .unwrap()
643            .public_key()
644            .to_sec1_bytes();
645        let key_public_cose = ec_public_key_sec1_to_cose(&key_public_sec1);
646        let hash = calculate_public_key_hash(&key_public_cose, hash_algo);
647
648        let token_sign1 = mgr.get_platform_token(&hash, &measurements).unwrap();
649        let payload = de::from_reader(&token_sign1.payload.unwrap()[..])
650            .expect("CoseSign1 is not a cbor Value");
651
652        let token_map = match payload {
653            Value::Map(platform_token_map) => platform_token_map,
654            _ => panic!("CoseSign1 payload is not a map!"),
655        };
656
657        let mut occurence_vec = claims_occurence_vector();
658
659        for (tag_value, value) in token_map {
660            let tag: u32 = match tag_value {
661                Value::Integer(tag_integer) => tag_integer.try_into().unwrap(),
662                _ => panic!("Tag is incorrect"),
663            };
664
665            match tag {
666                token_tag::CCA_PLAT_CHALLENGE => {
667                    if let Value::Bytes(dak_hash) = value {
668                        assert_eq!(dak_hash, hash);
669                    } else {
670                        panic!("CCA_PLAT_CHALLENGE incorrect");
671                    }
672                }
673                token_tag::CCA_PLAT_CONFIGURATION => {
674                    if let Value::Bytes(platform_config) = value {
675                        assert_eq!(&platform_config[..], &claims.platform_config[..]);
676                    } else {
677                        panic!("CCA_PLAT_CONFIGURATION incorrect");
678                    }
679                }
680                token_tag::CCA_PLAT_INSTANCE_ID => {
681                    if let Value::Bytes(instance_id) = value {
682                        assert_eq!(instance_id[0], 0x01);
683                        assert_eq!(
684                            instance_id[1..],
685                            calculate_public_key_hash(
686                                &mgr.cpak.key.public_key().to_sec1_bytes(),
687                                HashAlgo::Sha256
688                            )
689                        );
690                    } else {
691                        panic!("CCA_PLAT_INSTANCE_ID incorrect");
692                    }
693                }
694                token_tag::CCA_PLAT_PROFILE => {
695                    if let Value::Text(profile) = value {
696                        assert_eq!(profile, *claims.profile_definition.as_ref().unwrap());
697                    } else {
698                        panic!("CCA_PLAT_PROFILE incorrect");
699                    }
700                }
701                token_tag::CCA_PLAT_SECURITY_LIFECYCLE => {
702                    if let Value::Integer(security_lifecycle) = value {
703                        let value_u32: u32 = security_lifecycle.try_into().unwrap();
704                        assert_eq!(value_u32, claims.security_lifecycle as u32);
705                    } else {
706                        panic!("CCA_PLAT_PROFILE incorrect");
707                    }
708                }
709                token_tag::CCA_PLAT_IMPLEMENTATION_ID => {
710                    if let Value::Bytes(implementation_id) = value {
711                        assert_eq!(implementation_id, &claims.implementation_id);
712                    } else {
713                        panic!("CCA_PLAT_IMPLEMENTATION_ID incorrect");
714                    }
715                }
716                token_tag::CCA_PLAT_HASH_ALGO_DESC => {
717                    if let Value::Text(algo_desc) = value {
718                        let claim_desc: String = mgr.dak.as_ref().unwrap().hash_algo.into();
719                        assert_eq!(algo_desc, claim_desc);
720                    } else {
721                        panic!("CCA_PLAT_HASH_ALGO_DESC incorrect");
722                    }
723                }
724                token_tag::CCA_PLAT_VERIFICATION_SERVICE => {
725                    if let Value::Text(verficiation_service) = value {
726                        assert_eq!(
727                            verficiation_service,
728                            *claims.verification_service_url.as_ref().unwrap()
729                        );
730                    }
731                }
732                token_tag::CCA_PLAT_SW_COMPONENTS => {
733                    if let Value::Array(measurements_array) = value {
734                        // This code works only for measurements with one entry
735                        for measurement_map in measurements_array {
736                            if let Value::Map(measurement) = measurement_map {
737                                for (tag_value, value) in measurement {
738                                    let tag: u32 = match tag_value {
739                                        Value::Integer(tag_integer) => {
740                                            tag_integer.try_into().unwrap()
741                                        }
742                                        _ => panic!("SW COMP map tag not an Integer"),
743                                    };
744                                    match tag {
745                                        token_tag::CCA_SW_COMP_HASH_ALGORITHM => {
746                                            if let Value::Text(algorithm) = value {
747                                                let claim_desc: String =
748                                                    measurements[0].metadata.algorithm.into();
749                                                assert_eq!(algorithm, claim_desc);
750                                            } else {
751                                                panic!("CCA_SW_COMP_HASH_ALGORITHM incorrect");
752                                            }
753                                        }
754                                        token_tag::CCA_SW_COMP_TITLE => {
755                                            if let Value::Text(title) = value {
756                                                assert_eq!(title, measurements[0].metadata.sw_type);
757                                            } else {
758                                                panic!("CCA_SW_COMP_TITLE incorrect");
759                                            }
760                                        }
761                                        token_tag::CCA_SW_COMP_SIGNER_ID => {
762                                            if let Value::Bytes(signer_id) = value {
763                                                assert_eq!(
764                                                    &signer_id[..],
765                                                    &measurements[0].metadata.signer_id[..]
766                                                );
767                                            } else {
768                                                panic!("CCA_SW_COMP_SINGER_ID incorrect");
769                                            }
770                                        }
771                                        token_tag::CCA_SW_COMP_VERSION => {
772                                            if let Value::Text(version) = value {
773                                                assert_eq!(
774                                                    version,
775                                                    measurements[0].metadata.sw_version
776                                                );
777                                            } else {
778                                                panic!("CCA_SW_COMP_VERSION incorrect");
779                                            }
780                                        }
781                                        token_tag::CCA_SW_COMP_MEASUREMENT_VALUE => {
782                                            if let Value::Bytes(measurement_value) = value {
783                                                assert_eq!(
784                                                    measurement_value,
785                                                    &measurements[0].value[..]
786                                                );
787                                            } else {
788                                                panic!("CCA_SW_COMP_MEASUREMENT_VALUE incorrect");
789                                            }
790                                        }
791                                        _ => panic!("Invalid SW COMP tag value: {}", tag),
792                                    }
793                                }
794                            }
795                        }
796                    }
797                }
798                _ => panic!("Invalid tag value: {}", tag),
799            }
800            mark_occurence(&mut occurence_vec, tag);
801        }
802
803        assert!(verify_occurence(&occurence_vec));
804    }
805}