pub struct MessageSession { /* private fields */ }Expand description
MessageSession manages the cryptographic state for secure message exchange between two parties using post-quantum cryptographic algorithms.
Each session contains:
- A digital signature keypair for signing our messages
- A verifier for validating messages from the other party
- One chain key and one counter per direction, derived from the ML-KEM shared secret, so both parties can send at the same time without nonce reuse
- A transcript hash of the handshake that every message signature is bound to
Every message is encrypted with its own key, derived from the chain key, after which
the chain key moves forward and the old one is wiped. Someone who steals the session
(or a stored to_bytes() copy) can’t decrypt the messages that came before.
Messages must be validated in the order they were crafted.
Implementations§
Source§impl MessageSession
impl MessageSession
Sourcepub fn from_bytes(bytes: &[u8]) -> Result<Self, CryptoError>
pub fn from_bytes(bytes: &[u8]) -> Result<Self, CryptoError>
Sourcepub fn new_initiator(
my_signer: SignerPair,
base_nonce: [u8; 24],
target_prekey: &SignedPrekey,
target_verifier: &PublicKeyBytes,
) -> Result<(Self, [u8; 1568]), CryptoError>
pub fn new_initiator( my_signer: SignerPair, base_nonce: [u8; 24], target_prekey: &SignedPrekey, target_verifier: &PublicKeyBytes, ) -> Result<(Self, [u8; 1568]), CryptoError>
Creates a new session as the initiator
Send the responder everything it needs to call MessageSession::new_responder:
the returned ciphertext, target_prekey.id(), the base nonce and your FN-DSA
public key.
§Arguments
my_signer- Your own signer pairbase_nonce- Base nonce (0..16 session id, 16..24 counter), shared with the respondertarget_prekey- A signed prekey of the targettarget_verifier- FN-DSA public key of the target. Get it from a source you trust: it is what proves the prekey really belongs to the target.
§Returns
Result<(Self, [u8; pair::CIPHERTEXT_BYTES]), CryptoError>: The session and ciphertext for the responder, orInvalidSignatureif the prekey was not signed bytarget_verifier
Examples found in repository?
7fn main() {
8 let alice_signer = SignerPair::create();
9 let mut bob_signer = SignerPair::create();
10
11 // Bob creates a one-time prekey and signs its public part, which he shares with Alice
12 let bob_prekey = KEMPair::create();
13 let bob_signed_prekey = SignedPrekey::new(&mut bob_signer, &bob_prekey).unwrap();
14
15 // Create a base nonce with a new session id, and a counter of 0
16 let base_nonce = create_nonce(&gen_session_id(), 0);
17
18 // Lets create the message session for Alice first. This checks that the prekey
19 // was signed by Bob.
20 let (mut alice_session, ciphertext) = MessageSession::new_initiator(
21 alice_signer.clone(),
22 base_nonce,
23 &bob_signed_prekey,
24 bob_signer.pub_key_bytes(), // Bob's public signer key
25 )
26 .unwrap();
27
28 // Now for Bob it would look like this
29 let mut bob_session = MessageSession::new_responder(
30 &bob_prekey,
31 bob_signer.clone(),
32 base_nonce,
33 &ciphertext,
34 alice_signer.pub_key_bytes(), // Alice's public signer key
35 )
36 .unwrap();
37
38 // The prekey was one-time, so Bob deletes it now (dropping it wipes it from memory)
39 drop(bob_prekey);
40
41 // Both sessions now hold one chain key per direction, derived from the shared secret.
42 // Every message gets its own key, and the chain moves forward after each one.
43
44 // Alice creates a message and prepares to send it to Bob
45 let message = b"Hello, Bob! This is a secret message.";
46 let encrypted_message = alice_session.craft_message(message).unwrap();
47
48 // Bob decrypts and verifies Alice's message
49 let raw_message = bob_session.validate_message(&encrypted_message).unwrap();
50
51 // Both message and raw_message are equal, let's print them out to illustrate
52 let message_str = String::from_utf8_lossy(message);
53 let raw_message_str = String::from_utf8_lossy(&raw_message);
54
55 println!("[1] Alice's message: {}", message_str);
56 println!("[2] Bob's decrypted message: {}", raw_message_str);
57
58 // Bob crafts a reply message to Alice
59 let reply = b"Hello, Alice! I received your message safely.";
60 let encrypted_reply = bob_session.craft_message(reply).unwrap();
61
62 // Alice decrypts and verifies Bob's reply
63 let raw_reply = alice_session.validate_message(&encrypted_reply).unwrap();
64
65 // Both reply and raw_reply are equal, let's print them again
66 let reply_str = String::from_utf8_lossy(reply);
67 let raw_reply_str = String::from_utf8_lossy(&raw_reply);
68
69 println!("[3] Bob's reply: {}", reply_str);
70 println!("[4] Alice's decrypted reply: {}", raw_reply_str);
71}More examples
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}Sourcepub fn new_responder(
my_prekey: &KEMPair,
my_signer: SignerPair,
base_nonce: [u8; 24],
ciphertext: &[u8; 1568],
sender_verifier: &PublicKeyBytes,
) -> Result<Self, CryptoError>
pub fn new_responder( my_prekey: &KEMPair, my_signer: SignerPair, base_nonce: [u8; 24], ciphertext: &[u8; 1568], sender_verifier: &PublicKeyBytes, ) -> Result<Self, CryptoError>
Creates a new session as the responder
If my_prekey is a one-time prekey, delete it everywhere you store it once
this returns. That deletion is what gives the session forward secrecy.
§Arguments
my_prekey- The prekey the initiator used (look it up by the id they sent)my_signer- Your own signer pairbase_nonce- Base nonce (0..16 session id, 16..24 counter), shared with the initiatorciphertext- KEM ciphertext sent by the initiatorsender_verifier- FN-DSA public key of the initiator
§Returns
Result<Self, CryptoError>: The session or an error
Examples found in repository?
7fn main() {
8 let alice_signer = SignerPair::create();
9 let mut bob_signer = SignerPair::create();
10
11 // Bob creates a one-time prekey and signs its public part, which he shares with Alice
12 let bob_prekey = KEMPair::create();
13 let bob_signed_prekey = SignedPrekey::new(&mut bob_signer, &bob_prekey).unwrap();
14
15 // Create a base nonce with a new session id, and a counter of 0
16 let base_nonce = create_nonce(&gen_session_id(), 0);
17
18 // Lets create the message session for Alice first. This checks that the prekey
19 // was signed by Bob.
20 let (mut alice_session, ciphertext) = MessageSession::new_initiator(
21 alice_signer.clone(),
22 base_nonce,
23 &bob_signed_prekey,
24 bob_signer.pub_key_bytes(), // Bob's public signer key
25 )
26 .unwrap();
27
28 // Now for Bob it would look like this
29 let mut bob_session = MessageSession::new_responder(
30 &bob_prekey,
31 bob_signer.clone(),
32 base_nonce,
33 &ciphertext,
34 alice_signer.pub_key_bytes(), // Alice's public signer key
35 )
36 .unwrap();
37
38 // The prekey was one-time, so Bob deletes it now (dropping it wipes it from memory)
39 drop(bob_prekey);
40
41 // Both sessions now hold one chain key per direction, derived from the shared secret.
42 // Every message gets its own key, and the chain moves forward after each one.
43
44 // Alice creates a message and prepares to send it to Bob
45 let message = b"Hello, Bob! This is a secret message.";
46 let encrypted_message = alice_session.craft_message(message).unwrap();
47
48 // Bob decrypts and verifies Alice's message
49 let raw_message = bob_session.validate_message(&encrypted_message).unwrap();
50
51 // Both message and raw_message are equal, let's print them out to illustrate
52 let message_str = String::from_utf8_lossy(message);
53 let raw_message_str = String::from_utf8_lossy(&raw_message);
54
55 println!("[1] Alice's message: {}", message_str);
56 println!("[2] Bob's decrypted message: {}", raw_message_str);
57
58 // Bob crafts a reply message to Alice
59 let reply = b"Hello, Alice! I received your message safely.";
60 let encrypted_reply = bob_session.craft_message(reply).unwrap();
61
62 // Alice decrypts and verifies Bob's reply
63 let raw_reply = alice_session.validate_message(&encrypted_reply).unwrap();
64
65 // Both reply and raw_reply are equal, let's print them again
66 let reply_str = String::from_utf8_lossy(reply);
67 let raw_reply_str = String::from_utf8_lossy(&raw_reply);
68
69 println!("[3] Bob's reply: {}", reply_str);
70 println!("[4] Alice's decrypted reply: {}", raw_reply_str);
71}More examples
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}Sourcepub fn craft_message(&mut self, message: &[u8]) -> Result<Vec<u8>, CryptoError>
pub fn craft_message(&mut self, message: &[u8]) -> Result<Vec<u8>, CryptoError>
Creates a signed and encrypted message for the other party
§Arguments
message- The plaintext message to encrypt
§Returns
Result<Vec<u8>, CryptoError>: The encrypted message or an error
§Security Note
This method increments the send counter to ensure a unique nonce for each
message, and fails with NonceExhausted instead of ever reusing one.
Examples found in repository?
7fn main() {
8 let alice_signer = SignerPair::create();
9 let mut bob_signer = SignerPair::create();
10
11 // Bob creates a one-time prekey and signs its public part, which he shares with Alice
12 let bob_prekey = KEMPair::create();
13 let bob_signed_prekey = SignedPrekey::new(&mut bob_signer, &bob_prekey).unwrap();
14
15 // Create a base nonce with a new session id, and a counter of 0
16 let base_nonce = create_nonce(&gen_session_id(), 0);
17
18 // Lets create the message session for Alice first. This checks that the prekey
19 // was signed by Bob.
20 let (mut alice_session, ciphertext) = MessageSession::new_initiator(
21 alice_signer.clone(),
22 base_nonce,
23 &bob_signed_prekey,
24 bob_signer.pub_key_bytes(), // Bob's public signer key
25 )
26 .unwrap();
27
28 // Now for Bob it would look like this
29 let mut bob_session = MessageSession::new_responder(
30 &bob_prekey,
31 bob_signer.clone(),
32 base_nonce,
33 &ciphertext,
34 alice_signer.pub_key_bytes(), // Alice's public signer key
35 )
36 .unwrap();
37
38 // The prekey was one-time, so Bob deletes it now (dropping it wipes it from memory)
39 drop(bob_prekey);
40
41 // Both sessions now hold one chain key per direction, derived from the shared secret.
42 // Every message gets its own key, and the chain moves forward after each one.
43
44 // Alice creates a message and prepares to send it to Bob
45 let message = b"Hello, Bob! This is a secret message.";
46 let encrypted_message = alice_session.craft_message(message).unwrap();
47
48 // Bob decrypts and verifies Alice's message
49 let raw_message = bob_session.validate_message(&encrypted_message).unwrap();
50
51 // Both message and raw_message are equal, let's print them out to illustrate
52 let message_str = String::from_utf8_lossy(message);
53 let raw_message_str = String::from_utf8_lossy(&raw_message);
54
55 println!("[1] Alice's message: {}", message_str);
56 println!("[2] Bob's decrypted message: {}", raw_message_str);
57
58 // Bob crafts a reply message to Alice
59 let reply = b"Hello, Alice! I received your message safely.";
60 let encrypted_reply = bob_session.craft_message(reply).unwrap();
61
62 // Alice decrypts and verifies Bob's reply
63 let raw_reply = alice_session.validate_message(&encrypted_reply).unwrap();
64
65 // Both reply and raw_reply are equal, let's print them again
66 let reply_str = String::from_utf8_lossy(reply);
67 let raw_reply_str = String::from_utf8_lossy(&raw_reply);
68
69 println!("[3] Bob's reply: {}", reply_str);
70 println!("[4] Alice's decrypted reply: {}", raw_reply_str);
71}More examples
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}Sourcepub fn validate_message(
&mut self,
ciphertext: &[u8],
) -> Result<Vec<u8>, CryptoError>
pub fn validate_message( &mut self, ciphertext: &[u8], ) -> Result<Vec<u8>, CryptoError>
Decrypts and validates a message from the other party
§Arguments
ciphertext- The encrypted message
§Returns
Result<Vec<u8>, CryptoError>: The decrypted and validated message or an error
§Security Note
Messages must arrive in the order they were crafted. The receive counter only advances for authentic messages, so replayed or forged input is rejected without desynchronizing the session.
Examples found in repository?
7fn main() {
8 let alice_signer = SignerPair::create();
9 let mut bob_signer = SignerPair::create();
10
11 // Bob creates a one-time prekey and signs its public part, which he shares with Alice
12 let bob_prekey = KEMPair::create();
13 let bob_signed_prekey = SignedPrekey::new(&mut bob_signer, &bob_prekey).unwrap();
14
15 // Create a base nonce with a new session id, and a counter of 0
16 let base_nonce = create_nonce(&gen_session_id(), 0);
17
18 // Lets create the message session for Alice first. This checks that the prekey
19 // was signed by Bob.
20 let (mut alice_session, ciphertext) = MessageSession::new_initiator(
21 alice_signer.clone(),
22 base_nonce,
23 &bob_signed_prekey,
24 bob_signer.pub_key_bytes(), // Bob's public signer key
25 )
26 .unwrap();
27
28 // Now for Bob it would look like this
29 let mut bob_session = MessageSession::new_responder(
30 &bob_prekey,
31 bob_signer.clone(),
32 base_nonce,
33 &ciphertext,
34 alice_signer.pub_key_bytes(), // Alice's public signer key
35 )
36 .unwrap();
37
38 // The prekey was one-time, so Bob deletes it now (dropping it wipes it from memory)
39 drop(bob_prekey);
40
41 // Both sessions now hold one chain key per direction, derived from the shared secret.
42 // Every message gets its own key, and the chain moves forward after each one.
43
44 // Alice creates a message and prepares to send it to Bob
45 let message = b"Hello, Bob! This is a secret message.";
46 let encrypted_message = alice_session.craft_message(message).unwrap();
47
48 // Bob decrypts and verifies Alice's message
49 let raw_message = bob_session.validate_message(&encrypted_message).unwrap();
50
51 // Both message and raw_message are equal, let's print them out to illustrate
52 let message_str = String::from_utf8_lossy(message);
53 let raw_message_str = String::from_utf8_lossy(&raw_message);
54
55 println!("[1] Alice's message: {}", message_str);
56 println!("[2] Bob's decrypted message: {}", raw_message_str);
57
58 // Bob crafts a reply message to Alice
59 let reply = b"Hello, Alice! I received your message safely.";
60 let encrypted_reply = bob_session.craft_message(reply).unwrap();
61
62 // Alice decrypts and verifies Bob's reply
63 let raw_reply = alice_session.validate_message(&encrypted_reply).unwrap();
64
65 // Both reply and raw_reply are equal, let's print them again
66 let reply_str = String::from_utf8_lossy(reply);
67 let raw_reply_str = String::from_utf8_lossy(&raw_reply);
68
69 println!("[3] Bob's reply: {}", reply_str);
70 println!("[4] Alice's decrypted reply: {}", raw_reply_str);
71}More examples
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}Sourcepub fn send_counter(&self) -> u64
pub fn send_counter(&self) -> u64
Gets the counter of the last message sent
Sourcepub fn recv_counter(&self) -> u64
pub fn recv_counter(&self) -> u64
Gets the counter of the last message received