ANDROID: fips140: add show_invalid_inputs command to fips140_lab_util

Add a new fips140_lab_util command 'show_invalid_inputs' which uses
AF_ALG to call some crypto algorithms with invalid parameters to show
that they fail.  This is needed to meet a new requirement we've received
from the lab.  This requirement is vague, but a representative sample of
algorithms and inputs appears to be acceptable.

For this to work, AF_ALG needs to be enabled in the kernel.  This makes
fips140_lab_util start depending on a custom kernel build, not just on a
custom fips140 module build as was the case before.  However, the lab
testing was going to need custom boot images anyway once fips140.ko is
included in the normal builds, since the production build of fips140.ko
won't have CONFIG_CRYPTO_FIPS140_MOD_EVAL_TESTING=y.  AF_ALG is also
needed to do the Jitter RNG entropy analysis properly, and the
AF_ALG-enabled kernel can also be reused for ACVP testing.

Bug: 188620248
Change-Id: I69054eab5005fc3ca0ea081760877f73ea229f5b
Signed-off-by: Eric Biggers <ebiggers@google.com>
(cherry picked from commit 04e49b41be57bbc668e39a2bb65fa6022a22deba)
This commit is contained in:
Eric Biggers
2021-12-15 11:27:34 -08:00
parent a481d43521
commit 3a624c9ccd

View File

@@ -7,7 +7,9 @@
*
* While the fips140 kernel module can only be accessed directly by other kernel
* code, an easy-to-use userspace utility program was desired for lab testing.
* For this, a custom device node /dev/fips140 is used; this requires that the
* When possible, this program uses AF_ALG to access the crypto algorithms; this
* requires that the kernel has AF_ALG enabled. Where AF_ALG isn't sufficient,
* a custom device node /dev/fips140 is used instead; this requires that the
* fips140 module is loaded and has evaluation testing support compiled in.
*
* This program can be compiled and run on an Android device as follows:
@@ -23,6 +25,7 @@
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <linux/if_alg.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
@@ -30,6 +33,7 @@
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <unistd.h>
@@ -73,11 +77,38 @@ die(const char *format, ...)
va_end(va);
}
static void __attribute__((noreturn))
assertion_failed(const char *expr, const char *file, int line)
{
die("Assertion failed: %s at %s:%d", expr, file, line);
}
#define ASSERT(e) ({ if (!(e)) assertion_failed(#e, __FILE__, __LINE__); })
static void rand_bytes(uint8_t *bytes, size_t count)
{
size_t i;
for (i = 0; i < count; i++)
bytes[i] = rand();
}
static const char *booltostr(bool b)
{
return b ? "true" : "false";
}
static const char *bytes_to_hex(const uint8_t *bytes, size_t count)
{
static char hex[1025];
size_t i;
ASSERT(count <= 512);
for (i = 0; i < count; i++)
sprintf(&hex[2*i], "%02x", bytes[i]);
return hex;
}
static void usage(void);
/* ---------------------------------------------------------------------------
@@ -160,6 +191,258 @@ static const char *fips140_module_version(void)
return buf;
}
/* ---------------------------------------------------------------------------
* AF_ALG utilities
* ---------------------------------------------------------------------------*/
#define AF_ALG_MAX_RNG_REQUEST_SIZE 128
static int get_alg_fd(const char *alg_type, const char *alg_name)
{
struct sockaddr_alg addr = {};
int alg_fd;
alg_fd = socket(AF_ALG, SOCK_SEQPACKET, 0);
if (alg_fd < 0)
die("Failed to create AF_ALG socket.\n"
"AF_ALG is only available when it has been enabled in the kernel.\n");
strncpy((char *)addr.salg_type, alg_type, sizeof(addr.salg_type) - 1);
strncpy((char *)addr.salg_name, alg_name, sizeof(addr.salg_name) - 1);
if (bind(alg_fd, (void *)&addr, sizeof(addr)) != 0)
die_errno("Failed to bind AF_ALG socket to %s %s",
alg_type, alg_name);
return alg_fd;
}
static int get_req_fd(int alg_fd, const char *alg_name)
{
int req_fd = accept(alg_fd, NULL, NULL);
if (req_fd < 0)
die_errno("Failed to get request file descriptor for %s",
alg_name);
return req_fd;
}
/* ---------------------------------------------------------------------------
* show_invalid_inputs command
* ---------------------------------------------------------------------------*/
enum direction {
UNSPECIFIED,
DECRYPT,
ENCRYPT,
};
static const struct invalid_input_test {
const char *alg_type;
const char *alg_name;
const char *key;
size_t key_size;
const char *msg;
size_t msg_size;
const char *iv;
size_t iv_size;
enum direction direction;
int setkey_error;
int crypt_error;
} invalid_input_tests[] = {
{
.alg_type = "skcipher",
.alg_name = "cbc(aes)",
.key_size = 16,
}, {
.alg_type = "skcipher",
.alg_name = "cbc(aes)",
.key_size = 17,
.setkey_error = EINVAL,
}, {
.alg_type = "skcipher",
.alg_name = "cbc(aes)",
.key_size = 24,
}, {
.alg_type = "skcipher",
.alg_name = "cbc(aes)",
.key_size = 32,
}, {
.alg_type = "skcipher",
.alg_name = "cbc(aes)",
.key_size = 33,
.setkey_error = EINVAL,
}, {
.alg_type = "skcipher",
.alg_name = "cbc(aes)",
.key_size = 16,
.msg_size = 1,
.direction = DECRYPT,
.crypt_error = EINVAL,
}, {
.alg_type = "skcipher",
.alg_name = "cbc(aes)",
.key_size = 16,
.msg_size = 16,
.direction = ENCRYPT,
}, {
.alg_type = "skcipher",
.alg_name = "cbc(aes)",
.key_size = 16,
.msg_size = 17,
.direction = ENCRYPT,
.crypt_error = EINVAL,
}, {
.alg_type = "hash",
.alg_name = "cmac(aes)",
.key_size = 29,
.setkey_error = EINVAL,
}, {
.alg_type = "skcipher",
.alg_name = "xts(aes)",
.key_size = 32,
}, {
.alg_type = "skcipher",
.alg_name = "xts(aes)",
.key = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",
.key_size = 32,
.setkey_error = EINVAL,
}
};
static const char *describe_crypt_op(const struct invalid_input_test *t)
{
if (t->direction == ENCRYPT)
return "encryption";
if (t->direction == DECRYPT)
return "decryption";
if (strcmp(t->alg_type, "hash") == 0)
return "hashing";
ASSERT(0);
}
static bool af_alg_setkey(const struct invalid_input_test *t, int alg_fd)
{
const uint8_t *key = (const uint8_t *)t->key;
uint8_t _key[t->key_size];
if (t->key_size == 0)
return true;
if (t->key == NULL) {
rand_bytes(_key, t->key_size);
key = _key;
}
if (setsockopt(alg_fd, SOL_ALG, ALG_SET_KEY, key, t->key_size) != 0) {
printf("%s: setting %zu-byte key failed with error '%s'\n",
t->alg_name, t->key_size, strerror(errno));
printf("\tkey was %s\n\n", bytes_to_hex(key, t->key_size));
ASSERT(t->setkey_error == errno);
return false;
}
printf("%s: setting %zu-byte key succeeded\n",
t->alg_name, t->key_size);
printf("\tkey was %s\n\n", bytes_to_hex(key, t->key_size));
ASSERT(t->setkey_error == 0);
return true;
}
static void af_alg_process_msg(const struct invalid_input_test *t, int alg_fd)
{
struct iovec iov;
struct msghdr hdr = {
.msg_iov = &iov,
.msg_iovlen = 1,
};
const uint8_t *msg = (const uint8_t *)t->msg;
uint8_t *_msg = NULL;
uint8_t *output = NULL;
uint8_t *control = NULL;
size_t controllen = 0;
struct cmsghdr *cmsg;
int req_fd;
if (t->msg_size == 0)
return;
req_fd = get_req_fd(alg_fd, t->alg_name);
if (t->msg == NULL) {
_msg = malloc(t->msg_size);
rand_bytes(_msg, t->msg_size);
msg = _msg;
}
output = malloc(t->msg_size);
iov.iov_base = (void *)msg;
iov.iov_len = t->msg_size;
if (t->direction != UNSPECIFIED)
controllen += CMSG_SPACE(sizeof(uint32_t));
if (t->iv_size)
controllen += CMSG_SPACE(sizeof(struct af_alg_iv) + t->iv_size);
control = calloc(1, controllen);
hdr.msg_control = control;
hdr.msg_controllen = controllen;
cmsg = CMSG_FIRSTHDR(&hdr);
if (t->direction != UNSPECIFIED) {
cmsg->cmsg_level = SOL_ALG;
cmsg->cmsg_type = ALG_SET_OP;
cmsg->cmsg_len = CMSG_LEN(sizeof(uint32_t));
*(uint32_t *)CMSG_DATA(cmsg) = t->direction == DECRYPT ?
ALG_OP_DECRYPT : ALG_OP_ENCRYPT;
cmsg = CMSG_NXTHDR(&hdr, cmsg);
}
if (t->iv_size) {
struct af_alg_iv *alg_iv;
cmsg->cmsg_level = SOL_ALG;
cmsg->cmsg_type = ALG_SET_IV;
cmsg->cmsg_len = CMSG_LEN(sizeof(*alg_iv) + t->iv_size);
alg_iv = (struct af_alg_iv *)CMSG_DATA(cmsg);
alg_iv->ivlen = t->iv_size;
memcpy(alg_iv->iv, t->iv, t->iv_size);
}
if (sendmsg(req_fd, &hdr, 0) != t->msg_size)
die_errno("sendmsg failed");
if (read(req_fd, output, t->msg_size) != t->msg_size) {
printf("%s: %s of %zu-byte message failed with error '%s'\n",
t->alg_name, describe_crypt_op(t), t->msg_size,
strerror(errno));
printf("\tmessage was %s\n\n", bytes_to_hex(msg, t->msg_size));
ASSERT(t->crypt_error == errno);
} else {
printf("%s: %s of %zu-byte message succeeded\n",
t->alg_name, describe_crypt_op(t), t->msg_size);
printf("\tmessage was %s\n\n", bytes_to_hex(msg, t->msg_size));
ASSERT(t->crypt_error == 0);
}
free(_msg);
free(output);
free(control);
close(req_fd);
}
static void test_invalid_input(const struct invalid_input_test *t)
{
int alg_fd = get_alg_fd(t->alg_type, t->alg_name);
if (af_alg_setkey(t, alg_fd))
af_alg_process_msg(t, alg_fd);
close(alg_fd);
}
static int cmd_show_invalid_inputs(int argc, char *argv[])
{
int i;
for (i = 0; i < ARRAY_SIZE(invalid_input_tests); i++)
test_invalid_input(&invalid_input_tests[i]);
return 0;
}
/* ---------------------------------------------------------------------------
* show_module_version command
* ---------------------------------------------------------------------------*/
@@ -227,6 +510,7 @@ static const struct command {
const char *name;
int (*func)(int argc, char *argv[]);
} commands[] = {
{ "show_invalid_inputs", cmd_show_invalid_inputs },
{ "show_module_version", cmd_show_module_version },
{ "show_service_indicators", cmd_show_service_indicators },
};
@@ -235,6 +519,7 @@ static void usage(void)
{
fprintf(stderr,
"Usage:\n"
" fips140_lab_util show_invalid_inputs\n"
" fips140_lab_util show_module_version\n"
" fips140_lab_util show_service_indicators [SERVICE]...\n"
);