blob: 55fe4e97b86b44e5277c2ea01342581908346c21 [file] [log] [blame]
/* 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)
}
}