// Copyright 2014 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 . package accounts import ( "bytes" "crypto/ecdsa" "encoding/hex" "encoding/json" "io" "strings" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto/secp256k1" "github.com/pborman/uuid" ) const ( version = 3 ) type Key struct { Id uuid.UUID // Version 4 "random" for unique id not derived from key data // to simplify lookups we also store the address Address common.Address // we only store privkey as pubkey/address can be derived from it // privkey in this struct is always in plaintext PrivateKey *ecdsa.PrivateKey } type keyStore interface { // create new key using io.Reader entropy source and optionally using auth string GenerateNewKey(io.Reader, string) (*Key, error) GetKey(common.Address, string) (*Key, error) // get key from addr and auth string GetKeyAddresses() ([]common.Address, error) // get all addresses StoreKey(*Key, string) error // store key optionally using auth string DeleteKey(common.Address, string) error // delete key by addr and auth string Cleanup(keyAddr common.Address) (err error) } type plainKeyJSON struct { Address string `json:"address"` PrivateKey string `json:"privatekey"` Id string `json:"id"` Version int `json:"version"` } type encryptedKeyJSONV3 struct { Address string `json:"address"` Crypto cryptoJSON `json:"crypto"` Id string `json:"id"` Version int `json:"version"` } type encryptedKeyJSONV1 struct { Address string `json:"address"` Crypto cryptoJSON `json:"crypto"` Id string `json:"id"` Version string `json:"version"` } type cryptoJSON struct { Cipher string `json:"cipher"` CipherText string `json:"ciphertext"` CipherParams cipherparamsJSON `json:"cipherparams"` KDF string `json:"kdf"` KDFParams map[string]interface{} `json:"kdfparams"` MAC string `json:"mac"` } type cipherparamsJSON struct { IV string `json:"iv"` } type scryptParamsJSON struct { N int `json:"n"` R int `json:"r"` P int `json:"p"` DkLen int `json:"dklen"` Salt string `json:"salt"` } func (k *Key) MarshalJSON() (j []byte, err error) { jStruct := plainKeyJSON{ hex.EncodeToString(k.Address[:]), hex.EncodeToString(crypto.FromECDSA(k.PrivateKey)), k.Id.String(), version, } j, err = json.Marshal(jStruct) return j, err } func (k *Key) UnmarshalJSON(j []byte) (err error) { keyJSON := new(plainKeyJSON) err = json.Unmarshal(j, &keyJSON) if err != nil { return err } u := new(uuid.UUID) *u = uuid.Parse(keyJSON.Id) k.Id = *u addr, err := hex.DecodeString(keyJSON.Address) if err != nil { return err } privkey, err := hex.DecodeString(keyJSON.PrivateKey) if err != nil { return err } k.Address = common.BytesToAddress(addr) k.PrivateKey = crypto.ToECDSA(privkey) return nil } func newKeyFromECDSA(privateKeyECDSA *ecdsa.PrivateKey) *Key { id := uuid.NewRandom() key := &Key{ Id: id, Address: crypto.PubkeyToAddress(privateKeyECDSA.PublicKey), PrivateKey: privateKeyECDSA, } return key } func NewKey(rand io.Reader) *Key { randBytes := make([]byte, 64) _, err := rand.Read(randBytes) if err != nil { panic("key generation: could not read from random source: " + err.Error()) } reader := bytes.NewReader(randBytes) privateKeyECDSA, err := ecdsa.GenerateKey(secp256k1.S256(), reader) if err != nil { panic("key generation: ecdsa.GenerateKey failed: " + err.Error()) } return newKeyFromECDSA(privateKeyECDSA) } // generate 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. func NewKeyForDirectICAP(rand io.Reader) *Key { randBytes := make([]byte, 64) _, err := rand.Read(randBytes) if err != nil { panic("key generation: could not read from random source: " + err.Error()) } reader := bytes.NewReader(randBytes) privateKeyECDSA, err := ecdsa.GenerateKey(secp256k1.S256(), reader) if err != nil { panic("key generation: ecdsa.GenerateKey failed: " + err.Error()) } key := newKeyFromECDSA(privateKeyECDSA) if !strings.HasPrefix(key.Address.Hex(), "0x00") { return NewKeyForDirectICAP(rand) } return key }