SAFER (צופן)

בקריפטוגרפיה, SAFER(קיצור של "Secure And Fast Encryption Routine", בעברית רוטינת הצפנה בטוחה ומהירה) הוא שם כולל למשפחה של צפני בלוקים סימטריים איטרטיביים שפותחו עבור חברת Cylink ארצות הברית, בעיקר על ידי ג'יימס מסי (James Massey) מהמכון הטכנולוגי של ציריך. הגרסאה הראשונה SAFER K פותחה בדצמבר 1993 לאחריה פותחה SAFER SK הנבדלת ממנה במספר הסבבים ותהליך הכנת המפתח. גרסאות מתקדמות יותר הנקראות SAFER+ ו-SAFER++ פותחו בשנת 2000 והוצעו כמועמדים לתקן AES ולפרויקט NESSIE בהתאמה, אך לא הגיעו לגמר. כל האלגוריתמים חופשיים לשימוש ואינם מוגנים בפטנט כלשהו. SAFER+ אומץ על ידי בלוטות' כחלק מפרוטוקול אתגר-מענה לאימות זהויות בתקשורת אלחוטית בגרסאות עד 4.0.

משפחת האלגוריתמים SAFER כוללת נכון ל-2017 את הגרסאות הבאות לפי סדר כרונולוגי:
SAFER K-64
SAFER K-128
SAFER SK-64
SAFER SK-128
SAFER SK-40
SAFER+
SAFER++
גודל הבלוק בחמש הגרסאות הראשונות הוא 64 סיביות, גודל המפתח בסיביות נרמז במספר המופיע לאחר השם, כאשר בגרסאות SK תהליך הכנת המפתח חוזק בגלל חולשות שהתגלו בקריפטואנליזה של הגרסאות הראשונות. בשתי האחרונות, גודל הבלוק שונה ל-128 סיביות כדי להכשירן כמועמדות לתקני ההצפנה הבינלאומיים. בדומה ל-AES אורך המפתח הנתמך מגיע בשלוש וריאציות 128/192/256 בהתאמה. שתי הגרסאות האחרונות פותחו על ידי מסי ועמיתיו מהאקדמיה הלאומית הארמנית והן מציעות שיפור נוסף על פני קודמותיהן הן ביעילות והן בביטחון. אפקט הפיזור של SAFER מושג על ידי טרנספורמציה ליניארית רב-ממדית הנקראת PHT שהיא התמרת פסבדו-אדמר המפורטת בהמשך. בכל הגרסאות הראשונות נעשה שימוש ב-PHT דו-נקודתית ). בגרסה SAFER+ מתבצע פיזור מהיר יותר על ידי מה שנקרא "הערבוב הארמני" שנקרא כך על שם ממציאיו Gurgen Khachatrian ו-Melsik Kuregian. בגרסה SAFER++ בנוסף לשיפורים המנויים, שופר הפיזור באמצעות PHT בארבע נקודות ). גרסה זו כוללת גם גרסת מורשת (legacy) עם בלוק באורך 40 סיביות.

שיקולי פיתוח ומאפיינים כלליים
שם הצופן ניתן על ידי ג'יימס מסי שהיה ממפתחי IDEA והרעיונות ששולבו לראשונה ב-IDEA היוו מקור השראה ל-SAFER. התוספת SK בגרסאות המתקדמות היא ראשי התיבות Strengthened Key (מפתח מחוזק) והיא ניתנה בעקבות קריפטואנליזה שבוצעה על ידי לארס קנודסן שגילה שאם הצופן משמש בתצורת פונקציית גיבוב, בגלל "קיבעון" כלשהו של בתי המפתח אפשר למצוא התנגשויות בפחות מהזמן הדרוש בכוח גס. הוא הציע לשנות את התהליך כדי למנוע חולשה זו ובעקבות כך פותחה הגרסה SK. יש המפרשים בהלצה את ראשי התיבות SK כ-Stop Knudesen שהיא "עצה טובה לפיתוח כל צופן בלוקים".

העקרונות המובילים בפיתוח אלגוריתם SAFER במיוחד SAFER++ הם:
סגנון רשת החלפה-תמורה. למרות היתרון של רשת פייסטל בכך שאין צורך ליישם פונקציית פענוח, היתרון ברשת החלפה-תמורה שהיא מאפשרת פיזור או "אפקט מפולת" מהיר הרבה יותר. בנוסף בשיטה זו הפונקציה הפנימית לא צריכה להסתמך על תיבות החלפה ש"ניראות אקראיות" מה שמהווה מקור לחשש שמא הוחדרה בהן דלת אחורית כמו במקרה של DES. צופן SAFER משתמש ברשת של פונקציות החלפה/טרנספורמציה ליניארית הפיכות בעלות מבנה אלגברי בולט. הסגנון של SAFER נותן חופש רב יותר ביצירת אפקט מפולת מהיר במיוחד.
הצפנה ברמה של בתים. בכל גרסאות SAFER כל פעולות הצופן למעט כמה פעולות חד-פעמיות בתהליך ההכנה, מתבצעות בית לבית. עובדה זו מאפשרת שיפור ביצועים במיוחד בחומרה מוגבלת משאבים.
פעולות אלגבריות בשתי חבורות שונות. בשלב החיבור עם המפתח הקלט מחולק לשתי חבורות שונות, כאשר באחת מתבצע חיבור מודולו 256 ובשנייה חיבור XOR. בגלל אי-הליניאריות של שילוב הפעולות הללו (כי אסוציאטיביות נהרסת), מתקבל אפקט ערבוב גבוה שמחזק את הצופן נגד קריפטואנליזה דיפרנציאלית. בנוסף, חיבור המפתח לפני הסבב הראשון, שהוא בעצם המפתח המסופק על ידי המשתמש עם הטקסט הקריא, בהנחה שהמפתח נבחר באקראי, מניב מעין סודיות מושלמת כמו בפנקס חד-פעמי. זהו מאפיין רצוי שקיים בצפנים מודרניים רבים, לעיתים נקרא הלבנה.
שימוש בטבלאות החלפה הפיכות. השכבה האי-ליניארית של SAFER מורכבת מסדרה של פונקציות העלאה בחזקה ולוגריתמים מעל  שהן פעולות משלימות. מבנה זה ידוע בתכונות המתמטיות שלו, הוא מציע שקיפות מלאה כיוון שתיבות ההחלפה אינן אקראיות אלא הן פונקציה של הקלט וכן הוכח שבתנאים אילו השכבה זו מפיקה אי-ליניאריות מקסימלית. 
פיזור הפיך באמצעות כפל מטריצות. בכל גרסאות SAFER שכבת הטרנספורמציה הליניארית הציגה נתונים מרשימים, פיזור מלא כמעט בתוך שלושה סבבים בטכניקה לא מסורתית שנקראת "פיזור אדמר" או התמרת פסבדו-אדמר (בקיצור PHT) על שם המתמטיקאי היהודי-צרפתי ז'אק אדמר. הרעיון הוא להכפיל את הבלוק במטריצה עם תכונות פיזור גבוהות כשכל הפעולות האריתמטיות הן בגבולות בתים (מודולו 256). בגרסת SAFER++ הפיזור אף שופר משמעותית כך שלא רק שהושג פיזור טוב יותר אלא גם ביעילות רבה יותר. הרעיון הוא שימוש ב-PHT בארבע נקודות ויישומו דורש רק שש פעולות חיבור או חיסור במקרה של הפענוח (כמתואר בתרשים להלן) בנוסף לתמורות קבועות נוספות שקיבלו את הכינוי "הפיזור הארמני" על שם ממציאיו, על פי עיקרון שהבית ה- בקלט הופך לבית ה- בפלט כאשר  ו- שקולים מודולו 4. הם הראו שבחירה זו משיגה פיזור אופטימלי והיא הפיכה.
סקלביליות. כל גרסאות SAFER הן בעלות סקלביליות גבוהה, כלומר אפשר ליישם גרסת מיני של הצופן למשל בגרסת שתי סיביות שבה הבית הוא כביכול באורך 2 סיביות, הבלוק באורך 32 סיביות וכל הפעולות האריתמטיות הן מודולו 4 או 5 בהתאמה. בגרסת 4 סיביות הבלוק באורך 64 סיביות והפעולות הן מודולו 16 ו-17 בהתאמה. אפשר ליישם את הצופן בגרסת "מקסי" עם בתים באורך של 16 סיביות אך יעילות הצופן יורדת בעקבות כך. גרסאות מיני שימושיות במיוחד לצורך קריפטואנליזה והתגלו יעילות במציאת חולשות ותיקונן. 
תהליך הרחבת מפתח עם הטיה. תהליך הרחבת המפתח מביא בחשבון מפתחות חלשים (מפתחות בעלי תכונה מיוחדת כגון מפתח שמכיל הרבה אפסים, שגורמים לפלט הצופן להתנהג בצורה לא אקראית). ההטיה בתהליך הכנת המפתח מבטלת השפעות חיצוניות וגורמת למפתח המורחב להראות אקראי ללא תלות במפתח המסופק על ידי המשתמש. אף על פי שתוספת זו אינה קריטית היא חשובה במקרים מסוימים, בין היתר כאשר משתמשים בצופן כפונקציית גיבוב חסינת התנגשויות. לארס קנודסן הציע שני שיפורים שהוכנסו בגרסאות החדשות, האחד נקרא "תיבות בחירה" שמתנהגות כתמורות של בתי המפתח שנועדו לפתור את בעיית ה"קיבעון" שלהם לפני הוספת ההטיה והשני הוא הוספת בית זוגיות שהוא חיבור מודולו 2 של כל בתי המפתח.
מספר סבבים. ההתקפה הכי אפקטיבית נגד גרסאות SAFER לדעת המפתחים היא קריפטואנליזה דיפרנציאלית. התקפה זו מנצלת חולשות שנובעות מערבוב חלש של השכבה האי-ליניארית ולמספר הסבבים השפעה מכרעת על הצלחת ההתקפה. בגרסאות הראשונות מספר הסבבים היה נמוך (6 או 7 סבבים) בגרסת SAFER++ עם מפתח באורך 256 סיביות נקבעו עשרה סבבים מה שמספק שולי ביטחון גבוהים מאוד.
יעילות. הצופן פשוט, גמיש וקל ליישום. חסרונו הוא שפונקציית הפענוח שונה מפונקציית ההצפנה לא רק בסדר המפתחות. ביישום פורטבילי על פלטפורמת פנטיום 3 עם מפתח באורך 128 סיביות SAFER++ מצפינה בקצב של 40 מחזורי שעון לבית והכנת המפתח 145 מחזורי שעון לבית. עם מפתח 256 סיביות קצב ההצפנה 57 מחזורי שעון והכנת מפתח 202. צופן SAFER איטי מעט בהשוואה לצפנים ברמה של AES.
ביטחון. לדעת מפתחי הצופן לא קיימת קריפטואנליזה מוצלחת של SAFER++ במלוא הסבבים. בגרסאות הקודמות התגלו בעיות מינוריות שתוקנו כולן בגרסה האחרונה. כיוון שהגרסה האחרונה חדשה קריפטואנליזה שלה דלה יחסית. המפתחים הצהירו שלא קיימת הוכחה מתמטית לביטחון האלגוריתם במונחים של סיבוכיות חישובית, אך יש לציין שהדברים נכונים כמעט לגבי כל צופן בלוקים מודרני ידוע.

התמרת פסבדו-אדמר
הרעיון המרכזי של שאנון לפיתוח צופן בלוקים הוא ליצור "אפקט מפולת" או פיזור (diffusion) מהיר ככל האפשר על ידי פונקציית הצפנה שמבוצעת באופן חוזר ונשנה, כך שתוך מספר מצומצם של חזרות, שינוי קל מאוד בטקסט המקורי יתפתח לשינוי גדול בטקסט המוצפן. ולא יהיה שום יחס או קורלציה בין שני טקסטים מקוריים הנבדלים ביניהם רק בסיבית אחת על ידי יחס דומה בין הטקסטים המוצפנים. לצורך אפקט הפיזור צופן SAFER מיישם מוטיב קריפטוגרפי בלתי שיגרתי; טרנספורמציה ליניארית הנקראת "התמרת פסבדו אדמר" או "התמרת וולש-אדמר".

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

והפעולה ההופכית המסומנת כאן בקיצור  היא:

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

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

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 אז  לכן . שוב מחסרים את המפתח המתאים לפי הסדר המופיע בהצפנה ובזה הושלם סבב אחד עליו חוזרים כמובן  פעמים עם המפתחות המתאימים. תהליך הפענוח מתואר בתרשים משמאל.

הכנת מפתח

תהליך הכנת המפתח (key schedule) של SAFER K-64 מייצר סדרה של  תת-מפתחות  שכל אחד מהם באורך 8 בתים וכל זוג מתאים לסבב בודד. המפתח הנוסף משמש בסוף התהליך לצורך הפונקציה המסיימת. את המפתחות מכינים ממפתח המאסטר שסופק על ידי המשתמש באופן כזה שהמפתחות "ייראו" אקראיים ובלתי תלויים זה בזה. כדי להימנע ממפתחות חלשים (מפתחות שמכילים הרבה אפסים) מבצעים "הטיה" של כל המפתחות למעט הראשון על ידי חיבור עם רצף של בתי הטיה . המפתח הראשון  הוא בעצם המפתח שסופק על ידי המשתמש כמו שהוא. הפונקציות להכנת ערכי ההטייה הן העלאה כפולה בחזקת 45 כדלהלן: 

כאשר  מתייחס לבית ה- של ההטייה . את ההטיות כמובן אפשר לחשב מראש ולאחסן בטבלה. את כל תת-המפתחות מייצרים כך: המפתח  שהוא מפתח המאסטר שהמשתמש בחר מועתק ל- ו"מסובב" על ידי הזזה מעגלית של הסיביות בתוך כל בית 3 פוזיציות ואז חיבור עם ההטייה המתאימה  מודולו 256. עוברים למפתח הבא וכן הלאה עבור כל המפתחות.

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 המסומן .

פונקציית הסבב הפנימית
התרשים משמאל מתאר את הפונקציה הפנימית של SAFER+. בסגנון דומה לצפנים הקודמים במשפחת SAFER, הפונקציה הפנימית מבצעת בכל סבב סדרת פעולות כדלהלן: 
תחילה חיבור עם תת-המפתח הראשון של הסבב הנוכחי.
לאחר מכן החלפה באמצעות שני סוגי תיבות החלפה אי-ליניאריות המשלימות זו את זו המובאות להלן.
הוספת תת-המפתח השני של הסבב הנוכחי.
ואז שלוש רמות של "התמרת פסבדו אדמר" תלת־ממדית בשתי נקודות  כשבין כל רמה קיים שלב פיזור נוסף שהוא תמורה הפיכה ייחודית שנקראת "ערבוב ארמני". התמורה היא:

{| class="wikitable" dir="ltr"
|- align="center"
| 0 || 1 || 2 || 3 || 4 || 5 || 6 || 7 || 8 || 9 || 10 || 11 || 12 || 13 || 14 || 15
|- align="center"
| 8 || 11 || 12 || 15 || 2 || 1 || 6 || 5 || 10 || 9 || 14 || 13 || 0 || 7 || 4 || 3
|}

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

 כך שמתקיים .

הכנת מפתחות
להכנת תת-מפתחות לפונקציה הפנימית של הצופן תחילה לוקחים את 16 הבתים של המפתח שנבחר על ידי המשתמש ומוסיפים להם בית זוגיות שהוא XOR של כל בתי המפתח כך שמתקבלים בסך הכול 17 בתים. ואז עבור כל סבב של הצפנה מכינים שני מפתחות כל אחד באורך 16 בתים כדלהלן:
 בתוך כל בית של בלוק המפתח מבצעים הזזה מעגלית שלוש סיביות שמאלה. 
 משתמשים ב"תיבות בחירה" שהן בעצם תמורה המסובבת את כל 17 בתי המפתח בהזזה מעגלית לשמאל בית אחד. במילים אחרות, בסבב הראשון נוטלים את הבתים 0 עד 15, בסבב השני את הבתים 1 עד 16, בשלישי 2 עד 0 וכן הלאה.
 מחברים את הבתים שנבחרו עם קבועים שנקראים בתי "הטייה". כל בית הטיה  כאשר  הוא אינדקס של תת-המפתח ו- מתייחס למספר הבית משמאל, הוא:
.
התוצאה המתקבלת תהיה 16 בתי תת-המפתח הראשון . כך חוזרים על התהליך עד שמפיקים את כל 17 המפתחות.

תקן בלוטות'
תקן בלוטות' בגרסאות הקודמות עד 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. התוצאה בשלב זה מועברת בטרנספורמציה הליניארית הפיכה להוספת פיזור.

הטרנספורמציה הליניארית

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

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

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

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

הכנת מפתחות
תהליך הכנת תת-המפתחות של SAFER++ מפיק מהמפתח המסופק על ידי המשתמש  מפתחות באורך 16 בתים כל אחד, כל זוג מתאים לסבב אחד של הפונקציה הפנימית של הצופן. בגלל שמספר הסבבים שונה בהתאם לאורך המפתח של המשתמש ובגלל שבגרסאות הישנות גודל המפתח היה רק 64 סיביות, את המפתחות מפיקים באופן כזה שכל תת-המפתחות הם למעשה קינון של המפתחות בהתאם לסוג הצופן, כלומר המפתחות הראשונים מתאימים לגרסת 64 סיביות, אחריהם המפתחות של גרסת מפתח 128 סיביות ואחריהם המפתחות לגרסת 256. כך שאפשר להשתמש באותו תהליך הכנה לכל הווריאציות של הצופן. את המפתחות מכינים בדרך מאוד פשוטה תחילה מוסיפים בית זוגיות שהוא XOR של כל בתי המפתח שנבחר על ידי המשתמש. ואז מפיקים את כל תת-המפתחות על ידי שילוב של הזזה מעגלית ברמה של סיביות בתוך כל בית, הזזה מעגלית ברמה של בתים של כל מפתח וחיבור עם קבועים הנקראים בתי "הטיה" שמספרם תלוי באורך המפתח. את בתי ההטייה מחשבים על ידי:

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

בסך הכול קיימים לכל היותר  בתי הטיה.

סיכום סדר הפעולות להכנת המפתחות
מתחילים עם המפתח שהמשתמש בחר בסך הכול 16 בתים.
מוסיפים בית זוגיות כך שמתקבלים 17 בתים.
המפתח הראשון  הוא בעצם המפתח שהמשתמש בחר כמו שהוא.
מסובבים בהזזה מעגלית את סיביות כל בתי המפתח 6 פוזיציות שמאלה, בגבולות בית אחד.
מסובבים את בתי המפתח בסך הכול שני בתים לשמאל כלומר מתקבל 3,4,5,...,17,1
מחברים עם בתי ההטייה והתוצאה היא המפתח הבא.
חוזרים על הפעולות הללו עד שמקבלים את כל המפתחות הדרושים. עבור מפתח באורך 256 סיביות דרושים בסך הכול 21 תת-מפתחות.

 בתי ההטייה מופיעים בטבלה הבאה:

קוד לדוגמה
להלן קוד 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);
};

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

קישורים חיצוניים

הערות שוליים

צפני בלוקים