HC-128

HC-128 הוא צופן זרם סינכרוני מותאם במיוחד לתוכנה שפותח ב-2004 על ידי Hongjun Wu אוניברסיטת לוון, הוצע לפרויקט eSTREAM ונבחר כצופן זרם מועדף בקטגוריית תוכנה יחד עם מבחר אלגוריתמים נוספים. הצופן מקבל מפתח בגודל 128 סיביות ווקטור אתחול בגודל 128 סיביות. המצב הפנימי (internal state) של הצופן מורכב משתי טבלאות כל אחת בגודל 512 אוגרים בגודל 32 סיביות. בכל צעד, אוגר אחד באחת הטבלאות מעודכן באמצעות פונקציית הזנה אי-ליניארית והפלט בגודל 32 סיביות נוצר על ידי פונקציית סינון אי-ליניארית. אפשר לייצר זרם מפתח בגודל מרבי של  סיביות. המסר מוצפן ומפוענח באמצעות XOR עם זרם המפתח.
מפתח הצופן מציין את ההצהרות הבאות: מחזוריות הצופן היא לכל היותר . שחזור מפתח ההצפנה קשה כחיפוש ממצה. התקפת הבחנה (distinguishing attack) דורשת לפחות  סיביות מפתח. לא קיימים מפתחות הצפנה חלשים. הצופן אינו מוגן בפטנט כלשהו. מהירות ההצפנה היא בערך 3.05 מחזורי שעון לבית על מחשב פנטיום.
HC-128 הוא גרסה פשוטה של צופן HC-256 והוא פשוט ומהיר בתוכנה, מנצל את יכולות המעבד המודרני ומאפשר מקיביליות, פונקציית ההזנה ופונקציית הפלט יכולות להתבצע בו זמנית.

ביטחון

פונקציות הפלט וההזנה של הצופן הן בעלי תכונת אי-ליניאריות גבוהה, לכן התקפת קורלציה והתקפה אלגברית אינן ישימות כנגד צופן זה. בנוסף הטבלאות מתעדכנות בזמן ריצה לכן קשה לפתח יחס ליניארי כלשהו בין הקלט לפלט של תיבות ההחלפה. מצב הצופן מכיל בסך הכול 32,778 סיביות, כך שהוא מכיל מחזוריות גבוהה למדי, אם כי קשה להעריכה במדויק. בממוצע מדובר על  סיביות. כן המספר הגדול של מצבי צופן, מסכל התקפת איזון זמן/זיכרון/דטה כנגד צופן זרם.
אף על פי שפונקציית הפלט האי-ליניארית מדליפה מעט מידע בכל צעד, פונקציית ההזנה מבטיחה שלא ניתן יהיה לנצל מידע זה כדי לשחזר את המפתח הסודי. בשלב האתחול הושם דגש על פיזור טוב והשפעה של סיביות המפתח ווקטור האתחול על כל סיביות טבלאות המצב הפנימי, כדי למנוע התקפה דיפרנציאלית.

קוד לדוגמה

typedef struct { 
      uint P[512];
      uint Q[512];
      uint counter1024;     // counter1024 = i mod 1024
      uint keystreamword;} HC128_State;#define ROTR32(x,n)   ( ((x) >> (n))  | ((x) << (32 - (n))) )#define ROTL32(x,n)   ( ((x) << (n))  | ((x) >> (32 - (n))) )#define f1(x)    (ROTR32((x),7) ^ ROTR32((x),18) ^ ((x) >> 3))#define f2(x)    (ROTR32((x),17) ^ ROTR32((x),19) ^ ((x) >> 10))uint h1(HC128_State *state, uint u) {
      return state->Q[u]+state->Q[256+(u >> 16)];}uint h2(HC128_State *state, uint u) {
      return state->P[u]+state->P[256+(u >> 16)];}void OneStep(HC128_State *state) 
{
      uint i   = state->counter1024 & 0x1ff;
      uint i3  = (i - 3) & 0x1ff;
      uint i10 = (i - 10) & 0x1ff;
      uint i12 = (i - 12) & 0x1ff;
      uint i511 = (i - 511) & 0x1ff;

      if (state->counter1024 < 512) {
            state->P[i] = state->P[i] + g1(state->P[i3], state->P[i10], state->P[i511]);
            state->keystreamword = h1(state, state->P[i12]) ^ state->P[i];
            // state->P[i] = h1(state,state->P[i12]) ^ state->P[i]; // replace with this for init
      }
      else {
            state->Q[i] = state->Q[i] + g2(state->Q[i3], state->Q[i10], state->Q[i511]);
            state->keystreamword = h2(state, state->Q[i12]) ^ state->Q[i];
            // state->Q[i] = h2(state,state->Q[i12]) ^ state->Q[i]; // replace with this for init
      }
      state->counter1024 = (state->counter1024+1) & 0x3ff;}void Initialization(HC128_State *state, byte *key, byte *iv) 
{
      uint W[1024+256],i;

      for (i = 0; i < 4; i++) {
          W[i] = ((uint*)key)[i]; W[i+4] = ((uint*)key)[i];
      }
      for (i = 0; i < 4; i++) {
          W[i+8] = ((uint*)iv)[i]; W[i+12] = ((uint*)iv)[i];
      }

      for (i = 16; i < 1024+256; i++) 
         W[i] = f2(W[i-2]) + W[i-7] + f1(W[i-15]) + W[i-16]+i;

      for (i = 0; i < 512; i++)  state->P[i] = W[i+256];
      for (i = 0; i < 512; i++)  state->Q[i] = W[i+256+512];

      state->counter1024 = 0;

      for (i = 0; i < 1024; i++)  InitOneStep(state); // see comment}void EncryptMessage(HC128_State *state, byte *message, byte *ciphertext, long msglength){
      uint64 i;
      uint j;
      
      for (i = 0; (i+4) <= msglength; i += 4, message += 4, ciphertext += 4) 
      {
            OneStep(state);   
            ((uint*)ciphertext)[0] = ((uint*)message)[0] ^ state->keystreamword;   
      }
 
      if ((msglength & 3) != 0)
      {
            OneStep(state);
            for (j = 0; j < (msglength & 3); j++) {
                  *(ciphertext+j) = *(message+j) ^ ((byte*)&state->keystreamword)[j];
            }
      }    
}
הערה: הפונקציה OneStep בשלב האתחול זהה למעט שתי שורות המסומנות בהערה, יש להחליף את השורות הללו בשורות שלפניהן ולקרוא לפונקציה בהתאם InitOneStep (הפונקציה הושמטה כאן מטעמי חסכון מקום).