From: Boojin Kim Date: Wed, 24 Jan 2018 08:03:23 +0000 (+0900) Subject: [COMMON] fmp: add fmp driver X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=e9be92a8ff36321cd502002adc970dd631cc83ee;p=GitHub%2Fmoto-9609%2Fandroid_kernel_motorola_exynos9610.git [COMMON] fmp: add fmp driver Change-Id: Idf587c60984db4d519464521cb2b96f9746ea0ba Signed-off-by: Boojin Kim --- diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig index fe33c199fc1a..d7690f6bb8ef 100644 --- a/drivers/crypto/Kconfig +++ b/drivers/crypto/Kconfig @@ -729,4 +729,11 @@ config CRYPTO_DEV_ARTPEC6 To compile this driver as a module, choose M here. +config CRYPTO_DEV_FMP + bool "FMP diskcipher engine driver" + depends on CRYPTO_DISKCIPHER && EXYNOS_FMP + default y + +source "drivers/crypto/fmp/Kconfig" + endif # CRYPTO_HW diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile index c00708d04be6..f7a77287de61 100644 --- a/drivers/crypto/Makefile +++ b/drivers/crypto/Makefile @@ -46,3 +46,7 @@ obj-$(CONFIG_CRYPTO_DEV_VMX) += vmx/ obj-$(CONFIG_CRYPTO_DEV_BCM_SPU) += bcm/ obj-$(CONFIG_CRYPTO_DEV_SAFEXCEL) += inside-secure/ obj-$(CONFIG_CRYPTO_DEV_ARTPEC6) += axis/ +obj-$(CONFIG_CRYPTO_DEV_FMP) += exynos-diskcipher.o +ifneq (,$(filter $(CONFIG_EXYNOS_SMU) $(CONFIG_EXYNOS_FMP),y)) +obj-y += fmp/ +endif diff --git a/drivers/crypto/exynos-diskcipher.c b/drivers/crypto/exynos-diskcipher.c new file mode 100644 index 000000000000..c6f113988dd4 --- /dev/null +++ b/drivers/crypto/exynos-diskcipher.c @@ -0,0 +1,191 @@ +/* + * Exynos FMP driver + * + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int fmp_crypt(struct crypto_tfm *tfm, void *priv) +{ + struct fmp_crypto_info *ci = crypto_tfm_ctx(tfm); + + return exynos_fmp_crypt(ci, priv); +} + +static int fmp_clear(struct crypto_tfm *tfm, void *priv) +{ + struct fmp_crypto_info *ci = crypto_tfm_ctx(tfm); + + return exynos_fmp_clear(ci, priv); +} + +static int fmp_setkey(struct crypto_tfm *tfm, const char *in_key, u32 keylen, + bool persistent) +{ + struct fmp_crypto_info *ci = crypto_tfm_ctx(tfm); + + return exynos_fmp_setkey(ci, (char *)in_key, keylen, persistent); +} + +static int fmp_clearkey(struct crypto_tfm *tfm) +{ + struct fmp_crypto_info *ci = crypto_tfm_ctx(tfm); + + return exynos_fmp_clearkey(ci); +} + +#ifndef CONFIG_CRYPTO_MANAGER_DISABLE_TESTS +static int fmp_do_test_crypt(struct crypto_tfm *tfm, + struct diskcipher_test_request *req) +{ + struct fmp_crypto_info *ci = crypto_tfm_ctx(tfm); + struct crypto_diskcipher *diskc = __crypto_diskcipher_cast(tfm); + + if (!req) { + pr_err("%s: invalid parameter\n", __func__); + return -EINVAL; + } + + return exynos_fmp_test_crypt(ci, req->iv, diskc->ivsize, + sg_virt(req->src), sg_virt(req->dst), + req->cryptlen, req->enc ? 1 : 0, diskc); +} +#endif + +static void *fmp_ctx; + +static inline void fmp_algo_init(struct crypto_tfm *tfm, + enum fmp_crypto_algo_mode algo) +{ + struct fmp_crypto_info *ci = crypto_tfm_ctx(tfm); + struct crypto_diskcipher *diskc = __crypto_diskcipher_cast(tfm); + + /* This field's stongly aligned 'fmp_crypto_info->use_diskc' */ + diskc->algo = (u32)algo; + diskc->ivsize = FMP_IV_SIZE_16; + ci->ctx = fmp_ctx; + ci->algo_mode = algo; +} + +static int fmp_aes_xts_init(struct crypto_tfm *tfm) +{ + fmp_algo_init(tfm, EXYNOS_FMP_ALGO_MODE_AES_XTS); + return 0; +} + +static int fmp_cbc_aes_init(struct crypto_tfm *tfm) +{ + fmp_algo_init(tfm, EXYNOS_FMP_ALGO_MODE_AES_CBC); + return 0; +} + +static struct diskcipher_alg fmp_algs[] = {{ + .base = { + .cra_name = "xts(aes)-disk", + .cra_driver_name = "xts(aes)-disk(fmp)", + .cra_priority = 200, + .cra_module = THIS_MODULE, + .cra_ctxsize = sizeof(struct fmp_crypto_info), + .cra_init = fmp_aes_xts_init, + } +}, { + .base = { + .cra_name = "cbc(aes)-disk", + .cra_driver_name = "cbc(aes)-disk(fmp)", + .cra_priority = 200, + .cra_module = THIS_MODULE, + .cra_ctxsize = sizeof(struct fmp_crypto_info), + .cra_init = fmp_cbc_aes_init, + } +} }; + +static int exynos_fmp_probe(struct platform_device *pdev) +{ + struct diskcipher_alg *alg; + int ret; + int i; + + fmp_ctx = exynos_fmp_init(pdev); + if (!fmp_ctx) { + dev_err(&pdev->dev, + "%s: Fail to register diskciphero\n", __func__); + return -EINVAL; + } + dev_set_drvdata(&pdev->dev, fmp_ctx); + + for (i = 0; i < ARRAY_SIZE(fmp_algs); i++) { + alg = &fmp_algs[i]; + alg->setkey = fmp_setkey; + alg->clearkey = fmp_clearkey; + alg->crypt = fmp_crypt; + alg->clear = fmp_clear; +#ifndef CONFIG_CRYPTO_MANAGER_DISABLE_TESTS + alg->do_crypt = fmp_do_test_crypt; +#endif + } + ret = crypto_register_diskciphers(fmp_algs, ARRAY_SIZE(fmp_algs)); + if (ret) { + dev_err(&pdev->dev, + "%s: Fail to register diskciphero. ret = %d\n", + __func__, ret); + return -EINVAL; + } + dev_info(&pdev->dev, "Exynos FMP driver is registered to diskcipher\n"); + return 0; +} + +static int exynos_fmp_remove(struct platform_device *pdev) +{ + void *drv_data = dev_get_drvdata(&pdev->dev); + + if (!drv_data) { + pr_err("%s: Fail to get drvdata\n", __func__); + return 0; + } + crypto_unregister_diskciphers(fmp_algs, ARRAY_SIZE(fmp_algs)); + exynos_fmp_exit(drv_data); + return 0; +} + +static const struct of_device_id exynos_fmp_match[] = { + {.compatible = "samsung,exynos-fmp"}, + {}, +}; + +static struct platform_driver exynos_fmp_driver = { + .driver = { + .name = "exynos-fmp", + .owner = THIS_MODULE, + .pm = NULL, + .of_match_table = exynos_fmp_match, + }, + .probe = exynos_fmp_probe, + .remove = exynos_fmp_remove, +}; + +static int __init fmp_init(void) +{ + return platform_driver_register(&exynos_fmp_driver); +} +late_initcall(fmp_init); + +static void __exit fmp_exit(void) +{ + platform_driver_unregister(&exynos_fmp_driver); +} +module_exit(fmp_exit); +MODULE_DESCRIPTION("Exynos Spedific crypto algo driver"); diff --git a/drivers/crypto/fmp/Kconfig b/drivers/crypto/fmp/Kconfig new file mode 100644 index 000000000000..4215d52fd6c7 --- /dev/null +++ b/drivers/crypto/fmp/Kconfig @@ -0,0 +1,53 @@ +# +# SMU/FMP controller drivers +# + +config EXYNOS_SMU + tristate "Samsung EXYNOS SMU driver" + help + Say yes here to build support for SMU (Secure Manangement Unit) + to control access permission to storage device. + If unsure, say N. + + To compile this driver as a module, choose M here + +config EXYNOS_FMP + tristate "Samsung EXYNOS FMP driver" + depends on CRYPTO_DISKCIPHER + help + Say yes here to build support for FMP (Flash Memory Protector) + to encrypt and decrypt userdata using inline H/W crypto module. + If unsure, say N. + + To compile this driver as a module, choose M here + +config EXYNOS_FMP_FIPS + tristate "Samsung EXYNOS FMP Validation Driver for FIPS" + depends on EXYNOS_FMP + depends on !FUNCTION_TRACER + help + Say yes here to build support for FMP (Flash Memory Protector) + FIPS validation driver for integrity checking, self and functional test. + If unsure, say N. + + To compile this driver as a module, choose M here + +choice + prompt "Option for self-test failure" + depends on EXYNOS_FMP_FIPS + default NODE_FOR_SELFTEST_FAIL + +config NODE_FOR_SELFTEST_FAIL + bool "Set fips fmp node when self-test fails" + depends on EXYNOS_FMP_FIPS + help + Choose here to select that fips fmp node was set to zero + when FMP self-test fails. + +config PANIC_FOR_SELFTEST_FAIL + bool "Panic when self-test fails" + depends on EXYNOS_FMP_FIPS + help + Choose here to select that kernel panic occurs when FMP self-test fails. + +endchoice diff --git a/drivers/crypto/fmp/Makefile b/drivers/crypto/fmp/Makefile new file mode 100644 index 000000000000..3e14d208343f --- /dev/null +++ b/drivers/crypto/fmp/Makefile @@ -0,0 +1,15 @@ +# Exynos FMP/SMU makefile +#ccflags-y := -DCONFIG_EXYNOS_FMP_FIPS_FUNC_TEST +obj-$(CONFIG_EXYNOS_SMU) += smu.o +obj-$(CONFIG_EXYNOS_FMP_FIPS) += first_file.o +obj-$(CONFIG_EXYNOS_FMP) += fmp.o fmp_test.o +CFLAGS_fmp_fips_selftest.o = -fno-merge-constants +obj-$(CONFIG_EXYNOS_FMP_FIPS) += fmp_fips_main.o fmp_fips_fops.o fmp_fips_selftest.o \ + fmp_fips_integrity.o hmac-sha256.o \ + fmp_fips_cipher.o +ifneq ($(filter y, $(CONFIG_EXYNOS_FMP) $(CONFIG_EXYNOS_FMP_FIPS)),) +obj-y += sha256.o +endif +#obj-y += fmp_fips_func_test.o +obj-$(CONFIG_EXYNOS_FMP_FIPS) += last_file.o +obj-$(CONFIG_EXYNOS_FMP_FIPS) += fips_out.o diff --git a/drivers/crypto/fmp/fips_out.c b/drivers/crypto/fmp/fips_out.c new file mode 100644 index 000000000000..9ec925803cf7 --- /dev/null +++ b/drivers/crypto/fmp/fips_out.c @@ -0,0 +1,12 @@ +#include +#include +#include "fmp_fips_integrity.h" + +__attribute__ ((section(".rodata"))) +const __s8 builtime_fmp_hmac[FIPS_HMAC_SIZE] = {0}; + +__attribute__ ((section(".rodata"))) +const struct first_last integrity_fmp_addrs[FIPS_FMP_ADDRS_SIZE] = {{0}, }; + +__attribute__ ((section(".rodata"))) +const __u64 fmp_buildtime_address = 10; diff --git a/drivers/crypto/fmp/first_file.c b/drivers/crypto/fmp/first_file.c new file mode 100644 index 000000000000..acb91db114d3 --- /dev/null +++ b/drivers/crypto/fmp/first_file.c @@ -0,0 +1,19 @@ +/* + * First file for Exynos FMP FIPS integrity check + * + * Copyright (C) 2015 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +__attribute__ ((section(".rodata"), unused)) +const unsigned char first_fmp_rodata = 0x10; + +__attribute__ ((section(".text"), unused)) +void first_fmp_text(void){} + +__attribute__ ((section(".init.text"), optimize("-O0"), unused)) +static void first_fmp_init(void){}; diff --git a/drivers/crypto/fmp/fmp.c b/drivers/crypto/fmp/fmp.c new file mode 100644 index 000000000000..2dd14999c315 --- /dev/null +++ b/drivers/crypto/fmp/fmp.c @@ -0,0 +1,605 @@ +/* + * Exynos FMP driver + * + * Copyright (C) 2015 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fmp_test.h" +#include "fmp_fips_main.h" +#include "fmp_fips_info.h" +#include "fmp_fips_func_test.h" + +#define WORD_SIZE 4 +#define FMP_IV_MAX_IDX (FMP_IV_SIZE_16 / WORD_SIZE) + +#define byte2word(b0, b1, b2, b3) \ + (((unsigned int)(b0) << 24) | \ + ((unsigned int)(b1) << 16) | \ + ((unsigned int)(b2) << 8) | (b3)) +#define get_word(x, c) byte2word(((unsigned char *)(x) + 4 * (c))[0], \ + ((unsigned char *)(x) + 4 * (c))[1], \ + ((unsigned char *)(x) + 4 * (c))[2], \ + ((unsigned char *)(x) + 4 * (c))[3]) + +static inline void dump_ci(struct fmp_crypto_info *c) +{ + if (c) { + pr_info("%s: crypto:%p algo:%d enc:%d key_size:%d key:%p\n", + __func__, c, c->algo_mode, c->enc_mode, + c->key_size, c->key); + if (c->enc_mode == EXYNOS_FMP_FILE_ENC) + print_hex_dump(KERN_CONT, "key:", + DUMP_PREFIX_OFFSET, 16, 1, c->key, + sizeof(c->key), false); + } +} + +static inline void dump_table(struct fmp_table_setting *table) +{ + print_hex_dump(KERN_CONT, "dump_table:", DUMP_PREFIX_OFFSET, 16, 1, + table, sizeof(struct fmp_table_setting), false); +} + +static inline int is_set_fmp_disk_key(struct exynos_fmp *fmp) +{ + return (fmp->status_disk_key == KEY_SET) ? TRUE : FALSE; +} + +static inline int is_stored_fmp_disk_key(struct exynos_fmp *fmp) +{ + return (fmp->status_disk_key == KEY_STORED) ? TRUE : FALSE; +} + +static inline int is_supported_ivsize(u32 ivlen) +{ + if (ivlen && (ivlen <= FMP_IV_SIZE_16)) + return TRUE; + else + return FALSE; +} + +static inline int check_aes_xts_size(struct fmp_table_setting *table, + bool cmdq_enabled) +{ + int size; + + if (cmdq_enabled) + size = GET_CMDQ_LENGTH(table); + else + size = GET_LENGTH(table); + return (size > MAX_AES_XTS_TRANSFER_SIZE) ? size : 0; +} + +/* check for fips that no allow same keys */ +static inline int check_aes_xts_key(char *key, + enum fmp_crypto_key_size key_size) +{ + char *enckey, *twkey; + + enckey = key; + twkey = key + key_size; + return (memcmp(enckey, twkey, key_size)) ? 0 : -1; +} + +int fmplib_set_algo_mode(struct fmp_table_setting *table, + struct fmp_crypto_info *crypto, bool cmdq_enabled) +{ + int ret; + enum fmp_crypto_algo_mode algo_mode = crypto->algo_mode; + + if (algo_mode == EXYNOS_FMP_ALGO_MODE_AES_XTS) { + ret = check_aes_xts_size(table, cmdq_enabled); + if (ret) { + pr_err("%s: Fail FMP XTS due to invalid size(%d)\n", + __func__, ret); + return -EINVAL; + } + } + + switch (crypto->enc_mode) { + case EXYNOS_FMP_FILE_ENC: + if (cmdq_enabled) + SET_CMDQ_FAS(table, algo_mode); + else + SET_FAS(table, algo_mode); + break; + case EXYNOS_FMP_DISK_ENC: + if (cmdq_enabled) + SET_CMDQ_DAS(table, algo_mode); + else + SET_DAS(table, algo_mode); + break; + default: + pr_err("%s: Invalid fmp enc mode %d\n", __func__, + crypto->enc_mode); + ret = -EINVAL; + break; + } + return 0; +} + +static int fmplib_set_file_key(struct fmp_table_setting *table, + struct fmp_crypto_info *crypto) +{ + enum fmp_crypto_algo_mode algo_mode = crypto->algo_mode; + enum fmp_crypto_key_size key_size = crypto->fmp_key_size; + char *key = crypto->key; + int idx, max; + + if (!key || (crypto->enc_mode != EXYNOS_FMP_FILE_ENC) || + ((key_size != EXYNOS_FMP_KEY_SIZE_16) && + (key_size != EXYNOS_FMP_KEY_SIZE_32))) { + pr_err("%s: Invalid crypto:%p key:%p key_size:%d enc_mode:%d\n", + __func__, crypto, key, key_size, crypto->enc_mode); + return -EINVAL; + } + + if ((algo_mode == EXYNOS_FMP_ALGO_MODE_AES_XTS) + && check_aes_xts_key(key, key_size)) { + pr_err("%s: Fail FMP XTS due to the same enc and twkey\n", + __func__); + return -EINVAL; + } + + if (algo_mode == EXYNOS_FMP_ALGO_MODE_AES_CBC) { + max = key_size / WORD_SIZE; + for (idx = 0; idx < max; idx++) + *(&table->file_enckey0 + idx) = + get_word(key, max - (idx + 1)); + } else if (algo_mode == EXYNOS_FMP_ALGO_MODE_AES_XTS) { + key_size *= 2; + max = key_size / WORD_SIZE; + for (idx = 0; idx < (max / 2); idx++) + *(&table->file_enckey0 + idx) = + get_word(key, (max / 2) - (idx + 1)); + for (idx = 0; idx < (max / 2); idx++) + *(&table->file_twkey0 + idx) = + get_word(key, max - (idx + 1)); + } + return 0; +} + +static int fmplib_set_key_size(struct fmp_table_setting *table, + struct fmp_crypto_info *crypto, bool cmdq_enabled) +{ + enum fmp_crypto_key_size key_size; + + key_size = crypto->fmp_key_size; + + if ((key_size != EXYNOS_FMP_KEY_SIZE_16) && + (key_size != EXYNOS_FMP_KEY_SIZE_32)) { + pr_err("%s: Invalid keysize %d\n", __func__, key_size); + return -EINVAL; + } + + switch (crypto->enc_mode) { + case EXYNOS_FMP_FILE_ENC: + if (cmdq_enabled) + SET_CMDQ_KEYLEN(table, + (key_size == + FMP_KEY_SIZE_32) ? FKL_CMDQ : 0); + else + SET_KEYLEN(table, + (key_size == FMP_KEY_SIZE_32) ? FKL : 0); + break; + case EXYNOS_FMP_DISK_ENC: + if (cmdq_enabled) + SET_CMDQ_KEYLEN(table, + (key_size == + FMP_KEY_SIZE_32) ? DKL_CMDQ : 0); + else + SET_KEYLEN(table, + (key_size == FMP_KEY_SIZE_32) ? DKL : 0); + break; + default: + pr_err("%s: Invalid fmp enc mode %d\n", __func__, + crypto->enc_mode); + return -EINVAL; + } + return 0; +} + +static int fmplib_set_disk_key(struct exynos_fmp *fmp, u8 *key, u32 key_size) +{ + int ret; + + __flush_dcache_area(key, (size_t) FMP_MAX_KEY_SIZE); + ret = + exynos_smc(SMC_CMD_FMP_DISK_KEY_STORED, 0, virt_to_phys(key), + key_size); + if (ret) { + pr_err("%s: Fail to set FMP disk key. ret = %d\n", __func__, + ret); + fmp->status_disk_key = KEY_ERROR; + return -EINVAL; + } + fmp->status_disk_key = KEY_STORED; + return 0; +} + +static int fmplib_clear_disk_key(struct exynos_fmp *fmp) +{ + int ret; + + ret = exynos_smc(SMC_CMD_FMP_DISK_KEY_CLEAR, 0, 0, 0); + if (ret) { + pr_err("%s: Fail to clear FMP disk key. ret = %d\n", + __func__, ret); + fmp->status_disk_key = KEY_ERROR; + return -EPERM; + } + + fmp->status_disk_key = KEY_CLEAR; + return 0; +} + +static int fmplib_set_iv(struct fmp_table_setting *table, + struct fmp_crypto_info *crypto, u8 *iv) +{ + int idx; + + switch (crypto->enc_mode) { + case EXYNOS_FMP_FILE_ENC: + for (idx = 0; idx < FMP_IV_MAX_IDX; idx++) + *(&table->file_iv0 + idx) = + get_word(iv, FMP_IV_MAX_IDX - (idx + 1)); + break; + case EXYNOS_FMP_DISK_ENC: + for (idx = 0; idx < FMP_IV_MAX_IDX; idx++) + *(&table->disk_iv0 + idx) = + get_word(iv, FMP_IV_MAX_IDX - (idx + 1)); + break; + default: + pr_err("%s: Invalid fmp enc mode %d\n", __func__, + crypto->enc_mode); + return -EINVAL; + } + return 0; +} + +int exynos_fmp_crypt(struct fmp_crypto_info *ci, void *priv) +{ + struct exynos_fmp *fmp = ci->ctx; + struct fmp_request *r = priv; + int ret = 0; + u8 iv[FMP_IV_SIZE_16]; + bool test_mode = 0; + + if (!r || !fmp) { + pr_err("%s: invalid req:%p, fmp:%p\n", __func__, r, fmp); + return -EINVAL; + } + + if (unlikely(in_fmp_fips_err())) { + pr_err("%s: Fail to work fmp config due to fips in error.\n", + __func__); + return -EINVAL; + } + + /* check test mode */ + if (ci->algo_mode & EXYNOS_FMP_ALGO_MODE_TEST) { + ci->algo_mode &= EXYNOS_FMP_ALGO_MODE_MASK; + if (!ci->algo_mode) + return 0; + + if (!fmp->test_data) { + pr_err("%s: no test_data for test mode\n", __func__); + goto out; + } + test_mode = 1; + /* use test manager's iv instead of host driver's iv */ + r->iv = fmp->test_data->iv; + r->ivsize = sizeof(fmp->test_data->iv); + } + + /* check crypto info & input param */ + if (!ci->algo_mode || !is_supported_ivsize(r->ivsize) || + !r->table || !r->iv) { + dev_err(fmp->dev, + "%s: invalid mode:%d iv:%p ivsize:%d table:%p\n", + __func__, ci->algo_mode, r->iv, r->ivsize, r->table); + ret = -EINVAL; + goto out; + } + + /* set algo & enc mode into table */ + ret = fmplib_set_algo_mode(r->table, ci, r->cmdq_enabled); + if (ret) { + dev_err(fmp->dev, "%s: Fail to set FMP encryption mode\n", + __func__); + ret = -EINVAL; + goto out; + } + + /* set key size into table */ + switch (ci->enc_mode) { + case EXYNOS_FMP_FILE_ENC: + ret = fmplib_set_file_key(r->table, ci); + if (ret) { + dev_err(fmp->dev, "%s: Fail to set FMP key\n", + __func__); + ret = -EINVAL; + goto out; + } + break; + case EXYNOS_FMP_DISK_ENC: + if (is_stored_fmp_disk_key(fmp)) { + exynos_smc(SMC_CMD_FMP_DISK_KEY_SET, 0, 0, 0); + fmp->status_disk_key = KEY_SET; + } else if (!is_set_fmp_disk_key(fmp)) { + dev_err(fmp->dev, + "%s: Cannot configure FMP because disk key is clear\n", + __func__); + ret = -EINVAL; + goto out; + } + break; + default: + dev_err(fmp->dev, "%s: Invalid fmp enc mode %d\n", __func__, + ci->enc_mode); + ret = -EINVAL; + goto out; + } + + /* set key size into table */ + ret = fmplib_set_key_size(r->table, ci, r->cmdq_enabled); + if (ret) { + dev_err(fmp->dev, "%s: Fail to set FMP key size\n", __func__); + ret = -EINVAL; + goto out; + } + + /* set iv */ + memset(iv, 0, FMP_IV_SIZE_16); + memcpy(iv, r->iv, r->ivsize); + ret = fmplib_set_iv(r->table, ci, iv); + if (ret) { + dev_err(fmp->dev, "%s: Fail to set FMP IV\n", __func__); + ret = -EINVAL; + goto out; + } +out: + if (ret) { + dump_ci(ci); + if (r && r->table) + dump_table(r->table); + } + return ret; +} + +static inline void fmplib_clear_file_key(struct fmp_table_setting *table) +{ + memset(&table->file_iv0, 0, sizeof(__le32) * 24); +} + +int exynos_fmp_clear(struct fmp_crypto_info *ci, void *priv) +{ + struct fmp_request *r = priv; +#ifdef CONFIG_EXYNOS_FMP_FIPS + struct exynos_fmp *fmp = ci->ctx; + struct exynos_fmp_fips_test_vops *test_vops = fmp->test_vops; + int ret; +#endif + + if (!r) { + pr_err("%s: Invalid input\n", __func__); + return -EINVAL; + } + + if (!r->table) { + pr_err("%s: Invalid input table\n", __func__); + return -EINVAL; + } + +#ifdef CONFIG_EXYNOS_FMP_FIPS + if (test_vops) { + ret = test_vops->zeroization(r->table, "before"); + if (ret) + dev_err(fmp->dev, + "%s: Fail to check zeroization(before)\n", + __func__); + } +#endif + fmplib_clear_file_key(r->table); + +#ifdef CONFIG_EXYNOS_FMP_FIPS + if (test_vops) { + ret = test_vops->zeroization(r->table, "after"); + if (ret) + dev_err(fmp->dev, + "%s: Fail to check zeroization(after)\n", + __func__); + } +#endif + + return 0; +} + +int exynos_fmp_setkey(struct fmp_crypto_info *ci, char *in_key, u32 keylen, + bool persistent) +{ + struct exynos_fmp *fmp = ci->ctx; + int ret = 0; + int keylen_org = keylen; + + if (!fmp || !in_key) { + pr_err("%s: invalid input param\n", __func__); + return -EINVAL; + } + + /* set key_size & fmp_key_size */ + if (ci->algo_mode == EXYNOS_FMP_ALGO_MODE_AES_XTS) + keylen = keylen >> 1; + switch (keylen) { + case FMP_KEY_SIZE_16: + ci->fmp_key_size = EXYNOS_FMP_KEY_SIZE_16; + break; + case FMP_KEY_SIZE_32: + ci->fmp_key_size = EXYNOS_FMP_KEY_SIZE_32; + break; + default: + pr_err("%s: FMP doesn't support key size %d\n", __func__, + keylen); + return -ENOKEY; + } + ci->key_size = keylen_org; + + /* set key */ + if (persistent) { + ci->enc_mode = EXYNOS_FMP_DISK_ENC; + ret = fmplib_set_disk_key(fmp, in_key, ci->key_size); + if (ret) + pr_err("%s: Fail to set FMP disk key\n", __func__); + } else { + ci->enc_mode = EXYNOS_FMP_FILE_ENC; + memset(ci->key, 0, sizeof(ci->key)); + memcpy(ci->key, in_key, ci->key_size); + } + return ret; +} + +int exynos_fmp_clearkey(struct fmp_crypto_info *ci) +{ + struct exynos_fmp *fmp = ci->ctx; + int ret = 0; + + if (!fmp) { + pr_err("%s: invalid input param\n", __func__); + ret = -EINVAL; + goto out; + } + + if (ci->enc_mode == EXYNOS_FMP_DISK_ENC) { + ret = fmplib_clear_disk_key(fmp); + if (ret) + pr_err("%s: fail to clear FMP disk key\n", __func__); + } else if (ci->enc_mode == EXYNOS_FMP_FILE_ENC) { + memset(ci->key, 0, sizeof(ci->key)); + ci->key_size = 0; + } else { + pr_err("%s: invalid algo mode:%d\n", __func__, ci->enc_mode); + ret = -EINVAL; + } +out: + return ret; +} + +#ifndef CONFIG_CRYPTO_MANAGER_DISABLE_TESTS +int exynos_fmp_test_crypt(struct fmp_crypto_info *ci, + const uint8_t *iv, uint32_t ivlen, uint8_t *src, + uint8_t *dst, uint32_t len, bool enc, void *priv) +{ + struct exynos_fmp *fmp = ci->ctx; + int ret = 0; + + if (!fmp || !iv || !src || !dst) { + pr_err("%s: invalid input: fmp:%p, iv:%p, s:%p, d:%p\n", + __func__, fmp, iv, src, dst); + return -EINVAL; + } + + /* init fips test to get test block */ + fmp->test_data = fmp_test_init(fmp); + if (!fmp->test_data) { + dev_err(fmp->dev, "%s: fail to initialize fmp test.", + __func__); + goto err; + } + + /* setiv */ + if (iv && (ivlen <= FMP_IV_SIZE_16)) { + memset(fmp->test_data->iv, 0, FMP_IV_SIZE_16); + memcpy(fmp->test_data->iv, iv, ivlen); + } else { + dev_err(fmp->dev, "%s: fail to set fmp iv. ret(%d)", + __func__, ret); + goto err; + } + + /* do crypt: priv: struct crypto_diskcipher */ + ret = fmp_test_crypt(fmp, fmp->test_data, + src, dst, len, enc ? ENCRYPT : DECRYPT, priv, ci); + if (ret) + dev_err(fmp->dev, "%s: fail to run fmp test. ret(%d)", + __func__, ret); + +err: + if (fmp->test_data) + fmp_test_exit(fmp->test_data); + return ret; +} +#endif + +void *exynos_fmp_init(struct platform_device *pdev) +{ + int ret = 0; + struct exynos_fmp *fmp; + + if (!pdev) { + pr_err("%s: Invalid platform_device.\n", __func__); + return NULL; + } + + fmp = devm_kzalloc(&pdev->dev, sizeof(struct exynos_fmp), GFP_KERNEL); + if (!fmp) + return NULL; + + fmp->dev = &pdev->dev; + if (!fmp->dev) { + pr_err("%s: Invalid device.\n", __func__); + goto err_dev; + } + + /* init disk key status */ + fmp->status_disk_key = KEY_CLEAR; + + dev_info(fmp->dev, "Exynos FMP Version: %s\n", FMP_DRV_VERSION); + +#ifdef CONFIG_EXYNOS_FMP_FIPS + ret = exynos_fmp_func_test_KAT_case(pdev, fmp); + if (ret) { + dev_err(fmp->dev, "%s: Fail to test KAT case. ret(%d)", + __func__, ret); + goto err_dev; + } +#endif + + /* init fips */ + ret = exynos_fmp_fips_init(fmp); + if (ret) { + dev_err(fmp->dev, "%s: Fail to initialize fmp fips. ret(%d)", + __func__, ret); + exynos_fmp_fips_exit(fmp); + goto err_dev; + } + + dev_info(fmp->dev, "Exynos FMP driver is initialized\n"); + return fmp; + +err_dev: + kzfree(fmp); + return NULL; +} + +void exynos_fmp_exit(struct exynos_fmp *fmp) +{ + exynos_fmp_fips_exit(fmp); + kzfree(fmp); +} diff --git a/drivers/crypto/fmp/fmp_fips_cipher.c b/drivers/crypto/fmp/fmp_fips_cipher.c new file mode 100644 index 000000000000..21d5b42cfea3 --- /dev/null +++ b/drivers/crypto/fmp/fmp_fips_cipher.c @@ -0,0 +1,91 @@ +/* + * Exynos FMP cipher driver + * + * Copyright (C) 2016 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include "fmp_fips_info.h" + +int fmp_cipher_set_key(struct fmp_test_data *fdata, + uint8_t *key, uint32_t key_len) +{ + int ret = 0; + struct fmp_crypto_info *ci; + + if (!fdata) + return -EINVAL; + + ci = &fdata->ci; + memset(ci->key, 0, FMP_MAX_KEY_SIZE); + ci->key_size = key_len; + + if (ci->algo_mode == EXYNOS_FMP_ALGO_MODE_AES_CBC) { + switch (key_len) { + case FMP_KEY_SIZE_16: + ci->fmp_key_size = EXYNOS_FMP_KEY_SIZE_16; + break; + case FMP_KEY_SIZE_32: + ci->fmp_key_size = EXYNOS_FMP_KEY_SIZE_32; + break; + default: + pr_err("%s: Invalid FMP CBC key size(%d)\n", + __func__, key_len); + ret = -EINVAL; + goto err; + } + memcpy(ci->key, key, key_len); + } else if (ci->algo_mode == EXYNOS_FMP_ALGO_MODE_AES_XTS) { + switch (key_len >> 1) { + case FMP_KEY_SIZE_16: + ci->fmp_key_size = EXYNOS_FMP_KEY_SIZE_16; + break; + case FMP_KEY_SIZE_32: + ci->fmp_key_size = EXYNOS_FMP_KEY_SIZE_32; + break; + default: + pr_err("%s: Invalid FMP XTS key size(%d)\n", + __func__, key_len); + ret = -EINVAL; + goto err; + } + memcpy(ci->key, key, key_len); + } else { + pr_err("%s: Invalid FMP encryption mode(%d)\n", + __func__, ci->algo_mode); + ret = -EINVAL; + goto err; + } +err: + return ret; +} + +int fmp_cipher_set_iv(struct fmp_test_data *fdata, + uint8_t *iv, uint32_t iv_len) +{ + int ret = 0; + struct fmp_crypto_info *ci; + + if (!fdata) + return -EINVAL; + + ci = &fdata->ci; + if (iv_len != FMP_IV_SIZE_16) { + pr_err("%s: Invalid FMP iv size(%d)\n", __func__, iv_len); + ret = -EINVAL; + goto err; + } + + memset(fdata->iv, 0, FMP_IV_SIZE_16); + memcpy(fdata->iv, iv, iv_len); + +err: + return ret; +} diff --git a/drivers/crypto/fmp/fmp_fips_cipher.h b/drivers/crypto/fmp/fmp_fips_cipher.h new file mode 100644 index 000000000000..39b2dc397381 --- /dev/null +++ b/drivers/crypto/fmp/fmp_fips_cipher.h @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2016 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef _FMP_FIPS_CIPHER_H_ +#define _FMP_FIPS_CIPHER_H_ +int fmp_cipher_set_key(struct fmp_test_data *fdata, + uint8_t *key, uint32_t key_len); +int fmp_cipher_set_iv(struct fmp_test_data *fdata, + uint8_t *iv, uint32_t iv_len); +#endif diff --git a/drivers/crypto/fmp/fmp_fips_fops.c b/drivers/crypto/fmp/fmp_fips_fops.c new file mode 100644 index 000000000000..7687783e8498 --- /dev/null +++ b/drivers/crypto/fmp/fmp_fips_fops.c @@ -0,0 +1,1424 @@ +/* + * Exynos FMP func test driver + * + * Copyright (C) 2015 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "fmp_fips_main.h" +#include "fmp_fips_info.h" +#include "fmp_fips_cipher.h" +#include "sha256.h" +#include "hmac-sha256.h" +#include "fmp_test.h" + +#define INIT 0 +#define UPDATE 1 +#define FINAL 2 + +/* ====== Compile-time config ====== */ + +/* + * Default (pre-allocated) and maximum size of the job queue. + * These are free, pending and done items all together. + */ +#define DEF_COP_RINGSIZE 16 +#define MAX_COP_RINGSIZE 64 + +static int fmp_fips_set_key(struct exynos_fmp *fmp, struct fmp_fips_info *info, + uint8_t *enckey, uint8_t *twkey, uint32_t key_len) +{ + int ret = 0; + uint8_t *key; + + if (twkey) { + key = kzalloc(key_len * 2, GFP_KERNEL); + if (!key) + return -ENOMEM; + memcpy(key, enckey, key_len); + memcpy(key + key_len, twkey, key_len); + key_len *= 2; + } else { + key = kzalloc(key_len, GFP_KERNEL); + if (!key) + return -ENOMEM; + memcpy(key, enckey, key_len); + } + + ret = fmp_cipher_set_key(info->data, key, key_len); + if (ret) + dev_err(fmp->dev, "%s: Fail to set key. ret(%d)\n", + __func__, ret); + kzfree(key); + return ret; +} + +static int fmp_fips_cipher_init(struct fmp_fips_info *info, + struct cipher_data *out, const char *alg_name, + uint8_t *enckey, uint8_t *twkey, size_t key_len) +{ + int ret = 0; + struct exynos_fmp *fmp; + + if (!info || !info->fmp) { + pr_err("%s: Invalid fmp info\n", __func__); + return -ENODEV; + } + fmp = info->fmp; + + memset(out, 0, sizeof(*out)); + + if (!strcmp(alg_name, "cbc(aes-fmp)")) + info->data->ci.algo_mode = EXYNOS_FMP_ALGO_MODE_AES_CBC; + else if (!strcmp(alg_name, "xts(aes-fmp)")) + info->data->ci.algo_mode = EXYNOS_FMP_ALGO_MODE_AES_XTS; + else { + dev_err(fmp->dev, "%s: Invalid mode\n", __func__); + ret = -EINVAL; + goto err; + } + if (ret) { + dev_err(fmp->dev, "%s: Fail to init fmp cipher\n", __func__); + goto err; + } + + out->blocksize = 16; + out->ivsize = 16; + ret = fmp_fips_set_key(fmp, info, enckey, twkey, key_len); + if (ret) { + dev_err(fmp->dev, "%s: Fail to set fmp key\n", __func__); + goto err; + } + + out->init = 1; +err: + return ret; +} + +static void fmp_fips_cipher_deinit(struct cipher_data *cdata) +{ + if (cdata->init) + cdata->init = 0; +} + +static int fmp_fips_hash_init(struct fmp_fips_info *info, struct hash_data *hdata, + const char *alg_name, + int hmac_mode, void *mackey, size_t mackeylen) +{ + int ret = -ENOMSG; + struct exynos_fmp *fmp; + + if (!info || !info->fmp) { + pr_err("%s: Invalid fmp info\n", __func__); + return -ENODEV; + } + fmp = info->fmp; + + hdata->init = 0; + + switch (hmac_mode) { + case 0: + hdata->sha = kzalloc(sizeof(*hdata->sha), GFP_KERNEL); + if (!hdata->sha) + return -ENOMEM; + + ret = sha256_init(hdata->sha); + break; + case 1: + hdata->hmac = kzalloc(sizeof(*hdata->hmac), GFP_KERNEL); + if (!hdata->hmac) + return -ENOMEM; + + ret = hmac_sha256_init(hdata->hmac, mackey, mackeylen); + break; + default: + dev_err(fmp->dev, "Wrong mode\n"); + return ret; + } + + if (ret == 0) + hdata->init = 1; + + return ret; +} + +static void fmp_fips_hash_deinit(struct hash_data *hdata) +{ + if (hdata->hmac != NULL) { + hmac_sha256_ctx_cleanup(hdata->hmac); + kzfree(hdata->hmac); + return; + } + + if (hdata->sha != NULL) { + memset(hdata->sha, 0x00, sizeof(*hdata->sha)); + kzfree(hdata->sha); + return; + } +} + +static ssize_t fmp_fips_hash_update(struct exynos_fmp *fmp, + struct hash_data *hdata, + struct scatterlist *sg, size_t len) +{ + int ret = -ENOMSG; + int8_t *buf; + + buf = kmalloc(len, GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto exit; + } + + if (len != sg_copy_to_buffer(sg, 1, buf, len)) { + dev_err(fmp->dev, "%s: sg_copy_buffer error copying\n", __func__); + goto exit; + } + + if (hdata->sha != NULL) + ret = sha256_update(hdata->sha, buf, len); + else + ret = hmac_sha256_update(hdata->hmac, buf, len); + +exit: + kzfree(buf); + return ret; +} + +static int fmp_fips_hash_final(struct exynos_fmp *fmp, + struct hash_data *hdata, void *output) +{ + int ret_crypto = 0; /* OK if zero */ + + if (hdata->sha != NULL) + ret_crypto = sha256_final(hdata->sha, output); + else + ret_crypto = hmac_sha256_final(hdata->hmac, output); + + if (ret_crypto != 0) + return -ENOMSG; + + hdata->digestsize = 32; + return 0; +} + +static int fmp_n_crypt(struct fmp_fips_info *info, struct csession *ses_ptr, + struct crypt_op *cop, + struct scatterlist *src_sg, struct scatterlist *dst_sg, + uint32_t len) +{ + int ret; + struct exynos_fmp *fmp; + + if (!info || !info->fmp) { + pr_err("%s: Invalid fmp info\n", __func__); + return -ENODEV; + } + fmp = info->fmp; + + if (cop->op == COP_ENCRYPT) { + if (ses_ptr->hdata.init != 0) { + ret = fmp_fips_hash_update(fmp, &ses_ptr->hdata, + src_sg, len); + if (unlikely(ret)) + goto out_err; + } + + if (ses_ptr->cdata.init != 0) { + if (info->data) { + ret = fmp_test_crypt(fmp, info->data, + sg_virt(src_sg), sg_virt(dst_sg), len, + ENCRYPT, NULL, &info->data->ci); + if (unlikely(ret)) + goto out_err; + } else { + dev_err(fmp->dev, + "%s: no crypt for enc\n", __func__); + ret = -EINVAL; + } + } + } else { + if (ses_ptr->cdata.init != 0) { + if (info->data) { + ret = fmp_test_crypt(fmp, info->data, + sg_virt(src_sg), sg_virt(dst_sg), len, + DECRYPT, NULL, &info->data->ci); + if (unlikely(ret)) + goto out_err; + } else { + dev_err(fmp->dev, + "%s: no crypt for dec\n", __func__); + ret = -EINVAL; + } + } + + if (ses_ptr->hdata.init != 0) { + ret = fmp_fips_hash_update(fmp, &ses_ptr->hdata, + dst_sg, len); + if (unlikely(ret)) + goto out_err; + } + } + + return 0; +out_err: + dev_err(fmp->dev, "%s: FMP crypt failure: %d\n", __func__, ret); + return ret; +} + +static int __fmp_run_std(struct fmp_fips_info *info, + struct csession *ses_ptr, struct crypt_op *cop) +{ + char *data; + struct exynos_fmp *fmp; + char __user *src, *dst; + size_t nbytes, bufsize; + struct scatterlist sg; + int ret = 0; + + if (!info || !info->fmp) { + pr_err("%s: Invalid fmp info\n", __func__); + return -ENODEV; + } + fmp = info->fmp; + + nbytes = cop->len; + data = (char *)__get_free_page(GFP_KERNEL); + if (unlikely(!data)) { + dev_err(fmp->dev, "Error getting free page.\n"); + return -ENOMEM; + } + + bufsize = (nbytes > PAGE_SIZE) ? PAGE_SIZE : nbytes; + + src = cop->src; + dst = cop->dst; + + while (nbytes > 0) { + size_t current_len = nbytes > bufsize ? bufsize : nbytes; + + if (unlikely(copy_from_user(data, src, current_len))) { + dev_err(fmp->dev, "Error copying %d bytes from user address %p\n", + (int)current_len, src); + ret = -EFAULT; + break; + } + + sg_init_one(&sg, data, current_len); + ret = fmp_n_crypt(info, ses_ptr, cop, &sg, &sg, current_len); + if (unlikely(ret)) { + dev_err(fmp->dev, "fmp_n_crypt failed\n"); + break; + } + + if (ses_ptr->cdata.init != 0) { + if (unlikely(copy_to_user(dst, data, current_len))) { + dev_err(fmp->dev, "could not copy to user\n"); + ret = -EFAULT; + break; + } + } + + dst += current_len; + nbytes -= current_len; + src += current_len; + } + free_page((unsigned long)data); + + return ret; +} + +static int fmp_run(struct fmp_fips_info *info, struct fcrypt *fcr, + struct kernel_crypt_op *kcop) +{ + struct exynos_fmp *fmp; + struct csession *ses_ptr; + struct crypt_op *cop = &kcop->cop; + int ret = -EINVAL; + + if (!info || !info->fmp) { + pr_err("%s: Invalid fmp info\n", __func__); + return -ENODEV; + } + fmp = info->fmp; + + if (unlikely(cop->op != COP_ENCRYPT && cop->op != COP_DECRYPT)) { + dev_err(fmp->dev, "invalid operation op=%u\n", cop->op); + return -EINVAL; + } + + /* this also enters ses_ptr->sem */ + ses_ptr = fmp_get_session_by_sid(fcr, cop->ses); + if (unlikely(!ses_ptr)) { + dev_err(fmp->dev, "invalid session ID=0x%08X\n", cop->ses); + return -EINVAL; + } + + if ((ses_ptr->cdata.init != 0) && (cop->len > PAGE_SIZE)) { + dev_err(fmp->dev, "Invalid input length. len = %d\n", cop->len); + return -EINVAL; + } + + if (ses_ptr->cdata.init != 0) { + int blocksize = ses_ptr->cdata.blocksize; + + if (unlikely(cop->len % blocksize)) { + dev_err(fmp->dev, + "data size (%u) isn't a multiple of block size(%u)\n", + cop->len, blocksize); + ret = -EINVAL; + goto out_unlock; + } + + if (cop->flags == COP_FLAG_AES_CBC) + ret = fmp_cipher_set_iv(info->data, kcop->iv, 16); + else if (cop->flags == COP_FLAG_AES_XTS) + ret = fmp_cipher_set_iv(info->data, + (uint8_t *)&cop->data_unit_seqnumber, 16); + else + ret = -EINVAL; + + if (unlikely(ret)) { + dev_err(fmp->dev, "%s: set_iv failed\n", __func__); + goto out_unlock; + } + } + + if (likely(cop->len)) { + ret = __fmp_run_std(info, ses_ptr, &kcop->cop); + if (unlikely(ret)) + goto out_unlock; + } + + if (ses_ptr->hdata.init != 0 && + ((cop->flags & COP_FLAG_FINAL) || + (!(cop->flags & COP_FLAG_UPDATE) || cop->len == 0))) { + ret = fmp_fips_hash_final(fmp, &ses_ptr->hdata, kcop->hash_output); + if (unlikely(ret)) { + dev_err(fmp->dev, "CryptoAPI failure: %d\n", ret); + goto out_unlock; + } + kcop->digestsize = ses_ptr->hdata.digestsize; + } + +out_unlock: + fmp_put_session(ses_ptr); + return ret; +} + +static int fmp_run_AES_CBC_MCT(struct fmp_fips_info *info, struct fcrypt *fcr, + struct kernel_crypt_op *kcop) +{ + struct csession *ses_ptr; + struct crypt_op *cop = &kcop->cop; + char **Ct = 0; + char **Pt = 0; + int ret = 0, k = 0; + struct exynos_fmp *fmp; + char *data = NULL; + char __user *src, *dst, *secondLast; + struct scatterlist sg; + size_t nbytes, bufsize; + int y = 0; + + if (!info || !info->fmp) { + pr_err("%s: Invalid fmp info\n", __func__); + return -ENODEV; + } + fmp = info->fmp; + + if (unlikely(cop->op != COP_ENCRYPT && cop->op != COP_DECRYPT)) { + dev_err(fmp->dev, "invalid operation op=%u\n", cop->op); + return -EINVAL; + } + + /* this also enters ses_ptr->sem */ + ses_ptr = fmp_get_session_by_sid(fcr, cop->ses); + if (unlikely(!ses_ptr)) { + dev_err(fmp->dev, "invalid session ID=0x%08X\n", cop->ses); + return -EINVAL; + } + + if (cop->len > PAGE_SIZE) { + dev_err(fmp->dev, "Invalid input length. len = %d\n", cop->len); + return -EINVAL; + } + + if (ses_ptr->cdata.init != 0) { + int blocksize = ses_ptr->cdata.blocksize; + + if (unlikely(cop->len % blocksize)) { + dev_err(fmp->dev, + "data size (%u) isn't a multiple of block size(%u)\n", + cop->len, blocksize); + ret = -EINVAL; + goto out_unlock; + } + + ret = fmp_cipher_set_iv(info->data, kcop->iv, 16); + if (unlikely(ret)) { + dev_err(fmp->dev, "%s: set_iv failed\n", __func__); + goto out_unlock; + } + } + + if (!cop->len || !(cop->flags & COP_FLAG_AES_CBC_MCT)) { + ret = -EINVAL; + goto out_unlock; + } + + nbytes = cop->len; + data = (char *)__get_free_page(GFP_KERNEL); + if (unlikely(!data)) { + ret = -ENOMEM; + goto out_unlock; + } + + Pt = kzalloc(1000 * sizeof(char *), GFP_KERNEL); + if (!Pt) { + ret = -ENOMEM; + goto out_err_mem_pt; + } + + for (k = 0; k < 1000; k++) { + Pt[k] = kzalloc(nbytes, GFP_KERNEL); + if (!Pt[k]) { + ret = -ENOMEM; + goto out_err_mem_pt_k; + } + } + + Ct = kzalloc(1000 * sizeof(char *), GFP_KERNEL); + if (!Ct) { + ret = -ENOMEM; + goto out_err_mem_ct; + } + + for (k = 0; k < 1000; k++) { + Ct[k] = kzalloc(nbytes, GFP_KERNEL); + if (!Ct[k]) { + ret = -ENOMEM; + goto out_err_mem_ct_k; + } + } + + bufsize = (nbytes > PAGE_SIZE) ? PAGE_SIZE : nbytes; + + src = cop->src; + dst = cop->dst; + secondLast = cop->secondLastEncodedData; + + if (unlikely(copy_from_user(data, src, nbytes))) { + dev_err(fmp->dev, + "Error copying %d bytes from user address %p.\n", + (int)nbytes, src); + ret = -EFAULT; + goto out_err_fail; + } + + sg_init_one(&sg, data, nbytes); + for (y = 0; y < 1000; y++) { + memcpy(Pt[y], data, nbytes); + ret = fmp_n_crypt(info, ses_ptr, cop, &sg, &sg, nbytes); + memcpy(Ct[y], data, nbytes); + + if (!memcmp(Pt[y], Ct[y], nbytes)) + dev_warn(fmp->dev, + "plaintext and ciphertext is the same\n"); + + if (y == 998) { + if (unlikely(copy_to_user(secondLast, data, nbytes))) + dev_err(fmp->dev, + "unable to copy second last data for AES_CBC_MCT\n"); + } + + if (y == 0) + memcpy(data, kcop->iv, kcop->ivlen); + else { + if (y != 999) + memcpy(data, Ct[y-1], nbytes); + } + + if (unlikely(ret)) { + dev_err(fmp->dev, "fmp_n_crypt failed.\n"); + ret = -EFAULT; + goto out_err_fail; + } + + if (cop->op == COP_ENCRYPT) + ret = fmp_cipher_set_iv(info->data, Ct[y], 16); + else if (cop->op == COP_DECRYPT) + ret = fmp_cipher_set_iv(info->data, Pt[y], 16); + else + ret = -EINVAL; + if (unlikely(ret)) { + dev_err(fmp->dev, "%s: set_iv failed\n", __func__); + goto out_unlock; + } + } + + if (ses_ptr->cdata.init != 0) { + if (unlikely(copy_to_user(dst, data, nbytes))) { + dev_err(fmp->dev, "could not copy to user.\n"); + ret = -EFAULT; + goto out_err_fail; + } + } + +out_err_fail: + for (k = 0; k < 1000; k++) + kzfree(Ct[k]); +out_err_mem_ct_k: + kzfree(Ct); + +out_err_mem_ct: + for (k = 0; k < 1000; k++) + kzfree(Pt[k]); +out_err_mem_pt_k: + kzfree(Pt); +out_err_mem_pt: + free_page((unsigned long)data); +out_unlock: + fmp_put_session(ses_ptr); + + return ret; +} + +static int fmp_create_session(struct fmp_fips_info *info, + struct fcrypt *fcr, struct session_op *sop) +{ + int ret = 0; + struct exynos_fmp *fmp; + struct csession *ses_new = NULL, *ses_ptr; + const char *alg_name = NULL; + const char *hash_name = NULL; + int hmac_mode = 1; + + /* + * With composite aead ciphers, only ckey is used and it can cover all the + * structure space; otherwise both keys may be used simultaneously but they + * are confined to their spaces + */ + struct { + uint8_t mkey[FMP_HMAC_MAX_KEY_LEN]; + } keys; + + if (!info || !info->fmp) { + pr_err("%s: Invalid fmp info\n", __func__); + return -ENODEV; + } + fmp = info->fmp; + + if (unlikely(!sop->cipher && !sop->mac)) { + dev_err(fmp->dev, "Both 'cipher' and 'mac' unset.\n"); + return -EINVAL; + } + + switch (sop->cipher) { + case 0: + break; + case FMP_AES_CBC: + alg_name = "cbc(aes-fmp)"; + break; + case FMP_AES_XTS: + alg_name = "xts(aes-fmp)"; + break; + default: + dev_err(fmp->dev, "Invalid cipher : %d\n", sop->cipher); + return -EINVAL; + } + + switch (sop->mac) { + case 0: + break; + case FMP_SHA2_256_HMAC: + hash_name = "hmac-fmp(sha256-fmp)"; + break; + case FMP_SHA2_256: + hash_name = "sha256-fmp"; + hmac_mode = 0; + break; + default: + dev_err(fmp->dev, "Invalid mac : %d\n", sop->mac); + return -EINVAL; + } + + /* Create a session and put it to the list. */ + ses_new = kzalloc(sizeof(*ses_new), GFP_KERNEL); + if (!ses_new) + return -ENOMEM; + + if (!fmp->test_data) { + dev_err(fmp->dev, "Invalid fips data\n"); + return -EINVAL; + } + + /* Set-up crypto transform. */ + if (alg_name) { + uint8_t keyp[FMP_CIPHER_MAX_KEY_LEN]; + + if (unlikely(sop->keylen > FMP_CIPHER_MAX_KEY_LEN)) { + dev_err(fmp->dev, "Setting key failed for %s-%zu.\n", + alg_name, (size_t)sop->keylen*8); + ret = -EINVAL; + goto error_cipher; + } + if (unlikely(copy_from_user(keyp, sop->key, sop->keylen))) { + ret = -EFAULT; + goto error_cipher; + } + + if (!strcmp(alg_name, "xts(aes-fmp)")) + ret = fmp_fips_cipher_init(info, &ses_new->cdata, alg_name, + keyp, keyp + (sop->keylen >> 1), + sop->keylen >> 1); + else + ret = fmp_fips_cipher_init(info, &ses_new->cdata, alg_name, + keyp, NULL, sop->keylen); + if (ret < 0) { + dev_err(fmp->dev, "%s: Fail to load cipher for %s\n", + __func__, alg_name); + ret = -EINVAL; + goto error_cipher; + } + } + + if (hash_name) { + if (unlikely(sop->mackeylen > FMP_HMAC_MAX_KEY_LEN)) { + dev_err(fmp->dev, "Setting key failed for %s-%zu.", + hash_name, (size_t)sop->mackeylen*8); + ret = -EINVAL; + goto error_hash; + } + + if (sop->mackey && unlikely(copy_from_user(keys.mkey, sop->mackey, + sop->mackeylen))) { + ret = -EFAULT; + goto error_hash; + } + + ret = fmp_fips_hash_init(info, &ses_new->hdata, hash_name, + hmac_mode, keys.mkey, + sop->mackeylen); + if (ret != 0) { + dev_err(fmp->dev, "Failed to load hash for %s", hash_name); + ret = -EINVAL; + goto error_hash; + } + } + + ses_new->alignmask = max(ses_new->cdata.alignmask, + ses_new->hdata.alignmask); + ses_new->array_size = DEFAULT_PREALLOC_PAGES; + ses_new->pages = kzalloc(ses_new->array_size * + sizeof(struct page *), GFP_KERNEL); + ses_new->sg = kzalloc(ses_new->array_size * + sizeof(struct scatterlist), GFP_KERNEL); + if (ses_new->sg == NULL || ses_new->pages == NULL) { + dev_err(fmp->dev, "Memory error\n"); + ret = -ENOMEM; + goto error_hash; + } + + /* put the new session to the list */ + get_random_bytes(&ses_new->sid, sizeof(ses_new->sid)); + mutex_init(&ses_new->sem); + + mutex_lock(&fcr->sem); +restart: + list_for_each_entry(ses_ptr, &fcr->list, entry) { + /* Check for duplicate SID */ + if (unlikely(ses_new->sid == ses_ptr->sid)) { + get_random_bytes(&ses_new->sid, sizeof(ses_new->sid)); + /* + * Unless we have a broken RNG this + * shouldn't loop forever... ;-) + */ + goto restart; + } + } + + list_add(&ses_new->entry, &fcr->list); + mutex_unlock(&fcr->sem); + + /* Fill in some values for the user. */ + sop->ses = ses_new->sid; + + return 0; + +error_hash: + fmp_fips_cipher_deinit(&ses_new->cdata); + kzfree(ses_new->sg); + kzfree(ses_new->pages); +error_cipher: + kzfree(ses_new); + return ret; +} + +static inline void fmp_destroy_session(struct fmp_fips_info *info, struct csession *ses_ptr) +{ + struct exynos_fmp *fmp; + + if (!info || !info->fmp) { + pr_err("%s: Invalid fmp info\n", __func__); + return; + } + fmp = info->fmp; + + if (!mutex_trylock(&ses_ptr->sem)) { + dev_err(fmp->dev, "Waiting for semaphore of sid=0x%08X\n", ses_ptr->sid); + mutex_lock(&ses_ptr->sem); + } + fmp_fips_cipher_deinit(&ses_ptr->cdata); + fmp_fips_hash_deinit(&ses_ptr->hdata); + kzfree(ses_ptr->pages); + kzfree(ses_ptr->sg); + mutex_unlock(&ses_ptr->sem); + mutex_destroy(&ses_ptr->sem); + kzfree(ses_ptr); +} + +/* Look up a session by ID and remove. */ +static int fmp_finish_session(struct fmp_fips_info *info, struct fcrypt *fcr, uint32_t sid) +{ + struct exynos_fmp *fmp; + struct csession *tmp, *ses_ptr; + struct list_head *head; + int ret = 0; + + if (!info || !info->fmp) { + pr_err("%s: Invalid fmp info\n", __func__); + return -ENODEV; + } + + mutex_lock(&fcr->sem); + head = &fcr->list; + fmp = info->fmp; + + list_for_each_entry_safe(ses_ptr, tmp, head, entry) { + if (ses_ptr->sid == sid) { + list_del(&ses_ptr->entry); + fmp_destroy_session(info, ses_ptr); + break; + } + } + + if (unlikely(!ses_ptr)) { + dev_err(fmp->dev, "%s: Session with sid=0x%08X not found!\n", + __func__, sid); + ret = -ENOENT; + } + mutex_unlock(&fcr->sem); + + return ret; +} + +/* Remove all sessions when closing the file */ +static int fmp_finish_all_sessions(struct fmp_fips_info *info, struct fcrypt *fcr) +{ + struct csession *tmp, *ses_ptr; + struct list_head *head; + + mutex_lock(&fcr->sem); + + head = &fcr->list; + list_for_each_entry_safe(ses_ptr, tmp, head, entry) { + list_del(&ses_ptr->entry); + fmp_destroy_session(info, ses_ptr); + } + mutex_unlock(&fcr->sem); + + return 0; +} + +/* Look up session by session ID. The returned session is locked. */ +struct csession *fmp_get_session_by_sid(struct fcrypt *fcr, uint32_t sid) +{ + struct csession *ses_ptr, *retval = NULL; + + if (unlikely(fcr == NULL)) + return NULL; + + mutex_lock(&fcr->sem); + list_for_each_entry(ses_ptr, &fcr->list, entry) { + if (ses_ptr->sid == sid) { + mutex_lock(&ses_ptr->sem); + retval = ses_ptr; + break; + } + } + mutex_unlock(&fcr->sem); + + return retval; +} + +static void fmptask_routine(struct work_struct *work) +{ + struct fmp_fips_info *info = container_of(work, struct fmp_fips_info, fmptask); + struct exynos_fmp *fmp; + struct todo_list_item *item; + LIST_HEAD(tmp); + + if (!info || !info->fmp) { + pr_err("%s: Invalid fmp info\n", __func__); + return; + } + fmp = info->fmp; + + /* fetch all pending jobs into the temporary list */ + mutex_lock(&info->todo.lock); + list_cut_position(&tmp, &info->todo.list, info->todo.list.prev); + mutex_unlock(&info->todo.lock); + + /* handle each job locklessly */ + list_for_each_entry(item, &tmp, __hook) { + item->result = fmp_run(info, &info->fcrypt, &item->kcop); + if (unlikely(item->result)) + dev_err(fmp->dev, "%s: crypto_run() failed: %d\n", + __func__, item->result); + } + + /* push all handled jobs to the done list at once */ + mutex_lock(&info->done.lock); + list_splice_tail(&tmp, &info->done.list); + mutex_unlock(&info->done.lock); + + /* wake for POLLIN */ + wake_up_interruptible(&info->user_waiter); +} + +int fmp_fips_open(struct inode *inode, struct file *file) +{ + int i, ret = 0; + struct fmp_fips_info *info = NULL; + struct todo_list_item *tmp; + struct exynos_fmp *fmp = container_of(file->private_data, + struct exynos_fmp, miscdev); + + if (!fmp || !fmp->dev) { + pr_err("%s: Invalid fmp driver\n", __func__); + ret = -EINVAL; + goto err; + } + + dev_info(fmp->dev, "fmp fips driver name : %s\n", dev_name(fmp->dev)); + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) { + ret = -ENOMEM; + goto err; + } + memset(info, 0, sizeof(*info)); + + info->data = fmp_test_init(fmp); + if (!info->data) { + dev_err(fmp->dev, "%s: Fail to initialize fips test.\n", + __func__); + goto err; + } + + mutex_init(&info->fcrypt.sem); + INIT_LIST_HEAD(&info->fcrypt.list); + INIT_LIST_HEAD(&info->free.list); + INIT_LIST_HEAD(&info->todo.list); + INIT_LIST_HEAD(&info->done.list); + INIT_WORK(&info->fmptask, fmptask_routine); + mutex_init(&info->free.lock); + mutex_init(&info->todo.lock); + mutex_init(&info->done.lock); + init_waitqueue_head(&info->user_waiter); + + for (i = 0; i < DEF_COP_RINGSIZE; i++) { + tmp = kzalloc(sizeof(struct todo_list_item), GFP_KERNEL); + info->itemcount++; + dev_info(fmp->dev, "%s: allocated new item at %lx\n", + __func__, (unsigned long)tmp); + list_add(&tmp->__hook, &info->free.list); + } + + info->fmp = fmp; + file->private_data = info; + + dev_info(fmp->dev, "%s opened.\n", dev_name(fmp->dev)); + return ret; +err: + if (info) + fmp_test_exit(info->data); + return ret; +} + +/* this function has to be called from process context */ +static int fill_kcop_from_cop(struct fmp_fips_info *info, + struct kernel_crypt_op *kcop, struct fcrypt *fcr) +{ + struct exynos_fmp *fmp; + struct crypt_op *cop = &kcop->cop; + struct csession *ses_ptr; + int rc; + + if (!info || !info->fmp) { + pr_err("%s: Invalid fmp info\n", __func__); + return -ENODEV; + } + fmp = info->fmp; + + /* this also enters ses_ptr->sem */ + ses_ptr = fmp_get_session_by_sid(fcr, cop->ses); + if (unlikely(!ses_ptr)) { + dev_err(fmp->dev, "invalid session ID=0x%08X\n", cop->ses); + return -EINVAL; + } + kcop->digestsize = 0; /* will be updated during operation */ + + fmp_put_session(ses_ptr); + + kcop->task = current; + kcop->mm = current->mm; + + if (cop->iv) { + kcop->ivlen = ses_ptr->cdata.ivsize; + rc = copy_from_user(kcop->iv, cop->iv, kcop->ivlen); + if (unlikely(rc)) { + dev_err(fmp->dev, + "error copying IV (%d bytes), copy_from_user returned %d for address %lx\n", + kcop->ivlen, rc, (unsigned long)cop->iv); + return -EFAULT; + } + } else { + kcop->ivlen = 0; + } + + return 0; +} + +/* this function has to be called from process context */ +static int fill_cop_from_kcop(struct kernel_crypt_op *kcop, struct fcrypt *fcr) +{ + int ret; + + if (kcop->digestsize) { + ret = copy_to_user(kcop->cop.mac, + kcop->hash_output, kcop->digestsize); + if (unlikely(ret)) + return -EFAULT; + } + if (kcop->ivlen && kcop->cop.flags & COP_FLAG_WRITE_IV) { + ret = copy_to_user(kcop->cop.iv, + kcop->iv, kcop->ivlen); + if (unlikely(ret)) + return -EFAULT; + } + return 0; +} + + +static int kcop_from_user(struct fmp_fips_info *info, struct kernel_crypt_op *kcop, + struct fcrypt *fcr, void __user *arg) +{ + if (unlikely(copy_from_user(&kcop->cop, arg, sizeof(kcop->cop)))) + return -EFAULT; + + return fill_kcop_from_cop(info, kcop, fcr); +} + +static int kcop_to_user(struct fmp_fips_info *info, struct kernel_crypt_op *kcop, + struct fcrypt *fcr, void __user *arg) +{ + int ret; + struct exynos_fmp *fmp; + + if (!info || !info->fmp) { + pr_err("%s: Invalid fmp info\n", __func__); + return -ENODEV; + } + fmp = info->fmp; + + ret = fill_cop_from_kcop(kcop, fcr); + if (unlikely(ret)) { + dev_err(fmp->dev, "Error in fill_cop_from_kcop\n"); + return ret; + } + + if (unlikely(copy_to_user(arg, &kcop->cop, sizeof(kcop->cop)))) { + dev_err(fmp->dev, "Cannot copy to userspace\n"); + return -EFAULT; + } + return 0; +} + +static int get_session_info(struct fmp_fips_info *info, + struct fcrypt *fcr, struct session_info_op *siop) +{ + struct csession *ses_ptr; + struct exynos_fmp *fmp; + + if (!info || !info->fmp) { + pr_err("%s: Invalid fmp info\n", __func__); + return -ENODEV; + } + fmp = info->fmp; + + /* this also enters ses_ptr->sem */ + ses_ptr = fmp_get_session_by_sid(fcr, siop->ses); + if (unlikely(!ses_ptr)) { + dev_err(fmp->dev, "invalid session ID=0x%08X\n", siop->ses); + return -EINVAL; + } + + siop->flags = 0; + siop->alignmask = ses_ptr->alignmask; + fmp_put_session(ses_ptr); + + return 0; +} + +long fmp_fips_ioctl(struct file *file, unsigned int cmd, unsigned long arg_) +{ + int ret; + struct session_op sop; + struct kernel_crypt_op kcop; + struct fmp_fips_info *info = file->private_data; + struct exynos_fmp *fmp; + void __user *arg = (void __user *)arg_; + struct fcrypt *fcr; + struct session_info_op siop; + uint32_t ses; + + if (!info || !info->fmp) { + pr_err("%s: Invalid fmp info\n", __func__); + return -ENODEV; + } + fmp = info->fmp; + fcr = &info->fcrypt; + + switch (cmd) { + case FMPGSESSION: + if (unlikely(copy_from_user(&sop, arg, sizeof(sop)))) + return -EFAULT; + + ret = fmp_create_session(info, fcr, &sop); + if (unlikely(ret)) { + dev_err(fmp->dev, "%s: Fail to create fmp session. ret = %d\n", + __func__, ret); + return ret; + } + ret = copy_to_user(arg, &sop, sizeof(sop)); + if (unlikely(ret)) { + fmp_finish_session(info, fcr, sop.ses); + return -EFAULT; + } + return ret; + case FMPFSESSION: + ret = get_user(ses, (uint32_t __user *)arg); + if (unlikely(ret)) + return ret; + ret = fmp_finish_session(info, fcr, ses); + if (unlikely(ret)) + dev_err(fmp->dev, "%s: Fail to finish fmp session. ret = %d\n", + __func__, ret); + return ret; + case FMPGSESSIONINFO: + if (unlikely(copy_from_user(&siop, arg, sizeof(siop)))) + return -EFAULT; + ret = get_session_info(info, fcr, &siop); + if (unlikely(ret)) { + dev_err(fmp->dev, "%s: Fail to get fmp session. ret = %d\n", + __func__, ret); + return ret; + } + return copy_to_user(arg, &siop, sizeof(siop)); + case FMPCRYPT: + ret = kcop_from_user(info, &kcop, fcr, arg); + if (unlikely(ret)) { + dev_err(fmp->dev, "%s: Error copying from user", __func__); + return ret; + } + + if (unlikely(in_fmp_fips_err())) { + dev_err(fmp->dev, "%s: Fail to run fmp due to fips in error.", + __func__); + return -EPERM; + } + + ret = fmp_run(info, fcr, &kcop); + if (unlikely(ret)) { + dev_err(fmp->dev, "%s: Fail to run fmp crypt. ret = %d\n", + __func__, ret); + return ret; + } + return kcop_to_user(info, &kcop, fcr, arg); + case FMP_AES_CBC_MCT: + ret = kcop_from_user(info, &kcop, fcr, arg); + if (unlikely(ret)) { + dev_err(fmp->dev, "%s: Error copying from user", __func__); + return ret; + } + + if (unlikely(in_fmp_fips_err())) { + dev_err(fmp->dev, "%s: Fail to run fmp due to fips in error.", + __func__); + return -EPERM; + } + + ret = fmp_run_AES_CBC_MCT(info, fcr, &kcop); + if (unlikely(ret)) { + dev_err(fmp->dev, "%s: Error in fmp_run_AES_CBC_MCT", __func__); + return ret; + } + return kcop_to_user(info, &kcop, fcr, arg); + default: + dev_err(fmp->dev, "%s: Invalid command : 0x%x\n", __func__, cmd); + return -EINVAL; + } +} + +/* compatibility code for 32bit userlands */ +#ifdef CONFIG_COMPAT +static inline void compat_to_session_op(struct compat_session_op *compat, + struct session_op *sop) +{ + sop->cipher = compat->cipher; + sop->mac = compat->mac; + sop->keylen = compat->keylen; + + sop->key = compat_ptr(compat->key); + sop->mackeylen = compat->mackeylen; + sop->mackey = compat_ptr(compat->mackey); + sop->ses = compat->ses; +} + +static inline void session_op_to_compat(struct session_op *sop, + struct compat_session_op *compat) +{ + compat->cipher = sop->cipher; + compat->mac = sop->mac; + compat->keylen = sop->keylen; + + compat->key = ptr_to_compat(sop->key); + compat->mackeylen = sop->mackeylen; + compat->mackey = ptr_to_compat(sop->mackey); + compat->ses = sop->ses; +} + +static inline void compat_to_crypt_op(struct compat_crypt_op *compat, + struct crypt_op *cop) +{ + cop->ses = compat->ses; + cop->op = compat->op; + cop->flags = compat->flags; + cop->len = compat->len; + + cop->src = compat_ptr(compat->src); + cop->dst = compat_ptr(compat->dst); + cop->mac = compat_ptr(compat->mac); + cop->iv = compat_ptr(compat->iv); + + cop->data_unit_len = compat->data_unit_len; + cop->data_unit_seqnumber = compat->data_unit_seqnumber; + + cop->secondLastEncodedData = compat_ptr(compat->secondLastEncodedData); + cop->thirdLastEncodedData = compat_ptr(compat->thirdLastEncodedData); +} + +static inline void crypt_op_to_compat(struct crypt_op *cop, + struct compat_crypt_op *compat) +{ + compat->ses = cop->ses; + compat->op = cop->op; + compat->flags = cop->flags; + compat->len = cop->len; + + compat->src = ptr_to_compat(cop->src); + compat->dst = ptr_to_compat(cop->dst); + compat->mac = ptr_to_compat(cop->mac); + compat->iv = ptr_to_compat(cop->iv); + + compat->data_unit_len = cop->data_unit_len; + compat->data_unit_seqnumber = cop->data_unit_seqnumber; + + compat->secondLastEncodedData = ptr_to_compat(cop->secondLastEncodedData); + compat->thirdLastEncodedData = ptr_to_compat(cop->thirdLastEncodedData); +} + +static int compat_kcop_from_user(struct fmp_fips_info *info, + struct kernel_crypt_op *kcop, + struct fcrypt *fcr, void __user *arg) +{ + struct compat_crypt_op compat_cop; + + if (unlikely(copy_from_user(&compat_cop, arg, sizeof(compat_cop)))) + return -EFAULT; + compat_to_crypt_op(&compat_cop, &kcop->cop); + + return fill_kcop_from_cop(info, kcop, fcr); +} + +static int compat_kcop_to_user(struct fmp_fips_info *info, + struct kernel_crypt_op *kcop, + struct fcrypt *fcr, void __user *arg) +{ + int ret; + struct exynos_fmp *fmp; + struct compat_crypt_op compat_cop; + + if (!info || !info->fmp) { + pr_err("%s: fmp info is already free.\n", __func__); + return 0; + } + fmp = info->fmp; + + ret = fill_cop_from_kcop(kcop, fcr); + if (unlikely(ret)) { + dev_err(fmp->dev, "Error in fill_cop_from_kcop"); + return ret; + } + crypt_op_to_compat(&kcop->cop, &compat_cop); + + if (unlikely(copy_to_user(arg, &compat_cop, sizeof(compat_cop)))) { + dev_err(fmp->dev, "Error copying to user"); + return -EFAULT; + } + return 0; +} + +long fmp_fips_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg_) +{ + int ret; + struct session_op sop; + struct compat_session_op compat_sop; + struct kernel_crypt_op kcop; + struct fmp_fips_info *info = file->private_data; + struct exynos_fmp *fmp; + void __user *arg = (void __user *)arg_; + struct fcrypt *fcr; + + if (!info || !info->fmp) { + pr_err("%s: fmp info is already free.\n", __func__); + return 0; + } + fmp = info->fmp; + + fcr = &info->fcrypt; + + switch (cmd) { + case FMPFSESSION: + case FMPGSESSIONINFO: + return fmp_fips_ioctl(file, cmd, arg_); + + case COMPAT_FMPGSESSION: + if (unlikely(copy_from_user(&compat_sop, arg, sizeof(compat_sop)))) + return -EFAULT; + compat_to_session_op(&compat_sop, &sop); + + ret = fmp_create_session(info, fcr, &sop); + if (unlikely(ret)) { + dev_err(fmp->dev, "Fail to create fmp session. ret = %d\n", ret); + return ret; + } + + session_op_to_compat(&sop, &compat_sop); + ret = copy_to_user(arg, &compat_sop, sizeof(compat_sop)); + if (unlikely(ret)) { + fmp_finish_session(info, fcr, sop.ses); + return -EFAULT; + } + return ret; + case COMPAT_FMPCRYPT: + ret = compat_kcop_from_user(info, &kcop, fcr, arg); + if (unlikely(ret)) { + dev_err(fmp->dev, "Error copying from user"); + return ret; + } + + if (unlikely(in_fmp_fips_err())) { + dev_err(fmp->dev, "Fail to run fmp due to fips in error."); + return -EPERM; + } + + ret = fmp_run(info, fcr, &kcop); + if (unlikely(ret)) { + dev_err(fmp->dev, "Fail to run fm)p crypt. ret = %d\n", ret); + return ret; + } + return compat_kcop_to_user(info, &kcop, fcr, arg); + case COMPAT_FMP_AES_CBC_MCT: + ret = compat_kcop_from_user(info, &kcop, fcr, arg); + if (unlikely(ret)) { + dev_err(fmp->dev, "Error copying from user"); + return ret; + } + + if (unlikely(in_fmp_fips_err())) { + dev_err(fmp->dev, "Fail to run fmp due to fips in error."); + return -EPERM; + } + + ret = fmp_run_AES_CBC_MCT(info, fcr, &kcop); + if (unlikely(ret)) { + dev_err(fmp->dev, "Error in fmp_run_AES_CBC_MCT"); + return ret; + } + return compat_kcop_to_user(info, &kcop, fcr, arg); + default: + dev_err(fmp->dev, "Invalid command : 0x%x\n", cmd); + return -EINVAL; + } +} +#endif /* CONFIG_COMPAT */ + +int fmp_fips_release(struct inode *inode, struct file *file) +{ + struct fmp_fips_info *info = file->private_data; + struct exynos_fmp *fmp; + struct todo_list_item *item, *item_safe; + int items_freed = 0; + + if (!info || !info->fmp) { + pr_err("%s: fmp info is already free.\n", __func__); + return 0; + } + fmp = info->fmp; + + cancel_work_sync(&info->fmptask); + + mutex_destroy(&info->todo.lock); + mutex_destroy(&info->done.lock); + mutex_destroy(&info->free.lock); + + list_splice_tail(&info->todo.list, &info->free.list); + list_splice_tail(&info->done.list, &info->free.list); + + list_for_each_entry_safe(item, item_safe, &info->free.list, __hook) { + dev_err(fmp->dev, "%s: freeing item at %lx\n", + __func__, (unsigned long)item); + list_del(&item->__hook); + kzfree(item); + items_freed++; + } + if (items_freed != info->itemcount) + dev_err(fmp->dev, "%s: freed %d items, but %d should exist!\n", + __func__, items_freed, info->itemcount); + + fmp_finish_all_sessions(info, &info->fcrypt); + fmp_test_exit(info->data); + kzfree(info); + file->private_data = NULL; + dev_info(fmp->dev, "%s released.\n", dev_name(fmp->dev)); + + return 0; +} diff --git a/drivers/crypto/fmp/fmp_fips_fops.h b/drivers/crypto/fmp/fmp_fips_fops.h new file mode 100644 index 000000000000..6c678d7f160b --- /dev/null +++ b/drivers/crypto/fmp/fmp_fips_fops.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2016 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef _FMP_FIPS_FOPS_H_ +#define _FMP_FIPS_FOPS_H_ +int fmp_fips_open(struct inode *inode, struct file *file); +int fmp_fips_release(struct inode *inode, struct file *file); +long fmp_fips_ioctl(struct file *file, unsigned int cmd, unsigned long arg_); +#ifdef CONFIG_COMPAT +long fmp_fips_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg_); +#endif /* CONFIG_COMPAT */ +#endif /* _FMP_FIPS_FOPS_H_ */ diff --git a/drivers/crypto/fmp/fmp_fips_fops_info.h b/drivers/crypto/fmp/fmp_fips_fops_info.h new file mode 100644 index 000000000000..c6ea996f6fdd --- /dev/null +++ b/drivers/crypto/fmp/fmp_fips_fops_info.h @@ -0,0 +1,121 @@ +/* + * Exynos FMP device information for FIPS + * + * Copyright (C) 2015 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef _FMP_FIPS_FOPS_INFO_ +#define _FMP_FIPS_FOPS_INFO_ + +/* API extensions for linux */ +#define FMP_HMAC_MAX_KEY_LEN 512 +#define FMP_CIPHER_MAX_KEY_LEN 64 + +enum fmpdev_crypto_op_t { + FMP_AES_CBC = 1, + FMP_AES_XTS = 2, + FMP_SHA2_256_HMAC = 3, + FMP_SHA2_256 = 4, + FMPFW_SHA2_256_HMAC = 5, + FMPFW_SHA2_256 = 6, + FMP_ALGORITHM_ALL, /* Keep updated - see below */ +}; +#define FMP_ALGORITHM_MAX (FMP_ALGORITHM_ALL - 1) + +/* the maximum of the above */ +#define EALG_MAX_BLOCK_LEN 16 + +/* Values for hashes/MAC */ +#define AALG_MAX_RESULT_LEN 64 + +/* maximum length of verbose alg names (depends on CRYPTO_MAX_ALG_NAME) */ +#define FMPDEV_MAX_ALG_NAME 64 + +#define DEFAULT_PREALLOC_PAGES 32 + +/* input of FMPGSESSION */ +struct session_op { + __u32 cipher; /* cryptodev_crypto_op_t */ + __u32 mac; /* cryptodev_crypto_op_t */ + + __u32 keylen; + __u8 __user *key; + __u32 mackeylen; + __u8 __user *mackey; + + __u32 ses; /* session identifier */ +}; + +struct session_info_op { + __u32 ses; /* session identifier */ + + /* verbose names for the requested ciphers */ + struct alg_info { + char cra_name[FMPDEV_MAX_ALG_NAME]; + char cra_driver_name[FMPDEV_MAX_ALG_NAME]; + } cipher_info, hash_info; + + __u16 alignmask; /* alignment constraints */ + __u32 flags; /* SIOP_FLAGS_* */ +}; + +/* If this flag is set then this algorithm uses + * a driver only available in kernel (software drivers, + * or drivers based on instruction sets do not set this flag). + * + * If multiple algorithms are involved (as in AEAD case), then + * if one of them is kernel-driver-only this flag will be set. + */ +#define SIOP_FLAG_KERNEL_DRIVER_ONLY 1 + +#define COP_ENCRYPT 0 +#define COP_DECRYPT 1 + +/* input of FMPCRYPT */ +struct crypt_op { + __u32 ses; /* session identifier */ + __u16 op; /* COP_ENCRYPT or COP_DECRYPT */ + __u16 flags; /* see COP_FLAG_* */ + __u32 len; /* length of source data */ + __u8 __user *src; /* source data */ + __u8 __user *dst; /* pointer to output data */ + /* pointer to output data for hash/MAC operations */ + __u8 __user *mac; + /* initialization vector for encryption operations */ + __u8 __user *iv; + + __u32 data_unit_len; + __u32 data_unit_seqnumber; + + __u8 __user *secondLastEncodedData; + __u8 __user *thirdLastEncodedData; +}; + +#define COP_FLAG_NONE (0 << 0) /* totally no flag */ +#define COP_FLAG_UPDATE (1 << 0) /* multi-update hash mode */ +#define COP_FLAG_FINAL (1 << 1) /* multi-update final hash mode */ +#define COP_FLAG_WRITE_IV (1 << 2) /* update the IV during operation */ +#define COP_FLAG_NO_ZC (1 << 3) /* do not zero-copy */ +#define COP_FLAG_AEAD_TLS_TYPE (1 << 4) /* authenticate and encrypt using the + * TLS protocol rules */ +#define COP_FLAG_AEAD_SRTP_TYPE (1 << 5) /* authenticate and encrypt using the + * SRTP protocol rules */ +#define COP_FLAG_RESET (1 << 6) /* multi-update reset the state. + * should be used in combination + * with COP_FLAG_UPDATE */ +#define COP_FLAG_AES_CBC (1 << 7) +#define COP_FLAG_AES_XTS (1 << 8) +#define COP_FLAG_AES_CBC_MCT (1 << 9) + +#define FMPGSESSION _IOWR('c', 200, struct session_op) +#define FMPFSESSION _IOWR('c', 201, __u32) +#define FMPGSESSIONINFO _IOWR('c', 202, struct session_info_op) +#define FMPCRYPT _IOWR('c', 203, struct crypt_op) +#define FMP_AES_CBC_MCT _IOWR('c', 204, struct crypt_op) + +#endif diff --git a/drivers/crypto/fmp/fmp_fips_func_test.c b/drivers/crypto/fmp/fmp_fips_func_test.c new file mode 100644 index 000000000000..5d656fcd9c7a --- /dev/null +++ b/drivers/crypto/fmp/fmp_fips_func_test.c @@ -0,0 +1,177 @@ +/* + * Exynos FMP FIPS functional test + * + * Copyright (C) 2017 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include + +#include + +#include "fmp_fips_info.h" +#include "fmp_fips_main.h" +#include "fmp_fips_func_test.h" + +static char *fips_functest_mode; + +static char *fips_functest_KAT_list[] = { + "aes-xts", + "aes-cbc", + "sha256", + "hmac-sha256", + "integrity", + "zeroization" +}; + +void set_fmp_fips_functest_KAT_mode(const int num) +{ + if (num >= 0 && num < FMP_FUNCTEST_KAT_CASE_NUM) + fips_functest_mode = fips_functest_KAT_list[num]; + else + fips_functest_mode = FMP_FUNCTEST_NO_TEST; +} + +char *get_fmp_fips_functest_mode(void) +{ + if (fips_functest_mode) + return fips_functest_mode; + else + return FMP_FUNCTEST_NO_TEST; +} + +static int fmp_func_test_integrity(HMAC_SHA256_CTX *desc, + unsigned long *start_addr) +{ + int ret = 0; + + if (!strcmp("integrity", get_fmp_fips_functest_mode())) { + pr_info("FIPS(%s): Failing Integrity Test\n", __func__); + ret = hmac_sha256_update(desc, + (unsigned char *)start_addr, 1); + } + + return ret; +} + +static int fmp_func_test_zeroization(struct fmp_table_setting *table, + char *str) +{ + if (!table || !str) { + pr_err("%s: invalid fmp table address or string\n", __func__); + return -EINVAL; + } + + if (!strcmp("zeroization", get_fmp_fips_functest_mode())) { + pr_info("FIPS FMP descriptor zeroize (%s) ", str); + print_hex_dump(KERN_ERR, "FIPS FMP descriptor zeroize: ", + DUMP_PREFIX_NONE, 16, 1, &table->file_iv0, + sizeof(__le32) * 20, false); + } + + return 0; +} + +static int fmp_func_test_hmac_sha256(char *digest, char *algorithm) +{ + char *str = get_fmp_fips_functest_mode(); + + if (!digest || !algorithm) { + pr_err("%s: invalid digest address or algorithm\n", __func__); + return -EINVAL; + } + + if (!strcmp(algorithm, str)) + digest[0] += 1; + + return 0; +} + +static int fmp_func_test_aes(const int mode, char *key, unsigned char klen) +{ + char *str = get_fmp_fips_functest_mode(); + + if (!key || (klen == 0)) { + pr_err("%s: invalid fmp key or key length\n", __func__); + return -EINVAL; + } + + if ((!strcmp("aes-xts", str) && XTS_MODE == mode) + || (!strcmp("aes-cbc", str) && CBC_MODE == mode)) + key[0] += 1; + + return 0; +} + +const struct exynos_fmp_fips_test_vops exynos_fmp_fips_test_ops = { + .integrity = fmp_func_test_integrity, + .zeroization = fmp_func_test_zeroization, + .hmac_sha256 = fmp_func_test_hmac_sha256, + .aes = fmp_func_test_aes, +}; + +int exynos_fmp_func_test_KAT_case(struct platform_device *pdev, + struct exynos_fmp *fmp) +{ + int i, ret; + struct fmp_crypto_info data; + struct fmp_request req; + struct device *dev; + + if (!fmp || !fmp->dev) { + pr_err("%s: invalid fmp context or device\n", __func__); + ret = -EINVAL; + goto out; + } + + dev = fmp->dev; + fmp->test_vops = (struct exynos_fmp_fips_test_vops *)&exynos_fmp_fips_test_ops; + + for (i = 0; i < FMP_FUNCTEST_KAT_CASE_NUM; i++) { + set_fmp_fips_functest_KAT_mode(i); + dev_info(dev, "FIPS FUNC : -----------------------------------\n"); + dev_info(dev, "FIPS FUNC : Test case %d - [%s]\n", + i + 1, get_fmp_fips_functest_mode()); + dev_info(dev, "FIPS FUNC : -----------------------------------\n"); + + ret = exynos_fmp_fips_init(fmp); + if (ret) { + dev_err(dev, "FIPS FUNC : Fail to initialize fmp fips. ret(%d)", + ret); + exynos_fmp_fips_exit(fmp); + goto out; + } + dev_info(dev, "FIPS FUNC : (%d-1) Selftest done. FMP FIPS status : %s\n", + i + 1, in_fmp_fips_err() ? "FAILED" : "PASSED"); + + memset(&data, 0, sizeof(struct fmp_crypto_info)); + memset(&req, 0, sizeof(struct fmp_request)); + dev_info(dev, "FIPS FUNC : (%d-2) Try to set config\n", i + 1); + + ret = exynos_fmp_crypt(&data, &req); + if (ret) + dev_info(dev, + "FIPS FUNC : (%d-3) Fail FMP config as expected. ret(%d)\n", + i + 1, ret); + + exynos_fmp_fips_exit(fmp); + } + + set_fmp_fips_functest_KAT_mode(-1); + + dev_info(dev, "FIPS FUNC : Functional test end\n"); + dev_info(dev, "FIPS FUNC : -----------------------------------\n"); + dev_info(dev, "FIPS FUNC : Normal init start\n"); + dev_info(dev, "FIPS FUNC : -----------------------------------\n"); + + ret = 0; +out: + fmp->test_vops = NULL; + return ret; +} diff --git a/drivers/crypto/fmp/fmp_fips_func_test.h b/drivers/crypto/fmp/fmp_fips_func_test.h new file mode 100644 index 000000000000..bab336af294f --- /dev/null +++ b/drivers/crypto/fmp/fmp_fips_func_test.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2017 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef _FMP_FIPS_FUNC_TEST_H_ +#define _FMP_FIPS_FUNC_TEST_H_ + +#if defined(CONFIG_EXYNOS_FMP_FIPS) +#if defined(CONFIG_EXYNOS_FMP_FIPS_FUNC_TEST) +#define FMP_FUNCTEST_KAT_CASE_NUM 6 +#define FMP_FUNCTEST_NO_TEST "NO_TEST" + +int exynos_fmp_func_test_KAT_case(struct platform_device *pdev, + struct exynos_fmp *fmp); +#else +inline int exynos_fmp_func_test_KAT_case(struct platform_device *pdev, + struct exynos_fmp *fmp) +{ + if (fmp) + fmp->test_vops = NULL; + return 0; +} +#endif /* CONFIG_EXYNOS_FMP_FIPS_FUNC_TEST */ +#endif /* CONFIG_EXYNOS_FMP_FIPS */ +#endif /* _FMP_FIPS_FUNC_TEST_H_ */ diff --git a/drivers/crypto/fmp/fmp_fips_info.h b/drivers/crypto/fmp/fmp_fips_info.h new file mode 100644 index 000000000000..ee8e51c80b26 --- /dev/null +++ b/drivers/crypto/fmp/fmp_fips_info.h @@ -0,0 +1,228 @@ +/* + * Exynos FMP device header for FIPS + * + * Copyright (C) 2015 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __FMP_FIPS_INFO_H__ +#define __FMP_FIPS_INFO_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fmp_fips_fops_info.h" +#include "sha256.h" +#include "hmac-sha256.h" + +#define BYPASS_MODE 0 +#define CBC_MODE 1 +#define XTS_MODE 2 + +struct fcrypt { + struct list_head list; + struct mutex sem; +}; + +/* kernel-internal extension to struct crypt_op */ +struct kernel_crypt_op { + struct crypt_op cop; + + int ivlen; + __u8 iv[EALG_MAX_BLOCK_LEN]; + + int digestsize; + uint8_t hash_output[AALG_MAX_RESULT_LEN]; + + struct task_struct *task; + struct mm_struct *mm; +}; + +struct todo_list_item { + struct list_head __hook; + struct kernel_crypt_op kcop; + int result; +}; + +struct locked_list { + struct list_head list; + struct mutex lock; +}; + +struct fmp_fips_info { + struct fcrypt fcrypt; + struct locked_list free, todo, done; + int itemcount; + struct work_struct fmptask; + wait_queue_head_t user_waiter; + struct exynos_fmp *fmp; + struct fmp_test_data *data; +}; + +/* compatibility stuff */ +#ifdef CONFIG_COMPAT +#include + +/* input of FMPGSESSION */ +struct compat_session_op { + /* Specify either cipher or mac + */ + uint32_t cipher; /* cryptodev_crypto_op_t */ + uint32_t mac; /* cryptodev_crypto_op_t */ + + uint32_t keylen; + compat_uptr_t key; /* pointer to key data */ + uint32_t mackeylen; + compat_uptr_t mackey; /* pointer to mac key data */ + + uint32_t ses; /* session identifier */ +}; + +/* input of FMPCRYPT */ +struct compat_crypt_op { + uint32_t ses; /* session identifier */ + uint16_t op; /* COP_ENCRYPT or COP_DECRYPT */ + uint16_t flags; /* see COP_FLAG_* */ + uint32_t len; /* length of source data */ + compat_uptr_t src; /* source data */ + compat_uptr_t dst; /* pointer to output data */ + compat_uptr_t mac;/* pointer to output data for hash/MAC operations */ + compat_uptr_t iv;/* initialization vector for encryption operations */ + + __u32 data_unit_len; + __u32 data_unit_seqnumber; + + compat_uptr_t secondLastEncodedData; + compat_uptr_t thirdLastEncodedData; +}; + +#define COMPAT_FMPGSESSION _IOWR('c', 200, struct compat_session_op) +#define COMPAT_FMPCRYPT _IOWR('c', 203, struct compat_crypt_op) +#define COMPAT_FMP_AES_CBC_MCT _IOWR('c', 204, struct compat_crypt_op) +#endif + +/* the maximum of the above */ +#define EALG_MAX_BLOCK_LEN 16 + +struct cipher_data { + int init; /* 0 uninitialized */ + int blocksize; + int aead; + int stream; + int ivsize; + int alignmask; + struct { + /* block ciphers */ + struct crypto_ablkcipher *s; + struct ablkcipher_request *request; + + /* AEAD ciphers */ + struct crypto_aead *as; + struct aead_request *arequest; + + struct fmp_fips_result *result; + uint8_t iv[EALG_MAX_BLOCK_LEN]; + } async; +}; + +struct hash_data { + int init; /* 0 uninitialized */ + int digestsize; + int alignmask; + SHA256_CTX *sha; + HMAC_SHA256_CTX *hmac; + struct { + struct crypto_ahash *s; + struct fmp_fips_result *result; + struct ahash_request *request; + } async; +}; + +struct fmp_fips_result { + struct completion completion; + int err; +}; + +/* other internal structs */ +struct csession { + struct list_head entry; + struct mutex sem; + struct cipher_data cdata; + struct hash_data hdata; + uint32_t sid; + uint32_t alignmask; + + unsigned int array_size; + unsigned int used_pages; /* the number of pages that are used */ + /* the number of pages marked as writable (first are the readable) */ + unsigned int readable_pages; + struct page **pages; + struct scatterlist *sg; +}; + +struct csession *fmp_get_session_by_sid(struct fcrypt *fcr, uint32_t sid); + +static inline void fmp_put_session(struct csession *ses_ptr) +{ + mutex_unlock(&ses_ptr->sem); +} +int adjust_sg_array(struct csession *ses, int pagecount); + +#define MAX_TAP 8 +#define XBUFSIZE 8 + +struct cipher_testvec { + char *key; + char *iv; + char *input; + char *result; + unsigned short tap[MAX_TAP]; + int np; + unsigned char also_non_np; + unsigned char klen; + unsigned short ilen; + unsigned short rlen; +}; + +struct hash_testvec { + /* only used with keyed hash algorithms */ + char *key; + char *plaintext; + char *digest; + unsigned char tap[MAX_TAP]; + unsigned short psize; + unsigned char np; + unsigned char ksize; +}; + +struct cipher_test_suite { + struct { + struct cipher_testvec *vecs; + unsigned int count; + } enc; +}; + +struct hash_test_suite { + struct hash_testvec *vecs; + unsigned int count; +}; + +struct exynos_fmp_fips_test_vops { + int (*integrity)(HMAC_SHA256_CTX *desc, unsigned long *start_addr); + int (*zeroization)(struct fmp_table_setting *table, char *str); + int (*hmac_sha256)(char *digest, char *algorithm); + int (*aes)(const int mode, char *key, unsigned char klen); +}; + +#endif diff --git a/drivers/crypto/fmp/fmp_fips_integrity.c b/drivers/crypto/fmp/fmp_fips_integrity.c new file mode 100644 index 000000000000..5907022e0aaf --- /dev/null +++ b/drivers/crypto/fmp/fmp_fips_integrity.c @@ -0,0 +1,153 @@ +/* + * Perform FIPS Integrity test on FMP driver + * + * 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 +#include +#include +#include + +#include + +#include "hmac-sha256.h" +#include "fmp_fips_info.h" +#include "fmp_fips_integrity.h" + +#undef FIPS_DEBUG_INTEGRITY + +/* Same as build time */ +static const unsigned char *integrity_check_key = "The quick brown fox jumps over the lazy dog"; + +/* + * This function return kernel offset. + * If CONFIG_RELOCATABLE is set + * Then the kernel will be placed into the random offset in memory + * At the build time, we write self address of the "buildtime_address" + * to the "buildtime_address" variable + */ +static __u64 get_kernel_offset(void) +{ + static __u64 runtime_address = (__u64) &fmp_buildtime_address; + __u64 kernel_offset = abs(fmp_buildtime_address - runtime_address); + return kernel_offset; +} + +int do_fmp_integrity_check(struct exynos_fmp *fmp) +{ + int i, rows, err; + unsigned long start_addr = 0; + unsigned long end_addr = 0; + unsigned char runtime_hmac[32]; + struct hmac_sha256_ctx ctx; + const char *builtime_hmac = 0; + unsigned int size = 0; +#ifdef FIPS_DEBUG_INTEGRITY + unsigned int covered = 0; + unsigned int num_addresses = 0; +#endif + struct exynos_fmp_fips_test_vops *test_vops; + + if (!fmp || !fmp->dev) { + pr_err("%s: invalid fmp device\n", __func__); + return -EINVAL; + } + + memset(runtime_hmac, 0x00, 32); + + err = hmac_sha256_init(&ctx, integrity_check_key, strlen(integrity_check_key)); + if (err) { + pr_err("FIPS(%s): init_hash failed\n", __func__); + return -1; + } + + rows = (__u32) ARRAY_SIZE(integrity_fmp_addrs); + + for (i = 0; integrity_fmp_addrs[i].first && i < rows; i++) { + + start_addr = integrity_fmp_addrs[i].first + get_kernel_offset(); + end_addr = integrity_fmp_addrs[i].last + get_kernel_offset(); + + /* Print addresses for HMAC calculation */ +#ifdef FIPS_DEBUG_INTEGRITY + pr_info("FIPS_DEBUG_INTEGRITY : first last %08llux %08llux\n", + integrity_fmp_addrs[i].first, + integrity_fmp_addrs[i].last); + covered += (end_addr - start_addr); +#endif + + size = (uint32_t)(end_addr - start_addr); + + err = hmac_sha256_update(&ctx, (unsigned char *)start_addr, size); + if (err) { + pr_err("FIPS(%s): Error to update hash", __func__); + return -1; + } + } + + test_vops = (struct exynos_fmp_fips_test_vops *)fmp->test_vops; + + if (test_vops) { + err = test_vops->integrity(&ctx, &start_addr); + if (err) { + pr_err("FIPS(%s): Error to update hash for func test\n", + __func__); + return -1; + } + } + +/* Dump bytes for HMAC */ +#ifdef FIPS_DEBUG_INTEGRITY + num_addresses = i; + for (i = 0; integrity_fmp_addrs[i].first && i < rows; i++) { + start_addr = integrity_fmp_addrs[i].first + get_kernel_offset(); + end_addr = integrity_fmp_addrs[i].last + get_kernel_offset(); + size = (uint32_t)(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 = hmac_sha256_final(&ctx, runtime_hmac); + if (err) { + pr_err("FIPS(%s): Error in finalize", __func__); + hmac_sha256_ctx_cleanup(&ctx); + return -1; + } + + hmac_sha256_ctx_cleanup(&ctx); + builtime_hmac = builtime_fmp_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 FMP RUNTIME : runtime hmac = ", + DUMP_PREFIX_NONE, 16, 1, runtime_hmac, sizeof(runtime_hmac), false); + print_hex_dump(KERN_INFO, "FIPS FMP RUNTIME : builtime_hmac = ", + DUMP_PREFIX_NONE, 16, 1, builtime_hmac, sizeof(runtime_hmac), false); +#endif + + if (!memcmp(builtime_hmac, runtime_hmac, sizeof(runtime_hmac))) { + pr_info("FIPS(%s): Integrity Check Passed", __func__); + return 0; + } + + pr_err("FIPS(%s): Integrity Check Failed", __func__); + return -1; +} +EXPORT_SYMBOL_GPL(do_fmp_integrity_check); diff --git a/drivers/crypto/fmp/fmp_fips_integrity.h b/drivers/crypto/fmp/fmp_fips_integrity.h new file mode 100644 index 000000000000..8106cbf12b81 --- /dev/null +++ b/drivers/crypto/fmp/fmp_fips_integrity.h @@ -0,0 +1,31 @@ +/* + * Exynos FMP integrity header + * + * Copyright (C) 2015 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __FMP_INTEGRITY_H__ +#define __FMP_INTEGRITY_H__ + +#include + +#define FIPS_HMAC_SIZE (32) +#define FIPS_FMP_ADDRS_SIZE (100) + +struct first_last { + aligned_u64 first; + aligned_u64 last; +}; + +extern const __u64 fmp_buildtime_address; +extern const struct first_last integrity_fmp_addrs[FIPS_FMP_ADDRS_SIZE]; +extern const __s8 builtime_fmp_hmac[FIPS_HMAC_SIZE]; + +int do_fmp_integrity_check(struct exynos_fmp *fmp); + +#endif diff --git a/drivers/crypto/fmp/fmp_fips_main.c b/drivers/crypto/fmp/fmp_fips_main.c new file mode 100644 index 000000000000..6ccad737956e --- /dev/null +++ b/drivers/crypto/fmp/fmp_fips_main.c @@ -0,0 +1,213 @@ +/* + * Exynos FMP test driver + * + * Copyright (C) 2015 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "fmp_fips_main.h" +#include "fmp_fips_fops.h" +#include "fmp_fips_selftest.h" +#include "fmp_fips_integrity.h" +#include "fmp_test.h" + +static const char pass[] = "passed"; +static const char fail[] = "failed"; + +enum fips_state { + FMP_FIPS_INIT_STATE, + FMP_FIPS_PROGRESS_STATE, + FMP_FIPS_ERR_STATE, + FMP_FIPS_SUCCESS_STATE +}; + +static enum fips_state fmp_fips_state = FMP_FIPS_INIT_STATE; + +bool in_fmp_fips_err(void) +{ + if (fmp_fips_state == FMP_FIPS_INIT_STATE || + fmp_fips_state == FMP_FIPS_ERR_STATE) + return true; + return false; +} + +static void set_fmp_fips_state(uint32_t val) +{ + fmp_fips_state = val; +} + +static ssize_t fmp_fips_result_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct exynos_fmp *fmp = dev_get_drvdata(dev); + + return snprintf(buf, sizeof(pass), "%s\n", fmp->result.overall ? pass : fail); +} + +static ssize_t fmp_fips_aes_xts_result_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct exynos_fmp *fmp = dev_get_drvdata(dev); + + return snprintf(buf, sizeof(pass), "%s\n", fmp->result.aes_xts ? pass : fail); +} + +static ssize_t fmp_fips_aes_cbc_result_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct exynos_fmp *fmp = dev_get_drvdata(dev); + + return snprintf(buf, sizeof(pass), "%s\n", fmp->result.aes_cbc ? pass : fail); +} + +static ssize_t fmp_fips_sha256_result_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct exynos_fmp *fmp = dev_get_drvdata(dev); + + return snprintf(buf, sizeof(pass), "%s\n", fmp->result.sha256 ? pass : fail); +} + +static ssize_t fmp_fips_hmac_result_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct exynos_fmp *fmp = dev_get_drvdata(dev); + + return snprintf(buf, sizeof(pass), "%s\n", fmp->result.hmac ? pass : fail); +} + +static ssize_t fmp_fips_integrity_result_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct exynos_fmp *fmp = dev_get_drvdata(dev); + + return snprintf(buf, sizeof(pass), "%s\n", fmp->result.integrity ? pass : fail); +} + +static DEVICE_ATTR(fmp_fips_status, 0444, fmp_fips_result_show, NULL); +static DEVICE_ATTR(aes_xts_status, 0444, fmp_fips_aes_xts_result_show, NULL); +static DEVICE_ATTR(aes_cbc_status, 0444, fmp_fips_aes_cbc_result_show, NULL); +static DEVICE_ATTR(sha256_status, 0444, fmp_fips_sha256_result_show, NULL); +static DEVICE_ATTR(hmac_status, 0444, fmp_fips_hmac_result_show, NULL); +static DEVICE_ATTR(integrity_status, 0444, fmp_fips_integrity_result_show, NULL); + +static struct attribute *fmp_fips_attr[] = { + &dev_attr_fmp_fips_status.attr, + &dev_attr_aes_xts_status.attr, + &dev_attr_aes_cbc_status.attr, + &dev_attr_sha256_status.attr, + &dev_attr_hmac_status.attr, + &dev_attr_integrity_status.attr, + NULL, +}; + +static struct attribute_group fmp_fips_attr_group = { + .name = "fmp-fips", + .attrs = fmp_fips_attr, +}; + +static const struct file_operations fmp_fips_fops = { + .owner = THIS_MODULE, + .open = fmp_fips_open, + .release = fmp_fips_release, + .unlocked_ioctl = fmp_fips_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = fmp_fips_compat_ioctl, +#endif +}; + +int exynos_fmp_fips_init(struct exynos_fmp *fmp) +{ + int ret; + + if (!fmp || !fmp->dev) { + pr_err("%s: Invalid exynos fmp dev\n", __func__); + return -EINVAL; + } + + set_fmp_fips_state(FMP_FIPS_INIT_STATE); + + fmp->miscdev.minor = MISC_DYNAMIC_MINOR; + fmp->miscdev.name = "fmp"; + fmp->miscdev.fops = &fmp_fips_fops; + ret = misc_register(&fmp->miscdev); + if (ret) { + dev_err(fmp->dev, "%s: Fail to register misc device. ret(%d)\n", + __func__, ret); + return -EINVAL; + } + + ret = sysfs_create_group(&fmp->dev->kobj, &fmp_fips_attr_group); + if (ret) { + dev_err(fmp->dev, "%s: Fail to create sysfs. ret(%d)\n", + __func__, ret); + return -EINVAL; + } + + set_fmp_fips_state(FMP_FIPS_SUCCESS_STATE); + + fmp->test_data = fmp_test_init(fmp); + if (!fmp->test_data) { + dev_err(fmp->dev, + "%s: fails to initialize fips test.\n", __func__); + ret = -EINVAL; + goto err; + } + + ret = do_fmp_selftest(fmp); + if (ret) { + dev_err(fmp->dev, "%s: self-tests for FMP failed\n", __func__); + goto err; + } else { + dev_info(fmp->dev, "%s: self-tests for FMP passed\n", __func__); + } + + /* TODO: Add integrity check */ +#ifdef CHECK_INTEGRITY + ret = do_fmp_integrity_check(fmp); + if (ret) { + dev_err(fmp->dev, "%s: integrity check for FMP failed\n", __func__); + fmp->result.integrity = 0; + goto err; + } else { + dev_info(fmp->dev, "%s: integrity check for FMP passed\n", __func__); + fmp->result.integrity = 1; + } +#else + fmp->result.integrity = 1; +#endif + set_fmp_fips_state(FMP_FIPS_SUCCESS_STATE); + fmp->result.overall = 1; + fmp_test_exit(fmp->test_data); + return 0; +err: +#if defined(CONFIG_NODE_FOR_SELFTEST_FAIL) + set_fmp_fips_state(FMP_FIPS_ERR_STATE); + fmp->result.overall = 0; + fmp_test_exit(fmp->test_data); + return 0; +#else + panic("%s: Panic due to FMP self test for FIPS KAT", __func__); +#endif + return -EINVAL; +} + +void exynos_fmp_fips_exit(struct exynos_fmp *fmp) +{ + sysfs_remove_group(&fmp->dev->kobj, &fmp_fips_attr_group); + misc_deregister(&fmp->miscdev); +} diff --git a/drivers/crypto/fmp/fmp_fips_main.h b/drivers/crypto/fmp/fmp_fips_main.h new file mode 100644 index 000000000000..fd8cbbc9730b --- /dev/null +++ b/drivers/crypto/fmp/fmp_fips_main.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2016 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef _FMP_FIPS_MAIN_H_ +#define _FMP_FIPS_MAIN_H_ + +#if defined(CONFIG_EXYNOS_FMP_FIPS) +int exynos_fmp_fips_init(struct exynos_fmp *fmp); +void exynos_fmp_fips_exit(struct exynos_fmp *fmp); +bool in_fmp_fips_err(void); +#else +inline int exynos_fmp_fips_init(struct exynos_fmp *fmp) +{ + return 0; +} + +inline void exynos_fmp_fips_exit(struct exynos_fmp *fmp) +{ +} + +inline bool in_fmp_fips_err(void) +{ + return 0; +} +#endif /* CONFIG_EXYNOS_FMP_FIPS */ +#endif /* _FMP_FIPS_MAIN_H_ */ diff --git a/drivers/crypto/fmp/fmp_fips_selftest.c b/drivers/crypto/fmp/fmp_fips_selftest.c new file mode 100644 index 000000000000..a9507f51f0b5 --- /dev/null +++ b/drivers/crypto/fmp/fmp_fips_selftest.c @@ -0,0 +1,1068 @@ +/* + * Exynos FMP selftest driver + * + * Copyright (C) 2016 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include + +#include + +#include "fmp_fips_info.h" +#include "fmp_test.h" +#include "fmp_fips_cipher.h" +#include "sha256.h" +#include "hmac-sha256.h" + +static const char *ALG_SHA256_FMP = "sha256"; +static const char *ALG_HMAC_SHA256_FMP = "hmac(sha256)"; + +#if 0 +/* + * Indexes into the xbuf to simulate cross-page access. + */ +#define IDX1 32 +#define IDX2 32400 +#define IDX3 1 +#define IDX4 8193 +#define IDX5 22222 +#define IDX6 17101 +#define IDX7 27333 +#define IDX8 3000 + +static unsigned int IDX[8] = { IDX1, IDX2, IDX3, IDX4, IDX5, IDX6, IDX7, IDX8 }; +#endif + +#define AES_XTS_ENC_TEST_VECTORS 5 +#define AES_CBC_ENC_TEST_VECTORS 4 + +static void hexdump(uint8_t *buf, uint32_t len) +{ + print_hex_dump(KERN_CONT, "", DUMP_PREFIX_OFFSET, + 16, 1, + buf, len, false); +} + +static int alloc_buf(char *buf[XBUFSIZE]) +{ + int i; + + for (i = 0; i < XBUFSIZE; i++) { + buf[i] = (void *)__get_free_page(GFP_KERNEL); + if (!buf[i]) + goto err_free_buf; + } + + return 0; + +err_free_buf: + while (i-- > 0) + free_page((unsigned long)buf[i]); + + return -ENOMEM; +} + +static void free_buf(char *buf[XBUFSIZE]) +{ + int i; + + for (i = 0; i < XBUFSIZE; i++) + free_page((unsigned long)buf[i]); +} + +static struct cipher_testvec aes_xts_enc_tv_template[] = { + /* http://grouper.ieee.org/groups/1619/email/pdf00086.pdf */ + { /* XTS-AES 1 */ + .key = "\xa1\xb9\x0c\xba\x3f\x06\xac\x35" + "\x3b\x2c\x34\x38\x76\x08\x17\x62" + "\x09\x09\x23\x02\x6e\x91\x77\x18" + "\x15\xf2\x9d\xab\x01\x93\x2f\x2f", + .klen = 32, + .iv = "\x4f\xae\xf7\x11\x7c\xda\x59\xc6" + "\x6e\x4b\x92\x01\x3e\x76\x8a\xd5", + .input = "\xeb\xab\xce\x95\xb1\x4d\x3c\x8d" + "\x6f\xb3\x50\x39\x07\x90\x31\x1c", + .ilen = 16, + .result = "\x77\x8a\xe8\xb4\x3c\xb9\x8d\x5a" + "\x82\x50\x81\xd5\xbe\x47\x1c\x63", + .rlen = 16, + }, { /* XTS-AES 2 */ + .key = "\x11\x11\x11\x11\x11\x11\x11\x11" + "\x11\x11\x11\x11\x11\x11\x11\x11" + "\x22\x22\x22\x22\x22\x22\x22\x22" + "\x22\x22\x22\x22\x22\x22\x22\x22", + .klen = 32, + .iv = "\x33\x33\x33\x33\x33\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "\x44\x44\x44\x44\x44\x44\x44\x44" + "\x44\x44\x44\x44\x44\x44\x44\x44" + "\x44\x44\x44\x44\x44\x44\x44\x44" + "\x44\x44\x44\x44\x44\x44\x44\x44", + .ilen = 32, + .result = "\xc4\x54\x18\x5e\x6a\x16\x93\x6e" + "\x39\x33\x40\x38\xac\xef\x83\x8b" + "\xfb\x18\x6f\xff\x74\x80\xad\xc4" + "\x28\x93\x82\xec\xd6\xd3\x94\xf0", + .rlen = 32, + }, { /* XTS-AES 3 */ + .key = "\xff\xfe\xfd\xfc\xfb\xfa\xf9\xf8" + "\xf7\xf6\xf5\xf4\xf3\xf2\xf1\xf0" + "\x22\x22\x22\x22\x22\x22\x22\x22" + "\x22\x22\x22\x22\x22\x22\x22\x22", + .klen = 32, + .iv = "\x33\x33\x33\x33\x33\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "\x44\x44\x44\x44\x44\x44\x44\x44" + "\x44\x44\x44\x44\x44\x44\x44\x44" + "\x44\x44\x44\x44\x44\x44\x44\x44" + "\x44\x44\x44\x44\x44\x44\x44\x44", + .ilen = 32, + .result = "\xaf\x85\x33\x6b\x59\x7a\xfc\x1a" + "\x90\x0b\x2e\xb2\x1e\xc9\x49\xd2" + "\x92\xdf\x4c\x04\x7e\x0b\x21\x53" + "\x21\x86\xa5\x97\x1a\x22\x7a\x89", + .rlen = 32, + }, { /* XTS-AES 4 */ + .key = "\x27\x18\x28\x18\x28\x45\x90\x45" + "\x23\x53\x60\x28\x74\x71\x35\x26" + "\x31\x41\x59\x26\x53\x58\x97\x93" + "\x23\x84\x62\x64\x33\x83\x27\x95", + .klen = 32, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + "\x20\x21\x22\x23\x24\x25\x26\x27" + "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" + "\x30\x31\x32\x33\x34\x35\x36\x37" + "\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" + "\x40\x41\x42\x43\x44\x45\x46\x47" + "\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" + "\x50\x51\x52\x53\x54\x55\x56\x57" + "\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" + "\x60\x61\x62\x63\x64\x65\x66\x67" + "\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" + "\x70\x71\x72\x73\x74\x75\x76\x77" + "\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f" + "\x80\x81\x82\x83\x84\x85\x86\x87" + "\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" + "\x90\x91\x92\x93\x94\x95\x96\x97" + "\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" + "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7" + "\xa8\xa9\xaa\xab\xac\xad\xae\xaf" + "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7" + "\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" + "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" + "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf" + "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7" + "\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" + "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7" + "\xe8\xe9\xea\xeb\xec\xed\xee\xef" + "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7" + "\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + "\x20\x21\x22\x23\x24\x25\x26\x27" + "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" + "\x30\x31\x32\x33\x34\x35\x36\x37" + "\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" + "\x40\x41\x42\x43\x44\x45\x46\x47" + "\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" + "\x50\x51\x52\x53\x54\x55\x56\x57" + "\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" + "\x60\x61\x62\x63\x64\x65\x66\x67" + "\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" + "\x70\x71\x72\x73\x74\x75\x76\x77" + "\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f" + "\x80\x81\x82\x83\x84\x85\x86\x87" + "\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" + "\x90\x91\x92\x93\x94\x95\x96\x97" + "\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" + "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7" + "\xa8\xa9\xaa\xab\xac\xad\xae\xaf" + "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7" + "\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" + "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" + "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf" + "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7" + "\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" + "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7" + "\xe8\xe9\xea\xeb\xec\xed\xee\xef" + "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7" + "\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff", + .ilen = 512, + .result = "\x27\xa7\x47\x9b\xef\xa1\xd4\x76" + "\x48\x9f\x30\x8c\xd4\xcf\xa6\xe2" + "\xa9\x6e\x4b\xbe\x32\x08\xff\x25" + "\x28\x7d\xd3\x81\x96\x16\xe8\x9c" + "\xc7\x8c\xf7\xf5\xe5\x43\x44\x5f" + "\x83\x33\xd8\xfa\x7f\x56\x00\x00" + "\x05\x27\x9f\xa5\xd8\xb5\xe4\xad" + "\x40\xe7\x36\xdd\xb4\xd3\x54\x12" + "\x32\x80\x63\xfd\x2a\xab\x53\xe5" + "\xea\x1e\x0a\x9f\x33\x25\x00\xa5" + "\xdf\x94\x87\xd0\x7a\x5c\x92\xcc" + "\x51\x2c\x88\x66\xc7\xe8\x60\xce" + "\x93\xfd\xf1\x66\xa2\x49\x12\xb4" + "\x22\x97\x61\x46\xae\x20\xce\x84" + "\x6b\xb7\xdc\x9b\xa9\x4a\x76\x7a" + "\xae\xf2\x0c\x0d\x61\xad\x02\x65" + "\x5e\xa9\x2d\xc4\xc4\xe4\x1a\x89" + "\x52\xc6\x51\xd3\x31\x74\xbe\x51" + "\xa1\x0c\x42\x11\x10\xe6\xd8\x15" + "\x88\xed\xe8\x21\x03\xa2\x52\xd8" + "\xa7\x50\xe8\x76\x8d\xef\xff\xed" + "\x91\x22\x81\x0a\xae\xb9\x9f\x91" + "\x72\xaf\x82\xb6\x04\xdc\x4b\x8e" + "\x51\xbc\xb0\x82\x35\xa6\xf4\x34" + "\x13\x32\xe4\xca\x60\x48\x2a\x4b" + "\xa1\xa0\x3b\x3e\x65\x00\x8f\xc5" + "\xda\x76\xb7\x0b\xf1\x69\x0d\xb4" + "\xea\xe2\x9c\x5f\x1b\xad\xd0\x3c" + "\x5c\xcf\x2a\x55\xd7\x05\xdd\xcd" + "\x86\xd4\x49\x51\x1c\xeb\x7e\xc3" + "\x0b\xf1\x2b\x1f\xa3\x5b\x91\x3f" + "\x9f\x74\x7a\x8a\xfd\x1b\x13\x0e" + "\x94\xbf\xf9\x4e\xff\xd0\x1a\x91" + "\x73\x5c\xa1\x72\x6a\xcd\x0b\x19" + "\x7c\x4e\x5b\x03\x39\x36\x97\xe1" + "\x26\x82\x6f\xb6\xbb\xde\x8e\xcc" + "\x1e\x08\x29\x85\x16\xe2\xc9\xed" + "\x03\xff\x3c\x1b\x78\x60\xf6\xde" + "\x76\xd4\xce\xcd\x94\xc8\x11\x98" + "\x55\xef\x52\x97\xca\x67\xe9\xf3" + "\xe7\xff\x72\xb1\xe9\x97\x85\xca" + "\x0a\x7e\x77\x20\xc5\xb3\x6d\xc6" + "\xd7\x2c\xac\x95\x74\xc8\xcb\xbc" + "\x2f\x80\x1e\x23\xe5\x6f\xd3\x44" + "\xb0\x7f\x22\x15\x4b\xeb\xa0\xf0" + "\x8c\xe8\x89\x1e\x64\x3e\xd9\x95" + "\xc9\x4d\x9a\x69\xc9\xf1\xb5\xf4" + "\x99\x02\x7a\x78\x57\x2a\xee\xbd" + "\x74\xd2\x0c\xc3\x98\x81\xc2\x13" + "\xee\x77\x0b\x10\x10\xe4\xbe\xa7" + "\x18\x84\x69\x77\xae\x11\x9f\x7a" + "\x02\x3a\xb5\x8c\xca\x0a\xd7\x52" + "\xaf\xe6\x56\xbb\x3c\x17\x25\x6a" + "\x9f\x6e\x9b\xf1\x9f\xdd\x5a\x38" + "\xfc\x82\xbb\xe8\x72\xc5\x53\x9e" + "\xdb\x60\x9e\xf4\xf7\x9c\x20\x3e" + "\xbb\x14\x0f\x2e\x58\x3c\xb2\xad" + "\x15\xb4\xaa\x5b\x65\x50\x16\xa8" + "\x44\x92\x77\xdb\xd4\x77\xef\x2c" + "\x8d\x6c\x01\x7d\xb7\x38\xb1\x8d" + "\xeb\x4a\x42\x7d\x19\x23\xce\x3f" + "\xf2\x62\x73\x57\x79\xa4\x18\xf2" + "\x0a\x28\x2d\xf9\x20\x14\x7b\xea" + "\xbe\x42\x1e\xe5\x31\x9d\x05\x68", + .rlen = 512, + }, { /* XTS-AES 10, XTS-AES-256, data unit 512 bytes */ + .key = "\x27\x18\x28\x18\x28\x45\x90\x45" + "\x23\x53\x60\x28\x74\x71\x35\x26" + "\x62\x49\x77\x57\x24\x70\x93\x69" + "\x99\x59\x57\x49\x66\x96\x76\x27" + "\x31\x41\x59\x26\x53\x58\x97\x93" + "\x23\x84\x62\x64\x33\x83\x27\x95" + "\x02\x88\x41\x97\x16\x93\x99\x37" + "\x51\x05\x82\x09\x74\x94\x45\x92", + .klen = 64, + .iv = "\xff\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + "\x20\x21\x22\x23\x24\x25\x26\x27" + "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" + "\x30\x31\x32\x33\x34\x35\x36\x37" + "\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" + "\x40\x41\x42\x43\x44\x45\x46\x47" + "\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" + "\x50\x51\x52\x53\x54\x55\x56\x57" + "\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" + "\x60\x61\x62\x63\x64\x65\x66\x67" + "\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" + "\x70\x71\x72\x73\x74\x75\x76\x77" + "\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f" + "\x80\x81\x82\x83\x84\x85\x86\x87" + "\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" + "\x90\x91\x92\x93\x94\x95\x96\x97" + "\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" + "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7" + "\xa8\xa9\xaa\xab\xac\xad\xae\xaf" + "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7" + "\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" + "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" + "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf" + "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7" + "\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" + "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7" + "\xe8\xe9\xea\xeb\xec\xed\xee\xef" + "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7" + "\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + "\x20\x21\x22\x23\x24\x25\x26\x27" + "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" + "\x30\x31\x32\x33\x34\x35\x36\x37" + "\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" + "\x40\x41\x42\x43\x44\x45\x46\x47" + "\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" + "\x50\x51\x52\x53\x54\x55\x56\x57" + "\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" + "\x60\x61\x62\x63\x64\x65\x66\x67" + "\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" + "\x70\x71\x72\x73\x74\x75\x76\x77" + "\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f" + "\x80\x81\x82\x83\x84\x85\x86\x87" + "\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" + "\x90\x91\x92\x93\x94\x95\x96\x97" + "\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" + "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7" + "\xa8\xa9\xaa\xab\xac\xad\xae\xaf" + "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7" + "\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" + "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" + "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf" + "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7" + "\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" + "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7" + "\xe8\xe9\xea\xeb\xec\xed\xee\xef" + "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7" + "\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff", + .ilen = 512, + .result = "\x1c\x3b\x3a\x10\x2f\x77\x03\x86" + "\xe4\x83\x6c\x99\xe3\x70\xcf\x9b" + "\xea\x00\x80\x3f\x5e\x48\x23\x57" + "\xa4\xae\x12\xd4\x14\xa3\xe6\x3b" + "\x5d\x31\xe2\x76\xf8\xfe\x4a\x8d" + "\x66\xb3\x17\xf9\xac\x68\x3f\x44" + "\x68\x0a\x86\xac\x35\xad\xfc\x33" + "\x45\xbe\xfe\xcb\x4b\xb1\x88\xfd" + "\x57\x76\x92\x6c\x49\xa3\x09\x5e" + "\xb1\x08\xfd\x10\x98\xba\xec\x70" + "\xaa\xa6\x69\x99\xa7\x2a\x82\xf2" + "\x7d\x84\x8b\x21\xd4\xa7\x41\xb0" + "\xc5\xcd\x4d\x5f\xff\x9d\xac\x89" + "\xae\xba\x12\x29\x61\xd0\x3a\x75" + "\x71\x23\xe9\x87\x0f\x8a\xcf\x10" + "\x00\x02\x08\x87\x89\x14\x29\xca" + "\x2a\x3e\x7a\x7d\x7d\xf7\xb1\x03" + "\x55\x16\x5c\x8b\x9a\x6d\x0a\x7d" + "\xe8\xb0\x62\xc4\x50\x0d\xc4\xcd" + "\x12\x0c\x0f\x74\x18\xda\xe3\xd0" + "\xb5\x78\x1c\x34\x80\x3f\xa7\x54" + "\x21\xc7\x90\xdf\xe1\xde\x18\x34" + "\xf2\x80\xd7\x66\x7b\x32\x7f\x6c" + "\x8c\xd7\x55\x7e\x12\xac\x3a\x0f" + "\x93\xec\x05\xc5\x2e\x04\x93\xef" + "\x31\xa1\x2d\x3d\x92\x60\xf7\x9a" + "\x28\x9d\x6a\x37\x9b\xc7\x0c\x50" + "\x84\x14\x73\xd1\xa8\xcc\x81\xec" + "\x58\x3e\x96\x45\xe0\x7b\x8d\x96" + "\x70\x65\x5b\xa5\xbb\xcf\xec\xc6" + "\xdc\x39\x66\x38\x0a\xd8\xfe\xcb" + "\x17\xb6\xba\x02\x46\x9a\x02\x0a" + "\x84\xe1\x8e\x8f\x84\x25\x20\x70" + "\xc1\x3e\x9f\x1f\x28\x9b\xe5\x4f" + "\xbc\x48\x14\x57\x77\x8f\x61\x60" + "\x15\xe1\x32\x7a\x02\xb1\x40\xf1" + "\x50\x5e\xb3\x09\x32\x6d\x68\x37" + "\x8f\x83\x74\x59\x5c\x84\x9d\x84" + "\xf4\xc3\x33\xec\x44\x23\x88\x51" + "\x43\xcb\x47\xbd\x71\xc5\xed\xae" + "\x9b\xe6\x9a\x2f\xfe\xce\xb1\xbe" + "\xc9\xde\x24\x4f\xbe\x15\x99\x2b" + "\x11\xb7\x7c\x04\x0f\x12\xbd\x8f" + "\x6a\x97\x5a\x44\xa0\xf9\x0c\x29" + "\xa9\xab\xc3\xd4\xd8\x93\x92\x72" + "\x84\xc5\x87\x54\xcc\xe2\x94\x52" + "\x9f\x86\x14\xdc\xd2\xab\xa9\x91" + "\x92\x5f\xed\xc4\xae\x74\xff\xac" + "\x6e\x33\x3b\x93\xeb\x4a\xff\x04" + "\x79\xda\x9a\x41\x0e\x44\x50\xe0" + "\xdd\x7a\xe4\xc6\xe2\x91\x09\x00" + "\x57\x5d\xa4\x01\xfc\x07\x05\x9f" + "\x64\x5e\x8b\x7e\x9b\xfd\xef\x33" + "\x94\x30\x54\xff\x84\x01\x14\x93" + "\xc2\x7b\x34\x29\xea\xed\xb4\xed" + "\x53\x76\x44\x1a\x77\xed\x43\x85" + "\x1a\xd7\x7f\x16\xf5\x41\xdf\xd2" + "\x69\xd5\x0d\x6a\x5f\x14\xfb\x0a" + "\xab\x1c\xbb\x4c\x15\x50\xbe\x97" + "\xf7\xab\x40\x66\x19\x3c\x4c\xaa" + "\x77\x3d\xad\x38\x01\x4b\xd2\x09" + "\x2f\xa7\x55\xc8\x24\xbb\x5e\x54" + "\xc4\xf3\x6f\xfd\xa9\xfc\xea\x70" + "\xb9\xc6\xe6\x93\xe1\x48\xc1\x51", + .rlen = 512, + } +}; + +static struct cipher_testvec aes_cbc_enc_tv_template[] = { + { /* From RFC 3602 */ + .key = "\x06\xa9\x21\x40\x36\xb8\xa1\x5b" + "\x51\x2e\x03\xd5\x34\x12\x00\x06", + .klen = 16, + .iv = "\x3d\xaf\xba\x42\x9d\x9e\xb4\x30" + "\xb4\x22\xda\x80\x2c\x9f\xac\x41", + .input = "Single block msg", + .ilen = 16, + .result = "\xe3\x53\x77\x9c\x10\x79\xae\xb8" + "\x27\x08\x94\x2d\xbe\x77\x18\x1a", + .rlen = 16, + }, { + .key = "\xc2\x86\x69\x6d\x88\x7c\x9a\xa0" + "\x61\x1b\xbb\x3e\x20\x25\xa4\x5a", + .klen = 16, + .iv = "\x56\x2e\x17\x99\x6d\x09\x3d\x28" + "\xdd\xb3\xba\x69\x5a\x2e\x6f\x58", + .input = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + .ilen = 32, + .result = "\xd2\x96\xcd\x94\xc2\xcc\xcf\x8a" + "\x3a\x86\x30\x28\xb5\xe1\xdc\x0a" + "\x75\x86\x60\x2d\x25\x3c\xff\xf9" + "\x1b\x82\x66\xbe\xa6\xd6\x1a\xb1", + .rlen = 32, + }, { + .key = "\x60\x3d\xeb\x10\x15\xca\x71\xbe" + "\x2b\x73\xae\xf0\x85\x7d\x77\x81" + "\x1f\x35\x2c\x07\x3b\x61\x08\xd7" + "\x2d\x98\x10\xa3\x09\x14\xdf\xf4", + .klen = 32, + .iv = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + .input = "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96" + "\xe9\x3d\x7e\x11\x73\x93\x17\x2a" + "\xae\x2d\x8a\x57\x1e\x03\xac\x9c" + "\x9e\xb7\x6f\xac\x45\xaf\x8e\x51" + "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11" + "\xe5\xfb\xc1\x19\x1a\x0a\x52\xef" + "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17" + "\xad\x2b\x41\x7b\xe6\x6c\x37\x10", + .ilen = 64, + .result = "\xf5\x8c\x4c\x04\xd6\xe5\xf1\xba" + "\x77\x9e\xab\xfb\x5f\x7b\xfb\xd6" + "\x9c\xfc\x4e\x96\x7e\xdb\x80\x8d" + "\x67\x9f\x77\x7b\xc6\x70\x2c\x7d" + "\x39\xf2\x33\x69\xa9\xd9\xba\xcf" + "\xa5\x30\xe2\x63\x04\x23\x14\x61" + "\xb2\xeb\x05\xe2\xc3\x9b\xe9\xfc" + "\xda\x6c\x19\x07\x8c\x6a\x9d\x1b", + .rlen = 64, + }, { /* Generated with Crypto++ */ + .key = "\xC9\x83\xA6\xC9\xEC\x0F\x32\x55" + "\x0F\x32\x55\x78\x9B\xBE\x78\x9B" + "\xBE\xE1\x04\x27\xE1\x04\x27\x4A" + "\x6D\x90\x4A\x6D\x90\xB3\xD6\xF9", + .klen = 32, + .iv = "\xE7\x82\x1D\xB8\x53\x11\xAC\x47" + "\xE2\x7D\x18\xD6\x71\x0C\xA7\x42", + .input = "\x50\xB9\x22\xAE\x17\x80\x0C\x75" + "\xDE\x47\xD3\x3C\xA5\x0E\x9A\x03" + "\x6C\xF8\x61\xCA\x33\xBF\x28\x91" + "\x1D\x86\xEF\x58\xE4\x4D\xB6\x1F" + "\xAB\x14\x7D\x09\x72\xDB\x44\xD0" + "\x39\xA2\x0B\x97\x00\x69\xF5\x5E" + "\xC7\x30\xBC\x25\x8E\x1A\x83\xEC" + "\x55\xE1\x4A\xB3\x1C\xA8\x11\x7A" + "\x06\x6F\xD8\x41\xCD\x36\x9F\x08" + "\x94\xFD\x66\xF2\x5B\xC4\x2D\xB9" + "\x22\x8B\x17\x80\xE9\x52\xDE\x47" + "\xB0\x19\xA5\x0E\x77\x03\x6C\xD5" + "\x3E\xCA\x33\x9C\x05\x91\xFA\x63" + "\xEF\x58\xC1\x2A\xB6\x1F\x88\x14" + "\x7D\xE6\x4F\xDB\x44\xAD\x16\xA2" + "\x0B\x74\x00\x69\xD2\x3B\xC7\x30" + "\x99\x02\x8E\xF7\x60\xEC\x55\xBE" + "\x27\xB3\x1C\x85\x11\x7A\xE3\x4C" + "\xD8\x41\xAA\x13\x9F\x08\x71\xFD" + "\x66\xCF\x38\xC4\x2D\x96\x22\x8B" + "\xF4\x5D\xE9\x52\xBB\x24\xB0\x19" + "\x82\x0E\x77\xE0\x49\xD5\x3E\xA7" + "\x10\x9C\x05\x6E\xFA\x63\xCC\x35" + "\xC1\x2A\x93\x1F\x88\xF1\x5A\xE6" + "\x4F\xB8\x21\xAD\x16\x7F\x0B\x74" + "\xDD\x46\xD2\x3B\xA4\x0D\x99\x02" + "\x6B\xF7\x60\xC9\x32\xBE\x27\x90" + "\x1C\x85\xEE\x57\xE3\x4C\xB5\x1E" + "\xAA\x13\x7C\x08\x71\xDA\x43\xCF" + "\x38\xA1\x0A\x96\xFF\x68\xF4\x5D" + "\xC6\x2F\xBB\x24\x8D\x19\x82\xEB" + "\x54\xE0\x49\xB2\x1B\xA7\x10\x79" + "\x05\x6E\xD7\x40\xCC\x35\x9E\x07" + "\x93\xFC\x65\xF1\x5A\xC3\x2C\xB8" + "\x21\x8A\x16\x7F\xE8\x51\xDD\x46" + "\xAF\x18\xA4\x0D\x76\x02\x6B\xD4" + "\x3D\xC9\x32\x9B\x04\x90\xF9\x62" + "\xEE\x57\xC0\x29\xB5\x1E\x87\x13" + "\x7C\xE5\x4E\xDA\x43\xAC\x15\xA1" + "\x0A\x73\xFF\x68\xD1\x3A\xC6\x2F" + "\x98\x01\x8D\xF6\x5F\xEB\x54\xBD" + "\x26\xB2\x1B\x84\x10\x79\xE2\x4B" + "\xD7\x40\xA9\x12\x9E\x07\x70\xFC" + "\x65\xCE\x37\xC3\x2C\x95\x21\x8A" + "\xF3\x5C\xE8\x51\xBA\x23\xAF\x18" + "\x81\x0D\x76\xDF\x48\xD4\x3D\xA6" + "\x0F\x9B\x04\x6D\xF9\x62\xCB\x34" + "\xC0\x29\x92\x1E\x87\xF0\x59\xE5" + "\x4E\xB7\x20\xAC\x15\x7E\x0A\x73" + "\xDC\x45\xD1\x3A\xA3\x0C\x98\x01" + "\x6A\xF6\x5F\xC8\x31\xBD\x26\x8F" + "\x1B\x84\xED\x56\xE2\x4B\xB4\x1D" + "\xA9\x12\x7B\x07\x70\xD9\x42\xCE" + "\x37\xA0\x09\x95\xFE\x67\xF3\x5C" + "\xC5\x2E\xBA\x23\x8C\x18\x81\xEA" + "\x53\xDF\x48\xB1\x1A\xA6\x0F\x78" + "\x04\x6D\xD6\x3F\xCB\x34\x9D\x06" + "\x92\xFB\x64\xF0\x59\xC2\x2B\xB7" + "\x20\x89\x15\x7E\xE7\x50\xDC\x45" + "\xAE\x17\xA3\x0C\x75\x01\x6A\xD3" + "\x3C\xC8\x31\x9A\x03\x8F\xF8\x61" + "\xED\x56\xBF\x28\xB4\x1D\x86\x12", + .ilen = 496, + .result = "\xEA\x65\x8A\x19\xB0\x66\xC1\x3F" + "\xCE\xF1\x97\x75\xC1\xFD\xB5\xAF" + "\x52\x65\xF7\xFF\xBC\xD8\x2D\x9F" + "\x2F\xB9\x26\x9B\x6F\x10\xB7\xB8" + "\x26\xA1\x02\x46\xA2\xAD\xC6\xC0" + "\x11\x15\xFF\x6D\x1E\x82\x04\xA6" + "\xB1\x74\xD1\x08\x13\xFD\x90\x7C" + "\xF5\xED\xD3\xDB\x5A\x0A\x0C\x2F" + "\x0A\x70\xF1\x88\x07\xCF\x21\x26" + "\x40\x40\x8A\xF5\x53\xF7\x24\x4F" + "\x83\x38\x43\x5F\x08\x99\xEB\xE3" + "\xDC\x02\x64\x67\x50\x6E\x15\xC3" + "\x01\x1A\xA0\x81\x13\x65\xA6\x73" + "\x71\xA6\x3B\x91\x83\x77\xBE\xFA" + "\xDB\x71\x73\xA6\xC1\xAE\x43\xC3" + "\x36\xCE\xD6\xEB\xF9\x30\x1C\x4F" + "\x80\x38\x5E\x9C\x6E\xAB\x98\x2F" + "\x53\xAF\xCF\xC8\x9A\xB8\x86\x43" + "\x3E\x86\xE7\xA1\xF4\x2F\x30\x40" + "\x03\xA8\x6C\x50\x42\x9F\x77\x59" + "\x89\xA0\xC5\xEC\x9A\xB8\xDD\x99" + "\x16\x24\x02\x07\x48\xAE\xF2\x31" + "\x34\x0E\xC3\x85\xFE\x1C\x95\x99" + "\x87\x58\x98\x8B\xE7\xC6\xC5\x70" + "\x73\x81\x07\x7C\x56\x2F\xD8\x1B" + "\xB7\xB9\x2B\xAB\xE3\x01\x87\x0F" + "\xD8\xBB\xC0\x0D\xAC\x2C\x2F\x98" + "\x3C\x0B\xA2\x99\x4A\x8C\xF7\x04" + "\xE0\xE0\xCF\xD1\x81\x5B\xFE\xF5" + "\x24\x04\xFD\xB8\xDF\x13\xD8\xCD" + "\xF1\xE3\x3D\x98\x50\x02\x77\x9E" + "\xBC\x22\xAB\xFA\xC2\x43\x1F\x66" + "\x20\x02\x23\xDA\xDF\xA0\x89\xF6" + "\xD8\xF3\x45\x24\x53\x6F\x16\x77" + "\x02\x3E\x7B\x36\x5F\xA0\x3B\x78" + "\x63\xA2\xBD\xB5\xA4\xCA\x1E\xD3" + "\x57\xBC\x0B\x9F\x43\x51\x28\x4F" + "\x07\x50\x6C\x68\x12\x07\xCF\xFA" + "\x6B\x72\x0B\xEB\xF8\x88\x90\x2C" + "\x7E\xF5\x91\xD1\x03\xD8\xD5\xBD" + "\x22\x39\x7B\x16\x03\x01\x69\xAF" + "\x3D\x38\x66\x28\x0C\xBE\x5B\xC5" + "\x03\xB4\x2F\x51\x8A\x56\x17\x2B" + "\x88\x42\x6D\x40\x68\x8F\xD0\x11" + "\x19\xF9\x1F\x43\x79\x95\x31\xFA" + "\x28\x7A\x3D\xF7\x66\xEB\xEF\xAC" + "\x06\xB2\x01\xAD\xDB\x68\xDB\xEC" + "\x8D\x53\x6E\x72\x68\xA3\xC7\x63" + "\x43\x2B\x78\xE0\x04\x29\x8F\x72" + "\xB2\x2C\xE6\x84\x03\x30\x6D\xCD" + "\x26\x92\x37\xE1\x2F\xBB\x8B\x9D" + "\xE4\x4C\xF6\x93\xBC\xD9\xAD\x44" + "\x52\x65\xC7\xB0\x0E\x3F\x0E\x61" + "\x56\x5D\x1C\x6D\xA7\x05\x2E\xBC" + "\x58\x08\x15\xAB\x12\xAB\x17\x4A" + "\x5E\x1C\xF2\xCD\xB8\xA2\xAE\xFB" + "\x9B\x2E\x0E\x85\x34\x80\x0E\x3F" + "\x4C\xB8\xDB\xCE\x1C\x90\xA1\x61" + "\x6C\x69\x09\x35\x9E\xD4\xF4\xAD" + "\xBC\x06\x41\xE3\x01\xB4\x4E\x0A" + "\xE0\x1F\x91\xF8\x82\x96\x2D\x65" + "\xA3\xAA\x13\xCC\x50\xFF\x7B\x02", + .rlen = 496, + }, +}; + +/* + * SHA256 test vectors from from NIST + */ +#define SHA256_TEST_VECTORS 2 + +static struct hash_testvec sha256_tv_template[] = { + { + .plaintext = "abc", + .psize = 3, + .digest = "\xba\x78\x16\xbf\x8f\x01\xcf\xea" + "\x41\x41\x40\xde\x5d\xae\x22\x23" + "\xb0\x03\x61\xa3\x96\x17\x7a\x9c" + "\xb4\x10\xff\x61\xf2\x00\x15\xad", + }, { + .plaintext = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + .psize = 56, + .digest = "\x24\x8d\x6a\x61\xd2\x06\x38\xb8" + "\xe5\xc0\x26\x93\x0c\x3e\x60\x39" + "\xa3\x3c\xe4\x59\x64\xff\x21\x67" + "\xf6\xec\xed\xd4\x19\xdb\x06\xc1", + .np = 2, + .tap = { 28, 28 } + }, +}; + +/* + * HMAC-SHA256 test vectors from + * draft-ietf-ipsec-ciph-sha-256-01.txt + */ +#define HMAC_SHA256_TEST_VECTORS 10 + +static struct hash_testvec hmac_sha256_tv_template[] = { + { + .key = "\x01\x02\x03\x04\x05\x06\x07\x08" + "\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10" + "\x11\x12\x13\x14\x15\x16\x17\x18" + "\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20", + .ksize = 32, + .plaintext = "abc", + .psize = 3, + .digest = "\xa2\x1b\x1f\x5d\x4c\xf4\xf7\x3a" + "\x4d\xd9\x39\x75\x0f\x7a\x06\x6a" + "\x7f\x98\xcc\x13\x1c\xb1\x6a\x66" + "\x92\x75\x90\x21\xcf\xab\x81\x81", + }, { + .key = "\x01\x02\x03\x04\x05\x06\x07\x08" + "\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10" + "\x11\x12\x13\x14\x15\x16\x17\x18" + "\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20", + .ksize = 32, + .plaintext = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + .psize = 56, + .digest = "\x10\x4f\xdc\x12\x57\x32\x8f\x08" + "\x18\x4b\xa7\x31\x31\xc5\x3c\xae" + "\xe6\x98\xe3\x61\x19\x42\x11\x49" + "\xea\x8c\x71\x24\x56\x69\x7d\x30", + }, { + .key = "\x01\x02\x03\x04\x05\x06\x07\x08" + "\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10" + "\x11\x12\x13\x14\x15\x16\x17\x18" + "\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20", + .ksize = 32, + .plaintext = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + .psize = 112, + .digest = "\x47\x03\x05\xfc\x7e\x40\xfe\x34" + "\xd3\xee\xb3\xe7\x73\xd9\x5a\xab" + "\x73\xac\xf0\xfd\x06\x04\x47\xa5" + "\xeb\x45\x95\xbf\x33\xa9\xd1\xa3", + }, { + .key = "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b" + "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b" + "\x0b\x0b\x0b\x0b\x0b\x0b", + .ksize = 32, + .plaintext = "Hi There", + .psize = 8, + .digest = "\x19\x8a\x60\x7e\xb4\x4b\xfb\xc6" + "\x99\x03\xa0\xf1\xcf\x2b\xbd\xc5" + "\xba\x0a\xa3\xf3\xd9\xae\x3c\x1c" + "\x7a\x3b\x16\x96\xa0\xb6\x8c\xf7", + }, { + .key = "Jefe", + .ksize = 4, + .plaintext = "what do ya want for nothing?", + .psize = 28, + .digest = "\x5b\xdc\xc1\x46\xbf\x60\x75\x4e" + "\x6a\x04\x24\x26\x08\x95\x75\xc7" + "\x5a\x00\x3f\x08\x9d\x27\x39\x83" + "\x9d\xec\x58\xb9\x64\xec\x38\x43", + .np = 2, + .tap = { 14, 14 } + }, { + .key = "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa", + .ksize = 32, + .plaintext = "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd" + "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd" + "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd" + "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd", + .psize = 50, + .digest = "\xcd\xcb\x12\x20\xd1\xec\xcc\xea" + "\x91\xe5\x3a\xba\x30\x92\xf9\x62" + "\xe5\x49\xfe\x6c\xe9\xed\x7f\xdc" + "\x43\x19\x1f\xbd\xe4\x5c\x30\xb0", + }, { + .key = "\x01\x02\x03\x04\x05\x06\x07\x08" + "\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10" + "\x11\x12\x13\x14\x15\x16\x17\x18" + "\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20" + "\x21\x22\x23\x24\x25", + .ksize = 37, + .plaintext = "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd", + .psize = 50, + .digest = "\xd4\x63\x3c\x17\xf6\xfb\x8d\x74" + "\x4c\x66\xde\xe0\xf8\xf0\x74\x55" + "\x6e\xc4\xaf\x55\xef\x07\x99\x85" + "\x41\x46\x8e\xb4\x9b\xd2\xe9\x17", + }, { + .key = "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c" + "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c" + "\x0c\x0c\x0c\x0c\x0c\x0c", + .ksize = 32, + .plaintext = "Test With Truncation", + .psize = 20, + .digest = "\x75\x46\xaf\x01\x84\x1f\xc0\x9b" + "\x1a\xb9\xc3\x74\x9a\x5f\x1c\x17" + "\xd4\xf5\x89\x66\x8a\x58\x7b\x27" + "\x00\xa9\xc9\x7c\x11\x93\xcf\x42", + }, { + .key = "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa", + .ksize = 80, + .plaintext = "Test Using Larger Than Block-Size Key - Hash Key First", + .psize = 54, + .digest = "\x69\x53\x02\x5e\xd9\x6f\x0c\x09" + "\xf8\x0a\x96\xf7\x8e\x65\x38\xdb" + "\xe2\xe7\xb8\x20\xe3\xdd\x97\x0e" + "\x7d\xdd\x39\x09\x1b\x32\x35\x2f", + }, { + .key = "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa", + .ksize = 80, + .plaintext = "Test Using Larger Than Block-Size Key and Larger Than " + "One Block-Size Data", + .psize = 73, + .digest = "\x63\x55\xac\x22\xe8\x90\xd0\xa3" + "\xc8\x48\x1a\x5c\xa4\x82\x5b\xc8" + "\x84\xd3\xe7\xa1\xff\x98\xa2\xfc" + "\x2a\xc7\xd8\xe0\x64\xc3\xb2\xe6", + }, +}; + +struct fmp_test_result { + struct completion completion; + int err; +}; + +static int selftest_sha256(struct exynos_fmp *fmp) +{ + int i; + int ret; + unsigned char buf[SHA256_DIGEST_LENGTH]; + unsigned char temp_digest[SHA256_DIGEST_LENGTH]; + struct exynos_fmp_fips_test_vops *test_vops; + + test_vops = (struct exynos_fmp_fips_test_vops *)fmp->test_vops; + + for (i = 0; i < SHA256_TEST_VECTORS; i++) { + if (0 != sha256(sha256_tv_template[i].plaintext, sha256_tv_template[i].psize, buf)) + return -EINVAL; + + memcpy(temp_digest, sha256_tv_template[i].digest, SHA256_DIGEST_LENGTH); + if (test_vops) { + ret = test_vops->hmac_sha256(temp_digest, "sha256"); + if (ret) { + pr_err("%s: Fail to set digest for func test. ret(%d)\n", + __func__, ret); + return -EINVAL; + } + } + + ret = memcmp(buf, temp_digest, SHA256_DIGEST_LENGTH); + if (ret) { + print_hex_dump_bytes("FIPS SHA256 REQ: ", DUMP_PREFIX_NONE, + sha256_tv_template[i].digest, + SHA256_DIGEST_LENGTH); + print_hex_dump_bytes("FIPS SHA256 RES: ", DUMP_PREFIX_NONE, + buf, + SHA256_DIGEST_LENGTH); + return -EINVAL; + } + } + return ret; +} + +static int selftest_hmac_sha256(struct exynos_fmp *fmp) +{ + int i; + int ret; + unsigned char buf[SHA256_DIGEST_LENGTH]; + unsigned char temp_digest[SHA256_DIGEST_LENGTH]; + struct exynos_fmp_fips_test_vops *test_vops; + + test_vops = (struct exynos_fmp_fips_test_vops *)fmp->test_vops; + + for (i = 0; i < HMAC_SHA256_TEST_VECTORS; i++) { + if (0 != hmac_sha256(hmac_sha256_tv_template[i].key, + hmac_sha256_tv_template[i].ksize, + hmac_sha256_tv_template[i].plaintext, + hmac_sha256_tv_template[i].psize, + buf)) + return -EINVAL; + + memcpy(temp_digest, hmac_sha256_tv_template[i].digest, + SHA256_DIGEST_LENGTH); + if (test_vops) { + ret = + test_vops->hmac_sha256(temp_digest, "hmac-sha256"); + if (ret) { + pr_err("%s: Fail to set digest for func test. ret(%d)\n", + __func__, ret); + return -EINVAL; + } + } + + ret = memcmp(buf, temp_digest, SHA256_DIGEST_LENGTH); + if (ret) { + print_hex_dump_bytes("FIPS HMAC-SHA256 REQ: ", + DUMP_PREFIX_NONE, + hmac_sha256_tv_template[i].digest, + SHA256_DIGEST_LENGTH); + print_hex_dump_bytes("FIPS HMAC-SHA256 RES: ", + DUMP_PREFIX_NONE, + buf, SHA256_DIGEST_LENGTH); + return -EINVAL; + } + } + return ret; +} + +static int fmp_test_run(struct exynos_fmp *fmp, struct cipher_testvec *template, + const int idx, uint8_t *data, uint32_t len, + const int mode, uint32_t write) +{ + int ret = 0; + char *temp_key; + struct exynos_fmp_fips_test_vops *test_vops; + + temp_key = kzalloc(template->klen, GFP_KERNEL); + if (!temp_key) { + dev_err(fmp->dev, "%s: Fail to alloc key buffer\n", __func__); + return -ENOMEM; + } + memcpy(temp_key, template->key, template->klen); + + test_vops = (struct exynos_fmp_fips_test_vops *)fmp->test_vops; + if (test_vops) { + ret = test_vops->aes(mode, temp_key, template->klen); + if (ret) { + dev_err(fmp->dev, "%s: Fail to set key for func test. ret(%d)\n", + __func__, ret); + goto err; + } + } + + ret = fmp_cipher_set_key(fmp->test_data, temp_key, template->klen); + if (ret) { + dev_err(fmp->dev, "%s: Fail to set key. ret(%d)\n", + __func__, ret); + goto err; + } + + ret = fmp_cipher_set_iv(fmp->test_data, template->iv, 16); + if (ret) { + dev_err(fmp->dev, "%s: Fail to set iv. ret(%d)\n", __func__, ret); + goto err; + } + + ret = fmp_cipher_run(fmp, fmp->test_data, data, len, + (mode == BYPASS_MODE) ? 1 : 0, write, NULL, + &fmp->test_data->ci); + if (ret) { + dev_err(fmp->dev, "%s: Fail to run. ret(%d)\n", __func__, ret); + goto err; + } +err: + kzfree(temp_key); + return ret; +} + +static int test_cipher(struct exynos_fmp *fmp, int mode, + struct cipher_testvec *template, uint32_t tcount) +{ + int ret; + uint32_t idx; + char *inbuf[XBUFSIZE]; + char *outbuf[XBUFSIZE]; + void *indata, *outdata; + + if (alloc_buf(inbuf)) { + dev_err(fmp->dev, "%s: Fail to alloc input buf.\n", __func__); + goto err_alloc_inbuf; + } + + if (alloc_buf(outbuf)) { + dev_err(fmp->dev, "%s: Fail to alloc output buf.\n", __func__); + goto err_alloc_outbuf; + } + + for (idx = 0; idx < tcount; idx++) { + if (template[idx].np) + continue; + + if (WARN_ON(template[idx].ilen > PAGE_SIZE)) { + dev_err(fmp->dev, "%s: Invalid input length. ilen (%d)\n", + __func__, template[idx].ilen); + goto err_ilen; + } + + indata = inbuf[0]; + outdata = outbuf[0]; + memset(indata, 0, FMP_BLK_SIZE); + memcpy(indata, template[idx].input, template[idx].ilen); + + ret = fmp_test_run(fmp, &template[idx], idx, indata, + template[idx].ilen, mode, WRITE_MODE); + if (ret) { + dev_err(fmp->dev, "%s: Fail to run fmp encrypt write. ret(%d)\n", + __func__, ret); + goto err_aes_run; + } + + memset(outdata, 0, FMP_BLK_SIZE); + ret = fmp_test_run(fmp, &template[idx], idx, outdata, + template[idx].ilen, BYPASS_MODE, READ_MODE); + if (ret) { + dev_err(fmp->dev, "%s: Fail to run fmp bypass read. ret(%d)\n", + __func__, ret); + goto err_aes_run; + } + + if (memcmp(outdata, template[idx].result, template[idx].rlen)) { + dev_err(fmp->dev, "%s: Fail to compare encrypted result.\n", + __func__); + hexdump(indata, template[idx].rlen); + hexdump(outdata, template[idx].rlen); + goto err_cmp; + } + + ret = fmp_test_run(fmp, &template[idx], idx, outdata, + template[idx].ilen, BYPASS_MODE, WRITE_MODE); + if (ret) { + dev_err(fmp->dev, "%s: Fail to run fmp bypass write. ret(%d)\n", + __func__, ret); + goto err_aes_run; + } + + memset(indata, 0, FMP_BLK_SIZE); + ret = fmp_test_run(fmp, &template[idx], idx, indata, + template[idx].ilen, mode, READ_MODE); + if (ret) { + dev_err(fmp->dev, "%s: Fail to run fmp decrypt read. ret(%d)\n", + __func__, ret); + goto err_aes_run; + } + + if (memcmp(indata, template[idx].input, template[idx].rlen)) { + dev_err(fmp->dev, "%s: Fail to compare decrypted result.\n", + __func__); + hexdump(outdata, template[idx].rlen); + goto err_cmp; + } + } + + free_buf(inbuf); + free_buf(outbuf); + + return 0; + +err_ilen: +err_aes_run: +err_cmp: + free_buf(outbuf); +err_alloc_outbuf: + free_buf(inbuf); +err_alloc_inbuf: + + return -1; +} + +int do_fmp_selftest(struct exynos_fmp *fmp) +{ + int ret; + struct cipher_test_suite xts_cipher; + struct cipher_test_suite cbc_cipher; + + if (!fmp || !fmp->dev) { + pr_err("%s: Invalid exynos fmp dev\n", __func__); + return -EINVAL; + } + + /* Self test for AES XTS mode */ + fmp->test_data->ci.algo_mode = EXYNOS_FMP_ALGO_MODE_AES_XTS; + xts_cipher.enc.vecs = aes_xts_enc_tv_template; + xts_cipher.enc.count = AES_XTS_ENC_TEST_VECTORS; + ret = test_cipher(fmp, XTS_MODE, xts_cipher.enc.vecs, xts_cipher.enc.count); + if (ret) { + dev_err(fmp->dev, "FIPS: self-tests for FMP aes-xts failed\n"); + fmp->result.aes_xts = 0; + goto err_xts_cipher; + } + dev_info(fmp->dev, "FIPS: self-tests for FMP aes-xts passed\n"); + fmp->result.aes_xts = 1; + + /* Self test for AES CBC mode */ + fmp->test_data->ci.algo_mode = EXYNOS_FMP_ALGO_MODE_AES_CBC; + cbc_cipher.enc.vecs = aes_cbc_enc_tv_template; + cbc_cipher.enc.count = AES_CBC_ENC_TEST_VECTORS; + ret = test_cipher(fmp, CBC_MODE, cbc_cipher.enc.vecs, cbc_cipher.enc.count); + if (ret) { + dev_err(fmp->dev, "FIPS: self-tests for FMP aes-cbc failed\n"); + fmp->result.aes_cbc = 0; + goto err_cbc_cipher; + } + dev_info(fmp->dev, "FIPS: self-tests for FMP aes-cbc passed\n"); + fmp->result.aes_cbc = 1; + + ret = selftest_sha256(fmp); + if (ret) { + dev_err(fmp->dev, "FIPS: self-tests for UFSFMP %s failed\n", ALG_SHA256_FMP); + fmp->result.sha256 = 0; + goto err_xts_cipher; + } + dev_info(fmp->dev, "FIPS: self-tests for FMP %s passed\n", ALG_SHA256_FMP); + fmp->result.sha256 = 1; + + ret = selftest_hmac_sha256(fmp); + if (ret) { + dev_err(fmp->dev, "FIPS: self-tests for UFSFMP %s failed\n", ALG_HMAC_SHA256_FMP); + fmp->result.hmac = 0; + goto err_xts_cipher; + } + dev_info(fmp->dev, "FIPS: self-tests for UFSFMP %s passed\n", ALG_HMAC_SHA256_FMP); + fmp->result.hmac = 1; + + return 0; + +err_cbc_cipher: +err_xts_cipher: + return -1; +} diff --git a/drivers/crypto/fmp/fmp_fips_selftest.h b/drivers/crypto/fmp/fmp_fips_selftest.h new file mode 100644 index 000000000000..f27a0943a0e1 --- /dev/null +++ b/drivers/crypto/fmp/fmp_fips_selftest.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2016 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef _FMP_SELFTEST_H_ +int do_fmp_selftest(struct exynos_fmp *fmp); +#endif diff --git a/drivers/crypto/fmp/fmp_test.c b/drivers/crypto/fmp/fmp_test.c new file mode 100644 index 000000000000..f4a58e610af1 --- /dev/null +++ b/drivers/crypto/fmp/fmp_test.c @@ -0,0 +1,305 @@ +/* + * Exynos FMP cipher driver + * + * Copyright (C) 2016 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fmp_test.h" + +#define MAX_SCAN_PART (50) +#define MAX_RETRY_COUNT (0x100000) + +static dev_t find_devt_for_test(struct exynos_fmp *fmp, + struct fmp_test_data *data) +{ + int i, idx = 0; + uint32_t count = 0; + uint64_t size; + uint64_t size_list[MAX_SCAN_PART]; + dev_t devt_list[MAX_SCAN_PART]; + dev_t devt_scan, devt; + struct block_device *bdev; + struct device *dev = fmp->dev; + fmode_t fmode = FMODE_WRITE | FMODE_READ; + + memset(size_list, 0, sizeof(size_list)); + memset(devt_list, 0, sizeof(devt_list)); + if (!data || !data->block_type) { + dev_err(dev, "Invalid fmp test data\n"); + return (dev_t) 0; + } + + do { + for (i = 1; i < MAX_SCAN_PART; i++) { + devt_scan = blk_lookup_devt(data->block_type, i); + bdev = blkdev_get_by_dev(devt_scan, fmode, NULL); + if (IS_ERR(bdev)) + continue; + else { + size_list[idx] = + (uint64_t) i_size_read(bdev->bd_inode); + devt_list[idx++] = devt_scan; + blkdev_put(bdev, fmode); + } + } + if (!idx) { + mdelay(100); + count++; + continue; + } + for (i = 0; i < idx; i++) { + if (i == 0) { + size = size_list[i]; + devt = devt_list[i]; + } else { + if (size < size_list[i]) + devt = devt_list[i]; + } + } + bdev = blkdev_get_by_dev(devt, fmode, NULL); + dev_dbg(dev, "Found partno %d for FMP test\n", + bdev->bd_part->partno); + blkdev_put(bdev, fmode); + return devt; + } while (count < MAX_RETRY_COUNT); + dev_err(dev, "Block device isn't initialized yet for FMP test\n"); + return (dev_t) 0; +} + +static int get_fmp_host_type(struct device *dev, + struct fmp_test_data *data) +{ + int ret; + struct device_node *node = dev->of_node; + const char *type; + + ret = + of_property_read_string_index(node, "exynos,block-type", 0, &type); + if (ret) { + pr_err("%s: Could not get block type\n", __func__); + return ret; + } + strlcpy(data->block_type, type, FMP_BLOCK_TYPE_NAME_LEN); + return 0; +} + +static int get_fmp_test_block_offset(struct device *dev, + struct fmp_test_data *data) +{ + int ret = 0; + struct device_node *node = dev->of_node; + uint32_t offset; + + ret = of_property_read_u32(node, "exynos,fips-block_offset", &offset); + if (ret) { + pr_err("%s: Could not get fips test block offset\n", __func__); + ret = -EINVAL; + goto err; + } + data->test_block_offset = offset; +err: + return ret; +} + +/* test block device init for fmp test */ +struct fmp_test_data *fmp_test_init(struct exynos_fmp *fmp) +{ + int ret = 0; + struct fmp_test_data *data; + struct device *dev = fmp->dev; + struct inode *inode; + struct super_block *sb; + unsigned long blocksize; + unsigned char blocksize_bits; + fmode_t fmode = FMODE_WRITE | FMODE_READ; + + if (!fmp) { + dev_err(dev, "%s: Invalid exynos fmp struct\n", __func__); + return NULL; + } + data = kmalloc(sizeof(struct fmp_test_data), GFP_KERNEL); + if (!data) + return NULL; + + ret = get_fmp_host_type(dev, data); + if (ret) { + dev_err(dev, "%s: Fail to get host type. ret(%d)", __func__, + ret); + goto err; + } + data->devt = find_devt_for_test(fmp, data); + if (!data->devt) { + dev_err(dev, "%s: Fail to find devt for self test\n", __func__); + goto err; + } + data->bdev = blkdev_get_by_dev(data->devt, fmode, NULL); + if (IS_ERR(data->bdev)) { + dev_err(dev, "%s: Fail to open block device\n", __func__); + goto err; + } + ret = get_fmp_test_block_offset(dev, data); + if (ret) { + dev_err(dev, "%s: Fail to get fips offset. ret(%d)\n", + __func__, ret); + goto err; + } + inode = data->bdev->bd_inode; + sb = inode->i_sb; + blocksize = sb->s_blocksize; + blocksize_bits = sb->s_blocksize_bits; + data->sector = + (i_size_read(inode) - + (blocksize * data->test_block_offset)) >> blocksize_bits; + + return data; +err: + kzfree(data); + return NULL; +} + +int fmp_cipher_run(struct exynos_fmp *fmp, struct fmp_test_data *fdata, + uint8_t *data, uint32_t len, bool bypass, uint32_t write, + void *priv, struct fmp_crypto_info *ci) +{ + int ret = 0; + struct device *dev = fmp->dev; + static struct buffer_head *bh; + u32 org_algo_mode; + int op_flags; + + if (!fmp || !fdata || !ci) + return -EINVAL; + + bh = __getblk(fdata->bdev, fdata->sector, FMP_BLK_SIZE); + if (!bh) { + dev_err(dev, "%s: Fail to get block from bdev\n", __func__); + return -ENODEV; + } + + /* set algo_mode for test */ + org_algo_mode = ci->algo_mode; + if (bypass) + ci->algo_mode = EXYNOS_FMP_BYPASS_MODE; + ci->algo_mode |= EXYNOS_FMP_ALGO_MODE_TEST; + + get_bh(bh); + /* priv is diskc for crypto test. */ + if (!priv) { + /* ci is fmp_test_data->ci */ + fmp->test_data = fdata; + ci->ctx = fmp; + ci->use_diskc = 0; + ci->enc_mode = EXYNOS_FMP_FILE_ENC; + bh->b_private = ci; + } else { + /* ci is crypto_tfm_ctx(tfm) */ + bh->b_private = priv; + } + op_flags = REQ_CRYPT | REQ_AUX_PRIV; + + if (write == WRITE_MODE) { + memcpy(bh->b_data, data, len); + set_buffer_dirty(bh); + ret = __sync_dirty_buffer(bh, op_flags | REQ_SYNC); + if (ret) { + dev_err(dev, "%s: IO error syncing for write mode\n", + __func__); + ret = -EIO; + goto out; + } + memset(bh->b_data, 0, FMP_BLK_SIZE); + } else { + lock_buffer(bh); + bh->b_end_io = end_buffer_read_sync; + submit_bh(REQ_OP_READ, REQ_SYNC | REQ_PRIO | op_flags, bh); + wait_on_buffer(bh); + if (unlikely(!buffer_uptodate(bh))) { + ret = -EIO; + goto out; + } + memcpy(data, bh->b_data, len); + } +out: + if (ci) + ci->algo_mode = org_algo_mode; + put_bh(bh); + return ret; +} + +int fmp_test_crypt(struct exynos_fmp *fmp, struct fmp_test_data *fdata, + uint8_t *src, uint8_t *dst, uint32_t len, uint32_t enc, + void *priv, struct fmp_crypto_info *ci) +{ + int ret = 0; + + if (!fdata) { + pr_err("%s: Invalid exynos fmp struct\n", __func__); + return -1; + } + + if (enc == ENCRYPT) { + ret = fmp_cipher_run(fmp, fdata, src, len, 0, + WRITE_MODE, priv, ci); + if (ret) { + pr_err("Fail to run fmp cipher ret(%d)\n", + ret); + goto err; + } + ret = fmp_cipher_run(fmp, fdata, dst, len, 1, + READ_MODE, priv, ci); + if (ret) { + pr_err("Fail to run fmp cipher ret(%d)\n", + ret); + goto err; + } + } else if (enc == DECRYPT) { + ret = fmp_cipher_run(fmp, fdata, src, len, 1, + WRITE_MODE, priv, ci); + if (ret) { + pr_err("Fail to run fmp cipher ret(%d)\n", + ret); + goto err; + } + ret = fmp_cipher_run(fmp, fdata, dst, len, 0, + READ_MODE, priv, ci); + if (ret) { + pr_err("Fail to run fmp cipher ret(%d)\n", + ret); + goto err; + } + } else { + pr_err("%s: Invalid enc %d mode\n", __func__, enc); + goto err; + } + + return 0; +err: + return -EINVAL; +} + +/* test block device release for fmp test */ +void fmp_test_exit(struct fmp_test_data *fdata) +{ + fmode_t fmode = FMODE_WRITE | FMODE_READ; + + if (!fdata) + return; + if (fdata->bdev) + blkdev_put(fdata->bdev, fmode); + kzfree(fdata); +} diff --git a/drivers/crypto/fmp/fmp_test.h b/drivers/crypto/fmp/fmp_test.h new file mode 100644 index 000000000000..0990cbe8b400 --- /dev/null +++ b/drivers/crypto/fmp/fmp_test.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2016 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef _FMP_TEST_H_ +#define _FMP_TEST_H_ + +#define FMP_BLK_SIZE (4096) + +#define WRITE_MODE 1 +#define READ_MODE 2 + +#define ENCRYPT 1 +#define DECRYPT 2 + +struct fmp_test_data *fmp_test_init(struct exynos_fmp *fmp); +int fmp_cipher_run(struct exynos_fmp *fmp, struct fmp_test_data *fdata, + uint8_t *data, uint32_t len, bool bypass, uint32_t write, + void *priv, struct fmp_crypto_info *ci); +int fmp_test_crypt(struct exynos_fmp *fmp, struct fmp_test_data *fdata, + uint8_t *src, uint8_t *dst, uint32_t len, uint32_t enc, + void *priv, struct fmp_crypto_info *ci); +void fmp_test_exit(struct fmp_test_data *fdata); +#endif /* _FMP_TEST_H_ */ diff --git a/drivers/crypto/fmp/hmac-sha256.c b/drivers/crypto/fmp/hmac-sha256.c new file mode 100644 index 000000000000..751bb68e0d56 --- /dev/null +++ b/drivers/crypto/fmp/hmac-sha256.c @@ -0,0 +1,136 @@ +/* + * Cryptographic API. + * + * HMAC: Keyed-Hashing for Message Authentication (RFC2104). + * SHA-256 is used as an underlying hash function. + * + * Author : Igor Shcheglakov (i.shcheglako@samsung.com) + * Date : 15 Dec 2017 + * + * Copyright (C) 2017 Samsung Electronics Co., Ltd. + * + */ + +#include +#include + +#include "hmac-sha256.h" +#include "sha256.h" + +int hmac_sha256_init(struct hmac_sha256_ctx *ctx, const u8 *key, unsigned int key_len) +{ + int ret = -1; + unsigned int i; + u8 key_buf[SHA256_BLOCK_SIZE]; + unsigned int key_buf_len; + u8 inner_pad[SHA256_BLOCK_SIZE]; + u8 outer_pad[SHA256_BLOCK_SIZE]; + unsigned int block_size = SHA256_BLOCK_SIZE; + unsigned int digest_size = SHA256_DIGEST_SIZE; + + if (!ctx || !key) + goto err; + + // long keys are hashed + if (key_len > block_size) { + + if (sha256(key, key_len, key_buf)) + goto err; + + key_buf_len = digest_size; + } else { + memcpy(key_buf, key, key_len); + key_buf_len = key_len; + } + + // short keys are padded with zeroes to the right + if (key_buf_len != block_size) + memset(&key_buf[key_buf_len], 0, sizeof(key_buf) - key_buf_len); + + // inner and outer padding calculation + for (i = 0; i < block_size; i++) { + inner_pad[i] = 0x36 ^ key_buf[i]; + outer_pad[i] = 0x5c ^ key_buf[i]; + } + + // inner hash context preparation + if (sha256_init(&ctx->inner_ctx)) + goto err; + + if (sha256_update(&ctx->inner_ctx, inner_pad, sizeof(inner_pad))) + goto err; + + // outer hash context preparation + if (sha256_init(&ctx->outer_ctx)) + goto err; + + if (sha256_update(&ctx->outer_ctx, outer_pad, sizeof(outer_pad))) + goto err; + + ret = 0; + +err: + memset(key_buf, 0, sizeof(key_buf)); + memset(inner_pad, 0, sizeof(inner_pad)); + memset(outer_pad, 0, sizeof(outer_pad)); + + return ret; +} + +int hmac_sha256_update(struct hmac_sha256_ctx *ctx, const u8 *data, unsigned int data_len) +{ + if (!ctx || !data) + return 0; + + return sha256_update(&ctx->inner_ctx, data, data_len); +} + +int hmac_sha256_final(struct hmac_sha256_ctx *ctx, u8 *out) +{ + int ret = -1; + u8 result[SHA256_DIGEST_SIZE]; + + if (!ctx || !out) + goto err; + + if (sha256_final(&ctx->inner_ctx, result)) + goto err; + + if (sha256_update(&ctx->outer_ctx, result, sizeof(result))) + goto err; + + if (sha256_final(&ctx->outer_ctx, result)) + goto err; + + memcpy(out, result, sizeof(result)); + + ret = 0; + +err: + return ret; +} + +int hmac_sha256(const u8 *key, unsigned int key_len, const u8 *data, unsigned int data_len, u8 *out) +{ + int ret = -1; + struct hmac_sha256_ctx ctx; + + if (hmac_sha256_init(&ctx, key, key_len)) + goto err; + + if (hmac_sha256_update(&ctx, data, data_len)) + goto err; + + if (hmac_sha256_final(&ctx, out)) + goto err; + + ret = 0; + +err: + return ret; +} + +void hmac_sha256_ctx_cleanup(struct hmac_sha256_ctx *ctx) +{ + memset(ctx, 0, sizeof(struct hmac_sha256_ctx)); +} diff --git a/drivers/crypto/fmp/hmac-sha256.h b/drivers/crypto/fmp/hmac-sha256.h new file mode 100644 index 000000000000..c2be607cce9a --- /dev/null +++ b/drivers/crypto/fmp/hmac-sha256.h @@ -0,0 +1,57 @@ +/* + * Cryptographic API. + * + * HMAC: Keyed-Hashing for Message Authentication (RFC2104). + * SHA-256 is used as an underlying hash function. + * + * Author : Igor Shcheglakov (i.shcheglako@samsung.com) + * Date : 15 Dec 2017 + * + * Copyright (C) 2017 Samsung Electronics Co., Ltd. + * + */ + +#ifndef _FMP_HMAC_SHA256_H +#define _FMP_HMAC_SHA256_H + +#include "sha256.h" + +struct hmac_sha256_ctx { + struct shash_desc inner_ctx; + struct shash_desc outer_ctx; +}; + +typedef struct hmac_sha256_ctx HMAC_SHA256_CTX; + +/* Initialize hashing context using a key of key_len bytes. + * + * Return zero on success and negative otherwise + */ +int hmac_sha256_init(struct hmac_sha256_ctx *ctx, const u8 *key, unsigned int key_len); + +/* Hash data_len bytes of data + * + * Return zero on success and negative otherwise + */ +int hmac_sha256_update(struct hmac_sha256_ctx *ctx, const u8 *data, unsigned int data_len); + +/* Add final padding to a hash and write resulting HMAC to out. + * There must be at least SHA256_DIGEST_LENGTH bytes of space in out + * + * Return zero on success and negative otherwise + */ +int hmac_sha256_final(struct hmac_sha256_ctx *ctx, u8 *out); + +/* Hash data_len bytes of data using key of key_len bytes. + * Write resulting HMAC to out. There must be at least SHA256_DIGEST_LENGTH + * bytes of space in out + * + * Return zero on success and negative otherwise + */ +int hmac_sha256(const u8 *key, unsigned int key_len, const u8 *data, unsigned int data_len, u8 *out); + +/* Cleanup allocated resources */ +void hmac_sha256_ctx_cleanup(struct hmac_sha256_ctx *ctx); + +#endif /* _FMP_HMAC_SHA256_H */ + diff --git a/drivers/crypto/fmp/last_file.c b/drivers/crypto/fmp/last_file.c new file mode 100644 index 000000000000..c0d405d5d829 --- /dev/null +++ b/drivers/crypto/fmp/last_file.c @@ -0,0 +1,21 @@ +/* + * Last file for Exynos FMP FIPS integrity check + * + * Copyright (C) 2015 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include + +__attribute__ ((section(".rodata"), unused)) +const unsigned char last_fmp_rodata = 0x20; + +__attribute__ ((section(".text"), unused)) +void last_fmp_text(void){} + +__attribute__ ((section(".init.text"), optimize("-O0"), unused)) +static void last_fmp_init(void){}; diff --git a/drivers/crypto/fmp/sha256.c b/drivers/crypto/fmp/sha256.c new file mode 100644 index 000000000000..9767bc3d7c4a --- /dev/null +++ b/drivers/crypto/fmp/sha256.c @@ -0,0 +1,546 @@ +/* + * Cryptographic API. + * + * SHA-256, as specified in + * http://csrc.nist.gov/groups/STM/cavp/documents/shs/sha256-384-512.pdf + * + * SHA-256 code by Jean-Luc Cooke . + * + * Copyright (c) Jean-Luc Cooke + * Copyright (c) Andrew McDonald + * Copyright (c) 2002 James Morris + * SHA224 Support Copyright 2007 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "sha256.h" + +#define SHA256_H0 0x6a09e667UL +#define SHA256_H1 0xbb67ae85UL +#define SHA256_H2 0x3c6ef372UL +#define SHA256_H3 0xa54ff53aUL +#define SHA256_H4 0x510e527fUL +#define SHA256_H5 0x9b05688cUL +#define SHA256_H6 0x1f83d9abUL +#define SHA256_H7 0x5be0cd19UL + +#define e0(x) (ror32(x, 2) ^ ror32(x, 13) ^ ror32(x, 22)) +#define e1(x) (ror32(x, 6) ^ ror32(x, 11) ^ ror32(x, 25)) +#define s0(x) (ror32(x, 7) ^ ror32(x, 18) ^ (x >> 3)) +#define s1(x) (ror32(x, 17) ^ ror32(x, 19) ^ (x >> 10)) + +static const u8 sha256_zero_message_hash[SHA256_DIGEST_SIZE] = { + 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, + 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, + 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, + 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55 +}; + +static inline u32 Ch(u32 x, u32 y, u32 z) +{ + return z ^ (x & (y ^ z)); +} + +static inline u32 Maj(u32 x, u32 y, u32 z) +{ + return (x & y) | (z & (x | y)); +} + +static inline void *shash_desc_ctx(struct shash_desc *desc) +{ + return &desc->__ctx; +} + +static inline void LOAD_OP(int I, u32 *W, const u8 *input) +{ + W[I] = get_unaligned_be32((__u32 *) input + I); +} + +static inline void BLEND_OP(int I, u32 *W) +{ + W[I] = s1(W[I - 2]) + W[I - 7] + s0(W[I - 15]) + W[I - 16]; +} + +static void sha256_transform(u32 *state, const u8 *input) +{ + u32 a, b, c, d, e, f, g, h, t1, t2; + u32 W[64]; + int i; + + /* load the input */ + for (i = 0; i < 16; i++) + LOAD_OP(i, W, input); + + /* now blend */ + for (i = 16; i < 64; i++) + BLEND_OP(i, W); + + /* load the state into our registers */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + f = state[5]; + g = state[6]; + h = state[7]; + + /* now iterate */ + t1 = h + e1(e) + Ch(e, f, g) + 0x428a2f98 + W[0]; + t2 = e0(a) + Maj(a, b, c); + d += t1; + h = t1 + t2; + t1 = g + e1(d) + Ch(d, e, f) + 0x71374491 + W[1]; + t2 = e0(h) + Maj(h, a, b); + c += t1; + g = t1 + t2; + t1 = f + e1(c) + Ch(c, d, e) + 0xb5c0fbcf + W[2]; + t2 = e0(g) + Maj(g, h, a); + b += t1; + f = t1 + t2; + t1 = e + e1(b) + Ch(b, c, d) + 0xe9b5dba5 + W[3]; + t2 = e0(f) + Maj(f, g, h); + a += t1; + e = t1 + t2; + t1 = d + e1(a) + Ch(a, b, c) + 0x3956c25b + W[4]; + t2 = e0(e) + Maj(e, f, g); + h += t1; + d = t1 + t2; + t1 = c + e1(h) + Ch(h, a, b) + 0x59f111f1 + W[5]; + t2 = e0(d) + Maj(d, e, f); + g += t1; + c = t1 + t2; + t1 = b + e1(g) + Ch(g, h, a) + 0x923f82a4 + W[6]; + t2 = e0(c) + Maj(c, d, e); + f += t1; + b = t1 + t2; + t1 = a + e1(f) + Ch(f, g, h) + 0xab1c5ed5 + W[7]; + t2 = e0(b) + Maj(b, c, d); + e += t1; + a = t1 + t2; + + t1 = h + e1(e) + Ch(e, f, g) + 0xd807aa98 + W[8]; + t2 = e0(a) + Maj(a, b, c); + d += t1; + h = t1 + t2; + t1 = g + e1(d) + Ch(d, e, f) + 0x12835b01 + W[9]; + t2 = e0(h) + Maj(h, a, b); + c += t1; + g = t1 + t2; + t1 = f + e1(c) + Ch(c, d, e) + 0x243185be + W[10]; + t2 = e0(g) + Maj(g, h, a); + b += t1; + f = t1 + t2; + t1 = e + e1(b) + Ch(b, c, d) + 0x550c7dc3 + W[11]; + t2 = e0(f) + Maj(f, g, h); + a += t1; + e = t1 + t2; + t1 = d + e1(a) + Ch(a, b, c) + 0x72be5d74 + W[12]; + t2 = e0(e) + Maj(e, f, g); + h += t1; + d = t1 + t2; + t1 = c + e1(h) + Ch(h, a, b) + 0x80deb1fe + W[13]; + t2 = e0(d) + Maj(d, e, f); + g += t1; + c = t1 + t2; + t1 = b + e1(g) + Ch(g, h, a) + 0x9bdc06a7 + W[14]; + t2 = e0(c) + Maj(c, d, e); + f += t1; + b = t1 + t2; + t1 = a + e1(f) + Ch(f, g, h) + 0xc19bf174 + W[15]; + t2 = e0(b) + Maj(b, c, d); + e += t1; + a = t1 + t2; + + t1 = h + e1(e) + Ch(e, f, g) + 0xe49b69c1 + W[16]; + t2 = e0(a) + Maj(a, b, c); + d += t1; + h = t1 + t2; + t1 = g + e1(d) + Ch(d, e, f) + 0xefbe4786 + W[17]; + t2 = e0(h) + Maj(h, a, b); + c += t1; + g = t1 + t2; + t1 = f + e1(c) + Ch(c, d, e) + 0x0fc19dc6 + W[18]; + t2 = e0(g) + Maj(g, h, a); + b += t1; + f = t1 + t2; + t1 = e + e1(b) + Ch(b, c, d) + 0x240ca1cc + W[19]; + t2 = e0(f) + Maj(f, g, h); + a += t1; + e = t1 + t2; + t1 = d + e1(a) + Ch(a, b, c) + 0x2de92c6f + W[20]; + t2 = e0(e) + Maj(e, f, g); + h += t1; + d = t1 + t2; + t1 = c + e1(h) + Ch(h, a, b) + 0x4a7484aa + W[21]; + t2 = e0(d) + Maj(d, e, f); + g += t1; + c = t1 + t2; + t1 = b + e1(g) + Ch(g, h, a) + 0x5cb0a9dc + W[22]; + t2 = e0(c) + Maj(c, d, e); + f += t1; + b = t1 + t2; + t1 = a + e1(f) + Ch(f, g, h) + 0x76f988da + W[23]; + t2 = e0(b) + Maj(b, c, d); + e += t1; + a = t1 + t2; + + t1 = h + e1(e) + Ch(e, f, g) + 0x983e5152 + W[24]; + t2 = e0(a) + Maj(a, b, c); + d += t1; + h = t1 + t2; + t1 = g + e1(d) + Ch(d, e, f) + 0xa831c66d + W[25]; + t2 = e0(h) + Maj(h, a, b); + c += t1; + g = t1 + t2; + t1 = f + e1(c) + Ch(c, d, e) + 0xb00327c8 + W[26]; + t2 = e0(g) + Maj(g, h, a); + b += t1; + f = t1 + t2; + t1 = e + e1(b) + Ch(b, c, d) + 0xbf597fc7 + W[27]; + t2 = e0(f) + Maj(f, g, h); + a += t1; + e = t1 + t2; + t1 = d + e1(a) + Ch(a, b, c) + 0xc6e00bf3 + W[28]; + t2 = e0(e) + Maj(e, f, g); + h += t1; + d = t1 + t2; + t1 = c + e1(h) + Ch(h, a, b) + 0xd5a79147 + W[29]; + t2 = e0(d) + Maj(d, e, f); + g += t1; + c = t1 + t2; + t1 = b + e1(g) + Ch(g, h, a) + 0x06ca6351 + W[30]; + t2 = e0(c) + Maj(c, d, e); + f += t1; + b = t1 + t2; + t1 = a + e1(f) + Ch(f, g, h) + 0x14292967 + W[31]; + t2 = e0(b) + Maj(b, c, d); + e += t1; + a = t1 + t2; + + t1 = h + e1(e) + Ch(e, f, g) + 0x27b70a85 + W[32]; + t2 = e0(a) + Maj(a, b, c); + d += t1; + h = t1 + t2; + t1 = g + e1(d) + Ch(d, e, f) + 0x2e1b2138 + W[33]; + t2 = e0(h) + Maj(h, a, b); + c += t1; + g = t1 + t2; + t1 = f + e1(c) + Ch(c, d, e) + 0x4d2c6dfc + W[34]; + t2 = e0(g) + Maj(g, h, a); + b += t1; + f = t1 + t2; + t1 = e + e1(b) + Ch(b, c, d) + 0x53380d13 + W[35]; + t2 = e0(f) + Maj(f, g, h); + a += t1; + e = t1 + t2; + t1 = d + e1(a) + Ch(a, b, c) + 0x650a7354 + W[36]; + t2 = e0(e) + Maj(e, f, g); + h += t1; + d = t1 + t2; + t1 = c + e1(h) + Ch(h, a, b) + 0x766a0abb + W[37]; + t2 = e0(d) + Maj(d, e, f); + g += t1; + c = t1 + t2; + t1 = b + e1(g) + Ch(g, h, a) + 0x81c2c92e + W[38]; + t2 = e0(c) + Maj(c, d, e); + f += t1; + b = t1 + t2; + t1 = a + e1(f) + Ch(f, g, h) + 0x92722c85 + W[39]; + t2 = e0(b) + Maj(b, c, d); + e += t1; + a = t1 + t2; + + t1 = h + e1(e) + Ch(e, f, g) + 0xa2bfe8a1 + W[40]; + t2 = e0(a) + Maj(a, b, c); + d += t1; + h = t1 + t2; + t1 = g + e1(d) + Ch(d, e, f) + 0xa81a664b + W[41]; + t2 = e0(h) + Maj(h, a, b); + c += t1; + g = t1 + t2; + t1 = f + e1(c) + Ch(c, d, e) + 0xc24b8b70 + W[42]; + t2 = e0(g) + Maj(g, h, a); + b += t1; + f = t1 + t2; + t1 = e + e1(b) + Ch(b, c, d) + 0xc76c51a3 + W[43]; + t2 = e0(f) + Maj(f, g, h); + a += t1; + e = t1 + t2; + t1 = d + e1(a) + Ch(a, b, c) + 0xd192e819 + W[44]; + t2 = e0(e) + Maj(e, f, g); + h += t1; + d = t1 + t2; + t1 = c + e1(h) + Ch(h, a, b) + 0xd6990624 + W[45]; + t2 = e0(d) + Maj(d, e, f); + g += t1; + c = t1 + t2; + t1 = b + e1(g) + Ch(g, h, a) + 0xf40e3585 + W[46]; + t2 = e0(c) + Maj(c, d, e); + f += t1; + b = t1 + t2; + t1 = a + e1(f) + Ch(f, g, h) + 0x106aa070 + W[47]; + t2 = e0(b) + Maj(b, c, d); + e += t1; + a = t1 + t2; + + t1 = h + e1(e) + Ch(e, f, g) + 0x19a4c116 + W[48]; + t2 = e0(a) + Maj(a, b, c); + d += t1; + h = t1 + t2; + t1 = g + e1(d) + Ch(d, e, f) + 0x1e376c08 + W[49]; + t2 = e0(h) + Maj(h, a, b); + c += t1; + g = t1 + t2; + t1 = f + e1(c) + Ch(c, d, e) + 0x2748774c + W[50]; + t2 = e0(g) + Maj(g, h, a); + b += t1; + f = t1 + t2; + t1 = e + e1(b) + Ch(b, c, d) + 0x34b0bcb5 + W[51]; + t2 = e0(f) + Maj(f, g, h); + a += t1; + e = t1 + t2; + t1 = d + e1(a) + Ch(a, b, c) + 0x391c0cb3 + W[52]; + t2 = e0(e) + Maj(e, f, g); + h += t1; + d = t1 + t2; + t1 = c + e1(h) + Ch(h, a, b) + 0x4ed8aa4a + W[53]; + t2 = e0(d) + Maj(d, e, f); + g += t1; + c = t1 + t2; + t1 = b + e1(g) + Ch(g, h, a) + 0x5b9cca4f + W[54]; + t2 = e0(c) + Maj(c, d, e); + f += t1; + b = t1 + t2; + t1 = a + e1(f) + Ch(f, g, h) + 0x682e6ff3 + W[55]; + t2 = e0(b) + Maj(b, c, d); + e += t1; + a = t1 + t2; + + t1 = h + e1(e) + Ch(e, f, g) + 0x748f82ee + W[56]; + t2 = e0(a) + Maj(a, b, c); + d += t1; + h = t1 + t2; + t1 = g + e1(d) + Ch(d, e, f) + 0x78a5636f + W[57]; + t2 = e0(h) + Maj(h, a, b); + c += t1; + g = t1 + t2; + t1 = f + e1(c) + Ch(c, d, e) + 0x84c87814 + W[58]; + t2 = e0(g) + Maj(g, h, a); + b += t1; + f = t1 + t2; + t1 = e + e1(b) + Ch(b, c, d) + 0x8cc70208 + W[59]; + t2 = e0(f) + Maj(f, g, h); + a += t1; + e = t1 + t2; + t1 = d + e1(a) + Ch(a, b, c) + 0x90befffa + W[60]; + t2 = e0(e) + Maj(e, f, g); + h += t1; + d = t1 + t2; + t1 = c + e1(h) + Ch(h, a, b) + 0xa4506ceb + W[61]; + t2 = e0(d) + Maj(d, e, f); + g += t1; + c = t1 + t2; + t1 = b + e1(g) + Ch(g, h, a) + 0xbef9a3f7 + W[62]; + t2 = e0(c) + Maj(c, d, e); + f += t1; + b = t1 + t2; + t1 = a + e1(f) + Ch(f, g, h) + 0xc67178f2 + W[63]; + t2 = e0(b) + Maj(b, c, d); + e += t1; + a = t1 + t2; + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + state[5] += f; + state[6] += g; + state[7] += h; + + /* clear any sensitive info... */ + a = b = c = d = e = f = g = h = t1 = t2 = 0; + memzero_explicit(W, 64 * sizeof(u32)); +} + +static void sha256_generic_block_fn(struct sha256_state *sst, u8 const *src, + int blocks) +{ + while (blocks--) { + sha256_transform(sst->state, src); + src += SHA256_BLOCK_SIZE; + } +} + +static int sha256_base_do_update(struct shash_desc *desc, + const u8 *data, + unsigned int len, sha256_block_fn *block_fn) +{ + struct sha256_state *sctx = shash_desc_ctx(desc); + unsigned int partial = sctx->count % SHA256_BLOCK_SIZE; + + sctx->count += len; + + if (unlikely((partial + len) >= SHA256_BLOCK_SIZE)) { + int blocks; + + if (partial) { + int p = SHA256_BLOCK_SIZE - partial; + + memcpy(sctx->buf + partial, data, p); + data += p; + len -= p; + + block_fn(sctx, sctx->buf, 1); + } + + blocks = len / SHA256_BLOCK_SIZE; + len %= SHA256_BLOCK_SIZE; + + if (blocks) { + block_fn(sctx, data, blocks); + data += blocks * SHA256_BLOCK_SIZE; + } + partial = 0; + } + if (len) + memcpy(sctx->buf + partial, data, len); + + return 0; +} + +static int sha256_base_do_finalize(struct shash_desc *desc, + sha256_block_fn *block_fn) +{ + const int bit_offset = SHA256_BLOCK_SIZE - sizeof(__be64); + struct sha256_state *sctx = shash_desc_ctx(desc); + __be64 *bits = (__be64 *) (sctx->buf + bit_offset); + unsigned int partial = sctx->count % SHA256_BLOCK_SIZE; + + sctx->buf[partial++] = 0x80; + if (partial > bit_offset) { + memset(sctx->buf + partial, 0x0, SHA256_BLOCK_SIZE - partial); + partial = 0; + + block_fn(sctx, sctx->buf, 1); + } + + memset(sctx->buf + partial, 0x0, bit_offset - partial); + *bits = cpu_to_be64(sctx->count << 3); + block_fn(sctx, sctx->buf, 1); + + return 0; +} + +static inline int sha256_base_finish(struct shash_desc *desc, u8 *out) +{ + unsigned int digest_size = SHA256_DIGEST_SIZE; + struct sha256_state *sctx = shash_desc_ctx(desc); + __be32 *digest = (__be32 *) out; + int i; + + for (i = 0; digest_size > 0; i++, digest_size -= sizeof(__be32)) + put_unaligned_be32(sctx->state[i], digest++); + + *sctx = (struct sha256_state) { + }; + return 0; +} + +int sha256_init(struct shash_desc *desc) +{ + struct sha256_state *sctx = NULL; + + if (!desc) + return -EINVAL; + + sctx = shash_desc_ctx(desc); + + sctx->state[0] = SHA256_H0; + sctx->state[1] = SHA256_H1; + sctx->state[2] = SHA256_H2; + sctx->state[3] = SHA256_H3; + sctx->state[4] = SHA256_H4; + sctx->state[5] = SHA256_H5; + sctx->state[6] = SHA256_H6; + sctx->state[7] = SHA256_H7; + sctx->count = 0; + + return 0; +} + +int sha256_update(struct shash_desc *desc, const u8 *data, unsigned int len) +{ + if (!desc || !data) + return -EINVAL; + + return sha256_base_do_update(desc, data, len, sha256_generic_block_fn); +} + +int sha256_final(struct shash_desc *desc, u8 *out) +{ + if (!desc || !out) + return -EINVAL; + + sha256_base_do_finalize(desc, sha256_generic_block_fn); + return sha256_base_finish(desc, out); +} + +int sha256_finup(struct shash_desc *desc, const u8 *data, + unsigned int len, u8 *hash) +{ + if (!desc || !data || !hash) + return -EINVAL; + + sha256_base_do_update(desc, data, len, sha256_generic_block_fn); + return sha256_final(desc, hash); +} + +int sha256_desc_copy(struct shash_desc *dst, const struct shash_desc *src) +{ + if (!dst || !src) + return -EINVAL; + + memcpy(dst, src, sizeof(struct shash_desc)); + return 0; +} + +int sha256(const u8 *data, unsigned int len, u8 *out) +{ + struct shash_desc ctx; + int ret = -EINVAL; + + if (!out || !data) + return -EINVAL; + + ret = sha256_init(&ctx); + if (ret != 0) + goto exit_error; + + ret = sha256_update(&ctx, data, len); + if (ret != 0) + goto exit_error; + + ret = sha256_final(&ctx, out); + if (ret != 0) + goto exit_error; + + memzero_explicit(&ctx, sizeof(ctx)); + return 0; + +exit_error: + return ret; +} diff --git a/drivers/crypto/fmp/sha256.h b/drivers/crypto/fmp/sha256.h new file mode 100644 index 000000000000..378f261132ef --- /dev/null +++ b/drivers/crypto/fmp/sha256.h @@ -0,0 +1,73 @@ +/* + * Cryptographic API. + * + * SHA-256, as specified in + * http://csrc.nist.gov/groups/STM/cavp/documents/shs/sha256-384-512.pdf + * + * SHA-256 code by Jean-Luc Cooke . + * + * Copyright (c) Jean-Luc Cooke + * Copyright (c) Andrew McDonald + * Copyright (c) 2002 James Morris + * SHA224 Support Copyright 2007 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + */ + +#ifndef SHA256_FMP_H +#define SHA256_FMP_H + +#include + +#define SHA256_DIGEST_SIZE 32 +#define SHA256_BLOCK_SIZE 64 +#define SHA256_DIGEST_LENGTH SHA256_DIGEST_SIZE + +struct sha256_state { + u32 state[SHA256_DIGEST_SIZE / 4]; + u64 count; + u8 buf[SHA256_BLOCK_SIZE]; +}; + +struct shash_desc { + struct sha256_state __ctx; +}; + +typedef struct shash_desc SHA256_CTX; + +typedef void (sha256_block_fn)(struct sha256_state *sst, u8 const *src, + int blocks); + +/* Initialises desc + * Returns 0 at success, nonzero otherwise + */ +int sha256_init(struct shash_desc *desc); + +/* Adds len bytes from data to desc. + * Returns 0 at success, nonzero otherwise + */ +int sha256_update(struct shash_desc *desc, const u8 *data, + unsigned int len); + +/* Adds the final padding to desc and writes the resulting digest + * to out, which must have at least SHA256_DIGEST_SIZE bytes of space. + * Returns 0 at success, nonzero otherwise + */ +int sha256_final(struct shash_desc *desc, u8 *out); + +/* Writes the digest of len bytes from data to out. Returns out. + * Out should be preallocated at least SHA256_DIGEST_SIZE length. + * Returns 0 at success, nonzero otherwise + */ +int sha256(const u8 *data, unsigned int len, u8 *out); + +/* desc dup + * Returns 0 at success, nonzero otherwise + */ +int sha256_desc_copy(struct shash_desc *dst, const struct shash_desc *src); + +#endif /* SHA256_FMP_H */ diff --git a/drivers/crypto/fmp/smu.c b/drivers/crypto/fmp/smu.c new file mode 100644 index 000000000000..ce43fb7cfddd --- /dev/null +++ b/drivers/crypto/fmp/smu.c @@ -0,0 +1,249 @@ +/* + * UFS SMU (Secure Management Unit) driver for Exynos + * + * Copyright (C) 2016 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include + +#include + +static LIST_HEAD(smu_devices); + +struct exynos_smu { + struct list_head list; + int id; + int command; + int desc_type; + struct device *dev; +}; + +static int exynos_smu_init(struct platform_device *pdev, + struct smu_data_setting *smu_set) +{ + int ret = 0; + struct exynos_smu *smu = NULL; + + if (!pdev || !smu_set) { + pr_err("%s: Invalid input parameter.\n", __func__); + ret = -EINVAL; + goto err; + } + + smu = platform_get_drvdata(pdev); + ret = exynos_smc(SMC_CMD_SMU, smu_set->command, smu_set->id, 0); + if (ret) + dev_err(smu->dev, "%s: Fail smc call for SMU init. ret(%d)\n", + __func__, ret); +err: + return ret; +} + +static int exynos_smu_sec_config(struct platform_device *pdev, + struct smu_data_setting *smu_set) +{ + int ret = 0; + struct exynos_smu *smu = NULL; + + if (!pdev || !smu_set) { + pr_err("%s: Invalid input parameter.\n", __func__); + ret = -EINVAL; + goto err; + } + + smu = platform_get_drvdata(pdev); + ret = exynos_smc(SMC_CMD_FMP_SECURITY, 0, smu_set->id, smu_set->desc_type); + if (ret) + dev_err(smu->dev, "%s: Fail smc for FMPSECURITY config. ret(%d)\n", + __func__, ret); +err: + return ret; +} + +static int exynos_smu_resume(struct platform_device *pdev, + struct smu_data_setting *smu_set) +{ + int ret = 0; + struct exynos_smu *smu = NULL; + + if (!pdev || !smu_set) { + pr_err("%s: Invalid input parameter.\n", __func__); + ret = -EINVAL; + goto err; + } + + smu = platform_get_drvdata(pdev); + ret = exynos_smc(SMC_CMD_FMP_SMU_RESUME, 0, smu_set->id, 0); + if (ret) + dev_err(smu->dev, "%s: Fail smc call for SMU resume. ret(%d)\n", + __func__, ret); +err: + return ret; +} + +static int exynos_smu_abort(struct platform_device *pdev, + struct smu_data_setting *smu_set) +{ + int ret = 0; + + if (!pdev || !smu_set) { + pr_err("%s: Invalid input parameter.\n", __func__); + ret = -EINVAL; + goto err; + } + + ret = exynos_smc(SMC_CMD_SMU, smu_set->command, smu_set->id, 0); +err: + return ret; +} + +const struct exynos_smu_variant_ops exynos_smu_ops = { + .name = "exynos-smu", + .init = exynos_smu_init, + .sec_config = exynos_smu_sec_config, + .resume = exynos_smu_resume, + .abort = exynos_smu_abort, +}; + +static const struct of_device_id exynos_smu_match[] = { + { + .compatible = "samsung,exynos-smu", + .data = (void *)&exynos_smu_ops, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, exynos_smu_match); + +struct platform_device *exynos_smu_get_pdevice(struct device_node *node) +{ + struct platform_device *smu_pdev = NULL; + struct exynos_smu *smu = NULL; + + if (!node) { + pr_err("%s: Invalid node\n", __func__); + goto err; + } + + if (!of_device_is_available(node)) { + pr_err("%s: Unavailable device\n", __func__); + goto err; + } + + if (list_empty(&smu_devices)) { + pr_err("%s: Invalie device list\n", __func__); + smu_pdev = ERR_PTR(-EPROBE_DEFER); + goto err; + } + + list_for_each_entry(smu, &smu_devices, list) { + if (smu->dev->of_node == node) { + pr_info("%s: Found smu device %p\n", __func__, smu); + break; + } + } + + smu_pdev = to_platform_device(smu->dev); + pr_info("%s: Matching platform device %p\n", __func__, smu_pdev); +err: + return smu_pdev; +} +EXPORT_SYMBOL(exynos_smu_get_pdevice); + +struct exynos_smu_variant_ops *exynos_smu_get_variant_ops(struct device_node *node) +{ + if (node) { + const struct of_device_id *match; + + match = of_match_node(exynos_smu_match, node); + if (match) + return (struct exynos_smu_variant_ops *)(match->data); + pr_err("%s: Error matching\n", __func__); + } else { + pr_err("%s: Invalid node\n", __func__); + } + return NULL; +} +EXPORT_SYMBOL(exynos_smu_get_variant_ops); + +static int exynos_smu_probe(struct platform_device *pdev) +{ + int ret = 0; + struct exynos_smu *smu; + + if (!pdev) { + pr_err("%s: Invalid platform_device.\n", __func__); + ret = -EINVAL; + goto err_pdev; + } + + smu = kzalloc(sizeof(struct exynos_smu), GFP_KERNEL); + if (!smu) { + ret = -ENOMEM; + goto err_mem; + } + + smu->dev = &pdev->dev; + if (!smu->dev) { + pr_err("%s: Invalid device.\n", __func__); + ret = -EINVAL; + goto err_dev; + } + + platform_set_drvdata(pdev, smu); + list_add_tail(&smu->list, &smu_devices); + + pr_info("%s: Exynos SMU driver is proved\n", __func__); + return ret; + +err_dev: + kfree(smu); +err_mem: +err_pdev: + return ret; +} + +static int exynos_smu_remove(struct platform_device *pdev) +{ + struct exynos_smu *smu; + + smu = (struct exynos_smu *)platform_get_drvdata(pdev); + if (!smu) + return 0; + + kfree(smu); + return 0; +} + +static struct platform_driver exynos_smu_driver = { + .driver = { + .name = "exynos-smu", + .owner = THIS_MODULE, + .pm = NULL, + .of_match_table = exynos_smu_match, + }, + .probe = exynos_smu_probe, + .remove = exynos_smu_remove, +}; + +static int __init smu_init(void) +{ + return platform_driver_register(&exynos_smu_driver); +} +subsys_initcall(smu_init); + +static void __exit smu_exit(void) +{ + platform_driver_unregister(&exynos_smu_driver); +} +module_exit(smu_exit); + +MODULE_DESCRIPTION("Exynos Specific SMU(Secure Management Unit) driver"); diff --git a/include/crypto/fmp.h b/include/crypto/fmp.h new file mode 100644 index 000000000000..9275ed94dd79 --- /dev/null +++ b/include/crypto/fmp.h @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2016 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef _EXYNOS_FMP_H_ +#define _EXYNOS_FMP_H_ + +#include +#include + +#define FMP_DRV_VERSION "1.5.0" + +#define FMP_KEY_SIZE_16 16 +#define FMP_KEY_SIZE_32 32 +#define FMP_IV_SIZE_16 16 + +#define FMP_CBC_MAX_KEY_SIZE FMP_KEY_SIZE_16 +#define FMP_XTS_MAX_KEY_SIZE ((FMP_KEY_SIZE_32) * (2)) +#define FMP_MAX_KEY_SIZE FMP_XTS_MAX_KEY_SIZE + +#define FMP_HOST_TYPE_NAME_LEN 8 +#define FMP_BLOCK_TYPE_NAME_LEN 8 + +#define FMP_SECTOR_SIZE 0x1000 +#define FMP_MIN_SECTOR_SIZE 0x200 +#define NUM_SECTOR_UNIT ((FMP_SECTOR_SIZE)/(FMP_MIN_SECTOR_SIZE)) + +#define MAX_AES_XTS_TRANSFER_SIZE 0x1000 + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +enum fmp_crypto_algo_mode { + EXYNOS_FMP_BYPASS_MODE = 0, + EXYNOS_FMP_ALGO_MODE_AES_CBC = 1, + EXYNOS_FMP_ALGO_MODE_AES_XTS = 2, +}; + +enum fmp_crypto_key_size { + EXYNOS_FMP_KEY_SIZE_16 = 16, + EXYNOS_FMP_KEY_SIZE_32 = 32, +}; + +enum fmp_crypto_enc_mode { + EXYNOS_FMP_FILE_ENC = 0, + EXYNOS_FMP_DISK_ENC = 1, /* use persistent key */ + EXYNOS_FMP_ENC_MAX +}; + +enum fmp_disk_key_status { + KEY_STORED = 0, + KEY_SET = 1, + KEY_CLEAR = 2, + KEY_ERROR = -1, +}; + +struct fmp_crypto_info { + /* This field's stongly aligned 'crypto_diskcipher->algo' */ + u32 use_diskc; + u8 key[FMP_MAX_KEY_SIZE]; + u32 key_size; + enum fmp_crypto_key_size fmp_key_size; + enum fmp_crypto_enc_mode enc_mode; + enum fmp_crypto_algo_mode algo_mode; + void *ctx; +}; + +#if defined(CONFIG_MMC_DW_EXYNOS_FMP) +struct fmp_table_setting { + __le32 des0; /* des0 */ +#define GET_CMDQ_LENGTH(d) \ + (((d)->des0 & 0xffff0000) >> 16) + __le32 des1; /* des1 */ + __le32 des2; /* des2 */ +#define FKL BIT(26) +#define DKL BIT(27) +#define SET_KEYLEN(d, v) ((d)->des2 |= (uint32_t)v) +#define SET_FAS(d, v) \ + ((d)->des2 = ((d)->des2 & 0xcfffffff) | v << 28) +#define SET_DAS(d, v) \ + ((d)->des2 = ((d)->des2 & 0x3fffffff) | v << 30) +#define GET_FAS(d) ((d)->des2 & 0x30000000) +#define GET_DAS(d) ((d)->des2 & 0xc0000000) +#define GET_LENGTH(d) \ + ((d)->des2 & 0x3ffffff) + __le32 des3; /* des3 */ + /* CMDQ Operation */ +#define FKL_CMDQ BIT(0) +#define DKL_CMDQ BIT(1) +#define SET_CMDQ_KEYLEN(d, v) ((d)->des2 |= (uint32_t)v) +#define SET_CMDQ_FAS(d, v) \ + ((d)->des3 = ((d)->des3 & 0xfffffff3) | v << 2) +#define SET_CMDQ_DAS(d, v) \ + ((d)->des3 = ((d)->des3 & 0xffffffcf) | v << 4) +#define GET_CMDQ_FAS(d) ((d)->des3 & 0x0000000c) +#define GET_CMDQ_DAS(d) ((d)->des3 & 0x00000030) + __le32 reserved0; /* des4 */ + __le32 reserved1; + __le32 reserved2; + __le32 reserved3; + __le32 file_iv0; /* des8 */ + __le32 file_iv1; + __le32 file_iv2; + __le32 file_iv3; + __le32 file_enckey0; /* des12 */ + __le32 file_enckey1; + __le32 file_enckey2; + __le32 file_enckey3; + __le32 file_enckey4; + __le32 file_enckey5; + __le32 file_enckey6; + __le32 file_enckey7; + __le32 file_twkey0; /* des20 */ + __le32 file_twkey1; + __le32 file_twkey2; + __le32 file_twkey3; + __le32 file_twkey4; + __le32 file_twkey5; + __le32 file_twkey6; + __le32 file_twkey7; + __le32 disk_iv0; /* des28 */ + __le32 disk_iv1; + __le32 disk_iv2; + __le32 disk_iv3; +}; +#elif defined(CONFIG_SCSI_UFS_EXYNOS_FMP) +struct fmp_table_setting { + __le32 des0; /* des0 */ +#define GET_CMDQ_LENGTH(d) \ + (((d)->des0 & 0xffff0000) >> 16) + __le32 des1; /* des1 */ + __le32 des2; /* des2 */ + __le32 des3; /* des3 */ +/* Legacy Operation */ +#define FKL BIT(26) +#define DKL BIT(27) +#define SET_KEYLEN(d, v) ((d)->des3 |= (uint32_t)v) +#define SET_FAS(d, v) \ + ((d)->des3 = ((d)->des3 & 0xcfffffff) | v << 28) +#define SET_DAS(d, v) \ + ((d)->des3 = ((d)->des3 & 0x3fffffff) | v << 30) +#define GET_FAS(d) ((d)->des3 & 0x30000000) +#define GET_DAS(d) ((d)->des3 & 0xc0000000) +#define GET_LENGTH(d) \ + ((d)->des3 & 0x3ffffff) +/* CMDQ Operation */ +#define FKL_CMDQ BIT(0) +#define DKL_CMDQ BIT(1) +#define SET_CMDQ_KEYLEN(d, v) ((d)->des3 |= (uint32_t)v) +#define SET_CMDQ_FAS(d, v) \ + ((d)->des3 = ((d)->des3 & 0xfffffff3) | v << 2) +#define SET_CMDQ_DAS(d, v) \ + ((d)->des3 = ((d)->des3 & 0xffffffcf) | v << 4) +#define GET_CMDQ_FAS(d) ((d)->des3 & 0x0000000c) +#define GET_CMDQ_DAS(d) ((d)->des3 & 0x00000030) + __le32 file_iv0; /* des4 */ + __le32 file_iv1; /* des5 */ + __le32 file_iv2; /* des6 */ + __le32 file_iv3; /* des7 */ + __le32 file_enckey0; /* des8 */ + __le32 file_enckey1; /* des9 */ + __le32 file_enckey2; /* des10 */ + __le32 file_enckey3; /* des11 */ + __le32 file_enckey4; /* des12 */ + __le32 file_enckey5; /* des13 */ + __le32 file_enckey6; /* des14 */ + __le32 file_enckey7; /* des15 */ + __le32 file_twkey0; /* des16 */ + __le32 file_twkey1; /* des17 */ + __le32 file_twkey2; /* des18 */ + __le32 file_twkey3; /* des19 */ + __le32 file_twkey4; /* des20 */ + __le32 file_twkey5; /* des21 */ + __le32 file_twkey6; /* des22 */ + __le32 file_twkey7; /* des23 */ + __le32 disk_iv0; /* des24 */ + __le32 disk_iv1; /* des25 */ + __le32 disk_iv2; /* des26 */ + __le32 disk_iv3; /* des27 */ + __le32 reserved0; /* des28 */ + __le32 reserved1; /* des29 */ + __le32 reserved2; /* des30 */ + __le32 reserved3; /* des31 */ +}; +#endif + +struct fmp_data_setting { + struct fmp_crypto_info crypt[EXYNOS_FMP_ENC_MAX]; + struct fmp_table_setting *table; + bool cmdq_enabled; +}; + +#ifdef CONFIG_EXYNOS_FMP_FIPS +struct fips_result { + bool overall; + bool aes_xts; + bool aes_cbc; + bool sha256; + bool hmac; + bool integrity; +}; +#endif + +#define EXYNOS_FMP_ALGO_MODE_MASK (0x3) +#define EXYNOS_FMP_ALGO_MODE_TEST_OFFSET (0xf) +#define EXYNOS_FMP_ALGO_MODE_TEST (1 << EXYNOS_FMP_ALGO_MODE_TEST_OFFSET) + +struct fmp_test_data { + char block_type[FMP_BLOCK_TYPE_NAME_LEN]; + struct block_device *bdev; + sector_t sector; + dev_t devt; + uint32_t test_block_offset; + /* iv to submitted */ + u8 iv[FMP_IV_SIZE_16]; + /* diskcipher for test */ + struct fmp_crypto_info ci; +}; + +struct exynos_fmp { + struct device *dev; + enum fmp_disk_key_status status_disk_key; + struct fmp_test_data *test_data; +#ifdef CONFIG_EXYNOS_FMP_FIPS + struct fips_result result; + struct miscdevice miscdev; + void *test_vops; +#endif +}; + +struct fmp_request { + void *table; + bool cmdq_enabled; + void *iv; + u32 ivsize; +}; + +#if defined(CONFIG_MMC_DW_EXYNOS_FMP) || defined(CONFIG_SCSI_UFS_EXYNOS_FMP) +static inline void exynos_fmp_bypass(void *desc, bool cmdq_enabled) +{ + if (cmdq_enabled) { + SET_CMDQ_FAS((struct fmp_table_setting *)desc, 0); + SET_CMDQ_DAS((struct fmp_table_setting *)desc, 0); + } else { + SET_FAS((struct fmp_table_setting *)desc, 0); + SET_DAS((struct fmp_table_setting *)desc, 0); + } +} +#endif + +int exynos_fmp_crypt(struct fmp_crypto_info *ci, void *priv); +int exynos_fmp_clear(struct fmp_crypto_info *ci, void *priv); +int exynos_fmp_setkey(struct fmp_crypto_info *ci, + char *in_key, u32 keylen, bool persistent); +int exynos_fmp_clearkey(struct fmp_crypto_info *ci); +void *exynos_fmp_init(struct platform_device *pdev); +void exynos_fmp_exit(struct exynos_fmp *fmp); +#ifndef CONFIG_CRYPTO_MANAGER_DISABLE_TESTS +int exynos_fmp_test_crypt(struct fmp_crypto_info *ci, + const uint8_t *iv, uint32_t ivlen, uint8_t *src, + uint8_t *dst, uint32_t len, bool enc, void *priv); +#endif +#endif /* _EXYNOS_FMP_H_ */ diff --git a/include/crypto/smu.h b/include/crypto/smu.h new file mode 100644 index 000000000000..d9fa4f0c10a2 --- /dev/null +++ b/include/crypto/smu.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2016 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef _EXYNOS_SMU_H_ +#define _EXYNOS_SMU_H_ + +#include + +#define CFG_DESCTYPE_0 0x0 +#define CFG_DESCTYPE_1 0x1 +#define CFG_DESCTYPE_2 0x2 +#define CFG_DESCTYPE_3 0x3 + +#if defined(CONFIG_EXYNOS_FMP) +#define CFG_DESCTYPE CFG_DESCTYPE_3 +#else +#define CFG_DESCTYPE CFG_DESCTYPE_0 +#endif + +#define ACCESS_CONTROL_ABORT 0x14 + +enum smu_id { + SMU_EMBEDDED = 0, + SMU_UFSCARD = 1, + SMU_SDCARD = 2, +}; + +enum smu_command { + SMU_INIT = 0, + SMU_SET = 1, + SMU_ABORT = 2, +}; + +struct smu_data_setting { + int id; + int command; + int desc_type; +}; + +struct exynos_smu_variant_ops { + const char *name; + int (*init)(struct platform_device *, struct smu_data_setting *); + int (*sec_config)(struct platform_device *, struct smu_data_setting *); + int (*resume)(struct platform_device *, struct smu_data_setting *); + int (*abort)(struct platform_device *, struct smu_data_setting *); +}; + +struct exynos_smu_variant_ops *exynos_smu_get_variant_ops(struct device_node *node); +struct platform_device *exynos_smu_get_pdevice(struct device_node *node); + +#endif /* _EXYNOS_SMU_H_ */