Skip to content

Commit

Permalink
Merge pull request #3 from robjsliwa/wasm
Browse files Browse the repository at this point in the history
WebAssembly Build Compatibility
  • Loading branch information
robjsliwa committed Jan 16, 2024
2 parents 6c2b5eb + c41224c commit dfa7ab3
Show file tree
Hide file tree
Showing 11 changed files with 663 additions and 48 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,7 @@ jobs:
run: cargo build --verbose
- name: Run tests
run: cargo test --verbose
- name: Build without Ring
run: cargo build --no-default-features --features "noring"
- name: Run tests without Ring
run: cargo test --no-default-features --features "noring"
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
/target
/Cargo.lock
.vscode/
16 changes: 13 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "sdjwt"
version = "0.7.0"
version = "0.7.1"
authors = ["Rob Sliwa <[email protected]>"]
license = "MIT"
readme = "README.md"
Expand All @@ -17,8 +17,18 @@ thiserror = "1.0.51"
rand = "0.8.5"
base64 = "0.21.5"
sha2 = "0.10.8"
jsonwebtoken = "9.2.0"
chrono = "0.4.31"
rsa = "0.8"
getrandom = { version = "0.2.12", features = ["js"] }

jsonwebtoken-rustcrypto = { git = "https://github.com/JadedBlueEyes/jsonwebtoken", rev = "a7758b0", optional = true }
serde_plain = { version = "1.0.2", optional = true }

jsonwebtoken = { version = "9.2.0", optional = true }

[features]
default = ["ring"]
ring = ["jsonwebtoken"]
noring = ["jsonwebtoken-rustcrypto", "serde_plain"]

[dev-dependencies]
rsa = "0.5"
104 changes: 85 additions & 19 deletions src/decoding.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
use crate::{Algorithm, Error, Validation};
use jsonwebtoken::DecodingKey;
#[cfg(feature = "noring")]
use base64::Engine;
#[cfg(feature = "ring")]
use jsonwebtoken::{
decode as jwt_decode, Algorithm as JwtAlgorithm, DecodingKey, Validation as JwtValidation,
};

#[cfg(feature = "noring")]
use jsonwebtoken_rustcrypto::{
decode as jwt_decode, Algorithm as JwtAlgorithm, DecodingKey, Validation as JwtValidation,
};
#[cfg(feature = "noring")]
use rsa::PublicKeyParts;
#[cfg(feature = "noring")]
use rsa::{pkcs1::DecodeRsaPublicKey, RsaPublicKey};

use serde_json::Value;

#[derive(Clone)]
Expand All @@ -20,64 +35,84 @@ impl KeyForDecoding {
})
}

#[cfg(feature = "ring")]
pub fn from_rsa_pem(key: &[u8]) -> Result<Self, Error> {
Ok(KeyForDecoding {
key: DecodingKey::from_rsa_pem(key)?,
})
}

#[cfg(feature = "noring")]
pub fn from_rsa_pem(key: &[u8]) -> Result<Self, Error> {
let rsa_key = RsaPublicKey::from_pkcs1_pem(std::str::from_utf8(key)?)?;

let modulus =
base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(rsa_key.n().to_bytes_be());
let exponent =
base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(rsa_key.e().to_bytes_be());

Self::from_rsa_components(&modulus, &exponent)
}

pub fn from_rsa_components(modulus: &str, exponent: &str) -> Result<Self, Error> {
Ok(KeyForDecoding {
key: DecodingKey::from_rsa_components(modulus, exponent)?,
})
}

#[cfg(feature = "ring")]
pub fn from_ec_pem(key: &[u8]) -> Result<Self, Error> {
Ok(KeyForDecoding {
key: DecodingKey::from_ec_pem(key)?,
})
}

#[cfg(feature = "ring")]
pub fn from_ed_pem(key: &[u8]) -> Result<Self, Error> {
Ok(KeyForDecoding {
key: DecodingKey::from_ed_pem(key)?,
})
}

#[cfg(feature = "ring")]
pub fn from_rsa_der(der: &[u8]) -> Self {
KeyForDecoding {
key: DecodingKey::from_rsa_der(der),
}
}

#[cfg(feature = "ring")]
pub fn from_ec_der(der: &[u8]) -> Self {
KeyForDecoding {
key: DecodingKey::from_ec_der(der),
}
}

#[cfg(feature = "ring")]
pub fn from_ed_der(der: &[u8]) -> Self {
KeyForDecoding {
key: DecodingKey::from_ed_der(der),
}
}
}

fn build_validation(validation: &Validation) -> jsonwebtoken::Validation {
let mut valid = jsonwebtoken::Validation::new(match validation.algorithms {
Algorithm::HS256 => jsonwebtoken::Algorithm::HS256,
Algorithm::HS384 => jsonwebtoken::Algorithm::HS384,
Algorithm::HS512 => jsonwebtoken::Algorithm::HS512,
Algorithm::RS256 => jsonwebtoken::Algorithm::RS256,
Algorithm::RS384 => jsonwebtoken::Algorithm::RS384,
Algorithm::RS512 => jsonwebtoken::Algorithm::RS512,
Algorithm::ES256 => jsonwebtoken::Algorithm::ES256,
Algorithm::ES384 => jsonwebtoken::Algorithm::ES384,
Algorithm::PS256 => jsonwebtoken::Algorithm::PS256,
Algorithm::PS384 => jsonwebtoken::Algorithm::PS384,
Algorithm::PS512 => jsonwebtoken::Algorithm::PS512,
Algorithm::EdDSA => jsonwebtoken::Algorithm::EdDSA,
#[cfg(feature = "ring")]
fn build_validation(validation: &Validation) -> JwtValidation {
let mut valid = JwtValidation::new(match validation.algorithms {
Algorithm::HS256 => JwtAlgorithm::HS256,
Algorithm::HS384 => JwtAlgorithm::HS384,
Algorithm::HS512 => JwtAlgorithm::HS512,
Algorithm::RS256 => JwtAlgorithm::RS256,
Algorithm::RS384 => JwtAlgorithm::RS384,
Algorithm::RS512 => JwtAlgorithm::RS512,
Algorithm::ES256 => JwtAlgorithm::ES256,
Algorithm::ES384 => JwtAlgorithm::ES384,
Algorithm::PS256 => JwtAlgorithm::PS256,
Algorithm::PS384 => JwtAlgorithm::PS384,
Algorithm::PS512 => JwtAlgorithm::PS512,
Algorithm::EdDSA => JwtAlgorithm::EdDSA,
});

valid.required_spec_claims = validation.required_spec_claims.clone();
valid.leeway = validation.leeway;
valid.validate_exp = validation.validate_exp;
Expand All @@ -89,13 +124,39 @@ fn build_validation(validation: &Validation) -> jsonwebtoken::Validation {
valid
}

#[cfg(feature = "noring")]
fn build_validation(validation: &Validation) -> JwtValidation {
let mut valid = JwtValidation::new(match validation.algorithms {
Algorithm::HS256 => JwtAlgorithm::HS256,
Algorithm::HS384 => JwtAlgorithm::HS384,
Algorithm::HS512 => JwtAlgorithm::HS512,
Algorithm::RS256 => JwtAlgorithm::RS256,
Algorithm::RS384 => JwtAlgorithm::RS384,
Algorithm::RS512 => JwtAlgorithm::RS512,
Algorithm::ES256 => JwtAlgorithm::ES256,
Algorithm::ES384 => JwtAlgorithm::ES384,
Algorithm::PS256 => JwtAlgorithm::PS256,
Algorithm::PS384 => JwtAlgorithm::PS384,
Algorithm::PS512 => JwtAlgorithm::PS512,
Algorithm::EdDSA => JwtAlgorithm::EdDSA,
});

valid.leeway = validation.leeway;
valid.validate_exp = validation.validate_exp;
valid.validate_nbf = validation.validate_nbf;
valid.aud = validation.aud.clone();
valid.iss = validation.iss.clone().and_then(|mut hs| hs.drain().next());
valid.sub = validation.sub.clone();
valid
}

pub fn decode(
token: &str,
key: &KeyForDecoding,
validation: &Validation,
) -> Result<(Value, Value), Error> {
let validation = build_validation(validation);
let token_data = jsonwebtoken::decode(token, &key.key, &validation)?;
let token_data = jwt_decode(token, &key.key, &validation)?;
let header: Value = serde_json::from_str(&serde_json::to_string(&token_data.header)?)?;
Ok((header, token_data.claims))
}
Expand Down Expand Up @@ -124,7 +185,9 @@ mod tests {
use super::*;
use chrono::{Duration, Utc};
use rand::rngs::OsRng;
use rsa::{pkcs1::ToRsaPublicKey, pkcs8::ToPrivateKey, RsaPrivateKey, RsaPublicKey};
use rsa::pkcs1::EncodeRsaPublicKey;
use rsa::pkcs8::EncodePrivateKey;
use rsa::{RsaPrivateKey, RsaPublicKey};

const TEST_CLAIMS: &str = r#"{
"sub": "user_42",
Expand Down Expand Up @@ -158,8 +221,11 @@ mod tests {

fn convert_to_pem(private_key: RsaPrivateKey, public_key: RsaPublicKey) -> (String, String) {
(
private_key.to_pkcs8_pem().unwrap().to_string(),
public_key.to_pkcs1_pem().unwrap(),
private_key
.to_pkcs8_pem(rsa::pkcs8::LineEnding::CR)
.unwrap()
.to_string(),
public_key.to_pkcs1_pem(rsa::pkcs1::LineEnding::CR).unwrap(),
)
}

Expand Down
Loading

0 comments on commit dfa7ab3

Please sign in to comment.