|
@@ -258,6 +258,7 @@ static int drbg_kcapi_sym(struct drbg_state *drbg, unsigned char *outval,
|
|
|
const struct drbg_string *in);
|
|
|
static int drbg_init_sym_kernel(struct drbg_state *drbg);
|
|
|
static int drbg_fini_sym_kernel(struct drbg_state *drbg);
|
|
|
+static int drbg_kcapi_sym_ctr(struct drbg_state *drbg, u8 *outbuf, u32 outlen);
|
|
|
|
|
|
/* BCC function for CTR DRBG as defined in 10.4.3 */
|
|
|
static int drbg_ctr_bcc(struct drbg_state *drbg,
|
|
@@ -482,36 +483,37 @@ static int drbg_ctr_update(struct drbg_state *drbg, struct list_head *seed,
|
|
|
drbg_blocklen(drbg);
|
|
|
unsigned char *temp_p, *df_data_p; /* pointer to iterate over buffers */
|
|
|
unsigned int len = 0;
|
|
|
- struct drbg_string cipherin;
|
|
|
|
|
|
if (3 > reseed)
|
|
|
memset(df_data, 0, drbg_statelen(drbg));
|
|
|
|
|
|
- /* 10.2.1.3.2 step 2 and 10.2.1.4.2 step 2 */
|
|
|
- if (seed) {
|
|
|
- ret = drbg_ctr_df(drbg, df_data, drbg_statelen(drbg), seed);
|
|
|
+ if (!reseed) {
|
|
|
+ /*
|
|
|
+ * The DRBG uses the CTR mode of the underlying AES cipher. The
|
|
|
+ * CTR mode increments the counter value after the AES operation
|
|
|
+ * but SP800-90A requires that the counter is incremented before
|
|
|
+ * the AES operation. Hence, we increment it at the time we set
|
|
|
+ * it by one.
|
|
|
+ */
|
|
|
+ crypto_inc(drbg->V, drbg_blocklen(drbg));
|
|
|
+
|
|
|
+ ret = crypto_skcipher_setkey(drbg->ctr_handle, drbg->C,
|
|
|
+ drbg_keylen(drbg));
|
|
|
if (ret)
|
|
|
goto out;
|
|
|
- drbg_kcapi_symsetkey(drbg, drbg->C);
|
|
|
}
|
|
|
|
|
|
- drbg_string_fill(&cipherin, drbg->V, drbg_blocklen(drbg));
|
|
|
- /*
|
|
|
- * 10.2.1.3.2 steps 2 and 3 are already covered as the allocation
|
|
|
- * zeroizes all memory during initialization
|
|
|
- */
|
|
|
- while (len < (drbg_statelen(drbg))) {
|
|
|
- /* 10.2.1.2 step 2.1 */
|
|
|
- crypto_inc(drbg->V, drbg_blocklen(drbg));
|
|
|
- /*
|
|
|
- * 10.2.1.2 step 2.2 */
|
|
|
- ret = drbg_kcapi_sym(drbg, temp + len, &cipherin);
|
|
|
+ /* 10.2.1.3.2 step 2 and 10.2.1.4.2 step 2 */
|
|
|
+ if (seed) {
|
|
|
+ ret = drbg_ctr_df(drbg, df_data, drbg_statelen(drbg), seed);
|
|
|
if (ret)
|
|
|
goto out;
|
|
|
- /* 10.2.1.2 step 2.3 and 3 */
|
|
|
- len += drbg_blocklen(drbg);
|
|
|
}
|
|
|
|
|
|
+ ret = drbg_kcapi_sym_ctr(drbg, temp, drbg_statelen(drbg));
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+
|
|
|
/* 10.2.1.2 step 4 */
|
|
|
temp_p = temp;
|
|
|
df_data_p = df_data;
|
|
@@ -522,9 +524,14 @@ static int drbg_ctr_update(struct drbg_state *drbg, struct list_head *seed,
|
|
|
|
|
|
/* 10.2.1.2 step 5 */
|
|
|
memcpy(drbg->C, temp, drbg_keylen(drbg));
|
|
|
- drbg_kcapi_symsetkey(drbg, drbg->C);
|
|
|
+ ret = crypto_skcipher_setkey(drbg->ctr_handle, drbg->C,
|
|
|
+ drbg_keylen(drbg));
|
|
|
+ if (ret)
|
|
|
+ goto out;
|
|
|
/* 10.2.1.2 step 6 */
|
|
|
memcpy(drbg->V, temp + drbg_keylen(drbg), drbg_blocklen(drbg));
|
|
|
+ /* See above: increment counter by one to compensate timing of CTR op */
|
|
|
+ crypto_inc(drbg->V, drbg_blocklen(drbg));
|
|
|
ret = 0;
|
|
|
|
|
|
out:
|
|
@@ -543,46 +550,26 @@ static int drbg_ctr_generate(struct drbg_state *drbg,
|
|
|
unsigned char *buf, unsigned int buflen,
|
|
|
struct list_head *addtl)
|
|
|
{
|
|
|
- int len = 0;
|
|
|
- int ret = 0;
|
|
|
- struct drbg_string data;
|
|
|
+ int ret;
|
|
|
+ int len = min_t(int, buflen, INT_MAX);
|
|
|
|
|
|
/* 10.2.1.5.2 step 2 */
|
|
|
if (addtl && !list_empty(addtl)) {
|
|
|
ret = drbg_ctr_update(drbg, addtl, 2);
|
|
|
if (ret)
|
|
|
return 0;
|
|
|
- drbg_kcapi_symsetkey(drbg, drbg->C);
|
|
|
}
|
|
|
|
|
|
/* 10.2.1.5.2 step 4.1 */
|
|
|
- crypto_inc(drbg->V, drbg_blocklen(drbg));
|
|
|
- drbg_string_fill(&data, drbg->V, drbg_blocklen(drbg));
|
|
|
- while (len < buflen) {
|
|
|
- int outlen = 0;
|
|
|
- /* 10.2.1.5.2 step 4.2 */
|
|
|
- ret = drbg_kcapi_sym(drbg, drbg->scratchpad, &data);
|
|
|
- if (ret) {
|
|
|
- len = ret;
|
|
|
- goto out;
|
|
|
- }
|
|
|
- outlen = (drbg_blocklen(drbg) < (buflen - len)) ?
|
|
|
- drbg_blocklen(drbg) : (buflen - len);
|
|
|
- /* 10.2.1.5.2 step 4.3 */
|
|
|
- memcpy(buf + len, drbg->scratchpad, outlen);
|
|
|
- len += outlen;
|
|
|
- /* 10.2.1.5.2 step 6 */
|
|
|
- if (len < buflen)
|
|
|
- crypto_inc(drbg->V, drbg_blocklen(drbg));
|
|
|
- }
|
|
|
+ ret = drbg_kcapi_sym_ctr(drbg, buf, len);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
|
|
|
/* 10.2.1.5.2 step 6 */
|
|
|
ret = drbg_ctr_update(drbg, NULL, 3);
|
|
|
if (ret)
|
|
|
len = ret;
|
|
|
|
|
|
-out:
|
|
|
- memset(drbg->scratchpad, 0, drbg_blocklen(drbg));
|
|
|
return len;
|
|
|
}
|
|
|
|
|
@@ -1634,10 +1621,46 @@ static int drbg_kcapi_hash(struct drbg_state *drbg, unsigned char *outval,
|
|
|
#endif /* (CONFIG_CRYPTO_DRBG_HASH || CONFIG_CRYPTO_DRBG_HMAC) */
|
|
|
|
|
|
#ifdef CONFIG_CRYPTO_DRBG_CTR
|
|
|
+static int drbg_fini_sym_kernel(struct drbg_state *drbg)
|
|
|
+{
|
|
|
+ struct crypto_cipher *tfm =
|
|
|
+ (struct crypto_cipher *)drbg->priv_data;
|
|
|
+ if (tfm)
|
|
|
+ crypto_free_cipher(tfm);
|
|
|
+ drbg->priv_data = NULL;
|
|
|
+
|
|
|
+ if (drbg->ctr_handle)
|
|
|
+ crypto_free_skcipher(drbg->ctr_handle);
|
|
|
+ drbg->ctr_handle = NULL;
|
|
|
+
|
|
|
+ if (drbg->ctr_req)
|
|
|
+ skcipher_request_free(drbg->ctr_req);;
|
|
|
+ drbg->ctr_req = NULL;
|
|
|
+
|
|
|
+ kfree(drbg->ctr_null_value_buf);
|
|
|
+ drbg->ctr_null_value = NULL;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void drbg_skcipher_cb(struct crypto_async_request *req, int error)
|
|
|
+{
|
|
|
+ struct drbg_state *drbg = req->data;
|
|
|
+
|
|
|
+ if (error == -EINPROGRESS)
|
|
|
+ return;
|
|
|
+ drbg->ctr_async_err = error;
|
|
|
+ complete(&drbg->ctr_completion);
|
|
|
+}
|
|
|
+
|
|
|
+#define DRBG_CTR_NULL_LEN 128
|
|
|
static int drbg_init_sym_kernel(struct drbg_state *drbg)
|
|
|
{
|
|
|
- int ret = 0;
|
|
|
struct crypto_cipher *tfm;
|
|
|
+ struct crypto_skcipher *sk_tfm;
|
|
|
+ struct skcipher_request *req;
|
|
|
+ unsigned int alignmask;
|
|
|
+ char ctr_name[CRYPTO_MAX_ALG_NAME];
|
|
|
|
|
|
tfm = crypto_alloc_cipher(drbg->core->backend_cra_name, 0, 0);
|
|
|
if (IS_ERR(tfm)) {
|
|
@@ -1647,16 +1670,41 @@ static int drbg_init_sym_kernel(struct drbg_state *drbg)
|
|
|
}
|
|
|
BUG_ON(drbg_blocklen(drbg) != crypto_cipher_blocksize(tfm));
|
|
|
drbg->priv_data = tfm;
|
|
|
- return ret;
|
|
|
-}
|
|
|
|
|
|
-static int drbg_fini_sym_kernel(struct drbg_state *drbg)
|
|
|
-{
|
|
|
- struct crypto_cipher *tfm =
|
|
|
- (struct crypto_cipher *)drbg->priv_data;
|
|
|
- if (tfm)
|
|
|
- crypto_free_cipher(tfm);
|
|
|
- drbg->priv_data = NULL;
|
|
|
+ if (snprintf(ctr_name, CRYPTO_MAX_ALG_NAME, "ctr(%s)",
|
|
|
+ drbg->core->backend_cra_name) >= CRYPTO_MAX_ALG_NAME) {
|
|
|
+ drbg_fini_sym_kernel(drbg);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+ sk_tfm = crypto_alloc_skcipher(ctr_name, 0, 0);
|
|
|
+ if (IS_ERR(sk_tfm)) {
|
|
|
+ pr_info("DRBG: could not allocate CTR cipher TFM handle: %s\n",
|
|
|
+ ctr_name);
|
|
|
+ drbg_fini_sym_kernel(drbg);
|
|
|
+ return PTR_ERR(sk_tfm);
|
|
|
+ }
|
|
|
+ drbg->ctr_handle = sk_tfm;
|
|
|
+
|
|
|
+ req = skcipher_request_alloc(sk_tfm, GFP_KERNEL);
|
|
|
+ if (!req) {
|
|
|
+ pr_info("DRBG: could not allocate request queue\n");
|
|
|
+ drbg_fini_sym_kernel(drbg);
|
|
|
+ return PTR_ERR(req);
|
|
|
+ }
|
|
|
+ drbg->ctr_req = req;
|
|
|
+ skcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
|
|
|
+ drbg_skcipher_cb, drbg);
|
|
|
+
|
|
|
+ alignmask = crypto_skcipher_alignmask(sk_tfm);
|
|
|
+ drbg->ctr_null_value_buf = kzalloc(DRBG_CTR_NULL_LEN + alignmask,
|
|
|
+ GFP_KERNEL);
|
|
|
+ if (!drbg->ctr_null_value_buf) {
|
|
|
+ drbg_fini_sym_kernel(drbg);
|
|
|
+ return -ENOMEM;
|
|
|
+ }
|
|
|
+ drbg->ctr_null_value = (u8 *)PTR_ALIGN(drbg->ctr_null_value_buf,
|
|
|
+ alignmask + 1);
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -1680,6 +1728,43 @@ static int drbg_kcapi_sym(struct drbg_state *drbg, unsigned char *outval,
|
|
|
crypto_cipher_encrypt_one(tfm, outval, in->buf);
|
|
|
return 0;
|
|
|
}
|
|
|
+
|
|
|
+static int drbg_kcapi_sym_ctr(struct drbg_state *drbg, u8 *outbuf, u32 outlen)
|
|
|
+{
|
|
|
+ struct scatterlist sg_in;
|
|
|
+
|
|
|
+ sg_init_one(&sg_in, drbg->ctr_null_value, DRBG_CTR_NULL_LEN);
|
|
|
+
|
|
|
+ while (outlen) {
|
|
|
+ u32 cryptlen = min_t(u32, outlen, DRBG_CTR_NULL_LEN);
|
|
|
+ struct scatterlist sg_out;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ sg_init_one(&sg_out, outbuf, cryptlen);
|
|
|
+ skcipher_request_set_crypt(drbg->ctr_req, &sg_in, &sg_out,
|
|
|
+ cryptlen, drbg->V);
|
|
|
+ ret = crypto_skcipher_encrypt(drbg->ctr_req);
|
|
|
+ switch (ret) {
|
|
|
+ case 0:
|
|
|
+ break;
|
|
|
+ case -EINPROGRESS:
|
|
|
+ case -EBUSY:
|
|
|
+ ret = wait_for_completion_interruptible(
|
|
|
+ &drbg->ctr_completion);
|
|
|
+ if (!ret && !drbg->ctr_async_err) {
|
|
|
+ reinit_completion(&drbg->ctr_completion);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ default:
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+ init_completion(&drbg->ctr_completion);
|
|
|
+
|
|
|
+ outlen -= cryptlen;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
#endif /* CONFIG_CRYPTO_DRBG_CTR */
|
|
|
|
|
|
/***************************************************************
|