Skip to main content

jwt/
jwk.rs

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/// a JSON Web Key
19/// https://www.rfc-editor.org/rfc/rfc7517
20/// https://www.rfc-editor.org/rfc/rfc8037
21/// https://www.ietf.org/archive/id/draft-ietf-jose-pqc-02.html
22/// Note: Jwk are not validated during deserialization
23#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct Jwk {
25    pub kid: SmallString<36>, // 36 = UUID length
26    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    /// EdDSA
38    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    /// ECDSA
47    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    /// Static keys
58    #[serde(rename = "oct")]
59    Oct {
60        #[serde(with = "base64_url_no_padding")]
61        key: SmallVec<u8, 32>,
62    },
63    /// RSA public key
64    #[serde(rename = "RSA")]
65    Rsa {
66        // Always heap-allocated. We use a `SmallVec` to avoid needing a separate implmentation for
67        // serde's `base64_url_no_padding`.
68        #[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    /// ML-DSA public key (draft "AKP" key type)
74    ///
75    /// `pub` holds the encoded public key and the optional `priv` holds the 32-byte seed the
76    /// signing key was generated from.
77    #[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// https://csrc.nist.gov/pubs/fips/186-5/final
99// https://csrc.nist.gov/pubs/sp/800/186/final
100// https://www.rfc-editor.org/rfc/rfc8032
101#[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    /// P-256 and SHA-256
126    #[serde(rename = "P-256")]
127    P256,
128
129    /// P-384 and SHA-384
130    #[serde(rename = "P-384")]
131    P384,
132
133    /// P-521 and SHA-512
134    #[serde(rename = "P-521")]
135    P521,
136}
137
138////////////////////////////////////////////////////////////////////////////////////////////////////
139// Secret key (BLAKE3 / HMAC)
140////////////////////////////////////////////////////////////////////////////////////////////////////
141
142impl 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
177////////////////////////////////////////////////////////////////////////////////////////////////////
178// Ed25519
179////////////////////////////////////////////////////////////////////////////////////////////////////
180
181impl 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
249////////////////////////////////////////////////////////////////////////////////////////////////////
250// P-256
251////////////////////////////////////////////////////////////////////////////////////////////////////
252
253impl 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
327////////////////////////////////////////////////////////////////////////////////////////////////////
328// P-384
329////////////////////////////////////////////////////////////////////////////////////////////////////
330
331impl 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
369////////////////////////////////////////////////////////////////////////////////////////////////////
370// P-521
371////////////////////////////////////////////////////////////////////////////////////////////////////
372
373impl 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
447////////////////////////////////////////////////////////////////////////////////////////////////////
448// RSA
449////////////////////////////////////////////////////////////////////////////////////////////////////
450
451impl 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
479////////////////////////////////////////////////////////////////////////////////////////////////////
480// ML-DSA-44
481////////////////////////////////////////////////////////////////////////////////////////////////////
482
483impl 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
549////////////////////////////////////////////////////////////////////////////////////////////////////
550// ML-DSA-65
551////////////////////////////////////////////////////////////////////////////////////////////////////
552
553impl 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
619////////////////////////////////////////////////////////////////////////////////////////////////////
620// ML-DSA-87
621////////////////////////////////////////////////////////////////////////////////////////////////////
622
623impl 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}