diff --git a/Cargo.lock b/Cargo.lock index 89926aba..d8577b40 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2658,6 +2658,7 @@ dependencies = [ "secrecy", "serde", "serde_json", + "snarkvm-algorithms 0.7.5 (git+https://github.com/AleoHQ/snarkVM.git?rev=fc997c)", "snarkvm-dpc 0.7.5 (git+https://github.com/AleoHQ/snarkVM.git?rev=fc997c)", "structopt", "unic-langid", diff --git a/setup1-cli-tools/Cargo.toml b/setup1-cli-tools/Cargo.toml index 85e52cfb..ac88cf13 100644 --- a/setup1-cli-tools/Cargo.toml +++ b/setup1-cli-tools/Cargo.toml @@ -8,12 +8,21 @@ edition = "2018" name = "public-key-extractor" path = "src/public_key_extractor.rs" +[[bin]] +name = "decrypt_text" +path = "src/decrypt_text.rs" + +[[bin]] +name = "encrypt_text" +path = "src/encrypt_text.rs" + [[bin]] name = "view-key" path = "src/view_key.rs" [dependencies] snarkvm-dpc = { git = "https://github.com/AleoHQ/snarkVM", rev = "fc997c" } +snarkvm-algorithms = { git = "https://github.com/AleoHQ/snarkVM", rev = "fc997c" } anyhow = "1.0.38" age = { version = "0.7", features = ["cli-common", "armor", "plugin"] } diff --git a/setup1-cli-tools/README.md b/setup1-cli-tools/README.md index 2b79db03..7b7dc758 100644 --- a/setup1-cli-tools/README.md +++ b/setup1-cli-tools/README.md @@ -27,3 +27,17 @@ view-key > view_key.txt # To produce a public key out of a private key: public-key-extractor --path keys.json ``` + +## Encrypt and Decrypt Guide + +Encrypt text: + +```bash +cargo run --bin encrypt_text -- --address aleo1ekhu99p7m6e8slh5tpjay28vv3jklmxhtrmphg43t7hvva7hyqgqtkq846 --plaintext "foo" +``` + +Decrypt text (using `keys.json`): + +```bash +cargo run --bin decrypt_text -- --path ./keys.json --ciphertext d62da3d3e9732ef58d98a7a1c987aa7fff8046451f4cdcf552b659171d7120057bf9c5485fcd77c5e3f6c56ed2b6d6df487e327c4de04b5ffe7e6d98761ac00a +``` diff --git a/setup1-cli-tools/src/decrypt_text.rs b/setup1-cli-tools/src/decrypt_text.rs new file mode 100644 index 00000000..c0d1e422 --- /dev/null +++ b/setup1-cli-tools/src/decrypt_text.rs @@ -0,0 +1,80 @@ +use hex::ToHex; +use snarkvm_algorithms::EncryptionScheme; +use snarkvm_dpc::{parameters::testnet2::Testnet2Parameters, Parameters, PrivateKey}; + +use age::Decryptor; +use anyhow::{anyhow, Result}; +use core::str; +use secrecy::{ExposeSecret, SecretString, SecretVec}; +use serde::Deserialize; +use std::{fs, io::Read, str::FromStr}; +use structopt::StructOpt; +use unic_langid::LanguageIdentifier; + +#[derive(Debug, StructOpt)] +#[structopt(name = "Decrypt Text")] +struct Options { + #[structopt(long)] + path: String, + #[structopt(short, long)] + ciphertext: String, +} + +// Should be the same as the one from setup1-contributor/src/objects.rs +// Copied here to reduce the compile time, which is +// about 50% longer with setup1-contributor included +#[derive(Deserialize, Debug, Clone)] +#[serde(rename_all = "camelCase")] +pub struct AleoSetupKeys { + pub encrypted_seed: String, + pub encrypted_private_key: String, +} + +fn decrypt(passphrase: &SecretString, encrypted: &str) -> Result> { + let decoded = SecretVec::new(hex::decode(encrypted)?); + let decryptor = Decryptor::new(decoded.expose_secret().as_slice())?; + match decryptor { + Decryptor::Passphrase(decryptor) => { + let mut output = vec![]; + let mut reader = decryptor.decrypt(passphrase, None)?; + reader.read_to_end(&mut output)?; + Ok(SecretVec::new(output)) + } + Decryptor::Recipients(_) => Err(anyhow!("Wrong age Decryptor, should be Passphrase, but got Recipients")), + } +} + +fn read_private_key(keys_path: &str) -> Result> { + let file_contents = fs::read(keys_path)?; + let keys: AleoSetupKeys = serde_json::from_slice(&file_contents)?; + let passphrase = age::cli_common::read_secret("Enter your Aleo setup passphrase", "Passphrase", None) + .map_err(|e| anyhow!("Error reading passphrase: {}", e))?; + let decrypted = decrypt(&passphrase, &keys.encrypted_private_key)?; + PrivateKey::from_str(std::str::from_utf8(decrypted.expose_secret())?).map_err(Into::into) +} + +fn main() { + let options = Options::from_args(); + let ciphertext = hex::decode(options.ciphertext).expect("Should decode the ciphertext"); + + let default_language: LanguageIdentifier = "en-US".parse().expect("Should parse a language indentifier"); + age::localizer() + .select(&[default_language]) + .expect("Should select the default language"); + + let scheme = Testnet2Parameters::account_encryption_scheme(); + + let private_key = read_private_key(&options.path).expect("Should read a private key"); + let privkey = private_key + .to_decryption_key() + .expect("Should convert to decryption key"); + + let plaintext = scheme.decrypt(&privkey, &ciphertext).expect("Should decrypt the text"); + + println!( + "{}", + str::from_utf8(&plaintext) + .map(|s| s.to_owned()) + .unwrap_or_else(|_| plaintext.encode_hex::()) + ); +} diff --git a/setup1-cli-tools/src/encrypt_text.rs b/setup1-cli-tools/src/encrypt_text.rs new file mode 100644 index 00000000..9fe4c206 --- /dev/null +++ b/setup1-cli-tools/src/encrypt_text.rs @@ -0,0 +1,29 @@ +use hex::ToHex; +use snarkvm_algorithms::EncryptionScheme; +use snarkvm_dpc::{parameters::testnet2::Testnet2Parameters, Address, Parameters}; + +use structopt::StructOpt; + +#[derive(Debug, StructOpt)] +#[structopt(name = "Encrypt Text")] +struct Options { + #[structopt(short, long)] + address: Address, + #[structopt(short, long)] + plaintext: String, +} + +fn main() { + let options = Options::from_args(); + + let scheme = Testnet2Parameters::account_encryption_scheme(); + let pubkey = options.address.to_encryption_key(); + let randomness = scheme + .generate_randomness(pubkey, &mut rand::thread_rng()) + .expect("Unable to generate randomness"); + let ciphertext = scheme + .encrypt(pubkey, &randomness, options.plaintext.as_bytes()) + .expect("Unable to encrypt the text"); + + println!("{}", ciphertext.encode_hex::()); +}