← Double Ratchet
Bölüm 4 / 10

4. Başlık şifrelemeli Double Ratchet

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))