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}