בקריפטוגרפיה, SNOW היא משפחה של צפני זרם סינכרוניים הפועלים ברמה של מילים ומותאמים לתוכנה. הגרסה הראשונה שכעת נקראת SNOW 1.0[1] פותחה בשנת 2000 על ידי Thomas Johansson ו-Patrik Ekdahl מאוניברסיטת לונד שבשוודיה. הצופן מקבל מפתח הצפנה בשני אורכים אפשריים, 128 או 256 סיביות ומפיק בכל פעימה מילה פסאודו-אקראית אחת באורך 32 סיביות המהווה חלק מזרם המפתח. את המפתח מחברים עם זרם המידע בדרך כלל ב-XOR לקבלת זרם מוצפן. SNOW יכול להפיק לכל היותר 250 מילים פסאודו-אקראיות עם מפתח סודי אחד.

SNOW היה מועמד לתחרות שאורגנה בשנים 2000 - 2003 על ידי NESSIE ובשל העובדה שהייתה דרישה לווקטור אתחול הוסיפו המפתחים אופציה כזו בגרסה נוספת של הצופן עם מצב הפעלה הנקרא מצב IV. בסופו של דבר SNOW 1.0 כמו שישה אלגוריתמים נוספים לא התקבלו בגלל קריפטואנליזה שהעידה על בעיות. ב-2003 פיתחו בתגובה אקדל ויוהנסון את SNOW 2.0[2] שלא רק יותר בטוח אלא גם יותר מהיר בתוכנה ולמעשה נחשב בין הצפנים המהירים ביותר כיום. הוא היה מועמד לפרויקט eSTREAM ושימש כצופן ייחוס לצורך השוואת ביצועים. אולם בשל חולשות תאורטיות שהתגלו בו לא נכלל בפורטפוליו. SNOW 2.0 נכלל בתקן איזו ISO/IEC 18033-4 לצופן זרם.

ב-2006 הוחלט על ידי 3GPP להוסיף צופן חדש לתקן הצפנת שיחות GSM מהדור השלישי ומהדור הרביעי כחלק מהאלגוריתמים UEA2 ו-UIA2 להצפנה מאומתת והוחלט להתבסס על SNOW עם שינויים אחדים שנועדו לחזקו נגד קריפטואנליזה והוא נקרא SNOW 3G. הצופן חופשי לשימוש, אינו מוגן בפטנט ונחשב למהיר יותר מ-AES ולדעת המפתחים בעל רמת ביטחון דומה, אם כי מחקרים מראים שהוא פחות בטוח ממה שהוצהר.

רקע תאורטי

צופן זרם שנקרא גם מחולל פסאודו-אקראי הוא אלגוריתם המקבל מחרוזת קלט אקראית קצרה שנקראת "גרעין" או מפתח סודי ומייצר ממנו מחרוזת פסאודו-אקראית ארוכה שנקראת "זרם מפתח". אף על פי שהאלגוריתם דטרמיניסטי זרם המפתח נראה על פניו כאקראי לכל דבר ועניין לעיני יריב בעל עוצמת חישוב מוגבלת שאינו יודע מהו הגרעין ההתחלתי, מסיבה זו הוא מכונה "פסאודו" - מדומה. ניתן להגשים מחולל כזה בשיטה ישירה עם צופן בלוקים בטוח כמו AES. פשוט מצפינים בלוק מונה כלשהו באופן רקורסיבי כשהמונה מקודם בכל הצפנה, צוברים את הפלט ההצפנה וממנו מכינים מפתח באורך הרצוי, שיטה זו פחות יעילה כי צופן בלוקים אינו מותאם למטרה זו. יתרונו של צופן זרם ייעודי הוא בפשטותו, מהירותו וקלות הטמעתו הן בתוכנה והן בחומרה ובשל כך משיג ביצועים טובים בהרבה. אחת הדרכים ליצור צופן זרם מהיר הן בחומרה והן בתוכנה היא הסתרת תהליך אי-ליניארי כמו פעולת ההחלפה הפנימית של צופן בלוקים הנקראת S-box בתוך תהליך ליניארי הנקרא LFSR, כאשר פלט המחולל בכל פעימה מתקבל על ידי מיזוג פלט שני התהליכים הפועלים במקביל. זהו העיקרון של SNOW. המרכיב האי-ליניארי בנוי ממכונת מצבים הכוללת בתוכה (בגרסה השנייה) את מנגנון ההחלפה של צופן הבלוקים ריינדל וה-LFSR הוא המרכיב הליניארי המשמש בתפקיד כפול, גם להזנת מכונת המצבים וגם להסתרת הפלט שלה שמהווה בסופו של דבר פלט המחולל.

SNOW 1.0

קובץ:Snow-01.png
תיאור סכימתי של צופן SNOW 1.0

באופן כללי SNOW כולל LFSR בעל 16 "מצבים" או תאים (Taps), כל אחד באורך 32 סיביות יחד עם פונקציית היזון מתאימה והפלט שלו משמש להזנת מכונת מצבים, המסומנת בקיצור FSM, המורכבת משני אוגרי 32 סיביות R1 ו-R2 ופונקציות לחישוב הפלט והמצב הבא (כמתואר בתרשים משמאל). תחילה מאתחלים את הצופן עם המפתח הסודי המסופק על ידי המשתמש וטוענים את וקטור האתחול אם ישנו בדרך המפורטת להלן ואז בכל פעימה 32 סיביות פלט הצופן הן תוצאת חיבור XOR של פלט ה-FSM עם כניסה 16 (האחרונה) של ה-LFSR. ביתר פירוט:

ה-LFSR נוצר על ידי פולינום פרימיטיבי מעל השדה 𝔽232:

p(x)=x16+x13+x7+α1,

והיוצר של השדה 𝔽232 שנבחר הוא:

π(x)=x32+x29+x20+x15+x10+x+1 מעל 𝔽2. מתקבל שדה בינארי מורחב שכל המקדמים בו הם סיביות.

כאשר π(α)=0 כלומר α הוא שורש של הפולינום π. מתייחסים לכניסות האוגר המסומנות על ידי s1,s2,...,s16 כאל מקדמים בייצוג פולינומי לפי בסיס α לדוגמה אם y𝔽232 אזי אפשר לכתוב אותו (y31,y30,...,y1,y0) כאשר y=y31α31+y30α30++y1α+y0.

בכל פעימה של הצופן, הכניסה הראשונה ב-LFSR שהיא s1 נהיית קלט למכונת המצבים FSM שהפלט שלה הוא:

FSMout=(s1R1)R2

פלט ה-FSM מחובר ב-XOR עם הכניסה האחרונה s16 וזרם המפתח הוא: key=FSMouts16.

המצב הפנימי של מכונת המצבים מתעדכן בכל פעימה לפי:

R1=((FSMoutR2)7)R1
R2=S(R1),
R1=R1

הסמל הוא חיבור מודולו 232, הפעולה 7 היא הזזה מעגלית 7 סיביות לשמאל והסמל הוא XOR. הפונקציה S מייצגת את תהליך ההחלפה המורכב מארבע תיבות ההחלפה בגודל 8×8 סיביות ופרמוטציה. כלומר הקלט מחולק לארבעה בתים, כל אחד עובר מיפוי אי-ליניארי לפי אחת מהתיבות ולאחר מכן התוצאה מוחלפת לפי טבלת התמורה. הקלט לתיבות ההחלפה הוא w=(w7,w6,...,w0) סך הכול שמונה סיביות כאשר w7 היא הסיבית המשמעותית ביותר ואילו w0 היא הסיבית הכי פחות משמעותית. w נחשב לווקטור המייצג אלמנט בשדה 𝔽28 עם פולינום הבסיס {β7,...,β,1} שהיוצר שלו הוא פולינום אי-פריק π(x)=x8+x5+x3+x+1 והמיפוי האי ליניארי הוא:

r=w7+β2+β+1

כאשר האריתמטיקה היא בשדה 𝔽28. לאחר ההחלפה האמורה מבצעים תמורה לפי הטבלה הבאה:

313029282726252423222120191817163102024014172971318255122327

151413121110987654321018212649193121116286152230

הסיבית ה-31 (הסיבית האחרונה כשהספירה מתחילה מאפס) מוחלפת בסיבית במיקום 3 (הרביעית), הסיבית במיקום ה-30 מוחלפת בסיבית ה-10 וכן הלאה.

אתחול הצופן

במקרה של מפתח באורך 128 סיביות המפתח המסופק על ידי המשתמש מחולק לארבע מילים k1,...,k4, במקרה של מפתח באורך 256 סיביות הוא מחולק לשמונה מילים בהתאמה. וקטור האתחול באורך 64 סיביות מחולק לשתי מילים. את מפתח ההצפנה המסופק על ידי המשתמש יחד עם וקטור האתחול אם ישנו, טוענים לתוך האוגר LFSR כדלהלן:

s1=k1IV1,s2=k2,s3=k3,s4=k4IV2,
s5=k11,s6=k21,s7=k31,s8=k41,
s9=k1,s10=k2,s11=k3,s12=k4,
s13=k11,s14=k21,s15=k31,s16=k41

כאשר 1 מייצג וקטור שכולו אחדים (32 סיביות). חיבור XOR עם וקטור כזה שקול לאופרטור השלילה. אם המפתח הנבחר באורך 256 סיביות טוענים את המפתח ווקטור האתחול כך:

s1=k1IV1,s2=k2,s3=k3,s4=k4IV2,
s5=k5,s6=k6,s7=k7,s8=k8,
s9=k11,s10=k21,s11=k31,s12=k41,
s13=k51,s14=k61,s15=k71,s16=k81.

אם משתמשים עם הצופן במצב ללא וקטור אתחול ההנחה היא ש-IV1=IV2=0. בשלב זה מפעילים את הצופן בדיוק v פעמים ללא שימוש בפלט, במקום זאת הפלט מוזן בחזרה ללולאת ההיזון של ה-LFSR. במצב ההפעלה הרגיל ללא IV המשתנה v=64, במקרה של הפעלה עם IV הוא v=32. בכל פעימה כל כניסות ה-LFSR מוזזות צעד אחד לימין, כלומר הכניסה השנייה מקבלת את הערך של הכניסה הראשונה, השלישית של השנייה וכן הלאה. בשלב האתחול הערך החדש של הכניסה הראשונה הוא חיבור של הכניסות ה-16, 13 ו-7 יחד עם תוצאת מכונת המצבים:

s1=α(s7s13s16FSMout).

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

s1=α(s7s13s16).

הצפנה

לאחר שלב האתחול, הצופן מופעל פעם אחת ללא שימוש בפלט, ולאחר מכן הוא מוכן לשימוש, 32 הסיביות הבאות של המחולל הן:

FSMouts16.

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

ביטחון

בפיתוח SNOW הושם דגש על עמידות נגד התקפת קורלציה שהיא התקפה גנרית אפקטיבית נגד כל צופן זרם שעושה שימוש ב-LFSR והיא מסתמכת על רמת מתאם מסוימת בין פלט המחולל למצב הפנימי בזמן נתון. למיטב ידיעת המפתחים כמות הזיכרון לצורך התקפה מעין זו נגד SNOW היא מעבר ליכולת הטכנולוגית הקיימת. התקפה נוספת שהצליחה במיוחד נגד A5/1 היא התקפת איזון זמן/זיכרון והיא אינה אפשרית נגד SNOW בשל העובדה שהמצב הפנימי גדול מדי. התקפה אפשרית אחרת היא מסוג Guess and Determine שאיתה הצליחו לפרוץ בין היתר את A5/1. היא פועלת בעיקרון כך; מנחשים חלק מהערכים הפנימיים ומנסים למצוא קשר או יחס כלשהו עם ערכים פנימיים אחרים בעיקר בפונקציית ההיזון. ההתקפה מצליחה אם מצליחים לנחש נכונה את כל המצב הפנימי. בפברואר 2002 פרסמו Hawkes ו-Rose התקפת ניחוש קריפטוגרפית כזו[3] נגד SNOW בסיבוכיות מקום של 295 מילים ובסיבוכיות זמן של 2224 פעולות. אף על פי שההתקפה תאורטית היא מצביעה על חולשה מסוימת כיוון שהוכח שניתן לפרוץ את הצופן בזמן נמוך מכוח גס כפי שנטען על ידי המפתחים. בין היתר ההתקפה ניצלה את העובדה שמכונת המצבים מקבלת קלט יחיד. התקפה אחרת מ-2002 של Coppersmith, Halevi, Jutla שהיא סוג של התקפת הבחנה גנרית נגד צופן זרם הכולל מיסוך אי-ליניארי[4] מצליחה לשבור את הצופן בזמן של 2100 פעולות ועם 295 מילות פלט של הצופן. ההתקפה מבוססת על ההבחנה שקיימת קורלציה בפולינום ההזנה וגם היא כמובן תאורטית. בגרסה השנייה המפתחים ניסו לפתור את הבעיות הללו.

SNOW 2.0

קובץ:Snow-02.png
תיאור סכימתי של צופן SNOW 2.0

מבחינה סכמתית ההבדל בין גרסה 1 לגרסה 2 מינורי (כמתואר בתרשים משמאל). כקודמו ה-LFSR מכיל 16 כניסות באורך 32 סיביות, אך פולינום ההזנה (feedback polynomial) השתנה. כמו כן מכונת המצבים FSM מקבלת שתי מילות קלט במקום אחת. וכמו בקודם, הפלט הוא XOR של פלט ה-FSM עם הכניסה האחרונה של ה-LFSR. פולינום ההזנה כולל כעת שני אלמנטים שונים, α ו-α1, כאשר הראשון הוא שורש של פולינום פרימיטיבי ממעלה 4 מעל 𝔽28, ביתר פירוט:

π(x)=αx16+x14+α1x5+1 מעל 𝔽232. האלמנט α הוא שורש של x4+β23x3+β245x2+β48x+β239 מעל 𝔽28 וכן β הוא שורש של x8+x7+x5+x3+1 מעל 𝔽2.
קובץ:Snow-key.png
בשלב האתחול של SNOW 2.0 הפלט של מכונת המצבים Ft מחובר עם פונקציית ההיזון של ה-LFSR.

המצב הפנימי בזמן t מיוצג על ידי st+15,st+14,...,st כאשר si𝔽232. האלמנט st הימני ביותר הוא האלמנט הראשון והוא פלט ה-LFSR בכל פעימה. הזמן t=0 פירושו הפעימה הראשונה מיד לאחר אתחול המצב הפנימי עם המפתח. הצופן מופעל פעם אחת על ריק לפני שמתחילים לייצר את זרם המפתח, כלומר בזמן t=1. פלט הצופן הוא z1,z2,z3,.... מכונת המצבים FSM מכילה שני אוגרים המסומנים R1 ו-R2 כל אחד מהם בעל קיבולת של 32 סיביות. תכולת האוגרים בזמן t0 מסומנת R1t, R2t בהתאמה. הקלט ל-FSM נלקח משתי הכניסות st+15,st+5 ב-LFSR ופלט מכונת המצבים בזמן t המיוצג על ידי Ft מחושב כדלהלן:

Ft=(st+15R1t)R2t

פלט הצופן בזמן זה הוא

zt=Ftst כאשר t1.

פונקציית העדכון של מכונת המצבים פועלת כך:

R1t+1=st+5R2t,R2t+1=S(R1t) כאשר S מייצג את תיבת ההחלפה.

S-box

תיבות ההחלפה של SNOW 2.0 המסומנות S(w) הן בעצם פרמוטציה המבוססת על פונקציית הסבב של צופן ריינדל כדלהלן; אם w=(w3,w2,w1,w0) הוא מילת קלט באורך 32 סיביות המחולקת לארבעה בתים כאשר w3 הוא הבית החשוב, אפשר להתייחס אליה כאל וקטור (עמודה). תחילה מיישמים החלפה לפי תיבות ההחלפה של ריינדל ולאחר מכן מבצעים את הפונקציה MixColumn של ריינדל. כלומר מתייחסים למילת הקלט כאל פולינום y ממעלה לכל היותר 3 מעל השדה 𝔽28 המיוצג על ידי פולינום אי-פריק x8+x4+x3+x+1 מעל 𝔽2 ומכפילים בפולינום קבוע c(y)=(x+1)y3+y2+y+x מעל 𝔽28[y]. הכפל האחרון יכול להתבצע כמו בריינדל על ידי חשבון מטריצות:

(r0r1r2r3)=(xx+1111xx+1111xx+1x+111x)(SR(w0)SR(w1)SR(w2)SR(w3))

התוצאה היא הבתים r0,r1,r2,r3. שתי הפעולות יחד ניתנות לביצוע במשולב כמו בפסאודו קוד הבא:

r0=MULx(SR[w0],0x1b)SR[w1]SR[w2]MULx(SR[w3],0x1b)SR[w3]
r1=MULx(SR[w0],0x1b)SR[w0]MULx(SR[w1],0x1b)SR[w2]SR[w3]
r2=SR[w0]MULx(SR[w1],0x1b)SR[w1]MULx(SR[w2],0x1b)SR[w3]
r3=SR[w0]SR[w1]MULx(SR[w2],0x1b)SR[w2]MULx(SR[w3],0x1b)

כאשר SR מייצגת את תיבות ההחלפה של ריינדל והפונקציה MULx היא פעולת הכפלה של אלמנט בשדה עם אלמנט פרימיטיבי (x) שהיא דומה לכפל ב-2, במילים אחרות; פעולת Shift לשמאל סיבית אחת במידה שאין גלישה, או פעולת Shift ו-XOR אחד עם ערך קבוע (0x1b) אם הייתה גלישה. דהיינו אם הסיבית המשמעותית ביותר הייתה '1' לפני הכפל (הפונקציה מתוארת ביתר פירוט ב-SNOW 3G להלן).

אתחול מפתח

תחילה, אם מפתח ההצפנה באורך 128 סיביות או ארבע מילים k0,...,k3 באורך 32 סיביות כל אחת, טוענים אותו יחד עם וקטור האתחול IV באורך ארבע מילים IV0,...IV3 אם ישנו, לתוך ה-LFSR לפי:

s15=k3IV0,s14=k2,s13=k1,s12k0IV1,
s11=k31,s10=k21IV2,s9=k11IV3,s8=k01
s7=k3,s6=k2,s5=k1,s4=k0,
s3=k31,s2=k21,s1=k11,s0=k01

כאשר 1 מייצג וקטור שכולו אחדים (0xffffffff). אם המפתח הוא 256 סיביות כלומר שמונה מילים k0,...,k7 אזי הטעינה מתבצעת לפי:

s15=k7IV0,s14=k6,s13=k5,s12=k4IV1,
s11=k3,s10=k2IV2,s9=k1IV3,s8=k0,
s7=k71,s6=k61,s5=k51,s4=k41,s3=k31,s2=k21,s1=k11,s0=k01.

לאחר מכן מאפסים את R1 ו-R2 ומפעילים את הצופן 32 פעימות (Clocks) מבלי לייצר כל פלט, אלא במקום זאת מזינים את התוצאה בחזרה לפונקציית ההזנה (כמתואר בתרשים), לכן במהלך האתחול האלמנט הבא שמוזן לתוך ה-LFSR הוא:

st+16=α1st+11st+2αstFt.

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

st+16=α1st+11st+2αst.

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

ההבדלים העיקריים בין גרסה 1 לגרסה 2 הם:

  • שינוי פולינום ההזנה, בעוד שב-SNOW 1.0 ניתן היה לחשב את לולאת ההזנה עם כפל יחיד ו-XOR אחד מותנה, כך שרוב הזמן בוצע רק Shift הרי שבגרסה 2 מבוצעות שתי הכפלות ו-XOR בלתי מותנה בכל אחת מהן. התוצאה היא פיזור טוב יותר של הסיביות מה שמחזק את הצופן נגד התקפת קורלציה.
  • שימוש בשני קבועים שונים בלולאת ההזנה במקום אחד. למיטב ידיעת המפתחים אין אפשרות למניפולציה של פולינום ההזנה כך שנוסחת הנסיגה הליניארית שלו תהיה בעלת משקל נמוך.
  • ביצוע XOR באופן קבוע ללא תלות במשתנים האחרים בפולינום ההזנה משפר ביצועים, לעומת הגרסה הקודמת שבה ה-XOR היה מותנה בסיבית המשמעותית.
  • ה-FSM של הגרסה השנייה מקבל שני פרמטרים במקום אחד. מה שמקשה על התקפות בסגנון ניחוש ובדיקה, כיוון שפלט R1 תלוי כעת גם בקלט מה-LFSR.
  • תיבות ההחלפה החדשות בגרסה 2 הושאלו מצופן ריינדל לעומת גרסה 1. הבחירה הזו משפרת את הפיזור כך שכל סיבית פלט מושפעת מכל סיביות הקלט, בניגוד לגרסה הקודמת.

ביטחון

ביטחון SNOW 2.0 טוב יותר מהגרסה המקורית אם כי גם בו התגלו חולשות לא מעטות. התקפות קירוב ליניארי[5] והתקפות הבחנה[6][7] שהתפרסמו מוכיחים שהצופן פחות בטוח ממה שהצהירו המפתחים.

SNOW 3G

קובץ:Snow-3g.png
תיאור סכמתי של צופן SNOW 3G

SNOW 3G מבוסס על SNOW 2.0 והוא שוכן בליבה המבצעת הצפנה מאומתת של תקשורת סלולרית באלגוריתמים UEA2 להצפנה ו-UIA2 לאימות בדור השלישי ובדור הרביעי של GSM. הצופן פועל ברמה של מילים באורך 32 סיביות, מקבל מפתח סודי באורך 128 סיביות וכן וקטור אתחול באורך 128 סיביות. הפלט מחובר עם השיחה או הנתונים מילה אחר מילה ב-XOR והתוצאה משודרת לצד השני. בפענוח הצד המקבל פועל באופן דומה, מפעיל את הצופן עם המפתח הסודי המשותף לקבלת זרם מפתח תואם ואז מחבר שוב עם השיחה המוצפנת כדי להחזירה למצב הגלוי.

SNOW 3G כולל אוגר LFSR אחד בעל 16 כניסות (Taps) באורך 32 סיביות כל אחת, מכונת מצבים אחת הכוללת שלושה אוגרים באורך 32 סיביות ופונקציות עדכון מתאימות לפי הפירוט בהמשך. בתחילת כל שיחה טוענים את המפתח ואת וקטור האתחול לתוך המצב הפנימי והצופן מופעל 32 פעימות ללא שימוש בפלט במקום זאת הפלט מוזן בחזרה לאוגר ולאחר מכן עוברים למצב הפעלה רגיל ומייצרים בכל פעימה 32 סיביות מפתח. בשני מצבי ההפעלה של הצופן (Clocking), מצב האתחול ומצב העבודה משתמשים בשתי הפונקציות הבאות:

הפונקציה MULx

פונקציה להכפלת אלמנטים בשדה 𝔽28 ב-2 (או הפולינום x), הנקראת MULx(V,c) מקבלת שני בתים V,c ומחזירה בית אחד:

אם הסיבית המשמעותית ביותר של V היא 1 מבצעים:
MULx(V,c)(V81)c
אחרת מבצעים:
MULx(V,c)V81.

הזזה שמאלה (sift left) שהיא פעולה המובנית בכל המעבדים שקולה לכפל בפולינום x (או ב-'02') שהוא אלמנט פרימיטיבי בשדה 𝔽28. אם הסיבית המשמעותית ביותר של V היא '1' תתרחש גלישה לאחר הזזה, במצב זה מתקבל אלמנט שאינו בשדה 𝔽28 ולכן יש צורך לבצע צימצום מודולרי עם הקבוע c ולקחת את השארית. כיוון שהגלישה מאוד קטנה אין צורך בביצוע פעולת חילוק ארוכה, לכל היותר יידרש חיסור אחד כדי לקבל את השארית. בהרחבה של שדה בינארי פעולת החיסור זהה לחיבור והיא XOR.

דוגמה במספרים: MULx(0x96,0x1B)=0x2C0x1B=0x37.

הפונקציה MULxPOW

הפונקציה MULxPOW משתמשת ב-MULx באופן רקורסיבי כדי לבצע העלאה בחזקה מודולרית של אלמנטים בשדה. היא מקבלת שני בתים V,c ומעריך i ומחזירה בית אחד כך:

אם i=0
מחזירים את V
אחרת מבצעים:
MULxPOW(V,i,c)=MULx(MULxPOW(V,i1,c),c).

הפונקציה MULx מקבילה למעשה להכפלה באיבר פרימיטיבי של ההרחבה ממעלה 8 מעל GF(2). יהי β שורש של פולינום אי פריק ממעלה 8 מעל GF(2)[β] אז עבור כל אלמנט VGF(28) התוצאה של MULx מקבילה ל-Vβ. כמו כן הפונקציה MULxPOW מקבילה להכפלה בחזקה i של האיבר הפרימיטיבי: Vβi.

LFSR ו-FSM

אוגר ה-LFSR מורכב מ-16 מצבים s0,s1,...,s15 כל אחד מכיל 32 סיביות. מכונת המצבים FSM מכילה שלושה אוגרי 32 סיביות הנקראים R1,R2,R3. פונקציות ההחלפה S1 ו-S2 משמשות לעדכון האוגרים R1 ו-R2 בהתאמה (כמתואר בתרשים משמאל). תיבות ההחלפה ממפות קלט באורך 32 סיביות לפלט באורך 32 סיביות. הקלט מחולק לארבעה בתים w=w0w1w2w3 כאשר w0 הוא הבית הכי פחות משמעותי (LSB).

פונקציות החלפה

את פונקציית ההחלפה S1 מכינים על ידי הטבלה SR אותה משאילים מצופן ריינדל (בהמשך). הקלט מחולק לארבעה בתים אותם מפרשים כמקדמים של אלמנט בשדה GF(28) המוגדר על ידי הפולינום x8+x4+x3+x+1. בנוסף להחלפה לפי ה-S-box של ריינדל מתבצעת הפונקציה MixColumn של ריינדל. שתי הפעולות, ההחלפה וערבוב העמודות מבוצעות במשולב כדלקמן:

r0=MULx(SR(w0),0x1B)SR(w1)SR(w2)MULx(SR(w3),0x1B)SR(w3),
r1=MULx(SR(w0),0x1B)SR(w0)MULx(SR(w1),0x1B)SR(w2)SR(w3),
r2=SR(w0)MULx(SR(w1),0x1B)SR(w1)MULx(SR(w2),0x1B)SR(w3),
r3=SR(w0)SR(w1)MULx(SR(w2),0x1B)SR(w2)MULx(SR(w3),0x1B).

את פונקציית ההחלפה S2 מכינים על ידי הטבלה SQ המובאת להלן. ההחלפה מתבצעת כך

r0=MULx(SQ(w0),0x69)SQ(w1)SQ(w2)MULx(SQ(w3),0x69)SQ(w3),
r1=MULx(SQ(w0),0x69)SQ(w0)MULx(SQ(w1),0x69)SQ(w2)SQ(w3),
r2=SQ(w0)MULx(SQ(w1),0x69)SQ(w1)MULx(SQ(w2),0x69)SQ(w3),
r3=SQ(w0)SQ(w1)MULx(SQ(w2),0x69)SQ(w2)MULx(SQ(w3),0x69).

פונקציית ההיזון של ה-LFSR וכן פונקציית העדכון של ה-FSM משתמשות בפונקציות MULα ו-DIVα המוגדרות כך:

MULα=(MULxPOW(c,23,0xA9)
MULxPOW(c,245,0xA9)
MULxPOW(c,48,0xA9)
MULxPOW(c,239,0xA9)).
DIVα=(MULxPOW(c,16,0xA9)
MULxPOW(c,39,0xA9)
MULxPOW(c,6,0xA9)
MULxPOW(c,64,0xA9)).

תיבות החלפה S-box

הטבלה SR מכילה 256 ערכים אפשריים באורך 8 סיביות (בתים) בבסיס הקסדצימלי מתוך תיבות ההחלפה של צופן ריינדל, כך שמתקיים SR(x024+x1)=y024+y1, התא המצוי בשורה x0 ועמודה x1 מכיל את הערך y0y1. לדוגמה SR(42)=SR(0x2A)=0xE5=229:

<syntaxhighlight lang="c">
  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F

0 63 7C 77 7B F2 6B 6F C5 30 01 67 2B FE D7 AB 76 1 CA 82 C9 7D FA 59 47 F0 AD D4 A2 AF 9C A4 72 C0 2 B7 FD 93 26 36 3F F7 CC 34 A5 E5 F1 71 D8 31 15 3 04 C7 23 C3 18 96 05 9A 07 12 80 E2 EB 27 B2 75 4 09 83 2C 1A 1B 6E 5A A0 52 3B D6 B3 29 E3 2F 84 5 53 D1 00 ED 20 FC B1 5B 6A CB BE 39 4A 4C 58 CF 6 D0 EF AA FB 43 4D 33 85 45 F9 02 7F 50 3C 9F A8 7 51 A3 40 8F 92 9D 38 F5 BC B6 DA 21 10 FF F3 D2 8 CD 0C 13 EC 5F 97 44 17 C4 A7 7E 3D 64 5D 19 73 9 60 81 4F DC 22 2A 90 88 46 EE B8 14 DE 5E 0B DB A E0 32 3A 0A 49 06 24 5C C2 D3 AC 62 91 95 E4 79 B E7 C8 37 6D 8D D5 4E A9 6C 56 F4 EA 65 7A AE 08 C BA 78 25 2E 1C A6 B4 C6 E8 DD 74 1F 4B BD 8B 8A D 70 3E B5 66 48 03 F6 0E 61 35 57 B9 86 C1 1D 9E E E1 F8 98 11 69 D9 8E 94 9B 1E 87 E9 CE 55 28 DF F 8C A1 89 0D BF E6 42 68 41 99 2D 0F B0 54 BB 16

</syntaxhighlight>

הטבלה SQ מכילה 256 ערכים אפשריים באורך 8 סיביות (בתים) בבסיס הקסדצימלי אותה מכינים על ידי פולינום דיקסון g49(x)=xx9x13x15x33x41x45x47x49 כך שעבור קלט x באורך 8 סיביות המייצג אלמנט בשדה GF(28) המוגדר על ידי הפולינום x8+x6+x5+x3+1 כל כניסה בטבלה מתאימה ל-g49(x)0x25:

<syntaxhighlight lang="c">
  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F

0 25 24 73 67 D7 AE 5C 30 A4 EE 6E CB 7D B5 82 DB 1 E4 8E 48 49 4F 5D 6A 78 70 88 E8 5F 5E 84 65 E2 2 D8 E9 CC ED 40 2F 11 28 57 D2 AC E3 4A 15 1B B9 3 B2 80 85 A6 2E 02 47 29 07 4B 0E C1 51 AA 89 D4 4 CA 01 46 B3 EF DD 44 7B C2 7F BE C3 9F 20 4C 64 5 83 A2 68 42 13 B4 41 CD BA C6 BB 6D 4D 71 21 F4 6 8D B0 E5 93 FE 8F E6 CF 43 45 31 22 37 36 96 FA 7 BC 0F 08 52 1D 55 1A C5 4E 23 69 7A 92 FF 5B 5A 8 EB 9A 1C A9 D1 7E 0D FC 50 8A B6 62 F5 0A F8 DC 9 03 3C 0C 39 F1 B8 F3 3D F2 D5 97 66 81 32 A0 00 A 06 CE F6 EA B7 17 F7 8C 79 D6 A7 BF 8B 3F 1F 53 B 63 75 35 2C 60 FD 27 D3 94 A5 7C A1 05 58 2D BD C D9 C7 AF 6B 54 0B E0 38 04 C8 9D E7 14 B1 87 9C D DF 6F F9 DA 2A C4 59 16 74 91 AB 26 61 76 34 2B E AD 99 FB 72 EC 33 12 DE 98 3B C0 9B 3E 18 10 3A F 56 E1 77 C9 1E 9E 95 A3 90 19 A8 6C 09 D0 F0 86 </syntaxhighlight>

מצב אתחול

באתחול הצופן בכל פעימה ה-LFSR מקודם כך שהערך המצוי בכל כניסה מועתק לכניסה הבאה משמאל לימין (כלומר s0=s1,s1=s2,s2=s3 וכן הלאה), הכניסה האחרונה s15 מתעדכנת על ידי פונקציית ההיזון לפי הנוסחה הבאה. מתייחסים ל-s0 כאל ארבעה בתים s0,0s0,1s0,2s0,3 וכן לגבי הכניסה s11:

s15=(s0,1s0,2s0,30x00)MULα(s0,0)s2(0x00s11,0s11,1s11,2)DIVα(s11,3)F.

הערך F הוא פלט מכונת המצבים המוגדרת להלן.

מצב הצפנה

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

s15=(s0,1s0,2s0,30x00)MULα(s0,0)s2(0x00s11,0s11,1s11,2)DIVα(s11,3).

בשלב זה לא כוללים את פלט מכונת המצבים בפונקציית ההזנה.

מכונת המצבים

מכונת המצבים FSM מקבלת מה-LFSR שתי מילים מהכניסות s15 ו-s5 ומייצרת מילה אחת F כדלהלן:

F=(s15(R1)R2

עדכון מכונת המצבים מתבצע כך:

r=R2(R3s5),
R3=S2(R2),
R2=S1(R1),
R1=r.

הכנת מפתח

בשלב הכנת הצופן טוענים את המפתח הסודי באורך 128 סיביות המחולק לארבע מילים k0,...,k3 ואת וקטור האתחול IV0,...,IV3 לתוך ה-LFSR כדלהלן:

s15=k3IV0,s14=k2,s13=k1,s12=k0IV1
s11=k31,s10=k21IV2,s9=k11IV3,s8=k01
s7=k3,s6=k2,s5=k1,s4=k0
s3=k31,s2=k21,s1=k11,s0=k01

כאן 1 מייצג מילה המכילה 32 אחדים (0xffffffff) כמו כן בשלב האתחול תכולת האוגרים של ה-FSM מאופסת. ואז מריצים את הצופן 32 פעימות ללא שימוש בפלט, אלא במקום זאת הפלט F מחובר לפונקציית ההיזון בחזרה. לאחר מכן מריצים את הצופן פעימה נוספת בה לא משתמשים בפלט כלל ולאחר מכן הצופן מוכן לשימוש.

הצפנה

בכל פעימת הצפנה המהלכים הבאים מתבצעים:

  1. מריצים את ה-FSM פעימה אחת ומייצרים את F,
  2. מכינים מילת מפתח אחת zt=Fs0,
  3. מריצים את ה-LFSR פעימה אחת.
  4. מחברים את מילת המפתח עם מילה אחת מהמסר או השיחה.

חוזרים שוב על התהליך עד להשלמת כל המסר.

ביטחון

בעקרון ביטחון גרסה זו מבוסס על SNOW 2.0 וקיימת קריפטואנליזה של שתי הגרסאות. מחקרים[8] מראים שקיימת חולשה שנקראת "מפתחות קשורים" (related keys) בשניהם וכן התגלו חולשות באקראיות הצופן. ב-2010 פורסמה התקפת ערוץ צדדי[9] נגד SNOW 3G שמראה שניתן לפרוץ את הצופן בזמן מאוד קצר (מספר שניות) אם לתוקף יש אפשרות להשיג מידע תיזמון הקשור במעבר מתיבת החלפה אחת לשנייה. הפתרון שהוצע במקרה כזה שימוש בטכניקה שנקראת BitSlice בדומה לצופן סרפנט.

היבטי יישום

צופן SNOW פותח תוך מתן דגש על יישום מהיר בתוכנה. הפעולות בצופן נפוצות בכל מעבד מצוי; XOR, חיבור שלמים, הזזה ואחזור טבלאות (קריאות זיכרון). אף על פי ש-LFSR הוא רכיב חומרה אפשר ליישמו ביעילות בתוכנה, השדה 𝔽232 הוא הרחבה של 𝔽28 עם α𝔽232 שורש ממעלה 4 של הפולינום

x4+β23x3+β245x2+β48x+β239𝔽28[x]

לכן אפשר לצמצם את המעלה של α כי

α4=β23α3+β245α2+β48α+β239

בסיכומו של דבר הכפל ב-α וב-α1 בלולאת ההזנה ניתנים למימוש בתוכנה בפעולת הזזה (shift) ברמה של בתים. לסיכום זה נראה כך:

MULα[c]=(cβ23,cβ245,cβ48,cβ239)
MULα1[c]=(cβ16,cβ39,cβ6,cβ64)

המקדמים ci הם בתי הקלט w. אפשר לבצע את הכפל והחילוק בשיטה הישירה כמתואר לעיל או באמצעות טבלאות חיפוש מוכנות מראש כמו בפסאודו קוד הבא: <syntaxhighlight lang="CPP"> result=(w<<8) ^ MUL_A[w & 0xff]; result=(w>>8) ^ MUL_AINVERSE[w & 0xff]; </syntaxhighlight> יישום פעולת ההחלפה של צופן SNOW בתוכנה ניתן להאצה בטכניקה דומה לזו שאומצה בצופן ריינדל. הכפל במטריצות המתואר לעיל ניתן לפיצול כצירוף ליניארי של העמודות תוך שימוש בארבע תיבות המכילות 256 מילים כל אחת. כל טבלה מכילה את כל התוצאות האפשריות של הכפלה בעמודה אחת בהתאם. לסיכום בפסאודו קוד הבא יוצאים מהנחה שקיימות ארבע טבלאות T0,...,T3 כאמור: <syntaxhighlight lang="text"> r=T0[w & 0xff] ^ T1[(w >> 8) & 0xff] ^ T2[(w >> 16) & 0xff] ^ T3[(w >> 24) & 0xff]; </syntaxhighlight> כמו כן אפשר ליישם את ה-LFSR בשני אופנים. האחד קל לקריאה והוא מיושם באמצעות מערכים ושימוש בטכניקת חלון, קרי במקום לבצע העתקה בפועל של ערכים מנהלים מעקב באמצעות מצביעים. השני הוא הארד קודינג של מצבי ה-LFSR. היתרון בשיטה האחרונה שבכל קריאה לפרוצדורה להפקת זרם המפתח נוצרות בבת אחת 512 סיביות המקבילות ל-16 פעימות שעון בשיטה הרגילה. בניסוי שערכו המפתחים על פנטיום 4 של אינטל 1.8GHz עם 512 מגה, בגרסה 2 הממוטבת (הארד קודינג) נדרשו 18 מחזורי שעון למילה אחת. שהוא קצב מהיר כמעט פי שניים.

קוד לדוגמה של צופן SNOW 3G

הקוד הבא הוא קוד שלם הכתוב בשפת C#‎ לפי היישום הבסיסי לא ממוטב שהומלץ על ידי התקן לצורך ייחוס והשוואה. הוא משתמש במערך LFSR באורך 16 כניסות בגודל 32 סיביות ומערך FSM עם שלוש כניסות באורך 32 סיביות וכן שתי טבלאות SR ו-SQ המכילות בכל אחת 256 ערכים קבועים: <syntaxhighlight lang="CPP">

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

       uint S2(uint w)
       {
           byte sqw0 = SQ[(byte)((w >> 24) & 0xff)];
           byte sqw1 = SQ[(byte)((w >> 16) & 0xff)];
           byte sqw2 = SQ[(byte)((w >> 8) & 0xff)];
           byte sqw3 = SQ[(byte)((w) & 0xff)];
           byte r0 = (byte)((MULx(sqw0, 0x69)) ^ (sqw1) ^ (sqw2) ^ ((MULx(sqw3, 0x69)) ^ sqw3));
           byte r1 = (byte)(((MULx(sqw0, 0x69)) ^ sqw0) ^ (MULx(sqw1, 0x69)) ^ (sqw2) ^ (sqw3));
           byte r2 = (byte)((sqw0) ^ ((MULx(sqw1, 0x69)) ^ sqw1) ^ (MULx(sqw2, 0x69)) ^ (sqw3));
           byte r3 = (byte)((sqw0) ^ (sqw1) ^ ((MULx(sqw2, 0x69)) ^ sqw2) ^ (MULx(sqw3, 0x69)));
           return ((((uint)r0) << 24) | (((uint)r1) << 16) | (((uint)r2) << 8) | (((uint)r3)));
       }
       void ClockLFSRInitializationMode(uint F)
       {
           uint v = (((LFSR[0] << 8) & 0xffffff00) ^ (MULalpha((byte)((LFSR[0] >> 24) & 0xff))) ^ (LFSR[2]) ^
                     ((LFSR[11] >> 8) & 0x00ffffff) ^ (DIValpha((byte)((LFSR[11]) & 0xff))) ^ (F));
           for (int i = 0; i < 15; i++)
           {
               LFSR[i] = LFSR[i + 1];
           }
           LFSR[15] = v;
       }
       void ClockLFSRKeyStreamMode()
       {
           uint v = (((LFSR[0] << 8) & 0xffffff00) ^ (MULalpha((byte)((LFSR[0] >> 24) & 0xff))) ^ (LFSR[2]) ^
                     ((LFSR[11] >> 8) & 0x00ffffff) ^ (DIValpha((byte)((LFSR[11]) & 0xff))));
           for (int i = 0; i < 15; i++)
           {
               LFSR[i] = LFSR[i + 1];
           }
           LFSR[15] = v;
       }
       uint ClockFSM()
       {
           uint F = ((LFSR[15] + FSM[0]) & 0xffffffff) ^ FSM[1];
           uint r = (FSM[1] + (FSM[2] ^ LFSR[5])) & 0xffffffff;
           FSM[2] = S2(FSM[1]);
           FSM[1] = S1(FSM[0]);
           FSM[0] = r;
           return F;
       }

       void Initialize(uint[] k, uint[] IV)
       {
           byte i = 0;
           uint F = 0x0;
           LFSR[15] = k[3] ^ IV[0];
           LFSR[14] = k[2];
           LFSR[13] = k[1];
           LFSR[12] = k[0] ^ IV[1];
           LFSR[11] = k[3] ^ 0xffffffff;
           LFSR[10] = k[2] ^ 0xffffffff ^ IV[2];
           LFSR[9]  = k[1] ^ 0xffffffff ^ IV[3];
           LFSR[8]  = k[0] ^ 0xffffffff;
           LFSR[7]  = k[3];
           LFSR[6]  = k[2];
           LFSR[5]  = k[1];
           LFSR[4]  = k[0];
           LFSR[3]  = k[3] ^ 0xffffffff;
           LFSR[2]  = k[2] ^ 0xffffffff;
           LFSR[1]  = k[1] ^ 0xffffffff;
           LFSR[0]  = k[0] ^ 0xffffffff;
           for (i = 0; i < 32; i++)
           {
               F = ClockFSM();
               ClockLFSRInitializationMode(F);
           }
       }
       void GenerateKeystream(uint n, uint[] ks)
       {
           ClockFSM();                // Clock FSM once. Discard the output.
           ClockLFSRKeyStreamMode();  // Clock LFSR in keystream mode once.
           for (int t = 0; t < n; t++)
           {
               uint F = ClockFSM();            // STEP 1 
               ks[t] = F ^ LFSR[0];       // STEP 2 
               ClockLFSRKeyStreamMode();  // STEP 3 
           }
       }

</syntaxhighlight>

ראו גם

הערות שוליים

  1. ^ SNOW - a new stream cipher
  2. ^ A New Version of the Stream Cipher SNOW
  3. ^ P. Hawkes, G. Rose, "Guess-and-determine attacks on SNOW", Preproceedings of Selected Areas in Cryptography (SAC), August 2002, St John's, Newfoundland, Canada
  4. ^ Cryptanalysis of stream ciphers with linear masking
  5. ^ Improved Linear Distinguishers for SNOW 2.0
  6. ^ Alexander Maximov and Thomas Johansson. Fast Computation of Large Distributions and Its Cryptographic Applications. In Asiacrypt 2005, LNCS 3788, Springer-Verlag, pages 313–332, 2005.
  7. ^ De Canni'ere. A Distiguishing Attack of SNOW 2.0 with Linear Masking Method. In Selected Areas in Cryptography, SAC 2003, LNCS 3006, Springer-Verlag, pages 222–233, 2004.
  8. ^ On the Sliding Property of SNOW 3G and SNOW 2.0
  9. ^ Consecutive S-box Lookups: A Timing Attack on SNOW 3G