Skip to content
Closed
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
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
backend: [ aws_lc_rs, rust_crypto ]
backend: [ aws_lc_rs, botan, rust_crypto ]
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@stable
Expand All @@ -37,7 +37,7 @@ jobs:
strategy:
matrix:
build: [ pinned, stable, nightly ]
backend: [ aws_lc_rs, rust_crypto ]
backend: [ aws_lc_rs, botan, rust_crypto ]
include:
- build: pinned
os: ubuntu-24.04
Expand Down
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ simple_asn1 = { version = "0.6", optional = true }
# "aws_lc_rs" feature
aws-lc-rs = { version = "1.10.0", optional = true }

# "botan" feature
botan = { git = "https://github.com/arckoor/botan-rs", optional = true, branch = "rsa-pkcs1-pubkey" }

# "rust_crypto" feature
ed25519-dalek = { version = "2.1.1", optional = true, features = ["pkcs8"] }
hmac = { version = "0.12.1", optional = true }
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jsonwebtoken = { version = "10", features = ["aws_lc_rs"] }
serde = {version = "1.0", features = ["derive"] }
```

Two crypto backends are available via features, `aws_lc_rs` and `rust_crypto`, exactly one of which must be enabled.
Three crypto backends are available via features, `aws_lc_rs`, `botan` and `rust_crypto`, exactly one of which must be enabled.

The minimum required Rust version (MSRV) is specified in the `rust-version` field in this project's [Cargo.toml](Cargo.toml).

Expand Down
107 changes: 107 additions & 0 deletions src/crypto/botan/ecdsa.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
use botan::{MPI, Privkey, Pubkey};
use signature::{Error, Signer, Verifier};

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

/// extract x and y from some DER bytes
fn extract_points(bytes: &[u8], curve: &str) -> Result<(MPI, MPI)> {
let point_length = match curve {
"secp256r1" => 32,
"secp384r1" => 48,
_ => unreachable!(),
};

if bytes.len() != 1 + 2 * point_length || bytes[0] != 4 {
return Err(ErrorKind::InvalidEcdsaKey.into());
}

let x_bytes = MPI::new_from_bytes(&bytes[1..point_length + 1])?;
let y_bytes = MPI::new_from_bytes(&bytes[point_length + 1..point_length * 2 + 1])?;

Ok((x_bytes, y_bytes))
}

macro_rules! define_ecdsa_signer {
($name:ident, $alg:expr, $padding:expr) => {
pub struct $name(Privkey);

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

Ok(Self(
Privkey::load_der(encoding_key.inner())
.map_err(|_| ErrorKind::InvalidEcdsaKey)?,
))
}
}

impl Signer<Vec<u8>> for $name {
fn try_sign(&self, msg: &[u8]) -> std::result::Result<Vec<u8>, Error> {
let mut rng =
botan::RandomNumberGenerator::new_system().map_err(Error::from_source)?;
let mut signer =
botan::Signer::new(&self.0, $padding).map_err(Error::from_source)?;
signer.update(msg).map_err(Error::from_source)?;
signer.finish(&mut rng).map_err(Error::from_source)
}
}

impl JwtSigner for $name {
fn algorithm(&self) -> Algorithm {
$alg
}
}
};
}

macro_rules! define_ecdsa_verifier {
($name:ident, $alg:expr, $padding:expr, $curve:expr) => {
pub struct $name(Pubkey);

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

let (x_bytes, y_bytes) = extract_points(decoding_key.as_bytes(), $curve)?;

Ok(Self(
Pubkey::load_ecdsa(&x_bytes, &y_bytes, $curve)
.map_err(|_| ErrorKind::InvalidEcdsaKey)?,
))
}
}

impl Verifier<Vec<u8>> for $name {
fn verify(&self, msg: &[u8], signature: &Vec<u8>) -> std::result::Result<(), Error> {
let mut verifier =
botan::Verifier::new(&self.0, $padding).map_err(Error::from_source)?;
verifier.update(msg).map_err(Error::from_source)?;
verifier
.finish(&signature)
.map_err(Error::from_source)?
.then_some(())
.ok_or(Error::new())
}
}

impl JwtVerifier for $name {
fn algorithm(&self) -> Algorithm {
$alg
}
}
};
}

define_ecdsa_signer!(Es256Signer, Algorithm::ES256, "SHA-256");
define_ecdsa_verifier!(Es256Verifier, Algorithm::ES256, "SHA-256", "secp256r1");

define_ecdsa_signer!(Es384Signer, Algorithm::ES384, "SHA-384");
define_ecdsa_verifier!(Es384Verifier, Algorithm::ES384, "SHA-384", "secp384r1");
63 changes: 63 additions & 0 deletions src/crypto/botan/eddsa.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
use botan::{Privkey, Pubkey};
use signature::{Error, Signer, Verifier};

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

pub struct EdDSASigner(Privkey);

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

Ok(Self(Privkey::load_der(encoding_key.inner()).map_err(|_| ErrorKind::InvalidEddsaKey)?))
}
}

impl Signer<Vec<u8>> for EdDSASigner {
fn try_sign(&self, msg: &[u8]) -> std::result::Result<Vec<u8>, Error> {
let mut rng = botan::RandomNumberGenerator::new_system().map_err(Error::from_source)?;
let mut signer = botan::Signer::new(&self.0, "Pure").map_err(Error::from_source)?;
signer.update(msg).map_err(Error::from_source)?;
signer.finish(&mut rng).map_err(Error::from_source)
}
}

impl JwtSigner for EdDSASigner {
fn algorithm(&self) -> Algorithm {
Algorithm::EdDSA
}
}

pub struct EdDSAVerifier(Pubkey);

impl EdDSAVerifier {
pub(crate) fn new(decoding_key: &DecodingKey) -> Result<Self> {
if decoding_key.family != AlgorithmFamily::Ed {
return Err(new_error(ErrorKind::InvalidKeyFormat));
}

Ok(Self(
Pubkey::load_ed25519(decoding_key.as_bytes())
.map_err(|_| ErrorKind::InvalidEddsaKey)?,
))
}
}

impl Verifier<Vec<u8>> for EdDSAVerifier {
fn verify(&self, msg: &[u8], signature: &Vec<u8>) -> std::result::Result<(), Error> {
let mut verifier = botan::Verifier::new(&self.0, "Pure").map_err(Error::from_source)?;
verifier.update(msg).map_err(Error::from_source)?;
verifier.finish(signature).map_err(Error::from_source)?.then_some(()).ok_or(Error::new())
}
}

impl JwtVerifier for EdDSAVerifier {
fn algorithm(&self) -> Algorithm {
Algorithm::EdDSA
}
}
82 changes: 82 additions & 0 deletions src/crypto/botan/hmac.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
use botan::MsgAuthCode;
use signature::{Error, Signer, Verifier};

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

macro_rules! define_hmac_signer {
($name:ident, $alg:expr, $algo:expr) => {
pub struct $name(Vec<u8>);

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

Ok(Self(encoding_key.try_get_hmac_secret()?.to_vec()))
}
}

impl Signer<Vec<u8>> for $name {
fn try_sign(&self, msg: &[u8]) -> std::result::Result<Vec<u8>, Error> {
let mut auth_code = MsgAuthCode::new($algo).map_err(Error::from_source)?;
auth_code.set_key(&self.0).map_err(Error::from_source)?;
auth_code.update(msg).map_err(Error::from_source)?;
auth_code.finish().map_err(Error::from_source)
}
}

impl JwtSigner for $name {
fn algorithm(&self) -> Algorithm {
$alg
}
}
};
}

macro_rules! define_hmac_verifier {
($name:ident, $alg:expr, $algo:expr) => {
pub struct $name(Vec<u8>);

impl $name {
pub(crate) fn new(decoding_key: &DecodingKey) -> Result<Self> {
if decoding_key.family != AlgorithmFamily::Hmac {
return Err(new_error(ErrorKind::InvalidKeyFormat));
}

Ok(Self(decoding_key.try_get_hmac_secret()?.to_vec()))
}
}

impl Verifier<Vec<u8>> for $name {
fn verify(&self, msg: &[u8], signature: &Vec<u8>) -> std::result::Result<(), Error> {
let mut auth_code = MsgAuthCode::new($algo).map_err(Error::from_source)?;
auth_code.set_key(&self.0).map_err(Error::from_source)?;
auth_code.update(msg).map_err(Error::from_source)?;
botan::const_time_compare(
&auth_code.finish().map_err(Error::from_source)?,
signature,
)
.then_some(())
.ok_or(Error::new())
}
}

impl JwtVerifier for $name {
fn algorithm(&self) -> Algorithm {
$alg
}
}
};
}

define_hmac_signer!(Hs256Signer, Algorithm::HS256, "HMAC(SHA-256)");
define_hmac_signer!(Hs384Signer, Algorithm::HS384, "HMAC(SHA-384)");
define_hmac_signer!(Hs512Signer, Algorithm::HS512, "HMAC(SHA-512)");

define_hmac_verifier!(Hs256Verifier, Algorithm::HS256, "HMAC(SHA-256)");
define_hmac_verifier!(Hs384Verifier, Algorithm::HS384, "HMAC(SHA-384)");
define_hmac_verifier!(Hs512Verifier, Algorithm::HS512, "HMAC(SHA-512)");
4 changes: 4 additions & 0 deletions src/crypto/botan/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub(crate) mod ecdsa;
pub(crate) mod eddsa;
pub(crate) mod hmac;
pub(crate) mod rsa;
101 changes: 101 additions & 0 deletions src/crypto/botan/rsa.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
use botan::{MPI, Privkey, Pubkey};
use signature::{Error, Signer, Verifier};

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

macro_rules! define_rsa_signer {
($name:ident, $alg:expr, $padding:expr) => {
pub struct $name(Privkey);

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

Ok(Self(
Privkey::load_rsa_pkcs1(encoding_key.inner())
.map_err(|_| ErrorKind::InvalidEcdsaKey)?,
))
}
}

impl Signer<Vec<u8>> for $name {
fn try_sign(&self, msg: &[u8]) -> std::result::Result<Vec<u8>, Error> {
let mut rng =
botan::RandomNumberGenerator::new_system().map_err(Error::from_source)?;
let mut signer =
botan::Signer::new(&self.0, $padding).map_err(Error::from_source)?;
signer.update(msg).map_err(Error::from_source)?;
signer.finish(&mut rng).map_err(Error::from_source)
}
}

impl JwtSigner for $name {
fn algorithm(&self) -> Algorithm {
$alg
}
}
};
}

macro_rules! define_rsa_verifier {
($name:ident, $alg:expr, $padding:expr) => {
pub struct $name(Pubkey);

impl $name {
pub(crate) fn new(decoding_key: &DecodingKey) -> Result<Self> {
if decoding_key.family != AlgorithmFamily::Rsa {
return Err(new_error(ErrorKind::InvalidKeyFormat));
}

let pubkey = match &decoding_key.kind {
DecodingKeyKind::SecretOrDer(items) => Pubkey::load_rsa_pkcs1(&items),
DecodingKeyKind::RsaModulusExponent { n, e } => {
Pubkey::load_rsa(&MPI::new_from_bytes(&n)?, &MPI::new_from_bytes(&e)?)
}
}
.map_err(|e| ErrorKind::InvalidRsaKey(e.to_string()))?;

Ok(Self(pubkey))
}
}

impl Verifier<Vec<u8>> for $name {
fn verify(&self, msg: &[u8], signature: &Vec<u8>) -> std::result::Result<(), Error> {
let mut verifier =
botan::Verifier::new(&self.0, $padding).map_err(Error::from_source)?;
verifier.update(msg).map_err(Error::from_source)?;
verifier
.finish(&signature)
.map_err(Error::from_source)?
.then_some(())
.ok_or(Error::new())
}
}

impl JwtVerifier for $name {
fn algorithm(&self) -> Algorithm {
$alg
}
}
};
}

define_rsa_signer!(Rsa256Signer, Algorithm::RS256, "PKCS1v15(SHA-256)");
define_rsa_signer!(Rsa384Signer, Algorithm::RS384, "PKCS1v15(SHA-384)");
define_rsa_signer!(Rsa512Signer, Algorithm::RS512, "PKCS1v15(SHA-512)");
define_rsa_signer!(RsaPss256Signer, Algorithm::PS256, "PSS(SHA-256)");
define_rsa_signer!(RsaPss384Signer, Algorithm::PS384, "PSS(SHA-384)");
define_rsa_signer!(RsaPss512Signer, Algorithm::PS512, "PSS(SHA-512)");

define_rsa_verifier!(Rsa256Verifier, Algorithm::RS256, "PKCS1v15(SHA-256)");
define_rsa_verifier!(Rsa384Verifier, Algorithm::RS384, "PKCS1v15(SHA-384)");
define_rsa_verifier!(Rsa512Verifier, Algorithm::RS512, "PKCS1v15(SHA-512)");
define_rsa_verifier!(RsaPss256Verifier, Algorithm::PS256, "PSS(SHA-256)");
define_rsa_verifier!(RsaPss384Verifier, Algorithm::PS384, "PSS(SHA-384)");
define_rsa_verifier!(RsaPss512Verifier, Algorithm::PS512, "PSS(SHA-512)");
Loading