Skip to content

Commit

Permalink
feat: Add rustcrypto backend
Browse files Browse the repository at this point in the history
  • Loading branch information
chrysn committed Nov 17, 2023
1 parent 31ff6a0 commit 05604ca
Show file tree
Hide file tree
Showing 6 changed files with 178 additions and 2 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
strategy:
fail-fast: false
matrix:
crypto_backend: [edhoc-crypto/hacspec, edhoc-crypto/psa]
crypto_backend: [edhoc-crypto/hacspec, edhoc-crypto/psa, edhoc-crypto/rustcrypto]
ead: [ead-none, ead-zeroconf]

steps:
Expand All @@ -46,7 +46,7 @@ jobs:
strategy:
fail-fast: false
matrix:
crypto_backend: [edhoc-crypto/hacspec, edhoc-crypto/psa, edhoc-crypto/psa-baremetal, edhoc-crypto/cryptocell310]
crypto_backend: [edhoc-crypto/hacspec, edhoc-crypto/psa, edhoc-crypto/psa-baremetal, edhoc-crypto/cryptocell310, edhoc-crypto/rustcrypto]
ead: [ead-none, ead-zeroconf]

steps:
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ members = [
"crypto/edhoc-crypto-cc2538",
"crypto/edhoc-crypto-hacspec",
"crypto/edhoc-crypto-psa",
"crypto/edhoc-crypto-rustcrypto",
"crypto/edhoc-crypto-cryptocell310-sys",
"crypto/edhoc-crypto-trait",
"examples/coap",
Expand Down
9 changes: 9 additions & 0 deletions crypto/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,19 @@ edhoc-crypto-psa = { path = "./edhoc-crypto-psa", default-features = false, opti
# cryptocell for nrf52840
edhoc-crypto-cryptocell310 = { path = "./edhoc-crypto-cryptocell310-sys", optional = true }

# software implementations from rustcrypto
edhoc-crypto-rustcrypto = { path = "./edhoc-crypto-rustcrypto", optional = true }
rand_core = { version = "0.6.4", optional = true, default-features = false }

[features]
default = [ ]
hacspec = [ "edhoc-crypto-hacspec" ]
cc2538 = [ "edhoc-crypto-cc2538" ]
psa = [ "edhoc-crypto-psa" ]
psa-baremetal = [ "psa", "edhoc-crypto-psa/baremetal" ]
cryptocell310 = [ "edhoc-crypto-cryptocell310" ]
# This requires std because we need to conjure randomness from thin air;
# embedded systems can still use rustcrypto but need to provide a crypto from
# edhoc-crypto-rustcrypto on their own, and combine it with an entropy choice
# of their avail.
rustcrypto = [ "edhoc-crypto-rustcrypto", "rand_core/getrandom" ]
18 changes: 18 additions & 0 deletions crypto/edhoc-crypto-rustcrypto/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[package]
name = "edhoc-crypto-rustcrypto"
version = "0.1.0"
edition = "2021"
license = "BSD"
description = "EDHOC crypto library backend based on the RustCrypto crates"

[dependencies]
edhoc-consts = { path = "../../consts" }
edhoc-crypto-trait.path = "../edhoc-crypto-trait"

aead = { version = "0.5.2", default-features = false }
aes = { version = "0.8.3", default-features = false }
ccm = { version = "0.5.0", default-features = false }
hkdf = { version = "0.12.3", default-features = false }
p256 = { version = "0.13.2", default-features = false, features = [ "ecdh" ] }
sha2 = { version = "0.10.8", default-features = false }
rand_core = { version = "0.6.4", default-features = false }
140 changes: 140 additions & 0 deletions crypto/edhoc-crypto-rustcrypto/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
#![no_std]

use edhoc_consts::{
BufferCiphertext3, BufferPlaintext3, BytesCcmIvLen, BytesCcmKeyLen, BytesHashLen,
BytesMaxBuffer, BytesMaxInfoBuffer, BytesP256ElemLen, EDHOCError, MessageBufferTrait,
AES_CCM_TAG_LEN, MAX_BUFFER_LEN,
};
use edhoc_crypto_trait::Crypto as CryptoTrait;

use ccm::AeadInPlace;
use ccm::KeyInit;
use p256::elliptic_curve::point::AffineCoordinates;
use p256::elliptic_curve::point::DecompressPoint;
use sha2::Digest;

type AesCcm16_64_128 = ccm::Ccm<aes::Aes128, ccm::consts::U8, ccm::consts::U13>;

/// A type representing cryptographic operations through various RustCrypto crates (eg. [aes],
/// [ccm], [p256]).
///
/// Its size depends on the implementation of Rng passed in at creation.
pub struct Crypto<Rng: rand_core::RngCore + rand_core::CryptoRng> {
rng: Rng,
}

impl<Rng: rand_core::RngCore + rand_core::CryptoRng> Crypto<Rng> {
pub const fn new(rng: Rng) -> Self {
Self { rng }
}
}

impl<Rng: rand_core::RngCore + rand_core::CryptoRng> core::fmt::Debug for Crypto<Rng> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
f.debug_struct("edhoc_crypto_rustcrypto::Crypto")
.field("rng", &core::any::type_name::<Rng>())
.finish()
}
}

impl<Rng: rand_core::RngCore + rand_core::CryptoRng> CryptoTrait for Crypto<Rng> {
fn sha256_digest(&mut self, message: &BytesMaxBuffer, message_len: usize) -> BytesHashLen {
let mut hasher = sha2::Sha256::new();
hasher.update(&message[..message_len]);
hasher.finalize().into()
}

fn hkdf_expand(
&mut self,
prk: &BytesHashLen,
info: &BytesMaxInfoBuffer,
info_len: usize,
length: usize,
) -> BytesMaxBuffer {
let hkdf =
hkdf::Hkdf::<sha2::Sha256>::from_prk(prk).expect("Static size was checked at extract");
let mut output: BytesMaxBuffer = [0; MAX_BUFFER_LEN];
hkdf.expand(&info[..info_len], &mut output[..length])
.expect("Static lengths match the algorithm");
output
}

fn hkdf_extract(&mut self, salt: &BytesHashLen, ikm: &BytesP256ElemLen) -> BytesHashLen {
// While it'd be nice to just pass around an Hkdf, the extract output is not a type generic
// of this trait (yet?).
let mut extracted = hkdf::HkdfExtract::<sha2::Sha256>::new(Some(salt));
extracted.input_ikm(ikm);
extracted.finalize().0.into()
}

fn aes_ccm_encrypt_tag_8(
&mut self,
key: &BytesCcmKeyLen,
iv: &BytesCcmIvLen,
ad: &[u8],
plaintext: &BufferPlaintext3,
) -> BufferCiphertext3 {
let key = AesCcm16_64_128::new(key.into());
let mut outbuffer = BufferCiphertext3::new();
outbuffer.content[..plaintext.len].copy_from_slice(&plaintext.content[..plaintext.len]);
if let Ok(tag) =
key.encrypt_in_place_detached(iv.into(), ad, &mut outbuffer.content[..plaintext.len])
{
outbuffer.content[plaintext.len..][..AES_CCM_TAG_LEN].copy_from_slice(&tag);
} else {
panic!("Preconfigured sizes should not allow encryption to fail")
}
outbuffer.len = plaintext.len + AES_CCM_TAG_LEN;
outbuffer
}

fn aes_ccm_decrypt_tag_8(
&mut self,
key: &BytesCcmKeyLen,
iv: &BytesCcmIvLen,
ad: &[u8],
ciphertext: &BufferCiphertext3,
) -> Result<BufferPlaintext3, EDHOCError> {
let key = AesCcm16_64_128::new(key.into());
let mut buffer = BufferPlaintext3::new();
buffer.len = ciphertext.len - AES_CCM_TAG_LEN;
buffer.content[..buffer.len].copy_from_slice(&ciphertext.content[..buffer.len]);
let tag = &ciphertext.content[buffer.len..][..AES_CCM_TAG_LEN];
key.decrypt_in_place_detached(iv.into(), ad, &mut buffer.content[..buffer.len], tag.into())
.map_err(|_| EDHOCError::MacVerificationFailed)?;
Ok(buffer)
}

fn p256_ecdh(
&mut self,
private_key: &BytesP256ElemLen,
public_key: &BytesP256ElemLen,
) -> BytesP256ElemLen {
let secret = p256::SecretKey::from_bytes(private_key.as_slice().into())
.expect("Invalid secret key generated");
let public = p256::AffinePoint::decompress(
public_key.into(),
1.into(), /* Y coordinate choice does not matter for ECDH operation */
)
// While this can actually panic so far, the proper fix is in
// https://github.com/openwsn-berkeley/edhoc-rs/issues/93 which will justify this to be a
// panic (because after that, public key validity will be an invariant of the public key
// type)
.expect("Public key is not a good point");

(*p256::ecdh::diffie_hellman(secret.to_nonzero_scalar(), public).raw_secret_bytes()).into()
}

fn get_random_byte(&mut self) -> u8 {
self.rng.next_u32() as _
}

fn p256_generate_key_pair(&mut self) -> (BytesP256ElemLen, BytesP256ElemLen) {
let secret = p256::SecretKey::random(&mut self.rng);

let public_key = secret.public_key().as_affine().x();
let private_key = secret.to_bytes();

(private_key.into(), public_key.into())
}
}
8 changes: 8 additions & 0 deletions crypto/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@ pub const fn default_crypto() -> Crypto {
edhoc_crypto_psa::Crypto
}

#[cfg(feature = "rustcrypto")]
pub type Crypto = edhoc_crypto_rustcrypto::Crypto<rand_core::OsRng>;

#[cfg(feature = "rustcrypto")]
pub const fn default_crypto() -> Crypto {
edhoc_crypto_rustcrypto::Crypto::new(rand_core::OsRng)
}

#[cfg(any(feature = "cryptocell310", feature = "cryptocell310-rust"))]
pub type Crypto = edhoc_crypto_cryptocell310::Crypto;

Expand Down

0 comments on commit 05604ca

Please sign in to comment.