blob: ddb3bb8e54606238a5fb896ddbaf890dfb7c5a08 [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.
*/
#include <openssl/aes.h>
#include <openssl/bn.h>
#include <openssl/ec.h>
#include <openssl/ecdsa.h>
#include <openssl/evp.h>
#include <openssl/obj_mac.h>
#include <openssl/ossl_typ.h>
#include <openssl/rand.h>
#include <openssl/rsa.h>
#include <openssl/x509.h>
#include <stddef.h>
#include <climits>
#include <cstdint>
#include <memory>
#include <string>
#include "securemessage/byte_buffer.h"
#include "securemessage/crypto_ops.h"
#include "securemessage/util.h"
using std::unique_ptr;
class EvpKeyPtr : public std::unique_ptr<EVP_PKEY, void (*)(EVP_PKEY *)> {
public:
explicit EvpKeyPtr(EVP_PKEY *pkp)
: std::unique_ptr<EVP_PKEY, void (*)(EVP_PKEY *)>(pkp, EVP_PKEY_free) {}
};
namespace securemessage {
static unique_ptr<ByteBuffer> PublicKeyToBytes(EC_KEY *eckey) {
int pub_key_len = i2d_EC_PUBKEY(eckey, NULL);
if (pub_key_len <= 0) {
Util::LogError("Unexpected pub key length for generated key");
return nullptr;
}
unsigned char *pub_key_buffer = NULL;
int return_code = i2d_EC_PUBKEY(eckey, &pub_key_buffer);
if (return_code != pub_key_len) {
if (pub_key_buffer != NULL) {
free(pub_key_buffer);
}
Util::LogErrorAndAbort("i2d_EC_PUBKEY returned an unexpected value");
return nullptr;
}
auto public_key_bytes = unique_ptr<ByteBuffer>(new ByteBuffer(
pub_key_buffer, pub_key_len));
free(pub_key_buffer);
return public_key_bytes;
}
// Create an EVP_PKEY structure using exported data for a private key.
static EvpKeyPtr CreateEvpPrivateKey(const CryptoOps::PrivateKey& key) {
// Extract key from storage.
const unsigned char *stored_priv_key = key.data().ImmutableUChar();
PKCS8_PRIV_KEY_INFO *extracted_pkcs8 =
d2i_PKCS8_PRIV_KEY_INFO(NULL, &stored_priv_key, key.data().size());
if (extracted_pkcs8 == NULL) {
Util::LogError("could not extract pkcs8 key");
return EvpKeyPtr(NULL);
}
// Put it into an EVP structure.
EvpKeyPtr extracted_evpkey(EVP_PKCS82PKEY(extracted_pkcs8));
// Careful to free this before we return null anywhere.
PKCS8_PRIV_KEY_INFO_free(extracted_pkcs8);
if (extracted_evpkey.get() == NULL) {
Util::LogError("could not stuff extracted key into evp structure");
return EvpKeyPtr(NULL);
}
return extracted_evpkey;
}
// Create an EVP_PKEY structure using exported data for a public key.
static EvpKeyPtr CreateEvpPublicKey(const CryptoOps::PublicKey& key) {
const unsigned char *pub_key_ptr = key.data().ImmutableUChar();
EvpKeyPtr extracted_public_evpkey(
d2i_PUBKEY(NULL, &pub_key_ptr, key.data().size()));
if (extracted_public_evpkey == nullptr) {
Util::LogError("could not import public key");
return EvpKeyPtr(NULL);
}
return extracted_public_evpkey;
}
unique_ptr<ByteBuffer> CryptoOps::Sha256(const ByteBuffer& message) {
if (message.size() == 0) {
Util::LogError("Message too short");
return nullptr;
}
// Create an empty digest with enough space. OpenSSL will tell us how much it
// actually wrote.
uint8_t md_value[EVP_MAX_MD_SIZE];
unsigned int md_length = 0;
int return_code;
// Create a new message digest context and initialize it
unique_ptr<EVP_MD_CTX, void (*)(EVP_MD_CTX *)> mdctx(EVP_MD_CTX_create(),
EVP_MD_CTX_destroy);
// Initialize the digest context
return_code = EVP_DigestInit_ex(mdctx.get(), EVP_sha256(), NULL);
if (return_code != 1) {
Util::LogErrorAndAbort("Could not initialize SHA256");
return nullptr;
}
// Set the message
return_code = EVP_DigestUpdate(mdctx.get(), message.ImmutableUInt8(),
message.size());
if (return_code != 1) {
Util::LogErrorAndAbort("Could not set message to hash in SHA256");
return nullptr;
}
// Execute the digest
return_code = EVP_DigestFinal_ex(mdctx.get(), md_value, &md_length);
if (return_code != 1) {
Util::LogErrorAndAbort("Could not set execute SHA256");
return nullptr;
}
return unique_ptr<ByteBuffer>(new ByteBuffer(md_value, md_length));
}
unique_ptr<ByteBuffer> CryptoOps::Sha256hmac(const ByteBuffer& key,
const ByteBuffer& message) {
// Sanity check
if (key.size() == 0 || message.size() == 0) {
Util::LogError("Hmac key or message too short");
return nullptr;
}
// Overflow check because of EVP_PKEY_new_mac_key function signature
if (key.size() > INT_MAX) {
Util::LogError("Key too big");
return nullptr;
}
// Create an empty digest with enough space. OpenSSL will tell us how much it
// actually wrote.
uint8_t md_value[kSha256DigestSize];
size_t md_length = 0;
int return_code; // Used to check all of OpenSSL's return codes
// Create the HMAC key structure
EvpKeyPtr hmacKey(
EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, key.ImmutableUInt8(),
static_cast<int>(key.size())));
if (hmacKey.get() == NULL) {
Util::LogErrorAndAbort("Could not create HMAC PKEY");
return nullptr;
}
// Create a new message digest context and initialize it
unique_ptr<EVP_MD_CTX, void (*)(EVP_MD_CTX *)> mdctx(EVP_MD_CTX_create(),
EVP_MD_CTX_destroy);
return_code =
EVP_DigestSignInit(mdctx.get(), NULL, EVP_sha256(), NULL, hmacKey.get());
if (return_code != 1) {
Util::LogErrorAndAbort("Could not initialize HMAC");
return nullptr;
}
// Set the message to digest
return_code = EVP_DigestSignUpdate(mdctx.get(), message.ImmutableUInt8(),
message.size());
if (return_code != 1) {
Util::LogErrorAndAbort("Could not set data to HMAC");
return nullptr;
}
// Compute the digest length and populate the md_length value. This lets us
// sanity check the expected digest length.
return_code = EVP_DigestSignFinal(mdctx.get(), NULL, &md_length);
if (return_code != 1) {
Util::LogErrorAndAbort("Could not determine HMAC length");
return nullptr;
}
if (md_length != kSha256DigestSize) {
Util::LogErrorAndAbort("HMAC returned an unexpected result");
return nullptr;
}
// Finally, compute the digest.
return_code = EVP_DigestSignFinal(mdctx.get(), md_value, &md_length);
if (return_code != 1) {
Util::LogErrorAndAbort("Could not execute HMAC");
return nullptr;
}
return unique_ptr<ByteBuffer>(new ByteBuffer(md_value, kSha256DigestSize));
}
unique_ptr<ByteBuffer> CryptoOps::Aes256CBCDecrypt(
const CryptoOps::SecretKey& decryptionKey,
const ByteBuffer& iv,
const ByteBuffer& ciphertext) {
if (decryptionKey.type() != SECRET) {
Util::LogError("AES Decrypt only works with SECRET keys");
return nullptr;
}
if (decryptionKey.algorithm() != AES_256_KEY) {
Util::LogError("AES Decrypt only works with AES 256 keys");
return nullptr;
}
if (decryptionKey.data().size() != kAesKeySize) {
Util::LogError("AES 256 key should be 32 bytes long");
return nullptr;
}
if (iv.size() != AES_BLOCK_SIZE) {
Util::LogError("Incorrect IV size");
return nullptr;
}
if (ciphertext.size() > INT_MAX) {
Util::LogError("Ciphertext too large to decrypt");
return nullptr;
}
ByteBuffer plaintext(ciphertext.size()); // Allocate space for plaintext
int return_code; // OpenSSL error checking
// Initialize the cipher context
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init(&ctx);
return_code = EVP_DecryptInit_ex(&ctx, EVP_aes_256_cbc(), NULL,
decryptionKey.data().ImmutableUInt8(),
iv.ImmutableUInt8());
if (return_code != 1) {
Util::LogErrorAndAbort("Could not initialize AES decryption");
return nullptr;
}
// Decrypt data
int plaintext_bytes_written = 0;
return_code =
EVP_DecryptUpdate(&ctx,
plaintext.MutableUInt8(),
&plaintext_bytes_written,
ciphertext.ImmutableUInt8(),
static_cast<int>(ciphertext.size()));
if (return_code != 1) {
Util::LogError("Could not decrypt data using AES");
EVP_CIPHER_CTX_cleanup(&ctx);
return nullptr;
}
// Handle the last block and padding
int last_block_bytes_written = 0;
return_code = EVP_DecryptFinal_ex(
&ctx, plaintext.MutableUInt8() + plaintext_bytes_written,
&last_block_bytes_written);
if (return_code != 1) {
Util::LogError("Could not decrypt data using AES; likely a padding error");
EVP_CIPHER_CTX_cleanup(&ctx);
return nullptr;
}
// Check that the correct number of bytes were written
if (plaintext_bytes_written > INT_MAX - last_block_bytes_written ||
static_cast<size_t>(plaintext_bytes_written + last_block_bytes_written) >
plaintext.size()) {
Util::LogErrorAndAbort("Unexpected amount of bytes written by AES Encrypt");
return nullptr;
}
// Cleanup
EVP_CIPHER_CTX_cleanup(&ctx);
return unique_ptr<ByteBuffer>(
new ByteBuffer(plaintext.ImmutableUInt8(),
plaintext_bytes_written + last_block_bytes_written));
}
unique_ptr<ByteBuffer> CryptoOps::Aes256CBCEncrypt(
const CryptoOps::SecretKey& encryptionKey,
const ByteBuffer& iv,
const ByteBuffer& plaintext) {
if (encryptionKey.type() != SECRET) {
Util::LogError("AES Encrypt only works with SECRET keys");
return nullptr;
}
if (encryptionKey.algorithm() != AES_256_KEY) {
Util::LogError("AES Encrypt only works with AES 256 keys");
return nullptr;
}
if (encryptionKey.data().size() != kAesKeySize) {
Util::LogError("AES 256 key should be 32 bytes long");
return nullptr;
}
if (iv.size() != AES_BLOCK_SIZE) {
Util::LogError("Incorrect IV size");
return nullptr;
}
// Calculate PKCS #5 padding size
size_t padding_size = AES_BLOCK_SIZE - (plaintext.size() % AES_BLOCK_SIZE);
// Paranoid bounds check to make sure that the static_cast<int> for
// EVP_EncryptUpdate doesn't cause an overflow
if (plaintext.size() > INT_MAX) {
Util::LogError("Plaintext is too large");
return nullptr;
}
// Allocate space for the ciphertext
unique_ptr<ByteBuffer> ciphertext(
new ByteBuffer(plaintext.size() + padding_size));
// Initialize the cipher context
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init(&ctx);
int return_code = EVP_EncryptInit_ex(&ctx, EVP_aes_256_cbc(), NULL,
encryptionKey.data().ImmutableUInt8(),
iv.ImmutableUInt8());
if (return_code != 1) {
Util::LogErrorAndAbort("Could not initialize encryption");
return nullptr;
}
// Encrypt data
int full_block_bytes_written = 0;
int last_block_bytes_written = 0;
return_code = EVP_EncryptUpdate(
&ctx, ciphertext->MutableUInt8(), &full_block_bytes_written,
plaintext.ImmutableUInt8(), static_cast<int>(plaintext.size()));
if (return_code != 1) {
Util::LogErrorAndAbort("Error during encryption");
return nullptr;
}
return_code = EVP_EncryptFinal_ex(
&ctx, ciphertext->MutableUInt8() + full_block_bytes_written,
&last_block_bytes_written);
if (return_code != 1) {
Util::LogErrorAndAbort("Error during encryption");
return nullptr;
}
// Check that the correct number of bytes were written
if (full_block_bytes_written > INT_MAX - last_block_bytes_written ||
static_cast<size_t>(full_block_bytes_written +
last_block_bytes_written) != ciphertext->size()) {
Util::LogErrorAndAbort("Unexpected amount of bytes written by AES Encrypt");
return nullptr;
}
// Cleanup
EVP_CIPHER_CTX_cleanup(&ctx);
return ciphertext;
}
unique_ptr<string> CryptoOps::GenerateIv(EncType encType) {
unique_ptr<string> iv = nullptr;
switch (encType) {
case NONE:
iv = nullptr;
break;
case AES_256_CBC:
unsigned char iv_bytes[AES_BLOCK_SIZE];
if (RAND_bytes(iv_bytes, AES_BLOCK_SIZE) != 1) {
Util::LogError("OpenSSL could not generate random bytes");
return nullptr;
}
iv = Util::MakeUniquePtrString(iv_bytes, AES_BLOCK_SIZE);
break;
default:
// Should never happen
Util::LogErrorAndAbort("Invalid encryption type");
return nullptr;
}
return iv;
}
unique_ptr<ByteBuffer> CryptoOps::EcdsaP256Sha256Sign(
const PrivateKey &private_key, const ByteBuffer &data) {
if (private_key.algorithm() != KeyAlgorithm::ECDSA_KEY ||
private_key.data().size() == 0 || data.size() == 0) {
return nullptr;
}
// Make sure we don't overflow EVP_SignUpdate
if (data.size() > UINT_MAX) {
Util::LogError("Data too big to sign");
return nullptr;
}
EvpKeyPtr extracted_evpkey = CreateEvpPrivateKey(private_key);
if (extracted_evpkey == nullptr) {
return nullptr;
}
// Create an empty signature buffer with enough space. OpenSSL will tell us
// how much it actually wrote.
int maximum_size = EVP_PKEY_size(extracted_evpkey.get());
if (maximum_size <= 0) {
Util::LogErrorAndAbort("unexpected max size when computing evp key size");
return nullptr;
}
ByteBuffer signature_buffer(maximum_size);
unsigned int signature_length = 0;
// Create a new message digest context and initialize it.
unique_ptr<EVP_MD_CTX, void (*)(EVP_MD_CTX *)> mdctx(EVP_MD_CTX_create(),
EVP_MD_CTX_destroy);
EVP_MD_CTX_init(mdctx.get());
int return_code = EVP_SignInit_ex(mdctx.get(), EVP_sha256(), NULL);
if (return_code != 1) {
Util::LogErrorAndAbort("could not initialize digest context");
return nullptr;
}
// On public signature types, we add salt.
return_code = EVP_SignUpdate(mdctx.get(), kSalt, sizeof(kSalt));
if (return_code != 1) {
Util::LogErrorAndAbort("could not add salt");
return nullptr;
}
// Set the message to sign.
return_code = EVP_SignUpdate(mdctx.get(), data.ImmutableUChar(),
data.size());
if (return_code != 1) {
Util::LogErrorAndAbort("could not set data to sign");
return nullptr;
}
// Compute the signature.
return_code = EVP_SignFinal(mdctx.get(), signature_buffer.MutableUChar(),
&signature_length, extracted_evpkey.get());
if (return_code != 1) {
Util::LogErrorAndAbort("could not execute signature");
return nullptr;
}
// Verify we got a signature.
if (signature_length <= 0) {
Util::LogErrorAndAbort("signature length is too small");
return nullptr;
}
// Verify the signature fits in the buffer.
if (signature_length > static_cast<unsigned int>(maximum_size)) {
Util::LogErrorAndAbort("signature buffer is too small");
return nullptr;
}
return unique_ptr<ByteBuffer>(
new ByteBuffer(signature_buffer.ImmutableUChar(), signature_length));
}
bool CryptoOps::EcdsaP256Sha256Verify(const PublicKey& public_key,
const ByteBuffer& signature,
const ByteBuffer& data) {
if (public_key.algorithm() != KeyAlgorithm::ECDSA_KEY ||
public_key.data().size() == 0 || data.size() == 0) {
return false;
}
// Make sure we don't overflow EVP_VerifyUpdate
if (data.size() > UINT_MAX) {
Util::LogError("Data too big to verify");
return nullptr;
}
EvpKeyPtr extracted_public_evpkey = CreateEvpPublicKey(public_key);
if (extracted_public_evpkey == nullptr) {
return false;
}
// Create a verify digest context and initialize it
unique_ptr<EVP_MD_CTX, void (*)(EVP_MD_CTX *)> verifyctx(EVP_MD_CTX_create(),
EVP_MD_CTX_destroy);
EVP_MD_CTX_init(verifyctx.get());
int return_code = EVP_VerifyInit_ex(verifyctx.get(), EVP_sha256(), NULL);
if (return_code != 1) {
Util::LogErrorAndAbort("could not initialize verify digest context");
return false;
}
// On public signatures, we added salt to the signature.
return_code = EVP_VerifyUpdate(verifyctx.get(), kSalt, sizeof(kSalt));
if (return_code != 1) {
Util::LogErrorAndAbort("could not add salt to verify");
return false;
}
// Set the message to verify
return_code =
EVP_VerifyUpdate(verifyctx.get(), data.ImmutableUChar(), data.size());
if (return_code != 1) {
Util::LogErrorAndAbort("could not set data to verify");
return false;
}
// Verify the signature
return_code =
EVP_VerifyFinal(verifyctx.get(), signature.ImmutableUChar(),
static_cast<unsigned int>(signature.size()),
extracted_public_evpkey.get());
if (return_code != 1) {
Util::LogError("could not verify signature");
return false;
}
return true;
}
unique_ptr<ByteBuffer> CryptoOps::EcdhKeyAgreement(
const PrivateKey& private_key,
const PublicKey& peer_key) {
// Shared return code.
int return_code;
// Create the private key from the data.
EvpKeyPtr evp_private_key = CreateEvpPrivateKey(private_key);
if (evp_private_key == nullptr) {
return nullptr;
}
// Create the public key from the data.
EvpKeyPtr evp_peer_key = CreateEvpPublicKey(peer_key);
if (evp_peer_key == nullptr) {
return nullptr;
}
// Create the context for the shared secret derivation.
unique_ptr<EVP_PKEY_CTX, void (*)(EVP_PKEY_CTX *)> ctx(
EVP_PKEY_CTX_new(evp_private_key.get(), NULL), EVP_PKEY_CTX_free);
if (ctx == nullptr) {
Util::LogErrorAndAbort("could not create ECDH context");
return nullptr;
}
// Initialize.
return_code = EVP_PKEY_derive_init(ctx.get());
if (return_code != 1) {
Util::LogErrorAndAbort("could not initialize context");
return nullptr;
}
// Provide the peer public key.
return_code = EVP_PKEY_derive_set_peer(ctx.get(), evp_peer_key.get());
if (return_code != 1) {
Util::LogErrorAndAbort("could not set peer");
return nullptr;
}
// Determine buffer length for shared secret.
size_t secret_len;
return_code = EVP_PKEY_derive(ctx.get(), NULL, &secret_len);
if (return_code != 1) {
Util::LogErrorAndAbort("could not determine secret length");
return nullptr;
}
// Create the buffer.
auto secret = unique_ptr<ByteBuffer>(new ByteBuffer(secret_len));
// Derive the shared secret.
return_code = EVP_PKEY_derive(ctx.get(), secret->MutableUChar(),
&secret_len);
if (return_code != 1 || secret_len != secret->size()) {
Util::LogErrorAndAbort("could not derive secret");
return nullptr;
}
return secret;
}
unique_ptr<ByteBuffer> CryptoOps::Rsa2048Sha256Sign(
const PrivateKey &private_key, const ByteBuffer &data) {
if (private_key.algorithm() != KeyAlgorithm::RSA_KEY ||
private_key.data().size() == 0 || data.size() == 0) {
return nullptr;
}
// Overflow check to make sure we don't overflow EVP_SignUpdate
if (data.size() > UINT_MAX) {
Util::LogError("Data too large to sign");
return nullptr;
}
int return_code;
const uint8_t *key_data = private_key.data().ImmutableUInt8();
// Import bytes into an RSA key structure
unique_ptr<RSA, void (*)(RSA *)> rsa_key(
d2i_RSAPrivateKey(NULL, &key_data, private_key.data().size()), RSA_free);
if (rsa_key.get() == NULL) {
Util::LogError("could not re-create rsa key");
return nullptr;
}
// Create an EVP key structure
EvpKeyPtr rsa_pkey(EVP_PKEY_new());
if (rsa_pkey.get() == NULL) {
Util::LogErrorAndAbort("Could not create new EVP_PKEY structure");
return nullptr;
}
// Place the RSA key into the EVP structure
return_code = EVP_PKEY_set1_RSA(rsa_pkey.get(), rsa_key.get());
if (return_code != 1) {
Util::LogErrorAndAbort("Could not set rsa key into pkey");
return nullptr;
}
// Set up a new signature context
unique_ptr<EVP_MD_CTX, void (*)(EVP_MD_CTX *)> ctx(EVP_MD_CTX_create(),
EVP_MD_CTX_destroy);
if (ctx.get() == NULL) {
Util::LogErrorAndAbort("Could not create signature context");
return nullptr;
}
// Initialize it
return_code = EVP_SignInit(ctx.get(), EVP_sha256());
if (return_code != 1) {
Util::LogErrorAndAbort("Could not initialize context");
return nullptr;
}
// On public signature types, we add salt.
return_code = EVP_SignUpdate(ctx.get(), kSalt, sizeof(kSalt));
if (return_code != 1) {
Util::LogErrorAndAbort("could not add salt");
return nullptr;
}
// Set the message to sign
return_code = EVP_SignUpdate(ctx.get(), data.ImmutableUChar(),
data.size());
if (return_code != 1) {
Util::LogErrorAndAbort("could not set data to sign");
return nullptr;
}
// Make space for the signature
size_t signature_size = EVP_PKEY_size(rsa_pkey.get());
if (signature_size == 0) {
Util::LogErrorAndAbort("Invalid signature size");
return nullptr;
}
ByteBuffer signature(signature_size);
// Perform the signature
unsigned int bytes_written = 0;
return_code = EVP_SignFinal(ctx.get(), signature.MutableUInt8(),
&bytes_written, rsa_pkey.get());
if (return_code != 1) {
Util::LogErrorAndAbort("Error while signing");
return nullptr;
}
return unique_ptr<ByteBuffer>(
new ByteBuffer(signature.ImmutableUInt8(), bytes_written));
}
bool CryptoOps::Rsa2048Sha256Verify(const PublicKey& public_key,
const ByteBuffer& signature,
const ByteBuffer& data) {
if (public_key.algorithm() != KeyAlgorithm::RSA_KEY ||
public_key.data().size() == 0 || data.size() == 0) {
return nullptr;
}
// Make sure we don't overflow EVP_VerifyUpdate
if (data.size() > UINT_MAX) {
Util::LogError("Data too big to verify");
return nullptr;
}
int return_code;
const uint8_t *key_data = public_key.data().ImmutableUInt8();
// Import bytes into an RSA key structure
unique_ptr<RSA, void (*)(RSA *)> rsa_public_key(
d2i_RSA_PUBKEY(NULL, &key_data, public_key.data().size()), RSA_free);
if (rsa_public_key.get() == NULL) {
Util::LogErrorAndAbort(
"could not import bytes into RSA key object for public key");
return false;
}
// Create an EVP key structure
EvpKeyPtr rsa_public_pkey(EVP_PKEY_new());
if (rsa_public_pkey.get() == NULL) {
Util::LogErrorAndAbort(
"could not create EVP structure for storing public RSA key");
return false;
}
// Place the RSA key into the EVP structure
return_code = EVP_PKEY_set1_RSA(rsa_public_pkey.get(), rsa_public_key.get());
if (return_code != 1) {
Util::LogErrorAndAbort("could not set RSA public key into EVP structure");
return false;
}
// Create verification context
unique_ptr<EVP_MD_CTX, void (*)(EVP_MD_CTX *)> verifyctx(EVP_MD_CTX_create(),
EVP_MD_CTX_destroy);
EVP_MD_CTX_init(verifyctx.get());
// Initialize it
return_code = EVP_VerifyInit_ex(verifyctx.get(), EVP_sha256(), NULL);
if (return_code != 1) {
Util::LogErrorAndAbort("could not initialize verify digest context");
return false;
}
// On public signatures, we added salt to the signature.
return_code = EVP_VerifyUpdate(verifyctx.get(), kSalt, sizeof(kSalt));
if (return_code != 1) {
Util::LogErrorAndAbort("could not add salt to verify");
return false;
}
// Set the message to verify
return_code =
EVP_VerifyUpdate(verifyctx.get(), data.ImmutableUChar(), data.size());
if (return_code != 1) {
Util::LogErrorAndAbort("could not set data to verify");
return false;
}
// Verify the signature
return_code = EVP_VerifyFinal(verifyctx.get(), signature.ImmutableUChar(),
static_cast<unsigned int>(signature.size()),
rsa_public_pkey.get());
if (return_code != 1) {
Util::LogError("could not verify signature");
return false;
}
return true;
}
unique_ptr<CryptoOps::PublicKey> CryptoOps::ImportEcP256Key(const string& x,
const string& y) {
int return_code; // Used to check all of OpenSSL's return codes
if (!IsValidEcP256CoordinateEncoding(x) ||
!IsValidEcP256CoordinateEncoding(y)) {
Util::LogError("error, invalid key format");
return nullptr;
}
// Generate new ec key structure for the P256 curve
unique_ptr<EC_KEY, void (*)(EC_KEY *)> eckey(
EC_KEY_new_by_curve_name(NID_X9_62_prime256v1),
EC_KEY_free);
if (eckey.get() == NULL) {
Util::LogErrorAndAbort("error, couldn't create new key structure");
return nullptr;
}
auto x_data = reinterpret_cast<const unsigned char *>(x.data());
auto y_data = reinterpret_cast<const unsigned char *>(y.data());
unique_ptr<BIGNUM, void (*)(BIGNUM *)> x_value(
BN_bin2bn(x_data, static_cast<unsigned int>(x.length()), NULL),
BN_free);
unique_ptr<BIGNUM, void (*)(BIGNUM *)> y_value(
BN_bin2bn(y_data, static_cast<unsigned int>(y.length()), NULL),
BN_free);
return_code = EC_KEY_set_public_key_affine_coordinates(eckey.get(),
x_value.get(),
y_value.get());
if (return_code != 1) {
Util::LogError("error, couldn't set public key");
return nullptr;
}
unique_ptr<ByteBuffer> public_key_bytes = PublicKeyToBytes(eckey.get());
if (public_key_bytes == nullptr) {
Util::LogErrorAndAbort("error, couldn't unwrap public key");
return nullptr;
}
return unique_ptr<PublicKey>(
new PublicKey(*public_key_bytes, KeyAlgorithm::ECDSA_KEY));
}
bool CryptoOps::ExportEcP256Key(const CryptoOps::PublicKey& key,
string *x,
string *y) {
int return_code; // Used to check all of OpenSSL's return codes
if (key.algorithm() != ECDSA_KEY) {
Util::LogError("error, invalid key type");
return false;
}
const unsigned char *pub_key_buffer = key.data().ImmutableUChar();
unique_ptr<EC_KEY, void (*)(EC_KEY *)> eckey(
d2i_EC_PUBKEY(NULL, &pub_key_buffer, key.data().size()),
EC_KEY_free);
if (eckey.get() == NULL) {
Util::LogError("error, couldn't import new key structure");
return false;
}
const EC_POINT *ecpoint = EC_KEY_get0_public_key(eckey.get());
const EC_GROUP *ecgroup = EC_KEY_get0_group(eckey.get());
if (ecpoint == NULL || ecgroup == NULL) {
Util::LogErrorAndAbort("error, couldn't get the public key details");
return false;
}
unique_ptr<BIGNUM, void (*)(BIGNUM *)> x_value(
BN_new(),
BN_free);
unique_ptr<BIGNUM, void (*)(BIGNUM *)> y_value(
BN_new(),
BN_free);
return_code = EC_POINT_get_affine_coordinates_GFp(ecgroup,
ecpoint,
x_value.get(),
y_value.get(),
NULL);
if (return_code != 1) {
Util::LogErrorAndAbort("error, could not capture coordinates of key");
return false;
}
ByteBuffer x_bytes(BN_num_bytes(x_value.get()));
ByteBuffer y_bytes(BN_num_bytes(y_value.get()));
BN_bn2bin(x_value.get(), x_bytes.MutableUChar());
BN_bn2bin(y_value.get(), y_bytes.MutableUChar());
// Make sure the byte format is in two's complement by adding a byte of 0's.
x_bytes.Prepend(string(1, '\x00'));
y_bytes.Prepend(string(1, '\x00'));
if (x != NULL) {
x->assign(x_bytes.String());
}
if (y != NULL) {
y->assign(y_bytes.String());
}
return true;
}
unique_ptr<CryptoOps::PublicKey> CryptoOps::ImportRsa2048Key(const string& n,
int32_t e) {
if (!IsValidRsa2048ModulusEncoding(n)) {
Util::LogError("error, invalid key encoding");
return nullptr;
}
unique_ptr<RSA, void (*)(RSA *)> rsa(RSA_new(), RSA_free);
if (rsa.get() == NULL) {
Util::LogErrorAndAbort("error, couldn't create new key structure");
return nullptr;
}
auto n_data = reinterpret_cast<const unsigned char *>(n.data());
string e_str = Int32BytesToString(e);
auto e_data = reinterpret_cast<const unsigned char *>(e_str.data());
unique_ptr<BIGNUM, void (*)(BIGNUM *)> n_value(
BN_bin2bn(n_data, static_cast<unsigned int>(n.length()), NULL),
BN_free);
unique_ptr<BIGNUM, void (*)(BIGNUM *)> e_value(
BN_bin2bn(e_data, static_cast<unsigned int>(e_str.length()), NULL),
BN_free);
rsa->n = n_value.release();
rsa->e = e_value.release();
unsigned char *public_key_buffer = NULL;
int public_key_len = i2d_RSA_PUBKEY(rsa.get(), &public_key_buffer);
if (public_key_len <= 0) {
Util::LogErrorAndAbort("invalid public key length");
return nullptr;
}
if (public_key_buffer == NULL) {
Util::LogErrorAndAbort("couldn't allocate public key buffer");
return nullptr;
}
ByteBuffer public_key_bytes(public_key_buffer, public_key_len);
free(public_key_buffer);
return unique_ptr<PublicKey>(
new PublicKey(public_key_bytes, KeyAlgorithm::RSA_KEY));
}
bool CryptoOps::ExportRsa2048Key(const CryptoOps::PublicKey& key,
string *n,
int32_t *e) {
if (key.algorithm() != RSA_KEY) {
Util::LogError("error, invalid key type");
return false;
}
const unsigned char *pub_key_buffer = key.data().ImmutableUChar();
unique_ptr<RSA, void (*)(RSA *)> rsa(
d2i_RSA_PUBKEY(NULL, &pub_key_buffer, key.data().size()),
RSA_free);
if (rsa.get() == NULL) {
Util::LogError("error, couldn't import new key structure");
return false;
}
if (rsa->n == NULL || rsa->e == NULL) {
Util::LogErrorAndAbort("error, couldn't get the public key details");
return false;
}
if (static_cast<size_t>(BN_num_bytes(rsa->e)) > sizeof(*e)) {
Util::LogErrorAndAbort("error, invalid exponent size");
return false;
}
ByteBuffer n_bytes(BN_num_bytes(rsa->n));
ByteBuffer e_bytes(BN_num_bytes(rsa->e));
BN_bn2bin(rsa->n, n_bytes.MutableUChar());
BN_bn2bin(rsa->e, e_bytes.MutableUChar());
// Make sure the modulus bytes are in two's complement by ensuring the
// leading bit is not 1.
n_bytes.Prepend(string(1, '\x00'));
int32_t e_int32 = 0;
if (!StringToInt32Bytes(e_bytes.String(), &e_int32)) {
Util::LogErrorAndAbort("error, invalid exponent bytes");
return false;
}
if (n != NULL) {
n->assign(n_bytes.String());
}
if (e != NULL) {
*e = e_int32;
}
return true;
}
unique_ptr<CryptoOps::SecretKey> CryptoOps::GenerateAes256SecretKey() {
size_t key_size_in_bytes = EVP_CIPHER_key_length(EVP_aes_256_cbc());
ByteBuffer key(key_size_in_bytes);
if (RAND_bytes(key.MutableUInt8(),
static_cast<unsigned int>(key_size_in_bytes)) != 1) {
Util::LogError(
"Could not get enough cryptographically secure bytes for key");
return nullptr;
}
return unique_ptr<SecretKey>(
new SecretKey(key.String(), KeyAlgorithm::AES_256_KEY));
}
unique_ptr<CryptoOps::KeyPair> CryptoOps::GenerateEcP256KeyPair() {
int return_code; // Used to check all of OpenSSL's return codes
// Create a new EVP key structure for the EC key
EvpKeyPtr ecpkey(EVP_PKEY_new());
if (ecpkey.get() == NULL) {
Util::LogErrorAndAbort("Could not create EVP PKEY structure");
return nullptr;
}
// Generate new ec key structure for the P256 curve
EC_KEY *eckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
if (eckey == NULL) {
Util::LogErrorAndAbort("error, couldn't create new key structure");
return nullptr;
}
// Generate a new key and stick it in the structure
return_code = EC_KEY_generate_key(eckey);
if (return_code != 1) {
Util::LogErrorAndAbort("Couldn't generate new key");
return nullptr;
}
// Assign the EC Key to a EVP PKEY structure
return_code = EVP_PKEY_assign_EC_KEY(ecpkey.get(), eckey);
if (return_code != 1) {
Util::LogErrorAndAbort("Could not assign EC key to EVP structure");
return nullptr;
}
// Convert public key to bytes
unique_ptr<ByteBuffer> public_key_bytes = PublicKeyToBytes(eckey);
if (public_key_bytes == nullptr) {
return nullptr;
}
// Convert private key to bytes
unique_ptr<PKCS8_PRIV_KEY_INFO, void (*)(PKCS8_PRIV_KEY_INFO *)> pkcs8(
EVP_PKEY2PKCS8(ecpkey.get()), PKCS8_PRIV_KEY_INFO_free);
if (pkcs8.get() == NULL) {
Util::LogError("could not convert evp key into pkcs8 key");
return nullptr;
}
int priv_key_len = i2d_PKCS8_PRIV_KEY_INFO(pkcs8.get(), NULL);
if (priv_key_len <= 0) {
Util::LogErrorAndAbort("unexpected private key length returned");
return nullptr;
}
unsigned char *priv_key_buffer = NULL;
return_code = i2d_PKCS8_PRIV_KEY_INFO(pkcs8.get(), &priv_key_buffer);
if (return_code != priv_key_len) {
Util::LogErrorAndAbort("wrote an unexpected number of private key bytes");
return nullptr;
}
ByteBuffer private_key_bytes(priv_key_buffer, priv_key_len);
free(priv_key_buffer);
return unique_ptr<KeyPair>(
new KeyPair(unique_ptr<PublicKey>(new PublicKey(
*public_key_bytes, KeyAlgorithm::ECDSA_KEY)),
unique_ptr<PrivateKey>(new PrivateKey(
private_key_bytes, KeyAlgorithm::ECDSA_KEY))));
}
unique_ptr<CryptoOps::KeyPair> CryptoOps::GenerateRsa2048KeyPair() {
// Create the exponent
unique_ptr<BIGNUM, void (*)(BIGNUM *)> exp(BN_new(), BN_clear_free);
if (exp.get() == NULL) {
Util::LogErrorAndAbort("Could not create new bignum");
return nullptr;
}
int return_code = BN_set_word(exp.get(), RSA_F4);
if (return_code != 1) {
Util::LogErrorAndAbort("Error setting exponent");
return nullptr;
}
// Generate a key
unique_ptr<RSA, void (*)(RSA *)> rsa(RSA_new(), RSA_free);
if (rsa.get() == NULL) {
Util::LogErrorAndAbort("Error creating RSA structure");
return nullptr;
}
return_code = RSA_generate_key_ex(rsa.get(), 2048, exp.get(), NULL);
if (return_code != 1) {
Util::LogErrorAndAbort("Error generating RSA key");
return nullptr;
}
// Store private key
unsigned char *private_key_buffer = NULL;
int private_key_len = i2d_RSAPrivateKey(rsa.get(), &private_key_buffer);
if (private_key_buffer == NULL) {
Util::LogErrorAndAbort("couldn't allocate private key buffer");
return nullptr;
}
if (private_key_len <= 0) {
Util::LogErrorAndAbort(
"invalid private key length encountered during key generation");
return nullptr;
}
ByteBuffer private_key(private_key_buffer, private_key_len);
free(private_key_buffer);
// Store public key
unsigned char *public_key_buffer = NULL;
int public_key_len = i2d_RSA_PUBKEY(rsa.get(), &public_key_buffer);
if (public_key_buffer == NULL) {
Util::LogErrorAndAbort("couldn't allocate public key buffer");
return nullptr;
}
if (public_key_len <= 0) {
Util::LogErrorAndAbort("invalid public key length");
return nullptr;
}
ByteBuffer public_key(public_key_buffer, public_key_len);
free(public_key_buffer);
return unique_ptr<KeyPair>(new KeyPair(
unique_ptr<PublicKey>(new PublicKey(public_key, KeyAlgorithm::RSA_KEY)),
unique_ptr<PrivateKey>(
new PrivateKey(private_key, KeyAlgorithm::RSA_KEY))));
}
} // namespace securemessage