[−][src]Struct double_ratchet::DoubleRatchet
The DoubleRatchet
can encrypt/decrypt messages while providing forward and future secrecy.
The DoubleRatchet
struct provides an implementation of the Double Ratchet Algorithm as
defined in its specification, including the unspecified symmetric initialization. After
initialization (with new_alice
or new_bob
) the user can interact with the DoubleRatchet
using the ratchet_encrypt
and ratchet_decrypt
methods, which automatically takes care of
deriving the correct keys and updating the internal state.
Initialization
When Alice and Bob want to use the DoubleRatchet
, they need to initialize it using different
constructors. The "Alice" or "Bob" role follows from the design of the authenticated key
exchange that is used to initialize the secure communications channel. Two "modes" are
possible, depending on whether just one or both of the parties must be able to send the first
data message. See new_alice
and new_bob
for further details.
Provided security
Conditional on the correct implementation of the CryptoProvider
, the DoubleRatchet
provides
confidentiality of the plaintext and authentication of both the ciphertext and associated data.
It does not provid anonimity, as the headers have to be sent in plain text and are sufficient
for identifying the communicating parties. See CryptoProvider
for further details on the
required security properties.
Forward secrecy (sometimes called the key-erasure property) preserves confidentiality of old
messages in case of a device compromise. The DoubleRatchet
provides forward secrecy by
deriving a fresh key for every message: the sender deletes it immediately after encrypting and
the receiver deletes it immediately after succesful decryption. Messages may arrive out of
order, in which case the receiver is able to derive and store the keys for the skipped messages
without compromising the forward secrecy of other messages. See secure deletion for further
discussion.
Future secrecy (sometimes called the self-healing property) restores confidentiality of new
messages in case of a past device compromise. The DoubleRatchet
provides future secrecy by
generating a fresh KeyPair
for every reply that is being sent. See recovery from compromise
for further discussion.
Examples
If Alice is guaranteed to send the first message to Bob, she can initialize her DoubleRatchet
as shown here, without providing the symmetric initial_receive
key. It is assumed that
shared_secret
and bobs_public_key
are the result of some secure key exchange. A higher
level protocol may force Alice to always send an empty initial message in order to fully
initialize both parties.
type DR = DoubleRatchet<MyCryptoProvider>; /// Alice and Bob have agreed on `shared_secret` and `bobs_public_key` let mut alice = DR::new_alice(&shared_secret, bobs_public_key, None, &mut csprng); let mut bob = DR::new_bob(shared_secret, bobs_keypair, None); /// Bob cannot send to Alice assert_eq!(Err(EncryptUninit), bob.try_ratchet_encrypt(b"Hi Alice", b"B2A", &mut csprng)); /// Alice can send to Bob let (head, ct) = alice.ratchet_encrypt(b"Hello Bob", b"A2B", &mut csprng); let pt = bob.ratchet_decrypt(&head, &ct, b"A2B").unwrap(); assert_eq!(&pt[..], b"Hello Bob"); /// Now Bob can send to Alice let (head, ct) = bob.ratchet_encrypt(b"Hi Alice", b"B2A", &mut csprng); let pt = alice.ratchet_decrypt(&head, &ct, b"B2A").unwrap(); assert_eq!(&pt[..], b"Hi Alice");
If it is required that either party can send the first message, the key exchange must provide
us with an extra_shared_secret
.
let mut alice = DR::new_alice(&shared_secret, bobs_public_key, Some(extra_shared_secret), &mut csprng); let mut bob = DR::new_bob(shared_secret, bobs_keypair, Some(extra_shared_secret)); /// Either Alice or Bob can send the first message let (head_bob, ct_bob) = bob.ratchet_encrypt(b"Hi Alice", b"from Bob to Alice", &mut csprng); let (head_alice, ct_alice) = alice.ratchet_encrypt(b"Hello Bob", b"from Alice to Bob", &mut csprng); let pt_bob = alice.ratchet_decrypt(&head_bob, &ct_bob, b"from Bob to Alice").unwrap(); let pt_alice = bob.ratchet_decrypt(&head_alice, &ct_alice, b"from Alice to Bob").unwrap(); assert_eq!(&pt_alice[..], b"Hello Bob"); assert_eq!(&pt_bob[..], b"Hi Alice");
Methods
impl<CP: CryptoProvider> DoubleRatchet<CP>
[src]
pub fn new_alice<R: CryptoRng + RngCore>(
shared_secret: &CP::RootKey,
them: CP::PublicKey,
initial_receive: Option<CP::ChainKey>,
rng: &mut R
) -> Self
[src]
shared_secret: &CP::RootKey,
them: CP::PublicKey,
initial_receive: Option<CP::ChainKey>,
rng: &mut R
) -> Self
Initialize "Alice": the sender of the first message.
This implements RatchetInitAlice
as defined in the specification when initial_receive = None
: after initialization Alice must send a message to Bob before he is able to provide
a reply.
Alternatively Alice provides an extra symmetric key: initial_receive = Some(key)
, so that
both Alice and Bob can send the first message. Note however that even when Alice and Bob
initialize this way the initialization is asymmetric in the sense that Alice requires Bob's
public key.
Either Alice and Bob must supply the same extra symmetric key or both must supply none.
Security considerations
For security, initialization through new_alice
has the following requirements:
shared_secret
must be both confidential and authenticatedthem
must be authenticatedinitial_receive
isNone
orSome(key)
wherekey
is confidential and authenticated
pub fn new_bob(
shared_secret: CP::RootKey,
us: CP::KeyPair,
initial_send: Option<CP::ChainKey>
) -> Self
[src]
shared_secret: CP::RootKey,
us: CP::KeyPair,
initial_send: Option<CP::ChainKey>
) -> Self
Initialize "Bob": the receiver of the first message.
This implements RatchetInitBob
as defined in the specification when initial_send = None
: after intialization Bob must receive a message from Alice before he can send his
first message.
Alternatively Bob provides an extra symmetric key: initial_send = Some(key)
, so that both
Alice and Bob can send the first message. Note however that even when Alice and Bob
initialize this way the initialization is asymmetric in the sense that Bob must provide his
public key to Alice.
Either Alice and Bob must supply the same extra symmetric key or both must supply none.
Security considerations
For security, initialization through new_bob
has the following requirements:
shared_secret
must be both confidential and authenticated- the private key of
us
must remain secret on Bob's device initial_send
isNone
orSome(key)
wherekey
is confidential and authenticated
pub fn try_ratchet_encrypt<R: CryptoRng + RngCore>(
&mut self,
plaintext: &[u8],
associated_data: &[u8],
rng: &mut R
) -> Result<(Header<CP::PublicKey>, Vec<u8>), EncryptUninit>
[src]
&mut self,
plaintext: &[u8],
associated_data: &[u8],
rng: &mut R
) -> Result<(Header<CP::PublicKey>, Vec<u8>), EncryptUninit>
Try to encrypt the plaintext. See ratchet_encrypt
for details.
Fails with EncryptUninit
when self
is not yet initialized for encrypting.
pub fn ratchet_encrypt<R: CryptoRng + RngCore>(
&mut self,
plaintext: &[u8],
associated_data: &[u8],
rng: &mut R
) -> (Header<CP::PublicKey>, Vec<u8>)
[src]
&mut self,
plaintext: &[u8],
associated_data: &[u8],
rng: &mut R
) -> (Header<CP::PublicKey>, Vec<u8>)
Encrypt the plaintext, ratchet forward and return the (header, ciphertext) pair.
Implements RatchetEncrypt
as defined in the specification. The header should be sent
along the ciphertext in order for the recipient to be able to ratchet_decrypt
. The
ciphertext is encrypted in some
AEAD mode, which encrypts the
plaintext
and authenticates the plaintext
, associated_data
and the header.
The internal state of the DoubleRatchet is automatically updated so that the next message key be sent with a fresh key.
Note that rng
is only used for updating the internal state and not for encrypting the
data.
Panics
Panics if the DoubleRatchet is not initialized for sending yet. If this is a concern, use
try_ratchet_encrypt
instead to avoid panics.
pub fn ratchet_decrypt(
&mut self,
header: &Header<CP::PublicKey>,
ciphertext: &[u8],
associated_data: &[u8]
) -> Result<Vec<u8>, DecryptError>
[src]
&mut self,
header: &Header<CP::PublicKey>,
ciphertext: &[u8],
associated_data: &[u8]
) -> Result<Vec<u8>, DecryptError>
Verify-decrypt the ciphertext, update the state and return the plaintext.
Implements RatchetDecrypt
as defined in the specification. Decryption of the ciphertext
includes verifying the authenticity of the header
, ciphertext
and associated_data
(optional).
The internal state of the DoubleRatchet is automatically updated upon successful
decryption. This includes storing the MessageKeys
of any skipped messages so these
messages can be decrypted if they arrive out of order.
Returns a DecryptError
when the plaintext could not be decrypted: the internal state
remains unchanged in that case. There could be many reasons: inspect the returned
error-value for further details.
Trait Implementations
impl<CP> Debug for DoubleRatchet<CP> where
CP: CryptoProvider,
CP::KeyPair: Debug,
CP::PublicKey: Debug,
CP::RootKey: Debug,
CP::ChainKey: Debug,
CP::MessageKey: Debug,
[src]
CP: CryptoProvider,
CP::KeyPair: Debug,
CP::PublicKey: Debug,
CP::RootKey: Debug,
CP::ChainKey: Debug,
CP::MessageKey: Debug,
Auto Trait Implementations
impl<CP> Send for DoubleRatchet<CP> where
<CP as CryptoProvider>::ChainKey: Send,
<CP as CryptoProvider>::KeyPair: Send,
<CP as CryptoProvider>::MessageKey: Send,
<CP as CryptoProvider>::PublicKey: Send,
<CP as CryptoProvider>::RootKey: Send,
<CP as CryptoProvider>::ChainKey: Send,
<CP as CryptoProvider>::KeyPair: Send,
<CP as CryptoProvider>::MessageKey: Send,
<CP as CryptoProvider>::PublicKey: Send,
<CP as CryptoProvider>::RootKey: Send,
impl<CP> Sync for DoubleRatchet<CP> where
<CP as CryptoProvider>::ChainKey: Sync,
<CP as CryptoProvider>::KeyPair: Sync,
<CP as CryptoProvider>::MessageKey: Sync,
<CP as CryptoProvider>::PublicKey: Sync,
<CP as CryptoProvider>::RootKey: Sync,
<CP as CryptoProvider>::ChainKey: Sync,
<CP as CryptoProvider>::KeyPair: Sync,
<CP as CryptoProvider>::MessageKey: Sync,
<CP as CryptoProvider>::PublicKey: Sync,
<CP as CryptoProvider>::RootKey: Sync,
Blanket Implementations
impl<T, U> Into for T where
U: From<T>,
[src]
U: From<T>,
impl<T> From for T
[src]
impl<T, U> TryFrom for T where
U: Into<T>,
[src]
U: Into<T>,
type Error = Infallible
The type returned in the event of a conversion error.
fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
[src]
impl<T> Borrow for T where
T: ?Sized,
[src]
T: ?Sized,
impl<T> BorrowMut for T where
T: ?Sized,
[src]
T: ?Sized,
fn borrow_mut(&mut self) -> &mut T
[src]
impl<T, U> TryInto for T where
U: TryFrom<T>,
[src]
U: TryFrom<T>,
type Error = <U as TryFrom<T>>::Error
The type returned in the event of a conversion error.
fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
[src]
impl<T> Any for T where
T: 'static + ?Sized,
[src]
T: 'static + ?Sized,