Skip to main content

kbs_types/
lib.rs

1// Support using this crate without the standard library
2#![cfg_attr(not(feature = "std"), no_std)]
3
4// As long as there is a memory allocator, we can still use this crate
5// without the rest of the standard library by using the `alloc` crate
6#[cfg(feature = "alloc")]
7extern crate alloc;
8
9mod error;
10mod hash_algorithm;
11
12pub use error::{KbsTypesError, Result};
13pub use hash_algorithm::HashAlgorithm;
14
15#[cfg(all(feature = "alloc", not(feature = "std")))]
16use alloc::{string::String, vec::Vec};
17use base64::{prelude::BASE64_URL_SAFE_NO_PAD, Engine};
18#[cfg(feature = "std")]
19use ear::{self, RawValue};
20use serde::{Deserialize, Serialize};
21use serde_json::{Map, Value};
22#[cfg(all(feature = "std", not(feature = "alloc")))]
23use std::string::String;
24
25use strum::{AsRefStr, Display, EnumString};
26
27#[derive(
28    Serialize, Clone, Copy, Deserialize, Debug, Eq, Hash, PartialEq, AsRefStr, Display, EnumString,
29)]
30#[serde(rename_all = "lowercase")]
31pub enum Tee {
32    // Azure CVMs with vTPM attestation
33    #[serde(rename = "az-snp-vtpm")]
34    #[strum(serialize = "az-snp-vtpm")]
35    AzSnpVtpm,
36    #[serde(rename = "az-tdx-vtpm")]
37    #[strum(serialize = "az-tdx-vtpm")]
38    AzTdxVtpm,
39    #[strum(serialize = "nvidia")]
40    Nvidia,
41    #[strum(serialize = "sgx")]
42    Sgx,
43    #[strum(serialize = "snp")]
44    Snp,
45    #[strum(serialize = "tdx")]
46    Tdx,
47    // Arm Confidential Compute Architecture
48    #[strum(serialize = "cca")]
49    Cca,
50    // China Secure Virtualization
51    #[strum(serialize = "csv")]
52    Csv,
53    // IBM Z Secure Execution
54    #[strum(serialize = "se")]
55    Se,
56
57    /// Hygon DCU (Deep Computing Unit)
58    #[strum(serialize = "hygondcu")]
59    HygonDcu,
60
61    /// NVIDIA DPU attestation
62    #[serde(rename = "nvidia-dpu", alias = "nvidiadpu")]
63    #[strum(to_string = "nvidia-dpu", serialize = "nvidiadpu")]
64    NvidiaDpu,
65
66    // Trusted Platform Module
67    #[strum(serialize = "tpm")]
68    Tpm,
69
70    // These values are only used for testing an attestation server, and should not
71    // be used in an actual attestation scenario.
72    #[strum(serialize = "sample")]
73    Sample,
74    #[strum(serialize = "sampledevice")]
75    SampleDevice,
76}
77
78#[derive(Clone, Serialize, Deserialize, Debug)]
79pub struct Request {
80    pub version: String,
81    pub tee: Tee,
82    #[serde(rename = "extra-params")]
83    pub extra_params: Value,
84}
85
86#[derive(Clone, Serialize, Deserialize, Debug)]
87pub struct Challenge {
88    pub nonce: String,
89    #[serde(rename = "extra-params")]
90    pub extra_params: Value,
91}
92
93#[derive(Clone, Serialize, Deserialize, Debug)]
94#[serde(tag = "kty")]
95pub enum TeePubKey {
96    RSA {
97        alg: String,
98        #[serde(rename = "n")]
99        k_mod: String,
100        #[serde(rename = "e")]
101        k_exp: String,
102    },
103    /// Elliptic Curve Keys
104    /// fields defined in
105    /// [RFC 7518 Section 6.1](https://www.rfc-editor.org/rfc/rfc7518.html#page-28)
106    EC {
107        crv: String,
108        alg: String,
109        x: String,
110        y: String,
111    },
112    /// Algorithm Key Pair (AKP) key type for PQC algorithm support as per
113    /// [draft-ietf-jose-pqc-kem-05](https://datatracker.ietf.org/doc/draft-ietf-jose-pqc-kem/)
114    AKP {
115        alg: String,
116        #[serde(rename = "pub")]
117        public_key: String,
118    },
119}
120
121#[cfg(feature = "std")]
122impl From<&TeePubKey> for ear::RawValue {
123    fn from(tpk: &TeePubKey) -> RawValue {
124        let mut map: Vec<(RawValue, RawValue)> = vec![];
125
126        match tpk {
127            TeePubKey::RSA { alg, k_mod, k_exp } => {
128                map.push((
129                    RawValue::String("kty".to_string()),
130                    RawValue::String("RSA".to_string()),
131                ));
132                map.push((
133                    RawValue::String("alg".to_string()),
134                    RawValue::String(alg.clone()),
135                ));
136                map.push((
137                    RawValue::String("n".to_string()),
138                    RawValue::String(k_mod.clone()),
139                ));
140                map.push((
141                    RawValue::String("e".to_string()),
142                    RawValue::String(k_exp.clone()),
143                ));
144            }
145            TeePubKey::EC { crv, alg, x, y } => {
146                map.push((
147                    RawValue::String("kty".to_string()),
148                    RawValue::String("EC".to_string()),
149                ));
150                map.push((
151                    RawValue::String("crv".to_string()),
152                    RawValue::String(crv.clone()),
153                ));
154                map.push((
155                    RawValue::String("alg".to_string()),
156                    RawValue::String(alg.clone()),
157                ));
158                map.push((
159                    RawValue::String("x".to_string()),
160                    RawValue::String(x.clone()),
161                ));
162                map.push((
163                    RawValue::String("y".to_string()),
164                    RawValue::String(y.clone()),
165                ));
166            }
167            TeePubKey::AKP { alg, public_key } => {
168                map.push((
169                    RawValue::String("kty".to_string()),
170                    RawValue::String("AKP".to_string()),
171                ));
172                map.push((
173                    RawValue::String("alg".to_string()),
174                    RawValue::String(alg.clone()),
175                ));
176                map.push((
177                    RawValue::String("pub".to_string()),
178                    RawValue::String(public_key.clone()),
179                ));
180            }
181        }
182
183        RawValue::Map(map)
184    }
185}
186
187/// Data generated during the attestation process between client and server. Relevant only to the
188/// client-server pairing.
189#[derive(Clone, Debug, Deserialize, Serialize)]
190pub struct RuntimeData {
191    /// Nonce string generated by server.
192    pub nonce: String,
193
194    /// TEE public key generated by client.
195    #[serde(rename = "tee-pubkey")]
196    pub tee_pubkey: TeePubKey,
197}
198
199/// Combined evidence of all TEE devices found within a client.
200#[derive(Clone, Debug, Deserialize, Serialize)]
201pub struct CompositeEvidence {
202    /// Primary TEE evidence. Deserialization dependent on underlying attestation service.
203    pub primary_evidence: Value,
204
205    /// Additional evidence for secondary TEE devices within a client. JSON mapping of:
206    ///
207    /// Tee --> (TEE class, TEE evidence)
208    ///
209    /// Represented as string to avoid {de}serialization inconsistencies.
210    pub additional_evidence: String,
211}
212
213/// Initialization data injected from an untrusted host into a TEE guest.
214#[derive(Clone, Debug, Deserialize, Serialize)]
215pub struct InitData {
216    /// Format that INITDATA body should be deserialized/read to. Dependent on attestation service.
217    pub format: String,
218
219    /// Initialization data contents.
220    pub body: String,
221}
222
223#[derive(Clone, Serialize, Deserialize, Debug)]
224#[serde(rename_all = "kebab-case")]
225pub struct Attestation {
226    pub init_data: Option<InitData>,
227    pub runtime_data: RuntimeData,
228    pub tee_evidence: CompositeEvidence,
229}
230
231#[derive(Clone, Serialize, Deserialize, Debug)]
232pub struct ProtectedHeader {
233    /// Enryption algorithm for encrypted key
234    pub alg: String,
235    /// Encryption algorithm for payload
236    pub enc: String,
237
238    /// Other fields of Protected Header
239    #[serde(skip_serializing_if = "Map::is_empty", flatten)]
240    pub other_fields: Map<String, Value>,
241}
242
243impl ProtectedHeader {
244    /// The generation of AAD for JWE follows [A.3.5 RFC7516](https://www.rfc-editor.org/rfc/rfc7516#appendix-A.3.5)
245    pub fn generate_aad(&self) -> Result<Vec<u8>> {
246        let protected_utf8 = serde_json::to_string(&self).map_err(|_| KbsTypesError::Serde)?;
247        let aad = BASE64_URL_SAFE_NO_PAD.encode(protected_utf8);
248        Ok(aad.into_bytes())
249    }
250}
251
252fn serialize_base64_protected_header<S>(
253    sub: &ProtectedHeader,
254    serializer: S,
255) -> core::result::Result<S::Ok, S::Error>
256where
257    S: serde::Serializer,
258{
259    let protected_header_json = serde_json::to_string(sub).map_err(serde::ser::Error::custom)?;
260    let encoded = BASE64_URL_SAFE_NO_PAD.encode(protected_header_json);
261    serializer.serialize_str(&encoded)
262}
263
264fn deserialize_base64_protected_header<'de, D>(
265    deserializer: D,
266) -> core::result::Result<ProtectedHeader, D::Error>
267where
268    D: serde::Deserializer<'de>,
269{
270    let encoded = String::deserialize(deserializer)?;
271    let decoded = BASE64_URL_SAFE_NO_PAD
272        .decode(encoded)
273        .map_err(serde::de::Error::custom)?;
274    let protected_header = serde_json::from_slice(&decoded).map_err(serde::de::Error::custom)?;
275
276    Ok(protected_header)
277}
278
279fn serialize_base64<S>(sub: &Vec<u8>, serializer: S) -> core::result::Result<S::Ok, S::Error>
280where
281    S: serde::Serializer,
282{
283    let encoded = BASE64_URL_SAFE_NO_PAD.encode(sub);
284    serializer.serialize_str(&encoded)
285}
286
287fn deserialize_base64<'de, D>(deserializer: D) -> core::result::Result<Vec<u8>, D::Error>
288where
289    D: serde::Deserializer<'de>,
290{
291    let encoded = String::deserialize(deserializer)?;
292    let decoded = BASE64_URL_SAFE_NO_PAD
293        .decode(encoded)
294        .map_err(serde::de::Error::custom)?;
295
296    Ok(decoded)
297}
298
299fn serialize_base64_vec<S>(
300    sub: &Option<Vec<u8>>,
301    serializer: S,
302) -> core::result::Result<S::Ok, S::Error>
303where
304    S: serde::Serializer,
305{
306    match sub {
307        Some(value) => {
308            let encoded = String::from_utf8(value.clone()).map_err(serde::ser::Error::custom)?;
309            serializer.serialize_str(&encoded)
310        }
311        None => serializer.serialize_none(),
312    }
313}
314
315fn deserialize_base64_vec<'de, D>(
316    deserializer: D,
317) -> core::result::Result<Option<Vec<u8>>, D::Error>
318where
319    D: serde::Deserializer<'de>,
320{
321    let string = String::deserialize(deserializer)?;
322    let bytes = string.into_bytes();
323
324    Ok(Some(bytes))
325}
326
327#[derive(Clone, Serialize, Deserialize, Debug)]
328pub struct Response {
329    #[serde(
330        serialize_with = "serialize_base64_protected_header",
331        deserialize_with = "deserialize_base64_protected_header"
332    )]
333    pub protected: ProtectedHeader,
334
335    #[serde(
336        serialize_with = "serialize_base64",
337        deserialize_with = "deserialize_base64"
338    )]
339    pub encrypted_key: Vec<u8>,
340
341    #[serde(
342        skip_serializing_if = "Option::is_none",
343        default = "Option::default",
344        serialize_with = "serialize_base64_vec",
345        deserialize_with = "deserialize_base64_vec"
346    )]
347    pub aad: Option<Vec<u8>>,
348
349    #[serde(
350        serialize_with = "serialize_base64",
351        deserialize_with = "deserialize_base64"
352    )]
353    pub iv: Vec<u8>,
354
355    #[serde(
356        serialize_with = "serialize_base64",
357        deserialize_with = "deserialize_base64"
358    )]
359    pub ciphertext: Vec<u8>,
360
361    #[serde(
362        serialize_with = "serialize_base64",
363        deserialize_with = "deserialize_base64"
364    )]
365    pub tag: Vec<u8>,
366}
367
368#[derive(Clone, Serialize, Deserialize, Debug)]
369pub struct ErrorInformation {
370    #[serde(rename = "type")]
371    pub error_type: String,
372    pub detail: String,
373}
374
375#[cfg(test)]
376mod tests {
377    use serde_json::json;
378
379    use crate::*;
380
381    #[cfg(all(feature = "alloc", not(feature = "std")))]
382    use alloc::string::ToString;
383
384    #[test]
385    fn parse_request() {
386        let data = r#"
387        {
388            "version": "0.0.0",
389            "tee": "tdx",
390            "extra-params": ""
391        }"#;
392
393        let request: Request = serde_json::from_str(data).unwrap();
394
395        assert_eq!(request.version, "0.0.0");
396        assert_eq!(request.tee, Tee::Tdx);
397        assert_eq!(request.extra_params, "");
398    }
399
400    #[test]
401    fn parse_challenge() {
402        let data = r#"
403        {
404            "nonce": "42",
405            "extra-params": ""
406        }"#;
407
408        let challenge: Challenge = serde_json::from_str(data).unwrap();
409
410        assert_eq!(challenge.nonce, "42");
411        assert_eq!(challenge.extra_params, "");
412    }
413
414    #[test]
415    fn protected_header_generate_aad() {
416        let protected_header = ProtectedHeader {
417            alg: "fakealg".to_string(),
418            enc: "fakeenc".to_string(),
419            other_fields: Map::new(),
420        };
421
422        let aad = protected_header.generate_aad().unwrap();
423
424        assert_eq!(
425            aad,
426            "eyJhbGciOiJmYWtlYWxnIiwiZW5jIjoiZmFrZWVuYyJ9".as_bytes()
427        );
428    }
429
430    #[test]
431    fn parse_response() {
432        let data = r#"
433        {
434            "protected": "eyJhbGciOiJmYWtlYWxnIiwiZW5jIjoiZmFrZWVuYyJ9",
435            "encrypted_key": "ZmFrZWtleQ",
436            "iv": "cmFuZG9tZGF0YQ",
437            "ciphertext": "ZmFrZWVuY291dHB1dA",
438            "tag": "ZmFrZXRhZw"
439        }"#;
440
441        let response: Response = serde_json::from_str(data).unwrap();
442
443        assert_eq!(response.protected.alg, "fakealg");
444        assert_eq!(response.protected.enc, "fakeenc");
445        assert!(response.protected.other_fields.is_empty());
446        assert_eq!(response.encrypted_key, "fakekey".as_bytes());
447        assert_eq!(response.iv, "randomdata".as_bytes());
448        assert_eq!(response.ciphertext, "fakeencoutput".as_bytes());
449        assert_eq!(response.tag, "faketag".as_bytes());
450        assert_eq!(response.aad, None);
451    }
452
453    #[test]
454    fn parse_response_nested_protected_header() {
455        let data = r#"
456        {
457            "protected": "eyJhbGciOiJmYWtlYWxnIiwiZW5jIjoiZmFrZWVuYyIsImVwayI6eyJrdHkiOiJPS1AiLCJjcnYiOiJYMjU1MTkiLCJ4IjoiaFNEd0NZa3dwMVIwaTMzY3RENzNXZzJfT2cwbU9CcjA2NlNwanFxYlRtbyJ9fQo",
458            "encrypted_key": "ZmFrZWtleQ",
459            "iv": "cmFuZG9tZGF0YQ",
460            "ciphertext": "ZmFrZWVuY291dHB1dA",
461            "tag": "ZmFrZXRhZw"
462        }"#;
463
464        let response: Response = serde_json::from_str(data).unwrap();
465
466        assert_eq!(response.protected.alg, "fakealg");
467        assert_eq!(response.protected.enc, "fakeenc");
468
469        let expected_other_fields = json!({
470            "epk": {
471                "kty" : "OKP",
472                "crv": "X25519",
473                "x": "hSDwCYkwp1R0i33ctD73Wg2_Og0mOBr066SpjqqbTmo"
474            }
475        })
476        .as_object()
477        .unwrap()
478        .clone();
479
480        assert_eq!(response.protected.other_fields, expected_other_fields);
481        assert_eq!(response.encrypted_key, "fakekey".as_bytes());
482        assert_eq!(response.iv, "randomdata".as_bytes());
483        assert_eq!(response.ciphertext, "fakeencoutput".as_bytes());
484        assert_eq!(response.tag, "faketag".as_bytes());
485        assert_eq!(response.aad, None);
486    }
487
488    #[test]
489    fn parse_response_with_aad() {
490        let data = r#"
491        {
492            "protected": "eyJhbGciOiJmYWtlYWxnIiwiZW5jIjoiZmFrZWVuYyJ9Cg",
493            "encrypted_key": "ZmFrZWtleQ",
494            "iv": "cmFuZG9tZGF0YQ",
495            "aad": "fakeaad",
496            "ciphertext": "ZmFrZWVuY291dHB1dA",
497            "tag": "ZmFrZXRhZw"
498        }"#;
499
500        let response: Response = serde_json::from_str(data).unwrap();
501
502        assert_eq!(response.protected.alg, "fakealg");
503        assert_eq!(response.protected.enc, "fakeenc");
504        assert!(response.protected.other_fields.is_empty());
505        assert_eq!(response.encrypted_key, "fakekey".as_bytes());
506        assert_eq!(response.iv, "randomdata".as_bytes());
507        assert_eq!(response.ciphertext, "fakeencoutput".as_bytes());
508        assert_eq!(response.tag, "faketag".as_bytes());
509        assert_eq!(response.aad, Some("fakeaad".into()));
510    }
511
512    #[test]
513    fn parse_response_with_protectedheader() {
514        let data = r#"
515        {
516            "protected": "eyJhbGciOiJmYWtlYWxnIiwiZW5jIjoiZmFrZWVuYyIsImZha2VmaWVsZCI6ImZha2V2YWx1ZSJ9",
517            "encrypted_key": "ZmFrZWtleQ",
518            "iv": "cmFuZG9tZGF0YQ",
519            "aad": "fakeaad",
520            "ciphertext": "ZmFrZWVuY291dHB1dA",
521            "tag": "ZmFrZXRhZw"
522        }"#;
523
524        let response: Response = serde_json::from_str(data).unwrap();
525
526        assert_eq!(response.protected.alg, "fakealg");
527        assert_eq!(response.protected.enc, "fakeenc");
528        assert_eq!(response.protected.other_fields["fakefield"], "fakevalue");
529        assert_eq!(response.encrypted_key, "fakekey".as_bytes());
530        assert_eq!(response.iv, "randomdata".as_bytes());
531        assert_eq!(response.ciphertext, "fakeencoutput".as_bytes());
532        assert_eq!(response.tag, "faketag".as_bytes());
533        assert_eq!(response.aad, Some("fakeaad".into()));
534    }
535
536    #[test]
537    fn serialize_response() {
538        let response = Response {
539            protected: ProtectedHeader {
540                alg: "fakealg".into(),
541                enc: "fakeenc".into(),
542                other_fields: [("fakefield".into(), "fakevalue".into())]
543                    .into_iter()
544                    .collect(),
545            },
546            encrypted_key: "fakekey".as_bytes().to_vec(),
547            iv: "randomdata".as_bytes().to_vec(),
548            aad: Some("fakeaad".into()),
549            tag: "faketag".as_bytes().to_vec(),
550            ciphertext: "fakeencoutput".as_bytes().to_vec(),
551        };
552
553        let expected = json!({
554            "protected": "eyJhbGciOiJmYWtlYWxnIiwiZW5jIjoiZmFrZWVuYyIsImZha2VmaWVsZCI6ImZha2V2YWx1ZSJ9",
555            "encrypted_key": "ZmFrZWtleQ",
556            "iv": "cmFuZG9tZGF0YQ",
557            "aad": "fakeaad",
558            "ciphertext": "ZmFrZWVuY291dHB1dA",
559            "tag": "ZmFrZXRhZw"
560        });
561
562        let serialized = serde_json::to_value(&response).unwrap();
563        assert_eq!(serialized, expected);
564    }
565
566    #[test]
567    fn parse_attestation_ec() {
568        let data = r#"
569        {
570            "runtime-data": {
571                "nonce": "test_nonce",
572                "tee-pubkey": {
573                    "kty": "EC",
574                    "crv": "fakecrv",
575                    "alg": "fakealgorithm",
576                    "x": "fakex",
577                    "y": "fakey"
578                }
579            },
580            "tee-evidence": {
581                "primary_evidence": "test_primary_evidence",
582                "additional_evidence": "test_additional_evidence"
583            }
584        }"#;
585
586        let attestation: Attestation = serde_json::from_str(data).unwrap();
587        let tee_pubkey = attestation.runtime_data.tee_pubkey;
588
589        let TeePubKey::EC { alg, crv, x, y } = tee_pubkey else {
590            panic!("Must be an EC key");
591        };
592
593        assert_eq!(alg, "fakealgorithm");
594        assert_eq!(crv, "fakecrv");
595        assert_eq!(x, "fakex");
596        assert_eq!(y, "fakey");
597        assert_eq!(
598            attestation.tee_evidence.primary_evidence,
599            "test_primary_evidence"
600        );
601        assert_eq!(
602            attestation.tee_evidence.additional_evidence,
603            "test_additional_evidence"
604        );
605    }
606
607    #[test]
608    fn parse_attestation_rsa() {
609        let data = r#"
610        {
611            "runtime-data": {
612                "nonce": "test_nonce",
613                "tee-pubkey": {
614                    "kty": "RSA",
615                    "alg": "fakealgorithm",
616                    "n": "fakemodulus",
617                    "e": "fakeexponent"
618                }
619            },
620            "tee-evidence": {
621                "primary_evidence": "test_primary_evidence",
622                "additional_evidence": "test_additional_evidence"
623            }
624        }"#;
625
626        let attestation: Attestation = serde_json::from_str(data).unwrap();
627        let tee_pubkey = attestation.runtime_data.tee_pubkey;
628
629        let TeePubKey::RSA { alg, k_mod, k_exp } = tee_pubkey else {
630            panic!("Must be a RSA key");
631        };
632
633        assert_eq!(attestation.runtime_data.nonce, "test_nonce");
634        assert_eq!(alg, "fakealgorithm");
635        assert_eq!(k_mod, "fakemodulus");
636        assert_eq!(k_exp, "fakeexponent");
637        assert_eq!(
638            attestation.tee_evidence.primary_evidence,
639            "test_primary_evidence"
640        );
641        assert_eq!(
642            attestation.tee_evidence.additional_evidence,
643            "test_additional_evidence"
644        );
645    }
646
647    #[test]
648    fn parse_attestation_akp() {
649        let data = r#"
650        {
651            "runtime-data": {
652                "nonce": "test_nonce",
653                "tee-pubkey": {
654                    "kty": "AKP",
655                    "alg": "fakealgorithm",
656                    "pub": "fakepublickey"
657                }
658            },
659            "tee-evidence": {
660                "primary_evidence": "test_primary_evidence",
661                "additional_evidence": "test_additional_evidence"
662            }
663        }"#;
664
665        let attestation: Attestation = serde_json::from_str(data).unwrap();
666        let tee_pubkey = attestation.runtime_data.tee_pubkey;
667
668        let TeePubKey::AKP { alg, public_key } = tee_pubkey else {
669            panic!("Must be a AKP key");
670        };
671
672        assert_eq!(attestation.runtime_data.nonce, "test_nonce");
673        assert_eq!(alg, "fakealgorithm");
674        assert_eq!(public_key, "fakepublickey");
675        assert_eq!(
676            attestation.tee_evidence.primary_evidence,
677            "test_primary_evidence"
678        );
679        assert_eq!(
680            attestation.tee_evidence.additional_evidence,
681            "test_additional_evidence"
682        );
683    }
684
685    #[test]
686    fn parse_error_information() {
687        let data = r#"
688        {
689            "type": "problemtype",
690            "detail": "problemdetail"
691        }"#;
692
693        let info: ErrorInformation = serde_json::from_str(data).unwrap();
694
695        assert_eq!(info.error_type, "problemtype");
696        assert_eq!(info.detail, "problemdetail");
697    }
698
699    #[test]
700    #[cfg(feature = "std")]
701    fn tee_pubkey_ear_json_deserialize() {
702        // RSA key.
703        let tpk = TeePubKey::RSA {
704            alg: "test".to_string(),
705            k_mod: "test".to_string(),
706            k_exp: "test".to_string(),
707        };
708        let ear_raw: RawValue = (&tpk).into();
709        let json_str = serde_json::to_string(&ear_raw).unwrap();
710        assert_eq!(json_str, serde_json::to_string(&tpk).unwrap());
711
712        // EC key.
713        let tpk = TeePubKey::EC {
714            crv: "test".to_string(),
715            alg: "test".to_string(),
716            x: "test".to_string(),
717            y: "test".to_string(),
718        };
719        let ear_raw: RawValue = (&tpk).into();
720        let json_str = serde_json::to_string(&ear_raw).unwrap();
721        assert_eq!(json_str, serde_json::to_string(&tpk).unwrap());
722
723        // AKP key.
724        let tpk = TeePubKey::AKP {
725            alg: "test".to_string(),
726            public_key: "test".to_string(),
727        };
728        let ear_raw: RawValue = (&tpk).into();
729        let json_str = serde_json::to_string(&ear_raw).unwrap();
730        assert_eq!(json_str, serde_json::to_string(&tpk).unwrap());
731    }
732
733    #[test]
734    fn tee_as_ref() {
735        assert_eq!(Tee::AzSnpVtpm.as_ref(), "az-snp-vtpm");
736        assert_eq!(Tee::AzTdxVtpm.as_ref(), "az-tdx-vtpm");
737        assert_eq!(Tee::Nvidia.as_ref(), "nvidia");
738        assert_eq!(Tee::Sgx.as_ref(), "sgx");
739        assert_eq!(Tee::Snp.as_ref(), "snp");
740        assert_eq!(Tee::Tdx.as_ref(), "tdx");
741        assert_eq!(Tee::Cca.as_ref(), "cca");
742        assert_eq!(Tee::Csv.as_ref(), "csv");
743        assert_eq!(Tee::Se.as_ref(), "se");
744        assert_eq!(Tee::HygonDcu.as_ref(), "hygondcu");
745        assert_eq!(Tee::NvidiaDpu.as_ref(), "nvidia-dpu");
746        assert_eq!(Tee::Tpm.as_ref(), "tpm");
747        assert_eq!(Tee::Sample.as_ref(), "sample");
748        assert_eq!(Tee::SampleDevice.as_ref(), "sampledevice");
749    }
750
751    #[cfg(feature = "std")]
752    #[test]
753    fn tee_from_str() {
754        use std::str::FromStr;
755
756        assert_eq!(Tee::from_str("az-snp-vtpm").unwrap(), Tee::AzSnpVtpm);
757        assert_eq!(Tee::from_str("az-tdx-vtpm").unwrap(), Tee::AzTdxVtpm);
758        assert_eq!(Tee::from_str("nvidia").unwrap(), Tee::Nvidia);
759        assert_eq!(Tee::from_str("sgx").unwrap(), Tee::Sgx);
760        assert_eq!(Tee::from_str("snp").unwrap(), Tee::Snp);
761        assert_eq!(Tee::from_str("tdx").unwrap(), Tee::Tdx);
762        assert_eq!(Tee::from_str("cca").unwrap(), Tee::Cca);
763        assert_eq!(Tee::from_str("csv").unwrap(), Tee::Csv);
764        assert_eq!(Tee::from_str("se").unwrap(), Tee::Se);
765        assert_eq!(Tee::from_str("hygondcu").unwrap(), Tee::HygonDcu);
766        assert_eq!(Tee::from_str("nvidia-dpu").unwrap(), Tee::NvidiaDpu);
767        assert_eq!(Tee::from_str("nvidiadpu").unwrap(), Tee::NvidiaDpu);
768        assert_eq!(Tee::from_str("tpm").unwrap(), Tee::Tpm);
769        assert_eq!(Tee::from_str("sample").unwrap(), Tee::Sample);
770        assert_eq!(Tee::from_str("sampledevice").unwrap(), Tee::SampleDevice);
771        Tee::from_str("invalid").unwrap_err();
772    }
773
774    #[test]
775    fn tee_serde_nvidia_dpu_alias() {
776        // Primary serialization output is "nvidia-dpu"
777        let serialized = serde_json::to_string(&Tee::NvidiaDpu).unwrap();
778        assert_eq!(serialized, "\"nvidia-dpu\"");
779
780        // "nvidiadpu" is accepted as a deserialization alias
781        let deserialized: Tee = serde_json::from_str("\"nvidiadpu\"").unwrap();
782        assert_eq!(deserialized, Tee::NvidiaDpu);
783    }
784}