From patchwork Tue Jan 11 18:03:05 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Roberto Sassu X-Patchwork-Id: 531224 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id C2552C433F5 for ; Tue, 11 Jan 2022 18:04:03 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1345350AbiAKSEB (ORCPT ); Tue, 11 Jan 2022 13:04:01 -0500 Received: from frasgout.his.huawei.com ([185.176.79.56]:4389 "EHLO frasgout.his.huawei.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1345158AbiAKSDv (ORCPT ); Tue, 11 Jan 2022 13:03:51 -0500 Received: from fraeml714-chm.china.huawei.com (unknown [172.18.147.200]) by frasgout.his.huawei.com (SkyGuard) with ESMTP id 4JYJPq0CrVz67cQc; Wed, 12 Jan 2022 02:00:15 +0800 (CST) Received: from roberto-ThinkStation-P620.huawei.com (10.204.63.22) by fraeml714-chm.china.huawei.com (10.206.15.33) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2308.20; Tue, 11 Jan 2022 19:03:47 +0100 From: Roberto Sassu To: , , , CC: , , , , , , , Roberto Sassu Subject: [PATCH 01/14] mpi: Introduce mpi_key_length() Date: Tue, 11 Jan 2022 19:03:05 +0100 Message-ID: <20220111180318.591029-2-roberto.sassu@huawei.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20220111180318.591029-1-roberto.sassu@huawei.com> References: <20220111180318.591029-1-roberto.sassu@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.204.63.22] X-ClientProxiedBy: lhreml754-chm.china.huawei.com (10.201.108.204) To fraeml714-chm.china.huawei.com (10.206.15.33) X-CFilter-Loop: Reflected Precedence: bulk List-ID: X-Mailing-List: linux-crypto@vger.kernel.org Introduce the new function to get the number of bits and bytes from an MPI. Signed-off-by: Roberto Sassu --- include/linux/mpi.h | 2 ++ lib/mpi/mpicoder.c | 33 ++++++++++++++++++++++++++------- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/include/linux/mpi.h b/include/linux/mpi.h index eb0d1c1db208..a7dd4c9d8120 100644 --- a/include/linux/mpi.h +++ b/include/linux/mpi.h @@ -90,6 +90,8 @@ enum gcry_mpi_format { }; MPI mpi_read_raw_data(const void *xbuffer, size_t nbytes); +int mpi_key_length(const void *xbuffer, unsigned int ret_nread, + unsigned int *nbits_arg, unsigned int *nbytes_arg); MPI mpi_read_from_buffer(const void *buffer, unsigned *ret_nread); int mpi_fromstr(MPI val, const char *str); MPI mpi_scanval(const char *string); diff --git a/lib/mpi/mpicoder.c b/lib/mpi/mpicoder.c index 39c4c6731094..cd10ae9968b7 100644 --- a/lib/mpi/mpicoder.c +++ b/lib/mpi/mpicoder.c @@ -79,22 +79,41 @@ MPI mpi_read_raw_data(const void *xbuffer, size_t nbytes) } EXPORT_SYMBOL_GPL(mpi_read_raw_data); -MPI mpi_read_from_buffer(const void *xbuffer, unsigned *ret_nread) +int mpi_key_length(const void *xbuffer, unsigned int ret_nread, + unsigned int *nbits_arg, unsigned int *nbytes_arg) { const uint8_t *buffer = xbuffer; - unsigned int nbits, nbytes; - MPI val; + unsigned int nbits; - if (*ret_nread < 2) - return ERR_PTR(-EINVAL); + if (ret_nread < 2) + return -EINVAL; nbits = buffer[0] << 8 | buffer[1]; if (nbits > MAX_EXTERN_MPI_BITS) { pr_info("MPI: mpi too large (%u bits)\n", nbits); - return ERR_PTR(-EINVAL); + return -EINVAL; } - nbytes = DIV_ROUND_UP(nbits, 8); + if (nbits_arg) + *nbits_arg = nbits; + if (nbytes_arg) + *nbytes_arg = DIV_ROUND_UP(nbits, 8); + + return 0; +} +EXPORT_SYMBOL_GPL(mpi_key_length); + +MPI mpi_read_from_buffer(const void *xbuffer, unsigned int *ret_nread) +{ + const uint8_t *buffer = xbuffer; + unsigned int nbytes; + MPI val; + int ret; + + ret = mpi_key_length(xbuffer, *ret_nread, NULL, &nbytes); + if (ret < 0) + return ERR_PTR(ret); + if (nbytes + 2 > *ret_nread) { pr_info("MPI: mpi larger than buffer nbytes=%u ret_nread=%u\n", nbytes, *ret_nread); From patchwork Tue Jan 11 18:03:06 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Roberto Sassu X-Patchwork-Id: 531223 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id C51B9C433FE for ; Tue, 11 Jan 2022 18:04:05 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1350042AbiAKSEC (ORCPT ); Tue, 11 Jan 2022 13:04:02 -0500 Received: from frasgout.his.huawei.com ([185.176.79.56]:4390 "EHLO frasgout.his.huawei.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1345415AbiAKSDv (ORCPT ); Tue, 11 Jan 2022 13:03:51 -0500 Received: from fraeml714-chm.china.huawei.com (unknown [172.18.147.226]) by frasgout.his.huawei.com (SkyGuard) with ESMTP id 4JYJPq5XqSz67k5K; Wed, 12 Jan 2022 02:00:15 +0800 (CST) Received: from roberto-ThinkStation-P620.huawei.com (10.204.63.22) by fraeml714-chm.china.huawei.com (10.206.15.33) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2308.20; Tue, 11 Jan 2022 19:03:48 +0100 From: Roberto Sassu To: , , , CC: , , , , , , , Roberto Sassu Subject: [PATCH 02/14] rsa: add parser of raw format Date: Tue, 11 Jan 2022 19:03:06 +0100 Message-ID: <20220111180318.591029-3-roberto.sassu@huawei.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20220111180318.591029-1-roberto.sassu@huawei.com> References: <20220111180318.591029-1-roberto.sassu@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.204.63.22] X-ClientProxiedBy: lhreml754-chm.china.huawei.com (10.201.108.204) To fraeml714-chm.china.huawei.com (10.206.15.33) X-CFilter-Loop: Reflected Precedence: bulk List-ID: X-Mailing-List: linux-crypto@vger.kernel.org Parse the RSA key with RAW format if the ASN.1 parser returns an error. Signed-off-by: Roberto Sassu --- crypto/rsa.c | 14 +++++-- crypto/rsa_helper.c | 69 +++++++++++++++++++++++++++++++++++ include/crypto/internal/rsa.h | 6 +++ 3 files changed, 85 insertions(+), 4 deletions(-) diff --git a/crypto/rsa.c b/crypto/rsa.c index 4cdbec95d077..ece7bafd6984 100644 --- a/crypto/rsa.c +++ b/crypto/rsa.c @@ -164,8 +164,11 @@ static int rsa_set_pub_key(struct crypto_akcipher *tfm, const void *key, rsa_free_mpi_key(mpi_key); ret = rsa_parse_pub_key(&raw_key, key, keylen); - if (ret) - return ret; + if (ret) { + ret = rsa_parse_pub_key_raw(&raw_key, key, keylen); + if (ret) + return ret; + } mpi_key->e = mpi_read_raw_data(raw_key.e, raw_key.e_sz); if (!mpi_key->e) @@ -198,8 +201,11 @@ static int rsa_set_priv_key(struct crypto_akcipher *tfm, const void *key, rsa_free_mpi_key(mpi_key); ret = rsa_parse_priv_key(&raw_key, key, keylen); - if (ret) - return ret; + if (ret) { + ret = rsa_parse_priv_key_raw(&raw_key, key, keylen); + if (ret) + return ret; + } mpi_key->d = mpi_read_raw_data(raw_key.d, raw_key.d_sz); if (!mpi_key->d) diff --git a/crypto/rsa_helper.c b/crypto/rsa_helper.c index 94266f29049c..fb9443df8f0b 100644 --- a/crypto/rsa_helper.c +++ b/crypto/rsa_helper.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include "rsapubkey.asn1.h" #include "rsaprivkey.asn1.h" @@ -148,6 +149,32 @@ int rsa_get_qinv(void *context, size_t hdrlen, unsigned char tag, return 0; } +typedef int (*rsa_get_func)(void *, size_t, unsigned char, + const void *, size_t); + +static int rsa_parse_key_raw(struct rsa_key *rsa_key, + const void *key, unsigned int key_len, + rsa_get_func *func, int n_func) +{ + unsigned int nbytes, len = key_len; + const void *key_ptr = key; + int ret, i; + + for (i = 0; i < n_func; i++) { + ret = mpi_key_length(key_ptr, len, NULL, &nbytes); + if (ret < 0) + return ret; + + ret = func[i](rsa_key, 0, 0, key_ptr + 2, nbytes); + if (ret < 0) + return ret; + + key_ptr += nbytes + 2; + } + + return (key_ptr == key + key_len) ? 0 : -EINVAL; +} + /** * rsa_parse_pub_key() - decodes the BER encoded buffer and stores in the * provided struct rsa_key, pointers to the raw key as is, @@ -166,6 +193,27 @@ int rsa_parse_pub_key(struct rsa_key *rsa_key, const void *key, } EXPORT_SYMBOL_GPL(rsa_parse_pub_key); +/** + * rsa_parse_pub_key_raw() - parse the RAW key and store 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 RAW format + * @key_len: length of key + * + * Return: 0 on success or error code in case of error + */ +int rsa_parse_pub_key_raw(struct rsa_key *rsa_key, const void *key, + unsigned int key_len) +{ + rsa_get_func pub_func[] = {rsa_get_n, rsa_get_e}; + + return rsa_parse_key_raw(rsa_key, key, key_len, + pub_func, ARRAY_SIZE(pub_func)); +} +EXPORT_SYMBOL_GPL(rsa_parse_pub_key_raw); + /** * rsa_parse_priv_key() - decodes the BER encoded buffer and stores in the * provided struct rsa_key, pointers to the raw key @@ -184,3 +232,24 @@ int rsa_parse_priv_key(struct rsa_key *rsa_key, const void *key, return asn1_ber_decoder(&rsaprivkey_decoder, rsa_key, key, key_len); } EXPORT_SYMBOL_GPL(rsa_parse_priv_key); + +/** + * rsa_parse_priv_key_raw() - parse the RAW key and store 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 RAW format + * @key_len: length of key + * + * Return: 0 on success or error code in case of error + */ +int rsa_parse_priv_key_raw(struct rsa_key *rsa_key, const void *key, + unsigned int key_len) +{ + rsa_get_func priv_func[] = {rsa_get_n, rsa_get_e, rsa_get_d}; + + return rsa_parse_key_raw(rsa_key, key, key_len, + priv_func, ARRAY_SIZE(priv_func)); +} +EXPORT_SYMBOL_GPL(rsa_parse_priv_key_raw); diff --git a/include/crypto/internal/rsa.h b/include/crypto/internal/rsa.h index e870133f4b77..7141e806ceea 100644 --- a/include/crypto/internal/rsa.h +++ b/include/crypto/internal/rsa.h @@ -50,8 +50,14 @@ struct rsa_key { int rsa_parse_pub_key(struct rsa_key *rsa_key, const void *key, unsigned int key_len); +int rsa_parse_pub_key_raw(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); +int rsa_parse_priv_key_raw(struct rsa_key *rsa_key, const void *key, + unsigned int key_len); + extern struct crypto_template rsa_pkcs1pad_tmpl; #endif From patchwork Tue Jan 11 18:03:07 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Roberto Sassu X-Patchwork-Id: 531221 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 7A477C433EF for ; Tue, 11 Jan 2022 18:04:53 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S242564AbiAKSEu (ORCPT ); Tue, 11 Jan 2022 13:04:50 -0500 Received: from frasgout.his.huawei.com ([185.176.79.56]:4391 "EHLO frasgout.his.huawei.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1345189AbiAKSDw (ORCPT ); Tue, 11 Jan 2022 13:03:52 -0500 Received: from fraeml714-chm.china.huawei.com (unknown [172.18.147.201]) by frasgout.his.huawei.com (SkyGuard) with ESMTP id 4JYJPr45rbz67wXw; Wed, 12 Jan 2022 02:00:16 +0800 (CST) Received: from roberto-ThinkStation-P620.huawei.com (10.204.63.22) by fraeml714-chm.china.huawei.com (10.206.15.33) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2308.20; Tue, 11 Jan 2022 19:03:49 +0100 From: Roberto Sassu To: , , , CC: , , , , , , , Roberto Sassu Subject: [PATCH 03/14] PGPLIB: PGP definitions (RFC 4880) Date: Tue, 11 Jan 2022 19:03:07 +0100 Message-ID: <20220111180318.591029-4-roberto.sassu@huawei.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20220111180318.591029-1-roberto.sassu@huawei.com> References: <20220111180318.591029-1-roberto.sassu@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.204.63.22] X-ClientProxiedBy: lhreml754-chm.china.huawei.com (10.201.108.204) To fraeml714-chm.china.huawei.com (10.206.15.33) X-CFilter-Loop: Reflected Precedence: bulk List-ID: X-Mailing-List: linux-crypto@vger.kernel.org From: David Howells Provide some useful PGP definitions from RFC 4880. These describe details of public key crypto as used by crypto keys for things like signature verification. Signed-off-by: David Howells Co-developed-by: Roberto Sassu Signed-off-by: Roberto Sassu --- crypto/asymmetric_keys/pgp.h | 206 +++++++++++++++++++++++++++++++++++ 1 file changed, 206 insertions(+) create mode 100644 crypto/asymmetric_keys/pgp.h diff --git a/crypto/asymmetric_keys/pgp.h b/crypto/asymmetric_keys/pgp.h new file mode 100644 index 000000000000..5eb4f4222090 --- /dev/null +++ b/crypto/asymmetric_keys/pgp.h @@ -0,0 +1,206 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* PGP definitions (RFC 4880) + * + * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#include + +struct pgp_key_ID { + u8 id[8]; +} __packed; + +struct pgp_time { + u8 time[4]; +} __packed; + +/* + * PGP public-key algorithm identifiers [RFC4880: 9.1] + */ +enum pgp_pubkey_algo { + PGP_PUBKEY_RSA_ENC_OR_SIG = 1, + PGP_PUBKEY_RSA_ENC_ONLY = 2, + PGP_PUBKEY_RSA_SIG_ONLY = 3, + PGP_PUBKEY_ELGAMAL = 16, + PGP_PUBKEY_DSA = 17, + PGP_PUBKEY__LAST +}; + +/* + * PGP symmetric-key algorithm identifiers [RFC4880: 9.2] + */ +enum pgp_symkey_algo { + PGP_SYMKEY_PLAINTEXT = 0, + PGP_SYMKEY_IDEA = 1, + PGP_SYMKEY_3DES = 2, + PGP_SYMKEY_CAST5 = 3, + PGP_SYMKEY_BLOWFISH = 4, + PGP_SYMKEY_AES_128KEY = 7, + PGP_SYMKEY_AES_192KEY = 8, + PGP_SYMKEY_AES_256KEY = 9, + PGP_SYMKEY_TWOFISH_256KEY = 10, +}; + +/* + * PGP compression algorithm identifiers [RFC4880: 9.3] + */ +enum pgp_compr_algo { + PGP_COMPR_UNCOMPRESSED = 0, + PGP_COMPR_ZIP = 1, + PGP_COMPR_ZLIB = 2, + PGP_COMPR_BZIP2 = 3, +}; + +/* + * PGP hash algorithm identifiers [RFC4880: 9.4] + */ +enum pgp_hash_algo { + PGP_HASH_MD5 = 1, + PGP_HASH_SHA1 = 2, + PGP_HASH_RIPE_MD_160 = 3, + PGP_HASH_SHA256 = 8, + PGP_HASH_SHA384 = 9, + PGP_HASH_SHA512 = 10, + PGP_HASH_SHA224 = 11, + PGP_HASH__LAST +}; + +extern const char *const pgp_hash_algorithms[PGP_HASH__LAST]; + +/* + * PGP packet type tags [RFC4880: 4.3]. + */ +enum pgp_packet_tag { + PGP_PKT_RESERVED = 0, + PGP_PKT_PUBKEY_ENC_SESSION_KEY = 1, + PGP_PKT_SIGNATURE = 2, + PGP_PKT_SYMKEY_ENC_SESSION_KEY = 3, + PGP_PKT_ONEPASS_SIGNATURE = 4, + PGP_PKT_SECRET_KEY = 5, + PGP_PKT_PUBLIC_KEY = 6, + PGP_PKT_SECRET_SUBKEY = 7, + PGP_PKT_COMPRESSED_DATA = 8, + PGP_PKT_SYM_ENC_DATA = 9, + PGP_PKT_MARKER = 10, + PGP_PKT_LITERAL_DATA = 11, + PGP_PKT_TRUST = 12, + PGP_PKT_USER_ID = 13, + PGP_PKT_PUBLIC_SUBKEY = 14, + PGP_PKT_USER_ATTRIBUTE = 17, + PGP_PKT_SYM_ENC_AND_INTEG_DATA = 18, + PGP_PKT_MODIFY_DETECT_CODE = 19, + PGP_PKT_PRIVATE_0 = 60, + PGP_PKT_PRIVATE_3 = 63, + PGP_PKT__HIGHEST = 63 +}; + +/* + * Signature (tag 2) packet [RFC4880: 5.2]. + */ +enum pgp_signature_version { + PGP_SIG_VERSION_3 = 3, + PGP_SIG_VERSION_4 = 4, +}; + +enum pgp_signature_type { + PGP_SIG_BINARY_DOCUMENT_SIG = 0x00, + PGP_SIG_CANONICAL_TEXT_DOCUMENT_SIG = 0x01, + PGP_SIG_STANDALONE_SIG = 0x02, + PGP_SIG_GENERAL_CERT_OF_UID_PUBKEY = 0x10, + PGP_SIG_PERSONAL_CERT_OF_UID_PUBKEY = 0x11, + PGP_SIG_CASUAL_CERT_OF_UID_PUBKEY = 0x12, + PGP_SIG_POSTITIVE_CERT_OF_UID_PUBKEY = 0x13, + PGP_SIG_SUBKEY_BINDING_SIG = 0x18, + PGP_SIG_PRIMARY_KEY_BINDING_SIG = 0x19, + PGP_SIG_DIRECTLY_ON_KEY = 0x1F, + PGP_SIG_KEY_REVOCATION_SIG = 0x20, + PGP_SIG_SUBKEY_REVOCATION_SIG = 0x28, + PGP_SIG_CERT_REVOCATION_SIG = 0x30, + PGP_SIG_TIMESTAMP_SIG = 0x40, + PGP_SIG_THIRD_PARTY_CONFIRM_SIG = 0x50, +}; + +struct pgp_signature_v3_packet { + enum pgp_signature_version version : 8; /* == PGP_SIG_VERSION_3 */ + u8 length_of_hashed; /* == 5 */ + struct { + enum pgp_signature_type signature_type : 8; + struct pgp_time creation_time; + } __packed hashed; + struct pgp_key_ID issuer; + enum pgp_pubkey_algo pubkey_algo : 8; + enum pgp_hash_algo hash_algo : 8; +} __packed; + +struct pgp_signature_v4_packet { + enum pgp_signature_version version : 8; /* == PGP_SIG_VERSION_4 */ + enum pgp_signature_type signature_type : 8; + enum pgp_pubkey_algo pubkey_algo : 8; + enum pgp_hash_algo hash_algo : 8; +} __packed; + +/* + * V4 signature subpacket types [RFC4880: 5.2.3.1]. + */ +enum pgp_sig_subpkt_type { + PGP_SIG_CREATION_TIME = 2, + PGP_SIG_EXPIRATION_TIME = 3, + PGP_SIG_EXPORTABLE_CERT = 4, + PGP_SIG_TRUST_SIG = 5, + PGP_SIG_REGEXP = 6, + PGP_SIG_REVOCABLE = 7, + PGP_SIG_KEY_EXPIRATION_TIME = 9, + PGP_SIG_PREF_SYM_ALGO = 11, + PGP_SIG_REVOCATION_KEY = 12, + PGP_SIG_ISSUER = 16, + PGP_SIG_NOTATION_DATA = 20, + PGP_SIG_PREF_HASH_ALGO = 21, + PGP_SIG_PREF_COMPR_ALGO = 22, + PGP_SIG_KEY_SERVER_PREFS = 23, + PGP_SIG_PREF_KEY_SERVER = 24, + PGP_SIG_PRIMARY_USER_ID = 25, + PGP_SIG_POLICY_URI = 26, + PGP_SIG_KEY_FLAGS = 27, + PGP_SIG_SIGNERS_USER_ID = 28, + PGP_SIG_REASON_FOR_REVOCATION = 29, + PGP_SIG_FEATURES = 30, + PGP_SIG_TARGET = 31, + PGP_SIG_EMBEDDED_SIG = 32, + PGP_SIG__LAST +}; + +#define PGP_SIG_SUBPKT_TYPE_CRITICAL_MASK 0x80 + +/* + * Key (tag 5, 6, 7 and 14) packet + */ +enum pgp_key_version { + PGP_KEY_VERSION_2 = 2, + PGP_KEY_VERSION_3 = 3, + PGP_KEY_VERSION_4 = 4, +}; + +struct pgp_key_v3_packet { + enum pgp_key_version version : 8; + struct pgp_time creation_time; + u8 expiry[2]; /* 0 or time in days till expiry */ + enum pgp_pubkey_algo pubkey_algo : 8; + u8 key_material[0]; +} __packed; + +struct pgp_key_v4_packet { + enum pgp_key_version version : 8; + struct pgp_time creation_time; + enum pgp_pubkey_algo pubkey_algo : 8; + u8 key_material[0]; +} __packed; + +/* + * Literal Data (tag 11) packet + */ +enum pgp_literal_data_format { + PGP_LIT_FORMAT_BINARY = 0x62, + PGP_LIT_FORMAT_TEXT = 0x74, + PGP_LIT_FORMAT_TEXT_UTF8 = 0x75, +}; From patchwork Tue Jan 11 18:03:11 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Roberto Sassu X-Patchwork-Id: 531220 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 3873BC4321E for ; Tue, 11 Jan 2022 18:04:54 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S242171AbiAKSEw (ORCPT ); Tue, 11 Jan 2022 13:04:52 -0500 Received: from frasgout.his.huawei.com ([185.176.79.56]:4395 "EHLO frasgout.his.huawei.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1345494AbiAKSDz (ORCPT ); Tue, 11 Jan 2022 13:03:55 -0500 Received: from fraeml714-chm.china.huawei.com (unknown [172.18.147.206]) by frasgout.his.huawei.com (SkyGuard) with ESMTP id 4JYJQp5x6Vz683S6; Wed, 12 Jan 2022 02:01:06 +0800 (CST) Received: from roberto-ThinkStation-P620.huawei.com (10.204.63.22) by fraeml714-chm.china.huawei.com (10.206.15.33) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2308.20; Tue, 11 Jan 2022 19:03:52 +0100 From: Roberto Sassu To: , , , CC: , , , , , , , Roberto Sassu Subject: [PATCH 07/14] KEYS: Provide PGP key description autogeneration Date: Tue, 11 Jan 2022 19:03:11 +0100 Message-ID: <20220111180318.591029-8-roberto.sassu@huawei.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20220111180318.591029-1-roberto.sassu@huawei.com> References: <20220111180318.591029-1-roberto.sassu@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.204.63.22] X-ClientProxiedBy: lhreml754-chm.china.huawei.com (10.201.108.204) To fraeml714-chm.china.huawei.com (10.206.15.33) X-CFilter-Loop: Reflected Precedence: bulk List-ID: X-Mailing-List: linux-crypto@vger.kernel.org From: David Howells Provide a facility to autogenerate the name of PGP keys from the contents of the payload. If add_key() is given a blank description, a description is constructed from the last user ID packet in the payload data plus the last 8 hex digits of the key ID. For instance: keyctl padd asymmetric "" @s Co-developed-by: Roberto Sassu Signed-off-by: Roberto Sassu --- crypto/asymmetric_keys/pgp_public_key.c | 47 ++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/crypto/asymmetric_keys/pgp_public_key.c b/crypto/asymmetric_keys/pgp_public_key.c index 2763a53ea378..378ed5ff0a3a 100644 --- a/crypto/asymmetric_keys/pgp_public_key.c +++ b/crypto/asymmetric_keys/pgp_public_key.c @@ -59,6 +59,8 @@ struct pgp_key_data_parse_context { struct public_key *pub; u8 raw_fingerprint[HASH_MAX_DIGESTSIZE]; size_t raw_fingerprint_len; + const char *user_id; + size_t user_id_len; }; /* @@ -210,6 +212,15 @@ static int pgp_process_public_key(struct pgp_parse_context *context, kenter(",%u,%u,,%zu", type, headerlen, datalen); + if (type == PGP_PKT_USER_ID) { + if (!ctx->user_id_len) { + ctx->user_id = data; + ctx->user_id_len = datalen; + } + kleave(" = 0 [user ID]"); + return 0; + } + if (ctx->raw_fingerprint_len) { kleave(" = -ENOKEY [already]"); return -EBADMSG; @@ -315,13 +326,47 @@ static int pgp_key_parse(struct key_preparsed_payload *prep) kenter(""); memset(&ctx, 0, sizeof(ctx)); - ctx.pgp.types_of_interest = (1 << PGP_PKT_PUBLIC_KEY); + ctx.pgp.types_of_interest = (1 << PGP_PKT_PUBLIC_KEY) | + (1 << PGP_PKT_USER_ID); ctx.pgp.process_packet = pgp_process_public_key; ret = pgp_parse_packets(prep->data, prep->datalen, &ctx.pgp); if (ret < 0) goto error; + if (ctx.user_id && ctx.user_id_len > 0) { + /* + * Propose a description for the key (user ID without the + * comment). + */ + size_t ulen = ctx.user_id_len; + const char *p; + + p = memchr(ctx.user_id, '(', ulen); + if (p) { + /* Remove the comment */ + do { + p--; + } while (*p == ' ' && p > ctx.user_id); + if (*p != ' ') + p++; + ulen = p - ctx.user_id; + } + + if (ulen > 255 - 9) + ulen = 255 - 9; + prep->description = kmalloc(ulen + 1 + 8 + 1, GFP_KERNEL); + ret = -ENOMEM; + if (!prep->description) + goto error; + memcpy(prep->description, ctx.user_id, ulen); + prep->description[ulen] = ' '; + bin2hex(prep->description + ulen + 1, + ctx.raw_fingerprint + ctx.raw_fingerprint_len - 4, 4); + prep->description[ulen + 9] = 0; + pr_debug("desc '%s'\n", prep->description); + } + /* We're pinning the module by being linked against it */ __module_get(public_key_subtype.owner); prep->payload.data[asym_subtype] = &public_key_subtype; From patchwork Tue Jan 11 18:03:12 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Roberto Sassu X-Patchwork-Id: 531218 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 8316AC4321E for ; Tue, 11 Jan 2022 18:06:31 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1344957AbiAKSGa (ORCPT ); Tue, 11 Jan 2022 13:06:30 -0500 Received: from frasgout.his.huawei.com ([185.176.79.56]:4396 "EHLO frasgout.his.huawei.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1345192AbiAKSD4 (ORCPT ); Tue, 11 Jan 2022 13:03:56 -0500 Received: from fraeml714-chm.china.huawei.com (unknown [172.18.147.226]) by frasgout.his.huawei.com (SkyGuard) with ESMTP id 4JYJTz1xTVz682DK; Wed, 12 Jan 2022 02:03:51 +0800 (CST) Received: from roberto-ThinkStation-P620.huawei.com (10.204.63.22) by fraeml714-chm.china.huawei.com (10.206.15.33) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2308.20; Tue, 11 Jan 2022 19:03:53 +0100 From: Roberto Sassu To: , , , CC: , , , , , , , Roberto Sassu Subject: [PATCH 08/14] KEYS: PGP-based public key signature verification Date: Tue, 11 Jan 2022 19:03:12 +0100 Message-ID: <20220111180318.591029-9-roberto.sassu@huawei.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20220111180318.591029-1-roberto.sassu@huawei.com> References: <20220111180318.591029-1-roberto.sassu@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.204.63.22] X-ClientProxiedBy: lhreml754-chm.china.huawei.com (10.201.108.204) To fraeml714-chm.china.huawei.com (10.206.15.33) X-CFilter-Loop: Reflected Precedence: bulk List-ID: X-Mailing-List: linux-crypto@vger.kernel.org From: David Howells Provide handlers for PGP-based public-key algorithm signature verification. This does most of the work involved in signature verification as most of it is public-key algorithm agnostic. The public-key verification algorithm itself is just the last little bit and is supplied the complete hash data to process. This requires glue logic putting on top to make use of it - something that the patch introducing verify_pgp_signature() provides. Signed-off-by: David Howells Co-developed-by: Roberto Sassu Signed-off-by: Roberto Sassu --- MAINTAINERS | 1 + crypto/asymmetric_keys/Makefile | 3 +- crypto/asymmetric_keys/pgp_signature.c | 507 +++++++++++++++++++++++++ include/crypto/pgp.h | 29 ++ 4 files changed, 539 insertions(+), 1 deletion(-) create mode 100644 crypto/asymmetric_keys/pgp_signature.c create mode 100644 include/crypto/pgp.h diff --git a/MAINTAINERS b/MAINTAINERS index 306de106f31b..844ed460da59 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3025,6 +3025,7 @@ L: keyrings@vger.kernel.org S: Maintained F: Documentation/crypto/asymmetric-keys.rst F: crypto/asymmetric_keys/ +F: include/crypto/pgp.h F: include/crypto/pkcs7.h F: include/crypto/public_key.h F: include/linux/verification.h diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile index a68f9a5d1746..e2aeb2a4b6a6 100644 --- a/crypto/asymmetric_keys/Makefile +++ b/crypto/asymmetric_keys/Makefile @@ -94,4 +94,5 @@ obj-$(CONFIG_PGP_LIBRARY) += pgp_library.o obj-$(CONFIG_PGP_KEY_PARSER) += pgp_key_parser.o pgp_key_parser-y := \ - pgp_public_key.o + pgp_public_key.o \ + pgp_signature.o diff --git a/crypto/asymmetric_keys/pgp_signature.c b/crypto/asymmetric_keys/pgp_signature.c new file mode 100644 index 000000000000..4aa71559bcf4 --- /dev/null +++ b/crypto/asymmetric_keys/pgp_signature.c @@ -0,0 +1,507 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* PGP public key signature verification [RFC 4880] + * + * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#define pr_fmt(fmt) "PGPSIG: "fmt +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pgp_parser.h" + +static const struct { + enum hash_algo algo : 8; +} pgp_pubkey_hash[PGP_HASH__LAST] = { + [PGP_HASH_MD5].algo = HASH_ALGO_MD5, + [PGP_HASH_SHA1].algo = HASH_ALGO_SHA1, + [PGP_HASH_RIPE_MD_160].algo = HASH_ALGO_RIPE_MD_160, + [PGP_HASH_SHA256].algo = HASH_ALGO_SHA256, + [PGP_HASH_SHA384].algo = HASH_ALGO_SHA384, + [PGP_HASH_SHA512].algo = HASH_ALGO_SHA512, + [PGP_HASH_SHA224].algo = HASH_ALGO_SHA224, +}; + +struct pgp_current_pkt { + enum pgp_packet_tag type; + u8 headerlen; + const u8 *data; + size_t datalen; +}; + +struct pgp_sig_verify { + enum pgp_signature_version sig_version : 8; + struct public_key_signature *sig; + u8 signed_hash_msw[2]; + struct shash_desc *hash; + struct pgp_current_pkt pkt; +}; + +/* + * Find a key in the given keyring by issuer and authority. + */ +static struct key *pgp_request_asymmetric_key(struct pgp_sig_verify *ctx, + struct key *keyring) +{ + struct key *key; + __be32 *issuer32; + char id[12]; + + issuer32 = (__be32 *)ctx->sig->auth_ids[0]->data; + snprintf(id, sizeof(id), "id:%08x%08x", be32_to_cpu(issuer32[0]), + be32_to_cpu(issuer32[1])); + + kenter(",,%s", id); + + pr_debug("Look up key: \"%s\"\n", id); + + key = find_asymmetric_key(keyring, ctx->sig->auth_ids[0], + ctx->sig->auth_ids[1], true); + if (IS_ERR(key)) { + pr_debug("Request for public key '%08x%08x' err %ld\n", + issuer32[0], issuer32[1], PTR_ERR(key)); + + switch (PTR_ERR(key)) { + /* Hide some search errors */ + case -EACCES: + case -ENOTDIR: + case -EAGAIN: + kleave(" = -ENOKEY"); + return ERR_PTR(-ENOKEY); + default: + kleave(" = %ld", PTR_ERR(key)); + return ERR_CAST(key); + } + } + + kleave(" = 0 [%x]", key_serial(key)); + return key; +} + +struct pgp_sig_parse_context { + struct pgp_parse_context pgp; + struct pgp_sig_parameters params; + struct pgp_current_pkt pkt; +}; + +static int pgp_parse_signature(struct pgp_parse_context *context, + enum pgp_packet_tag type, + u8 headerlen, + const u8 *data, + size_t datalen) +{ + struct pgp_sig_parse_context *ctx = + container_of(context, struct pgp_sig_parse_context, pgp); + struct pgp_sig_parameters tmp_params; + struct pgp_current_pkt tmp_pkt = { type, headerlen, data, datalen}; + int ret; + + ret = pgp_parse_sig_params(&data, &datalen, &tmp_params); + if (ret < 0) + return ret; + + if (tmp_params.signature_type != PGP_SIG_BINARY_DOCUMENT_SIG && + tmp_params.signature_type != PGP_SIG_STANDALONE_SIG && + tmp_params.signature_type != PGP_SIG_POSTITIVE_CERT_OF_UID_PUBKEY) + return 0; + + memcpy(&ctx->params, &tmp_params, sizeof(tmp_params)); + memcpy(&ctx->pkt, &tmp_pkt, sizeof(tmp_pkt)); + return 0; +} + +/** + * pgp_sig_parse - Begin the process of verifying a signature + * @sigdata: Signature blob + * @siglen: Length of signature blob + * + * This involves allocating the hash into which first the data and then the + * metadata will be put, and parsing the signature to get the issuer ID from + * which the key used to verify the signature will be searched. + * + * Return: a PGP sig context pointer on success, an error pointer on error + */ +struct pgp_sig_verify *pgp_sig_parse(const u8 *sigdata, size_t siglen) +{ + struct pgp_sig_parse_context p; + struct pgp_sig_verify *ctx; + struct crypto_shash *tfm; + const char *pkey_algo; + size_t digest_size, desc_size; + int ret; + + kenter(",,%zu", siglen); + + p.pgp.types_of_interest = (1 << PGP_PKT_SIGNATURE); + p.pgp.process_packet = pgp_parse_signature; + p.pkt.data = NULL; + ret = pgp_parse_packets(sigdata, siglen, &p.pgp); + if (ret < 0) { + kleave(" = ERR_PTR [bad pkt]"); + return ERR_PTR(ret); + } + + if (!p.pkt.data) { + kleave(" = ERR_PTR [no pkt]"); + return ERR_PTR(-ENOENT); + } + + /* Check the signature itself for usefulness */ + if (p.params.pubkey_algo >= PGP_PUBKEY__LAST) + goto unsupported_pkey_algo; + pkey_algo = pgp_to_public_key_algo[p.params.pubkey_algo]; + if (!pkey_algo) + goto unsupported_pkey_algo; + + if (p.params.hash_algo >= PGP_HASH__LAST || + !pgp_hash_algorithms[p.params.hash_algo]) { + pr_debug("Unsupported hash algorithm %u\n", + p.params.hash_algo); + kleave(" = -ENOPKG [unsupp hash algo]"); + return ERR_PTR(-ENOPKG); + } + + pr_debug("Signature generated with %s hash\n", + pgp_hash_algorithms[p.params.hash_algo]); + + /* Allocate the hashing algorithm we're going to need and find out how + * big the hash operational data will be. + */ + tfm = crypto_alloc_shash(pgp_hash_algorithms[p.params.hash_algo], 0, 0); + if (IS_ERR(tfm)) { + ret = (PTR_ERR(tfm) == -ENOENT ? -ENOPKG : PTR_ERR(tfm)); + kleave(" = %d", ret); + return ERR_PTR(ret); + } + + desc_size = crypto_shash_descsize(tfm); + digest_size = crypto_shash_digestsize(tfm); + + /* We allocate the hash operational data storage on the end of our + * context data. + */ + ctx = kzalloc(sizeof(*ctx) + sizeof(struct shash_desc) + desc_size, + GFP_KERNEL); + if (!ctx) { + ret = -ENOMEM; + goto error_have_shash; + } + + ctx->sig = kzalloc(sizeof(*ctx->sig), GFP_KERNEL); + if (!ctx->sig) { + ret = -ENOMEM; + goto error_have_ctx; + } + + ctx->sig->auth_ids[0] = asymmetric_key_generate_id(p.params.issuer.id, + sizeof(p.params.issuer.id), "", 0); + if (IS_ERR(ctx->sig->auth_ids[0])) { + ret = -ENOMEM; + goto error_have_ctx_sig; + } + + ctx->sig->encoding = "pkcs1"; + ctx->sig->pkey_algo = pkey_algo; + ctx->sig->hash_algo = pgp_hash_algorithms[p.params.hash_algo]; + ctx->sig->digest_size = digest_size; + ctx->hash = (struct shash_desc *)((void *)ctx + + sizeof(*ctx)); + ctx->hash->tfm = tfm; + ctx->sig_version = p.params.version; + + memcpy(&ctx->pkt, &p.pkt, sizeof(p.pkt)); + + ret = crypto_shash_init(ctx->hash); + if (ret < 0) + goto error_have_auth_ids; + + kleave(" = %p", ctx); + return ctx; + +error_have_auth_ids: + kfree(ctx->sig->auth_ids[0]); +error_have_ctx_sig: + kfree(ctx->sig); +error_have_ctx: + kfree(ctx); +error_have_shash: + crypto_free_shash(tfm); + kleave(" = %d", ret); + return ERR_PTR(ret); + +unsupported_pkey_algo: + pr_debug("Unsupported public key algorithm %u\n", + p.params.pubkey_algo); + kleave(" = -ENOPKG [unsupp pk algo]"); + return ERR_PTR(-ENOPKG); +} + +/* + * Load data into the hash + */ +int pgp_sig_add_data(struct pgp_sig_verify *ctx, const void *data, + size_t datalen) +{ + return crypto_shash_update(ctx->hash, data, datalen); +} + +/* + * Extract required metadata from the signature packet and add what we need to + * the hash; finalise the hash. + */ +static int pgp_digest_signature(struct pgp_sig_verify *ctx) +{ + enum pgp_signature_version version; + unsigned int nbytes, nbytes_alloc; + enum pgp_packet_tag type = ctx->pkt.type; + const u8 *data = ctx->pkt.data; + size_t datalen = ctx->pkt.datalen; + int ret; + + kenter(",%u,,%zu", type, datalen); + + if (ctx->sig->digest) { + kleave(" = 0 [digest found]"); + return 0; + } + + version = *data; + if (version == PGP_SIG_VERSION_3) { + /* We just include an excerpt of the metadata from a V3 + * signature. + */ + crypto_shash_update(ctx->hash, data + 2, 5); + data += sizeof(struct pgp_signature_v3_packet); + datalen -= sizeof(struct pgp_signature_v3_packet); + } else if (version == PGP_SIG_VERSION_4) { + /* We add the whole metadata header and some of the hashed data + * for a V4 signature, plus a trailer. + */ + size_t hashedsz, unhashedsz; + u8 trailer[6]; + + hashedsz = 4 + 2 + (data[4] << 8) + data[5]; + crypto_shash_update(ctx->hash, data, hashedsz); + + trailer[0] = version; + trailer[1] = 0xffU; + trailer[2] = hashedsz >> 24; + trailer[3] = hashedsz >> 16; + trailer[4] = hashedsz >> 8; + trailer[5] = hashedsz; + + crypto_shash_update(ctx->hash, trailer, 6); + data += hashedsz; + datalen -= hashedsz; + + unhashedsz = 2 + (data[0] << 8) + data[1]; + data += unhashedsz; + datalen -= unhashedsz; + } + + if (datalen <= 2) { + kleave(" = -EBADMSG"); + return -EBADMSG; + } + + /* There's a quick check on the hash available. */ + ctx->signed_hash_msw[0] = *data++; + ctx->signed_hash_msw[1] = *data++; + datalen -= 2; + + /* And then the cryptographic data, which we'll need for the + * algorithm. + */ + ret = mpi_key_length(data, datalen, NULL, &nbytes); + if (ret < 0) { + kleave(" = -EBADMSG [key length]"); + return ret; + } + + if (datalen != nbytes + 2) { + kleave(" = -EBADMSG [size mismatch]"); + return -EBADMSG; + } + + nbytes_alloc = DIV_ROUND_UP(nbytes, 8) * 8; + + ctx->sig->s = kzalloc(nbytes_alloc, GFP_KERNEL); + if (!ctx->sig->s) { + ret = -ENOMEM; + goto out; + } + + memcpy(ctx->sig->s + nbytes_alloc - nbytes, data + 2, nbytes); + ctx->sig->s_size = nbytes_alloc; + + ctx->sig->digest = kmalloc(ctx->sig->digest_size, GFP_KERNEL); + if (!ctx->sig->digest) { + ret = -ENOMEM; + goto out; + } + + ret = crypto_shash_final(ctx->hash, ctx->sig->digest); + if (ret < 0) + goto out; + + pr_debug("hash: %*phN\n", ctx->sig->digest_size, ctx->sig->digest); +out: + kleave(" = %d", ret); + return ret; +} + +/** + * pgp_sig_get_digest - Finalize digest calculation + * @ctx: PGP sig verification context to use + * @buf: Buffer digest is written to + * @len: Buffer length + * @hash_algo: Digest algorithm + * + * Copy the calculated digest, length and algorithm to the destinations provided + * by the caller. + * + * Return: 0 on success, a negative value on error + */ +int pgp_sig_get_digest(struct pgp_sig_verify *ctx, const u8 **buf, u32 *len, + enum hash_algo *hash_algo) +{ + int ret, i; + + kenter(""); + + ret = pgp_digest_signature(ctx); + if (ret < 0) + goto out; + + i = match_string(hash_algo_name, HASH_ALGO__LAST, + ctx->sig->hash_algo); + if (i < 0) { + ret = -ENOENT; + goto out; + } + + *hash_algo = i; + *buf = ctx->sig->digest; + *len = ctx->sig->digest_size; +out: + kleave(" = %d", ret); + return ret; +} + +/** + * pgp_sig_verify - Verify the PGP signature + * @ctx: PGP sig verification context to use + * @keyring: Keyring containing the key for signature verification + * + * Search the key to be used for signature verification, and verify the PGP + * signature. + * + * Return: 0 if the signature is valid, a negative value otherwise + */ +int pgp_sig_verify(struct pgp_sig_verify *ctx, struct key *keyring) +{ + const struct public_key *pub; + struct key *key; + int ret; + + kenter(""); + + ret = pgp_digest_signature(ctx); + if (ret < 0) + goto out; + + if (ctx->sig->digest[0] != ctx->signed_hash_msw[0] || + ctx->sig->digest[1] != ctx->signed_hash_msw[1]) { + pr_err("Hash (%02x%02x) mismatch against quick check (%02x%02x)\n", + ctx->sig->digest[0], ctx->sig->digest[1], + ctx->signed_hash_msw[0], ctx->signed_hash_msw[1]); + ret = -EKEYREJECTED; + goto out; + } + + /* Now we need to find a key to use */ + key = pgp_request_asymmetric_key(ctx, keyring); + if (IS_ERR(key)) { + ret = PTR_ERR(key); + goto out; + } + + pub = key->payload.data[asym_crypto]; + + if (strcmp(ctx->sig->pkey_algo, pub->pkey_algo)) { + ret = -EKEYREJECTED; + goto out_key; + } + + ret = verify_signature(key, ctx->sig); +out_key: + key_put(key); +out: + kleave(" = %d", ret); + return ret; +} + +/** + * pgp_sig_verify_cancel - End the PGP signature verification + * @ctx: PGP sig verification context to use + * @keep_sig: Don't deallocate the signature + * + * Free the memory used for the signature verification. + */ +void pgp_sig_verify_cancel(struct pgp_sig_verify *ctx, bool keep_sig) +{ + kenter(""); + + crypto_free_shash(ctx->hash->tfm); + if (!keep_sig) + public_key_signature_free(ctx->sig); + + kfree(ctx); + + kleave(""); +} + +/** + * pgp_sig_get_sig - Return the PGP signature + * @ctx: PGP sig verification context to use + * + * Finalize the signature by calculating the digest if not already done. Then, + * return the PGP signature to the caller. + * + * Return: the PGP signature if successfully finalized, an error pointer + * otherwise + */ +struct public_key_signature *pgp_sig_get_sig(struct pgp_sig_verify *ctx) +{ + int ret; + + ret = pgp_digest_signature(ctx); + if (ret < 0) + return ERR_PTR(-ENOENT); + + return ctx->sig; +} + +/** + * pgp_sig_get_version - Return the PGP signature version + * @ctx: PGP sig verification context to use + * + * Return the version of the PGP signature to the caller. + * + * Return: the PGP signature version + */ +u8 pgp_sig_get_version(struct pgp_sig_verify *ctx) +{ + return ctx->sig_version; +} diff --git a/include/crypto/pgp.h b/include/crypto/pgp.h new file mode 100644 index 000000000000..a58453843dc8 --- /dev/null +++ b/include/crypto/pgp.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* PGP signature processing + * + * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#ifndef _CRYPTO_PGP_H +#define _CRYPTO_PGP_H + +#include + +struct key; +struct pgp_sig_verify; + +/* + * pgp_signature.c + */ +extern struct pgp_sig_verify *pgp_sig_parse(const u8 *sigdata, size_t siglen); +extern int pgp_sig_add_data(struct pgp_sig_verify *ctx, + const void *data, size_t datalen); +extern int pgp_sig_get_digest(struct pgp_sig_verify *ctx, const u8 **buf, + u32 *len, enum hash_algo *hash_algo); +extern int pgp_sig_verify(struct pgp_sig_verify *ctx, struct key *keyring); +extern void pgp_sig_verify_cancel(struct pgp_sig_verify *ctx, bool keep_sig); +extern struct public_key_signature *pgp_sig_get_sig(struct pgp_sig_verify *ctx); +extern u8 pgp_sig_get_version(struct pgp_sig_verify *ctx); + +#endif /* _CRYPTO_PGP_H */ From patchwork Tue Jan 11 18:03:14 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Roberto Sassu X-Patchwork-Id: 531222 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 6B83FC4321E for ; Tue, 11 Jan 2022 18:04:14 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S245091AbiAKSEM (ORCPT ); Tue, 11 Jan 2022 13:04:12 -0500 Received: from frasgout.his.huawei.com ([185.176.79.56]:4398 "EHLO frasgout.his.huawei.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1345554AbiAKSD6 (ORCPT ); Tue, 11 Jan 2022 13:03:58 -0500 Received: from fraeml714-chm.china.huawei.com (unknown [172.18.147.207]) by frasgout.his.huawei.com (SkyGuard) with ESMTP id 4JYJQs1ypdz6842D; Wed, 12 Jan 2022 02:01:09 +0800 (CST) Received: from roberto-ThinkStation-P620.huawei.com (10.204.63.22) by fraeml714-chm.china.huawei.com (10.206.15.33) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2308.20; Tue, 11 Jan 2022 19:03:54 +0100 From: Roberto Sassu To: , , , CC: , , , , , , , Roberto Sassu Subject: [PATCH 10/14] KEYS: Calculate key digest and get signature of the key Date: Tue, 11 Jan 2022 19:03:14 +0100 Message-ID: <20220111180318.591029-11-roberto.sassu@huawei.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20220111180318.591029-1-roberto.sassu@huawei.com> References: <20220111180318.591029-1-roberto.sassu@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.204.63.22] X-ClientProxiedBy: lhreml754-chm.china.huawei.com (10.201.108.204) To fraeml714-chm.china.huawei.com (10.206.15.33) X-CFilter-Loop: Reflected Precedence: bulk List-ID: X-Mailing-List: linux-crypto@vger.kernel.org Calculate the digest of the signature, according to the RFC4880 section 5.2.4, get the last suitable signature with type 0x13 (Positive certification of a User ID and Public Key packet), and store it in the asym_auth field of the key payload, so that it is available for validating a restriction on a keyring. The rationale of taking the last signature is that, if there are multiple signatures, that would be of a different issuer (not a self-signature), that likely has more chances to be useful for the restriction verification. If there is one (the self-signature), that will be used. Signed-off-by: Roberto Sassu --- crypto/asymmetric_keys/pgp_public_key.c | 81 +++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/crypto/asymmetric_keys/pgp_public_key.c b/crypto/asymmetric_keys/pgp_public_key.c index 378ed5ff0a3a..cecd67bab4e6 100644 --- a/crypto/asymmetric_keys/pgp_public_key.c +++ b/crypto/asymmetric_keys/pgp_public_key.c @@ -14,6 +14,7 @@ #include #include #include +#include #include "pgp_parser.h" @@ -61,6 +62,8 @@ struct pgp_key_data_parse_context { size_t raw_fingerprint_len; const char *user_id; size_t user_id_len; + const char *key_pkt; + size_t key_pkt_len; }; /* @@ -226,6 +229,12 @@ static int pgp_process_public_key(struct pgp_parse_context *context, return -EBADMSG; } + /* Pointer refers to data being processed. */ + if (type == PGP_PKT_PUBLIC_KEY) { + ctx->key_pkt = data; + ctx->key_pkt_len = datalen; + } + pub = kzalloc(sizeof(struct public_key), GFP_KERNEL); if (!pub) return -ENOMEM; @@ -314,6 +323,77 @@ static struct asymmetric_key_ids *pgp_key_generate_id( return NULL; } +/* + * Calculate the digest of the signature according to the RFC4880, section + * 5.2.4 (packet type 0x13). + */ +static int pgp_key_add_sig_data(struct pgp_key_data_parse_context *ctx, + struct pgp_sig_verify *sig_ctx) +{ + loff_t offset = 0; + u8 *data; + + if (!ctx->key_pkt_len || !ctx->user_id_len) + return 0; + + /* 0x99 + key pkt len + key pkt + 0xb4 + user ID len + user ID */ + data = kmalloc(1 + sizeof(u16) + ctx->key_pkt_len + + 1 + sizeof(u32) + ctx->user_id_len, GFP_KERNEL); + if (!data) + return -ENOMEM; + + data[offset++] = 0x99; + data[offset++] = ctx->key_pkt_len >> 8; + data[offset++] = ctx->key_pkt_len; + + memcpy(data + offset, ctx->key_pkt, ctx->key_pkt_len); + offset += ctx->key_pkt_len; + + if (pgp_sig_get_version(sig_ctx) == PGP_SIG_VERSION_4) { + data[offset++] = 0xb4; + data[offset++] = ctx->user_id_len >> 24; + data[offset++] = ctx->user_id_len >> 16; + data[offset++] = ctx->user_id_len >> 8; + data[offset++] = ctx->user_id_len; + } + + memcpy(data + offset, ctx->user_id, ctx->user_id_len); + offset += ctx->user_id_len; + + pgp_sig_add_data(sig_ctx, data, offset); + kfree(data); + return 0; +} + +static struct public_key_signature *pgp_key_get_sig( + struct key_preparsed_payload *prep, + struct pgp_key_data_parse_context *ctx) +{ + struct public_key_signature *sig = NULL; + struct pgp_sig_verify *sig_ctx; + bool keep_sig = false; + int ret; + + sig_ctx = pgp_sig_parse(prep->data, prep->datalen); + if (IS_ERR(sig_ctx)) + return NULL; + + ret = pgp_key_add_sig_data(ctx, sig_ctx); + if (ret < 0) + goto out; + + sig = pgp_sig_get_sig(sig_ctx); + if (IS_ERR(sig)) { + sig = NULL; + goto out; + } + + keep_sig = true; +out: + pgp_sig_verify_cancel(sig_ctx, keep_sig); + return sig; +} + /* * Attempt to parse the instantiation data blob for a key as a PGP packet * message holding a key. @@ -372,6 +452,7 @@ static int pgp_key_parse(struct key_preparsed_payload *prep) prep->payload.data[asym_subtype] = &public_key_subtype; prep->payload.data[asym_key_ids] = pgp_key_generate_id(&ctx); prep->payload.data[asym_crypto] = ctx.pub; + prep->payload.data[asym_auth] = pgp_key_get_sig(prep, &ctx); prep->quotalen = 100; return 0; From patchwork Tue Jan 11 18:03:15 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Roberto Sassu X-Patchwork-Id: 531219 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 64843C433FE for ; Tue, 11 Jan 2022 18:04:59 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1345549AbiAKSEy (ORCPT ); Tue, 11 Jan 2022 13:04:54 -0500 Received: from frasgout.his.huawei.com ([185.176.79.56]:4401 "EHLO frasgout.his.huawei.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1349792AbiAKSEB (ORCPT ); Tue, 11 Jan 2022 13:04:01 -0500 Received: from fraeml714-chm.china.huawei.com (unknown [172.18.147.206]) by frasgout.his.huawei.com (SkyGuard) with ESMTP id 4JYJPz0y9yz688jd; Wed, 12 Jan 2022 02:00:23 +0800 (CST) Received: from roberto-ThinkStation-P620.huawei.com (10.204.63.22) by fraeml714-chm.china.huawei.com (10.206.15.33) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2308.20; Tue, 11 Jan 2022 19:03:55 +0100 From: Roberto Sassu To: , , , CC: , , , , , , , Roberto Sassu Subject: [PATCH 11/14] verification: introduce verify_pgp_signature() Date: Tue, 11 Jan 2022 19:03:15 +0100 Message-ID: <20220111180318.591029-12-roberto.sassu@huawei.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20220111180318.591029-1-roberto.sassu@huawei.com> References: <20220111180318.591029-1-roberto.sassu@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.204.63.22] X-ClientProxiedBy: lhreml754-chm.china.huawei.com (10.201.108.204) To fraeml714-chm.china.huawei.com (10.206.15.33) X-CFilter-Loop: Reflected Precedence: bulk List-ID: X-Mailing-List: linux-crypto@vger.kernel.org Introduce verify_pgp_signature() to verify PGP signatures from detached data. It will be used by fsverity and by IMA. Signed-off-by: Roberto Sassu --- certs/system_keyring.c | 70 ++++++++++++++++++++++++++++++++++++ include/linux/verification.h | 23 ++++++++++++ 2 files changed, 93 insertions(+) diff --git a/certs/system_keyring.c b/certs/system_keyring.c index 692365dee2bd..26a11b1dcd59 100644 --- a/certs/system_keyring.c +++ b/certs/system_keyring.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "common.h" static struct key *builtin_trusted_keys; @@ -289,6 +290,75 @@ int verify_pkcs7_signature(const void *data, size_t len, } EXPORT_SYMBOL_GPL(verify_pkcs7_signature); +#ifdef CONFIG_PGP_KEY_PARSER +/** + * verify_pgp_signature - Verify a PGP-based signature on system data. + * @data: The data to be verified (must be provided). + * @len: Size of @data. + * @raw_pgp: The PGP message that is the signature. + * @pgp_len: The size of @raw_pgp. + * @trusted_keys: Trusted keys to use (NULL for builtin trusted keys only, + * (void *)1UL for all trusted keys). + * @usage: The use to which the key is being put. + * @view_content: Callback to gain access to content. + * @ctx: Context for callback. + */ +int verify_pgp_signature(const void *data, size_t len, + const void *raw_pgp, size_t pgp_len, + struct key *trusted_keys, + enum key_being_used_for usage, + int (*view_content)(void *ctx, + const void *data, size_t len, + size_t asn1hdrlen), + void *ctx) +{ + struct pgp_sig_verify *pgp_ctx; + int ret; + + if (!data || !len) + return -EINVAL; + + pgp_ctx = pgp_sig_parse(raw_pgp, pgp_len); + if (IS_ERR(pgp_ctx)) + return PTR_ERR(pgp_ctx); + + if (!trusted_keys) { + trusted_keys = builtin_trusted_keys; + } else if (trusted_keys == VERIFY_USE_SECONDARY_KEYRING) { +#ifdef CONFIG_SECONDARY_TRUSTED_KEYRING + trusted_keys = secondary_trusted_keys; +#else + trusted_keys = builtin_trusted_keys; +#endif + } else if (trusted_keys == VERIFY_USE_PLATFORM_KEYRING) { +#ifdef CONFIG_INTEGRITY_PLATFORM_KEYRING + trusted_keys = platform_trusted_keys; +#else + trusted_keys = NULL; +#endif + if (!trusted_keys) { + ret = -ENOKEY; + pr_devel("PGP platform keyring is not available\n"); + goto error; + } + } + + /* The data should be detached - so we need to supply it. */ + if (pgp_sig_add_data(pgp_ctx, data, len)) { + pr_err("Failed to supply data for PGP signature\n"); + ret = -EBADMSG; + goto error; + } + + ret = pgp_sig_verify(pgp_ctx, trusted_keys); +error: + pgp_sig_verify_cancel(pgp_ctx, false); + pr_devel("<==%s() = %d\n", __func__, ret); + return ret; +} +EXPORT_SYMBOL_GPL(verify_pgp_signature); + +#endif /* CONFIG_PGP_KEY_PARSER */ #endif /* CONFIG_SYSTEM_DATA_VERIFICATION */ #ifdef CONFIG_INTEGRITY_PLATFORM_KEYRING diff --git a/include/linux/verification.h b/include/linux/verification.h index a655923335ae..c27516d68962 100644 --- a/include/linux/verification.h +++ b/include/linux/verification.h @@ -54,6 +54,29 @@ extern int verify_pkcs7_message_sig(const void *data, size_t len, size_t asn1hdrlen), void *ctx); +#ifdef CONFIG_PGP_KEY_PARSER +extern int verify_pgp_signature(const void *data, size_t len, + const void *raw_pgp, size_t pgp_len, + struct key *trusted_keys, + enum key_being_used_for usage, + int (*view_content)(void *ctx, + const void *data, size_t len, + size_t asn1hdrlen), + void *ctx); +#else +static inline int verify_pgp_signature(const void *data, size_t len, + const void *raw_pgp, size_t pgp_len, + struct key *trusted_keys, + enum key_being_used_for usage, + int (*view_content)(void *ctx, + const void *data, size_t len, + size_t asn1hdrlen), + void *ctx) +{ + return -EOPNOTSUPP; +} +#endif /* CONFIG_PGP_KEY_PARSER */ + #ifdef CONFIG_SIGNED_PE_FILE_VERIFICATION extern int verify_pefile_signature(const void *pebuf, unsigned pelen, struct key *trusted_keys, From patchwork Tue Jan 11 18:03:18 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Roberto Sassu X-Patchwork-Id: 531217 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id B17BBC433FE for ; Tue, 11 Jan 2022 18:06:32 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1345294AbiAKSGb (ORCPT ); Tue, 11 Jan 2022 13:06:31 -0500 Received: from frasgout.his.huawei.com ([185.176.79.56]:4402 "EHLO frasgout.his.huawei.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1345411AbiAKSEC (ORCPT ); Tue, 11 Jan 2022 13:04:02 -0500 Received: from fraeml714-chm.china.huawei.com (unknown [172.18.147.206]) by frasgout.his.huawei.com (SkyGuard) with ESMTP id 4JYJQ13qZMz688Bk; Wed, 12 Jan 2022 02:00:25 +0800 (CST) Received: from roberto-ThinkStation-P620.huawei.com (10.204.63.22) by fraeml714-chm.china.huawei.com (10.206.15.33) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2308.20; Tue, 11 Jan 2022 19:03:58 +0100 From: Roberto Sassu To: , , , CC: , , , , , , , Roberto Sassu Subject: [PATCH 14/14] KEYS: Introduce load_pgp_public_keyring() Date: Tue, 11 Jan 2022 19:03:18 +0100 Message-ID: <20220111180318.591029-15-roberto.sassu@huawei.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20220111180318.591029-1-roberto.sassu@huawei.com> References: <20220111180318.591029-1-roberto.sassu@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.204.63.22] X-ClientProxiedBy: lhreml754-chm.china.huawei.com (10.201.108.204) To fraeml714-chm.china.huawei.com (10.206.15.33) X-CFilter-Loop: Reflected Precedence: bulk List-ID: X-Mailing-List: linux-crypto@vger.kernel.org Preload PGP keys from 'pubring.gpg', placed in certs/ of the kernel source directory. Signed-off-by: Roberto Sassu --- certs/Kconfig | 11 +++++++++++ certs/Makefile | 7 +++++++ certs/system_certificates.S | 18 ++++++++++++++++++ certs/system_keyring.c | 21 +++++++++++++++++++++ 4 files changed, 57 insertions(+) diff --git a/certs/Kconfig b/certs/Kconfig index ae7f2e876a31..2f7fa68cd958 100644 --- a/certs/Kconfig +++ b/certs/Kconfig @@ -126,4 +126,15 @@ config SYSTEM_REVOCATION_KEYS containing X.509 certificates to be included in the default blacklist keyring. +config PGP_PRELOAD_PUBLIC_KEYS + bool "Preload PGP public keys" + depends on SYSTEM_TRUSTED_KEYRING + select PGP_PRELOAD + default n + help + Load at boot time the PGP public keys from a reserved area (populated + with the content of 'certs/pubring.gpg' provided at kernel build + time), and add them to the built-in keyring. Invalid keys are ignored + and the loading continues. + endmenu diff --git a/certs/Makefile b/certs/Makefile index 279433783b10..c85e0ff560ca 100644 --- a/certs/Makefile +++ b/certs/Makefile @@ -22,6 +22,13 @@ $(obj)/system_certificates.o: $(obj)/x509_certificate_list # Cope with signing_key.x509 existing in $(srctree) not $(objtree) AFLAGS_system_certificates.o := -I$(srctree) +ifdef CONFIG_PGP_PRELOAD_PUBLIC_KEYS +ifeq ($(shell ls $(srctree)/certs/pubring.gpg 2> /dev/null), $(srctree)/certs/pubring.gpg) +AFLAGS_system_certificates.o += -DHAVE_PUBRING_GPG +$(obj)/system_certificates.o: $(srctree)/certs/pubring.gpg +endif +endif + quiet_cmd_extract_certs = EXTRACT_CERTS $(patsubst "%",%,$(2)) cmd_extract_certs = scripts/extract-cert $(2) $@ diff --git a/certs/system_certificates.S b/certs/system_certificates.S index e1645e6f4d97..03b361bec758 100644 --- a/certs/system_certificates.S +++ b/certs/system_certificates.S @@ -47,3 +47,21 @@ module_cert_size: #else .long __module_cert_end - __module_cert_start #endif + + .align 8 + .globl pgp_public_keys +pgp_public_keys: +__pgp_key_list_start: +#ifdef HAVE_PUBRING_GPG + .incbin "certs/pubring.gpg" +#endif +__pgp_key_list_end: + + .align 8 + .globl pgp_public_keys_size +pgp_public_keys_size: +#ifdef CONFIG_64BIT + .quad __pgp_key_list_end - __pgp_key_list_start +#else + .long __pgp_key_list_end - __pgp_key_list_start +#endif diff --git a/certs/system_keyring.c b/certs/system_keyring.c index 26a11b1dcd59..1612fb97a652 100644 --- a/certs/system_keyring.c +++ b/certs/system_keyring.c @@ -167,6 +167,27 @@ static __init int load_system_certificate_list(void) } late_initcall(load_system_certificate_list); +#ifdef CONFIG_PGP_PRELOAD_PUBLIC_KEYS +extern __initconst const u8 pgp_public_keys[]; +extern __initconst const unsigned long pgp_public_keys_size; + +/* + * Load a list of PGP keys. + */ +static __init int load_pgp_public_keyring(void) +{ + pr_notice("Load PGP public keys\n"); + + if (preload_pgp_keys(pgp_public_keys, + pgp_public_keys_size, + builtin_trusted_keys) < 0) + pr_err("Can't load PGP public keys\n"); + + return 0; +} +late_initcall(load_pgp_public_keyring); +#endif /* CONFIG_PGP_PRELOAD_PUBLIC_KEYS */ + #ifdef CONFIG_SYSTEM_DATA_VERIFICATION /**