Skip to content

Merge multilingual support with null safety #25

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

Open
wants to merge 9 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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ build/
# Directory created by dartdoc
doc/api/
.idea/
.vscode/
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,16 @@

- Remove async function


## 1.0.4

-forked from bip39
-added multi lingual support and languages

## 1.0.6

- Resolved nullsafety code issues

## 1.0.7

- Add multilangual mnemonic support: English, French, Italian and Spanish
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'
}
```
5 changes: 3 additions & 2 deletions example/bip39_example.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import 'package:bip39/bip39.dart' as bip39;
import 'package:bip39_multi_nullsafety/bip39_multi_nullsafety.dart' as bip39;

main() async {
String randomMnemonic = bip39.generateMnemonic();
print(randomMnemonic);
String seed = bip39.mnemonicToSeedHex("update elbow source spin squeeze horror world become oak assist bomb nuclear");
String seed = bip39.mnemonicToSeedHex(
"update elbow source spin squeeze horror world become oak assist bomb nuclear");
print(seed);
String mnemonic = bip39.entropyToMnemonic('00000000000000000000000000000000');
print(mnemonic);
Expand Down
File renamed without changes.
51 changes: 25 additions & 26 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 @@ -45,17 +46,18 @@ 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 +74,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 +89,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 +122,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
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