serde-encrypt
Serialize
.
Alice Bob
+-----------------------------------+ +-----------------------------------+
| #[derive(Serialize, Deserialize)] | | #[derive(Serialize, Deserialize)] |
| struct Message | | struct Message |
+-----------------------------------+ +-----------------------------------+
| .encrypt() ^
v | ::decrypt()
+-----------------------------------+ +-----------------------------------+
| struct EncryptedMessage | | struct EncryptedMessage |
+-----------------------------------+ +-----------------------------------+
| .serialize() ^
v | ::deserialize()
+-----------------------------------+ +-----------------------------------+
| struct Vec<u8> | -----> | struct Vec<u8> |
+-----------------------------------+ +-----------------------------------+
Overview
serde-encrypt encrypts/decrypts any struct
s and enum
s that implements serde::{Serialize, Deserialize
}.
serde-encrypt supports both shared-key encryption (XChaCha20-Poly1305) and public-key encryption (XChaCha20-Poly1305 with X25519 key-exchange), both of which are considered to be secure enough.
serde-encrypt is optionally available in no_std environments.
[dependencies]
serde-encrypt = "(version)" # If you use std
serde-encrypt = {version = "(version)", default-features = false} # If you need no_std
Example
Good first example from shared key encryption test.
If you and your peer already have shared-key, just implement SerdeEncryptSharedKey
trait to your Serialize
and Deserialize
data types.
#[derive(Debug, Serialize, Deserialize)]
struct Message {
content: String,
sender: String,
}
impl SerdeEncryptSharedKey for Message {
type S = BincodeSerializer<Self>; // you can specify serializer implementation (or implement it by yourself).
}
Then, you can serialize the Message
into Vec<u8>
in encrypted form.
// Alternative:
// const SHARED_KEY: SharedKey = SharedKey::new_const([0u8; 32]);
let shared_key = SharedKey::new([0u8; 32]); // or your peer reads from elsewhere.
let msg = Message {
content: "I ❤️ you.".to_string(),
sender: "Alice".to_string(),
};
let encrypted_message = msg.encrypt(&shared_key)?;
let serialized_encrypted_message: Vec<u8> = encrypted_message.serialize();
After your peer gets the binary, they can decrypt and deserialize it to Message
.
let shared_key = SharedKey::new([0u8; 32]);
let encrypted_message = EncryptedMessage::deserialize(serialized_encrypted_message)?;
let msg = Message::decrypt_owned(&encrypted_message, &shared_key);
Further examples...
- 👀 Encrypts struct with reference fields
🔑 Generates shared-key and safely exchange it to your peer. And then, encrypt/decrypt messages using the shared-key.📚 Encrypts/Decrypts complex serde types
Features and uses cases
Feature comparison
SerdeEncryptSharedKey |
SerdeEncryptSharedKeyDeterministic |
SerdeEncryptPublicKey |
|
---|---|---|---|
(a)symmetric? | symmetric | symmetric | asymmetric |
deterministic? (*1) | no | yes | no |
performance | high | high | low |
(*1) Deterministic encryptions always produce the same cipher-text from a given plain-text. More vulnerable but useful for equal-matching in cipher-text (e.g. RDBMS's encrypted index eq-search).
Encryption algorithm
SerdeEncryptSharedKey |
SerdeEncryptSharedKeyDeterministic |
SerdeEncryptPublicKey |
|
---|---|---|---|
key exchange | - | - | X25519 |
encryption | XChaCha20 | XChaCha20 | XChaCha20 |
message auth | Poly1305 | Poly1305 | Poly1305 |
nonce (*2) | XSalsa20 (random 24-byte) | Fixed 24-byte | XSalsa20 (random 24-byte) |
Rng (*3) for nonce | ChaCha20Rng | - | ChaCha20Rng |
Implementation | XChaCha20Poly1305 | XChaCha20Poly1305 | ChaChaBox |
(*2) "Number used once": to make encryption non-deterministic. Although nonce for each encryption is not secret, nonce among different encryption must be different in order for attackers to get harder to guess plain-text.
(*3) Random number generator.
Serializer
Crate users can choose and even implement by themselves serialize representations in design.
Currently, the following serializers are built-in.
BincodeSerializer
(onlystd
feature)- Best choice for
std
to reduce message size in most cases.
- Best choice for
PostcardSerializer
- Best choice for no_std to reduce message size in most cases.
CborSerializer
- Has large message size but deals with complex serde types. See Encrypts/Decrypts complex serde types example to check kind of serde types only
CborSerializer
can serialize. - Single available choice in
serde-encrypt-sgx
.- Both bincode and postcard crates cannot compile with Rust SGX SDK
- Has large message size but deals with complex serde types. See Encrypts/Decrypts complex serde types example to check kind of serde types only
Use cases
SerdeEncryptSharedKey
- Both message sender and receiver already hold shared key.
- Needs shared-key exchange via any safe way but wants high-speed encryption/decryption (e.g. communicates large amounts of messages).
SerdeEncryptSharedKeyDeterministic
- Only when you need deterministic encryption for equal-matching in cipher-text.
- Note that this is more vulnerable than
SerdeEncryptSharedKey
because, for example, attackers can find repeated patterns in cipher-text and then guess repeated patterns in plain-text.
SerdeEncryptPublicKey
- To exchange
SharedKey
. - Quickly sends/receive small amounts of messages without secret shared key.
- To exchange
Rust SGX SDK support
Use serde-encrypt-sgx crate.
Feature flags
std
(serde-encrypt
[default] ;serde-encrypt-core
[default])std::error::Error
trait implementation toserde_encrypt::Error
.- Random number generator is created via
SeedableRng::from_entropy()
, which is considered to be more secure in OS-available environments. BincodeSerializer
available.
Implementation
Crates
serde-encrypt
is a cargo workspace project and two crates are inside:
serde-encrypt-core
- Encryption / Decryption implementations.
- Traits for serialization / deserialization.
- Traits for RNG (Random Number Generator) singleton.
serde-encrypt
(depends onserde-encrypt-core
)- Serialization / Deserialization impls.
- RNG singleton impls.
serde-encrypt-sgx
crate is also available in separate repository.
It's in the same layer as serde-encrypt
.
In order to use serde with Rust SGX SDK, people should use forked version serde-sgx. Also, Rust SGX SDK compiles with only old version of rustc (nightly-2020-10-25, currently), even simple no_std crate cannot build sometimes (e.g. spin crate cannot).
It is another choice to make serde-encrypt-sgx
inside this repository using feature flags but it breaks cargo build --all-features
in latest rustc.
Encryption
crypto_box crate is used both for public-key encryption and shared-key encryption.
Pure Rust implementation of the crypto_box public-key authenticated encryption scheme from NaCl-family libraries (e.g. libsodium, TweetNaCl) which combines the X25519 Diffie-Hellman function and the XSalsa20Poly1305 authenticated encryption cipher into an Elliptic Curve Integrated Encryption Scheme (ECIES).
Serialization
struct
s and enum
s to encrypt are serialized before encrypted.
Built-in serializers are listed here.
Users can also implement TypedSerialized trait by themselves to get better serialization.
Changelog
See CHANGELOG.md.
License
Licensed under either of Apache License, Version 2.0 or MIT license at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in serde-encrypt by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.