accounts/keystore: encrypt the master seed on disk (#17704)

This commit is contained in:
Daniel Liu 2025-01-14 10:56:07 +08:00
parent e2ea9e87b5
commit c0acd9c3ad
2 changed files with 47 additions and 36 deletions

View file

@ -64,19 +64,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"`

View file

@ -134,29 +134,25 @@ 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)
@ -166,12 +162,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,
@ -179,9 +174,19 @@ 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[:]),
key.Address.String(),
cryptoStruct, cryptoStruct,
key.Id.String(), key.Id.String(),
version, version,
@ -227,42 +232,48 @@ func DecryptKey(keyjson []byte, auth string) (*Key, error) {
}, nil }, nil
} }
func decryptKeyV3(keyProtected *encryptedKeyJSONV3, auth string) (keyBytes []byte, keyId []byte, err error) { func DecryptDataV3(cryptoJson CryptoJSON, auth string) ([]byte, error) {
if keyProtected.Version != version { if cryptoJson.Cipher != "aes-128-ctr" {
return nil, nil, fmt.Errorf("not supported Version: %v", keyProtected.Version) return nil, fmt.Errorf("Cipher not supported: %v", cryptoJson.Cipher)
} }
mac, err := hex.DecodeString(cryptoJson.MAC)
if keyProtected.Crypto.Cipher != "aes-128-ctr" {
return nil, nil, fmt.Errorf("not supported Cipher: %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 +314,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 {