From 945798f913d5cabd79635f45045b680b02396bf9 Mon Sep 17 00:00:00 2001 From: Gustav Simonsson Date: Wed, 31 Dec 2014 15:39:33 +0100 Subject: Add new key_store interface and two new key stores * Add new generic key_store interface * Add new plaintext key store storing unprotected keys on disk * Add new encrypted key store storing encrypted keys on disk * Add new entropy mixing function using OS and go runtime sources --- crypto/key.go | 205 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 205 insertions(+) create mode 100644 crypto/key.go (limited to 'crypto/key.go') diff --git a/crypto/key.go b/crypto/key.go new file mode 100644 index 000000000..d4845ee22 --- /dev/null +++ b/crypto/key.go @@ -0,0 +1,205 @@ +/* + This file is part of go-ethereum + + go-ethereum 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. + + go-ethereum 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 General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with go-ethereum. If not, see . +*/ +/** + * @authors + * Gustav Simonsson + * @date 2015 + * + */ + +package crypto + +import ( + "bytes" + "code.google.com/p/go-uuid/uuid" + "crypto/ecdsa" + "crypto/elliptic" + crand "crypto/rand" + "encoding/binary" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "io" + "os" + "runtime" + "strings" + "time" +) + +type Key struct { + Id *uuid.UUID // Version 4 "random" for unique id not derived from key data + Flags [4]byte // RFU + // we only store privkey as pubkey/address can be derived from it + // privkey in this struct is always in plaintext + PrivateKey *ecdsa.PrivateKey +} + +type KeyPlainJSON struct { + Id string + Flags string + PrivateKey string +} + +type CipherJSON struct { + Salt string + IV string + CipherText string +} + +type KeyProtectedJSON struct { + Id string + Flags string + Crypto CipherJSON +} + +func (k *Key) Address() []byte { + pubBytes := FromECDSAPub(&k.PrivateKey.PublicKey) + return Sha3(pubBytes)[12:] +} + +func (k *Key) MarshalJSON() (j []byte, err error) { + stringStruct := KeyPlainJSON{ + k.Id.String(), + hex.EncodeToString(k.Flags[:]), + hex.EncodeToString(FromECDSA(k.PrivateKey)), + } + j, _ = json.Marshal(stringStruct) + return +} + +func (k *Key) UnmarshalJSON(j []byte) (err error) { + keyJSON := new(KeyPlainJSON) + err = json.Unmarshal(j, &keyJSON) + if err != nil { + return + } + + u := new(uuid.UUID) + *u = uuid.Parse(keyJSON.Id) + if *u == nil { + err = errors.New("UUID parsing failed") + return + } + k.Id = u + + flagsBytes, err := hex.DecodeString(keyJSON.Flags) + if err != nil { + return + } + + PrivateKeyBytes, err := hex.DecodeString(keyJSON.PrivateKey) + if err != nil { + return + } + + copy(k.Flags[:], flagsBytes[0:4]) + k.PrivateKey = ToECDSA(PrivateKeyBytes) + + return +} + +func NewKey() *Key { + randBytes := GetEntropyCSPRNG(32) + reader := bytes.NewReader(randBytes) + _, x, y, err := elliptic.GenerateKey(S256(), reader) + if err != nil { + panic("key generation: elliptic.GenerateKey failed: " + err.Error()) + } + privateKeyMarshalled := elliptic.Marshal(S256(), x, y) + privateKeyECDSA := ToECDSA(privateKeyMarshalled) + + key := new(Key) + id := uuid.NewRandom() + key.Id = &id + // flags := new([4]byte) + // key.Flags = flags + key.PrivateKey = privateKeyECDSA + return key +} + +// plain crypto/rand. this is /dev/urandom on Unix-like systems. +func GetEntropyCSPRNG(n int) []byte { + mainBuff := make([]byte, n) + _, err := io.ReadFull(crand.Reader, mainBuff) + if err != nil { + panic("key generation: reading from crypto/rand failed: " + err.Error()) + } + return mainBuff +} + +// TODO: verify. Do not use until properly discussed. +// we start with crypt/rand, then mix in additional sources of entropy. +// These sources are from three types: OS, go runtime and ethereum client state. +func GetEntropyTinFoilHat() []byte { + startTime := time.Now().UnixNano() + // for each source, we XOR in it's SHA3 hash. + mainBuff := GetEntropyCSPRNG(32) + // 1. OS entropy sources + startTimeBytes := make([]byte, 32) + binary.PutVarint(startTimeBytes, startTime) + startTimeHash := Sha3(startTimeBytes) + mix32Byte(mainBuff, startTimeHash) + + pid := os.Getpid() + pidBytes := make([]byte, 32) + binary.PutUvarint(pidBytes, uint64(pid)) + pidHash := Sha3(pidBytes) + mix32Byte(mainBuff, pidHash) + + osEnv := os.Environ() + osEnvBytes := []byte(strings.Join(osEnv, "")) + osEnvHash := Sha3(osEnvBytes) + mix32Byte(mainBuff, osEnvHash) + + // not all OS have hostname in env variables + osHostName, err := os.Hostname() + if err != nil { + osHostNameBytes := []byte(osHostName) + osHostNameHash := Sha3(osHostNameBytes) + mix32Byte(mainBuff, osHostNameHash) + } + + // 2. go runtime entropy sources + memStats := new(runtime.MemStats) + runtime.ReadMemStats(memStats) + memStatsBytes := []byte(fmt.Sprintf("%v", memStats)) + memStatsHash := Sha3(memStatsBytes) + mix32Byte(mainBuff, memStatsHash) + + // 3. Mix in ethereum / client state + // TODO: list of network peers structs (IP, port, etc) + // TODO: merkle patricia tree root hash for world state and tx list + + // 4. Yo dawg we heard you like entropy so we'll grab some entropy from how + // long it took to grab the above entropy. And a yield, for good measure. + runtime.Gosched() + diffTime := time.Now().UnixNano() - startTime + diffTimeBytes := make([]byte, 32) + binary.PutVarint(diffTimeBytes, diffTime) + diffTimeHash := Sha3(diffTimeBytes) + mix32Byte(mainBuff, diffTimeHash) + + return mainBuff +} + +func mix32Byte(buff []byte, mixBuff []byte) []byte { + for i := 0; i < 32; i++ { + buff[i] ^= mixBuff[i] + } + return buff +} -- cgit v1.2.3 From a1c2749380523178f87ae3fdfb02bc6641362924 Mon Sep 17 00:00:00 2001 From: Gustav Simonsson Date: Wed, 7 Jan 2015 16:06:26 +0100 Subject: Address pull request comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Simplify scrypt constants with const block * Add key store constructors and make their types private * Simplify key store and file namings to be less Java Enterpriseā„¢ * Change test error logging to use t.Error(err) * Reduce number of naked returns (just like my ex-gf) * Simplify file reading path code --- crypto/key.go | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'crypto/key.go') diff --git a/crypto/key.go b/crypto/key.go index d4845ee22..1a8b770e0 100644 --- a/crypto/key.go +++ b/crypto/key.go @@ -49,7 +49,7 @@ type Key struct { PrivateKey *ecdsa.PrivateKey } -type KeyPlainJSON struct { +type PlainKeyJSON struct { Id string Flags string PrivateKey string @@ -61,7 +61,7 @@ type CipherJSON struct { CipherText string } -type KeyProtectedJSON struct { +type EncryptedKeyJSON struct { Id string Flags string Crypto CipherJSON @@ -73,44 +73,44 @@ func (k *Key) Address() []byte { } func (k *Key) MarshalJSON() (j []byte, err error) { - stringStruct := KeyPlainJSON{ + stringStruct := PlainKeyJSON{ k.Id.String(), hex.EncodeToString(k.Flags[:]), hex.EncodeToString(FromECDSA(k.PrivateKey)), } - j, _ = json.Marshal(stringStruct) - return + j, err = json.Marshal(stringStruct) + return j, err } func (k *Key) UnmarshalJSON(j []byte) (err error) { - keyJSON := new(KeyPlainJSON) + keyJSON := new(PlainKeyJSON) err = json.Unmarshal(j, &keyJSON) if err != nil { - return + return err } u := new(uuid.UUID) *u = uuid.Parse(keyJSON.Id) if *u == nil { err = errors.New("UUID parsing failed") - return + return err } k.Id = u flagsBytes, err := hex.DecodeString(keyJSON.Flags) if err != nil { - return + return err } PrivateKeyBytes, err := hex.DecodeString(keyJSON.PrivateKey) if err != nil { - return + return err } copy(k.Flags[:], flagsBytes[0:4]) k.PrivateKey = ToECDSA(PrivateKeyBytes) - return + return err } func NewKey() *Key { -- cgit v1.2.3 From 47d3b3dd58172c2e7c1f72fb072bd9385aff8205 Mon Sep 17 00:00:00 2001 From: Gustav Simonsson Date: Thu, 15 Jan 2015 17:45:45 +0100 Subject: Address pull request comments * Remove flags field from key struct * Change JSON struct fields from string to []byte * Change GenerateNewKey API to take io.Reader for random source * Remove mixing entropy source function * Use testing Fatal in tests --- crypto/key.go | 150 +++++++++++----------------------------------------------- 1 file changed, 28 insertions(+), 122 deletions(-) (limited to 'crypto/key.go') diff --git a/crypto/key.go b/crypto/key.go index 1a8b770e0..0e7c04275 100644 --- a/crypto/key.go +++ b/crypto/key.go @@ -28,43 +28,31 @@ import ( "code.google.com/p/go-uuid/uuid" "crypto/ecdsa" "crypto/elliptic" - crand "crypto/rand" - "encoding/binary" - "encoding/hex" "encoding/json" - "errors" - "fmt" "io" - "os" - "runtime" - "strings" - "time" ) type Key struct { - Id *uuid.UUID // Version 4 "random" for unique id not derived from key data - Flags [4]byte // RFU + Id *uuid.UUID // Version 4 "random" for unique id not derived from key data // 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 { - Id string - Flags string - PrivateKey string +type plainKeyJSON struct { + Id []byte + PrivateKey []byte } -type CipherJSON struct { - Salt string - IV string - CipherText string +type cipherJSON struct { + Salt []byte + IV []byte + CipherText []byte } -type EncryptedKeyJSON struct { - Id string - Flags string - Crypto CipherJSON +type encryptedKeyJSON struct { + Id []byte + Crypto cipherJSON } func (k *Key) Address() []byte { @@ -73,48 +61,40 @@ func (k *Key) Address() []byte { } func (k *Key) MarshalJSON() (j []byte, err error) { - stringStruct := PlainKeyJSON{ - k.Id.String(), - hex.EncodeToString(k.Flags[:]), - hex.EncodeToString(FromECDSA(k.PrivateKey)), + jStruct := plainKeyJSON{ + *k.Id, + FromECDSA(k.PrivateKey), } - j, err = json.Marshal(stringStruct) + j, err = json.Marshal(jStruct) return j, err } func (k *Key) UnmarshalJSON(j []byte) (err error) { - keyJSON := new(PlainKeyJSON) + keyJSON := new(plainKeyJSON) err = json.Unmarshal(j, &keyJSON) if err != nil { return err } u := new(uuid.UUID) - *u = uuid.Parse(keyJSON.Id) - if *u == nil { - err = errors.New("UUID parsing failed") - return err - } + *u = keyJSON.Id k.Id = u - flagsBytes, err := hex.DecodeString(keyJSON.Flags) - if err != nil { - return err - } - - PrivateKeyBytes, err := hex.DecodeString(keyJSON.PrivateKey) - if err != nil { - return err - } - - copy(k.Flags[:], flagsBytes[0:4]) - k.PrivateKey = ToECDSA(PrivateKeyBytes) + k.PrivateKey = ToECDSA(keyJSON.PrivateKey) return err } -func NewKey() *Key { - randBytes := GetEntropyCSPRNG(32) +func NewKey(rand io.Reader) *Key { + randBytes := make([]byte, 32) + n, err := rand.Read(randBytes) + if err != nil { + panic("key generation: could not read from random source: " + err.Error()) + } else { + if n != 32 { + panic("key generation: read less than required bytes from random source: " + err.Error()) + } + } reader := bytes.NewReader(randBytes) _, x, y, err := elliptic.GenerateKey(S256(), reader) if err != nil { @@ -126,80 +106,6 @@ func NewKey() *Key { key := new(Key) id := uuid.NewRandom() key.Id = &id - // flags := new([4]byte) - // key.Flags = flags key.PrivateKey = privateKeyECDSA return key } - -// plain crypto/rand. this is /dev/urandom on Unix-like systems. -func GetEntropyCSPRNG(n int) []byte { - mainBuff := make([]byte, n) - _, err := io.ReadFull(crand.Reader, mainBuff) - if err != nil { - panic("key generation: reading from crypto/rand failed: " + err.Error()) - } - return mainBuff -} - -// TODO: verify. Do not use until properly discussed. -// we start with crypt/rand, then mix in additional sources of entropy. -// These sources are from three types: OS, go runtime and ethereum client state. -func GetEntropyTinFoilHat() []byte { - startTime := time.Now().UnixNano() - // for each source, we XOR in it's SHA3 hash. - mainBuff := GetEntropyCSPRNG(32) - // 1. OS entropy sources - startTimeBytes := make([]byte, 32) - binary.PutVarint(startTimeBytes, startTime) - startTimeHash := Sha3(startTimeBytes) - mix32Byte(mainBuff, startTimeHash) - - pid := os.Getpid() - pidBytes := make([]byte, 32) - binary.PutUvarint(pidBytes, uint64(pid)) - pidHash := Sha3(pidBytes) - mix32Byte(mainBuff, pidHash) - - osEnv := os.Environ() - osEnvBytes := []byte(strings.Join(osEnv, "")) - osEnvHash := Sha3(osEnvBytes) - mix32Byte(mainBuff, osEnvHash) - - // not all OS have hostname in env variables - osHostName, err := os.Hostname() - if err != nil { - osHostNameBytes := []byte(osHostName) - osHostNameHash := Sha3(osHostNameBytes) - mix32Byte(mainBuff, osHostNameHash) - } - - // 2. go runtime entropy sources - memStats := new(runtime.MemStats) - runtime.ReadMemStats(memStats) - memStatsBytes := []byte(fmt.Sprintf("%v", memStats)) - memStatsHash := Sha3(memStatsBytes) - mix32Byte(mainBuff, memStatsHash) - - // 3. Mix in ethereum / client state - // TODO: list of network peers structs (IP, port, etc) - // TODO: merkle patricia tree root hash for world state and tx list - - // 4. Yo dawg we heard you like entropy so we'll grab some entropy from how - // long it took to grab the above entropy. And a yield, for good measure. - runtime.Gosched() - diffTime := time.Now().UnixNano() - startTime - diffTimeBytes := make([]byte, 32) - binary.PutVarint(diffTimeBytes, diffTime) - diffTimeHash := Sha3(diffTimeBytes) - mix32Byte(mainBuff, diffTimeHash) - - return mainBuff -} - -func mix32Byte(buff []byte, mixBuff []byte) []byte { - for i := 0; i < 32; i++ { - buff[i] ^= mixBuff[i] - } - return buff -} -- cgit v1.2.3 From 3cf038f300950c5a14b99fb0249ec09f663ef6af Mon Sep 17 00:00:00 2001 From: Gustav Simonsson Date: Mon, 19 Jan 2015 20:24:30 +0100 Subject: Address pull request comments * Allocate with composite literal instead of new * Remove check of number of bytes read from rand --- crypto/key.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'crypto/key.go') diff --git a/crypto/key.go b/crypto/key.go index 0e7c04275..d371ad4dc 100644 --- a/crypto/key.go +++ b/crypto/key.go @@ -87,13 +87,9 @@ func (k *Key) UnmarshalJSON(j []byte) (err error) { func NewKey(rand io.Reader) *Key { randBytes := make([]byte, 32) - n, err := rand.Read(randBytes) + _, err := rand.Read(randBytes) if err != nil { panic("key generation: could not read from random source: " + err.Error()) - } else { - if n != 32 { - panic("key generation: read less than required bytes from random source: " + err.Error()) - } } reader := bytes.NewReader(randBytes) _, x, y, err := elliptic.GenerateKey(S256(), reader) -- cgit v1.2.3 From 1f8290ca44df31cc4c4b68b30eef412476ed24e0 Mon Sep 17 00:00:00 2001 From: Gustav Simonsson Date: Tue, 20 Jan 2015 23:55:13 +0100 Subject: Add ImportPreSaleKey * ImportPreSaleKey takes a KeyStore, a presale key JSON (e.g. file content) and a password string. It stores the key in the given key store. * Refactored common AES decryption and moved some functions to crypto.go --- crypto/key.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'crypto/key.go') diff --git a/crypto/key.go b/crypto/key.go index d371ad4dc..ca29b691f 100644 --- a/crypto/key.go +++ b/crypto/key.go @@ -57,7 +57,7 @@ type encryptedKeyJSON struct { func (k *Key) Address() []byte { pubBytes := FromECDSAPub(&k.PrivateKey.PublicKey) - return Sha3(pubBytes)[12:] + return Sha3(pubBytes[1:])[12:] } func (k *Key) MarshalJSON() (j []byte, err error) { @@ -99,9 +99,10 @@ func NewKey(rand io.Reader) *Key { privateKeyMarshalled := elliptic.Marshal(S256(), x, y) privateKeyECDSA := ToECDSA(privateKeyMarshalled) - key := new(Key) id := uuid.NewRandom() - key.Id = &id - key.PrivateKey = privateKeyECDSA + key := &Key{ + Id: &id, + PrivateKey: privateKeyECDSA, + } return key } -- cgit v1.2.3 From 512ffa2bf4308b44aa6f43f25238b375b58d7dbc Mon Sep 17 00:00:00 2001 From: Gustav Simonsson Date: Sun, 25 Jan 2015 02:07:20 +0100 Subject: Add accounts package and refactor key stores * Add initial UserAccount and AccountManager structs * Add NewAccount, Sign and Accounts functions * Refactor key stores to use key address as main identifier while keeping the UUID. * Use key address as file/dir names instead of UUID --- crypto/key.go | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) (limited to 'crypto/key.go') diff --git a/crypto/key.go b/crypto/key.go index ca29b691f..f8f64c35c 100644 --- a/crypto/key.go +++ b/crypto/key.go @@ -33,7 +33,9 @@ import ( ) type Key struct { - Id *uuid.UUID // Version 4 "random" for unique id not derived from key data + Id uuid.UUID // Version 4 "random" for unique id not derived from key data + // to simplify lookups we also store the address + Address []byte // we only store privkey as pubkey/address can be derived from it // privkey in this struct is always in plaintext PrivateKey *ecdsa.PrivateKey @@ -41,6 +43,7 @@ type Key struct { type plainKeyJSON struct { Id []byte + Address []byte PrivateKey []byte } @@ -51,18 +54,15 @@ type cipherJSON struct { } type encryptedKeyJSON struct { - Id []byte - Crypto cipherJSON -} - -func (k *Key) Address() []byte { - pubBytes := FromECDSAPub(&k.PrivateKey.PublicKey) - return Sha3(pubBytes[1:])[12:] + Id []byte + Address []byte + Crypto cipherJSON } func (k *Key) MarshalJSON() (j []byte, err error) { jStruct := plainKeyJSON{ - *k.Id, + k.Id, + k.Address, FromECDSA(k.PrivateKey), } j, err = json.Marshal(jStruct) @@ -78,8 +78,8 @@ func (k *Key) UnmarshalJSON(j []byte) (err error) { u := new(uuid.UUID) *u = keyJSON.Id - k.Id = u - + k.Id = *u + k.Address = keyJSON.Address k.PrivateKey = ToECDSA(keyJSON.PrivateKey) return err @@ -101,7 +101,8 @@ func NewKey(rand io.Reader) *Key { id := uuid.NewRandom() key := &Key{ - Id: &id, + Id: id, + Address: pubkeyToAddress(privateKeyECDSA.PublicKey), PrivateKey: privateKeyECDSA, } return key -- cgit v1.2.3 From 8d9752a557e33341a5fb73239dbae664b2f8aaa0 Mon Sep 17 00:00:00 2001 From: Gustav Simonsson Date: Wed, 28 Jan 2015 05:12:57 +0100 Subject: Address pull request comments * Use crypto.Sign instead of directly calling secp256k1 lib * Rename UserAccount to Account and Addr to Address (for consistency) * Change AccountManager.Sign to take ptr to Account instead of address byte array * Simplify copying of Accounts in Accounts() * PubkeyToAddress and GetEntropyCSPRNG now exported --- crypto/key.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'crypto/key.go') diff --git a/crypto/key.go b/crypto/key.go index f8f64c35c..b9ad34f47 100644 --- a/crypto/key.go +++ b/crypto/key.go @@ -102,7 +102,7 @@ func NewKey(rand io.Reader) *Key { id := uuid.NewRandom() key := &Key{ Id: id, - Address: pubkeyToAddress(privateKeyECDSA.PublicKey), + Address: PubkeyToAddress(privateKeyECDSA.PublicKey), PrivateKey: privateKeyECDSA, } return key -- cgit v1.2.3 From 0c7df37351ab85c577fe815d6d22f4627832b0c3 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Tue, 10 Feb 2015 12:29:50 +0100 Subject: crypto: add key loading functions --- crypto/key.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'crypto/key.go') diff --git a/crypto/key.go b/crypto/key.go index b9ad34f47..ec4908c30 100644 --- a/crypto/key.go +++ b/crypto/key.go @@ -25,11 +25,12 @@ package crypto import ( "bytes" - "code.google.com/p/go-uuid/uuid" "crypto/ecdsa" "crypto/elliptic" "encoding/json" "io" + + "code.google.com/p/go-uuid/uuid" ) type Key struct { -- cgit v1.2.3