2
0
mirror of synced 2025-02-24 20:18:07 +00:00

Added sanity checksums to all BIP39 wordlists on load.

This commit is contained in:
Richard Moore 2018-07-14 20:51:51 -04:00
parent e6c8db88bd
commit 8d6fa3dc93
No known key found for this signature in database
GPG Key ID: 525F70A6FCABC295
6 changed files with 97 additions and 62 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,15 +1,11 @@
import { register, Wordlist } from './wordlist'; import { check, register, Wordlist } from './wordlist';
import { id } from '../utils/hash';
import { hexlify } from '../utils/bytes'; import { hexlify } from '../utils/bytes';
import { toUtf8Bytes, toUtf8String } from '../utils/utf8'; import { toUtf8Bytes, toUtf8String } from '../utils/utf8';
import * as errors from '../utils/errors'; import * as errors from '../utils/errors';
const CheckId = "0xe561f52ac5f1fe957460337bc400302f30af56ac8da6adc7311efa89fbbc0777";
const data = [ const data = [
// 4-kana words // 4-kana words
@ -37,7 +33,7 @@ const data = [
// Maps each character into its kana value (the index) // 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" 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) { function hex(word: string) {
return hexlify(toUtf8Bytes(word)); return hexlify(toUtf8Bytes(word));
@ -46,13 +42,10 @@ function hex(word: string) {
const KiYoKu = '0xe3818de38284e3818f'; const KiYoKu = '0xe3818de38284e3818f';
const KyoKu = '0xe3818de38283e3818f' const KyoKu = '0xe3818de38283e3818f'
let error: Error = null; function loadWords(lang: Wordlist) {
function loadWords() { if (wordlist !== null) { return; }
if (words !== null) {
if (error) { throw error; } wordlist = [];
return;
}
words = [];
// Transforms for normalizing (sort is a not quite UTF-8) // Transforms for normalizing (sort is a not quite UTF-8)
var transform: { [key: string]: string | boolean } = {}; var transform: { [key: string]: string | boolean } = {};
@ -101,26 +94,25 @@ function loadWords() {
word.push((k & 0x40) ? 130: 129); word.push((k & 0x40) ? 130: 129);
word.push((k & 0x3f) + 128); 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 (!!). // For some reason kyoku and kiyoku are flipped in node (!!).
// The order SHOULD be: // The order SHOULD be:
// - kyoku // - kyoku
// - kiyoku // - kiyoku
if (hex(words[442]) === KiYoKu && hex(words[443]) === KyoKu) { if (hex(wordlist[442]) === KiYoKu && hex(wordlist[443]) === KyoKu) {
let tmp = words[442]; let tmp = wordlist[442];
words[442] = words[443]; wordlist[442] = wordlist[443];
words[443] = tmp; wordlist[443] = tmp;
} }
let check = id(words.join('\n')); if (check(lang) !== '0xcb36b09e6baa935787fd762ce65e80b0c6a8dabdfbc3a7f86ac0e2c4fd111600') {
if (check !== CheckId) { wordlist = null;
error = new Error('Japanese Wordlist FAILED to load; your browser sort is broken; please contract support@ethers.io.'); throw new Error('BIP39 Wordlist for ja (Japanese) FAILED');
throw error;
} }
} }
@ -130,13 +122,13 @@ class LangJa extends Wordlist {
} }
getWord(index: number): string { getWord(index: number): string {
loadWords(); loadWords(this);
return words[index]; return wordlist[index];
} }
getWordIndex(word: string): number { getWordIndex(word: string): number {
loadWords(); loadWords(this);
return words.indexOf(word); return wordlist.indexOf(word);
} }
split(mnemonic: string): Array<string> { split(mnemonic: string): Array<string> {

View File

@ -1,5 +1,5 @@
import { register, Wordlist } from './wordlist'; import { check, register, Wordlist } from './wordlist';
import { toUtf8String } from '../utils/utf8'; import { toUtf8String } from '../utils/utf8';
@ -28,7 +28,7 @@ function getHangul(code: number): string {
let wordlist: Array<string> = null; let wordlist: Array<string> = null;
function loadWords(): void { function loadWords(lang: Wordlist): void {
if (wordlist != null) { return; } if (wordlist != null) { return; }
wordlist = []; wordlist = [];
@ -45,9 +45,12 @@ function loadWords(): void {
}); });
wordlist.sort(); wordlist.sort();
}
loadWords();
if (check(lang) !== '0xf9eddeace9c5d3da9c93cf7d3cd38f6a13ed3affb933259ae865714e8a3ae71a') {
wordlist = null;
throw new Error('BIP39 Wordlist for ko (Korean) FAILED');
}
}
class LangKo extends Wordlist { class LangKo extends Wordlist {
@ -56,12 +59,12 @@ class LangKo extends Wordlist {
} }
getWord(index: number): string { getWord(index: number): string {
loadWords(); loadWords(this);
return wordlist[index]; return wordlist[index];
} }
getWordIndex(word: string): number { getWordIndex(word: string): number {
loadWords(); loadWords(this);
return wordlist.indexOf(word); return wordlist.indexOf(word);
} }
} }

View File

@ -1,5 +1,5 @@
import { register, Wordlist } from './wordlist'; import { check, register, Wordlist } from './wordlist';
import { toUtf8String } from '../utils/utf8'; import { toUtf8String } from '../utils/utf8';
@ -8,29 +8,47 @@ const deltaData = "FAZDC6BALcLZCA+GBARCW8wNCcDDZ8LVFBOqqDUiou+M42TFAyERXFb7EjhP+
// @TODO: Load lazily // @TODO: Load lazily
const words: { [key: string]: Array<string> } = { const wordlist: { [key: string]: Array<string> } = {
zh_cn: [], zh_cn: null,
zh_tw: [] zh_tw: null
} }
var codes = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; const Checks: { [key: string]: string } = {
var style = "~!@#$%^&*_-=[]{}|;:,.()<>?" zh_cn: '0x17bcc4d8547e5a7135e365d1ab443aaae95e76d8230c2782c67305d4f21497a1',
zh_tw: '0x51e720e90c7b87bec1d70eb6e74a21a449bd3ec9c020b01d3a40ed991b60ce5d'
}
let deltaOffset = 0; const codes = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
for (var i = 0; i < 2048; i++) { const style = "~!@#$%^&*_-=[]{}|;:,.()<>?"
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));
let common = s % 4; function loadWords(lang: Wordlist) {
for (let i = common; i < 3; i++) { if (wordlist[lang.locale] !== null) { return; }
bytes[i] = codes.indexOf(deltaData[deltaOffset++]) + ((i == 0) ? 228: 128);
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]),
];
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);
}
}
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');
} }
words.zh_tw.push(toUtf8String(bytes));
} }
class LangZh extends Wordlist { class LangZh extends Wordlist {
@ -39,11 +57,13 @@ class LangZh extends Wordlist {
} }
getWord(index: number): string { getWord(index: number): string {
return words[this.locale][index]; loadWords(this);
return wordlist[this.locale][index];
} }
getWordIndex(word: string): number { getWordIndex(word: string): number {
return words[this.locale].indexOf(word); loadWords(this);
return wordlist[this.locale].indexOf(word);
} }
split(mnemonic: string): Array<string> { split(mnemonic: string): Array<string> {

View File

@ -2,10 +2,22 @@
// This gets overriddenby gulp during bip39-XX // This gets overriddenby gulp during bip39-XX
var exportWordlist = false; var exportWordlist = false;
import { id } from '../utils/hash';
import { defineReadOnly } from '../utils/properties'; import { defineReadOnly } from '../utils/properties';
import { Wordlist as _Wordlist } from '../utils/types'; 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 { export abstract class Wordlist implements _Wordlist {
locale: string; locale: string;