Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
25 changes: 14 additions & 11 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ features = ["rust_crypto"]
base64 = "0.22"
serde = { version = "1.0.228", features = ["derive"] }
serde_json = "1.0"
signature = { version = "2.2.0", features = ["std"] }
signature = { version = "3.0.0", features = ["alloc"] }

# For PEM decoding
pem = { version = "3", optional = true }
Expand All @@ -36,15 +36,18 @@ simple_asn1 = { version = "0.6", optional = true }
aws-lc-rs = { version = "1.15.0", optional = true }

# "rust_crypto" feature
ed25519-dalek = { version = "2.1.1", optional = true, features = ["pkcs8"] }
hmac = { version = "0.12.1", optional = true, features = ["reset"] }
p256 = { version = "0.13.2", optional = true, features = ["ecdsa"] }
p384 = { version = "0.13.0", optional = true, features = ["ecdsa"] }
rand = { version = "0.8.5", optional = true, features = [
ed25519-dalek = { version = "3.0.0", optional = true, features = ["pkcs8"] }
hmac = { version = "0.13.0", optional = true }
p256 = { version = "0.14", optional = true, features = ["ecdsa"] }
p384 = { version = "0.14", optional = true, features = ["ecdsa"] }
p521 = { version = "0.14", optional = true, features = ["ecdsa", "pkcs8"] }
rand = { version = "0.10.0", optional = true, features = [
"std",
"std_rng",
"thread_rng",
], default-features = false }
rsa = { version = "0.9.6", optional = true }
sha2 = { version = "0.10.7", optional = true, features = ["oid"] }
rsa = { version = "0.10.0-rc.18", optional = true }
sha2 = { version = "0.11", optional = true, features = ["oid"] }
zeroize = { version = "1.8.2", features = ["derive"] }

[target.'cfg(target_arch = "wasm32")'.dependencies]
Expand All @@ -53,9 +56,8 @@ getrandom = "0.2"

[dev-dependencies]
wasm-bindgen-test = "0.3.1"
ed25519-dalek = { version = "2.1.1", features = ["pkcs8", "rand_core"] }
rand = { version = "0.8.5", features = ["std"], default-features = false }
rand_core = "0.6.4"
ed25519-dalek = { version = "3.0.0", features = ["pkcs8", "rand_core", "alloc"] }
rand = { version = "0.10.0", features = ["std", "std_rng", "thread_rng"], default-features = false }
[target.'cfg(not(all(target_arch = "wasm32", not(any(target_os = "emscripten", target_os = "wasi")))))'.dev-dependencies]
# For the custom time example
time = "0.3"
Expand All @@ -74,6 +76,7 @@ rust_crypto = [
"dep:hmac",
"dep:p256",
"dep:p384",
"dep:p521",
"dep:rand",
"dep:rsa",
"dep:sha2",
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ This library currently supports the following:
- PS512
- ES256
- ES384
- ES512
- EdDSA


Expand Down
15 changes: 4 additions & 11 deletions examples/ed25519.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use ed25519_dalek::SigningKey;
use ed25519_dalek::pkcs8::EncodePrivateKey;
use rand_core::OsRng;
use serde::{Deserialize, Serialize};

use jsonwebtoken::{
Expand All @@ -14,12 +13,9 @@ pub struct Claims {
}

fn main() {
let signing_key = SigningKey::generate(&mut OsRng);
let signing_key = SigningKey::generate(&mut rand::rng());
let pkcs8 = signing_key.to_pkcs8_der().unwrap();
let pkcs8 = pkcs8.as_bytes();
// The `to_pkcs8_der` includes the public key, the first 48 bits are the private key.
let pkcs8 = &pkcs8[..48];
let encoding_key = EncodingKey::from_ed_der(pkcs8);
let encoding_key = EncodingKey::from_ed_der(pkcs8.as_bytes());

let verifying_key = signing_key.verifying_key();
let public_key = verifying_key.as_bytes();
Expand All @@ -45,12 +41,9 @@ mod tests {

impl Jot {
fn new() -> Jot {
let signing_key = SigningKey::generate(&mut OsRng);
let signing_key = SigningKey::generate(&mut rand::rng());
let pkcs8 = signing_key.to_pkcs8_der().unwrap();
let pkcs8 = pkcs8.as_bytes();
// The `to_pkcs8_der` includes the public key, the first 48 bits are the private key.
let pkcs8 = &pkcs8[..48];
let encoding_key = EncodingKey::from_ed_der(&pkcs8);
let encoding_key = EncodingKey::from_ed_der(pkcs8.as_bytes());

let verifying_key = signing_key.verifying_key();
let public_key = verifying_key.as_bytes();
Expand Down
7 changes: 5 additions & 2 deletions src/algorithms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ impl AlgorithmFamily {
Algorithm::PS384,
Algorithm::PS512,
],
Self::Ec => &[Algorithm::ES256, Algorithm::ES384],
Self::Ec => &[Algorithm::ES256, Algorithm::ES384, Algorithm::ES512],
Self::Ed => &[Algorithm::EdDSA],
}
}
Expand All @@ -53,6 +53,8 @@ pub enum Algorithm {
ES256,
/// ECDSA using SHA-384
ES384,
/// ECDSA using SHA-512
ES512,

/// RSASSA-PKCS1-v1_5 using SHA-256
RS256,
Expand Down Expand Up @@ -81,6 +83,7 @@ impl FromStr for Algorithm {
"HS512" => Ok(Algorithm::HS512),
"ES256" => Ok(Algorithm::ES256),
"ES384" => Ok(Algorithm::ES384),
"ES512" => Ok(Algorithm::ES512),
"RS256" => Ok(Algorithm::RS256),
"RS384" => Ok(Algorithm::RS384),
"PS256" => Ok(Algorithm::PS256),
Expand All @@ -104,7 +107,7 @@ impl Algorithm {
| Algorithm::PS256
| Algorithm::PS384
| Algorithm::PS512 => AlgorithmFamily::Rsa,
Algorithm::ES256 | Algorithm::ES384 => AlgorithmFamily::Ec,
Algorithm::ES256 | Algorithm::ES384 | Algorithm::ES512 => AlgorithmFamily::Ec,
Algorithm::EdDSA => AlgorithmFamily::Ed,
}
}
Expand Down
6 changes: 5 additions & 1 deletion src/crypto/aws_lc/ecdsa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ use crate::{Algorithm, DecodingKey, EncodingKey};
use aws_lc_rs::rand::SystemRandom;
use aws_lc_rs::signature::{
ECDSA_P256_SHA256_FIXED, ECDSA_P256_SHA256_FIXED_SIGNING, ECDSA_P384_SHA384_FIXED,
ECDSA_P384_SHA384_FIXED_SIGNING, EcdsaKeyPair, VerificationAlgorithm,
ECDSA_P384_SHA384_FIXED_SIGNING, ECDSA_P521_SHA512_FIXED, ECDSA_P521_SHA512_FIXED_SIGNING,
EcdsaKeyPair, VerificationAlgorithm,
};
use signature::{Error, Signer, Verifier};

Expand Down Expand Up @@ -85,3 +86,6 @@ define_ecdsa_verifier!(Es256Verifier, Algorithm::ES256, ECDSA_P256_SHA256_FIXED)

define_ecdsa_signer!(Es384Signer, Algorithm::ES384, &ECDSA_P384_SHA384_FIXED_SIGNING);
define_ecdsa_verifier!(Es384Verifier, Algorithm::ES384, ECDSA_P384_SHA384_FIXED);

define_ecdsa_signer!(Es512Signer, Algorithm::ES512, &ECDSA_P521_SHA512_FIXED_SIGNING);
define_ecdsa_verifier!(Es512Verifier, Algorithm::ES512, ECDSA_P521_SHA512_FIXED);
5 changes: 4 additions & 1 deletion src/crypto/aws_lc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use aws_lc_rs::{
digest,
signature::{
self as aws_sig, ECDSA_P256_SHA256_FIXED_SIGNING, ECDSA_P384_SHA384_FIXED_SIGNING,
EcdsaKeyPair, Ed25519KeyPair, KeyPair,
ECDSA_P521_SHA512_FIXED_SIGNING, EcdsaKeyPair, Ed25519KeyPair, KeyPair,
},
};

Expand Down Expand Up @@ -41,6 +41,7 @@ fn ec_components_from_private_key(
let (signing_alg, curve, pub_elem_bytes) = match alg {
Algorithm::ES256 => (&ECDSA_P256_SHA256_FIXED_SIGNING, EllipticCurve::P256, 32),
Algorithm::ES384 => (&ECDSA_P384_SHA384_FIXED_SIGNING, EllipticCurve::P384, 48),
Algorithm::ES512 => (&ECDSA_P521_SHA512_FIXED_SIGNING, EllipticCurve::P521, 66),
_ => return Err(ErrorKind::InvalidEcdsaKey.into()),
};

Expand Down Expand Up @@ -86,6 +87,7 @@ fn new_signer(algorithm: &Algorithm, key: &EncodingKey) -> Result<Box<dyn JwtSig
Algorithm::HS512 => Box::new(hmac::Hs512Signer::new(key)?) as Box<dyn JwtSigner>,
Algorithm::ES256 => Box::new(ecdsa::Es256Signer::new(key)?) as Box<dyn JwtSigner>,
Algorithm::ES384 => Box::new(ecdsa::Es384Signer::new(key)?) as Box<dyn JwtSigner>,
Algorithm::ES512 => Box::new(ecdsa::Es512Signer::new(key)?) as Box<dyn JwtSigner>,
Algorithm::RS256 => Box::new(rsa::Rsa256Signer::new(key)?) as Box<dyn JwtSigner>,
Algorithm::RS384 => Box::new(rsa::Rsa384Signer::new(key)?) as Box<dyn JwtSigner>,
Algorithm::RS512 => Box::new(rsa::Rsa512Signer::new(key)?) as Box<dyn JwtSigner>,
Expand All @@ -108,6 +110,7 @@ fn new_verifier(
Algorithm::HS512 => Box::new(hmac::Hs512Verifier::new(key)?) as Box<dyn JwtVerifier>,
Algorithm::ES256 => Box::new(ecdsa::Es256Verifier::new(key)?) as Box<dyn JwtVerifier>,
Algorithm::ES384 => Box::new(ecdsa::Es384Verifier::new(key)?) as Box<dyn JwtVerifier>,
Algorithm::ES512 => Box::new(ecdsa::Es512Verifier::new(key)?) as Box<dyn JwtVerifier>,
Algorithm::RS256 => Box::new(rsa::Rsa256Verifier::new(key)?) as Box<dyn JwtVerifier>,
Algorithm::RS384 => Box::new(rsa::Rsa384Verifier::new(key)?) as Box<dyn JwtVerifier>,
Algorithm::RS512 => Box::new(rsa::Rsa512Verifier::new(key)?) as Box<dyn JwtVerifier>,
Expand Down
37 changes: 36 additions & 1 deletion src/crypto/rust_crypto/ecdsa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ use p256::pkcs8::DecodePrivateKey;
use p384::ecdsa::{
Signature as Signature384, SigningKey as SigningKey384, VerifyingKey as VerifyingKey384,
};
use p521::ecdsa::{
Signature as Signature521, SigningKey as SigningKey521, VerifyingKey as VerifyingKey521,
};
use signature::{Error, Signer, Verifier};

macro_rules! define_ecdsa_signer {
Expand All @@ -33,7 +36,7 @@ macro_rules! define_ecdsa_signer {

impl Signer<Vec<u8>> for $name {
fn try_sign(&self, msg: &[u8]) -> std::result::Result<Vec<u8>, Error> {
let signature = self.0.sign_recoverable(msg).map_err(Error::from_source)?.0;
let signature = self.0.sign_recoverable(msg).0;
Ok(signature.to_vec())
}
}
Expand Down Expand Up @@ -85,3 +88,35 @@ define_ecdsa_signer!(Es384Signer, Algorithm::ES384, SigningKey384);

define_ecdsa_verifier!(Es256Verifier, Algorithm::ES256, VerifyingKey256, Signature256);
define_ecdsa_verifier!(Es384Verifier, Algorithm::ES384, VerifyingKey384, Signature384);

// P-521 (ES512) has no `sign_recoverable()` support in RustCrypto, so it can't
// go through the `define_ecdsa_signer!` macro used for ES256/ES384.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not adjust the macro? We just discard the recovery ID do we not?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that was easy 😅

pub struct Es512Signer(SigningKey521);

impl Es512Signer {
pub(crate) fn new(encoding_key: &EncodingKey) -> Result<Self> {
if encoding_key.family() != AlgorithmFamily::Ec {
return Err(new_error(ErrorKind::InvalidKeyFormat));
}

Ok(Self(
SigningKey521::from_pkcs8_der(encoding_key.as_bytes())
.map_err(|_| ErrorKind::InvalidEcdsaKey)?,
))
}
}

impl Signer<Vec<u8>> for Es512Signer {
fn try_sign(&self, msg: &[u8]) -> std::result::Result<Vec<u8>, Error> {
let signature: Signature521 = self.0.sign(msg);
Ok(signature.to_vec())
}
}

impl JwtSigner for Es512Signer {
fn algorithm(&self) -> Algorithm {
Algorithm::ES512
}
}

define_ecdsa_verifier!(Es512Verifier, Algorithm::ES512, VerifyingKey521, Signature521);
8 changes: 4 additions & 4 deletions src/crypto/rust_crypto/hmac.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
//! Implementations of the [`JwtSigner`] and [`JwtVerifier`] traits for the
//! HMAC family of algorithms using `RustCrypto`'s [`hmac`].

use hmac::{Hmac, Mac};
use hmac::{HmacReset, KeyInit, Mac};
use sha2::{Sha256, Sha384, Sha512};
use signature::{Signer, Verifier};

use crate::crypto::{JwtSigner, JwtVerifier};
use crate::errors::{ErrorKind, Result, new_error};
use crate::{Algorithm, AlgorithmFamily, DecodingKey, EncodingKey};

type HmacSha256 = Hmac<Sha256>;
type HmacSha384 = Hmac<Sha384>;
type HmacSha512 = Hmac<Sha512>;
type HmacSha256 = HmacReset<Sha256>;
type HmacSha384 = HmacReset<Sha384>;
type HmacSha512 = HmacReset<Sha512>;

/// Macro to define an HMAC signer for a specific algorithm
macro_rules! define_hmac_signer {
Expand Down
29 changes: 25 additions & 4 deletions src/crypto/rust_crypto/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use ::rsa::{
use ed25519_dalek::SigningKey as Ed25519SigningKey;
use p256::{ecdsa::SigningKey as P256SigningKey, pkcs8::DecodePrivateKey};
use p384::ecdsa::SigningKey as P384SigningKey;
use p521::ecdsa::SigningKey as P521SigningKey;
use sha2::{Digest, Sha256, Sha384, Sha512};

use crate::{
Expand All @@ -24,13 +25,19 @@ fn rsa_components_from_private_key(key_content: &[u8]) -> errors::Result<(Vec<u8
let private_key = RsaPrivateKey::from_pkcs1_der(key_content)
.map_err(|e| ErrorKind::InvalidRsaKey(e.to_string()))?;
let public_key = private_key.to_public_key();
Ok((public_key.n().to_bytes_be(), public_key.e().to_bytes_be()))
Ok((
public_key.n().to_be_bytes_trimmed_vartime().to_vec(),
public_key.e().to_be_bytes_trimmed_vartime().to_vec(),
))
}

fn rsa_components_from_public_key(key_content: &[u8]) -> errors::Result<(Vec<u8>, Vec<u8>)> {
let public_key = RsaPublicKey::from_pkcs1_der(key_content)
.map_err(|e| ErrorKind::InvalidRsaKey(e.to_string()))?;
Ok((public_key.n().to_bytes_be(), public_key.e().to_bytes_be()))
Ok((
public_key.n().to_be_bytes_trimmed_vartime().to_vec(),
public_key.e().to_be_bytes_trimmed_vartime().to_vec(),
))
}

fn ec_components_from_private_key(
Expand All @@ -42,7 +49,7 @@ fn ec_components_from_private_key(
let signing_key = P256SigningKey::from_pkcs8_der(key_content)
.map_err(|_| ErrorKind::InvalidEcdsaKey)?;
let public_key = signing_key.verifying_key();
let encoded = public_key.to_encoded_point(false);
let encoded = public_key.to_sec1_point(false);
match encoded.coordinates() {
p256::elliptic_curve::sec1::Coordinates::Uncompressed { x, y } => {
Ok((EllipticCurve::P256, x.to_vec(), y.to_vec()))
Expand All @@ -54,14 +61,26 @@ fn ec_components_from_private_key(
let signing_key = P384SigningKey::from_pkcs8_der(key_content)
.map_err(|_| ErrorKind::InvalidEcdsaKey)?;
let public_key = signing_key.verifying_key();
let encoded = public_key.to_encoded_point(false);
let encoded = public_key.to_sec1_point(false);
match encoded.coordinates() {
p384::elliptic_curve::sec1::Coordinates::Uncompressed { x, y } => {
Ok((EllipticCurve::P384, x.to_vec(), y.to_vec()))
}
_ => Err(ErrorKind::InvalidEcdsaKey.into()),
}
}
Algorithm::ES512 => {
let signing_key = P521SigningKey::from_pkcs8_der(key_content)
.map_err(|_| ErrorKind::InvalidEcdsaKey)?;
let public_key = signing_key.verifying_key();
let encoded = public_key.to_sec1_point(false);
match encoded.coordinates() {
p521::elliptic_curve::sec1::Coordinates::Uncompressed { x, y } => {
Ok((EllipticCurve::P521, x.to_vec(), y.to_vec()))
}
_ => Err(ErrorKind::InvalidEcdsaKey.into()),
}
}
_ => Err(ErrorKind::InvalidEcdsaKey.into()),
}
}
Expand Down Expand Up @@ -95,6 +114,7 @@ fn new_signer(algorithm: &Algorithm, key: &EncodingKey) -> Result<Box<dyn JwtSig
Algorithm::HS512 => Box::new(hmac::Hs512Signer::new(key)?) as Box<dyn JwtSigner>,
Algorithm::ES256 => Box::new(ecdsa::Es256Signer::new(key)?) as Box<dyn JwtSigner>,
Algorithm::ES384 => Box::new(ecdsa::Es384Signer::new(key)?) as Box<dyn JwtSigner>,
Algorithm::ES512 => Box::new(ecdsa::Es512Signer::new(key)?) as Box<dyn JwtSigner>,
Algorithm::RS256 => Box::new(rsa::Rsa256Signer::new(key)?) as Box<dyn JwtSigner>,
Algorithm::RS384 => Box::new(rsa::Rsa384Signer::new(key)?) as Box<dyn JwtSigner>,
Algorithm::RS512 => Box::new(rsa::Rsa512Signer::new(key)?) as Box<dyn JwtSigner>,
Expand All @@ -117,6 +137,7 @@ fn new_verifier(
Algorithm::HS512 => Box::new(hmac::Hs512Verifier::new(key)?) as Box<dyn JwtVerifier>,
Algorithm::ES256 => Box::new(ecdsa::Es256Verifier::new(key)?) as Box<dyn JwtVerifier>,
Algorithm::ES384 => Box::new(ecdsa::Es384Verifier::new(key)?) as Box<dyn JwtVerifier>,
Algorithm::ES512 => Box::new(ecdsa::Es512Verifier::new(key)?) as Box<dyn JwtVerifier>,
Algorithm::RS256 => Box::new(rsa::Rsa256Verifier::new(key)?) as Box<dyn JwtVerifier>,
Algorithm::RS384 => Box::new(rsa::Rsa384Verifier::new(key)?) as Box<dyn JwtVerifier>,
Algorithm::RS512 => Box::new(rsa::Rsa512Verifier::new(key)?) as Box<dyn JwtVerifier>,
Expand Down
8 changes: 4 additions & 4 deletions src/crypto/rust_crypto/rsa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//! RSA family of algorithms using RustCrypto.

use rsa::{
BigUint, Pkcs1v15Sign, Pss, RsaPublicKey,
BoxedUint, Pkcs1v15Sign, Pss, RsaPublicKey,
pkcs1::{DecodeRsaPrivateKey, DecodeRsaPublicKey},
pkcs1v15::SigningKey,
pkcs8::AssociatedOid,
Expand All @@ -29,7 +29,7 @@ fn try_sign_rsa<H>(
where
H: Digest + AssociatedOid + FixedOutputReset,
{
let mut rng = rand::thread_rng();
let mut rng = rand::rng();
let private_key = rsa::RsaPrivateKey::from_pkcs1_der(encoding_key.as_bytes())
.map_err(signature::Error::from_source)?;
if pss {
Expand Down Expand Up @@ -57,7 +57,7 @@ fn verify_rsa<S: SignatureScheme, H: Digest + AssociatedOid>(
.map_err(signature::Error::from_source)?;
}
DecodingKeyKind::RsaModulusExponent { n, e } => {
RsaPublicKey::new(BigUint::from_bytes_be(n), BigUint::from_bytes_be(e))?
RsaPublicKey::new(BoxedUint::from_be_slice_vartime(n), BoxedUint::from_be_slice_vartime(e))?
.verify(scheme, &digest, signature)
.map_err(signature::Error::from_source)?;
}
Expand Down Expand Up @@ -115,7 +115,7 @@ macro_rules! define_rsa_verifier {
signature: &Vec<u8>,
) -> std::result::Result<(), signature::Error> {
if $pss {
verify_rsa::<Pss, $hash>(Pss::new::<$hash>(), &self.0, msg, signature)
verify_rsa::<Pss<$hash>, $hash>(Pss::<$hash>::new(), &self.0, msg, signature)
} else {
verify_rsa::<_, $hash>(Pkcs1v15Sign::new::<$hash>(), &self.0, msg, signature)
}
Expand Down
Loading
Loading