Hasła jednorazowe

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;