]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
crypto: rsassa-pkcs1 - Reinstate support for legacy protocols
authorLukas Wunner <lukas@wunner.de>
Tue, 29 Oct 2024 10:24:57 +0000 (11:24 +0100)
committerHerbert Xu <herbert@gondor.apana.org.au>
Sun, 10 Nov 2024 03:50:54 +0000 (11:50 +0800)
Commit 1e562deacecc ("crypto: rsassa-pkcs1 - Migrate to sig_alg backend")
enforced that rsassa-pkcs1 sign/verify operations specify a hash
algorithm.  That is necessary because per RFC 8017 sec 8.2, a hash
algorithm identifier must be prepended to the hash before generating or
verifying the signature ("Full Hash Prefix").

However the commit went too far in that it changed user space behavior:
KEYCTL_PKEY_QUERY system calls now return -EINVAL unless they specify a
hash algorithm.  Intel Wireless Daemon (iwd) is one application issuing
such system calls (for EAP-TLS).

Closer analysis of the Embedded Linux Library (ell) used by iwd reveals
that the problem runs even deeper:  When iwd uses TLS 1.1 or earlier, it
not only queries for keys, but performs sign/verify operations without
specifying a hash algorithm.  These legacy TLS versions concatenate an
MD5 to a SHA-1 hash and omit the Full Hash Prefix:

https://git.kernel.org/pub/scm/libs/ell/ell.git/tree/ell/tls-suites.c#n97

TLS 1.1 was deprecated in 2021 by RFC 8996, but removal of support was
inadvertent in this case.  It probably should be coordinated with iwd
maintainers first.

So reinstate support for such legacy protocols by defaulting to hash
algorithm "none" which uses an empty Full Hash Prefix.

If it is later on decided to remove TLS 1.1 support but still allow
KEYCTL_PKEY_QUERY without a hash algorithm, that can be achieved by
reverting the present commit and replacing it with the following patch:

https://lore.kernel.org/r/ZxalYZwH5UiGX5uj@wunner.de/

It's worth noting that Python's cryptography library gained support for
such legacy use cases very recently, so they do seem to still be a thing.
The Python developers identified IKE version 1 as another protocol
omitting the Full Hash Prefix:

https://github.com/pyca/cryptography/issues/10226
https://github.com/pyca/cryptography/issues/5495

The author of those issues, Zoltan Kelemen, spent considerable effort
searching for test vectors but only found one in a 2019 blog post by
Kevin Jones.  Add it to testmgr.h to verify correctness of this feature.

Examination of wpa_supplicant as well as various IKE daemons (libreswan,
strongswan, isakmpd, raccoon) has determined that none of them seems to
use the kernel's Key Retention Service, so iwd is the only affected user
space application known so far.

Fixes: 1e562deacecc ("crypto: rsassa-pkcs1 - Migrate to sig_alg backend")
Reported-by: Klara Modin <klarasmodin@gmail.com>
Tested-by: Klara Modin <klarasmodin@gmail.com>
Closes: https://lore.kernel.org/r/2ed09a22-86c0-4cf0-8bda-ef804ccb3413@gmail.com/
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
crypto/asymmetric_keys/public_key.c
crypto/rsassa-pkcs1.c
crypto/testmgr.c
crypto/testmgr.h

index c98c1588802b4fbf99aa445ead9fc09ae22266f5..bbd07a9022e60994f647ba4ab8dc71b6f7144537 100644 (file)
@@ -93,7 +93,7 @@ software_key_determine_akcipher(const struct public_key *pkey,
                                             pkey->pkey_algo);
                        } else {
                                if (!hash_algo)
-                                       return -EINVAL;
+                                       hash_algo = "none";
                                n = snprintf(alg_name, CRYPTO_MAX_ALG_NAME,
                                             "pkcs1(%s,%s)",
                                             pkey->pkey_algo, hash_algo);
index 9c28f1c628264a7e4bcdef038977a6c3995568e9..4d077fc9607672061bdf14ebc23a12cee18364c9 100644 (file)
@@ -27,6 +27,8 @@
  * https://www.rfc-editor.org/rfc/rfc9580#table-24
  */
 
+static const u8 hash_prefix_none[] = { };
+
 static const u8 hash_prefix_md5[] = {
        0x30, 0x20, 0x30, 0x0c, 0x06, 0x08,       /* SEQUENCE (SEQUENCE (OID */
        0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, /*      <algorithm>, */
@@ -93,6 +95,7 @@ static const struct hash_prefix {
        size_t          size;
 } hash_prefixes[] = {
 #define _(X) { #X, hash_prefix_##X, sizeof(hash_prefix_##X) }
+       _(none),
        _(md5),
        _(sha1),
        _(rmd160),
@@ -119,8 +122,17 @@ static const struct hash_prefix *rsassa_pkcs1_find_hash_prefix(const char *name)
        return NULL;
 }
 
-static unsigned int rsassa_pkcs1_hash_len(const struct hash_prefix *p)
+static bool rsassa_pkcs1_invalid_hash_len(unsigned int len,
+                                         const struct hash_prefix *p)
 {
+       /*
+        * Legacy protocols such as TLS 1.1 or earlier and IKE version 1
+        * do not prepend a Full Hash Prefix to the hash.  In that case,
+        * the size of the Full Hash Prefix is zero.
+        */
+       if (p->data == hash_prefix_none)
+               return false;
+
        /*
         * The final byte of the Full Hash Prefix encodes the hash length.
         *
@@ -130,7 +142,7 @@ static unsigned int rsassa_pkcs1_hash_len(const struct hash_prefix *p)
         */
        static_assert(HASH_MAX_DIGESTSIZE <= 127);
 
-       return p->data[p->size - 1];
+       return len != p->data[p->size - 1];
 }
 
 struct rsassa_pkcs1_ctx {
@@ -167,7 +179,7 @@ static int rsassa_pkcs1_sign(struct crypto_sig *tfm,
        if (dlen < ctx->key_size)
                return -EOVERFLOW;
 
-       if (slen != rsassa_pkcs1_hash_len(hash_prefix))
+       if (rsassa_pkcs1_invalid_hash_len(slen, hash_prefix))
                return -EINVAL;
 
        if (slen + hash_prefix->size > ctx->key_size - 11)
@@ -237,7 +249,7 @@ static int rsassa_pkcs1_verify(struct crypto_sig *tfm,
        /* RFC 8017 sec 8.2.2 step 1 - length checking */
        if (!ctx->key_size ||
            slen != ctx->key_size ||
-           dlen != rsassa_pkcs1_hash_len(hash_prefix))
+           rsassa_pkcs1_invalid_hash_len(dlen, hash_prefix))
                return -EINVAL;
 
        /* RFC 8017 sec 8.2.2 step 2 - RSA verification */
index 7d768f0ed81f54732f6960d8b19704040cdd1dca..86126be814cf2f1350ae29ca48112df72cd25eda 100644 (file)
@@ -5539,6 +5539,12 @@ static const struct alg_test_desc alg_test_descs[] = {
                .suite = {
                        .cipher = __VECS(fcrypt_pcbc_tv_template)
                }
+       }, {
+               .alg = "pkcs1(rsa,none)",
+               .test = alg_test_sig,
+               .suite = {
+                       .sig = __VECS(pkcs1_rsa_none_tv_template)
+               }
        }, {
                .alg = "pkcs1(rsa,sha224)",
                .test = alg_test_null,
index 55aae1859d2c5d90195e4d27f37266a553245f90..430d33d9ac1359c4b81d0b6e11fa2cfb6458040d 100644 (file)
@@ -1982,6 +1982,61 @@ static const struct sig_testvec ecrdsa_tv_template[] = {
        },
 };
 
+/*
+ * PKCS#1 RSA test vectors for hash algorithm "none"
+ * (i.e. the hash in "m" is not prepended by a Full Hash Prefix)
+ *
+ * Obtained from:
+ * https://vcsjones.dev/sometimes-valid-rsa-dotnet/
+ * https://gist.github.com/vcsjones/ab4c2327b53ed018eada76b75ef4fd99
+ */
+static const struct sig_testvec pkcs1_rsa_none_tv_template[] = {
+       {
+       .key =
+       "\x30\x82\x01\x0a\x02\x82\x01\x01\x00\xa2\x63\x0b\x39\x44\xb8\xbb"
+       "\x23\xa7\x44\x49\xbb\x0e\xff\xa1\xf0\x61\x0a\x53\x93\xb0\x98\xdb"
+       "\xad\x2c\x0f\x4a\xc5\x6e\xff\x86\x3c\x53\x55\x0f\x15\xce\x04\x3f"
+       "\x2b\xfd\xa9\x96\x96\xd9\xbe\x61\x79\x0b\x5b\xc9\x4c\x86\x76\xe5"
+       "\xe0\x43\x4b\x22\x95\xee\xc2\x2b\x43\xc1\x9f\xd8\x68\xb4\x8e\x40"
+       "\x4f\xee\x85\x38\xb9\x11\xc5\x23\xf2\x64\x58\xf0\x15\x32\x6f\x4e"
+       "\x57\xa1\xae\x88\xa4\x02\xd7\x2a\x1e\xcd\x4b\xe1\xdd\x63\xd5\x17"
+       "\x89\x32\x5b\xb0\x5e\x99\x5a\xa8\x9d\x28\x50\x0e\x17\xee\x96\xdb"
+       "\x61\x3b\x45\x51\x1d\xcf\x12\x56\x0b\x92\x47\xfc\xab\xae\xf6\x66"
+       "\x3d\x47\xac\x70\x72\xe7\x92\xe7\x5f\xcd\x10\xb9\xc4\x83\x64\x94"
+       "\x19\xbd\x25\x80\xe1\xe8\xd2\x22\xa5\xd0\xba\x02\x7a\xa1\x77\x93"
+       "\x5b\x65\xc3\xee\x17\x74\xbc\x41\x86\x2a\xdc\x08\x4c\x8c\x92\x8c"
+       "\x91\x2d\x9e\x77\x44\x1f\x68\xd6\xa8\x74\x77\xdb\x0e\x5b\x32\x8b"
+       "\x56\x8b\x33\xbd\xd9\x63\xc8\x49\x9d\x3a\xc5\xc5\xea\x33\x0b\xd2"
+       "\xf1\xa3\x1b\xf4\x8b\xbe\xd9\xb3\x57\x8b\x3b\xde\x04\xa7\x7a\x22"
+       "\xb2\x24\xae\x2e\xc7\x70\xc5\xbe\x4e\x83\x26\x08\xfb\x0b\xbd\xa9"
+       "\x4f\x99\x08\xe1\x10\x28\x72\xaa\xcd\x02\x03\x01\x00\x01",
+       .key_len = 270,
+       .m =
+       "\x68\xb4\xf9\x26\x34\x31\x25\xdd\x26\x50\x13\x68\xc1\x99\x26\x71"
+       "\x19\xa2\xde\x81",
+       .m_size = 20,
+       .c =
+       "\x6a\xdb\x39\xe5\x63\xb3\x25\xde\x58\xca\xc3\xf1\x36\x9c\x0b\x36"
+       "\xb7\xd6\x69\xf9\xba\xa6\x68\x14\x8c\x24\x52\xd3\x25\xa5\xf3\xad"
+       "\xc9\x47\x44\xde\x06\xd8\x0f\x56\xca\x2d\xfb\x0f\xe9\x99\xe2\x9d"
+       "\x8a\xe8\x7f\xfb\x9a\x99\x96\xf1\x2c\x4a\xe4\xc0\xae\x4d\x29\x47"
+       "\x38\x96\x51\x2f\x6d\x8e\xb8\x88\xbd\x1a\x0a\x70\xbc\x23\x38\x67"
+       "\x62\x22\x01\x23\x71\xe5\xbb\x95\xea\x6b\x8d\x31\x62\xbf\xf0\xc4"
+       "\xb9\x46\xd6\x67\xfc\x4c\xe6\x1f\xd6\x5d\xf7\xa9\xad\x3a\xf1\xbf"
+       "\xa2\xf9\x66\xde\xb6\x8e\xec\x8f\x81\x8d\x1e\x3a\x12\x27\x6a\xfc"
+       "\xae\x92\x9f\xc3\x87\xc3\xba\x8d\x04\xb8\x8f\x0f\x61\x68\x9a\x96"
+       "\x2c\x80\x2c\x32\x40\xde\x9d\xb9\x9b\xe2\xe4\x45\x2e\x91\x47\x5c"
+       "\x47\xa4\x9d\x02\x57\x59\xf7\x75\x5d\x5f\x32\x82\x75\x5d\xe5\x78"
+       "\xc9\x19\x61\x46\x06\x9d\xa5\x1d\xd6\x32\x48\x9a\xdb\x09\x29\x81"
+       "\x14\x2e\xf0\x27\xe9\x37\x13\x74\xec\xa5\xcd\x67\x6b\x19\xf6\x88"
+       "\xf0\xc2\x8b\xa8\x7f\x2f\x76\x5a\x3e\x0c\x47\x5d\xe8\x82\x50\x27"
+       "\x40\xce\x27\x41\x45\xa0\xcf\xaa\x2f\xd3\xad\x3c\xbf\x73\xff\x93"
+       "\xe3\x78\x49\xd9\xa9\x78\x22\x81\x9a\xe5\xe2\x94\xe9\x40\xab\xf1",
+       .c_size = 256,
+       .public_key_vec = true,
+       },
+};
+
 /*
  * PKCS#1 RSA test vectors. Obtained from CAVS testing.
  */