123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Copyright 2021 Google LLC
- *
- * This program provides commands that dump certain types of output from the
- * fips140 kernel module, as required by the FIPS lab for evaluation purposes.
- *
- * 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.
- * 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:
- *
- * NDK_DIR=$HOME/android-ndk-r23b # adjust directory path as needed
- * $NDK_DIR/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android31-clang \
- * fips140_lab_util.c -O2 -Wall -o fips140_lab_util
- * adb push fips140_lab_util /data/local/tmp/
- * adb root
- * adb shell /data/local/tmp/fips140_lab_util
- */
- #include <errno.h>
- #include <fcntl.h>
- #include <getopt.h>
- #include <limits.h>
- #include <linux/if_alg.h>
- #include <stdarg.h>
- #include <stdbool.h>
- #include <stdint.h>
- #include <stdio.h>
- #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>
- #include "../../crypto/fips140-eval-testing-uapi.h"
- /* ---------------------------------------------------------------------------
- * Utility functions
- * ---------------------------------------------------------------------------*/
- #define ARRAY_SIZE(A) (sizeof(A) / sizeof((A)[0]))
- #define MIN(a, b) ((a) < (b) ? (a) : (b))
- #define MAX(a, b) ((a) > (b) ? (a) : (b))
- static void __attribute__((noreturn))
- do_die(const char *format, va_list va, int err)
- {
- fputs("ERROR: ", stderr);
- vfprintf(stderr, format, va);
- if (err)
- fprintf(stderr, ": %s", strerror(err));
- putc('\n', stderr);
- exit(1);
- }
- static void __attribute__((noreturn, format(printf, 1, 2)))
- die_errno(const char *format, ...)
- {
- va_list va;
- va_start(va, format);
- do_die(format, va, errno);
- va_end(va);
- }
- static void __attribute__((noreturn, format(printf, 1, 2)))
- die(const char *format, ...)
- {
- va_list va;
- va_start(va, format);
- do_die(format, va, 0);
- 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 full_write(int fd, const void *buf, size_t count)
- {
- while (count) {
- ssize_t ret = write(fd, buf, count);
- if (ret < 0)
- die_errno("write failed");
- buf += ret;
- count -= ret;
- }
- }
- enum {
- OPT_AMOUNT,
- OPT_ITERATIONS,
- };
- static void usage(void);
- /* ---------------------------------------------------------------------------
- * /dev/fips140 ioctls
- * ---------------------------------------------------------------------------*/
- static int get_fips140_device_number(void)
- {
- FILE *f;
- char line[128];
- int number;
- char name[32];
- f = fopen("/proc/devices", "r");
- if (!f)
- die_errno("Failed to open /proc/devices");
- while (fgets(line, sizeof(line), f)) {
- if (sscanf(line, "%d %31s", &number, name) == 2 &&
- strcmp(name, "fips140") == 0)
- return number;
- }
- fclose(f);
- die("fips140 device node is unavailable.\n"
- "The fips140 device node is only available when the fips140 module is loaded\n"
- "and has been built with evaluation testing support.");
- }
- static void create_fips140_node_if_needed(void)
- {
- struct stat stbuf;
- int major;
- if (stat("/dev/fips140", &stbuf) == 0)
- return;
- major = get_fips140_device_number();
- if (mknod("/dev/fips140", S_IFCHR | 0600, makedev(major, 1)) != 0)
- die_errno("Failed to create fips140 device node");
- }
- static int fips140_dev_fd = -1;
- static int fips140_ioctl(int cmd, const void *arg)
- {
- if (fips140_dev_fd < 0) {
- create_fips140_node_if_needed();
- fips140_dev_fd = open("/dev/fips140", O_RDONLY);
- if (fips140_dev_fd < 0)
- die_errno("Failed to open /dev/fips140");
- }
- return ioctl(fips140_dev_fd, cmd, arg);
- }
- static bool fips140_is_approved_service(const char *name)
- {
- int ret = fips140_ioctl(FIPS140_IOCTL_IS_APPROVED_SERVICE, name);
- if (ret < 0)
- die_errno("FIPS140_IOCTL_IS_APPROVED_SERVICE unexpectedly failed");
- if (ret == 1)
- return true;
- if (ret == 0)
- return false;
- die("FIPS140_IOCTL_IS_APPROVED_SERVICE returned unexpected value %d",
- ret);
- }
- static const char *fips140_module_version(void)
- {
- static char buf[256];
- int ret;
- memset(buf, 0, sizeof(buf));
- ret = fips140_ioctl(FIPS140_IOCTL_MODULE_VERSION, buf);
- if (ret < 0)
- die_errno("FIPS140_IOCTL_MODULE_VERSION unexpectedly failed");
- if (ret != 0)
- die("FIPS140_IOCTL_MODULE_VERSION returned unexpected value %d",
- ret);
- 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;
- }
- /* ---------------------------------------------------------------------------
- * dump_jitterentropy command
- * ---------------------------------------------------------------------------*/
- static void dump_from_jent_fd(int fd, size_t count)
- {
- uint8_t buf[AF_ALG_MAX_RNG_REQUEST_SIZE];
- while (count) {
- ssize_t ret;
- memset(buf, 0, sizeof(buf));
- ret = read(fd, buf, MIN(count, sizeof(buf)));
- if (ret < 0)
- die_errno("error reading from jitterentropy_rng");
- full_write(STDOUT_FILENO, buf, ret);
- count -= ret;
- }
- }
- static int cmd_dump_jitterentropy(int argc, char *argv[])
- {
- static const struct option longopts[] = {
- { "amount", required_argument, NULL, OPT_AMOUNT },
- { "iterations", required_argument, NULL, OPT_ITERATIONS },
- { NULL, 0, NULL, 0 },
- };
- size_t amount = 128;
- size_t iterations = 1;
- size_t i;
- int c;
- while ((c = getopt_long(argc, argv, "", longopts, NULL)) != -1) {
- switch (c) {
- case OPT_AMOUNT:
- amount = strtoul(optarg, NULL, 0);
- if (amount <= 0 || amount >= ULONG_MAX)
- die("invalid argument to --amount");
- break;
- case OPT_ITERATIONS:
- iterations = strtoul(optarg, NULL, 0);
- if (iterations <= 0 || iterations >= ULONG_MAX)
- die("invalid argument to --iterations");
- break;
- default:
- usage();
- return 1;
- }
- }
- for (i = 0; i < iterations; i++) {
- int alg_fd = get_alg_fd("rng", "jitterentropy_rng");
- int req_fd = get_req_fd(alg_fd, "jitterentropy_rng");
- dump_from_jent_fd(req_fd, amount);
- close(req_fd);
- close(alg_fd);
- }
- return 0;
- }
- /* ---------------------------------------------------------------------------
- * 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
- * ---------------------------------------------------------------------------*/
- static int cmd_show_module_version(int argc, char *argv[])
- {
- printf("fips140_module_version() => \"%s\"\n",
- fips140_module_version());
- return 0;
- }
- /* ---------------------------------------------------------------------------
- * show_service_indicators command
- * ---------------------------------------------------------------------------*/
- static const char * const default_services_to_show[] = {
- "aes",
- "cbc(aes)",
- "cbcmac(aes)",
- "cmac(aes)",
- "ctr(aes)",
- "cts(cbc(aes))",
- "ecb(aes)",
- "essiv(cbc(aes),sha256)",
- "gcm(aes)",
- "hmac(sha1)",
- "hmac(sha224)",
- "hmac(sha256)",
- "hmac(sha384)",
- "hmac(sha512)",
- "jitterentropy_rng",
- "sha1",
- "sha224",
- "sha256",
- "sha384",
- "sha512",
- "stdrng",
- "xcbc(aes)",
- "xts(aes)",
- };
- static int cmd_show_service_indicators(int argc, char *argv[])
- {
- const char * const *services = default_services_to_show;
- int count = ARRAY_SIZE(default_services_to_show);
- int i;
- if (argc > 1) {
- services = (const char **)(argv + 1);
- count = argc - 1;
- }
- for (i = 0; i < count; i++) {
- printf("fips140_is_approved_service(\"%s\") => %s\n",
- services[i],
- booltostr(fips140_is_approved_service(services[i])));
- }
- return 0;
- }
- /* ---------------------------------------------------------------------------
- * main()
- * ---------------------------------------------------------------------------*/
- static const struct command {
- const char *name;
- int (*func)(int argc, char *argv[]);
- } commands[] = {
- { "dump_jitterentropy", cmd_dump_jitterentropy },
- { "show_invalid_inputs", cmd_show_invalid_inputs },
- { "show_module_version", cmd_show_module_version },
- { "show_service_indicators", cmd_show_service_indicators },
- };
- static void usage(void)
- {
- fprintf(stderr,
- "Usage:\n"
- " fips140_lab_util dump_jitterentropy [OPTION]...\n"
- " fips140_lab_util show_invalid_inputs\n"
- " fips140_lab_util show_module_version\n"
- " fips140_lab_util show_service_indicators [SERVICE]...\n"
- "\n"
- "Options for dump_jitterentropy:\n"
- " --amount=AMOUNT Amount to dump in bytes per iteration (default 128)\n"
- " --iterations=COUNT Number of start-up iterations (default 1)\n"
- );
- }
- int main(int argc, char *argv[])
- {
- int i;
- if (argc < 2) {
- usage();
- return 2;
- }
- for (i = 1; i < argc; i++) {
- if (strcmp(argv[i], "--help") == 0) {
- usage();
- return 2;
- }
- }
- for (i = 0; i < ARRAY_SIZE(commands); i++) {
- if (strcmp(commands[i].name, argv[1]) == 0)
- return commands[i].func(argc - 1, argv + 1);
- }
- fprintf(stderr, "Unknown command: %s\n\n", argv[1]);
- usage();
- return 2;
- }
|