forked from phantasma-io/Poltergeist
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
14 changed files
with
450 additions
and
5 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
148 changes: 148 additions & 0 deletions
148
Assets/Scripts/LegacySeedDecoder/RFC2898_PBDF2_HMAC-SHA512.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
11
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.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.