/* * Copyright (c) 2018-2023, [Ribose Inc](https://www.ribose.com). * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include #include #ifdef HAVE_UNISTD_H #include #else #include "uniwin.h" #endif #include #include #include #include #include "types.h" #include "stream-sig.h" #include "stream-packet.h" #include "stream-armor.h" #include "pgp-key.h" #include "crypto/signatures.h" #include void signature_hash_key(const pgp_key_pkt_t &key, rnp::Hash &hash) { uint8_t hdr[3] = {0x99, 0x00, 0x00}; if (key.hashed_data) { write_uint16(hdr + 1, key.hashed_len); hash.add(hdr, 3); hash.add(key.hashed_data, key.hashed_len); return; } /* call self recursively if hashed data is not filled, to overcome const restriction */ pgp_key_pkt_t keycp(key, true); keycp.fill_hashed_data(); signature_hash_key(keycp, hash); } void signature_hash_userid(const pgp_userid_pkt_t &uid, rnp::Hash &hash, pgp_version_t sigver) { if (sigver < PGP_V4) { hash.add(uid.uid, uid.uid_len); return; } uint8_t hdr[5] = {0}; switch (uid.tag) { case PGP_PKT_USER_ID: hdr[0] = 0xB4; break; case PGP_PKT_USER_ATTR: hdr[0] = 0xD1; break; default: RNP_LOG("wrong uid"); throw rnp::rnp_exception(RNP_ERROR_BAD_PARAMETERS); } STORE32BE(hdr + 1, uid.uid_len); hash.add(hdr, 5); hash.add(uid.uid, uid.uid_len); } std::unique_ptr signature_hash_certification(const pgp_signature_t & sig, const pgp_key_pkt_t & key, const pgp_userid_pkt_t &userid) { auto hash = signature_init(key.material, sig.halg); signature_hash_key(key, *hash); signature_hash_userid(userid, *hash, sig.version); return hash; } std::unique_ptr signature_hash_binding(const pgp_signature_t &sig, const pgp_key_pkt_t & key, const pgp_key_pkt_t & subkey) { auto hash = signature_init(key.material, sig.halg); signature_hash_key(key, *hash); signature_hash_key(subkey, *hash); return hash; } std::unique_ptr signature_hash_direct(const pgp_signature_t &sig, const pgp_key_pkt_t &key) { auto hash = signature_init(key.material, sig.halg); signature_hash_key(key, *hash); return hash; } rnp_result_t process_pgp_signatures(pgp_source_t &src, pgp_signature_list_t &sigs) { sigs.clear(); /* Allow binary or armored input, including multiple armored messages */ rnp::ArmoredSource armor( src, rnp::ArmoredSource::AllowBinary | rnp::ArmoredSource::AllowMultiple); /* read sequence of OpenPGP signatures */ while (!armor.error()) { if (armor.eof() && armor.multiple()) { armor.restart(); } if (armor.eof()) { break; } int ptag = stream_pkt_type(armor.src()); if (ptag != PGP_PKT_SIGNATURE) { RNP_LOG("wrong signature tag: %d", ptag); sigs.clear(); return RNP_ERROR_BAD_FORMAT; } sigs.emplace_back(); rnp_result_t ret = sigs.back().parse(armor.src()); if (ret) { sigs.clear(); return ret; } } if (armor.error()) { sigs.clear(); return RNP_ERROR_READ; } return RNP_SUCCESS; } pgp_sig_subpkt_t::pgp_sig_subpkt_t(const pgp_sig_subpkt_t &src) { type = src.type; len = src.len; data = (uint8_t *) malloc(len); if (!data) { throw std::bad_alloc(); } memcpy(data, src.data, len); critical = src.critical; hashed = src.hashed; parsed = false; parse(); } pgp_sig_subpkt_t::pgp_sig_subpkt_t(pgp_sig_subpkt_t &&src) { type = src.type; len = src.len; data = src.data; src.data = NULL; critical = src.critical; hashed = src.hashed; parsed = src.parsed; memcpy(&fields, &src.fields, sizeof(fields)); src.fields = {}; } pgp_sig_subpkt_t & pgp_sig_subpkt_t::operator=(pgp_sig_subpkt_t &&src) { if (&src == this) { return *this; } if (parsed && (type == PGP_SIG_SUBPKT_EMBEDDED_SIGNATURE)) { delete fields.sig; } type = src.type; len = src.len; free(data); data = src.data; src.data = NULL; critical = src.critical; hashed = src.hashed; parsed = src.parsed; fields = src.fields; src.fields = {}; return *this; } pgp_sig_subpkt_t & pgp_sig_subpkt_t::operator=(const pgp_sig_subpkt_t &src) { if (&src == this) { return *this; } if (parsed && (type == PGP_SIG_SUBPKT_EMBEDDED_SIGNATURE)) { delete fields.sig; } type = src.type; len = src.len; free(data); data = (uint8_t *) malloc(len); if (!data) { throw std::bad_alloc(); } memcpy(data, src.data, len); critical = src.critical; hashed = src.hashed; parsed = false; fields = {}; parse(); return *this; } bool pgp_sig_subpkt_t::parse() { bool oklen = true; bool checked = true; switch (type) { case PGP_SIG_SUBPKT_CREATION_TIME: if (!hashed) { RNP_LOG("creation time subpacket must be hashed"); checked = false; } if ((oklen = len == 4)) { fields.create = read_uint32(data); } break; case PGP_SIG_SUBPKT_EXPIRATION_TIME: case PGP_SIG_SUBPKT_KEY_EXPIRY: if ((oklen = len == 4)) { fields.expiry = read_uint32(data); } break; case PGP_SIG_SUBPKT_EXPORT_CERT: if ((oklen = len == 1)) { fields.exportable = data[0] != 0; } break; case PGP_SIG_SUBPKT_TRUST: if ((oklen = len == 2)) { fields.trust.level = data[0]; fields.trust.amount = data[1]; } break; case PGP_SIG_SUBPKT_REGEXP: fields.regexp.str = (const char *) data; fields.regexp.len = len; break; case PGP_SIG_SUBPKT_REVOCABLE: if ((oklen = len == 1)) { fields.revocable = data[0] != 0; } break; case PGP_SIG_SUBPKT_PREFERRED_SKA: case PGP_SIG_SUBPKT_PREFERRED_HASH: case PGP_SIG_SUBPKT_PREF_COMPRESS: case PGP_SIG_SUBPKT_PREFERRED_AEAD: fields.preferred.arr = data; fields.preferred.len = len; break; case PGP_SIG_SUBPKT_REVOCATION_KEY: if ((oklen = len == 22)) { fields.revocation_key.klass = data[0]; fields.revocation_key.pkalg = (pgp_pubkey_alg_t) data[1]; fields.revocation_key.fp = &data[2]; } break; case PGP_SIG_SUBPKT_ISSUER_KEY_ID: if ((oklen = len == 8)) { fields.issuer = data; } break; case PGP_SIG_SUBPKT_NOTATION_DATA: if ((oklen = len >= 8)) { memcpy(fields.notation.flags, data, 4); fields.notation.human = fields.notation.flags[0] & 0x80; fields.notation.nlen = read_uint16(&data[4]); fields.notation.vlen = read_uint16(&data[6]); if (len != 8 + fields.notation.nlen + fields.notation.vlen) { oklen = false; } else { fields.notation.name = data + 8; fields.notation.value = fields.notation.name + fields.notation.nlen; } } break; case PGP_SIG_SUBPKT_KEYSERV_PREFS: if ((oklen = len >= 1)) { fields.ks_prefs.no_modify = (data[0] & 0x80) != 0; } break; case PGP_SIG_SUBPKT_PREF_KEYSERV: fields.preferred_ks.uri = (const char *) data; fields.preferred_ks.len = len; break; case PGP_SIG_SUBPKT_PRIMARY_USER_ID: if ((oklen = len == 1)) { fields.primary_uid = data[0] != 0; } break; case PGP_SIG_SUBPKT_POLICY_URI: fields.policy.uri = (const char *) data; fields.policy.len = len; break; case PGP_SIG_SUBPKT_KEY_FLAGS: if ((oklen = len >= 1)) { fields.key_flags = data[0]; } break; case PGP_SIG_SUBPKT_SIGNERS_USER_ID: fields.signer.uid = (const char *) data; fields.signer.len = len; break; case PGP_SIG_SUBPKT_REVOCATION_REASON: if ((oklen = len >= 1)) { fields.revocation_reason.code = (pgp_revocation_type_t) data[0]; fields.revocation_reason.str = (const char *) &data[1]; fields.revocation_reason.len = len - 1; } break; case PGP_SIG_SUBPKT_FEATURES: if ((oklen = len >= 1)) { fields.features = data[0]; } break; case PGP_SIG_SUBPKT_SIGNATURE_TARGET: if ((oklen = len >= 18)) { fields.sig_target.pkalg = (pgp_pubkey_alg_t) data[0]; fields.sig_target.halg = (pgp_hash_alg_t) data[1]; fields.sig_target.hash = &data[2]; fields.sig_target.hlen = len - 2; } break; case PGP_SIG_SUBPKT_EMBEDDED_SIGNATURE: try { /* parse signature */ pgp_packet_body_t pkt(data, len); pgp_signature_t sig; oklen = checked = !sig.parse(pkt); if (checked) { fields.sig = new pgp_signature_t(std::move(sig)); } break; } catch (const std::exception &e) { RNP_LOG("%s", e.what()); return false; } case PGP_SIG_SUBPKT_ISSUER_FPR: if ((oklen = len >= 21)) { fields.issuer_fp.version = data[0]; fields.issuer_fp.fp = &data[1]; fields.issuer_fp.len = len - 1; } break; case PGP_SIG_SUBPKT_PRIVATE_100: case PGP_SIG_SUBPKT_PRIVATE_101: case PGP_SIG_SUBPKT_PRIVATE_102: case PGP_SIG_SUBPKT_PRIVATE_103: case PGP_SIG_SUBPKT_PRIVATE_104: case PGP_SIG_SUBPKT_PRIVATE_105: case PGP_SIG_SUBPKT_PRIVATE_106: case PGP_SIG_SUBPKT_PRIVATE_107: case PGP_SIG_SUBPKT_PRIVATE_108: case PGP_SIG_SUBPKT_PRIVATE_109: case PGP_SIG_SUBPKT_PRIVATE_110: oklen = true; checked = !critical; if (!checked) { RNP_LOG("unknown critical private subpacket %d", (int) type); } break; case PGP_SIG_SUBPKT_RESERVED_1: case PGP_SIG_SUBPKT_RESERVED_8: case PGP_SIG_SUBPKT_PLACEHOLDER: case PGP_SIG_SUBPKT_RESERVED_13: case PGP_SIG_SUBPKT_RESERVED_14: case PGP_SIG_SUBPKT_RESERVED_15: case PGP_SIG_SUBPKT_RESERVED_17: case PGP_SIG_SUBPKT_RESERVED_18: case PGP_SIG_SUBPKT_RESERVED_19: /* do not report reserved/placeholder subpacket */ return !critical; default: RNP_LOG("unknown subpacket : %d", (int) type); return !critical; } if (!oklen) { RNP_LOG("wrong len %d of subpacket type %d", (int) len, (int) type); } else { parsed = 1; } return oklen && checked; } pgp_sig_subpkt_t::~pgp_sig_subpkt_t() { if (parsed && (type == PGP_SIG_SUBPKT_EMBEDDED_SIGNATURE)) { delete fields.sig; } free(data); } pgp_signature_t::pgp_signature_t(const pgp_signature_t &src) { version = src.version; type_ = src.type_; palg = src.palg; halg = src.halg; memcpy(lbits, src.lbits, sizeof(src.lbits)); creation_time = src.creation_time; signer = src.signer; hashed_len = src.hashed_len; hashed_data = NULL; if (src.hashed_data) { if (!(hashed_data = (uint8_t *) malloc(hashed_len))) { throw std::bad_alloc(); } memcpy(hashed_data, src.hashed_data, hashed_len); } material_len = src.material_len; material_buf = NULL; if (src.material_buf) { if (!(material_buf = (uint8_t *) malloc(material_len))) { throw std::bad_alloc(); } memcpy(material_buf, src.material_buf, material_len); } subpkts = src.subpkts; } pgp_signature_t::pgp_signature_t(pgp_signature_t &&src) { version = src.version; type_ = src.type_; palg = src.palg; halg = src.halg; memcpy(lbits, src.lbits, sizeof(src.lbits)); creation_time = src.creation_time; signer = src.signer; hashed_len = src.hashed_len; hashed_data = src.hashed_data; src.hashed_data = NULL; material_len = src.material_len; material_buf = src.material_buf; src.material_buf = NULL; subpkts = std::move(src.subpkts); } pgp_signature_t & pgp_signature_t::operator=(pgp_signature_t &&src) { if (this == &src) { return *this; } version = src.version; type_ = src.type_; palg = src.palg; halg = src.halg; memcpy(lbits, src.lbits, sizeof(src.lbits)); creation_time = src.creation_time; signer = src.signer; hashed_len = src.hashed_len; free(hashed_data); hashed_data = src.hashed_data; src.hashed_data = NULL; material_len = src.material_len; free(material_buf); material_buf = src.material_buf; src.material_buf = NULL; subpkts = std::move(src.subpkts); return *this; } pgp_signature_t & pgp_signature_t::operator=(const pgp_signature_t &src) { if (this == &src) { return *this; } version = src.version; type_ = src.type_; palg = src.palg; halg = src.halg; memcpy(lbits, src.lbits, sizeof(src.lbits)); creation_time = src.creation_time; signer = src.signer; hashed_len = src.hashed_len; free(hashed_data); hashed_data = NULL; if (src.hashed_data) { if (!(hashed_data = (uint8_t *) malloc(hashed_len))) { throw std::bad_alloc(); } memcpy(hashed_data, src.hashed_data, hashed_len); } material_len = src.material_len; free(material_buf); material_buf = NULL; if (src.material_buf) { if (!(material_buf = (uint8_t *) malloc(material_len))) { throw std::bad_alloc(); } memcpy(material_buf, src.material_buf, material_len); } subpkts = src.subpkts; return *this; } bool pgp_signature_t::operator==(const pgp_signature_t &src) const { if ((lbits[0] != src.lbits[0]) || (lbits[1] != src.lbits[1])) { return false; } if ((hashed_len != src.hashed_len) || memcmp(hashed_data, src.hashed_data, hashed_len)) { return false; } return (material_len == src.material_len) && !memcmp(material_buf, src.material_buf, material_len); } bool pgp_signature_t::operator!=(const pgp_signature_t &src) const { return !(*this == src); } pgp_signature_t::~pgp_signature_t() { free(hashed_data); free(material_buf); } pgp_sig_id_t pgp_signature_t::get_id() const { auto hash = rnp::Hash::create(PGP_HASH_SHA1); hash->add(hashed_data, hashed_len); hash->add(material_buf, material_len); pgp_sig_id_t res = {0}; static_assert(std::tuple_size::value == PGP_SHA1_HASH_SIZE, "pgp_sig_id_t size mismatch"); hash->finish(res.data()); return res; } pgp_sig_subpkt_t * pgp_signature_t::get_subpkt(pgp_sig_subpacket_type_t stype, bool hashed) { if (version < PGP_V4) { return NULL; } for (auto &subpkt : subpkts) { /* if hashed is false then accept any hashed/not hashed subpacket */ if ((subpkt.type == stype) && (!hashed || subpkt.hashed)) { return &subpkt; } } return NULL; } const pgp_sig_subpkt_t * pgp_signature_t::get_subpkt(pgp_sig_subpacket_type_t stype, bool hashed) const { if (version < PGP_V4) { return NULL; } for (auto &subpkt : subpkts) { /* if hashed is false then accept any hashed/not hashed subpacket */ if ((subpkt.type == stype) && (!hashed || subpkt.hashed)) { return &subpkt; } } return NULL; } bool pgp_signature_t::has_subpkt(pgp_sig_subpacket_type_t stype, bool hashed) const { if (version < PGP_V4) { return false; } for (auto &subpkt : subpkts) { /* if hashed is false then accept any hashed/not hashed subpacket */ if ((subpkt.type == stype) && (!hashed || subpkt.hashed)) { return true; } } return false; } bool pgp_signature_t::has_keyid() const { return (version < PGP_V4) || has_subpkt(PGP_SIG_SUBPKT_ISSUER_KEY_ID, false) || has_keyfp(); } pgp_key_id_t pgp_signature_t::keyid() const noexcept { /* version 3 uses signature field */ if (version < PGP_V4) { return signer; } /* version 4 and up use subpackets */ pgp_key_id_t res{}; static_assert(std::tuple_size::value == PGP_KEY_ID_SIZE, "pgp_key_id_t size mismatch"); const pgp_sig_subpkt_t *subpkt = get_subpkt(PGP_SIG_SUBPKT_ISSUER_KEY_ID, false); if (subpkt) { memcpy(res.data(), subpkt->fields.issuer, PGP_KEY_ID_SIZE); return res; } if ((subpkt = get_subpkt(PGP_SIG_SUBPKT_ISSUER_FPR))) { memcpy(res.data(), subpkt->fields.issuer_fp.fp + subpkt->fields.issuer_fp.len - PGP_KEY_ID_SIZE, PGP_KEY_ID_SIZE); return res; } return res; } void pgp_signature_t::set_keyid(const pgp_key_id_t &id) { if (version < PGP_V4) { signer = id; return; } static_assert(std::tuple_size::type>::value == PGP_KEY_ID_SIZE, "pgp_key_id_t size mismatch"); pgp_sig_subpkt_t &subpkt = add_subpkt(PGP_SIG_SUBPKT_ISSUER_KEY_ID, PGP_KEY_ID_SIZE, true); subpkt.parsed = true; subpkt.hashed = false; memcpy(subpkt.data, id.data(), PGP_KEY_ID_SIZE); subpkt.fields.issuer = subpkt.data; } bool pgp_signature_t::has_keyfp() const { if (version < PGP_V4) { return false; } const pgp_sig_subpkt_t *subpkt = get_subpkt(PGP_SIG_SUBPKT_ISSUER_FPR); return subpkt && (subpkt->fields.issuer_fp.len <= PGP_FINGERPRINT_SIZE); } pgp_fingerprint_t pgp_signature_t::keyfp() const noexcept { pgp_fingerprint_t res{}; if (version < PGP_V4) { return res; } const pgp_sig_subpkt_t *subpkt = get_subpkt(PGP_SIG_SUBPKT_ISSUER_FPR); if (!subpkt || (subpkt->fields.issuer_fp.len > sizeof(res.fingerprint))) { return res; } res.length = subpkt->fields.issuer_fp.len; memcpy(res.fingerprint, subpkt->fields.issuer_fp.fp, subpkt->fields.issuer_fp.len); return res; } void pgp_signature_t::set_keyfp(const pgp_fingerprint_t &fp) { if (version < PGP_V4) { throw rnp::rnp_exception(RNP_ERROR_BAD_STATE); } pgp_sig_subpkt_t &subpkt = add_subpkt(PGP_SIG_SUBPKT_ISSUER_FPR, 1 + fp.length, true); subpkt.parsed = true; subpkt.hashed = true; subpkt.data[0] = 4; memcpy(subpkt.data + 1, fp.fingerprint, fp.length); subpkt.fields.issuer_fp.len = fp.length; subpkt.fields.issuer_fp.version = subpkt.data[0]; subpkt.fields.issuer_fp.fp = subpkt.data + 1; } uint32_t pgp_signature_t::creation() const { if (version < PGP_V4) { return creation_time; } const pgp_sig_subpkt_t *subpkt = get_subpkt(PGP_SIG_SUBPKT_CREATION_TIME); return subpkt ? subpkt->fields.create : 0; } void pgp_signature_t::set_creation(uint32_t ctime) { if (version < PGP_V4) { creation_time = ctime; return; } pgp_sig_subpkt_t &subpkt = add_subpkt(PGP_SIG_SUBPKT_CREATION_TIME, 4, true); subpkt.parsed = true; subpkt.hashed = true; STORE32BE(subpkt.data, ctime); subpkt.fields.create = ctime; } uint32_t pgp_signature_t::expiration() const { const pgp_sig_subpkt_t *subpkt = get_subpkt(PGP_SIG_SUBPKT_EXPIRATION_TIME); return subpkt ? subpkt->fields.expiry : 0; } void pgp_signature_t::set_expiration(uint32_t etime) { if (version < PGP_V4) { throw rnp::rnp_exception(RNP_ERROR_BAD_STATE); } pgp_sig_subpkt_t &subpkt = add_subpkt(PGP_SIG_SUBPKT_EXPIRATION_TIME, 4, true); subpkt.parsed = true; subpkt.hashed = true; STORE32BE(subpkt.data, etime); subpkt.fields.expiry = etime; } uint32_t pgp_signature_t::key_expiration() const { const pgp_sig_subpkt_t *subpkt = get_subpkt(PGP_SIG_SUBPKT_KEY_EXPIRY); return subpkt ? subpkt->fields.expiry : 0; } void pgp_signature_t::set_key_expiration(uint32_t etime) { if (version < PGP_V4) { throw rnp::rnp_exception(RNP_ERROR_BAD_STATE); } pgp_sig_subpkt_t &subpkt = add_subpkt(PGP_SIG_SUBPKT_KEY_EXPIRY, 4, true); subpkt.parsed = true; subpkt.hashed = true; STORE32BE(subpkt.data, etime); subpkt.fields.expiry = etime; } uint8_t pgp_signature_t::key_flags() const { const pgp_sig_subpkt_t *subpkt = get_subpkt(PGP_SIG_SUBPKT_KEY_FLAGS); return subpkt ? subpkt->fields.key_flags : 0; } void pgp_signature_t::set_key_flags(uint8_t flags) { pgp_sig_subpkt_t &subpkt = add_subpkt(PGP_SIG_SUBPKT_KEY_FLAGS, 1, true); subpkt.parsed = true; subpkt.hashed = true; subpkt.data[0] = flags; subpkt.fields.key_flags = flags; } bool pgp_signature_t::primary_uid() const { const pgp_sig_subpkt_t *subpkt = get_subpkt(PGP_SIG_SUBPKT_PRIMARY_USER_ID); return subpkt ? subpkt->fields.primary_uid : false; } void pgp_signature_t::set_primary_uid(bool primary) { pgp_sig_subpkt_t &subpkt = add_subpkt(PGP_SIG_SUBPKT_PRIMARY_USER_ID, 1, true); subpkt.parsed = true; subpkt.hashed = true; subpkt.data[0] = primary; subpkt.fields.primary_uid = primary; } std::vector pgp_signature_t::preferred(pgp_sig_subpacket_type_t type) const { const pgp_sig_subpkt_t *subpkt = get_subpkt(type); return subpkt ? std::vector(subpkt->fields.preferred.arr, subpkt->fields.preferred.arr + subpkt->fields.preferred.len) : std::vector(); } void pgp_signature_t::set_preferred(const std::vector &data, pgp_sig_subpacket_type_t type) { if (version < PGP_V4) { throw rnp::rnp_exception(RNP_ERROR_BAD_STATE); } if (data.empty()) { pgp_sig_subpkt_t *subpkt = get_subpkt(type); if (subpkt) { remove_subpkt(subpkt); } return; } pgp_sig_subpkt_t &subpkt = add_subpkt(type, data.size(), true); subpkt.parsed = true; subpkt.hashed = true; memcpy(subpkt.data, data.data(), data.size()); subpkt.fields.preferred.arr = subpkt.data; subpkt.fields.preferred.len = data.size(); } std::vector pgp_signature_t::preferred_symm_algs() const { return preferred(PGP_SIG_SUBPKT_PREFERRED_SKA); } void pgp_signature_t::set_preferred_symm_algs(const std::vector &algs) { set_preferred(algs, PGP_SIG_SUBPKT_PREFERRED_SKA); } std::vector pgp_signature_t::preferred_hash_algs() const { return preferred(PGP_SIG_SUBPKT_PREFERRED_HASH); } void pgp_signature_t::set_preferred_hash_algs(const std::vector &algs) { set_preferred(algs, PGP_SIG_SUBPKT_PREFERRED_HASH); } std::vector pgp_signature_t::preferred_z_algs() const { return preferred(PGP_SIG_SUBPKT_PREF_COMPRESS); } void pgp_signature_t::set_preferred_z_algs(const std::vector &algs) { set_preferred(algs, PGP_SIG_SUBPKT_PREF_COMPRESS); } uint8_t pgp_signature_t::key_server_prefs() const { const pgp_sig_subpkt_t *subpkt = get_subpkt(PGP_SIG_SUBPKT_KEYSERV_PREFS); return subpkt ? subpkt->data[0] : 0; } void pgp_signature_t::set_key_server_prefs(uint8_t prefs) { if (version < PGP_V4) { throw rnp::rnp_exception(RNP_ERROR_BAD_STATE); } pgp_sig_subpkt_t &subpkt = add_subpkt(PGP_SIG_SUBPKT_KEYSERV_PREFS, 1, true); subpkt.parsed = true; subpkt.hashed = true; subpkt.data[0] = prefs; subpkt.fields.ks_prefs.no_modify = prefs & 0x80; } std::string pgp_signature_t::key_server() const { const pgp_sig_subpkt_t *subpkt = get_subpkt(PGP_SIG_SUBPKT_PREF_KEYSERV); return subpkt ? std::string((char *) subpkt->data, subpkt->len) : ""; } void pgp_signature_t::set_key_server(const std::string &uri) { if (version < PGP_V4) { throw rnp::rnp_exception(RNP_ERROR_BAD_STATE); } if (uri.empty()) { pgp_sig_subpkt_t *subpkt = get_subpkt(PGP_SIG_SUBPKT_PREF_KEYSERV); if (subpkt) { remove_subpkt(subpkt); } return; } pgp_sig_subpkt_t &subpkt = add_subpkt(PGP_SIG_SUBPKT_PREF_KEYSERV, uri.size(), true); subpkt.parsed = true; subpkt.hashed = true; memcpy(subpkt.data, uri.data(), uri.size()); subpkt.fields.preferred_ks.uri = (char *) subpkt.data; subpkt.fields.preferred_ks.len = uri.size(); } uint8_t pgp_signature_t::trust_level() const { const pgp_sig_subpkt_t *subpkt = get_subpkt(PGP_SIG_SUBPKT_TRUST); return subpkt ? subpkt->fields.trust.level : 0; } uint8_t pgp_signature_t::trust_amount() const { const pgp_sig_subpkt_t *subpkt = get_subpkt(PGP_SIG_SUBPKT_TRUST); return subpkt ? subpkt->fields.trust.amount : 0; } void pgp_signature_t::set_trust(uint8_t level, uint8_t amount) { pgp_sig_subpkt_t &subpkt = add_subpkt(PGP_SIG_SUBPKT_TRUST, 2, true); subpkt.parsed = true; subpkt.hashed = true; subpkt.data[0] = level; subpkt.data[1] = amount; subpkt.fields.trust.level = level; subpkt.fields.trust.amount = amount; } bool pgp_signature_t::revocable() const { const pgp_sig_subpkt_t *subpkt = get_subpkt(PGP_SIG_SUBPKT_REVOCABLE); return subpkt ? subpkt->fields.revocable : true; } void pgp_signature_t::set_revocable(bool status) { pgp_sig_subpkt_t &subpkt = add_subpkt(PGP_SIG_SUBPKT_REVOCABLE, 1, true); subpkt.parsed = true; subpkt.hashed = true; subpkt.data[0] = status; subpkt.fields.revocable = status; } std::string pgp_signature_t::revocation_reason() const { const pgp_sig_subpkt_t *subpkt = get_subpkt(PGP_SIG_SUBPKT_REVOCATION_REASON); return subpkt ? std::string(subpkt->fields.revocation_reason.str, subpkt->fields.revocation_reason.len) : ""; } pgp_revocation_type_t pgp_signature_t::revocation_code() const { const pgp_sig_subpkt_t *subpkt = get_subpkt(PGP_SIG_SUBPKT_REVOCATION_REASON); return subpkt ? subpkt->fields.revocation_reason.code : PGP_REVOCATION_NO_REASON; } void pgp_signature_t::set_revocation_reason(pgp_revocation_type_t code, const std::string &reason) { size_t datalen = 1 + reason.size(); pgp_sig_subpkt_t &subpkt = add_subpkt(PGP_SIG_SUBPKT_REVOCATION_REASON, datalen, true); subpkt.hashed = true; subpkt.data[0] = code; memcpy(subpkt.data + 1, reason.data(), reason.size()); if (!subpkt.parse()) { throw rnp::rnp_exception(RNP_ERROR_BAD_STATE); } } pgp_key_feature_t pgp_signature_t::key_get_features() const { const pgp_sig_subpkt_t *subpkt = get_subpkt(PGP_SIG_SUBPKT_FEATURES); return (pgp_key_feature_t)(subpkt ? subpkt->data[0] : 0); } bool pgp_signature_t::key_has_features(pgp_key_feature_t flags) const { const pgp_sig_subpkt_t *subpkt = get_subpkt(PGP_SIG_SUBPKT_FEATURES); return subpkt ? subpkt->data[0] & flags : false; } void pgp_signature_t::set_key_features(pgp_key_feature_t flags) { pgp_sig_subpkt_t &subpkt = add_subpkt(PGP_SIG_SUBPKT_FEATURES, 1, true); subpkt.hashed = true; subpkt.data[0] = flags; subpkt.fields.features = flags; subpkt.parsed = true; } std::string pgp_signature_t::signer_uid() const { const pgp_sig_subpkt_t *subpkt = get_subpkt(PGP_SIG_SUBPKT_SIGNERS_USER_ID); return subpkt ? std::string(subpkt->fields.signer.uid, subpkt->fields.signer.len) : ""; } void pgp_signature_t::set_signer_uid(const std::string &uid) { pgp_sig_subpkt_t &subpkt = add_subpkt(PGP_SIG_SUBPKT_SIGNERS_USER_ID, uid.size(), true); subpkt.hashed = true; memcpy(subpkt.data, uid.data(), uid.size()); subpkt.fields.signer.uid = (const char *) subpkt.data; subpkt.fields.signer.len = subpkt.len; subpkt.parsed = true; } void pgp_signature_t::add_notation(const std::string & name, const std::vector &value, bool human, bool critical) { auto nlen = name.size(); auto vlen = value.size(); if ((nlen > 0xffff) || (vlen > 0xffff)) { RNP_LOG("wrong length"); throw rnp::rnp_exception(RNP_ERROR_BAD_PARAMETERS); } auto &subpkt = add_subpkt(PGP_SIG_SUBPKT_NOTATION_DATA, 8 + nlen + vlen, false); subpkt.hashed = true; subpkt.critical = critical; if (human) { subpkt.data[0] = 0x80; } write_uint16(subpkt.data + 4, nlen); write_uint16(subpkt.data + 6, vlen); memcpy(subpkt.data + 8, name.data(), nlen); memcpy(subpkt.data + 8 + nlen, value.data(), vlen); if (!subpkt.parse()) { throw rnp::rnp_exception(RNP_ERROR_BAD_STATE); } } void pgp_signature_t::add_notation(const std::string &name, const std::string &value, bool critical) { add_notation(name, std::vector(value.begin(), value.end()), true, critical); } void pgp_signature_t::set_embedded_sig(const pgp_signature_t &esig) { pgp_rawpacket_t esigpkt(esig); rnp::MemorySource mem(esigpkt.raw); size_t len = 0; stream_read_pkt_len(&mem.src(), &len); if (!len || (len > 0xffff) || (len >= esigpkt.raw.size())) { RNP_LOG("wrong pkt len"); throw rnp::rnp_exception(RNP_ERROR_BAD_STATE); } pgp_sig_subpkt_t &subpkt = add_subpkt(PGP_SIG_SUBPKT_EMBEDDED_SIGNATURE, len, true); subpkt.hashed = false; size_t skip = esigpkt.raw.size() - len; memcpy(subpkt.data, esigpkt.raw.data() + skip, len); subpkt.fields.sig = new pgp_signature_t(esig); subpkt.parsed = true; } pgp_sig_subpkt_t & pgp_signature_t::add_subpkt(pgp_sig_subpacket_type_t type, size_t datalen, bool reuse) { if (version < PGP_V4) { RNP_LOG("wrong signature version"); throw std::invalid_argument("version"); } uint8_t *newdata = (uint8_t *) calloc(1, datalen); if (!newdata) { RNP_LOG("Allocation failed"); throw std::bad_alloc(); } pgp_sig_subpkt_t *subpkt = NULL; if (reuse && (subpkt = get_subpkt(type))) { *subpkt = {}; } else { subpkts.push_back({}); subpkt = &subpkts.back(); } subpkt->data = newdata; subpkt->type = type; subpkt->len = datalen; return *subpkt; } void pgp_signature_t::remove_subpkt(pgp_sig_subpkt_t *subpkt) { for (auto it = subpkts.begin(); it < subpkts.end(); it++) { if (&*it == subpkt) { subpkts.erase(it); return; } } } bool pgp_signature_t::matches_onepass(const pgp_one_pass_sig_t &onepass) const { if (!has_keyid()) { return false; } return (halg == onepass.halg) && (palg == onepass.palg) && (type_ == onepass.type) && (onepass.keyid == keyid()); } rnp_result_t pgp_signature_t::parse_v3(pgp_packet_body_t &pkt) { /* parse v3-specific fields, not the whole signature */ uint8_t buf[16] = {}; if (!pkt.get(buf, 16)) { RNP_LOG("cannot get enough bytes"); return RNP_ERROR_BAD_FORMAT; } /* length of hashed data, 5 */ if (buf[0] != 5) { RNP_LOG("wrong length of hashed data"); return RNP_ERROR_BAD_FORMAT; } /* hashed data */ free(hashed_data); if (!(hashed_data = (uint8_t *) malloc(5))) { RNP_LOG("allocation failed"); return RNP_ERROR_OUT_OF_MEMORY; } memcpy(hashed_data, &buf[1], 5); hashed_len = 5; /* signature type */ type_ = (pgp_sig_type_t) buf[1]; /* creation time */ creation_time = read_uint32(&buf[2]); /* signer's key id */ static_assert(std::tuple_size::value == PGP_KEY_ID_SIZE, "v3 signer field size mismatch"); memcpy(signer.data(), &buf[6], PGP_KEY_ID_SIZE); /* public key algorithm */ palg = (pgp_pubkey_alg_t) buf[14]; /* hash algorithm */ halg = (pgp_hash_alg_t) buf[15]; return RNP_SUCCESS; } #define MAX_SUBPACKETS 64 bool pgp_signature_t::parse_subpackets(uint8_t *buf, size_t len, bool hashed) { bool res = true; while (len > 0) { if (subpkts.size() >= MAX_SUBPACKETS) { RNP_LOG("too many signature subpackets"); return false; } if (len < 2) { RNP_LOG("got single byte %d", (int) *buf); return false; } /* subpacket length */ size_t splen; if (*buf < 192) { splen = *buf; buf++; len--; } else if (*buf < 255) { splen = ((buf[0] - 192) << 8) + buf[1] + 192; buf += 2; len -= 2; } else { if (len < 5) { RNP_LOG("got 4-byte len but only %d bytes in buffer", (int) len); return false; } splen = read_uint32(&buf[1]); buf += 5; len -= 5; } if (splen < 1) { RNP_LOG("got subpacket with 0 length"); return false; } /* subpacket data */ if (len < splen) { RNP_LOG("got subpacket len %d, while only %d bytes left", (int) splen, (int) len); return false; } pgp_sig_subpkt_t subpkt; if (!(subpkt.data = (uint8_t *) malloc(splen - 1))) { RNP_LOG("subpacket data allocation failed"); return false; } subpkt.type = (pgp_sig_subpacket_type_t)(*buf & 0x7f); subpkt.critical = !!(*buf & 0x80); subpkt.hashed = hashed; subpkt.parsed = 0; memcpy(subpkt.data, buf + 1, splen - 1); subpkt.len = splen - 1; res = res && subpkt.parse(); subpkts.push_back(std::move(subpkt)); len -= splen; buf += splen; } return res; } rnp_result_t pgp_signature_t::parse_v4(pgp_packet_body_t &pkt) { /* parse v4-specific fields, not the whole signature */ uint8_t buf[5]; if (!pkt.get(buf, 5)) { RNP_LOG("cannot get first 5 bytes"); return RNP_ERROR_BAD_FORMAT; } /* signature type */ type_ = (pgp_sig_type_t) buf[0]; /* public key algorithm */ palg = (pgp_pubkey_alg_t) buf[1]; /* hash algorithm */ halg = (pgp_hash_alg_t) buf[2]; /* hashed subpackets length */ uint16_t splen = read_uint16(&buf[3]); /* hashed subpackets length + 2 bytes of length of unhashed subpackets */ if (pkt.left() < (size_t)(splen + 2)) { RNP_LOG("wrong packet or hashed subpackets length"); return RNP_ERROR_BAD_FORMAT; } /* building hashed data */ free(hashed_data); if (!(hashed_data = (uint8_t *) malloc(splen + 6))) { RNP_LOG("allocation failed"); return RNP_ERROR_OUT_OF_MEMORY; } hashed_data[0] = version; memcpy(hashed_data + 1, buf, 5); if (!pkt.get(hashed_data + 6, splen)) { RNP_LOG("cannot get hashed subpackets data"); return RNP_ERROR_BAD_FORMAT; } hashed_len = splen + 6; /* parsing hashed subpackets */ if (!parse_subpackets(hashed_data + 6, splen, true)) { RNP_LOG("failed to parse hashed subpackets"); return RNP_ERROR_BAD_FORMAT; } /* reading unhashed subpackets */ if (!pkt.get(splen)) { RNP_LOG("cannot get unhashed len"); return RNP_ERROR_BAD_FORMAT; } if (pkt.left() < splen) { RNP_LOG("not enough data for unhashed subpackets"); return RNP_ERROR_BAD_FORMAT; } std::vector spbuf(splen); if (!pkt.get(spbuf.data(), splen)) { RNP_LOG("read of unhashed subpackets failed"); return RNP_ERROR_READ; } if (!parse_subpackets(spbuf.data(), splen, false)) { RNP_LOG("failed to parse unhashed subpackets"); return RNP_ERROR_BAD_FORMAT; } return RNP_SUCCESS; } rnp_result_t pgp_signature_t::parse(pgp_packet_body_t &pkt) { uint8_t ver = 0; if (!pkt.get(ver)) { return RNP_ERROR_BAD_FORMAT; } version = (pgp_version_t) ver; /* v3 or v4 signature body */ rnp_result_t res; if ((ver == PGP_V2) || (ver == PGP_V3)) { res = parse_v3(pkt); } else if (ver == PGP_V4) { res = parse_v4(pkt); } else { RNP_LOG("unknown signature version: %d", (int) ver); res = RNP_ERROR_BAD_FORMAT; } if (res) { return res; } /* left 16 bits of the hash */ if (!pkt.get(lbits, 2)) { RNP_LOG("not enough data for hash left bits"); return RNP_ERROR_BAD_FORMAT; } /* raw signature material */ material_len = pkt.left(); if (!material_len) { RNP_LOG("No signature material"); return RNP_ERROR_BAD_FORMAT; } material_buf = (uint8_t *) malloc(material_len); if (!material_buf) { RNP_LOG("Allocation failed"); return RNP_ERROR_OUT_OF_MEMORY; } /* we cannot fail here */ pkt.get(material_buf, material_len); /* check whether it can be parsed */ pgp_signature_material_t material = {}; if (!parse_material(material)) { return RNP_ERROR_BAD_FORMAT; } return RNP_SUCCESS; } rnp_result_t pgp_signature_t::parse(pgp_source_t &src) { pgp_packet_body_t pkt(PGP_PKT_SIGNATURE); rnp_result_t res = pkt.read(src); if (res) { return res; } return parse(pkt); } bool pgp_signature_t::parse_material(pgp_signature_material_t &material) const { pgp_packet_body_t pkt(material_buf, material_len); switch (palg) { case PGP_PKA_RSA: case PGP_PKA_RSA_SIGN_ONLY: if (!pkt.get(material.rsa.s)) { return false; } break; case PGP_PKA_DSA: if (!pkt.get(material.dsa.r) || !pkt.get(material.dsa.s)) { return false; } break; case PGP_PKA_EDDSA: if (version < PGP_V4) { RNP_LOG("Warning! v3 EdDSA signature."); } #if (!defined(_MSVC_LANG) || _MSVC_LANG >= 201703L) [[fallthrough]]; #endif case PGP_PKA_ECDSA: case PGP_PKA_SM2: case PGP_PKA_ECDH: if (!pkt.get(material.ecc.r) || !pkt.get(material.ecc.s)) { return false; } break; case PGP_PKA_ELGAMAL: /* we support reading it but will not validate */ case PGP_PKA_ELGAMAL_ENCRYPT_OR_SIGN: if (!pkt.get(material.eg.r) || !pkt.get(material.eg.s)) { return false; } break; default: RNP_LOG("Unknown pk algorithm : %d", (int) palg); return false; } if (pkt.left()) { RNP_LOG("extra %d bytes in signature packet", (int) pkt.left()); return false; } return true; } void pgp_signature_t::write(pgp_dest_t &dst) const { if ((version < PGP_V2) || (version > PGP_V4)) { RNP_LOG("don't know version %d", (int) version); throw rnp::rnp_exception(RNP_ERROR_BAD_PARAMETERS); } pgp_packet_body_t pktbody(PGP_PKT_SIGNATURE); if (version < PGP_V4) { /* for v3 signatures hashed data includes only type + creation_time */ pktbody.add_byte(version); pktbody.add_byte(hashed_len); pktbody.add(hashed_data, hashed_len); pktbody.add(signer); pktbody.add_byte(palg); pktbody.add_byte(halg); } else { /* for v4 sig->hashed_data must contain most of signature fields */ pktbody.add(hashed_data, hashed_len); pktbody.add_subpackets(*this, false); } pktbody.add(lbits, 2); /* write mpis */ pktbody.add(material_buf, material_len); pktbody.write(dst); } void pgp_signature_t::write_material(const pgp_signature_material_t &material) { pgp_packet_body_t pktbody(PGP_PKT_SIGNATURE); switch (palg) { case PGP_PKA_RSA: case PGP_PKA_RSA_SIGN_ONLY: pktbody.add(material.rsa.s); break; case PGP_PKA_DSA: pktbody.add(material.dsa.r); pktbody.add(material.dsa.s); break; case PGP_PKA_EDDSA: case PGP_PKA_ECDSA: case PGP_PKA_SM2: case PGP_PKA_ECDH: pktbody.add(material.ecc.r); pktbody.add(material.ecc.s); break; case PGP_PKA_ELGAMAL: /* we support writing it but will not generate */ case PGP_PKA_ELGAMAL_ENCRYPT_OR_SIGN: pktbody.add(material.eg.r); pktbody.add(material.eg.s); break; default: RNP_LOG("Unknown pk algorithm : %d", (int) palg); throw rnp::rnp_exception(RNP_ERROR_BAD_PARAMETERS); } free(material_buf); material_buf = (uint8_t *) malloc(pktbody.size()); if (!material_buf) { RNP_LOG("allocation failed"); throw rnp::rnp_exception(RNP_ERROR_OUT_OF_MEMORY); } memcpy(material_buf, pktbody.data(), pktbody.size()); material_len = pktbody.size(); } void pgp_signature_t::fill_hashed_data() { /* we don't have a need to write v2-v3 signatures */ if ((version < PGP_V2) || (version > PGP_V4)) { RNP_LOG("don't know version %d", (int) version); throw rnp::rnp_exception(RNP_ERROR_BAD_PARAMETERS); } pgp_packet_body_t hbody(PGP_PKT_RESERVED); if (version < PGP_V4) { hbody.add_byte(type()); hbody.add_uint32(creation_time); } else { hbody.add_byte(version); hbody.add_byte(type()); hbody.add_byte(palg); hbody.add_byte(halg); hbody.add_subpackets(*this, true); } free(hashed_data); hashed_data = (uint8_t *) malloc(hbody.size()); if (!hashed_data) { RNP_LOG("allocation failed"); throw std::bad_alloc(); } memcpy(hashed_data, hbody.data(), hbody.size()); hashed_len = hbody.size(); } void rnp_selfsig_cert_info_t::populate(pgp_userid_pkt_t &uid, pgp_signature_t &sig) { /* populate signature */ sig.set_type(PGP_CERT_POSITIVE); if (key_expiration) { sig.set_key_expiration(key_expiration); } if (key_flags) { sig.set_key_flags(key_flags); } if (primary) { sig.set_primary_uid(true); } if (!prefs.symm_algs.empty()) { sig.set_preferred_symm_algs(prefs.symm_algs); } if (!prefs.hash_algs.empty()) { sig.set_preferred_hash_algs(prefs.hash_algs); } if (!prefs.z_algs.empty()) { sig.set_preferred_z_algs(prefs.z_algs); } if (!prefs.ks_prefs.empty()) { sig.set_key_server_prefs(prefs.ks_prefs[0]); } if (!prefs.key_server.empty()) { sig.set_key_server(prefs.key_server); } /* populate uid */ uid.tag = PGP_PKT_USER_ID; uid.uid_len = userid.size(); if (!(uid.uid = (uint8_t *) malloc(uid.uid_len))) { RNP_LOG("alloc failed"); throw rnp::rnp_exception(RNP_ERROR_OUT_OF_MEMORY); } memcpy(uid.uid, userid.data(), uid.uid_len); }