blob: fe44f7499b0a14c9ffdadc5cf9d87588a1b7e092 [file] [log] [blame]
/*
* Copyright 2014 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//
// This file is a partial implementation of the CryptoOps class. The remaining
// functions are specified in a crypto-library specific continuation (e.g., the
// OpenSSL versions are in cryto_ops_openssl.cc
//
#include <stddef.h>
#include "securemessage/crypto_ops.h"
#include "securemessage/secure_message_wrapper.h"
#include "securemessage/util.h"
using std::unique_ptr;
// Maximum number of bytes in a 2's complement encoding of a NIST P-256 elliptic
// curve point.
static const size_t kMaxP256EncodingBytes = 33;
// Maximum number of bytes in a 2's complement encoding of a 2048 bit RSA
// key modulus.
static const size_t kMaxRsa2048ModulusEncodingBytes = 257;
// The expected byte size of a 2048 bit RSA key modulus. The modulus bit size
// is 8 * |kRsa2048ModulusByteSize|.
static const size_t kRsa2048ModulusByteSize = 256;
// TODO(aczeskis): get rid of as many uses of string as possible and move to
// using ByteBuffer because of secure wipe. If easier and less refactoring,
// make a custom string that wipes memory on deallocation
namespace securemessage {
// We have to initialize salt this way because C++ doesn't allow initialization
// and declaration in the same place... Go figure.
// The value below is SHA256("SecureMessage"). This is tested in the TestSalt
// test in crypto_ops_test
const uint8_t CryptoOps::kSalt[kSaltSize] = {
0xbf, 0x9d, 0x2a, 0x53, 0xc6, 0x36, 0x16, 0xd7, 0x5d, 0xb0, 0xa7,
0x16, 0x5b, 0x91, 0xc1, 0xef, 0x73, 0xe5, 0x37, 0xf2, 0x42, 0x74,
0x05, 0xfa, 0x23, 0x61, 0x0a, 0x4b, 0xe6, 0x57, 0x64, 0x2e};
unique_ptr<ByteBuffer> CryptoOps::HkdfSha256Extract(
const ByteBuffer& inputKeyMaterial,
const ByteBuffer& salt) {
// Computes an HMAC of the inputKeyMaterial keyed with the salt
unique_ptr<ByteBuffer> result = Sha256hmac(salt, inputKeyMaterial);
if (result == nullptr) {
Util::LogError("HMAC returned null");
}
return result;
}
unique_ptr<ByteBuffer> CryptoOps::HkdfSha256Expand(
const ByteBuffer& pseudoRandomKey,
const ByteBuffer& info) {
if (pseudoRandomKey.size() == 0) {
Util::LogError("length of PseudoRandomKey is zero");
return nullptr;
}
// Computes an HMAC of info || 0x01 (where || is concatenation)
ByteBuffer extendedInfo = info;
extendedInfo.Append(static_cast<size_t>(1), static_cast<uint8_t>(0x01));
unique_ptr<ByteBuffer> result = Sha256hmac(pseudoRandomKey, extendedInfo);
if (result == nullptr) {
Util::LogError("HMAC returned null");
}
return result;
}
unique_ptr<string> CryptoOps::Hkdf(const string& inputKeyMaterial,
const string& salt,
const string& info) {
unique_ptr<ByteBuffer> extracted_result =
HkdfSha256Extract(ByteBuffer(inputKeyMaterial), ByteBuffer(salt));
if (extracted_result == nullptr || extracted_result->size() == 0) {
Util::LogError("HKDF_Extract returned an invalid result");
return nullptr;
}
unique_ptr<ByteBuffer> expanded_result =
HkdfSha256Expand(*extracted_result, ByteBuffer(info));
if (expanded_result == nullptr) {
Util::LogError("HkdfSha256Expand return an invalid result");
return nullptr;
}
return unique_ptr<string>(new string(expanded_result->String()));
}
unique_ptr<CryptoOps::SecretKey> CryptoOps::DeriveAes256KeyFor(
const CryptoOps::SecretKey& masterKey, const string& purpose) {
string key_string(masterKey.data().String());
string salt_string(reinterpret_cast<const char*>(kSalt), kSaltSize);
unique_ptr<string> key_data = Hkdf(key_string, salt_string, purpose);
if (key_data == nullptr || key_data->size() != kAesKeySize) {
Util::LogError("HKDF returned invalid key");
return nullptr;
}
unique_ptr<CryptoOps::SecretKey> derived_key =
unique_ptr<CryptoOps::SecretKey>(
new CryptoOps::SecretKey(*key_data, CryptoOps::AES_256_KEY));
return derived_key;
}
unique_ptr<string> CryptoOps::Digest(const string& data) {
unique_ptr<ByteBuffer> full_digest = Sha256(ByteBuffer(data));
if (full_digest == nullptr || full_digest->size() != kSha256DigestSize) {
return nullptr;
}
return unique_ptr<string>(
new string(full_digest->SubArray(0, kDigestLength)->String()));
}
unique_ptr<string> CryptoOps::Decrypt(const CryptoOps::SecretKey& decryptionKey,
CryptoOps::EncType encType,
const string& iv,
const string& ciphertext) {
if (ciphertext.empty()) {
Util::LogError("Cannot decrypt empty ciphertext");
return nullptr;
}
if (decryptionKey.data().size() == 0) {
Util::LogError("Cannot decrypt using empty decryption key");
return nullptr;
}
if (iv.empty()) {
Util::LogError("Cannot decrypt with empty iv");
return nullptr;
}
unique_ptr<ByteBuffer> plaintext = nullptr;
switch (encType) {
case NONE: {
plaintext = nullptr;
break;
}
case AES_256_CBC: {
unique_ptr<SecretKey> derived_key =
CryptoOps::DeriveAes256KeyFor(decryptionKey, GetPurpose(encType));
plaintext = Aes256CBCDecrypt(*derived_key, ByteBuffer(iv),
ByteBuffer(ciphertext));
break;
}
default:
// This should never happen and might indicate an attack
Util::LogError("Invalid encryption type");
return nullptr;
}
if (plaintext == nullptr) {
Util::LogError("Could not decrypt ciphertext");
return nullptr;
}
return unique_ptr<string>(new string(plaintext->String()));
}
unique_ptr<string> CryptoOps::Encrypt(const CryptoOps::SecretKey& encryptionKey,
CryptoOps::EncType encType,
const string& iv,
const string& plaintext) {
if (encryptionKey.data().size() == 0) {
Util::LogError("Cannot encrypt using empty encryption key");
return nullptr;
}
unique_ptr<ByteBuffer> ciphertext = nullptr;
switch (encType) {
case NONE: {
ciphertext = nullptr;
break;
}
case AES_256_CBC: {
unique_ptr<SecretKey> derived_key =
CryptoOps::DeriveAes256KeyFor(encryptionKey, GetPurpose(encType));
ciphertext =
Aes256CBCEncrypt(*derived_key, ByteBuffer(iv), ByteBuffer(plaintext));
break;
}
default:
// This should never happen and might indicate an attack
Util::LogError("Invalid encryption type");
return nullptr;
}
if (ciphertext == nullptr) {
Util::LogError("Could not encrypt plaintext");
return nullptr;
}
return unique_ptr<string>(new string(ciphertext->String()));
}
unique_ptr<CryptoOps::SecretKey> CryptoOps::KeyAgreementSha256(
const PrivateKey& private_key,
const PublicKey& peer_key) {
if (private_key.algorithm() != peer_key.algorithm()) {
Util::LogError("Algorithms for public and private key must match");
return nullptr;
}
if (private_key.algorithm() != ECDSA_KEY) {
Util::LogError("Only ECDSA algorithms are supported for Key agreements");
return nullptr;
}
unique_ptr<ByteBuffer> secret = EcdhKeyAgreement(private_key, peer_key);
if (secret == nullptr) {
Util::LogError("Error computing key agreement");
return nullptr;
}
unique_ptr<ByteBuffer> secret_digest = Sha256(*secret);
if (secret_digest == nullptr) {
Util::LogError("Error computing Sha256");
return nullptr;
}
return unique_ptr<SecretKey>(new SecretKey(*secret_digest, AES_256_KEY));
}
string CryptoOps::GetPurpose(EncType encType) {
return "ENC:" +
std::to_string(SecureMessageWrapper::GetEncScheme(encType));
}
string CryptoOps::GetPurpose(SigType sigType) {
return "SIG:" +
std::to_string(SecureMessageWrapper::GetSigScheme(sigType));
}
bool CryptoOps::IsPublicKeyScheme(SigType sigType) {
switch (sigType) {
case HMAC_SHA256:
return false;
case ECDSA_P256_SHA256:
return true;
case RSA2048_SHA256:
return true;
case SIG_TYPE_END:
// Exists only for testing, should never actually be called.
Util::LogErrorAndAbort("wrong sigtype");
}
// Control should never reach here, this is here to make the compiler happy
Util::LogErrorAndAbort("wrong sigtype");
return false;
}
unique_ptr<string> CryptoOps::Sign(SigType sigType, const Key& signingKey,
const string& data) {
if (signingKey.data().size() == 0) {
Util::LogError("Signing Key cannot be empty!");
return nullptr;
}
if (data.length() == 0) {
Util::LogError("Cannot sign empty data");
return nullptr;
}
unique_ptr<ByteBuffer> signature = nullptr;
switch (sigType) {
case HMAC_SHA256: {
if (signingKey.type() != SECRET) {
Util::LogError("Invalid signing key type");
return nullptr;
}
unique_ptr<SecretKey> derived_key = DeriveAes256KeyFor(
SecretKey(signingKey.data(), signingKey.algorithm()),
GetPurpose(sigType));
if (derived_key == nullptr) {
Util::LogError("Invalid derived key");
return nullptr;
}
signature = Sha256hmac(derived_key->data(), ByteBuffer(data));
break;
}
case ECDSA_P256_SHA256: {
if (signingKey.type() != PRIVATE) {
Util::LogError("Expected a private key");
return nullptr;
}
signature = EcdsaP256Sha256Sign(
PrivateKey(signingKey.data(), signingKey.algorithm()),
ByteBuffer(data));
break;
}
case RSA2048_SHA256: {
if (signingKey.type() != PRIVATE) {
Util::LogError("Expected a private key");
return nullptr;
}
signature = Rsa2048Sha256Sign(
PrivateKey(signingKey.data(), signingKey.algorithm()),
ByteBuffer(data));
break;
}
case SIG_TYPE_END:
// Case only exists for testing and is never expected to actually be
// called.
Util::LogErrorAndAbort("wrong sigtype");
}
if (signature == nullptr) {
return nullptr;
} else {
return unique_ptr<string>(new string(signature->String()));
}
}
bool CryptoOps::Verify(SigType sigType,
const Key& verificationKey,
const string& signature,
const string& data) {
// Do some basic checks
if (verificationKey.data().size() == 0) {
Util::LogError("Verification Key cannot be empty!");
return false;
}
if (signature.length() == 0) {
Util::LogError("Signature cannot be empty");
return false;
}
if (data.length() == 0) {
Util::LogError("Cannot verify signature over empty data");
return false;
}
// Decide what to do based on the signature type
switch (sigType) {
case HMAC_SHA256: {
if (verificationKey.type() != SECRET) {
Util::LogError("Invalid signing key type");
return nullptr;
}
// Compute expected signature
unique_ptr<SecretKey> derived_key = DeriveAes256KeyFor(
SecretKey(verificationKey.data(), verificationKey.algorithm()),
GetPurpose(sigType));
if (derived_key == nullptr) {
Util::LogError("Invalid derived key");
return false;
}
unique_ptr<ByteBuffer> expected_signature =
Sha256hmac(ByteBuffer(derived_key->data()), ByteBuffer(data));
if (expected_signature == nullptr) {
Util::LogError("Invalid expected signature");
return false;
}
// Constant time array comparison
return expected_signature->Equals(ByteBuffer(signature));
}
case ECDSA_P256_SHA256: {
if (verificationKey.type() != PUBLIC) {
Util::LogError("Expected a public key");
return false;
}
if (verificationKey.algorithm() != KeyAlgorithm::ECDSA_KEY) {
Util::LogError("Wrong key type");
return false;
}
return EcdsaP256Sha256Verify(
PublicKey(verificationKey.data(), verificationKey.algorithm()),
ByteBuffer(signature), ByteBuffer(data));
}
case RSA2048_SHA256: {
if (verificationKey.type() != PUBLIC) {
Util::LogError("Expected a public key");
return false;
}
if (verificationKey.algorithm() != KeyAlgorithm::RSA_KEY) {
Util::LogError("Wrong key type");
return false;
}
return Rsa2048Sha256Verify(
PublicKey(verificationKey.data(), verificationKey.algorithm()),
ByteBuffer(signature), ByteBuffer(data));
}
default:
return false;
}
}
bool CryptoOps::TaggedPlaintextRequired(const Key& signing_key,
SigType sig_type,
const Key& encryption_key) {
// We need a tag if different keys are being used to "sign" vs. encrypt
return IsPublicKeyScheme(sig_type) ||
!signing_key.data().Equals(encryption_key.data());
}
bool CryptoOps::IsValidEcP256CoordinateEncoding(const string& bytes) {
// The bytes must be between 1 and Max P256 encoding bytes, inclusive. If
// the bytes are full, then the first byte must not be 0.
return !(
(bytes.size() == 0) ||
(bytes.size() > kMaxP256EncodingBytes) ||
(bytes.size() == kMaxP256EncodingBytes && bytes.data()[0] != 0));
}
bool CryptoOps::IsValidRsa2048ModulusEncoding(const string& bytes) {
if (bytes.size() > kMaxRsa2048ModulusEncodingBytes)
return false;
if (bytes.size() < kRsa2048ModulusByteSize)
return false;
// Index at which the first non-zero byte in |bytes| should be.
const size_t leading_byte_index = bytes.size() - kRsa2048ModulusByteSize;
for (size_t i = 0; i < leading_byte_index; ++i) {
if (bytes[i] != 0)
return false;
}
// Make sure that the first non-zero byte has leading 1.
return static_cast<uint8_t>(bytes[leading_byte_index]) & 0x80;
}
string CryptoOps::Int32BytesToString(int32_t value) {
string result(sizeof(value), '\0');
for (size_t i = sizeof(value); i > 0 && value != 0; --i) {
result[i - 1] = static_cast<char>(value & 0xFF);
value >>= 8;
}
return result;
}
bool CryptoOps::StringToInt32Bytes(const string& value, int32_t* result) {
if (value.length() > sizeof(*result))
return false;
*result = 0;
for (size_t i = 0; i < value.length(); ++i) {
*result <<= 8;
*result |= static_cast<uint8_t>(value[i]);
}
return true;
}
CryptoOps::~CryptoOps() {}
} // namespace securemessage