216 lines
5.1 KiB
C
Executable File
216 lines
5.1 KiB
C
Executable File
/*
|
|
* Perform FIPS Integrity test on Kernel Crypto API
|
|
*
|
|
*/
|
|
|
|
#include <linux/crypto.h>
|
|
#include <linux/kallsyms.h>
|
|
#include <linux/err.h>
|
|
#include <linux/scatterlist.h>
|
|
#include <crypto/hash.h>
|
|
#include "fips140.h"
|
|
#include "fips140_test.h"
|
|
|
|
/*
|
|
* This function return kernel offset.
|
|
* If CONFIG_RELOCATABLE_KERNEL is set
|
|
* Then the kernel will be placed into the random offset in memory
|
|
* At the build time, we write self address of the "crypto_buildtime_address"
|
|
* to the "crypto_buildtime_address" variable
|
|
*/
|
|
static __u64 get_kernel_offset(void)
|
|
{
|
|
static __u64 runtime_address = (__u64) &crypto_buildtime_address;
|
|
__u64 kernel_offset = abs(crypto_buildtime_address - runtime_address);
|
|
return kernel_offset;
|
|
}
|
|
|
|
struct sdesc {
|
|
struct shash_desc shash;
|
|
char ctx[];
|
|
};
|
|
|
|
static struct sdesc *init_hash(void)
|
|
{
|
|
struct crypto_shash *tfm = NULL;
|
|
struct sdesc *sdesc = NULL;
|
|
int size;
|
|
int ret = -1;
|
|
|
|
/* Same as build time */
|
|
const unsigned char *key = "The quick brown fox jumps over the lazy dog";
|
|
|
|
tfm = crypto_alloc_shash("hmac(sha256)", 0, 0);
|
|
|
|
if (IS_ERR(tfm)) {
|
|
pr_err("FIPS(%s): integ failed to allocate tfm %ld\n", __func__, PTR_ERR(tfm));
|
|
return ERR_PTR(-EINVAL);
|
|
}
|
|
|
|
ret = crypto_shash_setkey(tfm, key, strlen(key));
|
|
|
|
if (ret) {
|
|
pr_err("FIPS(%s): fail at crypto_hash_setkey\n", __func__);
|
|
return ERR_PTR(-EINVAL);
|
|
}
|
|
|
|
size = sizeof(struct shash_desc) + crypto_shash_descsize(tfm);
|
|
sdesc = kmalloc(size, GFP_KERNEL);
|
|
if (!sdesc)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
sdesc->shash.tfm = tfm;
|
|
sdesc->shash.flags = 0x0;
|
|
|
|
ret = crypto_shash_init(&sdesc->shash);
|
|
|
|
if (ret) {
|
|
pr_err("FIPS(%s): fail at crypto_hash_init\n", __func__);
|
|
return ERR_PTR(-EINVAL);
|
|
}
|
|
|
|
return sdesc;
|
|
}
|
|
|
|
static int
|
|
finalize_hash(struct shash_desc *desc, unsigned char *out, unsigned int out_size)
|
|
{
|
|
int ret = -1;
|
|
|
|
if (!desc || !desc->tfm || !out || !out_size) {
|
|
pr_err("FIPS(%s): Invalid args\n", __func__);
|
|
return ret;
|
|
}
|
|
|
|
if (crypto_shash_digestsize(desc->tfm) > out_size) {
|
|
pr_err("FIPS(%s): Not enough space for digest\n", __func__);
|
|
return ret;
|
|
}
|
|
|
|
ret = crypto_shash_final(desc, out);
|
|
|
|
if (ret) {
|
|
pr_err("FIPS(%s): crypto_hash_final failed\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
update_hash(struct shash_desc *desc, unsigned char *start_addr, unsigned int size)
|
|
{
|
|
int ret = -1;
|
|
|
|
ret = crypto_shash_update(desc, start_addr, size);
|
|
|
|
if (ret) {
|
|
pr_err("FIPS(%s): crypto_hash_update failed\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
#ifdef CONFIG_CRYPTO_FIPS_FUNC_TEST
|
|
if (!strcmp("integrity", get_fips_functest_mode())) {
|
|
ret = crypto_shash_update(desc, start_addr, 4);
|
|
|
|
if (ret) {
|
|
pr_err("FIPS(%s): crypto_hash_update failed\n", __func__);
|
|
return -1;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
do_integrity_check(void)
|
|
{
|
|
int i, rows;
|
|
int err = -1;
|
|
unsigned long start_addr = 0;
|
|
unsigned long end_addr = 0;
|
|
unsigned char runtime_hmac[32];
|
|
struct sdesc *sdesc = NULL;
|
|
const char *builtime_hmac = 0;
|
|
unsigned int size = 0;
|
|
#ifdef FIPS_DEBUG_INTEGRITY
|
|
unsigned int covered = 0;
|
|
unsigned int num_addresses = 0;
|
|
#endif
|
|
|
|
sdesc = init_hash();
|
|
|
|
if (IS_ERR(sdesc)) {
|
|
pr_err("FIPS(%s) : init_hash failed\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
rows = (unsigned int) ARRAY_SIZE(integrity_crypto_addrs);
|
|
|
|
for (i = 0; integrity_crypto_addrs[i].first && i < rows; i++) {
|
|
start_addr = integrity_crypto_addrs[i].first + get_kernel_offset();
|
|
end_addr = integrity_crypto_addrs[i].last + get_kernel_offset();
|
|
|
|
/* Print addresses for HMAC calculation */
|
|
#ifdef FIPS_DEBUG_INTEGRITY
|
|
pr_info("FIPS_DEBUG_INTEGRITY : first last %08llux %08llulx\n",
|
|
integrity_crypto_addrs[i].first,
|
|
integrity_crypto_addrs[i].last);
|
|
covered += (end_addr - start_addr);
|
|
#endif
|
|
|
|
size = end_addr - start_addr;
|
|
err = update_hash(&sdesc->shash, (unsigned char *)start_addr, size);
|
|
|
|
if (err) {
|
|
pr_err("FIPS(%s) : Error to update hash\n", __func__);
|
|
crypto_free_shash(sdesc->shash.tfm);
|
|
kzfree(sdesc);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* Dump bytes for HMAC */
|
|
#ifdef FIPS_DEBUG_INTEGRITY
|
|
num_addresses = i;
|
|
for (i = 0; integrity_crypto_addrs[i].first && i < rows; i++) {
|
|
|
|
start_addr = integrity_crypto_addrs[i].first + get_kernel_offset();
|
|
end_addr = integrity_crypto_addrs[i].last + get_kernel_offset();
|
|
size = end_addr - start_addr;
|
|
print_hex_dump(KERN_INFO, "FIPS_DEBUG_INTEGRITY : bytes for HMAC = ", DUMP_PREFIX_NONE,
|
|
16, 1,
|
|
(char *)start_addr, size, false);
|
|
}
|
|
#endif
|
|
|
|
err = finalize_hash(&sdesc->shash, runtime_hmac, sizeof(runtime_hmac));
|
|
|
|
crypto_free_shash(sdesc->shash.tfm);
|
|
kzfree(sdesc);
|
|
|
|
if (err) {
|
|
pr_err("FIPS(%s) : Error in finalize\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
builtime_hmac = builtime_crypto_hmac;
|
|
|
|
#ifdef FIPS_DEBUG_INTEGRITY
|
|
pr_info("FIPS_DEBUG_INTEGRITY : %d bytes are covered, Address fragments (%d)", covered, num_addresses);
|
|
print_hex_dump(KERN_INFO, "FIPS_DEBUG_INTEGRITY : runtime hmac = ", DUMP_PREFIX_NONE,
|
|
16, 1,
|
|
runtime_hmac, sizeof(runtime_hmac), false);
|
|
print_hex_dump(KERN_INFO, "FIPS_DEBUG_INTEGRITY : builtime_hmac = ", DUMP_PREFIX_NONE,
|
|
16, 1,
|
|
builtime_hmac, sizeof(runtime_hmac), false);
|
|
#endif
|
|
|
|
if (!memcmp(builtime_hmac, runtime_hmac, sizeof(runtime_hmac)))
|
|
return 0;
|
|
else
|
|
return -1;
|
|
}
|
|
EXPORT_SYMBOL_GPL(do_integrity_check);
|