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}