| @@ 1-183 (lines=183) @@ | ||
| 1 | 'use strict' |
|
| 2 | let BigInteger = require('bigi') |
|
| 3 | let ALPHABET = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l' |
|
| 4 | ||
| 5 | // pre-compute lookup table |
|
| 6 | let SEPARATOR = ':' |
|
| 7 | let CSLEN = 8 |
|
| 8 | let ALPHABET_MAP = {} |
|
| 9 | for (let z = 0; z < ALPHABET.length; z++) { |
|
| 10 | let x = ALPHABET.charAt(z) |
|
| 11 | if (ALPHABET_MAP[x] !== undefined) { |
|
| 12 | throw new TypeError(x + ' is ambiguous') |
|
| 13 | } |
|
| 14 | ALPHABET_MAP[x] = z |
|
| 15 | } |
|
| 16 | ||
| 17 | function polymodStep (pre) { |
|
| 18 | let b = pre.shiftRight(35) |
|
| 19 | let mask = BigInteger.fromHex('07ffffffff') |
|
| 20 | ||
| 21 | let v = pre.and(mask).shiftLeft(new BigInteger('5')) |
|
| 22 | ||
| 23 | if (b.and(new BigInteger('1')).intValue() > 0) { |
|
| 24 | v = v.xor(BigInteger.fromHex('98f2bc8e61')) |
|
| 25 | } |
|
| 26 | if (b.and(new BigInteger('2')).intValue()) { |
|
| 27 | v = v.xor(BigInteger.fromHex('79b76d99e2')) |
|
| 28 | } |
|
| 29 | if (b.and(new BigInteger('4')).intValue()) { |
|
| 30 | v = v.xor(BigInteger.fromHex('f33e5fb3c4')) |
|
| 31 | } |
|
| 32 | if (b.and(new BigInteger('8')).intValue()) { |
|
| 33 | v = v.xor(BigInteger.fromHex('ae2eabe2a8')) |
|
| 34 | } |
|
| 35 | if (b.and(new BigInteger('16')).intValue()) { |
|
| 36 | v = v.xor(BigInteger.fromHex('1e4f43e470')) |
|
| 37 | } |
|
| 38 | ||
| 39 | return v |
|
| 40 | } |
|
| 41 | ||
| 42 | function prefixChk (prefix) { |
|
| 43 | let chk = new BigInteger('1') |
|
| 44 | for (let i = 0; i < prefix.length; ++i) { |
|
| 45 | let c = prefix.charCodeAt(i) |
|
| 46 | ||
| 47 | let mixwith = new BigInteger('' + (c & 0x1f)) |
|
| 48 | chk = polymodStep(chk).xor(mixwith) |
|
| 49 | } |
|
| 50 | ||
| 51 | chk = polymodStep(chk) |
|
| 52 | return chk |
|
| 53 | } |
|
| 54 | ||
| 55 | function encode (prefix, words) { |
|
| 56 | // too long? |
|
| 57 | if ((prefix.length + CSLEN + 1 + words.length) > 90) { |
|
| 58 | throw new TypeError('Exceeds Base32 maximum length') |
|
| 59 | } |
|
| 60 | ||
| 61 | prefix = prefix.toLowerCase() |
|
| 62 | ||
| 63 | // determine chk mod |
|
| 64 | let chk = prefixChk(prefix) |
|
| 65 | let result = prefix + SEPARATOR |
|
| 66 | for (let i = 0; i < words.length; ++i) { |
|
| 67 | let x = words[i] |
|
| 68 | if ((x >>> 5) !== 0) { |
|
| 69 | throw new Error('Non 5-bit word') |
|
| 70 | } |
|
| 71 | ||
| 72 | chk = polymodStep(chk).xor(new BigInteger('' + x)) |
|
| 73 | result += ALPHABET.charAt(x) |
|
| 74 | } |
|
| 75 | ||
| 76 | for (let i = 0; i < CSLEN; ++i) { |
|
| 77 | chk = polymodStep(chk) |
|
| 78 | } |
|
| 79 | chk = chk.xor(new BigInteger('1')) |
|
| 80 | for (let i = 0; i < CSLEN; ++i) { |
|
| 81 | let pos = 5 * (CSLEN - 1 - i) |
|
| 82 | let v2 = chk.shiftRight(new BigInteger('' + pos)).and(BigInteger.fromHex('1f')) |
|
| 83 | result += ALPHABET.charAt(v2.toString(10)) |
|
| 84 | } |
|
| 85 | ||
| 86 | return result |
|
| 87 | } |
|
| 88 | ||
| 89 | function decode (str) { |
|
| 90 | if (str.length < 8) { |
|
| 91 | throw new TypeError(str + ' too short') |
|
| 92 | } |
|
| 93 | if (str.length > 90) { |
|
| 94 | throw new TypeError(str + ' too long') |
|
| 95 | } |
|
| 96 | ||
| 97 | // don't allow mixed case |
|
| 98 | let lowered = str.toLowerCase() |
|
| 99 | let uppered = str.toUpperCase() |
|
| 100 | if (str !== lowered && str !== uppered) { |
|
| 101 | throw new Error('Mixed-case string ' + str) |
|
| 102 | } |
|
| 103 | ||
| 104 | str = lowered |
|
| 105 | ||
| 106 | let split = str.lastIndexOf(SEPARATOR) |
|
| 107 | if (split === -1) { |
|
| 108 | throw new Error('No separator character for ' + str) |
|
| 109 | } |
|
| 110 | ||
| 111 | if (split === 0) { |
|
| 112 | throw new Error('Missing prefix for ' + str) |
|
| 113 | } |
|
| 114 | ||
| 115 | let prefix = str.slice(0, split) |
|
| 116 | let wordChars = str.slice(split + 1) |
|
| 117 | if (wordChars.length < 6) { |
|
| 118 | throw new Error('Data too short') |
|
| 119 | } |
|
| 120 | ||
| 121 | let chk = prefixChk(prefix) |
|
| 122 | let words = [] |
|
| 123 | for (let i = 0; i < wordChars.length; ++i) { |
|
| 124 | let c = wordChars.charAt(i) |
|
| 125 | let v = ALPHABET_MAP[c] |
|
| 126 | if (v === undefined) { |
|
| 127 | throw new Error('Unknown character ' + c) |
|
| 128 | } |
|
| 129 | ||
| 130 | chk = polymodStep(chk).xor(new BigInteger('' + v)) |
|
| 131 | // not in the checksum? |
|
| 132 | if (i + CSLEN >= wordChars.length) { |
|
| 133 | continue |
|
| 134 | } |
|
| 135 | words.push(v) |
|
| 136 | } |
|
| 137 | ||
| 138 | if (chk.toString(10) !== '1') { |
|
| 139 | throw new Error('Invalid checksum for ' + str) |
|
| 140 | } |
|
| 141 | ||
| 142 | return { prefix, words } |
|
| 143 | } |
|
| 144 | ||
| 145 | function convert (data, inBits, outBits, pad) { |
|
| 146 | let value = 0 |
|
| 147 | let bits = 0 |
|
| 148 | let maxV = (1 << outBits) - 1 |
|
| 149 | ||
| 150 | let result = [] |
|
| 151 | for (let i = 0; i < data.length; ++i) { |
|
| 152 | value = (value << inBits) | data[i] |
|
| 153 | bits += inBits |
|
| 154 | ||
| 155 | while (bits >= outBits) { |
|
| 156 | bits -= outBits |
|
| 157 | result.push((value >>> bits) & maxV) |
|
| 158 | } |
|
| 159 | } |
|
| 160 | ||
| 161 | if (pad) { |
|
| 162 | if (bits > 0) { |
|
| 163 | result.push((value << (outBits - bits)) & maxV) |
|
| 164 | } |
|
| 165 | } else { |
|
| 166 | if (bits >= inBits) { |
|
| 167 | throw new Error('Excess padding') |
|
| 168 | } |
|
| 169 | if ((value << (outBits - bits)) & maxV) { |
|
| 170 | throw new Error('Non-zero padding') |
|
| 171 | } |
|
| 172 | } |
|
| 173 | ||
| 174 | return result |
|
| 175 | } |
|
| 176 | ||
| 177 | function toWords (bytes) { |
|
| 178 | return convert(bytes, 8, 5, true) |
|
| 179 | } |
|
| 180 | ||
| 181 | function fromWords (words) { |
|
| 182 | return convert(words, 5, 8, false) |
|
| 183 | } |
|
| 184 | ||
| 185 | module.exports = { decode, encode, toWords, fromWords } |
|
| 186 | ||
| @@ 1-182 (lines=182) @@ | ||
| 1 | 'use strict'; |
|
| 2 | ||
| 3 | var BigInteger = require('bigi'); |
|
| 4 | var ALPHABET = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l'; |
|
| 5 | ||
| 6 | // pre-compute lookup table |
|
| 7 | var SEPARATOR = ':'; |
|
| 8 | var CSLEN = 8; |
|
| 9 | var ALPHABET_MAP = {}; |
|
| 10 | for (var z = 0; z < ALPHABET.length; z++) { |
|
| 11 | var x = ALPHABET.charAt(z); |
|
| 12 | if (ALPHABET_MAP[x] !== undefined) { |
|
| 13 | throw new TypeError(x + ' is ambiguous'); |
|
| 14 | } |
|
| 15 | ALPHABET_MAP[x] = z; |
|
| 16 | } |
|
| 17 | ||
| 18 | function polymodStep(pre) { |
|
| 19 | var b = pre.shiftRight(35); |
|
| 20 | var mask = BigInteger.fromHex('07ffffffff'); |
|
| 21 | ||
| 22 | var v = pre.and(mask).shiftLeft(new BigInteger('5')); |
|
| 23 | ||
| 24 | if (b.and(new BigInteger('1')).intValue() > 0) { |
|
| 25 | v = v.xor(BigInteger.fromHex('98f2bc8e61')); |
|
| 26 | } |
|
| 27 | if (b.and(new BigInteger('2')).intValue()) { |
|
| 28 | v = v.xor(BigInteger.fromHex('79b76d99e2')); |
|
| 29 | } |
|
| 30 | if (b.and(new BigInteger('4')).intValue()) { |
|
| 31 | v = v.xor(BigInteger.fromHex('f33e5fb3c4')); |
|
| 32 | } |
|
| 33 | if (b.and(new BigInteger('8')).intValue()) { |
|
| 34 | v = v.xor(BigInteger.fromHex('ae2eabe2a8')); |
|
| 35 | } |
|
| 36 | if (b.and(new BigInteger('16')).intValue()) { |
|
| 37 | v = v.xor(BigInteger.fromHex('1e4f43e470')); |
|
| 38 | } |
|
| 39 | ||
| 40 | return v; |
|
| 41 | } |
|
| 42 | ||
| 43 | function prefixChk(prefix) { |
|
| 44 | var chk = new BigInteger('1'); |
|
| 45 | for (var i = 0; i < prefix.length; ++i) { |
|
| 46 | var c = prefix.charCodeAt(i); |
|
| 47 | ||
| 48 | var mixwith = new BigInteger('' + (c & 0x1f)); |
|
| 49 | chk = polymodStep(chk).xor(mixwith); |
|
| 50 | } |
|
| 51 | ||
| 52 | chk = polymodStep(chk); |
|
| 53 | return chk; |
|
| 54 | } |
|
| 55 | ||
| 56 | function encode(prefix, words) { |
|
| 57 | // too long? |
|
| 58 | if (prefix.length + CSLEN + 1 + words.length > 90) { |
|
| 59 | throw new TypeError('Exceeds Base32 maximum length'); |
|
| 60 | } |
|
| 61 | ||
| 62 | prefix = prefix.toLowerCase(); |
|
| 63 | ||
| 64 | // determine chk mod |
|
| 65 | var chk = prefixChk(prefix); |
|
| 66 | var result = prefix + SEPARATOR; |
|
| 67 | for (var i = 0; i < words.length; ++i) { |
|
| 68 | var _x = words[i]; |
|
| 69 | if (_x >>> 5 !== 0) { |
|
| 70 | throw new Error('Non 5-bit word'); |
|
| 71 | } |
|
| 72 | ||
| 73 | chk = polymodStep(chk).xor(new BigInteger('' + _x)); |
|
| 74 | result += ALPHABET.charAt(_x); |
|
| 75 | } |
|
| 76 | ||
| 77 | for (var _i = 0; _i < CSLEN; ++_i) { |
|
| 78 | chk = polymodStep(chk); |
|
| 79 | } |
|
| 80 | chk = chk.xor(new BigInteger('1')); |
|
| 81 | for (var _i2 = 0; _i2 < CSLEN; ++_i2) { |
|
| 82 | var pos = 5 * (CSLEN - 1 - _i2); |
|
| 83 | var v2 = chk.shiftRight(new BigInteger('' + pos)).and(BigInteger.fromHex('1f')); |
|
| 84 | result += ALPHABET.charAt(v2.toString(10)); |
|
| 85 | } |
|
| 86 | ||
| 87 | return result; |
|
| 88 | } |
|
| 89 | ||
| 90 | function decode(str) { |
|
| 91 | if (str.length < 8) { |
|
| 92 | throw new TypeError(str + ' too short'); |
|
| 93 | } |
|
| 94 | if (str.length > 90) { |
|
| 95 | throw new TypeError(str + ' too long'); |
|
| 96 | } |
|
| 97 | ||
| 98 | // don't allow mixed case |
|
| 99 | var lowered = str.toLowerCase(); |
|
| 100 | var uppered = str.toUpperCase(); |
|
| 101 | if (str !== lowered && str !== uppered) { |
|
| 102 | throw new Error('Mixed-case string ' + str); |
|
| 103 | } |
|
| 104 | ||
| 105 | str = lowered; |
|
| 106 | ||
| 107 | var split = str.lastIndexOf(SEPARATOR); |
|
| 108 | if (split === -1) { |
|
| 109 | throw new Error('No separator character for ' + str); |
|
| 110 | } |
|
| 111 | ||
| 112 | if (split === 0) { |
|
| 113 | throw new Error('Missing prefix for ' + str); |
|
| 114 | } |
|
| 115 | ||
| 116 | var prefix = str.slice(0, split); |
|
| 117 | var wordChars = str.slice(split + 1); |
|
| 118 | if (wordChars.length < 6) { |
|
| 119 | throw new Error('Data too short'); |
|
| 120 | } |
|
| 121 | ||
| 122 | var chk = prefixChk(prefix); |
|
| 123 | var words = []; |
|
| 124 | for (var i = 0; i < wordChars.length; ++i) { |
|
| 125 | var c = wordChars.charAt(i); |
|
| 126 | var v = ALPHABET_MAP[c]; |
|
| 127 | if (v === undefined) { |
|
| 128 | throw new Error('Unknown character ' + c); |
|
| 129 | } |
|
| 130 | ||
| 131 | chk = polymodStep(chk).xor(new BigInteger('' + v)); |
|
| 132 | // not in the checksum? |
|
| 133 | if (i + CSLEN >= wordChars.length) { |
|
| 134 | continue; |
|
| 135 | } |
|
| 136 | words.push(v); |
|
| 137 | } |
|
| 138 | ||
| 139 | if (chk.toString(10) !== '1') { |
|
| 140 | throw new Error('Invalid checksum for ' + str); |
|
| 141 | } |
|
| 142 | ||
| 143 | return { prefix: prefix, words: words }; |
|
| 144 | } |
|
| 145 | ||
| 146 | function convert(data, inBits, outBits, pad) { |
|
| 147 | var value = 0; |
|
| 148 | var bits = 0; |
|
| 149 | var maxV = (1 << outBits) - 1; |
|
| 150 | ||
| 151 | var result = []; |
|
| 152 | for (var i = 0; i < data.length; ++i) { |
|
| 153 | value = value << inBits | data[i]; |
|
| 154 | bits += inBits; |
|
| 155 | ||
| 156 | while (bits >= outBits) { |
|
| 157 | bits -= outBits; |
|
| 158 | result.push(value >>> bits & maxV); |
|
| 159 | } |
|
| 160 | } |
|
| 161 | ||
| 162 | if (pad) { |
|
| 163 | if (bits > 0) { |
|
| 164 | result.push(value << outBits - bits & maxV); |
|
| 165 | } |
|
| 166 | } else { |
|
| 167 | if (bits >= inBits) { |
|
| 168 | throw new Error('Excess padding'); |
|
| 169 | } |
|
| 170 | if (value << outBits - bits & maxV) { |
|
| 171 | throw new Error('Non-zero padding'); |
|
| 172 | } |
|
| 173 | } |
|
| 174 | ||
| 175 | return result; |
|
| 176 | } |
|
| 177 | ||
| 178 | function toWords(bytes) { |
|
| 179 | return convert(bytes, 8, 5, true); |
|
| 180 | } |
|
| 181 | ||
| 182 | function fromWords(words) { |
|
| 183 | return convert(words, 5, 8, false); |
|
| 184 | } |
|
| 185 | ||