1use affinidi_encoding::{ED25519_PUB, MultiEncoded, P256_PUB, P384_PUB, SECP256K1_PUB, X25519_PUB};
4use serde::{Deserialize, Serialize};
5use zeroize::{Zeroize, ZeroizeOnDrop};
6
7use crate::{CryptoError, KeyType};
8
9#[derive(Debug, Serialize, Deserialize, Clone, Zeroize, ZeroizeOnDrop)]
11pub struct JWK {
12 #[serde(rename = "kid")]
13 #[serde(skip_serializing_if = "Option::is_none")]
14 pub key_id: Option<String>,
15 #[serde(flatten)]
16 pub params: Params,
17}
18
19impl JWK {
20 pub fn key_type(&self) -> KeyType {
22 match &self.params {
23 Params::EC(params) => match params.curve.as_str() {
24 "P-256" => KeyType::P256,
25 "secp256k1" => KeyType::Secp256k1,
26 "P-384" => KeyType::P384,
27 _ => KeyType::Unknown,
28 },
29 Params::OKP(params) => match params.curve.as_str() {
30 "Ed25519" => KeyType::Ed25519,
31 "X25519" => KeyType::X25519,
32 _ => KeyType::Unknown,
33 },
34 }
35 }
36
37 pub fn from_multikey(key: &str) -> Result<Self, CryptoError> {
39 let (_, data) = multibase::decode(key)
41 .map_err(|e| CryptoError::Decoding(format!("Failed to decode multibase: {e}")))?;
42
43 let decoded = MultiEncoded::new(&data)?;
45
46 match decoded.codec() {
47 #[cfg(feature = "p256")]
48 P256_PUB => crate::p256::public_jwk(decoded.data()),
49 #[cfg(feature = "p384")]
50 P384_PUB => crate::p384::public_jwk(decoded.data()),
51 #[cfg(feature = "k256")]
52 SECP256K1_PUB => crate::secp256k1::public_jwk(decoded.data()),
53 #[cfg(feature = "ed25519")]
54 ED25519_PUB => crate::ed25519::public_jwk(decoded.data()),
55 #[cfg(feature = "ed25519")]
56 X25519_PUB => crate::ed25519::x25519_public_jwk(decoded.data()),
57 codec => Err(CryptoError::UnsupportedKeyType(format!(
58 "Unsupported key type codec (0x{codec:x})"
59 ))),
60 }
61 }
62}
63
64#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Zeroize, ZeroizeOnDrop)]
66#[serde(tag = "kty")]
67pub enum Params {
68 EC(ECParams),
69 OKP(OctectParams),
70}
71
72#[derive(Debug, Serialize, Deserialize, Clone, Zeroize, PartialEq, ZeroizeOnDrop)]
74pub struct ECParams {
75 #[serde(rename = "crv")]
76 pub curve: String,
77 pub x: String,
78 pub y: String,
79 pub d: Option<String>,
80}
81
82#[derive(Debug, Serialize, Deserialize, Clone, Zeroize, PartialEq, ZeroizeOnDrop)]
84pub struct OctectParams {
85 #[serde(rename = "crv")]
86 pub curve: String,
87 pub x: String,
88 pub d: Option<String>,
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94
95 #[test]
96 fn deserialize_okp_jwk() {
97 let raw = r#"{
98 "crv": "Ed25519",
99 "d": "jybTAuX6NlN7cJLWNCSOLUnJpblpsGr05TTp7scjSvE",
100 "kty": "OKP",
101 "x": "Xx4_L89E6RsyvDTzN9wuN3cDwgifPkXMgFJv_HMIxdk"
102 }"#;
103
104 let jwk: JWK = serde_json::from_str(raw).expect("Couldn't deserialize JWK");
105
106 assert_eq!(
107 jwk.params,
108 Params::OKP(OctectParams {
109 curve: "Ed25519".to_string(),
110 x: "Xx4_L89E6RsyvDTzN9wuN3cDwgifPkXMgFJv_HMIxdk".to_string(),
111 d: Some("jybTAuX6NlN7cJLWNCSOLUnJpblpsGr05TTp7scjSvE".to_string())
112 })
113 );
114 }
115
116 #[test]
117 fn deserialize_ec_jwk() {
118 let raw = r#"{
119 "crv": "P-256",
120 "d": "kQrTUKhBU-6bHbCdiY0dIfg3knd5U2-1FlLGGHSbF6U",
121 "kty": "EC",
122 "x": "sl56LMzaiR5efwwWU1jzC_dfbxQ8gzyLj_N1q2cJmkE",
123 "y": "UnAimUtlHMPj_T_wIDVPoJAolKHy8DoXXTb8wch4hgU"
124 }"#;
125
126 let jwk: JWK = serde_json::from_str(raw).expect("Couldn't deserialize JWK");
127
128 assert_eq!(
129 jwk.params,
130 Params::EC(ECParams {
131 curve: "P-256".to_string(),
132 x: "sl56LMzaiR5efwwWU1jzC_dfbxQ8gzyLj_N1q2cJmkE".to_string(),
133 y: "UnAimUtlHMPj_T_wIDVPoJAolKHy8DoXXTb8wch4hgU".to_string(),
134 d: Some("kQrTUKhBU-6bHbCdiY0dIfg3knd5U2-1FlLGGHSbF6U".to_string())
135 })
136 );
137 }
138
139 #[test]
140 fn from_multikey_secp256k1() {
141 assert!(JWK::from_multikey("zQ3shT2ynSjzY5XoTxhWHvYVZ6GiLWhBVincVekcEpZDRCBHV").is_ok());
142 }
143
144 #[test]
145 fn from_multikey_p256() {
146 assert!(JWK::from_multikey("zDnaerDaTF5BXEavCrfRZEk316dpbLsfPDZ3WJ5hRTPFU2169").is_ok());
147 }
148
149 #[test]
150 fn from_multikey_p384() {
151 assert!(
152 JWK::from_multikey(
153 "z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9"
154 )
155 .is_ok()
156 );
157 }
158
159 #[test]
160 fn from_multikey_ed25519() {
161 assert!(JWK::from_multikey("z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp").is_ok());
162 }
163}