gl_client/pairing/
new_device.rs

1use super::PairingSessionData;
2use crate::{
3    credentials::{Device, TlsConfigProvider},
4    pb::scheduler::{pairing_client::PairingClient, PairDeviceRequest},
5    tls::{self, TlsConfig},
6};
7use log::debug;
8use tokio::sync::mpsc;
9use tonic::transport::Channel;
10
11type Result<T, E = super::Error> = core::result::Result<T, E>;
12
13pub struct Unconnected();
14pub struct Connected(PairingClient<Channel>);
15
16pub struct Client<T> {
17    inner: T,
18    uri: String,
19    tls: TlsConfig,
20}
21
22impl Client<Unconnected> {
23    pub fn new<T>(creds: T) -> Client<Unconnected>
24    where
25        T: TlsConfigProvider,
26    {
27        Client {
28            inner: Unconnected(),
29            uri: crate::utils::scheduler_uri(),
30            tls: creds.tls_config(),
31        }
32    }
33}
34
35impl Client<Unconnected> {
36    pub fn with_uri(mut self, uri: String) -> Client<Unconnected> {
37        self.uri = uri;
38        self
39    }
40
41    pub async fn connect(self) -> Result<Client<Connected>> {
42        let channel = tonic::transport::Endpoint::from_shared(self.uri.clone())?
43            .tls_config(self.tls.inner.clone())?
44            .tcp_keepalive(Some(crate::TCP_KEEPALIVE))
45            .http2_keep_alive_interval(crate::TCP_KEEPALIVE)
46            .keep_alive_timeout(crate::TCP_KEEPALIVE_TIMEOUT)
47            .keep_alive_while_idle(true)
48            .connect_lazy();
49        Ok(Client {
50            inner: Connected(PairingClient::new(channel)),
51            uri: self.uri,
52            tls: self.tls,
53        })
54    }
55}
56
57impl Client<Connected> {
58    pub async fn pair_device(
59        &self,
60        name: &str,
61        description: &str,
62        restrictions: &str,
63    ) -> Result<mpsc::Receiver<PairingSessionData>> {
64        debug!("Start a new pairing request");
65
66        let device_name = name.to_string();
67        let description = description.to_string();
68
69        // Generate key pair.
70        let kp = tls::generate_ecdsa_key_pair();
71
72        // Generate csr.
73        let device_cert = tls::generate_self_signed_device_cert(
74            &hex::encode("00"), // We don't know the node id yet, this is to be filled out by the attestation device.
75            name,
76            vec!["localhost".into()],
77            Some(kp),
78        );
79        let device_id = hex::encode(device_cert.get_key_pair().public_key_raw());
80        let csr = device_cert.serialize_request_pem()?;
81
82        // Restrictions should always contain the pubkey field to bind them to
83        // the private key of the device.
84        let mut restriction = format!("pubkey={}", device_id.clone());
85        if !restrictions.is_empty() {
86            // Append restrictions if set.
87            restriction = format!("{}&{}", restriction, restrictions);
88        }
89        let restrictions = restriction;
90
91        // Create a channel to communicate beyond the bounds of this function
92        let (tx, rx) = mpsc::channel(1);
93
94        let mut client = self.inner.0.clone();
95        // The worker that handles the pairing. Communicate to the outside world
96        // via the channel.
97        tokio::spawn(async move {
98            // Step 1 of the Pairing Protocol: Request pairing at the Greenlight
99            // Backend.
100            let request = client.pair_device(PairDeviceRequest {
101                device_id: device_id.clone(),
102                csr: csr.into_bytes(),
103                device_name,
104                description,
105                restrictions,
106            });
107
108            // Step 2 of the Pairing Protocol: Return the PairingQR for the new
109            // device to show it to an old device.
110            let data = format!("gl-pairing:{}", device_id);
111            tx.send(PairingSessionData::PairingQr(data))
112                .await
113                .expect("could not pass qr data to the channel"); // We can unwrap here as there is no need to continue if the channel is broken.
114
115            // Step 8 of the Pairing Protocol: Get back the response. We do fire
116            // and forget here.
117            let _ = match request.await {
118                Ok(r) => {
119                    let mut res = r.into_inner();
120                    res.device_key = device_cert.serialize_private_key_pem();
121                    let creds = Device::with(
122                        res.device_cert.clone().into_bytes(),
123                        res.device_key.clone().into_bytes(),
124                        res.rune.clone(),
125                    );
126
127                    res.creds = creds.into();
128                    tx.send(PairingSessionData::PairingResponse(res))
129                }
130                Err(e) => {
131                    debug!("got an error during pairing process {}.", e);
132                    tx.send(PairingSessionData::PairingError(e))
133                }
134            }
135            .await;
136
137            return;
138        });
139
140        Ok(rx)
141    }
142}