summaryrefslogtreecommitdiffstats
path: root/openpgp/src/crypto/backend/botan/ecdh.rs
blob: 6e5da2bb3c0f7df3928c7b8277e046e4fc30fc40 (plain)
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
//! Elliptic Curve Diffie-Hellman.

use botan::{
    RandomNumberGenerator,
    Privkey,
};

use crate::{
    Error,
    Result,
};
use crate::crypto::SessionKey;
use crate::crypto::ecdh::{encrypt_wrap, decrypt_unwrap2};
use crate::crypto::mem::Protected;
use crate::crypto::mpi::{
    MPI,
    PublicKey, SecretKeyMaterial, Ciphertext};
use crate::packet::{key, Key};
use crate::types::Curve;

/// Wraps a session key using Elliptic Curve Diffie-Hellman.
#[allow(non_snake_case)]
pub fn encrypt<R>(recipient: &Key<key::PublicParts, R>,
                  session_key: &SessionKey)
    -> Result<Ciphertext>
    where R: key::KeyRole
{
    let mut rng = RandomNumberGenerator::new_userspace()?;

    if let PublicKey::ECDH {
        ref curve, ref q,..
    } = recipient.mpis() {
        match curve {
            Curve::Cv25519 =>
                Err(Error::InvalidArgument("implemented elsewhere".into()).into()),

            // N/A
            Curve::Unknown(_) if ! curve.is_brainpoolp384() =>
                Err(Error::UnsupportedEllipticCurve(curve.clone()).into()),
            Curve::Ed25519 =>
                Err(Error::UnsupportedEllipticCurve(curve.clone()).into()),

            Curve::NistP256 | Curve::NistP384 | Curve::NistP521 |
            Curve::BrainpoolP256 |
            Curve::Unknown(_) | // XXX: this is BrainpoolP384
            Curve::BrainpoolP512 => {
                // Obtain the recipient public key R
                let R = &q.value();

                // Generate an ephemeral key pair {v, V=vG}
                let field_size = curve.field_size()?;
                let v = Privkey::create("ECDH", curve.botan_name()?, &mut rng)?;
                let Vx = v.pubkey()?.get_field("public_x")?;
                let Vy = v.pubkey()?.get_field("public_y")?;

                // Compute the shared point S = vR;
                let S: Protected = v.agree(&R, 32, b"", "Raw")?.into();
                let Sx: Protected = S[..field_size].into();

                encrypt_wrap(recipient, session_key,
                             MPI::new_point(&Vx.to_bin()?, &Vy.to_bin()?,
                                            field_size * 8),
                             &Sx.into())
            }
        }
    } else {
        Err(Error::InvalidArgument("Expected an ECDHPublicKey".into()).into())
    }
}

/// Unwraps a session key using Elliptic Curve Diffie-Hellman.
#[allow(non_snake_case)]
pub fn decrypt<R>(recipient: &Key<key::PublicParts, R>,
                  recipient_sec: &SecretKeyMaterial,
                  ciphertext: &Ciphertext,
                  plaintext_len: Option<usize>)
    -> Result<SessionKey>
    where R: key::KeyRole
{
    match (recipient.mpis(), recipient_sec, ciphertext) {
        (PublicKey::ECDH { ref curve, ..},
         SecretKeyMaterial::ECDH { ref scalar, },
         Ciphertext::ECDH { ref e, .. }) =>
        {
            let S: Protected = match curve {
                Curve::Cv25519 => return
                    Err(Error::InvalidArgument("implemented elsewhere".into()).into()),

                // N/A
                Curve::Unknown(_) if ! curve.is_brainpoolp384() => return
                    Err(Error::UnsupportedEllipticCurve(curve.clone()).into()),
                Curve::Ed25519 => return
                    Err(Error::UnsupportedEllipticCurve(curve.clone()).into()),


                Curve::NistP256 | Curve::NistP384 | Curve::NistP521 |
                Curve::BrainpoolP256 |
                Curve::Unknown(_) | // XXX: this is BrainpoolP384
                Curve::BrainpoolP512 => {
                    // Get the public part V of the ephemeral key.
                    let V = &e.value();

                    // Get our secret key.
                    let r = Privkey::load_ecdh(
                        &botan::MPI::new_from_bytes(scalar.value())?,
                        curve.botan_name()?)?;

                    // Compute the shared point S = rV = rvG, where (r, R)
                    // is the recipient's key pair.
                    r.agree(V, curve.field_size()?, b"", "Raw")?.into()
                },
            };

            decrypt_unwrap2(recipient.role_as_unspecified(), &S, ciphertext,
                            plaintext_len)
        }

        _ =>
            Err(Error::InvalidArgument("Expected an ECDHPublicKey".into()).into()),
    }
}