credential_exchange_protocol/
lib.rs

1#![doc = include_str!("../README.md")]
2
3use credential_exchange_format::B64Url;
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Serialize, Deserialize)]
7#[serde(rename_all = "camelCase")]
8pub struct ExportRequest {
9    pub version: Version,
10    pub hpke: Vec<HpkeParameters>,
11    pub importer: String,
12    #[serde(default, skip_serializing_if = "Option::is_none")]
13    pub credential_types: Option<Vec<CredentialType>>,
14    #[serde(default, skip_serializing_if = "Option::is_none")]
15    pub known_extensions: Option<Vec<KnownExtension>>,
16}
17
18#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
19#[serde(from = "u8", into = "u8")]
20pub enum Version {
21    V0,
22    Unknown(u8),
23}
24
25impl From<u8> for Version {
26    fn from(value: u8) -> Self {
27        match value {
28            0 => Version::V0,
29            v => Version::Unknown(v),
30        }
31    }
32}
33
34impl From<Version> for u8 {
35    fn from(value: Version) -> Self {
36        match value {
37            Version::V0 => 0,
38            Version::Unknown(v) => v,
39        }
40    }
41}
42
43#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
44#[serde(rename_all = "kebab-case")]
45pub enum CredentialType {
46    BasicAuth,
47    Passkey,
48    Totp,
49    Note,
50    File,
51    Address,
52    CreditCard,
53    DriverLicense,
54    ItemReference,
55    IdentityDocument,
56    Passport,
57    PersonName,
58    SshKey,
59    ApiKey,
60    #[serde(untagged)]
61    Unknown(String),
62}
63
64#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
65#[serde(rename_all = "kebab-case")]
66pub enum KnownExtension {
67    Shared,
68    #[serde(untagged)]
69    Unknown(String),
70}
71
72#[derive(Clone, Debug, Serialize, Deserialize)]
73#[serde(rename_all = "camelCase")]
74pub struct HpkeParameters {
75    pub mode: HpkeMode,
76    pub kem: HpkeKem,
77    pub kdf: HpkeKdf,
78    pub aead: HpkeAead,
79    pub key: Option<jose_jwk::Jwk>,
80}
81
82impl PartialEq for HpkeParameters {
83    fn eq(&self, other: &Self) -> bool {
84        self.mode == other.mode
85            && self.kem == other.kem
86            && self.kdf == other.kdf
87            && self.aead == other.aead
88        // Explicitly ignoring the key option as it should be ephemeral
89        // and the important bits are the parameters themselves
90    }
91}
92
93#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
94#[serde(rename_all = "kebab-case")]
95pub enum HpkeMode {
96    Base,
97    Psk,
98    Auth,
99    AuthPsk,
100    #[serde(untagged)]
101    Unknown(String),
102}
103
104#[derive(Debug, Serialize, Deserialize)]
105#[serde(rename_all = "camelCase")]
106pub struct ExportResponse {
107    pub version: Version,
108    pub hpke: HpkeParameters,
109    pub exporter: String,
110    pub payload: B64Url,
111}
112
113#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
114#[serde(from = "u16", into = "u16")]
115pub enum HpkeKem {
116    Reserved,
117    DhP256,
118    DhP384,
119    DhP521,
120    DhCP256,
121    DhCP384,
122    DhCP521,
123    DhSecP256K1,
124    DhX25519,
125    DhX448,
126    X25519Kyber768Draft00,
127    Unasssigned(u16),
128}
129
130impl From<HpkeKem> for u16 {
131    fn from(value: HpkeKem) -> Self {
132        match value {
133            HpkeKem::Reserved => 0x0000,
134            HpkeKem::DhP256 => 0x0010,
135            HpkeKem::DhP384 => 0x0011,
136            HpkeKem::DhP521 => 0x0012,
137            HpkeKem::DhCP256 => 0x0013,
138            HpkeKem::DhCP384 => 0x0014,
139            HpkeKem::DhCP521 => 0x0015,
140            HpkeKem::DhSecP256K1 => 0x0016,
141            HpkeKem::DhX25519 => 0x0020,
142            HpkeKem::DhX448 => 0x0021,
143            HpkeKem::X25519Kyber768Draft00 => 0x0030,
144            HpkeKem::Unasssigned(u) => u,
145        }
146    }
147}
148
149impl From<u16> for HpkeKem {
150    fn from(value: u16) -> Self {
151        match value {
152            0x0000 => HpkeKem::Reserved,
153            u @ 0x0001..=0x000F => HpkeKem::Unasssigned(u),
154            0x0010 => HpkeKem::DhP256,
155            0x0011 => HpkeKem::DhP384,
156            0x0012 => HpkeKem::DhP521,
157            0x0013 => HpkeKem::DhCP256,
158            0x0014 => HpkeKem::DhCP384,
159            0x0015 => HpkeKem::DhCP521,
160            0x0016 => HpkeKem::DhSecP256K1,
161            u @ 0x0017..=0x001F => HpkeKem::Unasssigned(u),
162            0x0020 => HpkeKem::DhX25519,
163            0x0021 => HpkeKem::DhX448,
164            u @ 0x0022..=0x002F => HpkeKem::Unasssigned(u),
165            0x0030 => HpkeKem::X25519Kyber768Draft00,
166            u @ 0x0031..=0xFFFF => HpkeKem::Unasssigned(u),
167        }
168    }
169}
170#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
171#[serde(from = "u16", into = "u16")]
172pub enum HpkeKdf {
173    Reserved,
174    HkdfSha256,
175    HkdfSha384,
176    HkdfSha512,
177    Unassigned(u16),
178}
179
180impl From<HpkeKdf> for u16 {
181    fn from(value: HpkeKdf) -> Self {
182        match value {
183            HpkeKdf::Reserved => 0x0000,
184            HpkeKdf::HkdfSha256 => 0x0001,
185            HpkeKdf::HkdfSha384 => 0x0002,
186            HpkeKdf::HkdfSha512 => 0x0003,
187            HpkeKdf::Unassigned(u) => u,
188        }
189    }
190}
191
192impl From<u16> for HpkeKdf {
193    fn from(value: u16) -> HpkeKdf {
194        match value {
195            0x0000 => HpkeKdf::Reserved,
196            0x0001 => HpkeKdf::HkdfSha256,
197            0x0002 => HpkeKdf::HkdfSha384,
198            0x0003 => HpkeKdf::HkdfSha512,
199            u @ 0x0004..=0xFFFF => HpkeKdf::Unassigned(u),
200        }
201    }
202}
203
204#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
205#[serde(from = "u16", into = "u16")]
206pub enum HpkeAead {
207    Reserved,
208    Aes128Gcm,
209    Aes256Gcm,
210    ChaCha20Poly1305,
211    Unassigned(u16),
212    ExportOnly,
213}
214
215impl From<HpkeAead> for u16 {
216    fn from(value: HpkeAead) -> Self {
217        match value {
218            HpkeAead::Reserved => 0x0000,
219            HpkeAead::Aes128Gcm => 0x0001,
220            HpkeAead::Aes256Gcm => 0x0002,
221            HpkeAead::ChaCha20Poly1305 => 0x0003,
222            HpkeAead::Unassigned(u) => u,
223            HpkeAead::ExportOnly => 0xFFFF,
224        }
225    }
226}
227
228impl From<u16> for HpkeAead {
229    fn from(value: u16) -> HpkeAead {
230        match value {
231            0x0000 => HpkeAead::Reserved,
232            0x0001 => HpkeAead::Aes128Gcm,
233            0x0002 => HpkeAead::Aes256Gcm,
234            0x0003 => HpkeAead::ChaCha20Poly1305,
235            u @ 0x0004..=0xFFFE => HpkeAead::Unassigned(u),
236            0xFFFF => HpkeAead::ExportOnly,
237        }
238    }
239}
240
241pub struct ErrorResponse {
242    pub version: Version,
243    pub error: ErrorCode,
244}
245
246#[derive(Debug)]
247pub enum ErrorCode {
248    /// Indicates that a user confirmation action was refused, thus cancelling the exchange.
249    UserCanceled,
250    /// The exporting provider does not support any of the requested [HpkeParameters].
251    IncompatibleHpkeParameters,
252    /// The importing provider did not provide a key when it was required by the associated
253    /// [HpkeParameters].
254    MissingImporterKey,
255    /// The importing provider provided an invalid key for the associated [HpkeParameters].
256    IncorrectImporterKeyEncoding,
257    /// The exporting provider does not support the requested credential exchange protocol version.
258    UnsupportedVersion,
259    /// An error occurred while parsing the JSON [ExportRequest].
260    InvalidJson,
261    /// The exporting provider refused the export due to either policy or inability to validate the
262    /// exporting provider.
263    ForbiddenAction,
264}