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:
JoelKatz
2012-01-02 23:05:04 -08:00
parent 82a88671dd
commit 7a0dde60ad
5 changed files with 138 additions and 67 deletions

View File

@@ -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;
}

View File

@@ -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 &params)
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)

View File

@@ -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
);

View File

@@ -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);

View File

@@ -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&);