Don't double-free an openssl struct

This commit is contained in:
Simon Walton 2019-04-19 17:03:43 -07:00
parent 38ca699ff5
commit 5e251b1033

View file

@ -41,12 +41,13 @@
#include "ui/SecurityImageProvider.h"
#include "scripting/HMDScriptingInterface.h"
static const char* KEY_FILE = "hifikey";
static const char* INSTRUCTIONS_FILE = "backup_instructions.html";
static const char* IMAGE_HEADER = "-----BEGIN SECURITY IMAGE-----\n";
static const char* IMAGE_FOOTER = "-----END SECURITY IMAGE-----\n";
namespace {
const char* KEY_FILE = "hifikey";
const char* INSTRUCTIONS_FILE = "backup_instructions.html";
const char* IMAGE_HEADER = "-----BEGIN SECURITY IMAGE-----\n";
const char* IMAGE_FOOTER = "-----END SECURITY IMAGE-----\n";
void initialize() {
void initialize() {
static bool initialized = false;
if (!initialized) {
SSL_load_error_strings();
@ -54,32 +55,15 @@ void initialize() {
OpenSSL_add_all_algorithms();
initialized = true;
}
}
}
QString keyFilePath() {
QString keyFilePath() {
auto accountManager = DependencyManager::get<AccountManager>();
return PathUtils::getAppDataFilePath(QString("%1.%2").arg(accountManager->getAccountInfo().getUsername(), KEY_FILE));
}
bool Wallet::copyKeyFileFrom(const QString& pathname) {
QString existing = getKeyFilePath();
qCDebug(commerce) << "Old keyfile" << existing;
if (!existing.isEmpty()) {
QString backup = QString(existing).insert(existing.indexOf(KEY_FILE) - 1,
QDateTime::currentDateTime().toString(Qt::ISODate).replace(":", ""));
qCDebug(commerce) << "Renaming old keyfile to" << backup;
if (!QFile::rename(existing, backup)) {
qCCritical(commerce) << "Unable to backup" << existing << "to" << backup;
return false;
}
}
QString destination = keyFilePath();
bool result = QFile::copy(pathname, destination);
qCDebug(commerce) << "copy" << pathname << "to" << destination << "=>" << result;
return result;
}
// use the cached _passphrase if it exists, otherwise we need to prompt
int passwordCallback(char* password, int maxPasswordSize, int rwFlag, void* u) {
// use the cached _passphrase if it exists, otherwise we need to prompt
int passwordCallback(char* password, int maxPasswordSize, int rwFlag, void* u) {
// just return a hardcoded pwd for now
auto wallet = DependencyManager::get<Wallet>();
auto passphrase = wallet->getPassphrase();
@ -94,9 +78,9 @@ int passwordCallback(char* password, int maxPasswordSize, int rwFlag, void* u) {
qCCritical(commerce) << "no cached passphrase while decrypting!";
return 0;
}
}
}
EC_KEY* readKeys(QString filename) {
EC_KEY* readKeys(QString filename) {
QFile file(filename);
EC_KEY* key = NULL;
if (file.open(QFile::ReadOnly)) {
@ -112,8 +96,6 @@ EC_KEY* readKeys(QString filename) {
if ((key = PEM_read_bio_ECPrivateKey(bufio, &key, passwordCallback, NULL))) {
qCDebug(commerce) << "read private key";
BIO_free(bufio);
file.close();
} else {
qCDebug(commerce) << "failed to read private key";
}
@ -126,49 +108,9 @@ EC_KEY* readKeys(QString filename) {
qCDebug(commerce) << "failed to open key file" << filename;
}
return key;
}
bool Wallet::writeBackupInstructions() {
QString inputFilename(PathUtils::resourcesPath() + "html/commerce/backup_instructions.html");
QString outputFilename = PathUtils::getAppDataFilePath(INSTRUCTIONS_FILE);
QFile inputFile(inputFilename);
QFile outputFile(outputFilename);
bool retval = false;
if (getKeyFilePath().isEmpty()) {
return false;
}
if (QFile::exists(inputFilename) && inputFile.open(QIODevice::ReadOnly)) {
if (outputFile.open(QIODevice::ReadWrite)) {
// Read the data from the original file, then close it
QByteArray fileData = inputFile.readAll();
inputFile.close();
// Translate the data from the original file into a QString
QString text(fileData);
// Replace the necessary string
text.replace(QString("HIFIKEY_PATH_REPLACEME"), keyFilePath());
// Write the new text back to the file
outputFile.write(text.toUtf8());
// Close the output file
outputFile.close();
retval = true;
qCDebug(commerce) << "wrote html file successfully";
} else {
qCDebug(commerce) << "failed to open output html file" << outputFilename;
}
} else {
qCDebug(commerce) << "failed to open input html file" << inputFilename;
}
return retval;
}
bool writeKeys(QString filename, EC_KEY* keys) {
bool writeKeys(QString filename, EC_KEY* keys) {
BIO* bio = BIO_new(BIO_s_mem());
bool retval = false;
if (!PEM_write_bio_EC_PUBKEY(bio, keys)) {
@ -198,35 +140,11 @@ bool writeKeys(QString filename, EC_KEY* keys) {
}
BIO_free(bio);
return retval;
}
}
bool Wallet::setWallet(const QByteArray& wallet) {
QFile file(keyFilePath());
if (!file.open(QIODevice::WriteOnly)) {
qCCritical(commerce) << "Unable to open wallet for write in" << keyFilePath();
return false;
}
if (file.write(wallet) != wallet.count()) {
qCCritical(commerce) << "Unable to write wallet in" << keyFilePath();
return false;
}
file.close();
return true;
}
QByteArray Wallet::getWallet() {
QFile file(keyFilePath());
if (!file.open(QIODevice::ReadOnly)) {
qCInfo(commerce) << "No existing wallet in" << keyFilePath();
return QByteArray();
}
QByteArray wallet = file.readAll();
file.close();
return wallet;
}
QPair<QByteArray*, QByteArray*> generateECKeypair() {
QPair<QByteArray*, QByteArray*> generateECKeypair() {
EC_KEY* keyPair = EC_KEY_new_by_curve_name(NID_secp256k1);
QPair<QByteArray*, QByteArray*> retval{};
QPair<QByteArray*, QByteArray*> retval {};
EC_KEY_set_asn1_flag(keyPair, OPENSSL_EC_NAMED_CURVE);
if (!EC_KEY_generate_key(keyPair)) {
@ -276,11 +194,11 @@ QPair<QByteArray*, QByteArray*> generateECKeypair() {
OPENSSL_free(publicKeyDER);
OPENSSL_free(privateKeyDER);
return retval;
}
// END copied code (which will soon change)
}
// END copied code (which will soon change)
// the public key can just go into a byte array
QByteArray readPublicKey(QString filename) {
// the public key can just go into a byte array
QByteArray readPublicKey(QString filename) {
QByteArray retval;
QFile file(filename);
if (file.open(QIODevice::ReadOnly)) {
@ -316,11 +234,11 @@ QByteArray readPublicKey(QString filename) {
qCDebug(commerce) << "couldn't open" << filename;
}
return QByteArray();
}
}
// the private key should be read/copied into heap memory. For now, we need the EC_KEY struct
// so I'll return that.
EC_KEY* readPrivateKey(QString filename) {
// the private key should be read/copied into heap memory. For now, we need the EC_KEY struct
// so I'll return that.
EC_KEY* readPrivateKey(QString filename) {
QFile file(filename);
EC_KEY* key = NULL;
if (file.open(QIODevice::ReadOnly)) {
@ -344,23 +262,25 @@ EC_KEY* readPrivateKey(QString filename) {
qCDebug(commerce) << "couldn't open" << filename;
}
return key;
}
}
// QT's QByteArray will convert to Base64 without any embedded newlines. This just
// writes it with embedded newlines, which is more readable.
void outputBase64WithNewlines(QFile& file, const QByteArray& b64Array) {
// QT's QByteArray will convert to Base64 without any embedded newlines. This just
// writes it with embedded newlines, which is more readable.
void outputBase64WithNewlines(QFile& file, const QByteArray& b64Array) {
for (int i = 0; i < b64Array.size(); i += 64) {
file.write(b64Array.mid(i, 64));
file.write("\n");
}
}
}
void initializeAESKeys(unsigned char* ivec, unsigned char* ckey, const QByteArray& salt) {
void initializeAESKeys(unsigned char* ivec, unsigned char* ckey, const QByteArray& salt) {
// use the ones in the wallet
auto wallet = DependencyManager::get<Wallet>();
memcpy(ivec, wallet->getIv(), 16);
memcpy(ckey, wallet->getCKey(), 32);
}
}
} // close unnamed namespace
Wallet::Wallet() {
auto nodeList = DependencyManager::get<NodeList>();
@ -423,6 +343,88 @@ Wallet::~Wallet() {
}
}
bool Wallet::setWallet(const QByteArray& wallet) {
QFile file(keyFilePath());
if (!file.open(QIODevice::WriteOnly)) {
qCCritical(commerce) << "Unable to open wallet for write in" << keyFilePath();
return false;
}
if (file.write(wallet) != wallet.count()) {
qCCritical(commerce) << "Unable to write wallet in" << keyFilePath();
return false;
}
file.close();
return true;
}
QByteArray Wallet::getWallet() {
QFile file(keyFilePath());
if (!file.open(QIODevice::ReadOnly)) {
qCInfo(commerce) << "No existing wallet in" << keyFilePath();
return QByteArray();
}
QByteArray wallet = file.readAll();
file.close();
return wallet;
}
bool Wallet::copyKeyFileFrom(const QString& pathname) {
QString existing = getKeyFilePath();
qCDebug(commerce) << "Old keyfile" << existing;
if (!existing.isEmpty()) {
QString backup = QString(existing).insert(existing.indexOf(KEY_FILE) - 1,
QDateTime::currentDateTime().toString(Qt::ISODate).replace(":", ""));
qCDebug(commerce) << "Renaming old keyfile to" << backup;
if (!QFile::rename(existing, backup)) {
qCCritical(commerce) << "Unable to backup" << existing << "to" << backup;
return false;
}
}
QString destination = keyFilePath();
bool result = QFile::copy(pathname, destination);
qCDebug(commerce) << "copy" << pathname << "to" << destination << "=>" << result;
return result;
}
bool Wallet::writeBackupInstructions() {
QString inputFilename(PathUtils::resourcesPath() + "html/commerce/backup_instructions.html");
QString outputFilename = PathUtils::getAppDataFilePath(INSTRUCTIONS_FILE);
QFile inputFile(inputFilename);
QFile outputFile(outputFilename);
bool retval = false;
if (getKeyFilePath().isEmpty()) {
return false;
}
if (QFile::exists(inputFilename) && inputFile.open(QIODevice::ReadOnly)) {
if (outputFile.open(QIODevice::ReadWrite)) {
// Read the data from the original file, then close it
QByteArray fileData = inputFile.readAll();
inputFile.close();
// Translate the data from the original file into a QString
QString text(fileData);
// Replace the necessary string
text.replace(QString("HIFIKEY_PATH_REPLACEME"), keyFilePath());
// Write the new text back to the file
outputFile.write(text.toUtf8());
// Close the output file
outputFile.close();
retval = true;
qCDebug(commerce) << "wrote html file successfully";
} else {
qCDebug(commerce) << "failed to open output html file" << outputFilename;
}
} else {
qCDebug(commerce) << "failed to open input html file" << inputFilename;
}
return retval;
}
bool Wallet::setPassphrase(const QString& passphrase) {
if (_passphrase) {
delete _passphrase;