ARIA (צופן)

בקריפטוגרפיה, **ARIA** הוא צופן בלוקים סימטרי שפותח ב-2004 על ידי קבוצת חוקרים ב-NSRI עבור המשרד לתקנים וטכנולוגיה של דרום קוריאה (KATS), החבר בגופי התקינה הבינלאומיים ISO ו-IEC.

האלגוריתם בנוי מרשת החלפה-תמורה אינוולוציונית ולמעשה המפרט והממשק שלו דומים במידה רבה ל-AES. הוא מקבל בלוק טקסט קריא באורך 128 סיביות ומפתח הצפנה בשלושה אורכים אפשריים: 128, 192 או 256 סיביות ומפיק מהטקסט הקריא בלוק מוצפן באורך 128 סיביות. הוא עושה שימוש בתיבות החלפה (Sbox) בגודל 8\times 8 סיביות המבוססות על תיבות ההחלפה של ריינדל והוא פועל ב-12, 14, או 16 סבבים בהתאם לאורך המפתח. תהליך הכנת מפתח ההצפנה מרחיב את המפתח המסופק על ידי המשתמש באמצעות רשת פייסטל משולשת בתוספת קבועים שהם הייצוג הבינארי של \pi^{-1} (זאת במטרה להוכיח שאינו מכיל דלתות סתר).

האלגוריתם הפך לתקן רשמי של KATS וכן IETF, הוצע בטיוטה  וכן הוצע כתוסף לפרוטוקול SSL/TLS בטיוטה . ביטחונו נחקר היטב על ידי מומחי הצפנה, ביניהם ניתן למנות כמה מחקרים שמראים שהצופן בטוח לשימוש בגרסה העדכנית שלו ולא נמצאו בו פגמים רציניים.

## גרסאות והיבטי יישום
400px|ממוזער|שמאל|תרשים כללי של צופן ARIA
ARIA 0.8 הוצע לראשונה ב-2003 בוועידה השנתית לאבטחת מידע של דרום קוריאה. בתחילה הצופן הכיל רק 10, 12, או 14 סבבים בהתאם לאורך המפתח 128/192/256 והכיל רק שתי תיבות החלפה בהן נעשה שימוש לסירוגין. בגרסה 0.9 שהוצעה ב-ICIS 2003 נוספו שתי תיבות החלפה. גרסה 1.0 הנוכחית הוכרזה והופצה על ידי NSRI ב-2004. מספר הסבבים עלה ל-12/14/16 בהתאמה וכן בוצעו שינויים בתהליך הרחבת המפתח. בדצמבר 2004 הכניס מכון התקנים של דרום קוריאה את הצופן לתקן KS X 1213.

ARIA מתאים ליישום בחומרת 8 ביט וכן ב-32 ביט הן בתוכנה והן בחומרה. רוב הפעולות בצופן הן פעולות החלפה פשוטות אותן אפשר לבצע ביעילות עם טבלאות חיפוש (lookup table). אם הזיכרון זמין ניתן להמיר את שכבת הפיזור להכפלה בארבע מטריצות וכן אפשר לשלב את שכבת ההחלפה יחד עם שכבת הפיזור באופן יעיל ולהשיג בכך שיפור משמעותי בתפוקת הצופן. באופן כללי תפוקת ARIA דומה לזו של AES וטובה מקמליה.

## תיאור כללי
הצופן בנוי מרשת החלפה תמורה המבצעת סדרה של פעולות הפיכות על בלוק קלט באורך 128 סיביות הנקרא "מצב" (state) באופן אחיד. הפעולות הן ברמה של בתים והמצב הפנימי מיוצג כמערך המכיל 16 בתים. הצופן מבצע את פעולות המצב באופן חוזר ונשנה 12, 14 או 16 פעמים בהתאם לאורך המפתח. עבור מפתח 128 סיביות מבוצעים בסך הכול 12 סבבים, עבור מפתח באורך 192 סיביות מבוצעים בסך הכול 14 סבבים ואילו עבור מפתח באורך 256 סיביות מבוצעים 16 סבבים. המצב הפנימי מאותחל בתחילה עם סיביות הקלט, בכל סבב הצופן מעדכן את המצב והפלט הסופי הוא המצב האחרון לאחר כל הסבבים. פעולות הצופן מחולקות לשלושה מרכיבים:
***הוספת מפתח הסבב**. בשלב זה קטע מהמפתח המורחב המתאים לסבב הנוכחי מחובר ב-XOR עם המצב הפנימי והתוצאה נשמרת כמצב החדש.
***שכבת החלפה**. בשכבת ההחלפה המצב הפנימי מוחלף בית אחר בית לפי טבלאות החלפה קבועות. ישנן ארבע תיבות החלפה שבהן משתמשים לסירוגין לפי סדר המתואר בהמשך.
***שכבת פיזור**. בשכבת הפיזור המצב הפנימי מוכפל במטריצה בינארית הפיכה קבועה מסדר 16x16 סיביות.
כמו כן פעולת הרחבת המפתח מקבלת את המפתח הסודי המסופק על ידי המשתמש ומרחיבה אותו בסך הכול ל-13, 15 או 17 מקטעים באורך 16 בתים כל אחד, בהתאם לאורך המפתח. כלומר 13 מקטעים עבור מפתח באורך 128 סיביות, 15 מקטעים עבור מפתח באורך 192 סיביות ו-17 מקטעים עבור מפתח באורך 256. הסיבה היא משום שבסבב האחרון במקום שכבת הפיזור מבוצעת שכבת הוספת המפתח פעם נוספת, ראו תרשים.

## פירוט פעולות הצופן
הפעולות הבאות מבוצעות על כל בתי המצב באופן אחיד.

### שכבת החלפה
בשכבת ההחלפה (substitution layer) משתמש ARIA בשתי תיבות החלפה S_1 ו-S_2 וכן התיבות ההופכיות שלהן S_1^{-1} ו-S_2^{-1}. תיבות ההחלפה נתונות בארבע הטבלאות להלן. הטבלאות בנויות מ-16 שורות ו-16 עמודות, בסך הכול 256 כניסות, כל כניסה מכילה מספר באורך 8 סיביות בטווח בין 0 ל-256 (או שתי ספרות הקסדצימליות בין \text{0x00} ל-\text{0xff}). הקלט לפונקציית ההחלפה הוא בית אחד המשמש כאינדקס לשורה והעמודה בטבלה, הניבל הראשון (ספרה הקסדצימלית ראשונה בטווח בין 0 ל-15) מציין עמודה ואילו הניבל השני מציין שורה. למשל אם הקלט הוא \text{0x00} הטבלה S_1 תחזיר \text{0x63} (בבסיס הקסדצימלי) ואילו הטבלה S_2 תחזיר \text{0xe2} וכן S_1^{-1}(\text{0x95})=\text{0xad} או S_2^{-1}(\text{0x48})=\text{0x67}:

הצופן עושה שימוש בתיבות ההחלפה בשני אופנים (כמתואר בתרשים לעיל). באופן הראשון הנקרא type 1 הבית הראשון מוחלף לפי התיבה הראשונה S_1 הבית השני לפי התיבה S_2, השלישי והרביעי לפי ההופכיות שלהן S_1^{-1} ו-S_2^{-1} בהתאמה, החמישי והשישי שוב בתיבה הראשונה והשנייה וחוזר חלילה עד שמחליפים את כל בתי המצב. באופן השני הנקרא type 2 מבצעים את ההחלפה בסדר הפוך תחילה בתיבות ההופכיות S_1^{-1} ו-S_2^{-1} ולאחר מכן S_1 ו-S_2 לסירוגין, כמתואר בתרשים להלן. הפסאודו קוד הבא מתאר את פונקציות ההחלפה SL_1 ו-SL_2 לפי type 1 ו-type 2 בהתאמה, הקלט הוא המצב הנוכחי X=x_0,...,x_{15} והפלט הוא המצב החדש Y=y_0,...,y_{15}:









480px|ממוזער|מרכז|סדר השימוש בתיבות ההחלפה

### שכבת פיזור
שכבת הפיזור היא פונקציה המסומנת באות **A** הממפה קלט באורך 16 בתים (x_0,x_1,...,x_{15}) לפלט באורך דומה (y_0,y_1,...,y_{15}) והיא מוגדרת כדלהלן:

שכבת הפיזור של ARIA תוכננה כך שהיא תהיה אינוולוציה כלומר עבור כל וקטור x מתקיים A(A(x))=x. דרך אחרת להציג את המיפוי של A היא על ידי כפל מטריצות הבא:
620px|ממוזער|מרכז|שכבת הפיזור של ARIA המבוצעת ככפל מטריצות

### פונקציות סבב
בכל סבב של צופן ARIA מופעלת אחת משתי הפונקציות F_e ו-F_o שהן פונקציות סבב זוגית ואי זוגית בהתאמה המבצעות את שלוש הפעולות המתוארות לפי הסדר: הוספת מפתח, החלפה ופיזור, או בניסוח רשמי: 

:F_o(P_i,ek_i) = A(SL_1(P_i \oplus ek_i))
:F_e(P_i,ek_i) = A(SL_2(P_i \oplus ek_i))

כאשר P_i הוא הבלוק הקלט של סבב i ו-A היא פונקציית הפיזור, SL_i מתייחס לשכבת ההחלפה והערכים ek_i הם מפתחות הסבב אותם מכינים לפי תיאור תהליך הכנת המפתחות להלן. ההבדל בין שתי הפונקציות הוא סדר ההחלפה, ב-F_o ההחלפה מבוצעת לפי type 1 ואילו F_e היא לפי type 2.

### הכנת מפתח
240px|ממוזער|שמאל|תיאור תהליך הרחבת המפתח של ARIA
תהליך ההכנה של מפתחות ההצפנה עבור כל הסבבים כולל שני שלבים: בשלב הראשון מכינים ארבעה ערכים באורך 128 סיביות כל אחד המסומנים W_0,W_1,W_2,W_3 בהם מציבים את המפתח הסודי MK (מפתח מאסטר) שהוא המפתח הסודי שסופק על ידי המשתמש, תוך שימוש ברשת פייסטל בשלושה סבבים לפי השיטה הבאה. תחילה מחלקים את המפתח הסודי לשני חצאים ימין ושמאל, כלומר מציבים את 128 הסיביות הראשונות של MK במשתנה זמני KL ומה שנותר מהמפתח מציבים במשתנה זמני נוסף KR. יתר סיביות המשתנה הזמני KR מאופסות. כך שמתקבל:

:KL \ \| \ KR = MK\ \| \ 0...0


הסימן \| מייצג שרשור. בשלב השני מיישמים את רשת פייסטל בשלושה סבבים כך:



הפונקציות F_e ו-F_o הן פונקציות הסבב המתוארות לעיל כאשר הקבועים CK_i משמשים כמפתחות הצפנה. כדי להכין את הקבועים CK_i תחילה מחשבים את 128\times 3 הסיביות הראשונות של חלק השבר של \pi^{-1} כשהן מחולקות לשלושה המשתנים. הערכים הם:

:C_1 =\text{0x517cc1b727220a94fe12abe8fa9a6ee0}
:C_2 = \text{0x6db14acc9e21c820ff28b1d5ef5de2b0}
:C_3 = \text{0xdb92371d2126e9700324977504e8c90e}


ואז הקבועים CK_i מוגדרים בהתאם לאורך המפתח, לדוגמה עבור מפתח 128 סיביות משתמשים בקבועים לפי הסדר הזה ואילו עבור מפתחות אחרים משנים את סדר הקבועים לפי הטבלה הבאה:



את מפתחות כל הסבבים מכינים על ידי שילוב של ערכי W_i ומהם מייצרים את 17 מפתחות ההצפנה והפענוח עבור כל הסבבים:

הביטוי \lll^{n} פירושו הזזה מעגלית ברמה של סיביות n צעדים לשמאל בגבולות משתנה באורך 32 סיביות.

יש לשים לב שקיים מפתח סבב נוסף מעבר למספר הסבבים. מפתח זה נועד לשימוש בסבב האחרון שבו הטרנספורמציה A מוחלפת בשכבת הוספת מפתח נוספת. בסיכומו של דבר מספר המפתחות הדרוש הוא 13, 15 או 17 בהתאם לאורך המפתח. מפתחות הפענוח נגזרים ממפתחות ההצפנה כדלהלן:


:dk_1=ek_{n-1},dk_2=A(ek_n),dk_3=A(ek_{n-1}),...,dk_n=A(ek_2),dk_{n+1}=ek_1.


### הצפנה ופענוח
להצפנת בלוק טקסט קריא P באורך 16 בתים עם מפתח באורך 128 סיביות, משתמשים ב-13 מפתחות הסבבים ek_1,ek_2,...,ek_{13} מתהליך הכנת המפתחות לעיל ומבצעים:




C הוא הטקסט המוצפן. הפונקציות F_e ו-F_o הן פונקציות הסבב המתוארות לעיל. בסבב האחרון הושמטה פונקציית הפיזור A ובמקומה מבוצע חיבור עם המפתח ה-13. במקרה של מפתחות אחרים המבנה נותר זהה ורק מספר הסבבים משתנה. פונקציית הפענוח של ARIA זהה לחלוטין לפונקציית ההצפנה למעט מפתחות הסבבים המוחלפים ב-dk_1,dk_2,...,dk_{13} אותם מכינים ממפתחות ההצפנה כמתואר לעיל.

### וקטור בדיקה
להלן וקטור בדיקה של הצפנה עם מפתח באורך 256 סיביות:

## קוד לדוגמה

```c++
typedef unsigned int uint;
typedef unsigned char byte;
#define ARIA_BLOCK_SIZE 16
#define MODE_ENC 0
#define MODE_DEC 1
#define SWAPINT32(x) ( \
      (((x) & 0x000000FFUL) << 24) | (((x) & 0x0000FF00UL) << 8) | \
      (((x) & 0x00FF0000UL) >> 8) |  (((x) & 0xFF000000UL) >> 24))

typedef struct
{
	uint nr;
	uint k[16];
	uint ek[68];
	uint dk[68];
	bool mode = false;
} AriaContext;

//Move operation
#define MOV128(b, a) \
    { \
       (b)[0] = (a)[0]; (b)[1] = (a)[1]; \
       (b)[2] = (a)[2]; (b)[3] = (a)[3]; \
    }

//XOR operation
#define XOR128(b, a) \
    { \
       (b)[0] ^= (a)[0]; (b)[1] ^= (a)[1]; \
       (b)[2] ^= (a)[2]; (b)[3] ^= (a)[3]; \
    }

//Rotate left operation
#define ROL128(b, a, n) \
    { \
       (b)[0] = ((a)[((n) / 32 + 0) % 4] << ((n) % 32)) | ((a)[((n) / 32 + 1) % 4] >> (32 - ((n) % 32))); \
       (b)[1] = ((a)[((n) / 32 + 1) % 4] << ((n) % 32)) | ((a)[((n) / 32 + 2) % 4] >> (32 - ((n) % 32))); \
       (b)[2] = ((a)[((n) / 32 + 2) % 4] << ((n) % 32)) | ((a)[((n) / 32 + 3) % 4] >> (32 - ((n) % 32))); \
       (b)[3] = ((a)[((n) / 32 + 3) % 4] << ((n) % 32)) | ((a)[((n) / 32 + 0) % 4] >> (32 - ((n) % 32))); \
    }

//Substitution layer SL1
#define SL1(b, a) \
    { \
       byte *x = (byte *) (a); \
       byte *y = (byte *) (b); \
       y[0] = sb1[x[0]];     y[1] = sb2[x[1]]; \
       y[2] = sb3[x[2]];     y[3] = sb4[x[3]]; \
       y[4] = sb1[x[4]];     y[5] = sb2[x[5]]; \
       y[6] = sb3[x[6]];     y[7] = sb4[x[7]]; \
       y[8] = sb1[x[8]];     y[9] = sb2[x[9]]; \
       y[10] = sb3[x[10]];   y[11] = sb4[x[11]]; \
       y[12] = sb1[x[12]];   y[13] = sb2[x[13]]; \
       y[14] = sb3[x[14]];   y[15] = sb4[x[15]]; \
    }

//Substitution layer SL2
#define SL2(b, a) \
    { \
      byte *x = (byte *) (a); \
      byte *y = (byte *) (b); \
      y[0] = sb3[x[0]];       y[1] = sb4[x[1]]; \
      y[2] = sb1[x[2]];       y[3] = sb2[x[3]]; \
      y[4] = sb3[x[4]];       y[5] = sb4[x[5]]; \
      y[6] = sb1[x[6]];       y[7] = sb2[x[7]]; \
      y[8] = sb3[x[8]];       y[9] = sb4[x[9]]; \
      y[10] = sb1[x[10]];     y[11] = sb2[x[11]]; \
      y[12] = sb3[x[12]];     y[13] = sb4[x[13]]; \
      y[14] = sb1[x[14]];     y[15] = sb2[x[15]]; \
   }

//Diffusion layer
#define A(b, a) \
   { \
      byte *x = (byte *) (a); \
      byte *y = (byte *) (b); \
      y[0] = x[3] ^ x[4] ^ x[6] ^ x[8] ^ x[9] ^ x[13] ^ x[14]; \
      y[1] = x[2] ^ x[5] ^ x[7] ^ x[8] ^ x[9] ^ x[12] ^ x[15]; \
      y[2] = x[1] ^ x[4] ^ x[6] ^ x[10] ^ x[11] ^ x[12] ^ x[15]; \
      y[3] = x[0] ^ x[5] ^ x[7] ^ x[10] ^ x[11] ^ x[13] ^ x[14]; \
      y[4] = x[0] ^ x[2] ^ x[5] ^ x[8] ^ x[11] ^ x[14] ^ x[15]; \
      y[5] = x[1] ^ x[3] ^ x[4] ^ x[9] ^ x[10] ^ x[14] ^ x[15]; \
      y[6] = x[0] ^ x[2] ^ x[7] ^ x[9] ^ x[10] ^ x[12] ^ x[13]; \
      y[7] = x[1] ^ x[3] ^ x[6] ^ x[8] ^ x[11] ^ x[12] ^ x[13]; \
      y[8] = x[0] ^ x[1] ^ x[4] ^ x[7] ^ x[10] ^ x[13] ^ x[15]; \
      y[9] = x[0] ^ x[1] ^ x[5] ^ x[6] ^ x[11] ^ x[12] ^ x[14]; \
      y[10] = x[2] ^ x[3] ^ x[5] ^ x[6] ^ x[8] ^ x[13] ^ x[15]; \
      y[11] = x[2] ^ x[3] ^ x[4] ^ x[7] ^ x[9] ^ x[12] ^ x[14]; \
      y[12] = x[1] ^ x[2] ^ x[6] ^ x[7] ^ x[9] ^ x[11] ^ x[12]; \
      y[13] = x[0] ^ x[3] ^ x[6] ^ x[7] ^ x[8] ^ x[10] ^ x[13]; \
      y[14] = x[0] ^ x[3] ^ x[4] ^ x[5] ^ x[9] ^ x[11] ^ x[14]; \
      y[15] = x[1] ^ x[2] ^ x[4] ^ x[5] ^ x[8] ^ x[10] ^ x[15]; \
   }

//S-box 1
static const byte sb1[256] =
{
	0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
	0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
	0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
	0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
	0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
	0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
	0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
	0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
	0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
	0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
	0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
	0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
	0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
	0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
	0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
	0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16
};

//S-box 2
static const byte sb2[256] =
{
	0xE2, 0x4E, 0x54, 0xFC, 0x94, 0xC2, 0x4A, 0xCC, 0x62, 0x0D, 0x6A, 0x46, 0x3C, 0x4D, 0x8B, 0xD1,
	0x5E, 0xFA, 0x64, 0xCB, 0xB4, 0x97, 0xBE, 0x2B, 0xBC, 0x77, 0x2E, 0x03, 0xD3, 0x19, 0x59, 0xC1,
	0x1D, 0x06, 0x41, 0x6B, 0x55, 0xF0, 0x99, 0x69, 0xEA, 0x9C, 0x18, 0xAE, 0x63, 0xDF, 0xE7, 0xBB,
	0x00, 0x73, 0x66, 0xFB, 0x96, 0x4C, 0x85, 0xE4, 0x3A, 0x09, 0x45, 0xAA, 0x0F, 0xEE, 0x10, 0xEB,
	0x2D, 0x7F, 0xF4, 0x29, 0xAC, 0xCF, 0xAD, 0x91, 0x8D, 0x78, 0xC8, 0x95, 0xF9, 0x2F, 0xCE, 0xCD,
	0x08, 0x7A, 0x88, 0x38, 0x5C, 0x83, 0x2A, 0x28, 0x47, 0xDB, 0xB8, 0xC7, 0x93, 0xA4, 0x12, 0x53,
	0xFF, 0x87, 0x0E, 0x31, 0x36, 0x21, 0x58, 0x48, 0x01, 0x8E, 0x37, 0x74, 0x32, 0xCA, 0xE9, 0xB1,
	0xB7, 0xAB, 0x0C, 0xD7, 0xC4, 0x56, 0x42, 0x26, 0x07, 0x98, 0x60, 0xD9, 0xB6, 0xB9, 0x11, 0x40,
	0xEC, 0x20, 0x8C, 0xBD, 0xA0, 0xC9, 0x84, 0x04, 0x49, 0x23, 0xF1, 0x4F, 0x50, 0x1F, 0x13, 0xDC,
	0xD8, 0xC0, 0x9E, 0x57, 0xE3, 0xC3, 0x7B, 0x65, 0x3B, 0x02, 0x8F, 0x3E, 0xE8, 0x25, 0x92, 0xE5,
	0x15, 0xDD, 0xFD, 0x17, 0xA9, 0xBF, 0xD4, 0x9A, 0x7E, 0xC5, 0x39, 0x67, 0xFE, 0x76, 0x9D, 0x43,
	0xA7, 0xE1, 0xD0, 0xF5, 0x68, 0xF2, 0x1B, 0x34, 0x70, 0x05, 0xA3, 0x8A, 0xD5, 0x79, 0x86, 0xA8,
	0x30, 0xC6, 0x51, 0x4B, 0x1E, 0xA6, 0x27, 0xF6, 0x35, 0xD2, 0x6E, 0x24, 0x16, 0x82, 0x5F, 0xDA,
	0xE6, 0x75, 0xA2, 0xEF, 0x2C, 0xB2, 0x1C, 0x9F, 0x5D, 0x6F, 0x80, 0x0A, 0x72, 0x44, 0x9B, 0x6C,
	0x90, 0x0B, 0x5B, 0x33, 0x7D, 0x5A, 0x52, 0xF3, 0x61, 0xA1, 0xF7, 0xB0, 0xD6, 0x3F, 0x7C, 0x6D,
	0xED, 0x14, 0xE0, 0xA5, 0x3D, 0x22, 0xB3, 0xF8, 0x89, 0xDE, 0x71, 0x1A, 0xAF, 0xBA, 0xB5, 0x81
};

//S-box 3
static const byte sb3[256] =
{
	0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB,
	0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB,
	0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
	0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25,
	0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92,
	0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,
	0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06,
	0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B,
	0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,
	0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E,
	0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B,
	0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,
	0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F,
	0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF,
	0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
	0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D
};

//S-box 4
static const byte sb4[256] =
{
	0x30, 0x68, 0x99, 0x1B, 0x87, 0xB9, 0x21, 0x78, 0x50, 0x39, 0xDB, 0xE1, 0x72, 0x09, 0x62, 0x3C,
	0x3E, 0x7E, 0x5E, 0x8E, 0xF1, 0xA0, 0xCC, 0xA3, 0x2A, 0x1D, 0xFB, 0xB6, 0xD6, 0x20, 0xC4, 0x8D,
	0x81, 0x65, 0xF5, 0x89, 0xCB, 0x9D, 0x77, 0xC6, 0x57, 0x43, 0x56, 0x17, 0xD4, 0x40, 0x1A, 0x4D,
	0xC0, 0x63, 0x6C, 0xE3, 0xB7, 0xC8, 0x64, 0x6A, 0x53, 0xAA, 0x38, 0x98, 0x0C, 0xF4, 0x9B, 0xED,
	0x7F, 0x22, 0x76, 0xAF, 0xDD, 0x3A, 0x0B, 0x58, 0x67, 0x88, 0x06, 0xC3, 0x35, 0x0D, 0x01, 0x8B,
	0x8C, 0xC2, 0xE6, 0x5F, 0x02, 0x24, 0x75, 0x93, 0x66, 0x1E, 0xE5, 0xE2, 0x54, 0xD8, 0x10, 0xCE,
	0x7A, 0xE8, 0x08, 0x2C, 0x12, 0x97, 0x32, 0xAB, 0xB4, 0x27, 0x0A, 0x23, 0xDF, 0xEF, 0xCA, 0xD9,
	0xB8, 0xFA, 0xDC, 0x31, 0x6B, 0xD1, 0xAD, 0x19, 0x49, 0xBD, 0x51, 0x96, 0xEE, 0xE4, 0xA8, 0x41,
	0xDA, 0xFF, 0xCD, 0x55, 0x86, 0x36, 0xBE, 0x61, 0x52, 0xF8, 0xBB, 0x0E, 0x82, 0x48, 0x69, 0x9A,
	0xE0, 0x47, 0x9E, 0x5C, 0x04, 0x4B, 0x34, 0x15, 0x79, 0x26, 0xA7, 0xDE, 0x29, 0xAE, 0x92, 0xD7,
	0x84, 0xE9, 0xD2, 0xBA, 0x5D, 0xF3, 0xC5, 0xB0, 0xBF, 0xA4, 0x3B, 0x71, 0x44, 0x46, 0x2B, 0xFC,
	0xEB, 0x6F, 0xD5, 0xF6, 0x14, 0xFE, 0x7C, 0x70, 0x5A, 0x7D, 0xFD, 0x2F, 0x18, 0x83, 0x16, 0xA5,
	0x91, 0x1F, 0x05, 0x95, 0x74, 0xA9, 0xC1, 0x5B, 0x4A, 0x85, 0x6D, 0x13, 0x07, 0x4F, 0x4E, 0x45,
	0xB2, 0x0F, 0xC9, 0x1C, 0xA6, 0xBC, 0xEC, 0x73, 0x90, 0x7B, 0xCF, 0x59, 0x8F, 0xA1, 0xF9, 0x2D,
	0xF2, 0xB1, 0x00, 0x94, 0x37, 0x9F, 0xD0, 0x2E, 0x9C, 0x6E, 0x28, 0x3F, 0x80, 0xF0, 0x3D, 0xD3,
	0x25, 0x8A, 0xB5, 0xE7, 0x42, 0xB3, 0xC7, 0xEA, 0xF7, 0x4C, 0x11, 0x33, 0x03, 0xA2, 0xAC, 0x60
};

//Key scheduling constants
static const uint c[12] =
{
	SWAPINT32(0x517CC1B7), SWAPINT32(0x27220A94), SWAPINT32(0xFE13ABE8), SWAPINT32(0xFA9A6EE0),
	SWAPINT32(0x6DB14ACC), SWAPINT32(0x9E21C820), SWAPINT32(0xFF28B1D5), SWAPINT32(0xEF5DE2B0),
	SWAPINT32(0xDB92371D), SWAPINT32(0x2126E970), SWAPINT32(0x03249775), SWAPINT32(0x04E8C90E)
};

static void OF(uint *d, const uint *rk)
{
	uint t[4];
	XOR128(d, rk);  //XOR D with RK
	SL1(t, d);      //Substitution layer SL1
	A(d, t);        //Diffusion layer
}

static void EF(uint *d, const uint *rk)
{
	uint t[4];
	XOR128(d, rk);   //XOR D with RK
	SL2(t, d);       //Substitution layer SL2
	A(d, t);         //Diffusion layer
}

int ariaInit(AriaContext *context, const byte *key, size_t keyLength)
{
	uint i;
	uint *ek, *dk;
	const uint *ck1, *ck2, *ck3;
	uint w[16];

	//128-bit master key?
	if (keyLength == 16)
	{
		ck1 = c + 0;  //Select the relevant constants
		ck2 = c + 4;
		ck3 = c + 8;
		//The number of rounds depends on the size of the master key
		context->nr = 12;
	} else if (keyLength == 24) { //192-bit master key?
	    ck1 = c + 4;  //Select the relevant constants
		ck2 = c + 8;
		ck3 = c + 0;
		//The number of rounds depends on the size of the master key
		context->nr = 14;
	} else if (keyLength == 32)	{  //256-bit master key?
		ck1 = c + 8;   //Select the relevant constants
		ck2 = c + 0;
		ck3 = c + 4;
		//The number of rounds depends on the size of the master key
		context->nr = 16;
	}
	else return -1;

	//Compute 128-bit values KL and KR
	memset(w, 0, sizeof(w));
	memcpy(w, key, keyLength);

	//Save KR...
	MOV128(w + 8, w + 4);

	//Compute intermediate values W0, W1, W2, and W3
	MOV128(w + 4, w + 0);
	OF(w + 4, ck1);
	XOR128(w + 4, w + 8);

	MOV128(w + 8, w + 4);
	EF(w + 8, ck2);
	XOR128(w + 8, w + 0);

	MOV128(w + 12, w + 8);
	OF(w + 12, ck3);
	XOR128(w + 12, w + 4);

	//Convert from big-endian byte order to host byte order
	for (i = 0; i < 16; i++)
		w[i] = SWAPINT32(w[i]);

	//Point to the encryption round keys
	ek = context->ek;

	//Compute ek1, ..., ek17 as follow
	ROL128(ek + 0, w + 4, 109);    	XOR128(ek + 0, w + 0);
	ROL128(ek + 4, w + 8, 109);  	XOR128(ek + 4, w + 4);
	ROL128(ek + 8, w + 12, 109);	XOR128(ek + 8, w + 8);
	ROL128(ek + 12, w + 0, 109);	XOR128(ek + 12, w + 12);
	ROL128(ek + 16, w + 4, 97);   	XOR128(ek + 16, w + 0);
	ROL128(ek + 20, w + 8, 97); 	XOR128(ek + 20, w + 4);
	ROL128(ek + 24, w + 12, 97);	XOR128(ek + 24, w + 8);
	ROL128(ek + 28, w + 0, 97);  	XOR128(ek + 28, w + 12);
	ROL128(ek + 32, w + 4, 61); 	XOR128(ek + 32, w + 0);
	ROL128(ek + 36, w + 8, 61); 	XOR128(ek + 36, w + 4);
	ROL128(ek + 40, w + 12, 61); 	XOR128(ek + 40, w + 8);
	ROL128(ek + 44, w + 0, 61); 	XOR128(ek + 44, w + 12);
	ROL128(ek + 48, w + 4, 31); 	XOR128(ek + 48, w + 0);
	ROL128(ek + 52, w + 8, 31); 	XOR128(ek + 52, w + 4);
	ROL128(ek + 56, w + 12, 31); 	XOR128(ek + 56, w + 8);
	ROL128(ek + 60, w + 0, 31); 	XOR128(ek + 60, w + 12);
	ROL128(ek + 64, w + 4, 19); 	XOR128(ek + 64, w + 0);

	//Convert from host byte order to big-endian byte order
	for (i = 0; i < 68; i++)
		ek[i] = SWAPINT32(ek[i]);

	//Decryption round keys are derived from the encryption round keys
	dk = context->dk;
	//Compute dk1
	MOV128(dk + 0, ek + context->nr * 4);

	//Compute dk2, ..., dk(n)
	for (i = 1; i < context->nr; i++)
		A(dk + i * 4, ek + (context->nr - i) * 4);

	//Compute dk(n + 1)
	MOV128(dk + i * 4, ek + 0);

	//No error to report
	return 0;
}


void ariaEncryptBlock(AriaContext *context, const byte *input, byte *output, bool mode)
{
	uint *ek;
	uint p[4], q[4];

	//Copy the plaintext to the buffer
	memcpy(p, input, ARIA_BLOCK_SIZE);

	//Point to the encryption/decryption round keys
	if(mode == MODE_DEC) ek = context->dk;
	else ek = context->ek;
	
	//Apply 11 rounds
	OF(p, ek + 0);     	EF(p, ek + 4);
	OF(p, ek + 8);   	EF(p, ek + 12);
	OF(p, ek + 16);  	EF(p, ek + 20);
	OF(p, ek + 24); 	EF(p, ek + 28);
	OF(p, ek + 32); 	EF(p, ek + 36);
	OF(p, ek + 40);

	//128-bit master keys require a total of 12 rounds
	if (context->nr == 12)
	{
		XOR128(p, ek + 44);
		SL2(q, p);
		XOR128(q, ek + 48);
	} else if (context->nr == 14) {  //192-bit master keys require a total of 14 rounds
		EF(p, ek + 44);
		OF(p, ek + 48);
		XOR128(p, ek + 52);
		SL2(q, p);
		XOR128(q, ek + 56);
	} else { //256-bit master keys require a total of 16 rounds
		EF(p, ek + 44);
		OF(p, ek + 48);
		EF(p, ek + 52);
		OF(p, ek + 56);
		XOR128(p, ek + 60);
		SL2(q, p);
		XOR128(q, ek + 64);
	}
	//Copy the resulting ciphertext from the buffer
	memcpy(output, q, ARIA_BLOCK_SIZE);
}
```


## ראו גם
* צופן בלוקים
* AES
* SSL

## הערות שוליים

קטגוריה:צפני בלוקים