Precalculate keys and destroy in-memory ephemeral key copies ASAP.
This commit is contained in:
53
crypto.cc
53
crypto.cc
@@ -49,7 +49,12 @@ void CryptoUtil::DerivePublicKey(const SecretKey& secret_key, PublicKey* public_
|
|||||||
public_key->MarkSet();
|
public_key->MarkSet();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<TLVNode> CryptoUtil::EncodeEncrypt(const SecretKey& secret_key, const PublicKey& public_key, const TLVNode& input) {
|
void CryptoUtil::PrecalculateKey(const SecretKey& secret_key, const PublicKey& public_key, PrecalcKey* precalc_key) {
|
||||||
|
assert(!crypto_box_beforenm(precalc_key->MutableKey(), public_key.Key(), secret_key.Key()));
|
||||||
|
precalc_key->MarkSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<TLVNode> CryptoUtil::EncodeEncrypt(const PrecalcKey& precalc_key, const TLVNode& input) {
|
||||||
std::string encoded;
|
std::string encoded;
|
||||||
input.Encode(&encoded);
|
input.Encode(&encoded);
|
||||||
|
|
||||||
@@ -59,7 +64,7 @@ std::unique_ptr<TLVNode> CryptoUtil::EncodeEncrypt(const SecretKey& secret_key,
|
|||||||
randombytes_buf(nonce, crypto_box_NONCEBYTES);
|
randombytes_buf(nonce, crypto_box_NONCEBYTES);
|
||||||
|
|
||||||
unsigned char output[encrypted_bytes];
|
unsigned char output[encrypted_bytes];
|
||||||
assert(!crypto_box_easy(output, (const unsigned char*)encoded.data(), encoded.length(), nonce, public_key.Key(), secret_key.Key()));
|
assert(!crypto_box_easy_afternm(output, (const unsigned char*)encoded.data(), encoded.length(), nonce, precalc_key.Key()));
|
||||||
|
|
||||||
std::unique_ptr<TLVNode> encrypted(new TLVNode(TLV_TYPE_ENCRYPTED));
|
std::unique_ptr<TLVNode> encrypted(new TLVNode(TLV_TYPE_ENCRYPTED));
|
||||||
encrypted->AppendChild(new TLVNode(TLV_TYPE_NONCE, std::string((char*)nonce, crypto_box_NONCEBYTES)));
|
encrypted->AppendChild(new TLVNode(TLV_TYPE_NONCE, std::string((char*)nonce, crypto_box_NONCEBYTES)));
|
||||||
@@ -68,7 +73,7 @@ std::unique_ptr<TLVNode> CryptoUtil::EncodeEncrypt(const SecretKey& secret_key,
|
|||||||
return encrypted;
|
return encrypted;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<TLVNode> CryptoUtil::DecryptDecode(const SecretKey& secret_key, const PublicKey& public_key, const TLVNode& input) {
|
std::unique_ptr<TLVNode> CryptoUtil::DecryptDecode(const PrecalcKey& precalc_key, const TLVNode& input) {
|
||||||
assert(input.GetType() == TLV_TYPE_ENCRYPTED);
|
assert(input.GetType() == TLV_TYPE_ENCRYPTED);
|
||||||
|
|
||||||
auto nonce = input.FindChild(TLV_TYPE_NONCE);
|
auto nonce = input.FindChild(TLV_TYPE_NONCE);
|
||||||
@@ -83,7 +88,7 @@ std::unique_ptr<TLVNode> CryptoUtil::DecryptDecode(const SecretKey& secret_key,
|
|||||||
size_t decrypted_bytes = encrypted->GetValue().length() - crypto_box_MACBYTES;
|
size_t decrypted_bytes = encrypted->GetValue().length() - crypto_box_MACBYTES;
|
||||||
|
|
||||||
unsigned char output[decrypted_bytes];
|
unsigned char output[decrypted_bytes];
|
||||||
if (crypto_box_open_easy(output, (const unsigned char*)encrypted->GetValue().data(), encrypted->GetValue().length(), (const unsigned char*)nonce->GetValue().data(), public_key.Key(), secret_key.Key())) {
|
if (crypto_box_open_easy_afternm(output, (const unsigned char*)encrypted->GetValue().data(), encrypted->GetValue().length(), (const unsigned char*)nonce->GetValue().data(), precalc_key.Key())) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,10 +104,13 @@ CryptoKey::CryptoKey(const size_t key_bytes)
|
|||||||
}
|
}
|
||||||
|
|
||||||
CryptoKey::~CryptoKey() {
|
CryptoKey::~CryptoKey() {
|
||||||
sodium_free(key_);
|
if (key_) {
|
||||||
|
sodium_free(key_);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CryptoKey::WriteToFile(const std::string& filename) const {
|
void CryptoKey::WriteToFile(const std::string& filename) const {
|
||||||
|
assert(key_);
|
||||||
assert(is_set_);
|
assert(is_set_);
|
||||||
int fd = open(filename.c_str(), O_WRONLY | O_CREAT | O_EXCL, 0400);
|
int fd = open(filename.c_str(), O_WRONLY | O_CREAT | O_EXCL, 0400);
|
||||||
assert(fd != -1);
|
assert(fd != -1);
|
||||||
@@ -111,6 +119,7 @@ void CryptoKey::WriteToFile(const std::string& filename) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CryptoKey::ReadFromFile(const std::string& filename) {
|
void CryptoKey::ReadFromFile(const std::string& filename) {
|
||||||
|
assert(key_);
|
||||||
assert(!is_set_);
|
assert(!is_set_);
|
||||||
int fd = open(filename.c_str(), O_RDONLY);
|
int fd = open(filename.c_str(), O_RDONLY);
|
||||||
assert(fd != -1);
|
assert(fd != -1);
|
||||||
@@ -120,25 +129,34 @@ void CryptoKey::ReadFromFile(const std::string& filename) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const unsigned char* CryptoKey::Key() const {
|
const unsigned char* CryptoKey::Key() const {
|
||||||
|
assert(key_);
|
||||||
assert(is_set_);
|
assert(is_set_);
|
||||||
return key_;
|
return key_;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CryptoKey::IsSet() const {
|
bool CryptoKey::IsSet() const {
|
||||||
|
assert(key_);
|
||||||
return is_set_;
|
return is_set_;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char* CryptoKey::MutableKey() {
|
unsigned char* CryptoKey::MutableKey() {
|
||||||
|
assert(key_);
|
||||||
assert(!is_set_);
|
assert(!is_set_);
|
||||||
return key_;
|
return key_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CryptoKey::MarkSet() {
|
void CryptoKey::MarkSet() {
|
||||||
|
assert(key_);
|
||||||
assert(!is_set_);
|
assert(!is_set_);
|
||||||
is_set_ = true;
|
is_set_ = true;
|
||||||
assert(!sodium_mprotect_readonly(key_));
|
assert(!sodium_mprotect_readonly(key_));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CryptoKey::Clear() {
|
||||||
|
sodium_free(key_);
|
||||||
|
key_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
SharedKey::SharedKey()
|
SharedKey::SharedKey()
|
||||||
: CryptoKey(crypto_secretbox_KEYBYTES) {}
|
: CryptoKey(crypto_secretbox_KEYBYTES) {}
|
||||||
@@ -175,6 +193,10 @@ void PublicKey::FromString(const std::string& str) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PrecalcKey::PrecalcKey()
|
||||||
|
: CryptoKey(crypto_box_BEFORENMBYTES) {}
|
||||||
|
|
||||||
|
|
||||||
std::ostream& CryptoBase::Log(void *obj) {
|
std::ostream& CryptoBase::Log(void *obj) {
|
||||||
char buf[64];
|
char buf[64];
|
||||||
snprintf(buf, 64, "[%p] ", obj ? obj : this);
|
snprintf(buf, 64, "[%p] ", obj ? obj : this);
|
||||||
@@ -202,9 +224,15 @@ std::unique_ptr<TLVNode> CryptoPubConnBase::BuildSecureHandshake() {
|
|||||||
PublicKey ephemeral_public_key;
|
PublicKey ephemeral_public_key;
|
||||||
CryptoUtil::GenKeyPair(&ephemeral_secret_key_, &ephemeral_public_key);
|
CryptoUtil::GenKeyPair(&ephemeral_secret_key_, &ephemeral_public_key);
|
||||||
|
|
||||||
|
if (peer_ephemeral_public_key_.IsSet()) {
|
||||||
|
CryptoUtil::PrecalculateKey(ephemeral_secret_key_, peer_ephemeral_public_key_, &ephemeral_precalc_key_);
|
||||||
|
ephemeral_secret_key_.Clear();
|
||||||
|
peer_ephemeral_public_key_.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
TLVNode secure_handshake(TLV_TYPE_HANDSHAKE_SECURE);
|
TLVNode secure_handshake(TLV_TYPE_HANDSHAKE_SECURE);
|
||||||
secure_handshake.AppendChild(new TLVNode(TLV_TYPE_PUBLIC_KEY, ephemeral_public_key.AsString()));
|
secure_handshake.AppendChild(new TLVNode(TLV_TYPE_PUBLIC_KEY, ephemeral_public_key.AsString()));
|
||||||
return CryptoUtil::EncodeEncrypt(secret_key_, peer_public_key_, secure_handshake);
|
return CryptoUtil::EncodeEncrypt(precalc_key_, secure_handshake);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<TLVNode> CryptoPubConnBase::BuildHandshake() {
|
std::unique_ptr<TLVNode> CryptoPubConnBase::BuildHandshake() {
|
||||||
@@ -227,7 +255,7 @@ void CryptoPubConnBase::SendHandshake() {
|
|||||||
bool CryptoPubConnBase::HandleSecureHandshake(const TLVNode& node) {
|
bool CryptoPubConnBase::HandleSecureHandshake(const TLVNode& node) {
|
||||||
assert(node.GetType() == TLV_TYPE_ENCRYPTED);
|
assert(node.GetType() == TLV_TYPE_ENCRYPTED);
|
||||||
|
|
||||||
std::unique_ptr<TLVNode> decrypted(CryptoUtil::DecryptDecode(secret_key_, peer_public_key_, node));
|
std::unique_ptr<TLVNode> decrypted(CryptoUtil::DecryptDecode(precalc_key_, node));
|
||||||
if (!decrypted.get()) {
|
if (!decrypted.get()) {
|
||||||
LogFatal("Protocol error (handshake; decryption failure)");
|
LogFatal("Protocol error (handshake; decryption failure)");
|
||||||
return false;
|
return false;
|
||||||
@@ -243,6 +271,11 @@ bool CryptoPubConnBase::HandleSecureHandshake(const TLVNode& node) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
peer_ephemeral_public_key_.FromString(peer_ephemeral_public_key->GetValue());
|
peer_ephemeral_public_key_.FromString(peer_ephemeral_public_key->GetValue());
|
||||||
|
if (ephemeral_secret_key_.IsSet()) {
|
||||||
|
CryptoUtil::PrecalculateKey(ephemeral_secret_key_, peer_ephemeral_public_key_, &ephemeral_precalc_key_);
|
||||||
|
ephemeral_secret_key_.Clear();
|
||||||
|
peer_ephemeral_public_key_.Clear();
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,6 +303,7 @@ bool CryptoPubConnBase::HandleHandshake(const TLVNode& node) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
peer_public_key_.FromString(peer_public_key->GetValue());
|
peer_public_key_.FromString(peer_public_key->GetValue());
|
||||||
|
CryptoUtil::PrecalculateKey(secret_key_, peer_public_key_, &precalc_key_);
|
||||||
}
|
}
|
||||||
auto encrypted = node.FindChild(TLV_TYPE_ENCRYPTED);
|
auto encrypted = node.FindChild(TLV_TYPE_ENCRYPTED);
|
||||||
if (!encrypted) {
|
if (!encrypted) {
|
||||||
@@ -281,7 +315,7 @@ bool CryptoPubConnBase::HandleHandshake(const TLVNode& node) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CryptoPubConnBase::EncryptSend(const TLVNode& node) {
|
void CryptoPubConnBase::EncryptSend(const TLVNode& node) {
|
||||||
auto encrypted = CryptoUtil::EncodeEncrypt(ephemeral_secret_key_, peer_ephemeral_public_key_, node);
|
auto encrypted = CryptoUtil::EncodeEncrypt(ephemeral_precalc_key_, node);
|
||||||
std::string out;
|
std::string out;
|
||||||
encrypted->Encode(&out);
|
encrypted->Encode(&out);
|
||||||
bufferevent_write(bev_, out.data(), out.length());
|
bufferevent_write(bev_, out.data(), out.length());
|
||||||
@@ -313,7 +347,7 @@ void CryptoPubConnBase::OnReadable() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<TLVNode> decrypted(CryptoUtil::DecryptDecode(ephemeral_secret_key_, peer_ephemeral_public_key_, *decoded));
|
std::unique_ptr<TLVNode> decrypted(CryptoUtil::DecryptDecode(ephemeral_precalc_key_, *decoded));
|
||||||
if (!decrypted.get()) {
|
if (!decrypted.get()) {
|
||||||
LogFatal("Protocol error (decryption failure)");
|
LogFatal("Protocol error (decryption failure)");
|
||||||
return;
|
return;
|
||||||
@@ -437,6 +471,7 @@ CryptoPubClient::CryptoPubClient(struct sockaddr* addr, socklen_t addrlen, const
|
|||||||
channel_bitrates_(channel_bitrates) {
|
channel_bitrates_(channel_bitrates) {
|
||||||
bev_ = bufferevent_socket_new(event_base_, -1, BEV_OPT_CLOSE_ON_FREE);
|
bev_ = bufferevent_socket_new(event_base_, -1, BEV_OPT_CLOSE_ON_FREE);
|
||||||
peer_public_key_.FromString(server_public_key.AsString());
|
peer_public_key_.FromString(server_public_key.AsString());
|
||||||
|
CryptoUtil::PrecalculateKey(secret_key_, peer_public_key_, &precalc_key_);
|
||||||
|
|
||||||
bufferevent_setcb(bev_, &CryptoPubClient::OnReadable_, NULL, &CryptoPubClient::OnConnectOrError_, this);
|
bufferevent_setcb(bev_, &CryptoPubClient::OnReadable_, NULL, &CryptoPubClient::OnConnectOrError_, this);
|
||||||
bufferevent_enable(bev_, EV_READ);
|
bufferevent_enable(bev_, EV_READ);
|
||||||
|
|||||||
16
crypto.h
16
crypto.h
@@ -20,8 +20,10 @@ class CryptoKey {
|
|||||||
unsigned char* MutableKey();
|
unsigned char* MutableKey();
|
||||||
void MarkSet();
|
void MarkSet();
|
||||||
|
|
||||||
|
void Clear();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
unsigned char* const key_;
|
unsigned char* key_;
|
||||||
bool is_set_;
|
bool is_set_;
|
||||||
const size_t key_bytes_;
|
const size_t key_bytes_;
|
||||||
};
|
};
|
||||||
@@ -45,14 +47,20 @@ class PublicKey : public CryptoKey {
|
|||||||
void FromString(const std::string& str);
|
void FromString(const std::string& str);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class PrecalcKey : public CryptoKey {
|
||||||
|
public:
|
||||||
|
PrecalcKey();
|
||||||
|
};
|
||||||
|
|
||||||
class CryptoUtil {
|
class CryptoUtil {
|
||||||
public:
|
public:
|
||||||
static void GenKey(SharedKey* key);
|
static void GenKey(SharedKey* key);
|
||||||
static void GenKeyPair(SecretKey* secret_key, PublicKey* public_key);
|
static void GenKeyPair(SecretKey* secret_key, PublicKey* public_key);
|
||||||
static void DerivePublicKey(const SecretKey& secret_key, PublicKey* public_key);
|
static void DerivePublicKey(const SecretKey& secret_key, PublicKey* public_key);
|
||||||
|
static void PrecalculateKey(const SecretKey& secret_key, const PublicKey& public_key, PrecalcKey* precalc_key);
|
||||||
|
|
||||||
static std::unique_ptr<TLVNode> EncodeEncrypt(const SecretKey& secret_key, const PublicKey& public_key, const TLVNode& input);
|
static std::unique_ptr<TLVNode> EncodeEncrypt(const PrecalcKey& precalc_key, const TLVNode& input);
|
||||||
static std::unique_ptr<TLVNode> DecryptDecode(const SecretKey& secret_key, const PublicKey& public_key, const TLVNode& input);
|
static std::unique_ptr<TLVNode> DecryptDecode(const PrecalcKey& precalc_key, const TLVNode& input);
|
||||||
};
|
};
|
||||||
|
|
||||||
class CryptoBase {
|
class CryptoBase {
|
||||||
@@ -91,9 +99,11 @@ class CryptoPubConnBase : public CryptoBase {
|
|||||||
const SecretKey& secret_key_;
|
const SecretKey& secret_key_;
|
||||||
PublicKey public_key_;
|
PublicKey public_key_;
|
||||||
PublicKey peer_public_key_;
|
PublicKey peer_public_key_;
|
||||||
|
PrecalcKey precalc_key_;
|
||||||
|
|
||||||
SecretKey ephemeral_secret_key_;
|
SecretKey ephemeral_secret_key_;
|
||||||
PublicKey peer_ephemeral_public_key_;
|
PublicKey peer_ephemeral_public_key_;
|
||||||
|
PrecalcKey ephemeral_precalc_key_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CryptoPubServerConnection;
|
class CryptoPubServerConnection;
|
||||||
|
|||||||
Reference in New Issue
Block a user