Reputation: 1
I have two ESP8266 microcontroller boards:
Board A is running a HTTP server and is able to switch a relay by GET request from Board B, which is the HTTP client.
To ensure that only Board B, and nobody else, will switch the relais on Board A, I want to implement some kind of challenge response authentication.
My idea was the following:
So if an attacker is sniffing network communication, he will receive both the raw bytes and the encrypted ones.
My questions to you:
Thanks in advance, Chris
Upvotes: 0
Views: 307
Reputation: 21583
DISCLAIMER: i am not a cryptography expert.
Is it (easily) possible to calculate the XTEA key if the attacker knows raw bytes and the encryptes ones?
nope, you still have to do a bruteforce to deduce the key and number of rounds used, AFAIK. (at least if you're using 19 rounds or more, as currently the only known XTEA cryptographic attacks affects 18 rounds or less, as of 2009. but given that the default-and-recommended is 32 rounds, that shouldn't be an issue unless you use a custom and low number of rounds.. like 18)
Is the described method a reasonable solution for my problem?
your protocol is vulnerable to bit-flipping attacks from a MITM attacker, as well as not providing protection against snooping/monitoring, a MITM attacker will know what command you're giving, and be able to change the command given, both of which could be easily avoided...
i think it would be better if the client just asks for the random bytes as a token, and sends the actual command together with the token, encrypted. this will protect your command from snooping, it will make a MITM attacker unable to deduce what command you sent even IF the attacker knows how the protocol works, as the token now serves as a salt for the encrypted command.. but you're still vulnerable to bit-flipping from a MITM attacker even if the attacker does not know the key, thus you should also add a checksum to make sure the ciphertext has not been tampered with... how about for the client:
// start with the actual command
$data=encrypt("switch_relay(5);"); // or whatever
function encrypt(string $command){
// because of XTEA length padding, we need to tell the server the inner command length, so add a big endian 16 bit `size header`
$data=to_big_endian_uint16_t(strlen($data)).$data;
// get a unique 1-time-token? this should serve as salt AND protect against replay attack
$token=fetchToken();
// add the token
$data=$token.$data;
// now calculate a checksum to protect against bit-flipping attacks
$checksum=hash('adler32be',$data); // or whatever checksum you prefer. just has to be strong enough to detect random bit-flipping from attackers that can't decrypt-modify-encrypt because they don't know the encryption key, see https://en.wikipedia.org/wiki/Malleability_(cryptography) / https://en.wikipedia.org/wiki/Bit-flipping_attack
// add checksum
$data=$checksum.$data;
// encrypt data
$data=XTEA::encrypt($data, $key, XTEA::PAD_RANDOM, 32);
return $data;
}
after this i would normally add another size header so the server knows how many bytes to read for the entire packet, but since you say you're using the HTTP protocol, i assume you'll use a Content-Length: X
header as the outer size header.. (or if you don't, you should probably do another $data=big_endian_uint16_t(strlen($data)).$data;
after xtea-encrypting it)
and for the server do like
function decrypt(string $data){
// 4=checksum 8=token 2=inner_command_length
if(strlen($data) < (4+8+2) || strlen($data) % 8 !== 0){
// can't be an xtea-encrypted command, wrong length.
return ERR_INVALID_LENGTH;
}
$data=XTEA::decrypt($data,$key,32);
$checksum=substr($data,0,4);
$data=substr($data,4);
if(hash('adler32be',$data)!=$checksum){
// checksum fail, can't be an xtea-encrypted command (or maybe it was corrupted or tampered with?)
return ERR_INVALID_CHECKSUM;
}
$token=substr($data,0,8);
$data=substr($data,8);
if(!is_valid_token($token)){
return ERR_INVALID_TOKEN;
}
$inner_size_len=big_endian_uint16_t_to_native_number(substr($data,0,2));
$data=substr($data,2);
if(strlen($data) < $inner_size_len){
return ERR_INVALID_INNER_SIZE;
}
// remove padding bytes
$data=substr($data,0,$inner_size_len);
return $data; // the actual decrypted command
}
..?
(i still see 3 potential issues with this, 1: forward secrecy is not provided, for that you'd need something much more complex, i think. 2: an attacker could maybe DoS-attack you by requesting one-time-tokens until you run out of ram or whatever, preventing legitimate clients from generating tokens, but given the token lifetime of 1 second, it would have to be a continuous active attack, and stop working once the attacker is blocked/removed. 3: if your commands can be larger than 65535 bytes, you may want to switch to a 32bit size header, or if your commands can be over 4GB, you may want to switch to an 64bit size header, and so on. but if your commands are small, a 16bit size header at 65535 bytes should suffice?)
Upvotes: 0