c6d5a76721
To meet FIPS 140 requirements, add support for building a kernel module "fips140.ko" that contains various cryptographic algorithms built from existing kernel source files. At load time, the module checks its own integrity and self-tests its algorithms, then registers the algorithms with the crypto API to supersede the original algorithms provided by the kernel itself. [ebiggers: this commit originated from "ANDROID: crypto: fips140 - perform load time integrity check", but I've folded many later commits into it to make forward porting easier. See below] Original commits: android12-5.10: 6be141eb36fe ("ANDROID: crypto: fips140 - perform load time integrity check") 868be244bbed ("ANDROID: inject correct HMAC digest into fips140.ko at build time") 091338cb398e ("ANDROID: fips140: add missing static keyword to fips140_init()") c799c6644b52 ("ANDROID: fips140: adjust some log messages") 92de53472e68 ("ANDROID: fips140: log already-live algorithms") 0af06624eadc ("ANDROID: fips140: check for errors from initcalls") 634445a640a4 ("ANDROID: fips140: fix deadlock in unregister_existing_fips140_algos()") e886dd4c339e ("ANDROID: fips140: unregister existing DRBG algorithms") b7397e89db29 ("ANDROID: fips140: add power-up cryptographic self-tests") 50661975be74 ("ANDROID: fips140: add/update module help text") b397a0387cb2 ("ANDROID: fips140: test all implementations") 17ccefe14021 ("ANDROID: fips140: use full 16-byte IV") 1be58af0776a ("ANDROID: fips140: remove non-prediction-resistant DRBG test") 2b5843ae2d90 ("ANDROID: fips140: add AES-CBC-CTS") 2ee56aad318c ("ANDROID: fips140: add AES-CMAC") 960ebb2b565b ("ANDROID: fips140: add jitterentropy to fips140 module") e5b14396f9d2 ("ANDROID: fips140: take into account AES-GCM not being approvable") 52b70d491bd4 ("ANDROID: fips140: use FIPS140_CFLAGS when compiling fips140-selftests.c") 6b995f5a5403 ("ANDROID: fips140: preserve RELA sections without relying on the module loader") e45108ecff64 ("ANDROID: fips140: block crypto operations until tests complete") ecf9341134d1 ("ANDROID: fips140: remove in-place updating of live algorithms") 482b0323cf29 ("ANDROID: fips140: zeroize temporary values from integrity check") 64d769e53f20 ("ANDROID: fips140: add service indicators") 8d7f609cdaa4 ("ANDROID: fips140: add name and version, and a function to retrieve them") 6b7c37f6c449 ("ANDROID: fips140: use UTS_RELEASE as FIPS version") 903e97a0ca6d ("ANDROID: fips140: refactor evaluation testing support") 97fb2104fe22 ("ANDROID: fips140: add support for injecting integrity error") 109f31ac23f5 ("ANDROID: fips140: add userspace interface for evaluation testing") android14-5.15: 84572a0c7981 ("ANDROID: fips140: split dump-section+add-section into 2 ops") b0f8873811d4 ("ANDROID: kleaf: convert fips140 to kleaf") 2535deae8069 ("ANDROID: GKI: Source GKI_BUILD_CONFIG_FRAGMENT after setting all variables") 685a2ade28bb ("ANDROID: fips140: add crypto_memneq() back to the module") 320dfca58a3d ("ANDROID: fips140: fix in-tree builds") d4966a820397 ("ANDROID: fips140: remove CONFIG_CRYPTO_FIPS140 option") 6da26b8750f5 ("ANDROID: fips140: require 'm' to enable CRYPTO_FIPS140_MOD") bfcfcce3803b ("ANDROID: fips140: unapply ABS32 relocations generated by KCFI") 63f46b45dda2 ("ANDROID: fips140: eliminate crypto-fips.a build step") ae4ca7a09bb6 ("ANDROID: fips140: allow building without LTO") Bug: 153614920 Bug: 188620248 Test: tested that the module builds and can be loaded on raven. Change-Id: I3fde49dbc3d16b149b072a27ba5b4c6219015c94 Signed-off-by: Ard Biesheuvel <ardb@google.com> Signed-off-by: Eric Biggers <ebiggers@google.com>
195 lines
4.6 KiB
C
195 lines
4.6 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Copyright (C) 2021 - Google LLC
|
|
* Author: Ard Biesheuvel <ardb@google.com>
|
|
*
|
|
* This is a host tool that is intended to be used to take the HMAC digest of
|
|
* the .text and .rodata sections of the fips140.ko module, and store it inside
|
|
* the module. The module will perform an integrity selfcheck at module_init()
|
|
* time, by recalculating the digest and comparing it with the value calculated
|
|
* here.
|
|
*
|
|
* Note that the peculiar way an HMAC is being used as a digest with a public
|
|
* key rather than as a symmetric key signature is mandated by FIPS 140-2.
|
|
*/
|
|
|
|
#include <elf.h>
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
|
|
#include <openssl/hmac.h>
|
|
|
|
static Elf64_Ehdr *ehdr;
|
|
static Elf64_Shdr *shdr;
|
|
static int num_shdr;
|
|
static const char *strtab, *shstrtab;
|
|
static Elf64_Sym *syms;
|
|
static int num_syms;
|
|
|
|
static Elf64_Shdr *find_symtab_section(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < num_shdr; i++)
|
|
if (shdr[i].sh_type == SHT_SYMTAB)
|
|
return &shdr[i];
|
|
return NULL;
|
|
}
|
|
|
|
static int get_section_idx(const char *name)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < num_shdr; i++)
|
|
if (!strcmp(shstrtab + shdr[i].sh_name, name))
|
|
return i;
|
|
return -1;
|
|
}
|
|
|
|
static int get_sym_idx(const char *sym_name)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < num_syms; i++)
|
|
if (!strcmp(strtab + syms[i].st_name, sym_name))
|
|
return i;
|
|
return -1;
|
|
}
|
|
|
|
static void *get_sym_addr(const char *sym_name)
|
|
{
|
|
int i = get_sym_idx(sym_name);
|
|
|
|
if (i >= 0)
|
|
return (void *)ehdr + shdr[syms[i].st_shndx].sh_offset +
|
|
syms[i].st_value;
|
|
return NULL;
|
|
}
|
|
|
|
static int update_rela_ref(const char *name)
|
|
{
|
|
/*
|
|
* We need to do a couple of things to ensure that the copied RELA data
|
|
* is accessible to the module itself at module init time:
|
|
* - the associated entry in the symbol table needs to refer to the
|
|
* correct section index, and have SECTION type and GLOBAL linkage.
|
|
* - the 'count' global variable in the module need to be set to the
|
|
* right value based on the size of the RELA section.
|
|
*/
|
|
unsigned int *size_var;
|
|
int sec_idx, sym_idx;
|
|
char str[32];
|
|
|
|
sprintf(str, "fips140_rela_%s", name);
|
|
size_var = get_sym_addr(str);
|
|
if (!size_var) {
|
|
printf("variable '%s' not found, disregarding .%s section\n",
|
|
str, name);
|
|
return 1;
|
|
}
|
|
|
|
sprintf(str, "__sec_rela_%s", name);
|
|
sym_idx = get_sym_idx(str);
|
|
|
|
sprintf(str, ".init.rela.%s", name);
|
|
sec_idx = get_section_idx(str);
|
|
|
|
if (sec_idx < 0 || sym_idx < 0) {
|
|
fprintf(stderr, "failed to locate metadata for .%s section in binary\n",
|
|
name);
|
|
return 0;
|
|
}
|
|
|
|
syms[sym_idx].st_shndx = sec_idx;
|
|
syms[sym_idx].st_info = (STB_GLOBAL << 4) | STT_SECTION;
|
|
|
|
size_var[1] = shdr[sec_idx].sh_size / sizeof(Elf64_Rela);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void hmac_section(HMAC_CTX *hmac, const char *start, const char *end)
|
|
{
|
|
void *start_addr = get_sym_addr(start);
|
|
void *end_addr = get_sym_addr(end);
|
|
|
|
HMAC_Update(hmac, start_addr, end_addr - start_addr);
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
Elf64_Shdr *symtab_shdr;
|
|
const char *hmac_key;
|
|
unsigned char *dg;
|
|
unsigned int dglen;
|
|
struct stat stat;
|
|
HMAC_CTX *hmac;
|
|
int fd, ret;
|
|
|
|
if (argc < 2) {
|
|
fprintf(stderr, "file argument missing\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
fd = open(argv[1], O_RDWR);
|
|
if (fd < 0) {
|
|
fprintf(stderr, "failed to open %s\n", argv[1]);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
ret = fstat(fd, &stat);
|
|
if (ret < 0) {
|
|
fprintf(stderr, "failed to stat() %s\n", argv[1]);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
ehdr = mmap(0, stat.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
|
if (ehdr == MAP_FAILED) {
|
|
fprintf(stderr, "failed to mmap() %s\n", argv[1]);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
shdr = (void *)ehdr + ehdr->e_shoff;
|
|
num_shdr = ehdr->e_shnum;
|
|
|
|
symtab_shdr = find_symtab_section();
|
|
|
|
syms = (void *)ehdr + symtab_shdr->sh_offset;
|
|
num_syms = symtab_shdr->sh_size / sizeof(Elf64_Sym);
|
|
|
|
strtab = (void *)ehdr + shdr[symtab_shdr->sh_link].sh_offset;
|
|
shstrtab = (void *)ehdr + shdr[ehdr->e_shstrndx].sh_offset;
|
|
|
|
if (!update_rela_ref("text") || !update_rela_ref("rodata"))
|
|
exit(EXIT_FAILURE);
|
|
|
|
hmac_key = get_sym_addr("fips140_integ_hmac_key");
|
|
if (!hmac_key) {
|
|
fprintf(stderr, "failed to locate HMAC key in binary\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
dg = get_sym_addr("fips140_integ_hmac_digest");
|
|
if (!dg) {
|
|
fprintf(stderr, "failed to locate HMAC digest in binary\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
hmac = HMAC_CTX_new();
|
|
HMAC_Init_ex(hmac, hmac_key, strlen(hmac_key), EVP_sha256(), NULL);
|
|
|
|
hmac_section(hmac, "__fips140_text_start", "__fips140_text_end");
|
|
hmac_section(hmac, "__fips140_rodata_start", "__fips140_rodata_end");
|
|
|
|
HMAC_Final(hmac, dg, &dglen);
|
|
|
|
close(fd);
|
|
return 0;
|
|
}
|