dif_presentation_exchange/
presentation_submission.rs

1use crate::presentation_definition::ClaimFormatDesignation;
2use serde::{Deserialize, Serialize};
3use serde_with::skip_serializing_none;
4
5/// As specified in https://identity.foundation/presentation-exchange/#presentation-definition.
6#[allow(dead_code)]
7#[derive(Deserialize, Debug, Serialize, PartialEq, Clone)]
8pub struct PresentationSubmission {
9    // TODO: Must be unique.
10    pub id: String,
11    // TODO: Value must be the id value of a valid presentation definition.
12    pub definition_id: String,
13    pub descriptor_map: Vec<InputDescriptorMappingObject>,
14}
15
16#[allow(dead_code)]
17#[skip_serializing_none]
18#[derive(Deserialize, Debug, Serialize, PartialEq, Clone)]
19pub struct InputDescriptorMappingObject {
20    // Matches the `id` property of the Input Descriptor in the Presentation Definition that this Presentation
21    // Submission is related to.
22    pub id: String,
23    // Matches one of the Claim Format Designation. This denotes the data format of the Claim.
24    pub format: ClaimFormatDesignation,
25    // TODO Must be a JSONPath string expression
26    // Indicates the Claim submitted in relation to the identified Input Descriptor, When executed against the
27    // top-level of the object the Presentation Submission is embedded within.
28    pub path: String,
29    pub path_nested: Option<PathNested>,
30}
31
32#[allow(dead_code)]
33#[skip_serializing_none]
34#[derive(Deserialize, Debug, Serialize, PartialEq, Clone)]
35pub struct PathNested {
36    pub id: Option<String>,
37    pub format: ClaimFormatDesignation,
38    pub path: String,
39    pub path_nested: Option<Box<Self>>,
40}
41
42#[cfg(test)]
43mod tests {
44    use super::*;
45    use serde::de::DeserializeOwned;
46    use std::{fs::File, path::Path};
47
48    fn json_example<T>(path: &str) -> T
49    where
50        T: DeserializeOwned,
51    {
52        let file_path = Path::new(path);
53        let file = File::open(file_path).expect("file does not exist");
54        serde_json::from_reader::<_, T>(file).expect("could not parse json")
55    }
56
57    #[test]
58    fn test_deserialize_presentation_submission() {
59        assert_eq!(
60            PresentationSubmission {
61                id: "Presentation example 2".to_string(),
62                definition_id: "Example with multiple VPs".to_string(),
63                descriptor_map: vec![
64                    InputDescriptorMappingObject {
65                        id: "ID Card with constraints".to_string(),
66                        format: ClaimFormatDesignation::LdpVp,
67                        path: "$[0]".to_string(),
68                        path_nested: Some(PathNested {
69                            format: ClaimFormatDesignation::LdpVc,
70                            path: "$[0].verifiableCredential[0]".to_string(),
71                            id: None,
72                            path_nested: None
73                        })
74                    },
75                    InputDescriptorMappingObject {
76                        id: "Ontario Health Insurance Plan".to_string(),
77                        format: ClaimFormatDesignation::JwtVpJson,
78                        path: "$[1]".to_string(),
79                        path_nested: Some(PathNested {
80                            format: ClaimFormatDesignation::JwtVcJson,
81                            path: "$[1].vp.verifiableCredential[0]".to_string(),
82                            id: None,
83                            path_nested: None
84                        })
85                    }
86                ]
87            },
88            json_example::<PresentationSubmission>(
89                "../oid4vp/tests/examples/response/presentation_submission_multiple_vps.json"
90            )
91        );
92
93        assert_eq!(
94            PresentationSubmission {
95                id: "Presentation example 1".to_string(),
96                definition_id: "Example with selective disclosure".to_string(),
97                descriptor_map: vec![InputDescriptorMappingObject {
98                    id: "ID card with constraints".to_string(),
99                    format: ClaimFormatDesignation::LdpVp,
100                    path: "$".to_string(),
101                    path_nested: Some(PathNested {
102                        format: ClaimFormatDesignation::LdpVc,
103                        path: "$.verifiableCredential[0]".to_string(),
104                        id: None,
105                        path_nested: None
106                    })
107                }]
108            },
109            json_example::<PresentationSubmission>("../oid4vp/tests/examples/response/presentation_submission.json")
110        );
111
112        assert_eq!(
113            PresentationSubmission {
114                definition_id: "example_vc_ac_sd".to_string(),
115                id: "example_vc_ac_sd_presentation_submission".to_string(),
116                descriptor_map: vec![InputDescriptorMappingObject {
117                    id: "id_credential".to_string(),
118                    path: "$".to_string(),
119                    format: ClaimFormatDesignation::AcVp,
120                    path_nested: Some(PathNested {
121                        path: "$.requested_proof.revealed_attr_groups.id_card_credential".to_string(),
122                        format: ClaimFormatDesignation::AcVc,
123                        id: None,
124                        path_nested: None
125                    })
126                }]
127            },
128            json_example::<PresentationSubmission>("../oid4vp/tests/examples/response/ps_ac_vc_sd.json")
129        );
130
131        assert_eq!(
132            PresentationSubmission {
133                definition_id: "example_jwt_vc".to_string(),
134                id: "example_jwt_vc_presentation_submission".to_string(),
135                descriptor_map: vec![InputDescriptorMappingObject {
136                    id: "id_credential".to_string(),
137                    path: "$".to_string(),
138                    format: ClaimFormatDesignation::JwtVpJson,
139                    path_nested: Some(PathNested {
140                        path: "$.vp.verifiableCredential[0]".to_string(),
141                        format: ClaimFormatDesignation::JwtVcJson,
142                        id: None,
143                        path_nested: None
144                    })
145                }]
146            },
147            json_example::<PresentationSubmission>("../oid4vp/tests/examples/response/ps_jwt_vc.json")
148        );
149
150        assert_eq!(
151            PresentationSubmission {
152                definition_id: "example_ldp_vc".to_string(),
153                id: "example_ldp_vc_presentation_submission".to_string(),
154                descriptor_map: vec![InputDescriptorMappingObject {
155                    id: "id_credential".to_string(),
156                    path: "$".to_string(),
157                    format: ClaimFormatDesignation::LdpVp,
158                    path_nested: Some(PathNested {
159                        format: ClaimFormatDesignation::LdpVc,
160                        path: "$.verifiableCredential[0]".to_string(),
161                        id: None,
162                        path_nested: None
163                    })
164                }]
165            },
166            json_example::<PresentationSubmission>("../oid4vp/tests/examples/response/ps_ldp_vc.json")
167        );
168
169        assert_eq!(
170            PresentationSubmission {
171                definition_id: "mDL-sample-req".to_string(),
172                id: "mDL-sample-res".to_string(),
173                descriptor_map: vec![InputDescriptorMappingObject {
174                    id: "mDL".to_string(),
175                    path: "$".to_string(),
176                    format: ClaimFormatDesignation::MsoMdoc,
177                    path_nested: None
178                }]
179            },
180            json_example::<PresentationSubmission>("../oid4vp/tests/examples/response/ps_mdl_iso_cbor.json")
181        );
182    }
183}