1use js_sys::JsString;
2use serde::{Deserialize, Serialize};
3use std::str::FromStr;
4use timegraph_identity::{bytes_decode, bytes_encode, ApiKey, Cert, Identity, Role, Signer};
5use wasm_bindgen::prelude::*;
6
7#[wasm_bindgen]
8pub fn version() -> JsString {
9 "1.0.0".into()
10}
11
12#[derive(Serialize, Deserialize)]
13pub struct SskIdentity {
14 pub ns: u64,
15 pub key: String,
16 pub secret: Option<String>,
17 pub expiration: u64,
18 pub user_id: Option<u64>,
19}
20
21impl SskIdentity {
22 fn identity(&self) -> Result<Identity, JsString> {
23 let pk = Signer::pubkey_from_address(&self.key)
24 .and_then(|x| bytes_encode(&x.to_bytes()))
25 .map_err(|e| JsString::from(format!("bad key: {e}")))?;
26 Ok(match self.user_id {
27 Some(user_id) => Identity::User {
28 subgraph_id: self.ns,
29 user_id,
30 key: pk,
31 expiration: self.expiration,
32 origins: "*".to_string(),
33 signature: "".to_string(),
34 },
35 None => Identity::ApiKey {
36 subgraph_id: self.ns,
37 key: pk,
38 expiration: self.expiration,
39 origins: "*".to_string(),
40 signature: "".to_string(),
41 },
42 })
43 }
44}
45
46#[derive(Serialize, Deserialize)]
47pub struct Ssk {
48 pub ns: u64,
49 pub key: String,
50 pub expiration: u64,
51 pub user_id: Option<u64>,
52}
53
54#[wasm_bindgen]
55pub fn new_ssk(identity: JsValue) -> Result<JsString, JsString> {
56 let data: SskIdentity = serde_wasm_bindgen::from_value(identity)
57 .map_err(|e| JsString::from(format!("bad identity: {e}")))?;
58 let mut key = data.identity()?;
59 key.sign(
60 &data
61 .secret
62 .ok_or(JsString::from("secret must be specified"))?,
63 )
64 .map_err(|e| JsString::from(format!("failed to sign session key: {e}")))?;
65 key.to_ssk()
66 .map(JsString::from)
67 .map_err(|e| JsString::from(format!("failed to stringify session key: {e}")))
68}
69
70#[wasm_bindgen]
71pub fn encode_ssk(identity: JsValue) -> Result<JsString, JsString> {
72 let data: SskIdentity = serde_wasm_bindgen::from_value(identity)
73 .map_err(|e| JsString::from(format!("bad identity: {e}")))?;
74 let key = data.identity()?;
75 key.stringify_as_ssk()
76 .map(|(x, _)| JsString::from(x))
77 .map_err(|e| JsString::from(format!("failed to encode SSK data: {e}")))
78}
79
80#[wasm_bindgen]
81pub fn build_ssk(ssk_data: String, signature: JsValue) -> Result<JsString, JsString> {
82 let signature: Vec<u8> = serde_wasm_bindgen::from_value(signature)
83 .map_err(|e| JsString::from(format!("failed to decode signature: {e}")))?;
84 let signature_part =
85 bytes_encode(&signature).map_err(|e| JsString::from(format!("bad signature: {e}")))?;
86 let ssk = ssk_data + &signature_part;
87 Identity::from_ssk(&ssk)
88 .map_err(|e| JsString::from(format!("failed to unpack session key: {e}")))?;
89 Ok(ssk.into())
90}
91
92#[wasm_bindgen]
93pub fn verify_ssk(js_ssk: String) -> Result<JsValue, JsString> {
94 let identity = Identity::from_ssk(&js_ssk)
95 .map_err(|e| JsString::from(format!("failed to unpack session key: {e}")))?;
96 Ok(match identity {
97 Identity::ApiKey {
98 subgraph_id,
99 key,
100 expiration,
101 ..
102 } => serde_wasm_bindgen::to_value(&Ssk {
103 ns: subgraph_id,
104 key,
105 expiration,
106 user_id: None,
107 }),
108 Identity::User {
109 subgraph_id,
110 key,
111 expiration,
112 user_id,
113 ..
114 } => serde_wasm_bindgen::to_value(&Ssk {
115 ns: subgraph_id,
116 key,
117 expiration,
118 user_id: Some(user_id),
119 }),
120 }
121 .map_err(|e| JsString::from(format!("failed to represent session key: {e}")))?)
122}
123
124#[wasm_bindgen]
125pub fn get_pubkey_of(sk: String) -> Result<JsString, JsString> {
126 bytes_decode(&sk)
127 .and_then(|x| Signer::from_bytes(&x))
128 .and_then(|x| x.stringify_public_key())
129 .map(JsString::from)
130 .map_err(|e| JsString::from(format!("failed to decode private key: {e}")))
131}
132
133#[derive(Serialize, Deserialize)]
134pub struct JsApiKey {
135 pub key: String,
136 pub secret: String,
137 pub cert: String,
138}
139
140#[wasm_bindgen]
141pub fn new_apikey(sk: String, role: String) -> Result<JsValue, JsString> {
142 let owner = bytes_decode(&sk)
143 .and_then(|x| Signer::from_bytes(&x))
144 .map_err(|e| JsString::from(format!("failed to decode private key: {e}")))?;
145 let ak =
146 ApiKey::new().map_err(|e| JsString::from(format!("failed to generate key pair: {e}")))?;
147 let r = Role::from_str(&role)
148 .map_err(|e| JsString::from(format!("failed to recognize role: {e}")))?;
149 let cert = ak
150 .certify(r, &owner)
151 .map_err(|e| JsString::from(format!("failed to certify apikey: {e}")))?;
152 make_apikey(&ak, &cert)
153}
154
155fn make_apikey(ak: &ApiKey, cert: &Cert) -> Result<JsValue, JsString> {
156 Ok(serde_wasm_bindgen::to_value(&JsApiKey {
157 key: ak
158 .key()
159 .map_err(|e| JsString::from(format!("failed to stringify apikey: {e}")))?,
160 secret: ak
161 .secret()
162 .map_err(|e| JsString::from(format!("failed to stringify apikey: {e}")))?,
163 cert: cert
164 .stringify()
165 .map_err(|e| JsString::from(format!("failed to stringify cert: {e}")))?,
166 })
167 .map_err(|e| JsString::from(format!("failed to represent apikey: {e}")))?)
168}
169
170#[derive(Serialize, Deserialize)]
171pub struct JsCert {
172 pub owner: String,
173 pub delegate: String,
174 pub role: String,
175}
176
177#[wasm_bindgen]
178pub fn pubkey_as_address(key: String) -> Result<JsString, JsString> {
179 let pubkey = Signer::pubkey_from_bytes(&key.as_bytes())
180 .map_err(|e| JsString::from(format!("failed to decode key: {e}")))?;
181 Signer::pubkey_to_address(&pubkey)
182 .map(JsString::from)
183 .map_err(|e| {
184 JsString::from(format!(
185 "failed to construct address for the public key: {e}"
186 ))
187 })
188}
189
190#[wasm_bindgen]
191pub fn address_as_pubkey(address: String) -> Result<JsString, JsString> {
192 let pubkey = Signer::pubkey_from_address(&address)
193 .map_err(|e| JsString::from(format!("failed to decode wallet: {e}")))?;
194 bytes_encode(&pubkey.to_bytes())
195 .map(JsString::from)
196 .map_err(|e| JsString::from(format!("failed to encode public key: {e}")))
197}
198
199#[wasm_bindgen]
200pub fn verify_cert(cert: String) -> Result<JsValue, JsString> {
201 let cert = Cert::verify_str(&cert)
202 .map_err(|e| JsString::from(format!("failed to verify cert: {e}")))?;
203 Ok(serde_wasm_bindgen::to_value(&JsCert {
204 owner: bytes_encode(&cert.owner.to_bytes())
205 .map_err(|e| JsString::from(format!("failed to encode owner: {e}")))?,
206 delegate: bytes_encode(&cert.delegate.to_bytes())
207 .map_err(|e| JsString::from(format!("failed to encode delegate: {e}")))?,
208 role: cert.role.to_string(),
209 })
210 .map_err(|e| JsString::from(format!("failed to represent cert: {e}")))?)
211}
212
213#[wasm_bindgen]
214pub fn new_cert(owner: String, role: String) -> Result<JsValue, JsString> {
215 let owner = Signer::pubkey_from_address(&owner)
216 .map_err(|e| JsString::from(format!("failed to decode private key: {e}")))?;
217 let ak =
218 ApiKey::new().map_err(|e| JsString::from(format!("failed to generate key pair: {e}")))?;
219 let r = Role::from_str(&role)
220 .map_err(|e| JsString::from(format!("failed to recognize role: {e}")))?;
221 let cert = ak
222 .build(r, &owner)
223 .map_err(|e| JsString::from(format!("failed to build apikey certificate: {e}")))?;
224 let secret = ak
225 .secret()
226 .map_err(|e| JsString::from(format!("failed to stringify secret: {e}")))?;
227 Ok(serde_wasm_bindgen::to_value(&(&cert, secret))
228 .map_err(|e| JsString::from(format!("failed to serialize the cert and secret: {e}")))?)
229}
230
231#[wasm_bindgen]
232pub fn build_apikey(
233 secret: String,
234 cert: JsValue,
235 signature: JsValue,
236) -> Result<JsValue, JsString> {
237 let signature: Vec<u8> = serde_wasm_bindgen::from_value(signature)
238 .map_err(|e| JsString::from(format!("failed to decode signature: {e}")))?;
239 let cert: Vec<u8> = serde_wasm_bindgen::from_value(cert)
240 .map_err(|e| JsString::from(format!("failed to decode cert: {e}")))?;
241 let cert = Cert::complete(&cert, &signature)
242 .map_err(|e| JsString::from(format!("failed to build apikey certificate: {e}")))?;
243 cert.verify()
244 .map_err(|e| JsString::from(format!("invalid certificate found: {e}")))?;
245 let ak = &JsApiKey {
246 key: cert
247 .stringify_delegate()
248 .map_err(|e| JsString::from(format!("failed to stringify apikey: {e}")))?,
249 secret: secret,
250 cert: cert
251 .stringify()
252 .map_err(|e| JsString::from(format!("failed to stringify cert: {e}")))?,
253 };
254 Ok(serde_wasm_bindgen::to_value(ak)
255 .map_err(|e| JsString::from(format!("failed to represent apikey: {e}")))?)
256}