New keystore format and add UAddress

This commit is contained in:
fnaticwang 2019-09-23 14:52:33 +08:00
parent 63b18027dc
commit f8346a2457
10 changed files with 274 additions and 28 deletions

View file

@ -31,6 +31,7 @@ import (
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/crypto"
"github.com/pborman/uuid"
)
@ -46,6 +47,16 @@ type Key struct {
// we only store privkey as pubkey/address can be derived from it
// privkey in this struct is always in plaintext
PrivateKey *ecdsa.PrivateKey
// add a second privkey for privary
PrivateKey2 *ecdsa.PrivateKey
// compact wanchain address format
UAddress common.UAddress
}
// Used to import and export raw keypair
type keyPair struct {
D string `json:"privateKey"`
D1 string `json:"privateKey1"`
}
type keyStore interface {
@ -65,10 +76,12 @@ type plainKeyJSON struct {
}
type encryptedKeyJSONV3 struct {
Address string `json:"address"`
Crypto CryptoJSON `json:"crypto"`
Id string `json:"id"`
Version int `json:"version"`
Address string `json:"address"`
Crypto CryptoJSON `json:"crypto"`
Crypto2 CryptoJSON `json:"crypto2"`
Id string `json:"id"`
Version int `json:"version"`
UAddress string `json:"uaddress"`
}
type encryptedKeyJSONV1 struct {
@ -127,16 +140,44 @@ func (k *Key) UnmarshalJSON(j []byte) (err error) {
return nil
}
func newKeyFromECDSA(privateKeyECDSA *ecdsa.PrivateKey) *Key {
func newKeyFromECDSA(sk1, sk2 *ecdsa.PrivateKey) *Key {
id := uuid.NewRandom()
key := &Key{
Id: id,
Address: crypto.PubkeyToAddress(privateKeyECDSA.PublicKey),
PrivateKey: privateKeyECDSA,
Id: id,
Address: crypto.PubkeyToAddress(sk1.PublicKey),
PrivateKey: sk1,
PrivateKey2: sk2,
}
updateUaddress(key)
return key
}
// updateuaddress adds UAddress field to the Key struct
func updateUaddress(k *Key) {
k.UAddress = *GenerateUaddressFromPK(&k.PrivateKey.PublicKey, &k.PrivateKey2.PublicKey)
}
// ECDSAPKCompression serializes a public key in a 33-byte compressed format from btcec
func ECDSAPKCompression(p *ecdsa.PublicKey) []byte {
const pubkeyCompressed byte = 0x2
b := make([]byte, 0, 33)
format := pubkeyCompressed
if p.Y.Bit(0) == 1 {
format |= 0x1
}
b = append(b, format)
b = append(b, math.PaddedBigBytes(p.X, 32)...)
return b
}
func GenerateUaddressFromPK(A *ecdsa.PublicKey, B *ecdsa.PublicKey) *common.UAddress {
var tmp common.UAddress
copy(tmp[:33], ECDSAPKCompression(A))
copy(tmp[33:], ECDSAPKCompression(B))
return &tmp
}
// NewKeyForDirectICAP generates a key whose address fits into < 155 bits so it can fit
// into the Direct ICAP spec. for simplicity and easier compatibility with other libs, we
// retry until the first byte is 0.
@ -147,11 +188,15 @@ func NewKeyForDirectICAP(rand io.Reader) *Key {
panic("key generation: could not read from random source: " + err.Error())
}
reader := bytes.NewReader(randBytes)
privateKeyECDSA, err := ecdsa.GenerateKey(crypto.S256(), reader)
sk1, err := ecdsa.GenerateKey(crypto.S256(), reader)
if err != nil {
panic("key generation: ecdsa.GenerateKey failed: " + err.Error())
}
key := newKeyFromECDSA(privateKeyECDSA)
sk2, err := ecdsa.GenerateKey(crypto.S256(), reader)
if err != nil {
panic("key generation: ecdsa.GenerateKey failed: " + err.Error())
}
key := newKeyFromECDSA(sk1, sk2)
if !strings.HasPrefix(key.Address.Hex(), "0x00") {
return NewKeyForDirectICAP(rand)
}
@ -163,7 +208,13 @@ func newKey(rand io.Reader) (*Key, error) {
if err != nil {
return nil, err
}
return newKeyFromECDSA(privateKeyECDSA), nil
privateKeyECDSA2, err := ecdsa.GenerateKey(crypto.S256(), rand)
if err != nil {
return nil, err
}
return newKeyFromECDSA(privateKeyECDSA, privateKeyECDSA2), nil
}
func storeNewKey(ks keyStore, rand io.Reader, auth string) (*Key, accounts.Account, error) {
@ -230,3 +281,42 @@ func toISO8601(t time.Time) string {
return fmt.Sprintf("%04d-%02d-%02dT%02d-%02d-%02d.%09d%s",
t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), tz)
}
// LoadECDSAPair loads a secp256k1 private key pair from the given file
func LoadECDSAPair(file string) (*ecdsa.PrivateKey, *ecdsa.PrivateKey, error) {
// read the given file including private key pair
kp := keyPair{}
raw, err := ioutil.ReadFile(file)
if err != nil {
return nil, nil, err
}
err = json.Unmarshal(raw, &kp)
if err != nil {
return nil, nil, err
}
// Decode the key pair
d, err := hex.DecodeString(kp.D)
if err != nil {
return nil, nil, err
}
d1, err := hex.DecodeString(kp.D1)
if err != nil {
return nil, nil, err
}
// Generate ecdsa private keys
sk, err := crypto.ToECDSA(d)
if err != nil {
return nil, nil, err
}
sk1, err := crypto.ToECDSA(d1)
if err != nil {
return nil, nil, err
}
return sk, sk1, err
}

View file

@ -447,8 +447,8 @@ func (ks *KeyStore) Import(keyJSON []byte, passphrase, newPassphrase string) (ac
}
// ImportECDSA stores the given key into the key directory, encrypting it with the passphrase.
func (ks *KeyStore) ImportECDSA(priv *ecdsa.PrivateKey, passphrase string) (accounts.Account, error) {
key := newKeyFromECDSA(priv)
func (ks *KeyStore) ImportECDSA(priv1, priv2 *ecdsa.PrivateKey, passphrase string) (accounts.Account, error) {
key := newKeyFromECDSA(priv1, priv2)
if ks.cache.hasAddress(key.Address) {
return accounts.Account{}, fmt.Errorf("account already exists")
}

View file

@ -28,10 +28,12 @@ package keystore
import (
"bytes"
"crypto/aes"
"crypto/ecdsa"
"crypto/rand"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
@ -42,6 +44,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/randentropy"
"github.com/pborman/uuid"
"golang.org/x/crypto/pbkdf2"
"golang.org/x/crypto/scrypt"
@ -70,6 +73,13 @@ const (
scryptDKLen = 32
)
var (
ErrUAddressFieldNotExist = errors.New("It seems that this account doesn't include a valid usechain address field, please update your keyfile version")
ErrUAddressInvalid = errors.New("invalid usechain address")
ErrInvalidAccountKey = errors.New("invalid account key")
ErrInvalidPrivateKey = errors.New("invalid private key")
)
type keyStorePassphrase struct {
keysDirPath string
scryptN int
@ -183,18 +193,90 @@ func EncryptDataV3(data, auth []byte, scryptN, scryptP int) (CryptoJSON, error)
// EncryptKey encrypts a key using the specified scrypt parameters into a json
// blob that can be decrypted later on.
func EncryptKey(key *Key, auth string, scryptN, scryptP int) ([]byte, error) {
keyBytes := math.PaddedBigBytes(key.PrivateKey.D, 32)
cryptoStruct, err := EncryptDataV3(keyBytes, []byte(auth), scryptN, scryptP)
if key == nil {
return nil, ErrInvalidAccountKey
}
cryptoStruct, err := EncryptOnePrivateKey(key.PrivateKey, auth, scryptN, scryptP)
if err != nil {
return nil, err
}
cryptoStruct2, err := EncryptOnePrivateKey(key.PrivateKey2, auth, scryptN, scryptP)
if err != nil {
return nil, err
}
encryptedKeyJSONV3 := encryptedKeyJSONV3{
hex.EncodeToString(key.Address[:]),
cryptoStruct,
key.Address.Hex()[2:],
*cryptoStruct,
*cryptoStruct2,
key.Id.String(),
version,
hex.EncodeToString(key.UAddress[:]),
}
return json.Marshal(encryptedKeyJSONV3)
// keyBytes := math.PaddedBigBytes(key.PrivateKey.D, 32)
// cryptoStruct, err := EncryptDataV3(keyBytes, []byte(auth), scryptN, scryptP)
// if err != nil {
// return nil, err
// }
// encryptedKeyJSONV3 := encryptedKeyJSONV3{
// hex.EncodeToString(key.Address[:]),
// cryptoStruct,
// key.Id.String(),
// version,
// }
// return json.Marshal(encryptedKeyJSONV3)
}
// EncryptOnePrivateKey encrypts a key using the specified scrypt parameters into one field of a json
// blob that can be decrypted later on.
func EncryptOnePrivateKey(privateKey *ecdsa.PrivateKey, auth string, scryptN, scryptP int) (*CryptoJSON, error) {
if privateKey == nil {
return nil, ErrInvalidPrivateKey
}
authArray := []byte(auth)
salt := randentropy.GetEntropyCSPRNG(32)
derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptR, scryptP, scryptDKLen)
if err != nil {
return nil, err
}
encryptKey := derivedKey[:16]
keyBytes := math.PaddedBigBytes(privateKey.D, 32)
iv := randentropy.GetEntropyCSPRNG(aes.BlockSize) // 16
cipherText, err := aesCTRXOR(encryptKey, keyBytes, iv)
if err != nil {
return nil, err
}
mac := crypto.Keccak256(derivedKey[16:32], cipherText)
scryptParamsJSON := make(map[string]interface{}, 5)
scryptParamsJSON["n"] = scryptN
scryptParamsJSON["r"] = scryptR
scryptParamsJSON["p"] = scryptP
scryptParamsJSON["dklen"] = scryptDKLen
scryptParamsJSON["salt"] = hex.EncodeToString(salt)
cipherParamsJSON := cipherparamsJSON{
IV: hex.EncodeToString(iv),
}
cryptoStruct := &CryptoJSON{
Cipher: "aes-128-ctr",
CipherText: hex.EncodeToString(cipherText),
CipherParams: cipherParamsJSON,
KDF: keyHeaderKDF,
KDFParams: scryptParamsJSON,
MAC: hex.EncodeToString(mac),
}
return cryptoStruct, nil
}
// DecryptKey decrypts a key from a json blob, returning the private key itself.

View file

@ -24,7 +24,6 @@ import (
"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/console"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
"gopkg.in/urfave/cli.v1"
)
@ -368,7 +367,7 @@ func accountImport(ctx *cli.Context) error {
if len(keyfile) == 0 {
utils.Fatalf("keyfile must be given as argument")
}
key, err := crypto.LoadECDSA(keyfile)
key, key1, err := keystore.LoadECDSAPair(keyfile)
if err != nil {
utils.Fatalf("Failed to load the private key: %v", err)
}
@ -376,7 +375,7 @@ func accountImport(ctx *cli.Context) error {
passphrase := getPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx))
ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
acct, err := ks.ImportECDSA(key, passphrase)
acct, err := ks.ImportECDSA(key, key1, passphrase)
if err != nil {
utils.Fatalf("Could not create the account: %v", err)
}

View file

@ -36,6 +36,8 @@ const (
HashLength = 32
// AddressLength is the expected length of the address
AddressLength = 20
UAddressLength = 66
)
var (
@ -174,6 +176,9 @@ func (h UnprefixedHash) MarshalText() ([]byte, error) {
// Address represents the 20 byte address of an Ethereum account.
type Address [AddressLength]byte
// UAddress represents the 66 byte address of an Usechain account
type UAddress [UAddressLength]byte
// BytesToAddress returns Address with value b.
// If b is larger than len(h), b will be cropped from the left.
func BytesToAddress(b []byte) Address {

View file

@ -0,0 +1,42 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package randentropy
import (
crand "crypto/rand"
"io"
)
var Reader io.Reader = &randEntropy{}
type randEntropy struct {
}
func (*randEntropy) Read(bytes []byte) (n int, err error) {
readBytes := GetEntropyCSPRNG(len(bytes))
copy(bytes, readBytes)
return len(bytes), nil
}
func GetEntropyCSPRNG(n int) []byte {
mainBuff := make([]byte, n)
_, err := io.ReadFull(crand.Reader, mainBuff)
if err != nil {
panic("reading from crypto/rand failed: " + err.Error())
}
return mainBuff
}

View file

@ -19,6 +19,7 @@ package ethapi
import (
"bytes"
"context"
"encoding/hex"
"errors"
"fmt"
"math/big"
@ -297,12 +298,35 @@ func fetchKeystore(am *accounts.Manager) *keystore.KeyStore {
// ImportRawKey stores the given hex encoded ECDSA key into the key directory,
// encrypting it with the passphrase.
func (s *PrivateAccountAPI) ImportRawKey(privkey string, password string) (common.Address, error) {
key, err := crypto.HexToECDSA(privkey)
func (s *PrivateAccountAPI) ImportRawKey(privkey0, privkey1 string, password string) (common.Address, error) {
if strings.HasPrefix(privkey0, "0x") {
privkey0 = privkey0[2:]
}
if strings.HasPrefix(privkey1, "0x") {
privkey1 = privkey1[2:]
}
r0, err := hex.DecodeString(privkey0)
if err != nil {
return common.Address{}, err
}
acc, err := fetchKeystore(s.am).ImportECDSA(key, password)
r1, err := hex.DecodeString(privkey1)
if err != nil {
return common.Address{}, err
}
sk0, err := crypto.ToECDSA(r0)
if err != nil {
return common.Address{}, nil
}
sk1, err := crypto.ToECDSA(r1)
if err != nil {
return common.Address{}, nil
}
acc, err := fetchKeystore(s.am).ImportECDSA(sk0, sk1, password)
return acc.Address, err
}

View file

@ -84,7 +84,7 @@ func main() {
// Inject the signer key and start sealing with it
store := node.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
signer, err := store.ImportECDSA(sealer, "")
signer, err := store.ImportECDSA(sealer, nil, "")
if err != nil {
panic(err)
}

View file

@ -203,7 +203,7 @@ func (ks *KeyStore) ImportECDSAKey(key []byte, passphrase string) (account *Acco
if err != nil {
return nil, err
}
acc, err := ks.keystore.ImportECDSA(privkey, passphrase)
acc, err := ks.keystore.ImportECDSA(privkey, nil, passphrase)
if err != nil {
return nil, err
}

View file

@ -118,8 +118,12 @@ func fetchKeystore(am *accounts.Manager) *keystore.KeyStore {
// encrypting it with the passphrase.
// Example call (should fail on password too short)
// {"jsonrpc":"2.0","method":"clef_importRawKey","params":["1111111111111111111111111111111111111111111111111111111111111111","test"], "id":6}
func (s *UIServerAPI) ImportRawKey(privkey string, password string) (accounts.Account, error) {
key, err := crypto.HexToECDSA(privkey)
func (s *UIServerAPI) ImportRawKey(privkey1, privkey2 string, password string) (accounts.Account, error) {
key1, err := crypto.HexToECDSA(privkey1)
if err != nil {
return accounts.Account{}, err
}
key2, err := crypto.HexToECDSA(privkey2)
if err != nil {
return accounts.Account{}, err
}
@ -127,7 +131,7 @@ func (s *UIServerAPI) ImportRawKey(privkey string, password string) (accounts.Ac
return accounts.Account{}, fmt.Errorf("password requirements not met: %v", err)
}
// No error
return fetchKeystore(s.am).ImportECDSA(key, password)
return fetchKeystore(s.am).ImportECDSA(key1, key2, password)
}
// OpenWallet initiates a hardware wallet opening procedure, establishing a USB