| /* Copyright (c) 2023, Google Inc. | |
| * | |
| * Permission to use, copy, modify, and/or distribute this software for any | |
| * purpose with or without fee is hereby granted, provided that the above | |
| * copyright notice and this permission notice appear in all copies. | |
| * | |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
| * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
| * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY | |
| * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
| * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION | |
| * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |
| * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
| */ | |
| //! `EcKey` and `EcGroup` structs for working with elliptic curve cryptography. This module is | |
| //! intended for internal use within this crate only, to create higher-level abstractions suitable | |
| //! to be exposed externally. | |
| use alloc::borrow::ToOwned; | |
| use alloc::vec; | |
| use alloc::vec::Vec; | |
| use core::panic; | |
| use core::{borrow::Borrow, fmt::Debug, ops::Deref}; | |
| use crate::{bn::BigNum, CSlice, CSliceMut, ForeignType, ForeignTypeRef}; | |
| #[derive(Debug)] | |
| pub(crate) struct EcKey { | |
| ptr: *mut bssl_sys::EC_KEY, | |
| } | |
| // Safety: Implementation ensures `from_ptr(x).as_ptr() == x` | |
| unsafe impl ForeignType for EcKey { | |
| type CType = bssl_sys::EC_KEY; | |
| unsafe fn from_ptr(ptr: *mut Self::CType) -> Self { | |
| Self { ptr } | |
| } | |
| fn as_ptr(&self) -> *mut Self::CType { | |
| self.ptr | |
| } | |
| } | |
| // Safety: | |
| // - `EC_KEY`'s documentation says "A given object may be used concurrently on multiple threads by | |
| // non-mutating functions, provided no other thread is concurrently calling a mutating function.", | |
| // which matches Rust's aliasing rules. | |
| // - `ptr(&self)` and `ptr_mut(&mut self)` ensures that only a mutable reference can get a mutable | |
| // `EC_KEY` pointer outside of this module. | |
| unsafe impl Send for EcKey {} | |
| impl Clone for EcKey { | |
| fn clone(&self) -> Self { | |
| // Safety: | |
| // - EcKey makes sure self.ptr is a valid pointer. | |
| let ptr = unsafe { bssl_sys::EC_KEY_dup(self.ptr) }; | |
| Self { ptr } | |
| } | |
| } | |
| /// Error type returned when conversion to or from an `EcKey` failed. | |
| pub(crate) struct ConversionFailed; | |
| impl EcKey { | |
| pub fn new_by_ec_group(ec_group: &EcGroupRef) -> Self { | |
| // Safety: `EC_KEY_new` does not have preconditions | |
| let eckey = unsafe { bssl_sys::EC_KEY_new() }; | |
| assert!(!eckey.is_null()); | |
| // Safety: | |
| // - `eckey` is just allocated and doesn't have its group set yet | |
| // - `EcGroup` ensures the `ptr` it contains is valid | |
| unsafe { | |
| assert_eq!( | |
| bssl_sys::EC_KEY_set_group(eckey, ec_group.as_ptr()), | |
| 1, | |
| "EC_KEY_set_group failed" | |
| ); | |
| } | |
| // Safety: `eckey` is allocated and null-checked | |
| unsafe { Self::from_ptr(eckey) } | |
| } | |
| /// Try to create a public-key version of `EcKey` from the given `value`. Returns error if the | |
| /// slice is not a valid representation of a public key for the given curve. | |
| /// | |
| /// `curve_nid` should be a value defined in `bssl_sys::NID_*`. | |
| #[allow(clippy::panic)] | |
| pub(crate) fn try_new_public_key_from_bytes( | |
| ec_group: &EcGroupRef, | |
| value: &[u8], | |
| ) -> Result<Self, ConversionFailed> { | |
| let eckey = Self::new_by_ec_group(ec_group); | |
| let value_ffi = CSlice(value); | |
| // Safety: The input slice `value_ffi` is a CSlice from safe Rust. | |
| let result = unsafe { | |
| bssl_sys::EC_KEY_oct2key( | |
| eckey.ptr, | |
| value_ffi.as_ptr(), | |
| value_ffi.len(), | |
| core::ptr::null_mut(), | |
| ) | |
| }; | |
| match result { | |
| 0 => Err(ConversionFailed), | |
| 1 => Ok(eckey), | |
| _ => panic!("Unexpected return value {result} from EC_KEY_oct2key"), | |
| } | |
| } | |
| pub(crate) fn to_affine_coordinates(&self) -> (BigNum, BigNum) { | |
| let ecpoint = unsafe { bssl_sys::EC_KEY_get0_public_key(self.ptr) }; | |
| let bn_x = BigNum::new(); | |
| let bn_y = BigNum::new(); | |
| // Safety: | |
| // - `EcKey` and `BigNum` structs ensures validity of their pointers. | |
| let result = unsafe { | |
| bssl_sys::EC_POINT_get_affine_coordinates( | |
| bssl_sys::EC_KEY_get0_group(self.ptr), | |
| ecpoint, | |
| bn_x.as_ptr(), | |
| bn_y.as_ptr(), | |
| core::ptr::null_mut(), | |
| ) | |
| }; | |
| assert_eq!( | |
| result, 1, | |
| "bssl_sys::EC_POINT_get_affine_coordinates failed" | |
| ); | |
| (bn_x, bn_y) | |
| } | |
| pub(crate) fn generate(ec_group: &EcGroupRef) -> Self { | |
| let eckey = EcKey::new_by_ec_group(ec_group); | |
| // Safety: `EcKey` ensures eckey.ptr is valid. | |
| let result = unsafe { bssl_sys::EC_KEY_generate_key(eckey.as_ptr()) }; | |
| assert_eq!(result, 1, "bssl_sys::EC_KEY_generate_key failed"); | |
| eckey | |
| } | |
| pub(crate) fn try_new_public_key_from_affine_coordinates( | |
| ec_group: &EcGroupRef, | |
| x: &[u8], | |
| y: &[u8], | |
| ) -> Result<Self, ConversionFailed> { | |
| let bn_x = BigNum::from(x); | |
| let bn_y = BigNum::from(y); | |
| let eckey = EcKey::new_by_ec_group(ec_group); | |
| // Safety: | |
| // - Wrapper classes `EcKey` and `BigNum` ensures validity of the pointers | |
| let result = unsafe { | |
| bssl_sys::EC_KEY_set_public_key_affine_coordinates( | |
| eckey.as_ptr(), | |
| bn_x.as_ptr(), | |
| bn_y.as_ptr(), | |
| ) | |
| }; | |
| if result == 1 { | |
| Ok(eckey) | |
| } else { | |
| Err(ConversionFailed) | |
| } | |
| } | |
| /// Tries to convert the given bytes into a private key contained within `EcKey`. | |
| /// | |
| /// `private_key_bytes` must be padded to the size of `curve_nid`'s group order, otherwise the | |
| /// conversion will fail. | |
| pub(crate) fn try_from_raw_bytes( | |
| ec_group: &EcGroupRef, | |
| private_key_bytes: &[u8], | |
| ) -> Result<Self, ConversionFailed> { | |
| let eckey = EcKey::new_by_ec_group(ec_group); | |
| let private_key_bytes_ffi = CSlice(private_key_bytes); | |
| // Safety: | |
| // - `EcKey` ensures `eckey.ptr` is valid. | |
| // - `private_key_bytes` is a CSlice from safe-rust. | |
| let result = unsafe { | |
| bssl_sys::EC_KEY_oct2priv( | |
| eckey.as_ptr(), | |
| private_key_bytes_ffi.as_ptr(), | |
| private_key_bytes_ffi.len(), | |
| ) | |
| }; | |
| if result != 1 { | |
| return Err(ConversionFailed); | |
| } | |
| Ok(eckey) | |
| } | |
| /// Converts between the private key component of `eckey` and octet form. The octet form | |
| /// consists of the content octets of the `privateKey` `OCTET STRING` in an `ECPrivateKey` ASN.1 | |
| /// structure | |
| pub(crate) fn to_raw_bytes(&self) -> Vec<u8> { | |
| let mut output = vec![0_u8; 66]; | |
| let mut private_key_bytes_ffi = CSliceMut::from(&mut output[..]); | |
| // Safety: | |
| // - `EcKey` ensures `self.ptr` is valid. | |
| // - `private_key_bytes_ffi` is a CSliceMut we just allocated. | |
| // - 66 bytes is guaranteed to be sufficient to store an EC private key | |
| let num_octets_stored = unsafe { | |
| bssl_sys::EC_KEY_priv2oct( | |
| self.as_ptr(), | |
| private_key_bytes_ffi.as_mut_ptr(), | |
| private_key_bytes_ffi.len(), | |
| ) | |
| }; | |
| // Safety: `EC_KEY_priv2oct` just wrote `num_octets_stored` into the buffer. | |
| unsafe { output.set_len(num_octets_stored) } | |
| output | |
| } | |
| pub(crate) fn public_key_eq(&self, other: &Self) -> bool { | |
| let result = unsafe { | |
| bssl_sys::EC_POINT_cmp( | |
| bssl_sys::EC_KEY_get0_group(self.ptr), | |
| bssl_sys::EC_KEY_get0_public_key(self.ptr), | |
| bssl_sys::EC_KEY_get0_public_key(other.ptr), | |
| core::ptr::null_mut(), | |
| ) | |
| }; | |
| assert_ne!(result, -1, "bssl_sys::EC_POINT_cmp failed"); | |
| result == 0 | |
| } | |
| pub(crate) fn to_vec(&self) -> Vec<u8> { | |
| // Safety: `self.ptr` is owned by `self` | |
| let ecgroup = unsafe { bssl_sys::EC_KEY_get0_group(self.ptr) }; | |
| let ecpoint = unsafe { bssl_sys::EC_KEY_get0_public_key(self.ptr) }; | |
| let conv_form = unsafe { bssl_sys::EC_KEY_get_conv_form(self.ptr) }; | |
| // Safety: | |
| // - When passing null to EC_POINT_point2oct's `buf` argument, it returns the size of the | |
| // resulting buffer. | |
| let output_size = unsafe { | |
| bssl_sys::EC_POINT_point2oct( | |
| ecgroup, | |
| ecpoint, | |
| conv_form, | |
| core::ptr::null_mut(), | |
| 0, | |
| core::ptr::null_mut(), | |
| ) | |
| }; | |
| assert_ne!(output_size, 0, "bssl_sys::EC_POINT_point2oct failed"); | |
| let mut result_vec = Vec::<u8>::with_capacity(output_size); | |
| let buf_len = unsafe { | |
| bssl_sys::EC_POINT_point2oct( | |
| ecgroup, | |
| ecpoint, | |
| conv_form, | |
| result_vec.as_mut_ptr(), | |
| output_size, | |
| core::ptr::null_mut(), | |
| ) | |
| }; | |
| assert_ne!(buf_len, 0, "bssl_sys::EC_POINT_point2oct failed"); | |
| // Safety: The length is what EC_POINT_point2oct just told us it filled into the buffer. | |
| unsafe { result_vec.set_len(buf_len) } | |
| result_vec | |
| } | |
| } | |
| impl Drop for EcKey { | |
| fn drop(&mut self) { | |
| // Safety: `self.ptr` is owned by this struct | |
| unsafe { bssl_sys::EC_KEY_free(self.ptr) } | |
| } | |
| } | |
| /// Describes an elliptic curve. | |
| #[non_exhaustive] | |
| pub struct EcGroupRef; | |
| // Safety: Default implementation in ForeignTypeRef ensures the preconditions | |
| // required by that trait holds. | |
| unsafe impl ForeignTypeRef for EcGroupRef { | |
| type CType = bssl_sys::EC_GROUP; | |
| } | |
| impl Borrow<EcGroupRef> for EcGroup { | |
| fn borrow(&self) -> &EcGroupRef { | |
| unsafe { EcGroupRef::from_ptr(self.ptr) } | |
| } | |
| } | |
| impl ToOwned for EcGroupRef { | |
| type Owned = EcGroup; | |
| fn to_owned(&self) -> Self::Owned { | |
| // Safety: `EcGroupRef` is a valid pointer | |
| let new_ec_group = unsafe { bssl_sys::EC_GROUP_dup(self.as_ptr()) }; | |
| assert!(!new_ec_group.is_null(), "EC_GROUP_dup failed"); | |
| EcGroup { ptr: new_ec_group } | |
| } | |
| } | |
| impl AsRef<EcGroupRef> for EcGroup { | |
| fn as_ref(&self) -> &EcGroupRef { | |
| self.deref() | |
| } | |
| } | |
| impl PartialEq for EcGroupRef { | |
| fn eq(&self, other: &Self) -> bool { | |
| // Safety: | |
| // - Self and other are valid pointers since they come from `EcGroupRef` | |
| // - Third argument is ignored | |
| unsafe { | |
| bssl_sys::EC_GROUP_cmp( | |
| self.as_ptr(), | |
| other.as_ptr(), | |
| /* ignored */ core::ptr::null_mut(), | |
| ) == 0 | |
| } | |
| } | |
| } | |
| impl Eq for EcGroupRef {} | |
| pub struct EcGroup { | |
| ptr: *mut bssl_sys::EC_GROUP, | |
| } | |
| impl Deref for EcGroup { | |
| type Target = EcGroupRef; | |
| fn deref(&self) -> &Self::Target { | |
| unsafe { EcGroupRef::from_ptr(self.ptr) } | |
| } | |
| } | |
| impl Drop for EcGroup { | |
| fn drop(&mut self) { | |
| unsafe { bssl_sys::EC_GROUP_free(self.ptr) } | |
| } | |
| } | |
| /// An elliptic curve, used as the type parameter for [`PublicKey`] and [`PrivateKey`]. | |
| pub trait Curve: Debug { | |
| /// The size of the affine coordinates for this curve. | |
| const AFFINE_COORDINATE_SIZE: usize; | |
| /// Create a new [`EcGroup`] for this curve. | |
| fn ec_group() -> &'static EcGroupRef; | |
| } | |
| /// The P-224 curve, corresponding to `NID_secp224r1`. | |
| #[derive(Debug)] | |
| pub struct P224; | |
| impl Curve for P224 { | |
| const AFFINE_COORDINATE_SIZE: usize = 28; | |
| fn ec_group() -> &'static EcGroupRef { | |
| // Safety: EC_group_p224 does not have any preconditions | |
| unsafe { EcGroupRef::from_ptr(bssl_sys::EC_group_p224() as *mut _) } | |
| } | |
| } | |
| /// The P-256 curve, corresponding to `NID_X9_62_prime256v1`. | |
| #[derive(Debug)] | |
| pub struct P256; | |
| impl Curve for P256 { | |
| const AFFINE_COORDINATE_SIZE: usize = 32; | |
| fn ec_group() -> &'static EcGroupRef { | |
| // Safety: EC_group_p256 does not have any preconditions | |
| unsafe { EcGroupRef::from_ptr(bssl_sys::EC_group_p256() as *mut _) } | |
| } | |
| } | |
| /// The P-384 curve, corresponding to `NID_secp384r1`. | |
| #[derive(Debug)] | |
| pub struct P384; | |
| impl Curve for P384 { | |
| const AFFINE_COORDINATE_SIZE: usize = 48; | |
| fn ec_group() -> &'static EcGroupRef { | |
| // Safety: EC_group_p384 does not have any preconditions | |
| unsafe { EcGroupRef::from_ptr(bssl_sys::EC_group_p384() as *mut _) } | |
| } | |
| } | |
| /// The P-521 curve, corresponding to `NID_secp521r1`. | |
| #[derive(Debug)] | |
| pub struct P521; | |
| impl Curve for P521 { | |
| const AFFINE_COORDINATE_SIZE: usize = 66; | |
| fn ec_group() -> &'static EcGroupRef { | |
| // Safety: EC_group_p521 does not have any preconditions | |
| unsafe { EcGroupRef::from_ptr(bssl_sys::EC_group_p521() as *mut _) } | |
| } | |
| } | |
| #[cfg(test)] | |
| mod test { | |
| use crate::ec::P521; | |
| use super::{Curve, EcGroupRef, P256}; | |
| #[test] | |
| fn test_ec_group_clone_and_eq() { | |
| let group = P256::ec_group(); | |
| let group_clone = group.to_owned(); | |
| let group2: &EcGroupRef = &group_clone; | |
| assert!(group == group2); | |
| } | |
| #[test] | |
| fn test_ec_group_not_equal() { | |
| let group = P256::ec_group(); | |
| let group2 = P521::ec_group(); | |
| assert!(group != group2) | |
| } | |
| } |