Skip to main content

prekey_server/
prekey_server.rs

1//! The full prekey flow through the reference server.
2//! Run with: cargo run --example prekey_server --features server
3
4use std::collections::HashMap;
5
6use pq_msg::{
7    exchange::{
8        pair::KEMPair,
9        prekey::{PrekeyId, SignedPrekey},
10    },
11    messaging::{MessageSession, create_nonce, gen_session_id},
12    server::PrekeyServer,
13    signatures::keypair::{SignerPair, ViewOperations},
14};
15
16fn main() {
17    let mut server = PrekeyServer::new();
18
19    // --- Bob's device: create prekeys, keep the secrets, publish the signed public parts
20    let mut bob_signer = SignerPair::create();
21    let bob_last_resort = KEMPair::create();
22    let bob_last_resort_signed = SignedPrekey::new(&mut bob_signer, &bob_last_resort).unwrap();
23
24    let mut bob_one_time: HashMap<PrekeyId, KEMPair> = HashMap::new();
25    let mut published = Vec::new();
26    for _ in 0..3 {
27        let prekey = KEMPair::create();
28        let signed = SignedPrekey::new(&mut bob_signer, &prekey).unwrap();
29        bob_one_time.insert(signed.id(), prekey);
30        published.push(signed);
31    }
32    server
33        .publish(
34            "bob",
35            *bob_signer.pub_key_bytes(),
36            bob_last_resort_signed.clone(),
37            published,
38        )
39        .unwrap();
40    println!(
41        "Bob published 3 one-time prekeys and a last-resort prekey ({} one-time left)",
42        server.one_time_remaining(&"bob")
43    );
44
45    // --- Alice's device: fetch Bob's bundle and start a session
46    let alice_signer = SignerPair::create();
47    let bundle = server.fetch(&"bob").unwrap();
48
49    // In a real app Alice checks Bob's identity key against a copy she trusts
50    // (for example by comparing safety numbers in person)
51    assert_eq!(&bundle.identity, bob_signer.pub_key_bytes());
52
53    let base_nonce = create_nonce(&gen_session_id(), 0);
54    let (mut alice_session, ciphertext) = MessageSession::new_initiator(
55        alice_signer.clone(),
56        base_nonce,
57        &bundle.prekey,
58        &bundle.identity,
59    )
60    .unwrap();
61
62    // Alice sends Bob: the ciphertext, the prekey id, the base nonce and her identity key
63    let prekey_id = bundle.prekey.id();
64
65    // --- Bob's device: find the prekey Alice used and set up his side
66    let mut bob_session = if let Some(prekey) = bob_one_time.remove(&prekey_id) {
67        // One-time prekey: it is removed from Bob's store above and wiped when dropped
68        // at the end of this block, which gives the session forward secrecy
69        MessageSession::new_responder(
70            &prekey,
71            bob_signer.clone(),
72            base_nonce,
73            &ciphertext,
74            alice_signer.pub_key_bytes(),
75        )
76        .unwrap()
77    } else if prekey_id == bob_last_resort_signed.id() {
78        // Last-resort prekey: kept until Bob rotates it
79        MessageSession::new_responder(
80            &bob_last_resort,
81            bob_signer.clone(),
82            base_nonce,
83            &ciphertext,
84            alice_signer.pub_key_bytes(),
85        )
86        .unwrap()
87    } else {
88        panic!("unknown prekey");
89    };
90    println!(
91        "Session set up; Bob has {} one-time prekey secrets left on his device",
92        bob_one_time.len()
93    );
94
95    // --- Talk
96    let encrypted = alice_session.craft_message(b"Hi Bob!").unwrap();
97    let message = bob_session.validate_message(&encrypted).unwrap();
98    println!("Bob received: {}", String::from_utf8_lossy(&message));
99
100    let encrypted = bob_session.craft_message(b"Hi Alice!").unwrap();
101    let reply = alice_session.validate_message(&encrypted).unwrap();
102    println!("Alice received: {}", String::from_utf8_lossy(&reply));
103}