diff options
Diffstat (limited to 'src/librepgp/stream-packet.cpp')
-rw-r--r-- | src/librepgp/stream-packet.cpp | 1228 |
1 files changed, 1228 insertions, 0 deletions
diff --git a/src/librepgp/stream-packet.cpp b/src/librepgp/stream-packet.cpp new file mode 100644 index 0000000..49dd63d --- /dev/null +++ b/src/librepgp/stream-packet.cpp @@ -0,0 +1,1228 @@ +/* + * Copyright (c) 2017-2020, [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 <sys/stat.h> +#include <stdlib.h> +#include <stdio.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#else +#include "uniwin.h" +#endif +#include <string.h> +#include <inttypes.h> +#include <rnp/rnp_def.h> +#include "types.h" +#include "crypto.h" +#include "crypto/mem.h" +#include "stream-packet.h" +#include "stream-key.h" +#include <algorithm> + +uint32_t +read_uint32(const uint8_t *buf) +{ + return ((uint32_t) buf[0] << 24) | ((uint32_t) buf[1] << 16) | ((uint32_t) buf[2] << 8) | + (uint32_t) buf[3]; +} + +uint16_t +read_uint16(const uint8_t *buf) +{ + return ((uint16_t) buf[0] << 8) | buf[1]; +} + +void +write_uint16(uint8_t *buf, uint16_t val) +{ + buf[0] = val >> 8; + buf[1] = val & 0xff; +} + +size_t +write_packet_len(uint8_t *buf, size_t len) +{ + if (len < 192) { + buf[0] = len; + return 1; + } else if (len < 8192 + 192) { + buf[0] = ((len - 192) >> 8) + 192; + buf[1] = (len - 192) & 0xff; + return 2; + } else { + buf[0] = 0xff; + STORE32BE(&buf[1], len); + return 5; + } +} + +int +get_packet_type(uint8_t ptag) +{ + if (!(ptag & PGP_PTAG_ALWAYS_SET)) { + return -1; + } + + if (ptag & PGP_PTAG_NEW_FORMAT) { + return (int) (ptag & PGP_PTAG_NF_CONTENT_TAG_MASK); + } else { + return (int) ((ptag & PGP_PTAG_OF_CONTENT_TAG_MASK) >> PGP_PTAG_OF_CONTENT_TAG_SHIFT); + } +} + +int +stream_pkt_type(pgp_source_t &src) +{ + if (src_eof(&src)) { + return 0; + } + size_t hdrneed = 0; + if (!stream_pkt_hdr_len(src, hdrneed)) { + return -1; + } + uint8_t hdr[PGP_MAX_HEADER_SIZE]; + if (!src_peek_eq(&src, hdr, hdrneed)) { + return -1; + } + return get_packet_type(hdr[0]); +} + +bool +stream_pkt_hdr_len(pgp_source_t &src, size_t &hdrlen) +{ + uint8_t buf[2]; + + if (!src_peek_eq(&src, buf, 2) || !(buf[0] & PGP_PTAG_ALWAYS_SET)) { + return false; + } + + if (buf[0] & PGP_PTAG_NEW_FORMAT) { + if (buf[1] < 192) { + hdrlen = 2; + } else if (buf[1] < 224) { + hdrlen = 3; + } else if (buf[1] < 255) { + hdrlen = 2; + } else { + hdrlen = 6; + } + return true; + } + + switch (buf[0] & PGP_PTAG_OF_LENGTH_TYPE_MASK) { + case PGP_PTAG_OLD_LEN_1: + hdrlen = 2; + return true; + case PGP_PTAG_OLD_LEN_2: + hdrlen = 3; + return true; + case PGP_PTAG_OLD_LEN_4: + hdrlen = 5; + return true; + case PGP_PTAG_OLD_LEN_INDETERMINATE: + hdrlen = 1; + return true; + default: + return false; + } +} + +static bool +get_pkt_len(uint8_t *hdr, size_t *pktlen) +{ + if (hdr[0] & PGP_PTAG_NEW_FORMAT) { + // 1-byte length + if (hdr[1] < 192) { + *pktlen = hdr[1]; + return true; + } + // 2-byte length + if (hdr[1] < 224) { + *pktlen = ((size_t)(hdr[1] - 192) << 8) + (size_t) hdr[2] + 192; + return true; + } + // partial length - we do not allow it here + if (hdr[1] < 255) { + return false; + } + // 4-byte length + *pktlen = read_uint32(&hdr[2]); + return true; + } + + switch (hdr[0] & PGP_PTAG_OF_LENGTH_TYPE_MASK) { + case PGP_PTAG_OLD_LEN_1: + *pktlen = hdr[1]; + return true; + case PGP_PTAG_OLD_LEN_2: + *pktlen = read_uint16(&hdr[1]); + return true; + case PGP_PTAG_OLD_LEN_4: + *pktlen = read_uint32(&hdr[1]); + return true; + default: + return false; + } +} + +bool +stream_read_pkt_len(pgp_source_t *src, size_t *pktlen) +{ + uint8_t buf[6] = {}; + size_t read = 0; + + if (!stream_pkt_hdr_len(*src, read)) { + return false; + } + + if (!src_read_eq(src, buf, read)) { + return false; + } + + return get_pkt_len(buf, pktlen); +} + +bool +stream_read_partial_chunk_len(pgp_source_t *src, size_t *clen, bool *last) +{ + uint8_t hdr[5] = {}; + size_t read = 0; + + if (!src_read(src, hdr, 1, &read)) { + RNP_LOG("failed to read header"); + return false; + } + if (read < 1) { + RNP_LOG("wrong eof"); + return false; + } + + *last = true; + // partial length + if ((hdr[0] >= 224) && (hdr[0] < 255)) { + *last = false; + *clen = get_partial_pkt_len(hdr[0]); + return true; + } + // 1-byte length + if (hdr[0] < 192) { + *clen = hdr[0]; + return true; + } + // 2-byte length + if (hdr[0] < 224) { + if (!src_read_eq(src, &hdr[1], 1)) { + RNP_LOG("wrong 2-byte length"); + return false; + } + *clen = ((size_t)(hdr[0] - 192) << 8) + (size_t) hdr[1] + 192; + return true; + } + // 4-byte length + if (!src_read_eq(src, &hdr[1], 4)) { + RNP_LOG("wrong 4-byte length"); + return false; + } + *clen = ((size_t) hdr[1] << 24) | ((size_t) hdr[2] << 16) | ((size_t) hdr[3] << 8) | + (size_t) hdr[4]; + return true; +} + +bool +stream_old_indeterminate_pkt_len(pgp_source_t *src) +{ + uint8_t ptag = 0; + if (!src_peek_eq(src, &ptag, 1)) { + return false; + } + return !(ptag & PGP_PTAG_NEW_FORMAT) && + ((ptag & PGP_PTAG_OF_LENGTH_TYPE_MASK) == PGP_PTAG_OLD_LEN_INDETERMINATE); +} + +bool +stream_partial_pkt_len(pgp_source_t *src) +{ + uint8_t hdr[2] = {}; + if (!src_peek_eq(src, hdr, 2)) { + return false; + } + return (hdr[0] & PGP_PTAG_NEW_FORMAT) && (hdr[1] >= 224) && (hdr[1] < 255); +} + +size_t +get_partial_pkt_len(uint8_t blen) +{ + return 1 << (blen & 0x1f); +} + +rnp_result_t +stream_peek_packet_hdr(pgp_source_t *src, pgp_packet_hdr_t *hdr) +{ + size_t hlen = 0; + memset(hdr, 0, sizeof(*hdr)); + if (!stream_pkt_hdr_len(*src, hlen)) { + uint8_t hdr2[2] = {0}; + if (!src_peek_eq(src, hdr2, 2)) { + RNP_LOG("pkt header read failed"); + return RNP_ERROR_READ; + } + + RNP_LOG("bad packet header: 0x%02x%02x", hdr2[0], hdr2[1]); + return RNP_ERROR_BAD_FORMAT; + } + + if (!src_peek_eq(src, hdr->hdr, hlen)) { + RNP_LOG("failed to read pkt header"); + return RNP_ERROR_READ; + } + + hdr->hdr_len = hlen; + hdr->tag = (pgp_pkt_type_t) get_packet_type(hdr->hdr[0]); + + if (stream_partial_pkt_len(src)) { + hdr->partial = true; + } else if (stream_old_indeterminate_pkt_len(src)) { + hdr->indeterminate = true; + } else { + (void) get_pkt_len(hdr->hdr, &hdr->pkt_len); + } + + return RNP_SUCCESS; +} + +static rnp_result_t +stream_read_packet_partial(pgp_source_t *src, pgp_dest_t *dst) +{ + uint8_t hdr = 0; + if (!src_read_eq(src, &hdr, 1)) { + return RNP_ERROR_READ; + } + + bool last = false; + size_t partlen = 0; + if (!stream_read_partial_chunk_len(src, &partlen, &last)) { + return RNP_ERROR_BAD_FORMAT; + } + + uint8_t *buf = (uint8_t *) malloc(PGP_INPUT_CACHE_SIZE); + if (!buf) { + return RNP_ERROR_OUT_OF_MEMORY; + } + + while (partlen > 0) { + size_t read = std::min(partlen, (size_t) PGP_INPUT_CACHE_SIZE); + if (!src_read_eq(src, buf, read)) { + free(buf); + return RNP_ERROR_READ; + } + if (dst) { + dst_write(dst, buf, read); + } + partlen -= read; + if (partlen > 0) { + continue; + } + if (last) { + break; + } + if (!stream_read_partial_chunk_len(src, &partlen, &last)) { + free(buf); + return RNP_ERROR_BAD_FORMAT; + } + } + free(buf); + return RNP_SUCCESS; +} + +rnp_result_t +stream_read_packet(pgp_source_t *src, pgp_dest_t *dst) +{ + if (stream_old_indeterminate_pkt_len(src)) { + return dst_write_src(src, dst, PGP_MAX_OLD_LEN_INDETERMINATE_PKT_SIZE); + } + + if (stream_partial_pkt_len(src)) { + return stream_read_packet_partial(src, dst); + } + + try { + pgp_packet_body_t body(PGP_PKT_RESERVED); + rnp_result_t ret = body.read(*src); + if (dst) { + body.write(*dst, false); + } + return ret; + } catch (const std::exception &e) { + RNP_LOG("%s", e.what()); + return RNP_ERROR_GENERIC; + } +} + +rnp_result_t +stream_skip_packet(pgp_source_t *src) +{ + return stream_read_packet(src, NULL); +} + +rnp_result_t +stream_parse_marker(pgp_source_t &src) +{ + try { + pgp_packet_body_t pkt(PGP_PKT_MARKER); + rnp_result_t res = pkt.read(src); + if (res) { + return res; + } + if ((pkt.size() != PGP_MARKER_LEN) || + memcmp(pkt.data(), PGP_MARKER_CONTENTS, PGP_MARKER_LEN)) { + return RNP_ERROR_BAD_FORMAT; + } + return RNP_SUCCESS; + } catch (const std::exception &e) { + RNP_LOG("%s", e.what()); + return RNP_ERROR_OUT_OF_MEMORY; + } +} + +bool +is_key_pkt(int tag) +{ + switch (tag) { + case PGP_PKT_PUBLIC_KEY: + case PGP_PKT_PUBLIC_SUBKEY: + case PGP_PKT_SECRET_KEY: + case PGP_PKT_SECRET_SUBKEY: + return true; + default: + return false; + } +} + +bool +is_subkey_pkt(int tag) +{ + return (tag == PGP_PKT_PUBLIC_SUBKEY) || (tag == PGP_PKT_SECRET_SUBKEY); +} + +bool +is_primary_key_pkt(int tag) +{ + return (tag == PGP_PKT_PUBLIC_KEY) || (tag == PGP_PKT_SECRET_KEY); +} + +bool +is_public_key_pkt(int tag) +{ + switch (tag) { + case PGP_PKT_PUBLIC_KEY: + case PGP_PKT_PUBLIC_SUBKEY: + return true; + default: + return false; + } +} + +bool +is_secret_key_pkt(int tag) +{ + switch (tag) { + case PGP_PKT_SECRET_KEY: + case PGP_PKT_SECRET_SUBKEY: + return true; + default: + return false; + } +} + +bool +is_rsa_key_alg(pgp_pubkey_alg_t alg) +{ + switch (alg) { + case PGP_PKA_RSA: + case PGP_PKA_RSA_ENCRYPT_ONLY: + case PGP_PKA_RSA_SIGN_ONLY: + return true; + default: + return false; + } +} + +pgp_packet_body_t::pgp_packet_body_t(pgp_pkt_type_t tag) +{ + data_.reserve(16); + tag_ = tag; + secure_ = is_secret_key_pkt(tag); +} + +pgp_packet_body_t::pgp_packet_body_t(const uint8_t *data, size_t len) +{ + data_.assign(data, data + len); + tag_ = PGP_PKT_RESERVED; + secure_ = false; +} + +pgp_packet_body_t::~pgp_packet_body_t() +{ + if (secure_) { + secure_clear(data_.data(), data_.size()); + } +} + +uint8_t * +pgp_packet_body_t::data() noexcept +{ + return data_.data(); +} + +size_t +pgp_packet_body_t::size() const noexcept +{ + return data_.size(); +} + +size_t +pgp_packet_body_t::left() const noexcept +{ + return data_.size() - pos_; +} + +bool +pgp_packet_body_t::get(uint8_t &val) noexcept +{ + if (pos_ >= data_.size()) { + return false; + } + val = data_[pos_++]; + return true; +} + +bool +pgp_packet_body_t::get(uint16_t &val) noexcept +{ + if (pos_ + 2 > data_.size()) { + return false; + } + val = read_uint16(data_.data() + pos_); + pos_ += 2; + return true; +} + +bool +pgp_packet_body_t::get(uint32_t &val) noexcept +{ + if (pos_ + 4 > data_.size()) { + return false; + } + val = read_uint32(data_.data() + pos_); + pos_ += 4; + return true; +} + +bool +pgp_packet_body_t::get(uint8_t *val, size_t len) noexcept +{ + if (pos_ + len > data_.size()) { + return false; + } + memcpy(val, data_.data() + pos_, len); + pos_ += len; + return true; +} + +bool +pgp_packet_body_t::get(pgp_key_id_t &val) noexcept +{ + static_assert(std::tuple_size<pgp_key_id_t>::value == PGP_KEY_ID_SIZE, + "pgp_key_id_t size mismatch"); + return get(val.data(), val.size()); +} + +bool +pgp_packet_body_t::get(pgp_mpi_t &val) noexcept +{ + uint16_t bits = 0; + if (!get(bits)) { + return false; + } + size_t len = (bits + 7) >> 3; + if (len > PGP_MPINT_SIZE) { + RNP_LOG("too large mpi"); + return false; + } + if (!len) { + RNP_LOG("0 mpi"); + return false; + } + if (!get(val.mpi, len)) { + RNP_LOG("failed to read mpi body"); + return false; + } + /* check the mpi bit count */ + val.len = len; + size_t mbits = mpi_bits(&val); + if (mbits != bits) { + RNP_LOG( + "Warning! Wrong mpi bit count: got %" PRIu16 ", but actual is %zu", bits, mbits); + } + return true; +} + +bool +pgp_packet_body_t::get(pgp_curve_t &val) noexcept +{ + uint8_t oidlen = 0; + if (!get(oidlen)) { + return false; + } + uint8_t oid[MAX_CURVE_OID_HEX_LEN] = {0}; + if (!oidlen || (oidlen == 0xff) || (oidlen > sizeof(oid))) { + RNP_LOG("unsupported curve oid len: %" PRIu8, oidlen); + return false; + } + if (!get(oid, oidlen)) { + return false; + } + pgp_curve_t res = find_curve_by_OID(oid, oidlen); + if (res == PGP_CURVE_MAX) { + RNP_LOG("unsupported curve"); + return false; + } + val = res; + return true; +} + +bool +pgp_packet_body_t::get(pgp_s2k_t &s2k) noexcept +{ + uint8_t spec = 0, halg = 0; + if (!get(spec) || !get(halg)) { + return false; + } + s2k.specifier = (pgp_s2k_specifier_t) spec; + s2k.hash_alg = (pgp_hash_alg_t) halg; + + switch (s2k.specifier) { + case PGP_S2KS_SIMPLE: + return true; + case PGP_S2KS_SALTED: + return get(s2k.salt, PGP_SALT_SIZE); + case PGP_S2KS_ITERATED_AND_SALTED: { + uint8_t iter = 0; + if (!get(s2k.salt, PGP_SALT_SIZE) || !get(iter)) { + return false; + } + s2k.iterations = iter; + return true; + } + case PGP_S2KS_EXPERIMENTAL: { + try { + s2k.experimental = {data_.begin() + pos_, data_.end()}; + } catch (const std::exception &e) { + RNP_LOG("%s", e.what()); + return false; + } + uint8_t gnu[3] = {0}; + if (!get(gnu, 3) || memcmp(gnu, "GNU", 3)) { + RNP_LOG("Unknown experimental s2k. Skipping."); + pos_ = data_.size(); + s2k.gpg_ext_num = PGP_S2K_GPG_NONE; + return true; + } + uint8_t ext_num = 0; + if (!get(ext_num)) { + return false; + } + if ((ext_num != PGP_S2K_GPG_NO_SECRET) && (ext_num != PGP_S2K_GPG_SMARTCARD)) { + RNP_LOG("Unsupported gpg extension num: %" PRIu8 ", skipping", ext_num); + pos_ = data_.size(); + s2k.gpg_ext_num = PGP_S2K_GPG_NONE; + return true; + } + s2k.gpg_ext_num = (pgp_s2k_gpg_extension_t) ext_num; + if (s2k.gpg_ext_num == PGP_S2K_GPG_NO_SECRET) { + return true; + } + if (!get(s2k.gpg_serial_len)) { + RNP_LOG("Failed to get GPG serial len"); + return false; + } + size_t len = s2k.gpg_serial_len; + if (s2k.gpg_serial_len > 16) { + RNP_LOG("Warning: gpg_serial_len is %d", (int) len); + len = 16; + } + if (!get(s2k.gpg_serial, len)) { + RNP_LOG("Failed to get GPG serial"); + return false; + } + return true; + } + default: + RNP_LOG("unknown s2k specifier: %d", (int) s2k.specifier); + return false; + } +} + +void +pgp_packet_body_t::add(const void *data, size_t len) +{ + data_.insert(data_.end(), (uint8_t *) data, (uint8_t *) data + len); +} + +void +pgp_packet_body_t::add_byte(uint8_t bt) +{ + data_.push_back(bt); +} + +void +pgp_packet_body_t::add_uint16(uint16_t val) +{ + uint8_t bytes[2]; + write_uint16(bytes, val); + add(bytes, 2); +} + +void +pgp_packet_body_t::add_uint32(uint32_t val) +{ + uint8_t bytes[4]; + STORE32BE(bytes, val); + add(bytes, 4); +} + +void +pgp_packet_body_t::add(const pgp_key_id_t &val) +{ + add(val.data(), val.size()); +} + +void +pgp_packet_body_t::add(const pgp_mpi_t &val) +{ + if (!val.len) { + throw rnp::rnp_exception(RNP_ERROR_BAD_PARAMETERS); + } + + unsigned idx = 0; + while ((idx < val.len - 1) && (!val.mpi[idx])) { + idx++; + } + + unsigned bits = (val.len - idx - 1) << 3; + unsigned hibyte = val.mpi[idx]; + while (hibyte) { + bits++; + hibyte = hibyte >> 1; + } + + uint8_t hdr[2] = {(uint8_t)(bits >> 8), (uint8_t)(bits & 0xff)}; + add(hdr, 2); + add(val.mpi + idx, val.len - idx); +} + +void +pgp_packet_body_t::add_subpackets(const pgp_signature_t &sig, bool hashed) +{ + pgp_packet_body_t spbody(PGP_PKT_RESERVED); + + for (auto &subpkt : sig.subpkts) { + if (subpkt.hashed != hashed) { + continue; + } + + uint8_t splen[6]; + size_t lenlen = write_packet_len(splen, subpkt.len + 1); + spbody.add(splen, lenlen); + spbody.add_byte(subpkt.type | (subpkt.critical << 7)); + spbody.add(subpkt.data, subpkt.len); + } + + if (spbody.data_.size() > 0xffff) { + throw rnp::rnp_exception(RNP_ERROR_BAD_PARAMETERS); + } + add_uint16(spbody.data_.size()); + add(spbody.data_.data(), spbody.data_.size()); +} + +void +pgp_packet_body_t::add(const pgp_curve_t curve) +{ + const ec_curve_desc_t *desc = get_curve_desc(curve); + if (!desc) { + throw rnp::rnp_exception(RNP_ERROR_BAD_PARAMETERS); + } + add_byte((uint8_t) desc->OIDhex_len); + add(desc->OIDhex, (uint8_t) desc->OIDhex_len); +} + +void +pgp_packet_body_t::add(const pgp_s2k_t &s2k) +{ + add_byte(s2k.specifier); + add_byte(s2k.hash_alg); + + switch (s2k.specifier) { + case PGP_S2KS_SIMPLE: + return; + case PGP_S2KS_SALTED: + add(s2k.salt, PGP_SALT_SIZE); + return; + case PGP_S2KS_ITERATED_AND_SALTED: { + unsigned iter = s2k.iterations; + if (iter > 255) { + iter = pgp_s2k_encode_iterations(iter); + } + add(s2k.salt, PGP_SALT_SIZE); + add_byte(iter); + return; + } + case PGP_S2KS_EXPERIMENTAL: { + if ((s2k.gpg_ext_num != PGP_S2K_GPG_NO_SECRET) && + (s2k.gpg_ext_num != PGP_S2K_GPG_SMARTCARD)) { + RNP_LOG("Unknown experimental s2k."); + add(s2k.experimental.data(), s2k.experimental.size()); + return; + } + add("GNU", 3); + add_byte(s2k.gpg_ext_num); + if (s2k.gpg_ext_num == PGP_S2K_GPG_SMARTCARD) { + static_assert(sizeof(s2k.gpg_serial) == 16, "invalid gpg serial length"); + size_t slen = s2k.gpg_serial_len > 16 ? 16 : s2k.gpg_serial_len; + add_byte(s2k.gpg_serial_len); + add(s2k.gpg_serial, slen); + } + return; + } + default: + RNP_LOG("unknown s2k specifier"); + throw rnp::rnp_exception(RNP_ERROR_BAD_PARAMETERS); + } +} + +rnp_result_t +pgp_packet_body_t::read(pgp_source_t &src) noexcept +{ + /* Make sure we have enough data for packet header */ + if (!src_peek_eq(&src, hdr_, 2)) { + return RNP_ERROR_READ; + } + + /* Read the packet header and length */ + size_t len = 0; + if (!stream_pkt_hdr_len(src, len)) { + return RNP_ERROR_BAD_FORMAT; + } + if (!src_peek_eq(&src, hdr_, len)) { + return RNP_ERROR_READ; + } + hdr_len_ = len; + + int ptag = get_packet_type(hdr_[0]); + if ((ptag < 0) || ((tag_ != PGP_PKT_RESERVED) && (tag_ != ptag))) { + RNP_LOG("tag mismatch: %d vs %d", (int) tag_, ptag); + return RNP_ERROR_BAD_FORMAT; + } + tag_ = (pgp_pkt_type_t) ptag; + + if (!stream_read_pkt_len(&src, &len)) { + return RNP_ERROR_READ; + } + + /* early exit for the empty packet */ + if (!len) { + return RNP_SUCCESS; + } + + if (len > PGP_MAX_PKT_SIZE) { + RNP_LOG("too large packet"); + return RNP_ERROR_BAD_FORMAT; + } + + /* Read the packet contents */ + try { + data_.resize(len); + } catch (const std::exception &e) { + RNP_LOG("malloc of %d bytes failed, %s", (int) len, e.what()); + return RNP_ERROR_OUT_OF_MEMORY; + } + + size_t read = 0; + if (!src_read(&src, data_.data(), len, &read) || (read != len)) { + RNP_LOG("read %d instead of %d", (int) read, (int) len); + return RNP_ERROR_READ; + } + pos_ = 0; + return RNP_SUCCESS; +} + +void +pgp_packet_body_t::write(pgp_dest_t &dst, bool hdr) noexcept +{ + if (hdr) { + uint8_t hdrbt[6] = { + (uint8_t)(tag_ | PGP_PTAG_ALWAYS_SET | PGP_PTAG_NEW_FORMAT), 0, 0, 0, 0, 0}; + size_t hlen = 1 + write_packet_len(&hdrbt[1], data_.size()); + dst_write(&dst, hdrbt, hlen); + } + dst_write(&dst, data_.data(), data_.size()); +} + +void +pgp_packet_body_t::mark_secure(bool secure) noexcept +{ + secure_ = secure; +} + +void +pgp_sk_sesskey_t::write(pgp_dest_t &dst) const +{ + pgp_packet_body_t pktbody(PGP_PKT_SK_SESSION_KEY); + /* version and algorithm fields */ + pktbody.add_byte(version); + pktbody.add_byte(alg); + if (version == PGP_SKSK_V5) { + pktbody.add_byte(aalg); + } + /* S2K specifier */ + pktbody.add_byte(s2k.specifier); + pktbody.add_byte(s2k.hash_alg); + + switch (s2k.specifier) { + case PGP_S2KS_SIMPLE: + break; + case PGP_S2KS_SALTED: + pktbody.add(s2k.salt, sizeof(s2k.salt)); + break; + case PGP_S2KS_ITERATED_AND_SALTED: + pktbody.add(s2k.salt, sizeof(s2k.salt)); + pktbody.add_byte(s2k.iterations); + break; + default: + RNP_LOG("Unexpected s2k specifier: %d", (int) s2k.specifier); + throw rnp::rnp_exception(RNP_ERROR_BAD_PARAMETERS); + } + /* v5 : iv */ + if (version == PGP_SKSK_V5) { + pktbody.add(iv, ivlen); + } + /* encrypted key and auth tag for v5 */ + if (enckeylen) { + pktbody.add(enckey, enckeylen); + } + /* write packet */ + pktbody.write(dst); +} + +rnp_result_t +pgp_sk_sesskey_t::parse(pgp_source_t &src) +{ + pgp_packet_body_t pkt(PGP_PKT_SK_SESSION_KEY); + rnp_result_t res = pkt.read(src); + if (res) { + return res; + } + + /* version */ + uint8_t bt; + if (!pkt.get(bt) || ((bt != PGP_SKSK_V4) && (bt != PGP_SKSK_V5))) { + RNP_LOG("wrong packet version"); + return RNP_ERROR_BAD_FORMAT; + } + version = bt; + /* symmetric algorithm */ + if (!pkt.get(bt)) { + RNP_LOG("failed to get symm alg"); + return RNP_ERROR_BAD_FORMAT; + } + alg = (pgp_symm_alg_t) bt; + + if (version == PGP_SKSK_V5) { + /* aead algorithm */ + if (!pkt.get(bt)) { + RNP_LOG("failed to get aead alg"); + return RNP_ERROR_BAD_FORMAT; + } + aalg = (pgp_aead_alg_t) bt; + if ((aalg != PGP_AEAD_EAX) && (aalg != PGP_AEAD_OCB)) { + RNP_LOG("unsupported AEAD algorithm : %d", (int) aalg); + return RNP_ERROR_BAD_PARAMETERS; + } + } + + /* s2k */ + if (!pkt.get(s2k)) { + RNP_LOG("failed to parse s2k"); + return RNP_ERROR_BAD_FORMAT; + } + + /* v4 key */ + if (version == PGP_SKSK_V4) { + /* encrypted session key if present */ + size_t keylen = pkt.left(); + if (keylen) { + if (keylen > PGP_MAX_KEY_SIZE + 1) { + RNP_LOG("too long esk"); + return RNP_ERROR_BAD_FORMAT; + } + if (!pkt.get(enckey, keylen)) { + RNP_LOG("failed to get key"); + return RNP_ERROR_BAD_FORMAT; + } + } + enckeylen = keylen; + return RNP_SUCCESS; + } + + /* v5: iv + esk + tag. For both EAX and OCB ivlen and taglen are 16 octets */ + size_t noncelen = pgp_cipher_aead_nonce_len(aalg); + size_t taglen = pgp_cipher_aead_tag_len(aalg); + size_t keylen = 0; + + if (pkt.left() > noncelen + taglen + PGP_MAX_KEY_SIZE) { + RNP_LOG("too long esk"); + return RNP_ERROR_BAD_FORMAT; + } + if (pkt.left() < noncelen + taglen + 8) { + RNP_LOG("too short esk"); + return RNP_ERROR_BAD_FORMAT; + } + /* iv */ + if (!pkt.get(iv, noncelen)) { + RNP_LOG("failed to get iv"); + return RNP_ERROR_BAD_FORMAT; + } + ivlen = noncelen; + + /* key */ + keylen = pkt.left(); + if (!pkt.get(enckey, keylen)) { + RNP_LOG("failed to get key"); + return RNP_ERROR_BAD_FORMAT; + } + enckeylen = keylen; + return RNP_SUCCESS; +} + +void +pgp_pk_sesskey_t::write(pgp_dest_t &dst) const +{ + pgp_packet_body_t pktbody(PGP_PKT_PK_SESSION_KEY); + pktbody.add_byte(version); + pktbody.add(key_id); + pktbody.add_byte(alg); + pktbody.add(material_buf.data(), material_buf.size()); + pktbody.write(dst); +} + +rnp_result_t +pgp_pk_sesskey_t::parse(pgp_source_t &src) +{ + pgp_packet_body_t pkt(PGP_PKT_PK_SESSION_KEY); + rnp_result_t res = pkt.read(src); + if (res) { + return res; + } + /* version */ + uint8_t bt = 0; + if (!pkt.get(bt) || (bt != PGP_PKSK_V3)) { + RNP_LOG("wrong packet version"); + return RNP_ERROR_BAD_FORMAT; + } + version = bt; + /* key id */ + if (!pkt.get(key_id)) { + RNP_LOG("failed to get key id"); + return RNP_ERROR_BAD_FORMAT; + } + /* public key algorithm */ + if (!pkt.get(bt)) { + RNP_LOG("failed to get palg"); + return RNP_ERROR_BAD_FORMAT; + } + alg = (pgp_pubkey_alg_t) bt; + + /* raw signature material */ + if (!pkt.left()) { + RNP_LOG("No encrypted material"); + return RNP_ERROR_BAD_FORMAT; + } + try { + material_buf.resize(pkt.left()); + } catch (const std::exception &e) { + RNP_LOG("%s", e.what()); + return RNP_ERROR_OUT_OF_MEMORY; + } + /* we cannot fail here */ + pkt.get(material_buf.data(), material_buf.size()); + /* check whether it can be parsed */ + pgp_encrypted_material_t material = {}; + if (!parse_material(material)) { + return RNP_ERROR_BAD_FORMAT; + } + return RNP_SUCCESS; +} + +bool +pgp_pk_sesskey_t::parse_material(pgp_encrypted_material_t &material) const +{ + pgp_packet_body_t pkt(material_buf.data(), material_buf.size()); + switch (alg) { + case PGP_PKA_RSA: + case PGP_PKA_RSA_ENCRYPT_ONLY: + /* RSA m */ + if (!pkt.get(material.rsa.m)) { + RNP_LOG("failed to get rsa m"); + return false; + } + break; + case PGP_PKA_ELGAMAL: + case PGP_PKA_ELGAMAL_ENCRYPT_OR_SIGN: + /* ElGamal g, m */ + if (!pkt.get(material.eg.g) || !pkt.get(material.eg.m)) { + RNP_LOG("failed to get elgamal mpis"); + return false; + } + break; + case PGP_PKA_SM2: + /* SM2 m */ + if (!pkt.get(material.sm2.m)) { + RNP_LOG("failed to get sm2 m"); + return false; + } + break; + case PGP_PKA_ECDH: { + /* ECDH ephemeral point */ + if (!pkt.get(material.ecdh.p)) { + RNP_LOG("failed to get ecdh p"); + return false; + } + /* ECDH m */ + uint8_t bt = 0; + if (!pkt.get(bt)) { + RNP_LOG("failed to get ecdh m len"); + return false; + } + if (bt > ECDH_WRAPPED_KEY_SIZE) { + RNP_LOG("wrong ecdh m len"); + return false; + } + material.ecdh.mlen = bt; + if (!pkt.get(material.ecdh.m, bt)) { + RNP_LOG("failed to get ecdh m len"); + return false; + } + break; + } + default: + RNP_LOG("unknown pk alg %d", (int) alg); + return false; + } + + if (pkt.left()) { + RNP_LOG("extra %d bytes in pk packet", (int) pkt.left()); + return false; + } + return true; +} + +void +pgp_pk_sesskey_t::write_material(const pgp_encrypted_material_t &material) +{ + pgp_packet_body_t pktbody(PGP_PKT_PK_SESSION_KEY); + + switch (alg) { + case PGP_PKA_RSA: + case PGP_PKA_RSA_ENCRYPT_ONLY: + pktbody.add(material.rsa.m); + break; + case PGP_PKA_SM2: + pktbody.add(material.sm2.m); + break; + case PGP_PKA_ECDH: + pktbody.add(material.ecdh.p); + pktbody.add_byte(material.ecdh.mlen); + pktbody.add(material.ecdh.m, material.ecdh.mlen); + break; + case PGP_PKA_ELGAMAL: + pktbody.add(material.eg.g); + pktbody.add(material.eg.m); + break; + default: + RNP_LOG("Unknown pk alg: %d", (int) alg); + throw rnp::rnp_exception(RNP_ERROR_BAD_PARAMETERS); + } + material_buf = {pktbody.data(), pktbody.data() + pktbody.size()}; +} + +void +pgp_one_pass_sig_t::write(pgp_dest_t &dst) const +{ + pgp_packet_body_t pktbody(PGP_PKT_ONE_PASS_SIG); + pktbody.add_byte(version); + pktbody.add_byte(type); + pktbody.add_byte(halg); + pktbody.add_byte(palg); + pktbody.add(keyid); + pktbody.add_byte(nested); + pktbody.write(dst); +} + +rnp_result_t +pgp_one_pass_sig_t::parse(pgp_source_t &src) +{ + pgp_packet_body_t pkt(PGP_PKT_ONE_PASS_SIG); + /* Read the packet into memory */ + rnp_result_t res = pkt.read(src); + if (res) { + return res; + } + + uint8_t buf[13] = {0}; + if ((pkt.size() != 13) || !pkt.get(buf, 13)) { + return RNP_ERROR_BAD_FORMAT; + } + /* version */ + if (buf[0] != 3) { + RNP_LOG("wrong packet version"); + return RNP_ERROR_BAD_FORMAT; + } + version = buf[0]; + /* signature type */ + type = (pgp_sig_type_t) buf[1]; + /* hash algorithm */ + halg = (pgp_hash_alg_t) buf[2]; + /* pk algorithm */ + palg = (pgp_pubkey_alg_t) buf[3]; + /* key id */ + static_assert(std::tuple_size<decltype(keyid)>::value == PGP_KEY_ID_SIZE, + "pgp_one_pass_sig_t.keyid size mismatch"); + memcpy(keyid.data(), &buf[4], PGP_KEY_ID_SIZE); + /* nested flag */ + nested = buf[12]; + return RNP_SUCCESS; +} |