Unverified Commit d5c7a605 authored by Martin Holst Swende's avatar Martin Holst Swende Committed by GitHub

cmd/clef: encrypt the master seed on disk (#17704)

* cmd/clef: encrypt master seed of clef
Signed-off-by: 's avatarYaoZengzeng <yaozengzeng@zju.edu.cn>

* keystore: refactor for external use of encryption

* clef: utilize keystore encryption, check flags correctly

* clef: validate master password

* clef: add json wrapping around encrypted master seed
parent ff5538ad
...@@ -66,19 +66,19 @@ type plainKeyJSON struct { ...@@ -66,19 +66,19 @@ type plainKeyJSON struct {
type encryptedKeyJSONV3 struct { type encryptedKeyJSONV3 struct {
Address string `json:"address"` Address string `json:"address"`
Crypto cryptoJSON `json:"crypto"` Crypto CryptoJSON `json:"crypto"`
Id string `json:"id"` Id string `json:"id"`
Version int `json:"version"` Version int `json:"version"`
} }
type encryptedKeyJSONV1 struct { type encryptedKeyJSONV1 struct {
Address string `json:"address"` Address string `json:"address"`
Crypto cryptoJSON `json:"crypto"` Crypto CryptoJSON `json:"crypto"`
Id string `json:"id"` Id string `json:"id"`
Version string `json:"version"` Version string `json:"version"`
} }
type cryptoJSON struct { type CryptoJSON struct {
Cipher string `json:"cipher"` Cipher string `json:"cipher"`
CipherText string `json:"ciphertext"` CipherText string `json:"ciphertext"`
CipherParams cipherparamsJSON `json:"cipherparams"` CipherParams cipherparamsJSON `json:"cipherparams"`
......
...@@ -135,29 +135,26 @@ func (ks keyStorePassphrase) JoinPath(filename string) string { ...@@ -135,29 +135,26 @@ func (ks keyStorePassphrase) JoinPath(filename string) string {
return filepath.Join(ks.keysDirPath, filename) return filepath.Join(ks.keysDirPath, filename)
} }
// EncryptKey encrypts a key using the specified scrypt parameters into a json // Encryptdata encrypts the data given as 'data' with the password 'auth'.
// blob that can be decrypted later on. func EncryptDataV3(data, auth []byte, scryptN, scryptP int) (CryptoJSON, error) {
func EncryptKey(key *Key, auth string, scryptN, scryptP int) ([]byte, error) {
authArray := []byte(auth)
salt := make([]byte, 32) salt := make([]byte, 32)
if _, err := io.ReadFull(rand.Reader, salt); err != nil { if _, err := io.ReadFull(rand.Reader, salt); err != nil {
panic("reading from crypto/rand failed: " + err.Error()) panic("reading from crypto/rand failed: " + err.Error())
} }
derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptR, scryptP, scryptDKLen) derivedKey, err := scrypt.Key(auth, salt, scryptN, scryptR, scryptP, scryptDKLen)
if err != nil { if err != nil {
return nil, err return CryptoJSON{}, err
} }
encryptKey := derivedKey[:16] encryptKey := derivedKey[:16]
keyBytes := math.PaddedBigBytes(key.PrivateKey.D, 32)
iv := make([]byte, aes.BlockSize) // 16 iv := make([]byte, aes.BlockSize) // 16
if _, err := io.ReadFull(rand.Reader, iv); err != nil { if _, err := io.ReadFull(rand.Reader, iv); err != nil {
panic("reading from crypto/rand failed: " + err.Error()) panic("reading from crypto/rand failed: " + err.Error())
} }
cipherText, err := aesCTRXOR(encryptKey, keyBytes, iv) cipherText, err := aesCTRXOR(encryptKey, data, iv)
if err != nil { if err != nil {
return nil, err return CryptoJSON{}, err
} }
mac := crypto.Keccak256(derivedKey[16:32], cipherText) mac := crypto.Keccak256(derivedKey[16:32], cipherText)
...@@ -167,12 +164,11 @@ func EncryptKey(key *Key, auth string, scryptN, scryptP int) ([]byte, error) { ...@@ -167,12 +164,11 @@ func EncryptKey(key *Key, auth string, scryptN, scryptP int) ([]byte, error) {
scryptParamsJSON["p"] = scryptP scryptParamsJSON["p"] = scryptP
scryptParamsJSON["dklen"] = scryptDKLen scryptParamsJSON["dklen"] = scryptDKLen
scryptParamsJSON["salt"] = hex.EncodeToString(salt) scryptParamsJSON["salt"] = hex.EncodeToString(salt)
cipherParamsJSON := cipherparamsJSON{ cipherParamsJSON := cipherparamsJSON{
IV: hex.EncodeToString(iv), IV: hex.EncodeToString(iv),
} }
cryptoStruct := cryptoJSON{ cryptoStruct := CryptoJSON{
Cipher: "aes-128-ctr", Cipher: "aes-128-ctr",
CipherText: hex.EncodeToString(cipherText), CipherText: hex.EncodeToString(cipherText),
CipherParams: cipherParamsJSON, CipherParams: cipherParamsJSON,
...@@ -180,6 +176,17 @@ func EncryptKey(key *Key, auth string, scryptN, scryptP int) ([]byte, error) { ...@@ -180,6 +176,17 @@ func EncryptKey(key *Key, auth string, scryptN, scryptP int) ([]byte, error) {
KDFParams: scryptParamsJSON, KDFParams: scryptParamsJSON,
MAC: hex.EncodeToString(mac), MAC: hex.EncodeToString(mac),
} }
return cryptoStruct, nil
}
// 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 err != nil {
return nil, err
}
encryptedKeyJSONV3 := encryptedKeyJSONV3{ encryptedKeyJSONV3 := encryptedKeyJSONV3{
hex.EncodeToString(key.Address[:]), hex.EncodeToString(key.Address[:]),
cryptoStruct, cryptoStruct,
...@@ -226,43 +233,48 @@ func DecryptKey(keyjson []byte, auth string) (*Key, error) { ...@@ -226,43 +233,48 @@ func DecryptKey(keyjson []byte, auth string) (*Key, error) {
PrivateKey: key, PrivateKey: key,
}, nil }, nil
} }
func DecryptDataV3(cryptoJson CryptoJSON, auth string) ([]byte, error) {
func decryptKeyV3(keyProtected *encryptedKeyJSONV3, auth string) (keyBytes []byte, keyId []byte, err error) { if cryptoJson.Cipher != "aes-128-ctr" {
if keyProtected.Version != version { return nil, fmt.Errorf("Cipher not supported: %v", cryptoJson.Cipher)
return nil, nil, fmt.Errorf("Version not supported: %v", keyProtected.Version)
} }
mac, err := hex.DecodeString(cryptoJson.MAC)
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 { if err != nil {
return nil, nil, err return nil, err
} }
iv, err := hex.DecodeString(keyProtected.Crypto.CipherParams.IV) iv, err := hex.DecodeString(cryptoJson.CipherParams.IV)
if err != nil { if err != nil {
return nil, nil, err return nil, err
} }
cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText) cipherText, err := hex.DecodeString(cryptoJson.CipherText)
if err != nil { if err != nil {
return nil, nil, err return nil, err
} }
derivedKey, err := getKDFKey(keyProtected.Crypto, auth) derivedKey, err := getKDFKey(cryptoJson, auth)
if err != nil { if err != nil {
return nil, nil, err return nil, err
} }
calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText) calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText)
if !bytes.Equal(calculatedMAC, mac) { if !bytes.Equal(calculatedMAC, mac) {
return nil, nil, ErrDecrypt return nil, ErrDecrypt
} }
plainText, err := aesCTRXOR(derivedKey[:16], cipherText, iv) plainText, err := aesCTRXOR(derivedKey[:16], cipherText, iv)
if err != nil {
return nil, err
}
return plainText, err
}
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)
}
keyId = uuid.Parse(keyProtected.Id)
plainText, err := DecryptDataV3(keyProtected.Crypto, auth)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
...@@ -303,7 +315,7 @@ func decryptKeyV1(keyProtected *encryptedKeyJSONV1, auth string) (keyBytes []byt ...@@ -303,7 +315,7 @@ func decryptKeyV1(keyProtected *encryptedKeyJSONV1, auth string) (keyBytes []byt
return plainText, keyId, err return plainText, keyId, err
} }
func getKDFKey(cryptoJSON cryptoJSON, auth string) ([]byte, error) { func getKDFKey(cryptoJSON CryptoJSON, auth string) ([]byte, error) {
authArray := []byte(auth) authArray := []byte(auth)
salt, err := hex.DecodeString(cryptoJSON.KDFParams["salt"].(string)) salt, err := hex.DecodeString(cryptoJSON.KDFParams["salt"].(string))
if err != nil { if err != nil {
......
### Changelog for internal API (ui-api) ### Changelog for internal API (ui-api)
### 3.0.0
* Make use of `OnInputRequired(info UserInputRequest)` for obtaining master password during startup
### 2.1.0 ### 2.1.0
* Add `OnInputRequired(info UserInputRequest)` to internal API. This method is used when Clef needs user input, e.g. passwords. * Add `OnInputRequired(info UserInputRequest)` to internal API. This method is used when Clef needs user input, e.g. passwords.
...@@ -14,7 +18,6 @@ The following structures are used: ...@@ -14,7 +18,6 @@ The following structures are used:
UserInputResponse struct { UserInputResponse struct {
Text string `json:"text"` Text string `json:"text"`
} }
```
### 2.0.0 ### 2.0.0
......
This diff is collapsed.
...@@ -197,6 +197,12 @@ type ( ...@@ -197,6 +197,12 @@ type (
Message struct { Message struct {
Text string `json:"text"` Text string `json:"text"`
} }
PasswordRequest struct {
Prompt string `json:"prompt"`
}
PasswordResponse struct {
Password string `json:"password"`
}
StartupInfo struct { StartupInfo struct {
Info map[string]interface{} `json:"info"` Info map[string]interface{} `json:"info"`
} }
......
...@@ -81,6 +81,10 @@ func (alwaysDenyUI) OnInputRequired(info core.UserInputRequest) (core.UserInputR ...@@ -81,6 +81,10 @@ func (alwaysDenyUI) OnInputRequired(info core.UserInputRequest) (core.UserInputR
func (alwaysDenyUI) OnSignerStartup(info core.StartupInfo) { func (alwaysDenyUI) OnSignerStartup(info core.StartupInfo) {
} }
func (alwaysDenyUI) OnMasterPassword(request *core.PasswordRequest) (core.PasswordResponse, error) {
return core.PasswordResponse{}, nil
}
func (alwaysDenyUI) ApproveTx(request *core.SignTxRequest) (core.SignTxResponse, error) { func (alwaysDenyUI) ApproveTx(request *core.SignTxRequest) (core.SignTxResponse, error) {
return core.SignTxResponse{Transaction: request.Transaction, Approved: false, Password: ""}, nil return core.SignTxResponse{Transaction: request.Transaction, Approved: false, Password: ""}, nil
} }
...@@ -250,6 +254,11 @@ func (d *dummyUI) ShowInfo(message string) { ...@@ -250,6 +254,11 @@ func (d *dummyUI) ShowInfo(message string) {
func (d *dummyUI) OnApprovedTx(tx ethapi.SignTransactionResult) { func (d *dummyUI) OnApprovedTx(tx ethapi.SignTransactionResult) {
d.calls = append(d.calls, "OnApprovedTx") d.calls = append(d.calls, "OnApprovedTx")
} }
func (d *dummyUI) OnMasterPassword(request *core.PasswordRequest) (core.PasswordResponse, error) {
return core.PasswordResponse{}, nil
}
func (d *dummyUI) OnSignerStartup(info core.StartupInfo) { func (d *dummyUI) OnSignerStartup(info core.StartupInfo) {
} }
...@@ -526,6 +535,10 @@ func (d *dontCallMe) OnInputRequired(info core.UserInputRequest) (core.UserInput ...@@ -526,6 +535,10 @@ func (d *dontCallMe) OnInputRequired(info core.UserInputRequest) (core.UserInput
func (d *dontCallMe) OnSignerStartup(info core.StartupInfo) { func (d *dontCallMe) OnSignerStartup(info core.StartupInfo) {
} }
func (d *dontCallMe) OnMasterPassword(request *core.PasswordRequest) (core.PasswordResponse, error) {
return core.PasswordResponse{}, nil
}
func (d *dontCallMe) ApproveTx(request *core.SignTxRequest) (core.SignTxResponse, error) { func (d *dontCallMe) ApproveTx(request *core.SignTxRequest) (core.SignTxResponse, error) {
d.t.Fatalf("Did not expect next-handler to be called") d.t.Fatalf("Did not expect next-handler to be called")
return core.SignTxResponse{}, core.ErrRequestDenied return core.SignTxResponse{}, core.ErrRequestDenied
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment