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.
我正在尝试解密使用 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 inoutlen
is lost in the current code, see also here.