使用 C openssl AES-GCM 加密创建 ESP 数据包会抛出错误的 ICV
Creating ESP packet using C openssl AES-GCM encryption throws wrong ICV
我正在尝试使用 AES128-CCM16 加密我的 ICMP 数据包。我使用 c openssl 库进行加密。但是加密后的结果是错误的!
我使用了两个 Linux 18.04 虚拟机来模拟带有 strongswan IPsec 的 ESP 数据包。我捕获了 ESP 数据包并在我的解密函数中打印了我的整个变量。
static bool _aes_ccm_encrypt(uint8_t* payload, uint16_t payload_len, uint8_t* key, uint16_t key_len, uint8_t* iv, uint8_t iv_len, uint8_t* aad, uint8_t aad_len, uint8_t* icv, uint8_t icv_len) {
EVP_CIPHER_CTX *ctx = NULL;
int len;
uint8_t salt_len = 3;
uint8_t new_iv[iv_len + salt_len];
EVP_CIPHER *cipher = NULL;
switch(key_len) {
case 19: cipher = EVP_aes_128_ccm(); break;
case 27: cipher = EVP_aes_192_ccm(); break;
case 35: cipher = EVP_aes_256_ccm(); break;
default: break;
}
// Add salt to IV
memcpy(new_iv, key + (key_len - salt_len), salt_len);
memcpy(new_iv + salt_len, iv, iv_len);
iv = new_iv;
iv_len += salt_len;
ctx = EVP_CIPHER_CTX_new();
if(1 != EVP_EncryptInit_ex(ctx, cipher, NULL, NULL, NULL)
|| 1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_IVLEN, iv_len, NULL)
|| 1 != EVP_EncryptInit_ex(ctx, NULL, NULL, key, iv)
|| 1 != EVP_EncryptUpdate(ctx, NULL, &len, NULL, payload_len)
|| 1 != EVP_EncryptUpdate(ctx, NULL, &len, aad, aad_len)
|| 1 != EVP_EncryptUpdate(ctx, payload, &len, payload, payload_len)
|| 1 != EVP_EncryptFinal_ex(ctx, payload+len, &len))
goto failure;
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_GET_TAG, icv_len, icv);
print_hex("icv", icv, icv_len);
EVP_CIPHER_CTX_free(ctx);
return true;
failure:
ZF_LOGE("AES CCM Encryption failed");
if(ctx)
EVP_CIPHER_CTX_free(ctx);
return false;
}
这是我捕获的数据包
tcpdump: listening on enp0s8, link-type EN10MB (Ethernet), capture size 262144 bytes
08:48:00.686858 IP (tos 0x0, ttl 64, id 3134, offset 0, flags [none], proto ESP (50), length 140)
10.10.10.100 > pc2: ESP(spi=0xc8ee6879,seq=0x0), length 120
0x0000: ffff ffff ffff ffff ffff ffff 0800 4500
0x0010: 008c 0c3e 0000 4032 4526 0a0a 0a64 0a0a
0x0020: 0a65 c8ee 6879 0000 0000 0a0a 0a64 0a0a
0x0030: 0a65 c8df 30c2 3f71 282b 1ee8 73de 411f
0x0040: a937 fbe2 dd3a ee2d 77fd 8b5f 83d6 2523
0x0050: 8b7c 22d0 6af3 2fb7 1af3 6182 e263 caa4
0x0060: 6a43 12b5 fc84 52d7 1daa 2437 86a7 9dce
0x0070: 028e ee0c febe 543d 49f8 e6ca 96c1 e83b
0x0080: 6514 f23a b916 ba23 58fb 3f52 a522 bd6a
0x0090: 33e5 0739 0462 0000 0000
这是我打印的变量
原始负载
payload_o(88):
0x0x105790806: 4500 0054 0c3e 0000 4001 44a4 0a00 0a64
0x0x105790816: 0a00 0b64 0000 45be 1910 0001 408e 105d
0x0x105790826: 0000 0000 8772 0a00 0000 0000 1011 1213
0x0x105790836: 1415 1617 1819 1a1b 1c1d 1e1f 2021 2223
0x0x105790846: 2425 2627 2829 2a2b 2c2d 2e2f 3031 3233
0x0x105790856: 3435 3637 0000 0204
密钥
key(19):
0x0x7ff8a400215c: f2e2 66ad ebca 5110 6110 3d29 4900 263d
0x0x7ff8a400216c: 650a 19
四
iv(11):
0x0x7ffda9dbcc90: 650a 190a 0a0a 640a 0a0a 65
AAD
aad(8):
0x0x1057907f6: c8ee 6879 0000 0000
ICV
icv(16):
0x0x10579085e: 3f52 a522 bd6a 33e5 0739 0462 0000 0000
加密有效负载
payload_e(88):
0x0x105790806: c8df 30c2 3f71 282b 1ee8 73de 411f a937
0x0x105790816: fbe2 dd3a ee2d 77fd 8b5f 83d6 2523 8b7c
0x0x105790826: 22d0 6af3 2fb7 1af3 6182 e263 caa4 6a43
0x0x105790836: 12b5 fc84 52d7 1daa 2437 86a7 9dce 028e
0x0x105790846: ee0c febe 543d 49f8 e6ca 96c1 e83b 6514
0x0x105790856: f23a b916 ba23 58fb
我用零填充的 ICV 发送了这个数据包,但是 Linux IPsec 丢弃了这个数据包。
我错过了任何 openssl 选项吗?
我用过
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_GET_TAG, icv_len, icv);
使用参数 icv_len=16,但它只创建 12 字节的 ICV。
我解决了问题。所以自己回答
在 EVP_EncryptInit_ex(...)
之前,应插入以下代码。
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG, icv_len, NULL)
最终代码应该是这样的
static bool _aes_ccm_encrypt(uint8_t* payload, uint16_t payload_len, uint8_t* key, uint16_t key_len, uint8_t* iv, uint8_t iv_len, uint8_t* aad, uint8_t aad_len, uint8_t* icv, uint8_t icv_len) {
EVP_CIPHER_CTX *ctx = NULL;
int len;
uint8_t salt_len = 3;
uint8_t new_iv[iv_len + salt_len];
EVP_CIPHER *cipher = NULL;
switch(key_len) {
case 19: cipher = EVP_aes_128_ccm(); break;
case 27: cipher = EVP_aes_192_ccm(); break;
case 35: cipher = EVP_aes_256_ccm(); break;
default: break;
}
// Add salt to IV
memcpy(new_iv, key + (key_len - salt_len), salt_len);
memcpy(new_iv + salt_len, iv, iv_len);
iv = new_iv;
iv_len += salt_len;
print_hex("payload_o", payload, payload_len);
print_hex("key", key, key_len);
print_hex("iv", iv, iv_len);
print_hex("aad", aad, aad_len);
ctx = EVP_CIPHER_CTX_new();
if(1 != EVP_EncryptInit_ex(ctx, cipher, NULL, NULL, NULL)
|| 1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_IVLEN, iv_len, NULL)
|| 1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG, icv_len, NULL)
|| 1 != EVP_EncryptInit_ex(ctx, NULL, NULL, key, iv)
|| 1 != EVP_EncryptUpdate(ctx, NULL, &len, NULL, payload_len)
|| 1 != EVP_EncryptUpdate(ctx, NULL, &len, aad, aad_len)
|| 1 != EVP_EncryptUpdate(ctx, payload, &len, payload, payload_len)
|| 1 != EVP_EncryptFinal_ex(ctx, payload+len, &len))
goto failure;
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_GET_TAG, icv_len, icv);
print_hex("icv", icv, icv_len);
print_hex("payload_e", payload, payload_len);
EVP_CIPHER_CTX_free(ctx);
return true;
failure:
ZF_LOGE("AES CCM Encryption failed");
if(ctx)
EVP_CIPHER_CTX_free(ctx);
return false;
}
我正在尝试使用 AES128-CCM16 加密我的 ICMP 数据包。我使用 c openssl 库进行加密。但是加密后的结果是错误的!
我使用了两个 Linux 18.04 虚拟机来模拟带有 strongswan IPsec 的 ESP 数据包。我捕获了 ESP 数据包并在我的解密函数中打印了我的整个变量。
static bool _aes_ccm_encrypt(uint8_t* payload, uint16_t payload_len, uint8_t* key, uint16_t key_len, uint8_t* iv, uint8_t iv_len, uint8_t* aad, uint8_t aad_len, uint8_t* icv, uint8_t icv_len) {
EVP_CIPHER_CTX *ctx = NULL;
int len;
uint8_t salt_len = 3;
uint8_t new_iv[iv_len + salt_len];
EVP_CIPHER *cipher = NULL;
switch(key_len) {
case 19: cipher = EVP_aes_128_ccm(); break;
case 27: cipher = EVP_aes_192_ccm(); break;
case 35: cipher = EVP_aes_256_ccm(); break;
default: break;
}
// Add salt to IV
memcpy(new_iv, key + (key_len - salt_len), salt_len);
memcpy(new_iv + salt_len, iv, iv_len);
iv = new_iv;
iv_len += salt_len;
ctx = EVP_CIPHER_CTX_new();
if(1 != EVP_EncryptInit_ex(ctx, cipher, NULL, NULL, NULL)
|| 1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_IVLEN, iv_len, NULL)
|| 1 != EVP_EncryptInit_ex(ctx, NULL, NULL, key, iv)
|| 1 != EVP_EncryptUpdate(ctx, NULL, &len, NULL, payload_len)
|| 1 != EVP_EncryptUpdate(ctx, NULL, &len, aad, aad_len)
|| 1 != EVP_EncryptUpdate(ctx, payload, &len, payload, payload_len)
|| 1 != EVP_EncryptFinal_ex(ctx, payload+len, &len))
goto failure;
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_GET_TAG, icv_len, icv);
print_hex("icv", icv, icv_len);
EVP_CIPHER_CTX_free(ctx);
return true;
failure:
ZF_LOGE("AES CCM Encryption failed");
if(ctx)
EVP_CIPHER_CTX_free(ctx);
return false;
}
这是我捕获的数据包
tcpdump: listening on enp0s8, link-type EN10MB (Ethernet), capture size 262144 bytes
08:48:00.686858 IP (tos 0x0, ttl 64, id 3134, offset 0, flags [none], proto ESP (50), length 140)
10.10.10.100 > pc2: ESP(spi=0xc8ee6879,seq=0x0), length 120
0x0000: ffff ffff ffff ffff ffff ffff 0800 4500
0x0010: 008c 0c3e 0000 4032 4526 0a0a 0a64 0a0a
0x0020: 0a65 c8ee 6879 0000 0000 0a0a 0a64 0a0a
0x0030: 0a65 c8df 30c2 3f71 282b 1ee8 73de 411f
0x0040: a937 fbe2 dd3a ee2d 77fd 8b5f 83d6 2523
0x0050: 8b7c 22d0 6af3 2fb7 1af3 6182 e263 caa4
0x0060: 6a43 12b5 fc84 52d7 1daa 2437 86a7 9dce
0x0070: 028e ee0c febe 543d 49f8 e6ca 96c1 e83b
0x0080: 6514 f23a b916 ba23 58fb 3f52 a522 bd6a
0x0090: 33e5 0739 0462 0000 0000
这是我打印的变量
原始负载
payload_o(88):
0x0x105790806: 4500 0054 0c3e 0000 4001 44a4 0a00 0a64
0x0x105790816: 0a00 0b64 0000 45be 1910 0001 408e 105d
0x0x105790826: 0000 0000 8772 0a00 0000 0000 1011 1213
0x0x105790836: 1415 1617 1819 1a1b 1c1d 1e1f 2021 2223
0x0x105790846: 2425 2627 2829 2a2b 2c2d 2e2f 3031 3233
0x0x105790856: 3435 3637 0000 0204
密钥
key(19):
0x0x7ff8a400215c: f2e2 66ad ebca 5110 6110 3d29 4900 263d
0x0x7ff8a400216c: 650a 19
四
iv(11):
0x0x7ffda9dbcc90: 650a 190a 0a0a 640a 0a0a 65
AAD
aad(8):
0x0x1057907f6: c8ee 6879 0000 0000
ICV
icv(16):
0x0x10579085e: 3f52 a522 bd6a 33e5 0739 0462 0000 0000
加密有效负载
payload_e(88):
0x0x105790806: c8df 30c2 3f71 282b 1ee8 73de 411f a937
0x0x105790816: fbe2 dd3a ee2d 77fd 8b5f 83d6 2523 8b7c
0x0x105790826: 22d0 6af3 2fb7 1af3 6182 e263 caa4 6a43
0x0x105790836: 12b5 fc84 52d7 1daa 2437 86a7 9dce 028e
0x0x105790846: ee0c febe 543d 49f8 e6ca 96c1 e83b 6514
0x0x105790856: f23a b916 ba23 58fb
我用零填充的 ICV 发送了这个数据包,但是 Linux IPsec 丢弃了这个数据包。 我错过了任何 openssl 选项吗?
我用过
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_GET_TAG, icv_len, icv);
使用参数 icv_len=16,但它只创建 12 字节的 ICV。
我解决了问题。所以自己回答
在 EVP_EncryptInit_ex(...)
之前,应插入以下代码。
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG, icv_len, NULL)
最终代码应该是这样的
static bool _aes_ccm_encrypt(uint8_t* payload, uint16_t payload_len, uint8_t* key, uint16_t key_len, uint8_t* iv, uint8_t iv_len, uint8_t* aad, uint8_t aad_len, uint8_t* icv, uint8_t icv_len) {
EVP_CIPHER_CTX *ctx = NULL;
int len;
uint8_t salt_len = 3;
uint8_t new_iv[iv_len + salt_len];
EVP_CIPHER *cipher = NULL;
switch(key_len) {
case 19: cipher = EVP_aes_128_ccm(); break;
case 27: cipher = EVP_aes_192_ccm(); break;
case 35: cipher = EVP_aes_256_ccm(); break;
default: break;
}
// Add salt to IV
memcpy(new_iv, key + (key_len - salt_len), salt_len);
memcpy(new_iv + salt_len, iv, iv_len);
iv = new_iv;
iv_len += salt_len;
print_hex("payload_o", payload, payload_len);
print_hex("key", key, key_len);
print_hex("iv", iv, iv_len);
print_hex("aad", aad, aad_len);
ctx = EVP_CIPHER_CTX_new();
if(1 != EVP_EncryptInit_ex(ctx, cipher, NULL, NULL, NULL)
|| 1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_IVLEN, iv_len, NULL)
|| 1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG, icv_len, NULL)
|| 1 != EVP_EncryptInit_ex(ctx, NULL, NULL, key, iv)
|| 1 != EVP_EncryptUpdate(ctx, NULL, &len, NULL, payload_len)
|| 1 != EVP_EncryptUpdate(ctx, NULL, &len, aad, aad_len)
|| 1 != EVP_EncryptUpdate(ctx, payload, &len, payload, payload_len)
|| 1 != EVP_EncryptFinal_ex(ctx, payload+len, &len))
goto failure;
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_GET_TAG, icv_len, icv);
print_hex("icv", icv, icv_len);
print_hex("payload_e", payload, payload_len);
EVP_CIPHER_CTX_free(ctx);
return true;
failure:
ZF_LOGE("AES CCM Encryption failed");
if(ctx)
EVP_CIPHER_CTX_free(ctx);
return false;
}