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 { 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> {

View File

@ -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);
}
}

View File

@ -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++) {
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));
const codes = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
const style = "~!@#$%^&*_-=[]{}|;:,.()<>?"
let common = s % 4;
for (let i = common; i < 3; i++) {
bytes[i] = codes.indexOf(deltaData[deltaOffset++]) + ((i == 0) ? 228: 128);
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]),
];
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 {
@ -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> {

View File

@ -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;