summaryrefslogtreecommitdiffstats
path: root/crypto/drbg.c
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/drbg.c')
-rw-r--r--crypto/drbg.c233
1 files changed, 124 insertions, 109 deletions
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);
}
}
OpenPOWER on IntegriCloud