SAFER (צופן)

SAFER K-64

צופן SAFER K-64 הוא צופן בלוקים איטרטיבי בסגנון רשת החלפה תמורה המקבל בלוק טקסט קריא באורך 8 בתים ומפתח סודי באורך 8 בתים ומפיק טקסט מוצפן באורך 8 בתים. הפונקציה פנימית של הצופן מבוצעת שש פעמים (או יותר) כאשר בכל סבב פלט הפונקציה הופך לקלט בסבב הבא ופלט הצופן הסופי הוא פונקציה מסיימת של התוצאה מהסבב האחרון. בכל סבב שני תת-מפתחות באורך 8 בתים כל אחד, אותם מכינים באמצעות תהליך הכנה שיתואר להלן, מחוברים עם הבלוק הנוכחי בשיטה מיוחדת, מלבד פעולות פיזור וערבוב המפורטים להלן. בסך הכול הצופן משתמש ב- תת-מפתחות עבור  סבבים, כל אחד באורך שמונה בתים. הפונקציה המסיימת היא XOR של הבתים 1,4,5,8 עם הבתים המתאימים של תת-המפתח  בית אחרי בית וחיבור מודולו 256 של יתר הבתים 2,3,6,7 עם הבתים המתאימים במפתח. לפי המוסכמה סדר הבתים גדול, בית מס' 1 הוא הבית הכי משמעותי (מסדר גבוה). סדר פעולות ההצפנה מתואר בתרשים משמאל.

מבנה פונקציית ההצפנה

הצעד הראשון בתוך הסבב ה- כמתואר בתרשים משמאל, הוא חיבור בשני אופנים של המפתח עם בלוק הקלט, הבתים 1,4,5,8 מחוברים ב-XOR ואילו הבתים 2,3,6,7 מחוברים בחיבור רגיל מודולו 256 (בגבולות בתים). הסיבה לטכניקה זו היא ניסיון להרוס ליניאריות על ידי שילוב של פעולות מחבורות אלגבריות שונות כך שלא תהיה אסוציאטיביות ביניהן. התוצאה מועברת בשכבה אי-ליניארית אשר מיישמת החלפה לפי שני סוגי תיבות החלפה המשלימים זה את זה, (1) תיבות החלפה שערכיהן הם כל החזקות  של 45 מודולו 257 כאשר , למעט יוצא דופן אחד  ולא 256 ו(2) תיבות החלפה שערכיהן הם לוגריתם בדידי בבסיס 45 של כל הערכים  מודולו 257, למעט יוצא דופן אחד במקרה ש- התוצאה היא 128. לפי התיבות הראשונות אם הקלט הוא  התוצאה היא , לפי התיבות השניות אם הקלט הוא  הפלט הוא  כך שמתקיים . בפועל משתמשים בשתי טבלאות אחזור (look-up) שהוכנו מראש. ההחלפה מתבצעת לפי כלל דומה, הבתים 1,4,5,8 מוחלפים לפי התיבות מהסוג הראשון והאחרים לפי התיבות מהסוג השני. הפלט של שמונה הפונקציות האי-ליניאריות משולב עם תת-מפתח אחר באותו אופן אך בסדר הפוך, הבתים 1,4,5,8 מחוברים בחיבור רגיל ואילו הבתים 2,3,6,7 מחוברים ב-XOR. הפלט לאחר החיבור האחרון עם המפתח המתאים עובר בשלוש רמות של הטרנספורמציה הליניארית  המתוארת לעיל. פונקציה זו פועלת על שני בתים לכן מתבצעות בסך הכול ארבע טרנספורמציות בכל רמה (כל אחת מקבלת שני בתים ופולטת שני בתים) כשבין כל רמה של PHT מתבצעת תמורה (סידור מחדש) ברמה של בתים הנקראת Decimation-by-2 שהיא טריק ידוע מ-FFT שבו פשוט כל זוג מחליף בית אחד עם זוג אחר. למשל הבית השני בפלט של ה-PHT הראשון עובר למיקום השלישי ולאחר ה-PHT השני עובר למיקום החמישי. לאחר הפיזור הליניארי מסתיים הסבב וחוזרים להתחלה.

מבנה פונקציית הפענוח

מבנה הפענוח כמתואר בתרשים משמאל הוא פשוט הפעולות ההופכיות, בסדר הפוך למבנה ההצפנה צעד אחרי צעד ובסדר הפוך של המפתחות. תחילה מבוצעת טרנספורמציה על הקלט שהיא חיסור מודולו 256 של בתי תת-המפתח  המתאימים וכן XOR של יתר הבתים. היות ש-XOR הופכי של עצמו פשוט חוזרים עליו שוב ומתקבל הערך המקורי לדוגמה אם המפתח הוא 15 ובית המידע הוא 66 הרי שמתקבל . בחיסור מודולו 256 מספר שלילי הופך להיות חיובי על ידי החסרה מהמודולוס למשל המספר השלילי  הוא בעצם . לאחר מכן מבצעים  סבבים המבטלים את  הסבבים של ההצפנה. תחילה מעבירים את הקלט בטרנספורמציה הליניארית ההופכית IPHT וכן מעבירים בתמורות ההופכיות (ראה חיצים), מחסרים את תת-המפתח  מהפלט לפי הסדר, בבתים 1,4,5,8 מבצעים חיסור מודולו 256, ביתר הבתים מבצעים XOR. ועוברים לשלב האי-ליניארי ההופכי. בשלב זה פשוט מבצעים החלפה לפי סדר הפוך מסדר ההחלפה בהצפנה, היות שתיבות ההחלפה משלימות זו את זו, האפקט המתקבל הוא ביטול השפעת ההחלפה משלב ההצפנה. אם מחליפים ערך נתון לפי תיבת החלפה מסוג אחד ולאחר מכן מחליפים את התוצאה בתיבת ההחלפה מהסוג השני מתקבל הערך המקורי לדוגמה אם בית המידע הוא 174 אז  לכן . שוב מחסרים את המפתח המתאים לפי הסדר המופיע בהצפנה ובזה הושלם סבב אחד עליו חוזרים כמובן  פעמים עם המפתחות המתאימים. תהליך הפענוח מתואר בתרשים משמאל.

SAFER K-128

הצופן SAFER K-128 שונה מקודמו באורך המפתח ובמספר הסבבים וכן בתהליך הכנת המפתח בהתאם והוא מומחש בתרשים משמאל. גודל הבלוק, מבנה פונקציות ההצפנה והפענוח כולל טבלת ההטיות של המפתחות זהים לחלוטין ל-SAFER K-64. ההבדל הוא ש- מפתחות הסבבים מופקים ממפתח באורך 128 סיביות במקום 64 סיביות כפי שהיה בצופן הקודם. תהליך ההכנה החדש פותח על ידי קבוצת מפתחים מסינגפור. הם המליצו על לפחות  סבבים של הצפנה ופענוח עם מפתח 128 סיביות ולא יותר מ-12 סבבים.
תהליך הכנת המפתח תואם בצורה כזו שניתן יהיה להשתמש באותו קוד של האלגוריתם לשני המפתחות. תהליך הכנת המפתח מחלק את המפתח לשני חצאים ימין ושמאל המסומנים  ו- באורך של 64 סיביות כל אחד. במידה שמעוניינים במפתח באורך 64 סיביות פשוט מעתיקים את המפתח פעמיים כך שמתקבל  ואז תוצאת תהליך ההרחבה של שני החצאים מפיק תת-מפתחות זהים בדיוק לצופן SAFER K-64. במידה שמעוניינים במפתח 128 סיביות מעתיקים מחצית מהמפתח ל- והמחצית השנייה ל-.

SAFER SK-64 ו-SAFER SK-128

כאמור לאור קריפטואנליזה התגלה שהיות ובתי המפתח נשארים קבועים במקומם והשינויים מתרחשים רק בתוך הבתים עובדה זו משפיעה לרעה על התוצאות, כפי שהוכח, חומר המפתח במקרה זה מאפשר בתנאים מסוימים מציאת התנגשויות בדרך קלה יותר מאשר בכוח גס. כלומר שני ערכים שונים של מפתח שמפיקים טקסט מוצפן זהה מה שגורם לכך שהאלגוריתם לא יהיה מומלץ לשימוש כחלק מפונקציית גיבוב שמסתמכת על ההנחה שקשה למצוא התנגשויות. לאור זאת ביצע מסי שינויים בתהליך הכנת המפתח של אלגוריתם SAFER SK כאשר האותיות SK מציינות "מפתח מחוזק". ביתר פירוט בוצעו שני שינויים חשובים. תחילה נוסף בית זוגיות. כלומר המפתח הורחב משמונה בתים במקרה של SK-64 לתשעה בתים. ושבעה עשר בתים במקום ששה עשר במקרה של SK-128. הבית הנוסף הוא תוצאה של XOR של כל בתי המפתח. השינוי השני הוא הוספת הזזה מעגלית ברמה של בתים של כל בתי המפתח כולל בית הזוגיות. במילים אחרות מתחילים ממאגר של תשעה בתים או שבעה עשר בתים (מפתח 64 או 128 סיביות בהתאמה) וכל תת-מפתח נלקח מבתים אחרים לפי הסדר הבא: המפתח הראשון נלקח מהבתים הראשונים (כלומר לא נעשה כל שינוי בסדר). כעת אם מדובר על מפתח באורך 64 סיביות. תת-המפתח השני נלקח מהבתים 2 עד 9, השלישי מהבתים 3 עד 1 הרביעי מ-4 עד 2 (במעין מעגל סגור) וכן הלאה. במקרה של מפתח באורך 128 סיביות מתחילים מהבתים 1 עד 16, לאחר מכן הבתים 2 עד 17, לאחר מכן הבתים 3 עד 1, הבתים 4 עד 2 וכן הלאה. זאת בנוסף ליתר הפעולות המתוארות בסעיפים הקודמים. טריק דומה בוצע בצופן Threefish ובצפנים מודרניים נוספים.

SAFER+‎

SAFER+‎ פותח בשנת 2000 על ידי ג'יימס מסי ועמיתיו מהאקדמיה הארמנית הלאומית Gurgen Khahatrian ו-Melsik K. Kuregian. בניגוד לגרסאות קודמות של SAFER, בגרסה זו הושג שיפור נוסף באפקט הפיזור על ידי בחירה מושכלת של התמורות בין שכבות ה-PHT. התמורות החדשות של SAFER+‎ נקראו על ידי מסי "הערבוב הארמני" על שם ממציאיו. השם SAFER+‎ נועד להגדיש את השיפור המשמעותי בצופן זה לעומת קודמיו במשפחה. בנוסף גודל הבלוק ב-SAFER+‎ הורחב ל-128 סיביות, זאת כדי להתאימו לתקנים הבינלאומיים.
SAFER+‎ הוא צופן בלוקים איטרטיבי בשמונה סבבים (חזרות). הצופן מקבל בלוק טקסט קריא שהוא מחרוזת באורך 16 בתים ומפתח הצפנה באורך 16 בתים ומחזיר בלוק טקסט מוצפן באורך 16 בתים. בנוסף קיים תהליך מקביל של הכנת מפתח המפורט להלן שמפיק מהמפתח שנבחר על ידי המשתמש 17 תת-מפתחות כל אחד באורך 16 בתים, כל זוג מתאים לסבב אחד והאחרון משמש לפונקציה המסיימת. הפונקציה המסיימת פשוטה. היא מחברת את בתי המפתח  עם בתי התוצאה מהסבב האחרון של הפונקציה הפנימית. החיבור דומה לצפנים הקודמים במשפחה. כלומר מחצית מבתי הבלוק לסירוגין (הבתים שמספרם לפי הסדר משמאל לימין הוא 1,4,5,8,9,12,13,16) מחוברים ב-XOR המסומן  והמחצית השנייה הבתים שמספרם הוא 2,3,6,7,10,11,14,15 בחיבור רגיל מודולו 256 המסומן .

תקן בלוטות'

תקן בלוטות' בגרסאות הקודמות עד 4.0 אימץ את SAFER+‎ כחלק מהתהליך האימות בשלב ההתחברות בין התקנים וביצע שינוי קטן בצופן המשולב בתהליך האימות, תוצאת הסבב הראשון חוברה לקלט של הסבב השלישי. שינוי זה נועד להרוס את ההפיכות של הצופן כדי שלא ניתן יהיה להשתמש בו גם כפונקציית הצפנה.

SAFER++‎

SAFER++‎ הוא צופן בלוקים איטרטיבי המהווה שיפור נוסף על הצפנים הקודמים במשפחת SAFER. הצופן מקבל בלוק טקסט קריא באורך 128 סיביות ומפתח הצפנה שמגיע בשתי אופציות 128 סיביות או 256 סיביות ומפיק בלוק טקסט מוצפן באורך 128 סיביות. הפונקציה הפנימית מבוצעת 7 פעמים במקרה של מפתח 128 סיביות ו-10 פעמים במקרה של מפתח באורך 256 סיביות. בסך הכול הצופן עושה שימוש ב- מפתחות עבור  הסבבים של הפונקציה הפנימית, כל אחד באורך 16 בתים, לכל סבב של הפונקציה הפנימית מתאימים שני תת-מפתחות. את המפתחות מפיקים באמצעות תהליך הרחבה שיתואר בהמשך. המפתח הנוסף  מתאים לפונקציה המסיימת לאחר כל הסבבים. לפענוח משתמשים במפתחות בסדר הפוך.
הפונקציה המסיימת מבצעת חיבור עם המפתח האחרון בשיטה זו: הבתים 1,4,5,8,9,12,13,16 של הקלט מחוברים סיבית אחר סיבית מודולו 2 (פעולת XOR) ואילו הבתים 2,3,6,7,10,11,14,15 מחוברים בחיבור רגיל מודולו 256, ובפענוח מבצעים חיסור במקום חיבור.

מבנה הפונקציה הפנימית

בדומה לפונקציות הקודמות במשפחה, הפונקציה הפנימית מורכבת מפעולת חיבור עם המפתח הסודי, פעולת החלפה הנשלטת על ידי מפתח ההצפנה ואחריה טרנספורמציה ליניארית הפיכה. הפעולה הראשונה בסבב ה- היא חיבור של הקלט עם מפתח הסבב הראשון  ברמה של בתים. הבתים 1,4,5,8,9,12,13,16 מחוברים ב-XOR והבתים 2,3,6,7,10,11,14,15 מחוברים בחיבור רגיל מודולו 256. התוצאה עוברת בשכבה האי-ליניארית המורכבת משני סוגי החלפה, הראשונה היא המרה של הערך  של הבית ה- ל- מודולו 257. כאשר . והמוסכמה היא שהתוצאה של  צריכה להיות מודולו 256, כלומר אם התוצאה היא 256 היא מאופסת. במילים אחרות  ולא 256. ההחלפה השנייה היא המרה של הערך  של הבית ה- ללוגריתם בדידי בבסיס 45 של  עבור . במילים אחרות תוצאת הפונקציה על הערך  היא הערך  כך שמתקיים . בשלב זה מחברים לתוצאה האחרונה את תת-המפתח השני של הסבב הנוכחי , אך הפעם בסדר הפוך, הבתים 2,3,6,7,10,11,14,15 מחוברים ב-XOR והבתים 1,4,5,8,9,12,13,16 מחוברים מודולו 256. התוצאה בשלב זה מועברת בטרנספורמציה הליניארית הפיכה להוספת פיזור.

קוד לדוגמה

להלן קוד C++‎ שלם של אלגוריתם SAFER+‎ כפי שאומץ בתקן בלוטות'. הקוד אינו אופטימלי ונותר כך למען הפשטות והבהירות. קובץ הכותר SAFER.h מכיל את השורות הבאות:
typedef unsigned char BYTE; /* an 8 bit unsigned character type */typedef unsigned int  UINT; /* a 32 bit unsigned integer type   */void set_key(const UINT in_key[], const UINT key_len);void encrypt(const UINT in_blk[4], UINT out_blk[4]);void decrypt(const UINT in_blk[4], UINT out_blk[4]);#define rotr(x,n)   (((x) >> ((int)(n))) | ((x) << (32 - (int)(n))))#define rotl(x,n)   (((x) << ((int)(n))) | ((x) >> (32 - (int)(n))))#define get_block(x)                   \    ((UINT*)(x))[0] = in_blk[0];     \    ((UINT*)(x))[1] = in_blk[1];     \    ((UINT*)(x))[2] = in_blk[2];     \    ((UINT*)(x))[3] = in_blk[3]#define put_block(x)                   \    out_blk[0] = ((UINT*)(x))[0];    \    out_blk[1] = ((UINT*)(x))[1];    \    out_blk[2] = ((UINT*)(x))[2];    \    out_blk[3] = ((UINT*)(x))[3]#define get_key(x,len)                          \    ((UINT*)(x))[4] = ((UINT*)(x))[5] =     \    ((UINT*)(x))[6] = ((UINT*)(x))[7] = 0;  \    switch((((len) + 63) / 64)) {               \    case 4:                                     \    ((UINT*)(x))[6] = in_key[6];     \    ((UINT*)(x))[7] = in_key[7];     \    case 3:                            \    ((UINT*)(x))[4] = in_key[4];     \    ((UINT*)(x))[5] = in_key[5];     \    case 2:                            \    ((UINT*)(x))[0] = in_key[0];     \    ((UINT*)(x))[1] = in_key[1];     \    ((UINT*)(x))[2] = in_key[2];     \    ((UINT*)(x))[3] = in_key[3];     \    }
הקוד בקובץ SAFER.cpp הוא:
#include "SAFER.h"
BYTE  expf[256] = {
 1,  45, 226, 147, 190,  69,  21, 174, 120,   3, 135, 164, 184,  56, 207,  63,
     8, 103,   9, 148, 235,  38, 168, 107, 189,  24,  52,  27, 187, 191, 114, 247,
    64,  53,  72, 156,  81,  47,  59,  85, 227, 192, 159, 216, 211, 243, 141, 177,
   255, 167,  62, 220, 134, 119, 215, 166,  17, 251, 244, 186, 146, 145, 100, 131,
   241,  51, 239, 218,  44, 181, 178,  43, 136, 209, 153, 203, 140, 132,  29,  20,
   129, 151, 113, 202,  95, 163, 139,  87,  60, 130, 196,  82,  92,  28, 232, 160,
     4, 180, 133,  74, 246,  19,  84, 182, 223,  12,  26, 142, 222, 224,  57, 252,
    32, 155,  36,  78, 169, 152, 158, 171, 242,  96, 208, 108, 234, 250, 199, 217,
     0, 212,  31, 110,  67, 188, 236,  83, 137, 254, 122,  93,  73, 201,  50, 194,
   249, 154, 248, 109,  22, 219,  89, 150,  68, 233, 205, 230,  70,  66, 143,  10,
   193, 204, 185, 101, 176, 210, 198, 172,  30,  65,  98,  41,  46,  14, 116,  80,
     2,  90, 195,  37, 123, 138,  42,  91, 240,   6,  13,  71, 111, 112, 157, 126,
    16, 206,  18,  39, 213,  76,  79, 214, 121,  48, 104,  54, 117, 125, 228, 237,
   128, 106, 144,  55, 162,  94, 118, 170, 197, 127,  61, 175, 165, 229,  25,  97,
   253,  77, 124, 183,  11, 238, 173,  75,  34, 245, 231, 115,  35,  33, 200,   5,
   225, 102, 221, 179,  88, 105,  99,  86,  15, 161,  49, 149,  23,   7,  58,  40
};

BYTE logf[512] = {
128,   0, 176,   9,  96, 239, 185, 253,  16,  18, 159, 228, 105, 186, 173, 248,
192,  56, 194, 101,  79,   6, 148, 252,  25, 222, 106,  27,  93,  78, 168, 130,
112, 237, 232, 236, 114, 179,  21, 195, 255, 171, 182,  71,  68,   1, 172,  37,
201, 250, 142,  65,  26,  33, 203, 211,  13, 110, 254,  38,  88, 218,  50,  15,
 32, 169, 157, 132, 152,   5, 156, 187,  34, 140,  99, 231, 197, 225, 115, 198,
175,  36,  91, 135, 102,  39, 247,  87, 244, 150, 177, 183,  92, 139, 213,  84,
121, 223, 170, 246,  62, 163, 241,  17, 202, 245, 209,  23, 123, 147, 131, 188,
189,  82,  30, 235, 174, 204, 214,  53,   8, 200, 138, 180, 226, 205, 191, 217,
208,  80,  89,  63,  77,  98,  52,  10,  72, 136, 181,  86,  76,  46, 107, 158,
210,  61,  60,   3,  19, 251, 151,  81, 117,  74, 145, 113,  35, 190, 118,  42,
 95, 249, 212,  85,  11, 220,  55,  49,  22, 116, 215, 119, 167, 230,   7, 219,
164,  47,  70, 243,  97,  69, 103, 227,  12, 162,  59,  28, 133,  24,   4,  29,
 41, 160, 143, 178,  90, 216, 166, 126, 238, 141,  83,  75, 161, 154, 193,  14,
122,  73, 165,  44, 129, 196, 199,  54,  43, 127,  67, 149,  51, 242, 108, 104,
109, 240,   2,  40, 206, 221, 155, 234,  94, 153, 124,  20, 134, 207, 229,  66,
184,  64, 120,  45,  58, 233, 100,  31, 146, 144, 125,  57, 111, 224, 137,  48,
128,   0, 176,   9,  96, 239, 185, 253,  16,  18, 159, 228, 105, 186, 173, 248,
192,  56, 194, 101,  79,   6, 148, 252,  25, 222, 106,  27,  93,  78, 168, 130,
112, 237, 232, 236, 114, 179,  21, 195, 255, 171, 182,  71,  68,   1, 172,  37,
201, 250, 142,  65,  26,  33, 203, 211,  13, 110, 254,  38,  88, 218,  50,  15,
 32, 169, 157, 132, 152,   5, 156, 187,  34, 140,  99, 231, 197, 225, 115, 198,
175,  36,  91, 135, 102,  39, 247,  87, 244, 150, 177, 183,  92, 139, 213,  84,
121, 223, 170, 246,  62, 163, 241,  17, 202, 245, 209,  23, 123, 147, 131, 188,
189,  82,  30, 235, 174, 204, 214,  53,   8, 200, 138, 180, 226, 205, 191, 217,
208,  80,  89,  63,  77,  98,  52,  10,  72, 136, 181,  86,  76,  46, 107, 158,
210,  61,  60,   3,  19, 251, 151,  81, 117,  74, 145, 113,  35, 190, 118,  42,
 95, 249, 212,  85,  11, 220,  55,  49,  22, 116, 215, 119, 167, 230,   7, 219,
164,  47,  70, 243,  97,  69, 103, 227,  12, 162,  59,  28, 133,  24,   4,  29,
 41, 160, 143, 178,  90, 216, 166, 126, 238, 141,  83,  75, 161, 154, 193,  14,
122,  73, 165,  44, 129, 196, 199,  54,  43, 127,  67, 149,  51, 242, 108, 104,
109, 240,   2,  40, 206, 221, 155, 234,  94, 153, 124,  20, 134, 207, 229,  66,
184,  64, 120,  45,  58, 233, 100,  31, 146, 144, 125,  57, 111, 224, 137,  48
};

BYTE  l_key[33 * 16];
UINT  k_bytes;

void set_key(const UINT in_key[], const UINT key_len)
{
    BYTE  by, lk[33];
UINT  i, j, k, l, m;

get_key(lk, key_len);
k_bytes = key_len / 8; lk[k_bytes] = 0;

for (i = 0; i < k_bytes; ++i){
lk[k_bytes] ^= lk[i]; l_key[i] = lk[i];
}

for (i = 0; i < k_bytes; ++i){
for (j = 0; j <= k_bytes; ++j){
by = lk[j]; lk[j] = by << 3 | by >> 5;
}
k = 17 * i + 35; l = 16 * i + 16; m = i + 1;

if (i < 16){
for (j = 0; j < 16; ++j){
l_key[l + j] = lk[m] + expf[expf[(k + j) & 255]];

m = (m == k_bytes ? 0 : m + 1);
}
}else{
for (j = 0; j < 16; ++j){
l_key[l + j] = lk[m] + expf[(k + j) & 255];

m = (m == k_bytes ? 0 : m + 1);
}
}
}
};

void do_forward_round(BYTE x[16], BYTE *kp)
{
    BYTE  t;

x[0] = expf[x[0] ^ kp[0]] + kp[16];
x[1] = logf[x[1] + kp[1]] ^ kp[17];
x[2] = logf[x[2] + kp[2]] ^ kp[18];
x[3] = expf[x[3] ^ kp[3]] + kp[19];

x[4] = expf[x[4] ^ kp[4]] + kp[20];
x[5] = logf[x[5] + kp[5]] ^ kp[21];
x[6] = logf[x[6] + kp[6]] ^ kp[22];
x[7] = expf[x[7] ^ kp[7]] + kp[23];

x[8] = expf[x[8] ^ kp[8]] + kp[24];
x[9] = logf[x[9] + kp[9]] ^ kp[25];
x[10] = logf[x[10] + kp[10]] ^ kp[26];
x[11] = expf[x[11] ^ kp[11]] + kp[27];

x[12] = expf[x[12] ^ kp[12]] + kp[28];
x[13] = logf[x[13] + kp[13]] ^ kp[29];
x[14] = logf[x[14] + kp[14]] ^ kp[30];
x[15] = expf[x[15] ^ kp[15]] + kp[31];

x[1] += x[0]; x[0] += x[1];
x[3] += x[2]; x[2] += x[3];
x[5] += x[4]; x[4] += x[5];
x[7] += x[6]; x[6] += x[7];
x[9] += x[8]; x[8] += x[9];
x[11] += x[10]; x[10] += x[11];
x[13] += x[12]; x[12] += x[13];
x[15] += x[14]; x[14] += x[15];

x[7] += x[0]; x[0] += x[7];
x[1] += x[2]; x[2] += x[1];
x[3] += x[4]; x[4] += x[3];
x[5] += x[6]; x[6] += x[5];
x[11] += x[8]; x[8] += x[11];
x[9] += x[10]; x[10] += x[9];
x[15] += x[12]; x[12] += x[15];
x[13] += x[14]; x[14] += x[13];

x[3] += x[0]; x[0] += x[3];
x[15] += x[2]; x[2] += x[15];
x[7] += x[4]; x[4] += x[7];
x[1] += x[6]; x[6] += x[1];
x[5] += x[8]; x[8] += x[5];
x[13] += x[10]; x[10] += x[13];
x[11] += x[12]; x[12] += x[11];
x[9] += x[14]; x[14] += x[9];

x[13] += x[0]; x[0] += x[13];
x[5] += x[2]; x[2] += x[5];
x[9] += x[4]; x[4] += x[9];
x[11] += x[6]; x[6] += x[11];
x[15] += x[8]; x[8] += x[15];
x[1] += x[10]; x[10] += x[1];
x[3] += x[12]; x[12] += x[3];
x[7] += x[14]; x[14] += x[7];

t = x[0]; x[0] = x[14]; x[14] = x[12]; x[12] = x[10]; x[10] = x[2];
x[2] = x[8]; x[8] = x[4]; x[4] = t;

t = x[1]; x[1] = x[7]; x[7] = x[11]; x[11] = x[5]; x[5] = x[13]; x[13] = t;
t = x[15]; x[15] = x[3]; x[3] = t;
};

void do_inverse_round(BYTE x[16], BYTE *kp)
{
    BYTE  t;

t = x[3]; x[3] = x[15]; x[15] = t;
t = x[13]; x[13] = x[5]; x[5] = x[11]; x[11] = x[7]; x[7] = x[1]; x[1] = t;
t = x[4]; x[4] = x[8]; x[8] = x[2]; x[2] = x[10];
x[10] = x[12]; x[12] = x[14]; x[14] = x[0]; x[0] = t;

x[14] -= x[7]; x[7] -= x[14];
x[12] -= x[3]; x[3] -= x[12];
x[10] -= x[1]; x[1] -= x[10];
x[8] -= x[15]; x[15] -= x[8];
x[6] -= x[11]; x[11] -= x[6];
x[4] -= x[9]; x[9] -= x[4];
x[2] -= x[5]; x[5] -= x[2];
x[0] -= x[13]; x[13] -= x[0];

x[14] -= x[9]; x[9] -= x[14];
x[12] -= x[11]; x[11] -= x[12];
x[10] -= x[13]; x[13] -= x[10];
x[8] -= x[5]; x[5] -= x[8];
x[6] -= x[1]; x[1] -= x[6];
x[4] -= x[7]; x[7] -= x[4];
x[2] -= x[15]; x[15] -= x[2];
x[0] -= x[3]; x[3] -= x[0];

x[14] -= x[13]; x[13] -= x[14];
x[12] -= x[15]; x[15] -= x[12];
x[10] -= x[9]; x[9] -= x[10];
x[8] -= x[11]; x[11] -= x[8];
x[6] -= x[5]; x[5] -= x[6];
x[4] -= x[3]; x[3] -= x[4];
x[2] -= x[1]; x[1] -= x[2];
x[0] -= x[7]; x[7] -= x[0];

x[14] -= x[15]; x[15] -= x[14];
x[12] -= x[13]; x[13] -= x[12];
x[10] -= x[11]; x[11] -= x[10];
x[8] -= x[9]; x[9] -= x[8];
x[6] -= x[7]; x[7] -= x[6];
x[4] -= x[5]; x[5] -= x[4];
x[2] -= x[3]; x[3] -= x[2];
x[0] -= x[1]; x[1] -= x[0];

x[0] = logf[x[0] - kp[16] + 256] ^ kp[0];
x[1] = expf[x[1] ^ kp[17]] - kp[1];
x[2] = expf[x[2] ^ kp[18]] - kp[2];
x[3] = logf[x[3] - kp[19] + 256] ^ kp[3];

x[4] = logf[x[4] - kp[20] + 256] ^ kp[4];
x[5] = expf[x[5] ^ kp[21]] - kp[5];
x[6] = expf[x[6] ^ kp[22]] - kp[6];
x[7] = logf[x[7] - kp[23] + 256] ^ kp[7];

x[8] = logf[x[8] - kp[24] + 256] ^ kp[8];
x[9] = expf[x[9] ^ kp[25]] - kp[9];
x[10] = expf[x[10] ^ kp[26]] - kp[10];
x[11] = logf[x[11] - kp[27] + 256] ^ kp[11];

x[12] = logf[x[12] - kp[28] + 256] ^ kp[12];
x[13] = expf[x[13] ^ kp[29]] - kp[13];
x[14] = expf[x[14] ^ kp[30]] - kp[14];
x[15] = logf[x[15] - kp[31] + 256] ^ kp[15];
};

void encrypt(const UINT in_blk[4], UINT out_blk[4])
{
BYTE  blk[16], *kp;

get_block(blk);

do_forward_round(blk, l_key);       do_forward_round(blk, l_key + 32);
do_forward_round(blk, l_key + 64);  do_forward_round(blk, l_key + 96);
do_forward_round(blk, l_key + 128); do_forward_round(blk, l_key + 160);
do_forward_round(blk, l_key + 192); do_forward_round(blk, l_key + 224);

if (k_bytes > 16){
do_forward_round(blk, l_key + 256);
                do_forward_round(blk, l_key + 288);
do_forward_round(blk, l_key + 320);
                do_forward_round(blk, l_key + 352);
}

if (k_bytes > 24){
do_forward_round(blk, l_key + 384);
                do_forward_round(blk, l_key + 416);
do_forward_round(blk, l_key + 448);
                do_forward_round(blk, l_key + 480);
}

kp = l_key + 16 * k_bytes;

blk[0] ^= kp[0]; blk[1] += kp[1];
blk[2] += kp[2]; blk[3] ^= kp[3];
blk[4] ^= kp[4]; blk[5] += kp[5];
blk[6] += kp[6]; blk[7] ^= kp[7];
blk[8] ^= kp[8]; blk[9] += kp[9];
blk[10] += kp[10]; blk[11] ^= kp[11];
blk[12] ^= kp[12]; blk[13] += kp[13];
blk[14] += kp[14]; blk[15] ^= kp[15];

put_block(blk);
};

void decrypt(const UINT in_blk[4], UINT out_blk[4])
{
BYTE  blk[16], *kp;

get_block(blk);
kp = l_key + 16 * k_bytes;

blk[0] ^= kp[0]; blk[1] -= kp[1];
blk[2] -= kp[2]; blk[3] ^= kp[3];
blk[4] ^= kp[4]; blk[5] -= kp[5];
blk[6] -= kp[6]; blk[7] ^= kp[7];
blk[8] ^= kp[8]; blk[9] -= kp[9];
blk[10] -= kp[10]; blk[11] ^= kp[11];
blk[12] ^= kp[12]; blk[13] -= kp[13];
blk[14] -= kp[14]; blk[15] ^= kp[15];

if (k_bytes > 24){
do_inverse_round(blk, l_key + 480); do_inverse_round(blk, l_key + 448);
do_inverse_round(blk, l_key + 416); do_inverse_round(blk, l_key + 384);
}

if (k_bytes > 16){
do_inverse_round(blk, l_key + 352); do_inverse_round(blk, l_key + 320);
do_inverse_round(blk, l_key + 288); do_inverse_round(blk, l_key + 256);
}

do_inverse_round(blk, l_key + 224); do_inverse_round(blk, l_key + 192);
do_inverse_round(blk, l_key + 160); do_inverse_round(blk, l_key + 128);
do_inverse_round(blk, l_key + 96);  do_inverse_round(blk, l_key + 64);
do_inverse_round(blk, l_key + 32);  do_inverse_round(blk, l_key);

put_block(blk);
};