mirror of
https://github.com/CorentinTh/it-tools.git
synced 2025-04-25 01:06:15 -04:00
128 lines
3.9 KiB
TypeScript
128 lines
3.9 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import {
|
|
base32toHex,
|
|
buildKeyUri,
|
|
generateHOTP,
|
|
generateTOTP,
|
|
hexToBytes,
|
|
verifyHOTP,
|
|
verifyTOTP,
|
|
} from './otp.service';
|
|
|
|
describe('otp functions', () => {
|
|
describe('hexToBytes', () => {
|
|
it('convert an hexstring to a byte array', () => {
|
|
expect(hexToBytes('1')).to.eql([1]);
|
|
expect(hexToBytes('ffffff')).to.eql([255, 255, 255]);
|
|
expect(hexToBytes('000000000')).to.eql([0, 0, 0, 0, 0]);
|
|
expect(hexToBytes('a3218bcef89')).to.eql([163, 33, 139, 206, 248, 9]);
|
|
expect(hexToBytes('063679ca')).toEqual([6, 54, 121, 202]);
|
|
expect(hexToBytes('0102030405060708090a0b0c0d0e0f')).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]);
|
|
});
|
|
});
|
|
describe('base32toHex', () => {
|
|
it('convert a base32 to hex string', () => {
|
|
expect(base32toHex('ABCDEF')).to.eql('00443205');
|
|
expect(base32toHex('7777')).to.eql('ffff0f');
|
|
expect(base32toHex('JBSWY3DPEHPK3PXP')).to.eql('48656c6c6f21deadbeef');
|
|
});
|
|
|
|
it('case does not matter', () => {
|
|
expect(base32toHex('ABC')).to.eql(base32toHex('abc'));
|
|
});
|
|
});
|
|
|
|
describe('generateHOTP', () => {
|
|
it('generates HOTP codes for a given counter', () => {
|
|
const key = 'JBSWY3DPEHPK3PXP';
|
|
const hotpCodes = ['282760', '996554', '602287', '143627', '960129'];
|
|
|
|
for (const [counter, code] of hotpCodes.entries()) {
|
|
expect(generateHOTP({ key, counter })).to.eql(code);
|
|
}
|
|
});
|
|
});
|
|
|
|
describe('verifyHOTP', () => {
|
|
it('validate hotp for a given secret', () => {
|
|
const key = 'JBSWY3DPEHPK3PXP';
|
|
const hotpCodes = ['282760', '996554', '602287', '143627', '960129'];
|
|
|
|
for (const [counter, token] of hotpCodes.entries()) {
|
|
expect(verifyHOTP({ token, key, counter, window: 0 })).to.eql(true);
|
|
}
|
|
|
|
expect(verifyHOTP({ token: 'INVALID', key })).to.eql(false);
|
|
});
|
|
|
|
it('does not validate hotp out of sync', () => {
|
|
const key = 'JBSWY3DPEHPK3PXP';
|
|
const token = '282760';
|
|
|
|
expect(verifyHOTP({ token, key, counter: 5, window: 2 })).to.eql(false);
|
|
expect(verifyHOTP({ token, key, counter: 5, window: 5 })).to.eql(true);
|
|
});
|
|
});
|
|
|
|
describe('generateTOTP', () => {
|
|
it('generates TOTP codes', () => {
|
|
const key = 'JBSWY3DPEHPK3PXP';
|
|
|
|
const codes = [
|
|
{ token: '282760', now: 0 },
|
|
{ token: '341128', now: 1465324707000 },
|
|
{ token: '089029', now: 1365324707000 },
|
|
];
|
|
|
|
for (const { token, now } of codes) {
|
|
expect(generateTOTP({ key, now })).to.eql(token);
|
|
}
|
|
});
|
|
});
|
|
|
|
describe('verifyTOTP', () => {
|
|
it('verify TOTP in sync codes against a key', () => {
|
|
const key = 'JBSWY3DPEHPK3PXP';
|
|
|
|
const codes = [
|
|
{ token: '282760', now: 0 },
|
|
{ token: '341128', now: 1465324707000 },
|
|
{ token: '089029', now: 1365324707000 },
|
|
];
|
|
|
|
for (const { token, now } of codes) {
|
|
expect(verifyTOTP({ key, token, now })).to.eql(true);
|
|
}
|
|
});
|
|
|
|
it('does not validate totp out of sync', () => {
|
|
const key = 'JBSWY3DPEHPK3PXP';
|
|
const token = '635183';
|
|
const now = 1661266455000;
|
|
|
|
expect(verifyTOTP({ key, token, now, window: 2 })).to.eql(true);
|
|
expect(verifyTOTP({ key, token, now, window: 1 })).to.eql(false);
|
|
});
|
|
});
|
|
|
|
describe('buildKeyUri', () => {
|
|
it('build a key uri string', () => {
|
|
expect(buildKeyUri({ secret: 'JBSWY3DPEHPK3PXP' })).to.eql(
|
|
'otpauth://totp/IT-Tools:demo-user?issuer=IT-Tools&secret=JBSWY3DPEHPK3PXP&algorithm=SHA1&digits=6&period=30',
|
|
);
|
|
|
|
expect(
|
|
buildKeyUri({
|
|
secret: 'JBSWY3DPEHPK3PXP',
|
|
app: 'app-name',
|
|
account: 'account',
|
|
algorithm: 'algo',
|
|
digits: 7,
|
|
period: 10,
|
|
}),
|
|
).to.eql(
|
|
'otpauth://totp/app-name:account?issuer=app-name&secret=JBSWY3DPEHPK3PXP&algorithm=algo&digits=7&period=10',
|
|
);
|
|
});
|
|
});
|
|
});
|