Sleds/libeye/eyering.cpp

3271 lines
107 KiB
C++
Raw Normal View History

2025-03-13 21:28:38 +00:00
// Copyright (c) 2013-2015 IONU Security, Inc. All rights reserved.
//
// Keyring and Key classes for secure storage and management of keys
#include <cstring>
#include <sys/stat.h>
#include <algorithm>
#include <openssl/ec.h>
#include <openssl/err.h>
#include <openssl/hmac.h>
#include <openssl/pem.h>
#include <openssl/rand.h>
#include <openssl/rsa.h>
#include "eyedrbg.h"
#include "eyelog.h"
#include "eyering.h"
#include "eyeutils.h"
using namespace std;
using namespace sequencelogic;
//#define TIME_DEBUG 1 // set to enable performance output data
//#define JAVASCRIPT 1 // set to enable key information to match with libeye.js
namespace sequencelogic {
//static const char* PEM_PUBLIC_BEGIN = "-----BEGIN PUBLIC KEY-----";
//tatic const char* PEM_PUBLIC_END = "-----END PUBLIC KEY-----";
static const char* PEM_PRIVATE_BEGIN = "-----BEGIN RSA PRIVATE KEY-----";
static const char* PEM_PRIVATE_END = "-----END RSA PRIVATE KEY-----";
//static const char* PEM_ENC_PRIVATE_BEGIN = "-----BEGIN ENCRYPTED PRIVATE KEY-----";
//static const char* PEM_ENC_PRIVATE_END = "-----END ENCRYPTED PRIVATE KEY-----";
static const char* const SL_KEYRING_VERSION = "0.1";
static const char* const SL_KEYRING_MAGIC = "IOnUMaGiCKeYriNg";
bool sortkeys_alpa_ascending (Key* i, Key* j) { return (i->GetName().compare(j->GetName()) < 0); }
}
Key::Key (const std::string& name, const std::string& desc, size_t len, const unsigned char* key, KEY_TYPE type)
{
// Check for garbage input
if (name.size() < 1) {
IONUDEBUG ("Key::Key() - Empty or missing key name");
_name = "";
}
else
_name = name;
_desc = desc;
time_t now;
time (&now);
_len = len;
_type = type;
_key = NULL;
_iv = NULL;
sequencelogic::RandomBytes (1, &_obf);
if (len > 0 && key) {
_key = new unsigned char[len + 1];
if (_key) {
_key[len] = '\0'; // NULL terminate in case client uses key as a string
memcpy (_key, key, len);
}
else {
_len = 0;
}
}
else {
IONUDEBUG ("Key::Key (%s) - NULL or 0 length key", name.c_str());
}
}
Key::Key (const std::string& name, const std::string& desc, const std::string& key, KEY_TYPE type)
{
// Check for garbage input
if (name.size() < 1) {
IONUDEBUG ("Key::Key() - Empty or missing key name");
_name = "";
}
else
_name = name;
_desc = desc;
_len = 0;
_key = NULL;
_iv = NULL;
sequencelogic::RandomBytes (1, &_obf);
_type = type;
_len = key.size();
if (_len > 0) {
_key = new unsigned char[_len+1];
if (_key) {
memcpy (_key, key.data(), _len);
// For PK keys may need to convert "\n" and "\/" (JSON) to '\n' and '/' (PEM)
if (type == RSA || type == RSA_PUBLIC || type == EC || type == EC_PUBLIC) {
unsigned char* s = _key;
unsigned char* d = _key;
size_t keypos = 0;
while (keypos < key.size()) {
if (*s == '\\' && *(s+1) == 'n') {
*d++ = '\n';
_len -= 1;
s++;
s++;
keypos += 2;
}
else if (*s == '\\' && *(s+1) == '/') {
*d++ = '/';
_len -= 1;
s++;
s++;
keypos += 2;
}
else if (*s == '\"') { // Skip quotes
_len -= 1;
s++;
keypos++;
}
else {
*d++ = *s++;
keypos++;
}
}
*d = '\0'; // Terminate for C style string
}
}
else {
_len = 0;
}
}
else {
IONUDEBUG ("Key::Key (%s) - NULL or 0 length key", name.c_str());
}
}
// Constructors that load the key from a .pem, .der or other file format
Key::Key (const std::string& name, const std::string& desc, const std::string& filename, const std::string& password)
{
// Check for garbage input
if (name.size() < 1) {
IONUDEBUG ("Key::Key() - Empty or missing key name");
_name = "";
}
else
_name = name;
_desc = desc;
_type = GENERIC;
_len = 0;
_key = nullptr;
_iv = nullptr;
sequencelogic::RandomBytes (1, &_obf);
if (! Import (filename, password)) {
; // TODO error log
}
}
Key::Key (const std::string& filename, const std::string& password)
{
// Check for garbage input
if (sequencelogic::CanReadFile (filename))
_name = filename;
else
_name = "";
_desc = "Imported";
_type = GENERIC;
_len = 0;
_key = nullptr;
_iv = nullptr;
sequencelogic::RandomBytes (1, &_obf);
Import (filename, password);
}
Key::Key ()
{
_name = "";
_desc = "";
_type = GENERIC;
_len = 0;
_key = nullptr;
_iv = nullptr;
sequencelogic::RandomBytes (1, &_obf);
}
Key::~Key()
{
if (_key) {
if (_len > 0)
sequencelogic::MemSet (_key, 0, _len); // Obliterate key from memory
delete[] _key;
_key = nullptr;
}
if (_iv) {
delete[] _iv;
_iv = nullptr;
}
}
// This only works on unlocked keys
bool Key::IsValid() const
{
if (_name.size() < 1 || !_key)
return false;
bool rc = true;
switch (_type) {
case AES :
if (_len != SL_AES_KEY_LEN) {
IONUDEBUG ("Key::IsValid(%s) - Invalid AES key", _name.c_str());
rc = false;
}
break;
case AESHMAC :
if (_len != 2*SL_AES_KEY_LEN) {
IONUDEBUG ("Key::IsValid(%s) - Invalid AES HMAC key", _name.c_str());
rc = false;
}
break;
case EC :
case EC_PUBLIC :
case RSA :
case RSA_PUBLIC :
if (_len > SL_RSA_KEY_LEN / 2) {
BIO* bp = BIO_new_mem_buf (_key, static_cast<int>(_len));
EVP_PKEY* key = NULL;
if (_type == RSA || _type == EC)
key = PEM_read_bio_PrivateKey (bp, NULL, 0, (void*)NULL);
else
key = PEM_read_bio_PUBKEY (bp, NULL, 0, (void*)NULL);
BIO_free(bp);
if (!key)
rc = false;
else
EVP_PKEY_free(key);
}
else
rc = false;
break;
case STRING :
case GENERIC :
if (_len == 0)
rc = false;
break;
default :
rc = false;
break;
}
return rc;
}
// Clone a key, only updating the fields that are different
void Key::Duplicate (const Key* dupkey)
{
if (dupkey) {
if (_name.compare (dupkey->GetName()) != 0) {
_name = dupkey->GetName();
}
if (_desc.compare(dupkey->GetDescription()) != 0) {
_desc = dupkey->GetDescription();
}
if (_len != dupkey->GetLength()) {
if (_key && _len > 0)
delete[] _key;
_len = dupkey->GetLength();
_key = new unsigned char [_len];
}
memcpy (_key, dupkey->GetKey(), _len);
_type = dupkey->GetType();
}
}
void Key::SetKey (const unsigned char* key, size_t bytes)
{
if (bytes == _len)
sequencelogic::MemCpy (_key, key, _len);
else {
if (_key) delete[] _key;
_len = bytes;
_key = new unsigned char[_len];
sequencelogic::MemCpy (_key, key, _len);
}
}
void Key::SetIv (const unsigned char* iv, size_t bytes)
{
if (_iv) delete[] _iv;
_iv = new unsigned char[bytes];
sequencelogic::MemCpy (_iv, iv, bytes);
}
const std::string Key::GetStringKey() const
{
std::string strkey = "";
switch (_type) {
case EC :
case EC_PUBLIC :
case RSA :
case RSA_PUBLIC :
case STRING :
case GENERIC :
strkey.assign ((char*) _key, _len);
break;
case AES :
case AESHMAC :
default:
// base64?
break;
}
return strkey;
}
// Determine from file extension whether to use PEM or DER file format
Key::KEY_FORMAT Key::GetKeyFormat (const std::string& filename) const
{
KEY_FORMAT format = UNKNOWN_FORMAT;
if (filename.size() > 4)
{
std::string ext = "";
size_t dot = filename.find_last_of(".");
if (dot != std::string::npos && ++dot < filename.size())
ext = filename.substr (dot, filename.size() - dot);
if (ext.size() == 3) {
if (ext == "der" || ext == "DER")
format = DER;
else if (ext == "pem" || ext == "PEM")
format = PEM;
}
}
return format;
}
// Export to PEM, DER or PKCS#8 format as determined by extension and presence of password
bool Key::Export (const std::string& filename, const std::string& password) const
{
// Check for garbage input
if (!sequencelogic::IsValidFilename (filename))
return false;
bool rc = true;
KEY_FORMAT format = GetKeyFormat (filename);
if (password.size() < 2) {
ofstream file (filename, ios::out | ios::binary | ios::trunc);
if (! file.is_open())
return false;
if (format == PEM) {
file.write ((char*)_key, _len);
}
else if (format == DER) { // ASN1 format
char* ptr = strchr ((char*)_key, '\n');
char* tptr = (char*) _key + _len - 2;
if (ptr++) {
while (*--tptr != '\n')
;
*tptr = '\0';
size_t bytes = (_len * 3) / 4;
unsigned char* output = new unsigned char[bytes];
bytes = sequencelogic::Base64Decode (ptr, output);
*tptr = '\n';
file.write ((char*)output, bytes);
delete[] output;
}
}
if (file.bad())
rc = false;
file.close();
} else {
BIO* bp = BIO_new_mem_buf (_key, static_cast<int>(_len));
EVP_PKEY* key = PEM_read_bio_PrivateKey (bp, NULL, NULL, NULL);
if (!key || !PEM_write_bio_PKCS8PrivateKey (bp, key, EVP_des_ede3_cbc(), NULL, 0, 0, (void*)password.c_str()))
rc = false;
// if (format == DER) i2d_PKCS8PrivateKey_bio()
// if (format == PKCS12) { PKCS12_create(), i2d_PKCS12_bio }
BIO_free(bp);
EVP_PKEY_free(key);
}
return rc;
}
// Import from PEM, DER or PKCS#8 format as determined by extension
bool Key::Import (const std::string& filename, const std::string& password)
{
bool rc = true;
// Check for garbage input
if (!sequencelogic::CanReadFile (filename))
return false;
KEY_FORMAT format = GetKeyFormat (filename);
ifstream file (filename, ios::in | ios::binary | ios::ate);
if (file.is_open())
{
// Get the file size, reset to the beginning and read entire file
size_t bytes = (size_t)file.tellg();
file.seekg (0, ios::beg);
char* buffer = new char[bytes];
if (!buffer) {
file.close();
return rc;
}
file.read (buffer, bytes);
if (file.bad()) {
IONUDEBUG ("Key::Import (%s) failed with bad read", filename.c_str());
rc = false;
}
file.close();
if (rc)
rc = Import (buffer, bytes, password, format);
delete[] buffer;
}
return rc;
}
// Import from PEM, DER or PKCS#8 format
bool Key::Import (const char* buffer, size_t bytes, const std::string& password, KEY_FORMAT format)
{
if (_key) delete[] _key;
_len = 0;
// Check for garbage input
if (!buffer || bytes == 0)
return false;
_type = GENERIC;
if (format == DER) {
// Convert to PEM for internal usage
// Get an approximate length for PEM encoded version (base64, \n every 64, header, footer, margin
_len = (bytes * 4) / 3 + (bytes / 48) * 2 + static_cast<int>(strlen (PEM_PRIVATE_BEGIN)) * 2 + 64;
_key = new unsigned char[_len];
if (!_key)
return false;
strcpy ((char*)_key, PEM_PRIVATE_BEGIN);
strcat ((char*)_key, "\n");
char buff64[256];
unsigned char* bptr = (unsigned char*) buffer;
while (bytes > 0) {
sequencelogic::Base64Encode (bptr, (bytes >= 48 ? 48 : bytes), buff64);
strcat ((char*)_key, buff64);
strcat ((char*)_key, "\n");
bptr += 48;
if (bytes > 48)
bytes -= 48;
else
bytes = 0;
}
strcat ((char*)_key, PEM_PRIVATE_END);
strcat ((char*)_key, "\n");
_len = static_cast<int>(strlen ((char*)_key)); // Set the exact length now
//printf ("%s\n", _key);
}
else {
_key = new unsigned char[bytes];
if (!_key)
return false;
memcpy (_key, buffer, bytes);
_len = bytes;
}
BIO* bp = BIO_new_mem_buf (_key, static_cast<int>(_len));
EC_KEY* eckey = NULL;
EVP_PKEY* evpkey = NULL;
std::string pem ((char*)_key, _len);
if (pem.find ("PRIVATE KEY") != std::string::npos) {
eckey = PEM_read_bio_ECPrivateKey (bp, NULL, NULL, (void*)password.c_str());
if (eckey) {
_type = EC;
EC_KEY_free (eckey);
}
else {
BIO_free(bp);
bp = BIO_new_mem_buf (_key, static_cast<int>(_len));
evpkey = PEM_read_bio_PrivateKey (bp, &evpkey, NULL, (void*)password.c_str());
if (evpkey) {
_type = RSA;
EVP_PKEY_free (evpkey);
}
else {
IONUDEBUG ("Key::Import - failed with %s", ERR_error_string(ERR_get_error(), NULL));
}
}
}
else if (pem.find ("PUBLIC") != std::string::npos) {
eckey = PEM_read_bio_EC_PUBKEY (bp, NULL, NULL, (void*)password.c_str());
if (eckey) {
_type = EC_PUBLIC;
EC_KEY_free (eckey);
}
else {
BIO_free(bp);
bp = BIO_new_mem_buf (_key, static_cast<int>(_len));
evpkey = PEM_read_bio_PUBKEY (bp, &evpkey, 0, (void*)NULL);
if (evpkey) {
_type = RSA_PUBLIC;
EVP_PKEY_free (evpkey);
}
else {
IONUDEBUG ("Key::Import - failed with %s", ERR_error_string(ERR_get_error(), NULL));
}
}
}
BIO_free(bp);
if (_type == GENERIC) {
IONUDEBUG ("Key::Import(%s) - failed", _name.c_str());
Dump();
return false;
}
return true;
}
// Import the public key from an X509 certificate
bool Keyring::ImportPubKeyFromCert (const std::string& filename)
{
// Check for garbage input
if (!sequencelogic::CanReadFile (filename))
return false;
bool rc = false;
//KEY_FORMAT format = GetKeyFormat (filename);
BIO* in = BIO_new_file(filename.c_str(), "r");
X509* cert = NULL;
//if (format == DER) cert = d2i_X509_bio(in, NULL); for DER/ASN1 format
cert = PEM_read_bio_X509(in, &cert, NULL, NULL);
if (cert) {
EVP_PKEY* evpkey = NULL;
evpkey = X509_get_pubkey (cert);
if (evpkey) {
struct rsa_st* rsa = EVP_PKEY_get1_RSA (evpkey);
BIO* out = BIO_new (BIO_s_mem());
PEM_write_bio_RSA_PUBKEY (out, rsa);
char* data = NULL;
//BIO_flush (out);
long bytes = BIO_get_mem_data (out, &data);
Key* k = new Key ("cert", filename, bytes, (const unsigned char*)data, Key::RSA_PUBLIC);
if (k) { rc = AddKey (k);
}
RSA_free (rsa);
BIO_free (out);
EVP_PKEY_free (evpkey);
}
}
X509_free (cert);
BIO_free (in);
return rc;
}
// Return the PEM format public key for RSA or EC private key, NULL if wrong type
char* Key::GetPubKey() const
{
char* pk =NULL;
if (GetType() == Key::RSA) {
BIO* bp = BIO_new_mem_buf ((void*)GetKey(), static_cast<int>(GetLength()));
EVP_PKEY* evpkey = NULL;
evpkey = PEM_read_bio_PrivateKey (bp, &evpkey, NULL, NULL);
BIO_free(bp);
if (evpkey) {
struct rsa_st* rsa = EVP_PKEY_get1_RSA (evpkey);
BIO* out = BIO_new (BIO_s_mem());
PEM_write_bio_RSA_PUBKEY (out, rsa);
char* data = NULL;
long bytes = BIO_get_mem_data (out, &data);
pk = new char[bytes + 1];
memcpy (pk, data, bytes);
pk[bytes] = '\0';
BIO_free (out);
EVP_PKEY_free (evpkey);
RSA_free (rsa);
}
}
else if (GetType() == Key::EC) {
BIO* bp = BIO_new_mem_buf ((void*)GetKey(), static_cast<int>(GetLength()));
EVP_PKEY* evpkey = NULL;
evpkey = PEM_read_bio_PrivateKey (bp, &evpkey, NULL, NULL);
BIO_free(bp);
if (evpkey) {
struct ec_key_st* ec = EVP_PKEY_get1_EC_KEY (evpkey);
BIO* out = BIO_new (BIO_s_mem());
PEM_write_bio_EC_PUBKEY (out, ec);
char* data = NULL;
long bytes = BIO_get_mem_data (out, &data);
pk = new char[bytes + 1];
memcpy (pk, data, bytes);
pk[bytes] = '\0';
BIO_free (out);
EVP_PKEY_free (evpkey);
EC_KEY_free (ec);
}
}
return pk;
}
std::string Key::PublicKeyFingerPrint () const
{
std::string fingerprint = "";
unsigned char buffer[1024];
#ifdef SSH_FINGER
// Not yet quite right, but the basics of how to do SSH fingerprint
BIO* bp = BIO_new_mem_buf (_key, static_cast<int>(_len));
EVP_PKEY* key = NULL;
if (_type == RSA || _type == EC) {
key = PEM_read_bio_PrivateKey (bp, NULL, 0, (void*)NULL);
if (key) {
struct rsa_st* rsa = EVP_PKEY_get1_RSA (key);
BIO* out = BIO_new (BIO_s_mem());
PEM_write_bio_RSA_PUBKEY (out, rsa);
EVP_PKEY_free(key);
key = PEM_read_bio_PUBKEY (out, NULL, 0, (void*)NULL);
BIO_free (out);
RSA_free (rsa);
}
}
else if (_type == RSA_PUBLIC || _type == EC_PUBLIC) {
key = PEM_read_bio_PUBKEY (bp, NULL, 0, (void*)NULL);
}
else { // Wrong type
return fingerprint;
}
BIO_free(bp);
size_t bytes = 7;
memcpy (buffer, "ssh-rsa", 7);
struct rsa_st* rsa = EVP_PKEY_get1_RSA (key);
BN_bn2bin (rsa->e, &buffer[bytes]);
bytes += BN_num_bytes (rsa->e);
BN_bn2bin (rsa->n, &buffer[bytes]);
bytes += BN_num_bytes (rsa->n);
RSA_free (rsa);
fingerprint = sequencelogic::DigestMessage (MESSAGE_DIGEST_MD5, buffer, bytes);
#else
std::string b64;
if (_type == RSA || _type == EC) {
char* pk = GetPubKey();
b64 = pk;
delete[] pk;
}
else if (_type == RSA_PUBLIC || _type == EC_PUBLIC) {
b64.assign ((char*)_key, _len);
}
else { // Wrong type
return fingerprint;
}
// Strip all of the newlines and header/footer
b64.erase(remove(b64.begin(), b64.end(), '\n'), b64.end());
sequencelogic::ReplaceStringInSitu (b64, "-----BEGIN PUBLIC KEY-----", "");
sequencelogic::ReplaceStringInSitu (b64, "-----END PUBLIC KEY-----", "");
//cout << b64 << endl;
// Decode base64 to DER formated public key, compute SHA1 digest
size_t bytes = sequencelogic::Base64Decode (b64.c_str(), buffer);
fingerprint = sequencelogic::DigestMessage (MESSAGE_DIGEST_SHA1, buffer, bytes);
#endif
// Return fingerprint as the standard colon separated string
std::string fp = "";
for (size_t i = 0; i < fingerprint.size(); i += 2)
fp += (i > 0 ? ":" : "") + fingerprint.substr (i, 2);
return fp;
}
// Load key parameters from a json string
bool Key::LoadValueJSON (const std::string& name, const std::string& json)
{
_name = name;
EyeJSONObject params (json.c_str());
if (params.GetNumMembers() < 3)
return false;
for (size_t i = 0; i < params.GetNumMembers(); ++i) {
EyeJSONScalar* item = static_cast<EyeJSONScalar*>(params.GetMember(i));
if (sequencelogic::StrCmp (item->GetKey() , "desc") == 0)
_desc = item->GetValue();
else if (sequencelogic::StrCmp (item->GetKey() , "type") == 0) {
if (sequencelogic::StrCmp (item->GetValue(), "RSA") == 0) _type = Key::RSA;
else if (sequencelogic::StrCmp (item->GetValue(), "RSA_PUBLIC") == 0) _type = Key::RSA_PUBLIC;
else if (sequencelogic::StrCmp (item->GetValue(), "STRING") == 0) _type = Key::STRING;
else if (sequencelogic::StrCmp (item->GetValue(), "EC") == 0) _type = Key::EC;
else if (sequencelogic::StrCmp (item->GetValue(), "EC_PUBLIC") == 0) _type = Key::EC_PUBLIC;
else if (sequencelogic::StrCmp (item->GetValue(), "AES") == 0) _type = Key::AES;
else if (sequencelogic::StrCmp (item->GetValue(), "AESHMAC") == 0) _type = Key::AESHMAC;
else _type = Key::GENERIC;
}
else if (sequencelogic::StrCmp (item->GetKey() , "key") == 0) {
_len = 3 * strlen (item->GetValue()) / 4;
if (_key) delete[] _key;
_key = new unsigned char [_len];
_len = sequencelogic::Base64Decode (item->GetValue(), _key); // Decode and set exact length
}
}
return true;
}
// Return key parameters in a json string
std::string Key::GetValueJSON() const
{
std::string json;
// Base64 encode key, expecting it to be binary
size_t b64len = 4 * _len / 3 + 4;
char* b64 = (char*)sequencelogic::New (b64len);
if (b64 == nullptr)
return json;
sequencelogic::Base64Encode (_key, _len, b64);
EyeJSONObject params (NULL, JSON_OBJECT, NULL);
if (_desc.size() > 0)
params.AddMember (new EyeJSONScalar ("desc", _desc.c_str(), JSON_STRING));
params.AddMember (new EyeJSONScalar ("key", b64, JSON_STRING));
params.AddMember (new EyeJSONScalar ("type", GetKeyTypeStr().c_str(), JSON_STRING));
delete[] b64;
params.GetJSONString (json);
return json;
}
// Return key as a JSON object
EyeJSONObject* Key::GetJSONObject() const
{
// Base64 encode key, expecting it to be binary
size_t b64len = 4 * _len / 3 + 4;
char* b64 = (char*)sequencelogic::New (b64len);
if (b64 == nullptr)
return nullptr;
bool isAscii = false;
if (_type == STRING || _type == RSA || _type == EC || _type == RSA_PUBLIC || _type == EC_PUBLIC) {
size_t i = 0;
for (i = 0; i < _len; ++i) {
if (! isascii (_key[i])) {
break;
}
}
if (i == _len) {
isAscii = true;
}
}
if (isAscii) {
memcpy (b64, _key, _len);
b64[_len] = '\0';
}
else {
sequencelogic::Base64Encode (_key, _len, b64);
}
EyeJSONObject* key = new EyeJSONObject (NULL, JSON_OBJECT, NULL);
key->AddMember (new EyeJSONScalar ("name", _name.c_str(), JSON_STRING));
if (_desc.size() > 0)
key->AddMember (new EyeJSONScalar ("desc", _desc.c_str(), JSON_STRING));
key->AddMember (new EyeJSONScalar ("key", b64, JSON_STRING));
key->AddMember (new EyeJSONScalar ("type", GetKeyTypeStr().c_str(), JSON_STRING));
delete[] b64;
return key;
}
// Decrypt the key
void Key::Decrypt (EVP_CIPHER_CTX* ctx, const unsigned char* key, const unsigned char* iv)
{
unsigned char* plaintext = new unsigned char [_len];
size_t bytes = Decryptor (ctx, _len, _key, plaintext, key, iv);
if (bytes == _len) {
memcpy (_key, plaintext, _len);
}
else {
if (_len > 0) {
delete[] _key;
_key = NULL;
}
_len = bytes;
if (bytes > 0)
{
_key = new unsigned char[_len];
memcpy (_key, plaintext, _len);
}
}
delete[] plaintext;
}
// Encrypt the key
void Key::Encrypt (EVP_CIPHER_CTX* ctx, const unsigned char* key, const unsigned char* iv)
{
unsigned char* ciphertext = new unsigned char [_len + 2 * SL_AES_BLOCK_LEN];
size_t bytes = Encryptor (ctx, _len, _key, ciphertext, key, iv);
if (bytes == _len) {
memcpy (_key, ciphertext, _len);
}
else {
if (_len > 0) {
sequencelogic::MemSet (_key, 0, _len); // Obliterate the plaintext from memory
delete[] _key;
}
_len = bytes;
_key = new unsigned char[_len];
memcpy (_key, ciphertext, _len);
}
delete[] ciphertext;
}
unsigned char* Key::SymmetricEncryptBuffer (const unsigned char* plaintext, size_t inlen, size_t* outlen,
const unsigned char* iv) const
{
// Check for garbage input
if (!plaintext || !outlen || inlen == 0)
return nullptr;
if ((_type != AES) || (_len != SL_AES_KEY_LEN && _len != SL_AES_KEY_LEN/2)) {
IONUDEBUG ("Key::SymmetricEncryptBuffer() is only supported with 128 or 256 bit AES keys");
return nullptr;
}
*outlen = 0;
unsigned char* ciphertext = sequencelogic::New (inlen + 2 * SL_AES_BLOCK_LEN); //Padding
if (ciphertext) {
int bytes = 0;
int fbytes = 0;
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init (&ctx);
if (_len == SL_AES_KEY_LEN && !EVP_EncryptInit_ex (&ctx, EVP_aes_256_cbc(), NULL, _key, iv)) {
IONUDEBUG ("Key::SymmetricEncryptBuffer() - EVP_EncryptInit_ex failed");
EVP_CIPHER_CTX_cleanup (&ctx);
delete[] ciphertext;
return nullptr;
}
else if (!EVP_EncryptInit_ex (&ctx, EVP_aes_128_cbc(), NULL, _key, iv)) {
IONUDEBUG ("Key::SymmetricEncryptBuffer() - EVP_EncryptInit_ex failed");
EVP_CIPHER_CTX_cleanup (&ctx);
delete[] ciphertext;
return nullptr;
}
if (!EVP_EncryptUpdate (&ctx, ciphertext, &bytes, plaintext, static_cast<int>(inlen))) {
IONUDEBUG ("Key::SymmetricEncryptBuffer() - EVP_EncryptUpdate failed for %d bytes", (int)inlen);
EVP_CIPHER_CTX_cleanup (&ctx);
delete[] ciphertext;
return nullptr;
}
if (!EVP_EncryptFinal_ex (&ctx, ciphertext + bytes, &fbytes)) {
IONUDEBUG ("Key::SymmetricEncryptBuffer() - EVP_EncryptFinal_ex failed");
EVP_CIPHER_CTX_cleanup (&ctx);
delete[] ciphertext;
return nullptr;
}
*outlen = bytes + fbytes;
EVP_CIPHER_CTX_cleanup (&ctx);
}
return ciphertext;
}
unsigned char* Key::SymmetricDecryptBuffer (const unsigned char* ciphertext, size_t inlen, size_t* outlen,
const unsigned char* iv) const
{
// Check for garbage input
if (!ciphertext || !outlen || inlen == 0)
return nullptr;
if ((_type != AES) || (_len != SL_AES_KEY_LEN && _len != SL_AES_KEY_LEN/2)) {
IONUDEBUG ("Key::SymmetricDecryptBuffer() is only supported with 128 or 256 bit AES keys");
return nullptr;
}
*outlen = 0;
unsigned char* plaintext = sequencelogic::New (inlen);
if (plaintext) {
int bytes = 0;
int fbytes = 0;
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init (&ctx);
if (_len == SL_AES_KEY_LEN && !EVP_DecryptInit_ex (&ctx, EVP_aes_256_cbc(), NULL, _key, iv)) {
IONUDEBUG ("Key::SymmetricDecryptBuffer() - EVP_DecryptInit_ex failed");
EVP_CIPHER_CTX_cleanup (&ctx);
delete[] plaintext;
return nullptr;
}
else if (!EVP_DecryptInit_ex (&ctx, EVP_aes_128_cbc(), NULL, _key, iv)) {
IONUDEBUG ("Key::SymmetricDecryptBuffer() - EVP_DecryptInit_ex failed");
EVP_CIPHER_CTX_cleanup (&ctx);
delete[] plaintext;
return nullptr;
}
if (!EVP_DecryptUpdate (&ctx, plaintext, &bytes, ciphertext, static_cast<int>(inlen))) {
IONUDEBUG ("Key::SymmetricDecryptBuffer() - EVP_DecryptUpdate failed for %d bytes", (int)inlen);
EVP_CIPHER_CTX_cleanup (&ctx);
delete[] plaintext;
return nullptr;
}
if (!EVP_DecryptFinal_ex (&ctx, plaintext + bytes, &fbytes)) {
IONUDEBUG ("Key::SymmetricDecryptBuffer() - EVP_DecryptFinal_ex failed");
EVP_CIPHER_CTX_cleanup (&ctx);
delete[] plaintext;
return nullptr;
}
*outlen = bytes + fbytes;
EVP_CIPHER_CTX_cleanup (&ctx);
}
return plaintext;
}
bool Key::SymmetricEncryptFile (const std::string& plainfile, const std::string& encryptedfile,
const unsigned char* iv) const
{
if (_type != AES || (_len != SL_AES_KEY_LEN && _len != SL_AES_KEY_LEN/2)) {
IONUDEBUG ("Key::SymmetricEncryptFile() is only supported with 128 or 256 bit AES keys");
return false;
}
ifstream ifile (plainfile, ios::in | ios::binary | ios::ate);
if (!ifile.is_open()) {
IONUDEBUG ("Key::SymmetricEncryptFile(%s) - can't read input file", plainfile.c_str());
return false;
}
ofstream ofile (encryptedfile, ios::out | ios::binary | ios::trunc);
if (!ofile.is_open()) {
IONUDEBUG ("Key::SymmetricEncryptFile(%s) - invalid output file specified: %s", plainfile.c_str(), encryptedfile.c_str());
ifile.close();
return false;
}
// Get the input file size and reset to the beginning
std::streamoff bytes = ifile.tellg();
ifile.seekg (0, ios::beg);
// Initialize cipher
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init (&ctx);
EVP_EncryptInit_ex (&ctx, EVP_aes_256_cbc(), NULL, _key, iv);
bool rc = true;
int ebytes, fbytes, encryptlen;
// Allocate read and encryption buffers
char* readbuff = (char*)sequencelogic::New (SL_EYE_STREAM_CHUNK);
unsigned char* ebuff = sequencelogic::New (SL_EYE_STREAM_CHUNK + SL_AES_BLOCK_LEN);
if (readbuff == nullptr || ebuff == nullptr) {
IONUDEBUG ("Key::SymmetricEncryptFile(%s) - buffer allocation failed", plainfile.c_str());
rc = false;
bytes = 0;
}
while (bytes > 0) {
if (bytes >= SL_EYE_STREAM_CHUNK) {
ifile.read (readbuff, SL_EYE_STREAM_CHUNK);
if (ifile.gcount() != SL_EYE_STREAM_CHUNK) {
IONUDEBUG ("Key::SymmetricEncryptFile(%s) - read failed for %d bytes", plainfile.c_str(), SL_EYE_STREAM_CHUNK);
rc = false;
break;
}
bytes -= SL_EYE_STREAM_CHUNK;
encryptlen = SL_EYE_STREAM_CHUNK;
}
else {
ifile.read (readbuff, (std::streamsize)bytes);
if (ifile.gcount() != (std::streamsize)bytes) {
IONUDEBUG ("Key::SymmetricEncryptFile(%s) - read failed for %d bytes", plainfile.c_str(), bytes);
rc = false;
break;
}
encryptlen = (int)bytes;
bytes = 0;
}
if(!EVP_EncryptUpdate (&ctx, ebuff, &ebytes, (const unsigned char*)readbuff, encryptlen)) {
IONUDEBUG ("Key::SymmetricEncryptFile(%s) - EVP_EncryptUpdate failed", plainfile.c_str());
rc = false;
break;
}
if (bytes == 0) {
if(!EVP_EncryptFinal_ex (&ctx, ebuff + ebytes, &fbytes)) {
IONUDEBUG ("Key::SymmetricEncryptFile(%s) - EVP_EncryptFinal failed", plainfile.c_str());
rc = false;
break;
}
}
else {
fbytes = 0;
}
ofile.write ((char*)ebuff, (std::streamsize)(ebytes + fbytes));
if (ofile.bad()) {
IONUDEBUG ("Key::SymmetricEncryptFile(%s) - write failed", plainfile.c_str());
rc = false;
break;
}
}
// Clean up and close files
if (readbuff) delete[] readbuff;
if (ebuff) delete[] ebuff;
ifile.close();
ofile.close();
EVP_CIPHER_CTX_cleanup (&ctx);
// If there was a failure, remove the incomplete/invalid encrypted file
if (!rc)
sequencelogic::RemoveFile (encryptedfile);
return rc;
}
bool Key::SymmetricDecryptFile (const std::string& encryptedfile, const std::string& plainfile,
const unsigned char* iv) const
{
if (_type != AES || (_len != SL_AES_KEY_LEN && _len != SL_AES_KEY_LEN/2)) {
IONUDEBUG ("Key::SymmetricDecryptFile() is only supported with 128 or 256 bit AES keys");
return false;
}
ifstream ifile (encryptedfile, ios::in | ios::binary | ios::ate);
if (!ifile.is_open()) {
IONUDEBUG ("Key::SymmetricDecryptFile(%s) - can't read input file", encryptedfile.c_str());
return false;
}
ofstream ofile (plainfile, ios::out | ios::binary | ios::trunc);
if (!ofile.is_open()) {
IONUDEBUG ("Key::SymmetricDecryptFile(%s) - can't create output", plainfile.c_str());
ifile.close();
return false;
}
// Get the input file size and reset to the beginning
std::streamoff bytes = ifile.tellg();
ifile.seekg (0, ios::beg);
// Initialize cipher
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init (&ctx);
EVP_DecryptInit_ex (&ctx, EVP_aes_256_cbc(), NULL, _key, iv);
bool rc = true;
int ebytes, fbytes, encryptlen;
// Allocate read and encryption buffers
char* readbuff = (char*)sequencelogic::New (SL_EYE_STREAM_CHUNK);
unsigned char* ebuff = sequencelogic::New (SL_EYE_STREAM_CHUNK + SL_AES_BLOCK_LEN);
if (readbuff == nullptr || ebuff == nullptr) {
IONUDEBUG ("Key::SymmetricDecryptFile(%s) - buffer allocation failed", encryptedfile.c_str());
rc = false;
bytes = 0;
}
while (bytes > 0) {
if (bytes >= SL_EYE_STREAM_CHUNK) {
ifile.read (readbuff, SL_EYE_STREAM_CHUNK);
if (ifile.gcount() != SL_EYE_STREAM_CHUNK) {
IONUDEBUG ("Key::SymmetricDecryptFile(%s) - read failed for %d bytes", encryptedfile.c_str(), SL_EYE_STREAM_CHUNK);
rc = false;
break;
}
bytes -= SL_EYE_STREAM_CHUNK;
encryptlen = SL_EYE_STREAM_CHUNK;
}
else {
ifile.read (readbuff, (std::streamsize)bytes);
if (ifile.gcount() != (std::streamsize)bytes) {
IONUDEBUG ("Key::SymmetricDecryptFile(%s) - read failed for %d bytes", encryptedfile.c_str(), bytes);
rc = false;
break;
}
encryptlen = (int)bytes;
bytes = 0;
}
if(!EVP_DecryptUpdate (&ctx, ebuff, &ebytes, (const unsigned char*)readbuff, encryptlen)) {
IONUDEBUG ("Key::SymmetricDecryptFile(%s) - EVP_DecryptUpdate failed", encryptedfile.c_str());
rc = false;
break;
}
if (bytes == 0) {
if(!EVP_DecryptFinal_ex (&ctx, ebuff + ebytes, &fbytes)) {
IONUDEBUG ("Key::SymmetricDecryptFile(%s) - EVP_DecryptFinal failed", encryptedfile.c_str());
rc = false;
break;
}
}
else {
fbytes = 0;
}
ofile.write ((char*)ebuff, (std::streamsize)(ebytes + fbytes));
if (ofile.bad()) {
IONUDEBUG ("Key::SymmetricDecryptFile(%s) - write failed", plainfile.c_str());
rc = false;
break;
}
}
// Clean up and close files
if (readbuff) delete[] readbuff;
if (ebuff) delete[] ebuff;
ifile.close();
ofile.close();
EVP_CIPHER_CTX_cleanup (&ctx);
// If there was a failure, remove the incomplete/invalid encrypted file
if (!rc)
sequencelogic::RemoveFile (plainfile);
return rc;
}
unsigned char* Key::SymmetricEncryptAuthenticated (const unsigned char *plaintext, size_t inlen, size_t* outlen,
const unsigned char *iv,
const unsigned char *aad, size_t aadlen) const
{
// Check for garbage input
if (!plaintext || !outlen || inlen == 0)
return NULL;
if (_type != AES || _len != SL_AES_KEY_LEN) {
IONUDEBUG ("Key::SymmetricEncryptAuthenticated() is only supported with 256 bit AES keys");
return NULL;
}
*outlen = 0;
#ifndef ANDROID
unsigned char* ciphertext = sequencelogic::New (inlen + 2 * SL_AES_BLOCK_LEN); // Tag
if (ciphertext) {
unsigned char* tag = ciphertext;
ciphertext += 16;
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init (&ctx);
int len;
if (1 != EVP_EncryptInit_ex (&ctx, EVP_aes_256_gcm(), NULL, NULL, NULL)) {
EVP_CIPHER_CTX_cleanup (&ctx);
delete[] tag;
return nullptr;
}
if (1 != EVP_CIPHER_CTX_ctrl (&ctx, EVP_CTRL_GCM_SET_IVLEN, 16, NULL)) {
EVP_CIPHER_CTX_cleanup (&ctx);
delete[] tag;
return nullptr;
}
if (1 != EVP_EncryptInit_ex (&ctx, NULL, NULL, _key, iv)) {
EVP_CIPHER_CTX_cleanup (&ctx);
delete[] tag;
return nullptr;
}
// Provide any AAD data
if (aad && aadlen > 0 && !EVP_EncryptUpdate (&ctx, NULL, &len, aad, (int)aadlen)) {
EVP_CIPHER_CTX_cleanup (&ctx);
delete[] tag;
return nullptr;
}
if (1 != EVP_EncryptUpdate (&ctx, ciphertext, &len, plaintext, (int)inlen)) {
EVP_CIPHER_CTX_cleanup (&ctx);
delete[] tag;
return nullptr;
}
*outlen = len;
if (1 != EVP_EncryptFinal_ex (&ctx, ciphertext + len, &len)) {
EVP_CIPHER_CTX_cleanup (&ctx);
delete[] tag;
return nullptr;
}
*outlen += len;
// Get the authentication tag in the first 16 bytes
if (!EVP_CIPHER_CTX_ctrl (&ctx, EVP_CTRL_GCM_GET_TAG, 16, tag)) {
EVP_CIPHER_CTX_cleanup (&ctx);
delete[] tag;
*outlen = 0;
return nullptr;
}
EVP_CIPHER_CTX_cleanup (&ctx);
return tag;
}
#endif
return NULL;
}
unsigned char* Key::SymmetricDecryptAuthenticated (const unsigned char *ciphertext, size_t inlen, size_t* outlen,
const unsigned char *iv,
const unsigned char *aad, size_t aadlen) const
{
// Check for garbage input
if (!ciphertext || !outlen || inlen == 0)
return NULL;
if (_type != AES || _len != SL_AES_KEY_LEN) {
IONUDEBUG ("Key::SymmetricDecryptAuthenticated() is only supported with 256 bit AES keys");
return NULL;
}
*outlen = 0;
unsigned char* plaintext = NULL;
#ifndef ANDROID
plaintext = sequencelogic::New (inlen);
if (plaintext) {
unsigned char* tag = (unsigned char*)ciphertext;
ciphertext += 16;
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init (&ctx);
int len;
if (!EVP_DecryptInit_ex (&ctx, EVP_aes_256_gcm(), NULL, NULL, NULL)) {
IONUDEBUG ("Key::SymmetricDecryptAuthenticated() - EVP_DecryptInit_ex gcm");
EVP_CIPHER_CTX_cleanup (&ctx);
delete[] plaintext;
return nullptr;
}
if (!EVP_CIPHER_CTX_ctrl (&ctx, EVP_CTRL_GCM_SET_IVLEN, 16, NULL)) {
IONUDEBUG ("Key::SymmetricDecryptAuthenticated() - EVP_CIPHER_CTX_ctrl");
EVP_CIPHER_CTX_cleanup (&ctx);
delete[] plaintext;
return nullptr;
}
if (!EVP_DecryptInit_ex (&ctx, NULL, NULL, _key, iv)) {
IONUDEBUG ("Key::SymmetricDecryptAuthenticated() - EVP_DecryptInit_ex key");
EVP_CIPHER_CTX_cleanup (&ctx);
delete[] plaintext;
return nullptr;
}
// Set the expected authentication tag
if (!EVP_CIPHER_CTX_ctrl (&ctx, EVP_CTRL_GCM_SET_TAG, 16, tag)) {
IONUDEBUG ("Key::SymmetricDecryptAuthenticated() - !EVP_CIPHER_CTX_ctrl failed set tag");
EVP_CIPHER_CTX_cleanup (&ctx);
delete[] plaintext;
return nullptr;
}
// Provide any AAD data
if (aad && aadlen > 0 && !EVP_DecryptUpdate (&ctx, NULL, &len, aad, (int)aadlen)) {
IONUDEBUG ("Key::SymmetricDecryptAuthenticated() - EVP_DecryptUpdate aad");
EVP_CIPHER_CTX_cleanup (&ctx);
delete[] plaintext;
return nullptr;
}
if (!EVP_DecryptUpdate (&ctx, plaintext, &len, ciphertext, (int)inlen)) {
IONUDEBUG ("Key::SymmetricDecryptAuthenticated() - EVP_DecryptUpdate");
EVP_CIPHER_CTX_cleanup (&ctx);
delete[] plaintext;
return nullptr;
}
*outlen = len;
// Finalise the decryption. A positive return value indicates success,
int ret = EVP_DecryptFinal_ex (&ctx, plaintext + len, &len);
EVP_CIPHER_CTX_cleanup (&ctx);
if (ret > 0) {
*outlen += len;
IONUDEBUG ("Key::SymmetricDecryptAuthenticated() - authenticated");
}
else {
IONUDEBUG ("Key::SymmetricDecryptAuthenticated() - EVP_DecryptFinal_ex failed %d", ret);
delete[] plaintext;
*outlen = 0;
plaintext = nullptr;
}
}
#endif
return plaintext;
}
unsigned char* Key::HMACEncrypt (const unsigned char* in, size_t inlen, size_t* outlen) const
{
return HMACCrypt (true, in, inlen, outlen);
}
unsigned char* Key::HMACDecrypt (const unsigned char* in, size_t inlen, size_t* outlen) const
{
return HMACCrypt (false, in, inlen, outlen);
}
unsigned char* Key::HMACCrypt (bool encrypt, const unsigned char* in, size_t inlen, size_t* outlen) const
{
// Check for garbage input
if (!in || !outlen || inlen == 0)
return NULL;
if (_type != AESHMAC || _len != 2*SL_AES_KEY_LEN) {
IONUDEBUG ("Key::HMACCrypt() AES encryption with HMAC is only supported with AESHMAC keys");
*outlen = 0;
return NULL;
}
unsigned char* ciphertext = new unsigned char [inlen + 2 * SL_AES_BLOCK_LEN];
unsigned char *out = NULL;
size_t bytes = 0;
*outlen = 0;
if (_key) {
unsigned int md_len = 0;
unsigned char md_value [SL_AES_KEY_LEN];
unsigned char* hmacKey = _key + SL_AES_KEY_LEN;
HMAC_CTX hmctx;
HMAC_CTX_init (&hmctx);
HMAC_Init (&hmctx, hmacKey, SL_AES_KEY_LEN, EVP_sha256());
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init (&ctx);
if (encrypt) {
if (_iv)
bytes = Encryptor (&ctx, inlen, in, ciphertext, _key, _iv);
else
bytes = Encryptor (&ctx, inlen, in, ciphertext, _key, hmacKey - 8);
// HMAC the encrypted data
HMAC_Update (&hmctx, ciphertext, bytes);
}
else {
HMAC_Update (&hmctx, in + SL_ECDH_HMAC_LEN, inlen - SL_ECDH_HMAC_LEN);
if (_iv)
bytes = Decryptor (&ctx, inlen - SL_ECDH_HMAC_LEN, in + SL_ECDH_HMAC_LEN, ciphertext, _key, _iv);
else
bytes = Decryptor (&ctx, inlen - SL_ECDH_HMAC_LEN, in + SL_ECDH_HMAC_LEN, ciphertext, _key, hmacKey - 8);
}
HMAC_Final (&hmctx, md_value, &md_len);
HMAC_CTX_cleanup (&hmctx);
HMAC_cleanup (&hmctx);
EVP_CIPHER_CTX_cleanup (&ctx);
if (encrypt) {
out = new unsigned char[bytes + md_len];
sequencelogic::MemCpy (out, md_value, md_len);
sequencelogic::MemCpy (out + md_len, ciphertext, bytes);
*outlen = bytes + md_len;
}
else {
out = new unsigned char[bytes];
sequencelogic::MemCpy (out, ciphertext, bytes);
if (sequencelogic::MemCmp (md_value, in, SL_ECDH_HMAC_LEN) != 0) {
IONUDEBUG ("Key::HMACDecrypt() - Authentication failure");
delete[] out;
delete[] ciphertext;
return NULL;
}
*outlen = bytes;
}
}
delete[] ciphertext;
return out;
}
unsigned char* Key::PrivateKeyDecrypt (const unsigned char* in, size_t inlen, size_t* outlen) const
{
// Check for garbage input
if (!in || !outlen || inlen == 0)
return nullptr;
if (_type != RSA) {
IONUDEBUG ("Key::PrivateKeyDecrypt() is only supported with RSA private keys");
*outlen = 0;
return nullptr;
}
return PublicKeyCrypt (false, in, inlen, outlen);
}
unsigned char* Key::PublicKeyEncrypt (const unsigned char* in, size_t inlen, size_t* outlen) const
{
// Check for garbage input
if (!in || !outlen)
return nullptr;
// Only supported by RSA
if (_type != RSA && _type != RSA_PUBLIC) {
IONUDEBUG ("Key::PublicKeyEncrypt() is only supported with RSA keys");
*outlen = 0;
return nullptr;
}
if (inlen > 0 && inlen <= 214) // (214 byte maximum with EME-OAEP padding)
return PublicKeyCrypt (true, in, inlen, outlen);
else {
IONUDEBUG ("Key::PublicKeyEncrypt() input must be [1-214] bytes, use RSAIES if more needed");
*outlen = 0;
return nullptr;
}
}
unsigned char* Key::PublicKeyCrypt (bool encrypt, const unsigned char* in, size_t inlen, size_t* outlen) const
{
unsigned char *out = NULL;
bool isok = true;
int rc;
BIO* bp = BIO_new_mem_buf (_key, static_cast<int>(_len));
EVP_PKEY* key = NULL;
if (_type == RSA || _type == EC) {
key = PEM_read_bio_PrivateKey (bp, NULL, 0, (void*)NULL);
if (key && encrypt) { // If encrypting, get the public key
struct rsa_st* rsa = EVP_PKEY_get1_RSA (key);
BIO* out = BIO_new (BIO_s_mem());
PEM_write_bio_RSA_PUBKEY (out, rsa);
EVP_PKEY_free(key);
key = PEM_read_bio_PUBKEY (out, NULL, 0, (void*)NULL);
BIO_free (out);
RSA_free (rsa);
}
}
else if (_type == RSA_PUBLIC || _type == EC_PUBLIC)
key = PEM_read_bio_PUBKEY (bp, NULL, 0, (void*)NULL);
BIO_free(bp);
EVP_PKEY_CTX* ctx = NULL;
if (key) {
ctx = EVP_PKEY_CTX_new(key, NULL);
if (ctx) {
if (encrypt)
rc = EVP_PKEY_encrypt_init(ctx);
else {
// RSA is vulnerable to timing attacks when attackers can measure
// the time of RSA decryption or signature operations, blinding adds
// randomness to the time to defeat this.
struct rsa_st* rsa = EVP_PKEY_get1_RSA (key);
size_t keysize = RSA_size (rsa);
if (keysize == inlen) { // Encryption key must be same size
RSA_blinding_on (rsa, NULL);
rc = EVP_PKEY_decrypt_init(ctx);
RSA_blinding_off (rsa);
}
else {
IONUDEBUG ("Key::PublicKeyCrypt - RSA encryption key was %d bits decryption is %d", (int)inlen * 8, keysize * 8);
rc = 0;
}
RSA_free (rsa);
}
if (rc <= 0)
isok = false;
if (isok && key->type == EVP_PKEY_RSA) {
// EME-OAEP as defined in PKCS #1 v2.0 with SHA-1, MGF1 and an empty encoding parameter
if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0) {
isok = false;
}
}
}
else {
isok = false;
}
}
else {
IONUDEBUG ("Key::PublicKeyCrypt - failed with %s", ERR_error_string(ERR_get_error(), NULL));
isok = false;
}
// Determine buffer length
if (isok) {
if (encrypt)
rc = EVP_PKEY_encrypt(ctx, NULL, outlen, in, inlen);
else
rc = EVP_PKEY_decrypt(ctx, NULL, outlen, in, inlen);
if (rc <= 0)
isok = false;
else
out = sequencelogic::New (*outlen);
if (!out)
isok = false;
}
// Decrypt
if (isok) {
if (encrypt)
rc = EVP_PKEY_encrypt(ctx, out, outlen, in, inlen);
else {
rc = EVP_PKEY_decrypt(ctx, out, outlen, in, inlen);
// If failed with OAEP padding, try PKCS1.5 padding
if (rc <= 0) {
if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) > 0)
rc = EVP_PKEY_decrypt(ctx, out, outlen, in, inlen);
}
}
if (rc <= 0) {
IONUDEBUG ("Key::PublicKeyCrypt - failed with %s", ERR_error_string(ERR_get_error(), NULL));
delete[] out;
out = nullptr;
}
}
// Free the key and context
if (key)
EVP_PKEY_free(key);
if (ctx)
EVP_PKEY_CTX_free(ctx);
if (out == nullptr)
*outlen = 0;
return out;
}
// These are unsecure and misnamed, provided for compatibility with the libionu version for CG token exchange
unsigned char* Key::RSAPrivateKeyEncrypt (const unsigned char* in, size_t inlen, size_t* outlen) const
{
// Check for garbage input
if (!in || !outlen || inlen == 0)
return nullptr;
if (_type != RSA) {
IONUDEBUG ("Key::PrivateKeyEncrypt() is only supported with RSA private keys");
*outlen = 0;
return nullptr;
}
return RSAKeyCrypt (true, in, inlen, outlen);
}
unsigned char* Key::RSAPublicKeyDecrypt (const unsigned char* in, size_t inlen, size_t* outlen) const
{
// Check for garbage input
if (!in || !outlen || inlen == 0)
return nullptr;
if (_type != RSA && _type != RSA_PUBLIC) {
IONUDEBUG ("Key::PublicKeyDecrypt() is only supported with RSA public keys");
*outlen = 0;
return nullptr;
}
return RSAKeyCrypt (false, in, inlen, outlen);
}
unsigned char* Key::RSAKeyCrypt (bool encrypt, const unsigned char* in, size_t inlen, size_t* outlen) const
{
unsigned char *out = NULL;
BIO* bp = BIO_new_mem_buf (_key, static_cast<int>(_len));
EVP_PKEY* key = NULL;
if (_type == RSA) {
key = PEM_read_bio_PrivateKey (bp, NULL, 0, (void*)NULL);
if (key && !encrypt) { // If decrypting, get the public key
struct rsa_st* rsa = EVP_PKEY_get1_RSA (key);
BIO* out = BIO_new (BIO_s_mem());
PEM_write_bio_RSA_PUBKEY (out, rsa);
EVP_PKEY_free(key);
key = PEM_read_bio_PUBKEY (out, NULL, 0, (void*)NULL);
BIO_free (out);
RSA_free (rsa);
}
}
else if (_type == RSA_PUBLIC)
key = PEM_read_bio_PUBKEY (bp, NULL, 0, (void*)NULL);
BIO_free(bp);
if (key) {
struct rsa_st* rsa = EVP_PKEY_get1_RSA (key);
size_t keysize = RSA_size (rsa);
if (encrypt) {
*outlen = keysize;
out = new unsigned char[keysize];
int elen = RSA_private_encrypt ((int)inlen, in, out, rsa, RSA_PKCS1_PADDING);
if (elen <=0 ) {
IONUDEBUG ("Key::RSAKeyCrypt - %s", ERR_error_string(ERR_get_error(), NULL));
delete[] out;
out = nullptr;
}
}
else {
if (keysize == inlen) { // Decryption key must be same size
out = new unsigned char[keysize];
int elen = RSA_public_decrypt ((int)inlen, in, out, rsa, RSA_PKCS1_PADDING);
if (elen <= 0) {
IONUDEBUG ("Key::RSAKeyCrypt - %s", ERR_error_string(ERR_get_error(), NULL));
delete[] out;
out = nullptr;
}
else
*outlen = elen;
}
else {
IONUDEBUG ("RSA encryption key was %d bits decryption is %d", (int)inlen * 8, keysize * 8);
}
}
RSA_free (rsa);
EVP_PKEY_free(key);
}
if (out == nullptr)
*outlen = 0;
return out;
}
// RSA is limited to 214 byte maximum with EME-OAEP padding. This is a hybrid scheme that will
// use plain RSA encryption if <= 214, or switches to AES key exchange, with minimum payload: The
// first 32 bytes are the AES key, followed by 182 bytes of data encrypted with RSA, followed by
// (len-182) bytes encrypted with AES.
unsigned char* Key::RSAIESEncrypt (const unsigned char* in, size_t inlen, size_t* outlen) const
{
// Only supported by RSA
if (_type != RSA && _type != RSA_PUBLIC) {
IONUDEBUG ("Key::RSAIESEncrypt() encryption is only supported with RSA keys");
*outlen = 0;
return NULL;
}
if (inlen <= 214) // (214 byte maximum with EME-OAEP padding)
return PublicKeyCrypt (true, in, inlen, outlen);
// More than 214 bytes, switch to integrated encryption scheme
unsigned char iv[SL_AES_BLOCK_LEN];
sequencelogic::MemSet (iv, 85, SL_AES_BLOCK_LEN);
size_t partial = 214-SL_AES_KEY_LEN;
size_t rsabytes = 0;
size_t aesbytes = 0;
Keyring ring ("temp", "orary");
ring.GenerateAESKey ("AES", "Ephemeral");
const Key* key = ring.GetKey ("AES");
unsigned char rsabuff[214];
sequencelogic::MemCpy (rsabuff, key->GetKey(), key->GetLength());
sequencelogic::MemCpy (rsabuff+SL_AES_KEY_LEN, in, partial);
unsigned char* rsapart = PublicKeyCrypt (true, rsabuff, 214, &rsabytes);
if (rsapart == nullptr) {
*outlen = 0;
IONUDEBUG ("Key::RSAIESEncrypt() - RSA encrypt failed, hybrid scheme");
return nullptr;
}
unsigned char* aespart = key->SymmetricEncryptBuffer (in + partial, inlen - partial, &aesbytes, iv);
unsigned char* buff = new unsigned char[rsabytes + aesbytes];
*outlen = rsabytes + aesbytes;
sequencelogic::MemCpy (buff, rsapart, rsabytes);
sequencelogic::MemCpy (buff + rsabytes, aespart, aesbytes);
delete[] rsapart;
delete[] aespart;
return buff;
}
unsigned char* Key::RSAIESDecrypt (const unsigned char* in, size_t inlen, size_t* outlen) const
{
// Only supported by RSA
if (_type != RSA) {
IONUDEBUG ("Key::RSAIESDecrypt() is only supported with RSA private keys");
*outlen = 0;
return nullptr;
}
if (inlen == 256) // (214 byte maximum with EME-OAEP padding)
return PublicKeyCrypt (false, in, inlen, outlen);
// More than 214 bytes, switch to integrated encryption scheme
size_t rsabytes = 0;
unsigned char* rsapart = PublicKeyCrypt (false, in, 256, &rsabytes);
if (rsapart == NULL) {
IONUDEBUG ("Key::RSAIESDecrypt() - RSA decrypt failed, hybrid scheme");
*outlen = 0;
return nullptr;
}
unsigned char iv[SL_AES_BLOCK_LEN];
sequencelogic::MemSet (iv, 85, SL_AES_BLOCK_LEN);
size_t partial = 214-SL_AES_KEY_LEN;
size_t aesbytes = 0;
Key key ("AES", "Emphemeral", SL_AES_KEY_LEN, rsapart, Key::AES);
unsigned char* aespart = key.SymmetricDecryptBuffer (in + 256, inlen - 256, &aesbytes, iv);
unsigned char* buff = new unsigned char[partial + aesbytes];
sequencelogic::MemCpy (buff, rsapart + SL_AES_KEY_LEN, partial);
sequencelogic::MemCpy (buff + partial, aespart, aesbytes);
*outlen = partial + aesbytes;
delete[] rsapart;
delete[] aespart;
return buff;
}
unsigned char* Key::ECIESEncrypt (const unsigned char* in, size_t inlen, size_t* outlen) const
{
// Check for garbage input
if (!in || !outlen || inlen == 0)
return nullptr;
// Only supported by EC
if (_type != EC && _type != EC_PUBLIC) {
IONUDEBUG ("Key::ECIESEncrypt() is only supported with EC keys");
*outlen = 0;
return nullptr;
}
Keyring* ring = new Keyring ("temp", "orary");
ring->GenerateECKey ("ECIES", "Ephemeral");
const Key* key = ring->GetKey ("ECIES");
char* ephemeral = key->GetPubKey ();
size_t ephlen = sequencelogic::StrLen (ephemeral, 1024) + 1;
unsigned char* ciphertext = key->ECDHKeyCrypt (this, true, in, inlen, outlen);
delete ring;
unsigned char* ecies = new unsigned char[ephlen + *outlen];
memcpy (ecies, ephemeral, ephlen);
memcpy (ecies + ephlen, ciphertext, *outlen);
*outlen += ephlen;
delete[] ephemeral;
delete[] ciphertext;
return ecies;
}
unsigned char* Key::ECIESDecrypt (const unsigned char* in, size_t inlen, size_t* outlen) const
{
// Check for garbage input
if (!in || !outlen || inlen == 0)
return nullptr;
// Only supported by EC
if (_type != EC) {
IONUDEBUG ("Key::ECIESDecrypt() is only supported with EC private keys");
*outlen = 0;
return nullptr;
}
size_t ephlen = sequencelogic::StrLen ((char*)in, 1024) + 1; // Skip ephemeral public key
if (ephlen == 0 || inlen <= ephlen) {
IONUDEBUG ("Key::ECIESDecrypt() - invalid EC public key");
*outlen = 0;
return NULL;
}
Key* key = new Key ("ECIES", "ephemeral", (char*)in, EC_PUBLIC);
unsigned char* plaintext = ECDHKeyCrypt (key, false, in + ephlen, inlen - ephlen, outlen);
delete key;
return plaintext;
}
unsigned char* Key::ECDHEncrypt (const Key* peer, const unsigned char* in, size_t inlen, size_t* outlen) const
{
// Check for garbage input
if (!peer || !in || !outlen || inlen == 0)
return nullptr;
// Only supported by EC
if (_type != EC && _type != EC_PUBLIC) {
IONUDEBUG ("Key::ECDHEncrypt() is only supported with EC keys");
*outlen = 0;
return nullptr;
}
return ECDHKeyCrypt (peer, true, in, inlen, outlen);
}
unsigned char* Key::ECDHDecrypt (const Key* peer, const unsigned char* in, size_t inlen, size_t* outlen) const
{
// Check for garbage input
if (!peer || !in || !outlen || inlen == 0)
return nullptr;
// Only supported by EC
if (_type != EC) {
IONUDEBUG ("Key::ECDHDecrypt() is only supported with EC private keys");
*outlen = 0;
return nullptr;
}
return ECDHKeyCrypt (peer, false, in, inlen, outlen);
}
// Encrypted value is [hmac-32-bytes]:[encrypted-bytes]
unsigned char* Key::ECDHKeyCrypt (const Key* peer, bool encrypt, const unsigned char* in, size_t inlen, size_t* outlen) const
{
size_t bytes = 0;
size_t secret_len = 0;
unsigned char* ciphertext = new unsigned char [inlen + 2 * SL_AES_BLOCK_LEN];
unsigned char *out = nullptr;
*outlen = 0;
unsigned char* secret = GenerateECDHSecret (peer, &secret_len);
if (secret) {
unsigned int md_len = 0;
unsigned char md_value [SL_AES_KEY_LEN];
unsigned char* hmacKey = secret + SL_AES_KEY_LEN;
HMAC_CTX hmctx;
HMAC_CTX_init (&hmctx);
HMAC_Init (&hmctx, hmacKey, SL_AES_KEY_LEN, EVP_sha256());
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init (&ctx);
if (encrypt) {
HMAC_Update (&hmctx, in, inlen);
bytes = Encryptor (&ctx, inlen, in, ciphertext, secret, hmacKey - 8);
}
else {
bytes = Decryptor (&ctx, inlen - SL_ECDH_HMAC_LEN, in + SL_ECDH_HMAC_LEN, ciphertext, secret, hmacKey - 8);
HMAC_Update (&hmctx, ciphertext, bytes);
}
sequencelogic::MemSet (secret, 0, secret_len);
delete[] secret;
HMAC_Final (&hmctx, md_value, &md_len);
HMAC_CTX_cleanup (&hmctx);
HMAC_cleanup (&hmctx);
EVP_CIPHER_CTX_cleanup (&ctx);
if (encrypt) {
out = new unsigned char[bytes + md_len];
sequencelogic::MemCpy (out, md_value, md_len);
sequencelogic::MemCpy (out + md_len, ciphertext, bytes);
*outlen = bytes + md_len;
}
else {
out = new unsigned char[bytes];
sequencelogic::MemCpy (out, ciphertext, bytes);
if (sequencelogic::MemCmp (md_value, in, SL_ECDH_HMAC_LEN) != 0) {
IONUDEBUG ("Key::ECDHKeyCrypt() - Authentication failure");
delete[] out;
delete[] ciphertext;
return NULL;
}
*outlen = bytes;
}
}
delete[] ciphertext;
return out;
}
unsigned char* Key::PrivateKeySign (const unsigned char* tbs, size_t tbslen, size_t* siglen) const
{
if (_type != RSA) {
IONUDEBUG ("Private key signing is only supported for RSA private keys");
*siglen = 0;
return nullptr;
}
// Check for garbage input
if (!tbs || !siglen || tbslen == 0)
return nullptr;
BIO* bp = BIO_new_mem_buf (_key, static_cast<int>(_len));
struct rsa_st* privateKey = NULL;
int rc = 1;
if (!PEM_read_bio_RSAPrivateKey(bp, &privateKey, NULL, NULL))
rc = 0;
BIO_free(bp);
if (rc != 1)
return nullptr;
unsigned char* sig = new unsigned char[RSA_size(privateKey)];
std::string mdhex = sequencelogic::DigestMessage (MESSAGE_DIGEST_SHA1, tbs, (int) tbslen);
unsigned char md[SL_SHA1_LEN];
sequencelogic::HexToBinary (mdhex.c_str(), md);
// RSA is vulnerable to timing attacks when attackers can measure
// the time of RSA decryption or signature operations, blinding adds
// randomness to the time to defeat this.
RSA_blinding_on (privateKey, NULL);
rc = RSA_sign (NID_sha1, md, SL_SHA1_LEN, sig, (unsigned int*)siglen, privateKey);
RSA_blinding_off (privateKey);
RSA_free (privateKey);
if (rc == 1)
return (sig);
else {
delete[] sig;
*siglen = 0;
return nullptr;
}
}
int Key::PublicKeyVerify (const unsigned char* sig, size_t siglen, const unsigned char* tbs, size_t tbslen) const
{
// Only supported by RSA
if (_type != RSA && _type != RSA_PUBLIC) {
IONUDEBUG ("Public key verification is only supported for RSA keys");
return 0;
}
// Check for garbage input
if (!sig || !tbs || siglen == 0 || tbslen == 0)
return 0;
BIO* bp = BIO_new_mem_buf (_key, static_cast<int>(_len));
struct rsa_st* publicKey = NULL;
size_t keysize = 0;
int rc = 1;
if (_type == RSA) {
// Get the public key
EVP_PKEY* key = PEM_read_bio_PrivateKey (bp, NULL, 0, (void*)NULL);
struct rsa_st* rsa = EVP_PKEY_get1_RSA (key);
keysize = RSA_size (rsa);
BIO* out = BIO_new (BIO_s_mem());
PEM_write_bio_RSA_PUBKEY (out, rsa);
EVP_PKEY_free (key);
publicKey = PEM_read_bio_RSA_PUBKEY (out, NULL, 0, (void*)NULL);
BIO_free (out);
RSA_free (rsa);
}
else {
if (!PEM_read_bio_RSA_PUBKEY(bp, &publicKey, NULL, NULL))
rc = 0;
else {
keysize = RSA_size (publicKey);
}
}
BIO_free(bp);
if (keysize == siglen) { // Verify key must be same size
std::string mdhex = sequencelogic::DigestMessage (MESSAGE_DIGEST_SHA1, tbs, (int) tbslen);
unsigned char md[SL_SHA1_LEN];
sequencelogic::HexToBinary (mdhex.c_str(), md);
if (rc == 1)
rc = RSA_verify (NID_sha1, md, SL_SHA1_LEN, sig, (int)siglen, publicKey);
}
else {
if (rc == 1) {
IONUDEBUG ("RSA signing key was %d bits verifying is %d", (int)siglen * 8, keysize * 8);
}
rc = 0;
}
RSA_free (publicKey);
return rc;
}
unsigned char* Key::GenerateECDHSecret (const Key* peer, size_t* secret_len) const
{
unsigned char *secret;
EVP_PKEY_CTX *ctx;
EVP_PKEY *pkey = NULL, *peerkey = NULL;
BIO* bp = BIO_new_mem_buf (_key, static_cast<int>(_len));
pkey = PEM_read_bio_PrivateKey (bp, NULL, 0, (void*)NULL);
BIO_free(bp);
bp = BIO_new_mem_buf ((void*)peer->GetKey(), static_cast<int>(peer->GetLength()));
if (peer->GetType() == EC)
peerkey = PEM_read_bio_PrivateKey (bp, NULL, 0, (void*)NULL);
else
peerkey = PEM_read_bio_PUBKEY (bp, NULL, 0, (void*)NULL);
BIO_free(bp);
ctx = EVP_PKEY_CTX_new (pkey, NULL);
EVP_PKEY_derive_init (ctx);
/* Provide the peer public key */
EVP_PKEY_derive_set_peer (ctx, peerkey);
/* Determine buffer length for shared secret */
EVP_PKEY_derive (ctx, NULL, secret_len);
secret = new unsigned char[*secret_len];
/* Derive the shared secret */
EVP_PKEY_derive (ctx, secret, secret_len);
EVP_PKEY_CTX_free (ctx);
EVP_PKEY_free (pkey);
EVP_PKEY_free (peerkey);
// We need enough key material, wrong NID curve used if this ever appears
if (*secret_len < SL_ECDH_SECRET_LEN) {
IONUDEBUG ("Key::GenerateECDHSecret() - Expected >= %d bytes, got %d\n", SL_ECDH_SECRET_LEN, (int)*secret_len);
delete[] secret;
return NULL;
}
// Never use a derived secret directly, hash it. We have 521 bits (66 bytes), hash
// this using sha512 giving a 256 bit AES key and a 256 bit HMAC key
unsigned char* md_value = new unsigned char[SL_ECDH_SECRET_LEN];
unsigned int md_len;
EVP_MD_CTX *mdctx = EVP_MD_CTX_create();
EVP_DigestInit_ex (mdctx, EVP_sha512(), NULL);
EVP_DigestUpdate (mdctx, secret, *secret_len);
EVP_DigestFinal_ex (mdctx, md_value, &md_len);
EVP_MD_CTX_destroy (mdctx);
delete[] secret;
*secret_len = md_len;
return md_value;
}
// Convert Key object to JSON string
std::string Key::ToJSON() const
{
std::string json;
EyeJSONObject* jkey = GetJSONObject();
if (jkey) {
jkey->GetJSONString (json);
delete jkey;
}
return json;
}
std::string Key::GetKeyTypeStr() const
{
std::string type;
switch (_type) {
case EC :
type ="EC";
break;
case RSA :
type ="RSA";
break;
case AES :
type = "AES";
break;
case AESHMAC :
type = "AESHMAC";
break;
case EC_PUBLIC :
type = "EC_PUBLIC";
break;
case RSA_PUBLIC :
type = "RSA_PUBLIC";
break;
case STRING :
type = "STRING";
break;
case GENERIC:
default:
type = "GENERIC";
break;
}
return type;
}
LIBEYE_DLL bool Key::Equals (const Key& other) const
{
if (_type == other.GetType() && _len == other.GetLength() && sequencelogic::MemCmp (_key, other.GetKey(), _len) == 0)
return true;
else
return false;
}
// Debug dump of Key object
void Key::Dump() const
{
char buff[128];
size_t len = (_len > 64 ? 64 : _len);
bool isAscii = true;
for (size_t i = 0; i < len; ++i) {
if (! isascii (_key[i])) {
isAscii = false;
break;
}
}
if (isAscii) {
memcpy (buff, _key, len);
buff[len] = '\0';
}
else {
sequencelogic::Base64Encode (_key, len, buff);
}
if ((isAscii && _len > 64) || (!isAscii && _len > 48))
strcat (buff, "...");
//#ifdef DEBUG
std::cout << "Key: " << _name << " " << _desc << " " << GetKeyTypeStr() << " " <<_len << " " << buff << std::endl;
//#endif
}
// Keyring holds any number of keys and linked keyrings
// It is always encrypted on disk, and until unlocked also in memory
Keyring::Keyring (const char* name, const char* desc)
{
if (sequencelogic::StrLen (name, 8) > 0)
_name = name;
if (sequencelogic::StrLen (desc, 8) > 0)
_desc = desc;
_db = nullptr;
_status = VALID;
_filename = nullptr;
_attempts = 0;
_sync = true;
_version = SL_KEYRING_VERSION;
_isLocked = false;
memset (_iv, 0, SL_AES_BLOCK_LEN);
memset (_digest, 0, SL_KEYRING_DIGEST_LEN);
EVP_CIPHER_CTX_init (&_ctx);
}
// Constructor that loads from file
Keyring::Keyring (const char* filename)
{
_status = VALID;
_filename = nullptr;
_db = nullptr;
_attempts = 0;
_sync = true;
_version = SL_KEYRING_VERSION;
_name = "";
_desc = "";
_isLocked = false;
memset (_iv, 0, SL_AES_BLOCK_LEN);
memset (_digest, 0, SL_KEYRING_DIGEST_LEN);
EVP_CIPHER_CTX_init (&_ctx);
if (sequencelogic::IsValidFilename (filename) && strlen (filename) <= SL_MAX_PATH) {
_filename = sequencelogic::StrDup (filename);
struct stat stat_buf;
int rc = stat(filename, &stat_buf);
if (rc == 0 && stat_buf.st_size > 16) {
ifstream file (filename, ios::in | ios::binary);
if (file.is_open()) {
char* json = new char [stat_buf.st_size + 1];
if (json) {
file.read (json, (streamsize)stat_buf.st_size);
json[stat_buf.st_size] = '\0';
if (file.bad() || !LoadFromJSON (json)) {
IONUDEBUG ("Keyring::Keyring (%s) - load failed", filename);
_status = CORRUPT;
}
delete[] json;
}
file.close();
}
else {
IONUDEBUG ("Keyring: open failed <%s>", filename);
}
}
}
else {
if (filename == NULL) {
IONUDEBUG ("Keyring::Keyring (nullptr) - load file failed");
_status = INVALID;
}
else {
IONUDEBUG ("Keyring::Keyring () - load file failed");
_status = INVALID;
}
}
}
Keyring::Keyring (const std::string& name, const std::string& database, const Key& key)
{
_status = VALID;
_filename = nullptr;
memset (_iv, 0, SL_AES_BLOCK_LEN);
memset (_digest, 0, SL_KEYRING_DIGEST_LEN);
_attempts = 0;
_sync = true;
_version = SL_KEYRING_VERSION;
_name = name;
_desc = "";
_isLocked = false;
_db = new EyeEncryptedDB (database, key.GetKey());
}
Keyring::Keyring (const std::string& name, const std::string& database, const std::string& password)
{
unsigned char key[SL_AES_KEY_LEN];
sequencelogic::DeriveKey (password.c_str(), key);
_status = VALID;
_filename = nullptr;
memset (_iv, 0, SL_AES_BLOCK_LEN);
memset (_digest, 0, SL_KEYRING_DIGEST_LEN);
_attempts = 0;
_sync = true;
_version = SL_KEYRING_VERSION;
_name = name;
_desc = "";
_isLocked = false;
_db = new EyeEncryptedDB (database, key);
}
Keyring::~Keyring()
{
if (_filename) {
delete[] _filename;
_filename = nullptr;
}
memset (_iv, 0, SL_AES_BLOCK_LEN);
memset (_digest, 0, SL_KEYRING_DIGEST_LEN);
if (_db) {
delete _db;
_db = nullptr;
}
else {
for (std::vector<Key*>::const_iterator itr = _keys.begin(); itr != _keys.end(); ++itr)
delete (*itr);
_keys.clear();
for (std::vector<Keyring*>::const_iterator itr = _keyrings.begin(); itr != _keyrings.end(); ++itr)
delete (*itr);
_keyrings.clear();
EVP_CIPHER_CTX_cleanup (&_ctx);
}
}
// Compute a secure digest that can be used to validate unlock and verify integrity
bool Keyring::DigestKeys (unsigned char* md_value)
{
// If unlocked/decrypted return false
if (!_isLocked || !md_value)
return false;
// If no keys then digest is 0, no work to do
if (_keys.size() < 1)
return true;
unsigned int md_len;
EVP_MD_CTX *mdctx;
mdctx = EVP_MD_CTX_create();
EVP_DigestInit_ex (mdctx, EVP_sha1(), NULL);
for (std::vector<Key*>::const_iterator itr = _keys.begin(); itr != _keys.end(); ++itr)
EVP_DigestUpdate (mdctx, (*itr)->GetKey(), (*itr)->GetLength());
EVP_DigestFinal_ex (mdctx, md_value, &md_len);
// Hash the digest to prevent any length extension attacks
EVP_DigestInit_ex (mdctx, EVP_sha1(), NULL);
EVP_DigestUpdate (mdctx, md_value, md_len);
EVP_DigestFinal_ex (mdctx, md_value, &md_len);
EVP_MD_CTX_destroy (mdctx);
//char b64[128];
//sequencelogic::Base64Encode (md_value, SL_KEYRING_DIGEST_LEN, b64);
//printf ("%d, %s\n", md_len, b64);
return true;
}
// Unlock the keyring with a password
bool Keyring::Unlock (const char* password)
{
// Check to see if it is valid
if (_status != VALID)
return false;
// Check to see if it is valid and locked
if (!_isLocked)
return true;
// Check for garbage input
if (sequencelogic::StrLen (password, SL_PASSWORD_MAX_LEN) == 0)
return false;
unsigned char key[SL_AES_KEY_LEN];
DeriveKey (password, key);
return Unlock (key);
}
// Unlock the keyring with a 256 bit AES key
bool Keyring::Unlock (const unsigned char* key)
{
// SQLite based keyring is always unlocked
if (_db)
return _db->IsOpen();
// Check to see if it is valid
if (_status != VALID)
return false;
// Check to see if it is locked or has any keys
if (!_isLocked)
return true;
if (!key)
return false;
unsigned char md_value[SL_KEYRING_DIGEST_LEN];
unsigned char enc[128];
if (!DigestKeys (md_value))
return false;
Encryptor (&_ctx, SL_KEYRING_DIGEST_LEN, md_value, enc, key, _iv);
// Compare the encrypted digest of the encrypted keys to see if this is right ring key
// Encrypted digest is actually 32 bytes, 20 + 12 padding, which is ignored in this check
if (memcmp (_digest, enc, SL_KEYRING_DIGEST_LEN) != 0) {
IONUDEBUG ("Keyring::Unlock(%s) - failed with wrong key", _filename);
return false;
}
else {
// Iterate through the keys and Decrypt them
for (std::vector<Key*>::const_iterator itr = _keys.begin(); itr != _keys.end(); ++itr)
(*itr)->Decrypt (&_ctx, key, _iv);
_isLocked = false;
return true;
}
return false;
}
// Lock the keyring with a password
bool Keyring::Lock (const char* password)
{
if (_isLocked || sequencelogic::StrLen (password, SL_PASSWORD_MAX_LEN) == 0)
return false;
unsigned char key[SL_AES_KEY_LEN];
DeriveKey (password, key);
return Lock (key);
}
// Lock the keyring with a 256 bit AES key
bool Keyring::Lock (const unsigned char* key)
{
// SQLite based keyring is always unlocked
if (_db)
return _db->IsOpen();
// Check to see if it is valid
if (_status != VALID)
return false;
// If locked/encrypted or no keys then return false
if (_isLocked || !key)
return false;
// Generate a random IV each time so it is never the same
if (!RAND_bytes(_iv, SL_AES_BLOCK_LEN))
return false;
// Iterate through the keys and encrypt them
for (std::vector<Key*>::const_iterator itr = _keys.begin(); itr != _keys.end(); ++itr)
(*itr)->Encrypt (&_ctx, key, _iv);
_isLocked = true;
// Compute a secure digest of the encrypted keys, then encrypt it
DigestKeys (_digest);
unsigned char enc[128];
Encryptor (&_ctx, SL_KEYRING_DIGEST_LEN, _digest, enc, key, _iv);
memcpy (_digest, enc, SL_KEYRING_DIGEST_LEN);
return true;
}
bool Keyring::AddJSONKey (EyeJSONObject* jkey)
{
std::string name = "";
std::string desc = "";
long expiration = 0;
size_t len = 0;
unsigned char* keydata = nullptr;
Key::KEY_TYPE type = Key::GENERIC;
for (size_t i = 0; i < jkey->GetNumMembers(); ++i) {
EyeJSONScalar* item = static_cast<EyeJSONScalar*>(jkey->GetMember(i));
if (sequencelogic::StrCmp (item->GetKey() , "name") == 0)
name = item->GetValue();
else if (sequencelogic::StrCmp (item->GetKey() , "desc") == 0)
desc = item->GetValue();
else if (sequencelogic::StrCmp (item->GetKey() , "type") == 0) {
if (sequencelogic::StrCmp (item->GetValue(), "RSA") == 0) type = Key::RSA;
else if (sequencelogic::StrCmp (item->GetValue(), "RSA_PUBLIC") == 0) type = Key::RSA_PUBLIC;
else if (sequencelogic::StrCmp (item->GetValue(), "STRING") == 0) type = Key::STRING;
else if (sequencelogic::StrCmp (item->GetValue(), "EC") == 0) type = Key::EC;
else if (sequencelogic::StrCmp (item->GetValue(), "EC_PUBLIC") == 0) type = Key::EC_PUBLIC;
else if (sequencelogic::StrCmp (item->GetValue(), "AES") == 0) type = Key::AES;
else if (sequencelogic::StrCmp (item->GetValue(), "AESHMAC") == 0) type = Key::AESHMAC;
else type = Key::GENERIC;
}
else if (sequencelogic::StrCmp (item->GetKey() , "key") == 0) {
len = 3 * strlen (item->GetValue()) / 4;
keydata = new unsigned char [len];
len = sequencelogic::Base64Decode (item->GetValue(), keydata); // Decode and set exact length
}
else if (sequencelogic::StrCmp (item->GetKey() , "expiration") == 0) {
std::stringstream ss (item->GetValue());
ss >> expiration;
}
}
Key* key = new Key (name, desc, len, keydata, type);
if (_db) {
_db->Put (_name, key->GetName(), key->GetValueJSON());
delete key;
}
else {
_keys.push_back (key); // On load, skip the duplicate check in AddKey (k);
}
if (keydata) delete [] keydata;
return true;
}
// Load a Keyring from JSON string
bool Keyring::LoadFromJSON (const std::string& json)
{
// Check for garbage input
if (json.size() < 16)
return false;
// If using SQLite db, drop the keys table to remove all existing keys
if (_db) {
if (_db->IsOpen())
_db->Remove (_name);
else
return false;
}
// Remove any existing keys and keyrings
if (_keys.size() > 0) {
for (std::vector<Key*>::const_iterator itr = _keys.begin(); itr != _keys.end(); ++itr)
delete (*itr);
_keys.clear();
}
if (_keyrings.size() > 0) {
for (std::vector<Keyring*>::const_iterator itr = _keyrings.begin(); itr != _keyrings.end(); ++itr)
delete (*itr);
_keyrings.clear();
}
EyeJSONObject ring (json.c_str());
if (ring.GetMember ("digest"))
_isLocked = true;
else
_isLocked = false;
return (AddJSONKeyring (&ring));
}
bool Keyring::AddJSONKeyring (EyeJSONObject* jring)
{
if (jring->GetType() != JSON_OBJECT || jring->GetNumMembers() <= 5)
return false;
for (size_t i = 0; i < jring->GetNumMembers(); ++i) {
EyeJSONNode* item = jring->GetMember(i);
if (item->GetType() == JSON_STRING) {
EyeJSONScalar* attr = dynamic_cast<EyeJSONScalar*>(item);
if (sequencelogic::StrCmp (attr->GetKey() , "name") == 0)
_name = attr->GetValue();
else if (sequencelogic::StrCmp (attr->GetKey() , "desc") == 0)
_desc = attr->GetValue();
else if (sequencelogic::StrCmp (attr->GetKey() , "magic") == 0) {
if (sequencelogic::StrCmp (attr->GetValue(), SL_KEYRING_MAGIC) != 0)
return false;
}
else if (sequencelogic::StrCmp (attr->GetKey(), "digest") == 0)
sequencelogic::Base64Decode (attr->GetValue(), _digest);
else if (sequencelogic::StrCmp (attr->GetKey(), "iv") == 0)
sequencelogic::Base64Decode (attr->GetValue(), _iv);
else if (sequencelogic::StrCmp (attr->GetKey(), "sync") == 0)
_sync = (sequencelogic::StrCmp (attr->GetValue(), "true") == 0);
else if (sequencelogic::StrCmp (attr->GetKey(), "ver") == 0)
_version = attr->GetValue();
else {
IONUDEBUG ("Keyring::AddJSONKeyring() - unknown attribute %s", attr->GetKey());
}
}
else if (item->GetType() == JSON_ARRAY) {
EyeJSONObject* array = dynamic_cast<EyeJSONObject*>(item);
// Loop through "keys" array and add them to this ring
if (sequencelogic::StrCmp (array->GetKey(), "keys") == 0) {
for (size_t j = 0; j < array->GetNumMembers(); ++j)
AddJSONKey (dynamic_cast<EyeJSONObject*>(array->GetMember(j)));
}
// Loop through "keyrings" array and add them to this ring
else if (sequencelogic::StrCmp (array->GetKey(), "keyrings") == 0) {
for (size_t j = 0; j < array->GetNumMembers(); ++j) {
Keyring* nested = new Keyring (nullptr, nullptr);
if (nested->AddJSONKeyring (dynamic_cast<EyeJSONObject*>(array->GetMember(j))))
_keyrings.push_back (nested);
else
delete nested;
}
}
else {
return false;
}
}
}
return true;
}
EyeJSONObject* Keyring::GetJSONObject () const
{
if (_db && _db->IsOpen()) {
IONUDEBUG ("Keyring::GetJSONObject() - SQLite based keyring requires key");
return nullptr;
}
EyeJSONObject* jring = new EyeJSONObject (nullptr, JSON_OBJECT, nullptr);
jring->AddMember (new EyeJSONScalar ("name", _name.c_str(), JSON_STRING));
jring->AddMember (new EyeJSONScalar ("desc", _desc.c_str(), JSON_STRING));
jring->AddMember (new EyeJSONScalar ("magic", SL_KEYRING_MAGIC, JSON_STRING));
if (_sync)
jring->AddMember (new EyeJSONScalar ("sync", "true", JSON_BOOL));
jring->AddMember (new EyeJSONScalar ("ver", _version.c_str(), JSON_STRING));
EyeJSONObject* keys = new EyeJSONObject ("keys", JSON_ARRAY, jring);
jring->AddMember (keys);
if (_keys.size() > 0 && _isLocked) {
char digest[4 * SL_KEYRING_DIGEST_LEN / 3 + 4];
sequencelogic::Base64Encode (_iv, SL_AES_BLOCK_LEN, digest);
jring->AddMember (new EyeJSONScalar ("iv", digest, JSON_STRING));
sequencelogic::Base64Encode (_digest, SL_KEYRING_DIGEST_LEN, digest);
jring->AddMember (new EyeJSONScalar ("digest", digest, JSON_STRING));
}
else {
IONUDEBUG ("Keyring::GetJSONObject() - getting JSON for unlocked keyring");
}
for (std::vector<Key*>::const_iterator itr = _keys.begin(); itr != _keys.end(); ++itr) {
keys->AddMember ((*itr)->GetJSONObject());
}
if (_keyrings.size() > 0) {
EyeJSONObject* keyrings = new EyeJSONObject ("keyrings", JSON_ARRAY, jring);
jring->AddMember (keyrings);
for (std::vector<Keyring*>::const_iterator itr = _keyrings.begin(); itr != _keyrings.end(); ++itr) {
keyrings->AddMember ((*itr)->GetJSONObject());
}
}
return jring;
}
// Get JSON object for SQLite based keyring or unlocked keyring
EyeJSONObject* Keyring::GetJSONObject (const Key& ringkey)
{
if (_db && _db->IsOpen()) {
Keyring tmpRing (_name.c_str(), _desc.c_str());
STRING_STRING_MAP keys = _db->GetAll (_name);
Key key;
for (std::map<std::string, std::string>::const_iterator itr = keys.begin(); itr != keys.end(); ++itr) {
key.LoadValueJSON (itr->first, itr->second);
Key* dbkey = new Key();
dbkey->Duplicate (&key);
tmpRing.AddKey (dbkey);
}
tmpRing.Lock (ringkey.GetKey());
return tmpRing.GetJSONObject();
}
return GetJSONObject();
}
std::string Keyring::Stringify (const Key& key)
{
std::string json = "";
EyeJSONObject* jring = GetJSONObject (key);
if (jring) {
jring->GetJSONString (json);
delete jring;
}
return json;
}
bool Keyring::Save ()
{
// When using SQLite db, it is already saved
if (_db && _db->IsOpen())
return true;
else if (_filename)
return SaveAs (_filename);
else
return false;
}
bool Keyring::SaveAs (const char* filename)
{
// Check for garbage input
if (!sequencelogic::IsValidFilename (filename) || strlen (filename) > SL_MAX_PATH) {
IONUDEBUG ("Keyring::SaveAs() invalid filename");
return false;
}
// When using SQLite db, it is already saved
if (_db) {
IONUDEBUG ("Keyring::SaveAs() SQLite based keyring must be closed and then renamed");
return false;
}
if (!_isLocked) {
IONUDEBUG ("Keyring::SaveAs() keyring is not locked");
}
// Save Keyring as JSON, write to a temporary file, delete and rename if successful
std::string tmpname = sequencelogic::TempFilename (filename, ".keyring");
ofstream file (tmpname, ios::out | ios::binary | ios::trunc);
if (file.is_open()) {
bool rc = true;
std::string json;
EyeJSONObject* jring = GetJSONObject();
if (jring) {
jring->GetJSONString (json);
delete jring;
file.write (json.c_str(), json.length());
if (file.bad()) {
IONUDEBUG ("Keyring::SaveAs(%s) - write keyring file failed", tmpname);
rc = false;
}
}
file.close();
if (rc) {
rc = sequencelogic::RenameFile (tmpname, filename);
// Rename failed
if (!rc) {
file.open (filename, ios::out | ios::binary | ios::trunc);
if (file.is_open()) {
file.write (json.c_str(), json.length());
if (!file.bad())
rc = true;
file.close();
}
}
}
else
sequencelogic::RemoveFile (tmpname);
// Update the filename member if different
if (sequencelogic::StrCmp (_filename, filename) != 0) {
if (_filename) {
delete[] _filename;
_filename = nullptr;
}
}
// Save the filename for subsequent saves
if (!_filename)
_filename = sequencelogic::StrDup (filename);
return rc;
}
else {
IONUDEBUG ("Keyring::SaveAs(%s) - unable to write keyring file", tmpname);
}
return false;
}
// Add a Key, Keyring now owns the Key
bool Keyring::AddKey (Key* key)
{
if (_isLocked || !key) // Can only add if unlocked
return false;
bool added = true;
// When using SQLite db, add the key to table
if (_db) {
added = _db->Put (_name, key->GetName(), key->GetValueJSON());
delete key;
}
else {
Key* tkey = const_cast<Key*>(GetKey(key->GetName()));
if (tkey) {
tkey->Duplicate (key);
delete key;
}
else
_keys.push_back (key);
}
return added;
}
bool Keyring::AddKey (const std::string& name, const std::string& desc, size_t len, const unsigned char* key, Key::KEY_TYPE type)
{
Key* rkey = new Key (name.c_str(), desc.c_str(), len, key, type);
return this->AddKey (rkey);
}
// Return JSON array of key names
std::string Keyring::GetKeyNames()
{
std::string json = "[";
if (_db) {
STRING_STRING_MAP keys = _db->GetAll (_name);
for (std::map<std::string, std::string>::const_iterator itr = keys.begin(); itr != keys.end(); ++itr) {
if (itr != keys.begin())
json += ",\"";
else
json += "\"";
json += itr->first;
json += "\"";
}
}
else {
for (std::vector<Key*>::const_iterator itr = _keys.begin(); itr != _keys.end(); ++itr) {
if (itr != _keys.begin())
json += ",\"";
else
json += "\"";
json += (*itr)->GetName();
json += "\"";
}
}
json += "]";
return json;
}
// Remove a key
bool Keyring::RemoveKey (const std::string& keyname)
{
// Can only remove if unlocked, and no garbage names
if (_isLocked || keyname.size() < 1)
return false;
// When using SQLite db, remove the key from table
if (_db) {
return _db->Remove (_name, keyname);
}
else {
size_t pos = 0;
bool found = false;
for (std::vector<Key*>::const_iterator itr = _keys.begin(); itr != _keys.end(); ++itr) {
if ((*itr)->GetName() == keyname) {
Key* tkey = _keys[pos];
_keys.erase (_keys.begin() + pos);
delete tkey;
found = true;
break;
}
pos++;
}
return found;
}
}
// Update a key
bool Keyring::UpdateKey (const Key* key)
{
if (_isLocked || !key) // Can only update if unlocked
return false;
bool updated = true;
if (_db) // INSERT OR REPLACE
updated = _db->Put (_name, key->GetName(), key->GetValueJSON());
else {
Key* tkey = const_cast<Key*>(GetKey(key->GetName()));
if (tkey)
tkey->Duplicate (key);
else
updated = false;
}
return updated;
}
// Get the named key
const Key* Keyring::GetKey (const std::string& keyname) const
{
if (_db) {
Key* key = new Key();
if (GetKey (keyname, *key))
return key;
else
return nullptr;
}
else {
Key* key = nullptr;
for (std::vector<Key*>::const_iterator itr = _keys.begin(); itr != _keys.end(); ++itr) {
if ((*itr)->GetName() == keyname) {
key = *itr;
break;
}
}
return key;
}
}
bool Keyring::GetKey (const std::string& keyname, Key& key) const
{
// When using SQLite db, get the key from the table
if (_db) {
char* value = _db->Get (_name, keyname);
if (!value)
return false;
key.LoadValueJSON (keyname, value);
sequencelogic::MemSet (value, 0, sequencelogic::StrLen (value, 1024));
delete[] value;
}
else {
const Key* rkey = GetKey (keyname);
if (!rkey)
return false;
key.Duplicate (rkey);
}
return true;
}
// Return the PEM format public key for RSA or EC private key, NULL if not found or wrong type
char* Keyring::GetPubKey (const char* keyname)
{
// Check for garbage input
if (sequencelogic::StrLen (keyname, 8) == 0)
return NULL;
Key key;
GetKey (keyname, key);
return key.GetPubKey();
}
// Generate a random 256 bit AES key and add to the ring
bool Keyring::GenerateAESKey (const char* name, const char* desc)
{
// Check for garbage input
if (sequencelogic::StrLen (name, 8) == 0)
return false;
bool rc = false;
unsigned char* data = new unsigned char [SL_AES_KEY_LEN];
if (RAND_bytes(data, SL_AES_KEY_LEN)) {
Key* k = new Key (name, desc, SL_AES_KEY_LEN, (const unsigned char*)data, Key::AES);
if (k) {
AddKey (k);
rc = true;
}
}
sequencelogic::MemSet (data, 0, SL_AES_KEY_LEN); // Obliterate from memory
delete[] data;
return rc;
}
// Generate a random 256 bit AES key, 256 bit HMAC key and add to the ring
bool Keyring::GenerateAESHMACKey (const char* name, const char* desc)
{
// Check for garbage input
if (sequencelogic::StrLen (name, 8) == 0)
return false;
bool rc = false;
unsigned char* data = new unsigned char [2*SL_AES_KEY_LEN];
if (RAND_bytes(data, 2*SL_AES_KEY_LEN)) {
Key* k = new Key (name, desc, 2*SL_AES_KEY_LEN, (const unsigned char*)data, Key::AESHMAC);
if (k) {
AddKey (k);
rc = true;
}
}
sequencelogic::MemSet (data, 0, 2*SL_AES_KEY_LEN); // Obliterate from memory
delete[] data;
return rc;
}
// Generate random 2048 bit RSA key pair and add the private key to the ring
// The OpenSSL private key has all parameters for both private and public
bool Keyring::GenerateRSAKey (const char* name, const char* desc)
{
// Check for garbage input
if (sequencelogic::StrLen (name, 8) == 0) {
IONUDEBUG ("GenerateRSAKey: invalid key name");
return false;
}
#ifdef TIME_DEBUG
long ms; // System time in milliseconds
ms = sequencelogic::GetSystemTime();
#endif
bool rc = false;
EVP_PKEY* evpkey = NULL;
EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new_id (EVP_PKEY_RSA, NULL);
if (!ctx) {
IONUDEBUG ("GenerateRSAKey:ctx error");
return false;
}
if (EVP_PKEY_keygen_init(ctx) <= 0) {
IONUDEBUG ("GenerateRSAKey:init error");
return false;
}
if (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, SL_RSA_KEY_BITS) <= 0) {
IONUDEBUG ("GenerateRSAKey:set error");
return false;
}
/* Generate key */
if (EVP_PKEY_keygen(ctx, &evpkey) <= 0) {
IONUDEBUG ("GenerateRSAKey:gen error");
return false;
}
if (evpkey != NULL) {
char* data = NULL;
struct rsa_st* rsa = EVP_PKEY_get1_RSA (evpkey);
BIO* out = BIO_new (BIO_s_mem());
PEM_write_bio_RSAPrivateKey (out, rsa, NULL, NULL, 0, NULL, NULL);
long bytes = BIO_get_mem_data (out, &data);
Key* k = new Key (name, desc, bytes, (const unsigned char*)data, Key::RSA);
if (k) {
AddKey (k);
rc = true;
}
BIO_free (out);
//PEM_write_bio_RSA_PUBKEY (out, rsa);
//BIO_flush (out);
//out = BIO_new_file("eprv.pem", "w");
//PEM_write_bio_RSAPrivateKey(out,rsa,
// EVP_des_ede3_cbc(), NULL, 0, NULL, "password");
//BIO_free (out);
EVP_PKEY_free(evpkey);
RSA_free (rsa);
EVP_PKEY_CTX_free(ctx);
}
#ifdef TIME_DEBUG
ms = sequencelogic::GetSystemTime() - ms;
printf ("RSA = %ld ms\n", ms);
#endif
return rc;
}
bool Keyring::GenerateRSAKey (const std::string& keyname, const std::string& desc, const std::string& seed)
{
//cout << seed << endl;
bool rc = false;
struct rsa_st* rsa = RSA_new();
EyeDRBG* drbg = new EyeDRBG ((unsigned char*)seed.data(), seed.size());
if (drbg->GenerateDRBGRSAKey (rsa)) {
#ifdef JAVASCRIPT
printf ("n:\n%s\n", BN_bn2hex(rsa->n));
printf ("e:\n%s\n", BN_bn2hex(rsa->e));
printf ("d:\n%s\n", BN_bn2hex(rsa->d));
printf ("p:\n%s\n", BN_bn2hex(rsa->p));
printf ("q:\n%s\n", BN_bn2hex(rsa->q));
printf ("dmp1:\n%s\n", BN_bn2hex(rsa->dmp1));
printf ("dmq1:\n%s\n", BN_bn2hex(rsa->dmq1));
printf ("iqmp:\n%s\n", BN_bn2hex(rsa->iqmp));
#endif
char* data = NULL;
BIO* out = BIO_new (BIO_s_mem());
PEM_write_bio_RSAPrivateKey (out, rsa, NULL, NULL, 0, NULL, NULL);
long bytes = BIO_get_mem_data (out, &data);
Key* k = new Key (keyname, desc, bytes, (const unsigned char*)data, Key::RSA);
if (k) {
AddKey (k);
rc = true;
}
BIO_free (out);
}
delete drbg;
RSA_free (rsa);
#ifdef TIME_DEBUG
ms = sequencelogic::GetSystemTime() - ms;
printf ("RSAc = %ld ms\n", ms);
#endif
return rc;
}
// Provision a challenge based account key pair with salt
bool Keyring::ProvisionChallengeRSAKey (const std::string& keyname, const std::string& challenges, std::string& salt)
{
// Check for garbage input
if (keyname.size() == 0 || challenges.size() == 0) {
IONUDEBUG ("Keyring::ProvisionChallengeRSAKey() - invalid key name or challenges");
return false;
}
#ifdef TIME_DEBUG
long ms; // System time in milliseconds
ms = sequencelogic::GetSystemTime();
#endif
unsigned char chal32[SL_SHA256_LEN];
unsigned char salt32[SL_SHA256_LEN * 2]; // Can be 64 bytes
unsigned char seed32[SL_SHA256_LEN];
char seed[SL_SHA256_LEN * 2 + 2];
char newsalt[512];
// Initial provision has empty salt
if (salt.size() == 0) {
sequencelogic::RandomBytes (SL_SHA256_LEN, salt32);
sequencelogic::Base64Encode (salt32, SL_SHA256_LEN, newsalt);
salt = newsalt;
}
else {
sequencelogic::Base64Decode (salt.c_str(), salt32);
}
std::string hex = sequencelogic::DigestMessage (MESSAGE_DIGEST_SHA256, (const unsigned char*)challenges.data(), challenges.size());
sequencelogic::HexToBinary (hex.c_str(), chal32);
XorKeys (chal32, salt32, seed32, SL_SHA256_LEN);
sequencelogic::BinaryToHex (seed32, SL_SHA256_LEN, seed);
return GenerateRSAKey (keyname, "", seed);
}
// Provision an account code based key pair
bool Keyring::ProvisionAccountRSAKey (const std::string& keyname, std::string& code, std::string& salt, const std::string& challenges)
{
unsigned char code32[SL_SHA256_LEN];
unsigned char salt32[SL_SHA256_LEN * 2]; // Can be 64 bytes
unsigned char seed32[SL_SHA256_LEN];
char seed[SL_SHA256_LEN * 2 + 2];
char newsalt[512];
std::string hex = "";
size_t saltlen = 0;
// Create new account
if (salt.size() == 0) {
if (code.size() == 0)
code = sequencelogic::RandomBase32 (8);
if (challenges.size() > 0)
saltlen = SL_SHA256_LEN;
hex = sequencelogic::DigestMessage (MESSAGE_DIGEST_SHA256, (const unsigned char*)code.data(), code.size());
sequencelogic::HexToBinary (hex.c_str(), code32);
sequencelogic::RandomBytes (SL_SHA256_LEN, salt32 + saltlen);
XorKeys (code32, salt32 + saltlen, seed32, SL_SHA256_LEN);
sequencelogic::BinaryToHex (seed32, SL_SHA256_LEN, seed);
// Create challenge salt if specified
if (challenges.size() > 0) {
hex = sequencelogic::DigestMessage (MESSAGE_DIGEST_SHA256, (const unsigned char*)challenges.data(), challenges.size());
sequencelogic::HexToBinary (hex.c_str(), code32);
XorKeys (code32, seed32, salt32, SL_SHA256_LEN);
saltlen = SL_SHA256_LEN;
}
sequencelogic::Base64Encode (salt32, SL_SHA256_LEN + saltlen, newsalt);
salt = newsalt;
}
// Recover or initial user provision
else {
hex = sequencelogic::DigestMessage (MESSAGE_DIGEST_SHA256, (const unsigned char*)code.data(), code.size());
sequencelogic::HexToBinary (hex.c_str(), code32);
if (salt.size() == 44 || salt.size() == 88)
saltlen = sequencelogic::Base64Decode (salt.c_str(), salt32) - SL_SHA256_LEN;
else {
IONUDEBUG ("Keyring::Provision(%s) - invalid salt", keyname.c_str());
return false;
}
// If both challenge and account salt are present, account salt is second 32 byte value
size_t rsalt = (salt.size() > 44 ? SL_SHA256_LEN : 0);
XorKeys (code32, salt32 + rsalt, seed32, SL_SHA256_LEN);
sequencelogic::BinaryToHex (seed32, SL_SHA256_LEN, seed);
// Create challenge salt if specified
if (challenges.size() > 0) {
hex = sequencelogic::DigestMessage (MESSAGE_DIGEST_SHA256, (const unsigned char*)challenges.data(), challenges.size());
sequencelogic::HexToBinary (hex.c_str(), code32);
XorKeys (code32, seed32, salt32, SL_SHA256_LEN);
saltlen = SL_SHA256_LEN;
}
// Generate account/recovery code and new salt, old code is invalidated
code = sequencelogic::RandomBase32 (8);
hex = sequencelogic::DigestMessage (MESSAGE_DIGEST_SHA256, (const unsigned char*)code.data(), code.size());
sequencelogic::HexToBinary (hex.c_str(), code32);
XorKeys (code32, seed32, salt32 + saltlen, SL_SHA256_LEN);
sequencelogic::Base64Encode (salt32, SL_SHA256_LEN + saltlen, newsalt);
salt = newsalt;
}
return GenerateRSAKey (keyname, "", seed);
}
bool Keyring::GenerateRSAKey (const std::string& name, const std::string& desc, const std::string& challenge, const std::string& secret, std::string& salt)
{
// Check for garbage input
if (name.size() == 0) {
IONUDEBUG ("Keyring::GenerateRSAKey() - invalid key name");
return false;
}
unsigned char k1[SL_SHA256_LEN];
unsigned char k2[SL_SHA256_LEN];
unsigned char k3[SL_SHA256_LEN];
char seed[SL_SHA256_LEN * 2 + 2];
if (challenge.size() > 0) {
std::string h1 = sequencelogic::DigestMessage (MESSAGE_DIGEST_SHA256, (const unsigned char*)challenge.data(), challenge.size());
sequencelogic::HexToBinary (h1.c_str(), k1);
if (salt.size() > 0) {
if (salt.size() == 44)
sequencelogic::Base64Decode (salt.c_str(), k2);
else if (salt.size() == 64)
sequencelogic::HexToBinary (salt.c_str(), salt.size(), k2);
else {
std::string h2 = sequencelogic::DigestMessage (MESSAGE_DIGEST_SHA256, (const unsigned char*)salt.data(), salt.size());
sequencelogic::HexToBinary (h2.c_str(), k2);
}
XorKeys (k1, k2, k3, SL_SHA256_LEN);
sequencelogic::BinaryToHex (k3, SL_SHA256_LEN, seed);
}
else if (secret.size() > 0) {
std::string h2 = sequencelogic::DigestMessage (MESSAGE_DIGEST_SHA256, (const unsigned char*)secret.data(), secret.size());
sequencelogic::HexToBinary (h2.c_str(), k2);
XorKeys (k1, k2, k3, SL_SHA256_LEN);
sequencelogic::Base64Encode (k3, SL_SHA256_LEN, seed);
salt = seed;
strcpy (seed, h2.c_str());
}
else // Old style challenges with no salt
return GenerateRSAKey (name, desc, challenge);
}
else if (secret.size() > 0) {
std::string h1 = sequencelogic::DigestMessage (MESSAGE_DIGEST_SHA256, (const unsigned char*)secret.data(), secret.size());
strcpy (seed, h1.c_str());
}
return GenerateRSAKey (name, desc, seed);
}
// Create a set of keys from a van and list of email addresses, keyname=email, desc=secret
bool Keyring::GenerateRSAKeys (const std::string& van, const std::vector<std::string>& emailist)
{
for (auto& email : emailist) {
std::string secret = sequencelogic::RandomBase32 (8);
std::string seed (van);
seed += email;
seed += secret;
if (! GenerateRSAKey (email, secret, seed)) {
IONUDEBUG ("Keyring::GenerateRSAKeys(%s) - failed", email.c_str());
return false;
}
}
return true;
}
// Generate random 521 bit EC key using ANSI X9.62 Prime 521 curve and add to the keyring
bool Keyring::GenerateECKey (const char* name, const char* desc)
{
// Check for garbage input
if (sequencelogic::StrLen (name, 8) == 0) {
IONUDEBUG ("Keyring::GenerateECKey() - invalid key name");
return false;
}
bool rc = false;
EVP_PKEY_CTX *pctx, *kctx;
EVP_PKEY *pkey = NULL, *params = NULL;
/* NB: assumes pkey, peerkey have been already set up */
pctx = EVP_PKEY_CTX_new_id (EVP_PKEY_EC, NULL);
if (! pctx)
return false;
EVP_PKEY_paramgen_init (pctx);
EVP_PKEY_CTX_set_ec_paramgen_curve_nid (pctx, NID_secp521r1);
EVP_PKEY_paramgen (pctx, &params);
/* Create the context for the key generation */
kctx = EVP_PKEY_CTX_new (params, NULL);
/* Generate the key */
EVP_PKEY_keygen_init (kctx);
EVP_PKEY_keygen (kctx, &pkey);
if (pkey) {
char* data = NULL;
BIO* out = BIO_new (BIO_s_mem());
PEM_write_bio_PrivateKey (out, pkey, NULL, NULL, 0, NULL, NULL);
long bytes = BIO_get_mem_data (out, &data);
Key* k = new Key (name, desc, bytes, (const unsigned char*)data, Key::EC);
if (k) {
AddKey (k);
rc = true;
}
BIO_free (out);
}
EVP_PKEY_free (pkey);
EVP_PKEY_CTX_free (kctx);
EVP_PKEY_free (params);
EVP_PKEY_CTX_free (pctx);
return rc;
}
// Generate 521 bit EC keypair using a json challenge string to seed a DBRG, such that
// given same challenges will produce the same key, and add the private key to the ring
bool Keyring::GenerateECKey (const char* name, const char* desc, const char* challenges)
{
// Check for garbage input
if (sequencelogic::StrLen (name, 8) == 0) {
IONUDEBUG ("Keyring::GenerateECKey() - invalid key name");
return false;
}
#ifdef TIME_DEBUG
long ms; // System time in milliseconds
ms = sequencelogic::GetSystemTime();
#endif
bool rc = false;
struct ec_key_st* ec = EC_KEY_new_by_curve_name(NID_secp521r1);
EyeDRBG* drbg = new EyeDRBG ((unsigned char*)challenges, strlen (challenges));
if (drbg->GenerateDRBGECKey (ec)) {
char* data = NULL;
BIO* out = BIO_new (BIO_s_mem());
PEM_write_bio_ECPrivateKey (out, ec, NULL, NULL, 0, NULL, NULL);
long bytes = BIO_get_mem_data (out, &data);
Key* k = new Key (name, desc, bytes, (const unsigned char*)data, Key::EC);
if (k) {
AddKey (k);
rc = true;
}
BIO_free (out);
}
delete drbg;
EC_KEY_free (ec);
#ifdef TIME_DEBUG
ms = sequencelogic::GetSystemTime() - ms;
printf ("ECc = %ld ms\n", ms);
#endif
return rc;
}
bool Keyring::GenerateECKey (const std::string& name, const std::string& desc, const std::string& challenge, const std::string& secret, std::string& salt)
{
// Check for garbage input
if (name.size() == 0) {
IONUDEBUG ("Keyring::GenerateECKey() - invalid key name");
return false;
}
unsigned char k1[SL_SHA256_LEN];
unsigned char k2[SL_SHA256_LEN];
unsigned char k3[SL_SHA256_LEN];
char seed[SL_SHA256_LEN * 2 + 2];
if (challenge.size() > 0) {
std::string h1 = sequencelogic::DigestMessage (MESSAGE_DIGEST_SHA256, (const unsigned char*)challenge.data(), challenge.size());
sequencelogic::HexToBinary (h1.c_str(), k1);
if (salt.size() > 0) {
if (salt.size() == 44)
sequencelogic::Base64Decode (salt.c_str(), k2);
else if (salt.size() == 64)
sequencelogic::HexToBinary (salt.c_str(), salt.size(), k2);
else {
std::string h2 = sequencelogic::DigestMessage (MESSAGE_DIGEST_SHA256, (const unsigned char*)salt.data(), salt.size());
sequencelogic::HexToBinary (h2.c_str(), k2);
}
XorKeys (k1, k2, k3, SL_SHA256_LEN);
sequencelogic::BinaryToHex (k3, SL_SHA256_LEN, seed);
}
else if (secret.size() > 0) {
std::string h2 = sequencelogic::DigestMessage (MESSAGE_DIGEST_SHA256, (const unsigned char*)secret.data(), secret.size());
sequencelogic::HexToBinary (h2.c_str(), k2);
XorKeys (k1, k2, k3, SL_SHA256_LEN);
sequencelogic::Base64Encode (k3, SL_SHA256_LEN, seed);
salt = seed;
strcpy (seed, h2.c_str());
}
else // Old style challenges with no salt
return GenerateECKey (name.c_str(), desc.size() > 0 ? desc.c_str() : NULL, challenge.c_str());
}
else if (secret.size() > 0) {
std::string h1 = sequencelogic::DigestMessage (MESSAGE_DIGEST_SHA256, (const unsigned char*)secret.data(), secret.size());
strcpy (seed, h1.c_str());
}
return GenerateECKey (name.c_str(), desc.c_str(), seed);
}
unsigned char* Keyring::Encrypt (const std::string keyname, const unsigned char *plaintext,
size_t inlen, size_t* outlen, const unsigned char *iv) const
{
Key key;
GetKey (keyname, key);
switch (key.GetType()) {
case Key::AES :
return key.SymmetricEncryptBuffer (plaintext, inlen, outlen, iv);
case Key::EC :
case Key::EC_PUBLIC :
return key.ECIESEncrypt (plaintext, inlen, outlen);
case Key::RSA :
case Key::RSA_PUBLIC :
return key.RSAIESEncrypt (plaintext, inlen, outlen);
case Key::AESHMAC :
return key.HMACEncrypt (plaintext, inlen, outlen);
default:
IONUDEBUG ("Keyring::Encrypt (%s) - not supported with %s", keyname.c_str(), key.GetKeyTypeStr().c_str());
return nullptr;
}
}
unsigned char* Keyring::Decrypt (const std::string keyname, const unsigned char *ciphertext,
size_t inlen, size_t* outlen, const unsigned char *iv) const
{
Key key;
GetKey (keyname, key);
switch (key.GetType()) {
case Key::AES :
return key.SymmetricDecryptBuffer (ciphertext, inlen, outlen, iv);
case Key::EC :
return key.ECIESDecrypt (ciphertext, inlen, outlen);
case Key::RSA :
return key.RSAIESDecrypt (ciphertext, inlen, outlen);
case Key::AESHMAC :
return key.HMACDecrypt (ciphertext, inlen, outlen);
default:
IONUDEBUG ("Keyring::Decrypt (%s) - not supported with %s", keyname.c_str(), key.GetKeyTypeStr().c_str());
return nullptr;
}
}
// Access to linked keyrings
bool Keyring::AddKeyring (Keyring* keyring)
{
if (_isLocked || !keyring) // Can only add if unlocked
return false;
bool added = true;
if (GetKeyring(keyring->GetName()))
added = false;
else
_keyrings.push_back (keyring);
return added;
}
bool Keyring::RemoveKeyring (const char* ringname)
{
if (_isLocked || !ringname || _keyrings.size() == 0) // Can only remove if unlocked
return false;
size_t pos = 0;
bool found = false;
for (std::vector<Keyring*>::const_iterator itr = _keyrings.begin(); itr != _keyrings.end(); ++itr) {
if (sequencelogic::StrCmp ((*itr)->GetName(), ringname) == 0) {
Keyring* tkeyring = _keyrings[pos];
_keyrings.erase (_keyrings.begin() + pos);
delete tkeyring;
found = true;
break;
}
pos++;
}
return found;
}
const Keyring* Keyring::GetKeyring (const char* ringname) const
{
Keyring* keyring = NULL;
if (sequencelogic::StrLen (ringname, 8) > 0) {
for (std::vector<Keyring*>::const_iterator itr = _keyrings.begin(); itr != _keyrings.end(); ++itr) {
if (sequencelogic::StrCmp ((*itr)->GetName(), ringname) == 0) {
keyring = *itr;
break;
}
}
}
return keyring;
}
// Debug
void Keyring::Dump()
{
#ifdef DEBUG
if (_db) {
STRING_STRING_MAP keys = _db->GetAll (_name);
std::cout << "Keyring: name=" << _name << ", desc=" << _desc << ", ver=" << _version << ", keys=" << keys.size() << std::endl;
for (std::map<std::string, std::string>::const_iterator itr = keys.begin(); itr != keys.end(); ++itr) {
std::cout << itr->first << " " << itr->second << std::endl;
}
}
else {
std::cout << "Keyring: name=\"" << _name << "\", desc=\"" << _desc << "\", ver=\"" << _version << "\", keys=" << _keys.size() << " locked=" << (_isLocked ? 't' : 'f') << std::endl;
for (std::vector<Key*>::const_iterator itr = _keys.begin(); itr != _keys.end(); ++itr)
(*itr)->Dump();
for (std::vector<Keyring*>::const_iterator itr = _keyrings.begin(); itr != _keyrings.end(); ++itr)
(*itr)->Dump();
}
#endif
}