1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
use std::fs;
use std::path::Path;
use anyhow::Result;
use gpgme::{Context, EncryptFlags, Protocol};
use thiserror::Error;
use zeroize::Zeroize;
use crate::{
types::{Ciphertext, Plaintext},
Recipients,
};
const PROTO: Protocol = Protocol::OpenPgp;
const ENCRYPT_FLAGS: EncryptFlags = EncryptFlags::ALWAYS_TRUST;
pub fn context() -> Result<Context> {
Context::from_protocol(PROTO).map_err(|err| Err::Context(err).into())
}
pub fn encrypt(recipients: &Recipients, plaintext: Plaintext) -> Result<Ciphertext> {
let mut ciphertext = vec![];
let recipients: Vec<_> = recipients.keys().iter().map(|k| k.0.clone()).collect();
context()?
.encrypt_with_flags(
&recipients,
plaintext.unsecure_ref(),
&mut ciphertext,
ENCRYPT_FLAGS,
)
.map_err(Err::Encrypt)?;
let result = ciphertext.to_vec().into();
ciphertext.zeroize();
Ok(result)
}
pub fn encrypt_file(recipients: &Recipients, plaintext: Plaintext, path: &Path) -> Result<()> {
fs::write(path, encrypt(recipients, plaintext)?.unsecure_ref())
.map_err(|err| Err::WriteFile(err).into())
}
pub fn decrypt(ciphertext: Ciphertext) -> Result<Plaintext> {
let mut plaintext = vec![];
context()?
.decrypt(ciphertext.unsecure_ref(), &mut plaintext)
.map_err(Err::Decrypt)?;
let result = Ok(plaintext.to_vec().into());
plaintext.zeroize();
result
}
pub fn decrypt_file(path: &Path) -> Result<Plaintext> {
decrypt(fs::read(path).map_err(Err::ReadFile)?.into())
}
pub fn can_decrypt(ciphertext: Ciphertext) -> Result<bool> {
let mut plaintext = vec![];
let result = context()?.decrypt(ciphertext.unsecure_ref(), &mut plaintext);
plaintext.zeroize();
match result {
Ok(_) => Ok(true),
Err(err) if gpgme::error::Error::NO_SECKEY.code() == err.code() => Ok(false),
_ => Ok(true),
}
}
pub fn can_decrypt_file(path: &Path) -> Result<bool> {
can_decrypt(fs::read(path).map_err(Err::ReadFile)?.into())
}
#[derive(Debug, Error)]
pub enum Err {
#[error("failed to obtain GPGME cryptography context")]
Context(#[source] gpgme::Error),
#[error("failed to encrypt plaintext")]
Encrypt(#[source] gpgme::Error),
#[error("failed to decrypt ciphertext")]
Decrypt(#[source] gpgme::Error),
#[error("failed to write ciphertext to file")]
WriteFile(#[source] std::io::Error),
#[error("failed to read ciphertext from file")]
ReadFile(#[source] std::io::Error),
}