Remove KeePass integration (#4628)

* Remove KeePass integration

This integration is not actively supported. It has zero tests, little documentation, and has not really been actively maintained. As far as I can tell, noone uses this integration, and even if they do, they will simply have to copy/paste password from keepass instead of using this integration.

* continued
This commit is contained in:
PastaPastaPasta 2021-12-22 03:53:11 -06:00 committed by GitHub
parent e95c13c207
commit 5bc79fefc9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 3 additions and 901 deletions

View File

@ -1,58 +0,0 @@
### What is it about
More info regarding KeePass: http://keepass.info/
KeePass integration use KeePassHttp (https://github.com/pfn/keepasshttp/) to facilitate communications between the client and KeePass. KeePassHttp is a plugin for KeePass 2.x and provides a secure means of exposing KeePass entries via HTTP for clients to consume.
The implementation is dependent on the following:
- crypter.h for AES encryption helper functions.
- rpcprotocol.h for handling RPC communications. Could only be used partially however due some static values in the code.
- OpenSSL for base64 encoding. regular util.h libraries were not used for base64 encoding/decoding since they do not use secure allocation.
- JSON Spirit for reading / writing RPC communications
### A note about security
Every connection to KeePassHTTP server is done via plain HTTP and even though protocol uses some internal encryption it should not be considered to be a highly secure one. This protocol has certain flaw which allow an attacker to decrypt your passwords when they manage to intercept communication between a KeePassHTTP server over a network connection (see [here](https://github.com/pfn/keepasshttp/issues/258) and [here](https://github.com/keepassxreboot/keepassxc/issues/147)). Dash Core therefore strictly limits communication between itself and KeePassHttp to your local computer. As long as your computer is not compromised, your wallet passphrase is as safe as if you would enter it directly.
### What's new
The following new options are available for dashd and dash-qt:
- _-keepass_ Use KeePass 2 integration using KeePassHttp plugin (default: 0)
- _-keepassport=_ Connect to KeePassHttp on port (default: 19455)
- _-keepasskey=_ KeePassHttp key for AES encrypted communication with KeePass
- _-keepassid=_ KeePassHttp id for the established association
- _-keepassname=_ Name to construct url for KeePass entry that stores the wallet passphrase
The following rpc commands are available:
- _keepass genkey_: generates a base64 encoded 256 bit AES key that can be used for the communication with KeePassHttp. Only necessary for manual configuration. Use init for automatic configuration.
- _keepass init_: sets up the association between dashd and keepass by generating an AES key and sending an association message to KeePassHttp. This will trigger KeePass to ask for an Id for the association. Returns the association and the base64 encoded string for the AES key.
- _keepass setpassphrase_: updates the passphrase in KeePassHttp to a new value. This should match the passphrase you intend to use for the wallet. Please note that the standard RPC commands _walletpassphrasechange_ and the wallet encryption from the QT GUI already send the updates to KeePassHttp, so this is only necessary for manual manipulation of the password.
### How to setup
Sample initialization flow from _dash-qt_ console (this needs to be done only once to set up the association):
- Have KeePass running with an open database
- Start _dash-qt_
- Open console
- Type "_keepass init_" in dash-qt console
- Keepass pops up and asks for an association id, fill that in, for example, "_mydrkwallet_"
- You should get a response like this "_Association successful. Id: mydrkwalletdash - Key: AgQkcs6cI7v9tlSYKjG/+s8wJrGALHl3jLosJpPLzUE=_"
- Edit _dash.conf_ and fill in these values
```
keepass=1
keepasskey=AgQkcs6cI7v9tlSYKjG/+s8wJrGALHl3jLosJpPLzUE=
keepassid=mydrkwallet
keepassname=testwallet
```
- Restart _dash-qt_
At this point, the association is made. The next action depends on your particular situation:
- current wallet is not yet encrypted. Encrypting the wallet will trigger the integration and stores the password in KeePass (Under the '_KeePassHttp Passwords_' group, named after _keepassname_.
- current wallet is already encrypted: use "_keepass setpassphrase_" to store the passphrase in KeePass.
At this point, the passphrase is stored in KeePassHttp. When Unlocking the wallet, one can use _keepass_ as the passphrase to trigger retrieval of the password. This works from the RPC commands as well as the GUI.
Extended guide with screenshots is also available: https://www.dash.org/forum/threads/keepass-integration.3620/

View File

@ -199,7 +199,6 @@ BITCOIN_CORE_H = \
interfaces/wallet.h \ interfaces/wallet.h \
key.h \ key.h \
key_io.h \ key_io.h \
keepass.h \
keystore.h \ keystore.h \
dbwrapper.h \ dbwrapper.h \
limitedmap.h \ limitedmap.h \
@ -463,7 +462,6 @@ libdash_wallet_a_SOURCES = \
coinjoin/options.cpp \ coinjoin/options.cpp \
coinjoin/util.cpp \ coinjoin/util.cpp \
interfaces/wallet.cpp \ interfaces/wallet.cpp \
keepass.cpp \
wallet/coincontrol.cpp \ wallet/coincontrol.cpp \
wallet/crypter.cpp \ wallet/crypter.cpp \
wallet/db.cpp \ wallet/db.cpp \

View File

@ -23,7 +23,6 @@ public:
// Dash Specific WalletInitInterface InitCoinJoinSettings // Dash Specific WalletInitInterface InitCoinJoinSettings
void AutoLockMasternodeCollaterals() const override {} void AutoLockMasternodeCollaterals() const override {}
void InitCoinJoinSettings() const override {} void InitCoinJoinSettings() const override {}
void InitKeePass() const override {}
bool InitAutoBackup() const override {return true;} bool InitAutoBackup() const override {return true;}
}; };
@ -55,11 +54,6 @@ void DummyWalletInit::AddWalletOptions() const
"-mnemonic=<text>", "-mnemonic=<text>",
"-mnemonicpassphrase=<text>", "-mnemonicpassphrase=<text>",
"-usehd", "-usehd",
"-keepass",
"-keepassid=<id>",
"-keepasskey=<key>",
"-keepassname=<name>",
"-keepassport=<port>",
"-enablecoinjoin", "-enablecoinjoin",
"-coinjoinamount=<n>", "-coinjoinamount=<n>",
"-coinjoinautostart", "-coinjoinautostart",

View File

@ -1757,8 +1757,6 @@ bool AppInitMain(InitInterfaces& interfaces)
} }
} }
// Initialize KeePass Integration
g_wallet_init_interface.InitKeePass();
// ********************************************************* Step 6: network initialization // ********************************************************* Step 6: network initialization
// Note that we absolutely cannot open any actual connections // Note that we absolutely cannot open any actual connections
// until the very end ("start node") as the UTXO/block state // until the very end ("start node") as the UTXO/block state

View File

@ -1,568 +0,0 @@
// Copyright (c) 2014-2020 The Dash Core developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <keepass.h>
#include <wallet/crypter.h>
#include <clientversion.h>
#include <protocol.h>
#include <random.h>
#include <rpc/protocol.h>
// Necessary to prevent compile errors due to forward declaration of
//CScript in serialize.h (included from crypter.h)
#include <script/script.h>
#include <script/standard.h>
#include <util/system.h>
#include <util/strencodings.h>
#include <event2/event.h>
#include <event2/http.h>
#include <event2/buffer.h>
#include <event2/keyvalq_struct.h>
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/buffer.h>
#include <support/cleanse.h> // for OPENSSL_cleanse()
const char* CKeePassIntegrator::KEEPASS_HTTP_HOST = "localhost";
CKeePassIntegrator keePassInt;
// Base64 decoding with secure memory allocation
SecureString DecodeBase64Secure(const SecureString& sInput)
{
SecureString output;
// Init openssl BIO with base64 filter and memory input
BIO *b64, *mem;
b64 = BIO_new(BIO_f_base64());
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); //Do not use newlines to flush buffer
mem = BIO_new_mem_buf((void *) sInput.data(), sInput.size());
BIO_push(b64, mem);
// Prepare buffer to receive decoded data
if(sInput.size() % 4 != 0) {
throw std::runtime_error("Input length should be a multiple of 4");
}
size_t nMaxLen = sInput.size() / 4 * 3; // upper bound, guaranteed divisible by 4
output.resize(nMaxLen);
// Decode the string
size_t nLen;
nLen = BIO_read(b64, (void *) output.data(), sInput.size());
output.resize(nLen);
// Free memory
BIO_free_all(b64);
return output;
}
// Base64 encoding with secure memory allocation
SecureString EncodeBase64Secure(const SecureString& sInput)
{
// Init openssl BIO with base64 filter and memory output
BIO *b64, *mem;
b64 = BIO_new(BIO_f_base64());
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); // No newlines in output
mem = BIO_new(BIO_s_mem());
BIO_push(b64, mem);
// Decode the string
BIO_write(b64, sInput.data(), sInput.size());
(void) BIO_flush(b64);
// Create output variable from buffer mem ptr
BUF_MEM *bptr;
BIO_get_mem_ptr(b64, &bptr);
SecureString output(bptr->data, bptr->length);
// Cleanse secure data buffer from memory
memory_cleanse((void *) bptr->data, bptr->length);
// Free memory
BIO_free_all(b64);
return output;
}
CKeePassIntegrator::CKeePassIntegrator()
:sKeyBase64(" "), sKey(" "), sUrl(" ") // Prevent LockedPageManagerBase complaints
{
sKeyBase64.clear(); // Prevent LockedPageManagerBase complaints
sKey.clear(); // Prevent LockedPageManagerBase complaints
sUrl.clear(); // Prevent LockedPageManagerBase complaints
bIsActive = false;
nPort = DEFAULT_KEEPASS_HTTP_PORT;
}
// Initialize from application context
void CKeePassIntegrator::init()
{
bIsActive = gArgs.GetBoolArg("-keepass", false);
nPort = gArgs.GetArg("-keepassport", DEFAULT_KEEPASS_HTTP_PORT);
sKeyBase64 = SecureString(gArgs.GetArg("-keepasskey", "").c_str());
strKeePassId = gArgs.GetArg("-keepassid", "");
strKeePassEntryName = gArgs.GetArg("-keepassname", "");
// Convert key if available
if(sKeyBase64.size() > 0)
{
sKey = DecodeBase64Secure(sKeyBase64);
}
// Construct url if available
if(strKeePassEntryName.size() > 0)
{
sUrl = SecureString("http://");
sUrl += SecureString(strKeePassEntryName.c_str());
sUrl += SecureString("/");
}
}
void CKeePassIntegrator::CKeePassRequest::addStrParameter(const std::string& strName, const std::string& strValue)
{
requestObj.pushKV(strName, strValue);
}
void CKeePassIntegrator::CKeePassRequest::addStrParameter(const std::string& strName, const SecureString& sValue)
{
std::string sCipherValue;
if(!EncryptAES256(sKey, sValue, strIV, sCipherValue))
{
throw std::runtime_error("Unable to encrypt Verifier");
}
addStrParameter(strName, EncodeBase64(sCipherValue));
}
std::string CKeePassIntegrator::CKeePassRequest::getJson()
{
return requestObj.write();
}
void CKeePassIntegrator::CKeePassRequest::init()
{
SecureString sIVSecure = generateRandomKey(KEEPASS_CRYPTO_BLOCK_SIZE);
strIV = std::string(sIVSecure.data(), sIVSecure.size());
// Generate Nonce, Verifier and RequestType
SecureString sNonceBase64Secure = EncodeBase64Secure(sIVSecure);
addStrParameter("Nonce", std::string(sNonceBase64Secure.data(), sNonceBase64Secure.size())); // Plain
addStrParameter("Verifier", sNonceBase64Secure); // Encoded
addStrParameter("RequestType", strType);
}
void CKeePassIntegrator::CKeePassResponse::parseResponse(const std::string& strResponse)
{
UniValue responseValue;
if(!responseValue.read(strResponse))
{
throw std::runtime_error("Unable to parse KeePassHttp response");
}
responseObj = responseValue;
// retrieve main values
bSuccess = responseObj["Success"].get_bool();
strType = getStr("RequestType");
strIV = DecodeBase64(getStr("Nonce"));
}
std::string CKeePassIntegrator::CKeePassResponse::getStr(const std::string& strName)
{
return responseObj[strName].get_str();
}
SecureString CKeePassIntegrator::CKeePassResponse::getSecureStr(const std::string& strName)
{
std::string strValueBase64Encrypted(responseObj[strName].get_str());
SecureString sValue;
try
{
sValue = decrypt(strValueBase64Encrypted);
}
catch (std::exception &e)
{
std::string strError = "Exception occurred while decrypting ";
strError += strName + ": " + e.what();
throw std::runtime_error(strError);
}
return sValue;
}
SecureString CKeePassIntegrator::CKeePassResponse::decrypt(const std::string& strValueBase64Encrypted)
{
std::string strValueEncrypted = DecodeBase64(strValueBase64Encrypted);
SecureString sValue;
if(!DecryptAES256(sKey, strValueEncrypted, strIV, sValue))
{
throw std::runtime_error("Unable to decrypt value.");
}
return sValue;
}
std::vector<CKeePassIntegrator::CKeePassEntry> CKeePassIntegrator::CKeePassResponse::getEntries()
{
std::vector<CKeePassEntry> vEntries;
UniValue aEntries = responseObj["Entries"].get_array();
for(size_t i = 0; i < aEntries.size(); i++)
{
SecureString sEntryUuid(decrypt(aEntries[i]["Uuid"].get_str().c_str()));
SecureString sEntryName(decrypt(aEntries[i]["Name"].get_str().c_str()));
SecureString sEntryLogin(decrypt(aEntries[i]["Login"].get_str().c_str()));
SecureString sEntryPassword(decrypt(aEntries[i]["Password"].get_str().c_str()));
CKeePassEntry entry(sEntryUuid, sEntryName, sEntryLogin, sEntryPassword);
vEntries.push_back(entry);
}
return vEntries;
}
SecureString CKeePassIntegrator::generateRandomKey(size_t nSize)
{
// Generates random key
SecureString sKey;
sKey.resize(nSize);
GetStrongRandBytes((unsigned char *) sKey.data(), nSize);
return sKey;
}
// Construct POST body for RPC JSON call
std::string CKeePassIntegrator::constructHTTPPost(const std::string& strMsg, const std::map<std::string,std::string>& mapRequestHeaders)
{
std::ostringstream streamOut;
streamOut << "POST / HTTP/1.1\r\n"
<< "User-Agent: dash-json-rpc/" << FormatFullVersion() << "\r\n"
<< "Host: localhost\r\n"
<< "Content-Type: application/json\r\n"
<< "Content-Length: " << strMsg.size() << "\r\n"
<< "Connection: close\r\n"
<< "Accept: application/json\r\n";
for (const auto& item : mapRequestHeaders)
streamOut << item.first << ": " << item.second << "\r\n";
streamOut << "\r\n" << strMsg;
return streamOut.str();
}
/** Reply structure for request_done to fill in */
struct HTTPReply
{
int nStatus;
std::string strBody;
};
static void http_request_done(struct evhttp_request *req, void *ctx)
{
HTTPReply *reply = static_cast<HTTPReply*>(ctx);
if (req == nullptr) {
/* If req is nullptr, it means an error occurred while connecting, but
* I'm not sure how to find out which one. We also don't really care.
*/
reply->nStatus = 0;
return;
}
reply->nStatus = evhttp_request_get_response_code(req);
struct evbuffer *buf = evhttp_request_get_input_buffer(req);
if (buf)
{
size_t size = evbuffer_get_length(buf);
const char *data = (const char*)evbuffer_pullup(buf, size);
if (data)
reply->strBody = std::string(data, size);
evbuffer_drain(buf, size);
}
}
// Send RPC message to KeePassHttp
void CKeePassIntegrator::doHTTPPost(const std::string& sRequest, int& nStatusRet, std::string& strResponseRet)
{
// Create event base
struct event_base *base = event_base_new(); // TODO RAII
if (!base)
throw std::runtime_error("cannot create event_base");
// Synchronously look up hostname
struct evhttp_connection *evcon = evhttp_connection_base_new(base, nullptr, KEEPASS_HTTP_HOST, DEFAULT_KEEPASS_HTTP_PORT); // TODO RAII
if (evcon == nullptr)
throw std::runtime_error("create connection failed");
evhttp_connection_set_timeout(evcon, KEEPASS_HTTP_CONNECT_TIMEOUT);
HTTPReply response;
struct evhttp_request *req = evhttp_request_new(http_request_done, (void*)&response); // TODO RAII
if (req == nullptr)
throw std::runtime_error("create http request failed");
struct evkeyvalq *output_headers = evhttp_request_get_output_headers(req);
assert(output_headers);
evhttp_add_header(output_headers, "User-Agent", ("dash-json-rpc/" + FormatFullVersion()).c_str());
evhttp_add_header(output_headers, "Host", KEEPASS_HTTP_HOST);
evhttp_add_header(output_headers, "Accept", "application/json");
evhttp_add_header(output_headers, "Content-Type", "application/json");
evhttp_add_header(output_headers, "Connection", "close");
LogPrint(BCLog::KEEPASS, "CKeePassIntegrator::doHTTPPost -- send POST data\n");
struct evbuffer * output_buffer = evhttp_request_get_output_buffer(req);
assert(output_buffer);
evbuffer_add(output_buffer, sRequest.data(), sRequest.size());
int r = evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/");
if (r != 0) {
evhttp_connection_free(evcon);
event_base_free(base);
throw std::runtime_error("send http request failed");
}
event_base_dispatch(base);
evhttp_connection_free(evcon);
event_base_free(base);
nStatusRet = response.nStatus;
if (response.nStatus == 0)
throw std::runtime_error("couldn't connect to server");
else if (response.nStatus >= 400 && response.nStatus != HTTP_BAD_REQUEST && response.nStatus != HTTP_NOT_FOUND && response.nStatus != HTTP_INTERNAL_SERVER_ERROR)
throw std::runtime_error(strprintf("server returned HTTP error %d", response.nStatus));
else if (response.strBody.empty())
throw std::runtime_error("no response from server");
// Parse reply
UniValue valReply(UniValue::VSTR);
if (!valReply.read(response.strBody))
throw std::runtime_error("couldn't parse reply from server");
const UniValue& reply = valReply.get_obj();
if (reply.empty())
throw std::runtime_error("expected reply to have result, error and id properties");
strResponseRet = valReply.get_str();
}
void CKeePassIntegrator::rpcTestAssociation(bool bTriggerUnlock)
{
CKeePassRequest request(sKey, "test-associate");
request.addStrParameter("TriggerUnlock", std::string(bTriggerUnlock ? "true" : "false"));
int nStatus;
std::string strResponse;
doHTTPPost(request.getJson(), nStatus, strResponse);
LogPrint(BCLog::KEEPASS, "CKeePassIntegrator::rpcTestAssociation -- send result: status: %d response: %s\n", nStatus, strResponse);
}
std::vector<CKeePassIntegrator::CKeePassEntry> CKeePassIntegrator::rpcGetLogins()
{
// Convert key format
SecureString sKey = DecodeBase64Secure(sKeyBase64);
CKeePassRequest request(sKey, "get-logins");
request.addStrParameter("addStrParameter", std::string("true"));
request.addStrParameter("TriggerUnlock", std::string("true"));
request.addStrParameter("Id", strKeePassId);
request.addStrParameter("Url", sUrl);
int nStatus;
std::string strResponse;
doHTTPPost(request.getJson(), nStatus, strResponse);
LogPrint(BCLog::KEEPASS, "CKeePassIntegrator::rpcGetLogins -- send result: status: %d\n", nStatus);
if(nStatus != 200)
{
std::string strError = "Error returned by KeePassHttp: HTTP code ";
strError += itostr(nStatus);
strError += " - Response: ";
strError += " response: [";
strError += strResponse;
strError += "]";
throw std::runtime_error(strError);
}
// Parse the response
CKeePassResponse response(sKey, strResponse);
if(!response.getSuccess())
{
std::string strError = "KeePassHttp returned failure status";
throw std::runtime_error(strError);
}
return response.getEntries();
}
void CKeePassIntegrator::rpcSetLogin(const SecureString& sWalletPass, const SecureString& sEntryId)
{
// Convert key format
SecureString sKey = DecodeBase64Secure(sKeyBase64);
CKeePassRequest request(sKey, "set-login");
request.addStrParameter("Id", strKeePassId);
request.addStrParameter("Url", sUrl);
LogPrint(BCLog::KEEPASS, "CKeePassIntegrator::rpcSetLogin -- send Url: %s\n", sUrl);
//request.addStrParameter("SubmitUrl", sSubmitUrl); // Is used to construct the entry title
request.addStrParameter("Login", SecureString("dash"));
request.addStrParameter("Password", sWalletPass);
if(sEntryId.size() != 0)
{
request.addStrParameter("Uuid", sEntryId); // Update existing
}
int nStatus;
std::string strResponse;
doHTTPPost(request.getJson(), nStatus, strResponse);
LogPrint(BCLog::KEEPASS, "CKeePassIntegrator::rpcSetLogin -- send result: status: %d response: %s\n", nStatus, strResponse);
if(nStatus != 200)
{
std::string strError = "Error returned: HTTP code ";
strError += itostr(nStatus);
strError += " - Response: ";
strError += " response: [";
strError += strResponse;
strError += "]";
throw std::runtime_error(strError);
}
// Parse the response
CKeePassResponse response(sKey, strResponse);
if(!response.getSuccess())
{
throw std::runtime_error("KeePassHttp returned failure status");
}
}
SecureString CKeePassIntegrator::generateKeePassKey()
{
SecureString sKey = generateRandomKey(KEEPASS_CRYPTO_KEY_SIZE);
SecureString sKeyBase64 = EncodeBase64Secure(sKey);
return sKeyBase64;
}
void CKeePassIntegrator::rpcAssociate(std::string& strIdRet, SecureString& sKeyBase64Ret)
{
sKey = generateRandomKey(KEEPASS_CRYPTO_KEY_SIZE);
CKeePassRequest request(sKey, "associate");
sKeyBase64Ret = EncodeBase64Secure(sKey);
request.addStrParameter("Key", std::string(sKeyBase64Ret.data(), sKeyBase64Ret.size()));
int nStatus;
std::string strResponse;
doHTTPPost(request.getJson(), nStatus, strResponse);
LogPrint(BCLog::KEEPASS, "CKeePassIntegrator::rpcAssociate -- send result: status: %d response: %s\n", nStatus, strResponse);
if(nStatus != 200)
{
std::string strError = "Error returned: HTTP code ";
strError += itostr(nStatus);
strError += " - Response: ";
strError += " response: [";
strError += strResponse;
strError += "]";
throw std::runtime_error(strError);
}
// Parse the response
CKeePassResponse response(sKey, strResponse);
if(!response.getSuccess())
{
throw std::runtime_error("KeePassHttp returned failure status");
}
// If we got here, we were successful. Return the information
strIdRet = response.getStr("Id");
}
// Retrieve wallet passphrase from KeePass
SecureString CKeePassIntegrator::retrievePassphrase()
{
// Check we have all required information
if(sKey.size() == 0)
{
throw std::runtime_error("keepasskey parameter is not defined. Please specify the configuration parameter.");
}
if(strKeePassId.size() == 0)
{
throw std::runtime_error("keepassid parameter is not defined. Please specify the configuration parameter.");
}
if(strKeePassEntryName == "")
{
throw std::runtime_error("keepassname parameter is not defined. Please specify the configuration parameter.");
}
// Retrieve matching logins from KeePass
std::vector<CKeePassIntegrator::CKeePassEntry> vecEntries = rpcGetLogins();
// Only accept one unique match
if(vecEntries.size() == 0)
{
throw std::runtime_error("KeePassHttp returned 0 matches, please verify the keepassurl setting.");
}
if(vecEntries.size() > 1)
{
throw std::runtime_error("KeePassHttp returned multiple matches, bailing out.");
}
return vecEntries[0].getPassword();
}
// Update wallet passphrase in keepass
void CKeePassIntegrator::updatePassphrase(const SecureString& sWalletPassphrase)
{
// Check we have all required information
if(sKey.size() == 0)
{
throw std::runtime_error("keepasskey parameter is not defined. Please specify the configuration parameter.");
}
if(strKeePassId.size() == 0)
{
throw std::runtime_error("keepassid parameter is not defined. Please specify the configuration parameter.");
}
if(strKeePassEntryName == "")
{
throw std::runtime_error("keepassname parameter is not defined. Please specify the configuration parameter.");
}
SecureString sEntryId("");
std::string strError;
// Lookup existing entry
std::vector<CKeePassIntegrator::CKeePassEntry> vecEntries = rpcGetLogins();
if(vecEntries.size() > 1)
{
throw std::runtime_error("KeePassHttp returned multiple matches, bailing out.");
}
if(vecEntries.size() == 1)
{
sEntryId = vecEntries[0].getUuid();
}
// Update wallet passphrase in KeePass
rpcSetLogin(sWalletPassphrase, sEntryId);
}

View File

@ -1,133 +0,0 @@
// Copyright (c) 2014-2020 The Dash Core developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_KEEPASS_H
#define BITCOIN_KEEPASS_H
#include <support/allocators/secure.h>
#include <univalue.h>
class CKeePassIntegrator;
static const unsigned int DEFAULT_KEEPASS_HTTP_PORT = 19455;
extern CKeePassIntegrator keePassInt;
class CKeePassIntegrator {
private:
static const int KEEPASS_CRYPTO_KEY_SIZE = 32;
static const int KEEPASS_CRYPTO_BLOCK_SIZE = 16;
static const int KEEPASS_HTTP_CONNECT_TIMEOUT = 30;
static const char* KEEPASS_HTTP_HOST;
bool bIsActive;
unsigned int nPort;
SecureString sKeyBase64;
SecureString sKey;
SecureString sUrl;
//SecureString sSubmitUrl;
std::string strKeePassId;
std::string strKeePassEntryName;
class CKeePassRequest {
UniValue requestObj;
std::string strType;
std::string strIV;
SecureString sKey;
void init();
public:
void addStrParameter(const std::string& strName, const std::string& strValue); // Regular
void addStrParameter(const std::string& strName, const SecureString& sValue); // Encrypt
std::string getJson();
CKeePassRequest(const SecureString& sKey, const std::string& strType)
{
this->sKey = sKey;
this->strType = strType;
init();
};
};
class CKeePassEntry {
SecureString sUuid;
SecureString sName;
SecureString sLogin;
SecureString sPassword;
public:
CKeePassEntry(const SecureString& sUuid, const SecureString& sName, const SecureString& sLogin, const SecureString& sPassword) :
sUuid(sUuid), sName(sName), sLogin(sLogin), sPassword(sPassword) {
}
SecureString getUuid() {
return sUuid;
}
SecureString getName() {
return sName;
}
SecureString getLogin() {
return sLogin;
}
SecureString getPassword() {
return sPassword;
}
};
class CKeePassResponse {
bool bSuccess;
std::string strType;
std::string strIV;
SecureString sKey;
void parseResponse(const std::string& strResponse);
public:
UniValue responseObj;
CKeePassResponse(const SecureString& sKey, const std::string& strResponse) {
this->sKey = sKey;
parseResponse(strResponse);
}
bool getSuccess() {
return bSuccess;
}
SecureString getSecureStr(const std::string& strName);
std::string getStr(const std::string& strName);
std::vector<CKeePassEntry> getEntries();
SecureString decrypt(const std::string& strValue); // DecodeBase64 and decrypt arbitrary string value
};
static SecureString generateRandomKey(size_t nSize);
static std::string constructHTTPPost(const std::string& strMsg, const std::map<std::string,std::string>& mapRequestHeaders);
void doHTTPPost(const std::string& strRequest, int& nStatusRet, std::string& strResponseRet);
void rpcTestAssociation(bool bTriggerUnlock);
std::vector<CKeePassEntry> rpcGetLogins();
void rpcSetLogin(const SecureString& sWalletPass, const SecureString& sEntryId);
public:
CKeePassIntegrator();
void init();
static SecureString generateKeePassKey();
void rpcAssociate(std::string& strIdRet, SecureString& sKeyBase64Ret);
SecureString retrievePassphrase();
void updatePassphrase(const SecureString& sWalletPassphrase);
};
#endif // BITCOIN_KEEPASS_H

View File

@ -164,7 +164,6 @@ const CLogCategoryDesc LogCategories[] =
{BCLog::CHAINLOCKS, "chainlocks"}, {BCLog::CHAINLOCKS, "chainlocks"},
{BCLog::GOBJECT, "gobject"}, {BCLog::GOBJECT, "gobject"},
{BCLog::INSTANTSEND, "instantsend"}, {BCLog::INSTANTSEND, "instantsend"},
{BCLog::KEEPASS, "keepass"},
{BCLog::LLMQ, "llmq"}, {BCLog::LLMQ, "llmq"},
{BCLog::LLMQ_DKG, "llmq-dkg"}, {BCLog::LLMQ_DKG, "llmq-dkg"},
{BCLog::LLMQ_SIGS, "llmq-sigs"}, {BCLog::LLMQ_SIGS, "llmq-sigs"},

View File

@ -61,7 +61,6 @@ namespace BCLog {
CHAINLOCKS = ((uint64_t)1 << 32), CHAINLOCKS = ((uint64_t)1 << 32),
GOBJECT = ((uint64_t)1 << 33), GOBJECT = ((uint64_t)1 << 33),
INSTANTSEND = ((uint64_t)1 << 34), INSTANTSEND = ((uint64_t)1 << 34),
KEEPASS = ((uint64_t)1 << 35),
LLMQ = ((uint64_t)1 << 36), LLMQ = ((uint64_t)1 << 36),
LLMQ_DKG = ((uint64_t)1 << 37), LLMQ_DKG = ((uint64_t)1 << 37),
LLMQ_SIGS = ((uint64_t)1 << 38), LLMQ_SIGS = ((uint64_t)1 << 38),
@ -71,7 +70,7 @@ namespace BCLog {
SPORK = ((uint64_t)1 << 42), SPORK = ((uint64_t)1 << 42),
NETCONN = ((uint64_t)1 << 43), NETCONN = ((uint64_t)1 << 43),
DASH = CHAINLOCKS | GOBJECT | INSTANTSEND | KEEPASS | LLMQ | LLMQ_DKG DASH = CHAINLOCKS | GOBJECT | INSTANTSEND | LLMQ | LLMQ_DKG
| LLMQ_SIGS | MNPAYMENTS | MNSYNC | COINJOIN | SPORK | NETCONN, | LLMQ_SIGS | MNPAYMENTS | MNSYNC | COINJOIN | SPORK | NETCONN,
NET_NETCONN = NET | NETCONN, // use this to have something logged in NET and NETCONN as well NET_NETCONN = NET | NETCONN, // use this to have something logged in NET and NETCONN as well

View File

@ -647,9 +647,6 @@ std::string ArgsManager::GetHelpMessage() const
case OptionsCategory::WALLET_HD: case OptionsCategory::WALLET_HD:
usage += HelpMessageGroup("HD wallet options:"); usage += HelpMessageGroup("HD wallet options:");
break; break;
case OptionsCategory::WALLET_KEEPASS:
usage += HelpMessageGroup("KeePass options:");
break;
case OptionsCategory::WALLET_COINJOIN: case OptionsCategory::WALLET_COINJOIN:
usage += HelpMessageGroup("CoinJoin options:"); usage += HelpMessageGroup("CoinJoin options:");
break; break;

View File

@ -140,7 +140,6 @@ enum class OptionsCategory {
WALLET, WALLET,
WALLET_FEE, WALLET_FEE,
WALLET_HD, WALLET_HD,
WALLET_KEEPASS,
WALLET_COINJOIN, WALLET_COINJOIN,
WALLET_DEBUG_TEST, WALLET_DEBUG_TEST,
ZMQ, ZMQ,

View File

@ -5,7 +5,6 @@
#include <init.h> #include <init.h>
#include <interfaces/chain.h> #include <interfaces/chain.h>
#include <keepass.h>
#include <net.h> #include <net.h>
#include <scheduler.h> #include <scheduler.h>
#include <util/error.h> #include <util/error.h>
@ -36,7 +35,6 @@ public:
// Dash Specific Wallet Init // Dash Specific Wallet Init
void AutoLockMasternodeCollaterals() const override; void AutoLockMasternodeCollaterals() const override;
void InitCoinJoinSettings() const override; void InitCoinJoinSettings() const override;
void InitKeePass() const override;
bool InitAutoBackup() const override; bool InitAutoBackup() const override;
}; };
@ -80,12 +78,6 @@ void WalletInit::AddWalletOptions() const
gArgs.AddArg("-mnemonicpassphrase=<text>", "User defined mnemonic passphrase for HD wallet (BIP39). Only has effect during wallet creation/first start (default: empty string)", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET_HD); gArgs.AddArg("-mnemonicpassphrase=<text>", "User defined mnemonic passphrase for HD wallet (BIP39). Only has effect during wallet creation/first start (default: empty string)", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET_HD);
gArgs.AddArg("-usehd", strprintf("Use hierarchical deterministic key generation (HD) after BIP39/BIP44. Only has effect during wallet creation/first start (default: %u)", DEFAULT_USE_HD_WALLET), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET_HD); gArgs.AddArg("-usehd", strprintf("Use hierarchical deterministic key generation (HD) after BIP39/BIP44. Only has effect during wallet creation/first start (default: %u)", DEFAULT_USE_HD_WALLET), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET_HD);
gArgs.AddArg("-keepass", strprintf("Use KeePass 2 integration using KeePassHttp plugin (default: %u)", 0), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET_KEEPASS);
gArgs.AddArg("-keepassid=<id>", "KeePassHttp id for the established association", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET_KEEPASS);
gArgs.AddArg("-keepasskey=<key>", "KeePassHttp key for AES encrypted communication with KeePass", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET_KEEPASS);
gArgs.AddArg("-keepassname=<name>", "Name to construct url for KeePass entry that stores the wallet passphrase", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET_KEEPASS);
gArgs.AddArg("-keepassport=<port>", strprintf("Connect to KeePassHttp on port <port> (default: %u)", DEFAULT_KEEPASS_HTTP_PORT), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET_KEEPASS);
gArgs.AddArg("-enablecoinjoin", strprintf("Enable use of CoinJoin for funds stored in this wallet (0-1, default: %u)", 0), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET_COINJOIN); gArgs.AddArg("-enablecoinjoin", strprintf("Enable use of CoinJoin for funds stored in this wallet (0-1, default: %u)", 0), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET_COINJOIN);
gArgs.AddArg("-coinjoinamount=<n>", strprintf("Target CoinJoin balance (%u-%u, default: %u)", MIN_COINJOIN_AMOUNT, MAX_COINJOIN_AMOUNT, DEFAULT_COINJOIN_AMOUNT), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET_COINJOIN); gArgs.AddArg("-coinjoinamount=<n>", strprintf("Target CoinJoin balance (%u-%u, default: %u)", MIN_COINJOIN_AMOUNT, MAX_COINJOIN_AMOUNT, DEFAULT_COINJOIN_AMOUNT), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET_COINJOIN);
gArgs.AddArg("-coinjoinautostart", strprintf("Start CoinJoin automatically (0-1, default: %u)", DEFAULT_COINJOIN_AUTOSTART), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET_COINJOIN); gArgs.AddArg("-coinjoinautostart", strprintf("Start CoinJoin automatically (0-1, default: %u)", DEFAULT_COINJOIN_AUTOSTART), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET_COINJOIN);
@ -291,11 +283,6 @@ void WalletInit::InitCoinJoinSettings() const
CCoinJoinClientOptions::GetDenomsHardCap()); CCoinJoinClientOptions::GetDenomsHardCap());
} }
void WalletInit::InitKeePass() const
{
keePassInt.init();
}
bool WalletInit::InitAutoBackup() const bool WalletInit::InitAutoBackup() const
{ {
return CWallet::InitAutoBackup(); return CWallet::InitAutoBackup();

View File

@ -11,7 +11,6 @@
#include <httpserver.h> #include <httpserver.h>
#include <init.h> #include <init.h>
#include <interfaces/chain.h> #include <interfaces/chain.h>
#include <keepass.h>
#include <policy/feerate.h> #include <policy/feerate.h>
#include <policy/fees.h> #include <policy/fees.h>
#include <rpc/mining.h> #include <rpc/mining.h>
@ -2819,61 +2818,6 @@ static UniValue upgradetohd(const JSONRPCRequest& request)
return true; return true;
} }
static UniValue keepass(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet* const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
return NullUniValue;
std::string strCommand;
if (!request.params[0].isNull())
strCommand = request.params[0].get_str();
if (request.fHelp ||
(strCommand != "genkey" && strCommand != "init" && strCommand != "setpassphrase"))
throw std::runtime_error(
"keepass <genkey|init|setpassphrase>\n");
if (strCommand == "genkey")
{
SecureString sResult;
// Generate RSA key
SecureString sKey = CKeePassIntegrator::generateKeePassKey();
sResult = "Generated Key: ";
sResult += sKey;
return sResult.c_str();
}
else if(strCommand == "init")
{
// Generate base64 encoded 256 bit RSA key and associate with KeePassHttp
SecureString sResult;
SecureString sKey;
std::string strId;
keePassInt.rpcAssociate(strId, sKey);
sResult = "Association successful. Id: ";
sResult += strId.c_str();
sResult += " - Key: ";
sResult += sKey.c_str();
return sResult.c_str();
}
else if(strCommand == "setpassphrase")
{
if(request.params.size() != 2) {
return "setlogin: invalid number of parameters. Requires a passphrase";
}
SecureString sPassphrase = SecureString(request.params[1].get_str().c_str());
keePassInt.updatePassphrase(sPassphrase);
return "setlogin: Updated credentials.";
}
return "Invalid command";
}
static UniValue loadwallet(const JSONRPCRequest& request) static UniValue loadwallet(const JSONRPCRequest& request)
{ {
if (request.fHelp || request.params.size() != 1) if (request.fHelp || request.params.size() != 1)
@ -4247,7 +4191,6 @@ static const CRPCCommand commands[] =
{ "wallet", "importprunedfunds", &importprunedfunds, {"rawtransaction","txoutproof"} }, { "wallet", "importprunedfunds", &importprunedfunds, {"rawtransaction","txoutproof"} },
{ "wallet", "importpubkey", &importpubkey, {"pubkey","label","rescan"} }, { "wallet", "importpubkey", &importpubkey, {"pubkey","label","rescan"} },
{ "wallet", "importwallet", &importwallet, {"filename"} }, { "wallet", "importwallet", &importwallet, {"filename"} },
{ "wallet", "keepass", &keepass, {} },
{ "wallet", "keypoolrefill", &keypoolrefill, {"newsize"} }, { "wallet", "keypoolrefill", &keypoolrefill, {"newsize"} },
{ "wallet", "listaddressbalances", &listaddressbalances, {"minamount"} }, { "wallet", "listaddressbalances", &listaddressbalances, {"minamount"} },
{ "wallet", "listaddressgroupings", &listaddressgroupings, {} }, { "wallet", "listaddressgroupings", &listaddressgroupings, {} },

View File

@ -42,7 +42,6 @@
#include <coinjoin/options.h> #include <coinjoin/options.h>
#include <governance/governance.h> #include <governance/governance.h>
#include <evo/deterministicmns.h> #include <evo/deterministicmns.h>
#include <keepass.h>
#include <evo/providertx.h> #include <evo/providertx.h>
@ -599,23 +598,9 @@ bool CWallet::LoadWatchOnly(const CScript &dest)
bool CWallet::Unlock(const SecureString& strWalletPassphrase, bool fForMixingOnly, bool accept_no_keys) bool CWallet::Unlock(const SecureString& strWalletPassphrase, bool fForMixingOnly, bool accept_no_keys)
{ {
SecureString strWalletPassphraseFinal;
if (!IsLocked()) // was already fully unlocked, not only for mixing if (!IsLocked()) // was already fully unlocked, not only for mixing
return true; return true;
// Verify KeePassIntegration
if (strWalletPassphrase == "keepass" && gArgs.GetBoolArg("-keepass", false)) {
try {
strWalletPassphraseFinal = keePassInt.retrievePassphrase();
} catch (std::exception& e) {
WalletLogPrintf("CWallet::Unlock could not retrieve passphrase from KeePass: Error: %s\n", e.what());
return false;
}
} else {
strWalletPassphraseFinal = strWalletPassphrase;
}
CCrypter crypter; CCrypter crypter;
CKeyingMaterial _vMasterKey; CKeyingMaterial _vMasterKey;
@ -623,7 +608,7 @@ bool CWallet::Unlock(const SecureString& strWalletPassphrase, bool fForMixingOnl
LOCK(cs_wallet); LOCK(cs_wallet);
for (const MasterKeyMap::value_type& pMasterKey : mapMasterKeys) for (const MasterKeyMap::value_type& pMasterKey : mapMasterKeys)
{ {
if (!crypter.SetKeyFromPassphrase(strWalletPassphraseFinal, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod)) if (!crypter.SetKeyFromPassphrase(strWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod))
return false; return false;
if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, _vMasterKey)) if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, _vMasterKey))
continue; // try another master key continue; // try another master key
@ -643,22 +628,6 @@ bool CWallet::Unlock(const SecureString& strWalletPassphrase, bool fForMixingOnl
bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase) bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase)
{ {
bool fWasLocked = IsLocked(true); bool fWasLocked = IsLocked(true);
bool bUseKeePass = false;
SecureString strOldWalletPassphraseFinal;
// Verify KeePassIntegration
if(strOldWalletPassphrase == "keepass" && gArgs.GetBoolArg("-keepass", false)) {
bUseKeePass = true;
try {
strOldWalletPassphraseFinal = keePassInt.retrievePassphrase();
} catch (std::exception& e) {
WalletLogPrintf("CWallet::ChangeWalletPassphrase -- could not retrieve passphrase from KeePass: Error: %s\n", e.what());
return false;
}
} else {
strOldWalletPassphraseFinal = strOldWalletPassphrase;
}
{ {
LOCK(cs_wallet); LOCK(cs_wallet);
@ -668,7 +637,7 @@ bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase,
CKeyingMaterial _vMasterKey; CKeyingMaterial _vMasterKey;
for (MasterKeyMap::value_type& pMasterKey : mapMasterKeys) for (MasterKeyMap::value_type& pMasterKey : mapMasterKeys)
{ {
if(!crypter.SetKeyFromPassphrase(strOldWalletPassphraseFinal, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod)) if(!crypter.SetKeyFromPassphrase(strOldWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod))
return false; return false;
if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, _vMasterKey)) if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, _vMasterKey))
return false; return false;
@ -695,17 +664,6 @@ bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase,
if (fWasLocked) if (fWasLocked)
Lock(); Lock();
// Update KeePass if necessary
if(bUseKeePass) {
WalletLogPrintf("CWallet::ChangeWalletPassphrase -- Updating KeePass with new passphrase\n");
try {
keePassInt.updatePassphrase(strNewWalletPassphrase);
} catch (std::exception& e) {
WalletLogPrintf("CWallet::ChangeWalletPassphrase -- could not update passphrase in KeePass: Error: %s\n", e.what());
return false;
}
}
return true; return true;
} }
} }
@ -980,16 +938,6 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
// bits of the unencrypted private key in slack space in the database file. // bits of the unencrypted private key in slack space in the database file.
database->Rewrite(); database->Rewrite();
// Update KeePass if necessary
if(gArgs.GetBoolArg("-keepass", false)) {
WalletLogPrintf("CWallet::EncryptWallet -- Updating KeePass with new passphrase\n");
try {
keePassInt.updatePassphrase(strWalletPassphrase);
} catch (std::exception& e) {
WalletLogPrintf("CWallet::EncryptWallet -- could not update passphrase in KeePass: Error: %s\n", e.what());
}
}
// BDB seems to have a bad habit of writing old data into // BDB seems to have a bad habit of writing old data into
// slack space in .dat files; that is bad if the old data is // slack space in .dat files; that is bad if the old data is
// unencrypted private keys. So: // unencrypted private keys. So:

View File

@ -21,7 +21,6 @@ public:
// Dash Specific WalletInitInterface // Dash Specific WalletInitInterface
virtual void AutoLockMasternodeCollaterals() const = 0; virtual void AutoLockMasternodeCollaterals() const = 0;
virtual void InitCoinJoinSettings() const = 0; virtual void InitCoinJoinSettings() const = 0;
virtual void InitKeePass() const = 0;
virtual bool InitAutoBackup() const = 0; virtual bool InitAutoBackup() const = 0;
virtual ~WalletInitInterface() {} virtual ~WalletInitInterface() {}