Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a RustCrypto based backend for cryptographic COSE operations #22

Merged
merged 4 commits into from
Aug 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,23 @@ jobs:
with:
command: test

test_all_features:
name: Test Suite (all features)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: true
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
- uses: actions-rs/cargo@v1
with:
command: test
args: --all-features

test_nostd:
name: Test Suite (no_std)
runs-on: ubuntu-latest
Expand Down
29 changes: 28 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@ exclude = [
[features]
default = ["std", "openssl"]
std = ["serde/std", "ciborium/std", "serde_bytes/std", "erased-serde/std", "derive_builder/std"]
rustcrypto = ["rustcrypto-aes-gcm", "rustcrypto-aes-kw", "rustcrypto-ecdsa", "rustcrypto-hmac"]
rustcrypto-encrypt = ["rustcrypto-aes-gcm"]
rustcrypto-sign = ["rustcrypto-ecdsa"]
rustcrypto-key-distribution = ["rustcrypto-aes-kw"]
rustcrypto-mac = ["rustcrypto-hmac"]
rustcrypto-aes-gcm = ["dep:aes-gcm", "dep:typenum", "dep:aead", "dep:aes"]
rustcrypto-aes-kw = ["dep:aes-kw", "dep:aes", "dep:typenum", "dep:crypto-common"]
rustcrypto-ecdsa = ["dep:ecdsa", "dep:p256", "dep:p384", "dep:digest", "dep:sha2", "dep:elliptic-curve"]
rustcrypto-hmac = ["dep:hmac", "dep:digest", "dep:sha2"]

[dependencies]
serde = { version = "1.0", default-features = false, features = ["derive"] }
Expand All @@ -31,10 +40,28 @@ enumflags2 = { version = "^0.7", default-features = false }
rand = { version = "^0.8", default-features = false }
openssl = { version = "^0.10", optional = true }
lazy_static = "1.4.0"
aes-gcm = { version = "0.10.3", optional = true, default-features = false, features = ["alloc", "aes"] }
typenum = { version = "1.17.0", optional = true, default-features = false, features = ["const-generics"] }
crypto-common = { version = "0.1.6", optional = true, default-features = false }
aead = { version = "0.5.2", optional = true, default-features = false }
aes-kw = { version = "0.2.1", optional = true, default-features = false, features = ["alloc"] }
aes = { version = "0.8.4", optional = true, default-features = false }
hmac = { version = "0.12.1", optional = true, default-features = false }
digest = { version = "0.10.7", optional = true, default-features = false }
sha2 = { version = "0.10.8", optional = true, default-features = false }
elliptic-curve = { version = "0.13.8", default-features = false, optional = true }
ecdsa = { version = "0.16.9", optional = true, default-features = false, features = ["sha2"] }
p256 = { version = "0.13.2", optional = true, default-features = false, features = ["alloc", "ecdsa", "arithmetic"] }
p384 = { version = "0.13.0", optional = true, default-features = false, features = ["alloc", "ecdsa", "arithmetic"] }
# P-521 must implement DigestPrimitive in order to be usable in ECDSA, which was only recently added and is not released yet.
# p521 = { version = "0.14.0-pre.1", optional = true }

[dev-dependencies]
hex = { version = "^0.4", features = ["serde"] }
base64 = "0.22.1"
rstest = "0.21.0"
rstest = "0.22.0"
serde_json = "1.0.118"
rand = { version = "^0.8", default-features = false, features = ["std_rng", "std"] }

[build-dependencies]
cfg_aliases = "0.2.1"
45 changes: 45 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright (c) 2024 The NAMIB Project Developers.
* Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
* https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
* <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
* option. This file may not be copied, modified, or distributed
* except according to those terms.
*
* SPDX-License-Identifier: MIT OR Apache-2.0
*/
use cfg_aliases::cfg_aliases;

fn main() {
// Set up some aliases for conditional compilation (in order to avoid repetition).
cfg_aliases! {
rustcrypto_encrypt_base: {
any(
feature = "rustcrypto-aes-gcm"
)
},
rustcrypto_sign_base: {
any(
feature = "rustcrypto-ecdsa"
)
},
rustcrypto_key_distribution_base: {
any(
feature = "rustcrypto-aes-kw"
)
},
rustcrypto_mac_base: {
any(
feature = "rustcrypto-hmac"
)
},
rustcrypto_base: {
any(
rustcrypto_encrypt_base,
rustcrypto_sign_base,
rustcrypto_key_distribution_base,
rustcrypto_mac_base
)
},
}
}
2 changes: 1 addition & 1 deletion src/common/cbor_map/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 The NAMIB Project Developers.
* Copyright (c) 2022, 2024 The NAMIB Project Developers.
* Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
* https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
* <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
Expand Down
2 changes: 1 addition & 1 deletion src/common/cbor_values/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 The NAMIB Project Developers.
* Copyright (c) 2022, 2024 The NAMIB Project Developers.
* Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
* https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
* <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
Expand Down
2 changes: 1 addition & 1 deletion src/common/cbor_values/tests.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 The NAMIB Project Developers.
* Copyright (c) 2022, 2024 The NAMIB Project Developers.
* Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
* https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
* <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
Expand Down
2 changes: 1 addition & 1 deletion src/common/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 The NAMIB Project Developers.
* Copyright (c) 2022, 2024 The NAMIB Project Developers.
* Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
* https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
* <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
Expand Down
2 changes: 1 addition & 1 deletion src/common/scope/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 The NAMIB Project Developers.
* Copyright (c) 2022, 2024 The NAMIB Project Developers.
* Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
* https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
* <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
Expand Down
2 changes: 1 addition & 1 deletion src/common/scope/tests.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 The NAMIB Project Developers.
* Copyright (c) 2022, 2024 The NAMIB Project Developers.
* Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
* https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
* <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
Expand Down
2 changes: 1 addition & 1 deletion src/common/test_helper.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 The NAMIB Project Developers.
* Copyright (c) 2022-2024 The NAMIB Project Developers.
* Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
* https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
* <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
Expand Down
2 changes: 1 addition & 1 deletion src/endpoints/creation_hint/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 The NAMIB Project Developers.
* Copyright (c) 2022, 2024 The NAMIB Project Developers.
* Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
* https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
* <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
Expand Down
2 changes: 1 addition & 1 deletion src/endpoints/creation_hint/tests.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 The NAMIB Project Developers.
* Copyright (c) 2022, 2024 The NAMIB Project Developers.
* Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
* https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
* <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
Expand Down
2 changes: 1 addition & 1 deletion src/endpoints/token_req/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 The NAMIB Project Developers.
* Copyright (c) 2022, 2024 The NAMIB Project Developers.
* Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
* https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
* <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
Expand Down
2 changes: 1 addition & 1 deletion src/endpoints/token_req/tests.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 The NAMIB Project Developers.
* Copyright (c) 2022, 2024 The NAMIB Project Developers.
* Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
* https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
* <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
Expand Down
2 changes: 1 addition & 1 deletion src/error/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 The NAMIB Project Developers.
* Copyright (c) 2022, 2024 The NAMIB Project Developers.
* Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
* https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
* <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 The NAMIB Project Developers.
* Copyright (c) 2022-2024 The NAMIB Project Developers.
* Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
* https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
* <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
Expand Down
4 changes: 4 additions & 0 deletions src/token/cose/crypto_impl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,7 @@
/// Cryptographic backend based on the OpenSSL library (accessed using the `openssl` crate).
#[cfg(feature = "openssl")]
pub mod openssl;

/// Cryptographic backend based on the RustCrypto collection of crates.
#[cfg(rustcrypto_base)]
pub mod rustcrypto;
1 change: 1 addition & 0 deletions src/token/cose/crypto_impl/openssl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ impl From<openssl::aes::KeyError> for CoseCipherError<CoseOpensslCipherError> {
///
/// Generic properties of this backend:
/// - [ ] Can derive EC public key components if only the private component (d) is present.
/// - [ ] Can work with compressed EC public keys (EC keys using point compression)
///
/// Algorithm support:
/// - Signature Algorithms (for COSE_Sign and COSE_Sign1)
Expand Down
110 changes: 110 additions & 0 deletions src/token/cose/crypto_impl/rustcrypto/encrypt/aes_gcm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
* Copyright (c) 2024 The NAMIB Project Developers.
* Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
* https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
* <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
* option. This file may not be copied, modified, or distributed
* except according to those terms.
*
* SPDX-License-Identifier: MIT OR Apache-2.0
*/
use aead::{Aead, AeadCore, Key, KeyInit, Nonce, Payload};
use aes::Aes192;
use aes_gcm::{Aes128Gcm, Aes256Gcm, AesGcm};
use coset::{iana, Algorithm};
use rand::CryptoRng;
use rand::RngCore;
use typenum::consts::U12;

use crate::error::CoseCipherError;
use crate::token::cose::{CoseSymmetricKey, CryptoBackend};

use super::RustCryptoContext;

impl<RNG: RngCore + CryptoRng> RustCryptoContext<RNG> {
/// Perform an AEAD encryption operation on `plaintext` and the additional authenticated
/// data `aad` using the given `iv` and `key`.
fn encrypt_aead<AEAD: Aead + AeadCore + KeyInit>(
key: &CoseSymmetricKey<'_, <RustCryptoContext<RNG> as CryptoBackend>::Error>,
plaintext: &[u8],
aad: &[u8],
iv: &[u8],
) -> Result<Vec<u8>, CoseCipherError<<Self as CryptoBackend>::Error>> {
let aes_key = Key::<AEAD>::from_slice(key.k);
let cipher = AEAD::new(aes_key);
let nonce = Nonce::<AEAD>::from_slice(iv);
let payload = Payload {
msg: plaintext,
aad,
};
cipher
.encrypt(nonce, payload)
.map_err(CoseCipherError::from)
}

/// Perform an AEAD decryption operation on `ciphertext` and the additional authenticated
/// data `aad` using the given `iv` and `key`.
fn decrypt_aead<AEAD: Aead + AeadCore + KeyInit>(
key: &CoseSymmetricKey<'_, <RustCryptoContext<RNG> as CryptoBackend>::Error>,
ciphertext: &[u8],
aad: &[u8],
iv: &[u8],
) -> Result<Vec<u8>, CoseCipherError<<Self as CryptoBackend>::Error>> {
let aes_key = Key::<AEAD>::from_slice(key.k);
let cipher = AEAD::new(aes_key);
let nonce = Nonce::<AEAD>::from_slice(iv);
let payload = Payload {
msg: ciphertext,
aad,
};
cipher
.decrypt(nonce, payload)
.map_err(CoseCipherError::from)
}

/// Perform an AES-GCM encryption operation on `plaintext` and the additional authenticated
/// data `aad` using the given `iv` and `key` with the given `algorithm` variant of AES-GCM.
pub(super) fn encrypt_aes_gcm(
algorithm: iana::Algorithm,
key: &CoseSymmetricKey<'_, <Self as CryptoBackend>::Error>,
plaintext: &[u8],
aad: &[u8],
iv: &[u8],
) -> Result<Vec<u8>, CoseCipherError<<Self as CryptoBackend>::Error>> {
match algorithm {
iana::Algorithm::A128GCM => Self::encrypt_aead::<Aes128Gcm>(key, plaintext, aad, iv),
iana::Algorithm::A192GCM => {
Self::encrypt_aead::<AesGcm<Aes192, U12>>(key, plaintext, aad, iv)
}
iana::Algorithm::A256GCM => Self::encrypt_aead::<Aes256Gcm>(key, plaintext, aad, iv),
a => Err(CoseCipherError::UnsupportedAlgorithm(Algorithm::Assigned(
a,
))),
}
}

/// Perform an AES-GCM decryption operation on `ciphertext` and the additional authenticated
/// data `aad` using the given `iv` and `key` with the given `algorithm` variant of AES-GCM.
pub(super) fn decrypt_aes_gcm(
algorithm: iana::Algorithm,
key: &CoseSymmetricKey<'_, <Self as CryptoBackend>::Error>,
ciphertext_with_tag: &[u8],
aad: &[u8],
iv: &[u8],
) -> Result<Vec<u8>, CoseCipherError<<Self as CryptoBackend>::Error>> {
match algorithm {
iana::Algorithm::A128GCM => {
Self::decrypt_aead::<Aes128Gcm>(key, ciphertext_with_tag, aad, iv)
}
iana::Algorithm::A192GCM => {
Self::decrypt_aead::<AesGcm<Aes192, U12>>(key, ciphertext_with_tag, aad, iv)
}
iana::Algorithm::A256GCM => {
Self::decrypt_aead::<Aes256Gcm>(key, ciphertext_with_tag, aad, iv)
}
a => Err(CoseCipherError::UnsupportedAlgorithm(Algorithm::Assigned(
a,
))),
}
}
}
45 changes: 45 additions & 0 deletions src/token/cose/crypto_impl/rustcrypto/encrypt/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright (c) 2024 The NAMIB Project Developers.
* Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
* https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
* <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
* option. This file may not be copied, modified, or distributed
* except according to those terms.
*
* SPDX-License-Identifier: MIT OR Apache-2.0
*/
use coset::iana;
use rand::{CryptoRng, RngCore};

use crate::error::CoseCipherError;
use crate::token::cose::crypto_impl::rustcrypto::RustCryptoContext;
use crate::token::cose::{CoseSymmetricKey, EncryptCryptoBackend};

#[cfg(feature = "rustcrypto-aes-gcm")]
mod aes_gcm;

impl<RNG: RngCore + CryptoRng> EncryptCryptoBackend for RustCryptoContext<RNG> {
#[cfg(feature = "rustcrypto-aes-gcm")]
fn encrypt_aes_gcm(
&mut self,
algorithm: iana::Algorithm,
key: CoseSymmetricKey<'_, Self::Error>,
plaintext: &[u8],
aad: &[u8],
iv: &[u8],
) -> Result<Vec<u8>, CoseCipherError<Self::Error>> {
Self::encrypt_aes_gcm(algorithm, &key, plaintext, aad, iv)
}

#[cfg(feature = "rustcrypto-aes-gcm")]
fn decrypt_aes_gcm(
&mut self,
algorithm: iana::Algorithm,
key: CoseSymmetricKey<'_, Self::Error>,
ciphertext_with_tag: &[u8],
aad: &[u8],
iv: &[u8],
) -> Result<Vec<u8>, CoseCipherError<Self::Error>> {
Self::decrypt_aes_gcm(algorithm, &key, ciphertext_with_tag, aad, iv)
}
}
Loading
Loading