use crate::account::{Encryptor, PrivateKey};
use crate::types::native::CiphertextNative;
use std::{ops::Deref, str::FromStr};
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct PrivateKeyCiphertext(CiphertextNative);
#[wasm_bindgen]
impl PrivateKeyCiphertext {
#[wasm_bindgen(js_name = encryptPrivateKey)]
pub fn encrypt_private_key(private_key: &PrivateKey, secret: &str) -> Result<PrivateKeyCiphertext, String> {
let ciphertext = Encryptor::encrypt_private_key_with_secret(private_key, secret)
.map_err(|_| "Encryption failed".to_string())?;
Ok(Self::from(ciphertext))
}
#[wasm_bindgen(js_name = decryptToPrivateKey)]
pub fn decrypt_to_private_key(&self, secret: &str) -> Result<PrivateKey, String> {
let private_key = Encryptor::decrypt_private_key_with_secret(&self.0, secret)
.map_err(|_| "Decryption failed - ciphertext was not a private key")?;
Ok(PrivateKey::from(private_key))
}
#[allow(clippy::inherent_to_string)]
#[wasm_bindgen(js_name = toString)]
pub fn to_string(&self) -> String {
self.0.to_string()
}
#[wasm_bindgen(js_name = fromString)]
pub fn from_string(ciphertext: String) -> Result<PrivateKeyCiphertext, String> {
Self::try_from(ciphertext).map_err(|_| "Invalid ciphertext".to_string())
}
}
impl From<CiphertextNative> for PrivateKeyCiphertext {
fn from(ciphertext: CiphertextNative) -> Self {
Self(ciphertext)
}
}
impl TryFrom<String> for PrivateKeyCiphertext {
type Error = String;
fn try_from(ciphertext: String) -> Result<Self, Self::Error> {
Ok(Self(CiphertextNative::from_str(&ciphertext).map_err(|_| "Invalid ciphertext".to_string())?))
}
}
impl Deref for PrivateKeyCiphertext {
type Target = CiphertextNative;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[cfg(test)]
mod tests {
use super::*;
use wasm_bindgen_test::wasm_bindgen_test;
#[wasm_bindgen_test]
fn test_private_key_ciphertext_to_and_from_string() {
let private_key = PrivateKey::new();
let private_key_ciphertext = PrivateKeyCiphertext::encrypt_private_key(&private_key, "mypasword").unwrap();
let private_key_ciphertext_2 = PrivateKeyCiphertext::from_string(private_key_ciphertext.to_string()).unwrap();
assert_eq!(private_key_ciphertext, private_key_ciphertext_2);
}
#[wasm_bindgen_test]
fn test_private_key_from_string_decryption_edge_cases() {
let private_key =
PrivateKey::from_string("APrivateKey1zkpAYS46Dq4rnt9wdohyWMwdmjmTeMJKPZdp5AhvjXZDsVG").unwrap();
let ciphertext = "ciphertext1qvqg7rgvam3xdcu55pwu6sl8rxwefxaj5gwthk0yzln6jv5fastzup0qn0qftqlqq7jcckyx03fzv9kke0z9puwd7cl7jzyhxfy2f2juplz39dkqs6p24urhxymhv364qm3z8mvyklv5gr52n4fxr2z59jgqytyddj8";
let private_key_ciphertext = PrivateKeyCiphertext::from_string(ciphertext.to_string()).unwrap();
let decrypted_private_key = private_key_ciphertext.decrypt_to_private_key("mypassword").unwrap();
assert_eq!(private_key, decrypted_private_key);
assert!(private_key_ciphertext.decrypt_to_private_key("badpassword").is_err());
let bad_ciphertext = "ciphertext1qvqg7rgvam3xdcu55pwu6sl8rxwefxaj5gwthk0yzln6jv5fastzup0qn0qftqlqq7jcckyx03fzv9kke0z9puwd7cl7jzyhxfy2f2juplz39dkqs6p24urhxymhv364qm3z8mvyklv5er52n4fxr2z59jgqytyddj8";
assert!(PrivateKeyCiphertext::from_string(bad_ciphertext.to_string()).is_err());
}
#[wasm_bindgen_test]
fn test_private_key_ciphertext_encrypt_and_decrypt() {
let private_key = PrivateKey::new();
let private_key_ciphertext = PrivateKeyCiphertext::encrypt_private_key(&private_key, "mypassword").unwrap();
let recovered_private_key = private_key_ciphertext.decrypt_to_private_key("mypassword").unwrap();
assert_eq!(private_key, recovered_private_key);
}
#[wasm_bindgen_test]
fn test_private_key_ciphertext_doesnt_decrypt_with_wrong_password() {
let private_key = PrivateKey::new();
let private_key_ciphertext = PrivateKeyCiphertext::encrypt_private_key(&private_key, "mypassword").unwrap();
let recovered_private_key = private_key_ciphertext.decrypt_to_private_key("wrong_password");
assert!(recovered_private_key.is_err())
}
#[wasm_bindgen_test]
fn test_private_key_ciphertext_doesnt_produce_same_ciphertext_on_different_runs() {
let private_key = PrivateKey::new();
let private_key_ciphertext = PrivateKeyCiphertext::encrypt_private_key(&private_key, "mypassword").unwrap();
let private_key_ciphertext_2 = PrivateKeyCiphertext::encrypt_private_key(&private_key, "mypassword").unwrap();
assert_ne!(private_key_ciphertext, private_key_ciphertext_2);
let recovered_key_1 = private_key_ciphertext.decrypt_to_private_key("mypassword").unwrap();
let recovered_key_2 = private_key_ciphertext_2.decrypt_to_private_key("mypassword").unwrap();
assert_eq!(recovered_key_1, recovered_key_2);
}
#[wasm_bindgen_test]
fn test_private_key_ciphertext_encrypted_with_different_passwords_match() {
let private_key = PrivateKey::new();
let private_key_ciphertext = PrivateKeyCiphertext::encrypt_private_key(&private_key, "mypassword").unwrap();
let private_key_ciphertext_2 = PrivateKeyCiphertext::encrypt_private_key(&private_key, "mypassword2").unwrap();
assert_ne!(private_key_ciphertext, private_key_ciphertext_2);
let recovered_key_1 = private_key_ciphertext.decrypt_to_private_key("mypassword").unwrap();
let recovered_key_2 = private_key_ciphertext_2.decrypt_to_private_key("mypassword2").unwrap();
assert_eq!(recovered_key_1, recovered_key_2);
}
#[wasm_bindgen_test]
fn test_private_key_ciphertext_different_private_keys_encrypted_with_same_password_dont_match() {
let private_key = PrivateKey::new();
let private_key_2 = PrivateKey::new();
let private_key_ciphertext = PrivateKeyCiphertext::encrypt_private_key(&private_key, "mypassword").unwrap();
let private_key_ciphertext_2 = PrivateKeyCiphertext::encrypt_private_key(&private_key_2, "mypassword").unwrap();
assert_ne!(private_key_ciphertext, private_key_ciphertext_2);
let recovered_key_1 = private_key_ciphertext.decrypt_to_private_key("mypassword").unwrap();
let recovered_key_2 = private_key_ciphertext_2.decrypt_to_private_key("mypassword").unwrap();
assert_ne!(recovered_key_1, recovered_key_2);
}
#[wasm_bindgen_test]
fn test_private_key_ciphertext_decrypts_properly_when_formed_with_secret() {
let private_key_ciphertext_1 = PrivateKey::new_encrypted("mypassword").unwrap();
let private_key_ciphertext_2 = PrivateKey::new_encrypted("mypassword").unwrap();
assert_ne!(private_key_ciphertext_1, private_key_ciphertext_2);
let recovered_private_key_1_1 = private_key_ciphertext_1.decrypt_to_private_key("mypassword").unwrap();
let recovered_private_key_1_2 =
PrivateKey::from_private_key_ciphertext(&private_key_ciphertext_1, "mypassword").unwrap();
assert_eq!(recovered_private_key_1_1, recovered_private_key_1_2);
let recovered_private_key_2_1 = private_key_ciphertext_2.decrypt_to_private_key("mypassword").unwrap();
let recovered_private_key_2_2 =
PrivateKey::from_private_key_ciphertext(&private_key_ciphertext_2, "mypassword").unwrap();
assert_eq!(recovered_private_key_2_1, recovered_private_key_2_2);
assert_ne!(recovered_private_key_1_1, recovered_private_key_2_1);
assert_ne!(recovered_private_key_1_2, recovered_private_key_2_2);
assert_ne!(recovered_private_key_1_1, recovered_private_key_2_1);
assert_ne!(recovered_private_key_1_2, recovered_private_key_2_2);
}
#[wasm_bindgen_test]
fn test_private_key_encryption_functions() {
let private_key = PrivateKey::new();
let private_key_ciphertext = private_key.to_ciphertext("mypassword").unwrap();
let recovered_private_key_1 =
PrivateKey::from_private_key_ciphertext(&private_key_ciphertext, "mypassword").unwrap();
let recovered_private_key_2 = private_key_ciphertext.decrypt_to_private_key("mypassword").unwrap();
assert_eq!(private_key, recovered_private_key_2);
assert_eq!(private_key, recovered_private_key_1);
assert_eq!(recovered_private_key_1, recovered_private_key_2);
let bad_secret_attempt = PrivateKey::from_private_key_ciphertext(&private_key_ciphertext, "badpassword");
assert!(bad_secret_attempt.is_err());
}
}