Skip to main content

bssl_crypto/
ecdh.rs

1// Copyright 2023 The BoringSSL Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Elliptic Curve Diffie-Hellman operations.
16//!
17//! This module implements ECDH over the NIST curves P-256 and P-384.
18//!
19//! ```
20//! use bssl_crypto::{ecdh, ec::P256};
21//!
22//! let alice_private_key = ecdh::PrivateKey::<P256>::generate();
23//! let alice_public_key_serialized = alice_private_key.to_x962_uncompressed();
24//!
25//! // Somehow, Alice's public key is sent to Bob.
26//! let bob_private_key = ecdh::PrivateKey::<P256>::generate();
27//! let alice_public_key =
28//!     ecdh::PublicKey::<P256>::from_x962_uncompressed(
29//!         alice_public_key_serialized.as_ref())
30//!     .unwrap();
31//! let shared_key1 = bob_private_key.compute_shared_key(&alice_public_key);
32//!
33//! // Likewise, Alice gets Bob's public key and computes the same shared key.
34//! let bob_public_key = bob_private_key.to_public_key();
35//! let shared_key2 = alice_private_key.compute_shared_key(&bob_public_key);
36//! assert_eq!(shared_key1, shared_key2);
37//! ```
38
39use crate::{
40    ec::{self, Group},
41    with_output_vec, Buffer,
42};
43use alloc::vec::Vec;
44use core::marker::PhantomData;
45
46/// An ECDH public key over the given curve.
47pub struct PublicKey<C: ec::Curve> {
48    point: ec::Point,
49    marker: PhantomData<C>,
50}
51
52impl<C: ec::Curve> PublicKey<C> {
53    /// Parse a public key in uncompressed X9.62 format. (This is the common
54    /// format for elliptic curve points beginning with an 0x04 byte.)
55    pub fn from_x962_uncompressed(x962: &[u8]) -> Option<Self> {
56        let point = ec::Point::from_x962_uncompressed(C::group(), x962)?;
57        Some(Self {
58            point,
59            marker: PhantomData,
60        })
61    }
62
63    /// Serialize this key as uncompressed X9.62 format.
64    pub fn to_x962_uncompressed(&self) -> Buffer {
65        self.point.to_x962_uncompressed()
66    }
67}
68
69/// Parsed `ECPrivateKey` dispatched into the corresponding curve types.
70pub enum ParsedPrivateKey {
71    /// A P-256 private key.
72    P256(PrivateKey<ec::P256>),
73    /// A P-384 private key.
74    P384(PrivateKey<ec::P384>),
75}
76
77impl ParsedPrivateKey {
78    /// Parses an ECPrivateKey structure from a DER encoded structure per [RFC 5915],
79    /// whose curve is specified by the `ECParameters`.
80    ///
81    /// Unless the curve group is one of the variants of [`Group`], this method returns [`None`].
82    ///
83    /// [RFC 5915]: <https://datatracker.ietf.org/doc/html/rfc5915>
84    pub fn from_der(der: &[u8]) -> Option<Self> {
85        let key = ec::Key::from_der_ec_private_key_with_curve_names(der)?;
86        match key.get_group()? {
87            Group::P256 => Some(ParsedPrivateKey::P256(PrivateKey {
88                key,
89                marker: PhantomData,
90            })),
91            Group::P384 => Some(ParsedPrivateKey::P384(PrivateKey {
92                key,
93                marker: PhantomData,
94            })),
95        }
96    }
97}
98
99/// An ECDH private key over the given curve.
100pub struct PrivateKey<C: ec::Curve> {
101    key: ec::Key,
102    marker: PhantomData<C>,
103}
104
105impl<C: ec::Curve> Clone for PrivateKey<C> {
106    fn clone(&self) -> Self {
107        Self {
108            key: self.key.clone(),
109            marker: PhantomData,
110        }
111    }
112}
113
114impl<C: ec::Curve> PrivateKey<C> {
115    /// Generate a random private key.
116    pub fn generate() -> Self {
117        Self {
118            key: ec::Key::generate(C::group()),
119            marker: PhantomData,
120        }
121    }
122
123    /// Parse a `PrivateKey` from a zero-padded, big-endian representation of the secret scalar.
124    pub fn from_big_endian(scalar: &[u8]) -> Option<Self> {
125        let key = ec::Key::from_big_endian(C::group(), scalar)?;
126        Some(Self {
127            key,
128            marker: PhantomData,
129        })
130    }
131
132    /// Return the private scalar as zero-padded, big-endian bytes.
133    pub fn to_big_endian(&self) -> Buffer {
134        self.key.to_big_endian()
135    }
136
137    /// Parse an ECPrivateKey structure (from RFC 5915). The key must be on the
138    /// specified curve.
139    pub fn from_der_ec_private_key(der: &[u8]) -> Option<Self> {
140        let key = ec::Key::from_der_ec_private_key(C::group(), der)?;
141        Some(Self {
142            key,
143            marker: PhantomData,
144        })
145    }
146
147    /// Serialize this private key as an ECPrivateKey structure (from RFC 5915).
148    pub fn to_der_ec_private_key(&self) -> Buffer {
149        self.key.to_der_ec_private_key()
150    }
151
152    /// Parse a PrivateKeyInfo structure (from RFC 5208). The key must be on the
153    /// specified curve.
154    pub fn from_der_private_key_info(der: &[u8]) -> Option<Self> {
155        let key = ec::Key::from_der_private_key_info(C::group(), der)?;
156        Some(Self {
157            key,
158            marker: PhantomData,
159        })
160    }
161
162    /// Serialize this private key as a PrivateKeyInfo structure (from RFC 5208).
163    pub fn to_der_private_key_info(&self) -> Buffer {
164        self.key.to_der_private_key_info()
165    }
166
167    /// Serialize the _public_ part of this key in uncompressed X9.62 format.
168    pub fn to_x962_uncompressed(&self) -> Buffer {
169        self.key.to_x962_uncompressed()
170    }
171
172    /// Compute the shared key between this private key and the given public key.
173    /// The result should be used with a key derivation function that includes
174    /// the two public keys.
175    pub fn compute_shared_key(&self, other_public_key: &PublicKey<C>) -> Vec<u8> {
176        // 384 bits is the largest curve supported. The buffer is sized to be
177        // larger than this so that truncation of the output can be noticed.
178        let max_output = 384 / 8 + 1;
179        unsafe {
180            with_output_vec(max_output, |out_buf| {
181                // Safety:
182                //   - `out_buf` points to at least `max_output` bytes, as
183                //     required.
184                //   - The `EC_POINT` and `EC_KEY` pointers are valid by construction.
185                let num_out_bytes = bssl_sys::ECDH_compute_key(
186                    out_buf as *mut core::ffi::c_void,
187                    max_output,
188                    other_public_key.point.as_ffi_ptr(),
189                    self.key.as_ffi_ptr(),
190                    None,
191                );
192                // Out of memory is not handled by this crate.
193                assert!(num_out_bytes > 0);
194                let num_out_bytes = num_out_bytes as usize;
195                // If the buffer was completely filled then it was probably
196                // truncated, which should never happen.
197                assert!(num_out_bytes < max_output);
198                num_out_bytes
199            })
200        }
201    }
202
203    /// Return the public key corresponding to this private key.
204    pub fn to_public_key(&self) -> PublicKey<C> {
205        PublicKey {
206            point: self.key.to_point(),
207            marker: PhantomData,
208        }
209    }
210}
211
212#[cfg(test)]
213mod test {
214    use super::*;
215    use crate::ec::{P256, P384};
216
217    fn check_curve<C: ec::Curve>() {
218        let alice_private_key = PrivateKey::<C>::generate();
219        let alice_public_key = alice_private_key.to_public_key();
220        let alice_private_key =
221            PrivateKey::<C>::from_big_endian(alice_private_key.to_big_endian().as_ref()).unwrap();
222        let alice_private_key_der = alice_private_key.to_der_ec_private_key();
223        let alice_private_key =
224            PrivateKey::<C>::from_der_ec_private_key(alice_private_key_der.as_ref()).unwrap();
225
226        let bob_private_key = PrivateKey::<C>::generate();
227        let bob_public_key = bob_private_key.to_public_key();
228
229        let shared_key1 = alice_private_key.compute_shared_key(&bob_public_key);
230        let shared_key2 = bob_private_key.compute_shared_key(&alice_public_key);
231
232        assert_eq!(shared_key1, shared_key2);
233
234        match ParsedPrivateKey::from_der(alice_private_key_der.as_ref()).unwrap() {
235            ParsedPrivateKey::P256(_) => assert!(matches!(C::group(), Group::P256)),
236            ParsedPrivateKey::P384(_) => assert!(matches!(C::group(), Group::P384)),
237        }
238    }
239
240    #[test]
241    fn p256() {
242        check_curve::<P256>();
243    }
244
245    #[test]
246    fn p384() {
247        check_curve::<P384>();
248    }
249}