Skip to main content

bssl_crypto/
ecdh.rs

1/* Copyright 2023 The BoringSSL Authors
2 *
3 * Permission to use, copy, modify, and/or distribute this software for any
4 * purpose with or without fee is hereby granted, provided that the above
5 * copyright notice and this permission notice appear in all copies.
6 *
7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14 */
15
16//! Elliptic Curve Diffie-Hellman operations.
17//!
18//! This module implements ECDH over the NIST curves P-256 and P-384.
19//!
20//! ```
21//! use bssl_crypto::{ecdh, ec::P256};
22//!
23//! let alice_private_key = ecdh::PrivateKey::<P256>::generate();
24//! let alice_public_key_serialized = alice_private_key.to_x962_uncompressed();
25//!
26//! // Somehow, Alice's public key is sent to Bob.
27//! let bob_private_key = ecdh::PrivateKey::<P256>::generate();
28//! let alice_public_key =
29//!     ecdh::PublicKey::<P256>::from_x962_uncompressed(
30//!         alice_public_key_serialized.as_ref())
31//!     .unwrap();
32//! let shared_key1 = bob_private_key.compute_shared_key(&alice_public_key);
33//!
34//! // Likewise, Alice gets Bob's public key and computes the same shared key.
35//! let bob_public_key = bob_private_key.to_public_key();
36//! let shared_key2 = alice_private_key.compute_shared_key(&bob_public_key);
37//! assert_eq!(shared_key1, shared_key2);
38//! ```
39
40use crate::{ec, sealed, with_output_vec, Buffer};
41use alloc::vec::Vec;
42use core::marker::PhantomData;
43
44/// An ECDH public key over the given curve.
45pub struct PublicKey<C: ec::Curve> {
46    point: ec::Point,
47    marker: PhantomData<C>,
48}
49
50impl<C: ec::Curve> PublicKey<C> {
51    /// Parse a public key in uncompressed X9.62 format. (This is the common
52    /// format for elliptic curve points beginning with an 0x04 byte.)
53    pub fn from_x962_uncompressed(x962: &[u8]) -> Option<Self> {
54        let point = ec::Point::from_x962_uncompressed(C::group(sealed::Sealed), x962)?;
55        Some(Self {
56            point,
57            marker: PhantomData,
58        })
59    }
60
61    /// Serialize this key as uncompressed X9.62 format.
62    pub fn to_x962_uncompressed(&self) -> Buffer {
63        self.point.to_x962_uncompressed()
64    }
65}
66
67/// An ECDH private key over the given curve.
68pub struct PrivateKey<C: ec::Curve> {
69    key: ec::Key,
70    marker: PhantomData<C>,
71}
72
73impl<C: ec::Curve> PrivateKey<C> {
74    /// Generate a random private key.
75    pub fn generate() -> Self {
76        Self {
77            key: ec::Key::generate(C::group(sealed::Sealed)),
78            marker: PhantomData,
79        }
80    }
81
82    /// Parse a `PrivateKey` from a zero-padded, big-endian representation of the secret scalar.
83    pub fn from_big_endian(scalar: &[u8]) -> Option<Self> {
84        let key = ec::Key::from_big_endian(C::group(sealed::Sealed), scalar)?;
85        Some(Self {
86            key,
87            marker: PhantomData,
88        })
89    }
90
91    /// Return the private scalar as zero-padded, big-endian bytes.
92    pub fn to_big_endian(&self) -> Buffer {
93        self.key.to_big_endian()
94    }
95
96    /// Parse an ECPrivateKey structure (from RFC 5915). The key must be on the
97    /// specified curve.
98    pub fn from_der_ec_private_key(der: &[u8]) -> Option<Self> {
99        let key = ec::Key::from_der_ec_private_key(C::group(sealed::Sealed), der)?;
100        Some(Self {
101            key,
102            marker: PhantomData,
103        })
104    }
105
106    /// Serialize this private key as an ECPrivateKey structure (from RFC 5915).
107    pub fn to_der_ec_private_key(&self) -> Buffer {
108        self.key.to_der_ec_private_key()
109    }
110
111    /// Parse a PrivateKeyInfo structure (from RFC 5208). The key must be on the
112    /// specified curve.
113    pub fn from_der_private_key_info(der: &[u8]) -> Option<Self> {
114        let key = ec::Key::from_der_private_key_info(C::group(sealed::Sealed), der)?;
115        Some(Self {
116            key,
117            marker: PhantomData,
118        })
119    }
120
121    /// Serialize this private key as a PrivateKeyInfo structure (from RFC 5208).
122    pub fn to_der_private_key_info(&self) -> Buffer {
123        self.key.to_der_private_key_info()
124    }
125
126    /// Serialize the _public_ part of this key in uncompressed X9.62 format.
127    pub fn to_x962_uncompressed(&self) -> Buffer {
128        self.key.to_x962_uncompressed()
129    }
130
131    /// Compute the shared key between this private key and the given public key.
132    /// The result should be used with a key derivation function that includes
133    /// the two public keys.
134    pub fn compute_shared_key(&self, other_public_key: &PublicKey<C>) -> Vec<u8> {
135        // 384 bits is the largest curve supported. The buffer is sized to be
136        // larger than this so that truncation of the output can be noticed.
137        let max_output = 384 / 8 + 1;
138        unsafe {
139            with_output_vec(max_output, |out_buf| {
140                // Safety:
141                //   - `out_buf` points to at least `max_output` bytes, as
142                //     required.
143                //   - The `EC_POINT` and `EC_KEY` pointers are valid by construction.
144                let num_out_bytes = bssl_sys::ECDH_compute_key(
145                    out_buf as *mut core::ffi::c_void,
146                    max_output,
147                    other_public_key.point.as_ffi_ptr(),
148                    self.key.as_ffi_ptr(),
149                    None,
150                );
151                // Out of memory is not handled by this crate.
152                assert!(num_out_bytes > 0);
153                let num_out_bytes = num_out_bytes as usize;
154                // If the buffer was completely filled then it was probably
155                // truncated, which should never happen.
156                assert!(num_out_bytes < max_output);
157                num_out_bytes
158            })
159        }
160    }
161
162    /// Return the public key corresponding to this private key.
163    pub fn to_public_key(&self) -> PublicKey<C> {
164        PublicKey {
165            point: self.key.to_point(),
166            marker: PhantomData,
167        }
168    }
169}
170
171#[cfg(test)]
172mod test {
173    use super::*;
174    use crate::ec::{P256, P384};
175
176    fn check_curve<C: ec::Curve>() {
177        let alice_private_key = PrivateKey::<C>::generate();
178        let alice_public_key = alice_private_key.to_public_key();
179        let alice_private_key =
180            PrivateKey::<C>::from_big_endian(alice_private_key.to_big_endian().as_ref()).unwrap();
181        let alice_private_key = PrivateKey::<C>::from_der_ec_private_key(
182            alice_private_key.to_der_ec_private_key().as_ref(),
183        )
184        .unwrap();
185
186        let bob_private_key = PrivateKey::<C>::generate();
187        let bob_public_key = bob_private_key.to_public_key();
188
189        let shared_key1 = alice_private_key.compute_shared_key(&bob_public_key);
190        let shared_key2 = bob_private_key.compute_shared_key(&alice_public_key);
191
192        assert_eq!(shared_key1, shared_key2);
193    }
194
195    #[test]
196    fn p256() {
197        check_curve::<P256>();
198    }
199
200    #[test]
201    fn p384() {
202        check_curve::<P384>();
203    }
204}