diff mbox series

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

Message ID 20230720153247.3755856-12-roberto.sassu@huaweicloud.com (mailing list archive)
State New
Headers show
Series None | expand

Commit Message

Roberto Sassu July 20, 2023, 3:32 p.m. UTC
From: Roberto Sassu <roberto.sassu@huawei.com>

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@huawei.com>
---
 g10/conv-packet.c | 200 ++++++++++++++++++++++++++++++++++++++++++++++
 g10/conv-packet.h |   7 ++
 g10/mainproc.c    |   1 +
 3 files changed, 208 insertions(+)
diff mbox series

Patch

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);