/* * 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 #include #include #ifdef HAVE_UNISTD_H #include #else #include "uniwin.h" #endif #include #include #include #include "types.h" #include "crypto.h" #include "crypto/mem.h" #include "stream-packet.h" #include "stream-key.h" #include 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::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::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; }