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.
- A yeni bir ML-KEM anahtar çifti örnekler: (dk, ek_seed, ek_vector) = ML-KEM-KeyGen().
- 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.
- 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.
- B, ct1'i kodlar ve bunu parça parça A'ya göndermeye başlar.
- 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.
- Artık A ve B iletilerini paralel olarak gönderir.
- A, ct1'in tamamını aldığında, gelecekte B'ye gönderilecek iletilerde bu alımı onaylamaya başlar.
- 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.
- B, ct2'yi kodlar ve bunu parça parça A'ya göndermeye başlar.
- A, ct2'nin ilk parçasını aldığında, ek_vector parçalarını göndermeyi bırakır.
- A, ct2'nin tamamını aldığında paylaşılan gizin kapsülünü açar: shared_secret = ML-KEM-Decaps(dk, ct1, ct2).
- 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.
- 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
KEM: Artımlı arayüz sunan IND-CPA güvenli bir Key Encapsulation Mechanism. Bu belge için bu, ML-KEM-512, ML-KEM-768 veya ML-KEM-1024'ten biri olacaktır. KEM, Bölüm 1.2'de açıklanan artımlı arayüzü sunar.
Sabitler: KEM ile ilişkili birkaç sabit daha vardır ve protokol açıklamasında gereklidir:
| 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 |
Encode/Decode: Uzun bir iletiyi, alıcı bu parçalardan yeterli sayıda aldığında, sıra ve düşen kod sözcüklerinden bağımsız olarak özgün iletinin yeniden kurulmasını sağlayacak şekilde, bir kod sözcükleri ya da parçalar akışına kodlayabilen bir silme kodu veya fountain code. w baytlık parça boyutu için GF(2^16)^(w/2) üzerinde Reed-Solomon tabanlı silme kodları önerilir.
- Encode(byte_array) -> encoder: byte_array'i yeniden kurmak için çözülebilecek kod sözcükleri ya da chunks akışı üreten durumlu bir kodlama nesnesi döndürür. Bu kod sözcüklerine encoder.next_chunk() yöntemi çağrılarak erişilir.
- Decoder.new(message_size) -> decoder: Tek bir kodlayıcı tarafından üretilen kod sözcükleri kümesinden message_size uzunluğunda bir ileti çözecek durumlu bir çözümleyici nesnesi döndürür. Şu işlevleri açığa çıkarır:
- decoder.add_chunk(chunk): Bir kod sözcüğünü çözümleyicinin durumuna ekler.
- decoder.has_message() -> bool: Çözümleyici iletinin yeniden kurulması için yeterli sayıda kod sözcüğü aldığında doğru döndürür.
- decoder.message() -> maybe_byte_array: Mümkünse yeniden kurulmuş iletiyi döndürür; aksi takdirde Null döndürür.
EPOCH_TYPE: Epoch'ları temsil etmek için kullanılan işaretsiz tamsayı türü. İşaretsiz 64 bitlik tamsayılar kullanmanızı öneriyoruz.
ToBytes(epoch): Bir epoch'u bayt dizisi olarak temsil eder. EPOCH_TYPE 64 bitlik işaretsiz tamsayı olduğunda big-endian kodlama kullanımı önerilir.
MAC(mac_key, msg): İleti doğrulama kodu. HMAC-SHA256 önerilir.
MAC_SIZE: MAC çıktısının boyutu, bayt cinsinden.
PROTOCOL_INFO: Bir protokol tanımlayıcısı, KEM'in dizge gösterimi ve MAC'in dizge gösteriminin "
_" ayırıcısıyla birleştirilmesidir; örneğin "MyProtocol_MLKEM768_SHA-256". ML-KEM Braid parametrelerinin dizge gösterimleri uygulayıcı tarafından tanımlanır.KDF_AUTH(root_key, update_key, epoch): [5]'teki HKDF algoritması kullanılarak hash ile elde edilen 64 bayt çıktı; girdiler şunlardır:
- HKDF input key material = update_key
- HKDF salt = root_key
- HKDF info = PROTOCOL_INFO || ":Authenticator Update" || ToBytes(epoch)
- HKDF length = 64
KDF_OK(shared_secret, epoch): [5]'teki HKDF algoritması kullanılarak hash ile elde edilen 32 bayt çıktı; girdiler şunlardır:
- HKDF input key material = shared_secret
- HKDF salt = Uzunluğu bayt cinsinden hash çıktı uzunluğuna eşit olan, sıfırlarla doldurulmuş bir bayt dizisi.
- HKDF info = PROTOCOL_INFO || ":SCKA Key" || ToBytes(epoch)
- HKDF length = 32
2.3 İletiler
İletiler aşağıdaki alanlardan oluşur:
- epoch (işaretsiz tamsayı): Müzakere edilmekte olan güncel epoch
- type (enum): {None, Hdr, Ek, EkCt1Ack, Ct1Ack, Ct1, Ct2} değerlerinden biri; anlamları şunlardır:
- None: Yük yoktur
- Hdr: Yük, header'ın bir chunk'ını içerir.
- Ek: Yük, kapsülleme anahtarının bir chunk'ını içerir.
- EkCt1Ack: Yük, kapsülleme anahtarının bir chunk'ını içerir ve gönderen ct1'i bütünüyle almıştır.
- Ct1Ack: Yük yoktur; ancak gönderen ct1'i bütünüyle almıştır.
- Ct1: Yük, ct1'in bir chunk'ını içerir.
- Ct2: Yük, ct2'nin bir chunk'ını içerir.
- data (baytlar, isteğe bağlı): type değeri { None, Ct1Ack } kümesinden biri olmadığında silme kodu parçası
İ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:
- root_key: 32 baytlık bir değer.
- mac_key: MAC ile kullanım için 32 baytlık bir anahtar.
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:
- epoch: Müzakere edilmekte olan anahtarın epoch'unu tanımlayan işaretsiz bir tamsayı.
- auth: Bir Authenticator nesnesi.
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:
- dk: bir KEM kapsül açma anahtarı
- ek_vector: bir KEM kapsülleme anahtarının vektör kısmı
- header_encoder
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:
- dk: bir KEM kapsül açma anahtarı
- ct1_decoder
- ek_encoder
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:
- dk: bir KEM kapsül açma anahtarı
- ct1: bir KEM şifreli metninin sıkıştırılmış açık anahtar kısmı
- ek_encoder
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:
- dk: bir KEM kapsül açma anahtarı
- ct1: bir KEM şifreli metninin sıkıştırılmış açık anahtar kısmı
- ct2_decoder
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:
- header_decoder
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:
- ek_seed: bir KEM kapsülleme anahtarının tohumu
- hek: ek_seed || ek_vector değerinin SHA3 özeti
- ek_decoder
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:
- ek_seed: bir KEM kapsülleme anahtarının tohumu
- hek: ek_seed || ek_vector değerinin SHA3 özeti
- encaps_secret: bir KEM şifreli metnini kapsüllemek için kullanılan gizli malzeme
- ct1: bir KEM şifreli metninin sıkıştırılmış açık anahtar kısmı
- ct1_encoder
- ek_decoder
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:
- encaps_secret: bir KEM şifreli metnini kapsüllemek için kullanılan gizli malzeme
- ct1: bir KEM şifreli metninin sıkıştırılmış açık anahtar kısmı
- ek_seed
- ek_vector
- ct1_encoder
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:
- ek_seed: bir KEM kapsülleme anahtarının tohumu
- hek: ek_seed || ek_vector değerinin SHA3 özeti
- encaps_secret: bir KEM şifreli metnini kapsüllemek için kullanılan gizli malzeme
- ct1: bir KEM şifreli metninin sıkıştırılmış açık anahtar kısmı
- ek_decoder
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:
- ct2_encoder
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.