Add DecodingKey

This commit is contained in:
Vincent Prouillet 2019-12-29 21:50:06 +01:00
parent 0abeeac25f
commit 77ae0effc8
14 changed files with 235 additions and 185 deletions

View File

@ -1,6 +1,6 @@
[package]
name = "jsonwebtoken"
version = "7.0.0-alpha.2"
version = "7.0.0-alpha.3"
authors = ["Vincent Prouillet <hello@vincentprouillet.com>"]
license = "MIT"
readme = "README.md"

View File

@ -38,7 +38,7 @@ Complete examples are available in the examples directory: a basic one and one w
In terms of imports and structs:
```rust
use serde::{Serialize, Deserialize};
use jsonwebtoken::{encode, decode, Header, Algorithm, Validation, EncodingKey};
use jsonwebtoken::{encode, decode, Header, Algorithm, Validation, EncodingKey, DecodingKey};
/// Our claims struct, it needs to derive `Serialize` and/or `Deserialize`
#[derive(Debug, Serialize, Deserialize)]
@ -73,7 +73,7 @@ Look at `examples/custom_header.rs` for a full working example.
// HS256
let token = encode(&Header::default(), &my_claims, &EncodingKey::from_secret("secret".as_ref()))?;
// RSA
let token = encode(&Header::new(Algorithm::RS256), &my_claims, include_str!("privkey.pem"))?;
let token = encode(&Header::new(Algorithm::RS256), &my_claims, &EncodingKey::from_rsa_pem(include_bytes!("privkey.pem"))?)?;
```
Encoding a JWT takes 3 parameters:
@ -82,13 +82,13 @@ Encoding a JWT takes 3 parameters:
- a key/secret
When using HS256, HS2384 or HS512, the key is always a shared secret like in the example above. When using
RSA/EC, the key should always be the content of the private key in the PEM format.
RSA/EC, the key should always be the content of the private key in the PEM or DER format.
### Decoding
```rust
// `token` is a struct with 2 fields: `header` and `claims` where `claims` is your own struct.
let token = decode::<Claims>(&token, "secret".as_ref(), &Validation::default())?;
let token = decode::<Claims>(&token, &DecodingKey::from_secret("secret".as_ref()), &Validation::default())?;
```
`decode` can error for a variety of reasons:
@ -97,7 +97,7 @@ let token = decode::<Claims>(&token, "secret".as_ref(), &Validation::default())?
- validation of at least one reserved claim failed
As with encoding, when using HS256, HS2384 or HS512, the key is always a shared secret like in the example above. When using
RSA/EC, the key should always be the content of the public key in the PEM format.
RSA/EC, the key should always be the content of the public key in the PEM or DER format.
In some cases, for example if you don't know the algorithm used or need to grab the `kid`, you can choose to decode only the header:
@ -121,15 +121,7 @@ The main use-case is for JWK where your public key is in a JSON format like so:
```rust
// `token` is a struct with 2 fields: `header` and `claims` where `claims` is your own struct.
let token = decode_rsa_components::<Claims>(&token, jwk["n"], jwk["e"], &Validation::new(Algorithm::RS256))?;
```
### Converting .der to .pem
You can use openssl for that:
```bash
openssl rsa -inform DER -outform PEM -in mykey.der -out mykey.pem
let token = decode::<Claims>(&token, &EncodingKey::from_rsa_components(jwk["n"], jwk["e"]), &Validation::new(Algorithm::RS256))?;
```
### Convert SEC1 private key to PKCS8
@ -145,7 +137,7 @@ openssl pkcs8 -topk8 -nocrypt -in sec1.pem -out pkcs8.pem
This library validates automatically the `exp` claim and `nbf` is validated if present. You can also validate the `sub`, `iss` and `aud` but
those require setting the expected value in the `Validation` struct.
Since validating time fields is always a bit tricky due to clock skew,
Since validating time fields is always a bit tricky due to clock skew,
you can add some leeway to the `iat`, `exp` and `nbf` validation by setting the `leeway` field.
Last but not least, you will need to set the algorithm(s) allowed for this token if you are not using `HS256`.

View File

@ -1,7 +1,7 @@
#![feature(test)]
extern crate test;
use jsonwebtoken::{decode, encode, Header, Validation};
use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, Validation};
use serde::{Deserialize, Serialize};
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
@ -13,12 +13,15 @@ struct Claims {
#[bench]
fn bench_encode(b: &mut test::Bencher) {
let claim = Claims { sub: "b@b.com".to_owned(), company: "ACME".to_owned() };
let key = EncodingKey::from_secret("secret".as_ref());
b.iter(|| encode(&Header::default(), &claim, "secret".as_ref()));
b.iter(|| encode(&Header::default(), &claim, &key));
}
#[bench]
fn bench_decode(b: &mut test::Bencher) {
let token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ";
b.iter(|| decode::<Claims>(token, "secret".as_ref(), &Validation::default()));
let key = DecodingKey::from_secret("secret".as_ref());
b.iter(|| decode::<Claims>(token, &key, &Validation::default()));
}

View File

@ -1,5 +1,5 @@
use chrono::prelude::*;
use jsonwebtoken::{EncodingKey, Header, Validation};
use jsonwebtoken::{DecodingKey, EncodingKey, Header, Validation};
use serde::{Deserialize, Serialize};
const SECRET: &str = "some-secret";
@ -57,8 +57,12 @@ mod jwt_numeric_date {
assert_eq!(&token, EXPECTED_TOKEN);
let decoded = decode::<Claims>(&token, SECRET.as_ref(), &Validation::default())
.expect("Failed to decode token");
let decoded = decode::<Claims>(
&token,
&DecodingKey::from_secret(SECRET.as_ref()),
&Validation::default(),
)
.expect("Failed to decode token");
assert_eq!(decoded.claims, claims);
}
@ -91,8 +95,11 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("serialized token: {}", &token);
let token_data =
jsonwebtoken::decode::<Claims>(&token, SECRET.as_ref(), &Validation::default())?;
let token_data = jsonwebtoken::decode::<Claims>(
&token,
&DecodingKey::from_secret(SECRET.as_ref()),
&Validation::default(),
)?;
println!("token data:\n{:#?}", &token_data);
Ok(())

View File

@ -1,7 +1,7 @@
use serde::{Deserialize, Serialize};
use jsonwebtoken::errors::ErrorKind;
use jsonwebtoken::{decode, encode, Algorithm, EncodingKey, Header, Validation};
use jsonwebtoken::{decode, encode, Algorithm, DecodingKey, EncodingKey, Header, Validation};
#[derive(Debug, Serialize, Deserialize)]
struct Claims {
@ -25,7 +25,11 @@ fn main() {
};
println!("{:?}", token);
let token_data = match decode::<Claims>(&token, key, &Validation::new(Algorithm::HS512)) {
let token_data = match decode::<Claims>(
&token,
&DecodingKey::from_secret(key),
&Validation::new(Algorithm::HS512),
) {
Ok(c) => c,
Err(err) => match *err.kind() {
ErrorKind::InvalidToken => panic!(), // Example on how to handle a specific error

View File

@ -1,5 +1,5 @@
use jsonwebtoken::errors::ErrorKind;
use jsonwebtoken::{decode, encode, EncodingKey, Header, Validation};
use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, Validation};
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
@ -19,7 +19,7 @@ fn main() {
};
let validation = Validation { sub: Some("b@b.com".to_string()), ..Validation::default() };
let token_data = match decode::<Claims>(&token, key, &validation) {
let token_data = match decode::<Claims>(&token, &DecodingKey::from_secret(key), &validation) {
Ok(c) => c,
Err(err) => match *err.kind() {
ErrorKind::InvalidToken => panic!("Token is invalid"), // Example on how to handle a specific error

View File

@ -2,9 +2,9 @@ use ring::constant_time::verify_slices_are_equal;
use ring::{hmac, signature};
use crate::algorithms::Algorithm;
use crate::decoding::{DecodingKey, DecodingKeyKind};
use crate::encoding::EncodingKey;
use crate::errors::Result;
use crate::pem::decoder::PemEncodedKey;
use crate::serialization::{b64_decode, b64_encode};
pub(crate) mod ecdsa;
@ -62,57 +62,37 @@ fn verify_ring(
/// `signature` is the signature part of a jwt (text after the second '.')
///
/// `message` is base64(header) + "." + base64(claims)
/// For ECDSA/RSA, the `key` is the pem public key. If you want to verify using the public key
/// components (modulus/exponent), use `verify_rsa_components` instead.
pub fn verify(signature: &str, message: &str, key: &[u8], algorithm: Algorithm) -> Result<bool> {
pub fn verify(
signature: &str,
message: &str,
key: &DecodingKey,
algorithm: Algorithm,
) -> Result<bool> {
match algorithm {
Algorithm::HS256 | Algorithm::HS384 | Algorithm::HS512 => {
// we just re-sign the message with the key and compare if they are equal
let signed = sign(message, &EncodingKey::from_secret(key), algorithm)?;
let signed = sign(message, &EncodingKey::from_secret(key.as_bytes()), algorithm)?;
Ok(verify_slices_are_equal(signature.as_ref(), signed.as_ref()).is_ok())
}
Algorithm::ES256 | Algorithm::ES384 => {
let pem_key = PemEncodedKey::new(key)?;
verify_ring(
ecdsa::alg_to_ec_verification(algorithm),
signature,
message,
pem_key.as_ec_public_key()?,
)
}
Algorithm::ES256 | Algorithm::ES384 => verify_ring(
ecdsa::alg_to_ec_verification(algorithm),
signature,
message,
key.as_bytes(),
),
Algorithm::RS256
| Algorithm::RS384
| Algorithm::RS512
| Algorithm::PS256
| Algorithm::PS384
| Algorithm::PS512 => {
let pem_key = PemEncodedKey::new(key)?;
verify_ring(
rsa::alg_to_rsa_parameters(algorithm),
signature,
message,
pem_key.as_rsa_key()?,
)
let alg = rsa::alg_to_rsa_parameters(algorithm);
match &key.kind {
DecodingKeyKind::SecretOrDer(bytes) => verify_ring(alg, signature, message, bytes),
DecodingKeyKind::RsaModulusExponent { n, e } => {
rsa::verify_from_components(alg, signature, message, (n, e))
}
}
}
}
}
/// Verify the signature given using the (n, e) components of a RSA public key.
///
/// `signature` is the signature part of a jwt (text after the second '.')
///
/// `message` is base64(header) + "." + base64(claims)
pub fn verify_rsa_components(
signature: &str,
message: &str,
components: (&str, &str),
alg: Algorithm,
) -> Result<bool> {
let signature_bytes = b64_decode(signature)?;
rsa::verify_from_components(
rsa::alg_to_rsa_parameters(alg),
&signature_bytes,
message,
components,
)
}

View File

@ -50,12 +50,14 @@ pub(crate) fn sign(
Ok(b64_encode(&signature))
}
/// Checks that a signature is valid based on the (n, e) RSA pubkey components
pub(crate) fn verify_from_components(
alg: &'static signature::RsaParameters,
signature_bytes: &[u8],
signature: &str,
message: &str,
components: (&str, &str),
) -> Result<bool> {
let signature_bytes = b64_decode(signature)?;
let n = BigUint::from_bytes_be(&b64_decode(components.0)?).to_bytes_be();
let e = BigUint::from_bytes_be(&b64_decode(components.1)?).to_bytes_be();
let pubkey = signature::RsaPublicKeyComponents { n, e };

View File

@ -1,8 +1,11 @@
use std::borrow::Cow;
use serde::de::DeserializeOwned;
use crate::crypto::{verify, verify_rsa_components};
use crate::crypto::verify;
use crate::errors::{new_error, ErrorKind, Result};
use crate::header::Header;
use crate::pem::decoder::PemEncodedKey;
use crate::serialization::from_jwt_part_claims;
use crate::validation::{validate, Validation};
@ -27,15 +30,78 @@ macro_rules! expect_two {
}};
}
/// Internal way to differentiate between public key types
enum DecodingKey<'a> {
SecretOrPem(&'a [u8]),
#[derive(Debug, Clone, PartialEq)]
pub(crate) enum DecodingKeyKind<'a> {
SecretOrDer(Cow<'a, [u8]>),
RsaModulusExponent { n: &'a str, e: &'a str },
}
fn _decode<T: DeserializeOwned>(
/// All the different kind of keys we can use to decode a JWT
/// This key can be re-used so make sure you only initialize it once if you can for better performance
#[derive(Debug, Clone, PartialEq)]
pub struct DecodingKey<'a> {
pub(crate) kind: DecodingKeyKind<'a>,
}
impl<'a> DecodingKey<'a> {
/// If you're using HMAC, use this.
pub fn from_secret(secret: &'a [u8]) -> Self {
DecodingKey { kind: DecodingKeyKind::SecretOrDer(Cow::Borrowed(secret)) }
}
/// If you are loading a public RSA key in a PEM format, use this.
pub fn from_rsa_pem(key: &'a [u8]) -> Result<Self> {
let pem_key = PemEncodedKey::new(key)?;
let content = pem_key.as_rsa_key()?;
Ok(DecodingKey { kind: DecodingKeyKind::SecretOrDer(Cow::Owned(content.to_vec())) })
}
/// If you have (n, e) RSA public key components, use this.
pub fn from_rsa_components(modulus: &'a str, exponent: &'a str) -> Self {
DecodingKey { kind: DecodingKeyKind::RsaModulusExponent { n: modulus, e: exponent } }
}
/// If you have a ECDSA public key in PEM format, use this.
pub fn from_ec_pem(key: &'a [u8]) -> Result<Self> {
let pem_key = PemEncodedKey::new(key)?;
let content = pem_key.as_ec_public_key()?;
Ok(DecodingKey { kind: DecodingKeyKind::SecretOrDer(Cow::Owned(content.to_vec())) })
}
/// If you know what you're doing and have the DER encoded public key, use this.
pub fn from_der(der: &'a [u8]) -> Self {
DecodingKey { kind: DecodingKeyKind::SecretOrDer(Cow::Borrowed(der)) }
}
pub(crate) fn as_bytes(&self) -> &[u8] {
match &self.kind {
DecodingKeyKind::SecretOrDer(b) => &b,
DecodingKeyKind::RsaModulusExponent { .. } => unreachable!(),
}
}
}
/// Decode and validate a JWT
///
/// If the token or its signature is invalid or the claims fail validation, it will return an error.
///
/// ```rust
/// use serde::{Deserialize, Serialize};
/// use jsonwebtoken::{decode, DecodingKey, Validation, Algorithm};
///
/// #[derive(Debug, Serialize, Deserialize)]
/// struct Claims {
/// sub: String,
/// company: String
/// }
///
/// let token = "a.jwt.token".to_string();
/// // Claims is a struct that implements Deserialize
/// let token_message = decode::<Claims>(&token, &DecodingKey::from_secret("secret".as_ref()), &Validation::new(Algorithm::HS256));
/// ```
pub fn decode<T: DeserializeOwned>(
token: &str,
key: DecodingKey,
key: &DecodingKey,
validation: &Validation,
) -> Result<TokenData<T>> {
let (signature, message) = expect_two!(token.rsplitn(2, '.'));
@ -46,14 +112,7 @@ fn _decode<T: DeserializeOwned>(
return Err(new_error(ErrorKind::InvalidAlgorithm));
}
let is_valid = match key {
DecodingKey::SecretOrPem(k) => verify(signature, message, k, header.alg),
DecodingKey::RsaModulusExponent { n, e } => {
verify_rsa_components(signature, message, (n, e), header.alg)
}
}?;
if !is_valid {
if !verify(signature, message, key, header.alg)? {
return Err(new_error(ErrorKind::InvalidSignature));
}
@ -63,61 +122,6 @@ fn _decode<T: DeserializeOwned>(
Ok(TokenData { header, claims: decoded_claims })
}
/// Decode and validate a JWT using a secret for HS and a public PEM format for RSA/EC
///
/// If the token or its signature is invalid or the claims fail validation, it will return an error.
///
/// ```rust
/// use serde::{Deserialize, Serialize};
/// use jsonwebtoken::{decode, Validation, Algorithm};
///
/// #[derive(Debug, Serialize, Deserialize)]
/// struct Claims {
/// sub: String,
/// company: String
/// }
///
/// let token = "a.jwt.token".to_string();
/// // Claims is a struct that implements Deserialize
/// let token_message = decode::<Claims>(&token, "secret".as_ref(), &Validation::new(Algorithm::HS256));
/// ```
pub fn decode<T: DeserializeOwned>(
token: &str,
key: &[u8],
validation: &Validation,
) -> Result<TokenData<T>> {
_decode(token, DecodingKey::SecretOrPem(key), validation)
}
/// Decode and validate a JWT using (n, e) base64 encoded public key components for RSA
///
/// If the token or its signature is invalid or the claims fail validation, it will return an error.
///
/// ```rust
/// use serde::{Deserialize, Serialize};
/// use jsonwebtoken::{decode_rsa_components, Validation, Algorithm};
///
/// #[derive(Debug, Serialize, Deserialize)]
/// struct Claims {
/// sub: String,
/// company: String
/// }
///
/// let modulus = "some-base64-data";
/// let exponent = "some-base64-data";
/// let token = "a.jwt.token".to_string();
/// // Claims is a struct that implements Deserialize
/// let token_message = decode_rsa_components::<Claims>(&token, &modulus, &exponent, &Validation::new(Algorithm::HS256));
/// ```
pub fn decode_rsa_components<T: DeserializeOwned>(
token: &str,
modulus: &str,
exponent: &str,
validation: &Validation,
) -> Result<TokenData<T>> {
_decode(token, DecodingKey::RsaModulusExponent { n: modulus, e: exponent }, validation)
}
/// Decode a JWT without any signature verification/validations.
///
/// NOTE: Do not use this unless you know what you are doing! If the token's signature is invalid, it will *not* return an error.

View File

@ -9,6 +9,7 @@ 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(Debug, Clone, PartialEq)]
pub struct EncodingKey<'a> {
content: Cow<'a, [u8]>,
@ -20,8 +21,8 @@ impl<'a> EncodingKey<'a> {
EncodingKey { content: Cow::Borrowed(secret) }
}
/// If you are loading a RSA key from a .pem file
/// This errors if the key is not a valid RSA key
/// If you are loading a RSA key from a .pem file.
/// This errors if the key is not a valid RSA key.
pub fn from_rsa_pem(key: &'a [u8]) -> Result<Self> {
let pem_key = PemEncodedKey::new(key)?;
let content = pem_key.as_rsa_key()?;
@ -42,7 +43,7 @@ impl<'a> EncodingKey<'a> {
}
/// Access the key, normal users do not need to use that.
pub fn inner(&'a self) -> &'a [u8] {
pub(crate) fn inner(&'a self) -> &'a [u8] {
&self.content
}
}

View File

@ -16,9 +16,7 @@ mod serialization;
mod validation;
pub use algorithms::Algorithm;
pub use decoding::{
dangerous_unsafe_decode, decode, decode_header, decode_rsa_components, TokenData,
};
pub use decoding::{dangerous_unsafe_decode, decode, decode_header, DecodingKey, TokenData};
pub use encoding::{encode, EncodingKey};
pub use header::Header;
pub use validation::Validation;

View File

@ -1,7 +1,7 @@
use chrono::Utc;
use jsonwebtoken::{
crypto::{sign, verify},
decode, encode, Algorithm, EncodingKey, Header, Validation,
decode, encode, Algorithm, DecodingKey, EncodingKey, Header, Validation,
};
use serde::{Deserialize, Serialize};
@ -12,30 +12,37 @@ pub struct Claims {
exp: i64,
}
// TODO: remove completely?
//#[test]
//fn round_trip_sign_verification_pk8() {
// let privkey = include_bytes!("private_ecdsa_key.pk8");
// let encrypted = sign("hello world", privkey, Algorithm::ES256).unwrap();
// let pubkey = include_bytes!("public_ecdsa_key.pk8");
// let is_valid = verify(&encrypted, "hello world", pubkey, Algorithm::ES256).unwrap();
// assert!(is_valid);
//}
#[test]
fn round_trip_sign_verification_pk8() {
let privkey = include_bytes!("private_ecdsa_key.pk8");
let pubkey = include_bytes!("public_ecdsa_key.pk8");
let encrypted = sign("hello world", &EncodingKey::from_der(privkey), Algorithm::ES256).unwrap();
let is_valid = verify(&encrypted, "hello world", &DecodingKey::from_der(pubkey), Algorithm::ES256).unwrap();
assert!(is_valid);
}
#[test]
fn round_trip_sign_verification_pem() {
let privkey = include_bytes!("private_ecdsa_key.pem");
let pubkey = include_bytes!("public_ecdsa_key.pem");
let privkey_pem = include_bytes!("private_ecdsa_key.pem");
let pubkey_pem = include_bytes!("public_ecdsa_key.pem");
let encrypted =
sign("hello world", &EncodingKey::from_ec_pem(privkey).unwrap(), Algorithm::ES256).unwrap();
let is_valid = verify(&encrypted, "hello world", pubkey, Algorithm::ES256).unwrap();
sign("hello world", &EncodingKey::from_ec_pem(privkey_pem).unwrap(), Algorithm::ES256)
.unwrap();
let is_valid = verify(
&encrypted,
"hello world",
&DecodingKey::from_ec_pem(pubkey_pem).unwrap(),
Algorithm::ES256,
)
.unwrap();
assert!(is_valid);
}
#[test]
fn round_trip_claim() {
let privkey = include_bytes!("private_ecdsa_key.pem");
let pubkey = include_bytes!("public_ecdsa_key.pem");
let privkey_pem = include_bytes!("private_ecdsa_key.pem");
let pubkey_pem = include_bytes!("public_ecdsa_key.pem");
let my_claims = Claims {
sub: "b@b.com".to_string(),
company: "ACME".to_string(),
@ -44,10 +51,15 @@ fn round_trip_claim() {
let token = encode(
&Header::new(Algorithm::ES256),
&my_claims,
&EncodingKey::from_ec_pem(privkey).unwrap(),
&EncodingKey::from_ec_pem(privkey_pem).unwrap(),
)
.unwrap();
let token_data = decode::<Claims>(
&token,
&DecodingKey::from_ec_pem(pubkey_pem).unwrap(),
&Validation::new(Algorithm::ES256),
)
.unwrap();
let token_data = decode::<Claims>(&token, pubkey, &Validation::new(Algorithm::ES256)).unwrap();
assert_eq!(my_claims, token_data.claims);
}
@ -55,8 +67,8 @@ fn round_trip_claim() {
#[test]
fn roundtrip_with_jwtio_example() {
// We currently do not support SEC1 so we use the converted PKCS8 formatted
let privkey = include_bytes!("private_jwtio_pkcs8.pem");
let pubkey = include_bytes!("public_jwtio.pem");
let privkey_pem = include_bytes!("private_jwtio_pkcs8.pem");
let pubkey_pem = include_bytes!("public_jwtio.pem");
let my_claims = Claims {
sub: "b@b.com".to_string(),
company: "ACME".to_string(),
@ -65,9 +77,14 @@ fn roundtrip_with_jwtio_example() {
let token = encode(
&Header::new(Algorithm::ES384),
&my_claims,
&EncodingKey::from_ec_pem(privkey).unwrap(),
&EncodingKey::from_ec_pem(privkey_pem).unwrap(),
)
.unwrap();
let token_data = decode::<Claims>(
&token,
&DecodingKey::from_ec_pem(pubkey_pem).unwrap(),
&Validation::new(Algorithm::ES384),
)
.unwrap();
let token_data = decode::<Claims>(&token, pubkey, &Validation::new(Algorithm::ES384)).unwrap();
assert_eq!(my_claims, token_data.claims);
}

View File

@ -1,8 +1,8 @@
use chrono::Utc;
use jsonwebtoken::{
crypto::{sign, verify},
dangerous_unsafe_decode, decode, decode_header, encode, Algorithm, EncodingKey, Header,
Validation,
dangerous_unsafe_decode, decode, decode_header, encode, Algorithm, DecodingKey, EncodingKey,
Header, Validation,
};
use serde::{Deserialize, Serialize};
@ -24,7 +24,8 @@ fn sign_hs256() {
#[test]
fn verify_hs256() {
let sig = "c0zGLzKEFWj0VxWuufTXiRMk5tlI5MbGDAYhzaxIYjo";
let valid = verify(sig, "hello world", b"secret", Algorithm::HS256).unwrap();
let valid =
verify(sig, "hello world", &DecodingKey::from_secret(b"secret"), Algorithm::HS256).unwrap();
assert!(valid);
}
@ -38,7 +39,9 @@ fn encode_with_custom_header() {
let mut header = Header::default();
header.kid = Some("kid".to_string());
let token = encode(&header, &my_claims, &EncodingKey::from_secret(b"secret")).unwrap();
let token_data = decode::<Claims>(&token, b"secret", &Validation::default()).unwrap();
let token_data =
decode::<Claims>(&token, &DecodingKey::from_secret(b"secret"), &Validation::default())
.unwrap();
assert_eq!(my_claims, token_data.claims);
assert_eq!("kid", token_data.header.kid.unwrap());
}
@ -52,7 +55,9 @@ fn round_trip_claim() {
};
let token =
encode(&Header::default(), &my_claims, &EncodingKey::from_secret(b"secret")).unwrap();
let token_data = decode::<Claims>(&token, b"secret", &Validation::default()).unwrap();
let token_data =
decode::<Claims>(&token, &DecodingKey::from_secret(b"secret"), &Validation::default())
.unwrap();
assert_eq!(my_claims, token_data.claims);
assert!(token_data.header.kid.is_none());
}
@ -60,7 +65,8 @@ fn round_trip_claim() {
#[test]
fn decode_token() {
let token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJiQGIuY29tIiwiY29tcGFueSI6IkFDTUUiLCJleHAiOjI1MzI1MjQ4OTF9.9r56oF7ZliOBlOAyiOFperTGxBtPykRQiWNFxhDCW98";
let claims = decode::<Claims>(token, b"secret", &Validation::default());
let claims =
decode::<Claims>(token, &DecodingKey::from_secret(b"secret"), &Validation::default());
println!("{:?}", claims);
claims.unwrap();
}
@ -69,7 +75,8 @@ fn decode_token() {
#[should_panic(expected = "InvalidToken")]
fn decode_token_missing_parts() {
let token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9";
let claims = decode::<Claims>(token, b"secret", &Validation::default());
let claims =
decode::<Claims>(token, &DecodingKey::from_secret(b"secret"), &Validation::default());
claims.unwrap();
}
@ -78,7 +85,8 @@ fn decode_token_missing_parts() {
fn decode_token_invalid_signature() {
let token =
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJiQGIuY29tIiwiY29tcGFueSI6IkFDTUUifQ.wrong";
let claims = decode::<Claims>(token, b"secret", &Validation::default());
let claims =
decode::<Claims>(token, &DecodingKey::from_secret(b"secret"), &Validation::default());
claims.unwrap();
}
@ -86,14 +94,19 @@ fn decode_token_invalid_signature() {
#[should_panic(expected = "InvalidAlgorithm")]
fn decode_token_wrong_algorithm() {
let token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJiQGIuY29tIiwiY29tcGFueSI6IkFDTUUifQ.I1BvFoHe94AFf09O6tDbcSB8-jp8w6xZqmyHIwPeSdY";
let claims = decode::<Claims>(token, b"secret", &Validation::new(Algorithm::RS512));
let claims = decode::<Claims>(
token,
&DecodingKey::from_secret(b"secret"),
&Validation::new(Algorithm::RS512),
);
claims.unwrap();
}
#[test]
fn decode_token_with_bytes_secret() {
let token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJiQGIuY29tIiwiY29tcGFueSI6IkFDTUUiLCJleHAiOjI1MzI1MjQ4OTF9.Hm0yvKH25TavFPz7J_coST9lZFYH1hQo0tvhvImmaks";
let claims = decode::<Claims>(token, b"\x01\x02\x03", &Validation::default());
let claims =
decode::<Claims>(token, &DecodingKey::from_secret(b"\x01\x02\x03"), &Validation::default());
assert!(claims.is_ok());
}

View File

@ -1,7 +1,7 @@
use chrono::Utc;
use jsonwebtoken::{
crypto::{sign, verify},
decode, decode_rsa_components, encode, Algorithm, EncodingKey, Header, Validation,
decode, encode, Algorithm, DecodingKey, EncodingKey, Header, Validation,
};
use serde::{Deserialize, Serialize};
@ -29,7 +29,9 @@ fn round_trip_sign_verification_pem_pkcs1() {
for &alg in RSA_ALGORITHMS {
let encrypted =
sign("hello world", &EncodingKey::from_rsa_pem(privkey_pem).unwrap(), alg).unwrap();
let is_valid = verify(&encrypted, "hello world", pubkey_pem, alg).unwrap();
let is_valid =
verify(&encrypted, "hello world", &DecodingKey::from_rsa_pem(pubkey_pem).unwrap(), alg)
.unwrap();
assert!(is_valid);
}
}
@ -42,7 +44,24 @@ fn round_trip_sign_verification_pem_pkcs8() {
for &alg in RSA_ALGORITHMS {
let encrypted =
sign("hello world", &EncodingKey::from_rsa_pem(privkey_pem).unwrap(), alg).unwrap();
let is_valid = verify(&encrypted, "hello world", pubkey_pem, alg).unwrap();
let is_valid =
verify(&encrypted, "hello world", &DecodingKey::from_rsa_pem(pubkey_pem).unwrap(), alg)
.unwrap();
assert!(is_valid);
}
}
#[test]
fn round_trip_sign_verification_der() {
let privkey_der = include_bytes!("private_rsa_key.der");
let pubkey_der = include_bytes!("public_rsa_key.der");
for &alg in RSA_ALGORITHMS {
let encrypted =
sign("hello world", &EncodingKey::from_der(privkey_der), alg).unwrap();
let is_valid =
verify(&encrypted, "hello world", &DecodingKey::from_der(pubkey_der), alg)
.unwrap();
assert!(is_valid);
}
}
@ -54,15 +73,16 @@ fn round_trip_claim() {
company: "ACME".to_string(),
exp: Utc::now().timestamp() + 10000,
};
let privkey = include_bytes!("private_rsa_key_pkcs1.pem");
let privkey_pem = include_bytes!("private_rsa_key_pkcs1.pem");
let pubkey_pem = include_bytes!("public_rsa_key_pkcs1.pem");
for &alg in RSA_ALGORITHMS {
let token =
encode(&Header::new(alg), &my_claims, &EncodingKey::from_rsa_pem(privkey).unwrap())
encode(&Header::new(alg), &my_claims, &EncodingKey::from_rsa_pem(privkey_pem).unwrap())
.unwrap();
let token_data = decode::<Claims>(
&token,
include_bytes!("public_rsa_key_pkcs1.pem"),
&DecodingKey::from_rsa_pem(pubkey_pem).unwrap(),
&Validation::new(alg),
)
.unwrap();
@ -88,7 +108,11 @@ fn rsa_modulus_exponent() {
&EncodingKey::from_rsa_pem(privkey.as_ref()).unwrap(),
)
.unwrap();
let res = decode_rsa_components::<Claims>(&encrypted, n, e, &Validation::new(Algorithm::RS256));
let res = decode::<Claims>(
&encrypted,
&DecodingKey::from_rsa_components(n, e),
&Validation::new(Algorithm::RS256),
);
assert!(res.is_ok());
}
@ -108,7 +132,12 @@ fn roundtrip_with_jwtio_example_jey() {
let token =
encode(&Header::new(alg), &my_claims, &EncodingKey::from_rsa_pem(privkey_pem).unwrap())
.unwrap();
let token_data = decode::<Claims>(&token, pubkey_pem, &Validation::new(alg)).unwrap();
let token_data = decode::<Claims>(
&token,
&DecodingKey::from_rsa_pem(pubkey_pem).unwrap(),
&Validation::new(alg),
)
.unwrap();
assert_eq!(my_claims, token_data.claims);
}
}