]>
Commit | Line | Data |
---|---|---|
e1178600 SL |
1 | /* |
2 | * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved. | |
3 | * | |
4 | * Licensed under the Apache License 2.0 (the "License"). You may not use | |
5 | * this file except in compliance with the License. You can obtain a copy | |
6 | * in the file LICENSE in the source distribution or at | |
7 | * https://www.openssl.org/source/license.html | |
8 | */ | |
9 | ||
10 | /* | |
11 | * Generic dispatch table functions for ciphers. | |
12 | */ | |
13 | ||
68a51d59 | 14 | #include "ciphercommon_local.h" |
ddd21319 RL |
15 | #include "prov/provider_ctx.h" |
16 | #include "prov/providercommonerr.h" | |
e1178600 SL |
17 | |
18 | /*- | |
4a42e264 | 19 | * Generic cipher functions for OSSL_PARAM gettables and settables |
e1178600 SL |
20 | */ |
21 | static const OSSL_PARAM cipher_known_gettable_params[] = { | |
1c3ace68 SL |
22 | OSSL_PARAM_uint(OSSL_CIPHER_PARAM_MODE, NULL), |
23 | OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL), | |
24 | OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_IVLEN, NULL), | |
25 | OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_BLOCK_SIZE, NULL), | |
26 | OSSL_PARAM_ulong(OSSL_CIPHER_PARAM_FLAGS, NULL), | |
e1178600 SL |
27 | OSSL_PARAM_END |
28 | }; | |
4a42e264 | 29 | const OSSL_PARAM *cipher_generic_gettable_params(void) |
e1178600 SL |
30 | { |
31 | return cipher_known_gettable_params; | |
32 | } | |
33 | ||
1c3ace68 SL |
34 | int cipher_generic_get_params(OSSL_PARAM params[], unsigned int md, |
35 | unsigned long flags, | |
36 | size_t kbits, size_t blkbits, size_t ivbits) | |
e1178600 SL |
37 | { |
38 | OSSL_PARAM *p; | |
39 | ||
40 | p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_MODE); | |
1c3ace68 | 41 | if (p != NULL && !OSSL_PARAM_set_uint(p, md)) { |
e1178600 SL |
42 | ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); |
43 | return 0; | |
44 | } | |
45 | p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_FLAGS); | |
46 | if (p != NULL && !OSSL_PARAM_set_ulong(p, flags)) { | |
47 | ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); | |
48 | return 0; | |
49 | } | |
50 | p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_KEYLEN); | |
1c3ace68 | 51 | if (p != NULL && !OSSL_PARAM_set_size_t(p, kbits / 8)) { |
e1178600 SL |
52 | ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); |
53 | return 0; | |
54 | } | |
55 | p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_BLOCK_SIZE); | |
1c3ace68 | 56 | if (p != NULL && !OSSL_PARAM_set_size_t(p, blkbits / 8)) { |
e1178600 SL |
57 | ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); |
58 | return 0; | |
59 | } | |
60 | p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_IVLEN); | |
1c3ace68 | 61 | if (p != NULL && !OSSL_PARAM_set_size_t(p, ivbits / 8)) { |
e1178600 SL |
62 | ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); |
63 | return 0; | |
64 | } | |
65 | return 1; | |
66 | } | |
67 | ||
4a42e264 SL |
68 | CIPHER_DEFAULT_GETTABLE_CTX_PARAMS_START(cipher_generic) |
69 | CIPHER_DEFAULT_GETTABLE_CTX_PARAMS_END(cipher_generic) | |
e1178600 | 70 | |
6a41156c SL |
71 | CIPHER_DEFAULT_SETTABLE_CTX_PARAMS_START(cipher_generic) |
72 | CIPHER_DEFAULT_SETTABLE_CTX_PARAMS_END(cipher_generic) | |
e1178600 | 73 | |
d23adad1 MC |
74 | /* |
75 | * Variable key length cipher functions for OSSL_PARAM settables | |
76 | */ | |
77 | ||
78 | int cipher_var_keylen_set_ctx_params(void *vctx, const OSSL_PARAM params[]) | |
79 | { | |
80 | PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx; | |
81 | const OSSL_PARAM *p; | |
82 | ||
83 | if (!cipher_generic_set_ctx_params(vctx, params)) | |
84 | return 0; | |
85 | p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_KEYLEN); | |
86 | if (p != NULL) { | |
87 | size_t keylen; | |
88 | ||
89 | if (!OSSL_PARAM_get_size_t(p, &keylen)) { | |
90 | ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); | |
91 | return 0; | |
92 | } | |
93 | ctx->keylen = keylen; | |
94 | } | |
95 | return 1; | |
96 | } | |
97 | ||
98 | CIPHER_DEFAULT_SETTABLE_CTX_PARAMS_START(cipher_var_keylen) | |
99 | OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL), | |
100 | CIPHER_DEFAULT_SETTABLE_CTX_PARAMS_END(cipher_var_keylen) | |
101 | ||
e1178600 SL |
102 | /*- |
103 | * AEAD cipher functions for OSSL_PARAM gettables and settables | |
104 | */ | |
105 | static const OSSL_PARAM cipher_aead_known_gettable_ctx_params[] = { | |
1c3ace68 SL |
106 | OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL), |
107 | OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_IVLEN, NULL), | |
dc64dc2e | 108 | OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_AEAD_TAGLEN, NULL), |
e1178600 SL |
109 | OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_IV, NULL, 0), |
110 | OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, NULL, 0), | |
111 | OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_AEAD_TLS1_AAD_PAD, NULL), | |
11b44359 | 112 | OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_TLS1_GET_IV_GEN, NULL, 0), |
e1178600 SL |
113 | OSSL_PARAM_END |
114 | }; | |
115 | const OSSL_PARAM *cipher_aead_gettable_ctx_params(void) | |
116 | { | |
117 | return cipher_aead_known_gettable_ctx_params; | |
118 | } | |
119 | ||
120 | static const OSSL_PARAM cipher_aead_known_settable_ctx_params[] = { | |
e1178600 SL |
121 | OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_AEAD_IVLEN, NULL), |
122 | OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, NULL, 0), | |
123 | OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_TLS1_AAD, NULL, 0), | |
124 | OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_TLS1_IV_FIXED, NULL, 0), | |
11b44359 | 125 | OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_TLS1_SET_IV_INV, NULL, 0), |
e1178600 SL |
126 | OSSL_PARAM_END |
127 | }; | |
128 | const OSSL_PARAM *cipher_aead_settable_ctx_params(void) | |
129 | { | |
130 | return cipher_aead_known_settable_ctx_params; | |
131 | } | |
132 | ||
133 | static int cipher_generic_init_internal(PROV_CIPHER_CTX *ctx, | |
134 | const unsigned char *key, size_t keylen, | |
135 | const unsigned char *iv, size_t ivlen, | |
136 | int enc) | |
137 | { | |
1c3ace68 | 138 | ctx->enc = enc ? 1 : 0; |
e1178600 SL |
139 | |
140 | if (iv != NULL && ctx->mode != EVP_CIPH_ECB_MODE) { | |
089cb623 | 141 | if (!cipher_generic_initiv(ctx, iv, ivlen)) |
e1178600 | 142 | return 0; |
e1178600 SL |
143 | } |
144 | if (key != NULL) { | |
55c7dc79 SL |
145 | if ((ctx->flags & EVP_CIPH_VARIABLE_LENGTH) == 0) { |
146 | if (keylen != ctx->keylen) { | |
147 | ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEYLEN); | |
148 | return 0; | |
149 | } | |
150 | } else { | |
151 | ctx->keylen = keylen; | |
e1178600 SL |
152 | } |
153 | return ctx->hw->init(ctx, key, ctx->keylen); | |
154 | } | |
155 | return 1; | |
156 | } | |
157 | ||
158 | int cipher_generic_einit(void *vctx, const unsigned char *key, size_t keylen, | |
159 | const unsigned char *iv, size_t ivlen) | |
160 | { | |
161 | return cipher_generic_init_internal((PROV_CIPHER_CTX *)vctx, key, keylen, | |
162 | iv, ivlen, 1); | |
163 | } | |
164 | ||
165 | int cipher_generic_dinit(void *vctx, const unsigned char *key, size_t keylen, | |
166 | const unsigned char *iv, size_t ivlen) | |
167 | { | |
168 | return cipher_generic_init_internal((PROV_CIPHER_CTX *)vctx, key, keylen, | |
169 | iv, ivlen, 0); | |
170 | } | |
171 | ||
172 | int cipher_generic_block_update(void *vctx, unsigned char *out, size_t *outl, | |
173 | size_t outsize, const unsigned char *in, | |
174 | size_t inl) | |
175 | { | |
176 | size_t outlint = 0; | |
177 | PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx; | |
4a42e264 SL |
178 | size_t blksz = ctx->blocksize; |
179 | size_t nextblocks = fillblock(ctx->buf, &ctx->bufsz, blksz, &in, &inl); | |
e1178600 SL |
180 | |
181 | /* | |
182 | * If we're decrypting and we end an update on a block boundary we hold | |
183 | * the last block back in case this is the last update call and the last | |
184 | * block is padded. | |
185 | */ | |
4a42e264 SL |
186 | if (ctx->bufsz == blksz && (ctx->enc || inl > 0 || !ctx->pad)) { |
187 | if (outsize < blksz) { | |
e1178600 SL |
188 | ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL); |
189 | return 0; | |
190 | } | |
4a42e264 | 191 | if (!ctx->hw->cipher(ctx, out, ctx->buf, blksz)) { |
e1178600 SL |
192 | ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED); |
193 | return 0; | |
194 | } | |
195 | ctx->bufsz = 0; | |
4a42e264 SL |
196 | outlint = blksz; |
197 | out += blksz; | |
e1178600 SL |
198 | } |
199 | if (nextblocks > 0) { | |
200 | if (!ctx->enc && ctx->pad && nextblocks == inl) { | |
4a42e264 | 201 | if (!ossl_assert(inl >= blksz)) { |
e1178600 SL |
202 | ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL); |
203 | return 0; | |
204 | } | |
4a42e264 | 205 | nextblocks -= blksz; |
e1178600 SL |
206 | } |
207 | outlint += nextblocks; | |
208 | if (outsize < outlint) { | |
209 | ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL); | |
210 | return 0; | |
211 | } | |
51356a06 PS |
212 | } |
213 | if (nextblocks > 0) { | |
e1178600 SL |
214 | if (!ctx->hw->cipher(ctx, out, in, nextblocks)) { |
215 | ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED); | |
216 | return 0; | |
217 | } | |
218 | in += nextblocks; | |
219 | inl -= nextblocks; | |
220 | } | |
4a42e264 | 221 | if (!trailingdata(ctx->buf, &ctx->bufsz, blksz, &in, &inl)) { |
e1178600 SL |
222 | /* ERR_raise already called */ |
223 | return 0; | |
224 | } | |
225 | ||
226 | *outl = outlint; | |
227 | return inl == 0; | |
228 | } | |
229 | ||
230 | int cipher_generic_block_final(void *vctx, unsigned char *out, size_t *outl, | |
231 | size_t outsize) | |
232 | { | |
233 | PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx; | |
4a42e264 | 234 | size_t blksz = ctx->blocksize; |
e1178600 SL |
235 | |
236 | if (ctx->enc) { | |
237 | if (ctx->pad) { | |
4a42e264 | 238 | padblock(ctx->buf, &ctx->bufsz, blksz); |
e1178600 SL |
239 | } else if (ctx->bufsz == 0) { |
240 | *outl = 0; | |
241 | return 1; | |
4a42e264 | 242 | } else if (ctx->bufsz != blksz) { |
e1178600 SL |
243 | ERR_raise(ERR_LIB_PROV, PROV_R_WRONG_FINAL_BLOCK_LENGTH); |
244 | return 0; | |
245 | } | |
246 | ||
4a42e264 | 247 | if (outsize < blksz) { |
e1178600 SL |
248 | ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL); |
249 | return 0; | |
250 | } | |
4a42e264 | 251 | if (!ctx->hw->cipher(ctx, out, ctx->buf, blksz)) { |
e1178600 SL |
252 | ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED); |
253 | return 0; | |
254 | } | |
255 | ctx->bufsz = 0; | |
4a42e264 | 256 | *outl = blksz; |
e1178600 SL |
257 | return 1; |
258 | } | |
259 | ||
260 | /* Decrypting */ | |
4a42e264 | 261 | if (ctx->bufsz != blksz) { |
e1178600 SL |
262 | if (ctx->bufsz == 0 && !ctx->pad) { |
263 | *outl = 0; | |
264 | return 1; | |
265 | } | |
266 | ERR_raise(ERR_LIB_PROV, PROV_R_WRONG_FINAL_BLOCK_LENGTH); | |
267 | return 0; | |
268 | } | |
269 | ||
4a42e264 | 270 | if (!ctx->hw->cipher(ctx, ctx->buf, ctx->buf, blksz)) { |
e1178600 SL |
271 | ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED); |
272 | return 0; | |
273 | } | |
274 | ||
4a42e264 | 275 | if (ctx->pad && !unpadblock(ctx->buf, &ctx->bufsz, blksz)) { |
e1178600 SL |
276 | /* ERR_raise already called */ |
277 | return 0; | |
278 | } | |
279 | ||
280 | if (outsize < ctx->bufsz) { | |
281 | ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL); | |
282 | return 0; | |
283 | } | |
284 | memcpy(out, ctx->buf, ctx->bufsz); | |
285 | *outl = ctx->bufsz; | |
286 | ctx->bufsz = 0; | |
287 | return 1; | |
288 | } | |
289 | ||
290 | int cipher_generic_stream_update(void *vctx, unsigned char *out, size_t *outl, | |
291 | size_t outsize, const unsigned char *in, | |
292 | size_t inl) | |
293 | { | |
294 | PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx; | |
295 | ||
4b9c750b MC |
296 | if (inl == 0) { |
297 | *outl = 0; | |
298 | return 1; | |
299 | } | |
300 | ||
e1178600 SL |
301 | if (outsize < inl) { |
302 | ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL); | |
303 | return 0; | |
304 | } | |
305 | ||
306 | if (!ctx->hw->cipher(ctx, out, in, inl)) { | |
307 | ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED); | |
308 | return 0; | |
309 | } | |
310 | ||
311 | *outl = inl; | |
312 | return 1; | |
313 | } | |
314 | int cipher_generic_stream_final(void *vctx, unsigned char *out, size_t *outl, | |
315 | size_t outsize) | |
316 | { | |
317 | *outl = 0; | |
318 | return 1; | |
319 | } | |
320 | ||
321 | int cipher_generic_cipher(void *vctx, | |
322 | unsigned char *out, size_t *outl, size_t outsize, | |
323 | const unsigned char *in, size_t inl) | |
324 | { | |
325 | PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx; | |
326 | ||
327 | if (outsize < inl) { | |
328 | ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL); | |
329 | return 0; | |
330 | } | |
331 | ||
332 | if (!ctx->hw->cipher(ctx, out, in, inl)) { | |
333 | ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED); | |
334 | return 0; | |
335 | } | |
336 | ||
337 | *outl = inl; | |
338 | return 1; | |
339 | } | |
340 | ||
341 | int cipher_generic_get_ctx_params(void *vctx, OSSL_PARAM params[]) | |
342 | { | |
343 | PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx; | |
344 | OSSL_PARAM *p; | |
345 | ||
346 | p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_IVLEN); | |
1c3ace68 | 347 | if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->ivlen)) { |
e1178600 SL |
348 | ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); |
349 | return 0; | |
350 | } | |
351 | p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_PADDING); | |
1c3ace68 | 352 | if (p != NULL && !OSSL_PARAM_set_uint(p, ctx->pad)) { |
e1178600 SL |
353 | ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); |
354 | return 0; | |
355 | } | |
356 | p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_IV); | |
357 | if (p != NULL | |
089cb623 SL |
358 | && !OSSL_PARAM_set_octet_ptr(p, &ctx->oiv, ctx->ivlen) |
359 | && !OSSL_PARAM_set_octet_string(p, &ctx->oiv, ctx->ivlen)) { | |
e1178600 SL |
360 | ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); |
361 | return 0; | |
362 | } | |
363 | p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_NUM); | |
1c3ace68 | 364 | if (p != NULL && !OSSL_PARAM_set_uint(p, ctx->num)) { |
e1178600 SL |
365 | ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); |
366 | return 0; | |
367 | } | |
368 | p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_KEYLEN); | |
1c3ace68 | 369 | if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->keylen)) { |
e1178600 SL |
370 | ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); |
371 | return 0; | |
372 | } | |
e1178600 SL |
373 | return 1; |
374 | } | |
375 | ||
376 | int cipher_generic_set_ctx_params(void *vctx, const OSSL_PARAM params[]) | |
377 | { | |
378 | PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx; | |
379 | const OSSL_PARAM *p; | |
380 | ||
381 | p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_PADDING); | |
382 | if (p != NULL) { | |
1c3ace68 | 383 | unsigned int pad; |
e1178600 | 384 | |
1c3ace68 | 385 | if (!OSSL_PARAM_get_uint(p, &pad)) { |
e1178600 SL |
386 | ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); |
387 | return 0; | |
388 | } | |
389 | ctx->pad = pad ? 1 : 0; | |
390 | } | |
391 | p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_NUM); | |
392 | if (p != NULL) { | |
1c3ace68 | 393 | unsigned int num; |
e1178600 | 394 | |
1c3ace68 | 395 | if (!OSSL_PARAM_get_uint(p, &num)) { |
e1178600 SL |
396 | ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); |
397 | return 0; | |
398 | } | |
399 | ctx->num = num; | |
400 | } | |
e1178600 SL |
401 | return 1; |
402 | } | |
403 | ||
089cb623 SL |
404 | int cipher_generic_initiv(PROV_CIPHER_CTX *ctx, const unsigned char *iv, |
405 | size_t ivlen) | |
406 | { | |
407 | if (ivlen != ctx->ivlen | |
408 | || ivlen > sizeof(ctx->iv)) { | |
409 | ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IVLEN); | |
410 | return 0; | |
411 | } | |
412 | ctx->iv_set = 1; | |
413 | memcpy(ctx->iv, iv, ivlen); | |
414 | memcpy(ctx->oiv, iv, ivlen); | |
415 | return 1; | |
416 | } | |
417 | ||
4a42e264 | 418 | void cipher_generic_initkey(void *vctx, size_t kbits, size_t blkbits, |
55c7dc79 | 419 | size_t ivbits, unsigned int mode, uint64_t flags, |
4a42e264 | 420 | const PROV_CIPHER_HW *hw, void *provctx) |
e1178600 SL |
421 | { |
422 | PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx; | |
423 | ||
55c7dc79 | 424 | ctx->flags = flags; |
e1178600 SL |
425 | ctx->pad = 1; |
426 | ctx->keylen = ((kbits) / 8); | |
4a42e264 | 427 | ctx->ivlen = ((ivbits) / 8); |
e1178600 SL |
428 | ctx->hw = hw; |
429 | ctx->mode = mode; | |
4a42e264 SL |
430 | ctx->blocksize = blkbits / 8; |
431 | if (provctx != NULL) | |
432 | ctx->libctx = PROV_LIBRARY_CONTEXT_OF(provctx); /* used for rand */ | |
e1178600 | 433 | } |