Skip to content

Commit

Permalink
refactor!: Move crypto operations into a trait.
Browse files Browse the repository at this point in the history
This is incomplete in two aspects:

* it only adjusts the hacspec and psa crypto backends, and
* it still goes through the edhoc-crypto crate to create import-based
  dispatch.

It also does not do a full cargo-fmt, because this allows the delta of
this commit to be small.
  • Loading branch information
chrysn committed Oct 7, 2023
1 parent 567669d commit 9cdd483
Show file tree
Hide file tree
Showing 12 changed files with 165 additions and 79 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ members = [
"crypto/edhoc-crypto-hacspec",
"crypto/edhoc-crypto-psa",
"crypto/edhoc-crypto-cryptocell310-sys",
"crypto/edhoc-crypto-trait",
"examples/coap",
"examples/edhoc-rs-no_std",
"examples/edhoc-rs-cc2538",
Expand Down
2 changes: 2 additions & 0 deletions crypto/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ description = "EDHOC crypto library dispatch crate"
[dependencies]
edhoc-consts = { path = "../consts", default-features = false }

edhoc-crypto-trait.path = "./edhoc-crypto-trait"

# hacspec
edhoc-crypto-hacspec = { path = "./edhoc-crypto-hacspec", optional = true }

Expand Down
1 change: 1 addition & 0 deletions crypto/edhoc-crypto-hacspec/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ description = "EDHOC crypto library hacspec backend"

[dependencies]
edhoc-consts = { path = "../../consts", default-features = false }
edhoc-crypto-trait.path = "../edhoc-crypto-trait"
hacspec-lib = { version = "0.1.0-beta.1", default-features = false }
hacspec-p256 = { version = "0.1.0" }
hacspec-hkdf = { version = "0.1.0" }
Expand Down
26 changes: 17 additions & 9 deletions crypto/edhoc-crypto-hacspec/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ use hacspec_p256::*;
use hacspec_sha256::*;
use rand::Rng;

use edhoc_crypto_trait::Crypto as CryptoTrait;

// Types and functions to aid in translation between the hacspec and non-hacspec world

// TODO: the `array!` construct is not needed anymore.
Expand Down Expand Up @@ -70,7 +72,11 @@ type BufferPlaintext3Hacspec = EdhocMessageBufferHacspec;

// Public functions

pub fn sha256_digest(message: &BytesMaxBuffer, message_len: usize) -> BytesHashLen {
pub struct Crypto;

impl CryptoTrait for Crypto {

fn sha256_digest(message: &BytesMaxBuffer, message_len: usize) -> BytesHashLen {
let message: BytesMaxBufferHacspec = BytesMaxBufferHacspec::from_public_slice(message);

let output =
Expand All @@ -79,7 +85,7 @@ pub fn sha256_digest(message: &BytesMaxBuffer, message_len: usize) -> BytesHashL
output.to_public_array()
}

pub fn hkdf_expand(
fn hkdf_expand(
prk: &BytesHashLen,
info: &BytesMaxInfoBuffer,
info_len: usize,
Expand All @@ -102,7 +108,7 @@ pub fn hkdf_expand(
output.to_public_array()
}

pub fn hkdf_extract(salt: &BytesHashLen, ikm: &BytesP256ElemLen) -> BytesHashLen {
fn hkdf_extract(salt: &BytesHashLen, ikm: &BytesP256ElemLen) -> BytesHashLen {
let output = BytesHashLenHacspec::from_seq(&extract(
&ByteSeq::from_slice(&BytesHashLenHacspec::from_public_slice(salt), 0, salt.len()),
&ByteSeq::from_slice(
Expand All @@ -114,7 +120,7 @@ pub fn hkdf_extract(salt: &BytesHashLen, ikm: &BytesP256ElemLen) -> BytesHashLen
output.to_public_array()
}

pub fn aes_ccm_encrypt_tag_8(
fn aes_ccm_encrypt_tag_8(
key: &BytesCcmKeyLen,
iv: &BytesCcmIvLen,
ad: &BytesEncStructureLen,
Expand All @@ -137,7 +143,7 @@ pub fn aes_ccm_encrypt_tag_8(
output.to_public_buffer()
}

pub fn aes_ccm_decrypt_tag_8(
fn aes_ccm_decrypt_tag_8(
key: &BytesCcmKeyLen,
iv: &BytesCcmIvLen,
ad: &BytesEncStructureLen,
Expand All @@ -162,7 +168,7 @@ pub fn aes_ccm_decrypt_tag_8(
}
}

pub fn p256_ecdh(
fn p256_ecdh(
private_key: &BytesP256ElemLen,
public_key: &BytesP256ElemLen,
) -> BytesP256ElemLen {
Expand All @@ -184,12 +190,12 @@ pub fn p256_ecdh(
}

#[cfg(not(feature = "hacspec-pure"))]
pub fn get_random_byte() -> u8 {
fn get_random_byte() -> u8 {
rand::thread_rng().gen::<u8>()
}

#[cfg(not(feature = "hacspec-pure"))]
pub fn p256_generate_key_pair() -> (BytesP256ElemLen, BytesP256ElemLen) {
fn p256_generate_key_pair() -> (BytesP256ElemLen, BytesP256ElemLen) {
// generate a private key
let mut private_key = BytesP256ElemLenHacspec::new();
loop {
Expand All @@ -209,13 +215,15 @@ pub fn p256_generate_key_pair() -> (BytesP256ElemLen, BytesP256ElemLen) {
(private_key.to_public_array(), public_key.to_public_array())
}

}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_p256_keys() {
let (x, g_x) = p256_generate_key_pair();
let (x, g_x) = Crypto::p256_generate_key_pair();
assert_eq!(x.len(), 32);
assert_eq!(g_x.len(), 32);

Expand Down
1 change: 1 addition & 0 deletions crypto/edhoc-crypto-psa/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ description = "EDHOC crypto library PSA backend"

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

[features]
Expand Down
102 changes: 56 additions & 46 deletions crypto/edhoc-crypto-psa/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ use psa_crypto::types::algorithm::Hash;
use psa_crypto::types::algorithm::{Aead, AeadWithDefaultLengthTag, KeyAgreement, RawKeyAgreement};
use psa_crypto::types::key::{Attributes, EccFamily, Lifetime, Policy, Type, UsageFlags};

use edhoc_crypto_trait::Crypto as CryptoTrait;

#[no_mangle]
pub extern "C" fn mbedtls_hardware_poll(
data: *mut ::core::ffi::c_void,
Expand All @@ -20,7 +22,11 @@ pub extern "C" fn mbedtls_hardware_poll(
0i32
}

pub fn sha256_digest(message: &BytesMaxBuffer, message_len: usize) -> BytesHashLen {
pub struct Crypto;

impl CryptoTrait for Crypto {

fn sha256_digest(message: &BytesMaxBuffer, message_len: usize) -> BytesHashLen {
let hash_alg = Hash::Sha256;
let mut hash: [u8; SHA256_DIGEST_LEN] = [0; SHA256_DIGEST_LEN];
psa_crypto::init().unwrap();
Expand All @@ -29,7 +35,9 @@ pub fn sha256_digest(message: &BytesMaxBuffer, message_len: usize) -> BytesHashL
hash
}

pub fn hkdf_expand(
// FIXME: Together with hkdf_extract, and the hmac_sha256 helper, this could be a provided
// function.
fn hkdf_expand(
prk: &BytesHashLen,
info: &BytesMaxInfoBuffer,
info_len: usize,
Expand Down Expand Up @@ -68,7 +76,7 @@ pub fn hkdf_expand(
output
}

pub fn hkdf_extract(salt: &BytesHashLen, ikm: &BytesP256ElemLen) -> BytesHashLen {
fn hkdf_extract(salt: &BytesHashLen, ikm: &BytesP256ElemLen) -> BytesHashLen {
// Implementation of HKDF-Extract as per RFC 5869

// TODO generalize if salt is not provided
Expand All @@ -77,7 +85,7 @@ pub fn hkdf_extract(salt: &BytesHashLen, ikm: &BytesP256ElemLen) -> BytesHashLen
output
}

pub fn aes_ccm_encrypt_tag_8(
fn aes_ccm_encrypt_tag_8(
key: &BytesCcmKeyLen,
iv: &BytesCcmIvLen,
ad: &BytesEncStructureLen,
Expand Down Expand Up @@ -118,7 +126,7 @@ pub fn aes_ccm_encrypt_tag_8(
output_buffer
}

pub fn aes_ccm_decrypt_tag_8(
fn aes_ccm_decrypt_tag_8(
key: &BytesCcmKeyLen,
iv: &BytesCcmIvLen,
ad: &BytesEncStructureLen,
Expand Down Expand Up @@ -161,7 +169,7 @@ pub fn aes_ccm_decrypt_tag_8(
}
}

pub fn p256_ecdh(
fn p256_ecdh(
private_key: &BytesP256ElemLen,
public_key: &BytesP256ElemLen,
) -> BytesP256ElemLen {
Expand Down Expand Up @@ -193,7 +201,46 @@ pub fn p256_ecdh(
output_buffer
}

pub fn hmac_sha256(message: &[u8], key: &[u8; SHA256_DIGEST_LEN]) -> BytesHashLen {
fn get_random_byte() -> u8 {
psa_crypto::init().unwrap();
let mut buffer = [0u8; 1];
generate_random(&mut buffer); // TODO: check return value
buffer[0]
}

fn p256_generate_key_pair() -> (BytesP256ElemLen, BytesP256ElemLen) {
let alg = RawKeyAgreement::Ecdh;
let mut usage_flags: UsageFlags = UsageFlags::default();
usage_flags.set_export();
usage_flags.set_derive();
let attributes = Attributes {
key_type: Type::EccKeyPair {
curve_family: EccFamily::SecpR1,
},
bits: 256,
lifetime: Lifetime::Volatile,
policy: Policy {
usage_flags,
permitted_algorithms: KeyAgreement::Raw(alg).into(),
},
};

psa_crypto::init().unwrap();

let key_id = key_management::generate(attributes, None).unwrap();
let mut private_key: [u8; P256_ELEM_LEN] = [0; P256_ELEM_LEN];
key_management::export(key_id, &mut private_key).unwrap();

let mut public_key: [u8; P256_ELEM_LEN * 2 + 1] = [0; P256_ELEM_LEN * 2 + 1]; // allocate buffer for: sign, x, and y coordinates
key_management::export_public(key_id, &mut public_key).unwrap();
let public_key: [u8; P256_ELEM_LEN] = public_key[1..33].try_into().unwrap(); // return only the x coordinate

(private_key, public_key)
}

}

fn hmac_sha256(message: &[u8], key: &[u8; SHA256_DIGEST_LEN]) -> BytesHashLen {
// implementation of HMAC as per RFC2104

const IPAD: [u8; 64] = [0x36; 64];
Expand All @@ -217,7 +264,7 @@ pub fn hmac_sha256(message: &[u8], key: &[u8; SHA256_DIGEST_LEN]) -> BytesHashLe
s2[64..64 + message.len()].copy_from_slice(message);

// (4) apply H to the stream generated in step (3)
let ih = sha256_digest(&s2, 64 + message.len());
let ih = Crypto::sha256_digest(&s2, 64 + message.len());

// (5) XOR (bitwise exclusive-OR) the B byte string computed in
// step (1) with opad
Expand All @@ -231,48 +278,11 @@ pub fn hmac_sha256(message: &[u8], key: &[u8; SHA256_DIGEST_LEN]) -> BytesHashLe

// (7) apply H to the stream generated in step (6) and output
// the result
let oh = sha256_digest(&s5, 3 * SHA256_DIGEST_LEN);
let oh = Crypto::sha256_digest(&s5, 3 * SHA256_DIGEST_LEN);

oh
}

pub fn get_random_byte() -> u8 {
psa_crypto::init().unwrap();
let mut buffer = [0u8; 1];
generate_random(&mut buffer); // TODO: check return value
buffer[0]
}

pub fn p256_generate_key_pair() -> (BytesP256ElemLen, BytesP256ElemLen) {
let alg = RawKeyAgreement::Ecdh;
let mut usage_flags: UsageFlags = UsageFlags::default();
usage_flags.set_export();
usage_flags.set_derive();
let attributes = Attributes {
key_type: Type::EccKeyPair {
curve_family: EccFamily::SecpR1,
},
bits: 256,
lifetime: Lifetime::Volatile,
policy: Policy {
usage_flags,
permitted_algorithms: KeyAgreement::Raw(alg).into(),
},
};

psa_crypto::init().unwrap();

let key_id = key_management::generate(attributes, None).unwrap();
let mut private_key: [u8; P256_ELEM_LEN] = [0; P256_ELEM_LEN];
key_management::export(key_id, &mut private_key).unwrap();

let mut public_key: [u8; P256_ELEM_LEN * 2 + 1] = [0; P256_ELEM_LEN * 2 + 1]; // allocate buffer for: sign, x, and y coordinates
key_management::export_public(key_id, &mut public_key).unwrap();
let public_key: [u8; P256_ELEM_LEN] = public_key[1..33].try_into().unwrap(); // return only the x coordinate

(private_key, public_key)
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
7 changes: 7 additions & 0 deletions crypto/edhoc-crypto-trait/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
name = "edhoc-crypto-trait"
version = "0.1.0"
edition = "2021"

[dependencies]
edhoc-consts = { path = "../../consts", default-features = false }
31 changes: 31 additions & 0 deletions crypto/edhoc-crypto-trait/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//! Cryptography trait back-end for the edhoc-crypto crate
#![no_std]

use edhoc_consts::*;

pub trait Crypto {
fn sha256_digest(message: &BytesMaxBuffer, message_len: usize) -> BytesHashLen;
fn hkdf_expand(
prk: &BytesHashLen,
info: &BytesMaxInfoBuffer,
info_len: usize,
length: usize,
) -> BytesMaxBuffer;
fn hkdf_extract(salt: &BytesHashLen, ikm: &BytesP256ElemLen) -> BytesHashLen;
fn aes_ccm_encrypt_tag_8(
key: &BytesCcmKeyLen,
iv: &BytesCcmIvLen,
ad: &BytesEncStructureLen,
plaintext: &BufferPlaintext3,
) -> BufferCiphertext3;
fn aes_ccm_decrypt_tag_8(
key: &BytesCcmKeyLen,
iv: &BytesCcmIvLen,
ad: &BytesEncStructureLen,
ciphertext: &BufferCiphertext3,
) -> Result<BufferPlaintext3, EDHOCError>;
fn p256_ecdh(private_key: &BytesP256ElemLen, public_key: &BytesP256ElemLen)
-> BytesP256ElemLen;
fn get_random_byte() -> u8;
fn p256_generate_key_pair() -> (BytesP256ElemLen, BytesP256ElemLen);
}
26 changes: 24 additions & 2 deletions crypto/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,35 @@
//! Cryptography dispatch for the edhoc-rs crate
//!
//! This crate is used by edhoc-rs to decide which cryptographic back-end to use. Its presence
//! avoids the need for all edhoc-rs types to be generic over a back-end, which would then be
//! provided by the user at initialization time. On the long run, its type may turn into a
//! default associated type.
#![no_std]

/// Convenience re-export
pub use edhoc_crypto_trait::Crypto as CryptoTrait;

#[cfg(feature = "hacspec")]
pub use edhoc_crypto_hacspec::*;
pub type Crypto = edhoc_crypto_hacspec::Crypto;

// FIXME: Does not work with crypto-as-trait yet
#[cfg(feature = "cc2538")]
pub use edhoc_crypto_cc2538::*;

#[cfg(any(feature = "psa", feature = "psa-rust",))]
pub use edhoc_crypto_psa::*;
pub type Crypto = edhoc_crypto_psa::Crypto;

// FIXME: Does not work with crypto-as-trait yet
#[cfg(any(feature = "cryptocell310", feature = "cryptocell310-rust"))]
pub use edhoc_crypto_cryptocell310::*;

/// See test_implements_crypto
#[allow(dead_code)]
fn test_helper<T: CryptoTrait>() {}

/// Ensure at build time that whichever type as selected for Crypto actually implements the Crypto
/// trait, and that one is actually defined.
#[allow(dead_code)]
fn test_implements_crypto() {
test_helper::<Crypto>()
}
Loading

0 comments on commit 9cdd483

Please sign in to comment.