Kembali ke Beranda
Kriptografi

Referensi Implementasi Enkripsi

Enkripsi yang berjalan lebih sempit daripada enkripsi yang sudah ditulis, dan selisih itulah alasan halaman ini ada. Sealed box libsodium adalah satu-satunya enkripsi pesan yang dijalankan Railgun — dan itu hanya berjalan pada jalur klien tempat modul native libsignal tidak ada. Pada build desktop Electron, tempat libsignal memang dimuat, penangan yang mengenkripsi pesan langsung masih berupa placeholder yang meng-encode teks biasa ke base64. Pesan komunitas dan kanal tidak dienkripsi pada jalur mana pun. Protokol bergaya Signal yang dijelaskan pada paruh kedua halaman ini sudah ditulis dan ditinjau, dan tidak terhubung ke klien mana pun yang dapat Anda unduh.

Ini adalah referensi arsitektur, bukan audit keamanan independen. Bagian 1 dan 2 menjelaskan apa yang benar-benar dirilis. Bagian 4 sampai 7 menjelaskan desain yang sedang dituju Railgun dan diberi label Direncanakan di sepanjang halaman. Tidak ada yang di sini boleh dibaca sebagai klaim bahwa klien Railgun saat ini menerapkan properti yang ditandai direncanakan.

Apa yang dirilis hari ini

JalurMekanismeStatus
Pesan langsung — build browser dan pengembanganSealed box libsodium — kesepakatan kunci X25519, enkripsi terautentikasi XSalsa20-Poly1305. Dipilih ketika modul native libsignal tidak ada.Terenkripsi
Pesan langsung — build desktop Electronlibsignal dimuat, sehingga encryptDm dialihkan ke penangan IPC Electron — sebuah placeholder yang meng-encode teks biasa ke base64. Relai menerima teks yang dapat dibaca.Tidak dienkripsi
Pesan komunitas & kanalDikirim sebagai teks biasa yang dibungkus base64. Enkripsi kanal sudah ditulis tetapi mengembalikan penanda teks biasa.Tidak dienkripsi
X3DH + Double RatchetDiimplementasikan terhadap libsignal, diimpor hanya sebagai tipe. Tidak ada jalur runtime yang menginstansiasinya.Direncanakan
Penyimpanan kunci privatKunci dibuat dan disimpan di perangkat. Tidak ada jalur kode yang mengirimkan materi kunci privat atau kunci identitas.Diimplementasikan

1. Sealed box — enkripsi yang berjalan

Di tempat Railgun benar-benar mengenkripsi pesan langsung, ia melakukannya dengan sealed box libsodium — konstruksi crypto_box_seal. Konstruksi ini menggabungkan kesepakatan kunci X25519 dengan enkripsi terautentikasi XSalsa20-Poly1305. Ini kriptografi nyata yang sudah ditinjau dengan baik. Ini juga konstruksi yang sengaja dibuat sederhana, dan batasannya penting. Klien mana yang sampai ke sana dibahas di bagian 2: build browser dan pengembangan sampai ke sana; build desktop Electron tidak.

Bagaimana sealed box dibangun

// Sender has only the recipient's public key, PK_r

(pk_e, sk_e) = crypto_box_keypair() // fresh, single-use

nonce = BLAKE2b(pk_e || PK_r) // derived, not random

ct = crypto_box(plaintext, nonce, PK_r, sk_e)

sealed = pk_e || ct // ephemeral key is prepended

erase(sk_e)

Penerima memulihkan kunci publik efemeral dari bagian depan pesan, menghitung ulang rahasia bersama yang sama dengan kunci privatnya sendiri, lalu membuka kotak itu. Tag Poly1305 berarti ciphertext yang dimodifikasi gagal dibuka, bukan terdekripsi menjadi sampah.

Apa yang Anda dapatkan

  • Kerahasiaan terhadap relai dan siapa pun di jalur jaringan.
  • Integritas — perusakan terdeteksi, bukan didekripsi secara diam-diam.
  • Anonimitas pengirim: kunci efemeral tidak mengungkapkan apa pun tentang siapa yang mengirimnya.
  • Tidak ada penyiapan sesi, sehingga tetap bekerja saat penerima sedang offline.

Apa yang tidak Anda dapatkan

  • Tidak ada forward secrecy. Kunci privat jangka panjang penerima membuka setiap pesan yang pernah dikirim kepadanya. Jika kunci itu bocor, ciphertext yang telah direkam ikut bocor bersamanya.
  • Tidak ada autentikasi pengirim. Sealed box bersifat anonim secara konstruksi; ia tidak membuktikan apa pun tentang siapa yang menulisnya.
  • Tidak ada pemulihan pasca-kompromi. Tidak ada ratchet untuk melangkah keluar dari kunci yang telah dikompromikan.

Ketiga celah itu persis yang hendak ditutup oleh X3DH dan Double Ratchet, dan itulah sebabnya pekerjaan tersebut sedang berjalan. Sampai itu terwujud, ringkasan jujur tentang DM sealed box adalah: kuat terhadap pengamat jaringan pasif dan terhadap relai, lemah terhadap penyerang yang pada akhirnya memperoleh kunci perangkat penerima. Ringkasan itu hanya berlaku di tempat kode ini benar-benar berjalan.

2. Apa yang tidak dienkripsi

Pesan komunitas dan kanal — sebagian besar dari apa yang sebenarnya diketik kebanyakan pengguna Railgun — tidak dienkripsi pada jalur klien mana pun hari ini. Fungsi enkripsi kanal memang ada, dan ia jujur tentang dirinya sendiri: ia mencatat peringatan dan mengembalikan teks biasa yang dibungkus base64 di balik sebuah string penanda, sehingga tidak ada pihak di hilir yang mengiranya sebagai ciphertext.

// What encryptChannel actually returns

logger.warn("Channel encryption not yet implemented")

ciphertext = base64("[DEVCRYPTO:PLAINTEXT]" + plaintext)

Konsekuensi praktisnya: untuk konten komunitas dan kanal, server Railgun berada pada posisi yang sama dengan server obrolan biasa mana pun. Server dapat membaca apa yang Anda tulis di sana. Distribusi kunci grup — satu kunci per kanal yang diberikan kepada anggota di dalam sealed box — adalah perbaikan yang direncanakan dan belum dirilis.

Pesan langsung pada build desktop Electron

Celah kedua adalah pesan langsung pada klien yang paling banyak dijalankan orang, dan hal ini mudah dinyatakan terbalik, jadi berikut logika pemilihannya persis seperti yang dieksekusi kode. initCrypto() menanyakan kepada proses utama apakah libsignal berhasil dimuat. @signalapp/libsignal-client adalah dependensi nyata dan diperlukan saat startup, sehingga pada build Electron yang dirilis ia memang dimuat — dan jawaban itulah yang memilih implementasi IPC Electron alih-alih implementasi sealed box. encryptDm miliknya meneruskan ke penangan crypto:encryptDm, yang merupakan sebuah placeholder:

// crypto:encryptDm, Electron main process

const plaintextBytes = new TextEncoder().encode(plaintext)

ciphertext: toBase64(plaintextBytes) // not encryption

Jadi sealed box di bagian 1 baru tercapai ketika libsignal tidak ada — di browser dan dalam pengembangan. Pada klien desktop, pesan langsung sampai ke relai sebagai teks biasa yang di-encode base64. libsignal dimuat di sana untuk menghasilkan kunci identitas dan bundel prekey; ia tidak membangun sesi apa pun dan tidak melakukan ratchet apa pun — crypto:ensureDmSession adalah log debug tanpa isi.

3. Curve25519 — kurva yang mendasarinya

Baik sealed box yang dirilis maupun desain X3DH yang tidak dirilis bertumpu pada kurva eliptik yang sama: Curve25519, dirancang oleh Daniel J. Bernstein untuk pertukaran kunci Diffie-Hellman berkecepatan tinggi. Bagian ini menjelaskan kurvanya sendiri, dan berlaku untuk keduanya.

Persamaan Kurva

y² = x³ + 486662x² + x

over the prime field 𝔽ₚ where p = 2²⁵⁵ − 19

Bilangan prima 2²⁵⁵ − 19 dipilih karena memungkinkan aritmetika modular yang sangat cepat. Koefisien 486662 dipilih sebagai nilai terkecil yang menghasilkan kurva aman dengan properti keamanan yang diperlukan.

Pembuatan Kunci

1. Kunci privat: Hasilkan 32 byte acak menggunakan CSPRNG (pembangkit bilangan pseudoacak yang aman secara kriptografis). Jepit kunci dengan membersihkan 3 bit terendah dan bit tertinggi, serta menyetel bit tertinggi kedua. Ini memastikan kunci merupakan kelipatan 8 dan berada dalam rentang yang valid.

a = random(32 bytes)

a[0] &= 248 // Clear low 3 bits

a[31] &= 127 // Clear high bit

a[31] |= 64 // Set second-highest bit

2. Kunci publik: Hitung perkalian skalar kunci privat dengan titik basis G = 9.

A = a · G // Scalar multiplication on Curve25519

Keamanannya bergantung pada Masalah Logaritma Diskret Kurva Eliptik (ECDLP): diberikan A dan G, secara komputasi tidak mungkin memulihkan a. Curve25519 memberikan sekitar 128 bit keamanan.

Pertukaran Kunci Diffie-Hellman

Dua pihak (Alice dan Bob) dapat menghitung rahasia bersama tanpa pernah mengirimkannya:

Alice: a (private), A = a·G (public)

Bob: b (private), B = b·G (public)

Alice computes: S = a · B = a · (b·G)

Bob computes: S = b · A = b · (a·G)

Both arrive at S = ab·G — the same shared secret

Penyadap yang mengetahui A dan B (keduanya publik) tidak dapat menghitung S tanpa memecahkan ECDLP. Inilah asumsi Computational Diffie-Hellman (CDH).

Sealed box adalah pertukaran ini dengan satu sisi dibuat efemeral lalu dibuang. X3DH, di bawah, adalah pertukaran ini yang dilakukan empat kali atas pasangan kunci yang berbeda sehingga hasilnya mengautentikasi kedua pihak sekaligus menyembunyikan isinya.

4. X3DH — Extended Triple Diffie-Hellman

DIRENCANAKAN
Ditulis terhadap libsignal dan dapat ditinjau di dalam basis kode. Ia diimpor hanya sebagai tipe — tidak ada klien yang dirilis membangunnya. Perlakukan semua yang ada di bagian ini sebagai desain yang dituju, bukan perilaku saat ini.

X3DH memecahkan masalah yang sulit: bagaimana dua orang membangun sesi terenkripsi ketika salah satunya sedang offline? Diffie-Hellman tradisional mengharuskan kedua pihak online. X3DH menggunakan bundel kunci yang diunggah lebih dulu untuk memungkinkan kesepakatan kunci secara asinkron.

Jenis Kunci

Kunci Identitas (IK)

Pasangan kunci Curve25519 jangka panjang. Dibuat sekali, mengidentifikasi pengguna secara kriptografis. Tidak pernah berubah kecuali pengguna mendaftar ulang.

Pra-Kunci Bertanda Tangan (SPK)

Pasangan kunci Curve25519 jangka menengah, ditandatangani oleh Kunci Identitas. Dirotasi secara berkala. Tanda tangannya membuktikan bahwa kunci itu milik pemegang kunci identitas.

Pra-Kunci Sekali Pakai (OPK)

Pasangan kunci Curve25519 efemeral yang diunggah dalam batch. Masing-masing dipakai sekali lalu dihapus. Memberikan lapisan forward secrecy tambahan untuk pesan awal.

Kunci Efemeral (EK)

Pasangan kunci Curve25519 baru yang dibuat pengirim untuk setiap sesi baru. Tidak pernah disimpan di sisi server.

Handshake X3DH

Ketika Alice ingin mengirim pesan kepada Bob (yang mungkin sedang offline), ia mengambil bundel pra-kunci Bob dari server dan melakukan empat komputasi Diffie-Hellman:

// Alice has: IKₐ (her identity key), EKₐ (fresh ephemeral)

// Bob published: IKᵦ, SPKᵦ, OPKᵦ

DH1 = DH(IKₐ, SPKᵦ) // her identity ↔ his signed pre-key

DH2 = DH(EKₐ, IKᵦ) // her ephemeral ↔ his identity

DH3 = DH(EKₐ, SPKᵦ) // her ephemeral ↔ his signed pre-key

DH4 = DH(EKₐ, OPKᵦ) // her ephemeral ↔ his one-time pre-key

SK = KDF(DH1 || DH2 || DH3 || DH4)

// SK is the shared secret, derived via HKDF-SHA-256

Mengapa empat operasi DH?

  • DH1 memberikan autentikasi timbal balik (kedua kunci identitas terlibat)
  • DH2 memastikan kunci efemeral terikat pada identitas Bob
  • DH3 memberikan forward secrecy melalui kunci efemeral
  • DH4 memberikan forward secrecy tambahan. Jika tidak ada OPK yang tersedia, X3DH tetap bekerja hanya dengan DH1–DH3

DH1 adalah operasi yang tidak dimiliki sealed box. Itulah sebabnya jalur yang dirilis tidak dapat memberi tahu Anda dari siapa sebuah pesan berasal.

KDF — Fungsi Derivasi Kunci

Keluaran DH mentah digabungkan menggunakan HKDF-SHA-256 (Fungsi Derivasi Kunci berbasis HMAC). HKDF mengekstrak entropi dari keluaran DH yang digabungkan dan mengembangkannya menjadi rahasia bersama yang acak seragam:

PRK = HKDF-Extract(salt="", input=DH1||DH2||DH3||DH4)

SK = HKDF-Expand(PRK, info="RailGunX3DH", length=32)

5. Algoritma Double Ratchet

DIRENCANAKAN
Tidak ada rilis Railgun yang melakukan ratchet kunci hari ini. Pada jalur sealed box setiap pesan langsung dienkripsi ke kunci penerima jangka panjang yang sama; pada jalur desktop Electron pesan itu tidak dienkripsi sama sekali.

Setelah X3DH membangun rahasia bersama awal, Double Ratchet mengambil alih. Ia menggunakan dua "ratchet" yang saling mengunci untuk menurunkan kunci unik baru bagi setiap pesan. Desainnya melindungi pesan-pesan sebelumnya dan dimaksudkan untuk memulihkan perlindungan bagi pesan-pesan berikutnya setelah satu langkah ratchet yang baru.

Ratchet DH (Asimetris)

Setiap kali arah percakapan berubah (Alice → Bob, lalu Bob → Alice), terjadi pertukaran kunci Diffie-Hellman baru menggunakan kunci efemeral yang baru. Ini "me-ratchet" kunci akar maju ke depan:

dh_out = DH(my_ratchet_key, their_ratchet_key)

root_key, chain_key = KDF(root_key, dh_out)

Inilah yang akan memberikan keamanan pasca-kompromi: penyerang yang mencuri state saat ini kehilangan akses begitu langkah ratchet DH baru terjadi.

Ratchet Simetris (Rantai Hash)

Di antara langkah-langkah ratchet DH, sebuah ratchet berbasis hash menurunkan kunci pesan baru dari kunci rantai untuk setiap pesan:

message_key = HMAC-SHA256(chain_key, 0x01)

chain_key = HMAC-SHA256(chain_key, 0x02)

Kunci rantai lama dihapus setelah setiap langkah. Kunci pesan mengenkripsi tepat satu pesan, lalu dihapus. Inilah mekanisme forward secrecy.

Perkembangan Ratchet

Jika divisualisasikan, Double Ratchet terlihat seperti ini. Setiap panah adalah fungsi satu arah — Anda dapat maju tetapi tidak pernah mundur:

Root Key Chain:

RK₀ ──DH──→ RK₁ ──DH──→ RK₂ ──DH──→ RK₃ ...

Each RK step produces a Chain Key:

↓ ↓ ↓

CK₀ CK₁ CK₂

↓ ↓ ↓

Each CK step produces Message Keys:

CK₀→MK₀ CK₁→MK₃ CK₂→MK₅

CK₀→MK₁ CK₁→MK₄ CK₂→MK₆

CK₀→MK₂ CK₂→MK₇

Setiap MKₙ akan mengenkripsi tepat satu pesan lalu dihapus, sehingga penyerang yang memperoleh MK₄ tidak dapat menurunkan MK₃ atau MK₅. Pada jalur sealed box yang dirilis hari ini tidak ada kunci per pesan untuk dicuri dan tidak ada rantai untuk ditelusuri — yang ada satu kunci jangka panjang yang membuka segalanya.

6. Forward secrecy — apa yang dibutuhkan

DIRENCANAKAN

Forward secrecy berarti bahwa terkompromikannya kunci jangka panjang tidak mengompromikan kunci sesi masa lalu. Railgun tidak memilikinya hari ini. Dua mekanisme dapat memberikannya, dan keduanya termasuk protokol yang direncanakan di atas:

Kunci efemeral dalam X3DH

Pengirim membuat pasangan kunci efemeral baru untuk setiap sesi baru dan menghapus bagian privatnya setelah handshake. Penyerang yang kemudian mencuri kunci identitas kedua pihak tetap tidak dapat merekonstruksi rahasia sesi. Sealed box memang menggunakan kunci efemeral baru di sisi pengirim — tetapi bagian penerima dalam pertukaran itu adalah kunci jangka panjangnya, dan justru itulah sebabnya properti ini tidak berlaku.

Penghapusan kunci ratchet

Double Ratchet menghapus kunci rantai dan kunci pesan lama setelah dipakai. Rantai KDF bersifat satu arah — diberikan CKₙ, Anda dapat menghitung CKₙ₊₁ tetapi tidak CKₙ₋₁, sehingga kompromi pada waktu T tidak mengungkapkan apa pun yang dikirim sebelum T.

Keamanan pasca-kompromi

Ratchet DH juga memberikan future secrecy: penyerang yang mengompromikan state sesi saat ini kehilangannya pada langkah ratchet berikutnya, yang memasukkan keacakan yang tidak ia miliki. Tanpa ratchet, kunci yang telah dikompromikan tetap berguna bagi penyerang tanpa batas waktu.

7. Arsitektur Relai dan Antrean

Railgun merelai amplop dan dapat mengantrekannya sementara untuk pengiriman offline. Relai dibangun sedemikian rupa sehingga tidak memerlukan teks biasa pesan, tetapi hari ini ia menerima teks biasa dalam dua kasus: pesan kanal, yang tidak dienkripsi oleh klien mana pun, dan pesan langsung dari build desktop Electron, yang penangan IPC-nya meng-encode base64 alih-alih mengenkripsi. Pesan langsung dari jalur sealed box tiba sebagai ciphertext yang tidak dapat dibuka relai.

Apa yang Dilihat Server

Server menyimpan:

  • Bundel kunci publik
  • Info pendaftaran pengguna
  • Metadata dan keanggotaan komunitas/kanal
  • Ciphertext sealed box yang tidak dapat dibukanya (DM dari jalur browser/pengembangan)
  • Teks biasa DM yang di-encode base64 dari build desktop Electron
  • Konten pesan kanal, sampai enkripsi kanal dirilis
  • Catatan audit autentikasi

Server tidak pernah menerima:

  • Kunci privat atau kunci identitas
  • State sesi klien
  • Salinan permanen riwayat pesan Anda

Pesan direlai ke penerima yang online secara real time melalui WebSocket. Untuk perangkat yang offline, amplop diantrekan di Redis dengan TTL maksimum 30 hari dan dihapus setelah perangkat mengonfirmasinya. Tidak ada arsip pesan di sisi server — riwayat klien tersimpan di penyimpanan lokal pada perangkat Anda sendiri, sehingga perangkat yang hilang berarti riwayat yang hilang.

8. Ringkasan Properti Keamanan

PropertiMekanismeStatus
Kerahasiaan DMSealed box hanya pada jalur browser/pengembangan; penangan desktop Electron meng-encode base64Sebagian
Integritas DMTag Poly1305 pada jalur sealed box; jalur desktop Electron mengirim base64 tanpa autentikasiSebagian
Kunci tetap di perangkatPenyimpanan kunci lokal; tidak ada jalur kode yang mengirimkan kunci privatDiimplementasikan
Pengiriman offlineAmplop diantrekan di Redis dengan TTL 30 hari, dihapus saat dikonfirmasiDiimplementasikan
Kerahasiaan kanalDistribusi kunci grup tidak diimplementasikan; konten dikirim dalam keadaan dapat dibacaTidak diimplementasikan
Autentikasi pengirimSealed box bersifat anonim secara konstruksiTidak diimplementasikan
Forward secrecyMembutuhkan Double Ratchet, yang tidak diinstansiasi oleh klien mana punDirencanakan
Pemulihan pasca-kompromiMembutuhkan ratchet DH, yang tidak diinstansiasi oleh klien mana punDirencanakan
Audit independenTidak ada audit kriptografi pihak ketiga yang selesai yang diklaimTidak diselesaikan
Jaminan pascakuantumTidak ada jaminan keamanan pascakuantum menyeluruh yang diklaimTidak diklaim

Dari mana halaman ini berasal

Setiap status di atas dibaca dari kode sumber klien, bukan dari dokumen desain. Repositori klien saat ini tidak publik, sehingga kami tidak dapat meminta Anda memeriksanya sendiri — dan itu adalah alasan untuk menimbang halaman ini sebagai pengungkapan, bukan sebagai bukti. Pertanyaan tentang klaim tertentu kami terima di security@railgun.chat.