7-bo‘lim
Simmetrik shifrlash va rejimlar
AES, blok rejimlari va nima uchun ECB hech qachon ishlatilmasligi kerak.
Ushbu bo‘lim mundarijasi
Simmetrik shifrlash - shifrlash va ochish uchun bitta kalit ishlatiladigan usul.
Eng keng tarqalgani - AES. Lekin AES ning o'zi yetarli emas: uni qaysi rejimda ishlatish natijani butunlay o'zgartiradi.
Blok shifri nima #
function bloklar(string $baytlar, int $olcham = 16): string
{
$natija = [];
foreach (str_split(bin2hex($baytlar), $olcham * 2) as $i => $b) {
$natija[] = sprintf(' %d: %s', $i, $b);
}
return implode(PHP_EOL, $natija);
}
echo 'AES blok o\'lchami: ',
openssl_cipher_iv_length('aes-256-cbc'), ' bayt', PHP_EOL;
foreach (['aes-128-cbc', 'aes-192-cbc', 'aes-256-cbc'] as $shifr) {
// kalit uzunligini nomidan olamiz
$bit = (int) explode('-', $shifr)[1];
printf('%-12s kalit: %2d bayt (%d bit)%s', $shifr, $bit / 8, $bit, PHP_EOL);
}
AES blok o'lchami: 16 bayt
aes-128-cbc kalit: 16 bayt (128 bit)
aes-192-cbc kalit: 24 bayt (192 bit)
aes-256-cbc kalit: 32 bayt (256 bit)
AES 16 baytlik blokni 16 baytlik blokka aylantiradi. U faqat shu ishni qiladi.
| Xususiyat | Qiymat |
|---|---|
| Blok o'lchami | 16 bayt (128 bit) - doimo |
| Kalit o'lchami | 16, 24 yoki 32 bayt |
| Chiqish o'lchami | Kirish bilan bir xil |
Savol tug'iladi: 16 baytdan uzun ma'lumotni qanday shifrlash kerak?
Javob - rejim (mode of operation). U bloklarni qanday bog'lashni belgilaydi. Va aynan shu yerda eng ko'p xato qilinadi.
ECB - eng oddiy va eng xavfli rejim #
$kalit = hex2bin(str_repeat('11', 32));
// Ataylab takrorlanuvchi ma'lumot
$matn = str_repeat('AAAABBBBCCCCDDDD', 4); // 4 ta bir xil blok
echo 'ochiq matn (4 blok, hammasi bir xil):', PHP_EOL;
echo bloklar($matn), PHP_EOL, PHP_EOL;
$shifr = openssl_encrypt($matn, 'aes-256-ecb', $kalit,
OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING);
echo 'ECB bilan shifrlangan:', PHP_EOL;
echo bloklar($shifr), PHP_EOL;
ochiq matn (4 blok, hammasi bir xil):
0: 41414141424242424343434344444444
1: 41414141424242424343434344444444
2: 41414141424242424343434344444444
3: 41414141424242424343434344444444
ECB bilan shifrlangan:
0: a0aaf87de1cb2896d7bd3c2d99be11c9
1: a0aaf87de1cb2896d7bd3c2d99be11c9
2: a0aaf87de1cb2896d7bd3c2d99be11c9
3: a0aaf87de1cb2896d7bd3c2d99be11c9
Shifrmatndagi to'rtala blok ham bir xil.
Sabab: ECB har blokni mustaqil shifrlaydi. Bir xil ochiq blok - har doim bir xil shifrblok.
Bu shuni anglatadi: hujumchi shifrni ochmasdan turib ma'lumot tuzilishini ko'radi.
Buni yaqqolroq ko'rsatamiz - oddiy "rasm" bilan:
$kalit = hex2bin(str_repeat('11', 32));
// 8x8 "rasm": 1 - qora nuqta, 0 - oq
$rasm = [
'0000000000000000', '0011111111110000', '0011000000110000',
'0011000000110000', '0011111111110000', '0011000000110000',
'0011000000110000', '0000000000000000',
];
echo 'ASL "RASM":', PHP_EOL;
foreach ($rasm as $q) {
echo ' ', strtr($q, ['0' => '.', '1' => '#']), PHP_EOL;
}
// Har qatorni alohida blok sifatida ECB bilan shifrlaymiz
echo PHP_EOL, 'ECB dan KEYIN (har qator - bitta blok):', PHP_EOL;
foreach ($rasm as $q) {
$s = openssl_encrypt($q, 'aes-256-ecb', $kalit,
OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING);
echo ' ', substr(bin2hex($s), 0, 32), PHP_EOL;
}
ASL "RASM":
................
..##########....
..##......##....
..##......##....
..##########....
..##......##....
..##......##....
................
ECB dan KEYIN (har qator - bitta blok):
c49df3f75ca6d86094ded52d5def0e2a
9f792a648eefcb768b160a072aa28af5
136727c6b09d854eeed0afa396896fee
136727c6b09d854eeed0afa396896fee
9f792a648eefcb768b160a072aa28af5
136727c6b09d854eeed0afa396896fee
136727c6b09d854eeed0afa396896fee
c49df3f75ca6d86094ded52d5def0e2a
Chiqishga diqqat bilan qarang: bir xil qatorlar bir xil shifrmatn berdi.
- 1-va 8-qator (
0000...) - bir xil - 3-, 4-, 6-, 7-qatorlar (
0011000000110000) - bir xil - 2-va 5-qator (
0011111111110000) - bir xil
Ya'ni "rasm" ning tuzilishi shifrlangandan keyin ham o'qiladi.
Aynan shu sabab bilan ECB rejimida shifrlangan tasvir mashhur misolga aylangan: pingvin rasmi shifrlangandan keyin ham tanib olinadi.
| ECB nima yashiradi | Nima yashirmaydi |
|---|---|
| Har blokning aniq mazmuni | Bloklar takrorlanishini |
| Baytlarning qiymati | Ma'lumot tuzilishini |
| Uzunlikni | |
| Naqshlarni |
Xulosa: ECB ni hech qachon ishlatmang. Undan foydalanadigan yagona to'g'ri holat - bitta, tasodifiy, takrorlanmaydigan blokni shifrlash.
CBC - bloklarni bog'lash #
$kalit = hex2bin(str_repeat('11', 32));
$iv = hex2bin(str_repeat('22', 16));
$matn = str_repeat('AAAABBBBCCCCDDDD', 4);
$shifr = openssl_encrypt($matn, 'aes-256-cbc', $kalit,
OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv);
echo 'CBC bilan (bir xil ochiq bloklar):', PHP_EOL;
echo bloklar($shifr), PHP_EOL;
echo PHP_EOL, 'endi bloklar HAR XIL', PHP_EOL;
CBC bilan (bir xil ochiq bloklar):
0: 918973ccd319a41aa72b4c929945d49b
1: f32d20b5ef53af71374414749e5824cd
2: bb1d398cfb04a7f9d62f742171562dac
3: 988295171fd30a18b9a63fb1b94fc0f7
endi bloklar HAR XIL
CBC har blokni shifrlashdan oldin oldingi shifrblok bilan XOR qiladi. Birinchi blok uchun esa IV (initialization vector) ishlatiladi.
$kalit = hex2bin(str_repeat('11', 32));
$matn = 'Bir xil xabar!!!';
// Bir xil matn, TURLI IV
foreach (['33', '44', '55'] as $bayt) {
$iv = hex2bin(str_repeat($bayt, 16));
$s = openssl_encrypt($matn, 'aes-256-cbc', $kalit,
OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv);
printf('IV %s...: %s%s', $bayt . $bayt, bin2hex($s), PHP_EOL);
}
IV 3333...: a7b0f8cbb0eac7bb47324e16053e3eaf
IV 4444...: f2be64d906943acb2ee5854b41e0e925
IV 5555...: 2a10c4a31c42774ae30698794ca6d08a
CBC ning jiddiy kamchiligi: o'zgartirish sezilmaydi #
$kalit = hex2bin(str_repeat('11', 32));
$iv = hex2bin(str_repeat('22', 16));
// Ikki blokli xabar
$matn = 'AAAAAAAAAAAAAAAA' . 'rol=user;id=1234';
$shifr = openssl_encrypt($matn, 'aes-256-cbc', $kalit,
OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv);
echo 'asl matn: ', $matn, PHP_EOL;
echo 'ochilgan: ', openssl_decrypt($shifr, 'aes-256-cbc', $kalit,
OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv), PHP_EOL;
// Hujumchi BIRINCHI blokning bir baytini o'zgartiradi
$buzilgan = $shifr;
$buzilgan[5] = chr(ord($buzilgan[5]) ^ 0x04);
$natija = openssl_decrypt($buzilgan, 'aes-256-cbc', $kalit,
OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv);
echo PHP_EOL, 'shifrmatn ozgartirilgandan keyin:', PHP_EOL;
echo 'xato berdimi: ', $natija === false ? 'ha' : 'YOQ', PHP_EOL;
echo '1-blok: ', bin2hex(substr($natija, 0, 16)), PHP_EOL;
echo '2-blok: ', substr($natija, 16), PHP_EOL;
asl matn: AAAAAAAAAAAAAAAArol=user;id=1234
ochilgan: AAAAAAAAAAAAAAAArol=user;id=1234
shifrmatn ozgartirilgandan keyin:
xato berdimi: YOQ
1-blok: 9ab2b2f2017f69d7a740c1f73fdff2d1
2-blok: rol=uwer;id=1234
Ikki narsa sodir bo'ldi:
- Birinchi blok axlatga aylandi (kutilgan natija).
- Ikkinchi blok aniq boshqarilgan tarzda o'zgardi:
rol=user->rol=uwer. Biz shifrmatnning 5-baytini0x04bilan XOR qildik va ochiq matnning aynan 5-bayti o'sha qiymatga o'zgardi (s=0x73->w=0x77).
CBC da ochish shunday ishlaydi:
ochiq_blok[i] = AES_ochish(shifr_blok[i]) XOR shifr_blok[i-1]
Ya'ni shifr_blok[i-1] dagi har bir bit ochiq_blok[i] dagi
o'sha o'rindagi bitni to'g'ridan-to'g'ri o'zgartiradi.
Hujumchi bitta blokni qurbon qilib, keyingi blokni
tanlagan tarzda o'zgartira oladi. Masalan rol=user ni
rol=admin ga aylantirish.
Bu moslashuvchanlik (malleability) muammosi. U CBC ning kamchiligi emas, balki butunlik tekshiruvi yo'qligining oqibati.
Eng muhimi: openssl_decrypt false qaytarmadi. Kod
buzilgan ma'lumotni normal deb qabul qildi.
To'ldirish (padding) #
$kalit = hex2bin(str_repeat('11', 32));
$iv = hex2bin(str_repeat('22', 16));
foreach (['a', 'salom', str_repeat('x', 15), str_repeat('x', 16)] as $m) {
// OPENSSL_ZERO_PADDING SIZ - PKCS#7 to'ldirish ishlatiladi
$s = openssl_encrypt($m, 'aes-256-cbc', $kalit, OPENSSL_RAW_DATA, $iv);
printf('%2d bayt matn -> %2d bayt shifr%s',
strlen($m), strlen($s), PHP_EOL);
}
1 bayt matn -> 16 bayt shifr
5 bayt matn -> 16 bayt shifr
15 bayt matn -> 16 bayt shifr
16 bayt matn -> 32 bayt shifr
Blok shifri to'liq bloklar bilan ishlaydi, shuning uchun oxirgi blok to'ldiriladi.
PKCS#7 qoidasi: nechta bayt yetishmasa, o'sha qiymatdagi shuncha bayt qo'shiladi.
| Matn uzunligi | Qo'shiladi | To'liq uzunlik |
|---|---|---|
| 15 bayt | 01 × 1 | 16 |
| 14 bayt | 02 × 2 | 16 |
| 16 bayt | 10 × 16 | 32 |
Oxirgi qatorga e'tibor bering: matn aynan blok uzunligida bo'lsa ham, butun bir blok qo'shiladi. Aks holda ochishda to'ldirish bor-yo'qligini bilib bo'lmasdi.
Tarixiy jihatdan to'ldirish jiddiy zaiflik manbai bo'lgan (padding oracle): server "to'ldirish noto'g'ri" degan xato bersa, hujumchi shu javobga qarab shifrni bayt-bayt ochishi mumkin edi.
AEAD rejimlari (8-bo'lim) bu muammoni butunlay yo'q qiladi: ular oqim shifridan foydalanadi va to'ldirish umuman kerak emas.
CTR - blok shifrini oqim shifriga aylantirish #
$kalit = hex2bin(str_repeat('11', 32));
$nonce = hex2bin(str_repeat('33', 16));
foreach (['a', 'salom', str_repeat('x', 100)] as $m) {
$s = openssl_encrypt($m, 'aes-256-ctr', $kalit, OPENSSL_RAW_DATA, $nonce);
printf('%3d bayt matn -> %3d bayt shifr%s',
strlen($m), strlen($s), PHP_EOL);
}
echo PHP_EOL, 'CTR da toldirish YOQ - uzunlik bir xil', PHP_EOL;
1 bayt matn -> 1 bayt shifr
5 bayt matn -> 5 bayt shifr
100 bayt matn -> 100 bayt shifr
CTR da toldirish YOQ - uzunlik bir xil
Rejimlarni solishtirish #
| Rejim | Naqsh yashiriladimi | To'ldirish | Butunlik | Tavsiya |
|---|---|---|---|---|
| ECB | Yo'q | Kerak | Yo'q | Hech qachon |
| CBC | Ha (IV bilan) | Kerak | Yo'q | Faqat HMAC bilan |
| CTR | Ha | Kerak emas | Yo'q | Faqat HMAC bilan |
| GCM | Ha | Kerak emas | Ha | Ha |
| ChaCha20-Poly1305 | Ha | Kerak emas | Ha | Ha |
$mavjud = openssl_get_cipher_methods();
foreach (['aes-256-ecb', 'aes-256-cbc', 'aes-256-ctr',
'aes-256-gcm', 'chacha20-poly1305'] as $c) {
printf('%-20s %s%s', $c,
in_array($c, $mavjud, true) ? 'mavjud' : 'yoq', PHP_EOL);
}
aes-256-ecb mavjud
aes-256-cbc mavjud
aes-256-ctr mavjud
aes-256-gcm mavjud
chacha20-poly1305 mavjud
Yangi kod yozayotgan bo'lsangiz, tanlov oddiy:
| Vaziyat | Tanlov |
|---|---|
| Umumiy holat | AES-256-GCM |
| Uskunada AES tezlatgichi yo'q | ChaCha20-Poly1305 |
| Juda uzun oqim (terabaytlar) | Bo'laklarga bo'lib AEAD |
| Eski tizim bilan moslik | CBC + HMAC (encrypt-then-MAC) |
ECB va "yalang'och" CBC ni yangi kodda umuman ishlatmang.
Keyingi bo'lim to'liq AEAD ga bag'ishlangan.
- AES blok o'lchamini va uch xil kalit uzunligini chiqaring.
- Takrorlanuvchi matnni ECB bilan shifrlang va bloklarni solishtiring.
- "Rasm" misolini takrorlang - qaysi qatorlar bir xil chiqdi?
- Xuddi shu matnni CBC bilan shifrlang va farqni ko'ring.
- Bir xil matnni uch xil IV bilan shifrlab, natijalarni solishtiring.
- CBC shifrmatnining bir baytini o'zgartiring va ochib ko'ring.
- Ochish xato berdimi? Ikkinchi blok qanday o'zgardi?
- 15, 16 va 17 baytlik matnlar uchun shifr uzunligini o'lchang.
- Xuddi shu matnlarni CTR bilan shifrlang - uzunlik o'zgardimi?
- Qaysi rejimni qachon ishlatish kerakligini jadval qilib yozing.
Xulosa #
- AES 16 baytlik blokni shifrlaydi; undan uzun ma'lumot uchun rejim kerak.
- ECB har blokni mustaqil shifrlaydi va naqshni saqlab qoladi.
- Bir xil ochiq blok ECB da har doim bir xil shifrblok beradi - tuzilma oshkor bo'ladi.
- ECB ni hech qachon ishlatmang.
- CBC har blokni oldingi shifrblok bilan XOR qiladi, birinchisi uchun IV ishlatiladi.
- Bir xil matn turli IV bilan butunlay boshqa shifrmatn beradi.
- CBC butunlikni tekshirmaydi: o'zgartirilgan shifrmatn xato bermay ochiladi.
- Hujumchi bitta blokni qurbon qilib, keyingisini boshqarib o'zgartira oladi.
- PKCS#7 to'ldirish: matn blok uzunligiga teng bo'lsa ham butun blok qo'shiladi.
- Yangi kodda AES-256-GCM yoki ChaCha20-Poly1305 ishlating.
Keyingi bo'limda autentifikatsiyalangan shifrlash (AEAD) bilan tanishamiz.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
Xatolik topdingizmi?
Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.