crypto: drbg - use of kernel linked list

The DRBG-style linked list to manage input data that is fed into the
cipher invocations is replaced with the kernel linked list
implementation.

The change is transparent to users of the interfaces offered by the
DRBG. Therefore, no changes to the testmgr code is needed.

Reported-by: kbuild test robot <fengguang.wu@intel.com>
Signed-off-by: Stephan Mueller <smueller@chronox.de>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
This commit is contained in:
Stephan Mueller 2014-06-28 21:58:24 +02:00 committed by Herbert Xu
parent 8fecaad77f
commit 8c98716601
2 changed files with 128 additions and 112 deletions

View file

@ -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 @@ static int drbg_ctr_df(struct drbg_state *drbg,
}
/* 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 @@ static int drbg_hash_df(struct drbg_state *drbg,
}
/* 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);
}
}

View file

@ -50,6 +50,7 @@
#include <crypto/rng.h>
#include <linux/fips.h>
#include <linux/spinlock.h>
#include <linux/list.h>
/*
* 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,