1use base64::{Engine as _, engine::general_purpose::STANDARD as B64};
2use futures::AsyncReadExt;
3use hmac::{Hmac, Mac};
4use serde::{Deserialize, Serialize};
5use sha2::Sha256;
6
7const MAX_CONTROL_MSG: usize = 4096;
8
9#[derive(Debug, Serialize, Deserialize)]
10#[serde(tag = "type")]
11pub enum ControlMsg {
12 Auth {
15 subdomain: Option<String>,
16 nonce: String,
17 hmac: Option<String>,
18 },
19 AuthOk {
20 subdomain: String,
21 url: String,
22 },
23 Error {
24 message: String,
25 },
26}
27
28impl ControlMsg {
29 pub fn encode(&self) -> anyhow::Result<Vec<u8>> {
30 let mut buf = serde_json::to_vec(self)?;
31 buf.push(b'\n');
32 Ok(buf)
33 }
34
35 fn decode(line: &[u8]) -> anyhow::Result<Self> {
36 Ok(serde_json::from_slice(line)?)
37 }
38}
39
40pub fn compute_hmac(secret: &str, nonce: &str) -> String {
42 type HmacSha256 = Hmac<Sha256>;
43 let mut mac =
44 HmacSha256::new_from_slice(secret.as_bytes()).expect("HMAC accepts any key length");
45 mac.update(nonce.as_bytes());
46 B64.encode(mac.finalize().into_bytes())
47}
48
49pub async fn read_msg(stream: &mut yamux::Stream) -> anyhow::Result<ControlMsg> {
51 let mut buf = Vec::with_capacity(256);
52 let mut byte = [0u8; 1];
53 loop {
54 let n = stream.read(&mut byte).await?;
55 if n == 0 {
56 anyhow::bail!("stream closed");
57 }
58 if byte[0] == b'\n' {
59 break;
60 }
61 buf.push(byte[0]);
62 if buf.len() > MAX_CONTROL_MSG {
63 anyhow::bail!("control message too large");
64 }
65 }
66 ControlMsg::decode(&buf)
67}