func getKDFKey(cryptoJSON cryptoJSON, auth string) ([]byte, error) { authArray := []byte(auth) salt, err := hex.DecodeString(cryptoJSON.KDFParams["salt"].(string)) if err != nil { return nil, err } dkLen := ensureInt(cryptoJSON.KDFParams["dklen"]) if cryptoJSON.KDF == "scrypt" { n := ensureInt(cryptoJSON.KDFParams["n"]) r := ensureInt(cryptoJSON.KDFParams["r"]) p := ensureInt(cryptoJSON.KDFParams["p"]) return scrypt.Key(authArray, salt, n, r, p, dkLen) } else if cryptoJSON.KDF == "pbkdf2" { c := ensureInt(cryptoJSON.KDFParams["c"]) prf := cryptoJSON.KDFParams["prf"].(string) if prf != "hmac-sha256" { return nil, fmt.Errorf("Unsupported PBKDF2 PRF: ", prf) } key := pbkdf2.Key(authArray, salt, c, dkLen, sha256.New) return key, nil } return nil, fmt.Errorf("Unsupported KDF: ", cryptoJSON.KDF) }
// Key derives a key from the password, salt, and cost parameters, returning // a byte slice of length keyLen that can be used as cryptographic key. // // N is a CPU/memory cost parameter, which must be a power of two greater than 1. // r and p must satisfy r * p < 2³⁰. If the parameters do not satisfy the // limits, the function returns a nil byte slice and an error. // // For example, you can get a derived key for e.g. AES-256 (which needs a // 32-byte key) by doing: // // dk := scrypt.Key([]byte("some password"), salt, 16384, 8, 1, 32) // // The recommended parameters for interactive logins as of 2009 are N=16384, // r=8, p=1. They should be increased as memory latency and CPU parallelism // increases. Remember to get a good random salt. func Key(password, salt []byte, N, r, p, keyLen int) ([]byte, error) { if N <= 1 || N&(N-1) != 0 { return nil, errors.New("scrypt: N must be > 1 and a power of 2") } if uint64(r)*uint64(p) >= 1<<30 || r > maxInt/128/p || r > maxInt/256 || N > maxInt/128/r { return nil, errors.New("scrypt: parameters are too large") } xy := make([]uint32, 64*r) v := make([]uint32, 32*N*r) b := pbkdf2.Key(password, salt, 1, p*128*r, sha256.New) for i := 0; i < p; i++ { smix(b[i*128*r:], r, N, v, xy) } return pbkdf2.Key(password, b, 1, keyLen, sha256.New), nil }
func decryptPreSaleKey(fileContent []byte, password string) (key *Key, err error) { preSaleKeyStruct := struct { EncSeed string EthAddr string Email string BtcAddr string }{} err = json.Unmarshal(fileContent, &preSaleKeyStruct) if err != nil { return nil, err } encSeedBytes, err := hex.DecodeString(preSaleKeyStruct.EncSeed) iv := encSeedBytes[:16] cipherText := encSeedBytes[16:] /* See https://github.com/ethereum/pyethsaletool pyethsaletool generates the encryption key from password by 2000 rounds of PBKDF2 with HMAC-SHA-256 using password as salt (:(). 16 byte key length within PBKDF2 and resulting key is used as AES key */ passBytes := []byte(password) derivedKey := pbkdf2.Key(passBytes, passBytes, 2000, 16, sha256.New) plainText, err := aesCBCDecrypt(derivedKey, cipherText, iv) if err != nil { return nil, err } ethPriv := Sha3(plainText) ecKey := ToECDSA(ethPriv) key = &Key{ Id: nil, Address: PubkeyToAddress(ecKey.PublicKey), PrivateKey: ecKey, } derivedAddr := hex.EncodeToString(key.Address.Bytes()) // needed because .Hex() gives leading "0x" expectedAddr := preSaleKeyStruct.EthAddr if derivedAddr != expectedAddr { err = fmt.Errorf("decrypted addr '%s' not equal to expected addr '%s'", derivedAddr, expectedAddr) } return key, err }