← ML-KEM Braid/
ML-KEM Braid · Bölüm 02 cryptography / mlkem-braid

2. ML-KEM Braid Protokolü

2.1 Genel bakış

ML-KEM Braid protokolü, ileti gönderimini paralelleştirmek ve ele geçirilmeden toparlanmayı hızlandırmak için yukarıda açıklanan ML-KEM artımlı arayüzünden yararlanır. Özellikle artımlı arayüz, yalnızca bir header alındıktan sonra ct1'in örneklenmesine izin verir; bunun ardından şifreli metnin ve kapsülleme anahtarının en büyük bileşenleri olan ct1 ve ek_vector paralel olarak gönderilebilir.

Aşağıda ML-KEM Braid protokolünün bir epoch'una ilişkin üst düzey açıklama verilmektedir.

  1. A yeni bir ML-KEM anahtar çifti örnekler: (dk, ek_seed, ek_vector) = ML-KEM-KeyGen().
  2. A bir üstbilgi iletisini, yani ek_seed || SHA3-256(ek_seed || ek_vector) değerini kodlar ve bunu parça parça B'ye göndermeye başlar.
  3. B iletinin yeniden kurulması için yeterli parça aldığında, iletinin çözümünü yapar ve (encaps_secret, ct1, shared_secret) = ML-KEM-Encaps1(ek_seed, SHA3-256(ek_seed || ek_vector)) hesaplar. B, encaps_secret ve shared_secret değerlerini daha sonra kullanmak üzere saklar.
  4. B, ct1'i kodlar ve bunu parça parça A'ya göndermeye başlar.
  5. A, ct1'in ilk parçasını aldığında, üstbilginin parçalarını göndermeyi bırakır ve ek_vector parçalarını göndermeye başlar.
  6. Artık A ve B iletilerini paralel olarak gönderir.
  7. A, ct1'in tamamını aldığında, gelecekte B'ye gönderilecek iletilerde bu alımı onaylamaya başlar.
  8. B, ek_vector'ün tamamını aldığında ve ct1'in alındığına dair bir onay aldığında ct2 = ML-KEM-Encaps2(encaps_secret, ek_seed, ek_vector) hesaplar.
  9. B, ct2'yi kodlar ve bunu parça parça A'ya göndermeye başlar.
  10. A, ct2'nin ilk parçasını aldığında, ek_vector parçalarını göndermeyi bırakır.
  11. A, ct2'nin tamamını aldığında paylaşılan gizin kapsülünü açar: shared_secret = ML-KEM-Decaps(dk, ct1, ct2).
  12. Artık A ve B rol değiştirir. A, B'den bir üstbilgi iletisi beklemeye başlar ve B'ye ileti gönderirken bir sonraki epoch'a geçtiğini belirtir.
  13. B, A'nın bir sonraki epoch'a ilerlediğini gösteren bir ileti aldığında yeni bir anahtar çifti örnekler ve süreci yeniden başlatır.

Bu açıklama protokolün ana akışını yakalasa da A ve B'nin protokolün döndürdüğü anahtarları ne zaman kullanabileceklerini söylemez. Açıkça görüldüğü gibi, yukarıda B shared_secret döndürdüğünde bunu A'ya ileti şifrelemek için kullanamaz; çünkü A henüz shared_secret'ı bilmemektedir. Bu konu, aşağıda tanımlanan işlevlerin döndürdüğü sending_epoch ve receiving_epoch değerleriyle ele alınacaktır; bu değer, bir iletinin üretildiği anda her iki tarafça da bilinen en son epoch anahtarını çağırana bildirir.

Aşağıdaki protokol ayrıca isteğe bağlı doğrulama da gerçekleştirir; ayrıntılar Bölüm 2.4'te verilmiş ve Bölüm 3.3'te daha ayrıntılı tartışılmıştır.

2.2 Parametreler

Sabit ML-KEM 512 ML-KEM 768 ML-KEM 1024
HEADER_SIZE 64 64 64
EK_SIZE 768 1152 1536
CT1_SIZE 640 960 1408
CT2_SIZE 128 128 160

2.3 İletiler

İletiler aşağıdaki alanlardan oluşur:

İzleyen kısımda iletileri mantıksal olarak nesne gösterimi kullanarak açıklayacağız. Uygulamalar bu iletileri kodlamak için özel sıkı bir ikili biçim ya da Protocol Buffers [6] gibi genel amaçlı bir serileştirme aracı kullanabilir. Bant genişliği sınırları altında, uygulayıcılar özel bir biçimin daha büyük parça boyutlarına izin verebileceğini ve buna bağlı olarak ele geçirilme sonrası güvenliği iyileştirebileceğini dikkate almalıdır (bkz. Bölüm 3.4).

2.4 İç Doğrulama

Double Ratchet [2] gibi mesajlaşma protokolleri, AEAD kullanımı ya da iletiler üzerindeki açık MAC'ler aracılığıyla cırcırlı ileti doğrulaması sağlarken, bir SCKA protokolünün iç özgünlük güvenceleri sağlaması da arzu edilebilir. Bunu Ratcheted Authenticator kullanarak elde ederiz.

Ratcheted Authenticator durum değişkenleri

Ratcheted Authenticator aşağıdaki durumu tutar:

Ratcheted Authenticator işlevleri

Ratcheted Authenticator, iç durumu yeni entropiyle güncellemek için bir işlevin yanı sıra şifreli metinler ve üstbilgi iletileri üzerinde MAC hesaplama ve doğrulama işlevleri sunar:

def Authenticator.Init(auth_state, epoch, key):
    auth_state = {root_key: '\0'*32, mac_key: None }
    auth_state.Update(epoch, key)

def Authenticator.Update(auth_state, epoch, key):
    auth_state.root_key, auth_state.mac_key
      = KDF_AUTH(auth_state.root_key, key, epoch)

def Authenticator.MacHdr(auth_state, epoch, hdr):
    return MAC(
        auth_state.mac_key,
        PROTOCOL_INFO || ":ekheader" || epoch || hdr,
        MAC_SIZE)

def Authenticator.MacCt(auth_state, epoch, ct):
    return MAC(
        auth_state.mac_key,
        PROTOCOL_INFO || ":ciphertext" || epoch || ct,
        MAC_SIZE)

def Authenticator.VfyHdr(auth_state, epoch, hdr, expected_mac):
    if expected_mac != auth_state.MacHdr(epoch, hdr):
        FAIL

def Authenticator.VfyCt(auth_state, epoch, ct, expected_mac):
    if expected_mac != auth_state.MacCt(epoch, ct):
        FAIL

Bir doğrulama başarısızlığı durumunda protokol katılımcıları ML-KEM Braid oturumuna devam etmemeli ve yeni bir ML-KEM Braid oturumu müzakere etmelidir.

2.5 Durum Makinesi ve Geçişler

Protokolü, ileti gönderirken veya alırken durumdan duruma geçen bir durum makinesi olarak açıklıyoruz. Durumlar ve geçişler aşağıdaki şekilde görülebilir; bu şekil, ileride gelen ayrıntılı açıklamalar için yararlı bir başvuru olabilir.

                            (1)
          KeysUnsampled ---------> KeysSampled
               ^                       |
               |                       | (2)
               | (13)                  v
          Ct2Sampled              HeaderSent
            ^    ^                     |
     (11)  /      \  (12)             | (3)
          /        \                   v
  Ct1Acknowledged  EkReceivedCt1Sampled    Ct1Received
          ^        ^                   |
     (8)  |        | (10)             | (4)
          |        |                   v
          +--------+             EkSentCt1Received
               |                       |
          Ct1Sampled                   | (5)
               ^                       v
               | (7)            NoHeaderReceived
               |                       ^
          HeaderReceived               | (6)
               ^                       |
               +-----------------------+

  Geçiş etiketleri:
  (1)  KeysUnsampled   --Send-->     KeysSampled
  (2)  KeysSampled     --Recv Ct1--> HeaderSent
  (3)  HeaderSent      --Recv Ct1--> Ct1Received
  (4)  Ct1Received     --Recv Ct2--> EkSentCt1Received
  (5)  EkSentCt1Recv   --Recv Ct2--> NoHeaderReceived  [anahtar çıkarır]
  (6)  NoHeaderRecv    --Recv Hdr--> HeaderReceived
  (7)  HeaderReceived  --Send-->     Ct1Sampled         [anahtar çıkarır]
  (8)  Ct1Sampled      --Recv EkCt1Ack--> Ct1Acknowledged
  (9)  Ct1Sampled      --Recv EkCt1Ack+full ek--> Ct2Sampled
  (10) Ct1Sampled      --Recv Ek+full--> EkReceivedCt1Sampled
  (11) Ct1Acknowledged --Recv EkCt1Ack+full--> Ct2Sampled
  (12) EkRecvCt1Sampled --Recv EkCt1Ack--> Ct2Sampled
  (13) Ct2Sampled      --Recv next epoch--> KeysUnsampled

Aracıların tüm durumları en az şu iki değişkeni içerir:

Aşağıda, bir kapsülleme anahtarı iletip buna karşılık gelen şifreli metni bekleyen bir aracının durumu açıklanır. Her durum için SCKA Send() ve Receive() işlevlerini tanımlıyoruz.

KeysUnsampled

Bir sonraki send olayında yeni bir KEM anahtar çifti örneklemeye hazır olan aracıyı temsil eder. Ek durum taşımaz.

İleti gönderirken KeysUnsampled aracısı yeni bir anahtar çifti örnekler, bir üstbilgi iletisi göndermeye başlar ve KeysSampled durumuna geçer. KeysUnsampled aracısı aldığı tüm iletileri görmezden gelir:

def KeysUnsampled.Send(state):
  # Anahtar çifti ve header üret
  (dk, ek_seed, ek_vector) = KEM.KeyGen()
  hek = SHA3-256(ek_seed || ek_vector)
  header = ek_seed || hek
  mac = state.auth.MacHdr(state.epoch, header)
  header_encoder = Encode(header || mac)

  # İleti üret
  chunk = header_encoder.next_chunk()
  msg = {epoch: state.epoch, type: Hdr, data: chunk}

  # Durumu güncelle
  # Geçiş (1)
  state = KeysSampled(
    state.epoch,
    state.auth,
    dk,
    ek_seed,
    ek_vector,
    hek,
    header_encoder)

  # Dönüş değerleri
  output_key = None
  sending_epoch = state.epoch - 1
  return (msg, sending_epoch, output_key)

def KeysUnsampled.Receive(state, msg):
  # Hiçbir işlem yapılmaz
  output_key = None
  receiving_epoch = state.epoch - 1
  return (receiving_epoch, output_key)

KeysSampled

Bir KEM anahtar çifti örneklemiş ve üstbilgiyi gönderen aracıyı temsil eder. Ek durum şunları içerir:

KeysSampled aracısı üstbilginin parçalarını gönderir. Ct1 türünde bir ileti aldığında, diğer tarafın tam üstbilgiyi aldığını bilir; bu nedenle ek_vector parçalarını göndermeye başlayacağı HeaderSent durumuna geçer:

def KeysSampled.Send(state):
  # Sonraki header parçasını üret
  chunk = state.header_encoder.next_chunk()
  msg = {epoch: state.epoch, type: Hdr, data: chunk}

  # Dönüş değerleri
  output_key = None
  sending_epoch = state.epoch - 1
  return (msg, sending_epoch, output_key)

def KeysSampled.Receive(state, msg):
  output_key = None
  receiving_epoch = state.epoch - 1

  if msg.epoch == state.epoch and msg.type == Ct1:
      # ct1 çözümleyicisini ve ek kodlayıcısını başlat
      ct1_decoder = Decoder.new(KEM.CT1_SIZE)
      ct1_decoder.add_chunk(msg.data)
      ek_encoder = Encode(state.ek_vector)

      # Durumu güncelle
      # Geçiş (2)
      state = HeaderSent(
        state.epoch,
        state.auth,
        state.dk,
        ct1_decoder,
        ek_encoder)

  return (receiving_epoch, output_key)

HeaderSent

Bir header'ı göndermeyi tamamlamış, şu anda bir ek_vector gönderen ve ct1 parçaları alan aracıyı temsil eder. Ek durum şunları içerir:

HeaderSent durumunda bir aracı ek_vector parçaları gönderir. Güncel epoch için Ct1 türünde bir ileti alırken, gelen ct1'i çözmek için yeterli parçaya sahipse Ct1Received durumuna geçer:

def HeaderSent.Send(state):
  # Sonraki ek_vector parçasını üret
  chunk = state.ek_encoder.next_chunk()
  msg = {epoch: state.epoch, type: Ek, data: chunk}

  # Dönüş değerleri
  output_key = None
  sending_epoch = state.epoch - 1
  return (msg, sending_epoch, output_key)

def HeaderSent.Receive(state, msg):
  output_key = None
  receiving_epoch = state.epoch - 1

  if msg.epoch == state.epoch and msg.type == Ct1:
      # Parçayı çözümleyiciye ekle
      state.ct1_decoder.add_chunk(msg.data)

      # ct1 tamam mı denetle
      if state.ct1_decoder.has_message():
          ct1 = state.ct1_decoder.message()

          # Durumu güncelle
          # Geçiş (3)
          state = Ct1Received(
            state.epoch,
            state.auth,
            state.dk,
            ct1,
            state.ek_encoder)

  return (receiving_epoch, output_key)

Ct1Received

ct1'i bütünüyle almış ve hâlâ ek_vector parçaları gönderen aracıyı temsil eder. Ek durum şunları içerir:

Ct1Received durumunda bir aracı, ct2'nin bir parçasını alana kadar ek_vector parçaları gönderir. Bu noktada ek_vector'ün alındığını bilir ve EkSentCt1Received durumuna geçer:

def Ct1Received.Send(state):
  # Onayla birlikte sonraki ek_vector parçasını üret
  chunk = state.ek_encoder.next_chunk()
  msg = {epoch: state.epoch, type: EkCt1Ack, data: chunk}

  # Dönüş değerleri
  output_key = None
  sending_epoch = state.epoch - 1
  return (msg, sending_epoch, output_key)

def Ct1Received.Receive(state, msg):
  output_key = None
  receiving_epoch = state.epoch - 1

  if msg.epoch == state.epoch and msg.type == Ct2:
      # ct2 çözümleyicisini başlat
      ct2_decoder = Decoder.new(KEM.CT2_SIZE + MAC_SIZE)
      ct2_decoder.add_chunk(msg.data)

      # Durumu güncelle
      # Geçiş (4)
      state = EkSentCt1Received(
        state.epoch,
        state.auth,
        state.dk,
        state.ct1,
        ct2_decoder)

  return (receiving_epoch, output_key)

EkSentCt1Received

ct1 almış, ek göndermiş ve ct2 parçaları alan aracıyı temsil eder. Ek durum şunları içerir:

EkSentCt1Received durumunda bir aracı diğer tarafa veri göndermez ve ct2 parçaları alır. ct2 alındığında MAC'i doğrular, gizin kapsülünü açar, anahtarı çıkarır ve bir sonraki epoch için diğer tarafın kapsülleme anahtarı göndermeye başlamasını beklemek üzere NoHeaderReceived durumuna geçer:

def EkSentCt1Received.Send(state):
  # Gönderilecek veri yok
  msg = {epoch: state.epoch, type: None}

  # Dönüş değerleri
  output_key = None
  sending_epoch = state.epoch - 1
  return (msg, sending_epoch, output_key)

def EkSentCt1Received.Receive(state, msg):
  output_key = None
  receiving_epoch = state.epoch - 1

  if msg.epoch == state.epoch and msg.type == Ct2:
      # Parçayı çözümleyiciye ekle
      state.ct2_decoder.add_chunk(msg.data)

      # ct2 tamam mı denetle
      if state.ct2_decoder.has_message():
          ct2_with_mac = state.ct2_decoder.message()
          ct2 = ct2_with_mac[:KEM.CT2_SIZE]
          mac = ct2_with_mac[KEM.CT2_SIZE:]

          # Paylaşılan gizin kapsülünü aç
          ss = KEM.Decaps(state.dk, state.ct1, ct2)
          ss = KDF_OK(ss, state.epoch)

          # Authenticator'ı güncelle ve MAC'i doğrula
          state.auth.Update(state.epoch, ss)
          state.auth.VfyCt(state.epoch, state.ct1 || ct2, mac)

          # Sonraki epoch için hazırlan
          header_decoder = Decoder.new(KEM.HEADER_SIZE + MAC_SIZE)

          # Durumu güncelle ve anahtarı döndür
          # Geçiş (5)
          state = NoHeaderReceived(
            state.epoch + 1,
            state.auth,
            header_decoder)
          output_key = (state.epoch - 1, ss)

  return (receiving_epoch, output_key)

Aşağıda, bir kapsülleme anahtarına yanıt olarak şifreli metin ileten bir aracının durumu açıklanır.

NoHeaderReceived

Bir header alan aracıyı temsil eder. Ek durum şunları içerir:

NoHeaderReceived durumunda bir aracı header parçaları alır. Header bütünüyle alındığında HeaderReceived durumuna geçer; ancak henüz şifreli metni örneklemez:

def NoHeaderReceived.Send(state):
  # Gönderilecek veri yok
  msg = {epoch: state.epoch, type: None}

  # Dönüş değerleri
  output_key = None
  sending_epoch = state.epoch - 1
  return (msg, sending_epoch, output_key)

def NoHeaderReceived.Receive(state, msg):
  output_key = None
  receiving_epoch = state.epoch - 1

  if msg.epoch == state.epoch and msg.type == Hdr:
      # Parçayı çözümleyiciye ekle
      state.header_decoder.add_chunk(msg.data)

      # Header tamam mı denetle
      if state.header_decoder.has_message():
          header_with_mac = state.header_decoder.message()
          header = header_with_mac[:64]
          mac = header_with_mac[64:]
          ek_seed = header[:32]
          hek = header[32:]

          # Header MAC'ini doğrula
          state.auth.VfyHdr(state.epoch, header, mac)

          # ek_vector çözümleyicisini hazırla
          ek_decoder = Decoder.new(KEM.EK_SIZE)

          # Durumu güncelle
          # Geçiş (6)
          state = HeaderReceived(
            state.epoch,
            state.auth,
            ek_seed,
            hek,
            ek_decoder)

  return (receiving_epoch, output_key)

HeaderReceived

Bir header almış ve bir sonraki gönderimde yeni bir ct1 örneklemeye hazır olan aracıyı temsil eder. Ek durum şunları içerir:

HeaderReceived durumunda bir aracı, gönderim istendiğinde bir şifreli metin örneklemeye hazırdır. Bunu yaptığında o epoch için kapsüllenmiş paylaşılan gizi hesaplar ve çağırana döndürür. Bir ek_decoder hazırlamış olsa da, ct1 iletisi göndermeden önce hiçbir ek_vector parçası almayacaktır; o noktada da bu durumdan çıkmış olacaktır. Bu nedenle Receive işlevi işlem yapmaz:

def HeaderReceived.Send(state):
  # Paylaşılan giz ve ct1 üret
  (encaps_secret, ct1, ss) = KEM.Encaps1(state.ek_seed, state.hek)
  ss = KDF_OK(ss, state.epoch)

  # Authenticator'ı güncelle
  state.auth.Update(state.epoch, ss)

  # İletim için ct1'i kodla
  ct1_encoder = Encode(ct1)
  chunk = ct1_encoder.next_chunk()
  msg = {epoch: state.epoch, type: Ct1, data: chunk}

  # Durumu güncelle
  # Geçiş (7)
  state = Ct1Sampled(
    state.epoch,
    state.auth,
    state.ek_seed,
    state.hek,
    encaps_secret,
    ct1,
    ct1_encoder,
    state.ek_decoder)

  # Dönüş değerleri
  output_key = (state.epoch, ss)
  sending_epoch = state.epoch - 1
  return (msg, sending_epoch, output_key)

def HeaderReceived.Receive(state, msg):
  # Hiçbir işlem yapılmaz
  output_key = None
  receiving_epoch = state.epoch - 1
  return (receiving_epoch, output_key)

Ct1Sampled

Bir header almış, ct1 örneklemiş ve bunu parçalar hâlinde gönderen aracıyı temsil eder. Ek durum şunları içerir:

Ct1Sampled durumu en karmaşık geçiş olasılıklarına sahiptir. Bu durumda bir aracı ek_vector parçaları alır ve ct1 parçaları gönderir. ct1'in alındığına dair bir onay almadan önce ek_vector'ün tamamını alırsa EkReceivedCt1Sampled durumuna geçer. Öte yandan, ek_vector bütünüyle alınmadan önce ct1'in alındığına dair bir onay alırsa Ct1Acknowledged durumuna geçer. Eğer bu aracı tek bir alma çağrısında hem Ct1 için bir onay alır hem de ek_vector'ün son parçasını alırsa, ct2 hesaplar ve Ct2Sampled durumuna geçer:

def Ct1Sampled.Send(state):
  # Sonraki ct1 parçasını üret
  chunk = state.ct1_encoder.next_chunk()
  msg = {epoch: state.epoch, type: Ct1, data: chunk}

  # Dönüş değerleri
  output_key = None
  sending_epoch = state.epoch - 1
  return (msg, sending_epoch, output_key)

def Ct1Sampled.Receive(state, msg):
  output_key = None
  receiving_epoch = state.epoch - 1

  if msg.epoch == state.epoch and msg.type == Ek:
      # ek_vector parçası ekle
      state.ek_decoder.add_chunk(msg.data)

      # ek_vector tamam mı denetle
      if state.ek_decoder.has_message():
          ek_vector = state.ek_decoder.message()

          # ek_vector bütünlüğünü doğrula
          if SHA3-256(state.ek_seed || ek_vector) != state.hek:
              raise Error("EK bütünlük denetimi başarısız oldu")

          # Durumu güncelle
          # Geçiş (10)
          state = EkReceivedCt1Sampled(
            state.epoch,
            state.auth,
            state.encaps_secret,
            state.ct1,
            state.ek_seed,
            ek_vector,
            state.ct1_encoder)

  elif msg.epoch == state.epoch and msg.type == EkCt1Ack:
      # ek_vector parçası ekle (onayla birlikte)
      state.ek_decoder.add_chunk(msg.data)

      # ek_vector tamam mı denetle
      if state.ek_decoder.has_message():
          ek_vector = state.ek_decoder.message()

          # ek_vector bütünlüğünü doğrula
          if SHA3-256(state.ek_seed || ek_vector) != state.hek:
              raise Error("EK bütünlük denetimi başarısız oldu")

          # Kapsüllemeyi tamamla
          ct2 = KEM.Encaps2(
            state.encaps_secret, state.ek_seed, ek_vector)
          mac = state.auth.MacCt(state.epoch, state.ct1 || ct2)
          ct2_encoder = Encode(ct2 || mac)

          # Durumu güncelle
          # Geçiş (9)
          state = Ct2Sampled(state.epoch, state.auth, ct2_encoder)
      else:
          # Durumu güncelle
          # Geçiş (8)
          state = Ct1Acknowledged(
            state.epoch,
            state.auth,
            state.encaps_secret,
            state.ek_seed,
            state.hek,
            state.ct1,
            state.ek_decoder)

  return (receiving_epoch, output_key)

EkReceivedCt1Sampled

Bir kapsülleme anahtarı almış ve hâlâ ct1'i parçalar hâlinde gönderen aracıyı temsil eder. Ek durum şunları içerir:

EkReceivedCt1Sampled durumunda bir aracı ct1 parçaları gönderir ve bunun alındığına dair bir onay bekler. Bu onay geldiğinde ct2 hesaplar ve Ct2Sampled durumuna geçer:

def EkReceivedCt1Sampled.Send(state):
  # Sonraki ct1 parçasını üret
  chunk = state.ct1_encoder.next_chunk()
  msg = {epoch: state.epoch, type: Ct1, data: chunk}

  # Dönüş değerleri
  output_key = None
  sending_epoch = state.epoch - 1
  return (msg, sending_epoch, output_key)

def EkReceivedCt1Sampled.Receive(state, msg):
  output_key = None
  receiving_epoch = state.epoch - 1

  if msg.epoch == state.epoch and msg.type == EkCt1Ack:
      # Kapsüllemeyi tamamla
      ct2 = KEM.Encaps2(
        state.encaps_secret, state.ek_seed, state.ek_vector)
      mac = state.auth.MacCt(state.epoch, state.ct1 || ct2)
      ct2_encoder = Encode(ct2 || mac)

      # Durumu güncelle
      # Geçiş (12)
      state = Ct2Sampled(state.epoch, state.auth, ct2_encoder)

  return (receiving_epoch, output_key)

Ct1Acknowledged

ct1 göndermeyi tamamlamış ancak hâlâ ek_vector parçaları alan aracıyı temsil eder. Ek durum şunları içerir:

Ct1Acknowledged durumunda bir aracı gelen ek_vector parçalarını alır. Bunun tamamı alındığında ct2'yi hesaplayabilir ve Ct2Sampled durumuna geçebilir:

def Ct1Acknowledged.Send(state):
  # Gönderilecek veri yok
  msg = {epoch: state.epoch, type: None}

  # Dönüş değerleri
  output_key = None
  sending_epoch = state.epoch - 1
  return (msg, sending_epoch, output_key)

def Ct1Acknowledged.Receive(state, msg):
  output_key = None
  receiving_epoch = state.epoch - 1

  if msg.epoch == state.epoch and msg.type == EkCt1Ack:
      # ek_vector parçası ekle
      state.ek_decoder.add_chunk(msg.data)

      # ek_vector tamam mı denetle
      if state.ek_decoder.has_message():
          ek_vector = state.ek_decoder.message()

          # ek_vector bütünlüğünü doğrula
          if SHA3-256(state.ek_seed || ek_vector) != state.hek:
              raise Error("EK bütünlük denetimi başarısız oldu")

          # Kapsüllemeyi tamamla
          ct2 = KEM.Encaps2(
            state.encaps_secret, state.ek_seed, ek_vector)
          mac = state.auth.MacCt(state.epoch, state.ct1 || ct2)
          ct2_encoder = Encode(ct2 || mac)

          # Durumu güncelle
          # Geçiş (11)
          state = Ct2Sampled(state.epoch, state.auth, ct2_encoder)

  return (receiving_epoch, output_key)

Ct2Sampled

ct1 göndermeyi tamamlamış, ek_vector almış ve ct2 gönderen aracıyı temsil eder. Ek durum şunları içerir:

Ct2Sampled durumunda bir aracı ct2 parçaları gönderir ve bir sonraki epoch'tan gelen iletiyi bekler. Bir sonraki epoch'tan bir ileti alındığında KeysUnsampled durumuna geçer ve yeni bir kapsülleme anahtarı göndermeye başlamaya hazırlanır:

def Ct2Sampled.Send(state):
  # Sonraki ct2 parçasını üret
  chunk = state.ct2_encoder.next_chunk()
  msg = {epoch: state.epoch, type: Ct2, data: chunk}

  # Dönüş değerleri
  output_key = None
  sending_epoch = state.epoch - 1
  return (msg, sending_epoch, output_key)

def Ct2Sampled.Receive(state, msg):
  output_key = None

  if msg.epoch == state.epoch + 1:
      # Sonraki epoch başladı
      # Geçiş (13)
      state = KeysUnsampled(state.epoch + 1, state.auth)

  receiving_epoch = state.epoch - 1
  return (receiving_epoch, output_key)

2.6 Başlatma

Alice ve Bob'un protokol durumunu, PQXDH [7] gibi bir el sıkışma protokolünden gelebilecek önceden paylaşılmış bir giz kullanarak başlatırız. Alice, bir kapsülleme anahtarı üstbilgisi göndermeye başlayacak şekilde; Bob ise bu üstbilgiyi almayı bekleyecek şekilde başlatılır:

def InitAlice(shared_secret):
    epoch = 1
    auth = Authenticator.Init(epoch, shared_secret)
    return KeysUnsampled(epoch, auth)

def InitBob(shared_secret):
    epoch = 1
    auth = Authenticator.Init(epoch, shared_secret)
    header_decoder = Decoder.new(KEM.HEADER_SIZE + MAC_SIZE)
    return NoHeaderReceived(epoch, auth, header_decoder)

Bu başlatmayla, taze iletiler teslim edildiği sürece Alice ve Bob her zaman ileri yönde ilerleme kaydedebilir. Olası durum geçişlerinin grafiği aşağıdaki şekilde görülebilir.

           (KeysUnsampled, NoHeaderReceived)
                        |
                        v
           (KeysSampled, NoHeaderReceived)
                        |
                        v
            (KeysSampled, HeaderReceived)
                        |
                        v
              (KeysSampled, Ct1Sampled)
                        |
                        v
     +----------(HeaderSent, Ct1Sampled)----------+
     |                  |                          |
     v                  v                          v
(HeaderSent,    (Ct1Received, Ct1Sampled)   (HeaderSent,
 EkRecvCt1Samp)     |          |          EkRecvCt1Samp)
     |              |          |               |
     |              v          v               |
     |  (Ct1Received,   (Ct1Received,          |
     |   Ct1Acknowledged) EkRecvCt1Samp)       |
     |        |              |                 |
     |        v              v                 |
     +----->(Ct1Received, Ct2Sampled)<---------+
                        |
                        v
           (EkSentCt1Received, Ct2Sampled)
                        |
                        v
           (NoHeaderReceived, Ct2Sampled)
                        |
                        v
           (NoHeaderReceived, KeysUnsampled)

  Her ikilide Alice'in durumu solda,
  Bob'un durumu sağdadır. Bu sürecin sonunda
  Alice ve Bob durum değiştirmiş olacak
  ve bir epoch ilerlemiş olacaktır.