Skip to main content

typhoon/certificate/
server.rs

1//! Server key material: [`ServerKeyPair`] (file I/O) and [`ServerSecret`] (runtime crypto).
2
3#[cfg(test)]
4#[path = "../../tests/certificate/server.rs"]
5mod tests;
6
7use std::fmt::Debug;
8#[cfg(any(feature = "full_software", feature = "full_hardware"))]
9use std::fmt::{Formatter, Result as FmtResult};
10use std::fs::File;
11use std::io::{Read, Write};
12use std::net::SocketAddr;
13use std::path::Path;
14use std::sync::Arc;
15
16use cfg_if::cfg_if;
17use classic_mceliece_rust::{PublicKey as McEliecePublicKey, SecretKey, keypair_boxed};
18use ed25519_dalek::SigningKey;
19use rand::RngCore;
20
21cfg_if! {
22    if #[cfg(any(feature = "full_software", feature = "full_hardware"))] {
23        use x25519_dalek::{PublicKey as X25519PublicKey, StaticSecret};
24        use super::utils::X25519_BYTES;
25    }
26}
27use super::client::ClientCertificate;
28use super::utils::{CertificateError, ED25519_BYTES, EPK_BYTES, ESK_BYTES, ObfuscationBufferContainer, TYPE_SERVER, read_header, write_header};
29use crate::bytes::FixedByteBuffer;
30use crate::utils::random::get_rng;
31
32// ── ServerSecret ──────────────────────────────────────────────────────────────
33
34/// Server secret: McEliece secret key + Ed25519 signing key + X25519 obfuscation keys (full mode).
35#[cfg(any(feature = "full_software", feature = "full_hardware"))]
36pub(crate) struct ServerSecret<'a> {
37    pub esk: SecretKey<'a>,
38    pub vsk: SigningKey,
39    pub opk: X25519PublicKey,
40    pub osk: StaticSecret,
41}
42
43/// Server secret: McEliece secret key + Ed25519 signing key + obfuscation key (fast mode).
44#[cfg(any(feature = "fast_software", feature = "fast_hardware"))]
45pub(crate) struct ServerSecret<'a> {
46    pub esk: SecretKey<'a>,
47    pub vsk: SigningKey,
48    pub obfs: FixedByteBuffer<ED25519_BYTES>,
49}
50
51impl ObfuscationBufferContainer for ServerSecret<'_> {
52    #[cfg(any(feature = "full_software", feature = "full_hardware"))]
53    #[inline]
54    fn obfuscation_buffer(&self) -> FixedByteBuffer<ED25519_BYTES> {
55        FixedByteBuffer::from(self.opk.as_bytes())
56    }
57
58    #[cfg(any(feature = "fast_software", feature = "fast_hardware"))]
59    #[inline]
60    fn obfuscation_buffer(&self) -> FixedByteBuffer<ED25519_BYTES> {
61        self.obfs
62    }
63}
64
65// ── ServerKeyPair ─────────────────────────────────────────────────────────────
66
67/// Full server key material: McEliece + Ed25519 + symmetric obfuscation key (fast mode).
68///
69/// Generated once at server setup, saved to a file, and loaded on each server start.
70/// Use [`to_client_certificate`](Self::to_client_certificate) to produce distributable client
71/// certificates, and pass to [`ListenerBuilder::new`](crate::socket::ListenerBuilder::new) to
72/// start the server.
73#[cfg(any(feature = "fast_software", feature = "fast_hardware"))]
74#[derive(Debug)]
75pub struct ServerKeyPair {
76    epk: Arc<McEliecePublicKey<'static>>,
77    esk: SecretKey<'static>,
78    vsk: SigningKey,
79    obfs: FixedByteBuffer<ED25519_BYTES>,
80}
81
82/// Full server key material: McEliece + Ed25519 + X25519 obfuscation keys (full mode).
83///
84/// Generated once at server setup, saved to a file, and loaded on each server start.
85/// Use [`to_client_certificate`](Self::to_client_certificate) to produce distributable client
86/// certificates, and pass to [`ListenerBuilder::new`](crate::socket::ListenerBuilder::new) to
87/// start the server.
88#[cfg(any(feature = "full_software", feature = "full_hardware"))]
89pub struct ServerKeyPair {
90    epk: Arc<McEliecePublicKey<'static>>,
91    esk: SecretKey<'static>,
92    vsk: SigningKey,
93    opk: X25519PublicKey,
94    osk: StaticSecret,
95}
96
97impl ServerKeyPair {
98    /// Generate a fresh server key pair using the system RNG.
99    #[cfg(any(feature = "fast_software", feature = "fast_hardware"))]
100    pub fn generate() -> Self {
101        let rng = &mut get_rng();
102        let (pk, sk) = keypair_boxed(rng);
103        let mut vsk_bytes = [0u8; ED25519_BYTES];
104        rng.fill_bytes(&mut vsk_bytes);
105        let vsk = SigningKey::from_bytes(&vsk_bytes);
106        let mut obfs_bytes = [0u8; ED25519_BYTES];
107        rng.fill_bytes(&mut obfs_bytes);
108        Self {
109            epk: Arc::new(pk),
110            esk: sk,
111            vsk,
112            obfs: FixedByteBuffer::from(obfs_bytes),
113        }
114    }
115
116    /// Generate a fresh server key pair using the system RNG.
117    #[cfg(any(feature = "full_software", feature = "full_hardware"))]
118    pub fn generate() -> Self {
119        let rng = &mut get_rng();
120        let (pk, sk) = keypair_boxed(rng);
121        let mut vsk_bytes = [0u8; ED25519_BYTES];
122        rng.fill_bytes(&mut vsk_bytes);
123        let vsk = SigningKey::from_bytes(&vsk_bytes);
124        let osk = StaticSecret::random_from_rng(rng);
125        let opk = X25519PublicKey::from(&osk);
126        Self {
127            epk: Arc::new(pk),
128            esk: sk,
129            vsk,
130            opk,
131            osk,
132        }
133    }
134
135    /// Derive a client certificate from this key pair, embedding the given server addresses.
136    #[cfg(any(feature = "fast_software", feature = "fast_hardware"))]
137    pub fn to_client_certificate(&self, addresses: Vec<SocketAddr>) -> ClientCertificate {
138        ClientCertificate {
139            epk: self.epk.clone(),
140            vpk: self.vsk.verifying_key(),
141            obfs: self.obfs,
142            addresses,
143        }
144    }
145
146    /// Derive a client certificate from this key pair, embedding the given server addresses.
147    #[cfg(any(feature = "full_software", feature = "full_hardware"))]
148    pub fn to_client_certificate(&self, addresses: Vec<SocketAddr>) -> ClientCertificate {
149        ClientCertificate {
150            epk: self.epk.clone(),
151            vpk: self.vsk.verifying_key(),
152            opk: self.opk,
153            addresses,
154        }
155    }
156
157    /// Consume the key pair and return the inner [`ServerSecret`] for use with the socket layer.
158    #[cfg(all(feature = "server", any(feature = "fast_software", feature = "fast_hardware")))]
159    pub(crate) fn into_server_secret(self) -> ServerSecret<'static> {
160        ServerSecret {
161            esk: self.esk,
162            vsk: self.vsk,
163            obfs: self.obfs,
164        }
165    }
166
167    /// Consume the key pair and return the inner [`ServerSecret`] for use with the socket layer.
168    #[cfg(all(feature = "server", any(feature = "full_software", feature = "full_hardware")))]
169    pub(crate) fn into_server_secret(self) -> ServerSecret<'static> {
170        ServerSecret {
171            esk: self.esk,
172            vsk: self.vsk,
173            opk: self.opk,
174            osk: self.osk,
175        }
176    }
177
178    /// Save the server key pair to a binary file (fast mode).
179    ///
180    /// # File layout (fast mode, `F`)
181    ///
182    /// | Offset       | Size                 | Field  | Description |
183    /// |--------------|----------------------|--------|-------------|
184    /// | 0            | 10                   | Header | Magic `TYPHOON`, type `S`, mode `F`, version `1` |
185    /// | 10           | 261120 (`EPK_BYTES`) | EPK    | Classic McEliece 348864 public key |
186    /// | 261130       | 6492 (`ESK_BYTES`)   | ESK    | Classic McEliece 348864 secret key |
187    /// | 267622       | 32 (`ED25519_BYTES`) | VSK    | Ed25519 signing key (seed) |
188    /// | 267654       | 32 (`ED25519_BYTES`) | OBFS   | Symmetric tailer obfuscation key |
189    /// | **267686**   | —                    | EOF    | |
190    #[cfg(any(feature = "fast_software", feature = "fast_hardware"))]
191    pub fn save(&self, path: impl AsRef<Path>) -> Result<(), CertificateError> {
192        let mut f = File::create(path)?;
193        write_header(&mut f, TYPE_SERVER)?;
194        f.write_all(self.epk.as_array())?;
195        f.write_all(self.esk.as_array())?;
196        f.write_all(&self.vsk.to_bytes())?;
197        f.write_all(self.obfs.as_ref())?;
198        Ok(())
199    }
200
201    /// Save the server key pair to a binary file (full mode).
202    ///
203    /// # File layout (full mode, `U`)
204    ///
205    /// | Offset       | Size                 | Field  | Description |
206    /// |--------------|----------------------|--------|-------------|
207    /// | 0            | 10                   | Header | Magic `TYPHOON`, type `S`, mode `U`, version `1` |
208    /// | 10           | 261120 (`EPK_BYTES`) | EPK    | Classic McEliece 348864 public key |
209    /// | 261130       | 6492 (`ESK_BYTES`)   | ESK    | Classic McEliece 348864 secret key |
210    /// | 267622       | 32 (`ED25519_BYTES`) | VSK    | Ed25519 signing key (seed) |
211    /// | 267654       | 32 (`X25519_BYTES`)  | OPK    | X25519 long-term public key |
212    /// | 267686       | 32 (`X25519_BYTES`)  | OSK    | X25519 static secret key |
213    /// | **267718**   | —                    | EOF    | |
214    #[cfg(any(feature = "full_software", feature = "full_hardware"))]
215    pub fn save(&self, path: impl AsRef<Path>) -> Result<(), CertificateError> {
216        let mut f = File::create(path)?;
217        write_header(&mut f, TYPE_SERVER)?;
218        f.write_all(self.epk.as_array())?;
219        f.write_all(self.esk.as_array())?;
220        f.write_all(&self.vsk.to_bytes())?;
221        f.write_all(self.opk.as_bytes())?;
222        f.write_all(self.osk.as_bytes())?;
223        Ok(())
224    }
225
226    /// Load a server key pair from a binary file produced by [`save`](Self::save) (fast mode).
227    #[cfg(any(feature = "fast_software", feature = "fast_hardware"))]
228    pub fn load(path: impl AsRef<Path>) -> Result<Self, CertificateError> {
229        let mut f = File::open(path)?;
230        read_header(&mut f, TYPE_SERVER)?;
231        let mut epk_buf = Box::new([0u8; EPK_BYTES]);
232        f.read_exact(epk_buf.as_mut())?;
233        let mut esk_buf = Box::new([0u8; ESK_BYTES]);
234        f.read_exact(esk_buf.as_mut())?;
235        let mut vsk_arr = [0u8; ED25519_BYTES];
236        f.read_exact(&mut vsk_arr)?;
237        let vsk = SigningKey::from_bytes(&vsk_arr);
238        let mut obfs_arr = [0u8; ED25519_BYTES];
239        f.read_exact(&mut obfs_arr)?;
240        Ok(Self {
241            epk: Arc::new(McEliecePublicKey::from(epk_buf)),
242            esk: SecretKey::from(esk_buf),
243            vsk,
244            obfs: FixedByteBuffer::from(obfs_arr),
245        })
246    }
247
248    /// Load a server key pair from a binary file produced by [`save`](Self::save) (full mode).
249    #[cfg(any(feature = "full_software", feature = "full_hardware"))]
250    pub fn load(path: impl AsRef<Path>) -> Result<Self, CertificateError> {
251        let mut f = File::open(path)?;
252        read_header(&mut f, TYPE_SERVER)?;
253        let mut epk_buf = Box::new([0u8; EPK_BYTES]);
254        f.read_exact(epk_buf.as_mut())?;
255        let mut esk_buf = Box::new([0u8; ESK_BYTES]);
256        f.read_exact(esk_buf.as_mut())?;
257        let mut vsk_arr = [0u8; ED25519_BYTES];
258        f.read_exact(&mut vsk_arr)?;
259        let vsk = SigningKey::from_bytes(&vsk_arr);
260        let mut opk_arr = [0u8; X25519_BYTES];
261        f.read_exact(&mut opk_arr)?;
262        let mut osk_arr = [0u8; X25519_BYTES];
263        f.read_exact(&mut osk_arr)?;
264        Ok(Self {
265            epk: Arc::new(McEliecePublicKey::from(epk_buf)),
266            esk: SecretKey::from(esk_buf),
267            vsk,
268            opk: X25519PublicKey::from(opk_arr),
269            osk: StaticSecret::from(osk_arr),
270        })
271    }
272}
273
274// ── Debug impl ────────────────────────────────────────────────────────────────
275
276/// Manual Debug for full-mode ServerKeyPair: StaticSecret does not implement Debug.
277#[cfg(any(feature = "full_software", feature = "full_hardware"))]
278impl Debug for ServerKeyPair {
279    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
280        f.debug_struct("ServerKeyPair").field("epk", &"<McEliece public key>").field("esk", &"<McEliece secret key>").field("vsk", &self.vsk).field("opk", &self.opk).field("osk", &"<X25519 static secret>").finish()
281    }
282}