Using my previous function as a base I've come up the following:
create function dbo.Totp (
@key varbinary(8000)
, @timeStep int = 90
)
returns table
with schemabinding as
return (
select [Pin].s as Pin
from dbo.GetUnixTime() UnixEpoch
cross apply dbo.Hmac(@key, Cast(Floor(1. * UnixEpoch.n / @timeStep) as bigint)) [Hash]
cross apply (values(Cast(Substring([Hash].Bytes, DataLength([Hash].Bytes), 1) as int) & 0xF)) Offset (n)
cross apply (values(
((Cast(Substring([Hash].Bytes, Offset.n + 1, 1) as int) & 0x7F) * Power(2, 24))
+ ((Cast(Substring([Hash].Bytes, Offset.n + 2, 1) as int) & 0xFF) * Power(2, 16))
+ ((Cast(Substring([Hash].Bytes, Offset.n + 3, 1) as int) & 0xFF) * Power(2, 8))
+ (Cast(Substring([Hash].Bytes, Offset.n + 4, 1) as int) & 0xFF)
)) [Binary] (n)
cross apply (values([Binary].n % 100000000)) [Password] (n)
cross apply dbo.PadLeft([Password].n, 0, 8) [Pin]
);