HOTP i TOTP to kody wyświetlane przez aplikację uwierzytelniającą w telefonie. Obie funkcje znajdują się w jednostce sgcCrypto_OTP i są zbudowane na HMAC (patrz Haszowanie i uwierzytelnianie wiadomości).
HOTP (oparty na liczniku)
Kod zależy od sekretu współdzielonego oraz licznika, który zwiększa się o jeden przy każdym użyciu. Klient i serwer muszą pozostać zsynchronizowane: jeśli klient wygeneruje kody, których serwer nigdy nie zobaczy (na przykład przycisk naciśnięty bez połączenia sieciowego), liczniki się rozjeżdżają i każdy kod pokazywany później przez klienta zawodzi, dopóki serwer nie zresynchronizuje się. Ten dryf jest głównym powodem, dla którego TOTP zastąpił HOTP w większości konsumenckich zastosowań aplikacji uwierzytelniających.
function sgcHOTP(const aSecret: TBytes; aCounter: Int64; aDigits: Integer = 6): string;
TOTP (oparty na czasie)
Licznik z HOTP zostaje zastąpiony bieżącym czasem uniksowym podzielonym na stałe kroki, domyślnie 30 sekund, więc klient i serwer potrzebują jedynie w przybliżeniu zsynchronizowanych zegarów, a nie zsynchronizowanego licznika. Właśnie to implementuje każda aplikacja uwierzytelniająca.
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;
Weryfikacja kodu
aWindow akceptuje kody z tylu kroków przed i po bieżącym, ile podano, co pochłania niewielkie przesunięcie zegara albo użytkownika, który wolno wpisał kod. Porównanie jest wewnętrznie wykonywane w stałym czasie, więc nie ujawnia, ile cyfr zgadywanego kodu było poprawnych.
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;
Aprowizacja sekretu
Aplikacja uwierzytelniająca jest konfigurowana przez zeskanowanie kodu QR, który koduje URI otpauth://. Sekret zawarty w nim jest zakodowany w base32 (patrz sgcBase32Encode na stronie przeglądowej Kryptografia):
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;