Source code
Revision control
Copy as Markdown
Other Tools
/*
* 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.
*/
#ifndef STREAM_PACKET_H_
#define STREAM_PACKET_H_
#include <stdint.h>
#include <stdbool.h>
#include <sys/types.h>
#include "types.h"
#include "stream-common.h"
/* maximum size of the 'small' packet */
#define PGP_MAX_PKT_SIZE 0x100000
/* maximum size of indeterminate-size packet allowed with old length format */
#define PGP_MAX_OLD_LEN_INDETERMINATE_PKT_SIZE 0x40000000
typedef struct pgp_packet_hdr_t {
pgp_pkt_type_t tag; /* packet tag */
uint8_t hdr[PGP_MAX_HEADER_SIZE]; /* PGP packet header, needed for AEAD */
size_t hdr_len; /* length of the header */
size_t pkt_len; /* packet body length if non-partial and non-indeterminate */
bool partial; /* partial length packet */
bool indeterminate; /* indeterminate length packet */
} pgp_packet_hdr_t;
/* structure for convenient writing or parsing of non-stream packets */
typedef struct pgp_packet_body_t {
private:
pgp_pkt_type_t tag_; /* packet tag */
std::vector<uint8_t> data_; /* packet bytes */
/* fields below are filled only for parsed packet */
uint8_t hdr_[PGP_MAX_HEADER_SIZE]{}; /* packet header bytes */
size_t hdr_len_{}; /* number of bytes in hdr */
size_t pos_{}; /* current read position in packet data */
bool secure_{}; /* contents of the packet are secure so must be wiped in the destructor */
public:
/** @brief initialize writing of packet body
* @param tag tag of the packet
**/
pgp_packet_body_t(pgp_pkt_type_t tag);
/** @brief init packet body (without headers) with memory. Used for easier data parsing.
* @param data buffer with packet body part
* @param len number of available bytes in mem
*/
pgp_packet_body_t(const uint8_t *data, size_t len);
pgp_packet_body_t(const pgp_packet_body_t &src) = delete;
pgp_packet_body_t(pgp_packet_body_t &&src) = delete;
pgp_packet_body_t &operator=(const pgp_packet_body_t &) = delete;
pgp_packet_body_t &operator=(pgp_packet_body_t &&) = delete;
~pgp_packet_body_t();
/** @brief pointer to the data, kept in the packet */
uint8_t *data() noexcept;
/** @brief number of bytes, kept in the packet (without the header) */
size_t size() const noexcept;
/** @brief number of bytes left to read */
size_t left() const noexcept;
/** @brief get next byte from the packet body, populated with read() call.
* @param val result will be stored here on success
* @return true on success or false otherwise (if end of the packet is reached)
**/
bool get(uint8_t &val) noexcept;
/** @brief get next big-endian uint16 from the packet body, populated with read() call.
* @param val result will be stored here on success
* @return true on success or false otherwise (if end of the packet is reached)
**/
bool get(uint16_t &val) noexcept;
/** @brief get next big-endian uint32 from the packet body, populated with read() call.
* @param val result will be stored here on success
* @return true on success or false otherwise (if end of the packet is reached)
**/
bool get(uint32_t &val) noexcept;
/** @brief get some bytes from the packet body, populated with read() call.
* @param val packet body bytes will be stored here. Must be capable of storing len bytes.
* @param len number of bytes to read
* @return true on success or false otherwise (if end of the packet is reached)
**/
bool get(uint8_t *val, size_t len) noexcept;
/** @brief get next keyid from the packet body, populated with read() call.
* @param val result will be stored here on success
* @return true on success or false otherwise (if end of the packet is reached)
**/
bool get(pgp_key_id_t &val) noexcept;
/** @brief get next mpi from the packet body, populated with read() call.
* @param val result will be stored here on success
* @return true on success or false otherwise (if end of the packet is reached
* or mpi is ill-formed)
**/
bool get(pgp_mpi_t &val) noexcept;
/** @brief Read ECC key curve and convert it to pgp_curve_t */
bool get(pgp_curve_t &val) noexcept;
/** @brief read s2k from the packet */
bool get(pgp_s2k_t &s2k) noexcept;
/** @brief append some bytes to the packet body */
void add(const void *data, size_t len);
/** @brief append single byte to the packet body */
void add_byte(uint8_t bt);
/** @brief append big endian 16-bit value to the packet body */
void add_uint16(uint16_t val);
/** @brief append big endian 32-bit value to the packet body */
void add_uint32(uint32_t val);
/** @brief append keyid to the packet body */
void add(const pgp_key_id_t &val);
/** @brief add pgp mpi (including header) to the packet body */
void add(const pgp_mpi_t &val);
/**
* @brief add pgp signature subpackets (including their length) to the packet body
* @param sig signature, containing subpackets
* @param hashed whether write hashed or not hashed subpackets
*/
void add_subpackets(const pgp_signature_t &sig, bool hashed);
/** @brief add ec curve description to the packet body */
void add(const pgp_curve_t curve);
/** @brief add s2k description to the packet body */
void add(const pgp_s2k_t &s2k);
/** @brief read 'short-length' packet body (including tag and length bytes) from the source
* @param src source to read from
* @return RNP_SUCCESS or error code if operation failed
**/
rnp_result_t read(pgp_source_t &src) noexcept;
/** @brief write packet header, length and body to the dst
* @param dst destination to write to.
* @param hdr write packet's header or not
**/
void write(pgp_dest_t &dst, bool hdr = true) noexcept;
/** @brief mark contents as secure, so secure_clear() must be called in the destructor */
void mark_secure(bool secure = true) noexcept;
} pgp_packet_body_t;
/** public-key encrypted session key packet */
typedef struct pgp_pk_sesskey_t {
unsigned version{};
pgp_key_id_t key_id{};
pgp_pubkey_alg_t alg{};
std::vector<uint8_t> material_buf{};
void write(pgp_dest_t &dst) const;
rnp_result_t parse(pgp_source_t &src);
/**
* @brief Parse encrypted material which is stored in packet in raw.
* @param material on success parsed material will be stored here.
* @return true on success or false otherwise. May also throw an exception.
*/
bool parse_material(pgp_encrypted_material_t &material) const;
/**
* @brief Write encrypted material to the material_buf.
* @param material populated encrypted material.
*/
void write_material(const pgp_encrypted_material_t &material);
} pgp_pk_sesskey_t;
/** pkp_sk_sesskey_t */
typedef struct pgp_sk_sesskey_t {
unsigned version{};
pgp_symm_alg_t alg{};
pgp_s2k_t s2k{};
uint8_t enckey[PGP_MAX_KEY_SIZE + PGP_AEAD_MAX_TAG_LEN + 1]{};
unsigned enckeylen{};
/* v5 specific fields */
pgp_aead_alg_t aalg{};
uint8_t iv[PGP_MAX_BLOCK_SIZE]{};
unsigned ivlen{};
void write(pgp_dest_t &dst) const;
rnp_result_t parse(pgp_source_t &src);
} pgp_sk_sesskey_t;
/** pgp_one_pass_sig_t */
typedef struct pgp_one_pass_sig_t {
uint8_t version{};
pgp_sig_type_t type{};
pgp_hash_alg_t halg{};
pgp_pubkey_alg_t palg{};
pgp_key_id_t keyid{};
unsigned nested{};
void write(pgp_dest_t &dst) const;
rnp_result_t parse(pgp_source_t &src);
} pgp_one_pass_sig_t;
/** Struct to hold userid or userattr packet. We don't parse userattr now, just storing the
* binary blob as it is. It may be distinguished by tag field.
*/
typedef struct pgp_userid_pkt_t {
pgp_pkt_type_t tag;
uint8_t * uid;
size_t uid_len;
pgp_userid_pkt_t() : tag(PGP_PKT_RESERVED), uid(NULL), uid_len(0){};
pgp_userid_pkt_t(const pgp_userid_pkt_t &src);
pgp_userid_pkt_t(pgp_userid_pkt_t &&src);
pgp_userid_pkt_t &operator=(pgp_userid_pkt_t &&src);
pgp_userid_pkt_t &operator=(const pgp_userid_pkt_t &src);
bool operator==(const pgp_userid_pkt_t &src) const;
bool operator!=(const pgp_userid_pkt_t &src) const;
~pgp_userid_pkt_t();
void write(pgp_dest_t &dst) const;
rnp_result_t parse(pgp_source_t &src);
} pgp_userid_pkt_t;
uint16_t read_uint16(const uint8_t *buf);
uint32_t read_uint32(const uint8_t *buf);
void write_uint16(uint8_t *buf, uint16_t val);
/** @brief write new packet length
* @param buf pre-allocated buffer, must have 5 bytes
* @param len packet length
* @return number of bytes, saved in buf
**/
size_t write_packet_len(uint8_t *buf, size_t len);
/** @brief get packet type from the packet header byte
* @param ptag first byte of the packet header
* @return packet type or -1 if ptag is wrong
**/
int get_packet_type(uint8_t ptag);
/** @brief peek the packet type from the stream
* @param src source to peek from
* @return packet tag or -1 if read failed or packet header is malformed
*/
int stream_pkt_type(pgp_source_t &src);
/** @brief Peek length of the packet header. Returns false on error.
* @param src source to read length from
* @param hdrlen header length will be put here on success. Cannot be NULL.
* @return true on success or false if there is a read error or packet length
* is ill-formed
**/
bool stream_pkt_hdr_len(pgp_source_t &src, size_t &hdrlen);
bool stream_old_indeterminate_pkt_len(pgp_source_t *src);
bool stream_partial_pkt_len(pgp_source_t *src);
size_t get_partial_pkt_len(uint8_t blen);
/** @brief Read packet length for fixed-size (say, small) packet. Returns false on error.
* Will also read packet tag byte. We do not allow partial length here as well as large
* packets (so ignoring possible size_t overflow)
*
* @param src source to read length from
* @param pktlen packet length will be stored here on success. Cannot be NULL.
* @return true on success or false if there is read error or packet length is ill-formed
**/
bool stream_read_pkt_len(pgp_source_t *src, size_t *pktlen);
/** @brief Read partial packet chunk length.
*
* @param src source to read length from
* @param clen chunk length will be stored here on success. Cannot be NULL.
* @param last will be set to true if chunk is last (i.e. has non-partial length)
* @return true on success or false if there is read error or packet length is ill-formed
**/
bool stream_read_partial_chunk_len(pgp_source_t *src, size_t *clen, bool *last);
/** @brief get and parse OpenPGP packet header to the structure.
* Note: this will not read but just peek required bytes.
*
* @param src source to read from
* @param hdr header structure
* @return RNP_SUCCESS or error code if operation failed
**/
rnp_result_t stream_peek_packet_hdr(pgp_source_t *src, pgp_packet_hdr_t *hdr);
/* Packet handling functions */
/** @brief read OpenPGP packet from the stream, and write its contents to another stream.
* @param src source with packet data
* @param dst destination to write packet contents. All write failures on dst
* will be ignored. Can be NULL if you need just to skip packet.
* @return RNP_SUCCESS or error code if operation failed.
*/
rnp_result_t stream_read_packet(pgp_source_t *src, pgp_dest_t *dst);
rnp_result_t stream_skip_packet(pgp_source_t *src);
rnp_result_t stream_parse_marker(pgp_source_t &src);
/* Public/Private key or Subkey */
bool is_key_pkt(int tag);
bool is_subkey_pkt(int tag);
bool is_primary_key_pkt(int tag);
bool is_public_key_pkt(int tag);
bool is_secret_key_pkt(int tag);
bool is_rsa_key_alg(pgp_pubkey_alg_t alg);
#endif