1use crate::{
2 scheduler::Scheduler,
3 signer::Signer,
4 tls::{self, TlsConfig},
5 utils::get_node_id_from_tls_config,
6};
7use log::debug;
12use std::{convert::TryFrom, path::Path};
13use thiserror;
14
15const CRED_VERSION: u32 = 1u32;
16const CA_RAW: &[u8] = include_str!("../.resources/tls/ca.pem").as_bytes();
17const NOBODY_CRT: &[u8] = include_str!(env!("GL_NOBODY_CRT")).as_bytes();
18const NOBODY_KEY: &[u8] = include_str!(env!("GL_NOBODY_KEY")).as_bytes();
19
20#[derive(thiserror::Error, Debug)]
21pub enum Error {
22 #[error("could not get from identity: {}", .0)]
23 GetFromIdentityError(String),
24 #[error("identity mismatch: {}", .0)]
25 IsIdentityError(String),
26 #[error("could not decode into credentials")]
27 DecodeCredentialsError(#[from] prost::DecodeError),
28 #[error("could not encode credentials")]
29 EncodeCredentialError(#[from] prost::EncodeError),
30 #[error("could not upgrade credentials: {}", .0)]
31 UpgradeCredentialsError(String),
32 #[error("could not build credentials {}", .0)]
33 BuildCredentialsError(String),
34 #[error("could not create create credentials from data: {}", .0)]
35 TransformDataIntoCredentialsError(String),
36 #[error("could not create tls config {}", .0)]
37 CreateTlsConfigError(#[source] anyhow::Error),
38 #[error("could not read from file: {}", .0)]
39 ReadFromFileError(#[from] std::io::Error),
40 #[error("could not fetch default nobody credentials: {}", .0)]
41 FetchDefaultNobodyCredentials(#[source] anyhow::Error),
42}
43
44pub type Result<T, E = Error> = std::result::Result<T, E>;
45
46pub trait TlsConfigProvider: Send + Sync {
47 fn tls_config(&self) -> TlsConfig;
48}
49
50pub trait RuneProvider {
51 fn rune(&self) -> String;
52}
53
54pub trait NodeIdProvider {
55 fn node_id(&self) -> Result<Vec<u8>>;
56}
57
58#[derive(Clone, Debug)]
61struct Identity {
62 cert: Vec<u8>,
63 key: Vec<u8>,
64}
65
66impl Default for Identity {
67 fn default() -> Self {
68 let key = load_file_or_default("GL_NOBODY_KEY", NOBODY_KEY)
69 .expect("Could not load file from GL_NOBODY_KEY");
70 let cert = load_file_or_default("GL_NOBODY_CRT", NOBODY_CRT)
71 .expect("Could not load file from GL_NOBODY_CRT");
72 Self { cert, key }
73 }
74}
75
76#[derive(Clone, Debug)]
79pub struct Nobody {
80 pub cert: Vec<u8>,
81 pub key: Vec<u8>,
82 pub ca: Vec<u8>,
83}
84
85impl Nobody {
86 pub fn new() -> Self {
88 Self::default()
89 }
90
91 pub fn with<V>(cert: V, key: V) -> Self
93 where
94 V: Into<Vec<u8>>,
95 {
96 let ca =
97 load_file_or_default("GL_CA_CRT", CA_RAW).expect("Could not load file from GL_CA_CRT");
98
99 Self {
100 cert: cert.into(),
101 key: key.into(),
102 ca,
103 }
104 }
105
106 pub fn with_ca<V>(self, ca: V) -> Self
107 where
108 V: Into<Vec<u8>>,
109 {
110 Nobody {
111 ca: ca.into(),
112 ..self
113 }
114 }
115}
116
117impl TlsConfigProvider for Nobody {
118 fn tls_config(&self) -> TlsConfig {
119 tls::TlsConfig::with(&self.cert, &self.key, &self.ca)
120 }
121}
122
123impl Default for Nobody {
124 fn default() -> Self {
125 let ca =
126 load_file_or_default("GL_CA_CRT", CA_RAW).expect("Could not load file from GL_CA_CRT");
127 let identity = Identity::default();
128
129 Self {
130 cert: identity.cert,
131 key: identity.key,
132 ca,
133 }
134 }
135}
136
137#[derive(Clone, Debug)]
140pub struct Device {
141 pub version: u32,
142 pub cert: Vec<u8>,
143 pub key: Vec<u8>,
144 pub ca: Vec<u8>,
145 pub rune: String,
146}
147
148impl Device {
149 pub fn from_bytes(data: impl AsRef<[u8]>) -> Self {
152 let mut creds = Self::default();
153 log::trace!("Build authenticated credentials from: {:?}", data.as_ref());
154 if let Ok(data) = model::Data::try_from(data.as_ref()) {
155 creds.version = data.version;
156 if let Some(cert) = data.cert {
157 creds.cert = cert
158 }
159 if let Some(key) = data.key {
160 creds.key = key
161 }
162 if let Some(ca) = data.ca {
163 creds.ca = ca
164 }
165 if let Some(rune) = data.rune {
166 creds.rune = rune
167 }
168 }
169 creds
170 }
171
172 pub fn from_path(path: impl AsRef<Path>) -> Self {
176 debug!("Read credentials data from {:?}", path.as_ref());
177 let data = std::fs::read(path).unwrap_or_default();
178 Device::from_bytes(data)
179 }
180
181 pub fn with<V, S>(cert: V, key: V, rune: S) -> Self
184 where
185 V: Into<Vec<u8>>,
186 S: Into<String>,
187 {
188 let ca =
189 load_file_or_default("GL_CA_CRT", CA_RAW).expect("Could not load file from GL_CA_CRT");
190
191 Self {
192 version: CRED_VERSION,
193 cert: cert.into(),
194 key: key.into(),
195 rune: rune.into(),
196 ca
197 }
198 }
199
200 pub fn with_ca<V>(self, ca: V) -> Self
201 where
202 V: Into<Vec<u8>>,
203 {
204 Device {
205 ca: ca.into(),
206 ..self
207 }
208 }
209
210 pub async fn upgrade<T>(mut self, _scheduler: &Scheduler<T>, signer: &Signer) -> Result<Self>
213 where
214 T: TlsConfigProvider,
215 {
216 use Error::*;
217
218 self.version = CRED_VERSION;
219
220 if self.rune.is_empty() {
221 let node_id = self
222 .node_id()
223 .map_err(|e| UpgradeCredentialsError(e.to_string()))?;
224
225 let alt = runeauth::Alternative::new(
226 "pubkey".to_string(),
227 runeauth::Condition::Equal,
228 hex::encode(node_id),
229 false,
230 )
231 .map_err(|e| UpgradeCredentialsError(e.to_string()))?;
232
233 self.rune = signer
234 .create_rune(None, vec![vec![&alt.encode()]])
235 .map_err(|e| UpgradeCredentialsError(e.to_string()))?;
236 };
237 Ok(self)
238 }
239
240 pub fn to_bytes(&self) -> Vec<u8> {
243 self.to_owned().into()
244 }
245}
246
247impl TlsConfigProvider for Device {
248 fn tls_config(&self) -> TlsConfig {
249 tls::TlsConfig::with(&self.cert, &self.key, &self.ca)
250 }
251
252}
253
254impl RuneProvider for Device {
255 fn rune(&self) -> String {
256 self.to_owned().rune
257 }
258}
259
260impl NodeIdProvider for Device {
261 fn node_id(&self) -> Result<Vec<u8>> {
262 get_node_id_from_tls_config(&self.tls_config()).map_err(|_e| {
263 Error::GetFromIdentityError(
264 "node_id could not be retrieved from the certificate".to_string(),
265 )
266 })
267 }
268}
269
270impl From<Device> for Vec<u8> {
271 fn from(value: Device) -> Self {
272 let data: model::Data = value.into();
273 data.into()
274 }
275}
276
277impl From<Device> for model::Data {
278 fn from(device: Device) -> Self {
279 model::Data {
280 version: CRED_VERSION,
281 cert: Some(device.cert),
282 key: Some(device.key),
283 ca: Some(device.ca),
284 rune: Some(device.rune),
285 }
286 }
287}
288
289impl Default for Device {
290 fn default() -> Self {
291 let ca =
292 load_file_or_default("GL_CA_CRT", CA_RAW).expect("Could not load file from GL_CA_CRT");
293 let identity = Identity::default();
294 Self {
295 version: 0,
296 cert: identity.cert,
297 key: identity.key,
298 ca,
299 rune: Default::default(),
300 }
301 }
302}
303
304mod model {
305 use prost::Message;
306 use std::convert::TryFrom;
307
308 #[derive(Message, Clone)]
311 pub struct Data {
312 #[prost(uint32, tag = "1")]
313 pub version: u32,
314 #[prost(bytes, optional, tag = "2")]
315 pub cert: Option<Vec<u8>>,
316 #[prost(bytes, optional, tag = "3")]
317 pub key: Option<Vec<u8>>,
318 #[prost(bytes, optional, tag = "4")]
319 pub ca: Option<Vec<u8>>,
320 #[prost(string, optional, tag = "5")]
321 pub rune: Option<String>,
322 }
323
324 impl TryFrom<&[u8]> for Data {
325 type Error = super::Error;
326
327 fn try_from(buf: &[u8]) -> std::prelude::v1::Result<Self, Self::Error> {
328 let data: Data = Data::decode(buf)?;
329 Ok(data)
330 }
331 }
332
333 impl From<Data> for Vec<u8> {
334 fn from(value: Data) -> Self {
335 value.encode_to_vec()
336 }
337 }
338}
339
340fn load_file_or_default(varname: &str, default: &[u8]) -> Result<Vec<u8>> {
344 match std::env::var(varname) {
345 Ok(fname) => {
346 debug!("Loading file {} for envvar {}", fname, varname);
347 let f = std::fs::read(fname.clone())?;
348 Ok(f)
349 }
350 Err(_) => Ok(default.to_vec()),
351 }
352}
353
354#[cfg(test)]
355mod tests {
356 use super::*;
357
358 #[test]
359 fn test_encode() {
360 let cert: Vec<u8> = vec![99, 98];
361 let key = vec![97, 96];
362 let ca = vec![95, 94];
363 let rune = "non_functional_rune".to_string();
364 let data = model::Data {
365 version: 1,
366 cert: Some(cert.clone()),
367 key: Some(key.clone()),
368 ca: Some(ca.clone()),
369 rune: Some(rune.clone()),
370 };
371 let buf: Vec<u8> = data.into();
372 print!("{:?}", buf);
373 for n in cert {
374 assert!(buf.contains(&n));
375 }
376 for n in key {
377 assert!(buf.contains(&n));
378 }
379 for n in ca {
380 assert!(buf.contains(&n));
381 }
382 for n in rune.as_bytes() {
383 assert!(buf.contains(n));
384 }
385 }
386
387 #[test]
388 fn test_decode() {
389 let data: Vec<u8> = vec![
390 8, 1, 18, 2, 99, 98, 26, 2, 97, 96, 34, 2, 95, 94, 42, 19, 110, 111, 110, 95, 102, 117,
391 110, 99, 116, 105, 111, 110, 97, 108, 95, 114, 117, 110, 101,
392 ];
393 let data = model::Data::try_from(&data[..]).unwrap();
394 assert!(data.version == 1);
395 assert!(data.cert.is_some_and(|d| d == vec![99, 98]));
396 assert!(data.key.is_some_and(|d| d == vec![97, 96]));
397 assert!(data.ca.is_some_and(|d| d == vec![95, 94]));
398 assert!(data.rune.is_some_and(|d| d == *"non_functional_rune"));
399 }
400}