
Ferramenta automatizada de fuzzing diferencial para software criptográfico que detecta erros de implementação, falhas de conformidade e vazamentos de canais laterais por meio de testes inteligentes e paralelizados em vários idiomas e plataformas.
CDF é uma ferramenta para testar automaticamente a correção e segurança de software criptográfico. O CDF pode detectar erros de implementação, falhas de conformidade, vazamentos de canais laterais, etc.
O CDF implementa uma combinação de testes unitários com "fuzzing diferencial", uma abordagem que compara o comportamento de diferentes implementações das mesmas primitivas quando alimentadas com casos extremos e valores que maximizam a cobertura de código.
Ao contrário de fuzzers e softwares de teste de propósito geral, o CDF é:
Inteligente: O CDF sabe que tipo de algoritmo está testando e se adapta às funções testadas
Rápido: O CDF testa apenas o que precisa ser testado e paraleliza seus testes ao máximo
Polivalente: O CDF não é específico para uma linguagem ou API, mas suporta programas ou scripts executáveis arbitrários
Portátil: O CDF funcionará em qualquer plataforma Unix ou Windows, pois é escrito em Go sem qualquer dependência específica de plataforma
O propósito do CDF é fornecer uma ferramenta de teste mais eficiente para desenvolvedores e pesquisadores de segurança, sendo mais eficaz do que vetores de teste e mais barato do que auditoria manual de verificação formal.
O CDF foi apresentado pela primeira vez no Black Hat USA 2017. Você pode ver os slides da nossa apresentação, que contêm informações gerais sobre a lógica por trás e o design do CDF.
O CDF é codificado em Go, a versão atual foi desenvolvida usando Go 1.8. Não tem dependências fora da biblioteca padrão do Go.
No entanto, fornecemos programas de exemplo para serem testados usando o CDF, que estão em C, Python, C++, Java e Go e requerem bibliotecas criptográficas específicas para serem executados. Atualmente, as bibliotecas necessárias são:
make irá construir o binário cdf.
Vários programas de exemplo estão disponíveis em example: make examples-all irá construir todos os exemplos, enquanto make examples-go irá construir apenas os exemplos Go.
make test irá executar testes unitários (do CDF).
Para começar, você pode querer visualizar as informações de uso executando cdf -h.
Você pode então tentar um exemplo como a interface rsaenc contra os exemplos RSA OAEP Go e CryptoPP. Considerando o CryptoPP como nossa referência, você pode testar a implementação Go fazendo:
cdf rsaenc /examples/oaep_rsa2048_go /examples/oaep_rsa2048_cryptopp
Este comando realizará vários testes específicos da interface rsaenc.
Neste exemplo, o CDF deve reclamar sobre o tamanho máximo do expoente público que a implementação Go suporta: se verificarmos seu código podemos ver que o expoente público está sendo armazenado como um inteiro normal, enquanto no CryptoPP (e na maioria das outras implementações), ele é armazenado como um inteiro grande. Isso é, no entanto, por design e provavelmente não será alterado.
Os parâmetros são definidos em config.json. A maioria dos parâmetros é autoexplicativa. Você pode querer definir outras chaves privadas para rsaenc e ecdsa (essas interfaces são testadas com chaves fixas, embora alguns parâmetros de chave, como os expoentes, sejam alterados em alguns dos testes).
O parâmetro seed permite alterar a semente usada nos geradores pseudoaleatórios do CDF. (No entanto, o programa testado pode estar usando algum PRNG semeado de outra forma, como os exemplos OAEP.) O parâmetro concurrency permite definir o número de goroutines concorrentes que o CDF deve gerar ao bifurcar os programas. Note que é melhor manter esse número abaixo do número real de núcleos. O parâmetro verboseLog, se definido como true, escreverá todas as entradas e saídas dos programas, mesmo para os testes bem-sucedidos, em um arquivo log.txt.
Para testar seu software usando o CDF, você precisa criar um programa que leia a entrada e escreva a saída em conformidade com as interfaces do CDF, e que internamente chame o programa testado. As interfaces do CDF são abstrações de funcionalidades criptográficas, para permitir testes de caixa preta de implementações arbitrárias.
Por exemplo, se você implementou o esquema de assinatura ECDSA, seu programa deve satisfazer a interface ecdsa e, como tal, receber como entrada 4 ou 5 argumentos, respectivamente para assinar uma mensagem ou verificar uma assinatura. Esses argumentos são a coordenada X pública, a coordenada Y pública, o inteiro grande D privado e a mensagem que você deseja assinar, e então deve gerar apenas os inteiros grandes R e S, cada um em uma nova linha. Ou, para verificar uma mensagem, deve aceitar X, Y, R, S e a mensagem e então deve gerar apenas True ou False. As especificações das interfaces são detalhadas abaixo.
Nossos exemplos de implementações de interface ajudarão você a criar as suas.
O tratamento de erros é deixado para o programa testado, no entanto, para ter erros significativos no CDF, é melhor sair em caso de falha, retornar um código de erro e imprimir uma mensagem de erro.
O programa de interface pode ser escrito em qualquer linguagem, apenas precisa ser um arquivo executável em conformidade com uma interface do CDF. Um programa de interface é tipicamente escrito na mesma linguagem que o programa testado, mas isso não é obrigatório (pode ser um wrapper em outra linguagem, por exemplo, para programas Java).
O CDF atualmente suporta as seguintes interfaces, nas quais os parâmetros são codificados como strings hexadecimais ASCII, a menos que descrito de outra forma:
A interface dsa testa implementações do Algoritmo de Assinatura Digital (DSA). Ela deve suportar as operações de assinatura e verificação:
| Operação | Entrada | Saída |
|---|---|---|
| Assinatura | p q g y x m | r s |
| Verificação | p q g y r s m | truth value |
Aqui p, q, g são parâmetros DSA, y é uma chave pública, x é uma chave privada, m é uma mensagem, r e s formam a assinatura, que deve ser retornada separada por uma nova linha. O valor verdade, “true” ou “false”, é representado como uma string.
A interface dsa suporta um teste opcional: o -h permite ignorar o processo de hash e fornecer diretamente o valor hash a ser assinado. Isso permite que o CDF realize mais testes, como verificar estouros ou truncamento de hash.
A interface ecdsa testa implementações do Algoritmo de Assinatura Digital de Curva Elíptica (ECDSA). Ela deve suportar as operações de assinatura e verificação:
| Operação | Entrada | Saída |
|---|---|---|
| Assinatura | x y d m | r s |
| Verificação | x y r s m | truth value |
Aqui x e y são coordenadas de chave pública ECDSA, d é uma chave privada, m é uma mensagem, e r e s formam a assinatura, que deve ser retornada separada por uma nova linha. O valor verdade, “true” ou “false”, é representado por uma string.
A flag -h serve ao mesmo propósito que no dsa.
Por favor, note que nosso design atual assume uma curva fixa, definida no programa testado.
Para obter resultados reproduzíveis com esses testes e aproveitar todas as capacidades de detecção do CDF, você precisa semear seu gerador aleatório com uma semente fixa ou usar uma variante ECDSA determinística, caso contrário o CDF não pode detectar problemas como problemas de mesmas tags automaticamente.
A interface enc testa operações de criptografia e descriptografia simétricas, tipicamente quando realizadas com uma cifra de bloco (cifras de fluxo podem ser testadas com a interface prf). Ela deve suportar criptografia e descriptografia:
| Operação | Entrada | Saída |
|---|---|---|
| Criptografia | k m | c |
| Descriptografia | k c | r |
Aqui k é uma chave, m é uma mensagem, c é um texto cifrado c e r é um texto claro recuperado.
A interface prf testa hash com chave (funções pseudoaleatórias, MACs), bem como cifras de fluxo:
| Operação | Entrada | Saída |
|---|---|---|
| Computação | k m | h |
Aqui k é uma chave, m é uma mensagem (ou nonce no caso de uma cifra de fluxo), e h é o resultado da computação PRF. Nossa interface assume tamanho de chave fixo e comprimentos de entrada variáveis. Se uma chave específica deve ser especificada, é responsabilidade do programa testado ignorar a entrada da chave ou a interface xof pode ser uma escolha melhor.
O rsaenc testa criptografia e descriptografia RSA, tanto OAEP (PKCS 2.1) quanto PKCS 1.5:
| Operação | Entrada | Saída |
|---|---|---|
| Criptografia | n e m | c |
| Descriptografia | p q e d c | r |
Aqui n é um módulo, e é um expoente público (para compatibilidade com certas bibliotecas, e também é necessário para descriptografia), m é uma mensagem m, p e q são fatores de n (tais que p > q, pois as bibliotecas geralmente exigem isso), d é um expoente privado, e r é um texto claro recuperado.
A interface xof testa funções hash, funções de saída extensível (XOFs), geradores determinísticos de bits aleatórios (DRBGs):
| Operação | Entrada | Saída |
|---|---|---|
| Computação | m | h |
Aqui m é a mensagem e h é o resultado h.
O CDF é baseado em ideias iniciais de JP Aumasson, divulgado pela primeira vez no WarCon 2016, e a maior parte do código foi escrita por Yolan Romailler.
O CDF é protegido por direitos autorais (c) 2016-2017 Nagravision SA, todos os direitos reservados.
O CDF é lançado sob a GPLv3.