summaryrefslogtreecommitdiffstats
path: root/openpgp/src/crypto/backend/openssl/ecdh.rs
blob: bf6237c2d6e7192c0490b0a02f90b23d9d1146a0 (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
122
123
124
125
126
//! Elliptic Curve Diffie-Hellman.
use std::convert::{TryFrom, TryInto};

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

use openssl::bn::{BigNum, BigNumContext};
use openssl::derive::Deriver;
use openssl::ec::{EcGroup, EcKey, EcPoint, PointConversionForm};
use openssl::pkey::PKey;

/// Wraps a session key using Elliptic Curve Diffie-Hellman.
pub fn encrypt<R>(
    recipient: &Key<key::PublicParts, R>,
    session_key: &SessionKey,
) -> Result<Ciphertext>
where
    R: key::KeyRole,
{
    let (curve, q) = match recipient.mpis() {
        mpi::PublicKey::ECDH { curve, q, .. } => (curve, q),
        _ => return Err(Error::InvalidArgument("Expected an ECDHPublicKey".into()).into()),
    };
    if curve == &Curve::Cv25519 {
        let public = q.decode_point(curve)?.0;

        let public_key = PKey::public_key_from_raw_bytes(public, openssl::pkey::Id::X25519)?;

        let key = PKey::generate_x25519()?;
        let mut deriver = Deriver::new(&key)?;
        deriver.set_peer(&public_key)?;

        let secret = deriver.derive_to_vec()?.into();

        let q = mpi::MPI::new_compressed_point(&key.raw_public_key()?);

        return encrypt_wrap(recipient, session_key, q, &secret);
    }

    let nid = curve.try_into()?;
    let group = EcGroup::from_curve_name(nid)?;
    let mut ctx = BigNumContext::new()?;
    let point = EcPoint::from_bytes(&group, q.value(), &mut ctx)?;
    let recipient_key = EcKey::from_public_key(&group, &point)?;
    let recipient_key = PKey::<_>::try_from(recipient_key)?;

    let key = EcKey::generate(&group)?;

    let q = mpi::MPI::new(&key.public_key().to_bytes(
        &group,
        PointConversionForm::COMPRESSED,
        &mut ctx,
    )?);

    let key = PKey::<_>::try_from(key)?;
    let mut deriver = Deriver::new(&key)?;
    deriver.set_peer(&recipient_key)?;

    let secret = deriver.derive_to_vec()?.into();

    encrypt_wrap(recipient, session_key, q, &secret)
}

/// Unwraps a session key using Elliptic Curve Diffie-Hellman.
pub fn decrypt<R>(
    recipient: &Key<key::PublicParts, R>,
    recipient_sec: &SecretKeyMaterial,
    ciphertext: &Ciphertext,
    plaintext_len: Option<usize>,
) -> Result<SessionKey>
where
    R: key::KeyRole,
{
    let (curve, scalar, e, q) = match (recipient.mpis(), recipient_sec, ciphertext) {
        (
            mpi::PublicKey::ECDH {
                ref curve, ref q, ..
            },
            SecretKeyMaterial::ECDH { ref scalar },
            Ciphertext::ECDH { ref e, .. },
        ) => (curve, scalar, e, q),
        _ => return Err(Error::InvalidArgument("Expected an ECDHPublicKey".into()).into()),
    };

    if curve == &Curve::Cv25519 {
        let mut scalar = scalar.value_padded(32);
        scalar.reverse();

        let key = PKey::private_key_from_raw_bytes(&scalar[..], openssl::pkey::Id::X25519)?;

        let public = e.decode_point(curve)?.0;
        let public_key = PKey::public_key_from_raw_bytes(public, openssl::pkey::Id::X25519)?;

        let mut deriver = Deriver::new(&key)?;
        deriver.set_peer(&public_key)?;
        let secret = deriver.derive_to_vec()?.into();

        return decrypt_unwrap2(recipient.role_as_unspecified(), &secret,
                               ciphertext, plaintext_len);
    }

    let nid = curve.try_into()?;
    let group = EcGroup::from_curve_name(nid)?;
    let mut ctx = BigNumContext::new()?;
    let point = EcPoint::from_bytes(&group, e.value(), &mut ctx)?;

    let public_point = EcPoint::from_bytes(&group, q.value(), &mut ctx)?;
    let scalar = BigNum::from_slice(scalar.value())?;
    let key = EcKey::from_private_components(&group, &scalar, &public_point)?;

    let recipient_key = EcKey::from_public_key(&group, &point)?;
    let recipient_key = PKey::<_>::try_from(recipient_key)?;

    let key = PKey::<_>::try_from(key)?;
    let mut deriver = Deriver::new(&key)?;
    deriver.set_peer(&recipient_key)?;
    let secret = deriver.derive_to_vec()?.into();

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