HOTP et TOTP sont les codes qu'affiche une application d'authentification sur téléphone. Les deux fonctions résident dans sgcCrypto_OTP et reposent sur HMAC (voir Hachage & authentification des messages).
HOTP (basé sur un compteur)
Le code dépend d'un secret partagé et d'un compteur qui avance de un à chaque utilisation. Le client et le serveur doivent rester synchronisés : si le client génère des codes que le serveur ne voit jamais (un bouton pressé sans connexion réseau, par exemple), les compteurs divergent et chaque code que le client affiche ensuite échoue jusqu'à ce que le serveur se resynchronise. Cette dérive est la principale raison pour laquelle TOTP a remplacé HOTP dans la plupart des usages d'authentificateur grand public.
function sgcHOTP(const aSecret: TBytes; aCounter: Int64; aDigits: Integer = 6): string;
TOTP (basé sur le temps)
Le compteur de HOTP est remplacé par l'heure Unix actuelle divisée en intervalles fixes, 30 secondes par défaut, de sorte que le client et le serveur n'ont besoin que d'horloges à peu près synchronisées plutôt que d'un compteur synchronisé. C'est ce qu'implémente chaque application d'authentification.
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;
Vérifier un code
aWindow accepte les codes provenant d'autant d'intervalles avant et après l'intervalle actuel, ce qui absorbe un léger décalage d'horloge ou un utilisateur lent à saisir le code. La comparaison est en temps constant en interne, elle ne divulgue donc pas combien de chiffres d'une tentative étaient corrects.
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;
Provisionner un secret
Une application d'authentification est configurée en scannant un code QR qui encode une URI otpauth://. Le secret qu'elle contient est encodé en base32 (voir sgcBase32Encode sur la page de présentation Cryptographie) :
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;