@@ -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