From 2b3a65cc921c2136f75a336b7c8a38dd40599115 Mon Sep 17 00:00:00 2001 From: Marcus Brinkmann Date: Wed, 16 May 2018 14:05:09 +0200 Subject: [PATCH] Add PublicKeyPacket and PublicSubkeyPacket. --- lib/CMakeLists.txt | 24 ++++ lib/include/neopg/intern/pegtl.h | 42 +++++++ .../public_key/data/v3_public_key_data.cpp | 115 ++++++++++++++++++ .../public_key/data/v3_public_key_data.h | 64 ++++++++++ .../data/v3_public_key_data_tests.cpp | 75 ++++++++++++ .../public_key/data/v4_public_key_data.cpp | 100 +++++++++++++++ .../public_key/data/v4_public_key_data.h | 60 +++++++++ .../data/v4_public_key_data_tests.cpp | 49 ++++++++ .../material/dsa_public_key_material.cpp | 28 +++++ .../material/dsa_public_key_material.h | 49 ++++++++ .../dsa_public_key_material_tests.cpp | 33 +++++ .../material/ecdh_public_key_material.cpp | 51 ++++++++ .../material/ecdh_public_key_material.h | 51 ++++++++ .../ecdh_public_key_material_tests.cpp | 33 +++++ .../material/ecdsa_public_key_material.cpp | 25 ++++ .../material/ecdsa_public_key_material.h | 49 ++++++++ .../ecdsa_public_key_material_tests.cpp | 30 +++++ .../material/eddsa_public_key_material.cpp | 25 ++++ .../material/eddsa_public_key_material.h | 49 ++++++++ .../eddsa_public_key_material_tests.cpp | 30 +++++ .../material/elgamal_public_key_material.cpp | 26 ++++ .../material/elgamal_public_key_material.h | 49 ++++++++ .../elgamal_public_key_material_tests.cpp | 31 +++++ .../material/rsa_public_key_material.cpp | 24 ++++ .../material/rsa_public_key_material.h | 47 +++++++ .../rsa_public_key_material_tests.cpp | 44 +++++++ lib/openpgp/public_key/public_key_data.cpp | 35 ++++++ lib/openpgp/public_key/public_key_data.h | 51 ++++++++ .../public_key/public_key_data_tests.cpp | 17 +++ .../public_key/public_key_material.cpp | 43 +++++++ lib/openpgp/public_key/public_key_material.h | 68 +++++++++++ .../public_key/public_key_material_tests.cpp | 35 ++++++ lib/openpgp/public_key_packet.cpp | 79 ++++++++++++ lib/openpgp/public_key_packet.h | 66 ++++++++++ lib/openpgp/public_key_packet_tests.cpp | 76 ++++++++++++ lib/openpgp/public_subkey_packet.cpp | 81 ++++++++++++ lib/openpgp/public_subkey_packet.h | 70 +++++++++++ lib/openpgp/public_subkey_packet_tests.cpp | 50 ++++++++ lib/tests/CMakeLists.txt | 12 ++ src/cli/packet_command.cpp | 7 ++ 40 files changed, 1893 insertions(+) create mode 100644 lib/openpgp/public_key/data/v3_public_key_data.cpp create mode 100644 lib/openpgp/public_key/data/v3_public_key_data.h create mode 100644 lib/openpgp/public_key/data/v3_public_key_data_tests.cpp create mode 100644 lib/openpgp/public_key/data/v4_public_key_data.cpp create mode 100644 lib/openpgp/public_key/data/v4_public_key_data.h create mode 100644 lib/openpgp/public_key/data/v4_public_key_data_tests.cpp create mode 100644 lib/openpgp/public_key/material/dsa_public_key_material.cpp create mode 100644 lib/openpgp/public_key/material/dsa_public_key_material.h create mode 100644 lib/openpgp/public_key/material/dsa_public_key_material_tests.cpp create mode 100644 lib/openpgp/public_key/material/ecdh_public_key_material.cpp create mode 100644 lib/openpgp/public_key/material/ecdh_public_key_material.h create mode 100644 lib/openpgp/public_key/material/ecdh_public_key_material_tests.cpp create mode 100644 lib/openpgp/public_key/material/ecdsa_public_key_material.cpp create mode 100644 lib/openpgp/public_key/material/ecdsa_public_key_material.h create mode 100644 lib/openpgp/public_key/material/ecdsa_public_key_material_tests.cpp create mode 100644 lib/openpgp/public_key/material/eddsa_public_key_material.cpp create mode 100644 lib/openpgp/public_key/material/eddsa_public_key_material.h create mode 100644 lib/openpgp/public_key/material/eddsa_public_key_material_tests.cpp create mode 100644 lib/openpgp/public_key/material/elgamal_public_key_material.cpp create mode 100644 lib/openpgp/public_key/material/elgamal_public_key_material.h create mode 100644 lib/openpgp/public_key/material/elgamal_public_key_material_tests.cpp create mode 100644 lib/openpgp/public_key/material/rsa_public_key_material.cpp create mode 100644 lib/openpgp/public_key/material/rsa_public_key_material.h create mode 100644 lib/openpgp/public_key/material/rsa_public_key_material_tests.cpp create mode 100644 lib/openpgp/public_key/public_key_data.cpp create mode 100644 lib/openpgp/public_key/public_key_data.h create mode 100644 lib/openpgp/public_key/public_key_data_tests.cpp create mode 100644 lib/openpgp/public_key/public_key_material.cpp create mode 100644 lib/openpgp/public_key/public_key_material.h create mode 100644 lib/openpgp/public_key/public_key_material_tests.cpp create mode 100644 lib/openpgp/public_key_packet.cpp create mode 100644 lib/openpgp/public_key_packet.h create mode 100644 lib/openpgp/public_key_packet_tests.cpp create mode 100644 lib/openpgp/public_subkey_packet.cpp create mode 100644 lib/openpgp/public_subkey_packet.h create mode 100644 lib/openpgp/public_subkey_packet_tests.cpp diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 4eb9dda0d..755ae9776 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -19,6 +19,18 @@ set(NeopgHeaders openpgp/object_identifier.h openpgp/packet.h openpgp/packet_header.h + openpgp/public_key/data/v3_public_key_data.h + openpgp/public_key/data/v4_public_key_data.h + openpgp/public_key/material/dsa_public_key_material.h + openpgp/public_key/material/ecdh_public_key_material.h + openpgp/public_key/material/ecdsa_public_key_material.h + openpgp/public_key/material/eddsa_public_key_material.h + openpgp/public_key/material/elgamal_public_key_material.h + openpgp/public_key/material/rsa_public_key_material.h + openpgp/public_key/public_key_data.h + openpgp/public_key/public_key_material.h + openpgp/public_key_packet.h + openpgp/public_subkey_packet.h openpgp/raw_packet.h openpgp/symmetrically_encrypted_data_packet.h openpgp/symmetrically_encrypted_integrity_protected_data_packet.h @@ -46,6 +58,18 @@ add_library(neopg openpgp/object_identifier.cpp openpgp/packet.cpp openpgp/packet_header.cpp + openpgp/public_key_packet.cpp + openpgp/public_key/data/v3_public_key_data.cpp + openpgp/public_key/data/v4_public_key_data.cpp + openpgp/public_key/material/dsa_public_key_material.cpp + openpgp/public_key/material/ecdh_public_key_material.cpp + openpgp/public_key/material/ecdsa_public_key_material.cpp + openpgp/public_key/material/eddsa_public_key_material.cpp + openpgp/public_key/material/elgamal_public_key_material.cpp + openpgp/public_key/material/rsa_public_key_material.cpp + openpgp/public_key/public_key_data.cpp + openpgp/public_key/public_key_material.cpp + openpgp/public_subkey_packet.cpp openpgp/raw_packet.cpp openpgp/symmetrically_encrypted_data_packet.cpp openpgp/symmetrically_encrypted_integrity_protected_data_packet.cpp diff --git a/lib/include/neopg/intern/pegtl.h b/lib/include/neopg/intern/pegtl.h index 4782eeecb..15aac7268 100644 --- a/lib/include/neopg/intern/pegtl.h +++ b/lib/include/neopg/intern/pegtl.h @@ -9,6 +9,10 @@ #include #include +#include + +#include + #include #include @@ -91,5 +95,43 @@ struct bind { } }; +template +struct bind { + template + static void apply(const Input& in, T& pkt) { + auto src = in.begin(); + auto ptr = reinterpret_cast(src); + static_assert(sizeof(*src) == sizeof(*ptr), "can't do pointer arithmetic"); + pkt.*Field = Botan::load_be(ptr, 0); + } +}; + +template +struct bind { + template + static void apply(const Input& in, T& pkt) { + auto src = in.begin(); + auto ptr = reinterpret_cast(src); + static_assert(sizeof(*src) == sizeof(*ptr), "can't do pointer arithmetic"); + pkt.*Field = Botan::load_be(ptr, 0); + } +}; + +template +struct bind { + template + static void apply(const Input& in, T& pkt) { + pkt.*Field = static_cast(in.peek_byte()); + } +}; + +template +struct bind { + template + static void apply(const Input& in, T& pkt) { + pkt.*Field = static_cast(in.peek_byte()); + } +}; + } // namespace TAO_PEGTL_NAMESPACE } // namespace tao diff --git a/lib/openpgp/public_key/data/v3_public_key_data.cpp b/lib/openpgp/public_key/data/v3_public_key_data.cpp new file mode 100644 index 000000000..2d38852f3 --- /dev/null +++ b/lib/openpgp/public_key/data/v3_public_key_data.cpp @@ -0,0 +1,115 @@ +// OpenPGP public key packet data v3 (implementation) +// Copyright 2018 The NeoPG developers +// +// NeoPG is released under the Simplified BSD License (see license.txt) + +#include + +#include + +#include +#include + +#include + +using namespace NeoPG; + +namespace NeoPG { +namespace v3_public_key_data { + +using namespace pegtl; + +// Grammar +struct created : uint32_be::any {}; +struct days_valid : uint16_be::any {}; +struct algorithm + : uint8::one(PublicKeyAlgorithm::Rsa), + static_cast(PublicKeyAlgorithm::RsaEncrypt), + static_cast(PublicKeyAlgorithm::RsaSign)> {}; +struct grammar : must {}; + +// Action +template +struct action : nothing {}; + +template <> +struct action + : bind {}; + +template <> +struct action + : bind {}; + +template <> +struct action + : bind { +}; + +// Control +template +struct control : pegtl::normal { + static const std::string error_message; + + template + static void raise(const Input& in, States&&...) { + throw parser_error(error_message, in); + } +}; + +template <> +const std::string control::error_message = + "v3 public key data is missing created time"; + +template <> +const std::string control::error_message = + "v3 public key data is missing days invalid"; + +template <> +const std::string control::error_message = + "v3 public key data has invalid algorithm specifier"; +} // namespace v3_public_key_data + +std::unique_ptr V3PublicKeyData::create_or_throw( + ParserInput& in) { + auto packet = make_unique(); + + pegtl::parse(in.m_impl->m_input, *packet.get()); + packet->m_key = PublicKeyMaterial::create_or_throw(packet->m_algorithm, in); + + return packet; +} + +void V3PublicKeyData::write(std::ostream& out) const { + out << static_cast(m_created >> 24) + << static_cast(m_created >> 16) + << static_cast(m_created >> 8) + << static_cast(m_created); + out << static_cast(m_days_valid >> 8) + << static_cast(m_days_valid); + out << static_cast(m_algorithm); + if (m_key) m_key->write(out); +} + +std::vector V3PublicKeyData::fingerprint() const { + Botan::MD5 md5; + auto rsa = dynamic_cast(m_key.get()); + if (rsa) { + md5.update(rsa->m_n.bits()); + md5.update(rsa->m_e.bits()); + } + return md5.final_stdvec(); +} + +std::vector V3PublicKeyData::keyid() const { + std::vector keyid(KEYID_LENGTH, static_cast(0x00)); + auto rsa = dynamic_cast(m_key.get()); + if (rsa) { + const std::vector& n = rsa->m_n.bits(); + size_t len = std::min(keyid.size(), n.size()); + std::copy_backward(n.end() - len, n.end(), keyid.end()); + } + return keyid; +} + +} // namespace NeoPG diff --git a/lib/openpgp/public_key/data/v3_public_key_data.h b/lib/openpgp/public_key/data/v3_public_key_data.h new file mode 100644 index 000000000..3e738438d --- /dev/null +++ b/lib/openpgp/public_key/data/v3_public_key_data.h @@ -0,0 +1,64 @@ +// OpenPGP public key packet data v3 +// Copyright 2018 The NeoPG developers +// +// NeoPG is released under the Simplified BSD License (see license.txt) + +/// \file +/// This file contains the version 3 specific part of public key packets. + +#pragma once + +#include + +namespace NeoPG { + +class NEOPG_UNSTABLE_API V3PublicKeyData : public PublicKeyData { + public: + /// Create new public key data from \p input. Throw an exception on error. + /// + /// \param input the parser input to read from + /// + /// \return pointer to packet + /// + /// \throws ParserError + static std::unique_ptr create_or_throw(ParserInput& input); + + /// The created timestamp. + uint32_t m_created{0}; + + /// The number of days until expiration. + uint16_t m_days_valid{0}; + + /// The algorithm identifier. + PublicKeyAlgorithm m_algorithm{PublicKeyAlgorithm::Rsa}; + + /// The key material. + std::unique_ptr m_key; + + /// Write the packet body to the output stream. + /// + /// \param out the output stream to write to + void write(std::ostream& out) const override; + + /// Return the public key version. + /// + /// \return the value PublicKeyVersion::V3. + PublicKeyVersion version() const noexcept override { + return PublicKeyVersion::V3; + } + + /// Return the public key fingerprint. + std::vector fingerprint() const override; + + /// The length of the keyid (8). + static constexpr size_t KEYID_LENGTH{8}; + + /// Return the public key id. Can return a truncated string if the RSA + /// parameter n is too short for a complete key id. + std::vector keyid() const override; + + /// Construct new v3 public key packet data. + V3PublicKeyData() = default; +}; + +} // namespace NeoPG diff --git a/lib/openpgp/public_key/data/v3_public_key_data_tests.cpp b/lib/openpgp/public_key/data/v3_public_key_data_tests.cpp new file mode 100644 index 000000000..a0336a10e --- /dev/null +++ b/lib/openpgp/public_key/data/v3_public_key_data_tests.cpp @@ -0,0 +1,75 @@ +// OpenPGP v3 public key packet data (tests) +// Copyright 2018 The NeoPG developers +// +// NeoPG is released under the Simplified BSD License (see license.txt) + +#include + +#include + +#include + +#include + +#include +#include + +using namespace NeoPG; + +TEST(OpenpgpV3PublicKeyData, Create) { + // Test V3 packets. + const std::string raw{ + "\x12\x34\x56\x78" + "\xab\xcd" + "\x01" + "\x00\x11\x01\x42\x23" + "\x00\x02\x03", + 15}; + auto fpr = + std::vector{0xb5, 0xb5, 0xbe, 0xc2, 0x3d, 0x70, 0xea, 0x0e, + 0x05, 0x68, 0x45, 0x64, 0xac, 0xa7, 0x3d, 0xc7}; + auto keyid = + std::vector{0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x42, 0x23}; + ParserInput in(raw.data(), raw.length()); + auto key = PublicKeyData::create_or_throw(PublicKeyVersion::V3, in); + ASSERT_EQ(key->version(), PublicKeyVersion::V3); + auto v3key = dynamic_cast(key.get()); + ASSERT_NE(v3key, nullptr); + ASSERT_EQ(v3key->m_created, 0x12345678); + ASSERT_EQ(v3key->m_days_valid, 0xabcd); + ASSERT_EQ(v3key->m_algorithm, PublicKeyAlgorithm::Rsa); + ASSERT_NE(v3key->m_key, nullptr); + ASSERT_EQ(v3key->m_key->algorithm(), PublicKeyAlgorithm::Rsa); + ASSERT_EQ(v3key->fingerprint(), fpr); + ASSERT_EQ(v3key->keyid(), keyid); + auto rsa = dynamic_cast(v3key->m_key.get()); + ASSERT_EQ(rsa->m_n, MultiprecisionInteger(0x14223)); + ASSERT_EQ(rsa->m_e, MultiprecisionInteger(0x3)); + + std::stringstream out; + v3key->write(out); + ASSERT_EQ(out.str(), raw); +} + +TEST(OpenpgpV3PublicKeyData, KeyId) { + V3PublicKeyData v3key; + v3key.m_key = make_unique(); + auto rsa = dynamic_cast(v3key.m_key.get()); + rsa->m_n.m_bits = std::vector{0xff, 0x08, 0x07, 0x06, 0x05, + 0x04, 0x03, 0x02, 0x01, 0x00}; + rsa->m_n.m_length = rsa->m_n.m_bits.size() * 8; + auto keyid = + std::vector{0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00}; + ASSERT_EQ(v3key.keyid(), keyid); +} + +TEST(OpenpgpV3PublicKeyData, ShortKeyId) { + V3PublicKeyData v3key; + v3key.m_key = make_unique(); + auto rsa = dynamic_cast(v3key.m_key.get()); + rsa->m_n.m_bits = std::vector{0xff, 0x01, 0x00}; + rsa->m_n.m_length = rsa->m_n.m_bits.size() * 8; + auto keyid = + std::vector{0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x00}; + ASSERT_EQ(v3key.keyid(), keyid); +} diff --git a/lib/openpgp/public_key/data/v4_public_key_data.cpp b/lib/openpgp/public_key/data/v4_public_key_data.cpp new file mode 100644 index 000000000..a6ba26f1e --- /dev/null +++ b/lib/openpgp/public_key/data/v4_public_key_data.cpp @@ -0,0 +1,100 @@ +// OpenPGP v4 public key packet data (implementation) +// Copyright 2018 The NeoPG developers +// +// NeoPG is released under the Simplified BSD License (see license.txt) + +#include + +#include +#include + +#include + +using namespace NeoPG; + +namespace NeoPG { +namespace v4_public_key_data { + +using namespace pegtl; + +// Grammar +struct created : bytes<4> {}; +struct algorithm : any {}; +struct grammar : must {}; + +// Action +template +struct action : nothing {}; + +template <> +struct action + : bind {}; + +template <> +struct action + : bind { +}; + +// Control +template +struct control : pegtl::normal { + static const std::string error_message; + + template + static void raise(const Input& in, States&&...) { + throw parser_error(error_message, in); + } +}; + +template <> +const std::string control::error_message = + "v4 public key data is missing created time"; + +template <> +const std::string control::error_message = + "v4 public key data is missing algorithm specifier"; + +} // namespace v4_public_key_data +} // namespace NeoPG + +std::unique_ptr V4PublicKeyData::create_or_throw( + ParserInput& in) { + auto packet = make_unique(); + + pegtl::parse(in.m_impl->m_input, *packet.get()); + packet->m_key = PublicKeyMaterial::create_or_throw(packet->m_algorithm, in); + // We accept all algorithms that are known to PublicKeyMaterial. + + return packet; +} + +void V4PublicKeyData::write(std::ostream& out) const { + out << static_cast(m_created >> 24) + << static_cast(m_created >> 16) + << static_cast(m_created >> 8) + << static_cast(m_created); + out << static_cast(m_algorithm); + if (m_key) m_key->write(out); +} + +std::vector V4PublicKeyData::fingerprint() const { + std::stringstream out; + out << static_cast(version()); + write(out); + auto public_key = out.str(); + + Botan::SHA_160 sha1; + sha1.update(0x99); + // The length may be truncated. + auto length = static_cast(public_key.size()); + sha1.update_be(length); + sha1.update(public_key); + + return sha1.final_stdvec(); +} + +std::vector V4PublicKeyData::keyid() const { + auto fpr = fingerprint(); + return std::vector(fpr.begin() + 12, fpr.end()); +} diff --git a/lib/openpgp/public_key/data/v4_public_key_data.h b/lib/openpgp/public_key/data/v4_public_key_data.h new file mode 100644 index 000000000..31dbbb29c --- /dev/null +++ b/lib/openpgp/public_key/data/v4_public_key_data.h @@ -0,0 +1,60 @@ +// OpenPGP v4 public key packet data +// Copyright 2018 The NeoPG developers +// +// NeoPG is released under the Simplified BSD License (see license.txt) + +/// \file +/// This file contains the version specific part of public key packets. + +#pragma once + +#include +#include + +#include + +namespace NeoPG { + +class NEOPG_UNSTABLE_API V4PublicKeyData : public PublicKeyData { + public: + /// Create new public key data from \p input. Throw an exception on error. + /// + /// \param input the parser input to read from + /// + /// \return pointer to packet + /// + /// \throws ParserError + static std::unique_ptr create_or_throw(ParserInput& input); + + /// The created timestamp. + uint32_t m_created{0}; + + /// The algorithm identifier. + PublicKeyAlgorithm m_algorithm{PublicKeyAlgorithm::Rsa}; + + /// The key material. + std::unique_ptr m_key; + + /// Write the packet body to the output stream. + /// + /// \param out the output stream to write to + void write(std::ostream& out) const override; + + /// Return the public key version. + /// + /// \return the value PublicKeyVersion::V4. + PublicKeyVersion version() const noexcept override { + return PublicKeyVersion::V4; + } + + /// Return the public key fingerprint. + std::vector fingerprint() const override; + + /// Return the public key id. + std::vector keyid() const override; + + /// Construct new v4 public key packet data. + V4PublicKeyData() = default; +}; + +} // namespace NeoPG diff --git a/lib/openpgp/public_key/data/v4_public_key_data_tests.cpp b/lib/openpgp/public_key/data/v4_public_key_data_tests.cpp new file mode 100644 index 000000000..bbe2dd810 --- /dev/null +++ b/lib/openpgp/public_key/data/v4_public_key_data_tests.cpp @@ -0,0 +1,49 @@ +// OpenPGP v4 public key packet data (tests) +// Copyright 2018 The NeoPG developers +// +// NeoPG is released under the Simplified BSD License (see license.txt) + +#include + +#include + +#include + +#include + +#include +#include + +using namespace NeoPG; + +TEST(OpenpgpV4PublicKeyData, Create) { + // Test V4 packets. + const std::string raw{ + "\x12\x34\x56\x78" + "\x01" + "\x00\x11\x01\x42\x23" + "\x00\x02\x03", + 13}; + auto fpr = std::vector{0x69, 0x33, 0xee, 0xde, 0x37, 0x4c, 0x96, + 0xc5, 0x4d, 0xf9, 0x2d, 0x76, 0x5f, 0x46, + 0xd7, 0x00, 0xcb, 0x74, 0x27, 0xbf}; + auto keyid = std::vector{fpr.end() - 8, fpr.end()}; + ParserInput in(raw.data(), raw.length()); + auto key = PublicKeyData::create_or_throw(PublicKeyVersion::V4, in); + ASSERT_EQ(key->version(), PublicKeyVersion::V4); + auto v4key = dynamic_cast(key.get()); + ASSERT_NE(v4key, nullptr); + ASSERT_EQ(v4key->m_created, 0x12345678); + ASSERT_EQ(v4key->m_algorithm, PublicKeyAlgorithm::Rsa); + ASSERT_NE(v4key->m_key, nullptr); + ASSERT_EQ(v4key->m_key->algorithm(), PublicKeyAlgorithm::Rsa); + ASSERT_EQ(v4key->fingerprint(), fpr); + ASSERT_EQ(v4key->keyid(), keyid); + auto rsa = dynamic_cast(v4key->m_key.get()); + ASSERT_EQ(rsa->m_n, MultiprecisionInteger(0x14223)); + ASSERT_EQ(rsa->m_e, MultiprecisionInteger(0x3)); + + std::stringstream out; + v4key->write(out); + ASSERT_EQ(out.str(), raw); +} diff --git a/lib/openpgp/public_key/material/dsa_public_key_material.cpp b/lib/openpgp/public_key/material/dsa_public_key_material.cpp new file mode 100644 index 000000000..d702f3d92 --- /dev/null +++ b/lib/openpgp/public_key/material/dsa_public_key_material.cpp @@ -0,0 +1,28 @@ +// OpenPGP DSA public key material (implementation) +// Copyright 2018 The NeoPG developers +// +// NeoPG is released under the Simplified BSD License (see license.txt) + +#include + +#include +#include + +using namespace NeoPG; + +std::unique_ptr DsaPublicKeyMaterial::create_or_throw( + ParserInput& in) { + auto data = make_unique(); + data->m_p.parse(in); + data->m_q.parse(in); + data->m_g.parse(in); + data->m_y.parse(in); + return data; +} + +void DsaPublicKeyMaterial::write(std::ostream& out) const { + m_p.write(out); + m_q.write(out); + m_g.write(out); + m_y.write(out); +} diff --git a/lib/openpgp/public_key/material/dsa_public_key_material.h b/lib/openpgp/public_key/material/dsa_public_key_material.h new file mode 100644 index 000000000..f8dc5a15b --- /dev/null +++ b/lib/openpgp/public_key/material/dsa_public_key_material.h @@ -0,0 +1,49 @@ +// OpenPGP DSA public key material +// Copyright 2018 The NeoPG developers +// +// NeoPG is released under the Simplified BSD License (see license.txt) + +/// \file +/// This file contains the DSA specific key material for public key packets. + +#pragma once + +#include + +#include +#include + +#include + +namespace NeoPG { + +/// Key material for DSA public keys. +class NEOPG_UNSTABLE_API DsaPublicKeyMaterial : public PublicKeyMaterial { + public: + MultiprecisionInteger m_p; + MultiprecisionInteger m_q; + MultiprecisionInteger m_g; + MultiprecisionInteger m_y; // g**x mod p (x secret) + + /// Create a new instance from \p input. + /// + /// \param input parser input with key material + /// + /// Throws parse_error if input can not be parsed. + static std::unique_ptr create_or_throw( + ParserInput& input); + + /// Return the public key algorithm. + /// + /// \return the value PublicKeyAlgorithm::Dsa + PublicKeyAlgorithm algorithm() const override { + return PublicKeyAlgorithm::Dsa; + }; + + /// Write the key material to the output stream. + /// + /// \param out the output stream to write to + void write(std::ostream& out) const override; +}; + +} // namespace NeoPG diff --git a/lib/openpgp/public_key/material/dsa_public_key_material_tests.cpp b/lib/openpgp/public_key/material/dsa_public_key_material_tests.cpp new file mode 100644 index 000000000..5266d251a --- /dev/null +++ b/lib/openpgp/public_key/material/dsa_public_key_material_tests.cpp @@ -0,0 +1,33 @@ +// OpenPGP DSA public key material (tests) +// Copyright 2018 The NeoPG developers +// +// NeoPG is released under the Simplified BSD License (see license.txt) + +#include + +#include + +#include +#include + +using namespace NeoPG; + +TEST(OpenpgpDsaPublicKeyMaterial, Create) { + const auto raw = + std::string{"\x00\x09\x01\x62\x00\x02\x03\x00\x02\x02\x00\x01\x01", 13}; + + ParserInput in(raw.data(), raw.length()); + auto dsa_ptr = DsaPublicKeyMaterial::create_or_throw(in); + ASSERT_EQ(in.size(), 0); + + DsaPublicKeyMaterial& dsa{*dsa_ptr}; + ASSERT_EQ(dsa.algorithm(), PublicKeyAlgorithm::Dsa); + ASSERT_EQ(dsa.m_p, MultiprecisionInteger(0x162)); + ASSERT_EQ(dsa.m_q, MultiprecisionInteger(3)); + ASSERT_EQ(dsa.m_g, MultiprecisionInteger(2)); + ASSERT_EQ(dsa.m_y, MultiprecisionInteger(1)); + + std::stringstream out; + dsa.write(out); + ASSERT_EQ(out.str(), raw); +} diff --git a/lib/openpgp/public_key/material/ecdh_public_key_material.cpp b/lib/openpgp/public_key/material/ecdh_public_key_material.cpp new file mode 100644 index 000000000..35a44b682 --- /dev/null +++ b/lib/openpgp/public_key/material/ecdh_public_key_material.cpp @@ -0,0 +1,51 @@ +// OpenPGP ECDH public key material (implementation) +// Copyright 2018 The NeoPG developers +// +// NeoPG is released under the Simplified BSD License (see license.txt) + +#include + +#include +#include + +using namespace NeoPG; + +namespace NeoPG { +namespace public_key_material { +using namespace pegtl; + +struct ecdh_kdf : must, one<(char)0x01>, any, any> {}; + +template +struct action : nothing {}; + +template <> +struct action { + template + static void apply(const Input& in, uint8_t& m_hash, uint8_t& m_sym) { + m_hash = in.peek_byte(2); + m_sym = in.peek_byte(3); + } +}; // namespace public_key_material + +} // namespace public_key_material +} // namespace NeoPG + +using namespace NeoPG; + +std::unique_ptr EcdhPublicKeyMaterial::create_or_throw( + ParserInput& in) { + auto data = make_unique(); + data->m_curve.parse(in); + data->m_key.parse(in); + pegtl::parse( + in.m_impl->m_input, data->m_hash, data->m_sym); + return data; +} + +void EcdhPublicKeyMaterial::write(std::ostream& out) const { + m_curve.write(out); + m_key.write(out); + out << static_cast(0x03) << static_cast(0x01) + << static_cast(m_hash) << static_cast(m_sym); +} diff --git a/lib/openpgp/public_key/material/ecdh_public_key_material.h b/lib/openpgp/public_key/material/ecdh_public_key_material.h new file mode 100644 index 000000000..240e610a6 --- /dev/null +++ b/lib/openpgp/public_key/material/ecdh_public_key_material.h @@ -0,0 +1,51 @@ +// OpenPGP ECDH public key material +// Copyright 2018 The NeoPG developers +// +// NeoPG is released under the Simplified BSD License (see license.txt) + +/// \file +/// This file contains the ECDH specific key material for public key packets. + +#pragma once + +#include + +#include +#include +#include + +#include + +namespace NeoPG { + +/// Key material for [Ecdh public +/// keys](https://tools.ietf.org/html/rfc6637#section-9). +class NEOPG_UNSTABLE_API EcdhPublicKeyMaterial : public PublicKeyMaterial { + public: + ObjectIdentifier m_curve; + MultiprecisionInteger m_key; + uint8_t m_hash; + uint8_t m_sym; + + /// Create a new instance from \p input. + /// + /// \param input parser input with key material + /// + /// Throws parse_error if input can not be parsed. + static std::unique_ptr create_or_throw( + ParserInput& input); + + /// Return the public key algorithm. + /// + /// \return the value PublicKeyAlgorithm::Ecdh + PublicKeyAlgorithm algorithm() const override { + return PublicKeyAlgorithm::Ecdh; + }; + + /// Write the key material to the output stream. + /// + /// \param out the output stream to write to + void write(std::ostream& out) const override; +}; + +} // namespace NeoPG diff --git a/lib/openpgp/public_key/material/ecdh_public_key_material_tests.cpp b/lib/openpgp/public_key/material/ecdh_public_key_material_tests.cpp new file mode 100644 index 000000000..b903e370f --- /dev/null +++ b/lib/openpgp/public_key/material/ecdh_public_key_material_tests.cpp @@ -0,0 +1,33 @@ +// OpenPGP ECDH public key material (tests) +// Copyright 2018 The NeoPG developers +// +// NeoPG is released under the Simplified BSD License (see license.txt) + +#include + +#include + +#include +#include + +using namespace NeoPG; + +TEST(OpenpgpEcdhPublicKeyMaterial, Create) { + const auto raw = + std::string{"\x05\x2b\x81\x04\x00\x23\x00\x02\x03\x03\x01\x01\x02", 13}; + + ParserInput in(raw.data(), raw.length()); + auto ecdh_ptr = EcdhPublicKeyMaterial::create_or_throw(in); + ASSERT_EQ(in.size(), 0); + + EcdhPublicKeyMaterial& ecdh{*ecdh_ptr}; + ASSERT_EQ(ecdh.algorithm(), PublicKeyAlgorithm::Ecdh); + ASSERT_EQ(ecdh.m_curve.as_string(), "1.3.132.0.35"); + ASSERT_EQ(ecdh.m_key, MultiprecisionInteger(0x03)); + ASSERT_EQ(ecdh.m_hash, 0x01); + ASSERT_EQ(ecdh.m_sym, 0x02); + + std::stringstream out; + ecdh.write(out); + ASSERT_EQ(out.str(), raw); +} diff --git a/lib/openpgp/public_key/material/ecdsa_public_key_material.cpp b/lib/openpgp/public_key/material/ecdsa_public_key_material.cpp new file mode 100644 index 000000000..fe9df7d98 --- /dev/null +++ b/lib/openpgp/public_key/material/ecdsa_public_key_material.cpp @@ -0,0 +1,25 @@ +// OpenPGP ECDSA public key material (implementation) +// Copyright 2018 The NeoPG developers +// +// NeoPG is released under the Simplified BSD License (see license.txt) + +#include + +#include +#include + +using namespace NeoPG; + +std::unique_ptr EcdsaPublicKeyMaterial::create_or_throw( + ParserInput& in) { + auto data = make_unique(); + data->m_curve.parse(in); + data->m_key.parse(in); + + return data; +} + +void EcdsaPublicKeyMaterial::write(std::ostream& out) const { + m_curve.write(out); + m_key.write(out); +} diff --git a/lib/openpgp/public_key/material/ecdsa_public_key_material.h b/lib/openpgp/public_key/material/ecdsa_public_key_material.h new file mode 100644 index 000000000..acde36d58 --- /dev/null +++ b/lib/openpgp/public_key/material/ecdsa_public_key_material.h @@ -0,0 +1,49 @@ +// OpenPGP ECDSA public key material +// Copyright 2018 The NeoPG developers +// +// NeoPG is released under the Simplified BSD License (see license.txt) + +/// \file +/// This file contains the Ecdsa specific key material for public key packets. + +#pragma once + +#include + +#include +#include +#include + +#include + +namespace NeoPG { + +/// Key material for [Ecdsa public +/// keys](https://tools.ietf.org/html/rfc6637#section-9). +class NEOPG_UNSTABLE_API EcdsaPublicKeyMaterial : public PublicKeyMaterial { + public: + ObjectIdentifier m_curve; + MultiprecisionInteger m_key; + + /// Create a new instance from \p input. + /// + /// \param input parser input with key material + /// + /// Throws parse_error if input can not be parsed. + static std::unique_ptr create_or_throw( + ParserInput& input); + + /// Return the public key algorithm. + /// + /// \return the value PublicKeyAlgorithm::Ecdh + PublicKeyAlgorithm algorithm() const override { + return PublicKeyAlgorithm::Ecdsa; + }; + + /// Write the key material to the output stream. + /// + /// \param out the output stream to write to + void write(std::ostream& out) const override; +}; + +} // namespace NeoPG diff --git a/lib/openpgp/public_key/material/ecdsa_public_key_material_tests.cpp b/lib/openpgp/public_key/material/ecdsa_public_key_material_tests.cpp new file mode 100644 index 000000000..1077e27c3 --- /dev/null +++ b/lib/openpgp/public_key/material/ecdsa_public_key_material_tests.cpp @@ -0,0 +1,30 @@ +// OpenPGP ECDSA public key material (tests) +// Copyright 2018 The NeoPG developers +// +// NeoPG is released under the Simplified BSD License (see license.txt) + +#include + +#include + +#include +#include + +using namespace NeoPG; + +TEST(OpenpgpEcdsaPublicKeyMaterial, Create) { + const auto raw = std::string{"\x05\x2b\x81\x04\x00\x23\x00\x02\x03", 9}; + + ParserInput in(raw.data(), raw.length()); + auto ecdsa_ptr = EcdsaPublicKeyMaterial::create_or_throw(in); + ASSERT_EQ(in.size(), 0); + + EcdsaPublicKeyMaterial& ecdsa{*ecdsa_ptr}; + ASSERT_EQ(ecdsa.algorithm(), PublicKeyAlgorithm::Ecdsa); + ASSERT_EQ(ecdsa.m_curve.as_string(), "1.3.132.0.35"); + ASSERT_EQ(ecdsa.m_key, MultiprecisionInteger(0x3)); + + std::stringstream out; + ecdsa.write(out); + ASSERT_EQ(out.str(), raw); +} diff --git a/lib/openpgp/public_key/material/eddsa_public_key_material.cpp b/lib/openpgp/public_key/material/eddsa_public_key_material.cpp new file mode 100644 index 000000000..d44a89aa3 --- /dev/null +++ b/lib/openpgp/public_key/material/eddsa_public_key_material.cpp @@ -0,0 +1,25 @@ +// OpenPGP EDDSA public key material (implementation) +// Copyright 2018 The NeoPG developers +// +// NeoPG is released under the Simplified BSD License (see license.txt) + +#include + +#include +#include + +using namespace NeoPG; + +std::unique_ptr EddsaPublicKeyMaterial::create_or_throw( + ParserInput& in) { + auto data = make_unique(); + data->m_curve.parse(in); + data->m_key.parse(in); + + return data; +} + +void EddsaPublicKeyMaterial::write(std::ostream& out) const { + m_curve.write(out); + m_key.write(out); +} diff --git a/lib/openpgp/public_key/material/eddsa_public_key_material.h b/lib/openpgp/public_key/material/eddsa_public_key_material.h new file mode 100644 index 000000000..033091239 --- /dev/null +++ b/lib/openpgp/public_key/material/eddsa_public_key_material.h @@ -0,0 +1,49 @@ +// OpenPGP Eddsa public key material +// Copyright 2018 The NeoPG developers +// +// NeoPG is released under the Simplified BSD License (see license.txt) + +/// \file +/// This file contains the Eddsa specific key material for public key packets. + +#pragma once + +#include + +#include +#include +#include + +#include + +namespace NeoPG { + +/// Key material for [Eddsa public +/// keys](https://tools.ietf.org/html/rfc6637#section-9). +class NEOPG_UNSTABLE_API EddsaPublicKeyMaterial : public PublicKeyMaterial { + public: + ObjectIdentifier m_curve; + MultiprecisionInteger m_key; + + /// Create a new instance from \p input. + /// + /// \param input parser input with key material + /// + /// Throws parse_error if input can not be parsed. + static std::unique_ptr create_or_throw( + ParserInput& input); + + /// Return the public key algorithm. + /// + /// \return the value PublicKeyAlgorithm::Ecdh + PublicKeyAlgorithm algorithm() const override { + return PublicKeyAlgorithm::Eddsa; + }; + + /// Write the key material to the output stream. + /// + /// \param out the output stream to write to + void write(std::ostream& out) const override; +}; + +} // namespace NeoPG diff --git a/lib/openpgp/public_key/material/eddsa_public_key_material_tests.cpp b/lib/openpgp/public_key/material/eddsa_public_key_material_tests.cpp new file mode 100644 index 000000000..a85ca1ae5 --- /dev/null +++ b/lib/openpgp/public_key/material/eddsa_public_key_material_tests.cpp @@ -0,0 +1,30 @@ +// OpenPGP EDDSA public key material (tests) +// Copyright 2018 The NeoPG developers +// +// NeoPG is released under the Simplified BSD License (see license.txt) + +#include + +#include + +#include +#include + +using namespace NeoPG; + +TEST(OpenpgpEddsaPublicKeyMaterial, Create) { + const auto raw = std::string{"\x05\x2b\x81\x04\x00\x23\x00\x02\x03", 9}; + + ParserInput in(raw.data(), raw.length()); + auto eddsa_ptr = EddsaPublicKeyMaterial::create_or_throw(in); + ASSERT_EQ(in.size(), 0); + + EddsaPublicKeyMaterial& eddsa{*eddsa_ptr}; + ASSERT_EQ(eddsa.algorithm(), PublicKeyAlgorithm::Eddsa); + ASSERT_EQ(eddsa.m_curve.as_string(), "1.3.132.0.35"); + ASSERT_EQ(eddsa.m_key, MultiprecisionInteger(0x03)); + + std::stringstream out; + eddsa.write(out); + ASSERT_EQ(out.str(), raw); +} diff --git a/lib/openpgp/public_key/material/elgamal_public_key_material.cpp b/lib/openpgp/public_key/material/elgamal_public_key_material.cpp new file mode 100644 index 000000000..34a9cfdfa --- /dev/null +++ b/lib/openpgp/public_key/material/elgamal_public_key_material.cpp @@ -0,0 +1,26 @@ +// OpenPGP Elgamal public key material (implementation) +// Copyright 2018 The NeoPG developers +// +// NeoPG is released under the Simplified BSD License (see license.txt) + +#include + +#include +#include + +using namespace NeoPG; + +std::unique_ptr +ElgamalPublicKeyMaterial::create_or_throw(ParserInput& in) { + auto data = make_unique(); + data->m_p.parse(in); + data->m_g.parse(in); + data->m_y.parse(in); + return data; +} + +void ElgamalPublicKeyMaterial::write(std::ostream& out) const { + m_p.write(out); + m_g.write(out); + m_y.write(out); +} diff --git a/lib/openpgp/public_key/material/elgamal_public_key_material.h b/lib/openpgp/public_key/material/elgamal_public_key_material.h new file mode 100644 index 000000000..389dd58e7 --- /dev/null +++ b/lib/openpgp/public_key/material/elgamal_public_key_material.h @@ -0,0 +1,49 @@ +// OpenPGP ElGamal public key material +// Copyright 2018 The NeoPG developers +// +// NeoPG is released under the Simplified BSD License (see license.txt) + +/// \file +/// This file contains the ElGamal specific key material for public key packets. + +#pragma once + +#include + +#include +#include +#include + +#include + +namespace NeoPG { + +/// Key material for Elgamal public keys. +class NEOPG_UNSTABLE_API ElgamalPublicKeyMaterial : public PublicKeyMaterial { + public: + MultiprecisionInteger m_p; + MultiprecisionInteger m_g; + MultiprecisionInteger m_y; // g**x mod p (x secret) + + /// Create a new instance from \p input. + /// + /// \param input parser input with key material + /// + /// Throws parse_error if input can not be parsed. + static std::unique_ptr create_or_throw( + ParserInput& input); + + /// Return the public key algorithm. + /// + /// \return the value PublicKeyAlgorithm::Elgamal + PublicKeyAlgorithm algorithm() const override { + return PublicKeyAlgorithm::Elgamal; + }; + + /// Write the key material to the output stream. + /// + /// \param out the output stream to write to + void write(std::ostream& out) const override; +}; + +} // namespace NeoPG diff --git a/lib/openpgp/public_key/material/elgamal_public_key_material_tests.cpp b/lib/openpgp/public_key/material/elgamal_public_key_material_tests.cpp new file mode 100644 index 000000000..1a88e0148 --- /dev/null +++ b/lib/openpgp/public_key/material/elgamal_public_key_material_tests.cpp @@ -0,0 +1,31 @@ +// OpenPGP Elgamal public key material (tests) +// Copyright 2018 The NeoPG developers +// +// NeoPG is released under the Simplified BSD License (see license.txt) + +#include + +#include + +#include +#include + +using namespace NeoPG; + +TEST(OpenpgpElgamalPublicKeyMaterial, Create) { + const auto raw = std::string{"\x00\x09\x01\x62\x00\x02\x03\x00\x02\x02", 10}; + + ParserInput in(raw.data(), raw.length()); + auto elgamal_ptr = ElgamalPublicKeyMaterial::create_or_throw(in); + ASSERT_EQ(in.size(), 0); + + ElgamalPublicKeyMaterial& elgamal{*elgamal_ptr}; + ASSERT_EQ(elgamal.algorithm(), PublicKeyAlgorithm::Elgamal); + ASSERT_EQ(elgamal.m_p, MultiprecisionInteger(0x162)); + ASSERT_EQ(elgamal.m_g, MultiprecisionInteger(3)); + ASSERT_EQ(elgamal.m_y, MultiprecisionInteger(2)); + + std::stringstream out; + elgamal.write(out); + ASSERT_EQ(out.str(), raw); +} diff --git a/lib/openpgp/public_key/material/rsa_public_key_material.cpp b/lib/openpgp/public_key/material/rsa_public_key_material.cpp new file mode 100644 index 000000000..f0aafb70c --- /dev/null +++ b/lib/openpgp/public_key/material/rsa_public_key_material.cpp @@ -0,0 +1,24 @@ +// OpenPGP RSA public key material (implementation) +// Copyright 2018 The NeoPG developers +// +// NeoPG is released under the Simplified BSD License (see license.txt) + +#include + +#include +#include + +using namespace NeoPG; + +std::unique_ptr RsaPublicKeyMaterial::create_or_throw( + ParserInput& in) { + auto data = make_unique(); + data->m_n.parse(in); + data->m_e.parse(in); + return data; +} + +void RsaPublicKeyMaterial::write(std::ostream& out) const { + m_n.write(out); + m_e.write(out); +} diff --git a/lib/openpgp/public_key/material/rsa_public_key_material.h b/lib/openpgp/public_key/material/rsa_public_key_material.h new file mode 100644 index 000000000..b75184963 --- /dev/null +++ b/lib/openpgp/public_key/material/rsa_public_key_material.h @@ -0,0 +1,47 @@ +// OpenPGP RSA public key material +// Copyright 2018 The NeoPG developers +// +// NeoPG is released under the Simplified BSD License (see license.txt) + +/// \file +/// This file contains the RSA specific key material for public key packets. + +#pragma once + +#include + +#include +#include + +#include + +namespace NeoPG { + +/// Key material for RSA public keys. +class NEOPG_UNSTABLE_API RsaPublicKeyMaterial : public PublicKeyMaterial { + public: + MultiprecisionInteger m_n; + MultiprecisionInteger m_e; + + /// Create a new instance from \p input. + /// + /// \param input parser input with key material + /// + /// Throws parse_error if input can not be parsed. + static std::unique_ptr create_or_throw( + ParserInput& input); + + /// Return the public key algorithm. + /// + /// \return the value PublicKeyAlgorithm::Rsa + PublicKeyAlgorithm algorithm() const override { + return PublicKeyAlgorithm::Rsa; + }; + + /// Write the key material to the output stream. + /// + /// \param out the output stream to write to + void write(std::ostream& out) const override; +}; + +} // namespace NeoPG diff --git a/lib/openpgp/public_key/material/rsa_public_key_material_tests.cpp b/lib/openpgp/public_key/material/rsa_public_key_material_tests.cpp new file mode 100644 index 000000000..0f0976c50 --- /dev/null +++ b/lib/openpgp/public_key/material/rsa_public_key_material_tests.cpp @@ -0,0 +1,44 @@ +// OpenPGP RSA public key material (tests) +// Copyright 2018 The NeoPG developers +// +// NeoPG is released under the Simplified BSD License (see license.txt) + +#include + +#include + +#include +#include + +using namespace NeoPG; + +TEST(OpenpgpRsaPublicKeyMaterial, Create) { + const auto raw = std::string{"\x00\x09\x01\x62\x00\x02\x03", 7}; + + ParserInput in(raw.data(), raw.length()); + auto rsa_ptr = RsaPublicKeyMaterial::create_or_throw(in); + ASSERT_EQ(in.size(), 0); + + RsaPublicKeyMaterial& rsa{*rsa_ptr}; + ASSERT_EQ(rsa.algorithm(), PublicKeyAlgorithm::Rsa); + ASSERT_EQ(rsa.m_n, MultiprecisionInteger(0x162)); + ASSERT_EQ(rsa.m_e, MultiprecisionInteger(3)); + + std::stringstream out; + rsa.write(out); + ASSERT_EQ(out.str(), raw); +} + +TEST(OpenpgpRsaPublicKeyMaterial, ParseError) { + const auto raw = std::string{"\x00\x09\x01\x62\x00\x02\x03\x00\x02\x02", 10}; + ParserInput in1(raw.data(), raw.length()); + ASSERT_NO_THROW(RsaPublicKeyMaterial::create_or_throw(in1)); + ASSERT_EQ(in1.position(), 7); + + ParserInput in2(raw.data(), raw.length() - 6); + ASSERT_ANY_THROW(RsaPublicKeyMaterial::create_or_throw(in2)); + + ParserInput in3(raw.data(), raw.length() - 3); + in3.bump(4); + ASSERT_ANY_THROW(RsaPublicKeyMaterial::create_or_throw(in3)); +} diff --git a/lib/openpgp/public_key/public_key_data.cpp b/lib/openpgp/public_key/public_key_data.cpp new file mode 100644 index 000000000..91a3b9803 --- /dev/null +++ b/lib/openpgp/public_key/public_key_data.cpp @@ -0,0 +1,35 @@ +// OpenPGP public key packet data (implementation) +// Copyright 2018 The NeoPG developers +// +// NeoPG is released under the Simplified BSD License (see license.txt) + +#include + +#include +#include + +#include +#include + +#include + +using namespace NeoPG; + +std::unique_ptr PublicKeyData::create_or_throw( + PublicKeyVersion version, ParserInput& in) { + std::unique_ptr public_key; + + switch (version) { + case PublicKeyVersion::V2: + case PublicKeyVersion::V3: + public_key = V3PublicKeyData::create_or_throw(in); + break; + case PublicKeyVersion::V4: + public_key = V4PublicKeyData::create_or_throw(in); + break; + default: + in.error("unknown public key version"); + } + + return public_key; +} diff --git a/lib/openpgp/public_key/public_key_data.h b/lib/openpgp/public_key/public_key_data.h new file mode 100644 index 000000000..1becb12e5 --- /dev/null +++ b/lib/openpgp/public_key/public_key_data.h @@ -0,0 +1,51 @@ +// OpenPGP public key packet data +// Copyright 2018 The NeoPG developers +// +// NeoPG is released under the Simplified BSD License (see license.txt) + +/// \file +/// This file contains the version specific part of public key packets. + +#pragma once + +#include +#include + +#include + +namespace NeoPG { + +/// Represent an OpenPGP [public key +/// version](https://tools.ietf.org/html/rfc4880#section-5.5.2) +enum class PublicKeyVersion : uint8_t { V2 = 2, V3 = 3, V4 = 4 }; + +/// Represent the version specific part of an OpenPGP [public-key +/// packet](https://tools.ietf.org/html/rfc4880#section-5.5.2). +class NEOPG_UNSTABLE_API PublicKeyData { + public: + /// Create new public key data from \p input. Throw an exception on error. + /// + /// \param input the parser input to read from + /// + /// \return pointer to packet + /// + /// \throws ParserError + static std::unique_ptr create_or_throw( + PublicKeyVersion version, ParserInput& input); + + /// Write the packet body to the output stream. + /// + /// \param out the output stream to write to + virtual void write(std::ostream& out) const = 0; + + /// Return the public key version. + virtual PublicKeyVersion version() const noexcept = 0; + + /// Return the public key fingerprint. + virtual std::vector fingerprint() const = 0; + + /// Return the public key id. + virtual std::vector keyid() const = 0; +}; + +} // namespace NeoPG diff --git a/lib/openpgp/public_key/public_key_data_tests.cpp b/lib/openpgp/public_key/public_key_data_tests.cpp new file mode 100644 index 000000000..3871387ee --- /dev/null +++ b/lib/openpgp/public_key/public_key_data_tests.cpp @@ -0,0 +1,17 @@ +// OpenPGP public key packet data (tests) +// Copyright 2018 The NeoPG developers +// +// NeoPG is released under the Simplified BSD License (see license.txt) + +#include + +#include + +#include +#include + +using namespace NeoPG; + +TEST(OpenpgpPublicKeyData, Create) { + // FIXME: More tests. +} diff --git a/lib/openpgp/public_key/public_key_material.cpp b/lib/openpgp/public_key/public_key_material.cpp new file mode 100644 index 000000000..eef6e545b --- /dev/null +++ b/lib/openpgp/public_key/public_key_material.cpp @@ -0,0 +1,43 @@ +// OpenPGP public key material (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 + +using namespace NeoPG; + +std::unique_ptr PublicKeyMaterial::create_or_throw( + PublicKeyAlgorithm algorithm, ParserInput& in) { + switch (algorithm) { + case PublicKeyAlgorithm::Rsa: + case PublicKeyAlgorithm::RsaEncrypt: + case PublicKeyAlgorithm::RsaSign: + return RsaPublicKeyMaterial::create_or_throw(in); + case PublicKeyAlgorithm::Dsa: + return DsaPublicKeyMaterial::create_or_throw(in); + case PublicKeyAlgorithm::Elgamal: + case PublicKeyAlgorithm::ElgamalEncrypt: + return ElgamalPublicKeyMaterial::create_or_throw(in); + case PublicKeyAlgorithm::Ecdsa: + return EcdsaPublicKeyMaterial::create_or_throw(in); + case PublicKeyAlgorithm::Ecdh: + return EcdhPublicKeyMaterial::create_or_throw(in); + case PublicKeyAlgorithm::Eddsa: + return EddsaPublicKeyMaterial::create_or_throw(in); + default: + in.error("unknown public key algorithm"); + } + // Never reached. + return nullptr; +} diff --git a/lib/openpgp/public_key/public_key_material.h b/lib/openpgp/public_key/public_key_material.h new file mode 100644 index 000000000..947c09a32 --- /dev/null +++ b/lib/openpgp/public_key/public_key_material.h @@ -0,0 +1,68 @@ +// OpenPGP public key material +// Copyright 2018 The NeoPG developers +// +// NeoPG is released under the Simplified BSD License (see license.txt) + +/// \file +/// This file contains the algorithm specific key material of public key +/// packets. + +#pragma once + +#include +#include +#include + +#include + +namespace NeoPG { + +/// Public key [algorithm +/// specifier](https://tools.ietf.org/html/rfc4880#section-9.1). +enum class PublicKeyAlgorithm : uint8_t { + Rsa = 1, + RsaEncrypt = 2, + RsaSign = 3, + ElgamalEncrypt = 16, + Dsa = 17, + Ecdh = 18, + Ecdsa = 19, + Elgamal = 20, + DhX942 = 21, + Eddsa = 22, + Private_100 = 100, + Private_101 = 101, + Private_102 = 102, + Private_103 = 103, + Private_104 = 104, + Private_105 = 105, + Private_106 = 106, + Private_107 = 107, + Private_108 = 108, + Private_109 = 109, + Private_110 = 110 +}; + +/// Algorithm-specific key material for a [public +/// key](https://tools.ietf.org/html/rfc4880#section-5.5.2). +class NEOPG_UNSTABLE_API PublicKeyMaterial { + public: + /// Create an instance based on the algorithm. + /// + /// \param algorithm algorithm specifier + /// \param input parser input with key material + /// + /// Throws parse_error if input can not be parsed. + static std::unique_ptr create_or_throw( + PublicKeyAlgorithm algorithm, ParserInput& input); + + /// \return the algorithm specifier + virtual PublicKeyAlgorithm algorithm() const = 0; + + /// Write the key material to the output stream. + /// + /// \param out output stream + virtual void write(std::ostream& out) const = 0; +}; + +} // namespace NeoPG diff --git a/lib/openpgp/public_key/public_key_material_tests.cpp b/lib/openpgp/public_key/public_key_material_tests.cpp new file mode 100644 index 000000000..537077757 --- /dev/null +++ b/lib/openpgp/public_key/public_key_material_tests.cpp @@ -0,0 +1,35 @@ +// OpenPGP public key material (tests) +// Copyright 2018 The NeoPG developers +// +// NeoPG is released under the Simplified BSD License (see license.txt) + +#include + +#include + +#include + +#include +#include + +using namespace NeoPG; + +TEST(OpenpgpPublicKeyMaterial, CreateRsa) { + const auto raw = std::string{"\x00\x09\x01\x62\x00\x02\x03", 7}; + + ParserInput in(raw.data(), raw.length()); + auto mat = PublicKeyMaterial::create_or_throw(PublicKeyAlgorithm::Rsa, in); + ASSERT_EQ(in.size(), 0); + + auto rsa_ptr = dynamic_cast(mat.get()); + ASSERT_NE(rsa_ptr, nullptr); + + RsaPublicKeyMaterial& rsa{*rsa_ptr}; + ASSERT_EQ(rsa.algorithm(), PublicKeyAlgorithm::Rsa); + ASSERT_EQ(rsa.m_n, MultiprecisionInteger(0x162)); + ASSERT_EQ(rsa.m_e, MultiprecisionInteger(3)); + + std::stringstream out; + rsa.write(out); + ASSERT_EQ(out.str(), raw); +} diff --git a/lib/openpgp/public_key_packet.cpp b/lib/openpgp/public_key_packet.cpp new file mode 100644 index 000000000..a9ffdd77c --- /dev/null +++ b/lib/openpgp/public_key_packet.cpp @@ -0,0 +1,79 @@ +// OpenPGP public key packet (implementation) +// Copyright 2018 The NeoPG developers +// +// NeoPG is released under the Simplified BSD License (see license.txt) + +#include + +#include +#include + +using namespace NeoPG; + +namespace NeoPG { +namespace public_key_packet { + +using namespace pegtl; + +// Grammar +struct version : any {}; +struct grammar : must {}; + +// In this case, we don't use nested parsing, because we don't know the size of +// the public key data ahead of time. After parsing the public key material +// we reenter here. +struct end : must {}; + +// Action +template +struct action : nothing {}; + +template <> +struct action + : bind {}; + +// Control +template +struct control : pegtl::normal { + static const std::string error_message; + + template + static void raise(const Input& in, States&&...) { + throw parser_error(error_message, in); + } +}; + +template <> +const std::string control::error_message = + "public key packet has invalid version number"; + +template <> +const std::string control::error_message = + "public key packet is too large"; + +} // namespace public_key_packet +} // namespace NeoPG + +std::unique_ptr PublicKeyPacket::create(ParserInput& in) { + try { + return PublicKeyPacket::create_or_throw(in); + } catch (const pegtl::parse_error&) { + return nullptr; + } +} + +std::unique_ptr PublicKeyPacket::create_or_throw( + ParserInput& in) { + auto packet = make_unique(); + pegtl::parse(in.m_impl->m_input, *packet.get()); + packet->m_public_key = PublicKeyData::create_or_throw(packet->version(), in); + pegtl::parse(in.m_impl->m_input, *packet.get()); + return packet; +} + +void PublicKeyPacket::write_body(std::ostream& out) const { + out << static_cast(m_version); + if (m_public_key) m_public_key->write(out); +} diff --git a/lib/openpgp/public_key_packet.h b/lib/openpgp/public_key_packet.h new file mode 100644 index 000000000..6bd50f56f --- /dev/null +++ b/lib/openpgp/public_key_packet.h @@ -0,0 +1,66 @@ +// OpenPGP public key packet +// Copyright 2018 The NeoPG developers +// +// NeoPG is released under the Simplified BSD License (see license.txt) + +/// \file +/// This file contains support for OpenPGP public key packets. + +#pragma once + +#include +#include + +#include + +namespace NeoPG { + +/// Represent an OpenPGP [public-key +/// packet](https://tools.ietf.org/html/rfc4880#section-5.5.2). +class NEOPG_UNSTABLE_API PublicKeyPacket : public Packet { + public: + /// Create a new public key packet from \p input. + /// + /// \param input the parser input to read from + /// + /// \return pointer to packet or nullptr on error + static std::unique_ptr create_or_throw(ParserInput& input); + + /// Create a new public key packet from \p input. Throw an exception on error. + /// + /// \param input the parser input to read from + /// + /// \return pointer to packet + /// + /// \throws ParserError + static std::unique_ptr create(ParserInput& input); + + /// The version of the public key packet. + PublicKeyVersion m_version{PublicKeyVersion::V4}; + + /// The public key data. + /// + /// The public key data has its own version. For example, \a m_version may + /// indicate a version 2 public key with \a m_public_key being version 3. + std::unique_ptr m_public_key; + + /// Write the packet body to the output stream. + /// + /// \param out the output stream to write to + void write_body(std::ostream& out) const override; + + /// Return the packet type. + /// + /// \return the value PacketType::PublicKey + PacketType type() const noexcept override { return PacketType::PublicKey; }; + + /// Return the public key version of the packet. + /// + /// \return the public key version + PublicKeyVersion version() const noexcept { return m_version; } + + /// Construct a new public key packet. + PublicKeyPacket() = default; +}; + +} // namespace NeoPG diff --git a/lib/openpgp/public_key_packet_tests.cpp b/lib/openpgp/public_key_packet_tests.cpp new file mode 100644 index 000000000..fac4f0d77 --- /dev/null +++ b/lib/openpgp/public_key_packet_tests.cpp @@ -0,0 +1,76 @@ +// OpenPGP public key packet (tests) +// Copyright 2018 The NeoPG developers +// +// NeoPG is released under the Simplified BSD License (see license.txt) + +#include + +#include + +#include +#include + +using namespace NeoPG; + +TEST(OpenpgpPublicKeyPacket, WriteWithOldHeader) { + // Test old packet header. + std::stringstream out; + PublicKeyPacket packet; + + packet.write(out, OldPacketHeader::create_or_throw); + // Not really a packet, but good enough for testing. + ASSERT_EQ(out.str(), std::string("\x98\x01\x04", 3)); +} + +TEST(OpenpgpPublicKeyPacket, WriteWithNewHeader) { + // Test new packet header. + std::stringstream out; + PublicKeyPacket packet; + packet.write(out); + ASSERT_EQ(out.str(), std::string("\xc6\x01\x04", 3)); +} + +TEST(OpenpgpPublicKeyPacket, ParseV3) { + // Test V3 packets. + const std::string raw{ + "\x03" + "\x12\x34\x56\x78" + "\xab\xcd" + "\x01" + "\x00\x11\x01\x42\x23" + "\x00\x02\x03", + 16}; + ParserInput in(raw.data(), raw.length()); + auto packet = PublicKeyPacket::create_or_throw(in); + ASSERT_EQ(packet->version(), PublicKeyVersion::V3); + auto public_key = packet->m_public_key.get(); + ASSERT_NE(public_key, nullptr); + ASSERT_EQ(public_key->version(), PublicKeyVersion::V3); + + // Test writing. + std::stringstream out; + packet->write_body(out); + ASSERT_EQ(out.str(), raw); +} + +TEST(OpenpgpPublicKeyPacket, ParseV4) { + // Test V4 packets. + const std::string raw{ + "\x04" + "\x12\x34\x56\x78" + "\x01" + "\x00\x11\x01\x42\x23" + "\x00\x02\x03", + 14}; + ParserInput in(raw.data(), raw.length()); + auto packet = PublicKeyPacket::create_or_throw(in); + ASSERT_EQ(packet->version(), PublicKeyVersion::V4); + auto public_key = packet->m_public_key.get(); + ASSERT_NE(public_key, nullptr); + ASSERT_EQ(public_key->version(), PublicKeyVersion::V4); + + // Test writing. + std::stringstream out; + packet->write_body(out); + ASSERT_EQ(out.str(), raw); +} diff --git a/lib/openpgp/public_subkey_packet.cpp b/lib/openpgp/public_subkey_packet.cpp new file mode 100644 index 000000000..bbd4cb038 --- /dev/null +++ b/lib/openpgp/public_subkey_packet.cpp @@ -0,0 +1,81 @@ +// OpenPGP public subkey packet (implementation) +// Copyright 2018 The NeoPG developers +// +// NeoPG is released under the Simplified BSD License (see license.txt) + +#include + +#include +#include + +using namespace NeoPG; + +namespace NeoPG { +namespace public_subkey_packet { + +using namespace pegtl; + +// Grammar +struct version : any {}; +struct grammar : must {}; + +// In this case, we can't use nested parsing, because we don't know the size of +// the public key data ahead of time. +struct end : must {}; + +// Action +template +struct action : nothing {}; + +template <> +struct action : bind {}; + +// Control +template +struct control : pegtl::normal { + static const std::string error_message; + + template + static void raise(const Input& in, States&&...) { + throw parser_error(error_message, in); + } +}; + +template <> +const std::string control::error_message = + "public subkey packet has invalid version number"; + +template <> +const std::string control::error_message = + "public subkey packet is too large"; + +} // namespace public_subkey_packet +} // namespace NeoPG + +std::unique_ptr PublicSubkeyPacket::create( + ParserInput& in) { + try { + return PublicSubkeyPacket::create_or_throw(in); + } catch (const pegtl::parse_error&) { + return nullptr; + } +} + +std::unique_ptr PublicSubkeyPacket::create_or_throw( + ParserInput& in) { + auto packet = make_unique(); + pegtl::parse(in.m_impl->m_input, + *packet.get()); + packet->m_public_key = PublicKeyData::create_or_throw(packet->version(), in); + pegtl::parse(in.m_impl->m_input, + *packet.get()); + return packet; +} + +void PublicSubkeyPacket::write_body(std::ostream& out) const { + out << static_cast(m_version); + if (m_public_key) m_public_key->write(out); +} diff --git a/lib/openpgp/public_subkey_packet.h b/lib/openpgp/public_subkey_packet.h new file mode 100644 index 000000000..7f9214c06 --- /dev/null +++ b/lib/openpgp/public_subkey_packet.h @@ -0,0 +1,70 @@ +// OpenPGP public subkey packet +// Copyright 2018 The NeoPG developers +// +// NeoPG is released under the Simplified BSD License (see license.txt) + +/// \file +/// This file contains support for OpenPGP public key packets. + +#pragma once + +#include +#include + +#include + +namespace NeoPG { + +/// Represent an OpenPGP [public-subkey +/// packet](https://tools.ietf.org/html/rfc4880#section-5.5.2). +class NEOPG_UNSTABLE_API PublicSubkeyPacket : public Packet { + public: + /// Create a new public subkey packet from \p input. + /// + /// \param input the parser input to read from + /// + /// \return pointer to packet or nullptr on error + static std::unique_ptr create_or_throw( + ParserInput& input); + + /// Create a new public subkey packet from \p input. Throw an exception on + /// error. + /// + /// \param input the parser input to read from + /// + /// \return pointer to packet + /// + /// \throws ParserError + static std::unique_ptr create(ParserInput& input); + + /// The version of the public key packet. + PublicKeyVersion m_version{PublicKeyVersion::V4}; + + /// The public key data. + /// + /// The public key data has its own version. For example, \a m_version may + /// indicate a version 2 public key with \a m_public_key being version 3. + std::unique_ptr m_public_key; + + /// Write the packet body to the output stream. + /// + /// \param out the output stream to write to + void write_body(std::ostream& out) const override; + + /// Return the packet type. + /// + /// \return the value PacketType::PublicKey + PacketType type() const noexcept override { + return PacketType::PublicSubkey; + }; + + /// Return the public subkey version of the packet. + /// + /// \return the public subkey version + PublicKeyVersion version() const noexcept { return m_version; } + + /// Construct a new public subkey packet. + PublicSubkeyPacket() = default; +}; + +} // namespace NeoPG diff --git a/lib/openpgp/public_subkey_packet_tests.cpp b/lib/openpgp/public_subkey_packet_tests.cpp new file mode 100644 index 000000000..a15a7e836 --- /dev/null +++ b/lib/openpgp/public_subkey_packet_tests.cpp @@ -0,0 +1,50 @@ +// OpenPGP public subkey packet (tests) +// Copyright 2018 The NeoPG developers +// +// NeoPG is released under the Simplified BSD License (see license.txt) + +#include + +#include + +#include +#include + +using namespace NeoPG; + +TEST(OpenpgpPublicSubkeyPacket, WriteWithOldHeader) { + // Test old packet header. + std::stringstream out; + PublicSubkeyPacket packet; + + packet.write(out, OldPacketHeader::create_or_throw); + // Not really a packet, but good enough for testing. + ASSERT_EQ(out.str(), std::string("\xb8\x01\x04", 3)); +} + +TEST(OpenpgpPublicSubkeyPacket, WriteWithNewHeader) { + // Test new packet header. + std::stringstream out; + PublicSubkeyPacket packet; + packet.write(out); + ASSERT_EQ(out.str(), std::string("\xce\x01\x04", 3)); +} + +TEST(OpenpgpPublicSubkeyPacket, ParseV3) { + // Test V3 packets. + const std::string raw{ + "\x03" + "\x12\x34\x56\x78" + "\xab\xcd" + "\x01" + "\x00\x11\x01\x42\x23" + "\x00\x02\x03", + 16}; + ParserInput in(raw.data(), raw.length()); + auto packet = PublicSubkeyPacket::create_or_throw(in); + ASSERT_EQ(packet->version(), PublicKeyVersion::V3); + auto public_key = packet->m_public_key.get(); + ASSERT_NE(public_key, nullptr); + ASSERT_EQ(public_key->version(), PublicKeyVersion::V3); +} +// FIXME: More tests (writing packet). diff --git a/lib/tests/CMakeLists.txt b/lib/tests/CMakeLists.txt index 16afc87b0..565f74975 100644 --- a/lib/tests/CMakeLists.txt +++ b/lib/tests/CMakeLists.txt @@ -12,6 +12,18 @@ add_executable(test-libneopg ../openpgp/multiprecision_integer_tests.cpp ../openpgp/object_identifier_tests.cpp ../openpgp/packet_header_tests.cpp + ../openpgp/public_key_packet_tests.cpp + ../openpgp/public_key/data/v3_public_key_data_tests.cpp + ../openpgp/public_key/data/v4_public_key_data_tests.cpp + ../openpgp/public_key/material/dsa_public_key_material_tests.cpp + ../openpgp/public_key/material/ecdh_public_key_material_tests.cpp + ../openpgp/public_key/material/ecdsa_public_key_material_tests.cpp + ../openpgp/public_key/material/eddsa_public_key_material_tests.cpp + ../openpgp/public_key/material/elgamal_public_key_material_tests.cpp + ../openpgp/public_key/material/rsa_public_key_material_tests.cpp + ../openpgp/public_key/public_key_data_tests.cpp + ../openpgp/public_key/public_key_material_tests.cpp + ../openpgp/public_subkey_packet_tests.cpp ../openpgp/symmetrically_encrypted_data_packet_tests.cpp ../openpgp/symmetrically_encrypted_integrity_protected_data_packet_tests.cpp ../openpgp/trust_packet_tests.cpp diff --git a/src/cli/packet_command.cpp b/src/cli/packet_command.cpp index 42e079e13..abdf1af29 100644 --- a/src/cli/packet_command.cpp +++ b/src/cli/packet_command.cpp @@ -13,6 +13,13 @@ #include #include +#include +#include +#include +#include +#include +#include + #include #include