diff --git a/Cargo.lock b/Cargo.lock index fb78b6a..b8c34e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21,6 +21,7 @@ checksum = "9e711289a6cb28171b4f0e6c8019c69ff9476050508dc082167575d458ff74d0" dependencies = [ "amplify_derive", "amplify_num", + "amplify_syn", "ascii", "wasm-bindgen", ] @@ -126,6 +127,19 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" +[[package]] +name = "ascii-armor" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673486110323d50d9f33d99f0f526726b4721b1b596284351e75d3692225abe8" +dependencies = [ + "amplify", + "baid64", + "base85", + "sha2", + "strict_encoding", +] + [[package]] name = "autocfg" version = "1.2.0" @@ -150,6 +164,15 @@ version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51" +[[package]] +name = "base85" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36915bbaca237c626689b5bd14d02f2ba7a5a359d30a2a08be697392e3718079" +dependencies = [ + "thiserror", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -241,7 +264,7 @@ version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "quote", "syn 2.0.60", @@ -376,6 +399,12 @@ dependencies = [ "wasi", ] +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "heck" version = "0.5.0" @@ -570,6 +599,7 @@ version = "0.2.0" dependencies = [ "aes", "amplify", + "ascii-armor", "baid64", "chrono", "clap", @@ -582,6 +612,7 @@ dependencies = [ "secp256k1", "sha2", "shellexpand", + "strict_encoding", ] [[package]] @@ -623,6 +654,29 @@ dependencies = [ "dirs", ] +[[package]] +name = "strict_encoding" +version = "2.7.0-beta.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c463f8ea993e323740d78544473e791adb91ac659f5bf2c1a59db64a34f99fc" +dependencies = [ + "amplify", + "strict_encoding_derive", +] + +[[package]] +name = "strict_encoding_derive" +version = "2.7.0-beta.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "475fa6f1fdde6e0555422b5111ad34bde30a1459af3599f920c3af9829772c0e" +dependencies = [ + "amplify_syn", + "heck 0.4.1", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "strsim" version = "0.11.1" diff --git a/Cargo.toml b/Cargo.toml index 88bcf92..a651947 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,8 @@ required-features = ["cli"] [dependencies] amplify = "4.6.0" +strict_encoding = "2.7.0-beta.3" +ascii-armor = "0.6.0" baid64 = "0.1.0" secp256k1 = { version = "0.29.0", features = ["rand", "global-context", "rand-std"] } ec25519 = "0.1.0" diff --git a/src/bip340.rs b/src/bip340.rs index c9cd77c..064f5dd 100644 --- a/src/bip340.rs +++ b/src/bip340.rs @@ -100,13 +100,13 @@ impl Bip340Secret { let msg = Message::from_digest(msg); let keypair = Keypair::from_secret_key(SECP256K1, &self.0); let sig = SECP256K1.sign_schnorr(&msg, &keypair); - SsiSig(sig.serialize()) + SsiSig::from(sig.serialize()) } } impl SsiPub { pub fn verify_bip360(self, msg: [u8; 32], sig: SsiSig) -> Result<(), InvalidSig> { - let sig = Signature::from_slice(&sig.0).map_err(|_| InvalidSig::InvalidData)?; + let sig = Signature::from_slice(sig.as_slice()).map_err(|_| InvalidSig::InvalidData)?; let msg = Message::from_digest(msg); let pk = XOnlyPublicKey::try_from(self)?; sig.verify(&msg, &pk).map_err(|_| InvalidSig::InvalidSig) diff --git a/src/ed25519.rs b/src/ed25519.rs index 4809965..d3d0611 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -99,13 +99,13 @@ impl Ed25519Secret { pub fn sign(&self, msg: [u8; 32]) -> SsiSig { let sig = self.0.sign(msg, None); - SsiSig(*sig) + SsiSig::from(*sig) } } impl SsiPub { pub fn verify_ed25519(self, msg: [u8; 32], sig: SsiSig) -> Result<(), InvalidSig> { - let sig = Signature::from_slice(&sig.0).map_err(|_| InvalidSig::InvalidData)?; + let sig = Signature::from_slice(sig.as_slice()).map_err(|_| InvalidSig::InvalidData)?; let pk = PublicKey::try_from(self)?; pk.verify(msg, &sig).map_err(|err| { eprintln!("{err}"); diff --git a/src/encrypt.rs b/src/encrypt.rs new file mode 100644 index 0000000..17ca745 --- /dev/null +++ b/src/encrypt.rs @@ -0,0 +1,162 @@ +// Self-sovereign identity +// +// SPDX-License-Identifier: Apache-2.0 +// +// Written in 2024 by +// Dr Maxim Orlovsky +// +// Copyright (C) 2024 LNP/BP Standards Association. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +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 armor::{ArmorHeader, ArmorParseError, AsciiArmor}; +use ec25519::{edwards25519, Error}; +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}; + +#[derive(Copy, Clone, Debug, Display, Error)] +pub enum EncryptionError { + #[display("the number of receivers exceeds 2^16")] + TooManyReceivers, + #[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); + +impl AsRef<[u8]> for SymmetricKey { + fn as_ref(&self) -> &[u8] { self.0.as_ref() } +} + +impl SymmetricKey { + pub fn new() -> Self { + let key = random::<[u8; 32]>(); + Self(Bytes32::from_byte_array(key)) + } +} + +#[derive(Clone, Debug, Display)] +#[display(AsciiArmor::to_ascii_armored_string)] +#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_SSI)] +pub struct Encrypted { + pub keys: SmallOrdMap, + pub data: Confined, 0, U64MAX>, +} + +impl StrictSerialize for Encrypted {} +impl StrictDeserialize for Encrypted {} + +impl AsciiArmor for Encrypted { + type Err = ArmorParseError; + const PLATE_TITLE: &'static str = "SSI MESSAGE"; + + fn ascii_armored_headers(&self) -> Vec { + vec![ArmorHeader::with("Receivers", self.keys.keys().map(|pk| pk.to_string()))] + } + + fn to_ascii_armored_data(&self) -> Vec { + self.to_strict_serialized::() + .expect("64 bits will not error") + .into_inner() + } + + fn with_headers_data(_headers: Vec, data: Vec) -> Result { + // TODO: Check receivers list + Ok(Self::from_strict_serialized::(Confined::from_collection_unsafe(data)) + .expect("64 bits will never fail")) + } +} + +impl FromStr for Encrypted { + type Err = ArmorParseError; + + fn from_str(s: &str) -> Result { Self::from_ascii_armored_str(s) } +} + +impl Encrypted { + pub fn encrypt( + source: Vec, + receivers: impl IntoIterator, + ) -> Result { + let key = SymmetricKey::new(); + let mut keys = bmap![]; + for pk in receivers { + keys.insert( + pk, + pk.encrypt(&key) + .map_err(|_| EncryptionError::InvalidPubkey(pk))?, + ); + } + let msg = encrypt(source, key); + Ok(Self { + keys: Confined::try_from(keys).map_err(|_| EncryptionError::TooManyReceivers)?, + data: Confined::from_collection_unsafe(msg), + }) + } +} + +impl SsiPub { + pub fn encrypt(&self, key: &SymmetricKey) -> Result { + match self.algo() { + Algo::Ed25519 => self.encrypt_ed25519(key), + Algo::Bip340 | Algo::Other(_) => Err(InvalidPubkey), + } + } + + pub fn encrypt_ed25519(&self, key: &SymmetricKey) -> Result { + let pk = ec25519::PublicKey::try_from(*self)?; + let ge = edwards25519::GeP3::from_bytes_vartime(&pk).ok_or(InvalidPubkey)?; + + Ok(edwards25519::ge_scalarmult(key.as_ref(), &ge) + .to_bytes() + .into()) + } +} + +pub fn encrypt(mut source: Vec, passwd: impl AsRef<[u8]>) -> Vec { + let key = Sha256::digest(passwd.as_ref()); + let key = GenericArray::from_slice(key.as_slice()); + let cipher = Aes256::new(key); + + for chunk in source.chunks_mut(16) { + let block = Block::from_mut_slice(chunk); + cipher.encrypt_block(block); + } + source +} + +pub fn decrypt(mut source: Vec, passwd: impl AsRef<[u8]>) -> Vec { + let key = Sha256::digest(passwd.as_ref()); + let key = GenericArray::from_slice(key.as_slice()); + let cipher = Aes256::new(key); + + for chunk in source.chunks_mut(16) { + let block = Block::from_mut_slice(chunk); + cipher.decrypt_block(block); + } + source +} diff --git a/src/identity.rs b/src/identity.rs index 6ca769e..61b788a 100644 --- a/src/identity.rs +++ b/src/identity.rs @@ -78,7 +78,7 @@ impl FromStr for Uid { fn from_str(s: &str) -> Result { Self::parse_str(&s.replace(['<', '>'], "")) } } -#[derive(Clone, Eq, PartialEq, Hash, Debug)] +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] pub struct Ssi { pub pk: SsiPub, pub uids: BTreeSet, @@ -92,7 +92,7 @@ impl Ssi { pk: secret.to_public(), uids, expiry, - sig: SsiSig([0u8; 64]), + sig: SsiSig::from([0u8; 64]), }; me.sig = secret.sign(me.to_message()); me diff --git a/src/lib.rs b/src/lib.rs index 8942808..85a91aa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,7 +21,10 @@ #[macro_use] extern crate amplify; +#[macro_use] +extern crate strict_encoding; +mod encrypt; mod identity; mod secret; mod public; @@ -32,6 +35,7 @@ mod runtime; pub use bip340::Bip340Secret; pub use ed25519::Ed25519Secret; +pub use encrypt::{decrypt, encrypt, Encrypted, EncryptionError, SymmetricKey}; pub use identity::{Ssi, SsiParseError, Uid}; pub use public::{ Algo, CertParseError, Chain, Fingerprint, InvalidPubkey, InvalidSig, SsiCert, SsiPub, SsiQuery, @@ -39,3 +43,5 @@ pub use public::{ }; pub use runtime::{Error, SsiRuntime, SSI_DIR}; pub use secret::{SecretParseError, SsiPair, SsiSecret}; + +pub const LIB_NAME_SSI: &str = "SSI"; diff --git a/src/main.rs b/src/main.rs index 24565e8..365bea8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,6 +19,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#[macro_use] +extern crate amplify; #[macro_use] extern crate clap; @@ -29,7 +31,10 @@ use std::str::FromStr; use chrono::{DateTime, Utc}; use clap::Parser; -use ssi::{Algo, Chain, InvalidSig, Ssi, SsiCert, SsiQuery, SsiRuntime, SsiSecret, Uid}; +use rand::random; +use ssi::{ + encrypt, Algo, Chain, Encrypted, InvalidSig, Ssi, SsiCert, SsiQuery, SsiRuntime, SsiSecret, Uid, +}; #[derive(Parser, Clone, Debug)] pub struct Args { @@ -102,6 +107,20 @@ pub enum Command { /// Signature certificate to verify signature: SsiCert, }, + + Encrypt { + /// Identities which must be able to decrypt + #[clap(short, long, required = true)] + receiver: Vec, + + /// Text message to encrypt + #[clap(short, long, conflicts_with = "file")] + text: Option, + + /// File to encrypt + #[clap(short, long)] + file: Option, + }, // Recover, } @@ -191,18 +210,7 @@ fn main() { let passwd = rpassword::prompt_password("Password for private key encryption: ") .expect("unable to read password"); - let msg = match (text, file) { - (Some(t), None) => t.into_bytes(), - (None, Some(f)) => fs::read(f).expect("unable to read the file"), - (None, None) => { - let mut s = String::new(); - stdin() - .read_to_string(&mut s) - .expect("unable to read standard input"); - s.into_bytes() - } - _ => unreachable!(), - }; + let msg = get_message(text, file); let signer = runtime .find_signer(ssi, &passwd) .expect("unknown signing identity"); @@ -227,20 +235,49 @@ fn main() { Err(err) => eprintln!("invalid: {err}"), } println!(); - } /* - Command::Recover => { - use std::collections::HashSet; - let passwd = rpassword::prompt_password("Password for private key encryption: ") - .expect("unable to read password"); - let mut identities = HashSet::new(); - for mut ssi in runtime.identities.iter().cloned() { - let secret = runtime.find_signer(ssi.pk.fingerprint(), &passwd).unwrap(); - ssi.sig = secret.sk.sign(ssi.to_message()); - identities.push(ssi); - } - runtime.identities = identities; - runtime.store().unwrap() - } - */ + } /* */ + //Command::Recover => { + //use std::collections::HashSet; + //let passwd = rpassword::prompt_password("Password for private key encryption: ") + //.expect("unable to read password"); + //let mut identities = HashSet::new(); + //for mut ssi in runtime.identities.iter().cloned() { + //let secret = runtime.find_signer(ssi.pk.fingerprint(), &passwd).unwrap(); + //ssi.sig = secret.sk.sign(ssi.to_message()); + //identities.push(ssi); + //} + //runtime.identities = identities; + //runtime.store().unwrap() + //} + Command::Encrypt { + receiver, + text, + file, + } => { + let msg = get_message(text, file); + let receivers = receiver.into_iter().map(|query| { + runtime + .find_identity(query.clone()) + .unwrap_or_else(|| panic!("unknown identity {query}")) + .pk + }); + let encrypted = Encrypted::encrypt(msg, receivers).expect("unable to encrypt"); + println!("{encrypted}"); + } + } +} + +fn get_message(text: Option, file: Option) -> Vec { + match (text, file) { + (Some(t), None) => t.into_bytes(), + (None, Some(f)) => fs::read(f).expect("unable to read the file"), + (None, None) => { + let mut s = String::new(); + stdin() + .read_to_string(&mut s) + .expect("unable to read standard input"); + s.into_bytes() + } + _ => unreachable!(), } } diff --git a/src/public.rs b/src/public.rs index 1e665c7..9556399 100644 --- a/src/public.rs +++ b/src/public.rs @@ -21,11 +21,18 @@ use std::fmt::{self, Display, Formatter}; use std::hash::Hash; +use std::io; use std::str::FromStr; -use amplify::{hex, Bytes, Bytes32, Display}; +use amplify::{hex, Bytes, Bytes32, Bytes64, Display}; use baid64::{Baid64ParseError, DisplayBaid64, FromBaid64Str}; use sha2::{Digest, Sha256}; +use strict_encoding::{ + DecodeError, ReadTuple, StrictDecode, StrictEncode, StrictProduct, StrictTuple, StrictType, + TypeName, TypedRead, TypedWrite, WriteTuple, +}; + +use crate::LIB_NAME_SSI; #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Display, Default)] #[non_exhaustive] @@ -39,6 +46,28 @@ pub enum Algo { Other(u8), } +impl StrictType for Algo { + const STRICT_LIB_NAME: &'static str = LIB_NAME_SSI; + fn strict_name() -> Option { Some(tn!("Algo")) } +} +impl StrictProduct for Algo {} +impl StrictTuple for Algo { + const FIELD_COUNT: u8 = 1; +} +impl StrictEncode for Algo { + fn strict_encode(&self, writer: W) -> io::Result { + writer.write_tuple::(|w| Ok(w.write_field(&self.to_u8())?.complete())) + } +} +impl StrictDecode for Algo { + fn strict_decode(reader: &mut impl TypedRead) -> Result { + reader.read_tuple(|r| { + let val = r.read_field::()?; + Ok(Self::from(val)) + }) + } +} + #[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display, Error)] #[display("unknown algorithm '{0}'")] pub struct UnknownAlgo(String); @@ -56,13 +85,7 @@ impl FromStr for Algo { } impl From for u8 { - fn from(algo: Algo) -> Self { - match algo { - Algo::Ed25519 => 0x13, - Algo::Bip340 => 0, - Algo::Other(v) => v, - } - } + fn from(algo: Algo) -> Self { algo.to_u8() } } impl From for Algo { @@ -75,6 +98,16 @@ impl From for Algo { } } +impl Algo { + pub fn to_u8(&self) -> u8 { + match self { + Algo::Ed25519 => 0x13, + Algo::Bip340 => 0, + Algo::Other(v) => *v, + } + } +} + #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default, Display)] #[display(lowercase)] #[non_exhaustive] @@ -86,6 +119,28 @@ pub enum Chain { Other(u8), } +impl StrictType for Chain { + const STRICT_LIB_NAME: &'static str = LIB_NAME_SSI; + fn strict_name() -> Option { Some(tn!("Chain")) } +} +impl StrictProduct for Chain {} +impl StrictTuple for Chain { + const FIELD_COUNT: u8 = 1; +} +impl StrictEncode for Chain { + fn strict_encode(&self, writer: W) -> io::Result { + writer.write_tuple::(|w| Ok(w.write_field(&self.to_u8())?.complete())) + } +} +impl StrictDecode for Chain { + fn strict_decode(reader: &mut impl TypedRead) -> Result { + reader.read_tuple(|r| { + let val = r.read_field::()?; + Ok(Self::from(val)) + }) + } +} + #[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display, Error)] #[display("unknown chain '{0}'")] pub struct UnknownChain(String); @@ -103,13 +158,7 @@ impl FromStr for Chain { } impl From for u8 { - fn from(chain: Chain) -> Self { - match chain { - Chain::Bitcoin => 0xB7, - Chain::Liquid => 0x10, - Chain::Other(v) => v, - } - } + fn from(chain: Chain) -> Self { chain.to_u8() } } impl From for Chain { @@ -122,11 +171,23 @@ impl From for Chain { } } -#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] +impl Chain { + pub fn to_u8(&self) -> u8 { + match self { + Chain::Bitcoin => 0xB7, + Chain::Liquid => 0x10, + Chain::Other(v) => *v, + } + } +} + +#[derive(Getters, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] +#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_SSI)] pub struct SsiPub { - pub(crate) chain: Chain, - pub(crate) algo: Algo, - pub(crate) key: Bytes<30>, + chain: Chain, + algo: Algo, + key: Bytes<30>, } impl DisplayBaid64 for SsiPub { @@ -198,7 +259,13 @@ impl FromStr for SsiPub { } #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From)] -pub struct SsiSig(pub(crate) [u8; 64]); +#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_SSI)] +pub struct SsiSig(#[from([u8; 64])] Bytes64); + +impl SsiSig { + pub fn as_slice(&self) -> &[u8] { self.0.as_slice() } +} impl DisplayBaid64<64> for SsiSig { const HRI: &'static str = ""; @@ -207,7 +274,7 @@ impl DisplayBaid64<64> for SsiSig { const EMBED_CHECKSUM: bool = false; const MNEMONIC: bool = false; - fn to_baid64_payload(&self) -> [u8; 64] { self.0 } + fn to_baid64_payload(&self) -> [u8; 64] { self.0.to_byte_array() } } impl FromBaid64Str<64> for SsiSig {} @@ -268,6 +335,8 @@ impl FromStr for SsiQuery { } #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, From)] +#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_SSI)] pub struct Fingerprint([u8; 6]); impl DisplayBaid64<6> for Fingerprint { @@ -293,6 +362,8 @@ impl FromStr for Fingerprint { } #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] +#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_SSI)] pub struct SsiCert { pub fp: Fingerprint, pub pk: Option, diff --git a/src/runtime.rs b/src/runtime.rs index 28a719b..de1e692 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -21,7 +21,7 @@ pub const SSI_DIR: &str = "~/.ssi"; -use std::collections::{BTreeSet, HashSet}; +use std::collections::BTreeSet; use std::fs; use std::io::{self, BufRead, Write}; use std::os::unix::fs::PermissionsExt; @@ -49,7 +49,7 @@ pub enum Error { pub struct SsiRuntime { pub secrets: BTreeSet, - pub identities: HashSet, + pub identities: BTreeSet, } impl SsiRuntime { @@ -85,7 +85,7 @@ impl SsiRuntime { let mut permissions = file.metadata()?.permissions(); permissions.set_mode(0o600); let reader = io::BufReader::new(file); - let mut identities = set![]; + let mut identities = bset![]; for line in reader.lines() { let line = line?; identities.insert(line.parse()?); diff --git a/src/secret.rs b/src/secret.rs index f5fdae0..44f90fb 100644 --- a/src/secret.rs +++ b/src/secret.rs @@ -23,15 +23,15 @@ use std::fmt::{self, Display, Formatter}; use std::hash::Hash; use std::str::FromStr; -use aes::cipher::generic_array::GenericArray; -use aes::cipher::{BlockDecrypt, BlockEncrypt, KeyInit}; -use aes::{Aes256, Block}; use amplify::Bytes32; use baid64::Baid64ParseError; use chrono::{DateTime, Utc}; use sha2::{Digest, Sha256}; -use crate::{Algo, Bip340Secret, Chain, Ed25519Secret, Fingerprint, Ssi, SsiCert, SsiPub, SsiSig}; +use crate::{ + decrypt, encrypt, Algo, Bip340Secret, Chain, Ed25519Secret, Fingerprint, Ssi, SsiCert, SsiPub, + SsiSig, +}; #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] pub enum SsiSecret { @@ -143,29 +143,11 @@ impl SsiSecret { } pub fn encrypt(&mut self, passwd: impl AsRef) { - let key = Sha256::digest(passwd.as_ref().as_bytes()); - let key = GenericArray::from_slice(key.as_slice()); - let cipher = Aes256::new(key); - - let mut source = self.to_vec(); - for chunk in source.chunks_mut(16) { - let block = Block::from_mut_slice(chunk); - cipher.encrypt_block(block); - } - self.replace(&source); + self.replace(&encrypt(self.to_vec(), passwd.as_ref())); } pub fn decrypt(&mut self, passwd: impl AsRef) { - let key = Sha256::digest(passwd.as_ref().as_bytes()); - let key = GenericArray::from_slice(key.as_slice()); - let cipher = Aes256::new(key); - - let mut source = self.to_vec(); - for chunk in source.chunks_mut(16) { - let block = Block::from_mut_slice(chunk); - cipher.decrypt_block(block); - } - self.replace(&source); + self.replace(&decrypt(self.to_vec(), passwd.as_ref())); } pub fn to_vec(&self) -> Vec {