pq_msg/exchange/
prekey.rs1use sha2::{Digest, Sha256};
2
3use crate::{
4 errors::CryptoError,
5 exchange::pair::{self, KEMPair},
6 signatures::keypair::{self, PublicKeyBytes, SignerPair, VerifierPair, ViewOperations},
7};
8
9const PREKEY_LABEL: &[u8] = b"pq-msg/v2 prekey";
12
13pub const SIGNED_PREKEY_BYTES: usize = pair::PUBLIC_KEY_BYTES + keypair::SIGNATURE_BYTES;
15
16pub type PrekeyId = [u8; 32];
18
19#[derive(Clone)]
33pub struct SignedPrekey {
34 kem_pub_key: [u8; pair::PUBLIC_KEY_BYTES],
35 signature: keypair::SignatureBytes,
36}
37
38impl SignedPrekey {
39 pub fn new(owner: &mut SignerPair, prekey: &KEMPair) -> Result<Self, CryptoError> {
48 let kem_pub_key = prekey.pub_key_bytes();
49 let signature = owner.sign(&signed_data(&kem_pub_key))?;
50 Ok(Self {
51 kem_pub_key,
52 signature,
53 })
54 }
55
56 pub fn verify(&self, identity: &PublicKeyBytes) -> bool {
64 VerifierPair::new(identity)
65 .is_ok_and(|v| v.verify(&signed_data(&self.kem_pub_key), &self.signature))
66 }
67
68 pub fn id(&self) -> PrekeyId {
70 Sha256::digest(self.kem_pub_key).into()
71 }
72
73 pub fn kem_pub_key(&self) -> &[u8; pair::PUBLIC_KEY_BYTES] {
75 &self.kem_pub_key
76 }
77
78 pub fn to_bytes(&self) -> [u8; SIGNED_PREKEY_BYTES] {
80 let mut bytes = [0u8; SIGNED_PREKEY_BYTES];
81 bytes[..pair::PUBLIC_KEY_BYTES].copy_from_slice(&self.kem_pub_key);
82 bytes[pair::PUBLIC_KEY_BYTES..].copy_from_slice(&self.signature);
83 bytes
84 }
85
86 pub fn from_bytes(bytes: &[u8]) -> Result<Self, CryptoError> {
97 if bytes.len() != SIGNED_PREKEY_BYTES {
98 return Err(CryptoError::IncongruentLength(
99 SIGNED_PREKEY_BYTES,
100 bytes.len(),
101 ));
102 }
103 let (kem_pub_key, signature) = bytes.split_at(pair::PUBLIC_KEY_BYTES);
104 Ok(Self {
105 kem_pub_key: kem_pub_key.try_into()?,
106 signature: signature.try_into()?,
107 })
108 }
109}
110
111fn signed_data(kem_pub_key: &[u8; pair::PUBLIC_KEY_BYTES]) -> Vec<u8> {
113 [PREKEY_LABEL, &kem_pub_key[..]].concat()
114}
115
116#[cfg(test)]
117mod tests {
118 use super::*;
119
120 #[test]
121 fn test_signed_prekey() {
122 let mut owner = SignerPair::create();
123 let prekey = KEMPair::create();
124 let signed = SignedPrekey::new(&mut owner, &prekey).unwrap();
125
126 assert!(signed.verify(owner.pub_key_bytes()));
127 assert!(!signed.verify(SignerPair::create().pub_key_bytes()));
128 let mut undecodable = *owner.pub_key_bytes();
129 undecodable[0] ^= 0xFF;
130 assert!(!signed.verify(&undecodable));
131 assert_eq!(signed.kem_pub_key(), &prekey.pub_key_bytes());
132
133 let restored = SignedPrekey::from_bytes(&signed.to_bytes()).unwrap();
134 assert!(restored.verify(owner.pub_key_bytes()));
135 assert_eq!(restored.id(), signed.id());
136 assert_ne!(signed.id(), {
137 let other = KEMPair::create();
138 SignedPrekey::new(&mut owner, &other).unwrap().id()
139 });
140 }
141
142 #[test]
143 fn test_swapped_kem_key_rejected() {
144 let mut owner = SignerPair::create();
146 let signed = SignedPrekey::new(&mut owner, &KEMPair::create()).unwrap();
147
148 let mut bytes = signed.to_bytes();
149 bytes[..pair::PUBLIC_KEY_BYTES].copy_from_slice(&KEMPair::create().pub_key_bytes());
150 let swapped = SignedPrekey::from_bytes(&bytes).unwrap();
151 assert!(!swapped.verify(owner.pub_key_bytes()));
152
153 assert!(matches!(
154 SignedPrekey::from_bytes(&bytes[1..]),
155 Err(CryptoError::IncongruentLength(SIGNED_PREKEY_BYTES, _))
156 ));
157 }
158
159 #[test]
160 fn test_validly_signed_invalid_kem_key_rejected() {
161 let mut owner = SignerPair::create();
164 let kem_pub_key = [0xFF; pair::PUBLIC_KEY_BYTES];
165 let bad = SignedPrekey {
166 kem_pub_key,
167 signature: owner.sign(&signed_data(&kem_pub_key)).unwrap(),
168 };
169 assert!(bad.verify(owner.pub_key_bytes()));
170
171 let result = crate::messaging::MessageSession::new_initiator(
172 SignerPair::create(),
173 [0u8; 24],
174 &bad,
175 owner.pub_key_bytes(),
176 );
177 assert!(matches!(result, Err(CryptoError::InvalidKey)));
178 }
179}