summaryrefslogtreecommitdiffstats
path: root/src/librepgp/stream-sig.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/librepgp/stream-sig.cpp')
-rw-r--r--src/librepgp/stream-sig.cpp1557
1 files changed, 1557 insertions, 0 deletions
diff --git a/src/librepgp/stream-sig.cpp b/src/librepgp/stream-sig.cpp
new file mode 100644
index 0000000..6f3bc81
--- /dev/null
+++ b/src/librepgp/stream-sig.cpp
@@ -0,0 +1,1557 @@
+/*
+ * Copyright (c) 2018-2022, [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 <stdlib.h>
+#include <stdio.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#else
+#include "uniwin.h"
+#endif
+#include <string.h>
+#include <type_traits>
+#include <stdexcept>
+#include <rnp/rnp_def.h>
+#include "types.h"
+#include "stream-sig.h"
+#include "stream-packet.h"
+#include "stream-armor.h"
+#include "pgp-key.h"
+#include "crypto/signatures.h"
+
+#include <time.h>
+
+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<rnp::Hash>
+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<rnp::Hash>
+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<rnp::Hash>
+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<decltype(res)>::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<decltype(res)>::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<std::remove_reference<decltype(id)>::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<uint8_t>
+pgp_signature_t::preferred(pgp_sig_subpacket_type_t type) const
+{
+ const pgp_sig_subpkt_t *subpkt = get_subpkt(type);
+ return subpkt ? std::vector<uint8_t>(subpkt->fields.preferred.arr,
+ subpkt->fields.preferred.arr +
+ subpkt->fields.preferred.len) :
+ std::vector<uint8_t>();
+}
+
+void
+pgp_signature_t::set_preferred(const std::vector<uint8_t> &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<uint8_t>
+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<uint8_t> &algs)
+{
+ set_preferred(algs, PGP_SIG_SUBPKT_PREFERRED_SKA);
+}
+
+std::vector<uint8_t>
+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<uint8_t> &algs)
+{
+ set_preferred(algs, PGP_SIG_SUBPKT_PREFERRED_HASH);
+}
+
+std::vector<uint8_t>
+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<uint8_t> &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);
+ }
+}
+
+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<uint8_t> &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<uint8_t>(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<decltype(signer)>::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<uint8_t> 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.");
+ }
+ [[fallthrough]];
+ 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);
+}