Added sanity checksums to all BIP39 wordlists on load.
This commit is contained in:
parent
e6c8db88bd
commit
8d6fa3dc93
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,15 +1,11 @@
|
||||
|
||||
import { register, Wordlist } from './wordlist';
|
||||
|
||||
import { id } from '../utils/hash';
|
||||
import { check, register, Wordlist } from './wordlist';
|
||||
|
||||
import { hexlify } from '../utils/bytes';
|
||||
import { toUtf8Bytes, toUtf8String } from '../utils/utf8';
|
||||
|
||||
import * as errors from '../utils/errors';
|
||||
|
||||
const CheckId = "0xe561f52ac5f1fe957460337bc400302f30af56ac8da6adc7311efa89fbbc0777";
|
||||
|
||||
const data = [
|
||||
|
||||
// 4-kana words
|
||||
@ -37,7 +33,7 @@ const data = [
|
||||
// Maps each character into its kana value (the index)
|
||||
const mapping = "~~AzB~X~a~KN~Q~D~S~C~G~E~Y~p~L~I~O~eH~g~V~hxyumi~~U~~Z~~v~~s~~dkoblPjfnqwMcRTr~W~~~F~~~~~Jt"
|
||||
|
||||
let words: Array<string> = null;
|
||||
let wordlist: Array<string> = null;
|
||||
|
||||
function hex(word: string) {
|
||||
return hexlify(toUtf8Bytes(word));
|
||||
@ -46,13 +42,10 @@ function hex(word: string) {
|
||||
const KiYoKu = '0xe3818de38284e3818f';
|
||||
const KyoKu = '0xe3818de38283e3818f'
|
||||
|
||||
let error: Error = null;
|
||||
function loadWords() {
|
||||
if (words !== null) {
|
||||
if (error) { throw error; }
|
||||
return;
|
||||
}
|
||||
words = [];
|
||||
function loadWords(lang: Wordlist) {
|
||||
if (wordlist !== null) { return; }
|
||||
|
||||
wordlist = [];
|
||||
|
||||
// Transforms for normalizing (sort is a not quite UTF-8)
|
||||
var transform: { [key: string]: string | boolean } = {};
|
||||
@ -101,26 +94,25 @@ function loadWords() {
|
||||
word.push((k & 0x40) ? 130: 129);
|
||||
word.push((k & 0x3f) + 128);
|
||||
}
|
||||
words.push(toUtf8String(word));
|
||||
wordlist.push(toUtf8String(word));
|
||||
}
|
||||
}
|
||||
words.sort(sortJapanese);
|
||||
wordlist.sort(sortJapanese);
|
||||
|
||||
// For some reason kyoku and kiyoku are flipped in node (!!).
|
||||
// The order SHOULD be:
|
||||
// - kyoku
|
||||
// - kiyoku
|
||||
|
||||
if (hex(words[442]) === KiYoKu && hex(words[443]) === KyoKu) {
|
||||
let tmp = words[442];
|
||||
words[442] = words[443];
|
||||
words[443] = tmp;
|
||||
if (hex(wordlist[442]) === KiYoKu && hex(wordlist[443]) === KyoKu) {
|
||||
let tmp = wordlist[442];
|
||||
wordlist[442] = wordlist[443];
|
||||
wordlist[443] = tmp;
|
||||
}
|
||||
|
||||
let check = id(words.join('\n'));
|
||||
if (check !== CheckId) {
|
||||
error = new Error('Japanese Wordlist FAILED to load; your browser sort is broken; please contract support@ethers.io.');
|
||||
throw error;
|
||||
if (check(lang) !== '0xcb36b09e6baa935787fd762ce65e80b0c6a8dabdfbc3a7f86ac0e2c4fd111600') {
|
||||
wordlist = null;
|
||||
throw new Error('BIP39 Wordlist for ja (Japanese) FAILED');
|
||||
}
|
||||
}
|
||||
|
||||
@ -130,13 +122,13 @@ class LangJa extends Wordlist {
|
||||
}
|
||||
|
||||
getWord(index: number): string {
|
||||
loadWords();
|
||||
return words[index];
|
||||
loadWords(this);
|
||||
return wordlist[index];
|
||||
}
|
||||
|
||||
getWordIndex(word: string): number {
|
||||
loadWords();
|
||||
return words.indexOf(word);
|
||||
loadWords(this);
|
||||
return wordlist.indexOf(word);
|
||||
}
|
||||
|
||||
split(mnemonic: string): Array<string> {
|
||||
|
@ -1,5 +1,5 @@
|
||||
|
||||
import { register, Wordlist } from './wordlist';
|
||||
import { check, register, Wordlist } from './wordlist';
|
||||
|
||||
import { toUtf8String } from '../utils/utf8';
|
||||
|
||||
@ -28,7 +28,7 @@ function getHangul(code: number): string {
|
||||
|
||||
let wordlist: Array<string> = null;
|
||||
|
||||
function loadWords(): void {
|
||||
function loadWords(lang: Wordlist): void {
|
||||
if (wordlist != null) { return; }
|
||||
|
||||
wordlist = [];
|
||||
@ -45,9 +45,12 @@ function loadWords(): void {
|
||||
});
|
||||
|
||||
wordlist.sort();
|
||||
}
|
||||
loadWords();
|
||||
|
||||
if (check(lang) !== '0xf9eddeace9c5d3da9c93cf7d3cd38f6a13ed3affb933259ae865714e8a3ae71a') {
|
||||
wordlist = null;
|
||||
throw new Error('BIP39 Wordlist for ko (Korean) FAILED');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class LangKo extends Wordlist {
|
||||
@ -56,12 +59,12 @@ class LangKo extends Wordlist {
|
||||
}
|
||||
|
||||
getWord(index: number): string {
|
||||
loadWords();
|
||||
loadWords(this);
|
||||
return wordlist[index];
|
||||
}
|
||||
|
||||
getWordIndex(word: string): number {
|
||||
loadWords();
|
||||
loadWords(this);
|
||||
return wordlist.indexOf(word);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
|
||||
import { register, Wordlist } from './wordlist';
|
||||
import { check, register, Wordlist } from './wordlist';
|
||||
|
||||
import { toUtf8String } from '../utils/utf8';
|
||||
|
||||
@ -8,29 +8,47 @@ const deltaData = "FAZDC6BALcLZCA+GBARCW8wNCcDDZ8LVFBOqqDUiou+M42TFAyERXFb7EjhP+
|
||||
|
||||
// @TODO: Load lazily
|
||||
|
||||
const words: { [key: string]: Array<string> } = {
|
||||
zh_cn: [],
|
||||
zh_tw: []
|
||||
const wordlist: { [key: string]: Array<string> } = {
|
||||
zh_cn: null,
|
||||
zh_tw: null
|
||||
}
|
||||
|
||||
var codes = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
var style = "~!@#$%^&*_-=[]{}|;:,.()<>?"
|
||||
const Checks: { [key: string]: string } = {
|
||||
zh_cn: '0x17bcc4d8547e5a7135e365d1ab443aaae95e76d8230c2782c67305d4f21497a1',
|
||||
zh_tw: '0x51e720e90c7b87bec1d70eb6e74a21a449bd3ec9c020b01d3a40ed991b60ce5d'
|
||||
}
|
||||
|
||||
let deltaOffset = 0;
|
||||
for (var i = 0; i < 2048; i++) {
|
||||
const codes = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
const style = "~!@#$%^&*_-=[]{}|;:,.()<>?"
|
||||
|
||||
function loadWords(lang: Wordlist) {
|
||||
if (wordlist[lang.locale] !== null) { return; }
|
||||
|
||||
wordlist[lang.locale] = [];
|
||||
|
||||
let deltaOffset = 0;
|
||||
for (var i = 0; i < 2048; i++) {
|
||||
let s = style.indexOf(data[i * 3]);
|
||||
let bytes = [
|
||||
228 + (s >> 2),
|
||||
128 + codes.indexOf(data[i * 3 + 1]),
|
||||
128 + codes.indexOf(data[i * 3 + 2]),
|
||||
];
|
||||
words.zh_cn.push(toUtf8String(bytes));
|
||||
|
||||
if (lang.locale === 'zh_tw') {
|
||||
let common = s % 4;
|
||||
for (let i = common; i < 3; i++) {
|
||||
bytes[i] = codes.indexOf(deltaData[deltaOffset++]) + ((i == 0) ? 228: 128);
|
||||
}
|
||||
words.zh_tw.push(toUtf8String(bytes));
|
||||
}
|
||||
|
||||
wordlist[lang.locale].push(toUtf8String(bytes));
|
||||
}
|
||||
|
||||
if (check(lang) !== Checks[lang.locale]) {
|
||||
wordlist[lang.locale] = null;
|
||||
throw new Error('BIP39 Wordlist for ' + lang.locale + ' (Chinese) FAILED');
|
||||
}
|
||||
}
|
||||
|
||||
class LangZh extends Wordlist {
|
||||
@ -39,11 +57,13 @@ class LangZh extends Wordlist {
|
||||
}
|
||||
|
||||
getWord(index: number): string {
|
||||
return words[this.locale][index];
|
||||
loadWords(this);
|
||||
return wordlist[this.locale][index];
|
||||
}
|
||||
|
||||
getWordIndex(word: string): number {
|
||||
return words[this.locale].indexOf(word);
|
||||
loadWords(this);
|
||||
return wordlist[this.locale].indexOf(word);
|
||||
}
|
||||
|
||||
split(mnemonic: string): Array<string> {
|
||||
|
@ -2,10 +2,22 @@
|
||||
// This gets overriddenby gulp during bip39-XX
|
||||
var exportWordlist = false;
|
||||
|
||||
import { id } from '../utils/hash';
|
||||
|
||||
import { defineReadOnly } from '../utils/properties';
|
||||
|
||||
import { Wordlist as _Wordlist } from '../utils/types';
|
||||
|
||||
export function check(wordlist: _Wordlist) {
|
||||
var words = [];
|
||||
for (let i = 0; i < 2048; i++) {
|
||||
let word = wordlist.getWord(i);
|
||||
if (i !== wordlist.getWordIndex(word)) { return '0x'; }
|
||||
words.push(word);
|
||||
}
|
||||
return id(words.join('\n') + '\n');
|
||||
}
|
||||
|
||||
export abstract class Wordlist implements _Wordlist {
|
||||
locale: string;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user