2019-05-27 14:55:01 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2008-08-31 13:47:27 +08:00
|
|
|
/*
|
|
|
|
* Synchronous Cryptographic Hash operations.
|
|
|
|
*
|
|
|
|
* Copyright (c) 2008 Herbert Xu <herbert@gondor.apana.org.au>
|
|
|
|
*/
|
|
|
|
|
2008-08-31 16:52:18 +08:00
|
|
|
#include <crypto/scatterwalk.h>
|
2008-08-31 13:47:27 +08:00
|
|
|
#include <crypto/internal/hash.h>
|
|
|
|
#include <linux/err.h>
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/seq_file.h>
|
2011-09-27 13:26:10 +08:00
|
|
|
#include <linux/cryptouser.h>
|
|
|
|
#include <net/netlink.h>
|
2016-12-31 23:56:23 +08:00
|
|
|
#include <linux/compiler.h>
|
2008-08-31 13:47:27 +08:00
|
|
|
|
2008-08-31 16:52:18 +08:00
|
|
|
#include "internal.h"
|
|
|
|
|
2009-02-18 16:56:59 +08:00
|
|
|
static const struct crypto_type crypto_shash_type;
|
|
|
|
|
crypto: hmac - require that the underlying hash algorithm is unkeyed
Because the HMAC template didn't check that its underlying hash
algorithm is unkeyed, trying to use "hmac(hmac(sha3-512-generic))"
through AF_ALG or through KEYCTL_DH_COMPUTE resulted in the inner HMAC
being used without having been keyed, resulting in sha3_update() being
called without sha3_init(), causing a stack buffer overflow.
This is a very old bug, but it seems to have only started causing real
problems when SHA-3 support was added (requires CONFIG_CRYPTO_SHA3)
because the innermost hash's state is ->import()ed from a zeroed buffer,
and it just so happens that other hash algorithms are fine with that,
but SHA-3 is not. However, there could be arch or hardware-dependent
hash algorithms also affected; I couldn't test everything.
Fix the bug by introducing a function crypto_shash_alg_has_setkey()
which tests whether a shash algorithm is keyed. Then update the HMAC
template to require that its underlying hash algorithm is unkeyed.
Here is a reproducer:
#include <linux/if_alg.h>
#include <sys/socket.h>
int main()
{
int algfd;
struct sockaddr_alg addr = {
.salg_type = "hash",
.salg_name = "hmac(hmac(sha3-512-generic))",
};
char key[4096] = { 0 };
algfd = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(algfd, (const struct sockaddr *)&addr, sizeof(addr));
setsockopt(algfd, SOL_ALG, ALG_SET_KEY, key, sizeof(key));
}
Here was the KASAN report from syzbot:
BUG: KASAN: stack-out-of-bounds in memcpy include/linux/string.h:341 [inline]
BUG: KASAN: stack-out-of-bounds in sha3_update+0xdf/0x2e0 crypto/sha3_generic.c:161
Write of size 4096 at addr ffff8801cca07c40 by task syzkaller076574/3044
CPU: 1 PID: 3044 Comm: syzkaller076574 Not tainted 4.14.0-mm1+ #25
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011
Call Trace:
__dump_stack lib/dump_stack.c:17 [inline]
dump_stack+0x194/0x257 lib/dump_stack.c:53
print_address_description+0x73/0x250 mm/kasan/report.c:252
kasan_report_error mm/kasan/report.c:351 [inline]
kasan_report+0x25b/0x340 mm/kasan/report.c:409
check_memory_region_inline mm/kasan/kasan.c:260 [inline]
check_memory_region+0x137/0x190 mm/kasan/kasan.c:267
memcpy+0x37/0x50 mm/kasan/kasan.c:303
memcpy include/linux/string.h:341 [inline]
sha3_update+0xdf/0x2e0 crypto/sha3_generic.c:161
crypto_shash_update+0xcb/0x220 crypto/shash.c:109
shash_finup_unaligned+0x2a/0x60 crypto/shash.c:151
crypto_shash_finup+0xc4/0x120 crypto/shash.c:165
hmac_finup+0x182/0x330 crypto/hmac.c:152
crypto_shash_finup+0xc4/0x120 crypto/shash.c:165
shash_digest_unaligned+0x9e/0xd0 crypto/shash.c:172
crypto_shash_digest+0xc4/0x120 crypto/shash.c:186
hmac_setkey+0x36a/0x690 crypto/hmac.c:66
crypto_shash_setkey+0xad/0x190 crypto/shash.c:64
shash_async_setkey+0x47/0x60 crypto/shash.c:207
crypto_ahash_setkey+0xaf/0x180 crypto/ahash.c:200
hash_setkey+0x40/0x90 crypto/algif_hash.c:446
alg_setkey crypto/af_alg.c:221 [inline]
alg_setsockopt+0x2a1/0x350 crypto/af_alg.c:254
SYSC_setsockopt net/socket.c:1851 [inline]
SyS_setsockopt+0x189/0x360 net/socket.c:1830
entry_SYSCALL_64_fastpath+0x1f/0x96
Reported-by: syzbot <syzkaller@googlegroups.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2017-11-29 10:01:38 +08:00
|
|
|
int shash_no_setkey(struct crypto_shash *tfm, const u8 *key,
|
|
|
|
unsigned int keylen)
|
2009-07-11 22:17:39 +08:00
|
|
|
{
|
|
|
|
return -ENOSYS;
|
|
|
|
}
|
crypto: hmac - require that the underlying hash algorithm is unkeyed
Because the HMAC template didn't check that its underlying hash
algorithm is unkeyed, trying to use "hmac(hmac(sha3-512-generic))"
through AF_ALG or through KEYCTL_DH_COMPUTE resulted in the inner HMAC
being used without having been keyed, resulting in sha3_update() being
called without sha3_init(), causing a stack buffer overflow.
This is a very old bug, but it seems to have only started causing real
problems when SHA-3 support was added (requires CONFIG_CRYPTO_SHA3)
because the innermost hash's state is ->import()ed from a zeroed buffer,
and it just so happens that other hash algorithms are fine with that,
but SHA-3 is not. However, there could be arch or hardware-dependent
hash algorithms also affected; I couldn't test everything.
Fix the bug by introducing a function crypto_shash_alg_has_setkey()
which tests whether a shash algorithm is keyed. Then update the HMAC
template to require that its underlying hash algorithm is unkeyed.
Here is a reproducer:
#include <linux/if_alg.h>
#include <sys/socket.h>
int main()
{
int algfd;
struct sockaddr_alg addr = {
.salg_type = "hash",
.salg_name = "hmac(hmac(sha3-512-generic))",
};
char key[4096] = { 0 };
algfd = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(algfd, (const struct sockaddr *)&addr, sizeof(addr));
setsockopt(algfd, SOL_ALG, ALG_SET_KEY, key, sizeof(key));
}
Here was the KASAN report from syzbot:
BUG: KASAN: stack-out-of-bounds in memcpy include/linux/string.h:341 [inline]
BUG: KASAN: stack-out-of-bounds in sha3_update+0xdf/0x2e0 crypto/sha3_generic.c:161
Write of size 4096 at addr ffff8801cca07c40 by task syzkaller076574/3044
CPU: 1 PID: 3044 Comm: syzkaller076574 Not tainted 4.14.0-mm1+ #25
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011
Call Trace:
__dump_stack lib/dump_stack.c:17 [inline]
dump_stack+0x194/0x257 lib/dump_stack.c:53
print_address_description+0x73/0x250 mm/kasan/report.c:252
kasan_report_error mm/kasan/report.c:351 [inline]
kasan_report+0x25b/0x340 mm/kasan/report.c:409
check_memory_region_inline mm/kasan/kasan.c:260 [inline]
check_memory_region+0x137/0x190 mm/kasan/kasan.c:267
memcpy+0x37/0x50 mm/kasan/kasan.c:303
memcpy include/linux/string.h:341 [inline]
sha3_update+0xdf/0x2e0 crypto/sha3_generic.c:161
crypto_shash_update+0xcb/0x220 crypto/shash.c:109
shash_finup_unaligned+0x2a/0x60 crypto/shash.c:151
crypto_shash_finup+0xc4/0x120 crypto/shash.c:165
hmac_finup+0x182/0x330 crypto/hmac.c:152
crypto_shash_finup+0xc4/0x120 crypto/shash.c:165
shash_digest_unaligned+0x9e/0xd0 crypto/shash.c:172
crypto_shash_digest+0xc4/0x120 crypto/shash.c:186
hmac_setkey+0x36a/0x690 crypto/hmac.c:66
crypto_shash_setkey+0xad/0x190 crypto/shash.c:64
shash_async_setkey+0x47/0x60 crypto/shash.c:207
crypto_ahash_setkey+0xaf/0x180 crypto/ahash.c:200
hash_setkey+0x40/0x90 crypto/algif_hash.c:446
alg_setkey crypto/af_alg.c:221 [inline]
alg_setsockopt+0x2a1/0x350 crypto/af_alg.c:254
SYSC_setsockopt net/socket.c:1851 [inline]
SyS_setsockopt+0x189/0x360 net/socket.c:1830
entry_SYSCALL_64_fastpath+0x1f/0x96
Reported-by: syzbot <syzkaller@googlegroups.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2017-11-29 10:01:38 +08:00
|
|
|
EXPORT_SYMBOL_GPL(shash_no_setkey);
|
2009-07-11 22:17:39 +08:00
|
|
|
|
2008-08-31 13:47:27 +08:00
|
|
|
static int shash_setkey_unaligned(struct crypto_shash *tfm, const u8 *key,
|
|
|
|
unsigned int keylen)
|
|
|
|
{
|
|
|
|
struct shash_alg *shash = crypto_shash_alg(tfm);
|
|
|
|
unsigned long alignmask = crypto_shash_alignmask(tfm);
|
|
|
|
unsigned long absize;
|
|
|
|
u8 *buffer, *alignbuffer;
|
|
|
|
int err;
|
|
|
|
|
2010-05-19 09:50:58 +08:00
|
|
|
absize = keylen + (alignmask & ~(crypto_tfm_ctx_alignment() - 1));
|
2017-10-03 10:25:22 +08:00
|
|
|
buffer = kmalloc(absize, GFP_ATOMIC);
|
2008-08-31 13:47:27 +08:00
|
|
|
if (!buffer)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
alignbuffer = (u8 *)ALIGN((unsigned long)buffer, alignmask + 1);
|
|
|
|
memcpy(alignbuffer, key, keylen);
|
|
|
|
err = shash->setkey(tfm, alignbuffer, keylen);
|
2009-07-14 21:35:36 +08:00
|
|
|
kzfree(buffer);
|
2008-08-31 13:47:27 +08:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2019-01-07 10:47:42 +08:00
|
|
|
static void shash_set_needkey(struct crypto_shash *tfm, struct shash_alg *alg)
|
|
|
|
{
|
2019-11-30 03:35:22 +08:00
|
|
|
if (crypto_shash_alg_needs_key(alg))
|
2019-01-07 10:47:42 +08:00
|
|
|
crypto_shash_set_flags(tfm, CRYPTO_TFM_NEED_KEY);
|
|
|
|
}
|
|
|
|
|
2008-08-31 13:47:27 +08:00
|
|
|
int crypto_shash_setkey(struct crypto_shash *tfm, const u8 *key,
|
|
|
|
unsigned int keylen)
|
|
|
|
{
|
|
|
|
struct shash_alg *shash = crypto_shash_alg(tfm);
|
|
|
|
unsigned long alignmask = crypto_shash_alignmask(tfm);
|
crypto: hash - prevent using keyed hashes without setting key
Currently, almost none of the keyed hash algorithms check whether a key
has been set before proceeding. Some algorithms are okay with this and
will effectively just use a key of all 0's or some other bogus default.
However, others will severely break, as demonstrated using
"hmac(sha3-512-generic)", the unkeyed use of which causes a kernel crash
via a (potentially exploitable) stack buffer overflow.
A while ago, this problem was solved for AF_ALG by pairing each hash
transform with a 'has_key' bool. However, there are still other places
in the kernel where userspace can specify an arbitrary hash algorithm by
name, and the kernel uses it as unkeyed hash without checking whether it
is really unkeyed. Examples of this include:
- KEYCTL_DH_COMPUTE, via the KDF extension
- dm-verity
- dm-crypt, via the ESSIV support
- dm-integrity, via the "internal hash" mode with no key given
- drbd (Distributed Replicated Block Device)
This bug is especially bad for KEYCTL_DH_COMPUTE as that requires no
privileges to call.
Fix the bug for all users by adding a flag CRYPTO_TFM_NEED_KEY to the
->crt_flags of each hash transform that indicates whether the transform
still needs to be keyed or not. Then, make the hash init, import, and
digest functions return -ENOKEY if the key is still needed.
The new flag also replaces the 'has_key' bool which algif_hash was
previously using, thereby simplifying the algif_hash implementation.
Reported-by: syzbot <syzkaller@googlegroups.com>
Cc: stable@vger.kernel.org
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2018-01-04 03:16:27 +08:00
|
|
|
int err;
|
2008-08-31 13:47:27 +08:00
|
|
|
|
|
|
|
if ((unsigned long)key & alignmask)
|
crypto: hash - prevent using keyed hashes without setting key
Currently, almost none of the keyed hash algorithms check whether a key
has been set before proceeding. Some algorithms are okay with this and
will effectively just use a key of all 0's or some other bogus default.
However, others will severely break, as demonstrated using
"hmac(sha3-512-generic)", the unkeyed use of which causes a kernel crash
via a (potentially exploitable) stack buffer overflow.
A while ago, this problem was solved for AF_ALG by pairing each hash
transform with a 'has_key' bool. However, there are still other places
in the kernel where userspace can specify an arbitrary hash algorithm by
name, and the kernel uses it as unkeyed hash without checking whether it
is really unkeyed. Examples of this include:
- KEYCTL_DH_COMPUTE, via the KDF extension
- dm-verity
- dm-crypt, via the ESSIV support
- dm-integrity, via the "internal hash" mode with no key given
- drbd (Distributed Replicated Block Device)
This bug is especially bad for KEYCTL_DH_COMPUTE as that requires no
privileges to call.
Fix the bug for all users by adding a flag CRYPTO_TFM_NEED_KEY to the
->crt_flags of each hash transform that indicates whether the transform
still needs to be keyed or not. Then, make the hash init, import, and
digest functions return -ENOKEY if the key is still needed.
The new flag also replaces the 'has_key' bool which algif_hash was
previously using, thereby simplifying the algif_hash implementation.
Reported-by: syzbot <syzkaller@googlegroups.com>
Cc: stable@vger.kernel.org
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2018-01-04 03:16:27 +08:00
|
|
|
err = shash_setkey_unaligned(tfm, key, keylen);
|
|
|
|
else
|
|
|
|
err = shash->setkey(tfm, key, keylen);
|
|
|
|
|
2019-01-07 10:47:42 +08:00
|
|
|
if (unlikely(err)) {
|
|
|
|
shash_set_needkey(tfm, shash);
|
crypto: hash - prevent using keyed hashes without setting key
Currently, almost none of the keyed hash algorithms check whether a key
has been set before proceeding. Some algorithms are okay with this and
will effectively just use a key of all 0's or some other bogus default.
However, others will severely break, as demonstrated using
"hmac(sha3-512-generic)", the unkeyed use of which causes a kernel crash
via a (potentially exploitable) stack buffer overflow.
A while ago, this problem was solved for AF_ALG by pairing each hash
transform with a 'has_key' bool. However, there are still other places
in the kernel where userspace can specify an arbitrary hash algorithm by
name, and the kernel uses it as unkeyed hash without checking whether it
is really unkeyed. Examples of this include:
- KEYCTL_DH_COMPUTE, via the KDF extension
- dm-verity
- dm-crypt, via the ESSIV support
- dm-integrity, via the "internal hash" mode with no key given
- drbd (Distributed Replicated Block Device)
This bug is especially bad for KEYCTL_DH_COMPUTE as that requires no
privileges to call.
Fix the bug for all users by adding a flag CRYPTO_TFM_NEED_KEY to the
->crt_flags of each hash transform that indicates whether the transform
still needs to be keyed or not. Then, make the hash init, import, and
digest functions return -ENOKEY if the key is still needed.
The new flag also replaces the 'has_key' bool which algif_hash was
previously using, thereby simplifying the algif_hash implementation.
Reported-by: syzbot <syzkaller@googlegroups.com>
Cc: stable@vger.kernel.org
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2018-01-04 03:16:27 +08:00
|
|
|
return err;
|
2019-01-07 10:47:42 +08:00
|
|
|
}
|
2008-08-31 13:47:27 +08:00
|
|
|
|
crypto: hash - prevent using keyed hashes without setting key
Currently, almost none of the keyed hash algorithms check whether a key
has been set before proceeding. Some algorithms are okay with this and
will effectively just use a key of all 0's or some other bogus default.
However, others will severely break, as demonstrated using
"hmac(sha3-512-generic)", the unkeyed use of which causes a kernel crash
via a (potentially exploitable) stack buffer overflow.
A while ago, this problem was solved for AF_ALG by pairing each hash
transform with a 'has_key' bool. However, there are still other places
in the kernel where userspace can specify an arbitrary hash algorithm by
name, and the kernel uses it as unkeyed hash without checking whether it
is really unkeyed. Examples of this include:
- KEYCTL_DH_COMPUTE, via the KDF extension
- dm-verity
- dm-crypt, via the ESSIV support
- dm-integrity, via the "internal hash" mode with no key given
- drbd (Distributed Replicated Block Device)
This bug is especially bad for KEYCTL_DH_COMPUTE as that requires no
privileges to call.
Fix the bug for all users by adding a flag CRYPTO_TFM_NEED_KEY to the
->crt_flags of each hash transform that indicates whether the transform
still needs to be keyed or not. Then, make the hash init, import, and
digest functions return -ENOKEY if the key is still needed.
The new flag also replaces the 'has_key' bool which algif_hash was
previously using, thereby simplifying the algif_hash implementation.
Reported-by: syzbot <syzkaller@googlegroups.com>
Cc: stable@vger.kernel.org
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2018-01-04 03:16:27 +08:00
|
|
|
crypto_shash_clear_flags(tfm, CRYPTO_TFM_NEED_KEY);
|
|
|
|
return 0;
|
2008-08-31 13:47:27 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(crypto_shash_setkey);
|
|
|
|
|
|
|
|
static int shash_update_unaligned(struct shash_desc *desc, const u8 *data,
|
|
|
|
unsigned int len)
|
|
|
|
{
|
|
|
|
struct crypto_shash *tfm = desc->tfm;
|
|
|
|
struct shash_alg *shash = crypto_shash_alg(tfm);
|
|
|
|
unsigned long alignmask = crypto_shash_alignmask(tfm);
|
|
|
|
unsigned int unaligned_len = alignmask + 1 -
|
|
|
|
((unsigned long)data & alignmask);
|
2018-08-08 05:18:42 +08:00
|
|
|
/*
|
|
|
|
* We cannot count on __aligned() working for large values:
|
|
|
|
* https://patchwork.kernel.org/patch/9507697/
|
|
|
|
*/
|
|
|
|
u8 ubuf[MAX_ALGAPI_ALIGNMASK * 2];
|
2009-07-14 21:43:56 +08:00
|
|
|
u8 *buf = PTR_ALIGN(&ubuf[0], alignmask + 1);
|
2009-07-14 21:35:36 +08:00
|
|
|
int err;
|
2008-08-31 13:47:27 +08:00
|
|
|
|
2018-08-08 05:18:42 +08:00
|
|
|
if (WARN_ON(buf + unaligned_len > ubuf + sizeof(ubuf)))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2009-03-27 13:03:51 +08:00
|
|
|
if (unaligned_len > len)
|
|
|
|
unaligned_len = len;
|
|
|
|
|
2008-08-31 13:47:27 +08:00
|
|
|
memcpy(buf, data, unaligned_len);
|
2009-07-14 21:35:36 +08:00
|
|
|
err = shash->update(desc, buf, unaligned_len);
|
|
|
|
memset(buf, 0, unaligned_len);
|
2008-08-31 13:47:27 +08:00
|
|
|
|
2009-07-14 21:35:36 +08:00
|
|
|
return err ?:
|
2008-08-31 13:47:27 +08:00
|
|
|
shash->update(desc, data + unaligned_len, len - unaligned_len);
|
|
|
|
}
|
|
|
|
|
|
|
|
int crypto_shash_update(struct shash_desc *desc, const u8 *data,
|
|
|
|
unsigned int len)
|
|
|
|
{
|
|
|
|
struct crypto_shash *tfm = desc->tfm;
|
|
|
|
struct shash_alg *shash = crypto_shash_alg(tfm);
|
|
|
|
unsigned long alignmask = crypto_shash_alignmask(tfm);
|
|
|
|
|
|
|
|
if ((unsigned long)data & alignmask)
|
|
|
|
return shash_update_unaligned(desc, data, len);
|
|
|
|
|
|
|
|
return shash->update(desc, data, len);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(crypto_shash_update);
|
|
|
|
|
|
|
|
static int shash_final_unaligned(struct shash_desc *desc, u8 *out)
|
|
|
|
{
|
|
|
|
struct crypto_shash *tfm = desc->tfm;
|
|
|
|
unsigned long alignmask = crypto_shash_alignmask(tfm);
|
|
|
|
struct shash_alg *shash = crypto_shash_alg(tfm);
|
|
|
|
unsigned int ds = crypto_shash_digestsize(tfm);
|
2018-08-08 05:18:42 +08:00
|
|
|
/*
|
|
|
|
* We cannot count on __aligned() working for large values:
|
|
|
|
* https://patchwork.kernel.org/patch/9507697/
|
|
|
|
*/
|
|
|
|
u8 ubuf[MAX_ALGAPI_ALIGNMASK + HASH_MAX_DIGESTSIZE];
|
2009-07-14 21:43:56 +08:00
|
|
|
u8 *buf = PTR_ALIGN(&ubuf[0], alignmask + 1);
|
2008-08-31 13:47:27 +08:00
|
|
|
int err;
|
|
|
|
|
2018-08-08 05:18:42 +08:00
|
|
|
if (WARN_ON(buf + ds > ubuf + sizeof(ubuf)))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2008-08-31 13:47:27 +08:00
|
|
|
err = shash->final(desc, buf);
|
2009-07-14 21:35:36 +08:00
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
2008-08-31 13:47:27 +08:00
|
|
|
memcpy(out, buf, ds);
|
2009-07-14 21:35:36 +08:00
|
|
|
|
|
|
|
out:
|
|
|
|
memset(buf, 0, ds);
|
2008-08-31 13:47:27 +08:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
int crypto_shash_final(struct shash_desc *desc, u8 *out)
|
|
|
|
{
|
|
|
|
struct crypto_shash *tfm = desc->tfm;
|
|
|
|
struct shash_alg *shash = crypto_shash_alg(tfm);
|
|
|
|
unsigned long alignmask = crypto_shash_alignmask(tfm);
|
|
|
|
|
|
|
|
if ((unsigned long)out & alignmask)
|
|
|
|
return shash_final_unaligned(desc, out);
|
|
|
|
|
|
|
|
return shash->final(desc, out);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(crypto_shash_final);
|
|
|
|
|
|
|
|
static int shash_finup_unaligned(struct shash_desc *desc, const u8 *data,
|
|
|
|
unsigned int len, u8 *out)
|
|
|
|
{
|
|
|
|
return crypto_shash_update(desc, data, len) ?:
|
|
|
|
crypto_shash_final(desc, out);
|
|
|
|
}
|
|
|
|
|
|
|
|
int crypto_shash_finup(struct shash_desc *desc, const u8 *data,
|
|
|
|
unsigned int len, u8 *out)
|
|
|
|
{
|
|
|
|
struct crypto_shash *tfm = desc->tfm;
|
|
|
|
struct shash_alg *shash = crypto_shash_alg(tfm);
|
|
|
|
unsigned long alignmask = crypto_shash_alignmask(tfm);
|
|
|
|
|
2009-07-09 20:36:44 +08:00
|
|
|
if (((unsigned long)data | (unsigned long)out) & alignmask)
|
2008-08-31 13:47:27 +08:00
|
|
|
return shash_finup_unaligned(desc, data, len, out);
|
|
|
|
|
|
|
|
return shash->finup(desc, data, len, out);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(crypto_shash_finup);
|
|
|
|
|
|
|
|
static int shash_digest_unaligned(struct shash_desc *desc, const u8 *data,
|
|
|
|
unsigned int len, u8 *out)
|
|
|
|
{
|
|
|
|
return crypto_shash_init(desc) ?:
|
2009-07-08 23:32:08 +08:00
|
|
|
crypto_shash_finup(desc, data, len, out);
|
2008-08-31 13:47:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
int crypto_shash_digest(struct shash_desc *desc, const u8 *data,
|
|
|
|
unsigned int len, u8 *out)
|
|
|
|
{
|
|
|
|
struct crypto_shash *tfm = desc->tfm;
|
|
|
|
struct shash_alg *shash = crypto_shash_alg(tfm);
|
|
|
|
unsigned long alignmask = crypto_shash_alignmask(tfm);
|
|
|
|
|
crypto: hash - prevent using keyed hashes without setting key
Currently, almost none of the keyed hash algorithms check whether a key
has been set before proceeding. Some algorithms are okay with this and
will effectively just use a key of all 0's or some other bogus default.
However, others will severely break, as demonstrated using
"hmac(sha3-512-generic)", the unkeyed use of which causes a kernel crash
via a (potentially exploitable) stack buffer overflow.
A while ago, this problem was solved for AF_ALG by pairing each hash
transform with a 'has_key' bool. However, there are still other places
in the kernel where userspace can specify an arbitrary hash algorithm by
name, and the kernel uses it as unkeyed hash without checking whether it
is really unkeyed. Examples of this include:
- KEYCTL_DH_COMPUTE, via the KDF extension
- dm-verity
- dm-crypt, via the ESSIV support
- dm-integrity, via the "internal hash" mode with no key given
- drbd (Distributed Replicated Block Device)
This bug is especially bad for KEYCTL_DH_COMPUTE as that requires no
privileges to call.
Fix the bug for all users by adding a flag CRYPTO_TFM_NEED_KEY to the
->crt_flags of each hash transform that indicates whether the transform
still needs to be keyed or not. Then, make the hash init, import, and
digest functions return -ENOKEY if the key is still needed.
The new flag also replaces the 'has_key' bool which algif_hash was
previously using, thereby simplifying the algif_hash implementation.
Reported-by: syzbot <syzkaller@googlegroups.com>
Cc: stable@vger.kernel.org
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2018-01-04 03:16:27 +08:00
|
|
|
if (crypto_shash_get_flags(tfm) & CRYPTO_TFM_NEED_KEY)
|
|
|
|
return -ENOKEY;
|
|
|
|
|
2009-07-09 20:36:44 +08:00
|
|
|
if (((unsigned long)data | (unsigned long)out) & alignmask)
|
2008-08-31 13:47:27 +08:00
|
|
|
return shash_digest_unaligned(desc, data, len, out);
|
|
|
|
|
|
|
|
return shash->digest(desc, data, len, out);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(crypto_shash_digest);
|
|
|
|
|
2009-07-22 12:37:06 +08:00
|
|
|
static int shash_default_export(struct shash_desc *desc, void *out)
|
2008-11-02 21:38:11 +08:00
|
|
|
{
|
2009-07-22 12:37:06 +08:00
|
|
|
memcpy(out, shash_desc_ctx(desc), crypto_shash_descsize(desc->tfm));
|
|
|
|
return 0;
|
2009-07-09 20:30:57 +08:00
|
|
|
}
|
2008-11-02 21:38:11 +08:00
|
|
|
|
2009-07-22 12:37:06 +08:00
|
|
|
static int shash_default_import(struct shash_desc *desc, const void *in)
|
2009-07-09 20:30:57 +08:00
|
|
|
{
|
2009-07-22 12:37:06 +08:00
|
|
|
memcpy(shash_desc_ctx(desc), in, crypto_shash_descsize(desc->tfm));
|
|
|
|
return 0;
|
2008-11-02 21:38:11 +08:00
|
|
|
}
|
|
|
|
|
2008-08-31 16:52:18 +08:00
|
|
|
static int shash_async_setkey(struct crypto_ahash *tfm, const u8 *key,
|
|
|
|
unsigned int keylen)
|
|
|
|
{
|
|
|
|
struct crypto_shash **ctx = crypto_ahash_ctx(tfm);
|
|
|
|
|
|
|
|
return crypto_shash_setkey(*ctx, key, keylen);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int shash_async_init(struct ahash_request *req)
|
|
|
|
{
|
|
|
|
struct crypto_shash **ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req));
|
|
|
|
struct shash_desc *desc = ahash_request_ctx(req);
|
|
|
|
|
|
|
|
desc->tfm = *ctx;
|
|
|
|
|
|
|
|
return crypto_shash_init(desc);
|
|
|
|
}
|
|
|
|
|
2009-07-12 21:25:20 +08:00
|
|
|
int shash_ahash_update(struct ahash_request *req, struct shash_desc *desc)
|
2008-08-31 16:52:18 +08:00
|
|
|
{
|
|
|
|
struct crypto_hash_walk walk;
|
|
|
|
int nbytes;
|
|
|
|
|
|
|
|
for (nbytes = crypto_hash_walk_first(req, &walk); nbytes > 0;
|
|
|
|
nbytes = crypto_hash_walk_done(&walk, nbytes))
|
|
|
|
nbytes = crypto_shash_update(desc, walk.data, nbytes);
|
|
|
|
|
|
|
|
return nbytes;
|
|
|
|
}
|
2009-07-12 21:25:20 +08:00
|
|
|
EXPORT_SYMBOL_GPL(shash_ahash_update);
|
|
|
|
|
|
|
|
static int shash_async_update(struct ahash_request *req)
|
|
|
|
{
|
|
|
|
return shash_ahash_update(req, ahash_request_ctx(req));
|
|
|
|
}
|
2008-08-31 16:52:18 +08:00
|
|
|
|
|
|
|
static int shash_async_final(struct ahash_request *req)
|
|
|
|
{
|
|
|
|
return crypto_shash_final(ahash_request_ctx(req), req->result);
|
|
|
|
}
|
|
|
|
|
2009-07-15 12:40:40 +08:00
|
|
|
int shash_ahash_finup(struct ahash_request *req, struct shash_desc *desc)
|
|
|
|
{
|
|
|
|
struct crypto_hash_walk walk;
|
|
|
|
int nbytes;
|
|
|
|
|
2009-07-15 21:26:41 +08:00
|
|
|
nbytes = crypto_hash_walk_first(req, &walk);
|
|
|
|
if (!nbytes)
|
|
|
|
return crypto_shash_final(desc, req->result);
|
|
|
|
|
|
|
|
do {
|
2009-07-15 12:40:40 +08:00
|
|
|
nbytes = crypto_hash_walk_last(&walk) ?
|
|
|
|
crypto_shash_finup(desc, walk.data, nbytes,
|
|
|
|
req->result) :
|
|
|
|
crypto_shash_update(desc, walk.data, nbytes);
|
2009-07-15 21:26:41 +08:00
|
|
|
nbytes = crypto_hash_walk_done(&walk, nbytes);
|
|
|
|
} while (nbytes > 0);
|
2009-07-15 12:40:40 +08:00
|
|
|
|
|
|
|
return nbytes;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(shash_ahash_finup);
|
|
|
|
|
|
|
|
static int shash_async_finup(struct ahash_request *req)
|
|
|
|
{
|
|
|
|
struct crypto_shash **ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req));
|
|
|
|
struct shash_desc *desc = ahash_request_ctx(req);
|
|
|
|
|
|
|
|
desc->tfm = *ctx;
|
|
|
|
|
|
|
|
return shash_ahash_finup(req, desc);
|
|
|
|
}
|
|
|
|
|
2009-07-12 21:25:20 +08:00
|
|
|
int shash_ahash_digest(struct ahash_request *req, struct shash_desc *desc)
|
2008-08-31 16:52:18 +08:00
|
|
|
{
|
|
|
|
unsigned int nbytes = req->nbytes;
|
2017-10-09 23:30:02 +08:00
|
|
|
struct scatterlist *sg;
|
|
|
|
unsigned int offset;
|
2008-08-31 16:52:18 +08:00
|
|
|
int err;
|
|
|
|
|
2017-10-09 23:30:02 +08:00
|
|
|
if (nbytes &&
|
|
|
|
(sg = req->src, offset = sg->offset,
|
2019-04-15 07:23:33 +08:00
|
|
|
nbytes <= min(sg->length, ((unsigned int)(PAGE_SIZE)) - offset))) {
|
2008-08-31 16:52:18 +08:00
|
|
|
void *data;
|
|
|
|
|
2011-11-25 23:14:17 +08:00
|
|
|
data = kmap_atomic(sg_page(sg));
|
2008-08-31 16:52:18 +08:00
|
|
|
err = crypto_shash_digest(desc, data + offset, nbytes,
|
|
|
|
req->result);
|
2011-11-25 23:14:17 +08:00
|
|
|
kunmap_atomic(data);
|
2009-07-12 21:25:20 +08:00
|
|
|
} else
|
|
|
|
err = crypto_shash_init(desc) ?:
|
2009-07-15 12:40:40 +08:00
|
|
|
shash_ahash_finup(req, desc);
|
2008-08-31 16:52:18 +08:00
|
|
|
|
2009-07-12 21:25:20 +08:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(shash_ahash_digest);
|
2008-08-31 16:52:18 +08:00
|
|
|
|
2009-07-12 21:25:20 +08:00
|
|
|
static int shash_async_digest(struct ahash_request *req)
|
|
|
|
{
|
|
|
|
struct crypto_shash **ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req));
|
|
|
|
struct shash_desc *desc = ahash_request_ctx(req);
|
2008-08-31 16:52:18 +08:00
|
|
|
|
2009-07-12 21:25:20 +08:00
|
|
|
desc->tfm = *ctx;
|
2008-08-31 16:52:18 +08:00
|
|
|
|
2009-07-12 21:25:20 +08:00
|
|
|
return shash_ahash_digest(req, desc);
|
2008-08-31 16:52:18 +08:00
|
|
|
}
|
|
|
|
|
2009-07-15 12:40:40 +08:00
|
|
|
static int shash_async_export(struct ahash_request *req, void *out)
|
|
|
|
{
|
|
|
|
return crypto_shash_export(ahash_request_ctx(req), out);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int shash_async_import(struct ahash_request *req, const void *in)
|
|
|
|
{
|
2010-11-05 02:00:22 +08:00
|
|
|
struct crypto_shash **ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req));
|
|
|
|
struct shash_desc *desc = ahash_request_ctx(req);
|
|
|
|
|
|
|
|
desc->tfm = *ctx;
|
|
|
|
|
|
|
|
return crypto_shash_import(desc, in);
|
2009-07-15 12:40:40 +08:00
|
|
|
}
|
|
|
|
|
2008-08-31 16:52:18 +08:00
|
|
|
static void crypto_exit_shash_ops_async(struct crypto_tfm *tfm)
|
|
|
|
{
|
|
|
|
struct crypto_shash **ctx = crypto_tfm_ctx(tfm);
|
|
|
|
|
|
|
|
crypto_free_shash(*ctx);
|
|
|
|
}
|
|
|
|
|
2009-07-14 12:28:26 +08:00
|
|
|
int crypto_init_shash_ops_async(struct crypto_tfm *tfm)
|
2008-08-31 16:52:18 +08:00
|
|
|
{
|
|
|
|
struct crypto_alg *calg = tfm->__crt_alg;
|
2009-07-15 12:40:40 +08:00
|
|
|
struct shash_alg *alg = __crypto_shash_alg(calg);
|
2009-07-14 12:28:26 +08:00
|
|
|
struct crypto_ahash *crt = __crypto_ahash_cast(tfm);
|
2008-08-31 16:52:18 +08:00
|
|
|
struct crypto_shash **ctx = crypto_tfm_ctx(tfm);
|
|
|
|
struct crypto_shash *shash;
|
|
|
|
|
|
|
|
if (!crypto_mod_get(calg))
|
|
|
|
return -EAGAIN;
|
|
|
|
|
2009-02-18 16:56:59 +08:00
|
|
|
shash = crypto_create_tfm(calg, &crypto_shash_type);
|
2008-08-31 16:52:18 +08:00
|
|
|
if (IS_ERR(shash)) {
|
|
|
|
crypto_mod_put(calg);
|
|
|
|
return PTR_ERR(shash);
|
|
|
|
}
|
|
|
|
|
|
|
|
*ctx = shash;
|
|
|
|
tfm->exit = crypto_exit_shash_ops_async;
|
|
|
|
|
|
|
|
crt->init = shash_async_init;
|
|
|
|
crt->update = shash_async_update;
|
2009-07-15 12:40:40 +08:00
|
|
|
crt->final = shash_async_final;
|
|
|
|
crt->finup = shash_async_finup;
|
2008-08-31 16:52:18 +08:00
|
|
|
crt->digest = shash_async_digest;
|
2019-01-07 10:47:42 +08:00
|
|
|
if (crypto_shash_alg_has_setkey(alg))
|
|
|
|
crt->setkey = shash_async_setkey;
|
2016-01-27 00:16:37 +08:00
|
|
|
|
crypto: hash - prevent using keyed hashes without setting key
Currently, almost none of the keyed hash algorithms check whether a key
has been set before proceeding. Some algorithms are okay with this and
will effectively just use a key of all 0's or some other bogus default.
However, others will severely break, as demonstrated using
"hmac(sha3-512-generic)", the unkeyed use of which causes a kernel crash
via a (potentially exploitable) stack buffer overflow.
A while ago, this problem was solved for AF_ALG by pairing each hash
transform with a 'has_key' bool. However, there are still other places
in the kernel where userspace can specify an arbitrary hash algorithm by
name, and the kernel uses it as unkeyed hash without checking whether it
is really unkeyed. Examples of this include:
- KEYCTL_DH_COMPUTE, via the KDF extension
- dm-verity
- dm-crypt, via the ESSIV support
- dm-integrity, via the "internal hash" mode with no key given
- drbd (Distributed Replicated Block Device)
This bug is especially bad for KEYCTL_DH_COMPUTE as that requires no
privileges to call.
Fix the bug for all users by adding a flag CRYPTO_TFM_NEED_KEY to the
->crt_flags of each hash transform that indicates whether the transform
still needs to be keyed or not. Then, make the hash init, import, and
digest functions return -ENOKEY if the key is still needed.
The new flag also replaces the 'has_key' bool which algif_hash was
previously using, thereby simplifying the algif_hash implementation.
Reported-by: syzbot <syzkaller@googlegroups.com>
Cc: stable@vger.kernel.org
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2018-01-04 03:16:27 +08:00
|
|
|
crypto_ahash_set_flags(crt, crypto_shash_get_flags(shash) &
|
|
|
|
CRYPTO_TFM_NEED_KEY);
|
2009-07-15 12:40:40 +08:00
|
|
|
|
2019-01-07 11:08:01 +08:00
|
|
|
crt->export = shash_async_export;
|
|
|
|
crt->import = shash_async_import;
|
2008-08-31 16:52:18 +08:00
|
|
|
|
|
|
|
crt->reqsize = sizeof(struct shash_desc) + crypto_shash_descsize(shash);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-12-08 13:42:51 +08:00
|
|
|
static void crypto_shash_exit_tfm(struct crypto_tfm *tfm)
|
|
|
|
{
|
|
|
|
struct crypto_shash *hash = __crypto_shash_cast(tfm);
|
|
|
|
struct shash_alg *alg = crypto_shash_alg(hash);
|
|
|
|
|
|
|
|
alg->exit_tfm(hash);
|
|
|
|
}
|
|
|
|
|
2009-07-13 20:46:25 +08:00
|
|
|
static int crypto_shash_init_tfm(struct crypto_tfm *tfm)
|
2008-08-31 13:47:27 +08:00
|
|
|
{
|
2009-07-14 12:50:12 +08:00
|
|
|
struct crypto_shash *hash = __crypto_shash_cast(tfm);
|
crypto: hash - prevent using keyed hashes without setting key
Currently, almost none of the keyed hash algorithms check whether a key
has been set before proceeding. Some algorithms are okay with this and
will effectively just use a key of all 0's or some other bogus default.
However, others will severely break, as demonstrated using
"hmac(sha3-512-generic)", the unkeyed use of which causes a kernel crash
via a (potentially exploitable) stack buffer overflow.
A while ago, this problem was solved for AF_ALG by pairing each hash
transform with a 'has_key' bool. However, there are still other places
in the kernel where userspace can specify an arbitrary hash algorithm by
name, and the kernel uses it as unkeyed hash without checking whether it
is really unkeyed. Examples of this include:
- KEYCTL_DH_COMPUTE, via the KDF extension
- dm-verity
- dm-crypt, via the ESSIV support
- dm-integrity, via the "internal hash" mode with no key given
- drbd (Distributed Replicated Block Device)
This bug is especially bad for KEYCTL_DH_COMPUTE as that requires no
privileges to call.
Fix the bug for all users by adding a flag CRYPTO_TFM_NEED_KEY to the
->crt_flags of each hash transform that indicates whether the transform
still needs to be keyed or not. Then, make the hash init, import, and
digest functions return -ENOKEY if the key is still needed.
The new flag also replaces the 'has_key' bool which algif_hash was
previously using, thereby simplifying the algif_hash implementation.
Reported-by: syzbot <syzkaller@googlegroups.com>
Cc: stable@vger.kernel.org
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2018-01-04 03:16:27 +08:00
|
|
|
struct shash_alg *alg = crypto_shash_alg(hash);
|
2019-12-08 13:42:51 +08:00
|
|
|
int err;
|
crypto: hash - prevent using keyed hashes without setting key
Currently, almost none of the keyed hash algorithms check whether a key
has been set before proceeding. Some algorithms are okay with this and
will effectively just use a key of all 0's or some other bogus default.
However, others will severely break, as demonstrated using
"hmac(sha3-512-generic)", the unkeyed use of which causes a kernel crash
via a (potentially exploitable) stack buffer overflow.
A while ago, this problem was solved for AF_ALG by pairing each hash
transform with a 'has_key' bool. However, there are still other places
in the kernel where userspace can specify an arbitrary hash algorithm by
name, and the kernel uses it as unkeyed hash without checking whether it
is really unkeyed. Examples of this include:
- KEYCTL_DH_COMPUTE, via the KDF extension
- dm-verity
- dm-crypt, via the ESSIV support
- dm-integrity, via the "internal hash" mode with no key given
- drbd (Distributed Replicated Block Device)
This bug is especially bad for KEYCTL_DH_COMPUTE as that requires no
privileges to call.
Fix the bug for all users by adding a flag CRYPTO_TFM_NEED_KEY to the
->crt_flags of each hash transform that indicates whether the transform
still needs to be keyed or not. Then, make the hash init, import, and
digest functions return -ENOKEY if the key is still needed.
The new flag also replaces the 'has_key' bool which algif_hash was
previously using, thereby simplifying the algif_hash implementation.
Reported-by: syzbot <syzkaller@googlegroups.com>
Cc: stable@vger.kernel.org
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2018-01-04 03:16:27 +08:00
|
|
|
|
|
|
|
hash->descsize = alg->descsize;
|
|
|
|
|
2019-01-07 10:47:42 +08:00
|
|
|
shash_set_needkey(hash, alg);
|
2009-07-14 12:50:12 +08:00
|
|
|
|
2019-12-08 13:42:51 +08:00
|
|
|
if (alg->exit_tfm)
|
|
|
|
tfm->exit = crypto_shash_exit_tfm;
|
|
|
|
|
|
|
|
if (!alg->init_tfm)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err = alg->init_tfm(hash);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
/* ->init_tfm() may have increased the descsize. */
|
|
|
|
if (WARN_ON_ONCE(hash->descsize > HASH_MAX_DESCSIZE)) {
|
|
|
|
if (alg->exit_tfm)
|
|
|
|
alg->exit_tfm(hash);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2008-08-31 13:47:27 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-11-03 20:46:07 +08:00
|
|
|
#ifdef CONFIG_NET
|
2011-09-27 13:26:10 +08:00
|
|
|
static int crypto_shash_report(struct sk_buff *skb, struct crypto_alg *alg)
|
|
|
|
{
|
|
|
|
struct crypto_report_hash rhash;
|
|
|
|
struct shash_alg *salg = __crypto_shash_alg(alg);
|
|
|
|
|
2018-11-04 05:56:03 +08:00
|
|
|
memset(&rhash, 0, sizeof(rhash));
|
|
|
|
|
|
|
|
strscpy(rhash.type, "shash", sizeof(rhash.type));
|
2013-02-06 01:19:13 +08:00
|
|
|
|
2011-09-27 13:26:10 +08:00
|
|
|
rhash.blocksize = alg->cra_blocksize;
|
|
|
|
rhash.digestsize = salg->digestsize;
|
|
|
|
|
2018-11-04 05:56:03 +08:00
|
|
|
return nla_put(skb, CRYPTOCFGA_REPORT_HASH, sizeof(rhash), &rhash);
|
2011-09-27 13:26:10 +08:00
|
|
|
}
|
2011-11-03 20:46:07 +08:00
|
|
|
#else
|
|
|
|
static int crypto_shash_report(struct sk_buff *skb, struct crypto_alg *alg)
|
|
|
|
{
|
|
|
|
return -ENOSYS;
|
|
|
|
}
|
|
|
|
#endif
|
2011-09-27 13:26:10 +08:00
|
|
|
|
2008-08-31 13:47:27 +08:00
|
|
|
static void crypto_shash_show(struct seq_file *m, struct crypto_alg *alg)
|
2016-12-31 23:56:23 +08:00
|
|
|
__maybe_unused;
|
2008-08-31 13:47:27 +08:00
|
|
|
static void crypto_shash_show(struct seq_file *m, struct crypto_alg *alg)
|
|
|
|
{
|
|
|
|
struct shash_alg *salg = __crypto_shash_alg(alg);
|
|
|
|
|
|
|
|
seq_printf(m, "type : shash\n");
|
|
|
|
seq_printf(m, "blocksize : %u\n", alg->cra_blocksize);
|
|
|
|
seq_printf(m, "digestsize : %u\n", salg->digestsize);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct crypto_type crypto_shash_type = {
|
2015-04-20 13:39:01 +08:00
|
|
|
.extsize = crypto_alg_extsize,
|
2008-08-31 13:47:27 +08:00
|
|
|
.init_tfm = crypto_shash_init_tfm,
|
|
|
|
#ifdef CONFIG_PROC_FS
|
|
|
|
.show = crypto_shash_show,
|
|
|
|
#endif
|
2011-09-27 13:26:10 +08:00
|
|
|
.report = crypto_shash_report,
|
2008-08-31 13:47:27 +08:00
|
|
|
.maskclear = ~CRYPTO_ALG_TYPE_MASK,
|
|
|
|
.maskset = CRYPTO_ALG_TYPE_MASK,
|
|
|
|
.type = CRYPTO_ALG_TYPE_SHASH,
|
|
|
|
.tfmsize = offsetof(struct crypto_shash, base),
|
|
|
|
};
|
|
|
|
|
2020-01-03 11:58:49 +08:00
|
|
|
int crypto_grab_shash(struct crypto_shash_spawn *spawn,
|
|
|
|
struct crypto_instance *inst,
|
|
|
|
const char *name, u32 type, u32 mask)
|
|
|
|
{
|
|
|
|
spawn->base.frontend = &crypto_shash_type;
|
|
|
|
return crypto_grab_spawn(&spawn->base, inst, name, type, mask);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(crypto_grab_shash);
|
|
|
|
|
2008-08-31 13:47:27 +08:00
|
|
|
struct crypto_shash *crypto_alloc_shash(const char *alg_name, u32 type,
|
|
|
|
u32 mask)
|
|
|
|
{
|
2009-02-18 16:56:59 +08:00
|
|
|
return crypto_alloc_tfm(alg_name, &crypto_shash_type, type, mask);
|
2008-08-31 13:47:27 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(crypto_alloc_shash);
|
|
|
|
|
2009-07-08 18:46:23 +08:00
|
|
|
static int shash_prepare_alg(struct shash_alg *alg)
|
2008-08-31 13:47:27 +08:00
|
|
|
{
|
|
|
|
struct crypto_alg *base = &alg->base;
|
|
|
|
|
2018-08-08 05:18:38 +08:00
|
|
|
if (alg->digestsize > HASH_MAX_DIGESTSIZE ||
|
|
|
|
alg->descsize > HASH_MAX_DESCSIZE ||
|
|
|
|
alg->statesize > HASH_MAX_STATESIZE)
|
2008-08-31 13:47:27 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
2019-01-07 11:07:20 +08:00
|
|
|
if ((alg->export && !alg->import) || (alg->import && !alg->export))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2008-08-31 13:47:27 +08:00
|
|
|
base->cra_type = &crypto_shash_type;
|
|
|
|
base->cra_flags &= ~CRYPTO_ALG_TYPE_MASK;
|
|
|
|
base->cra_flags |= CRYPTO_ALG_TYPE_SHASH;
|
2009-07-09 20:30:57 +08:00
|
|
|
|
2009-07-09 20:36:44 +08:00
|
|
|
if (!alg->finup)
|
|
|
|
alg->finup = shash_finup_unaligned;
|
|
|
|
if (!alg->digest)
|
|
|
|
alg->digest = shash_digest_unaligned;
|
2009-07-22 12:37:06 +08:00
|
|
|
if (!alg->export) {
|
|
|
|
alg->export = shash_default_export;
|
|
|
|
alg->import = shash_default_import;
|
|
|
|
alg->statesize = alg->descsize;
|
|
|
|
}
|
2009-07-11 22:17:39 +08:00
|
|
|
if (!alg->setkey)
|
|
|
|
alg->setkey = shash_no_setkey;
|
2009-07-09 20:30:57 +08:00
|
|
|
|
2009-07-08 18:46:23 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int crypto_register_shash(struct shash_alg *alg)
|
|
|
|
{
|
|
|
|
struct crypto_alg *base = &alg->base;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = shash_prepare_alg(alg);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2008-08-31 13:47:27 +08:00
|
|
|
|
|
|
|
return crypto_register_alg(base);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(crypto_register_shash);
|
|
|
|
|
2019-12-16 07:51:19 +08:00
|
|
|
void crypto_unregister_shash(struct shash_alg *alg)
|
2008-08-31 13:47:27 +08:00
|
|
|
{
|
2019-12-16 07:51:19 +08:00
|
|
|
crypto_unregister_alg(&alg->base);
|
2008-08-31 13:47:27 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(crypto_unregister_shash);
|
|
|
|
|
2012-07-11 19:20:20 +08:00
|
|
|
int crypto_register_shashes(struct shash_alg *algs, int count)
|
|
|
|
{
|
|
|
|
int i, ret;
|
|
|
|
|
|
|
|
for (i = 0; i < count; i++) {
|
|
|
|
ret = crypto_register_shash(&algs[i]);
|
|
|
|
if (ret)
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err:
|
|
|
|
for (--i; i >= 0; --i)
|
|
|
|
crypto_unregister_shash(&algs[i]);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(crypto_register_shashes);
|
|
|
|
|
2019-12-16 07:51:19 +08:00
|
|
|
void crypto_unregister_shashes(struct shash_alg *algs, int count)
|
2012-07-11 19:20:20 +08:00
|
|
|
{
|
2019-12-16 07:51:19 +08:00
|
|
|
int i;
|
2012-07-11 19:20:20 +08:00
|
|
|
|
2019-12-16 07:51:19 +08:00
|
|
|
for (i = count - 1; i >= 0; --i)
|
|
|
|
crypto_unregister_shash(&algs[i]);
|
2012-07-11 19:20:20 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(crypto_unregister_shashes);
|
|
|
|
|
2009-07-08 18:46:23 +08:00
|
|
|
int shash_register_instance(struct crypto_template *tmpl,
|
|
|
|
struct shash_instance *inst)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = shash_prepare_alg(&inst->alg);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
return crypto_register_instance(tmpl, shash_crypto_instance(inst));
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(shash_register_instance);
|
|
|
|
|
2009-07-07 15:17:12 +08:00
|
|
|
void shash_free_instance(struct crypto_instance *inst)
|
|
|
|
{
|
|
|
|
crypto_drop_spawn(crypto_instance_ctx(inst));
|
|
|
|
kfree(shash_instance(inst));
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(shash_free_instance);
|
|
|
|
|
2008-08-31 13:47:27 +08:00
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
MODULE_DESCRIPTION("Synchronous cryptographic hash type");
|