From fa58c198a870279ba4d742a2a0a2055c4330409f Mon Sep 17 00:00:00 2001 From: Justus K Date: Sat, 3 May 2025 12:34:56 +0200 Subject: [PATCH 1/5] feat(tests): start working on new integration tests by refactoring jwk tests --- .config/nextest.toml | 3 + Cargo.lock | 23 + Cargo.toml | 8 +- devenv.nix | 46 ++ src/jwk/serde_impl.rs | 6 +- tests/common/mod.rs | 16 + tests/jwk.rs | 675 +++++++++++------- tests/jws.rs | 467 ------------ tests/keys/hs256.json | 6 - .../keys/key_ops_duplicates_rsa_enc.pub.json | 14 - tests/vectors/jwk/3_1.ec_public_key.json | 8 + tests/vectors/jwk/3_2.ec_private_key.json | 9 + tests/vectors/jwk/3_3.rsa_public_key.json | 7 + tests/vectors/jwk/3_4.rsa_private_key.json | 13 + .../3_5.symmetric_key_mac_computation.json} | 0 .../jwk/3_6.symmetric_key_encryption.json | 7 + tests/{keys => vectors/jwk}/ed25519.json | 2 +- tests/{keys => vectors/jwk}/ed25519.pub.json | 0 .../jwk_optional_parameters_rsa_enc.pub.json | 0 .../jwk_optional_parameters_rsa_sig.pub.json | 0 .../jwk}/key_ops_rsa_enc.pub.json | 0 tests/{keys => vectors/jwk}/p256.json | 0 tests/{keys => vectors/jwk}/p256.pub.json | 0 tests/{keys => vectors/jwk}/p384.json | 0 tests/{keys => vectors/jwk}/p384.pub.json | 0 tests/{keys => vectors/jwk}/rsa.json | 0 tests/{keys => vectors/jwk}/rsa.pub.json | 0 27 files changed, 547 insertions(+), 763 deletions(-) create mode 100644 .config/nextest.toml create mode 100644 tests/common/mod.rs delete mode 100644 tests/keys/hs256.json delete mode 100644 tests/keys/key_ops_duplicates_rsa_enc.pub.json create mode 100644 tests/vectors/jwk/3_1.ec_public_key.json create mode 100644 tests/vectors/jwk/3_2.ec_private_key.json create mode 100644 tests/vectors/jwk/3_3.rsa_public_key.json create mode 100644 tests/vectors/jwk/3_4.rsa_private_key.json rename tests/{keys/cookbook_hs256.json => vectors/jwk/3_5.symmetric_key_mac_computation.json} (100%) create mode 100644 tests/vectors/jwk/3_6.symmetric_key_encryption.json rename tests/{keys => vectors/jwk}/ed25519.json (99%) rename tests/{keys => vectors/jwk}/ed25519.pub.json (100%) rename tests/{keys => vectors/jwk}/jwk_optional_parameters_rsa_enc.pub.json (100%) rename tests/{keys => vectors/jwk}/jwk_optional_parameters_rsa_sig.pub.json (100%) rename tests/{keys => vectors/jwk}/key_ops_rsa_enc.pub.json (100%) rename tests/{keys => vectors/jwk}/p256.json (100%) rename tests/{keys => vectors/jwk}/p256.pub.json (100%) rename tests/{keys => vectors/jwk}/p384.json (100%) rename tests/{keys => vectors/jwk}/p384.pub.json (100%) rename tests/{keys => vectors/jwk}/rsa.json (100%) rename tests/{keys => vectors/jwk}/rsa.pub.json (100%) diff --git a/.config/nextest.toml b/.config/nextest.toml new file mode 100644 index 0000000..5aa8a7d --- /dev/null +++ b/.config/nextest.toml @@ -0,0 +1,3 @@ +# Profile to only run `tests/jwk.rs` tests. +[profile.jwk] +default-filter = "binary_id(jose::jwk)" diff --git a/Cargo.lock b/Cargo.lock index 3da5ad3..faf8862 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -330,6 +330,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + [[package]] name = "digest" version = "0.10.7" @@ -664,6 +670,7 @@ dependencies = [ "openssl-sys", "p256", "p384", + "pretty_assertions", "rand_core", "ring", "rsa", @@ -956,6 +963,16 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "pretty_assertions" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" +dependencies = [ + "diff", + "yansi", +] + [[package]] name = "prettyplease" version = "0.2.32" @@ -1595,6 +1612,12 @@ dependencies = [ "bitflags", ] +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + [[package]] name = "zerocopy" version = "0.8.24" diff --git a/Cargo.toml b/Cargo.toml index 68c59d9..a6aa09b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ categories = [ ] [features] -default = [] +default = ["crypto-openssl"] std = [ "thiserror/std", "fluent-uri/std", @@ -130,6 +130,9 @@ eyre = "0.6.12" clap = { version = "4.5.27", features = ["derive"] } clio = { version = "0.3.5", features = ["clap-parse"] } +# For integration tests +pretty_assertions = "1.4.1" + # For X.509 certificates of Json Web Keys # x509-cert = { git = "https://github.com/RustCrypto/formats.git" } @@ -140,3 +143,6 @@ required-features = ["std"] [package.metadata.docs.rs] features = ["crypto-rustcrypto", "std", "deterministic-ecdsa"] rustdoc-args = ["--cfg", "docsrs"] + +[profile.dev.package.num-bigint-dig] +opt-level = 3 diff --git a/devenv.nix b/devenv.nix index 25a6d61..55a5067 100644 --- a/devenv.nix +++ b/devenv.nix @@ -7,6 +7,9 @@ cargo-udeps cargo-hack cargo-nextest + cargo-tarpaulin + + jose openssl pkg-config @@ -30,4 +33,47 @@ "rust-src" ]; }; + + scripts = { + each-crypto.exec = '' + CRYPTO_FEATURES="$(cat Cargo.toml | grep -o 'crypto-[^ ]* =' | tr -d ' =' | paste -sd ',')" + + cargo hack \ + --each-feature \ + --include-features $CRYPTO_FEATURES \ + "$@" + ''; + + jwk-thumbprints.exec = '' + # write a script, that takes a list of files as arguments + # and calculates sha256, sha384 and sha512 thumbprints + # for each file, and prints the result in a table + # the script should be called jwk-thumbprints + + files="$@" + + if [ -z "$files" ]; then + echo "No files provided" + exit 1 + fi + + for file in $files; do + if [ ! -f "$file" ]; then + echo "$file is not a file" + continue + fi + + echo "$file" + + sha256=$(jose jwk thp -a S256 -i "$file") + sha384=$(jose jwk thp -a S384 -i "$file") + sha512=$(jose jwk thp -a S512 -i "$file") + + echo -e "\tSHA256: $sha256" + echo -e "\tSHA384: $sha384" + echo -e "\tSHA512: $sha512" + echo -e "\n" + done + ''; + }; } diff --git a/src/jwk/serde_impl.rs b/src/jwk/serde_impl.rs index c29bb6c..4993ed6 100644 --- a/src/jwk/serde_impl.rs +++ b/src/jwk/serde_impl.rs @@ -1,4 +1,4 @@ -use alloc::vec::Vec; +use alloc::{borrow::Cow, vec::Vec}; use core::ops::Deref; use base64ct::{Base64, Base64UrlUnpadded, Encoding}; @@ -53,10 +53,10 @@ pub fn deserialize_ga<'de, D, const N: usize>(deserializer: D) -> Result, { - Ok(match Option::<&str>::deserialize(deserializer)? { + Ok(match Option::>::deserialize(deserializer)? { Some(val) => { let mut buf = [0u8; N]; - Base64UrlUnpadded::decode(val, &mut buf).map_err(::custom)?; + Base64UrlUnpadded::decode(&*val, &mut buf).map_err(::custom)?; Some(buf) } None => None, diff --git a/tests/common/mod.rs b/tests/common/mod.rs new file mode 100644 index 0000000..47143ba --- /dev/null +++ b/tests/common/mod.rs @@ -0,0 +1,16 @@ +//! Common test helpers. + +use serde_json::Value; + +pub type TestResult = Result>; + +/// Reads a key file from the `tests/vectors/jwk` directory. +pub fn read_jwk(name: &str) -> TestResult { + let json = std::fs::read_to_string(format!( + "{}/tests/vectors/jwk/{name}.json", + env!("CARGO_MANIFEST_DIR"), + ))?; + let key: Value = serde_json::from_str(&json)?; + + Ok(key) +} diff --git a/tests/jwk.rs b/tests/jwk.rs index f2906f3..f3a2a39 100644 --- a/tests/jwk.rs +++ b/tests/jwk.rs @@ -1,351 +1,484 @@ use jose::{ crypto::hmac, - jwa::{EcDSA, Hmac, JsonWebAlgorithm, JsonWebSigningAlgorithm}, - jwk::{ - policy::{Checkable, Checked, StandardPolicy}, - symmetric::{FromOctetSequenceError, OctetSequence}, - AsymmetricJsonWebKey, EcPrivate, EcPublic, FromKey as _, JsonWebKey, JsonWebKeyType, - JwkSigner, OkpPrivate, Private, Public, Thumbprint, - }, - jws::Signer, - Base64UrlString, + jwa, + jwk::{self, FromKey as _, Thumbprint as _}, + Base64UrlString, JsonWebKey, }; -use serde_json::json; - -fn read_key_file(name: &str) -> String { - std::fs::read_to_string(format!( - "{}/tests/keys/{}.json", - env!("CARGO_MANIFEST_DIR"), - name, - )) - .unwrap() -} +use pretty_assertions::assert_eq; + +use crate::common::{read_jwk, TestResult}; -macro_rules! key_roundtrip_test { - ($priv:ty, $pub:ty, $file:literal, [$($priv_field:literal),*$(,)?], [$($pub_field:literal),*$(,)?]$(,)?) => { - #[test] - fn private_key_serialisation_roundtrip() { - let json_str = crate::read_key_file($file); - let json = serde_json::from_str::(&json_str).unwrap(); +mod common; - let key: $priv = serde_json::from_str(&json_str).unwrap(); +fn roundtrip(file: &str, unsupported: bool, check: fn(&JsonWebKey)) -> TestResult { + let mut json_key = read_jwk(file)?; + let json_key_str = serde_json::to_string(&json_key)?; - let json2 = serde_json::to_value(&key).unwrap(); + let jwk = match serde_json::from_value::(json_key.clone()) { + Ok(..) if unsupported => panic!("Unsupported key type was successful"), + Ok(jwk) => jwk, + Err(..) if unsupported => return Ok(()), + Err(e) => return Err(e.into()), + }; + check(&jwk); - let key2: $priv = serde_json::from_value(json2.clone()).unwrap(); - assert_eq!(key, key2); + // we want to deserialize the JWK from an owned value, and a borrowed value + let jwk_from_str = serde_json::from_str::(&json_key_str)?; + assert_eq!(jwk.key_type(), jwk_from_str.key_type()); - assert_eq!(json["kty"], json2["kty"]); + let mut serialized = serde_json::to_value(&jwk)?; + let mut serialized_from_str = serde_json::to_value(&jwk_from_str)?; - $(assert_eq!(json[$priv_field], json2[$priv_field]);)* + // removes the field `name` from the given json value, mostly because some + // fields are serialized non-deterministic (key_ops), because the order is + // random + let remove_field = |value: &mut serde_json::Value, name: &str| { + if let Some(obj) = value.as_object_mut() { + obj.remove(name); } + }; + + let mut remove_field_from_all = |name: &str| { + remove_field(&mut json_key, name); + remove_field(&mut serialized, name); + remove_field(&mut serialized_from_str, name); + }; - #[test] - fn public_key_serialisation_roundtrip() { - let json_str = crate::read_key_file(&format!("{}.pub", $file)); - let json = serde_json::from_str::(&json_str).unwrap(); + remove_field_from_all("key_ops"); - let key: $pub = serde_json::from_str(&json_str).unwrap(); + assert_eq!(json_key, serialized); + assert_eq!(json_key, serialized_from_str); - let json2 = serde_json::to_value(&key).unwrap(); + Ok(()) +} - let key2: $pub = serde_json::from_value(json2.clone()).unwrap(); - assert_eq!(key, key2); +fn roundtrip_pair(private: &str, public: &str, check: fn(&JsonWebKey)) -> TestResult { + roundtrip(private, false, check)?; + roundtrip(public, false, check)?; - assert_eq!(json["kty"], json2["kty"]); - $(assert_eq!(json[$pub_field], json2[$pub_field]);)* - } - }; + Ok(()) } -pub mod rsa { - use jose::crypto::rsa; +fn assert_thumbprint(jwk: &JsonWebKey, sha256: &str, sha384: &str, sha512: &str) { + let print_sha256 = Base64UrlString::encode(jwk.thumbprint_sha256()); + assert_eq!(&*print_sha256, sha256); - key_roundtrip_test! { - rsa::PrivateKey, - rsa::PublicKey, - "rsa", - ["n", "e", "d", "p", "q", "dp", "dq", "di"], - ["n", "e"], - } + let print_sha384 = Base64UrlString::encode(jwk.thumbprint_sha384()); + assert_eq!(&*print_sha384, sha384); + + let print_sha512 = Base64UrlString::encode(jwk.thumbprint_sha512()); + assert_eq!(&*print_sha512, sha512); +} + +pub mod roundtrip { + use jose::{ + jwa, + jwk::{self, AsymmetricJsonWebKey, JsonWebKeyType}, + }; + use pretty_assertions::assert_eq; + + use crate::{assert_thumbprint, common::TestResult, roundtrip, roundtrip_pair}; #[test] - fn deny_key_with_other_primes() { - let json = r#"{ - "use":"sig", - "kty":"RSA", - "kid":"nYPs6qc5zj3VOVKr4yY-EzirO-AcdUl0JC5bcXKGE6Y", - "alg":"RS256", - "n":"vxmzEVX_Fus8i8BWT_sC_m389t615iPxKSMavPFv0xEhES42RWkO6yNpb_cwWhlJtoy_UdiRW8-0DHYJIbpiwkw4oRRnfMYX4FU77yjovSQLEhKPfIuYDBuP-9LQgF8_NgB9z1WokSUcH-tAf_35MpiXptGxoFuIe1EE7u1TWpTnMwGBnqO1EvJltyWej9R6rt47oqizn1VN8P2No3tys181B_TE9c6N2tXzWpnm5QY7UZO2zPLtYFGbj7hJCNhc5SL3vt-81KkaNjPcW1rgCIoRKvHVI79n_H81LQdiJDJyIobtvPH3XFQa_tM4CP2ul121E_Zi0tcjuD1zyLCq7Q", - "e":"AQAB", - "d":"n0FzkYbxRtBTbMOlKpItNIvEvJdtT5W0bGvs5HjwkB0-SWsRn1amMB8ax0xg5zUb0R4KctLgkHrPuXLEuW7yzqlmqBaxB7KuQy3E_NJC4x0efLkrCsfqtmxh2aMeT10Q-JgAQMFJ8WvTvGX5IrEs85VnDIbEWLbvTpV-Xv8478qjjt4v91wyhlzZW8A5-fsluy9faZDgKm3a3RaAKzP15ouffYf3ui5CTckXB-50fcrDasNEYlXzUzS1rHgQjF16ApjN7WupFP-FnlDkxudt6VfWmErakQV3iQVinpbqXU4IhtxgthgUOPQPP7cMvV1IVTWT8pHEdv7XZySFUHOwoQ", - "p":"wI-kPWTpm34wn0O1KzvCweAUBGJm-6s4R_sdkhE62Aht9NuGE86Z6zFfb6HI-dI6bvwPBNEROKNumrlsABgR13m9AVGFwq4OP-ipI4BVXpxMWi6tnKYFPegP21lDHk9p1cu_LIBWSq-GmqUtZo5Qz3suKeKOKmUNeo3VvM963Vc", - "q":"_g7dBzo7f002nuzQO0Ie_2Mb1lBXkmcEXLeouSdwLyYUSH4kuJFVuaq404NknFHotB8wjajP8V3w8s6oscO_2qcLcIeOJOVHvpoVJp0XodFPJteC8ROR2epzkjSxNh_j_plHcCd0ly8IVcWmgoZdf6pjeSfbFPO3OB48wimAy1s", - "dp":"e3jfoHpfjNP6i3UX6zPzqutrCnCqhj-A5C7yBCJGMBYfo31L2NGGQpgzENqVixMxYs7_NmB0gXPSTSYOSXUlo5wtBHZopa-D9ZjTM69rjjH8h2sc6bBO9iYiXM08y2eyfmOaHwffzR4F2o2FshgZWyEqNbNO44JOhUIDRoFn0Bs", - "dq":"Hd9tid4FBPD1TTaXPYCG2Iy0xzxnL6XBU42c3ziN7l1R4TxD4RfltpEmbmhyuha_f_5y3RVObhkXrdUy7MQRmQovRCoMQrZa-0Ru3D14e-R6pByPHv2oFrGEqVpcw_p3-oXXao6ZHPXAyyUUcSCPeeV1ENfo4MvPbV_Q0RvEMyU", - "qi":"srk3oe6CxebsQo1QTTygg-dWBlXongHf2m4Asj7GBeswoa49NcqzUvv5wlWuTgKJeihjjp-L5lkC5JWiFfUpRkBqr7tUE9faUmDa6fPLlvqWcB9A04rrZ3aJYqHgJJZ9e6OrEKwhgliIYSsTxlD-bLGZVLj-dp0R7xSVOFqiRX0", - "oth": [] - }"#; - - let err = serde_json::from_str::(json).unwrap_err(); - assert_eq!( - err.to_string(), - "RSA private keys with `oth` field set are not supported" - ); + fn _3_1_ec_public_key() -> TestResult { + roundtrip( + "3_1.ec_public_key", + // RustCrypto and ring do not support P-521 curve + cfg!(feature = "crypto-rustcrypto") || cfg!(feature = "crypto-ring"), + |jwk| { + let JsonWebKeyType::Asymmetric(key) = jwk.key_type() else { + panic!("Expected asymmetric key type"); + }; + + assert!(matches!( + **key, + AsymmetricJsonWebKey::Public(jwk::Public::Ec(jwk::EcPublic::P521(..))) + )); + assert_eq!(jwk.key_usage(), Some(&jwk::KeyUsage::Signing)); + assert_thumbprint( + jwk, + "dHri3SADZkrush5HU_50AoRhcKFryN-PI6jPBtPL55M", + "HncTFMje-quVjjwt2ufqfFb75ZwHLDh9M-VY4wJ9awQkfbu194TmVpeGbG6Ykb9b", + "i8RIsIb6HVP2AO9o38HtraybJAP5veAfBIgynNUqpxlhuvq2UDgSA3JFgGgle1YvmCQDHllAn7MG52Idb8B4fA" + ); + }, + ) } #[test] - fn deny_key_with_missing_prime() { - let json = r#"{ - "use":"sig", - "kty":"RSA", - "kid":"nYPs6qc5zj3VOVKr4yY-EzirO-AcdUl0JC5bcXKGE6Y", - "alg":"RS256", - "n":"vxmzEVX_Fus8i8BWT_sC_m389t615iPxKSMavPFv0xEhES42RWkO6yNpb_cwWhlJtoy_UdiRW8-0DHYJIbpiwkw4oRRnfMYX4FU77yjovSQLEhKPfIuYDBuP-9LQgF8_NgB9z1WokSUcH-tAf_35MpiXptGxoFuIe1EE7u1TWpTnMwGBnqO1EvJltyWej9R6rt47oqizn1VN8P2No3tys181B_TE9c6N2tXzWpnm5QY7UZO2zPLtYFGbj7hJCNhc5SL3vt-81KkaNjPcW1rgCIoRKvHVI79n_H81LQdiJDJyIobtvPH3XFQa_tM4CP2ul121E_Zi0tcjuD1zyLCq7Q", - "e":"AQAB", - "d":"n0FzkYbxRtBTbMOlKpItNIvEvJdtT5W0bGvs5HjwkB0-SWsRn1amMB8ax0xg5zUb0R4KctLgkHrPuXLEuW7yzqlmqBaxB7KuQy3E_NJC4x0efLkrCsfqtmxh2aMeT10Q-JgAQMFJ8WvTvGX5IrEs85VnDIbEWLbvTpV-Xv8478qjjt4v91wyhlzZW8A5-fsluy9faZDgKm3a3RaAKzP15ouffYf3ui5CTckXB-50fcrDasNEYlXzUzS1rHgQjF16ApjN7WupFP-FnlDkxudt6VfWmErakQV3iQVinpbqXU4IhtxgthgUOPQPP7cMvV1IVTWT8pHEdv7XZySFUHOwoQ", - "p":"wI-kPWTpm34wn0O1KzvCweAUBGJm-6s4R_sdkhE62Aht9NuGE86Z6zFfb6HI-dI6bvwPBNEROKNumrlsABgR13m9AVGFwq4OP-ipI4BVXpxMWi6tnKYFPegP21lDHk9p1cu_LIBWSq-GmqUtZo5Qz3suKeKOKmUNeo3VvM963Vc", - "q":"_g7dBzo7f002nuzQO0Ie_2Mb1lBXkmcEXLeouSdwLyYUSH4kuJFVuaq404NknFHotB8wjajP8V3w8s6oscO_2qcLcIeOJOVHvpoVJp0XodFPJteC8ROR2epzkjSxNh_j_plHcCd0ly8IVcWmgoZdf6pjeSfbFPO3OB48wimAy1s", - "dq":"Hd9tid4FBPD1TTaXPYCG2Iy0xzxnL6XBU42c3ziN7l1R4TxD4RfltpEmbmhyuha_f_5y3RVObhkXrdUy7MQRmQovRCoMQrZa-0Ru3D14e-R6pByPHv2oFrGEqVpcw_p3-oXXao6ZHPXAyyUUcSCPeeV1ENfo4MvPbV_Q0RvEMyU", - "qi":"srk3oe6CxebsQo1QTTygg-dWBlXongHf2m4Asj7GBeswoa49NcqzUvv5wlWuTgKJeihjjp-L5lkC5JWiFfUpRkBqr7tUE9faUmDa6fPLlvqWcB9A04rrZ3aJYqHgJJZ9e6OrEKwhgliIYSsTxlD-bLGZVLj-dp0R7xSVOFqiRX0" - }"#; - - let err = serde_json::from_str::(json).unwrap_err(); - assert_eq!( - err.to_string(), - "expected `dp` to be present because all prime fields must be set if one of them is \ - set" - ); + fn _3_2_ec_private_key() -> TestResult { + roundtrip( + "3_2.ec_private_key", + // RustCrypto and ring do not support P-521 curve + cfg!(feature = "crypto-rustcrypto") || cfg!(feature = "crypto-ring"), + |jwk| { + let JsonWebKeyType::Asymmetric(key) = jwk.key_type() else { + panic!("Expected asymmetric key type"); + }; + + assert!(matches!( + **key, + AsymmetricJsonWebKey::Private(jwk::Private::Ec(jwk::EcPrivate::P521(..))) + )); + assert_eq!(jwk.key_usage(), Some(&jwk::KeyUsage::Signing)); + + assert_thumbprint( + jwk, + "dHri3SADZkrush5HU_50AoRhcKFryN-PI6jPBtPL55M", + "HncTFMje-quVjjwt2ufqfFb75ZwHLDh9M-VY4wJ9awQkfbu194TmVpeGbG6Ykb9b", + "i8RIsIb6HVP2AO9o38HtraybJAP5veAfBIgynNUqpxlhuvq2UDgSA3JFgGgle1YvmCQDHllAn7MG52Idb8B4fA" + ); + }, + ) } -} -pub mod ec_p256 { - key_roundtrip_test! { - jose::crypto::ec::P256PrivateKey, - jose::crypto::ec::P256PublicKey, - "p256", - ["crv", "e", "x", "y", "d"], - ["crv", "x", "y"], + #[test] + fn _3_3_rsa_public_key() -> TestResult { + roundtrip("3_3.rsa_public_key", false, |jwk| { + assert_thumbprint( + jwk, + "9jg46WB3rR_AHD-EBXdN7cBkH1WOu0tA3M9fm21mqTI", + "iRBthSmwxk6o9pTGF6a9yLHohmMXSFRvKoN9rgcbOWFgLldwqED1DrOgDtLq5Q4R", + "FerGBUpYnzT0ptNAC7Y3qNpGINqILXdZ_9-Na3UkPUtDznnAChw7NWluNRjx-lmKDnuO1CpmIZL7e2bzRkQBew", + ); + }) } -} -pub mod ec_p384 { - key_roundtrip_test! { - jose::crypto::ec::P384PrivateKey, - jose::crypto::ec::P384PublicKey, - "p384", - ["crv", "e", "x", "y", "d"], - ["crv", "x", "y"], + #[test] + fn _3_4_rsa_private_key() -> TestResult { + roundtrip("3_4.rsa_private_key", false, |jwk| { + assert_thumbprint( + jwk, + "9jg46WB3rR_AHD-EBXdN7cBkH1WOu0tA3M9fm21mqTI", + "iRBthSmwxk6o9pTGF6a9yLHohmMXSFRvKoN9rgcbOWFgLldwqED1DrOgDtLq5Q4R", + "FerGBUpYnzT0ptNAC7Y3qNpGINqILXdZ_9-Na3UkPUtDznnAChw7NWluNRjx-lmKDnuO1CpmIZL7e2bzRkQBew", + ); + }) } -} - -pub mod ec { - use jose::jwk; - - use super::*; #[test] - fn parse_generic_public_key() { - let json = read_key_file("p256.pub"); - let key: jwk::EcPublic = serde_json::from_str(&json).unwrap(); - assert!(matches!(key, jwk::EcPublic::P256(..))); + fn _3_5_symmetric_key_mac() -> TestResult { + roundtrip("3_5.symmetric_key_mac_computation", false, |jwk| { + assert_eq!( + jwk.algorithm(), + Some(&jwa::JsonWebAlgorithm::from(jwa::Hmac::Hs256)) + ); + + assert_thumbprint( + jwk, + "RtoRur_1Dir5M4wuOfqNkDYOf9O_4RJ-aHkTA75RLA8", + "KG7sBEFjGfsIG21uR9cggZfOEIdKSvylcD7ndWgQsnG2k_5Wpw700r__c63SBBwf", + "EI4XUPoajddrVSS3fgSS6AcPt1uuacMmuYIi9i4A2CgjnWHuUV1qyNks84w03blKdF75HPSTJTJWgqRNEU_ZIg" + ); + }) } #[test] - fn parse_generic_private_key() { - let json = read_key_file("p256"); - let key: jwk::EcPrivate = serde_json::from_str(&json).unwrap(); - assert!(matches!(key, jwk::EcPrivate::P256(..))); + fn _3_6_symmetric_key_encryption() -> TestResult { + roundtrip("3_6.symmetric_key_encryption", false, |jwk| { + // NOTE: We do not support A256GCM as a JWK, because it's a one-time use + // session key, and must not be stored or reused + assert_eq!( + jwk.algorithm(), + Some(&jwa::JsonWebAlgorithm::Other("A256GCM".to_string())) + ); + assert_eq!(jwk.key_usage(), Some(&jwk::KeyUsage::Encryption)); + + assert_thumbprint( + jwk, + "VDMp1ZgGGv1OKgOeDc1EUKHXNQzMdLkCnxPETHdA4v0", + "4YmDfp3zlozmoDxrRxpMBiU6XHA9X82IKNW3bWiitdnudrwmAiKOi4yWWj4fyeYm", + "FmHGxbagOqt0LS__rv4hIpgnQ9pAB7nDneYNPN9i1gHIbvJJILw-VyyYIP_RTgsOU7K683SdE5aeplCUqz4ZaA" + ); + }) } -} -pub mod okp_ed25519 { - key_roundtrip_test! { - jose::jwk::OkpPrivate, - jose::jwk::OkpPublic, - "ed25519", - ["crv", "x", "d"], - ["crv", "x"], + #[test] + fn ed25519() -> TestResult { + roundtrip_pair("ed25519", "ed25519.pub", |jwk| { + assert_eq!( + jwk.algorithm(), + Some(&jwa::JsonWebAlgorithm::from( + jwa::JsonWebSigningAlgorithm::EdDSA + )) + ); + + assert_thumbprint( + jwk, + "IpNACexNZWO9hVeADtTT0Nvturu6OtMV3B4u1OVr1fU", + "NibgvGWph4nhazrZ1PcyRXYy55bdTotSDi1L4iE8gB0VtxDhh_a-du0fSnGExFwa", + "uS4j-x1iQUeF2a4a7M3iHZhPQGwyKgXU2Fh_GeNn9_uw_KAj1VTmNVenxTiFdDqcDHoWBemcLjioFY4slFbIZA", + ); + }) } -} -#[test] -fn generic_public_key_roundtrip() { - let json = read_key_file("p256.pub"); - - let key: JsonWebKeyType = serde_json::from_str(&json).unwrap(); - - let inner = match key { - JsonWebKeyType::Asymmetric(ref inner) => &**inner, - _ => unreachable!(), - }; - - assert!(matches!( - inner, - AsymmetricJsonWebKey::Public(Public::Ec(EcPublic::P256(..))) - )); - - let json2 = serde_json::to_string(&key).unwrap(); - let key2: JsonWebKeyType = serde_json::from_str(&json2).unwrap(); - - assert_eq!(key, key2); -} -#[test] -fn generic_private_key_roundtrip() { - let json = read_key_file("p256"); - - let key: JsonWebKeyType = serde_json::from_str(&json).unwrap(); - - let inner = match key { - JsonWebKeyType::Asymmetric(ref inner) => &**inner, - _ => unreachable!(), - }; - - assert!(matches!( - inner, - AsymmetricJsonWebKey::Private(Private::Ec(EcPrivate::P256(..))) - )); + #[test] + fn rsa_enc_optional_parameters() -> TestResult { + roundtrip("jwk_optional_parameters_rsa_enc.pub", false, |jwk| { + assert_eq!( + jwk.algorithm(), + Some(&jwa::JsonWebAlgorithm::from(jwa::RsaesOaep::RsaesOaep)) + ); + + assert_eq!(jwk.key_usage(), Some(&jwk::KeyUsage::Encryption)); + assert!(jwk.x509_certificate_sha1_thumbprint().is_some()); + assert!(jwk.x509_certificate_sha256_thumbprint().is_some()); + + assert_thumbprint( + jwk, + "ZwmJSHbFy5nl7WenHepIG5N9Rz16NH8SPGeqoZPTTuc", + "06f-mujFCZ0cUPKCgm0m7EuE0TW2mUmoQ0I519rD73v5JDAWti5QsuOX2PqTYuhV", + "EaW6WfYvQ5rauUmOYPZi82-ADGjmOb3Jz76jNVHUIQ_vA42s7CFve47jVTyb1n3UQbqLw3DDguD4u0wlFL4sbg" + ); + }) + } - let json2 = serde_json::to_string(&key).unwrap(); - let key2: JsonWebKeyType = serde_json::from_str(&json2).unwrap(); + #[test] + fn rsa_sign_optional_parameters() -> TestResult { + roundtrip("jwk_optional_parameters_rsa_sig.pub", false, |jwk| { + assert_eq!( + jwk.algorithm(), + Some(&jwa::JsonWebAlgorithm::from(jwa::RsassaPkcs1V1_5::Rs256)) + ); + + assert_eq!(jwk.key_usage(), Some(&jwk::KeyUsage::Signing)); + assert!(jwk.x509_certificate_sha1_thumbprint().is_some()); + assert!(jwk.x509_certificate_sha256_thumbprint().is_some()); + + assert_thumbprint( + jwk, + "bVeakRIe7OjtcR6E5FfkZvFAhEXfhIboYrcZ7OhZ1UY", + "_jnSoeQaW04m5PzXFnP7w2Zl_WFEop1UEKZ2vGHtQCdfsSqOkLhTbn2vyaDcMbHQ", + "cHiM1PMNsVve19_e5cfCiJqIMnQhYx0CkcvmiNCdwulCJ5B5ftaAvFYwr7_NQCMTQeXMcTKdbsP2a4mEKojPaQ" + ); + }) + } - assert_eq!(key, key2); -} + #[test] + fn rsa_with_key_ops_for_enc() -> TestResult { + roundtrip("key_ops_rsa_enc.pub", false, |jwk| { + assert_eq!( + jwk.algorithm(), + Some(&jwa::JsonWebAlgorithm::from(jwa::RsaesOaep::RsaesOaep)) + ); + + let ops = jwk.key_operations().unwrap(); + + assert_eq!(ops.len(), 2); + assert!(ops.contains(&jwk::KeyOperation::Encrypt)); + assert!(ops.contains(&jwk::KeyOperation::Decrypt)); + + assert_eq!(jwk.key_usage(), Some(&jwk::KeyUsage::Encryption)); + + assert_thumbprint( + jwk, + "ZwmJSHbFy5nl7WenHepIG5N9Rz16NH8SPGeqoZPTTuc", + "06f-mujFCZ0cUPKCgm0m7EuE0TW2mUmoQ0I519rD73v5JDAWti5QsuOX2PqTYuhV", + "EaW6WfYvQ5rauUmOYPZi82-ADGjmOb3Jz76jNVHUIQ_vA42s7CFve47jVTyb1n3UQbqLw3DDguD4u0wlFL4sbg" + ); + }) + } -#[test] -fn serde_jwk() { - let enc_json = read_key_file("jwk_optional_parameters_rsa_enc.pub"); - let enc: JsonWebKey = serde_json::from_str(&enc_json).unwrap(); - match enc.algorithm().unwrap() { - JsonWebAlgorithm::Encryption(_) => (), - _ => panic!(), + #[test] + fn p256() -> TestResult { + roundtrip_pair("p256", "p256.pub", |jwk| { + assert_eq!( + jwk.algorithm(), + Some(&jwa::JsonWebAlgorithm::from(jwa::EcDSA::Es256)) + ); + + assert_thumbprint( + jwk, + "6j1ImYAlN6DnVupozzN13UKnLR7BfEvngNmVl5bLlI0", + "u5W5WvG_wZc2u18HY0hqP48hVOOwytBz3BZzBimJl43SA3A4l-INFnhMNLEWL8a3", + "8fmi-z_V-FykWlKQAscDYj3I_uonEd2-0ChLqb7BwRJqnQiitQ9widx6Pk9ewMkhFk8NnBr1hCFa51kyrg7Pyw" + ); + }) } - let sig_json = read_key_file("jwk_optional_parameters_rsa_sig.pub"); - let sig: JsonWebKey = serde_json::from_str(&sig_json).unwrap(); - match sig.algorithm().unwrap() { - JsonWebAlgorithm::Signing(_) => (), - _ => panic!(), + #[test] + fn p384() -> TestResult { + roundtrip_pair("p384", "p384.pub", |jwk| { + assert_eq!( + jwk.algorithm(), + Some(&jwa::JsonWebAlgorithm::from(jwa::EcDSA::Es384)) + ); + + assert_thumbprint( + jwk, + "B_9VooM6jEuy9OvK_plFUDVADfKKnjCUPrqfc5Wtgq4", + "TXXx3K1KOHjCKWt_bY9cFZfsI9E8NUw8sK1xSOfd0jVgaBMiCP1hFvsxaamAQfK5", + "AtsWpt8bdnsqT49ovBfv67rhq_PB2eqnhvJ4F-uH-STeCZTVO97hWeEc0zGoOT18XtmHf_2o6ENmwIv9gcXyRQ" + ); + }) } - // It is not required for json keys to maintain order. Therefore, the input - // and output json string might differ in the sense that keys appear in a - // different order but by the definition of the json spec, they are the same - // assert_eq!(sig_json, serde_json::to_string(&sig).unwrap()); + #[test] + fn rsa() -> TestResult { + roundtrip_pair("rsa", "rsa.pub", |jwk| { + assert_thumbprint( + jwk, + "nYPs6qc5zj3VOVKr4yY-EzirO-AcdUl0JC5bcXKGE6Y", + "JhX_riWIrTLs3p7SnueDgpcO27pDgXh1xQOivPzOKsU3CaQgHoLgiKIinmb2CMoE", + "DQd_FsR8hTwlVrv3WGQP2E1KQejcBbJCFtqWy489xmmPm8LdNT91zYFX-yTghCtq2zutBGYY2mkwIWN-VQWYyQ" + ); + }) + } } +// TODO: test to ensure correct length of x, y, d + #[test] fn deny_duplicates_key_operations() { - let _ok: JsonWebKey = serde_json::from_str(&read_key_file("key_ops_rsa_enc.pub")).unwrap(); - let _err = serde_json::from_str::(&read_key_file("key_ops_duplicates_rsa_enc.pub")) - .unwrap_err(); + let key = r#" +{ + "key_ops": ["encrypt", "decrypt", "encrypt"], + "kty": "oct", + "alg": "HS256", + "k": "hJtXIZ2uSN5kbQfbtTNWbpdmhkV8FJG-Onbc6mxCcYg" } +"#; -#[test] -fn jwk_signer() { - let hs256: Checked = serde_json::from_str::(&read_key_file("hs256")) - .unwrap() - .check(StandardPolicy::default()) - .unwrap(); - let hs256_signer: JwkSigner = hs256.try_into().unwrap(); - assert_eq!( - hs256_signer.algorithm(), - JsonWebSigningAlgorithm::Hmac(Hmac::Hs256) - ); - - let p256: Checked<_, _> = serde_json::from_str::(&read_key_file("p256")) - .unwrap() - .check(StandardPolicy::default()) - .unwrap(); - let p256_signer: JwkSigner = p256.try_into().unwrap(); - - assert_eq!( - p256_signer.algorithm(), - JsonWebSigningAlgorithm::EcDSA(EcDSA::Es256) - ); + serde_json::from_str::(key).unwrap_err(); } #[test] -fn deny_hmac_key_with_short_key() { +fn deny_hmac_key_with_short_key() -> TestResult { let raw_key = r#"{"kty": "oct", "k": "QUFBQQ"}"#; - let key = serde_json::from_str::(raw_key).unwrap(); + let key = serde_json::from_str::(raw_key)?; - let hmac = hmac::Key::::from_key( - &key, - JsonWebAlgorithm::Signing(JsonWebSigningAlgorithm::Hmac(Hmac::Hs256)), - ); + let hmac = hmac::Key::::from_key(&key, jwa::Hmac::Hs256.into()); assert!(matches!( hmac.unwrap_err(), - FromOctetSequenceError::InvalidLength + jwk::symmetric::FromOctetSequenceError::InvalidLength )); + + Ok(()) } -#[test] -fn ed25519_json_web_key() { - let jwk: JsonWebKey = serde_json::from_str(&read_key_file("ed25519")).unwrap(); - match jwk.key_type() { - JsonWebKeyType::Asymmetric(key) => match **key { - AsymmetricJsonWebKey::Private(Private::Okp(OkpPrivate::Ed25519(..))) => (), - _ => panic!(), +pub mod generate { + use jose::{ + crypto::{ + ec::{P256PrivateKey, P384PrivateKey, P521PrivateKey}, + hmac, okp, rsa, }, - _ => panic!(), + jwk::Thumbprint as _, + }; + + use crate::common::TestResult; + + #[test] + #[cfg_attr( + feature = "crypto-ring", + ignore = "crypto backend does not support ECC key generation" + )] + fn ec_p256() -> TestResult { + let p256 = P256PrivateKey::generate()?; + let p256_pub = p256.to_public_key(); + assert_eq!(p256.thumbprint_sha256(), p256_pub.thumbprint_sha256()); + + Ok(()) } -} -#[test] -fn convert_to_public_key() { - let private_json = read_key_file("p256"); - let public_json = read_key_file("p256.pub"); + #[test] + #[cfg_attr( + feature = "crypto-ring", + ignore = "crypto backend does not support ECC key generation" + )] + fn ec_p384() -> TestResult { + let p384 = P384PrivateKey::generate()?; + let p384_pub = p384.to_public_key(); + assert_eq!(p384.thumbprint_sha256(), p384_pub.thumbprint_sha256()); + + Ok(()) + } - let private: JsonWebKey = serde_json::from_str(&private_json).unwrap(); - let public: JsonWebKey = serde_json::from_str(&public_json).unwrap(); + #[test] + #[cfg_attr( + any(feature = "crypto-rustcrypto", feature = "crypto-ring"), + ignore = "crypto backend does not support P-521 curve" + )] + fn ec_p521() -> TestResult { + let p521 = P521PrivateKey::generate()?; + let p521_pub = p521.to_public_key(); + assert_eq!(p521.thumbprint_sha256(), p521_pub.thumbprint_sha256()); + + Ok(()) + } - let public_converted = private.clone().into_verifying_key(); - assert_eq!(public.key_type(), public_converted.key_type()); + #[test] + #[cfg_attr( + feature = "crypto-ring", + ignore = "crypto backend does not support RSA key generation" + )] + fn rsa() -> TestResult { + let rsa = rsa::PrivateKey::generate(4096)?; + let rsa_pub = rsa.to_public_key(); + assert_eq!(rsa.thumbprint_sha256(), rsa_pub.thumbprint_sha256()); + + Ok(()) + } - let public_converted = private.strip_secret_material().unwrap(); - assert_eq!(public.key_type(), public_converted.key_type()); -} + #[test] + fn hmac() -> TestResult { + let _hmac = hmac::Key::::generate()?; + Ok(()) + } -#[test] -fn symmetric_key_can_not_strip_secret() { - let key = read_key_file("hs256"); - let key: JsonWebKey = serde_json::from_str(&key).unwrap(); + #[test] + #[cfg_attr( + feature = "crypto-ring", + ignore = "crypto backend does not support Ed448 curve" + )] + fn ed25519() -> TestResult { + let ed25519 = okp::PrivateKey::::generate()?; + let ed25519_pub = ed25519.to_public_key(); + assert_eq!(ed25519.thumbprint_sha256(), ed25519_pub.thumbprint_sha256()); + + Ok(()) + } - assert!(key.strip_secret_material().is_none()); + #[test] + #[cfg_attr( + any( + feature = "crypto-rustcrypto", + feature = "crypto-ring", + feature = "crypto-aws-lc" + ), + ignore = "crypto backend does not support Ed448 curve" + )] + fn ed448() -> TestResult { + let ed448 = okp::PrivateKey::::generate()?; + let ed448_pub = ed448.to_public_key(); + assert_eq!(ed448.thumbprint_sha256(), ed448_pub.thumbprint_sha256()); + + Ok(()) + } } #[test] -fn ec_p256_thumbprint() { - let jwk = read_key_file("p256"); - let jwk: JsonWebKey = serde_json::from_str(&jwk).unwrap(); +fn convert_to_public_key() -> TestResult { + let private_json = read_jwk("p256")?; + let public_json = read_jwk("p256.pub")?; - let p = jwk.thumbprint_sha256(); - let p = Base64UrlString::encode(p); - assert_eq!(&*p, "6j1ImYAlN6DnVupozzN13UKnLR7BfEvngNmVl5bLlI0"); -} + let private: JsonWebKey = serde_json::from_value(private_json)?; + let public: JsonWebKey = serde_json::from_value(public_json)?; -#[test] -fn rsa_thumbprint() { - let jwk = read_key_file("rsa"); - let jwk: JsonWebKey = serde_json::from_str(&jwk).unwrap(); + let public_converted = private.clone().into_verifying_key(); + assert_eq!(public.key_type(), public_converted.key_type()); - let p = jwk.thumbprint_sha256(); - let p = Base64UrlString::encode(p); - assert_eq!(&*p, "nYPs6qc5zj3VOVKr4yY-EzirO-AcdUl0JC5bcXKGE6Y"); + let public_converted = private.strip_secret_material().unwrap(); + assert_eq!(public.key_type(), public_converted.key_type()); + + Ok(()) } #[test] -fn symmetric_thumbprint() { - let jwk = json!({ - "k": "FyCq1CKBflh3I5gikEjpYrdOXllzxB_yc02za8ERknI", - "kty": "oct", - }); - - let jwk: JsonWebKey = serde_json::from_value(jwk).unwrap(); +fn symmetric_key_can_not_strip_secret() -> TestResult { + let key = read_jwk("3_5.symmetric_key_mac_computation")?; + let key: JsonWebKey = serde_json::from_value(key)?; + assert!(key.strip_secret_material().is_none()); - let p = jwk.thumbprint_sha256(); - let p = Base64UrlString::encode(p); - assert_eq!(&*p, "prDKy90VJzrDTpm8-W2Q_pv_kzrX_zyZ7ANjRAasDxc"); + Ok(()) } diff --git a/tests/jws.rs b/tests/jws.rs index e036b57..e69de29 100644 --- a/tests/jws.rs +++ b/tests/jws.rs @@ -1,467 +0,0 @@ -// tests for header parsing: -// - duplicate paremeter name -// - additional (private, public) headers -// - for supporting all MUST BE UNDERSTOOD params - -use std::{convert::Infallible, str::FromStr}; - -use jose::{ - crypto::{ - self, - okp::{Ed25519PrivateKey, Ed25519PublicKey, Ed25519Signer, Ed25519Verifier}, - }, - format::{Compact, JsonFlattened, JsonGeneral}, - header::HeaderValue, - jwa::JsonWebSigningAlgorithm, - jwk::{ - policy::{Checkable, StandardPolicy}, - JwkSigner, JwkVerifier, - }, - jws::{ - FromRawPayload, IntoPayload, IntoSigner, IntoVerifier, ManyUnverified, PayloadData, - PayloadKind, Signer, Unverified, Verifier, VerifyError, - }, - Base64UrlString, JsonWebKey, Jws, -}; - -fn read_key_file(name: &str) -> String { - std::fs::read_to_string(format!( - "{}/tests/keys/{}.json", - env!("CARGO_MANIFEST_DIR"), - name, - )) - .unwrap() -} - -#[derive(Debug, PartialEq, Eq)] -struct StringPayload(String); - -impl From<&str> for StringPayload { - fn from(value: &str) -> Self { - StringPayload(value.to_string()) - } -} - -impl FromRawPayload for StringPayload { - type Context = (); - type Error = String; - - fn from_attached(_: &(), payload: PayloadData) -> Result { - match payload { - PayloadData::Standard(s) => String::from_utf8(s.decode()) - .map(StringPayload) - .map_err(|e| e.to_string()), - } - } - - fn from_detached( - _: &(), - _: &jose::JoseHeader, - ) -> Result<(Self, PayloadData), Self::Error> { - Err(String::from("detached payload not supported")) - } - - fn from_detached_many( - _: &(), - _: &[jose::JoseHeader], - ) -> Result<(Self, PayloadData), Self::Error> { - Err(String::from("detached payload not supported")) - } -} - -impl IntoPayload for StringPayload { - type Error = Infallible; - - fn into_payload(self) -> Result { - let s = Base64UrlString::encode(self.0); - Ok(PayloadKind::Attached(PayloadData::Standard(s))) - } -} - -struct NoneKey; -impl Signer<[u8; 0]> for NoneKey { - fn sign(&mut self, _msg: &[u8]) -> Result<[u8; 0], crypto::Error> { - Ok([]) - } - - fn algorithm(&self) -> JsonWebSigningAlgorithm { - JsonWebSigningAlgorithm::None - } - - fn key_id(&self) -> Option<&str> { - Some("none") - } -} - -struct NoneVerifier; -impl Verifier for NoneVerifier { - fn verify(&mut self, _: &[u8], _: &[u8]) -> Result<(), VerifyError> { - Ok(()) - } -} - -#[test] -fn signer_without_key_id() { - let signer = NoneKey; - - assert_eq!(signer.key_id(), Some("none")); - - let without_key_id = signer.without_key_id(); - - assert_eq!(without_key_id.key_id(), None); -} - -#[test] -fn none_verifier_roundtrip() { - let jws = Jws::::builder() - .build(StringPayload::from("abc")) - .unwrap(); - let jws_compact = jws.sign(&mut NoneKey.without_key_id()).unwrap().encode(); - - assert_eq!( - jws_compact.to_string(), - String::from("eyJhbGciOiJub25lIn0.YWJj.") - ); - - let parsed_jws = Unverified::>::decode(jws_compact) - .unwrap() - .verify(&mut NoneVerifier) - .unwrap(); - - assert_eq!(parsed_jws.payload(), &StringPayload::from("abc")); -} - -#[test] -fn detached_payload_with_context() { - use jose::{ - crypto::ec::{P256PrivateKey, P256Signer, P256Verifier}, - jwa::EcDSA, - }; - - #[derive(Debug)] - struct MyPayload(String); - - impl IntoPayload for MyPayload { - type Error = (); - - fn into_payload(self) -> Result { - let s = Base64UrlString::encode(self.0); - Ok(PayloadKind::Detached(PayloadData::Standard(s))) - } - } - - impl FromRawPayload for MyPayload { - type Context = String; - type Error = (); - - fn from_attached( - context: &Self::Context, - _payload: PayloadData, - ) -> Result { - Ok(Self(context.clone())) - } - - fn from_detached( - context: &Self::Context, - _header: &jose::JoseHeader, - ) -> Result<(Self, PayloadData), Self::Error> { - let data = PayloadData::Standard(Base64UrlString::encode(context)); - Ok((Self(context.clone()), data)) - } - - fn from_detached_many( - _context: &Self::Context, - _headers: &[jose::JoseHeader], - ) -> Result<(Self, PayloadData), Self::Error> { - todo!() - } - } - - let key = std::fs::read_to_string(format!( - "{}/tests/keys/p256.json", - env!("CARGO_MANIFEST_DIR"), - )) - .unwrap(); - let key: P256PrivateKey = serde_json::from_str(&key).unwrap(); - - let mut signer: P256Signer = key - .clone() - .into_signer(JsonWebSigningAlgorithm::EcDSA(EcDSA::Es256)) - .unwrap(); - - let mut verifier: P256Verifier = key - .into_verifier(JsonWebSigningAlgorithm::EcDSA(EcDSA::Es256)) - .unwrap(); - - let context = "hello".to_string(); - - let jws = Jws::::builder() - .build(MyPayload(context.clone())) - .unwrap() - .sign(&mut signer) - .unwrap() - .encode(); - - // After a recent change in jose, ecdas is not deterministic anymore - // assert_eq!( - // jws.to_string(), - // "eyJhbGciOiJFUzI1NiJ9..\ - // 66Pd7hVwuNAOP4qFlQW5zSOmLNehj69TbZifg7pD5QjRWMqbxEdalWzMJFmRtQisYunNK2Vhm7H54xOnL6_Q4w" - // ); - - let parsed_jws = - Unverified::>::decode_with_context(jws.clone(), &context) - .unwrap() - .verify(&mut verifier) - .unwrap(); - - assert_eq!(parsed_jws.payload().0, context); - - // decoding with another context, should fail signature validation - let context2 = "world".to_string(); - Unverified::>::decode_with_context(jws, &context2) - .unwrap() - .verify(&mut verifier) - .unwrap_err(); -} - -#[test] -fn sign_jws_using_p256() { - use jose::{ - crypto::ec::{P256PrivateKey, P256Signer, P256Verifier}, - jwa::EcDSA, - }; - - let key = std::fs::read_to_string(format!( - "{}/tests/keys/p256.json", - env!("CARGO_MANIFEST_DIR"), - )) - .unwrap(); - - let key: P256PrivateKey = serde_json::from_str(&key).unwrap(); - - let mut signer: P256Signer = key - .clone() - .into_signer(JsonWebSigningAlgorithm::EcDSA(EcDSA::Es256)) - .unwrap(); - - let mut verifier: P256Verifier = key - .into_verifier(JsonWebSigningAlgorithm::EcDSA(EcDSA::Es256)) - .unwrap(); - - let jws = Jws::::builder() - .build(StringPayload::from("hello world!")) - .unwrap() - .sign(&mut signer) - .unwrap() - .encode(); - - // ecdsas is not deterministic anymore - // assert_eq!( - // jws.to_string().as_str(), - // "eyJhbGciOiJFUzI1NiJ9.aGVsbG8gd29ybGQh.\ - // lVKmpTNK_Im3-JEpF1JzuXM-vP9tNSkR8785hqnYzOHd1__VVOeMzGW7nywUe7Xkp6Wlu3KgWXlvsxhQdU1PlQ" - // ); - - let parsed_jws = Unverified::>::decode(jws.clone()) - .unwrap() - .verify(&mut verifier) - .unwrap(); - - assert_eq!(parsed_jws.payload().0, "hello world!"); -} - -#[test] -fn deny_compact_jws_with_empty_protected_header() { - let jws: Jws = Jws::builder() - .header(|b| b.algorithm(HeaderValue::Unprotected(JsonWebSigningAlgorithm::None))) - .build(StringPayload::from("abc")) - .unwrap(); - - jws.sign(&mut NoneKey).unwrap_err(); -} - -#[test] -fn json_flattened_jws_with_no_protected_header() { - let key = std::fs::read_to_string(format!( - "{}/tests/keys/cookbook_hs256.json", - env!("CARGO_MANIFEST_DIR"), - )) - .unwrap(); - - let key: JsonWebKey = serde_json::from_str(&key).unwrap(); - let key = key.check(StandardPolicy::new()).unwrap(); - - let mut signer = JwkSigner::try_from(key).unwrap(); - - let payload = "It's a dangerous business, Frodo, going out your door. You step onto the road, \ - and if you don't keep your feet, there's no knowing where you"; - - let jws: Jws = Jws::builder() - .header(|b| b.algorithm(HeaderValue::Unprotected(JsonWebSigningAlgorithm::None))) - .build(StringPayload::from(payload)) - .unwrap(); - - let jws = jws.sign(&mut signer).unwrap(); - - println!("{jws:#}"); -} - -#[test] -fn smoke() { - let key = std::fs::read_to_string(format!( - "{}/tests/keys/p256.json", - env!("CARGO_MANIFEST_DIR"), - )) - .unwrap(); - - let key2 = std::fs::read_to_string(format!( - "{}/tests/keys/hs256.json", - env!("CARGO_MANIFEST_DIR"), - )) - .unwrap(); - - let key: JsonWebKey = serde_json::from_str(&key).unwrap(); - let key = key.check(StandardPolicy::new()).unwrap(); - - let key2: JsonWebKey = serde_json::from_str(&key2).unwrap(); - let key2 = key2.check(StandardPolicy::new()).unwrap(); - - let mut signer = JwkSigner::try_from(key.clone()).unwrap(); - let mut signer2 = JwkSigner::try_from(key2.clone()).unwrap(); - - let mut verifier = JwkVerifier::try_from(key).unwrap(); - let mut verifier2 = JwkVerifier::try_from(key2).unwrap(); - - let payload = r#"{"iss":"joe","exp":1300819380,"http://example.com/is_root":true}"#; - let payload = StringPayload::from(payload); - - let signers: [&mut dyn Signer>; 2] = [&mut signer, &mut signer2]; - - let jws = Jws::::builder() - .header(|b| b) - .header(|b| b) - .build(payload) - .unwrap() - .sign_many(signers) - .unwrap() - .encode(); - println!("{jws}"); - - let verifiers: [&mut dyn Verifier; 2] = [&mut verifier, &mut verifier2]; - let parsed_jws = ManyUnverified::>::decode(jws) - .unwrap() - .verify_many(verifiers) - .unwrap(); - - println!("{parsed_jws:#?}"); -} - -#[test] -fn additional_jwk_parameters_in_header() { - let key = std::fs::read_to_string(format!( - "{}/tests/keys/p256.json", - env!("CARGO_MANIFEST_DIR"), - )) - .unwrap(); - - #[derive(serde::Serialize, serde::Deserialize)] - struct Additional { - #[serde(rename = "example.com/custom-key")] - foo: usize, - } - - let additional = Additional { foo: 1337 }; - - let key: JsonWebKey = serde_json::from_str(&key).unwrap(); - let key = key.check(StandardPolicy::new()).unwrap(); - let mut signer = JwkSigner::try_from(key.clone()).unwrap(); - let mut verifier = JwkVerifier::try_from(key.clone()).unwrap(); - - let key = key - .into_inner() - .0 - .into_builder() - .additional(additional) - .build() - .unwrap(); - let key = key.into_untyped_additional().unwrap(); - - let jws: Jws = Jws::builder() - .header(|b| b.json_web_key(Some(HeaderValue::Protected(key)))) - .build(StringPayload::from("abc")) - .unwrap(); - - let jws = jws.sign(&mut signer).unwrap().encode(); - - let parsed_jws = Unverified::>::decode(jws) - .unwrap() - .verify(&mut verifier) - .unwrap(); - - let jwk = parsed_jws - .header() - .json_web_key() - .unwrap() - .into_inner() - .clone(); - let jwk: JsonWebKey = jwk.deserialize_additional().unwrap(); - - assert_eq!(jwk.additional().foo, 1337); -} - -#[test] -fn ed25519() { - let private: Ed25519PrivateKey = - serde_json::from_str(&read_key_file("ed25519")).expect("valid ed25519 private key"); - let _wrong_private = serde_json::from_str::(&read_key_file("ed25519.pub")) - .expect_err("is a public key"); - - let public: Ed25519PublicKey = - serde_json::from_str(&read_key_file("ed25519.pub")).expect("is a valid public key"); - let _wrong_public = serde_json::from_str::(&read_key_file("ed25519")) - .expect("public key can be parsed from private key"); - - let msg = "I was cured all right."; - - let mut signer: Ed25519Signer = private - .into_signer(jose::jwa::JsonWebSigningAlgorithm::EdDSA) - .unwrap(); - - let jws = Jws::::builder() - .build(StringPayload(msg.to_string())) - .unwrap(); - - let jws = jws.sign(&mut signer).unwrap(); - - println!("{jws:#?}"); - - let mut verifier: Ed25519Verifier = public - .into_verifier(JsonWebSigningAlgorithm::EdDSA) - .unwrap(); - - let encoded = jws.encode().to_string(); - assert_eq!( - encoded, - "eyJhbGciOiJFZERTQSJ9.SSB3YXMgY3VyZWQgYWxsIHJpZ2h0Lg.\ - JtDl6Eq4ORiI_fYmeik8QLMUPur0s37AgaK-o8W2ywEXnomeSPpf3Je0EvCI8K55k0uu0zkdHmGs2vu-DiuLDw" - ); - - let unverified_jws = - Unverified::>::decode(Compact::from_str(&encoded).unwrap()) - .unwrap(); - unverified_jws - .verify(&mut verifier) - .expect("valid signature"); - - let signature_by_different_key = - "eyJhbGciOiJFZERTQSJ9.SSB3YXMgY3VyZWQgYWxsIHJpZ2h0Lg.\ - xuBNTX8MSX1Du5sAdSGBUKijn8yqHG8v-3CYrZpoHizzwU9T6aT-XqbS5FBuMR9_pGagmpO6EiPHqZiTUpxXDQ"; - - let wrong_jws: Unverified> = - Unverified::decode(Compact::from_str(signature_by_different_key).unwrap()).unwrap(); - - wrong_jws - .verify(&mut verifier) - .expect_err("signature made by different private key"); -} diff --git a/tests/keys/hs256.json b/tests/keys/hs256.json deleted file mode 100644 index 6d294e6..0000000 --- a/tests/keys/hs256.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "use": "sig", - "kty": "oct", - "alg": "HS256", - "k": "QXk0V2FZSWYyMFR3RVNMZmt4SFVwU3Z4MkRtNlpINE4" -} diff --git a/tests/keys/key_ops_duplicates_rsa_enc.pub.json b/tests/keys/key_ops_duplicates_rsa_enc.pub.json deleted file mode 100644 index 8ddd2be..0000000 --- a/tests/keys/key_ops_duplicates_rsa_enc.pub.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "key_ops": ["encrypt", "decrypt", "decrypt"], - "kid": "XEK_bZ60VtJpXsnOGMWokkDgdTS3RC-_-5kwlgukIgw", - "kty": "RSA", - "alg": "RSA-OAEP", - "use": "enc", - "n": "kzMp0r1czDSZAH4iye4yMlBcHMCHo5G9uxC6UjzDEh_Zwj3sDGeMsUbX-X-UoLnkYySw2bxsdwHrPhuidkJrCC-CzFaujeARRrb3qDgQzOPj8zc8nlJvybC68KBPmj45wxFheHzp_VpfQ90LmUKbs3T8BJssDAKnWhuJSer1GR7vRhU4uKTX2ulxChuHdUJgApzj_Detohm66W8mnnPeSXbF4x8kl6JP1Txiq5GPYZWPDvLX0IjLzUuEIaOyQALObomv3DrWA1dfT2VevAr9ZWmqZNqFvalRrTuINxppF9cYH02Al6TucQOBnacwAj0JeHW-bvypiCuvhwpnRUqWgw", - "e": "AQAB", - "x5c": [ - "MIICmzCCAYMCBgGB2iU8RDANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZtYXN0ZXIwHhcNMjIwNzA3MTkyOTQyWhcNMzIwNzA3MTkzMTIyWjARMQ8wDQYDVQQDDAZtYXN0ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCTMynSvVzMNJkAfiLJ7jIyUFwcwIejkb27ELpSPMMSH9nCPewMZ4yxRtf5f5SgueRjJLDZvGx3Aes+G6J2QmsIL4LMVq6N4BFGtveoOBDM4+PzNzyeUm/JsLrwoE+aPjnDEWF4fOn9Wl9D3QuZQpuzdPwEmywMAqdaG4lJ6vUZHu9GFTi4pNfa6XEKG4d1QmACnOP8N62iGbrpbyaec95JdsXjHySXok/VPGKrkY9hlY8O8tfQiMvNS4Qho7JAAs5uia/cOtYDV19PZV68Cv1laapk2oW9qVGtO4g3GmkX1xgfTYCXpO5xA4GdpzACPQl4db5u/KmIK6+HCmdFSpaDAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAEcHfQlE2z+0+wpeUXb5JAzL1Mclki8kUzbE53MkCQsDpPrmuuX9gKz4ReEvyTpYJmxzx0TtZ3E8+o5h3i1WSY/V6dVMsXSlwUCzDsVpZ1JwAyC/c1HVhJGT/AqsNsK5XNsYmACGKlV5Z1utn+9aNBBGJWlu1BNqxSy9+jWo73/0K19yBFuKB+dCHpr653NPf1YlD8H5SZ5I0mwdr3Hhm/bXM464KFBNMjDzNtHNF3ucM0OI6CgtMoS44kJOhElAretzYFCtiZA1Nrx0B0kf19GdIyGPXaukn6lbgWutepp6r4hAzVNiOSxXKRpPNZxM1bwNmo3QP+YSqfoh7OYWmR8=" - ], - "x5t": "blHgLn0uMGRznf8iQCCVGtPIpGY", - "x5t#S256": "VUTpD68AR1hdCkR02VSODfYJuQtvF0Dqtlv5c_kz3w0" -} diff --git a/tests/vectors/jwk/3_1.ec_public_key.json b/tests/vectors/jwk/3_1.ec_public_key.json new file mode 100644 index 0000000..1d0eb45 --- /dev/null +++ b/tests/vectors/jwk/3_1.ec_public_key.json @@ -0,0 +1,8 @@ +{ + "kty": "EC", + "kid": "bilbo.baggins@hobbiton.example", + "use": "sig", + "crv": "P-521", + "x": "AHKZLLOsCOzz5cY97ewNUajB957y-C-U88c3v13nmGZx6sYl_oJXu9A5RkTKqjqvjyekWF-7ytDyRXYgCF5cj0Kt", + "y": "AdymlHvOiLxXkEhayXQnNCvDX4h9htZaCJN34kfmC6pV5OhQHiraVySsUdaQkAgDPrwQrJmbnX9cwlGfP-HqHZR1" +} diff --git a/tests/vectors/jwk/3_2.ec_private_key.json b/tests/vectors/jwk/3_2.ec_private_key.json new file mode 100644 index 0000000..2083943 --- /dev/null +++ b/tests/vectors/jwk/3_2.ec_private_key.json @@ -0,0 +1,9 @@ +{ + "kty": "EC", + "kid": "bilbo.baggins@hobbiton.example", + "use": "sig", + "crv": "P-521", + "x": "AHKZLLOsCOzz5cY97ewNUajB957y-C-U88c3v13nmGZx6sYl_oJXu9A5RkTKqjqvjyekWF-7ytDyRXYgCF5cj0Kt", + "y": "AdymlHvOiLxXkEhayXQnNCvDX4h9htZaCJN34kfmC6pV5OhQHiraVySsUdaQkAgDPrwQrJmbnX9cwlGfP-HqHZR1", + "d": "AAhRON2r9cqXX1hg-RoI6R1tX5p2rUAYdmpHZoC1XNM56KtscrX6zbKipQrCW9CGZH3T4ubpnoTKLDYJ_fF3_rJt" +} diff --git a/tests/vectors/jwk/3_3.rsa_public_key.json b/tests/vectors/jwk/3_3.rsa_public_key.json new file mode 100644 index 0000000..4529cfc --- /dev/null +++ b/tests/vectors/jwk/3_3.rsa_public_key.json @@ -0,0 +1,7 @@ +{ + "kty": "RSA", + "kid": "bilbo.baggins@hobbiton.example", + "use": "sig", + "n": "n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqVwGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuCLqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5gHdrNP5zw", + "e": "AQAB" +} diff --git a/tests/vectors/jwk/3_4.rsa_private_key.json b/tests/vectors/jwk/3_4.rsa_private_key.json new file mode 100644 index 0000000..04843d8 --- /dev/null +++ b/tests/vectors/jwk/3_4.rsa_private_key.json @@ -0,0 +1,13 @@ +{ + "kty": "RSA", + "kid": "bilbo.baggins@hobbiton.example", + "use": "sig", + "n": "n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqVwGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuCLqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5gHdrNP5zw", + "e": "AQAB", + "d": "bWUC9B-EFRIo8kpGfh0ZuyGPvMNKvYWNtB_ikiH9k20eT-O1q_I78eiZkpXxXQ0UTEs2LsNRS-8uJbvQ-A1irkwMSMkK1J3XTGgdrhCku9gRldY7sNA_AKZGh-Q661_42rINLRCe8W-nZ34ui_qOfkLnK9QWDDqpaIsA-bMwWWSDFu2MUBYwkHTMEzLYGqOe04noqeq1hExBTHBOBdkMXiuFhUq1BU6l-DqEiWxqg82sXt2h-LMnT3046AOYJoRioz75tSUQfGCshWTBnP5uDjd18kKhyv07lhfSJdrPdM5Plyl21hsFf4L_mHCuoFau7gdsPfHPxxjVOcOpBrQzwQ", + "p": "3Slxg_DwTXJcb6095RoXygQCAZ5RnAvZlno1yhHtnUex_fp7AZ_9nRaO7HX_-SFfGQeutao2TDjDAWU4Vupk8rw9JR0AzZ0N2fvuIAmr_WCsmGpeNqQnev1T7IyEsnh8UMt-n5CafhkikzhEsrmndH6LxOrvRJlsPp6Zv8bUq0k", + "q": "uKE2dh-cTf6ERF4k4e_jy78GfPYUIaUyoSSJuBzp3Cubk3OCqs6grT8bR_cu0Dm1MZwWmtdqDyI95HrUeq3MP15vMMON8lHTeZu2lmKvwqW7anV5UzhM1iZ7z4yMkuUwFWoBvyY898EXvRD-hdqRxHlSqAZ192zB3pVFJ0s7pFc", + "dp": "B8PVvXkvJrj2L-GYQ7v3y9r6Kw5g9SahXBwsWUzp19TVlgI-YV85q1NIb1rxQtD-IsXXR3-TanevuRPRt5OBOdiMGQp8pbt26gljYfKU_E9xn-RULHz0-ed9E9gXLKD4VGngpz-PfQ_q29pk5xWHoJp009Qf1HvChixRX59ehik", + "dq": "CLDmDGduhylc9o7r84rEUVn7pzQ6PF83Y-iBZx5NT-TpnOZKF1pErAMVeKzFEl41DlHHqqBLSM0W1sOFbwTxYWZDm6sI6og5iTbwQGIC3gnJKbi_7k_vJgGHwHxgPaX2PnvP-zyEkDERuf-ry4c_Z11Cq9AqC2yeL6kdKT1cYF8", + "qi": "3PiqvXQN0zwMeE-sBvZgi289XP9XCQF3VWqPzMKnIgQp7_Tugo6-NZBKCQsMf3HaEGBjTVJs_jcK8-TRXvaKe-7ZMaQj8VfBdYkssbu0NKDDhjJ-GtiseaDVWt7dcH0cfwxgFUHpQh7FoCrjFJ6h6ZEpMF6xmujs4qMpPz8aaI4" +} diff --git a/tests/keys/cookbook_hs256.json b/tests/vectors/jwk/3_5.symmetric_key_mac_computation.json similarity index 100% rename from tests/keys/cookbook_hs256.json rename to tests/vectors/jwk/3_5.symmetric_key_mac_computation.json diff --git a/tests/vectors/jwk/3_6.symmetric_key_encryption.json b/tests/vectors/jwk/3_6.symmetric_key_encryption.json new file mode 100644 index 0000000..e70ad94 --- /dev/null +++ b/tests/vectors/jwk/3_6.symmetric_key_encryption.json @@ -0,0 +1,7 @@ +{ + "kty": "oct", + "kid": "1e571774-2e08-40da-8308-e8d68773842d", + "use": "enc", + "alg": "A256GCM", + "k": "AAPapAv4LbFbiVawEjagUBluYqN5rhna-8nuldDvOx8" +} diff --git a/tests/keys/ed25519.json b/tests/vectors/jwk/ed25519.json similarity index 99% rename from tests/keys/ed25519.json rename to tests/vectors/jwk/ed25519.json index 3d1cd8e..f2112c7 100644 --- a/tests/keys/ed25519.json +++ b/tests/vectors/jwk/ed25519.json @@ -6,4 +6,4 @@ "alg": "EdDSA", "x": "etkJX1EBhliHzBaimUQb0h2JhJKQ3G0beRVR3ssiedY", "d": "629kbTnU3Pgfkoq7zG9qe5LPuMi1PdaLcNo87nUya1I" -} \ No newline at end of file +} diff --git a/tests/keys/ed25519.pub.json b/tests/vectors/jwk/ed25519.pub.json similarity index 100% rename from tests/keys/ed25519.pub.json rename to tests/vectors/jwk/ed25519.pub.json diff --git a/tests/keys/jwk_optional_parameters_rsa_enc.pub.json b/tests/vectors/jwk/jwk_optional_parameters_rsa_enc.pub.json similarity index 100% rename from tests/keys/jwk_optional_parameters_rsa_enc.pub.json rename to tests/vectors/jwk/jwk_optional_parameters_rsa_enc.pub.json diff --git a/tests/keys/jwk_optional_parameters_rsa_sig.pub.json b/tests/vectors/jwk/jwk_optional_parameters_rsa_sig.pub.json similarity index 100% rename from tests/keys/jwk_optional_parameters_rsa_sig.pub.json rename to tests/vectors/jwk/jwk_optional_parameters_rsa_sig.pub.json diff --git a/tests/keys/key_ops_rsa_enc.pub.json b/tests/vectors/jwk/key_ops_rsa_enc.pub.json similarity index 100% rename from tests/keys/key_ops_rsa_enc.pub.json rename to tests/vectors/jwk/key_ops_rsa_enc.pub.json diff --git a/tests/keys/p256.json b/tests/vectors/jwk/p256.json similarity index 100% rename from tests/keys/p256.json rename to tests/vectors/jwk/p256.json diff --git a/tests/keys/p256.pub.json b/tests/vectors/jwk/p256.pub.json similarity index 100% rename from tests/keys/p256.pub.json rename to tests/vectors/jwk/p256.pub.json diff --git a/tests/keys/p384.json b/tests/vectors/jwk/p384.json similarity index 100% rename from tests/keys/p384.json rename to tests/vectors/jwk/p384.json diff --git a/tests/keys/p384.pub.json b/tests/vectors/jwk/p384.pub.json similarity index 100% rename from tests/keys/p384.pub.json rename to tests/vectors/jwk/p384.pub.json diff --git a/tests/keys/rsa.json b/tests/vectors/jwk/rsa.json similarity index 100% rename from tests/keys/rsa.json rename to tests/vectors/jwk/rsa.json diff --git a/tests/keys/rsa.pub.json b/tests/vectors/jwk/rsa.pub.json similarity index 100% rename from tests/keys/rsa.pub.json rename to tests/vectors/jwk/rsa.pub.json From f44af9a14099a25d2a71b721a4f549f6883cbaeb Mon Sep 17 00:00:00 2001 From: Justus K Date: Sat, 3 May 2025 13:24:05 +0200 Subject: [PATCH 2/5] feat(tests): add more JWK related tests --- Cargo.lock | 146 ++++ Cargo.toml | 1 + tarpaulin-report.html | 671 ++++++++++++++++++ tests/jwk.rs | 243 ++++--- tests/jws.rs | 1 + tests/vectors/jwk/ed448.json | 6 + tests/vectors/jwk/ed448.pub.json | 5 + tests/vectors/jwk/k256.json | 9 + tests/vectors/jwk/k256.pub.json | 8 + .../jwk/rsa_with_additional_props.pub.json | 10 + 10 files changed, 1015 insertions(+), 85 deletions(-) create mode 100644 tarpaulin-report.html create mode 100644 tests/vectors/jwk/ed448.json create mode 100644 tests/vectors/jwk/ed448.pub.json create mode 100644 tests/vectors/jwk/k256.json create mode 100644 tests/vectors/jwk/k256.pub.json create mode 100644 tests/vectors/jwk/rsa_with_additional_props.pub.json diff --git a/Cargo.lock b/Cargo.lock index faf8862..2081b08 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -505,6 +505,49 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-timer" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-core", + "futures-macro", + "futures-task", + "pin-project-lite", + "pin-utils", + "slab", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -605,6 +648,16 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" +[[package]] +name = "indexmap" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "is-terminal" version = "0.4.16" @@ -674,6 +727,7 @@ dependencies = [ "rand_core", "ring", "rsa", + "rstest", "secrecy", "serde", "serde-value", @@ -927,6 +981,18 @@ dependencies = [ "base64ct", ] +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + [[package]] name = "pkcs1" version = "0.7.5" @@ -992,6 +1058,15 @@ dependencies = [ "elliptic-curve", ] +[[package]] +name = "proc-macro-crate" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +dependencies = [ + "toml_edit", +] + [[package]] name = "proc-macro2" version = "1.0.95" @@ -1094,6 +1169,12 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +[[package]] +name = "relative-path" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" + [[package]] name = "rfc6979" version = "0.4.0" @@ -1138,6 +1219,36 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rstest" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fc39292f8613e913f7df8fa892b8944ceb47c247b78e1b1ae2f09e019be789d" +dependencies = [ + "futures-timer", + "futures-util", + "rstest_macros", + "rustc_version", +] + +[[package]] +name = "rstest_macros" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f168d99749d307be9de54d23fd226628d99768225ef08f6ffb52e0182a27746" +dependencies = [ + "cfg-if", + "glob", + "proc-macro-crate", + "proc-macro2", + "quote", + "regex", + "relative-path", + "rustc_version", + "syn", + "unicode-ident", +] + [[package]] name = "rustc-hash" version = "1.1.0" @@ -1304,6 +1415,15 @@ dependencies = [ "rand_core", ] +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + [[package]] name = "smallvec" version = "1.15.0" @@ -1382,6 +1502,23 @@ dependencies = [ "syn", ] +[[package]] +name = "toml_datetime" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" + +[[package]] +name = "toml_edit" +version = "0.22.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + [[package]] name = "typenum" version = "1.18.0" @@ -1603,6 +1740,15 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "winnow" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9fb597c990f03753e08d3c29efbfcf2019a003b4bf4ba19225c158e1549f0f3" +dependencies = [ + "memchr", +] + [[package]] name = "wit-bindgen-rt" version = "0.39.0" diff --git a/Cargo.toml b/Cargo.toml index a6aa09b..b6b3d65 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -132,6 +132,7 @@ clio = { version = "0.3.5", features = ["clap-parse"] } # For integration tests pretty_assertions = "1.4.1" +rstest = "0.25.0" # For X.509 certificates of Json Web Keys # x509-cert = { git = "https://github.com/RustCrypto/formats.git" } diff --git a/tarpaulin-report.html b/tarpaulin-report.html new file mode 100644 index 0000000..c0cdb23 --- /dev/null +++ b/tarpaulin-report.html @@ -0,0 +1,671 @@ + + + + + + + +
+ + + + + + \ No newline at end of file diff --git a/tests/jwk.rs b/tests/jwk.rs index f3a2a39..904dcfb 100644 --- a/tests/jwk.rs +++ b/tests/jwk.rs @@ -1,10 +1,11 @@ use jose::{ crypto::hmac, - jwa, - jwk::{self, FromKey as _, Thumbprint as _}, + jwa::{self, JsonWebAlgorithm}, + jwk::{self, policy::Checkable, FromKey as _, Thumbprint as _}, Base64UrlString, JsonWebKey, }; use pretty_assertions::assert_eq; +use serde::{Deserialize, Serialize}; use crate::common::{read_jwk, TestResult}; @@ -49,12 +50,62 @@ fn roundtrip(file: &str, unsupported: bool, check: fn(&JsonWebKey)) -> TestResul assert_eq!(json_key, serialized); assert_eq!(json_key, serialized_from_str); + // now try constructing a builder using this key, and check invalid + // algorithm + let err = JsonWebKey::builder(jwk.key_type().clone()) + .algorithm(Some(JsonWebAlgorithm::Other("foo".to_string()))) + .build() + .unwrap_err(); + assert!(matches!( + err, + jwk::JsonWebKeyBuildError::IncompatibleKeyType + )); + + let err = jwk + .into_builder() + .algorithm(Some(JsonWebAlgorithm::Other("foo".to_string()))) + .build() + .unwrap_err(); + assert!(matches!( + err, + jwk::JsonWebKeyBuildError::IncompatibleKeyType + )); + Ok(()) } -fn roundtrip_pair(private: &str, public: &str, check: fn(&JsonWebKey)) -> TestResult { - roundtrip(private, false, check)?; - roundtrip(public, false, check)?; +fn roundtrip_pair( + private: &str, + public: &str, + unsupported: bool, + check: fn(&JsonWebKey), +) -> TestResult { + roundtrip(private, unsupported, check)?; + roundtrip(public, unsupported, check)?; + + let private_key: JsonWebKey = serde_json::from_value(read_jwk(private)?)?; + let public_key: JsonWebKey = serde_json::from_value(read_jwk(public)?)?; + + assert!(private_key.is_signing_key()); + assert!(!public_key.is_signing_key()); + + assert!(!private_key.is_symmetric()); + assert!(!public_key.is_symmetric()); + + assert!(private_key.is_asymmetric()); + assert!(public_key.is_asymmetric()); + + let stripped = private_key.clone().strip_secret_material().unwrap(); + assert_eq!(stripped.key_type(), public_key.key_type()); + + let stripped_from_public = public_key.clone().strip_secret_material().unwrap(); + assert_eq!(stripped_from_public.key_type(), public_key.key_type()); + + let into_verifying = private_key.into_verifying_key(); + assert_eq!(into_verifying.key_type(), public_key.key_type()); + + let public_into_verifying = public_key.clone().into_verifying_key(); + assert_eq!(public_into_verifying.key_type(), public_key.key_type()); Ok(()) } @@ -71,57 +122,20 @@ fn assert_thumbprint(jwk: &JsonWebKey, sha256: &str, sha384: &str, sha512: &str) } pub mod roundtrip { - use jose::{ - jwa, - jwk::{self, AsymmetricJsonWebKey, JsonWebKeyType}, - }; + use jose::{jwa, jwk}; use pretty_assertions::assert_eq; use crate::{assert_thumbprint, common::TestResult, roundtrip, roundtrip_pair}; #[test] - fn _3_1_ec_public_key() -> TestResult { - roundtrip( - "3_1.ec_public_key", - // RustCrypto and ring do not support P-521 curve - cfg!(feature = "crypto-rustcrypto") || cfg!(feature = "crypto-ring"), - |jwk| { - let JsonWebKeyType::Asymmetric(key) = jwk.key_type() else { - panic!("Expected asymmetric key type"); - }; - - assert!(matches!( - **key, - AsymmetricJsonWebKey::Public(jwk::Public::Ec(jwk::EcPublic::P521(..))) - )); - assert_eq!(jwk.key_usage(), Some(&jwk::KeyUsage::Signing)); - assert_thumbprint( - jwk, - "dHri3SADZkrush5HU_50AoRhcKFryN-PI6jPBtPL55M", - "HncTFMje-quVjjwt2ufqfFb75ZwHLDh9M-VY4wJ9awQkfbu194TmVpeGbG6Ykb9b", - "i8RIsIb6HVP2AO9o38HtraybJAP5veAfBIgynNUqpxlhuvq2UDgSA3JFgGgle1YvmCQDHllAn7MG52Idb8B4fA" - ); - }, - ) - } - - #[test] - fn _3_2_ec_private_key() -> TestResult { - roundtrip( + fn _3_1_and_2_ec() -> TestResult { + roundtrip_pair( "3_2.ec_private_key", + "3_1.ec_public_key", // RustCrypto and ring do not support P-521 curve cfg!(feature = "crypto-rustcrypto") || cfg!(feature = "crypto-ring"), |jwk| { - let JsonWebKeyType::Asymmetric(key) = jwk.key_type() else { - panic!("Expected asymmetric key type"); - }; - - assert!(matches!( - **key, - AsymmetricJsonWebKey::Private(jwk::Private::Ec(jwk::EcPrivate::P521(..))) - )); assert_eq!(jwk.key_usage(), Some(&jwk::KeyUsage::Signing)); - assert_thumbprint( jwk, "dHri3SADZkrush5HU_50AoRhcKFryN-PI6jPBtPL55M", @@ -133,20 +147,8 @@ pub mod roundtrip { } #[test] - fn _3_3_rsa_public_key() -> TestResult { - roundtrip("3_3.rsa_public_key", false, |jwk| { - assert_thumbprint( - jwk, - "9jg46WB3rR_AHD-EBXdN7cBkH1WOu0tA3M9fm21mqTI", - "iRBthSmwxk6o9pTGF6a9yLHohmMXSFRvKoN9rgcbOWFgLldwqED1DrOgDtLq5Q4R", - "FerGBUpYnzT0ptNAC7Y3qNpGINqILXdZ_9-Na3UkPUtDznnAChw7NWluNRjx-lmKDnuO1CpmIZL7e2bzRkQBew", - ); - }) - } - - #[test] - fn _3_4_rsa_private_key() -> TestResult { - roundtrip("3_4.rsa_private_key", false, |jwk| { + fn _3_3_and_4_rsa() -> TestResult { + roundtrip_pair("3_4.rsa_private_key", "3_3.rsa_public_key", false, |jwk| { assert_thumbprint( jwk, "9jg46WB3rR_AHD-EBXdN7cBkH1WOu0tA3M9fm21mqTI", @@ -164,6 +166,11 @@ pub mod roundtrip { Some(&jwa::JsonWebAlgorithm::from(jwa::Hmac::Hs256)) ); + assert!(jwk.is_symmetric()); + assert!(jwk.is_signing_key()); + + assert_eq!(jwk.key_type(), jwk.clone().into_verifying_key().key_type()); + assert_thumbprint( jwk, "RtoRur_1Dir5M4wuOfqNkDYOf9O_4RJ-aHkTA75RLA8", @@ -195,7 +202,7 @@ pub mod roundtrip { #[test] fn ed25519() -> TestResult { - roundtrip_pair("ed25519", "ed25519.pub", |jwk| { + roundtrip_pair("ed25519", "ed25519.pub", false, |jwk| { assert_eq!( jwk.algorithm(), Some(&jwa::JsonWebAlgorithm::from( @@ -281,7 +288,7 @@ pub mod roundtrip { #[test] fn p256() -> TestResult { - roundtrip_pair("p256", "p256.pub", |jwk| { + roundtrip_pair("p256", "p256.pub", false, |jwk| { assert_eq!( jwk.algorithm(), Some(&jwa::JsonWebAlgorithm::from(jwa::EcDSA::Es256)) @@ -298,7 +305,7 @@ pub mod roundtrip { #[test] fn p384() -> TestResult { - roundtrip_pair("p384", "p384.pub", |jwk| { + roundtrip_pair("p384", "p384.pub", false, |jwk| { assert_eq!( jwk.algorithm(), Some(&jwa::JsonWebAlgorithm::from(jwa::EcDSA::Es384)) @@ -313,9 +320,27 @@ pub mod roundtrip { }) } + #[test] + #[cfg_attr(feature = "crypto-ring", ignore)] + fn secp256k1() -> TestResult { + roundtrip_pair("k256", "k256.pub", false, |jwk| { + assert_eq!( + jwk.algorithm(), + Some(&jwa::JsonWebAlgorithm::from(jwa::EcDSA::Es256K)) + ); + + assert_thumbprint( + jwk, + "i0H0zy_Zyc4g9gUfIU3ZgSk21eC_a9B-J_keq5eRVq4", + "NWo1frAmwhk6vYKYK0YCTpJWbgvI-EDV5ZvEFvA_7V4y6VRAG0l4Q_uNkFIiisuL", + "E1oJ78FUrNMsq66wi7AT8jIU4QUMoV_JnYiCqwy2vgDod7yDHMXLkweJ0Vhd1A1TJysPMFNr4Q8yVvQ4Q1fXKg" + ); + }) + } + #[test] fn rsa() -> TestResult { - roundtrip_pair("rsa", "rsa.pub", |jwk| { + roundtrip_pair("rsa", "rsa.pub", false, |jwk| { assert_thumbprint( jwk, "nYPs6qc5zj3VOVKr4yY-EzirO-AcdUl0JC5bcXKGE6Y", @@ -324,6 +349,26 @@ pub mod roundtrip { ); }) } + + #[test] + #[cfg_attr( + any( + feature = "crypto-ring", + feature = "crypto-aws-lc", + feature = "crypto-rustcrypto" + ), + ignore + )] + fn ed448() -> TestResult { + roundtrip_pair("ed448", "ed448.pub", false, |jwk| { + assert_thumbprint( + jwk, + "-K8d13H2SA_vuRYSxn05sQN4hAkeWXFt5XainSnkfZc", + "a_AWZk_w5qSm2XziCOKyHRLU1amUzTlbb1df8Q0JCx3bOaQp0YcLqdHS2Sbyw2PQ", + "qM1ai1zIZ-NRzbkOKxVkOY6DXVXmDWsSXMCQRRy7oIZEwRzQ0gQK5c-cruMkpyYcBs7ftQcofV_YXWfCdKhSWw", + ); + }) + } } // TODO: test to ensure correct length of x, y, d @@ -369,10 +414,7 @@ pub mod generate { use crate::common::TestResult; #[test] - #[cfg_attr( - feature = "crypto-ring", - ignore = "crypto backend does not support ECC key generation" - )] + #[cfg_attr(feature = "crypto-ring", ignore)] fn ec_p256() -> TestResult { let p256 = P256PrivateKey::generate()?; let p256_pub = p256.to_public_key(); @@ -382,10 +424,7 @@ pub mod generate { } #[test] - #[cfg_attr( - feature = "crypto-ring", - ignore = "crypto backend does not support ECC key generation" - )] + #[cfg_attr(feature = "crypto-ring", ignore)] fn ec_p384() -> TestResult { let p384 = P384PrivateKey::generate()?; let p384_pub = p384.to_public_key(); @@ -395,10 +434,7 @@ pub mod generate { } #[test] - #[cfg_attr( - any(feature = "crypto-rustcrypto", feature = "crypto-ring"), - ignore = "crypto backend does not support P-521 curve" - )] + #[cfg_attr(any(feature = "crypto-rustcrypto", feature = "crypto-ring"), ignore)] fn ec_p521() -> TestResult { let p521 = P521PrivateKey::generate()?; let p521_pub = p521.to_public_key(); @@ -408,10 +444,7 @@ pub mod generate { } #[test] - #[cfg_attr( - feature = "crypto-ring", - ignore = "crypto backend does not support RSA key generation" - )] + #[cfg_attr(feature = "crypto-ring", ignore)] fn rsa() -> TestResult { let rsa = rsa::PrivateKey::generate(4096)?; let rsa_pub = rsa.to_public_key(); @@ -427,10 +460,7 @@ pub mod generate { } #[test] - #[cfg_attr( - feature = "crypto-ring", - ignore = "crypto backend does not support Ed448 curve" - )] + #[cfg_attr(feature = "crypto-ring", ignore)] fn ed25519() -> TestResult { let ed25519 = okp::PrivateKey::::generate()?; let ed25519_pub = ed25519.to_public_key(); @@ -446,7 +476,7 @@ pub mod generate { feature = "crypto-ring", feature = "crypto-aws-lc" ), - ignore = "crypto backend does not support Ed448 curve" + ignore )] fn ed448() -> TestResult { let ed448 = okp::PrivateKey::::generate()?; @@ -482,3 +512,46 @@ fn symmetric_key_can_not_strip_secret() -> TestResult { Ok(()) } + +#[test] +fn additional_properties() -> TestResult { + #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] + struct Additional { + #[serde(rename = "additional/one")] + one: String, + another_additional: i32, + } + + impl Checkable for Additional { + fn check( + self, + policy: P, + ) -> Result, (Self, P::Error)> { + Ok(jwk::policy::Checked::new(self, policy)) + } + } + + let key = read_jwk("rsa_with_additional_props.pub")?; + let key: JsonWebKey = serde_json::from_value(key)?; + + assert_eq!(key.additional().one.as_str(), "my rsa key"); + assert_eq!(key.additional().another_additional, 1); + + let untyped = key.clone().into_untyped_additional()?; + assert_eq!( + untyped + .clone() + .deserialize_additional::()? + .additional(), + key.additional() + ); + + let untyped = untyped.additional(); + + assert_eq!(untyped["additional/one"], "my rsa key"); + assert_eq!(untyped["another_additional"], 1); + + let _checked = key.check(jwk::policy::StandardPolicy::new()).unwrap(); + + Ok(()) +} diff --git a/tests/jws.rs b/tests/jws.rs index e69de29..8b13789 100644 --- a/tests/jws.rs +++ b/tests/jws.rs @@ -0,0 +1 @@ + diff --git a/tests/vectors/jwk/ed448.json b/tests/vectors/jwk/ed448.json new file mode 100644 index 0000000..773a2d6 --- /dev/null +++ b/tests/vectors/jwk/ed448.json @@ -0,0 +1,6 @@ +{ + "crv": "Ed448", + "kty": "OKP", + "x": "TLmubeIL94Lp5tUdNMzMS0IoqiWNqBdlmIedMI504TcPLMMXqCcHrlPtxbnKS3mmvZzSzg4bC98A", + "d": "HuEpUexGlbKpPJD0eivXsuTEgymVIEdmpNdbgsSPbZSXOQjX7s9RtumPy1fYzYT2BdmvB7io5rrS" +} diff --git a/tests/vectors/jwk/ed448.pub.json b/tests/vectors/jwk/ed448.pub.json new file mode 100644 index 0000000..edf51a3 --- /dev/null +++ b/tests/vectors/jwk/ed448.pub.json @@ -0,0 +1,5 @@ +{ + "crv": "Ed448", + "kty": "OKP", + "x": "TLmubeIL94Lp5tUdNMzMS0IoqiWNqBdlmIedMI504TcPLMMXqCcHrlPtxbnKS3mmvZzSzg4bC98A" +} diff --git a/tests/vectors/jwk/k256.json b/tests/vectors/jwk/k256.json new file mode 100644 index 0000000..6d8abe2 --- /dev/null +++ b/tests/vectors/jwk/k256.json @@ -0,0 +1,9 @@ +{ + "alg":"ES256K", + "crv":"secp256k1", + "d":"8WieVX5crsjLd1VZmsWa6fdip1apcxv7sasaBhXNxKU", + "key_ops":["sign","verify"], + "kty":"EC", + "x":"KBKyqszcuKAz2PovGR0vu46v8X9nVx43T4rLiMKmuyw", + "y":"jmE55gZG_Qe4tebTrZIwUfwkM7KXEdJHR_yqH87rSp8" +} diff --git a/tests/vectors/jwk/k256.pub.json b/tests/vectors/jwk/k256.pub.json new file mode 100644 index 0000000..8264d51 --- /dev/null +++ b/tests/vectors/jwk/k256.pub.json @@ -0,0 +1,8 @@ +{ + "alg":"ES256K", + "crv":"secp256k1", + "key_ops":["verify"], + "kty":"EC", + "x":"KBKyqszcuKAz2PovGR0vu46v8X9nVx43T4rLiMKmuyw", + "y":"jmE55gZG_Qe4tebTrZIwUfwkM7KXEdJHR_yqH87rSp8" +} diff --git a/tests/vectors/jwk/rsa_with_additional_props.pub.json b/tests/vectors/jwk/rsa_with_additional_props.pub.json new file mode 100644 index 0000000..d61d2f6 --- /dev/null +++ b/tests/vectors/jwk/rsa_with_additional_props.pub.json @@ -0,0 +1,10 @@ +{ + "use": "sig", + "kty": "RSA", + "kid": "nYPs6qc5zj3VOVKr4yY-EzirO-AcdUl0JC5bcXKGE6Y", + "alg": "RS256", + "n": "vxmzEVX_Fus8i8BWT_sC_m389t615iPxKSMavPFv0xEhES42RWkO6yNpb_cwWhlJtoy_UdiRW8-0DHYJIbpiwkw4oRRnfMYX4FU77yjovSQLEhKPfIuYDBuP-9LQgF8_NgB9z1WokSUcH-tAf_35MpiXptGxoFuIe1EE7u1TWpTnMwGBnqO1EvJltyWej9R6rt47oqizn1VN8P2No3tys181B_TE9c6N2tXzWpnm5QY7UZO2zPLtYFGbj7hJCNhc5SL3vt-81KkaNjPcW1rgCIoRKvHVI79n_H81LQdiJDJyIobtvPH3XFQa_tM4CP2ul121E_Zi0tcjuD1zyLCq7Q", + "e": "AQAB", + "additional/one": "my rsa key", + "another_additional": 1 +} From 44b9865130a9c3b4e715494aef4b61f5f1f1de19 Mon Sep 17 00:00:00 2001 From: Justus K Date: Sat, 3 May 2025 13:26:48 +0200 Subject: [PATCH 3/5] chore: remove default feature from Cargo.toml --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index b6b3d65..8520866 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ categories = [ ] [features] -default = ["crypto-openssl"] +default = [] std = [ "thiserror/std", "fluent-uri/std", From ab9fa3edafb99635281a41544da959cf3fcee8b4 Mon Sep 17 00:00:00 2001 From: Justus K Date: Sat, 3 May 2025 13:50:07 +0200 Subject: [PATCH 4/5] fix(tests): skip roundtrip on unsupported crypto backends --- tests/jwk.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/jwk.rs b/tests/jwk.rs index 904dcfb..87709e9 100644 --- a/tests/jwk.rs +++ b/tests/jwk.rs @@ -83,6 +83,10 @@ fn roundtrip_pair( roundtrip(private, unsupported, check)?; roundtrip(public, unsupported, check)?; + if unsupported { + return Ok(()); + } + let private_key: JsonWebKey = serde_json::from_value(read_jwk(private)?)?; let public_key: JsonWebKey = serde_json::from_value(read_jwk(public)?)?; From 5b68aef338e24effb933e93f7db57a8ce89cdbf8 Mon Sep 17 00:00:00 2001 From: Justus K Date: Sat, 3 May 2025 14:25:05 +0200 Subject: [PATCH 5/5] chore: delete tarpaulin-report file --- tarpaulin-report.html | 671 ------------------------------------------ 1 file changed, 671 deletions(-) delete mode 100644 tarpaulin-report.html diff --git a/tarpaulin-report.html b/tarpaulin-report.html deleted file mode 100644 index c0cdb23..0000000 --- a/tarpaulin-report.html +++ /dev/null @@ -1,671 +0,0 @@ - - - - - - - -
- - - - - - \ No newline at end of file