0

我有两个 ESP8266 微控制器板:

板 A 正在运行 HTTP 服务器,并且能够通过来自板 B(即 HTTP 客户端)的 GET 请求来切换中继。

为了确保只有板 B 而没有其他人会切换板 A 上的继电器,我想实现某种质询响应身份验证。

我的想法如下:

  1. 板 B 要求板 A 切换继电器
  2. 板 A 发送一些随机字节作为质询
  3. 板 B 使用 XTEA 算法加密这些原始字节并将值返回给板 A
  4. 板 A 解密板 B 的响应,并将其与自己的结果进行比较。如果响应来得太晚(例如一秒钟后)或响应无效,则身份验证将中止,并在下一次生成新的挑战。如果响应有效,则继电器将切换,并且下一次尝试也会有新的挑战。

因此,如果攻击者正在嗅探网络通信,他将同时收到原始字节和加密字节。

我对你的问题:

  1. 如果攻击者知道原始字节和加密字节,是否(容易)计算出 XTEA 密钥?
  2. 所描述的方法是我的问题的合理解决方案吗?

在此先感谢,克里斯

4

1 回答 1

0

免责声明:我不是密码学专家。

如果攻击者知道原始字节和加密字节,是否(容易)计算出 XTEA 密钥?

不,您仍然需要进行暴力破解来推断使用的密钥和轮数,AFAIK。(至少如果您使用 19 轮或更多,因为截至 2009 年,目前唯一已知的 XTEA 加密攻击会影响 18 轮或更少。但鉴于默认和推荐是 32 轮,这不应该是问题,除非您使用自定义和低轮数.. 像 18)

所描述的方法是我的问题的合理解决方案吗?

您的协议容易受到来自 MITM 攻击者的位翻转攻击,并且提供针对窥探/监视的保护,MITM 攻击者将知道您正在给出什么命令,并且能够更改给出的命令,这两者都可以很容易避免...

我认为如果客户端只要求随机字节作为令牌,并将实际命令与令牌一起发送,加密会更好。这将保护您的命令不被窥探,即使攻击者知道协议的工作原理,它也会使 MITM 攻击者无法推断出您发送的命令,因为令牌现在用作加密命令的盐......但你仍然即使攻击者不知道密钥,也容易受到 MITM 攻击者的位翻转,因此您还应该添加校验和以确保密文没有被篡改......对于客户端怎么样:

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

在此之后,我通常会添加另一个大小标头,以便服务器知道要为整个数据包读取多少字节,但是由于您说您使用的是 HTTP 协议,我假设您将使用Content-Length: X标头作为外部大小标头.. (或者如果你不这样做,你可能应该$data=big_endian_uint16_t(strlen($data)).$data;在 xtea 加密之后再做一次)

并且对于服务器确实喜欢

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
}

..?

(我仍然看到 3 个潜在问题,1:未提供前向保密,因为我认为您需要更复杂的东西。2:攻击者可能会通过请求一次性令牌来攻击您,直到您用完了 ram 或其他任何东西,阻止了合法客户端生成令牌,但鉴于令牌的生命周期为 1 秒,它必须是一个持续的主动攻击,一旦攻击者被阻止/移除,它就会停止工作。3:如果你的命令可以大于 65535 字节,您可能想要切换到 32 位大小的标头,或者如果您的命令可以超过 4GB,您可能想要切换到 64 位大小的标头,依此类推。但是如果您的命令很小,a 65535 字节的 16 位大小的标头就足够了?)

于 2019-03-03T21:29:04.583 回答