Posted: Feburary 2024
Safenet has been reworked. I have changed the internal code structure to allow easier addition of other encryption protocols. As of now, there are three encryption protocols that are supported out of the box.
Signing | Key Negotiation | Hashing (KDF) | Symmetrical Encryption | |
---|---|---|---|---|
Legacy | ECDSA | ECDH | Blake2b | ChaCha20Poly1305 |
Kyber | Kyber | Kyber | Blake2b | ChaCha20Poly1305 |
Kyber-Dith | Dilithium | Kyber | Blake2b | ChaCha20Poly1305 |
CRYSTALS stands for Cryptographic Suite for Algebraic Lattices. It is a group of quantum-resilient algorithms.
Kyber is a key negotiation algorithm, like ECDH. Dilithium is a digital verification (sigining) algorithm, like ECDSA. Unlike ECDSA and ECDH, Kyber and Dilithium are quantum-reslient, making them future-proof.
Kyber supports MAKE (Mutually Authenticated Key Exchange), which defeats the purpose of a signing algorithm. However, this requires multiple trips back and forth between clients.
You would not be able to perform this exchange within one HTTP POST. If you're using a different network protocol, this might not matter. The Safenet Chat demo uses websockets, so it is able to use Kyber MAKE without much hassle.
This is why I also added a Kyber-Dilithium hybrid. It does not use the MAKE provided by Kyber and uses Dilithium as a signing algorithm.
It can perform a key exchange within one HTTP POST. The only drawback is the payload size. It is around 10kb each way (that's the cost of PQE). The Legacy protocol only requires ~450 bytes.
In order to communicate using DataFrame
s, you must complete a round of pairing with a client. Ultimately, it's up to you but it depends on the network protocol you are using.
// Client let init_frame = InitFrame::default(); let response = post("http://<enter address here>/conn/init", init_frame.to_bytes()); // Use whatever HTTP client library/crate to send a POST request init_frame.from_peer(response.bytes());
// Server #[post("/conn/init")] fn conn_init(request_body: Vec<u8>) -> Vec<u8> { let init_frame = InitFrame::default() // Defaults to Legacy encryption for now, most likely will change init_frame.from_peer(request_body) // .from_peer() returns Vec<u8> }
As I said, you can use whatever network protcol you want. In my demo, I use websockets. You will have to handle the InitFrame
s accordingly.
Another change I have made to the API, is the initialization of the App State. All client/server keys are saved in the aforementioned App State. The server keys used to be generated everytime at runtime, now you can save/load keys for reuse.
use safenet::app_state::AppState; // Generate keys fn main() { AppState::init().unwrap(); // Generates server keys, fails if AppState is already init'd let bytes = AppState::priv_key_to_bytes(); // Returns server keys in bytes } // Reuse previously generated keys fn main() { let bytes = vec![]; // Obviously don't use an empty vector. AppState::init_with_priv_key(bytes); // Fails if AppState is already init'd }
made by ~~> Mateo