From 7a0dde60ad0c563e019be6b817ddf1307c118f00 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Mon, 2 Jan 2012 23:05:04 -0800 Subject: [PATCH] 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. --- DeterministicKeys.cpp | 83 ++++++++++++++++++++++++++++++++++--------- RPCServer.cpp | 8 ++--- SQLiteDatabases.sql | 7 ++-- Wallet.cpp | 73 ++++++++++++++++++++++++------------- Wallet.h | 34 +++++++++--------- 5 files changed, 138 insertions(+), 67 deletions(-) diff --git a/DeterministicKeys.cpp b/DeterministicKeys.cpp index c969829b6..222269277 100644 --- a/DeterministicKeys.cpp +++ b/DeterministicKeys.cpp @@ -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; } diff --git a/RPCServer.cpp b/RPCServer.cpp index a58e30e6b..8c044ca5e 100644 --- a/RPCServer.cpp +++ b/RPCServer.cpp @@ -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) diff --git a/SQLiteDatabases.sql b/SQLiteDatabases.sql index f098d1ca8..13ad457ad 100644 --- a/SQLiteDatabases.sql +++ b/SQLiteDatabases.sql @@ -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 ); diff --git a/Wallet.cpp b/Wallet.cpp index 56dc07166..e86e129a7 100644 --- a/Wallet.cpp +++ b/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::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(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& 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::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::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); diff --git a/Wallet.h b/Wallet.h index 19192e01c..8fcf28370 100644 --- a/Wallet.h +++ b/Wallet.h @@ -18,14 +18,14 @@ public: typedef boost::shared_ptr 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& 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 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&);