diff --git a/Cargo.toml b/Cargo.toml index 7312990..ee0e860 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,8 @@ indicatif = "0.13.0" urlencoding = "1.0.0" base64 = "0.11.0" reqwest = "0.9.22" +sequoia-openpgp = "0.12.0" +oss-rust-sdk = "0.1.12" rust_util = { git = "https://github.com/jht5945/rust_util" } diff --git a/src/main.rs b/src/main.rs index cc493e5..e0b4f76 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,30 +1,134 @@ +extern crate sequoia_openpgp as openpgp; pub mod oss_util; +pub mod pgp_util; use std::{ - fs::File, + io::Write, time::SystemTime, }; -use oss_util::{ - OSSClient -}; use rust_util::{ XResult, }; +use crate::openpgp::armor; +use openpgp::{ + types::KeyFlags, + TPK, + parse::Parse, + serialize::stream::{ + Message, Encryptor, LiteralWriter, + }, +}; +// https://docs.sequoia-pgp.org/sequoia_openpgp/serialize/stream/struct.Encryptor.html +// https://gitlab.com/sequoia-pgp/sequoia/blob/master/openpgp/examples/generate-encrypt-decrypt.rs fn main() -> XResult<()> { println!("Hello, world!"); println!("{}", SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs()); - let oss_client = OSSClient::new("oss-cn-shanghai.aliyuncs.com", "***", "***"); - let oss_put_url = oss_client.generate_signed_put_url("hatterbucket", "abc_2.txt", 1000_u64); + // let oss_client = OSSClient::new("oss-cn-shanghai.aliyuncs.com", "***", "***"); + // let oss_put_url = oss_client.generate_signed_put_url("hatterbucket", "abc_2.txt", 1000_u64); - let client = reqwest::Client::new(); - let f = File::open("README.md")?; - let resp = client.put(&oss_put_url) - .body(f) - .send()?; - println!("RESP: {:?}", resp); + // let client = reqwest::Client::new(); + // let f = File::open("README.md")?; + // let resp = client.put(&oss_put_url) + // .body(f) + // .send()?; + // println!("RESP: {:?}", resp); + + let b = r#"-----BEGIN PGP PUBLIC KEY BLOCK----- + Comment: GPGTools - https://gpgtools.org + + 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 + =HzsD + -----END PGP PUBLIC KEY BLOCK-----"#.as_bytes(); + + + let tpk = TPK::from_bytes(b)?; + + let recipient = tpk.keys_valid() + .key_flags(KeyFlags::default() + .set_encrypt_at_rest(true) + .set_encrypt_for_transport(true)) + .map(|(_, _, key)| key.into()) + .nth(0) + .unwrap(); + + let sink = armor::Writer::new(std::io::stdout(), armor::Kind::Message, &[])?; + + let message = Message::new(sink); + let encryptor = Encryptor::for_recipient(message, recipient).build()?; + let mut pgp_encrypt_writer = LiteralWriter::new(encryptor).build()?; + pgp_encrypt_writer.write_all(b"Hello world.")?; + pgp_encrypt_writer.finalize()?; Ok(()) } diff --git a/src/oss_util.rs b/src/oss_util.rs index b42b47e..cdbbe09 100644 --- a/src/oss_util.rs +++ b/src/oss_util.rs @@ -1,4 +1,5 @@ use std::{ + fs::File, time::SystemTime, }; use crypto::{ @@ -9,6 +10,8 @@ use crypto::{ hmac::Hmac, sha1::Sha1, }; +use rust_util::XResult; +use reqwest::Response; pub const OSS_VERB_GET: &str = "GET"; pub const OSS_VERB_PUT: &str = "PUT"; @@ -29,6 +32,11 @@ impl<'a> OSSClient<'a> { } } + pub fn put_file(&self, bucket_name: &str, key: &str, expire_in_seconds: u64, file: File) -> XResult { + let client = reqwest::Client::new(); + Ok(client.put(&self.generate_signed_put_url(bucket_name, key, expire_in_seconds)).body(file).send()?) + } + pub fn generate_signed_put_url(&self, bucket_name: &str, key: &str, expire_in_seconds: u64) -> String { self.generate_signed_url(OSS_VERB_PUT, bucket_name, key, expire_in_seconds, true) } diff --git a/src/pgp_util.rs b/src/pgp_util.rs new file mode 100644 index 0000000..99d54d4 --- /dev/null +++ b/src/pgp_util.rs @@ -0,0 +1,65 @@ +extern crate sequoia_openpgp as openpgp; +use crate::openpgp::armor; +use std::{ + fs::File, + io::{ + Write, + BufWriter, + }, +}; +use rust_util::{ + XResult, + new_box_error, +}; +use openpgp::{ + types::KeyFlags, + TPK, + parse::Parse, + serialize::stream::{ + Recipient, + Message, + Encryptor, + LiteralWriter, + }, +}; + +pub struct OpenPGPClient { + pub tpk: TPK, +} + +impl OpenPGPClient { + pub fn from_file(file: &str) -> XResult { + Ok(OpenPGPClient{ + tpk: TPK::from_file(std::path::Path::new(file))?, + }) + } + + pub fn from_bytes(bs: &[u8]) -> XResult { + Ok(OpenPGPClient{ + tpk: TPK::from_bytes(&bs)?, + }) + } + + pub fn encrypt_file(&self, from_file: &str, to_file: &str, armor: bool) -> XResult<()> { + let recipient: Recipient = match self.tpk.keys_valid() + .key_flags(KeyFlags::default().set_encrypt_at_rest(true).set_encrypt_for_transport(true)) + .map(|(_, _, key)| key.into()) + .nth(0) { + None => return Err(new_box_error("Encryption key not found in TPK")), + Some(r) => r, + }; + let message = if armor { + Message::new(armor::Writer::new(std::io::stdout(), armor::Kind::Message, &[])?) + } else { + Message::new(BufWriter::new(File::open(to_file)?)) + }; + let encryptor = Encryptor::for_recipient(message, recipient).build()?; + let mut pgp_encrypt_writer = LiteralWriter::new(encryptor).build()?; + // TODO read from from_file + pgp_encrypt_writer.write_all(b"Hello world.")?; + pgp_encrypt_writer.finalize()?; + + Ok(()) + } +} +