From 65f8b4619a1a926ab580d85dd205b1036c6da393 Mon Sep 17 00:00:00 2001 From: Marcus Brinkmann Date: Wed, 16 May 2018 14:26:24 +0200 Subject: [PATCH] Implement more of packet filter command. --- src/CMakeLists.txt | 1 + src/cli/packet_command.cpp | 323 +++++++++++++++++++++++++++++++++++++ 2 files changed, 324 insertions(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8ffd1ec3d..feb101d6a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -36,6 +36,7 @@ target_include_directories(neopg-tool PUBLIC ${BOTAN2_INCLUDE_DIRS} ${CLI11_INCLUDE_DIR} ${RANG_INCLUDE_DIR} + ${JSON_INCLUDE_DIR} ../include ) diff --git a/src/cli/packet_command.cpp b/src/cli/packet_command.cpp index 54dbb54b8..c77dcd003 100644 --- a/src/cli/packet_command.cpp +++ b/src/cli/packet_command.cpp @@ -9,15 +9,23 @@ #include #include +#include #include +#include #include #include #include #include +#include + +#include +#include #include +#include + #include #include @@ -35,6 +43,273 @@ void UserIdPacketCommand::run() { packet.write(std::cout); } +static void output_public_key_data(PublicKeyData* pub) { + PublicKeyMaterial* key = nullptr; + switch (pub->version()) { + case PublicKeyVersion::V2: + case PublicKeyVersion::V3: { + auto v3pub = dynamic_cast(pub); + std::cout << "\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); + std::cout << "\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: + std::cout << "\tversion " << static_cast(pub->version()) << "\n"; + break; + } + if (key) { + switch (key->algorithm()) { + case PublicKeyAlgorithm::Rsa: { + auto rsa = dynamic_cast(key); + std::cout << "\tpkey[0]: [" << rsa->m_n.length() << " bits]\n"; + std::cout << "\tpkey[1]: [" << rsa->m_e.length() << " bits]\n"; + } break; + case PublicKeyAlgorithm::Dsa: { + auto dsa = dynamic_cast(key); + std::cout << "\tpkey[0]: [" << dsa->m_p.length() << " bits]\n"; + std::cout << "\tpkey[1]: [" << dsa->m_q.length() << " bits]\n"; + std::cout << "\tpkey[2]: [" << dsa->m_g.length() << " bits]\n"; + std::cout << "\tpkey[3]: [" << dsa->m_y.length() << " bits]\n"; + } break; + case PublicKeyAlgorithm::Elgamal: { + auto elgamal = dynamic_cast(key); + std::cout << "\tpkey[0]: [" << elgamal->m_p.length() << " bits]\n"; + std::cout << "\tpkey[1]: [" << elgamal->m_g.length() << " bits]\n"; + std::cout << "\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); + std::cout << "\tpkey[0]: [" << (1 + ecdsa->m_curve.length()) * 8 + << " bits] " << Botan::OIDS::lookup(oid) << " (" << oidstr + << ")\n"; + std::cout << "\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); + std::cout << "\tpkey[0]: [" << (1 + eddsa->m_curve.length()) * 8 + << " bits] " << Botan::OIDS::lookup(oid) << " (" << oidstr + << ")\n"; + std::cout << "\tpkey[1]: [" << eddsa->m_key.length() << " bits]\n"; + } break; + default: + break; + } + auto keyid = pub->keyid(); + std::cout << "\tkeyid: " << Botan::hex_encode(keyid.data(), keyid.size()) + << "\n"; + } +} + +static void output_signature_subpacket(const std::string& variant, + SignatureSubpacket* subpacket) { + std::cout << "\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); + std::cout << " (sig created " << sub->m_created << ")"; + break; + } + case SignatureSubpacketType::SignatureExpirationTime: { + auto sub = dynamic_cast(subpacket); + if (sub->m_expiration) + std::cout << " (sig expires after " << sub->m_expiration << ")"; + else + std::cout << " (sig does not expire)"; + break; + } + case SignatureSubpacketType::ExportableCertification: { + auto sub = dynamic_cast(subpacket); + if (sub->m_exportable) + std::cout << " (exportable)"; + else + std::cout << " (not exportable)"; + break; + } + case SignatureSubpacketType::TrustSignature: { + auto sub = dynamic_cast(subpacket); + std::cout << " (trust signature of depth " << (int)sub->m_level + << ", value " << (int)sub->m_amount << ")"; + break; + } + case SignatureSubpacketType::RegularExpression: { + auto sub = dynamic_cast(subpacket); + const tao::json::value str = sub->m_regex; + std::cout << " (regular expression: " << str << ")"; + break; + } + case SignatureSubpacketType::KeyExpirationTime: { + auto sub = dynamic_cast(subpacket); + 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; + std::cout << " (key expires after " << years << "y" << (days % 365) + << "d" << (hours % 24) << "h" << (minutes % 60) << "m)"; + } else + std::cout << " (key does not expire)"; + break; + } + case SignatureSubpacketType::PreferredSymmetricAlgorithms: { + auto sub = + dynamic_cast(subpacket); + std::cout << " (pref-sym-algos:"; + for (auto& algorithm : sub->m_algorithms) + std::cout << " " << (int)algorithm; + std::cout << ")"; + break; + } + case SignatureSubpacketType::Issuer: { + auto sub = dynamic_cast(subpacket); + std::cout << " (issuer key ID " + << Botan::hex_encode(sub->m_issuer.data(), sub->m_issuer.size()) + << ")"; + break; + } + case SignatureSubpacketType::PreferredHashAlgorithms: { + auto sub = dynamic_cast(subpacket); + std::cout << " (pref-hash-algos:"; + for (auto& algorithm : sub->m_algorithms) + std::cout << " " << (int)algorithm; + std::cout << ")"; + break; + } + case SignatureSubpacketType::PreferredCompressionAlgorithms: { + auto sub = + dynamic_cast(subpacket); + std::cout << " (pref-zip-algos:"; + for (auto& algorithm : sub->m_algorithms) + std::cout << " " << (int)algorithm; + std::cout << ")"; + break; + } + case SignatureSubpacketType::KeyServerPreferences: { + auto sub = dynamic_cast(subpacket); + std::cout << " (keyserver preferences: " + << Botan::hex_encode(sub->m_features.data(), + sub->m_features.size()) + << ")"; + break; + } + case SignatureSubpacketType::PrimaryUserId: { + auto sub = dynamic_cast(subpacket); + std::cout << " (primary user ID: " + << (boost::format("%02x") % static_cast(sub->m_primary)) + << ")"; + break; + } + case SignatureSubpacketType::KeyFlags: { + auto sub = dynamic_cast(subpacket); + std::cout << " (key flags: " << Botan::hex_encode(&sub->m_flags, 1) + << ")"; + break; + } + case SignatureSubpacketType::Features: { + auto sub = dynamic_cast(subpacket); + std::cout << " (features: " + << Botan::hex_encode(sub->m_features.data(), + sub->m_features.size()) + << ")"; + break; + } + default: + break; + } + std::cout << "\n"; +} + +static void output_signature_data(SignatureData* sig) { + SignatureMaterial* sigmat = nullptr; + switch (sig->version()) { + case SignatureVersion::V2: + case SignatureVersion::V3: { + auto v3sig = dynamic_cast(sig); + std::cout << "\tversion 3, created " << v3sig->m_created + << ", md5len 5, sigclass 0x" + << (boost::format("%02x") % + static_cast(v3sig->signature_type())) + << "\n"; + std::cout << "\tdigest algo " << static_cast(v3sig->hash_algorithm()) + << ", begin of digest " + << (boost::format("%02x") % + static_cast(v3sig->m_quick.data()[0])) + << " " + << (boost::format("%02x") % + static_cast(v3sig->m_quick.data()[1])) + << "\n"; + sigmat = v3sig->m_signature.get(); + } break; + case SignatureVersion::V4: { + auto v4sig = dynamic_cast(sig); + // FIXME: Try to get created from subpackets. + std::cout << "\tversion 4, created " << v4sig->m_created + << ", md5len 0, sigclass 0x" + << (boost::format("%02x") % + static_cast(v4sig->signature_type())) + << "\n"; + std::cout << "\tdigest algo " << static_cast(v4sig->hash_algorithm()) + << ", begin of digest " + << (boost::format("%02x") % + static_cast(v4sig->m_quick.data()[0])) + << " " + << (boost::format("%02x") % + static_cast(v4sig->m_quick.data()[1])) + << "\n"; + for (auto& subpacket : v4sig->m_hashed_subpackets) { + output_signature_subpacket("hashed subpkt", subpacket.get()); + } + for (auto& subpacket : v4sig->m_unhashed_subpackets) { + output_signature_subpacket("subpkt", subpacket.get()); + } + sigmat = v4sig->m_signature.get(); + } break; + default: + break; + } + if (sigmat) { + switch (sigmat->algorithm()) { + case PublicKeyAlgorithm::Rsa: { + auto rsa = dynamic_cast(sigmat); + std::cout << "\tdata: [" << rsa->m_m_pow_d.length() << " bits]\n"; + } break; + case PublicKeyAlgorithm::Dsa: { + auto dsa = dynamic_cast(sigmat); + std::cout << "\tdata: [" << dsa->m_r.length() << " bits]\n"; + std::cout << "\tdata: [" << dsa->m_s.length() << " bits]\n"; + } break; + case PublicKeyAlgorithm::Ecdsa: { + auto ecdsa = dynamic_cast(sigmat); + std::cout << "\tdata: [" << ecdsa->m_r.length() << " bits]\n"; + std::cout << "\tdata: [" << ecdsa->m_s.length() << " bits]\n"; + } break; + case PublicKeyAlgorithm::Eddsa: { + auto eddsa = dynamic_cast(sigmat); + std::cout << "\tdata: [" << eddsa->m_r.length() << " bits]\n"; + std::cout << "\tdata: [" << eddsa->m_s.length() << " bits]\n"; + } break; + default: + break; + } + } +} + struct LegacyPacketSink : public RawPacketSink { void next_packet(std::unique_ptr header, const char* data, size_t length) { @@ -52,6 +327,54 @@ struct LegacyPacketSink : public RawPacketSink { << " ctb=" << (boost::format("%02x") % (int)(uint8_t)head[0]) << " tag=" << (int)header->type() << " hlen=" << head.length() << " plen=" << length << (new_header ? " new-ctb" : "") << "\n"; + + // FIXME: Catch exception in nested parsing, show useful debug output, + // default to raw. + try { + ParserInput in{data, length}; + auto packet = Packet::create_or_throw(header->type(), in); + packet->m_header = std::move(header); + switch (packet->type()) { + case PacketType::Marker: + std::cout << ":marker packet: PGP\n"; + break; + case PacketType::UserId: { + auto uid = dynamic_cast(packet.get()); + assert(uid); + const tao::json::value str = uid->m_content; + std::cout << ":user ID packet: " << str << "\n"; + } break; + case PacketType::PublicKey: { + auto pubkey = dynamic_cast(packet.get()); + assert(pubkey); + auto pub = dynamic_cast(pubkey->m_public_key.get()); + assert(pub); + std::cout << ":public key packet:\n"; + output_public_key_data(pub); + } break; + case PacketType::PublicSubkey: { + auto pubkey = dynamic_cast(packet.get()); + assert(pubkey); + auto pub = dynamic_cast(pubkey->m_public_key.get()); + assert(pub); + std::cout << ":public sub key packet:\n"; + output_public_key_data(pub); + } break; + case PacketType::Signature: { + auto signature = dynamic_cast(packet.get()); + assert(signature); + auto sig = dynamic_cast(signature->m_signature.get()); + assert(sig); + std::cout << ":signature packet: algo " + << static_cast(sig->public_key_algorithm()) << "\n"; + output_signature_data(sig); + } break; + default: + break; + } + } catch (const std::runtime_error& exc) { + std::cout << "ERROR:" << exc.what() << "\n"; + } } void start_packet(std::unique_ptr header){}; void continue_packet(const char* data, size_t length){};