diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 5f50b57597..8628f4ba2f 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -148,32 +148,6 @@ jobs: with: os: ${{matrix.os}} - build-and-test-libjose: - needs: check-for-run-condition - if: ${{ needs.check-for-run-condition.outputs.should-run == 'true' }} - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - os: [ ubuntu-latest ] - include: - - os: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - - name: Setup Rust - uses: './.github/actions/rust/rust-setup' - with: - os: ${{ runner.os }} - job: ${{ github.job }} - - - name: Build - run: cargo build --manifest-path ./libjose/Cargo.toml --release - - - name: Run tests - run: cargo test --manifest-path ./libjose/Cargo.toml --release - build-wasm: needs: check-for-run-condition if: ${{ needs.check-for-run-condition.outputs.should-run == 'true' }} diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 001eeb8710..b3488a554b 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -50,8 +50,3 @@ jobs: # uses: actions-rs-plus/clippy-check@b09a9c37c9df7db8b1a5d52e8fe8e0b6e3d574c4 # with: # args: --manifest-path ./bindings/stronghold-nodejs/Cargo.toml --all-targets --all-features -- -D warnings - - - name: libjose clippy check - uses: actions-rs-plus/clippy-check@b09a9c37c9df7db8b1a5d52e8fe8e0b6e3d574c4 - with: - args: --manifest-path ./libjose/Cargo.toml --all-targets --all-features -- -D warnings diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index 5c98c5de1e..96089f91d1 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -52,9 +52,6 @@ jobs: # - name: stronghold-nodejs fmt check # run: cargo fmt --manifest-path ./bindings/stronghold-nodejs/Cargo.toml --all -- --check - - name: libjose fmt check - run: cargo +nightly fmt --manifest-path ./libjose/Cargo.toml --all -- --check - # Use `dprint` to check Cargo.toml formatting. # To fix, run `dprint fmt` locally. - name: Cargo.toml fmt check diff --git a/Cargo.toml b/Cargo.toml index 4a4d978fc0..58e9a5f092 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,6 @@ exclude = [ "identity_iota_core_legacy", "bindings/stronghold-nodejs", "bindings/wasm", - "libjose", ] [workspace.dependencies] diff --git a/libjose/Cargo.toml b/libjose/Cargo.toml deleted file mode 100644 index 2585c4b939..0000000000 --- a/libjose/Cargo.toml +++ /dev/null @@ -1,70 +0,0 @@ -[package] -name = "libjose" -version = "0.1.0" -authors = ["IOTA Stiftung"] -edition = "2021" -homepage = "https://www.iota.org" -keywords = ["iota", "tangle", "identity", "jose", "jwa", "jwe", "jwk", "jwm", "jws", "jwt"] -license = "Apache-2.0" -readme = "README.md" -repository = "https://github.com/iotaledger/identity.rs" -description = "A library for JOSE (JSON Object Signing and Encryption)" - -[dependencies] -base64 = { version = "0.13", default-features = false } -miniz_oxide = { version = "0.4", default-features = false } -serde = { version = "1.0", default-features = false, features = ["derive"] } -serde_json = { version = "1.0", default-features = false } -url = { version = "2.2", default-features = false, features = ["serde"] } -zeroize = { version = "1.2", default-features = false, features = ["zeroize_derive"] } - -curve25519-dalek = { version = "3.0", default-features = false } -num-bigint-dig = { version = "0.7", default-features = false } -rand = { version = "0.8", default-features = false, features = ["getrandom"] } -rsa = { version = "0.5", default-features = false, features = ["pem", "std"] } -sha-1 = { version = "0.9", default-features = false } -subtle = { version = "2.4", default-features = false } - -[dependencies.iota-crypto] -git = "https://github.com/iotaledger/crypto.rs" -rev = "0967233dd40ba562f575c2073cb8f4a22744e2d4" -default-features = false -features = [ - "aes", - "aes-cbc", - "aes-kw", - "chacha", - "ed25519", - "hmac", - "p256", - "pbkdf", - "random", - "secp256k1", - "sha", - "x25519", - "x448", -] - -[features] -default = ["std"] - -std = [ - "alloc", - "serde/std", - "serde_json/std", -] - -alloc = [ - "base64/alloc", - "serde/alloc", - "serde_json/alloc", -] - -test-rsa-enc = [] -test-rsa-sig = [] - -[package.metadata.docs.rs] -# To build locally: -# RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --all-features --no-deps --workspace --open -all-features = true -rustdoc-args = ["--cfg", "docsrs"] diff --git a/libjose/README.md b/libjose/README.md deleted file mode 100644 index aa4ba05204..0000000000 --- a/libjose/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# libjose - -A library for JSON Object Signing and Encryption (JOSE) diff --git a/libjose/examples/basic_jws.rs b/libjose/examples/basic_jws.rs deleted file mode 100644 index 3f443011df..0000000000 --- a/libjose/examples/basic_jws.rs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! cargo run --example basic_jws - -use libjose::jwk::Jwk; -use libjose::jws::Encoder; -use libjose::jws::JwsAlgorithm; -use libjose::jws::JwsHeader; -use libjose::jwt::JwtClaims; - -fn main() { - let header: JwsHeader = JwsHeader::new(JwsAlgorithm::HS256); - let claims: JwtClaims = JwtClaims::new(); - let secret: Jwk = Jwk::random(header.alg()).unwrap(); - - println!("Header: {}", serde_json::to_string_pretty(&header).unwrap()); - println!("Claims: {}", serde_json::to_string_pretty(&claims).unwrap()); - println!("Secret: {}", serde_json::to_string_pretty(&secret).unwrap()); - - let token: String = Encoder::new() - .recipient((&secret, &header)) - .encode_serde(&claims) - .unwrap(); - - println!("Token: {token}"); -} diff --git a/libjose/examples/custom_claims.rs b/libjose/examples/custom_claims.rs deleted file mode 100644 index 35340ef3ef..0000000000 --- a/libjose/examples/custom_claims.rs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! cargo run --example custom_claims - -#[macro_use] -extern crate serde; - -use libjose::jwk::Jwk; -use libjose::jws::Encoder; -use libjose::jws::JwsAlgorithm; -use libjose::jws::JwsHeader; -use libjose::jwt::JwtClaims; - -#[derive(Debug, PartialEq, Deserialize, Serialize)] -struct MyClaims { - claim1: String, - claim2: Vec, -} - -fn main() { - let header: JwsHeader = JwsHeader::new(JwsAlgorithm::HS256); - let mut claims: JwtClaims = JwtClaims::new(); - let secret: Jwk = Jwk::random(header.alg()).unwrap(); - - claims.set_custom(MyClaims { - claim1: "hello".into(), - claim2: "world".chars().collect(), - }); - - println!("Header: {}", serde_json::to_string_pretty(&header).unwrap()); - println!("Claims: {}", serde_json::to_string_pretty(&claims).unwrap()); - println!("Secret: {}", serde_json::to_string_pretty(&secret).unwrap()); - - let token: String = Encoder::new() - .recipient((&secret, &header)) - .encode_serde(&claims) - .unwrap(); - - println!("Token: {token}"); -} diff --git a/libjose/examples/custom_payload.rs b/libjose/examples/custom_payload.rs deleted file mode 100644 index d3f22706a4..0000000000 --- a/libjose/examples/custom_payload.rs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! cargo run --example custom_payload - -use core::str; - -use libjose::jwk::Jwk; -use libjose::jws::Decoder; -use libjose::jws::Encoder; -use libjose::jws::JwsAlgorithm; -use libjose::jws::JwsHeader; -use libjose::jws::Token; - -fn main() { - let header: JwsHeader = JwsHeader::new(JwsAlgorithm::HS256); - let claims: &str = "hello world"; - let secret: Jwk = Jwk::random(header.alg()).unwrap(); - - println!("Header: {}", serde_json::to_string_pretty(&header).unwrap()); - println!("Claims: {claims:?}"); - println!("Secret: {}", serde_json::to_string_pretty(&secret).unwrap()); - - let token: String = Encoder::new() - .recipient((&secret, &header)) - .encode(claims.as_bytes()) - .unwrap(); - - println!("Token: {token}"); - - let decoded: Token<'_> = Decoder::new(&secret).decode(token.as_bytes()).unwrap(); - let decoded: &str = str::from_utf8(&decoded.claims).unwrap(); - - println!("Decoded: {decoded:?}"); - - assert_eq!(claims, decoded); -} diff --git a/libjose/src/error.rs b/libjose/src/error.rs deleted file mode 100644 index 7e07f1fdda..0000000000 --- a/libjose/src/error.rs +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! Errors that may occur in the library. - -use core::fmt::Display; -use core::fmt::Formatter; - -/// Alias for a `Result` with the error type [Error]. -pub type Result = core::result::Result; - -/// All possible errors that can occur in the library. -#[derive(Debug)] -pub enum Error { - InvalidRsaPrime, - InvalidJson(serde_json::Error), - InvalidBase64(base64::DecodeError), - InvalidDecompression(miniz_oxide::inflate::TINFLStatus), - InvalidUtf8(core::str::Utf8Error), - InvalidClaim(&'static str), - MissingClaim(&'static str), - InvalidParam(&'static str), - MissingParam(&'static str), - InvalidContent(&'static str), - InvalidArray(&'static str), - AlgError(&'static str), - EncError(&'static str), - SigError(&'static str), - KeyError(&'static str), - CryptoError(crypto::Error), -} - -impl Display for Error { - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - match self { - Self::InvalidRsaPrime => f.write_str("Invalid Rsa Prime Value"), - Self::InvalidJson(inner) => f.write_fmt(format_args!("Invalid JSON: {inner}")), - Self::InvalidBase64(inner) => f.write_fmt(format_args!("Invalid Base64: {inner}")), - Self::InvalidDecompression(inner) => f.write_fmt(format_args!("Invalid Decompression: {inner:?}")), - Self::InvalidUtf8(inner) => f.write_fmt(format_args!("Invalid Utf-8: {inner:?}")), - Self::InvalidClaim(inner) => f.write_fmt(format_args!("Invalid Claim: {inner}")), - Self::MissingClaim(inner) => f.write_fmt(format_args!("Missing Claim: {inner}")), - Self::InvalidParam(inner) => f.write_fmt(format_args!("Invalid Param: {inner}")), - Self::MissingParam(inner) => f.write_fmt(format_args!("Missing Param: {inner}")), - Self::InvalidContent(inner) => f.write_fmt(format_args!("Invalid Content: {inner}")), - Self::InvalidArray(inner) => f.write_fmt(format_args!("Invalid Array: {inner}")), - Self::AlgError(inner) => f.write_fmt(format_args!("Unsupported Algorithm: {inner}")), - Self::EncError(inner) => f.write_fmt(format_args!("Encryption Error: {inner}")), - Self::SigError(inner) => f.write_fmt(format_args!("Signature Error: {inner}")), - Self::KeyError(inner) => f.write_fmt(format_args!("Invalid Key Format: {inner}")), - Self::CryptoError(inner) => f.write_fmt(format_args!("Crypto Error: {inner}")), - } - } -} - -#[cfg(feature = "std")] -impl std::error::Error for Error {} - -impl From for Error { - fn from(other: crypto::Error) -> Self { - Self::CryptoError(other) - } -} - -impl From for Error { - fn from(other: serde_json::Error) -> Self { - Self::InvalidJson(other) - } -} - -impl From for Error { - fn from(other: base64::DecodeError) -> Self { - Self::InvalidBase64(other) - } -} - -impl From for Error { - fn from(other: miniz_oxide::inflate::TINFLStatus) -> Self { - Self::InvalidDecompression(other) - } -} - -impl From<::rsa::errors::Error> for Error { - fn from(_: ::rsa::errors::Error) -> Self { - // TODO: Return better errors - Self::CryptoError(crypto::Error::CipherError { alg: "Rsa" }) - } -} diff --git a/libjose/src/jose/cty.rs b/libjose/src/jose/cty.rs deleted file mode 100644 index 56f4bbfb98..0000000000 --- a/libjose/src/jose/cty.rs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use core::fmt::Display; -use core::fmt::Formatter; -use core::fmt::Result; - -/// Possible values of the JOSE "cty" header parameter -/// -/// [More Info](https://tools.ietf.org/html/rfc7519#section-5.2) -#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] -#[allow(non_camel_case_types)] -pub enum JoseContentType { - /// Indicates the content of the token is a JSON Web Token. - /// - /// Note: This indicates a nested JWT structure. - /// - /// [More Info](https://tools.ietf.org/html/rfc7519#section-5.2) - JWT, - /// Indicates the content of the token is a JSON Web Message. - /// - /// Note: This indicates a nested JWM structure. - /// - /// [More Info](https://tools.ietf.org/id/draft-looker-jwm-01.html#rfc.section.4.2) - JWM, -} - -impl JoseContentType { - /// Returns the JOSE "cty" parameter as a `str` slice. - pub const fn name(&self) -> &'static str { - match self { - Self::JWT => "JWT", - Self::JWM => "JWM", - } - } -} - -impl Display for JoseContentType { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - f.write_str(self.name()) - } -} diff --git a/libjose/src/jose/mod.rs b/libjose/src/jose/mod.rs deleted file mode 100644 index dca3f9e18f..0000000000 --- a/libjose/src/jose/mod.rs +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -mod cty; -mod traits; -mod typ; - -pub use self::cty::*; -pub use self::traits::*; -pub use self::typ::*; diff --git a/libjose/src/jose/traits.rs b/libjose/src/jose/traits.rs deleted file mode 100644 index a2407432af..0000000000 --- a/libjose/src/jose/traits.rs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use crate::jwt::JwtHeader; - -pub trait JoseHeader { - fn common(&self) -> &JwtHeader; - - fn has_claim(&self, claim: &str) -> bool; -} - -impl<'a, T: 'a> JoseHeader for &'a T -where - T: JoseHeader, -{ - fn common(&self) -> &JwtHeader { - (**self).common() - } - - fn has_claim(&self, claim: &str) -> bool { - (**self).has_claim(claim) - } -} diff --git a/libjose/src/jose/typ.rs b/libjose/src/jose/typ.rs deleted file mode 100644 index 98f8475802..0000000000 --- a/libjose/src/jose/typ.rs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use core::fmt::Display; -use core::fmt::Formatter; -use core::fmt::Result; - -/// Possible values of the JOSE "typ" header parameter -/// -/// [More Info](https://tools.ietf.org/html/rfc7519#section-5.1) -#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] -#[allow(non_camel_case_types)] -pub enum JoseTokenType { - /// Indicates that the token is a JSON Web Token. - /// - /// [More Info](https://tools.ietf.org/html/rfc7519#section-5.1) - JWT, - /// Indicates the token is a JSON Web Message. - /// - /// [More Info](https://tools.ietf.org/id/draft-looker-jwm-01.html#rfc.section.4.1) - JWM, - /// Indicates that the token is a JWE/JWS using compact serialization. - /// - /// [More Info](https://tools.ietf.org/html/rfc7515#section-4.1.9) - JOSE, - /// Indicates that the token is a JWE/JWS using JSON serialization. - /// - /// [More Info](https://tools.ietf.org/html/rfc7515#section-4.1.9) - #[serde(rename = "JOSE+JSON")] - JOSE_JSON, -} - -impl JoseTokenType { - /// Returns the JOSE "typ" parameter as a `str` slice. - pub const fn name(&self) -> &'static str { - match self { - Self::JWT => "JWT", - Self::JWM => "JWM", - Self::JOSE => "JOSE", - Self::JOSE_JSON => "JOSE+JSON", - } - } -} - -impl Display for JoseTokenType { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - f.write_str(self.name()) - } -} diff --git a/libjose/src/jwe/algorithm.rs b/libjose/src/jwe/algorithm.rs deleted file mode 100644 index d06a4d2a80..0000000000 --- a/libjose/src/jwe/algorithm.rs +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use core::fmt::Display; -use core::fmt::Formatter; - -use crypto::ciphers::aes::Aes128Gcm; -use crypto::ciphers::aes::Aes192Gcm; -use crypto::ciphers::aes::Aes256Gcm; -use crypto::ciphers::chacha::ChaCha20Poly1305; -use crypto::ciphers::chacha::XChaCha20Poly1305; -use crypto::ciphers::traits::Aead; - -use crate::error::Error; -use crate::error::Result; - -/// Supported algorithms for the JSON Web Encryption `alg` claim. -/// -/// [More Info](https://www.iana.org/assignments/jose/jose.xhtml#web-signature-encryption-algorithms) -/// -/// [ChaCha20-Poly1305 (draft)](https://tools.ietf.org/html/draft-amringer-jose-chacha-02) -/// -/// [ECDH-1PU (draft)](https://tools.ietf.org/html/draft-madden-jose-ecdh-1pu-03) -#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)] -#[allow(non_camel_case_types)] -pub enum JweAlgorithm { - /// RSAES-PKCS1-v1_5 - RSA1_5, - /// RSAES OAEP using default parameters - #[serde(rename = "RSA-OAEP")] - RSA_OAEP, - /// RSAES OAEP using SHA-256 and MGF1 with SHA-256 - #[serde(rename = "RSA-OAEP-256")] - RSA_OAEP_256, - /// RSA-OAEP using SHA-384 and MGF1 with SHA-384 - #[serde(rename = "RSA-OAEP-384")] - RSA_OAEP_384, - /// RSA-OAEP using SHA-512 and MGF1 with SHA-512 - #[serde(rename = "RSA-OAEP-512")] - RSA_OAEP_512, - /// AES Key Wrap with default initial value using 128-bit key - A128KW, - /// AES Key Wrap with default initial value using 192-bit key - A192KW, - /// AES Key Wrap with default initial value using 256-bit key - A256KW, - /// Direct use of a shared symmetric key as the CEK - #[serde(rename = "dir")] - DIR, - /// Elliptic Curve Diffie-Hellman Ephemeral Static key agreement using Concat - /// KDF - #[serde(rename = "ECDH-ES")] - ECDH_ES, - /// ECDH-ES using Concat KDF and CEK wrapped with "A128KW" - #[serde(rename = "ECDH-ES+A128KW")] - ECDH_ES_A128KW, - /// ECDH-ES using Concat KDF and CEK wrapped with "A192KW" - #[serde(rename = "ECDH-ES+A192KW")] - ECDH_ES_A192KW, - /// ECDH-ES using Concat KDF and CEK wrapped with "A256KW" - #[serde(rename = "ECDH-ES+A256KW")] - ECDH_ES_A256KW, - /// ECDH-ES using Concat KDF and CEK wrapped with C20PKW - #[serde(rename = "ECDH-ES+C20PKW")] - ECDH_ES_C20PKW, - /// ECDH-ES using Concat KDF and CEK wrapped with XC20PKW - #[serde(rename = "ECDH-ES+XC20PKW")] - ECDH_ES_XC20PKW, - /// Key wrapping with AES GCM using 128-bit key - A128GCMKW, - /// Key wrapping with AES GCM using 192-bit key - A192GCMKW, - /// Key wrapping with AES GCM using 256-bit key - A256GCMKW, - /// PBES2 with HMAC SHA-256 and "A128KW" wrapping - #[serde(rename = "PBES2-HS256+A128KW")] - PBES2_HS256_A128KW, - /// PBES2 with HMAC SHA-384 and "A192KW" wrapping - #[serde(rename = "PBES2-HS384+A192KW")] - PBES2_HS384_A192KW, - /// PBES2 with HMAC SHA-512 and "A256KW" wrapping - #[serde(rename = "PBES2-HS512+A256KW")] - PBES2_HS512_A256KW, - /// ECDH One-Pass Unified Model using one-pass KDF - #[serde(rename = "ECDH-1PU")] - ECDH_1PU, - /// ECDH-1PU using one-pass KDF and CEK wrapped with "A128KW" - #[serde(rename = "ECDH-1PU+A128KW")] - ECDH_1PU_A128KW, - /// ECDH-1PU using one-pass KDF and CEK wrapped with "A192KW" - #[serde(rename = "ECDH-1PU+A192KW")] - ECDH_1PU_A192KW, - /// ECDH-1PU using one-pass KDF and CEK wrapped with "A256KW" - #[serde(rename = "ECDH-1PU+A256KW")] - ECDH_1PU_A256KW, - /// Key wrapping with ChaCha20-Poly1305 - C20PKW, - /// Key wrapping with XChaCha20-Poly1305 - XC20PKW, -} - -impl JweAlgorithm { - pub const ALL: &'static [JweAlgorithm] = &[ - Self::RSA1_5, - Self::RSA_OAEP, - Self::RSA_OAEP_256, - Self::RSA_OAEP_384, - Self::RSA_OAEP_512, - Self::A128KW, - Self::A192KW, - Self::A256KW, - Self::DIR, - Self::ECDH_ES, - Self::ECDH_ES_A128KW, - Self::ECDH_ES_A192KW, - Self::ECDH_ES_A256KW, - Self::ECDH_ES_C20PKW, - Self::ECDH_ES_XC20PKW, - Self::A128GCMKW, - Self::A192GCMKW, - Self::A256GCMKW, - Self::PBES2_HS256_A128KW, - Self::PBES2_HS384_A192KW, - Self::PBES2_HS512_A256KW, - Self::ECDH_1PU, - Self::ECDH_1PU_A128KW, - Self::ECDH_1PU_A192KW, - Self::ECDH_1PU_A256KW, - Self::C20PKW, - Self::XC20PKW, - ]; - - /// Returns the JWE algorithm as a `str` slice. - pub const fn name(self) -> &'static str { - match self { - Self::RSA1_5 => "RSA1_5", - Self::RSA_OAEP => "RSA-OAEP", - Self::RSA_OAEP_256 => "RSA-OAEP-256", - Self::RSA_OAEP_384 => "RSA-OAEP-384", - Self::RSA_OAEP_512 => "RSA-OAEP-512", - Self::A128KW => "A128KW", - Self::A192KW => "A192KW", - Self::A256KW => "A256KW", - Self::DIR => "dir", - Self::ECDH_ES => "ECDH-ES", - Self::ECDH_ES_A128KW => "ECDH-ES+A128KW", - Self::ECDH_ES_A192KW => "ECDH-ES+A192KW", - Self::ECDH_ES_A256KW => "ECDH-ES+A256KW", - Self::ECDH_ES_C20PKW => "ECDH-ES+C20PKW", - Self::ECDH_ES_XC20PKW => "ECDH-ES+XC20PKW", - Self::A128GCMKW => "A128GCMKW", - Self::A192GCMKW => "A192GCMKW", - Self::A256GCMKW => "A256GCMKW", - Self::PBES2_HS256_A128KW => "PBES2-HS256+A128KW", - Self::PBES2_HS384_A192KW => "PBES2-HS384+A192KW", - Self::PBES2_HS512_A256KW => "PBES2-HS512+A256KW", - Self::ECDH_1PU => "ECDH-1PU", - Self::ECDH_1PU_A128KW => "ECDH-1PU+A128KW", - Self::ECDH_1PU_A192KW => "ECDH-1PU+A192KW", - Self::ECDH_1PU_A256KW => "ECDH-1PU+A256KW", - Self::C20PKW => "C20PKW", - Self::XC20PKW => "XC20PKW", - } - } - - pub const fn key_len(self) -> Option { - match self { - Self::RSA1_5 => None, - Self::RSA_OAEP => None, - Self::RSA_OAEP_256 => None, - Self::RSA_OAEP_384 => None, - Self::RSA_OAEP_512 => None, - Self::A128KW => Some(Aes128Gcm::KEY_LENGTH), - Self::A192KW => Some(Aes192Gcm::KEY_LENGTH), - Self::A256KW => Some(Aes256Gcm::KEY_LENGTH), - Self::DIR => None, - Self::ECDH_ES => None, - Self::ECDH_ES_A128KW => Some(Aes128Gcm::KEY_LENGTH), - Self::ECDH_ES_A192KW => Some(Aes192Gcm::KEY_LENGTH), - Self::ECDH_ES_A256KW => Some(Aes256Gcm::KEY_LENGTH), - Self::ECDH_ES_C20PKW => Some(ChaCha20Poly1305::KEY_LENGTH), - Self::ECDH_ES_XC20PKW => Some(XChaCha20Poly1305::KEY_LENGTH), - Self::A128GCMKW => Some(Aes128Gcm::KEY_LENGTH), - Self::A192GCMKW => Some(Aes192Gcm::KEY_LENGTH), - Self::A256GCMKW => Some(Aes256Gcm::KEY_LENGTH), - Self::PBES2_HS256_A128KW => Some(Aes128Gcm::KEY_LENGTH), - Self::PBES2_HS384_A192KW => Some(Aes192Gcm::KEY_LENGTH), - Self::PBES2_HS512_A256KW => Some(Aes256Gcm::KEY_LENGTH), - Self::ECDH_1PU => None, - Self::ECDH_1PU_A128KW => Some(Aes128Gcm::KEY_LENGTH), - Self::ECDH_1PU_A192KW => Some(Aes192Gcm::KEY_LENGTH), - Self::ECDH_1PU_A256KW => Some(Aes256Gcm::KEY_LENGTH), - Self::C20PKW => Some(ChaCha20Poly1305::KEY_LENGTH), - Self::XC20PKW => Some(XChaCha20Poly1305::KEY_LENGTH), - } - } - - pub fn try_key_len(self) -> Result { - self.key_len().ok_or_else(|| Error::KeyError(self.name())) - } -} - -impl Display for JweAlgorithm { - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - f.write_str(self.name()) - } -} diff --git a/libjose/src/jwe/compression.rs b/libjose/src/jwe/compression.rs deleted file mode 100644 index ca61266cbd..0000000000 --- a/libjose/src/jwe/compression.rs +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use core::fmt::Display; -use core::fmt::Formatter; - -use miniz_oxide::deflate::compress_to_vec; -use miniz_oxide::deflate::CompressionLevel; -use miniz_oxide::inflate::decompress_to_vec; - -use crate::error::Result; -use crate::lib::*; - -const DEFLATE_LEVEL: u8 = CompressionLevel::DefaultLevel as u8; - -/// Supported algorithms for the JSON Web Encryption `zip` claim. -/// -/// [More Info](https://www.iana.org/assignments/jose/jose.xhtml#web-encryption-compression-algorithms) -#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] -pub enum JweCompression { - /// Compression with the DEFLATE [RFC1951](https://tools.ietf.org/html/rfc1951) algorithm. - #[serde(rename = "DEF")] - Deflate, -} - -impl JweCompression { - /// Returns the JWE "zip" claim as a `str` slice. - pub const fn name(&self) -> &'static str { - match self { - Self::Deflate => "DEF", - } - } - - pub fn compress(&self, content: &[u8]) -> Result> { - match self { - Self::Deflate => Ok(compress_to_vec(content, DEFLATE_LEVEL)), - } - } - - pub fn decompress(&self, content: &[u8]) -> Result> { - match self { - Self::Deflate => decompress_to_vec(content).map_err(Into::into), - } - } -} - -impl Default for JweCompression { - fn default() -> Self { - Self::Deflate - } -} - -impl Display for JweCompression { - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - f.write_str(self.name()) - } -} diff --git a/libjose/src/jwe/decoder.rs b/libjose/src/jwe/decoder.rs deleted file mode 100644 index 2b05d0dfbd..0000000000 --- a/libjose/src/jwe/decoder.rs +++ /dev/null @@ -1,600 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use core::convert::TryFrom; -use crypto::ciphers::aes::Aes128Gcm; -use crypto::ciphers::aes::Aes192Gcm; -use crypto::ciphers::aes::Aes256Gcm; -use crypto::ciphers::aes_cbc::Aes128CbcHmac256; -use crypto::ciphers::aes_cbc::Aes192CbcHmac384; -use crypto::ciphers::aes_cbc::Aes256CbcHmac512; -use crypto::ciphers::aes_kw::Aes128Kw; -use crypto::ciphers::aes_kw::Aes192Kw; -use crypto::ciphers::aes_kw::Aes256Kw; -use crypto::ciphers::chacha::ChaCha20Poly1305; -use crypto::ciphers::chacha::XChaCha20Poly1305; -use crypto::ciphers::traits::Aead; -use crypto::hashes::sha::SHA256_LEN; -use crypto::hashes::sha::SHA384_LEN; -use crypto::hashes::sha::SHA512_LEN; -use crypto::keys::pbkdf::PBKDF2_HMAC_SHA256; -use crypto::keys::pbkdf::PBKDF2_HMAC_SHA384; -use crypto::keys::pbkdf::PBKDF2_HMAC_SHA512; -use serde_json::from_slice; - -use crate::error::Error; -use crate::error::Result; -use crate::jwe::JweAlgorithm; -use crate::jwe::JweEncryption; -use crate::jwe::JweFormat; -use crate::jwe::JweHeader; -use crate::jwk::EcdhCurve; -use crate::jwk::EcxCurve; -use crate::jwk::Jwk; -use crate::jwk::KeyManagement; -use crate::jwt::JwtHeaderSet; -use crate::lib::*; -use crate::utils::check_slice_param; -use crate::utils::concat_kdf; -use crate::utils::create_aad; -use crate::utils::create_pbes2_salt; -use crate::utils::decode_b64; -use crate::utils::decode_b64_json; -use crate::utils::diffie_hellman; -use crate::utils::filter_non_empty_bytes; -use crate::utils::parse_cek; -use crate::utils::parse_utf8; -use crate::utils::validate_jwe_headers; -use crate::utils::Secret; - -pub type Token = (JweHeader, Vec); - -type HeaderSet<'a> = JwtHeaderSet<'a, JweHeader>; - -type Cek<'a> = Cow<'a, [u8]>; - -const COMPACT_SEGMENTS: usize = 5; - -#[derive(Debug, Deserialize)] -#[serde(deny_unknown_fields)] -struct Recipient<'a> { - header: Option, - encrypted_key: Option<&'a str>, -} - -#[derive(Debug, Deserialize)] -#[serde(deny_unknown_fields)] -struct General<'a> { - protected: Option<&'a str>, - unprotected: Option<&'a str>, - iv: Option<&'a str>, - aad: Option<&'a str>, - ciphertext: &'a str, - tag: Option<&'a str>, - recipients: Vec>, -} - -#[derive(Debug, Deserialize)] -#[serde(deny_unknown_fields)] -struct Flatten<'a> { - protected: Option<&'a str>, - unprotected: Option<&'a str>, - iv: Option<&'a str>, - aad: Option<&'a str>, - ciphertext: &'a str, - tag: Option<&'a str>, - #[serde(flatten)] - recipient: Recipient<'a>, -} - -// ============================================================================= -// ============================================================================= - -pub struct Decoder<'a> { - /// The expected format of the encoded token. - format: JweFormat, - /// The curve used for Ecdh key agreements. - ecdh_curve: EcdhCurve, - /// The secret key used for key agreements and content decryption. - secret: Secret<'a>, - /// The public key used for key agreements. - public: Option>, - /// A list of permitted key management algorithms. - algs: Option>, - /// A list of permitted content encryption algorithms. - encs: Option>, - /// A list of permitted extension parameters. - crits: Option>, - /// The expected key id of the decoded token. - key_id: Option, -} - -impl<'a> Decoder<'a> { - pub fn new(secret: impl Into>) -> Self { - Self { - format: JweFormat::Compact, - ecdh_curve: EcdhCurve::Ecx(EcxCurve::X25519), - secret: secret.into(), - public: None, - algs: None, - encs: None, - crits: None, - key_id: None, - } - } - - pub fn format(mut self, value: JweFormat) -> Self { - self.format = value; - self - } - - pub fn ecdh_curve(mut self, value: impl Into) -> Self { - self.ecdh_curve = value.into(); - self - } - - pub fn public(mut self, value: impl Into>) -> Self { - self.public = Some(value.into()); - self - } - - pub fn algorithm(mut self, value: JweAlgorithm) -> Self { - self.algs.get_or_insert_with(Vec::new).push(value); - self - } - - pub fn encryption(mut self, value: JweEncryption) -> Self { - self.encs.get_or_insert_with(Vec::new).push(value); - self - } - - pub fn critical(mut self, value: impl Into) -> Self { - self.crits.get_or_insert_with(Vec::new).push(value.into()); - self - } - - pub fn key_id(mut self, value: impl Into) -> Self { - self.key_id = Some(value.into()); - self - } - - pub fn decode(&self, data: &[u8]) -> Result { - self.expand(data, |expanded| { - let protected: Option = expanded.protected.map(decode_b64_json).transpose()?; - let unprotected: Option = expanded.unprotected.map(decode_b64_json).transpose()?; - - let protected_ref: Option<&JweHeader> = protected.as_ref(); - let unprotected_ref: Option<&JweHeader> = unprotected.as_ref(); - - validate_jwe_headers( - protected_ref, - unprotected_ref, - expanded.recipients.iter().map(|recipient| recipient.header.as_ref()), - self.crits.as_deref(), - )?; - - let cek: Option<(Cow<'_, [u8]>, Option)> = expanded - .recipients - .into_iter() - .find_map(|recipient| self.expand_recipient(protected_ref, unprotected_ref, recipient).ok()); - - if let Some((cek, header)) = cek { - let merged: HeaderSet<'_> = HeaderSet::new() - .header(&header) - .protected(protected_ref) - .unprotected(unprotected_ref); - - let iv: Vec = expanded.iv.map(decode_b64).transpose()?.unwrap_or_default(); - let tag: Vec = expanded.tag.map(decode_b64).transpose()?.unwrap_or_default(); - let aad: Vec = create_aad(expanded.protected, expanded.aad); - let ciphertext: Vec = decode_b64(expanded.ciphertext)?; - let encryption: JweEncryption = merged.try_enc()?; - let plaintext: Vec = decrypt_content(encryption, &cek, &iv, &aad, &tag, &ciphertext)?; - - let claims: Vec = if let Some(zip) = protected_ref.and_then(JweHeader::zip) { - zip.decompress(&plaintext)? - } else { - plaintext - }; - - // TODO: Return Owned Header Set - Ok((header.or(protected).or(unprotected).unwrap(), claims)) - } else { - Err(Error::InvalidContent("Recipient (not found)")) - } - }) - } - - fn expand_recipient( - &self, - protected: Option<&JweHeader>, - unprotected: Option<&JweHeader>, - mut recipient: Recipient<'_>, - ) -> Result<(Cek<'a>, Option)> { - let header: Option = recipient.header.take(); - - let merged: HeaderSet<'_> = HeaderSet::new() - .header(header.as_ref()) - .protected(protected) - .unprotected(unprotected); - - Ok((self.decrypt_cek(merged, recipient)?, header)) - } - - fn decrypt_cek(&self, header: HeaderSet<'_>, recipient: Recipient<'_>) -> Result> { - let alg: JweAlgorithm = header.try_alg()?; - let enc: JweEncryption = header.try_enc()?; - - self.check_alg(alg)?; - self.check_enc(enc)?; - self.check_kid(header.kid())?; - - let cek: Cow<'_, [u8]> = self.decrypt_key(alg, enc, header, recipient)?; - - // THE CEK length MUST be equal to the required length of the content - // encryption algorithm. - if cek.len() == enc.key_len() { - Ok(cek) - } else { - Err(Error::InvalidContent("CEK (length)")) - } - } - - #[doc(hidden)] - pub fn __test_decrypt_key(&self, protected: &JweHeader) -> Result> { - self.decrypt_key( - protected.alg(), - protected.enc(), - HeaderSet::new().protected(protected), - Recipient { - header: None, - encrypted_key: None, - }, - ) - } - - fn decrypt_key( - &self, - algorithm: JweAlgorithm, - encryption: JweEncryption, - header: HeaderSet<'_>, - recipient: Recipient<'_>, - ) -> Result> { - macro_rules! rsa { - ($padding:ident, $recipient:expr, $secret:expr) => {{ - let ctx: Vec = parse_cek($recipient.encrypted_key)?; - let key: _ = $secret.to_rsa_secret()?; - let pad: _ = $crate::rsa_padding!(@$padding); - let ptx: Vec = key.decrypt(pad, &ctx)?; - - Ok(Cow::Owned(ptx)) - }}; - } - - macro_rules! aead { - ($impl:ident, $header:expr, $recipient:expr, $secret:expr) => {{ - let ctx: Vec = parse_cek($recipient.encrypted_key)?; - let key: Cow<'_, [u8]> = $secret.to_oct_key($impl::KEY_LENGTH)?; - let iv: Vec = $header.try_iv().and_then(decode_b64)?; - let tag: Vec = $header.try_tag().and_then(decode_b64)?; - let mut ptx: Vec = vec![0; ctx.len()]; - - $impl::try_decrypt(&key, &iv, &[], &mut ptx, &ctx, &tag)?; - - Ok(Cow::Owned(ptx)) - }}; - } - - macro_rules! pbes2 { - (($impl:ident, $digest_len:ident), $wrap:ident, $header:expr, $recipient:expr, $secret:expr) => {{ - let ctx: Vec = parse_cek($recipient.encrypted_key)?; - let key: Cow<'_, [u8]> = $secret.to_oct_key(0)?; - let p2s: Vec = $header.try_p2s().and_then(decode_b64)?; - let p2c: usize = usize::try_from($header.try_p2c()?).map_err(|_| Error::InvalidClaim("p2c"))?; - let salt: Vec = create_pbes2_salt($header.try_alg()?.name(), &p2s); - let mut derived: [u8; $digest_len / 2] = [0; $digest_len / 2]; - - $impl(&key, &salt, p2c, &mut derived)?; - - let mut ptx: Vec = ctx - .len() - .checked_sub($wrap::BLOCK) - .ok_or(Error::InvalidContent("CEK (length)")) - .map(|length| vec![0; length])?; - - $wrap::new(&derived).unwrap_key(&ctx, &mut ptx)?; - - Ok(Cow::Owned(ptx)) - }}; - } - - macro_rules! aes_kw { - ($impl:ident, $recipient:expr, $secret:expr) => {{ - let ctx: Vec = parse_cek($recipient.encrypted_key)?; - let key: Cow<'_, [u8]> = $secret.to_oct_key($impl::KEY_LENGTH)?; - - let mut ptx: Vec = ctx - .len() - .checked_sub($impl::BLOCK) - .ok_or(Error::InvalidContent("CEK (length)")) - .map(|length| vec![0; length])?; - - $impl::new(&key).unwrap_key(&ctx, &mut ptx)?; - - Ok(Cow::Owned(ptx)) - }}; - } - - macro_rules! ecdh_kw { - (@es, $wrap:ident, $header:expr, $recipient:expr, $this:expr) => { - ecdh_kw!(derive_ecdh_es, $wrap, $header, $recipient, $this) - }; - (@1pu, $wrap:ident, $header:expr, $recipient:expr, $this:expr) => { - ecdh_kw!(derive_ecdh_1pu, $wrap, $header, $recipient, $this) - }; - ($derive:ident, $wrap:ident, $header:expr, $recipient:expr, $this:expr) => {{ - let algorithm: &str = $header.try_alg()?.name(); - let key_len: usize = $header.try_alg()?.try_key_len()?; - let deriver: EcdhDeriver<'_, '_> = EcdhDeriver::new($this); - let derived: Vec = deriver.$derive(&$header, algorithm, key_len)?; - let ctx: Vec = parse_cek($recipient.encrypted_key)?; - - let mut ptx: Vec = ctx - .len() - .checked_sub($wrap::BLOCK) - .ok_or(Error::InvalidContent("CEK (length)")) - .map(|length| vec![0; length])?; - - $wrap::new(&derived).unwrap_key(&ctx, &mut ptx)?; - - Ok(Cow::Owned(ptx)) - }}; - } - - macro_rules! ecdh_chacha { - (@es, $wrap:ident, $header:expr, $recipient:expr, $this:expr) => { - ecdh_chacha!(derive_ecdh_es, $wrap, $header, $recipient, $this) - }; - (@1pu, $wrap:ident, $header:expr, $recipient:expr, $this:expr) => { - ecdh_chacha!(derive_ecdh_1pu, $wrap, $header, $recipient, $this) - }; - ($derive:ident, $wrap:ident, $header:expr, $recipient:expr, $this:expr) => {{ - let algorithm: &str = $header.try_alg()?.name(); - let key_len: usize = $header.try_alg()?.try_key_len()?; - let deriver: EcdhDeriver<'_, '_> = EcdhDeriver::new($this); - let derived: Vec = deriver.$derive(&header, algorithm, key_len)?; - let ctx: Vec = parse_cek($recipient.encrypted_key)?; - let iv: Vec = $header.try_iv().and_then(decode_b64)?; - let tag: Vec = $header.try_tag().and_then(decode_b64)?; - - let mut ptx: Vec = vec![0; ctx.len()]; - - $wrap::try_decrypt(&derived, &iv, &[], &mut ptx, &ctx, &tag)?; - - Ok(Cow::Owned(ptx)) - }}; - } - - if KeyManagement::from(algorithm).is_direct() && recipient.encrypted_key.is_some() { - return Err(Error::EncError("CEK (non-empty)")); - } - - match algorithm { - JweAlgorithm::RSA1_5 => rsa!(RSA1_5, recipient, self.secret), - JweAlgorithm::RSA_OAEP => rsa!(RSA_OAEP, recipient, self.secret), - JweAlgorithm::RSA_OAEP_256 => rsa!(RSA_OAEP_256, recipient, self.secret), - JweAlgorithm::RSA_OAEP_384 => rsa!(RSA_OAEP_384, recipient, self.secret), - JweAlgorithm::RSA_OAEP_512 => rsa!(RSA_OAEP_512, recipient, self.secret), - JweAlgorithm::A128KW => aes_kw!(Aes128Kw, recipient, self.secret), - JweAlgorithm::A192KW => aes_kw!(Aes192Kw, recipient, self.secret), - JweAlgorithm::A256KW => aes_kw!(Aes256Kw, recipient, self.secret), - JweAlgorithm::DIR => self.secret.to_oct_key(0), - JweAlgorithm::ECDH_ES => EcdhDeriver::new(self) - .derive_ecdh_es(&header, encryption.name(), encryption.key_len()) - .map(Cow::Owned), - JweAlgorithm::ECDH_ES_A128KW => ecdh_kw!(@es, Aes128Kw, header, recipient, self), - JweAlgorithm::ECDH_ES_A192KW => ecdh_kw!(@es, Aes192Kw, header, recipient, self), - JweAlgorithm::ECDH_ES_A256KW => ecdh_kw!(@es, Aes256Kw, header, recipient, self), - JweAlgorithm::ECDH_ES_C20PKW => ecdh_chacha!(@es, ChaCha20Poly1305, header, recipient, self), - JweAlgorithm::ECDH_ES_XC20PKW => ecdh_chacha!(@es, XChaCha20Poly1305, header, recipient, self), - JweAlgorithm::A128GCMKW => { - aead!(Aes128Gcm, header, recipient, self.secret) - } - JweAlgorithm::A192GCMKW => { - aead!(Aes192Gcm, header, recipient, self.secret) - } - JweAlgorithm::A256GCMKW => { - aead!(Aes256Gcm, header, recipient, self.secret) - } - JweAlgorithm::PBES2_HS256_A128KW => { - pbes2!( - (PBKDF2_HMAC_SHA256, SHA256_LEN), - Aes128Kw, - header, - recipient, - self.secret - ) - } - JweAlgorithm::PBES2_HS384_A192KW => { - pbes2!( - (PBKDF2_HMAC_SHA384, SHA384_LEN), - Aes192Kw, - header, - recipient, - self.secret - ) - } - JweAlgorithm::PBES2_HS512_A256KW => { - pbes2!( - (PBKDF2_HMAC_SHA512, SHA512_LEN), - Aes256Kw, - header, - recipient, - self.secret - ) - } - JweAlgorithm::ECDH_1PU => EcdhDeriver::new(self) - .derive_ecdh_1pu(&header, encryption.name(), encryption.key_len()) - .map(Cow::Owned), - JweAlgorithm::ECDH_1PU_A128KW => ecdh_kw!(@1pu, Aes128Kw, header, recipient, self), - JweAlgorithm::ECDH_1PU_A192KW => ecdh_kw!(@1pu, Aes192Kw, header, recipient, self), - JweAlgorithm::ECDH_1PU_A256KW => ecdh_kw!(@1pu, Aes256Kw, header, recipient, self), - JweAlgorithm::C20PKW => { - aead!(ChaCha20Poly1305, header, recipient, self.secret) - } - JweAlgorithm::XC20PKW => { - aead!(XChaCha20Poly1305, header, recipient, self.secret) - } - } - } - - fn expand(&self, data: &[u8], format: impl Fn(Expanded<'_>) -> Result) -> Result { - match self.format { - JweFormat::Compact => { - let split: Vec<&[u8]> = data.split(|byte| *byte == b'.').collect(); - - if split.len() != COMPACT_SEGMENTS { - return Err(Error::InvalidContent("Invalid Segments")); - } - - format(Expanded { - protected: filter_non_empty_bytes(split[0]), - unprotected: None, - recipients: vec![Recipient { - header: None, - encrypted_key: filter_non_empty_bytes(split[1]).map(parse_utf8).transpose()?, - }], - iv: filter_non_empty_bytes(split[2]), - aad: None, - ciphertext: split[3], - tag: filter_non_empty_bytes(split[4]), - }) - } - JweFormat::General => { - let data: General<'_> = from_slice(data)?; - - format(Expanded { - protected: filter_non_empty_bytes(data.protected), - unprotected: filter_non_empty_bytes(data.unprotected), - recipients: data.recipients, - iv: filter_non_empty_bytes(data.iv), - aad: filter_non_empty_bytes(data.aad), - ciphertext: data.ciphertext.as_bytes(), - tag: filter_non_empty_bytes(data.tag), - }) - } - JweFormat::Flatten => { - let data: Flatten<'_> = from_slice(data)?; - - format(Expanded { - protected: filter_non_empty_bytes(data.protected), - unprotected: filter_non_empty_bytes(data.unprotected), - recipients: vec![data.recipient], - iv: filter_non_empty_bytes(data.iv), - aad: filter_non_empty_bytes(data.aad), - ciphertext: data.ciphertext.as_bytes(), - tag: filter_non_empty_bytes(data.tag), - }) - } - } - } - - fn check_alg(&self, value: JweAlgorithm) -> Result<()> { - check_slice_param("alg", self.algs.as_deref(), &value) - } - - fn check_enc(&self, value: JweEncryption) -> Result<()> { - check_slice_param("enc", self.encs.as_deref(), &value) - } - - fn check_kid(&self, value: Option<&str>) -> Result<()> { - if self.key_id.as_deref() == value { - Ok(()) - } else { - Err(Error::InvalidParam("kid")) - } - } -} - -// ============================================================================= -// ============================================================================= - -#[derive(Debug)] -struct Expanded<'a> { - protected: Option<&'a [u8]>, - unprotected: Option<&'a [u8]>, - recipients: Vec>, - iv: Option<&'a [u8]>, - aad: Option<&'a [u8]>, - ciphertext: &'a [u8], - tag: Option<&'a [u8]>, -} - -fn decrypt_content( - encryption: JweEncryption, - key: &[u8], - iv: &[u8], - aad: &[u8], - tag: &[u8], - ciphertext: &[u8], -) -> Result> { - let mut plaintext: Vec = vec![0; ciphertext.len()]; - - let length: usize = match encryption { - JweEncryption::A128CBC_HS256 => Aes128CbcHmac256::try_decrypt(key, iv, aad, &mut plaintext, ciphertext, tag)?, - JweEncryption::A192CBC_HS384 => Aes192CbcHmac384::try_decrypt(key, iv, aad, &mut plaintext, ciphertext, tag)?, - JweEncryption::A256CBC_HS512 => Aes256CbcHmac512::try_decrypt(key, iv, aad, &mut plaintext, ciphertext, tag)?, - JweEncryption::A128GCM => Aes128Gcm::try_decrypt(key, iv, aad, &mut plaintext, ciphertext, tag)?, - JweEncryption::A192GCM => Aes192Gcm::try_decrypt(key, iv, aad, &mut plaintext, ciphertext, tag)?, - JweEncryption::A256GCM => Aes256Gcm::try_decrypt(key, iv, aad, &mut plaintext, ciphertext, tag)?, - JweEncryption::C20P => ChaCha20Poly1305::try_decrypt(key, iv, aad, &mut plaintext, ciphertext, tag)?, - JweEncryption::XC20P => XChaCha20Poly1305::try_decrypt(key, iv, aad, &mut plaintext, ciphertext, tag)?, - }; - - plaintext.truncate(length); - - Ok(plaintext) -} - -struct EcdhDeriver<'a, 'b>(&'b Decoder<'a>); - -impl<'a, 'b> EcdhDeriver<'a, 'b> { - fn new(decoder: &'b Decoder<'a>) -> Self { - Self(decoder) - } - - fn derive_ecdh_es(&self, header: &HeaderSet<'_>, algorithm: &str, key_len: usize) -> Result> { - self.derive_ecdh_key(header, algorithm, key_len, |epk| { - diffie_hellman(self.0.ecdh_curve, epk, self.0.secret) - }) - } - - fn derive_ecdh_1pu(&self, header: &HeaderSet<'_>, algorithm: &str, key_len: usize) -> Result> { - self.derive_ecdh_key(header, algorithm, key_len, |epk| { - let public: Secret<'a> = self.0.public.ok_or(Error::EncError("Missing ECDH-1PU Public Key"))?; - let ze: Vec = diffie_hellman(self.0.ecdh_curve, epk, self.0.secret)?; - let zs: Vec = diffie_hellman(self.0.ecdh_curve, public, self.0.secret)?; - - Ok([ze, zs].concat()) - }) - } - - fn derive_ecdh_key( - &self, - header: &HeaderSet<'_>, - algorithm: &str, - key_len: usize, - key_exchange: impl Fn(&Jwk) -> Result>, - ) -> Result> { - let apu: Option> = header.apu().map(decode_b64).transpose()?; - let apv: Option> = header.apv().map(decode_b64).transpose()?; - - concat_kdf( - algorithm, - key_len, - &key_exchange(header.try_epk()?)?, - apu.as_deref().unwrap_or_default(), - apv.as_deref().unwrap_or_default(), - ) - } -} diff --git a/libjose/src/jwe/encoder.rs b/libjose/src/jwe/encoder.rs deleted file mode 100644 index 55cda7b059..0000000000 --- a/libjose/src/jwe/encoder.rs +++ /dev/null @@ -1,756 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use core::convert::TryFrom as _; -use core::num::NonZeroUsize; -use crypto::ciphers::aes::Aes128Gcm; -use crypto::ciphers::aes::Aes192Gcm; -use crypto::ciphers::aes::Aes256Gcm; -use crypto::ciphers::aes_cbc::Aes128CbcHmac256; -use crypto::ciphers::aes_cbc::Aes192CbcHmac384; -use crypto::ciphers::aes_cbc::Aes256CbcHmac512; -use crypto::ciphers::aes_kw::Aes128Kw; -use crypto::ciphers::aes_kw::Aes192Kw; -use crypto::ciphers::aes_kw::Aes256Kw; -use crypto::ciphers::chacha::ChaCha20Poly1305; -use crypto::ciphers::chacha::XChaCha20Poly1305; -use crypto::ciphers::traits::Aead; -use crypto::ciphers::traits::Nonce; -use crypto::ciphers::traits::Tag; -use crypto::hashes::sha::SHA256_LEN; -use crypto::hashes::sha::SHA384_LEN; -use crypto::hashes::sha::SHA512_LEN; -use crypto::keys::pbkdf::PBKDF2_HMAC_SHA256; -use crypto::keys::pbkdf::PBKDF2_HMAC_SHA384; -use crypto::keys::pbkdf::PBKDF2_HMAC_SHA512; -use serde::Serialize; -use serde_json::to_vec; - -use crate::error::Error; -use crate::error::Result; -use crate::jwe::JweAlgorithm; -use crate::jwe::JweEncryption; -use crate::jwe::JweFormat; -use crate::jwe::JweHeader; -use crate::jwe::Recipient; -use crate::jwk::Jwk; -use crate::jwt::JwtHeaderSet; -use crate::lib::*; -use crate::utils::concat_kdf; -use crate::utils::create_aad; -use crate::utils::create_pbes2_salt; -use crate::utils::decode_b64; -use crate::utils::diffie_hellman; -use crate::utils::encode_b64; -use crate::utils::encode_b64_json; -use crate::utils::random_bytes; -use crate::utils::validate_jwe_headers; -use crate::utils::Secret; - -type HeaderSet<'a> = JwtHeaderSet<'a, JweHeader>; - -const MIN_P2S: usize = 8; -const MIN_P2C: usize = 1000; - -macro_rules! to_json { - ($data:expr) => {{ - ::serde_json::to_string(&$data).map_err(Into::into) - }}; -} - -#[derive(Serialize)] -struct __Recipient<'a> { - #[serde(skip_serializing_if = "Option::is_none")] - header: Option<&'a JweHeader>, - #[serde(skip_serializing_if = "Option::is_none")] - encrypted_key: Option<&'a str>, -} - -#[derive(Serialize)] -struct General<'a> { - protected: Option<&'a str>, - unprotected: Option<&'a str>, - #[serde(skip_serializing_if = "Option::is_none")] - iv: Option<&'a str>, - #[serde(skip_serializing_if = "Option::is_none")] - aad: Option<&'a str>, - ciphertext: &'a str, - #[serde(skip_serializing_if = "Option::is_none")] - tag: Option<&'a str>, - recipients: Vec<__Recipient<'a>>, -} - -#[derive(Serialize)] -struct Flatten<'a> { - protected: Option<&'a str>, - unprotected: Option<&'a str>, - #[serde(skip_serializing_if = "Option::is_none")] - iv: Option<&'a str>, - #[serde(skip_serializing_if = "Option::is_none")] - aad: Option<&'a str>, - ciphertext: &'a str, - #[serde(skip_serializing_if = "Option::is_none")] - tag: Option<&'a str>, - #[serde(flatten)] - recipient: __Recipient<'a>, -} - -// ============================================================================= -// ============================================================================= - -pub struct Encoder<'a> { - /// The output format of the encoded token. - format: JweFormat, - /// The secret key used for key agreements. - secret: Option>, - /// Additional authenticated data. - aad: Option<&'a [u8]>, - /// Agreement PartyUInfo used with Concat KDF. - apu: Option<&'a [u8]>, - /// Agreement PartyVInfo used with Concat KDF - apv: Option<&'a [u8]>, - /// The salt input used with PBES2. - pbes2_p2s: usize, - /// The PBKDF2 iteration count used with PBES2. - pbes2_p2c: usize, - /// The integrity-protected JOSE header. - protected: Option<&'a JweHeader>, - /// The non integrity-protected JOSE header. - unprotected: Option<&'a JweHeader>, - /// Per-recipient configuration. - recipients: Vec>, -} - -impl Default for Encoder<'_> { - fn default() -> Self { - Self::new() - } -} - -impl<'a> Encoder<'a> { - pub const fn new() -> Self { - Self { - format: JweFormat::Compact, - secret: None, - aad: None, - apu: None, - apv: None, - pbes2_p2s: MIN_P2S, - pbes2_p2c: MIN_P2C, - protected: None, - unprotected: None, - recipients: Vec::new(), - } - } - - pub fn format(mut self, value: JweFormat) -> Self { - self.format = value; - self - } - - pub fn secret(mut self, value: impl Into>) -> Self { - self.secret = Some(value.into()); - self - } - - pub fn aad(mut self, value: &'a [u8]) -> Self { - self.aad = Some(value); - self - } - - pub fn apu(mut self, value: &'a [u8]) -> Self { - self.apu = Some(value); - self - } - - pub fn apv(mut self, value: &'a [u8]) -> Self { - self.apv = Some(value); - self - } - - pub fn protected(mut self, value: &'a JweHeader) -> Self { - self.protected = Some(value); - self - } - - pub fn unprotected(mut self, value: &'a JweHeader) -> Self { - self.unprotected = Some(value); - self - } - - pub fn recipient(mut self, value: impl Into>) -> Self { - self.recipients.push(value.into()); - self - } - - pub fn encode_serde(&self, claims: &T) -> Result - where - T: Serialize, - { - self.encode(&to_vec(claims)?) - } - - pub fn encode(&self, claims: &[u8]) -> Result { - if self.recipients.is_empty() { - return Err(Error::EncError("Missing Recipients")); - } - - self.validate()?; - - let mut context: __Context<'_, '_> = __Context::new(self, self.recipients.len()); - - for recipient in self.recipients.iter() { - context.expand_recipient(self.protected, self.unprotected, *recipient)?; - } - - let encryption: JweEncryption = HeaderSet::new() - .protected(self.protected) - .unprotected(self.unprotected) - .try_enc()?; - - let encryption_key: Cow<'_, [u8]> = if let Some(cek) = context.encryption_key { - cek - } else { - Cow::Owned(random_bytes(encryption.key_len())?) - }; - - let compressed: Vec; - let payload: &[u8] = if let Some(zip) = self.protected.and_then(JweHeader::zip) { - compressed = zip.compress(claims)?; - &compressed - } else { - claims - }; - - let recipients: Vec<(Option, JweHeader)> = context - .recipients - .into_iter() - .map(|(recipient, mut output)| { - let encrypted_key: Option = self - .encrypt_cek(&encryption_key, &mut output, recipient)? - .map(encode_b64); - - Ok((encrypted_key, output)) - }) - .collect::>()?; - - let protected_b64: Option = if self.format == JweFormat::Compact { - assert_eq!(recipients.len(), 1); - Some(encode_b64_json(&recipients[0].1)?) - } else { - assert_eq!(recipients.len(), self.recipients.len()); - self.protected.map(encode_b64_json).transpose()? - }; - - let aad_b64: Option = self.aad.map(encode_b64); - let aad: Vec = create_aad(protected_b64.as_deref(), aad_b64.as_deref()); - let iv: Vec = random_bytes(encryption.iv_len())?; - let (ciphertext, tag): _ = encrypt_content(encryption, &encryption_key, &iv, &aad, payload)?; - - match (self.format, &*recipients) { - (JweFormat::Compact, [(encrypted_key, _)]) => Ok(format!( - "{}.{}.{}.{}.{}", - protected_b64.as_deref().unwrap_or_default(), - encrypted_key.as_deref().unwrap_or_default(), - encode_b64(iv), - encode_b64(ciphertext), - encode_b64(tag), - )), - (JweFormat::General, _) => { - let recipients: Vec<__Recipient<'_>> = recipients - .iter() - .map(|recipient| __Recipient { - encrypted_key: recipient.0.as_deref(), - header: Some(&recipient.1), - }) - .collect(); - - to_json!(General { - protected: protected_b64.as_deref(), - unprotected: self.unprotected.map(encode_b64_json).transpose()?.as_deref(), - ciphertext: &encode_b64(ciphertext), - aad: aad_b64.as_deref(), - iv: Some(&encode_b64(iv)), - tag: Some(&encode_b64(tag)), - recipients, - }) - } - (JweFormat::Flatten, [(encrypted_key, header)]) => { - to_json!(Flatten { - protected: protected_b64.as_deref(), - unprotected: self.unprotected.map(encode_b64_json).transpose()?.as_deref(), - ciphertext: &encode_b64(ciphertext), - aad: aad_b64.as_deref(), - iv: Some(&encode_b64(iv)), - tag: Some(&encode_b64(tag)), - recipient: __Recipient { - encrypted_key: encrypted_key.as_deref(), - header: Some(header), - }, - }) - } - _ => unreachable!(), - } - } - - fn generate_cek<'cek>( - &self, - algorithm: JweAlgorithm, - encryption: JweEncryption, - output: &mut JweHeader, - recipient: Recipient<'cek>, - ) -> Result>> { - match algorithm { - JweAlgorithm::DIR => { - let key: Cow<'_, [u8]> = recipient.public.to_oct_key(0)?; - - if key.len() != encryption.key_len() { - return Err(Error::EncError("CEK (length)")); - } - - Ok(Some(key)) - } - JweAlgorithm::ECDH_ES => EcdhDeriver::new(self, &recipient) - .derive_ecdh_es(output, encryption.name(), encryption.key_len()) - .map(Cow::Owned) - .map(Some), - JweAlgorithm::ECDH_1PU => EcdhDeriver::new(self, &recipient) - .derive_ecdh_1pu(output, encryption.name(), encryption.key_len()) - .map(Cow::Owned) - .map(Some), - _ => Ok(None), - } - } - - fn extract_p2s(&self, output: &mut JweHeader) -> Result> { - match output.p2s() { - Some(p2s) => { - let p2s: Vec = decode_b64(p2s)?; - - if p2s.len() < MIN_P2S { - return Err(Error::InvalidClaim("p2s")); - } - - Ok(p2s) - } - None => { - let p2s: Vec = random_bytes(self.pbes2_p2s)?; - output.set_p2s(encode_b64(&p2s)); - Ok(p2s) - } - } - } - - fn extract_p2c(&self, output: &mut JweHeader) -> Result { - match output.p2c() { - Some(p2c) => usize::try_from(p2c).map_err(|_| Error::InvalidClaim("p2c")), - None => { - output.set_p2c(self.pbes2_p2c as u64); - - Ok(self.pbes2_p2c) - } - } - } - - fn encrypt_cek( - &self, - encryption_key: &[u8], - output: &mut JweHeader, - recipient: Recipient<'_>, - ) -> Result>> { - macro_rules! rsa { - ($padding:ident, $encryption_key:expr, $public:expr) => {{ - use ::rsa::PublicKey; - use ::rand::rngs::OsRng; - - let key: _ = $public.to_rsa_public()?; - let pad: _ = $crate::rsa_padding!(@$padding); - let ctx: Vec = key.encrypt(&mut OsRng, pad, $encryption_key)?; - - Ok(Some(ctx)) - }}; - } - - macro_rules! aead { - ($impl:ident, $encryption_key:expr, $public:expr, $output:expr) => {{ - let key: Cow<'_, [u8]> = $public.to_oct_key($impl::KEY_LENGTH)?; - let nonce: Nonce<$impl> = $impl::random_nonce()?; - - let mut ctx: Vec = vec![0; $encryption_key.len()]; - let mut tag: Tag<$impl> = Default::default(); - - $impl::try_encrypt(&key, &nonce, &[], $encryption_key, &mut ctx, &mut tag)?; - - $output.set_iv(encode_b64(&nonce)); - $output.set_tag(encode_b64(&tag)); - - Ok(Some(ctx)) - }}; - } - - macro_rules! pbes2 { - (($impl:ident, $digest_len:ident), $wrap:ident, $encryption_key:expr, $public:expr, $output:expr, $this:expr) => {{ - let key: Cow<'_, [u8]> = $public.to_oct_key(0)?; - let p2s: Vec = $this.extract_p2s($output)?; - let p2c: usize = $this.extract_p2c($output)?; - let salt: Vec = create_pbes2_salt($output.alg().name(), &p2s); - let mut derived: [u8; $digest_len / 2] = [0; $digest_len / 2]; - - $impl(&key, &salt, p2c, &mut derived)?; - - let mut ctx: Vec = vec![0; $encryption_key.len() + $wrap::BLOCK]; - - $wrap::new(&derived).wrap_key($encryption_key, &mut ctx)?; - - Ok(Some(ctx)) - }}; - } - - macro_rules! aes_kw { - ($impl:ident, $encryption_key:expr, $public:expr) => {{ - let key: Cow<'_, [u8]> = $public.to_oct_key($impl::KEY_LENGTH)?; - let mut ctx: Vec = vec![0; $encryption_key.len() + $impl::BLOCK]; - - $impl::new(&key).wrap_key($encryption_key, &mut ctx)?; - - Ok(Some(ctx)) - }}; - } - - macro_rules! ecdh_kw { - (@es, $wrap:ident, $encryption_key:expr, $recipient:expr, $output:expr, $this:expr) => {{ - ecdh_kw!(derive_ecdh_es, $wrap, $encryption_key, $recipient, $output, $this) - }}; - (@1pu, $wrap:ident, $encryption_key:expr, $recipient:expr, $output:expr, $this:expr) => {{ - ecdh_kw!(derive_ecdh_1pu, $wrap, $encryption_key, $recipient, $output, $this) - }}; - ($derive:ident, $wrap:ident, $encryption_key:expr, $recipient:expr, $output:expr, $this:expr) => {{ - let algorithm: &str = $output.alg().name(); - let key_len: usize = $output.alg().try_key_len()?; - let deriver: EcdhDeriver<'_, '_> = EcdhDeriver::new($this, &$recipient); - let derived: Vec = deriver.$derive($output, algorithm, key_len)?; - let mut ctx: Vec = vec![0; $encryption_key.len() + $wrap::BLOCK]; - - $wrap::new(&derived).wrap_key($encryption_key, &mut ctx)?; - - Ok(Some(ctx)) - }}; - } - - macro_rules! ecdh_chacha { - (@es, $wrap:ident, $encryption_key:expr, $recipient:expr, $output:expr, $this:expr) => {{ - ecdh_chacha!(derive_ecdh_es, $wrap, $encryption_key, $recipient, $output, $this) - }}; - (@1pu, $wrap:ident, $encryption_key:expr, $recipient:expr, $output:expr, $this:expr) => {{ - ecdh_chacha!(derive_ecdh_1pu, $wrap, $encryption_key, $recipient, $output, $this) - }}; - ($derive:ident, $wrap:ident, $encryption_key:expr, $recipient:expr, $output:expr, $this:expr) => {{ - let algorithm: &str = $output.alg().name(); - let key_len: usize = $output.alg().try_key_len()?; - let deriver: EcdhDeriver<'_, '_> = EcdhDeriver::new($this, &$recipient); - let derived: Vec = deriver.$derive($output, algorithm, key_len)?; - let nonce: Nonce<$wrap> = $wrap::random_nonce()?; - - let mut ctx: Vec = vec![0; $encryption_key.len()]; - let mut tag: Tag<$wrap> = Default::default(); - - $wrap::try_encrypt(&derived, &nonce, &[], $encryption_key, &mut ctx, &mut tag)?; - - $output.set_iv(encode_b64(&nonce)); - $output.set_tag(encode_b64(&tag)); - - Ok(Some(ctx)) - }}; - } - - match output.alg() { - JweAlgorithm::RSA1_5 => rsa!(RSA1_5, encryption_key, recipient.public), - JweAlgorithm::RSA_OAEP => rsa!(RSA_OAEP, encryption_key, recipient.public), - JweAlgorithm::RSA_OAEP_256 => rsa!(RSA_OAEP_256, encryption_key, recipient.public), - JweAlgorithm::RSA_OAEP_384 => rsa!(RSA_OAEP_384, encryption_key, recipient.public), - JweAlgorithm::RSA_OAEP_512 => rsa!(RSA_OAEP_512, encryption_key, recipient.public), - JweAlgorithm::A128KW => aes_kw!(Aes128Kw, encryption_key, recipient.public), - JweAlgorithm::A192KW => aes_kw!(Aes192Kw, encryption_key, recipient.public), - JweAlgorithm::A256KW => aes_kw!(Aes256Kw, encryption_key, recipient.public), - JweAlgorithm::DIR => Ok(None), - JweAlgorithm::ECDH_ES => Ok(None), - JweAlgorithm::ECDH_ES_A128KW => { - ecdh_kw!(@es, Aes128Kw, encryption_key, recipient, output, self) - } - JweAlgorithm::ECDH_ES_A192KW => { - ecdh_kw!(@es, Aes192Kw, encryption_key, recipient, output, self) - } - JweAlgorithm::ECDH_ES_A256KW => { - ecdh_kw!(@es, Aes256Kw, encryption_key, recipient, output, self) - } - JweAlgorithm::ECDH_ES_C20PKW => { - ecdh_chacha!(@es, ChaCha20Poly1305, encryption_key, recipient, output, self) - } - JweAlgorithm::ECDH_ES_XC20PKW => { - ecdh_chacha!(@es, XChaCha20Poly1305, encryption_key, recipient, output, self) - } - JweAlgorithm::A128GCMKW => { - aead!(Aes128Gcm, encryption_key, recipient.public, output) - } - JweAlgorithm::A192GCMKW => { - aead!(Aes192Gcm, encryption_key, recipient.public, output) - } - JweAlgorithm::A256GCMKW => { - aead!(Aes256Gcm, encryption_key, recipient.public, output) - } - JweAlgorithm::PBES2_HS256_A128KW => { - pbes2!( - (PBKDF2_HMAC_SHA256, SHA256_LEN), - Aes128Kw, - encryption_key, - recipient.public, - output, - self - ) - } - JweAlgorithm::PBES2_HS384_A192KW => { - pbes2!( - (PBKDF2_HMAC_SHA384, SHA384_LEN), - Aes192Kw, - encryption_key, - recipient.public, - output, - self - ) - } - JweAlgorithm::PBES2_HS512_A256KW => { - pbes2!( - (PBKDF2_HMAC_SHA512, SHA512_LEN), - Aes256Kw, - encryption_key, - recipient.public, - output, - self - ) - } - JweAlgorithm::ECDH_1PU => Ok(None), - JweAlgorithm::ECDH_1PU_A128KW => { - ecdh_kw!(@1pu, Aes128Kw, encryption_key, recipient, output, self) - } - JweAlgorithm::ECDH_1PU_A192KW => { - ecdh_kw!(@1pu, Aes192Kw, encryption_key, recipient, output, self) - } - JweAlgorithm::ECDH_1PU_A256KW => { - ecdh_kw!(@1pu, Aes256Kw, encryption_key, recipient, output, self) - } - JweAlgorithm::C20PKW => { - aead!(ChaCha20Poly1305, encryption_key, recipient.public, output) - } - JweAlgorithm::XC20PKW => { - aead!(XChaCha20Poly1305, encryption_key, recipient.public, output) - } - } - } - - fn validate(&self) -> Result<()> { - validate_jwe_headers( - self.protected, - self.unprotected, - self.recipients.iter().map(|recipient| recipient.header), - self.protected.and_then(|header| header.crit()), - )?; - - match (self.format, &*self.recipients, self.aad) { - (JweFormat::Compact, &[Recipient { header: None, .. }], None) => { - if self.protected.is_some() { - Ok(()) - } else { - Err(Error::EncError("JWE Compact Serialization requires a protected header")) - } - } - (JweFormat::Compact, _, _) => Err(Error::EncError( - "JWE Compact Serialization doesn't support multiple recipients, per-recipient headers, or AAD", - )), - (JweFormat::General, _, _) => Ok(()), - (JweFormat::Flatten, &[_], _) => Ok(()), - (JweFormat::Flatten, _, _) => Err(Error::EncError( - "JWE Flattened Serialization doesn't support multiple recipients", - )), - } - } -} - -// ============================================================================= -// ============================================================================= - -struct __Context<'a, 'b> { - encoder: &'b Encoder<'a>, - recipients: Vec<(Recipient<'a>, JweHeader)>, - encryption_key: Option>, -} - -impl<'a, 'b> __Context<'a, 'b> { - pub fn new(encoder: &'b Encoder<'a>, recipients: usize) -> Self { - Self { - encoder, - recipients: Vec::with_capacity(recipients), - encryption_key: None, - } - } - - pub fn expand_recipient( - &mut self, - protected: Option<&'a JweHeader>, - unprotected: Option<&'a JweHeader>, - recipient: Recipient<'a>, - ) -> Result<()> { - let merged: HeaderSet<'_> = HeaderSet::new() - .header(recipient.header) - .protected(protected) - .unprotected(unprotected); - - let algorithm: JweAlgorithm = merged.try_alg()?; - let encryption: JweEncryption = merged.try_enc()?; - - let mut output: JweHeader = if let Some(recipient) = recipient.header { - recipient.clone() - } else if self.encoder.format == JweFormat::Compact { - protected.cloned().unwrap() - } else { - JweHeader::new(algorithm, encryption) - }; - - let cek: Option> = self - .encoder - .generate_cek(algorithm, encryption, &mut output, recipient)?; - - if let Some(encryption_key) = cek { - if let Some(cek) = self.encryption_key.as_ref() { - if cek.as_ref() != encryption_key.as_ref() { - return Err(Error::EncError("CEK (mismatch)")); - } - } else { - self.encryption_key = Some(encryption_key); - } - } - - self.recipients.push((recipient, output)); - - Ok(()) - } -} - -fn encrypt_content( - encryption: JweEncryption, - key: &[u8], - iv: &[u8], - aad: &[u8], - plaintext: &[u8], -) -> Result<(Vec, Vec)> { - let mut ciphertext: Vec = vec![0; plaintext.len()]; - - let tag: Vec = match encryption { - JweEncryption::A128CBC_HS256 => { - __encrypt_content::(key, iv, aad, plaintext, &mut ciphertext)?.to_vec() - } - JweEncryption::A192CBC_HS384 => { - __encrypt_content::(key, iv, aad, plaintext, &mut ciphertext)?.to_vec() - } - JweEncryption::A256CBC_HS512 => { - __encrypt_content::(key, iv, aad, plaintext, &mut ciphertext)?.to_vec() - } - JweEncryption::A128GCM => __encrypt_content::(key, iv, aad, plaintext, &mut ciphertext)?.to_vec(), - JweEncryption::A192GCM => __encrypt_content::(key, iv, aad, plaintext, &mut ciphertext)?.to_vec(), - JweEncryption::A256GCM => __encrypt_content::(key, iv, aad, plaintext, &mut ciphertext)?.to_vec(), - JweEncryption::C20P => __encrypt_content::(key, iv, aad, plaintext, &mut ciphertext)?.to_vec(), - JweEncryption::XC20P => __encrypt_content::(key, iv, aad, plaintext, &mut ciphertext)?.to_vec(), - }; - - Ok((ciphertext, tag)) -} - -fn __encrypt_content(key: &[u8], iv: &[u8], aad: &[u8], plaintext: &[u8], ciphertext: &mut Vec) -> Result> -where - A: Aead, -{ - let padding: usize = A::padsize(plaintext).map(NonZeroUsize::get).unwrap_or_default(); - - ciphertext.resize(plaintext.len() + padding, 0); - - let mut tag: Tag = Default::default(); - - A::try_encrypt(key, iv, aad, plaintext, ciphertext, &mut tag)?; - - Ok(tag) -} - -struct EcdhDeriver<'a, 'b>(&'b Encoder<'a>, &'b Recipient<'a>); - -impl<'a, 'b> EcdhDeriver<'a, 'b> { - fn new(encoder: &'b Encoder<'a>, recipient: &'b Recipient<'a>) -> Self { - Self(encoder, recipient) - } - - fn derive_ecdh_es(&self, output: &mut JweHeader, algorithm: &str, key_len: usize) -> Result> { - self.derive_ecdh_key(output, algorithm, key_len, |eph_secret| { - diffie_hellman(self.1.ecdh_curve, self.1.public, eph_secret) - }) - } - - fn derive_ecdh_1pu(&self, output: &mut JweHeader, algorithm: &str, key_len: usize) -> Result> { - self.derive_ecdh_key(output, algorithm, key_len, |eph_secret| { - let secret: Secret<'_> = self.0.secret.ok_or(Error::EncError("Missing ECDH-1PU Secret Key"))?; - let ze: Vec = diffie_hellman(self.1.ecdh_curve, self.1.public, eph_secret)?; - let zs: Vec = diffie_hellman(self.1.ecdh_curve, self.1.public, secret)?; - - Ok([ze, zs].concat()) - }) - } - - fn derive_ecdh_key( - &self, - output: &mut JweHeader, - algorithm: &str, - key_len: usize, - key_exchange: impl Fn(&Jwk) -> Result>, - ) -> Result> { - let __apu: Vec; - let __apv: Vec; - - let apu: &[u8] = match output.apu() { - Some(value) => { - __apu = decode_b64(value)?; - &__apu - } - None => match self.0.apu { - Some(value) => { - output.set_apu(encode_b64(value)); - value - } - None => &[], - }, - }; - - let apv: &[u8] = match output.apv() { - Some(value) => { - __apv = decode_b64(value)?; - &__apv - } - None => match self.0.apv { - Some(value) => { - output.set_apv(encode_b64(value)); - value - } - None => &[], - }, - }; - - // Generate an ephemeral key pair - let eph_secret: Jwk = Jwk::random(self.1.ecdh_curve)?; - let eph_public: Jwk = eph_secret.to_public(); - - // Compute the shared secret - let z: Vec = key_exchange(&eph_secret)?; - - // Set the ephemeral public key claim - output.set_epk(eph_public); - - // Concat KDF - concat_kdf(algorithm, key_len, &z, apu, apv) - } -} diff --git a/libjose/src/jwe/encryption.rs b/libjose/src/jwe/encryption.rs deleted file mode 100644 index 359f6e3f33..0000000000 --- a/libjose/src/jwe/encryption.rs +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use core::fmt::Display; -use core::fmt::Formatter; -use core::fmt::Result; -use crypto::ciphers::aes::Aes128Gcm; -use crypto::ciphers::aes::Aes192Gcm; -use crypto::ciphers::aes::Aes256Gcm; -use crypto::ciphers::aes_cbc::Aes128CbcHmac256; -use crypto::ciphers::aes_cbc::Aes192CbcHmac384; -use crypto::ciphers::aes_cbc::Aes256CbcHmac512; -use crypto::ciphers::chacha::ChaCha20Poly1305; -use crypto::ciphers::chacha::XChaCha20Poly1305; -use crypto::ciphers::traits::Aead; - -/// Supported algorithms for the JSON Web Encryption `enc` claim. -/// -/// [More Info](https://www.iana.org/assignments/jose/jose.xhtml#web-signature-encryption-algorithms) -/// -/// [ChaCha20-Poly1305 (draft)](https://tools.ietf.org/html/draft-amringer-jose-chacha-02) -#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)] -#[allow(non_camel_case_types)] -pub enum JweEncryption { - /// AES_128_CBC_HMAC_SHA_256 authenticated encryption algorithm. - #[serde(rename = "A128CBC-HS256")] - A128CBC_HS256, - /// AES_192_CBC_HMAC_SHA_384 authenticated encryption algorithm. - #[serde(rename = "A192CBC-HS384")] - A192CBC_HS384, - /// AES_256_CBC_HMAC_SHA_512 authenticated encryption algorithm. - #[serde(rename = "A256CBC-HS512")] - A256CBC_HS512, - /// AES GCM using 128-bit key. - A128GCM, - /// AES GCM using 192-bit key. - A192GCM, - /// AES GCM using 256-bit key. - A256GCM, - /// ChaCha20-Poly1305. - C20P, - /// ChaCha20-Poly1305. - XC20P, -} - -impl JweEncryption { - pub const ALL: &'static [Self] = &[ - Self::A128CBC_HS256, - Self::A192CBC_HS384, - Self::A256CBC_HS512, - Self::A128GCM, - Self::A192GCM, - Self::A256GCM, - Self::C20P, - Self::XC20P, - ]; - - /// Returns the JWE "enc" claim as a `str` slice. - pub const fn name(self) -> &'static str { - match self { - Self::A128CBC_HS256 => "A128CBC-HS256", - Self::A192CBC_HS384 => "A192CBC-HS384", - Self::A256CBC_HS512 => "A256CBC-HS512", - Self::A128GCM => "A128GCM", - Self::A192GCM => "A192GCM", - Self::A256GCM => "A256GCM", - Self::C20P => "C20P", - Self::XC20P => "XC20P", - } - } - - pub const fn key_len(self) -> usize { - match self { - Self::A128CBC_HS256 => Aes128CbcHmac256::KEY_LENGTH, - Self::A192CBC_HS384 => Aes192CbcHmac384::KEY_LENGTH, - Self::A256CBC_HS512 => Aes256CbcHmac512::KEY_LENGTH, - Self::A128GCM => Aes128Gcm::KEY_LENGTH, - Self::A192GCM => Aes192Gcm::KEY_LENGTH, - Self::A256GCM => Aes256Gcm::KEY_LENGTH, - Self::C20P => ChaCha20Poly1305::KEY_LENGTH, - Self::XC20P => XChaCha20Poly1305::KEY_LENGTH, - } - } - - pub const fn iv_len(self) -> usize { - match self { - Self::A128CBC_HS256 => Aes128CbcHmac256::NONCE_LENGTH, - Self::A192CBC_HS384 => Aes192CbcHmac384::NONCE_LENGTH, - Self::A256CBC_HS512 => Aes256CbcHmac512::NONCE_LENGTH, - Self::A128GCM => Aes128Gcm::NONCE_LENGTH, - Self::A192GCM => Aes192Gcm::NONCE_LENGTH, - Self::A256GCM => Aes256Gcm::NONCE_LENGTH, - Self::C20P => ChaCha20Poly1305::NONCE_LENGTH, - Self::XC20P => XChaCha20Poly1305::NONCE_LENGTH, - } - } - - pub const fn tag_len(self) -> usize { - match self { - Self::A128CBC_HS256 => Aes128CbcHmac256::TAG_LENGTH, - Self::A192CBC_HS384 => Aes192CbcHmac384::TAG_LENGTH, - Self::A256CBC_HS512 => Aes256CbcHmac512::TAG_LENGTH, - Self::A128GCM => Aes128Gcm::TAG_LENGTH, - Self::A192GCM => Aes192Gcm::TAG_LENGTH, - Self::A256GCM => Aes256Gcm::TAG_LENGTH, - Self::C20P => ChaCha20Poly1305::TAG_LENGTH, - Self::XC20P => XChaCha20Poly1305::TAG_LENGTH, - } - } -} - -impl Display for JweEncryption { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - f.write_str(self.name()) - } -} diff --git a/libjose/src/jwe/format.rs b/libjose/src/jwe/format.rs deleted file mode 100644 index e1551de1f4..0000000000 --- a/libjose/src/jwe/format.rs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] -pub enum JweFormat { - Compact, - General, - Flatten, -} - -impl Default for JweFormat { - fn default() -> Self { - Self::Compact - } -} diff --git a/libjose/src/jwe/header.rs b/libjose/src/jwe/header.rs deleted file mode 100644 index e5d434c15e..0000000000 --- a/libjose/src/jwe/header.rs +++ /dev/null @@ -1,300 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use core::ops::Deref; -use core::ops::DerefMut; - -use crate::error::Error; -use crate::error::Result; -use crate::jose::JoseHeader; -use crate::jwe::JweAlgorithm; -use crate::jwe::JweCompression; -use crate::jwe::JweEncryption; -use crate::jwk::Jwk; -use crate::jwt::JwtHeader; -use crate::lib::*; - -/// JSON Web Encryption JOSE Header. -/// -/// [More Info](https://tools.ietf.org/html/rfc7516#section-4) -#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] -pub struct JweHeader { - /// Common JOSE Header Parameters. - #[serde(flatten)] - common: JwtHeader, - /// Algorithm. - /// - /// Identifies the cryptographic algorithm used to secure the JWS. - /// - /// [More Info](https://tools.ietf.org/html/rfc7516#section-4.1.1) - alg: JweAlgorithm, - /// Encryption Algorithm. - /// - /// Identifies the content encryption algorithm used to perform - /// authenticated encryption on the plaintext to produce the ciphertext and - /// the Authentication Tag. - /// - /// [More Info](https://tools.ietf.org/html/rfc7516#section-4.1.2) - enc: JweEncryption, - /// Compression Algorithm. - /// - /// The compression algorithm applied to the plaintext before encryption. - /// - /// [More Info](https://tools.ietf.org/html/rfc7516#section-4.1.3) - #[serde(skip_serializing_if = "Option::is_none")] - zip: Option, - /// Ephemeral Public Key. - /// - /// Public key created by the originator for the use in key agreement - /// algorithms. - /// - /// [More Info](https://tools.ietf.org/html/rfc7518#section-4.6.1.1) - #[serde(skip_serializing_if = "Option::is_none")] - epk: Option, - /// Agreement PartyUInfo. - /// - /// Value used for key derivation via Concat KDF. - /// - /// [More Info](https://tools.ietf.org/html/rfc7518#section-4.6.1.2) - #[serde(skip_serializing_if = "Option::is_none")] - apu: Option, - /// Agreement PartyVInfo. - /// - /// Value used for key derivation via Concat KDF. - /// - /// [More Info](https://tools.ietf.org/html/rfc7518#section-4.6.1.3) - #[serde(skip_serializing_if = "Option::is_none")] - apv: Option, - /// Initialization Vector. - /// - /// The base64url-encoded representation of the 96-bit IV value used for the - /// key encryption operation. - /// - /// [More Info](https://tools.ietf.org/html/rfc7518#section-4.7.1.1) - #[serde(skip_serializing_if = "Option::is_none")] - iv: Option, - /// Authentication Tag. - /// - /// The base64url-encoded representation of the 128-bit Authentication Tag - /// value resulting from the key encryption operation. - /// - /// [More Info](https://tools.ietf.org/html/rfc7518#section-4.7.1.2) - #[serde(skip_serializing_if = "Option::is_none")] - tag: Option, - /// PBES2 Salt Input. - /// - /// A base64url-encoded value, which is used as part of the PBKDF2 salt value. - /// - /// [More Info](https://tools.ietf.org/html/rfc7518#section-4.8.1.1) - #[serde(skip_serializing_if = "Option::is_none")] - p2s: Option, - /// PBES2 Count. - /// - /// The PBKDF2 iteration count - /// - /// [More Info](https://tools.ietf.org/html/rfc7518#section-4.8.1.2) - #[serde(skip_serializing_if = "Option::is_none")] - p2c: Option, -} - -impl JweHeader { - /// Creates a new `JweHeader` with the given `alg` and `enc` claims. - pub const fn new(alg: JweAlgorithm, enc: JweEncryption) -> Self { - Self { - common: JwtHeader::new(), - alg, - enc, - zip: None, - epk: None, - apu: None, - apv: None, - iv: None, - tag: None, - p2s: None, - p2c: None, - } - } - - /// Returns the value for the algorithm claim (alg). - pub fn alg(&self) -> JweAlgorithm { - self.alg - } - - /// Sets a value for the algorithm claim (alg). - pub fn set_alg(&mut self, value: impl Into) { - self.alg = value.into(); - } - - /// Returns the value of the encryption claim (enc). - pub fn enc(&self) -> JweEncryption { - self.enc - } - - /// Sets a value for the encryption claim (enc). - pub fn set_enc(&mut self, value: impl Into) { - self.enc = value.into(); - } - - /// Returns the value of the compression claim (zip). - pub fn zip(&self) -> Option { - self.zip - } - - /// Returns the value of the compression claim (zip). - pub fn try_zip(&self) -> Result { - self.zip().ok_or(Error::MissingParam("zip")) - } - - /// Sets a value for the compression claim (zip). - pub fn set_zip(&mut self, value: impl Into) { - self.zip = Some(value.into()); - } - - /// Returns the value of the ephemeral public key claim (epk). - pub fn epk(&self) -> Option<&Jwk> { - self.epk.as_ref() - } - - /// Returns the value of the ephemeral public key claim (epk). - pub fn try_epk(&self) -> Result<&Jwk> { - self.epk().ok_or(Error::MissingParam("epk")) - } - - /// Sets a value for the ephemeral public key claim (epk). - pub fn set_epk(&mut self, value: impl Into) { - self.epk = Some(value.into()); - } - - /// Returns the value of the partyuinfo claim (apu). - pub fn apu(&self) -> Option<&str> { - self.apu.as_deref() - } - - /// Returns the value of the partyuinfo claim (apu). - pub fn try_apu(&self) -> Result<&str> { - self.apu().ok_or(Error::MissingParam("apu")) - } - - /// Sets a value for the partyuinfo claim (apu). - pub fn set_apu(&mut self, value: impl Into) { - self.apu = Some(value.into()); - } - - /// Returns the value of the partyvinfo claim (apv). - pub fn apv(&self) -> Option<&str> { - self.apv.as_deref() - } - - /// Returns the value of the partyvinfo claim (apv). - pub fn try_apv(&self) -> Result<&str> { - self.apv().ok_or(Error::MissingParam("apv")) - } - - /// Sets a value for the partyvinfo claim (apv). - pub fn set_apv(&mut self, value: impl Into) { - self.apv = Some(value.into()); - } - - /// Returns the value of the initialization vector claim (iv). - pub fn iv(&self) -> Option<&str> { - self.iv.as_deref() - } - - /// Returns the value of the initialization vector claim (iv). - pub fn try_iv(&self) -> Result<&str> { - self.iv().ok_or(Error::MissingParam("iv")) - } - - /// Sets a value for the initialization vector claim (iv). - pub fn set_iv(&mut self, value: impl Into) { - self.iv = Some(value.into()); - } - - /// Returns the value of the authentication tag claim (tag). - pub fn tag(&self) -> Option<&str> { - self.tag.as_deref() - } - - /// Returns the value of the authentication tag claim (tag). - pub fn try_tag(&self) -> Result<&str> { - self.tag().ok_or(Error::MissingParam("tag")) - } - - /// Sets a value for the authentication tag claim (tag). - pub fn set_tag(&mut self, value: impl Into) { - self.tag = Some(value.into()); - } - - /// Returns the value of the authentication pbes2 salt input claim (p2s). - pub fn p2s(&self) -> Option<&str> { - self.p2s.as_deref() - } - - /// Returns the value of the authentication pbes2 salt input claim (p2s). - pub fn try_p2s(&self) -> Result<&str> { - self.p2s().ok_or(Error::MissingParam("p2s")) - } - - /// Sets a value for the authentication pbes2 salt input claim (p2s). - pub fn set_p2s(&mut self, value: impl Into) { - self.p2s = Some(value.into()); - } - - /// Returns the value of the authentication pbes2 count claim (p2c). - pub fn p2c(&self) -> Option { - self.p2c - } - - /// Returns the value of the authentication pbes2 count claim (p2c). - pub fn try_p2c(&self) -> Result { - self.p2c().ok_or(Error::MissingParam("p2c")) - } - - /// Sets a value for the authentication pbes2 count claim (p2c). - pub fn set_p2c(&mut self, value: impl Into) { - self.p2c = Some(value.into()); - } - - // =========================================================================== - // =========================================================================== - - pub fn has(&self, claim: &str) -> bool { - match claim { - "alg" => true, // we always have an algorithm - "enc" => true, // we always have an encryption algorithm - "zip" => self.zip().is_some(), - "epk" => self.epk().is_some(), - "apu" => self.apu().is_some(), - "apv" => self.apv().is_some(), - "iv" => self.iv().is_some(), - "tag" => self.tag().is_some(), - "p2s" => self.p2s().is_some(), - "p2c" => self.p2c().is_some(), - _ => self.common.has(claim), - } - } -} - -impl Deref for JweHeader { - type Target = JwtHeader; - - fn deref(&self) -> &Self::Target { - &self.common - } -} - -impl DerefMut for JweHeader { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.common - } -} - -impl JoseHeader for JweHeader { - fn common(&self) -> &JwtHeader { - self - } - - fn has_claim(&self, claim: &str) -> bool { - self.has(claim) - } -} diff --git a/libjose/src/jwe/mod.rs b/libjose/src/jwe/mod.rs deleted file mode 100644 index 804ef90d54..0000000000 --- a/libjose/src/jwe/mod.rs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! JSON Web Encryption ([JWE](https://tools.ietf.org/html/rfc7516)) - -mod algorithm; -mod compression; -mod decoder; -mod encoder; -mod encryption; -mod format; -mod header; -mod recipient; - -pub use self::algorithm::*; -pub use self::compression::*; -pub use self::decoder::*; -pub use self::encoder::*; -pub use self::encryption::*; -pub use self::format::*; -pub use self::header::*; -pub use self::recipient::*; diff --git a/libjose/src/jwe/recipient.rs b/libjose/src/jwe/recipient.rs deleted file mode 100644 index 12239c4f31..0000000000 --- a/libjose/src/jwe/recipient.rs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use crate::jwe::JweHeader; -use crate::jwk::EcdhCurve; -use crate::jwk::EcxCurve; -use crate::utils::Secret; - -#[derive(Clone, Copy)] -pub struct Recipient<'a> { - /// The curve used for Ecdh key agreements. - pub ecdh_curve: EcdhCurve, - /// The public key used for key agreements and content encryption. - pub public: Secret<'a>, - /// The non integrity-protected JOSE header. - pub header: Option<&'a JweHeader>, -} - -impl<'a> Recipient<'a> { - pub fn new(public: impl Into>) -> Self { - Self { - ecdh_curve: EcdhCurve::Ecx(EcxCurve::X25519), - public: public.into(), - header: None, - } - } - - pub fn ecdh_curve(mut self, value: impl Into) -> Self { - self.ecdh_curve = value.into(); - self - } - - pub fn header(mut self, value: &'a JweHeader) -> Self { - self.header = Some(value); - self - } -} - -impl<'a, T> From for Recipient<'a> -where - T: Into>, -{ - fn from(other: T) -> Self { - Self::new(other) - } -} - -impl<'a, T> From<(T, &'a JweHeader)> for Recipient<'a> -where - T: Into>, -{ - fn from(other: (T, &'a JweHeader)) -> Self { - Self::new(other.0).header(other.1) - } -} diff --git a/libjose/src/jwk/curve/ec.rs b/libjose/src/jwk/curve/ec.rs deleted file mode 100644 index 5524faa495..0000000000 --- a/libjose/src/jwk/curve/ec.rs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use core::fmt::Display; -use core::fmt::Formatter; -use core::fmt::Result; - -/// Supported Elliptic Curves. -/// -/// [More Info](https://www.iana.org/assignments/jose/jose.xhtml#web-key-elliptic-curve) -#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] -pub enum EcCurve { - /// P-256 Curve. - P256, - /// P-384 Curve. - P384, - /// P-521 Curve. - P521, - /// SECG secp256k1 curve. - Secp256K1, -} - -impl EcCurve { - pub const fn name(self) -> &'static str { - match self { - Self::P256 => "P-256", - Self::P384 => "P-384", - Self::P521 => "P-521", - Self::Secp256K1 => "secp256k1", - } - } -} - -impl Display for EcCurve { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - f.write_str(self.name()) - } -} diff --git a/libjose/src/jwk/curve/ecdh.rs b/libjose/src/jwk/curve/ecdh.rs deleted file mode 100644 index e31ab97bcb..0000000000 --- a/libjose/src/jwk/curve/ecdh.rs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use core::fmt::Display; -use core::fmt::Formatter; -use core::fmt::Result; - -use crate::jwk::EcCurve; -use crate::jwk::EcxCurve; - -#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] -pub enum EcdhCurve { - Ec(EcCurve), - Ecx(EcxCurve), -} - -impl EcdhCurve { - pub const fn name(self) -> &'static str { - match self { - Self::Ec(inner) => inner.name(), - Self::Ecx(inner) => inner.name(), - } - } -} - -impl Display for EcdhCurve { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - f.write_str(self.name()) - } -} - -impl From for EcdhCurve { - fn from(other: EcCurve) -> Self { - Self::Ec(other) - } -} - -impl From for EcdhCurve { - fn from(other: EcxCurve) -> Self { - Self::Ecx(other) - } -} diff --git a/libjose/src/jwk/curve/ecx.rs b/libjose/src/jwk/curve/ecx.rs deleted file mode 100644 index f85aafbc70..0000000000 --- a/libjose/src/jwk/curve/ecx.rs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use core::fmt::Display; -use core::fmt::Formatter; -use core::fmt::Result; - -/// Supported Elliptic Curves. -/// -/// [More Info](https://www.iana.org/assignments/jose/jose.xhtml#web-key-elliptic-curve) -#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] -pub enum EcxCurve { - /// X25519 function key pairs. - X25519, - /// X448 function key pairs. - X448, -} - -impl EcxCurve { - pub const fn name(self) -> &'static str { - match self { - Self::X25519 => "X25519", - Self::X448 => "X448", - } - } -} - -impl Display for EcxCurve { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - f.write_str(self.name()) - } -} diff --git a/libjose/src/jwk/curve/ed.rs b/libjose/src/jwk/curve/ed.rs deleted file mode 100644 index dc109b8682..0000000000 --- a/libjose/src/jwk/curve/ed.rs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use core::fmt::Display; -use core::fmt::Formatter; -use core::fmt::Result; - -/// Supported Elliptic Curves. -/// -/// [More Info](https://www.iana.org/assignments/jose/jose.xhtml#web-key-elliptic-curve) -#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] -pub enum EdCurve { - /// Ed25519 signature algorithm key pairs. - Ed25519, - /// Ed448 signature algorithm key pairs. - Ed448, -} - -impl EdCurve { - pub const fn name(self) -> &'static str { - match self { - Self::Ed25519 => "Ed25519", - Self::Ed448 => "Ed448", - } - } -} - -impl Display for EdCurve { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - f.write_str(self.name()) - } -} diff --git a/libjose/src/jwk/curve/mod.rs b/libjose/src/jwk/curve/mod.rs deleted file mode 100644 index 642fbc281d..0000000000 --- a/libjose/src/jwk/curve/mod.rs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -mod ec; -mod ecdh; -mod ecx; -mod ed; - -pub use self::ec::*; -pub use self::ecdh::*; -pub use self::ecx::*; -pub use self::ed::*; diff --git a/libjose/src/jwk/key.rs b/libjose/src/jwk/key.rs deleted file mode 100644 index 938bd6d00a..0000000000 --- a/libjose/src/jwk/key.rs +++ /dev/null @@ -1,574 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use crate::utils::RsaComputed; -use crypto::hashes::sha::SHA256; -use crypto::hashes::sha::SHA256_LEN; -use rand::rngs::OsRng; -use rsa::PublicKeyParts as _; -use url::Url; -use zeroize::Zeroize; - -use crate::error::Error; -use crate::error::Result; -use crate::jwk::EcCurve; -use crate::jwk::EcxCurve; -use crate::jwk::EdCurve; -use crate::jwk::JwkOperation; -use crate::jwk::JwkParams; -use crate::jwk::JwkParamsEc; -use crate::jwk::JwkParamsOct; -use crate::jwk::JwkParamsOkp; -use crate::jwk::JwkParamsRsa; -use crate::jwk::JwkType; -use crate::jwk::JwkUse; -use crate::lib::*; -use crate::utils::encode_b64; -use crate::utils::random_bytes; -use crate::utils::Ed25519SecretKey; -use crate::utils::K256SecretKey; -use crate::utils::KeyParams; -use crate::utils::P256SecretKey; -use crate::utils::RsaSecretKey; -use crate::utils::X25519SecretKey; -use crate::utils::X448SecretKey; - -/// A SHA256 JSON Web Key Thumbprint. -pub type JwkThumbprint = [u8; SHA256_LEN]; - -/// JSON Web Key. -/// -/// [More Info](https://tools.ietf.org/html/rfc7517#section-4) -#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] -pub struct Jwk { - /// Key Type. - /// - /// Identifies the cryptographic algorithm family used with the key. - /// - /// [More Info](https://tools.ietf.org/html/rfc7517#section-4.1) - kty: JwkType, - /// Public Key Use. - /// - /// Identifies the intended use of the public key. - /// - /// [More Info](https://tools.ietf.org/html/rfc7517#section-4.2) - #[serde(rename = "use", skip_serializing_if = "Option::is_none")] - use_: Option, - /// Key Operations. - /// - /// Identifies the operation(s) for which the key is intended to be used. - /// - /// [More Info](https://tools.ietf.org/html/rfc7517#section-4.3) - #[serde(skip_serializing_if = "Option::is_none")] - key_ops: Option>, - /// Algorithm. - /// - /// Identifies the algorithm intended for use with the key. - /// - /// [More Info](https://tools.ietf.org/html/rfc7517#section-4.4) - #[serde(skip_serializing_if = "Option::is_none")] - alg: Option, - /// Key ID. - /// - /// Used to match a specific key among a set of keys within a JWK Set. - /// - /// [More Info](https://tools.ietf.org/html/rfc7517#section-4.5) - #[serde(skip_serializing_if = "Option::is_none")] - kid: Option, - /// X.509 URL. - /// - /// A URI that refers to a resource for an X.509 public key certificate or - /// certificate chain. - /// - /// [More Info](https://tools.ietf.org/html/rfc7517#section-4.6) - #[serde(skip_serializing_if = "Option::is_none")] - x5u: Option, - /// X.509 Certificate Chain. - /// - /// Contains a chain of one or more PKIX certificates. - /// - /// [More Info](https://tools.ietf.org/html/rfc7517#section-4.7) - #[serde(skip_serializing_if = "Option::is_none")] - x5c: Option>, - /// X.509 Certificate SHA-1 Thumbprint. - /// - /// A base64url-encoded SHA-1 thumbprint of the DER encoding of an X.509 - /// certificate. - /// - /// [More Info](https://tools.ietf.org/html/rfc7517#section-4.8) - #[serde(skip_serializing_if = "Option::is_none")] - x5t: Option, - /// X.509 Certificate SHA-256 Thumbprint. - /// - /// A base64url-encoded SHA-256 thumbprint of the DER encoding of an X.509 - /// certificate. - /// - /// [More Info](https://tools.ietf.org/html/rfc7517#section-4.9) - #[serde(rename = "x5t#S256", skip_serializing_if = "Option::is_none")] - x5t_s256: Option, - /// Type-Specific Key Properties. - /// - /// [More Info](https://tools.ietf.org/html/rfc7517#section-4) - #[serde(flatten)] - params: JwkParams, -} - -impl Jwk { - /// Creates a new `Jwk` with the given `kty` parameter. - pub const fn new(kty: JwkType) -> Self { - Self { - kty, - use_: None, - key_ops: None, - alg: None, - kid: None, - x5u: None, - x5c: None, - x5t: None, - x5t_s256: None, - params: JwkParams::new(kty), - } - } - - /// Creates a new `Jwk` from the given params. - pub fn from_params(params: impl Into) -> Self { - let params: JwkParams = params.into(); - - Self { - kty: params.kty(), - use_: None, - key_ops: None, - alg: None, - kid: None, - x5u: None, - x5c: None, - x5t: None, - x5t_s256: None, - params, - } - } - - /// Returns the value for the key type parameter (kty). - pub fn kty(&self) -> JwkType { - self.kty - } - - /// Sets a value for the key type parameter (kty). - pub fn set_kty(&mut self, value: impl Into) { - self.kty = value.into(); - self.params = JwkParams::new(self.kty); - } - - /// Returns the value for the use property (use). - pub fn use_(&self) -> Option { - self.use_ - } - - /// Sets a value for the key use parameter (use). - pub fn set_use(&mut self, value: impl Into) { - self.use_ = Some(value.into()); - } - - /// Returns the value for the key operations parameter (key_ops). - pub fn key_ops(&self) -> Option<&[JwkOperation]> { - self.key_ops.as_deref() - } - - /// Sets values for the key operations parameter (key_ops). - pub fn set_key_ops(&mut self, value: impl IntoIterator>) { - self.key_ops = Some(value.into_iter().map(Into::into).collect()); - } - - /// Returns the value for the algorithm property (alg). - pub fn alg(&self) -> Option<&str> { - self.alg.as_deref() - } - - /// Sets a value for the algorithm property (alg). - pub fn set_alg(&mut self, value: impl Into) { - self.alg = Some(value.into()); - } - - /// Returns the value of the key ID property (kid). - pub fn kid(&self) -> Option<&str> { - self.kid.as_deref() - } - - /// Sets a value for the key ID property (kid). - pub fn set_kid(&mut self, value: impl Into) { - self.kid = Some(value.into()); - } - - /// Returns the value of the X.509 URL property (x5u). - pub fn x5u(&self) -> Option<&Url> { - self.x5u.as_ref() - } - - /// Sets a value for the X.509 URL property (x5u). - pub fn set_x5u(&mut self, value: impl Into) { - self.x5u = Some(value.into()); - } - - /// Returns the value of the X.509 certificate chain property (x5c). - pub fn x5c(&self) -> Option<&[String]> { - self.x5c.as_deref() - } - - /// Sets values for the X.509 certificate chain property (x5c). - pub fn set_x5c(&mut self, value: impl IntoIterator>) { - self.x5c = Some(value.into_iter().map(Into::into).collect()); - } - - /// Returns the value of the X.509 certificate SHA-1 thumbprint property - /// (x5t). - pub fn x5t(&self) -> Option<&str> { - self.x5t.as_deref() - } - - /// Sets a value for the X.509 certificate SHA-1 thumbprint property (x5t). - pub fn set_x5t(&mut self, value: impl Into) { - self.x5t = Some(value.into()); - } - - /// Returns the value of the X.509 certificate SHA-256 thumbprint property - /// (x5t#S256). - pub fn x5t_s256(&self) -> Option<&str> { - self.x5t_s256.as_deref() - } - - /// Sets a value for the X.509 certificate SHA-256 thumbprint property - /// (x5t#S256). - pub fn set_x5t_s256(&mut self, value: impl Into) { - self.x5t_s256 = Some(value.into()); - } - - /// Returns a reference to the custom JWK properties. - pub fn params(&self) -> &JwkParams { - &self.params - } - - /// Returns a mutable reference to the custom JWK properties. - pub fn params_mut(&mut self) -> &mut JwkParams { - &mut self.params - } - - /// Sets the value of the custom JWK properties. - pub fn set_params(&mut self, value: impl Into) { - match (self.kty, value.into()) { - (JwkType::Ec, value @ JwkParams::Ec(_)) => { - self.set_params_unchecked(value); - } - (JwkType::Rsa, value @ JwkParams::Rsa(_)) => { - self.set_params_unchecked(value); - } - (JwkType::Oct, value @ JwkParams::Oct(_)) => { - self.set_params_unchecked(value); - } - (JwkType::Okp, value @ JwkParams::Okp(_)) => { - self.set_params_unchecked(value); - } - (_, _) => { - // TODO: Return an error - } - } - } - - /// Sets the value of the custom JWK properties. Does not assert valid params. - pub fn set_params_unchecked(&mut self, value: impl Into) { - self.params = value.into(); - } - - pub fn try_ec_params(&self) -> Result<&JwkParamsEc> { - match self.params() { - JwkParams::Ec(params) => Ok(params), - _ => Err(Error::KeyError("Ec")), - } - } - - pub fn try_ec_params_mut(&mut self) -> Result<&mut JwkParamsEc> { - match self.params_mut() { - JwkParams::Ec(params) => Ok(params), - _ => Err(Error::KeyError("Ec")), - } - } - - pub fn try_rsa_params(&self) -> Result<&JwkParamsRsa> { - match self.params() { - JwkParams::Rsa(params) => Ok(params), - _ => Err(Error::KeyError("Rsa")), - } - } - - pub fn try_rsa_params_mut(&mut self) -> Result<&mut JwkParamsRsa> { - match self.params_mut() { - JwkParams::Rsa(params) => Ok(params), - _ => Err(Error::KeyError("Rsa")), - } - } - - pub fn try_oct_params(&self) -> Result<&JwkParamsOct> { - match self.params() { - JwkParams::Oct(params) => Ok(params), - _ => Err(Error::KeyError("Oct")), - } - } - - pub fn try_oct_params_mut(&mut self) -> Result<&mut JwkParamsOct> { - match self.params_mut() { - JwkParams::Oct(params) => Ok(params), - _ => Err(Error::KeyError("Oct")), - } - } - - pub fn try_okp_params(&self) -> Result<&JwkParamsOkp> { - match self.params() { - JwkParams::Okp(params) => Ok(params), - _ => Err(Error::KeyError("Okp")), - } - } - - pub fn try_okp_params_mut(&mut self) -> Result<&mut JwkParamsOkp> { - match self.params_mut() { - JwkParams::Okp(params) => Ok(params), - _ => Err(Error::KeyError("Okp")), - } - } - - // =========================================================================== - // Thumbprint - // =========================================================================== - - /// Creates a Thumbprint of the JSON Web Key according to [RFC7638](https://tools.ietf.org/html/rfc7638). - /// - /// `SHA2-256` is used as the hash function *H*. - /// - /// The thumbprint is returned as a base64url-encoded string. - pub fn thumbprint_b64(&self) -> Result { - self.thumbprint_raw().map(encode_b64) - } - - /// Creates a Thumbprint of the JSON Web Key according to [RFC7638](https://tools.ietf.org/html/rfc7638). - /// - /// `SHA2-256` is used as the hash function *H*. - /// - /// The thumbprint is returned as an unencoded vector of bytes. - pub fn thumbprint_raw(&self) -> Result { - let kty: &str = self.kty.name(); - - let json: String = match self.params() { - JwkParams::Ec(JwkParamsEc { crv, x, y, .. }) => { - format!(r#"{{"crv":"{crv}","kty":"{kty}","x":"{x}","y":"{y}"}}"#) - } - JwkParams::Rsa(JwkParamsRsa { e, n, .. }) => { - format!(r#"{{"e":"{e}","kty":"{kty}","n":"{n}"}}"#) - } - JwkParams::Oct(JwkParamsOct { k }) => { - format!(r#"{{"k":"{k}","kty":"{kty}"}}"#) - } - JwkParams::Okp(JwkParamsOkp { crv, x, .. }) => { - format!(r#"{{"crv":"{crv}","kty":"{kty}","x":"{x}"}}"#) - } - }; - - let mut out: JwkThumbprint = Default::default(); - - SHA256(json.as_bytes(), &mut out); - - Ok(out) - } - - // =========================================================================== - // Validations - // =========================================================================== - - pub fn check_use(&self, expected: JwkUse) -> Result<()> { - match self.use_() { - Some(value) if value == expected => Ok(()), - Some(_) => Err(Error::InvalidClaim("use")), - None => Ok(()), - } - } - - pub fn check_ops(&self, expected: JwkOperation) -> Result<()> { - match self.key_ops() { - Some(ops) if ops.contains(&expected) => Ok(()), - Some(_) => Err(Error::InvalidClaim("key_ops")), - None => Ok(()), - } - } - - pub fn check_alg(&self, expected: &str) -> Result<()> { - match self.alg() { - Some(value) if value == expected => Ok(()), - Some(_) => Err(Error::InvalidClaim("alg")), - None => Ok(()), - } - } - - pub fn check_signing_key(&self, algorithm: &str) -> Result<()> { - self.check_use(JwkUse::Signature)?; - self.check_ops(JwkOperation::Sign)?; - self.check_alg(algorithm)?; - - Ok(()) - } - - pub fn check_verifying_key(&self, algorithm: &str) -> Result<()> { - self.check_use(JwkUse::Signature)?; - self.check_ops(JwkOperation::Verify)?; - self.check_alg(algorithm)?; - - Ok(()) - } - - pub fn check_encryption_key(&self, algorithm: &str) -> Result<()> { - self.check_use(JwkUse::Encryption)?; - self.check_ops(JwkOperation::Encrypt)?; - self.check_alg(algorithm)?; - - Ok(()) - } - - pub fn check_decryption_key(&self, algorithm: &str) -> Result<()> { - self.check_use(JwkUse::Encryption)?; - self.check_ops(JwkOperation::Decrypt)?; - self.check_alg(algorithm)?; - - Ok(()) - } - - pub fn try_ec_curve(&self) -> Result { - match self.params() { - JwkParams::Ec(inner) => inner.try_ec_curve(), - _ => Err(Error::KeyError("Ec Curve")), - } - } - - pub fn try_ed_curve(&self) -> Result { - match self.params() { - JwkParams::Okp(inner) => inner.try_ed_curve(), - _ => Err(Error::KeyError("Ed Curve")), - } - } - - pub fn try_ecx_curve(&self) -> Result { - match self.params() { - JwkParams::Okp(inner) => inner.try_ecx_curve(), - _ => Err(Error::KeyError("Ecx Curve")), - } - } - - pub fn to_public(&self) -> Jwk { - let mut public: Jwk = Jwk::from_params(self.params().to_public()); - - if let Some(value) = self.use_() { - public.set_use(value); - } - - if let Some(value) = self.key_ops() { - public.set_key_ops(value.iter().map(|op| op.invert())); - } - - if let Some(value) = self.alg() { - public.set_alg(value); - } - - if let Some(value) = self.kid() { - public.set_kid(value); - } - - public - } - - pub fn random(params: impl Into) -> Result { - macro_rules! generate_ec { - ($secret:ident, $curve:expr) => {{ - let secret: $secret = $secret::generate()?; - let (x, y): _ = secret.public_key().to_coord()?; - - Ok(Jwk::from_params(JwkParamsEc { - crv: $curve.to_string(), - x: encode_b64(x), - y: encode_b64(y), - d: Some(encode_b64(secret.to_bytes())), - })) - }}; - } - - macro_rules! generate_ecx { - ($secret:ident, $curve:expr) => {{ - let secret: $secret = $secret::generate()?; - - Ok(Jwk::from_params(JwkParamsOkp { - crv: $curve.name().to_string(), - x: encode_b64(secret.public_key().to_bytes()), - d: Some(encode_b64(secret.to_bytes())), - })) - }}; - } - - match params.into() { - KeyParams::None => Ok(Jwk::new(JwkType::Oct)), - KeyParams::Oct(key_len) => Ok(Jwk::from_params(JwkParamsOct { - k: encode_b64(random_bytes(key_len)?), - })), - KeyParams::Rsa(bits) => { - let secret: RsaSecretKey = RsaSecretKey::new(&mut OsRng, bits.bits())?; - - let (p, q) = match secret.primes() { - [_] => unreachable!(), - [p, q] => (p, q), - [..] => return Err(Error::KeyError("multi prime keys are not supported")), - }; - - let values: RsaComputed = RsaComputed::new(secret.d(), p, q)?; - - Ok(Jwk::from_params(JwkParamsRsa { - n: encode_b64(secret.n().to_bytes_be()), - e: encode_b64(secret.e().to_bytes_be()), - d: Some(encode_b64(secret.d().to_bytes_be())), - p: Some(encode_b64(p.to_bytes_be())), - q: Some(encode_b64(q.to_bytes_be())), - dp: Some(encode_b64(values.dp.to_bytes_be())), - dq: Some(encode_b64(values.dq.to_bytes_be())), - qi: Some(encode_b64(values.qi.to_bytes_be())), - oth: None, - })) - } - KeyParams::Ec(curve) => match curve { - EcCurve::P256 => generate_ec!(P256SecretKey, curve), - EcCurve::P384 => Err(Error::AlgError("Ec/P384")), - EcCurve::P521 => Err(Error::AlgError("Ec/P521")), - EcCurve::Secp256K1 => generate_ec!(K256SecretKey, curve), - }, - KeyParams::Ed(curve) => match curve { - EdCurve::Ed25519 => { - let secret: Ed25519SecretKey = Ed25519SecretKey::generate()?; - - Ok(Jwk::from_params(JwkParamsOkp { - crv: curve.name().to_string(), - x: encode_b64(secret.public_key().to_compressed_bytes()), - d: Some(encode_b64(secret.to_le_bytes())), - })) - } - EdCurve::Ed448 => Err(Error::AlgError("Ed/P521")), - }, - KeyParams::Ecx(curve) => match curve { - EcxCurve::X25519 => generate_ecx!(X25519SecretKey, curve), - EcxCurve::X448 => generate_ecx!(X448SecretKey, curve), - }, - } - } -} - -impl Zeroize for Jwk { - fn zeroize(&mut self) { - self.params.zeroize(); - } -} - -impl Drop for Jwk { - fn drop(&mut self) { - self.zeroize(); - } -} diff --git a/libjose/src/jwk/key_management.rs b/libjose/src/jwk/key_management.rs deleted file mode 100644 index 287ef6972d..0000000000 --- a/libjose/src/jwk/key_management.rs +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use crate::jwe::JweAlgorithm; - -/// Type of cryptographic algorithm used to encrypt or determine the Content -/// Encryption Key (CEK). -/// -/// [More Info](https://tools.ietf.org/html/rfc7518#section-4) -#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] -pub enum KeyManagement { - /// A Key Management Mode in which the CEK value is encrypted to - /// the intended recipient using an asymmetric encryption algorithm. - EncryptionAsymmetric, - /// A Key Management Mode in which the CEK value is encrypted to the - /// intended recipient using a symmetric key wrapping algorithm. - KeyWrapSymmetric, - /// A Key Management Mode in which a key agreement algorithm is used to - /// agree upon the CEK value. - KeyAgreementDirect, - /// A Key Management Mode in which a key agreement algorithm is used to - /// agree upon a symmetric key used to encrypt the CEK value to the - /// intended recipient using a symmetric key wrapping algorithm. - KeyAgreementKeyWrap, - /// A Key Management Mode in which the CEK value used is the secret - /// symmetric key value shared between the parties. - EncryptionDirect, -} - -impl KeyManagement { - /// Returns true if this management mode uses a direct key value. - pub const fn is_direct(self) -> bool { - matches!(self, Self::KeyAgreementDirect | Self::EncryptionDirect) - } -} - -impl From for KeyManagement { - fn from(other: JweAlgorithm) -> Self { - match other { - JweAlgorithm::RSA1_5 => Self::EncryptionAsymmetric, - JweAlgorithm::RSA_OAEP => Self::EncryptionAsymmetric, - JweAlgorithm::RSA_OAEP_256 => Self::EncryptionAsymmetric, - JweAlgorithm::RSA_OAEP_384 => Self::EncryptionAsymmetric, - JweAlgorithm::RSA_OAEP_512 => Self::EncryptionAsymmetric, - JweAlgorithm::A128KW => Self::KeyWrapSymmetric, - JweAlgorithm::A192KW => Self::KeyWrapSymmetric, - JweAlgorithm::A256KW => Self::KeyWrapSymmetric, - JweAlgorithm::DIR => Self::EncryptionDirect, - JweAlgorithm::ECDH_ES => Self::KeyAgreementDirect, - JweAlgorithm::ECDH_ES_A128KW => Self::KeyAgreementKeyWrap, - JweAlgorithm::ECDH_ES_A192KW => Self::KeyAgreementKeyWrap, - JweAlgorithm::ECDH_ES_A256KW => Self::KeyAgreementKeyWrap, - JweAlgorithm::ECDH_ES_C20PKW => Self::KeyAgreementKeyWrap, - JweAlgorithm::ECDH_ES_XC20PKW => Self::KeyAgreementKeyWrap, - JweAlgorithm::A128GCMKW => Self::KeyWrapSymmetric, - JweAlgorithm::A192GCMKW => Self::KeyWrapSymmetric, - JweAlgorithm::A256GCMKW => Self::KeyWrapSymmetric, - JweAlgorithm::PBES2_HS256_A128KW => Self::KeyWrapSymmetric, - JweAlgorithm::PBES2_HS384_A192KW => Self::KeyWrapSymmetric, - JweAlgorithm::PBES2_HS512_A256KW => Self::KeyWrapSymmetric, - JweAlgorithm::ECDH_1PU => Self::KeyAgreementDirect, - JweAlgorithm::ECDH_1PU_A128KW => Self::KeyAgreementKeyWrap, - JweAlgorithm::ECDH_1PU_A192KW => Self::KeyAgreementKeyWrap, - JweAlgorithm::ECDH_1PU_A256KW => Self::KeyAgreementKeyWrap, - JweAlgorithm::C20PKW => Self::KeyWrapSymmetric, - JweAlgorithm::XC20PKW => Self::KeyWrapSymmetric, - } - } -} diff --git a/libjose/src/jwk/key_operation.rs b/libjose/src/jwk/key_operation.rs deleted file mode 100644 index a9623eb584..0000000000 --- a/libjose/src/jwk/key_operation.rs +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use core::fmt::Display; -use core::fmt::Formatter; -use core::fmt::Result; - -/// Supported algorithms for the JSON Web Key `key_ops` property. -/// -/// [More Info](https://www.iana.org/assignments/jose/jose.xhtml#web-key-operations) -#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub enum JwkOperation { - /// Compute digital signature or MAC. - Sign, - /// Verify digital signature or MAC. - Verify, - /// Encrypt content. - Encrypt, - /// Decrypt content and validate decryption, if applicable. - Decrypt, - /// Encrypt key. - WrapKey, - /// Decrypt key and validate decryption, if applicable. - UnwrapKey, - /// Derive key. - DeriveKey, - /// Derive bits not to be used as a key. - DeriveBits, -} - -impl JwkOperation { - /// Returns the JWK "key_ops" as a `str` slice. - pub const fn name(&self) -> &'static str { - match self { - Self::Sign => "sign", - Self::Verify => "verify", - Self::Encrypt => "encrypt", - Self::Decrypt => "decrypt", - Self::WrapKey => "wrapKey", - Self::UnwrapKey => "unwrapKey", - Self::DeriveKey => "deriveKey", - Self::DeriveBits => "deriveBits", - } - } - - pub const fn invert(&self) -> Self { - match self { - Self::Sign => Self::Verify, - Self::Verify => Self::Sign, - Self::Encrypt => Self::Decrypt, - Self::Decrypt => Self::Encrypt, - Self::WrapKey => Self::UnwrapKey, - Self::UnwrapKey => Self::WrapKey, - Self::DeriveKey => Self::DeriveKey, - Self::DeriveBits => Self::DeriveBits, - } - } -} - -impl Display for JwkOperation { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - f.write_str(self.name()) - } -} diff --git a/libjose/src/jwk/key_params.rs b/libjose/src/jwk/key_params.rs deleted file mode 100644 index 9858ea14ac..0000000000 --- a/libjose/src/jwk/key_params.rs +++ /dev/null @@ -1,353 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use zeroize::Zeroize; - -use crate::error::Error; -use crate::error::Result; -use crate::jwk::EcCurve; -use crate::jwk::EcxCurve; -use crate::jwk::EdCurve; -use crate::jwk::JwkType; -use crate::lib::*; - -/// Algorithm-specific parameters for JSON Web Keys. -/// -/// [More Info](https://tools.ietf.org/html/rfc7518#section-6) -#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)] -#[serde(untagged)] -#[derive(Zeroize)] -#[zeroize(drop)] -pub enum JwkParams { - Ec(JwkParamsEc), - Rsa(JwkParamsRsa), - Oct(JwkParamsOct), - Okp(JwkParamsOkp), -} - -impl JwkParams { - pub const fn new(kty: JwkType) -> Self { - match kty { - JwkType::Ec => Self::Ec(JwkParamsEc::new()), - JwkType::Rsa => Self::Rsa(JwkParamsRsa::new()), - JwkType::Oct => Self::Oct(JwkParamsOct::new()), - JwkType::Okp => Self::Okp(JwkParamsOkp::new()), - } - } - - pub const fn kty(&self) -> JwkType { - match self { - Self::Ec(inner) => inner.kty(), - Self::Rsa(inner) => inner.kty(), - Self::Oct(inner) => inner.kty(), - Self::Okp(inner) => inner.kty(), - } - } - - pub fn to_public(&self) -> Self { - match self { - Self::Ec(inner) => Self::Ec(inner.to_public()), - Self::Rsa(inner) => Self::Rsa(inner.to_public()), - Self::Oct(inner) => Self::Oct(inner.to_public()), - Self::Okp(inner) => Self::Okp(inner.to_public()), - } - } -} - -// ============================================================================= -// Jwk Params Ec -// ============================================================================= - -/// Parameters for Elliptic Curve Keys. -/// -/// [More Info](https://tools.ietf.org/html/rfc7518#section-6.2) -#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize, Zeroize)] -#[zeroize(drop)] -pub struct JwkParamsEc { - /// Identifies the cryptographic curve used with the key. - /// - /// [More Info](https://tools.ietf.org/html/rfc7518#section-6.2.1.1) - pub crv: String, // Curve - /// The `x` coordinate for the Elliptic Curve point as a base64url-encoded - /// value. - /// - /// [More Info](https://tools.ietf.org/html/rfc7518#section-6.2.1.2) - pub x: String, // X Coordinate - /// The `y` coordinate for the Elliptic Curve point as a base64url-encoded - /// value. - /// - /// [More Info](https://tools.ietf.org/html/rfc7518#section-6.2.1.3) - pub y: String, // Y Coordinate - /// The Elliptic Curve private key as a base64url-encoded value. - /// - /// [More Info](https://tools.ietf.org/html/rfc7518#section-6.2.2.1) - #[serde(skip_serializing_if = "Option::is_none")] - pub d: Option, // ECC Private Key -} - -impl JwkParamsEc { - pub const fn new() -> Self { - Self { - crv: String::new(), - x: String::new(), - y: String::new(), - d: None, - } - } - - pub const fn kty(&self) -> JwkType { - JwkType::Ec - } - - pub fn to_public(&self) -> Self { - Self { - crv: self.crv.clone(), - x: self.x.clone(), - y: self.y.clone(), - d: None, - } - } - - pub fn try_ec_curve(&self) -> Result { - match &*self.crv { - "P-256" => Ok(EcCurve::P256), - "P-384" => Ok(EcCurve::P384), - "P-521" => Ok(EcCurve::P521), - "secp256k1" => Ok(EcCurve::Secp256K1), - _ => Err(Error::KeyError("Ec Curve")), - } - } -} - -impl From for JwkParams { - fn from(other: JwkParamsEc) -> Self { - Self::Ec(other) - } -} - -// ============================================================================= -// Jwk Params Rsa -// ============================================================================= - -/// Parameters for RSA Keys. -/// -/// [More Info](https://tools.ietf.org/html/rfc7518#section-6.3) -#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize, Zeroize)] -#[zeroize(drop)] -pub struct JwkParamsRsa { - /// The modulus value for the RSA public key as a base64urlUInt-encoded value. - /// - /// [More Info](https://tools.ietf.org/html/rfc7518#section-6.3.1.1) - pub n: String, // Modulus - /// The exponent value for the RSA public key as a base64urlUInt-encoded - /// value. - /// - /// [More Info](https://tools.ietf.org/html/rfc7518#section-6.3.1.2) - pub e: String, // Exponent - /// The private exponent value for the RSA private key as a - /// base64urlUInt-encoded value. - /// - /// [More Info](https://tools.ietf.org/html/rfc7518#section-6.3.2.1) - #[serde(skip_serializing_if = "Option::is_none")] - pub d: Option, // Private Exponent - /// The first prime factor as a base64urlUInt-encoded value. - /// - /// [More Info](https://tools.ietf.org/html/rfc7518#section-6.3.2.2) - #[serde(skip_serializing_if = "Option::is_none")] - pub p: Option, // First Prime Factor - /// The second prime factor as a base64urlUInt-encoded value. - /// - /// [More Info](https://tools.ietf.org/html/rfc7518#section-6.3.2.3) - #[serde(skip_serializing_if = "Option::is_none")] - pub q: Option, // Second Prime Factor - /// The Chinese Remainder Theorem (CRT) exponent of the first factor as a - /// base64urlUInt-encoded value. - /// - /// [More Info](https://tools.ietf.org/html/rfc7518#section-6.3.2.4) - #[serde(skip_serializing_if = "Option::is_none")] - pub dp: Option, // First Factor CRT Exponent - /// The CRT exponent of the second factor as a base64urlUInt-encoded value. - /// - /// [More Info](https://tools.ietf.org/html/rfc7518#section-6.3.2.5) - #[serde(skip_serializing_if = "Option::is_none")] - pub dq: Option, // Second Factor CRT Exponent - /// The CRT coefficient of the second factor as a base64urlUInt-encoded value. - /// - /// [More Info](https://tools.ietf.org/html/rfc7518#section-6.3.2.6) - #[serde(skip_serializing_if = "Option::is_none")] - pub qi: Option, // First CRT Coefficient - /// An array of information about any third and subsequent primes, should they - /// exist. - /// - /// [More Info](https://tools.ietf.org/html/rfc7518#section-6.3.2.7) - #[serde(skip_serializing_if = "Option::is_none")] - pub oth: Option>, // Other Primes Info -} - -/// Parameters for RSA Primes -/// -/// [More Info](https://tools.ietf.org/html/rfc7518#section-6.3.2.7) -#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize, Zeroize)] -#[zeroize(drop)] -pub struct JwkParamsRsaPrime { - /// The value of a subsequent prime factor as a base64urlUInt-encoded value. - /// - /// [More Info](https://tools.ietf.org/html/rfc7518#section-6.3.2.7.1) - pub r: String, // Prime Factor - /// The CRT exponent of the corresponding prime factor as a - /// base64urlUInt-encoded value. - /// - /// [More Info](https://tools.ietf.org/html/rfc7518#section-6.3.2.7.2) - pub d: String, // Factor CRT Exponent - /// The CRT coefficient of the corresponding prime factor as a - /// base64urlUInt-encoded value. - /// - /// [More Info](https://tools.ietf.org/html/rfc7518#section-6.3.2.7.3) - pub t: String, // Factor CRT Coefficient -} - -impl JwkParamsRsa { - pub const fn new() -> Self { - Self { - n: String::new(), - e: String::new(), - d: None, - p: None, - q: None, - dp: None, - dq: None, - qi: None, - oth: None, - } - } - - pub const fn kty(&self) -> JwkType { - JwkType::Rsa - } - - pub fn to_public(&self) -> Self { - Self { - n: self.n.clone(), - e: self.e.clone(), - d: None, - p: None, - q: None, - dp: None, - dq: None, - qi: None, - oth: None, - } - } -} - -impl From for JwkParams { - fn from(other: JwkParamsRsa) -> Self { - Self::Rsa(other) - } -} - -// ============================================================================= -// Jwk Params Oct -// ============================================================================= - -/// Parameters for Symmetric Keys. -/// -/// [More Info](https://tools.ietf.org/html/rfc7518#section-6.4) -#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize, Zeroize)] -#[zeroize(drop)] -pub struct JwkParamsOct { - /// The symmetric key as a base64url-encoded value. - /// - /// [More Info](https://tools.ietf.org/html/rfc7518#section-6.4.1) - pub k: String, // Key Value -} - -impl JwkParamsOct { - pub const fn new() -> Self { - Self { k: String::new() } - } - - pub const fn kty(&self) -> JwkType { - JwkType::Oct - } - - // TODO: Make the behaviour consistent with the adapted implementation in `identity_jose`. - pub fn to_public(&self) -> Self { - unimplemented!("Oct parameters are not considered public by this library"); - } -} - -impl From for JwkParams { - fn from(other: JwkParamsOct) -> Self { - Self::Oct(other) - } -} - -// ============================================================================= -// Jwk Params Okp -// ============================================================================= - -/// Parameters for Octet Key Pairs. -/// -/// [More Info](https://tools.ietf.org/html/rfc8037#section-2) -#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize, Zeroize)] -#[zeroize(drop)] -pub struct JwkParamsOkp { - /// The subtype of the key pair. - /// - /// [More Info](https://tools.ietf.org/html/rfc8037#section-2) - pub crv: String, // Key SubType - /// The public key as a base64url-encoded value. - /// - /// [More Info](https://tools.ietf.org/html/rfc8037#section-2) - pub x: String, // Public Key - /// The private key as a base64url-encoded value. - /// - /// [More Info](https://tools.ietf.org/html/rfc8037#section-2) - #[serde(skip_serializing_if = "Option::is_none")] - pub d: Option, // Private Key -} - -impl JwkParamsOkp { - pub const fn new() -> Self { - Self { - crv: String::new(), - x: String::new(), - d: None, - } - } - - pub const fn kty(&self) -> JwkType { - JwkType::Okp - } - - pub fn to_public(&self) -> Self { - Self { - crv: self.crv.clone(), - x: self.x.clone(), - d: None, - } - } - - pub fn try_ed_curve(&self) -> Result { - match &*self.crv { - "Ed25519" => Ok(EdCurve::Ed25519), - "Ed448" => Ok(EdCurve::Ed448), - _ => Err(Error::KeyError("Ed Curve")), - } - } - - pub fn try_ecx_curve(&self) -> Result { - match &*self.crv { - "X25519" => Ok(EcxCurve::X25519), - "X448" => Ok(EcxCurve::X448), - _ => Err(Error::KeyError("Ecx Curve")), - } - } -} - -impl From for JwkParams { - fn from(other: JwkParamsOkp) -> Self { - Self::Okp(other) - } -} diff --git a/libjose/src/jwk/key_set.rs b/libjose/src/jwk/key_set.rs deleted file mode 100644 index f765aaa381..0000000000 --- a/libjose/src/jwk/key_set.rs +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use core::iter::FromIterator; -use core::ops::Index; -use core::ops::IndexMut; -use core::slice::Iter; -use core::slice::SliceIndex; -use zeroize::Zeroize; - -use crate::jwk::Jwk; -use crate::lib::*; - -/// JSON Web Key Set. -/// -/// [More Info](https://tools.ietf.org/html/rfc7517#section-5) -#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)] -#[repr(transparent)] -pub struct JwkSet { - /// An array of JWK values. - /// - /// [More Info](https://tools.ietf.org/html/rfc7517#section-5.1) - keys: Vec, -} - -impl JwkSet { - /// Creates a new `JwkSet`. - pub const fn new() -> Self { - Self { keys: Vec::new() } - } - - /// Returns the total number of keys in the set. - pub fn len(&self) -> usize { - self.keys.len() - } - - /// Returns a boolean indicating if the set of keys is empty. - pub fn is_empty(&self) -> bool { - self.keys.is_empty() - } - - /// Returns a slice containing the entire vector of keys. - pub fn as_slice(&self) -> &[Jwk] { - &self.keys - } - - pub fn iter(&self) -> Iter<'_, Jwk> { - self.keys.iter() - } - - /// Returns a list of keys matching the given `kid`. - pub fn get(&self, kid: &str) -> Vec<&Jwk> { - self - .keys - .iter() - .filter(|key| matches!(key.kid(), Some(value) if value == kid)) - .collect() - } - - /// Adds a new `key` to the set. - pub fn add(&mut self, key: impl Into) { - self.keys.push(key.into()); - } - - /// Removes the key at position `index`, returning true if the key was - /// removed. - pub fn del(&mut self, index: usize) -> bool { - if index < self.keys.len() { - self.keys.remove(index); - true - } else { - false - } - } - - /// Removes and returns the last `key` in the set. - pub fn pop(&mut self) -> Option { - self.keys.pop() - } -} - -impl FromIterator for JwkSet { - fn from_iter(iter: I) -> Self - where - I: IntoIterator, - { - Self { - keys: Vec::from_iter(iter), - } - } -} - -impl Index for JwkSet -where - I: SliceIndex<[Jwk]>, -{ - type Output = I::Output; - - fn index(&self, index: I) -> &Self::Output { - Index::index(&*self.keys, index) - } -} - -impl IndexMut for JwkSet -where - I: SliceIndex<[Jwk]>, -{ - fn index_mut(&mut self, index: I) -> &mut Self::Output { - IndexMut::index_mut(&mut *self.keys, index) - } -} - -impl Zeroize for JwkSet { - fn zeroize(&mut self) { - self.keys.zeroize(); - } -} - -impl Drop for JwkSet { - fn drop(&mut self) { - self.zeroize(); - } -} diff --git a/libjose/src/jwk/key_type.rs b/libjose/src/jwk/key_type.rs deleted file mode 100644 index 1ade77dc30..0000000000 --- a/libjose/src/jwk/key_type.rs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use core::fmt::Display; -use core::fmt::Formatter; -use core::fmt::Result; - -/// Supported types for the JSON Web Key `typ` property. -/// -/// [More Info](https://www.iana.org/assignments/jose/jose.xhtml#web-key-types) -#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)] -pub enum JwkType { - /// Elliptic Curve. - #[serde(rename = "EC")] - Ec, - /// RSA. - #[serde(rename = "RSA")] - Rsa, - /// Octet sequence. - #[serde(rename = "oct")] - Oct, - /// Octet string key pairs. - #[serde(rename = "OKP")] - Okp, -} - -impl JwkType { - /// Returns the JWK "typ" as a `str` slice. - pub const fn name(self) -> &'static str { - match self { - Self::Ec => "EC", - Self::Rsa => "RSA", - Self::Oct => "oct", - Self::Okp => "OKP", - } - } -} - -impl Display for JwkType { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - f.write_str(self.name()) - } -} diff --git a/libjose/src/jwk/key_use.rs b/libjose/src/jwk/key_use.rs deleted file mode 100644 index f74b06dc40..0000000000 --- a/libjose/src/jwk/key_use.rs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use core::fmt::Display; -use core::fmt::Formatter; -use core::fmt::Result; - -/// Supported algorithms for the JSON Web Key `use` property. -/// -/// [More Info](https://www.iana.org/assignments/jose/jose.xhtml#web-key-use) -#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)] -pub enum JwkUse { - /// Digital Signature or MAC. - #[serde(rename = "sig")] - Signature, - /// Encryption. - #[serde(rename = "enc")] - Encryption, -} - -impl JwkUse { - /// Returns the JWK "use" as a `str` slice. - pub const fn name(&self) -> &'static str { - match self { - Self::Signature => "sig", - Self::Encryption => "enc", - } - } -} - -impl Display for JwkUse { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - f.write_str(self.name()) - } -} diff --git a/libjose/src/jwk/mod.rs b/libjose/src/jwk/mod.rs deleted file mode 100644 index d2c05fdde7..0000000000 --- a/libjose/src/jwk/mod.rs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! JSON Web Keys ([JWK](https://tools.ietf.org/html/rfc7517)) - -mod curve; -mod key; -mod key_management; -mod key_operation; -mod key_params; -mod key_set; -mod key_type; -mod key_use; - -pub use self::curve::*; -pub use self::key::*; -pub use self::key_management::*; -pub use self::key_operation::*; -pub use self::key_params::*; -pub use self::key_set::*; -pub use self::key_type::*; -pub use self::key_use::*; diff --git a/libjose/src/jwm/attributes.rs b/libjose/src/jwm/attributes.rs deleted file mode 100644 index dcc615c740..0000000000 --- a/libjose/src/jwm/attributes.rs +++ /dev/null @@ -1,197 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use serde_json::Map; -use serde_json::Value; - -use crate::jwt::JwtClaims; -use crate::lib::*; - -/// JSON Web Message Attributes Set -/// -/// [More Info](https://tools.ietf.org/id/draft-looker-jwm-01.html#rfc.section.3) -#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)] -pub struct JwmAttributes { - /// A unique identifier for the JWM. - /// - /// [More Info](https://tools.ietf.org/id/draft-looker-jwm-01.html#attributes-id) - #[serde(skip_serializing_if = "Option::is_none")] - id: Option, // Message ID - /// The type of the message. - /// - /// [More Info](https://tools.ietf.org/id/draft-looker-jwm-01.html#attributes-type) - #[serde(skip_serializing_if = "Option::is_none")] - type_: Option, // Message Type - /// Application-level message content. - /// - /// [More Info](https://tools.ietf.org/id/draft-looker-jwm-01.html#attributes-body) - #[serde(skip_serializing_if = "Option::is_none")] - body: Option>, // Message Body - /// The intended recipients of the JWM. - /// - /// [More Info](https://tools.ietf.org/id/draft-looker-jwm-01.html#attributes-to) - #[serde(skip_serializing_if = "Option::is_none")] - to: Option>, // Recipients - /// The sender of the JWM - /// - /// [More Info](https://tools.ietf.org/id/draft-looker-jwm-01.html#attributes-from) - #[serde(skip_serializing_if = "Option::is_none")] - from: Option, // Message From - /// Associates the JWM to a group of related messages. - /// - /// [More Info](https://tools.ietf.org/id/draft-looker-jwm-01.html#attributes-thread-id) - #[serde(skip_serializing_if = "Option::is_none")] - thread_id: Option, // Message Thread ID - /// The time in which the message was created. - /// - /// [More Info](https://tools.ietf.org/id/draft-looker-jwm-01.html#attributes-created-time) - #[serde(skip_serializing_if = "Option::is_none")] - created_time: Option, // Message Created Time - /// The lifespan or lifetime of the JWM. - /// - /// [More Info](https://tools.ietf.org/id/draft-looker-jwm-01.html#attributes-expires-time) - #[serde(skip_serializing_if = "Option::is_none")] - expires_time: Option, // Message Expiry Time - /// A url to which a response to the message can be sent. - /// - /// [More Info](https://tools.ietf.org/id/draft-looker-jwm-01.html#attributes-reply-url) - #[serde(skip_serializing_if = "Option::is_none")] - reply_url: Option, // Message Reply URL - /// Who a response to the message should be sent to. - /// - /// [More Info](https://tools.ietf.org/id/draft-looker-jwm-01.html#attributes-reply-to) - #[serde(skip_serializing_if = "Option::is_none")] - reply_to: Option>, // Message Reply To - /// Default JWT claims. - #[serde(flatten)] - inner: JwtClaims, -} - -impl JwmAttributes { - /// Create a new `JwmAttributes` set. - pub const fn new() -> Self { - Self { - id: None, - type_: None, - body: None, - to: None, - from: None, - thread_id: None, - created_time: None, - expires_time: None, - reply_url: None, - reply_to: None, - inner: JwtClaims::new(), - } - } - - /// Returns the value for the ID atribute (id). - pub fn id(&self) -> Option<&str> { - self.id.as_deref() - } - - /// Sets a value for the ID atribute (id). - pub fn set_id(&mut self, value: impl Into) { - self.id = Some(value.into()); - } - - /// Returns the value for the type atribute (type). - pub fn type_(&self) -> Option<&str> { - self.type_.as_deref() - } - - /// Sets a value for the type atribute (type). - pub fn set_type(&mut self, value: impl Into) { - self.type_ = Some(value.into()); - } - - /// Returns the value for the body atribute (body). - pub fn body(&self) -> Option<&Map> { - self.body.as_ref() - } - - /// Sets a value for the body atribute (body). - pub fn set_body(&mut self, value: impl IntoIterator>) { - self.body = Some(value.into_iter().map(Into::into).collect()) - } - - /// Returns the value for the to atribute (to). - pub fn to(&self) -> Option<&[String]> { - self.to.as_deref() - } - - /// Sets a value for the to atribute (to). - pub fn set_to(&mut self, value: impl IntoIterator>) { - self.to = Some(value.into_iter().map(Into::into).collect()); - } - - /// Returns the value for the from atribute (from). - pub fn from(&self) -> Option<&str> { - self.from.as_deref() - } - - /// Sets a value for the from atribute (from). - pub fn set_from(&mut self, value: impl Into) { - self.from = Some(value.into()); - } - - /// Returns the value for the thread ID atribute (thread_id). - pub fn thread_id(&self) -> Option<&str> { - self.thread_id.as_deref() - } - - /// Sets a value for the thread ID atribute (thread_id). - pub fn set_thread_id(&mut self, value: impl Into) { - self.thread_id = Some(value.into()); - } - - /// Returns the value for the created time atribute (created_time). - pub fn created_time(&self) -> Option { - self.created_time - } - - /// Sets a value for the created time atribute (created_time). - pub fn set_created_time(&mut self, value: impl Into) { - self.created_time = Some(value.into()); - } - - /// Returns the value for the expires time atribute (expires_time). - pub fn expires_time(&self) -> Option { - self.expires_time - } - - /// Sets a value for the expires time atribute (expires_time). - pub fn set_expires_time(&mut self, value: impl Into) { - self.expires_time = Some(value.into()); - } - - /// Returns the value for the reply URL atribute (reply_url). - pub fn reply_url(&self) -> Option<&str> { - self.reply_url.as_deref() - } - - /// Sets a value for the reply URL atribute (reply_url). - pub fn set_reply_url(&mut self, value: impl Into) { - self.reply_url = Some(value.into()); - } - - /// Returns the value for the reply to atribute (reply_to). - pub fn reply_to(&self) -> Option<&[String]> { - self.reply_to.as_deref() - } - - /// Sets a value for the reply to atribute (reply_to). - pub fn set_reply_to(&mut self, value: impl IntoIterator>) { - self.reply_to = Some(value.into_iter().map(Into::into).collect()); - } - - /// Returns a reference to the inner `JwtClaims` object. - pub fn claims(&self) -> &JwtClaims { - &self.inner - } - - /// Returns a mutable reference to the inner `JwtClaims` object. - pub fn claims_mut(&mut self) -> &mut JwtClaims { - &mut self.inner - } -} diff --git a/libjose/src/jwm/mod.rs b/libjose/src/jwm/mod.rs deleted file mode 100644 index dc9a2a9144..0000000000 --- a/libjose/src/jwm/mod.rs +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! JSON Web Message ([JWM](https://tools.ietf.org/html/draft-looker-jwm-01)) - -mod attributes; - -pub use self::attributes::*; diff --git a/libjose/src/jws/algorithm.rs b/libjose/src/jws/algorithm.rs deleted file mode 100644 index 34807864ea..0000000000 --- a/libjose/src/jws/algorithm.rs +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use core::fmt::Display; -use core::fmt::Formatter; -use core::fmt::Result; - -/// Supported algorithms for the JSON Web Signatures `alg` claim. -/// -/// [More Info](https://www.iana.org/assignments/jose/jose.xhtml#web-signature-encryption-algorithms) -#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)] -#[allow(non_camel_case_types)] -pub enum JwsAlgorithm { - /// HMAC using SHA-256 - HS256, - /// HMAC using SHA-384 - HS384, - /// HMAC using SHA-512 - HS512, - /// RSASSA-PKCS1-v1_5 using SHA-256 - RS256, - /// RSASSA-PKCS1-v1_5 using SHA-384 - RS384, - /// RSASSA-PKCS1-v1_5 using SHA-512 - RS512, - /// RSASSA-PSS using SHA-256 and MGF1 with SHA-256 - PS256, - /// RSASSA-PSS using SHA-384 and MGF1 with SHA-384 - PS384, - /// RSASSA-PSS using SHA-512 and MGF1 with SHA-512 - PS512, - /// ECDSA using P-256 and SHA-256 - ES256, - /// ECDSA using P-384 and SHA-384 - ES384, - /// ECDSA using P-521 and SHA-512 - ES512, - /// ECDSA using secp256k1 curve and SHA-256 - ES256K, - #[serde(rename = "none")] - /// No digital signature or MAC performed - NONE, - /// EdDSA signature algorithms - EdDSA, -} - -impl JwsAlgorithm { - pub const ALL: &'static [Self] = &[ - Self::HS256, - Self::HS384, - Self::HS512, - Self::RS256, - Self::RS384, - Self::RS512, - Self::PS256, - Self::PS384, - Self::PS512, - Self::ES256, - Self::ES384, // unsupported - Self::ES512, // unsupported - Self::ES256K, - Self::NONE, // unsupported - Self::EdDSA, - ]; - - /// Returns the JWS algorithm as a `str` slice. - pub const fn name(self) -> &'static str { - match self { - Self::HS256 => "HS256", - Self::HS384 => "HS384", - Self::HS512 => "HS512", - Self::RS256 => "RS256", - Self::RS384 => "RS384", - Self::RS512 => "RS512", - Self::PS256 => "PS256", - Self::PS384 => "PS384", - Self::PS512 => "PS512", - Self::ES256 => "ES256", - Self::ES384 => "ES384", - Self::ES512 => "ES512", - Self::ES256K => "ES256K", - Self::NONE => "none", - Self::EdDSA => "EdDSA", - } - } -} - -impl Display for JwsAlgorithm { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - f.write_str(self.name()) - } -} diff --git a/libjose/src/jws/charset.rs b/libjose/src/jws/charset.rs deleted file mode 100644 index c7b8a68231..0000000000 --- a/libjose/src/jws/charset.rs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use core::str::from_utf8; - -use crate::error::Error; -use crate::error::Result; - -#[derive(Clone, Copy, Debug)] -pub enum CharSet { - /// The ASCII space character and all printable ASCII characters other - /// than period ('.') (those characters in the ranges %x20-2D and %x2F-7E) - /// MAY be included in a non-detached payload using the JWS Compact - /// Serialization, provided that the application can transmit the - /// resulting JWS without modification. - Default, - /// If a JWS using the JWS Compact Serialization and a non-detached - /// payload is to be transmitted in a context that requires URL-safe - /// characters, then the application MUST ensure that the payload - /// contains only the URL-safe characters 'a'-'z', 'A'-'Z', '0'-'9', - /// dash ('-'), underscore ('_'), and tilde ('~'). - UrlSafe, -} - -impl CharSet { - /// Validate unencoded content for used with the compact serialization - /// format. The payload MUST NOT contain a period (`.`) and MAY be - /// required to only contain URL-safe characters. - /// - /// [More Info](https://tools.ietf.org/html/rfc7797#section-5.2) - pub fn validate(&self, data: &[u8]) -> Result<()> { - let payload: &str = from_utf8(data).map_err(|_| Error::InvalidContent("UTF-8"))?; - - if payload.contains('.') { - return Err(Error::InvalidContent("Invalid Character: `.`")); - } - - if !self.__validate(payload) { - // TODO: Improve this error - return Err(Error::InvalidContent("Invalid Character(s)")); - } - - Ok(()) - } - - fn __validate(&self, data: &str) -> bool { - match self { - Self::Default => data.chars().all(|ch| matches!(ch, '\x20'..='\x2D' | '\x2F'..='\x7E')), - Self::UrlSafe => data - .chars() - .all(|ch| matches!(ch, 'a'..='z' | 'A'..='Z' | '0'..='9' | '-' | '_' | '~')), - } - } -} diff --git a/libjose/src/jws/decoder.rs b/libjose/src/jws/decoder.rs deleted file mode 100644 index b580040284..0000000000 --- a/libjose/src/jws/decoder.rs +++ /dev/null @@ -1,299 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use core::convert::TryInto; -use core::str; -use crypto::hashes::sha::SHA256; -use crypto::hashes::sha::SHA256_LEN; -use crypto::hashes::sha::SHA384; -use crypto::hashes::sha::SHA384_LEN; -use crypto::hashes::sha::SHA512; -use crypto::hashes::sha::SHA512_LEN; -use crypto::macs::hmac::HMAC_SHA256; -use crypto::macs::hmac::HMAC_SHA384; -use crypto::macs::hmac::HMAC_SHA512; -use serde_json::from_slice; -use subtle::ConstantTimeEq as _; - -use crate::error::Error; -use crate::error::Result; -use crate::jwk::EdCurve; -use crate::jws::JwsAlgorithm; -use crate::jws::JwsFormat; -use crate::jws::JwsHeader; -use crate::jwt::JwtHeaderSet; -use crate::lib::*; -use crate::utils::check_slice_param; -use crate::utils::create_message; -use crate::utils::decode_b64; -use crate::utils::decode_b64_json; -use crate::utils::filter_non_empty_bytes; -use crate::utils::parse_utf8; -use crate::utils::validate_jws_headers; -use crate::utils::Secret; - -type HeaderSet<'a> = JwtHeaderSet<'a, JwsHeader>; - -type Ed25519Signature = crypto::signatures::ed25519::Signature; - -const COMPACT_SEGMENTS: usize = 3; - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Token<'a> { - pub protected: Option, - pub unprotected: Option, - pub claims: Cow<'a, [u8]>, -} - -#[derive(Deserialize)] -#[serde(deny_unknown_fields)] -struct Signature<'a> { - header: Option, - protected: Option<&'a str>, - signature: &'a str, -} - -#[derive(Deserialize)] -#[serde(deny_unknown_fields)] -struct General<'a> { - payload: Option<&'a str>, - signatures: Vec>, -} - -#[derive(Deserialize)] -#[serde(deny_unknown_fields)] -struct Flatten<'a> { - payload: Option<&'a str>, - #[serde(flatten)] - signature: Signature<'a>, -} - -// ============================================================================= -// ============================================================================= - -pub struct Decoder<'a, 'b> { - /// The expected format of the encoded token. - format: JwsFormat, - /// The curve used for EdDSA signatures. - eddsa_curve: EdCurve, - /// The public key used for signature verification. - public: Secret<'a>, - /// A list of permitted signature algorithms. - algs: Option>, - /// A list of permitted extension parameters. - crits: Option>, - /// The expected key id of the decoded token. - key_id: Option, - /// The detached payload, if using detached content - /// - /// [More Info](https://tools.ietf.org/html/rfc7515#appendix-F) - payload: Option<&'b [u8]>, -} - -impl<'a, 'b> Decoder<'a, 'b> { - pub fn new(public: impl Into>) -> Self { - Self { - format: JwsFormat::Compact, - eddsa_curve: EdCurve::Ed25519, - public: public.into(), - algs: None, - crits: None, - key_id: None, - payload: None, - } - } - - pub fn format(mut self, value: JwsFormat) -> Self { - self.format = value; - self - } - - pub fn eddsa_curve(mut self, value: EdCurve) -> Self { - self.eddsa_curve = value; - self - } - - pub fn algorithm(mut self, value: JwsAlgorithm) -> Self { - self.algs.get_or_insert_with(Vec::new).push(value); - self - } - - pub fn critical(mut self, value: impl Into) -> Self { - self.crits.get_or_insert_with(Vec::new).push(value.into()); - self - } - - pub fn key_id(mut self, value: impl Into) -> Self { - self.key_id = Some(value.into()); - self - } - - pub fn payload(mut self, value: &'b [u8]) -> Self { - self.payload = Some(value); - self - } - - pub fn decode(&self, data: &'b [u8]) -> Result> { - self.expand(data, |payload, signatures| { - for signature in signatures { - if let Ok(token) = self.decode_one(payload, signature) { - return Ok(token); - } - } - - Err(Error::InvalidContent("Recipient (not found)")) - }) - } - - fn decode_one(&self, payload: &'b [u8], signature: Signature<'a>) -> Result> { - let protected: Option = signature.protected.map(decode_b64_json).transpose()?; - - validate_jws_headers(protected.as_ref(), signature.header.as_ref(), self.crits.as_deref())?; - - let merged: HeaderSet<'_> = HeaderSet::new() - .protected(protected.as_ref()) - .unprotected(signature.header.as_ref()); - - self.check_alg(merged.try_alg()?)?; - self.check_kid(merged.kid())?; - - { - let protected: &[u8] = signature.protected.map(str::as_bytes).unwrap_or_default(); - let message: Vec = create_message(protected, payload); - let signature: Vec = decode_b64(signature.signature)?; - - self.verify(merged.try_alg()?, &message, &signature)?; - } - - let claims: Cow<'b, [u8]> = if merged.b64().unwrap_or(true) { - Cow::Owned(decode_b64(payload)?) - } else { - Cow::Borrowed(payload) - }; - - Ok(Token { - protected, - unprotected: signature.header, - claims, - }) - } - - fn expand(&self, data: &'b [u8], format: impl Fn(&'b [u8], Vec>) -> Result) -> Result { - match self.format { - JwsFormat::Compact => { - let split: Vec<&[u8]> = data.split(|byte| *byte == b'.').collect(); - - if split.len() != COMPACT_SEGMENTS { - return Err(Error::InvalidContent("Segments (count)")); - } - - let signature: Signature<'_> = Signature { - header: None, - protected: Some(parse_utf8(split[0])?), - signature: parse_utf8(split[2])?, - }; - - format(self.expand_payload(Some(split[1]))?, vec![signature]) - } - JwsFormat::General => { - let data: General<'_> = from_slice(data)?; - - format(self.expand_payload(data.payload)?, data.signatures) - } - JwsFormat::Flatten => { - let data: Flatten<'_> = from_slice(data)?; - - format(self.expand_payload(data.payload)?, vec![data.signature]) - } - } - } - - fn expand_payload(&self, payload: Option<&'b (impl AsRef<[u8]> + ?Sized)>) -> Result<&'b [u8]> { - match (self.payload, filter_non_empty_bytes(payload)) { - (Some(payload), None) => Ok(payload), - (None, Some(payload)) => Ok(payload), - (Some(_), Some(_)) => Err(Error::InvalidContent("Payload (multiple)")), - (None, None) => Err(Error::InvalidContent("Payload (missing)")), - } - } - - fn check_alg(&self, value: JwsAlgorithm) -> Result<()> { - check_slice_param("alg", self.algs.as_deref(), &value) - } - - fn check_kid(&self, value: Option<&str>) -> Result<()> { - if self.key_id.as_deref() == value { - Ok(()) - } else { - Err(Error::InvalidParam("kid")) - } - } - - fn verify(&self, algorithm: JwsAlgorithm, message: &[u8], signature: &[u8]) -> Result<()> { - macro_rules! hmac { - ($impl:ident, $key_len:ident, $message:expr, $signature:expr, $secret:expr) => {{ - let secret: Cow<'_, [u8]> = $secret.to_oct_key($key_len)?; - let mut mac: [u8; $key_len] = [0; $key_len]; - - $impl($message, &secret, &mut mac); - - if $signature.ct_eq(&mac).unwrap_u8() != 1 { - return Err(Error::SigError("HMAC")); - } - }}; - } - - macro_rules! rsa { - ($padding:ident, $digest:ident, $digest_len:ident, $message:expr, $signature:expr, $secret:expr) => {{ - let mut digest: [u8; $digest_len] = [0; $digest_len]; - - $digest($message, &mut digest); - - let secret: _ = $secret.to_rsa_public()?; - let padding: _ = $crate::rsa_padding!(@$padding); - - rsa::PublicKey::verify(&secret, padding, &digest, $signature)?; - }}; - } - - let public: Secret<'_> = self.public; - - public.check_verifying_key(algorithm.name())?; - - match algorithm { - JwsAlgorithm::HS256 => hmac!(HMAC_SHA256, SHA256_LEN, message, signature, public), - JwsAlgorithm::HS384 => hmac!(HMAC_SHA384, SHA384_LEN, message, signature, public), - JwsAlgorithm::HS512 => hmac!(HMAC_SHA512, SHA512_LEN, message, signature, public), - JwsAlgorithm::RS256 => rsa!(PKCS1_SHA256, SHA256, SHA256_LEN, message, signature, public), - JwsAlgorithm::RS384 => rsa!(PKCS1_SHA384, SHA384, SHA384_LEN, message, signature, public), - JwsAlgorithm::RS512 => rsa!(PKCS1_SHA512, SHA512, SHA512_LEN, message, signature, public), - JwsAlgorithm::PS256 => rsa!(PSS_SHA256, SHA256, SHA256_LEN, message, signature, public), - JwsAlgorithm::PS384 => rsa!(PSS_SHA384, SHA384, SHA384_LEN, message, signature, public), - JwsAlgorithm::PS512 => rsa!(PSS_SHA512, SHA512, SHA512_LEN, message, signature, public), - JwsAlgorithm::ES256 => public.to_p256_public()?.verify(message, signature)?, - JwsAlgorithm::ES384 => return Err(Error::AlgError("ES384")), - JwsAlgorithm::ES512 => return Err(Error::AlgError("ES512")), - JwsAlgorithm::ES256K => public.to_k256_public()?.verify(message, signature)?, - JwsAlgorithm::NONE => return Err(Error::AlgError("NONE")), - JwsAlgorithm::EdDSA => match self.eddsa_curve { - EdCurve::Ed25519 => verify_ed25519(public, message, signature)?, - EdCurve::Ed448 => return Err(Error::AlgError("EdDSA/Ed448")), - }, - } - - Ok(()) - } -} - -fn verify_ed25519(key: Secret<'_>, message: &[u8], signature: &[u8]) -> Result<()> { - let signature: Ed25519Signature = signature - .try_into() - .map_err(|_| Error::SigError("Ed25519")) - .map(Ed25519Signature::from_bytes)?; - - if key.to_ed25519_public()?.verify(&signature, message) { - return Ok(()); - } - - Err(Error::SigError("Ed25519")) -} diff --git a/libjose/src/jws/encoder.rs b/libjose/src/jws/encoder.rs deleted file mode 100644 index 11baf8cdc2..0000000000 --- a/libjose/src/jws/encoder.rs +++ /dev/null @@ -1,319 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use core::str; -use crypto::hashes::sha::SHA256; -use crypto::hashes::sha::SHA256_LEN; -use crypto::hashes::sha::SHA384; -use crypto::hashes::sha::SHA384_LEN; -use crypto::hashes::sha::SHA512; -use crypto::hashes::sha::SHA512_LEN; -use crypto::macs::hmac::HMAC_SHA256; -use crypto::macs::hmac::HMAC_SHA384; -use crypto::macs::hmac::HMAC_SHA512; -use serde::Serialize; -use serde_json::to_vec; - -use crate::error::Error; -use crate::error::Result; -use crate::jwk::EdCurve; -use crate::jws::CharSet; -use crate::jws::JwsAlgorithm; -use crate::jws::JwsFormat; -use crate::jws::JwsHeader; -use crate::jws::Recipient; -use crate::lib::*; -use crate::utils::create_message; -use crate::utils::encode_b64; -use crate::utils::encode_b64_json; -use crate::utils::extract_b64; -use crate::utils::validate_jws_headers; -use crate::utils::Secret; - -macro_rules! to_json { - ($data:expr) => {{ - ::serde_json::to_string(&$data).map_err(Into::into) - }}; -} - -#[derive(Serialize)] -struct Signature<'a> { - #[serde(skip_serializing_if = "Option::is_none")] - header: Option<&'a JwsHeader>, - #[serde(skip_serializing_if = "Option::is_none")] - protected: Option, - signature: String, -} - -#[derive(Serialize)] -struct General<'a> { - #[serde(skip_serializing_if = "Option::is_none")] - payload: Option<&'a str>, - signatures: Vec>, -} - -#[derive(Serialize)] -struct Flatten<'a, 'b> { - #[serde(skip_serializing_if = "Option::is_none")] - payload: Option<&'a str>, - #[serde(flatten)] - signature: &'b Signature<'a>, -} - -// ============================================================================= -// ============================================================================= - -pub struct Encoder<'a> { - /// The output format of the encoded token. - format: JwsFormat, - /// Content validation rules for unencoded content using the compact format. - charset: CharSet, - /// Encode the token with detached content. - /// - /// [More Info](https://tools.ietf.org/html/rfc7515#appendix-F) - detached: bool, - /// Per-recipient configuration. - recipients: Vec>, -} - -impl Default for Encoder<'_> { - fn default() -> Self { - Self::new() - } -} - -impl<'a> Encoder<'a> { - pub fn new() -> Self { - Self { - format: JwsFormat::Compact, - charset: CharSet::Default, - detached: false, - recipients: Vec::new(), - } - } - - pub fn format(mut self, value: JwsFormat) -> Self { - self.format = value; - self - } - - pub fn charset(mut self, value: CharSet) -> Self { - self.charset = value; - self - } - - pub fn detached(mut self, value: bool) -> Self { - self.detached = value; - self - } - - pub fn recipient(mut self, value: impl Into>) -> Self { - self.recipients.push(value.into()); - self - } - - pub fn encode_serde(&self, claims: &T) -> Result - where - T: Serialize, - { - self.encode(&to_vec(claims)?) - } - - pub fn encode(&self, claims: &[u8]) -> Result { - if self.recipients.is_empty() { - return Err(Error::SigError("Missing Recipients")); - } - - self.validate()?; - - let b64: bool = extract_b64(self.recipients[0].protected); - let tmp: String; - - // Extract the "b64" header parameter and encode the payload as required. - // - // See: https://tools.ietf.org/html/rfc7797#section-3 - let payload: &[u8] = if b64 { - tmp = encode_b64(claims); - tmp.as_bytes() - } else if self.detached { - claims - } else { - self.charset.validate(claims)?; - claims - }; - - let encoded: Vec> = self - .recipients - .iter() - .copied() - .map(|recipient| encode_recipient(payload, recipient)) - .collect::>()?; - - assert_eq!(encoded.len(), self.recipients.len()); - - match (self.format, &*encoded) { - (JwsFormat::Compact, [recipient]) => { - let protected: &str = recipient.protected.as_deref().unwrap_or_default(); - - if let Some(payload) = self.format_payload(payload) { - Ok(format!("{}.{}.{}", protected, payload, recipient.signature)) - } else { - Ok(format!("{}..{}", protected, recipient.signature)) - } - } - (JwsFormat::General, _) => { - to_json!(General { - payload: self.format_payload(payload), - signatures: encoded, - }) - } - (JwsFormat::Flatten, [recipient]) => { - to_json!(Flatten { - payload: self.format_payload(payload), - signature: recipient, - }) - } - _ => { - unreachable!() - } - } - } - - fn format_payload(&self, payload: &'a [u8]) -> Option<&'a str> { - if self.detached { - None - } else { - // SAFETY: We validated the payload and ensured valid UTF-8 as an earlier - // step in the encoding process; this function is not exposed to users. - Some(unsafe { str::from_utf8_unchecked(payload) }) - } - } - - fn validate(&self) -> Result<()> { - match (self.format, &*self.recipients) { - (JwsFormat::Compact, &[recipient @ Recipient { unprotected: None, .. }]) => validate_jws_headers( - recipient.protected, - None, - recipient.protected.and_then(|header| header.crit()), - ), - (JwsFormat::Compact, _) => Err(Error::SigError( - "JWS Compact Serialization doesn't support multiple recipients or unprotected headers", - )), - (JwsFormat::General, recipients) => { - let mut __b64: bool = false; - - for recipient in recipients { - if !__b64 && recipient.protected.and_then(JwsHeader::b64).is_some() { - __b64 = true; - } - - validate_jws_headers( - recipient.protected, - recipient.unprotected, - recipient.protected.and_then(|header| header.crit()), - )?; - } - - if __b64 { - validate_recipient_b64(recipients)?; - } - - Ok(()) - } - (JwsFormat::Flatten, &[recipient]) => validate_jws_headers( - recipient.protected, - recipient.unprotected, - recipient.protected.and_then(|header| header.crit()), - ), - (JwsFormat::Flatten, _) => Err(Error::SigError( - "JWS Flattened Serialization doesn't support multiple recipients", - )), - } - } -} - -// ============================================================================= -// ============================================================================= - -fn validate_recipient_b64(recipients: &[Recipient<'_>]) -> Result<()> { - // The "b64" header parameter value MUST be the same for all recipients - recipients - .iter() - .map(|recipient| crate::utils::extract_b64(recipient.protected)) - .try_fold::<_, _, Result<_>>(None, |acc, item| match acc { - Some(current) if current == item => Ok(acc), - Some(_) => Err(Error::InvalidParam("b64")), - None => Ok(Some(item)), - }) - .map(|_| ()) -} - -fn encode_recipient<'a>(payload: &[u8], recipient: Recipient<'a>) -> Result> { - let algorithm: JwsAlgorithm = recipient - .protected - .map(JwsHeader::alg) - .or_else(|| recipient.unprotected.map(JwsHeader::alg)) - .ok_or(Error::InvalidParam("alg"))?; - - let protected: Option = recipient.protected.map(encode_b64_json).transpose()?; - let header: &[u8] = protected.as_deref().map(str::as_bytes).unwrap_or_default(); - let message: Vec = create_message(header, payload); - let signature: String = sign(algorithm, &message, recipient)?; - - Ok(Signature { - header: recipient.unprotected, - protected, - signature, - }) -} - -fn sign(algorithm: JwsAlgorithm, message: &[u8], recipient: Recipient<'_>) -> Result { - macro_rules! hmac { - ($impl:ident, $key_len:ident, $message:expr, $secret:expr) => {{ - let secret: Cow<'_, [u8]> = $secret.to_oct_key($key_len)?; - let mut mac: [u8; $key_len] = [0; $key_len]; - - $impl($message, &secret, &mut mac); - - Ok(encode_b64(mac)) - }}; - } - - macro_rules! rsa { - ($padding:ident, $digest:ident, $digest_len:ident, $message:expr, $secret:expr) => {{ - let mut digest: [u8; $digest_len] = [0; $digest_len]; - - $digest($message, &mut digest); - - let secret: _ = $secret.to_rsa_secret()?; - let padding: _ = $crate::rsa_padding!(@$padding); - - Ok(encode_b64(secret.sign(padding, &digest)?)) - }}; - } - - let secret: Secret<'_> = recipient.secret; - - secret.check_signing_key(algorithm.name())?; - - match algorithm { - JwsAlgorithm::HS256 => hmac!(HMAC_SHA256, SHA256_LEN, message, secret), - JwsAlgorithm::HS384 => hmac!(HMAC_SHA384, SHA384_LEN, message, secret), - JwsAlgorithm::HS512 => hmac!(HMAC_SHA512, SHA512_LEN, message, secret), - JwsAlgorithm::RS256 => rsa!(PKCS1_SHA256, SHA256, SHA256_LEN, message, secret), - JwsAlgorithm::RS384 => rsa!(PKCS1_SHA384, SHA384, SHA384_LEN, message, secret), - JwsAlgorithm::RS512 => rsa!(PKCS1_SHA512, SHA512, SHA512_LEN, message, secret), - JwsAlgorithm::PS256 => rsa!(PSS_SHA256, SHA256, SHA256_LEN, message, secret), - JwsAlgorithm::PS384 => rsa!(PSS_SHA384, SHA384, SHA384_LEN, message, secret), - JwsAlgorithm::PS512 => rsa!(PSS_SHA512, SHA512, SHA512_LEN, message, secret), - JwsAlgorithm::ES256 => Ok(encode_b64(secret.to_p256_secret()?.sign(message)?)), - JwsAlgorithm::ES384 => Err(Error::AlgError("ES384")), - JwsAlgorithm::ES512 => Err(Error::AlgError("ES512")), - JwsAlgorithm::ES256K => Ok(encode_b64(secret.to_k256_secret()?.sign(message)?)), - JwsAlgorithm::NONE => Err(Error::AlgError("NONE")), - JwsAlgorithm::EdDSA => match recipient.eddsa_curve { - EdCurve::Ed25519 => Ok(encode_b64(secret.to_ed25519_secret()?.sign(message).to_bytes())), - EdCurve::Ed448 => Err(Error::AlgError("EdDSA/Ed448")), - }, - } -} diff --git a/libjose/src/jws/format.rs b/libjose/src/jws/format.rs deleted file mode 100644 index 93369eb852..0000000000 --- a/libjose/src/jws/format.rs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] -pub enum JwsFormat { - Compact, - General, - Flatten, -} - -impl Default for JwsFormat { - fn default() -> Self { - Self::Compact - } -} diff --git a/libjose/src/jws/header.rs b/libjose/src/jws/header.rs deleted file mode 100644 index d4be0454f5..0000000000 --- a/libjose/src/jws/header.rs +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use core::ops::Deref; -use core::ops::DerefMut; - -use crate::jose::JoseHeader; -use crate::jws::JwsAlgorithm; -use crate::jwt::JwtHeader; -use crate::lib::*; - -/// JSON Web Signature JOSE Header. -/// -/// [More Info](https://tools.ietf.org/html/rfc7515#section-4) -#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] -pub struct JwsHeader { - /// Common JOSE Header Parameters. - #[serde(flatten)] - common: JwtHeader, - /// Algorithm. - /// - /// Identifies the cryptographic algorithm used to secure the JWS. - /// - /// [More Info](https://tools.ietf.org/html/rfc7515#section-4.1.1) - alg: JwsAlgorithm, - /// Base64url-Encode Payload. - /// - /// Determines whether the payload is represented in the JWS and the JWS - /// signing input as ASCII(BASE64URL(JWS Payload)) or as the JWS Payload - /// value itself with no encoding performed. - /// - /// [More Info](https://tools.ietf.org/html/rfc7797#section-3) - /// - /// The following table shows the JWS Signing Input computation, depending - /// upon the value of this parameter: - /// - /// +-------+-----------------------------------------------------------+ - /// | "b64" | JWS Signing Input Formula | - /// +-------+-----------------------------------------------------------+ - /// | true | ASCII(BASE64URL(UTF8(JWS Protected Header)) || '.' || | - /// | | BASE64URL(JWS Payload)) | - /// | | | - /// | false | ASCII(BASE64URL(UTF8(JWS Protected Header)) || '.') || | - /// | | JWS Payload | - /// +-------+-----------------------------------------------------------+ - #[serde(skip_serializing_if = "Option::is_none")] - b64: Option, - /// PASSporT extension identifier. - /// - /// [More Info](https://tools.ietf.org/html/rfc8225#section-8.1) - #[serde(skip_serializing_if = "Option::is_none")] - ppt: Option, -} - -impl JwsHeader { - /// Create a new `JwsHeader` with the given `alg` claim. - pub const fn new(alg: JwsAlgorithm) -> Self { - Self { - common: JwtHeader::new(), - alg, - b64: None, - ppt: None, - } - } - - /// Returns the value for the algorithm claim (alg). - pub fn alg(&self) -> JwsAlgorithm { - self.alg - } - - /// Sets a value for the algorithm claim (alg). - pub fn set_alg(&mut self, value: impl Into) { - self.alg = value.into(); - } - - /// Returns the value of the base64url-encode payload claim (b64). - pub fn b64(&self) -> Option { - self.b64 - } - - /// Sets a value for the base64url-encode payload claim (b64). - pub fn set_b64(&mut self, value: impl Into) { - self.b64 = Some(value.into()); - } - - /// Returns the value of the passport extension claim (ppt). - pub fn ppt(&self) -> Option<&str> { - self.ppt.as_deref() - } - - /// Sets a value for the passport extension claim (ppt). - pub fn set_ppt(&mut self, value: impl Into) { - self.ppt = Some(value.into()); - } - - // =========================================================================== - // =========================================================================== - - pub fn has(&self, claim: &str) -> bool { - match claim { - "alg" => true, // we always have an algorithm - "b64" => self.b64().is_some(), - "ppt" => self.ppt().is_some(), - _ => self.common.has(claim), - } - } -} - -impl Deref for JwsHeader { - type Target = JwtHeader; - - fn deref(&self) -> &Self::Target { - &self.common - } -} - -impl DerefMut for JwsHeader { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.common - } -} - -impl JoseHeader for JwsHeader { - fn common(&self) -> &JwtHeader { - self - } - - fn has_claim(&self, claim: &str) -> bool { - self.has(claim) - } -} diff --git a/libjose/src/jws/mod.rs b/libjose/src/jws/mod.rs deleted file mode 100644 index 56f19ae8ad..0000000000 --- a/libjose/src/jws/mod.rs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! JSON Web Signatures ([JWS](https://tools.ietf.org/html/rfc7515)) - -mod algorithm; -mod charset; -mod decoder; -mod encoder; -mod format; -mod header; -mod recipient; - -pub use self::algorithm::*; -pub use self::charset::*; -pub use self::decoder::*; -pub use self::encoder::*; -pub use self::format::*; -pub use self::header::*; -pub use self::recipient::*; diff --git a/libjose/src/jws/recipient.rs b/libjose/src/jws/recipient.rs deleted file mode 100644 index 168f65bbce..0000000000 --- a/libjose/src/jws/recipient.rs +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use crate::jwk::EdCurve; -use crate::jws::JwsHeader; -use crate::utils::Secret; - -#[derive(Clone, Copy)] -pub struct Recipient<'a> { - /// The curve used for EdDSA signatures. - pub eddsa_curve: EdCurve, - /// The private key used for signature creation. - pub secret: Secret<'a>, - /// The integrity-protected JOSE header. - pub protected: Option<&'a JwsHeader>, - /// The non integrity-protected JOSE header. - pub unprotected: Option<&'a JwsHeader>, -} - -impl<'a> Recipient<'a> { - pub fn new(secret: impl Into>) -> Self { - Self { - eddsa_curve: EdCurve::Ed25519, - secret: secret.into(), - protected: None, - unprotected: None, - } - } - - pub fn eddsa_curve(mut self, value: EdCurve) -> Self { - self.eddsa_curve = value; - self - } - - pub fn protected(mut self, value: &'a JwsHeader) -> Self { - self.protected = Some(value); - self - } - - pub fn unprotected(mut self, value: &'a JwsHeader) -> Self { - self.unprotected = Some(value); - self - } -} - -impl<'a, T> From for Recipient<'a> -where - T: Into>, -{ - fn from(other: T) -> Self { - Self::new(other) - } -} - -impl<'a, T> From<(T, &'a JwsHeader)> for Recipient<'a> -where - T: Into>, -{ - fn from(other: (T, &'a JwsHeader)) -> Self { - Self::new(other.0).protected(other.1) - } -} - -impl<'a, T> From<(T, &'a JwsHeader, &'a JwsHeader)> for Recipient<'a> -where - T: Into>, -{ - fn from(other: (T, &'a JwsHeader, &'a JwsHeader)) -> Self { - Self::new(other.0).protected(other.1).unprotected(other.2) - } -} diff --git a/libjose/src/jwt/claims.rs b/libjose/src/jwt/claims.rs deleted file mode 100644 index aede10c063..0000000000 --- a/libjose/src/jwt/claims.rs +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use serde_json::Map; -use serde_json::Value; - -use crate::lib::*; - -/// JSON Web Token Claims -/// -/// [More Info](https://tools.ietf.org/html/rfc7519#section-4) -#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)] -pub struct JwtClaims { - /// Identifies the principal that issued the JWT - /// - /// [More Info](https://tools.ietf.org/html/rfc7519#section-4.1.1) - #[serde(skip_serializing_if = "Option::is_none")] - iss: Option, // Issuer - /// Identifies the principal that is the subject of the JWT. - /// - /// [More Info](https://tools.ietf.org/html/rfc7519#section-4.1.2) - #[serde(skip_serializing_if = "Option::is_none")] - sub: Option, // Subject - /// Identifies the recipients that the JWT is intended for. - /// - /// TODO: Handle single value - /// - /// [More Info](https://tools.ietf.org/html/rfc7519#section-4.1.3) - #[serde(skip_serializing_if = "Option::is_none")] - aud: Option>, // Audience - /// Identifies the expiration time on or after which the JWT MUST NOT be - /// accepted for processing. - /// - /// [More Info](https://tools.ietf.org/html/rfc7519#section-4.1.4) - #[serde(skip_serializing_if = "Option::is_none")] - exp: Option, // Expiration Time - /// Identifies the time before which the JWT MUST NOT be accepted for - /// processing. - /// - /// [More Info](https://tools.ietf.org/html/rfc7519#section-4.1.5) - #[serde(skip_serializing_if = "Option::is_none")] - nbf: Option, // Not Before - /// Identifies the time at which the JWT was issued. - /// - /// [More Info](https://tools.ietf.org/html/rfc7519#section-4.1.6) - #[serde(skip_serializing_if = "Option::is_none")] - iat: Option, // Issued At - /// Provides a unique identifier for the JWT. - /// - /// [More Info](https://tools.ietf.org/html/rfc7519#section-4.1.7) - #[serde(skip_serializing_if = "Option::is_none")] - jti: Option, // JWT ID - /// Identifies a decentralized digital identity. - /// - /// [More Info](https://www.w3.org/TR/did-core/) - #[serde(skip_serializing_if = "Option::is_none")] - did: Option, // Decentralized Identifier - /// Contains the properties of a Verifiable Credential - /// - /// [More Info](https://w3c.github.io/vc-data-model/#json-web-token) - #[serde(skip_serializing_if = "Option::is_none")] - vc: Option>, // Verifiable Credential - /// Contains the properties of a Verifiable Presentation - /// - /// [More Info](https://w3c.github.io/vc-data-model/#json-web-token) - #[serde(skip_serializing_if = "Option::is_none")] - vp: Option>, // Verifiable Presentation - /// Public/Private Claim Names - /// - /// [More Info](https://tools.ietf.org/html/rfc7519#section-4.2) - #[serde(flatten, skip_serializing_if = "Option::is_none")] - custom: Option, -} - -impl JwtClaims { - /// Create a new `JwtClaims` set. - pub const fn new() -> Self { - Self { - iss: None, - sub: None, - aud: None, - exp: None, - nbf: None, - iat: None, - jti: None, - did: None, - vc: None, - vp: None, - custom: None, - } - } - - /// Returns the value for the issuer claim (iss). - pub fn iss(&self) -> Option<&str> { - self.iss.as_deref() - } - - /// Sets a value for the issuer claim (iss). - pub fn set_iss(&mut self, value: impl Into) { - self.iss = Some(value.into()); - } - - /// Returns the value for the subject claim (sub). - pub fn sub(&self) -> Option<&str> { - self.sub.as_deref() - } - - /// Sets a value for the subject claim (sub). - pub fn set_sub(&mut self, value: impl Into) { - self.sub = Some(value.into()); - } - - /// Returns the values for the audience claim (aud). - pub fn aud(&self) -> Option<&[String]> { - self.aud.as_deref() - } - - /// Sets values for the audience claim (aud). - pub fn set_aud(&mut self, value: impl IntoIterator>) { - self.aud = Some(value.into_iter().map(Into::into).collect()); - } - - /// Returns the time for the expires at claim (exp). - pub fn exp(&self) -> Option { - self.exp - } - - /// Sets a time for the expires at claim (exp). - pub fn set_exp(&mut self, value: impl Into) { - self.exp = Some(value.into()); - } - - /// Returns the time for the not before claim (nbf). - pub fn nbf(&self) -> Option { - self.nbf - } - - /// Sets a time for the not before claim (nbf). - pub fn set_nbf(&mut self, value: impl Into) { - self.nbf = Some(value.into()); - } - - /// Returns the time for the issued at claim (iat). - pub fn iat(&self) -> Option { - self.iat - } - - /// Sets a time for the issued at claim (iat). - pub fn set_iat(&mut self, value: impl Into) { - self.iat = Some(value.into()); - } - - /// Returns the value for the JWT ID claim (jti). - pub fn jti(&self) -> Option<&str> { - self.jti.as_deref() - } - - /// Sets a value for the JWT ID claim (jti). - pub fn set_jti(&mut self, value: impl Into) { - self.jti = Some(value.into()); - } - - /// Returns the value for the JWT DID claim (did). - pub fn did(&self) -> Option<&str> { - self.did.as_deref() - } - - /// Sets a value for the JWT DID claim (did). - pub fn set_did(&mut self, value: impl Into) { - self.did = Some(value.into()); - } - - /// Returns the value for the JWT verifiable credential claim (vc). - pub fn vc(&self) -> Option<&Map> { - self.vc.as_ref() - } - - /// Sets a value for the JWT verifiable credential claim (vc). - pub fn set_vc(&mut self, value: impl Into>) { - self.vc = Some(value.into()); - } - - /// Returns the value for the JWT verifiable presentation claim (vp). - pub fn vp(&self) -> Option<&Map> { - self.vp.as_ref() - } - - /// Sets a value for the JWT verifiable presentation claim (vp). - pub fn set_vp(&mut self, value: impl Into>) { - self.vp = Some(value.into()); - } - - /// Returns a reference to the custom JWT claims. - pub fn custom(&self) -> Option<&T> { - self.custom.as_ref() - } - - /// Returns a mutable reference to the custom JWT claims. - pub fn custom_mut(&mut self) -> Option<&mut T> { - self.custom.as_mut() - } - - /// Sets the value of the custom JWT claims. - pub fn set_custom(&mut self, value: impl Into) { - self.custom = Some(value.into()); - } -} diff --git a/libjose/src/jwt/header.rs b/libjose/src/jwt/header.rs deleted file mode 100644 index 203b8a9fe8..0000000000 --- a/libjose/src/jwt/header.rs +++ /dev/null @@ -1,280 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use url::Url; - -use crate::jose::JoseHeader; -use crate::jwk::Jwk; -use crate::lib::*; - -/// JSON Web Token JOSE Header. -/// -/// [More Info (JWS)](https://tools.ietf.org/html/rfc7515#section-4) -/// [More Info (JWE)](https://tools.ietf.org/html/rfc7516#section-4) -#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] -pub struct JwtHeader { - /// JWK Set URL. - /// - /// Refers to a resource for a set of JSON-encoded public keys, one of which - /// corresponds to the key used to digitally sign the JWS. - /// - /// [More Info](https://tools.ietf.org/html/rfc7515#section-4.1.2) - #[serde(skip_serializing_if = "Option::is_none")] - jku: Option, - /// JSON Web Key. - /// - /// The public key that corresponds to the key used to digitally sign the JWS. - /// - /// [More Info](https://tools.ietf.org/html/rfc7515#section-4.1.3) - #[serde(skip_serializing_if = "Option::is_none")] - jwk: Option, - /// Key ID. - /// - /// A hint indicating which key was used to secure the JWS. - /// - /// [More Info](https://tools.ietf.org/html/rfc7515#section-4.1.4) - #[serde(skip_serializing_if = "Option::is_none")] - kid: Option, - /// X.509 URL. - /// - /// A URI that refers to a resource for the X.509 public key certificate or - /// certificate chain corresponding to the key used to digitally sign the JWS. - /// - /// [More Info](https://tools.ietf.org/html/rfc7515#section-4.1.5) - #[serde(skip_serializing_if = "Option::is_none")] - x5u: Option, - /// X.509 Certificate Chain. - /// - /// Contains the X.509 public key certificate or certificate chain - /// corresponding to the key used to digitally sign the JWS. - /// - /// [More Info](https://tools.ietf.org/html/rfc7515#section-4.1.6) - #[serde(skip_serializing_if = "Option::is_none")] - x5c: Option>, - /// X.509 Certificate SHA-1 Thumbprint. - /// - /// A base64url-encoded SHA-1 thumbprint of the DER encoding of the X.509 - /// certificate corresponding to the key used to digitally sign the JWS. - /// - /// [More Info](https://tools.ietf.org/html/rfc7515#section-4.1.7) - #[serde(skip_serializing_if = "Option::is_none")] - x5t: Option, - /// X.509 Certificate SHA-256 Thumbprint. - /// - /// A base64url-encoded SHA-256 thumbprint of the DER encoding of the X.509 - /// certificate corresponding to the key used to digitally sign the JWS. - /// - /// [More Info](https://tools.ietf.org/html/rfc7515#section-4.1.8) - #[serde(rename = "x5t#S256", skip_serializing_if = "Option::is_none")] - x5t_s256: Option, - /// Type. - /// - /// Used by JWS applications to declare the media type of this complete JWS. - /// - /// [More Info](https://tools.ietf.org/html/rfc7515#section-4.1.9) - #[serde(skip_serializing_if = "Option::is_none")] - typ: Option, - /// Content Type. - /// - /// Used by JWS applications to declare the media type of the secured content. - /// - /// [More Info](https://tools.ietf.org/html/rfc7515#section-4.1.10) - #[serde(skip_serializing_if = "Option::is_none")] - cty: Option, - /// Critical. - /// - /// Indicates that JWS extensions are being used that MUST be understood and - /// processed. - /// - /// [More Info](https://tools.ietf.org/html/rfc7515#section-4.1.11) - #[serde(skip_serializing_if = "Option::is_none")] - crit: Option>, - /// URL. - /// - /// Specifies the URL to which this JWS object is directed. - /// - /// [More Info](https://tools.ietf.org/html/rfc8555#section-6.4.1) - #[serde(skip_serializing_if = "Option::is_none")] - url: Option, - /// Nonce. - /// - /// Provides a unique value that enables the verifier of a JWS to recognize - /// when replay has occurred. - /// - /// [More Info](https://tools.ietf.org/html/rfc8555#section-6.5.2) - #[serde(skip_serializing_if = "Option::is_none")] - nonce: Option, -} - -impl JwtHeader { - /// Create a new `JwtHeader`. - pub const fn new() -> Self { - Self { - jku: None, - jwk: None, - kid: None, - x5u: None, - x5c: None, - x5t: None, - x5t_s256: None, - typ: None, - cty: None, - crit: None, - url: None, - nonce: None, - } - } - - /// Returns the value of the JWK Set URL claim (jku). - pub fn jku(&self) -> Option<&Url> { - self.jku.as_ref() - } - - /// Sets a value for the JWK Set URL claim (jku). - pub fn set_jku(&mut self, value: impl Into) { - self.jku = Some(value.into()); - } - - /// Returns the value of the JWK claim (jwk). - pub fn jwk(&self) -> Option<&Jwk> { - self.jwk.as_ref() - } - - /// Sets a value for the JWK claim (jwk). - pub fn set_jwk(&mut self, value: impl Into) { - self.jwk = Some(value.into()); - } - - /// Returns the value of the key ID claim (kid). - pub fn kid(&self) -> Option<&str> { - self.kid.as_deref() - } - - /// Sets a value for the key ID claim (kid). - pub fn set_kid(&mut self, value: impl Into) { - self.kid = Some(value.into()); - } - - /// Returns the value of the X.509 URL claim (x5u). - pub fn x5u(&self) -> Option<&Url> { - self.x5u.as_ref() - } - - /// Sets a value for the X.509 URL claim (x5u). - pub fn set_x5u(&mut self, value: impl Into) { - self.x5u = Some(value.into()); - } - - /// Returns the value of the X.509 certificate chain claim (x5c). - pub fn x5c(&self) -> Option<&[String]> { - self.x5c.as_deref() - } - - /// Sets values for the X.509 certificate chain claim (x5c). - pub fn set_x5c(&mut self, value: impl IntoIterator>) { - self.x5c = Some(value.into_iter().map(Into::into).collect()); - } - - /// Returns the value of the X.509 certificate SHA-1 thumbprint claim (x5t). - pub fn x5t(&self) -> Option<&str> { - self.x5t.as_deref() - } - - /// Sets a value for the X.509 certificate SHA-1 thumbprint claim (x5t). - pub fn set_x5t(&mut self, value: impl Into) { - self.x5t = Some(value.into()); - } - - /// Returns the value of the X.509 certificate SHA-256 thumbprint claim - /// (x5t#S256). - pub fn x5t_s256(&self) -> Option<&str> { - self.x5t_s256.as_deref() - } - - /// Sets a value for the X.509 certificate SHA-256 thumbprint claim - /// (x5t#S256). - pub fn set_x5t_s256(&mut self, value: impl Into) { - self.x5t_s256 = Some(value.into()); - } - - /// Returns the value of the token type claim (typ). - pub fn typ(&self) -> Option<&str> { - self.typ.as_deref() - } - - /// Sets a value for the token type claim (typ). - pub fn set_typ(&mut self, value: impl Into) { - self.typ = Some(value.into()); - } - - /// Returns the value of the content type claim (cty). - pub fn cty(&self) -> Option<&str> { - self.cty.as_deref() - } - - /// Sets a value for the content type claim (cty). - pub fn set_cty(&mut self, value: impl Into) { - self.cty = Some(value.into()); - } - - /// Returns the value of the critical claim (crit). - pub fn crit(&self) -> Option<&[String]> { - self.crit.as_deref() - } - - /// Sets values for the critical claim (crit). - pub fn set_crit(&mut self, value: impl IntoIterator>) { - self.crit = Some(value.into_iter().map(Into::into).collect()); - } - - /// Returns the value of the url claim (url). - pub fn url(&self) -> Option<&Url> { - self.url.as_ref() - } - - /// Sets a value for the url claim (url). - pub fn set_url(&mut self, value: impl Into) { - self.url = Some(value.into()); - } - - /// Returns the value of the nonce claim (nonce). - pub fn nonce(&self) -> Option<&str> { - self.nonce.as_deref() - } - - /// Sets a value for the nonce claim (nonce). - pub fn set_nonce(&mut self, value: impl Into) { - self.nonce = Some(value.into()); - } - - // =========================================================================== - // =========================================================================== - - pub fn has(&self, claim: &str) -> bool { - match claim { - "jku" => self.jku().is_some(), - "jwk" => self.jwk().is_some(), - "kid" => self.kid().is_some(), - "x5u" => self.x5u().is_some(), - "x5c" => self.x5c().is_some(), - "x5t" => self.x5t().is_some(), - "x5t#S256" => self.x5t_s256().is_some(), - "typ" => self.typ().is_some(), - "cty" => self.cty().is_some(), - "crit" => self.crit().is_some(), - "url" => self.url().is_some(), - "nonce" => self.nonce().is_some(), - _ => false, - } - } -} - -impl JoseHeader for JwtHeader { - fn common(&self) -> &JwtHeader { - self - } - - fn has_claim(&self, claim: &str) -> bool { - self.has(claim) - } -} diff --git a/libjose/src/jwt/header_set.rs b/libjose/src/jwt/header_set.rs deleted file mode 100644 index b7f4f29e77..0000000000 --- a/libjose/src/jwt/header_set.rs +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use core::ops::Deref; -use url::Url; - -use crate::error::Error; -use crate::error::Result; -use crate::jwe::JweAlgorithm; -use crate::jwe::JweCompression; -use crate::jwe::JweEncryption; -use crate::jwe::JweHeader; -use crate::jwk::Jwk; -use crate::jws::JwsAlgorithm; -use crate::jws::JwsHeader; -use crate::jwt::JwtHeader; -use crate::lib::*; - -macro_rules! accessor { - ($claim:ident, $this:expr) => {{ - accessor!(@impl, and_then, $claim, $this.header, $this.unprotected, $this.protected) - }}; - (@unwrapped, $claim:ident, $this:expr) => {{ - accessor!(@impl, map, $claim, $this.header, $this.unprotected, $this.protected) - }}; - (@protected, $claim:ident, $this:expr) => {{ - accessor!(@impl, and_then, $claim, $this.protected, $this.header, $this.unprotected) - }}; - (@impl, $next:ident, $claim:ident, $a:expr, $b:expr, $c:expr) => {{ - $a - .$next(|header| header.$claim()) - .or_else(|| $b.$next(|header| header.$claim())) - .or_else(|| $c.$next(|header| header.$claim())) - }}; -} - -macro_rules! impl_accessors { - ($fn:ident, $try_fn:ident, $ty:ty) => { - pub fn $fn(&self) -> Option<$ty> { - accessor!($fn, self) - } - - pub fn $try_fn(&self) -> Result<$ty> { - self.$fn().ok_or(Error::MissingParam(stringify!($fn))) - } - }; - (@unwrapped, $fn:ident, $try_fn:ident, $ty:ty) => { - pub fn $fn(&self) -> Option<$ty> { - accessor!(@unwrapped, $fn, self) - } - - pub fn $try_fn(&self) -> Result<$ty> { - self.$fn().ok_or(Error::MissingParam(stringify!($fn))) - } - }; - (@protected, $fn:ident, $try_fn:ident, $ty:ty) => { - pub fn $fn(&self) -> Option<$ty> { - accessor!(@protected, $fn, self) - } - - pub fn $try_fn(&self) -> Result<$ty> { - self.$fn().ok_or(Error::MissingParam(stringify!($fn))) - } - }; -} - -#[derive(Debug)] -pub struct JwtHeaderSet<'a, T> { - protected: Option<&'a T>, - unprotected: Option<&'a T>, - header: Option<&'a T>, -} - -impl<'a, T> JwtHeaderSet<'a, T> { - pub const fn new() -> Self { - Self { - protected: None, - unprotected: None, - header: None, - } - } - - pub fn protected(mut self, value: impl Into>) -> Self { - self.protected = value.into(); - self - } - - pub fn unprotected(mut self, value: impl Into>) -> Self { - self.unprotected = value.into(); - self - } - - pub fn header(mut self, value: impl Into>) -> Self { - self.header = value.into(); - self - } -} - -#[rustfmt::skip] -impl<'a, T: 'a> JwtHeaderSet<'a, T> -where - T: Deref, -{ - impl_accessors!(jku, try_jku, &Url); - impl_accessors!(jwk, try_jwk, &Jwk); - impl_accessors!(kid, try_kid, &str); - impl_accessors!(x5u, try_x5u, &Url); - impl_accessors!(x5c, try_x5c, &[String]); - impl_accessors!(x5t, try_x5t, &str); - impl_accessors!(x5t_s256, try_x5t_s256, &str); - impl_accessors!(typ, try_typ, &str); - impl_accessors!(cty, try_cty, &str); - impl_accessors!(crit, try_crit, &[String]); - impl_accessors!(url, try_url, &Url); - impl_accessors!(nonce, try_nonce, &str); -} - -#[rustfmt::skip] -impl<'a> JwtHeaderSet<'a, JwsHeader> { - impl_accessors!(@unwrapped, alg, try_alg, JwsAlgorithm); - impl_accessors!(@protected, b64, try_b64, bool); - impl_accessors!(@protected, ppt, try_ppt, &str); -} - -#[rustfmt::skip] -impl<'a> JwtHeaderSet<'a, JweHeader> { - impl_accessors!(@unwrapped, alg, try_alg, JweAlgorithm); - impl_accessors!(@unwrapped, enc, try_enc, JweEncryption); - impl_accessors!(zip, try_zip, JweCompression); - impl_accessors!(epk, try_epk, &Jwk); - impl_accessors!(@protected, apu, try_apu, &str); - impl_accessors!(@protected, apv, try_apv, &str); - impl_accessors!(@protected, iv, try_iv, &str); - impl_accessors!(@protected, tag, try_tag, &str); - impl_accessors!(@protected, p2s, try_p2s, &str); - impl_accessors!(@protected, p2c, try_p2c, u64); -} - -impl<'a, T: 'a> Default for JwtHeaderSet<'a, T> { - fn default() -> Self { - Self::new() - } -} diff --git a/libjose/src/jwt/mod.rs b/libjose/src/jwt/mod.rs deleted file mode 100644 index 52576a2a9d..0000000000 --- a/libjose/src/jwt/mod.rs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! JSON Web Tokens ([JWT](https://tools.ietf.org/html/rfc7519)) - -mod claims; -mod header; -mod header_set; -mod profile; - -pub use self::claims::*; -pub use self::header::*; -pub use self::header_set::*; -pub use self::profile::*; diff --git a/libjose/src/jwt/profile/core.rs b/libjose/src/jwt/profile/core.rs deleted file mode 100644 index 1543486977..0000000000 --- a/libjose/src/jwt/profile/core.rs +++ /dev/null @@ -1,261 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use core::time::Duration; - -#[cfg(feature = "std")] -use std::time::SystemTime; -#[cfg(not(feature = "std"))] -type SystemTime = (); - -use crate::error::Error; -use crate::error::Result; -use crate::jwt::JwtClaims; -use crate::lib::*; - -#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] -pub enum Validation { - /// Indicates that the claim/property is NOT required. - Optional, - /// Indicates that the claim/property is required; the value is NOT validated. - Required, - /// Indicates that the claim/property is required AND the value must match the - /// given string. - Matching(String), -} - -impl From for Validation { - fn from(other: String) -> Self { - Self::Matching(other) - } -} - -#[derive(Clone, Debug)] -pub struct CoreProfile { - rule_iss: Validation, - rule_sub: Validation, - rule_aud: Validation, - rule_jti: Validation, - rule_exp: Validation, - rule_nbf: Validation, - rule_iat: Validation, - timecop: Option, -} - -impl CoreProfile { - /// Creates a new `CoreProfile` validator. - pub const fn new() -> Self { - Self { - rule_iss: Validation::Optional, - rule_sub: Validation::Optional, - rule_aud: Validation::Optional, - rule_jti: Validation::Optional, - rule_exp: Validation::Optional, - rule_nbf: Validation::Optional, - rule_iat: Validation::Optional, - timecop: None, - } - } - - /// Sets validation rules for the issuer claim (iss). - pub fn set_iss(&mut self, value: impl Into) { - self.rule_iss = value.into(); - } - - /// Sets validation rules for the subject claim (sub). - pub fn set_sub(&mut self, value: impl Into) { - self.rule_sub = value.into(); - } - - /// Sets validation rules for the audience claim (aud). - pub fn set_aud(&mut self, value: impl Into) { - self.rule_aud = value.into(); - } - - /// Sets validation rules for the token ID claim (jti). - pub fn set_jti(&mut self, value: impl Into) { - self.rule_jti = value.into(); - } - - /// Sets validation rules for the expiration claim (exp). - pub fn set_exp(&mut self, value: impl Into) { - self.rule_exp = value.into(); - } - - /// Sets validation rules for the not-before claim (nbf). - pub fn set_nbf(&mut self, value: impl Into) { - self.rule_nbf = value.into(); - } - - /// Sets validation rules for the issued-at claim (iat). - pub fn set_iat(&mut self, value: impl Into) { - self.rule_iat = value.into(); - } - - /// Sets options for timestamp validation. - pub fn set_timecop(&mut self, value: impl Into) { - self.timecop = Some(value.into()); - } - - /// Validates the given claims with the current rule configuration. - pub fn validate(&self, claims: &JwtClaims) -> Result<()> { - // Validate registered claims with the current rules. - self.validate_aud(claims)?; - self.validate_iss(claims)?; - self.validate_jti(claims)?; - self.validate_sub(claims)?; - - #[cfg(feature = "std")] - { - // Check expiration/issuance time/etc. - self.validate_timestamps(claims)?; - } - - Ok(()) - } - - // Validates the audience (aud) claim value. - fn validate_aud(&self, claims: &JwtClaims) -> Result<()> { - match (&self.rule_aud, claims.aud()) { - (Validation::Optional, _) => Ok(()), - (Validation::Required, Some(_)) => Ok(()), - (Validation::Matching(expected), Some(aud)) if aud.contains(expected) => Ok(()), - (Validation::Required, _) => Err(Error::MissingClaim("aud")), - (Validation::Matching(_), _) => Err(Error::InvalidClaim("aud")), - } - } - - // Validates the issuer (iss) claim value. - fn validate_iss(&self, claims: &JwtClaims) -> Result<()> { - match (&self.rule_iss, claims.iss()) { - (Validation::Optional, _) => Ok(()), - (Validation::Required, Some(_)) => Ok(()), - (Validation::Matching(expected), Some(iss)) if iss == expected => Ok(()), - (Validation::Required, _) => Err(Error::MissingClaim("iss")), - (Validation::Matching(_), _) => Err(Error::InvalidClaim("iss")), - } - } - - // Validates the JWT ID (jti) claim value. - fn validate_jti(&self, claims: &JwtClaims) -> Result<()> { - match (&self.rule_jti, claims.jti()) { - (Validation::Optional, _) => Ok(()), - (Validation::Required, Some(_)) => Ok(()), - (Validation::Matching(expected), Some(jti)) if jti == expected => Ok(()), - (Validation::Required, _) => Err(Error::MissingClaim("jti")), - (Validation::Matching(_), _) => Err(Error::InvalidClaim("jti")), - } - } - - // Validates the subject (sub) claim value. - fn validate_sub(&self, claims: &JwtClaims) -> Result<()> { - match (&self.rule_sub, claims.sub()) { - (Validation::Optional, _) => Ok(()), - (Validation::Required, Some(_)) => Ok(()), - (Validation::Matching(expected), Some(sub)) if sub == expected => Ok(()), - (Validation::Required, _) => Err(Error::MissingClaim("sub")), - (Validation::Matching(_), _) => Err(Error::InvalidClaim("sub")), - } - } - - // Validates the registered timestamp claims (exp, nbf, iat) - #[cfg(feature = "std")] - fn validate_timestamps(&self, claims: &JwtClaims) -> Result<()> { - fn timestamp(value: i64) -> Duration { - use core::convert::TryFrom as _; - u64::try_from(value).map(Duration::from_secs).unwrap_or_default() - } - - let timecop: TimeCop = self.timecop.unwrap_or_else(TimeCop::new); - let current: Duration = timecop.resolve_current(); - let min_iat: Duration = timecop.min_iat.unwrap_or_default(); - let max_iat: Duration = timecop.max_iat.unwrap_or(current); - - // Check the expiration claim and ensure the token hasn't expired - match (&self.rule_exp, claims.exp()) { - (Validation::Optional, _) => {} - (Validation::Matching(_), _) => {} - (Validation::Required, Some(exp)) if timestamp(exp) <= current => { - return Err(Error::InvalidClaim("exp")); - } - (Validation::Required, Some(_)) => {} - (Validation::Required, None) => { - return Err(Error::MissingClaim("exp")); - } - } - - // Check the "not before" claim and ensure the token isn't used before intended - match (&self.rule_nbf, claims.nbf()) { - (Validation::Optional, _) => {} - (Validation::Matching(_), _) => {} - (Validation::Required, Some(nbf)) if timestamp(nbf) > current => { - return Err(Error::InvalidClaim("exp")); - } - (Validation::Required, Some(_)) => {} - (Validation::Required, None) => { - return Err(Error::MissingClaim("nbf")); - } - } - - // Ensure the token was issued within the appropriate issuance period - match (&self.rule_iat, claims.iat()) { - (Validation::Optional, _) => {} - (Validation::Matching(_), _) => {} - (Validation::Required, Some(iat)) if timestamp(iat) < min_iat => { - return Err(Error::InvalidClaim("iat")); - } - (Validation::Required, Some(iat)) if timestamp(iat) > max_iat => { - return Err(Error::InvalidClaim("iat")); - } - (Validation::Required, Some(_)) => {} - (Validation::Required, None) => { - return Err(Error::MissingClaim("iat")); - } - } - - Ok(()) - } -} - -/// Validation options for time-related claims and properties. -#[derive(Clone, Copy, Debug)] -pub struct TimeCop { - /// A specific time for temporal validations. Defaults to `SystemTime::now()`. - current: Option, - /// The maximum allowed time of the issued-at claim (iat). - max_iat: Option, - /// The minimum allowed time of the issued-at claim (iat). - min_iat: Option, -} - -impl TimeCop { - /// Creates a new `TimeCop`. - pub const fn new() -> Self { - Self { - current: None, - max_iat: None, - min_iat: None, - } - } - - pub fn set_current(&mut self, value: SystemTime) { - self.current = Some(value); - } - - pub fn set_max_iat(&mut self, value: impl Into) { - self.max_iat = Some(value.into()); - } - - pub fn set_min_iat(&mut self, value: impl Into) { - self.min_iat = Some(value.into()); - } - - #[cfg(feature = "std")] - fn resolve_current(&self) -> Duration { - self - .current - .unwrap_or_else(SystemTime::now) - .duration_since(SystemTime::UNIX_EPOCH) - .expect("Epoch Fail") - } -} diff --git a/libjose/src/jwt/profile/mod.rs b/libjose/src/jwt/profile/mod.rs deleted file mode 100644 index 1aa404a045..0000000000 --- a/libjose/src/jwt/profile/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -mod core; - -pub use self::core::*; diff --git a/libjose/src/lib.rs b/libjose/src/lib.rs deleted file mode 100644 index 7aef65d2de..0000000000 --- a/libjose/src/lib.rs +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! # libjose -//! -//! A library for JSON Object Signing and Encryption (JOSE). -//! -//! ## References -//! - [IANA JOSE Registry](https://www.iana.org/assignments/jose/jose.xhtml) -//! - [JWT Handbook](https://auth0.com/e-books/jwt-handbook) -//! -//! ### RFCs -//! - [JSON Web Algorithms](https://tools.ietf.org/html/rfc7518) -//! - [JSON Web Encryption](https://tools.ietf.org/html/rfc7516) -//! - [JSON Web Key](https://tools.ietf.org/html/rfc7517) -//! - [JSON Web Key Thumbprint](https://tools.ietf.org/html/rfc7638) -//! - [JSON Web Signature](https://tools.ietf.org/html/rfc7515) -//! - [JSON Web Signature Unencoded Payload Option](https://tools.ietf.org/html/rfc7797) -//! - [JSON Web Token](https://tools.ietf.org/html/rfc7519) -//! - [CFRG Elliptic Curve ECDH and Signatures](https://tools.ietf.org/html/rfc8037) -//! - [JOSE Registrations for Web Authentication Algorithms](https://tools.ietf.org/html/rfc8812) -//! - [Chacha derived AEAD algorithms in JOSE](https://tools.ietf.org/html/draft-amringer-jose-chacha-02) -//! - [JSON Web Token Best Current Practices](https://tools.ietf.org/html/rfc8725) -//! - [Public Key Authenticated Encryption for JOSE: ECDH-1PU](https://tools.ietf.org/html/draft-madden-jose-ecdh-1pu-03) -#![cfg_attr(not(feature = "std"), no_std)] -#![allow(clippy::upper_case_acronyms)] -#![warn( - rust_2018_idioms, - // unreachable_pub, - // missing_docs, - rustdoc::missing_crate_level_docs, - rustdoc::broken_intra_doc_links, - rustdoc::private_intra_doc_links, - rustdoc::private_doc_tests, - clippy::missing_safety_doc, - // clippy::missing_errors_doc, -)] - -#[cfg(not(feature = "alloc"))] -compile_error!("This crate does not yet support environments without liballoc."); - -#[cfg(all(feature = "alloc", not(feature = "std")))] -extern crate alloc; -#[cfg(feature = "std")] -extern crate std; - -#[macro_use] -extern crate serde; - -#[macro_use] -mod macros; - -pub mod error; -pub mod jose; -pub mod jwe; -pub mod jwk; -pub mod jwm; -pub mod jws; -pub mod jwt; -#[doc(hidden)] -pub mod utils; - -pub use self::error::Error; -pub use self::error::Result; - -mod lib { - #[cfg(all(feature = "alloc", not(feature = "std")))] - pub(crate) use alloc::format; - #[cfg(feature = "std")] - pub(crate) use std::format; - - #[cfg(all(feature = "alloc", not(feature = "std")))] - pub(crate) use alloc::vec; - #[cfg(feature = "std")] - pub(crate) use std::vec; - - #[cfg(all(feature = "alloc", not(feature = "std")))] - pub(crate) use alloc::borrow::Cow; - #[cfg(feature = "std")] - pub(crate) use std::borrow::Cow; - - #[rustfmt::skip] - #[cfg(all(feature = "alloc", not(feature = "std")))] - pub(crate) use alloc::string::{String, ToString}; - #[rustfmt::skip] - #[cfg(feature = "std")] - pub(crate) use std::string::{String, ToString}; - - #[cfg(all(feature = "alloc", not(feature = "std")))] - pub(crate) use alloc::vec::Vec; - #[cfg(feature = "std")] - pub(crate) use std::vec::Vec; -} diff --git a/libjose/src/macros.rs b/libjose/src/macros.rs deleted file mode 100644 index 2916c9877c..0000000000 --- a/libjose/src/macros.rs +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -#[macro_export] -#[doc(hidden)] -macro_rules! rsa_padding { - (@PKCS1_SHA256) => { - ::rsa::PaddingScheme::new_pkcs1v15_sign(Some(::rsa::Hash::SHA2_256)) - }; - (@PKCS1_SHA384) => { - ::rsa::PaddingScheme::new_pkcs1v15_sign(Some(::rsa::Hash::SHA2_384)) - }; - (@PKCS1_SHA512) => { - ::rsa::PaddingScheme::new_pkcs1v15_sign(Some(::rsa::Hash::SHA2_512)) - }; - (@PSS_SHA256) => { - ::rsa::PaddingScheme::new_pss::<::crypto::hashes::sha::Sha256, _>(::rand::rngs::OsRng) - }; - (@PSS_SHA384) => { - ::rsa::PaddingScheme::new_pss::<::crypto::hashes::sha::Sha384, _>(::rand::rngs::OsRng) - }; - (@PSS_SHA512) => { - ::rsa::PaddingScheme::new_pss::<::crypto::hashes::sha::Sha512, _>(::rand::rngs::OsRng) - }; - (@RSA1_5) => { - ::rsa::PaddingScheme::new_pkcs1v15_encrypt() - }; - (@RSA_OAEP) => { - ::rsa::PaddingScheme::new_oaep::<::sha1::Sha1>() - }; - (@RSA_OAEP_256) => { - ::rsa::PaddingScheme::new_oaep::<::crypto::hashes::sha::Sha256>() - }; - (@RSA_OAEP_384) => { - ::rsa::PaddingScheme::new_oaep::<::crypto::hashes::sha::Sha384>() - }; - (@RSA_OAEP_512) => { - ::rsa::PaddingScheme::new_oaep::<::crypto::hashes::sha::Sha512>() - }; -} diff --git a/libjose/src/utils/base64.rs b/libjose/src/utils/base64.rs deleted file mode 100644 index 04b9f87c76..0000000000 --- a/libjose/src/utils/base64.rs +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use serde::de::DeserializeOwned; -use serde::Serialize; -use serde_json::from_slice; -use serde_json::to_vec; - -use crate::error::Result; -use crate::lib::*; - -pub fn encode_b64(data: impl AsRef<[u8]>) -> String { - base64::encode_config(data.as_ref(), base64::URL_SAFE_NO_PAD) -} - -pub fn encode_b64_into(data: impl AsRef<[u8]>, buffer: &mut String) { - base64::encode_config_buf(data.as_ref(), base64::URL_SAFE_NO_PAD, buffer) -} - -pub fn decode_b64(data: impl AsRef<[u8]>) -> Result> { - base64::decode_config(data.as_ref(), base64::URL_SAFE_NO_PAD).map_err(Into::into) -} - -pub fn decode_b64_into(data: impl AsRef<[u8]>, buffer: &mut Vec) -> Result<()> { - base64::decode_config_buf(data.as_ref(), base64::URL_SAFE_NO_PAD, buffer).map_err(Into::into) -} - -pub fn encode_b64_json(data: &T) -> Result -where - T: Serialize, -{ - to_vec(data).map(encode_b64).map_err(Into::into) -} - -pub fn decode_b64_json(data: impl AsRef<[u8]>) -> Result -where - T: DeserializeOwned, -{ - decode_b64(data).and_then(|data| from_slice(&data).map_err(Into::into)) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn smoke() { - assert!(decode_b64(encode_b64(b"libjose")).is_ok()); - } -} diff --git a/libjose/src/utils/crypto/concat_kdf.rs b/libjose/src/utils/crypto/concat_kdf.rs deleted file mode 100644 index c4b1e73e6b..0000000000 --- a/libjose/src/utils/crypto/concat_kdf.rs +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use core::convert::TryFrom; -use crypto::hashes::sha::Sha256; -use crypto::hashes::Digest; - -use crate::error::Error; -use crate::error::Result; -use crate::lib::*; - -/// The Concat KDF (using SHA-256) as defined in Section 5.8.1 of NIST.800-56A -pub fn concat_kdf(alg: &str, len: usize, z: &[u8], apu: &[u8], apv: &[u8]) -> Result> { - let mut digest: Sha256 = Sha256::new(); - let mut output: Vec = Vec::new(); - - let target: usize = (len + (Sha256::output_size() - 1)) / Sha256::output_size(); - let rounds: u32 = u32::try_from(target).map_err(|_| Error::KeyError("Iteration Overflow"))?; - - for count in 0..rounds { - // Iteration Count - digest.update((count + 1).to_be_bytes()); - - // Derived Secret - digest.update(z); - - // AlgorithmId - digest.update((alg.len() as u32).to_be_bytes()); - digest.update(alg.as_bytes()); - - // PartyUInfo - digest.update((apu.len() as u32).to_be_bytes()); - digest.update(apu); - - // PartyVInfo - digest.update((apv.len() as u32).to_be_bytes()); - digest.update(apv); - - // Shared Key Length - digest.update(((len * 8) as u32).to_be_bytes()); - - output.extend_from_slice(&digest.finalize_reset()); - } - - output.truncate(len); - - Ok(output) -} diff --git a/libjose/src/utils/crypto/diffie_hellman.rs b/libjose/src/utils/crypto/diffie_hellman.rs deleted file mode 100644 index 7e78c70da9..0000000000 --- a/libjose/src/utils/crypto/diffie_hellman.rs +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use crate::error::Error; -use crate::error::Result; -use crate::jwk::EcCurve; -use crate::jwk::EcdhCurve; -use crate::jwk::EcxCurve; -use crate::lib::*; -use crate::utils::Secret; - -pub fn diffie_hellman<'a, 'b>( - curve: impl Into, - public: impl Into>, - secret: impl Into>, -) -> Result> { - let public: Secret<'a> = public.into(); - let secret: Secret<'b> = secret.into(); - - match curve.into() { - EcdhCurve::Ec(curve) => match curve { - EcCurve::P256 => { - let public: _ = public.to_p256_public()?; - let secret: _ = secret.to_p256_secret()?; - let shared: _ = secret.diffie_hellman(&public); - - Ok(shared.as_bytes().to_vec()) - } - EcCurve::P384 => Err(Error::AlgError("Diffie-Hellman (P384)")), - EcCurve::P521 => Err(Error::AlgError("Diffie-Hellman (P521)")), - EcCurve::Secp256K1 => { - let public: _ = public.to_k256_public()?; - let secret: _ = secret.to_k256_secret()?; - let shared: _ = secret.diffie_hellman(&public); - - Ok(shared.as_bytes().to_vec()) - } - }, - EcdhCurve::Ecx(curve) => match curve { - EcxCurve::X25519 => { - let public: _ = public.to_x25519_public()?; - let secret: _ = secret.to_x25519_secret()?; - let shared: _ = secret.diffie_hellman(&public); - - Ok(shared.as_bytes().to_vec()) - } - EcxCurve::X448 => { - let public: _ = public.to_x448_public()?; - let secret: _ = secret.to_x448_secret()?; - let shared: _ = secret.diffie_hellman(&public); - - Ok(shared.as_bytes().to_vec()) - } - }, - } -} diff --git a/libjose/src/utils/crypto/key_params.rs b/libjose/src/utils/crypto/key_params.rs deleted file mode 100644 index e4bcaa7b03..0000000000 --- a/libjose/src/utils/crypto/key_params.rs +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use crypto::ciphers::aes::Aes128Gcm; -use crypto::ciphers::aes::Aes192Gcm; -use crypto::ciphers::aes::Aes256Gcm; -use crypto::ciphers::chacha::ChaCha20Poly1305; -use crypto::ciphers::chacha::XChaCha20Poly1305; -use crypto::ciphers::traits::Aead; -use crypto::hashes::sha::SHA256_LEN; -use crypto::hashes::sha::SHA384_LEN; -use crypto::hashes::sha::SHA512_LEN; - -use crate::jwe::JweAlgorithm; -use crate::jwe::JweEncryption; -use crate::jwk::EcCurve; -use crate::jwk::EcdhCurve; -use crate::jwk::EcxCurve; -use crate::jwk::EdCurve; -use crate::jws::JwsAlgorithm; - -/// Supported sizes for RSA key generation. -#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] -pub enum RsaBits { - B2048 = 2048, - B3072 = 3072, - B4096 = 4096, -} - -impl RsaBits { - pub const fn bits(self) -> usize { - match self { - Self::B2048 => 2048, - Self::B3072 => 3072, - Self::B4096 => 4096, - } - } -} - -#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] -pub enum KeyParams { - None, - Oct(usize), - Rsa(RsaBits), - Ec(EcCurve), - Ecx(EcxCurve), - Ed(EdCurve), -} - -impl From for KeyParams { - fn from(other: usize) -> Self { - Self::Oct(other) - } -} - -impl From for KeyParams { - fn from(other: RsaBits) -> Self { - Self::Rsa(other) - } -} - -impl From for KeyParams { - fn from(other: EcCurve) -> Self { - Self::Ec(other) - } -} - -impl From for KeyParams { - fn from(other: EcxCurve) -> Self { - Self::Ecx(other) - } -} - -impl From for KeyParams { - fn from(other: EdCurve) -> Self { - Self::Ed(other) - } -} - -impl From for KeyParams { - fn from(other: EcdhCurve) -> Self { - match other { - EcdhCurve::Ec(curve) => Self::Ec(curve), - EcdhCurve::Ecx(curve) => Self::Ecx(curve), - } - } -} - -impl From for KeyParams { - fn from(other: JwsAlgorithm) -> Self { - match other { - JwsAlgorithm::HS256 => Self::Oct(SHA256_LEN), - JwsAlgorithm::HS384 => Self::Oct(SHA384_LEN), - JwsAlgorithm::HS512 => Self::Oct(SHA512_LEN), - JwsAlgorithm::RS256 => Self::Rsa(RsaBits::B2048), - JwsAlgorithm::RS384 => Self::Rsa(RsaBits::B2048), - JwsAlgorithm::RS512 => Self::Rsa(RsaBits::B2048), - JwsAlgorithm::PS256 => Self::Rsa(RsaBits::B2048), - JwsAlgorithm::PS384 => Self::Rsa(RsaBits::B2048), - JwsAlgorithm::PS512 => Self::Rsa(RsaBits::B2048), - JwsAlgorithm::ES256 => Self::Ec(EcCurve::P256), - JwsAlgorithm::ES384 => Self::Ec(EcCurve::P384), - JwsAlgorithm::ES512 => Self::Ec(EcCurve::P521), - JwsAlgorithm::ES256K => Self::Ec(EcCurve::Secp256K1), - JwsAlgorithm::NONE => Self::None, - JwsAlgorithm::EdDSA => Self::Ed(EdCurve::Ed25519), - } - } -} - -impl From<(JweAlgorithm, JweEncryption)> for KeyParams { - fn from(other: (JweAlgorithm, JweEncryption)) -> Self { - match other { - (JweAlgorithm::RSA1_5, _) => Self::Rsa(RsaBits::B2048), - (JweAlgorithm::RSA_OAEP, _) => Self::Rsa(RsaBits::B2048), - (JweAlgorithm::RSA_OAEP_256, _) => Self::Rsa(RsaBits::B2048), - (JweAlgorithm::RSA_OAEP_384, _) => Self::Rsa(RsaBits::B2048), - (JweAlgorithm::RSA_OAEP_512, _) => Self::Rsa(RsaBits::B2048), - (JweAlgorithm::A128KW, _) => Self::Oct(Aes128Gcm::KEY_LENGTH), - (JweAlgorithm::A192KW, _) => Self::Oct(Aes192Gcm::KEY_LENGTH), - (JweAlgorithm::A256KW, _) => Self::Oct(Aes256Gcm::KEY_LENGTH), - (JweAlgorithm::DIR, encryption) => Self::Oct(encryption.key_len()), - (JweAlgorithm::ECDH_ES, _) => Self::Ecx(EcxCurve::X25519), - (JweAlgorithm::ECDH_ES_A128KW, _) => Self::Ecx(EcxCurve::X25519), - (JweAlgorithm::ECDH_ES_A192KW, _) => Self::Ecx(EcxCurve::X25519), - (JweAlgorithm::ECDH_ES_A256KW, _) => Self::Ecx(EcxCurve::X25519), - (JweAlgorithm::ECDH_ES_C20PKW, _) => Self::Ecx(EcxCurve::X25519), - (JweAlgorithm::ECDH_ES_XC20PKW, _) => Self::Ecx(EcxCurve::X25519), - (JweAlgorithm::A128GCMKW, _) => Self::Oct(Aes128Gcm::KEY_LENGTH), - (JweAlgorithm::A192GCMKW, _) => Self::Oct(Aes192Gcm::KEY_LENGTH), - (JweAlgorithm::A256GCMKW, _) => Self::Oct(Aes256Gcm::KEY_LENGTH), - (JweAlgorithm::PBES2_HS256_A128KW, _) => Self::Oct(Aes128Gcm::KEY_LENGTH), - (JweAlgorithm::PBES2_HS384_A192KW, _) => Self::Oct(Aes192Gcm::KEY_LENGTH), - (JweAlgorithm::PBES2_HS512_A256KW, _) => Self::Oct(Aes256Gcm::KEY_LENGTH), - (JweAlgorithm::ECDH_1PU, _) => Self::Ecx(EcxCurve::X25519), - (JweAlgorithm::ECDH_1PU_A128KW, _) => Self::Ecx(EcxCurve::X25519), - (JweAlgorithm::ECDH_1PU_A192KW, _) => Self::Ecx(EcxCurve::X25519), - (JweAlgorithm::ECDH_1PU_A256KW, _) => Self::Ecx(EcxCurve::X25519), - (JweAlgorithm::C20PKW, _) => Self::Oct(ChaCha20Poly1305::KEY_LENGTH), - (JweAlgorithm::XC20PKW, _) => Self::Oct(XChaCha20Poly1305::KEY_LENGTH), - } - } -} diff --git a/libjose/src/utils/crypto/key_repr.rs b/libjose/src/utils/crypto/key_repr.rs deleted file mode 100644 index b3f94c944e..0000000000 --- a/libjose/src/utils/crypto/key_repr.rs +++ /dev/null @@ -1,372 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use core::convert::TryInto; - -use crate::error::Error; -use crate::error::Result; -use crate::jwk::EcCurve; -use crate::jwk::EcxCurve; -use crate::jwk::EdCurve; -use crate::jwk::Jwk; -use crate::jwk::JwkParamsEc; -use crate::jwk::JwkParamsOct; -use crate::jwk::JwkParamsOkp; -use crate::jwk::JwkParamsRsa; -use crate::lib::*; -use crate::utils::decode_b64; -use rsa::pkcs1::FromRsaPrivateKey; -use rsa::pkcs1::FromRsaPublicKey; -use rsa::pkcs8::FromPrivateKey; -use rsa::pkcs8::FromPublicKey; - -pub type RsaPublicKey = rsa::RsaPublicKey; -pub type RsaSecretKey = rsa::RsaPrivateKey; -pub type RsaUint = rsa::BigUint; -pub type RsaPadding = rsa::PaddingScheme; - -pub type Ed25519PublicKey = crypto::signatures::ed25519::PublicKey; -pub type Ed25519SecretKey = crypto::signatures::ed25519::SecretKey; -pub type Ed25519Signature = crypto::signatures::ed25519::Signature; - -pub type P256PublicKey = crypto::signatures::p256::PublicKey; -pub type P256SecretKey = crypto::signatures::p256::SecretKey; - -pub type K256PublicKey = crypto::signatures::secp256k1::PublicKey; -pub type K256SecretKey = crypto::signatures::secp256k1::SecretKey; - -pub type X25519PublicKey = crypto::keys::x25519::PublicKey; -pub type X25519SecretKey = crypto::keys::x25519::SecretKey; - -pub type X448PublicKey = crypto::keys::x448::PublicKey; -pub type X448SecretKey = crypto::keys::x448::SecretKey; - -pub const ED25519_PUBLIC_KEY_LEN: usize = crypto::signatures::ed25519::COMPRESSED_PUBLIC_KEY_LENGTH; -pub const ED25519_SECRET_KEY_LEN: usize = crypto::signatures::ed25519::SECRET_KEY_LENGTH; - -pub const X25519_PUBLIC_KEY_LEN: usize = crypto::keys::x25519::PUBLIC_KEY_LEN; -pub const X25519_SECRET_KEY_LEN: usize = crypto::keys::x25519::SECRET_KEY_LEN; - -#[derive(Clone, Copy, Debug)] -pub enum Secret<'a> { - Arr(&'a [u8]), - Jwk(&'a Jwk), -} - -impl<'a> Secret<'a> { - pub fn check_signing_key(&self, algorithm: &str) -> Result<()> { - if let Self::Jwk(jwk) = self { - jwk.check_signing_key(algorithm)?; - } - - Ok(()) - } - - pub fn check_verifying_key(&self, algorithm: &str) -> Result<()> { - if let Self::Jwk(jwk) = self { - jwk.check_verifying_key(algorithm)?; - } - - Ok(()) - } - - pub fn check_encryption_key(&self, algorithm: &str) -> Result<()> { - if let Self::Jwk(jwk) = self { - jwk.check_encryption_key(algorithm)?; - } - - Ok(()) - } - - pub fn check_decryption_key(&self, algorithm: &str) -> Result<()> { - if let Self::Jwk(jwk) = self { - jwk.check_decryption_key(algorithm)?; - } - - Ok(()) - } - - pub fn to_oct_key(self, key_len: usize) -> Result> { - match self { - Secret::Arr(arr) => { - if arr.len() >= key_len { - return Ok(Cow::Borrowed(arr)); - } - } - Secret::Jwk(jwk) => { - let params: &JwkParamsOct = jwk.try_oct_params()?; - let k: Vec = decode_b64(¶ms.k)?; - - if k.len() >= key_len { - return Ok(Cow::Owned(k)); - } - } - } - - Err(Error::KeyError("Oct Key Length")) - } - - pub fn to_rsa_public(self) -> Result { - match self { - Secret::Arr(arr) => RsaPublicKey::from_pkcs1_der(arr) - .or_else(|_| RsaPublicKey::from_public_key_der(arr)) - .map_err(|err| rsa::errors::Error::from(err).into()), - Secret::Jwk(jwk) => { - let params: &JwkParamsRsa = jwk.try_rsa_params()?; - let n: RsaUint = decode_rsa_uint(¶ms.n)?; - let e: RsaUint = decode_rsa_uint(¶ms.e)?; - - RsaPublicKey::new(n, e).map_err(Into::into) - } - } - } - - #[allow(clippy::many_single_char_names)] - pub fn to_rsa_secret(self) -> Result { - match self { - Secret::Arr(arr) => RsaSecretKey::from_pkcs1_der(arr) - .or_else(|_| RsaSecretKey::from_pkcs8_der(arr)) - .map_err(|err| rsa::errors::Error::from(err).into()), - Secret::Jwk(jwk) => { - let params: &JwkParamsRsa = jwk.try_rsa_params()?; - - // TODO: Handle Multi-prime keys - if params.oth.is_some() { - return Err(Error::KeyError("multi prime keys are not supported")); - } - - let n: RsaUint = decode_rsa_uint(¶ms.n)?; - let e: RsaUint = decode_rsa_uint(¶ms.e)?; - let d: RsaUint = decode_rsa_uint_opt(params.d.as_deref())?; - let p: RsaUint = decode_rsa_uint_opt(params.p.as_deref())?; - let q: RsaUint = decode_rsa_uint_opt(params.q.as_deref())?; - - // TODO: Check against generated properties - - let _dp: RsaUint = decode_rsa_uint_opt(params.dp.as_deref())?; - let _dq: RsaUint = decode_rsa_uint_opt(params.dq.as_deref())?; - let _qi: RsaUint = decode_rsa_uint_opt(params.qi.as_deref())?; - - let key: _ = RsaSecretKey::from_components(n, e, d, vec![p, q]); - - key.validate()?; - - Ok(key) - } - } - } - - pub fn to_p256_public(self) -> Result { - expand_ec_public(EcCurve::P256, self, P256PublicKey::from_bytes) - } - - pub fn to_p256_secret(self) -> Result { - expand_ec_secret(EcCurve::P256, self, P256SecretKey::from_bytes) - } - - pub fn to_k256_public(self) -> Result { - expand_ec_public(EcCurve::Secp256K1, self, K256PublicKey::from_bytes) - } - - pub fn to_k256_secret(self) -> Result { - expand_ec_secret(EcCurve::Secp256K1, self, K256SecretKey::from_bytes) - } - - pub fn to_ed25519_public(self) -> Result { - expand_ed_public(EdCurve::Ed25519, self, parse_ed25519_public_key) - } - - pub fn to_ed25519_secret(self) -> Result { - expand_ed_secret(EdCurve::Ed25519, self, parse_ed25519_secret_key) - } - - pub fn to_x25519_public(self) -> Result { - expand_ecx_public(EcxCurve::X25519, self, X25519PublicKey::from_bytes) - } - - pub fn to_x25519_secret(self) -> Result { - expand_ecx_secret(EcxCurve::X25519, self, X25519SecretKey::from_bytes) - } - - pub fn to_x448_public(self) -> Result { - expand_ecx_public(EcxCurve::X448, self, X448PublicKey::from_bytes) - } - - pub fn to_x448_secret(self) -> Result { - expand_ecx_secret(EcxCurve::X448, self, X448SecretKey::from_bytes) - } - - pub(crate) fn expand( - self, - expand_arr: impl Fn(&[u8]) -> Result, - expand_jwk: impl Fn(&Jwk) -> Result>, - ) -> Result - where - E: Into, - { - match self { - Self::Arr(arr) => expand_arr(arr).map_err(Into::into), - Self::Jwk(jwk) => expand_arr(&expand_jwk(jwk)?).map_err(Into::into), - } - } -} - -impl<'a> From<&'a [u8]> for Secret<'a> { - fn from(other: &'a [u8]) -> Self { - Self::Arr(other) - } -} - -impl<'a> From<&'a Jwk> for Secret<'a> { - fn from(other: &'a Jwk) -> Self { - Self::Jwk(other) - } -} - -impl<'a, T> From<&'a T> for Secret<'a> -where - T: AsRef<[u8]>, -{ - fn from(other: &'a T) -> Self { - Self::Arr(other.as_ref()) - } -} - -// ============================================================================= -// ============================================================================= - -fn decode_rsa_uint(data: impl AsRef<[u8]>) -> Result { - decode_b64(data).map(|data| RsaUint::from_bytes_be(&data)) -} - -fn decode_rsa_uint_opt(data: Option>) -> Result { - data.ok_or(Error::KeyError("RSA")).and_then(decode_rsa_uint) -} - -fn parse_ed25519_public_key(arr: &[u8]) -> Result { - let bytes: [u8; ED25519_PUBLIC_KEY_LEN] = arr.try_into().map_err(|_| Error::KeyError(EdCurve::Ed25519.name()))?; - - Ed25519PublicKey::from_compressed_bytes(bytes).map_err(|_| Error::KeyError(EdCurve::Ed25519.name())) -} - -fn parse_ed25519_secret_key(arr: &[u8]) -> Result { - let bytes: [u8; ED25519_SECRET_KEY_LEN] = arr.try_into().map_err(|_| Error::KeyError(EdCurve::Ed25519.name()))?; - - Ed25519SecretKey::from_le_bytes(bytes).map_err(|_| Error::KeyError(EdCurve::Ed25519.name())) -} - -fn expand_ec_public(curve: EcCurve, secret: Secret<'_>, f: impl Fn(&[u8]) -> Result) -> Result -where - E: Into, -{ - secret.expand(f, |jwk| { - let params: &JwkParamsEc = jwk.try_ec_params()?; - - if params.try_ec_curve()? != curve { - return Err(Error::KeyError(curve.name())); - } - - let bytes: Vec = decode_b64(¶ms.x)? - .into_iter() - .chain(decode_b64(¶ms.y)?.into_iter()) - .collect(); - - Ok(bytes) - }) -} - -fn expand_ec_secret(curve: EcCurve, secret: Secret<'_>, f: impl Fn(&[u8]) -> Result) -> Result -where - E: Into, -{ - secret.expand(f, |jwk| { - let params: &JwkParamsEc = jwk.try_ec_params()?; - - if params.try_ec_curve()? != curve { - return Err(Error::KeyError(curve.name())); - } - - params - .d - .as_ref() - .map(decode_b64) - .transpose()? - .ok_or_else(|| Error::KeyError(curve.name())) - }) -} - -fn expand_ed_public(curve: EdCurve, secret: Secret<'_>, f: impl Fn(&[u8]) -> Result) -> Result -where - E: Into, -{ - secret.expand( - |arr| f(arr), - |jwk| { - let params: &JwkParamsOkp = jwk.try_okp_params()?; - - if params.try_ed_curve()? != curve { - return Err(Error::KeyError(curve.name())); - } - - decode_b64(¶ms.x) - }, - ) -} - -fn expand_ed_secret(curve: EdCurve, secret: Secret<'_>, f: impl Fn(&[u8]) -> Result) -> Result -where - E: Into, -{ - secret.expand( - |arr| f(arr), - |jwk| { - let params: &JwkParamsOkp = jwk.try_okp_params()?; - - if params.try_ed_curve()? != curve { - return Err(Error::KeyError(curve.name())); - } - - params - .d - .as_deref() - .map(decode_b64) - .transpose()? - .ok_or_else(|| Error::KeyError(curve.name())) - }, - ) -} - -fn expand_ecx_public(curve: EcxCurve, secret: Secret<'_>, f: impl Fn(&[u8]) -> Result) -> Result { - secret.expand( - |arr| f(arr).map_err(|_| Error::KeyError(curve.name())), - |jwk| { - let params: &JwkParamsOkp = jwk.try_okp_params()?; - - if params.try_ecx_curve()? != curve { - return Err(Error::KeyError(curve.name())); - } - - decode_b64(¶ms.x) - }, - ) -} - -fn expand_ecx_secret(curve: EcxCurve, secret: Secret<'_>, f: impl Fn(&[u8]) -> Result) -> Result { - secret.expand( - |arr| f(arr).map_err(|_| Error::KeyError(curve.name())), - |jwk| { - let params: &JwkParamsOkp = jwk.try_okp_params()?; - - if params.try_ecx_curve()? != curve { - return Err(Error::KeyError(curve.name())); - } - - params - .d - .as_ref() - .map(decode_b64) - .transpose()? - .ok_or_else(|| Error::KeyError(curve.name())) - }, - ) -} diff --git a/libjose/src/utils/crypto/mod.rs b/libjose/src/utils/crypto/mod.rs deleted file mode 100644 index e33a0c44a1..0000000000 --- a/libjose/src/utils/crypto/mod.rs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -mod concat_kdf; -mod diffie_hellman; -mod key_params; -mod key_repr; -mod random; -mod rsa_primes; -mod x25519; - -pub use self::concat_kdf::*; -pub use self::diffie_hellman::*; -pub use self::key_params::*; -pub use self::key_repr::*; -pub use self::random::*; -pub use self::rsa_primes::*; -pub use self::x25519::*; diff --git a/libjose/src/utils/crypto/random.rs b/libjose/src/utils/crypto/random.rs deleted file mode 100644 index 371b4c757a..0000000000 --- a/libjose/src/utils/crypto/random.rs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use crypto::utils::rand; - -use crate::error::Result; -use crate::lib::*; - -pub fn random_bytes(size: usize) -> Result> { - let mut bytes: Vec = vec![0; size]; - - rand::fill(&mut bytes)?; - - Ok(bytes) -} diff --git a/libjose/src/utils/crypto/rsa_primes.rs b/libjose/src/utils/crypto/rsa_primes.rs deleted file mode 100644 index 690ae615b7..0000000000 --- a/libjose/src/utils/crypto/rsa_primes.rs +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use num_bigint_dig::traits::ModInverse; -use zeroize::Zeroize; - -use crate::error::Error; -use crate::error::Result; -use crate::utils::RsaUint; - -pub struct RsaComputed { - // D mod (P-1) - pub(crate) dp: RsaUint, - // D mod (Q-1) - pub(crate) dq: RsaUint, - // Q^-1 mod P - pub(crate) qi: RsaUint, -} - -impl RsaComputed { - pub fn new(d: &RsaUint, p: &RsaUint, q: &RsaUint) -> Result { - let one: RsaUint = RsaUint::new(vec![1]); - - let dp: RsaUint = d % (p - &one); - let dq: RsaUint = d % (q - &one); - - let qi: RsaUint = q - .clone() - .mod_inverse(p) - .ok_or(Error::InvalidRsaPrime)? - .to_biguint() - .ok_or(Error::InvalidRsaPrime)?; - - Ok(Self { dp, dq, qi }) - } -} - -impl Zeroize for RsaComputed { - fn zeroize(&mut self) { - self.dp.zeroize(); - self.dq.zeroize(); - self.qi.zeroize(); - } -} - -impl Drop for RsaComputed { - fn drop(&mut self) { - self.zeroize(); - } -} diff --git a/libjose/src/utils/crypto/x25519.rs b/libjose/src/utils/crypto/x25519.rs deleted file mode 100644 index a8397d1fc1..0000000000 --- a/libjose/src/utils/crypto/x25519.rs +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use core::convert::TryInto; -use crypto::hashes::sha::Sha512; -use crypto::hashes::Digest; -use crypto::hashes::Output; -use curve25519_dalek::edwards::CompressedEdwardsY; -use zeroize::Zeroize; - -use crate::error::Error; -use crate::error::Result; -use crate::utils::ED25519_PUBLIC_KEY_LEN; -use crate::utils::ED25519_SECRET_KEY_LEN; -use crate::utils::X25519_PUBLIC_KEY_LEN; -use crate::utils::X25519_SECRET_KEY_LEN; - -pub fn ed25519_to_x25519_public(public: &T) -> Result<[u8; X25519_PUBLIC_KEY_LEN]> -where - T: AsRef<[u8]> + ?Sized, -{ - let mut ed25519: [u8; ED25519_PUBLIC_KEY_LEN] = public - .as_ref() - .try_into() - .map_err(|_| Error::KeyError("ed25519_to_x25519_public"))?; - - let x25519: [u8; X25519_PUBLIC_KEY_LEN] = CompressedEdwardsY(ed25519) - .decompress() - .map(|edwards| edwards.to_montgomery().0) - .ok_or(Error::KeyError("ed25519_to_x25519_public"))?; - - ed25519.zeroize(); - - Ok(x25519) -} - -pub fn ed25519_to_x25519_secret(secret: &T) -> Result<[u8; X25519_SECRET_KEY_LEN]> -where - T: AsRef<[u8]> + ?Sized, -{ - let mut ed25519: [u8; ED25519_SECRET_KEY_LEN] = secret - .as_ref() - .try_into() - .map_err(|_| Error::KeyError("ed25519_to_x25519_secret"))?; - - let mut x25519: [u8; X25519_SECRET_KEY_LEN] = [0; X25519_SECRET_KEY_LEN]; - let hash: Output = Sha512::digest(&ed25519); - - x25519.copy_from_slice(&hash[..X25519_SECRET_KEY_LEN]); - x25519[0] &= 248; - x25519[31] &= 127; - x25519[31] |= 64; - - ed25519.zeroize(); - - Ok(x25519) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::utils::Ed25519PublicKey; - use crate::utils::Ed25519SecretKey; - use crate::utils::X25519PublicKey; - use crate::utils::X25519SecretKey; - - #[test] - fn test_convert_ed25519() { - let ed25519_sk: Ed25519SecretKey = Ed25519SecretKey::generate().unwrap(); - let ed25519_pk: Ed25519PublicKey = ed25519_sk.public_key(); - - let derived_sk = ed25519_to_x25519_secret(&ed25519_sk.to_le_bytes()).unwrap(); - let derived_pk = ed25519_to_x25519_public(ed25519_pk.as_ref()).unwrap(); - - let x25519_sk: X25519SecretKey = X25519SecretKey::from_bytes(&derived_sk).unwrap(); - let x25519_pk: X25519PublicKey = X25519PublicKey::from_bytes(&derived_pk).unwrap(); - - assert_eq!(derived_sk, x25519_sk.to_bytes()); - assert_eq!(derived_pk, x25519_pk.to_bytes()); - } -} diff --git a/libjose/src/utils/mod.rs b/libjose/src/utils/mod.rs deleted file mode 100644 index c7e7c80452..0000000000 --- a/libjose/src/utils/mod.rs +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -mod base64; -mod crypto; -mod serde; - -pub use self::base64::*; -pub use self::crypto::*; -pub use self::serde::*; diff --git a/libjose/src/utils/serde.rs b/libjose/src/utils/serde.rs deleted file mode 100644 index 534a3c632b..0000000000 --- a/libjose/src/utils/serde.rs +++ /dev/null @@ -1,191 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use core::str; - -use crate::error::Error; -use crate::error::Result; -use crate::jose::JoseHeader; -use crate::jwe::JweHeader; -use crate::jws::JwsHeader; -use crate::lib::*; -use crate::utils::decode_b64; - -// The default value of the "b64" header parameter -const DEFAULT_B64: bool = true; - -// Claims defined in the base JWE/JWS RFCs -const PREDEFINED: &[&str] = &[ - "alg", "jku", "jwk", "kid", "x5u", "x5c", "x5t", "x5t#s256", "typ", "cty", "crit", "enc", "zip", "epk", "apu", "apv", - "iv", "tag", "p2s", "p2c", -]; - -pub fn parse_utf8(slice: &(impl AsRef<[u8]> + ?Sized)) -> Result<&str> { - str::from_utf8(slice.as_ref()).map_err(Error::InvalidUtf8) -} - -pub fn check_slice_param(name: &'static str, slice: Option<&[T]>, value: &T) -> Result<()> -where - T: PartialEq, -{ - if slice.map(|slice| slice.contains(value)).unwrap_or(true) { - Ok(()) - } else { - Err(Error::InvalidParam(name)) - } -} - -pub fn filter_non_empty_bytes<'a, T, U: 'a>(value: T) -> Option<&'a [u8]> -where - T: Into>, - U: AsRef<[u8]> + ?Sized, -{ - value.into().map(AsRef::as_ref).filter(|value| !value.is_empty()) -} - -pub fn create_aad(header: Option<&T>, data: Option<&U>) -> Vec -where - T: AsRef<[u8]> + ?Sized, - U: AsRef<[u8]> + ?Sized, -{ - let header: &[u8] = header.map(AsRef::as_ref).unwrap_or_default(); - let data: &[u8] = data.map(AsRef::as_ref).unwrap_or_default(); - - if data.is_empty() { - header.to_vec() - } else { - create_message(header, data) - } -} - -pub fn parse_cek(cek: Option<&str>) -> Result> { - cek.ok_or(Error::EncError("CEK (missing)")).and_then(decode_b64) -} - -pub fn create_pbes2_salt(algorithm: &str, p2s: &[u8]) -> Vec { - let capacity: usize = algorithm.len() + 1 + p2s.len(); - let mut salt: Vec = Vec::with_capacity(capacity); - - // The salt value used is (UTF8(Alg) || 0x00 || Salt Input) - salt.extend_from_slice(algorithm.as_bytes()); - salt.push(0x0); - salt.extend_from_slice(p2s); - salt -} - -pub fn create_message(header: &[u8], claims: &[u8]) -> Vec { - let capacity: usize = header.len() + 1 + claims.len(); - let mut message: Vec = Vec::with_capacity(capacity); - - message.extend(header); - message.push(b'.'); - message.extend(claims); - message -} - -pub fn extract_b64(header: Option<&JwsHeader>) -> bool { - header.and_then(JwsHeader::b64).unwrap_or(DEFAULT_B64) -} - -pub fn validate_jws_headers( - protected: Option<&JwsHeader>, - unprotected: Option<&JwsHeader>, - permitted: Option<&[String]>, -) -> Result<()> { - // TODO: Validate Disjoint - - validate_crit(protected, unprotected, permitted)?; - validate_b64(protected, unprotected)?; - - Ok(()) -} - -pub fn validate_jwe_headers<'a>( - protected: Option<&JweHeader>, - unprotected: Option<&JweHeader>, - recipients: impl Iterator>, - permitted: Option<&[String]>, -) -> Result<()> { - // TODO: Validate Disjoint - - for recipient in recipients { - // TODO: Validate Disjoint - - let unprotected: Option> = match (unprotected, recipient) { - (Some(header), None) => Some(Cow::Borrowed(header)), - (None, Some(header)) => Some(Cow::Borrowed(header)), - (Some(_lhs), Some(_rhs)) => todo!("Merge Headers"), - (None, None) => None, - }; - - validate_crit(protected, unprotected.as_deref(), permitted)?; - - // The "zip" parameter MUST be integrity protected - if unprotected.map(|header| header.has("zip")).unwrap_or_default() { - return Err(Error::InvalidParam("zip (unprotected)")); - } - } - - Ok(()) -} - -pub fn validate_crit(protected: Option<&T>, unprotected: Option<&T>, permitted: Option<&[String]>) -> Result<()> -where - T: JoseHeader, -{ - // The "crit" parameter MUST be integrity protected - if unprotected.map(|header| header.has_claim("crit")).unwrap_or_default() { - return Err(Error::InvalidParam("crit (unprotected)")); - } - - let values: Option<&[String]> = protected.and_then(|header| header.common().crit()); - - // The "crit" parameter MUST NOT be an empty list - if values.map(|values| values.is_empty()).unwrap_or_default() { - return Err(Error::InvalidParam("crit (empty)")); - } - - let permitted: &[String] = permitted.unwrap_or_default(); - let values: &[String] = values.unwrap_or_default(); - - for value in values { - // The "crit" parameter MUST NOT contain any header parameters defined by - // the JOSE JWS/JWA specifications. - if PREDEFINED.contains(&&**value) { - return Err(Error::InvalidParam("crit (pre-defined)")); - } - - // The "crit" parameter MUST be understood by the application. - if !permitted.contains(value) { - return Err(Error::InvalidParam("crit (unpermitted)")); - } - - let exists: bool = protected - .map(|header| header.has_claim(value)) - .or_else(|| unprotected.map(|header| header.has_claim(value))) - .unwrap_or_default(); - - if !exists { - return Err(Error::InvalidParam("crit")); - } - } - - Ok(()) -} - -pub fn validate_b64(protected: Option<&JwsHeader>, unprotected: Option<&JwsHeader>) -> Result<()> { - // The "b64" parameter MUST be integrity protected - if unprotected.and_then(JwsHeader::b64).is_some() { - return Err(Error::InvalidParam("b64 (unprotected)")); - } - - let b64: Option = protected.and_then(|header| header.b64()); - let crit: Option<&[String]> = protected.and_then(|header| header.crit()); - - // The "b64" parameter MUST be included in the "crit" parameter values - match (b64, crit) { - (Some(_), Some(values)) if values.iter().any(|value| value == "b64") => Ok(()), - (Some(_), None) => Err(Error::InvalidParam("b64 (non-critical)")), - _ => Ok(()), - } -} diff --git a/libjose/tests/fixtures/rfc7515.rs b/libjose/tests/fixtures/rfc7515.rs deleted file mode 100644 index 2e176e9c20..0000000000 --- a/libjose/tests/fixtures/rfc7515.rs +++ /dev/null @@ -1,70 +0,0 @@ -[ - // https://tools.ietf.org/html/rfc7515#appendix-A.1 - TestVector { - // This test vector is NOT deterministic because the encoded token has - // line breaks in the header. - deterministic: false, - header: r#"{"typ":"JWT","alg":"HS256"}"#, - claims: &[123, 34, 105, 115, 115, 34, 58, 34, 106, 111, 101, 34, 44, 13, 10, 32, 34, 101, 120, 112, 34, 58, 49, 51, 48, 48, 56, 49, 57, 51, 56, 48, 44, 13, 10, 32, 34, 104, 116, 116, 112, 58, 47, 47, 101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109, 47, 105, 115, 95, 114, 111, 111, 116, 34, 58, 116, 114, 117, 101, 125], - encoded: b"eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk", - private_key: r#" - { - "kty": "oct", - "k": "AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow" - } - "#, - }, - // https://tools.ietf.org/html/rfc7515#appendix-A.2 - TestVector { - deterministic: true, - header: r#"{"alg":"RS256"}"#, - claims: &[123, 34, 105, 115, 115, 34, 58, 34, 106, 111, 101, 34, 44, 13, 10, 32, 34, 101, 120, 112, 34, 58, 49, 51, 48, 48, 56, 49, 57, 51, 56, 48, 44, 13, 10, 32, 34, 104, 116, 116, 112, 58, 47, 47, 101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109, 47, 105, 115, 95, 114, 111, 111, 116, 34, 58, 116, 114, 117, 101, 125], - encoded: b"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.cC4hiUPoj9Eetdgtv3hF80EGrhuB__dzERat0XF9g2VtQgr9PJbu3XOiZj5RZmh7AAuHIm4Bh-0Qc_lF5YKt_O8W2Fp5jujGbds9uJdbF9CUAr7t1dnZcAcQjbKBYNX4BAynRFdiuB--f_nZLgrnbyTyWzO75vRK5h6xBArLIARNPvkSjtQBMHlb1L07Qe7K0GarZRmB_eSN9383LcOLn6_dO--xi12jzDwusC-eOkHWEsqtFZESc6BfI7noOPqvhJ1phCnvWh6IeYI2w9QOYEUipUTI8np6LbgGY9Fs98rqVt5AXLIhWkWywlVmtVrBp0igcN_IoypGlUPQGe77Rw", - private_key: r#" - { - "kty": "RSA", - "n": "ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddxHmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMsD1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSHSXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdVMTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ", - "e": "AQAB", - "d": "Eq5xpGnNCivDflJsRQBXHx1hdR1k6Ulwe2JZD50LpXyWPEAeP88vLNO97IjlA7_GQ5sLKMgvfTeXZx9SE-7YwVol2NXOoAJe46sui395IW_GO-pWJ1O0BkTGoVEn2bKVRUCgu-GjBVaYLU6f3l9kJfFNS3E0QbVdxzubSu3Mkqzjkn439X0M_V51gfpRLI9JYanrC4D4qAdGcopV_0ZHHzQlBjudU2QvXt4ehNYTCBr6XCLQUShb1juUO1ZdiYoFaFQT5Tw8bGUl_x_jTj3ccPDVZFD9pIuhLhBOneufuBiB4cS98l2SR_RQyGWSeWjnczT0QU91p1DhOVRuOopznQ", - "p": "4BzEEOtIpmVdVEZNCqS7baC4crd0pqnRH_5IB3jw3bcxGn6QLvnEtfdUdiYrqBdss1l58BQ3KhooKeQTa9AB0Hw_Py5PJdTJNPY8cQn7ouZ2KKDcmnPGBY5t7yLc1QlQ5xHdwW1VhvKn-nXqhJTBgIPgtldC-KDV5z-y2XDwGUc", - "q": "uQPEfgmVtjL0Uyyx88GZFF1fOunH3-7cepKmtH4pxhtCoHqpWmT8YAmZxaewHgHAjLYsp1ZSe7zFYHj7C6ul7TjeLQeZD_YwD66t62wDmpe_HlB-TnBA-njbglfIsRLtXlnDzQkv5dTltRJ11BKBBypeeF6689rjcJIDEz9RWdc", - "dp": "BwKfV3Akq5_MFZDFZCnW-wzl-CCo83WoZvnLQwCTeDv8uzluRSnm71I3QCLdhrqE2e9YkxvuxdBfpT_PI7Yz-FOKnu1R6HsJeDCjn12Sk3vmAktV2zb34MCdy7cpdTh_YVr7tss2u6vneTwrA86rZtu5Mbr1C1XsmvkxHQAdYo0", - "dq": "h_96-mK1R_7glhsum81dZxjTnYynPbZpHziZjeeHcXYsXaaMwkOlODsWa7I9xXDoRwbKgB719rrmI2oKr6N3Do9U0ajaHF-NKJnwgjMd2w9cjz3_-kyNlxAr2v4IKhGNpmM5iIgOS1VZnOZ68m6_pbLBSp3nssTdlqvd0tIiTHU", - "qi": "IYd7DHOhrWvxkwPQsRM2tOgrjbcrfvtQJipd-DlcxyVuuM9sQLdgjVk2oy26F0EmpScGLq2MowX7fhd_QJQ3ydy5cY7YIBi87w93IKLEdfnbJtoOPLUW0ITrJReOgo1cq9SbsxYawBgfp_gh6A5603k2-ZQwVK0JKSHuLFkuQ3U" - } - "#, - }, - // https://tools.ietf.org/html/rfc7515#appendix-A.3 - TestVector { - deterministic: true, - header: r#"{"alg":"ES256"}"#, - claims: &[123, 34, 105, 115, 115, 34, 58, 34, 106, 111, 101, 34, 44, 13, 10, 32, 34, 101, 120, 112, 34, 58, 49, 51, 48, 48, 56, 49, 57, 51, 56, 48, 44, 13, 10, 32, 34, 104, 116, 116, 112, 58, 47, 47, 101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109, 47, 105, 115, 95, 114, 111, 111, 116, 34, 58, 116, 114, 117, 101, 125], - // encoded: b"eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8ISlSApmWQxfKTUJqPP3-Kg6NU1Q", - encoded: b"eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.e4ZrhZdbFQ7630Tq51E6RQiJaae9bFNGJszIhtusEwzvO21rzH76Wer6yRn2Zb34VjIm3cVRl0iQctbf4uBY3w", - private_key: r#" - { - "kty": "EC", - "crv": "P-256", - "x": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU", - "y": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0", - "d": "jpsQnnGQmL-YBIffH1136cspYG6-0iY7X1fCE9-E9LI" - } - "#, - }, - // // https://tools.ietf.org/html/rfc7515#appendix-A.4 - // TestVector { - // deterministic: true, - // header: r#"{"alg":"ES512"}"#, - // claims: & [80, 97, 121, 108, 111, 97, 100], - // encoded: b"eyJhbGciOiJFUzUxMiJ9.UGF5bG9hZA.AdwMgeerwtHoh-l192l60hp9wAHZFVJbLfD_UxMi70cwnZOYaRI1bKPWROc-mZZqwqT2SI-KGDKB34XO0aw_7XdtAG8GaSwFKdCAPZgoXD2YBJZCPEX3xKpRwcdOO8KpEHwJjyqOgzDO7iKvU8vcnwNrmxYbSW9ERBXukOXolLzeO_Jn", - // private_key: r#" - // { - // "kty": "EC", - // "crv": "P-521", - // "x": "AekpBQ8ST8a8VcfVOTNl353vSrDCLLJXmPk06wTjxrrjcBpXp5EOnYG_NjFZ6OvLFV1jSfS9tsz4qUxcWceqwQGk", - // "y": "ADSmRA43Z1DSNx_RvcLI87cdL07l6jQyyBXMoxVg_l2Th-x3S1WDhjDly79ajL4Kkd0AZMaZmh9ubmf63e3kyMj2", - // "d": "AY5pb7A0UFiB3RELSD64fTLOSV_jazdF7fLYyuTw8lOfRhWg6Y6rUrPAxerEzgdRhajnu0ferB0d53vM9mE15j2C" - // } - // "#, - // }, -] diff --git a/libjose/tests/fixtures/rfc7516.rs b/libjose/tests/fixtures/rfc7516.rs deleted file mode 100644 index decc1baf65..0000000000 --- a/libjose/tests/fixtures/rfc7516.rs +++ /dev/null @@ -1,52 +0,0 @@ -[ - // https://tools.ietf.org/html/rfc7516#appendix-A.1 - TestVector { - claims: b"The true sign of intelligence is not knowledge but imagination.", - header: r#"{"alg":"RSA-OAEP","enc":"A256GCM"}"#, - recipient: r#" - { - "kty": "RSA", - "n": "oahUIoWw0K0usKNuOR6H4wkf4oBUXHTxRvgb48E-BVvxkeDNjbC4he8rUWcJoZmds2h7M70imEVhRU5djINXtqllXI4DFqcI1DgjT9LewND8MW2Krf3Spsk_ZkoFnilakGygTwpZ3uesH-PFABNIUYpOiN15dsQRkgr0vEhxN92i2asbOenSZeyaxziK72UwxrrKoExv6kc5twXTq4h-QChLOln0_mtUZwfsRaMStPs6mS6XrgxnxbWhojf663tuEQueGC-FCMfra36C9knDFGzKsNa7LZK2djYgyD3JR_MB_4NUJW_TqOQtwHYbxevoJArm-L5StowjzGy-_bq6Gw", - "e": "AQAB", - "d": "kLdtIj6GbDks_ApCSTYQtelcNttlKiOyPzMrXHeI-yk1F7-kpDxY4-WY5NWV5KntaEeXS1j82E375xxhWMHXyvjYecPT9fpwR_M9gV8n9Hrh2anTpTD93Dt62ypW3yDsJzBnTnrYu1iwWRgBKrEYY46qAZIrA2xAwnm2X7uGR1hghkqDp0Vqj3kbSCz1XyfCs6_LehBwtxHIyh8Ripy40p24moOAbgxVw3rxT_vlt3UVe4WO3JkJOzlpUf-KTVI2Ptgm-dARxTEtE-id-4OJr0h-K-VFs3VSndVTIznSxfyrj8ILL6MG_Uv8YAu7VILSB3lOW085-4qE3DzgrTjgyQ", - "p": "1r52Xk46c-LsfB5P442p7atdPUrxQSy4mti_tZI3Mgf2EuFVbUoDBvaRQ-SWxkbkmoEzL7JXroSBjSrK3YIQgYdMgyAEPTPjXv_hI2_1eTSPVZfzL0lffNn03IXqWF5MDFuoUYE0hzb2vhrlN_rKrbfDIwUbTrjjgieRbwC6Cl0", - "q": "wLb35x7hmQWZsWJmB_vle87ihgZ19S8lBEROLIsZG4ayZVe9Hi9gDVCOBmUDdaDYVTSNx_8Fyw1YYa9XGrGnDew00J28cRUoeBB_jKI1oma0Orv1T9aXIWxKwd4gvxFImOWr3QRL9KEBRzk2RatUBnmDZJTIAfwTs0g68UZHvtc", - "dp": "ZK-YwE7diUh0qR1tR7w8WHtolDx3MZ_OTowiFvgfeQ3SiresXjm9gZ5KLhMXvo-uz-KUJWDxS5pFQ_M0evdo1dKiRTjVw_x4NyqyXPM5nULPkcpU827rnpZzAJKpdhWAgqrXGKAECQH0Xt4taznjnd_zVpAmZZq60WPMBMfKcuE", - "dq": "Dq0gfgJ1DdFGXiLvQEZnuKEN0UUmsJBxkjydc3j4ZYdBiMRAy86x0vHCjywcMlYYg4yoC4YZa9hNVcsjqA3FeiL19rk8g6Qn29Tt0cj8qqyFpz9vNDBUfCAiJVeESOjJDZPYHdHY8v1b-o-Z2X5tvLx-TCekf7oxyeKDUqKWjis", - "qi": "VIMpMYbPf47dT1w_zDUXfPimsSegnMOA1zTaX7aGk_8urY6R8-ZW1FxU7AlWAyLWybqq6t16VFd7hQd0y6flUK4SlOydB61gwanOsXGOAOv82cHq0E3eL4HrtZkUuKvnPrMnsUUFlfUdybVzxyjz9JF_XyaY14ardLSjf4L_FNY" - } - "#, - encoded: "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ.OKOawDo13gRp2ojaHV7LFpZcgV7T6DVZKTyKOMTYUmKoTCVJRgckCL9kiMT03JGeipsEdY3mx_etLbbWSrFr05kLzcSr4qKAq7YN7e9jwQRb23nfa6c9d-StnImGyFDbSv04uVuxIp5Zms1gNxKKK2Da14B8S4rzVRltdYwam_lDp5XnZAYpQdb76FdIKLaVmqgfwX7XWRxv2322i-vDxRfqNzo_tETKzpVLzfiwQyeyPGLBIO56YJ7eObdv0je81860ppamavo35UgoRdbYaBcoh9QcfylQr66oc6vFWXRcZ_ZT2LawVCWTIy3brGPi6UklfCpIMfIjf7iGdXKHzg.48V1_ALb6US04U3b.5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8iB7j6jiSdiwkIr3ajwQzaBtQD_A.XFBoMYUZodetZdvTiFvSkQ", - }, - // https://tools.ietf.org/html/rfc7516#appendix-A.2 - TestVector { - claims: b"Live long and prosper.", - header: r#"{"alg":"RSA1_5","enc":"A128CBC-HS256"}"#, - recipient: r#" - { - "kty": "RSA", - "n": "sXchDaQebHnPiGvyDOAT4saGEUetSyo9MKLOoWFsueri23bOdgWp4Dy1WlUzewbgBHod5pcM9H95GQRV3JDXboIRROSBigeC5yjU1hGzHHyXss8UDprecbAYxknTcQkhslANGRUZmdTOQ5qTRsLAt6BTYuyvVRdhS8exSZEy_c4gs_7svlJJQ4H9_NxsiIoLwAEk7-Q3UXERGYw_75IDrGA84-lA_-Ct4eTlXHBIY2EaV7t7LjJaynVJCpkv4LKjTTAumiGUIuQhrNhZLuF_RJLqHpM2kgWFLU7-VTdL1VbC2tejvcI2BlMkEpk1BzBZI0KQB0GaDWFLN-aEAw3vRw", - "e": "AQAB", - "d": "VFCWOqXr8nvZNyaaJLXdnNPXZKRaWCjkU5Q2egQQpTBMwhprMzWzpR8Sxq1OPThh_J6MUD8Z35wky9b8eEO0pwNS8xlh1lOFRRBoNqDIKVOku0aZb-rynq8cxjDTLZQ6Fz7jSjR1Klop-YKaUHc9GsEofQqYruPhzSA-QgajZGPbE_0ZaVDJHfyd7UUBUKunFMScbflYAAOYJqVIVwaYR5zWEEceUjNnTNo_CVSj-VvXLO5VZfCUAVLgW4dpf1SrtZjSt34YLsRarSb127reG_DUwg9Ch-KyvjT1SkHgUWRVGcyly7uvVGRSDwsXypdrNinPA4jlhoNdizK2zF2CWQ", - "p": "9gY2w6I6S6L0juEKsbeDAwpd9WMfgqFoeA9vEyEUuk4kLwBKcoe1x4HG68ik918hdDSE9vDQSccA3xXHOAFOPJ8R9EeIAbTi1VwBYnbTp87X-xcPWlEPkrdoUKW60tgs1aNd_Nnc9LEVVPMS390zbFxt8TN_biaBgelNgbC95sM", - "q": "uKlCKvKv_ZJMVcdIs5vVSU_6cPtYI1ljWytExV_skstvRSNi9r66jdd9-yBhVfuG4shsp2j7rGnIio901RBeHo6TPKWVVykPu1iYhQXw1jIABfw-MVsN-3bQ76WLdt2SDxsHs7q7zPyUyHXmps7ycZ5c72wGkUwNOjYelmkiNS0", - "dp": "w0kZbV63cVRvVX6yk3C8cMxo2qCM4Y8nsq1lmMSYhG4EcL6FWbX5h9yuvngs4iLEFk6eALoUS4vIWEwcL4txw9LsWH_zKI-hwoReoP77cOdSL4AVcraHawlkpyd2TWjE5evgbhWtOxnZee3cXJBkAi64Ik6jZxbvk-RR3pEhnCs", - "dq": "o_8V14SezckO6CNLKs_btPdFiO9_kC1DsuUTd2LAfIIVeMZ7jn1Gus_Ff7B7IVx3p5KuBGOVF8L-qifLb6nQnLysgHDh132NDioZkhH7mI7hPG-PYE_odApKdnqECHWw0J-F0JWnUd6D2B_1TvF9mXA2Qx-iGYn8OVV1Bsmp6qU", - "qi": "eNho5yRBEBxhGBtQRww9QirZsB66TrfFReG_CcteI1aCneT0ELGhYlRlCtUkTRclIfuEPmNsNDPbLoLqqCVznFbvdB7x-Tl-m0l_eFTj2KiqwGqE9PZB9nNTwMVvH3VRRSLWACvPnSiwP8N5Usy-WRXS-V7TbpxIhvepTfE0NNo" - } - "#, - encoded: "eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0.UGhIOguC7IuEvf_NPVaXsGMoLOmwvc1GyqlIKOK1nN94nHPoltGRhWhw7Zx0-kFm1NJn8LE9XShH59_i8J0PH5ZZyNfGy2xGdULU7sHNF6Gp2vPLgNZ__deLKxGHZ7PcHALUzoOegEI-8E66jX2E4zyJKx-YxzZIItRzC5hlRirb6Y5Cl_p-ko3YvkkysZIFNPccxRU7qve1WYPxqbb2Yw8kZqa2rMWI5ng8OtvzlV7elprCbuPhcCdZ6XDP0_F8rkXds2vE4X-ncOIM8hAYHHi29NX0mcKiRaD0-D-ljQTP-cFPgwCp6X-nZZd9OHBv-B3oWh2TbqmScqXMR4gp_A.AxY8DCtDaGlsbGljb3RoZQ.KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY.9hH0vgRfYgPnAHOd8stkvw", - }, - // https://tools.ietf.org/html/rfc7516#appendix-A.3 - TestVector { - claims: b"Live long and prosper.", - header: r#"{"alg":"A128KW","enc":"A128CBC-HS256"}"#, - recipient: r#" - { - "kty": "oct", - "k": "GawgguFyGrWKav7AX4VKUg" - } - "#, - encoded: "eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0.6KB707dM9YTIgHtLvtgWQ8mKwboJW3of9locizkDTHzBC2IlrT1oOQ.AxY8DCtDaGlsbGljb3RoZQ.KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY.U0m_YmjN04DJvceFICbCVQ", - }, -] diff --git a/libjose/tests/fixtures/rfc7517.rs b/libjose/tests/fixtures/rfc7517.rs deleted file mode 100644 index 2627420517..0000000000 --- a/libjose/tests/fixtures/rfc7517.rs +++ /dev/null @@ -1,109 +0,0 @@ -[ - // https://tools.ietf.org/html/rfc7517#appendix-A.1 - TestVector::KeySet { - json: r#" - { - "keys": [ - { - "kty": "EC", - "crv": "P-256", - "x": "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", - "y": "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", - "use": "enc", - "kid": "1" - }, - { - "kty": "RSA", - "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", - "e": "AQAB", - "alg": "RS256", - "kid": "2011-04-29" - } - ] - } - "#, - }, - // https://tools.ietf.org/html/rfc7517#appendix-A.2 - TestVector::KeySet { - json: r#" - { - "keys": [ - { - "kty": "EC", - "crv": "P-256", - "x": "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", - "y": "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", - "d": "870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE", - "use": "enc", - "kid": "1" - }, - { - "kty": "RSA", - "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", - "e": "AQAB", - "d": "X4cTteJY_gn4FYPsXB8rdXix5vwsg1FLN5E3EaG6RJoVH-HLLKD9M7dx5oo7GURknchnrRweUkC7hT5fJLM0WbFAKNLWY2vv7B6NqXSzUvxT0_YSfqijwp3RTzlBaCxWp4doFk5N2o8Gy_nHNKroADIkJ46pRUohsXywbReAdYaMwFs9tv8d_cPVY3i07a3t8MN6TNwm0dSawm9v47UiCl3Sk5ZiG7xojPLu4sbg1U2jx4IBTNBznbJSzFHK66jT8bgkuqsk0GjskDJk19Z4qwjwbsnn4j2WBii3RL-Us2lGVkY8fkFzme1z0HbIkfz0Y6mqnOYtqc0X4jfcKoAC8Q", - "p": "83i-7IvMGXoMXCskv73TKr8637FiO7Z27zv8oj6pbWUQyLPQBQxtPVnwD20R-60eTDmD2ujnMt5PoqMrm8RfmNhVWDtjjMmCMjOpSXicFHj7XOuVIYQyqVWlWEh6dN36GVZYk93N8Bc9vY41xy8B9RzzOGVQzXvNEvn7O0nVbfs", - "q": "3dfOR9cuYq-0S-mkFLzgItgMEfFzB2q3hWehMuG0oCuqnb3vobLyumqjVZQO1dIrdwgTnCdpYzBcOfW5r370AFXjiWft_NGEiovonizhKpo9VVS78TzFgxkIdrecRezsZ-1kYd_s1qDbxtkDEgfAITAG9LUnADun4vIcb6yelxk", - "dp": "G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oimYwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_NmtuYZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0", - "dq": "s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUUvMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk", - "qi": "GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzgUIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rxyR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU", - "alg": "RS256", - "kid": "2011-04-29" - } - ] - } - "#, - }, - // https://tools.ietf.org/html/rfc7517#appendix-A.3 - TestVector::KeySet { - json: r#" - { - "keys": [ - { - "kty": "oct", - "alg": "A128KW", - "k": "GawgguFyGrWKav7AX4VKUg" - }, - { - "kty": "oct", - "k": "AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow", - "kid": "HMAC key used in JWS spec Appendix A.1 example" - } - ] - } - "#, - }, - // https://tools.ietf.org/html/rfc7517#appendix-B - TestVector::Key { - json: r#" - { - "kty": "RSA", - "use": "sig", - "kid": "1b94c", - "n": "vrjOfz9Ccdgx5nQudyhdoR17V-IubWMeOZCwX_jj0hgAsz2J_pqYW08PLbK_PdiVGKPrqzmDIsLI7sA25VEnHU1uCLNwBuUiCO11_-7dYbsr4iJmG0Qu2j8DsVyT1azpJC_NG84Ty5KKthuCaPod7iI7w0LK9orSMhBEwwZDCxTWq4aYWAchc8t-emd9qOvWtVMDC2BXksRngh6X5bUYLy6AyHKvj-nUy1wgzjYQDwHMTplCoLtU-o-8SNnZ1tmRoGE9uJkBLdh5gFENabWnU5m1ZqZPdwS-qo-meMvVfJb6jJVWRpl2SUtCnYG2C32qvbWbjZ_jBPD5eunqsIo1vQ", - "e": "AQAB", - "x5c": [ - "MIIDQjCCAiqgAwIBAgIGATz/FuLiMA0GCSqGSIb3DQEBBQUAMGIxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDTzEPMA0GA1UEBxMGRGVudmVyMRwwGgYDVQQKExNQaW5nIElkZW50aXR5IENvcnAuMRcwFQYDVQQDEw5CcmlhbiBDYW1wYmVsbDAeFw0xMzAyMjEyMzI5MTVaFw0xODA4MTQyMjI5MTVaMGIxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDTzEPMA0GA1UEBxMGRGVudmVyMRwwGgYDVQQKExNQaW5nIElkZW50aXR5IENvcnAuMRcwFQYDVQQDEw5CcmlhbiBDYW1wYmVsbDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL64zn8/QnHYMeZ0LncoXaEde1fiLm1jHjmQsF/449IYALM9if6amFtPDy2yvz3YlRij66s5gyLCyO7ANuVRJx1NbgizcAblIgjtdf/u3WG7K+IiZhtELto/A7Fck9Ws6SQvzRvOE8uSirYbgmj6He4iO8NCyvaK0jIQRMMGQwsU1quGmFgHIXPLfnpnfajr1rVTAwtgV5LEZ4Iel+W1GC8ugMhyr4/p1MtcIM42EA8BzE6ZQqC7VPqPvEjZ2dbZkaBhPbiZAS3YeYBRDWm1p1OZtWamT3cEvqqPpnjL1XyW+oyVVkaZdklLQp2Btgt9qr21m42f4wTw+Xrp6rCKNb0CAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAh8zGlfSlcI0o3rYDPBB07aXNswb4ECNIKG0CETTUxmXl9KUL+9gGlqCz5iWLOgWsnrcKcY0vXPG9J1r9AqBNTqNgHq2G03X09266X5CpOe1zFo+Owb1zxtp3PehFdfQJ610CDLEaS9V9Rqp17hCyybEpOGVwe8fnk+fbEL2Bo3UPGrpsHzUoaGpDftmWssZkhpBJKVMJyf/RuP2SmmaIzmnw9JiSlYhzo4tpzd5rFXhjRbg4zW9C+2qok+2+qDM1iJ684gPHMIY8aLWrdgQTxkumGmTqgawR+N5MDtdPTEQ0XfIBc2cJEUyMTY5MPvACWpkA6SdS4xSvdXK3IVfOWA==" - ] - } - "#, - }, - // https://tools.ietf.org/html/rfc7517#appendix-C - TestVector::Key { - json: r#" - { - "kty": "RSA", - "kid": "juliet@capulet.lit", - "use": "enc", - "n": "t6Q8PWSi1dkJj9hTP8hNYFlvadM7DflW9mWepOJhJ66w7nyoK1gPNqFMSQRyO125Gp-TEkodhWr0iujjHVx7BcV0llS4w5ACGgPrcAd6ZcSR0-Iqom-QFcNP8Sjg086MwoqQU_LYywlAGZ21WSdS_PERyGFiNnj3QQlO8Yns5jCtLCRwLHL0Pb1fEv45AuRIuUfVcPySBWYnDyGxvjYGDSM-AqWS9zIQ2ZilgT-GqUmipg0XOC0Cc20rgLe2ymLHjpHciCKVAbY5-L32-lSeZO-Os6U15_aXrk9Gw8cPUaX1_I8sLGuSiVdt3C_Fn2PZ3Z8i744FPFGGcG1qs2Wz-Q", - "e": "AQAB", - "d": "GRtbIQmhOZtyszfgKdg4u_N-R_mZGU_9k7JQ_jn1DnfTuMdSNprTeaSTyWfSNkuaAwnOEbIQVy1IQbWVV25NY3ybc_IhUJtfri7bAXYEReWaCl3hdlPKXy9UvqPYGR0kIXTQRqns-dVJ7jahlI7LyckrpTmrM8dWBo4_PMaenNnPiQgO0xnuToxutRZJfJvG4Ox4ka3GORQd9CsCZ2vsUDmsXOfUENOyMqADC6p1M3h33tsurY15k9qMSpG9OX_IJAXmxzAh_tWiZOwk2K4yxH9tS3Lq1yX8C1EWmeRDkK2ahecG85-oLKQt5VEpWHKmjOi_gJSdSgqcN96X52esAQ", - "p": "2rnSOV4hKSN8sS4CgcQHFbs08XboFDqKum3sc4h3GRxrTmQdl1ZK9uw-PIHfQP0FkxXVrx-WE-ZEbrqivH_2iCLUS7wAl6XvARt1KkIaUxPPSYB9yk31s0Q8UK96E3_OrADAYtAJs-M3JxCLfNgqh56HDnETTQhH3rCT5T3yJws", - "q": "1u_RiFDP7LBYh3N4GXLT9OpSKYP0uQZyiaZwBtOCBNJgQxaj10RWjsZu0c6Iedis4S7B_coSKB0Kj9PaPaBzg-IySRvvcQuPamQu66riMhjVtG6TlV8CLCYKrYl52ziqK0E_ym2QnkwsUX7eYTB7LbAHRK9GqocDE5B0f808I4s", - "dp": "KkMTWqBUefVwZ2_Dbj1pPQqyHSHjj90L5x_MOzqYAJMcLMZtbUtwKqvVDq3tbEo3ZIcohbDtt6SbfmWzggabpQxNxuBpoOOf_a_HgMXK_lhqigI4y_kqS1wY52IwjUn5rgRrJ-yYo1h41KR-vz2pYhEAeYrhttWtxVqLCRViD6c", - "dq": "AvfS0-gRxvn0bwJoMSnFxYcK1WnuEjQFluMGfwGitQBWtfZ1Er7t1xDkbN9GQTB9yqpDoYaN06H7CFtrkxhJIBQaj6nkF5KKS3TQtQ5qCzkOkmxIe3KRbBymXxkb5qwUpX5ELD5xFc6FeiafWYY63TmmEAu_lRFCOJ3xDea-ots", - "qi": "lSQi-w9CpyUReMErP1RsBLk7wNtOvs5EQpPqmuMvqW57NBUczScEoPwmUqqabu9V0-Py4dQ57_bapoKRu1R90bvuFnU63SHWEFglZQvJDMeAvmj4sm-Fp0oYu_neotgQ0hzbI5gry7ajdYy9-2lNx_76aBZoOUu9HCJ-UsfSOI8" - } - "#, - }, -] diff --git a/libjose/tests/fixtures/rfc7518.rs b/libjose/tests/fixtures/rfc7518.rs deleted file mode 100644 index 4dcd9a963d..0000000000 --- a/libjose/tests/fixtures/rfc7518.rs +++ /dev/null @@ -1,40 +0,0 @@ -[ - // https://tools.ietf.org/html/rfc7518#appendix-C - TestVector { - alice_jwk: r#" - { - "kty": "EC", - "crv": "P-256", - "x": "gI0GAILBdu7T53akrFmMyGcsF3n5dO7MmwNBHKW5SV0", - "y": "SLW_xSffzlPWrHEVI30DHM_4egVwt3NQqeUD7nMFpps", - "d": "0_NxaRPUMQoAJt50Gz8YiTr8gRTwyEaCumd-MToTmIo" - } - "#, - bob_jwk: r#" - { - "kty": "EC", - "crv": "P-256", - "x": "weNJy2HscCSM6AEDTDg04biOvhFhyyWvOHQfeF_PxMQ", - "y": "e8lnCO-AlStT-NJVX-crhB7QRYhiix03illJOVAOyck", - "d": "VEmDZpDXXK8p8N0Cndsxs924q6nS1RXFASRl6BfUqdw" - } - "#, - header: r#" - { - "alg": "ECDH-ES", - "enc": "A128GCM", - "apu": "QWxpY2U", - "apv": "Qm9i", - "epk": { - "kty": "EC", - "crv": "P-256", - "x": "gI0GAILBdu7T53akrFmMyGcsF3n5dO7MmwNBHKW5SV0", - "y": "SLW_xSffzlPWrHEVI30DHM_4egVwt3NQqeUD7nMFpps" - } - } - "#, - apu_bytes: b"Alice", - apv_bytes: b"Bob", - derived_key_b64: "VqqN6vgjbSBcIijNcacQGg", - }, -] diff --git a/libjose/tests/fixtures/rfc7638.rs b/libjose/tests/fixtures/rfc7638.rs deleted file mode 100644 index 7d6b4a3104..0000000000 --- a/libjose/tests/fixtures/rfc7638.rs +++ /dev/null @@ -1,15 +0,0 @@ -[ - // https://tools.ietf.org/html/rfc7638#section-3.1 - TestVector { - jwk_json: r#" - { - "kty": "RSA", - "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", - "e": "AQAB", - "alg": "RS256", - "kid": "2011-04-29" - } - "#, - thumbprint_b64: "NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs", - }, -] diff --git a/libjose/tests/fixtures/rfc7797.rs b/libjose/tests/fixtures/rfc7797.rs deleted file mode 100644 index 58c6eb8c64..0000000000 --- a/libjose/tests/fixtures/rfc7797.rs +++ /dev/null @@ -1,28 +0,0 @@ -[ - // https://tools.ietf.org/html/rfc7797#section-4.1 - TestVector { - detach: false, - header: br#"{"alg":"HS256"}"#, - encoded: b"eyJhbGciOiJIUzI1NiJ9.JC4wMg.5mvfOroL-g7HyqJoozehmsaqmvTYGEq5jTI1gVvoEoQ", - payload: b"$.02", - public_key: r#" - { - "kty": "oct", - "k": "AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow" - } - "#, - }, - // https://tools.ietf.org/html/rfc7797#section-4.2 - TestVector { - detach: true, - header: br#"{"alg":"HS256","b64":false,"crit":["b64"]}"#, - encoded: b"eyJhbGciOiJIUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..A5dxf2s96_n5FLueVuW1Z_vh161FwXZC4YLPff6dmDY", - payload: b"$.02", - public_key: r#" - { - "kty": "oct", - "k": "AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow" - } - "#, - }, -] diff --git a/libjose/tests/fixtures/rfc8037_ed25519.rs b/libjose/tests/fixtures/rfc8037_ed25519.rs deleted file mode 100644 index d74e050fec..0000000000 --- a/libjose/tests/fixtures/rfc8037_ed25519.rs +++ /dev/null @@ -1,24 +0,0 @@ -[ - // https://tools.ietf.org/html/rfc8037#appendix-A.4 - TestVector { - private_jwk: r#" - { - "kty": "OKP", - "crv": "Ed25519", - "d": "nWGxne_9WmC6hEr0kuwsxERJxWl7MmkZcDusAxyuf2A", - "x": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo" - } - "#, - public_jwk: r#" - { - "kty": "OKP", - "crv": "Ed25519", - "x": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo" - } - "#, - thumbprint_b64: "kPrK_qmxVWaYVA9wwBF6Iuo3vVzz7TxHCTwXBygrS4k", - header: r#"{"alg":"EdDSA"}"#, - payload: "Example of Ed25519 signing", - encoded: "eyJhbGciOiJFZERTQSJ9.RXhhbXBsZSBvZiBFZDI1NTE5IHNpZ25pbmc.hgyY0il_MGCjP0JzlnLWG1PPOt7-09PGcvMg3AIbQR6dWbhijcNR4ki4iylGjg5BhVsPt9g7sVvpAr_MuM0KAg", - }, -] diff --git a/libjose/tests/fixtures/rfc8037_x25519.rs b/libjose/tests/fixtures/rfc8037_x25519.rs deleted file mode 100644 index b8df760924..0000000000 --- a/libjose/tests/fixtures/rfc8037_x25519.rs +++ /dev/null @@ -1,24 +0,0 @@ -[ - // https://tools.ietf.org/html/rfc8037#appendix-A.6 - TestVector { - public_jwk: r#" - { - "kty": "OKP", - "crv": "X25519", - "kid":"Bob", - "x": "3p7bfXt9wbTTW2HC7OQ1Nz-DQ8hbeGdNrfx-FG-IK08" - } - "#, - public_key: &[0xde, 0x9e, 0xdb, 0x7d, 0x7b, 0x7d, 0xc1, 0xb4, 0xd3, 0x5b, 0x61, 0xc2, 0xec, 0xe4, 0x35, 0x37, 0x3f, 0x83, 0x43, 0xc8, 0x5b, 0x78, 0x67, 0x4d, 0xad, 0xfc, 0x7e, 0x14, 0x6f, 0x88, 0x2b, 0x4f], - eph_public_jwk: r#" - { - "kty": "OKP", - "crv": "X25519", - "x": "hSDwCYkwp1R0i33ctD73Wg2_Og0mOBr066SpjqqbTmo" - } - "#, - eph_public_key: &[0x85, 0x20, 0xf0, 0x09, 0x89, 0x30, 0xa7, 0x54, 0x74, 0x8b, 0x7d, 0xdc, 0xb4, 0x3e, 0xf7, 0x5a, 0x0d, 0xbf, 0x3a, 0x0d, 0x26, 0x38, 0x1a, 0xf4, 0xeb, 0xa4, 0xa9, 0x8e, 0xaa, 0x9b, 0x4e, 0x6a], - eph_secret_key: &[0x77, 0x07, 0x6d, 0x0a, 0x73, 0x18, 0xa5, 0x7d, 0x3c, 0x16, 0xc1, 0x72, 0x51, 0xb2, 0x66, 0x45, 0xdf, 0x4c, 0x2f, 0x87, 0xeb, 0xc0, 0x99, 0x2a, 0xb1, 0x77, 0xfb, 0xa5, 0x1d, 0xb9, 0x2c, 0x2a], - z: &[0x4a, 0x5d, 0x9d, 0x5b, 0xa4, 0xce, 0x2d, 0xe1, 0x72, 0x8e, 0x3b, 0xf4, 0x80, 0x35, 0x0f, 0x25, 0xe0, 0x7e, 0x21, 0xc9, 0x47, 0xd1, 0x9e, 0x33, 0x76, 0xf0, 0x9b, 0x3c, 0x1e, 0x16, 0x17, 0x42], - }, -] diff --git a/libjose/tests/fixtures/rfc8037_x448.rs b/libjose/tests/fixtures/rfc8037_x448.rs deleted file mode 100644 index f448adebf7..0000000000 --- a/libjose/tests/fixtures/rfc8037_x448.rs +++ /dev/null @@ -1,24 +0,0 @@ -[ - // https://tools.ietf.org/html/rfc8037#appendix-A.7 - TestVector { - public_jwk: r#" - { - "kty": "OKP", - "crv": "X448", - "kid": "Dave", - "x": "PreoKbDNIPW8_AtZm2_sz22kYnEHvbDU80W0MCfYuXL8PjT7QjKhPKcG3LV67D2uB73BxnvzNgk" - } - "#, - public_key: &[0x3e, 0xb7, 0xa8, 0x29, 0xb0, 0xcd, 0x20, 0xf5, 0xbc, 0xfc, 0x0b, 0x59, 0x9b, 0x6f, 0xec, 0xcf, 0x6d, 0xa4, 0x62, 0x71, 0x07, 0xbd, 0xb0, 0xd4, 0xf3, 0x45, 0xb4, 0x30, 0x27, 0xd8, 0xb9, 0x72, 0xfc, 0x3e, 0x34, 0xfb, 0x42, 0x32, 0xa1, 0x3c, 0xa7, 0x06, 0xdc, 0xb5, 0x7a, 0xec, 0x3d, 0xae, 0x07, 0xbd, 0xc1, 0xc6, 0x7b, 0xf3, 0x36, 0x09], - eph_public_jwk: r#" - { - "kty": "OKP", - "crv": "X448", - "x": "mwj3zDG34-Z9ItWuoSEHSic70rg94Jxj-qc9LCLF2bvINmRyQdlT1AxbEtqIEg1TF3-A5TLEH6A" - } - "#, - eph_public_key: &[0x9b, 0x08, 0xf7, 0xcc, 0x31, 0xb7, 0xe3, 0xe6, 0x7d, 0x22, 0xd5, 0xae, 0xa1, 0x21, 0x07, 0x4a, 0x27, 0x3b, 0xd2, 0xb8, 0x3d, 0xe0, 0x9c, 0x63, 0xfa, 0xa7, 0x3d, 0x2c, 0x22, 0xc5, 0xd9, 0xbb, 0xc8, 0x36, 0x64, 0x72, 0x41, 0xd9, 0x53, 0xd4, 0x0c, 0x5b, 0x12, 0xda, 0x88, 0x12, 0x0d, 0x53, 0x17, 0x7f, 0x80, 0xe5, 0x32, 0xc4, 0x1f, 0xa0], - eph_secret_key: &[0x9a, 0x8f, 0x49, 0x25, 0xd1, 0x51, 0x9f, 0x57, 0x75, 0xcf, 0x46, 0xb0, 0x4b, 0x58, 0x00, 0xd4, 0xee, 0x9e, 0xe8, 0xba, 0xe8, 0xbc, 0x55, 0x65, 0xd4, 0x98, 0xc2, 0x8d, 0xd9, 0xc9, 0xba, 0xf5, 0x74, 0xa9, 0x41, 0x97, 0x44, 0x89, 0x73, 0x91, 0x00, 0x63, 0x82, 0xa6, 0xf1, 0x27, 0xab, 0x1d, 0x9a, 0xc2, 0xd8, 0xc0, 0xa5, 0x98, 0x72, 0x6b], - z: &[0x07, 0xff, 0xf4, 0x18, 0x1a, 0xc6, 0xcc, 0x95, 0xec, 0x1c, 0x16, 0xa9, 0x4a, 0x0f, 0x74, 0xd1, 0x2d, 0xa2, 0x32, 0xce, 0x40, 0xa7, 0x75, 0x52, 0x28, 0x1d, 0x28, 0x2b, 0xb6, 0x0c, 0x0b, 0x56, 0xfd, 0x24, 0x64, 0xc3, 0x35, 0x54, 0x39, 0x36, 0x52, 0x1c, 0x24, 0x40, 0x30, 0x85, 0xd5, 0x9a, 0x44, 0x9a, 0x50, 0x37, 0x51, 0x4a, 0x87, 0x9d], - } -] diff --git a/libjose/tests/jwe.rs b/libjose/tests/jwe.rs deleted file mode 100644 index 88cfb3c9b7..0000000000 --- a/libjose/tests/jwe.rs +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use libjose::jwe::Decoder; -use libjose::jwe::Encoder; -use libjose::jwe::JweAlgorithm; -use libjose::jwe::JweAlgorithm::*; -use libjose::jwe::JweEncryption; -use libjose::jwe::JweFormat; -use libjose::jwe::JweHeader; -use libjose::jwe::Token; -use libjose::jwk::Jwk; - -const __RSA: bool = cfg!(not(feature = "test-rsa-enc")); - -const CLAIMS: &[u8] = b"libjose"; - -fn roundtrip(algorithm: JweAlgorithm, encryption: JweEncryption) -> Result<(), Box> { - let header: JweHeader = JweHeader::new(algorithm, encryption); - - let secret: Jwk = Jwk::random((algorithm, encryption))?; - if secret.kty().name() == "oct" { - // TODO: Make a proper fix for this. - return Err(String::from("cannot call to_public when kty = oct").into()); - }; - let public: Jwk = secret.to_public(); - - let secret2: Jwk = Jwk::random((algorithm, encryption))?; - if secret.kty().name() == "oct" { - // TODO: Make a proper fix for this. - return Err(String::from("cannot call to_public when kty = oct").into()); - }; - let public2: Jwk = secret2.to_public(); - - let mut encoder: Encoder = Encoder::new().protected(&header).secret(&secret2).recipient(&public); - let mut decoder: Decoder = Decoder::new(&secret).public(&public2); - - let encoded: String = encoder.encode(CLAIMS)?; - let decoded: Token = decoder.decode(encoded.as_bytes())?; - - assert_eq!(decoded.0.alg(), header.alg()); - assert_eq!(decoded.0.enc(), header.enc()); - assert_eq!(decoded.1, CLAIMS); - - encoder = encoder.format(JweFormat::General); - decoder = decoder.format(JweFormat::General); - - let encoded: String = encoder.encode(CLAIMS)?; - let decoded: Token = decoder.decode(encoded.as_bytes())?; - - assert_eq!(decoded.0.alg(), header.alg()); - assert_eq!(decoded.0.enc(), header.enc()); - assert_eq!(decoded.1, CLAIMS); - - encoder = encoder.format(JweFormat::Flatten); - decoder = decoder.format(JweFormat::Flatten); - - let encoded: String = encoder.encode(CLAIMS)?; - let decoded: Token = decoder.decode(encoded.as_bytes())?; - - assert_eq!(decoded.0.alg(), header.alg()); - assert_eq!(decoded.0.enc(), header.enc()); - assert_eq!(decoded.1, CLAIMS); - - Ok(()) -} - -#[test] -fn test_jwe_roundtrip() { - for alg in JweAlgorithm::ALL { - // skip unless opted-in - rsa is SLOWWWW - if __RSA && matches!(alg, RSA1_5 | RSA_OAEP | RSA_OAEP_256 | RSA_OAEP_384 | RSA_OAEP_512) { - continue; - } - - for enc in JweEncryption::ALL { - let result = roundtrip(*alg, *enc); - assert!( - result.is_ok() - || result - .err() - .unwrap() - .to_string() - .contains("cannot call to_public when kty = oct") - ); - } - } -} diff --git a/libjose/tests/jws.rs b/libjose/tests/jws.rs deleted file mode 100644 index 2a12faf5a5..0000000000 --- a/libjose/tests/jws.rs +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use libjose::jwk::Jwk; -use libjose::jws::Decoder; -use libjose::jws::Encoder; -use libjose::jws::JwsAlgorithm; -use libjose::jws::JwsAlgorithm::*; -use libjose::jws::JwsFormat; -use libjose::jws::JwsHeader; -use libjose::jws::Token; - -const __RSA: bool = cfg!(not(feature = "test-rsa-sig")); - -const CLAIMS: &[u8] = b"libjose"; - -fn roundtrip(algorithm: JwsAlgorithm) -> Result<(), Box> { - let header: JwsHeader = JwsHeader::new(algorithm); - let secret: Jwk = Jwk::random(algorithm)?; - if secret.kty().name() == "oct" { - // TODO: Make a proper fix for this. - return Err(String::from("cannot call to_public when kty = oct").into()); - }; - - let public: Jwk = secret.to_public(); - - let mut encoder: Encoder<'_> = Encoder::new().recipient((&secret, &header)); - let mut decoder: Decoder<'_, '_> = Decoder::new(&public); - - let encoded: String = encoder.encode(CLAIMS)?; - let decoded: Token<'_> = decoder.decode(encoded.as_bytes())?; - - assert_eq!(decoded.protected.unwrap(), header); - assert_eq!(decoded.claims, CLAIMS); - - encoder = encoder.format(JwsFormat::General); - decoder = decoder.format(JwsFormat::General); - - let encoded: String = encoder.encode(CLAIMS)?; - let decoded: Token<'_> = decoder.decode(encoded.as_bytes())?; - - assert_eq!(decoded.protected.unwrap(), header); - assert_eq!(decoded.claims, CLAIMS); - - encoder = encoder.format(JwsFormat::Flatten); - decoder = decoder.format(JwsFormat::Flatten); - - let encoded: String = encoder.encode(CLAIMS)?; - let decoded: Token<'_> = decoder.decode(encoded.as_bytes())?; - - assert_eq!(decoded.protected.unwrap(), header); - assert_eq!(decoded.claims, CLAIMS); - - Ok(()) -} - -#[test] -fn test_jws_roundtrip() { - for alg in JwsAlgorithm::ALL { - // skip - not supported - if matches!(alg, ES384 | ES512 | NONE) { - continue; - } - - // skip unless opted-in - rsa is SLOWWWW - if __RSA && matches!(alg, RS256 | RS384 | RS512 | PS256 | PS384 | PS512) { - continue; - } - - let result = roundtrip(*alg); - assert!( - result.is_ok() - || result - .err() - .unwrap() - .to_string() - .contains("cannot call to_public when kty = oct") - ); - } -} diff --git a/libjose/tests/rfc.rs b/libjose/tests/rfc.rs deleted file mode 100644 index b3b3fc51bc..0000000000 --- a/libjose/tests/rfc.rs +++ /dev/null @@ -1,312 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use libjose::jwe; -use libjose::jwe::JweHeader; -use libjose::jwk::EcxCurve; -use libjose::jwk::Jwk; -use libjose::jwk::JwkParamsOkp; -use libjose::jwk::JwkSet; -use libjose::jws; -use libjose::jws::JwsHeader; -use libjose::utils::diffie_hellman; -use libjose::utils::encode_b64; -use libjose::utils::Secret; -use serde_json::Value; - -#[test] -fn test_rfc7515() { - struct TestVector { - deterministic: bool, - header: &'static str, - claims: &'static [u8], - encoded: &'static [u8], - private_key: &'static str, - } - - static TVS: &[TestVector] = &include!("fixtures/rfc7515.rs"); - - for tv in TVS { - let header: JwsHeader = serde_json::from_str(tv.header).unwrap(); - let jwk: Jwk = serde_json::from_str(tv.private_key).unwrap(); - - if tv.deterministic { - let encoded: String = jws::Encoder::new() - .recipient((&jwk, &header)) - .encode(tv.claims) - .unwrap(); - - assert_eq!(encoded.as_bytes(), tv.encoded); - } - - let decoded: _ = jws::Decoder::new(&jwk).decode(tv.encoded).unwrap(); - - assert_eq!(decoded.protected.unwrap(), header); - assert_eq!(decoded.claims, tv.claims); - } -} - -#[test] -fn test_rfc7516() { - struct TestVector { - claims: &'static [u8], - header: &'static str, - recipient: &'static str, - encoded: &'static str, - } - - static TVS: &[TestVector] = &include!("fixtures/rfc7516.rs"); - - for tv in TVS { - let secret: Jwk = serde_json::from_str(tv.recipient).unwrap(); - let secret: Secret<'_> = Secret::Jwk(&secret); - - let decoded: jwe::Token = jwe::Decoder::new(secret).decode(tv.encoded.as_bytes()).unwrap(); - let header: JweHeader = serde_json::from_str(tv.header).unwrap(); - - assert_eq!(decoded.0, header); - assert_eq!(decoded.1, tv.claims); - } -} - -#[test] -fn test_rfc7517() { - enum TestVector { - KeySet { json: &'static str }, - Key { json: &'static str }, - } - - static TVS: &[TestVector] = &include!("fixtures/rfc7517.rs"); - - for tv in TVS { - match tv { - TestVector::KeySet { json } => { - let value: Value = serde_json::from_str(json).unwrap(); - let jwks: JwkSet = serde_json::from_str(json).unwrap(); - - for (index, jwk) in jwks.iter().enumerate() { - let ser: Value = serde_json::to_value(jwk).unwrap(); - assert_eq!(ser, value["keys"][index]); - } - } - TestVector::Key { json } => { - let value: Value = serde_json::from_str(json).unwrap(); - let jwk: Jwk = serde_json::from_str(json).unwrap(); - let ser: Value = serde_json::to_value(&jwk).unwrap(); - - assert_eq!(ser, value); - } - } - } -} - -#[test] -fn test_rfc7518() { - struct TestVector { - alice_jwk: &'static str, - bob_jwk: &'static str, - header: &'static str, - apu_bytes: &'static [u8], - apv_bytes: &'static [u8], - derived_key_b64: &'static str, - } - - static TVS: &[TestVector] = &include!("fixtures/rfc7518.rs"); - - for tv in TVS { - let alice_jwk: Jwk = serde_json::from_str(tv.alice_jwk).unwrap(); - let bob_jwk: Jwk = serde_json::from_str(tv.bob_jwk).unwrap(); - let epk_jwk: Jwk = alice_jwk.to_public(); - - let header: JweHeader = serde_json::from_str(tv.header).unwrap(); - assert_eq!(header.apu().unwrap(), encode_b64(tv.apu_bytes)); - assert_eq!(header.apv().unwrap(), encode_b64(tv.apv_bytes)); - assert_eq!(header.epk().unwrap(), &epk_jwk); - - let encryption_key: _ = jwe::Decoder::new(&bob_jwk) - .ecdh_curve(bob_jwk.try_ec_curve().unwrap()) - .__test_decrypt_key(&header) - .unwrap(); - - assert_eq!(encode_b64(encryption_key), tv.derived_key_b64); - } -} - -#[test] -fn test_rfc7638() { - struct TestVector { - jwk_json: &'static str, - thumbprint_b64: &'static str, - } - - static TVS: &[TestVector] = &include!("fixtures/rfc7638.rs"); - - for tv in TVS { - let key: Jwk = serde_json::from_str(tv.jwk_json).unwrap(); - let kid: String = key.thumbprint_b64().unwrap(); - - assert_eq!(kid, tv.thumbprint_b64); - } -} - -#[test] -fn test_rfc7797() { - struct TestVector { - detach: bool, - header: &'static [u8], - encoded: &'static [u8], - payload: &'static [u8], - public_key: &'static str, - } - - static TVS: &[TestVector] = &include!("fixtures/rfc7797.rs"); - - for tv in TVS { - let header: JwsHeader = serde_json::from_slice(tv.header).unwrap(); - let jwk: Jwk = serde_json::from_str(tv.public_key).unwrap(); - - let mut decoder: jws::Decoder = jws::Decoder::new(&jwk); - - if tv.detach { - decoder = decoder.payload(tv.payload); - } - - let decoded: _ = decoder.critical("b64").decode(tv.encoded).unwrap(); - - assert_eq!(decoded.protected.unwrap(), header); - assert_eq!(decoded.claims, tv.payload); - } -} - -#[test] -fn test_rfc8037_ed25519() { - struct TestVector { - private_jwk: &'static str, - public_jwk: &'static str, - thumbprint_b64: &'static str, - header: &'static str, - payload: &'static str, - encoded: &'static str, - } - - static TVS: &[TestVector] = &include!("fixtures/rfc8037_ed25519.rs"); - - for tv in TVS { - let secret: Jwk = serde_json::from_str(tv.private_jwk).unwrap(); - let public: Jwk = serde_json::from_str(tv.public_jwk).unwrap(); - - assert_eq!(secret.thumbprint_b64().unwrap(), tv.thumbprint_b64); - assert_eq!(public.thumbprint_b64().unwrap(), tv.thumbprint_b64); - - let header: JwsHeader = serde_json::from_str(tv.header).unwrap(); - let encoded: String = jws::Encoder::new() - .recipient((&secret, &header)) - .encode(tv.payload.as_bytes()) - .unwrap(); - - assert_eq!(encoded, tv.encoded); - - let decoded: jws::Token = jws::Decoder::new(&public).decode(encoded.as_bytes()).unwrap(); - - assert_eq!(decoded.protected.unwrap(), header); - assert_eq!(decoded.claims, tv.payload.as_bytes()); - } -} - -#[test] -fn test_rfc8037_x25519() { - struct TestVector { - public_jwk: &'static str, - public_key: &'static [u8], - eph_public_jwk: &'static str, - eph_public_key: &'static [u8], - eph_secret_key: &'static [u8], - z: &'static [u8], - } - - static TVS: &[TestVector] = &include!("fixtures/rfc8037_x25519.rs"); - - for tv in TVS { - let public: Jwk = serde_json::from_str(tv.public_jwk).unwrap(); - - assert_eq!( - EcxCurve::X25519, - public.try_okp_params().unwrap().try_ecx_curve().unwrap() - ); - - assert_eq!( - tv.public_key, - &Secret::Jwk(&public).to_x25519_public().unwrap().to_bytes()[..] - ); - - let eph_public: Jwk = serde_json::from_str(tv.eph_public_jwk).unwrap(); - - assert_eq!( - EcxCurve::X25519, - eph_public.try_okp_params().unwrap().try_ecx_curve().unwrap() - ); - - assert_eq!( - tv.eph_public_key, - &Secret::Jwk(&eph_public).to_x25519_public().unwrap().to_bytes()[..] - ); - - let eph_secret: Jwk = Jwk::from_params(JwkParamsOkp { - crv: EcxCurve::X25519.name().to_string(), - x: encode_b64(tv.eph_public_key), - d: Some(encode_b64(tv.eph_secret_key)), - }); - - assert_eq!(eph_public, eph_secret.to_public()); - assert_eq!(tv.z, diffie_hellman(EcxCurve::X25519, &public, &eph_secret).unwrap()); - } -} - -#[test] -fn test_rfc8037_x448() { - struct TestVector { - public_jwk: &'static str, - public_key: &'static [u8], - eph_public_jwk: &'static str, - eph_public_key: &'static [u8], - eph_secret_key: &'static [u8], - z: &'static [u8], - } - - static TVS: &[TestVector] = &include!("fixtures/rfc8037_x448.rs"); - - for tv in TVS { - let public: Jwk = serde_json::from_str(tv.public_jwk).unwrap(); - - assert_eq!( - EcxCurve::X448, - public.try_okp_params().unwrap().try_ecx_curve().unwrap() - ); - - assert_eq!( - tv.public_key, - &Secret::Jwk(&public).to_x448_public().unwrap().to_bytes()[..] - ); - - let eph_public: Jwk = serde_json::from_str(tv.eph_public_jwk).unwrap(); - - assert_eq!( - EcxCurve::X448, - eph_public.try_okp_params().unwrap().try_ecx_curve().unwrap() - ); - - assert_eq!( - tv.eph_public_key, - &Secret::Jwk(&eph_public).to_x448_public().unwrap().to_bytes()[..] - ); - - let eph_secret: Jwk = Jwk::from_params(JwkParamsOkp { - crv: EcxCurve::X448.name().to_string(), - x: encode_b64(tv.eph_public_key), - d: Some(encode_b64(tv.eph_secret_key)), - }); - - assert_eq!(eph_public, eph_secret.to_public()); - assert_eq!(tv.z, diffie_hellman(EcxCurve::X448, &public, &eph_secret).unwrap()); - } -}