Merge branch 'release' of git://git.kernel.org/pub/scm/linux/kernel/git/aegl/linux-2.6
[sfrench/cifs-2.6.git] / crypto / hash.c
index 4ccd22deef399aa477ba9e5c958d1516ff5dc50f..7dcff671c19b6d91a6127467b57c4db91a23fff4 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/errno.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/slab.h>
 #include <linux/seq_file.h>
 
 #include "internal.h"
@@ -22,6 +23,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, keylen);
+       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 +71,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;