diff --git a/index.js b/index.js index 8150106..edd442d 100644 --- a/index.js +++ b/index.js @@ -42,6 +42,16 @@ my.REGEXP_ALPHABET = { b64u: /^[A-Za-z0-9\-_]+$/, b64t: /^[A-Za-z0-9\._]+$/, } +let lm = {} +lm.chinese = lm.cn = lm.zh = lm.china = lm.zhcn = lm.chinese_simplified = 'chinese_simplified' +lm.taiwanese = lm.tw = lm.zhtw = lm.chinese_traditional = 'chinese_traditional' +lm.en = lm.us = lm.uk = lm.enus = lm.enlish = 'english' +lm.fr = lm.france = lm.frfr = lm.french = 'french' +lm.it = lm.italy = lm.itit = lm.italian = 'italian' +lm.ko = lm.kr = lm.korean = lm.kokr = lm.koren = 'korean' +lm.ja = lm.jp = lm.japan = lm.jajp = lm.japanese = 'japanese' +lm.es = lm.eses = lm.spanish = 'spanish' +my.langMap = lm /** * @@ -99,20 +109,26 @@ class TicCrypto { * @return {Boolean} * @memberof TicCrypto */ - static is_secword ({ secword, mode = 'strict' } = {}) { - // 注意 not all 12 words combinations are valid for both bitcore and bip39, because there are checksum in mnemonic. 另外,实际上bitcore和bip39对12, 15, 18, 21, 24 长度的合法助记词都返回 true。 + static is_secword ({ secword, mode = 'strict', lang } = {}) { + // 注意 not all 12 words combinations are valid for both bitcore and bip39, because there are checksum in mnemonic. 另外,实际上bitcore和bip39对 12, 15, 18, 21, 24 长度的合法助记词都返回 true。 + //// for bitcore-mnemonic. 注意,bitcore-mnemonic 对少于12词的会抛出异常,很蠢。 - // if (typeof secword==='string' && 12===secword.split(/ +/).length) + // if (typeof secword==='string' && [12,15,18,21,24].includes(secword.split(/ +/).length)) // return BitcoreMnemonic.isValid(secword) // else // return false //// for bip39. 注意,bip39对当前defaultWordlist之外其他语言的合法 mnemonic 也返回 false,这一点不如 bitcore-mnemonic. 所以不能直接 bip39.validateMnemonic(secword) - if (typeof secword === 'string' && !/(^\s)|\s\s|(\s$)/.test(secword) && 12 === secword.split(/\s+/).length) { - if (mode === 'easy') return true // easy模式不检查校验等等严格的合法性了,反正 secword2seed是接受一切字符串的 - for (let lang of Object.keys(bip39.wordlists)) { - bip39.setDefaultWordlist(lang) - if (bip39.validateMnemonic(secword)) return true + if (typeof secword === 'string' && !/(^\s)|\s\s|(\s$)/.test(secword) && [12, 15, 18, 21, 24].includes(secword.split(/\s+/).length)) { + if (mode === 'easy') return true // easy模式不检查校验等等严格的合法性了,反正 secword_to_seed 是接受一切字符串的 + if (my.langMap[lang?.toLowerCase()]) { + // 指定了语言则针对该语言词库检查 + return bip39.validateMnemonic(secword, bip39.wordlists[my.langMap[lang?.toLowerCase()]]) + } else { + // 未指定语言则检查所有可能语言词库 + for (let lang of Object.keys(bip39.wordlists)) { + return bip39.validateMnemonic(secword, bip39.wordlists[my.langMap[lang?.toLowerCase()]]) + } } } return false @@ -359,7 +375,7 @@ class TicCrypto { * @return {Object} {pubkey, seckey, address,} * @memberof TicCrypto */ - static pass2keypair ({ pass, hasher } = {}) { + static pass_to_keypair ({ pass, hasher } = {}) { // 如果使用其他机制,例如密码、随机数,不使用secword,也可生成keypair if (this.is_hashable({ data: pass })) { hasher = my.HASHER_LIST.includes(hasher) ? hasher : my.HASHER @@ -398,9 +414,14 @@ class TicCrypto { * @return {*} * @memberof TicCrypto */ - static secword_to_entropy ({ secword } = {}) { + static secword_to_entropy ({ secword, lang } = {}) { // secword could be of length 12|15|18|21|24,which outputs hex of length 32|40|48|56|64. - return bip39.mnemonicToEntropy(secword) // results are the same for the same secword. + try { + return bip39.mnemonicToEntropy(secword, bip39.wordlists[my.langMap[lang?.toLowerCase()] || 'english']) // results are the same for the same secword + } catch (exception) { + // 如果助记词不合法(例如,语言不符合,长度非法,校验码不正确),会抛出异常。 + return '' + } } /** @@ -836,6 +857,7 @@ class TicCrypto { /** * 生成随机的助记词 + * accepts case-insensitive lang, such as 'chinese, cn, tw, en' * 1) 生成 128、160、192、224、256 位的随机墒 * 2)sha256 取前 墒长度/32 位作为校验和,即可为 4、5、6、7、8 位 * 3)在2048(=2^11)个词的表中,每11位指向一个词,共可生成 (128+4)/11=12, (160+5)/11=15, (192+6)/11=18, (224+7)/11=21, (256+8)/11=24 个词 @@ -845,8 +867,7 @@ class TicCrypto { * @return {*} * @memberof TicCrypto */ - static randomize_secword ({ lang = 'english', wordCount = 12 } = {}) { - // accepts case-insensitive lang, such as 'chinese, cn, tw, en' + static randomize_secword ({ lang, wordCount = 12 } = {}) { //// for BitcoreMnemonic // lang=lang?.toUpperCase() // let language = { ZHCN: 'CHINESE', ENUS: 'ENGLISH', FRFR: 'FRENCH', ITIT: 'ITALIAN', JAJP: 'JAPANESE', KOKR: 'KOREAN', ESES: 'SPANISH' }[lang] @@ -855,31 +876,8 @@ class TicCrypto { // for bip39 const bitLength = { 12: 128, 15: 160, 18: 192, 21: 224, 24: 256 }[wordCount] || 128 - - const langMap = { - zhcn: 'chinese_simplified', - zhtw: 'chinese_traditional', - enus: 'english', - frfr: 'french', - itit: 'italian', - jajp: 'japanese', - kokr: 'korean', - eses: 'spanish', - } - langMap.chinese = langMap.cn = langMap.zh = langMap.china = langMap.zhcn - langMap.taiwanese = langMap.tw = langMap.zhtw - langMap.en = langMap.us = langMap.uk = langMap.enus - langMap.fr = langMap.france = langMap.frfr - langMap.it = langMap.italy = langMap.itit - langMap.ko = langMap.kr = langMap.korean = langMap.kokr - langMap.ja = langMap.jp = langMap.japan = langMap.jajp - let language = 'english' - if (typeof lang === 'string') { - lang = lang.toLowerCase() - language = langMap[lang] || (bip39.wordlists[lang] ? lang : 'english') - } - // bip39.setDefaultWordlist(language) - return bip39.generateMnemonic(bitLength, undefined, bip39.wordlists[language]) // 内部使用 crypto.randomBytes 来获取随机墒 + // bip39.setDefaultWordlist(langMap[lang?.toLowerCase()] || 'english') + return bip39.generateMnemonic(bitLength, undefined, bip39.wordlists[my.langMap[lang?.toLowerCase()] || 'english']) // 内部使用 crypto.randomBytes 来获取随机墒 } /**