Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support decoding byte slices #387

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions benches/jwt.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use jsonwebtoken::{decode, encode, Algorithm, DecodingKey, EncodingKey, Header, Validation};
use jsonwebtoken::{
decode, decode_header, encode, Algorithm, DecodingKey, EncodingKey, Header, Validation,
};
use serde::{Deserialize, Serialize};

#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
Expand All @@ -21,7 +23,10 @@ fn bench_decode(c: &mut Criterion) {
let token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ";
let key = DecodingKey::from_secret("secret".as_ref());

c.bench_function("bench_decode", |b| {
let mut group = c.benchmark_group("decode");
group.throughput(criterion::Throughput::Bytes(token.len() as u64));

group.bench_function("str", |b| {
b.iter(|| {
decode::<Claims>(
black_box(token),
Expand All @@ -30,6 +35,19 @@ fn bench_decode(c: &mut Criterion) {
)
})
});

drop(group);
let mut group = c.benchmark_group("header");
group.throughput(criterion::Throughput::Bytes(token.len() as u64));

group.bench_function("str", |b| {
b.iter(|| {
decode_header(
// Simulate the cost of validating &str before decoding
black_box(token),
)
})
});
}

criterion_group!(benches, bench_encode, bench_decode);
Expand Down
7 changes: 4 additions & 3 deletions src/crypto/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ pub fn sign(message: &[u8], key: &EncodingKey, algorithm: Algorithm) -> Result<S
/// See Ring docs for more details
fn verify_ring(
alg: &'static dyn signature::VerificationAlgorithm,
signature: &str,
signature: impl AsRef<[u8]>,
message: &[u8],
key: &[u8],
) -> Result<bool> {
Expand All @@ -66,16 +66,17 @@ fn verify_ring(
///
/// `message` is base64(header) + "." + base64(claims)
pub fn verify(
signature: &str,
signature: impl AsRef<[u8]>,
message: &[u8],
key: &DecodingKey,
algorithm: Algorithm,
) -> Result<bool> {
let signature = signature.as_ref();
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.as_bytes()), algorithm)?;
Ok(verify_slices_are_equal(signature.as_ref(), signed.as_ref()).is_ok())
Ok(verify_slices_are_equal(signature, signed.as_ref()).is_ok())
}
Algorithm::ES256 | Algorithm::ES384 => verify_ring(
ecdsa::alg_to_ec_verification(algorithm),
Expand Down
2 changes: 1 addition & 1 deletion src/crypto/rsa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ pub(crate) fn sign(
/// 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: &str,
signature: impl AsRef<[u8]>,
message: &[u8],
components: (&[u8], &[u8]),
) -> Result<bool> {
Expand Down
32 changes: 17 additions & 15 deletions src/decoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,11 +204,11 @@ impl DecodingKey {
/// Verify signature of a JWT, and return header object and raw payload
///
/// If the token or its signature is invalid, it will return an error.
fn verify_signature<'a>(
token: &'a str,
fn verify_signature_bytes<'a>(
token: &'a [u8],
key: &DecodingKey,
validation: &Validation,
) -> Result<(Header, &'a str)> {
) -> Result<(Header, &'a [u8])> {
if validation.validate_signature && validation.algorithms.is_empty() {
return Err(new_error(ErrorKind::MissingAlgorithm));
}
Expand All @@ -221,15 +221,15 @@ fn verify_signature<'a>(
}
}

let (signature, message) = expect_two!(token.rsplitn(2, '.'));
let (payload, header) = expect_two!(message.rsplitn(2, '.'));
let (signature, message) = expect_two!(token.rsplitn(2, |b| *b == b'.'));
let (payload, header) = expect_two!(message.rsplitn(2, |b| *b == b'.'));
let header = Header::from_encoded(header)?;

if validation.validate_signature && !validation.algorithms.contains(&header.alg) {
return Err(new_error(ErrorKind::InvalidAlgorithm));
}

if validation.validate_signature && !verify(signature, message.as_bytes(), key, header.alg)? {
if validation.validate_signature && !verify(signature, message, key, header.alg)? {
return Err(new_error(ErrorKind::InvalidSignature));
}

Expand All @@ -250,16 +250,17 @@ fn verify_signature<'a>(
/// company: String
/// }
///
/// let token = "a.jwt.token".to_string();
/// let token = "a.jwt.token";
/// // Claims is a struct that implements Deserialize
/// let token_message = decode::<Claims>(&token, &DecodingKey::from_secret("secret".as_ref()), &Validation::new(Algorithm::HS256));
/// let token_message = decode::<Claims>(token, &DecodingKey::from_secret("secret".as_ref()), &Validation::new(Algorithm::HS256));
/// ```
pub fn decode<T: DeserializeOwned>(
token: &str,
token: impl AsRef<[u8]>,
key: &DecodingKey,
validation: &Validation,
) -> Result<TokenData<T>> {
match verify_signature(token, key, validation) {
let token = token.as_ref();
match verify_signature_bytes(token, key, validation) {
Err(e) => Err(e),
Ok((header, claims)) => {
let decoded_claims = DecodedJwtPartClaims::from_jwt_part_claims(claims)?;
Expand All @@ -278,11 +279,12 @@ pub fn decode<T: DeserializeOwned>(
/// ```rust
/// use jsonwebtoken::decode_header;
///
/// let token = "a.jwt.token".to_string();
/// let header = decode_header(&token);
/// let token = "a.jwt.token";
/// let header = decode_header(token);
/// ```
pub fn decode_header(token: &str) -> Result<Header> {
let (_, message) = expect_two!(token.rsplitn(2, '.'));
let (_, header) = expect_two!(message.rsplitn(2, '.'));
pub fn decode_header(token: impl AsRef<[u8]>) -> Result<Header> {
let token = token.as_ref();
let (_, message) = expect_two!(token.rsplitn(2, |b| *b == b'.'));
let (_, header) = expect_two!(message.rsplitn(2, |b| *b == b'.'));
Header::from_encoded(header)
}
4 changes: 2 additions & 2 deletions tests/ecdsa/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ fn round_trip_sign_verification_pk8() {
let encrypted =
sign(b"hello world", &EncodingKey::from_ec_der(privkey), Algorithm::ES256).unwrap();
let is_valid =
verify(&encrypted, b"hello world", &DecodingKey::from_ec_der(pubkey), Algorithm::ES256)
verify(encrypted, b"hello world", &DecodingKey::from_ec_der(pubkey), Algorithm::ES256)
.unwrap();
assert!(is_valid);
}
Expand All @@ -41,7 +41,7 @@ fn round_trip_sign_verification_pem() {
sign(b"hello world", &EncodingKey::from_ec_pem(privkey_pem).unwrap(), Algorithm::ES256)
.unwrap();
let is_valid = verify(
&encrypted,
encrypted,
b"hello world",
&DecodingKey::from_ec_pem(pubkey_pem).unwrap(),
Algorithm::ES256,
Expand Down
4 changes: 2 additions & 2 deletions tests/eddsa/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ fn round_trip_sign_verification_pk8() {
let encrypted =
sign(b"hello world", &EncodingKey::from_ed_der(privkey), Algorithm::EdDSA).unwrap();
let is_valid =
verify(&encrypted, b"hello world", &DecodingKey::from_ed_der(pubkey), Algorithm::EdDSA)
verify(encrypted, b"hello world", &DecodingKey::from_ed_der(pubkey), Algorithm::EdDSA)
.unwrap();
assert!(is_valid);
}
Expand All @@ -41,7 +41,7 @@ fn round_trip_sign_verification_pem() {
sign(b"hello world", &EncodingKey::from_ed_pem(privkey_pem).unwrap(), Algorithm::EdDSA)
.unwrap();
let is_valid = verify(
&encrypted,
encrypted,
b"hello world",
&DecodingKey::from_ed_pem(pubkey_pem).unwrap(),
Algorithm::EdDSA,
Expand Down
Loading