1use crate::types::SharedSizedLockedArray;
4use crate::*;
5use futures::future::BoxFuture;
6use one_err::OneErr;
7use std::future::Future;
8use std::sync::{Arc, Mutex};
9
10fn is_false(b: impl std::borrow::Borrow<bool>) -> bool {
11 !b.borrow()
12}
13
14pub mod traits {
17 use super::*;
18
19 pub trait AsLairStore: 'static + Send + Sync {
21 fn get_bidi_ctx_key(&self) -> SharedSizedLockedArray<32>;
24
25 fn list_entries(
27 &self,
28 ) -> BoxFuture<'static, LairResult<Vec<LairEntryInfo>>>;
29
30 fn write_entry(
33 &self,
34 entry: LairEntry,
35 ) -> BoxFuture<'static, LairResult<()>>;
36
37 fn get_entry_by_tag(
39 &self,
40 tag: Arc<str>,
41 ) -> BoxFuture<'static, LairResult<LairEntry>>;
42
43 fn get_entry_by_ed25519_pub_key(
45 &self,
46 ed25519_pub_key: Ed25519PubKey,
47 ) -> BoxFuture<'static, LairResult<LairEntry>>;
48
49 fn get_entry_by_x25519_pub_key(
51 &self,
52 x25519_pub_key: X25519PubKey,
53 ) -> BoxFuture<'static, LairResult<LairEntry>>;
54 }
55
56 pub trait AsLairStoreFactory: 'static + Send + Sync {
58 fn connect_to_store(
60 &self,
61 unlock_secret: SharedSizedLockedArray<32>,
62 ) -> BoxFuture<'static, LairResult<LairStore>>;
63 }
64}
65use traits::*;
66
67#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
69#[serde(rename_all = "camelCase")]
70pub struct SeedInfo {
71 pub ed25519_pub_key: Ed25519PubKey,
73
74 pub x25519_pub_key: X25519PubKey,
76
77 #[serde(skip_serializing_if = "is_false", default)]
79 pub exportable: bool,
80}
81
82pub type CertDigest = BinDataSized<32>;
84
85#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
87#[serde(rename_all = "camelCase")]
88pub struct CertInfo {
89 pub sni: Arc<str>,
91
92 pub digest: CertDigest,
94
95 pub cert: BinData,
97}
98
99#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
101#[serde(tag = "type", rename_all = "camelCase")]
102#[non_exhaustive]
103pub enum LairEntryInfo {
104 Seed {
106 tag: Arc<str>,
108
109 seed_info: SeedInfo,
111 },
112
113 DeepLockedSeed {
115 tag: Arc<str>,
117
118 seed_info: SeedInfo,
120 },
121
122 WkaTlsCert {
124 tag: Arc<str>,
126
127 cert_info: CertInfo,
129 },
130}
131
132pub type Seed = SecretDataSized<32, 49>;
134
135#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
138#[serde(tag = "type", rename_all = "camelCase")]
139#[non_exhaustive]
140pub enum LairEntryInner {
141 Seed {
149 tag: Arc<str>,
151
152 seed_info: SeedInfo,
154
155 seed: Seed,
157 },
158
159 DeepLockedSeed {
161 tag: Arc<str>,
163
164 seed_info: SeedInfo,
166
167 salt: BinDataSized<16>,
169
170 ops_limit: u32,
172
173 mem_limit: u32,
175
176 seed: Seed,
178 },
179
180 WkaTlsCert {
184 tag: Arc<str>,
186
187 cert_info: CertInfo,
189
190 priv_key: SecretData,
192 },
193}
194
195impl LairEntryInner {
196 pub fn encode(&self) -> LairResult<Box<[u8]>> {
198 use serde::Serialize;
199 let mut se =
200 rmp_serde::encode::Serializer::new(Vec::new()).with_struct_map();
201 self.serialize(&mut se).map_err(one_err::OneErr::new)?;
202 Ok(se.into_inner().into_boxed_slice())
203 }
204
205 pub fn decode(bytes: &[u8]) -> LairResult<LairEntryInner> {
207 let item: LairEntryInner =
208 rmp_serde::from_read(bytes).map_err(one_err::OneErr::new)?;
209 Ok(item)
210 }
211
212 pub fn tag(&self) -> Arc<str> {
214 match self {
215 Self::Seed { tag, .. } => tag.clone(),
216 Self::DeepLockedSeed { tag, .. } => tag.clone(),
217 Self::WkaTlsCert { tag, .. } => tag.clone(),
218 }
219 }
220}
221
222pub type LairEntry = Arc<LairEntryInner>;
225
226#[derive(Clone)]
229pub struct LairStore(pub Arc<dyn AsLairStore>);
230
231impl LairStore {
232 pub fn get_bidi_ctx_key(&self) -> SharedSizedLockedArray<32> {
235 AsLairStore::get_bidi_ctx_key(&*self.0)
236 }
237
238 pub fn insert_seed(
242 &self,
243 seed: SharedSizedLockedArray<32>,
244 tag: Arc<str>,
245 exportable: bool,
246 ) -> impl Future<Output = LairResult<SeedInfo>> + 'static + Send {
247 let inner = self.0.clone();
248 async move {
249 let mut ed_pk = [0; sodoken::sign::PUBLICKEYBYTES];
251 let mut ed_sk = sodoken::SizedLockedArray::<
252 { sodoken::sign::SECRETKEYBYTES },
253 >::new()?;
254 sodoken::sign::seed_keypair(
255 &mut ed_pk,
256 &mut ed_sk.lock(),
257 &seed.lock().unwrap().lock(),
258 )?;
259
260 let mut x_pk = [0; sodoken::crypto_box::XSALSA_PUBLICKEYBYTES];
262 let mut x_sk = sodoken::SizedLockedArray::<
263 { sodoken::crypto_box::XSALSA_SECRETKEYBYTES },
264 >::new()?;
265 sodoken::crypto_box::xsalsa_seed_keypair(
266 &mut x_pk,
267 &mut x_sk.lock(),
268 &seed.lock().unwrap().lock(),
269 )?;
270
271 let key = inner.get_bidi_ctx_key();
273 let seed = SecretDataSized::encrypt(key, seed).await?;
274
275 let seed_info = SeedInfo {
277 ed25519_pub_key: ed_pk.into(),
278 x25519_pub_key: x_pk.into(),
279 exportable,
280 };
281
282 let entry = LairEntryInner::Seed {
284 tag,
285 seed_info: seed_info.clone(),
286 seed,
287 };
288
289 inner.write_entry(Arc::new(entry)).await?;
291
292 Ok(seed_info)
294 }
295 }
296
297 pub fn new_seed(
301 &self,
302 tag: Arc<str>,
303 exportable: bool,
304 ) -> impl Future<Output = LairResult<SeedInfo>> + 'static + Send {
305 let this = self.clone();
306 async move {
307 let mut seed = sodoken::SizedLockedArray::<32>::new()?;
309 sodoken::random::randombytes_buf(&mut *seed.lock())?;
310
311 let seed = Arc::new(Mutex::new(seed));
312
313 this.insert_seed(seed, tag, exportable).await
314 }
315 }
316
317 pub fn insert_deep_locked_seed(
323 &self,
324 seed: SharedSizedLockedArray<32>,
325 tag: Arc<str>,
326 ops_limit: u32,
327 mem_limit: u32,
328 mut deep_lock_passphrase: sodoken::SizedLockedArray<64>,
329 exportable: bool,
330 ) -> impl Future<Output = LairResult<SeedInfo>> + 'static + Send {
331 let inner = self.0.clone();
332 async move {
333 let mut ed_pk = [0; sodoken::sign::PUBLICKEYBYTES];
335 let mut ed_sk = sodoken::SizedLockedArray::<
336 { sodoken::sign::SECRETKEYBYTES },
337 >::new()?;
338 sodoken::sign::seed_keypair(
339 &mut ed_pk,
340 &mut ed_sk.lock(),
341 &seed.lock().unwrap().lock(),
342 )?;
343
344 let mut x_pk = [0; sodoken::crypto_box::XSALSA_PUBLICKEYBYTES];
346 let mut x_sk = sodoken::SizedLockedArray::<
347 { sodoken::crypto_box::XSALSA_SECRETKEYBYTES },
348 >::new()?;
349 sodoken::crypto_box::xsalsa_seed_keypair(
350 &mut x_pk,
351 &mut x_sk.lock(),
352 &seed.lock().unwrap().lock(),
353 )?;
354
355 let (salt, key) = tokio::task::spawn_blocking({
357 move || -> std::io::Result<([u8; sodoken::argon2::ARGON2_ID_SALTBYTES], sodoken::SizedLockedArray<32>)> {
358 let mut salt = [0; sodoken::argon2::ARGON2_ID_SALTBYTES];
360 sodoken::random::randombytes_buf(&mut salt)?;
361
362 let mut key = sodoken::SizedLockedArray::<32>::new()?;
363 sodoken::argon2::blocking_argon2id(
364 &mut *key.lock(),
365 &*deep_lock_passphrase.lock(),
366 &salt,
367 ops_limit,
368 mem_limit,
369 )?;
370
371 Ok((salt, key))
372 }
373 })
374 .await
375 .map_err(OneErr::new)??;
376
377 let seed =
379 SecretDataSized::encrypt(Arc::new(Mutex::new(key)), seed)
380 .await?;
381
382 let seed_info = SeedInfo {
384 ed25519_pub_key: ed_pk.into(),
385 x25519_pub_key: x_pk.into(),
386 exportable,
387 };
388
389 let entry = LairEntryInner::DeepLockedSeed {
391 tag,
392 seed_info: seed_info.clone(),
393 salt: salt.into(),
394 ops_limit,
395 mem_limit,
396 seed,
397 };
398
399 inner.write_entry(Arc::new(entry)).await?;
401
402 Ok(seed_info)
404 }
405 }
406
407 pub fn new_deep_locked_seed(
413 &self,
414 tag: Arc<str>,
415 ops_limit: u32,
416 mem_limit: u32,
417 deep_lock_passphrase: sodoken::SizedLockedArray<64>,
418 exportable: bool,
419 ) -> impl Future<Output = LairResult<SeedInfo>> + 'static + Send {
420 let this = self.clone();
421 async move {
422 let mut seed = sodoken::SizedLockedArray::<32>::new()?;
424 sodoken::random::randombytes_buf(&mut *seed.lock())?;
425
426 let seed = Arc::new(Mutex::new(seed));
427
428 this.insert_deep_locked_seed(
429 seed,
430 tag,
431 ops_limit,
432 mem_limit,
433 deep_lock_passphrase,
434 exportable,
435 )
436 .await
437 }
438 }
439
440 pub fn new_wka_tls_cert(
444 &self,
445 tag: Arc<str>,
446 ) -> impl Future<Output = LairResult<CertInfo>> + 'static + Send {
447 let inner = self.0.clone();
448 async move {
449 use crate::internal::tls::*;
450
451 let TlsCertGenResult {
453 sni,
454 priv_key,
455 cert,
456 digest,
457 } = tls_cert_self_signed_new().await?;
458
459 let key = inner.get_bidi_ctx_key();
461 let priv_key = SecretData::encrypt(key, priv_key).await?;
462
463 let cert_info = CertInfo {
465 sni,
466 digest: digest.into(),
467 cert: cert.into(),
468 };
469
470 let entry = LairEntryInner::WkaTlsCert {
472 tag,
473 cert_info: cert_info.clone(),
474 priv_key,
475 };
476
477 inner.write_entry(Arc::new(entry)).await?;
479
480 Ok(cert_info)
482 }
483 }
484
485 pub fn list_entries(
487 &self,
488 ) -> impl Future<Output = LairResult<Vec<LairEntryInfo>>> + 'static + Send
489 {
490 AsLairStore::list_entries(&*self.0)
491 }
492
493 pub fn get_entry_by_tag(
495 &self,
496 tag: Arc<str>,
497 ) -> impl Future<Output = LairResult<LairEntry>> + 'static + Send {
498 AsLairStore::get_entry_by_tag(&*self.0, tag)
499 }
500
501 pub fn get_entry_by_ed25519_pub_key(
503 &self,
504 ed25519_pub_key: Ed25519PubKey,
505 ) -> impl Future<Output = LairResult<LairEntry>> + 'static + Send {
506 AsLairStore::get_entry_by_ed25519_pub_key(&*self.0, ed25519_pub_key)
507 }
508
509 pub fn get_entry_by_x25519_pub_key(
511 &self,
512 x25519_pub_key: X25519PubKey,
513 ) -> impl Future<Output = LairResult<LairEntry>> + 'static + Send {
514 AsLairStore::get_entry_by_x25519_pub_key(&*self.0, x25519_pub_key)
515 }
516}
517
518#[derive(Clone)]
521pub struct LairStoreFactory(pub Arc<dyn AsLairStoreFactory>);
522
523impl LairStoreFactory {
524 pub fn connect_to_store(
526 &self,
527 unlock_secret: sodoken::SizedLockedArray<32>,
528 ) -> impl Future<Output = LairResult<LairStore>> + 'static + Send {
529 AsLairStoreFactory::connect_to_store(
530 &*self.0,
531 Arc::new(Mutex::new(unlock_secret)),
532 )
533 }
534}