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
KEMPaireverywhere 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
impl SignedPrekey
Sourcepub fn new(
owner: &mut SignerPair,
prekey: &KEMPair,
) -> Result<Self, CryptoError>
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 ownerprekey- 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
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}Sourcepub fn verify(&self, identity: &PublicKeyBytes) -> bool
pub fn verify(&self, identity: &PublicKeyBytes) -> bool
Sourcepub fn id(&self) -> PrekeyId
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}Sourcepub fn kem_pub_key(&self) -> &[u8; 1568]
pub fn kem_pub_key(&self) -> &[u8; 1568]
Returns the prekey’s ML-KEM public key
Sourcepub fn to_bytes(&self) -> [u8; 2848]
pub fn to_bytes(&self) -> [u8; 2848]
Serializes the signed prekey: public key followed by signature
Sourcepub fn from_bytes(bytes: &[u8]) -> Result<Self, CryptoError>
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§
Auto Trait Implementations§
impl Freeze for SignedPrekey
impl RefUnwindSafe for SignedPrekey
impl Send for SignedPrekey
impl Sync for SignedPrekey
impl Unpin for SignedPrekey
impl UnsafeUnpin for SignedPrekey
impl UnwindSafe for SignedPrekey
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more