mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
New keystore format and add UAddress
This commit is contained in:
parent
63b18027dc
commit
f8346a2457
10 changed files with 274 additions and 28 deletions
|
|
@ -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 {
|
||||
|
|
@ -67,8 +78,10 @@ type plainKeyJSON struct {
|
|||
type encryptedKeyJSONV3 struct {
|
||||
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,
|
||||
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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
42
crypto/randentropy/rand_entropy.go
Normal file
42
crypto/randentropy/rand_entropy.go
Normal 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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue