Embedded SDK
Embedded SDK
cx_hmac.c
Go to the documentation of this file.
1 
2 /*******************************************************************************
3  * Ledger Nano S - Secure firmware
4  * (c) 2022 Ledger
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  ********************************************************************************/
18 
19 #ifdef HAVE_HMAC
20 
21 #include "cx_hmac.h"
22 #include "cx_hash.h"
23 #include "cx_ram.h"
24 #include "errors.h"
25 #include "exceptions.h"
26 #include "lcx_common.h"
27 
28 #include <string.h>
29 
30 #define IPAD 0x36u
31 #define OPAD 0x5cu
32 
33 static size_t cx_get_block_size(cx_md_t md)
34 {
35  const cx_hash_info_t *info = cx_hash_get_info(md);
36  if (info == NULL) {
37  return 0;
38  }
39  return info->block_size;
40 }
41 
42 static cx_md_t cx_get_algorithm(cx_hmac_t *ctx)
43 {
44  return ctx->hash_ctx.info->md_type;
45 }
46 
47 static bool cx_is_allowed_digest(cx_md_t md_type)
48 {
49  const cx_md_t allowed_algorithms[] = {
50 #ifdef HAVE_SHA224
51  CX_SHA224,
52 #endif
53 #ifdef HAVE_SHA256
54  CX_SHA256,
55 #endif
56 #ifdef HAVE_SHA384
57  CX_SHA384,
58 #endif
59 #ifdef HAVE_SHA512
60  CX_SHA512,
61 #endif
62 #ifdef HAVE_RIPEMD160
63  CX_RIPEMD160,
64 #endif
65  };
66  for (unsigned int i = 0; i < sizeof(allowed_algorithms) / sizeof(allowed_algorithms[0]); i++) {
67  if (allowed_algorithms[i] == md_type) {
68  return true;
69  }
70  }
71  return false;
72 }
73 
74 cx_err_t cx_hmac_init(cx_hmac_t *ctx, cx_md_t hash_id, const uint8_t *key, size_t key_len)
75 {
76  cx_hash_t *hash_ctx;
77  cx_err_t error;
78 
79  if ((ctx == NULL) || (!cx_is_allowed_digest(hash_id)) || (key == NULL && key_len != 0)) {
80  return CX_INVALID_PARAMETER;
81  }
82 
83  hash_ctx = &ctx->hash_ctx;
84  memset(ctx, 0, sizeof(cx_hmac_t));
85  size_t block_size = cx_get_block_size(hash_id);
86 
87  if (key) {
88  if (key_len > block_size) {
89  CX_CHECK(cx_hash_init(hash_ctx, hash_id));
90  CX_CHECK(cx_hash_update(hash_ctx, key, key_len));
91  CX_CHECK(cx_hash_final(hash_ctx, ctx->key));
92  }
93  else {
94  memcpy(ctx->key, key, key_len);
95  }
96 
97  for (unsigned int i = 0; i < block_size; i++) {
98  ctx->key[i] ^= IPAD;
99  }
100  }
101 
102  CX_CHECK(cx_hash_init(hash_ctx, hash_id));
103  CX_CHECK(cx_hash_update(hash_ctx, ctx->key, block_size));
104 end:
105  return error;
106 }
107 
108 cx_err_t cx_hmac_update(cx_hmac_t *ctx, const uint8_t *data, size_t data_len)
109 {
110  if (data_len == 0) {
111  return CX_OK;
112  }
113  return cx_hash_update(&ctx->hash_ctx, data, data_len);
114 }
115 
116 cx_err_t cx_hmac_final(cx_hmac_t *ctx, uint8_t *out, size_t *out_len)
117 {
118  cx_err_t error;
119 
120  uint8_t inner_hash[MAX_HASH_SIZE];
121  uint8_t hkey[MAX_HASH_BLOCK_SIZE];
122 
123  cx_hash_t *hash_ctx = &ctx->hash_ctx;
124 
125  cx_md_t hash_algorithm = cx_get_algorithm(ctx);
126  size_t block_size = cx_get_block_size(hash_algorithm);
127  size_t hash_output_size = cx_hash_get_size(hash_ctx);
128 
129  CX_CHECK(cx_hash_final(hash_ctx, inner_hash));
130 
131  // hash key xor 5c (and 36 to remove prepadding at init)
132  memcpy(hkey, ctx->key, block_size);
133  for (unsigned int i = 0; i < block_size; i++) {
134  hkey[i] ^= OPAD ^ IPAD;
135  }
136 
137  CX_CHECK(cx_hash_init(hash_ctx, hash_algorithm));
138  CX_CHECK(cx_hash_update(hash_ctx, hkey, block_size));
139  CX_CHECK(cx_hash_update(hash_ctx, inner_hash, hash_output_size));
140  CX_CHECK(cx_hash_final(hash_ctx, hkey));
141 
142  // length result
143  if (*out_len >= hash_output_size) {
144  *out_len = hash_output_size;
145  }
146  memcpy(out, hkey, *out_len);
147 end:
148  return error;
149 }
150 
151 #ifdef HAVE_SHA224
152 cx_err_t cx_hmac_sha224_init(cx_hmac_sha256_t *hmac, const uint8_t *key, unsigned int key_len)
153 {
154  return cx_hmac_init((cx_hmac_t *) hmac, CX_SHA224, key, key_len);
155 }
156 #endif
157 #ifdef HAVE_SHA256
158 cx_err_t cx_hmac_sha256_init_no_throw(cx_hmac_sha256_t *hmac, const uint8_t *key, size_t key_len)
159 {
160  return cx_hmac_init((cx_hmac_t *) hmac, CX_SHA256, key, key_len);
161 }
162 #endif
163 
164 #ifdef HAVE_SHA384
165 cx_err_t cx_hmac_sha384_init(cx_hmac_sha512_t *hmac, const uint8_t *key, unsigned int key_len)
166 {
167  return cx_hmac_init((cx_hmac_t *) hmac, CX_SHA384, key, key_len);
168 }
169 #endif
170 #ifdef HAVE_SHA512
171 cx_err_t cx_hmac_sha512_init_no_throw(cx_hmac_sha512_t *hmac, const uint8_t *key, size_t key_len)
172 {
173  return cx_hmac_init((cx_hmac_t *) hmac, CX_SHA512, key, key_len);
174 }
175 #endif
176 
177 #ifdef HAVE_RIPEMD160
178 cx_err_t cx_hmac_ripemd160_init_no_throw(cx_hmac_ripemd160_t *hmac,
179  const uint8_t *key,
180  size_t key_len)
181 {
182  return cx_hmac_init((cx_hmac_t *) hmac, CX_RIPEMD160, key, key_len);
183 }
184 #endif
185 
186 cx_err_t cx_hmac_no_throw(cx_hmac_t *hmac,
187  uint32_t mode,
188  const uint8_t *in,
189  size_t len,
190  uint8_t *out,
191  size_t out_len)
192 {
193  size_t output_size = 0;
194  cx_err_t error = CX_OK;
195 
196  if (in == NULL && len != 0) {
197  return CX_INVALID_PARAMETER;
198  }
199  if (out == NULL && out_len != 0) {
200  return CX_INVALID_PARAMETER;
201  }
202 
203  if (in != NULL) {
204  CX_CHECK(cx_hmac_update(hmac, in, len));
205  }
206 
207  if (mode & CX_LAST) {
208  output_size = out_len;
209  CX_CHECK(cx_hmac_final(hmac, out, &output_size));
210 
211  if (!(mode & CX_NO_REINIT)) {
212  CX_CHECK(cx_hmac_init(hmac, cx_get_algorithm(hmac), NULL, 0));
213  }
214  }
215 
216 end:
217  return error;
218 }
219 
220 #ifdef HAVE_SHA256
221 size_t cx_hmac_sha256(const uint8_t *key,
222  size_t key_len,
223  const uint8_t *in,
224  size_t len,
225  uint8_t *out,
226  size_t out_len)
227 {
228  size_t mac_len = out_len;
229  if (cx_hmac_init(&G_cx.hmac, CX_SHA256, key, key_len) != CX_OK
230  || cx_hmac_update(&G_cx.hmac, in, len) != CX_OK
231  || cx_hmac_final(&G_cx.hmac, out, &mac_len) != CX_OK) {
232  mac_len = 0;
233  }
234  explicit_bzero(&G_cx.hmac, sizeof(cx_hmac_sha256_t));
235  return mac_len;
236 }
237 #endif
238 
239 #ifdef HAVE_SHA512
240 size_t cx_hmac_sha512(const uint8_t *key,
241  size_t key_len,
242  const uint8_t *in,
243  size_t len,
244  uint8_t *out,
245  size_t out_len)
246 {
247  size_t mac_len = out_len;
248  if (cx_hmac_init(&G_cx.hmac, CX_SHA512, key, key_len) != CX_OK
249  || cx_hmac_update(&G_cx.hmac, in, len) != CX_OK
250  || cx_hmac_final(&G_cx.hmac, out, &mac_len) != CX_OK) {
251  mac_len = 0;
252  }
253  explicit_bzero(&G_cx.hmac, sizeof(cx_hmac_sha512_t));
254  return mac_len;
255 }
256 #endif
257 
258 #endif // HAVE_HMAC
union cx_u G_cx
Definition: cx_ram.c:21
Cryptography flags.
#define CX_NO_REINIT
Definition: lcx_common.h:183
#define CX_LAST
Definition: lcx_common.h:115
unsigned char uint8_t
Definition: usbd_conf.h:53