Send and receive messages

This commit is contained in:
Sosthene 2024-05-04 01:18:05 +02:00
parent f1c2f0e4ed
commit 870fdc831f
3 changed files with 83 additions and 27 deletions

View File

@ -16,6 +16,7 @@ use serde_json::Error as SerdeJsonError;
use shamir::SecretData; use shamir::SecretData;
use sp_client::bitcoin::consensus::{deserialize, serialize}; use sp_client::bitcoin::consensus::{deserialize, serialize};
use sp_client::bitcoin::hex::{parse, DisplayHex, FromHex, HexToBytesError}; use sp_client::bitcoin::hex::{parse, DisplayHex, FromHex, HexToBytesError};
use sp_client::bitcoin::secp256k1::ecdh::shared_secret_point;
use sp_client::bitcoin::secp256k1::{PublicKey, SecretKey}; use sp_client::bitcoin::secp256k1::{PublicKey, SecretKey};
use sp_client::bitcoin::{Amount, OutPoint, Transaction, Txid}; use sp_client::bitcoin::{Amount, OutPoint, Transaction, Txid};
use sp_client::silentpayments::Error as SpError; use sp_client::silentpayments::Error as SpError;
@ -336,27 +337,40 @@ pub fn check_transaction_for_silent_payments(
tweak_data_hex: String, tweak_data_hex: String,
) -> ApiResult<String> { ) -> ApiResult<String> {
let tx = deserialize::<Transaction>(&Vec::from_hex(&tx_hex)?)?; let tx = deserialize::<Transaction>(&Vec::from_hex(&tx_hex)?)?;
// check that we don't already have scanned the tx
if lock_scanned_transactions()?.contains_key(&tx.txid()) {
return Err(ApiError { message: "Transaction already scanned".to_owned()});
}
let tweak_data = PublicKey::from_str(&tweak_data_hex)?; let tweak_data = PublicKey::from_str(&tweak_data_hex)?;
let mut connected_user = lock_connected_user()?; let mut connected_user = lock_connected_user()?;
if let Ok(recover) = connected_user.try_get_mut_recover() { if let Ok(recover) = connected_user.try_get_mut_recover() {
if let Ok(txid) = check_transaction(&tx, recover, blockheight, tweak_data) { if let Ok(txid) = check_transaction(&tx, recover, blockheight, tweak_data) {
let shared_point = shared_secret_point(&tweak_data, &recover.get_client().get_scan_key());
lock_scanned_transactions()?.insert(tx.txid(), vec![("".to_owned(), AnkSharedSecret::new(shared_point, false))]);
return Ok(txid); return Ok(txid);
} }
} }
if let Ok(main) = connected_user.try_get_mut_main() { if let Ok(main) = connected_user.try_get_mut_main() {
if let Ok(txid) = check_transaction(&tx, main, blockheight, tweak_data) { if let Ok(txid) = check_transaction(&tx, main, blockheight, tweak_data) {
let shared_point = shared_secret_point(&tweak_data, &main.get_client().get_scan_key());
lock_scanned_transactions()?.insert(tx.txid(), vec![("".to_owned(), AnkSharedSecret::new(shared_point, false))]);
return Ok(txid); return Ok(txid);
} }
} }
if let Ok(revoke) = connected_user.try_get_mut_revoke() { if let Ok(revoke) = connected_user.try_get_mut_revoke() {
if let Ok(txid) = check_transaction(&tx, revoke, blockheight, tweak_data) { if let Ok(txid) = check_transaction(&tx, revoke, blockheight, tweak_data) {
let shared_point = shared_secret_point(&tweak_data, &revoke.get_client().get_scan_key());
lock_scanned_transactions()?.insert(tx.txid(), vec![("".to_owned(), AnkSharedSecret::new(shared_point, false))]);
return Ok(txid); return Ok(txid);
} }
} }
// we still want to insert an empty entry in our cache to make sure we don't scan the transaction again
lock_scanned_transactions()?.insert(tx.txid(), vec![("".to_owned(), AnkSharedSecret::default())]);
Err(ApiError { Err(ApiError {
message: "No output found".to_owned(), message: "No output found".to_owned(),
}) })
@ -399,23 +413,30 @@ pub fn parse_network_msg(raw: String) -> ApiResult<parseNetworkMsgReturn> {
}) })
} }
AnkFlag::Unknown => { AnkFlag::Unknown => {
let transaction_cache = lock_scanned_transactions()?;
// try to decrypt the cipher with all available keys // try to decrypt the cipher with all available keys
let mut plaintext: String = "".to_owned(); let mut plaintext: String = "".to_owned();
for (txid, secret_vec) in transaction_cache.iter() { for (txid, secret_vec) in lock_scanned_transactions()?.iter() {
for (shared_with, ank_secret) in secret_vec.iter() { for (shared_with, ank_secret) in secret_vec.iter() {
if *ank_secret == AnkSharedSecret::default() {
continue;
}
let shared_secret = ank_secret.to_byte_array(); let shared_secret = ank_secret.to_byte_array();
if let Ok(msg_decrypt) = Aes256Decryption::new( if let Ok(msg_decrypt) = Aes256Decryption::new(
Purpose::Arbitrary, Purpose::Arbitrary,
ank_msg.content.as_bytes().to_vec(), Vec::from_hex(&ank_msg.content.trim_matches('\"'))?,
shared_secret, shared_secret,
) { ) {
if let Ok(plain) = msg_decrypt.decrypt_with_key() { match msg_decrypt.decrypt_with_key() {
plaintext = String::from_utf8(plain)?; Ok(plain) => {
break; plaintext = String::from_utf8(plain)?;
break;
},
Err(e) => {
debug!("{}", e);
debug!("Failed to decrypt message {} with key {}", ank_msg.content, shared_secret.to_lower_hex_string());
}
} }
} }
continue;
} }
} }
if plaintext.is_empty() { if plaintext.is_empty() {
@ -523,6 +544,8 @@ pub fn create_notification_transaction(
Amount::from_sat(fee_rate.into()), Amount::from_sat(fee_rate.into()),
)?; )?;
debug!("Created transaction with secret {}", shared_secret.to_byte_array().to_lower_hex_string());
let mut address2secret: Vec<(String, AnkSharedSecret)> = vec![]; let mut address2secret: Vec<(String, AnkSharedSecret)> = vec![];
address2secret.push((sp_address.into(), shared_secret)); address2secret.push((sp_address.into(), shared_secret));
@ -560,7 +583,8 @@ pub fn encrypt_with_key(plaintext: String, key: String) -> ApiResult<String> {
)?; )?;
let cipher = aes_enc.encrypt_with_aes_key()?; let cipher = aes_enc.encrypt_with_aes_key()?;
Ok(String::from_utf8(cipher)?)
Ok(cipher.to_lower_hex_string())
} }
#[wasm_bindgen] #[wasm_bindgen]

View File

@ -107,17 +107,31 @@ class Services {
let notificationInfo = await services.notify_address_for_message(recipientSpAddress, message); let notificationInfo = await services.notify_address_for_message(recipientSpAddress, message);
if (notificationInfo) { if (notificationInfo) {
let ciphers: string[] = [];
console.info('Successfully sent notification transaction'); console.info('Successfully sent notification transaction');
// Save the secret to db // Save the secret to db
// encrypt the message(s) // encrypt the message(s)
services.encryptData(message, notificationInfo.address2secret); for (const [address, ankSharedSecret] of Object.entries(notificationInfo.address2secret)) {
// encrypt the key try {
let cipher = await services.encryptData(message, ankSharedSecret.secret);
ciphers.push(cipher);
} catch (error) {
throw error;
}
}
const connection = await services.pickWebsocketConnectionRandom();
const flag: AnkFlag = "Unknown";
// for testing we only take the first cipher
const payload = ciphers.at(0);
if (!payload) {
console.error("No payload");
return;
}
// add peers list // add peers list
// add processes list // add processes list
// send message (transaction in envelope) // send message (transaction in envelope)
connection?.sendMessage(flag, payload);
} }
console.log(notificationInfo);
} }
public async createId(event: Event): Promise<void> { public async createId(event: Event): Promise<void> {
@ -224,6 +238,10 @@ class Services {
if (user) { if (user) {
services.sdkClient.login_user(password, user.pre_id, user.recover_data, user.shares, user.outputs); services.sdkClient.login_user(password, user.pre_id, user.recover_data, user.shares, user.outputs);
this.sp_address = services.sdkClient.get_recover_address(); this.sp_address = services.sdkClient.get_recover_address();
if (this.sp_address) {
console.info('Using sp_address:', this.sp_address);
await services.obtainTokenWithFaucet(this.sp_address);
}
} }
} catch (error) { } catch (error) {
console.error(error); console.error(error);
@ -802,7 +820,7 @@ class Services {
} }
try { try {
const feeRate = 1000; const feeRate = 1;
let notificationInfo: createNotificationTransactionReturn = services.sdkClient.create_notification_transaction(sp_address, message, feeRate); let notificationInfo: createNotificationTransactionReturn = services.sdkClient.create_notification_transaction(sp_address, message, feeRate);
const flag: AnkFlag = "NewTx"; const flag: AnkFlag = "NewTx";
const newTxMsg: NewTxMessage = { const newTxMsg: NewTxMessage = {
@ -817,26 +835,38 @@ class Services {
} }
} }
public async encryptData(data: string, sharedSecret: Record<string, AnkSharedSecret>): Promise<encryptWithNewKeyResult> { // public async encryptData(data: string, sharedSecret: Record<string, AnkSharedSecret>): Promise<Map<string, string>> {
// const services = await Services.getInstance();
// let msg_cipher: encryptWithNewKeyResult;
// try {
// msg_cipher = services.sdkClient.encrypt_with_new_key(data);
// } catch (error) {
// throw error;
// }
// let res = new Map<string, string>();
// for (const [recipient, secret] of Object.entries(sharedSecret)) {
// try {
// const key = secret.secret;
// const encryptedKey: string = await services.sdkClient.encrypt_with_key(msg_cipher.key, key);
// res.set(recipient, encryptedKey);
// } catch (error) {
// throw new Error(`Failed to encrypt key for recipient ${recipient}: ${error}`);
// }
// }
// return res;
// }
public async encryptData(data: string, key: string): Promise<string> {
const services = await Services.getInstance(); const services = await Services.getInstance();
let msg_cipher: encryptWithNewKeyResult;
try { try {
msg_cipher = services.sdkClient.encrypt_with_new_key(data); let res: string = services.sdkClient.encrypt_with_key(data, key);
return res;
} catch (error) { } catch (error) {
throw error; throw error;
} }
let res = new Map<string, string>();
for (const [recipient, secret] of Object.entries(sharedSecret)) {
try {
const key = secret.secret;
const encryptedKey = await services.sdkClient.encrypt_with_key(msg_cipher.key, key);
res.set(recipient, encryptedKey);
} catch (error) {
throw new Error(`Failed to encrypt key for recipient ${recipient}: ${error}`);
}
} }
}
public async decryptData(cipher: string, key: string): Promise<string> { public async decryptData(cipher: string, key: string): Promise<string> {
const services = await Services.getInstance(); const services = await Services.getInstance();

View File

@ -32,6 +32,8 @@ class WebSocketClient {
// we received a tx // we received a tx
window.alert(`New tx\n${res.message}`); window.alert(`New tx\n${res.message}`);
await services.updateOwnedOutputsForUser(); await services.updateOwnedOutputsForUser();
} else if (res.topic === 'unknown') {
window.alert(`new message: ${res.message}`);
} }
} }
} else { } else {