public (PrivateKey PrivateKey, Result Result) GetKey(Address address, SecureString password) { if (!password.IsReadOnly()) { throw new InvalidOperationException("Cannot work with password that is not readonly"); } var serializedKey = ReadKey(address); if (serializedKey == null) { return(null, Result.Fail("Cannot find key")); } var keyStoreItem = _jsonSerializer.Deserialize <KeyStoreItem>(serializedKey); if (keyStoreItem?.Crypto == null) { return(null, Result.Fail("Cannot deserialize key")); } var validationResult = Validate(keyStoreItem); if (validationResult.ResultType != ResultType.Success) { return(null, validationResult); } byte[] mac = Bytes.FromHexString(keyStoreItem.Crypto.MAC); byte[] iv = Bytes.FromHexString(keyStoreItem.Crypto.CipherParams.IV); byte[] cipher = Bytes.FromHexString(keyStoreItem.Crypto.CipherText); byte[] salt = Bytes.FromHexString(keyStoreItem.Crypto.KDFParams.Salt); var kdfParams = keyStoreItem.Crypto.KDFParams; var passBytes = password.ToByteArray(_keyStoreEncoding); byte[] derivedKey; var kdf = keyStoreItem.Crypto.KDF.Trim(); switch (kdf) { case "scrypt": int r = kdfParams.R.Value; int p = kdfParams.P.Value; int n = kdfParams.N.Value; derivedKey = SCrypt.ComputeDerivedKey(passBytes, salt, n, r, p, null, kdfParams.DkLen); break; case "pbkdf2": int c = kdfParams.C.Value; var deriveBytes = new Rfc2898DeriveBytes(passBytes, salt, kdfParams.C.Value, HashAlgorithmName.SHA256); derivedKey = deriveBytes.GetBytes(256); break; default: return(null, Result.Fail($"Unsupported algoritm: {kdf}")); } var restoredMac = Keccak.Compute(derivedKey.Slice(kdfParams.DkLen - 16, 16).Concat(cipher).ToArray()).Bytes; if (!Bytes.AreEqual(mac, restoredMac)) { return(null, Result.Fail("Incorrect MAC")); } var cipherType = keyStoreItem.Crypto.Cipher.Trim(); byte[] decryptKey; if (kdf == "scrypt" && cipherType == "aes-128-cbc") { decryptKey = Keccak.Compute(derivedKey.Slice(0, 16)).Bytes.Slice(0, 16); } else { decryptKey = derivedKey.Slice(0, 16); } byte[] key = _symmetricEncrypter.Decrypt(cipher, decryptKey, iv, cipherType); if (key == null) { return(null, Result.Fail("Error during decryption")); } // TODO: maybe only allow to sign here so the key never leaves the area? return(new PrivateKey(key), Result.Success); }
public (PrivateKey PrivateKey, Result Result) GetKey(Address address, SecureString password) { var serializedKey = ReadKey(address.ToString()); if (serializedKey == null) { return(null, Result.Fail("Cannot find key")); } var keyStoreItem = _jsonSerializer.Deserialize <KeyStoreItem>(serializedKey); if (keyStoreItem?.Crypto == null) { return(null, Result.Fail("Cannot deserialize key")); } var validationResult = Validate(keyStoreItem); if (validationResult.ResultType != ResultType.Success) { return(null, validationResult); } Hex mac = keyStoreItem.Crypto.MAC; Hex iv = keyStoreItem.Crypto.CipherParams.IV; Hex cipher = keyStoreItem.Crypto.CipherText; Hex salt = keyStoreItem.Crypto.KDFParams.Salt; var kdfParams = keyStoreItem.Crypto.KDFParams; var passBytes = password.ToByteArray(_keyStoreEncoding); byte[] derivedKey; var kdf = keyStoreItem.Crypto.KDF.Trim(); switch (kdf) { case "scrypt": derivedKey = SCrypt.ComputeDerivedKey(passBytes, salt, kdfParams.N, kdfParams.R, kdfParams.P, null, kdfParams.DkLen); break; case "pbkdf2": var deriveBytes = new Rfc2898DeriveBytes(passBytes, salt, kdfParams.C, HashAlgorithmName.SHA256); derivedKey = deriveBytes.GetBytes(256); break; default: return(null, Result.Fail($"Unsupported algoritm: {kdf}")); } var restoredMac = Keccak.Compute(derivedKey.Slice(kdfParams.DkLen - 16, 16).Concat((byte[])cipher).ToArray()).Bytes; if (!mac.Equals(new Hex(restoredMac))) { return(null, Result.Fail("Incorrect MAC")); } var cipherType = keyStoreItem.Crypto.Cipher.Trim(); byte[] decryptKey; if (kdf == "scrypt" && cipherType == "aes-128-cbc") { decryptKey = Keccak.Compute(derivedKey.Slice(0, 16)).Bytes.Slice(0, 16); } else { decryptKey = derivedKey.Slice(0, 16); } byte[] key = _symmetricEncrypter.Decrypt(cipher, decryptKey, iv, cipherType); if (key == null) { return(null, Result.Fail("Error during decryption")); } // TODO: maybe only allow to sign here so the key never leaves the area? return(new PrivateKey(new Hex(key)), Result.Success()); }