Passed
Pull Request — master (#1)
by thomas
01:46
created

index.js ➔ polymodStep   B

Complexity

Conditions 6
Paths 32

Size

Total Lines 24

Duplication

Lines 24
Ratio 100 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
c 1
b 0
f 0
nc 32
nop 1
dl 24
loc 24
rs 8.5125
1 View Code Duplication
'use strict';
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
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) {
0 ignored issues
show
introduced by
You have used a bitwise operator & in a condition. Did you maybe want to use the logical operator &&
Loading history...
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
186
module.exports = { decode: decode, encode: encode, toWords: toWords, fromWords: fromWords };