bump package version to 1.1.5
[squeep-totp] / test / hotp.js
1 /* eslint-env mocha */
2 'use strict';
3
4 const assert = require('assert');
5 const HOTP = require('../lib/hotp');
6
7 describe('HOTP', function () {
8 let options, hotp;
9
10 const tests = [
11 { count: 0n, hmac: Buffer.from('cc93cf18508d94934c64b65d8ba7667fb7cde4b0', 'hex'), hex: '4c93cf18', dec: 1284755224, hotp: '755224' },
12 { count: 1n, hmac: Buffer.from('75a48a19d4cbe100644e8ac1397eea747a2d33ab', 'hex'), hex: '41397eea', dec: 1094287082, hotp: '287082' },
13 { count: 2n, hmac: Buffer.from('0bacb7fa082fef30782211938bc1c5e70416ff44', 'hex'), hex: '82fef30', dec: 137359152, hotp: '359152' },
14 { count: 3n, hmac: Buffer.from('66c28227d03a2d5529262ff016a1e6ef76557ece', 'hex'), hex: '66ef7655', dec: 1726969429, hotp: '969429' },
15 { count: 4n, hmac: Buffer.from('a904c900a64b35909874b33e61c5938a8e15ed1c', 'hex'), hex: '61c5938a', dec: 1640338314, hotp: '338314' },
16 { count: 5n, hmac: Buffer.from('a37e783d7b7233c083d4f62926c7a25f238d0316', 'hex'), hex: '33c083d4', dec: 868254676, hotp: '254676' },
17 { count: 6n, hmac: Buffer.from('bc9cd28561042c83f219324d3c607256c03272ae', 'hex'), hex: '7256c032', dec: 1918287922, hotp: '287922' },
18 { count: 7n, hmac: Buffer.from('a4fb960c0bc06e1eabb804e5b397cdc4b45596fa', 'hex'), hex: '4e5b397', dec: 82162583, hotp: '162583' },
19 { count: 8n, hmac: Buffer.from('1b3c89f65e6c9e883012052823443f048b4332db', 'hex'), hex: '2823443f', dec: 673399871, hotp: '399871' },
20 { count: 9n, hmac: Buffer.from('1637409809a679dc698207310c8c7fc07290d9e5', 'hex'), hex: '2679dc69', dec: 645520489, hotp: '520489' },
21 ];
22
23 beforeEach(function () {
24 options = {
25 key: Buffer.from('12345678901234567890'),
26 };
27 hotp = new HOTP(options);
28 });
29
30 describe('constructor', function () {
31 it('covers invalid key', function () {
32 options.key = Buffer.from('blah');
33 assert.throws(() => new HOTP(options), RangeError);
34 });
35 it('covers invalid algorithm', function () {
36 options.algorithm = 'md5';
37 assert.throws(() => new HOTP(options), RangeError);
38 });
39 it('covers missing options', function () {
40 assert.throws(() => new HOTP());
41 });
42 it('covers counter type conversion', function () {
43 options.counter = 4;
44 hotp = new HOTP(options);
45 assert.strictEqual(hotp.counter, 4n);
46 });
47 it('covers key specified as buffer', function () {
48 options.keyEncoding = 'buffer';
49 hotp = new HOTP(options);
50 assert(hotp);
51 });
52 it('covers key specified as base32', function () {
53 options.key = 'GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ';
54 options.keyEncoding = 'base32';
55 hotp = new HOTP(options);
56 assert(hotp);
57 });
58 it('covers key specified as ascii', function () {
59 options.key = '12345678901234567890';
60 options.keyEncoding = 'ascii';
61 hotp = new HOTP(options);
62 assert(hotp);
63 });
64 }); // constructor
65
66 it('_hmac sanity', function () {
67 for (const test of tests) {
68 const result = hotp._hmac(test.count);
69 assert.strictEqual(Buffer.compare(result, test.hmac), 0, `count: ${test.count} result: ${result.toString('hex')} expected: ${test.hmac.toString('hex')}`);
70 }
71 }); // _hmac sanity
72
73 it('_truncate sanity', function () {
74 for (const test of tests) {
75 const result = hotp._truncate(test.count);
76 assert.strictEqual(result, test.dec, `count: ${test.count} result: ${result} expected: ${test.dec} (hex: ${result.toString(16)} hexpected: ${test.hex})`);
77 }
78 }); // _truncate sanity
79
80 it('generate', function () {
81 for (const test of tests) {
82 const result = hotp.generate();
83 assert.strictEqual(result, test.hotp, `count: ${test.count} htop.counter: ${hotp.counter} result: ${result} expected: ${test.hotp}`);
84 assert.strictEqual(hotp.counter, test.count + 1n);
85 }
86 }); // generate
87
88 it('covers generate with count', function () {
89 const result = hotp.generate(3n);
90 assert.strictEqual(result, tests[3].hotp);
91 }); // generate coverage
92
93 describe('validate', function () {
94 it('accepts correct hotp', function () {
95 const result = hotp.validate(tests[2].hotp, 2n);
96 assert.strictEqual(result, true);
97 });
98 }); // validate
99
100 describe('createKey', function () {
101 const algorithm = 'sha1';
102 it('creates a sha1 key by default', async function () {
103 const key = await HOTP.createKey();
104 assert.strictEqual(key.length, 40);
105 });
106 it('creates a sha1 key', async function () {
107 const key = await HOTP.createKey(algorithm);
108 assert.strictEqual(key.length, 40);
109 });
110 it('creates a sha1 key in base32 encoding', async function () {
111 const key = await HOTP.createKey(algorithm, 'base32');
112 assert.strictEqual(key.length, 32);
113 });
114 it('creates a sha1 key in alternate encoding', async function () {
115 const key = await HOTP.createKey(algorithm, 'base64');
116 assert.strictEqual(key.length, 28);
117 });
118 it('creates a sha1 key as a buffer', async function () {
119 const key = await HOTP.createKey(algorithm, 'buffer');
120 assert(Buffer.isBuffer(key));
121 });
122 it('rejects unknown algorithm', async function () {
123 await assert.rejects(() => HOTP.createKey('md5'), RangeError);
124 });
125 }); // createKey
126
127 describe('createKeySVG', function () {
128 let options, key, keyEncoding;
129 beforeEach(function () {
130 options = {
131 issuer: 'Squeep',
132 accountname: 'test@example.com',
133 counter: 0n,
134 };
135 key = undefined;
136 keyEncoding = undefined;
137 });
138 it('creates an svg from base32 key', function () {
139 key = 'GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ';
140 keyEncoding = 'base32';
141 const { secret, svg, uri } = HOTP.createKeySVG(options, key, keyEncoding);
142 assert(svg);
143 assert(uri.includes('hotp'));
144 assert.strictEqual(secret, 'GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ');
145 });
146 it('creates an svg from buffer key', function () {
147 key = Buffer.from('12345678901234567890');
148 keyEncoding = 'buffer';
149 const { secret, svg, uri } = HOTP.createKeySVG(options, key, keyEncoding);
150 assert(svg);
151 assert(uri.includes('hotp'));
152 assert.strictEqual(secret, 'GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ');
153 });
154 it('creates an svg from buffer key', function () {
155 key = Buffer.from('12345678901234567890');
156 const { secret, svg, uri } = HOTP.createKeySVG(options, key, keyEncoding);
157 assert(svg);
158 assert(uri.includes('hotp'));
159 assert.strictEqual(secret, 'GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ');
160 });
161 it('creates an svg from ascii key', function () {
162 key = '12345678901234567890';
163 keyEncoding = 'ascii';
164 const { secret, svg, uri } = HOTP.createKeySVG(options, key, keyEncoding);
165 assert(svg);
166 assert(uri.includes('hotp'));
167 assert.strictEqual(secret, 'GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ');
168 });
169 }); // createKeySVG
170
171 describe('_qrURI', function () {
172 let options;
173 beforeEach(function () {
174 options = {
175 secret: 'GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ',
176 accountname: 'test@example.com',
177 issuer: 'Squeep',
178 counter: 0n,
179 };
180 });
181 it('requires accountname', function () {
182 delete options.accountname;
183 assert.throws(() => HOTP._qrURI(options), RangeError);
184 });
185 it('requires secret', function () {
186 delete options.secret;
187 assert.throws(() => HOTP._qrURI(options), RangeError);
188 });
189 it('requires counter', function () {
190 delete options.counter;
191 assert.throws(() => HOTP._qrURI(options), RangeError);
192 });
193 it('requires secret', function () {
194 delete options.secret;
195 assert.throws(() => HOTP._qrURI(options), RangeError);
196 });
197 it('requires known digits', function () {
198 options.digits = 10;
199 assert.throws(() => HOTP._qrURI(options), RangeError);
200 });
201 it('requires known algorithm', function () {
202 options.algorithm = 'md5';
203 assert.throws(() => HOTP._qrURI(options), RangeError);
204 });
205 it('covers no issuer', function () {
206 delete options.issuer;
207 const uri = HOTP._qrURI(options);
208 assert(!uri.includes('issuer'));
209 });
210 it('covers params', function () {
211 options.digits = 6;
212 options.algorithm = 'sha1';
213 const uri = HOTP._qrURI(options);
214 assert(uri.includes('digits'));
215 assert(uri.includes('SHA1'));
216 });
217 }); // _qrURI
218
219 }); // HOTP