aboutsummaryrefslogtreecommitdiffstats
path: root/crypto
diff options
context:
space:
mode:
authorFelix Lange <fjl@twurst.com>2016-03-02 20:57:15 +0800
committerFelix Lange <fjl@twurst.com>2016-04-12 21:56:49 +0800
commit85e6c40c0081bd0db80448640db648887804010c (patch)
tree326a2c3bc115a445b481624cb20f00b28e44f92a /crypto
parentdff9b4246f3ef9e6c254b57eef6d0433809f16b9 (diff)
downloadgo-tangerine-85e6c40c0081bd0db80448640db648887804010c.tar
go-tangerine-85e6c40c0081bd0db80448640db648887804010c.tar.gz
go-tangerine-85e6c40c0081bd0db80448640db648887804010c.tar.bz2
go-tangerine-85e6c40c0081bd0db80448640db648887804010c.tar.lz
go-tangerine-85e6c40c0081bd0db80448640db648887804010c.tar.xz
go-tangerine-85e6c40c0081bd0db80448640db648887804010c.tar.zst
go-tangerine-85e6c40c0081bd0db80448640db648887804010c.zip
accounts, crypto: move keystore to package accounts
The account management API was originally implemented as a thin layer around crypto.KeyStore, on the grounds that several kinds of key stores would be implemented later on. It turns out that this won't happen so KeyStore is a superflous abstraction. In this commit crypto.KeyStore and everything related to it moves to package accounts and is unexported.
Diffstat (limited to 'crypto')
-rw-r--r--crypto/crypto.go106
-rw-r--r--crypto/key.go168
-rw-r--r--crypto/key_store_passphrase.go315
-rw-r--r--crypto/key_store_passphrase_test.go51
-rw-r--r--crypto/key_store_plain.go209
-rw-r--r--crypto/key_store_test.go233
-rw-r--r--crypto/tests/v1/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e1
-rw-r--r--crypto/tests/v1_test_vector.json28
-rw-r--r--crypto/tests/v3_test_vector.json49
9 files changed, 0 insertions, 1160 deletions
diff --git a/crypto/crypto.go b/crypto/crypto.go
index cd0e4e101..b24d08010 100644
--- a/crypto/crypto.go
+++ b/crypto/crypto.go
@@ -17,8 +17,6 @@
package crypto
import (
- "crypto/aes"
- "crypto/cipher"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
@@ -30,7 +28,6 @@ import (
"os"
"encoding/hex"
- "encoding/json"
"errors"
"github.com/ethereum/go-ethereum/common"
@@ -38,8 +35,6 @@ import (
"github.com/ethereum/go-ethereum/crypto/secp256k1"
"github.com/ethereum/go-ethereum/crypto/sha3"
"github.com/ethereum/go-ethereum/rlp"
- "github.com/pborman/uuid"
- "golang.org/x/crypto/pbkdf2"
"golang.org/x/crypto/ripemd160"
)
@@ -217,107 +212,6 @@ func Decrypt(prv *ecdsa.PrivateKey, ct []byte) ([]byte, error) {
return key.Decrypt(rand.Reader, ct, nil, nil)
}
-// creates a Key and stores that in the given KeyStore by decrypting a presale key JSON
-func ImportPreSaleKey(keyStore KeyStore, keyJSON []byte, password string) (*Key, error) {
- key, err := decryptPreSaleKey(keyJSON, password)
- if err != nil {
- return nil, err
- }
- key.Id = uuid.NewRandom()
- err = keyStore.StoreKey(key, password)
- return key, err
-}
-
-func decryptPreSaleKey(fileContent []byte, password string) (key *Key, err error) {
- preSaleKeyStruct := struct {
- EncSeed string
- EthAddr string
- Email string
- BtcAddr string
- }{}
- err = json.Unmarshal(fileContent, &preSaleKeyStruct)
- if err != nil {
- return nil, err
- }
- encSeedBytes, err := hex.DecodeString(preSaleKeyStruct.EncSeed)
- iv := encSeedBytes[:16]
- cipherText := encSeedBytes[16:]
- /*
- See https://github.com/ethereum/pyethsaletool
-
- pyethsaletool generates the encryption key from password by
- 2000 rounds of PBKDF2 with HMAC-SHA-256 using password as salt (:().
- 16 byte key length within PBKDF2 and resulting key is used as AES key
- */
- passBytes := []byte(password)
- derivedKey := pbkdf2.Key(passBytes, passBytes, 2000, 16, sha256.New)
- plainText, err := aesCBCDecrypt(derivedKey, cipherText, iv)
- if err != nil {
- return nil, err
- }
- ethPriv := Keccak256(plainText)
- ecKey := ToECDSA(ethPriv)
- key = &Key{
- Id: nil,
- Address: PubkeyToAddress(ecKey.PublicKey),
- PrivateKey: ecKey,
- }
- derivedAddr := hex.EncodeToString(key.Address.Bytes()) // needed because .Hex() gives leading "0x"
- expectedAddr := preSaleKeyStruct.EthAddr
- if derivedAddr != expectedAddr {
- err = fmt.Errorf("decrypted addr '%s' not equal to expected addr '%s'", derivedAddr, expectedAddr)
- }
- return key, err
-}
-
-// AES-128 is selected due to size of encryptKey
-func aesCTRXOR(key, inText, iv []byte) ([]byte, error) {
- aesBlock, err := aes.NewCipher(key)
- if err != nil {
- return nil, err
- }
- stream := cipher.NewCTR(aesBlock, iv)
- outText := make([]byte, len(inText))
- stream.XORKeyStream(outText, inText)
- return outText, err
-}
-
-func aesCBCDecrypt(key, cipherText, iv []byte) ([]byte, error) {
- aesBlock, err := aes.NewCipher(key)
- if err != nil {
- return nil, err
- }
- decrypter := cipher.NewCBCDecrypter(aesBlock, iv)
- paddedPlaintext := make([]byte, len(cipherText))
- decrypter.CryptBlocks(paddedPlaintext, cipherText)
- plaintext := PKCS7Unpad(paddedPlaintext)
- if plaintext == nil {
- err = errors.New("Decryption failed: PKCS7Unpad failed after AES decryption")
- }
- return plaintext, err
-}
-
-// From https://leanpub.com/gocrypto/read#leanpub-auto-block-cipher-modes
-func PKCS7Unpad(in []byte) []byte {
- if len(in) == 0 {
- return nil
- }
-
- padding := in[len(in)-1]
- if int(padding) > len(in) || padding > aes.BlockSize {
- return nil
- } else if padding == 0 {
- return nil
- }
-
- for i := len(in) - 1; i > len(in)-int(padding)-1; i-- {
- if in[i] != padding {
- return nil
- }
- }
- return in[:len(in)-int(padding)]
-}
-
func PubkeyToAddress(p ecdsa.PublicKey) common.Address {
pubBytes := FromECDSAPub(&p)
return common.BytesToAddress(Keccak256(pubBytes[1:])[12:])
diff --git a/crypto/key.go b/crypto/key.go
deleted file mode 100644
index 8e2d8553b..000000000
--- a/crypto/key.go
+++ /dev/null
@@ -1,168 +0,0 @@
-// 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 <http://www.gnu.org/licenses/>.
-
-package crypto
-
-import (
- "bytes"
- "crypto/ecdsa"
- "encoding/hex"
- "encoding/json"
- "io"
- "strings"
-
- "github.com/ethereum/go-ethereum/common"
- "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 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(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 = ToECDSA(privkey)
-
- return nil
-}
-
-func NewKeyFromECDSA(privateKeyECDSA *ecdsa.PrivateKey) *Key {
- id := uuid.NewRandom()
- key := &Key{
- Id: id,
- Address: 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
-}
diff --git a/crypto/key_store_passphrase.go b/crypto/key_store_passphrase.go
deleted file mode 100644
index 19e77de91..000000000
--- a/crypto/key_store_passphrase.go
+++ /dev/null
@@ -1,315 +0,0 @@
-// 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 <http://www.gnu.org/licenses/>.
-
-/*
-
-This key store behaves as KeyStorePlain with the difference that
-the private key is encrypted and on disk uses another JSON encoding.
-
-The crypto is documented at https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition
-
-*/
-
-package crypto
-
-import (
- "bytes"
- "crypto/aes"
- "crypto/sha256"
- "encoding/hex"
- "encoding/json"
- "errors"
- "fmt"
- "io"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/crypto/randentropy"
- "github.com/pborman/uuid"
- "golang.org/x/crypto/pbkdf2"
- "golang.org/x/crypto/scrypt"
-)
-
-const (
- keyHeaderKDF = "scrypt"
-
- // n,r,p = 2^18, 8, 1 uses 256MB memory and approx 1s CPU time on a modern CPU.
- StandardScryptN = 1 << 18
- StandardScryptP = 1
-
- // n,r,p = 2^12, 8, 6 uses 4MB memory and approx 100ms CPU time on a modern CPU.
- LightScryptN = 1 << 12
- LightScryptP = 6
-
- scryptR = 8
- scryptDKLen = 32
-)
-
-type keyStorePassphrase struct {
- keysDirPath string
- scryptN int
- scryptP int
-}
-
-func NewKeyStorePassphrase(path string, scryptN int, scryptP int) KeyStore {
- return &keyStorePassphrase{path, scryptN, scryptP}
-}
-
-func (ks keyStorePassphrase) GenerateNewKey(rand io.Reader, auth string) (key *Key, err error) {
- return GenerateNewKeyDefault(ks, rand, auth)
-}
-
-func (ks keyStorePassphrase) GetKey(keyAddr common.Address, auth string) (key *Key, err error) {
- return decryptKeyFromFile(ks.keysDirPath, keyAddr, auth)
-}
-
-func (ks keyStorePassphrase) Cleanup(keyAddr common.Address) (err error) {
- return cleanup(ks.keysDirPath, keyAddr)
-}
-
-func (ks keyStorePassphrase) GetKeyAddresses() (addresses []common.Address, err error) {
- return getKeyAddresses(ks.keysDirPath)
-}
-
-func (ks keyStorePassphrase) StoreKey(key *Key, auth string) error {
- keyjson, err := EncryptKey(key, auth, ks.scryptN, ks.scryptP)
- if err != nil {
- return err
- }
- return writeKeyFile(key.Address, ks.keysDirPath, keyjson)
-}
-
-// 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) {
- 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 := FromECDSA(key.PrivateKey)
-
- iv := randentropy.GetEntropyCSPRNG(aes.BlockSize) // 16
- cipherText, err := aesCTRXOR(encryptKey, keyBytes, iv)
- if err != nil {
- return nil, err
- }
- mac := 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: "scrypt",
- KDFParams: scryptParamsJSON,
- MAC: hex.EncodeToString(mac),
- }
- encryptedKeyJSONV3 := encryptedKeyJSONV3{
- hex.EncodeToString(key.Address[:]),
- cryptoStruct,
- key.Id.String(),
- version,
- }
- return json.Marshal(encryptedKeyJSONV3)
-}
-
-func (ks keyStorePassphrase) DeleteKey(keyAddr common.Address, auth string) error {
- // only delete if correct passphrase is given
- if _, err := decryptKeyFromFile(ks.keysDirPath, keyAddr, auth); err != nil {
- return err
- }
- return deleteKey(ks.keysDirPath, keyAddr)
-}
-
-// DecryptKey decrypts a key from a json blob, returning the private key itself.
-func DecryptKey(keyjson []byte, auth string) (*Key, error) {
- // Parse the json into a simple map to fetch the key version
- m := make(map[string]interface{})
- if err := json.Unmarshal(keyjson, &m); err != nil {
- return nil, err
- }
- // Depending on the version try to parse one way or another
- var (
- keyBytes, keyId []byte
- err error
- )
- if version, ok := m["version"].(string); ok && version == "1" {
- k := new(encryptedKeyJSONV1)
- if err := json.Unmarshal(keyjson, k); err != nil {
- return nil, err
- }
- keyBytes, keyId, err = decryptKeyV1(k, auth)
- } else {
- k := new(encryptedKeyJSONV3)
- if err := json.Unmarshal(keyjson, k); err != nil {
- return nil, err
- }
- keyBytes, keyId, err = decryptKeyV3(k, auth)
- }
- // Handle any decryption errors and return the key
- if err != nil {
- return nil, err
- }
- key := ToECDSA(keyBytes)
- return &Key{
- Id: uuid.UUID(keyId),
- Address: PubkeyToAddress(key.PublicKey),
- PrivateKey: key,
- }, nil
-}
-
-func decryptKeyFromFile(keysDirPath string, keyAddr common.Address, auth string) (*Key, error) {
- // Load the key from the keystore and decrypt its contents
- keyjson, err := getKeyFile(keysDirPath, keyAddr)
- if err != nil {
- return nil, err
- }
- key, err := DecryptKey(keyjson, auth)
- if err != nil {
- return nil, err
- }
- // Make sure we're really operating on the requested key (no swap attacks)
- if keyAddr != key.Address {
- return nil, fmt.Errorf("key content mismatch: have account %x, want %x", key.Address, keyAddr)
- }
- return key, nil
-}
-
-func decryptKeyV3(keyProtected *encryptedKeyJSONV3, auth string) (keyBytes []byte, keyId []byte, err error) {
- if keyProtected.Version != version {
- return nil, nil, fmt.Errorf("Version not supported: %v", keyProtected.Version)
- }
-
- if keyProtected.Crypto.Cipher != "aes-128-ctr" {
- return nil, nil, fmt.Errorf("Cipher not supported: %v", keyProtected.Crypto.Cipher)
- }
-
- keyId = uuid.Parse(keyProtected.Id)
- mac, err := hex.DecodeString(keyProtected.Crypto.MAC)
- if err != nil {
- return nil, nil, err
- }
-
- iv, err := hex.DecodeString(keyProtected.Crypto.CipherParams.IV)
- if err != nil {
- return nil, nil, err
- }
-
- cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText)
- if err != nil {
- return nil, nil, err
- }
-
- derivedKey, err := getKDFKey(keyProtected.Crypto, auth)
- if err != nil {
- return nil, nil, err
- }
-
- calculatedMAC := Keccak256(derivedKey[16:32], cipherText)
- if !bytes.Equal(calculatedMAC, mac) {
- return nil, nil, errors.New("Decryption failed: MAC mismatch")
- }
-
- plainText, err := aesCTRXOR(derivedKey[:16], cipherText, iv)
- if err != nil {
- return nil, nil, err
- }
- return plainText, keyId, err
-}
-
-func decryptKeyV1(keyProtected *encryptedKeyJSONV1, auth string) (keyBytes []byte, keyId []byte, err error) {
- keyId = uuid.Parse(keyProtected.Id)
- mac, err := hex.DecodeString(keyProtected.Crypto.MAC)
- if err != nil {
- return nil, nil, err
- }
-
- iv, err := hex.DecodeString(keyProtected.Crypto.CipherParams.IV)
- if err != nil {
- return nil, nil, err
- }
-
- cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText)
- if err != nil {
- return nil, nil, err
- }
-
- derivedKey, err := getKDFKey(keyProtected.Crypto, auth)
- if err != nil {
- return nil, nil, err
- }
-
- calculatedMAC := Keccak256(derivedKey[16:32], cipherText)
- if !bytes.Equal(calculatedMAC, mac) {
- return nil, nil, errors.New("Decryption failed: MAC mismatch")
- }
-
- plainText, err := aesCBCDecrypt(Keccak256(derivedKey[:16])[:16], cipherText, iv)
- if err != nil {
- return nil, nil, err
- }
- return plainText, keyId, err
-}
-
-func getKDFKey(cryptoJSON cryptoJSON, auth string) ([]byte, error) {
- authArray := []byte(auth)
- salt, err := hex.DecodeString(cryptoJSON.KDFParams["salt"].(string))
- if err != nil {
- return nil, err
- }
- dkLen := ensureInt(cryptoJSON.KDFParams["dklen"])
-
- if cryptoJSON.KDF == "scrypt" {
- n := ensureInt(cryptoJSON.KDFParams["n"])
- r := ensureInt(cryptoJSON.KDFParams["r"])
- p := ensureInt(cryptoJSON.KDFParams["p"])
- return scrypt.Key(authArray, salt, n, r, p, dkLen)
-
- } else if cryptoJSON.KDF == "pbkdf2" {
- c := ensureInt(cryptoJSON.KDFParams["c"])
- prf := cryptoJSON.KDFParams["prf"].(string)
- if prf != "hmac-sha256" {
- return nil, fmt.Errorf("Unsupported PBKDF2 PRF: ", prf)
- }
- key := pbkdf2.Key(authArray, salt, c, dkLen, sha256.New)
- return key, nil
- }
-
- return nil, fmt.Errorf("Unsupported KDF: ", cryptoJSON.KDF)
-}
-
-// TODO: can we do without this when unmarshalling dynamic JSON?
-// why do integers in KDF params end up as float64 and not int after
-// unmarshal?
-func ensureInt(x interface{}) int {
- res, ok := x.(int)
- if !ok {
- res = int(x.(float64))
- }
- return res
-}
diff --git a/crypto/key_store_passphrase_test.go b/crypto/key_store_passphrase_test.go
deleted file mode 100644
index bcdd58ad9..000000000
--- a/crypto/key_store_passphrase_test.go
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2016 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 crypto
-
-import (
- "testing"
-
- "github.com/ethereum/go-ethereum/common"
-)
-
-// Tests that a json key file can be decrypted and encrypted in multiple rounds.
-func TestKeyEncryptDecrypt(t *testing.T) {
- address := common.HexToAddress("f626acac23772cbe04dd578bee681b06bdefb9fa")
- keyjson := []byte("{\"address\":\"f626acac23772cbe04dd578bee681b06bdefb9fa\",\"crypto\":{\"cipher\":\"aes-128-ctr\",\"ciphertext\":\"1bcf0ab9b14459795ce59f63e63255ffd84dc38d31614a5a78e37144d7e4a17f\",\"cipherparams\":{\"iv\":\"df4c7e225ee2d81adef522013e3fbe24\"},\"kdf\":\"scrypt\",\"kdfparams\":{\"dklen\":32,\"n\":262144,\"p\":1,\"r\":8,\"salt\":\"2909a99dd2bfa7079a4b40991773b1083f8512c0c55b9b63402ab0e3dc8db8b3\"},\"mac\":\"4ecf6a4ad92ae2c016cb7c44abade74799480c3303eb024661270dfefdbc7510\"},\"id\":\"b4718210-9a30-4883-b8a6-dbdd08bd0ceb\",\"version\":3}")
- password := ""
-
- // Do a few rounds of decryption and encryption
- for i := 0; i < 3; i++ {
- // Try a bad password first
- if _, err := DecryptKey(keyjson, password+"bad"); err == nil {
- t.Error("test %d: json key decrypted with bad password", i)
- }
- // Decrypt with the correct password
- key, err := DecryptKey(keyjson, password)
- if err != nil {
- t.Errorf("test %d: json key failed to decrypt: %v", i, err)
- }
- if key.Address != address {
- t.Errorf("test %d: key address mismatch: have %x, want %x", i, key.Address, address)
- }
- // Recrypt with a new password and start over
- password += "new data appended"
- if keyjson, err = EncryptKey(key, password, LightScryptN, LightScryptP); err != nil {
- t.Errorf("test %d: failed to recrypt key %v", err)
- }
- }
-}
diff --git a/crypto/key_store_plain.go b/crypto/key_store_plain.go
deleted file mode 100644
index 4ce789a30..000000000
--- a/crypto/key_store_plain.go
+++ /dev/null
@@ -1,209 +0,0 @@
-// 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 <http://www.gnu.org/licenses/>.
-
-package crypto
-
-import (
- "encoding/hex"
- "encoding/json"
- "fmt"
- "io"
- "io/ioutil"
- "os"
- "path/filepath"
- "time"
-
- "github.com/ethereum/go-ethereum/common"
-)
-
-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 keyStorePlain struct {
- keysDirPath string
-}
-
-func NewKeyStorePlain(path string) KeyStore {
- return &keyStorePlain{path}
-}
-
-func (ks keyStorePlain) GenerateNewKey(rand io.Reader, auth string) (key *Key, err error) {
- return GenerateNewKeyDefault(ks, rand, auth)
-}
-
-func GenerateNewKeyDefault(ks KeyStore, rand io.Reader, auth string) (key *Key, err error) {
- defer func() {
- if r := recover(); r != nil {
- err = fmt.Errorf("GenerateNewKey error: %v", r)
- }
- }()
- key = NewKey(rand)
- err = ks.StoreKey(key, auth)
- return key, err
-}
-
-func (ks keyStorePlain) GetKey(keyAddr common.Address, auth string) (*Key, error) {
- keyjson, err := getKeyFile(ks.keysDirPath, keyAddr)
- if err != nil {
- return nil, err
- }
- key := new(Key)
- if err := json.Unmarshal(keyjson, key); err != nil {
- return nil, err
- }
- return key, nil
-}
-
-func (ks keyStorePlain) GetKeyAddresses() (addresses []common.Address, err error) {
- return getKeyAddresses(ks.keysDirPath)
-}
-
-func (ks keyStorePlain) Cleanup(keyAddr common.Address) (err error) {
- return cleanup(ks.keysDirPath, keyAddr)
-}
-
-func (ks keyStorePlain) StoreKey(key *Key, auth string) (err error) {
- keyJSON, err := json.Marshal(key)
- if err != nil {
- return
- }
- err = writeKeyFile(key.Address, ks.keysDirPath, keyJSON)
- return
-}
-
-func (ks keyStorePlain) DeleteKey(keyAddr common.Address, auth string) (err error) {
- return deleteKey(ks.keysDirPath, keyAddr)
-}
-
-func deleteKey(keysDirPath string, keyAddr common.Address) (err error) {
- var path string
- path, err = getKeyFilePath(keysDirPath, keyAddr)
- if err == nil {
- addrHex := hex.EncodeToString(keyAddr[:])
- if path == filepath.Join(keysDirPath, addrHex, addrHex) {
- path = filepath.Join(keysDirPath, addrHex)
- }
- err = os.RemoveAll(path)
- }
- return
-}
-
-func getKeyFilePath(keysDirPath string, keyAddr common.Address) (keyFilePath string, err error) {
- addrHex := hex.EncodeToString(keyAddr[:])
- matches, err := filepath.Glob(filepath.Join(keysDirPath, fmt.Sprintf("*--%s", addrHex)))
- if len(matches) > 0 {
- if err == nil {
- keyFilePath = matches[len(matches)-1]
- }
- return
- }
- keyFilePath = filepath.Join(keysDirPath, addrHex, addrHex)
- _, err = os.Stat(keyFilePath)
- return
-}
-
-func cleanup(keysDirPath string, keyAddr common.Address) (err error) {
- fileInfos, err := ioutil.ReadDir(keysDirPath)
- if err != nil {
- return
- }
- var paths []string
- account := hex.EncodeToString(keyAddr[:])
- for _, fileInfo := range fileInfos {
- path := filepath.Join(keysDirPath, fileInfo.Name())
- if len(path) >= 40 {
- addr := path[len(path)-40 : len(path)]
- if addr == account {
- if path == filepath.Join(keysDirPath, addr, addr) {
- path = filepath.Join(keysDirPath, addr)
- }
- paths = append(paths, path)
- }
- }
- }
- if len(paths) > 1 {
- for i := 0; err == nil && i < len(paths)-1; i++ {
- err = os.RemoveAll(paths[i])
- if err != nil {
- break
- }
- }
- }
- return
-}
-
-func getKeyFile(keysDirPath string, keyAddr common.Address) (fileContent []byte, err error) {
- var keyFilePath string
- keyFilePath, err = getKeyFilePath(keysDirPath, keyAddr)
- if err == nil {
- fileContent, err = ioutil.ReadFile(keyFilePath)
- }
- return
-}
-
-func writeKeyFile(addr common.Address, keysDirPath string, content []byte) (err error) {
- filename := keyFileName(addr)
- // read, write and dir search for user
- err = os.MkdirAll(keysDirPath, 0700)
- if err != nil {
- return err
- }
- // read, write for user
- return ioutil.WriteFile(filepath.Join(keysDirPath, filename), content, 0600)
-}
-
-// keyFilePath implements the naming convention for keyfiles:
-// UTC--<created_at UTC ISO8601>-<address hex>
-func keyFileName(keyAddr common.Address) string {
- ts := time.Now().UTC()
- return fmt.Sprintf("UTC--%s--%s", toISO8601(ts), hex.EncodeToString(keyAddr[:]))
-}
-
-func toISO8601(t time.Time) string {
- var tz string
- name, offset := t.Zone()
- if name == "UTC" {
- tz = "Z"
- } else {
- tz = fmt.Sprintf("%03d00", offset/3600)
- }
- 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)
-}
-
-func getKeyAddresses(keysDirPath string) (addresses []common.Address, err error) {
- fileInfos, err := ioutil.ReadDir(keysDirPath)
- if err != nil {
- return nil, err
- }
- for _, fileInfo := range fileInfos {
- filename := fileInfo.Name()
- if len(filename) >= 40 {
- addr := filename[len(filename)-40 : len(filename)]
- address, err := hex.DecodeString(addr)
- if err == nil {
- addresses = append(addresses, common.BytesToAddress(address))
- }
- }
- }
- return addresses, err
-}
diff --git a/crypto/key_store_test.go b/crypto/key_store_test.go
deleted file mode 100644
index 5a44a6026..000000000
--- a/crypto/key_store_test.go
+++ /dev/null
@@ -1,233 +0,0 @@
-// 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 <http://www.gnu.org/licenses/>.
-
-package crypto
-
-import (
- "encoding/hex"
- "fmt"
- "reflect"
- "strings"
- "testing"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/crypto/randentropy"
-)
-
-func TestKeyStorePlain(t *testing.T) {
- ks := NewKeyStorePlain(common.DefaultDataDir())
- pass := "" // not used but required by API
- k1, err := ks.GenerateNewKey(randentropy.Reader, pass)
- if err != nil {
- t.Fatal(err)
- }
-
- k2 := new(Key)
- k2, err = ks.GetKey(k1.Address, pass)
- if err != nil {
- t.Fatal(err)
- }
-
- if !reflect.DeepEqual(k1.Address, k2.Address) {
- t.Fatal(err)
- }
-
- if !reflect.DeepEqual(k1.PrivateKey, k2.PrivateKey) {
- t.Fatal(err)
- }
-
- err = ks.DeleteKey(k2.Address, pass)
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func TestKeyStorePassphrase(t *testing.T) {
- ks := NewKeyStorePassphrase(common.DefaultDataDir(), LightScryptN, LightScryptP)
- pass := "foo"
- k1, err := ks.GenerateNewKey(randentropy.Reader, pass)
- if err != nil {
- t.Fatal(err)
- }
- k2 := new(Key)
- k2, err = ks.GetKey(k1.Address, pass)
- if err != nil {
- t.Fatal(err)
- }
- if !reflect.DeepEqual(k1.Address, k2.Address) {
- t.Fatal(err)
- }
-
- if !reflect.DeepEqual(k1.PrivateKey, k2.PrivateKey) {
- t.Fatal(err)
- }
-
- err = ks.DeleteKey(k2.Address, pass) // also to clean up created files
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func TestKeyStorePassphraseDecryptionFail(t *testing.T) {
- ks := NewKeyStorePassphrase(common.DefaultDataDir(), LightScryptN, LightScryptP)
- pass := "foo"
- k1, err := ks.GenerateNewKey(randentropy.Reader, pass)
- if err != nil {
- t.Fatal(err)
- }
-
- _, err = ks.GetKey(k1.Address, "bar") // wrong passphrase
- if err == nil {
- t.Fatal(err)
- }
-
- err = ks.DeleteKey(k1.Address, "bar") // wrong passphrase
- if err == nil {
- t.Fatal(err)
- }
-
- err = ks.DeleteKey(k1.Address, pass) // to clean up
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func TestImportPreSaleKey(t *testing.T) {
- // file content of a presale key file generated with:
- // python pyethsaletool.py genwallet
- // with password "foo"
- fileContent := "{\"encseed\": \"26d87f5f2bf9835f9a47eefae571bc09f9107bb13d54ff12a4ec095d01f83897494cf34f7bed2ed34126ecba9db7b62de56c9d7cd136520a0427bfb11b8954ba7ac39b90d4650d3448e31185affcd74226a68f1e94b1108e6e0a4a91cdd83eba\", \"ethaddr\": \"d4584b5f6229b7be90727b0fc8c6b91bb427821f\", \"email\": \"gustav.simonsson@gmail.com\", \"btcaddr\": \"1EVknXyFC68kKNLkh6YnKzW41svSRoaAcx\"}"
- ks := NewKeyStorePassphrase(common.DefaultDataDir(), LightScryptN, LightScryptP)
- pass := "foo"
- _, err := ImportPreSaleKey(ks, []byte(fileContent), pass)
- if err != nil {
- t.Fatal(err)
- }
-}
-
-// Test and utils for the key store tests in the Ethereum JSON tests;
-// tests/KeyStoreTests/basic_tests.json
-type KeyStoreTestV3 struct {
- Json encryptedKeyJSONV3
- Password string
- Priv string
-}
-
-type KeyStoreTestV1 struct {
- Json encryptedKeyJSONV1
- Password string
- Priv string
-}
-
-func TestV3_PBKDF2_1(t *testing.T) {
- tests := loadKeyStoreTestV3("tests/v3_test_vector.json", t)
- testDecryptV3(tests["wikipage_test_vector_pbkdf2"], t)
-}
-
-func TestV3_PBKDF2_2(t *testing.T) {
- tests := loadKeyStoreTestV3("../tests/files/KeyStoreTests/basic_tests.json", t)
- testDecryptV3(tests["test1"], t)
-}
-
-func TestV3_PBKDF2_3(t *testing.T) {
- tests := loadKeyStoreTestV3("../tests/files/KeyStoreTests/basic_tests.json", t)
- testDecryptV3(tests["python_generated_test_with_odd_iv"], t)
-}
-
-func TestV3_PBKDF2_4(t *testing.T) {
- tests := loadKeyStoreTestV3("../tests/files/KeyStoreTests/basic_tests.json", t)
- testDecryptV3(tests["evilnonce"], t)
-}
-
-func TestV3_Scrypt_1(t *testing.T) {
- tests := loadKeyStoreTestV3("tests/v3_test_vector.json", t)
- testDecryptV3(tests["wikipage_test_vector_scrypt"], t)
-}
-
-func TestV3_Scrypt_2(t *testing.T) {
- tests := loadKeyStoreTestV3("../tests/files/KeyStoreTests/basic_tests.json", t)
- testDecryptV3(tests["test2"], t)
-}
-
-func TestV1_1(t *testing.T) {
- tests := loadKeyStoreTestV1("tests/v1_test_vector.json", t)
- testDecryptV1(tests["test1"], t)
-}
-
-func TestV1_2(t *testing.T) {
- ks := NewKeyStorePassphrase("tests/v1", LightScryptN, LightScryptP)
- addr := common.HexToAddress("cb61d5a9c4896fb9658090b597ef0e7be6f7b67e")
- k, err := ks.GetKey(addr, "g")
- if err != nil {
- t.Fatal(err)
- }
- if k.Address != addr {
- t.Fatal(fmt.Errorf("Unexpected address: %v, expected %v", k.Address, addr))
- }
-
- privHex := hex.EncodeToString(FromECDSA(k.PrivateKey))
- expectedHex := "d1b1178d3529626a1a93e073f65028370d14c7eb0936eb42abef05db6f37ad7d"
- if privHex != expectedHex {
- t.Fatal(fmt.Errorf("Unexpected privkey: %v, expected %v", privHex, expectedHex))
- }
-}
-
-func testDecryptV3(test KeyStoreTestV3, t *testing.T) {
- privBytes, _, err := decryptKeyV3(&test.Json, test.Password)
- if err != nil {
- t.Fatal(err)
- }
- privHex := hex.EncodeToString(privBytes)
- if test.Priv != privHex {
- t.Fatal(fmt.Errorf("Decrypted bytes not equal to test, expected %v have %v", test.Priv, privHex))
- }
-}
-
-func testDecryptV1(test KeyStoreTestV1, t *testing.T) {
- privBytes, _, err := decryptKeyV1(&test.Json, test.Password)
- if err != nil {
- t.Fatal(err)
- }
- privHex := hex.EncodeToString(privBytes)
- if test.Priv != privHex {
- t.Fatal(fmt.Errorf("Decrypted bytes not equal to test, expected %v have %v", test.Priv, privHex))
- }
-}
-
-func loadKeyStoreTestV3(file string, t *testing.T) map[string]KeyStoreTestV3 {
- tests := make(map[string]KeyStoreTestV3)
- err := common.LoadJSON(file, &tests)
- if err != nil {
- t.Fatal(err)
- }
- return tests
-}
-
-func loadKeyStoreTestV1(file string, t *testing.T) map[string]KeyStoreTestV1 {
- tests := make(map[string]KeyStoreTestV1)
- err := common.LoadJSON(file, &tests)
- if err != nil {
- t.Fatal(err)
- }
- return tests
-}
-
-func TestKeyForDirectICAP(t *testing.T) {
- key := NewKeyForDirectICAP(randentropy.Reader)
- if !strings.HasPrefix(key.Address.Hex(), "0x00") {
- t.Errorf("Expected first address byte to be zero, have: %s", key.Address.Hex())
- }
-}
diff --git a/crypto/tests/v1/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e b/crypto/tests/v1/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e
deleted file mode 100644
index 498d8131e..000000000
--- a/crypto/tests/v1/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e
+++ /dev/null
@@ -1 +0,0 @@
-{"address":"cb61d5a9c4896fb9658090b597ef0e7be6f7b67e","Crypto":{"cipher":"aes-128-cbc","ciphertext":"6143d3192db8b66eabd693d9c4e414dcfaee52abda451af79ccf474dafb35f1bfc7ea013aa9d2ee35969a1a2e8d752d0","cipherparams":{"iv":"35337770fc2117994ecdcad026bccff4"},"kdf":"scrypt","kdfparams":{"n":262144,"r":8,"p":1,"dklen":32,"salt":"9afcddebca541253a2f4053391c673ff9fe23097cd8555d149d929e4ccf1257f"},"mac":"3f3d5af884b17a100b0b3232c0636c230a54dc2ac8d986227219b0dd89197644","version":"1"},"id":"e25f7c1f-d318-4f29-b62c-687190d4d299","version":"1"} \ No newline at end of file
diff --git a/crypto/tests/v1_test_vector.json b/crypto/tests/v1_test_vector.json
deleted file mode 100644
index 3d09b55b5..000000000
--- a/crypto/tests/v1_test_vector.json
+++ /dev/null
@@ -1,28 +0,0 @@
-{
- "test1": {
- "json": {
- "Crypto": {
- "cipher": "aes-128-cbc",
- "cipherparams": {
- "iv": "35337770fc2117994ecdcad026bccff4"
- },
- "ciphertext": "6143d3192db8b66eabd693d9c4e414dcfaee52abda451af79ccf474dafb35f1bfc7ea013aa9d2ee35969a1a2e8d752d0",
- "kdf": "scrypt",
- "kdfparams": {
- "dklen": 32,
- "n": 262144,
- "p": 1,
- "r": 8,
- "salt": "9afcddebca541253a2f4053391c673ff9fe23097cd8555d149d929e4ccf1257f"
- },
- "mac": "3f3d5af884b17a100b0b3232c0636c230a54dc2ac8d986227219b0dd89197644",
- "version": "1"
- },
- "address": "cb61d5a9c4896fb9658090b597ef0e7be6f7b67e",
- "id": "e25f7c1f-d318-4f29-b62c-687190d4d299",
- "version": "1"
- },
- "password": "g",
- "priv": "d1b1178d3529626a1a93e073f65028370d14c7eb0936eb42abef05db6f37ad7d"
- }
-}
diff --git a/crypto/tests/v3_test_vector.json b/crypto/tests/v3_test_vector.json
deleted file mode 100644
index e9d7b62f0..000000000
--- a/crypto/tests/v3_test_vector.json
+++ /dev/null
@@ -1,49 +0,0 @@
-{
- "wikipage_test_vector_scrypt": {
- "json": {
- "crypto" : {
- "cipher" : "aes-128-ctr",
- "cipherparams" : {
- "iv" : "83dbcc02d8ccb40e466191a123791e0e"
- },
- "ciphertext" : "d172bf743a674da9cdad04534d56926ef8358534d458fffccd4e6ad2fbde479c",
- "kdf" : "scrypt",
- "kdfparams" : {
- "dklen" : 32,
- "n" : 262144,
- "r" : 1,
- "p" : 8,
- "salt" : "ab0c7876052600dd703518d6fc3fe8984592145b591fc8fb5c6d43190334ba19"
- },
- "mac" : "2103ac29920d71da29f15d75b4a16dbe95cfd7ff8faea1056c33131d846e3097"
- },
- "id" : "3198bc9c-6672-5ab3-d995-4942343ae5b6",
- "version" : 3
- },
- "password": "testpassword",
- "priv": "7a28b5ba57c53603b0b07b56bba752f7784bf506fa95edc395f5cf6c7514fe9d"
- },
- "wikipage_test_vector_pbkdf2": {
- "json": {
- "crypto" : {
- "cipher" : "aes-128-ctr",
- "cipherparams" : {
- "iv" : "6087dab2f9fdbbfaddc31a909735c1e6"
- },
- "ciphertext" : "5318b4d5bcd28de64ee5559e671353e16f075ecae9f99c7a79a38af5f869aa46",
- "kdf" : "pbkdf2",
- "kdfparams" : {
- "c" : 262144,
- "dklen" : 32,
- "prf" : "hmac-sha256",
- "salt" : "ae3cd4e7013836a3df6bd7241b12db061dbe2c6785853cce422d148a624ce0bd"
- },
- "mac" : "517ead924a9d0dc3124507e3393d175ce3ff7c1e96529c6c555ce9e51205e9b2"
- },
- "id" : "3198bc9c-6672-5ab3-d995-4942343ae5b6",
- "version" : 3
- },
- "password": "testpassword",
- "priv": "7a28b5ba57c53603b0b07b56bba752f7784bf506fa95edc395f5cf6c7514fe9d"
- }
-}