Skip to content

Commit c6b5c79

Browse files
committed
Merge branch 'chenzhenjia-wasm_support' into new-backends
2 parents 98a18f1 + c55c12d commit c6b5c79

File tree

20 files changed

+400
-213
lines changed

20 files changed

+400
-213
lines changed

.github/workflows/ci.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,12 @@ jobs:
5454
- name: Build System Info
5555
run: rustc --version
5656

57+
- name: Downgrade bumpalo
58+
run: cargo update -p [email protected] --precise 3.14.0
59+
60+
- name: Downgrade half
61+
run: cargo update -p [email protected] --precise 2.2.1
62+
5763
- name: Run tests default features
5864
run: cargo test
5965

Cargo.toml

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,21 @@ base64 = "0.22"
2727
pem = { version = "3", optional = true }
2828
simple_asn1 = { version = "0.6", optional = true }
2929

30-
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
31-
ring = { version = "0.17.4", features = ["std"] }
32-
30+
hmac = "0.12.1"
31+
rsa = "0.9.6"
32+
sha2 = { version = "0.10.7", features = ["oid"] }
33+
getrandom = { version = "0.2.10", features = ["js"] }
34+
rand = { version = "0.8.5", features = ["std"], default-features = false }
35+
ed25519-dalek = { version = "2.1.1" }
36+
p256 = { version = "0.13.2", features = ["ecdsa"] }
37+
p384 = { version = "0.13.0", features = ["ecdsa"] }
38+
rand_core = "0.6.4"
3339
[target.'cfg(target_arch = "wasm32")'.dependencies]
3440
js-sys = "0.3"
35-
ring = { version = "0.17.4", features = ["std", "wasm32_unknown_unknown_js"] }
3641

3742
[dev-dependencies]
3843
wasm-bindgen-test = "0.3.1"
39-
44+
ed25519-dalek = { version = "2.1.1", features = ["pkcs8", "rand_core"] }
4045
[target.'cfg(not(all(target_arch = "wasm32", not(any(target_os = "emscripten", target_os = "wasi")))))'.dev-dependencies]
4146
# For the custom time example
4247
time = "0.3"
@@ -49,7 +54,7 @@ criterion = { version = "0.4", default-features = false }
4954

5055
[features]
5156
default = ["use_pem"]
52-
use_pem = ["pem", "simple_asn1"]
57+
use_pem = ["pem", "simple_asn1", 'p256/pem', 'p384/pem']
5358

5459
[[bench]]
5560
name = "jwt"

examples/custom_time.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
use jsonwebtoken::{Algorithm, DecodingKey, EncodingKey, Header, Validation};
21
use serde::{Deserialize, Serialize};
32
use time::{Duration, OffsetDateTime};
43

4+
use jsonwebtoken::{Algorithm, DecodingKey, EncodingKey, Header, Validation};
5+
56
const SECRET: &str = "some-secret";
67

78
#[derive(Debug, PartialEq, Serialize, Deserialize)]
@@ -60,13 +61,15 @@ mod jwt_numeric_date {
6061

6162
#[cfg(test)]
6263
mod tests {
63-
const EXPECTED_TOKEN: &str = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJDdXN0b20gT2Zmc2V0RGF0ZVRpbWUgc2VyL2RlIiwiaWF0IjowLCJleHAiOjMyNTAzNjgwMDAwfQ.BcPipupP9oIV6uFRI6Acn7FMLws_wA3oo6CrfeFF3Gg";
64+
use time::{Duration, OffsetDateTime};
6465

65-
use super::super::{Claims, SECRET};
6666
use jsonwebtoken::{
6767
decode, encode, Algorithm, DecodingKey, EncodingKey, Header, Validation,
6868
};
69-
use time::{Duration, OffsetDateTime};
69+
70+
use super::super::{Claims, SECRET};
71+
72+
const EXPECTED_TOKEN: &str = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJDdXN0b20gT2Zmc2V0RGF0ZVRpbWUgc2VyL2RlIiwiaWF0IjowLCJleHAiOjMyNTAzNjgwMDAwfQ.BcPipupP9oIV6uFRI6Acn7FMLws_wA3oo6CrfeFF3Gg";
7073

7174
#[test]
7275
fn round_trip() {

examples/ed25519.rs

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1+
use ed25519_dalek::pkcs8::EncodePrivateKey;
2+
use ed25519_dalek::SigningKey;
3+
use rand_core::OsRng;
4+
use serde::{Deserialize, Serialize};
5+
16
use jsonwebtoken::{
27
decode, encode, get_current_timestamp, Algorithm, DecodingKey, EncodingKey, Validation,
38
};
4-
use ring::signature::{Ed25519KeyPair, KeyPair};
5-
use serde::{Deserialize, Serialize};
69

710
#[derive(Debug, Serialize, Deserialize)]
811
pub struct Claims {
@@ -11,11 +14,16 @@ pub struct Claims {
1114
}
1215

1316
fn main() {
14-
let doc = Ed25519KeyPair::generate_pkcs8(&ring::rand::SystemRandom::new()).unwrap();
15-
let encoding_key = EncodingKey::from_ed_der(doc.as_ref());
17+
let signing_key = SigningKey::generate(&mut OsRng);
18+
let pkcs8 = signing_key.to_pkcs8_der().unwrap();
19+
let pkcs8 = pkcs8.as_bytes();
20+
// The `to_pkcs8_der` includes the public key, the first 48 bits are the private key.
21+
let pkcs8 = &pkcs8[..48];
22+
let encoding_key = EncodingKey::from_ed_der(pkcs8);
1623

17-
let pair = Ed25519KeyPair::from_pkcs8(doc.as_ref()).unwrap();
18-
let decoding_key = DecodingKey::from_ed_der(pair.public_key().as_ref());
24+
let verifying_key = signing_key.verifying_key();
25+
let public_key = verifying_key.as_bytes();
26+
let decoding_key = DecodingKey::from_ed_der(public_key);
1927

2028
let claims = Claims { sub: "test".to_string(), exp: get_current_timestamp() };
2129

@@ -37,11 +45,17 @@ mod tests {
3745

3846
impl Jot {
3947
fn new() -> Jot {
40-
let doc = Ed25519KeyPair::generate_pkcs8(&ring::rand::SystemRandom::new()).unwrap();
41-
let encoding_key = EncodingKey::from_ed_der(doc.as_ref());
48+
let signing_key = SigningKey::generate(&mut OsRng);
49+
let pkcs8 = signing_key.to_pkcs8_der().unwrap();
50+
let pkcs8 = pkcs8.as_bytes();
51+
// The `to_pkcs8_der` includes the public key, the first 48 bits are the private key.
52+
let pkcs8 = &pkcs8[..48];
53+
let encoding_key = EncodingKey::from_ed_der(&pkcs8);
54+
55+
let verifying_key = signing_key.verifying_key();
56+
let public_key = verifying_key.as_bytes();
57+
let decoding_key = DecodingKey::from_ed_der(public_key);
4258

43-
let pair = Ed25519KeyPair::from_pkcs8(doc.as_ref()).unwrap();
44-
let decoding_key = DecodingKey::from_ed_der(pair.public_key().as_ref());
4559
Jot { encoding_key, decoding_key }
4660
}
4761
}

examples/validation.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1+
use serde::{Deserialize, Serialize};
2+
13
use jsonwebtoken::errors::ErrorKind;
24
use jsonwebtoken::{decode, encode, Algorithm, DecodingKey, EncodingKey, Header, Validation};
3-
use serde::{Deserialize, Serialize};
45

56
#[derive(Debug, Serialize, Deserialize)]
67
struct Claims {

src/algorithms.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
use crate::errors::{Error, ErrorKind, Result};
2-
use serde::{Deserialize, Serialize};
31
use std::str::FromStr;
42

3+
use serde::{Deserialize, Serialize};
4+
5+
use crate::errors::{Error, ErrorKind, Result};
6+
57
#[derive(Debug, Eq, PartialEq, Copy, Clone, Serialize, Deserialize)]
68
pub(crate) enum AlgorithmFamily {
79
Hmac,

src/crypto/ecdsa.rs

Lines changed: 60 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,74 @@
1-
use ring::{rand, signature};
2-
31
use crate::algorithms::Algorithm;
42
use crate::errors::Result;
5-
use crate::serialization::b64_encode;
3+
use crate::serialization::{b64_decode, b64_encode};
64

7-
/// Only used internally when validating EC, to map from our enum to the Ring EcdsaVerificationAlgorithm structs.
8-
pub(crate) fn alg_to_ec_verification(
9-
alg: Algorithm,
10-
) -> &'static signature::EcdsaVerificationAlgorithm {
5+
/// The actual ECDSA signing + encoding
6+
/// The key needs to be in PKCS8 format
7+
pub(crate) fn sign(alg: Algorithm, key: &[u8], message: &[u8]) -> Result<String> {
118
match alg {
12-
Algorithm::ES256 => &signature::ECDSA_P256_SHA256_FIXED,
13-
Algorithm::ES384 => &signature::ECDSA_P384_SHA384_FIXED,
9+
Algorithm::ES256 => es256_sign(key, message),
10+
Algorithm::ES384 => es384_sign(key, message),
11+
1412
_ => unreachable!("Tried to get EC alg for a non-EC algorithm"),
1513
}
1614
}
1715

18-
/// Only used internally when signing EC, to map from our enum to the Ring EcdsaVerificationAlgorithm structs.
19-
pub(crate) fn alg_to_ec_signing(alg: Algorithm) -> &'static signature::EcdsaSigningAlgorithm {
16+
fn es256_sign(key: &[u8], message: &[u8]) -> Result<String> {
17+
use p256::ecdsa::signature::Signer;
18+
use p256::ecdsa::{Signature, SigningKey};
19+
use p256::pkcs8::DecodePrivateKey;
20+
use p256::SecretKey;
21+
let secret_key =
22+
SecretKey::from_pkcs8_der(key).map_err(|_e| crate::errors::ErrorKind::InvalidEcdsaKey)?;
23+
let signing_key: SigningKey = secret_key.into();
24+
25+
let signature: Signature = signing_key.sign(message);
26+
let bytes = signature.to_bytes();
27+
Ok(b64_encode(bytes))
28+
}
29+
30+
fn es384_sign(key: &[u8], message: &[u8]) -> Result<String> {
31+
use p384::ecdsa::signature::Signer;
32+
use p384::ecdsa::{Signature, SigningKey};
33+
use p384::pkcs8::DecodePrivateKey;
34+
use p384::SecretKey;
35+
let secret_key =
36+
SecretKey::from_pkcs8_der(key).map_err(|_e| crate::errors::ErrorKind::InvalidEcdsaKey)?;
37+
let signing_key: SigningKey = secret_key.into();
38+
let signature: Signature = signing_key.sign(message);
39+
let bytes = signature.to_bytes();
40+
Ok(b64_encode(bytes))
41+
}
42+
43+
pub(crate) fn verify(alg: Algorithm, signature: &str, message: &[u8], key: &[u8]) -> Result<bool> {
2044
match alg {
21-
Algorithm::ES256 => &signature::ECDSA_P256_SHA256_FIXED_SIGNING,
22-
Algorithm::ES384 => &signature::ECDSA_P384_SHA384_FIXED_SIGNING,
45+
Algorithm::ES256 => es256_verify(signature, message, key),
46+
Algorithm::ES384 => es384_verify(signature, message, key),
2347
_ => unreachable!("Tried to get EC alg for a non-EC algorithm"),
2448
}
2549
}
2650

27-
/// The actual ECDSA signing + encoding
28-
/// The key needs to be in PKCS8 format
29-
pub fn sign(
30-
alg: &'static signature::EcdsaSigningAlgorithm,
31-
key: &[u8],
32-
message: &[u8],
33-
) -> Result<String> {
34-
let rng = rand::SystemRandom::new();
35-
let signing_key = signature::EcdsaKeyPair::from_pkcs8(alg, key, &rng)?;
36-
let out = signing_key.sign(&rng, message)?;
37-
Ok(b64_encode(out))
51+
fn es384_verify(signature: &str, message: &[u8], key: &[u8]) -> Result<bool> {
52+
use p384::ecdsa::signature::Verifier;
53+
use p384::ecdsa::{Signature, VerifyingKey};
54+
use p384::PublicKey;
55+
56+
let public_key =
57+
PublicKey::from_sec1_bytes(key).map_err(|_e| crate::errors::ErrorKind::InvalidEcdsaKey)?;
58+
let verifying_key: VerifyingKey = public_key.into();
59+
let signature = Signature::from_slice(&b64_decode(signature)?)
60+
.map_err(|_e| crate::errors::ErrorKind::InvalidSignature)?;
61+
Ok(verifying_key.verify(message, &signature).is_ok())
62+
}
63+
64+
fn es256_verify(signature: &str, message: &[u8], key: &[u8]) -> Result<bool> {
65+
use p256::ecdsa::signature::Verifier;
66+
use p256::ecdsa::{Signature, VerifyingKey};
67+
use p256::PublicKey;
68+
let public_key =
69+
PublicKey::from_sec1_bytes(key).map_err(|_e| crate::errors::ErrorKind::InvalidEcdsaKey)?;
70+
let verifying_key: VerifyingKey = public_key.into();
71+
let signature = Signature::from_slice(&b64_decode(signature)?)
72+
.map_err(|_e| crate::errors::ErrorKind::InvalidSignature)?;
73+
Ok(verifying_key.verify(message, &signature).is_ok())
3874
}

src/crypto/eddsa.rs

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,29 @@
1-
use ring::signature;
1+
use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey};
22

3-
use crate::algorithms::Algorithm;
4-
use crate::errors::Result;
5-
use crate::serialization::b64_encode;
3+
use crate::errors::{new_error, ErrorKind, Result};
4+
use crate::serialization::{b64_decode, b64_encode};
65

7-
/// Only used internally when signing or validating EdDSA, to map from our enum to the Ring EdDSAParameters structs.
8-
pub(crate) fn alg_to_ec_verification(alg: Algorithm) -> &'static signature::EdDSAParameters {
9-
// To support additional key subtypes, like Ed448, we would need to match on the JWK's ("crv")
10-
// parameter.
11-
match alg {
12-
Algorithm::EdDSA => &signature::ED25519,
13-
_ => unreachable!("Tried to get EdDSA alg for a non-EdDSA algorithm"),
14-
}
6+
fn parse_key(key: &[u8]) -> Result<SigningKey> {
7+
let key = key.try_into().map_err(|_| new_error(ErrorKind::InvalidEddsaKey))?;
8+
let signing_key = SigningKey::from_bytes(key);
9+
Ok(signing_key)
10+
}
11+
12+
pub(crate) fn verify(signature: &str, message: &[u8], key: &[u8]) -> Result<bool> {
13+
let signature = b64_decode(signature)?;
14+
let signature =
15+
Signature::from_slice(&signature).map_err(|_e| new_error(ErrorKind::InvalidSignature))?;
16+
let key = key.try_into().map_err(|_| new_error(ErrorKind::InvalidEddsaKey))?;
17+
let verifying_key =
18+
VerifyingKey::from_bytes(key).map_err(|_| new_error(ErrorKind::InvalidEddsaKey))?;
19+
Ok(verifying_key.verify(message, &signature).is_ok())
1520
}
1621

1722
/// The actual EdDSA signing + encoding
1823
/// The key needs to be in PKCS8 format
1924
pub fn sign(key: &[u8], message: &[u8]) -> Result<String> {
20-
let signing_key = signature::Ed25519KeyPair::from_pkcs8_maybe_unchecked(key)?;
25+
let key = key[16..].into();
26+
let signing_key = parse_key(key)?;
2127
let out = signing_key.sign(message);
22-
Ok(b64_encode(out))
28+
Ok(b64_encode(out.to_bytes()))
2329
}

0 commit comments

Comments
 (0)