Skip to content

Commit f3eccec

Browse files
committed
pkcs7: Allow the signing algo to do whatever digestion it wants itself
Allow the data to be verified in a PKCS#7 or CMS message to be passed directly to an asymmetric cipher algorithm (e.g. ML-DSA) if it wants to do whatever passes for hashing/digestion itself. The normal digestion of the data is then skipped as that would be ignored unless another signed info in the message has some other algorithm that needs it. The 'data to be verified' may be the content of the PKCS#7 message or it will be the authenticatedAttributes (signedAttrs if CMS), modified, if those are present. This is done by: (1) Make ->m and ->m_size point to the data to be verified rather than making public_key_verify_signature() access the data directly. This is so that keyctl(KEYCTL_PKEY_VERIFY) will still work. (2) Add a flag, ->algo_takes_data, to indicate that the verification algorithm wants to access the data to be verified directly rather than having it digested first. (3) If the PKCS#7 message has authenticatedAttributes (or CMS signedAttrs), then the digest contained therein will be validated as now, and the modified attrs blob will either be digested or assigned to ->m as appropriate. (4) If present, always copy and modify the authenticatedAttributes (or signedAttrs) then digest that in one go rather than calling the shash update twice (once for the tag and once for the rest). (5) For ML-DSA, point ->m to the TBSCertificate instead of digesting it and using the digest. Note that whilst ML-DSA does allow for an "external mu", CMS doesn't yet have that standardised. Signed-off-by: David Howells <dhowells@redhat.com> cc: Lukas Wunner <lukas@wunner.de> cc: Ignat Korchagin <ignat@cloudflare.com> cc: Stephan Mueller <smueller@chronox.de> cc: Eric Biggers <ebiggers@kernel.org> cc: Herbert Xu <herbert@gondor.apana.org.au> cc: keyrings@vger.kernel.org cc: linux-crypto@vger.kernel.org
1 parent f728074 commit f3eccec

5 files changed

Lines changed: 51 additions & 20 deletions

File tree

crypto/asymmetric_keys/pkcs7_parser.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -599,8 +599,8 @@ int pkcs7_sig_note_set_of_authattrs(void *context, size_t hdrlen,
599599
}
600600

601601
/* We need to switch the 'CONT 0' to a 'SET OF' when we digest */
602-
sinfo->authattrs = value - (hdrlen - 1);
603-
sinfo->authattrs_len = vlen + (hdrlen - 1);
602+
sinfo->authattrs = value - hdrlen;
603+
sinfo->authattrs_len = vlen + hdrlen;
604604
return 0;
605605
}
606606

crypto/asymmetric_keys/pkcs7_verify.c

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,16 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7,
3030

3131
kenter(",%u,%s", sinfo->index, sinfo->sig->hash_algo);
3232

33+
if (!sinfo->authattrs && sig->algo_takes_data) {
34+
/* There's no intermediate digest and the signature algo
35+
* doesn't want the data prehashing.
36+
*/
37+
sig->m = (void *)pkcs7->data;
38+
sig->m_size = pkcs7->data_len;
39+
sig->m_free = false;
40+
return 0;
41+
}
42+
3343
/* The digest was calculated already. */
3444
if (sig->m)
3545
return 0;
@@ -48,9 +58,10 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7,
4858
sig->m_size = crypto_shash_digestsize(tfm);
4959

5060
ret = -ENOMEM;
51-
sig->m = kmalloc(sig->m_size, GFP_KERNEL);
61+
sig->m = kmalloc(umax(sinfo->authattrs_len, sig->m_size), GFP_KERNEL);
5262
if (!sig->m)
5363
goto error_no_desc;
64+
sig->m_free = true;
5465

5566
desc = kzalloc(desc_size, GFP_KERNEL);
5667
if (!desc)
@@ -69,8 +80,6 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7,
6980
* digest we just calculated.
7081
*/
7182
if (sinfo->authattrs) {
72-
u8 tag;
73-
7483
if (!sinfo->msgdigest) {
7584
pr_warn("Sig %u: No messageDigest\n", sinfo->index);
7685
ret = -EKEYREJECTED;
@@ -96,21 +105,25 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7,
96105
* as the contents of the digest instead. Note that we need to
97106
* convert the attributes from a CONT.0 into a SET before we
98107
* hash it.
108+
*
109+
* However, for certain algorithms, such as ML-DSA, the digest
110+
* is integrated into the signing algorithm. In such a case,
111+
* we copy the authattrs, modifying the tag type, and set that
112+
* as the digest.
99113
*/
100-
memset(sig->m, 0, sig->m_size);
101-
102-
103-
ret = crypto_shash_init(desc);
104-
if (ret < 0)
105-
goto error;
106-
tag = ASN1_CONS_BIT | ASN1_SET;
107-
ret = crypto_shash_update(desc, &tag, 1);
108-
if (ret < 0)
109-
goto error;
110-
ret = crypto_shash_finup(desc, sinfo->authattrs,
111-
sinfo->authattrs_len, sig->m);
112-
if (ret < 0)
113-
goto error;
114+
memcpy(sig->m, sinfo->authattrs, sinfo->authattrs_len);
115+
sig->m[0] = ASN1_CONS_BIT | ASN1_SET;
116+
117+
if (sig->algo_takes_data) {
118+
sig->m_size = sinfo->authattrs_len;
119+
ret = 0;
120+
} else {
121+
ret = crypto_shash_digest(desc, sig->m,
122+
sinfo->authattrs_len,
123+
sig->m);
124+
if (ret < 0)
125+
goto error;
126+
}
114127
pr_devel("AADigest = [%*ph]\n", 8, sig->m);
115128
}
116129

@@ -137,6 +150,11 @@ int pkcs7_get_digest(struct pkcs7_message *pkcs7, const u8 **buf, u32 *len,
137150
ret = pkcs7_digest(pkcs7, sinfo);
138151
if (ret)
139152
return ret;
153+
if (!sinfo->sig->m_free) {
154+
pr_notice_once("%s: No digest available\n", __func__);
155+
return -EINVAL; /* TODO: MLDSA doesn't necessarily calculate an
156+
* intermediate digest. */
157+
}
140158

141159
*buf = sinfo->sig->m;
142160
*len = sinfo->sig->m_size;

crypto/asymmetric_keys/signature.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ void public_key_signature_free(struct public_key_signature *sig)
2828
for (i = 0; i < ARRAY_SIZE(sig->auth_ids); i++)
2929
kfree(sig->auth_ids[i]);
3030
kfree(sig->s);
31-
kfree(sig->m);
31+
if (sig->m_free)
32+
kfree(sig->m);
3233
kfree(sig);
3334
}
3435
}

crypto/asymmetric_keys/x509_public_key.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,14 @@ int x509_get_sig_params(struct x509_certificate *cert)
5050

5151
sig->s_size = cert->raw_sig_size;
5252

53+
if (sig->algo_takes_data) {
54+
/* The signature algorithm does whatever passes for hashing. */
55+
sig->m = (u8 *)cert->tbs;
56+
sig->m_size = cert->tbs_size;
57+
sig->m_free = false;
58+
goto out;
59+
}
60+
5361
/* Allocate the hashing algorithm we're going to need and find out how
5462
* big the hash operational data will be.
5563
*/
@@ -69,6 +77,7 @@ int x509_get_sig_params(struct x509_certificate *cert)
6977
sig->m = kmalloc(sig->m_size, GFP_KERNEL);
7078
if (!sig->m)
7179
goto error;
80+
sig->m_free = true;
7281

7382
desc = kzalloc(desc_size, GFP_KERNEL);
7483
if (!desc)
@@ -84,6 +93,7 @@ int x509_get_sig_params(struct x509_certificate *cert)
8493
kfree(desc);
8594
error:
8695
crypto_free_shash(tfm);
96+
out:
8797
pr_devel("<==%s() = %d\n", __func__, ret);
8898
return ret;
8999
}

include/crypto/public_key.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ struct public_key_signature {
4646
u8 *m; /* Message data to pass to verifier */
4747
u32 s_size; /* Number of bytes in signature */
4848
u32 m_size; /* Number of bytes in ->m */
49+
bool m_free; /* T if ->m needs freeing */
50+
bool algo_takes_data; /* T if public key algo operates on data, not a hash */
4951
const char *pkey_algo;
5052
const char *hash_algo;
5153
const char *encoding;

0 commit comments

Comments
 (0)