Skip to content

Commit

Permalink
merge: pull request #26 from namib-project/chacha_poly
Browse files Browse the repository at this point in the history
Add support for ChaCha20/Poly1305
  • Loading branch information
pulsastrix authored Aug 27, 2024
2 parents 47f4982 + 7909305 commit 3832486
Show file tree
Hide file tree
Showing 25 changed files with 453 additions and 81 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ jobs:
- uses: dtolnay/rust-toolchain@stable
with:
components: rustc, cargo
- run: cargo test --no-default-features --features openssl
- run: cargo test --no-default-features --features openssl,rustcrypto

fmt:
name: Rustfmt
Expand Down
4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,13 @@ exclude = [
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-aes-ccm"]
rustcrypto-encrypt = ["rustcrypto-aes-gcm", "rustcrypto-aes-ccm", "rustcrypto-chacha20-poly1305"]
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-ccm = ["dep:ccm", "dep:typenum", "dep:aead", "dep:aes"]
rustcrypto-chacha20-poly1305 = ["dep:chacha20poly1305", "dep:typenum", "dep:aead"]
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"]
Expand All @@ -43,6 +44,7 @@ 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"] }
ccm = { version = "0.5.0", optional = true, default-features = false, features = ["alloc"] }
chacha20poly1305 = { version = "0.10.1", optional = true, default-features = false, features = ["alloc"] }
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 }
Expand Down
4 changes: 3 additions & 1 deletion build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ fn main() {
cfg_aliases! {
rustcrypto_encrypt_base: {
any(
feature = "rustcrypto-aes-gcm"
feature = "rustcrypto-aes-gcm",
feature = "rustcrypto-aes-ccm",
feature = "rustcrypto-chacha20-poly1305"
)
},
rustcrypto_sign_base: {
Expand Down
79 changes: 73 additions & 6 deletions src/token/cose/crypto_impl/openssl/encrypt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

use crate::error::CoseCipherError;
use crate::token::cose::crypto_impl::openssl::OpensslContext;
use crate::token::cose::util::{aes_ccm_algorithm_tag_len, AES_GCM_TAG_LEN};
use crate::token::cose::util::symmetric_algorithm_tag_len;
use crate::token::cose::{crypto_impl, CoseSymmetricKey, EncryptCryptoBackend};
use alloc::vec::Vec;
use coset::iana;
Expand All @@ -27,6 +27,7 @@ impl EncryptCryptoBackend for OpensslContext {
iv: &[u8],
) -> Result<Vec<u8>, CoseCipherError<Self::Error>> {
let cipher = crypto_impl::openssl::algorithm_to_cipher(algorithm)?;
let tag_len = symmetric_algorithm_tag_len(algorithm)?;
let mut ctx = CipherCtx::new()?;
// So, apparently OpenSSL requires a very specific order of operations which differs
// slightly for AES-GCM and AES-CCM in order to work.
Expand All @@ -51,7 +52,7 @@ impl EncryptCryptoBackend for OpensslContext {
// 6. Then, we can finish the operation.
ctx.cipher_final_vec(&mut ciphertext)?;
let ciphertext_len = ciphertext.len();
ciphertext.resize(ciphertext_len + AES_GCM_TAG_LEN, 0u8);
ciphertext.resize(ciphertext_len + tag_len, 0u8);
ctx.tag(&mut ciphertext[ciphertext_len..])?;
Ok(ciphertext)
}
Expand All @@ -65,8 +66,9 @@ impl EncryptCryptoBackend for OpensslContext {
iv: &[u8],
) -> Result<Vec<u8>, CoseCipherError<Self::Error>> {
let cipher = crypto_impl::openssl::algorithm_to_cipher(algorithm)?;
let auth_tag = &ciphertext_with_tag[(ciphertext_with_tag.len() - AES_GCM_TAG_LEN)..];
let ciphertext = &ciphertext_with_tag[..(ciphertext_with_tag.len() - AES_GCM_TAG_LEN)];
let tag_len = symmetric_algorithm_tag_len(algorithm)?;
let auth_tag = &ciphertext_with_tag[(ciphertext_with_tag.len() - tag_len)..];
let ciphertext = &ciphertext_with_tag[..(ciphertext_with_tag.len() - tag_len)];

let mut ctx = CipherCtx::new()?;
// Refer to https://wiki.openssl.org/index.php/EVP_Authenticated_Encryption_and_Decryption#Authenticated_Decryption_using_GCM_mode
Expand Down Expand Up @@ -103,7 +105,7 @@ impl EncryptCryptoBackend for OpensslContext {
iv: &[u8],
) -> Result<Vec<u8>, CoseCipherError<Self::Error>> {
let cipher = crypto_impl::openssl::algorithm_to_cipher(algorithm)?;
let tag_len = aes_ccm_algorithm_tag_len(algorithm)?;
let tag_len = symmetric_algorithm_tag_len(algorithm)?;
let mut ctx = CipherCtx::new()?;
// Refer to https://wiki.openssl.org/index.php/EVP_Authenticated_Encryption_and_Decryption#Authenticated_Encryption_using_CCM_mode
// for reference.
Expand Down Expand Up @@ -140,7 +142,7 @@ impl EncryptCryptoBackend for OpensslContext {
iv: &[u8],
) -> Result<Vec<u8>, CoseCipherError<Self::Error>> {
let cipher = crypto_impl::openssl::algorithm_to_cipher(algorithm)?;
let tag_len = aes_ccm_algorithm_tag_len(algorithm)?;
let tag_len = symmetric_algorithm_tag_len(algorithm)?;
let auth_tag = &ciphertext_with_tag[(ciphertext_with_tag.len() - tag_len)..];
let ciphertext = &ciphertext_with_tag[..(ciphertext_with_tag.len() - tag_len)];

Expand Down Expand Up @@ -169,4 +171,69 @@ impl EncryptCryptoBackend for OpensslContext {

Ok(plaintext)
}

fn encrypt_chacha20_poly1305(
&mut self,
key: CoseSymmetricKey<'_, Self::Error>,
plaintext: &[u8],
aad: &[u8],
iv: &[u8],
) -> Result<Vec<u8>, CoseCipherError<Self::Error>> {
let cipher = openssl::cipher::Cipher::chacha20_poly1305();
let tag_len = symmetric_algorithm_tag_len(iana::Algorithm::ChaCha20Poly1305)?;
let mut ctx = CipherCtx::new()?;
// Refer to https://docs.openssl.org/1.1.1/man3/EVP_EncryptInit/#chacha20-poly1305 for
// reference.
// 1. First, we set the cipher.
ctx.encrypt_init(Some(cipher), None, None)?;
// 2. We *must* set the IV length _before_ setting the IV.
ctx.set_iv_length(iv.len())?;
// 3. Now we can set key and IV.
ctx.encrypt_init(None, Some(key.k), Some(iv))?;
let mut ciphertext = vec![];
// 4. Then, we set the AAD before setting the plaintext.
ctx.cipher_update(aad, None)?;
// 5. Finally, we provide the plaintext.
ctx.cipher_update_vec(plaintext, &mut ciphertext)?;
// 6. Then, we can finish the operation.
ctx.cipher_final_vec(&mut ciphertext)?;
let ciphertext_len = ciphertext.len();
ciphertext.resize(ciphertext_len + tag_len, 0u8);
ctx.tag(&mut ciphertext[ciphertext_len..])?;
Ok(ciphertext)
}

fn decrypt_chacha20_poly1305(
&mut self,
key: CoseSymmetricKey<'_, Self::Error>,
ciphertext_with_tag: &[u8],
aad: &[u8],
iv: &[u8],
) -> Result<Vec<u8>, CoseCipherError<Self::Error>> {
let cipher = openssl::cipher::Cipher::chacha20_poly1305();
let tag_len = symmetric_algorithm_tag_len(iana::Algorithm::ChaCha20Poly1305)?;
let auth_tag = &ciphertext_with_tag[(ciphertext_with_tag.len() - tag_len)..];
let ciphertext = &ciphertext_with_tag[..(ciphertext_with_tag.len() - tag_len)];

let mut ctx = CipherCtx::new()?;
// Refer to https://docs.openssl.org/1.1.1/man3/EVP_EncryptInit/#chacha20-poly1305 for
// reference.
// 1. First, we set the cipher.
ctx.decrypt_init(Some(cipher), None, None)?;
// 2. We *must* set the tag and IV length _before_ setting key and IV.
ctx.set_iv_length(iv.len())?;
ctx.set_tag(auth_tag)?;
// 3. Now we can set key and IV.
ctx.decrypt_init(None, Some(key.k), Some(iv))?;
// 4. Then, we set the AAD before setting the ciphertext.
ctx.cipher_update(aad, None)?;
// 5. Finally, we provide the ciphertext for decryption.
let mut plaintext = vec![0; ciphertext.len()];
let plaintext_len = ctx.cipher_update(ciphertext, Some(&mut plaintext))?;
plaintext.truncate(plaintext_len);
// No call to cipher_final() here, I guess?
// The official examples in the OpenSSL wiki don't finalize, so we won't either.

Ok(plaintext)
}
}
1 change: 1 addition & 0 deletions src/token/cose/crypto_impl/rustcrypto/encrypt/aead.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use crate::error::CoseCipherError;
use crate::token::cose::{CoseSymmetricKey, CryptoBackend};

use super::RustCryptoContext;
use alloc::vec::Vec;

impl<RNG: RngCore + CryptoRng> RustCryptoContext<RNG> {
/// Perform an AEAD encryption operation on `plaintext` and the additional authenticated
Expand Down
1 change: 1 addition & 0 deletions src/token/cose/crypto_impl/rustcrypto/encrypt/aes_ccm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use crate::error::CoseCipherError;
use crate::token::cose::{CoseSymmetricKey, CryptoBackend};

use super::RustCryptoContext;
use alloc::vec::Vec;

impl<RNG: RngCore + CryptoRng> RustCryptoContext<RNG> {
/// Perform an AES-CCM encryption operation on `plaintext` and the additional authenticated
Expand Down
1 change: 1 addition & 0 deletions src/token/cose/crypto_impl/rustcrypto/encrypt/aes_gcm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use crate::error::CoseCipherError;
use crate::token::cose::{CoseSymmetricKey, CryptoBackend};

use super::RustCryptoContext;
use alloc::vec::Vec;

impl<RNG: RngCore + CryptoRng> RustCryptoContext<RNG> {
/// Perform an AES-GCM encryption operation on `plaintext` and the additional authenticated
Expand Down
41 changes: 41 additions & 0 deletions src/token/cose/crypto_impl/rustcrypto/encrypt/chacha_poly.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* 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 crate::error::CoseCipherError;
use crate::token::cose::crypto_impl::rustcrypto::RustCryptoContext;
use crate::token::cose::{CoseSymmetricKey, CryptoBackend};
use alloc::vec::Vec;
use chacha20poly1305::ChaCha20Poly1305;
use rand::CryptoRng;
use rand::RngCore;

impl<RNG: RngCore + CryptoRng> RustCryptoContext<RNG> {
/// Perform a ChaCha20/Poly1305 encryption operation on `plaintext` and the additional
/// authenticated data `aad` using the given `iv` and `key`.
pub(super) fn encrypt_chacha20_poly1305(
key: &CoseSymmetricKey<'_, <Self as CryptoBackend>::Error>,
plaintext: &[u8],
aad: &[u8],
iv: &[u8],
) -> Result<Vec<u8>, CoseCipherError<<Self as CryptoBackend>::Error>> {
Self::encrypt_aead::<ChaCha20Poly1305>(key, plaintext, aad, iv)
}

/// Perform a ChaCha20/Poly1305 decryption operation on `ciphertext_with_tag` and the additional
/// authenticated data `aad` using the given `iv` and `key`.
pub(super) fn decrypt_chacha20_poly1305(
key: &CoseSymmetricKey<'_, <Self as CryptoBackend>::Error>,
ciphertext_with_tag: &[u8],
aad: &[u8],
iv: &[u8],
) -> Result<Vec<u8>, CoseCipherError<<Self as CryptoBackend>::Error>> {
Self::decrypt_aead::<ChaCha20Poly1305>(key, ciphertext_with_tag, aad, iv)
}
}
32 changes: 31 additions & 1 deletion src/token/cose/crypto_impl/rustcrypto/encrypt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,22 @@ use rand::{CryptoRng, RngCore};
use crate::error::CoseCipherError;
use crate::token::cose::crypto_impl::rustcrypto::RustCryptoContext;
use crate::token::cose::{CoseSymmetricKey, EncryptCryptoBackend};
use alloc::vec::Vec;

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

#[cfg(feature = "rustcrypto-aes-ccm")]
mod aes_ccm;

#[cfg(any(feature = "rustcrypto-aes-gcm", feature = "rustcrypto-aes-ccm"))]
#[cfg(feature = "rustcrypto-chacha20-poly1305")]
mod chacha_poly;

#[cfg(any(
feature = "rustcrypto-aes-gcm",
feature = "rustcrypto-aes-ccm",
feature = "rustcrypto-chacha20-poly1305"
))]
mod aead;

impl<RNG: RngCore + CryptoRng> EncryptCryptoBackend for RustCryptoContext<RNG> {
Expand Down Expand Up @@ -72,4 +80,26 @@ impl<RNG: RngCore + CryptoRng> EncryptCryptoBackend for RustCryptoContext<RNG> {
) -> Result<Vec<u8>, CoseCipherError<Self::Error>> {
Self::decrypt_aes_ccm(algorithm, &key, ciphertext_with_tag, aad, iv)
}

#[cfg(feature = "rustcrypto-chacha20-poly1305")]
fn encrypt_chacha20_poly1305(
&mut self,
key: CoseSymmetricKey<'_, Self::Error>,
plaintext: &[u8],
aad: &[u8],
iv: &[u8],
) -> Result<Vec<u8>, CoseCipherError<Self::Error>> {
Self::encrypt_chacha20_poly1305(&key, plaintext, aad, iv)
}

#[cfg(feature = "rustcrypto-chacha20-poly1305")]
fn decrypt_chacha20_poly1305(
&mut self,
key: CoseSymmetricKey<'_, Self::Error>,
ciphertext_with_tag: &[u8],
aad: &[u8],
iv: &[u8],
) -> Result<Vec<u8>, CoseCipherError<Self::Error>> {
Self::decrypt_chacha20_poly1305(&key, ciphertext_with_tag, aad, iv)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use crate::error::CoseCipherError;
use crate::token::cose::{CoseSymmetricKey, CryptoBackend};

use super::RustCryptoContext;
use alloc::vec::Vec;

impl<RNG: RngCore + CryptoRng> RustCryptoContext<RNG> {
/// Perform an AES key wrap operation on the key contained in `plaintext` which is wrapped
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use rand::{CryptoRng, RngCore};
use crate::error::CoseCipherError;
use crate::token::cose::crypto_impl::rustcrypto::RustCryptoContext;
use crate::token::cose::{CoseSymmetricKey, KeyDistributionCryptoBackend};
use alloc::vec::Vec;

#[cfg(feature = "rustcrypto-aes-kw")]
mod aes_key_wrap;
Expand Down
1 change: 1 addition & 0 deletions src/token/cose/crypto_impl/rustcrypto/mac/hmac.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use sha2::{Sha256, Sha384, Sha512};
use crate::error::CoseCipherError;
use crate::token::cose::crypto_impl::rustcrypto::RustCryptoContext;
use crate::token::cose::{CoseSymmetricKey, CryptoBackend};
use alloc::vec::Vec;

impl<RNG: RngCore + CryptoRng> RustCryptoContext<RNG> {
/// Compute the HMAC of `payload` using the given `key` with the HMAC function
Expand Down
1 change: 1 addition & 0 deletions src/token/cose/crypto_impl/rustcrypto/mac/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use crate::token::cose::crypto_impl::rustcrypto::CoseCipherError;
use crate::token::cose::crypto_impl::rustcrypto::RustCryptoContext;
use crate::token::cose::CoseSymmetricKey;
use crate::token::cose::MacCryptoBackend;
use alloc::vec::Vec;
use coset::iana;
use rand::{CryptoRng, RngCore};

Expand Down
6 changes: 5 additions & 1 deletion src/token/cose/crypto_impl/rustcrypto/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,11 @@ impl From<digest::MacError> for CoseCipherError<CoseRustCryptoCipherError> {
}
}

#[cfg(feature = "rustcrypto-aes-gcm")]
#[cfg(any(
feature = "rustcrypto-aes-gcm",
feature = "rustcrypto-aes-ccm",
feature = "rustcrypto-chacha20-poly1305"
))]
impl From<aead::Error> for CoseCipherError<CoseRustCryptoCipherError> {
fn from(_value: aead::Error) -> Self {
CoseCipherError::VerificationFailure
Expand Down
1 change: 1 addition & 0 deletions src/token/cose/crypto_impl/rustcrypto/sign/ecdsa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ use crate::error::CoseCipherError;
use crate::token::cose::crypto_impl::rustcrypto::CoseRustCryptoCipherError;
use crate::token::cose::crypto_impl::rustcrypto::RustCryptoContext;
use crate::token::cose::{CoseEc2Key, CryptoBackend, EllipticCurve};
use alloc::vec::Vec;

impl<RNG: RngCore + CryptoRng> RustCryptoContext<RNG> {
/// Perform an ECDSA signature operation with the ECDSA variant given in `algorithm` for the
Expand Down
1 change: 1 addition & 0 deletions src/token/cose/crypto_impl/rustcrypto/sign/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use rand::{CryptoRng, RngCore};

use crate::token::cose::crypto_impl::rustcrypto::RustCryptoContext;
use crate::token::SignCryptoBackend;
use alloc::vec::Vec;

#[cfg(feature = "rustcrypto-ecdsa")]
mod ecdsa;
Expand Down
Loading

0 comments on commit 3832486

Please sign in to comment.