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 +-- crypto/key_store_passphrase.go | 253 ++++++++++++++++++++++++++++++++ crypto/key_store_passphrase_encryped.go | 244 ------------------------------ crypto/key_store_plain.go | 114 ++++++++++++++ crypto/key_store_plaintext_file.go | 113 -------------- crypto/key_store_test.go | 31 ++-- 6 files changed, 392 insertions(+), 385 deletions(-) create mode 100644 crypto/key_store_passphrase.go delete mode 100644 crypto/key_store_passphrase_encryped.go create mode 100644 crypto/key_store_plain.go delete mode 100644 crypto/key_store_plaintext_file.go (limited to 'crypto') 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 { diff --git a/crypto/key_store_passphrase.go b/crypto/key_store_passphrase.go new file mode 100644 index 000000000..eaf73422f --- /dev/null +++ b/crypto/key_store_passphrase.go @@ -0,0 +1,253 @@ +/* + 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 + * + */ +/* + +This key store behaves as KeyStorePlaintextFile with the difference that +the private key is encrypted and encoded as a JSON object within the +key JSON object. + +Cryptography: + +1. Encryption key is scrypt derived key from user passphrase. Scrypt parameters + (work factors) [1][2] are defined as constants below. +2. Scrypt salt is 32 random bytes from CSPRNG. It is appended to ciphertext. +3. Checksum is SHA3 of the private key bytes. +4. Plaintext is concatenation of private key bytes and checksum. +5. Encryption algo is AES 256 CBC [3][4] +6. CBC IV is 16 random bytes from CSPRNG. It is appended to ciphertext. +7. Plaintext padding is PKCS #7 [5][6] + +Encoding: + +1. On disk, ciphertext, salt and IV are encoded as a JSON object. + cat a key file to see the structure. +2. byte arrays are ASCII HEX encoded as JSON strings. +3. The EC private key bytes are in uncompressed form [7]. + They are a big-endian byte slice of the absolute value of D [8][9]. +4. The checksum is the last 32 bytes of the plaintext byte array and the + private key is the preceeding bytes. + +References: + +1. http://www.tarsnap.com/scrypt/scrypt-slides.pdf +2. http://stackoverflow.com/questions/11126315/what-are-optimal-scrypt-work-factors +3. http://en.wikipedia.org/wiki/Advanced_Encryption_Standard +4. http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher-block_chaining_.28CBC.29 +5. https://leanpub.com/gocrypto/read#leanpub-auto-block-cipher-modes +6. http://tools.ietf.org/html/rfc2315 +7. http://bitcoin.stackexchange.com/questions/3059/what-is-a-compressed-bitcoin-key +8. http://golang.org/pkg/crypto/ecdsa/#PrivateKey +9. https://golang.org/pkg/math/big/#Int.Bytes + +*/ + +package crypto + +import ( + "bytes" + "code.google.com/p/go-uuid/uuid" + "code.google.com/p/go.crypto/scrypt" + "crypto/aes" + "crypto/cipher" + "encoding/hex" + "encoding/json" + "errors" + "os" + "path" +) + +const ( + // 2^18 / 8 / 1 uses 256MB memory and approx 1s CPU time on a modern CPU. + scryptN = 1 << 18 + scryptr = 8 + scryptp = 1 + scryptdkLen = 32 +) + +type keyStorePassphrase struct { + keysDirPath string +} + +func NewKeyStorePassphrase(path string) KeyStore2 { + ks := new(keyStorePassphrase) + ks.keysDirPath = path + return ks +} + +func (ks keyStorePassphrase) GenerateNewKey(auth string) (key *Key, err error) { + return GenerateNewKeyDefault(ks, auth) +} + +func (ks keyStorePassphrase) GetKey(keyId *uuid.UUID, auth string) (key *Key, err error) { + keyBytes, flags, err := DecryptKey(ks, keyId, auth) + if err != nil { + return nil, err + } + key = new(Key) + key.Id = keyId + copy(key.Flags[:], flags[0:4]) + key.PrivateKey = ToECDSA(keyBytes) + return key, err +} + +func (ks keyStorePassphrase) StoreKey(key *Key, auth string) (err error) { + authArray := []byte(auth) + salt := GetEntropyCSPRNG(32) + derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptr, scryptp, scryptdkLen) + if err != nil { + return err + } + + keyBytes := FromECDSA(key.PrivateKey) + keyBytesHash := Sha3(keyBytes) + toEncrypt := PKCS7Pad(append(keyBytes, keyBytesHash...)) + + AES256Block, err := aes.NewCipher(derivedKey) + if err != nil { + return err + } + + iv := GetEntropyCSPRNG(aes.BlockSize) // 16 + AES256CBCEncrypter := cipher.NewCBCEncrypter(AES256Block, iv) + cipherText := make([]byte, len(toEncrypt)) + AES256CBCEncrypter.CryptBlocks(cipherText, toEncrypt) + + cipherStruct := CipherJSON{ + hex.EncodeToString(salt), + hex.EncodeToString(iv), + hex.EncodeToString(cipherText), + } + keyStruct := EncryptedKeyJSON{ + key.Id.String(), + hex.EncodeToString(key.Flags[:]), + cipherStruct, + } + keyJSON, err := json.Marshal(keyStruct) + if err != nil { + return err + } + + return WriteKeyFile(key.Id.String(), ks.keysDirPath, keyJSON) +} + +func (ks keyStorePassphrase) DeleteKey(keyId *uuid.UUID, auth string) (err error) { + // only delete if correct passphrase is given + _, _, err = DecryptKey(ks, keyId, auth) + if err != nil { + return err + } + + keyDirPath := path.Join(ks.keysDirPath, keyId.String()) + return os.RemoveAll(keyDirPath) +} + +func DecryptKey(ks keyStorePassphrase, keyId *uuid.UUID, auth string) (keyBytes []byte, flags []byte, err error) { + fileContent, err := GetKeyFile(ks.keysDirPath, keyId) + if err != nil { + return nil, nil, err + } + + keyProtected := new(EncryptedKeyJSON) + err = json.Unmarshal(fileContent, keyProtected) + + flags, err = hex.DecodeString(keyProtected.Flags) + if err != nil { + return nil, nil, err + } + + salt, err := hex.DecodeString(keyProtected.Crypto.Salt) + if err != nil { + return nil, nil, err + } + + iv, err := hex.DecodeString(keyProtected.Crypto.IV) + if err != nil { + return nil, nil, err + } + + cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText) + if err != nil { + return nil, nil, err + } + + authArray := []byte(auth) + derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptr, scryptp, scryptdkLen) + if err != nil { + return nil, nil, err + } + + AES256Block, err := aes.NewCipher(derivedKey) + if err != nil { + return nil, nil, err + } + + AES256CBCDecrypter := cipher.NewCBCDecrypter(AES256Block, iv) + paddedPlainText := make([]byte, len(cipherText)) + AES256CBCDecrypter.CryptBlocks(paddedPlainText, cipherText) + + plainText := PKCS7Unpad(paddedPlainText) + if plainText == nil { + err = errors.New("Decryption failed: PKCS7Unpad failed after decryption") + return nil, nil, err + } + + keyBytes = plainText[:len(plainText)-32] + keyBytesHash := plainText[len(plainText)-32:] + if !bytes.Equal(Sha3(keyBytes), keyBytesHash) { + err = errors.New("Decryption failed: checksum mismatch") + return nil, nil, err + } + return keyBytes, flags, err +} + +// From https://leanpub.com/gocrypto/read#leanpub-auto-block-cipher-modes +func PKCS7Pad(in []byte) []byte { + padding := 16 - (len(in) % 16) + if padding == 0 { + padding = 16 + } + for i := 0; i < padding; i++ { + in = append(in, byte(padding)) + } + return in +} + +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)] +} diff --git a/crypto/key_store_passphrase_encryped.go b/crypto/key_store_passphrase_encryped.go deleted file mode 100644 index b17536c3f..000000000 --- a/crypto/key_store_passphrase_encryped.go +++ /dev/null @@ -1,244 +0,0 @@ -/* - 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 - * - */ -/* - -This key store behaves as KeyStorePlaintextFile with the difference that -the private key is encrypted and encoded as a JSON object within the -key JSON object. - -Cryptography: - -1. Encryption key is scrypt derived key from user passphrase. Scrypt parameters - (work factors) [1][2] are defined as constants below. -2. Scrypt salt is 32 random bytes from CSPRNG. It is appended to ciphertext. -3. Checksum is SHA3 of the private key bytes. -4. Plaintext is concatenation of private key bytes and checksum. -5. Encryption algo is AES 256 CBC [3][4] -6. CBC IV is 16 random bytes from CSPRNG. It is appended to ciphertext. -7. Plaintext padding is PKCS #7 [5][6] - -Encoding: - -1. On disk, ciphertext, salt and IV are encoded as a JSON object. - cat a key file to see the structure. -2. byte arrays are ASCII HEX encoded as JSON strings. -3. The EC private key bytes are in uncompressed form [7]. - They are a big-endian byte slice of the absolute value of D [8][9]. -4. The checksum is the last 32 bytes of the plaintext byte array and the - private key is the preceeding bytes. - -References: - -1. http://www.tarsnap.com/scrypt/scrypt-slides.pdf -2. http://stackoverflow.com/questions/11126315/what-are-optimal-scrypt-work-factors -3. http://en.wikipedia.org/wiki/Advanced_Encryption_Standard -4. http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher-block_chaining_.28CBC.29 -5. https://leanpub.com/gocrypto/read#leanpub-auto-block-cipher-modes -6. http://tools.ietf.org/html/rfc2315 -7. http://bitcoin.stackexchange.com/questions/3059/what-is-a-compressed-bitcoin-key -8. http://golang.org/pkg/crypto/ecdsa/#PrivateKey -9. https://golang.org/pkg/math/big/#Int.Bytes - -*/ - -package crypto - -import ( - "bytes" - "code.google.com/p/go-uuid/uuid" - "code.google.com/p/go.crypto/scrypt" - "crypto/aes" - "crypto/cipher" - "encoding/hex" - "encoding/json" - "errors" - "os" - "path" -) - -const scryptN int = 262144 // 2^18 -const scryptr int = 8 -const scryptp int = 1 -const scryptdkLen int = 32 - -type KeyStorePassphrase struct { - keysDirPath string -} - -func (ks KeyStorePassphrase) GenerateNewKey(auth string) (key *Key, err error) { - key, err = GenerateNewKeyDefault(ks, auth) - return -} - -func (ks KeyStorePassphrase) GetKey(keyId *uuid.UUID, auth string) (key *Key, err error) { - keyBytes, flags, err := DecryptKey(ks, keyId, auth) - key = new(Key) - key.Id = keyId - copy(key.Flags[:], flags[0:4]) - key.PrivateKey = ToECDSA(keyBytes) - return -} - -func (ks KeyStorePassphrase) StoreKey(key *Key, auth string) (err error) { - authArray := []byte(auth) - salt := GetEntropyCSPRNG(32) - derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptr, scryptp, scryptdkLen) - if err != nil { - return - } - - keyBytes := FromECDSA(key.PrivateKey) - keyBytesHash := Sha3(keyBytes) - toEncrypt := PKCS7Pad(append(keyBytes, keyBytesHash...)) - - AES256Block, err := aes.NewCipher(derivedKey) - if err != nil { - return - } - - iv := GetEntropyCSPRNG(aes.BlockSize) // 16 - AES256CBCEncrypter := cipher.NewCBCEncrypter(AES256Block, iv) - cipherText := make([]byte, len(toEncrypt)) - AES256CBCEncrypter.CryptBlocks(cipherText, toEncrypt) - - cipherStruct := CipherJSON{ - hex.EncodeToString(salt), - hex.EncodeToString(iv), - hex.EncodeToString(cipherText), - } - keyStruct := KeyProtectedJSON{ - key.Id.String(), - hex.EncodeToString(key.Flags[:]), - cipherStruct, - } - keyJSON, err := json.Marshal(keyStruct) - if err != nil { - return - } - - err = WriteKeyFile(key.Id.String(), ks.keysDirPath, keyJSON) - return -} - -func (ks KeyStorePassphrase) DeleteKey(keyId *uuid.UUID, auth string) (err error) { - // only delete if correct passphrase is given - _, _, err = DecryptKey(ks, keyId, auth) - if err != nil { - return - } - - keyDirPath := path.Join(ks.keysDirPath, keyId.String()) - err = os.RemoveAll(keyDirPath) - return -} - -func DecryptKey(ks KeyStorePassphrase, keyId *uuid.UUID, auth string) (keyBytes []byte, flags []byte, err error) { - fileContent, err := GetKeyFile(ks.keysDirPath, keyId) - if err != nil { - return - } - - keyProtected := new(KeyProtectedJSON) - err = json.Unmarshal(fileContent, keyProtected) - - flags, err = hex.DecodeString(keyProtected.Flags) - if err != nil { - return - } - - salt, err := hex.DecodeString(keyProtected.Crypto.Salt) - if err != nil { - return - } - - iv, err := hex.DecodeString(keyProtected.Crypto.IV) - if err != nil { - return - } - - cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText) - if err != nil { - return - } - - authArray := []byte(auth) - derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptr, scryptp, scryptdkLen) - if err != nil { - return - } - - AES256Block, err := aes.NewCipher(derivedKey) - if err != nil { - return - } - - AES256CBCDecrypter := cipher.NewCBCDecrypter(AES256Block, iv) - paddedPlainText := make([]byte, len(cipherText)) - AES256CBCDecrypter.CryptBlocks(paddedPlainText, cipherText) - - plainText := PKCS7Unpad(paddedPlainText) - if plainText == nil { - err = errors.New("Decryption failed: PKCS7Unpad failed after decryption") - return - } - - keyBytes = plainText[:len(plainText)-32] - keyBytesHash := plainText[len(plainText)-32:] - if !bytes.Equal(Sha3(keyBytes), keyBytesHash) { - err = errors.New("Decryption failed: checksum mismatch") - return - } - return keyBytes, flags, err -} - -// From https://leanpub.com/gocrypto/read#leanpub-auto-block-cipher-modes -func PKCS7Pad(in []byte) []byte { - padding := 16 - (len(in) % 16) - if padding == 0 { - padding = 16 - } - for i := 0; i < padding; i++ { - in = append(in, byte(padding)) - } - return in -} - -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)] -} diff --git a/crypto/key_store_plain.go b/crypto/key_store_plain.go new file mode 100644 index 000000000..00d9767b6 --- /dev/null +++ b/crypto/key_store_plain.go @@ -0,0 +1,114 @@ +/* + 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 ( + "code.google.com/p/go-uuid/uuid" + "encoding/json" + "fmt" + "io/ioutil" + "os" + "os/user" + "path" +) + +// TODO: rename to KeyStore when replacing existing KeyStore +type KeyStore2 interface { + GenerateNewKey(string) (*Key, error) // create and store new key, optionally using auth string + GetKey(*uuid.UUID, string) (*Key, error) // key from id and auth string + StoreKey(*Key, string) error // store key optionally using auth string + DeleteKey(*uuid.UUID, string) error // delete key by id and auth string +} + +type keyStorePlain struct { + keysDirPath string +} + +// TODO: copied from cmd/ethereum/flags.go +func DefaultDataDir() string { + usr, _ := user.Current() + return path.Join(usr.HomeDir, ".ethereum") +} + +func NewKeyStorePlain(path string) KeyStore2 { + ks := new(keyStorePlain) + ks.keysDirPath = path + return ks +} + +func (ks keyStorePlain) GenerateNewKey(auth string) (key *Key, err error) { + return GenerateNewKeyDefault(ks, auth) +} + +func GenerateNewKeyDefault(ks KeyStore2, auth string) (key *Key, err error) { + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("GenerateNewKey error: %v", r) + } + }() + key = NewKey() + err = ks.StoreKey(key, auth) + return key, err +} + +func (ks keyStorePlain) GetKey(keyId *uuid.UUID, auth string) (key *Key, err error) { + fileContent, err := GetKeyFile(ks.keysDirPath, keyId) + if err != nil { + return nil, err + } + + key = new(Key) + err = json.Unmarshal(fileContent, key) + return key, err +} + +func (ks keyStorePlain) StoreKey(key *Key, auth string) (err error) { + keyJSON, err := json.Marshal(key) + if err != nil { + return err + } + err = WriteKeyFile(key.Id.String(), ks.keysDirPath, keyJSON) + return err +} + +func (ks keyStorePlain) DeleteKey(keyId *uuid.UUID, auth string) (err error) { + keyDirPath := path.Join(ks.keysDirPath, keyId.String()) + err = os.RemoveAll(keyDirPath) + return err +} + +func GetKeyFile(keysDirPath string, keyId *uuid.UUID) (fileContent []byte, err error) { + id := keyId.String() + return ioutil.ReadFile(path.Join(keysDirPath, id, id)) +} + +func WriteKeyFile(id string, keysDirPath string, content []byte) (err error) { + keyDirPath := path.Join(keysDirPath, id) + keyFilePath := path.Join(keyDirPath, id) + err = os.MkdirAll(keyDirPath, 0700) // read, write and dir search for user + if err != nil { + return err + } + return ioutil.WriteFile(keyFilePath, content, 0600) // read, write for user +} diff --git a/crypto/key_store_plaintext_file.go b/crypto/key_store_plaintext_file.go deleted file mode 100644 index 7ca5e5679..000000000 --- a/crypto/key_store_plaintext_file.go +++ /dev/null @@ -1,113 +0,0 @@ -/* - 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 ( - "code.google.com/p/go-uuid/uuid" - "encoding/json" - "fmt" - "io/ioutil" - "os" - "os/user" - "path" -) - -// TODO: rename to KeyStore when replacing existing KeyStore -type KeyStore2 interface { - GenerateNewKey(string) (*Key, error) // create and store new key, optionally using auth string - GetKey(*uuid.UUID, string) (*Key, error) // key from id and auth string - StoreKey(*Key, string) error // store key optionally using auth string - DeleteKey(*uuid.UUID, string) error // delete key by id and auth string -} - -type KeyStorePlaintext struct { - keysDirPath string -} - -// TODO: copied from cmd/ethereum/flags.go -func DefaultDataDir() string { - usr, _ := user.Current() - return path.Join(usr.HomeDir, ".ethereum") -} - -func (ks KeyStorePlaintext) GenerateNewKey(auth string) (key *Key, err error) { - key, err = GenerateNewKeyDefault(ks, auth) - return -} - -func GenerateNewKeyDefault(ks KeyStore2, auth string) (key *Key, err error) { - defer func() { - if r := recover(); r != nil { - err = fmt.Errorf("GenerateNewKey error: %v", r) - } - }() - key = NewKey() - err = ks.StoreKey(key, auth) - return -} - -func (ks KeyStorePlaintext) GetKey(keyId *uuid.UUID, auth string) (key *Key, err error) { - fileContent, err := GetKeyFile(ks.keysDirPath, keyId) - if err != nil { - return - } - - key = new(Key) - err = json.Unmarshal(fileContent, key) - return -} - -func (ks KeyStorePlaintext) StoreKey(key *Key, auth string) (err error) { - keyJSON, err := json.Marshal(key) - if err != nil { - return - } - err = WriteKeyFile(key.Id.String(), ks.keysDirPath, keyJSON) - return -} - -func (ks KeyStorePlaintext) DeleteKey(keyId *uuid.UUID, auth string) (err error) { - keyDirPath := path.Join(ks.keysDirPath, keyId.String()) - err = os.RemoveAll(keyDirPath) - return -} - -func GetKeyFile(keysDirPath string, keyId *uuid.UUID) (fileContent []byte, err error) { - idString := keyId.String() - keyDirPath := path.Join(keysDirPath, idString) - keyFilePath := path.Join(keyDirPath, idString) - fileContent, err = ioutil.ReadFile(keyFilePath) - return -} - -func WriteKeyFile(idString string, keysDirPath string, content []byte) (err error) { - keyDirPath := path.Join(keysDirPath, idString) - keyFilePath := path.Join(keyDirPath, idString) - err = os.MkdirAll(keyDirPath, 0700) // read, write and dir search for user - if err != nil { - return - } - err = ioutil.WriteFile(keyFilePath, content, 0600) // read, write for user - return -} diff --git a/crypto/key_store_test.go b/crypto/key_store_test.go index 412735444..8469d2369 100644 --- a/crypto/key_store_test.go +++ b/crypto/key_store_test.go @@ -6,20 +6,19 @@ import ( "testing" ) -func TestKeyStorePlaintext(t *testing.T) { - ks := new(KeyStorePlaintext) - ks.keysDirPath = DefaultDataDir() +func TestKeyStorePlain(t *testing.T) { + ks := NewKeyStorePlain(DefaultDataDir()) pass := "" // not used but required by API k1, err := ks.GenerateNewKey(pass) if err != nil { - fmt.Println(err.Error()) + t.Error(err) t.FailNow() } k2 := new(Key) k2, err = ks.GetKey(k1.Id, pass) if err != nil { - fmt.Println(err.Error()) + t.Error(err) t.FailNow() } @@ -40,25 +39,24 @@ func TestKeyStorePlaintext(t *testing.T) { err = ks.DeleteKey(k2.Id, pass) if err != nil { - fmt.Println(err.Error()) + t.Error(err) t.FailNow() } } func TestKeyStorePassphrase(t *testing.T) { - ks := new(KeyStorePassphrase) - ks.keysDirPath = DefaultDataDir() + ks := NewKeyStorePassphrase(DefaultDataDir()) pass := "foo" k1, err := ks.GenerateNewKey(pass) if err != nil { - fmt.Println(err.Error()) + t.Error(err) t.FailNow() } k2 := new(Key) k2, err = ks.GetKey(k1.Id, pass) if err != nil { - fmt.Println(err.Error()) + t.Error(err) t.FailNow() } @@ -79,36 +77,35 @@ func TestKeyStorePassphrase(t *testing.T) { err = ks.DeleteKey(k2.Id, pass) // also to clean up created files if err != nil { - fmt.Println(err.Error()) + t.Error(err) t.FailNow() } } func TestKeyStorePassphraseDecryptionFail(t *testing.T) { - ks := new(KeyStorePassphrase) - ks.keysDirPath = DefaultDataDir() + ks := NewKeyStorePassphrase(DefaultDataDir()) pass := "foo" k1, err := ks.GenerateNewKey(pass) if err != nil { - fmt.Println(err.Error()) + t.Error(err) t.FailNow() } _, err = ks.GetKey(k1.Id, "bar") // wrong passphrase - // fmt.Println(err.Error()) + // t.Error(err) if err == nil { t.FailNow() } err = ks.DeleteKey(k1.Id, "bar") // wrong passphrase if err == nil { - fmt.Println(err.Error()) + t.Error(err) t.FailNow() } err = ks.DeleteKey(k1.Id, pass) // to clean up if err != nil { - fmt.Println(err.Error()) + t.Error(err) t.FailNow() } } -- cgit