ESP 数据包的 C AES-GCM 解密不起作用

C AES-GCM Decryption of ESP packet not working

我正在尝试解密使用 AES128-GCM12 加密的 ESP 数据包。我使用 c openssl 库进行解密。但是解密结果是错误的!

我使用了两个 Linux 18.04 虚拟机来模拟带有 strongswan IPsec 的 ESP 数据包。我捕获了 ESP 数据包并在我的解密函数中打印了我的整个变量。

static void _aes_gcm_decrypt(uint8_t* payload, uint16_t payload_len, uint8_t* key, uint16_t key_len, uint8_t* iv, uint8_t iv_len, uint8_t* icv, uint8_t icv_len, uint8_t* aad, uint8_t aad_len) {
    EVP_CIPHER_CTX *ctx;
    int outlen;
    uint8_t outbuf[1024];
    int rv;
    const EVP_CIPHER *cipher;

    // Ignore salt
    key_len -= 4;
    switch(key_len) {
        case 16: cipher = EVP_aes_128_gcm(); break;
        case 24: cipher = EVP_aes_192_gcm(); break;
        case 32: cipher = EVP_aes_256_gcm(); break;
        default: break;
    }

    print_hex("original payload", payload, payload_len);
    print_hex("key", key, key_len);
    print_hex("iv", iv, iv_len);
    print_hex("icv", icv, icv_len);
    print_hex("aad", aad, aad_len);

    ctx = EVP_CIPHER_CTX_new();
    // Select cipher
    EVP_DecryptInit_ex(ctx, cipher, NULL, key, iv);
    // Set IV length, omit for 96 bits
    EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, iv_len, NULL);
    // Specify key and IV
    EVP_DecryptInit_ex(ctx, NULL, NULL, key, iv);
    // Zero or more calls to specify any AAD
    EVP_DecryptUpdate(ctx, NULL, &outlen, aad, aad_len);
    // Decrypt plaintext
    EVP_DecryptUpdate(ctx, outbuf, &outlen, payload, payload_len);
    print_hex("decrypted_payload", outbuf, outlen);

    EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, icv_len, icv);
    rv = EVP_DecryptFinal(ctx, icv, &outlen);

    printf("Tag Verify %s\n", rv > 0 ? "Successful!" : "Failed!");
    EVP_CIPHER_CTX_free(ctx);
}

这是我捕获的数据包。

04:51:57.347960 IP (tos 0x0, ttl 64, id 48604, offset 0, flags [DF], proto ESP (50), length 136)
    pc2 > 10.10.10.100: ESP(spi=0xc08247f5,seq=0x2), length 116
        0x0000:  0011 2233 4401 0800 2758 2898 0800 4500
        0x0010:  0088 bddc 4000 4032 538b 0a0a 0a65 0a0a
        0x0020:  0a64 c082 47f5 0000 0002 7a2b 37d7 160c
        0x0030:  853c 870d 1119 5a34 9d95 e597 be6a 8bc7
        0x0040:  2037 a1f7 ba02 1ef2 a0be de5e 5406 a5b1
        0x0050:  0e03 c463 c235 5c45 9b51 6734 1f28 e364
        0x0060:  2b36 470b 64da bfa3 1a68 f209 94aa 44b0
        0x0070:  9131 ffe0 12f1 9208 3a7b aa95 da51 bafd
        0x0080:  31cd 3d0a 8733 56e0 ae0d d7b5 13fe 8c5e
        0x0090:  96d8 598d a74f

这是我打印的变量

original payload(88): 
    0x0000:  870d 1119 5a34 9d95 e597 be6a 8bc7 2037 
    0x0010:  a1f7 ba02 1ef2 a0be de5e 5406 a5b1 0e03 
    0x0020:  c463 c235 5c45 9b51 6734 1f28 e364 2b36 
    0x0030:  470b 64da bfa3 1a68 f209 94aa 44b0 9131 
    0x0040:  ffe0 12f1 9208 3a7b aa95 da51 bafd 31cd 
    0x0050:  3d0a 8733 56e0 ae0d 
key(16): 
    0x0000:  1a0f cccc 0315 2b58 1b1a 02ea 3664 485f 
iv(8): 
    0x0000:  7a2b 37d7 160c 853c 
icv(12): 
    0x0000:  d7b5 13fe 8c5e 96d8 598d a74f 
aad(8): 
    0x0000:  c082 47f5 0000 0002 
decrypted_payload(88): 
    0x0000:  860c 9110 87c6 db5a c40a a592 f862 b8ea 
    0x0010:  b476 3be6 0881 06d4 ad11 eb2a 2e1c 698d 
    0x0020:  a803 2417 8ffb 7130 444d 4fc8 b402 c602 
    0x0030:  1a0b 031b b89a 4ddb 707b f920 04ea 48c5 
    0x0040:  5424 5a9b bb34 a88a 08ee 2556 5532 3419 
    0x0050:  2621 19e6 f4e1 72b2 
Tag Verify Failed!

解密后的负载应该是一些 ICMP 数据包。但这是错误的! 是不是解密方式不对,还是包里的变量取错了?

  • 如果当前8字节IV(ESP-IV,见下文),则消息可以成功解密:

                  7a 2b 37 d7 16 0c 85 3c
    

    被下面的12字节IV替换(Nonce/AES-GCM-IV,见下文):

      b3 0d e6 26 7a 2b 37 d7 16 0c 85 3c 
    
  • 解密后的消息是:

      45 00 00 54 8e 07 40 00 40 01 82 da 0a 00 0b 64 
      0a 00 0a 64 08 00 04 f2 28 7d 00 01 ed 10 0b 5d 
      00 00 00 00 0e 4f 05 00 00 00 00 00 10 11 12 13
      14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23
      24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33
      34 35 36 37 01 02 02 04
    
  • 解释:根据AES-GCM-ESP description, Section 8.1, the key for AES-128-GCM-ESP has 20 bytes. The first 16 bytes are the AES-128-GCM key, the last 4 bytes are the salt. The nonce is composed of salt (4 bytes) and ESP-IV (8 bytes) and is therefore 12 bytes in size. The nonce is the AES-GCM-IV. For an explanation of the difference between ESP-IV and AES-GCM-IV, see here,section 2.在当前代码中,ESP-IV被用作AES-GCM-IV,这是错误的。如果使用nonce为AES-GCM-IV,则解密成功

  • salt 的 4 个字节可以被暴力破解为:

      b3 0d e6 26
    

    它们应该对应于 20 字节 AES-128-GCM-ESP 密钥的最后 4 个字节。

  • _aes_gcm_decrypt 方法有一个小缺陷,但在 GCM-mode 中没有影响。尽管如此,该方法应修改如下:

      ...
      EVP_DecryptUpdate(ctx, outbuf, &outlen, payload, payload_len);
      int plaintext_len = outlen;                                    // added
    
      EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, icv_len, icv);
    
      rv = EVP_DecryptFinal_ex(ctx, outbuf + outlen, &outlen);       // modified
      plaintext_len += outlen;                                       // added
      ...
    

    plaintext_len中存储解密消息的长度。 EVP_DecryptFinal decrypts a possible last incompletely filled block when padding is enabled. Since the GCM-mode doesn't use padding, the error has no effect. However, the length of the decrypted message stored in outlen is lost in the current code, see also here.