มีบางอย่างที่ทำให้เข้าใจผิดในบทความ Wikipedia พวกเขาเขียน:
$s = b \oplus (b \lll 1) \oplus (b \lll 2) \oplus (b \lll 3) \oplus (b \lll 4)$
เท่ากับ:
$s = b \คูณ 31 \mod 257$
ที่ไหน $\times$ คือการคูณพหุนามของ $ข$ และ $31$ นำมาเป็นบิตอาร์เรย์ แต่ $\mod 257$ ไม่ใช่การทำงานของโมดูโลมาตรฐาน แต่เป็น mod in $GF(2^8)$ และ $257$ เป็นจริงพหุนามของรูปแบบ $x^{9}+1$.
เราสามารถเห็นได้อย่างง่ายดายว่าการคูณของ $b \คูณ 31$ เท่ากับ:
$s = b \oplus (b \ll 1) \oplus (b \ll 2) \oplus (b \ll 3) \oplus (b \ll 4)$
เป็นบิต $7,6,5,4$, ที่ไหน $\ll$ เป็นการเลื่อนระดับบิต ไม่ใช่การเลื่อนแบบวนรอบ แต่ในตัวอย่าง Wikipedia และใน AES มีการเลื่อนแบบวงกลม แล้ววงกลมมาจากไหน? มันมาจากการลดลงของพหุนาม $x^9+1$. นี่คือวิธีการคูณด้วยการลดพหุนาม $GF(2^8)$:
#รวม <cstdint>
#รวม <cstdio>
#รวม <บิตเซ็ต>
#รวม<iostream>
uint8_t ทวีคูณ (uint a, uint b)
{
uint8_t p = 1;
uint16_t L = 1;
uint16_t ค = 0;
สำหรับ (int i ที่ไม่ได้ลงชื่อ = 0; i < 8; ++i)
{
ค ^= ก & (L << i) ? (uint16_t)ข << ผม : 0;
}
สำหรับ (int ที่ไม่ได้ลงชื่อ i = 8; i-- > 0; )
{
ถ้า (c & (L << (i + 8)))
{
ค ^= L << (i + 8);
ค ^= (uint16_t)p << ผม;
}
}
กลับ (uint8_t)ค;
}
int หลัก ()
{
ผลลัพธ์ uint = 0;
ผลลัพธ์ = คูณ (131,31);
std::cout << ผลลัพธ์;
กลับ 0;
}
เราต้องการพหุนามดีกรี $9$แต่เราสามารถกำหนดพหุนามแบบลดขนาดในการใช้งานจริงได้โดยใช้เพียงตัวแรกเท่านั้น $8$ บิตที่มีนัยสำคัญน้อยที่สุด ดังนั้นในกรณีของเรา $p=1$. ตอนนี้ถ้าเราดูว่าการคูณแบบไม่มีพกพาทำงานอย่างไร (ลูปแรก) เราจะเห็นว่า $7,6,5,4$ บิตเราจะได้ผลลัพธ์เดียวกันกับ shift:
$s = b \oplus (b \ll 1) \oplus (b \ll 2) \oplus (b \ll 3) \oplus (b \ll 4)$
แต่แทนที่บิต $11,10,9,8$ จะมีการเลื่อนบิต (ยกเลิก) อย่างแน่นอนในกระบวนการเปลี่ยนระดับบิต นี่เป็นเพราะการคูณแบบไม่ใช้การพกพาจะย้ายพวกมันไปทางซ้าย $1,2,3...$ ตำแหน่งแล้วทั้งหมดจะถูก xored ชอบที่นี่:
เนื่องจากเรากำลังคูณด้วย $31$ เราจะได้รับเสมอ $4$ บิตพิเศษในครึ่งที่มีนัยสำคัญสูงกว่าของผลลัพธ์ 16 บิตของเรา และพวกเขาจะต้องเสียใจ เพราะนี่คือวิธีการทำงานของการคูณแบบไม่พก ตอนนี้เรามีบิต $7,6,5,4$ เท่ากับผลลัพธ์ของ:
$s = b \oplus (b \ll 1) \oplus (b \ll 2) \oplus (b \ll 3) \oplus (b \ll 4)$
และบิต $11,10,9,8$ซึ่งอาจใช้แทนบิต $3,2,1,0$. ในการเลื่อนแบบวงกลม เราจำเป็นต้อง xor กลับบิตนี้เท่านั้น สิ่งนี้ทำได้โดย:
สำหรับ (int ที่ไม่ได้ลงชื่อ i = 8; i-- > 0; )
{
ถ้า (c & (L << (i + 8)))
{
ค ^= L << (i + 8);
ค ^= (uint16_t)p << ผม;
}
}
ถ้า $p=1$. เราจะเห็นว่ารหัสนี้กำลังตรวจสอบว่ามีบิตเท่ากับหรือไม่ $1$ ด้านซ้ายบนตำแหน่ง $i$ ของครึ่งบนของเรา และถ้าเป็นเช่นนั้น มันก็จะ xor บิตนั้นด้วยบิต $i$ ในครึ่งล่างของเรา และอัลกอริทึมทั้งหมดนั้นก็เท่ากับ xoring กับ circialr shift $\llll$:
$s = b \oplus (b \lll 1) \oplus (b \lll 2) \oplus (b \lll 3) \oplus (b \lll 4)$
เมื่อรู้สิ่งนี้จะเห็นได้ชัดว่าความเท่าเทียมกันประเภทนี้มีไว้สำหรับสิ่งใดก็ตาม $GF(2^k)$, ให้เราเลือก $p=1$ (ตามแบบแผนของรหัสที่โพสต์) เป็นพหุนามและตัวคูณของเราเท่ากับ $2^{\frac {k}{2}+1}+1$.