diff --git a/drivers/crypto/rockchip/rk_crypto_core.c b/drivers/crypto/rockchip/rk_crypto_core.c index 373a294116b0..de7b73c61504 100644 --- a/drivers/crypto/rockchip/rk_crypto_core.c +++ b/drivers/crypto/rockchip/rk_crypto_core.c @@ -303,7 +303,7 @@ static int rk_crypto_register(struct rk_crypto_info *crypto_info) if (tmp_algs->type == ALG_TYPE_CIPHER) err = crypto_register_alg(&tmp_algs->alg.crypto); - else if (tmp_algs->type == ALG_TYPE_HASH) + else if (tmp_algs->type == ALG_TYPE_HASH || tmp_algs->type == ALG_TYPE_HMAC) err = crypto_register_ahash(&tmp_algs->alg.hash); else continue; @@ -321,7 +321,7 @@ err_cipher_algs: tmp_algs = rk_crypto_find_algs(crypto_info, *algs_name); if (tmp_algs->type == ALG_TYPE_CIPHER) crypto_unregister_alg(&tmp_algs->alg.crypto); - else if (tmp_algs->type == ALG_TYPE_HASH) + else if (tmp_algs->type == ALG_TYPE_HASH || tmp_algs->type == ALG_TYPE_HMAC) crypto_unregister_ahash(&tmp_algs->alg.hash); } return err; @@ -339,7 +339,7 @@ static void rk_crypto_unregister(struct rk_crypto_info *crypto_info) tmp_algs = rk_crypto_find_algs(crypto_info, *algs_name); if (tmp_algs->type == ALG_TYPE_CIPHER) crypto_unregister_alg(&tmp_algs->alg.crypto); - else if (tmp_algs->type == ALG_TYPE_HASH) + else if (tmp_algs->type == ALG_TYPE_HASH || tmp_algs->type == ALG_TYPE_HMAC) crypto_unregister_ahash(&tmp_algs->alg.hash); } } @@ -383,6 +383,12 @@ static struct rk_crypto_tmp *crypto_v2_algs[] = { &rk_v2_ahash_sha512, /* sha512 */ &rk_v2_ahash_md5, /* md5 */ &rk_v2_ahash_sm3, /* sm3 */ + + &rk_v2_ahash_hmac_sha1, /* hmac(sha1) */ + &rk_v2_ahash_hmac_sha256, /* hmac(sha256) */ + &rk_v2_ahash_hmac_sha512, /* hmac(sha512) */ + &rk_v2_ahash_hmac_md5, /* hmac(md5) */ + &rk_v2_ahash_hmac_sm3, /* hmac(sm3) */ }; static char *px30_algs_name[] = { @@ -397,6 +403,7 @@ static char *rv1126_algs_name[] = { "ecb(des)", "cbc(des)", "ecb(des3_ede)", "cbc(des3_ede)", "sha1", "sha256", "sha512", "md5", "sm3", + "hmac(sha1)", "hmac(sha256)", "hmac(sha512)", "hmac(md5)", "hmac(sm3)", }; static const struct rk_crypto_soc_data px30_soc_data = diff --git a/drivers/crypto/rockchip/rk_crypto_core.h b/drivers/crypto/rockchip/rk_crypto_core.h index 099c36493232..b1c8f23f4fe1 100644 --- a/drivers/crypto/rockchip/rk_crypto_core.h +++ b/drivers/crypto/rockchip/rk_crypto_core.h @@ -82,6 +82,8 @@ struct rk_crypto_info { /* the private variable of hash */ struct rk_ahash_ctx { struct rk_crypto_info *dev; + u8 authkey[SHA512_BLOCK_SIZE]; + /* for fallback */ struct crypto_ahash *fallback_tfm; }; @@ -103,6 +105,7 @@ struct rk_cipher_ctx { enum alg_type { ALG_TYPE_HASH, + ALG_TYPE_HMAC, ALG_TYPE_CIPHER, }; @@ -238,6 +241,41 @@ enum rk_cipher_mode { } \ } +#define RK_HMAC_ALGO_INIT(hash_algo, algo_name) {\ + .name = "hmac(" #algo_name ")",\ + .type = ALG_TYPE_HMAC,\ + .algo = HASH_ALGO_##hash_algo,\ + .alg.hash = {\ + .init = rk_ahash_init,\ + .update = rk_ahash_update,\ + .final = rk_ahash_final,\ + .finup = rk_ahash_finup,\ + .export = rk_ahash_export,\ + .import = rk_ahash_import,\ + .digest = rk_ahash_digest,\ + .setkey = rk_ahash_hmac_setkey,\ + .halg = {\ + .digestsize = hash_algo##_DIGEST_SIZE,\ + .statesize = sizeof(struct algo_name##_state),\ + .base = {\ + .cra_name = "hmac(" #algo_name ")",\ + .cra_driver_name = "hmac-" #algo_name "-rk",\ + .cra_priority = RK_CRYPTO_PRIORITY,\ + .cra_flags = CRYPTO_ALG_ASYNC |\ + CRYPTO_ALG_NEED_FALLBACK,\ + .cra_blocksize = hash_algo##_BLOCK_SIZE,\ + .cra_ctxsize = sizeof(struct rk_ahash_ctx),\ + .cra_alignmask = 3,\ + .cra_init = rk_cra_hash_init,\ + .cra_exit = rk_cra_hash_exit,\ + .cra_module = THIS_MODULE,\ + } \ + } \ + } \ +} + +#define IS_TYPE_HMAC(type) ((type) == ALG_TYPE_HMAC) + #define CRYPTO_READ(dev, offset) \ readl_relaxed(((dev)->reg + (offset))) #define CRYPTO_WRITE(dev, offset, val) \ diff --git a/drivers/crypto/rockchip/rk_crypto_v2.h b/drivers/crypto/rockchip/rk_crypto_v2.h index 525d7e424fde..7999a10fd58e 100644 --- a/drivers/crypto/rockchip/rk_crypto_v2.h +++ b/drivers/crypto/rockchip/rk_crypto_v2.h @@ -42,6 +42,12 @@ extern struct rk_crypto_tmp rk_v2_ahash_sha512; extern struct rk_crypto_tmp rk_v2_ahash_md5; extern struct rk_crypto_tmp rk_v2_ahash_sm3; +extern struct rk_crypto_tmp rk_v2_ahash_hmac_md5; +extern struct rk_crypto_tmp rk_v2_ahash_hmac_sha1; +extern struct rk_crypto_tmp rk_v2_ahash_hmac_sha256; +extern struct rk_crypto_tmp rk_v2_ahash_hmac_sha512; +extern struct rk_crypto_tmp rk_v2_ahash_hmac_sm3; + int rk_hw_crypto_v2_init(struct device *dev, void *hw_info); void rk_hw_crypto_v2_deinit(struct device *dev, void *hw_info); #endif diff --git a/drivers/crypto/rockchip/rk_crypto_v2_ahash.c b/drivers/crypto/rockchip/rk_crypto_v2_ahash.c index 61123a001c4d..a4459b8401a3 100644 --- a/drivers/crypto/rockchip/rk_crypto_v2_ahash.c +++ b/drivers/crypto/rockchip/rk_crypto_v2_ahash.c @@ -69,6 +69,14 @@ static const u32 hash_algo2bc[] = { [HASH_ALGO_SM3] = CRYPTO_SM3, }; +const char *hash_algo2name[] = { + [HASH_ALGO_MD5] = "md5", + [HASH_ALGO_SHA1] = "sha1", + [HASH_ALGO_SHA256] = "sha256", + [HASH_ALGO_SHA512] = "sha512", + [HASH_ALGO_SM3] = "sm3", +}; + static void word2byte(u32 word, u8 *ch, u32 endian) { /* 0: Big-Endian 1: Little-Endian */ @@ -90,6 +98,20 @@ static void word2byte(u32 word, u8 *ch, u32 endian) } } +static u32 byte2word(const u8 *ch, u32 endian) +{ + u32 w = 0; + + /* 0: Big-Endian 1: Little-Endian */ + if (endian == BIG_ENDIAN) + w = (*ch << 24) + (*(ch + 1) << 16) + + (*(ch + 2) << 8) + *(ch + 3); + else if (endian == LITTLE_ENDIAN) + w = (*(ch + 3) << 24) + (*(ch + 2) << 16) + + (*(ch + 1) << 8) + *ch; + + return w; +} static struct rk_crypto_tmp *rk_ahash_get_algt(struct crypto_ahash *tfm) { @@ -181,6 +203,19 @@ static inline void clear_hash_out_reg(struct rk_crypto_info *dev) CRYPTO_WRITE(dev, CRYPTO_HASH_DOUT_0 + 4 * i, 0); } +static void write_key_reg(struct rk_crypto_info *dev, const u8 *key, + u32 key_len) +{ + u32 i; + u32 chn_base_addr; + + chn_base_addr = CRYPTO_CH0_KEY_0; + + for (i = 0; i < key_len / 4; i++, chn_base_addr += 4) + CRYPTO_WRITE(dev, chn_base_addr, + byte2word(key + i * 4, BIG_ENDIAN)); +} + static int rk_ahash_init(struct ahash_request *req) { struct rk_ahash_rctx *rctx = ahash_request_ctx(req); @@ -281,16 +316,108 @@ static int rk_ahash_export(struct ahash_request *req, void *out) static int rk_ahash_digest(struct ahash_request *req) { struct rk_ahash_ctx *tctx = crypto_tfm_ctx(req->base.tfm); + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct rk_crypto_tmp *algt = rk_ahash_get_algt(tfm); struct rk_crypto_info *dev = tctx->dev; CRYPTO_TRACE("calc data %u bytes.", req->nbytes); if (!req->nbytes) - return zero_message_process(req); + return IS_TYPE_HMAC(algt->type) ? + crypto_ahash_digest(req) : + zero_message_process(req); else return dev->enqueue(dev, &req->base); } +static int rk_ahash_calc_digest(const char *alg_name, const u8 *key, u32 keylen, + u8 *digest, u32 digest_len) +{ + struct crypto_ahash *ahash_tfm; + struct ahash_request *req; + struct crypto_wait wait; + struct scatterlist sg; + int ret; + + CRYPTO_TRACE(); + + ahash_tfm = crypto_alloc_ahash(alg_name, 0, CRYPTO_ALG_NEED_FALLBACK); + if (IS_ERR(ahash_tfm)) + return PTR_ERR(ahash_tfm); + + req = ahash_request_alloc(ahash_tfm, GFP_KERNEL); + if (!req) { + crypto_free_ahash(ahash_tfm); + return -ENOMEM; + } + + init_completion(&wait.completion); + + crypto_ahash_clear_flags(ahash_tfm, ~0); + + sg_init_one(&sg, key, keylen); + ahash_request_set_crypt(req, &sg, digest, keylen); + + ret = crypto_wait_req(crypto_ahash_digest(req), &wait); + if (ret) { + CRYPTO_MSG("digest failed, ret = %d", ret); + goto exit; + } + +exit: + ahash_request_free(req); + crypto_free_ahash(ahash_tfm); + + return ret; +} + +static int rk_ahash_hmac_setkey(struct crypto_ahash *tfm, const u8 *key, + unsigned int keylen) +{ + unsigned int blocksize = crypto_tfm_alg_blocksize(crypto_ahash_tfm(tfm)); + unsigned int digestsize = crypto_ahash_digestsize(tfm); + struct rk_crypto_tmp *algt = rk_ahash_get_algt(tfm); + struct rk_ahash_ctx *ctx = crypto_ahash_ctx(tfm); + const char *alg_name; + int ret; + + CRYPTO_MSG(); + + crypto_ahash_clear_flags(ctx->fallback_tfm, ~0); + ret = crypto_ahash_setkey(ctx->fallback_tfm, key, keylen); + if (ret) { + CRYPTO_MSG("setkey failed, ret = %d\n", ret); + goto exit; + } + + memset(ctx->authkey, 0, sizeof(ctx->authkey)); + + if (keylen <= blocksize) { + memcpy(ctx->authkey, key, keylen); + goto exit; + } + + if (algt->algo >= ARRAY_SIZE(hash_algo2name)) { + CRYPTO_MSG("hash algo %d invalid\n", algt->algo); + return -EINVAL; + } + + alg_name = hash_algo2name[algt->algo]; + + CRYPTO_TRACE("calc key digest %s", alg_name); + + ret = rk_ahash_calc_digest(alg_name, key, keylen, ctx->authkey, digestsize); + if (ret) { + CRYPTO_MSG("rk_ahash_calc_digest error ret = %d\n", ret); + goto exit; + } + +exit: + if (ret) + crypto_ahash_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN); + return ret; +} + static void rk_ahash_dma_start(struct rk_crypto_info *dev) { struct rk_hw_crypto_v2_info *hw_info = @@ -334,6 +461,7 @@ static int rk_ahash_start(struct rk_crypto_info *dev) struct ahash_request *req = ahash_request_cast(dev->async_req); struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); struct rk_crypto_tmp *algt = rk_ahash_get_algt(tfm); + struct rk_ahash_ctx *ctx = crypto_ahash_ctx(tfm); u32 reg_ctrl = 0; CRYPTO_TRACE(); @@ -354,6 +482,13 @@ static int rk_ahash_start(struct rk_crypto_info *dev) clear_hash_out_reg(dev); reg_ctrl |= CRYPTO_HW_PAD_ENABLE; + + if (IS_TYPE_HMAC(algt->type)) { + CRYPTO_TRACE("this is hmac"); + reg_ctrl |= CRYPTO_HMAC_ENABLE; + write_key_reg(dev, ctx->authkey, sizeof(ctx->authkey)); + } + CRYPTO_WRITE(dev, CRYPTO_HASH_CTL, reg_ctrl | CRYPTO_WRITE_MASK_ALL); CRYPTO_WRITE(dev, CRYPTO_FIFO_CTL, 0x00030003); @@ -490,3 +625,9 @@ struct rk_crypto_tmp rk_v2_ahash_sha256 = RK_HASH_ALGO_INIT(SHA256, sha256); struct rk_crypto_tmp rk_v2_ahash_sha512 = RK_HASH_ALGO_INIT(SHA512, sha512); struct rk_crypto_tmp rk_v2_ahash_sm3 = RK_HASH_ALGO_INIT(SM3, sm3); +struct rk_crypto_tmp rk_v2_ahash_hmac_md5 = RK_HMAC_ALGO_INIT(MD5, md5); +struct rk_crypto_tmp rk_v2_ahash_hmac_sha1 = RK_HMAC_ALGO_INIT(SHA1, sha1); +struct rk_crypto_tmp rk_v2_ahash_hmac_sha256 = RK_HMAC_ALGO_INIT(SHA256, sha256); +struct rk_crypto_tmp rk_v2_ahash_hmac_sha512 = RK_HMAC_ALGO_INIT(SHA512, sha512); +struct rk_crypto_tmp rk_v2_ahash_hmac_sm3 = RK_HMAC_ALGO_INIT(SM3, sm3); +