xref: /xnu-11215.41.3/osfmk/corecrypto/ccdrbg_nisthmac.c (revision 33de042d024d46de5ff4e89f2471de6608e37fa4)
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 
59 struct 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 
71 static void
dump_state(const char * label,struct ccdrbg_nisthmac_state * drbg_ctx)72 dump_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
82 static void
done(struct ccdrbg_state * ctx)83 done(struct ccdrbg_state *ctx)
84 {
85 	struct ccdrbg_nisthmac_state *drbg_ctx = (struct ccdrbg_nisthmac_state *)ctx;
86 	cc_clear(sizeof(drbg_ctx->key), drbg_ctx->key);
87 	cc_clear(sizeof(drbg_ctx->V), drbg_ctx->V);
88 	drbg_ctx->reseed_counter = UINT64_MAX;
89 }
90 
91 // See NIST SP 800-90A, Rev. 1, 10.1.2.2
92 static void
update(struct ccdrbg_state * ctx,unsigned ndata,...)93 update(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(info, hmac_ctx, outlen, drbg_ctx->key);
105 
106 		cchmac_update(info, hmac_ctx, outlen, drbg_ctx->V);
107 
108 		cchmac_update(info, hmac_ctx, sizeof(b), &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(info, hmac_ctx, nbytes, buf);
117 
118 			data_nbytes += nbytes;
119 		}
120 
121 		va_end(args);
122 
123 		cchmac_final(info, hmac_ctx, drbg_ctx->key);
124 
125 		cchmac(info, outlen, drbg_ctx->key, outlen, drbg_ctx->V, drbg_ctx->V);
126 
127 		if (data_nbytes == 0) {
128 			break;
129 		}
130 	}
131 
132 	cchmac_di_clear(info, hmac_ctx);
133 }
134 
135 static bool
entropy_isvalid(size_t entropy_nbytes,const struct ccdigest_info * info)136 entropy_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
142 static int
init(const struct ccdrbg_info * info,struct ccdrbg_state * ctx,size_t entropy_nbytes,const void * entropy,size_t nonce_nbytes,const void * nonce,size_t ps_nbytes,const void * ps)143 init(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, 3, entropy_nbytes, entropy, nonce_nbytes, nonce, ps_nbytes, ps);
168 
169 	drbg_ctx->reseed_counter = 1;
170 
171 out:
172 	return status;
173 }
174 
175 static bool
add_isvalid(size_t add_nbytes)176 add_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
182 static int
reseed(struct ccdrbg_state * ctx,size_t entropy_nbytes,const void * entropy,size_t add_nbytes,const void * add)183 reseed(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, 2, entropy_nbytes, entropy, add_nbytes, add);
195 
196 	drbg_ctx->reseed_counter = 1;
197 
198 out:
199 	return status;
200 }
201 
202 static bool
must_reseed(const struct ccdrbg_state * ctx)203 must_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
212 static int
generate(struct ccdrbg_state * ctx,size_t out_nbytes,void * out,size_t add_nbytes,const void * add)213 generate(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, 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(info, outlen, drbg_ctx->key, outlen, drbg_ctx->V, drbg_ctx->V);
238 
239 		// See FIPS 140-2, 4.9.2 Conditional Tests
240 		if (cc_cmp_safe(outlen, Vprev, 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, 1, add_nbytes, add);
255 
256 	drbg_ctx->reseed_counter += 1;
257 
258 out:
259 	cc_clear(outlen, Vprev);
260 	return status;
261 }
262 
263 void
ccdrbg_factory_nisthmac(struct ccdrbg_info * info,const struct ccdrbg_nisthmac_custom * custom)264 ccdrbg_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