crypto: set CRYPTO_TFM_NEED_KEY if ->setkey() fails
cover crypto: set CRYPTO_TFM_NEED_KEY if ->setkey() fails
This series makes the crypto API mark shash, ahash, skcipher, and aead tfms as needing a key again if setting a key fails, since on failure many algorithms can leave the tfm in an intermediate state that is neither the old key nor the new key -- and use of such tfms for hashing, encryption, or decryption will produce bogus results or crashes.
Eric Biggers (3):
crypto: hash - set CRYPTO_TFM_NEED_KEY if ->setkey() fails
crypto: skcipher - set CRYPTO_TFM_NEED_KEY if ->setkey() fails
crypto: aead - set CRYPTO_TFM_NEED_KEY if ->setkey() fails
crypto/aead.c | 4 +++- crypto/ahash.c | 28 +++++++++++++++++++--------- crypto/shash.c | 18 +++++++++++++----- crypto/skcipher.c | 27 ++++++++++++++++++--------- 4 files changed, 53 insertions(+), 24 deletions(-)
Signature · 1 line
2.20.1
PATCH 1/3 crypto: hash - set CRYPTO_TFM_NEED_KEY if ->setkey() fails
From: Eric Biggers <[email protected]>
Some algorithms have a ->setkey() method that is not atomic, in the sense that setting a key can fail after changes were already made to the tfm context. In this case, if a key was already set the tfm can end up in a state that corresponds to neither the old key nor the new key.
It's not feasible to make all ->setkey() methods atomic, especially ones that have to key multiple sub-tfms. Therefore, make the crypto API set CRYPTO_TFM_NEED_KEY if ->setkey() fails and the algorithm requires a key, to prevent the tfm from being used until a new key is set.
Note: we can't set CRYPTO_TFM_NEED_KEY for OPTIONAL_KEY algorithms, so ->setkey() for those must nevertheless be atomic. That's fine for now since only the crc32 and crc32c algorithms set OPTIONAL_KEY, and it's not intended that OPTIONAL_KEY be used much.
[Cc stable mainly because when introducing the NEED_KEY flag I changed
AF_ALG to rely on it; and unlike in-kernel crypto API users, AF_ALG previously didn't have this problem. So these "incompletely keyed" states became theoretically accessible via AF_ALG -- though, the opportunities for causing real mischief seem pretty limited.]
--- crypto/ahash.c | 28 +++++++++++++++++++--------- crypto/shash.c | 18 +++++++++++++----- 2 files changed, 32 insertions(+), 14 deletions(-)
crypto/ahash.c +19 -9
@@ -190,6 +190,21 @@ static int ahash_setkey_unaligned(struct crypto_ahash *tfm, const u8 *key,return ret; } +static int ahash_nosetkey (struct crypto_ahash *tfm,const u8 *key, +unsigned int keylen) +{ +return -ENOSYS ; +} + +static void ahash_set_needkey (struct crypto_ahash *tfm) +{ +const struct hash_alg_common *alg =crypto_hash_alg_common (tfm); + +if (tfm->setkey != ahash_nosetkey && + !(alg->base.cra_flags &CRYPTO_ALG_OPTIONAL_KEY )) +crypto_ahash_set_flags (tfm,CRYPTO_TFM_NEED_KEY ); +} +int crypto_ahash_setkey (struct crypto_ahash *tfm,const u8 *key,unsigned int keylen) {
@@ -201,20 +216,16 @@ int crypto_ahash_setkey(struct crypto_ahash *tfm, const u8 *key,else err = tfm->setkey (tfm, key, keylen); -if (err) +if (unlikely (err)) { +ahash_set_needkey (tfm);return err; + }crypto_ahash_clear_flags (tfm,CRYPTO_TFM_NEED_KEY );return 0 ; }EXPORT_SYMBOL_GPL (crypto_ahash_setkey); -static int ahash_nosetkey (struct crypto_ahash *tfm,const u8 *key, -unsigned int keylen) -{ -return -ENOSYS ; -} -static inline unsigned int ahash_align_buffer_size (unsigned len,unsigned long mask) {
@@ -489,8 +500,7 @@ static int crypto_ahash_init_tfm(struct crypto_tfm *tfm)if (alg->setkey) { hash->setkey = alg->setkey; -if (!(alg->halg.base.cra_flags &CRYPTO_ALG_OPTIONAL_KEY )) -crypto_ahash_set_flags (hash,CRYPTO_TFM_NEED_KEY ); +ahash_set_needkey (hash); }return 0 ;
crypto/shash.c +13 -5
@@ -53,6 +53,13 @@ static int shash_setkey_unaligned(struct crypto_shash *tfm, const u8 *key,return err; } +static void shash_set_needkey (struct crypto_shash *tfm,struct shash_alg *alg) +{ +if (crypto_shash_alg_has_setkey (alg) && + !(alg->base.cra_flags &CRYPTO_ALG_OPTIONAL_KEY )) +crypto_shash_set_flags (tfm,CRYPTO_TFM_NEED_KEY ); +} +int crypto_shash_setkey (struct crypto_shash *tfm,const u8 *key,unsigned int keylen) {
@@ -65,8 +72,10 @@ int crypto_shash_setkey(struct crypto_shash *tfm, const u8 *key,else err = shash->setkey (tfm, key, keylen); -if (err) +if (unlikely (err)) { +shash_set_needkey (tfm, shash);return err; + }crypto_shash_clear_flags (tfm,CRYPTO_TFM_NEED_KEY );return 0 ;
@@ -373,7 +382,8 @@ int crypto_init_shash_ops_async(struct crypto_tfm *tfm) crt->final = shash_async_final; crt->finup = shash_async_finup; crt->digest = shash_async_digest; - crt->setkey = shash_async_setkey; +if (crypto_shash_alg_has_setkey (alg)) + crt->setkey = shash_async_setkey;crypto_ahash_set_flags (crt,crypto_shash_get_flags (shash) &CRYPTO_TFM_NEED_KEY );
@@ -395,9 +405,7 @@ static int crypto_shash_init_tfm(struct crypto_tfm *tfm) hash->descsize = alg->descsize; -if (crypto_shash_alg_has_setkey (alg) && - !(alg->base.cra_flags &CRYPTO_ALG_OPTIONAL_KEY )) -crypto_shash_set_flags (hash,CRYPTO_TFM_NEED_KEY ); +shash_set_needkey (hash, alg);return 0 ; }
Signature · 1 line
2.20.1
PATCH 2/3 crypto: skcipher - set CRYPTO_TFM_NEED_KEY if ->setkey() fails
In reply to Eric BiggersFrom: Eric Biggers <[email protected]>
Some algorithms have a ->setkey() method that is not atomic, in the sense that setting a key can fail after changes were already made to the tfm context. In this case, if a key was already set the tfm can end up in a state that corresponds to neither the old key nor the new key.
For example, in lrw.c, if gf128mul_init_64k_bbe() fails due to lack of memory, then priv::table will be left NULL. After that, encryption with that tfm will cause a NULL pointer dereference.
It's not feasible to make all ->setkey() methods atomic, especially ones that have to key multiple sub-tfms. Therefore, make the crypto API set CRYPTO_TFM_NEED_KEY if ->setkey() fails and the algorithm requires a key, to prevent the tfm from being used until a new key is set.
[Cc stable mainly because when introducing the NEED_KEY flag I changed
AF_ALG to rely on it; and unlike in-kernel crypto API users, AF_ALG previously didn't have this problem. So these "incompletely keyed" states became theoretically accessible via AF_ALG -- though, the opportunities for causing real mischief seem pretty limited.]
--- crypto/skcipher.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-)
crypto/skcipher.c +18 -9
@@ -585,6 +585,12 @@ static unsigned int crypto_skcipher_extsize(struct crypto_alg *alg)return crypto_alg_extsize (alg); } +static void skcipher_set_needkey (struct crypto_skcipher *tfm) +{ +if (tfm->keysize) +crypto_skcipher_set_flags (tfm,CRYPTO_TFM_NEED_KEY ); +} +static int skcipher_setkey_blkcipher (struct crypto_skcipher *tfm,const u8 *key,unsigned int keylen) {
@@ -598,8 +604,10 @@ static int skcipher_setkey_blkcipher(struct crypto_skcipher *tfm, err =crypto_blkcipher_setkey (blkcipher, key, keylen);crypto_skcipher_set_flags (tfm,crypto_blkcipher_get_flags (blkcipher) &CRYPTO_TFM_RES_MASK ); -if (err) +if (unlikely (err)) { +skcipher_set_needkey (tfm);return err; + }crypto_skcipher_clear_flags (tfm,CRYPTO_TFM_NEED_KEY );return 0 ;
@@ -677,8 +685,7 @@ static int crypto_init_skcipher_ops_blkcipher(struct crypto_tfm *tfm) skcipher->ivsize =crypto_blkcipher_ivsize (blkcipher); skcipher->keysize = calg->cra_blkcipher.max_keysize; -if (skcipher->keysize) -crypto_skcipher_set_flags (skcipher,CRYPTO_TFM_NEED_KEY ); +skcipher_set_needkey (skcipher);return 0 ; }
@@ -698,8 +705,10 @@ static int skcipher_setkey_ablkcipher(struct crypto_skcipher *tfm,crypto_skcipher_set_flags (tfm,crypto_ablkcipher_get_flags (ablkcipher) &CRYPTO_TFM_RES_MASK ); -if (err) +if (unlikely (err)) { +skcipher_set_needkey (tfm);return err; + }crypto_skcipher_clear_flags (tfm,CRYPTO_TFM_NEED_KEY );return 0 ;
@@ -776,8 +785,7 @@ static int crypto_init_skcipher_ops_ablkcipher(struct crypto_tfm *tfm)sizeof (struct ablkcipher_request ); skcipher->keysize = calg->cra_ablkcipher.max_keysize; -if (skcipher->keysize) -crypto_skcipher_set_flags (skcipher,CRYPTO_TFM_NEED_KEY ); +skcipher_set_needkey (skcipher);return 0 ; }
@@ -820,8 +828,10 @@ static int skcipher_setkey(struct crypto_skcipher *tfm, const u8 *key,else err = cipher->setkey (tfm, key, keylen); -if (err) +if (unlikely (err)) { +skcipher_set_needkey (tfm);return err; + }crypto_skcipher_clear_flags (tfm,CRYPTO_TFM_NEED_KEY );return 0 ;
@@ -852,8 +862,7 @@ static int crypto_skcipher_init_tfm(struct crypto_tfm *tfm) skcipher->ivsize = alg->ivsize; skcipher->keysize = alg->max_keysize; -if (skcipher->keysize) -crypto_skcipher_set_flags (skcipher,CRYPTO_TFM_NEED_KEY ); +skcipher_set_needkey (skcipher);if (alg->exit) skcipher->base.exit = crypto_skcipher_exit_tfm;
Signature · 1 line
2.20.1
PATCH 3/3 crypto: aead - set CRYPTO_TFM_NEED_KEY if ->setkey() fails
In reply to Eric BiggersFrom: Eric Biggers <[email protected]>
Some algorithms have a ->setkey() method that is not atomic, in the sense that setting a key can fail after changes were already made to the tfm context. In this case, if a key was already set the tfm can end up in a state that corresponds to neither the old key nor the new key.
For example, in gcm.c, if the kzalloc() fails due to lack of memory, then the CTR part of GCM will have the new key but GHASH will not.
It's not feasible to make all ->setkey() methods atomic, especially ones that have to key multiple sub-tfms. Therefore, make the crypto API set CRYPTO_TFM_NEED_KEY if ->setkey() fails, to prevent the tfm from being used until a new key is set.
[Cc stable mainly because when introducing the NEED_KEY flag I changed
AF_ALG to rely on it; and unlike in-kernel crypto API users, AF_ALG previously didn't have this problem. So these "incompletely keyed" states became theoretically accessible via AF_ALG -- though, the opportunities for causing real mischief seem pretty limited.]
--- crypto/aead.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
crypto/aead.c +3 -1
@@ -61,8 +61,10 @@ int crypto_aead_setkey(struct crypto_aead *tfm,else err =crypto_aead_alg (tfm)->setkey (tfm, key, keylen); -if (err) +if (unlikely (err)) { +crypto_aead_set_flags (tfm,CRYPTO_TFM_NEED_KEY );return err; + }crypto_aead_clear_flags (tfm,CRYPTO_TFM_NEED_KEY );return 0 ;
Signature · 1 line
2.20.1
Herbert Xu
maintainerQuoted 11 lines · Eric Biggers
This series makes the crypto API mark shash, ahash, skcipher, and aead tfms as needing a key again if setting a key fails, since on failure many algorithms can leave the tfm in an intermediate state that is neither the old key nor the new key -- and use of such tfms for hashing, encryption, or decryption will produce bogus results or crashes.
Eric Biggers (3):
crypto: hash - setCRYPTO_TFM_NEED_KEYif ->setkey() fails
crypto: skcipher - setCRYPTO_TFM_NEED_KEYif ->setkey() fails
crypto: aead - setCRYPTO_TFM_NEED_KEYif ->setkey() fails
Quoted 5 lines
crypto/aead.c | 4 +++- crypto/ahash.c | 28 +++++++++++++++++++--------- crypto/shash.c | 18 +++++++++++++----- crypto/skcipher.c | 27 ++++++++++++++++++--------- 4 files changed, 53 insertions(+), 24 deletions(-)
All applied. Thanks.
Signature · 3 lines
Email: Herbert Xu <[email protected]> Home Page: http://gondor.apana.org.au/~herbert/ PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt