Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 566a117a authored by David Howells's avatar David Howells
Browse files

PKCS#7: Make the signature a pointer rather than embedding it



Point to the public_key_signature struct from the pkcs7_signed_info struct
rather than embedding it.  This makes the code consistent with the X.509
signature handling and makes it possible to have a common cleanup function.

We also save a copy of the digest in the signature without sharing the
memory with the crypto layer metadata.

Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
parent 77d0910d
Loading
Loading
Loading
Loading
+23 −15
Original line number Diff line number Diff line
@@ -44,9 +44,7 @@ struct pkcs7_parse_context {
static void pkcs7_free_signed_info(struct pkcs7_signed_info *sinfo)
{
	if (sinfo) {
		kfree(sinfo->sig.s);
		kfree(sinfo->sig.digest);
		kfree(sinfo->signing_cert_id);
		public_key_signature_free(sinfo->sig);
		kfree(sinfo);
	}
}
@@ -125,6 +123,10 @@ struct pkcs7_message *pkcs7_parse_message(const void *data, size_t datalen)
	ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL);
	if (!ctx->sinfo)
		goto out_no_sinfo;
	ctx->sinfo->sig = kzalloc(sizeof(struct public_key_signature),
				  GFP_KERNEL);
	if (!ctx->sinfo->sig)
		goto out_no_sig;

	ctx->data = (unsigned long)data;
	ctx->ppcerts = &ctx->certs;
@@ -150,6 +152,7 @@ struct pkcs7_message *pkcs7_parse_message(const void *data, size_t datalen)
		ctx->certs = cert->next;
		x509_free_certificate(cert);
	}
out_no_sig:
	pkcs7_free_signed_info(ctx->sinfo);
out_no_sinfo:
	pkcs7_free_message(ctx->msg);
@@ -218,25 +221,26 @@ int pkcs7_sig_note_digest_algo(void *context, size_t hdrlen,

	switch (ctx->last_oid) {
	case OID_md4:
		ctx->sinfo->sig.hash_algo = "md4";
		ctx->sinfo->sig->hash_algo = "md4";
		break;
	case OID_md5:
		ctx->sinfo->sig.hash_algo = "md5";
		ctx->sinfo->sig->hash_algo = "md5";
		break;
	case OID_sha1:
		ctx->sinfo->sig.hash_algo = "sha1";
		ctx->sinfo->sig->hash_algo = "sha1";
		break;
	case OID_sha256:
		ctx->sinfo->sig.hash_algo = "sha256";
		ctx->sinfo->sig->hash_algo = "sha256";
		break;
	case OID_sha384:
		ctx->sinfo->sig.hash_algo = "sha384";
		ctx->sinfo->sig->hash_algo = "sha384";
		break;
	case OID_sha512:
		ctx->sinfo->sig.hash_algo = "sha512";
		ctx->sinfo->sig->hash_algo = "sha512";
		break;
	case OID_sha224:
		ctx->sinfo->sig.hash_algo = "sha224";
		ctx->sinfo->sig->hash_algo = "sha224";
		break;
	default:
		printk("Unsupported digest algo: %u\n", ctx->last_oid);
		return -ENOPKG;
@@ -255,7 +259,7 @@ int pkcs7_sig_note_pkey_algo(void *context, size_t hdrlen,

	switch (ctx->last_oid) {
	case OID_rsaEncryption:
		ctx->sinfo->sig.pkey_algo = "rsa";
		ctx->sinfo->sig->pkey_algo = "rsa";
		break;
	default:
		printk("Unsupported pkey algo: %u\n", ctx->last_oid);
@@ -615,11 +619,11 @@ int pkcs7_sig_note_signature(void *context, size_t hdrlen,
{
	struct pkcs7_parse_context *ctx = context;

	ctx->sinfo->sig.s = kmemdup(value, vlen, GFP_KERNEL);
	if (!ctx->sinfo->sig.s)
	ctx->sinfo->sig->s = kmemdup(value, vlen, GFP_KERNEL);
	if (!ctx->sinfo->sig->s)
		return -ENOMEM;

	ctx->sinfo->sig.s_size = vlen;
	ctx->sinfo->sig->s_size = vlen;
	return 0;
}

@@ -655,12 +659,16 @@ int pkcs7_note_signed_info(void *context, size_t hdrlen,

	pr_devel("SINFO KID: %u [%*phN]\n", kid->len, kid->len, kid->data);

	sinfo->signing_cert_id = kid;
	sinfo->sig->auth_ids[0] = kid;
	sinfo->index = ++ctx->sinfo_index;
	*ctx->ppsinfo = sinfo;
	ctx->ppsinfo = &sinfo->next;
	ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL);
	if (!ctx->sinfo)
		return -ENOMEM;
	ctx->sinfo->sig = kzalloc(sizeof(struct public_key_signature),
				  GFP_KERNEL);
	if (!ctx->sinfo->sig)
		return -ENOMEM;
	return 0;
}
+4 −6
Original line number Diff line number Diff line
@@ -41,19 +41,17 @@ struct pkcs7_signed_info {
#define	sinfo_has_ms_statement_type	5
	time64_t	signing_time;

	/* Issuing cert serial number and issuer's name [PKCS#7 or CMS ver 1]
	 * or issuing cert's SKID [CMS ver 3].
	 */
	struct asymmetric_key_id *signing_cert_id;

	/* Message signature.
	 *
	 * This contains the generated digest of _either_ the Content Data or
	 * the Authenticated Attributes [RFC2315 9.3].  If the latter, one of
	 * the attributes contains the digest of the the Content Data within
	 * it.
	 *
	 * THis also contains the issuing cert serial number and issuer's name
	 * [PKCS#7 or CMS ver 1] or issuing cert's SKID [CMS ver 3].
	 */
	struct public_key_signature sig;
	struct public_key_signature *sig;
};

struct pkcs7_message {
+2 −2
Original line number Diff line number Diff line
@@ -27,7 +27,7 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
				    struct pkcs7_signed_info *sinfo,
				    struct key *trust_keyring)
{
	struct public_key_signature *sig = &sinfo->sig;
	struct public_key_signature *sig = sinfo->sig;
	struct x509_certificate *x509, *last = NULL, *p;
	struct key *key;
	bool trusted;
@@ -105,7 +105,7 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
	 * the signed info directly.
	 */
	key = x509_request_asymmetric_key(trust_keyring,
					  sinfo->signing_cert_id,
					  sinfo->sig->auth_ids[0],
					  NULL,
					  false);
	if (!IS_ERR(key)) {
+26 −25
Original line number Diff line number Diff line
@@ -25,34 +25,36 @@
static int pkcs7_digest(struct pkcs7_message *pkcs7,
			struct pkcs7_signed_info *sinfo)
{
	struct public_key_signature *sig = sinfo->sig;
	struct crypto_shash *tfm;
	struct shash_desc *desc;
	size_t digest_size, desc_size;
	void *digest;
	size_t desc_size;
	int ret;

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

	if (!sinfo->sig.hash_algo)
	if (!sinfo->sig->hash_algo)
		return -ENOPKG;

	/* Allocate the hashing algorithm we're going to need and find out how
	 * big the hash operational data will be.
	 */
	tfm = crypto_alloc_shash(sinfo->sig.hash_algo, 0, 0);
	tfm = crypto_alloc_shash(sinfo->sig->hash_algo, 0, 0);
	if (IS_ERR(tfm))
		return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm);

	desc_size = crypto_shash_descsize(tfm) + sizeof(*desc);
	sinfo->sig.digest_size = digest_size = crypto_shash_digestsize(tfm);
	sig->digest_size = crypto_shash_digestsize(tfm);

	ret = -ENOMEM;
	digest = kzalloc(ALIGN(digest_size, __alignof__(*desc)) + desc_size,
			 GFP_KERNEL);
	if (!digest)
	sig->digest = kmalloc(sig->digest_size, GFP_KERNEL);
	if (!sig->digest)
		goto error_no_desc;

	desc = kzalloc(desc_size, GFP_KERNEL);
	if (!desc)
		goto error_no_desc;

	desc = PTR_ALIGN(digest + digest_size, __alignof__(*desc));
	desc->tfm   = tfm;
	desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;

@@ -60,10 +62,11 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7,
	ret = crypto_shash_init(desc);
	if (ret < 0)
		goto error;
	ret = crypto_shash_finup(desc, pkcs7->data, pkcs7->data_len, digest);
	ret = crypto_shash_finup(desc, pkcs7->data, pkcs7->data_len,
				 sig->digest);
	if (ret < 0)
		goto error;
	pr_devel("MsgDigest = [%*ph]\n", 8, digest);
	pr_devel("MsgDigest = [%*ph]\n", 8, sig->digest);

	/* However, if there are authenticated attributes, there must be a
	 * message digest attribute amongst them which corresponds to the
@@ -78,14 +81,15 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7,
			goto error;
		}

		if (sinfo->msgdigest_len != sinfo->sig.digest_size) {
		if (sinfo->msgdigest_len != sig->digest_size) {
			pr_debug("Sig %u: Invalid digest size (%u)\n",
				 sinfo->index, sinfo->msgdigest_len);
			ret = -EBADMSG;
			goto error;
		}

		if (memcmp(digest, sinfo->msgdigest, sinfo->msgdigest_len) != 0) {
		if (memcmp(sig->digest, sinfo->msgdigest,
			   sinfo->msgdigest_len) != 0) {
			pr_debug("Sig %u: Message digest doesn't match\n",
				 sinfo->index);
			ret = -EKEYREJECTED;
@@ -97,7 +101,7 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7,
		 * convert the attributes from a CONT.0 into a SET before we
		 * hash it.
		 */
		memset(digest, 0, sinfo->sig.digest_size);
		memset(sig->digest, 0, sig->digest_size);

		ret = crypto_shash_init(desc);
		if (ret < 0)
@@ -107,17 +111,14 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7,
		if (ret < 0)
			goto error;
		ret = crypto_shash_finup(desc, sinfo->authattrs,
					 sinfo->authattrs_len, digest);
					 sinfo->authattrs_len, sig->digest);
		if (ret < 0)
			goto error;
		pr_devel("AADigest = [%*ph]\n", 8, digest);
		pr_devel("AADigest = [%*ph]\n", 8, sig->digest);
	}

	sinfo->sig.digest = digest;
	digest = NULL;

error:
	kfree(digest);
	kfree(desc);
error_no_desc:
	crypto_free_shash(tfm);
	kleave(" = %d", ret);
@@ -144,12 +145,12 @@ static int pkcs7_find_key(struct pkcs7_message *pkcs7,
		 * PKCS#7 message - but I can't be 100% sure of that.  It's
		 * possible this will need element-by-element comparison.
		 */
		if (!asymmetric_key_id_same(x509->id, sinfo->signing_cert_id))
		if (!asymmetric_key_id_same(x509->id, sinfo->sig->auth_ids[0]))
			continue;
		pr_devel("Sig %u: Found cert serial match X.509[%u]\n",
			 sinfo->index, certix);

		if (x509->pub->pkey_algo != sinfo->sig.pkey_algo) {
		if (x509->pub->pkey_algo != sinfo->sig->pkey_algo) {
			pr_warn("Sig %u: X.509 algo and PKCS#7 sig algo don't match\n",
				sinfo->index);
			continue;
@@ -164,7 +165,7 @@ static int pkcs7_find_key(struct pkcs7_message *pkcs7,
	 */
	pr_debug("Sig %u: Issuing X.509 cert not found (#%*phN)\n",
		 sinfo->index,
		 sinfo->signing_cert_id->len, sinfo->signing_cert_id->data);
		 sinfo->sig->auth_ids[0]->len, sinfo->sig->auth_ids[0]->data);
	return 0;
}

@@ -334,7 +335,7 @@ static int pkcs7_verify_one(struct pkcs7_message *pkcs7,
	}

	/* Verify the PKCS#7 binary against the key */
	ret = public_key_verify_signature(sinfo->signer->pub, &sinfo->sig);
	ret = public_key_verify_signature(sinfo->signer->pub, sinfo->sig);
	if (ret < 0)
		return ret;