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