From 6f69e283a1503f8bde0e33ed2dd8407f9e8505ad Mon Sep 17 00:00:00 2001 From: Hatter Jiang Date: Thu, 14 Sep 2023 01:09:15 +0800 Subject: [PATCH] feat: ecdh --- Cargo.lock | 1 + Cargo.toml | 1 + src/cmd_encrypt.rs | 55 +++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 56 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 256362c..3741e9e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2185,6 +2185,7 @@ dependencies = [ "hex", "openpgp-card", "openpgp-card-pcsc", + "p256", "rand", "reqwest", "rpassword", diff --git a/Cargo.toml b/Cargo.toml index 4c9b163..ae320ff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ flate2 = "1.0.27" hex = "0.4.3" openpgp-card = "0.3.7" openpgp-card-pcsc = "0.3.0" +p256 = { version = "0.13.2", features = ["pem", "ecdh"] } rand = "0.8.5" reqwest = { version = "0.11.14", features = ["blocking", "rustls", "rustls-tls"] } rpassword = "7.2.0" diff --git a/src/cmd_encrypt.rs b/src/cmd_encrypt.rs index 969ae91..f2fef57 100644 --- a/src/cmd_encrypt.rs +++ b/src/cmd_encrypt.rs @@ -2,7 +2,11 @@ use std::fs; use std::path::PathBuf; use clap::Args; +use p256::{PublicKey, EncodedPoint}; +use p256::ecdh::EphemeralSecret; +use p256::elliptic_curve::sec1::{FromEncodedPoint, ToEncodedPoint}; use rand::random; +use rand::rngs::OsRng; use rsa::Pkcs1v15Encrypt; use rust_util::{debugging, failure, opt_result, simple_error, success, XResult}; @@ -73,6 +77,40 @@ fn encrypt_envelops(key: &[u8], envelops: &[&TinyEncryptConfigEnvelop]) -> XResu } fn encrypt_envelop_ecdh(key: &[u8], envelop: &TinyEncryptConfigEnvelop) -> XResult { + let public_key_point_hex = &envelop.public_part; + let public_key_point_bytes = opt_result!(hex::decode(public_key_point_hex), "Parse public key point hex failed: {}"); + let encoded_point = opt_result!(EncodedPoint::from_bytes(&public_key_point_bytes), "Parse public key point failed: {}"); + let public_key = PublicKey::from_encoded_point(&encoded_point).unwrap(); + + let esk = EphemeralSecret::random(&mut OsRng); + let epk = esk.public_key(); + let epk_bytes = EphemeralKeyBytes::from_public_key(&epk); + let public_key_encoded_point = public_key.to_encoded_point(false); + let shared_secret = esk.diffie_hellman(&public_key); + + // PORT Java Implementation + // public static WrapKey encryptEcdhP256(String kid, PublicKey publicKey, byte[] data) { + // AssertUtil.isTrue(publicKey instanceof ECPublicKey, "Public key must be EC public key"); + // if (data == null || data.length == 0) { + // return null; + // } + // final Tuple2 ecdh = ECUtil.ecdh(ECUtil.CURVE_SECP256R1, publicKey); + // final byte[] ePublicKeyBytes = ecdh.getVal1().getEncoded(); + // final byte[] key = KdfUtil.simpleKdf256(ecdh.getVal2()); + // + // final byte[] nonce = RandomTool.secureRandom().nextbytes(AESCryptTool.GCM_NONCE_LENGTH); + // final byte[] encryptedData = AESCryptTool.gcmEncrypt(key, nonce).from(Bytes.from(data)).toBytes().bytes(); + // final WrapKey wrapKey = new WrapKey(); + // final WrapKeyHeader wrapKeyHeader = new WrapKeyHeader(); + // wrapKeyHeader.setKid(kid); + // wrapKeyHeader.setEnc(ENC_AES256_GCM_P256); + // wrapKeyHeader.setePubKey(Base64s.uriCompatible().encode(ePublicKeyBytes)); + // wrapKey.setHeader(wrapKeyHeader); + // wrapKey.setNonce(nonce); + // wrapKey.setEncrytpedData(encryptedData); + // return wrapKey; + // } + Ok(TinyEncryptEnvelop { r#type: envelop.r#type, kid: envelop.kid.clone(), @@ -98,4 +136,19 @@ fn make_key256_and_nonce() -> (Vec, Vec) { let key: [u8; 32] = random(); let nonce: [u8; 12] = random(); (key.into(), nonce.into()) -} \ No newline at end of file +} + +#[derive(Debug)] +pub struct EphemeralKeyBytes(EncodedPoint); + +impl EphemeralKeyBytes { + fn from_public_key(epk: &PublicKey) -> Self { + EphemeralKeyBytes(epk.to_encoded_point(true)) + } + + fn decompress(&self) -> EncodedPoint { + // EphemeralKeyBytes is a valid compressed encoding by construction. + let p = PublicKey::from_encoded_point(&self.0).unwrap(); + p.to_encoded_point(false) + } +}