Skip to content

Commit

Permalink
Add PublicKeyPacket and PublicSubkeyPacket.
Browse files Browse the repository at this point in the history
  • Loading branch information
lambdafu committed Jun 25, 2018
1 parent e69e01d commit 2b3a65c
Show file tree
Hide file tree
Showing 40 changed files with 1,893 additions and 0 deletions.
24 changes: 24 additions & 0 deletions lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
42 changes: 42 additions & 0 deletions lib/include/neopg/intern/pegtl.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
#include <neopg/parser_input.h>
#include <neopg/parser_position.h>

#include <neopg/public_key_data.h>

#include <botan/loadstor.h>

#include <string>
#include <vector>

Expand Down Expand Up @@ -91,5 +95,43 @@ struct bind<T, std::string, Field> {
}
};

template <typename T, uint32_t T::*Field>
struct bind<T, uint32_t, Field> {
template <typename Input>
static void apply(const Input& in, T& pkt) {
auto src = in.begin();
auto ptr = reinterpret_cast<const uint8_t*>(src);
static_assert(sizeof(*src) == sizeof(*ptr), "can't do pointer arithmetic");
pkt.*Field = Botan::load_be<uint32_t>(ptr, 0);
}
};

template <typename T, uint16_t T::*Field>
struct bind<T, uint16_t, Field> {
template <typename Input>
static void apply(const Input& in, T& pkt) {
auto src = in.begin();
auto ptr = reinterpret_cast<const uint8_t*>(src);
static_assert(sizeof(*src) == sizeof(*ptr), "can't do pointer arithmetic");
pkt.*Field = Botan::load_be<uint16_t>(ptr, 0);
}
};

template <typename T, NeoPG::PublicKeyVersion T::*Field>
struct bind<T, NeoPG::PublicKeyVersion, Field> {
template <typename Input>
static void apply(const Input& in, T& pkt) {
pkt.*Field = static_cast<NeoPG::PublicKeyVersion>(in.peek_byte());
}
};

template <typename T, NeoPG::PublicKeyAlgorithm T::*Field>
struct bind<T, NeoPG::PublicKeyAlgorithm, Field> {
template <typename Input>
static void apply(const Input& in, T& pkt) {
pkt.*Field = static_cast<NeoPG::PublicKeyAlgorithm>(in.peek_byte());
}
};

} // namespace TAO_PEGTL_NAMESPACE
} // namespace tao
115 changes: 115 additions & 0 deletions lib/openpgp/public_key/data/v3_public_key_data.cpp
Original file line number Diff line number Diff line change
@@ -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 <neopg/v3_public_key_data.h>

#include <neopg/rsa_public_key_material.h>

#include <neopg/intern/cplusplus.h>
#include <neopg/intern/pegtl.h>

#include <botan/md5.h>

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<static_cast<uint8_t>(PublicKeyAlgorithm::Rsa),
static_cast<uint8_t>(PublicKeyAlgorithm::RsaEncrypt),
static_cast<uint8_t>(PublicKeyAlgorithm::RsaSign)> {};
struct grammar : must<created, days_valid, algorithm> {};

// Action
template <typename Rule>
struct action : nothing<Rule> {};

template <>
struct action<created>
: bind<V3PublicKeyData, uint32_t, &V3PublicKeyData::m_created> {};

template <>
struct action<days_valid>
: bind<V3PublicKeyData, uint16_t, &V3PublicKeyData::m_days_valid> {};

template <>
struct action<algorithm>
: bind<V3PublicKeyData, PublicKeyAlgorithm, &V3PublicKeyData::m_algorithm> {
};

// Control
template <typename Rule>
struct control : pegtl::normal<Rule> {
static const std::string error_message;

template <typename Input, typename... States>
static void raise(const Input& in, States&&...) {
throw parser_error(error_message, in);
}
};

template <>
const std::string control<created>::error_message =
"v3 public key data is missing created time";

template <>
const std::string control<days_valid>::error_message =
"v3 public key data is missing days invalid";

template <>
const std::string control<algorithm>::error_message =
"v3 public key data has invalid algorithm specifier";
} // namespace v3_public_key_data

std::unique_ptr<V3PublicKeyData> V3PublicKeyData::create_or_throw(
ParserInput& in) {
auto packet = make_unique<V3PublicKeyData>();

pegtl::parse<v3_public_key_data::grammar, v3_public_key_data::action,
v3_public_key_data::control>(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<uint8_t>(m_created >> 24)
<< static_cast<uint8_t>(m_created >> 16)
<< static_cast<uint8_t>(m_created >> 8)
<< static_cast<uint8_t>(m_created);
out << static_cast<uint8_t>(m_days_valid >> 8)
<< static_cast<uint8_t>(m_days_valid);
out << static_cast<uint8_t>(m_algorithm);
if (m_key) m_key->write(out);
}

std::vector<uint8_t> V3PublicKeyData::fingerprint() const {
Botan::MD5 md5;
auto rsa = dynamic_cast<RsaPublicKeyMaterial*>(m_key.get());
if (rsa) {
md5.update(rsa->m_n.bits());
md5.update(rsa->m_e.bits());
}
return md5.final_stdvec();
}

std::vector<uint8_t> V3PublicKeyData::keyid() const {
std::vector<uint8_t> keyid(KEYID_LENGTH, static_cast<uint8_t>(0x00));
auto rsa = dynamic_cast<RsaPublicKeyMaterial*>(m_key.get());
if (rsa) {
const std::vector<uint8_t>& 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
64 changes: 64 additions & 0 deletions lib/openpgp/public_key/data/v3_public_key_data.h
Original file line number Diff line number Diff line change
@@ -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 <neopg/public_key_data.h>

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<V3PublicKeyData> 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<PublicKeyMaterial> 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<uint8_t> 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<uint8_t> keyid() const override;

/// Construct new v3 public key packet data.
V3PublicKeyData() = default;
};

} // namespace NeoPG
75 changes: 75 additions & 0 deletions lib/openpgp/public_key/data/v3_public_key_data_tests.cpp
Original file line number Diff line number Diff line change
@@ -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 <neopg/v3_public_key_data.h>

#include <neopg/rsa_public_key_material.h>

#include <neopg/intern/cplusplus.h>

#include <gtest/gtest.h>

#include <memory>
#include <sstream>

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<uint8_t>{0xb5, 0xb5, 0xbe, 0xc2, 0x3d, 0x70, 0xea, 0x0e,
0x05, 0x68, 0x45, 0x64, 0xac, 0xa7, 0x3d, 0xc7};
auto keyid =
std::vector<uint8_t>{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<V3PublicKeyData*>(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<RsaPublicKeyMaterial*>(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<RsaPublicKeyMaterial>();
auto rsa = dynamic_cast<RsaPublicKeyMaterial*>(v3key.m_key.get());
rsa->m_n.m_bits = std::vector<uint8_t>{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<uint8_t>{0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00};
ASSERT_EQ(v3key.keyid(), keyid);
}

TEST(OpenpgpV3PublicKeyData, ShortKeyId) {
V3PublicKeyData v3key;
v3key.m_key = make_unique<RsaPublicKeyMaterial>();
auto rsa = dynamic_cast<RsaPublicKeyMaterial*>(v3key.m_key.get());
rsa->m_n.m_bits = std::vector<uint8_t>{0xff, 0x01, 0x00};
rsa->m_n.m_length = rsa->m_n.m_bits.size() * 8;
auto keyid =
std::vector<uint8_t>{0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x00};
ASSERT_EQ(v3key.keyid(), keyid);
}
Loading

0 comments on commit 2b3a65c

Please sign in to comment.