Skip to content

Commit

Permalink
implement decryption
Browse files Browse the repository at this point in the history
  • Loading branch information
dr-orlovsky committed May 13, 2024
1 parent ea23689 commit f165727
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 30 deletions.
71 changes: 57 additions & 14 deletions src/encrypt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,15 @@ use std::str::FromStr;
use aes::cipher::{BlockDecrypt, BlockEncrypt, KeyInit};
use aes::{Aes256, Block};
use amplify::confinement::{Confined, SmallOrdMap, U64 as U64MAX};
use amplify::Bytes32;
use amplify::{Bytes32, Wrapper};
use armor::{ArmorHeader, ArmorParseError, AsciiArmor};
use ec25519::{edwards25519, Error};
use ec25519::edwards25519;
use rand::random;
use sha2::digest::generic_array::GenericArray;
use sha2::{Digest, Sha256};
use strict_encoding::{StrictDeserialize, StrictSerialize};

use crate::{Algo, InvalidPubkey, SsiPub, LIB_NAME_SSI};
use crate::{Algo, InvalidPubkey, SsiPair, SsiPub, LIB_NAME_SSI};

#[derive(Copy, Clone, Debug, Display, Error)]
pub enum EncryptionError {
Expand All @@ -42,10 +42,22 @@ pub enum EncryptionError {
InvalidPubkey(SsiPub),
}

#[derive(Copy, Clone, Debug, Display, Error)]
pub enum DecryptionError {
#[display("the message can't be decrypted using key {0}")]
KeyMismatch(SsiPub),
#[display("invalid public key {0}")]
InvalidPubkey(SsiPub),
}

#[derive(Clone, Debug, From)]
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_SSI)]
pub struct SymmetricKey(Bytes32);
pub struct SymmetricKey(
#[from]
#[from([u8; 32])]
Bytes32,
);

impl AsRef<[u8]> for SymmetricKey {
fn as_ref(&self) -> &[u8] { self.0.as_ref() }
Expand Down Expand Up @@ -107,7 +119,7 @@ impl Encrypted {
for pk in receivers {
keys.insert(
pk,
pk.encrypt(&key)
pk.encrypt_key(&key)
.map_err(|_| EncryptionError::InvalidPubkey(pk))?,
);
}
Expand All @@ -117,28 +129,59 @@ impl Encrypted {
data: Confined::from_collection_unsafe(msg),
})
}

pub fn decrypt(&self, pair: SsiPair) -> Result<Vec<u8>, DecryptionError> {
let key = self
.keys
.iter()
.find(|(pk, _)| *pk == &pair.pk)
.map(|(_, secret)| secret)
.ok_or(DecryptionError::KeyMismatch(pair.pk))?
.copy();
let key = pair
.decrypt_key(key)
.map_err(|_| DecryptionError::InvalidPubkey(pair.pk))?;
Ok(decrypt(self.data.to_inner(), key))
}
}

impl SsiPub {
pub fn encrypt(&self, key: &SymmetricKey) -> Result<Bytes32, InvalidPubkey> {
pub fn encrypt_key(&self, key: &SymmetricKey) -> Result<Bytes32, InvalidPubkey> {
match self.algo() {
Algo::Ed25519 => self.encrypt_ed25519(key),
Algo::Ed25519 => self.encrypt_key_ed25519(key),
Algo::Bip340 | Algo::Other(_) => Err(InvalidPubkey),
}
}

pub fn encrypt_ed25519(&self, key: &SymmetricKey) -> Result<Bytes32, InvalidPubkey> {
let pk = ec25519::PublicKey::try_from(*self)?;
let ge = edwards25519::GeP3::from_bytes_vartime(&pk).ok_or(InvalidPubkey)?;
pub fn encrypt_key_ed25519(&self, key: &SymmetricKey) -> Result<Bytes32, InvalidPubkey> {
let ge =
edwards25519::GeP3::from_bytes_vartime(&self.to_byte_array()).ok_or(InvalidPubkey)?;

Ok(edwards25519::ge_scalarmult(key.as_ref(), &ge)
.to_bytes()
.into())
}
}

impl SsiPair {
pub fn decrypt_key(&self, key: Bytes32) -> Result<SymmetricKey, InvalidPubkey> {
match self.pk.algo() {
Algo::Ed25519 => self.decrypt_key_ed25519(key),
Algo::Bip340 | Algo::Other(_) => Err(InvalidPubkey),
}
}

pub fn decrypt_key_ed25519(&self, key: Bytes32) -> Result<SymmetricKey, InvalidPubkey> {
let ge = edwards25519::GeP3::from_bytes_negate_vartime(&self.pk.to_byte_array())
.ok_or(InvalidPubkey)?;
Ok(edwards25519::ge_scalarmult(key.as_ref(), &ge)
.to_bytes()
.into())
}
}

pub fn encrypt(mut source: Vec<u8>, passwd: impl AsRef<[u8]>) -> Vec<u8> {
let key = Sha256::digest(passwd.as_ref());
pub fn encrypt(mut source: Vec<u8>, key: impl AsRef<[u8]>) -> Vec<u8> {
let key = Sha256::digest(key.as_ref());
let key = GenericArray::from_slice(key.as_slice());
let cipher = Aes256::new(key);

Expand All @@ -149,8 +192,8 @@ pub fn encrypt(mut source: Vec<u8>, passwd: impl AsRef<[u8]>) -> Vec<u8> {
source
}

pub fn decrypt(mut source: Vec<u8>, passwd: impl AsRef<[u8]>) -> Vec<u8> {
let key = Sha256::digest(passwd.as_ref());
pub fn decrypt(mut source: Vec<u8>, key: impl AsRef<[u8]>) -> Vec<u8> {
let key = Sha256::digest(key.as_ref());
let key = GenericArray::from_slice(key.as_slice());
let cipher = Aes256::new(key);

Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ mod runtime;

pub use bip340::Bip340Secret;
pub use ed25519::Ed25519Secret;
pub use encrypt::{decrypt, encrypt, Encrypted, EncryptionError, SymmetricKey};
pub use encrypt::{decrypt, encrypt, DecryptionError, Encrypted, EncryptionError, SymmetricKey};
pub use identity::{Ssi, SsiParseError, Uid};
pub use public::{
Algo, CertParseError, Chain, Fingerprint, InvalidPubkey, InvalidSig, SsiCert, SsiPub, SsiQuery,
Expand Down
50 changes: 39 additions & 11 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.

#[macro_use]
extern crate amplify;
#[macro_use]
extern crate clap;

Expand All @@ -29,12 +27,10 @@ use std::io::{stdin, Read};
use std::path::PathBuf;
use std::str::FromStr;

use armor::AsciiArmor;
use chrono::{DateTime, Utc};
use clap::Parser;
use rand::random;
use ssi::{
encrypt, Algo, Chain, Encrypted, InvalidSig, Ssi, SsiCert, SsiQuery, SsiRuntime, SsiSecret, Uid,
};
use ssi::{Algo, Chain, Encrypted, InvalidSig, Ssi, SsiCert, SsiQuery, SsiRuntime, SsiSecret, Uid};

#[derive(Parser, Clone, Debug)]
pub struct Args {
Expand Down Expand Up @@ -107,7 +103,8 @@ pub enum Command {
/// Signature certificate to verify
signature: SsiCert,
},

// Recover,
/// Encrypt a message for receiver(s)
Encrypt {
/// Identities which must be able to decrypt
#[clap(short, long, required = true)]
Expand All @@ -121,7 +118,21 @@ pub enum Command {
#[clap(short, long)]
file: Option<PathBuf>,
},
// Recover,

/// Decrypt a message using one of your private keys
Decrypt {
/// Private key to use for decryption
#[clap(short, long)]
key: Option<SsiQuery>,

/// Text message to decrypt
#[clap(short, long, conflicts_with = "file")]
text: Option<String>,

/// File to decrypt
#[clap(short, long)]
file: Option<PathBuf>,
},
}

fn main() {
Expand Down Expand Up @@ -191,7 +202,7 @@ fn main() {
println!("{ssi}");

if !passwd.is_empty() {
secret.encrypt(passwd);
secret.conceal(passwd);
}

runtime.secrets.insert(secret);
Expand All @@ -208,7 +219,7 @@ fn main() {
} => {
eprintln!("Signing with {ssi} ...");

let passwd = rpassword::prompt_password("Password for private key encryption: ")
let passwd = rpassword::prompt_password("Password for the private key: ")
.expect("unable to read password");
let msg = get_message(text, file);
let signer = runtime
Expand All @@ -235,7 +246,7 @@ fn main() {
Err(err) => eprintln!("invalid: {err}"),
}
println!();
} /* */
}
//Command::Recover => {
//use std::collections::HashSet;
//let passwd = rpassword::prompt_password("Password for private key encryption: ")
Expand Down Expand Up @@ -264,6 +275,23 @@ fn main() {
let encrypted = Encrypted::encrypt(msg, receivers).expect("unable to encrypt");
println!("{encrypted}");
}
Command::Decrypt { key, text, file } => {
let key = key.unwrap_or_default();
eprintln!("Decrypting with {key} ...");

let passwd = rpassword::prompt_password("Password for the private key: ")
.expect("unable to read password");
let pair = runtime
.find_signer(key, &passwd)
.expect("unknown private key");
eprintln!("Using key {pair}");

let s = String::from_utf8(get_message(text, file))
.expect("the provided message is not ASCII armored");
let encrypted = Encrypted::from_ascii_armored_str(&s).expect("invalid ASCII armor");
let msg = encrypted.decrypt(pair).expect("can't decrypt the message");
println!("{}", String::from_utf8_lossy(&msg));
}
}
}

Expand Down
5 changes: 4 additions & 1 deletion src/public.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,9 +309,12 @@ pub enum InvalidSig {
UnsupportedAlgo(u8),
}

#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Display, From)]
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Default, Debug, Display, From)]
#[display(inner)]
pub enum SsiQuery {
#[default]
#[display("default key")]
Default,
#[from]
Pub(SsiPub),
#[from]
Expand Down
4 changes: 3 additions & 1 deletion src/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ impl SsiRuntime {
pub fn find_identity(&self, query: impl Into<SsiQuery>) -> Option<&Ssi> {
let query = query.into();
self.identities.iter().find(|ssi| match query {
// TODO: Support custom default keys
SsiQuery::Default => true,
SsiQuery::Pub(pk) => ssi.pk == pk,
SsiQuery::Fp(fp) => ssi.pk.fingerprint() == fp,
SsiQuery::Id(ref id) => ssi.uids.iter().any(|uid| {
Expand All @@ -137,7 +139,7 @@ impl SsiRuntime {
let sk = self.secrets.iter().find_map(|s| {
let mut s = (*s).clone();
if !passwd.is_empty() {
s.decrypt(passwd);
s.reveal(passwd);
}
if s.to_public() == ssi.pk {
Some(s)
Expand Down
4 changes: 2 additions & 2 deletions src/secret.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,11 +142,11 @@ impl SsiSecret {
}
}

pub fn encrypt(&mut self, passwd: impl AsRef<str>) {
pub fn conceal(&mut self, passwd: impl AsRef<str>) {
self.replace(&encrypt(self.to_vec(), passwd.as_ref()));
}

pub fn decrypt(&mut self, passwd: impl AsRef<str>) {
pub fn reveal(&mut self, passwd: impl AsRef<str>) {
self.replace(&decrypt(self.to_vec(), passwd.as_ref()));
}

Expand Down

0 comments on commit f165727

Please sign in to comment.