Crate pake_kem

Source
Expand description

An implementation of a password authenticate key exchange (PAKE) that relies on quantum-resistant cryptographic primitives

⚠️ Warning: This implementation has not been audited. Use at your own risk!

§Overview

pake-kem is a protocol between two parties: an initiator and a responder. At a high level, the initiator and responder each hold as input to the protocol an Input. After exchanging the protocol messages, the initiator and responder end up with an Output. If the two participants had matching Inputs, then they will end up with the same Output. Otherwise, their Outputs will not match, and in fact be (computationally) uncorrelated.

§Setup

In order to execute the protocol, the initiator and responder must first agree on a collection of primitives to be kept consistent throughout protocol execution. These include:

  • a (classically-secure) two-message PAKE protocol,
  • a (quantum-resistant) key encapsulation mechanism, and
  • a hashing function.

We will use the following choices in this example:

use pake_kem::CipherSuite;
struct Default;
impl CipherSuite for Default {
    type Pake = pake_kem::CPaceRistretto255;
    type Kem = ml_kem::MlKem768;
    type Hash = sha2::Sha256;
}

See examples/demo.rs for a working example for using pake-kem.

Like any symmetric (balanced) PAKE, the initiator and responder will each begin with their own input, exchange some messages as part of the protocol, and derive a secret as the output of the protocol.

If the initiator and responder used the exact same input to the protocol, then they are guaranteed to end up with the same secret (this would be a “shared secret”).

If the initiator and responder used different inputs, then they will not end up with the same shared secret (with overwhelming probability).

The way an input is created in pake-kem is as follows:

use pake_kem::Input;
let input = Input::new(b"password", b"initiator", b"responder");

§Protocol Execution

The pake-kem protocol occurs over four steps, involving three messages between the initiator and responder.

§Initiator Start

The initiator begins the protocol by invoking the following with an Input and source of randomness:

use pake_kem::EncodedSizeUser; // Needed for calling as_bytes()
use pake_kem::Initiator;
use rand_core::OsRng;

let mut initiator_rng = OsRng;
let (initiator, message_one) = Initiator::<Default>::start(&input, &mut initiator_rng)
   .expect("Error with Initiator::start()");
let message_one_bytes = message_one.as_bytes();
// Send message_one_bytes over the wire to the responder

The initiator retains the Initiator object for the third step, and sends the MessageOne object over the wire to the responder.

§Responder Start

Next, the responder invokes the following with an Input, a MessageOne object received from the initiator in the previous step, and a source of randomness:

use pake_kem::MessageOne;
use pake_kem::Responder;

let mut responder_rng = OsRng;
let message_one = MessageOne::from_bytes(&message_one_bytes);
let (responder, message_two) =
    Responder::<Default>::start(&input, &message_one, &mut responder_rng)
       .expect("Error with Responder::start()");
let message_two_bytes = message_two.as_bytes();
// Send message_two_bytes over the wire to the initiator

The responder retains the Responder object for the fourth step, and sends the MessageTwo object over the wire to the initiator.

§Initiator Finish

Next, the initiator invokes the following with the already-initialized object retained from the first step, a MessageTwo object received from the responder in the previous step, and a source of randomness:

use pake_kem::MessageTwo;

let message_two = MessageTwo::from_bytes(&message_two_bytes);
let (initiator_output, message_three) =
    initiator.finish(&message_two, &mut initiator_rng)
        .expect("Error with Initiator::finish()");
let message_three_bytes = message_three.as_bytes();
// Send message_three_bytes over the wire to the responder

The initiator retains the Output object as the output of the pake-kem protocol, and sends the MessageThree object over the wire to the responder.

§Responder Finish

Finally, the responder invokes the following with the already-initialized object retained from the second step and a MessageThree object received from the initiator in the previous step:

use pake_kem::MessageThree;

let message_three = MessageThree::from_bytes(&message_three_bytes);
let responder_output = responder.finish(&message_three)
   .expect("Error with Responder::finish()");

The responder retains the Output object as the output of the pake-kem protocol.

Re-exports§

pub use rand_core;

Structs§

Array
Array is a newtype for an inner [T; N] array where N is determined by a generic ArraySize parameter, which is a marker trait for a numeric value determined by ZSTs that impl the typenum::Unsigned trait.
CPaceRistretto255
An implementation of the CPace protocol using Ristretto255 and SHA-512.
DefaultCipherSuite
The default CipherSuite for pake-kem, based on CPaceRistretto255, MlKem768, and Sha256
Initiator
The main struct for the initiator of the pake-kem protocol
Input
The input to the pake-kem protocol
MessageOne
The first message in the pake-kem protocol, created by the initiator
MessageThree
The third message in the pake-kem protocol, created by the initiator
MessageTwo
The second message in the pake-kem protocol, created by the responder
Output
The output of the pake-kem protocol
Responder
The main struct for the responder of the pake-kem protocol

Enums§

PakeKemError
The library’s error type

Traits§

CipherSuite
Configures the primitives used in pake-kem:
EncodedSizeUser
An object that knows what size it is