#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/string.h>
#include <crypto/aes.h>
#include <crypto/df_sp80090a.h>
#include <crypto/internal/drbg.h>
static void drbg_kcapi_sym(struct aes_enckey *aeskey, unsigned char *outval,
const struct drbg_string *in, u8 blocklen_bytes)
{
BUG_ON(in->len < blocklen_bytes);
aes_encrypt(aeskey, outval, in->buf);
}
static void drbg_ctr_bcc(struct aes_enckey *aeskey,
unsigned char *out, const unsigned char *key,
struct list_head *in,
u8 blocklen_bytes,
u8 keylen)
{
struct drbg_string *curr = NULL;
struct drbg_string data;
short cnt = 0;
drbg_string_fill(&data, out, blocklen_bytes);
aes_prepareenckey(aeskey, key, keylen);
list_for_each_entry(curr, in, list) {
const unsigned char *pos = curr->buf;
size_t len = curr->len;
while (len) {
if (blocklen_bytes == cnt) {
cnt = 0;
drbg_kcapi_sym(aeskey, out, &data, blocklen_bytes);
}
out[cnt] ^= *pos;
pos++;
cnt++;
len--;
}
}
if (cnt)
drbg_kcapi_sym(aeskey, out, &data, blocklen_bytes);
}
int crypto_drbg_ctr_df(struct aes_enckey *aeskey,
unsigned char *df_data, size_t bytes_to_return,
struct list_head *seedlist,
u8 blocklen_bytes,
u8 statelen)
{
unsigned char L_N[8];
struct drbg_string S1, S2, S4, cipherin;
LIST_HEAD(bcc_list);
unsigned char *pad = df_data + statelen;
unsigned char *iv = pad + blocklen_bytes;
unsigned char *temp = iv + blocklen_bytes;
size_t padlen = 0;
unsigned int templen = 0;
unsigned int i = 0;
const unsigned char *K = (unsigned char *)
"\x00\x01\x02\x03\x04\x05\x06\x07"
"\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
"\x10\x11\x12\x13\x14\x15\x16\x17"
"\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f";
unsigned char *X;
size_t generated_len = 0;
size_t inputlen = 0;
struct drbg_string *seed = NULL;
u8 keylen;
memset(pad, 0, blocklen_bytes);
memset(iv, 0, blocklen_bytes);
keylen = statelen - blocklen_bytes;
if ((512 / 8) < bytes_to_return)
return -EINVAL;
list_for_each_entry(seed, seedlist, list)
inputlen += seed->len;
drbg_cpu_to_be32(inputlen, &L_N[0]);
drbg_cpu_to_be32(bytes_to_return, &L_N[4]);
padlen = (inputlen + sizeof(L_N) + 1) % (blocklen_bytes);
if (padlen)
padlen = blocklen_bytes - padlen;
padlen++;
pad[0] = 0x80;
drbg_string_fill(&S1, iv, blocklen_bytes);
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);
list_add_tail(&S4.list, &bcc_list);
while (templen < (keylen + (blocklen_bytes))) {
drbg_cpu_to_be32(i, iv);
drbg_ctr_bcc(aeskey, temp + templen, K, &bcc_list,
blocklen_bytes, keylen);
i++;
templen += blocklen_bytes;
}
X = temp + (keylen);
drbg_string_fill(&cipherin, X, blocklen_bytes);
aes_prepareenckey(aeskey, temp, keylen);
while (generated_len < bytes_to_return) {
short blocklen = 0;
drbg_kcapi_sym(aeskey, X, &cipherin, blocklen_bytes);
blocklen = (blocklen_bytes <
(bytes_to_return - generated_len)) ?
blocklen_bytes :
(bytes_to_return - generated_len);
memcpy(df_data + generated_len, X, blocklen);
generated_len += blocklen;
}
memset(iv, 0, blocklen_bytes);
memset(temp, 0, statelen + blocklen_bytes);
memset(pad, 0, blocklen_bytes);
return 0;
}
EXPORT_SYMBOL_GPL(crypto_drbg_ctr_df);
MODULE_IMPORT_NS("CRYPTO_INTERNAL");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Stephan Mueller <smueller@chronox.de>");
MODULE_DESCRIPTION("Derivation Function conformant to SP800-90A");