timegraph_wasm/
lib.rs

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}