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
//! Module dedicated to PGP helpers.

use pgp_native::{
    crypto::{hash::HashAlgorithm, sym::SymmetricKeyAlgorithm},
    types::{CompressionAlgorithm, SecretKeyTrait},
    Deserializable, KeyType, SecretKeyParamsBuilder, SignedPublicKey, SignedSecretKey,
    StandaloneSignature, SubkeyParamsBuilder,
};
use smallvec::smallvec;
use std::{fs, io::Cursor, path::PathBuf};
use tokio::task;

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)
    };

    task::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> {
    task::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> {
    task::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> {
    task::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> {
    task::spawn_blocking(move || {
        let (sig, _) = StandaloneSignature::from_armor_single(Cursor::new(&bytes))
            .map_err(Error::ReadStandaloneSignatureFromArmoredBytesError)?;
        Ok(sig)
    })
    .await?
}