Skip to content

Commit 09f5f48

Browse files
authored
fix: Stay compatible with the authenticator apps (#22)
1 parent bab1ccc commit 09f5f48

File tree

1 file changed

+23
-10
lines changed

1 file changed

+23
-10
lines changed

index.js

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -55,17 +55,30 @@ async function generateHOTP(
5555
['sign']
5656
)
5757
const signature = await crypto.subtle.sign('HMAC', key, byteCounter)
58-
const hashBytes = new Uint8Array(signature)
58+
const hashBytes = new Uint8Array(signature)
59+
// offset is always the last 4 bits of the signature; 0-15
60+
const offset = hashBytes[hashBytes.length-1] & 0xf
5961

60-
// Use more bytes for longer OTPs
61-
const bytesNeeded = Math.ceil((digits * Math.log2(charSet.length)) / 8)
62-
const offset = hashBytes[hashBytes.length - 1] & 0xf
63-
64-
// Convert bytes to BigInt for larger numbers
65-
let hotpVal = 0n
66-
for (let i = 0; i < Math.min(bytesNeeded, hashBytes.length - offset); i++) {
67-
hotpVal = (hotpVal << 8n) | BigInt(hashBytes[offset + i])
68-
}
62+
let hotpVal = 0n
63+
if (digits === 6) {
64+
// stay compatible with the authenticator apps and only use the bottom 32 bits of BigInt
65+
hotpVal = 0n |
66+
BigInt((hashBytes[offset] & 0x7f)) << 24n |
67+
BigInt((hashBytes[offset + 1])) << 16n |
68+
BigInt((hashBytes[offset + 2])) << 8n |
69+
BigInt(hashBytes[offset + 3])
70+
} else {
71+
// otherwise create a 64bit value from the hashBytes
72+
hotpVal = 0n |
73+
BigInt((hashBytes[offset] & 0x7f)) << 56n |
74+
BigInt((hashBytes[offset + 1])) << 48n |
75+
BigInt((hashBytes[offset + 2])) << 40n |
76+
BigInt((hashBytes[offset + 3])) << 32n |
77+
BigInt((hashBytes[offset + 4])) << 24n |
78+
BigInt((hashBytes[offset + 5])) << 16n |
79+
BigInt((hashBytes[offset + 6])) << 8n |
80+
BigInt(hashBytes[offset + 7])
81+
}
6982

7083
let hotp = ''
7184
const charSetLength = BigInt(charSet.length)

0 commit comments

Comments
 (0)