github.com/chain5j/chain5j-pkg@v1.0.7/crypto/scrypt/passphrase.go (about) 1 // Package scrypt 2 // 3 // @author: xwc1125 4 package scrypt 5 6 import ( 7 "bytes" 8 "crypto/aes" 9 "crypto/rand" 10 "crypto/sha256" 11 "encoding/hex" 12 "encoding/json" 13 "fmt" 14 "io" 15 16 "github.com/chain5j/chain5j-pkg/crypto/hashalg/sha3" 17 "github.com/pborman/uuid" 18 "golang.org/x/crypto/pbkdf2" 19 "golang.org/x/crypto/scrypt" 20 ) 21 22 const ( 23 keyHeaderKDF = "scrypt" 24 25 // StandardScryptN is the N parameter of Scrypt encryption algorithm, using 256MB 26 // memory and taking approximately 1s CPU time on a modern processor. 27 StandardScryptN = 1 << 18 28 29 // StandardScryptP is the P parameter of Scrypt encryption algorithm, using 256MB 30 // memory and taking approximately 1s CPU time on a modern processor. 31 StandardScryptP = 1 32 33 // LightScryptN is the N parameter of Scrypt encryption algorithm, using 4MB 34 // memory and taking approximately 100ms CPU time on a modern processor. 35 LightScryptN = 1 << 12 36 37 // LightScryptP is the P parameter of Scrypt encryption algorithm, using 4MB 38 // memory and taking approximately 100ms CPU time on a modern processor. 39 LightScryptP = 6 40 41 scryptR = 8 42 scryptDKLen = 32 43 ) 44 45 // EncryptDataV3 encrypts the data given as 'data' with the password 'auth'. 46 func EncryptDataV3(data, auth []byte, scryptN, scryptP int) (CryptoJSON, error) { 47 48 salt := make([]byte, 32) 49 if _, err := io.ReadFull(rand.Reader, salt); err != nil { 50 panic("reading from crypto/rand failed: " + err.Error()) 51 } 52 derivedKey, err := scrypt.Key(auth, salt, scryptN, scryptR, scryptP, scryptDKLen) 53 if err != nil { 54 return CryptoJSON{}, err 55 } 56 encryptKey := derivedKey[:16] 57 58 iv := make([]byte, aes.BlockSize) // 16 59 if _, err := io.ReadFull(rand.Reader, iv); err != nil { 60 panic("reading from crypto/rand failed: " + err.Error()) 61 } 62 cipherText, err := aesCTRXOR(encryptKey, data, iv) 63 if err != nil { 64 return CryptoJSON{}, err 65 } 66 mac := sha3.Keccak256(derivedKey[16:32], cipherText) 67 68 scryptParamsJSON := make(map[string]interface{}, 5) 69 scryptParamsJSON["n"] = scryptN 70 scryptParamsJSON["r"] = scryptR 71 scryptParamsJSON["p"] = scryptP 72 scryptParamsJSON["dklen"] = scryptDKLen 73 scryptParamsJSON["salt"] = hex.EncodeToString(salt) 74 cipherParamsJSON := cipherparamsJSON{ 75 IV: hex.EncodeToString(iv), 76 } 77 78 cryptoStruct := CryptoJSON{ 79 Cipher: "aes-128-ctr", 80 CipherText: hex.EncodeToString(cipherText), 81 CipherParams: cipherParamsJSON, 82 KDF: keyHeaderKDF, 83 KDFParams: scryptParamsJSON, 84 MAC: hex.EncodeToString(mac), 85 } 86 return cryptoStruct, nil 87 } 88 89 // EncryptKey encrypts a key using the specified scrypt parameters into a json 90 // blob that can be decrypted later on. 91 func EncryptKey(key *Key, auth string, scryptN, scryptP int) ([]byte, error) { 92 keyBytes := key.PrivateKey 93 cryptoStruct, err := EncryptDataV3(keyBytes, []byte(auth), scryptN, scryptP) 94 if err != nil { 95 return nil, err 96 } 97 encryptedKeyJSONV3 := encryptedKeyJSONV3{ 98 cryptoStruct, 99 key.Id.String(), 100 version, 101 } 102 return json.Marshal(encryptedKeyJSONV3) 103 } 104 105 // DecryptKey decrypts a key from a json blob, returning the private key itself. 106 func DecryptKey(keyjson []byte, auth string) (*Key, error) { 107 // Parse the json into a simple map to fetch the key version 108 m := make(map[string]interface{}) 109 if err := json.Unmarshal(keyjson, &m); err != nil { 110 return nil, err 111 } 112 // Depending on the version try to parse one way or another 113 var ( 114 keyBytes, keyId []byte 115 err error 116 ) 117 if version, ok := m["version"].(string); ok && version == "1" { 118 k := new(encryptedKeyJSONV1) 119 if err := json.Unmarshal(keyjson, k); err != nil { 120 return nil, err 121 } 122 keyBytes, keyId, err = decryptKeyV1(k, auth) 123 } else { 124 k := new(encryptedKeyJSONV3) 125 if err := json.Unmarshal(keyjson, k); err != nil { 126 return nil, err 127 } 128 keyBytes, keyId, err = decryptKeyV3(k, auth) 129 } 130 // Handle any decryption errors and return the key 131 if err != nil { 132 return nil, err 133 } 134 135 return &Key{ 136 Id: uuid.UUID(keyId), 137 PrivateKey: keyBytes, 138 }, nil 139 } 140 141 func DecryptDataV3(cryptoJson CryptoJSON, auth string) ([]byte, error) { 142 if cryptoJson.Cipher != "aes-128-ctr" { 143 return nil, fmt.Errorf("cipher not supported: %v", cryptoJson.Cipher) 144 } 145 mac, err := hex.DecodeString(cryptoJson.MAC) 146 if err != nil { 147 return nil, err 148 } 149 150 iv, err := hex.DecodeString(cryptoJson.CipherParams.IV) 151 if err != nil { 152 return nil, err 153 } 154 155 cipherText, err := hex.DecodeString(cryptoJson.CipherText) 156 if err != nil { 157 return nil, err 158 } 159 160 derivedKey, err := getKDFKey(cryptoJson, auth) 161 if err != nil { 162 return nil, err 163 } 164 165 calculatedMAC := sha3.Keccak256(derivedKey[16:32], cipherText) 166 if !bytes.Equal(calculatedMAC, mac) { 167 return nil, ErrDecrypt 168 } 169 170 plainText, err := aesCTRXOR(derivedKey[:16], cipherText, iv) 171 if err != nil { 172 return nil, err 173 } 174 return plainText, err 175 } 176 177 func decryptKeyV3(keyProtected *encryptedKeyJSONV3, auth string) (keyBytes []byte, keyId []byte, err error) { 178 if keyProtected.Version != version { 179 return nil, nil, fmt.Errorf("version not supported: %v", keyProtected.Version) 180 } 181 keyId = uuid.Parse(keyProtected.Id) 182 plainText, err := DecryptDataV3(keyProtected.Crypto, auth) 183 if err != nil { 184 return nil, nil, err 185 } 186 return plainText, keyId, err 187 } 188 189 func decryptKeyV1(keyProtected *encryptedKeyJSONV1, auth string) (keyBytes []byte, keyId []byte, err error) { 190 keyId = uuid.Parse(keyProtected.Id) 191 mac, err := hex.DecodeString(keyProtected.Crypto.MAC) 192 if err != nil { 193 return nil, nil, err 194 } 195 196 iv, err := hex.DecodeString(keyProtected.Crypto.CipherParams.IV) 197 if err != nil { 198 return nil, nil, err 199 } 200 201 cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText) 202 if err != nil { 203 return nil, nil, err 204 } 205 206 derivedKey, err := getKDFKey(keyProtected.Crypto, auth) 207 if err != nil { 208 return nil, nil, err 209 } 210 211 calculatedMAC := sha3.Keccak256(derivedKey[16:32], cipherText) 212 if !bytes.Equal(calculatedMAC, mac) { 213 return nil, nil, ErrDecrypt 214 } 215 216 plainText, err := aesCBCDecrypt(sha3.Keccak256(derivedKey[:16])[:16], cipherText, iv) 217 if err != nil { 218 return nil, nil, err 219 } 220 return plainText, keyId, err 221 } 222 223 func getKDFKey(cryptoJSON CryptoJSON, auth string) ([]byte, error) { 224 authArray := []byte(auth) 225 salt, err := hex.DecodeString(cryptoJSON.KDFParams["salt"].(string)) 226 if err != nil { 227 return nil, err 228 } 229 dkLen := ensureInt(cryptoJSON.KDFParams["dklen"]) 230 231 if cryptoJSON.KDF == keyHeaderKDF { 232 n := ensureInt(cryptoJSON.KDFParams["n"]) 233 r := ensureInt(cryptoJSON.KDFParams["r"]) 234 p := ensureInt(cryptoJSON.KDFParams["p"]) 235 return scrypt.Key(authArray, salt, n, r, p, dkLen) 236 237 } else if cryptoJSON.KDF == "pbkdf2" { 238 c := ensureInt(cryptoJSON.KDFParams["c"]) 239 prf := cryptoJSON.KDFParams["prf"].(string) 240 if prf != "hmac-sha256" { 241 return nil, fmt.Errorf("unsupported PBKDF2 PRF: %s", prf) 242 } 243 key := pbkdf2.Key(authArray, salt, c, dkLen, sha256.New) 244 return key, nil 245 } 246 247 return nil, fmt.Errorf("unsupported KDF: %s", cryptoJSON.KDF) 248 } 249 250 // TODO: can we do without this when unmarshalling dynamic JSON? 251 // why do integers in KDF params end up as float64 and not int after 252 // unmarshal? 253 func ensureInt(x interface{}) int { 254 res, ok := x.(int) 255 if !ok { 256 res = int(x.(float64)) 257 } 258 return res 259 }