Skip to content

Commit f165727

Browse files
committed
implement decryption
1 parent ea23689 commit f165727

File tree

6 files changed

+106
-30
lines changed

6 files changed

+106
-30
lines changed

src/encrypt.rs

+57-14
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,15 @@ use std::str::FromStr;
2424
use aes::cipher::{BlockDecrypt, BlockEncrypt, KeyInit};
2525
use aes::{Aes256, Block};
2626
use amplify::confinement::{Confined, SmallOrdMap, U64 as U64MAX};
27-
use amplify::Bytes32;
27+
use amplify::{Bytes32, Wrapper};
2828
use armor::{ArmorHeader, ArmorParseError, AsciiArmor};
29-
use ec25519::{edwards25519, Error};
29+
use ec25519::edwards25519;
3030
use rand::random;
3131
use sha2::digest::generic_array::GenericArray;
3232
use sha2::{Digest, Sha256};
3333
use strict_encoding::{StrictDeserialize, StrictSerialize};
3434

35-
use crate::{Algo, InvalidPubkey, SsiPub, LIB_NAME_SSI};
35+
use crate::{Algo, InvalidPubkey, SsiPair, SsiPub, LIB_NAME_SSI};
3636

3737
#[derive(Copy, Clone, Debug, Display, Error)]
3838
pub enum EncryptionError {
@@ -42,10 +42,22 @@ pub enum EncryptionError {
4242
InvalidPubkey(SsiPub),
4343
}
4444

45+
#[derive(Copy, Clone, Debug, Display, Error)]
46+
pub enum DecryptionError {
47+
#[display("the message can't be decrypted using key {0}")]
48+
KeyMismatch(SsiPub),
49+
#[display("invalid public key {0}")]
50+
InvalidPubkey(SsiPub),
51+
}
52+
4553
#[derive(Clone, Debug, From)]
4654
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
4755
#[strict_type(lib = LIB_NAME_SSI)]
48-
pub struct SymmetricKey(Bytes32);
56+
pub struct SymmetricKey(
57+
#[from]
58+
#[from([u8; 32])]
59+
Bytes32,
60+
);
4961

5062
impl AsRef<[u8]> for SymmetricKey {
5163
fn as_ref(&self) -> &[u8] { self.0.as_ref() }
@@ -107,7 +119,7 @@ impl Encrypted {
107119
for pk in receivers {
108120
keys.insert(
109121
pk,
110-
pk.encrypt(&key)
122+
pk.encrypt_key(&key)
111123
.map_err(|_| EncryptionError::InvalidPubkey(pk))?,
112124
);
113125
}
@@ -117,28 +129,59 @@ impl Encrypted {
117129
data: Confined::from_collection_unsafe(msg),
118130
})
119131
}
132+
133+
pub fn decrypt(&self, pair: SsiPair) -> Result<Vec<u8>, DecryptionError> {
134+
let key = self
135+
.keys
136+
.iter()
137+
.find(|(pk, _)| *pk == &pair.pk)
138+
.map(|(_, secret)| secret)
139+
.ok_or(DecryptionError::KeyMismatch(pair.pk))?
140+
.copy();
141+
let key = pair
142+
.decrypt_key(key)
143+
.map_err(|_| DecryptionError::InvalidPubkey(pair.pk))?;
144+
Ok(decrypt(self.data.to_inner(), key))
145+
}
120146
}
121147

122148
impl SsiPub {
123-
pub fn encrypt(&self, key: &SymmetricKey) -> Result<Bytes32, InvalidPubkey> {
149+
pub fn encrypt_key(&self, key: &SymmetricKey) -> Result<Bytes32, InvalidPubkey> {
124150
match self.algo() {
125-
Algo::Ed25519 => self.encrypt_ed25519(key),
151+
Algo::Ed25519 => self.encrypt_key_ed25519(key),
126152
Algo::Bip340 | Algo::Other(_) => Err(InvalidPubkey),
127153
}
128154
}
129155

130-
pub fn encrypt_ed25519(&self, key: &SymmetricKey) -> Result<Bytes32, InvalidPubkey> {
131-
let pk = ec25519::PublicKey::try_from(*self)?;
132-
let ge = edwards25519::GeP3::from_bytes_vartime(&pk).ok_or(InvalidPubkey)?;
156+
pub fn encrypt_key_ed25519(&self, key: &SymmetricKey) -> Result<Bytes32, InvalidPubkey> {
157+
let ge =
158+
edwards25519::GeP3::from_bytes_vartime(&self.to_byte_array()).ok_or(InvalidPubkey)?;
159+
160+
Ok(edwards25519::ge_scalarmult(key.as_ref(), &ge)
161+
.to_bytes()
162+
.into())
163+
}
164+
}
165+
166+
impl SsiPair {
167+
pub fn decrypt_key(&self, key: Bytes32) -> Result<SymmetricKey, InvalidPubkey> {
168+
match self.pk.algo() {
169+
Algo::Ed25519 => self.decrypt_key_ed25519(key),
170+
Algo::Bip340 | Algo::Other(_) => Err(InvalidPubkey),
171+
}
172+
}
133173

174+
pub fn decrypt_key_ed25519(&self, key: Bytes32) -> Result<SymmetricKey, InvalidPubkey> {
175+
let ge = edwards25519::GeP3::from_bytes_negate_vartime(&self.pk.to_byte_array())
176+
.ok_or(InvalidPubkey)?;
134177
Ok(edwards25519::ge_scalarmult(key.as_ref(), &ge)
135178
.to_bytes()
136179
.into())
137180
}
138181
}
139182

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

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

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

src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ mod runtime;
3535

3636
pub use bip340::Bip340Secret;
3737
pub use ed25519::Ed25519Secret;
38-
pub use encrypt::{decrypt, encrypt, Encrypted, EncryptionError, SymmetricKey};
38+
pub use encrypt::{decrypt, encrypt, DecryptionError, Encrypted, EncryptionError, SymmetricKey};
3939
pub use identity::{Ssi, SsiParseError, Uid};
4040
pub use public::{
4141
Algo, CertParseError, Chain, Fingerprint, InvalidPubkey, InvalidSig, SsiCert, SsiPub, SsiQuery,

src/main.rs

+39-11
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@
1919
// See the License for the specific language governing permissions and
2020
// limitations under the License.
2121

22-
#[macro_use]
23-
extern crate amplify;
2422
#[macro_use]
2523
extern crate clap;
2624

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

30+
use armor::AsciiArmor;
3231
use chrono::{DateTime, Utc};
3332
use clap::Parser;
34-
use rand::random;
35-
use ssi::{
36-
encrypt, Algo, Chain, Encrypted, InvalidSig, Ssi, SsiCert, SsiQuery, SsiRuntime, SsiSecret, Uid,
37-
};
33+
use ssi::{Algo, Chain, Encrypted, InvalidSig, Ssi, SsiCert, SsiQuery, SsiRuntime, SsiSecret, Uid};
3834

3935
#[derive(Parser, Clone, Debug)]
4036
pub struct Args {
@@ -107,7 +103,8 @@ pub enum Command {
107103
/// Signature certificate to verify
108104
signature: SsiCert,
109105
},
110-
106+
// Recover,
107+
/// Encrypt a message for receiver(s)
111108
Encrypt {
112109
/// Identities which must be able to decrypt
113110
#[clap(short, long, required = true)]
@@ -121,7 +118,21 @@ pub enum Command {
121118
#[clap(short, long)]
122119
file: Option<PathBuf>,
123120
},
124-
// Recover,
121+
122+
/// Decrypt a message using one of your private keys
123+
Decrypt {
124+
/// Private key to use for decryption
125+
#[clap(short, long)]
126+
key: Option<SsiQuery>,
127+
128+
/// Text message to decrypt
129+
#[clap(short, long, conflicts_with = "file")]
130+
text: Option<String>,
131+
132+
/// File to decrypt
133+
#[clap(short, long)]
134+
file: Option<PathBuf>,
135+
},
125136
}
126137

127138
fn main() {
@@ -191,7 +202,7 @@ fn main() {
191202
println!("{ssi}");
192203

193204
if !passwd.is_empty() {
194-
secret.encrypt(passwd);
205+
secret.conceal(passwd);
195206
}
196207

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

211-
let passwd = rpassword::prompt_password("Password for private key encryption: ")
222+
let passwd = rpassword::prompt_password("Password for the private key: ")
212223
.expect("unable to read password");
213224
let msg = get_message(text, file);
214225
let signer = runtime
@@ -235,7 +246,7 @@ fn main() {
235246
Err(err) => eprintln!("invalid: {err}"),
236247
}
237248
println!();
238-
} /* */
249+
}
239250
//Command::Recover => {
240251
//use std::collections::HashSet;
241252
//let passwd = rpassword::prompt_password("Password for private key encryption: ")
@@ -264,6 +275,23 @@ fn main() {
264275
let encrypted = Encrypted::encrypt(msg, receivers).expect("unable to encrypt");
265276
println!("{encrypted}");
266277
}
278+
Command::Decrypt { key, text, file } => {
279+
let key = key.unwrap_or_default();
280+
eprintln!("Decrypting with {key} ...");
281+
282+
let passwd = rpassword::prompt_password("Password for the private key: ")
283+
.expect("unable to read password");
284+
let pair = runtime
285+
.find_signer(key, &passwd)
286+
.expect("unknown private key");
287+
eprintln!("Using key {pair}");
288+
289+
let s = String::from_utf8(get_message(text, file))
290+
.expect("the provided message is not ASCII armored");
291+
let encrypted = Encrypted::from_ascii_armored_str(&s).expect("invalid ASCII armor");
292+
let msg = encrypted.decrypt(pair).expect("can't decrypt the message");
293+
println!("{}", String::from_utf8_lossy(&msg));
294+
}
267295
}
268296
}
269297

src/public.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -309,9 +309,12 @@ pub enum InvalidSig {
309309
UnsupportedAlgo(u8),
310310
}
311311

312-
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Display, From)]
312+
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Default, Debug, Display, From)]
313313
#[display(inner)]
314314
pub enum SsiQuery {
315+
#[default]
316+
#[display("default key")]
317+
Default,
315318
#[from]
316319
Pub(SsiPub),
317320
#[from]

src/runtime.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,8 @@ impl SsiRuntime {
121121
pub fn find_identity(&self, query: impl Into<SsiQuery>) -> Option<&Ssi> {
122122
let query = query.into();
123123
self.identities.iter().find(|ssi| match query {
124+
// TODO: Support custom default keys
125+
SsiQuery::Default => true,
124126
SsiQuery::Pub(pk) => ssi.pk == pk,
125127
SsiQuery::Fp(fp) => ssi.pk.fingerprint() == fp,
126128
SsiQuery::Id(ref id) => ssi.uids.iter().any(|uid| {
@@ -137,7 +139,7 @@ impl SsiRuntime {
137139
let sk = self.secrets.iter().find_map(|s| {
138140
let mut s = (*s).clone();
139141
if !passwd.is_empty() {
140-
s.decrypt(passwd);
142+
s.reveal(passwd);
141143
}
142144
if s.to_public() == ssi.pk {
143145
Some(s)

src/secret.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -142,11 +142,11 @@ impl SsiSecret {
142142
}
143143
}
144144

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

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

0 commit comments

Comments
 (0)