--- /dev/null
+/* eslint-env: mocha */
+'use strict';
+
+const assert = require('assert');
+const HOTP = require('../lib/hotp');
+
+describe('HOTP', function () {
+ let options, hotp;
+
+ const tests = [
+ { count: 0n, hmac: Buffer.from('cc93cf18508d94934c64b65d8ba7667fb7cde4b0', 'hex'), hex: '4c93cf18', dec: 1284755224, hotp: '755224' },
+ { count: 1n, hmac: Buffer.from('75a48a19d4cbe100644e8ac1397eea747a2d33ab', 'hex'), hex: '41397eea', dec: 1094287082, hotp: '287082' },
+ { count: 2n, hmac: Buffer.from('0bacb7fa082fef30782211938bc1c5e70416ff44', 'hex'), hex: '82fef30', dec: 137359152, hotp: '359152' },
+ { count: 3n, hmac: Buffer.from('66c28227d03a2d5529262ff016a1e6ef76557ece', 'hex'), hex: '66ef7655', dec: 1726969429, hotp: '969429' },
+ { count: 4n, hmac: Buffer.from('a904c900a64b35909874b33e61c5938a8e15ed1c', 'hex'), hex: '61c5938a', dec: 1640338314, hotp: '338314' },
+ { count: 5n, hmac: Buffer.from('a37e783d7b7233c083d4f62926c7a25f238d0316', 'hex'), hex: '33c083d4', dec: 868254676, hotp: '254676' },
+ { count: 6n, hmac: Buffer.from('bc9cd28561042c83f219324d3c607256c03272ae', 'hex'), hex: '7256c032', dec: 1918287922, hotp: '287922' },
+ { count: 7n, hmac: Buffer.from('a4fb960c0bc06e1eabb804e5b397cdc4b45596fa', 'hex'), hex: '4e5b397', dec: 82162583, hotp: '162583' },
+ { count: 8n, hmac: Buffer.from('1b3c89f65e6c9e883012052823443f048b4332db', 'hex'), hex: '2823443f', dec: 673399871, hotp: '399871' },
+ { count: 9n, hmac: Buffer.from('1637409809a679dc698207310c8c7fc07290d9e5', 'hex'), hex: '2679dc69', dec: 645520489, hotp: '520489' },
+ ];
+
+ beforeEach(function () {
+ options = {
+ key: Buffer.from('12345678901234567890'),
+ };
+ hotp = new HOTP(options);
+ });
+
+ describe('constructor', function () {
+ it('covers invalid key', function () {
+ options.key = Buffer.from('blah');
+ assert.throws(() => new HOTP(options), RangeError);
+ });
+ it('covers invalid algorithm', function () {
+ options.algorithm = 'md5';
+ assert.throws(() => new HOTP(options), RangeError);
+ });
+ it('covers missing options', function () {
+ assert.throws(() => new HOTP());
+ });
+ it('covers counter', function () {
+ options.counter = 4;
+ hotp = new HOTP(options);
+ assert.strictEqual(hotp.counter, 4n);
+ });
+ }); // constructor
+
+ it('_hmac sanity', function () {
+ for (const test of tests) {
+ const result = hotp._hmac(test.count);
+ assert.strictEqual(Buffer.compare(result, test.hmac), 0, `count: ${test.count} result: ${result.toString('hex')} expected: ${test.hmac.toString('hex')}`);
+ }
+ }); // _hmac sanity
+
+ it('_truncate sanity', function () {
+ for (const test of tests) {
+ const result = hotp._truncate(test.count);
+ assert.strictEqual(result, test.dec, `count: ${test.count} result: ${result} expected: ${test.dec} (hex: ${result.toString(16)} hexpected: ${test.hex})`);
+ }
+ }); // _truncate sanity
+
+ it('generate', function () {
+ for (const test of tests) {
+ const result = hotp.generate();
+ assert.strictEqual(result, test.hotp, `count: ${test.count} htop.counter: ${hotp.counter} result: ${result} expected: ${test.hotp}`);
+ assert.strictEqual(hotp.counter, test.count + 1n);
+ }
+ }); // generate
+
+ it('covers generate with count', function () {
+ const result = hotp.generate(3n);
+ assert.strictEqual(result, tests[3].hotp);
+ }); // generate coverage
+
+ describe('validate', function () {
+ it('accepts correct hotp', function () {
+ const result = hotp.validate(tests[2].hotp, 2n);
+ assert.strictEqual(result, true);
+ });
+ }); // validate
+
+}); // HOTP
\ No newline at end of file