1use mas_iana::jose::{
9 JsonWebKeyEcEllipticCurve, JsonWebKeyOkpEllipticCurve, JsonWebKeyType, JsonWebSignatureAlg,
10};
11use schemars::JsonSchema;
12use serde::{Deserialize, Serialize};
13
14use super::ParametersInfo;
15use crate::{base64::Base64UrlNoPad, jwk::Thumbprint};
16
17#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
18#[serde(tag = "kty")]
19pub enum JsonWebKeyPublicParameters {
20 #[serde(rename = "RSA")]
21 Rsa(RsaPublicParameters),
22
23 #[serde(rename = "EC")]
24 Ec(EcPublicParameters),
25
26 #[serde(rename = "OKP")]
27 Okp(OkpPublicParameters),
28}
29
30impl JsonWebKeyPublicParameters {
31 #[must_use]
32 pub const fn rsa(&self) -> Option<&RsaPublicParameters> {
33 match self {
34 Self::Rsa(params) => Some(params),
35 _ => None,
36 }
37 }
38
39 #[must_use]
40 pub const fn ec(&self) -> Option<&EcPublicParameters> {
41 match self {
42 Self::Ec(params) => Some(params),
43 _ => None,
44 }
45 }
46
47 #[must_use]
48 pub const fn okp(&self) -> Option<&OkpPublicParameters> {
49 match self {
50 Self::Okp(params) => Some(params),
51 _ => None,
52 }
53 }
54}
55
56impl Thumbprint for JsonWebKeyPublicParameters {
57 fn thumbprint_prehashed(&self) -> String {
58 match self {
59 JsonWebKeyPublicParameters::Rsa(RsaPublicParameters { n, e }) => {
60 format!("{{\"e\":\"{e}\",\"kty\":\"RSA\",\"n\":\"{n}\"}}")
61 }
62 JsonWebKeyPublicParameters::Ec(EcPublicParameters { crv, x, y }) => {
63 format!("{{\"crv\":\"{crv}\",\"kty\":\"EC\",\"x\":\"{x}\",\"y\":\"{y}\"}}")
64 }
65 JsonWebKeyPublicParameters::Okp(OkpPublicParameters { crv, x }) => {
66 format!("{{\"crv\":\"{crv}\",\"kty\":\"OKP\",\"x\":\"{x}\"}}")
67 }
68 }
69 }
70}
71
72impl ParametersInfo for JsonWebKeyPublicParameters {
73 fn kty(&self) -> JsonWebKeyType {
74 match self {
75 Self::Rsa(_) => JsonWebKeyType::Rsa,
76 Self::Ec(_) => JsonWebKeyType::Ec,
77 Self::Okp(_) => JsonWebKeyType::Okp,
78 }
79 }
80
81 fn possible_algs(&self) -> &[JsonWebSignatureAlg] {
82 match self {
83 JsonWebKeyPublicParameters::Rsa(p) => p.possible_algs(),
84 JsonWebKeyPublicParameters::Ec(p) => p.possible_algs(),
85 JsonWebKeyPublicParameters::Okp(p) => p.possible_algs(),
86 }
87 }
88}
89
90#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
91pub struct RsaPublicParameters {
92 #[schemars(with = "String")]
93 n: Base64UrlNoPad,
94
95 #[schemars(with = "String")]
96 e: Base64UrlNoPad,
97}
98
99impl ParametersInfo for RsaPublicParameters {
100 fn kty(&self) -> JsonWebKeyType {
101 JsonWebKeyType::Rsa
102 }
103
104 fn possible_algs(&self) -> &[JsonWebSignatureAlg] {
105 &[
106 JsonWebSignatureAlg::Rs256,
107 JsonWebSignatureAlg::Rs384,
108 JsonWebSignatureAlg::Rs512,
109 JsonWebSignatureAlg::Ps256,
110 JsonWebSignatureAlg::Ps384,
111 JsonWebSignatureAlg::Ps512,
112 ]
113 }
114}
115
116impl RsaPublicParameters {
117 pub const fn new(n: Base64UrlNoPad, e: Base64UrlNoPad) -> Self {
118 Self { n, e }
119 }
120}
121
122#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
123pub struct EcPublicParameters {
124 pub(crate) crv: JsonWebKeyEcEllipticCurve,
125
126 #[schemars(with = "String")]
127 x: Base64UrlNoPad,
128
129 #[schemars(with = "String")]
130 y: Base64UrlNoPad,
131}
132
133impl EcPublicParameters {
134 pub const fn new(crv: JsonWebKeyEcEllipticCurve, x: Base64UrlNoPad, y: Base64UrlNoPad) -> Self {
135 Self { crv, x, y }
136 }
137}
138
139impl ParametersInfo for EcPublicParameters {
140 fn kty(&self) -> JsonWebKeyType {
141 JsonWebKeyType::Ec
142 }
143
144 fn possible_algs(&self) -> &[JsonWebSignatureAlg] {
145 match &self.crv {
146 JsonWebKeyEcEllipticCurve::P256 => &[JsonWebSignatureAlg::Es256],
147 JsonWebKeyEcEllipticCurve::P384 => &[JsonWebSignatureAlg::Es384],
148 JsonWebKeyEcEllipticCurve::P521 => &[JsonWebSignatureAlg::Es512],
149 JsonWebKeyEcEllipticCurve::Secp256K1 => &[JsonWebSignatureAlg::Es256K],
150 _ => &[],
151 }
152 }
153}
154
155#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
156pub struct OkpPublicParameters {
157 crv: JsonWebKeyOkpEllipticCurve,
158
159 #[schemars(with = "String")]
160 x: Base64UrlNoPad,
161}
162
163impl ParametersInfo for OkpPublicParameters {
164 fn kty(&self) -> JsonWebKeyType {
165 JsonWebKeyType::Okp
166 }
167
168 fn possible_algs(&self) -> &[JsonWebSignatureAlg] {
169 &[JsonWebSignatureAlg::EdDsa]
170 }
171}
172
173impl OkpPublicParameters {
174 pub const fn new(crv: JsonWebKeyOkpEllipticCurve, x: Base64UrlNoPad) -> Self {
175 Self { crv, x }
176 }
177}
178
179mod rsa_impls {
180 use rsa::{BoxedUint, RsaPublicKey, traits::PublicKeyParts};
181
182 use super::{JsonWebKeyPublicParameters, RsaPublicParameters};
183 use crate::base64::Base64UrlNoPad;
184
185 impl From<RsaPublicKey> for JsonWebKeyPublicParameters {
186 fn from(key: RsaPublicKey) -> Self {
187 Self::from(&key)
188 }
189 }
190
191 impl From<&RsaPublicKey> for JsonWebKeyPublicParameters {
192 fn from(key: &RsaPublicKey) -> Self {
193 Self::Rsa(key.into())
194 }
195 }
196
197 impl From<RsaPublicKey> for RsaPublicParameters {
198 fn from(key: RsaPublicKey) -> Self {
199 Self::from(&key)
200 }
201 }
202
203 impl From<&RsaPublicKey> for RsaPublicParameters {
204 fn from(key: &RsaPublicKey) -> Self {
205 Self {
206 n: Base64UrlNoPad::new(key.n().to_be_bytes_trimmed_vartime().into_vec()),
207 e: Base64UrlNoPad::new(key.e().to_be_bytes_trimmed_vartime().into_vec()),
208 }
209 }
210 }
211
212 impl TryFrom<RsaPublicParameters> for RsaPublicKey {
213 type Error = rsa::errors::Error;
214 fn try_from(value: RsaPublicParameters) -> Result<Self, Self::Error> {
215 (&value).try_into()
216 }
217 }
218
219 impl TryFrom<&RsaPublicParameters> for RsaPublicKey {
220 type Error = rsa::errors::Error;
221 fn try_from(value: &RsaPublicParameters) -> Result<Self, Self::Error> {
222 let n = BoxedUint::from_be_slice_vartime(value.n.as_bytes());
223 let e = BoxedUint::from_be_slice_vartime(value.e.as_bytes());
224 let key = RsaPublicKey::new(n, e)?;
225 Ok(key)
226 }
227 }
228}
229
230mod ec_impls {
231 use digest::typenum::Unsigned;
232 use elliptic_curve::{
233 AffinePoint, FieldBytes, PublicKey,
234 sec1::{Coordinates, FromSec1Point, ModulusSize, Sec1Point, ToSec1Point},
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>: FromSec1Point<C> + ToSec1Point<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>::try_from(x).map_err(|_| elliptic_curve::Error)?;
260 let y = FieldBytes::<C>::try_from(y).map_err(|_| elliptic_curve::Error)?;
261 let pubkey = Sec1Point::<C>::from_affine_coordinates(&x, &y, false);
262 let pubkey: Option<_> = PublicKey::from_sec1_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>: FromSec1Point<C> + ToSec1Point<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>: FromSec1Point<C> + ToSec1Point<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>: FromSec1Point<C> + ToSec1Point<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>: FromSec1Point<C> + ToSec1Point<C>,
304 C::FieldBytesSize: ModulusSize,
305 {
306 fn from(key: &PublicKey<C>) -> Self {
307 let point = key.to_sec1_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}