Add malleable keys support.
author0xDEADFACE <masmfan@gmail.com>
Thu, 11 Feb 2016 00:06:38 +0000 (16:06 -0800)
committer0xDEADFACE <masmfan@gmail.com>
Thu, 11 Feb 2016 00:06:38 +0000 (16:06 -0800)
This feature may be useful for coin mixers and some other applications. Please see doc/MalleableKeys.txt for examples.

doc/MalleableKeys.txt [new file with mode: 0644]
src/bitcoinrpc.cpp
src/bitcoinrpc.h
src/key.cpp
src/key.h
src/rpcwallet.cpp

diff --git a/doc/MalleableKeys.txt b/doc/MalleableKeys.txt
new file mode 100644 (file)
index 0000000..357ef95
--- /dev/null
@@ -0,0 +1,34 @@
+> newmalleablekey
+{
+"PrivatePair" : "DfAmbLTs7VDvUor5YF1gU5K5CGsHWezFh9htgiJBbmd3Z48H2Cu3NpYjSDQ1qaKvMGctkCgyqLCiEJFNCEBNxhhwLpquXDAa",
+"PublicPair" : "5GRSi9q1XUVBYxifTr5D5bQUKFvyfWc8GFH8eLUqsE9fMYRtn2WhnNjNFpbaZBrypP3E9jMUYGwya7UM3Vfs8UqkqWTxiut4rKU"
+}
+
+> adjustmalleablepubkey 5GRSi9q1XUVBYxifTr5D5bQUKFvyfWc8GFH8eLUqsE9fMYRtn2WhnNjNFpbaZBrypP3E9jMUYGwya7UM3Vfs8UqkqWTxiut4rKU
+{
+"R" : "022f010598ec5687f719edce97f6ba7e32fc34c7d7b45ba49de115bb670210f59f",
+"PubkeyVariant" : "03105ec5214ce118828cfb08a349b04f336ed4eae50995dbe5268112748008fdf7",
+"KeyVariantID" : "4UcaTGRRNWiH2xs6tTFHwtVzjS1BZrMLrB"
+}
+
+> adjustmalleablekey DfAmbLTs7VDvUor5YF1gU5K5CGsHWezFh9htgiJBbmd3Z48H2Cu3NpYjSDQ1qaKvMGctkCgyqLCiEJFNCEBNxhhwLpquXDAa 03105ec5214ce118828cfb08a349b04f336ed4eae50995dbe5268112748008fdf7 022f010598ec5687f719edce97f6ba7e32fc34c7d7b45ba49de115bb670210f59f
+{
+"PrivateKey" : "MDhzYBXJHrkaoDVSnJjaxzHVYURyAgVzqEhft2oeMhyfww1pDW6K"
+}
+
+> importprivkey MDhzYBXJHrkaoDVSnJjaxzHVYURyAgVzqEhft2oeMhyfww1pDW6K "test"
+
+> validateaddress 4UcaTGRRNWiH2xs6tTFHwtVzjS1BZrMLrB
+{
+"isvalid" : true,
+"address" : "4UcaTGRRNWiH2xs6tTFHwtVzjS1BZrMLrB",
+"ismine" : true,
+"watchonly" : false,
+"isscript" : false,
+"pubkey" : "03105ec5214ce118828cfb08a349b04f336ed4eae50995dbe5268112748008fdf7",
+"iscompressed" : true,
+"account" : "test"
+}
+
+> dumpprivkey 4UcaTGRRNWiH2xs6tTFHwtVzjS1BZrMLrB
+MDhzYBXJHrkaoDVSnJjaxzHVYURyAgVzqEhft2oeMhyfww1pDW6K
index b10cee7..719aef0 100644 (file)
@@ -316,6 +316,9 @@ static const CRPCCommand vRPCCommands[] =
     { "repairwallet",           &repairwallet,           false,  true},
     { "resendtx",               &resendtx,               false,  true},
     { "makekeypair",            &makekeypair,            false,  true},
+    { "newmalleablekey",        &newmalleablekey,        false,  false},
+    { "adjustmalleablekey",     &adjustmalleablekey,     false,  false},
+    { "adjustmalleablepubkey",  &adjustmalleablepubkey,  false,  false},
     { "sendalert",              &sendalert,              false,  false},
 };
 
index dcf0863..3b0e199 100644 (file)
@@ -206,6 +206,9 @@ extern json_spirit::Value repairwallet(const json_spirit::Array& params, bool fH
 extern json_spirit::Value resendtx(const json_spirit::Array& params, bool fHelp);
 extern json_spirit::Value makekeypair(const json_spirit::Array& params, bool fHelp);
 extern json_spirit::Value mergecoins(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value newmalleablekey(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value adjustmalleablekey(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value adjustmalleablepubkey(const json_spirit::Array& params, bool fHelp);
 
 extern json_spirit::Value getrawtransaction(const json_spirit::Array& params, bool fHelp); // in rcprawtransaction.cpp
 extern json_spirit::Value listunspent(const json_spirit::Array& params, bool fHelp);
index e95fb17..adafc32 100644 (file)
@@ -8,6 +8,7 @@
 #include <openssl/obj_mac.h>
 
 #include "key.h"
+#include "base58.h"
 
 // Generate a private key from just the secret parameter
 int EC_KEY_regenerate_key(EC_KEY *eckey, BIGNUM *priv_key)
@@ -532,3 +533,562 @@ bool CKey::IsValid()
     key2.SetSecret(secret, fCompr);
     return GetPubKey() == key2.GetPubKey();
 }
+
+CPoint::CPoint()
+{
+    std::string err;
+    group = NULL;
+    point = NULL;
+    ctx   = NULL;
+
+    group = EC_GROUP_new_by_curve_name(NID_secp256k1);
+    if (!group) {
+        err = "EC_KEY_new_by_curve_name failed.";
+        goto finish;
+    }
+
+    point = EC_POINT_new(group);
+    if (!point) {
+        err = "EC_POINT_new failed.";
+        goto finish;
+    }
+
+    ctx = BN_CTX_new();
+    if (!ctx) {
+        err = "BN_CTX_new failed.";
+        goto finish;
+    }
+
+    return;
+
+finish:
+    if (group) EC_GROUP_free(group);
+    if (point) EC_POINT_free(point);
+    throw std::runtime_error(std::string("CPoint::CPoint() :  - ") + err);
+}
+
+bool CPoint::operator!=(const CPoint &a)
+{
+    if (EC_POINT_cmp(group, point, a.point, ctx) != 0)
+        return true;
+    return false;
+}
+CPoint::~CPoint()
+{
+    if (point) EC_POINT_free(point);
+    if (group) EC_GROUP_free(group);
+    if (ctx)   BN_CTX_free(ctx);
+}
+
+// Initialize from octets stream
+bool CPoint::setBytes(const std::vector<unsigned char> &vchBytes)
+{
+    if (!EC_POINT_oct2point(group, point, &vchBytes[0], vchBytes.size(), ctx)) {
+        return false;
+    }
+    return true;
+}
+
+// Serialize to octets stream
+bool CPoint::getBytes(std::vector<unsigned char> &vchBytes)
+{
+    unsigned int nSize = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED, NULL, 0, ctx);
+    vchBytes.resize(nSize);
+    if (!(nSize == EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED, &vchBytes[0], nSize, ctx))) {
+        return false;
+    }
+    return true;
+}
+
+// ECC multiplication by specified multiplier
+bool CPoint::ECMUL(const CBigNum &bnMultiplier)
+{
+    if (!EC_POINT_mul(group, point, NULL, point, &bnMultiplier, NULL)) {
+        printf("CPoint::ECMUL() : EC_POINT_mul failed");
+        return false;
+    }
+
+    return true;
+}
+
+// Calculate G*m + q
+bool CPoint::ECMULGEN(const CBigNum &bnMultiplier, const CPoint &qPoint)
+{
+    if (!EC_POINT_mul(group, point, &bnMultiplier, qPoint.point, BN_value_one(), NULL)) {
+        printf("CPoint::ECMULGEN() : EC_POINT_mul failed.");
+        return false;
+    }
+
+    return true;
+}
+
+// CMalleablePubKey
+
+void CMalleablePubKey::GetVariant(CPubKey &R, CPubKey &vchPubKeyVariant)
+{
+    EC_KEY *eckey = NULL;
+    eckey = EC_KEY_new_by_curve_name(NID_secp256k1);
+    if (eckey == NULL) {
+        throw key_error("CMalleablePubKey::GetVariant() : EC_KEY_new_by_curve_name failed");
+    }
+
+    // Use standard key generation function to get r and R values.
+    //
+    // r will be presented by private key;
+    // R is ECDSA public key which calculated as G*r
+    if (!EC_KEY_generate_key(eckey)) {
+        throw key_error("CMalleablePubKey::GetVariant() : EC_KEY_generate_key failed");
+    }
+
+    EC_KEY_set_conv_form(eckey, POINT_CONVERSION_COMPRESSED);
+
+    int nSize = i2o_ECPublicKey(eckey, NULL);
+    if (!nSize) {
+        throw key_error("CMalleablePubKey::GetVariant() : i2o_ECPublicKey failed");
+    }
+
+    std::vector<unsigned char> vchPubKey(nSize, 0);
+    unsigned char* pbegin_R = &vchPubKey[0];
+
+    if (i2o_ECPublicKey(eckey, &pbegin_R) != nSize) {
+        throw key_error("CMalleablePubKey::GetVariant() : i2o_ECPublicKey returned unexpected size");
+    }
+
+    // R = G*r
+    R = CPubKey(vchPubKey);
+
+    // OpenSSL BIGNUM representation of r value
+    CBigNum bnr;
+    bnr = *(CBigNum*) EC_KEY_get0_private_key(eckey);
+    EC_KEY_free(eckey);
+
+    CPoint point;
+    if (!point.setBytes(pubKeyL.Raw())) {
+        throw key_error("CMalleablePubKey::GetVariant() : Unable to decode L value");
+    }
+
+    // Calculate L*r
+    point.ECMUL(bnr);
+
+    std::vector<unsigned char> vchLr;
+    if (!point.getBytes(vchLr)) {
+        throw key_error("CMalleablePubKey::GetVariant() : Unable to convert Lr value");
+    }
+
+    // Calculate Hash(L*r) and then get a BIGNUM representation of hash value.
+    CBigNum bnHash;
+    bnHash.setuint160(Hash160(vchLr));
+
+    CPoint pointH;
+    pointH.setBytes(pubKeyH.Raw());
+
+    CPoint P;
+    // Calculate P = Hash(L*r)*G + H
+    P.ECMULGEN(bnHash, pointH);
+
+    if (P.IsInfinity()) {
+        throw key_error("CMalleablePubKey::GetVariant() : P is infinity");
+    }
+
+    std::vector<unsigned char> vchResult;
+    P.getBytes(vchResult);
+
+    vchPubKeyVariant = CPubKey(vchResult);
+}
+
+std::string CMalleablePubKey::ToString()
+{
+    CDataStream ssKey(SER_NETWORK, PROTOCOL_VERSION);
+    ssKey << *this;
+    std::vector<unsigned char> vch(ssKey.begin(), ssKey.end());
+
+    return EncodeBase58Check(vch);
+}
+
+bool CMalleablePubKey::SetString(const std::string& strMalleablePubKey)
+{
+    std::vector<unsigned char> vchTemp;
+    if (!DecodeBase58Check(strMalleablePubKey, vchTemp)) {
+        throw key_error("CMalleablePubKey::SetString() : Provided key data seems corrupted.");
+    }
+
+    CDataStream ssKey(vchTemp, SER_NETWORK, PROTOCOL_VERSION);
+    ssKey >> *this;
+
+    return IsValid();
+}
+
+bool CMalleablePubKey::operator==(const CMalleablePubKey &b)
+{
+    return (nVersion == b.nVersion &&
+            pubKeyL == b.pubKeyL &&
+            pubKeyH == b.pubKeyH);
+}
+
+
+// CMalleableKey
+
+void CMalleableKey::Reset()
+{
+    vchSecretL.clear();
+    vchSecretH.clear();
+
+    nVersion = 0;
+}
+
+void CMalleableKey::MakeNewKeys()
+{
+    CKey L, H;
+    bool fCompressed = true;
+
+    L.MakeNewKey(true);
+    H.MakeNewKey(true);
+
+    vchSecretL = L.GetSecret(fCompressed);
+    vchSecretH = H.GetSecret(fCompressed);
+
+    nVersion = CURRENT_VERSION;
+}
+
+CMalleableKey::CMalleableKey()
+{
+    Reset();
+}
+
+CMalleableKey::CMalleableKey(const CMalleableKey &b)
+{
+    SetSecrets(b.vchSecretL, b.vchSecretH);
+}
+
+CMalleableKey::CMalleableKey(const CSecret &L, const CSecret &H)
+{
+    SetSecrets(L, H);
+}
+
+CMalleableKey& CMalleableKey::operator=(const CMalleableKey &b)
+{
+    SetSecrets(b.vchSecretL, b.vchSecretH);
+
+    return (*this);
+}
+
+CMalleableKey::~CMalleableKey()
+{
+}
+
+bool CMalleableKey::IsNull() const
+{
+    return nVersion != CURRENT_VERSION;
+}
+
+bool CMalleableKey::SetSecrets(const CSecret &pvchSecretL, const CSecret &pvchSecretH)
+{
+    Reset();
+    CKey L, H;
+
+    if (!L.SetSecret(pvchSecretL, true) || !H.SetSecret(pvchSecretH, true))
+    {
+        nVersion = 0;
+        return false;
+    }
+
+    vchSecretL = pvchSecretL;
+    vchSecretH = pvchSecretH;
+    nVersion = CURRENT_VERSION;
+
+    return true;
+}
+
+void CMalleableKey::GetSecrets(CSecret &pvchSecretL, CSecret &pvchSecretH) const
+{
+    pvchSecretL = vchSecretL;
+    pvchSecretH = vchSecretH;
+}
+
+CMalleablePubKey CMalleableKey::GetMalleablePubKey() const
+{
+    CKey L, H;
+    L.SetSecret(vchSecretL, true);
+    H.SetSecret(vchSecretH, true);
+
+    std::vector<unsigned char> vchPubKeyL = L.GetPubKey().Raw();
+    std::vector<unsigned char> vchPubKeyH = H.GetPubKey().Raw();
+
+    return CMalleablePubKey(vchPubKeyL, vchPubKeyH);
+}
+
+// Check ownership
+bool CMalleableKey::CheckKeyVariant(const CPubKey &R, const CPubKey &vchPubKeyVariant)
+{
+    if (IsNull()) {
+        throw key_error("CMalleableKey::CheckKeyVariant() : Attempting to run on NULL key object.");
+    }
+
+    if (!R.IsValid()) {
+        throw key_error("CMalleableKey::CheckKeyVariant() : R is invalid");
+    }
+
+    if (!vchPubKeyVariant.IsValid()) {
+        throw key_error("CMalleableKey::CheckKeyVariant() : public key variant is invalid");
+    }
+
+    CPoint point_R;
+    if (!point_R.setBytes(R.Raw())) {
+        throw key_error("CMalleableKey::CheckKeyVariant() : Unable to decode R value");
+    }
+
+    CKey H;
+    H.SetSecret(vchSecretH, true);
+    std::vector<unsigned char> vchPubKeyH = H.GetPubKey().Raw();
+
+    CPoint point_H;
+    if (!point_H.setBytes(vchPubKeyH)) {
+        throw key_error("CMalleableKey::CheckKeyVariant() : Unable to decode H value");
+    }
+
+    CPoint point_P;
+    if (!point_P.setBytes(vchPubKeyVariant.Raw())) {
+        throw key_error("CMalleableKey::CheckKeyVariant() : Unable to decode P value");
+    }
+
+    // Infinity points are senseless
+    if (point_P.IsInfinity()) {
+        throw key_error("CMalleableKey::CheckKeyVariant() : P is infinity");
+    }
+
+    CBigNum bnl;
+    bnl.setBytes(std::vector<unsigned char>(vchSecretL.begin(), vchSecretL.end()));
+
+    point_R.ECMUL(bnl);
+
+    std::vector<unsigned char> vchRl;
+    if (!point_R.getBytes(vchRl)) {
+        throw key_error("CMalleableKey::CheckKeyVariant() : Unable to convert Rl value");
+    }
+
+    // Calculate Hash(R*l)
+    CBigNum bnHash;
+    bnHash.setuint160(Hash160(vchRl));
+
+    CPoint point_Ps;
+    // Calculate Ps = Hash(L*r)*G + H
+    point_Ps.ECMULGEN(bnHash, point_H);
+
+    // Infinity points are senseless
+    if (point_Ps.IsInfinity()) {
+        throw key_error("CMalleableKey::CheckKeyVariant() : Ps is infinity");
+    }
+
+    // Check ownership
+    if (point_Ps != point_P) {
+        return false;
+    }
+
+    return true;
+}
+
+// Check ownership and restore private key
+bool CMalleableKey::CheckKeyVariant(const CPubKey &R, const CPubKey &vchPubKeyVariant, CKey &privKeyVariant)
+{
+    if (IsNull()) {
+        throw key_error("CMalleableKey::CheckKeyVariant() : Attempting to run on NULL key object.");
+    }
+
+    if (!R.IsValid()) {
+        throw key_error("CMalleableKey::CheckKeyVariant() : R is invalid");
+    }
+
+    if (!vchPubKeyVariant.IsValid()) {
+        throw key_error("CMalleableKey::CheckKeyVariant() : public key variant is invalid");
+    }
+
+    CPoint point_R;
+    if (!point_R.setBytes(R.Raw())) {
+        throw key_error("CMalleableKey::CheckKeyVariant() : Unable to decode R value");
+    }
+
+    CKey H;
+    H.SetSecret(vchSecretH, true);
+    std::vector<unsigned char> vchPubKeyH = H.GetPubKey().Raw();
+
+    CPoint point_H;
+    if (!point_H.setBytes(vchPubKeyH)) {
+        throw key_error("CMalleableKey::CheckKeyVariant() : Unable to decode H value");
+    }
+
+    CPoint point_P;
+    if (!point_P.setBytes(vchPubKeyVariant.Raw())) {
+        throw key_error("CMalleableKey::CheckKeyVariant() : Unable to decode P value");
+    }
+
+    // Infinity points are senseless
+    if (point_P.IsInfinity()) {
+        throw key_error("CMalleableKey::CheckKeyVariant() : P is infinity");
+    }
+
+    CBigNum bnl;
+    bnl.setBytes(std::vector<unsigned char>(vchSecretL.begin(), vchSecretL.end()));
+
+    point_R.ECMUL(bnl);
+
+    std::vector<unsigned char> vchRl;
+    if (!point_R.getBytes(vchRl)) {
+        throw key_error("CMalleableKey::CheckKeyVariant() : Unable to convert Rl value");
+    }
+
+    // Calculate Hash(R*l)
+    CBigNum bnHash;
+    bnHash.setuint160(Hash160(vchRl));
+
+    CPoint point_Ps;
+    // Calculate Ps = Hash(L*r)*G + H
+    point_Ps.ECMULGEN(bnHash, point_H);
+
+    // Infinity points are senseless
+    if (point_Ps.IsInfinity()) {
+        throw key_error("CMalleableKey::CheckKeyVariant() : Ps is infinity");
+    }
+
+    // Check ownership
+    if (point_Ps != point_P) {
+        return false;
+    }
+
+    // OpenSSL BIGNUM representation of the second private key from (l, h) pair
+    CBigNum bnh;
+    bnh.setBytes(std::vector<unsigned char>(vchSecretH.begin(), vchSecretH.end()));
+
+    // Calculate p = Hash(R*l) + h
+    CBigNum bnp = bnHash + bnh;
+
+    std::vector<unsigned char> vchp = bnp.getBytes();
+    privKeyVariant.SetSecret(CSecret(vchp.begin(), vchp.end()), true);
+
+    return true;
+}
+
+std::string CMalleableKey::ToString()
+{
+    CDataStream ssKey(SER_NETWORK, PROTOCOL_VERSION);
+    ssKey << *this;
+    std::vector<unsigned char> vch(ssKey.begin(), ssKey.end());
+
+    return EncodeBase58Check(vch);
+}
+
+bool CMalleableKey::SetString(const std::string& strMutableKey)
+{
+    std::vector<unsigned char> vchTemp;
+    if (!DecodeBase58Check(strMutableKey, vchTemp)) {
+        throw key_error("CMalleableKey::SetString() : Provided key data seems corrupted.");
+    }
+
+    CDataStream ssKey(vchTemp, SER_NETWORK, PROTOCOL_VERSION);
+    ssKey >> *this;
+
+    return IsNull();
+}
+
+// CMalleableKeyView
+
+CMalleableKeyView::CMalleableKeyView(const CMalleableKey &b)
+{
+    assert(b.nVersion == CURRENT_VERSION);
+    vchSecretL = b.vchSecretL;
+
+    CKey H;
+    H.SetSecret(b.vchSecretH, true);
+    vchPubKeyH = H.GetPubKey().Raw();
+}
+
+CMalleableKeyView::CMalleableKeyView(const CSecret &L, const CPubKey &pvchPubKeyH)
+{
+    vchSecretL = L;
+    vchPubKeyH = pvchPubKeyH.Raw();
+}
+
+CMalleableKeyView& CMalleableKeyView::operator=(const CMalleableKey &b)
+{
+    assert(b.nVersion == CURRENT_VERSION);
+    vchSecretL = b.vchSecretL;
+
+    CKey H;
+    H.SetSecret(b.vchSecretH, true);
+    vchPubKeyH = H.GetPubKey().Raw();
+
+    return (*this);
+}
+
+CMalleableKeyView::~CMalleableKeyView()
+{
+}
+
+CMalleablePubKey CMalleableKeyView::GetMalleablePubKey() const
+{
+    CKey keyL;
+    keyL.SetSecret(vchSecretL, true);
+    return CMalleablePubKey(keyL.GetPubKey(), vchPubKeyH);
+}
+
+// Check ownership
+bool CMalleableKeyView::CheckKeyVariant(const CPubKey &R, const CPubKey &vchPubKeyVariant)
+{
+    if (!R.IsValid()) {
+        throw key_error("CMalleableKeyView::CheckKeyVariant() : R is invalid");
+    }
+
+    if (!vchPubKeyVariant.IsValid()) {
+        throw key_error("CMalleableKeyView::CheckKeyVariant() : public key variant is invalid");
+    }
+
+    CPoint point_R;
+    if (!point_R.setBytes(R.Raw())) {
+        throw key_error("CMalleableKeyView::CheckKeyVariant() : Unable to decode R value");
+    }
+
+    CPoint point_H;
+    if (!point_H.setBytes(vchPubKeyH)) {
+        throw key_error("CMalleableKeyView::CheckKeyVariant() : Unable to decode H value");
+    }
+
+    CPoint point_P;
+    if (!point_P.setBytes(vchPubKeyVariant.Raw())) {
+        throw key_error("CMalleableKeyView::CheckKeyVariant() : Unable to decode P value");
+    }
+
+    // Infinity points are senseless
+    if (point_P.IsInfinity()) {
+        throw key_error("CMalleableKeyView::CheckKeyVariant() : P is infinity");
+    }
+
+    CBigNum bnl;
+    bnl.setBytes(std::vector<unsigned char>(vchSecretL.begin(), vchSecretL.end()));
+
+    point_R.ECMUL(bnl);
+
+    std::vector<unsigned char> vchRl;
+    if (!point_R.getBytes(vchRl)) {
+        throw key_error("CMalleableKeyView::CheckKeyVariant() : Unable to convert Rl value");
+    }
+
+    // Calculate Hash(R*l)
+    CBigNum bnHash;
+    bnHash.setuint160(Hash160(vchRl));
+
+    CPoint point_Ps;
+    // Calculate Ps = Hash(L*r)*G + H
+    point_Ps.ECMULGEN(bnHash, point_H);
+
+    // Infinity points are senseless
+    if (point_Ps.IsInfinity()) {
+        throw key_error("CMalleableKeyView::CheckKeyVariant() : Ps is infinity");
+    }
+
+    // Check ownership
+    if (point_Ps != point_P) {
+        return false;
+    }
+
+    return true;
+}
+
index e329cf0..c189090 100644 (file)
--- a/src/key.h
+++ b/src/key.h
@@ -166,4 +166,134 @@ public:
     static bool ReserealizeSignature(std::vector<unsigned char>& vchSig);
 };
 
+class CPoint
+{
+private:
+    EC_POINT *point;
+    EC_GROUP* group;
+    BN_CTX* ctx;
+
+public:
+    CPoint();
+    bool operator!=(const CPoint &a);
+    ~CPoint();
+
+    // Initialize from octets stream
+    bool setBytes(const std::vector<unsigned char> &vchBytes);
+
+    // Serialize to octets stream
+    bool getBytes(std::vector<unsigned char> &vchBytes);
+
+    // ECC multiplication by specified multiplier
+    bool ECMUL(const CBigNum &bnMultiplier);
+
+    // Calculate G*m + q
+    bool ECMULGEN(const CBigNum &bnMultiplier, const CPoint &qPoint);
+
+    bool IsInfinity() { return EC_POINT_is_at_infinity(group, point); }
+};
+
+class CMalleablePubKey
+{
+private:
+    unsigned char nVersion;
+    CPubKey pubKeyL;
+    CPubKey pubKeyH;
+    friend class CMalleableKey;
+
+    static const unsigned char CURRENT_VERSION = 1;
+
+public:
+    CMalleablePubKey() { nVersion = CMalleablePubKey::CURRENT_VERSION; }
+    CMalleablePubKey(const std::string& strMalleablePubKey) { SetString(strMalleablePubKey); }
+    CMalleablePubKey(const CPubKey &pubKeyInL, const CPubKey &pubKeyInH) : pubKeyL(pubKeyInL), pubKeyH(pubKeyInH) { nVersion = CMalleablePubKey::CURRENT_VERSION; }
+    CMalleablePubKey(const std::vector<unsigned char> &pubKeyInL, const std::vector<unsigned char> &pubKeyInH) : pubKeyL(pubKeyInL), pubKeyH(pubKeyInH) { nVersion = CMalleablePubKey::CURRENT_VERSION; }
+
+    IMPLEMENT_SERIALIZE(
+        READWRITE(this->nVersion);
+        nVersion = this->nVersion;
+        READWRITE(pubKeyL);
+        READWRITE(pubKeyH);
+    )
+
+    bool IsValid() const {
+        return pubKeyL.IsValid() && pubKeyH.IsValid();
+    }
+
+    bool operator==(const CMalleablePubKey &b);
+    bool operator!=(const CMalleablePubKey &b) { return !(*this == b); }
+
+    std::string ToString();
+    bool SetString(const std::string& strMalleablePubKey);
+    uint256 GetID() const;
+
+    CPubKey& GetL() { return pubKeyL; }
+    CPubKey& GetH() { return pubKeyH; }
+    void GetVariant(CPubKey &R, CPubKey &vchPubKeyVariant);
+};
+
+class CMalleableKey
+{
+private:
+    unsigned char nVersion;
+    CSecret vchSecretL;
+    CSecret vchSecretH;
+
+    friend class CMalleableKeyView;
+
+    static const unsigned char CURRENT_VERSION = 1;
+
+public:
+    CMalleableKey();
+    CMalleableKey(const CMalleableKey &b);
+    CMalleableKey(const CSecret &L, const CSecret &H);
+    CMalleableKey& operator=(const CMalleableKey &b);
+    ~CMalleableKey();
+
+    IMPLEMENT_SERIALIZE(
+        READWRITE(this->nVersion);
+        nVersion = this->nVersion;
+        READWRITE(vchSecretL);
+        READWRITE(vchSecretH);
+    )
+
+    std::string ToString();
+    bool SetString(const std::string& strMalleablePubKey);
+
+    void Reset();
+    void MakeNewKeys();
+    bool IsNull() const;
+    bool SetSecrets(const CSecret &pvchSecretL, const CSecret &pvchSecretH);
+    void GetSecrets(CSecret &pvchSecretL, CSecret &pvchSecretH) const;
+
+    CMalleablePubKey GetMalleablePubKey() const;
+
+    bool CheckKeyVariant(const CPubKey &R, const CPubKey &vchPubKeyVariant);
+    bool CheckKeyVariant(const CPubKey &R, const CPubKey &vchPubKeyVariant, CKey &privKeyVariant);
+};
+
+class CMalleableKeyView
+{
+private:
+    CSecret vchSecretL;
+    std::vector<unsigned char> vchPubKeyH;
+
+    // disabled constructor
+    CMalleableKeyView() { };
+
+    static const unsigned char CURRENT_VERSION = 1;
+
+public:
+    CMalleableKeyView(const CMalleableKey &b);
+    CMalleableKeyView(const CSecret &L, const CPubKey &pvchPubKeyH);
+
+    CMalleableKeyView(const CMalleableKeyView &b);
+    CMalleableKeyView& operator=(const CMalleableKey &b);
+    ~CMalleableKeyView();
+
+    CMalleablePubKey GetMalleablePubKey() const;
+
+    bool CheckKeyVariant(const CPubKey &R, const CPubKey &vchPubKeyVariant);
+};
+
 #endif
index 60ef03e..ad20062 100644 (file)
@@ -1833,25 +1833,94 @@ Value resendtx(const Array& params, bool fHelp)
     return Value::null;
 }
 
-// ppcoin: make a public-private key pair
+// Make a public-private key pair
 Value makekeypair(const Array& params, bool fHelp)
 {
-    if (fHelp || params.size() > 1)
+    if (fHelp || params.size() > 0)
         throw runtime_error(
-            "makekeypair [prefix]\n"
-            "Make a public/private key pair.\n"
-            "[prefix] is optional preferred prefix for the public key.\n");
+            "makekeypair\n"
+            "Make a public/private key pair.\n");
 
     string strPrefix = "";
     if (params.size() > 0)
         strPrefix = params[0].get_str();
+
     CKey key;
-    key.MakeNewKey(false);
+    key.MakeNewKey(true);
 
     CPrivKey vchPrivKey = key.GetPrivKey();
     Object result;
     result.push_back(Pair("PrivateKey", HexStr<CPrivKey::iterator>(vchPrivKey.begin(), vchPrivKey.end())));
+
+    bool fCompressed;
+    CSecret vchSecret = key.GetSecret(fCompressed);
+    result.push_back(Pair("Secret", HexStr<CSecret::iterator>(vchSecret.begin(), vchSecret.end())));
     result.push_back(Pair("PublicKey", HexStr(key.GetPubKey().Raw())));
     return result;
 }
+
+Value newmalleablekey(const Array& params, bool fHelp)
+{
+    if (fHelp || params.size() > 0)
+        throw runtime_error(
+            "newmalleablekey\n"
+            "Make a malleable public/private key pair.\n");
+
+    CMalleableKey malleableKey;
+    malleableKey.MakeNewKeys();
+    CMalleablePubKey malleablePubKey = malleableKey.GetMalleablePubKey();
+
+    Object result;
+    result.push_back(Pair("PrivatePair", malleableKey.ToString()));
+    result.push_back(Pair("PublicPair", malleablePubKey.ToString()));
+
+    return result;
+}
+
+Value adjustmalleablekey(const Array& params, bool fHelp)
+{
+    if (fHelp || params.size() != 3)
+        throw runtime_error(
+            "adjustmalleablekey <Malleable key data> <Public key variant data> <R data>\n"
+            "Calculate new private key using provided malleable key, public key and R data.\n");
+
+    CMalleableKey malleableKey;
+    malleableKey.SetString(params[0].get_str());
+
+    CKey privKeyVariant;
+    CPubKey vchPubKeyVariant = CPubKey(ParseHex(params[1].get_str()));
+    CPubKey vchR = CPubKey(ParseHex(params[2].get_str()));
+
+    if (!malleableKey.CheckKeyVariant(vchR,vchPubKeyVariant, privKeyVariant)) {
+        throw runtime_error("Unable to calculate the private key");
+    }
+
+    Object result;
+    bool fCompressed;
+    CSecret vchPrivKeyVariant = privKeyVariant.GetSecret(fCompressed);
+
+    result.push_back(Pair("PrivateKey", CBitcoinSecret(vchPrivKeyVariant, fCompressed).ToString()));
+
+    return result;
+}
+
+Value adjustmalleablepubkey(const Array& params, bool fHelp)
+{
+    if (fHelp || params.size() > 2 || params.size() == 0)
+        throw runtime_error(
+            "adjustmalleablepubkey <Malleable public key data>\n"
+            "Calculate new public key using provided malleable public key data.\n");
+
+    CMalleablePubKey malleablePubKey;
+    malleablePubKey.SetString(params[0].get_str());
+
+    CPubKey R, vchPubKeyVariant;
+    malleablePubKey.GetVariant(R, vchPubKeyVariant);
+
+    Object result;
+    result.push_back(Pair("R", HexStr(R.Raw())));
+    result.push_back(Pair("PubkeyVariant", HexStr(vchPubKeyVariant.Raw())));
+    result.push_back(Pair("KeyVariantID", CBitcoinAddress(vchPubKeyVariant.GetID()).ToString()));
+
+    return result;
+}