diff --git a/Substrate.NET.Wallet.Test/EncodingTest.cs b/Substrate.NET.Wallet.Test/EncodingTest.cs
new file mode 100644
index 0000000..057cd49
--- /dev/null
+++ b/Substrate.NET.Wallet.Test/EncodingTest.cs
@@ -0,0 +1,58 @@
+using NUnit.Framework;
+using Substrate.NetApi;
+using System;
+using System.IO;
+
+namespace Substrate.NET.Wallet.Test
+{
+ public class EncodingTest
+ {
+ private Random _random;
+
+ [SetUp]
+ public void Setup()
+ {
+ SystemInteraction.ReadData = f => File.ReadAllText(Path.Combine(Environment.CurrentDirectory, f));
+ SystemInteraction.DataExists = f => File.Exists(Path.Combine(Environment.CurrentDirectory, f));
+ SystemInteraction.ReadPersistent = f => File.ReadAllText(Path.Combine(Environment.CurrentDirectory, f));
+ SystemInteraction.PersistentExists = f => File.Exists(Path.Combine(Environment.CurrentDirectory, f));
+ SystemInteraction.Persist = (f, c) => File.WriteAllText(Path.Combine(Environment.CurrentDirectory, f), c);
+
+ _random = new Random();
+ }
+
+ [Test]
+ public void EncryptionTest()
+ {
+ var origData = new byte[_random.Next(10, 500)];
+ _random.NextBytes(origData);
+
+ var salt = new byte[32];
+ _random.NextBytes(salt);
+
+ var encryptedData = Wallet.Encrypt(origData, "aA1234dd", salt);
+ var reprData = Wallet.Decrypt(encryptedData, "aA1234dd", salt);
+
+ Assert.AreEqual(origData, reprData);
+ }
+
+ [Test]
+ public void EncryptionSaltTest()
+ {
+ var address = "5CcaF7yE6YU67TyPHjSwd9DKiVBTAS2AktdxNG3DeLYs63gF";
+
+ var seed = new byte[16];
+ _random.NextBytes(seed);
+
+ var hash = new byte[16];
+ _random.NextBytes(hash);
+
+ var salt = Wallet.GetSalt(Utils.GetPublicKeyFrom(address), hash);
+
+ var encodedData = Wallet.Encrypt(seed, "aA1234dd", salt);
+ var reprData = Wallet.Decrypt(encodedData, "aA1234dd", salt);
+
+ Assert.AreEqual(seed, reprData);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Substrate.NET.Wallet.Test/Substrate.NET.Wallet.Test.csproj b/Substrate.NET.Wallet.Test/Substrate.NET.Wallet.Test.csproj
index 853ee64..5afe054 100644
--- a/Substrate.NET.Wallet.Test/Substrate.NET.Wallet.Test.csproj
+++ b/Substrate.NET.Wallet.Test/Substrate.NET.Wallet.Test.csproj
@@ -16,6 +16,12 @@
+
+
+ Always
+
+
+
Always
diff --git a/Substrate.NET.Wallet.Test/WalletTest.cs b/Substrate.NET.Wallet.Test/WalletTest.cs
index 7d5176d..788406a 100644
--- a/Substrate.NET.Wallet.Test/WalletTest.cs
+++ b/Substrate.NET.Wallet.Test/WalletTest.cs
@@ -1,12 +1,11 @@
using NUnit.Framework;
-using Substrate.NET.Wallet;
using Substrate.NetApi;
using Substrate.NetApi.Model.Types;
using System;
using System.IO;
using System.Text;
-namespace SubstrateNetWalletTest
+namespace Substrate.NET.Wallet.Test
{
public class WalletTest
{
@@ -49,14 +48,6 @@ public void LoadWalletFromFileTest()
Wallet.Load(walletName1, out Wallet wallet1);
Assert.True(wallet1.IsStored);
Assert.False(wallet1.IsUnlocked);
- Assert.AreEqual("Ed25519",
- wallet1.FileStore.KeyType.ToString());
- Assert.AreEqual("5FfzQe73TTQhmSQCgvYocrr6vh1jJXEKB8xUB6tExfpKVCEZ",
- Utils.GetAddressFrom(wallet1.FileStore.PublicKey));
- Assert.AreEqual("0x17E39AC65C894EC263396E9B8720D78A7A5FE0CB6C5C05DC32E756DF3D5D2D9622DBFDB41CE0C9067B810BB03E1DCE9C89CFC061FBB063B616FF91F3AA31498158632A35601C91DFEE5DA869D44FA8A4",
- Utils.Bytes2HexString(wallet1.FileStore.EncryptedSeed));
- Assert.AreEqual("0x34F0627DB7C9BF1B580A597122622E95",
- Utils.Bytes2HexString(wallet1.FileStore.Salt));
wallet1.Unlock("aA1234dd");
Assert.True(wallet1.IsUnlocked);
@@ -64,18 +55,8 @@ public void LoadWalletFromFileTest()
Wallet.Load(walletName2, out Wallet wallet2);
Assert.True(wallet2.IsStored);
Assert.False(wallet2.IsUnlocked);
- Assert.AreEqual("Sr25519",
- wallet2.FileStore.KeyType.ToString());
- Assert.AreEqual("5Fe24e21Ff5vRtuWa4ZNPv1EGQz1zBq1VtT8ojqfmzo9k11P",
- Utils.GetAddressFrom(wallet2.FileStore.PublicKey));
- Assert.AreEqual("0xDA24A6B58BE083B58E3F011929B8A454B5FE9F1B91961DCC766D3E9F6AFE7AF96AAC1372DBA4537856F95C7E47A365C10590ACC092DB5AA95D6ECF5E06167B799AC6247178B7C51AC9B8F64C16602659",
- Utils.Bytes2HexString(wallet2.FileStore.EncryptedSeed));
- Assert.AreEqual("0xD048477FCAD42D83402CDE3B2AF369D4",
- Utils.Bytes2HexString(wallet2.FileStore.Salt));
wallet2.Unlock("aA1234dd");
Assert.True(wallet2.IsUnlocked);
- Assert.AreEqual("0x6BED04FEE1504A49825339A68F601F7739FA7CEBF3B5E6A4A2476979F53CF40A112F6ED717AE8E8F5134C784A07DE6F3B2F7DA51D8117C566547A5038D4B3C27",
- Utils.Bytes2HexString(wallet2.Account.PrivateKey));
}
[Test]
@@ -89,14 +70,6 @@ public void LoadWalletFromFileStore()
var walletName2 = "dev_wallet3";
Wallet.Load(walletName2, wallet1.FileStore, out Wallet wallet2);
- Assert.AreEqual("Ed25519",
- wallet2.FileStore.KeyType.ToString());
- Assert.AreEqual("5FfzQe73TTQhmSQCgvYocrr6vh1jJXEKB8xUB6tExfpKVCEZ",
- Utils.GetAddressFrom(wallet2.FileStore.PublicKey));
- Assert.AreEqual("0x17E39AC65C894EC263396E9B8720D78A7A5FE0CB6C5C05DC32E756DF3D5D2D9622DBFDB41CE0C9067B810BB03E1DCE9C89CFC061FBB063B616FF91F3AA31498158632A35601C91DFEE5DA869D44FA8A4",
- Utils.Bytes2HexString(wallet2.FileStore.EncryptedSeed));
- Assert.AreEqual("0x34F0627DB7C9BF1B580A597122622E95",
- Utils.Bytes2HexString(wallet2.FileStore.Salt));
wallet2.Unlock("aA1234dd");
Assert.True(wallet2.IsUnlocked);
}
diff --git a/Substrate.NET.Wallet.Test/dev_wallet1.json b/Substrate.NET.Wallet.Test/dev_wallet1.json
index 9c3acf1..c21ed8b 100644
--- a/Substrate.NET.Wallet.Test/dev_wallet1.json
+++ b/Substrate.NET.Wallet.Test/dev_wallet1.json
@@ -1 +1 @@
-{"KeyType":0,"PublicKey":"n54BhZZyCmeLir4glnLK+jIGHT9uBADZ1unbIo7hWk4=","EncryptedSeed":"F+OaxlyJTsJjOW6bhyDXinpf4MtsXAXcMudW3z1dLZYi2/20HODJBnuBC7A+Hc6cic/AYfuwY7YW/5HzqjFJgVhjKjVgHJHf7l2oadRPqKQ=","Salt":"NPBifbfJvxtYCllxImIulQ=="}
\ No newline at end of file
+{"Encoded":"7Iy0sjfUMqXtZAXOIDJfkKxqZ3Pf0uo3frA6CgvrDVcvQ196eeFtOin2fknAwIzG/NdInwYLxGTGNTKN3kSsQhHexPWFcEaD","Encoding":{"Content":["pkcs8","ed25519"],"Type":["scrypt","xsalsa20-poly1305"],"Version":"3"},"Address":"5HUSXsYAg9h9KaWHmb6r5FXypyR8V7hsdRxsRAcy5M968PD4","Meta":{"GenesisHash":"0x3160D66BC1D86500DBAC8AA57FB845CE","IsHardware":false,"Name":"SUBSTRATE","Tags":[],"WhenCreated":1696592070618}}
\ No newline at end of file
diff --git a/Substrate.NET.Wallet.Test/dev_wallet2.json b/Substrate.NET.Wallet.Test/dev_wallet2.json
index e8dd052..34a148b 100644
--- a/Substrate.NET.Wallet.Test/dev_wallet2.json
+++ b/Substrate.NET.Wallet.Test/dev_wallet2.json
@@ -1 +1 @@
-{"KeyType":1,"PublicKey":"nh0WPDIVJFrGLIO2ecRI2LYNKEyneZbpEvGsb3LQCF4=","EncryptedSeed":"2iSmtYvgg7WOPwEZKbikVLX+nxuRlh3Mdm0+n2r+evlqrBNy26RTeFb5XH5Ho2XBBZCswJLbWqldbs9eBhZ7eZrGJHF4t8Uaybj2TBZgJlk=","Salt":"0EhHf8rULYNALN47KvNp1A=="}
\ No newline at end of file
+{"Encoded":"qVxFVCuaqvo/8ThK0uTpdxaqOaSuttmi333Q4xRMmEx6mdyo9WOmrrjf2/Tk+ZFB6vfD/ZK45BwdX2ALBHCHIP8y2p0ulP0Q","Encoding":{"Content":["pkcs8","sr25519"],"Type":["scrypt","xsalsa20-poly1305"],"Version":"3"},"Address":"5ELuscqHb44nDVyz5kHBLXQdBdp3hxJfGB8A6WQmpQtgf96z","Meta":{"GenesisHash":"0xEEF01B3AB74708E1F8ADC640B303C4FA","IsHardware":false,"Name":"SUBSTRATE","Tags":[],"WhenCreated":1696592071243}}
\ No newline at end of file
diff --git a/Substrate.NET.Wallet/FileStore.cs b/Substrate.NET.Wallet/FileStore.cs
deleted file mode 100644
index 712a255..0000000
--- a/Substrate.NET.Wallet/FileStore.cs
+++ /dev/null
@@ -1,57 +0,0 @@
-using Substrate.NetApi.Model.Types;
-
-namespace Substrate.NET.Wallet
-{
- ///
- /// Wallet File description.
- ///
- public class FileStore
- {
- ///
- /// Initializes a new instance of the class.
- ///
- /// Type of the key.
- /// The public key.
- /// The encrypted seed.
- /// The salt.
- public FileStore(KeyType keyType, byte[] publicKey, byte[] encryptedSeed, byte[] salt)
- {
- KeyType = keyType;
- PublicKey = publicKey;
- EncryptedSeed = encryptedSeed;
- Salt = salt;
- }
-
- ///
- /// Gets the type of the key.
- ///
- ///
- /// The type of the key.
- ///
- public KeyType KeyType { get; }
-
- ///
- /// Gets the public key.
- ///
- ///
- /// The public key.
- ///
- public byte[] PublicKey { get; }
-
- ///
- /// Gets the encrypted seed.
- ///
- ///
- /// The encrypted seed.
- ///
- public byte[] EncryptedSeed { get; }
-
- ///
- /// Gets the salt.
- ///
- ///
- /// The salt.
- ///
- public byte[] Salt { get; }
- }
-}
\ No newline at end of file
diff --git a/Substrate.NET.Wallet/Model/EncodedData.cs b/Substrate.NET.Wallet/Model/EncodedData.cs
new file mode 100644
index 0000000..57cc78f
--- /dev/null
+++ b/Substrate.NET.Wallet/Model/EncodedData.cs
@@ -0,0 +1,10 @@
+namespace Substrate.NET.Wallet.Model
+{
+ public class EncodedData
+ {
+ public string Encoded { get; set; }
+ public EncodingInfo Encoding { get; set; }
+ public string Address { get; set; }
+ public Metadata Meta { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/Substrate.NET.Wallet/Model/EncodingInfo.cs b/Substrate.NET.Wallet/Model/EncodingInfo.cs
new file mode 100644
index 0000000..ea3aa24
--- /dev/null
+++ b/Substrate.NET.Wallet/Model/EncodingInfo.cs
@@ -0,0 +1,11 @@
+using System.Collections.Generic;
+
+namespace Substrate.NET.Wallet.Model
+{
+ public class EncodingInfo
+ {
+ public List Content { get; set; } = new List();
+ public List Type { get; set; } = new List();
+ public string Version { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/Substrate.NET.Wallet/Model/Metadata.cs b/Substrate.NET.Wallet/Model/Metadata.cs
new file mode 100644
index 0000000..b4f4549
--- /dev/null
+++ b/Substrate.NET.Wallet/Model/Metadata.cs
@@ -0,0 +1,13 @@
+using System.Collections.Generic;
+
+namespace Substrate.NET.Wallet.Model
+{
+ public class Metadata
+ {
+ public string GenesisHash { get; set; }
+ public bool IsHardware { get; set; }
+ public string Name { get; set; }
+ public List Tags { get; set; } = new List();
+ public long WhenCreated { get; set; } // This seems to be a Unix timestamp in milliseconds
+ }
+}
\ No newline at end of file
diff --git a/Substrate.NET.Wallet/Substrate.NET.Wallet.csproj b/Substrate.NET.Wallet/Substrate.NET.Wallet.csproj
index 7ccfeb8..ebc6a57 100644
--- a/Substrate.NET.Wallet/Substrate.NET.Wallet.csproj
+++ b/Substrate.NET.Wallet/Substrate.NET.Wallet.csproj
@@ -3,7 +3,7 @@
Substrate.NET.Wallet
netstandard2.0;netstandard2.1;net6.0
- 1.0.5
+ 1.0.6
BloGa Tech AG
Cedric Decoster
true
@@ -22,6 +22,7 @@
+
diff --git a/Substrate.NET.Wallet/Wallet.cs b/Substrate.NET.Wallet/Wallet.cs
index 9632803..1bae165 100644
--- a/Substrate.NET.Wallet/Wallet.cs
+++ b/Substrate.NET.Wallet/Wallet.cs
@@ -2,14 +2,20 @@
using Schnorrkel;
using Schnorrkel.Keys;
using Serilog;
+using Sodium;
+using Substrate.NET.Wallet.Model;
using Substrate.NetApi;
using Substrate.NetApi.Model.Types;
using Substrate.NetApi.Sign;
using System;
+using System.Collections.Generic;
using System.Linq;
+using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Text;
+[assembly: InternalsVisibleTo("Substrate.NET.Wallet.Test")]
+
namespace Substrate.NET.Wallet
{
///
@@ -29,7 +35,7 @@ public class Wallet
public string FileName { get; private set; }
- public FileStore FileStore { get; private set; }
+ public EncodedData FileStore { get; private set; }
///
///
@@ -37,7 +43,7 @@ public class Wallet
///
///
///
- private Wallet(Account account, string walletName, FileStore fileStore)
+ private Wallet(Account account, string walletName, EncodedData fileStore)
{
Account = account;
FileName = walletName;
@@ -77,36 +83,41 @@ public bool Unlock(string password, bool noCheck = false)
Logger.Information("Unlock wallet.");
- try
+ if (!Enum.TryParse(FileStore.Encoding.Content[1], true, out KeyType keyType))
{
- var pswBytes = Encoding.UTF8.GetBytes(password);
-
- pswBytes = SHA256.Create().ComputeHash(pswBytes);
+ Logger.Warning("Couldn't parse key type defintion.");
+ return false;
+ }
- var seed = ManagedAes.DecryptStringFromBytes_Aes(FileStore.EncryptedSeed, pswBytes, FileStore.Salt);
+ try
+ {
+ var publicKeyBytes = Utils.GetPublicKeyFrom(FileStore.Address);
+ var salt = GetSalt(Utils.GetPublicKeyFrom(FileStore.Address), Utils.HexToByteArray(FileStore.Meta.GenesisHash));
+ var seed = Decrypt(FileStore.Encoded, password, salt);
byte[] publicKey = null;
byte[] privateKey = null;
- switch (FileStore.KeyType)
+
+ switch (keyType)
{
case KeyType.Ed25519:
- Ed25519.KeyPairFromSeed(out publicKey, out privateKey, Utils.HexToByteArray(seed));
+ Ed25519.KeyPairFromSeed(out publicKey, out privateKey, seed);
break;
case KeyType.Sr25519:
- var miniSecret = new MiniSecret(Utils.HexToByteArray(seed), ExpandMode.Ed25519);
+ var miniSecret = new MiniSecret(seed, ExpandMode.Ed25519);
var getPair = miniSecret.GetPair();
privateKey = getPair.Secret.ToBytes();
publicKey = getPair.Public.Key;
break;
}
- if (!noCheck && !publicKey.SequenceEqual(FileStore.PublicKey))
+ if (!noCheck && !publicKey.SequenceEqual(publicKeyBytes))
{
throw new NotSupportedException("Public key check failed!");
}
- Account = Account.Build(FileStore.KeyType, privateKey, publicKey);
+ Account = Account.Build(keyType, privateKey, publicKey);
}
catch (Exception e)
{
@@ -117,6 +128,14 @@ public bool Unlock(string password, bool noCheck = false)
return true;
}
+ internal static byte[] GetSalt(byte[] hash1, byte[] hash2)
+ {
+ byte[] concHash = new byte[hash1.Length + hash2.Length];
+ Array.Copy(hash1, 0, concHash, 0, hash1.Length);
+ Array.Copy(hash2, 0, concHash, hash1.Length, hash2.Length);
+ return SHA256.Create().ComputeHash(concHash);
+ }
+
///
///
///
@@ -133,9 +152,15 @@ public bool Lock(string password, bool noCheck = false)
Logger.Information("Lock wallet.");
+ if (!Enum.TryParse(FileStore.Encoding.Content[1], true, out KeyType keyType))
+ {
+ Logger.Warning("Couldn't parse key type defintion.");
+ return false;
+ }
+
try
{
- Account = Account.Build(FileStore.KeyType, null, Account.Bytes);
+ Account = Account.Build(keyType, null, Account.Bytes);
}
catch (Exception e)
{
@@ -183,14 +208,22 @@ public static bool Load(string walletName, out Wallet wallet)
}
var walletFileName = ConcatWalletFileType(walletName);
- if (!Caching.TryReadFile(walletFileName, out FileStore fileStore))
+ if (!Caching.TryReadFile(walletFileName, out EncodedData fileStore))
{
Logger.Warning("Failed to load wallet file '{walletFileName}'!", walletFileName);
return false;
}
+ if (!Enum.TryParse(fileStore.Encoding.Content[1], true, out KeyType keyType))
+ {
+ Logger.Warning("Couldn't parse key type defintion.");
+ return false;
+ }
+
+ var publicKeyBytes = Utils.HexToByteArray(fileStore.Address);
+
var newAccount = new Account();
- newAccount.Create(fileStore.KeyType, fileStore.PublicKey);
+ newAccount.Create(keyType, publicKeyBytes);
wallet = new Wallet(newAccount, walletName, fileStore);
@@ -198,13 +231,14 @@ public static bool Load(string walletName, out Wallet wallet)
}
///
- /// Load the wallet from file store object.
+ /// Load the wallet from the file store object.
///
///
///
///
+ ///
///
- public static bool Load(string walletName, FileStore fileStore, out Wallet wallet)
+ public static bool Load(string walletName, EncodedData fileStore, out Wallet wallet, bool tryPersist = true)
{
wallet = null;
@@ -214,8 +248,29 @@ public static bool Load(string walletName, FileStore fileStore, out Wallet walle
return false;
}
+ if (!Enum.TryParse(fileStore.Encoding.Content[1], true, out KeyType keyType))
+ {
+ Logger.Warning("Couldn't parse key type defintion.");
+ return false;
+ }
+
+ var publicKeyBytes = Utils.HexToByteArray(fileStore.Address);
+
var newAccount = new Account();
- newAccount.Create(fileStore.KeyType, fileStore.PublicKey);
+ newAccount.Create(keyType, publicKeyBytes);
+
+ if (tryPersist)
+ {
+ try
+ {
+ Caching.Persist(Wallet.ConcatWalletFileType(walletName), fileStore);
+ }
+ catch (Exception e)
+ {
+ Logger.Warning("Failed to persist wallet file '{walletFileName}'! {error}", walletName, e);
+ return false;
+ }
+ }
wallet = new Wallet(newAccount, walletName, fileStore);
@@ -223,12 +278,16 @@ public static bool Load(string walletName, FileStore fileStore, out Wallet walle
}
///
- /// Creates the asynchronous.
+ /// Creates the from random.
///
- /// The password.
- /// Name of the wallet.
+ ///
+ ///
+ ///
+ ///
+ ///
///
- public static bool CreateFromRandom(string password, KeyType keyType, string walletName, out Wallet wallet)
+ ///
+ public static bool CreateFromRandom(string password, KeyType keyType, string walletName, out Wallet wallet, bool tryPersist = true)
{
wallet = null;
@@ -241,7 +300,7 @@ public static bool CreateFromRandom(string password, KeyType keyType, string wal
if (!IsValidPassword(password))
{
Logger.Warning(
- "Password isn't is invalid, please provide a proper password. Minmimu eight size and must have upper, lower and digits.");
+ "Password is invalid, please provide a proper password. Minmimu eight size and must have upper, lower and digits.");
return false;
}
@@ -253,9 +312,7 @@ public static bool CreateFromRandom(string password, KeyType keyType, string wal
var memoryBytes = randomBytes.AsMemory();
- var pswBytes = Encoding.UTF8.GetBytes(password);
-
- var salt = memoryBytes.Slice(0, 16).ToArray();
+ var hash = memoryBytes.Slice(0, 16).ToArray();
var seed = memoryBytes.Slice(16, 32).ToArray();
@@ -276,28 +333,167 @@ public static bool CreateFromRandom(string password, KeyType keyType, string wal
throw new NotImplementedException($"KeyType {keyType} isn't implemented!");
}
- pswBytes = SHA256.Create().ComputeHash(pswBytes);
-
- var encryptedSeed =
- ManagedAes.EncryptStringToBytes_Aes(
- Utils.Bytes2HexString(seed, Utils.HexStringFormat.Pure), pswBytes, salt);
+ var fileStore = GetFileStore(account.Value, account.KeyType, hash, seed, password);
- var fileStore = new FileStore(keyType, account.Bytes, encryptedSeed, salt);
- Caching.Persist(Wallet.ConcatWalletFileType(walletName), fileStore);
+ if (tryPersist)
+ {
+ try
+ {
+ Caching.Persist(Wallet.ConcatWalletFileType(walletName), fileStore);
+ }
+ catch (Exception e)
+ {
+ Logger.Warning("Failed to persist wallet file '{walletFileName}'! {error}", walletName, e);
+ return false;
+ }
+ }
wallet = new Wallet(account, walletName, fileStore);
+ return !tryPersist || wallet.Save(password);
+ }
+
+ private static EncodedData GetFileStore(string address, KeyType keyType, byte[] hash, byte[] seed, string password)
+ {
+ var salt = GetSalt(Utils.GetPublicKeyFrom(address), hash);
+
+ return new EncodedData
+ {
+ Encoded = Encrypt(seed, password, salt),
+ Encoding = new Model.EncodingInfo
+ {
+ Content = new List { "pkcs8", keyType.ToString().ToLower() },
+ Type = new List { "scrypt", "xsalsa20-poly1305" },
+ Version = "3"
+ },
+ Address = address,
+ Meta = new Metadata
+ {
+ GenesisHash = Utils.Bytes2HexString(hash),
+ IsHardware = false,
+ Name = "SUBSTRATE",
+ Tags = new List(),
+ WhenCreated = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
+ }
+ };
+ }
+
+ private bool Save(string password)
+ {
+ if (!IsValidPassword(password))
+ {
+ Logger.Warning(
+ "Password is invalid, please provide a proper password. Minmimu eight size and must have upper, lower and digits.");
+ return false;
+ }
+
+ if (!IsUnlocked)
+ {
+ Logger.Warning("Unlock wallet first, before you store it.");
+ return false;
+ }
+
+ Caching.Persist(Wallet.ConcatWalletFileType(FileName), FileStore);
+
return true;
}
///
- /// Creates the asynchronous.
+ /// Encrypts the specified data.
///
- /// The password.
- /// The mnemonic.
- /// Name of the wallet.
+ ///
+ ///
+ ///
+ ///
+ public static string Encrypt(byte[] data, string password, byte[] salt)
+ {
+ // Derive the encryption key from the password using Scrypt
+ byte[] derivedKey = DeriveKeyUsingScrypt(password, salt, 32); // 32 bytes = 256 bits
+
+ // Encrypt the content using the derived key and XSalsa20-Poly1305
+ byte[] nonce;
+ byte[] encryptedDataBytes = EncryptUsingXSalsa20Poly1305(data, derivedKey, out nonce);
+
+ // Combine nonce and encrypted data
+ byte[] combined = new byte[nonce.Length + encryptedDataBytes.Length];
+ Array.Copy(nonce, 0, combined, 0, nonce.Length);
+ Array.Copy(encryptedDataBytes, 0, combined, nonce.Length, encryptedDataBytes.Length);
+
+ return Convert.ToBase64String(combined);
+ }
+
+ ///
+ /// Decrypts the specified encoded.
+ ///
+ ///
+ ///
+ ///
///
- public static bool CreateFromMnemonic(string password, string mnemonic, KeyType keyType, Mnemonic.BIP39Wordlist bIP39Wordlist, string walletName, out Wallet wallet)
+ public static byte[] Decrypt(string encoded, string password, byte[] salt)
+ {
+ byte[] combined = Convert.FromBase64String(encoded);
+
+ // Extract nonce and encrypted data
+ byte[] nonce = new byte[24];
+ byte[] encryptedDataBytes = new byte[combined.Length - nonce.Length];
+ Array.Copy(combined, 0, nonce, 0, nonce.Length);
+ Array.Copy(combined, nonce.Length, encryptedDataBytes, 0, encryptedDataBytes.Length);
+
+ // Derive the encryption key from the password using Scrypt
+ byte[] derivedKey = DeriveKeyUsingScrypt(password, salt, 32); // 32 bytes = 256 bits
+
+ // Decrypt the content using the derived key and XSalsa20-Poly1305
+ return DecryptUsingXSalsa20Poly1305(encryptedDataBytes, derivedKey, nonce);
+ }
+
+ ///
+ /// Derives the key using scrypt.
+ ///
+ ///
+ ///
+ ///
+ ///
+ private static byte[] DeriveKeyUsingScrypt(string password, byte[] salt, int keyLengthInBytes)
+ => PasswordHash.ScryptHashBinary(Encoding.UTF8.GetBytes(password), salt, PasswordHash.Strength.Medium, keyLengthInBytes);
+
+ ///
+ /// Encrypts the using XSalsa20Poly1305.
+ ///
+ ///
+ ///
+ ///
+ ///
+ private static byte[] EncryptUsingXSalsa20Poly1305(byte[] plainText, byte[] key, out byte[] nonce)
+ {
+ nonce = SecretBox.GenerateNonce();
+ return SecretBox.Create(plainText, nonce, key);
+ }
+
+ ///
+ /// Decrypts the using XSalsa20Poly1305.
+ ///
+ ///
+ ///
+ ///
+ ///
+ private static byte[] DecryptUsingXSalsa20Poly1305(byte[] cipherText, byte[] key, byte[] nonce)
+ {
+ return SecretBox.Open(cipherText, nonce, key);
+ }
+
+ ///
+ /// Creates the from mnemonic.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static bool CreateFromMnemonic(string password, string mnemonic, KeyType keyType, Mnemonic.BIP39Wordlist bIP39Wordlist, string walletName, out Wallet wallet, bool tryPersist = true)
{
wallet = null;
@@ -341,18 +537,22 @@ public static bool CreateFromMnemonic(string password, string mnemonic, KeyType
var memoryBytes = randomBytes.AsMemory();
- var pswBytes = Encoding.UTF8.GetBytes(password);
-
- var salt = memoryBytes.Slice(0, 16).ToArray();
+ var hash = memoryBytes.Slice(0, 16).ToArray();
- pswBytes = SHA256.Create().ComputeHash(pswBytes);
+ var fileStore = GetFileStore(account.Value, account.KeyType, hash, seed, password);
- var encryptedSeed =
- ManagedAes.EncryptStringToBytes_Aes(
- Utils.Bytes2HexString(seed, Utils.HexStringFormat.Pure), pswBytes, salt);
-
- var fileStore = new FileStore(keyType, account.Bytes, encryptedSeed, salt);
- Caching.Persist(Wallet.ConcatWalletFileType(walletName), fileStore);
+ if (tryPersist)
+ {
+ try
+ {
+ Caching.Persist(Wallet.ConcatWalletFileType(walletName), fileStore);
+ }
+ catch (Exception e)
+ {
+ Logger.Warning("Failed to persist wallet file '{walletFileName}'! {error}", walletName, e);
+ return false;
+ }
+ }
wallet = new Wallet(account, walletName, fileStore);