initial commit
[squeep-totp] / test / hotp.js
diff --git a/test/hotp.js b/test/hotp.js
new file mode 100644 (file)
index 0000000..f79f816
--- /dev/null
@@ -0,0 +1,83 @@
+/* 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