blob: 90452c547a8cedd0bf9014e2dce47b0da354d089 [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.
*/
//! Definitions of NIST elliptic curves.
//!
//! If you're looking for curve25519, see the `x25519` and `ed25519` modules.
// This module is substantially internal-only and is only public for the
// [`Curve`] trait, which is shared by ECDH and ECDSA.
use crate::{cbb_to_buffer, parse_with_cbs, scoped, sealed, Buffer, FfiSlice};
use alloc::{fmt::Debug, vec::Vec};
use core::ptr::{null, null_mut};
/// An elliptic curve.
pub trait Curve: Debug {
#[doc(hidden)]
fn group(_: sealed::Sealed) -> Group;
/// Hash `data` using a hash function suitable for the curve. (I.e.
/// SHA-256 for P-256 and SHA-384 for P-384.)
#[doc(hidden)]
fn hash(data: &[u8]) -> Vec<u8>;
}
/// The NIST P-256 curve, also called secp256r1.
#[derive(Debug)]
pub struct P256;
impl Curve for P256 {
fn group(_: sealed::Sealed) -> Group {
Group::P256
}
fn hash(data: &[u8]) -> Vec<u8> {
crate::digest::Sha256::hash(data).to_vec()
}
}
/// The NIST P-384 curve, also called secp384r1.
#[derive(Debug)]
pub struct P384;
impl Curve for P384 {
fn group(_: sealed::Sealed) -> Group {
Group::P384
}
fn hash(data: &[u8]) -> Vec<u8> {
crate::digest::Sha384::hash(data).to_vec()
}
}
#[derive(Copy, Clone)]
#[doc(hidden)]
pub enum Group {
P256,
P384,
}
impl Group {
fn as_ffi_ptr(self) -> *const bssl_sys::EC_GROUP {
// Safety: `group` is an address-space constant. These functions
// cannot fail and no resources need to be released in the future.
match self {
Group::P256 => unsafe { bssl_sys::EC_group_p256() },
Group::P384 => unsafe { bssl_sys::EC_group_p384() },
}
}
}
/// Point is a valid, finite point on some curve.
pub(crate) struct Point {
group: *const bssl_sys::EC_GROUP,
point: *mut bssl_sys::EC_POINT,
}
impl Point {
/// Construct an uninitialized curve point. This is not public and all
/// callers must ensure that the point is initialized before being returned.
fn new(group: Group) -> Self {
let group = group.as_ffi_ptr();
// Safety: `group` is valid because it was constructed just above.
let point = unsafe { bssl_sys::EC_POINT_new(group) };
// `EC_POINT_new` only fails if out of memory, which is not a case that
// is handled short of panicking.
assert!(!point.is_null());
Self { group, point }
}
/// Construct a point by multipling the curve's base point by the given
/// scalar.
unsafe fn from_scalar(group: Group, scalar: *const bssl_sys::BIGNUM) -> Option<Self> {
let point = Self::new(group);
// Safety: the members of `point` are valid by construction. `scalar`
// is assumed to be valid.
let result = unsafe {
bssl_sys::EC_POINT_mul(
point.group,
point.point,
scalar,
/*q=*/ null(),
/*m=*/ null(),
/*ctx=*/ null_mut(),
)
};
if result != 1 {
return None;
}
if 1 == unsafe { bssl_sys::EC_POINT_is_at_infinity(point.group, point.point) } {
return None;
}
Some(point)
}
/// Duplicate the given finite point.
unsafe fn clone_from_ptr(
group: *const bssl_sys::EC_GROUP,
point: *const bssl_sys::EC_POINT,
) -> Point {
assert_eq!(0, unsafe {
bssl_sys::EC_POINT_is_at_infinity(group, point)
});
// Safety: we assume that the caller is passing valid pointers
let new_point = unsafe { bssl_sys::EC_POINT_dup(point, group) };
// `EC_POINT_dup` only fails if out of memory, which is not a case that
// is handled short of panicking.
assert!(!new_point.is_null());
Self {
group,
point: new_point,
}
}
pub fn as_ffi_ptr(&self) -> *const bssl_sys::EC_POINT {
self.point
}
/// Create a new point from an uncompressed X9.62 representation.
///
/// (X9.62 is the standard representation of an elliptic-curve point that
/// starts with an 0x04 byte.)
pub fn from_x962_uncompressed(group: Group, x962: &[u8]) -> Option<Self> {
const UNCOMPRESSED: u8 =
bssl_sys::point_conversion_form_t::POINT_CONVERSION_UNCOMPRESSED as u8;
if x962.first()? != &UNCOMPRESSED {
return None;
}
let point = Self::new(group);
// Safety: `point` is valid by construction. `x962` is a valid memory
// buffer.
let result = unsafe {
bssl_sys::EC_POINT_oct2point(
point.group,
point.point,
x962.as_ffi_ptr(),
x962.len(),
/*bn_ctx=*/ null_mut(),
)
};
if result == 1 {
// X9.62 format cannot represent the point at infinity, so this
// should be moot, but `Point` must never contain infinity.
assert_eq!(0, unsafe {
bssl_sys::EC_POINT_is_at_infinity(point.group, point.point)
});
Some(point)
} else {
None
}
}
pub fn to_x962_uncompressed(&self) -> Buffer {
// Safety: arguments are valid, `EC_KEY` ensures that the the group is
// correct for the point, and a `Point` is always finite.
unsafe { to_x962_uncompressed(self.group, self.point) }
}
pub fn from_der_subject_public_key_info(group: Group, spki: &[u8]) -> Option<Self> {
let mut pkey = scoped::EvpPkey::from_ptr(parse_with_cbs(
spki,
// Safety: if called, `pkey` is the non-null result of `EVP_parse_public_key`.
|pkey| unsafe { bssl_sys::EVP_PKEY_free(pkey) },
// Safety: `cbs` is a valid pointer in this context.
|cbs| unsafe { bssl_sys::EVP_parse_public_key(cbs) },
)?);
let ec_key = unsafe { bssl_sys::EVP_PKEY_get0_EC_KEY(pkey.as_ffi_ptr()) };
if ec_key.is_null() {
// Not an ECC key.
return None;
}
let parsed_group = unsafe { bssl_sys::EC_KEY_get0_group(ec_key) };
if parsed_group != group.as_ffi_ptr() {
// ECC key for a different curve.
return None;
}
let point = unsafe { bssl_sys::EC_KEY_get0_public_key(ec_key) };
if point.is_null() {
return None;
}
// Safety: `ec_key` is still owned by `pkey` and doesn't need to be freed.
Some(unsafe { Self::clone_from_ptr(parsed_group, point) })
}
/// Calls `func` with an `EC_KEY` that contains a copy of this point.
pub fn with_point_as_ec_key<F, T>(&self, func: F) -> T
where
F: FnOnce(*mut bssl_sys::EC_KEY) -> T,
{
let mut ec_key = scoped::EcKey::new();
// Safety: `self.group` is always valid by construction and this doesn't
// pass ownership.
assert_eq!(1, unsafe {
bssl_sys::EC_KEY_set_group(ec_key.as_ffi_ptr(), self.group)
});
// Safety: `self.point` is always valid by construction and this doesn't
// pass ownership.
assert_eq!(1, unsafe {
bssl_sys::EC_KEY_set_public_key(ec_key.as_ffi_ptr(), self.point)
});
func(ec_key.as_ffi_ptr())
}
pub fn to_der_subject_public_key_info(&self) -> Buffer {
// Safety: `ec_key` is a valid pointer in this context.
self.with_point_as_ec_key(|ec_key| unsafe { to_der_subject_public_key_info(ec_key) })
}
}
// Safety:
//
// An `EC_POINT` can be used concurrently from multiple threads so long as no
// mutating operations are performed. The mutating operations used here are
// `EC_POINT_mul` and `EC_POINT_oct2point` (which can be observed by setting
// `point` to be `*const` in the struct and seeing what errors trigger.
//
// Both those operations are done internally, however, before a `Point` is
// returned. So, after construction, callers cannot mutate the `EC_POINT`.
unsafe impl Sync for Point {}
unsafe impl Send for Point {}
impl Drop for Point {
fn drop(&mut self) {
// Safety: `self.point` must be valid because only valid `Point`s can
// be constructed. `self.group` does not need to be freed.
unsafe { bssl_sys::EC_POINT_free(self.point) }
}
}
/// Key holds both a public and private key. While BoringSSL allows an `EC_KEY`
/// to also be a) empty, b) holding only a private scalar, or c) holding only
// a public key, those cases are never exposed as a `Key`.
pub(crate) struct Key(*mut bssl_sys::EC_KEY);
impl Key {
/// Construct an uninitialized key. This is not public and all
/// callers must ensure that the key is initialized before being returned.
fn new(group: Group) -> Self {
let key = unsafe { bssl_sys::EC_KEY_new() };
// `EC_KEY_new` only fails if out of memory, which is not a case that
// is handled short of panicking.
assert!(!key.is_null());
// Setting the group on a fresh `EC_KEY` never fails.
assert_eq!(1, unsafe {
bssl_sys::EC_KEY_set_group(key, group.as_ffi_ptr())
});
Self(key)
}
pub fn as_ffi_ptr(&self) -> *const bssl_sys::EC_KEY {
self.0
}
/// Generate a random private key.
pub fn generate(group: Group) -> Self {
let key = Self::new(group);
// Generation only fails if out of memory, which is only handled by
// panicking.
assert_eq!(1, unsafe { bssl_sys::EC_KEY_generate_key(key.0) });
// `EC_KEY_generate_key` is documented as also setting the public key.
key
}
/// Construct a private key from a big-endian representation of the private
/// scalar. The scalar must be zero padded to the correct length for the
/// curve.
pub fn from_big_endian(group: Group, scalar: &[u8]) -> Option<Self> {
let key = Self::new(group);
// Safety: `key.0` is always valid by construction.
let result = unsafe { bssl_sys::EC_KEY_oct2priv(key.0, scalar.as_ffi_ptr(), scalar.len()) };
if result != 1 {
return None;
}
// BoringSSL allows an `EC_KEY` to have a private scalar without a
// public point, but `Key` is never exposed in that state.
// Safety: `key.0` is valid by construction. The returned value is
// still owned the `EC_KEY`.
let scalar = unsafe { bssl_sys::EC_KEY_get0_private_key(key.0) };
assert!(!scalar.is_null());
// Safety: `scalar` is a valid pointer.
let point = unsafe { Point::from_scalar(group, scalar)? };
// Safety: `key.0` is valid by construction, as is `point.point`. The
// point is copied into the `EC_KEY` so ownership isn't being moved.
let result = unsafe { bssl_sys::EC_KEY_set_public_key(key.0, point.point) };
// Setting the public key should only fail if out of memory, which this
// crate doesn't handle, or if the groups don't match, which is
// impossible.
assert_eq!(result, 1);
Some(key)
}
pub fn to_big_endian(&self) -> Buffer {
let mut ptr: *mut u8 = null_mut();
// Safety: `self.0` is valid by construction. If this returns non-zero
// then ptr holds ownership of a buffer.
let len = unsafe { bssl_sys::EC_KEY_priv2buf(self.0, &mut ptr) };
assert!(len != 0);
Buffer { ptr, len }
}
/// Parses an ECPrivateKey structure (from RFC 5915).
pub fn from_der_ec_private_key(group: Group, der: &[u8]) -> Option<Self> {
let key = parse_with_cbs(
der,
// Safety: in this context, `key` is the non-null result of
// `EC_KEY_parse_private_key`.
|key| unsafe { bssl_sys::EC_KEY_free(key) },
// Safety: `cbs` is valid per `parse_with_cbs` and `group` always
// returns a valid pointer.
|cbs| unsafe { bssl_sys::EC_KEY_parse_private_key(cbs, group.as_ffi_ptr()) },
)?;
Some(Self(key))
}
/// Serializes this private key as an ECPrivateKey structure from RFC 5915.
pub fn to_der_ec_private_key(&self) -> Buffer {
cbb_to_buffer(64, |cbb| unsafe {
// Safety: the `EC_KEY` is always valid so `EC_KEY_marshal_private_key`
// should only fail if out of memory, which this crate doesn't handle.
assert_eq!(
1,
bssl_sys::EC_KEY_marshal_private_key(
cbb,
self.0,
bssl_sys::EC_PKEY_NO_PARAMETERS as u32
)
);
})
}
/// Parses a PrivateKeyInfo structure (from RFC 5208).
pub fn from_der_private_key_info(group: Group, der: &[u8]) -> Option<Self> {
let mut pkey = scoped::EvpPkey::from_ptr(parse_with_cbs(
der,
// Safety: in this context, `pkey` is the non-null result of
// `EVP_parse_private_key`.
|pkey| unsafe { bssl_sys::EVP_PKEY_free(pkey) },
// Safety: `cbs` is valid per `parse_with_cbs`.
|cbs| unsafe { bssl_sys::EVP_parse_private_key(cbs) },
)?);
let ec_key = unsafe { bssl_sys::EVP_PKEY_get1_EC_KEY(pkey.as_ffi_ptr()) };
if ec_key.is_null() {
return None;
}
// Safety: `ec_key` is now owned by this function.
let parsed_group = unsafe { bssl_sys::EC_KEY_get0_group(ec_key) };
if parsed_group == group.as_ffi_ptr() {
// Safety: parsing an EC_KEY always set the public key. It should
// be impossible for the public key to be infinity, but double-check.
let is_infinite = unsafe {
bssl_sys::EC_POINT_is_at_infinity(
bssl_sys::EC_KEY_get0_group(ec_key),
bssl_sys::EC_KEY_get0_public_key(ec_key),
)
};
if is_infinite == 0 {
// Safety: `EVP_PKEY_get1_EC_KEY` returned ownership, which we can move
// into the returned object.
return Some(Self(ec_key));
}
}
unsafe { bssl_sys::EC_KEY_free(ec_key) };
None
}
/// Serializes this private key as a PrivateKeyInfo structure from RFC 5208.
pub fn to_der_private_key_info(&self) -> Buffer {
let mut pkey = scoped::EvpPkey::new();
// Safety: `pkey` was just allocated above; the `EC_KEY` is valid by
// construction. This call takes a reference to the `EC_KEY` and so
// hasn't stolen ownership from `self`.
assert_eq!(1, unsafe {
bssl_sys::EVP_PKEY_set1_EC_KEY(pkey.as_ffi_ptr(), self.0)
});
cbb_to_buffer(64, |cbb| unsafe {
// `EVP_marshal_private_key` should always return one because this
// key is valid by construction.
assert_eq!(1, bssl_sys::EVP_marshal_private_key(cbb, pkey.as_ffi_ptr()));
})
}
pub fn to_point(&self) -> Point {
// Safety: `self.0` is valid by construction.
let group = unsafe { bssl_sys::EC_KEY_get0_group(self.0) };
let point = unsafe { bssl_sys::EC_KEY_get0_public_key(self.0) };
// A `Key` is never constructed without a public key.
assert!(!point.is_null());
// Safety: pointers are valid and `clone_from_ptr` doesn't take
// ownership.
unsafe { Point::clone_from_ptr(group, point) }
}
pub fn to_x962_uncompressed(&self) -> Buffer {
// Safety: `self.0` is valid by construction.
let group = unsafe { bssl_sys::EC_KEY_get0_group(self.0) };
let point = unsafe { bssl_sys::EC_KEY_get0_public_key(self.0) };
// Safety: arguments are valid, `EC_KEY` ensures that the the group is
// correct for the point, and a `Key` always holds a finite public point.
unsafe { to_x962_uncompressed(group, point) }
}
pub fn to_der_subject_public_key_info(&self) -> Buffer {
// Safety: `self.0` is always valid by construction.
unsafe { to_der_subject_public_key_info(self.0) }
}
}
// Safety:
//
// An `EC_KEY` is safe to use from multiple threads so long as no mutating
// operations are performed. (Reference count changes don't count as mutating.)
// The mutating operations used here are:
// * EC_KEY_generate_key
// * EC_KEY_oct2priv
// * EC_KEY_set_public_key
// But those are all done internally, before a `Key` is returned. So, once
// constructed, callers cannot mutate the `EC_KEY`.
unsafe impl Sync for Key {}
unsafe impl Send for Key {}
impl Drop for Key {
fn drop(&mut self) {
// Safety: `self.0` must be valid because only valid `Key`s can
// be constructed.
unsafe { bssl_sys::EC_KEY_free(self.0) }
}
}
/// Serialize a finite point to uncompressed X9.62 format.
///
/// Callers must ensure that the arguments are valid, that the point has the
/// specified group, and that the point is finite.
unsafe fn to_x962_uncompressed(
group: *const bssl_sys::EC_GROUP,
point: *const bssl_sys::EC_POINT,
) -> Buffer {
cbb_to_buffer(65, |cbb| unsafe {
// Safety: the caller must ensure that the arguments are valid.
let result = bssl_sys::EC_POINT_point2cbb(
cbb,
group,
point,
bssl_sys::point_conversion_form_t::POINT_CONVERSION_UNCOMPRESSED,
/*bn_ctx=*/ null_mut(),
);
// The public key is always finite, so `EC_POINT_point2cbb` only fails
// if out of memory, which isn't handled by this crate.
assert_eq!(result, 1);
})
}
unsafe fn to_der_subject_public_key_info(ec_key: *mut bssl_sys::EC_KEY) -> Buffer {
let mut pkey = scoped::EvpPkey::new();
// Safety: this takes a reference to `ec_key` and so doesn't steal ownership.
assert_eq!(1, unsafe {
bssl_sys::EVP_PKEY_set1_EC_KEY(pkey.as_ffi_ptr(), ec_key)
});
cbb_to_buffer(65, |cbb| unsafe {
// The arguments are valid so this will only fail if out of memory,
// which this crate doesn't handle.
assert_eq!(1, bssl_sys::EVP_marshal_public_key(cbb, pkey.as_ffi_ptr()));
})
}
#[cfg(test)]
mod test {
use super::*;
fn test_point_format<Serialize, Parse>(serialize_func: Serialize, parse_func: Parse)
where
Serialize: FnOnce(&Point) -> Buffer,
Parse: Fn(&[u8]) -> Option<Point>,
{
let key = Key::generate(Group::P256);
let point = key.to_point();
let mut vec = serialize_func(&point).as_ref().to_vec();
let point2 = parse_func(vec.as_slice()).unwrap();
assert_eq!(
point.to_x962_uncompressed().as_ref(),
point2.to_x962_uncompressed().as_ref()
);
assert!(parse_func(&vec.as_slice()[0..16]).is_none());
vec[10] ^= 1;
assert!(parse_func(vec.as_slice()).is_none());
vec[10] ^= 1;
assert!(parse_func(b"").is_none());
}
#[test]
fn x962() {
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";
assert!(Point::from_x962_uncompressed(Group::P256, x962).is_some());
test_point_format(
|point| point.to_x962_uncompressed(),
|buf| Point::from_x962_uncompressed(Group::P256, buf),
);
}
#[test]
fn spki() {
test_point_format(
|point| point.to_der_subject_public_key_info(),
|buf| Point::from_der_subject_public_key_info(Group::P256, buf),
);
}
fn test_key_format<Serialize, Parse>(serialize_func: Serialize, parse_func: Parse)
where
Serialize: FnOnce(&Key) -> Buffer,
Parse: Fn(&[u8]) -> Option<Key>,
{
let key = Key::generate(Group::P256);
let vec = serialize_func(&key).as_ref().to_vec();
let key2 = parse_func(vec.as_slice()).unwrap();
assert_eq!(
key.to_x962_uncompressed().as_ref(),
key2.to_x962_uncompressed().as_ref()
);
assert!(parse_func(&vec.as_slice()[0..16]).is_none());
assert!(parse_func(b"").is_none());
}
#[test]
fn der_ec_private_key() {
test_key_format(
|key| key.to_der_ec_private_key(),
|buf| Key::from_der_ec_private_key(Group::P256, buf),
);
}
#[test]
fn der_private_key_info() {
test_key_format(
|key| key.to_der_private_key_info(),
|buf| Key::from_der_private_key_info(Group::P256, buf),
);
}
#[test]
fn big_endian() {
test_key_format(
|key| key.to_big_endian(),
|buf| Key::from_big_endian(Group::P256, buf),
);
}
}