|  | /* | 
|  | * Cryptographic API. | 
|  | * | 
|  | * s390 implementation of the AES Cipher Algorithm. | 
|  | * | 
|  | * s390 Version: | 
|  | *   Copyright (C) 2005 IBM Deutschland GmbH, IBM Corporation | 
|  | *   Author(s): Jan Glauber (jang@de.ibm.com) | 
|  | * | 
|  | * Derived from "crypto/aes.c" | 
|  | * | 
|  | * 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 <linux/module.h> | 
|  | #include <linux/init.h> | 
|  | #include <linux/crypto.h> | 
|  | #include "crypt_s390.h" | 
|  |  | 
|  | #define AES_MIN_KEY_SIZE	16 | 
|  | #define AES_MAX_KEY_SIZE	32 | 
|  |  | 
|  | /* data block size for all key lengths */ | 
|  | #define AES_BLOCK_SIZE		16 | 
|  |  | 
|  | int has_aes_128 = 0; | 
|  | int has_aes_192 = 0; | 
|  | int has_aes_256 = 0; | 
|  |  | 
|  | struct s390_aes_ctx { | 
|  | u8 iv[AES_BLOCK_SIZE]; | 
|  | u8 key[AES_MAX_KEY_SIZE]; | 
|  | int key_len; | 
|  | }; | 
|  |  | 
|  | static int aes_set_key(struct crypto_tfm *tfm, const u8 *in_key, | 
|  | unsigned int key_len, u32 *flags) | 
|  | { | 
|  | struct s390_aes_ctx *sctx = crypto_tfm_ctx(tfm); | 
|  |  | 
|  | switch (key_len) { | 
|  | case 16: | 
|  | if (!has_aes_128) | 
|  | goto fail; | 
|  | break; | 
|  | case 24: | 
|  | if (!has_aes_192) | 
|  | goto fail; | 
|  |  | 
|  | break; | 
|  | case 32: | 
|  | if (!has_aes_256) | 
|  | goto fail; | 
|  | break; | 
|  | default: | 
|  | /* invalid key length */ | 
|  | goto fail; | 
|  | break; | 
|  | } | 
|  |  | 
|  | sctx->key_len = key_len; | 
|  | memcpy(sctx->key, in_key, key_len); | 
|  | return 0; | 
|  | fail: | 
|  | *flags |= CRYPTO_TFM_RES_BAD_KEY_LEN; | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | static void aes_encrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in) | 
|  | { | 
|  | const struct s390_aes_ctx *sctx = crypto_tfm_ctx(tfm); | 
|  |  | 
|  | switch (sctx->key_len) { | 
|  | case 16: | 
|  | crypt_s390_km(KM_AES_128_ENCRYPT, &sctx->key, out, in, | 
|  | AES_BLOCK_SIZE); | 
|  | break; | 
|  | case 24: | 
|  | crypt_s390_km(KM_AES_192_ENCRYPT, &sctx->key, out, in, | 
|  | AES_BLOCK_SIZE); | 
|  | break; | 
|  | case 32: | 
|  | crypt_s390_km(KM_AES_256_ENCRYPT, &sctx->key, out, in, | 
|  | AES_BLOCK_SIZE); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | static void aes_decrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in) | 
|  | { | 
|  | const struct s390_aes_ctx *sctx = crypto_tfm_ctx(tfm); | 
|  |  | 
|  | switch (sctx->key_len) { | 
|  | case 16: | 
|  | crypt_s390_km(KM_AES_128_DECRYPT, &sctx->key, out, in, | 
|  | AES_BLOCK_SIZE); | 
|  | break; | 
|  | case 24: | 
|  | crypt_s390_km(KM_AES_192_DECRYPT, &sctx->key, out, in, | 
|  | AES_BLOCK_SIZE); | 
|  | break; | 
|  | case 32: | 
|  | crypt_s390_km(KM_AES_256_DECRYPT, &sctx->key, out, in, | 
|  | AES_BLOCK_SIZE); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | static unsigned int aes_encrypt_ecb(const struct cipher_desc *desc, u8 *out, | 
|  | const u8 *in, unsigned int nbytes) | 
|  | { | 
|  | struct s390_aes_ctx *sctx = crypto_tfm_ctx(desc->tfm); | 
|  | int ret; | 
|  |  | 
|  | /* only use complete blocks */ | 
|  | nbytes &= ~(AES_BLOCK_SIZE - 1); | 
|  |  | 
|  | switch (sctx->key_len) { | 
|  | case 16: | 
|  | ret = crypt_s390_km(KM_AES_128_ENCRYPT, &sctx->key, out, in, nbytes); | 
|  | BUG_ON((ret < 0) || (ret != nbytes)); | 
|  | break; | 
|  | case 24: | 
|  | ret = crypt_s390_km(KM_AES_192_ENCRYPT, &sctx->key, out, in, nbytes); | 
|  | BUG_ON((ret < 0) || (ret != nbytes)); | 
|  | break; | 
|  | case 32: | 
|  | ret = crypt_s390_km(KM_AES_256_ENCRYPT, &sctx->key, out, in, nbytes); | 
|  | BUG_ON((ret < 0) || (ret != nbytes)); | 
|  | break; | 
|  | } | 
|  | return nbytes; | 
|  | } | 
|  |  | 
|  | static unsigned int aes_decrypt_ecb(const struct cipher_desc *desc, u8 *out, | 
|  | const u8 *in, unsigned int nbytes) | 
|  | { | 
|  | struct s390_aes_ctx *sctx = crypto_tfm_ctx(desc->tfm); | 
|  | int ret; | 
|  |  | 
|  | /* only use complete blocks */ | 
|  | nbytes &= ~(AES_BLOCK_SIZE - 1); | 
|  |  | 
|  | switch (sctx->key_len) { | 
|  | case 16: | 
|  | ret = crypt_s390_km(KM_AES_128_DECRYPT, &sctx->key, out, in, nbytes); | 
|  | BUG_ON((ret < 0) || (ret != nbytes)); | 
|  | break; | 
|  | case 24: | 
|  | ret = crypt_s390_km(KM_AES_192_DECRYPT, &sctx->key, out, in, nbytes); | 
|  | BUG_ON((ret < 0) || (ret != nbytes)); | 
|  | break; | 
|  | case 32: | 
|  | ret = crypt_s390_km(KM_AES_256_DECRYPT, &sctx->key, out, in, nbytes); | 
|  | BUG_ON((ret < 0) || (ret != nbytes)); | 
|  | break; | 
|  | } | 
|  | return nbytes; | 
|  | } | 
|  |  | 
|  | static unsigned int aes_encrypt_cbc(const struct cipher_desc *desc, u8 *out, | 
|  | const u8 *in, unsigned int nbytes) | 
|  | { | 
|  | struct s390_aes_ctx *sctx = crypto_tfm_ctx(desc->tfm); | 
|  | int ret; | 
|  |  | 
|  | /* only use complete blocks */ | 
|  | nbytes &= ~(AES_BLOCK_SIZE - 1); | 
|  |  | 
|  | memcpy(&sctx->iv, desc->info, AES_BLOCK_SIZE); | 
|  | switch (sctx->key_len) { | 
|  | case 16: | 
|  | ret = crypt_s390_kmc(KMC_AES_128_ENCRYPT, &sctx->iv, out, in, nbytes); | 
|  | BUG_ON((ret < 0) || (ret != nbytes)); | 
|  | break; | 
|  | case 24: | 
|  | ret = crypt_s390_kmc(KMC_AES_192_ENCRYPT, &sctx->iv, out, in, nbytes); | 
|  | BUG_ON((ret < 0) || (ret != nbytes)); | 
|  | break; | 
|  | case 32: | 
|  | ret = crypt_s390_kmc(KMC_AES_256_ENCRYPT, &sctx->iv, out, in, nbytes); | 
|  | BUG_ON((ret < 0) || (ret != nbytes)); | 
|  | break; | 
|  | } | 
|  | memcpy(desc->info, &sctx->iv, AES_BLOCK_SIZE); | 
|  |  | 
|  | return nbytes; | 
|  | } | 
|  |  | 
|  | static unsigned int aes_decrypt_cbc(const struct cipher_desc *desc, u8 *out, | 
|  | const u8 *in, unsigned int nbytes) | 
|  | { | 
|  | struct s390_aes_ctx *sctx = crypto_tfm_ctx(desc->tfm); | 
|  | int ret; | 
|  |  | 
|  | /* only use complete blocks */ | 
|  | nbytes &= ~(AES_BLOCK_SIZE - 1); | 
|  |  | 
|  | memcpy(&sctx->iv, desc->info, AES_BLOCK_SIZE); | 
|  | switch (sctx->key_len) { | 
|  | case 16: | 
|  | ret = crypt_s390_kmc(KMC_AES_128_DECRYPT, &sctx->iv, out, in, nbytes); | 
|  | BUG_ON((ret < 0) || (ret != nbytes)); | 
|  | break; | 
|  | case 24: | 
|  | ret = crypt_s390_kmc(KMC_AES_192_DECRYPT, &sctx->iv, out, in, nbytes); | 
|  | BUG_ON((ret < 0) || (ret != nbytes)); | 
|  | break; | 
|  | case 32: | 
|  | ret = crypt_s390_kmc(KMC_AES_256_DECRYPT, &sctx->iv, out, in, nbytes); | 
|  | BUG_ON((ret < 0) || (ret != nbytes)); | 
|  | break; | 
|  | } | 
|  | return nbytes; | 
|  | } | 
|  |  | 
|  |  | 
|  | static struct crypto_alg aes_alg = { | 
|  | .cra_name		=	"aes", | 
|  | .cra_flags		=	CRYPTO_ALG_TYPE_CIPHER, | 
|  | .cra_blocksize		=	AES_BLOCK_SIZE, | 
|  | .cra_ctxsize		=	sizeof(struct s390_aes_ctx), | 
|  | .cra_module		=	THIS_MODULE, | 
|  | .cra_list		=	LIST_HEAD_INIT(aes_alg.cra_list), | 
|  | .cra_u			=	{ | 
|  | .cipher = { | 
|  | .cia_min_keysize	=	AES_MIN_KEY_SIZE, | 
|  | .cia_max_keysize	=	AES_MAX_KEY_SIZE, | 
|  | .cia_setkey		=	aes_set_key, | 
|  | .cia_encrypt		=	aes_encrypt, | 
|  | .cia_decrypt		=	aes_decrypt, | 
|  | .cia_encrypt_ecb	=	aes_encrypt_ecb, | 
|  | .cia_decrypt_ecb	=	aes_decrypt_ecb, | 
|  | .cia_encrypt_cbc	=	aes_encrypt_cbc, | 
|  | .cia_decrypt_cbc	=	aes_decrypt_cbc, | 
|  | } | 
|  | } | 
|  | }; | 
|  |  | 
|  | static int __init aes_init(void) | 
|  | { | 
|  | int ret; | 
|  |  | 
|  | if (crypt_s390_func_available(KM_AES_128_ENCRYPT)) | 
|  | has_aes_128 = 1; | 
|  | if (crypt_s390_func_available(KM_AES_192_ENCRYPT)) | 
|  | has_aes_192 = 1; | 
|  | if (crypt_s390_func_available(KM_AES_256_ENCRYPT)) | 
|  | has_aes_256 = 1; | 
|  |  | 
|  | if (!has_aes_128 && !has_aes_192 && !has_aes_256) | 
|  | return -ENOSYS; | 
|  |  | 
|  | ret = crypto_register_alg(&aes_alg); | 
|  | if (ret != 0) | 
|  | printk(KERN_INFO "crypt_s390: aes_s390 couldn't be loaded.\n"); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static void __exit aes_fini(void) | 
|  | { | 
|  | crypto_unregister_alg(&aes_alg); | 
|  | } | 
|  |  | 
|  | module_init(aes_init); | 
|  | module_exit(aes_fini); | 
|  |  | 
|  | MODULE_ALIAS("aes"); | 
|  |  | 
|  | MODULE_DESCRIPTION("Rijndael (AES) Cipher Algorithm"); | 
|  | MODULE_LICENSE("GPL"); | 
|  |  |