venice_e2ee_proxy/
keys.rs1use std::{fmt, sync::Arc};
8
9use k256::{SecretKey, elliptic_curve::sec1::ToEncodedPoint};
10use rand_core::OsRng;
11use zeroize::ZeroizeOnDrop;
12
13use crate::config::KeysConfig;
14
15#[derive(Clone)]
17pub struct ProxyInstanceKey {
18 inner: Arc<ProxyInstanceKeyInner>,
19}
20
21struct ProxyInstanceKeyInner {
23 private_key: ProxyInstancePrivateKey,
24 public_key_hex: String,
25}
26
27struct ProxyInstancePrivateKey(SecretKey);
29
30impl ProxyInstancePrivateKey {
31 fn new(private_key: SecretKey) -> Self {
33 Self(private_key)
34 }
35
36 fn public_key_hex(&self) -> String {
38 let public_key = self.0.public_key();
39 hex::encode(public_key.to_encoded_point(false).as_bytes())
40 }
41
42 fn secret_key(&self) -> &SecretKey {
44 &self.0
45 }
46}
47
48impl ZeroizeOnDrop for ProxyInstancePrivateKey {}
49
50impl fmt::Debug for ProxyInstancePrivateKey {
51 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53 f.write_str("[redacted]")
54 }
55}
56
57impl ProxyInstanceKey {
58 pub fn generate() -> Self {
60 let private_key = SecretKey::random(&mut OsRng);
61 Self::from_private_key(private_key)
62 }
63
64 pub fn generate_from_config(config: &KeysConfig) -> Option<Self> {
66 config
67 .generate_proxy_instance_key_on_startup
68 .then(Self::generate)
69 }
70
71 fn from_private_key(private_key: SecretKey) -> Self {
73 let private_key = ProxyInstancePrivateKey::new(private_key);
74 let public_key_hex = private_key.public_key_hex();
75
76 Self {
77 inner: Arc::new(ProxyInstanceKeyInner {
78 private_key,
79 public_key_hex,
80 }),
81 }
82 }
83
84 pub fn public_key_hex(&self) -> &str {
88 &self.inner.public_key_hex
89 }
90
91 #[allow(dead_code)]
93 pub(crate) fn private_key(&self) -> &SecretKey {
94 self.inner.private_key.secret_key()
95 }
96}
97
98impl fmt::Debug for ProxyInstanceKey {
99 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101 f.debug_struct("ProxyInstanceKey")
102 .field("private_key", &"[redacted]")
103 .field("public_key_hex", &self.public_key_hex())
104 .finish()
105 }
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111
112 #[test]
113 fn generates_uncompressed_public_key_hex_in_venice_format() {
114 let key = ProxyInstanceKey::generate();
115
116 assert_eq!(key.public_key_hex().len(), 130);
117 assert!(key.public_key_hex().starts_with("04"));
118 assert!(key.public_key_hex().chars().all(|c| c.is_ascii_hexdigit()));
119 assert!(
120 key.public_key_hex()
121 .chars()
122 .all(|c| !c.is_ascii_uppercase())
123 );
124 }
125
126 #[test]
127 fn respects_startup_key_generation_config() {
128 let enabled = KeysConfig {
129 generate_proxy_instance_key_on_startup: true,
130 };
131 let disabled = KeysConfig {
132 generate_proxy_instance_key_on_startup: false,
133 };
134
135 assert!(ProxyInstanceKey::generate_from_config(&enabled).is_some());
136 assert!(ProxyInstanceKey::generate_from_config(&disabled).is_none());
137 }
138
139 #[test]
140 fn private_key_material_is_zeroized_on_drop() {
141 fn assert_zeroize_on_drop<T: ZeroizeOnDrop>() {}
142
143 assert_zeroize_on_drop::<SecretKey>();
144 assert_zeroize_on_drop::<ProxyInstancePrivateKey>();
145
146 let key = ProxyInstanceKey::generate();
147 let _private_key_ref: &SecretKey = key.private_key();
148 }
149
150 #[test]
151 fn debug_output_redacts_private_key_material() {
152 let key = ProxyInstanceKey::generate();
153 let debug = format!("{key:?}");
154
155 assert!(debug.contains("[redacted]"));
156 assert!(debug.contains(key.public_key_hex()));
157 }
158}