[RFC][GNUPG][PATCH v3 2/2] Convert PGP signatures to the user asymmetric key signatures format

From: Roberto Sassu
Date: Thu Jul 20 2023 - 11:37:48 EST


From: Roberto Sassu <roberto.sassu@xxxxxxxxxx>

Enhance the gpg command --conv-kernel to also support converting PGP
signatures to the user asymmetric key signatures format.

Signed-off-by: Roberto Sassu <roberto.sassu@xxxxxxxxxx>
---
g10/conv-packet.c | 200 ++++++++++++++++++++++++++++++++++++++++++++++
g10/conv-packet.h | 7 ++
g10/mainproc.c | 1 +
3 files changed, 208 insertions(+)

diff --git a/g10/conv-packet.c b/g10/conv-packet.c
index 8f2fc40b980..be7eb3c80f8 100644
--- a/g10/conv-packet.c
+++ b/g10/conv-packet.c
@@ -28,6 +28,8 @@
#include <linux/uasym_parser.h>
#include <asm/byteorder.h>
#include <linux/pub_key_info.h>
+#include <linux/sig_enc_info.h>
+#include <linux/hash_info.h>

#include "gpg.h"
#include "../common/util.h"
@@ -38,6 +40,16 @@

static estream_t listfp;

+static const enum hash_algo pgp_hash_algorithms[DIGEST_ALGO_SHA224 + 1] = {
+ [DIGEST_ALGO_MD5] = HASH_ALGO_MD5,
+ [DIGEST_ALGO_SHA1] = HASH_ALGO_SHA1,
+ [DIGEST_ALGO_RMD160] = HASH_ALGO_RIPE_MD_160,
+ [DIGEST_ALGO_SHA256] = HASH_ALGO_SHA256,
+ [DIGEST_ALGO_SHA384] = HASH_ALGO_SHA384,
+ [DIGEST_ALGO_SHA512] = HASH_ALGO_SHA512,
+ [DIGEST_ALGO_SHA224] = HASH_ALGO_SHA224,
+};
+
static void init_output(void)
{
if (!listfp)
@@ -282,3 +294,191 @@ out:
xfree(buffer);
return 0;
}
+
+/* Taken from sig_check.c */
+static int get_sig_data(PKT_signature * sig, __u8 **buf, __u32 *buf_len)
+{
+ __u8 *buf_ptr;
+
+ *buf = xmalloc_clear(4 + 2 + sig->hashed->len + 6);
+ if (!*buf)
+ return -ENOMEM;
+
+ buf_ptr = *buf;
+
+ if (sig->version >= 4)
+ *buf_ptr++ = sig->version;
+
+ *buf_ptr++ = sig->sig_class;
+ if (sig->version < 4)
+ {
+ u32 a = sig->timestamp;
+ *buf_ptr++ = ((a >> 24) & 0xff);
+ *buf_ptr++ = ((a >> 16) & 0xff);
+ *buf_ptr++ = ((a >> 8) & 0xff);
+ *buf_ptr++ = (a & 0xff);
+ }
+ else
+ {
+ size_t n;
+ *buf_ptr++ = sig->pubkey_algo;
+ *buf_ptr++ = sig->digest_algo;
+ if (sig->hashed)
+ {
+ n = sig->hashed->len;
+ *buf_ptr++ = n >> 8;
+ *buf_ptr++ = n;
+ memcpy(buf_ptr, sig->hashed->data, n);
+ buf_ptr += n;
+ n += 6;
+ }
+ else
+ {
+ /* Two octets for the (empty) length of the hashed
+ * section. */
+ *buf_ptr++ = 0;
+ *buf_ptr++ = 0;
+ n = 6;
+ }
+ /* Add some magic per Section 5.2.4 of RFC 4880. */
+ *buf_ptr++ = sig->version;
+ *buf_ptr++ = 0xff;
+ *buf_ptr++ = n >> 24;
+ *buf_ptr++ = n >> 16;
+ *buf_ptr++ = n >> 8;
+ *buf_ptr++ = n;
+ }
+
+ *buf_len = buf_ptr - *buf;
+ return 0;
+}
+
+int write_kernel_signature(PKT_signature *sig)
+{
+ unsigned char *buffer = NULL;
+ size_t buffer_len = 0, buffer_len_padded = 0;
+ struct tlv_hdr hdr = { 0 };
+ struct tlv_entry e_key_algo = { 0 };
+ struct tlv_entry e_hash_algo = { 0 };
+ struct tlv_entry e_sig_encoding = { 0 };
+ struct tlv_entry e_sig_kid0 = { 0 };
+ struct tlv_entry e_sig_pub = { 0 };
+ struct tlv_entry e_sig_data = { 0 };
+ __u8 pkey_algo;
+ __u8 hash_algo;
+ __u8 sig_encoding = SIG_ENC_PKCS1;
+ __u8 *sig_data = NULL;
+ __u32 _keyid, sig_data_len;
+ __u64 total_len = 0;
+ gpg_error_t err;
+ int ret = 0;
+
+ init_output();
+
+ ret = pgp_to_kernel_algo(sig->pubkey_algo, NULL, &pkey_algo);
+ if (ret < 0)
+ return ret;
+
+ if (pkey_algo == PKEY_ALGO_ECDSA)
+ sig_encoding = SIG_ENC_X962;
+
+ hash_algo = pgp_hash_algorithms[sig->digest_algo];
+
+ /* sig key algo */
+ e_key_algo.field = __cpu_to_be64(SIG_KEY_ALGO);
+ e_key_algo.length = __cpu_to_be64(sizeof(pkey_algo));
+ total_len += sizeof(e_key_algo) + sizeof(pkey_algo);
+
+ /* sig hash algo */
+ e_hash_algo.field = __cpu_to_be64(SIG_HASH_ALGO);
+ e_hash_algo.length = __cpu_to_be64(sizeof(hash_algo));
+ total_len += sizeof(e_hash_algo) + sizeof(hash_algo);
+
+ /* sig encoding */
+ e_sig_encoding.field = __cpu_to_be64(SIG_ENC);
+ e_sig_encoding.length = __cpu_to_be64(sizeof(sig_encoding));
+ total_len += sizeof(e_sig_encoding) + sizeof(sig_encoding);
+
+ /* sig kid0 */
+ e_sig_kid0.field = __cpu_to_be64(SIG_KID0);
+ e_sig_kid0.length = __cpu_to_be64(2 * sizeof(*sig->keyid));
+ total_len += sizeof(e_sig_kid0) + 2 * sizeof(*sig->keyid);
+
+ /* sig data */
+ e_sig_data.field = __cpu_to_be64(SIG_DATA_END);
+ ret = get_sig_data(sig, &sig_data, &sig_data_len);
+ if (ret < 0)
+ goto out;
+
+ e_sig_data.length = __cpu_to_be64(sig_data_len);
+ total_len += sizeof(e_sig_data) + sig_data_len;
+
+ switch (sig->pubkey_algo) {
+ case PUBKEY_ALGO_ECDSA:
+ ret = mpis_to_asn1_sequence(sig->data, 2, &buffer, &buffer_len_padded);
+ break;
+ case PUBKEY_ALGO_RSA:
+ err = gcry_mpi_print(GCRYMPI_FMT_USG, NULL, 0, &buffer_len, sig->data[0]);
+ if (err) {
+ ret = -EINVAL;
+ break;
+ }
+
+ buffer_len_padded = ((buffer_len + 7) / 8) * 8;
+ buffer = xmalloc_clear(buffer_len_padded);
+ if (!buffer) {
+ ret = -ENOMEM;
+ break;
+ }
+
+ err = gcry_mpi_print(GCRYMPI_FMT_USG,
+ buffer + buffer_len_padded - buffer_len, buffer_len,
+ &buffer_len, sig->data[0]);
+ if (err)
+ ret = -EINVAL;
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ if (ret < 0)
+ goto out;
+
+ /* key blob */
+ e_sig_pub.field = __cpu_to_be64(SIG_S);
+ e_sig_pub.length = __cpu_to_be64(buffer_len_padded);
+ total_len += sizeof(e_sig_pub) + buffer_len_padded;
+
+ hdr.data_type = __cpu_to_be64(TYPE_SIG);
+ hdr.num_fields = __cpu_to_be64(6);
+ hdr.total_len = __cpu_to_be64(total_len);
+
+ es_write(listfp, &hdr, sizeof(hdr), NULL);
+
+ es_write(listfp, &e_key_algo, sizeof(e_key_algo), NULL);
+ es_write(listfp, &pkey_algo, sizeof(pkey_algo), NULL);
+
+ es_write(listfp, &e_hash_algo, sizeof(e_hash_algo), NULL);
+ es_write(listfp, &hash_algo, sizeof(hash_algo), NULL);
+
+ es_write(listfp, &e_sig_encoding, sizeof(e_sig_encoding), NULL);
+ es_write(listfp, &sig_encoding, sizeof(sig_encoding), NULL);
+
+ es_write(listfp, &e_sig_kid0, sizeof(e_sig_kid0), NULL);
+ _keyid = __cpu_to_be32(sig->keyid[0]);
+ es_write(listfp, &_keyid, sizeof(_keyid), NULL);
+ _keyid = __cpu_to_be32(sig->keyid[1]);
+ es_write(listfp, &_keyid, sizeof(_keyid), NULL);
+
+ es_write(listfp, &e_sig_pub, sizeof(e_sig_pub), NULL);
+ es_write(listfp, buffer, buffer_len_padded, NULL);
+
+ es_write(listfp, &e_sig_data, sizeof(e_sig_data), NULL);
+ es_write(listfp, sig_data, sig_data_len, NULL);
+
+out:
+ xfree(sig_data);
+ xfree(buffer);
+ return 0;
+}
diff --git a/g10/conv-packet.h b/g10/conv-packet.h
index d35acb985fc..ef718de0a7a 100644
--- a/g10/conv-packet.h
+++ b/g10/conv-packet.h
@@ -26,6 +26,7 @@

#ifdef UASYM_KEYS_SIGS
int write_kernel_key(PKT_public_key *pk);
+int write_kernel_signature(PKT_signature *sig);
#else
static inline int write_kernel_key(PKT_public_key *pk)
{
@@ -33,5 +34,11 @@ static inline int write_kernel_key(PKT_public_key *pk)
return 0;
}

+static inline int write_kernel_signature(PKT_signature *sig)
+{
+ (void)sig;
+ return 0;
+}
+
#endif /* UASYM_KEYS_SIGS */
#endif /*G10_CONV_PACKET_H*/
diff --git a/g10/mainproc.c b/g10/mainproc.c
index edef9907127..1cb08d82000 100644
--- a/g10/mainproc.c
+++ b/g10/mainproc.c
@@ -502,6 +502,7 @@ proc_conv (PACKET *pkt)
switch (pkt->pkttype)
{
case PKT_PUBLIC_KEY: write_kernel_key(pkt->pkt.public_key); break;
+ case PKT_SIGNATURE: write_kernel_signature(pkt->pkt.signature); break;
default: break;
}
free_packet(pkt, NULL);
--
2.34.1