Password monouso

HOTP e TOTP sono i codici mostrati da un'app di autenticazione sul telefono. Entrambe le funzioni risiedono in sgcCrypto_OTP e sono costruite su HMAC (vedere Hashing e autenticazione dei messaggi).

HOTP (basato su contatore)

Il codice dipende da un segreto condiviso e da un contatore che avanza di uno a ogni utilizzo. Il client e il server devono restare sincronizzati: se il client genera codici che il server non vede mai (ad esempio un pulsante premuto senza connessione di rete), i contatori si disallineano e ogni codice mostrato successivamente dal client fallisce finché il server non si risincronizza. Questo disallineamento è il motivo principale per cui TOTP ha sostituito HOTP nella maggior parte degli usi consumer di autenticazione.


function sgcHOTP(const aSecret: TBytes; aCounter: Int64; aDigits: Integer = 6): string;

TOTP (basato sul tempo)

Il contatore di HOTP viene sostituito dall'ora Unix corrente suddivisa in step fissi, 30 secondi per default, in modo che il client e il server abbiano bisogno solo di orologi approssimativamente sincronizzati anziché di un contatore sincronizzato. Questo è ciò che implementa ogni app di autenticazione.


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;

Verifica di un codice

aWindow accetta codici da quel numero di step prima e dopo quello corrente, il che assorbe un piccolo scarto dell'orologio o un utente che ha impiegato tempo a digitare il codice. Il confronto è internamente a tempo costante, quindi non rivela quante cifre di un tentativo fossero corrette.


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;

Provisioning di un segreto

Un'app di autenticazione viene configurata scansionando un codice QR che codifica una URI otpauth://. Il segreto al suo interno è codificato in base32 (vedere sgcBase32Encode nella pagina panoramica Crittografia):


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;