1use mas_iana::jose::{
8 JsonWebKeyEcEllipticCurve, JsonWebKeyOkpEllipticCurve, JsonWebKeyType, JsonWebSignatureAlg,
9};
10use schemars::JsonSchema;
11use serde::{Deserialize, Serialize};
12
13use super::ParametersInfo;
14use crate::{base64::Base64UrlNoPad, jwk::Thumbprint};
15
16#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
17#[serde(tag = "kty")]
18pub enum JsonWebKeyPublicParameters {
19 #[serde(rename = "RSA")]
20 Rsa(RsaPublicParameters),
21
22 #[serde(rename = "EC")]
23 Ec(EcPublicParameters),
24
25 #[serde(rename = "OKP")]
26 Okp(OkpPublicParameters),
27}
28
29impl JsonWebKeyPublicParameters {
30 #[must_use]
31 pub const fn rsa(&self) -> Option<&RsaPublicParameters> {
32 match self {
33 Self::Rsa(params) => Some(params),
34 _ => None,
35 }
36 }
37
38 #[must_use]
39 pub const fn ec(&self) -> Option<&EcPublicParameters> {
40 match self {
41 Self::Ec(params) => Some(params),
42 _ => None,
43 }
44 }
45
46 #[must_use]
47 pub const fn okp(&self) -> Option<&OkpPublicParameters> {
48 match self {
49 Self::Okp(params) => Some(params),
50 _ => None,
51 }
52 }
53}
54
55impl Thumbprint for JsonWebKeyPublicParameters {
56 fn thumbprint_prehashed(&self) -> String {
57 match self {
58 JsonWebKeyPublicParameters::Rsa(RsaPublicParameters { n, e }) => {
59 format!("{{\"e\":\"{e}\",\"kty\":\"RSA\",\"n\":\"{n}\"}}")
60 }
61 JsonWebKeyPublicParameters::Ec(EcPublicParameters { crv, x, y }) => {
62 format!("{{\"crv\":\"{crv}\",\"kty\":\"EC\",\"x\":\"{x}\",\"y\":\"{y}\"}}")
63 }
64 JsonWebKeyPublicParameters::Okp(OkpPublicParameters { crv, x }) => {
65 format!("{{\"crv\":\"{crv}\",\"kty\":\"OKP\",\"x\":\"{x}\"}}")
66 }
67 }
68 }
69}
70
71impl ParametersInfo for JsonWebKeyPublicParameters {
72 fn kty(&self) -> JsonWebKeyType {
73 match self {
74 Self::Rsa(_) => JsonWebKeyType::Rsa,
75 Self::Ec(_) => JsonWebKeyType::Ec,
76 Self::Okp(_) => JsonWebKeyType::Okp,
77 }
78 }
79
80 fn possible_algs(&self) -> &[JsonWebSignatureAlg] {
81 match self {
82 JsonWebKeyPublicParameters::Rsa(p) => p.possible_algs(),
83 JsonWebKeyPublicParameters::Ec(p) => p.possible_algs(),
84 JsonWebKeyPublicParameters::Okp(p) => p.possible_algs(),
85 }
86 }
87}
88
89#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
90pub struct RsaPublicParameters {
91 #[schemars(with = "String")]
92 n: Base64UrlNoPad,
93
94 #[schemars(with = "String")]
95 e: Base64UrlNoPad,
96}
97
98impl ParametersInfo for RsaPublicParameters {
99 fn kty(&self) -> JsonWebKeyType {
100 JsonWebKeyType::Rsa
101 }
102
103 fn possible_algs(&self) -> &[JsonWebSignatureAlg] {
104 &[
105 JsonWebSignatureAlg::Rs256,
106 JsonWebSignatureAlg::Rs384,
107 JsonWebSignatureAlg::Rs512,
108 JsonWebSignatureAlg::Ps256,
109 JsonWebSignatureAlg::Ps384,
110 JsonWebSignatureAlg::Ps512,
111 ]
112 }
113}
114
115impl RsaPublicParameters {
116 pub const fn new(n: Base64UrlNoPad, e: Base64UrlNoPad) -> Self {
117 Self { n, e }
118 }
119}
120
121#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
122pub struct EcPublicParameters {
123 pub(crate) crv: JsonWebKeyEcEllipticCurve,
124
125 #[schemars(with = "String")]
126 x: Base64UrlNoPad,
127
128 #[schemars(with = "String")]
129 y: Base64UrlNoPad,
130}
131
132impl EcPublicParameters {
133 pub const fn new(crv: JsonWebKeyEcEllipticCurve, x: Base64UrlNoPad, y: Base64UrlNoPad) -> Self {
134 Self { crv, x, y }
135 }
136}
137
138impl ParametersInfo for EcPublicParameters {
139 fn kty(&self) -> JsonWebKeyType {
140 JsonWebKeyType::Ec
141 }
142
143 fn possible_algs(&self) -> &[JsonWebSignatureAlg] {
144 match &self.crv {
145 JsonWebKeyEcEllipticCurve::P256 => &[JsonWebSignatureAlg::Es256],
146 JsonWebKeyEcEllipticCurve::P384 => &[JsonWebSignatureAlg::Es384],
147 JsonWebKeyEcEllipticCurve::P521 => &[JsonWebSignatureAlg::Es512],
148 JsonWebKeyEcEllipticCurve::Secp256K1 => &[JsonWebSignatureAlg::Es256K],
149 _ => &[],
150 }
151 }
152}
153
154#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
155pub struct OkpPublicParameters {
156 crv: JsonWebKeyOkpEllipticCurve,
157
158 #[schemars(with = "String")]
159 x: Base64UrlNoPad,
160}
161
162impl ParametersInfo for OkpPublicParameters {
163 fn kty(&self) -> JsonWebKeyType {
164 JsonWebKeyType::Okp
165 }
166
167 fn possible_algs(&self) -> &[JsonWebSignatureAlg] {
168 &[JsonWebSignatureAlg::EdDsa]
169 }
170}
171
172impl OkpPublicParameters {
173 pub const fn new(crv: JsonWebKeyOkpEllipticCurve, x: Base64UrlNoPad) -> Self {
174 Self { crv, x }
175 }
176}
177
178mod rsa_impls {
179 use rsa::{BigUint, RsaPublicKey, traits::PublicKeyParts};
180
181 use super::{JsonWebKeyPublicParameters, RsaPublicParameters};
182 use crate::base64::Base64UrlNoPad;
183
184 impl From<RsaPublicKey> for JsonWebKeyPublicParameters {
185 fn from(key: RsaPublicKey) -> Self {
186 Self::from(&key)
187 }
188 }
189
190 impl From<&RsaPublicKey> for JsonWebKeyPublicParameters {
191 fn from(key: &RsaPublicKey) -> Self {
192 Self::Rsa(key.into())
193 }
194 }
195
196 impl From<RsaPublicKey> for RsaPublicParameters {
197 fn from(key: RsaPublicKey) -> Self {
198 Self::from(&key)
199 }
200 }
201
202 impl From<&RsaPublicKey> for RsaPublicParameters {
203 fn from(key: &RsaPublicKey) -> Self {
204 Self {
205 n: Base64UrlNoPad::new(key.n().to_bytes_be()),
206 e: Base64UrlNoPad::new(key.e().to_bytes_be()),
207 }
208 }
209 }
210
211 impl TryFrom<RsaPublicParameters> for RsaPublicKey {
212 type Error = rsa::errors::Error;
213 fn try_from(value: RsaPublicParameters) -> Result<Self, Self::Error> {
214 (&value).try_into()
215 }
216 }
217
218 impl TryFrom<&RsaPublicParameters> for RsaPublicKey {
219 type Error = rsa::errors::Error;
220 fn try_from(value: &RsaPublicParameters) -> Result<Self, Self::Error> {
221 let n = BigUint::from_bytes_be(value.n.as_bytes());
222 let e = BigUint::from_bytes_be(value.e.as_bytes());
223 let key = RsaPublicKey::new(n, e)?;
224 Ok(key)
225 }
226 }
227}
228
229mod ec_impls {
230 use digest::typenum::Unsigned;
231 use ecdsa::EncodedPoint;
232 use elliptic_curve::{
233 AffinePoint, FieldBytes, PublicKey,
234 sec1::{Coordinates, FromEncodedPoint, ModulusSize, ToEncodedPoint},
235 };
236
237 use super::{super::JwkEcCurve, EcPublicParameters, JsonWebKeyPublicParameters};
238 use crate::base64::Base64UrlNoPad;
239
240 impl<C> TryFrom<&EcPublicParameters> for PublicKey<C>
241 where
242 C: elliptic_curve::CurveArithmetic,
243 AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
244 C::FieldBytesSize: ModulusSize + Unsigned,
245 {
246 type Error = elliptic_curve::Error;
247 fn try_from(value: &EcPublicParameters) -> Result<Self, Self::Error> {
248 let x = value
249 .x
250 .as_bytes()
251 .get(..C::FieldBytesSize::USIZE)
252 .ok_or(elliptic_curve::Error)?;
253 let y = value
254 .y
255 .as_bytes()
256 .get(..C::FieldBytesSize::USIZE)
257 .ok_or(elliptic_curve::Error)?;
258
259 let x = FieldBytes::<C>::from_slice(x);
260 let y = FieldBytes::<C>::from_slice(y);
261 let pubkey = EncodedPoint::<C>::from_affine_coordinates(x, y, false);
262 let pubkey: Option<_> = PublicKey::from_encoded_point(&pubkey).into();
263 pubkey.ok_or(elliptic_curve::Error)
264 }
265 }
266
267 impl<C> From<PublicKey<C>> for JsonWebKeyPublicParameters
268 where
269 C: elliptic_curve::CurveArithmetic + JwkEcCurve,
270 AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
271 C::FieldBytesSize: ModulusSize,
272 {
273 fn from(key: PublicKey<C>) -> Self {
274 (&key).into()
275 }
276 }
277
278 impl<C> From<&PublicKey<C>> for JsonWebKeyPublicParameters
279 where
280 C: elliptic_curve::CurveArithmetic + JwkEcCurve,
281 AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
282 C::FieldBytesSize: ModulusSize,
283 {
284 fn from(key: &PublicKey<C>) -> Self {
285 Self::Ec(key.into())
286 }
287 }
288
289 impl<C> From<PublicKey<C>> for EcPublicParameters
290 where
291 C: elliptic_curve::CurveArithmetic + JwkEcCurve,
292 AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
293 C::FieldBytesSize: ModulusSize,
294 {
295 fn from(key: PublicKey<C>) -> Self {
296 (&key).into()
297 }
298 }
299
300 impl<C> From<&PublicKey<C>> for EcPublicParameters
301 where
302 C: elliptic_curve::CurveArithmetic + JwkEcCurve,
303 AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
304 C::FieldBytesSize: ModulusSize,
305 {
306 fn from(key: &PublicKey<C>) -> Self {
307 let point = key.to_encoded_point(false);
308 let Coordinates::Uncompressed { x, y } = point.coordinates() else {
309 unreachable!()
310 };
311 EcPublicParameters {
312 crv: C::CRV,
313 x: Base64UrlNoPad::new(x.to_vec()),
314 y: Base64UrlNoPad::new(y.to_vec()),
315 }
316 }
317 }
318}
319
320#[cfg(test)]
321mod tests {
322 use super::*;
323
324 #[test]
325 fn test_thumbprint_rfc_example() {
326 let n = Base64UrlNoPad::parse(
328 "\
329 0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAt\
330 VT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn6\
331 4tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FD\
332 W2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n9\
333 1CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINH\
334 aQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw",
335 )
336 .unwrap();
337 let e = Base64UrlNoPad::parse("AQAB").unwrap();
338
339 let jwkpps = JsonWebKeyPublicParameters::Rsa(RsaPublicParameters { n, e });
340
341 assert_eq!(
342 jwkpps.thumbprint_sha256_base64(),
343 "NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs"
344 );
345 }
346}