HOTP e TOTP são os códigos que um aplicativo autenticador de celular mostra. As duas funções vivem em sgcCrypto_OTP e são construídas sobre HMAC (veja Hashing & Autenticação de Mensagens).
HOTP (baseado em contador)
O código depende de um segredo compartilhado e de um contador que avança de um em um a cada uso. O cliente e o servidor precisam permanecer sincronizados: se o cliente gerar códigos que o servidor nunca vê (um botão pressionado sem conexão de rede, por exemplo), os contadores se desalinham e todo código que o cliente mostrar depois falha até o servidor ressincronizar. Essa deriva é o principal motivo pelo qual o TOTP substituiu o HOTP na maioria dos usos de autenticador para consumidores.
function sgcHOTP(const aSecret: TBytes; aCounter: Int64; aDigits: Integer = 6): string;
TOTP (baseado em tempo)
O contador do HOTP é substituído pelo tempo Unix atual dividido em passos fixos, 30 segundos por padrão, de modo que o cliente e o servidor só precisam de relógios aproximadamente sincronizados em vez de um contador sincronizado. É isso que todo aplicativo autenticador implementa.
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;
Verificando um código
aWindow aceita códigos de tantos passos antes e depois do atual, o que absorve uma pequena diferença de relógio ou um usuário que demorou para digitar o código. A comparação é internamente em tempo constante, portanto não vaza quantos dígitos de um palpite estavam corretos.
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;
Provisionando um segredo
Um aplicativo autenticador é configurado escaneando um código QR que codifica uma URI otpauth://. O segredo dentro dela é codificado em base32 (veja sgcBase32Encode na página de visão geral Criptografia):
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;