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 360db30eb8d..d85d5a35002 100644 --- a/g10/conv-packet.c +++ b/g10/conv-packet.c @@ -31,6 +31,8 @@ #include <linux/byteorder/little_endian.h> #endif #include <linux/pub_key_info.h> +#include <linux/sig_enc_info.h> +#include <linux/hash_info.h> #include "gpg.h" #include "../common/util.h" @@ -41,6 +43,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) @@ -285,3 +297,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 uasym_hdr hdr = { 0 }; + struct uasym_entry e_key_algo = { 0 }; + struct uasym_entry e_hash_algo = { 0 }; + struct uasym_entry e_sig_encoding = { 0 }; + struct uasym_entry e_sig_kid0 = { 0 }; + struct uasym_entry e_sig_pub = { 0 }; + struct uasym_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_be16(SIG_KEY_ALGO); + e_key_algo.length = __cpu_to_be32(sizeof(pkey_algo)); + total_len += sizeof(e_key_algo) + sizeof(pkey_algo); + + /* sig hash algo */ + e_hash_algo.field = __cpu_to_be16(SIG_HASH_ALGO); + e_hash_algo.length = __cpu_to_be32(sizeof(hash_algo)); + total_len += sizeof(e_hash_algo) + sizeof(hash_algo); + + /* sig encoding */ + e_sig_encoding.field = __cpu_to_be16(SIG_ENC); + e_sig_encoding.length = __cpu_to_be32(sizeof(sig_encoding)); + total_len += sizeof(e_sig_encoding) + sizeof(sig_encoding); + + /* sig kid0 */ + e_sig_kid0.field = __cpu_to_be16(SIG_KID0); + e_sig_kid0.length = __cpu_to_be32(2 * sizeof(*sig->keyid)); + total_len += sizeof(e_sig_kid0) + 2 * sizeof(*sig->keyid); + + /* sig data */ + e_sig_data.field = __cpu_to_be16(SIG_DATA_END); + ret = get_sig_data(sig, &sig_data, &sig_data_len); + if (ret < 0) + goto out; + + e_sig_data.length = __cpu_to_be32(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_be16(SIG_S); + e_sig_pub.length = __cpu_to_be32(buffer_len_padded); + total_len += sizeof(e_sig_pub) + buffer_len_padded; + + hdr.data_type = TYPE_SIG; + hdr.num_fields = __cpu_to_be16(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