Skip to content

Commit

Permalink
Merge branch 'development'
Browse files Browse the repository at this point in the history
  • Loading branch information
alexmpa committed Nov 23, 2024
2 parents 19d98d7 + 3d5f380 commit 26eb723
Show file tree
Hide file tree
Showing 14 changed files with 450 additions and 5 deletions.
8 changes: 8 additions & 0 deletions Assets/Scripts/LegacySeedDecoder.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

78 changes: 78 additions & 0 deletions Assets/Scripts/LegacySeedDecoder/BIP39.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
using System;
using System.Text;

namespace Bitcoin.BIP39
{
/// <summary>
/// A .NET implementation of the Bitcoin Improvement Proposal - 39 (BIP39)
/// BIP39 specification used as reference located here: https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki
/// Made by [email protected]
/// v1.0.1.1
/// I ♥ Bitcoin :)
/// Bitcoin:1ETQjMkR1NNh4jwLuN5LxY7bMsHC9PUPSV
/// </summary>
public class BIP39
{
#region Private Attributes

private byte[] _passphraseBytes;
private string _mnemonicSentence;

#endregion

#region Public Constants and Enums

public const string cEmptyString = "";
public const string cSaltHeader = "mnemonic"; //this is the first part of the salt as described in the BIP39 spec

#endregion

#region Constructors

/// <summary>
/// Constructor to build a BIP39 object using a supplied Mnemonic sentence and passphrase. If you are not worried about saving the entropy bytes, or using custom words not in a wordlist, you should consider the static method to do this instead.
/// </summary>
/// <param name="mnemonicSentence">The mnemonic sentencs used to derive seed bytes, Please ensure NFKD Normalized</param>
/// <param name="passphrase">Optional passphrase used to protect seed bytes, defaults to empty</param>
/// <param name="language">Optional language to use for wordlist, if not specified it will auto detect language and if it can't detect it will default to English</param>
public BIP39(string mnemonicSentence, string passphrase = cEmptyString)
{
_mnemonicSentence = Utilities.NormaliseStringNfkd(mnemonicSentence.Trim()); //just making sure we don't have any leading or trailing spaces
_passphraseBytes = UTF8Encoding.UTF8.GetBytes(Utilities.NormaliseStringNfkd(passphrase));
string[] words = _mnemonicSentence.Split(new char[] { ' ' });

//if the sentence is not at least 12 characters or cleanly divisible by 3, it is bad!
if (words.Length < 12 || words.Length % 3 != 0)
{
throw new Exception("Mnemonic sentence must be at least 12 words and it will increase by 3 words for each increment in entropy. Please ensure your sentence is at leas 12 words and has no remainder when word count is divided by 3");
}
}
#endregion

#region Properties
/// <summary>
/// Gets the mnemonic sentence built from ent+cs
/// </summary>
public string MnemonicSentence
{
get
{
return _mnemonicSentence;
}
}

/// <summary>
/// Gets the bytes of the seed created from the mnemonic sentence. This could become your root in BIP32
/// </summary>
public byte[] SeedBytes
{
get
{
//literally this is the bulk of the decoupled seed generation code, easy.
byte[] salt = Utilities.MergeByteArrays(UTF8Encoding.UTF8.GetBytes(cSaltHeader),_passphraseBytes);
return Rfc2898_pbkdf2_hmacsha512.PBKDF2(UTF8Encoding.UTF8.GetBytes(Utilities.NormaliseStringNfkd(MnemonicSentence)), salt);
}
}
#endregion
}
}
11 changes: 11 additions & 0 deletions Assets/Scripts/LegacySeedDecoder/BIP39.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions Assets/Scripts/LegacySeedDecoder/BIP39Legacy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using Phantasma.Core.Cryptography;
using System.Linq;

public static class BIP39Legacy
{
public static string DecodeLegacySeedToWif(string mnemonicPhrase, string password)
{
var privKey = BIP39Legacy.MnemonicToPK(mnemonicPhrase, password);
var decryptedKeys = new PhantasmaKeys(privKey);
return decryptedKeys.ToWIF();
}

private static byte[] MnemonicToPK(string mnemonicPhrase, string password)
{
var bip = new Bitcoin.BIP39.BIP39(mnemonicPhrase, password);
return bip.SeedBytes.Take(32).ToArray();
}
}
11 changes: 11 additions & 0 deletions Assets/Scripts/LegacySeedDecoder/BIP39Legacy.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

148 changes: 148 additions & 0 deletions Assets/Scripts/LegacySeedDecoder/RFC2898_PBDF2_HMAC-SHA512.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
using System;
using System.Linq;

namespace Bitcoin.BIP39
{
/// <summary>
/// Implementation of the Rfc2898 PBKDF2 specification located here http://www.ietf.org/rfc/rfc2898.txt using HMACSHA512 but modified as opposed to PWDTKto match the BIP39 test vectors
/// Using BouncyCastle for the HMAC-SHA512 instead of Microsoft implementation
/// NOTE NOT IDENTICLE TO PWDTK (PWDTK is concatenating password and salt together before hashing the concatenated byte block, this is simply hashing the salt as what we are told to do in BIP39, yes the mnemonic sentence is provided as the hmac key)
/// Created by [email protected]
/// v1.1.0.0
/// Bitcoin:1ETQjMkR1NNh4jwLuN5LxY7bbip39HC9PUPSV
/// </summary>
public class Rfc2898_pbkdf2_hmacsha512
{
#region Private Attributes

//I made the variable names match the definition in RFC2898 - PBKDF2 where possible, so you can trace the code functionality back to the specification
private readonly Byte[] P;
private readonly Byte[] S;
private readonly Int32 c;
private Int32 dkLen;

#endregion

#region Public Constants

public const int CMinIterations = 2048;
//Minimum recommended salt length in Rfc2898
public const int CMinSaltLength = 8;
//Length of the Hash Digest Output - 512 bits - 64 bytes
public const int hLen = 64;

#endregion

#region Constructors

/// <summary>
/// Constructor to create Rfc2898_pbkdf2_hmacsha512 object ready to perform Rfc2898 PBKDF2 functionality
/// </summary>
/// <param name="password">The Password to be hashed and is also the HMAC key</param>
/// <param name="salt">Salt to be concatenated with the password</param>
/// <param name="iterations">Number of iterations to perform HMACSHA Hashing for PBKDF2</param>
public Rfc2898_pbkdf2_hmacsha512(Byte[] password, Byte[] salt, int iterations = CMinIterations)
{
P = password;
S = salt;
c = iterations;
}

#endregion

#region Public Members And Static Methods
/// <summary>
/// Derive Key Bytes using PBKDF2 specification listed in Rfc2898 and HMACSHA512 as the underlying PRF (Psuedo Random Function)
/// </summary>
/// <param name="keyLength">Length in Bytes of Derived Key</param>
/// <returns>Derived Key</returns>
public Byte[] GetDerivedKeyBytes_PBKDF2_HMACSHA512(Int32 keyLength)
{
//no need to throw exception for dkLen too long as per spec because dkLen cannot be larger than Int32.MaxValue so not worth the overhead to check
dkLen = keyLength;

Double l = Math.Ceiling((Double)dkLen / hLen);

Byte[] finalBlock = new Byte[0];

for (Int32 i = 1; i <= l; i++)
{
//Concatenate each block from F into the final block (T_1..T_l)
finalBlock = Utilities.MergeByteArrays(finalBlock, F(P, S, c, i));
}

//returning DK note r not used as dkLen bytes of the final concatenated block returned rather than <0...r-1> substring of final intermediate block + prior blocks as per spec
return finalBlock.Take(dkLen).ToArray();

}

/// <summary>
/// A static publicly exposed version of GetDerivedKeyBytes_PBKDF2_HMACSHA512 which matches the exact specification in Rfc2898 PBKDF2 using HMACSHA512
/// </summary>
/// <param name="P">Password passed as a Byte Array</param>
/// <param name="S">Salt passed as a Byte Array</param>
/// <param name="c">Iterations to perform the underlying PRF over</param>
/// <param name="dkLen">Length of Bytes to return, an AES 256 key wold require 32 Bytes</param>
/// <returns>Derived Key in Byte Array form ready for use by chosen encryption function</returns>
public static Byte[] PBKDF2(Byte[] P, Byte[] S, int c = CMinIterations, int dkLen = hLen)
{
Rfc2898_pbkdf2_hmacsha512 rfcObj = new Rfc2898_pbkdf2_hmacsha512(P, S, c);
return rfcObj.GetDerivedKeyBytes_PBKDF2_HMACSHA512(dkLen);
}

#endregion

#region Private Members
//Main Function F as defined in Rfc2898 PBKDF2 spec
private Byte[] F(Byte[] P, Byte[] S, Int32 c, Int32 i)
{

//Salt and Block number Int(i) concatenated as per spec
Byte[] Si = Utilities.MergeByteArrays(S, INT(i));

//Initial hash (U_1) using password and salt concatenated with Int(i) as per spec
Byte[] temp = PRF(Si, P);

//Output block filled with initial hash value or U_1 as per spec
Byte[] U_c = temp;

for (Int32 C = 1; C < c; C++)
{
//rehashing the password using the previous hash value as salt as per spec
temp = PRF(temp, P);

for (Int32 j = 0; j < temp.Length; j++)
{
//xor each byte of the each hash block with each byte of the output block as per spec
U_c[j] ^= temp[j];
}
}

//return a T_i block for concatenation to create the final block as per spec
return U_c;
}

//PRF function as defined in Rfc2898 PBKDF2 spec
private Byte[] PRF(Byte[] S, Byte[] hmacKey)
{
//HMACSHA512 Hashing, better than the HMACSHA1 in Microsofts implementation ;)
return Utilities.HmacSha512Digest(S, 0, S.Count(), hmacKey);
}

//This method returns the 4 octet encoded Int32 with most significant bit first as per spec
private Byte[] INT(Int32 i)
{
Byte[] I = BitConverter.GetBytes(i);

//Make sure most significant bit is first
if (BitConverter.IsLittleEndian)
{
Array.Reverse(I);
}

return I;
}

#endregion
}
}
11 changes: 11 additions & 0 deletions Assets/Scripts/LegacySeedDecoder/RFC2898_PBDF2_HMAC-SHA512.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

64 changes: 64 additions & 0 deletions Assets/Scripts/LegacySeedDecoder/Utilities.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
using System;
using System.Text;
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Macs;
using Org.BouncyCastle.Crypto;


namespace Bitcoin.BIP39
{
/// <summary>
/// A Library that provides common functionality between my other Bitcoin Modules
/// Made by [email protected]
/// v1.0.0.2
/// Bitcoin:1ETQjMkR1NNh4jwLuN5LxY7bMsHC9PUPSV
/// </summary>
public static class Utilities
{
/// <summary>
/// Calculates the 64 byte checksum in accordance with HMAC-SHA512
/// </summary>
/// <param name="input">The bytes to derive the checksum from</param>
/// <param name="offset">Where to start calculating checksum in the input bytes</param>
/// <param name="length">Length of buytes to use to calculate checksum</param>
/// <param name="hmacKey">HMAC Key used to generate the checksum (note differing HMAC Keys provide unique checksums)</param>
/// <returns></returns>
public static byte[] HmacSha512Digest(byte[] input, int offset, int length, byte[] hmacKey)
{
byte[] output = new byte[64];
HMac _hmacsha512Obj;
_hmacsha512Obj = new HMac(new Sha512Digest());
ICipherParameters param = new Org.BouncyCastle.Crypto.Parameters.KeyParameter(hmacKey);
_hmacsha512Obj.Init(param);
_hmacsha512Obj.BlockUpdate(input, offset, length);
_hmacsha512Obj.DoFinal(output, 0);
return output;
}

/// <summary>
/// Merges two byte arrays
/// </summary>
/// <param name="source1">first byte array</param>
/// <param name="source2">second byte array</param>
/// <returns>A byte array which contains source1 bytes followed by source2 bytes</returns>
public static Byte[] MergeByteArrays(Byte[] source1, Byte[] source2)
{
//Most efficient way to merge two arrays this according to http://stackoverflow.com/questions/415291/best-way-to-combine-two-or-more-byte-arrays-in-c-sharp
Byte[] buffer = new Byte[source1.Length + source2.Length];
System.Buffer.BlockCopy(source1, 0, buffer, 0, source1.Length);
System.Buffer.BlockCopy(source2, 0, buffer, source1.Length, source2.Length);

return buffer;
}

/// <summary>
/// Normalises a string with NKFD normal form
/// </summary>
/// <param name="toNormalise">String to be normalised</param>
/// <returns>Normalised string</returns>
public static String NormaliseStringNfkd(String toNormalise)
{
return toNormalise.Trim().Normalize(NormalizationForm.FormKD);
}
}
}
11 changes: 11 additions & 0 deletions Assets/Scripts/LegacySeedDecoder/Utilities.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Assets/Scripts/Updater/UpdateChecker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public class UpdateChecker : MonoBehaviour
{
public string githubOwner = "phantasma-io"; // Your GitHub username
public string githubRepo = "PoltergeistLite"; // Your repository name
public string currentVersion = "1.2.0"; // Current version of your game
public string currentVersion = "1.3.0"; // Current version of your game

private const string GITHUB_RELEASES_URL = "https://github.com/";
private static string URL = "";
Expand Down
13 changes: 13 additions & 0 deletions Assets/Scripts/Wallet/AccountManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1525,6 +1525,19 @@ public string GetPhantasmaNftURL(string symbol, string tokenId)
return $"{url}{symbol.ToLower()}/{tokenId}";
}

public string GetEthExplorerURL(string address)
{
return $"https://etherscan.io/address/{address}";
}
public string GetBscExplorerURL(string address)
{
return $"https://bscscan.com/address/{address}";
}
public string GetN2ExplorerURL(string address)
{
return $"https://neo2.neotube.io/address/{address}";
}

public int AddWallet(string name, string wif, string password, bool legacySeed)
{
if (string.IsNullOrEmpty(name) || name.Length < 3)
Expand Down
Loading

0 comments on commit 26eb723

Please sign in to comment.