jsonwebtoken/crypto/
mod.rs

1use ring::constant_time::verify_slices_are_equal;
2use ring::{hmac, signature};
3
4use crate::algorithms::Algorithm;
5use crate::decoding::{DecodingKey, DecodingKeyKind};
6use crate::encoding::EncodingKey;
7use crate::errors::Result;
8use crate::serialization::{b64_decode, b64_encode};
9
10pub(crate) mod ecdsa;
11pub(crate) mod rsa;
12
13/// The actual HS signing + encoding
14/// Could be in its own file to match RSA/EC but it's 2 lines...
15pub(crate) fn sign_hmac(alg: hmac::Algorithm, key: &[u8], message: &str) -> Result<String> {
16    let digest = hmac::sign(&hmac::Key::new(alg, key), message.as_bytes());
17    Ok(b64_encode(digest.as_ref()))
18}
19
20/// Take the payload of a JWT, sign it using the algorithm given and return
21/// the base64 url safe encoded of the result.
22///
23/// If you just want to encode a JWT, use `encode` instead.
24pub fn sign(message: &str, key: &EncodingKey, algorithm: Algorithm) -> Result<String> {
25    match algorithm {
26        Algorithm::HS256 => sign_hmac(hmac::HMAC_SHA256, key.inner(), message),
27        Algorithm::HS384 => sign_hmac(hmac::HMAC_SHA384, key.inner(), message),
28        Algorithm::HS512 => sign_hmac(hmac::HMAC_SHA512, key.inner(), message),
29
30        Algorithm::ES256 | Algorithm::ES384 => {
31            ecdsa::sign(ecdsa::alg_to_ec_signing(algorithm), key.inner(), message)
32        }
33
34        Algorithm::RS256
35        | Algorithm::RS384
36        | Algorithm::RS512
37        | Algorithm::PS256
38        | Algorithm::PS384
39        | Algorithm::PS512 => rsa::sign(rsa::alg_to_rsa_signing(algorithm), key.inner(), message),
40    }
41}
42
43/// See Ring docs for more details
44fn verify_ring(
45    alg: &'static dyn signature::VerificationAlgorithm,
46    signature: &str,
47    message: &str,
48    key: &[u8],
49) -> Result<bool> {
50    let signature_bytes = b64_decode(signature)?;
51    let public_key = signature::UnparsedPublicKey::new(alg, key);
52    let res = public_key.verify(message.as_bytes(), &signature_bytes);
53
54    Ok(res.is_ok())
55}
56
57/// Compares the signature given with a re-computed signature for HMAC or using the public key
58/// for RSA/EC.
59///
60/// If you just want to decode a JWT, use `decode` instead.
61///
62/// `signature` is the signature part of a jwt (text after the second '.')
63///
64/// `message` is base64(header) + "." + base64(claims)
65pub fn verify(
66    signature: &str,
67    message: &str,
68    key: &DecodingKey,
69    algorithm: Algorithm,
70) -> Result<bool> {
71    match algorithm {
72        Algorithm::HS256 | Algorithm::HS384 | Algorithm::HS512 => {
73            // we just re-sign the message with the key and compare if they are equal
74            let signed = sign(message, &EncodingKey::from_secret(key.as_bytes()), algorithm)?;
75            Ok(verify_slices_are_equal(signature.as_ref(), signed.as_ref()).is_ok())
76        }
77        Algorithm::ES256 | Algorithm::ES384 => verify_ring(
78            ecdsa::alg_to_ec_verification(algorithm),
79            signature,
80            message,
81            key.as_bytes(),
82        ),
83        Algorithm::RS256
84        | Algorithm::RS384
85        | Algorithm::RS512
86        | Algorithm::PS256
87        | Algorithm::PS384
88        | Algorithm::PS512 => {
89            let alg = rsa::alg_to_rsa_parameters(algorithm);
90            match &key.kind {
91                DecodingKeyKind::SecretOrDer(bytes) => verify_ring(alg, signature, message, bytes),
92                DecodingKeyKind::RsaModulusExponent { n, e } => {
93                    rsa::verify_from_components(alg, signature, message, (n, e))
94                }
95            }
96        }
97    }
98}