Skip to content

Commit

Permalink
Merge pull request #179 from LedgerHQ/bboilot/hmac
Browse files Browse the repository at this point in the history
  • Loading branch information
bboilot-ledger authored Aug 22, 2024
2 parents e18d34f + 24fbc6e commit 37c5852
Show file tree
Hide file tree
Showing 6 changed files with 300 additions and 2 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion ledger_device_sdk/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "ledger_device_sdk"
version = "1.14.1"
version = "1.15.0"
authors = ["yhql", "yogh333", "agrojean-ledger", "kingofpayne"]
edition = "2021"
license.workspace = true
Expand Down
172 changes: 172 additions & 0 deletions ledger_device_sdk/src/hmac.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
//! 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().try_into().unwrap())
};
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());
}
}
36 changes: 36 additions & 0 deletions ledger_device_sdk/src/hmac/ripemd.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use super::HMACInit;
use core::mem;
use ledger_secure_sdk_sys::{cx_hmac_ripemd160_init_no_throw, cx_hmac_ripemd160_t, cx_hmac_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);
}
}
89 changes: 89 additions & 0 deletions ledger_device_sdk/src/hmac/sha2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
use super::HMACInit;
use core::mem;
use ledger_secure_sdk_sys::{
cx_hmac_sha224_init, cx_hmac_sha256_init_no_throw, cx_hmac_sha256_t, cx_hmac_sha384_init,
cx_hmac_sha512_init_no_throw, cx_hmac_sha512_t, cx_hmac_t,
};

use super::impl_hmac;
impl_hmac!(Sha2_224, cx_hmac_sha256_t, cx_hmac_sha224_init);
impl_hmac!(Sha2_256, cx_hmac_sha256_t, cx_hmac_sha256_init_no_throw);
impl_hmac!(Sha2_384, cx_hmac_sha512_t, cx_hmac_sha384_init);
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_sha224() {
let mut mac = Sha2_224::new(TEST_KEY);

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

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

let expected = [
0xc4, 0x64, 0x80, 0xfb, 0xea, 0xc7, 0x75, 0x6d, 0xee, 0xc1, 0x6a, 0xcb, 0x6d, 0xae,
0x6a, 0xfa, 0x5d, 0x03, 0x17, 0x73, 0xd6, 0x4d, 0x49, 0xea, 0xa8, 0x5e, 0x4c, 0x1d,
];
assert_eq!(&output, &expected);
}

#[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_sha384() {
let mut mac = Sha2_384::new(TEST_KEY);

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

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

let expected = [
0x20, 0x6d, 0x0d, 0xfd, 0xfd, 0x22, 0x43, 0xde, 0xb0, 0x23, 0xf8, 0x56, 0x63, 0xd1,
0xa2, 0x1e, 0xc1, 0x6a, 0x72, 0x6b, 0xa7, 0x8e, 0xc2, 0x25, 0xe2, 0x1e, 0x3e, 0x3b,
0xb2, 0xf0, 0x55, 0x1d, 0x4e, 0xde, 0x5f, 0x81, 0xf6, 0xa1, 0xff, 0x8e, 0x76, 0x86,
0xf1, 0x0f, 0x7a, 0xec, 0xbe, 0x35,
];
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 37c5852

Please sign in to comment.