[][src]Struct double_ratchet::DoubleRatchet

pub struct DoubleRatchet<CP: CryptoProvider> { /* fields omitted */ }

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]

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 authenticated
  • them must be authenticated
  • initial_receive is None or Some(key) where key is confidential and authenticated

pub fn new_bob(
    shared_secret: CP::RootKey,
    us: CP::KeyPair,
    initial_send: Option<CP::ChainKey>
) -> Self
[src]

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 is None or Some(key) where key 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]

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]

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]

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]

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

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

Blanket Implementations

impl<T, U> Into for T where
    U: From<T>, 
[src]

impl<T> From for T[src]

impl<T, U> TryFrom for T where
    U: Into<T>, 
[src]

type Error = Infallible

The type returned in the event of a conversion error.

impl<T> Borrow for T where
    T: ?Sized
[src]

impl<T> BorrowMut for T where
    T: ?Sized
[src]

impl<T, U> TryInto for T where
    U: TryFrom<T>, 
[src]

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.

impl<T> Any for T where
    T: 'static + ?Sized
[src]