1use crypto::{
2 curve25519::ed25519,
3 mldsa::{
4 MlDsa44PublicKey, MlDsa44SecretKey, MlDsa65PublicKey, MlDsa65SecretKey, MlDsa87PublicKey, MlDsa87SecretKey,
5 },
6 p256, p384, p521,
7};
8use serde::{Deserialize, Serialize};
9use small_collections::{SmallString, SmallVec};
10
11use crate::{Algorithm, Error, RsaPublicKey, SecretKey};
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct Jwks {
15 pub keys: SmallVec<Jwk, 5>,
16}
17
18#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct Jwk {
25 pub kid: SmallString<36>, pub r#use: KeyUse,
27 #[serde(rename = "alg")]
28 pub algorithm: Algorithm,
29
30 #[serde(flatten)]
31 pub crypto: JwkCrypto,
32}
33
34#[derive(Debug, Clone, Serialize, Deserialize)]
35#[serde(rename_all = "UPPERCASE", tag = "kty")]
36pub enum JwkCrypto {
37 Okp {
39 #[serde(rename = "crv")]
40 curve: OkpCurve,
41 #[serde(with = "base64_url_no_padding")]
42 x: SmallVec<u8, 32>,
43 #[serde(with = "base64_url_no_padding::option", skip_serializing_if = "Option::is_none")]
44 d: Option<SmallVec<u8, 32>>,
45 },
46 Ec {
48 #[serde(rename = "crv")]
49 curve: EcCurve,
50 #[serde(with = "base64_url_no_padding")]
51 x: SmallVec<u8, 32>,
52 #[serde(with = "base64_url_no_padding")]
53 y: SmallVec<u8, 32>,
54 #[serde(with = "base64_url_no_padding::option", skip_serializing_if = "Option::is_none")]
55 d: Option<SmallVec<u8, 32>>,
56 },
57 #[serde(rename = "oct")]
59 Oct {
60 #[serde(with = "base64_url_no_padding")]
61 key: SmallVec<u8, 32>,
62 },
63 #[serde(rename = "RSA")]
65 Rsa {
66 #[serde(with = "base64_url_no_padding")]
69 n: SmallVec<u8, 0>,
70 #[serde(with = "base64_url_no_padding")]
71 e: SmallVec<u8, 4>,
72 },
73 #[serde(rename = "AKP")]
78 Akp {
79 #[serde(rename = "pub", with = "base64_url_no_padding")]
80 pub_key: SmallVec<u8, 0>,
81 #[serde(
82 rename = "priv",
83 with = "base64_url_no_padding::option",
84 skip_serializing_if = "Option::is_none"
85 )]
86 private_key: Option<SmallVec<u8, 0>>,
87 },
88}
89
90#[derive(Copy, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
91pub enum KeyUse {
92 #[serde(rename = "sig")]
93 Sign,
94 #[serde(rename = "enc")]
95 Encrypt,
96}
97
98#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
102pub enum OkpCurve {
103 Ed25519,
104}
105
106impl core::str::FromStr for OkpCurve {
107 type Err = Error;
108
109 fn from_str(s: &str) -> Result<Self, Self::Err> {
110 match s {
111 "Ed25519" => Ok(OkpCurve::Ed25519),
112 _ => Err(Error::InvalidCurve),
113 }
114 }
115}
116
117impl core::fmt::Display for OkpCurve {
118 fn fmt(&self, f: &mut alloc::fmt::Formatter<'_>) -> alloc::fmt::Result {
119 write!(f, "{self:?}")
120 }
121}
122
123#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
124pub enum EcCurve {
125 #[serde(rename = "P-256")]
127 P256,
128
129 #[serde(rename = "P-384")]
131 P384,
132
133 #[serde(rename = "P-521")]
135 P521,
136}
137
138impl From<&SecretKey<'_>> for Jwk {
143 #[inline]
144 fn from(key: &SecretKey<'_>) -> Self {
145 return Jwk {
146 kid: SmallString::new(),
147 r#use: KeyUse::Sign,
148 algorithm: key.algorithm,
149 crypto: JwkCrypto::Oct {
150 key: key.key.into(),
151 },
152 };
153 }
154}
155
156impl<'a> TryFrom<&'a Jwk> for SecretKey<'a> {
157 type Error = Error;
158
159 #[inline]
160 fn try_from(jwk: &'a Jwk) -> Result<Self, Self::Error> {
161 if !matches!(
162 jwk.algorithm,
163 Algorithm::BLAKE3 | Algorithm::HS256 | Algorithm::HS384 | Algorithm::HS512
164 ) {
165 return Err(Error::InvalidKey);
166 }
167
168 match &jwk.crypto {
169 JwkCrypto::Oct {
170 key,
171 } => Ok(SecretKey::new(jwk.algorithm, key.as_slice())),
172 _ => Err(Error::InvalidKey),
173 }
174 }
175}
176
177impl From<&ed25519::SecretKey> for Jwk {
182 #[inline]
183 fn from(key: &ed25519::SecretKey) -> Self {
184 return Jwk {
185 kid: SmallString::new(),
186 r#use: KeyUse::Sign,
187 algorithm: Algorithm::EdDSA,
188 crypto: JwkCrypto::Okp {
189 curve: OkpCurve::Ed25519,
190 x: key.public_key().to_bytes().into(),
191 d: Some(key.to_bytes().into()),
192 },
193 };
194 }
195}
196
197impl From<&ed25519::PublicKey> for Jwk {
198 #[inline]
199 fn from(key: &ed25519::PublicKey) -> Self {
200 return Jwk {
201 kid: SmallString::new(),
202 r#use: KeyUse::Sign,
203 algorithm: Algorithm::EdDSA,
204 crypto: JwkCrypto::Okp {
205 curve: OkpCurve::Ed25519,
206 x: key.to_bytes().into(),
207 d: None,
208 },
209 };
210 }
211}
212
213impl TryFrom<&Jwk> for ed25519::SecretKey {
214 type Error = Error;
215
216 fn try_from(jwk: &Jwk) -> Result<Self, Self::Error> {
217 match &jwk.crypto {
218 JwkCrypto::Okp {
219 curve: OkpCurve::Ed25519,
220 d: Some(d_bytes),
221 ..
222 } => {
223 let seed: [u8; 32] = d_bytes.as_slice().try_into().map_err(|_| Error::InvalidKey)?;
224 Ok(ed25519::SecretKey::from_bytes(&seed))
225 }
226 _ => Err(Error::InvalidKey),
227 }
228 }
229}
230
231impl TryFrom<&Jwk> for ed25519::PublicKey {
232 type Error = Error;
233
234 fn try_from(jwk: &Jwk) -> Result<Self, Self::Error> {
235 match &jwk.crypto {
236 JwkCrypto::Okp {
237 curve: OkpCurve::Ed25519,
238 x,
239 ..
240 } => {
241 let public_key: [u8; 32] = x.as_slice().try_into().map_err(|_| Error::InvalidKey)?;
242 ed25519::PublicKey::from_bytes(&public_key).map_err(|_| Error::InvalidKey)
243 }
244 _ => Err(Error::InvalidKey),
245 }
246 }
247}
248
249impl From<&p256::SecretKey> for Jwk {
254 #[inline]
255 fn from(key: &p256::SecretKey) -> Self {
256 let (x, y) = key.public_key().x_y();
257 return Jwk {
258 kid: SmallString::new(),
259 r#use: KeyUse::Sign,
260 algorithm: Algorithm::ES256,
261 crypto: JwkCrypto::Ec {
262 curve: EcCurve::P256,
263 x: x.into(),
264 y: y.into(),
265 d: Some(key.to_bytes().into()),
266 },
267 };
268 }
269}
270
271impl From<&p256::PublicKey> for Jwk {
272 #[inline]
273 fn from(key: &p256::PublicKey) -> Self {
274 let (x, y) = key.x_y();
275 return Jwk {
276 kid: SmallString::new(),
277 r#use: KeyUse::Sign,
278 algorithm: Algorithm::ES256,
279 crypto: JwkCrypto::Ec {
280 curve: EcCurve::P256,
281 x: x.into(),
282 y: y.into(),
283 d: None,
284 },
285 };
286 }
287}
288
289impl TryFrom<&Jwk> for p256::SecretKey {
290 type Error = Error;
291
292 fn try_from(jwk: &Jwk) -> Result<Self, Self::Error> {
293 match &jwk.crypto {
294 JwkCrypto::Ec {
295 curve: EcCurve::P256,
296 d: Some(d_bytes),
297 ..
298 } => {
299 let key: [u8; 32] = d_bytes.as_slice().try_into().map_err(|_| Error::InvalidKey)?;
300 p256::SecretKey::from_bytes(&key).map_err(|_| Error::InvalidKey)
301 }
302 _ => Err(Error::InvalidKey),
303 }
304 }
305}
306
307impl TryFrom<&Jwk> for p256::PublicKey {
308 type Error = Error;
309
310 fn try_from(jwk: &Jwk) -> Result<Self, Self::Error> {
311 match &jwk.crypto {
312 JwkCrypto::Ec {
313 curve: EcCurve::P256,
314 x,
315 y,
316 ..
317 } => {
318 let x: [u8; 32] = x.as_slice().try_into().map_err(|_| Error::InvalidKey)?;
319 let y: [u8; 32] = y.as_slice().try_into().map_err(|_| Error::InvalidKey)?;
320 p256::PublicKey::from_x_y(&x, &y).map_err(|_| Error::InvalidKey)
321 }
322 _ => Err(Error::InvalidKey),
323 }
324 }
325}
326
327impl From<&p384::PublicKey> for Jwk {
332 #[inline]
333 fn from(key: &p384::PublicKey) -> Self {
334 let (x, y) = key.x_y();
335 return Jwk {
336 kid: SmallString::new(),
337 r#use: KeyUse::Sign,
338 algorithm: Algorithm::ES384,
339 crypto: JwkCrypto::Ec {
340 curve: EcCurve::P384,
341 x: x.into(),
342 y: y.into(),
343 d: None,
344 },
345 };
346 }
347}
348
349impl TryFrom<&Jwk> for p384::PublicKey {
350 type Error = Error;
351
352 fn try_from(jwk: &Jwk) -> Result<Self, Self::Error> {
353 match &jwk.crypto {
354 JwkCrypto::Ec {
355 curve: EcCurve::P384,
356 x,
357 y,
358 ..
359 } => {
360 let x: [u8; 48] = x.as_slice().try_into().map_err(|_| Error::InvalidKey)?;
361 let y: [u8; 48] = y.as_slice().try_into().map_err(|_| Error::InvalidKey)?;
362 p384::PublicKey::from_x_y(&x, &y).map_err(|_| Error::InvalidKey)
363 }
364 _ => Err(Error::InvalidKey),
365 }
366 }
367}
368
369impl From<&p521::SecretKey> for Jwk {
374 #[inline]
375 fn from(key: &p521::SecretKey) -> Self {
376 let (x, y) = key.public_key().x_y();
377 return Jwk {
378 kid: SmallString::new(),
379 r#use: KeyUse::Sign,
380 algorithm: Algorithm::ES512,
381 crypto: JwkCrypto::Ec {
382 curve: EcCurve::P521,
383 x: x.into(),
384 y: y.into(),
385 d: Some(key.to_bytes().into()),
386 },
387 };
388 }
389}
390
391impl From<&p521::PublicKey> for Jwk {
392 #[inline]
393 fn from(key: &p521::PublicKey) -> Self {
394 let (x, y) = key.x_y();
395 return Jwk {
396 kid: SmallString::new(),
397 r#use: KeyUse::Sign,
398 algorithm: Algorithm::ES512,
399 crypto: JwkCrypto::Ec {
400 curve: EcCurve::P521,
401 x: x.into(),
402 y: y.into(),
403 d: None,
404 },
405 };
406 }
407}
408
409impl TryFrom<&Jwk> for p521::SecretKey {
410 type Error = Error;
411
412 fn try_from(jwk: &Jwk) -> Result<Self, Self::Error> {
413 match &jwk.crypto {
414 JwkCrypto::Ec {
415 curve: EcCurve::P521,
416 d: Some(d_bytes),
417 ..
418 } => {
419 let key: [u8; 66] = d_bytes.as_slice().try_into().map_err(|_| Error::InvalidKey)?;
420 p521::SecretKey::from_bytes(&key).map_err(|_| Error::InvalidKey)
421 }
422 _ => Err(Error::InvalidKey),
423 }
424 }
425}
426
427impl TryFrom<&Jwk> for p521::PublicKey {
428 type Error = Error;
429
430 fn try_from(jwk: &Jwk) -> Result<Self, Self::Error> {
431 match &jwk.crypto {
432 JwkCrypto::Ec {
433 curve: EcCurve::P521,
434 x,
435 y,
436 ..
437 } => {
438 let x: [u8; 66] = x.as_slice().try_into().map_err(|_| Error::InvalidKey)?;
439 let y: [u8; 66] = y.as_slice().try_into().map_err(|_| Error::InvalidKey)?;
440 p521::PublicKey::from_x_y(&x, &y).map_err(|_| Error::InvalidKey)
441 }
442 _ => Err(Error::InvalidKey),
443 }
444 }
445}
446
447impl From<&RsaPublicKey> for Jwk {
452 fn from(key: &RsaPublicKey) -> Self {
453 Jwk {
454 kid: SmallString::new(),
455 r#use: KeyUse::Sign,
456 algorithm: key.alg,
457 crypto: JwkCrypto::Rsa {
458 n: key.key.n_bytes().into(),
459 e: key.key.e_bytes().into(),
460 },
461 }
462 }
463}
464
465impl TryFrom<&Jwk> for RsaPublicKey {
466 type Error = Error;
467
468 fn try_from(jwk: &Jwk) -> Result<Self, Self::Error> {
469 match &jwk.crypto {
470 JwkCrypto::Rsa {
471 n,
472 e,
473 } => RsaPublicKey::from_n_e(jwk.algorithm, n, e),
474 _ => Err(Error::InvalidKey),
475 }
476 }
477}
478
479impl From<&MlDsa44PublicKey> for Jwk {
484 fn from(key: &MlDsa44PublicKey) -> Self {
485 Jwk {
486 kid: SmallString::new(),
487 r#use: KeyUse::Sign,
488 algorithm: Algorithm::MlDsa44,
489 crypto: JwkCrypto::Akp {
490 pub_key: key.to_bytes().into(),
491 private_key: None,
492 },
493 }
494 }
495}
496
497impl From<&MlDsa44SecretKey> for Jwk {
498 fn from(key: &MlDsa44SecretKey) -> Self {
499 Jwk {
500 kid: SmallString::new(),
501 r#use: KeyUse::Sign,
502 algorithm: Algorithm::MlDsa44,
503 crypto: JwkCrypto::Akp {
504 pub_key: key.public_key().to_bytes().into(),
505 private_key: Some(key.seed().as_slice().into()),
506 },
507 }
508 }
509}
510
511impl TryFrom<&Jwk> for MlDsa44PublicKey {
512 type Error = Error;
513
514 fn try_from(jwk: &Jwk) -> Result<Self, Self::Error> {
515 if jwk.algorithm != Algorithm::MlDsa44 {
516 return Err(Error::InvalidKey);
517 }
518
519 match &jwk.crypto {
520 JwkCrypto::Akp {
521 pub_key, ..
522 } => MlDsa44PublicKey::try_from(pub_key.as_slice()).map_err(|_| Error::InvalidKey),
523 _ => Err(Error::InvalidKey),
524 }
525 }
526}
527
528impl TryFrom<&Jwk> for MlDsa44SecretKey {
529 type Error = Error;
530
531 fn try_from(jwk: &Jwk) -> Result<Self, Self::Error> {
532 if jwk.algorithm != Algorithm::MlDsa44 {
533 return Err(Error::InvalidKey);
534 }
535
536 match &jwk.crypto {
537 JwkCrypto::Akp {
538 private_key: Some(seed),
539 ..
540 } => {
541 let seed: [u8; 32] = seed.as_slice().try_into().map_err(|_| Error::InvalidKey)?;
542 Ok(MlDsa44SecretKey::new(&seed))
543 }
544 _ => Err(Error::InvalidKey),
545 }
546 }
547}
548
549impl From<&MlDsa65PublicKey> for Jwk {
554 fn from(key: &MlDsa65PublicKey) -> Self {
555 Jwk {
556 kid: SmallString::new(),
557 r#use: KeyUse::Sign,
558 algorithm: Algorithm::MlDsa65,
559 crypto: JwkCrypto::Akp {
560 pub_key: key.to_bytes().into(),
561 private_key: None,
562 },
563 }
564 }
565}
566
567impl From<&MlDsa65SecretKey> for Jwk {
568 fn from(key: &MlDsa65SecretKey) -> Self {
569 Jwk {
570 kid: SmallString::new(),
571 r#use: KeyUse::Sign,
572 algorithm: Algorithm::MlDsa65,
573 crypto: JwkCrypto::Akp {
574 pub_key: key.public_key().to_bytes().into(),
575 private_key: Some(key.seed().as_slice().into()),
576 },
577 }
578 }
579}
580
581impl TryFrom<&Jwk> for MlDsa65PublicKey {
582 type Error = Error;
583
584 fn try_from(jwk: &Jwk) -> Result<Self, Self::Error> {
585 if jwk.algorithm != Algorithm::MlDsa65 {
586 return Err(Error::InvalidKey);
587 }
588
589 match &jwk.crypto {
590 JwkCrypto::Akp {
591 pub_key, ..
592 } => MlDsa65PublicKey::try_from(pub_key.as_slice()).map_err(|_| Error::InvalidKey),
593 _ => Err(Error::InvalidKey),
594 }
595 }
596}
597
598impl TryFrom<&Jwk> for MlDsa65SecretKey {
599 type Error = Error;
600
601 fn try_from(jwk: &Jwk) -> Result<Self, Self::Error> {
602 if jwk.algorithm != Algorithm::MlDsa65 {
603 return Err(Error::InvalidKey);
604 }
605
606 match &jwk.crypto {
607 JwkCrypto::Akp {
608 private_key: Some(seed),
609 ..
610 } => {
611 let seed: [u8; 32] = seed.as_slice().try_into().map_err(|_| Error::InvalidKey)?;
612 Ok(MlDsa65SecretKey::new(&seed))
613 }
614 _ => Err(Error::InvalidKey),
615 }
616 }
617}
618
619impl From<&MlDsa87PublicKey> for Jwk {
624 fn from(key: &MlDsa87PublicKey) -> Self {
625 Jwk {
626 kid: SmallString::new(),
627 r#use: KeyUse::Sign,
628 algorithm: Algorithm::MlDsa87,
629 crypto: JwkCrypto::Akp {
630 pub_key: key.to_bytes().into(),
631 private_key: None,
632 },
633 }
634 }
635}
636
637impl From<&MlDsa87SecretKey> for Jwk {
638 fn from(key: &MlDsa87SecretKey) -> Self {
639 Jwk {
640 kid: SmallString::new(),
641 r#use: KeyUse::Sign,
642 algorithm: Algorithm::MlDsa87,
643 crypto: JwkCrypto::Akp {
644 pub_key: key.public_key().to_bytes().into(),
645 private_key: Some(key.seed().as_slice().into()),
646 },
647 }
648 }
649}
650
651impl TryFrom<&Jwk> for MlDsa87PublicKey {
652 type Error = Error;
653
654 fn try_from(jwk: &Jwk) -> Result<Self, Self::Error> {
655 if jwk.algorithm != Algorithm::MlDsa87 {
656 return Err(Error::InvalidKey);
657 }
658
659 match &jwk.crypto {
660 JwkCrypto::Akp {
661 pub_key, ..
662 } => MlDsa87PublicKey::try_from(pub_key.as_slice()).map_err(|_| Error::InvalidKey),
663 _ => Err(Error::InvalidKey),
664 }
665 }
666}
667
668impl TryFrom<&Jwk> for MlDsa87SecretKey {
669 type Error = Error;
670
671 fn try_from(jwk: &Jwk) -> Result<Self, Self::Error> {
672 if jwk.algorithm != Algorithm::MlDsa87 {
673 return Err(Error::InvalidKey);
674 }
675
676 match &jwk.crypto {
677 JwkCrypto::Akp {
678 private_key: Some(seed),
679 ..
680 } => {
681 let seed: [u8; 32] = seed.as_slice().try_into().map_err(|_| Error::InvalidKey)?;
682 Ok(MlDsa87SecretKey::new(&seed))
683 }
684 _ => Err(Error::InvalidKey),
685 }
686 }
687}
688
689mod base64_url_no_padding {
690 use base64::{Alphabet, decode, encode};
691 use serde::{Deserializer, Serializer};
692
693 use super::*;
694
695 pub fn serialize<S: Serializer, const N: usize>(data: &SmallVec<u8, N>, serializer: S) -> Result<S::Ok, S::Error> {
696 serializer.serialize_str(&encode(data, Alphabet::UrlNoPadding))
697 }
698
699 pub fn deserialize<'de, D: Deserializer<'de>, const N: usize>(
700 deserializer: D,
701 ) -> Result<SmallVec<u8, N>, D::Error> {
702 let s = <&str>::deserialize(deserializer)?;
703 let bytes = decode(s.as_bytes(), Alphabet::UrlNoPadding).map_err(serde::de::Error::custom)?;
704 Ok(SmallVec::from(bytes))
705 }
706
707 pub(crate) mod option {
708 use alloc::string::String;
709
710 use super::*;
711
712 pub fn serialize<S: Serializer, const N: usize>(
713 data: &Option<SmallVec<u8, N>>,
714 serializer: S,
715 ) -> Result<S::Ok, S::Error> {
716 match data {
717 Some(val) => serializer.serialize_str(&encode(val, Alphabet::UrlNoPadding)),
718 None => serializer.serialize_none(),
719 }
720 }
721
722 pub fn deserialize<'de, D: Deserializer<'de>, const N: usize>(
723 deserializer: D,
724 ) -> Result<Option<SmallVec<u8, N>>, D::Error> {
725 let opt: Option<String> = Option::deserialize(deserializer)?;
726 match opt {
727 Some(s) => {
728 let bytes = decode(s.as_bytes(), Alphabet::UrlNoPadding).map_err(serde::de::Error::custom)?;
729 Ok(Some(SmallVec::from(bytes)))
730 }
731 None => Ok(None),
732 }
733 }
734 }
735}