TwoParty

Struct TwoParty 

Source
pub struct TwoParty<KMG, KB> { /* private fields */ }
Expand description

Two-Party Secure Messaging (2SM) Key Agreement Protocol as specified in the paper “Key Agreement for Decentralized Secure Group Messaging with Strong Security Guarantees” (2020).

2SM is used for key-agreement as part of the DCGKA protocol allowing all members to learn about the “seed” for establishing new secret state. 2SM is pair-wise between all members of an encrypted group. p2panda uses 2SM for both “data-” and “message encryption” schemes.

§Protocol

An initiator “Alice” of a 2SM session uses the pre-keys of “Bob” to send the first encrypted message using the X3DH protocol. This only takes place once and the pre-keys can be considered “used” afterwards (which is especially important for one-time pre-keys).

All subsequent messages sent between Alice and Bob are encrypted using the HPKE protocol. For each round the sender uses the previous keys for HPKE and generates and attaches to the payload a new key-pair for future rounds.

To avoid reusing public keys (which would make FS impossible), whenever a party sends a message, it also updates the other party’s public key. To do so, it sends a new secret key along with its message, then deletes its own copy, storing only the public key.

To accommodate for messages arriving “late”, the secret key is kept until it or a newer secret has been used. In the case of a newer secret being used, all “previous” secret keys will be dropped.

§Forward secrecy

During the initial 2SM “round” using X3DH the forward secrecy is defined by the lifetime of the used pre-keys. For strong security guarantees it is recommended to use one-time pre-keys. If this requirement can be relaxed it is possible to use long-term pre-keys, with a lifetime defined by the application.

Each subsequent 2SM HPKE round uses exactly one secret key, which is then dropped and replaced by a newly-generated key-pair. This gives the key-agreement protocol strong forward secrecy guarantees for each round, independent of the used pre-keys.

§Cost of key-agreements

To make a group aware of a new secret key we could encrypt the secret pairwise with public-key encryption (PKE) towards each member of the group, resulting in an O(n^2) overhead as every member needs to share their secret with every other. The paper proposes an alternative approach with 2SM where a member prepares the next encryption keys not only for themselves but also for the other party, resulting in a more optimal O(n) cost when rotating keys. This allows us to “heal” the group in less steps after a member is removed.

§Message Ordering

2SM assumes that all messages are received in the order they have been sent. The application or underlying networking protocol needs to handle ordering. This is handled for us by the DCGKA protocol (as specified in the paper) and causally-ordered, authenticated broadcast in p2panda itself.

https://eprint.iacr.org/2020/1281.pdf

Implementations§

Source§

impl<KMG, KB> TwoParty<KMG, KB>
where KMG: IdentityManager<KMG::State> + PreKeyManager, KB: KeyBundle,

Source

pub fn init(their_prekey_bundle: KB) -> TwoPartyState<KB>

Initialise new 2SM state using the other party’s pre-key bundle.

Source

pub fn send( y: TwoPartyState<KB>, y_manager: &KMG::State, plaintext: &[u8], rng: &Rng, ) -> Result<(TwoPartyState<KB>, TwoPartyMessage), TwoPartyError>

Securely send a plaintext message to the other party.

Source

pub fn receive( y: TwoPartyState<KB>, y_manager: KMG::State, message: TwoPartyMessage, ) -> Result<(TwoPartyState<KB>, KMG::State, Vec<u8>), TwoPartyError>

Handle receiving a secure message from the other party.

Auto Trait Implementations§

§

impl<KMG, KB> Freeze for TwoParty<KMG, KB>

§

impl<KMG, KB> RefUnwindSafe for TwoParty<KMG, KB>
where KMG: RefUnwindSafe, KB: RefUnwindSafe,

§

impl<KMG, KB> Send for TwoParty<KMG, KB>
where KMG: Send, KB: Send,

§

impl<KMG, KB> Sync for TwoParty<KMG, KB>
where KMG: Sync, KB: Sync,

§

impl<KMG, KB> Unpin for TwoParty<KMG, KB>
where KMG: Unpin, KB: Unpin,

§

impl<KMG, KB> UnwindSafe for TwoParty<KMG, KB>
where KMG: UnwindSafe, KB: UnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

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

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V