ערכות מפתחות

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

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

דוגמה: AEAD

כדאי להשתמש בערכת מפתחות AEAD, שמכילה כמה מפתחות לפרימיטיב AEAD. כמו שהוסבר קודם, כל מפתח מציין באופן ייחודי שתי פונקציות: \(\mathrm{Enc}\) ו \(\mathrm{Dec}\). בנוסף, קבוצת המפתחות מציינת עכשיו שתי פונקציות חדשות: \(\mathrm{Enc}\) ו- \(\mathrm{Dec}\) – \(\mathrm{Enc}\) פשוט שווה לפונקציה \(\mathrm{Enc}\) של המפתח הראשי בקבוצת המפתחות, ואילו הפונקציה \(\mathrm{Dec}\) מנסה לפענח באמצעות כל המפתחות, לפי סדר מסוים (בקטע בהמשך מוסבר איך Tink משפרת את הביצועים של הפונקציה הזו).

חשוב לציין שערכות המקשים הן מקשים מלאים: הן תיאור מלא של הפונקציות \(\mathrm{Enc}\) והשימוש\(\mathrm{Dec}\) בהן. המשמעות היא שמשתמשים יכולים לכתוב כיתה שמקבלת כקלט KeysetHandle, ולהביע את הרעיון שהכיתה צריכה תיאור מלא של אובייקטים \(\mathrm{Enc}\) ו \(\mathrm{Dec}\) כדי לפעול בצורה תקינה. האפשרות הזו מאפשרת למשתמשים לכתוב ממשקי API שמעבירים את המסר הבא: כדי להשתמש במחלקה הזו, צריך לספק לי את התיאור של פרימיטיב קריפטוגרפי.

רוטציית מפתחות

נניח שמשתמש ב-Tink כותב תוכנית שמקבלת קבוצת מפתחות מ-KMS, יוצרת אובייקט AEAD מקבוצת המפתחות הזו, ולבסוף משתמשת באובייקט הזה כדי להצפין ולפענח טקסטים מוצפנים.

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

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

מזהים של מפתחות בטקסטים מוצפנים

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

לכן, Tink מאפשרת להוסיף למחרוזות המוצפנות מחרוזת של 5 בייטים שנגזרת מהמזהה. בהתאם לעיקרון של 'מפתחות מלאים' שצוין למעלה, הקידומת הזו היא חלק מהמפתח, וכל הטקסטים המוצפנים שנוצרו באמצעות המפתח הזה צריכים לכלול את הקידומת הזו. כשמשתמשים יוצרים מפתחות, הם יכולים לבחור אם המפתח ישתמש בקידומת כזו, או אם יש להשתמש בפורמט של טקסט מוצפן ללא קידומת.

כשמפתח נמצא ב-keyset, ‏ Tink מחשב את התג הזה מהמזהה של המפתח ב-keyset. העובדה שהמזהים ייחודיים2 בתוך קבוצת מפתחות מרמזת שהתגים ייחודיים. לכן, אם משתמשים רק במפתחות מתויגים, אין פגיעה בביצועים בהשוואה לפענוח באמצעות מפתח יחיד: Tink צריך לנסות רק אחד מהמפתחות בזמן הפענוח.

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

מפתחות עם דרישה למזהה אבל בלי קידומת של פלט

חלק מהמפתחות צריכים לכלול מזהה ספציפי, אבל לא מוסיפים קידומת לפלט שלהם. לדוגמה, מפתחות חתימה עם הווריאציה NO_PREFIX_WITH_PREHASH_ID (מאוחסנים עם סוג קידומת הפלט WITH_ID_REQUIREMENT) יוצרים חתימות ללא קידומת. כשמשתמשים במפתח כזה עם הפרימיטיב Prehash, ‏ Tink כותב את מזהה המפתח בערך ה-prehash, כדי שחותם מרוחק יידע באיזה מפתח לחתום.

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


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

  2. כדי שתהיה תאימות לספרייה פנימית של Google, ‏ Tink מאפשרת להשתמש בערכות מפתחות שבהן המזהים חוזרים על עצמם. התמיכה הזו תוסר בעתיד. ↩