不同语言中相同代码的不同行为
Different behaviour for same code in different languages
我正在尝试解密 Javascript (Node.js) 中的 Block TEA。我尝试在 C++ 中做同样的事情,它按预期工作:
#define DELTA 0x9e3779b9
#define MX (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[(p&3)^e] ^ z)))
#define CRYPT_WORDS (64-4)/4
#define CRYPT_OFFSET 1
void btea_decrypt(uint32_t *v, int n, int base_rounds, uint32_t const key[4])
{
uint32_t y, z, sum;
unsigned p, rounds, e;
/* Decoding Part */
rounds = base_rounds + 52/n;
sum = rounds*DELTA;
y = v[0];
do {
e = (sum >> 2) & 3;
for (p=n-1; p>0; p--) {
z = v[p-1];
y = v[p] -= MX;
}
z = v[n-1];
y = v[0] -= MX;
sum -= DELTA;
} while (--rounds);
}
int main()
{
static const uint32_t key[4] = {0x875bcc51, 0xa7637a66, 0x50960967, 0xf8536c51};
uint32_t buf[64] = {16, 23, 163, 242, 214, 213, 125, 48, 167, 44, 232,
23, 160, 192, 244, 116, 38, 255, 200, 38, 43, 57,
18, 235, 206, 103, 161, 210, 187, 164, 42, 227, 139,
248, 141, 205, 51, 132, 115, 233, 39, 53, 136, 207,
238, 190, 111, 57, 117, 233, 67, 133, 165, 84, 154,
161, 165, 173, 76, 115, 108, 0, 0, 71};
uint32_t cryptpart[CRYPT_WORDS];
// Decrypt encrypted portion
for (int i = 0; i < CRYPT_WORDS; i++) {
cryptpart[i] =
((uint32_t)buf[CRYPT_OFFSET+4*i ]) << 0 |
((uint32_t)buf[CRYPT_OFFSET+4*i+1]) << 8 |
((uint32_t)buf[CRYPT_OFFSET+4*i+2]) << 16 |
((uint32_t)buf[CRYPT_OFFSET+4*i+3]) << 24;
}
btea_decrypt(cryptpart, CRYPT_WORDS, 1, key);
for (int i = 0; i < CRYPT_WORDS; i++) {
buf[CRYPT_OFFSET+4*i ] = cryptpart[i] >> 0;
buf[CRYPT_OFFSET+4*i+1] = cryptpart[i] >> 8;
buf[CRYPT_OFFSET+4*i+2] = cryptpart[i] >> 16;
buf[CRYPT_OFFSET+4*i+3] = cryptpart[i] >> 24;
}
for (const auto& e : buf) {
std::cout << e << ", ";
}
}
它输出:
16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65280, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3561881601, 13913600, 54350, 212, 393407394, 1536747, 6002, 23, 3612094810, 14109745, 55116, 215, 589329, 2302, 8, 0, 5439472, 21247, 82, 0, 0, 0, 71,
但在 Node.js 中使用相同的代码(移植):
function btea_decrypt(v, n, base_rounds, key)
{
let y, z, sum;
let p, rounds, e;
/* Decoding Part */
rounds = base_rounds + 52/n;
sum = rounds*0x9e3779b9;
y = v[0];
do {
e = (sum >> 2) & 3;
for (p=n-1; p>0; p--) {
z = v[p-1];
y = v[p] -= (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[(p&3)^e] ^ z)));
}
z = v[n-1];
y = v[0] -= (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[(p&3)^e] ^ z)));
sum -= 0x9e3779b9;
} while (--rounds);
return v;
}
function main() {
let key = [0x875bcc51, 0xa7637a66, 0x50960967, 0xf8536c51];
let buf = [16, 23, 163, 242, 214, 213, 125, 48, 167, 44, 232,
23, 160, 192, 244, 116, 38, 255, 200, 38, 43, 57,
18, 235, 206, 103, 161, 210, 187, 164, 42, 227, 139,
248, 141, 205, 51, 132, 115, 233, 39, 53, 136, 207,
238, 190, 111, 57, 117, 233, 67, 133, 165, 84, 154,
161, 165, 173, 76, 115, 108, 0, 0, 71];
let cryptpart = [];
// Decrypt encrypted portion
for (let i = 0; i < (64-4)/4; i++) {
cryptpart[i] =
(buf[1+4*i ]) << 0 |
(buf[1+4*i+1]) << 8 |
(buf[1+4*i+2]) << 16 |
(buf[1+4*i+3]) << 24;
}
cryptpart = btea_decrypt(cryptpart, (64-4)/4, 1, key);
for (let i = 0; i < (64-4)/4; i++) {
buf[1+4*i ] = cryptpart[i] >> 0;
buf[1+4*i+1] = cryptpart[i] >> 8;
buf[1+4*i+2] = cryptpart[i] >> 16;
buf[1+4*i+3] = cryptpart[i] >> 24;
}
console.log(buf)
}
它永远卡在 do ... while
循环中。
我发现,Javascript 和 C++ 处理 0x9e3779b9
的方式不同,因为 JS 中的 0x9e3779b9 * 15
等于 39816536535 和 C++ 中的 1161830871。 C++ 中的数学有什么问题,如何在 JS 中实现?
抱歉,如果我的英语不是最好的。
您的问题是由整数溢出引起的。 unint32_t 是一个固定大小为 2^32 位的整数。 0x9e3779b9 * 15 是 39816536535,大约是 2^35。
这意味着您会溢出,因为内存位置根本不足以容纳您的号码。 Javascript 没有这个问题,因为它不是静态类型的,内存中分配的大小会动态增加以容纳它。
为 C++ 使用更大的数据类型,例如 unsigned long
或 size_t
(大多数系统上 unsigned long
的别名)。最好使用 auto
让编译器为您决定:
auto sum = rounds*0x9e3779b9;
这将解决您的问题并确保总和足够大以容纳数字
旁注:在您的 C++ 中有相当多的 C,在 C++ 中我们尽量避免 #define
s 并在我们第一次使用它们时声明变量,而不是在范围的顶部。编写 C++ 代码时,首选 const auto x
而不是 #define x
(如果是基本类型,可能 constexpr
)
我正在尝试解密 Javascript (Node.js) 中的 Block TEA。我尝试在 C++ 中做同样的事情,它按预期工作:
#define DELTA 0x9e3779b9
#define MX (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[(p&3)^e] ^ z)))
#define CRYPT_WORDS (64-4)/4
#define CRYPT_OFFSET 1
void btea_decrypt(uint32_t *v, int n, int base_rounds, uint32_t const key[4])
{
uint32_t y, z, sum;
unsigned p, rounds, e;
/* Decoding Part */
rounds = base_rounds + 52/n;
sum = rounds*DELTA;
y = v[0];
do {
e = (sum >> 2) & 3;
for (p=n-1; p>0; p--) {
z = v[p-1];
y = v[p] -= MX;
}
z = v[n-1];
y = v[0] -= MX;
sum -= DELTA;
} while (--rounds);
}
int main()
{
static const uint32_t key[4] = {0x875bcc51, 0xa7637a66, 0x50960967, 0xf8536c51};
uint32_t buf[64] = {16, 23, 163, 242, 214, 213, 125, 48, 167, 44, 232,
23, 160, 192, 244, 116, 38, 255, 200, 38, 43, 57,
18, 235, 206, 103, 161, 210, 187, 164, 42, 227, 139,
248, 141, 205, 51, 132, 115, 233, 39, 53, 136, 207,
238, 190, 111, 57, 117, 233, 67, 133, 165, 84, 154,
161, 165, 173, 76, 115, 108, 0, 0, 71};
uint32_t cryptpart[CRYPT_WORDS];
// Decrypt encrypted portion
for (int i = 0; i < CRYPT_WORDS; i++) {
cryptpart[i] =
((uint32_t)buf[CRYPT_OFFSET+4*i ]) << 0 |
((uint32_t)buf[CRYPT_OFFSET+4*i+1]) << 8 |
((uint32_t)buf[CRYPT_OFFSET+4*i+2]) << 16 |
((uint32_t)buf[CRYPT_OFFSET+4*i+3]) << 24;
}
btea_decrypt(cryptpart, CRYPT_WORDS, 1, key);
for (int i = 0; i < CRYPT_WORDS; i++) {
buf[CRYPT_OFFSET+4*i ] = cryptpart[i] >> 0;
buf[CRYPT_OFFSET+4*i+1] = cryptpart[i] >> 8;
buf[CRYPT_OFFSET+4*i+2] = cryptpart[i] >> 16;
buf[CRYPT_OFFSET+4*i+3] = cryptpart[i] >> 24;
}
for (const auto& e : buf) {
std::cout << e << ", ";
}
}
它输出:
16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65280, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3561881601, 13913600, 54350, 212, 393407394, 1536747, 6002, 23, 3612094810, 14109745, 55116, 215, 589329, 2302, 8, 0, 5439472, 21247, 82, 0, 0, 0, 71,
但在 Node.js 中使用相同的代码(移植):
function btea_decrypt(v, n, base_rounds, key)
{
let y, z, sum;
let p, rounds, e;
/* Decoding Part */
rounds = base_rounds + 52/n;
sum = rounds*0x9e3779b9;
y = v[0];
do {
e = (sum >> 2) & 3;
for (p=n-1; p>0; p--) {
z = v[p-1];
y = v[p] -= (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[(p&3)^e] ^ z)));
}
z = v[n-1];
y = v[0] -= (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[(p&3)^e] ^ z)));
sum -= 0x9e3779b9;
} while (--rounds);
return v;
}
function main() {
let key = [0x875bcc51, 0xa7637a66, 0x50960967, 0xf8536c51];
let buf = [16, 23, 163, 242, 214, 213, 125, 48, 167, 44, 232,
23, 160, 192, 244, 116, 38, 255, 200, 38, 43, 57,
18, 235, 206, 103, 161, 210, 187, 164, 42, 227, 139,
248, 141, 205, 51, 132, 115, 233, 39, 53, 136, 207,
238, 190, 111, 57, 117, 233, 67, 133, 165, 84, 154,
161, 165, 173, 76, 115, 108, 0, 0, 71];
let cryptpart = [];
// Decrypt encrypted portion
for (let i = 0; i < (64-4)/4; i++) {
cryptpart[i] =
(buf[1+4*i ]) << 0 |
(buf[1+4*i+1]) << 8 |
(buf[1+4*i+2]) << 16 |
(buf[1+4*i+3]) << 24;
}
cryptpart = btea_decrypt(cryptpart, (64-4)/4, 1, key);
for (let i = 0; i < (64-4)/4; i++) {
buf[1+4*i ] = cryptpart[i] >> 0;
buf[1+4*i+1] = cryptpart[i] >> 8;
buf[1+4*i+2] = cryptpart[i] >> 16;
buf[1+4*i+3] = cryptpart[i] >> 24;
}
console.log(buf)
}
它永远卡在 do ... while
循环中。
我发现,Javascript 和 C++ 处理 0x9e3779b9
的方式不同,因为 JS 中的 0x9e3779b9 * 15
等于 39816536535 和 C++ 中的 1161830871。 C++ 中的数学有什么问题,如何在 JS 中实现?
抱歉,如果我的英语不是最好的。
您的问题是由整数溢出引起的。 unint32_t 是一个固定大小为 2^32 位的整数。 0x9e3779b9 * 15 是 39816536535,大约是 2^35。
这意味着您会溢出,因为内存位置根本不足以容纳您的号码。 Javascript 没有这个问题,因为它不是静态类型的,内存中分配的大小会动态增加以容纳它。
为 C++ 使用更大的数据类型,例如 unsigned long
或 size_t
(大多数系统上 unsigned long
的别名)。最好使用 auto
让编译器为您决定:
auto sum = rounds*0x9e3779b9;
这将解决您的问题并确保总和足够大以容纳数字
旁注:在您的 C++ 中有相当多的 C,在 C++ 中我们尽量避免 #define
s 并在我们第一次使用它们时声明变量,而不是在范围的顶部。编写 C++ 代码时,首选 const auto x
而不是 #define x
(如果是基本类型,可能 constexpr
)