kevy_embedded/config_secure.rs
1//! Keys for encrypting an embed's replication links with Noise IK. Both
2//! directions stay plaintext unless one of these is configured.
3
4use crate::config::Config;
5
6pub use kevy_noise::Keypair;
7
8/// One end's key pair, and the public keys of the other ends it accepts.
9///
10/// ```
11/// use kevy_embedded::{Keypair, LinkKeys};
12///
13/// let keys = LinkKeys::new(Keypair::from_secret([1; 32])).with_peers(vec![[2; 32]]);
14/// assert_eq!(keys.peers.len(), 1);
15/// ```
16#[derive(Debug, Clone)]
17#[non_exhaustive]
18pub struct LinkKeys {
19 /// This store's static key pair.
20 ///
21 /// ```
22 /// let keys = kevy_embedded::LinkKeys::new(kevy_embedded::Keypair::from_secret([1; 32]));
23 /// assert_ne!(keys.local.public(), [0; 32]);
24 /// ```
25 pub local: Keypair,
26 /// On a replica, the primary keys it trusts, tried in turn until one
27 /// answers; it needs at least one. On a writer, the replica keys it
28 /// accepts; empty accepts any replica, still encrypted.
29 ///
30 /// ```
31 /// let primary = kevy_embedded::Keypair::from_secret([2; 32]);
32 /// let keys = kevy_embedded::LinkKeys::new(kevy_embedded::Keypair::from_secret([1; 32]))
33 /// .with_peers(vec![primary.public()]);
34 /// assert_eq!(keys.peers[0], primary.public());
35 /// ```
36 pub peers: Vec<[u8; 32]>,
37}
38
39impl LinkKeys {
40 /// This end's key pair `local`, with no peers yet. The key has no
41 /// default: without it the link authenticates nobody.
42 ///
43 /// ```
44 /// let keys = kevy_embedded::LinkKeys::new(kevy_embedded::Keypair::from_secret([1; 32]));
45 /// assert!(keys.peers.is_empty());
46 /// ```
47 #[must_use]
48 pub fn new(local: Keypair) -> Self {
49 Self { local, peers: Vec::new() }
50 }
51
52 /// Set [`Self::peers`].
53 ///
54 /// ```
55 /// let keys = kevy_embedded::LinkKeys::new(kevy_embedded::Keypair::from_secret([1; 32]))
56 /// .with_peers(vec![[2; 32], [3; 32]]);
57 /// assert_eq!(keys.peers.len(), 2);
58 /// ```
59 #[must_use]
60 pub fn with_peers(mut self, peers: Vec<[u8; 32]>) -> Self {
61 self.peers = peers;
62 self
63 }
64}
65
66/// The keys for each replication direction; `None` keeps it plaintext.
67#[derive(Debug, Clone, Default)]
68pub(crate) struct LinkSecurity {
69 pub(crate) replica: Option<LinkKeys>,
70 pub(crate) writer: Option<LinkKeys>,
71}
72
73impl Config {
74 /// Encrypt the link to the replication upstream: the replica presents
75 /// `keys.local` and connects only to a primary holding one of
76 /// `keys.peers`. Opening fails when `keys.peers` is empty.
77 ///
78 /// ```
79 /// use kevy_embedded::{Config, Keypair, LinkKeys, Store};
80 ///
81 /// let primary = Keypair::from_secret([2; 32]);
82 /// let replica = |peers| {
83 /// Config::default()
84 /// .without_aof()
85 /// .with_replica_upstream("127.0.0.1:16004")
86 /// .with_replica_security(LinkKeys::new(Keypair::from_secret([1; 32])).with_peers(peers))
87 /// };
88 /// assert!(Store::open(replica(vec![primary.public()]))?.is_replica());
89 /// // a secure replica that trusts no primary does not open
90 /// assert!(Store::open(replica(vec![])).is_err());
91 /// # Ok::<(), kevy_embedded::KevyError>(())
92 /// ```
93 #[must_use]
94 pub fn with_replica_security(mut self, keys: LinkKeys) -> Self {
95 self.link_security.replica = Some(keys);
96 self
97 }
98
99 /// Encrypt the embed-writer listener: every replica must complete a
100 /// Noise handshake against `keys.local`, and when `keys.peers` is not
101 /// empty, present one of those keys.
102 ///
103 /// ```
104 /// use kevy_embedded::{Config, Keypair, LinkKeys, Store};
105 ///
106 /// let writer = Store::open(
107 /// Config::default()
108 /// .with_embed_writer("127.0.0.1:0")
109 /// .with_writer_security(LinkKeys::new(Keypair::from_secret([1; 32]))),
110 /// )?;
111 /// assert!(writer.writer_addr().is_some());
112 /// # Ok::<(), kevy_embedded::KevyError>(())
113 /// ```
114 #[must_use]
115 pub fn with_writer_security(mut self, keys: LinkKeys) -> Self {
116 self.link_security.writer = Some(keys);
117 self
118 }
119}