Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mctrivia patch 1 #26

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# BIP39
# BIP39_MULTI

Dart implementation of [Bitcoin BIP39](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki): Mnemonic code for generating deterministic keys

Expand Down Expand Up @@ -34,7 +34,7 @@ bip39.validateMnemonic('basket actual')


``` dart
import 'package:bip39/bip39.dart' as bip39;
import 'package:bip39_multi/bip39_multi.dart' as bip39;

main() {
// Only support BIP39 English word list
Expand All @@ -53,7 +53,7 @@ main() {
isValid = await bip39.validateMnemonic('basket actual');
// => false

String entropy = bip39.mnemonicToEntropy(mnemonic)
String entropy = bip39.mnemonicToEntropy(mnemonic);
// => String '00000000000000000000000000000000'
}
```
2 changes: 1 addition & 1 deletion example/bip39_example.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import 'package:bip39/bip39.dart' as bip39;
import 'package:bip39_multi/bip39_multi.dart' as bip39;

main() async {
String randomMnemonic = bip39.generateMnemonic();
Expand Down
9 changes: 9 additions & 0 deletions lib/bip39_multi.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/// Support for doing something awesome.
///
/// More dartdocs go here.
library bip39_multi;

export 'src/bip39_multi_base.dart';
export 'src/wordlists/all.dart';

// TODO: Export any libraries intended for clients of this package.
49 changes: 25 additions & 24 deletions lib/src/bip39_base.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import 'package:crypto/crypto.dart' show sha256;
import 'package:hex/hex.dart';

import 'utils/pbkdf2.dart';
import 'wordlists/english.dart';
import 'wordlists/all.dart';

const String _defaultLanguage="english";
const int _SIZE_BYTE = 255;
const _INVALID_MNEMONIC = 'Invalid mnemonic';
const _INVALID_ENTROPY = 'Invalid entropy';
Expand Down Expand Up @@ -46,16 +47,19 @@ Uint8List _randomBytes(int size) {
return bytes;
}

String generateMnemonic(
{int strength = 128, RandomBytes randomBytes = _randomBytes}) {
String generateMnemonic({
int strength = 128,
RandomBytes randomBytes = _randomBytes,
String language=_defaultLanguage
}) {
assert(strength % 32 == 0);
final entropy = randomBytes(strength ~/ 8);
return entropyToMnemonic(HEX.encode(entropy));
return entropyToMnemonic(HEX.encode(entropy),language: language);
}

String entropyToMnemonic(String entropyString) {
String entropyToMnemonic(String entropyString,{String language=_defaultLanguage}) {
final entropy = Uint8List.fromList(HEX.decode(entropyString));
if (entropy.length < 16) {
if (entropy.length < 4) {
throw ArgumentError(_INVALID_ENTROPY);
}
if (entropy.length > 32) {
Expand All @@ -72,9 +76,8 @@ String entropyToMnemonic(String entropyString) {
.allMatches(bits)
.map((match) => match.group(0)!)
.toList(growable: false);
List<String> wordlist = WORDLIST;
String words =
chunks.map((binary) => wordlist[_binaryToByte(binary)]).join(' ');
List<String> wordlist = WORDLIST[language];
String words = chunks.map((binary) => wordlist[_binaryToByte(binary)]).join(' ');
return words;
}

Expand All @@ -88,30 +91,28 @@ String mnemonicToSeedHex(String mnemonic, {String passphrase = ""}) {
return byte.toRadixString(16).padLeft(2, '0');
}).join('');
}

bool validateMnemonic(String mnemonic) {
bool validateMnemonic(String mnemonic,{String language=_defaultLanguage}) {
try {
mnemonicToEntropy(mnemonic);
mnemonicToEntropy(mnemonic,language: language);
} catch (e) {
return false;
}
return true;
}

String mnemonicToEntropy(mnemonic) {
String mnemonicToEntropy (mnemonic,{String language=_defaultLanguage}) {
var words = mnemonic.split(' ');
if (words.length % 3 != 0) {
throw new ArgumentError(_INVALID_MNEMONIC);
}
final wordlist = WORDLIST;
// convert word indices to 11 bit binary strings
final bits = words.map((word) {
final index = wordlist.indexOf(word);
if (index == -1) {
throw new ArgumentError(_INVALID_MNEMONIC);
}
return index.toRadixString(2).padLeft(11, '0');
}).join('');
final wordlist = WORDLIST[language];
// convert word indices to 11 bit binary strings
final bits = words.map((word) {
final index = wordlist.indexOf(word);
if (index == -1) {
throw new ArgumentError(_INVALID_MNEMONIC);
}
return index.toRadixString(2).padLeft(11, '0');
}).join('');
// split the binary string into ENT/CS
final dividerIndex = (bits.length / 33).floor() * 32;
final entropyBits = bits.substring(0, dividerIndex);
Expand All @@ -123,7 +124,7 @@ String mnemonicToEntropy(mnemonic) {
.allMatches(entropyBits)
.map((match) => _binaryToByte(match.group(0)!))
.toList(growable: false));
if (entropyBytes.length < 16) {
if (entropyBytes.length < 4) {
throw StateError(_INVALID_ENTROPY);
}
if (entropyBytes.length > 32) {
Expand Down
147 changes: 147 additions & 0 deletions lib/src/bip39_multi_base.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import 'dart:async';
import 'dart:math';
import 'dart:typed_data';
import 'package:crypto/crypto.dart' show sha256;
import 'package:hex/hex.dart';
import 'utils/pbkdf2.dart';
import 'wordlists/all.dart';

const String _defaultLanguage="english";
const int _SIZE_BYTE = 255;
const _INVALID_MNEMONIC = 'Invalid mnemonic';
const _INVALID_ENTROPY = 'Invalid entropy';
const _INVALID_CHECKSUM = 'Invalid mnemonic checksum';

typedef Uint8List RandomBytes(int size);

int _binaryToByte(String binary) {
return int.parse(binary, radix: 2);
}

String _bytesToBinary(Uint8List bytes) {
return bytes.map((byte) => byte.toRadixString(2).padLeft(8, '0')).join('');
}

//Uint8List _createUint8ListFromString( String s ) {
// var ret = new Uint8List(s.length);
// for( var i=0 ; i<s.length ; i++ ) {
// ret[i] = s.codeUnitAt(i);
// }
// return ret;
//}


String _deriveChecksumBits(Uint8List entropy) {
final ENT = entropy.length * 8;
final CS = ENT ~/ 32;
final hash = sha256.newInstance().convert(entropy);
return _bytesToBinary(Uint8List.fromList(hash.bytes)).substring(0, CS);
}


Uint8List _randomBytes(int size) {
final rng = Random.secure();
final bytes = Uint8List(size);
for (var i = 0; i < size; i++) {
bytes[i] = rng.nextInt(_SIZE_BYTE);
}
return bytes;
}
String generateMnemonic({
int strength = 128,
RandomBytes randomBytes = _randomBytes,
String language=_defaultLanguage
}) {
assert(strength % 32 == 0);
final entropy = randomBytes(strength ~/ 8);
return entropyToMnemonic(HEX.encode(entropy),language: language);
}
String entropyToMnemonic(String entropyString,{String language=_defaultLanguage}) {
final entropy = HEX.decode(entropyString);
if (entropy.length < 4) {
throw ArgumentError(_INVALID_ENTROPY);
}
if (entropy.length > 32) {
throw ArgumentError(_INVALID_ENTROPY);
}
if (entropy.length % 4 != 0) {
throw ArgumentError(_INVALID_ENTROPY);
}
final entropyBits = _bytesToBinary(entropy);
final checksumBits = _deriveChecksumBits(entropy);
final bits = entropyBits + checksumBits;
final regex = new RegExp(r".{1,11}", caseSensitive: false, multiLine: false);
final chunks = regex
.allMatches(bits)
.map((match) => match.group(0))
.toList(growable: false);
print("***************");
print(language);
List<String> wordlist = WORDLIST[language];
String words = chunks.map((binary) => wordlist[_binaryToByte(binary)]).join(' ');
return words;
}
Uint8List mnemonicToSeed(String mnemonic) {
final pbkdf2 = new PBKDF2();
return pbkdf2.process(mnemonic);
}
String mnemonicToSeedHex(String mnemonic) {
return mnemonicToSeed(mnemonic).map((byte) {
return byte.toRadixString(16).padLeft(2, '0');
}).join('');
}
bool validateMnemonic(String mnemonic,{String language=_defaultLanguage}) {
try {
mnemonicToEntropy(mnemonic,language: language);
} catch (e) {
return false;
}
return true;
}
String mnemonicToEntropy (mnemonic,{String language=_defaultLanguage}) {
var words = mnemonic.split(' ');
if (words.length % 3 != 0) {
throw new ArgumentError(_INVALID_MNEMONIC);
}
final wordlist = WORDLIST[language];
// convert word indices to 11 bit binary strings
final bits = words.map((word) {
final index = wordlist.indexOf(word);
if (index == -1) {
throw new ArgumentError(_INVALID_MNEMONIC);
}
return index.toRadixString(2).padLeft(11, '0');
}).join('');
// split the binary string into ENT/CS
final dividerIndex = (bits.length / 33).floor() * 32;
final entropyBits = bits.substring(0, dividerIndex);
final checksumBits = bits.substring(dividerIndex);

// calculate the checksum and compare
final regex = RegExp(r".{1,8}");
final entropyBytes = Uint8List.fromList(regex
.allMatches(entropyBits)
.map((match) => _binaryToByte(match.group(0)))
.toList(growable: false));
if (entropyBytes.length < 4) {
throw StateError(_INVALID_ENTROPY);
}
if (entropyBytes.length > 32) {
throw StateError(_INVALID_ENTROPY);
}
if (entropyBytes.length % 4 != 0) {
throw StateError(_INVALID_ENTROPY);
}
final newChecksum = _deriveChecksumBits(entropyBytes);
if (newChecksum != checksumBits) {
throw StateError(_INVALID_CHECKSUM);
}
return entropyBytes.map((byte) {
return byte.toRadixString(16).padLeft(2, '0');
}).join('');
}
// List<String>> _loadWordList() {
// final res = new Resource('package:bip39/src/wordlists/english.json').readAsString();
// List<String> words = (json.decode(res) as List).map((e) => e.toString()).toList();
// return words;
// }
11 changes: 11 additions & 0 deletions lib/src/wordlists/all.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import 'english.dart' as english;
import 'french.dart' as french;
import 'italian.dart' as italian;
import 'spanish.dart' as spanish;

const Map<String,List<String>> WORDLIST = {
"english": english.WORDLIST,
"french": french.WORDLIST,
"italian": italian.WORDLIST,
"spanish": spanish.WORDLIST
};
2 changes: 1 addition & 1 deletion lib/src/wordlists/english.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const WORDLIST = [
const List<String> WORDLIST = [
"abandon",
"ability",
"able",
Expand Down
Loading