pgp/
utils.rs

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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
//! Module dedicated to PGP helpers.

use std::{fs, io::Cursor, path::PathBuf};

use native::{
    crypto::{hash::HashAlgorithm, sym::SymmetricKeyAlgorithm},
    types::{CompressionAlgorithm, SecretKeyTrait},
    Deserializable, KeyType, SecretKeyParamsBuilder, SignedPublicKey, SignedSecretKey,
    StandaloneSignature, SubkeyParamsBuilder,
};
use smallvec::smallvec;

use crate::{Error, Result};

/// Generates a new pair of secret and public keys for the given email
/// address and passphrase.
pub async fn gen_key_pair(
    email: impl ToString,
    passphrase: impl ToString,
) -> Result<(SignedSecretKey, SignedPublicKey)> {
    let email = email.to_string();
    let passphrase = passphrase.to_string();
    let passphrase = if passphrase.trim().is_empty() {
        None
    } else {
        Some(passphrase)
    };

    spawn_blocking(move || {
        let key_params = SecretKeyParamsBuilder::default()
            .key_type(KeyType::EdDSA)
            .can_create_certificates(true)
            .can_sign(true)
            .primary_user_id(email)
            .passphrase(passphrase.clone())
            .preferred_symmetric_algorithms(smallvec![SymmetricKeyAlgorithm::AES256])
            .preferred_hash_algorithms(smallvec![HashAlgorithm::SHA2_256])
            .preferred_compression_algorithms(smallvec![CompressionAlgorithm::ZLIB])
            .subkey(
                SubkeyParamsBuilder::default()
                    .key_type(KeyType::ECDH)
                    .can_encrypt(true)
                    .passphrase(passphrase)
                    .build()
                    .map_err(Error::BuildPublicKeyParamsError)?,
            )
            .build()
            .map_err(Error::BuildSecretKeyParamsError)?;

        let skey = key_params
            .generate()
            .map_err(Error::GenerateSecretKeyError)?;
        let skey = skey.sign(String::new).map_err(Error::SignSecretKeyError)?;
        skey.verify().map_err(Error::VerifySecretKeyError)?;

        let pkey = skey.public_key();
        let pkey = pkey
            .sign(&skey, String::new)
            .map_err(Error::SignPublicKeyError)?;
        pkey.verify().map_err(Error::VerifyPublicKeyError)?;

        Ok((skey, pkey))
    })
    .await?
}

/// Reads a signed public key from the given path.
///
/// The given path needs to contain a single armored secret key,
/// otherwise it fails.
pub async fn read_pkey_from_path(path: PathBuf) -> Result<SignedPublicKey> {
    spawn_blocking(move || {
        let data =
            fs::read(&path).map_err(|err| Error::ReadArmoredPublicKeyError(err, path.clone()))?;
        let (pkey, _) = SignedPublicKey::from_armor_single(Cursor::new(data))
            .map_err(|err| Error::ParseArmoredPublicKeyError(err, path.clone()))?;
        Ok(pkey)
    })
    .await?
}

/// Reads a signed secret key from the given path.
///
/// The given path needs to contain a single armored secret key,
/// otherwise it fails.
pub async fn read_skey_from_file(path: PathBuf) -> Result<SignedSecretKey> {
    spawn_blocking(move || {
        let data = fs::read(&path)
            .map_err(|err| Error::ReadArmoredSecretKeyFromPathError(err, path.clone()))?;
        let (skey, _) = SignedSecretKey::from_armor_single(Cursor::new(data))
            .map_err(|err| Error::ParseArmoredSecretKeyFromPathError(err, path.clone()))?;
        Ok(skey)
    })
    .await?
}

/// Reads a signed secret key from the given raw string.
///
/// The given raw string needs to contain a single armored secret key,
/// otherwise it fails.
pub async fn read_skey_from_string(string: String) -> Result<SignedSecretKey> {
    spawn_blocking(move || {
        let (skey, _) = SignedSecretKey::from_armor_single(Cursor::new(string))
            .map_err(Error::ParseArmoredSecretKeyFromStringError)?;
        Ok(skey)
    })
    .await?
}

/// Reads a standalone signature from the given raw bytes.
///
/// The given raw bytes needs to match a single armored signature,
/// otherwise it fails.
pub async fn read_sig_from_bytes(bytes: Vec<u8>) -> Result<StandaloneSignature> {
    spawn_blocking(move || {
        let (sig, _) = StandaloneSignature::from_armor_single(Cursor::new(&bytes))
            .map_err(Error::ReadStandaloneSignatureFromArmoredBytesError)?;
        Ok(sig)
    })
    .await?
}

// TODO
// #[cfg(feature = "async-std")]
// pub(crate) async fn spawn_blocking<F, T>(f: F) -> Result<T>
// where
//     F: FnOnce() -> T + Send + 'static,
//     T: Send + 'static,
// {
//     Ok(async_std::task::spawn_blocking(f).await)
// }

pub(crate) async fn spawn_blocking<F, T>(f: F) -> Result<T>
where
    F: FnOnce() -> T + Send + 'static,
    T: Send + 'static,
{
    Ok(tokio::task::spawn_blocking(f).await?)
}