From 5c8e5d62c4d88955c1d890b51e3ad3faf9336965 Mon Sep 17 00:00:00 2001 From: jok325 Date: Fri, 3 Apr 2020 23:15:08 +0200 Subject: [PATCH] Implements basic support for V5 keys and signatures. Issues #821, #820, #26 --- include/repgp/repgp_def.h | 4 +- src/lib/crypto/signatures.cpp | 14 +- src/lib/ffi-priv-types.h | 6 +- src/lib/fingerprint.cpp | 17 ++- src/lib/rnp.cpp | 12 +- src/lib/types.h | 2 +- src/librekey/key_store_kbx.cpp | 7 +- src/librepgp/stream-dump.cpp | 7 +- src/librepgp/stream-key.cpp | 2 + src/librepgp/stream-packet.cpp | 123 ++++++++++++++---- src/librepgp/stream-packet.h | 1 + src/librepgp/stream-sig.cpp | 20 ++- .../test_stream_key_load/ecc-25519-v5.asc | 16 +++ src/tests/streams.cpp | 15 +++ 14 files changed, 199 insertions(+), 47 deletions(-) create mode 100644 src/tests/data/test_stream_key_load/ecc-25519-v5.asc diff --git a/include/repgp/repgp_def.h b/include/repgp/repgp_def.h index f692345705..b65fe6ea22 100644 --- a/include/repgp/repgp_def.h +++ b/include/repgp/repgp_def.h @@ -93,6 +93,7 @@ /* Size of the fingerprint */ #define PGP_FINGERPRINT_SIZE 20 +#define PGP_FINGERPRINT_MAX_SIZE 32 #define PGP_FINGERPRINT_HEX_SIZE (PGP_FINGERPRINT_SIZE * 3) + 1 /* Size of the key grip */ @@ -406,7 +407,8 @@ enum { PGP_SE_IP_DATA_VERSION = 1, PGP_PKSK_V3 = 3, PGP_SKSK_V4 = 4, PGP_SKSK_V5 typedef enum { PGP_V2 = 2, /* Version 2 (essentially the same as v3) */ PGP_V3 = 3, /* Version 3 */ - PGP_V4 = 4 /* Version 4 */ + PGP_V4 = 4, /* Version 4 */ + PGP_V5 = 5 /* Version 5 */ } pgp_version_t; typedef enum pgp_op_t { diff --git a/src/lib/crypto/signatures.cpp b/src/lib/crypto/signatures.cpp index e6a2914968..928ff7c94d 100644 --- a/src/lib/crypto/signatures.cpp +++ b/src/lib/crypto/signatures.cpp @@ -51,10 +51,16 @@ signature_hash_finish(const pgp_signature_t *sig, goto error; } if (sig->version > PGP_V3) { - uint8_t trailer[6] = {0x04, 0xff, 0x00, 0x00, 0x00, 0x00}; - STORE32BE(&trailer[2], sig->hashed_len); - - if (pgp_hash_add(hash, trailer, 6)) { + uint8_t trailer[10] = {0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + size_t trail_size = 6; + if (sig->version == PGP_V5) { + trailer[0] = 0x05; + trail_size += 4; + STORE32BE(&trailer[6], sig->hashed_len); + } else { + STORE32BE(&trailer[2], sig->hashed_len); + } + if (pgp_hash_add(hash, trailer, trail_size)) { RNP_LOG("failed to add sig trailer"); goto error; } diff --git a/src/lib/ffi-priv-types.h b/src/lib/ffi-priv-types.h index b3a8f432ea..a53c03dc8b 100644 --- a/src/lib/ffi-priv-types.h +++ b/src/lib/ffi-priv-types.h @@ -149,9 +149,9 @@ struct rnp_identifier_iterator_st { pgp_key_t * keyp; unsigned uididx; json_object * tbl; - char - buf[1 + MAX(MAX(MAX(PGP_KEY_ID_SIZE * 2, PGP_KEY_GRIP_SIZE), PGP_FINGERPRINT_SIZE * 2), - MAX_ID_LENGTH)]; + char buf[1 + MAX(MAX(MAX(PGP_KEY_ID_SIZE * 2, PGP_KEY_GRIP_SIZE), + PGP_FINGERPRINT_MAX_SIZE * 2), + MAX_ID_LENGTH)]; }; /* This is just for readability at the call site and will hopefully reduce mistakes. diff --git a/src/lib/fingerprint.cpp b/src/lib/fingerprint.cpp index 8ba772f40d..6136883ff8 100644 --- a/src/lib/fingerprint.cpp +++ b/src/lib/fingerprint.cpp @@ -71,6 +71,19 @@ pgp_fingerprint(pgp_fingerprint_t *fp, const pgp_key_pkt_t *key) return RNP_SUCCESS; } + if (key->version == PGP_V5) { + if (!pgp_hash_create(&hash, PGP_HASH_SHA256)) { + RNP_LOG("bad sha256 alloc"); + return RNP_ERROR_NOT_SUPPORTED; + } + if (!signature_hash_key(key, &hash)) { + return RNP_ERROR_GENERIC; + } + fp->length = pgp_hash_finish(&hash, fp->fingerprint); + RNP_DHEX("sha256 fingerprint", fp->fingerprint, fp->length); + return RNP_SUCCESS; + } + RNP_LOG("unsupported key version"); return RNP_ERROR_NOT_SUPPORTED; } @@ -102,7 +115,9 @@ pgp_keyid(uint8_t *keyid, const size_t idlen, const pgp_key_pkt_t *key) if ((ret = pgp_fingerprint(&fp, key))) { return ret; } - (void) memcpy(keyid, fp.fingerprint + fp.length - idlen, idlen); + + (void) memcpy( + keyid, fp.fingerprint + (key->version == PGP_V5 ? 0 : fp.length - idlen), idlen); return RNP_SUCCESS; } diff --git a/src/lib/rnp.cpp b/src/lib/rnp.cpp index 5871242e19..ce7968d6e3 100644 --- a/src/lib/rnp.cpp +++ b/src/lib/rnp.cpp @@ -100,7 +100,7 @@ find_key(rnp_ffi_t ffi, } if (!key && ffi->getkeycb && try_key_provider) { char identifier[1 + MAX(MAX(MAX(PGP_KEY_ID_SIZE * 2, PGP_KEY_GRIP_SIZE), - PGP_FINGERPRINT_SIZE * 2), + PGP_FINGERPRINT_MAX_SIZE * 2), MAX_ID_LENGTH)]; const char *identifier_type = NULL; @@ -2959,12 +2959,16 @@ str_to_locator(rnp_ffi_t ffi, } break; case PGP_KEY_SEARCH_FINGERPRINT: { // TODO: support v5 fingerprints - if (strlen(identifier) != (PGP_FINGERPRINT_SIZE * 2)) { + switch (strlen(identifier)) { + case PGP_FINGERPRINT_SIZE * 2: + case PGP_FINGERPRINT_MAX_SIZE * 2: + break; + default: FFI_LOG(ffi, "Invalid fingerprint: %s", identifier); return RNP_ERROR_BAD_PARAMETERS; } locator->by.fingerprint.length = rnp_hex_decode( - identifier, locator->by.fingerprint.fingerprint, PGP_FINGERPRINT_SIZE); + identifier, locator->by.fingerprint.fingerprint, PGP_FINGERPRINT_MAX_SIZE); if (!locator->by.fingerprint.length) { FFI_LOG(ffi, "Invalid fingerprint: %s", identifier); return RNP_ERROR_BAD_PARAMETERS; @@ -6212,7 +6216,7 @@ key_to_json(json_object *jso, rnp_key_handle_t handle, uint32_t flags) return RNP_ERROR_OUT_OF_MEMORY; } // fingerprint - char fpr[PGP_FINGERPRINT_SIZE * 2 + 1]; + char fpr[PGP_FINGERPRINT_MAX_SIZE * 2 + 1]; if (!rnp_hex_encode(pgp_key_get_fp(key)->fingerprint, pgp_key_get_fp(key)->length, fpr, diff --git a/src/lib/types.h b/src/lib/types.h index 7f4d760906..ab9204c8f2 100644 --- a/src/lib/types.h +++ b/src/lib/types.h @@ -84,7 +84,7 @@ typedef uint8_t pgp_ss_rr_code_t; /** pgp_fingerprint_t */ typedef struct pgp_fingerprint_t { - uint8_t fingerprint[PGP_FINGERPRINT_SIZE]; + uint8_t fingerprint[PGP_FINGERPRINT_MAX_SIZE]; unsigned length; } pgp_fingerprint_t; diff --git a/src/librekey/key_store_kbx.cpp b/src/librekey/key_store_kbx.cpp index 0751213bee..a02171f8c7 100644 --- a/src/librekey/key_store_kbx.cpp +++ b/src/librekey/key_store_kbx.cpp @@ -516,6 +516,7 @@ rnp_key_store_kbx_write_pgp(rnp_key_store_t *key_store, pgp_key_t *key, pgp_dest bool result = false; list subkey_sig_expirations = NULL; // expirations (uint32_t) of subkey signatures uint32_t expiration = 0; + const pgp_fingerprint_t *fp; if (init_mem_dest(&memdst, NULL, BLOB_SIZE_LIMIT)) { RNP_LOG("alloc failed"); @@ -546,7 +547,8 @@ rnp_key_store_kbx_write_pgp(rnp_key_store_t *key_store, pgp_key_t *key, pgp_dest goto finish; } - if (!pbuf(&memdst, pgp_key_get_fp(key)->fingerprint, PGP_FINGERPRINT_SIZE) || + fp = pgp_key_get_fp(key); + if (!pbuf(&memdst, fp->fingerprint, fp->length) || !pu32(&memdst, memdst.writeb - 8) || // offset to keyid (part of fpr for V4) !pu16(&memdst, 0) || // flags, not used by GnuPG !pu16(&memdst, 0)) { // RFU @@ -557,7 +559,8 @@ rnp_key_store_kbx_write_pgp(rnp_key_store_t *key_store, pgp_key_t *key, pgp_dest for (list_item *sgrip = list_front(key->subkey_grips); sgrip; sgrip = list_next(sgrip)) { const pgp_key_t *subkey = rnp_key_store_get_key_by_grip(key_store, (const uint8_t *) sgrip); - if (!pbuf(&memdst, pgp_key_get_fp(subkey)->fingerprint, PGP_FINGERPRINT_SIZE) || + fp = pgp_key_get_fp(subkey); + if (!pbuf(&memdst, fp->fingerprint, fp->length) || !pu32(&memdst, memdst.writeb - 8) || // offset to keyid (part of fpr for V4) !pu16(&memdst, 0) || // flags, not used by GnuPG !pu16(&memdst, 0)) { // RFU diff --git a/src/librepgp/stream-dump.cpp b/src/librepgp/stream-dump.cpp index 41e98dbca8..60b0c2607d 100644 --- a/src/librepgp/stream-dump.cpp +++ b/src/librepgp/stream-dump.cpp @@ -550,8 +550,7 @@ signature_dump_subpacket(rnp_dump_ctx_t *ctx, pgp_dest_t *dst, pgp_sig_subpkt_t dst_printf(dst, "%s\n", sname); dst_printf(dst, "class: %d\n", (int) subpkt->fields.revocation_key.klass); dst_print_palg(dst, NULL, subpkt->fields.revocation_key.pkalg); - dst_print_hex( - dst, "fingerprint", subpkt->fields.revocation_key.fp, PGP_FINGERPRINT_SIZE, true); + dst_print_hex(dst, "fingerprint", subpkt->fields.revocation_key.fp, subpkt->len, true); break; case PGP_SIG_SUBPKT_ISSUER_KEY_ID: dst_print_keyid(dst, sname, subpkt->fields.issuer); @@ -852,7 +851,7 @@ stream_dump_key(rnp_dump_ctx_t *ctx, pgp_source_t *src, pgp_dest_t *dst) if (ctx->dump_grips) { if (rnp_key_store_get_key_grip(&key.material, keyfp.fingerprint)) { - dst_print_hex(dst, "grip", keyfp.fingerprint, PGP_FINGERPRINT_SIZE, false); + dst_print_hex(dst, "grip", keyfp.fingerprint, keyfp.length, false); } else { dst_printf(dst, "grip: failed to calculate"); } @@ -1477,7 +1476,7 @@ signature_dump_subpacket_json(rnp_dump_ctx_t *ctx, pgp_sig_subpkt_t *subpkt, jso obj_add_field_json( obj, "algorithm", json_object_new_int(subpkt->fields.revocation_key.pkalg)) && obj_add_hex_json( - obj, "fingerprint", subpkt->fields.revocation_key.fp, PGP_FINGERPRINT_SIZE); + obj, "fingerprint", subpkt->fields.revocation_key.fp, subpkt->len); case PGP_SIG_SUBPKT_ISSUER_KEY_ID: return obj_add_hex_json(obj, "issuer keyid", subpkt->fields.issuer, PGP_KEY_ID_SIZE); case PGP_SIG_SUBPKT_KEYSERV_PREFS: diff --git a/src/librepgp/stream-key.cpp b/src/librepgp/stream-key.cpp index 0dcacaba36..75c9fa9274 100644 --- a/src/librepgp/stream-key.cpp +++ b/src/librepgp/stream-key.cpp @@ -1274,6 +1274,8 @@ decrypt_secret_key(pgp_key_pkt_t *key, const char *password) } ret = decrypt_secret_key_v3(&crypt, decdata, key->sec_data, key->sec_len); break; + case PGP_V5: + /* FALLTHROUGH */ case PGP_V4: pgp_cipher_cfb_decrypt(&crypt, decdata, key->sec_data, key->sec_len); ret = RNP_SUCCESS; diff --git a/src/librepgp/stream-packet.cpp b/src/librepgp/stream-packet.cpp index 429078adb1..229d4f3761 100644 --- a/src/librepgp/stream-packet.cpp +++ b/src/librepgp/stream-packet.cpp @@ -57,6 +57,15 @@ write_uint16(uint8_t *buf, uint16_t val) buf[1] = val & 0xff; } +void +write_uint32(uint8_t *buf, uint32_t val) +{ + buf[0] = val >> 24; + buf[1] = (val >> 16) & 0xff; + buf[2] = (val >> 8) & 0xff; + buf[3] = val & 0xff; +} + size_t write_packet_len(uint8_t *buf, size_t len) { @@ -924,7 +933,7 @@ stream_write_signature(const pgp_signature_t *sig, pgp_dest_t *dst) pgp_packet_body_t pktbody; bool res; - if ((sig->version < PGP_V2) || (sig->version > PGP_V4)) { + if ((sig->version < PGP_V2) || (sig->version > PGP_V5)) { RNP_LOG("don't know version %d", (int) sig->version); return false; } @@ -1506,7 +1515,7 @@ signature_parse_subpackets(pgp_signature_t *sig, uint8_t *buf, size_t len, bool /* parse v4-specific fields, not the whole signature */ static rnp_result_t -signature_read_v4(pgp_packet_body_t *pkt, pgp_signature_t *sig) +signature_read_v4v5(pgp_packet_body_t *pkt, pgp_signature_t *sig, uint8_t ver) { uint8_t buf[5]; uint8_t * spbuf; @@ -1604,8 +1613,8 @@ stream_parse_signature_body(pgp_packet_body_t *pkt, pgp_signature_t *sig) /* v3 or v4 signature body */ if ((ver == PGP_V2) || (ver == PGP_V3)) { res = signature_read_v3(pkt, sig); - } else if (ver == PGP_V4) { - res = signature_read_v4(pkt, sig); + } else if (ver == PGP_V4 || ver == PGP_V5) { + res = signature_read_v4v5(pkt, sig, ver); } else { RNP_LOG("unknown signature version: %d", (int) ver); goto finish; @@ -1857,10 +1866,11 @@ bool key_fill_hashed_data(pgp_key_pkt_t *key) { pgp_packet_body_t hbody; + pgp_packet_body_t hbody_alg, *phbody; bool res = false; /* we don't have a need to write v2-v3 signatures */ - if (key->version != PGP_V4) { + if (key->version != PGP_V4 && key->version != PGP_V5) { RNP_LOG("unknown key version %d", (int) key->version); return false; } @@ -1869,6 +1879,11 @@ key_fill_hashed_data(pgp_key_pkt_t *key) RNP_LOG("allocation failed"); return false; } + if (key->version == PGP_V5 && !init_packet_body(&hbody_alg, PGP_PKT_RESERVED)) { + RNP_LOG("allocation failed"); + free_packet_body(&hbody); + return false; + } res = add_packet_body_byte(&hbody, key->version) && add_packet_body_uint32(&hbody, key->creation_time) && @@ -1879,50 +1894,61 @@ key_fill_hashed_data(pgp_key_pkt_t *key) } /* Algorithm specific fields */ + phbody = (key->version == PGP_V5 ? &hbody_alg : &hbody); switch (key->alg) { case PGP_PKA_RSA: case PGP_PKA_RSA_ENCRYPT_ONLY: case PGP_PKA_RSA_SIGN_ONLY: - res = add_packet_body_mpi(&hbody, &key->material.rsa.n) && - add_packet_body_mpi(&hbody, &key->material.rsa.e); + res = add_packet_body_mpi(phbody, &key->material.rsa.n) && + add_packet_body_mpi(phbody, &key->material.rsa.e); break; case PGP_PKA_DSA: - res = add_packet_body_mpi(&hbody, &key->material.dsa.p) && - add_packet_body_mpi(&hbody, &key->material.dsa.q) && - add_packet_body_mpi(&hbody, &key->material.dsa.g) && - add_packet_body_mpi(&hbody, &key->material.dsa.y); + res = add_packet_body_mpi(phbody, &key->material.dsa.p) && + add_packet_body_mpi(phbody, &key->material.dsa.q) && + add_packet_body_mpi(phbody, &key->material.dsa.g) && + add_packet_body_mpi(phbody, &key->material.dsa.y); break; case PGP_PKA_ELGAMAL: case PGP_PKA_ELGAMAL_ENCRYPT_OR_SIGN: - res = add_packet_body_mpi(&hbody, &key->material.eg.p) && - add_packet_body_mpi(&hbody, &key->material.eg.g) && - add_packet_body_mpi(&hbody, &key->material.eg.y); + res = add_packet_body_mpi(phbody, &key->material.eg.p) && + add_packet_body_mpi(phbody, &key->material.eg.g) && + add_packet_body_mpi(phbody, &key->material.eg.y); break; case PGP_PKA_ECDSA: case PGP_PKA_EDDSA: case PGP_PKA_SM2: - res = add_packet_body_key_curve(&hbody, key->material.ec.curve) && - add_packet_body_mpi(&hbody, &key->material.ec.p); + res = add_packet_body_key_curve(phbody, key->material.ec.curve) && + add_packet_body_mpi(phbody, &key->material.ec.p); break; case PGP_PKA_ECDH: - res = add_packet_body_key_curve(&hbody, key->material.ec.curve) && - add_packet_body_mpi(&hbody, &key->material.ec.p) && - add_packet_body_byte(&hbody, 3) && add_packet_body_byte(&hbody, 1) && - add_packet_body_byte(&hbody, key->material.ec.kdf_hash_alg) && - add_packet_body_byte(&hbody, key->material.ec.key_wrap_alg); + res = add_packet_body_key_curve(phbody, key->material.ec.curve) && + add_packet_body_mpi(phbody, &key->material.ec.p) && + add_packet_body_byte(phbody, 3) && add_packet_body_byte(phbody, 1) && + add_packet_body_byte(phbody, key->material.ec.kdf_hash_alg) && + add_packet_body_byte(phbody, key->material.ec.key_wrap_alg); break; default: RNP_LOG("unknown key algorithm: %d", (int) key->alg); res = false; } + if (res && key->version == PGP_V5) { + res = add_packet_body_uint32(&hbody, hbody_alg.len) && + add_packet_body(&hbody, hbody_alg.data, hbody_alg.len); + } /* get ownership on written data on success*/ if (res) { key->hashed_data = hbody.data; key->hashed_len = hbody.len; + if (key->version == PGP_V5) { + free_packet_body(&hbody_alg); + } return true; } error: + if (key->version == PGP_V5) { + free_packet_body(&hbody_alg); + } free_packet_body(&hbody); return false; } @@ -1953,6 +1979,8 @@ stream_write_key(pgp_key_pkt_t *key, pgp_dest_t *dst) } if (is_secret_key_pkt(key->tag)) { + pgp_version_t ver = key->hashed_len > 0 ? (pgp_version_t) key->hashed_data[0] : PGP_V4; + /* secret key fields should be pre-populated in sec_data field */ if (!key->sec_data || !key->sec_len) { RNP_LOG("secret key data is not populated"); @@ -1962,6 +1990,19 @@ stream_write_key(pgp_key_pkt_t *key, pgp_dest_t *dst) if (!(res = add_packet_body_byte(&pktbody, key->sec_protection.s2k.usage))) { goto finish; } + + if (ver == PGP_V5) { + uint8_t opt_cnt = 0; + switch (key->sec_protection.s2k.usage) { + case PGP_S2KU_ENCRYPTED_AND_HASHED: + case PGP_S2KU_ENCRYPTED: + opt_cnt = 3; + } + if (!(res = add_packet_body_byte(&pktbody, opt_cnt))) { + goto finish; + } + } + switch (key->sec_protection.s2k.usage) { case PGP_S2KU_NONE: res = true; @@ -1986,6 +2027,20 @@ stream_write_key(pgp_key_pkt_t *key, pgp_dest_t *dst) res = false; goto finish; } + + if (ver == PGP_V5) { + uint32_t sec_len = key->sec_len; + switch (key->sec_protection.s2k.usage) { + case PGP_S2KU_NONE: + case PGP_S2KU_ENCRYPTED: + sec_len = MAX(sec_len, sizeof(uint16_t)) - sizeof(uint16_t); // checksum + break; + } + res = add_packet_body_uint32(&pktbody, sec_len); + if (!res) { + goto finish; + } + } res = add_packet_body(&pktbody, key->sec_data, key->sec_len); } @@ -2029,7 +2084,7 @@ stream_parse_key(pgp_source_t *src, pgp_key_pkt_t *key) key->tag = pkt.tag; /* version */ - if (!get_packet_body_byte(&pkt, &ver) || (ver < PGP_V2) || (ver > PGP_V4)) { + if (!get_packet_body_byte(&pkt, &ver) || (ver < PGP_V2) || (ver > PGP_V5)) { RNP_LOG("wrong key packet version"); goto finish; } @@ -2052,6 +2107,11 @@ stream_parse_key(pgp_source_t *src, pgp_key_pkt_t *key) key->alg = (pgp_pubkey_alg_t) alg; key->material.alg = (pgp_pubkey_alg_t) alg; + uint32_t pub_data_len; + if (ver == PGP_V5 && !get_packet_body_uint32(&pkt, &pub_data_len)) { + goto finish; + } + /* v3 keys must be RSA-only */ if ((key->version < PGP_V4) && !is_rsa_key_alg(key->alg)) { RNP_LOG("wrong v3 pk algorithm"); @@ -2137,6 +2197,12 @@ stream_parse_key(pgp_source_t *src, pgp_key_pkt_t *key) key->sec_protection.s2k.usage = (pgp_s2k_usage_t) usage; key->sec_protection.cipher_mode = PGP_CIPHER_MODE_CFB; + if (ver == PGP_V5) { + uint8_t sec_opt_cnt = 0; + if (!get_packet_body_byte(&pkt, &sec_opt_cnt)) { + goto finish; + } + } switch (key->sec_protection.s2k.usage) { case PGP_S2KU_NONE: break; @@ -2172,6 +2238,19 @@ stream_parse_key(pgp_source_t *src, pgp_key_pkt_t *key) /* encrypted/cleartext secret MPIs are left */ size_t sec_len = pkt.len - pkt.pos; + if (ver == PGP_V5) { + uint32_t sec_length = sec_len; + if (!get_packet_body_uint32(&pkt, &sec_length)) { + goto finish; + } + switch (key->sec_protection.s2k.usage) { + case PGP_S2KU_NONE: + case PGP_S2KU_ENCRYPTED: + sec_length += sizeof(uint16_t); // checksum + break; + } + sec_len = sec_length; + } if (!(key->sec_data = (uint8_t *) calloc(1, sec_len))) { res = RNP_ERROR_OUT_OF_MEMORY; goto finish; diff --git a/src/librepgp/stream-packet.h b/src/librepgp/stream-packet.h index ef8bb352c8..84457cf351 100644 --- a/src/librepgp/stream-packet.h +++ b/src/librepgp/stream-packet.h @@ -63,6 +63,7 @@ uint16_t read_uint16(const uint8_t *buf); uint32_t read_uint32(const uint8_t *buf); void write_uint16(uint8_t *buf, uint16_t val); +void write_uint32(uint8_t *buf, uint32_t val); /** @brief write new packet length * @param buf pre-allocated buffer, must have 5 bytes diff --git a/src/librepgp/stream-sig.cpp b/src/librepgp/stream-sig.cpp index e5217d48e1..8b8f5ec63e 100644 --- a/src/librepgp/stream-sig.cpp +++ b/src/librepgp/stream-sig.cpp @@ -849,7 +849,7 @@ signature_fill_hashed_data(pgp_signature_t *sig) return false; } /* we don't have a need to write v2-v3 signatures */ - if ((sig->version < PGP_V2) || (sig->version > PGP_V4)) { + if ((sig->version < PGP_V2) || (sig->version > PGP_V5)) { RNP_LOG("don't know version %d", (int) sig->version); return false; } @@ -884,7 +884,7 @@ signature_fill_hashed_data(pgp_signature_t *sig) bool signature_hash_key(const pgp_key_pkt_t *key, pgp_hash_t *hash) { - uint8_t hdr[3] = {0x99, 0x00, 0x00}; + uint8_t hdr[5] = {0x99, 0x00, 0x00, 0x00, 0x00}; pgp_key_pkt_t keycp = {}; bool res = false; @@ -894,9 +894,19 @@ signature_hash_key(const pgp_key_pkt_t *key, pgp_hash_t *hash) } if (key->hashed_data) { - write_uint16(hdr + 1, key->hashed_len); - return !pgp_hash_add(hash, hdr, 3) && - !pgp_hash_add(hash, key->hashed_data, key->hashed_len); + if (key->version == PGP_V5) { + hdr[0] = 0x9A; + write_uint32(hdr + 1, key->hashed_len); + if (pgp_hash_add(hash, hdr, 5)) { + return false; + } + } else { + write_uint16(hdr + 1, key->hashed_len); + if (pgp_hash_add(hash, hdr, 3)) { + return false; + } + } + return !pgp_hash_add(hash, key->hashed_data, key->hashed_len); } /* call self recursively if hashed data is not filled, to overcome const restriction */ diff --git a/src/tests/data/test_stream_key_load/ecc-25519-v5.asc b/src/tests/data/test_stream_key_load/ecc-25519-v5.asc new file mode 100644 index 0000000000..a26385d741 --- /dev/null +++ b/src/tests/data/test_stream_key_load/ecc-25519-v5.asc @@ -0,0 +1,16 @@ +-----BEGIN PGP PRIVATE KEY BLOCK----- + +lGEFXJH05BYAAAAtCSsGAQQB2kcPAQEHQFhZlVcVVtwf+21xNQPX+ecMJJBL0MPd +fj75iux+my8QAAAAAAAiAQCHZ1SnSUmWqxEsoI6facIVZQu6mph3cBFzzTvcm5lA +Ng5ctBhlbW1hLmdvbGRtYW5AZXhhbXBsZS5uZXSIlgUTFggASCIhBRk0e8mHJGQC +X5nfPsLgAA7ZiEiS4fez6kyUAJFZVptUBQJckfTkAhsDBQsJCAcCAyICAQYVCgkI +CwIEFgIDAQIeBwIXgAAA9cAA/jiR3yMsZMeEQ40u6uzEoXa6UXeV/S3wwJAXRJy9 +M8s0AP9vuL/7AyTfFXwwzSjDnYmzS0qAhbLDQ643N+MXGBJ2BZxmBVyR9OQSAAAA +MgorBgEEAZdVAQUBAQdA+nysrzml2UCweAqtpDuncSPlvrcBWKU0yfU0YvYWWAoD +AQgHAAAAAAAiAP9OdAPppjU1WwpqjIItkxr+VPQRT8Zm/Riw7U3F6v3OiBFHiHoF +GBYIACwiIQUZNHvJhyRkAl+Z3z7C4AAO2YhIkuH3s+pMlACRWVabVAUCXJH05AIb +DAAAOSQBAP4BOOIR/sGLNMOfeb5fPs/02QMieoiSjIBnijhob2U5AQC+RtOHCHx7 +TcIYl5/Uyoi+FOvPLcNw4hOv2nwUzSSVAw== +=IiS2 +-----END PGP PRIVATE KEY BLOCK----- + diff --git a/src/tests/streams.cpp b/src/tests/streams.cpp index dbcb1fcc6a..c68b6dc975 100644 --- a/src/tests/streams.cpp +++ b/src/tests/streams.cpp @@ -781,6 +781,21 @@ TEST_F(rnp_tests, test_stream_key_load) assert_non_null(list_front(key->subkeys)); key_sequence_destroy(&keyseq); src_close(&keysrc); + + /* curve 25519 ecc V5 */ + assert_rnp_success(init_file_src(&keysrc, "data/test_stream_key_load/ecc-25519-v5.asc")); + assert_rnp_success(process_pgp_keys(&keysrc, &keyseq)); + assert_int_equal(list_length(keyseq.keys), 1); + assert_non_null(key = (pgp_transferable_key_t *) list_front(keyseq.keys)); + assert_rnp_success(pgp_fingerprint(&keyfp, &key->key)); + assert_true( + cmp_keyfp(&keyfp, "19347bc9872464025f99df3ec2e0000ed9884892e1f7b3ea4c94009159569b54")); + assert_rnp_success(pgp_keyid(keyid, PGP_KEY_ID_SIZE, &key->key)); + assert_true(cmp_keyid(keyid, "19347bc987246402")); + assert_int_equal(list_length(key->subkeys), 1); + assert_non_null(list_front(key->subkeys)); + key_sequence_destroy(&keyseq); + src_close(&keysrc); } static void