3271 lines
107 KiB
C++
3271 lines
107 KiB
C++
|
|
// 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, ¶ms);
|
||
|
|
|
||
|
|
/* 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
|
||
|
|
}
|
||
|
|
|