mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-19 18:45:52 +00:00
More work on the wallet/key scheme.
Call the public generator a public generator rather than a public key. Ignore private sub-keys greater than the EC group Bugfixes. When we create a new key family, validate the first 500 keys, just out of paranoia.
This commit is contained in:
@@ -134,14 +134,22 @@ EC_KEY* CKey::GenerateRootPubKey(const std::string& pubHex)
|
||||
return pkey;
|
||||
}
|
||||
|
||||
static BIGNUM* makeHash(const uint160& family, int seq)
|
||||
static BIGNUM* makeHash(const uint160& family, int seq, BIGNUM* order)
|
||||
{
|
||||
Serializer s;
|
||||
s.add160(family);
|
||||
s.add32(seq);
|
||||
uint256 root=s.getSHA512Half();
|
||||
s.secureErase();
|
||||
return BN_bin2bn((const unsigned char *) &root, sizeof(root), NULL);
|
||||
int subSeq=0;
|
||||
BIGNUM* ret=NULL;
|
||||
do
|
||||
{
|
||||
Serializer s(28);
|
||||
s.add160(family);
|
||||
s.add32(seq);
|
||||
s.add32(subSeq++);
|
||||
uint256 root=s.getSHA512Half();
|
||||
s.secureErase();
|
||||
ret=BN_bin2bn((const unsigned char *) &root, sizeof(root), ret);
|
||||
if(!ret) return NULL;
|
||||
} while (BN_is_zero(ret) || (BN_cmp(ret, order)>=0));
|
||||
return ret;
|
||||
}
|
||||
|
||||
EC_KEY* CKey::GeneratePublicDeterministicKey(const uint160& family, const EC_POINT* rootPubKey, int seq)
|
||||
@@ -164,8 +172,19 @@ EC_KEY* CKey::GeneratePublicDeterministicKey(const uint160& family, const EC_POI
|
||||
BN_CTX_free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
BIGNUM* hash=makeHash(family, seq);
|
||||
|
||||
BIGNUM* order=BN_new();
|
||||
if(!order || !EC_GROUP_get_order(EC_KEY_get0_group(pkey), order, ctx))
|
||||
{
|
||||
if(order) BN_free(order);
|
||||
EC_KEY_free(pkey);
|
||||
BN_CTX_free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// calculate the private additional key
|
||||
BIGNUM* hash=makeHash(family, seq, order);
|
||||
BN_free(order);
|
||||
if(hash==NULL)
|
||||
{
|
||||
EC_POINT_free(newPoint);
|
||||
@@ -174,15 +193,16 @@ EC_KEY* CKey::GeneratePublicDeterministicKey(const uint160& family, const EC_POI
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// calculate the corresponding public key
|
||||
EC_POINT_mul(EC_KEY_get0_group(pkey), newPoint, hash, NULL, NULL, ctx);
|
||||
BN_free(hash);
|
||||
|
||||
// add the master public key and set
|
||||
EC_POINT_add(EC_KEY_get0_group(pkey), newPoint, newPoint, rootPubKey, ctx);
|
||||
EC_KEY_set_public_key(pkey, newPoint);
|
||||
|
||||
EC_POINT_free(newPoint);
|
||||
BN_CTX_free(ctx);
|
||||
|
||||
return pkey;
|
||||
}
|
||||
|
||||
@@ -204,23 +224,52 @@ EC_KEY* CKey::GeneratePrivateDeterministicKey(const uint160& family, const BIGNU
|
||||
{
|
||||
BN_CTX_free(ctx);
|
||||
EC_KEY_free(pkey);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
EC_GROUP_get_order(EC_KEY_get0_group(pkey), order, ctx);
|
||||
|
||||
BIGNUM* privKey=makeHash(family, seq);
|
||||
if(!EC_GROUP_get_order(EC_KEY_get0_group(pkey), order, ctx))
|
||||
{
|
||||
BN_free(order);
|
||||
BN_CTX_free(ctx);
|
||||
EC_KEY_free(pkey);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// calculate the private additional key
|
||||
BIGNUM* privKey=makeHash(family, seq, order);
|
||||
if(privKey==NULL)
|
||||
{
|
||||
BN_free(order);
|
||||
BN_CTX_free(ctx);
|
||||
EC_KEY_free(pkey);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// calculate the final private key
|
||||
BN_mod_add(privKey, privKey, rootPrivKey, order, ctx);
|
||||
BN_free(order);
|
||||
|
||||
EC_KEY_set_private_key(pkey, privKey);
|
||||
|
||||
|
||||
// compute the corresponding public key
|
||||
EC_POINT* pubKey=EC_POINT_new(EC_KEY_get0_group(pkey));
|
||||
EC_POINT_mul(EC_KEY_get0_group(pkey), pubKey, privKey, NULL, NULL, ctx);
|
||||
if(!pubKey)
|
||||
{
|
||||
BN_free(privKey);
|
||||
BN_CTX_free(ctx);
|
||||
EC_KEY_free(pkey);
|
||||
return NULL;
|
||||
}
|
||||
if(EC_POINT_mul(EC_KEY_get0_group(pkey), pubKey, privKey, NULL, NULL, ctx)==0)
|
||||
{
|
||||
BN_free(privKey);
|
||||
BN_CTX_free(ctx);
|
||||
EC_KEY_free(pkey);
|
||||
return NULL;
|
||||
}
|
||||
BN_free(privKey);
|
||||
EC_KEY_set_public_key(pkey, pubKey);
|
||||
|
||||
EC_POINT_free(pubKey);
|
||||
BN_CTX_free(ctx);
|
||||
|
||||
return pkey;
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ Json::Value RPCServer::doCreateFamily(Json::Value& params)
|
||||
|
||||
ret["FamilyIdentifier"]=family.GetHex();
|
||||
ret["ShortName"]=theApp->getWallet().getShortName(family);
|
||||
ret["PublicGenerator"]=theApp->getWallet().getPubKeyHex(family);
|
||||
ret["PublicGenerator"]=theApp->getWallet().getPubGenHex(family);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -243,16 +243,16 @@ Json::Value RPCServer::doInfo(Json::Value ¶ms)
|
||||
family=theApp->getWallet().findFamilySN(fParam);
|
||||
if(!family) return JSONRPCError(500, "No such family");
|
||||
|
||||
std::string name, comment, pubKey;
|
||||
std::string name, comment, pubGen;
|
||||
bool isLocked;
|
||||
if(!theApp->getWallet().getFullFamilyInfo(family, name, comment, pubKey, isLocked))
|
||||
if(!theApp->getWallet().getFullFamilyInfo(family, name, comment, pubGen, isLocked))
|
||||
return JSONRPCError(500, "Family not found");
|
||||
Json::Value obj(Json::objectValue);
|
||||
obj["FamilyIdentifier"]=family.GetHex();
|
||||
obj["ShortName"]=name;
|
||||
if(!comment.empty())
|
||||
obj["Comment"]=comment;
|
||||
obj["PublicGenerator"]=pubKey;
|
||||
obj["PublicGenerator"]=pubGen;
|
||||
obj["Locked"]=isLocked ? "true" : "false";
|
||||
|
||||
if(paramCount==2)
|
||||
|
||||
@@ -77,12 +77,13 @@ CREATE TABLE LocalAcctFamilies ( -- a family of accounts that share a payphrase
|
||||
Comment TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE LocalAccounts ( -- an individual account
|
||||
CREATE TABLE LocalAccounts ( -- an individual account
|
||||
ID CHARACTER(40) PRIMARY KEY,
|
||||
DKID CHARACTER(40), -- root determinstic key
|
||||
DKSeq BIGINT UNSIGNED, -- sequence number
|
||||
KeyType CHARACTER(1) -- F=family
|
||||
PrivateKey TEXT, -- For F, FamilyName:Seq
|
||||
Seq BIGINT UNSIGNED, -- last transaction seen/issued
|
||||
Balance BIGINT UNSIGNED,
|
||||
LedgerSeq BIGINT UNSIGNED, -- ledger this balance is from
|
||||
Name TEXT,
|
||||
Comment TEXT
|
||||
);
|
||||
|
||||
73
Wallet.cpp
73
Wallet.cpp
@@ -10,22 +10,20 @@
|
||||
#include "NewcoinAddress.h"
|
||||
#include "Application.h"
|
||||
|
||||
// TEMPORARY
|
||||
#ifndef CHECK_NEW_FAMILIES
|
||||
#define CHECK_NEW_FAMILIES 500
|
||||
#endif
|
||||
|
||||
LocalAccountEntry::LocalAccountEntry(const uint160& accountFamily, int accountSeq, EC_POINT* rootPubKey) :
|
||||
mAccountFamily(accountFamily), mAccountSeq(accountSeq),
|
||||
mPublicKey(new CKey(accountFamily, rootPubKey, accountSeq)),
|
||||
mAccountFamily(accountFamily), mAccountSeq(accountSeq),
|
||||
mBalance(0), mLedgerSeq(0), mTxnSeq(0)
|
||||
{
|
||||
mAcctID=mPublicKey->GetAddress().GetHash160();
|
||||
if(theApp!=NULL) mPublicKey=theApp->getPubKeyCache().store(mAcctID, mPublicKey);
|
||||
}
|
||||
|
||||
void LocalAccountEntry::unlock(const BIGNUM* rootPrivKey)
|
||||
{
|
||||
if((mPrivateKey==NULL) && (rootPrivKey!=NULL))
|
||||
mPrivateKey=CKey::pointer(new CKey(mAccountFamily, rootPrivKey, mAccountSeq));
|
||||
}
|
||||
|
||||
std::string LocalAccountEntry::getAccountName() const
|
||||
{
|
||||
return mPublicKey->GetAddress().GetString();
|
||||
@@ -62,7 +60,30 @@ uint160 LocalAccountFamily::getAccount(int seq, bool keep)
|
||||
|
||||
void LocalAccountFamily::unlock(const BIGNUM* privateKey)
|
||||
{
|
||||
if(mRootPrivateKey!=NULL) mRootPrivateKey=BN_dup(privateKey);
|
||||
if(mRootPrivateKey==NULL) mRootPrivateKey=BN_dup(privateKey);
|
||||
|
||||
#ifdef CHECK_NEW_FAMILIES
|
||||
std::cerr << "CheckingFamily" << std::endl;
|
||||
for(int i=0; i<CHECK_NEW_FAMILIES; i++)
|
||||
{
|
||||
EC_KEY *pubkey=CKey::GeneratePublicDeterministicKey(mFamily, mRootPubKey, i);
|
||||
EC_KEY *privkey=CKey::GeneratePrivateDeterministicKey(mFamily, mRootPrivateKey, i);
|
||||
|
||||
int cmp=EC_POINT_cmp(EC_KEY_get0_group(pubkey), EC_KEY_get0_public_key(pubkey),
|
||||
EC_KEY_get0_public_key(privkey), NULL);
|
||||
if(cmp!=0)
|
||||
{
|
||||
std::cerr << BN_bn2hex(mRootPrivateKey) << std::endl;
|
||||
std::cerr << "family=" << mFamily.GetHex() << std::endl;
|
||||
std::cerr << "i=" << i << std::endl;
|
||||
std::cerr << "Key mismatch" << std::endl;
|
||||
assert(false);
|
||||
}
|
||||
|
||||
EC_KEY_free(pubkey);
|
||||
EC_KEY_free(privkey);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void LocalAccountFamily::lock()
|
||||
@@ -71,12 +92,16 @@ void LocalAccountFamily::lock()
|
||||
{
|
||||
BN_free(mRootPrivateKey);
|
||||
mRootPrivateKey=NULL;
|
||||
for(std::map<int, LocalAccountEntry::pointer>::iterator it=mAccounts.begin(); it!=mAccounts.end(); ++it)
|
||||
it->second->lock();
|
||||
}
|
||||
}
|
||||
|
||||
std::string LocalAccountFamily::getPubKeyHex() const
|
||||
CKey::pointer LocalAccountFamily::getPrivateKey(int seq)
|
||||
{
|
||||
if(!mRootPrivateKey) return CKey::pointer();
|
||||
return CKey::pointer(new CKey(mFamily, mRootPrivateKey, seq));
|
||||
}
|
||||
|
||||
std::string LocalAccountFamily::getPubGenHex() const
|
||||
{
|
||||
EC_GROUP *grp=EC_GROUP_new_by_curve_name(NID_secp256k1);
|
||||
if(!grp) return "";
|
||||
@@ -232,7 +257,7 @@ std::string LocalAccountFamily::getSQL() const
|
||||
std::string ret("('");
|
||||
ret.append(mFamily.GetHex());
|
||||
ret+="','";
|
||||
ret.append(getPubKeyHex());
|
||||
ret.append(getPubGenHex());
|
||||
ret.append("','");
|
||||
ret.append(boost::lexical_cast<std::string>(mLastSeq));
|
||||
ret.append("',");
|
||||
@@ -331,9 +356,7 @@ CKey::pointer LocalAccount::getPublicKey()
|
||||
|
||||
CKey::pointer LocalAccount::getPrivateKey()
|
||||
{
|
||||
LocalAccountEntry::pointer la(mFamily->get(mSeq));
|
||||
if(!la) return CKey::pointer();
|
||||
return la->getPrivKey();
|
||||
return mFamily->getPrivateKey(mSeq);
|
||||
}
|
||||
|
||||
void Wallet::getFamilies(std::vector<uint160>& familyIDs)
|
||||
@@ -354,14 +377,14 @@ bool Wallet::getFamilyInfo(const uint160& family, std::string& name, std::string
|
||||
}
|
||||
|
||||
bool Wallet::getFullFamilyInfo(const uint160& family, std::string& name, std::string& comment,
|
||||
std::string& pubKey, bool& isLocked)
|
||||
std::string& pubGen, bool& isLocked)
|
||||
{
|
||||
std::map<uint160, LocalAccountFamily::pointer>::iterator fit=families.find(family);
|
||||
if(fit==families.end()) return false;
|
||||
assert(fit->second->getFamily()==family);
|
||||
name=fit->second->getShortName();
|
||||
comment=fit->second->getComment();
|
||||
pubKey=fit->second->getPubKeyHex();
|
||||
pubGen=fit->second->getPubGenHex();
|
||||
isLocked=fit->second->isLocked();
|
||||
return true;
|
||||
}
|
||||
@@ -405,12 +428,12 @@ void Wallet::load()
|
||||
db->endIterRows();
|
||||
}
|
||||
|
||||
std::string Wallet::getPubKeyHex(const uint160& famBase)
|
||||
std::string Wallet::getPubGenHex(const uint160& famBase)
|
||||
{
|
||||
std::map<uint160, LocalAccountFamily::pointer>::iterator fit=families.find(famBase);
|
||||
if(fit==families.end()) return "";
|
||||
assert(fit->second->getFamily()==famBase);
|
||||
return fit->second->getPubKeyHex();
|
||||
return fit->second->getPubGenHex();
|
||||
}
|
||||
|
||||
std::string Wallet::getShortName(const uint160& famBase)
|
||||
@@ -550,23 +573,23 @@ bool Wallet::unitTest()
|
||||
std::cerr << "Priv: " << privBase.GetHex() << " Fam: " << fam.GetHex() << std::endl;
|
||||
#endif
|
||||
|
||||
std::string pubkey=priv.getPubKeyHex(fam);
|
||||
std::string pubGen=priv.getPubGenHex(fam);
|
||||
#ifdef DEBUG
|
||||
std::cerr << "Pub: " << pubkey << std::endl;
|
||||
std::cerr << "Pub: " << pubGen << std::endl;
|
||||
#endif
|
||||
|
||||
if(pub.addFamily(pubkey)!=fam)
|
||||
if(pub.addFamily(pubGen)!=fam)
|
||||
{
|
||||
assert(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(pub.getPubKeyHex(fam)!=pubkey)
|
||||
if(pub.getPubGenHex(fam)!=pubGen)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
std::cerr << std::endl;
|
||||
std::cerr << "PuK: " << pub.getPubKeyHex(fam) << std::endl;
|
||||
std::cerr << "PrK: " << priv.getPubKeyHex(fam) << std::endl;
|
||||
std::cerr << "PuK: " << pub.getPubGenHex(fam) << std::endl;
|
||||
std::cerr << "PrK: " << priv.getPubGenHex(fam) << std::endl;
|
||||
std::cerr << "Fam: " << fam.GetHex() << std::endl;
|
||||
#endif
|
||||
assert(false);
|
||||
|
||||
34
Wallet.h
34
Wallet.h
@@ -18,14 +18,14 @@ public:
|
||||
typedef boost::shared_ptr<LocalAccountEntry> pointer;
|
||||
|
||||
protected:
|
||||
// family informaiton
|
||||
uint160 mAccountFamily;
|
||||
int mAccountSeq;
|
||||
|
||||
// core account information
|
||||
CKey::pointer mPublicKey;
|
||||
CKey::pointer mPrivateKey;
|
||||
uint160 mAcctID;
|
||||
std::string mName, mComment;
|
||||
|
||||
// family information
|
||||
uint160 mAccountFamily;
|
||||
int mAccountSeq;
|
||||
|
||||
// local usage tracking
|
||||
uint64 mBalance; // The balance, last we checked/updated
|
||||
@@ -35,11 +35,11 @@ protected:
|
||||
public:
|
||||
LocalAccountEntry(const uint160& accountFamily, int accountSeq, EC_POINT* rootPubKey);
|
||||
|
||||
void unlock(const BIGNUM* rootPrivKey);
|
||||
void lock() { mPrivateKey=CKey::pointer(); }
|
||||
|
||||
bool write(bool create);
|
||||
bool read();
|
||||
// Database operations
|
||||
bool read(); // reads any existing data
|
||||
bool write(); // creates the record in the first place
|
||||
bool updateName(); // writes changed name/comment
|
||||
bool updateBalance(); // writes changed balance/seq
|
||||
|
||||
const uint160& getAccountID() const { return mAcctID; }
|
||||
int getAccountSeq() const { return mAccountSeq; }
|
||||
@@ -47,7 +47,6 @@ public:
|
||||
std::string getAccountName() const; // The normal account name used to send to this account
|
||||
|
||||
CKey::pointer getPubKey() { return mPublicKey; }
|
||||
CKey::pointer getPrivKey() { return mPrivateKey; }
|
||||
|
||||
void update(uint64 balance, uint32 seq);
|
||||
uint32 getTxnSeq() const { return mTxnSeq; }
|
||||
@@ -80,9 +79,10 @@ public:
|
||||
~LocalAccountFamily();
|
||||
|
||||
const uint160& getFamily() { return mFamily; }
|
||||
bool isLocked() const { return mRootPrivateKey==NULL; }
|
||||
|
||||
void unlock(const BIGNUM* privateKey);
|
||||
void lock();
|
||||
bool isLocked() const { return mRootPrivateKey==NULL; }
|
||||
|
||||
void setSeq(uint32 s) { mLastSeq=s; }
|
||||
void setName(const std::string& n) { mName=n; }
|
||||
@@ -91,8 +91,9 @@ public:
|
||||
std::map<int, LocalAccountEntry::pointer>& getAcctMap() { return mAccounts; }
|
||||
LocalAccountEntry::pointer get(int seq);
|
||||
uint160 getAccount(int seq, bool keep);
|
||||
CKey::pointer getPrivateKey(int seq);
|
||||
|
||||
std::string getPubKeyHex() const; // The text name of the public key
|
||||
std::string getPubGenHex() const; // The text name of the public key
|
||||
std::string getShortName() const { return mName; }
|
||||
std::string getComment() const { return mComment; }
|
||||
|
||||
@@ -100,13 +101,10 @@ public:
|
||||
std::string getSQL() const;
|
||||
static LocalAccountFamily::pointer readFamily(const uint160& family);
|
||||
void write(bool is_new);
|
||||
|
||||
};
|
||||
|
||||
class LocalAccount
|
||||
{ // tracks a single local account in a form that can be passed to other code.
|
||||
// We can't hand other code a LocalAccountEntry because an orphaned entry
|
||||
// could hold a private key we can't lock.
|
||||
public:
|
||||
typedef boost::shared_ptr<LocalAccount> pointer;
|
||||
|
||||
@@ -166,11 +164,11 @@ public:
|
||||
LocalAccount::pointer getLocalAccount(const uint160& famBase, int seq);
|
||||
LocalAccount::pointer getLocalAccount(const uint160& acctID);
|
||||
uint160 peekKey(const uint160& family, int seq);
|
||||
std::string getPubKeyHex(const uint160& famBase);
|
||||
std::string getPubGenHex(const uint160& famBase);
|
||||
std::string getShortName(const uint160& famBase);
|
||||
bool getFamilyInfo(const uint160& family, std::string& name, std::string& comment);
|
||||
bool getFullFamilyInfo(const uint160& family, std::string& name, std::string& comment,
|
||||
std::string& pubKey, bool& isLocked);
|
||||
std::string& pubGen, bool& isLocked);
|
||||
|
||||
static bool isHexPrivateKey(const std::string&);
|
||||
static bool isHexPublicKey(const std::string&);
|
||||
|
||||
Reference in New Issue
Block a user