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 { 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> {
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,13 +8,23 @@ 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'
|
||||||
|
}
|
||||||
|
|
||||||
|
const codes = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||||
|
const style = "~!@#$%^&*_-=[]{}|;:,.()<>?"
|
||||||
|
|
||||||
|
function loadWords(lang: Wordlist) {
|
||||||
|
if (wordlist[lang.locale] !== null) { return; }
|
||||||
|
|
||||||
|
wordlist[lang.locale] = [];
|
||||||
|
|
||||||
let deltaOffset = 0;
|
let deltaOffset = 0;
|
||||||
for (var i = 0; i < 2048; i++) {
|
for (var i = 0; i < 2048; i++) {
|
||||||
@ -24,13 +34,21 @@ for (var i = 0; i < 2048; i++) {
|
|||||||
128 + codes.indexOf(data[i * 3 + 1]),
|
128 + codes.indexOf(data[i * 3 + 1]),
|
||||||
128 + codes.indexOf(data[i * 3 + 2]),
|
128 + codes.indexOf(data[i * 3 + 2]),
|
||||||
];
|
];
|
||||||
words.zh_cn.push(toUtf8String(bytes));
|
|
||||||
|
|
||||||
|
if (lang.locale === 'zh_tw') {
|
||||||
let common = s % 4;
|
let common = s % 4;
|
||||||
for (let i = common; i < 3; i++) {
|
for (let i = common; i < 3; i++) {
|
||||||
bytes[i] = codes.indexOf(deltaData[deltaOffset++]) + ((i == 0) ? 228: 128);
|
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 {
|
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> {
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user