prs_lib/crypto/backend/gpgme/
context.rs

1//! Provides GPGME binary context adapter.
2
3use std::env;
4
5use anyhow::Result;
6use gpgme::{Context as GpgmeContext, PinentryMode, Protocol};
7use thiserror::Error;
8
9use super::raw;
10use crate::crypto::{Config, IsContext, Key, Proto, proto};
11use crate::{Ciphertext, Plaintext, Recipients, util};
12
13/// Protocol to use.
14const PROTO: Protocol = Protocol::OpenPgp;
15
16/// Create GPGME crypto context.
17pub fn context(config: &Config) -> Result<Context, Err> {
18    // Set environment when using GPG TTY
19    if config.gpg_tty
20        && !util::env::has_gpg_tty()
21        && let Some(tty) = util::tty::get_tty()
22    {
23        // Safe because GPGME will run in the same thread
24        unsafe { env::set_var("GPG_TTY", tty) };
25    }
26
27    let mut context = gpgme::Context::from_protocol(PROTO).map_err(Err::Context)?;
28
29    // Set pinentry mode when using GPG TTY
30    if config.gpg_tty {
31        context
32            .set_pinentry_mode(PinentryMode::Loopback)
33            .map_err(Err::Context)?;
34    }
35
36    Ok(Context::from(context))
37}
38
39/// GPGME crypto context.
40pub struct Context {
41    /// GPGME crytp context.
42    context: GpgmeContext,
43}
44
45impl Context {
46    pub fn from(context: GpgmeContext) -> Self {
47        Self { context }
48    }
49}
50
51impl IsContext for Context {
52    fn encrypt(&mut self, recipients: &Recipients, plaintext: Plaintext) -> Result<Ciphertext> {
53        let fingerprints: Vec<String> = recipients
54            .keys()
55            .iter()
56            .map(|key| key.fingerprint(false))
57            .collect();
58        let fingerprints: Vec<&str> = fingerprints.iter().map(|fp| fp.as_str()).collect();
59        raw::encrypt(&mut self.context, &fingerprints, plaintext)
60    }
61
62    fn decrypt(&mut self, ciphertext: Ciphertext) -> Result<Plaintext> {
63        raw::decrypt(&mut self.context, ciphertext)
64    }
65
66    fn can_decrypt(&mut self, ciphertext: Ciphertext) -> Result<bool> {
67        raw::can_decrypt(&mut self.context, ciphertext)
68    }
69
70    fn keys_public(&mut self) -> Result<Vec<Key>> {
71        Ok(raw::public_keys(&mut self.context)?
72            .into_iter()
73            .map(|key| {
74                Key::Gpg(proto::gpg::Key {
75                    fingerprint: key.0,
76                    user_ids: key.1,
77                })
78            })
79            .collect())
80    }
81
82    fn keys_private(&mut self) -> Result<Vec<Key>> {
83        Ok(raw::private_keys(&mut self.context)?
84            .into_iter()
85            .map(|key| {
86                Key::Gpg(proto::gpg::Key {
87                    fingerprint: key.0,
88                    user_ids: key.1,
89                })
90            })
91            .collect())
92    }
93
94    fn import_key(&mut self, key: &[u8]) -> Result<()> {
95        raw::import_key(&mut self.context, key)
96    }
97
98    fn export_key(&mut self, key: Key) -> Result<Vec<u8>> {
99        raw::export_key(&mut self.context, &key.fingerprint(false))
100    }
101
102    fn supports_proto(&self, proto: Proto) -> bool {
103        proto == Proto::Gpg
104    }
105}
106
107/// GPGME context error.
108#[derive(Debug, Error)]
109pub enum Err {
110    #[error("failed to obtain GPGME cryptography context")]
111    Context(#[source] gpgme::Error),
112}