Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 0 additions & 29 deletions Substrate.NET.Wallet.Test/ArrayExtensionTest.cs

This file was deleted.

6 changes: 3 additions & 3 deletions Substrate.NET.Wallet.Test/KeyringTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -208,10 +208,10 @@ public void KeyPairFromSeed()
var expected = Utils.HexToByteArray("46ebddef8cd9bb167dc30878d7113b7e168e6f0646beffd77d69d39bad76b47a");

var keyPair = Keyring.Keyring.KeyPairFromSeed(NetApi.Model.Types.KeyType.Sr25519, seed);
Assert.That(keyPair.PublicKey.Length, Is.EqualTo(Keys.PUBLIC_KEY_LENGTH));
Assert.That(keyPair.SecretKey.Length, Is.EqualTo(Keys.SECRET_KEY_LENGTH));
Assert.That(keyPair.Bytes.Length, Is.EqualTo(Keys.PUBLIC_KEY_LENGTH));
Assert.That(keyPair.PrivateKey.Length, Is.EqualTo(Keys.SECRET_KEY_LENGTH));

Assert.That(keyPair.PublicKey, Is.EqualTo(expected));
Assert.That(keyPair.Bytes, Is.EqualTo(expected));
}
}
}
8 changes: 5 additions & 3 deletions Substrate.NET.Wallet.Test/Keyrings/KeypairSr25519Tests.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
using NUnit.Framework;
using Substrate.NET.Schnorrkel;
using Substrate.NET.Wallet.Derivation;
using Substrate.NET.Wallet.Extensions;
using Substrate.NET.Wallet.Keyring;
using Substrate.NetApi;
using Substrate.NetApi.Extensions;
using Substrate.NetApi.Model.Types;
using Substrate.NetApi.Sign;
using System;
using System.Collections.Generic;
using System.Linq;

Expand Down Expand Up @@ -88,8 +88,10 @@ public void Sr25519_SignsAndVerifiesBothMethods()
// Just to check
// --
var concatenated_2 = miniSecret_Ed25519Bytes.GetPair().ToHalfEd25519Bytes();
var publicKey_2 = concatenated_2.SubArray(Keys.SECRET_KEY_LENGTH, Keys.SECRET_KEY_LENGTH + Keys.PUBLIC_KEY_LENGTH);
var secretKey_2 = concatenated_2.SubArray(0, Keys.SECRET_KEY_LENGTH);
Span<byte> concatenatedSpan = concatenated_2.AsSpan();
var publicKey_2 = concatenatedSpan.Slice(Keys.SECRET_KEY_LENGTH, Keys.PUBLIC_KEY_LENGTH).ToArray();
var secretKey_2 = concatenatedSpan.Slice(0, Keys.SECRET_KEY_LENGTH).ToArray();

var edBytes = miniSecret_Ed25519Bytes.ExpandToSecret().ToEd25519Bytes();
Assert.That(edBytes, Is.EquivalentTo(secretKey_2));
Assert.That(edBytes, Is.EquivalentTo(account_Ed25519Bytes.PrivateKey));
Expand Down
24 changes: 0 additions & 24 deletions Substrate.NET.Wallet/Extensions/AccountExtension.cs

This file was deleted.

36 changes: 0 additions & 36 deletions Substrate.NET.Wallet/Extensions/ArrayExtension.cs

This file was deleted.

9 changes: 4 additions & 5 deletions Substrate.NET.Wallet/Keyring/Keyring.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using Substrate.NET.Schnorrkel.Keys;
using Substrate.NET.Wallet.Derivation;
using Substrate.NET.Wallet.Extensions;
using Substrate.NetApi;
using Substrate.NetApi.Extensions;
using Substrate.NetApi.Model.Types;
Expand Down Expand Up @@ -222,7 +221,7 @@ public static byte[] JsonDecryptData(string password, byte[] encrypted, List<Wal
{
var scryptRes = Scrypt.FromBytes(encoded);
passwordBytes = Scrypt.ScryptEncode(password, scryptRes.Salt, scryptRes.Param).Password;
encrypted = encrypted.SubArray(SCRYPT_LENGTH);
encrypted = encrypted.AsSpan().Slice(SCRYPT_LENGTH).ToArray();
}

encoded = Chaos.NaCl.XSalsa20Poly1305.TryDecrypt(
Expand All @@ -237,7 +236,7 @@ public static byte[] JsonDecryptData(string password, byte[] encrypted, List<Wal
return encoded;
}

public static PairInfo KeyPairFromSeed(KeyType keyType, byte[] seed)
public static Account KeyPairFromSeed(KeyType keyType, byte[] seed)
{
if (seed.Length != 32)
throw new InvalidOperationException($"Seed is not 32 bytes (currently {seed.Length})");
Expand All @@ -246,11 +245,11 @@ public static PairInfo KeyPairFromSeed(KeyType keyType, byte[] seed)
{
case KeyType.Ed25519:
Chaos.NaCl.Ed25519.KeyPairFromSeed(out byte[] pubKey, out byte[] priKey, seed);
return new PairInfo(pubKey, priKey);
return Account.Build(keyType, priKey, pubKey);

case KeyType.Sr25519:
var miniSecret = new MiniSecret(seed, ExpandMode.Ed25519);
return new PairInfo(miniSecret.ExpandToPublic().Key, miniSecret.ExpandToSecret().ToEd25519Bytes());
return Account.Build(keyType, miniSecret.ExpandToSecret().ToEd25519Bytes(), miniSecret.ExpandToPublic().Key);

default:
throw new NotImplementedException($"KeyType {keyType} isn't implemented!");
Expand Down
79 changes: 42 additions & 37 deletions Substrate.NET.Wallet/Keyring/Pair.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using Chaos.NaCl;
using Substrate.NET.Wallet.Extensions;
using Substrate.NetApi.Extensions;
using Substrate.NetApi.Model.Types;
using System;
Expand All @@ -8,35 +7,6 @@

namespace Substrate.NET.Wallet.Keyring
{
public class DecodeResult : PairInfo
{
public DecodeResult(byte[] publicKey, byte[] privateKey, byte[] seed, byte[] secretKey) : base(publicKey, secretKey)
{
PrivateKey = privateKey;
Seed = seed;
}

public byte[] PrivateKey { get; }
public byte[] Seed { get; }
}

public class PairInfo
{
public PairInfo(byte[] publicKey) : this(publicKey, null)
{
}

public PairInfo(byte[] publicKey, byte[] secretKey)
{
PublicKey = publicKey;
SecretKey = secretKey;
}

public byte[] PublicKey { get; set; }
public byte[] SecretKey { get; set; }

}

public static class Pair
{
public const int PUB_LENGTH = 32;
Expand Down Expand Up @@ -74,27 +44,52 @@ public static Wallet CreatePair(KeyringAddress setup, Account account, Meta meta
return new Wallet(setup.ToSS58(account.Bytes, ss58Format), encoded, meta, account.Bytes, account.PrivateKey, setup.KeyType, encryptedEncoding);
}

public static PairInfo DecodePair(string password, byte[] encoded, List<WalletJson.EncryptedJsonEncoding> encryptionType)
/// <summary>
/// Decode a keypair from a JSON keypair
/// </summary>
/// <param name="password"></param>
/// <param name="encoded"></param>
/// <param name="encryptionType"></param>
/// <returns></returns>
/// <exception cref="InvalidOperationException"></exception>
public static Account DecodePair(string password, byte[] encoded, List<WalletJson.EncryptedJsonEncoding> encryptionType)
{
var decrypted = Keyring.JsonDecryptData(password, encoded, encryptionType);
var header = decrypted.SubArray(0, PKCS8_HEADER.Length);
var decryptedSpan = new Span<byte>(decrypted); // Convert the decrypted byte array to a Span<byte>

var header = decryptedSpan.Slice(0, PKCS8_HEADER.Length);

if (!header.SequenceEqual(PKCS8_HEADER))
throw new InvalidOperationException("Invalid PKCS8 header");

var offset = SEED_OFFSET + SEC_LENGTH;
var secretKey = decrypted.SubArray(SEED_OFFSET, offset);
var divider = decrypted.SubArray(offset, offset + PKCS8_DIVIDER.Length);
var secretKey = decryptedSpan.Slice(SEED_OFFSET, SEC_LENGTH).ToArray(); // Convert span slice to array
var divider = decryptedSpan.Slice(offset, PKCS8_DIVIDER.Length);

if (!divider.SequenceEqual(PKCS8_DIVIDER))
throw new InvalidOperationException("Invalid PKCS8 divider");

var publicOffset = offset + PKCS8_DIVIDER.Length;
var publicKey = decrypted.SubArray(publicOffset, publicOffset + PUB_LENGTH);
var publicKey = decryptedSpan.Slice(publicOffset, PUB_LENGTH).ToArray(); // Convert span slice to array

return new PairInfo(publicKey, secretKey);
if (secretKey.Length == 64)
{
return Account.Build(KeyType.Sr25519, secretKey, publicKey);
}
else
{
Chaos.NaCl.Ed25519.KeyPairFromSeed(out publicKey, out secretKey, encoded);
return Account.Build(KeyType.Ed25519, secretKey, publicKey);
}
}

/// <summary>
/// Encode a keypair into a byte array
/// </summary>
/// <param name="password"></param>
/// <param name="pair"></param>
/// <returns></returns>
/// <exception cref="InvalidOperationException"></exception>
public static byte[] EncodePair(string password, Account pair)
{
if (IsLocked(pair.PrivateKey))
Expand All @@ -110,7 +105,8 @@ public static byte[] EncodePair(string password, Account pair)
var scryptResult = Scrypt.ScryptEncode(password, ScryptParam.Default);

byte[] message = encoded;
byte[] secret = scryptResult.Password.SubArray(0, 32);
Span<byte> secretSpan = new Span<byte>(scryptResult.Password).Slice(0, 32);
byte[] secret = secretSpan.ToArray();
byte[] nonce = new byte[24].Populate();

var naclResult = XSalsa20Poly1305.Encrypt(message, secret, nonce);
Expand All @@ -121,6 +117,15 @@ public static byte[] EncodePair(string password, Account pair)
.ToArray();
}

/// <summary>
/// Transform a keypair into a JSON representation
/// </summary>
/// <param name="keyType"></param>
/// <param name="address"></param>
/// <param name="meta"></param>
/// <param name="encoded"></param>
/// <param name="isEncrypted"></param>
/// <returns></returns>
public static WalletFile ToJsonPair(KeyType keyType, string address, Meta meta, byte[] encoded, bool isEncrypted)
{
return new WalletFile()
Expand Down
12 changes: 1 addition & 11 deletions Substrate.NET.Wallet/Keyring/Pkcs8.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,7 @@ public static class Pkcs8
/// <returns></returns>
public static Account Decode(string password, byte[] encoded, List<WalletJson.EncryptedJsonEncoding> encryptedEncoding)
{
var decoded = Pair.DecodePair(password, encoded, encryptedEncoding);

if (decoded.SecretKey.Length == 64)
{
return Account.Build(KeyType.Sr25519, decoded.SecretKey, decoded.PublicKey);
}
else
{
Chaos.NaCl.Ed25519.KeyPairFromSeed(out byte[] publicKey, out byte[] privateKey, encoded);
return Account.Build(KeyType.Ed25519, privateKey, publicKey);
}
return Pair.DecodePair(password, encoded, encryptedEncoding);
}

/// <summary>
Expand Down
16 changes: 10 additions & 6 deletions Substrate.NET.Wallet/Keyring/Scrypt.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using Substrate.NET.Wallet.Extensions;
using Substrate.NetApi.Extensions;
using Substrate.NetApi.Extensions;
using System;
using System.Linq;
using System.Numerics;
Expand All @@ -20,10 +19,15 @@ public static class Scrypt
/// <exception cref="InvalidOperationException"></exception>
public static ScryptResult FromBytes(byte[] data)
{
var salt = data.SubArray(0, 32);
var N = new BigInteger(data.SubArray(32 + 0, 32 + 4));
var p = new BigInteger(data.SubArray(32 + 4, 32 + 8));
var r = new BigInteger(data.SubArray(32 + 8, 32 + 12));
var dataSpan = new Span<byte>(data);

// Extract salt directly using Span.Slice and convert to array
var salt = dataSpan.Slice(0, 32).ToArray();

// Convert slices for N, p, r directly to BigInteger using ReadOnlySpan
var N = new BigInteger(dataSpan.Slice(32 + 0, 4).ToArray());
var p = new BigInteger(dataSpan.Slice(32 + 4, 4).ToArray());
var r = new BigInteger(dataSpan.Slice(32 + 8, 4).ToArray());

if (N != ScryptParam.Default.IterationCount || p != ScryptParam.Default.ThreadCount || r != ScryptParam.Default.BlockSize)
{
Expand Down
Loading