From 6fdec891169fb9682f97052baadb9f5ca94d5599 Mon Sep 17 00:00:00 2001 From: Marcus Brinkmann Date: Wed, 31 Oct 2018 19:24:13 +0100 Subject: [PATCH] More packet dump stuff (unfinished, in particular the json format). --- neopg-tool/CMakeLists.txt | 3 + neopg-tool/cli/packet/dump/hex_dump.cpp | 733 ++++++++++++++++++ neopg-tool/cli/packet/dump/hex_dump.h | 32 + neopg-tool/cli/packet/dump/json_dump.cpp | 589 ++++++++++++++ neopg-tool/cli/packet/dump/json_dump.h | 28 + neopg-tool/cli/packet/dump/legacy_dump.h | 14 +- neopg-tool/cli/packet/dump_packet_command.cpp | 85 +- neopg-tool/io/hex_filter.cpp | 8 + neopg-tool/io/hex_filter.h | 18 + neopg/openpgp/object_identifier.h | 2 +- neopg/openpgp/packet_header.h | 9 + 11 files changed, 1454 insertions(+), 67 deletions(-) create mode 100644 neopg-tool/cli/packet/dump/hex_dump.cpp create mode 100644 neopg-tool/cli/packet/dump/hex_dump.h create mode 100644 neopg-tool/cli/packet/dump/json_dump.cpp create mode 100644 neopg-tool/cli/packet/dump/json_dump.h create mode 100644 neopg-tool/io/hex_filter.cpp create mode 100644 neopg-tool/io/hex_filter.h diff --git a/neopg-tool/CMakeLists.txt b/neopg-tool/CMakeLists.txt index a1d957c8a..cbbf63323 100644 --- a/neopg-tool/CMakeLists.txt +++ b/neopg-tool/CMakeLists.txt @@ -16,12 +16,15 @@ add_library(neopg-tool STATIC cli/command.cpp cli/compress_command.cpp cli/hash_command.cpp + cli/packet/dump/hex_dump.cpp + cli/packet/dump/json_dump.cpp cli/packet/dump/legacy_dump.cpp cli/packet/dump_packet_command.cpp cli/packet/dump_packet_sink.cpp cli/packet_command.cpp cli/random_command.cpp cli/version_command.cpp + io/hex_filter.cpp io/streams.cpp ) add_library(neopg::neopg-tool ALIAS neopg-tool) diff --git a/neopg-tool/cli/packet/dump/hex_dump.cpp b/neopg-tool/cli/packet/dump/hex_dump.cpp new file mode 100644 index 000000000..206cafbaa --- /dev/null +++ b/neopg-tool/cli/packet/dump/hex_dump.cpp @@ -0,0 +1,733 @@ +// hex dump format (implementation) +// Copyright 2018 The NeoPG developers +// +// NeoPG is released under the Simplified BSD License (see license.txt) + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include + +#include +#include + +#include + +#include + +#include + +#include + +#include + +using namespace NeoPG; + +#define LINELEN 16 + +class HexDump::Formatter { + public: + size_t m_offset{0}; + std::ostream& m_out; + + void header(const PacketHeader* header, const std::string& comment); + void comment(const std::string& comment); + void hex(const std::vector& raw); + void hex(const std::vector& raw, const std::string& comment); + void hex(const std::string& raw); + void hex(const std::string& raw, const std::string& comment); + void hex(uint8_t val, const std::string& comment); + void hex(uint8_t val); + void hex(uint16_t val, const std::string& comment); + void hex(uint32_t val, const std::string& comment); + void hex(const MultiprecisionInteger& val, const std::string& comment); + void hex(const ObjectIdentifier& val, const std::string& comment); + void hex(const PublicKeyAlgorithm& val, const std::string& comment); + // void hex(const SymmetricKeyAlgorithm& val, const std::string& comment); + void hex(const HashAlgorithm& val, const std::string& comment); + void hex(const SignatureSubpacket* subpacket); + void hex(const V4SignatureSubpacketData* subpackets, + const std::string& comment); + Formatter(std::ostream& out, uint64_t offset = 0) + : m_out{out}, m_offset{offset} {}; +}; + +void HexDump::Formatter::header(const PacketHeader* header, + const std::string& name) { + m_out << "\n"; + // FIXME: Generate default header if necessary. + assert(header != nullptr); + + switch (header->format()) { + case PacketFormat::Old: { + auto hdr = dynamic_cast(header); + assert(hdr != nullptr); + auto length = hdr->length(); + + std::stringstream hdr_raw; + hdr->write(hdr_raw); + hex(hdr_raw.str(), + fmt::format("{:s} ({:d}, old, length {:d})", name, + static_cast(hdr->type()), length)); + } break; + case PacketFormat::New: { + auto hdr = dynamic_cast(header); + assert(hdr != nullptr); + auto length = hdr->length(); + + std::stringstream hdr_raw; + hdr->write(hdr_raw); + hex(hdr_raw.str(), + fmt::format("{:s} ({:d}, new, length {:d})", name, + static_cast(hdr->type()), length)); + } break; + default: + throw std::logic_error("unknown header type"); + // Unreachable. + } +} + +void HexDump::Formatter::comment(const std::string& comment) { + m_out << std::string(8 + 2 + LINELEN * 3 + 2 + LINELEN, ' ') << "; " + << comment << "\n"; +} + +void HexDump::Formatter::hex(const std::string& raw) { hex(raw, ""); } + +static std::string _escaped(const uint8_t* in, size_t sz) { + std::string result; + for (size_t i = 0; i < sz; i++) { + auto chr = (char)(in[i]); + if (std::isgraph(chr)) + result.push_back(chr); + else if (chr == ' ') + result.append("␣"); + else + result.append("⬚"); + } + return result; +} + +static void _line(std::ostream& out, size_t& offset, const std::string& raw, + size_t& idx, const std::string& comment) { + auto left = raw.size() - idx; + if (left >= LINELEN) left = LINELEN; + + auto data = reinterpret_cast(raw.data() + idx); + out << rang::fg::gray << fmt::format("{:08x}:", offset) << rang::style::reset; + for (int i = 0; i < left; i++) out << fmt::format(" {:02x}", data[i]); + out << std::string((LINELEN - left) * 3 + 2, ' '); + out << rang::fg::gray << _escaped(data, left) + << std::string(LINELEN - left, ' ') << rang::style::reset; + + if (comment != "") out << " ; " << comment; + out << "\n"; + offset += left; + idx += left; +} + +void HexDump::Formatter::hex(const std::string& raw, + const std::string& comment) { + auto left{raw.size()}; + size_t idx{0}; + + _line(m_out, m_offset, raw, idx, comment); + while (idx < raw.size()) { + _line(m_out, m_offset, raw, idx, ""); + } +} + +void HexDump::Formatter::hex(const std::vector& raw) { + std::string str(reinterpret_cast(raw.data()), raw.size()); + hex(str); +} + +void HexDump::Formatter::hex(const std::vector& raw, + const std::string& comment) { + std::string str(reinterpret_cast(raw.data()), raw.size()); + hex(str, comment); +} + +void HexDump::Formatter::hex(uint8_t val, const std::string& comment) { + std::string str; + str.push_back(val); + hex(str, comment); +} + +void HexDump::Formatter::hex(uint8_t val) { + std::string str; + str.push_back(val); + hex(str, ""); +} + +void HexDump::Formatter::hex(uint16_t val, const std::string& comment) { + std::string str; + str.push_back(static_cast(val >> 8)); + str.push_back(static_cast(val)); + hex(str, comment); +} + +void HexDump::Formatter::hex(uint32_t val, const std::string& comment) { + std::string str; + str.push_back(static_cast(val >> 24)); + str.push_back(static_cast(val >> 16)); + str.push_back(static_cast(val >> 8)); + str.push_back(static_cast(val)); + hex(str, comment); +} + +void HexDump::Formatter::hex(const MultiprecisionInteger& val, + const std::string& comment) { + hex(val.m_length, fmt::format("{:s} ({:d} bits)", comment, val.m_length)); + hex(val.m_bits); +} + +void HexDump::Formatter::hex(const ObjectIdentifier& val, + const std::string& comment) { + const std::string oidstr = val.as_string(); + Botan::OID oid(oidstr); + hex(static_cast(val.m_data.size()), comment); + hex(val.m_data, fmt::format("{:s} = {:s}", oidstr, Botan::OIDS::lookup(oid))); +} + +void HexDump::Formatter::hex(const PublicKeyAlgorithm& val, + const std::string& comment) { + hex(static_cast(val), comment); +} + +// void HexDump::Formatter::hex(const SymmetricKeyAlgorithm& val, +// const std::string& comment) { +// hex(static_cast(val), comment); +// } + +void HexDump::Formatter::hex(const HashAlgorithm& val, + const std::string& comment) { + hex(static_cast(val), comment); +} + +void HexDump::Formatter::hex(const SignatureSubpacket* subpacket) { + std::stringstream raw_stream; + subpacket->write(raw_stream); + auto raw = raw_stream.str(); + + switch (subpacket->type()) { + case SignatureSubpacketType::SignatureCreationTime: { + auto sub = dynamic_cast(subpacket); + assert(sub != nullptr); + hex(raw, fmt::format("- sig created {:d}", sub->m_created)); + } break; + case SignatureSubpacketType::SignatureExpirationTime: { + auto sub = + dynamic_cast(subpacket); + assert(sub != nullptr); + if (sub->m_expiration) + hex(raw, fmt::format("- sig expires after {:d}", sub->m_expiration)); + else + hex(raw, "- sig does not expire"); + } break; + case SignatureSubpacketType::ExportableCertification: { + auto sub = + dynamic_cast(subpacket); + assert(sub != nullptr); + if (sub->m_exportable) + hex(raw, "- exportable"); + else + hex(raw, "- not exportable"); + } break; + case SignatureSubpacketType::TrustSignature: { + auto sub = dynamic_cast(subpacket); + assert(sub != nullptr); + hex(raw, fmt::format("- trust signature of depth {:d}, value {:d}", + sub->m_level, sub->m_amount)); + } break; + case SignatureSubpacketType::RegularExpression: { + auto sub = dynamic_cast(subpacket); + assert(sub != nullptr); + const tao::json::value str = sub->m_regex; + hex(raw, + fmt::format("- regular expression: {:s}", tao::json::to_string(str))); + } break; + case SignatureSubpacketType::Revocable: { + auto sub = dynamic_cast(subpacket); + assert(sub != nullptr); + if (sub->m_revocable) + hex(raw, "- revocable"); + else + hex(raw, "- not revocable"); + } break; + case SignatureSubpacketType::KeyExpirationTime: { + auto sub = dynamic_cast(subpacket); + assert(sub != nullptr); + if (sub->m_expiration) { + int seconds = sub->m_expiration; + int minutes = seconds / 60; + int hours = minutes / 60; + int days = hours / 24; + int years = days / 365; + hex(raw, fmt::format("- key expires after {:d}y {:d}d {:d}h {:d}m", + years, days % 365, hours % 24, minutes % 60)); + } else + hex(raw, "- key does not expire"); + } break; + case SignatureSubpacketType::PreferredSymmetricAlgorithms: { + auto sub = + dynamic_cast(subpacket); + assert(sub != nullptr); + std::string msg("- pref-sym-algos:"); + for (auto& algorithm : sub->m_algorithms) + msg.append(fmt::format(" {:d}", algorithm)); + hex(raw, msg); + } break; + case SignatureSubpacketType::RevocationKey: { + auto sub = dynamic_cast(subpacket); + assert(sub != nullptr); + hex(raw, fmt::format("- revocation key: c={:02x} a={:d} f={:s}", + static_cast(sub->m_class), + static_cast(sub->m_algorithm), + Botan::hex_encode(sub->m_fingerprint.data(), + sub->m_fingerprint.size()))); + + } break; + case SignatureSubpacketType::Issuer: { + auto sub = dynamic_cast(subpacket); + assert(sub != nullptr); + hex(raw, fmt::format("- issuer key ID {:s}", + Botan::hex_encode(sub->m_issuer.data(), + sub->m_issuer.size()))); + } break; + case SignatureSubpacketType::NotationData: { + auto sub = dynamic_cast(subpacket); + assert(sub != nullptr); + // FIXME: Avoid copy? + const tao::json::value name = + std::string{reinterpret_cast(sub->m_name.data()), + sub->m_name.size()}; + const tao::json::value value = + std::string{reinterpret_cast(sub->m_value.data()), + sub->m_value.size()}; + hex(raw, + fmt::format("- notation: {:s} = {:s}", tao::json::to_string(name), + tao::json::to_string(value))); + } break; + case SignatureSubpacketType::PreferredHashAlgorithms: { + auto sub = + dynamic_cast(subpacket); + assert(sub != nullptr); + std::string msg = "- pref-hash-algos:"; + for (auto& algorithm : sub->m_algorithms) + msg.append(fmt::format(" {:d}", (int)algorithm)); + } break; + case SignatureSubpacketType::PreferredCompressionAlgorithms: { + auto sub = dynamic_cast( + subpacket); + assert(sub != nullptr); + std::string msg = "- pref-zip-algos:"; + for (auto& algorithm : sub->m_algorithms) + msg.append(fmt::format("{:d}", algorithm)); + } break; + case SignatureSubpacketType::KeyServerPreferences: { + auto sub = dynamic_cast(subpacket); + assert(sub != nullptr); + hex(raw, fmt::format("- keyserver preferences: {:s}", + Botan::hex_encode(sub->m_flags.data(), + sub->m_flags.size()))); + } break; + case SignatureSubpacketType::PreferredKeyServer: { + auto sub = dynamic_cast(subpacket); + assert(sub != nullptr); + const tao::json::value str = sub->m_uri; + hex(raw, fmt::format("- preferred keyserver: {:s}", + tao::json::to_string(str))); + } break; + case SignatureSubpacketType::PrimaryUserId: { + auto sub = dynamic_cast(subpacket); + assert(sub != nullptr); + hex(raw, fmt::format("- primary user ID: {:02x}", + static_cast(sub->m_primary))); + } break; + case SignatureSubpacketType::PolicyUri: { + auto sub = dynamic_cast(subpacket); + assert(sub != nullptr); + const tao::json::value str = sub->m_uri; + hex(raw, fmt::format("- policy: {:s}", tao::json::to_string(str))); + } break; + case SignatureSubpacketType::KeyFlags: { + auto sub = dynamic_cast(subpacket); + assert(sub != nullptr); + hex(raw, fmt::format("- key flags: {:s}", + Botan::hex_encode(sub->m_flags.data(), + sub->m_flags.size()))); + + } break; + case SignatureSubpacketType::SignersUserId: { + auto sub = dynamic_cast(subpacket); + assert(sub != nullptr); + const tao::json::value str = sub->m_user_id; + hex(raw, + fmt::format("- signer's user ID: {:s}", tao::json::to_string(str))); + } break; + case SignatureSubpacketType::ReasonForRevocation: { + auto sub = dynamic_cast(subpacket); + assert(sub != nullptr); + const tao::json::value str = sub->m_reason; + hex(raw, fmt::format("- revocation reason 0x{:02x} {:s}", + static_cast(sub->m_code), + tao::json::to_string(str))); + } break; + case SignatureSubpacketType::Features: { + auto sub = dynamic_cast(subpacket); + assert(sub != nullptr); + hex(raw, fmt::format("- features: {:s}", + Botan::hex_encode(sub->m_features.data(), + sub->m_features.size()))); + + } break; + case SignatureSubpacketType::SignatureTarget: { + auto sub = dynamic_cast(subpacket); + assert(sub != nullptr); + hex(raw, fmt::format( + "- signature target: pubkey_algo {:d}, digest algo " + "{:d}), digest ", + static_cast(sub->m_public_key_algorithm), + static_cast(sub->m_hash_algorithm), + Botan::hex_encode(sub->m_hash.data(), sub->m_hash.size()))); + + } break; + case SignatureSubpacketType::EmbeddedSignature: { + auto sub = dynamic_cast(subpacket); + assert(sub != nullptr); + ParserInput in(sub->m_signature.data(), sub->m_signature.size()); + auto sig = SignaturePacket::create(in); + + if (sig == nullptr || !sig->m_signature) + hex(raw, "- signature: invalid"); + else { + switch (sig->m_signature->version()) { + case SignatureVersion::V2: + case SignatureVersion::V3: { + auto v3sig = + dynamic_cast(sig->m_signature.get()); + assert(v3sig != nullptr); + hex(raw, + fmt::format("- signature: v{:d}, class 0x{:02x}, algo {:d}, " + "digest algo {:d}", + static_cast(v3sig->version()), + static_cast(v3sig->signature_type()), + static_cast(v3sig->public_key_algorithm()), + static_cast(v3sig->hash_algorithm()))); + break; + } + case SignatureVersion::V4: { + auto v4sig = + dynamic_cast(sig->m_signature.get()); + assert(v4sig != nullptr); + hex(raw, + fmt::format("- signature: v{:d}, class 0x{:02x}, algo {:d}, " + "digest algo {:d}", + static_cast(v4sig->version()), + static_cast(v4sig->signature_type()), + static_cast(v4sig->public_key_algorithm()), + static_cast(v4sig->hash_algorithm()))); + break; + } + default: + hex(raw, fmt::format("- signature: v{:d}", + static_cast(sig->version()))); + break; + } + } + break; + } + default: + hex(raw, + fmt::format("- raw ({:d})", static_cast(subpacket->type()))); + break; + } +} + +void HexDump::Formatter::hex(const V4SignatureSubpacketData* subpackets, + const std::string& comment) { + CountingStream cnt; + subpackets->write(cnt); + auto bytes = cnt.bytes_written(); + hex(static_cast(bytes), comment); + + for (const auto& subpacket : subpackets->m_subpackets) { + hex(subpacket.get()); + } +} + +// Output helpers +static void output_public_key_data(HexDump::Formatter* fmt, + const PublicKeyData* pub) { + auto keyid = pub->keyid(); + fmt->hex( + static_cast(pub->version()), + fmt::format("version {:d}, key id {:s}", static_cast(pub->version()), + Botan::hex_encode(keyid.data(), keyid.size()))); + PublicKeyMaterial* key = nullptr; + switch (pub->version()) { + case PublicKeyVersion::V2: + case PublicKeyVersion::V3: { + auto v3pub = dynamic_cast(pub); + fmt->hex(v3pub->m_created, "creation time"); + fmt->hex(v3pub->m_days_valid, + fmt::format("days valid ({:s})", + v3pub->m_days_valid + ? fmt::format("{:d}d", v3pub->m_days_valid) + : "forever")); + fmt->hex(v3pub->m_algorithm, "public key algorithm"); + key = v3pub->m_key.get(); + } break; + case PublicKeyVersion::V4: { + auto v4pub = dynamic_cast(pub); + fmt->hex(v4pub->m_created, "creation time"); + fmt->hex(v4pub->m_algorithm, "public key algorithm"); + key = v4pub->m_key.get(); + } break; + default: + throw std::logic_error("unknown public key version"); + // Unreachable. + } + if (key) { + switch (key->algorithm()) { + case PublicKeyAlgorithm::Rsa: { + auto rsa = dynamic_cast(key); + fmt->hex(rsa->m_n, "rsa n"); + fmt->hex(rsa->m_e, "rsa e"); + } break; + case PublicKeyAlgorithm::Dsa: { + auto dsa = dynamic_cast(key); + fmt->hex(dsa->m_p, "dsa p"); + fmt->hex(dsa->m_q, "dsa q"); + fmt->hex(dsa->m_g, "dsa g"); + fmt->hex(dsa->m_y, "dsa y"); + } break; + case PublicKeyAlgorithm::Elgamal: { + auto elgamal = dynamic_cast(key); + fmt->hex(elgamal->m_p, "elgamal p"); + fmt->hex(elgamal->m_g, "elgamal g"); + fmt->hex(elgamal->m_y, "elgamal y"); + } break; + case PublicKeyAlgorithm::Ecdsa: { + auto ecdsa = dynamic_cast(key); + const std::string oidstr = ecdsa->m_curve.as_string(); + Botan::OID oid(oidstr); + fmt->hex(ecdsa->m_curve, "ecdsa curve"); + fmt->hex(ecdsa->m_key, "ecdsa key"); + } break; + case PublicKeyAlgorithm::Ecdh: { + auto ecdh = dynamic_cast(key); + const std::string oidstr = ecdh->m_curve.as_string(); + Botan::OID oid(oidstr); + fmt->hex(ecdh->m_curve, "ecdh curve"); + fmt->hex(ecdh->m_key, "ecdh key"); + std::string hdr; + hdr.push_back(0x03); + hdr.push_back(0x01); + fmt->hex(hdr); + fmt->hex(ecdh->m_hash, "ecdh hash algorithm"); + fmt->hex(ecdh->m_sym, "ecdh symmetric key algorithm"); + } break; + case PublicKeyAlgorithm::Eddsa: { + auto eddsa = dynamic_cast(key); + const std::string oidstr = eddsa->m_curve.as_string(); + Botan::OID oid(oidstr); + fmt->hex(eddsa->m_curve, "curve"); + fmt->hex(eddsa->m_key, "key"); + } break; + default: + auto raw = dynamic_cast(key); + assert(raw != nullptr); + fmt->hex(raw->m_content, "raw"); + } + } +} + +static void output_signature_data(HexDump::Formatter* fmt, + const SignatureData* sig) { + const SignatureMaterial* sigmat = nullptr; + fmt->hex(static_cast(sig->version()), + fmt::format("version {:d}", static_cast(sig->version()))); + + switch (sig->version()) { + case SignatureVersion::V2: + case SignatureVersion::V3: { + auto v3sig = dynamic_cast(sig); + assert(v3sig != nullptr); + fmt->hex(0x05); + fmt->hex( + static_cast(v3sig->signature_type()), + fmt::format("type {:d}", static_cast(v3sig->signature_type()))); + fmt->hex(v3sig->m_created, "creation time"); + fmt->hex( + std::vector(v3sig->m_signer.begin(), v3sig->m_signer.end()), + "signer's key id"); + fmt->hex(v3sig->m_public_key_algorithm, "public key algorithm"); + fmt->hex(v3sig->m_hash_algorithm, "hash algorithm"); + fmt->hex( + std::vector(v3sig->m_quick.begin(), v3sig->m_quick.end()), + "quick check"); + sigmat = v3sig->m_signature.get(); + } break; + case SignatureVersion::V4: { + auto v4sig = dynamic_cast(sig); + assert(v4sig != nullptr); + + fmt->hex( + static_cast(v4sig->signature_type()), + fmt::format("type {:d}", static_cast(v4sig->signature_type()))); + fmt->hex(v4sig->m_public_key_algorithm, "public key algorithm"); + fmt->hex(v4sig->m_hash_algorithm, "hash algorithm"); + fmt->hex(v4sig->m_hashed_subpackets.get(), "hashed subpackets"); + fmt->hex(v4sig->m_unhashed_subpackets.get(), "unhashed subpackets"); + fmt->hex( + std::vector(v4sig->m_quick.begin(), v4sig->m_quick.end()), + "quick check"); + sigmat = v4sig->m_signature.get(); + } break; + default: + std::logic_error("unknown signature version"); + // Not reached. + break; + } + if (sigmat) { + switch (sigmat->algorithm()) { + case PublicKeyAlgorithm::Rsa: { + auto rsa = dynamic_cast(sigmat); + fmt->hex(rsa->m_m_pow_d, "rsa m^d"); + } break; + case PublicKeyAlgorithm::Dsa: { + auto dsa = dynamic_cast(sigmat); + fmt->hex(dsa->m_r, "dsa r"); + fmt->hex(dsa->m_s, "dsa s"); + } break; + case PublicKeyAlgorithm::Ecdsa: { + auto ecdsa = dynamic_cast(sigmat); + fmt->hex(ecdsa->m_r, "ecdsa r"); + fmt->hex(ecdsa->m_s, "ecdsa s"); + } break; + case PublicKeyAlgorithm::Eddsa: { + auto eddsa = dynamic_cast(sigmat); + fmt->hex(eddsa->m_r, "eddsa r"); + fmt->hex(eddsa->m_s, "eddsa s"); + } break; + default: + std::logic_error("x"); + // Never reached. + return; + } + } +} + +namespace NeoPG { +// https://herbsutter.com/gotw/_102/ +template +std::unique_ptr make_unique(Args&&... args) { + return std::unique_ptr(new T(std::forward(args)...)); +} +} // namespace NeoPG + +HexDump::~HexDump() = default; + +HexDump::HexDump(std::ostream& out) : DumpPacketSink(out) {} + +void HexDump::dump(const Packet* packet) const { + std::stringstream str; + + m_fmt = NeoPG::make_unique(str, packet->m_header->m_offset); + DumpPacketSink::dump(packet); + m_fmt.reset(); + + m_out << str.str(); +} + +void HexDump::dump(const MarkerPacket* packet) const { + m_fmt->header(packet->m_header.get(), "marker"); + m_fmt->hex("PGP"); +} + +void HexDump::dump(const UserIdPacket* uid) const { + m_fmt->header(uid->m_header.get(), "user id"); + const tao::json::value str = uid->m_content; + m_fmt->hex(uid->m_content, tao::json::to_string(str)); +} + +void HexDump::dump(const UserAttributePacket* attr) const {} + +void HexDump::dump(const PublicKeyPacket* pubkey) const { + m_fmt->header(pubkey->m_header.get(), "public key"); + auto pub = dynamic_cast(pubkey->m_public_key.get()); + assert(pub != nullptr); + output_public_key_data(m_fmt.get(), pub); +} + +void HexDump::dump(const PublicSubkeyPacket* pubkey) const { + m_fmt->header(pubkey->m_header.get(), "public sub key"); + auto pub = dynamic_cast(pubkey->m_public_key.get()); + assert(pub != nullptr); + output_public_key_data(m_fmt.get(), pub); +} + +void HexDump::dump(const SignaturePacket* signature) const { + m_fmt->header(signature->m_header.get(), "signature"); + auto sig = dynamic_cast(signature->m_signature.get()); + assert(sig); + output_signature_data(m_fmt.get(), sig); +} diff --git a/neopg-tool/cli/packet/dump/hex_dump.h b/neopg-tool/cli/packet/dump/hex_dump.h new file mode 100644 index 000000000..ae910423a --- /dev/null +++ b/neopg-tool/cli/packet/dump/hex_dump.h @@ -0,0 +1,32 @@ +// hex dump format +// Copyright 2018 The NeoPG developers +// +// NeoPG is released under the Simplified BSD License (see license.txt) + +#pragma once + +#include + +namespace NeoPG { + +/// Hex dump format like GnuPG. +class HexDump : public DumpPacketSink { + public: + class Formatter; + mutable std::unique_ptr m_fmt; + + /// Dispatcher. + void dump(const Packet* packet) const override; + + void dump(const MarkerPacket* packet) const override; + void dump(const UserIdPacket* packet) const override; + void dump(const UserAttributePacket* packet) const override; + void dump(const PublicKeyPacket* packet) const override; + void dump(const PublicSubkeyPacket* packet) const override; + void dump(const SignaturePacket* packet) const override; + + HexDump(std::ostream& out); + ~HexDump(); +}; + +} // Namespace NeoPG diff --git a/neopg-tool/cli/packet/dump/json_dump.cpp b/neopg-tool/cli/packet/dump/json_dump.cpp new file mode 100644 index 000000000..530d6f50a --- /dev/null +++ b/neopg-tool/cli/packet/dump/json_dump.cpp @@ -0,0 +1,589 @@ +// json dump format (implementation) +// Copyright 2018 The NeoPG developers +// +// NeoPG is released under the Simplified BSD License (see license.txt) + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include + +#include + +#include + +#include + +#include + +#include + +using namespace NeoPG; + +static tao::json::value make_header(const PacketHeader* header) { + if (header == nullptr) return tao::json::empty_object; + + switch (header->format()) { + case PacketFormat::Old: { + auto hdr = dynamic_cast(header); + assert(hdr != nullptr); + auto length = hdr->length(); + + tao::json::value packet_header( + {{"format", "old"}, + {"type", static_cast(hdr->type())}, + {"length", length}, + {"_offset", hdr->m_offset}}); + if (hdr->m_length_type != OldPacketHeader::best_length_type(length)) + // FIXME: replace static cast + packet_header.insert( + {{"length_type", static_cast(hdr->m_length_type)}}); + return {{"_packet_header", packet_header}}; + } + case PacketFormat::New: { + auto hdr = dynamic_cast(header); + assert(hdr != nullptr); + auto length = hdr->length(); + + tao::json::value packet_header( + {{"format", "new"}, + {"type", static_cast(hdr->type())}, + {"length", length}, + {"_offset", hdr->m_offset}}); + if (hdr->m_length.m_length_type != + NewPacketLength::best_length_type(length)) + // FIXME: replace static cast + packet_header.insert( + {{"length_type", + static_cast(hdr->m_length.m_length_type)}}); + return {{"_packet_header", packet_header}}; + } + default: + throw std::logic_error("unknown header type"); + // Unreachable. + return tao::json::null; + } +} + +static void output_public_key_data(std::ostream& out, + const PublicKeyData* pub) { + PublicKeyMaterial* key = nullptr; + switch (pub->version()) { + case PublicKeyVersion::V2: + case PublicKeyVersion::V3: { + auto v3pub = dynamic_cast(pub); + out << "\tversion " << static_cast(pub->version()) << ", algo " + << static_cast(v3pub->m_algorithm) << ", created " + << v3pub->m_created << ", expires " << v3pub->m_days_valid << "\n"; + key = v3pub->m_key.get(); + } break; + case PublicKeyVersion::V4: { + auto v4pub = dynamic_cast(pub); + out << "\tversion " << static_cast(pub->version()) << ", algo " + << static_cast(v4pub->m_algorithm) << ", created " + << v4pub->m_created << ", expires 0" + << "\n"; + key = v4pub->m_key.get(); + } break; + default: + out << "\tversion " << static_cast(pub->version()) << "\n"; + break; + } + if (key) { + switch (key->algorithm()) { + case PublicKeyAlgorithm::Rsa: { + auto rsa = dynamic_cast(key); + out << "\tpkey[0]: [" << rsa->m_n.length() << " bits]\n"; + out << "\tpkey[1]: [" << rsa->m_e.length() << " bits]\n"; + } break; + case PublicKeyAlgorithm::Dsa: { + auto dsa = dynamic_cast(key); + out << "\tpkey[0]: [" << dsa->m_p.length() << " bits]\n"; + out << "\tpkey[1]: [" << dsa->m_q.length() << " bits]\n"; + out << "\tpkey[2]: [" << dsa->m_g.length() << " bits]\n"; + out << "\tpkey[3]: [" << dsa->m_y.length() << " bits]\n"; + } break; + case PublicKeyAlgorithm::Elgamal: { + auto elgamal = dynamic_cast(key); + out << "\tpkey[0]: [" << elgamal->m_p.length() << " bits]\n"; + out << "\tpkey[1]: [" << elgamal->m_g.length() << " bits]\n"; + out << "\tpkey[2]: [" << elgamal->m_y.length() << " bits]\n"; + } break; + case PublicKeyAlgorithm::Ecdsa: { + auto ecdsa = dynamic_cast(key); + const std::string oidstr = ecdsa->m_curve.as_string(); + Botan::OID oid(oidstr); + out << "\tpkey[0]: [" << (1 + ecdsa->m_curve.length()) * 8 << " bits] " + << Botan::OIDS::lookup(oid) << " (" << oidstr << ")\n"; + out << "\tpkey[1]: [" << ecdsa->m_key.length() << " bits]\n"; + } break; + case PublicKeyAlgorithm::Eddsa: { + auto eddsa = dynamic_cast(key); + const std::string oidstr = eddsa->m_curve.as_string(); + Botan::OID oid(oidstr); + out << "\tpkey[0]: [" << (1 + eddsa->m_curve.length()) * 8 << " bits] " + << Botan::OIDS::lookup(oid) << " (" << oidstr << ")\n"; + out << "\tpkey[1]: [" << eddsa->m_key.length() << " bits]\n"; + } break; + default: + out << "\tunknown algorithm " << static_cast(key->algorithm()) + << "\n"; + break; + } + auto keyid = pub->keyid(); + out << "\tkeyid: " << Botan::hex_encode(keyid.data(), keyid.size()) << "\n"; + } +} + +static void output_signature_subpacket(std::ostream& out, + const std::string& variant, + SignatureSubpacket* subpacket) { + out << "\t" << (subpacket->m_critical ? "critical " : "") << variant << " " + << static_cast(subpacket->type()) << " len " + << subpacket->body_length(); + switch (subpacket->type()) { + case SignatureSubpacketType::SignatureCreationTime: { + auto sub = dynamic_cast(subpacket); + assert(sub != nullptr); + out << " (sig created " << sub->m_created << ")"; + break; + } + case SignatureSubpacketType::SignatureExpirationTime: { + auto sub = + dynamic_cast(subpacket); + assert(sub != nullptr); + if (sub->m_expiration) + out << " (sig expires after " << sub->m_expiration << ")"; + else + out << " (sig does not expire)"; + break; + } + case SignatureSubpacketType::ExportableCertification: { + auto sub = + dynamic_cast(subpacket); + assert(sub != nullptr); + if (sub->m_exportable) + out << " (exportable)"; + else + out << " (not exportable)"; + break; + } + case SignatureSubpacketType::TrustSignature: { + auto sub = dynamic_cast(subpacket); + assert(sub != nullptr); + out << " (trust signature of depth " << (int)sub->m_level << ", value " + << (int)sub->m_amount << ")"; + break; + } + case SignatureSubpacketType::RegularExpression: { + auto sub = dynamic_cast(subpacket); + assert(sub != nullptr); + const tao::json::value str = sub->m_regex; + out << " (regular expression: " << str << ")"; + break; + } + case SignatureSubpacketType::Revocable: { + auto sub = dynamic_cast(subpacket); + assert(sub != nullptr); + if (sub->m_revocable) + out << " (revocable)"; + else + out << " (not revocable)"; + break; + } + case SignatureSubpacketType::KeyExpirationTime: { + auto sub = dynamic_cast(subpacket); + assert(sub != nullptr); + if (sub->m_expiration) { + int seconds = sub->m_expiration; + int minutes = seconds / 60; + int hours = minutes / 60; + int days = hours / 24; + int years = days / 365; + out << " (key expires after " << years << "y" << (days % 365) << "d" + << (hours % 24) << "h" << (minutes % 60) << "m)"; + } else + out << " (key does not expire)"; + break; + } + case SignatureSubpacketType::PreferredSymmetricAlgorithms: { + auto sub = + dynamic_cast(subpacket); + assert(sub != nullptr); + out << " (pref-sym-algos:"; + for (auto& algorithm : sub->m_algorithms) out << " " << (int)algorithm; + out << ")"; + break; + } + case SignatureSubpacketType::RevocationKey: { + auto sub = dynamic_cast(subpacket); + assert(sub != nullptr); + out << " (revocation key: c=" + << fmt::format("{:02x}", static_cast(sub->m_class)) + << " a=" << static_cast(sub->m_algorithm) << " f=" + << Botan::hex_encode(sub->m_fingerprint.data(), + sub->m_fingerprint.size()) + << ")"; + break; + } + case SignatureSubpacketType::Issuer: { + auto sub = dynamic_cast(subpacket); + assert(sub != nullptr); + out << " (issuer key ID " + << Botan::hex_encode(sub->m_issuer.data(), sub->m_issuer.size()) + << ")"; + break; + } + case SignatureSubpacketType::NotationData: { + auto sub = dynamic_cast(subpacket); + assert(sub != nullptr); + // FIXME: Avoid copy? + const tao::json::value name = + std::string{reinterpret_cast(sub->m_name.data()), + sub->m_name.size()}; + const tao::json::value value = + std::string{reinterpret_cast(sub->m_value.data()), + sub->m_value.size()}; + out << " (notation: " << name << " = " << value << ")"; + break; + } + case SignatureSubpacketType::PreferredHashAlgorithms: { + auto sub = + dynamic_cast(subpacket); + assert(sub != nullptr); + out << " (pref-hash-algos:"; + for (auto& algorithm : sub->m_algorithms) out << " " << (int)algorithm; + out << ")"; + break; + } + case SignatureSubpacketType::PreferredCompressionAlgorithms: { + auto sub = dynamic_cast( + subpacket); + assert(sub != nullptr); + out << " (pref-zip-algos:"; + for (auto& algorithm : sub->m_algorithms) out << " " << (int)algorithm; + out << ")"; + break; + } + case SignatureSubpacketType::KeyServerPreferences: { + auto sub = dynamic_cast(subpacket); + assert(sub != nullptr); + out << " (keyserver preferences: " + << Botan::hex_encode(sub->m_flags.data(), sub->m_flags.size()) << ")"; + break; + } + case SignatureSubpacketType::PreferredKeyServer: { + auto sub = dynamic_cast(subpacket); + assert(sub != nullptr); + const tao::json::value str = sub->m_uri; + out << " (preferred keyserver: " << str << ")"; + break; + } + case SignatureSubpacketType::PrimaryUserId: { + auto sub = dynamic_cast(subpacket); + assert(sub != nullptr); + out << " (primary user ID: " + << fmt::format("{:02x}", static_cast(sub->m_primary)) << ")"; + break; + } + case SignatureSubpacketType::PolicyUri: { + auto sub = dynamic_cast(subpacket); + assert(sub != nullptr); + const tao::json::value str = sub->m_uri; + out << " (policy: " << str << ")"; + break; + } + case SignatureSubpacketType::KeyFlags: { + auto sub = dynamic_cast(subpacket); + assert(sub != nullptr); + out << " (key flags: " + << Botan::hex_encode(sub->m_flags.data(), sub->m_flags.size()) << ")"; + break; + } + case SignatureSubpacketType::SignersUserId: { + auto sub = dynamic_cast(subpacket); + assert(sub != nullptr); + const tao::json::value str = sub->m_user_id; + out << " (signer's user ID: " << str << ")"; + break; + } + case SignatureSubpacketType::ReasonForRevocation: { + auto sub = dynamic_cast(subpacket); + assert(sub != nullptr); + const tao::json::value str = sub->m_reason; + out << " (revocation reason 0x" + << fmt::format("{:02x}", static_cast(sub->m_code)) << " " << str + << ")"; + break; + } + case SignatureSubpacketType::Features: { + auto sub = dynamic_cast(subpacket); + assert(sub != nullptr); + out << " (features: " + << Botan::hex_encode(sub->m_features.data(), sub->m_features.size()) + << ")"; + break; + } + case SignatureSubpacketType::SignatureTarget: { + auto sub = dynamic_cast(subpacket); + assert(sub != nullptr); + out << fmt::format( + " (signature target: pubkey_algo {:d}, digest algo " + "{:d}), digest ", + static_cast(sub->m_public_key_algorithm), + static_cast(sub->m_hash_algorithm)) + << Botan::hex_encode(sub->m_hash.data(), sub->m_hash.size()) << ")"; + break; + } + case SignatureSubpacketType::EmbeddedSignature: { + auto sub = dynamic_cast(subpacket); + assert(sub != nullptr); + ParserInput in(sub->m_signature.data(), sub->m_signature.size()); + auto sig = SignaturePacket::create(in); + + if (sig == nullptr || !sig->m_signature) + out << " (signature: invalid)"; + else { + switch (sig->m_signature->version()) { + case SignatureVersion::V2: + case SignatureVersion::V3: { + auto v3sig = + dynamic_cast(sig->m_signature.get()); + assert(v3sig != nullptr); + out << fmt::format( + " (signature: v{:d}, class 0x{:02x}, algo {:d}, digest algo " + "{:d})", + static_cast(v3sig->version()), + static_cast(v3sig->signature_type()), + static_cast(v3sig->public_key_algorithm()), + static_cast(v3sig->hash_algorithm())); + break; + } + case SignatureVersion::V4: { + auto v4sig = + dynamic_cast(sig->m_signature.get()); + assert(v4sig != nullptr); + out << fmt::format( + " (signature: v{:d}, class 0x{:02x}, algo {:d}, digest algo " + "{:d})", + static_cast(v4sig->version()), + static_cast(v4sig->signature_type()), + static_cast(v4sig->public_key_algorithm()), + static_cast(v4sig->hash_algorithm())); + break; + } + default: + out << fmt::format(" (signature: v{:d})", + static_cast(sig->version())); + break; + } + } + break; + } + default: + break; + } + out << "\n"; +} + +static void output_signature_data(std::ostream& out, const SignatureData* sig) { + const SignatureMaterial* sigmat = nullptr; + switch (sig->version()) { + case SignatureVersion::V2: + case SignatureVersion::V3: { + auto v3sig = dynamic_cast(sig); + assert(v3sig != nullptr); + out << ":signature packet: algo " + << static_cast(v3sig->public_key_algorithm()) << "\n"; + out << "\tversion 3, created " << v3sig->m_created + << ", md5len 5, sigclass 0x" + << fmt::format("{:02x}", static_cast(v3sig->signature_type())) + << "\n"; + out << "\tdigest algo " << static_cast(v3sig->hash_algorithm()) + << ", begin of digest " + << fmt::format("{:02x}", static_cast(v3sig->m_quick.data()[0])) + << " " + << fmt::format("{:02x}", static_cast(v3sig->m_quick.data()[1])) + << "\n"; + sigmat = v3sig->m_signature.get(); + } break; + case SignatureVersion::V4: { + auto v4sig = dynamic_cast(sig); + assert(v4sig != nullptr); + // FIXME: Try to get created from subpackets. + out << ":signature packet: algo " + << static_cast(v4sig->public_key_algorithm()) << "\n"; + out << "\tversion 4, created " << v4sig->m_created + << ", md5len 0, sigclass 0x" + << fmt::format("{:02x}", static_cast(v4sig->signature_type())) + << "\n"; + out << "\tdigest algo " << static_cast(v4sig->hash_algorithm()) + << ", begin of digest " + << fmt::format("{:02x}", static_cast(v4sig->m_quick.data()[0])) + << " " + << fmt::format("{:02x}", static_cast(v4sig->m_quick.data()[1])) + << "\n"; + for (auto& subpacket : v4sig->m_hashed_subpackets->m_subpackets) { + output_signature_subpacket(out, "hashed subpkt", subpacket.get()); + } + for (auto& subpacket : v4sig->m_unhashed_subpackets->m_subpackets) { + output_signature_subpacket(out, "subpkt", subpacket.get()); + } + sigmat = v4sig->m_signature.get(); + } break; + default: + break; + } + if (sigmat) { + switch (sigmat->algorithm()) { + case PublicKeyAlgorithm::Rsa: { + auto rsa = dynamic_cast(sigmat); + out << "\tdata: [" << rsa->m_m_pow_d.length() << " bits]\n"; + } break; + case PublicKeyAlgorithm::Dsa: { + auto dsa = dynamic_cast(sigmat); + out << "\tdata: [" << dsa->m_r.length() << " bits]\n"; + out << "\tdata: [" << dsa->m_s.length() << " bits]\n"; + } break; + case PublicKeyAlgorithm::Ecdsa: { + auto ecdsa = dynamic_cast(sigmat); + out << "\tdata: [" << ecdsa->m_r.length() << " bits]\n"; + out << "\tdata: [" << ecdsa->m_s.length() << " bits]\n"; + } break; + case PublicKeyAlgorithm::Eddsa: { + auto eddsa = dynamic_cast(sigmat); + out << "\tdata: [" << eddsa->m_r.length() << " bits]\n"; + out << "\tdata: [" << eddsa->m_s.length() << " bits]\n"; + } break; + default: + out << "\tunknown algorithm " << static_cast(sigmat->algorithm()) + << "\n"; + break; + } + } +} + +void JsonDump::dump(const Packet* packet) const { + DumpPacketSink::dump(packet); + m_out << "\n"; +} + +void JsonDump::dump(const MarkerPacket* packet) const { + auto val = make_header(packet->m_header.get()); + val.insert({{"_packet", "Marker"}}); + m_out << val; +} + +void JsonDump::dump(const UserIdPacket* uid) const { + auto val = make_header(uid->m_header.get()); + val.insert({{"_packet", "UserId"}, {"content", uid->m_content}}); + m_out << val; +} + +void JsonDump::dump(const UserAttributePacket* attr) const { + auto val = make_header(attr->m_header.get()); + tao::json::value subpackets = tao::json::empty_array; + for (const auto& sub : attr->m_subpackets) { + switch (sub->type()) { + case UserAttributeSubpacketType::Image: { + auto img = dynamic_cast(sub.get()); + assert(img != nullptr); + // FIXME: replace static cast, add subpacket header data + tao::json::value subpacket = { + {"type", "Image"}, + {"encoding", static_cast(img->m_encoding)}, + {"size", img->m_image.size()}}; + subpackets.append({subpacket}); + } break; + default: { + tao::json::value subpacket = { + // FIXME: replace static cast, add subpacket header data + {"type", "Raw"}, + {"_type", static_cast(sub->type())}, + {"size", sub->body_length()}}; + subpackets.append({subpacket}); + + break; + } + } + } + val.insert({{"_packet", "UserAttribute"}, {"subpackets", subpackets}}); + m_out << val; +} + +void JsonDump::dump(const PublicKeyPacket* pubkey) const { + m_out << ":public key packet:\n"; + auto pub = dynamic_cast(pubkey->m_public_key.get()); + assert(pub != nullptr); + output_public_key_data(m_out, pub); +} + +void JsonDump::dump(const PublicSubkeyPacket* pubkey) const { + m_out << ":public sub key packet:\n"; + auto pub = dynamic_cast(pubkey->m_public_key.get()); + assert(pub != nullptr); + output_public_key_data(m_out, pub); +} + +void JsonDump::dump(const SignaturePacket* signature) const { + auto sig = dynamic_cast(signature->m_signature.get()); + assert(sig); + output_signature_data(m_out, sig); +} diff --git a/neopg-tool/cli/packet/dump/json_dump.h b/neopg-tool/cli/packet/dump/json_dump.h new file mode 100644 index 000000000..a487414cd --- /dev/null +++ b/neopg-tool/cli/packet/dump/json_dump.h @@ -0,0 +1,28 @@ +// json dump format +// Copyright 2018 The NeoPG developers +// +// NeoPG is released under the Simplified BSD License (see license.txt) + +#pragma once + +#include + +namespace NeoPG { + +/// Json dump format like GnuPG. +class JsonDump : public DumpPacketSink { + public: + /// Dispatcher. + void dump(const Packet* packet) const override; + + void dump(const MarkerPacket* packet) const override; + void dump(const UserIdPacket* packet) const override; + void dump(const UserAttributePacket* packet) const override; + void dump(const PublicKeyPacket* packet) const override; + void dump(const PublicSubkeyPacket* packet) const override; + void dump(const SignaturePacket* packet) const override; + + JsonDump(std::ostream& out) : DumpPacketSink(out) {} +}; + +} // Namespace NeoPG diff --git a/neopg-tool/cli/packet/dump/legacy_dump.h b/neopg-tool/cli/packet/dump/legacy_dump.h index bea40390b..7e9c0fdb5 100644 --- a/neopg-tool/cli/packet/dump/legacy_dump.h +++ b/neopg-tool/cli/packet/dump/legacy_dump.h @@ -13,14 +13,14 @@ namespace NeoPG { class LegacyDump : public DumpPacketSink { public: /// Dispatcher. - virtual void dump(const Packet* packet) const; + void dump(const Packet* packet) const override; - virtual void dump(const MarkerPacket* packet) const; - virtual void dump(const UserIdPacket* packet) const; - virtual void dump(const UserAttributePacket* packet) const; - virtual void dump(const PublicKeyPacket* packet) const; - virtual void dump(const PublicSubkeyPacket* packet) const; - virtual void dump(const SignaturePacket* packet) const; + void dump(const MarkerPacket* packet) const override; + void dump(const UserIdPacket* packet) const override; + void dump(const UserAttributePacket* packet) const override; + void dump(const PublicKeyPacket* packet) const override; + void dump(const PublicSubkeyPacket* packet) const override; + void dump(const SignaturePacket* packet) const override; LegacyDump(std::ostream& out) : DumpPacketSink(out) {} }; diff --git a/neopg-tool/cli/packet/dump_packet_command.cpp b/neopg-tool/cli/packet/dump_packet_command.cpp index cf84e2431..ea75807f4 100644 --- a/neopg-tool/cli/packet/dump_packet_command.cpp +++ b/neopg-tool/cli/packet/dump_packet_command.cpp @@ -5,61 +5,10 @@ #include +#include +#include #include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include - #include #include #include @@ -77,13 +26,31 @@ #include +namespace NeoPG { +template +std::unique_ptr make_unique(Args&&... args) { + return std::unique_ptr(new T(std::forward(args)...)); +} +} // namespace NeoPG + using namespace NeoPG; -static void process_msg(Botan::DataSource& source, Botan::DataSink& out) { +#include +#include + +static void process_msg(const std::string& format, Botan::DataSource& source, + Botan::DataSink& out) { out.start_msg(); - // DumpPacketSink sink(std::cout); - LegacyDump sink(std::cout); - RawPacketParser parser(sink); + std::unique_ptr sink; + if (format == "legacy") + sink = NeoPG::make_unique(std::cout); + else if (format == "hex") + sink = NeoPG::make_unique(std::cout); + else + sink = NeoPG::make_unique(std::cout); + RawPacketParser parser(*sink); + + // Botan::Pipe parser(new Botan::Decompression_Filter("zlib")); try { parser.process(source); @@ -102,11 +69,11 @@ void DumpPacketCommand::run() { for (auto& file : m_files) { if (file == "-") { Botan::DataSource_Stream in{std::cin}; - process_msg(in, out); + process_msg(m_format, in, out); } else { // Open in binary mode. Botan::DataSource_Stream in{file, true}; - process_msg(in, out); + process_msg(m_format, in, out); } } } diff --git a/neopg-tool/io/hex_filter.cpp b/neopg-tool/io/hex_filter.cpp new file mode 100644 index 000000000..54e6bca56 --- /dev/null +++ b/neopg-tool/io/hex_filter.cpp @@ -0,0 +1,8 @@ +// Botan hex filter (implementation) +// Copyright 2018 The NeoPG developers +// +// NeoPG is released under the Simplified BSD License (see license.txt) + +#include + +using namespace NeoPG; diff --git a/neopg-tool/io/hex_filter.h b/neopg-tool/io/hex_filter.h new file mode 100644 index 000000000..68c149305 --- /dev/null +++ b/neopg-tool/io/hex_filter.h @@ -0,0 +1,18 @@ +// Botan hex filter +// Copyright 2018 The NeoPG developers +// +// NeoPG is released under the Simplified BSD License (see license.txt) + +/// \file +/// This file contains support for hex input format. + +#pragma once + +#include + +namespace NeoPG { + +/// Represent a hex input filter. +class HexFilter : public Botan::Filter {}; + +} // namespace NeoPG diff --git a/neopg/openpgp/object_identifier.h b/neopg/openpgp/object_identifier.h index 25b1d7977..d8a4a5e31 100644 --- a/neopg/openpgp/object_identifier.h +++ b/neopg/openpgp/object_identifier.h @@ -25,7 +25,7 @@ class NEOPG_UNSTABLE_API ObjectIdentifier { /// @return the length in bytes uint16_t length() const noexcept { return m_data.size(); } - /// @return the octed data + /// @return the octet data const std::vector& data() const noexcept { return m_data; } /// Write the mpi to the output stream. diff --git a/neopg/openpgp/packet_header.h b/neopg/openpgp/packet_header.h index 4bf676353..571fe6bd7 100644 --- a/neopg/openpgp/packet_header.h +++ b/neopg/openpgp/packet_header.h @@ -14,6 +14,9 @@ namespace NeoPG { +/// Represent the packet format (old or new). +enum class NEOPG_UNSTABLE_API PacketFormat : uint8_t { Old = 0x00, New = 0x40 }; + /// Represent an OpenPGP [packet /// type](https://tools.ietf.org/html/rfc4880#section-4.3). enum class NEOPG_UNSTABLE_API PacketType : uint8_t { @@ -67,6 +70,8 @@ struct NEOPG_UNSTABLE_API PacketHeader { virtual void write(std::ostream& out) const = 0; virtual PacketType type() const = 0; + virtual PacketFormat format() const noexcept = 0; + virtual uint32_t length() const = 0; // Prevent memory leak when upcasting in smart pointer containers. @@ -79,6 +84,8 @@ class NEOPG_UNSTABLE_API OldPacketHeader : public PacketHeader { PacketLengthType m_length_type; uint32_t m_length; + PacketFormat format() const noexcept override { return PacketFormat::Old; } + static std::unique_ptr create_or_throw(PacketType type, uint32_t length); @@ -134,6 +141,8 @@ class NEOPG_UNSTABLE_API NewPacketHeader : public PacketHeader { NewPacketTag m_tag; NewPacketLength m_length; + PacketFormat format() const noexcept override { return PacketFormat::New; } + static std::unique_ptr create_or_throw(PacketType type, uint32_t length);