HOTP en TOTP zijn de codes die een authenticator-app op de telefoon toont. Beide functies bevinden zich in sgcCrypto_OTP en zijn gebaseerd op HMAC (zie Hashing & berichtauthenticatie).
HOTP (op basis van een teller)
De code hangt af van een gedeeld geheim en een teller die bij elk gebruik met één ophoogt. De client en de server moeten synchroon blijven: als de client codes genereert die de server nooit ziet (bijvoorbeeld een knop die wordt ingedrukt zonder netwerkverbinding), lopen de tellers uit elkaar en mislukt elke code die de client daarna toont, totdat de server opnieuw synchroniseert. Die drift is de belangrijkste reden waarom TOTP HOTP heeft vervangen voor de meeste consumenten-authenticatortoepassingen.
function sgcHOTP(const aSecret: TBytes; aCounter: Int64; aDigits: Integer = 6): string;
TOTP (op basis van tijd)
De teller in HOTP wordt vervangen door de huidige Unix-tijd, opgedeeld in vaste stappen, standaard 30 seconden, zodat de client en de server alleen ruwweg gesynchroniseerde klokken nodig hebben in plaats van een gesynchroniseerde teller. Dit is wat elke authenticator-app implementeert.
function sgcTOTP(const aSecret: TBytes; aUnixTime: Int64; aDigits: Integer = 6;
aStep: Integer = 30; aHash: TsgcKDFHash = khSHA1): string;
{ Takes the base32 secret exactly as an authenticator app displays or scans it. }
function sgcTOTP_FromBase32(const aSecretBase32: string; aUnixTime: Int64;
aDigits: Integer = 6; aStep: Integer = 30; aHash: TsgcKDFHash = khSHA1): string;
Een code verifiëren
aWindow accepteert codes van dat aantal stappen vóór en na de huidige, wat een kleine klokafwijking opvangt, of een gebruiker die traag was met het intypen van de code. De vergelijking is intern constant time, dus ze lekt niet hoeveel cijfers van een gok correct waren.
function sgcTOTP_Verify(const aSecret: TBytes; const aCode: string; aUnixTime: Int64;
aWindow: Integer = 1; aDigits: Integer = 6; aStep: Integer = 30; aHash: TsgcKDFHash = khSHA1): Boolean;
var
oSecret: TBytes;
begin
oSecret := sgcRandomBytes(20);
if sgcTOTP_Verify(oSecret, edtCode.Text, DateTimeToUnix(Now, False)) then
{ accepted };
end;
Een geheim provisionen
Een authenticator-app wordt ingesteld door een QR-code te scannen die een otpauth://-URI codeert. Het geheim daarin is base32-gecodeerd (zie sgcBase32Encode op de overzichtspagina Cryptografie):
var
oSecret: TBytes;
vURI: string;
begin
oSecret := sgcRandomBytes(20);
vURI := 'otpauth://totp/MyApp:user@example.com?secret=' + sgcBase32Encode(oSecret) +
'&issuer=MyApp&digits=6&period=30';
{ render vURI as a QR code for the user to scan }
end;