Precalculate keys and destroy in-memory ephemeral key copies ASAP.

This commit is contained in:
Ian Gulliver
2015-02-08 22:36:23 +00:00
parent f35f9d8885
commit ac97f35e3c
2 changed files with 57 additions and 12 deletions

View File

@@ -49,7 +49,12 @@ void CryptoUtil::DerivePublicKey(const SecretKey& secret_key, PublicKey* public_
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;
input.Encode(&encoded);
@@ -59,7 +64,7 @@ std::unique_ptr<TLVNode> CryptoUtil::EncodeEncrypt(const SecretKey& secret_key,
randombytes_buf(nonce, crypto_box_NONCEBYTES);
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));
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;
}
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);
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;
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;
}
@@ -99,10 +104,13 @@ CryptoKey::CryptoKey(const size_t key_bytes)
}
CryptoKey::~CryptoKey() {
sodium_free(key_);
if (key_) {
sodium_free(key_);
}
}
void CryptoKey::WriteToFile(const std::string& filename) const {
assert(key_);
assert(is_set_);
int fd = open(filename.c_str(), O_WRONLY | O_CREAT | O_EXCL, 0400);
assert(fd != -1);
@@ -111,6 +119,7 @@ void CryptoKey::WriteToFile(const std::string& filename) const {
}
void CryptoKey::ReadFromFile(const std::string& filename) {
assert(key_);
assert(!is_set_);
int fd = open(filename.c_str(), O_RDONLY);
assert(fd != -1);
@@ -120,25 +129,34 @@ void CryptoKey::ReadFromFile(const std::string& filename) {
}
const unsigned char* CryptoKey::Key() const {
assert(key_);
assert(is_set_);
return key_;
}
bool CryptoKey::IsSet() const {
assert(key_);
return is_set_;
}
unsigned char* CryptoKey::MutableKey() {
assert(key_);
assert(!is_set_);
return key_;
}
void CryptoKey::MarkSet() {
assert(key_);
assert(!is_set_);
is_set_ = true;
assert(!sodium_mprotect_readonly(key_));
}
void CryptoKey::Clear() {
sodium_free(key_);
key_ = nullptr;
}
SharedKey::SharedKey()
: 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) {
char buf[64];
snprintf(buf, 64, "[%p] ", obj ? obj : this);
@@ -202,9 +224,15 @@ std::unique_ptr<TLVNode> CryptoPubConnBase::BuildSecureHandshake() {
PublicKey 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);
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() {
@@ -227,7 +255,7 @@ void CryptoPubConnBase::SendHandshake() {
bool CryptoPubConnBase::HandleSecureHandshake(const TLVNode& node) {
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()) {
LogFatal("Protocol error (handshake; decryption failure)");
return false;
@@ -243,6 +271,11 @@ bool CryptoPubConnBase::HandleSecureHandshake(const TLVNode& node) {
return false;
}
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;
}
@@ -270,6 +303,7 @@ bool CryptoPubConnBase::HandleHandshake(const TLVNode& node) {
}
} else {
peer_public_key_.FromString(peer_public_key->GetValue());
CryptoUtil::PrecalculateKey(secret_key_, peer_public_key_, &precalc_key_);
}
auto encrypted = node.FindChild(TLV_TYPE_ENCRYPTED);
if (!encrypted) {
@@ -281,7 +315,7 @@ bool CryptoPubConnBase::HandleHandshake(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;
encrypted->Encode(&out);
bufferevent_write(bev_, out.data(), out.length());
@@ -313,7 +347,7 @@ void CryptoPubConnBase::OnReadable() {
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()) {
LogFatal("Protocol error (decryption failure)");
return;
@@ -437,6 +471,7 @@ CryptoPubClient::CryptoPubClient(struct sockaddr* addr, socklen_t addrlen, const
channel_bitrates_(channel_bitrates) {
bev_ = bufferevent_socket_new(event_base_, -1, BEV_OPT_CLOSE_ON_FREE);
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_enable(bev_, EV_READ);

View File

@@ -20,8 +20,10 @@ class CryptoKey {
unsigned char* MutableKey();
void MarkSet();
void Clear();
protected:
unsigned char* const key_;
unsigned char* key_;
bool is_set_;
const size_t key_bytes_;
};
@@ -45,14 +47,20 @@ class PublicKey : public CryptoKey {
void FromString(const std::string& str);
};
class PrecalcKey : public CryptoKey {
public:
PrecalcKey();
};
class CryptoUtil {
public:
static void GenKey(SharedKey* key);
static void GenKeyPair(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> DecryptDecode(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 PrecalcKey& precalc_key, const TLVNode& input);
};
class CryptoBase {
@@ -91,9 +99,11 @@ class CryptoPubConnBase : public CryptoBase {
const SecretKey& secret_key_;
PublicKey public_key_;
PublicKey peer_public_key_;
PrecalcKey precalc_key_;
SecretKey ephemeral_secret_key_;
PublicKey peer_ephemeral_public_key_;
PrecalcKey ephemeral_precalc_key_;
};
class CryptoPubServerConnection;