Skip to content

Commit

Permalink
feat(hmac): first implementation for ripemd160, sha256 and sha512
Browse files Browse the repository at this point in the history
  • Loading branch information
bboilot-ledger committed Aug 16, 2024
1 parent e479bab commit 18751dc
Show file tree
Hide file tree
Showing 4 changed files with 259 additions and 0 deletions.
170 changes: 170 additions & 0 deletions ledger_device_sdk/src/hmac.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
//! Hash-based message authentication code (HMAC) related functions
use ledger_secure_sdk_sys::{
cx_hmac_final, cx_hmac_no_throw, cx_hmac_t, cx_hmac_update, CX_INVALID_PARAMETER, CX_LAST,
CX_OK,
};

pub mod ripemd;
pub mod sha2;

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum HMACError {
InvalidParameter,
InvalidOutputLength,
InternalError,
}

impl From<u32> for HMACError {
fn from(x: u32) -> HMACError {
match x {
CX_INVALID_PARAMETER => HMACError::InvalidParameter,
_ => HMACError::InternalError,
}
}
}

impl From<HMACError> for u32 {
fn from(e: HMACError) -> u32 {
e as u32
}
}

/// Defines the behavior of a rust HMAC object.
/// The implementation for a given algorithm is done using a rust macro
/// to avoid code duplication since only the C structures and functions
/// imported from the C SDK change.
pub trait HMACInit: Sized {
/// Recovers a mutable version of the HMAC context that can be used
/// to call HMAC related method in the C SDK.
fn as_ctx_mut(&mut self) -> &mut cx_hmac_t;
/// Recovers a constant version of the HMAC context that can be used
/// to call HMAC related method in the C SDK.
fn as_ctx(&self) -> &cx_hmac_t;
/// Creates the HMAC object by initializing the associated context using
/// the related C structure.
fn new(key: &[u8]) -> Self;

/// Computes a HMAC in one line by providing the complete input as well as the
/// output buffer.
/// An error can be returned if one of the parameter is invalid
/// or if the output buffer size is not enough.
fn hmac(&mut self, input: &[u8], output: &mut [u8]) -> Result<(), HMACError> {
let err = unsafe {
cx_hmac_no_throw(
self.as_ctx_mut(),
CX_LAST,
input.as_ptr(),
input.len(),
output.as_mut_ptr(),
output.len(),
)
};
if err != CX_OK {
Err(err.into())
} else {
Ok(())
}
}

/// Updates the current HMAC object state with the given input data.
/// This method may be called as many times needed (useful for large bufferized
/// inputs). This method should not be called after `finalize`.
/// An error can be returned if the input is invalid or the context in a wrong state.
fn update(&mut self, input: &[u8]) -> Result<(), HMACError> {
let err = unsafe { cx_hmac_update(self.as_ctx_mut(), input.as_ptr(), input.len()) };
if err != CX_OK {
Err(err.into())
} else {
Ok(())
}
}

/// Finalizes the computation of the MAC and stores the result in the output buffer
/// as well as returning the MAC length.
/// This method should be called after one or many calls to `update`.
/// An error can be returned if one of the parameter is invalid
/// or if the output buffer size is not enough.
fn finalize(&mut self, output: &mut [u8]) -> Result<usize, HMACError> {
let mut out_len = output.len();
let err = unsafe { cx_hmac_final(self.as_ctx_mut(), output.as_mut_ptr(), &mut out_len) };
if err != CX_OK {
Err(err.into())
} else {
Ok(out_len)
}
}
}

/// This macro can be used to implement the HMACInit trait for a given hash
/// algorithm by providing the structure name, the C context name, and the C
/// context initialization function.
macro_rules! impl_hmac {
($typename:ident, $ctxname:ident, $initfname:ident) => {
#[derive(Default)]
#[allow(non_camel_case_types)]
pub struct $typename {
ctx: $ctxname,
}
impl HMACInit for $typename {
fn as_ctx_mut(&mut self) -> &mut cx_hmac_t {
unsafe { mem::transmute::<&mut $ctxname, &mut cx_hmac_t>(&mut self.ctx) }
}

fn as_ctx(&self) -> &cx_hmac_t {
unsafe { mem::transmute::<&$ctxname, &cx_hmac_t>(&self.ctx) }
}

fn new(key: &[u8]) -> Self {
let mut ctx: $typename = Default::default();
let _err = unsafe { $initfname(&mut ctx.ctx, key.as_ptr(), key.len()) };
ctx
}
}
};
}
pub(crate) use impl_hmac;

#[cfg(test)]
mod tests {
use crate::assert_eq_err as assert_eq;
use crate::hmac::ripemd::Ripemd160;
use crate::hmac::HMACInit;
use crate::testing::TestType;
use testmacro::test_item as test;

const TEST_MSG: &[u8; 29] = b"Not your keys, not your coins";
const TEST_KEY: &[u8; 16] = b"hmac test key!!!";

#[test]
fn test_hmac_oneline() {
let mut mac = Ripemd160::new(TEST_KEY);

let mut output: [u8; 20] = [0u8; 20];

let _ = mac.hmac(TEST_MSG, &mut output);

let expected = [
0xfa, 0xde, 0x57, 0x70, 0xf8, 0xa5, 0x04, 0x1a, 0xac, 0xdb, 0xe1, 0xc5, 0x64, 0x21,
0x0d, 0xa6, 0x89, 0x9b, 0x2e, 0x6f,
];
assert_eq!(&output, &expected);
}

#[test]
fn test_hmac_update() {
let mut mac = Ripemd160::new(TEST_KEY);

let mut output: [u8; 20] = [0u8; 20];

let _ = mac.update(TEST_MSG);

let res = mac.finalize(&mut output);

let expected = [
0xfa, 0xde, 0x57, 0x70, 0xf8, 0xa5, 0x04, 0x1a, 0xac, 0xdb, 0xe1, 0xc5, 0x64, 0x21,
0x0d, 0xa6, 0x89, 0x9b, 0x2e, 0x6f,
];
assert_eq!(&output, &expected);
assert_eq!(res.unwrap(), expected.len());
}
}
33 changes: 33 additions & 0 deletions ledger_device_sdk/src/hmac/ripemd.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use core::mem;
use super::HMACInit;
use ledger_secure_sdk_sys::{cx_hmac_t, cx_hmac_ripemd160_init_no_throw, cx_hmac_ripemd160_t};

use super::impl_hmac;
impl_hmac!(Ripemd160, cx_hmac_ripemd160_t, cx_hmac_ripemd160_init_no_throw);


#[cfg(test)]
mod tests {
use crate::assert_eq_err as assert_eq;
use crate::hmac::ripemd::*;
use crate::testing::TestType;
use testmacro::test_item as test;

const TEST_MSG: &[u8; 29] = b"Not your keys, not your coins";
const TEST_KEY: &[u8; 16] = b"hmac test key!!!";

#[test]
fn test_hmac_ripemd160() {
let mut mac = Ripemd160::new(TEST_KEY);

let mut output: [u8; 20] = [0u8; 20];

let _ = mac.hmac(TEST_MSG, &mut output);

let expected = [
0xfa, 0xde, 0x57, 0x70, 0xf8, 0xa5, 0x04, 0x1a, 0xac, 0xdb, 0xe1, 0xc5, 0x64, 0x21,
0x0d, 0xa6, 0x89, 0x9b, 0x2e, 0x6f,
];
assert_eq!(&output, &expected);
}
}
55 changes: 55 additions & 0 deletions ledger_device_sdk/src/hmac/sha2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
use super::HMACInit;
use core::mem;
use ledger_secure_sdk_sys::{
cx_hmac_sha256_init_no_throw, cx_hmac_sha256_t, cx_hmac_sha512_init_no_throw, cx_hmac_sha512_t,
cx_hmac_t,
};

use super::impl_hmac;
impl_hmac!(Sha2_256, cx_hmac_sha256_t, cx_hmac_sha256_init_no_throw);
impl_hmac!(Sha2_512, cx_hmac_sha512_t, cx_hmac_sha512_init_no_throw);

#[cfg(test)]
mod tests {
use crate::assert_eq_err as assert_eq;
use crate::hmac::sha2::*;
use crate::testing::TestType;
use testmacro::test_item as test;

const TEST_MSG: &[u8; 29] = b"Not your keys, not your coins";
const TEST_KEY: &[u8; 16] = b"hmac test key!!!";

#[test]
fn test_hmac_sha256() {
let mut mac = Sha2_256::new(TEST_KEY);

let mut output: [u8; 32] = [0u8; 32];

let _ = mac.hmac(TEST_MSG, &mut output);

let expected = [
0x4d, 0x23, 0x82, 0xff, 0xc3, 0xb0, 0x60, 0x48, 0x59, 0xc0, 0xe5, 0x28, 0xf3, 0x66,
0xa0, 0xba, 0x5b, 0xcb, 0x2c, 0x24, 0x10, 0x9c, 0x9d, 0x0b, 0x3b, 0x0a, 0x75, 0x8d,
0x0f, 0x5a, 0x2a, 0x13,
];
assert_eq!(&output, &expected);
}

#[test]
fn test_hmac_sha512() {
let mut mac = Sha2_512::new(TEST_KEY);

let mut output: [u8; 64] = [0u8; 64];

let _ = mac.hmac(TEST_MSG, &mut output);

let expected = [
0x2d, 0x03, 0x14, 0x96, 0x68, 0x0e, 0xcc, 0x41, 0x2a, 0x42, 0xf2, 0x45, 0xf8, 0x0b,
0x10, 0x87, 0x43, 0x96, 0x4d, 0x80, 0x5d, 0x93, 0x5c, 0xd1, 0x6b, 0x95, 0xc1, 0x7a,
0xed, 0xbd, 0xd8, 0x8c, 0xf8, 0xa7, 0x60, 0xed, 0x04, 0xa2, 0x5b, 0x8d, 0xd8, 0x3d,
0xa3, 0x13, 0xa1, 0x6a, 0x07, 0x33, 0x49, 0x06, 0x15, 0x79, 0x70, 0xf3, 0xe9, 0x9a,
0xff, 0x25, 0xb6, 0x5e, 0x37, 0xd1, 0x7e, 0x2b,
];
assert_eq!(&output, &expected);
}
}
1 change: 1 addition & 0 deletions ledger_device_sdk/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pub mod ble;
pub mod ccid;
pub mod ecc;
pub mod hash;
pub mod hmac;
pub mod io;
pub mod nvm;
pub mod random;
Expand Down

0 comments on commit 18751dc

Please sign in to comment.