diff --git a/crypto/drbg.c b/crypto/drbg.c index 3f0b7e0f8bac..d6621a6181d7 100644 --- a/crypto/drbg.c +++ b/crypto/drbg.c @@ -370,13 +370,12 @@ static int drbg_fini_sym_kernel(struct drbg_state *drbg); /* BCC function for CTR DRBG as defined in 10.4.3 */ static int drbg_ctr_bcc(struct drbg_state *drbg, unsigned char *out, const unsigned char *key, - struct drbg_string *in) + struct list_head *in) { - int ret = -EFAULT; - struct drbg_string *curr = in; - size_t inpos = curr->len; - const unsigned char *pos = curr->buf; + int ret = 0; + struct drbg_string *curr = NULL; struct drbg_string data; + short cnt = 0; drbg_string_fill(&data, out, drbg_blocklen(drbg)); @@ -384,39 +383,29 @@ static int drbg_ctr_bcc(struct drbg_state *drbg, memset(out, 0, drbg_blocklen(drbg)); /* 10.4.3 step 2 / 4 */ - while (inpos) { - short cnt = 0; + list_for_each_entry(curr, in, list) { + const unsigned char *pos = curr->buf; + size_t len = curr->len; /* 10.4.3 step 4.1 */ - for (cnt = 0; cnt < drbg_blocklen(drbg); cnt++) { - out[cnt] ^= *pos; - pos++; inpos--; - /* - * The following branch implements the linked list - * iteration of drbg_string *in. If we are at the - * end of the current list member, we have to start - * using the next member if available. The inpos - * value always points to the current byte and will - * be zero if we have processed the last byte of - * the last linked list member. - */ - if (0 == inpos) { - curr = curr->next; - if (NULL != curr) { - pos = curr->buf; - inpos = curr->len; - } else { - inpos = 0; - break; - } + while (len) { + /* 10.4.3 step 4.2 */ + if (drbg_blocklen(drbg) == cnt) { + cnt = 0; + ret = drbg_kcapi_sym(drbg, key, out, &data); + if (ret) + return ret; } + out[cnt] ^= *pos; + pos++; + cnt++; + len--; } - /* 10.4.3 step 4.2 */ - ret = drbg_kcapi_sym(drbg, key, out, &data); - if (ret) - return ret; - /* 10.4.3 step 2 */ } - return 0; + /* 10.4.3 step 4.2 for last block */ + if (cnt) + ret = drbg_kcapi_sym(drbg, key, out, &data); + + return ret; } /* @@ -461,13 +450,13 @@ static int drbg_ctr_bcc(struct drbg_state *drbg, /* Derivation Function for CTR DRBG as defined in 10.4.2 */ static int drbg_ctr_df(struct drbg_state *drbg, unsigned char *df_data, size_t bytes_to_return, - struct drbg_string *addtl) + struct list_head *seedlist) { int ret = -EFAULT; unsigned char L_N[8]; /* S3 is input */ struct drbg_string S1, S2, S4, cipherin; - struct drbg_string *tempstr = addtl; + LIST_HEAD(bcc_list); unsigned char *pad = df_data + drbg_statelen(drbg); unsigned char *iv = pad + drbg_blocklen(drbg); unsigned char *temp = iv + drbg_blocklen(drbg); @@ -484,6 +473,7 @@ static int drbg_ctr_df(struct drbg_state *drbg, unsigned char *X; size_t generated_len = 0; size_t inputlen = 0; + struct drbg_string *seed = NULL; memset(pad, 0, drbg_blocklen(drbg)); memset(iv, 0, drbg_blocklen(drbg)); @@ -496,8 +486,8 @@ static int drbg_ctr_df(struct drbg_state *drbg, return -EINVAL; /* 10.4.2 step 2 -- calculate the entire length of all input data */ - for (; NULL != tempstr; tempstr = tempstr->next) - inputlen += tempstr->len; + list_for_each_entry(seed, seedlist, list) + inputlen += seed->len; drbg_int2byte(&L_N[0], inputlen, 4); /* 10.4.2 step 3 */ @@ -518,20 +508,12 @@ static int drbg_ctr_df(struct drbg_state *drbg, /* 10.4.2 step 4 -- first fill the linked list and then order it */ drbg_string_fill(&S1, iv, drbg_blocklen(drbg)); + list_add_tail(&S1.list, &bcc_list); drbg_string_fill(&S2, L_N, sizeof(L_N)); + list_add_tail(&S2.list, &bcc_list); + list_splice_tail(seedlist, &bcc_list); drbg_string_fill(&S4, pad, padlen); - S1.next = &S2; - S2.next = addtl; - - /* - * Splice in addtl between S2 and S4 -- we place S4 at the end - * of the input data chain. As this code is only triggered when - * addtl is not NULL, no NULL checks are necessary. - */ - tempstr = addtl; - while (tempstr->next) - tempstr = tempstr->next; - tempstr->next = &S4; + list_add_tail(&S4.list, &bcc_list); /* 10.4.2 step 9 */ while (templen < (drbg_keylen(drbg) + (drbg_blocklen(drbg)))) { @@ -542,7 +524,7 @@ static int drbg_ctr_df(struct drbg_state *drbg, */ drbg_int2byte(iv, i, 4); /* 10.4.2 step 9.2 -- BCC and concatenation with temp */ - ret = drbg_ctr_bcc(drbg, temp + templen, K, &S1); + ret = drbg_ctr_bcc(drbg, temp + templen, K, &bcc_list); if (ret) goto out; /* 10.4.2 step 9.3 */ @@ -586,8 +568,8 @@ out: } /* update function of CTR DRBG as defined in 10.2.1.2 */ -static int drbg_ctr_update(struct drbg_state *drbg, - struct drbg_string *addtl, int reseed) +static int drbg_ctr_update(struct drbg_state *drbg, struct list_head *seed, + int reseed) { int ret = -EFAULT; /* 10.2.1.2 step 1 */ @@ -603,9 +585,8 @@ static int drbg_ctr_update(struct drbg_state *drbg, memset(df_data, 0, drbg_statelen(drbg)); /* 10.2.1.3.2 step 2 and 10.2.1.4.2 step 2 */ - if (addtl && 0 < addtl->len) { - ret = drbg_ctr_df(drbg, df_data, drbg_statelen(drbg), - addtl); + if (seed) { + ret = drbg_ctr_df(drbg, df_data, drbg_statelen(drbg), seed); if (ret) goto out; } @@ -665,8 +646,10 @@ static int drbg_ctr_generate(struct drbg_state *drbg, /* 10.2.1.5.2 step 2 */ if (addtl && 0 < addtl->len) { - addtl->next = NULL; - ret = drbg_ctr_update(drbg, addtl, 1); + LIST_HEAD(addtllist); + + list_add_tail(&addtl->list, &addtllist); + ret = drbg_ctr_update(drbg, &addtllist, 1); if (ret) return 0; } @@ -697,16 +680,21 @@ static int drbg_ctr_generate(struct drbg_state *drbg, drbg_add_buf(drbg->V, drbg_blocklen(drbg), &prefix, 1); } - /* 10.2.1.5.2 step 6 */ - if (addtl) - addtl->next = NULL; /* + * 10.2.1.5.2 step 6 * The following call invokes the DF function again which could be * optimized. In step 2, the "additional_input" after step 2 is the * output of the DF function. If this result would be saved, the DF * function would not need to be invoked again at this point. */ - ret = drbg_ctr_update(drbg, addtl, 1); + if (addtl && 0 < addtl->len) { + LIST_HEAD(addtllist); + + list_add_tail(&addtl->list, &addtllist); + ret = drbg_ctr_update(drbg, &addtllist, 1); + } else { + ret = drbg_ctr_update(drbg, NULL, 1); + } if (ret) len = ret; @@ -729,19 +717,21 @@ static struct drbg_state_ops drbg_ctr_ops = { #if defined(CONFIG_CRYPTO_DRBG_HASH) || defined(CONFIG_CRYPTO_DRBG_HMAC) static int drbg_kcapi_hash(struct drbg_state *drbg, const unsigned char *key, - unsigned char *outval, const struct drbg_string *in); + unsigned char *outval, const struct list_head *in); static int drbg_init_hash_kernel(struct drbg_state *drbg); static int drbg_fini_hash_kernel(struct drbg_state *drbg); #endif /* (CONFIG_CRYPTO_DRBG_HASH || CONFIG_CRYPTO_DRBG_HMAC) */ #ifdef CONFIG_CRYPTO_DRBG_HMAC /* update function of HMAC DRBG as defined in 10.1.2.2 */ -static int drbg_hmac_update(struct drbg_state *drbg, - struct drbg_string *seed, int reseed) +static int drbg_hmac_update(struct drbg_state *drbg, struct list_head *seed, + int reseed) { int ret = -EFAULT; int i = 0; - struct drbg_string seed1, seed2, cipherin; + struct drbg_string seed1, seed2, vdata; + LIST_HEAD(seedlist); + LIST_HEAD(vdatalist); if (!reseed) { /* 10.1.2.3 step 2 */ @@ -750,13 +740,16 @@ static int drbg_hmac_update(struct drbg_state *drbg, } drbg_string_fill(&seed1, drbg->V, drbg_statelen(drbg)); + list_add_tail(&seed1.list, &seedlist); /* buffer of seed2 will be filled in for loop below with one byte */ drbg_string_fill(&seed2, NULL, 1); - seed1.next = &seed2; + list_add_tail(&seed2.list, &seedlist); /* input data of seed is allowed to be NULL at this point */ - seed2.next = seed; + if (seed) + list_splice_tail(seed, &seedlist); - drbg_string_fill(&cipherin, drbg->V, drbg_statelen(drbg)); + drbg_string_fill(&vdata, drbg->V, drbg_statelen(drbg)); + list_add_tail(&vdata.list, &vdatalist); for (i = 2; 0 < i; i--) { /* first round uses 0x0, second 0x1 */ unsigned char prefix = DRBG_PREFIX0; @@ -764,17 +757,17 @@ static int drbg_hmac_update(struct drbg_state *drbg, prefix = DRBG_PREFIX1; /* 10.1.2.2 step 1 and 4 -- concatenation and HMAC for key */ seed2.buf = &prefix; - ret = drbg_kcapi_hash(drbg, drbg->C, drbg->C, &seed1); + ret = drbg_kcapi_hash(drbg, drbg->C, drbg->C, &seedlist); if (ret) return ret; /* 10.1.2.2 step 2 and 5 -- HMAC for V */ - ret = drbg_kcapi_hash(drbg, drbg->C, drbg->V, &cipherin); + ret = drbg_kcapi_hash(drbg, drbg->C, drbg->V, &vdatalist); if (ret) return ret; /* 10.1.2.2 step 3 */ - if (!seed || 0 == seed->len) + if (!seed) return ret; } @@ -790,20 +783,24 @@ static int drbg_hmac_generate(struct drbg_state *drbg, int len = 0; int ret = 0; struct drbg_string data; + LIST_HEAD(datalist); /* 10.1.2.5 step 2 */ if (addtl && 0 < addtl->len) { - addtl->next = NULL; - ret = drbg_hmac_update(drbg, addtl, 1); + LIST_HEAD(addtllist); + + list_add_tail(&addtl->list, &addtllist); + ret = drbg_hmac_update(drbg, &addtllist, 1); if (ret) return ret; } drbg_string_fill(&data, drbg->V, drbg_statelen(drbg)); + list_add_tail(&data.list, &datalist); while (len < buflen) { unsigned int outlen = 0; /* 10.1.2.5 step 4.1 */ - ret = drbg_kcapi_hash(drbg, drbg->C, drbg->V, &data); + ret = drbg_kcapi_hash(drbg, drbg->C, drbg->V, &datalist); if (ret) return ret; outlen = (drbg_blocklen(drbg) < (buflen - len)) ? @@ -817,9 +814,14 @@ static int drbg_hmac_generate(struct drbg_state *drbg, } /* 10.1.2.5 step 6 */ - if (addtl) - addtl->next = NULL; - ret = drbg_hmac_update(drbg, addtl, 1); + if (addtl && 0 < addtl->len) { + LIST_HEAD(addtllist); + + list_add_tail(&addtl->list, &addtllist); + ret = drbg_hmac_update(drbg, &addtllist, 1); + } else { + ret = drbg_hmac_update(drbg, NULL, 1); + } if (ret) return ret; @@ -858,13 +860,13 @@ static struct drbg_state_ops drbg_hmac_ops = { /* Derivation Function for Hash DRBG as defined in 10.4.1 */ static int drbg_hash_df(struct drbg_state *drbg, unsigned char *outval, size_t outlen, - struct drbg_string *entropy) + struct list_head *entropylist) { int ret = 0; size_t len = 0; unsigned char input[5]; unsigned char *tmp = drbg->scratchpad + drbg_statelen(drbg); - struct drbg_string data1; + struct drbg_string data; memset(tmp, 0, drbg_blocklen(drbg)); @@ -873,14 +875,14 @@ static int drbg_hash_df(struct drbg_state *drbg, drbg_int2byte(&input[1], (outlen * 8), 4); /* 10.4.1 step 4.1 -- concatenation of data for input into hash */ - drbg_string_fill(&data1, input, 5); - data1.next = entropy; + drbg_string_fill(&data, input, 5); + list_add(&data.list, entropylist); /* 10.4.1 step 4 */ while (len < outlen) { short blocklen = 0; /* 10.4.1 step 4.1 */ - ret = drbg_kcapi_hash(drbg, NULL, tmp, &data1); + ret = drbg_kcapi_hash(drbg, NULL, tmp, entropylist); if (ret) goto out; /* 10.4.1 step 4.2 */ @@ -897,11 +899,13 @@ out: } /* update function for Hash DRBG as defined in 10.1.1.2 / 10.1.1.3 */ -static int drbg_hash_update(struct drbg_state *drbg, struct drbg_string *seed, +static int drbg_hash_update(struct drbg_state *drbg, struct list_head *seed, int reseed) { int ret = 0; struct drbg_string data1, data2; + LIST_HEAD(datalist); + LIST_HEAD(datalist2); unsigned char *V = drbg->scratchpad; unsigned char prefix = DRBG_PREFIX1; @@ -913,26 +917,25 @@ static int drbg_hash_update(struct drbg_state *drbg, struct drbg_string *seed, /* 10.1.1.3 step 1 */ memcpy(V, drbg->V, drbg_statelen(drbg)); drbg_string_fill(&data1, &prefix, 1); + list_add_tail(&data1.list, &datalist); drbg_string_fill(&data2, V, drbg_statelen(drbg)); - data1.next = &data2; - data2.next = seed; - } else { - drbg_string_fill(&data1, seed->buf, seed->len); - data1.next = seed->next; + list_add_tail(&data2.list, &datalist); } + list_splice_tail(seed, &datalist); /* 10.1.1.2 / 10.1.1.3 step 2 and 3 */ - ret = drbg_hash_df(drbg, drbg->V, drbg_statelen(drbg), &data1); + ret = drbg_hash_df(drbg, drbg->V, drbg_statelen(drbg), &datalist); if (ret) goto out; /* 10.1.1.2 / 10.1.1.3 step 4 */ prefix = DRBG_PREFIX0; drbg_string_fill(&data1, &prefix, 1); + list_add_tail(&data1.list, &datalist2); drbg_string_fill(&data2, drbg->V, drbg_statelen(drbg)); - data1.next = &data2; + list_add_tail(&data2.list, &datalist2); /* 10.1.1.2 / 10.1.1.3 step 4 */ - ret = drbg_hash_df(drbg, drbg->C, drbg_statelen(drbg), &data1); + ret = drbg_hash_df(drbg, drbg->C, drbg_statelen(drbg), &datalist2); out: memset(drbg->scratchpad, 0, drbg_statelen(drbg)); @@ -945,7 +948,7 @@ static int drbg_hash_process_addtl(struct drbg_state *drbg, { int ret = 0; struct drbg_string data1, data2; - struct drbg_string *data3; + LIST_HEAD(datalist); unsigned char prefix = DRBG_PREFIX2; /* this is value w as per documentation */ @@ -958,11 +961,10 @@ static int drbg_hash_process_addtl(struct drbg_state *drbg, /* 10.1.1.4 step 2a */ drbg_string_fill(&data1, &prefix, 1); drbg_string_fill(&data2, drbg->V, drbg_statelen(drbg)); - data3 = addtl; - data1.next = &data2; - data2.next = data3; - data3->next = NULL; - ret = drbg_kcapi_hash(drbg, NULL, drbg->scratchpad, &data1); + list_add_tail(&data1.list, &datalist); + list_add_tail(&data2.list, &datalist); + list_add_tail(&addtl->list, &datalist); + ret = drbg_kcapi_hash(drbg, NULL, drbg->scratchpad, &datalist); if (ret) goto out; @@ -985,6 +987,7 @@ static int drbg_hash_hashgen(struct drbg_state *drbg, unsigned char *src = drbg->scratchpad; unsigned char *dst = drbg->scratchpad + drbg_statelen(drbg); struct drbg_string data; + LIST_HEAD(datalist); unsigned char prefix = DRBG_PREFIX1; memset(src, 0, drbg_statelen(drbg)); @@ -994,10 +997,11 @@ static int drbg_hash_hashgen(struct drbg_state *drbg, memcpy(src, drbg->V, drbg_statelen(drbg)); drbg_string_fill(&data, src, drbg_statelen(drbg)); + list_add_tail(&data.list, &datalist); while (len < buflen) { unsigned int outlen = 0; /* 10.1.1.4 step hashgen 4.1 */ - ret = drbg_kcapi_hash(drbg, NULL, dst, &data); + ret = drbg_kcapi_hash(drbg, NULL, dst, &datalist); if (ret) { len = ret; goto out; @@ -1032,6 +1036,7 @@ static int drbg_hash_generate(struct drbg_state *drbg, unsigned char req[8]; unsigned char prefix = DRBG_PREFIX3; struct drbg_string data1, data2; + LIST_HEAD(datalist); /* 10.1.1.4 step 2 */ ret = drbg_hash_process_addtl(drbg, addtl); @@ -1044,9 +1049,10 @@ static int drbg_hash_generate(struct drbg_state *drbg, memset(drbg->scratchpad, 0, drbg_blocklen(drbg)); /* 10.1.1.4 step 4 */ drbg_string_fill(&data1, &prefix, 1); + list_add_tail(&data1.list, &datalist); drbg_string_fill(&data2, drbg->V, drbg_statelen(drbg)); - data1.next = &data2; - ret = drbg_kcapi_hash(drbg, NULL, drbg->scratchpad, &data1); + list_add_tail(&data2.list, &datalist); + ret = drbg_kcapi_hash(drbg, NULL, drbg->scratchpad, &datalist); if (ret) { len = ret; goto out; @@ -1099,6 +1105,7 @@ static int drbg_seed(struct drbg_state *drbg, struct drbg_string *pers, unsigned char *entropy = NULL; size_t entropylen = 0; struct drbg_string data1; + LIST_HEAD(seedlist); /* 9.1 / 9.2 / 9.3.1 step 3 */ if (pers && pers->len > (drbg_max_addtl(drbg))) { @@ -1133,18 +1140,19 @@ static int drbg_seed(struct drbg_state *drbg, struct drbg_string *pers, get_random_bytes(entropy, entropylen); drbg_string_fill(&data1, entropy, entropylen); } + list_add_tail(&data1.list, &seedlist); /* * concatenation of entropy with personalization str / addtl input) * the variable pers is directly handed in by the caller, so check its * contents whether it is appropriate */ - if (pers && pers->buf && 0 < pers->len && NULL == pers->next) { - data1.next = pers; + if (pers && pers->buf && 0 < pers->len) { + list_add_tail(&pers->list, &seedlist); pr_devel("DRBG: using personalization string\n"); } - ret = drbg->d_ops->update(drbg, &data1, reseed); + ret = drbg->d_ops->update(drbg, &seedlist, reseed); if (ret) goto out; @@ -1642,15 +1650,16 @@ static int drbg_fini_hash_kernel(struct drbg_state *drbg) } static int drbg_kcapi_hash(struct drbg_state *drbg, const unsigned char *key, - unsigned char *outval, const struct drbg_string *in) + unsigned char *outval, const struct list_head *in) { struct sdesc *sdesc = (struct sdesc *)drbg->priv_data; + struct drbg_string *input = NULL; if (key) crypto_shash_setkey(sdesc->shash.tfm, key, drbg_statelen(drbg)); crypto_shash_init(&sdesc->shash); - for (; NULL != in; in = in->next) - crypto_shash_update(&sdesc->shash, in->buf, in->len); + list_for_each_entry(input, in, list) + crypto_shash_update(&sdesc->shash, input->buf, input->len); return crypto_shash_final(&sdesc->shash, outval); } #endif /* (CONFIG_CRYPTO_DRBG_HASH || CONFIG_CRYPTO_DRBG_HMAC) */ @@ -1785,12 +1794,15 @@ static int drbg_kcapi_random(struct crypto_rng *tfm, u8 *rdata, return drbg_generate_long(drbg, rdata, dlen, NULL); } else { struct drbg_gen *data = (struct drbg_gen *)rdata; + struct drbg_string addtl; /* catch NULL pointer */ if (!data) return 0; drbg_set_testdata(drbg, data->test_data); + /* linked list variable is now local to allow modification */ + drbg_string_fill(&addtl, data->addtl->buf, data->addtl->len); return drbg_generate_long(drbg, data->outbuf, data->outlen, - data->addtl); + &addtl); } } @@ -1820,7 +1832,10 @@ static int drbg_kcapi_reset(struct crypto_rng *tfm, u8 *seed, unsigned int slen) if (!data) return drbg_instantiate(drbg, NULL, coreref, pr); drbg_set_testdata(drbg, data->test_data); - return drbg_instantiate(drbg, data->addtl, coreref, pr); + /* linked list variable is now local to allow modification */ + drbg_string_fill(&seed_string, data->addtl->buf, + data->addtl->len); + return drbg_instantiate(drbg, &seed_string, coreref, pr); } } diff --git a/include/crypto/drbg.h b/include/crypto/drbg.h index b507c5b6020a..4065dfca146a 100644 --- a/include/crypto/drbg.h +++ b/include/crypto/drbg.h @@ -50,6 +50,7 @@ #include #include #include +#include /* * Concatenation Helper and string operation helper @@ -64,7 +65,7 @@ struct drbg_string { const unsigned char *buf; size_t len; - struct drbg_string *next; + struct list_head list; }; static inline void drbg_string_fill(struct drbg_string *string, @@ -72,7 +73,7 @@ static inline void drbg_string_fill(struct drbg_string *string, { string->buf = buf; string->len = len; - string->next = NULL; + INIT_LIST_HEAD(&string->list); } struct drbg_state; @@ -97,7 +98,7 @@ struct drbg_core { }; struct drbg_state_ops { - int (*update)(struct drbg_state *drbg, struct drbg_string *seed, + int (*update)(struct drbg_state *drbg, struct list_head *seed, int reseed); int (*generate)(struct drbg_state *drbg, unsigned char *buf, unsigned int buflen,