Skip to main content

SignedPrekey

Struct SignedPrekey 

Source
pub struct SignedPrekey { /* private fields */ }
Expand description

The public half of a prekey, signed by its owner’s FN-DSA identity key

A prekey is an ordinary KEMPair that a responder creates ahead of time. The responder keeps the KEMPair and publishes this signed public part, for example on a server. An initiator starts a session by encapsulating to it.

There are two kinds, and the difference is only how the responder treats them:

  • One-time prekeys are used for a single session. Once the session is set up, the responder deletes the KEMPair everywhere it is stored. After that, nothing can decrypt that session’s handshake, which gives forward secrecy.
  • A last-resort prekey is used whenever no one-time prekey is left. It is reused, so sessions using it only gain forward secrecy once the responder replaces it and deletes the old one. Rotate it regularly (for example weekly).

Implementations§

Source§

impl SignedPrekey

Source

pub fn new( owner: &mut SignerPair, prekey: &KEMPair, ) -> Result<Self, CryptoError>

Signs the public key of prekey with the owner’s identity key

§Arguments
  • owner - The identity signer pair of the prekey’s owner
  • prekey - The prekey to publish
§Returns
  • Result<SignedPrekey, CryptoError>: The signed prekey or an error
Examples found in repository?
examples/full_exchange.rs (line 13)
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
Hide additional examples
examples/prekey_server.rs (line 22)
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}
Source

pub fn verify(&self, identity: &PublicKeyBytes) -> bool

Checks that this prekey was signed by the owner of identity

§Arguments
  • identity - The FN-DSA public key of the claimed owner
§Returns

True if the signature is valid for this identity, false otherwise

Source

pub fn id(&self) -> PrekeyId

Returns the identifier the responder uses to find the matching KEMPair

Examples found in repository?
examples/prekey_server.rs (line 29)
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}
Source

pub fn kem_pub_key(&self) -> &[u8; 1568]

Returns the prekey’s ML-KEM public key

Source

pub fn to_bytes(&self) -> [u8; 2848]

Serializes the signed prekey: public key followed by signature

Source

pub fn from_bytes(bytes: &[u8]) -> Result<Self, CryptoError>

Deserializes a signed prekey

This only checks the length. Call SignedPrekey::verify (as MessageSession::new_initiator does) before trusting it.

§Arguments
  • bytes - The serialized signed prekey
§Returns
  • Result<SignedPrekey, CryptoError>: The signed prekey or an error

Trait Implementations§

Source§

impl Clone for SignedPrekey

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more

Auto Trait Implementations§

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> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. 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> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

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

Source§

type Error = !

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

fn try_from(value: U) -> Result<T, !>

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.