/* * Copyright (c) 2018-2025, [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 #include #include "types.h" #include "stream-sig.h" #include "stream-packet.h" #include "stream-armor.h" #include "key.hpp" #include "crypto/signatures.h" #include #include void signature_hash_key(const pgp_key_pkt_t &key, rnp::Hash &hash, pgp_version_t pgpver) { if (key.pub_data.empty()) { /* 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, pgpver); return; } switch (pgpver) { case PGP_V2: FALLTHROUGH_STATEMENT; case PGP_V3: FALLTHROUGH_STATEMENT; case PGP_V4: { assert(key.pub_data.size() <= ((size_t) 0xffffu)); uint8_t hdr[3] = {0x99, 0x00, 0x00}; write_uint16(hdr + 1, key.pub_data.size()); hash.add(hdr, 3); hash.add(key.pub_data); break; } case PGP_V5: { assert(key.pub_data.size() <= (size_t) 0xffffffffu); uint8_t hdr[5] = {0x9A, 0x00, 0x00, 0x00, 0x00}; write_uint32(hdr + 1, key.pub_data.size()); hash.add(&hdr, 5); hash.add(key.pub_data); break; } #if defined(ENABLE_CRYPTO_REFRESH) case PGP_V6: { assert(key.pub_data.size() <= (size_t) 0xffffffffu); uint8_t hdr[5] = {0x9b, 0x00, 0x00, 0x00, 0x00}; write_uint32(hdr + 1, key.pub_data.size()); hash.add(hdr, sizeof(hdr)); hash.add(key.pub_data); break; } #endif default: RNP_LOG("unknown key/sig version: %d", (int) pgpver); throw rnp::rnp_exception(RNP_ERROR_OUT_OF_MEMORY); } } 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.data(), uid.uid.size()); 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); } write_uint32(hdr + 1, uid.uid.size()); hash.add(hdr, 5); hash.add(uid.uid.data(), uid.uid.size()); } std::unique_ptr signature_hash_certification(const pgp::pkt::Signature &sig, const pgp_key_pkt_t & key, const pgp_userid_pkt_t & userid) { auto hash = signature_init(key, sig); signature_hash_key(key, *hash, sig.version); signature_hash_userid(userid, *hash, sig.version); return hash; } std::unique_ptr signature_hash_binding(const pgp::pkt::Signature &sig, const pgp_key_pkt_t & key, const pgp_key_pkt_t & subkey) { auto hash = signature_init(key, sig); signature_hash_key(key, *hash, sig.version); signature_hash_key(subkey, *hash, sig.version); return hash; } std::unique_ptr signature_hash_direct(const pgp::pkt::Signature &sig, const pgp_key_pkt_t &key) { auto hash = signature_init(key, sig); signature_hash_key(key, *hash, sig.version); return hash; } rnp_result_t process_pgp_signatures(pgp_source_t &src, pgp::pkt::Signatures &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; } namespace pgp { namespace pkt { bool Signature::operator==(const Signature &src) const { // TODO-V6: could also compare salt return (lbits == src.lbits) && (hashed_data == src.hashed_data) && (material_buf == src.material_buf); } bool Signature::operator!=(const Signature &src) const { return !(*this == src); } SigID Signature::get_id() const { auto hash = rnp::Hash::create(PGP_HASH_SHA1); hash->add(hashed_data); hash->add(material_buf); SigID res = {0}; static_assert(std::tuple_size::value == PGP_SHA1_HASH_SIZE, "SigID size mismatch"); hash->finish(res.data()); return res; } sigsub::Raw * Signature::get_subpkt(uint8_t stype, bool hashed) { size_t idx = find_subpkt(stype, hashed); return idx == SIZE_MAX ? nullptr : subpkts[idx].get(); } const sigsub::Raw * Signature::get_subpkt(uint8_t stype, bool hashed) const { size_t idx = find_subpkt(stype, hashed); return idx == SIZE_MAX ? nullptr : subpkts[idx].get(); } sigsub::Raw * Signature::get_subpkt(sigsub::Type type, bool hashed) { return get_subpkt(static_cast(type), hashed); } const sigsub::Raw * Signature::get_subpkt(sigsub::Type type, bool hashed) const { return get_subpkt(static_cast(type), hashed); } bool Signature::has_subpkt(uint8_t stype, bool hashed) const { return find_subpkt(stype, hashed) != SIZE_MAX; } bool Signature::has_keyid() const { return (version < PGP_V4) || has_subpkt(PGP_SIG_SUBPKT_ISSUER_KEY_ID, false) || has_keyfp(); } KeyID Signature::keyid() const noexcept { /* version 3 uses signature field */ if (version < PGP_V4) { return signer; } /* version 4 and up use subpackets */ if (version == PGP_V4) { auto sub = dynamic_cast( get_subpkt(sigsub::Type::IssuerKeyID, false)); if (sub) { return sub->keyid(); } } /* v5 and up must have fingerprint, from which keyid would be extracted */ return keyfp().keyid(); } void Signature::set_keyid(const KeyID &id) { if (version < PGP_V4) { signer = id; return; } auto sub = std::unique_ptr(new sigsub::IssuerKeyID(false)); sub->set_keyid(id); add_subpkt(std::move(sub)); } bool Signature::has_keyfp() const { auto sub = dynamic_cast( get_subpkt(sigsub::Type::IssuerFingerprint)); if (!sub) { return false; } switch (version) { case PGP_V4: return sub->fp().size() == PGP_FINGERPRINT_V4_SIZE; case PGP_V5: #if defined(ENABLE_CRYPTO_REFRESH) case PGP_V6: #endif return sub->fp().size() == PGP_FINGERPRINT_V5_SIZE; default: return false; } } Fingerprint Signature::keyfp() const noexcept { auto sub = dynamic_cast( get_subpkt(sigsub::Type::IssuerFingerprint)); return sub ? sub->fp() : Fingerprint{}; } void Signature::set_keyfp(const Fingerprint &fp) { auto sub = std::unique_ptr(new sigsub::IssuerFingerprint()); #if defined(ENABLE_CRYPTO_REFRESH) sub->set_version(version); #else sub->set_version(4); #endif sub->set_fp(fp); add_subpkt(std::move(sub)); } uint32_t Signature::creation() const { if (version < PGP_V4) { return creation_time; } auto sub = dynamic_cast(get_subpkt(sigsub::Type::CreationTime)); return sub ? sub->time() : 0; } void Signature::set_creation(uint32_t ctime) { if (version < PGP_V4) { creation_time = ctime; return; } auto sub = std::unique_ptr(new sigsub::CreationTime()); sub->set_time(ctime); add_subpkt(std::move(sub)); } uint32_t Signature::expiration() const { auto sub = dynamic_cast(get_subpkt(sigsub::Type::ExpirationTime)); return sub ? sub->time() : 0; } void Signature::set_expiration(uint32_t etime) { auto sub = std::unique_ptr(new sigsub::ExpirationTime()); sub->set_time(etime); add_subpkt(std::move(sub)); } uint32_t Signature::key_expiration() const { auto sub = dynamic_cast( get_subpkt(sigsub::Type::KeyExpirationTime)); return sub ? sub->time() : 0; } void Signature::set_key_expiration(uint32_t etime) { auto sub = std::unique_ptr(new sigsub::KeyExpirationTime()); sub->set_time(etime); add_subpkt(std::move(sub)); } uint8_t Signature::key_flags() const { auto sub = dynamic_cast(get_subpkt(sigsub::Type::KeyFlags)); return sub ? sub->flags() : 0; } void Signature::set_key_flags(uint8_t flags) { auto sub = std::unique_ptr(new sigsub::KeyFlags()); sub->set_flags(flags); add_subpkt(std::move(sub)); } bool Signature::primary_uid() const { auto sub = dynamic_cast(get_subpkt(sigsub::Type::PrimaryUserID)); return sub ? sub->primary() : 0; } void Signature::set_primary_uid(bool primary) { auto sub = std::unique_ptr(new sigsub::PrimaryUserID()); sub->set_primary(primary); add_subpkt(std::move(sub)); } std::vector Signature::preferred(sigsub::Type type) const { auto sub = dynamic_cast(get_subpkt(type)); return sub ? sub->algs() : std::vector(); } void Signature::set_preferred(const std::vector &data, sigsub::Type type) { if (data.empty()) { /* Here we assume that there could be only one subpacket of the corresponding type */ remove_subpkt(find_subpkt(type)); remove_subpkt(find_subpkt(type, false)); return; } auto sub = sigsub::Raw::create(type); auto pref = dynamic_cast(sub.get()); if (!pref) { return; } pref->set_algs(data); add_subpkt(std::move(sub)); } std::vector Signature::preferred_symm_algs() const { return preferred(sigsub::Type::PreferredSymmetric); } void Signature::set_preferred_symm_algs(const std::vector &algs) { set_preferred(algs, sigsub::Type::PreferredSymmetric); } std::vector Signature::preferred_hash_algs() const { return preferred(sigsub::Type::PreferredHash); } void Signature::set_preferred_hash_algs(const std::vector &algs) { set_preferred(algs, sigsub::Type::PreferredHash); } std::vector Signature::preferred_z_algs() const { return preferred(sigsub::Type::PreferredCompress); } void Signature::set_preferred_z_algs(const std::vector &algs) { set_preferred(algs, sigsub::Type::PreferredCompress); } #if defined(ENABLE_CRYPTO_REFRESH) void Signature::set_preferred_aead_algs(const std::vector &algs) { set_preferred(algs, sigsub::Type::PreferredAEADv6); } std::vector Signature::preferred_aead_algs() const { return preferred(sigsub::Type::PreferredAEADv6); } #endif uint8_t Signature::key_server_prefs() const { auto sub = dynamic_cast(get_subpkt(sigsub::Type::KeyserverPrefs)); return sub ? sub->raw() : 0; } void Signature::set_key_server_prefs(uint8_t prefs) { auto sub = std::unique_ptr(new sigsub::KeyserverPrefs()); sub->set_raw(prefs); add_subpkt(std::move(sub)); } std::string Signature::key_server() const { auto sub = dynamic_cast( get_subpkt(sigsub::Type::PreferredKeyserver)); return sub ? sub->keyserver() : ""; } void Signature::set_key_server(const std::string &uri) { if (uri.empty()) { remove_subpkt(find_subpkt(sigsub::Type::PreferredKeyserver)); remove_subpkt(find_subpkt(sigsub::Type::PreferredKeyserver, false)); return; } auto sub = std::unique_ptr(new sigsub::PreferredKeyserver()); sub->set_keyserver(uri); add_subpkt(std::move(sub)); } uint8_t Signature::trust_level() const { auto sub = dynamic_cast(get_subpkt(sigsub::Type::Trust)); return sub ? sub->level() : 0; } uint8_t Signature::trust_amount() const { auto sub = dynamic_cast(get_subpkt(sigsub::Type::Trust)); return sub ? sub->amount() : 0; } void Signature::set_trust(uint8_t level, uint8_t amount) { auto sub = std::unique_ptr(new sigsub::Trust()); sub->set_level(level); sub->set_amount(amount); add_subpkt(std::move(sub)); } bool Signature::revocable() const { auto sub = dynamic_cast(get_subpkt(sigsub::Type::Revocable)); return sub ? sub->revocable() : true; } void Signature::set_revocable(bool status) { auto sub = std::unique_ptr(new sigsub::Revocable()); sub->set_revocable(status); add_subpkt(std::move(sub)); } std::string Signature::revocation_reason() const { auto sub = dynamic_cast( get_subpkt(sigsub::Type::RevocationReason)); return sub ? sub->reason() : ""; } pgp_revocation_type_t Signature::revocation_code() const { auto sub = dynamic_cast( get_subpkt(sigsub::Type::RevocationReason)); return sub ? sub->code() : PGP_REVOCATION_NO_REASON; } bool Signature::has_revocation_reason() const { return get_subpkt(sigsub::Type::RevocationReason); } void Signature::set_revocation_reason(pgp_revocation_type_t code, const std::string &reason) { auto sub = std::unique_ptr(new sigsub::RevocationReason()); sub->set_code(code); sub->set_reason(reason); add_subpkt(std::move(sub)); } uint32_t Signature::key_get_features() const { auto sub = dynamic_cast(get_subpkt(sigsub::Type::Features)); return sub ? sub->features() : 0; } bool Signature::key_has_features(uint32_t flags) const { auto sub = dynamic_cast(get_subpkt(sigsub::Type::Features)); return sub ? sub->features() & flags : false; } void Signature::set_key_features(uint32_t flags) { auto sub = std::unique_ptr(new sigsub::Features()); sub->set_features(flags & 0xff); add_subpkt(std::move(sub)); } std::string Signature::signer_uid() const { auto sub = dynamic_cast(get_subpkt(sigsub::Type::SignersUserID)); return sub ? sub->signer() : ""; } void Signature::set_signer_uid(const std::string &uid) { auto sub = std::unique_ptr(new sigsub::SignersUserID()); sub->set_signer(uid); add_subpkt(std::move(sub)); } void Signature::add_notation(const std::string & name, const std::vector &value, bool human, bool critical) { if ((name.size() > 0xffff) || (value.size() > 0xffff)) { RNP_LOG("wrong length"); throw rnp::rnp_exception(RNP_ERROR_BAD_PARAMETERS); } auto sub = std::unique_ptr(new sigsub::NotationData(true, critical)); sub->set_human_readable(human); sub->set_name(name); sub->set_value(value); add_subpkt(std::move(sub), false); } void Signature::add_notation(const std::string &name, const std::string &value, bool critical) { add_notation(name, std::vector(value.begin(), value.end()), true, critical); } void Signature::set_embedded_sig(const Signature &esig) { auto sub = std::unique_ptr(new sigsub::EmbeddedSignature(false)); sub->set_signature(esig); add_subpkt(std::move(sub)); } const sigsub::RevocationKey * Signature::revoker_subpkt() const noexcept { return dynamic_cast( get_subpkt(sigsub::Type::RevocationKey)); } bool Signature::has_revoker() const noexcept { return revoker_subpkt(); } Fingerprint Signature::revoker() const noexcept { auto sub = revoker_subpkt(); return sub ? sub->fp() : Fingerprint(); } void Signature::set_revoker(const rnp::Key &revoker, bool sensitive) { auto sub = std::unique_ptr(new sigsub::RevocationKey()); sub->set_rev_class(sensitive ? 0xC0 : 0x80); sub->set_alg(revoker.alg()); sub->set_fp(revoker.fp()); add_subpkt(std::move(sub)); } void Signature::add_subpkt(std::unique_ptr &&sub, bool replace) { if (version < PGP_V4) { RNP_LOG("wrong signature version"); throw std::invalid_argument("version"); } sub->write(); if (replace) { auto idx = find_subpkt(sub->raw_type(), sub->hashed()); if (idx != SIZE_MAX) { subpkts[idx] = std::move(sub); return; } } subpkts.items.push_back(std::move(sub)); } void Signature::remove_subpkt(size_t idx) { if (idx < subpkts.size()) { subpkts.items.erase(subpkts.begin() + idx); } } bool Signature::matches_onepass(const pgp_one_pass_sig_t &onepass) const { if (!has_keyid()) { return false; } /* check sig and OPS packet version binding (V3) */ if (onepass.version == PGP_OPS_V3) { if (version != PGP_V3 && version != PGP_V4 && version != PGP_V5) { return false; } } /* check keyid (V3) */ if (onepass.version == PGP_OPS_V3 && (onepass.keyid != keyid())) { return false; } #if defined(ENABLE_CRYPTO_REFRESH) /* checks for V6 */ if (onepass.version == PGP_OPS_V6) { /* check version binding) */ if (onepass.version == PGP_OPS_V6 && version != PGP_V6) { return false; } /* check fp */ if (onepass.fp != keyfp()) { return false; } /* check salt */ if (onepass.salt != salt) { return false; } } #endif /* check the remaining common attributes */ return (halg == onepass.halg) && (palg == onepass.palg) && (type_ == onepass.type); } bool Signature::version_supported(pgp_version_t version) { if ((version >= PGP_V2) && (version <= PGP_V5)) { return true; } #if defined(ENABLE_CRYPTO_REFRESH) return version == PGP_V6; #else return false; #endif } rnp_result_t Signature::parse_v2v3(pgp_packet_body_t &pkt) { /* parse v2/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 */ hashed_data.assign(buf + 1, buf + 6); /* 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 Signature::parse_subpackets(uint8_t *buf, size_t len, bool hashed) { bool res = true; while (len) { if (subpkts.size() >= MAX_SUBPACKETS) { RNP_LOG("too many signature subpackets"); return false; } if (len < 2) { RNP_LOG("got single byte %" PRIu8, *buf); return false; } /* subpacket length */ size_t splen = *buf++; len--; if ((splen >= 192) && (splen < 255)) { splen = ((splen - 192) << 8) + *buf++ + 192; len--; } else if (splen == 255) { if (len < 4) { RNP_LOG("got 4-byte len but only %zu bytes in buffer", len); return false; } splen = read_uint32(buf); buf += 4; len -= 4; } if (!splen) { RNP_LOG("got subpacket with 0 length"); return false; } /* subpacket data */ if (len < splen) { RNP_LOG("got subpacket len %zu, while only %zu bytes left", splen, len); return false; } auto subpkt = sigsub::Raw::create(buf, splen, hashed); if (!subpkt) { res = false; } else { subpkts.items.push_back(std::move(subpkt)); } len -= splen; buf += splen; } return res; } bool Signature::get_subpkt_len(pgp_packet_body_t &pkt, size_t &splen) { switch (version) { case PGP_V4: case PGP_V5: { uint16_t len = 0; if (!pkt.get(len)) { return false; } splen = len; return true; } #if defined(ENABLE_CRYPTO_REFRESH) case PGP_V6: { uint32_t len = 0; if (!pkt.get(len)) { return false; } splen = len; return true; } #endif default: RNP_LOG("unsupported signature version: %d", (int) version); return false; } } size_t Signature::find_subpkt(uint8_t stype, bool hashed, size_t skip) const { if (version < PGP_V4) { return SIZE_MAX; } for (size_t idx = 0; idx < subpkts.size(); idx++) { if ((subpkts[idx]->raw_type() != stype) || (hashed && !subpkts[idx]->hashed())) { continue; } if (!skip) { return idx; } skip--; } return SIZE_MAX; } size_t Signature::find_subpkt(sigsub::Type type, bool hashed, size_t skip) const { return find_subpkt(static_cast(type), hashed, skip); } rnp_result_t Signature::parse_v4up(pgp_packet_body_t &pkt) { /* parse v4 (and up) specific fields, not the whole signature */ uint8_t buf[3]; if (!pkt.get(buf, 3)) { RNP_LOG("cannot get first 3 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 */ size_t splen = 0; auto hash_begin = pkt.cur(); if (!get_subpkt_len(pkt, splen)) { RNP_LOG("cannot get hashed len"); return RNP_ERROR_BAD_FORMAT; } size_t splen_size = pkt.cur() - hash_begin; /* hashed subpackets length + splen_size bytes of length of unhashed subpackets */ if (pkt.left() < splen + splen_size) { RNP_LOG("wrong packet or hashed subpackets length"); return RNP_ERROR_BAD_FORMAT; } /* building hashed data */ size_t hlen = 4 + splen + splen_size; hashed_data.resize(hlen); hashed_data[0] = version; static_assert(sizeof(buf) == 3, "Wrong signature header size."); pkt.skip_back(3 + splen_size); if (!pkt.get(hashed_data.data() + 1, hlen - 1)) { RNP_LOG("cannot get hashed subpackets data"); return RNP_ERROR_BAD_FORMAT; } /* parsing hashed subpackets */ if (!parse_subpackets(hashed_data.data() + 4 + splen_size, splen, true)) { RNP_LOG("failed to parse hashed subpackets"); return RNP_ERROR_BAD_FORMAT; } /* reading unhashed subpackets */ if (!get_subpkt_len(pkt, 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; } if (!parse_subpackets(pkt.cur(), splen, false)) { RNP_LOG("failed to parse unhashed subpackets"); return RNP_ERROR_BAD_FORMAT; } pkt.skip(splen); return RNP_SUCCESS; } rnp_result_t Signature::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 or v6 signature body */ rnp_result_t res; switch (ver) { case PGP_V2: FALLTHROUGH_STATEMENT; case PGP_V3: res = parse_v2v3(pkt); break; case PGP_V4: FALLTHROUGH_STATEMENT; case PGP_V5: #if defined(ENABLE_CRYPTO_REFRESH) FALLTHROUGH_STATEMENT; case PGP_V6: #endif res = parse_v4up(pkt); break; default: 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.data(), 2)) { RNP_LOG("not enough data for hash left bits"); return RNP_ERROR_BAD_FORMAT; } #if defined(ENABLE_CRYPTO_REFRESH) if (ver == PGP_V6) { uint8_t salt_size = 0; if (!pkt.get(salt_size)) { RNP_LOG("not enough data for v6 salt size octet"); return RNP_ERROR_BAD_FORMAT; } size_t expect_salt_size; if (!pgp::pkt::Signature::v6_salt_size(halg, &expect_salt_size)) { RNP_LOG("invalid halg"); return RNP_ERROR_BAD_FORMAT; } if (salt_size != expect_salt_size) { RNP_LOG("invalid salt size"); return RNP_ERROR_BAD_FORMAT; } salt.resize(salt_size); if (!pkt.get(salt.data(), salt_size)) { RNP_LOG("not enough data for v6 signature salt"); return RNP_ERROR_BAD_FORMAT; } } #endif /* raw signature material */ /* we cannot fail here */ pkt.get(material_buf, pkt.left()); /* check whether it can be parsed */ if (!parse_material()) { return RNP_ERROR_BAD_FORMAT; } return RNP_SUCCESS; } rnp_result_t Signature::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); } std::unique_ptr Signature::parse_material() const { auto sig = SigMaterial::create(palg, halg); if (!sig) { return nullptr; } pgp_packet_body_t pkt(material_buf); if (!sig->parse(pkt)) { return nullptr; } if (pkt.left()) { RNP_LOG("extra %zu bytes in pk packet", pkt.left()); return nullptr; } return sig; } void Signature::write(pgp_dest_t &dst, bool hdr) const { if (!Signature::version_supported(version)) { 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_data.size()); pktbody.add(hashed_data); 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); pktbody.add_subpackets(*this, false); } pktbody.add(lbits.data(), 2); #if defined(ENABLE_CRYPTO_REFRESH) if (version == PGP_V6) { pktbody.add_byte(salt.size()); pktbody.add(salt); } #endif /* write mpis */ pktbody.add(material_buf); pktbody.write(dst, hdr); } std::vector Signature::write(bool hdr) const { rnp::MemoryDest dst; write(dst.dst(), hdr); return dst.to_vector(); } void Signature::write_material(const SigMaterial &material) { pgp_packet_body_t pktbody(PGP_PKT_SIGNATURE); material.write(pktbody); material_buf.assign(pktbody.data(), pktbody.data() + pktbody.size()); } void Signature::fill_hashed_data() { /* we don't have a need to write v2-v3 signatures */ if (!Signature::version_supported(version)) { 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); } hashed_data.assign(hbody.data(), hbody.data() + hbody.size()); } #if defined(ENABLE_CRYPTO_REFRESH) bool Signature::v6_salt_size(pgp_hash_alg_t halg, size_t *salt_size) { switch (halg) { case PGP_HASH_SHA256: *salt_size = 16; break; case PGP_HASH_SHA224: *salt_size = 16; break; case PGP_HASH_SHA384: *salt_size = 24; break; case PGP_HASH_SHA512: *salt_size = 32; break; case PGP_HASH_SHA3_256: *salt_size = 16; break; case PGP_HASH_SHA3_512: *salt_size = 32; break; default: RNP_LOG("no V6 salt size for algorithm"); return false; } return true; } #endif } // namespace pkt } // namespace pgp