4.1. Genel bakış
Bu bölüm, Double Ratchet'ın başlık şifreleme varyantını açıklamaktadır.
Mesaj başlıkları ratchet açık anahtarlarını ve (PN, N) değerlerini içerir. Bazı durumlarda, bir dinleyicinin hangi mesajların hangi oturumlara ait olduğunu veya bir oturum içindeki mesaj sıralamasını anlayamaması için başlıkları şifrelemek istenebilir.
Başlık şifreleme ile her taraf, hem gönderme hem de alma yönleri için simetrik bir başlık anahtarı ve sonraki başlık anahtarı saklar. Gönderme başlık anahtarı, mevcut gönderme zinciri için başlıkları şifrelemede kullanılır.
Bir alıcı mesaj aldığında önce bu mesajı, ilgili Double Ratchet oturumuyla ilişkilendirmelidir (farklı taraflarla farklı oturumları olduğu varsayımıyla). Bunun nasıl yapılacağı bu belgenin kapsamı dışındadır; ancak Pond protokolü bazı fikirler sunmaktadır [6].
Mesajı bir oturumla ilişkilendirdikten sonra alıcı, başlığı o oturumun alma başlık anahtarı, sonraki başlık anahtarı ve atlanmış mesajlara karşılık gelen başlık anahtarları ile çözmeye çalışır. Sonraki başlık anahtarıyla başarılı çözümleme, alıcının bir DH adımı gerçekleştirmesi gerektiğini gösterir. Bir DH adımı sırasında sonraki başlık anahtarları mevcut başlık anahtarlarının yerine geçer ve yeni sonraki başlık anahtarları kök KDF'den ek çıktı olarak alınır.
Aşağıdaki diyagramda Alice, Bob'un ratchet açık anahtarıyla ve başlangıç kök anahtarı, gönderme başlık anahtarı (HK) ve alma yönündeki sonraki başlık anahtarı (NHK) için paylaşılan sırlarla başlatılmıştır. Başlatmanın bir parçası olarak Alice kendi ratchet anahtar çiftini üretir ve yeni bir kök anahtarı, gönderme zinciri anahtarı ve gönderme yönündeki sonraki başlık anahtarını (NHK) türetmek için kök zincirini günceller:
Ratchet | Kök | Gönderme | Alma
| | |
[A]<--- | (RK) | Başlangıç paylaşılan sırları:
o | | | (HK) (NHK) | (NHK)
|---->[ ]--> [ ] | |
[A|a] | RK CK NHK HK | NHK
Alice ilk mesajı A1'i gönderdiğinde, başlığını başlatma sırasında aldığı gönderme başlık anahtarıyla şifreler:
Ratchet | Kök | Gönderme | Alma
| | |
[A]<--- | (RK) | |
o | | | |
|---->[ ]--> [ ] | |
[A|a] | RK CK NHK (HK) | NHK
| |[ ] |
| CK (A1) |
Alice daha sonra Bob'dan B1 yanıtını alırsa, bunun başlığı başlatma sırasında aldığı alma yönündeki sonraki başlık anahtarıyla şifrelenmiş olacaktır. Alice, sonraki başlık anahtarlarını mevcut başlık anahtarlarına taşıyan ve yeni sonraki başlık anahtarları üreten bir DH adımı uygular:
Ratchet | Kök | Gönderme | Alma
| | |
[A]<--- | (RK) | |
o | | | |
|---->[ ]--> [ ] | |
[A|a] | RK CK NHK HK | NHK
| |[ ] |
| CK A1 |
o---->[ ] | | | |
(B1)<-- | [ ] | CK NHK (HK) <-- NHK mevcut anahtara geçti
o---->[ ] | | | |[ ] |
[b|B] |RK CK NHK HK CK (B1) |
Alice daha sonra A2 mesajını gönderir; ardından mevcut alma başlık anahtarını kullanan ve B1 mesajında aldığıyla aynı ratchet açık anahtarını içeren B2 mesajını alır. Alice daha sonra A3 ve A4 mesajlarını gönderir. Gönderilen ve alınan tüm mesajlarda mevcut başlık anahtarları kullanılır:
Ratchet | Kök | Gönderme | Alma
| | |
[A]<--- | (RK) | |
o | | | |
|---->[ ]--> [ ] | |
[A|a] | RK CK NHK HK | NHK
| |[ ] |
| CK A1 |
o---->[ ] | | | |
(B1)<-- | [ ] | CK NHK (HK)
o---->[ ] | | |[ ] |[ ] |
[b|B] |RK CK (A2) CK B1 |
| |[ ] |[ ] |
| CK (A3) CK B2 |
| |[ ] | |
| CK (A4) | |
Alice daha sonra Bob'un sonraki ratchet anahtarını içeren ve başlığı alma yönündeki sonraki başlık anahtarıyla şifrelenmiş olan B3 mesajını alır. Başlığın sonraki başlık anahtarıyla başarılı biçimde çözülmesi bir DH adımını tetikleyecektir. Alice daha sonra aynı ratchet anahtarı ve başlık anahtarıyla gelen B4 mesajını alır, ardından A5 mesajını gönderir. Alice'in son durumu aşağıdaki gibi olacaktır:
Ratchet | Kök | Gönderme | Alma
| | |
[A]<--- | (RK) | |
o | | | |
|---->[ ]--> [ ] | |
[A|a] | RK CK NHK HK | NHK
| |[ ] |
| CK A1 |
o---->[ ] | | | |
(B1)<-- | [ ] | CK NHK HK
o---->[ ] | | |[ ] |[ ] |
[b|B] |RK CK A2 CK B1 |
| |[ ] |[ ] |
| CK A3 CK B2 |
| |[ ] | |
B3'ten önce | CK A4 | |
- - - - - - + - - - - - - - -+ - - - - -+- - -
o---->[ ] | | | |
(B3)<-- |[ ] | CK NHK HK
o---->[ ] || | |[ ] |
[d|D] |RK CK NHK HK |CK B3 |
| |[ ] |CK B4 |
| CK A5 | | |
4.2. Dış işlevler
Başlık şifreleme için ek işlevler gerekir:
HENCRYPT(hk, plaintext): plaintext'in, başlık anahtarı hk ile yapılan AEAD şifrelemesini döndürür. Aynı hk değeri tekrar tekrar kullanılacağından, AEAD nonce'u ya durum tutan ve tekrar etmeyen bir değer olmalı ya da en az 128 bit entropiyle seçilmiş rastgele ve tekrar etmeyen bir değer olmalıdır.
HDECRYPT(hk, ciphertext): ciphertext'in, başlık anahtarı hk ile yapılan doğrulanmış çözümünü döndürür. Doğrulama başarısız olursa veya başlık anahtarı hk boşsa (None), None döndürür.
KDF_RK_HE(rk, dh_out): Kök anahtarı rk ile anahtarlanmış bir KDF'nin, bir Diffie-Hellman çıktısı dh_out üzerine uygulanmasının sonucu olarak yeni bir kök anahtarı, zincir anahtarı ve sonraki başlık anahtarı döndürür.
4.3. Durum değişkenleri
Ek durum değişkenleri gerekir:
- HKs, HKr: Gönderme ve alma için 32 baytlık Başlık Anahtarları
- NHKs, NHKr: Gönderme ve alma için 32 baytlık Sonraki Başlık Anahtarları
Aşağıdaki değişkenin tanımı değişir:
- MKSKIPPED: Atlanan mesaj anahtarlarının, başlık anahtarı ve mesaj numarasıyla indekslenen sözlüğü. Çok fazla öğe saklanırsa bir istisna fırlatır.
4.4. Başlatma
Başlık anahtarlarını başlatmak için bazı ek paylaşılan sırlar kullanılmalıdır:
Alice'in gönderme başlık anahtarı ile Bob'un alma yönündeki sonraki başlık anahtarı aynı değere ayarlanmalıdır; böylece Alice'in ilk mesajı Bob için bir DH adımını tetikler.
Alice'in alma yönündeki sonraki başlık anahtarı ile Bob'un gönderme yönündeki sonraki başlık anahtarı aynı değere ayarlanmalıdır; böylece Bob'un ilk DH adımından sonra Bob'un sonraki mesajı Alice için bir DH adımını tetikler.
Alice ile Bob, SK, Bob'un ratchet açık anahtarı ve bu ek değerler üzerinde anlaştıktan sonra Alice RatchetInitAliceHE() çağrısını, Bob ise RatchetInitBobHE() çağrısını yapar:
function RatchetInitAliceHE(state, SK, bob_dh_public_key, shared_hka, shared_nhkb):
state.DHRs = GENERATE_DH()
state.DHRr = bob_dh_public_key
state.RK, state.CKs, state.NHKs = KDF_RK_HE(SK, DH(state.DHRs, state.DHRr))
state.CKr = None
state.Ns = 0
state.Nr = 0
state.PN = 0
state.MKSKIPPED = {}
state.HKs = shared_hka
state.HKr = None
state.NHKr = shared_nhkb
function RatchetInitBobHE(state, SK, bob_dh_key_pair, shared_hka, shared_nhkb):
state.DHRs = bob_dh_key_pair
state.DHRr = None
state.RK = SK
state.CKs = None
state.CKr = None
state.Ns = 0
state.Nr = 0
state.PN = 0
state.MKSKIPPED = {}
state.HKs = None
state.NHKs = shared_nhkb
state.HKr = None
state.NHKr = shared_hka
4.5. Mesajların şifrelenmesi
Başlık şifrelemeli mesajları şifrelemek için RatchetEncryptHE() işlevi çağrılır:
function RatchetEncryptHE(state, plaintext, AD):
state.CKs, mk = KDF_CK(state.CKs)
header = HEADER(state.DHRs, state.PN, state.Ns)
enc_header = HENCRYPT(state.HKs, header)
state.Ns = state.Ns + 1
return enc_header, ENCRYPT(mk, plaintext, CONCAT(AD, enc_header))
4.6. Mesajların şifresinin çözülmesi
Başlık şifrelemeli mesajların şifresini çözmek için RatchetDecryptHE() çağrılır:
function RatchetDecryptHE(state, enc_header, ciphertext, AD):
plaintext = TrySkippedMessageKeysHE(state, enc_header, ciphertext, AD)
if plaintext is not None:
return plaintext
header, dh_ratchet = DecryptHeader(state, enc_header)
if dh_ratchet:
SkipMessageKeysHE(state, header.pn)
DHRatchetHE(state, header)
SkipMessageKeysHE(state, header.n)
state.CKr, mk = KDF_CK(state.CKr)
state.Nr = state.Nr + 1
return DECRYPT(mk, ciphertext, CONCAT(AD, enc_header))
function TrySkippedMessageKeysHE(state, enc_header, ciphertext, AD):
for ((hk, n), mk) in state.MKSKIPPED.items():
header = HDECRYPT(hk, enc_header)
if header is not None and header.n == n:
remove state.MKSKIPPED[hk, n]
return DECRYPT(mk, ciphertext, CONCAT(AD, enc_header))
return None
function DecryptHeader(state, enc_header):
header = HDECRYPT(state.HKr, enc_header)
if header is not None:
return header, False
header = HDECRYPT(state.NHKr, enc_header)
if header is not None:
return header, True
signal Error
function SkipMessageKeysHE(state, until):
if state.Nr + MAX_SKIP < until:
signal Error
if state.CKr is not None:
while state.Nr < until:
state.CKr, mk = KDF_CK(state.CKr)
state.MKSKIPPED[state.HKr, state.Nr] = mk
state.Nr = state.Nr + 1
function DHRatchetHE(state, header):
state.PN = state.Ns
state.Ns = 0
state.Nr = 0
state.HKs = state.NHKs
state.HKr = state.NHKr
state.DHRr = header.dh
state.RK, state.CKr, state.NHKr = KDF_RK_HE(state.RK, DH(state.DHRs, state.DHRr))
state.DHRs = GENERATE_DH()
state.RK, state.CKs, state.NHKs = KDF_RK_HE(state.RK, DH(state.DHRs, state.DHRr))