android_kernel_samsung_on5x.../drivers/crypto/fmp/fmp_integrity.c
2018-06-19 23:16:04 +02:00

283 lines
7 KiB
C

/*
* Perform FIPS Integrity test on Kernel Crypto API
*
* At build time, hmac(sha256) of crypto code, avaiable in different ELF sections
* of vmlinux file, is generated. vmlinux file is updated with built-time hmac
* in a read-only data variable, so that it is available at run-time
*
* At run time, hmac(sha256) is again calculated using crypto bytes of a running
* At run time, hmac-fmp(sha256-fmp) is again calculated using crypto bytes of a running
* kernel.
* Run time hmac is compared to built time hmac to verify the integrity.
*
*
* Author : Rohit Kothari (r.kothari@samsung.com)
* Date : 11 Feb 2014
*
* Copyright (c) 2014 Samsung Electronics
*
*/
#include <linux/crypto.h>
#include <linux/kallsyms.h>
#include <linux/err.h>
#include <linux/scatterlist.h>
#include <linux/smc.h>
#include "fmpdev_int.h" //for FIPS_FMP_FUNC_TEST macro
//#define FIPS_DEBUG
static const char *symtab[][3] =
{{".text", "first_fmp_text", "last_fmp_text" },
{".rodata", "first_fmp_rodata", "last_fmp_rodata"},
{".init.text", "first_fmp_init", "last_fmp_init" }};
extern const char *get_builtime_fmp_hmac(void);
#ifdef FIPS_DEBUG
static int
dump_bytes(const char *section_name, const char *first_symbol, const char *last_symbol)
{
u8 *start_addr = (u8 *)kallsyms_lookup_name(first_symbol);
u8 *end_addr = (u8 *)kallsyms_lookup_name(last_symbol);
if (!start_addr || !end_addr || start_addr >= end_addr) {
printk(KERN_ERR "FIPS(%s): Error Invalid Addresses in Section : %s, Start_Addr : %p , End_Addr : %p",
__FUNCTION__, section_name, start_addr, end_addr);
return -1;
}
printk(KERN_INFO "FIPS CRYPTO RUNTIME : Section - %s, %s : %p, %s : %p \n", section_name, first_symbol, start_addr, last_symbol, end_addr);
print_hex_dump_bytes("FIPS CRYPTO RUNTIME : ", DUMP_PREFIX_NONE, start_addr, end_addr - start_addr);
return 0;
}
#endif
static int query_symbol_addresses(const char *first_symbol, const char *last_symbol,
unsigned long *start_addr,unsigned long *end_addr)
{
unsigned long start = kallsyms_lookup_name(first_symbol);
unsigned long end = kallsyms_lookup_name(last_symbol);
#ifdef FIPS_DEBUG
printk(KERN_INFO "FIPS CRYPTO RUNTIME : %s : %p, %s : %p\n", first_symbol, (u8*)start, last_symbol, (u8*)end);
#endif
if (!start || !end || start >= end) {
printk(KERN_ERR "FIPS(%s): Error Invalid Addresses.", __FUNCTION__);
return -1;
}
*start_addr = start;
*end_addr = end;
return 0;
}
static int init_hash(struct hash_desc *desc)
{
struct crypto_hash *tfm = NULL;
int ret = -1;
/* Same as build time */
const unsigned char *key = "The quick brown fox jumps over the lazy dog";
tfm = crypto_alloc_hash("hmac-fmp(sha256-fmp)", 0, 0);
if (IS_ERR(tfm)) {
printk(KERN_ERR "FIPS(%s): integrity failed to allocate tfm %ld", __FUNCTION__, PTR_ERR(tfm));
return -1;
}
ret = crypto_hash_setkey (tfm, key, strlen(key));
if (ret) {
printk(KERN_ERR "FIPS(%s): fail at crypto_hash_setkey", __FUNCTION__);
return -1;
}
desc->tfm = tfm;
desc->flags = 0;
ret = crypto_hash_init(desc);
if (ret) {
printk(KERN_ERR "FIPS(%s): fail at crypto_hash_init", __FUNCTION__);
return -1;
}
return 0;
}
static int finalize_hash(struct hash_desc *desc, unsigned char *out, unsigned int out_size)
{
int ret = -1;
if (!desc || !desc->tfm || !out || !out_size) {
printk(KERN_ERR "FIPS(%s): Invalid args", __FUNCTION__);
return ret;
}
if (crypto_hash_digestsize(desc->tfm) > out_size) {
printk(KERN_ERR "FIPS(%s): Not enough space for digest", __FUNCTION__);
return ret;
}
ret = crypto_hash_final(desc, out);
if (ret) {
printk(KERN_ERR "FIPS(%s): crypto_hash_final failed", __FUNCTION__);
return -1;
}
return 0;
}
static int update_hash(struct hash_desc *desc, unsigned char *start_addr, unsigned int size)
{
struct scatterlist sg;
unsigned char *buf = NULL;
unsigned char *cur = NULL;
unsigned int bytes_remaining;
unsigned int bytes;
int ret = -1;
#if FIPS_FMP_FUNC_TEST == 5
static int total = 0;
#endif
buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!buf) {
printk(KERN_ERR "FIPS(%s): kmalloc failed", __FUNCTION__);
return ret;
}
bytes_remaining = size;
cur = start_addr;
while (bytes_remaining > 0) {
if (bytes_remaining >= PAGE_SIZE)
bytes = PAGE_SIZE;
else
bytes = bytes_remaining;
memcpy(buf, cur, bytes);
sg_init_one(&sg, buf, bytes);
#if FIPS_FMP_FUNC_TEST == 5
if (total == 0) {
printk(KERN_INFO "FIPS : Failing Integrity Test");
buf[bytes / 2] += 1;
}
#endif
ret = crypto_hash_update(desc, &sg, bytes);
if (ret) {
printk(KERN_ERR "FIPS(%s): crypto_hash_update failed", __FUNCTION__);
kfree(buf);
buf = 0;
return -1;
}
cur += bytes;
bytes_remaining -= bytes;
#if FIPS_FMP_FUNC_TEST == 5
total += bytes;
#endif
}
if (buf) {
kfree(buf);
buf = 0;
}
return 0;
}
int do_fmp_fw_integrity_check(void)
{
int err = 0;
err = exynos_smc(SMC_CMD_FMP, FMP_FW_INTEGRITY, 0, 0);
if (err) {
printk(KERN_ERR "Fail to check integrity for FMP F/W. err = 0x%x\n", err);
return -1;
}
return 0;
}
int do_fips_fmp_integrity_check(void)
{
int i, rows, err;
unsigned long start_addr = 0;
unsigned long end_addr = 0;
unsigned char runtime_hmac[32];
struct hash_desc desc;
const char *builtime_hmac = 0;
unsigned int size = 0;
err = do_fmp_fw_integrity_check();
if (err) {
printk(KERN_ERR "FIPS(%s): FMP FW Integrity Check Failed", __FUNCTION__);
return -1;
}
err = init_hash(&desc);
if (err) {
printk(KERN_ERR "FIPS(%s): init_hash failed", __FUNCTION__);
return -1;
}
rows = (unsigned int)sizeof(symtab) / sizeof(symtab[0]);
for (i = 0; i < rows; i++) {
err = query_symbol_addresses(symtab[i][1], symtab[i][2], &start_addr, &end_addr);
if (err) {
printk (KERN_ERR "FIPS(%s): Error to get start / end addresses", __FUNCTION__);
crypto_free_hash(desc.tfm);
return -1;
}
#ifdef FIPS_DEBUG
dump_bytes(symtab[i][0], symtab[i][1], symtab[i][2]);
#endif
size = end_addr - start_addr;
err = update_hash(&desc, (unsigned char *)start_addr, size);
if (err) {
printk(KERN_ERR "FIPS(%s): Error to update hash", __FUNCTION__);
crypto_free_hash(desc.tfm);
return -1;
}
}
err = finalize_hash(&desc, runtime_hmac, sizeof(runtime_hmac));
if (err) {
printk(KERN_ERR "FIPS(%s): Error in finalize", __FUNCTION__);
crypto_free_hash(desc.tfm);
return -1;
}
crypto_free_hash(desc.tfm);
builtime_hmac = get_builtime_fmp_hmac();
if (!builtime_hmac) {
printk(KERN_ERR "FIPS(%s): Unable to retrieve builtime_hmac", __FUNCTION__);
return -1;
}
#ifdef FIPS_DEBUG
print_hex_dump_bytes("FIPS CRYPTO RUNTIME : runtime hmac = ", DUMP_PREFIX_NONE, runtime_hmac, sizeof(runtime_hmac));
print_hex_dump_bytes("FIPS CRYPTO RUNTIME : builtime_hmac = ", DUMP_PREFIX_NONE, builtime_hmac , sizeof(runtime_hmac));
#endif
if (!memcmp(builtime_hmac, runtime_hmac, sizeof(runtime_hmac))) {
printk(KERN_INFO "FIPS: Integrity Check Passed");
return 0;
} else {
printk(KERN_ERR "FIPS(%s): Integrity Check Failed", __FUNCTION__);
return -1;
}
return -1;
}
EXPORT_SYMBOL_GPL(do_fips_fmp_integrity_check);