1/* Copyright (c) (2014-2019,2021,2022) Apple Inc. All rights reserved.
2 *
3 * corecrypto is licensed under Apple Inc.’s Internal Use License Agreement (which
4 * is contained in the License.txt file distributed with corecrypto) and only to
5 * people who accept that license. IMPORTANT: Any license rights granted to you by
6 * Apple Inc. (if any) are limited to internal use within your organization only on
7 * devices and computers you own or control, for the sole purpose of verifying the
8 * security characteristics and correct functioning of the Apple Software. You may
9 * not, directly or indirectly, redistribute the Apple Software or any portions thereof.
10 *
11 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
12 *
13 * This file contains Original Code and/or Modifications of Original Code
14 * as defined in and that are subject to the Apple Public Source License
15 * Version 2.0 (the 'License'). You may not use this file except in
16 * compliance with the License. The rights granted to you under the License
17 * may not be used to create, or enable the creation or redistribution of,
18 * unlawful or unlicensed copies of an Apple operating system, or to
19 * circumvent, violate, or enable the circumvention or violation of, any
20 * terms of an Apple operating system software license agreement.
21 *
22 * Please obtain a copy of the License at
23 * http://www.opensource.apple.com/apsl/ and read it before using this file.
24 *
25 * The Original Code and all software distributed under the License are
26 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
27 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
28 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
29 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
30 * Please see the License for the specific language governing rights and
31 * limitations under the License.
32 *
33 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
34 */
35
36#include "cc_internal.h"
37
38#include <corecrypto/cc_priv.h>
39#include <corecrypto/ccdrbg.h>
40#include <corecrypto/cchmac.h>
41#include <corecrypto/ccsha2.h>
42#include "cc_macros.h"
43
44// This HMAC DRBG is described in:
45
46// NIST SP 800-90A Rev. 1
47// Recommendation for Random Number Generation Using Deterministic Random Bit Generators
48// June 2015
49
50// See in particular:
51// - 9 DRBG Mechanism Functions
52// - 10.1.2 HMAC_DRBG
53// - B.2 HMAC_DRBGExample
54
55#define DRBG_HMAC_MAX_OUTPUT_SIZE MAX_DIGEST_OUTPUT_SIZE
56
57#define MIN_REQ_ENTROPY(di) ((di)->output_size / 2)
58
59struct ccdrbg_nisthmac_state {
60 const struct ccdrbg_nisthmac_custom *custom;
61 uint8_t key[DRBG_HMAC_MAX_OUTPUT_SIZE];
62 uint8_t V[DRBG_HMAC_MAX_OUTPUT_SIZE];
63 uint64_t reseed_counter;
64};
65
66#define DRBG_NISTHMAC_DEBUG 0
67
68#if DRBG_NISTHMAC_DEBUG
69#include "cc_debug.h"
70
71static void
72dump_state(const char *label, struct ccdrbg_nisthmac_state *drbg_ctx)
73{
74 size_t outlen = drbg_ctx->custom->di->output_size;
75
76 cc_print(label, outlen, drbg_ctx->key);
77 cc_print(label, outlen, drbg_ctx->V);
78}
79#endif
80
81// See NIST SP 800-90A, Rev. 1, 9.4
82static void
83done(struct ccdrbg_state *ctx)
84{
85 struct ccdrbg_nisthmac_state *drbg_ctx = (struct ccdrbg_nisthmac_state *)ctx;
86 cc_clear(len: sizeof(drbg_ctx->key), dst: drbg_ctx->key);
87 cc_clear(len: sizeof(drbg_ctx->V), dst: drbg_ctx->V);
88 drbg_ctx->reseed_counter = UINT64_MAX;
89}
90
91// See NIST SP 800-90A, Rev. 1, 10.1.2.2
92static void
93update(struct ccdrbg_state *ctx, unsigned ndata, ...)
94{
95 struct ccdrbg_nisthmac_state *drbg_ctx = (struct ccdrbg_nisthmac_state *)ctx;
96 const struct ccdigest_info *info = drbg_ctx->custom->di;
97 size_t outlen = info->output_size;
98 size_t data_nbytes = 0;
99 va_list args;
100
101 cchmac_di_decl(info, hmac_ctx);
102
103 for (uint8_t b = 0; b < 2; b += 1) {
104 cchmac_init(di: info, ctx: hmac_ctx, key_len: outlen, key: drbg_ctx->key);
105
106 cchmac_update(di: info, ctx: hmac_ctx, data_len: outlen, data: drbg_ctx->V);
107
108 cchmac_update(di: info, ctx: hmac_ctx, data_len: sizeof(b), data: &b);
109
110 va_start(args, ndata);
111
112 for (unsigned i = 0; i < ndata; i += 1) {
113 size_t nbytes = va_arg(args, size_t);
114 const void *buf = va_arg(args, const void *);
115
116 cchmac_update(di: info, ctx: hmac_ctx, data_len: nbytes, data: buf);
117
118 data_nbytes += nbytes;
119 }
120
121 va_end(args);
122
123 cchmac_final(di: info, ctx: hmac_ctx, mac: drbg_ctx->key);
124
125 cchmac(di: info, key_len: outlen, key: drbg_ctx->key, data_len: outlen, data: drbg_ctx->V, mac: drbg_ctx->V);
126
127 if (data_nbytes == 0) {
128 break;
129 }
130 }
131
132 cchmac_di_clear(info, hmac_ctx);
133}
134
135static bool
136entropy_isvalid(size_t entropy_nbytes, const struct ccdigest_info *info)
137{
138 return (entropy_nbytes <= CCDRBG_MAX_ENTROPY_SIZE) && (entropy_nbytes >= MIN_REQ_ENTROPY(info));
139}
140
141// See NIST SP 800-90A, Rev. 1, 9.1 and 10.1.2.3
142static int
143init(const struct ccdrbg_info *info,
144 struct ccdrbg_state *ctx,
145 size_t entropy_nbytes,
146 const void *entropy,
147 size_t nonce_nbytes,
148 const void *nonce,
149 size_t ps_nbytes,
150 const void *ps)
151{
152 struct ccdrbg_nisthmac_state *drbg_ctx = (struct ccdrbg_nisthmac_state *)ctx;
153 drbg_ctx->custom = info->custom;
154 const struct ccdigest_info *digest_info = drbg_ctx->custom->di;
155 size_t outlen = digest_info->output_size;
156
157 int status = CCDRBG_STATUS_PARAM_ERROR;
158 cc_require(outlen <= DRBG_HMAC_MAX_OUTPUT_SIZE, out);
159 cc_require(entropy_isvalid(entropy_nbytes, digest_info), out);
160 cc_require(ps_nbytes <= CCDRBG_MAX_PSINPUT_SIZE, out);
161
162 status = CCDRBG_STATUS_OK;
163
164 cc_memset(drbg_ctx->key, 0, outlen);
165 cc_memset(drbg_ctx->V, 1, outlen);
166
167 update(ctx, ndata: 3, entropy_nbytes, entropy, nonce_nbytes, nonce, ps_nbytes, ps);
168
169 drbg_ctx->reseed_counter = 1;
170
171out:
172 return status;
173}
174
175static bool
176add_isvalid(size_t add_nbytes)
177{
178 return add_nbytes <= CCDRBG_MAX_ADDITIONALINPUT_SIZE;
179}
180
181// See NIST SP 800-90A, Rev. 1, 9.2 and 10.1.2.4
182static int
183reseed(struct ccdrbg_state *ctx, size_t entropy_nbytes, const void *entropy, size_t add_nbytes, const void *add)
184{
185 struct ccdrbg_nisthmac_state *drbg_ctx = (struct ccdrbg_nisthmac_state *)ctx;
186 const struct ccdigest_info *digest_info = drbg_ctx->custom->di;
187
188 int status = CCDRBG_STATUS_PARAM_ERROR;
189 cc_require(entropy_isvalid(entropy_nbytes, digest_info), out);
190 cc_require(add_isvalid(add_nbytes), out);
191
192 status = CCDRBG_STATUS_OK;
193
194 update(ctx, ndata: 2, entropy_nbytes, entropy, add_nbytes, add);
195
196 drbg_ctx->reseed_counter = 1;
197
198out:
199 return status;
200}
201
202static bool
203must_reseed(const struct ccdrbg_state *ctx)
204{
205 const struct ccdrbg_nisthmac_state *drbg_ctx = (const struct ccdrbg_nisthmac_state *)ctx;
206
207 return drbg_ctx->custom->strictFIPS &&
208 (drbg_ctx->reseed_counter > CCDRBG_RESEED_INTERVAL);
209}
210
211// See NIST SP 800-90A, Rev. 1, 9.3 and 10.1.2.5
212static int
213generate(struct ccdrbg_state *ctx, size_t out_nbytes, void *out, size_t add_nbytes, const void *add)
214{
215 struct ccdrbg_nisthmac_state *drbg_ctx = (struct ccdrbg_nisthmac_state *)ctx;
216 const struct ccdigest_info *info = drbg_ctx->custom->di;
217 size_t outlen = info->output_size;
218
219 int status = CCDRBG_STATUS_PARAM_ERROR;
220 cc_require(out_nbytes <= CCDRBG_MAX_REQUEST_SIZE, out);
221 cc_require(add_isvalid(add_nbytes), out);
222
223 status = CCDRBG_STATUS_NEED_RESEED;
224 cc_require(!must_reseed(ctx), out);
225
226 status = CCDRBG_STATUS_OK;
227
228 if (add_nbytes > 0) {
229 update(ctx, ndata: 1, add_nbytes, add);
230 }
231
232 uint8_t *out_bytes = out;
233 uint8_t Vprev[DRBG_HMAC_MAX_OUTPUT_SIZE];
234
235 while (out_nbytes > 0) {
236 cc_memcpy(Vprev, drbg_ctx->V, outlen);
237 cchmac(di: info, key_len: outlen, key: drbg_ctx->key, data_len: outlen, data: drbg_ctx->V, mac: drbg_ctx->V);
238
239 // See FIPS 140-2, 4.9.2 Conditional Tests
240 if (cc_cmp_safe(num: outlen, ptr1: Vprev, ptr2: drbg_ctx->V) == 0) {
241 done(ctx);
242 status = CCDRBG_STATUS_ABORT;
243 cc_try_abort(NULL);
244 goto out;
245 }
246
247 size_t n = CC_MIN(out_nbytes, outlen);
248 cc_memcpy(out_bytes, drbg_ctx->V, n);
249
250 out_bytes += n;
251 out_nbytes -= n;
252 }
253
254 update(ctx, ndata: 1, add_nbytes, add);
255
256 drbg_ctx->reseed_counter += 1;
257
258out:
259 cc_clear(len: outlen, dst: Vprev);
260 return status;
261}
262
263void
264ccdrbg_factory_nisthmac(struct ccdrbg_info *info, const struct ccdrbg_nisthmac_custom *custom)
265{
266 CC_ENSURE_DIT_ENABLED
267
268 info->size = sizeof(struct ccdrbg_nisthmac_state) + sizeof(struct ccdrbg_nisthmac_custom);
269 info->init = init;
270 info->generate = generate;
271 info->reseed = reseed;
272 info->done = done;
273 info->custom = custom;
274 info->must_reseed = must_reseed;
275};
276