sos_net/pairing/
share_url.rs1use super::{Error, Result};
2use hex;
3use rand::Rng;
4use sos_core::{csprng, AccountId};
5use std::str::FromStr;
6use url::Url;
7
8const AID: &str = "aid";
10const URL: &str = "url";
12const KEY: &str = "key";
14const PSK: &str = "psk";
16
17#[derive(Debug, Clone)]
19pub struct ServerPairUrl {
20 account_id: AccountId,
22 server: Url,
24 public_key: Vec<u8>,
26 pre_shared_key: [u8; 32],
28}
29
30impl ServerPairUrl {
31 pub fn new(
36 account_id: AccountId,
37 server: Url,
38 public_key: Vec<u8>,
39 ) -> Self {
40 let pre_shared_key: [u8; 32] = csprng().gen();
41 Self {
42 account_id,
43 server,
44 public_key,
45 pre_shared_key,
46 }
47 }
48
49 pub fn account_id(&self) -> &AccountId {
51 &self.account_id
52 }
53
54 pub fn server(&self) -> &Url {
56 &self.server
57 }
58
59 pub fn public_key(&self) -> &[u8] {
61 &self.public_key
62 }
63
64 pub fn pre_shared_key(&self) -> [u8; 32] {
66 self.pre_shared_key
67 }
68}
69
70impl From<ServerPairUrl> for Url {
71 fn from(value: ServerPairUrl) -> Self {
72 let mut url = Url::parse("data:text/plain,sos-pair").unwrap();
73 let key = hex::encode(&value.public_key);
74 let psk = hex::encode(&value.pre_shared_key);
75 url.query_pairs_mut()
76 .append_pair(AID, &value.account_id.to_string())
77 .append_pair(URL, &value.server.to_string())
78 .append_pair(KEY, &key)
79 .append_pair(PSK, &psk);
80 url
81 }
82}
83
84impl FromStr for ServerPairUrl {
85 type Err = Error;
86
87 fn from_str(s: &str) -> Result<Self> {
88 let url = Url::parse(s)?;
89
90 if url.scheme() != "data" {
91 return Err(Error::InvalidShareUrl);
92 }
93
94 if url.path() != "text/plain,sos-pair" {
95 return Err(Error::InvalidShareUrl);
96 }
97
98 let mut pairs = url.query_pairs();
99
100 let account_id = pairs.find_map(|q| {
101 if q.0.as_ref() == AID {
102 Some(q.1)
103 } else {
104 None
105 }
106 });
107 let account_id = account_id.ok_or(Error::InvalidShareUrl)?;
108 let account_id: AccountId = account_id.as_ref().parse()?;
109
110 let server = pairs.find_map(|q| {
111 if q.0.as_ref() == URL {
112 Some(q.1)
113 } else {
114 None
115 }
116 });
117 let server = server.ok_or(Error::InvalidShareUrl)?;
118 let server: Url = server.as_ref().parse()?;
119
120 let key = pairs.find_map(|q| {
121 if q.0.as_ref() == KEY {
122 Some(q.1)
123 } else {
124 None
125 }
126 });
127 let key = key.ok_or(Error::InvalidShareUrl)?;
128 let key = hex::decode(key.as_ref())?;
129
130 let psk = pairs.find_map(|q| {
131 if q.0.as_ref() == PSK {
132 Some(q.1)
133 } else {
134 None
135 }
136 });
137 let psk = psk.ok_or(Error::InvalidShareUrl)?;
138 let psk = hex::decode(psk.as_ref())?;
139 let psk: [u8; 32] = psk.as_slice().try_into()?;
140
141 Ok(Self {
142 account_id,
143 server,
144 public_key: key,
145 pre_shared_key: psk,
146 })
147 }
148}