Skip to content

Commit 37c5852

Browse files
Merge pull request #179 from LedgerHQ/bboilot/hmac
2 parents e18d34f + 24fbc6e commit 37c5852

File tree

6 files changed

+300
-2
lines changed

6 files changed

+300
-2
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ledger_device_sdk/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "ledger_device_sdk"
3-
version = "1.14.1"
3+
version = "1.15.0"
44
authors = ["yhql", "yogh333", "agrojean-ledger", "kingofpayne"]
55
edition = "2021"
66
license.workspace = true

ledger_device_sdk/src/hmac.rs

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
//! Hash-based message authentication code (HMAC) related functions
2+
use ledger_secure_sdk_sys::{
3+
cx_hmac_final, cx_hmac_no_throw, cx_hmac_t, cx_hmac_update, CX_INVALID_PARAMETER, CX_LAST,
4+
CX_OK,
5+
};
6+
7+
pub mod ripemd;
8+
pub mod sha2;
9+
10+
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
11+
pub enum HMACError {
12+
InvalidParameter,
13+
InvalidOutputLength,
14+
InternalError,
15+
}
16+
17+
impl From<u32> for HMACError {
18+
fn from(x: u32) -> HMACError {
19+
match x {
20+
CX_INVALID_PARAMETER => HMACError::InvalidParameter,
21+
_ => HMACError::InternalError,
22+
}
23+
}
24+
}
25+
26+
impl From<HMACError> for u32 {
27+
fn from(e: HMACError) -> u32 {
28+
e as u32
29+
}
30+
}
31+
32+
/// Defines the behavior of a rust HMAC object.
33+
/// The implementation for a given algorithm is done using a rust macro
34+
/// to avoid code duplication since only the C structures and functions
35+
/// imported from the C SDK change.
36+
pub trait HMACInit: Sized {
37+
/// Recovers a mutable version of the HMAC context that can be used
38+
/// to call HMAC related method in the C SDK.
39+
fn as_ctx_mut(&mut self) -> &mut cx_hmac_t;
40+
/// Recovers a constant version of the HMAC context that can be used
41+
/// to call HMAC related method in the C SDK.
42+
fn as_ctx(&self) -> &cx_hmac_t;
43+
/// Creates the HMAC object by initializing the associated context using
44+
/// the related C structure.
45+
fn new(key: &[u8]) -> Self;
46+
47+
/// Computes a HMAC in one line by providing the complete input as well as the
48+
/// output buffer.
49+
/// An error can be returned if one of the parameter is invalid
50+
/// or if the output buffer size is not enough.
51+
fn hmac(&mut self, input: &[u8], output: &mut [u8]) -> Result<(), HMACError> {
52+
let err = unsafe {
53+
cx_hmac_no_throw(
54+
self.as_ctx_mut(),
55+
CX_LAST,
56+
input.as_ptr(),
57+
input.len(),
58+
output.as_mut_ptr(),
59+
output.len(),
60+
)
61+
};
62+
if err != CX_OK {
63+
Err(err.into())
64+
} else {
65+
Ok(())
66+
}
67+
}
68+
69+
/// Updates the current HMAC object state with the given input data.
70+
/// This method may be called as many times needed (useful for large bufferized
71+
/// inputs). This method should not be called after `finalize`.
72+
/// An error can be returned if the input is invalid or the context in a wrong state.
73+
fn update(&mut self, input: &[u8]) -> Result<(), HMACError> {
74+
let err = unsafe { cx_hmac_update(self.as_ctx_mut(), input.as_ptr(), input.len()) };
75+
if err != CX_OK {
76+
Err(err.into())
77+
} else {
78+
Ok(())
79+
}
80+
}
81+
82+
/// Finalizes the computation of the MAC and stores the result in the output buffer
83+
/// as well as returning the MAC length.
84+
/// This method should be called after one or many calls to `update`.
85+
/// An error can be returned if one of the parameter is invalid
86+
/// or if the output buffer size is not enough.
87+
fn finalize(&mut self, output: &mut [u8]) -> Result<usize, HMACError> {
88+
let mut out_len = output.len();
89+
let err = unsafe { cx_hmac_final(self.as_ctx_mut(), output.as_mut_ptr(), &mut out_len) };
90+
if err != CX_OK {
91+
Err(err.into())
92+
} else {
93+
Ok(out_len)
94+
}
95+
}
96+
}
97+
98+
/// This macro can be used to implement the HMACInit trait for a given hash
99+
/// algorithm by providing the structure name, the C context name, and the C
100+
/// context initialization function.
101+
macro_rules! impl_hmac {
102+
($typename:ident, $ctxname:ident, $initfname:ident) => {
103+
#[derive(Default)]
104+
#[allow(non_camel_case_types)]
105+
pub struct $typename {
106+
ctx: $ctxname,
107+
}
108+
impl HMACInit for $typename {
109+
fn as_ctx_mut(&mut self) -> &mut cx_hmac_t {
110+
unsafe { mem::transmute::<&mut $ctxname, &mut cx_hmac_t>(&mut self.ctx) }
111+
}
112+
113+
fn as_ctx(&self) -> &cx_hmac_t {
114+
unsafe { mem::transmute::<&$ctxname, &cx_hmac_t>(&self.ctx) }
115+
}
116+
117+
fn new(key: &[u8]) -> Self {
118+
let mut ctx: $typename = Default::default();
119+
let _err = unsafe {
120+
$initfname(&mut ctx.ctx, key.as_ptr(), key.len().try_into().unwrap())
121+
};
122+
ctx
123+
}
124+
}
125+
};
126+
}
127+
pub(crate) use impl_hmac;
128+
129+
#[cfg(test)]
130+
mod tests {
131+
use crate::assert_eq_err as assert_eq;
132+
use crate::hmac::ripemd::Ripemd160;
133+
use crate::hmac::HMACInit;
134+
use crate::testing::TestType;
135+
use testmacro::test_item as test;
136+
137+
const TEST_MSG: &[u8; 29] = b"Not your keys, not your coins";
138+
const TEST_KEY: &[u8; 16] = b"hmac test key!!!";
139+
140+
#[test]
141+
fn test_hmac_oneline() {
142+
let mut mac = Ripemd160::new(TEST_KEY);
143+
144+
let mut output: [u8; 20] = [0u8; 20];
145+
146+
let _ = mac.hmac(TEST_MSG, &mut output);
147+
148+
let expected = [
149+
0xfa, 0xde, 0x57, 0x70, 0xf8, 0xa5, 0x04, 0x1a, 0xac, 0xdb, 0xe1, 0xc5, 0x64, 0x21,
150+
0x0d, 0xa6, 0x89, 0x9b, 0x2e, 0x6f,
151+
];
152+
assert_eq!(&output, &expected);
153+
}
154+
155+
#[test]
156+
fn test_hmac_update() {
157+
let mut mac = Ripemd160::new(TEST_KEY);
158+
159+
let mut output: [u8; 20] = [0u8; 20];
160+
161+
let _ = mac.update(TEST_MSG);
162+
163+
let res = mac.finalize(&mut output);
164+
165+
let expected = [
166+
0xfa, 0xde, 0x57, 0x70, 0xf8, 0xa5, 0x04, 0x1a, 0xac, 0xdb, 0xe1, 0xc5, 0x64, 0x21,
167+
0x0d, 0xa6, 0x89, 0x9b, 0x2e, 0x6f,
168+
];
169+
assert_eq!(&output, &expected);
170+
assert_eq!(res.unwrap(), expected.len());
171+
}
172+
}

ledger_device_sdk/src/hmac/ripemd.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
use super::HMACInit;
2+
use core::mem;
3+
use ledger_secure_sdk_sys::{cx_hmac_ripemd160_init_no_throw, cx_hmac_ripemd160_t, cx_hmac_t};
4+
5+
use super::impl_hmac;
6+
impl_hmac!(
7+
Ripemd160,
8+
cx_hmac_ripemd160_t,
9+
cx_hmac_ripemd160_init_no_throw
10+
);
11+
12+
#[cfg(test)]
13+
mod tests {
14+
use crate::assert_eq_err as assert_eq;
15+
use crate::hmac::ripemd::*;
16+
use crate::testing::TestType;
17+
use testmacro::test_item as test;
18+
19+
const TEST_MSG: &[u8; 29] = b"Not your keys, not your coins";
20+
const TEST_KEY: &[u8; 16] = b"hmac test key!!!";
21+
22+
#[test]
23+
fn test_hmac_ripemd160() {
24+
let mut mac = Ripemd160::new(TEST_KEY);
25+
26+
let mut output: [u8; 20] = [0u8; 20];
27+
28+
let _ = mac.hmac(TEST_MSG, &mut output);
29+
30+
let expected = [
31+
0xfa, 0xde, 0x57, 0x70, 0xf8, 0xa5, 0x04, 0x1a, 0xac, 0xdb, 0xe1, 0xc5, 0x64, 0x21,
32+
0x0d, 0xa6, 0x89, 0x9b, 0x2e, 0x6f,
33+
];
34+
assert_eq!(&output, &expected);
35+
}
36+
}

ledger_device_sdk/src/hmac/sha2.rs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
use super::HMACInit;
2+
use core::mem;
3+
use ledger_secure_sdk_sys::{
4+
cx_hmac_sha224_init, cx_hmac_sha256_init_no_throw, cx_hmac_sha256_t, cx_hmac_sha384_init,
5+
cx_hmac_sha512_init_no_throw, cx_hmac_sha512_t, cx_hmac_t,
6+
};
7+
8+
use super::impl_hmac;
9+
impl_hmac!(Sha2_224, cx_hmac_sha256_t, cx_hmac_sha224_init);
10+
impl_hmac!(Sha2_256, cx_hmac_sha256_t, cx_hmac_sha256_init_no_throw);
11+
impl_hmac!(Sha2_384, cx_hmac_sha512_t, cx_hmac_sha384_init);
12+
impl_hmac!(Sha2_512, cx_hmac_sha512_t, cx_hmac_sha512_init_no_throw);
13+
14+
#[cfg(test)]
15+
mod tests {
16+
use crate::assert_eq_err as assert_eq;
17+
use crate::hmac::sha2::*;
18+
use crate::testing::TestType;
19+
use testmacro::test_item as test;
20+
21+
const TEST_MSG: &[u8; 29] = b"Not your keys, not your coins";
22+
const TEST_KEY: &[u8; 16] = b"hmac test key!!!";
23+
24+
#[test]
25+
fn test_hmac_sha224() {
26+
let mut mac = Sha2_224::new(TEST_KEY);
27+
28+
let mut output: [u8; 28] = [0u8; 28];
29+
30+
let _ = mac.hmac(TEST_MSG, &mut output);
31+
32+
let expected = [
33+
0xc4, 0x64, 0x80, 0xfb, 0xea, 0xc7, 0x75, 0x6d, 0xee, 0xc1, 0x6a, 0xcb, 0x6d, 0xae,
34+
0x6a, 0xfa, 0x5d, 0x03, 0x17, 0x73, 0xd6, 0x4d, 0x49, 0xea, 0xa8, 0x5e, 0x4c, 0x1d,
35+
];
36+
assert_eq!(&output, &expected);
37+
}
38+
39+
#[test]
40+
fn test_hmac_sha256() {
41+
let mut mac = Sha2_256::new(TEST_KEY);
42+
43+
let mut output: [u8; 32] = [0u8; 32];
44+
45+
let _ = mac.hmac(TEST_MSG, &mut output);
46+
47+
let expected = [
48+
0x4d, 0x23, 0x82, 0xff, 0xc3, 0xb0, 0x60, 0x48, 0x59, 0xc0, 0xe5, 0x28, 0xf3, 0x66,
49+
0xa0, 0xba, 0x5b, 0xcb, 0x2c, 0x24, 0x10, 0x9c, 0x9d, 0x0b, 0x3b, 0x0a, 0x75, 0x8d,
50+
0x0f, 0x5a, 0x2a, 0x13,
51+
];
52+
assert_eq!(&output, &expected);
53+
}
54+
55+
#[test]
56+
fn test_hmac_sha384() {
57+
let mut mac = Sha2_384::new(TEST_KEY);
58+
59+
let mut output: [u8; 48] = [0u8; 48];
60+
61+
let _ = mac.hmac(TEST_MSG, &mut output);
62+
63+
let expected = [
64+
0x20, 0x6d, 0x0d, 0xfd, 0xfd, 0x22, 0x43, 0xde, 0xb0, 0x23, 0xf8, 0x56, 0x63, 0xd1,
65+
0xa2, 0x1e, 0xc1, 0x6a, 0x72, 0x6b, 0xa7, 0x8e, 0xc2, 0x25, 0xe2, 0x1e, 0x3e, 0x3b,
66+
0xb2, 0xf0, 0x55, 0x1d, 0x4e, 0xde, 0x5f, 0x81, 0xf6, 0xa1, 0xff, 0x8e, 0x76, 0x86,
67+
0xf1, 0x0f, 0x7a, 0xec, 0xbe, 0x35,
68+
];
69+
assert_eq!(&output, &expected);
70+
}
71+
72+
#[test]
73+
fn test_hmac_sha512() {
74+
let mut mac = Sha2_512::new(TEST_KEY);
75+
76+
let mut output: [u8; 64] = [0u8; 64];
77+
78+
let _ = mac.hmac(TEST_MSG, &mut output);
79+
80+
let expected = [
81+
0x2d, 0x03, 0x14, 0x96, 0x68, 0x0e, 0xcc, 0x41, 0x2a, 0x42, 0xf2, 0x45, 0xf8, 0x0b,
82+
0x10, 0x87, 0x43, 0x96, 0x4d, 0x80, 0x5d, 0x93, 0x5c, 0xd1, 0x6b, 0x95, 0xc1, 0x7a,
83+
0xed, 0xbd, 0xd8, 0x8c, 0xf8, 0xa7, 0x60, 0xed, 0x04, 0xa2, 0x5b, 0x8d, 0xd8, 0x3d,
84+
0xa3, 0x13, 0xa1, 0x6a, 0x07, 0x33, 0x49, 0x06, 0x15, 0x79, 0x70, 0xf3, 0xe9, 0x9a,
85+
0xff, 0x25, 0xb6, 0x5e, 0x37, 0xd1, 0x7e, 0x2b,
86+
];
87+
assert_eq!(&output, &expected);
88+
}
89+
}

ledger_device_sdk/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ pub mod ble;
1414
pub mod ccid;
1515
pub mod ecc;
1616
pub mod hash;
17+
pub mod hmac;
1718
pub mod io;
1819
pub mod nvm;
1920
pub mod random;

0 commit comments

Comments
 (0)