HOTP y TOTP son los códigos que muestra una aplicación autenticadora de teléfono. Ambas funciones residen en sgcCrypto_OTP y se construyen sobre HMAC (vea Hash y Autenticación de Mensajes).
HOTP (basado en contador)
El código depende de un secreto compartido y de un contador que avanza en uno con cada uso. El cliente y el servidor deben mantenerse sincronizados: si el cliente genera códigos que el servidor nunca ve (por ejemplo, un botón pulsado sin conexión de red), los contadores se desalinean y todos los códigos que el cliente muestre después fallan hasta que el servidor se resincronice. Esa desalineación es el motivo principal por el que TOTP sustituyó a HOTP en la mayoría de los usos de autenticadores de consumo.
function sgcHOTP(const aSecret: TBytes; aCounter: Int64; aDigits: Integer = 6): string;
TOTP (basado en tiempo)
El contador de HOTP se sustituye por el tiempo Unix actual dividido en pasos fijos, 30 segundos por defecto, de modo que el cliente y el servidor solo necesitan relojes aproximadamente sincronizados en lugar de un contador sincronizado. Esto es lo que implementa cualquier aplicación autenticadora.
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;
Verificar un código
aWindow acepta códigos de ese número de pasos antes y después del actual, lo que absorbe un pequeño desfase de reloj o un usuario que tardó en escribir el código. La comparación es internamente en tiempo constante, de modo que no filtra cuántos dígitos de una suposición eran correctos.
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;
Aprovisionar un secreto
Una aplicación autenticadora se configura escaneando un código QR que codifica una URI otpauth://. El secreto dentro de ella está codificado en base32 (vea sgcBase32Encode en la página general de Criptografía):
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;