/rust/registry/src/index.crates.io-1949cf8c6b5b557f/jsonwebtoken-10.3.0/src/encoding.rs
Line | Count | Source |
1 | | use std::fmt::{Debug, Formatter}; |
2 | | |
3 | | use base64::{ |
4 | | Engine, |
5 | | engine::general_purpose::{STANDARD, URL_SAFE}, |
6 | | }; |
7 | | use serde::ser::Serialize; |
8 | | |
9 | | use crate::algorithms::AlgorithmFamily; |
10 | | use crate::crypto::CryptoProvider; |
11 | | use crate::errors::{ErrorKind, Result, new_error}; |
12 | | use crate::header::Header; |
13 | | #[cfg(feature = "use_pem")] |
14 | | use crate::pem::decoder::PemEncodedKey; |
15 | | use crate::serialization::{b64_encode, b64_encode_part}; |
16 | | |
17 | | /// A key to encode a JWT with. Can be a secret, a PEM-encoded key or a DER-encoded key. |
18 | | /// This key can be re-used so make sure you only initialize it once if you can for better performance. |
19 | | #[derive(Clone)] |
20 | | pub struct EncodingKey { |
21 | | family: AlgorithmFamily, |
22 | | content: Vec<u8>, |
23 | | } |
24 | | |
25 | | impl EncodingKey { |
26 | | /// The algorithm family this key is for. |
27 | 0 | pub fn family(&self) -> AlgorithmFamily { |
28 | 0 | self.family |
29 | 0 | } |
30 | | |
31 | | /// If you're using a HMAC secret that is not base64, use that. |
32 | 0 | pub fn from_secret(secret: &[u8]) -> Self { |
33 | 0 | EncodingKey { family: AlgorithmFamily::Hmac, content: secret.to_vec() } |
34 | 0 | } |
35 | | |
36 | | /// If you have a base64 HMAC secret, use that. |
37 | 0 | pub fn from_base64_secret(secret: &str) -> Result<Self> { |
38 | 0 | let out = STANDARD.decode(secret)?; |
39 | 0 | Ok(EncodingKey { family: AlgorithmFamily::Hmac, content: out }) |
40 | 0 | } |
41 | | |
42 | | /// For loading websafe base64 HMAC secrets, ex: ACME EAB credentials. |
43 | 0 | pub fn from_urlsafe_base64_secret(secret: &str) -> Result<Self> { |
44 | 0 | let out = URL_SAFE.decode(secret)?; |
45 | 0 | Ok(EncodingKey { family: AlgorithmFamily::Hmac, content: out }) |
46 | 0 | } |
47 | | |
48 | | /// If you are loading a RSA key from a .pem file. |
49 | | /// This errors if the key is not a valid RSA key. |
50 | | /// Only exists if the feature `use_pem` is enabled. |
51 | | /// |
52 | | /// # NOTE |
53 | | /// |
54 | | /// According to the [ring doc](https://docs.rs/ring/latest/ring/signature/struct.RsaKeyPair.html#method.from_pkcs8), |
55 | | /// the key should be at least 2047 bits. |
56 | | /// |
57 | | #[cfg(feature = "use_pem")] |
58 | 0 | pub fn from_rsa_pem(key: &[u8]) -> Result<Self> { |
59 | 0 | let pem_key = PemEncodedKey::new(key)?; |
60 | 0 | let content = pem_key.as_rsa_key()?; |
61 | 0 | Ok(EncodingKey { family: AlgorithmFamily::Rsa, content: content.to_vec() }) |
62 | 0 | } |
63 | | |
64 | | /// If you are loading a ECDSA key from a .pem file |
65 | | /// This errors if the key is not a valid private EC key |
66 | | /// Only exists if the feature `use_pem` is enabled. |
67 | | /// |
68 | | /// # NOTE |
69 | | /// |
70 | | /// The key should be in PKCS#8 form. |
71 | | /// |
72 | | /// You can generate a key with the following: |
73 | | /// |
74 | | /// ```sh |
75 | | /// openssl ecparam -genkey -noout -name prime256v1 \ |
76 | | /// | openssl pkcs8 -topk8 -nocrypt -out ec-private.pem |
77 | | /// ``` |
78 | | #[cfg(feature = "use_pem")] |
79 | 0 | pub fn from_ec_pem(key: &[u8]) -> Result<Self> { |
80 | 0 | let pem_key = PemEncodedKey::new(key)?; |
81 | 0 | let content = pem_key.as_ec_private_key()?; |
82 | 0 | Ok(EncodingKey { family: AlgorithmFamily::Ec, content: content.to_vec() }) |
83 | 0 | } |
84 | | |
85 | | /// If you are loading a EdDSA key from a .pem file |
86 | | /// This errors if the key is not a valid private Ed key |
87 | | /// Only exists if the feature `use_pem` is enabled. |
88 | | #[cfg(feature = "use_pem")] |
89 | 0 | pub fn from_ed_pem(key: &[u8]) -> Result<Self> { |
90 | 0 | let pem_key = PemEncodedKey::new(key)?; |
91 | 0 | let content = pem_key.as_ed_private_key()?; |
92 | 0 | Ok(EncodingKey { family: AlgorithmFamily::Ed, content: content.to_vec() }) |
93 | 0 | } |
94 | | |
95 | | /// If you know what you're doing and have the DER-encoded key, for RSA only |
96 | 0 | pub fn from_rsa_der(der: &[u8]) -> Self { |
97 | 0 | EncodingKey { family: AlgorithmFamily::Rsa, content: der.to_vec() } |
98 | 0 | } |
99 | | |
100 | | /// If you know what you're doing and have the DER-encoded key, for ECDSA |
101 | 0 | pub fn from_ec_der(der: &[u8]) -> Self { |
102 | 0 | EncodingKey { family: AlgorithmFamily::Ec, content: der.to_vec() } |
103 | 0 | } |
104 | | |
105 | | /// If you know what you're doing and have the DER-encoded key, for EdDSA |
106 | 0 | pub fn from_ed_der(der: &[u8]) -> Self { |
107 | 0 | EncodingKey { family: AlgorithmFamily::Ed, content: der.to_vec() } |
108 | 0 | } |
109 | | |
110 | | /// Get the value of the key. |
111 | 0 | pub fn inner(&self) -> &[u8] { |
112 | 0 | &self.content |
113 | 0 | } |
114 | | |
115 | | /// Try to get the HMAC secret from a key. |
116 | 0 | pub fn try_get_hmac_secret(&self) -> Result<&[u8]> { |
117 | 0 | if self.family == AlgorithmFamily::Hmac { |
118 | 0 | Ok(self.inner()) |
119 | | } else { |
120 | 0 | Err(new_error(ErrorKind::InvalidKeyFormat)) |
121 | | } |
122 | 0 | } |
123 | | } |
124 | | |
125 | | impl Debug for EncodingKey { |
126 | 0 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { |
127 | 0 | f.debug_struct("EncodingKey") |
128 | 0 | .field("family", &self.family) |
129 | 0 | .field("content", &"[redacted]") |
130 | 0 | .finish() |
131 | 0 | } |
132 | | } |
133 | | |
134 | | /// Encode the header and claims given and sign the payload using the algorithm from the header and the key. |
135 | | /// If the algorithm given is RSA or EC, the key needs to be in the PEM format. |
136 | | /// |
137 | | /// ```rust |
138 | | /// use serde::{Deserialize, Serialize}; |
139 | | /// use jsonwebtoken::{encode, Algorithm, Header, EncodingKey}; |
140 | | /// |
141 | | /// #[derive(Debug, Serialize, Deserialize)] |
142 | | /// struct Claims { |
143 | | /// sub: String, |
144 | | /// company: String |
145 | | /// } |
146 | | /// |
147 | | /// let my_claims = Claims { |
148 | | /// sub: "b@b.com".to_owned(), |
149 | | /// company: "ACME".to_owned() |
150 | | /// }; |
151 | | /// |
152 | | /// // my_claims is a struct that implements Serialize |
153 | | /// // This will create a JWT using HS256 as algorithm |
154 | | /// let token = encode(&Header::default(), &my_claims, &EncodingKey::from_secret("secret".as_ref())).unwrap(); |
155 | | /// ``` |
156 | 0 | pub fn encode<T: Serialize>(header: &Header, claims: &T, key: &EncodingKey) -> Result<String> { |
157 | 0 | if key.family != header.alg.family() { |
158 | 0 | return Err(new_error(ErrorKind::InvalidAlgorithm)); |
159 | 0 | } |
160 | | |
161 | 0 | let signing_provider = (CryptoProvider::get_default().signer_factory)(&header.alg, key)?; |
162 | | |
163 | 0 | if signing_provider.algorithm() != header.alg { |
164 | 0 | return Err(new_error(ErrorKind::InvalidAlgorithm)); |
165 | 0 | } |
166 | | |
167 | 0 | let encoded_header = b64_encode_part(&header)?; |
168 | 0 | let encoded_claims = b64_encode_part(claims)?; |
169 | 0 | let message = [encoded_header, encoded_claims].join("."); |
170 | | |
171 | 0 | let signature = b64_encode(signing_provider.try_sign(message.as_bytes())?); |
172 | | |
173 | 0 | Ok([message, signature].join(".")) |
174 | 0 | } Unexecuted instantiation: jsonwebtoken::encoding::encode::<surrealdb_core::iam::token::Claims> Unexecuted instantiation: jsonwebtoken::encoding::encode::<_> |