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.


exemplo offset


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:

diagramaClasses

sequenciaCifrar

sequenciaDecifrar

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.

cifrarclasses

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/
/>

Tags:

Código

so2.tar.gz