Grupo: Douglas Bayer Santos, Gabriel Garcia Becker e João Lucas Scharf.
Nosso projeto consistem em garantir a autenticidade e privacidade de informações enviadas por um sensor.
Queremos garantir que a informação enviada por um sensor seja realmente dele e que, caso seja interceptada, esteja ilegível para o invasor.
Uma restrição do projeto é que toda o operação deve ser leva, pois um sensor não possui um grande poder de processamento.
Solução:
Utilizaremos uma solução criada a partir do OTP (One time password). Com o OTP, mesmo usando um algoritmo mais fraco, sua utilização se tornara mais segura, devido a janela de tempo. O invasor precisa interceptar a mensagem, quebrar e reenviar dentro dessa janela.
Cada sensor irá ter um Master Secret, único, e a base terá uma cópia desse MS. No momento dessa cópia, os relógios devem ser sincronizados.
Iremos usar o HMAC(Hash-based Message Authentication Code) para gerar uma chave a partir do master secret e o relógio. O HMac resultante da operação será a chave utilizada para cifrar a mensagem.
Para decifrar a mensagem é necessário a mesma chave.
HMac é uma implementação para calcular o MAC envolvendo um algoritmo de hash em combinação a uma chave criptografica. Para o calculo do HMac, podemos usar qualquer algoritmo de hash, como MD5, SHA1 e a familia SHA2. A força criptografica do HMac depende do algoritmo Hash usado, o tamanho da saida do Hash e do tamanho e do tipo da chave criptografica usada.
As chaves geradas mudam conforme o tempo avança, logo, cada chave só é valida por uma janela de tempo.
Caso, para decifrar, a chave gerada é invalida. Deve-se tentar com a chave da janela de tempo inferior, i -1 (esta atrasado), se novamente a chave for invalida, é feita uma tentativa com a próxima chave i+1 (esta adiantado). Caso este erro se persista, tentamos com até 20 posições anteriores e posteriores. Se ainda não foi possível decifrar, a operação é encerrada.
Se foi usada uma chave diferente da chave da janela de tempo esperada, salvamos essa diferença (offset), para nas próximas mensagens, a chance de acerto na primeira tentativa seja maior.
Algoritmos usados:
hash: SHA2
Cifragem simétrica: AES
Por que esses algoritmos?
A segurança de um algoritmo criptografico é diretamente proporcional a sua complexidade. Como os sensores possuem um baixo poder de processamento, optamos por usar algoritmos mais simples, que possuem um overhead baixo.
Outro fator da escolha desses algoritmos, é que o resultado do hash é o tamanho da chave necessária para a cifragem. Caso o tamanho não fosse compatível, seria preciso fazer compressão ou extensão das chaves
Diagramas:
Atualizações:
Como sugerido, utilizaremos o algoritmo Diffie-Helman para troca de chaves.
Resumidamente, o servidor e o sensor vão usar por padrão um mesmo primo p = 997 e uma raiz r = 2. Ambos geram um número natural aleatório, por exemplo, servidor = a = 11 e sensor = b = 13.
Servidor gera A = r^a mod p -> A = 2^11 mod 997 -> A = 54
Sensor gera B = r^b mod p -> B = 2^13 mod 997 -> B = 216
Servidor e sensor enviam A e B um para o outro
Servidor computa MS = B^a mod p = r^ba mod p, MS = 113
Sensor computa MS = A^b mod p = r^ab mod p, MS = 113
A MS sera igual para ambos, assim, compartilharão a mesma chave.
Andamento:
Pesquisamos implementações dos algoritmos de HMac e Hash e estamos fazendo as alterações necessárias para utilização no EPOS.
Implementação:
Conforme sugestão do professor, adicionamos a troca de chaves utilizando o algoritmo Diffie-Helman. Também alteramos os algoritmos usados. A utilização do acelerador AES nos forçou a mudar a nossa modelagem.
Para o calculo da janelo de tempo, é utilizada uma simples função:
basis = chronos.now()/lenghtOfWindow;
Nela, pegamos o tempo atual, e dividimos pelo tamanho da nossa janela de tempo, em segundos (Utilizamos uma janela de 86400s, equivalente a um dia). Caso a chave utilizada seja invalida, o tempo da janela de tempo pode ter se esgotado, ou a mensagem esta fora de sincronia, tratamos ambos casos:
unsigned long int verify = chronos.now()/lenghtOfWindow;
if(verify != basisCheck) {
basis = verify + (offSetCheck*lenghtOfWindow)
}
if(updateOffset) {
if (offSet = 0) {
offset++;
} else if(offSet > 0) {
offSet *= -1;
} else {
offSet *= -1;
offSet++;
}
offSetCheck = offSet;
}
A chave é gerada, conforme explicado anteriormente, através do calculo do hmac do tempo atual:
unsigned long int key = basis + (offSet*lenghtOfWindow);
//sha2
unsigned char digestOTP[SHA256_DIGEST_SIZE];
sha256((const unsigned char *) key, strlen(key), digestOTP);
//hmac
unsigned char macOTP[SHA256_DIGEST_SIZE];
hmac_sha256(masterSecret, 256, (unsigned char *) digestOTP, strlen(digestOTP), macOTP, mac_256_size);
OTPKey = (unsigned char *) macOTP;
Para cifrar um texto, testamos a chave, para ver se a janela de tempo não se esgotou, em seguida, utilizamos a chave OTP no acelerador AES:
char* SecurityGen::cipherData(char* data){
testKey();
AES_Controller *aes = AES_Controller::get_instance();
char *result;
aes->encrypt(data, OTPKey, result);
return result;
}
Para decifrar, fazemos procedimento semelhante, mas caso ocorra erro na decifragem, devemos atualizar o offset:
char* SecurityGen::decipherData(char* data){
char *result;
AES_Controller *aes = AES_Controller::get_instance();
bool sucess = aes->decrypt(data, OTPKey, result);
if(sucess){
basis = offSet;
offSet = 0;
return result;
} else {
testKey(true);
return decipherData(data);
}
}
Para testes, fizemos testes para os algoritmos de hash e hmac.
Neles, temos o calculo a partir dos mesmos elementos, onde esperamos resultados iguais. E de diferentes elementos, onde esperamos resultados diferentes.
Também temos um teste geral da aplicação, onde criamos um objeto SecurityGen, setamos uma masterKey nele (que pode ser gerada a partir do protocolo Diffie Helman disponivel no trabalho) ciframos uma informação, deciframos, e checamos se ambas são iguais.
Todos os testes foram realizados no eposmote. Com a proposta de acender um led verde para testes com sucesso, vermelho em caso de erro.
Códigos:
SecurityGen.h
#ifndef SECURITYGEN_H
#define SECURITYGEN_H
#include <mach/mc13224v/aes_controller.h>
#include "security/sha2.h"
#include "security/hmac_sha2.h"
#include "clock.h"
__BEGIN_SYS
class SecurityGen
{
private:
char* masterSecret;
char* OTPKey;
void testKey(bool updateOffset = false);
unsigned long int offSetCheck;
unsigned long int offSet;
unsigned long int basisCheck;
unsigned long int basis;
unsigned long int kmax; //const value
unsigned long int lenghtOfWindow; //lenght in seconds. const value
Clock chronos;
public:
SecurityGen();
char* cipherData(char* data);
char* decipherData(char* data);
void setMasterKey(char* masterKey);
};
__END_SYS
#endif
SecurityGen.cc
#include "security/securityGen.h"
__USING_SYS
#include "security/securityGen.h"
__USING_SYS
SecurityGen::SecurityGen(){
offSet = 0;
kmax = 2; //const value
lenghtOfWindow = 86400; //lenght in seconds. const value
basis = chronos.now()/lenghtOfWindow;
basisCheck = basis;
testKey();
}
void SecurityGen::setMasterKey(char* masterKey){
masterSecret = masterKey;
}
char* SecurityGen::cipherData(char* data){
testKey();
AES_Controller *aes = AES_Controller::get_instance();
char *result;
aes->encrypt(data, OTPKey, result);
return result;
}
void SecurityGen::setMasterKey(char* masterKey){
masterSecret = masterKey;
}
char* SecurityGen::decipherData(char* data){
char *result;
AES_Controller *aes = AES_Controller::get_instance();
bool sucess = aes->decrypt(data, OTPKey, result);
if(sucess){
return result;
} else {
testKey(true);
return decipherData(data);
}
}
void SecurityGen::testKey(bool updateOffset) {
if(updateOffset) {
if (offSet = 0) {
offset++;
} else if(offSet > 0) {
offSet *= -1;
} else {
offSet *= -1;
offSet++;
}
offSetCheck = offSet;
}
unsigned long int verify = chronos.now()/lenghtOfWindow;
if((verify != basisCheck) || OTPKey == NULL) {
basis = verify + (offSetCheck*lenghtOfWindow);
unsigned long int key = basis + (offSet*lenghtOfWindow);
//sha2
unsigned char digestOTP[SHA256_DIGEST_SIZE];
sha256((const unsigned char *) key, strlen(key), digestOTP);
//hmac
unsigned char macOTP[SHA256_DIGEST_SIZE];
hmac_sha256(masterSecret, 256, (unsigned char *) digestOTP, strlen(digestOTP), macOTP, mac_256_size);
OTPKey = (unsigned char *) macOTP;
}
/**
Cifra e envia a msg
*/
bool SecurityGen::sendCiphered(char* msg, TCP::Channel* client, char * address, int port)
{
char* result = cipherData(msg);
if(client->connect(TCP::Address(address, port))){
Alarm::delay(1000000);
client->send(result, sizeof(result));
Alarm::delay(1000000);
client->close();
return 1;
} else return 0;
}
/**
Recebe a msg e a decifra
*/
char* SecurityGen::receiveCiphered()
{
TCP::Channel socket;
socket.bind(1337);
char* msgReceived = 0;
char * decrypted = 0;
while(1){
while(!socket.listen());
socket.receive(msgReceived, sizeof(msgReceived));
decrypted = decipherData(msgReceived);
socket.close();
}
return decrypted;
}
}
Referencias:
OTP: http://www.faqs.org/rfcs/rfc2289.html
TOTP: http://tools.ietf.org/html/rfc6238
OverHead: http://www.cryptopp.com/benchmarks.html
Algoritmos: http://www.ouah.org/ogay/hmac/
/>