github.com/ethersphere/bee/v2@v2.2.0/pkg/keystore/file/key.go (about) 1 // Copyright 2020 The Swarm Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package file 6 7 import ( 8 "bytes" 9 "crypto/aes" 10 "crypto/cipher" 11 "crypto/ecdsa" 12 "crypto/elliptic" 13 "crypto/rand" 14 "encoding/hex" 15 "encoding/json" 16 "fmt" 17 "io" 18 19 "github.com/btcsuite/btcd/btcec/v2" 20 "github.com/ethersphere/bee/v2/pkg/crypto" 21 "github.com/ethersphere/bee/v2/pkg/keystore" 22 "github.com/google/uuid" 23 "golang.org/x/crypto/scrypt" 24 "golang.org/x/crypto/sha3" 25 ) 26 27 var _ keystore.Service = (*Service)(nil) 28 29 const ( 30 keyHeaderKDF = "scrypt" 31 keyVersion = 3 32 33 scryptN = 1 << 15 34 scryptR = 8 35 scryptP = 1 36 scryptDKLen = 32 37 ) 38 39 // This format is compatible with Ethereum JSON v3 key file format. 40 type encryptedKey struct { 41 Address string `json:"address"` 42 Crypto keyCripto `json:"crypto"` 43 Version int `json:"version"` 44 Id string `json:"id"` 45 } 46 47 type keyCripto struct { 48 Cipher string `json:"cipher"` 49 CipherText string `json:"ciphertext"` 50 CipherParams cipherParams `json:"cipherparams"` 51 KDF string `json:"kdf"` 52 KDFParams kdfParams `json:"kdfparams"` 53 MAC string `json:"mac"` 54 } 55 56 type cipherParams struct { 57 IV string `json:"iv"` 58 } 59 60 type kdfParams struct { 61 N int `json:"n"` 62 R int `json:"r"` 63 P int `json:"p"` 64 DKLen int `json:"dklen"` 65 Salt string `json:"salt"` 66 } 67 68 func encryptKey(k *ecdsa.PrivateKey, password string, edg keystore.EDG) ([]byte, error) { 69 data, err := edg.Encode(k) 70 if err != nil { 71 return nil, err 72 } 73 kc, err := encryptData(data, []byte(password)) 74 if err != nil { 75 return nil, err 76 } 77 var addr []byte 78 switch k.PublicKey.Curve { 79 case btcec.S256(): 80 a, err := crypto.NewEthereumAddress(k.PublicKey) 81 if err != nil { 82 return nil, err 83 } 84 addr = a 85 case elliptic.P256(): 86 addr = elliptic.Marshal(elliptic.P256(), k.PublicKey.X, k.PublicKey.Y) 87 default: 88 return nil, fmt.Errorf("unsupported curve: %v", k.PublicKey.Curve) 89 } 90 return json.Marshal(encryptedKey{ 91 Address: hex.EncodeToString(addr), 92 Crypto: *kc, 93 Version: keyVersion, 94 Id: uuid.NewString(), 95 }) 96 } 97 98 func decryptKey(data []byte, password string, edg keystore.EDG) (*ecdsa.PrivateKey, error) { 99 var k encryptedKey 100 if err := json.Unmarshal(data, &k); err != nil { 101 return nil, err 102 } 103 if k.Version != keyVersion { 104 return nil, fmt.Errorf("unsupported key version: %v", k.Version) 105 } 106 d, err := decryptData(k.Crypto, password) 107 if err != nil { 108 return nil, err 109 } 110 return edg.Decode(d) 111 } 112 113 func encryptData(data, password []byte) (*keyCripto, error) { 114 salt := make([]byte, 32) 115 if _, err := io.ReadFull(rand.Reader, salt); err != nil { 116 return nil, fmt.Errorf("read random data: %w", err) 117 } 118 derivedKey, err := scrypt.Key(password, salt, scryptN, scryptR, scryptP, scryptDKLen) 119 if err != nil { 120 return nil, err 121 } 122 encryptKey := derivedKey[:16] 123 124 iv := make([]byte, aes.BlockSize) 125 if _, err := io.ReadFull(rand.Reader, iv); err != nil { 126 return nil, fmt.Errorf("read random data: %w", err) 127 } 128 cipherText, err := aesCTRXOR(encryptKey, data, iv) 129 if err != nil { 130 return nil, err 131 } 132 mac, err := crypto.LegacyKeccak256(append(derivedKey[16:32], cipherText...)) 133 if err != nil { 134 return nil, err 135 } 136 137 return &keyCripto{ 138 Cipher: "aes-128-ctr", 139 CipherText: hex.EncodeToString(cipherText), 140 CipherParams: cipherParams{ 141 IV: hex.EncodeToString(iv), 142 }, 143 KDF: keyHeaderKDF, 144 KDFParams: kdfParams{ 145 N: scryptN, 146 R: scryptR, 147 P: scryptP, 148 DKLen: scryptDKLen, 149 Salt: hex.EncodeToString(salt), 150 }, 151 MAC: hex.EncodeToString(mac[:]), 152 }, nil 153 } 154 155 func decryptData(v keyCripto, password string) ([]byte, error) { 156 if v.Cipher != "aes-128-ctr" { 157 return nil, fmt.Errorf("unsupported cipher: %v", v.Cipher) 158 } 159 160 mac, err := hex.DecodeString(v.MAC) 161 if err != nil { 162 return nil, fmt.Errorf("hex decode mac: %w", err) 163 } 164 cipherText, err := hex.DecodeString(v.CipherText) 165 if err != nil { 166 return nil, fmt.Errorf("hex decode cipher text: %w", err) 167 } 168 derivedKey, err := getKDFKey(v, []byte(password)) 169 if err != nil { 170 return nil, err 171 } 172 calculatedMAC := sha3.Sum256(append(derivedKey[16:32], cipherText...)) 173 if !bytes.Equal(calculatedMAC[:], mac) { 174 // if this fails we might be trying to load an ethereum V3 keyfile 175 calculatedMACEth, err := crypto.LegacyKeccak256(append(derivedKey[16:32], cipherText...)) 176 if err != nil { 177 return nil, err 178 } 179 if !bytes.Equal(calculatedMACEth[:], mac) { 180 return nil, keystore.ErrInvalidPassword 181 } 182 } 183 184 iv, err := hex.DecodeString(v.CipherParams.IV) 185 if err != nil { 186 return nil, fmt.Errorf("hex decode IV cipher parameter: %w", err) 187 } 188 data, err := aesCTRXOR(derivedKey[:16], cipherText, iv) 189 if err != nil { 190 return nil, err 191 } 192 return data, nil 193 } 194 195 func aesCTRXOR(key, inText, iv []byte) ([]byte, error) { 196 aesBlock, err := aes.NewCipher(key) 197 if err != nil { 198 return nil, err 199 } 200 stream := cipher.NewCTR(aesBlock, iv) 201 outText := make([]byte, len(inText)) 202 stream.XORKeyStream(outText, inText) 203 return outText, nil 204 } 205 206 func getKDFKey(v keyCripto, password []byte) ([]byte, error) { 207 if v.KDF != keyHeaderKDF { 208 return nil, fmt.Errorf("unsupported KDF: %s", v.KDF) 209 } 210 salt, err := hex.DecodeString(v.KDFParams.Salt) 211 if err != nil { 212 return nil, fmt.Errorf("hex decode salt: %w", err) 213 } 214 return scrypt.Key( 215 password, 216 salt, 217 v.KDFParams.N, 218 v.KDFParams.R, 219 v.KDFParams.P, 220 v.KDFParams.DKLen, 221 ) 222 }