1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
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());
}
}