diff --git a/bare_minimum_crypto/cpp/Makefile b/bare_minimum_crypto/cpp/Makefile index e958479..46755ce 100644 --- a/bare_minimum_crypto/cpp/Makefile +++ b/bare_minimum_crypto/cpp/Makefile @@ -5,15 +5,24 @@ CXX_FLAGS=$(FLAGS) -c L_FLAGS=$(FLAGS) LIBS=-lsodium -bmc_test: bmc_test.o base64.o string_helpers.o time_helpers.o +bmc_test: bmc_test.o base64.o expiring_signature.o receiver.o sodium_helpers.o string_helpers.o time_helpers.o $(CXX) -o $@ $^ $(L_FLAGS) $(LIBS) -bmc_test.o: bmc_test.cpp string_helpers.h time_helpers.h +bmc_test.o: bmc_test.cpp expiring_signature.h receiver.h sodium_helpers.h string_helpers.h time_helpers.h $(CXX) -o $@ $(CXX_FLAGS) $< base64.o: cpp-base64/base64.cpp cpp-base64/base64.h $(CXX) -o $@ $(CXX_FLAGS) $< +expiring_signature.o: expiring_signature.cpp expiring_signature.h + $(CXX) -o $@ $(CXX_FLAGS) $< + +receiver.o: receiver.cpp receiver.h expiring_signature.h sodium_helpers.h string_helpers.h time_helpers.h + $(CXX) -o $@ $(CXX_FLAGS) $< + +sodium_helpers.o: sodium_helpers.cpp sodium_helpers.h + $(CXX) -o $@ $(CXX_FLAGS) $< + string_helpers.o: string_helpers.cpp string_helpers.h cpp-base64/base64.h $(CXX) -o $@ $(CXX_FLAGS) $< @@ -21,5 +30,5 @@ time_helpers.o: time_helpers.cpp time_helpers.h $(CXX) -o $@ $(CXX_FLAGS) $< clean: - rm -f bmc_test bmc_test.o base64.o string_helpers.o + rm -f bmc_test bmc_test.o base64.o expiring_signature.o receiver.o sodium_helpers.o string_helpers.o time_helpers.o diff --git a/bare_minimum_crypto/cpp/bmc_test.cpp b/bare_minimum_crypto/cpp/bmc_test.cpp index 94212db..f93e2bf 100644 --- a/bare_minimum_crypto/cpp/bmc_test.cpp +++ b/bare_minimum_crypto/cpp/bmc_test.cpp @@ -7,9 +7,12 @@ #include -#include "cpp-base64/base64.h" +// #include "cpp-base64/base64.h" #include "json.hpp" +#include "expiring_signature.h" +#include "receiver.h" +#include "sodium_helpers.h" #include "string_helpers.h" #include "time_helpers.h" @@ -18,113 +21,6 @@ using nlohmann::json; using namespace BareMinimumCrypto; -struct ExpiringSignature { - string cert_s; - vector sig; - - // C++ nonsense - bool operator == (const ExpiringSignature & o) const { - return - cert_s == o.cert_s && - sig == o.sig - ; - } - - bool operator != (const ExpiringSignature & o) const { - return ! (*this == o); - } -}; - -void try_sodium_init () { - if (sodium_init () < 0) { - throw std::runtime_error ("Can't initialize libsodium"); - } -} - -bool is_pubkey_length (const vector & v) { - return v.size () == crypto_sign_PUBLICKEYBYTES; -} - -optional > try_verify_signed_data ( - const ExpiringSignature & sig, - const vector & pubkey, - Instant now -) { - try_sodium_init (); - - if (! is_pubkey_length (pubkey)) { - return nullopt; - } - - if (crypto_sign_verify_detached ( - sig.sig.data (), - (const uint8_t *)sig.cert_s.data (), - sig.cert_s.size (), - pubkey.data () - ) != 0) { - return nullopt; - } - - const json j = json::parse (sig.cert_s); - - const TimeRange tr { - j ["not_before"], - j ["not_after"] - }; - - if (! tr.contains (now)) { - return nullopt; - } - - const string payload_b64 = j ["payload_b64"]; - const auto payload = std::move (*BareMinimumCrypto::base64_decode (payload_b64)); - - return payload; -} - -optional > verify_signed_data ( - const ExpiringSignature & sig, - const vector & pubkey, - Instant now -) { - try { - return try_verify_signed_data (sig, pubkey, now); - } - catch (json::exception &) { - return nullopt; - } -} - -optional > verify_cert_and_data ( - const ExpiringSignature & signed_cert, - const ExpiringSignature & signed_data, - const vector & root_pubkey, - Instant now -) { - auto subkey_opt = verify_signed_data (signed_cert, root_pubkey, now); - if (! subkey_opt) { - return nullopt; - } - const auto subkey = std::move (*subkey_opt); - - return verify_signed_data (signed_data, subkey, now); -} - -optional > verify_cert_and_data ( - const ExpiringSignature & signed_cert, - const ExpiringSignature & signed_data, - const vector & root_pubkey -) { - return verify_cert_and_data (signed_cert, signed_data, root_pubkey, Instant::now ()); -} - -optional > verify_signed_data ( - const ExpiringSignature & sig, - const vector & pubkey -) { - return verify_signed_data (sig, pubkey, Instant::now ()); -} - class SigningKey { vector pk; vector sk; @@ -243,7 +139,7 @@ int happy_path () { // even though the receiver has never seen the sub-key. const auto root_pubkey = root_key.pubkey (); - auto verified_opt = verify_cert_and_data (cert, signed_data, root_pubkey); + auto verified_opt = Receiver::verify_cert_and_data (cert, signed_data, root_pubkey); if (! verified_opt) { cerr << "Receiver couldn't verify cert and data" << endl; return 1; diff --git a/bare_minimum_crypto/cpp/expiring_signature.cpp b/bare_minimum_crypto/cpp/expiring_signature.cpp new file mode 100644 index 0000000..0b0448b --- /dev/null +++ b/bare_minimum_crypto/cpp/expiring_signature.cpp @@ -0,0 +1,15 @@ +#include "expiring_signature.h" + +namespace BareMinimumCrypto { + // C++ nonsense + bool ExpiringSignature::operator == (const ExpiringSignature & o) const { + return + cert_s == o.cert_s && + sig == o.sig + ; + } + + bool ExpiringSignature::operator != (const ExpiringSignature & o) const { + return ! (*this == o); + } +} diff --git a/bare_minimum_crypto/cpp/expiring_signature.h b/bare_minimum_crypto/cpp/expiring_signature.h new file mode 100644 index 0000000..7ab525b --- /dev/null +++ b/bare_minimum_crypto/cpp/expiring_signature.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include +#include + +namespace BareMinimumCrypto { + using namespace std; + + struct ExpiringSignature { + string cert_s; + vector sig; + + bool operator == (const ExpiringSignature & o) const; + bool operator != (const ExpiringSignature & o) const; + }; +} diff --git a/bare_minimum_crypto/cpp/receiver.cpp b/bare_minimum_crypto/cpp/receiver.cpp new file mode 100644 index 0000000..daee131 --- /dev/null +++ b/bare_minimum_crypto/cpp/receiver.cpp @@ -0,0 +1,91 @@ +#include "receiver.h" + +#include + +#include "json.hpp" + +#include "expiring_signature.h" +#include "sodium_helpers.h" +#include "string_helpers.h" +#include "time_helpers.h" + +namespace BareMinimumCrypto::Receiver { + using nlohmann::json; + + bool is_pubkey_length (const vector & v) { + return v.size () == crypto_sign_PUBLICKEYBYTES; + } + + optional > try_verify_signed_data ( + const ExpiringSignature & sig, + const vector & pubkey, + Instant now + ) { + try_sodium_init (); + + if (! is_pubkey_length (pubkey)) { + return nullopt; + } + + if (crypto_sign_verify_detached ( + sig.sig.data (), + (const uint8_t *)sig.cert_s.data (), + sig.cert_s.size (), + pubkey.data () + ) != 0) { + return nullopt; + } + + const json j = json::parse (sig.cert_s); + + const TimeRange tr { + j ["not_before"], + j ["not_after"] + }; + + if (! tr.contains (now)) { + return nullopt; + } + + const string payload_b64 = j ["payload_b64"]; + const auto payload = std::move (*BareMinimumCrypto::base64_decode (payload_b64)); + + return payload; + } + + optional > verify_signed_data ( + const ExpiringSignature & sig, + const vector & pubkey, + Instant now + ) { + try { + return try_verify_signed_data (sig, pubkey, now); + } + catch (json::exception &) { + return nullopt; + } + } + + optional > verify_cert_and_data ( + const ExpiringSignature & signed_cert, + const ExpiringSignature & signed_data, + const vector & root_pubkey, + Instant now + ) { + auto subkey_opt = verify_signed_data (signed_cert, root_pubkey, now); + if (! subkey_opt) { + return nullopt; + } + const auto subkey = std::move (*subkey_opt); + + return verify_signed_data (signed_data, subkey, now); + } + + optional > verify_cert_and_data ( + const ExpiringSignature & signed_cert, + const ExpiringSignature & signed_data, + const vector & root_pubkey + ) { + return verify_cert_and_data (signed_cert, signed_data, root_pubkey, Instant::now ()); + } +} diff --git a/bare_minimum_crypto/cpp/receiver.h b/bare_minimum_crypto/cpp/receiver.h new file mode 100644 index 0000000..d495c03 --- /dev/null +++ b/bare_minimum_crypto/cpp/receiver.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include +#include + +namespace BareMinimumCrypto { + struct ExpiringSignature; +} + +// Structs and functions for the receiver role. + +/* +The receiver needs to keep at least one root pubkey saved to +non-volatile memory. Since root keys are long-lived, you can +just compile them into the receiver app, too. + +All the receiver does is receive combined cert-and-data messages, +and attempt to verify them. The subkeys used to directly sign the +data don't need to be saved, but should be logged. +*/ + +namespace BareMinimumCrypto::Receiver { + using namespace std; + + optional > verify_cert_and_data ( + const ExpiringSignature & signed_cert, + const ExpiringSignature & signed_data, + const vector & root_pubkey + ); +} diff --git a/bare_minimum_crypto/cpp/sodium_helpers.cpp b/bare_minimum_crypto/cpp/sodium_helpers.cpp new file mode 100644 index 0000000..6d0aa2d --- /dev/null +++ b/bare_minimum_crypto/cpp/sodium_helpers.cpp @@ -0,0 +1,13 @@ +#include "sodium_helpers.h" + +#include + +#include + +namespace BareMinimumCrypto { + void try_sodium_init () { + if (sodium_init () < 0) { + throw std::runtime_error ("Can't initialize libsodium"); + } + } +} diff --git a/bare_minimum_crypto/cpp/sodium_helpers.h b/bare_minimum_crypto/cpp/sodium_helpers.h new file mode 100644 index 0000000..58d619a --- /dev/null +++ b/bare_minimum_crypto/cpp/sodium_helpers.h @@ -0,0 +1,5 @@ +#pragma once + +namespace BareMinimumCrypto { + void try_sodium_init (); +}