// SPDX-License-Identifier: GPL-2.0+ /* * Copyright IBM Corp. 2024 * * s390 specific HMAC support. */ #define KMSG_COMPONENT "hmac_s390" #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt #include #include #include #include #include /* * KMAC param block layout for sha2 function codes: * The layout of the param block for the KMAC instruction depends on the * blocksize of the used hashing sha2-algorithm function codes. The param block * contains the hash chaining value (cv), the input message bit-length (imbl) * and the hmac-secret (key). To prevent code duplication, the sizes of all * these are calculated based on the blocksize. * * param-block: * +-------+ * | cv | * +-------+ * | imbl | * +-------+ * | key | * +-------+ * * sizes: * part | sh2-alg | calculation | size | type * -----+---------+-------------+------+-------- * cv | 224/256 | blocksize/2 | 32 | u64[8] * | 384/512 | | 64 | u128[8] * imbl | 224/256 | blocksize/8 | 8 | u64 * | 384/512 | | 16 | u128 * key | 224/256 | blocksize | 64 | u8[64] * | 384/512 | | 128 | u8[128] */ #define MAX_DIGEST_SIZE SHA512_DIGEST_SIZE #define MAX_IMBL_SIZE sizeof(u128) #define MAX_BLOCK_SIZE SHA512_BLOCK_SIZE #define SHA2_CV_SIZE(bs) ((bs) >> 1) #define SHA2_IMBL_SIZE(bs) ((bs) >> 3) #define SHA2_IMBL_OFFSET(bs) (SHA2_CV_SIZE(bs)) #define SHA2_KEY_OFFSET(bs) (SHA2_CV_SIZE(bs) + SHA2_IMBL_SIZE(bs)) struct s390_hmac_ctx { u8 key[MAX_BLOCK_SIZE]; }; union s390_kmac_gr0 { unsigned long reg; struct { unsigned long : 48; unsigned long ikp : 1; unsigned long iimp : 1; unsigned long ccup : 1; unsigned long : 6; unsigned long fc : 7; }; }; struct s390_kmac_sha2_ctx { u8 param[MAX_DIGEST_SIZE + MAX_IMBL_SIZE + MAX_BLOCK_SIZE]; union s390_kmac_gr0 gr0; u8 buf[MAX_BLOCK_SIZE]; unsigned int buflen; }; /* * kmac_sha2_set_imbl - sets the input message bit-length based on the blocksize */ static inline void kmac_sha2_set_imbl(u8 *param, unsigned int buflen, unsigned int blocksize) { u8 *imbl = param + SHA2_IMBL_OFFSET(blocksize); switch (blocksize) { case SHA256_BLOCK_SIZE: *(u64 *)imbl = (u64)buflen * BITS_PER_BYTE; break; case SHA512_BLOCK_SIZE: *(u128 *)imbl = (u128)buflen * BITS_PER_BYTE; break; default: break; } } static int hash_key(const u8 *in, unsigned int inlen, u8 *digest, unsigned int digestsize) { unsigned long func; union { struct sha256_paramblock { u32 h[8]; u64 mbl; } sha256; struct sha512_paramblock { u64 h[8]; u128 mbl; } sha512; } __packed param; #define PARAM_INIT(x, y, z) \ param.sha##x.h[0] = SHA##y ## _H0; \ param.sha##x.h[1] = SHA##y ## _H1; \ param.sha##x.h[2] = SHA##y ## _H2; \ param.sha##x.h[3] = SHA##y ## _H3; \ param.sha##x.h[4] = SHA##y ## _H4; \ param.sha##x.h[5] = SHA##y ## _H5; \ param.sha##x.h[6] = SHA##y ## _H6; \ param.sha##x.h[7] = SHA##y ## _H7; \ param.sha##x.mbl = (z) switch (digestsize) { case SHA224_DIGEST_SIZE: func = CPACF_KLMD_SHA_256; PARAM_INIT(256, 224, inlen * 8); break; case SHA256_DIGEST_SIZE: func = CPACF_KLMD_SHA_256; PARAM_INIT(256, 256, inlen * 8); break; case SHA384_DIGEST_SIZE: func = CPACF_KLMD_SHA_512; PARAM_INIT(512, 384, inlen * 8); break; case SHA512_DIGEST_SIZE: func = CPACF_KLMD_SHA_512; PARAM_INIT(512, 512, inlen * 8); break; default: return -EINVAL; } #undef PARAM_INIT cpacf_klmd(func, ¶m, in, inlen); memcpy(digest, ¶m, digestsize); return 0; } static int s390_hmac_sha2_setkey(struct crypto_shash *tfm, const u8 *key, unsigned int keylen) { struct s390_hmac_ctx *tfm_ctx = crypto_shash_ctx(tfm); unsigned int ds = crypto_shash_digestsize(tfm); unsigned int bs = crypto_shash_blocksize(tfm); memset(tfm_ctx, 0, sizeof(*tfm_ctx)); if (keylen > bs) return hash_key(key, keylen, tfm_ctx->key, ds); memcpy(tfm_ctx->key, key, keylen); return 0; } static int s390_hmac_sha2_init(struct shash_desc *desc) { struct s390_hmac_ctx *tfm_ctx = crypto_shash_ctx(desc->tfm); struct s390_kmac_sha2_ctx *ctx = shash_desc_ctx(desc); unsigned int bs = crypto_shash_blocksize(desc->tfm); memcpy(ctx->param + SHA2_KEY_OFFSET(bs), tfm_ctx->key, bs); ctx->buflen = 0; ctx->gr0.reg = 0; switch (crypto_shash_digestsize(desc->tfm)) { case SHA224_DIGEST_SIZE: ctx->gr0.fc = CPACF_KMAC_HMAC_SHA_224; break; case SHA256_DIGEST_SIZE: ctx->gr0.fc = CPACF_KMAC_HMAC_SHA_256; break; case SHA384_DIGEST_SIZE: ctx->gr0.fc = CPACF_KMAC_HMAC_SHA_384; break; case SHA512_DIGEST_SIZE: ctx->gr0.fc = CPACF_KMAC_HMAC_SHA_512; break; default: return -EINVAL; } return 0; } static int s390_hmac_sha2_update(struct shash_desc *desc, const u8 *data, unsigned int len) { struct s390_kmac_sha2_ctx *ctx = shash_desc_ctx(desc); unsigned int bs = crypto_shash_blocksize(desc->tfm); unsigned int offset, n; /* check current buffer */ offset = ctx->buflen % bs; ctx->buflen += len; if (offset + len < bs) goto store; /* process one stored block */ if (offset) { n = bs - offset; memcpy(ctx->buf + offset, data, n); ctx->gr0.iimp = 1; _cpacf_kmac(&ctx->gr0.reg, ctx->param, ctx->buf, bs); data += n; len -= n; offset = 0; } /* process as many blocks as possible */ if (len >= bs) { n = (len / bs) * bs; ctx->gr0.iimp = 1; _cpacf_kmac(&ctx->gr0.reg, ctx->param, data, n); data += n; len -= n; } store: /* store incomplete block in buffer */ if (len) memcpy(ctx->buf + offset, data, len); return 0; } static int s390_hmac_sha2_final(struct shash_desc *desc, u8 *out) { struct s390_kmac_sha2_ctx *ctx = shash_desc_ctx(desc); unsigned int bs = crypto_shash_blocksize(desc->tfm); ctx->gr0.iimp = 0; kmac_sha2_set_imbl(ctx->param, ctx->buflen, bs); _cpacf_kmac(&ctx->gr0.reg, ctx->param, ctx->buf, ctx->buflen % bs); memcpy(out, ctx->param, crypto_shash_digestsize(desc->tfm)); return 0; } static int s390_hmac_sha2_digest(struct shash_desc *desc, const u8 *data, unsigned int len, u8 *out) { struct s390_kmac_sha2_ctx *ctx = shash_desc_ctx(desc); unsigned int ds = crypto_shash_digestsize(desc->tfm); int rc; rc = s390_hmac_sha2_init(desc); if (rc) return rc; ctx->gr0.iimp = 0; kmac_sha2_set_imbl(ctx->param, len, crypto_shash_blocksize(desc->tfm)); _cpacf_kmac(&ctx->gr0.reg, ctx->param, data, len); memcpy(out, ctx->param, ds); return 0; } #define S390_HMAC_SHA2_ALG(x) { \ .fc = CPACF_KMAC_HMAC_SHA_##x, \ .alg = { \ .init = s390_hmac_sha2_init, \ .update = s390_hmac_sha2_update, \ .final = s390_hmac_sha2_final, \ .digest = s390_hmac_sha2_digest, \ .setkey = s390_hmac_sha2_setkey, \ .descsize = sizeof(struct s390_kmac_sha2_ctx), \ .halg = { \ .digestsize = SHA##x##_DIGEST_SIZE, \ .base = { \ .cra_name = "hmac(sha" #x ")", \ .cra_driver_name = "hmac_s390_sha" #x, \ .cra_blocksize = SHA##x##_BLOCK_SIZE, \ .cra_priority = 400, \ .cra_ctxsize = sizeof(struct s390_hmac_ctx), \ .cra_module = THIS_MODULE, \ }, \ }, \ }, \ } static struct s390_hmac_alg { bool registered; unsigned int fc; struct shash_alg alg; } s390_hmac_algs[] = { S390_HMAC_SHA2_ALG(224), S390_HMAC_SHA2_ALG(256), S390_HMAC_SHA2_ALG(384), S390_HMAC_SHA2_ALG(512), }; static __always_inline void _s390_hmac_algs_unregister(void) { struct s390_hmac_alg *hmac; int i; for (i = ARRAY_SIZE(s390_hmac_algs) - 1; i >= 0; i--) { hmac = &s390_hmac_algs[i]; if (!hmac->registered) continue; crypto_unregister_shash(&hmac->alg); } } static int __init hmac_s390_init(void) { struct s390_hmac_alg *hmac; int i, rc = -ENODEV; if (!cpacf_query_func(CPACF_KLMD, CPACF_KLMD_SHA_256)) return -ENODEV; if (!cpacf_query_func(CPACF_KLMD, CPACF_KLMD_SHA_512)) return -ENODEV; for (i = 0; i < ARRAY_SIZE(s390_hmac_algs); i++) { hmac = &s390_hmac_algs[i]; if (!cpacf_query_func(CPACF_KMAC, hmac->fc)) continue; rc = crypto_register_shash(&hmac->alg); if (rc) { pr_err("unable to register %s\n", hmac->alg.halg.base.cra_name); goto out; } hmac->registered = true; pr_debug("registered %s\n", hmac->alg.halg.base.cra_name); } return rc; out: _s390_hmac_algs_unregister(); return rc; } static void __exit hmac_s390_exit(void) { _s390_hmac_algs_unregister(); } module_cpu_feature_match(S390_CPU_FEATURE_MSA, hmac_s390_init); module_exit(hmac_s390_exit); MODULE_DESCRIPTION("S390 HMAC driver"); MODULE_LICENSE("GPL");