Skip to content

Commit

Permalink
encrypton - add bind phrase feature
Browse files Browse the repository at this point in the history
  • Loading branch information
Consti10 committed Aug 10, 2023
1 parent ee15273 commit 74109e7
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 101 deletions.
4 changes: 2 additions & 2 deletions executables/benchmark.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,9 @@ void benchmark_fec_encode(const Options &options, bool printBlockTime = false) {
void benchmark_crypt(const Options &options,const bool packet_validation_only) {
assert(options.benchmarkType == BENCHMARK_ENCRYPT || options.benchmarkType == BENCHMARK_DECRYPT);
const bool encrypt=options.benchmarkType==BENCHMARK_ENCRYPT;
Encryptor encryptor{std::nullopt};
wb::Encryptor encryptor{wb::generate_keypair_deterministic(true)};
encryptor.set_encryption_enabled(!packet_validation_only);
Decryptor decryptor{std::nullopt};
wb::Decryptor decryptor{wb::generate_keypair_deterministic(true)};
decryptor.set_encryption_enabled(!packet_validation_only);
std::array<uint8_t, crypto_box_NONCEBYTES> sessionKeyNonce{};
std::array<uint8_t, crypto_aead_chacha20poly1305_KEYBYTES + crypto_box_MACBYTES> sessionKeyData{};
Expand Down
20 changes: 15 additions & 5 deletions executables/unit_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,17 +114,27 @@ static void test_encrypt_decrypt_validate(const bool useGeneratedFiles,bool mess
//const std::string filename_drone="drone.key";
const std::string filename_gs="../example_keys/gs.key";
const std::string filename_drone="../example_keys/drone.key";
std::optional<std::string> encKey = useGeneratedFiles ? std::optional<std::string>(filename_gs) : std::nullopt;
std::optional<std::string> decKey = useGeneratedFiles ? std::optional<std::string>(filename_drone) : std::nullopt;
wb::Keypair encKey{};
wb::Keypair decKey{};
if(useGeneratedFiles){
encKey=wb::read_keypair_from_file(filename_gs);
decKey=wb::read_keypair_from_file(filename_drone);
}else{
/*encKey=wb::generate_keypair_deterministic(false);
decKey=wb::generate_keypair_deterministic(false);*/
auto tmp=wb::generate_keypair_from_bind_phrase("openhd");
encKey=tmp.drone;
decKey=tmp.drone;
}
if(message_signing_only){
std::cout<<"Testing message signing\n";
}else{
std::cout<<"Testing encryption & signing\n";
}

Encryptor encryptor{encKey};
wb::Encryptor encryptor{encKey};
encryptor.set_encryption_enabled(!message_signing_only);
Decryptor decryptor{decKey};
wb::Decryptor decryptor{decKey};
decryptor.set_encryption_enabled(!message_signing_only);
struct SessionStuff{
std::array<uint8_t, crypto_box_NONCEBYTES> sessionKeyNonce{}; // random data
Expand All @@ -135,7 +145,7 @@ static void test_encrypt_decrypt_validate(const bool useGeneratedFiles,bool mess
encryptor.makeNewSessionKey(sessionKeyPacket.sessionKeyNonce, sessionKeyPacket.sessionKeyData);
// and "receive" session key (rx)
assert(decryptor.onNewPacketSessionKeyData(sessionKeyPacket.sessionKeyNonce, sessionKeyPacket.sessionKeyData)
== Decryptor::SESSION_VALID_NEW);
== wb::Decryptor::SESSION_VALID_NEW);
// now encrypt a couple of packets and decrypt them again afterwards
for (uint64_t nonce = 0; nonce < 200; nonce++) {
const auto data = GenericHelper::createRandomDataBuffer(FEC_PACKET_MAX_PAYLOAD_SIZE);
Expand Down
192 changes: 104 additions & 88 deletions src/Encryption.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,97 @@ static_assert(crypto_onetimeauth_BYTES==crypto_aead_chacha20poly1305_ABYTES);
// Encryption (or packet validation) adds this many bytes to the end of the message
static constexpr auto ENCRYPTION_ADDITIONAL_VALIDATION_DATA=crypto_aead_chacha20poly1305_ABYTES;

namespace wb{

struct Keypair{
std::array<uint8_t,crypto_box_PUBLICKEYBYTES> public_key;
std::array<uint8_t,crypto_box_SECRETKEYBYTES> secret_key;
};

struct KeypairData{
// NOTE: The key itself for drone exists of drone.secret and ground.public
Keypair drone;
Keypair ground;
};

// Generates a new keypair. Non-deterministic, 100% secure.
static KeypairData generate_keypair(){
KeypairData ret{};
crypto_box_keypair(ret.drone.public_key.data(), ret.drone.secret_key.data());
crypto_box_keypair(ret.ground.public_key.data(), ret.ground.secret_key.data());
return ret;
}
static Keypair generate_keypair_deterministic(bool is_air){
Keypair ret{};
std::array<uint8_t , crypto_box_SEEDBYTES> seed1{0};
std::array<uint8_t , crypto_box_SEEDBYTES> seed2{1};
crypto_box_seed_keypair(ret.public_key.data(), ret.secret_key.data(),is_air ? seed1.data(): seed2.data());
return ret;
}

/**
* Generates a deterministic keypair from the openhd bind_phrase. Deterministic,
* same bind phrase will always generate the same key-pairs (but reversing is hard)
*/
static KeypairData generate_keypair_from_bind_phrase(const std::string& bind_phrase=""){
// Simple default seed, different for air and ground
std::array<uint8_t , crypto_box_SEEDBYTES> seed_ground{0};
std::array<uint8_t , crypto_box_SEEDBYTES> seed_drone{UINT8_MAX};
assert(bind_phrase.length()<=seed_ground.size());
// We just use the bind-phrase as seed
memcpy(seed_ground.data(),bind_phrase.c_str(),bind_phrase.length());
memcpy(seed_drone.data(),bind_phrase.c_str(),bind_phrase.length());
KeypairData ret{};
crypto_box_seed_keypair(ret.drone.public_key.data(), ret.drone.secret_key.data(),seed_drone.data());
crypto_box_seed_keypair(ret.ground.public_key.data(), ret.ground.secret_key.data(),seed_ground.data());
return ret;
}

static int write_keypair_to_file(const Keypair& keypair,const std::string& filename){
FILE *fp;
if ((fp = fopen(filename.c_str(), "w")) == nullptr) {
std::cerr<<"Unable to save "<<filename<<std::endl;
return 1;
}
fwrite(keypair.secret_key.data(), crypto_box_SECRETKEYBYTES, 1, fp);
fwrite(keypair.public_key.data(), crypto_box_PUBLICKEYBYTES, 1, fp);
fclose(fp);
return 0;
}

static Keypair read_keypair_from_file(const std::string& filename){
Keypair ret{};
FILE *fp;
if ((fp = fopen(filename.c_str(), "r")) == nullptr) {
throw std::runtime_error(fmt::format("Unable to open {}: {}", filename.c_str(), strerror(errno)));
}
if (fread(ret.secret_key.data(), crypto_box_SECRETKEYBYTES, 1, fp) != 1) {
fclose(fp);
throw std::runtime_error(fmt::format("Unable to read secret key: {}", strerror(errno)));
}
if (fread(ret.public_key.data(), crypto_box_PUBLICKEYBYTES, 1, fp) != 1) {
fclose(fp);
throw std::runtime_error(fmt::format("Unable to read public key: {}", strerror(errno)));
}
fclose(fp);
return ret;
}

static int write_to_file(const KeypairData& data){
if(!write_keypair_to_file(Keypair{data.drone.secret_key,data.ground.public_key},"drone.key")){
return 1;
}
fprintf(stderr, "Drone keypair (drone sec + gs pub) saved to drone.key\n");

if(!write_keypair_to_file(Keypair{data.ground.secret_key,data.drone.public_key},"gs.key")){
return 1;
}
fprintf(stderr, "GS keypair (gs sec + drone pub) saved to gs.key\n");

return 0;
}


// https://libsodium.gitbook.io/doc/key_derivation
// Helper since we both support encryption and one time validation to save cpu performance
static std::array<uint8_t,32> create_onetimeauth_subkey(const uint64_t nonce,const std::array<uint8_t, crypto_aead_chacha20poly1305_KEYBYTES> session_key){
Expand All @@ -46,26 +137,9 @@ class Encryptor {
* @param keypair encryption key, otherwise enable a default deterministic encryption key by using std::nullopt
* @param DISABLE_ENCRYPTION_FOR_PERFORMANCE only validate, do not encrypt (less CPU usage)
*/
explicit Encryptor(std::optional<std::string> keypair){
if (keypair == std::nullopt) {
// use default encryption keys
crypto_box_seed_keypair(rx_publickey.data(), tx_secretkey.data(), DEFAULT_ENCRYPTION_SEED.data());
wifibroadcast::log::get_default()->debug("Using default keys");
} else {
FILE *fp;
if ((fp = fopen(keypair->c_str(), "r")) == nullptr) {
throw std::runtime_error(fmt::format("Unable to open {}: {}", keypair->c_str(), strerror(errno)));
}
if (fread(tx_secretkey.data(), crypto_box_SECRETKEYBYTES, 1, fp) != 1) {
fclose(fp);
throw std::runtime_error(fmt::format("Unable to read tx secret key: {}", strerror(errno)));
}
if (fread(rx_publickey.data(), crypto_box_PUBLICKEYBYTES, 1, fp) != 1) {
fclose(fp);
throw std::runtime_error(fmt::format("Unable to read rx public key: {}", strerror(errno)));
}
fclose(fp);
}
explicit Encryptor(wb::Keypair keypair)
: tx_secretkey(keypair.secret_key),
rx_publickey(keypair.public_key){
}
/**
* Creates a new session key, simply put, the data we can send publicly
Expand Down Expand Up @@ -95,7 +169,7 @@ class Encryptor {
if(!m_encrypt_data){
memcpy(dest,src, src_len);
uint8_t* sign=dest+src_len;
const auto sub_key=create_onetimeauth_subkey(nonce,session_key);
const auto sub_key=wb::create_onetimeauth_subkey(nonce,session_key);
crypto_onetimeauth(sign,src,src_len,sub_key.data());
return src_len+crypto_onetimeauth_BYTES;
}
Expand Down Expand Up @@ -123,8 +197,8 @@ class Encryptor {
}
private:
// tx->rx keypair
std::array<uint8_t, crypto_box_SECRETKEYBYTES> tx_secretkey{};
std::array<uint8_t, crypto_box_PUBLICKEYBYTES> rx_publickey{};
const std::array<uint8_t, crypto_box_SECRETKEYBYTES> tx_secretkey{};
const std::array<uint8_t, crypto_box_PUBLICKEYBYTES> rx_publickey{};
std::array<uint8_t, crypto_aead_chacha20poly1305_KEYBYTES> session_key{};
// use this one if you are worried about CPU usage when using encryption
bool m_encrypt_data= true;
Expand All @@ -134,34 +208,15 @@ class Decryptor {
public:
// enable a default deterministic encryption key by using std::nullopt
// else, pass path to file with encryption keys
explicit Decryptor(std::optional<std::string> keypair){
if (keypair == std::nullopt) {
crypto_box_seed_keypair(tx_publickey.data(), rx_secretkey.data(), DEFAULT_ENCRYPTION_SEED.data());
wifibroadcast::log::get_default()->debug("Using default keys");
} else {
FILE *fp;
if ((fp = fopen(keypair->c_str(), "r")) == nullptr) {
throw std::runtime_error(fmt::format("Unable to open {}: {}", keypair->c_str(), strerror(errno)));
}
if (fread(rx_secretkey.data(), crypto_box_SECRETKEYBYTES, 1, fp) != 1) {
fclose(fp);
throw std::runtime_error(fmt::format("Unable to read rx secret key: {}", strerror(errno)));
}
if (fread(tx_publickey.data(), crypto_box_PUBLICKEYBYTES, 1, fp) != 1) {
fclose(fp);
throw std::runtime_error(fmt::format("Unable to read tx public key: {}", strerror(errno)));
}
fclose(fp);
}
explicit Decryptor(wb::Keypair keypair)
:rx_secretkey(keypair.secret_key),tx_publickey(keypair.public_key){
memset(session_key.data(), 0, sizeof(session_key));
}
private:
// use this one if you are worried about CPU usage when using encryption
bool m_encrypt_data= true;
public:
std::array<uint8_t, crypto_box_SECRETKEYBYTES> rx_secretkey{};
public:
std::array<uint8_t, crypto_box_PUBLICKEYBYTES> tx_publickey{};
const std::array<uint8_t, crypto_box_SECRETKEYBYTES> rx_secretkey{};
const std::array<uint8_t, crypto_box_PUBLICKEYBYTES> tx_publickey{};
std::array<uint8_t, crypto_aead_chacha20poly1305_KEYBYTES> session_key{};
public:
static constexpr auto SESSION_VALID_NEW=0;
Expand All @@ -174,7 +229,7 @@ class Decryptor {
*
*/
int onNewPacketSessionKeyData(const std::array<uint8_t, crypto_box_NONCEBYTES> &sessionKeyNonce,
const std::array<uint8_t,crypto_aead_chacha20poly1305_KEYBYTES+ crypto_box_MACBYTES> &sessionKeyData) {
const std::array<uint8_t,crypto_aead_chacha20poly1305_KEYBYTES+ crypto_box_MACBYTES> &sessionKeyData) {
std::array<uint8_t, sizeof(session_key)> new_session_key{};
if (crypto_box_open_easy(new_session_key.data(),
sessionKeyData.data(), sessionKeyData.size(),
Expand Down Expand Up @@ -204,7 +259,7 @@ class Decryptor {
assert(payload_size>0);
const uint8_t* sign=encrypted+payload_size;
//const int res=crypto_auth_hmacsha256_verify(sign,msg,payload_size,session_key.data());
const auto sub_key=create_onetimeauth_subkey(nonce,session_key);
const auto sub_key=wb::create_onetimeauth_subkey(nonce,session_key);
const int res=crypto_onetimeauth_verify(sign,encrypted,payload_size,sub_key.data());
if(res!=-1){
memcpy(dest,encrypted,payload_size);
Expand Down Expand Up @@ -244,46 +299,7 @@ class Decryptor {
}
};

namespace wbencryption{

struct KeypairData{
unsigned char drone_publickey[crypto_box_PUBLICKEYBYTES];
unsigned char drone_secretkey[crypto_box_SECRETKEYBYTES];
unsigned char gs_publickey[crypto_box_PUBLICKEYBYTES];
unsigned char gs_secretkey[crypto_box_SECRETKEYBYTES];
};

static KeypairData generate_keypair(){
KeypairData ret{};
crypto_box_keypair(ret.drone_publickey, ret.drone_secretkey);
crypto_box_keypair(ret.gs_publickey, ret.gs_secretkey);
return ret;
}

static int write_to_file(const KeypairData& data){
FILE *fp;
if ((fp = fopen("drone.key", "w")) == NULL) {
perror("Unable to save drone.key");
return 1;
}
fwrite(data.drone_secretkey, crypto_box_SECRETKEYBYTES, 1, fp);
fwrite(data.gs_publickey, crypto_box_PUBLICKEYBYTES, 1, fp);
fclose(fp);

fprintf(stderr, "Drone keypair (drone sec + gs pub) saved to drone.key\n");

if ((fp = fopen("gs.key", "w")) == NULL) {
perror("Unable to save gs.key");
return 1;
}

fwrite(data.gs_secretkey, crypto_box_SECRETKEYBYTES, 1, fp);
fwrite(data.drone_publickey, crypto_box_PUBLICKEYBYTES, 1, fp);
fclose(fp);
fprintf(stderr, "GS keypair (gs sec + drone pub) saved to gs.key\n");
return 0;
}
} // namespace wb end

}

#endif //ENCRYPTION_HPP
14 changes: 10 additions & 4 deletions src/WBTxRx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,14 @@ WBTxRx::WBTxRx(std::vector<WifiCard> wifi_cards1,Options options1)
m_receive_pollfds[i].fd = fd;
m_receive_pollfds[i].events = POLLIN;
}
m_encryptor=std::make_unique<Encryptor>(m_options.encryption_key);
m_decryptor=std::make_unique<Decryptor>(m_options.encryption_key);
wb::Keypair keypair{};
if(m_options.encryption_key.has_value()){
keypair= wb::read_keypair_from_file(m_options.encryption_key.value());
}else{
keypair=wb::generate_keypair_deterministic(true);
}
//m_encryptor=std::make_unique<Encryptor>(m_options.encryption_key);
//m_decryptor=std::make_unique<Decryptor>(m_options.encryption_key);
m_encryptor->makeNewSessionKey(m_tx_sess_key_packet.sessionKeyNonce,m_tx_sess_key_packet.sessionKeyData);
// next session key in delta ms if packets are being fed
m_session_key_next_announce_ts = std::chrono::steady_clock::now();
Expand Down Expand Up @@ -376,11 +382,11 @@ void WBTxRx::on_new_packet(const uint8_t wlan_idx, const pcap_pkthdr &hdr,
}*/
SessionKeyPacket &sessionKeyPacket = *((SessionKeyPacket*) parsedPacket->payload);
const auto decrypt_res=m_decryptor->onNewPacketSessionKeyData(sessionKeyPacket.sessionKeyNonce, sessionKeyPacket.sessionKeyData);
if(wlan_idx==0 && (decrypt_res==Decryptor::SESSION_VALID_NEW || decrypt_res==Decryptor::SESSION_VALID_NOT_NEW)){
if(wlan_idx==0 && (decrypt_res==wb::Decryptor::SESSION_VALID_NEW || decrypt_res==wb::Decryptor::SESSION_VALID_NOT_NEW)){
m_pollution_openhd_rx_packets++;
recalculate_pollution_perc();
}
if (decrypt_res==Decryptor::SESSION_VALID_NEW) {
if (decrypt_res==wb::Decryptor::SESSION_VALID_NEW) {
m_console->debug("Initializing new session.");
m_rx_stats.n_received_valid_session_key_packets++;
for(auto& handler:m_rx_handlers){
Expand Down
4 changes: 2 additions & 2 deletions src/WBTxRx.h
Original file line number Diff line number Diff line change
Expand Up @@ -283,8 +283,8 @@ class WBTxRx {
// For multiple RX cards the card with the highest rx rssi is used to inject packets on
std::atomic<int> m_curr_tx_card=0;
SessionKeyPacket m_tx_sess_key_packet;
std::unique_ptr<Encryptor> m_encryptor;
std::unique_ptr<Decryptor> m_decryptor;
std::unique_ptr<wb::Encryptor> m_encryptor;
std::unique_ptr<wb::Decryptor> m_decryptor;
struct PcapTxRx{
pcap_t *tx= nullptr;
pcap_t *rx= nullptr;
Expand Down

0 comments on commit 74109e7

Please sign in to comment.