Skip to content

Commit 4615997

Browse files
committed
email signature attachment
1 parent 099f294 commit 4615997

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+4984
-1
lines changed

jacs/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ rpassword = "7.3.1"
7878
validator = "0.20.0"
7979
uuid = { version = "1.16.0", features = ["v4", "v7", "js"] }
8080
lazy_static = "1.5"
81+
mail-parser = "0.11"
82+
unicode-normalization = "0.1"
83+
thiserror = "2"
8184
dirs = "5.0"
8285
env_logger = "0.11.8"
8386
futures-util = "0.3.31"

jacs/src/crypt/mod.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,55 @@ pub trait KeyManager {
183183
}
184184

185185
impl Agent {
186+
/// Sign raw bytes and return the base64-encoded signature.
187+
///
188+
/// This is the byte-level equivalent of `sign_string`. Used by
189+
/// the email signing module where the payload is binary.
190+
pub fn sign_bytes(&mut self, data: &[u8]) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
191+
let config = self.config.as_ref().ok_or(
192+
"Byte signing failed: agent configuration not initialized.",
193+
)?;
194+
let key_algorithm = config.get_key_algorithm().map_err(|e| {
195+
format!("Byte signing failed: could not determine signing algorithm: {}", e)
196+
})?;
197+
198+
let binding = self.get_private_key().map_err(|e| {
199+
format!("Byte signing failed: private key not loaded: {}", e)
200+
})?;
201+
202+
let is_ephemeral = self.is_ephemeral();
203+
let has_key_store = self.get_key_store().is_some();
204+
let stored_algo = self.get_key_algorithm().cloned();
205+
let (key_bytes, ks_box): (Vec<u8>, Box<dyn KeyStore>) = if is_ephemeral {
206+
let raw = binding.expose_secret().clone();
207+
let ks: Box<dyn KeyStore> = if has_key_store {
208+
let algo = stored_algo.as_deref().unwrap_or("pq2025");
209+
Box::new(crate::keystore::InMemoryKeyStore::new(algo))
210+
} else {
211+
Box::new(FsEncryptedStore)
212+
};
213+
(raw, ks)
214+
} else {
215+
let decrypted =
216+
crate::crypt::aes_encrypt::decrypt_private_key_secure(binding.expose_secret())
217+
.map_err(|e| {
218+
format!("Byte signing failed: could not decrypt private key: {}", e)
219+
})?;
220+
(
221+
decrypted.as_slice().to_vec(),
222+
Box::new(FsEncryptedStore) as Box<dyn KeyStore>,
223+
)
224+
};
225+
226+
let sig_bytes = ks_box
227+
.sign_detached(&key_bytes, data, &key_algorithm)
228+
.map_err(|e| {
229+
format!("Byte signing failed: cryptographic signing operation failed: {}", e)
230+
})?;
231+
232+
Ok(sig_bytes)
233+
}
234+
186235
/// Generate keys using a specific KeyStore implementation.
187236
/// For ephemeral agents, uses set_keys_raw (no AES encryption).
188237
/// For persistent agents, uses set_keys (AES-encrypts private key).

0 commit comments

Comments
 (0)