From: Sebastian Siewior Date: Sat, 19 May 2007 09:51:21 +0000 (+1000) Subject: [CRYPTO] api: Handle unaligned keys in setkey X-Git-Tag: v2.6.23-rc1~1054^2~8^2~2 X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ca7c39385ce1a7b44894a4b225a4608624e90730;p=linux-2.6 [CRYPTO] api: Handle unaligned keys in setkey setkey() in {cipher,blkcipher,ablkcipher,hash}.c does not respect the requested alignment by the algorithm. This patch fixes it. The extra memory is allocated by kmalloc() with GFP_ATOMIC flag. Signed-off-by: Sebastian Siewior Signed-off-by: Herbert Xu --- diff --git a/crypto/ablkcipher.c b/crypto/ablkcipher.c index 9348ddd84a..d45fa16dff 100644 --- a/crypto/ablkcipher.c +++ b/crypto/ablkcipher.c @@ -19,16 +19,41 @@ #include #include +static int setkey_unaligned(struct crypto_ablkcipher *tfm, const u8 *key, unsigned int keylen) +{ + struct ablkcipher_alg *cipher = crypto_ablkcipher_alg(tfm); + unsigned long alignmask = crypto_ablkcipher_alignmask(tfm); + int ret; + u8 *buffer, *alignbuffer; + unsigned long absize; + + absize = keylen + alignmask; + buffer = kmalloc(absize, GFP_ATOMIC); + if (!buffer) + return -ENOMEM; + + alignbuffer = (u8 *)ALIGN((unsigned long)buffer, alignmask + 1); + memcpy(alignbuffer, key, keylen); + ret = cipher->setkey(tfm, alignbuffer, keylen); + memset(alignbuffer, 0, absize); + kfree(buffer); + return ret; +} + static int setkey(struct crypto_ablkcipher *tfm, const u8 *key, unsigned int keylen) { struct ablkcipher_alg *cipher = crypto_ablkcipher_alg(tfm); + unsigned long alignmask = crypto_ablkcipher_alignmask(tfm); if (keylen < cipher->min_keysize || keylen > cipher->max_keysize) { crypto_ablkcipher_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN); return -EINVAL; } + if ((unsigned long)key & alignmask) + return setkey_unaligned(tfm, key, keylen); + return cipher->setkey(tfm, key, keylen); } diff --git a/crypto/blkcipher.c b/crypto/blkcipher.c index 8edf40c835..40a3dcff15 100644 --- a/crypto/blkcipher.c +++ b/crypto/blkcipher.c @@ -336,16 +336,41 @@ static int blkcipher_walk_first(struct blkcipher_desc *desc, return blkcipher_walk_next(desc, walk); } +static int setkey_unaligned(struct crypto_tfm *tfm, const u8 *key, unsigned int keylen) +{ + struct blkcipher_alg *cipher = &tfm->__crt_alg->cra_blkcipher; + unsigned long alignmask = crypto_tfm_alg_alignmask(tfm); + int ret; + u8 *buffer, *alignbuffer; + unsigned long absize; + + absize = keylen + alignmask; + buffer = kmalloc(absize, GFP_ATOMIC); + if (!buffer) + return -ENOMEM; + + alignbuffer = (u8 *)ALIGN((unsigned long)buffer, alignmask + 1); + memcpy(alignbuffer, key, keylen); + ret = cipher->setkey(tfm, alignbuffer, keylen); + memset(alignbuffer, 0, absize); + kfree(buffer); + return ret; +} + static int setkey(struct crypto_tfm *tfm, const u8 *key, unsigned int keylen) { struct blkcipher_alg *cipher = &tfm->__crt_alg->cra_blkcipher; + unsigned long alignmask = crypto_tfm_alg_alignmask(tfm); if (keylen < cipher->min_keysize || keylen > cipher->max_keysize) { tfm->crt_flags |= CRYPTO_TFM_RES_BAD_KEY_LEN; return -EINVAL; } + if ((unsigned long)key & alignmask) + return setkey_unaligned(tfm, key, keylen); + return cipher->setkey(tfm, key, keylen); } diff --git a/crypto/cipher.c b/crypto/cipher.c index 333aab2f02..0b2650c201 100644 --- a/crypto/cipher.c +++ b/crypto/cipher.c @@ -20,16 +20,43 @@ #include #include "internal.h" +static int setkey_unaligned(struct crypto_tfm *tfm, const u8 *key, unsigned int keylen) +{ + struct cipher_alg *cia = &tfm->__crt_alg->cra_cipher; + unsigned long alignmask = crypto_tfm_alg_alignmask(tfm); + int ret; + u8 *buffer, *alignbuffer; + unsigned long absize; + + absize = keylen + alignmask; + buffer = kmalloc(absize, GFP_ATOMIC); + if (!buffer) + return -ENOMEM; + + alignbuffer = (u8 *)ALIGN((unsigned long)buffer, alignmask + 1); + memcpy(alignbuffer, key, keylen); + ret = cia->cia_setkey(tfm, alignbuffer, keylen); + memset(alignbuffer, 0, absize); + kfree(buffer); + return ret; + +} + static int setkey(struct crypto_tfm *tfm, const u8 *key, unsigned int keylen) { struct cipher_alg *cia = &tfm->__crt_alg->cra_cipher; - + unsigned long alignmask = crypto_tfm_alg_alignmask(tfm); + tfm->crt_flags &= ~CRYPTO_TFM_RES_MASK; if (keylen < cia->cia_min_keysize || keylen > cia->cia_max_keysize) { tfm->crt_flags |= CRYPTO_TFM_RES_BAD_KEY_LEN; return -EINVAL; - } else - return cia->cia_setkey(tfm, key, keylen); + } + + if ((unsigned long)key & alignmask) + return setkey_unaligned(tfm, key, keylen); + + return cia->cia_setkey(tfm, key, keylen); } static void cipher_crypt_unaligned(void (*fn)(struct crypto_tfm *, u8 *, diff --git a/crypto/hash.c b/crypto/hash.c index 4ccd22deef..4d75ca7b57 100644 --- a/crypto/hash.c +++ b/crypto/hash.c @@ -22,6 +22,42 @@ static unsigned int crypto_hash_ctxsize(struct crypto_alg *alg, u32 type, return alg->cra_ctxsize; } +static int hash_setkey_unaligned(struct crypto_hash *crt, const u8 *key, + unsigned int keylen) +{ + struct crypto_tfm *tfm = crypto_hash_tfm(crt); + struct hash_alg *alg = &tfm->__crt_alg->cra_hash; + unsigned long alignmask = crypto_hash_alignmask(crt); + int ret; + u8 *buffer, *alignbuffer; + unsigned long absize; + + absize = keylen + alignmask; + buffer = kmalloc(absize, GFP_ATOMIC); + if (!buffer) + return -ENOMEM; + + alignbuffer = (u8 *)ALIGN((unsigned long)buffer, alignmask + 1); + memcpy(alignbuffer, key, keylen); + ret = alg->setkey(crt, alignbuffer, keylen); + memset(alignbuffer, 0, absize); + kfree(buffer); + return ret; +} + +static int hash_setkey(struct crypto_hash *crt, const u8 *key, + unsigned int keylen) +{ + struct crypto_tfm *tfm = crypto_hash_tfm(crt); + struct hash_alg *alg = &tfm->__crt_alg->cra_hash; + unsigned long alignmask = crypto_hash_alignmask(crt); + + if ((unsigned long)key & alignmask) + return hash_setkey_unaligned(crt, key, keylen); + + return alg->setkey(crt, key, keylen); +} + static int crypto_init_hash_ops(struct crypto_tfm *tfm, u32 type, u32 mask) { struct hash_tfm *crt = &tfm->crt_hash; @@ -34,7 +70,7 @@ static int crypto_init_hash_ops(struct crypto_tfm *tfm, u32 type, u32 mask) crt->update = alg->update; crt->final = alg->final; crt->digest = alg->digest; - crt->setkey = alg->setkey; + crt->setkey = hash_setkey; crt->digestsize = alg->digestsize; return 0;