├── .clang-tidy ├── .github ├── pull_request_template.md └── workflows │ ├── macos.yml │ └── ubuntu.yml ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── COPYING ├── CppCheckSuppressions.txt ├── README.md ├── cmake ├── Modules │ ├── CheckCXX17SourceRuns.cmake │ ├── FindCryptoPP.cmake │ └── Findsodium.cmake ├── pgp-packet-config.cmake └── tests │ ├── std_span_test.cpp │ └── std_variant_test.cpp ├── examples ├── CMakeLists.txt ├── create_simple_packet.cpp ├── encoding_and_decoding.cpp └── key_from_raw_data.cpp ├── include ├── allocator.h ├── basic_key.h ├── basic_secret_key.h ├── compression_algorithm.h ├── curve_oid.h ├── decoder.h ├── decoder_traits.h ├── dsa_public_key.h ├── dsa_secret_key.h ├── dsa_signature.h ├── dsa_signature_encoder.h ├── ecdh_public_key.h ├── ecdh_secret_key.h ├── ecdsa_public_key.h ├── ecdsa_secret_key.h ├── ecdsa_signature.h ├── ecdsa_signature_encoder.h ├── eddsa_public_key.h ├── eddsa_secret_key.h ├── eddsa_signature.h ├── eddsa_signature_encoder.h ├── elgamal_public_key.h ├── elgamal_secret_key.h ├── expected_number.h ├── fixed_number.h ├── hash_algorithm.h ├── hash_encoder.h ├── key_algorithm.h ├── key_flag.h ├── multiprecision_integer.h ├── null_hash.h ├── packet.h ├── packet_tag.h ├── public_key.h ├── range_encoder.h ├── rsa_public_key.h ├── rsa_secret_key.h ├── rsa_signature.h ├── rsa_signature_encoder.h ├── secret_key.h ├── secure_object.h ├── signature.h ├── signature_subpacket │ ├── embedded.h │ ├── fixed_array.h │ ├── issuer_fingerprint.h │ ├── key_flags.h │ ├── numeric.h │ ├── preferred_algorithms.h │ └── unknown.h ├── signature_subpacket_set.h ├── signature_subpacket_type.h ├── signature_type.h ├── string_to_key.h ├── symmetric_key_algorithm.h ├── unknown_key.h ├── unknown_packet.h ├── unknown_signature.h ├── unknown_signature_encoder.h ├── user_id.h ├── util │ ├── narrow_cast.h │ ├── span.h │ ├── transaction.h │ ├── tuple.h │ ├── variant.h │ └── vector.h └── variable_number.h ├── source ├── curve_oid.cpp ├── decoder.cpp ├── dsa_public_key.cpp ├── dsa_secret_key.cpp ├── dsa_signature.cpp ├── dsa_signature_encoder.cpp ├── ecdh_public_key.cpp ├── ecdh_secret_key.cpp ├── ecdsa_public_key.cpp ├── ecdsa_secret_key.cpp ├── ecdsa_signature.cpp ├── ecdsa_signature_encoder.cpp ├── eddsa_public_key.cpp ├── eddsa_secret_key.cpp ├── eddsa_signature.cpp ├── eddsa_signature_encoder.cpp ├── elgamal_public_key.cpp ├── elgamal_secret_key.cpp ├── multiprecision_integer.cpp ├── packet.cpp ├── range_encoder.cpp ├── rsa_public_key.cpp ├── rsa_secret_key.cpp ├── rsa_signature.cpp ├── rsa_signature_encoder.cpp ├── signature.cpp ├── signature_subpacket │ ├── embedded.cpp │ ├── issuer_fingerprint.cpp │ └── unknown.cpp ├── signature_subpacket_set.cpp ├── string_to_key.cpp ├── unknown_signature_encoder.cpp ├── user_id.cpp └── variable_number.cpp └── tests ├── CMakeLists.txt ├── device_random_engine.cpp ├── device_random_engine.h ├── generate.cpp ├── generate.h ├── key_template.cpp ├── key_template.h ├── main.cpp └── unit_tests ├── curve_oid.cpp ├── decoder.cpp ├── device_random_engine.cpp ├── dsa_public_key.cpp ├── dsa_secret_key.cpp ├── dsa_signature.cpp ├── ecdh_public_key.cpp ├── ecdh_secret_key.cpp ├── ecdsa_public_key.cpp ├── ecdsa_secret_key.cpp ├── ecdsa_signature.cpp ├── eddsa_public_key.cpp ├── eddsa_secret_key.cpp ├── eddsa_signature.cpp ├── elgamal_public_key.cpp ├── elgamal_secret_key.cpp ├── expected_number.cpp ├── fixed_number.cpp ├── hash_encoder.cpp ├── multiprecision_integer.cpp ├── packet.cpp ├── public_key.cpp ├── range_encoder.cpp ├── rsa_public_key.cpp ├── rsa_secret_key.cpp ├── rsa_signature.cpp ├── secret_key.cpp ├── signature.cpp ├── signature_subpacket ├── embedded.cpp ├── fixed_array.cpp ├── issuer_fingerprint.cpp ├── key_flags.cpp ├── numeric.cpp └── unknown.cpp ├── signature_subpacket_set.cpp ├── unknown_signature.cpp ├── user_id.cpp └── variable_number.cpp /.clang-tidy: -------------------------------------------------------------------------------- 1 | --- 2 | Checks: 'clang-diagnostic-*,clang-analyzer-*,boost-*,bugprone-*,cert-*,cppcoreguidelines-*,-cppcoreguidelines-macro-usage,-cppcoreguidelines-avoid-magic-numbers,-cppcoreguidelines-c-copy-assignment-signature,-cppcoreguidelines-pro-type-reinterpret-cast,hicpp-*,-hicpp-explicit-conversions,misc-*,-misc-unused-using-decls,performance-*,portability-*' 3 | WarningsAsErrors: '' 4 | HeaderFilterRegex: '' 5 | AnalyzeTemporaryDtors: false 6 | FormatStyle: none 7 | CheckOptions: 8 | - key: cert-dcl16-c.NewSuffixes 9 | value: 'L;LL;LU;LLU' 10 | - key: cppcoreguidelines-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic 11 | value: '1' 12 | ... 13 | 14 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | By making a contribution to this project, I certify that: 2 | 3 | (a) The contribution was created in whole or in part by me and I 4 | have the right to submit it under the GPL-3.0 license; or 5 | 6 | (b) The contribution is based upon previous work that, to the best 7 | of my knowledge, is covered under an appropriate open source 8 | license and I have the right under that license to submit that 9 | work with modifications, whether created in whole or in part 10 | by me, under GPL-3.0 license; or 11 | 12 | (c) The contribution was provided directly to me by some other 13 | person who certified (a), (b) or (c) and I have not modified 14 | it. 15 | 16 | (d) I understand and agree that this project and the contribution 17 | are public and that a record of the contribution (including all 18 | personal information I submit with it, including my sign-off) is 19 | maintained indefinitely and may be redistributed consistent with 20 | this project or the license(s) involved. 21 | -------------------------------------------------------------------------------- /.github/workflows/macos.yml: -------------------------------------------------------------------------------- 1 | name: macos-latest 2 | 3 | 'on': 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | jobs: 12 | appleclang-latest: 13 | name: appleclang-13.0.0 14 | runs-on: macos-11 15 | 16 | steps: 17 | - uses: actions/checkout@v2 18 | - name: submodules 19 | run: git submodule update --init --recursive 20 | - name: dependencies 21 | run: | 22 | brew install boost 23 | curl -O https://www.cryptopp.com/cryptopp820.zip 24 | unzip cryptopp820.zip -d cryptopp820 25 | make -C cryptopp820 shared all 26 | make -C cryptopp820 install 27 | - name: cmake 28 | run: cmake -B build -DCMAKE_BUILD_TYPE=Debug 29 | - name: build 30 | run: cmake --build build 31 | - name: test 32 | run: ./build/tests/tests 33 | - name: examples 34 | run: | 35 | make install -C build 36 | cd examples 37 | cmake -B build 38 | make -C build 39 | ./build/create_simple_packet 40 | ./build/encoding_and_decoding 41 | ./build/key_from_raw_data 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | .*.swp 3 | default.profraw 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "GSL"] 2 | path = GSL 3 | url = https://github.com/microsoft/GSL.git 4 | [submodule "variant"] 5 | path = variant 6 | url = https://github.com/mpark/variant.git 7 | [submodule "tests/googletest"] 8 | path = tests/googletest 9 | url = https://github.com/google/googletest 10 | -------------------------------------------------------------------------------- /CppCheckSuppressions.txt: -------------------------------------------------------------------------------- 1 | // Skip the Google test libraries since they're full of issues. 2 | *:*google* 3 | 4 | // passedByValue trips over constructors 5 | passedByValue 6 | 7 | // UB on moved-from values 8 | throwInNoexceptFunction:source/signature_subpacket/embedded.cpp:56 9 | throwInNoexceptFunction:source/signature_subpacket/embedded.cpp:69 10 | -------------------------------------------------------------------------------- /cmake/Modules/CheckCXX17SourceRuns.cmake: -------------------------------------------------------------------------------- 1 | # Implementation idea stolen from CheckCXXSourceRuns, which doesn't support 2 | # setting the C++ language version 3 | macro(check_cxx17_source_runs FILENAME VAR) 4 | if(NOT CMAKE_REQUIRED_QUIET) 5 | message(STATUS "Performing test ${VAR}") 6 | endif() 7 | 8 | try_run(${VAR}_EXITCODE ${VAR}_COMPILED 9 | ${CMAKE_BINARY_DIR} 10 | ${FILENAME} 11 | CXX_STANDARD 17) 12 | 13 | if(NOT ${VAR}_COMPILED) 14 | set(${VAR}_EXITCODE 1) 15 | endif() 16 | if(${VAR}_EXITCODE EQUAL 0) 17 | set(${VAR} 1 CACHE INTERNAL "Test ${VAR}") 18 | if(NOT CMAKE_REQUIRED_QUIET) 19 | message(STATUS "Performing test ${VAR} - Success") 20 | endif() 21 | else() 22 | set(${VAR} "" CACHE INTERNAL "Test ${VAR}") 23 | if(NOT CMAKE_REQUIRED_QUIET) 24 | message(STATUS "Performing test ${VAR} - Failed") 25 | endif() 26 | endif() 27 | endmacro() 28 | -------------------------------------------------------------------------------- /cmake/Modules/FindCryptoPP.cmake: -------------------------------------------------------------------------------- 1 | find_path(CryptoPP_INCLUDE_DIR NAMES cryptopp/config.h DOC "CryptoPP include directory") 2 | find_library(CryptoPP_LIBRARY NAMES cryptopp DOC "CryptoPP library") 3 | 4 | if(CryptoPP_INCLUDE_DIR) 5 | find_file(CryptoPP_CONFIG_HEADER NAMES config_ver.h config.h PATHS ${CryptoPP_INCLUDE_DIR}/cryptopp) 6 | file(STRINGS ${CryptoPP_CONFIG_HEADER} _config_version REGEX "CRYPTOPP_VERSION") 7 | string(REGEX MATCH "([0-9])([0-9])([0-9])" _match_version ${_config_version}) 8 | set(CryptoPP_VERSION_STRING "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}.${CMAKE_MATCH_3}") 9 | endif() 10 | 11 | include(FindPackageHandleStandardArgs) 12 | find_package_handle_standard_args(CryptoPP 13 | REQUIRED_VARS CryptoPP_INCLUDE_DIR CryptoPP_LIBRARY 14 | FOUND_VAR CryptoPP_FOUND 15 | VERSION_VAR CryptoPP_VERSION_STRING) 16 | 17 | if(CryptoPP_FOUND AND NOT TARGET CryptoPP::CryptoPP) 18 | add_library(CryptoPP::CryptoPP UNKNOWN IMPORTED) 19 | set_target_properties(CryptoPP::CryptoPP PROPERTIES 20 | IMPORTED_LOCATION "${CryptoPP_LIBRARY}" 21 | INTERFACE_INCLUDE_DIRECTORIES "${CryptoPP_INCLUDE_DIR}") 22 | endif() 23 | 24 | mark_as_advanced(CryptoPP_INCLUDE_DIR CryptoPP_LIBRARY) 25 | set(CryptoPP_INCLUDE_DIRS ${CryptoPP_INCLUDE_DIR}) 26 | set(CryptoPP_LIBRARIES ${CryptoPP_LIBRARY}) 27 | -------------------------------------------------------------------------------- /cmake/Modules/Findsodium.cmake: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2014 Thomas Heller 2 | # Copyright (c) 2013 Jeroen Habraken 3 | # 4 | # Distributed under the Boost Software License, Version 1.0. (See accompanying 5 | # file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | find_package(PkgConfig) 8 | pkg_check_modules(PC_SODIUM QUIET sodium) 9 | 10 | find_path(SODIUM_INCLUDE_DIR sodium.h 11 | HINTS 12 | ${SODIUM_ROOT} ENV SODIUM_ROOT 13 | ${PC_SODIUM_INCLUDEDIR} 14 | ${PC_SODIUM_INCLUDE_DIRS} 15 | PATH_SUFFIXES include src/libsodium/include) 16 | 17 | find_library(SODIUM_LIBRARY NAMES sodium libsodium 18 | HINTS 19 | ${SODIUM_ROOT} ENV SODIUM_ROOT 20 | ${PC_SODIUM_LIBDIR} 21 | ${PC_SODIUM_LIBRARY_DIRS} 22 | PATH_SUFFIXES lib lib64 src/libsodium/.libs) 23 | 24 | set(SODIUM_LIBRARIES ${SODIUM_LIBRARY}) 25 | set(SODIUM_INCLUDE_DIRS ${SODIUM_INCLUDE_DIR}) 26 | 27 | find_package_handle_standard_args(Sodium DEFAULT_MSG 28 | SODIUM_LIBRARY SODIUM_INCLUDE_DIR) 29 | 30 | get_property(_type CACHE SODIUM_ROOT PROPERTY TYPE) 31 | if(_type) 32 | set_property(CACHE SODIUM_ROOT PROPERTY ADVANCED 1) 33 | if("x${_type}" STREQUAL "xUNINITIALIZED") 34 | set_property(CACHE SODIUM_ROOT PROPERTY TYPE PATH) 35 | endif() 36 | endif() 37 | 38 | mark_as_advanced(SODIUM_ROOT SODIUM_LIBRARY SODIUM_INCLUDE_DIR) 39 | 40 | -------------------------------------------------------------------------------- /cmake/pgp-packet-config.cmake: -------------------------------------------------------------------------------- 1 | include("${CMAKE_CURRENT_LIST_DIR}/pgp-packet-targets.cmake") 2 | 3 | # set module path 4 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_LIST_DIR}/Modules/) 5 | 6 | # find boost and sodium 7 | find_package(Boost REQUIRED) 8 | find_package(sodium 1.0.16 REQUIRED) 9 | 10 | # first try to find CryptoPP built using CMake 11 | find_package(cryptopp CONFIG QUIET) 12 | 13 | # if this didn't work, use our built-in search tool 14 | if (NOT TARGET cryptopp-static) 15 | find_package(CryptoPP REQUIRED) 16 | endif() 17 | -------------------------------------------------------------------------------- /cmake/tests/std_span_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() { 5 | std::span s1{"test"}; 6 | std::span s2{std::array{42, 43, 44}}; 7 | } 8 | -------------------------------------------------------------------------------- /cmake/tests/std_variant_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void okassert(bool ok, const char *description) { 6 | if (!ok) { 7 | std::cerr << "FAIL: " << description << std::endl; 8 | exit(1); 9 | } 10 | } 11 | 12 | int main() { 13 | std::variant v{std::in_place_type_t(), 'a'}; 14 | 15 | okassert(std::get(v) == 'a', "std::get not working"); 16 | 17 | bool error_thrown = false; 18 | try { std::get(v); } 19 | catch (std::bad_variant_access &) { error_thrown = true; } 20 | okassert(error_thrown, "std::get should throw error"); 21 | 22 | std::visit([](auto &&value) { 23 | okassert(value == 'a', "std::visit not working"); 24 | }, v); 25 | } 26 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13.0) 2 | 3 | project(pgp-packet-examples 4 | VERSION 0.0.1 5 | LANGUAGES CXX) 6 | 7 | find_package(pgp-packet REQUIRED) 8 | 9 | add_executable(create_simple_packet create_simple_packet.cpp) 10 | target_link_libraries(create_simple_packet pgp-packet) 11 | 12 | add_executable(encoding_and_decoding encoding_and_decoding.cpp) 13 | target_link_libraries(encoding_and_decoding pgp-packet) 14 | 15 | add_executable(key_from_raw_data key_from_raw_data.cpp) 16 | target_link_libraries(key_from_raw_data pgp-packet) 17 | -------------------------------------------------------------------------------- /examples/create_simple_packet.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | // initialize libsodium 8 | if (sodium_init() == -1) { 9 | return 1; 10 | } 11 | 12 | // when constructing a packet we must specify the body type that 13 | // is to be contained within the packet; the constructor for the 14 | // packet uses the same pattern a regular std::variant uses when 15 | // forwarding constructors. the first - unnamed - parameter sets 16 | // the alternative to construct inside the variant, while others 17 | // get forwarded to the constructor of the selected alternative. 18 | // 19 | // since a user_id packet has a constructor using an std::string 20 | // we can construct a user_id packet like this: 21 | pgp::packet packet{ 22 | pgp::in_place_type_t{}, 23 | std::string{ "Anne Onymous " } 24 | }; 25 | 26 | // packets have a tag function, which returns an enum indicating 27 | // the type of packet contained inside the body. there is a free 28 | // function called packet_tag_description which can be used when 29 | // debugging or otherwise creating a description of a packet. 30 | std::cout 31 | << "Packet type: " 32 | << pgp::packet_tag_description(packet.tag()) 33 | << std::endl; 34 | 35 | // besides getting the packet type, using the tag() function, it 36 | // is also possible to get to the body of the packet through the 37 | // body() function of the packet. since this is simply a variant 38 | // as provided by the STL, it can either be visit()'ed (whenever 39 | // writing generic code), or an explicit std::get can be done to 40 | // retrieve a specific type of body - which could throw an error 41 | // if the body is of a different type than the one requested. in 42 | // this case we are certain that the body will contain a user_id 43 | auto &body = pgp::get(packet.body()); 44 | 45 | // now we have access to the user_id body, which provides simple 46 | // getters for its relevant members. in this case, it's only the 47 | // id itself that is stored, which can be retrieved using the id 48 | // member function. 49 | std::cout 50 | << "Stored user id: " 51 | << body.id() 52 | << std::endl; 53 | 54 | return 0; 55 | } 56 | -------------------------------------------------------------------------------- /examples/encoding_and_decoding.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int main() 8 | { 9 | // initialize libsodium 10 | if (sodium_init() == -1) { 11 | return 1; 12 | } 13 | 14 | // create our simple user id packet 15 | pgp::packet packet{ 16 | pgp::in_place_type_t{}, 17 | std::string{ "Anne Onymous " } 18 | }; 19 | 20 | // create a buffer for storing the binary data - we allocate the 21 | // exact number of bytes the packet requests, and then we create 22 | // a range_encoder around it. the range_encoder works by writing 23 | // the raw data to a provided range of bytes, which must stay in 24 | // scope during the encoder operation. 25 | pgp::vector data; 26 | data.resize(packet.size()); 27 | 28 | // write out the packet data to the given buffer 29 | packet.encode(pgp::range_encoder{ data }); 30 | 31 | // now create a decoder to decode the freshly filled data buffer 32 | // and use this decoder to create a second packet containing the 33 | // same user id body with the exact same data 34 | pgp::decoder decoder{ data }; 35 | pgp::packet copied_packet{ decoder }; 36 | 37 | // the packets should be identical 38 | assert(packet == copied_packet); 39 | 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /include/allocator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | namespace pgp { 9 | 10 | /** 11 | * Class for securely allocating and deallocating 12 | * memory. Memory is prevented from being paged out 13 | * and guard pages are placed right before and after 14 | * the allocated memory to detect invalid access. 15 | */ 16 | template 17 | class allocator 18 | { 19 | public: 20 | /** 21 | * Type aliases 22 | */ 23 | using pointer = T*; 24 | using const_pointer = const T*; 25 | using value_type = T; 26 | 27 | /** 28 | * Constructor 29 | */ 30 | allocator() = default; 31 | 32 | /** 33 | * Allocate memory for zero or more instances 34 | * of `value_type`. The instances will not be 35 | * initialized, the memory is initialized with 36 | * 0xdb for security reasons. 37 | * 38 | * @param count Number of elements to allocate memory for 39 | * @return Pointer to the allocated memory 40 | * @throws std::bad_alloc 41 | */ 42 | pointer allocate(size_t count) 43 | { 44 | // allocate secure memory 45 | auto *result = sodium_allocarray(count, sizeof(aligned_t)); 46 | 47 | // check whether we got a valid pointer 48 | if (result == nullptr) { 49 | // memory allocation failed 50 | throw std::bad_alloc{}; 51 | } 52 | 53 | // cast to the requested type 54 | return static_cast(result); 55 | } 56 | 57 | /** 58 | * Free memory previously allocated using 59 | * this allocator. Does not destroy instances. 60 | * 61 | * Guard pages are checked, and memory is cleared 62 | * before release. Upon failure, no exceptions are 63 | * thrown, the program simply terminates. 64 | * 65 | * @param address The address to free 66 | */ 67 | void deallocate(pointer address, size_t) noexcept 68 | { 69 | // free the memory 70 | sodium_free(address); 71 | } 72 | 73 | /** 74 | * Are we logically the same as the other 75 | * given allocator? 76 | * 77 | * @return The result of the comparison 78 | */ 79 | constexpr bool operator==(const allocator &) noexcept { return true; } 80 | constexpr bool operator!=(const allocator &) noexcept { return false; } 81 | private: 82 | // alias for an aligned buffer capable of holding one instance of value_type 83 | using aligned_t = std::aligned_storage_t; 84 | }; 85 | 86 | } 87 | -------------------------------------------------------------------------------- /include/compression_algorithm.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | 6 | namespace pgp { 7 | 8 | /** 9 | * The available compression algorithms 10 | */ 11 | enum class compression_algorithm : uint8_t 12 | { 13 | uncompressed = 0, 14 | zip = 1, 15 | zlib = 2, 16 | bzip2 = 3, 17 | }; 18 | 19 | /** 20 | * Get a description of the compression algorithm 21 | * 22 | * @param algorithm The algorithm to get a description for 23 | * @return The description of the algorithm 24 | */ 25 | constexpr boost::string_view compression_algorithm_description(compression_algorithm algorithm) noexcept 26 | { 27 | // check the given algorithm 28 | switch (algorithm) { 29 | case compression_algorithm::uncompressed: return "uncompressed"; 30 | case compression_algorithm::zip: return "zip"; 31 | case compression_algorithm::zlib: return "zlib"; 32 | case compression_algorithm::bzip2: return "bzip2"; 33 | } 34 | 35 | // unknown algorithm found 36 | return "unknown compression algorithm"; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /include/decoder_traits.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "util/span.h" 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | namespace pgp { 10 | 11 | /** 12 | * Fallback struct for types not qualifying as 13 | * a decoder type 14 | */ 15 | template 16 | struct is_decoder : std::false_type {}; 17 | 18 | /** 19 | * Structure matching on valid decoders 20 | */ 21 | template 22 | struct is_decoder().splice(0)), // note: cannot check for a valid decoder return type, template recursion 24 | std::enable_if_t().empty()) >>, 25 | std::enable_if_t().size()) >>, 26 | std::enable_if_t().peek_bits(0)) >>, 27 | std::enable_if_t().extract_bits(0)) >>, 28 | std::enable_if_t().template peek_number()) >>, 29 | std::enable_if_t().template extract_number()) >>, 30 | std::enable_if_t, decltype(std::declval().template extract_blob(0)) >> 31 | >> : std::true_type {}; 32 | 33 | /** 34 | * Value alias for the decoder structs 35 | */ 36 | template 37 | constexpr bool is_decoder_v = is_decoder::value; 38 | 39 | } 40 | -------------------------------------------------------------------------------- /include/dsa_secret_key.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "decoder_traits.h" 6 | #include "multiprecision_integer.h" 7 | 8 | 9 | namespace pgp { 10 | 11 | /** 12 | * Class holding a DSA secret key 13 | */ 14 | class dsa_secret_key 15 | { 16 | public: 17 | /** 18 | * Constructor 19 | * 20 | * @param parser The decoder to parse the data 21 | */ 22 | template >> 23 | explicit dsa_secret_key(decoder &parser) : 24 | _x{ parser } 25 | {} 26 | 27 | /** 28 | * Constructor 29 | * 30 | * @param x The secret exponent 31 | */ 32 | explicit dsa_secret_key(multiprecision_integer x) noexcept; 33 | 34 | /** 35 | * Comparison operators 36 | * 37 | * @param other The object to compare with 38 | */ 39 | bool operator==(const dsa_secret_key &other) const noexcept; 40 | bool operator!=(const dsa_secret_key &other) const noexcept; 41 | 42 | /** 43 | * Determine the size used in encoded format 44 | * @return The number of bytes used for encoded storage 45 | */ 46 | size_t size() const noexcept; 47 | 48 | /** 49 | * Retrieve the secret exponent 50 | * 51 | * @return The secret exponent x 52 | */ 53 | const multiprecision_integer &x() const noexcept; 54 | 55 | /** 56 | * Write the data to an encoder 57 | * 58 | * @param writer The encoder to write to 59 | * @throws std::out_of_range, std::range_error 60 | */ 61 | template 62 | void encode(encoder_t&& writer) const 63 | { 64 | // encode the secret exponent 65 | _x.encode(writer); 66 | } 67 | private: 68 | multiprecision_integer _x; // the secret exponent x 69 | }; 70 | 71 | } 72 | -------------------------------------------------------------------------------- /include/dsa_signature.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "decoder_traits.h" 6 | #include "dsa_signature_encoder.h" 7 | #include "multiprecision_integer.h" 8 | 9 | 10 | namespace pgp { 11 | 12 | /** 13 | * Class for holding DSA signature-specific fields 14 | */ 15 | class dsa_signature 16 | { 17 | public: 18 | using encoder_t = dsa_signature_encoder; 19 | 20 | /** 21 | * Constructor 22 | * 23 | * @param parser The decoder to parse the data 24 | */ 25 | template >> 26 | explicit dsa_signature(decoder &parser) : 27 | _r{ parser }, 28 | _s{ parser } 29 | {} 30 | 31 | /** 32 | * Constructor 33 | * 34 | * @param r The DSA r value 35 | * @param s The DSA s value 36 | */ 37 | dsa_signature(multiprecision_integer r, multiprecision_integer s) noexcept; 38 | 39 | /** 40 | * Comparison operators 41 | * 42 | * @param other The object to compare with 43 | */ 44 | bool operator==(const dsa_signature &other) const noexcept; 45 | bool operator!=(const dsa_signature &other) const noexcept; 46 | 47 | /** 48 | * Determine the size used in encoded format 49 | * @return The number of bytes used for encoded storage 50 | */ 51 | size_t size() const noexcept; 52 | 53 | /** 54 | * Retrieve the DSA r value 55 | * 56 | * @return The r value 57 | */ 58 | const multiprecision_integer &r() const noexcept; 59 | 60 | /** 61 | * Retrieve the DSA s value 62 | * 63 | * @return The s value 64 | */ 65 | const multiprecision_integer &s() const noexcept; 66 | 67 | /** 68 | * Write the data to an encoder 69 | * 70 | * @param writer The encoder to write to 71 | * @throws std::out_of_range, std::range_error 72 | */ 73 | template 74 | void encode(encoder_t&& writer) const 75 | { 76 | // encode both values 77 | _r.encode(writer); 78 | _s.encode(writer); 79 | } 80 | private: 81 | multiprecision_integer _r; // the DSA r value 82 | multiprecision_integer _s; // the DSA s value 83 | }; 84 | 85 | } 86 | -------------------------------------------------------------------------------- /include/dsa_signature_encoder.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "basic_key.h" 6 | #include "hash_encoder.h" 7 | #include "multiprecision_integer.h" 8 | #include "packet_tag.h" 9 | #include "secret_key.h" 10 | 11 | 12 | namespace pgp { 13 | 14 | /** 15 | * An encoder to produce DSA signatures 16 | */ 17 | class dsa_signature_encoder : public sha256_encoder 18 | { 19 | public: 20 | /** 21 | * Create the encoder 22 | */ 23 | template 24 | explicit dsa_signature_encoder(basic_key>) 25 | { 26 | // TODO 27 | throw std::runtime_error{ "Generating DSA signatures is not yet implemented" }; 28 | } 29 | 30 | /** 31 | * Make the signature 32 | * 33 | * @return Tuple of the r and s parameters for the DSA signature 34 | */ 35 | std::tuple finalize(); 36 | }; 37 | 38 | } 39 | -------------------------------------------------------------------------------- /include/ecdh_secret_key.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "decoder_traits.h" 6 | #include "multiprecision_integer.h" 7 | 8 | 9 | namespace pgp { 10 | 11 | /** 12 | * Class for working with an ECDH secret key 13 | */ 14 | class ecdh_secret_key 15 | { 16 | public: 17 | /** 18 | * Constructor 19 | * 20 | * @param parser The decoder to parse the data 21 | */ 22 | template >> 23 | explicit ecdh_secret_key(decoder &parser) : 24 | _k{ parser } 25 | {} 26 | 27 | /** 28 | * Constructor 29 | * 30 | * @param k The secret scalar for the public point 31 | */ 32 | explicit ecdh_secret_key(multiprecision_integer k) noexcept; 33 | 34 | /** 35 | * Comparison operators 36 | * 37 | * @param other The object to compare with 38 | */ 39 | bool operator==(const ecdh_secret_key &other) const noexcept; 40 | bool operator!=(const ecdh_secret_key &other) const noexcept; 41 | 42 | /** 43 | * Determine the size used in encoded format 44 | * @return The number of bytes used for encoded storage 45 | */ 46 | size_t size() const noexcept; 47 | 48 | /** 49 | * Retrieve the secret scalar 50 | * 51 | * @return The secret scalar for the public point 52 | */ 53 | const multiprecision_integer &k() const noexcept; 54 | 55 | /** 56 | * Write the data to an encoder 57 | * 58 | * @param writer The encoder to write to 59 | * @throws std::out_of_range, std::range_error 60 | */ 61 | template 62 | void encode(encoder_t&& writer) const 63 | { 64 | // encode the secret scalar 65 | _k.encode(writer); 66 | } 67 | private: 68 | multiprecision_integer _k; // the secret scalar for the public point 69 | }; 70 | 71 | } 72 | -------------------------------------------------------------------------------- /include/ecdsa_public_key.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "decoder_traits.h" 6 | #include "curve_oid.h" 7 | #include "multiprecision_integer.h" 8 | 9 | 10 | namespace pgp { 11 | 12 | // Forward declaration to prevent header dependency cycles 13 | class ecdsa_signature; 14 | 15 | /** 16 | * Class for holding an ECDSA public key 17 | */ 18 | class ecdsa_public_key 19 | { 20 | public: 21 | /** 22 | * The public key type we belong to 23 | */ 24 | using public_key_t = ecdsa_public_key; 25 | 26 | /** 27 | * The signature type corresponding to this key type 28 | */ 29 | using signature_t = ecdsa_signature; 30 | 31 | /** 32 | * Constructor 33 | * 34 | * @param parser The decoder to parse the data 35 | */ 36 | template >> 37 | explicit ecdsa_public_key(decoder &parser) : 38 | _curve{ parser }, 39 | _Q{ parser } 40 | {} 41 | 42 | /** 43 | * Constructor 44 | * 45 | * @param curve The curve object identifier 46 | * @param Q The public curve point Q 47 | */ 48 | ecdsa_public_key(curve_oid curve, multiprecision_integer Q) noexcept; 49 | 50 | /** 51 | * Comparison operators 52 | * 53 | * @param other The object to compare with 54 | */ 55 | bool operator==(const ecdsa_public_key &other) const noexcept; 56 | bool operator!=(const ecdsa_public_key &other) const noexcept; 57 | 58 | /** 59 | * Determine the size used in encoded format 60 | * @return The number of bytes used for encoded storage 61 | */ 62 | size_t size() const noexcept; 63 | 64 | /** 65 | * Retrieve the curve object identifier 66 | * 67 | * @return The curve object identifier 68 | */ 69 | const curve_oid &curve() const noexcept; 70 | 71 | /** 72 | * Retrieve the public curve point Q 73 | * 74 | * @return The public curve point Q, in compressed format 75 | */ 76 | const multiprecision_integer &Q() const noexcept; 77 | 78 | /** 79 | * Write the data to an encoder 80 | * 81 | * @param writer The encoder to write to 82 | * @throws std::out_of_range, std::range_error 83 | */ 84 | template 85 | void encode(encoder_t&& writer) const 86 | { 87 | // encode the curve id and public point 88 | _curve.encode(writer); 89 | _Q.encode(writer); 90 | } 91 | private: 92 | curve_oid _curve; // the curve object identifier 93 | multiprecision_integer _Q; // the public curve point 94 | }; 95 | 96 | } 97 | -------------------------------------------------------------------------------- /include/ecdsa_secret_key.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "decoder_traits.h" 6 | #include "multiprecision_integer.h" 7 | 8 | 9 | namespace pgp { 10 | 11 | /** 12 | * Class for working with an ECDSA secret key 13 | */ 14 | class ecdsa_secret_key 15 | { 16 | public: 17 | /** 18 | * Constructor 19 | * 20 | * @param parser The decoder to parse the data 21 | */ 22 | template >> 23 | explicit ecdsa_secret_key(decoder &parser) : 24 | _k{ parser } 25 | {} 26 | 27 | /** 28 | * Constructor 29 | * 30 | * @param k The secret scalar for the public point 31 | */ 32 | explicit ecdsa_secret_key(multiprecision_integer k) noexcept; 33 | 34 | /** 35 | * Comparison operators 36 | * 37 | * @param other The object to compare with 38 | */ 39 | bool operator==(const ecdsa_secret_key &other) const noexcept; 40 | bool operator!=(const ecdsa_secret_key &other) const noexcept; 41 | 42 | /** 43 | * Determine the size used in encoded format 44 | * @return The number of bytes used for encoded storage 45 | */ 46 | size_t size() const noexcept; 47 | 48 | /** 49 | * Retrieve the secret scalar 50 | * 51 | * @return The secret scalar for the public point 52 | */ 53 | const multiprecision_integer &k() const noexcept; 54 | 55 | /** 56 | * Write the data to an encoder 57 | * 58 | * @param writer The encoder to write to 59 | * @throws std::out_of_range, std::range_error 60 | */ 61 | template 62 | void encode(encoder_t&& writer) const 63 | { 64 | // encode the secret key 65 | _k.encode(writer); 66 | } 67 | private: 68 | multiprecision_integer _k; // the secret scalar for the public point 69 | }; 70 | 71 | } 72 | -------------------------------------------------------------------------------- /include/ecdsa_signature.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "decoder_traits.h" 6 | #include "ecdsa_signature_encoder.h" 7 | #include "multiprecision_integer.h" 8 | 9 | 10 | namespace pgp { 11 | 12 | /** 13 | * Class for holding ECDSA signature-specific fields 14 | */ 15 | class ecdsa_signature 16 | { 17 | public: 18 | using encoder_t = ecdsa_signature_encoder; 19 | 20 | /** 21 | * Constructor 22 | * 23 | * @param parser The decoder to parse the data 24 | */ 25 | template >> 26 | explicit ecdsa_signature(decoder &parser) : 27 | _r{ parser }, 28 | _s{ parser } 29 | {} 30 | 31 | /** 32 | * Constructor 33 | * 34 | * @param r The ECDSA r value 35 | * @param s The ECDSA s value 36 | */ 37 | ecdsa_signature(multiprecision_integer r, multiprecision_integer s) noexcept; 38 | 39 | /** 40 | * Comparison operators 41 | * 42 | * @param other The object to compare with 43 | */ 44 | bool operator==(const ecdsa_signature &other) const noexcept; 45 | bool operator!=(const ecdsa_signature &other) const noexcept; 46 | 47 | /** 48 | * Determine the size used in encoded format 49 | * @return The number of bytes used for encoded storage 50 | */ 51 | size_t size() const noexcept; 52 | 53 | /** 54 | * Retrieve the ECDSA r value 55 | * 56 | * @return The r value 57 | */ 58 | const multiprecision_integer &r() const noexcept; 59 | 60 | /** 61 | * Retrieve the ECDSA s value 62 | * 63 | * @return The s value 64 | */ 65 | const multiprecision_integer &s() const noexcept; 66 | 67 | /** 68 | * Write the data to an encoder 69 | * 70 | * @param writer The encoder to write to 71 | * @throws std::out_of_range, std::range_error 72 | */ 73 | template 74 | void encode(encoder_t&& writer) const 75 | { 76 | // encode both values 77 | _r.encode(writer); 78 | _s.encode(writer); 79 | } 80 | private: 81 | multiprecision_integer _r; // the ECDSA r value 82 | multiprecision_integer _s; // the ECDSA s value 83 | }; 84 | 85 | } 86 | -------------------------------------------------------------------------------- /include/ecdsa_signature_encoder.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "basic_key.h" 5 | #include "basic_secret_key.h" 6 | #include "ecdsa_public_key.h" 7 | #include "ecdsa_secret_key.h" 8 | #include "hash_encoder.h" 9 | #include "multiprecision_integer.h" 10 | #include "packet_tag.h" 11 | #include "secret_key.h" 12 | 13 | 14 | namespace pgp { 15 | 16 | /** 17 | * An encoder to produce ECDSA signatures 18 | */ 19 | class ecdsa_signature_encoder : public sha256_encoder 20 | { 21 | public: 22 | /** 23 | * Create the encoder 24 | * 25 | * @param key The secret key with which to make the signature 26 | */ 27 | template 28 | explicit ecdsa_signature_encoder(const basic_key> &key) noexcept : 29 | ecdsa_key{get>(key.key())} 30 | {} 31 | 32 | /** 33 | * Make the signature 34 | * 35 | * @return Tuple of the r and s parameters for the ECDSA signature 36 | */ 37 | std::tuple finalize(); 38 | 39 | private: 40 | /** 41 | * The key with which to make the signature 42 | */ 43 | basic_secret_key ecdsa_key; 44 | }; 45 | 46 | } 47 | -------------------------------------------------------------------------------- /include/eddsa_public_key.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "decoder_traits.h" 6 | #include "curve_oid.h" 7 | #include "multiprecision_integer.h" 8 | 9 | 10 | namespace pgp { 11 | 12 | // Forward declaration to prevent header dependency cycles 13 | class eddsa_signature; 14 | 15 | /** 16 | * Class for holding an EdDSA public key 17 | */ 18 | class eddsa_public_key 19 | { 20 | public: 21 | /** 22 | * The public key type we belong to 23 | */ 24 | using public_key_t = eddsa_public_key; 25 | 26 | /** 27 | * The signature type corresponding to this key type 28 | */ 29 | using signature_t = eddsa_signature; 30 | 31 | /** 32 | * Constructor 33 | * 34 | * @param parser The decoder to parse the data 35 | */ 36 | template >> 37 | explicit eddsa_public_key(decoder &parser) : 38 | _curve{ parser }, 39 | _Q{ parser } 40 | {} 41 | 42 | /** 43 | * Constructor 44 | * 45 | * @param curve The curve object identifier 46 | * @param Q The public curve point Q 47 | */ 48 | eddsa_public_key(curve_oid curve, multiprecision_integer Q) noexcept; 49 | 50 | /** 51 | * Comparison operators 52 | * 53 | * @param other The object to compare with 54 | */ 55 | bool operator==(const eddsa_public_key &other) const noexcept; 56 | bool operator!=(const eddsa_public_key &other) const noexcept; 57 | 58 | /** 59 | * Determine the size used in encoded format 60 | * @return The number of bytes used for encoded storage 61 | */ 62 | size_t size() const noexcept; 63 | 64 | /** 65 | * Retrieve the curve object identifier 66 | * 67 | * @return The curve object identifier 68 | */ 69 | const curve_oid &curve() const noexcept; 70 | 71 | /** 72 | * Retrieve the public curve point Q 73 | * 74 | * @return The public curve point Q, in compressed format 75 | */ 76 | const multiprecision_integer &Q() const noexcept; 77 | 78 | /** 79 | * Write the data to an encoder 80 | * 81 | * @param writer The encoder to write to 82 | * @throws std::out_of_range, std::range_error 83 | */ 84 | template 85 | void encode(encoder_t&& writer) const 86 | { 87 | // encode the curve id and public point 88 | _curve.encode(writer); 89 | _Q.encode(writer); 90 | } 91 | private: 92 | curve_oid _curve; // the curve object identifier 93 | multiprecision_integer _Q; // the public curve point 94 | }; 95 | 96 | } 97 | -------------------------------------------------------------------------------- /include/eddsa_secret_key.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "decoder_traits.h" 6 | #include "multiprecision_integer.h" 7 | 8 | 9 | namespace pgp { 10 | 11 | /** 12 | * Class for working with an EdDSA secret key 13 | */ 14 | class eddsa_secret_key 15 | { 16 | public: 17 | /** 18 | * Constructor 19 | * 20 | * @param parser The decoder to parse the data 21 | */ 22 | template >> 23 | explicit eddsa_secret_key(decoder &parser) : 24 | _k{ parser } 25 | {} 26 | 27 | /** 28 | * Constructor 29 | * 30 | * @param k The secret scalar for the public point 31 | */ 32 | explicit eddsa_secret_key(multiprecision_integer k) noexcept; 33 | 34 | /** 35 | * Comparison operators 36 | * 37 | * @param other The object to compare with 38 | */ 39 | bool operator==(const eddsa_secret_key &other) const noexcept; 40 | bool operator!=(const eddsa_secret_key &other) const noexcept; 41 | 42 | /** 43 | * Determine the size used in encoded format 44 | * @return The number of bytes used for encoded storage 45 | */ 46 | size_t size() const noexcept; 47 | 48 | /** 49 | * Retrieve the secret scalar 50 | * 51 | * @return The secret scalar for the public point 52 | */ 53 | const multiprecision_integer &k() const noexcept; 54 | 55 | /** 56 | * Write the data to an encoder 57 | * 58 | * @param writer The encoder to write to 59 | * @throws std::out_of_range, std::range_error 60 | */ 61 | template 62 | void encode(encoder_t&& writer) const 63 | { 64 | // encode the secret key 65 | _k.encode(writer); 66 | } 67 | private: 68 | multiprecision_integer _k; // the secret scalar for the public point 69 | }; 70 | 71 | } 72 | -------------------------------------------------------------------------------- /include/eddsa_signature.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "decoder_traits.h" 6 | #include "eddsa_signature_encoder.h" 7 | #include "multiprecision_integer.h" 8 | 9 | 10 | namespace pgp { 11 | 12 | /** 13 | * Class for holding EdDSA signature-specific fields 14 | */ 15 | class eddsa_signature 16 | { 17 | public: 18 | using encoder_t = eddsa_signature_encoder; 19 | 20 | /** 21 | * Constructor 22 | * 23 | * @param parser The decoder to parse the data 24 | */ 25 | template >> 26 | explicit eddsa_signature(decoder &parser) : 27 | _r{ parser }, 28 | _s{ parser } 29 | {} 30 | 31 | /** 32 | * Constructor 33 | * 34 | * @param r The EdDSA r value 35 | * @param s The EdDSA s value 36 | */ 37 | eddsa_signature(multiprecision_integer r, multiprecision_integer s) noexcept; 38 | 39 | /** 40 | * Comparison operators 41 | * 42 | * @param other The object to compare with 43 | */ 44 | bool operator==(const eddsa_signature &other) const noexcept; 45 | bool operator!=(const eddsa_signature &other) const noexcept; 46 | 47 | /** 48 | * Determine the size used in encoded format 49 | * @return The number of bytes used for encoded storage 50 | */ 51 | size_t size() const noexcept; 52 | 53 | /** 54 | * Retrieve the EdDSA r value 55 | * 56 | * @return The r value 57 | */ 58 | const multiprecision_integer &r() const noexcept; 59 | 60 | /** 61 | * Retrieve the EdDSA s value 62 | * 63 | * @return The s value 64 | */ 65 | const multiprecision_integer &s() const noexcept; 66 | 67 | /** 68 | * Write the data to an encoder 69 | * 70 | * @param writer The encoder to write to 71 | * @throws std::out_of_range, std::range_error 72 | */ 73 | template 74 | void encode(encoder_t&& writer) const 75 | { 76 | // encode both values 77 | _r.encode(writer); 78 | _s.encode(writer); 79 | } 80 | private: 81 | multiprecision_integer _r; // the EdDSA r value 82 | multiprecision_integer _s; // the EdDSA s value 83 | }; 84 | 85 | } 86 | -------------------------------------------------------------------------------- /include/eddsa_signature_encoder.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "basic_key.h" 5 | #include "basic_secret_key.h" 6 | #include "eddsa_public_key.h" 7 | #include "eddsa_secret_key.h" 8 | #include "hash_encoder.h" 9 | #include "multiprecision_integer.h" 10 | #include "packet_tag.h" 11 | #include "secret_key.h" 12 | 13 | 14 | namespace pgp { 15 | 16 | /** 17 | * An encoder to produce EDDSA signatures 18 | */ 19 | class eddsa_signature_encoder : public sha256_encoder 20 | { 21 | public: 22 | /** 23 | * Create the encoder 24 | * 25 | * @param key The secret key with which to make the signature 26 | */ 27 | template 28 | explicit eddsa_signature_encoder(const basic_key> &key) noexcept : 29 | eddsa_key{get>(key.key())} 30 | {} 31 | 32 | /** 33 | * Make the signature 34 | * 35 | * @return Tuple of the r and s parameters for the EDDSA signature 36 | */ 37 | std::tuple finalize() noexcept; 38 | 39 | private: 40 | /** 41 | * The key with which to make the signature 42 | */ 43 | basic_secret_key eddsa_key; 44 | }; 45 | 46 | } 47 | -------------------------------------------------------------------------------- /include/elgamal_public_key.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "decoder_traits.h" 6 | #include "multiprecision_integer.h" 7 | 8 | 9 | namespace pgp { 10 | 11 | // Forward declaration to prevent header dependency cycles 12 | class unknown_signature; 13 | 14 | /** 15 | * Class for holding an elgamal key 16 | */ 17 | class elgamal_public_key 18 | { 19 | public: 20 | /** 21 | * The public key type we belong to 22 | */ 23 | using public_key_t = elgamal_public_key; 24 | 25 | /** 26 | * The signature type corresponding to this key type 27 | * 28 | * TODO: no elgamal signature class yet 29 | */ 30 | using signature_t = unknown_signature; 31 | 32 | /** 33 | * Constructor 34 | * 35 | * @param parser The decoder to parse the data from 36 | */ 37 | template >> 38 | explicit elgamal_public_key(decoder &parser) : 39 | _p{ parser }, 40 | _g{ parser }, 41 | _y{ parser } 42 | {} 43 | 44 | /** 45 | * Constructor 46 | * 47 | * @param p The prime p 48 | * @param g The group generator g 49 | * @param y The public key value: g**x mod p 50 | */ 51 | elgamal_public_key(multiprecision_integer p, multiprecision_integer g, multiprecision_integer y) noexcept; 52 | 53 | /** 54 | * Comparison operators 55 | * 56 | * @param other The object to compare with 57 | */ 58 | bool operator==(const elgamal_public_key &other) const noexcept; 59 | bool operator!=(const elgamal_public_key &other) const noexcept; 60 | 61 | /** 62 | * Determine the size used in encoded format 63 | * @return The number of bytes used for encoded storage 64 | */ 65 | size_t size() const noexcept; 66 | 67 | /** 68 | * Retrieve the the prime 69 | * 70 | * @return The prime p 71 | */ 72 | const multiprecision_integer &p() const noexcept; 73 | 74 | /** 75 | * Retrieve the group generator g 76 | * 77 | * @return The group generator g 78 | */ 79 | const multiprecision_integer &g() const noexcept; 80 | 81 | /** 82 | * Retrieve the public key value 83 | * 84 | * @return The public key value 85 | */ 86 | const multiprecision_integer &y() const noexcept; 87 | 88 | /** 89 | * Write the data to an encoder 90 | * 91 | * @param writer The encoder to write to 92 | * @throws std::out_of_range, std::range_error 93 | */ 94 | template 95 | void encode(encoder_t&& writer) const 96 | { 97 | // encode all the components 98 | _p.encode(writer); 99 | _g.encode(writer); 100 | _y.encode(writer); 101 | } 102 | private: 103 | multiprecision_integer _p; // the prime p 104 | multiprecision_integer _g; // the group generator g 105 | multiprecision_integer _y; // the public key value: g**x mod p 106 | }; 107 | 108 | } 109 | -------------------------------------------------------------------------------- /include/elgamal_secret_key.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "decoder_traits.h" 6 | #include "multiprecision_integer.h" 7 | 8 | 9 | namespace pgp { 10 | 11 | /** 12 | * Class for holding an elgamal secret key 13 | */ 14 | class elgamal_secret_key 15 | { 16 | public: 17 | /** 18 | * Constructor 19 | * 20 | * @param parser The decoder to parse the data from 21 | */ 22 | template >> 23 | explicit elgamal_secret_key(decoder &parser) : 24 | _x{ parser } 25 | {} 26 | 27 | /** 28 | * Constructor 29 | * 30 | * @param x The secret exponent x 31 | */ 32 | explicit elgamal_secret_key(multiprecision_integer x) noexcept; 33 | 34 | /** 35 | * Comparison operators 36 | * 37 | * @param other The object to compare with 38 | */ 39 | bool operator==(const elgamal_secret_key &other) const noexcept; 40 | bool operator!=(const elgamal_secret_key &other) const noexcept; 41 | 42 | /** 43 | * Determine the size used in encoded format 44 | * @return The number of bytes used for encoded storage 45 | */ 46 | size_t size() const noexcept; 47 | 48 | /** 49 | * Retrieve the secret exponent 50 | * 51 | * @return The secret exponent x 52 | */ 53 | const multiprecision_integer &x() const noexcept; 54 | 55 | /** 56 | * Write the data to an encoder 57 | * 58 | * @param writer The encoder to write to 59 | * @throws std::out_of_range, std::range_error 60 | */ 61 | template 62 | void encode(encoder_t&& writer) const 63 | { 64 | // encode the secret exponent 65 | _x.encode(writer); 66 | } 67 | private: 68 | multiprecision_integer _x; // the secret exponent x 69 | }; 70 | 71 | } 72 | -------------------------------------------------------------------------------- /include/expected_number.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "decoder_traits.h" 4 | 5 | 6 | namespace pgp { 7 | 8 | /** 9 | * Templated class to expect a specific number 10 | * in the encoded PGP data stream. 11 | */ 12 | template 13 | class expected_number 14 | { 15 | public: 16 | /** 17 | * Constructor 18 | */ 19 | constexpr expected_number() = default; 20 | 21 | /** 22 | * Constructor 23 | * 24 | * @param parser The decoder to parse the data 25 | * @throws std::range_error, std::out_of_range 26 | */ 27 | template >> 28 | explicit expected_number(decoder &parser) 29 | { 30 | // check whether the value is as expected 31 | if (parser.template extract_number() != number) { 32 | // invalid number was read 33 | throw std::range_error{ "A fixed number is outside of expected range" }; 34 | } 35 | } 36 | 37 | /** 38 | * Determine the size used in encoded format 39 | * @return The number of bytes used for encoded storage 40 | */ 41 | constexpr static size_t size() noexcept 42 | { 43 | // this is just the size of the number type 44 | return sizeof(T); 45 | } 46 | 47 | /** 48 | * The value of the number 49 | * @return The expected number 50 | */ 51 | constexpr T value() const noexcept 52 | { 53 | // return the expected value 54 | return number; 55 | } 56 | 57 | /** 58 | * Write the data to an encoder 59 | * 60 | * @param writer The encoder to write to 61 | * @throws std::out_of_range, std::range_error 62 | */ 63 | template 64 | void encode(encoder_t&& writer) const 65 | { 66 | // write out the number 67 | writer.push(value()); 68 | } 69 | }; 70 | 71 | } 72 | -------------------------------------------------------------------------------- /include/fixed_number.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "decoder_traits.h" 4 | #include 5 | #include 6 | 7 | 8 | namespace pgp { 9 | 10 | /** 11 | * Class implementing a number compatible with the 12 | * PGP RFC, using a fixed number of encoded octets 13 | */ 14 | template 15 | class fixed_number 16 | { 17 | public: 18 | /** 19 | * Constructor 20 | */ 21 | constexpr fixed_number() = default; 22 | 23 | /** 24 | * Constructor 25 | * 26 | * @param parser The decoder to parse the data 27 | */ 28 | template >> 29 | explicit fixed_number(decoder &&parser) : 30 | _value{ parser.template extract_number() } 31 | {} 32 | 33 | /** 34 | * Constructor 35 | * 36 | * @param value The value to hold 37 | */ 38 | constexpr explicit fixed_number(T value) noexcept : 39 | _value{ value } 40 | {} 41 | 42 | /** 43 | * Assignment operator 44 | * 45 | * @param parser The parser to assign the value from 46 | * @return self, for chaining 47 | */ 48 | template 49 | std::enable_if_t, fixed_number> &operator=(decoder &&parser) 50 | { 51 | // update value 52 | _value = parser.template extract_number(); 53 | 54 | // allow chaining 55 | return *this; 56 | } 57 | 58 | /** 59 | * Assignment operator 60 | * 61 | * @param value The value to assign 62 | * @return self, for chaining 63 | */ 64 | constexpr fixed_number &operator=(T value) noexcept 65 | { 66 | // update value 67 | _value = value; 68 | 69 | // allow chaining 70 | return *this; 71 | } 72 | 73 | /** 74 | * Determine the size used in encoded format 75 | * 76 | * @return The number of bytes used for encoded storage 77 | */ 78 | static constexpr size_t size() noexcept 79 | { 80 | // just use the size of the value 81 | return sizeof(T); 82 | } 83 | 84 | /** 85 | * Extract the stored value 86 | * 87 | * @return The stored value 88 | */ 89 | constexpr operator T() const noexcept 90 | { 91 | // return the stored value 92 | return _value; 93 | } 94 | 95 | /** 96 | * Write the data to an encoder 97 | * 98 | * @param writer The encoder to write to 99 | * @throws std::out_of_range, std::range_error 100 | */ 101 | template 102 | void encode(encoder_t&& writer) const 103 | { 104 | // write the number to the encoder 105 | writer.push(_value); 106 | } 107 | private: 108 | T _value{ 0 }; 109 | }; 110 | 111 | /** 112 | * Alias the commonly-used types 113 | */ 114 | using uint8 = fixed_number; 115 | using uint16 = fixed_number; 116 | using uint32 = fixed_number; 117 | 118 | } 119 | -------------------------------------------------------------------------------- /include/hash_algorithm.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | 6 | namespace pgp { 7 | 8 | /** 9 | * The available hashing algorithms 10 | */ 11 | enum class hash_algorithm : uint8_t 12 | { 13 | md5 = 1, 14 | sha1 = 2, 15 | ripemd160 = 3, 16 | sha256 = 8, 17 | sha384 = 9, 18 | sha512 = 10, 19 | sha224 = 11 20 | }; 21 | 22 | /** 23 | * Get a description of the hash algorithm 24 | * 25 | * @param algorithm The algorithm to get a description for 26 | * @return The description of the algorithm 27 | */ 28 | constexpr boost::string_view hash_algorithm_description(hash_algorithm algorithm) noexcept 29 | { 30 | // check the given algorithm 31 | switch (algorithm) { 32 | case hash_algorithm::md5: return "MD5"; 33 | case hash_algorithm::sha1: return "SHA1"; 34 | case hash_algorithm::ripemd160: return "RIPEMD160"; 35 | case hash_algorithm::sha256: return "SHA256"; 36 | case hash_algorithm::sha384: return "SHA384"; 37 | case hash_algorithm::sha512: return "SHA512"; 38 | case hash_algorithm::sha224: return "SHA224"; 39 | } 40 | 41 | // unknown hash algorithm found 42 | return "unknown hash algorithm"; 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /include/key_algorithm.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | 6 | namespace pgp { 7 | 8 | /** 9 | * The available algorithms for the key 10 | */ 11 | enum class key_algorithm : uint8_t 12 | { 13 | rsa_encrypt_or_sign = 1, 14 | rsa_encrypt_only = 2, 15 | rsa_sign_only = 3, 16 | elgamal_encrypt_only = 16, 17 | dsa = 17, 18 | ecdh = 18, 19 | ecdsa = 19, 20 | eddsa = 22 21 | }; 22 | 23 | /** 24 | * Get a description of the public key algorithm 25 | * 26 | * @param algorithm The algorithm to get a description for 27 | * @return The algorithm to describe 28 | */ 29 | constexpr boost::string_view key_algorithm_description(key_algorithm algorithm) noexcept 30 | { 31 | // check the provided algorithm 32 | switch (algorithm) { 33 | case key_algorithm::rsa_encrypt_or_sign: return "RSA (encrypt or sign)"; 34 | case key_algorithm::rsa_encrypt_only: return "RSA (encrypt only)"; 35 | case key_algorithm::rsa_sign_only: return "RSA (sign only)"; 36 | case key_algorithm::elgamal_encrypt_only: return "Elgamal (encrypt only)"; 37 | case key_algorithm::dsa: return "DSA"; 38 | case key_algorithm::ecdh: return "ECDH"; 39 | case key_algorithm::ecdsa: return "ECDSA"; 40 | case key_algorithm::eddsa: return "EdDSA"; 41 | } 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /include/key_flag.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | 6 | namespace pgp { 7 | 8 | /** 9 | * Enum containing the possible flags in a key_flags packet 10 | */ 11 | enum class key_flag : uint8_t 12 | { 13 | certification = 0x01, 14 | signing = 0x02, 15 | encryption_communications = 0x04, 16 | encryption_storage = 0x08, 17 | split_key = 0x10, 18 | authentication = 0x20, 19 | group_key = 0x80 20 | }; 21 | 22 | /** 23 | * Get a description of the key flag 24 | * 25 | * @param flag The key flag 26 | * @return The description of the key flag 27 | */ 28 | constexpr boost::string_view key_flag_description(key_flag flag) noexcept 29 | { 30 | // check the provided flag 31 | switch (flag) { 32 | case key_flag::certification: return "certification"; 33 | case key_flag::signing: return "signing"; 34 | case key_flag::encryption_communications: return "encryption of communications"; 35 | case key_flag::encryption_storage: return "encryption of storage"; 36 | case key_flag::split_key: return "key may be split"; 37 | case key_flag::authentication: return "authentication"; 38 | case key_flag::group_key: return "private component may be shared"; 39 | } 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /include/public_key.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "basic_key.h" 4 | #include "rsa_public_key.h" 5 | #include "dsa_public_key.h" 6 | #include "elgamal_public_key.h" 7 | #include "ecdh_public_key.h" 8 | #include "eddsa_public_key.h" 9 | #include "ecdsa_public_key.h" 10 | 11 | 12 | namespace pgp { 13 | 14 | /** 15 | * Trait used for public keys 16 | */ 17 | template 18 | struct public_key_traits 19 | { 20 | using rsa_key_t = rsa_public_key; 21 | using elgamal_key_t = elgamal_public_key; 22 | using dsa_key_t = dsa_public_key; 23 | using ecdh_key_t = ecdh_public_key; 24 | using eddsa_key_t = eddsa_public_key; 25 | using ecdsa_key_t = ecdsa_public_key; 26 | 27 | /** 28 | * Packet tag for public keys 29 | */ 30 | static constexpr packet_tag tag() noexcept 31 | { 32 | return key_tag; 33 | } 34 | 35 | static constexpr bool is_subkey() noexcept 36 | { 37 | return key_tag == packet_tag::public_subkey; 38 | } 39 | }; 40 | 41 | // specialize the public key type 42 | using public_key = basic_key>; 43 | using public_subkey = basic_key>; 44 | } 45 | -------------------------------------------------------------------------------- /include/rsa_public_key.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "decoder_traits.h" 6 | #include "multiprecision_integer.h" 7 | 8 | 9 | namespace pgp { 10 | 11 | // Forward declaration to prevent header dependency cycles 12 | class rsa_signature; 13 | 14 | /** 15 | * Class for holding RSA key data 16 | */ 17 | class rsa_public_key 18 | { 19 | public: 20 | /** 21 | * The public key type we belong to 22 | */ 23 | using public_key_t = rsa_public_key; 24 | 25 | /** 26 | * The signature type corresponding to this key type 27 | */ 28 | using signature_t = rsa_signature; 29 | 30 | /** 31 | * Constructor 32 | * 33 | * @param parser The decoder to parse the data 34 | */ 35 | template >> 36 | explicit rsa_public_key(decoder &parser) : 37 | _n{ parser }, 38 | _e{ parser } 39 | {} 40 | 41 | /** 42 | * Constructor 43 | * 44 | * @param n The public modulus n 45 | * @param e The encryption exponent e 46 | */ 47 | rsa_public_key(multiprecision_integer n, multiprecision_integer e) noexcept; 48 | 49 | /** 50 | * Comparison operators 51 | * 52 | * @param other The object to compare with 53 | */ 54 | bool operator==(const rsa_public_key &other) const noexcept; 55 | bool operator!=(const rsa_public_key &other) const noexcept; 56 | 57 | /** 58 | * Determine the size used in encoded format 59 | * @return The number of bytes used for encoded storage 60 | */ 61 | size_t size() const noexcept; 62 | 63 | /** 64 | * Retrieve the public modulus n 65 | * 66 | * @return The modulus n for the key 67 | */ 68 | const multiprecision_integer &n() const noexcept; 69 | 70 | /** 71 | * Retrieve the encryption exponent e 72 | * 73 | * @return The encryption exponent e 74 | */ 75 | const multiprecision_integer &e() const noexcept; 76 | 77 | /** 78 | * Write the data to an encoder 79 | * 80 | * @param writer The encoder to write to 81 | * @throws std::out_of_range, std::range_error 82 | */ 83 | template 84 | void encode(encoder_t&& writer) const 85 | { 86 | // encode the modulus and the exponent 87 | _n.encode(writer); 88 | _e.encode(writer); 89 | } 90 | private: 91 | multiprecision_integer _n; // the public modulus n 92 | multiprecision_integer _e; // the encryption exponent e 93 | }; 94 | 95 | } 96 | -------------------------------------------------------------------------------- /include/rsa_secret_key.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "decoder_traits.h" 6 | #include "multiprecision_integer.h" 7 | 8 | 9 | namespace pgp { 10 | 11 | /** 12 | * Class holding an RSA secret key 13 | */ 14 | class rsa_secret_key 15 | { 16 | public: 17 | /** 18 | * Constructor 19 | * 20 | * @param parser The decoder to parse the data 21 | */ 22 | template >> 23 | explicit rsa_secret_key(decoder &parser) : 24 | _d{ parser }, 25 | _p{ parser }, 26 | _q{ parser }, 27 | _u{ parser } 28 | {} 29 | 30 | /** 31 | * Constructor 32 | * 33 | * @param d The secret exponent d 34 | * @param p The secret prime value p 35 | * @param q The secret prime value q 36 | * @param u The multiplicative inverse p mod q 37 | */ 38 | rsa_secret_key(multiprecision_integer d, multiprecision_integer p, multiprecision_integer q, multiprecision_integer u) noexcept; 39 | 40 | /** 41 | * Comparison operators 42 | * 43 | * @param other The object to compare with 44 | */ 45 | bool operator==(const rsa_secret_key &other) const noexcept; 46 | bool operator!=(const rsa_secret_key &other) const noexcept; 47 | 48 | /** 49 | * Determine the size used in encoded format 50 | * @return The number of bytes used for encoded storage 51 | */ 52 | size_t size() const noexcept; 53 | 54 | /** 55 | * Retrieve the secret exponent d 56 | * 57 | * @return The secret exponent 58 | */ 59 | const multiprecision_integer &d() const noexcept; 60 | 61 | /** 62 | * Retrieve the secret prime value p 63 | * 64 | * @return The secret prime value p 65 | */ 66 | const multiprecision_integer &p() const noexcept; 67 | 68 | /** 69 | * Retrieve the secret prime value q 70 | * 71 | * @return The secret prime value q 72 | */ 73 | const multiprecision_integer &q() const noexcept; 74 | 75 | /** 76 | * Retrieve the u value 77 | * 78 | * @return The multiplicative inverse of p mod q 79 | */ 80 | const multiprecision_integer &u() const noexcept; 81 | 82 | /** 83 | * Write the data to an encoder 84 | * 85 | * @param writer The encoder to write to 86 | * @throws std::out_of_range, std::range_error 87 | */ 88 | template 89 | void encode(encoder_t&& writer) const 90 | { 91 | // encode all the secret fields 92 | _d.encode(writer); 93 | _p.encode(writer); 94 | _q.encode(writer); 95 | _u.encode(writer); 96 | } 97 | private: 98 | multiprecision_integer _d; // the secret exponent d 99 | multiprecision_integer _p; // the secret prime value p 100 | multiprecision_integer _q; // the secret prime value q 101 | multiprecision_integer _u; // the multiplicative inverse p mod q 102 | }; 103 | 104 | } 105 | -------------------------------------------------------------------------------- /include/rsa_signature.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "multiprecision_integer.h" 6 | #include "rsa_signature_encoder.h" 7 | #include "decoder_traits.h" 8 | 9 | 10 | namespace pgp { 11 | 12 | /** 13 | * Class for holding RSA signature-specific signature fields 14 | */ 15 | class rsa_signature 16 | { 17 | public: 18 | using encoder_t = rsa_signature_encoder; 19 | 20 | /** 21 | * Constructor 22 | * 23 | * @param parser The decoder to parse the data 24 | */ 25 | template >> 26 | explicit rsa_signature(decoder &parser) : 27 | _s{ parser } 28 | {} 29 | 30 | /** 31 | * Constructor 32 | * 33 | * @param s The signature value (m**d mod n) 34 | */ 35 | explicit rsa_signature(multiprecision_integer s) noexcept; 36 | 37 | /** 38 | * Comparison operators 39 | * 40 | * @param other The object to compare with 41 | */ 42 | bool operator==(const rsa_signature &other) const noexcept; 43 | bool operator!=(const rsa_signature &other) const noexcept; 44 | 45 | /** 46 | * Determine the size used in encoded format 47 | * @return The number of bytes used for encoded storage 48 | */ 49 | size_t size() const noexcept; 50 | 51 | /** 52 | * Retrieve the signature value 53 | * 54 | * @return The signature value (m**d mod n) 55 | */ 56 | const multiprecision_integer &s() const noexcept; 57 | 58 | /** 59 | * Write the data to an encoder 60 | * 61 | * @param writer The encoder to write to 62 | * @throws std::out_of_range, std::range_error 63 | */ 64 | template 65 | void encode(encoder_t&& writer) const 66 | { 67 | // encode the signature 68 | _s.encode(writer); 69 | } 70 | private: 71 | multiprecision_integer _s; // the signature value (m**d mod n); 72 | }; 73 | 74 | } 75 | -------------------------------------------------------------------------------- /include/secret_key.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "basic_key.h" 4 | #include "basic_secret_key.h" 5 | #include "rsa_secret_key.h" 6 | #include "rsa_public_key.h" 7 | #include "dsa_secret_key.h" 8 | #include "dsa_public_key.h" 9 | #include "elgamal_secret_key.h" 10 | #include "elgamal_public_key.h" 11 | #include "ecdh_secret_key.h" 12 | #include "ecdh_public_key.h" 13 | #include "eddsa_secret_key.h" 14 | #include "eddsa_public_key.h" 15 | #include "ecdsa_secret_key.h" 16 | #include "ecdsa_public_key.h" 17 | 18 | 19 | namespace pgp { 20 | 21 | /** 22 | * Trait used for secret keys 23 | */ 24 | template 25 | struct secret_key_traits 26 | { 27 | using rsa_key_t = basic_secret_key; 28 | using elgamal_key_t = basic_secret_key; 29 | using dsa_key_t = basic_secret_key; 30 | using ecdh_key_t = basic_secret_key; 31 | using eddsa_key_t = basic_secret_key; 32 | using ecdsa_key_t = basic_secret_key; 33 | 34 | /** 35 | * Packet tag for secret keys 36 | */ 37 | static constexpr packet_tag tag() noexcept 38 | { 39 | return key_tag; 40 | } 41 | 42 | static constexpr bool is_subkey() noexcept 43 | { 44 | return key_tag == packet_tag::secret_subkey; 45 | } 46 | }; 47 | 48 | // specialize the secret key types 49 | using secret_key = basic_key>; 50 | using secret_subkey = basic_key>; 51 | } 52 | -------------------------------------------------------------------------------- /include/secure_object.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | 6 | namespace pgp { 7 | 8 | /** 9 | * A class that explicitly locks and erases memory 10 | */ 11 | template 12 | class secure_object : public base_t 13 | { 14 | public: 15 | /** 16 | * Default constructor 17 | */ 18 | secure_object() 19 | { 20 | // lock memory 21 | lock(); 22 | } 23 | 24 | /** 25 | * Constructor 26 | * 27 | * @param parameter The first parameter 28 | * @param parameters Zero or more additional parameters 29 | */ 30 | template 31 | explicit secure_object(T&& parameter, arguments&&... parameters) : 32 | base_t{ (lock(), std::forward(parameter)), std::forward(parameters)... } 33 | {} 34 | 35 | /** 36 | * Copy constructor 37 | * 38 | * @param that The secure object to copy 39 | */ 40 | secure_object(const secure_object &that) : 41 | base_t{ (lock(), that) } 42 | {} 43 | 44 | /** 45 | * Move constructor 46 | * 47 | * @param that The secure object to move 48 | */ 49 | secure_object(secure_object &&that) : 50 | base_t{ (lock(), std::move(that)) } 51 | {} 52 | 53 | /** 54 | * Destructor 55 | */ 56 | ~secure_object() 57 | { 58 | // first destruct the managed object 59 | this->base_t::~base_t(); 60 | 61 | // zero out the memory and unlock it 62 | sodium_memzero(this, sizeof(*this)); 63 | sodium_munlock(this, sizeof(*this)); 64 | 65 | // default construct the base again 66 | // so that the implicit destructor 67 | // coming after us runs correctly 68 | new (this) base_t{}; 69 | } 70 | 71 | /** 72 | * Copy assignment 73 | * 74 | * @param that The secure object to copy 75 | * @return Same object for chaining 76 | */ 77 | secure_object &operator=(const secure_object &that) 78 | { 79 | // invoke base class copy operator and allow chaining 80 | base_t::operator=(that); 81 | return *this; 82 | } 83 | 84 | /** 85 | * Move assignment 86 | * 87 | * @param that The secure object to copy 88 | * @return Same object for chaining 89 | */ 90 | secure_object &operator=(secure_object &&that) 91 | { 92 | // invoke base class move operator and allow chaining 93 | base_t::operator=(std::move(that)); 94 | return *this; 95 | } 96 | private: 97 | /** 98 | * Lock the memory 99 | */ 100 | void lock() 101 | { 102 | // ensure the data is locked so it is 103 | // not swapped to disk in low-memory 104 | if (sodium_mlock(this, sizeof(*this)) == -1) { 105 | // failed to secure the memory 106 | throw std::runtime_error{ "Failed to lock memory, check ulimit" }; 107 | } 108 | } 109 | }; 110 | 111 | } 112 | -------------------------------------------------------------------------------- /include/signature_subpacket/unknown.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "../decoder_traits.h" 8 | #include "../signature_subpacket_type.h" 9 | #include "../variable_number.h" 10 | #include "../util/span.h" 11 | 12 | 13 | namespace pgp::signature_subpacket { 14 | 15 | /** 16 | * Class holding a single subpacket of unknown type 17 | */ 18 | class unknown 19 | { 20 | public: 21 | /** 22 | * Constructor 23 | * 24 | * @param type The subpacket type 25 | * @param parser The decoder to parse the data 26 | */ 27 | template >> 28 | unknown(signature_subpacket_type type, decoder &parser) : 29 | _type{ type } 30 | { 31 | // reserve memory for the data 32 | _data.reserve(parser.size()); 33 | 34 | // and fill the buffer 35 | while (!parser.empty()) { 36 | // add another byte 37 | _data.push_back(parser.template extract_number()); 38 | } 39 | } 40 | 41 | /** 42 | * Constructor 43 | * 44 | * @param type The signature subpacket type 45 | * @param data The data contained in the subpacket 46 | */ 47 | unknown(signature_subpacket_type type, span data); 48 | 49 | /** 50 | * Comparison operators 51 | * 52 | * @param other The object to compare with 53 | */ 54 | bool operator==(const unknown &other) const noexcept; 55 | bool operator!=(const unknown &other) const noexcept; 56 | 57 | /** 58 | * Determine the size used in encoded format 59 | * @return The number of bytes used for encoded storage 60 | */ 61 | size_t size() const noexcept; 62 | 63 | /** 64 | * Get the signature subpacket type 65 | * @return The type of signature subpacket 66 | */ 67 | signature_subpacket_type type() const noexcept; 68 | 69 | /** 70 | * Retrieve the data 71 | * @return A span containing all the integer numbers 72 | */ 73 | span data() const noexcept; 74 | 75 | /** 76 | * Write the data to an encoder 77 | * 78 | * @param writer The encoder to write to 79 | * @throws std::out_of_range, std::range_error 80 | */ 81 | template 82 | void encode(encoder_t&& writer) const 83 | { 84 | // first encode the length of the subpacket 85 | variable_number{ static_cast(sizeof(_type) + _data.size()) }.encode(writer); 86 | 87 | // add the subpacket type 88 | writer.push(_type); 89 | 90 | // now go over the whole data set 91 | for (auto number : _data) { 92 | // add the number 93 | writer.push(number); 94 | } 95 | } 96 | private: 97 | signature_subpacket_type _type; 98 | std::vector _data; 99 | }; 100 | 101 | } 102 | -------------------------------------------------------------------------------- /include/signature_type.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | 6 | namespace pgp { 7 | 8 | /** 9 | * Enum containing the various signature types 10 | */ 11 | enum class signature_type : uint8_t 12 | { 13 | binary_document = 0x00, 14 | canonical_text_document = 0x01, 15 | standalone = 0x02, 16 | generic_user_id_and_public_key_certification = 0x10, 17 | persona_user_id_and_public_key_certification = 0x11, 18 | casual_user_id_and_public_key_certification = 0x12, 19 | positive_user_id_and_public_key_certification = 0x13, 20 | subkey_binding = 0x18, 21 | primary_key_binding = 0x19, 22 | key_signature = 0x1f, 23 | key_revocation = 0x20, 24 | subkey_revocation = 0x28, 25 | certification_revocation = 0x30, 26 | timestamp = 0x40, 27 | third_party_confirmation = 0x50 28 | }; 29 | 30 | /** 31 | * Get a description of the signature type 32 | * 33 | * @param type The signature type 34 | * @return The description of the signature type 35 | */ 36 | constexpr boost::string_view signature_type_description(signature_type type) noexcept 37 | { 38 | // check the provided type 39 | switch (type) { 40 | case signature_type::binary_document: return "binary document signature"; 41 | case signature_type::canonical_text_document: return "canonical text document signature"; 42 | case signature_type::standalone: return "standalone signature"; 43 | case signature_type::generic_user_id_and_public_key_certification: return "generic certification of user id and public-key packet"; 44 | case signature_type::persona_user_id_and_public_key_certification: return "persona certification of user id and public-key packet"; 45 | case signature_type::casual_user_id_and_public_key_certification: return "casual certification of user id and public-key packet"; 46 | case signature_type::positive_user_id_and_public_key_certification: return "positive certification of user id and public-key packet"; 47 | case signature_type::subkey_binding: return "subkey binding signature"; 48 | case signature_type::primary_key_binding: return "primary key binding signature"; 49 | case signature_type::key_signature: return "key signature"; 50 | case signature_type::key_revocation: return "key recovation signature"; 51 | case signature_type::subkey_revocation: return "subkey revocation signature"; 52 | case signature_type::certification_revocation: return "certification revocation signature"; 53 | case signature_type::timestamp: return "timestamp signature"; 54 | case signature_type::third_party_confirmation: return "third-party confirmation signature"; 55 | } 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /include/string_to_key.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include "decoder_traits.h" 7 | #include "fixed_number.h" 8 | 9 | 10 | namespace pgp { 11 | 12 | /** 13 | * Class for holding the complete string-to-key 14 | * convention used for a secret or symmetric key 15 | */ 16 | class string_to_key 17 | { 18 | public: 19 | /** 20 | * Constructor 21 | */ 22 | string_to_key() = default; 23 | 24 | /** 25 | * Constructor 26 | * 27 | * @param parser The decoder to parse the data 28 | */ 29 | template >> 30 | explicit string_to_key(decoder &parser) : 31 | _convention{ parser } 32 | { 33 | // @TODO: support other conventions than "nothing" 34 | } 35 | 36 | /** 37 | * Comparison operators 38 | * 39 | * @param other The object to compare with 40 | */ 41 | bool operator==(const string_to_key &other) const noexcept; 42 | bool operator!=(const string_to_key &other) const noexcept; 43 | 44 | /** 45 | * Determine the size used in encoded format 46 | * @return The number of bytes used for encoded storage 47 | */ 48 | size_t size() const noexcept; 49 | 50 | /** 51 | * Retrieve the convention used 52 | * 53 | * @return The string-to-key convention 54 | */ 55 | uint8_t convention() const noexcept; 56 | 57 | /** 58 | * Write the data to an encoder 59 | * 60 | * @param writer The encoder to write to 61 | * @throws std::out_of_range, std::range_error 62 | */ 63 | template 64 | void encode(encoder_t&& writer) const 65 | { 66 | // encode the convention 67 | _convention.encode(writer); 68 | } 69 | private: 70 | uint8 _convention; // the string-to-key usage convention 71 | }; 72 | 73 | } 74 | -------------------------------------------------------------------------------- /include/symmetric_key_algorithm.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | 6 | namespace pgp { 7 | 8 | /** 9 | * The available symmetric key algorithms 10 | */ 11 | enum class symmetric_key_algorithm : uint8_t 12 | { 13 | plaintext = 0, 14 | idea = 1, 15 | triple_des = 2, 16 | cast5 = 3, 17 | blowfish = 4, 18 | aes128 = 7, 19 | aes192 = 8, 20 | aes256 = 9, 21 | twofish256 = 10, 22 | camellia128 = 11, 23 | camellia192 = 12, 24 | camellia256 = 13 25 | }; 26 | 27 | /** 28 | * Get a description of the symmetric key algorithm 29 | * 30 | * @param algorithm The algorithm to get a description for 31 | * @return The description of the algorithm 32 | */ 33 | constexpr boost::string_view symmetric_key_algorithm_description(symmetric_key_algorithm algorithm) noexcept 34 | { 35 | // check the given algorithm 36 | switch (algorithm) { 37 | case symmetric_key_algorithm::plaintext: return "plaintext"; 38 | case symmetric_key_algorithm::idea: return "IDEA"; 39 | case symmetric_key_algorithm::triple_des: return "TripleDES"; 40 | case symmetric_key_algorithm::cast5: return "CAST5"; 41 | case symmetric_key_algorithm::blowfish: return "Blowfish"; 42 | case symmetric_key_algorithm::aes128: return "AES with 128-bit key"; 43 | case symmetric_key_algorithm::aes192: return "AES with 192-bit key"; 44 | case symmetric_key_algorithm::aes256: return "AES with 256-bit key"; 45 | case symmetric_key_algorithm::twofish256: return "Twofish with 256-bit key"; 46 | case symmetric_key_algorithm::camellia128: return "Camellia with 128-bit key"; 47 | case symmetric_key_algorithm::camellia192: return "Camellia with 192-bit key"; 48 | case symmetric_key_algorithm::camellia256: return "Camellia with 256-bit key"; 49 | } 50 | 51 | // unknown algorithm found 52 | return "unknown symmetric key algorithm"; 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /include/unknown_key.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "decoder_traits.h" 4 | 5 | 6 | namespace pgp { 7 | 8 | // Forward declaration to prevent header dependency cycles 9 | class unknown_signature; 10 | 11 | /** 12 | * Class representing a key using an unknown algorithm 13 | */ 14 | class unknown_key 15 | { 16 | public: 17 | /** 18 | * The public key type we belong to 19 | */ 20 | using public_key_t = unknown_key; 21 | 22 | /** 23 | * The signature type corresponding to this key type 24 | */ 25 | using signature_t = unknown_signature; 26 | 27 | /** 28 | * Constructor 29 | */ 30 | unknown_key() = default; 31 | 32 | /** 33 | * Constructor 34 | */ 35 | template >> 36 | explicit unknown_key(decoder&) noexcept {} 37 | 38 | /** 39 | * Comparison operators 40 | */ 41 | bool operator==(const unknown_key&) const noexcept 42 | { 43 | return true; 44 | } 45 | 46 | /** 47 | * Comparison operators 48 | */ 49 | bool operator!=(const unknown_key &other) const noexcept 50 | { 51 | return !operator==(other); 52 | } 53 | 54 | /** 55 | * Determine the size used in encoded format 56 | * @return The number of bytes used for encoded storage 57 | * @throws std::runtime_error for the unknown key 58 | */ 59 | size_t size() const 60 | { 61 | // we do not know the size 62 | throw std::runtime_error{ "Unknown keys have an unknown size" }; 63 | } 64 | 65 | /** 66 | * Hash the key into a given hash context 67 | * 68 | * @throws std::runtime_error for the unknown key 69 | */ 70 | template 71 | void hash(encoder_t &) const 72 | { 73 | // unknown key cannot be hashed 74 | throw std::runtime_error{ "Unknown keys cannot be hashed" }; 75 | } 76 | 77 | /** 78 | * Write the data to an encoder 79 | * 80 | * @throws std::runtime_error for the unknown key 81 | */ 82 | template 83 | void encode(encoder_t&) const 84 | { 85 | // unknown key cannot be encoded 86 | throw std::runtime_error{ "Failed to encode unknown key" }; 87 | } 88 | }; 89 | 90 | } 91 | -------------------------------------------------------------------------------- /include/unknown_packet.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "decoder_traits.h" 4 | #include "packet_tag.h" 5 | 6 | 7 | namespace pgp { 8 | 9 | /** 10 | * A packet used when the packet tag is not supported 11 | * or if it is an unknown packet altogether. 12 | */ 13 | class unknown_packet 14 | { 15 | public: 16 | /** 17 | * Constructor 18 | */ 19 | unknown_packet() = default; 20 | 21 | /** 22 | * Constructor 23 | */ 24 | template >> 25 | explicit unknown_packet(decoder&) noexcept {} 26 | 27 | /** 28 | * Comparison operators 29 | */ 30 | bool operator==(const unknown_packet&) const noexcept 31 | { 32 | return true; 33 | } 34 | 35 | /** 36 | * Comparison operators 37 | */ 38 | bool operator!=(const unknown_packet &other) const noexcept 39 | { 40 | return !operator==(other); 41 | } 42 | 43 | /** 44 | * Retrieve the packet tag used for this 45 | * packet type 46 | * @return The packet type to use 47 | */ 48 | static constexpr packet_tag tag() noexcept 49 | { 50 | // an unknown packet has no tag 51 | return packet_tag::reserved; 52 | } 53 | 54 | /** 55 | * Determine the size used in encoded format 56 | * @return The number of bytes used for encoded storage 57 | * @throws std::runtime_error 58 | */ 59 | size_t size() const 60 | { 61 | // we don't have a known size 62 | throw std::runtime_error{ "Unknown packet does not have a known size" }; 63 | } 64 | 65 | /** 66 | * Write the data to an encoder 67 | * 68 | * @throws std::out_of_range, std::range_error 69 | */ 70 | template 71 | void encode(encoder_t&) const 72 | { 73 | // unknown packets cannot be encoded 74 | throw std::runtime_error{ "Failed to encode unknown packet" }; 75 | } 76 | }; 77 | 78 | } 79 | -------------------------------------------------------------------------------- /include/unknown_signature.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "unknown_signature_encoder.h" 4 | #include "decoder_traits.h" 5 | #include "secret_key.h" 6 | #include 7 | 8 | 9 | namespace pgp { 10 | 11 | /** 12 | * Class for holding an unknown signature 13 | */ 14 | class unknown_signature 15 | { 16 | public: 17 | using encoder_t = unknown_signature_encoder; 18 | 19 | /** 20 | * Constructor 21 | */ 22 | unknown_signature() = default; 23 | 24 | /** 25 | * Constructor 26 | */ 27 | template >> 28 | explicit unknown_signature(decoder&) noexcept {} 29 | 30 | /** 31 | * Comparison operators 32 | */ 33 | bool operator==(const unknown_signature&) const noexcept 34 | { 35 | return true; 36 | } 37 | 38 | /** 39 | * Comparison operators 40 | */ 41 | bool operator!=(const unknown_signature &other) const noexcept 42 | { 43 | return !operator==(other); 44 | } 45 | 46 | /** 47 | * Determine the size used in encoded format 48 | * @return The number of bytes used for encoded storage 49 | */ 50 | size_t size() const 51 | { 52 | // we do not know the size 53 | throw std::runtime_error{ "Unknown signatures have an unknown size" }; 54 | } 55 | 56 | /** 57 | * Write the data to an encoder 58 | * 59 | * @throws std::out_of_range, std::range_error 60 | */ 61 | template 62 | void encode(encoder_t&) const 63 | { 64 | // unknown key cannot be encoded 65 | throw std::runtime_error{ "Failed to encode unknown signature" }; 66 | } 67 | }; 68 | 69 | } 70 | -------------------------------------------------------------------------------- /include/unknown_signature_encoder.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "basic_key.h" 8 | #include "packet_tag.h" 9 | #include "secret_key.h" 10 | #include "util/span.h" 11 | 12 | 13 | namespace pgp { 14 | 15 | /** 16 | * An explicitly unimplemented (i.e. throwing) encoder for unknown signatures 17 | */ 18 | class unknown_signature_encoder 19 | { 20 | public: 21 | /** 22 | * Create a nonexistent encoder; throws. 23 | */ 24 | template 25 | explicit unknown_signature_encoder(basic_key>) 26 | { 27 | throw std::runtime_error{ "Unknown signatures cannot sign streamed data" }; 28 | } 29 | 30 | /** 31 | * Push a value to a nonexistent encoder; throws. 32 | */ 33 | template 34 | void push(T) 35 | { 36 | throw std::runtime_error{ "Unknown signatures cannot sign streamed data" }; 37 | } 38 | 39 | /** 40 | * Insert a blob to a nonexistent encoder; throws. 41 | */ 42 | template 43 | unknown_signature_encoder &insert_blob(span) 44 | { 45 | throw std::runtime_error{ "Unknown signatures cannot sign streamed data" }; 46 | } 47 | 48 | /** 49 | * Get the hash prefix of a nonexistent encoder; throws. 50 | */ 51 | std::array hash_prefix(); 52 | 53 | /** 54 | * Get the finalized parameters of a nonexistent encoder; throws. 55 | */ 56 | std::tuple<> finalize(); 57 | }; 58 | } 59 | -------------------------------------------------------------------------------- /include/user_id.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include "util/span.h" 7 | #include "packet_tag.h" 8 | #include "decoder_traits.h" 9 | 10 | 11 | namespace pgp { 12 | 13 | /** 14 | * Class for holding a user id 15 | */ 16 | class user_id 17 | { 18 | public: 19 | /** 20 | * Constructor 21 | * 22 | * @param parser The parser to decode data from 23 | */ 24 | template >> 25 | explicit user_id(decoder &parser) : 26 | user_id{ parser.template extract_blob(parser.size()) } 27 | {} 28 | 29 | /** 30 | * Constructor 31 | * 32 | * @param id The user id to use 33 | */ 34 | explicit user_id(span id) noexcept; 35 | 36 | /** 37 | * Constructor 38 | * 39 | * @param id The user id to use 40 | */ 41 | explicit user_id(std::string id) noexcept; 42 | 43 | /** 44 | * Comparison operators 45 | * 46 | * @param other The object to compare with 47 | */ 48 | bool operator==(const user_id &other) const noexcept; 49 | bool operator!=(const user_id &other) const noexcept; 50 | 51 | /** 52 | * Retrieve the packet tag used for this 53 | * packet type 54 | * @return The packet type to use 55 | */ 56 | packet_tag tag() const noexcept 57 | { 58 | // this is a user id packet 59 | return packet_tag::user_id; 60 | } 61 | 62 | /** 63 | * Determine the size used in encoded format 64 | * @return The number of bytes used for encoded storage 65 | * @throws std::runtime_error for unknown key types 66 | */ 67 | size_t size() const noexcept; 68 | 69 | /** 70 | * Retrieve the user id 71 | * 72 | * @return The user id 73 | */ 74 | const std::string &id() const noexcept; 75 | 76 | /** 77 | * Write the data to an encoder 78 | * 79 | * @param writer The encoder to write to 80 | * @throws std::out_of_range, std::range_error 81 | */ 82 | template 83 | void encode(encoder_t&& writer) const 84 | { 85 | // insert the id into the encoder 86 | writer.insert_blob(span{ _id }); 87 | } 88 | private: 89 | std::string _id; // the user id representation 90 | }; 91 | 92 | } 93 | -------------------------------------------------------------------------------- /include/util/narrow_cast.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | 6 | namespace util { 7 | 8 | /** 9 | * Searchable way to do narrow casts of values 10 | * 11 | * @param u The value to narrow 12 | * @return The narrowed value 13 | */ 14 | template 15 | constexpr T narrow_cast(U&& u) noexcept 16 | { 17 | return static_cast(std::forward(u)); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /include/util/span.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | 7 | namespace pgp { 8 | using gsl::span; 9 | } -------------------------------------------------------------------------------- /include/util/transaction.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | 6 | namespace util { 7 | 8 | /** 9 | * Helper class to call a change-restoring function unless the changes are 10 | * committed using commit() 11 | */ 12 | template 13 | class transaction { 14 | public: 15 | explicit transaction(Rollback rollback): 16 | _rollback{ std::forward(rollback) } {} 17 | 18 | /** 19 | * Copy and move constructors 20 | */ 21 | transaction(const transaction &other) = delete; 22 | transaction(transaction &&other) = default; 23 | 24 | /** 25 | * Assignment operators 26 | */ 27 | transaction &operator=(const transaction &other) = delete; 28 | transaction &operator=(transaction &&other) = default; 29 | 30 | /** 31 | * If commit() was not called, the restore function will be called. 32 | */ 33 | ~transaction() 34 | { 35 | if (!committed) { 36 | _rollback(); 37 | } 38 | } 39 | 40 | /** 41 | * Prevent the restore function from being called upon destruction. 42 | */ 43 | void commit() 44 | { 45 | committed = true; 46 | } 47 | 48 | private: 49 | using rollback_t = std::remove_cv_t>; 50 | 51 | rollback_t _rollback; 52 | bool committed = false; 53 | }; 54 | 55 | } 56 | -------------------------------------------------------------------------------- /include/util/tuple.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | 7 | /** 8 | * This file is only necessary to deal with implementations missing some 9 | * required functionality in the STL. 10 | * 11 | * When the STL implementation catches up, calls to util::* functions declared 12 | * in this file can be changed to the corresponding std::* functions, and this 13 | * file can then be deleted. 14 | */ 15 | 16 | namespace detail { 17 | 18 | /** 19 | * Private helper function to construct the type 20 | * 21 | * Source: https://en.cppreference.com/w/cpp/utility/make_from_tuple 22 | * 23 | * @param tuple The tuple to construct from 24 | * @return The constructed type T 25 | */ 26 | template 27 | constexpr T make_from_tuple_impl( Tuple&& tuple, std::index_sequence ) 28 | { 29 | // construct with all items from the tuple 30 | return T(std::get(std::forward(tuple))...); 31 | } 32 | 33 | /** 34 | * Private helper function to invoke the function 35 | * 36 | * Source: https://en.cppreference.com/w/cpp/utility/apply 37 | * 38 | * @param f The function to invoke 39 | * @param t The tuple with the arguments to the function 40 | * @return The return value of the invoked function 41 | */ 42 | template 43 | constexpr decltype(auto) apply_impl(F&& f, Tuple&& t, std::index_sequence) 44 | { 45 | // call the Callable using std::invoke with all items from the tuple 46 | return std::invoke(std::forward(f), std::get(std::forward(t))...); 47 | } 48 | 49 | } 50 | 51 | namespace util { 52 | 53 | /** 54 | * Create a type T from a tuple of parameters 55 | * 56 | * Adapted from: https://en.cppreference.com/w/cpp/utility/make_from_tuple 57 | * 58 | * @param tuple The tuple to create from 59 | * @return The constructed type T 60 | */ 61 | template 62 | constexpr T make_from_tuple(Tuple&& tuple) 63 | { 64 | // use the implementation, providing the index sequence into the tuple elements 65 | return detail::make_from_tuple_impl(std::forward(tuple), std::make_index_sequence>::value>{}); 66 | } 67 | 68 | /** 69 | * Invoke a Callable object with a tuple of arguments. 70 | * 71 | * Adapted from: https://en.cppreference.com/w/cpp/utility/apply 72 | * 73 | * @param f The function to invoke 74 | * @param t The tuple with the arguments to the function 75 | * @return The return value of the invoked function 76 | */ 77 | template 78 | constexpr decltype(auto) apply(F&& f, Tuple&& t) 79 | { 80 | // use the implementation, providing the index sequence into the tuple elements 81 | return detail::apply_impl( 82 | std::forward(f), std::forward(t), 83 | std::make_index_sequence>::value>{} 84 | ); 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /include/util/variant.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef USE_MPARK_VARIANT 4 | 5 | #define VARIANT_PROVIDER mpark 6 | #include 7 | 8 | #else 9 | 10 | #define VARIANT_PROVIDER std 11 | #include 12 | 13 | #endif 14 | 15 | namespace pgp { 16 | using VARIANT_PROVIDER::variant; 17 | using VARIANT_PROVIDER::in_place_type_t; 18 | using VARIANT_PROVIDER::get; 19 | using VARIANT_PROVIDER::visit; 20 | using VARIANT_PROVIDER::holds_alternative; 21 | using VARIANT_PROVIDER::swap; 22 | using VARIANT_PROVIDER::operator==; 23 | using VARIANT_PROVIDER::operator!=; 24 | using VARIANT_PROVIDER::operator>; 25 | using VARIANT_PROVIDER::operator<; 26 | using VARIANT_PROVIDER::operator<=; 27 | using VARIANT_PROVIDER::operator>=; 28 | using VARIANT_PROVIDER::bad_variant_access; 29 | using VARIANT_PROVIDER::variant_size; 30 | using VARIANT_PROVIDER::variant_size_v; 31 | using VARIANT_PROVIDER::monostate; 32 | using VARIANT_PROVIDER::variant_npos; 33 | } 34 | -------------------------------------------------------------------------------- /include/util/vector.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../allocator.h" 4 | #include "../secure_object.h" 5 | #include 6 | 7 | 8 | namespace pgp { 9 | 10 | /** 11 | * Alias for a vector using secure storage 12 | */ 13 | template 14 | using vector = secure_object>>; 15 | 16 | } 17 | -------------------------------------------------------------------------------- /source/curve_oid.cpp: -------------------------------------------------------------------------------- 1 | #include "curve_oid.h" 2 | 3 | 4 | namespace pgp { 5 | 6 | /** 7 | * Constructor 8 | * 9 | * @param data The range of numbers 10 | */ 11 | curve_oid::curve_oid(span data) noexcept : 12 | _data{ data.begin(), data.end() } 13 | {} 14 | 15 | /** 16 | * Constructor 17 | * 18 | * @param data The range of numbers 19 | */ 20 | curve_oid::curve_oid(std::initializer_list data) noexcept : 21 | _data{ data.begin(), data.end() } 22 | {} 23 | 24 | /** 25 | * Comparison operators 26 | * 27 | * @param other The object to compare with 28 | */ 29 | bool curve_oid::operator==(const curve_oid &other) const noexcept 30 | { 31 | return data() == other.data(); 32 | } 33 | 34 | /** 35 | * Comparison operators 36 | * 37 | * @param other The object to compare with 38 | */ 39 | bool curve_oid::operator!=(const curve_oid &other) const noexcept 40 | { 41 | return !operator==(other); 42 | } 43 | 44 | /** 45 | * Determine the size used in encoded format 46 | * @return The number of bytes used for encoded storage 47 | */ 48 | size_t curve_oid::size() const noexcept 49 | { 50 | // one byte for the header, plus the data itself 51 | return _data.size() + 1; 52 | } 53 | 54 | /** 55 | * Retrieve the data 56 | * @return A span containing all the integer numbers 57 | */ 58 | span curve_oid::data() const 59 | { 60 | // provide access to the underlying data 61 | return _data; 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /source/decoder.cpp: -------------------------------------------------------------------------------- 1 | #include "decoder.h" 2 | 3 | 4 | namespace pgp { 5 | 6 | /** 7 | * Constructor 8 | * 9 | * @param data The range to (de|en)code from or to 10 | */ 11 | decoder::decoder(span data) noexcept : 12 | _data{ data } 13 | {} 14 | 15 | /** 16 | * Splice the data in the decoder into a second decoder 17 | * 18 | * @param size Number of bytes to splice off into the other decoder 19 | * @return The decoder containing the sliced off data 20 | * @throws std::out_of_range 21 | */ 22 | decoder decoder::splice(size_t size) 23 | { 24 | // check whether we have enough data 25 | if (size > _data.size()) { 26 | // trying to read out-of-bounds 27 | throw std::out_of_range{ "Not enough data available to splice" }; 28 | } 29 | 30 | // first create a new decoder with the spliced data 31 | decoder result{ _data.first(size) }; 32 | 33 | // alter the stored data 34 | _data = _data.subspan(size); 35 | 36 | // return the result 37 | return result; 38 | } 39 | 40 | /** 41 | * Check whether the decoder is empty 42 | * @return Whether all encoded data is exhausted 43 | */ 44 | bool decoder::empty() const noexcept 45 | { 46 | // check whether the data stream is empty 47 | return _data.empty(); 48 | } 49 | 50 | /** 51 | * The number of bytes of encoded data still available 52 | * 53 | * @note This number is rounded up, if some bits of 54 | * a byte where consumed, the byte is still counted 55 | * @return The available number of bytes 56 | */ 57 | size_t decoder::size() const noexcept 58 | { 59 | // return the number of bytes available 60 | return _data.size(); 61 | } 62 | 63 | /** 64 | * Peek at bits at the current position, but 65 | * do not consume them 66 | * 67 | * @param count Number of bits to extract 68 | * @return The extracted bits 69 | * @throws std::out_of_range 70 | */ 71 | uint8_t decoder::peek_bits(size_t count) const 72 | { 73 | // check whether we have enough data 74 | if (empty()) { 75 | // trying to read out-of-bounds 76 | throw std::out_of_range{ "No more data left to read" }; 77 | } 78 | 79 | // retrieve the current leading byte and mask already-read bytes 80 | uint8_t result = mask(_data[0]); 81 | 82 | // remove the extra bits from the end 83 | return result >> (8 - count - _skip_bits); 84 | } 85 | 86 | /** 87 | * Extract bits at the current position 88 | * 89 | * @param count Number of bits to extract 90 | * @return The extracted bits 91 | * @throws std::out_of_range 92 | */ 93 | uint8_t decoder::extract_bits(size_t count) 94 | { 95 | // retrieve the current data 96 | auto result = peek_bits(count); 97 | 98 | // update the bits to skip 99 | if (_skip_bits + count >= 8) { 100 | // move on to the next byte 101 | _data = _data.subspan<1>(); 102 | _skip_bits = 0; 103 | } else { 104 | // just update the counter 105 | _skip_bits += count; 106 | } 107 | 108 | // return the result 109 | return result; 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /source/dsa_public_key.cpp: -------------------------------------------------------------------------------- 1 | #include "dsa_public_key.h" 2 | #include 3 | 4 | 5 | namespace pgp { 6 | 7 | /** 8 | * Constructor 9 | * 10 | * @param p The prime p 11 | * @param q The group order q 12 | * @param g The generator g 13 | * @param y The public key value 14 | */ 15 | dsa_public_key::dsa_public_key(multiprecision_integer p, multiprecision_integer q, multiprecision_integer g, multiprecision_integer y) noexcept : 16 | _p{ std::move(p) }, 17 | _q{ std::move(q) }, 18 | _g{ std::move(g) }, 19 | _y{ std::move(y) } 20 | {} 21 | 22 | /** 23 | * Comparison operators 24 | * 25 | * @param other The object to compare with 26 | */ 27 | bool dsa_public_key::operator==(const dsa_public_key &other) const noexcept 28 | { 29 | return p() == other.p() && q() == other.q() && g() == other.g() && y() == other.y(); 30 | } 31 | 32 | /** 33 | * Comparison operators 34 | * 35 | * @param other The object to compare with 36 | */ 37 | bool dsa_public_key::operator!=(const dsa_public_key &other) const noexcept 38 | { 39 | return !operator==(other); 40 | } 41 | 42 | /** 43 | * Determine the size used in encoded format 44 | * @return The number of bytes used for encoded storage 45 | */ 46 | size_t dsa_public_key::size() const noexcept 47 | { 48 | // we need to store all the components 49 | return _p.size() + _q.size() + _g.size() + _y.size(); 50 | } 51 | 52 | /** 53 | * Retrieve the prime p 54 | * 55 | * @return The prime number p 56 | */ 57 | const multiprecision_integer &dsa_public_key::p() const noexcept 58 | { 59 | // return the stored prime 60 | return _p; 61 | } 62 | 63 | /** 64 | * Retrieve the group order q 65 | * 66 | * @return The group number q 67 | */ 68 | const multiprecision_integer &dsa_public_key::q() const noexcept 69 | { 70 | // return the stored group order 71 | return _q; 72 | } 73 | 74 | /** 75 | * Retrieve the generator g 76 | * 77 | * @return The generator g 78 | */ 79 | const multiprecision_integer &dsa_public_key::g() const noexcept 80 | { 81 | // return the stored generator 82 | return _g; 83 | } 84 | 85 | /** 86 | * Retrieve the public key value 87 | * 88 | * @return The public key value: g**x mod p 89 | */ 90 | const multiprecision_integer &dsa_public_key::y() const noexcept 91 | { 92 | // return the stored public key 93 | return _y; 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /source/dsa_secret_key.cpp: -------------------------------------------------------------------------------- 1 | #include "dsa_secret_key.h" 2 | #include 3 | 4 | 5 | namespace pgp { 6 | 7 | /** 8 | * Constructor 9 | * 10 | * @param x The secret exponent 11 | */ 12 | dsa_secret_key::dsa_secret_key(multiprecision_integer x) noexcept : 13 | _x{ std::move(x) } 14 | {} 15 | 16 | /** 17 | * Comparison operators 18 | * 19 | * @param other The object to compare with 20 | */ 21 | bool dsa_secret_key::operator==(const dsa_secret_key &other) const noexcept 22 | { 23 | return x() == other.x(); 24 | } 25 | 26 | /** 27 | * Comparison operators 28 | * 29 | * @param other The object to compare with 30 | */ 31 | bool dsa_secret_key::operator!=(const dsa_secret_key &other) const noexcept 32 | { 33 | return !operator==(other); 34 | } 35 | 36 | /** 37 | * Determine the size used in encoded format 38 | * @return The number of bytes used for encoded storage 39 | */ 40 | size_t dsa_secret_key::size() const noexcept 41 | { 42 | // we need to encode the secret exponent 43 | return _x.size(); 44 | } 45 | 46 | /** 47 | * Retrieve the secret exponent 48 | * 49 | * @return The secret exponent x 50 | */ 51 | const multiprecision_integer &dsa_secret_key::x() const noexcept 52 | { 53 | // return the secret exponent 54 | return _x; 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /source/dsa_signature.cpp: -------------------------------------------------------------------------------- 1 | #include "dsa_signature.h" 2 | #include 3 | 4 | 5 | namespace pgp { 6 | 7 | /** 8 | * Constructor 9 | * 10 | * @param r The DSA r value 11 | * @param s The DSA s value 12 | */ 13 | dsa_signature::dsa_signature(multiprecision_integer r, multiprecision_integer s) noexcept : 14 | _r{ std::move(r) }, 15 | _s{ std::move(s) } 16 | {} 17 | 18 | /** 19 | * Comparison operators 20 | * 21 | * @param other The object to compare with 22 | */ 23 | bool dsa_signature::operator==(const dsa_signature &other) const noexcept 24 | { 25 | return r() == other.r() && s() == other.s(); 26 | } 27 | 28 | /** 29 | * Comparison operators 30 | * 31 | * @param other The object to compare with 32 | */ 33 | bool dsa_signature::operator!=(const dsa_signature &other) const noexcept 34 | { 35 | return !operator==(other); 36 | } 37 | 38 | /** 39 | * Determine the size used in encoded format 40 | * @return The number of bytes used for encoded storage 41 | */ 42 | size_t dsa_signature::size() const noexcept 43 | { 44 | // we need space to store both values 45 | return _r.size() + _s.size(); 46 | } 47 | 48 | /** 49 | * Retrieve the DSA r value 50 | * 51 | * @return The r value 52 | */ 53 | const multiprecision_integer &dsa_signature::r() const noexcept 54 | { 55 | // return the r value 56 | return _r; 57 | } 58 | 59 | /** 60 | * Retrieve the DSA s value 61 | * 62 | * @return The s value 63 | */ 64 | const multiprecision_integer &dsa_signature::s() const noexcept 65 | { 66 | // return the s value 67 | return _s; 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /source/dsa_signature_encoder.cpp: -------------------------------------------------------------------------------- 1 | #include "dsa_signature_encoder.h" 2 | #include 3 | 4 | 5 | namespace pgp { 6 | 7 | /** 8 | * Make the signature 9 | * 10 | * @return Tuple of the r and s parameters for the DSA signature 11 | */ 12 | std::tuple 13 | dsa_signature_encoder::finalize() 14 | { 15 | // TODO 16 | throw std::runtime_error{ "Generating DSA signatures is not yet implemented" }; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /source/ecdh_public_key.cpp: -------------------------------------------------------------------------------- 1 | #include "ecdh_public_key.h" 2 | #include 3 | 4 | 5 | namespace pgp { 6 | 7 | /** 8 | * Constructor 9 | * 10 | * @param curve The curve object identifier 11 | * @param Q The public curve point Q 12 | * @param hash_function The used KDF hash function 13 | * @param algorithm The symmetric alforithm for wrapping the symmetric key 14 | */ 15 | ecdh_public_key::ecdh_public_key(curve_oid curve, multiprecision_integer Q, hash_algorithm hash_function, symmetric_key_algorithm algorithm) noexcept : 16 | _curve{ std::move(curve) }, 17 | _Q{ std::move(Q) }, 18 | _hash_function{ hash_function }, 19 | _algorithm{ algorithm } 20 | {} 21 | 22 | /** 23 | * Comparison operators 24 | * 25 | * @param other The object to compare with 26 | */ 27 | bool ecdh_public_key::operator==(const ecdh_public_key &other) const noexcept 28 | { 29 | return curve() == other.curve() && 30 | Q() == other.Q() && 31 | hash_function() == other.hash_function() && 32 | algorithm() == other.algorithm(); 33 | } 34 | 35 | /** 36 | * Comparison operators 37 | * 38 | * @param other The object to compare with 39 | */ 40 | bool ecdh_public_key::operator!=(const ecdh_public_key &other) const noexcept 41 | { 42 | return !operator==(other); 43 | } 44 | 45 | /** 46 | * Determine the size used in encoded format 47 | * @return The number of bytes used for encoded storage 48 | */ 49 | size_t ecdh_public_key::size() const noexcept 50 | { 51 | // add the size of all the components 52 | return _curve.size() + _Q.size() + _kdf_size.size() + _reserved.size() + sizeof _hash_function + sizeof _algorithm; 53 | } 54 | 55 | /** 56 | * Retrieve the curve object identifier 57 | * 58 | * @return The curve object identifier 59 | */ 60 | const curve_oid &ecdh_public_key::curve() const noexcept 61 | { 62 | // return the stored curve id 63 | return _curve; 64 | } 65 | 66 | /** 67 | * Retrieve the public curve point Q 68 | * 69 | * @return The public curve point Q, in compressed format 70 | */ 71 | const multiprecision_integer &ecdh_public_key::Q() const noexcept 72 | { 73 | // return the public key point 74 | return _Q; 75 | } 76 | 77 | /** 78 | * Retrieve the KDF hash function 79 | * 80 | * @return The KDF hash function 81 | */ 82 | hash_algorithm ecdh_public_key::hash_function() const noexcept 83 | { 84 | // return the stored hash function 85 | return _hash_function; 86 | } 87 | 88 | /** 89 | * Retrieve the symmetric algorithm 90 | * 91 | * @return The symmetrict algorithm for wrapping the symmetric key 92 | */ 93 | symmetric_key_algorithm ecdh_public_key::algorithm() const noexcept 94 | { 95 | // return the stored algorithm 96 | return _algorithm; 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /source/ecdh_secret_key.cpp: -------------------------------------------------------------------------------- 1 | #include "ecdh_secret_key.h" 2 | #include 3 | 4 | 5 | namespace pgp { 6 | 7 | /** 8 | * Constructor 9 | * 10 | * @param k The secret scalar for the public point 11 | */ 12 | ecdh_secret_key::ecdh_secret_key(multiprecision_integer k) noexcept : 13 | _k{ std::move(k) } 14 | {} 15 | 16 | /** 17 | * Comparison operators 18 | * 19 | * @param other The object to compare with 20 | */ 21 | bool ecdh_secret_key::operator==(const ecdh_secret_key &other) const noexcept 22 | { 23 | return k() == other.k(); 24 | } 25 | 26 | /** 27 | * Comparison operators 28 | * 29 | * @param other The object to compare with 30 | */ 31 | bool ecdh_secret_key::operator!=(const ecdh_secret_key &other) const noexcept 32 | { 33 | return !operator==(other); 34 | } 35 | 36 | /** 37 | * Determine the size used in encoded format 38 | * @return The number of bytes used for encoded storage 39 | */ 40 | size_t ecdh_secret_key::size() const noexcept 41 | { 42 | // we need to store the secret scalar 43 | return _k.size(); 44 | } 45 | 46 | /** 47 | * Retrieve the secret scalar 48 | * 49 | * @return The secret scalar for the public point 50 | */ 51 | const multiprecision_integer &ecdh_secret_key::k() const noexcept 52 | { 53 | // return the stored scalar 54 | return _k; 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /source/ecdsa_public_key.cpp: -------------------------------------------------------------------------------- 1 | #include "ecdsa_public_key.h" 2 | #include 3 | 4 | 5 | namespace pgp { 6 | 7 | /** 8 | * Constructor 9 | * 10 | * @param curve The curve object identifier 11 | * @param Q The public curve point Q 12 | */ 13 | ecdsa_public_key::ecdsa_public_key(curve_oid curve, multiprecision_integer Q) noexcept : 14 | _curve{ std::move(curve) }, 15 | _Q{ std::move(Q) } 16 | {} 17 | 18 | /** 19 | * Comparison operators 20 | * 21 | * @param other The object to compare with 22 | */ 23 | bool ecdsa_public_key::operator==(const ecdsa_public_key &other) const noexcept 24 | { 25 | return curve() == other.curve() && Q() == other.Q(); 26 | } 27 | 28 | /** 29 | * Comparison operators 30 | * 31 | * @param other The object to compare with 32 | */ 33 | bool ecdsa_public_key::operator!=(const ecdsa_public_key &other) const noexcept 34 | { 35 | return !operator==(other); 36 | } 37 | 38 | /** 39 | * Determine the size used in encoded format 40 | * @return The number of bytes used for encoded storage 41 | */ 42 | size_t ecdsa_public_key::size() const noexcept 43 | { 44 | // we need to store the curve oid and the curve point 45 | return _curve.size() + _Q.size(); 46 | } 47 | 48 | /** 49 | * Retrieve the curve object identifier 50 | * 51 | * @return The curve object identifier 52 | */ 53 | const curve_oid &ecdsa_public_key::curve() const noexcept 54 | { 55 | // return the curve object identifier 56 | return _curve; 57 | } 58 | 59 | /** 60 | * Retrieve the public curve point Q 61 | * 62 | * @return The public curve point Q, in compressed format 63 | */ 64 | const multiprecision_integer &ecdsa_public_key::Q() const noexcept 65 | { 66 | // return the public point 67 | return _Q; 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /source/ecdsa_secret_key.cpp: -------------------------------------------------------------------------------- 1 | #include "ecdsa_secret_key.h" 2 | #include 3 | 4 | 5 | namespace pgp { 6 | 7 | /** 8 | * Constructor 9 | * 10 | * @param k The secret scalar for the public point 11 | */ 12 | ecdsa_secret_key::ecdsa_secret_key(multiprecision_integer k) noexcept : 13 | _k{ std::move(k) } 14 | {} 15 | 16 | /** 17 | * Comparison operators 18 | * 19 | * @param other The object to compare with 20 | */ 21 | bool ecdsa_secret_key::operator==(const ecdsa_secret_key &other) const noexcept 22 | { 23 | return k() == other.k(); 24 | } 25 | 26 | /** 27 | * Comparison operators 28 | * 29 | * @param other The object to compare with 30 | */ 31 | bool ecdsa_secret_key::operator!=(const ecdsa_secret_key &other) const noexcept 32 | { 33 | return !operator==(other); 34 | } 35 | 36 | /** 37 | * Determine the size used in encoded format 38 | * @return The number of bytes used for encoded storage 39 | */ 40 | size_t ecdsa_secret_key::size() const noexcept 41 | { 42 | // we need to store the secret scalar 43 | return _k.size(); 44 | } 45 | 46 | /** 47 | * Retrieve the secret scalar 48 | * 49 | * @return The secret scalar for the public point 50 | */ 51 | const multiprecision_integer &ecdsa_secret_key::k() const noexcept 52 | { 53 | // return the stored scalar 54 | return _k; 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /source/ecdsa_signature.cpp: -------------------------------------------------------------------------------- 1 | #include "ecdsa_signature.h" 2 | #include 3 | 4 | namespace pgp { 5 | 6 | /** 7 | * Constructor 8 | * 9 | * @param r The ECDSA r value 10 | * @param s The ECDSA s value 11 | */ 12 | ecdsa_signature::ecdsa_signature(multiprecision_integer r, multiprecision_integer s) noexcept : 13 | _r{ std::move(r) }, 14 | _s{ std::move(s) } 15 | {} 16 | 17 | /** 18 | * Comparison operators 19 | * 20 | * @param other The object to compare with 21 | */ 22 | bool ecdsa_signature::operator==(const ecdsa_signature &other) const noexcept 23 | { 24 | return r() == other.r() && s() == other.s(); 25 | } 26 | 27 | /** 28 | * Comparison operators 29 | * 30 | * @param other The object to compare with 31 | */ 32 | bool ecdsa_signature::operator!=(const ecdsa_signature &other) const noexcept 33 | { 34 | return !operator==(other); 35 | } 36 | 37 | /** 38 | * Determine the size used in encoded format 39 | * @return The number of bytes used for encoded storage 40 | */ 41 | size_t ecdsa_signature::size() const noexcept 42 | { 43 | // we need space to store both values 44 | return _r.size() + _s.size(); 45 | } 46 | 47 | /** 48 | * Retrieve the ECDSA r value 49 | * 50 | * @return The r value 51 | */ 52 | const multiprecision_integer &ecdsa_signature::r() const noexcept 53 | { 54 | // return the r value 55 | return _r; 56 | } 57 | 58 | /** 59 | * Retrieve the ECDSA s value 60 | * 61 | * @return The s value 62 | */ 63 | const multiprecision_integer &ecdsa_signature::s() const noexcept 64 | { 65 | // return the s value 66 | return _s; 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /source/ecdsa_signature_encoder.cpp: -------------------------------------------------------------------------------- 1 | #include "ecdsa_signature_encoder.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "util/span.h" 13 | #include "null_hash.h" 14 | 15 | 16 | namespace pgp { 17 | 18 | /** 19 | * Make the signature 20 | * 21 | * @return Tuple of the r and s parameters for the ECDSA signature 22 | */ 23 | std::tuple 24 | ecdsa_signature_encoder::finalize() 25 | { 26 | // Crypto++ does not export this information as constexpr 27 | constexpr size_t signature_length = 64; 28 | 29 | // retrieve the key data 30 | auto secret_data = ecdsa_key.k().data(); 31 | 32 | // ECDSA needs randomness for signatures 33 | CryptoPP::AutoSeededRandomPool prng; 34 | 35 | CryptoPP::Integer k1_exponent; 36 | k1_exponent.Decode(secret_data.data(), secret_data.size()); 37 | 38 | CryptoPP::ECDSA>::PrivateKey k1; 39 | k1.Initialize(CryptoPP::ASN1::secp256r1(), k1_exponent); 40 | 41 | // the buffer for the signed message and the concatenated key 42 | std::array signed_message; 43 | 44 | // construct the signer 45 | CryptoPP::ECDSA>::Signer signer{k1}; 46 | 47 | if (signer.MaxSignatureLength() != signature_length) { 48 | throw std::logic_error("Unexpected Crypto++ ECDSA maximum signature length"); 49 | } 50 | 51 | // get the digest to sign 52 | auto digest_data = digest(); 53 | 54 | // now sign the message 55 | size_t actual_length = signer.SignMessage(prng, digest_data.data(), digest_data.size(), signed_message.data()); 56 | 57 | if (actual_length != signature_length) { 58 | throw std::logic_error("Unexpected Crypto++ ECDSA actual signature length"); 59 | } 60 | 61 | // split up the data and return it 62 | return std::make_tuple( 63 | multiprecision_integer{span{ signed_message.data(), 32 }}, 64 | multiprecision_integer{span{ signed_message.data() + 32, 32 }} 65 | ); 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /source/eddsa_public_key.cpp: -------------------------------------------------------------------------------- 1 | #include "eddsa_public_key.h" 2 | #include 3 | 4 | 5 | namespace pgp { 6 | 7 | /** 8 | * Constructor 9 | * 10 | * @param curve The curve object identifier 11 | * @param Q The public curve point Q 12 | */ 13 | eddsa_public_key::eddsa_public_key(curve_oid curve, multiprecision_integer Q) noexcept : 14 | _curve{ std::move(curve) }, 15 | _Q{ std::move(Q) } 16 | {} 17 | 18 | /** 19 | * Comparison operators 20 | * 21 | * @param other The object to compare with 22 | */ 23 | bool eddsa_public_key::operator==(const eddsa_public_key &other) const noexcept 24 | { 25 | return curve() == other.curve() && Q() == other.Q(); 26 | } 27 | 28 | /** 29 | * Comparison operators 30 | * 31 | * @param other The object to compare with 32 | */ 33 | bool eddsa_public_key::operator!=(const eddsa_public_key &other) const noexcept 34 | { 35 | return !operator==(other); 36 | } 37 | 38 | /** 39 | * Determine the size used in encoded format 40 | * @return The number of bytes used for encoded storage 41 | */ 42 | size_t eddsa_public_key::size() const noexcept 43 | { 44 | // we need to store the curve oid and the curve point 45 | return _curve.size() + _Q.size(); 46 | } 47 | 48 | /** 49 | * Retrieve the curve object identifier 50 | * 51 | * @return The curve object identifier 52 | */ 53 | const curve_oid &eddsa_public_key::curve() const noexcept 54 | { 55 | // return the curve object identifier 56 | return _curve; 57 | } 58 | 59 | /** 60 | * Retrieve the public curve point Q 61 | * 62 | * @return The public curve point Q, in compressed format 63 | */ 64 | const multiprecision_integer &eddsa_public_key::Q() const noexcept 65 | { 66 | // return the public point 67 | return _Q; 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /source/eddsa_secret_key.cpp: -------------------------------------------------------------------------------- 1 | #include "eddsa_secret_key.h" 2 | #include 3 | 4 | 5 | namespace pgp { 6 | 7 | /** 8 | * Constructor 9 | * 10 | * @param k The secret scalar for the public point 11 | */ 12 | eddsa_secret_key::eddsa_secret_key(multiprecision_integer k) noexcept : 13 | _k{ std::move(k) } 14 | {} 15 | 16 | /** 17 | * Comparison operators 18 | * 19 | * @param other The object to compare with 20 | */ 21 | bool eddsa_secret_key::operator==(const eddsa_secret_key &other) const noexcept 22 | { 23 | return k() == other.k(); 24 | } 25 | 26 | /** 27 | * Comparison operators 28 | * 29 | * @param other The object to compare with 30 | */ 31 | bool eddsa_secret_key::operator!=(const eddsa_secret_key &other) const noexcept 32 | { 33 | return !operator==(other); 34 | } 35 | 36 | /** 37 | * Determine the size used in encoded format 38 | * @return The number of bytes used for encoded storage 39 | */ 40 | size_t eddsa_secret_key::size() const noexcept 41 | { 42 | // we need to store the secret scalar 43 | return _k.size(); 44 | } 45 | 46 | /** 47 | * Retrieve the secret scalar 48 | * 49 | * @return The secret scalar for the public point 50 | */ 51 | const multiprecision_integer &eddsa_secret_key::k() const noexcept 52 | { 53 | // return the stored scalar 54 | return _k; 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /source/eddsa_signature.cpp: -------------------------------------------------------------------------------- 1 | #include "eddsa_signature.h" 2 | #include 3 | 4 | 5 | namespace pgp { 6 | 7 | /** 8 | * Constructor 9 | * 10 | * @param r The EdDSA r value 11 | * @param s The EdDSA s value 12 | */ 13 | eddsa_signature::eddsa_signature(multiprecision_integer r, multiprecision_integer s) noexcept : 14 | _r{ std::move(r) }, 15 | _s{ std::move(s) } 16 | {} 17 | 18 | /** 19 | * Comparison operators 20 | * 21 | * @param other The object to compare with 22 | */ 23 | bool eddsa_signature::operator==(const eddsa_signature &other) const noexcept 24 | { 25 | return r() == other.r() && s() == other.s(); 26 | } 27 | 28 | /** 29 | * Comparison operators 30 | * 31 | * @param other The object to compare with 32 | */ 33 | bool eddsa_signature::operator!=(const eddsa_signature &other) const noexcept 34 | { 35 | return !operator==(other); 36 | } 37 | 38 | /** 39 | * Determine the size used in encoded format 40 | * @return The number of bytes used for encoded storage 41 | */ 42 | size_t eddsa_signature::size() const noexcept 43 | { 44 | // we need space to store both values 45 | return _r.size() + _s.size(); 46 | } 47 | 48 | /** 49 | * Retrieve the EdDSA r value 50 | * 51 | * @return The r value 52 | */ 53 | const multiprecision_integer &eddsa_signature::r() const noexcept 54 | { 55 | // return the r value 56 | return _r; 57 | } 58 | 59 | /** 60 | * Retrieve the EdDSA s value 61 | * 62 | * @return The s value 63 | */ 64 | const multiprecision_integer &eddsa_signature::s() const noexcept 65 | { 66 | // return the s value 67 | return _s; 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /source/eddsa_signature_encoder.cpp: -------------------------------------------------------------------------------- 1 | #include "eddsa_signature_encoder.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "util/span.h" 8 | 9 | 10 | namespace pgp { 11 | 12 | /** 13 | * Make the signature 14 | * 15 | * @return Tuple of the r and s parameters for the EDDSA signature 16 | */ 17 | std::tuple 18 | eddsa_signature_encoder::finalize() noexcept 19 | { 20 | // the buffer for the signed message and the concatenated key 21 | std::array signed_message; 22 | std::array key_data; 23 | 24 | // retrieve the key data - ignore the silly leading byte from the public key 25 | auto public_data = eddsa_key.Q().data().subspan<1>(); 26 | auto secret_data = eddsa_key.k().data(); 27 | 28 | // make sure the key fits within the provided key_data structure 29 | assert(public_data.size() <= 32); 30 | assert(secret_data.size() <= 32); 31 | 32 | // the iterator to work with 33 | auto iter = key_data.begin(); 34 | 35 | // if leading key bytes were missing (due to them being zero) we have to 36 | // prefill this with zeroes to avoid libsodium being fed uninitialized data 37 | iter = std::fill_n(iter, 32 - secret_data.size(), 0); 38 | iter = std::copy(secret_data.begin(), secret_data.end(), iter); 39 | 40 | // the public key data might also be missing leading bytes if they were zero 41 | // so we should similary prefill it 42 | iter = std::fill_n(iter, 32 - public_data.size(), 0); 43 | iter = std::copy(public_data.begin(), public_data.end(), iter); 44 | 45 | // get the digest to sign 46 | auto digest_data = digest(); 47 | 48 | // now sign the message 49 | crypto_sign_detached(signed_message.data(), nullptr, digest_data.data(), digest_data.size(), key_data.data()); 50 | 51 | // split up the data and return it 52 | return std::make_tuple( 53 | multiprecision_integer{span{ signed_message.data(), 32 }}, 54 | multiprecision_integer{span{ signed_message.data() + 32, 32 }} 55 | ); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /source/elgamal_public_key.cpp: -------------------------------------------------------------------------------- 1 | #include "elgamal_public_key.h" 2 | #include 3 | 4 | 5 | namespace pgp { 6 | 7 | /** 8 | * Constructor 9 | * 10 | * @param p The prime p 11 | * @param g The group generator g 12 | * @param y The public key value: g**x mod p 13 | */ 14 | elgamal_public_key::elgamal_public_key(multiprecision_integer p, multiprecision_integer g, multiprecision_integer y) noexcept : 15 | _p{ std::move(p) }, 16 | _g{ std::move(g) }, 17 | _y{ std::move(y) } 18 | {} 19 | 20 | /** 21 | * Comparison operators 22 | * 23 | * @param other The object to compare with 24 | */ 25 | bool elgamal_public_key::operator==(const elgamal_public_key &other) const noexcept 26 | { 27 | return p() == other.p() && g() == other.g() && y() == other.y(); 28 | } 29 | 30 | /** 31 | * Comparison operators 32 | * 33 | * @param other The object to compare with 34 | */ 35 | bool elgamal_public_key::operator!=(const elgamal_public_key &other) const noexcept 36 | { 37 | return !operator==(other); 38 | } 39 | 40 | /** 41 | * Determine the size used in encoded format 42 | * @return The number of bytes used for encoded storage 43 | */ 44 | size_t elgamal_public_key::size() const noexcept 45 | { 46 | // we need to store all the components 47 | return _p.size() + _g.size() + _y.size(); 48 | } 49 | 50 | /** 51 | * Retrieve the the prime 52 | * 53 | * @return The prime p 54 | */ 55 | const multiprecision_integer &elgamal_public_key::p() const noexcept 56 | { 57 | // return the stored prime 58 | return _p; 59 | } 60 | 61 | /** 62 | * Retrieve the group generator g 63 | * 64 | * @return The group generator g 65 | */ 66 | const multiprecision_integer &elgamal_public_key::g() const noexcept 67 | { 68 | // return the stored generator 69 | return _g; 70 | } 71 | 72 | /** 73 | * Retrieve the public key value 74 | * 75 | * @return The public key value 76 | */ 77 | const multiprecision_integer &elgamal_public_key::y() const noexcept 78 | { 79 | // return the stored public key value 80 | return _y; 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /source/elgamal_secret_key.cpp: -------------------------------------------------------------------------------- 1 | #include "elgamal_secret_key.h" 2 | #include 3 | 4 | 5 | namespace pgp { 6 | 7 | /** 8 | * Constructor 9 | * 10 | * @param x The secret exponent x 11 | */ 12 | elgamal_secret_key::elgamal_secret_key(multiprecision_integer x) noexcept : 13 | _x{ std::move(x) } 14 | {} 15 | 16 | /** 17 | * Comparison operators 18 | * 19 | * @param other The object to compare with 20 | */ 21 | bool elgamal_secret_key::operator==(const elgamal_secret_key &other) const noexcept 22 | { 23 | return x() == other.x(); 24 | } 25 | 26 | /** 27 | * Comparison operators 28 | * 29 | * @param other The object to compare with 30 | */ 31 | bool elgamal_secret_key::operator!=(const elgamal_secret_key &other) const noexcept 32 | { 33 | return !operator==(other); 34 | } 35 | 36 | /** 37 | * Determine the size used in encoded format 38 | * @return The number of bytes used for encoded storage 39 | */ 40 | size_t elgamal_secret_key::size() const noexcept 41 | { 42 | // we need the size for our secret exponent 43 | return _x.size(); 44 | } 45 | 46 | /** 47 | * Retrieve the secret exponent 48 | * 49 | * @return The secret exponent x 50 | */ 51 | const multiprecision_integer &elgamal_secret_key::x() const noexcept 52 | { 53 | // return the secret exponent 54 | return _x; 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /source/packet.cpp: -------------------------------------------------------------------------------- 1 | #include "packet.h" 2 | #include "util/narrow_cast.h" 3 | 4 | 5 | namespace pgp { 6 | 7 | /** 8 | * Comparison operators 9 | * 10 | * @param other The object to compare with 11 | */ 12 | bool packet::operator==(const packet &other) const noexcept 13 | { 14 | return body() == other.body(); 15 | } 16 | 17 | /** 18 | * Comparison operators 19 | * 20 | * @param other The object to compare with 21 | */ 22 | bool packet::operator!=(const packet &other) const noexcept 23 | { 24 | return !operator==(other); 25 | } 26 | 27 | /** 28 | * Retrieve the packet tag 29 | * @return The packet tag, as described in https://tools.ietf.org/html/rfc4880#section-4.3 30 | */ 31 | packet_tag packet::tag() const noexcept 32 | { 33 | // the tag to return 34 | packet_tag result; 35 | 36 | // retrieve the body 37 | visit([&result](auto &body) { 38 | // retrieve the tag from the body 39 | result = body.tag(); 40 | }, _body); 41 | 42 | // return the retrieved tag 43 | return result; 44 | } 45 | 46 | /** 47 | * Retrieve the body length 48 | * 49 | * Determine the size used in encoded format 50 | * @return The number of bytes used for encoded storage 51 | */ 52 | size_t packet::size() const 53 | { 54 | // the body size to return 55 | uint32_t result; 56 | 57 | // retrieve the body 58 | visit([&result](auto &body) { 59 | // retrieve the size from the body 60 | result = util::narrow_cast(body.size()); 61 | }, _body); 62 | 63 | // is the packet compatible with the old format? 64 | if (packet_tag_compatible_with_old_format(tag())) { 65 | // determine the storage type used 66 | if (result > 65535) { 67 | // we need four bytes for the body length, plus 68 | // an additional byte for storing the packet tag 69 | return result + 1 + 4; 70 | } else if (result > 255) { 71 | // we need two bytes for the body length, plus an 72 | // additional byte for storing the packet tag 73 | return result + 1 + 2; 74 | } else { 75 | // we need a single byte for the body length, plus 76 | // an additional byte for storing the packet tag 77 | return result + 1 + 1; 78 | } 79 | } else { 80 | // we need one byte for the tag and a variable number 81 | // to store the length of the body data 82 | return result + 1 + variable_number{ result }.size(); 83 | } 84 | } 85 | 86 | /** 87 | * Retrieve the decoded packet 88 | * 89 | * @return The packet that was parsed 90 | */ 91 | const packet::packet_variant &packet::body() const noexcept 92 | { 93 | // return the decoded body 94 | return _body; 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /source/range_encoder.cpp: -------------------------------------------------------------------------------- 1 | #include "range_encoder.h" 2 | 3 | 4 | namespace pgp { 5 | 6 | /** 7 | * Constructor 8 | * 9 | * @param data The range to encode to 10 | */ 11 | range_encoder::range_encoder(span data) : 12 | _data{ data } 13 | {} 14 | 15 | /** 16 | * Flush the encoder, so any partial-written bytes 17 | * are written out. Note that after this operation, 18 | * bitwise operations start at the beginning again. 19 | */ 20 | void range_encoder::flush() noexcept 21 | { 22 | // do we have any partially-filled bytes? 23 | if (_skip_bits > 0) { 24 | // write out the current byte 25 | _data[_size] = _current; 26 | 27 | // move to the next byte 28 | ++_size; 29 | _current = 0; 30 | _skip_bits = 0; 31 | } 32 | } 33 | 34 | /** 35 | * Retrieve the number of encoded bytes 36 | * @return The number of bytes stored in the encoder 37 | */ 38 | size_t range_encoder::size() const noexcept 39 | { 40 | // return the stored size 41 | return _size; 42 | } 43 | 44 | /** 45 | * Insert one or more bits 46 | * 47 | * @param count The number of bits to insert 48 | * @param value The value to store in the bits 49 | * @return self, for chaining 50 | * @throws std::out_of_range, std::range_error 51 | */ 52 | range_encoder &range_encoder::insert_bits(size_t count, uint8_t value) 53 | { 54 | // check whether the number fits within the given bit-size 55 | if (value > (1U << count) - 1U) { 56 | // the value is too large to encode 57 | throw std::range_error{ "Cannot encode value, too large for given bit-size" }; 58 | } 59 | 60 | // the write may not cross a byte boundary 61 | if (count + _skip_bits > 8) { 62 | // cannot encode the value, does not fit within byte 63 | throw std::out_of_range{ "Cannot encode value, bit-wise operation may not cross byte boundaries" }; 64 | } 65 | 66 | if (_size >= _data.size()) { 67 | // trying to write out-of-bounds 68 | throw std::out_of_range{ "Buffer too small for inserting bits" }; 69 | } 70 | 71 | // shift the data so it fits with the existing data and add it 72 | _current |= static_cast(value << static_cast(8U - _skip_bits - count)); 73 | 74 | // do we move on to the next byte? 75 | if (count + _skip_bits == 8) { 76 | // store the byte now 77 | _data[_size] = _current; 78 | _current = 0; 79 | 80 | // move to the next byte 81 | ++_size; 82 | _skip_bits = 0; 83 | } else { 84 | // just increment the bits to skip 85 | _skip_bits += count; 86 | } 87 | 88 | // allow chaining 89 | return *this; 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /source/rsa_public_key.cpp: -------------------------------------------------------------------------------- 1 | #include "rsa_public_key.h" 2 | #include 3 | 4 | 5 | namespace pgp { 6 | 7 | /** 8 | * Constructor 9 | * 10 | * @param n The public modulus n 11 | * @param e The encryption exponent e 12 | */ 13 | rsa_public_key::rsa_public_key(multiprecision_integer n, multiprecision_integer e) noexcept : 14 | _n{ std::move(n) }, 15 | _e{ std::move(e) } 16 | {} 17 | 18 | /** 19 | * Comparison operators 20 | * 21 | * @param other The object to compare with 22 | */ 23 | bool rsa_public_key::operator==(const rsa_public_key &other) const noexcept 24 | { 25 | return n() == other.n() && e() == other.e(); 26 | } 27 | 28 | /** 29 | * Comparison operators 30 | * 31 | * @param other The object to compare with 32 | */ 33 | bool rsa_public_key::operator!=(const rsa_public_key &other) const noexcept 34 | { 35 | return !operator==(other); 36 | } 37 | 38 | /** 39 | * Determine the size used in encoded format 40 | * @return The number of bytes used for encoded storage 41 | */ 42 | size_t rsa_public_key::size() const noexcept 43 | { 44 | // we need to store the n and e components 45 | return _n.size() + _e.size(); 46 | } 47 | 48 | /** 49 | * Retrieve the public modulus n 50 | * 51 | * @return The modulus n for the key 52 | */ 53 | const multiprecision_integer &rsa_public_key::n() const noexcept 54 | { 55 | // return the stored modulus 56 | return _n; 57 | } 58 | 59 | /** 60 | * Retrieve the encryption exponent e 61 | * 62 | * @return The encryption exponent e 63 | */ 64 | const multiprecision_integer &rsa_public_key::e() const noexcept 65 | { 66 | // return the stored exponent 67 | return _e; 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /source/rsa_secret_key.cpp: -------------------------------------------------------------------------------- 1 | #include "rsa_secret_key.h" 2 | #include 3 | 4 | 5 | namespace pgp { 6 | 7 | /** 8 | * Constructor 9 | * 10 | * @param d The secret exponent d 11 | * @param p The secret prime value p 12 | * @param q The secret prime value q 13 | * @param u The multiplicative inverse p mod q 14 | */ 15 | rsa_secret_key::rsa_secret_key(multiprecision_integer d, multiprecision_integer p, multiprecision_integer q, multiprecision_integer u) noexcept : 16 | _d{ std::move(d) }, 17 | _p{ std::move(p) }, 18 | _q{ std::move(q) }, 19 | _u{ std::move(u) } 20 | {} 21 | 22 | /** 23 | * Comparison operators 24 | * 25 | * @param other The object to compare with 26 | */ 27 | bool rsa_secret_key::operator==(const rsa_secret_key &other) const noexcept 28 | { 29 | return d() == other.d() && p() == other.p() && q() == other.q() && u() == other.u(); 30 | } 31 | 32 | /** 33 | * Comparison operators 34 | * 35 | * @param other The object to compare with 36 | */ 37 | bool rsa_secret_key::operator!=(const rsa_secret_key &other) const noexcept 38 | { 39 | return !operator==(other); 40 | } 41 | 42 | /** 43 | * Determine the size used in encoded format 44 | * @return The number of bytes used for encoded storage 45 | */ 46 | size_t rsa_secret_key::size() const noexcept 47 | { 48 | // we need the size of secret components 49 | return _d.size() + _p.size() + _q.size() + _u.size(); 50 | } 51 | 52 | /** 53 | * Retrieve the secret exponent d 54 | * 55 | * @return The secret exponent 56 | */ 57 | const multiprecision_integer &rsa_secret_key::d() const noexcept 58 | { 59 | // return the storet exponent 60 | return _d; 61 | } 62 | 63 | /** 64 | * Retrieve the secret prime value p 65 | * 66 | * @return The secret prime value p 67 | */ 68 | const multiprecision_integer &rsa_secret_key::p() const noexcept 69 | { 70 | // return the stored prime 71 | return _p; 72 | } 73 | 74 | /** 75 | * Retrieve the secret prime value q 76 | * 77 | * @return The secret prime value q 78 | */ 79 | const multiprecision_integer &rsa_secret_key::q() const noexcept 80 | { 81 | // return the stored prime 82 | return _q; 83 | } 84 | 85 | /** 86 | * Retrieve the u value 87 | * 88 | * @return The multiplicative inverse of p mod q 89 | */ 90 | const multiprecision_integer &rsa_secret_key::u() const noexcept 91 | { 92 | // return the stored multiplicative 93 | return _u; 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /source/rsa_signature.cpp: -------------------------------------------------------------------------------- 1 | #include "rsa_signature.h" 2 | #include 3 | 4 | 5 | namespace pgp { 6 | 7 | /** 8 | * Constructor 9 | * 10 | * @param s The signature value (m**d mod n) 11 | */ 12 | rsa_signature::rsa_signature(multiprecision_integer s) noexcept : 13 | _s{ std::move(s) } 14 | {} 15 | 16 | /** 17 | * Comparison operators 18 | * 19 | * @param other The object to compare with 20 | */ 21 | bool rsa_signature::operator==(const rsa_signature &other) const noexcept 22 | { 23 | return s() == other.s(); 24 | } 25 | 26 | /** 27 | * Comparison operators 28 | * 29 | * @param other The object to compare with 30 | */ 31 | bool rsa_signature::operator!=(const rsa_signature &other) const noexcept 32 | { 33 | return !operator==(other); 34 | } 35 | 36 | /** 37 | * Determine the size used in encoded format 38 | * @return The number of bytes used for encoded storage 39 | */ 40 | size_t rsa_signature::size() const noexcept 41 | { 42 | // we only need space for encoding the signature 43 | return _s.size(); 44 | } 45 | 46 | /** 47 | * Retrieve the signature value 48 | * 49 | * @return The signature value (m**d mod n) 50 | */ 51 | const multiprecision_integer &rsa_signature::s() const noexcept 52 | { 53 | // return the signature 54 | return _s; 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /source/rsa_signature_encoder.cpp: -------------------------------------------------------------------------------- 1 | #include "rsa_signature_encoder.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | namespace pgp { 9 | 10 | /** 11 | * Retrieve the RSA s parameter of the final sigature 12 | * 13 | * This method should be called *at most once*. 14 | * 15 | * @return The signature of the data 16 | */ 17 | std::tuple rsa_signature_encoder::finalize() noexcept 18 | { 19 | // construct a Crypto++ private key; we also have p, q, u at our 20 | // disposal, but Crypto++'s extended constructor needs dp and dq as 21 | // well, which we don't have 22 | CryptoPP::RSA::PrivateKey k1; 23 | k1.Initialize( 24 | static_cast(rsa_key.n()), 25 | static_cast(rsa_key.e()), 26 | static_cast(rsa_key.d()) 27 | ); 28 | 29 | // construct the RSA signer 30 | signer_t signer{k1}; 31 | 32 | // construct the target buffer for the signature 33 | vector signed_message; 34 | signed_message.resize(signer.MaxSignatureLength()); 35 | 36 | // sign the message, and resize the buffer to the actual size 37 | size_t actual_length = signer.Sign(_prng, _signature_context.get(), signed_message.data()); 38 | signed_message.resize(actual_length); 39 | 40 | // the Sign() method deallocated the accumulator, so forget the reference to it 41 | [[maybe_unused]] auto _ = _signature_context.release(); 42 | 43 | // return the signature parameter 44 | return std::make_tuple(pgp::multiprecision_integer{ std::move(signed_message) }); 45 | } 46 | 47 | /** 48 | * Retrieve the hash prefix: the first two bytes of the hash 49 | * 50 | * This method should be called *at most once*. 51 | * 52 | * @return The two-byte prefix of the hash of the data 53 | */ 54 | std::array rsa_signature_encoder::hash_prefix() noexcept 55 | { 56 | // the buffer to store the prefix in 57 | std::array result; 58 | 59 | // obtain the prefix from the hash context 60 | _hash_context.TruncatedFinal(result.data(), 2); 61 | 62 | // return the prefix 63 | return result; 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /source/signature_subpacket/embedded.cpp: -------------------------------------------------------------------------------- 1 | #include "signature_subpacket/embedded.h" 2 | #include "variable_number.h" 3 | #include "signature.h" 4 | 5 | 6 | namespace pgp::signature_subpacket { 7 | 8 | /** 9 | * Constructor 10 | * 11 | * @param value The value to store 12 | */ 13 | template 14 | embedded::embedded(contained_t value) : 15 | _contained{ std::make_unique(value) } 16 | {} 17 | 18 | /** 19 | * Copy constructor 20 | */ 21 | template 22 | embedded::embedded(const embedded &other) : 23 | _contained{ std::make_unique(other.contained()) } 24 | {} 25 | 26 | /** 27 | * Destructor 28 | */ 29 | template 30 | embedded::~embedded() = default; 31 | 32 | /** 33 | * Copy assignment operator 34 | */ 35 | template 36 | embedded & 37 | embedded::operator=(const embedded &other) noexcept 38 | { 39 | // copy the contained object 40 | _contained = std::make_unique(other.contained()); 41 | 42 | // allow chaining 43 | return *this; 44 | } 45 | 46 | /** 47 | * Comparison operators 48 | * 49 | * Precondition: neither this nor the object compared to have been moved from. 50 | * 51 | * @param other The object to compare with 52 | */ 53 | template 54 | bool embedded::operator==(const embedded &other) const noexcept 55 | { 56 | return type() == other.type() && contained() == other.contained(); 57 | } 58 | 59 | /** 60 | * Comparison operators 61 | * 62 | * Precondition: neither this nor the object compared to have been moved from. 63 | * 64 | * @param other The object to compare with 65 | */ 66 | template 67 | bool embedded::operator!=(const embedded &other) const noexcept 68 | { 69 | return !operator==(other); 70 | } 71 | 72 | /** 73 | * Determine the size used in encoded format 74 | * @return The number of bytes used for encoded storage 75 | */ 76 | template 77 | size_t embedded::size() const noexcept 78 | { 79 | // we need to store the value plus the type 80 | uint32_t size = _contained->size() + sizeof(type()); 81 | 82 | // and then store this number in a variable number 83 | return size + variable_number{ size }.size(); 84 | } 85 | 86 | /** 87 | * Retrieve the stored signature 88 | * 89 | * @return The stored signature 90 | */ 91 | template 92 | const contained_t &embedded::contained() const 93 | { 94 | if (_contained) { 95 | return *_contained; 96 | } else { 97 | throw std::runtime_error("contained() on an empty signature_subpacket::embedded"); 98 | } 99 | } 100 | 101 | 102 | /** 103 | * Explicit template instantiation for embedded_signature 104 | */ 105 | template class embedded; 106 | 107 | } 108 | -------------------------------------------------------------------------------- /source/signature_subpacket/issuer_fingerprint.cpp: -------------------------------------------------------------------------------- 1 | #include "signature_subpacket/issuer_fingerprint.h" 2 | #include "variable_number.h" 3 | #include "util/narrow_cast.h" 4 | 5 | 6 | namespace pgp::signature_subpacket { 7 | 8 | /** 9 | * Constructor 10 | * 11 | * @param data The array of data 12 | */ 13 | issuer_fingerprint::issuer_fingerprint(std::array data) noexcept : 14 | _data{ data } 15 | {} 16 | 17 | /** 18 | * Comparison operators 19 | * 20 | * @param other The object to compare with 21 | */ 22 | bool issuer_fingerprint::operator==(const issuer_fingerprint &other) const noexcept 23 | { 24 | return data() == other.data(); 25 | } 26 | 27 | /** 28 | * Comparison operators 29 | * 30 | * @param other The object to compare with 31 | */ 32 | bool issuer_fingerprint::operator!=(const issuer_fingerprint &other) const noexcept 33 | { 34 | return !operator==(other); 35 | } 36 | 37 | /** 38 | * Determine the size used in encoded format 39 | * @return The number of bytes used for encoded storage 40 | */ 41 | size_t issuer_fingerprint::size() const noexcept 42 | { 43 | // we need to store the number, together with the type and the key version 44 | auto size = util::narrow_cast(_data.size() + _version.size() + sizeof(type())); 45 | 46 | // and then store this number in a variable number 47 | return size + variable_number{ size }.size(); 48 | } 49 | 50 | /** 51 | * Retrieve the stored array 52 | * 53 | * @return The stored array 54 | */ 55 | const std::array &issuer_fingerprint::data() const noexcept 56 | { 57 | // retrieve the stored array 58 | return _data; 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /source/signature_subpacket/unknown.cpp: -------------------------------------------------------------------------------- 1 | #include "signature_subpacket/unknown.h" 2 | #include 3 | #include "signature_subpacket_type.h" 4 | #include "variable_number.h" 5 | #include "util/narrow_cast.h" 6 | 7 | 8 | namespace pgp::signature_subpacket { 9 | 10 | /** 11 | * Constructor 12 | * 13 | * @param type The signature subpacket type 14 | * @param data The data contained in the subpacket 15 | */ 16 | unknown::unknown(signature_subpacket_type type, span data) : 17 | _type{ type }, 18 | _data{ data.begin(), data.end() } 19 | {} 20 | 21 | /** 22 | * Comparison operators 23 | * 24 | * @param other The object to compare with 25 | */ 26 | bool unknown::operator==(const unknown &other) const noexcept 27 | { 28 | return _data.size() == other._data.size() && std::equal(_data.begin(), _data.end(), other._data.begin()); 29 | } 30 | 31 | /** 32 | * Comparison operators 33 | * 34 | * @param other The object to compare with 35 | */ 36 | bool unknown::operator!=(const unknown &other) const noexcept 37 | { 38 | return !operator==(other); 39 | } 40 | 41 | /** 42 | * Determine the size used in encoded format 43 | * @return The number of bytes used for encoded storage 44 | */ 45 | size_t unknown::size() const noexcept 46 | { 47 | // we need to encode the type and the data and encode that 48 | // size again using variable-length packet encoding 49 | auto size = util::narrow_cast(sizeof(_type) + _data.size()); 50 | 51 | // now add the size necessary to encode the size itself 52 | return size + variable_number{ size }.size(); 53 | } 54 | 55 | /** 56 | * Get the signature subpacket type 57 | * @return The type of signature subpacket 58 | */ 59 | signature_subpacket_type unknown::type() const noexcept 60 | { 61 | // return the stored type 62 | return _type; 63 | } 64 | 65 | /** 66 | * Retrieve the data 67 | * @return A span containing all the integer numbers 68 | */ 69 | span unknown::data() const noexcept 70 | { 71 | // return the stored data 72 | return _data; 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /source/signature_subpacket_set.cpp: -------------------------------------------------------------------------------- 1 | #include "signature_subpacket_set.h" 2 | #include "variable_number.h" 3 | #include "fixed_number.h" 4 | #include "signature.h" 5 | #include 6 | 7 | 8 | namespace pgp { 9 | 10 | /** 11 | * Constructor 12 | * 13 | * @param subpackets The subpackets to keep in the set 14 | */ 15 | signature_subpacket_set::signature_subpacket_set(std::vector subpackets) noexcept : 16 | _subpackets{ std::move(subpackets) } 17 | {} 18 | 19 | /** 20 | * Comparison operators 21 | * 22 | * @param other The object to compare with 23 | */ 24 | bool signature_subpacket_set::operator==(const pgp::signature_subpacket_set &other) const noexcept 25 | { 26 | return data() == other.data(); 27 | } 28 | 29 | /** 30 | * Comparison operators 31 | * 32 | * @param other The object to compare with 33 | */ 34 | bool signature_subpacket_set::operator!=(const pgp::signature_subpacket_set &other) const noexcept 35 | { 36 | return !operator==(other); 37 | } 38 | 39 | /** 40 | * Determine the size used in encoded format 41 | * @return The number of bytes used for encoded storage 42 | */ 43 | size_t signature_subpacket_set::size() const noexcept 44 | { 45 | // allocate size for the header and add size for all the packets 46 | return std::accumulate(_subpackets.begin(), _subpackets.end(), uint16::size(), [](uint16_t a, const subpacket_variant &b) { 47 | // retrieve the correct subpacket type 48 | visit([&a](auto &&subpacket) { 49 | // add the size of the subpacket 50 | a += subpacket.size(); 51 | }, b); 52 | 53 | // return the increased size 54 | return a; 55 | }); 56 | } 57 | 58 | /** 59 | * Retrieve a specific subpacket 60 | * 61 | * @param offset The offset for the subpacket to receive 62 | * @throws std::out_of_range 63 | */ 64 | const signature_subpacket_set::subpacket_variant &signature_subpacket_set::operator[](size_t offset) const 65 | { 66 | // retrieve subpacket at requested offset 67 | return _subpackets[offset]; 68 | } 69 | 70 | /** 71 | * Retrieve all subpackets 72 | * 73 | * @return The subpackets in the set 74 | */ 75 | span signature_subpacket_set::data() const noexcept 76 | { 77 | // return the stored subpackets 78 | return _subpackets; 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /source/string_to_key.cpp: -------------------------------------------------------------------------------- 1 | #include "string_to_key.h" 2 | 3 | 4 | namespace pgp { 5 | 6 | /** 7 | * Comparison operators 8 | * 9 | * @param other The object to compare with 10 | */ 11 | bool string_to_key::operator==(const string_to_key &other) const noexcept 12 | { 13 | return convention() == other.convention(); 14 | } 15 | 16 | /** 17 | * Comparison operators 18 | * 19 | * @param other The object to compare with 20 | */ 21 | bool string_to_key::operator!=(const string_to_key &other) const noexcept 22 | { 23 | return !operator==(other); 24 | } 25 | 26 | /** 27 | * Determine the size used in encoded format 28 | * @return The number of bytes used for encoded storage 29 | */ 30 | size_t string_to_key::size() const noexcept 31 | { 32 | // return the size of the convention 33 | return _convention.size(); 34 | } 35 | 36 | /** 37 | * Retrieve the convention used 38 | * 39 | * @return The string-to-key convention 40 | */ 41 | uint8_t string_to_key::convention() const noexcept 42 | { 43 | // return the stored convention 44 | return _convention; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /source/unknown_signature_encoder.cpp: -------------------------------------------------------------------------------- 1 | #include "unknown_signature_encoder.h" 2 | 3 | 4 | namespace pgp { 5 | 6 | 7 | /** 8 | * Get the hash prefix of a nonexistent encoder; throws. 9 | */ 10 | std::array unknown_signature_encoder::hash_prefix() 11 | { 12 | throw std::runtime_error{ "Unknown signatures cannot sign streamed data" }; 13 | } 14 | 15 | /** 16 | * Get the finalized parameters of a nonexistent encoder; throws. 17 | */ 18 | std::tuple<> unknown_signature_encoder::finalize() 19 | { 20 | throw std::runtime_error{ "Unknown signatures cannot sign streamed data" }; 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /source/user_id.cpp: -------------------------------------------------------------------------------- 1 | #include "user_id.h" 2 | #include 3 | 4 | 5 | namespace pgp { 6 | 7 | /** 8 | * Constructor 9 | * 10 | * @param id The user id to use 11 | */ 12 | user_id::user_id(span id) noexcept : 13 | _id{ id.data(), static_cast(id.size()) } 14 | {} 15 | 16 | /** 17 | * Constructor 18 | * 19 | * @param id The user id to use 20 | */ 21 | user_id::user_id(std::string id) noexcept : 22 | _id{ std::move(id) } 23 | {} 24 | 25 | /** 26 | * Comparison operators 27 | * 28 | * @param other The object to compare with 29 | */ 30 | bool user_id::operator==(const user_id &other) const noexcept 31 | { 32 | return id() == other.id(); 33 | } 34 | 35 | /** 36 | * Comparison operators 37 | * 38 | * @param other The object to compare with 39 | */ 40 | bool user_id::operator!=(const user_id &other) const noexcept 41 | { 42 | return !operator==(other); 43 | } 44 | 45 | /** 46 | * Determine the size used in encoded format 47 | * @return The number of bytes used for encoded storage 48 | * @throws std::runtime_error for unknown key types 49 | */ 50 | size_t user_id::size() const noexcept 51 | { 52 | // retrieve the size of the id 53 | return _id.size(); 54 | } 55 | 56 | /** 57 | * Retrieve the user id 58 | * 59 | * @return The user id 60 | */ 61 | const std::string &user_id::id() const noexcept 62 | { 63 | // return the stored id 64 | return _id; 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /source/variable_number.cpp: -------------------------------------------------------------------------------- 1 | #include "variable_number.h" 2 | 3 | 4 | namespace pgp { 5 | 6 | /** 7 | * Constructor 8 | * 9 | * @param value The value to hold 10 | */ 11 | variable_number::variable_number(uint32_t value) noexcept : 12 | _value{ value } 13 | {} 14 | 15 | /** 16 | * Assignment operator 17 | * 18 | * @param value The value to assign 19 | * @return self, for chaining 20 | */ 21 | variable_number &variable_number::operator=(uint32_t value) noexcept 22 | { 23 | // update value 24 | _value = value; 25 | 26 | // allow chaining 27 | return *this; 28 | } 29 | 30 | /** 31 | * Determine the size used in encoded format 32 | * 33 | * @return The number of bytes used for encoded storage 34 | */ 35 | size_t variable_number::size() const noexcept 36 | { 37 | // size depends on the stored value 38 | if (_value < 192) { 39 | // this can be done in a single octet 40 | return 1; 41 | } else if (_value < 8384) { 42 | // this will use two octets 43 | return 2; 44 | } else { 45 | // we will use five octets instead 46 | return 5; 47 | } 48 | } 49 | 50 | /** 51 | * Extract the stored value 52 | * 53 | * @return The stored value 54 | */ 55 | variable_number::operator uint32_t() const noexcept 56 | { 57 | // return the stored value 58 | return _value; 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /tests/device_random_engine.cpp: -------------------------------------------------------------------------------- 1 | #include "device_random_engine.h" 2 | 3 | 4 | namespace tests { 5 | device_random_engine::device_random_engine() : 6 | engine(std::random_device()()) 7 | {} 8 | 9 | device_random_engine::result_type device_random_engine::operator()() noexcept 10 | { 11 | return engine(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /tests/device_random_engine.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | 6 | namespace tests { 7 | class device_random_engine { 8 | public: 9 | using driver_engine = std::mt19937; 10 | using result_type = driver_engine::result_type; 11 | 12 | device_random_engine(); 13 | 14 | result_type operator()() noexcept; 15 | 16 | static constexpr result_type min() noexcept 17 | { 18 | return driver_engine::min(); 19 | } 20 | static constexpr result_type max() noexcept 21 | { 22 | return driver_engine::max(); 23 | } 24 | 25 | private: 26 | driver_engine engine; 27 | }; 28 | } 29 | -------------------------------------------------------------------------------- /tests/generate.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "symmetric_key_algorithm.h" 4 | #include "multiprecision_integer.h" 5 | #include "device_random_engine.h" 6 | #include 7 | #include "hash_algorithm.h" 8 | #include "secret_key.h" 9 | #include "curve_oid.h" 10 | #include 11 | #include 12 | 13 | 14 | namespace tests::generate { 15 | namespace detail { 16 | extern thread_local device_random_engine random_engine; 17 | } 18 | 19 | template 20 | T random_choice(std::vector options) 21 | { 22 | std::uniform_int_distribution distr(0, options.size() - 1); 23 | return options[distr(detail::random_engine)]; 24 | } 25 | 26 | pgp::multiprecision_integer mpi(); 27 | 28 | pgp::curve_oid oid(); 29 | 30 | pgp::hash_algorithm hashalgo(); 31 | 32 | pgp::symmetric_key_algorithm keyalgo(); 33 | 34 | namespace eddsa { 35 | constexpr const std::array public_key_tag{0x40}; 36 | constexpr const size_t public_key_size = public_key_tag.size() + crypto_sign_PUBLICKEYBYTES; 37 | constexpr const size_t secret_key_size = crypto_sign_SECRETKEYBYTES - crypto_sign_PUBLICKEYBYTES; 38 | 39 | /** 40 | * Generate an EDDSA secret key. 41 | * 42 | * @return The secret key, as well as the public and secret key data. 43 | */ 44 | std::tuple< 45 | pgp::secret_key, 46 | std::array, 47 | std::array 48 | > key(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tests/key_template.cpp: -------------------------------------------------------------------------------- 1 | #include "key_template.h" 2 | 3 | 4 | namespace tests { 5 | namespace detail { 6 | std::ostream& operator<<(std::ostream &os, const pgp::span &sp) 7 | { 8 | os << '{'; 9 | bool first = true; 10 | for (const uint8_t byte : sp) { 11 | if (first) first = false; 12 | else os << ", "; 13 | os << std::setw(2) << std::setfill('0') << static_cast(byte); 14 | } 15 | os << '}'; 16 | return os; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tests/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | 5 | int main(int argc, char **argv) { 6 | // ensure libsodium is initialized 7 | if (sodium_init() == -1) { 8 | // cannot run tests without libsodium 9 | throw std::runtime_error{ "Failed to initialize libsodium" }; 10 | } 11 | 12 | testing::InitGoogleTest(&argc, argv); 13 | return RUN_ALL_TESTS(); 14 | } 15 | -------------------------------------------------------------------------------- /tests/unit_tests/curve_oid.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "curve_oid.h" 7 | #include "decoder.h" 8 | #include "util/narrow_cast.h" 9 | 10 | 11 | TEST(curve_oid, size_preset_curves) 12 | { 13 | auto ed_oid = pgp::curve_oid::ed25519(); 14 | auto ed_data = ed_oid.data(); 15 | 16 | size_t num_bytes = ed_data.size(); 17 | 18 | ASSERT_EQ(ed_oid.size(), num_bytes + 1); 19 | 20 | std::vector data(num_bytes + 1); 21 | data[0] = util::narrow_cast(num_bytes); 22 | for (size_t i = 0; i < num_bytes; i++) { 23 | data[i + 1] = ed_data[i]; 24 | } 25 | 26 | pgp::decoder decoder{data}; 27 | pgp::curve_oid oid{decoder}; 28 | ASSERT_EQ(oid, ed_oid); 29 | ASSERT_EQ(oid.data(), ed_data); 30 | } 31 | 32 | TEST(curve_oid, preset_curves) 33 | { 34 | std::array ed25519{ 0x2b, 0x06, 0x01, 0x04, 0x01, 0xda, 0x47, 0x0f, 0x01 }; 35 | std::array curve_25519{ 0x2B, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01 }; 36 | 37 | ASSERT_EQ(pgp::curve_oid::ed25519().data(), pgp::span(ed25519)); 38 | ASSERT_EQ(pgp::curve_oid::curve_25519().data(), pgp::span(curve_25519)); 39 | } 40 | 41 | TEST(curve_oid, equality) 42 | { 43 | ASSERT_NE(pgp::curve_oid::ed25519(), pgp::curve_oid::curve_25519()); 44 | } 45 | 46 | TEST(curve_oid, other_data) 47 | { 48 | std::array data{4, 1, 2, 3, 4}; 49 | pgp::decoder decoder{data}; 50 | pgp::curve_oid oid{decoder}; 51 | 52 | ASSERT_EQ(pgp::span(data.data() + 1, data.data() + 5), oid.data()); 53 | 54 | pgp::curve_oid oid2{data}; 55 | ASSERT_EQ(pgp::span(data), oid2.data()); 56 | } 57 | -------------------------------------------------------------------------------- /tests/unit_tests/device_random_engine.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "device_random_engine.h" 5 | 6 | 7 | TEST(device_random_engine, is_random) 8 | { 9 | using result_type = tests::device_random_engine::result_type; 10 | tests::device_random_engine e1, e2; 11 | 12 | constexpr const size_t size = 64; 13 | std::array data1, data2; 14 | for (size_t i = 0; i < size; i++) data1[i] = e1(); 15 | for (size_t i = 0; i < size; i++) data2[i] = e2(); 16 | 17 | for (size_t i = 0; i < size; i++) { 18 | ASSERT_LE(e1.min(), data1[i]); 19 | ASSERT_LE(data1[i], e1.max()); 20 | } 21 | 22 | // Yes, I know, two separate draws of 64 bytes _MIGHT_ be the same with an 23 | // ideal random number generator. In practice, though, no. And testing this 24 | // _does_ protect against initialising the random engine with the same seed 25 | // every time. 26 | ASSERT_NE(data1, data2); 27 | } 28 | -------------------------------------------------------------------------------- /tests/unit_tests/dsa_public_key.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "../key_template.h" 3 | #include "dsa_public_key.h" 4 | 5 | 6 | TEST(dsa_public_key, test) 7 | { 8 | using key_type = pgp::dsa_public_key; 9 | tests::key_test, 11 | tests::parameters::mpi<&key_type::q>, 12 | tests::parameters::mpi<&key_type::g>, 13 | tests::parameters::mpi<&key_type::y>>(); 14 | } 15 | -------------------------------------------------------------------------------- /tests/unit_tests/dsa_secret_key.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "../key_template.h" 3 | #include "dsa_secret_key.h" 4 | 5 | 6 | TEST(dsa_secret_key, test) 7 | { 8 | using key_type = pgp::dsa_secret_key; 9 | tests::key_test>(); 11 | } 12 | -------------------------------------------------------------------------------- /tests/unit_tests/dsa_signature.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "decoder.h" 6 | #include "dsa_signature.h" 7 | #include "../generate.h" 8 | #include "multiprecision_integer.h" 9 | #include "range_encoder.h" 10 | 11 | 12 | TEST(dsa_signature, encode_decode) 13 | { 14 | auto rval = tests::generate::mpi(); 15 | auto sval = tests::generate::mpi(); 16 | pgp::dsa_signature sig{rval, sval}; 17 | 18 | ASSERT_EQ(sig.size(), rval.size() + sval.size()); 19 | ASSERT_EQ(sig.r().data(), rval.data()); 20 | ASSERT_EQ(sig.s().data(), sval.data()); 21 | 22 | std::vector data(2048); 23 | pgp::range_encoder encoder{data}; 24 | sig.encode(encoder); 25 | 26 | ASSERT_EQ(encoder.size(), sig.size()); 27 | 28 | pgp::decoder decoder{data}; 29 | pgp::dsa_signature sig2{decoder}; 30 | 31 | ASSERT_EQ(sig, sig2); 32 | } 33 | 34 | TEST(dsa_signature, equality) 35 | { 36 | pgp::multiprecision_integer rval{std::array{1, 2, 3}}; 37 | pgp::multiprecision_integer sval{std::array{4, 5, 6}}; 38 | pgp::multiprecision_integer diff{std::array{7, 8, 9}}; 39 | 40 | pgp::dsa_signature sig{rval, sval}; 41 | ASSERT_EQ(sig, sig); 42 | 43 | pgp::dsa_signature sig2{diff, sval}; 44 | ASSERT_NE(sig, sig2); 45 | 46 | pgp::dsa_signature sig3{rval, diff}; 47 | ASSERT_NE(sig, sig3); 48 | } 49 | -------------------------------------------------------------------------------- /tests/unit_tests/ecdh_public_key.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "../key_template.h" 3 | #include "ecdh_public_key.h" 4 | 5 | 6 | TEST(ecdh_public_key, test) 7 | { 8 | using key_type = pgp::ecdh_public_key; 9 | tests::key_test, 11 | tests::parameters::mpi<&key_type::Q>, 12 | tests::parameters::hashalgo<&key_type::hash_function>, 13 | tests::parameters::keyalgo<&key_type::algorithm>>(); 14 | } 15 | -------------------------------------------------------------------------------- /tests/unit_tests/ecdh_secret_key.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "../key_template.h" 3 | #include "ecdh_secret_key.h" 4 | 5 | 6 | TEST(ecdh_secret_key, test) 7 | { 8 | using key_type = pgp::ecdh_secret_key; 9 | tests::key_test>(); 11 | } 12 | -------------------------------------------------------------------------------- /tests/unit_tests/ecdsa_public_key.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "../key_template.h" 3 | #include "ecdsa_public_key.h" 4 | 5 | 6 | TEST(ecdsa_public_key, test) 7 | { 8 | using key_type = pgp::ecdsa_public_key; 9 | tests::key_test, 11 | tests::parameters::mpi<&key_type::Q>>(); 12 | } 13 | -------------------------------------------------------------------------------- /tests/unit_tests/ecdsa_secret_key.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "../key_template.h" 3 | #include "ecdsa_secret_key.h" 4 | 5 | 6 | TEST(ecdsa_secret_key, test) 7 | { 8 | using key_type = pgp::ecdsa_secret_key; 9 | tests::key_test>(); 11 | } 12 | -------------------------------------------------------------------------------- /tests/unit_tests/eddsa_public_key.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "../key_template.h" 3 | #include "eddsa_public_key.h" 4 | 5 | 6 | TEST(eddsa_public_key, test) 7 | { 8 | using key_type = pgp::eddsa_public_key; 9 | tests::key_test, 11 | tests::parameters::mpi<&key_type::Q>>(); 12 | } 13 | -------------------------------------------------------------------------------- /tests/unit_tests/eddsa_secret_key.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "../key_template.h" 3 | #include "eddsa_secret_key.h" 4 | 5 | 6 | TEST(eddsa_secret_key, test) 7 | { 8 | using key_type = pgp::eddsa_secret_key; 9 | tests::key_test>(); 11 | } 12 | -------------------------------------------------------------------------------- /tests/unit_tests/elgamal_public_key.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "../key_template.h" 3 | #include "elgamal_public_key.h" 4 | 5 | 6 | TEST(elgamal_public_key, test) 7 | { 8 | using key_type = pgp::elgamal_public_key; 9 | tests::key_test, 11 | tests::parameters::mpi<&key_type::g>, 12 | tests::parameters::mpi<&key_type::y>>(); 13 | } 14 | -------------------------------------------------------------------------------- /tests/unit_tests/elgamal_secret_key.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "../key_template.h" 3 | #include "elgamal_secret_key.h" 4 | 5 | 6 | TEST(elgamal_secret_key, test) 7 | { 8 | using key_type = pgp::elgamal_secret_key; 9 | tests::key_test>(); 11 | } 12 | -------------------------------------------------------------------------------- /tests/unit_tests/expected_number.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "decoder.h" 3 | #include "expected_number.h" 4 | #include "fixed_number.h" 5 | #include "range_encoder.h" 6 | 7 | 8 | TEST(expected_number, decode_encode) 9 | { 10 | std::array data; 11 | 12 | pgp::range_encoder encoder{data}; 13 | pgp::fixed_number{42}.encode(encoder); 14 | pgp::fixed_number{123}.encode(encoder); 15 | 16 | pgp::decoder decoder{data}; 17 | 18 | ASSERT_NO_THROW((pgp::expected_number{decoder})); 19 | ASSERT_THROW((pgp::expected_number{decoder}), std::range_error); 20 | } 21 | -------------------------------------------------------------------------------- /tests/unit_tests/fixed_number.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "decoder.h" 5 | #include "fixed_number.h" 6 | #include "range_encoder.h" 7 | #include "../device_random_engine.h" 8 | 9 | 10 | namespace { 11 | thread_local tests::device_random_engine random_engine; 12 | 13 | template 14 | void test_faithful_encoding_value(pgp::fixed_number number) { 15 | // encode into data 16 | std::vector data(2 + sizeof(T)); 17 | pgp::range_encoder encoder{data}; 18 | number.encode(encoder); 19 | 20 | // decode from data and check for equality 21 | pgp::decoder decoder{data}; 22 | pgp::fixed_number result{decoder}; 23 | ASSERT_EQ(number, result); 24 | } 25 | 26 | template 27 | void test_faithful_encoding() 28 | { 29 | std::uniform_int_distribution distr(0, std::numeric_limits::max()); 30 | for (int i = 0; i < 1000; i++) { 31 | pgp::fixed_number num{distr(random_engine)}; 32 | 33 | test_faithful_encoding_value(num); 34 | } 35 | 36 | test_faithful_encoding_value(pgp::fixed_number{0}); 37 | test_faithful_encoding_value(pgp::fixed_number{1}); 38 | test_faithful_encoding_value(pgp::fixed_number{std::numeric_limits::max()}); 39 | test_faithful_encoding_value(pgp::fixed_number{std::numeric_limits::max() - 1}); 40 | } 41 | } 42 | 43 | TEST(fixed_number, faithful_encoding) 44 | { 45 | test_faithful_encoding(); 46 | test_faithful_encoding(); 47 | test_faithful_encoding(); 48 | test_faithful_encoding(); 49 | } 50 | -------------------------------------------------------------------------------- /tests/unit_tests/multiprecision_integer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "util/vector.h" 4 | #include "multiprecision_integer.h" 5 | #include "range_encoder.h" 6 | #include "decoder.h" 7 | #include "fixed_number.h" 8 | 9 | 10 | TEST(multiprecision_integer, default_empty) 11 | { 12 | pgp::multiprecision_integer mi; 13 | ASSERT_EQ(mi.size(), 2); // to account for the bits field 14 | ASSERT_EQ(mi.data().size(), 0); 15 | } 16 | 17 | TEST(multiprecision_integer, decode) 18 | { 19 | const auto big = boost::endian::native_to_big; 20 | const std::array data{ 21 | big(0), big(21), 22 | big(0x1f), big(0x13), big(0x37) 23 | }; 24 | 25 | pgp::decoder decoder{data}; 26 | pgp::multiprecision_integer mi1{decoder}; 27 | 28 | pgp::multiprecision_integer mi2{pgp::span(data.data() + 2, 3)}; 29 | ASSERT_EQ(mi1, mi2); 30 | } 31 | 32 | TEST(multiprecision_integer, equality) 33 | { 34 | pgp::multiprecision_integer mi1{std::array{1, 2, 3}}; 35 | pgp::multiprecision_integer mi2{std::array{4, 5, 6}}; 36 | pgp::multiprecision_integer mi3{std::array{1, 2}}; 37 | pgp::multiprecision_integer mi4{std::array{1, 2, 3, 4}}; 38 | 39 | ASSERT_EQ(mi1, mi1); 40 | ASSERT_NE(mi1, mi2); 41 | ASSERT_NE(mi1, mi3); 42 | ASSERT_NE(mi1, mi4); 43 | } 44 | 45 | TEST(multiprecision_integer, assignment) 46 | { 47 | pgp::multiprecision_integer mi; 48 | 49 | auto test_for_data = [&mi](const pgp::span &data) { 50 | mi = data; 51 | ASSERT_EQ(mi.data(), data); 52 | ASSERT_EQ(mi.size(), data.size() + 2); 53 | }; 54 | 55 | test_for_data(std::vector{1, 2, 3}); 56 | test_for_data({}); 57 | } 58 | 59 | TEST(multiprecision_integer, vector_constructor) 60 | { 61 | auto test_for_vector = [](const pgp::vector &data) { 62 | auto nonzero_it = std::find_if(data.begin(), data.end(), [](uint8_t x) { return x != 0; }); 63 | size_t zero_bytes = std::distance(data.begin(), nonzero_it); 64 | 65 | pgp::multiprecision_integer mi{data}; 66 | ASSERT_EQ(mi, pgp::multiprecision_integer{pgp::span{data}}); 67 | // 2 for size prefix; zero bytes should be stripped 68 | ASSERT_EQ(mi.size(), 2 + data.size() - zero_bytes); 69 | }; 70 | 71 | test_for_vector(pgp::vector{std::initializer_list{}}); 72 | test_for_vector(pgp::vector{std::initializer_list{1, 2, 3, 4}}); 73 | test_for_vector(pgp::vector{std::initializer_list{0, 2, 3, 4}}); 74 | test_for_vector(pgp::vector{std::initializer_list{0, 0, 0xff, 4, 5 ,6, 7}}); 75 | } 76 | 77 | TEST(multiprecision_integer, computed_bits) 78 | { 79 | std::array data; 80 | 81 | data[1] = 12; 82 | data[2] = 34; 83 | 84 | for (int zeros = 0; zeros <= 7; zeros++) { 85 | data[0] = 0xff >> zeros; 86 | 87 | pgp::multiprecision_integer mi{data}; 88 | 89 | std::array dest; 90 | pgp::range_encoder encoder{dest}; 91 | mi.encode(encoder); 92 | 93 | pgp::decoder decoder{dest}; 94 | ASSERT_EQ(pgp::uint16(decoder), 8 - zeros + 16); 95 | } 96 | } 97 | 98 | TEST(multiprecision_integer, zero_stripping) 99 | { 100 | std::array data; 101 | 102 | std::fill(data.begin(), data.end(), 0xff); 103 | 104 | for (size_t i = 0; i <= data.size(); i++) { 105 | if (i > 0) data[i - 1] = 0; 106 | 107 | pgp::multiprecision_integer mi(data); 108 | 109 | std::array dest; 110 | pgp::range_encoder encoder{dest}; 111 | mi.encode(encoder); 112 | 113 | pgp::decoder decoder{dest}; 114 | ASSERT_EQ(pgp::uint16{decoder}, 8 * (data.size() - i)); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /tests/unit_tests/packet.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "packet.h" 3 | #include "range_encoder.h" 4 | #include "decoder.h" 5 | #include "../device_random_engine.h" 6 | 7 | 8 | namespace { 9 | thread_local tests::device_random_engine random_engine; 10 | } 11 | 12 | TEST(packet, constructor) 13 | { 14 | using namespace std::literals; 15 | 16 | pgp::packet packet(pgp::in_place_type_t(), "another username"s); 17 | 18 | ASSERT_EQ(packet.tag(), pgp::packet_tag::user_id); 19 | ASSERT_EQ(pgp::get(packet.body()).id(), "another username"); 20 | } 21 | 22 | TEST(packet, encode_decode) 23 | { 24 | std::uniform_int_distribution distr(0, 1048576); 25 | 26 | auto test_for_size = [](size_t size) { 27 | std::string iddata(size, 'a'); 28 | pgp::packet packet{pgp::in_place_type_t(), iddata}; 29 | 30 | std::vector data(packet.size()); 31 | pgp::range_encoder encoder{data}; 32 | packet.encode(encoder); 33 | 34 | ASSERT_EQ(encoder.size(), data.size()); 35 | 36 | pgp::decoder decoder{data}; 37 | pgp::packet packet2{decoder}; 38 | 39 | ASSERT_EQ(packet.tag(), packet2.tag()); 40 | ASSERT_EQ(packet, packet2); 41 | }; 42 | 43 | for (int i = 0; i < 100; i++) { 44 | test_for_size(distr(random_engine)); 45 | } 46 | 47 | // Some boundary values 48 | test_for_size(0); // zero is always a boundary value 49 | test_for_size(1); 50 | test_for_size(255); // case boundary in (en,de)coding 51 | test_for_size(256); 52 | test_for_size(65535); // case boundary in (en,de)coding 53 | test_for_size(65536); 54 | test_for_size(1048576); // 2**20, some high value 55 | test_for_size(1048577); // 2**20 + 1, some high value that isn't a power of 2 56 | } 57 | 58 | TEST(packet, decode_fail) 59 | { 60 | std::array data{1, 2, 3}; 61 | pgp::decoder decoder{data}; 62 | ASSERT_THROW(pgp::packet{decoder}, std::runtime_error); 63 | } 64 | 65 | TEST(packet, equality) 66 | { 67 | using namespace std::literals; 68 | 69 | pgp::packet p1{pgp::in_place_type_t(), "abc"s}; 70 | pgp::packet p2{pgp::in_place_type_t(), "def"s}; 71 | pgp::packet p3{pgp::in_place_type_t()}; 72 | 73 | ASSERT_EQ(p1, p1); 74 | ASSERT_NE(p1, p2); 75 | ASSERT_NE(p1, p3); 76 | } 77 | -------------------------------------------------------------------------------- /tests/unit_tests/public_key.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "../key_template.h" 3 | #include "public_key.h" 4 | #include "range_encoder.h" 5 | #include "decoder.h" 6 | 7 | 8 | TEST(public_key, constructor) 9 | { 10 | auto n = tests::generate::mpi(); 11 | auto e = tests::generate::mpi(); 12 | 13 | pgp::public_key k{ 14 | 1234, 15 | pgp::key_algorithm::rsa_encrypt_or_sign, 16 | pgp::in_place_type_t(), 17 | n, e 18 | }; 19 | 20 | ASSERT_EQ(k.tag(), pgp::packet_tag::public_key); 21 | ASSERT_EQ(k.creation_time(), 1234); 22 | ASSERT_EQ(k.algorithm(), pgp::key_algorithm::rsa_encrypt_or_sign); 23 | auto &keyval = pgp::get(k.key()); 24 | ASSERT_EQ(keyval.n().data(), n.data()); 25 | ASSERT_EQ(keyval.e().data(), e.data()); 26 | } 27 | 28 | TEST(public_key, encode_decode) 29 | { 30 | auto p = tests::generate::mpi(); 31 | auto q = tests::generate::mpi(); 32 | auto g = tests::generate::mpi(); 33 | auto y = tests::generate::mpi(); 34 | 35 | pgp::public_key k{ 36 | 5678, 37 | pgp::key_algorithm::dsa, 38 | pgp::in_place_type_t(), 39 | p, q, g, y 40 | }; 41 | 42 | std::vector data(2048); 43 | pgp::range_encoder encoder{data}; 44 | k.encode(encoder); 45 | 46 | ASSERT_EQ(encoder.size(), k.size()); 47 | data.resize(encoder.size()); 48 | 49 | pgp::decoder decoder{data}; 50 | pgp::public_key k2{decoder}; 51 | 52 | ASSERT_EQ(k, k2); 53 | } 54 | 55 | TEST(public_key, equality) 56 | { 57 | auto n = tests::generate::mpi(); 58 | auto e = tests::generate::mpi(); 59 | 60 | pgp::public_key k{ 61 | 1234, 62 | pgp::key_algorithm::rsa_encrypt_or_sign, 63 | pgp::in_place_type_t(), 64 | n, e 65 | }; 66 | 67 | pgp::public_key k2{ 68 | 4321, 69 | pgp::key_algorithm::rsa_encrypt_or_sign, 70 | pgp::in_place_type_t(), 71 | n, e 72 | }; 73 | 74 | pgp::public_key k3{ 75 | 1234, 76 | pgp::key_algorithm::rsa_sign_only, 77 | pgp::in_place_type_t(), 78 | n, e 79 | }; 80 | 81 | pgp::multiprecision_integer n2; 82 | do n2 = tests::generate::mpi(); 83 | while (n2 == n); 84 | 85 | pgp::public_key k4{ 86 | 1234, 87 | pgp::key_algorithm::rsa_encrypt_or_sign, 88 | pgp::in_place_type_t(), 89 | n2, e 90 | }; 91 | 92 | ASSERT_EQ(k, k); 93 | ASSERT_NE(k, k2); 94 | ASSERT_NE(k, k3); 95 | ASSERT_NE(k, k4); 96 | } 97 | 98 | TEST(public_key, fingerprint) 99 | { 100 | std::array qdata{1, 2, 4, 8, 3, 143, 32, 92}; 101 | 102 | pgp::curve_oid oid {pgp::curve_oid::ed25519()}; 103 | pgp::multiprecision_integer Q {qdata}; 104 | pgp::hash_algorithm hashalgo {pgp::hash_algorithm::sha1}; 105 | pgp::symmetric_key_algorithm keyalgo {pgp::symmetric_key_algorithm::aes256}; 106 | 107 | pgp::public_key k{ 108 | 1554103728, 109 | pgp::key_algorithm::ecdh, 110 | pgp::in_place_type_t(), 111 | oid, Q, hashalgo, keyalgo 112 | }; 113 | 114 | std::array expected = {0x3e, 0xb9, 0x45, 0xeb, 0x87, 0x7e, 0xbe, 0x0d}; 115 | ASSERT_EQ(k.key_id(), expected); 116 | } 117 | -------------------------------------------------------------------------------- /tests/unit_tests/rsa_public_key.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "../key_template.h" 3 | #include "rsa_public_key.h" 4 | 5 | 6 | TEST(rsa_public_key, test) 7 | { 8 | using key_type = pgp::rsa_public_key; 9 | tests::key_test, 11 | tests::parameters::mpi<&key_type::e>>(); 12 | } 13 | -------------------------------------------------------------------------------- /tests/unit_tests/rsa_secret_key.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "../key_template.h" 3 | #include "rsa_secret_key.h" 4 | 5 | 6 | TEST(rsa_secret_key, test) 7 | { 8 | using key_type = pgp::rsa_secret_key; 9 | tests::key_test, 11 | tests::parameters::mpi<&key_type::p>, 12 | tests::parameters::mpi<&key_type::q>, 13 | tests::parameters::mpi<&key_type::u>>(); 14 | } 15 | -------------------------------------------------------------------------------- /tests/unit_tests/signature_subpacket/fixed_array.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "signature_subpacket/fixed_array.h" 4 | #include "range_encoder.h" 5 | #include "decoder.h" 6 | 7 | 8 | TEST(signature_subpacket_array, issuer_constructors) 9 | { 10 | std::array data{1, 2, 4, 8, 9, 13, 10, 42}; 11 | 12 | pgp::decoder decoder{data}; 13 | pgp::signature_subpacket::issuer p1{decoder}; 14 | 15 | pgp::signature_subpacket::issuer p2{data}; 16 | 17 | ASSERT_EQ(p1.data(), p2.data()); 18 | } 19 | 20 | TEST(signature_subpacket_array, issuer_encode_decode) 21 | { 22 | std::array data{1, 2, 65, 2, 6, 9, 9, 8}; 23 | pgp::signature_subpacket::issuer p1{data}; 24 | 25 | // First encode the data 26 | std::array enc; 27 | pgp::range_encoder encoder{enc}; 28 | p1.encode(encoder); 29 | 30 | ASSERT_EQ(encoder.size(), p1.size()); 31 | 32 | // Then try to decode it again 33 | pgp::decoder decoder{enc}; 34 | pgp::variable_number dec_size{decoder}; 35 | // one extra for the type tag 36 | ASSERT_EQ(dec_size, 1 + data.size()); 37 | 38 | pgp::signature_subpacket_type type{decoder.extract_number()}; 39 | ASSERT_EQ(type, pgp::signature_subpacket::issuer::type()); 40 | 41 | pgp::signature_subpacket::issuer p2{decoder}; 42 | ASSERT_EQ(p1.data(), p2.data()); 43 | } 44 | 45 | TEST(signature_subpacket_array, issuer_type) 46 | { 47 | ASSERT_EQ(pgp::signature_subpacket::issuer::type(), pgp::signature_subpacket_type::issuer); 48 | } 49 | -------------------------------------------------------------------------------- /tests/unit_tests/signature_subpacket/issuer_fingerprint.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "signature_subpacket/issuer_fingerprint.h" 4 | #include "range_encoder.h" 5 | #include "decoder.h" 6 | 7 | 8 | TEST(signature_subpacket_issuer_fingerprint, constructors) 9 | { 10 | std::array data{ 11 | 4, 12 | 65, 8, 7, 2, 6, 8, 5, 8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 45, 64, 97 13 | }; 14 | 15 | std::array bare_data; 16 | std::copy(std::next(std::begin(data), 1), std::end(data), std::begin(bare_data)); 17 | 18 | pgp::decoder decoder{data}; 19 | pgp::signature_subpacket::issuer_fingerprint p1{decoder}; 20 | 21 | pgp::signature_subpacket::issuer_fingerprint p2{bare_data}; 22 | 23 | ASSERT_EQ(p1.data(), p2.data()); 24 | } 25 | 26 | TEST(signature_subpacket_issuer_fingerprint, encode_decode) 27 | { 28 | std::array bare_data{ 29 | 23, 85, 65, 89, 12, 2, 10, 63, 7, 4, 1, 5, 8, 9, 6, 3, 2, 31, 64, 79 30 | }; 31 | pgp::signature_subpacket::issuer_fingerprint p1{bare_data}; 32 | 33 | // First encode the data 34 | std::array enc; 35 | pgp::range_encoder encoder{enc}; 36 | p1.encode(encoder); 37 | 38 | ASSERT_EQ(encoder.size(), p1.size()); 39 | 40 | // Then try to decode it again 41 | pgp::decoder decoder{enc}; 42 | pgp::variable_number dec_size{decoder}; 43 | // one extra for the type tag, one extra for the key version 44 | ASSERT_EQ(dec_size, 1 + 1 + bare_data.size()); 45 | 46 | pgp::signature_subpacket_type type{decoder.extract_number()}; 47 | ASSERT_EQ(type, pgp::signature_subpacket_type::issuer_fingerprint); 48 | 49 | pgp::signature_subpacket::issuer_fingerprint p2{decoder}; 50 | ASSERT_EQ(p1.data(), p2.data()); 51 | ASSERT_EQ(p1, p2); 52 | 53 | // check whether operator!= works 54 | std::array other_data; 55 | std::fill(std::begin(other_data), std::end(other_data), 1); 56 | pgp::signature_subpacket::issuer_fingerprint p3{other_data}; 57 | ASSERT_NE(p1, p3); 58 | } 59 | 60 | TEST(signature_subpacket_issuer_fingerprint, type) 61 | { 62 | ASSERT_EQ(pgp::signature_subpacket::issuer_fingerprint::type(), pgp::signature_subpacket_type::issuer_fingerprint); 63 | } 64 | -------------------------------------------------------------------------------- /tests/unit_tests/signature_subpacket/key_flags.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "signature_subpacket/key_flags.h" 4 | #include "range_encoder.h" 5 | 6 | 7 | TEST(signature_subpacket_key_flags, variadic_constructor) 8 | { 9 | uint8_t a = 0x1, b = 0x4, c = 0x80; 10 | pgp::signature_subpacket::key_flags packet(a, b, c); 11 | ASSERT_TRUE(packet.is_set(0x1)); 12 | ASSERT_TRUE(packet.is_set(0x4)); 13 | ASSERT_TRUE(packet.is_set(0x80)); 14 | } 15 | 16 | TEST(signature_subpacket_key_flags, type) 17 | { 18 | ASSERT_EQ(pgp::signature_subpacket::key_flags::type(), pgp::signature_subpacket_type::key_flags); 19 | } 20 | 21 | TEST(signature_subpacket_key_flags, faithful_encoding) 22 | { 23 | for (int i = 0; i < 256; i++) { 24 | pgp::signature_subpacket::key_flags packet{static_cast(i)}; 25 | 26 | // For flags fields of at most 191 bytes, the packet length will be 3; 27 | // this is true for the forseeable future. 28 | ASSERT_EQ(packet.size(), 3); 29 | 30 | // Encode it to 'data' 31 | std::array data; 32 | pgp::range_encoder encoder{data}; 33 | packet.encode(encoder); 34 | 35 | ASSERT_EQ(encoder.size(), packet.size()); 36 | 37 | // Decode it from 'data'; extract the packet type and length outside the class 38 | pgp::decoder decoder{data}; 39 | 40 | pgp::uint8 decoded_length{decoder}; 41 | ASSERT_EQ(decoded_length, 2); // type + flags 42 | 43 | pgp::signature_subpacket_type decoded_type{decoder.extract_number()}; 44 | ASSERT_EQ(decoded_type, pgp::signature_subpacket::key_flags::type()); 45 | 46 | pgp::signature_subpacket::key_flags result{decoder}; 47 | 48 | ASSERT_EQ(packet, result); 49 | } 50 | } 51 | 52 | TEST(signature_subpacket_key_flags, equality) 53 | { 54 | pgp::signature_subpacket::key_flags p1{0x42}; 55 | pgp::signature_subpacket::key_flags p2{0x43}; 56 | 57 | ASSERT_EQ(p1, p1); 58 | ASSERT_NE(p1, p2); 59 | } 60 | -------------------------------------------------------------------------------- /tests/unit_tests/signature_subpacket/numeric.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "signature_subpacket/numeric.h" 4 | #include "range_encoder.h" 5 | #include "decoder.h" 6 | #include "device_random_engine.h" 7 | 8 | 9 | namespace { 10 | thread_local tests::device_random_engine random_engine; 11 | 12 | template 13 | class TestFunctions { 14 | private: 15 | std::uniform_int_distribution distr; 16 | 17 | public: 18 | TestFunctions(): 19 | distr{0, std::numeric_limits::max()} 20 | {} 21 | 22 | void test_constructors() 23 | { 24 | Int value{distr(random_engine)}; 25 | 26 | std::vector data(sizeof(Int)); 27 | pgp::range_encoder int_encoder{data}; 28 | int_encoder.push(value); 29 | 30 | data.resize(int_encoder.size()); 31 | 32 | pgp::decoder decoder{data}; 33 | T p1{decoder}; 34 | 35 | T p2{value}; 36 | 37 | ASSERT_EQ(p1.data(), p2.data()); 38 | } 39 | 40 | void test_encode_decode() 41 | { 42 | Int value{distr(random_engine)}; 43 | 44 | T p1{value}; 45 | 46 | // First encode the data 47 | std::vector enc(16); 48 | pgp::range_encoder encoder{enc}; 49 | p1.encode(encoder); 50 | 51 | enc.resize(encoder.size()); 52 | 53 | ASSERT_EQ(encoder.size(), p1.size()); 54 | 55 | // Then try to decode it again 56 | pgp::decoder decoder{enc}; 57 | pgp::variable_number dec_size{decoder}; 58 | // one extra for the type tag 59 | ASSERT_EQ(dec_size, 1 + sizeof(Int)); 60 | 61 | pgp::signature_subpacket_type type{decoder.extract_number()}; 62 | ASSERT_EQ(type, T::type()); 63 | 64 | T p2{decoder}; 65 | ASSERT_EQ(p1.data(), p2.data()); 66 | } 67 | }; 68 | } 69 | 70 | TEST(signature_subpacket_numeric, constructors) 71 | { 72 | TestFunctions().test_constructors(); 73 | TestFunctions().test_constructors(); 74 | } 75 | 76 | TEST(signature_subpacket_numeric, encode_decode) 77 | { 78 | TestFunctions().test_encode_decode(); 79 | TestFunctions().test_encode_decode(); 80 | } 81 | 82 | TEST(signature_subpacket_numeric, decode_throw) 83 | { 84 | pgp::signature_subpacket::key_expiration_time p1{42}; 85 | 86 | std::vector data(16); 87 | pgp::range_encoder encoder{data}; 88 | p1.encode(encoder); 89 | 90 | data.resize(encoder.size()); 91 | 92 | pgp::decoder decoder{data}; 93 | pgp::variable_number dec_size{decoder}; // Ignore the size 94 | decoder.extract_number(); // Ignore the type 95 | 96 | // Decode with shorter type throws error because parser is not exhausted 97 | ASSERT_THROW((pgp::signature_subpacket::revocable(decoder)), std::runtime_error); 98 | } 99 | -------------------------------------------------------------------------------- /tests/unit_tests/signature_subpacket/unknown.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "signature_subpacket/unknown.h" 5 | #include "range_encoder.h" 6 | #include "decoder.h" 7 | 8 | 9 | TEST(signature_subpacket_unknown, properties) 10 | { 11 | auto type_1 = pgp::signature_subpacket_type::features; 12 | 13 | std::array data{1, 5, 3, 3, 7, 5, 3, 4, 3, 4}; 14 | 15 | // Check that decoding and initializing with data is the same thing 16 | pgp::decoder decoder{data}; 17 | pgp::signature_subpacket::unknown p1{type_1, decoder}; 18 | pgp::signature_subpacket::unknown p2{type_1, data}; 19 | 20 | ASSERT_EQ(p1.data(), p2.data()); 21 | ASSERT_EQ(p1, p1); 22 | 23 | // Two extra bytes: one for the variable_number, one for the type tag 24 | ASSERT_EQ(p1.size(), 2 + data.size()); 25 | ASSERT_EQ(p1.type(), type_1); 26 | 27 | // Check that encoding does something useful 28 | std::vector encoded(20); 29 | pgp::range_encoder encoder{encoded}; 30 | p1.encode(encoder); 31 | encoded.resize(encoder.size()); 32 | 33 | ASSERT_EQ(encoded.size(), 2 + data.size()); 34 | // The size tag is a variable_number, but for these small inputs it's just 35 | // 1 byte; note that the size reports the byte for the type as well 36 | ASSERT_EQ(encoded[0], 1 + data.size()); 37 | ASSERT_EQ(encoded[1], static_cast(type_1)); 38 | ASSERT_EQ( 39 | (pgp::span{data.data(), data.size()}), 40 | (pgp::span{encoded.data() + 2, encoded.size() - 2}) 41 | ); 42 | } 43 | 44 | TEST(signature_subpacket_unknown, equality) 45 | { 46 | auto type_1 = pgp::signature_subpacket_type::features; 47 | 48 | pgp::signature_subpacket::unknown p1{type_1, std::array{10, 11, 12}}; 49 | pgp::signature_subpacket::unknown p2{type_1, std::array{11, 11, 12}}; 50 | pgp::signature_subpacket::unknown p3{type_1, std::array{10, 11, 12, 13, 14}}; 51 | ASSERT_EQ(p1, p1); 52 | ASSERT_NE(p1, p2); 53 | ASSERT_NE(p1, p3); 54 | } 55 | -------------------------------------------------------------------------------- /tests/unit_tests/unknown_signature.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "unknown_signature.h" 4 | #include "range_encoder.h" 5 | #include "decoder.h" 6 | 7 | 8 | TEST(unknown_signature, test) 9 | { 10 | pgp::unknown_signature sig; 11 | ASSERT_THROW(sig.size(), std::runtime_error); 12 | 13 | std::vector v; 14 | pgp::range_encoder encoder{v}; 15 | ASSERT_THROW(sig.encode(encoder), std::runtime_error); 16 | 17 | pgp::decoder decoder{v}; 18 | pgp::unknown_signature sig2{decoder}; 19 | } 20 | 21 | TEST(unknown_signature, equality) 22 | { 23 | ASSERT_EQ(pgp::unknown_signature{}, pgp::unknown_signature{}); 24 | ASSERT_FALSE(pgp::unknown_signature{} != pgp::unknown_signature{}); 25 | } 26 | -------------------------------------------------------------------------------- /tests/unit_tests/user_id.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "user_id.h" 4 | #include "decoder.h" 5 | #include "range_encoder.h" 6 | 7 | 8 | TEST(user_id, tag) 9 | { 10 | using namespace std::literals; 11 | ASSERT_EQ(pgp::user_id(""s).tag(), pgp::packet_tag::user_id); 12 | ASSERT_EQ(pgp::user_id(pgp::span("abc")).tag(), pgp::packet_tag::user_id); 13 | } 14 | 15 | TEST(user_id, encode_decode) 16 | { 17 | using namespace std::literals; 18 | pgp::user_id id1{"yellow submarine"s}; 19 | ASSERT_EQ(id1.size(), 16); 20 | 21 | ASSERT_EQ(id1.id(), "yellow submarine"); 22 | 23 | std::array data; 24 | pgp::range_encoder encoder{data}; 25 | id1.encode(encoder); 26 | ASSERT_EQ(encoder.size(), 16); 27 | 28 | ASSERT_EQ(data[0], 'y'); 29 | 30 | ASSERT_EQ( 31 | pgp::span(data), 32 | pgp::span((const uint8_t*)"yellow submarine", 16)); 33 | 34 | pgp::decoder decoder{data}; 35 | pgp::user_id id2{decoder}; 36 | ASSERT_EQ(decoder.size(), 0); 37 | ASSERT_EQ(id2.id(), id1.id()); 38 | 39 | ASSERT_EQ(id2, id1); 40 | 41 | pgp::user_id id3{"something else"s}; 42 | ASSERT_NE(id1, id3); 43 | } 44 | -------------------------------------------------------------------------------- /tests/unit_tests/variable_number.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "variable_number.h" 4 | #include "range_encoder.h" 5 | #include "decoder.h" 6 | 7 | 8 | TEST(variable_number, faithful_encoding) 9 | { 10 | for (uint32_t n = 0; n < 10000; n++) { 11 | pgp::variable_number varnum{n}; 12 | 13 | std::vector data(6); 14 | pgp::range_encoder encoder{data}; 15 | varnum.encode(encoder); 16 | 17 | ASSERT_EQ(encoder.size(), varnum.size()); 18 | data.resize(encoder.size()); 19 | 20 | pgp::decoder decoder{data}; 21 | pgp::variable_number result{decoder}; 22 | ASSERT_EQ(n, result); 23 | } 24 | } 25 | 26 | TEST(variable_number, assignment) 27 | { 28 | for (uint32_t n = 0; n < 100; n++) { 29 | pgp::variable_number varnum; 30 | // Test the assignment operator 31 | varnum = n; 32 | ASSERT_EQ(varnum, n); 33 | } 34 | } 35 | --------------------------------------------------------------------------------