1use ibig::UBig;
2use serde::{Deserialize, Serialize};
3use wasm_bindgen::prelude::*;
4
5use rose_crypto::cheetah::{PrivateKey, PublicKey, Signature};
6use rose_crypto::slip10::{derive_master_key as derive_master_key_internal, ExtendedKey};
7
8#[wasm_bindgen(js_name = Signature)]
9#[derive(Clone, Serialize, Deserialize)]
10pub struct WasmSignature {
11 #[wasm_bindgen(skip)]
12 pub c: Vec<u8>,
13 #[wasm_bindgen(skip)]
14 pub s: Vec<u8>,
15}
16
17#[wasm_bindgen(js_class = Signature)]
18impl WasmSignature {
19 #[wasm_bindgen(constructor)]
20 pub fn new(c: Vec<u8>, s: Vec<u8>) -> Self {
21 Self { c, s }
22 }
23
24 #[wasm_bindgen(getter)]
25 pub fn c(&self) -> Vec<u8> {
26 self.c.clone()
27 }
28
29 #[wasm_bindgen(getter)]
30 pub fn s(&self) -> Vec<u8> {
31 self.s.clone()
32 }
33
34 fn from_internal(sig: &Signature) -> Self {
35 Self {
36 c: sig.c.to_be_bytes(),
37 s: sig.s.to_be_bytes(),
38 }
39 }
40
41 fn to_internal(&self) -> Signature {
42 Signature {
43 c: UBig::from_be_bytes(&self.c),
44 s: UBig::from_be_bytes(&self.s),
45 }
46 }
47}
48
49#[wasm_bindgen(js_name = ExtendedKey)]
50#[derive(Serialize, Deserialize)]
51pub struct WasmExtendedKey {
52 #[wasm_bindgen(skip)]
53 pub private_key: Option<Vec<u8>>,
54 #[wasm_bindgen(skip)]
55 pub public_key: Vec<u8>,
56 #[wasm_bindgen(skip)]
57 pub chain_code: Vec<u8>,
58}
59
60#[wasm_bindgen(js_class = ExtendedKey)]
61impl WasmExtendedKey {
62 #[wasm_bindgen(getter, js_name = privateKey)]
63 pub fn private_key(&self) -> Option<Vec<u8>> {
64 self.private_key.clone()
65 }
66
67 #[wasm_bindgen(getter, js_name = publicKey)]
68 pub fn public_key(&self) -> Vec<u8> {
69 self.public_key.clone()
70 }
71
72 #[wasm_bindgen(getter, js_name = chainCode)]
73 pub fn chain_code(&self) -> Vec<u8> {
74 self.chain_code.clone()
75 }
76
77 #[wasm_bindgen(js_name = deriveChild)]
79 pub fn derive_child(&self, index: u32) -> Result<WasmExtendedKey, JsValue> {
80 let extended_key = self.to_internal().map_err(|e| JsValue::from_str(&e))?;
81
82 let child = extended_key.derive_child(index);
83 Ok(WasmExtendedKey::from_internal(&child))
84 }
85
86 fn to_internal(&self) -> Result<ExtendedKey, String> {
87 let private_key = if let Some(pk_bytes) = &self.private_key {
88 if pk_bytes.len() != 32 {
89 return Err("Private key must be 32 bytes".to_string());
90 }
91 Some(PrivateKey(UBig::from_be_bytes(pk_bytes)))
92 } else {
93 None
94 };
95
96 if self.public_key.len() != 97 {
97 return Err("Public key must be 97 bytes".to_string());
98 }
99 let mut pub_bytes = [0u8; 97];
100 pub_bytes.copy_from_slice(&self.public_key);
101 let public_key = PublicKey::from_be_bytes(&self.public_key);
102
103 if self.chain_code.len() != 32 {
104 return Err("Chain code must be 32 bytes".to_string());
105 }
106 let mut chain_code = [0u8; 32];
107 chain_code.copy_from_slice(&self.chain_code);
108
109 Ok(ExtendedKey {
110 private_key,
111 public_key,
112 chain_code,
113 })
114 }
115
116 fn from_internal(key: &ExtendedKey) -> Self {
117 WasmExtendedKey {
118 private_key: key.private_key.as_ref().map(|pk| pk.to_be_bytes().to_vec()),
119 public_key: key.public_key.to_be_bytes().to_vec(),
120 chain_code: key.chain_code.to_vec(),
121 }
122 }
123}
124
125#[wasm_bindgen(js_name = deriveMasterKey)]
127pub fn derive_master_key(seed: &[u8]) -> WasmExtendedKey {
128 let key = derive_master_key_internal(seed);
129 WasmExtendedKey::from_internal(&key)
130}
131
132#[wasm_bindgen(js_name = deriveMasterKeyFromMnemonic)]
134pub fn derive_master_key_from_mnemonic(
135 mnemonic: &str,
136 passphrase: Option<String>,
137) -> Result<WasmExtendedKey, JsValue> {
138 use bip39::Mnemonic;
139
140 let mnemonic = Mnemonic::parse(mnemonic)
141 .map_err(|e| JsValue::from_str(&format!("Invalid mnemonic: {}", e)))?;
142
143 let seed = mnemonic.to_seed(passphrase.as_deref().unwrap_or(""));
144 Ok(derive_master_key(&seed))
145}
146
147#[wasm_bindgen(js_name = hashPublicKey)]
149pub fn hash_public_key(public_key_bytes: &[u8]) -> Result<String, JsValue> {
150 use rose_ztd::Hashable;
151
152 if public_key_bytes.len() != 97 {
153 return Err(JsValue::from_str("Public key must be 97 bytes"));
154 }
155
156 let mut pub_bytes = [0u8; 97];
157 pub_bytes.copy_from_slice(public_key_bytes);
158 let public_key = PublicKey::from_be_bytes(&pub_bytes);
159
160 let digest = public_key.hash();
161 Ok(digest.to_string())
162}
163
164#[wasm_bindgen(js_name = hashU64)]
166pub fn hash_u64(value: u64) -> String {
167 use rose_ztd::Hashable;
168 let digest = value.hash();
169 digest.to_string()
170}
171
172#[wasm_bindgen(js_name = hashNoun)]
174pub fn hash_noun(noun: &[u8]) -> Result<String, JsValue> {
175 use rose_ztd::{cue, Hashable};
176 let noun = cue(noun).ok_or("Unable to cue noun")?;
177 let digest = noun.hash();
178 Ok(digest.to_string())
179}
180
181#[wasm_bindgen(js_name = signMessage)]
183pub fn sign_message(private_key_bytes: &[u8], message: &str) -> Result<WasmSignature, JsValue> {
184 use rose_ztd::{Belt, Hashable, NounEncode};
185 if private_key_bytes.len() != 32 {
186 return Err(JsValue::from_str("Private key must be 32 bytes"));
187 }
188 let private_key = PrivateKey(UBig::from_be_bytes(private_key_bytes));
189 let digest = Belt::from_bytes(message.as_bytes()).to_noun().hash();
190 Ok(WasmSignature::from_internal(&private_key.sign(&digest)))
191}
192
193#[wasm_bindgen(js_name = verifySignature)]
195pub fn verify_signature(
196 public_key_bytes: &[u8],
197 signature: &WasmSignature,
198 message: &str,
199) -> Result<bool, JsValue> {
200 use rose_ztd::{Belt, Hashable, NounEncode};
201 if public_key_bytes.len() != 97 {
202 return Err(JsValue::from_str("Public key must be 97 bytes"));
203 }
204 let mut pub_bytes = [0u8; 97];
205 pub_bytes.copy_from_slice(public_key_bytes);
206 let public_key = PublicKey::from_be_bytes(&pub_bytes);
207 let digest = Belt::from_bytes(message.as_bytes()).to_noun().hash();
208 Ok(public_key.verify(&digest, &signature.to_internal()))
209}