diff --git a/samples/crypto/fips140_lab_util.c b/samples/crypto/fips140_lab_util.c index 5ce2092bac8a..996839dbd2e3 100644 --- a/samples/crypto/fips140_lab_util.c +++ b/samples/crypto/fips140_lab_util.c @@ -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 #include #include +#include #include #include #include @@ -30,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -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" );