Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RSA Key Generation Support #315

Merged
merged 7 commits into from
Jan 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions aws-lc-rs/src/bn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,20 @@ impl TryFrom<&[u8]> for LcPtr<BIGNUM> {
}
}

impl TryFrom<u64> for LcPtr<BIGNUM> {
type Error = ();

fn try_from(value: u64) -> Result<Self, Self::Error> {
unsafe {
let bn = LcPtr::new(BN_new())?;
if 1 != BN_set_u64(*bn, value) {
return Err(());
}
Ok(bn)
}
}
}

impl TryFrom<&[u8]> for DetachableLcPtr<BIGNUM> {
type Error = ();

Expand Down
103 changes: 93 additions & 10 deletions aws-lc-rs/src/cbb.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,112 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR ISC

use aws_lc::{CBB_cleanup, CBB_init, CBB};
use crate::buffer::Buffer;
use crate::error::Unspecified;
use crate::ptr::LcPtr;
use aws_lc::{CBB_cleanup, CBB_finish, CBB_init, CBB_init_fixed, CBB};
use std::marker::PhantomData;
use std::mem::MaybeUninit;
use std::ptr::null_mut;

pub(crate) struct LcCBB(CBB);
pub(crate) struct LcCBB<'a>(CBB, PhantomData<&'a CBB>);

impl LcCBB {
impl LcCBB<'static> {
#[allow(dead_code)]
pub(crate) fn new(initial_capacity: usize) -> LcCBB<'static> {
let mut cbb = MaybeUninit::<CBB>::uninit();
let cbb = unsafe {
CBB_init(cbb.as_mut_ptr(), initial_capacity);
cbb.assume_init()
};
Self(cbb, PhantomData)
}

#[allow(dead_code)]
pub(crate) fn into_buffer<'a, T>(mut self) -> Result<Buffer<'a, T>, Unspecified> {
justsmth marked this conversation as resolved.
Show resolved Hide resolved
let mut out_data = null_mut::<u8>();
let mut out_len: usize = 0;

if 1 != unsafe { CBB_finish(self.as_mut_ptr(), &mut out_data, &mut out_len) } {
return Err(Unspecified);
};

let out_data = LcPtr::new(out_data)?;

// TODO: Need a type to just hold the owned pointer from CBB rather then copying
Ok(Buffer::take_from_slice(unsafe {
out_data.as_slice_mut(out_len)
}))
}
}

impl<'a> LcCBB<'a> {
pub(crate) fn new_fixed<const N: usize>(buffer: &'a mut [u8; N]) -> LcCBB<'a> {
let mut cbb = MaybeUninit::<CBB>::uninit();
let cbb = unsafe {
CBB_init_fixed(cbb.as_mut_ptr(), buffer.as_mut_ptr(), N);
cbb.assume_init()
};
Self(cbb, PhantomData)
}

pub(crate) fn finish(mut self) -> Result<usize, Unspecified> {
let mut pkcs8_bytes_ptr = null_mut::<u8>();
let mut out_len: usize = 0;
if 1 != unsafe { CBB_finish(self.as_mut_ptr(), &mut pkcs8_bytes_ptr, &mut out_len) } {
return Err(Unspecified);
}
Ok(out_len)
}
}
impl LcCBB<'_> {
pub(crate) fn as_mut_ptr(&mut self) -> *mut CBB {
&mut self.0
}
}

impl Drop for LcCBB {
impl Drop for LcCBB<'_> {
fn drop(&mut self) {
unsafe {
CBB_cleanup(&mut self.0);
}
}
}

#[inline]
#[allow(non_snake_case)]
pub(crate) unsafe fn build_CBB(initial_capacity: usize) -> LcCBB {
let mut cbb = MaybeUninit::<CBB>::uninit();
CBB_init(cbb.as_mut_ptr(), initial_capacity);
LcCBB(cbb.assume_init())
#[cfg(test)]
mod tests {
use super::LcCBB;
use aws_lc::CBB_add_asn1_bool;

#[test]
fn dynamic_buffer() {
let mut cbb = LcCBB::new(4);
assert_eq!(1, unsafe { CBB_add_asn1_bool(cbb.as_mut_ptr(), 1) });
let buffer = cbb.into_buffer::<'_, ()>().expect("be copied to buffer");
assert_eq!(buffer.as_ref(), &[1, 1, 255]);
}

#[test]
fn dynamic_buffer_grows() {
let mut cbb = LcCBB::new(1);
assert_eq!(1, unsafe { CBB_add_asn1_bool(cbb.as_mut_ptr(), 1) });
let buffer = cbb.into_buffer::<'_, ()>().expect("be copied to buffer");
assert_eq!(buffer.as_ref(), &[1, 1, 255]);
}

#[test]
fn fixed_buffer() {
let mut buffer = [0u8; 4];
let mut cbb = LcCBB::new_fixed(&mut buffer);
assert_eq!(1, unsafe { CBB_add_asn1_bool(cbb.as_mut_ptr(), 1) });
let out_len = cbb.finish().expect("cbb finishable");
assert_eq!(&buffer[..out_len], &[1, 1, 255]);
}

#[test]
fn fixed_buffer_no_growth() {
let mut buffer = [0u8; 1];
let mut cbb = LcCBB::new_fixed(&mut buffer);
assert_ne!(1, unsafe { CBB_add_asn1_bool(cbb.as_mut_ptr(), 1) });
}
}
9 changes: 9 additions & 0 deletions aws-lc-rs/src/encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ use crate::encoding::types::{
EcPublicKeyX509DerType,
};

use self::types::Pkcs8V1DerType;

mod types {
pub struct EcPrivateKeyBinType {
_priv: (),
Expand All @@ -25,6 +27,10 @@ mod types {
pub struct Curve25519SeedBufferType {
_priv: (),
}

pub struct Pkcs8V1DerType {
_priv: (),
}
}

/// Trait for types that can be serialized into a DER format.
Expand Down Expand Up @@ -56,3 +62,6 @@ pub type EcPublicKeyX509Der<'a> = Buffer<'a, EcPublicKeyX509DerType>;

/// Elliptic curve private key data encoded as a big-endian fixed-length integer.
pub type Curve25519SeedBin<'a> = Buffer<'a, Curve25519SeedBufferType>;

/// A PKCS#8 v1 (RFC 5208) DER encoded structure.
pub type Pkcs8V1Der<'a> = Buffer<'a, Pkcs8V1DerType>;
42 changes: 16 additions & 26 deletions aws-lc-rs/src/evp_pkey.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR ISC

use crate::cbb::LcCBB;
use crate::cbs;
use crate::ec::PKCS8_DOCUMENT_MAX_LEN;
use crate::error::{KeyRejected, Unspecified};
use crate::pkcs8::{Document, Version};
use crate::ptr::LcPtr;
use crate::{cbb, cbs};
use aws_lc::{
CBB_finish, EVP_PKEY_bits, EVP_PKEY_get1_EC_KEY, EVP_PKEY_get1_RSA, EVP_PKEY_id,
EVP_marshal_private_key, EVP_marshal_private_key_v2, EVP_parse_private_key, EC_KEY, EVP_PKEY,
RSA,
EVP_PKEY_bits, EVP_PKEY_get1_EC_KEY, EVP_PKEY_get1_RSA, EVP_PKEY_id, EVP_marshal_private_key,
EVP_marshal_private_key_v2, EVP_parse_private_key, EC_KEY, EVP_PKEY, RSA,
};
use std::mem::MaybeUninit;
use std::os::raw::c_int;
use std::ptr::null_mut;

impl TryFrom<&[u8]> for LcPtr<EVP_PKEY> {
type Error = KeyRejected;
Expand Down Expand Up @@ -85,38 +83,30 @@ impl LcPtr<EVP_PKEY> {
}

pub(crate) fn marshall_private_key(&self, version: Version) -> Result<Document, Unspecified> {
unsafe {
let mut cbb = cbb::build_CBB(PKCS8_DOCUMENT_MAX_LEN);
let mut buffer = vec![0u8; PKCS8_DOCUMENT_MAX_LEN];

let out_len = {
let mut cbb = LcCBB::new_fixed(<&mut [u8; PKCS8_DOCUMENT_MAX_LEN]>::try_from(
buffer.as_mut_slice(),
)?);

match version {
Version::V1 => {
if 1 != EVP_marshal_private_key(cbb.as_mut_ptr(), **self) {
if 1 != unsafe { EVP_marshal_private_key(cbb.as_mut_ptr(), **self) } {
return Err(Unspecified);
}
}
Version::V2 => {
if 1 != EVP_marshal_private_key_v2(cbb.as_mut_ptr(), **self) {
if 1 != unsafe { EVP_marshal_private_key_v2(cbb.as_mut_ptr(), **self) } {
return Err(Unspecified);
}
}
}
cbb.finish()?
};

let mut pkcs8_bytes_ptr = null_mut::<u8>();
let mut out_len = MaybeUninit::<usize>::uninit();
if 1 != CBB_finish(cbb.as_mut_ptr(), &mut pkcs8_bytes_ptr, out_len.as_mut_ptr()) {
return Err(Unspecified);
}
let pkcs8_bytes_ptr = LcPtr::new(pkcs8_bytes_ptr)?;
let out_len = out_len.assume_init();
buffer.truncate(out_len);

let bytes_slice = pkcs8_bytes_ptr.as_slice(out_len);
let mut pkcs8_bytes = [0u8; PKCS8_DOCUMENT_MAX_LEN];
pkcs8_bytes[0..out_len].copy_from_slice(bytes_slice);

Ok(Document {
bytes: pkcs8_bytes,
len: out_len,
})
}
Ok(Document::new(buffer.into_boxed_slice()))
}
}
12 changes: 8 additions & 4 deletions aws-lc-rs/src/pkcs8.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,23 @@
//!
//! [RFC 5208]: https://tools.ietf.org/html/rfc5208.

use crate::ec;
use zeroize::Zeroize;

/// A generated PKCS#8 document.
pub struct Document {
pub(crate) bytes: [u8; ec::PKCS8_DOCUMENT_MAX_LEN],
pub(crate) len: usize,
bytes: Box<[u8]>,
}

impl Document {
pub(crate) fn new(bytes: Box<[u8]>) -> Self {
Self { bytes }
}
}

impl AsRef<[u8]> for Document {
#[inline]
fn as_ref(&self) -> &[u8] {
&self.bytes[..self.len]
&self.bytes
}
}

Expand Down
12 changes: 12 additions & 0 deletions aws-lc-rs/src/ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ impl<P: Pointer> ManagedPointer<P> {
pub unsafe fn as_slice(&self, len: usize) -> &[P::T] {
std::slice::from_raw_parts(self.pointer.as_const_ptr(), len)
}

#[allow(clippy::mut_from_ref)]
pub unsafe fn as_slice_mut(&self, len: usize) -> &mut [P::T] {
justsmth marked this conversation as resolved.
Show resolved Hide resolved
std::slice::from_raw_parts_mut(self.pointer.as_mut_ptr(), len)
}
}

impl<P: Pointer> Drop for ManagedPointer<P> {
Expand Down Expand Up @@ -160,6 +165,7 @@ pub(crate) trait Pointer {

fn free(&mut self);
fn as_const_ptr(&self) -> *const Self::T;
fn as_mut_ptr(&self) -> *mut Self::T;
justsmth marked this conversation as resolved.
Show resolved Hide resolved
}

pub(crate) trait IntoPointer<P> {
Expand Down Expand Up @@ -190,9 +196,15 @@ macro_rules! create_pointer {
}
}

#[inline]
fn as_const_ptr(&self) -> *const Self::T {
self.cast()
}

#[inline]
fn as_mut_ptr(&self) -> *mut Self::T {
*self
}
}
};
}
Expand Down
3 changes: 2 additions & 1 deletion aws-lc-rs/src/rsa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@
// naming conventions. Also the standard camelCase names are used for `KeyPair`
// components.

mod encoding;
pub(crate) mod key;
pub(crate) mod signature;

pub use self::key::{KeyPair, PublicKey, PublicKeyComponents};
pub use self::key::{KeyPair, KeySize, PublicKey, PublicKeyComponents};
#[allow(clippy::module_name_repetitions)]
pub use self::signature::RsaParameters;

Expand Down
39 changes: 39 additions & 0 deletions aws-lc-rs/src/rsa/encoding.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR ISC

/// PKCS#8 Encoding Functions
pub(in super::super) mod pkcs8 {
use crate::{
cbb::LcCBB,
error::{KeyRejected, Unspecified},
ptr::LcPtr,
};
use aws_lc::{EVP_marshal_private_key, EVP_PKEY};

// Based on a measurement of a PKCS#8 v1 document containing an RSA-8192 key with an additional 1% capacity buffer
// rounded to an even 64-bit words (4678 + 1% + padding ≈ 4728).
const PKCS8_FIXED_CAPACITY_BUFFER: usize = 4728;

pub(in super::super) fn encode_v1_der(key: &LcPtr<EVP_PKEY>) -> Result<Vec<u8>, Unspecified> {
let mut buffer = vec![0u8; PKCS8_FIXED_CAPACITY_BUFFER];
let out_len = {
let mut cbb = LcCBB::new_fixed(<&mut [u8; PKCS8_FIXED_CAPACITY_BUFFER]>::try_from(
buffer.as_mut_slice(),
)?);

if 1 != unsafe { EVP_marshal_private_key(cbb.as_mut_ptr(), *key.as_const()) } {
return Err(Unspecified);
}
cbb.finish()?
};

buffer.truncate(out_len);

Ok(buffer)
}

// Supports v1 and v2 encodings through a single API entry-point.
pub(in super::super) fn decode_der(pkcs8: &[u8]) -> Result<LcPtr<EVP_PKEY>, KeyRejected> {
LcPtr::try_from(pkcs8)
}
}
Loading
Loading