bssl_crypto/ec.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//! Definitions of NIST elliptic curves.
17//!
18//! If you're looking for curve25519, see the `x25519` and `ed25519` modules.
19
20// This module is substantially internal-only and is only public for the
21// [`Curve`] trait, which is shared by ECDH and ECDSA.
22
23use crate::{cbb_to_buffer, parse_with_cbs, scoped, sealed, Buffer, FfiSlice};
24use alloc::{fmt::Debug, vec::Vec};
25use core::ptr::{null, null_mut};
26
27/// An elliptic curve.
28pub trait Curve: Debug {
29 #[doc(hidden)]
30 fn group(_: sealed::Sealed) -> Group;
31
32 /// Hash `data` using a hash function suitable for the curve. (I.e.
33 /// SHA-256 for P-256 and SHA-384 for P-384.)
34 #[doc(hidden)]
35 fn hash(data: &[u8]) -> Vec<u8>;
36}
37
38/// The NIST P-256 curve, also called secp256r1.
39#[derive(Debug)]
40pub struct P256;
41
42impl Curve for P256 {
43 fn group(_: sealed::Sealed) -> Group {
44 Group::P256
45 }
46
47 fn hash(data: &[u8]) -> Vec<u8> {
48 crate::digest::Sha256::hash(data).to_vec()
49 }
50}
51
52/// The NIST P-384 curve, also called secp384r1.
53#[derive(Debug)]
54pub struct P384;
55
56impl Curve for P384 {
57 fn group(_: sealed::Sealed) -> Group {
58 Group::P384
59 }
60
61 fn hash(data: &[u8]) -> Vec<u8> {
62 crate::digest::Sha384::hash(data).to_vec()
63 }
64}
65
66#[derive(Copy, Clone)]
67#[doc(hidden)]
68pub enum Group {
69 P256,
70 P384,
71}
72
73impl Group {
74 fn as_ffi_ptr(self) -> *const bssl_sys::EC_GROUP {
75 // Safety: `group` is an address-space constant. These functions
76 // cannot fail and no resources need to be released in the future.
77 match self {
78 Group::P256 => unsafe { bssl_sys::EC_group_p256() },
79 Group::P384 => unsafe { bssl_sys::EC_group_p384() },
80 }
81 }
82}
83
84/// Point is a valid, finite point on some curve.
85pub(crate) struct Point {
86 group: *const bssl_sys::EC_GROUP,
87 point: *mut bssl_sys::EC_POINT,
88}
89
90impl Point {
91 /// Construct an uninitialized curve point. This is not public and all
92 /// callers must ensure that the point is initialized before being returned.
93 fn new(group: Group) -> Self {
94 let group = group.as_ffi_ptr();
95 // Safety: `group` is valid because it was constructed just above.
96 let point = unsafe { bssl_sys::EC_POINT_new(group) };
97 // `EC_POINT_new` only fails if out of memory, which is not a case that
98 // is handled short of panicking.
99 assert!(!point.is_null());
100 Self { group, point }
101 }
102
103 /// Construct a point by multipling the curve's base point by the given
104 /// scalar.
105 ///
106 /// Safety: `scalar` must be a valid pointer.
107 unsafe fn from_scalar(group: Group, scalar: *const bssl_sys::BIGNUM) -> Option<Self> {
108 let point = Self::new(group);
109 // Safety: the members of `point` are valid by construction. `scalar`
110 // is assumed to be valid.
111 let result = unsafe {
112 bssl_sys::EC_POINT_mul(
113 point.group,
114 point.point,
115 scalar,
116 /*q=*/ null(),
117 /*m=*/ null(),
118 /*ctx=*/ null_mut(),
119 )
120 };
121 if result != 1 {
122 return None;
123 }
124 if 1 == unsafe { bssl_sys::EC_POINT_is_at_infinity(point.group, point.point) } {
125 return None;
126 }
127 Some(point)
128 }
129
130 /// Duplicate the given finite point.
131 unsafe fn clone_from_ptr(
132 group: *const bssl_sys::EC_GROUP,
133 point: *const bssl_sys::EC_POINT,
134 ) -> Point {
135 assert_eq!(0, unsafe {
136 bssl_sys::EC_POINT_is_at_infinity(group, point)
137 });
138
139 // Safety: we assume that the caller is passing valid pointers
140 let new_point = unsafe { bssl_sys::EC_POINT_dup(point, group) };
141 // `EC_POINT_dup` only fails if out of memory, which is not a case that
142 // is handled short of panicking.
143 assert!(!new_point.is_null());
144
145 Self {
146 group,
147 point: new_point,
148 }
149 }
150
151 pub fn as_ffi_ptr(&self) -> *const bssl_sys::EC_POINT {
152 self.point
153 }
154
155 /// Create a new point from an uncompressed X9.62 representation.
156 ///
157 /// (X9.62 is the standard representation of an elliptic-curve point that
158 /// starts with an 0x04 byte.)
159 pub fn from_x962_uncompressed(group: Group, x962: &[u8]) -> Option<Self> {
160 const UNCOMPRESSED: u8 =
161 bssl_sys::point_conversion_form_t::POINT_CONVERSION_UNCOMPRESSED as u8;
162 if x962.first()? != &UNCOMPRESSED {
163 return None;
164 }
165
166 let point = Self::new(group);
167 // Safety: `point` is valid by construction. `x962` is a valid memory
168 // buffer.
169 let result = unsafe {
170 bssl_sys::EC_POINT_oct2point(
171 point.group,
172 point.point,
173 x962.as_ffi_ptr(),
174 x962.len(),
175 /*bn_ctx=*/ null_mut(),
176 )
177 };
178 if result == 1 {
179 // X9.62 format cannot represent the point at infinity, so this
180 // should be moot, but `Point` must never contain infinity.
181 assert_eq!(0, unsafe {
182 bssl_sys::EC_POINT_is_at_infinity(point.group, point.point)
183 });
184 Some(point)
185 } else {
186 None
187 }
188 }
189
190 pub fn to_x962_uncompressed(&self) -> Buffer {
191 // Safety: arguments are valid, `EC_KEY` ensures that the the group is
192 // correct for the point, and a `Point` is always finite.
193 unsafe { to_x962_uncompressed(self.group, self.point) }
194 }
195
196 pub fn from_der_subject_public_key_info(group: Group, spki: &[u8]) -> Option<Self> {
197 let mut pkey = scoped::EvpPkey::from_ptr(parse_with_cbs(
198 spki,
199 // Safety: if called, `pkey` is the non-null result of `EVP_parse_public_key`.
200 |pkey| unsafe { bssl_sys::EVP_PKEY_free(pkey) },
201 // Safety: `cbs` is a valid pointer in this context.
202 |cbs| unsafe { bssl_sys::EVP_parse_public_key(cbs) },
203 )?);
204 let ec_key = unsafe { bssl_sys::EVP_PKEY_get0_EC_KEY(pkey.as_ffi_ptr()) };
205 if ec_key.is_null() {
206 // Not an ECC key.
207 return None;
208 }
209 let parsed_group = unsafe { bssl_sys::EC_KEY_get0_group(ec_key) };
210 if parsed_group != group.as_ffi_ptr() {
211 // ECC key for a different curve.
212 return None;
213 }
214 let point = unsafe { bssl_sys::EC_KEY_get0_public_key(ec_key) };
215 if point.is_null() {
216 return None;
217 }
218 // Safety: `ec_key` is still owned by `pkey` and doesn't need to be freed.
219 Some(unsafe { Self::clone_from_ptr(parsed_group, point) })
220 }
221
222 /// Calls `func` with an `EC_KEY` that contains a copy of this point.
223 pub fn with_point_as_ec_key<F, T>(&self, func: F) -> T
224 where
225 F: FnOnce(*mut bssl_sys::EC_KEY) -> T,
226 {
227 let mut ec_key = scoped::EcKey::new();
228 // Safety: `self.group` is always valid by construction and this doesn't
229 // pass ownership.
230 assert_eq!(1, unsafe {
231 bssl_sys::EC_KEY_set_group(ec_key.as_ffi_ptr(), self.group)
232 });
233 // Safety: `self.point` is always valid by construction and this doesn't
234 // pass ownership.
235 assert_eq!(1, unsafe {
236 bssl_sys::EC_KEY_set_public_key(ec_key.as_ffi_ptr(), self.point)
237 });
238 func(ec_key.as_ffi_ptr())
239 }
240
241 pub fn to_der_subject_public_key_info(&self) -> Buffer {
242 // Safety: `ec_key` is a valid pointer in this context.
243 self.with_point_as_ec_key(|ec_key| unsafe { to_der_subject_public_key_info(ec_key) })
244 }
245}
246
247// Safety:
248//
249// An `EC_POINT` can be used concurrently from multiple threads so long as no
250// mutating operations are performed. The mutating operations used here are
251// `EC_POINT_mul` and `EC_POINT_oct2point` (which can be observed by setting
252// `point` to be `*const` in the struct and seeing what errors trigger.
253//
254// Both those operations are done internally, however, before a `Point` is
255// returned. So, after construction, callers cannot mutate the `EC_POINT`.
256unsafe impl Sync for Point {}
257unsafe impl Send for Point {}
258
259impl Drop for Point {
260 fn drop(&mut self) {
261 // Safety: `self.point` must be valid because only valid `Point`s can
262 // be constructed. `self.group` does not need to be freed.
263 unsafe { bssl_sys::EC_POINT_free(self.point) }
264 }
265}
266
267/// Key holds both a public and private key. While BoringSSL allows an `EC_KEY`
268/// to also be a) empty, b) holding only a private scalar, or c) holding only
269// a public key, those cases are never exposed as a `Key`.
270pub(crate) struct Key(*mut bssl_sys::EC_KEY);
271
272impl Key {
273 /// Construct an uninitialized key. This is not public and all
274 /// callers must ensure that the key is initialized before being returned.
275 fn new(group: Group) -> Self {
276 let key = unsafe { bssl_sys::EC_KEY_new() };
277 // `EC_KEY_new` only fails if out of memory, which is not a case that
278 // is handled short of panicking.
279 assert!(!key.is_null());
280
281 // Setting the group on a fresh `EC_KEY` never fails.
282 assert_eq!(1, unsafe {
283 bssl_sys::EC_KEY_set_group(key, group.as_ffi_ptr())
284 });
285
286 Self(key)
287 }
288
289 pub fn as_ffi_ptr(&self) -> *const bssl_sys::EC_KEY {
290 self.0
291 }
292
293 /// Generate a random private key.
294 pub fn generate(group: Group) -> Self {
295 let key = Self::new(group);
296 // Generation only fails if out of memory, which is only handled by
297 // panicking.
298 assert_eq!(1, unsafe { bssl_sys::EC_KEY_generate_key(key.0) });
299 // `EC_KEY_generate_key` is documented as also setting the public key.
300 key
301 }
302
303 /// Construct a private key from a big-endian representation of the private
304 /// scalar. The scalar must be zero padded to the correct length for the
305 /// curve.
306 pub fn from_big_endian(group: Group, scalar: &[u8]) -> Option<Self> {
307 let key = Self::new(group);
308 // Safety: `key.0` is always valid by construction.
309 let result = unsafe { bssl_sys::EC_KEY_oct2priv(key.0, scalar.as_ffi_ptr(), scalar.len()) };
310 if result != 1 {
311 return None;
312 }
313
314 // BoringSSL allows an `EC_KEY` to have a private scalar without a
315 // public point, but `Key` is never exposed in that state.
316
317 // Safety: `key.0` is valid by construction. The returned value is
318 // still owned the `EC_KEY`.
319 let scalar = unsafe { bssl_sys::EC_KEY_get0_private_key(key.0) };
320 assert!(!scalar.is_null());
321
322 // Safety: `scalar` is a valid pointer.
323 let point = unsafe { Point::from_scalar(group, scalar)? };
324 // Safety: `key.0` is valid by construction, as is `point.point`. The
325 // point is copied into the `EC_KEY` so ownership isn't being moved.
326 let result = unsafe { bssl_sys::EC_KEY_set_public_key(key.0, point.point) };
327 // Setting the public key should only fail if out of memory, which this
328 // crate doesn't handle, or if the groups don't match, which is
329 // impossible.
330 assert_eq!(result, 1);
331
332 Some(key)
333 }
334
335 pub fn to_big_endian(&self) -> Buffer {
336 let mut ptr: *mut u8 = null_mut();
337 // Safety: `self.0` is valid by construction. If this returns non-zero
338 // then ptr holds ownership of a buffer.
339 unsafe {
340 let len = bssl_sys::EC_KEY_priv2buf(self.0, &mut ptr);
341 assert!(len != 0);
342 Buffer::new(ptr, len)
343 }
344 }
345
346 /// Parses an ECPrivateKey structure (from RFC 5915).
347 pub fn from_der_ec_private_key(group: Group, der: &[u8]) -> Option<Self> {
348 let key = parse_with_cbs(
349 der,
350 // Safety: in this context, `key` is the non-null result of
351 // `EC_KEY_parse_private_key`.
352 |key| unsafe { bssl_sys::EC_KEY_free(key) },
353 // Safety: `cbs` is valid per `parse_with_cbs` and `group` always
354 // returns a valid pointer.
355 |cbs| unsafe { bssl_sys::EC_KEY_parse_private_key(cbs, group.as_ffi_ptr()) },
356 )?;
357 Some(Self(key))
358 }
359
360 /// Serializes this private key as an ECPrivateKey structure from RFC 5915.
361 pub fn to_der_ec_private_key(&self) -> Buffer {
362 cbb_to_buffer(64, |cbb| unsafe {
363 // Safety: the `EC_KEY` is always valid so `EC_KEY_marshal_private_key`
364 // should only fail if out of memory, which this crate doesn't handle.
365 assert_eq!(
366 1,
367 bssl_sys::EC_KEY_marshal_private_key(
368 cbb,
369 self.0,
370 bssl_sys::EC_PKEY_NO_PARAMETERS as u32
371 )
372 );
373 })
374 }
375
376 /// Parses a PrivateKeyInfo structure (from RFC 5208).
377 pub fn from_der_private_key_info(group: Group, der: &[u8]) -> Option<Self> {
378 let mut pkey = scoped::EvpPkey::from_ptr(parse_with_cbs(
379 der,
380 // Safety: in this context, `pkey` is the non-null result of
381 // `EVP_parse_private_key`.
382 |pkey| unsafe { bssl_sys::EVP_PKEY_free(pkey) },
383 // Safety: `cbs` is valid per `parse_with_cbs`.
384 |cbs| unsafe { bssl_sys::EVP_parse_private_key(cbs) },
385 )?);
386 let ec_key = unsafe { bssl_sys::EVP_PKEY_get1_EC_KEY(pkey.as_ffi_ptr()) };
387 if ec_key.is_null() {
388 return None;
389 }
390 // Safety: `ec_key` is now owned by this function.
391 let parsed_group = unsafe { bssl_sys::EC_KEY_get0_group(ec_key) };
392 if parsed_group == group.as_ffi_ptr() {
393 // Safety: parsing an EC_KEY always set the public key. It should
394 // be impossible for the public key to be infinity, but double-check.
395 let is_infinite = unsafe {
396 bssl_sys::EC_POINT_is_at_infinity(
397 bssl_sys::EC_KEY_get0_group(ec_key),
398 bssl_sys::EC_KEY_get0_public_key(ec_key),
399 )
400 };
401 if is_infinite == 0 {
402 // Safety: `EVP_PKEY_get1_EC_KEY` returned ownership, which we can move
403 // into the returned object.
404 return Some(Self(ec_key));
405 }
406 }
407 unsafe { bssl_sys::EC_KEY_free(ec_key) };
408 None
409 }
410
411 /// Serializes this private key as a PrivateKeyInfo structure from RFC 5208.
412 pub fn to_der_private_key_info(&self) -> Buffer {
413 let mut pkey = scoped::EvpPkey::new();
414 // Safety: `pkey` was just allocated above; the `EC_KEY` is valid by
415 // construction. This call takes a reference to the `EC_KEY` and so
416 // hasn't stolen ownership from `self`.
417 assert_eq!(1, unsafe {
418 bssl_sys::EVP_PKEY_set1_EC_KEY(pkey.as_ffi_ptr(), self.0)
419 });
420 cbb_to_buffer(64, |cbb| unsafe {
421 // `EVP_marshal_private_key` should always return one because this
422 // key is valid by construction.
423 assert_eq!(1, bssl_sys::EVP_marshal_private_key(cbb, pkey.as_ffi_ptr()));
424 })
425 }
426
427 pub fn to_point(&self) -> Point {
428 // Safety: `self.0` is valid by construction.
429 let group = unsafe { bssl_sys::EC_KEY_get0_group(self.0) };
430 let point = unsafe { bssl_sys::EC_KEY_get0_public_key(self.0) };
431 // A `Key` is never constructed without a public key.
432 assert!(!point.is_null());
433 // Safety: pointers are valid and `clone_from_ptr` doesn't take
434 // ownership.
435 unsafe { Point::clone_from_ptr(group, point) }
436 }
437
438 pub fn to_x962_uncompressed(&self) -> Buffer {
439 // Safety: `self.0` is valid by construction.
440 let group = unsafe { bssl_sys::EC_KEY_get0_group(self.0) };
441 let point = unsafe { bssl_sys::EC_KEY_get0_public_key(self.0) };
442 // Safety: arguments are valid, `EC_KEY` ensures that the the group is
443 // correct for the point, and a `Key` always holds a finite public point.
444 unsafe { to_x962_uncompressed(group, point) }
445 }
446
447 pub fn to_der_subject_public_key_info(&self) -> Buffer {
448 // Safety: `self.0` is always valid by construction.
449 unsafe { to_der_subject_public_key_info(self.0) }
450 }
451}
452
453// Safety:
454//
455// An `EC_KEY` is safe to use from multiple threads so long as no mutating
456// operations are performed. (Reference count changes don't count as mutating.)
457// The mutating operations used here are:
458// * EC_KEY_generate_key
459// * EC_KEY_oct2priv
460// * EC_KEY_set_public_key
461// But those are all done internally, before a `Key` is returned. So, once
462// constructed, callers cannot mutate the `EC_KEY`.
463unsafe impl Sync for Key {}
464unsafe impl Send for Key {}
465
466impl Drop for Key {
467 fn drop(&mut self) {
468 // Safety: `self.0` must be valid because only valid `Key`s can
469 // be constructed.
470 unsafe { bssl_sys::EC_KEY_free(self.0) }
471 }
472}
473
474/// Serialize a finite point to uncompressed X9.62 format.
475///
476/// Callers must ensure that the arguments are valid, that the point has the
477/// specified group, and that the point is finite.
478unsafe fn to_x962_uncompressed(
479 group: *const bssl_sys::EC_GROUP,
480 point: *const bssl_sys::EC_POINT,
481) -> Buffer {
482 cbb_to_buffer(65, |cbb| unsafe {
483 // Safety: the caller must ensure that the arguments are valid.
484 let result = bssl_sys::EC_POINT_point2cbb(
485 cbb,
486 group,
487 point,
488 bssl_sys::point_conversion_form_t::POINT_CONVERSION_UNCOMPRESSED,
489 /*bn_ctx=*/ null_mut(),
490 );
491 // The public key is always finite, so `EC_POINT_point2cbb` only fails
492 // if out of memory, which isn't handled by this crate.
493 assert_eq!(result, 1);
494 })
495}
496
497unsafe fn to_der_subject_public_key_info(ec_key: *mut bssl_sys::EC_KEY) -> Buffer {
498 let mut pkey = scoped::EvpPkey::new();
499 // Safety: this takes a reference to `ec_key` and so doesn't steal ownership.
500 assert_eq!(1, unsafe {
501 bssl_sys::EVP_PKEY_set1_EC_KEY(pkey.as_ffi_ptr(), ec_key)
502 });
503 cbb_to_buffer(65, |cbb| unsafe {
504 // The arguments are valid so this will only fail if out of memory,
505 // which this crate doesn't handle.
506 assert_eq!(1, bssl_sys::EVP_marshal_public_key(cbb, pkey.as_ffi_ptr()));
507 })
508}
509
510#[cfg(test)]
511mod test {
512 use super::*;
513
514 fn test_point_format<Serialize, Parse>(serialize_func: Serialize, parse_func: Parse)
515 where
516 Serialize: FnOnce(&Point) -> Buffer,
517 Parse: Fn(&[u8]) -> Option<Point>,
518 {
519 let key = Key::generate(Group::P256);
520 let point = key.to_point();
521
522 let mut vec = serialize_func(&point).as_ref().to_vec();
523 let point2 = parse_func(vec.as_slice()).unwrap();
524 assert_eq!(
525 point.to_x962_uncompressed().as_ref(),
526 point2.to_x962_uncompressed().as_ref()
527 );
528
529 assert!(parse_func(&vec.as_slice()[0..16]).is_none());
530
531 vec[10] ^= 1;
532 assert!(parse_func(vec.as_slice()).is_none());
533 vec[10] ^= 1;
534
535 assert!(parse_func(b"").is_none());
536 }
537
538 #[test]
539 fn x962() {
540 let x962 = b"\x04\x74\xcf\x69\xcb\xd1\x2b\x75\x07\x42\x85\xcf\x69\x6f\xc2\x56\x4b\x90\xe7\xeb\xbc\xd0\xe7\x20\x36\x86\x66\xbe\xcc\x94\x75\xa2\xa4\x4c\x2a\xf8\xa2\x56\xb8\x92\xb7\x7d\x17\xba\x97\x93\xbb\xf2\x9f\x52\x26\x7d\x90\xf9\x2c\x37\x26\x02\xbb\x4e\xd1\x89\x7c\xad\x54";
541 assert!(Point::from_x962_uncompressed(Group::P256, x962).is_some());
542
543 test_point_format(
544 |point| point.to_x962_uncompressed(),
545 |buf| Point::from_x962_uncompressed(Group::P256, buf),
546 );
547 }
548
549 #[test]
550 fn spki() {
551 test_point_format(
552 |point| point.to_der_subject_public_key_info(),
553 |buf| Point::from_der_subject_public_key_info(Group::P256, buf),
554 );
555 }
556
557 fn test_key_format<Serialize, Parse>(serialize_func: Serialize, parse_func: Parse)
558 where
559 Serialize: FnOnce(&Key) -> Buffer,
560 Parse: Fn(&[u8]) -> Option<Key>,
561 {
562 let key = Key::generate(Group::P256);
563
564 let vec = serialize_func(&key).as_ref().to_vec();
565 let key2 = parse_func(vec.as_slice()).unwrap();
566 assert_eq!(
567 key.to_x962_uncompressed().as_ref(),
568 key2.to_x962_uncompressed().as_ref()
569 );
570
571 assert!(parse_func(&vec.as_slice()[0..16]).is_none());
572 assert!(parse_func(b"").is_none());
573 }
574
575 #[test]
576 fn der_ec_private_key() {
577 test_key_format(
578 |key| key.to_der_ec_private_key(),
579 |buf| Key::from_der_ec_private_key(Group::P256, buf),
580 );
581 }
582
583 #[test]
584 fn der_private_key_info() {
585 test_key_format(
586 |key| key.to_der_private_key_info(),
587 |buf| Key::from_der_private_key_info(Group::P256, buf),
588 );
589 }
590
591 #[test]
592 fn big_endian() {
593 test_key_format(
594 |key| key.to_big_endian(),
595 |buf| Key::from_big_endian(Group::P256, buf),
596 );
597 }
598}