ARIA (צופן)

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

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

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

גרסאות והיבטי יישום

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 בשתי תיבות החלפה  ו- וכן התיבות ההופכיות שלהן  ו-. תיבות ההחלפה נתונות בארבע הטבלאות להלן. הטבלאות בנויות מ-16 שורות ו-16 עמודות, בסך הכול 256 כניסות, כל כניסה מכילה מספר באורך 8 סיביות בטווח בין 0 ל-256 (או שתי ספרות הקסדצימליות בין  ל-). הקלט לפונקציית ההחלפה הוא בית אחד המשמש כאינדקס לשורה והעמודה בטבלה, הניבל הראשון (ספרה הקסדצימלית ראשונה בטווח בין 0 ל-15) מציין עמודה ואילו הניבל השני מציין שורה. למשל אם הקלט הוא  הטבלה  תחזיר  (בבסיס הקסדצימלי) ואילו הטבלה  תחזיר  וכן  או :

הצופן עושה שימוש בתיבות ההחלפה בשני אופנים (כמתואר בתרשים לעיל). באופן הראשון הנקרא type 1 הבית הראשון מוחלף לפי התיבה הראשונה  הבית השני לפי התיבה , השלישי והרביעי לפי ההופכיות שלהן  ו- בהתאמה, החמישי והשישי שוב בתיבה הראשונה והשנייה וחוזר חלילה עד שמחליפים את כל בתי המצב. באופן השני הנקרא type 2 מבצעים את ההחלפה בסדר הפוך תחילה בתיבות ההופכיות  ו- ולאחר מכן  ו- לסירוגין, כמתואר בתרשים להלן. הפסאודו קוד הבא מתאר את פונקציות ההחלפה  ו- לפי type 1 ו-type 2 בהתאמה, הקלט הוא המצב הנוכחי  והפלט הוא המצב החדש :

שכבת פיזור
שכבת הפיזור היא פונקציה המסומנת באות A הממפה קלט באורך 16 בתים  לפלט באורך דומה  והיא מוגדרת כדלהלן:

שכבת הפיזור של ARIA תוכננה כך שהיא תהיה אינוולוציה כלומר עבור כל וקטור  מתקיים . דרך אחרת להציג את המיפוי של  היא על ידי כפל מטריצות הבא:

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

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

הכנת מפתח

תהליך ההכנה של מפתחות ההצפנה עבור כל הסבבים כולל שני שלבים: בשלב הראשון מכינים ארבעה ערכים באורך 128 סיביות כל אחד המסומנים  בהם מציבים את המפתח הסודי  (מפתח מאסטר) שהוא המפתח הסודי שסופק על ידי המשתמש, תוך שימוש ברשת פייסטל בשלושה סבבים לפי השיטה הבאה. תחילה מחלקים את המפתח הסודי לשני חצאים ימין ושמאל, כלומר מציבים את 128 הסיביות הראשונות של  במשתנה זמני  ומה שנותר מהמפתח מציבים במשתנה זמני נוסף . יתר סיביות המשתנה הזמני  מאופסות. כך שמתקבל:

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

הפונקציות  ו- הן פונקציות הסבב המתוארות לעיל כאשר הקבועים  משמשים כמפתחות הצפנה. כדי להכין את הקבועים  תחילה מחשבים את  הסיביות הראשונות של חלק השבר של  כשהן מחולקות לשלושה המשתנים. הערכים הם:

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

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

הביטוי  פירושו הזזה מעגלית ברמה של סיביות  צעדים לשמאל בגבולות משתנה באורך 32 סיביות.

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

.

הצפנה ופענוח
להצפנת בלוק טקסט קריא  באורך 16 בתים עם מפתח באורך 128 סיביות, משתמשים ב-13 מפתחות הסבבים  מתהליך הכנת המפתחות לעיל ומבצעים:

 הוא הטקסט המוצפן. הפונקציות  ו- הן פונקציות הסבב המתוארות לעיל. בסבב האחרון הושמטה פונקציית הפיזור A ובמקומה מבוצע חיבור עם המפתח ה-13. במקרה של מפתחות אחרים המבנה נותר זהה ורק מספר הסבבים משתנה. פונקציית הפענוח של ARIA זהה לחלוטין לפונקציית ההצפנה למעט מפתחות הסבבים המוחלפים ב- אותם מכינים ממפתחות ההצפנה כמתואר לעיל.

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

קוד לדוגמה
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

הערות שוליים

צפני בלוקים