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