From c4e961ecec994059dbdd5f34b58454d5243ad8f0 Mon Sep 17 00:00:00 2001 From: AKASHI Takahiro Date: Wed, 13 Nov 2019 09:44:58 +0900 Subject: lib: crypto: add public key utility Imported from linux kernel v5.3: asymmetric-type.h with changes marked as __UBOOT__ asymmetric_type.c with changes marked as __UBOOT__ public_key.h with changes marked as __UBOOT__ public_key.c with changes marked as __UBOOT__ Signed-off-by: AKASHI Takahiro --- include/crypto/public_key.h | 90 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 include/crypto/public_key.h (limited to 'include/crypto') diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h new file mode 100644 index 00000000000..436a1ee1ee6 --- /dev/null +++ b/include/crypto/public_key.h @@ -0,0 +1,90 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Asymmetric public-key algorithm definitions + * + * See Documentation/crypto/asymmetric-keys.txt + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#ifndef _LINUX_PUBLIC_KEY_H +#define _LINUX_PUBLIC_KEY_H + +#ifdef __UBOOT__ +#include +#else +#include +#endif +#include + +/* + * Cryptographic data for the public-key subtype of the asymmetric key type. + * + * Note that this may include private part of the key as well as the public + * part. + */ +struct public_key { + void *key; + u32 keylen; + enum OID algo; + void *params; + u32 paramlen; + bool key_is_private; + const char *id_type; + const char *pkey_algo; +}; + +extern void public_key_free(struct public_key *key); + +/* + * Public key cryptography signature data + */ +struct public_key_signature { + struct asymmetric_key_id *auth_ids[2]; + u8 *s; /* Signature */ + u32 s_size; /* Number of bytes in signature */ + u8 *digest; + u8 digest_size; /* Number of bytes in digest */ + const char *pkey_algo; + const char *hash_algo; + const char *encoding; +}; + +extern void public_key_signature_free(struct public_key_signature *sig); + +#ifndef __UBOOT__ +extern struct asymmetric_key_subtype public_key_subtype; + +struct key; +struct key_type; +union key_payload; + +extern int restrict_link_by_signature(struct key *dest_keyring, + const struct key_type *type, + const union key_payload *payload, + struct key *trust_keyring); + +extern int restrict_link_by_key_or_keyring(struct key *dest_keyring, + const struct key_type *type, + const union key_payload *payload, + struct key *trusted); + +extern int restrict_link_by_key_or_keyring_chain(struct key *trust_keyring, + const struct key_type *type, + const union key_payload *payload, + struct key *trusted); + +extern int query_asymmetric_key(const struct kernel_pkey_params *, + struct kernel_pkey_query *); + +extern int encrypt_blob(struct kernel_pkey_params *, const void *, void *); +extern int decrypt_blob(struct kernel_pkey_params *, const void *, void *); +extern int create_signature(struct kernel_pkey_params *, const void *, void *); +extern int verify_signature(const struct key *, + const struct public_key_signature *); + +int public_key_verify_signature(const struct public_key *pkey, + const struct public_key_signature *sig); +#endif /* !__UBOOT__ */ + +#endif /* _LINUX_PUBLIC_KEY_H */ -- cgit v1.3.1 From 9b933bf6f4fa4db2f8ab94d3b977b7fefa2644dd Mon Sep 17 00:00:00 2001 From: AKASHI Takahiro Date: Wed, 13 Nov 2019 09:44:59 +0900 Subject: lib: crypto: add rsa public key parser Imported from linux kernel v5.3: rsapubkey.asn1 without changes rsa.h without changes rsa_helper.c with changes marked as __UBOOT__ Signed-off-by: AKASHI Takahiro --- include/crypto/internal/rsa.h | 57 ++++++++++++ lib/crypto/Kconfig | 11 +++ lib/crypto/Makefile | 11 +++ lib/crypto/rsa_helper.c | 198 ++++++++++++++++++++++++++++++++++++++++++ lib/crypto/rsapubkey.asn1 | 4 + 5 files changed, 281 insertions(+) create mode 100644 include/crypto/internal/rsa.h create mode 100644 lib/crypto/rsa_helper.c create mode 100644 lib/crypto/rsapubkey.asn1 (limited to 'include/crypto') diff --git a/include/crypto/internal/rsa.h b/include/crypto/internal/rsa.h new file mode 100644 index 00000000000..e870133f4b7 --- /dev/null +++ b/include/crypto/internal/rsa.h @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * RSA internal helpers + * + * Copyright (c) 2015, Intel Corporation + * Authors: Tadeusz Struk + */ +#ifndef _RSA_HELPER_ +#define _RSA_HELPER_ +#include + +/** + * rsa_key - RSA key structure + * @n : RSA modulus raw byte stream + * @e : RSA public exponent raw byte stream + * @d : RSA private exponent raw byte stream + * @p : RSA prime factor p of n raw byte stream + * @q : RSA prime factor q of n raw byte stream + * @dp : RSA exponent d mod (p - 1) raw byte stream + * @dq : RSA exponent d mod (q - 1) raw byte stream + * @qinv : RSA CRT coefficient q^(-1) mod p raw byte stream + * @n_sz : length in bytes of RSA modulus n + * @e_sz : length in bytes of RSA public exponent + * @d_sz : length in bytes of RSA private exponent + * @p_sz : length in bytes of p field + * @q_sz : length in bytes of q field + * @dp_sz : length in bytes of dp field + * @dq_sz : length in bytes of dq field + * @qinv_sz : length in bytes of qinv field + */ +struct rsa_key { + const u8 *n; + const u8 *e; + const u8 *d; + const u8 *p; + const u8 *q; + const u8 *dp; + const u8 *dq; + const u8 *qinv; + size_t n_sz; + size_t e_sz; + size_t d_sz; + size_t p_sz; + size_t q_sz; + size_t dp_sz; + size_t dq_sz; + size_t qinv_sz; +}; + +int rsa_parse_pub_key(struct rsa_key *rsa_key, const void *key, + unsigned int key_len); + +int rsa_parse_priv_key(struct rsa_key *rsa_key, const void *key, + unsigned int key_len); + +extern struct crypto_template rsa_pkcs1pad_tmpl; +#endif diff --git a/lib/crypto/Kconfig b/lib/crypto/Kconfig index b8e8288d2f8..9572ea8c87f 100644 --- a/lib/crypto/Kconfig +++ b/lib/crypto/Kconfig @@ -16,4 +16,15 @@ config ASYMMETRIC_PUBLIC_KEY_SUBTYPE appropriate hash algorithms (such as SHA-1) must be available. ENOPKG will be reported if the requisite algorithm is unavailable. +config RSA_PUBLIC_KEY_PARSER + bool "RSA public key parser" + depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE + select ASN1_DECODER + select ASN1_COMPILER + select OID_REGISTRY + help + This option provides support for parsing a blob containing RSA + public key data and provides the ability to instantiate a public + key. + endif # ASYMMETRIC_KEY_TYPE diff --git a/lib/crypto/Makefile b/lib/crypto/Makefile index a284de9e041..69330a9ebd2 100644 --- a/lib/crypto/Makefile +++ b/lib/crypto/Makefile @@ -8,3 +8,14 @@ obj-$(CONFIG_ASYMMETRIC_KEY_TYPE) += asymmetric_keys.o asymmetric_keys-y := asymmetric_type.o obj-$(CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE) += public_key.o + +# +# RSA public key parser +# +obj-$(CONFIG_RSA_PUBLIC_KEY_PARSER) += rsa_public_key.o +rsa_public_key-y := \ + rsapubkey.asn1.o \ + rsa_helper.o + +$(obj)/rsapubkey.asn1.o: $(obj)/rsapubkey.asn1.c $(obj)/rsapubkey.asn1.h +$(obj)/rsa_helper.o: $(obj)/rsapubkey.asn1.h diff --git a/lib/crypto/rsa_helper.c b/lib/crypto/rsa_helper.c new file mode 100644 index 00000000000..aca627a4a61 --- /dev/null +++ b/lib/crypto/rsa_helper.c @@ -0,0 +1,198 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * RSA key extract helper + * + * Copyright (c) 2015, Intel Corporation + * Authors: Tadeusz Struk + */ +#ifndef __UBOOT__ +#include +#include +#endif +#include +#ifndef __UBOOT__ +#include +#endif +#include +#include "rsapubkey.asn1.h" +#ifndef __UBOOT__ +#include "rsaprivkey.asn1.h" +#endif + +int rsa_get_n(void *context, size_t hdrlen, unsigned char tag, + const void *value, size_t vlen) +{ + struct rsa_key *key = context; +#ifndef __UBOOT__ + const u8 *ptr = value; + size_t n_sz = vlen; +#endif + + /* invalid key provided */ + if (!value || !vlen) + return -EINVAL; + +#ifndef __UBOOT__ + if (fips_enabled) { + while (n_sz && !*ptr) { + ptr++; + n_sz--; + } + + /* In FIPS mode only allow key size 2K and higher */ + if (n_sz < 256) { + pr_err("RSA: key size not allowed in FIPS mode\n"); + return -EINVAL; + } + } +#endif + + key->n = value; + key->n_sz = vlen; + + return 0; +} + +int rsa_get_e(void *context, size_t hdrlen, unsigned char tag, + const void *value, size_t vlen) +{ + struct rsa_key *key = context; + + /* invalid key provided */ + if (!value || !key->n_sz || !vlen || vlen > key->n_sz) + return -EINVAL; + + key->e = value; + key->e_sz = vlen; + + return 0; +} + +int rsa_get_d(void *context, size_t hdrlen, unsigned char tag, + const void *value, size_t vlen) +{ + struct rsa_key *key = context; + + /* invalid key provided */ + if (!value || !key->n_sz || !vlen || vlen > key->n_sz) + return -EINVAL; + + key->d = value; + key->d_sz = vlen; + + return 0; +} + +int rsa_get_p(void *context, size_t hdrlen, unsigned char tag, + const void *value, size_t vlen) +{ + struct rsa_key *key = context; + + /* invalid key provided */ + if (!value || !vlen || vlen > key->n_sz) + return -EINVAL; + + key->p = value; + key->p_sz = vlen; + + return 0; +} + +int rsa_get_q(void *context, size_t hdrlen, unsigned char tag, + const void *value, size_t vlen) +{ + struct rsa_key *key = context; + + /* invalid key provided */ + if (!value || !vlen || vlen > key->n_sz) + return -EINVAL; + + key->q = value; + key->q_sz = vlen; + + return 0; +} + +int rsa_get_dp(void *context, size_t hdrlen, unsigned char tag, + const void *value, size_t vlen) +{ + struct rsa_key *key = context; + + /* invalid key provided */ + if (!value || !vlen || vlen > key->n_sz) + return -EINVAL; + + key->dp = value; + key->dp_sz = vlen; + + return 0; +} + +int rsa_get_dq(void *context, size_t hdrlen, unsigned char tag, + const void *value, size_t vlen) +{ + struct rsa_key *key = context; + + /* invalid key provided */ + if (!value || !vlen || vlen > key->n_sz) + return -EINVAL; + + key->dq = value; + key->dq_sz = vlen; + + return 0; +} + +int rsa_get_qinv(void *context, size_t hdrlen, unsigned char tag, + const void *value, size_t vlen) +{ + struct rsa_key *key = context; + + /* invalid key provided */ + if (!value || !vlen || vlen > key->n_sz) + return -EINVAL; + + key->qinv = value; + key->qinv_sz = vlen; + + return 0; +} + +/** + * rsa_parse_pub_key() - decodes the BER encoded buffer and stores in the + * provided struct rsa_key, pointers to the raw key as is, + * so that the caller can copy it or MPI parse it, etc. + * + * @rsa_key: struct rsa_key key representation + * @key: key in BER format + * @key_len: length of key + * + * Return: 0 on success or error code in case of error + */ +int rsa_parse_pub_key(struct rsa_key *rsa_key, const void *key, + unsigned int key_len) +{ + return asn1_ber_decoder(&rsapubkey_decoder, rsa_key, key, key_len); +} +EXPORT_SYMBOL_GPL(rsa_parse_pub_key); + +#ifndef __UBOOT__ +/** + * rsa_parse_priv_key() - decodes the BER encoded buffer and stores in the + * provided struct rsa_key, pointers to the raw key + * as is, so that the caller can copy it or MPI parse it, + * etc. + * + * @rsa_key: struct rsa_key key representation + * @key: key in BER format + * @key_len: length of key + * + * Return: 0 on success or error code in case of error + */ +int rsa_parse_priv_key(struct rsa_key *rsa_key, const void *key, + unsigned int key_len) +{ + return asn1_ber_decoder(&rsaprivkey_decoder, rsa_key, key, key_len); +} +EXPORT_SYMBOL_GPL(rsa_parse_priv_key); +#endif diff --git a/lib/crypto/rsapubkey.asn1 b/lib/crypto/rsapubkey.asn1 new file mode 100644 index 00000000000..725498e461d --- /dev/null +++ b/lib/crypto/rsapubkey.asn1 @@ -0,0 +1,4 @@ +RsaPubKey ::= SEQUENCE { + n INTEGER ({ rsa_get_n }), + e INTEGER ({ rsa_get_e }) +} -- cgit v1.3.1 From e85a787c7d63592b6fd9214f0e202fcabca75cf4 Mon Sep 17 00:00:00 2001 From: AKASHI Takahiro Date: Wed, 13 Nov 2019 09:45:01 +0900 Subject: lib: crypto: add pkcs7 message parser Imported from linux kernel v5.3: pkcs7.asn1 without changes pkcs7.h with changes marked as __UBOOT__ pkcs7_parser.h without changes pkcs7_parser.c with changes marked as __UBOOT__ Signed-off-by: AKASHI Takahiro --- include/crypto/pkcs7.h | 47 ++++ lib/crypto/Kconfig | 10 + lib/crypto/Makefile | 11 + lib/crypto/pkcs7.asn1 | 135 +++++++++ lib/crypto/pkcs7_parser.c | 693 ++++++++++++++++++++++++++++++++++++++++++++++ lib/crypto/pkcs7_parser.h | 65 +++++ scripts/Makefile.build | 4 +- 7 files changed, 962 insertions(+), 3 deletions(-) create mode 100644 include/crypto/pkcs7.h create mode 100644 lib/crypto/pkcs7.asn1 create mode 100644 lib/crypto/pkcs7_parser.c create mode 100644 lib/crypto/pkcs7_parser.h (limited to 'include/crypto') diff --git a/include/crypto/pkcs7.h b/include/crypto/pkcs7.h new file mode 100644 index 00000000000..8f5c8a7ee3b --- /dev/null +++ b/include/crypto/pkcs7.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* PKCS#7 crypto data parser + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#ifndef _CRYPTO_PKCS7_H +#define _CRYPTO_PKCS7_H + +#ifndef __UBOOT__ +#include +#include +#endif + +struct key; +struct pkcs7_message; + +/* + * pkcs7_parser.c + */ +extern struct pkcs7_message *pkcs7_parse_message(const void *data, + size_t datalen); +extern void pkcs7_free_message(struct pkcs7_message *pkcs7); + +extern int pkcs7_get_content_data(const struct pkcs7_message *pkcs7, + const void **_data, size_t *_datalen, + size_t *_headerlen); + +#ifndef __UBOOT__ +/* + * pkcs7_trust.c + */ +extern int pkcs7_validate_trust(struct pkcs7_message *pkcs7, + struct key *trust_keyring); + +/* + * pkcs7_verify.c + */ +extern int pkcs7_verify(struct pkcs7_message *pkcs7, + enum key_being_used_for usage); + +extern int pkcs7_supply_detached_data(struct pkcs7_message *pkcs7, + const void *data, size_t datalen); +#endif + +#endif /* _CRYPTO_PKCS7_H */ diff --git a/lib/crypto/Kconfig b/lib/crypto/Kconfig index aeb599c593a..2b221b915aa 100644 --- a/lib/crypto/Kconfig +++ b/lib/crypto/Kconfig @@ -39,4 +39,14 @@ config X509_CERTIFICATE_PARSER data and provides the ability to instantiate a crypto key from a public key packet found inside the certificate. +config PKCS7_MESSAGE_PARSER + bool "PKCS#7 message parser" + depends on X509_CERTIFICATE_PARSER + select ASN1_DECODER + select ASN1_COMPILER + select OID_REGISTRY + help + This option provides support for parsing PKCS#7 format messages for + signature data and provides the ability to verify the signature. + endif # ASYMMETRIC_KEY_TYPE diff --git a/lib/crypto/Makefile b/lib/crypto/Makefile index d7e27be568a..8267fee0a7b 100644 --- a/lib/crypto/Makefile +++ b/lib/crypto/Makefile @@ -36,3 +36,14 @@ $(obj)/x509_cert_parser.o: \ $(obj)/x509.asn1.o: $(obj)/x509.asn1.c $(obj)/x509.asn1.h $(obj)/x509_akid.asn1.o: $(obj)/x509_akid.asn1.c $(obj)/x509_akid.asn1.h + +# +# PKCS#7 message handling +# +obj-$(CONFIG_PKCS7_MESSAGE_PARSER) += pkcs7_message.o +pkcs7_message-y := \ + pkcs7.asn1.o \ + pkcs7_parser.o + +$(obj)/pkcs7_parser.o: $(obj)/pkcs7.asn1.h +$(obj)/pkcs7.asn1.o: $(obj)/pkcs7.asn1.c $(obj)/pkcs7.asn1.h diff --git a/lib/crypto/pkcs7.asn1 b/lib/crypto/pkcs7.asn1 new file mode 100644 index 00000000000..1eca740b816 --- /dev/null +++ b/lib/crypto/pkcs7.asn1 @@ -0,0 +1,135 @@ +PKCS7ContentInfo ::= SEQUENCE { + contentType ContentType ({ pkcs7_check_content_type }), + content [0] EXPLICIT SignedData OPTIONAL +} + +ContentType ::= OBJECT IDENTIFIER ({ pkcs7_note_OID }) + +SignedData ::= SEQUENCE { + version INTEGER ({ pkcs7_note_signeddata_version }), + digestAlgorithms DigestAlgorithmIdentifiers, + contentInfo ContentInfo ({ pkcs7_note_content }), + certificates CHOICE { + certSet [0] IMPLICIT ExtendedCertificatesAndCertificates, + certSequence [2] IMPLICIT Certificates + } OPTIONAL ({ pkcs7_note_certificate_list }), + crls CHOICE { + crlSet [1] IMPLICIT CertificateRevocationLists, + crlSequence [3] IMPLICIT CRLSequence + } OPTIONAL, + signerInfos SignerInfos +} + +ContentInfo ::= SEQUENCE { + contentType ContentType ({ pkcs7_note_OID }), + content [0] EXPLICIT Data OPTIONAL +} + +Data ::= ANY ({ pkcs7_note_data }) + +DigestAlgorithmIdentifiers ::= CHOICE { + daSet SET OF DigestAlgorithmIdentifier, + daSequence SEQUENCE OF DigestAlgorithmIdentifier +} + +DigestAlgorithmIdentifier ::= SEQUENCE { + algorithm OBJECT IDENTIFIER ({ pkcs7_note_OID }), + parameters ANY OPTIONAL +} + +-- +-- Certificates and certificate lists +-- +ExtendedCertificatesAndCertificates ::= SET OF ExtendedCertificateOrCertificate + +ExtendedCertificateOrCertificate ::= CHOICE { + certificate Certificate, -- X.509 + extendedCertificate [0] IMPLICIT ExtendedCertificate -- PKCS#6 +} + +ExtendedCertificate ::= Certificate -- cheating + +Certificates ::= SEQUENCE OF Certificate + +CertificateRevocationLists ::= SET OF CertificateList + +CertificateList ::= SEQUENCE OF Certificate -- This may be defined incorrectly + +CRLSequence ::= SEQUENCE OF CertificateList + +Certificate ::= ANY ({ pkcs7_extract_cert }) -- X.509 + +-- +-- Signer information +-- +SignerInfos ::= CHOICE { + siSet SET OF SignerInfo, + siSequence SEQUENCE OF SignerInfo +} + +SignerInfo ::= SEQUENCE { + version INTEGER ({ pkcs7_note_signerinfo_version }), + sid SignerIdentifier, -- CMS variant, not PKCS#7 + digestAlgorithm DigestAlgorithmIdentifier ({ pkcs7_sig_note_digest_algo }), + authenticatedAttributes CHOICE { + aaSet [0] IMPLICIT SetOfAuthenticatedAttribute + ({ pkcs7_sig_note_set_of_authattrs }), + aaSequence [2] EXPLICIT SEQUENCE OF AuthenticatedAttribute + -- Explicit because easier to compute digest on + -- sequence of attributes and then reuse encoded + -- sequence in aaSequence. + } OPTIONAL, + digestEncryptionAlgorithm + DigestEncryptionAlgorithmIdentifier ({ pkcs7_sig_note_pkey_algo }), + encryptedDigest EncryptedDigest, + unauthenticatedAttributes CHOICE { + uaSet [1] IMPLICIT SET OF UnauthenticatedAttribute, + uaSequence [3] IMPLICIT SEQUENCE OF UnauthenticatedAttribute + } OPTIONAL +} ({ pkcs7_note_signed_info }) + +SignerIdentifier ::= CHOICE { + -- RFC5652 sec 5.3 + issuerAndSerialNumber IssuerAndSerialNumber, + subjectKeyIdentifier [0] IMPLICIT SubjectKeyIdentifier +} + +IssuerAndSerialNumber ::= SEQUENCE { + issuer Name ({ pkcs7_sig_note_issuer }), + serialNumber CertificateSerialNumber ({ pkcs7_sig_note_serial }) +} + +CertificateSerialNumber ::= INTEGER + +SubjectKeyIdentifier ::= OCTET STRING ({ pkcs7_sig_note_skid }) + +SetOfAuthenticatedAttribute ::= SET OF AuthenticatedAttribute + +AuthenticatedAttribute ::= SEQUENCE { + type OBJECT IDENTIFIER ({ pkcs7_note_OID }), + values SET OF ANY ({ pkcs7_sig_note_authenticated_attr }) +} + +UnauthenticatedAttribute ::= SEQUENCE { + type OBJECT IDENTIFIER, + values SET OF ANY +} + +DigestEncryptionAlgorithmIdentifier ::= SEQUENCE { + algorithm OBJECT IDENTIFIER ({ pkcs7_note_OID }), + parameters ANY OPTIONAL +} + +EncryptedDigest ::= OCTET STRING ({ pkcs7_sig_note_signature }) + +--- +--- X.500 Name +--- +Name ::= SEQUENCE OF RelativeDistinguishedName + +RelativeDistinguishedName ::= SET OF AttributeValueAssertion + +AttributeValueAssertion ::= SEQUENCE { + attributeType OBJECT IDENTIFIER ({ pkcs7_note_OID }), + attributeValue ANY +} diff --git a/lib/crypto/pkcs7_parser.c b/lib/crypto/pkcs7_parser.c new file mode 100644 index 00000000000..bf9e7e888f6 --- /dev/null +++ b/lib/crypto/pkcs7_parser.c @@ -0,0 +1,693 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* PKCS#7 parser + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#define pr_fmt(fmt) "PKCS7: "fmt +#ifdef __UBOOT__ +#include +#include +#endif +#include +#ifndef __UBOOT__ +#include +#include +#include +#endif +#include +#include +#include +#include "pkcs7_parser.h" +#include "pkcs7.asn1.h" + +MODULE_DESCRIPTION("PKCS#7 parser"); +MODULE_AUTHOR("Red Hat, Inc."); +MODULE_LICENSE("GPL"); + +struct pkcs7_parse_context { + struct pkcs7_message *msg; /* Message being constructed */ + struct pkcs7_signed_info *sinfo; /* SignedInfo being constructed */ + struct pkcs7_signed_info **ppsinfo; + struct x509_certificate *certs; /* Certificate cache */ + struct x509_certificate **ppcerts; + unsigned long data; /* Start of data */ + enum OID last_oid; /* Last OID encountered */ + unsigned x509_index; + unsigned sinfo_index; + const void *raw_serial; + unsigned raw_serial_size; + unsigned raw_issuer_size; + const void *raw_issuer; + const void *raw_skid; + unsigned raw_skid_size; + bool expect_skid; +}; + +/* + * Free a signed information block. + */ +static void pkcs7_free_signed_info(struct pkcs7_signed_info *sinfo) +{ + if (sinfo) { + public_key_signature_free(sinfo->sig); + kfree(sinfo); + } +} + +/** + * pkcs7_free_message - Free a PKCS#7 message + * @pkcs7: The PKCS#7 message to free + */ +void pkcs7_free_message(struct pkcs7_message *pkcs7) +{ + struct x509_certificate *cert; + struct pkcs7_signed_info *sinfo; + + if (pkcs7) { + while (pkcs7->certs) { + cert = pkcs7->certs; + pkcs7->certs = cert->next; + x509_free_certificate(cert); + } + while (pkcs7->crl) { + cert = pkcs7->crl; + pkcs7->crl = cert->next; + x509_free_certificate(cert); + } + while (pkcs7->signed_infos) { + sinfo = pkcs7->signed_infos; + pkcs7->signed_infos = sinfo->next; + pkcs7_free_signed_info(sinfo); + } + kfree(pkcs7); + } +} +EXPORT_SYMBOL_GPL(pkcs7_free_message); + +/* + * Check authenticatedAttributes are provided or not provided consistently. + */ +static int pkcs7_check_authattrs(struct pkcs7_message *msg) +{ + struct pkcs7_signed_info *sinfo; + bool want = false; + + sinfo = msg->signed_infos; + if (!sinfo) + goto inconsistent; + + if (sinfo->authattrs) { + want = true; + msg->have_authattrs = true; + } + + for (sinfo = sinfo->next; sinfo; sinfo = sinfo->next) + if (!!sinfo->authattrs != want) + goto inconsistent; + return 0; + +inconsistent: + pr_warn("Inconsistently supplied authAttrs\n"); + return -EINVAL; +} + +/** + * pkcs7_parse_message - Parse a PKCS#7 message + * @data: The raw binary ASN.1 encoded message to be parsed + * @datalen: The size of the encoded message + */ +struct pkcs7_message *pkcs7_parse_message(const void *data, size_t datalen) +{ + struct pkcs7_parse_context *ctx; + struct pkcs7_message *msg = ERR_PTR(-ENOMEM); + int ret; + + ctx = kzalloc(sizeof(struct pkcs7_parse_context), GFP_KERNEL); + if (!ctx) + goto out_no_ctx; + ctx->msg = kzalloc(sizeof(struct pkcs7_message), GFP_KERNEL); + if (!ctx->msg) + goto out_no_msg; + 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; + ctx->ppsinfo = &ctx->msg->signed_infos; + + /* Attempt to decode the signature */ + ret = asn1_ber_decoder(&pkcs7_decoder, ctx, data, datalen); + if (ret < 0) { + msg = ERR_PTR(ret); + goto out; + } + + ret = pkcs7_check_authattrs(ctx->msg); + if (ret < 0) { + msg = ERR_PTR(ret); + goto out; + } + + msg = ctx->msg; + ctx->msg = NULL; + +out: + while (ctx->certs) { + struct x509_certificate *cert = ctx->certs; + 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); +out_no_msg: + kfree(ctx); +out_no_ctx: + return msg; +} +EXPORT_SYMBOL_GPL(pkcs7_parse_message); + +/** + * pkcs7_get_content_data - Get access to the PKCS#7 content + * @pkcs7: The preparsed PKCS#7 message to access + * @_data: Place to return a pointer to the data + * @_data_len: Place to return the data length + * @_headerlen: Size of ASN.1 header not included in _data + * + * Get access to the data content of the PKCS#7 message. The size of the + * header of the ASN.1 object that contains it is also provided and can be used + * to adjust *_data and *_data_len to get the entire object. + * + * Returns -ENODATA if the data object was missing from the message. + */ +int pkcs7_get_content_data(const struct pkcs7_message *pkcs7, + const void **_data, size_t *_data_len, + size_t *_headerlen) +{ + if (!pkcs7->data) + return -ENODATA; + + *_data = pkcs7->data; + *_data_len = pkcs7->data_len; + if (_headerlen) + *_headerlen = pkcs7->data_hdrlen; + return 0; +} +EXPORT_SYMBOL_GPL(pkcs7_get_content_data); + +/* + * Note an OID when we find one for later processing when we know how + * to interpret it. + */ +int pkcs7_note_OID(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct pkcs7_parse_context *ctx = context; + + ctx->last_oid = look_up_OID(value, vlen); + if (ctx->last_oid == OID__NR) { + char buffer[50]; + sprint_oid(value, vlen, buffer, sizeof(buffer)); + printk("PKCS7: Unknown OID: [%lu] %s\n", + (unsigned long)value - ctx->data, buffer); + } + return 0; +} + +/* + * Note the digest algorithm for the signature. + */ +int pkcs7_sig_note_digest_algo(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct pkcs7_parse_context *ctx = context; + + switch (ctx->last_oid) { + case OID_md4: + ctx->sinfo->sig->hash_algo = "md4"; + break; + case OID_md5: + ctx->sinfo->sig->hash_algo = "md5"; + break; + case OID_sha1: + ctx->sinfo->sig->hash_algo = "sha1"; + break; + case OID_sha256: + ctx->sinfo->sig->hash_algo = "sha256"; + break; + case OID_sha384: + ctx->sinfo->sig->hash_algo = "sha384"; + break; + case OID_sha512: + ctx->sinfo->sig->hash_algo = "sha512"; + break; + case OID_sha224: + ctx->sinfo->sig->hash_algo = "sha224"; + break; + default: + printk("Unsupported digest algo: %u\n", ctx->last_oid); + return -ENOPKG; + } + return 0; +} + +/* + * Note the public key algorithm for the signature. + */ +int pkcs7_sig_note_pkey_algo(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct pkcs7_parse_context *ctx = context; + + switch (ctx->last_oid) { + case OID_rsaEncryption: + ctx->sinfo->sig->pkey_algo = "rsa"; + ctx->sinfo->sig->encoding = "pkcs1"; + break; + default: + printk("Unsupported pkey algo: %u\n", ctx->last_oid); + return -ENOPKG; + } + return 0; +} + +/* + * We only support signed data [RFC2315 sec 9]. + */ +int pkcs7_check_content_type(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct pkcs7_parse_context *ctx = context; + + if (ctx->last_oid != OID_signed_data) { + pr_warn("Only support pkcs7_signedData type\n"); + return -EINVAL; + } + + return 0; +} + +/* + * Note the SignedData version + */ +int pkcs7_note_signeddata_version(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct pkcs7_parse_context *ctx = context; + unsigned version; + + if (vlen != 1) + goto unsupported; + + ctx->msg->version = version = *(const u8 *)value; + switch (version) { + case 1: + /* PKCS#7 SignedData [RFC2315 sec 9.1] + * CMS ver 1 SignedData [RFC5652 sec 5.1] + */ + break; + case 3: + /* CMS ver 3 SignedData [RFC2315 sec 5.1] */ + break; + default: + goto unsupported; + } + + return 0; + +unsupported: + pr_warn("Unsupported SignedData version\n"); + return -EINVAL; +} + +/* + * Note the SignerInfo version + */ +int pkcs7_note_signerinfo_version(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct pkcs7_parse_context *ctx = context; + unsigned version; + + if (vlen != 1) + goto unsupported; + + version = *(const u8 *)value; + switch (version) { + case 1: + /* PKCS#7 SignerInfo [RFC2315 sec 9.2] + * CMS ver 1 SignerInfo [RFC5652 sec 5.3] + */ + if (ctx->msg->version != 1) + goto version_mismatch; + ctx->expect_skid = false; + break; + case 3: + /* CMS ver 3 SignerInfo [RFC2315 sec 5.3] */ + if (ctx->msg->version == 1) + goto version_mismatch; + ctx->expect_skid = true; + break; + default: + goto unsupported; + } + + return 0; + +unsupported: + pr_warn("Unsupported SignerInfo version\n"); + return -EINVAL; +version_mismatch: + pr_warn("SignedData-SignerInfo version mismatch\n"); + return -EBADMSG; +} + +/* + * Extract a certificate and store it in the context. + */ +int pkcs7_extract_cert(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct pkcs7_parse_context *ctx = context; + struct x509_certificate *x509; + + if (tag != ((ASN1_UNIV << 6) | ASN1_CONS_BIT | ASN1_SEQ)) { + pr_debug("Cert began with tag %02x at %lu\n", + tag, (unsigned long)ctx - ctx->data); + return -EBADMSG; + } + + /* We have to correct for the header so that the X.509 parser can start + * from the beginning. Note that since X.509 stipulates DER, there + * probably shouldn't be an EOC trailer - but it is in PKCS#7 (which + * stipulates BER). + */ + value -= hdrlen; + vlen += hdrlen; + + if (((u8*)value)[1] == 0x80) + vlen += 2; /* Indefinite length - there should be an EOC */ + + x509 = x509_cert_parse(value, vlen); + if (IS_ERR(x509)) + return PTR_ERR(x509); + + x509->index = ++ctx->x509_index; + pr_debug("Got cert %u for %s\n", x509->index, x509->subject); + pr_debug("- fingerprint %*phN\n", x509->id->len, x509->id->data); + + *ctx->ppcerts = x509; + ctx->ppcerts = &x509->next; + return 0; +} + +/* + * Save the certificate list + */ +int pkcs7_note_certificate_list(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct pkcs7_parse_context *ctx = context; + + pr_devel("Got cert list (%02x)\n", tag); + + *ctx->ppcerts = ctx->msg->certs; + ctx->msg->certs = ctx->certs; + ctx->certs = NULL; + ctx->ppcerts = &ctx->certs; + return 0; +} + +/* + * Note the content type. + */ +int pkcs7_note_content(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct pkcs7_parse_context *ctx = context; + + if (ctx->last_oid != OID_data && + ctx->last_oid != OID_msIndirectData) { + pr_warn("Unsupported data type %d\n", ctx->last_oid); + return -EINVAL; + } + + ctx->msg->data_type = ctx->last_oid; + return 0; +} + +/* + * Extract the data from the message and store that and its content type OID in + * the context. + */ +int pkcs7_note_data(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct pkcs7_parse_context *ctx = context; + + pr_debug("Got data\n"); + + ctx->msg->data = value; + ctx->msg->data_len = vlen; + ctx->msg->data_hdrlen = hdrlen; + return 0; +} + +/* + * Parse authenticated attributes. + */ +int pkcs7_sig_note_authenticated_attr(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct pkcs7_parse_context *ctx = context; + struct pkcs7_signed_info *sinfo = ctx->sinfo; + enum OID content_type; + + pr_devel("AuthAttr: %02x %zu [%*ph]\n", tag, vlen, (unsigned)vlen, value); + + switch (ctx->last_oid) { + case OID_contentType: + if (__test_and_set_bit(sinfo_has_content_type, &sinfo->aa_set)) + goto repeated; + content_type = look_up_OID(value, vlen); + if (content_type != ctx->msg->data_type) { + pr_warn("Mismatch between global data type (%d) and sinfo %u (%d)\n", + ctx->msg->data_type, sinfo->index, + content_type); + return -EBADMSG; + } + return 0; + + case OID_signingTime: + if (__test_and_set_bit(sinfo_has_signing_time, &sinfo->aa_set)) + goto repeated; + /* Should we check that the signing time is consistent + * with the signer's X.509 cert? + */ + return x509_decode_time(&sinfo->signing_time, + hdrlen, tag, value, vlen); + + case OID_messageDigest: + if (__test_and_set_bit(sinfo_has_message_digest, &sinfo->aa_set)) + goto repeated; + if (tag != ASN1_OTS) + return -EBADMSG; + sinfo->msgdigest = value; + sinfo->msgdigest_len = vlen; + return 0; + + case OID_smimeCapabilites: + if (__test_and_set_bit(sinfo_has_smime_caps, &sinfo->aa_set)) + goto repeated; +#ifdef __UBOOT__ /* OID_data is needed for authenticated UEFI variables */ + if (ctx->msg->data_type != OID_msIndirectData && + ctx->msg->data_type != OID_data) { +#else + if (ctx->msg->data_type != OID_msIndirectData) { +#endif + pr_warn("S/MIME Caps only allowed with Authenticode\n"); + return -EKEYREJECTED; + } + return 0; + + /* Microsoft SpOpusInfo seems to be contain cont[0] 16-bit BE + * char URLs and cont[1] 8-bit char URLs. + * + * Microsoft StatementType seems to contain a list of OIDs that + * are also used as extendedKeyUsage types in X.509 certs. + */ + case OID_msSpOpusInfo: + if (__test_and_set_bit(sinfo_has_ms_opus_info, &sinfo->aa_set)) + goto repeated; + goto authenticode_check; + case OID_msStatementType: + if (__test_and_set_bit(sinfo_has_ms_statement_type, &sinfo->aa_set)) + goto repeated; + authenticode_check: + if (ctx->msg->data_type != OID_msIndirectData) { + pr_warn("Authenticode AuthAttrs only allowed with Authenticode\n"); + return -EKEYREJECTED; + } + /* I'm not sure how to validate these */ + return 0; + default: + return 0; + } + +repeated: + /* We permit max one item per AuthenticatedAttribute and no repeats */ + pr_warn("Repeated/multivalue AuthAttrs not permitted\n"); + return -EKEYREJECTED; +} + +/* + * Note the set of auth attributes for digestion purposes [RFC2315 sec 9.3] + */ +int pkcs7_sig_note_set_of_authattrs(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct pkcs7_parse_context *ctx = context; + struct pkcs7_signed_info *sinfo = ctx->sinfo; + + if (!test_bit(sinfo_has_content_type, &sinfo->aa_set) || + !test_bit(sinfo_has_message_digest, &sinfo->aa_set)) { + pr_warn("Missing required AuthAttr\n"); + return -EBADMSG; + } + + if (ctx->msg->data_type != OID_msIndirectData && + test_bit(sinfo_has_ms_opus_info, &sinfo->aa_set)) { + pr_warn("Unexpected Authenticode AuthAttr\n"); + return -EBADMSG; + } + + /* We need to switch the 'CONT 0' to a 'SET OF' when we digest */ + sinfo->authattrs = value - (hdrlen - 1); + sinfo->authattrs_len = vlen + (hdrlen - 1); + return 0; +} + +/* + * Note the issuing certificate serial number + */ +int pkcs7_sig_note_serial(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct pkcs7_parse_context *ctx = context; + ctx->raw_serial = value; + ctx->raw_serial_size = vlen; + return 0; +} + +/* + * Note the issuer's name + */ +int pkcs7_sig_note_issuer(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct pkcs7_parse_context *ctx = context; + ctx->raw_issuer = value; + ctx->raw_issuer_size = vlen; + return 0; +} + +/* + * Note the issuing cert's subjectKeyIdentifier + */ +int pkcs7_sig_note_skid(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct pkcs7_parse_context *ctx = context; + + pr_devel("SKID: %02x %zu [%*ph]\n", tag, vlen, (unsigned)vlen, value); + + ctx->raw_skid = value; + ctx->raw_skid_size = vlen; + return 0; +} + +/* + * Note the signature data + */ +int pkcs7_sig_note_signature(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct pkcs7_parse_context *ctx = context; + + ctx->sinfo->sig->s = kmemdup(value, vlen, GFP_KERNEL); + if (!ctx->sinfo->sig->s) + return -ENOMEM; + + ctx->sinfo->sig->s_size = vlen; + return 0; +} + +/* + * Note a signature information block + */ +int pkcs7_note_signed_info(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct pkcs7_parse_context *ctx = context; + struct pkcs7_signed_info *sinfo = ctx->sinfo; + struct asymmetric_key_id *kid; + + if (ctx->msg->data_type == OID_msIndirectData && !sinfo->authattrs) { + pr_warn("Authenticode requires AuthAttrs\n"); + return -EBADMSG; + } + + /* Generate cert issuer + serial number key ID */ + if (!ctx->expect_skid) { + kid = asymmetric_key_generate_id(ctx->raw_serial, + ctx->raw_serial_size, + ctx->raw_issuer, + ctx->raw_issuer_size); + } else { + kid = asymmetric_key_generate_id(ctx->raw_skid, + ctx->raw_skid_size, + "", 0); + } + if (IS_ERR(kid)) + return PTR_ERR(kid); + + pr_devel("SINFO KID: %u [%*phN]\n", kid->len, kid->len, kid->data); + + 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; +} diff --git a/lib/crypto/pkcs7_parser.h b/lib/crypto/pkcs7_parser.h new file mode 100644 index 00000000000..6565fdc2d4c --- /dev/null +++ b/lib/crypto/pkcs7_parser.h @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* PKCS#7 crypto data parser internal definitions + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#include +#include +#include "x509_parser.h" + +#define kenter(FMT, ...) \ + pr_devel("==> %s("FMT")\n", __func__, ##__VA_ARGS__) +#define kleave(FMT, ...) \ + pr_devel("<== %s()"FMT"\n", __func__, ##__VA_ARGS__) + +struct pkcs7_signed_info { + struct pkcs7_signed_info *next; + struct x509_certificate *signer; /* Signing certificate (in msg->certs) */ + unsigned index; + bool unsupported_crypto; /* T if not usable due to missing crypto */ + bool blacklisted; + + /* Message digest - the digest of the Content Data (or NULL) */ + const void *msgdigest; + unsigned msgdigest_len; + + /* Authenticated Attribute data (or NULL) */ + unsigned authattrs_len; + const void *authattrs; + unsigned long aa_set; +#define sinfo_has_content_type 0 +#define sinfo_has_signing_time 1 +#define sinfo_has_message_digest 2 +#define sinfo_has_smime_caps 3 +#define sinfo_has_ms_opus_info 4 +#define sinfo_has_ms_statement_type 5 + time64_t signing_time; + + /* 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 pkcs7_message { + struct x509_certificate *certs; /* Certificate list */ + struct x509_certificate *crl; /* Revocation list */ + struct pkcs7_signed_info *signed_infos; + u8 version; /* Version of cert (1 -> PKCS#7 or CMS; 3 -> CMS) */ + bool have_authattrs; /* T if have authattrs */ + + /* Content Data (or NULL) */ + enum OID data_type; /* Type of Data */ + size_t data_len; /* Length of Data */ + size_t data_hdrlen; /* Length of Data ASN.1 header */ + const void *data; /* Content Data (or 0) */ +}; diff --git a/scripts/Makefile.build b/scripts/Makefile.build index 6d59ea91fac..26eb701f8de 100644 --- a/scripts/Makefile.build +++ b/scripts/Makefile.build @@ -419,11 +419,9 @@ targets += $(multi-used-y) $(multi-used-m) intermediate_targets = $(foreach sfx, $(2), \ $(patsubst %$(strip $(1)),%$(sfx), \ $(filter %$(strip $(1)), $(targets)))) -# %.asn1.o <- %.asn1.[ch] <- %.asn1 # %.lex.o <- %.lex.c <- %.l # %.tab.o <- %.tab.[ch] <- %.y -targets += $(call intermediate_targets, .asn1.o, .asn1.c .asn1.h) \ - $(call intermediate_targets, .lex.o, .lex.c) \ +targets += $(call intermediate_targets, .lex.o, .lex.c) \ $(call intermediate_targets, .tab.o, .tab.c .tab.h) # Descending -- cgit v1.3.1