/*
 * 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 <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);
    }
}

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<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.");
        }
#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);
}
