edge-core-js
Version:
Edge account & wallet management library
74 lines (65 loc) • 1.61 kB
JavaScript
import { hmacSha1 } from './hashes'
export function numberToBe64(number) {
const high = Math.floor(number / 0x100000000)
return new Uint8Array([
(high >> 24) & 0xff,
(high >> 16) & 0xff,
(high >> 8) & 0xff,
high & 0xff,
(number >> 24) & 0xff,
(number >> 16) & 0xff,
(number >> 8) & 0xff,
number & 0xff
])
}
/**
* Implements the rfc4226 HOTP specification.
* @param {*} secret The secret value, K, from rfc4226
* @param {*} counter The counter, C, from rfc4226
* @param {*} digits The number of digits to generate
*/
export function hotp(
secret,
counter,
digits
) {
const hmac = hmacSha1(numberToBe64(counter), secret)
const offset = hmac[19] & 0xf
const p =
((hmac[offset] & 0x7f) << 24) |
(hmac[offset + 1] << 16) |
(hmac[offset + 2] << 8) |
hmac[offset + 3]
const text = p.toString()
const padding = Array(digits).join('0')
return (padding + text).slice(-digits)
}
/**
* Generates an HOTP code based on the current time.
*/
export function totp(
secret,
now = Date.now() / 1000
) {
return hotp(secret, now / 30, 6)
}
/**
* Validates a TOTP code based on the current time,
* within an adjustable range.
*/
export function checkTotp(
secret,
otp,
opts = {}
) {
const { now = Date.now() / 1000, spread = 1 } = opts
const index = now / 30
// Try the middle:
if (otp === hotp(secret, index, 6)) return true
// Spiral outwards:
for (let i = 1; i <= spread; ++i) {
if (otp === hotp(secret, index - i, 6)) return true
if (otp === hotp(secret, index + i, 6)) return true
}
return false
}