blob: 3d5f5aacb660917b66b65c9118168c4eec75c997 [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/encrypted_messages/message_encrypter.h"
#include "base/logging.h"
#include "base/strings/string_piece.h"
#include "components/encrypted_messages/encrypted_message.pb.h"
#include "crypto/aead.h"
#include "crypto/hkdf.h"
#include "crypto/random.h"
#include "third_party/boringssl/src/include/openssl/curve25519.h"
namespace encrypted_messages {
namespace {
bool GetHkdfSubkeySecret(size_t subkey_length,
const uint8_t* private_key,
const uint8_t* public_key,
base::StringPiece hkdf_label,
std::string* secret) {
uint8_t shared_secret[X25519_SHARED_KEY_LEN];
if (!X25519(shared_secret, private_key, public_key))
return false;
base::StringPiece hkdf_input(reinterpret_cast<char*>(shared_secret),
sizeof(shared_secret));
*secret = crypto::HkdfSha256(hkdf_input, "", hkdf_label, subkey_length);
return true;
}
} // namespace
bool EncryptSerializedMessage(const uint8_t* server_public_key,
uint32_t server_public_key_version,
base::StringPiece hkdf_label,
const std::string& message,
EncryptedMessage* encrypted_message) {
// Generate an ephemeral key pair to generate a shared secret.
uint8_t public_key[X25519_PUBLIC_VALUE_LEN];
uint8_t private_key[X25519_PRIVATE_KEY_LEN];
crypto::RandBytes(private_key, sizeof(private_key));
X25519_public_from_private(public_key, private_key);
crypto::Aead aead(crypto::Aead::AES_128_CTR_HMAC_SHA256);
std::string key;
if (!GetHkdfSubkeySecret(aead.KeyLength(), private_key,
reinterpret_cast<const uint8_t*>(server_public_key),
hkdf_label, &key)) {
LOG(ERROR) << "Error getting subkey secret.";
return false;
}
aead.Init(&key);
// Use an all-zero nonce because the key is random per-message.
std::string nonce(aead.NonceLength(), '\0');
std::string ciphertext;
if (!aead.Seal(message, nonce, std::string(), &ciphertext)) {
LOG(ERROR) << "Error sealing message.";
return false;
}
encrypted_message->set_encrypted_message(ciphertext);
encrypted_message->set_server_public_key_version(server_public_key_version);
encrypted_message->set_client_public_key(reinterpret_cast<char*>(public_key),
sizeof(public_key));
encrypted_message->set_algorithm(
EncryptedMessage::AEAD_ECDH_AES_128_CTR_HMAC_SHA256);
return true;
}
// Used only by tests.
bool DecryptMessageForTesting(const uint8_t server_private_key[32],
base::StringPiece hkdf_label,
const EncryptedMessage& encrypted_message,
std::string* decrypted_serialized_message) {
crypto::Aead aead(crypto::Aead::AES_128_CTR_HMAC_SHA256);
std::string key;
if (!GetHkdfSubkeySecret(aead.KeyLength(), server_private_key,
reinterpret_cast<const uint8_t*>(
encrypted_message.client_public_key().data()),
hkdf_label, &key)) {
LOG(ERROR) << "Error getting subkey secret.";
return false;
}
aead.Init(&key);
// Use an all-zero nonce because the key is random per-message.
std::string nonce(aead.NonceLength(), 0);
return aead.Open(encrypted_message.encrypted_message(), nonce, std::string(),
decrypted_serialized_message);
}
} // namespace encrypted_messages