update master-with-bazel from master branch

This commit is contained in:
BoringSSL Robot
2024-01-19 18:26:22 +00:00
27 changed files with 2318 additions and 1616 deletions
+3 -2
View File
@@ -788,21 +788,22 @@ rust_bssl_sys = [
rust_bssl_crypto = [
"src/rust/bssl-crypto/src/aead.rs",
"src/rust/bssl-crypto/src/aes.rs",
"src/rust/bssl-crypto/src/bn.rs",
"src/rust/bssl-crypto/src/cipher/aes_cbc.rs",
"src/rust/bssl-crypto/src/cipher/aes_ctr.rs",
"src/rust/bssl-crypto/src/cipher/mod.rs",
"src/rust/bssl-crypto/src/digest.rs",
"src/rust/bssl-crypto/src/ec.rs",
"src/rust/bssl-crypto/src/ecdh.rs",
"src/rust/bssl-crypto/src/ecdsa.rs",
"src/rust/bssl-crypto/src/ed25519.rs",
"src/rust/bssl-crypto/src/hkdf.rs",
"src/rust/bssl-crypto/src/hmac.rs",
"src/rust/bssl-crypto/src/lib.rs",
"src/rust/bssl-crypto/src/macros.rs",
"src/rust/bssl-crypto/src/mem.rs",
"src/rust/bssl-crypto/src/pkey.rs",
"src/rust/bssl-crypto/src/rand.rs",
"src/rust/bssl-crypto/src/rsa.rs",
"src/rust/bssl-crypto/src/scoped.rs",
"src/rust/bssl-crypto/src/test_helpers.rs",
"src/rust/bssl-crypto/src/x25519.rs",
]
@@ -1155,14 +1155,15 @@ _CET_ENDBR
L$128_dec_start:
vzeroupper
vmovdqa (%rdx),%xmm0
vmovdqu 16(%rdx),%xmm15
vpor OR_MASK(%rip),%xmm15,%xmm15
movq %rdx,%rax
leaq 32(%rax),%rax
leaq 32(%rcx),%rcx
vmovdqu (%rdi,%r9,1),%xmm15
vpor OR_MASK(%rip),%xmm15,%xmm15
andq $~15,%r9
@@ -2377,14 +2378,15 @@ _CET_ENDBR
L$256_dec_start:
vzeroupper
vmovdqa (%rdx),%xmm0
vmovdqu 16(%rdx),%xmm15
vpor OR_MASK(%rip),%xmm15,%xmm15
movq %rdx,%rax
leaq 32(%rax),%rax
leaq 32(%rcx),%rcx
vmovdqu (%rdi,%r9,1),%xmm15
vpor OR_MASK(%rip),%xmm15,%xmm15
andq $~15,%r9
@@ -1165,14 +1165,15 @@ _CET_ENDBR
.L128_dec_start:
vzeroupper
vmovdqa (%rdx),%xmm0
vmovdqu 16(%rdx),%xmm15
vpor OR_MASK(%rip),%xmm15,%xmm15
movq %rdx,%rax
leaq 32(%rax),%rax
leaq 32(%rcx),%rcx
vmovdqu (%rdi,%r9,1),%xmm15
vpor OR_MASK(%rip),%xmm15,%xmm15
andq $~15,%r9
@@ -2387,14 +2388,15 @@ _CET_ENDBR
.L256_dec_start:
vzeroupper
vmovdqa (%rdx),%xmm0
vmovdqu 16(%rdx),%xmm15
vpor OR_MASK(%rip),%xmm15,%xmm15
movq %rdx,%rax
leaq 32(%rax),%rax
leaq 32(%rcx),%rcx
vmovdqu (%rdi,%r9,1),%xmm15
vpor OR_MASK(%rip),%xmm15,%xmm15
andq $~15,%r9
+3 -2
View File
@@ -2917,21 +2917,22 @@
"rust_bssl_crypto": [
"src/rust/bssl-crypto/src/aead.rs",
"src/rust/bssl-crypto/src/aes.rs",
"src/rust/bssl-crypto/src/bn.rs",
"src/rust/bssl-crypto/src/cipher/aes_cbc.rs",
"src/rust/bssl-crypto/src/cipher/aes_ctr.rs",
"src/rust/bssl-crypto/src/cipher/mod.rs",
"src/rust/bssl-crypto/src/digest.rs",
"src/rust/bssl-crypto/src/ec.rs",
"src/rust/bssl-crypto/src/ecdh.rs",
"src/rust/bssl-crypto/src/ecdsa.rs",
"src/rust/bssl-crypto/src/ed25519.rs",
"src/rust/bssl-crypto/src/hkdf.rs",
"src/rust/bssl-crypto/src/hmac.rs",
"src/rust/bssl-crypto/src/lib.rs",
"src/rust/bssl-crypto/src/macros.rs",
"src/rust/bssl-crypto/src/mem.rs",
"src/rust/bssl-crypto/src/pkey.rs",
"src/rust/bssl-crypto/src/rand.rs",
"src/rust/bssl-crypto/src/rsa.rs",
"src/rust/bssl-crypto/src/scoped.rs",
"src/rust/bssl-crypto/src/test_helpers.rs",
"src/rust/bssl-crypto/src/x25519.rs"
],
@@ -1257,14 +1257,15 @@ ___
.L${labelPrefix}_dec_start:
vzeroupper
vmovdqa ($POL), $T
# The claimed tag is provided after the current calculated tag value.
# CTRBLKs is made from it.
vmovdqu 16($POL), $CTR
vpor OR_MASK(%rip), $CTR, $CTR # CTR = [1]TAG[126...32][00..00]
movq $POL, $secureBuffer
leaq 32($secureBuffer), $secureBuffer
leaq 32($Htbl), $Htbl
# make CTRBLKs from given tag.
vmovdqu ($CT,$LEN), $CTR
vpor OR_MASK(%rip), $CTR, $CTR # CTR = [1]TAG[126...32][00..00]
andq \$~15, $LEN
# If less then 6 blocks, make singles
+30 -42
View File
@@ -126,16 +126,16 @@ extern void aesgcmsiv_htable_polyval(const uint8_t htable[16 * 8],
uint8_t in_out_poly[16]);
// aes128gcmsiv_dec decrypts |in_len| & ~15 bytes from |out| and writes them to
// |in|. (The full value of |in_len| is still used to find the authentication
// tag appended to the ciphertext, however, so must not be pre-masked.)
// |in|. |in| and |out| may be equal, but must not otherwise alias.
//
// |in| and |out| may be equal, but must not otherwise overlap.
// |in_out_calculated_tag_and_scratch|, on entry, must contain:
// 1. The current value of the calculated tag, which will be updated during
// decryption and written back to the beginning of this buffer on exit.
// 2. The claimed tag, which is needed to derive counter values.
//
// While decrypting, it updates the POLYVAL value found at the beginning of
// |in_out_calculated_tag_and_scratch| and writes the updated value back before
// return. During executation, it may use the whole of this space for other
// purposes. In order to decrypt and update the POLYVAL value, it uses the
// expanded key from |key| and the table of powers in |htable|.
// While decrypting, the whole of |in_out_calculated_tag_and_scratch| may be
// used for other purposes. In order to decrypt and update the POLYVAL value, it
// uses the expanded key from |key| and the table of powers in |htable|.
extern void aes128gcmsiv_dec(const uint8_t *in, uint8_t *out,
uint8_t in_out_calculated_tag_and_scratch[16 * 8],
const uint8_t htable[16 * 6],
@@ -393,14 +393,10 @@ static int aead_aes_gcm_siv_asm_seal_scatter(
return 1;
}
// TODO(martinkr): Add aead_aes_gcm_siv_asm_open_gather. N.B. aes128gcmsiv_dec
// expects ciphertext and tag in a contiguous buffer.
static int aead_aes_gcm_siv_asm_open(const EVP_AEAD_CTX *ctx, uint8_t *out,
size_t *out_len, size_t max_out_len,
const uint8_t *nonce, size_t nonce_len,
const uint8_t *in, size_t in_len,
const uint8_t *ad, size_t ad_len) {
static int aead_aes_gcm_siv_asm_open_gather(
const EVP_AEAD_CTX *ctx, uint8_t *out, const uint8_t *nonce,
size_t nonce_len, const uint8_t *in, size_t in_len, const uint8_t *in_tag,
size_t in_tag_len, const uint8_t *ad, size_t ad_len) {
const uint64_t ad_len_64 = ad_len;
if (ad_len_64 >= (UINT64_C(1) << 61)) {
OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE);
@@ -408,8 +404,8 @@ static int aead_aes_gcm_siv_asm_open(const EVP_AEAD_CTX *ctx, uint8_t *out,
}
const uint64_t in_len_64 = in_len;
if (in_len < EVP_AEAD_AES_GCM_SIV_TAG_LEN ||
in_len_64 > (UINT64_C(1) << 36) + AES_BLOCK_SIZE) {
if (in_len_64 > UINT64_C(1) << 36 ||
in_tag_len != EVP_AEAD_AES_GCM_SIV_TAG_LEN) {
OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_DECRYPT);
return 0;
}
@@ -420,13 +416,6 @@ static int aead_aes_gcm_siv_asm_open(const EVP_AEAD_CTX *ctx, uint8_t *out,
}
const struct aead_aes_gcm_siv_asm_ctx *gcm_siv_ctx = asm_ctx_from_ctx(ctx);
const size_t plaintext_len = in_len - EVP_AEAD_AES_GCM_SIV_TAG_LEN;
const uint8_t *const given_tag = in + plaintext_len;
if (max_out_len < plaintext_len) {
OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BUFFER_TOO_SMALL);
return 0;
}
alignas(16) uint64_t record_auth_key[2];
alignas(16) uint64_t record_enc_key[4];
@@ -459,27 +448,27 @@ static int aead_aes_gcm_siv_asm_open(const EVP_AEAD_CTX *ctx, uint8_t *out,
alignas(16) uint8_t htable[16 * 6];
aesgcmsiv_htable6_init(htable, (const uint8_t *)record_auth_key);
// aes[128|256]gcmsiv_dec needs access to the claimed tag. So it's put into
// its scratch space.
memcpy(calculated_tag + 16, in_tag, EVP_AEAD_AES_GCM_SIV_TAG_LEN);
if (gcm_siv_ctx->is_128_bit) {
aes128gcmsiv_dec(in, out, calculated_tag, htable, &expanded_key,
plaintext_len);
aes128gcmsiv_dec(in, out, calculated_tag, htable, &expanded_key, in_len);
} else {
aes256gcmsiv_dec(in, out, calculated_tag, htable, &expanded_key,
plaintext_len);
aes256gcmsiv_dec(in, out, calculated_tag, htable, &expanded_key, in_len);
}
if (plaintext_len & 15) {
if (in_len & 15) {
aead_aes_gcm_siv_asm_crypt_last_block(gcm_siv_ctx->is_128_bit, out, in,
plaintext_len, given_tag,
&expanded_key);
in_len, in_tag, &expanded_key);
OPENSSL_memset(scratch, 0, sizeof(scratch));
OPENSSL_memcpy(scratch, out + (plaintext_len & ~15), plaintext_len & 15);
OPENSSL_memcpy(scratch, out + (in_len & ~15), in_len & 15);
aesgcmsiv_polyval_horner(calculated_tag, (const uint8_t *)record_auth_key,
scratch, 1);
}
uint8_t length_block[16];
CRYPTO_store_u64_le(length_block, ad_len * 8);
CRYPTO_store_u64_le(length_block + 8, plaintext_len * 8);
CRYPTO_store_u64_le(length_block + 8, in_len * 8);
aesgcmsiv_polyval_horner(calculated_tag, (const uint8_t *)record_auth_key,
length_block, 1);
@@ -495,13 +484,12 @@ static int aead_aes_gcm_siv_asm_open(const EVP_AEAD_CTX *ctx, uint8_t *out,
aes256gcmsiv_ecb_enc_block(calculated_tag, calculated_tag, &expanded_key);
}
if (CRYPTO_memcmp(calculated_tag, given_tag, EVP_AEAD_AES_GCM_SIV_TAG_LEN) !=
if (CRYPTO_memcmp(calculated_tag, in_tag, EVP_AEAD_AES_GCM_SIV_TAG_LEN) !=
0) {
OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_DECRYPT);
return 0;
}
*out_len = in_len - EVP_AEAD_AES_GCM_SIV_TAG_LEN;
return 1;
}
@@ -515,9 +503,9 @@ static const EVP_AEAD aead_aes_128_gcm_siv_asm = {
aead_aes_gcm_siv_asm_init,
NULL /* init_with_direction */,
aead_aes_gcm_siv_asm_cleanup,
aead_aes_gcm_siv_asm_open,
NULL /* open */,
aead_aes_gcm_siv_asm_seal_scatter,
NULL /* open_gather */,
aead_aes_gcm_siv_asm_open_gather,
NULL /* get_iv */,
NULL /* tag_len */,
};
@@ -532,9 +520,9 @@ static const EVP_AEAD aead_aes_256_gcm_siv_asm = {
aead_aes_gcm_siv_asm_init,
NULL /* init_with_direction */,
aead_aes_gcm_siv_asm_cleanup,
aead_aes_gcm_siv_asm_open,
NULL /* open */,
aead_aes_gcm_siv_asm_seal_scatter,
NULL /* open_gather */,
aead_aes_gcm_siv_asm_open_gather,
NULL /* get_iv */,
NULL /* tag_len */,
};
@@ -647,8 +635,8 @@ static void gcm_siv_polyval(
}
uint8_t length_block[16];
CRYPTO_store_u64_le(length_block, ad_len * 8);
CRYPTO_store_u64_le(length_block + 8, in_len * 8);
CRYPTO_store_u64_le(length_block, ((uint64_t) ad_len) * 8);
CRYPTO_store_u64_le(length_block + 8, ((uint64_t) in_len) * 8);
CRYPTO_POLYVAL_update_blocks(&polyval_ctx, length_block,
sizeof(length_block));
+2 -2
View File
@@ -1,9 +1,9 @@
[package]
name = "bssl-crypto"
version = "0.1.0"
version = "0.2.0"
edition = "2021"
publish = false
license = "MIT"
license = "ISC"
[dependencies]
bssl-sys = {path = "../bssl-sys"}
+2 -2
View File
@@ -76,7 +76,7 @@ unlicensed = "deny"
# See https://spdx.org/licenses/ for list of possible licenses
# [possible values: any SPDX 3.11 short identifier (+ optional exception)].
allow = [
"MIT",
"ISC",
]
# List of explicitly disallowed licenses
# See https://spdx.org/licenses/ for list of possible licenses
@@ -164,7 +164,7 @@ highlight = "all"
# encouraged not to add dependencies here.
allow = [
# bssl-crypto should be allowed, version appropriately.
{ name = "bssl-crypto", version = "=0.1.0" },
{ name = "bssl-crypto", version = "=0.2.0" },
# bssl-sys should be allowed, version appropriately.
{ name = "bssl-sys", version = "=0.1.0" },
]
+421 -347
View File
@@ -13,242 +13,268 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
use crate::{CSlice, CSliceMut};
use alloc::vec::Vec;
use bssl_sys::{EVP_AEAD, EVP_AEAD_CTX};
//! Authenticated Encryption with Additional Data.
//!
//! AEAD couples confidentiality and integrity in a single primitive. AEAD
//! algorithms take a key and then can seal and open individual messages. Each
//! message has a unique, per-message nonce and, optionally, additional data
//! which is authenticated but not included in the ciphertext.
//!
//! No two distinct plaintexts must ever be sealed using the same (key, nonce)
//! pair. It is up to the user of these algorithms to ensure this. For example,
//! when encrypting a stream of messages (e.g. over a TCP socket) a message
//! counter can provide distinct nonces as long as the key is randomly generated
//! for the specific connection and is distinct in each direction.
//!
//! To implement that example:
//!
//! ```
//! use bssl_crypto::aead::{Aead, Aes256Gcm};
//!
//! let key = bssl_crypto::rand_array();
//! let aead = Aes256Gcm::new(&key);
//!
//! let mut message_counter: u64 = 0;
//! let mut nonce = bssl_crypto::rand_array();
//! nonce[4..].copy_from_slice(message_counter.to_be_bytes().as_slice());
//! message_counter += 1;
//! let plaintext = b"message";
//! let ciphertext = aead.seal(&nonce, plaintext, b"");
//!
//! let decrypted = aead.open(&nonce, ciphertext.as_slice(), b"");
//! assert_eq!(plaintext, decrypted.unwrap().as_slice());
//! ```
/// Error returned in the event of an unsuccessful AEAD operation.
use crate::{with_output_array, with_output_vec, with_output_vec_fallible, FfiMutSlice, FfiSlice};
use alloc::vec::Vec;
/// The error type returned when a fallible, in-place operation fails.
#[derive(Debug)]
pub struct AeadError;
pub struct InvalidCiphertext;
/// Authenticated Encryption with Associated Data (AEAD) algorithm trait.
pub trait Aead {
/// The size of the auth tag for the given AEAD implementation. This is the amount of bytes
/// appended to the data when it is encrypted.
const TAG_SIZE: usize;
/// The type of tags produced by this AEAD. Generally a u8 array of fixed
/// length.
type Tag: AsRef<[u8]>;
/// The byte array nonce type which specifies the size of the nonce used in the aes operations.
/// The type of nonces used by this AEAD. Generally a u8 array of fixed
/// length.
type Nonce: AsRef<[u8]>;
/// Encrypt the given buffer containing a plaintext message. On success returns the encrypted
/// `msg` and appended auth tag, which will result in a Vec which is `Self::TAG_SIZE` bytes
/// greater than the initial message.
fn encrypt(&self, msg: &[u8], aad: &[u8], nonce: &Self::Nonce) -> Result<Vec<u8>, AeadError>;
/// Encrypt and authenticate `plaintext`, and authenticate `ad`, returning
/// the result as a freshly allocated [`Vec`]. The `nonce` must never
/// be used in any sealing operation with the same key, ever again.
fn seal(&self, nonce: &Self::Nonce, plaintext: &[u8], ad: &[u8]) -> Vec<u8>;
/// Decrypt the message, returning the decrypted plaintext or an error in the event the
/// provided authentication tag does not match the given ciphertext. On success the returned
/// Vec will only contain the plaintext and so will be `Self::TAG_SIZE` bytes less than the
/// initial message.
fn decrypt(&self, msg: &[u8], aad: &[u8], nonce: &Self::Nonce) -> Result<Vec<u8>, AeadError>;
/// Encrypt and authenticate `plaintext`, and authenticate `ad`, writing
/// the ciphertext over `plaintext` and additionally returning the calculated
/// tag, which is usually appended to the ciphertext. The `nonce` must never
/// be used in any sealing operation with the same key, ever again.
fn seal_in_place(&self, nonce: &Self::Nonce, plaintext: &mut [u8], ad: &[u8]) -> Self::Tag;
/// Authenticate `ciphertext` and `ad` and, if valid, decrypt `ciphertext`,
/// returning the original plaintext in a newly allocated [`Vec`]. The `nonce`
/// must be the same value as given to the sealing operation that produced
/// `ciphertext`.
fn open(&self, nonce: &Self::Nonce, ciphertext: &[u8], ad: &[u8]) -> Option<Vec<u8>>;
/// Authenticate `ciphertext` and `ad` using `tag` and, if valid, decrypt
/// `ciphertext` in place. The `nonce` must be the same value as given to
/// the sealing operation that produced `ciphertext`.
fn open_in_place(
&self,
nonce: &Self::Nonce,
ciphertext: &mut [u8],
tag: &Self::Tag,
ad: &[u8],
) -> Result<(), InvalidCiphertext>;
}
/// AES-GCM-SIV implementation.
pub struct AesGcmSiv(AeadImpl<12, 16>);
/// AES-128 in Galois Counter Mode.
pub struct Aes128Gcm(EvpAead<16, 12, 16>);
aead_algo!(Aes128Gcm, EVP_aead_aes_128_gcm, 16, 12, 16);
/// Instantiates a new AES-128-GCM-SIV instance from key material.
pub fn new_aes_128_gcm_siv(key: &[u8; 16]) -> AesGcmSiv {
AesGcmSiv(AeadImpl::new::<EvpAes128GcmSiv>(key))
}
/// AES-256 in Galois Counter Mode.
pub struct Aes256Gcm(EvpAead<32, 12, 16>);
aead_algo!(Aes256Gcm, EVP_aead_aes_256_gcm, 32, 12, 16);
/// Instantiates a new AES-256-GCM-SIV instance from key material.
pub fn new_aes_256_gcm_siv(key: &[u8; 32]) -> AesGcmSiv {
AesGcmSiv(AeadImpl::new::<EvpAes256GcmSiv>(key))
}
/// AES-128 in GCM-SIV mode (which is different from SIV mode!).
pub struct Aes128GcmSiv(EvpAead<16, 12, 16>);
aead_algo!(Aes128GcmSiv, EVP_aead_aes_128_gcm_siv, 16, 12, 16);
impl Aead for AesGcmSiv {
const TAG_SIZE: usize = 16;
type Nonce = [u8; 12];
/// AES-256 in GCM-SIV mode (which is different from SIV mode!).
pub struct Aes256GcmSiv(EvpAead<32, 12, 16>);
aead_algo!(Aes256GcmSiv, EVP_aead_aes_256_gcm_siv, 32, 12, 16);
fn encrypt(&self, msg: &[u8], aad: &[u8], nonce: &[u8; 12]) -> Result<Vec<u8>, AeadError> {
self.0.encrypt(msg, aad, nonce)
/// The AEAD built from ChaCha20 and Poly1305 as described in <https://datatracker.ietf.org/doc/html/rfc8439>.
pub struct Chacha20Poly1305(EvpAead<32, 12, 16>);
aead_algo!(Chacha20Poly1305, EVP_aead_chacha20_poly1305, 32, 12, 16);
/// Chacha20Poly1305 with an extended nonce that makes random generation of nonces safe.
pub struct XChacha20Poly1305(EvpAead<32, 24, 16>);
aead_algo!(XChacha20Poly1305, EVP_aead_xchacha20_poly1305, 32, 24, 16);
/// An internal struct that implements AEAD operations given an `EVP_AEAD`.
struct EvpAead<const KEY_LEN: usize, const NONCE_LEN: usize, const TAG_LEN: usize>(
*mut bssl_sys::EVP_AEAD_CTX,
);
#[allow(clippy::unwrap_used)]
impl<const KEY_LEN: usize, const NONCE_LEN: usize, const TAG_LEN: usize>
EvpAead<KEY_LEN, NONCE_LEN, TAG_LEN>
{
// Tagged unsafe because `evp_aead` must be valid.
unsafe fn new(key: &[u8; KEY_LEN], evp_aead: *const bssl_sys::EVP_AEAD) -> Self {
// `evp_aead` is assumed to be valid. The function will validate
// the other lengths and return NULL on error. In that case we
// crash the address space because that should never happen.
let ptr =
unsafe { bssl_sys::EVP_AEAD_CTX_new(evp_aead, key.as_ffi_ptr(), key.len(), TAG_LEN) };
assert!(!ptr.is_null());
Self(ptr)
}
fn decrypt(&self, msg: &[u8], aad: &[u8], nonce: &[u8; 12]) -> Result<Vec<u8>, AeadError> {
self.0.decrypt(msg, aad, nonce)
}
}
trait EvpAeadType {
type Key: AsRef<[u8]>;
fn evp_aead() -> *const EVP_AEAD;
}
struct EvpAes128GcmSiv;
impl EvpAeadType for EvpAes128GcmSiv {
type Key = [u8; 16];
fn evp_aead() -> *const EVP_AEAD {
// Safety:
// - this just returns a constant value
unsafe { bssl_sys::EVP_aead_aes_128_gcm_siv() }
}
}
struct EvpAes256GcmSiv;
impl EvpAeadType for EvpAes256GcmSiv {
type Key = [u8; 32];
fn evp_aead() -> *const EVP_AEAD {
// Safety:
// - this just returns a constant value
unsafe { bssl_sys::EVP_aead_aes_256_gcm_siv() }
}
}
/// AES-GCM implementation.
pub struct AesGcm(AeadImpl<12, 16>);
/// Instantiates a new AES-128-GCM instance from key material.
pub fn new_aes_128_gcm(key: &[u8; 16]) -> AesGcm {
AesGcm(AeadImpl::new::<EvpAes128Gcm>(key))
}
/// Instantiates a new AES-256-GCM instance from key material.
pub fn new_aes_256_gcm(key: &[u8; 32]) -> AesGcm {
AesGcm(AeadImpl::new::<EvpAes256Gcm>(key))
}
impl Aead for AesGcm {
const TAG_SIZE: usize = 16;
type Nonce = [u8; 12];
fn encrypt(&self, msg: &[u8], aad: &[u8], nonce: &[u8; 12]) -> Result<Vec<u8>, AeadError> {
self.0.encrypt(msg, aad, nonce)
}
fn decrypt(&self, msg: &[u8], aad: &[u8], nonce: &[u8; 12]) -> Result<Vec<u8>, AeadError> {
self.0.decrypt(msg, aad, nonce)
}
}
struct EvpAes128Gcm;
impl EvpAeadType for EvpAes128Gcm {
type Key = [u8; 16];
fn evp_aead() -> *const EVP_AEAD {
// Safety:
// - this just returns a constant value
unsafe { bssl_sys::EVP_aead_aes_128_gcm() }
}
}
struct EvpAes256Gcm;
impl EvpAeadType for EvpAes256Gcm {
type Key = [u8; 32];
fn evp_aead() -> *const EVP_AEAD {
// Safety:
// - this just returns a constant value
unsafe { bssl_sys::EVP_aead_aes_256_gcm() }
}
}
// Private implementation of an AEAD which is generic over Nonce size and Tag size. This should
// only be exposed publicly by wrapper types which provide the correctly sized const generics for
// the given aead algorithm.
struct AeadImpl<const N: usize, const T: usize>(*mut EVP_AEAD_CTX);
impl<const N: usize, const T: usize> AeadImpl<N, T> {
// Create a new AeadImpl instance from key material and for a supported AeadType.
fn new<A: EvpAeadType>(key: &A::Key) -> Self {
let key_cslice = CSlice::from(key.as_ref());
// Safety:
// - This is always safe as long as the correct key size is set by the wrapper type.
let ctx = unsafe {
bssl_sys::EVP_AEAD_CTX_new(
A::evp_aead(),
key_cslice.as_ptr(),
key_cslice.len(),
bssl_sys::EVP_AEAD_DEFAULT_TAG_LENGTH as usize,
)
};
assert!(!ctx.is_null());
AeadImpl(ctx)
}
// Encrypts msg in-place, adding enough space to msg for the auth tag.
fn encrypt(&self, msg: &[u8], aad: &[u8], nonce: &[u8; N]) -> Result<Vec<u8>, AeadError> {
let mut out = Vec::new();
out.resize(msg.len() + T, 0u8);
let mut out_cslice = CSliceMut::from(out.as_mut_slice());
let msg_cslice = CSlice::from(msg);
let aad_cslice = CSlice::from(aad);
let nonce_cslice = CSlice::from(nonce.as_slice());
let mut out_len = 0usize;
// Safety:
// - The buffers are all valid, with corresponding ptr and length
let result = unsafe {
bssl_sys::EVP_AEAD_CTX_seal(
self.0,
out_cslice.as_mut_ptr(),
&mut out_len,
out_cslice.len(),
nonce_cslice.as_ptr(),
nonce_cslice.len(),
msg_cslice.as_ptr(),
msg_cslice.len(),
aad_cslice.as_ptr(),
aad_cslice.len(),
)
};
if result == 1 {
// Verify the correct number of bytes were written.
assert_eq!(out_len, out.len());
Ok(out)
} else {
Err(AeadError)
fn seal(&self, nonce: &[u8; NONCE_LEN], plaintext: &[u8], ad: &[u8]) -> Vec<u8> {
let max_output = plaintext.len() + TAG_LEN;
unsafe {
with_output_vec(max_output, |out_buf| {
let mut out_len = 0usize;
// Safety: the input buffers are all valid, with corresponding
// ptr and length. The output buffer has at least `max_output`
// bytes of space and that maximum is passed to
// `EVP_AEAD_CTX_seal` as a limit.
let result = bssl_sys::EVP_AEAD_CTX_seal(
self.0,
out_buf,
&mut out_len,
max_output,
nonce.as_ffi_ptr(),
nonce.len(),
plaintext.as_ffi_ptr(),
plaintext.len(),
ad.as_ffi_ptr(),
ad.len(),
);
// Sealing never fails unless there's a programmer error.
assert_eq!(result, 1);
// For the implemented AEADs, we should always have calculated
// the overhead exactly.
assert_eq!(out_len, max_output);
// Safety: `out_len` bytes have been written to.
out_len
})
}
}
// Decrypts msg in-place, on success msg will contain the plain text alone, without the auth
// tag.
fn decrypt(&self, msg: &[u8], aad: &[u8], nonce: &[u8; N]) -> Result<Vec<u8>, AeadError> {
if msg.len() < T {
return Err(AeadError);
fn seal_in_place(
&self,
nonce: &[u8; NONCE_LEN],
plaintext: &mut [u8],
ad: &[u8],
) -> [u8; TAG_LEN] {
// Safety: the buffers are all valid, with corresponding ptr and length.
// `tag_len` is passed at the maximum size of `tag` and `out_tag_len`
// is checked to ensure that the whole output was written to.
unsafe {
with_output_array(|tag, tag_len| {
let mut out_tag_len = 0usize;
let result = bssl_sys::EVP_AEAD_CTX_seal_scatter(
self.0,
plaintext.as_mut_ffi_ptr(),
tag,
&mut out_tag_len,
tag_len,
nonce.as_ffi_ptr(),
nonce.len(),
plaintext.as_ffi_ptr(),
plaintext.len(),
/*extra_in=*/ core::ptr::null(),
/*extra_in_len=*/ 0,
ad.as_ffi_ptr(),
ad.len(),
);
// Failure indicates that one of the configured lengths was wrong.
// Crashing is a good answer in that case.
assert_eq!(result, 1);
// The whole output must have been written to.
assert_eq!(out_tag_len, TAG_LEN);
})
}
let mut out = Vec::new();
out.resize(msg.len() - T, 0u8);
}
let mut out_cslice = CSliceMut::from(out.as_mut_slice());
let aad_cslice = CSlice::from(aad);
let msg_cslice = CSlice::from(msg);
let mut out_len = 0usize;
fn open(&self, nonce: &[u8; NONCE_LEN], ciphertext: &[u8], ad: &[u8]) -> Option<Vec<u8>> {
if ciphertext.len() < TAG_LEN {
return None;
}
let max_output = ciphertext.len() - TAG_LEN;
unsafe {
with_output_vec_fallible(max_output, |out_buf| {
let mut out_len = 0usize;
// Safety: the input buffers are all valid, with corresponding
// ptr and length. The output buffer has at least `max_output`
// bytes of space and that maximum is passed to
// `EVP_AEAD_CTX_open` as a limit.
let result = bssl_sys::EVP_AEAD_CTX_open(
self.0,
out_buf,
&mut out_len,
max_output,
nonce.as_ffi_ptr(),
nonce.len(),
ciphertext.as_ffi_ptr(),
ciphertext.len(),
ad.as_ffi_ptr(),
ad.len(),
);
if result == 1 {
// Safety: `out_len` bytes have been written to.
Some(out_len)
} else {
None
}
})
}
}
fn open_in_place(
&self,
nonce: &[u8; NONCE_LEN],
ciphertext: &mut [u8],
tag: &[u8; TAG_LEN],
ad: &[u8],
) -> Result<(), InvalidCiphertext> {
// Safety:
// - The buffers are all valid, with corresponding ptr and length
let result = unsafe {
bssl_sys::EVP_AEAD_CTX_open(
bssl_sys::EVP_AEAD_CTX_open_gather(
self.0,
out_cslice.as_mut_ptr(),
&mut out_len,
out_cslice.len(),
nonce.as_ptr(),
ciphertext.as_mut_ffi_ptr(),
nonce.as_ffi_ptr(),
nonce.len(),
msg_cslice.as_ptr(),
msg_cslice.len(),
aad_cslice.as_ptr(),
aad_cslice.len(),
ciphertext.as_ffi_ptr(),
ciphertext.len(),
tag.as_ffi_ptr(),
tag.len(),
ad.as_ffi_ptr(),
ad.len(),
)
};
if result == 1 {
// Verify the correct number of bytes were written.
assert_eq!(out_len, out.len());
Ok(out)
Ok(())
} else {
Err(AeadError)
Err(InvalidCiphertext)
}
}
}
impl<const N: usize, const T: usize> Drop for AeadImpl<N, T> {
impl<const KEY_LEN: usize, const NONCE_LEN: usize, const TAG_LEN: usize> Drop
for EvpAead<KEY_LEN, NONCE_LEN, TAG_LEN>
{
fn drop(&mut self) {
// Safety:
// - `self.0` was allocated by `EVP_AEAD_CTX_new` and has not yet been freed.
// Safety: `self.0` was initialized by `EVP_AEAD_CTX_init` because all
// paths to create an `EvpAead` do so.
unsafe { bssl_sys::EVP_AEAD_CTX_free(self.0) }
}
}
@@ -256,168 +282,216 @@ impl<const N: usize, const T: usize> Drop for AeadImpl<N, T> {
#[cfg(test)]
mod test {
use super::*;
use crate::test_helpers::decode_hex;
use crate::test_helpers::{decode_hex, decode_hex_into_vec};
#[test]
fn aes_128_gcm_siv_tests() {
// https://github.com/google/wycheproof/blob/master/testvectors/aes_gcm_siv_test.json
// TC1 - Empty Message
let key = decode_hex("01000000000000000000000000000000");
let nonce = decode_hex("030000000000000000000000");
let tag: [u8; 16] = decode_hex("dc20e2d83f25705bb49e439eca56de25");
let mut buf = Vec::from(&[] as &[u8]);
let aes = new_aes_128_gcm_siv(&key);
let result = aes.encrypt(&mut buf, b"", &nonce);
assert!(result.is_ok());
assert_eq!(result.unwrap(), &tag);
fn check_aead_invariants<
const NONCE_LEN: usize,
const TAG_LEN: usize,
A: Aead<Nonce = [u8; NONCE_LEN], Tag = [u8; TAG_LEN]>,
>(
aead: A,
) {
let plaintext = b"plaintext";
let ad = b"additional data";
let nonce: A::Nonce = [0u8; NONCE_LEN];
// TC2
let msg: [u8; 8] = decode_hex("0100000000000000");
let ct: [u8; 8] = decode_hex("b5d839330ac7b786");
let tag: [u8; 16] = decode_hex("578782fff6013b815b287c22493a364c");
let result = aes.encrypt(&msg, b"", &nonce);
assert!(result.is_ok());
let mut result_vec = result.unwrap();
assert_eq!(&result_vec[..8], &ct);
assert_eq!(&result_vec[8..], &tag);
let result = aes.decrypt(result_vec.as_mut_slice(), b"", &nonce);
assert!(result.is_ok());
assert_eq!(&result.unwrap(), &msg);
let mut ciphertext = aead.seal(&nonce, plaintext, ad);
let plaintext2 = aead
.open(&nonce, ciphertext.as_slice(), ad)
.expect("should decrypt");
assert_eq!(plaintext, plaintext2.as_slice());
// TC14 contains associated data
let msg: [u8; 4] = decode_hex("02000000");
let ct: [u8; 4] = decode_hex("a8fe3e87");
let aad: [u8; 12] = decode_hex("010000000000000000000000");
let tag: [u8; 16] = decode_hex("07eb1f84fb28f8cb73de8e99e2f48a14");
let result = aes.encrypt(&msg, &aad, &nonce);
assert!(result.is_ok());
let mut result_vec = result.unwrap();
assert_eq!(&result_vec[..4], &ct);
assert_eq!(&result_vec[4..], &tag);
let result = aes.decrypt(result_vec.as_mut_slice(), &aad, &nonce);
assert!(result.is_ok());
assert_eq!(&result.unwrap(), &msg);
ciphertext[0] ^= 1;
assert!(aead.open(&nonce, ciphertext.as_slice(), ad).is_none());
ciphertext[0] ^= 1;
let (ciphertext_in_place, tag_slice) =
ciphertext.as_mut_slice().split_at_mut(plaintext.len());
let tag: [u8; TAG_LEN] = tag_slice.try_into().unwrap();
aead.open_in_place(&nonce, ciphertext_in_place, &tag, ad)
.expect("should decrypt");
assert_eq!(plaintext, ciphertext_in_place);
let tag = aead.seal_in_place(&nonce, ciphertext_in_place, ad);
aead.open_in_place(&nonce, ciphertext_in_place, &tag, ad)
.expect("should decrypt");
assert_eq!(plaintext, ciphertext_in_place);
assert!(aead.open(&nonce, b"tooshort", b"").is_none());
}
#[test]
fn aes_256_gcm_siv_tests() {
// https://github.com/google/wycheproof/blob/master/testvectors/aes_gcm_siv_test.json
// TC77
let test_key =
decode_hex("0100000000000000000000000000000000000000000000000000000000000000");
let nonce = decode_hex("030000000000000000000000");
let aes = new_aes_256_gcm_siv(&test_key);
let mut msg: [u8; 8] = decode_hex("0100000000000000");
let ct: [u8; 8] = decode_hex("c2ef328e5c71c83b");
let tag: [u8; 16] = decode_hex("843122130f7364b761e0b97427e3df28");
let enc_result = aes.encrypt(&mut msg, b"", &nonce);
assert!(enc_result.is_ok());
let mut enc_data = enc_result.unwrap();
assert_eq!(&enc_data[..8], &ct);
assert_eq!(&enc_data[8..], &tag);
let result = aes.decrypt(enc_data.as_mut_slice(), b"", &nonce);
assert!(result.is_ok());
assert_eq!(&result.unwrap(), &msg);
// TC78
let mut msg: [u8; 12] = decode_hex("010000000000000000000000");
let ct: [u8; 12] = decode_hex("9aab2aeb3faa0a34aea8e2b1");
let tag: [u8; 16] = decode_hex("8ca50da9ae6559e48fd10f6e5c9ca17e");
let enc_result = aes.encrypt(&mut msg, b"", &nonce);
assert!(enc_result.is_ok());
let mut enc_data = enc_result.unwrap();
assert_eq!(&enc_data[..12], &ct);
assert_eq!(&enc_data[12..], &tag);
let result = aes.decrypt(enc_data.as_mut_slice(), b"", &nonce);
assert!(result.is_ok());
assert_eq!(&result.unwrap(), &msg);
// TC89 contains associated data
let mut msg: [u8; 4] = decode_hex("02000000");
let ct: [u8; 4] = decode_hex("22b3f4cd");
let tag: [u8; 16] = decode_hex("1835e517741dfddccfa07fa4661b74cf");
let aad: [u8; 12] = decode_hex("010000000000000000000000");
let enc_result = aes.encrypt(&mut msg, &aad, &nonce);
assert!(enc_result.is_ok());
let mut enc_data = enc_result.unwrap();
assert_eq!(&enc_data[..4], &ct);
assert_eq!(&enc_data[4..], &tag);
let result = aes.decrypt(enc_data.as_mut_slice(), &aad, &nonce);
assert!(result.is_ok());
assert_eq!(&result.unwrap(), &msg);
fn aes_128_gcm_invariants() {
check_aead_invariants(Aes128Gcm::new(&[0u8; 16]));
}
#[test]
fn aes_128_gcm_tests() {
// TC 1 from crypto/cipher_extra/test/aes_128_gcm_tests.txt
let key = decode_hex("d480429666d48b400633921c5407d1d1");
let nonce = decode_hex("3388c676dc754acfa66e172a");
let tag: [u8; 16] = decode_hex("7d7daf44850921a34e636b01adeb104f");
let mut buf = Vec::from(&[] as &[u8]);
let aes = new_aes_128_gcm(&key);
let result = aes.encrypt(&mut buf, b"", &nonce);
assert!(result.is_ok());
assert_eq!(result.unwrap(), &tag);
// TC2
let key = decode_hex("3881e7be1bb3bbcaff20bdb78e5d1b67");
let nonce = decode_hex("dcf5b7ae2d7552e2297fcfa9");
let msg: [u8; 5] = decode_hex("0a2714aa7d");
let ad: [u8; 5] = decode_hex("c60c64bbf7");
let ct: [u8; 5] = decode_hex("5626f96ecb");
let tag: [u8; 16] = decode_hex("ff4c4f1d92b0abb1d0820833d9eb83c7");
let mut buf = Vec::from(msg.as_slice());
let aes = new_aes_128_gcm(&key);
let result = aes.encrypt(&mut buf, &ad, &nonce);
assert!(result.is_ok());
let mut data = result.unwrap();
assert_eq!(&data[..5], &ct);
assert_eq!(&data[5..], &tag);
let result = aes.decrypt(data.as_mut_slice(), &ad, &nonce);
assert!(result.is_ok());
assert_eq!(result.unwrap(), &msg);
fn aes_256_gcm_invariants() {
check_aead_invariants(Aes256Gcm::new(&[0u8; 32]));
}
#[test]
fn aes_256_gcm_tests() {
// TC 1 from crypto/cipher_extra/test/aes_256_gcm_tests.txt
let key = decode_hex("e5ac4a32c67e425ac4b143c83c6f161312a97d88d634afdf9f4da5bd35223f01");
let nonce = decode_hex("5bf11a0951f0bfc7ea5c9e58");
let tag: [u8; 16] = decode_hex("d7cba289d6d19a5af45dc13857016bac");
let mut buf = Vec::from(&[] as &[u8]);
let aes = new_aes_256_gcm(&key);
let result = aes.encrypt(&mut buf, b"", &nonce);
assert!(result.is_ok());
assert_eq!(result.unwrap(), &tag);
// TC2
let key = decode_hex("73ad7bbbbc640c845a150f67d058b279849370cd2c1f3c67c4dd6c869213e13a");
let nonce = decode_hex("a330a184fc245812f4820caa");
let msg: [u8; 5] = decode_hex("f0535fe211");
let ad: [u8; 5] = decode_hex("e91428be04");
let ct: [u8; 5] = decode_hex("e9b8a896da");
let tag: [u8; 16] = decode_hex("9115ed79f26a030c14947b3e454db9e7");
let mut buf = Vec::from(msg.as_slice());
let aes = new_aes_256_gcm(&key);
let result = aes.encrypt(&mut buf, &ad, &nonce);
assert!(result.is_ok());
let mut data = result.unwrap();
assert_eq!(&data[..5], &ct);
assert_eq!(&data[5..], &tag);
let result = aes.decrypt(data.as_mut_slice(), &ad, &nonce);
assert!(result.is_ok());
assert_eq!(result.unwrap(), &msg);
fn aes_128_gcm_siv_invariants() {
check_aead_invariants(Aes128GcmSiv::new(&[0u8; 16]));
}
#[test]
fn test_invalid_data_length_decrypt() {
let key = decode_hex("00000000000000000000000000000000");
let nonce = decode_hex("000000000000000000000000");
let buf = Vec::from(&[] as &[u8]);
let aes = new_aes_128_gcm_siv(&key);
let result = aes.decrypt(&buf, b"", &nonce);
assert!(result.is_err());
fn aes_256_gcm_siv_invariants() {
check_aead_invariants(Aes256GcmSiv::new(&[0u8; 32]));
}
#[test]
fn chacha20_poly1305_invariants() {
check_aead_invariants(Chacha20Poly1305::new(&[0u8; 32]));
}
#[test]
fn xchacha20_poly1305_invariants() {
check_aead_invariants(XChacha20Poly1305::new(&[0u8; 32]));
}
struct TestCase<const KEY_LEN: usize, const NONCE_LEN: usize> {
key: [u8; KEY_LEN],
nonce: [u8; NONCE_LEN],
msg: Vec<u8>,
ad: Vec<u8>,
ciphertext: Vec<u8>,
}
fn check_test_cases<
const KEY_LEN: usize,
const NONCE_LEN: usize,
const TAG_LEN: usize,
F: Fn(&[u8; KEY_LEN]) -> Box<dyn Aead<Nonce = [u8; NONCE_LEN], Tag = [u8; TAG_LEN]>>,
>(
new_func: F,
test_cases: &[TestCase<KEY_LEN, NONCE_LEN>],
) {
for (test_num, test) in test_cases.iter().enumerate() {
let ctx = new_func(&test.key);
let ciphertext = ctx.seal(&test.nonce, test.msg.as_slice(), test.ad.as_slice());
assert_eq!(ciphertext, test.ciphertext, "Failed on test #{}", test_num);
let plaintext = ctx
.open(&test.nonce, ciphertext.as_slice(), test.ad.as_slice())
.unwrap();
assert_eq!(plaintext, test.msg, "Decrypt failed on test #{}", test_num);
}
}
#[test]
fn aes_128_gcm_siv() {
let test_cases: &[TestCase<16, 12>] = &[
TestCase {
// https://github.com/google/wycheproof/blob/master/testvectors/aes_gcm_siv_test.json
// TC1 - Empty Message
key: decode_hex("01000000000000000000000000000000"),
nonce: decode_hex("030000000000000000000000"),
msg: Vec::new(),
ad: Vec::new(),
ciphertext: decode_hex_into_vec("dc20e2d83f25705bb49e439eca56de25"),
},
TestCase {
// TC2
key: decode_hex("01000000000000000000000000000000"),
nonce: decode_hex("030000000000000000000000"),
msg: decode_hex_into_vec("0100000000000000"),
ad: Vec::new(),
ciphertext: decode_hex_into_vec("b5d839330ac7b786578782fff6013b815b287c22493a364c"),
},
TestCase {
// TC14
key: decode_hex("01000000000000000000000000000000"),
nonce: decode_hex("030000000000000000000000"),
msg: decode_hex_into_vec("02000000"),
ad: decode_hex_into_vec("010000000000000000000000"),
ciphertext: decode_hex_into_vec("a8fe3e8707eb1f84fb28f8cb73de8e99e2f48a14"),
},
];
check_test_cases(|key| Box::new(Aes128GcmSiv::new(key)), test_cases);
}
#[test]
fn aes_256_gcm_siv() {
let test_cases: &[TestCase<32, 12>] = &[
TestCase {
// https://github.com/google/wycheproof/blob/master/testvectors/aes_gcm_siv_test.json
// TC77
key: decode_hex("0100000000000000000000000000000000000000000000000000000000000000"),
nonce: decode_hex("030000000000000000000000"),
msg: decode_hex_into_vec("0100000000000000"),
ad: Vec::new(),
ciphertext: decode_hex_into_vec("c2ef328e5c71c83b843122130f7364b761e0b97427e3df28"),
},
TestCase {
// TC78
key: decode_hex("0100000000000000000000000000000000000000000000000000000000000000"),
nonce: decode_hex("030000000000000000000000"),
msg: decode_hex_into_vec("010000000000000000000000"),
ad: Vec::new(),
ciphertext: decode_hex_into_vec(
"9aab2aeb3faa0a34aea8e2b18ca50da9ae6559e48fd10f6e5c9ca17e",
),
},
TestCase {
// TC89 contains associated data
key: decode_hex("0100000000000000000000000000000000000000000000000000000000000000"),
nonce: decode_hex("030000000000000000000000"),
msg: decode_hex_into_vec("02000000"),
ad: decode_hex_into_vec("010000000000000000000000"),
ciphertext: decode_hex_into_vec("22b3f4cd1835e517741dfddccfa07fa4661b74cf"),
},
];
check_test_cases(|key| Box::new(Aes256GcmSiv::new(key)), test_cases);
}
#[test]
fn aes_128_gcm() {
let test_cases: &[TestCase<16, 12>] = &[
TestCase {
// TC 1 from crypto/cipher_extra/test/aes_128_gcm_tests.txt
key: decode_hex("d480429666d48b400633921c5407d1d1"),
nonce: decode_hex("3388c676dc754acfa66e172a"),
msg: Vec::new(),
ad: Vec::new(),
ciphertext: decode_hex_into_vec("7d7daf44850921a34e636b01adeb104f"),
},
TestCase {
// TC2
key: decode_hex("3881e7be1bb3bbcaff20bdb78e5d1b67"),
nonce: decode_hex("dcf5b7ae2d7552e2297fcfa9"),
msg: decode_hex_into_vec("0a2714aa7d"),
ad: decode_hex_into_vec("c60c64bbf7"),
ciphertext: decode_hex_into_vec("5626f96ecbff4c4f1d92b0abb1d0820833d9eb83c7"),
},
];
check_test_cases(|key| Box::new(Aes128Gcm::new(key)), test_cases);
}
#[test]
fn aes_256_gcm() {
let test_cases: &[TestCase<32, 12>] = &[
TestCase {
// TC 1 from crypto/cipher_extra/test/aes_128_gcm_tests.txt
key: decode_hex("e5ac4a32c67e425ac4b143c83c6f161312a97d88d634afdf9f4da5bd35223f01"),
nonce: decode_hex("5bf11a0951f0bfc7ea5c9e58"),
msg: Vec::new(),
ad: Vec::new(),
ciphertext: decode_hex_into_vec("d7cba289d6d19a5af45dc13857016bac"),
},
TestCase {
// TC2
key: decode_hex("73ad7bbbbc640c845a150f67d058b279849370cd2c1f3c67c4dd6c869213e13a"),
nonce: decode_hex("a330a184fc245812f4820caa"),
msg: decode_hex_into_vec("f0535fe211"),
ad: decode_hex_into_vec("e91428be04"),
ciphertext: decode_hex_into_vec("e9b8a896da9115ed79f26a030c14947b3e454db9e7"),
},
];
check_test_cases(|key| Box::new(Aes256Gcm::new(key)), test_cases);
}
}
+124 -173
View File
@@ -13,214 +13,165 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/// Block size in bytes for AES.
//! Advanced Encryption Standard.
//!
//! AES is a 128-bit block cipher that supports key sizes of 128, 192, or 256
//! bits. (Although 192 isn't supported here.)
//!
//! Each key defines a permutation of the set of 128-bit blocks and AES can
//! perform the forward and reverse permutation. (These directions are
//! arbitrarily labeled "encryption" and "decryption".)
//!
//! AES requires relatively expensive preprocessing of keys and thus the
//! processed form of the key is represented here using [`EncryptKey`] and
//! [`DecryptKey`].
//!
//! ```
//! use bssl_crypto::aes;
//!
//! let key_bytes = bssl_crypto::rand_array();
//! let enc_key = aes::EncryptKey::new_256(&key_bytes);
//! let block = [0u8; aes::BLOCK_SIZE];
//! let mut transformed_block = enc_key.encrypt(&block);
//!
//! let dec_key = aes::DecryptKey::new_256(&key_bytes);
//! dec_key.decrypt_in_place(&mut transformed_block);
//! assert_eq!(block, transformed_block);
//! ```
//!
//! AES is a low-level primitive and must be used in a more complex construction
//! in nearly every case. See the `aead` crate for usable encryption and
//! decryption primitives.
use crate::{initialized_struct_fallible, FfiMutSlice, FfiSlice};
use core::ffi::c_uint;
/// AES block size in bytes.
pub const BLOCK_SIZE: usize = bssl_sys::AES_BLOCK_SIZE as usize;
/// A single AES block.
pub type AesBlock = [u8; BLOCK_SIZE];
/// AES implementation used for encrypting/decrypting a single `AesBlock` at a time.
pub struct Aes;
impl Aes {
/// Encrypts `block` in place.
pub fn encrypt(key: &AesEncryptKey, block: &mut AesBlock) {
let input = *block;
// Safety:
// - AesBlock is always a valid size and key is guaranteed to already be initialized.
unsafe { bssl_sys::AES_encrypt(input.as_ptr(), block.as_mut_ptr(), &key.0) }
}
/// Decrypts `block` in place.
pub fn decrypt(key: &AesDecryptKey, block: &mut AesBlock) {
let input = *block;
// Safety:
// - AesBlock is always a valid size and key is guaranteed to already be initialized.
unsafe { bssl_sys::AES_decrypt(input.as_ptr(), block.as_mut_ptr(), &key.0) }
}
}
pub type Block = [u8; BLOCK_SIZE];
/// An initialized key which can be used for encrypting.
pub struct AesEncryptKey(bssl_sys::AES_KEY);
pub struct EncryptKey(bssl_sys::AES_KEY);
impl AesEncryptKey {
/// Initializes an encryption key from an appropriately sized array of bytes for AES-128 operations.
pub fn new_aes_128(key: [u8; 16]) -> AesEncryptKey {
new_encrypt_key(key)
impl EncryptKey {
/// Initializes an encryption key from an appropriately sized array of bytes
// for AES-128 operations.
pub fn new_128(key: &[u8; 16]) -> Self {
new_encrypt_key(key.as_slice())
}
/// Initializes an encryption key from an appropriately sized array of bytes for AES-256 operations.
pub fn new_aes_256(key: [u8; 32]) -> AesEncryptKey {
new_encrypt_key(key)
/// Initializes an encryption key from an appropriately sized array of bytes
// for AES-256 operations.
pub fn new_256(key: &[u8; 32]) -> Self {
new_encrypt_key(key.as_slice())
}
/// Return the encrypted version of the given block.
pub fn encrypt(&self, block: &Block) -> Block {
let mut ret = *block;
self.encrypt_in_place(&mut ret);
ret
}
/// Replace `block` with its encrypted version.
pub fn encrypt_in_place(&self, block: &mut Block) {
// Safety:
// - block is always a valid size and key is guaranteed to already be initialized.
unsafe { bssl_sys::AES_encrypt(block.as_ffi_ptr(), block.as_mut_ffi_ptr(), &self.0) }
}
}
/// An initialized key which can be used for decrypting
pub struct AesDecryptKey(bssl_sys::AES_KEY);
pub struct DecryptKey(bssl_sys::AES_KEY);
impl AesDecryptKey {
impl DecryptKey {
/// Initializes a decryption key from an appropriately sized array of bytes for AES-128 operations.
pub fn new_aes_128(key: [u8; 16]) -> AesDecryptKey {
new_decrypt_key(key)
pub fn new_128(key: &[u8; 16]) -> DecryptKey {
new_decrypt_key(key.as_slice())
}
/// Initializes a decryption key from an appropriately sized array of bytes for AES-256 operations.
pub fn new_aes_256(key: [u8; 32]) -> AesDecryptKey {
new_decrypt_key(key)
pub fn new_256(key: &[u8; 32]) -> DecryptKey {
new_decrypt_key(key.as_slice())
}
/// Return the decrypted version of the given block.
pub fn decrypt(&self, block: &Block) -> Block {
let mut ret = *block;
self.decrypt_in_place(&mut ret);
ret
}
/// Replace `block` with its decrypted version.
pub fn decrypt_in_place(&self, block: &mut Block) {
// Safety:
// - block is always a valid size and key is guaranteed to already be initialized.
unsafe { bssl_sys::AES_decrypt(block.as_ffi_ptr(), block.as_mut_ffi_ptr(), &self.0) }
}
}
/// Private generically implemented function for creating a new `AesEncryptKey` from an array of bytes.
/// This should only be publicly exposed by wrapper types with the correct key lengths
fn new_encrypt_key<const N: usize>(key: [u8; N]) -> AesEncryptKey {
let mut enc_key_uninit = core::mem::MaybeUninit::uninit();
// Safety:
// - key is guaranteed to point to bits/8 bytes determined by the len() * 8 used below.
// - bits is always a valid AES key size, as defined by the new_aes_* fns defined on the public
// key structs.
let result = unsafe {
bssl_sys::AES_set_encrypt_key(
key.as_ptr(),
key.len() as core::ffi::c_uint * 8,
enc_key_uninit.as_mut_ptr(),
)
};
assert_eq!(result, 0, "Error occurred in bssl_sys::AES_set_encrypt_key");
// Safety:
// - since we have checked above that initialization succeeded, this will never be UB
let enc_key = unsafe { enc_key_uninit.assume_init() };
AesEncryptKey(enc_key)
#[allow(clippy::unwrap_used)]
fn new_encrypt_key(key: &[u8]) -> EncryptKey {
EncryptKey(
unsafe {
initialized_struct_fallible(|aes_key| {
// The return value of this function differs from the usual BoringSSL
// convention.
bssl_sys::AES_set_encrypt_key(key.as_ffi_ptr(), key.len() as c_uint * 8, aes_key)
== 0
})
}
// unwrap: this function only fails if `key` is the wrong length, which
// must be prevented by the pub functions that call this.
.unwrap(),
)
}
/// Private generically implemented function for creating a new `AesDecryptKey` from an array of bytes.
/// This should only be publicly exposed by wrapper types with the correct key lengths.
fn new_decrypt_key<const N: usize>(key: [u8; N]) -> AesDecryptKey {
let mut dec_key_uninit = core::mem::MaybeUninit::uninit();
// Safety:
// - key is guaranteed to point to bits/8 bytes determined by the len() * 8 used below.
// - bits is always a valid AES key size, as defined by the new_aes_* fns defined on the public
// key structs.
let result = unsafe {
bssl_sys::AES_set_decrypt_key(
key.as_ptr(),
key.len() as core::ffi::c_uint * 8,
dec_key_uninit.as_mut_ptr(),
)
};
assert_eq!(result, 0, "Error occurred in bssl_sys::AES_set_decrypt_key");
// Safety:
// - Since we have checked above that initialization succeeded, this will never be UB.
let dec_key = unsafe { dec_key_uninit.assume_init() };
AesDecryptKey(dec_key)
#[allow(clippy::unwrap_used)]
fn new_decrypt_key(key: &[u8]) -> DecryptKey {
DecryptKey(
unsafe {
initialized_struct_fallible(|aes_key| {
// The return value of this function differs from the usual BoringSSL
// convention.
bssl_sys::AES_set_decrypt_key(key.as_ffi_ptr(), key.len() as c_uint * 8, aes_key)
== 0
})
}
// unwrap: this function only fails if `key` is the wrong length, which
// must be prevented by the pub functions that call this.
.unwrap(),
)
}
#[cfg(test)]
mod tests {
use crate::{
aes::{Aes, AesDecryptKey, AesEncryptKey},
aes::{DecryptKey, EncryptKey},
test_helpers::decode_hex,
};
// test data from https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf F.1.1
#[test]
fn aes_128_test_encrypt() {
let key = AesEncryptKey::new_aes_128(decode_hex("2b7e151628aed2a6abf7158809cf4f3c"));
let mut block = [0_u8; 16];
block.copy_from_slice(&decode_hex::<16>("6bc1bee22e409f96e93d7e117393172a"));
Aes::encrypt(&key, &mut block);
assert_eq!(decode_hex("3ad77bb40d7a3660a89ecaf32466ef97"), block);
block.copy_from_slice(&decode_hex::<16>("ae2d8a571e03ac9c9eb76fac45af8e51"));
Aes::encrypt(&key, &mut block);
assert_eq!(decode_hex("f5d3d58503b9699de785895a96fdbaaf"), block);
block.copy_from_slice(&decode_hex::<16>("30c81c46a35ce411e5fbc1191a0a52ef"));
Aes::encrypt(&key, &mut block);
assert_eq!(decode_hex("43b1cd7f598ece23881b00e3ed030688"), block);
block.copy_from_slice(&decode_hex::<16>("f69f2445df4f9b17ad2b417be66c3710"));
Aes::encrypt(&key, &mut block);
assert_eq!(decode_hex("7b0c785e27e8ad3f8223207104725dd4"), block);
fn aes_128() {
// test data from https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf F.1.1
let key = decode_hex("2b7e151628aed2a6abf7158809cf4f3c");
let plaintext = decode_hex("6bc1bee22e409f96e93d7e117393172a");
let ciphertext = decode_hex("3ad77bb40d7a3660a89ecaf32466ef97");
assert_eq!(ciphertext, EncryptKey::new_128(&key).encrypt(&plaintext));
assert_eq!(plaintext, DecryptKey::new_128(&key).decrypt(&ciphertext));
}
// test data from https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf F.1.2
#[test]
fn aes_128_test_decrypt() {
let key = AesDecryptKey::new_aes_128(decode_hex("2b7e151628aed2a6abf7158809cf4f3c"));
let mut block = [0_u8; 16];
block.copy_from_slice(&decode_hex::<16>("3ad77bb40d7a3660a89ecaf32466ef97"));
Aes::decrypt(&key, &mut block);
assert_eq!(decode_hex::<16>("6bc1bee22e409f96e93d7e117393172a"), block);
block.copy_from_slice(&decode_hex::<16>("f5d3d58503b9699de785895a96fdbaaf"));
Aes::decrypt(&key, &mut block);
assert_eq!(decode_hex::<16>("ae2d8a571e03ac9c9eb76fac45af8e51"), block);
block.copy_from_slice(&decode_hex::<16>("43b1cd7f598ece23881b00e3ed030688"));
Aes::decrypt(&key, &mut block);
assert_eq!(decode_hex::<16>("30c81c46a35ce411e5fbc1191a0a52ef"), block);
block.copy_from_slice(&decode_hex::<16>("7b0c785e27e8ad3f8223207104725dd4").as_slice());
Aes::decrypt(&key, &mut block);
assert_eq!(decode_hex::<16>("f69f2445df4f9b17ad2b417be66c3710"), block);
}
// test data from https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf F.1.5
#[test]
pub fn aes_256_test_encrypt() {
let key = AesEncryptKey::new_aes_256(decode_hex(
"603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4",
));
let mut block: [u8; 16];
block = decode_hex("6bc1bee22e409f96e93d7e117393172a");
Aes::encrypt(&key, &mut block);
assert_eq!(decode_hex("f3eed1bdb5d2a03c064b5a7e3db181f8"), block);
block = decode_hex("ae2d8a571e03ac9c9eb76fac45af8e51");
Aes::encrypt(&key, &mut block);
assert_eq!(decode_hex("591ccb10d410ed26dc5ba74a31362870"), block);
block = decode_hex("30c81c46a35ce411e5fbc1191a0a52ef");
Aes::encrypt(&key, &mut block);
assert_eq!(decode_hex("b6ed21b99ca6f4f9f153e7b1beafed1d"), block);
block = decode_hex("f69f2445df4f9b17ad2b417be66c3710");
Aes::encrypt(&key, &mut block);
assert_eq!(decode_hex("23304b7a39f9f3ff067d8d8f9e24ecc7"), block);
}
// test data from https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf F.1.6
#[test]
fn aes_256_test_decrypt() {
let key = AesDecryptKey::new_aes_256(decode_hex(
"603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4",
));
let mut block: [u8; 16];
block = decode_hex("f3eed1bdb5d2a03c064b5a7e3db181f8");
Aes::decrypt(&key, &mut block);
assert_eq!(decode_hex("6bc1bee22e409f96e93d7e117393172a"), block);
block = decode_hex("591ccb10d410ed26dc5ba74a31362870");
Aes::decrypt(&key, &mut block);
assert_eq!(decode_hex("ae2d8a571e03ac9c9eb76fac45af8e51"), block);
block = decode_hex("b6ed21b99ca6f4f9f153e7b1beafed1d");
Aes::decrypt(&key, &mut block);
assert_eq!(decode_hex("30c81c46a35ce411e5fbc1191a0a52ef"), block);
block = decode_hex("23304b7a39f9f3ff067d8d8f9e24ecc7");
Aes::decrypt(&key, &mut block);
assert_eq!(decode_hex("f69f2445df4f9b17ad2b417be66c3710"), block);
fn aes_256() {
// test data from https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf F.1.5
let key = decode_hex("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4");
let plaintext = decode_hex("6bc1bee22e409f96e93d7e117393172a");
let ciphertext = decode_hex("f3eed1bdb5d2a03c064b5a7e3db181f8");
assert_eq!(ciphertext, EncryptKey::new_256(&key).encrypt(&plaintext));
assert_eq!(plaintext, DecryptKey::new_256(&key).decrypt(&ciphertext));
}
}
-61
View File
@@ -1,61 +0,0 @@
/* 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.
*/
use crate::{CSlice, ForeignType};
pub(crate) struct BigNum {
ptr: *mut bssl_sys::BIGNUM,
}
// Safety: Implementation ensures `from_ptr(x).as_ptr() == x`
unsafe impl ForeignType for BigNum {
type CType = bssl_sys::BIGNUM;
unsafe fn from_ptr(ptr: *mut Self::CType) -> Self {
Self { ptr }
}
fn as_ptr(&self) -> *mut Self::CType {
self.ptr
}
}
impl BigNum {
pub(crate) fn new() -> Self {
// Safety: There are no preconditions for BN_new()
unsafe { Self::from_ptr(bssl_sys::BN_new()) }
}
}
impl From<&[u8]> for BigNum {
fn from(value: &[u8]) -> Self {
let value_ffi = CSlice(value);
// Safety:
// - `value` is a CSlice from safe Rust.
// - The `ret` argument can be null to request allocating a new result.
let ptr = unsafe {
bssl_sys::BN_bin2bn(value_ffi.as_ptr(), value_ffi.len(), core::ptr::null_mut())
};
assert!(!ptr.is_null());
Self { ptr }
}
}
impl Drop for BigNum {
fn drop(&mut self) {
// Safety: `self.ptr` is owned by `self`.
unsafe { bssl_sys::BN_free(self.ptr) }
}
}
+4
View File
@@ -31,6 +31,7 @@
//! ```
use crate::{sealed, FfiSlice, ForeignTypeRef};
use alloc::vec::Vec;
use bssl_sys;
#[non_exhaustive]
@@ -49,6 +50,9 @@ pub trait Algorithm {
/// Gets a reference to a message digest algorithm to be used by the HKDF implementation.
#[doc(hidden)]
fn get_md(_: sealed::Sealed) -> &'static MdRef;
/// Hashes a message.
fn hash_to_vec(input: &[u8]) -> Vec<u8>;
}
/// The insecure SHA-1 hash algorithm.
+543 -370
View File
@@ -13,409 +13,582 @@
* 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.
//! Definitions of NIST elliptic curves.
//!
//! If you're looking for curve25519, see the `x25519` and `ed25519` modules.
use alloc::{borrow::ToOwned, vec, vec::Vec};
use core::{borrow::Borrow, fmt::Debug, ops::Deref, panic};
// This module is substantially internal-only and is only public for the
// [`Curve`] trait, which is shared by ECDH and ECDSA.
use crate::{bn::BigNum, CSlice, CSliceMut, ForeignType, ForeignTypeRef};
use crate::{cbb_to_buffer, parse_with_cbs, scoped, sealed, Buffer, FfiSlice};
use alloc::{fmt::Debug, vec::Vec};
use core::ptr::{null, null_mut};
#[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`].
/// An elliptic curve.
pub trait Curve: Debug {
/// The size of the affine coordinates for this curve.
const AFFINE_COORDINATE_SIZE: usize;
#[doc(hidden)]
fn group(_: sealed::Sealed) -> Group;
/// Create a new [`EcGroup`] for this curve.
fn ec_group() -> &'static EcGroupRef;
/// 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 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`.
/// The NIST P-256 curve, also called secp256r1.
#[derive(Debug)]
pub struct P256;
impl Curve for P256 {
const AFFINE_COORDINATE_SIZE: usize = 32;
fn group(_: sealed::Sealed) -> Group {
Group::P256
}
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 _) }
fn hash(data: &[u8]) -> Vec<u8> {
crate::digest::Sha256::hash(data).to_vec()
}
}
/// The P-384 curve, corresponding to `NID_secp384r1`.
/// The NIST P-384 curve, also called secp384r1.
#[derive(Debug)]
pub struct P384;
impl Curve for P384 {
const AFFINE_COORDINATE_SIZE: usize = 48;
fn group(_: sealed::Sealed) -> Group {
Group::P384
}
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 _) }
fn hash(data: &[u8]) -> Vec<u8> {
crate::digest::Sha384::hash(data).to_vec()
}
}
/// The P-521 curve, corresponding to `NID_secp521r1`.
#[derive(Debug)]
pub struct P521;
#[derive(Copy, Clone)]
#[doc(hidden)]
pub enum Group {
P256,
P384,
}
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 _) }
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 crate::ec::P521;
use super::*;
use super::{Curve, EcGroupRef, P256};
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();
#[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);
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 test_ec_group_not_equal() {
let group = P256::ec_group();
let group2 = P521::ec_group();
assert!(group != group2)
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),
);
}
}
+156 -367
View File
@@ -13,403 +13,192 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//! Elliptic Curve Diffie-Hellman operations.
//!
//! This module implements ECDH over the NIST curves P-256 and P-384.
//!
//! ```
//! use bssl_crypto::{ecdh, ec::P256};
//!
//! let alice_private_key = ecdh::PrivateKey::<P256>::generate();
//! let alice_public_key_serialized = alice_private_key.to_x962_uncompressed();
//!
//! // Somehow, Alice's public key is sent to Bob.
//! let bob_private_key = ecdh::PrivateKey::<P256>::generate();
//! let alice_public_key =
//! ecdh::PublicKey::<P256>::from_x962_uncompressed(
//! alice_public_key_serialized.as_ref())
//! .unwrap();
//! let shared_key1 = bob_private_key.compute_shared_key(&alice_public_key);
//!
//! // Likewise, Alice gets Bob's public key and computes the same shared key.
//! let bob_public_key = bob_private_key.to_public_key();
//! let shared_key2 = alice_private_key.compute_shared_key(&bob_public_key);
//! assert_eq!(shared_key1, shared_key2);
//! ```
use crate::{ec, sealed, with_output_vec, Buffer};
use alloc::vec::Vec;
use core::marker::PhantomData;
use crate::{
ec::{Curve, EcKey},
pkey::{Pkey, PkeyCtx},
CSliceMut, ForeignType,
};
pub use crate::ec::P256;
/// Private key used in a elliptic curve Diffie-Hellman.
pub struct PrivateKey<C: Curve> {
/// An EcKey containing the private-public key pair
eckey: EcKey,
/// An ECDH public key over the given curve.
pub struct PublicKey<C: ec::Curve> {
point: ec::Point,
marker: PhantomData<C>,
}
/// Error type for ECDH operations.
#[derive(Debug)]
pub enum Error {
/// Failed when trying to convert between representations.
ConversionFailed,
/// The Diffie-Hellman key exchange failed.
DiffieHellmanFailed,
}
impl<C: Curve> PrivateKey<C> {
/// Derives a shared secret from this private key and the given public key.
///
/// # Panics
/// When `OUTPUT_SIZE` is insufficient to store the output of the shared secret.
#[allow(clippy::expect_used)]
pub fn diffie_hellman<const OUTPUT_SIZE: usize>(
&self,
other_public_key: &PublicKey<C>,
) -> Result<SharedSecret<OUTPUT_SIZE>, Error> {
let pkey: Pkey = (&self.eckey).into();
let pkey_ctx = PkeyCtx::new(&pkey);
let other_pkey: Pkey = (&other_public_key.eckey).into();
let mut output = [0_u8; OUTPUT_SIZE];
pkey_ctx
.diffie_hellman(&other_pkey, CSliceMut(&mut output))
.map(|_| SharedSecret(output))
.map_err(|_| Error::DiffieHellmanFailed)
impl<C: ec::Curve> PublicKey<C> {
/// Parse a public key in uncompressed X9.62 format. (This is the common
/// format for elliptic curve points beginning with an 0x04 byte.)
pub fn from_x962_uncompressed(x962: &[u8]) -> Option<Self> {
let point = ec::Point::from_x962_uncompressed(C::group(sealed::Sealed), x962)?;
Some(Self {
point,
marker: PhantomData,
})
}
/// Generate a new private key for use in a Diffie-Hellman key exchange.
/// Serialize this key as uncompressed X9.62 format.
pub fn to_x962_uncompressed(&self) -> Buffer {
self.point.to_x962_uncompressed()
}
}
/// An ECDH private key over the given curve.
pub struct PrivateKey<C: ec::Curve> {
key: ec::Key,
marker: PhantomData<C>,
}
impl<C: ec::Curve> PrivateKey<C> {
/// Generate a random private key.
pub fn generate() -> Self {
Self {
eckey: EcKey::generate(C::ec_group()),
key: ec::Key::generate(C::group(sealed::Sealed)),
marker: PhantomData,
}
}
/// Tries to convert the given bytes into an private key.
///
/// `private_key_bytes` is the octet form that consists of the content octets of the
/// `privateKey` `OCTET STRING` in an `ECPrivateKey` ASN.1 structure.
///
/// Returns an error if the given bytes is not a valid representation of a P-256 private key.
pub fn from_private_bytes(private_key_bytes: &[u8]) -> Result<Self, Error> {
EcKey::try_from_raw_bytes(C::ec_group(), private_key_bytes)
.map(|eckey| Self {
eckey,
marker: PhantomData,
})
.map_err(|_| Error::ConversionFailed)
}
/// Serializes this private key as a big-endian integer, zero-padded to the size of key's group
/// order and returns the result.
pub fn to_bytes(&self) -> Vec<u8> {
self.eckey.to_raw_bytes()
}
}
impl<'a, C: Curve> From<&'a PrivateKey<C>> for PublicKey<C> {
fn from(value: &'a PrivateKey<C>) -> Self {
Self {
eckey: value.eckey.clone(),
/// Parse a `PrivateKey` from a zero-padded, big-endian representation of the secret scalar.
pub fn from_big_endian(scalar: &[u8]) -> Option<Self> {
let key = ec::Key::from_big_endian(C::group(sealed::Sealed), scalar)?;
Some(Self {
key,
marker: PhantomData,
})
}
/// Return the private scalar as zero-padded, big-endian bytes.
pub fn to_big_endian(&self) -> Buffer {
self.key.to_big_endian()
}
/// Parse an ECPrivateKey structure (from RFC 5915). The key must be on the
/// specified curve.
pub fn from_der_ec_private_key(der: &[u8]) -> Option<Self> {
let key = ec::Key::from_der_ec_private_key(C::group(sealed::Sealed), der)?;
Some(Self {
key,
marker: PhantomData,
})
}
/// Serialize this private key as an ECPrivateKey structure (from RFC 5915).
pub fn to_der_ec_private_key(&self) -> Buffer {
self.key.to_der_ec_private_key()
}
/// Parse a PrivateKeyInfo structure (from RFC 5208). The key must be on the
/// specified curve.
pub fn from_der_private_key_info(der: &[u8]) -> Option<Self> {
let key = ec::Key::from_der_private_key_info(C::group(sealed::Sealed), der)?;
Some(Self {
key,
marker: PhantomData,
})
}
/// Serialize this private key as a PrivateKeyInfo structure (from RFC 5208).
pub fn to_der_private_key_info(&self) -> Buffer {
self.key.to_der_private_key_info()
}
/// Serialize the _public_ part of this key in uncompressed X9.62 format.
pub fn to_x962_uncompressed(&self) -> Buffer {
self.key.to_x962_uncompressed()
}
/// Compute the shared key between this private key and the given public key.
/// The result should be used with a key derivation function that includes
/// the two public keys.
pub fn compute_shared_key(&self, other_public_key: &PublicKey<C>) -> Vec<u8> {
// 384 bits is the largest curve supported. The buffer is sized to be
// larger than this so that truncation of the output can be noticed.
let max_output = 384 / 8 + 1;
unsafe {
with_output_vec(max_output, |out_buf| {
// Safety:
// - `out_buf` points to at least `max_output` bytes, as
// required.
// - The `EC_POINT` and `EC_KEY` pointers are valid by construction.
let num_out_bytes = bssl_sys::ECDH_compute_key(
out_buf as *mut core::ffi::c_void,
max_output,
other_public_key.point.as_ffi_ptr(),
self.key.as_ffi_ptr(),
None,
);
// Out of memory is not handled by this crate.
assert!(num_out_bytes > 0);
let num_out_bytes = num_out_bytes as usize;
// If the buffer was completely filled then it was probably
// truncated, which should never happen.
assert!(num_out_bytes < max_output);
num_out_bytes
})
}
}
}
/// A public key for elliptic curve.
#[derive(Clone, Debug)]
pub struct PublicKey<C: Curve> {
/// An EcKey containing the public key
eckey: EcKey,
marker: PhantomData<C>,
}
impl<C: Curve> Eq for PublicKey<C> {}
impl<C: Curve> PartialEq for PublicKey<C> {
fn eq(&self, other: &Self) -> bool {
self.eckey.public_key_eq(&other.eckey)
}
}
impl<C: Curve> PublicKey<C> {
/// Converts this public key to its byte representation.
pub fn to_vec(&self) -> Vec<u8> {
self.eckey.to_vec()
}
/// Converts the given affine coordinates into a public key.
pub fn from_affine_coordinates<const AFFINE_COORDINATE_SIZE: usize>(
x: &[u8; AFFINE_COORDINATE_SIZE],
y: &[u8; AFFINE_COORDINATE_SIZE],
) -> Result<Self, Error> {
assert_eq!(AFFINE_COORDINATE_SIZE, C::AFFINE_COORDINATE_SIZE);
EcKey::try_new_public_key_from_affine_coordinates(C::ec_group(), &x[..], &y[..])
.map(|eckey| Self {
eckey,
marker: PhantomData,
})
.map_err(|_| Error::ConversionFailed)
}
/// Converts this public key to its affine coordinates.
pub fn to_affine_coordinates<const AFFINE_COORDINATE_SIZE: usize>(
&self,
) -> ([u8; AFFINE_COORDINATE_SIZE], [u8; AFFINE_COORDINATE_SIZE]) {
assert_eq!(AFFINE_COORDINATE_SIZE, C::AFFINE_COORDINATE_SIZE);
let (bn_x, bn_y) = self.eckey.to_affine_coordinates();
let mut x_bytes_uninit = core::mem::MaybeUninit::<[u8; AFFINE_COORDINATE_SIZE]>::uninit();
let mut y_bytes_uninit = core::mem::MaybeUninit::<[u8; AFFINE_COORDINATE_SIZE]>::uninit();
// Safety:
// - `BigNum` guarantees the validity of its ptr
// - The size of `x/y_bytes_uninit` and the length passed to `BN_bn2bin_padded` are both
// `AFFINE_COORDINATE_SIZE`
let (result_x, result_y) = unsafe {
(
bssl_sys::BN_bn2bin_padded(
x_bytes_uninit.as_mut_ptr() as *mut _,
AFFINE_COORDINATE_SIZE,
bn_x.as_ptr(),
),
bssl_sys::BN_bn2bin_padded(
y_bytes_uninit.as_mut_ptr() as *mut _,
AFFINE_COORDINATE_SIZE,
bn_y.as_ptr(),
),
)
};
assert_eq!(result_x, 1, "bssl_sys::BN_bn2bin_padded failed");
assert_eq!(result_y, 1, "bssl_sys::BN_bn2bin_padded failed");
// Safety: Fields initialized by `BN_bn2bin_padded` above.
unsafe { (x_bytes_uninit.assume_init(), y_bytes_uninit.assume_init()) }
}
}
impl<C: Curve> TryFrom<&[u8]> for PublicKey<C> {
type Error = Error;
fn try_from(value: &[u8]) -> Result<Self, Error> {
EcKey::try_new_public_key_from_bytes(C::ec_group(), value)
.map(|eckey| Self {
eckey,
marker: PhantomData,
})
.map_err(|_| Error::ConversionFailed)
}
}
/// Shared secret derived from a Diffie-Hellman key exchange. Don't use the shared key directly,
/// rather use a KDF and also include the two public values as inputs.
pub struct SharedSecret<const SIZE: usize>(pub(crate) [u8; SIZE]);
impl<const SIZE: usize> SharedSecret<SIZE> {
/// Gets a copy of the shared secret.
pub fn to_bytes(&self) -> [u8; SIZE] {
self.0
}
/// Gets a reference to the underlying data in this shared secret.
pub fn as_bytes(&self) -> &[u8; SIZE] {
&self.0
/// Return the public key corresponding to this private key.
pub fn to_public_key(&self) -> PublicKey<C> {
PublicKey {
point: self.key.to_point(),
marker: PhantomData,
}
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod tests {
use crate::{
ec::{Curve, P224, P256, P384, P521},
ecdh::{PrivateKey, PublicKey},
test_helpers::decode_hex,
};
mod test {
use super::*;
use crate::ec::{P256, P384};
#[test]
fn p224_test_diffie_hellman() {
// From wycheproof ecdh_secp224r1_ecpoint_test.json, tcId 1
// sec1 public key manually extracted from the ASN encoded test data
let public_key_bytes: [u8; 57] = decode_hex(concat!(
"047d8ac211e1228eb094e285a957d9912e93deee433ed777440ae9fc719b01d0",
"50dfbe653e72f39491be87fb1a2742daa6e0a2aada98bb1aca",
));
let private_key_bytes: [u8; 28] =
decode_hex("565577a49415ca761a0322ad54e4ad0ae7625174baf372c2816f5328");
let expected_shared_secret: [u8; 28] =
decode_hex("b8ecdb552d39228ee332bafe4886dbff272f7109edf933bc7542bd4f");
fn check_curve<C: ec::Curve>() {
let alice_private_key = PrivateKey::<C>::generate();
let alice_public_key = alice_private_key.to_public_key();
let alice_private_key =
PrivateKey::<C>::from_big_endian(alice_private_key.to_big_endian().as_ref()).unwrap();
let alice_private_key = PrivateKey::<C>::from_der_ec_private_key(
alice_private_key.to_der_ec_private_key().as_ref(),
)
.unwrap();
let public_key: PublicKey<P224> = (&public_key_bytes[..]).try_into().unwrap();
let private_key = PrivateKey::from_private_bytes(&private_key_bytes)
.expect("Input private key should be valid");
let actual_shared_secret = private_key.diffie_hellman(&public_key).unwrap();
let bob_private_key = PrivateKey::<C>::generate();
let bob_public_key = bob_private_key.to_public_key();
assert_eq!(actual_shared_secret.0, expected_shared_secret);
let shared_key1 = alice_private_key.compute_shared_key(&bob_public_key);
let shared_key2 = bob_private_key.compute_shared_key(&alice_public_key);
assert_eq!(shared_key1, shared_key2);
}
#[test]
fn p256_test_diffie_hellman() {
// From wycheproof ecdh_secp256r1_ecpoint_test.json, tcId 1
// sec1 public key manually extracted from the ASN encoded test data
let public_key_bytes: [u8; 65] = decode_hex(concat!(
"0462d5bd3372af75fe85a040715d0f502428e07046868b0bfdfa61d731afe44f",
"26ac333a93a9e70a81cd5a95b5bf8d13990eb741c8c38872b4a07d275a014e30cf",
));
let private_key_bytes: [u8; 32] =
decode_hex("0612465c89a023ab17855b0a6bcebfd3febb53aef84138647b5352e02c10c346");
let expected_shared_secret: [u8; 32] =
decode_hex("53020d908b0219328b658b525f26780e3ae12bcd952bb25a93bc0895e1714285");
let public_key: PublicKey<P256> = (&public_key_bytes[..]).try_into().unwrap();
let private_key = PrivateKey::from_private_bytes(&private_key_bytes)
.expect("Input private key should be valid");
let actual_shared_secret = private_key.diffie_hellman(&public_key).unwrap();
assert_eq!(actual_shared_secret.0, expected_shared_secret);
fn p256() {
check_curve::<P256>();
}
#[test]
fn p384_test_diffie_hellman() {
// From wycheproof ecdh_secp384r1_ecpoint_test.json, tcId 1
// sec1 public key manually extracted from the ASN encoded test data
let public_key_bytes: [u8; 97] = decode_hex(concat!(
"04790a6e059ef9a5940163183d4a7809135d29791643fc43a2f17ee8bf677ab8",
"4f791b64a6be15969ffa012dd9185d8796d9b954baa8a75e82df711b3b56eadf",
"f6b0f668c3b26b4b1aeb308a1fcc1c680d329a6705025f1c98a0b5e5bfcb163caa",
));
let private_key_bytes: [u8; 48] = decode_hex(concat!(
"766e61425b2da9f846c09fc3564b93a6f8603b7392c785165bf20da948c49fd1",
"fb1dee4edd64356b9f21c588b75dfd81"
));
let expected_shared_secret: [u8; 48] = decode_hex(concat!(
"6461defb95d996b24296f5a1832b34db05ed031114fbe7d98d098f93859866e4",
"de1e229da71fef0c77fe49b249190135"
));
let public_key: PublicKey<P384> = (&public_key_bytes[..]).try_into().unwrap();
let private_key = PrivateKey::from_private_bytes(&private_key_bytes)
.expect("Input private key should be valid");
let actual_shared_secret = private_key.diffie_hellman(&public_key).unwrap();
assert_eq!(actual_shared_secret.0, expected_shared_secret);
}
#[test]
fn p521_test_diffie_hellman() {
// From wycheproof ecdh_secp521r1_ecpoint_test.json, tcId 1
// sec1 public key manually extracted from the ASN encoded test data
let public_key_bytes: [u8; 133] = decode_hex(concat!(
"040064da3e94733db536a74a0d8a5cb2265a31c54a1da6529a198377fbd38575",
"d9d79769ca2bdf2d4c972642926d444891a652e7f492337251adf1613cf30779",
"99b5ce00e04ad19cf9fd4722b0c824c069f70c3c0e7ebc5288940dfa92422152",
"ae4a4f79183ced375afb54db1409ddf338b85bb6dbfc5950163346bb63a90a70",
"c5aba098f7",
));
let private_key_bytes: [u8; 66] = decode_hex(concat!(
"01939982b529596ce77a94bc6efd03e92c21a849eb4f87b8f619d506efc9bb22",
"e7c61640c90d598f795b64566dc6df43992ae34a1341d458574440a7371f611c",
"7dcd"
));
let expected_shared_secret: [u8; 66] = decode_hex(concat!(
"01f1e410f2c6262bce6879a3f46dfb7dd11d30eeee9ab49852102e1892201dd1",
"0f27266c2cf7cbccc7f6885099043dad80ff57f0df96acf283fb090de53df95f",
"7d87",
));
let public_key: PublicKey<P521> = (&public_key_bytes[..]).try_into().unwrap();
let private_key = PrivateKey::from_private_bytes(&private_key_bytes)
.expect("Input private key should be valid");
let actual_shared_secret = private_key.diffie_hellman(&public_key).unwrap();
assert_eq!(actual_shared_secret.0, expected_shared_secret);
}
#[test]
fn p224_generate_diffie_hellman_matches() {
generate_diffie_hellman_matches::<P224, 28>()
}
#[test]
fn p256_generate_diffie_hellman_matches() {
generate_diffie_hellman_matches::<P256, 32>()
}
#[test]
fn p384_generate_diffie_hellman_matches() {
generate_diffie_hellman_matches::<P384, 48>()
}
#[test]
fn p521_generate_diffie_hellman_matches() {
generate_diffie_hellman_matches::<P521, 66>()
}
fn generate_diffie_hellman_matches<C: Curve, const OUTPUT_SIZE: usize>() {
let private_key_1 = PrivateKey::<C>::generate();
let private_key_2 = PrivateKey::<C>::generate();
let public_key_1 = PublicKey::from(&private_key_1);
let public_key_2 = PublicKey::from(&private_key_2);
let diffie_hellman_1 = private_key_1
.diffie_hellman::<OUTPUT_SIZE>(&public_key_2)
.unwrap();
let diffie_hellman_2 = private_key_2
.diffie_hellman::<OUTPUT_SIZE>(&public_key_1)
.unwrap();
assert_eq!(diffie_hellman_1.to_bytes(), diffie_hellman_2.to_bytes());
}
#[test]
fn p224_to_private_bytes() {
let private_key_bytes: [u8; 28] =
decode_hex("565577a49415ca761a0322ad54e4ad0ae7625174baf372c2816f5328");
let private_key = PrivateKey::<P224>::from_private_bytes(&private_key_bytes)
.expect("Input private key should be valid");
assert_eq!(&private_key.to_bytes()[..], &private_key_bytes[..]);
}
#[test]
fn p256_to_private_bytes() {
let private_key_bytes: [u8; 32] =
decode_hex("0612465c89a023ab17855b0a6bcebfd3febb53aef84138647b5352e02c10c346");
let private_key = PrivateKey::<P256>::from_private_bytes(&private_key_bytes)
.expect("Input private key should be valid");
assert_eq!(&private_key.to_bytes()[..], &private_key_bytes[..]);
}
#[test]
fn p384_to_private_bytes() {
let private_key_bytes: [u8; 48] = decode_hex(concat!(
"766e61425b2da9f846c09fc3564b93a6f8603b7392c785165bf20da948c49fd1",
"fb1dee4edd64356b9f21c588b75dfd81"
));
let private_key = PrivateKey::<P384>::from_private_bytes(&private_key_bytes)
.expect("Input private key should be valid");
assert_eq!(&private_key.to_bytes()[..], &private_key_bytes[..]);
}
#[test]
fn p521_to_private_bytes() {
let private_key_bytes: [u8; 66] = decode_hex(concat!(
"01939982b529596ce77a94bc6efd03e92c21a849eb4f87b8f619d506efc9bb22",
"e7c61640c90d598f795b64566dc6df43992ae34a1341d458574440a7371f611c",
"7dcd",
));
let private_key = PrivateKey::<P521>::from_private_bytes(&private_key_bytes)
.expect("Input private key should be valid");
assert_eq!(&private_key.to_bytes()[..], &private_key_bytes[..]);
}
#[test]
fn p224_affine_coordinates_test() {
affine_coordinates_test::<P224, { P224::AFFINE_COORDINATE_SIZE }>();
}
#[test]
fn p256_affine_coordinates_test() {
affine_coordinates_test::<P256, { P256::AFFINE_COORDINATE_SIZE }>();
}
#[test]
fn p384_affine_coordinates_test() {
affine_coordinates_test::<P384, { P384::AFFINE_COORDINATE_SIZE }>();
}
#[test]
fn p521_affine_coordinates_test() {
affine_coordinates_test::<P521, { P521::AFFINE_COORDINATE_SIZE }>();
}
fn affine_coordinates_test<C: Curve, const AFFINE_COORDINATE_SIZE: usize>() {
let private_key = PrivateKey::<C>::generate();
let public_key = PublicKey::from(&private_key);
let (x, y) = public_key.to_affine_coordinates::<AFFINE_COORDINATE_SIZE>();
let recreated_public_key = PublicKey::from_affine_coordinates(&x, &y);
assert_eq!(public_key, recreated_public_key.unwrap());
fn p384() {
check_curve::<P384>();
}
}
+249
View File
@@ -0,0 +1,249 @@
/* Copyright (c) 2024, 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.
*/
//! Elliptic Curve Digital Signature Algorithm.
//!
//! The module implements ECDSA for the NIST curves P-256 and P-384.
//!
//! ```
//! use bssl_crypto::{ecdsa, ec::P256};
//!
//! let key = ecdsa::PrivateKey::<P256>::generate();
//! // Publish your public key.
//! let public_key_bytes = key.to_der_subject_public_key_info();
//!
//! // Sign and publish some message.
//! let signed_message = b"hello world";
//! let mut sig = key.sign(signed_message);
//!
//! // Anyone with the public key can verify it.
//! let public_key = ecdsa::PublicKey::<P256>::from_der_subject_public_key_info(
//! public_key_bytes.as_ref()).unwrap();
//! assert!(public_key.verify(signed_message, sig.as_slice()).is_ok());
//! ```
use crate::{ec, sealed, with_output_vec, Buffer, FfiSlice, InvalidSignatureError};
use alloc::vec::Vec;
use core::marker::PhantomData;
/// An ECDSA public key over the given curve.
pub struct PublicKey<C: ec::Curve> {
point: ec::Point,
marker: PhantomData<C>,
}
impl<C: ec::Curve> PublicKey<C> {
/// Parse a public key in uncompressed X9.62 format. (This is the common
/// format for elliptic curve points beginning with an 0x04 byte.)
pub fn from_x962_uncompressed(x962: &[u8]) -> Option<Self> {
let point = ec::Point::from_x962_uncompressed(C::group(sealed::Sealed), x962)?;
Some(Self {
point,
marker: PhantomData,
})
}
/// Serialize this key as uncompressed X9.62 format.
pub fn to_x962_uncompressed(&self) -> Buffer {
self.point.to_x962_uncompressed()
}
/// Parse a public key in SubjectPublicKeyInfo format.
/// (This is found in, e.g., X.509 certificates.)
pub fn from_der_subject_public_key_info(spki: &[u8]) -> Option<Self> {
let point = ec::Point::from_der_subject_public_key_info(C::group(sealed::Sealed), spki)?;
Some(Self {
point,
marker: PhantomData,
})
}
/// Serialize this key in SubjectPublicKeyInfo format.
pub fn to_der_subject_public_key_info(&self) -> Buffer {
self.point.to_der_subject_public_key_info()
}
/// Verify `signature` as a valid signature of a digest of `signed_msg`
/// with this public key. SHA-256 will be used to produce the digest if the
/// curve of this public key is P-256. SHA-384 will be used to produce the
/// digest if the curve of this public key is P-384.
pub fn verify(&self, signed_msg: &[u8], signature: &[u8]) -> Result<(), InvalidSignatureError> {
let digest = C::hash(signed_msg);
let result = self.point.with_point_as_ec_key(|ec_key| unsafe {
// Safety: `ec_key` is valid per `with_point_as_ec_key`.
bssl_sys::ECDSA_verify(
/*type=*/ 0,
digest.as_slice().as_ffi_ptr(),
digest.len(),
signature.as_ffi_ptr(),
signature.len(),
ec_key,
)
});
if result == 1 {
Ok(())
} else {
Err(InvalidSignatureError)
}
}
}
/// An ECDH private key over the given curve.
pub struct PrivateKey<C: ec::Curve> {
key: ec::Key,
marker: PhantomData<C>,
}
impl<C: ec::Curve> PrivateKey<C> {
/// Generate a random private key.
pub fn generate() -> Self {
Self {
key: ec::Key::generate(C::group(sealed::Sealed)),
marker: PhantomData,
}
}
/// Parse a `PrivateKey` from a zero-padded, big-endian representation of the secret scalar.
pub fn from_big_endian(scalar: &[u8]) -> Option<Self> {
let key = ec::Key::from_big_endian(C::group(sealed::Sealed), scalar)?;
Some(Self {
key,
marker: PhantomData,
})
}
/// Return the private key as zero-padded, big-endian bytes.
pub fn to_big_endian(&self) -> Buffer {
self.key.to_big_endian()
}
/// Parse an ECPrivateKey structure (from RFC 5915). The key must be on the
/// specified curve.
pub fn from_der_ec_private_key(der: &[u8]) -> Option<Self> {
let key = ec::Key::from_der_ec_private_key(C::group(sealed::Sealed), der)?;
Some(Self {
key,
marker: PhantomData,
})
}
/// Serialize this private key as an ECPrivateKey structure (from RFC 5915).
pub fn to_der_ec_private_key(&self) -> Buffer {
self.key.to_der_ec_private_key()
}
/// Parse a PrivateKeyInfo structure (from RFC 5208), commonly called
/// "PKCS#8 format". The key must be on the specified curve.
pub fn from_der_private_key_info(der: &[u8]) -> Option<Self> {
let key = ec::Key::from_der_private_key_info(C::group(sealed::Sealed), der)?;
Some(Self {
key,
marker: PhantomData,
})
}
/// Serialize this private key as a PrivateKeyInfo structure (from RFC 5208),
/// commonly called "PKCS#8 format".
pub fn to_der_private_key_info(&self) -> Buffer {
self.key.to_der_private_key_info()
}
/// Serialize the _public_ part of this key in uncompressed X9.62 format.
pub fn to_x962_uncompressed(&self) -> Buffer {
self.key.to_x962_uncompressed()
}
/// Serialize this key in SubjectPublicKeyInfo format.
pub fn to_der_subject_public_key_info(&self) -> Buffer {
self.key.to_der_subject_public_key_info()
}
/// Return the public key corresponding to this private key.
pub fn to_public_key(&self) -> PublicKey<C> {
PublicKey {
point: self.key.to_point(),
marker: PhantomData,
}
}
/// Sign a digest of `to_be_signed` using this key and return the signature.
/// SHA-256 will be used to produce the digest if the curve of this public
/// key is P-256. SHA-384 will be used to produce the digest if the curve
/// of this public key is P-384.
pub fn sign(&self, to_be_signed: &[u8]) -> Vec<u8> {
// Safety: `self.key` is valid by construction.
let max_size = unsafe { bssl_sys::ECDSA_size(self.key.as_ffi_ptr()) };
// No curve can be empty.
assert_ne!(max_size, 0);
let digest = C::hash(to_be_signed);
unsafe {
with_output_vec(max_size, |out_buf| {
let mut out_len: core::ffi::c_uint = 0;
// Safety: `out_buf` points to at least `max_size` bytes,
// as required.
let result = {
bssl_sys::ECDSA_sign(
/*type=*/ 0,
digest.as_slice().as_ffi_ptr(),
digest.len(),
out_buf,
&mut out_len,
self.key.as_ffi_ptr(),
)
};
// Signing should never fail unless we're out of memory,
// which this crate doesn't handle.
assert_eq!(result, 1);
let out_len = out_len as usize;
assert!(out_len <= max_size);
// Safety: `out_len` bytes have been written.
out_len
})
}
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::ec::{P256, P384};
fn check_curve<C: ec::Curve>() {
let signed_message = b"hello world";
let key = PrivateKey::<C>::generate();
let mut sig = key.sign(signed_message);
let public_key = PublicKey::<C>::from_der_subject_public_key_info(
key.to_der_subject_public_key_info().as_ref(),
)
.unwrap();
assert!(public_key.verify(signed_message, sig.as_slice()).is_ok());
sig[10] ^= 1;
assert!(public_key.verify(signed_message, sig.as_slice()).is_err());
}
#[test]
fn p256() {
check_curve::<P256>();
}
#[test]
fn p384() {
check_curve::<P384>();
}
}
+87 -81
View File
@@ -13,108 +13,126 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
use crate::CSlice;
//! Ed25519, a signature scheme.
//!
//! Ed25519 builds a signature scheme over a curve that is isogenous to
//! curve25519. This module provides the "pure" signature scheme described in
//! <https://datatracker.ietf.org/doc/html/rfc8032>.
//!
//! ```
//! use bssl_crypto::ed25519;
//!
//! let key = ed25519::PrivateKey::generate();
//! // Publish your public key.
//! let public_key_bytes = *key.to_public().as_bytes();
//!
//! // Sign and publish some message.
//! let signed_message = b"hello world";
//! let mut sig = key.sign(signed_message);
//!
//! // Anyone with the public key can verify it.
//! let public_key = ed25519::PublicKey::from_bytes(&public_key_bytes);
//! assert!(public_key.verify(signed_message, &sig).is_ok());
//! ```
use crate::{FfiMutSlice, FfiSlice, InvalidSignatureError};
/// The length in bytes of an Ed25519 public key.
pub const PUBLIC_KEY_LENGTH: usize = bssl_sys::ED25519_PUBLIC_KEY_LEN as usize;
pub const PUBLIC_KEY_LEN: usize = bssl_sys::ED25519_PUBLIC_KEY_LEN as usize;
/// The length in bytes of an Ed25519 seed which is the 32-byte private key representation defined
/// in RFC 8032.
pub const SEED_LENGTH: usize =
/// The length in bytes of an Ed25519 seed which is the 32-byte private key
/// representation defined in RFC 8032.
pub const SEED_LEN: usize =
(bssl_sys::ED25519_PRIVATE_KEY_LEN - bssl_sys::ED25519_PUBLIC_KEY_LEN) as usize;
/// The length in bytes of an Ed25519 signature.
pub const SIGNATURE_LENGTH: usize = bssl_sys::ED25519_SIGNATURE_LEN as usize;
pub const SIGNATURE_LEN: usize = bssl_sys::ED25519_SIGNATURE_LEN as usize;
// The length in bytes of an Ed25519 keypair. In BoringSSL, the private key is suffixed with the
// public key, so the keypair length is the same as the private key length.
const KEYPAIR_LENGTH: usize = bssl_sys::ED25519_PRIVATE_KEY_LEN as usize;
const KEYPAIR_LEN: usize = bssl_sys::ED25519_PRIVATE_KEY_LEN as usize;
/// An Ed25519 private key.
pub struct PrivateKey([u8; KEYPAIR_LENGTH]);
/// An Ed25519 signature created by signing a message with a private key.
pub struct Signature([u8; SIGNATURE_LENGTH]);
pub struct PrivateKey([u8; KEYPAIR_LEN]);
/// An Ed25519 public key used to verify a signature + message.
pub struct PublicKey([u8; PUBLIC_KEY_LENGTH]);
pub struct PublicKey([u8; PUBLIC_KEY_LEN]);
/// Error returned if the verification on the signature + message fails.
#[derive(Debug)]
pub struct SignatureError;
/// An Ed25519 signature created by signing a message with a private key.
pub type Signature = [u8; SIGNATURE_LEN];
impl PrivateKey {
/// Generates a new Ed25519 keypair.
pub fn generate() -> Self {
let mut public_key = [0u8; PUBLIC_KEY_LENGTH];
let mut private_key = [0u8; KEYPAIR_LENGTH];
let mut public_key = [0u8; PUBLIC_KEY_LEN];
let mut private_key = [0u8; KEYPAIR_LEN];
// Safety:
// - Public key and private key are the correct length.
unsafe { bssl_sys::ED25519_keypair(public_key.as_mut_ptr(), private_key.as_mut_ptr()) }
unsafe {
bssl_sys::ED25519_keypair(public_key.as_mut_ffi_ptr(), private_key.as_mut_ffi_ptr())
}
PrivateKey(private_key)
}
/// Converts the key-pair to an array of bytes consisting of the bytes of the private key
/// followed by the bytes of the public key.
pub fn to_seed(&self) -> [u8; SEED_LENGTH] {
/// Returns the "seed" of this private key, as defined in RFC 8032.
pub fn to_seed(&self) -> [u8; SEED_LEN] {
// This code will never panic because a length 32 slice will always fit into a
// size 32 byte array. The private key is the first 32 bytes of the keypair.
#[allow(clippy::expect_used)]
self.0[..SEED_LENGTH].try_into().expect(
"A slice of length SEED_LENGTH will always fit into an array of length SEED_LENGTH",
)
self.0[..SEED_LEN]
.try_into()
.expect("A slice of length SEED_LEN will always fit into an array of length SEED_LEN")
}
/// Builds this key-pair from `seed`, which is the 32-byte private key representation defined
/// Derives a key-pair from `seed`, which is the 32-byte private key representation defined
/// in RFC 8032.
pub fn new_from_seed(seed: &[u8; SEED_LENGTH]) -> Self {
let mut public_key = [0u8; PUBLIC_KEY_LENGTH];
let mut private_key = [0u8; KEYPAIR_LENGTH];
pub fn from_seed(seed: &[u8; SEED_LEN]) -> Self {
let mut public_key = [0u8; PUBLIC_KEY_LEN];
let mut private_key = [0u8; KEYPAIR_LEN];
// Safety:
// - Public key, private key, and seed are the correct lengths.
unsafe {
bssl_sys::ED25519_keypair_from_seed(
public_key.as_mut_ptr(),
private_key.as_mut_ptr(),
seed.as_ptr(),
public_key.as_mut_ffi_ptr(),
private_key.as_mut_ffi_ptr(),
seed.as_ffi_ptr(),
)
}
PrivateKey(private_key)
}
/// Signs the given message and returns a digital signature.
/// Signs the given message and returns the signature.
pub fn sign(&self, msg: &[u8]) -> Signature {
let mut sig_bytes = [0u8; SIGNATURE_LENGTH];
let mut sig_bytes = [0u8; SIGNATURE_LEN];
let msg_ffi = CSlice(msg);
// Safety:
// - On allocation failure we panic.
// - Signature and private keys are always the correct length.
let result = unsafe {
bssl_sys::ED25519_sign(
sig_bytes.as_mut_ptr(),
msg_ffi.as_ptr(),
msg_ffi.len(),
self.0.as_ptr(),
sig_bytes.as_mut_ffi_ptr(),
msg.as_ffi_ptr(),
msg.len(),
self.0.as_ffi_ptr(),
)
};
assert_eq!(result, 1, "allocation failure in bssl_sys::ED25519_sign");
Signature(sig_bytes)
sig_bytes
}
/// Returns the PublicKey of the KeyPair.
pub fn public(&self) -> PublicKey {
let keypair_bytes = self.0;
/// Returns the [`PublicKey`] corresponding to this private key.
pub fn to_public(&self) -> PublicKey {
let keypair_bytes = &self.0;
// This code will never panic because a length 32 slice will always fit into a
// size 32 byte array. The public key is the last 32 bytes of the keypair.
#[allow(clippy::expect_used)]
PublicKey(
keypair_bytes[PUBLIC_KEY_LENGTH..]
keypair_bytes[PUBLIC_KEY_LEN..]
.try_into()
.expect("The slice is always the correct size for a public key"),
)
@@ -123,78 +141,66 @@ impl PrivateKey {
impl PublicKey {
/// Builds the public key from an array of bytes.
pub fn from_bytes(bytes: [u8; PUBLIC_KEY_LENGTH]) -> Self {
PublicKey(bytes)
pub fn from_bytes(bytes: &[u8; PUBLIC_KEY_LEN]) -> Self {
PublicKey(*bytes)
}
/// Returns the bytes of the public key.
pub fn to_bytes(&self) -> [u8; PUBLIC_KEY_LENGTH] {
self.0
pub fn as_bytes(&self) -> &[u8; PUBLIC_KEY_LEN] {
&self.0
}
/// Succeeds if the signature is a valid signature created by this keypair, otherwise returns an Error.
pub fn verify(&self, message: &[u8], signature: Signature) -> Result<(), SignatureError> {
let message_cslice = CSlice::from(message);
/// Verifies that `signature` is a valid signature, by this key, of `msg`.
pub fn verify(&self, msg: &[u8], signature: &Signature) -> Result<(), InvalidSignatureError> {
let ret = unsafe {
// Safety: `self.0` is the correct length and other buffers are valid.
bssl_sys::ED25519_verify(
message_cslice.as_ptr(),
message_cslice.len(),
signature.0.as_ptr(),
self.0.as_ptr(),
msg.as_ffi_ptr(),
msg.len(),
signature.as_ffi_ptr(),
self.0.as_ffi_ptr(),
)
};
if ret == 1 {
Ok(())
} else {
Err(SignatureError)
Err(InvalidSignatureError)
}
}
}
impl Signature {
/// Creates a signature from a byte array.
pub fn from_bytes(bytes: [u8; SIGNATURE_LENGTH]) -> Self {
Self(bytes)
}
/// Returns the bytes of the signature.
pub fn to_bytes(&self) -> [u8; SIGNATURE_LENGTH] {
self.0
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::test_helpers;
#[test]
fn ed25519_kp_gen_roundtrip() {
fn gen_roundtrip() {
let private_key = PrivateKey::generate();
assert_ne!([0u8; 64], private_key.0);
let seed = private_key.to_seed();
let new_private_key = PrivateKey::new_from_seed(&seed);
let new_private_key = PrivateKey::from_seed(&seed);
assert_eq!(private_key.0, new_private_key.0);
}
#[test]
fn ed25519_empty_msg() {
fn empty_msg() {
// Test Case 1 from RFC test vectors: https://www.rfc-editor.org/rfc/rfc8032#section-7.1
let pk = test_helpers::decode_hex(
"d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a",
);
let sk = test_helpers::decode_hex(
let seed = test_helpers::decode_hex(
"9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60",
);
let msg = [0u8; 0];
let sig_expected = test_helpers::decode_hex("e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b");
let kp = PrivateKey::new_from_seed(&sk);
let kp = PrivateKey::from_seed(&seed);
let sig = kp.sign(&msg);
assert_eq!(sig_expected, sig.0);
assert_eq!(sig_expected, sig);
let pub_key = PublicKey::from_bytes(pk);
assert_eq!(pub_key.to_bytes(), kp.public().to_bytes());
assert!(pub_key.verify(&msg, sig).is_ok());
let pub_key = PublicKey::from_bytes(&pk);
assert_eq!(pub_key.as_bytes(), kp.to_public().as_bytes());
assert!(pub_key.verify(&msg, &sig).is_ok());
}
#[test]
@@ -208,13 +214,13 @@ mod test {
);
let msg: [u8; 14] = test_helpers::decode_hex("55c7fa434f5ed8cdec2b7aeac173");
let sig_expected = test_helpers::decode_hex("6ee3fe81e23c60eb2312b2006b3b25e6838e02106623f844c44edb8dafd66ab0671087fd195df5b8f58a1d6e52af42908053d55c7321010092748795ef94cf06");
let kp = PrivateKey::new_from_seed(&sk);
let kp = PrivateKey::from_seed(&sk);
let sig = kp.sign(&msg);
assert_eq!(sig_expected, sig.0);
assert_eq!(sig_expected, sig);
let pub_key = PublicKey::from_bytes(pk);
assert_eq!(pub_key.to_bytes(), kp.public().to_bytes());
assert!(pub_key.verify(&msg, sig).is_ok());
let pub_key = PublicKey::from_bytes(&pk);
assert_eq!(pub_key.as_bytes(), kp.to_public().as_bytes());
assert!(pub_key.verify(&msg, &sig).is_ok());
}
}
+2 -2
View File
@@ -30,7 +30,7 @@
//! ```
//! use bssl_crypto::hmac::HmacSha256;
//!
//! let key = [0u8; 32];
//! let key = bssl_crypto::rand_array();
//! let mut ctx = HmacSha256::new(&key);
//! ctx.update(b"hel");
//! ctx.update(b"lo");
@@ -46,7 +46,7 @@
//! ```
//! use bssl_crypto::hmac::HmacSha256;
//!
//! let key = [0u8; 32];
//! let key = bssl_crypto::rand_array();
//! let mut keyed_ctx = HmacSha256::new(&key);
//! let mut ctx1 = keyed_ctx.clone();
//! ctx1.update(b"foo");
+150 -23
View File
@@ -24,51 +24,43 @@
#![cfg_attr(not(any(feature = "std", test)), no_std)]
//! Rust BoringSSL bindings
extern crate alloc;
extern crate alloc;
extern crate core;
use alloc::vec::Vec;
use core::ffi::c_void;
#[macro_use]
mod macros;
/// Authenticated Encryption with Additional Data algorithms.
pub mod aead;
/// AES block operations.
pub mod aes;
/// Ciphers.
pub mod cipher;
pub mod digest;
/// Ed25519, a signature scheme.
pub mod ec;
pub mod ecdh;
pub mod ecdsa;
pub mod ed25519;
pub mod hkdf;
pub mod hmac;
/// Random number generation.
pub mod rand;
pub mod rsa;
pub mod x25519;
/// Memory-manipulation operations.
pub mod mem;
/// Elliptic curve diffie-hellman operations.
pub mod ecdh;
pub(crate) mod bn;
pub(crate) mod ec;
pub(crate) mod pkey;
mod scoped;
#[cfg(test)]
mod test_helpers;
mod mem;
pub use mem::constant_time_compare;
mod rand;
pub use rand::{rand_array, rand_bytes};
/// Error type for when a "signature" (either a public-key signature or a MAC)
/// is incorrect.
#[derive(Debug)]
@@ -266,6 +258,24 @@ where
unsafe { out_uninit.assume_init() }
}
/// Returns a BoringSSL structure that is initialized by some function.
/// Requires that the given function completely initializes the value or else
/// returns false.
///
/// (Tagged `unsafe` because a no-op argument would otherwise expose
/// uninitialized memory.)
unsafe fn initialized_struct_fallible<T, F>(init: F) -> Option<T>
where
F: FnOnce(*mut T) -> bool,
{
let mut out_uninit = core::mem::MaybeUninit::<T>::uninit();
if init(out_uninit.as_mut_ptr()) {
Some(unsafe { out_uninit.assume_init() })
} else {
None
}
}
/// Wrap a closure that initializes an output buffer and return that buffer as
/// an array. Requires that the closure fully initialize the given buffer.
///
@@ -294,7 +304,7 @@ where
/// Safety: the closure must fully initialize the array if it returns one.
unsafe fn with_output_array_fallible<const N: usize, F>(func: F) -> Option<[u8; N]>
where
F: FnOnce(*mut u8, usize) -> core::ffi::c_int,
F: FnOnce(*mut u8, usize) -> bool,
{
let mut out_uninit = core::mem::MaybeUninit::<[u8; N]>::uninit();
let out_ptr = if N != 0 {
@@ -302,7 +312,7 @@ where
} else {
core::ptr::null_mut()
};
if func(out_ptr, N) == 1 {
if func(out_ptr, N) {
// Safety: `func` promises to fill all of `out_uninit` if it returns one.
unsafe { Some(out_uninit.assume_init()) }
} else {
@@ -310,6 +320,123 @@ where
}
}
/// Wrap a closure that writes at most `max_output` bytes to fill a vector.
/// It must return the number of bytes written.
#[allow(clippy::unwrap_used)]
unsafe fn with_output_vec<F>(max_output: usize, func: F) -> Vec<u8>
where
F: FnOnce(*mut u8) -> usize,
{
unsafe {
with_output_vec_fallible(max_output, |out_buf| Some(func(out_buf)))
// The closure cannot fail and thus neither can
// `with_output_array_fallible`.
.unwrap()
}
}
/// Wrap a closure that writes at most `max_output` bytes to fill a vector.
/// If successful, it must return the number of bytes written.
unsafe fn with_output_vec_fallible<F>(max_output: usize, func: F) -> Option<Vec<u8>>
where
F: FnOnce(*mut u8) -> Option<usize>,
{
let mut ret = Vec::with_capacity(max_output);
let out = ret.spare_capacity_mut();
let out_buf = out
.get_mut(0)
.map_or(core::ptr::null_mut(), |x| x.as_mut_ptr());
let num_written = func(out_buf)?;
assert!(num_written <= ret.capacity());
unsafe {
// Safety: `num_written` bytes have been written to.
ret.set_len(num_written);
}
Some(ret)
}
/// Buffer represents an owned chunk of memory on the BoringSSL heap.
/// Call `as_ref()` to get a `&[u8]` from it.
pub struct Buffer {
// This pointer is always allocated by BoringSSL and must be freed using
// `OPENSSL_free`.
pub(crate) ptr: *mut u8,
pub(crate) len: usize,
}
impl AsRef<[u8]> for Buffer {
fn as_ref(&self) -> &[u8] {
if self.len == 0 {
return &[];
}
// Safety: `ptr` and `len` describe a valid area of memory and `ptr`
// must be Rust-valid because `len` is non-zero.
unsafe { core::slice::from_raw_parts(self.ptr, self.len) }
}
}
impl Drop for Buffer {
fn drop(&mut self) {
// Safety: `ptr` is owned by this object and is on the BoringSSL heap.
unsafe {
bssl_sys::OPENSSL_free(self.ptr as *mut core::ffi::c_void);
}
}
}
/// Calls `parse_func` with a `CBS` structure pointing at `data`.
/// If that returns a null pointer then it returns [None].
/// Otherwise, if there's still data left in CBS, it calls `free_func` on the
/// pointer and returns [None]. Otherwise it returns the pointer.
fn parse_with_cbs<T, Parse, Free>(data: &[u8], free_func: Free, parse_func: Parse) -> Option<*mut T>
where
Parse: FnOnce(*mut bssl_sys::CBS) -> *mut T,
Free: FnOnce(*mut T),
{
// Safety: type checking ensures that `cbs` is the correct size.
let mut cbs =
unsafe { initialized_struct(|cbs| bssl_sys::CBS_init(cbs, data.as_ffi_ptr(), data.len())) };
let ptr = parse_func(&mut cbs);
if ptr.is_null() {
return None;
}
// Safety: `cbs` is still valid after parsing.
if unsafe { bssl_sys::CBS_len(&cbs) } != 0 {
// Safety: `ptr` is still owned by this function.
free_func(ptr);
return None;
}
Some(ptr)
}
/// Calls `func` with a `CBB` pointer and returns a [Buffer] of the ultimate
/// contents of that CBB.
#[allow(clippy::unwrap_used)]
fn cbb_to_buffer<F: FnOnce(*mut bssl_sys::CBB)>(initial_capacity: usize, func: F) -> Buffer {
// Safety: type checking ensures that `cbb` is the correct size.
let mut cbb = unsafe {
initialized_struct_fallible(|cbb| bssl_sys::CBB_init(cbb, initial_capacity) == 1)
}
// `CBB_init` only fails if out of memory, which isn't something that this crate handles.
.unwrap();
func(&mut cbb);
let mut ptr: *mut u8 = core::ptr::null_mut();
let mut len: usize = 0;
// `CBB_finish` only fails on programming error, which we convert into a
// panic.
assert_eq!(1, unsafe {
bssl_sys::CBB_finish(&mut cbb, &mut ptr, &mut len)
});
// Safety: `ptr` is on the BoringSSL heap and ownership is returned by
// `CBB_finish`.
Buffer { ptr, len }
}
/// Used to prevent external implementations of internal traits.
mod sealed {
pub struct Sealed;
+48
View File
@@ -31,6 +31,10 @@ macro_rules! unsafe_iuf_algo {
// - this always returns a valid pointer to an EVP_MD.
unsafe { MdRef::from_ptr(bssl_sys::$evp_md() as *mut _) }
}
fn hash_to_vec(input: &[u8]) -> Vec<u8> {
Self::hash(input).as_slice().to_vec()
}
}
impl $name {
@@ -103,3 +107,47 @@ macro_rules! unsafe_iuf_algo {
}
};
}
macro_rules! aead_algo {
($name:ident, $evp_md:ident, $key_len:expr, $nonce_len:expr, $tag_len:expr) => {
impl $name {
/// Create a new AEAD context for the given key.
pub fn new(key: &[u8; $key_len]) -> Self {
// Safety: $evp_md is assumed to return a valid `EVP_MD`.
unsafe { $name(EvpAead::new(key, { bssl_sys::$evp_md() })) }
}
}
impl Aead for $name {
type Tag = [u8; $tag_len];
type Nonce = [u8; $nonce_len];
fn seal(&self, nonce: &Self::Nonce, plaintext: &[u8], ad: &[u8]) -> Vec<u8> {
self.0.seal(nonce, plaintext, ad)
}
fn seal_in_place(
&self,
nonce: &Self::Nonce,
plaintext: &mut [u8],
ad: &[u8],
) -> Self::Tag {
self.0.seal_in_place(nonce, plaintext, ad)
}
fn open(&self, nonce: &Self::Nonce, ciphertext: &[u8], ad: &[u8]) -> Option<Vec<u8>> {
self.0.open(nonce, ciphertext, ad)
}
fn open_in_place(
&self,
nonce: &Self::Nonce,
ciphertext: &mut [u8],
tag: &Self::Tag,
ad: &[u8],
) -> Result<(), InvalidCiphertext> {
self.0.open_in_place(nonce, ciphertext, tag, ad)
}
}
};
}
+10 -8
View File
@@ -13,21 +13,23 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
use crate::FfiSlice;
/// Returns true iff `a` and `b` contain the same bytes. It takes an amount of time dependent on the
/// lengths, but independent of the contents of the slices `a` and `b`. The return type is a `bool`,
/// since unlike `memcmp` in C this function cannot be used to put elements into a defined order.
pub fn crypto_memcmp(a: &[u8], b: &[u8]) -> bool {
pub fn constant_time_compare(a: &[u8], b: &[u8]) -> bool {
if a.len() != b.len() {
return false;
}
if a.is_empty() && b.is_empty() {
if a.is_empty() {
// Avoid FFI issues with empty slices that may potentially cause UB
return true;
}
// Safety:
// - The lengths of a and b are checked above.
let result =
unsafe { bssl_sys::CRYPTO_memcmp(a.as_ptr() as *const _, b.as_ptr() as *const _, a.len()) };
unsafe { bssl_sys::CRYPTO_memcmp(a.as_ffi_void_ptr(), b.as_ffi_void_ptr(), a.len()) };
result == 0
}
@@ -37,26 +39,26 @@ mod test {
#[test]
fn test_different_length() {
assert!(!crypto_memcmp(&[0, 1, 2], &[0]))
assert!(!constant_time_compare(&[0, 1, 2], &[0]))
}
#[test]
fn test_same_length_different_content() {
assert!(!crypto_memcmp(&[0, 1, 2], &[1, 2, 3]))
assert!(!constant_time_compare(&[0, 1, 2], &[1, 2, 3]))
}
#[test]
fn test_same_content() {
assert!(crypto_memcmp(&[0, 1, 2], &[0, 1, 2]))
assert!(constant_time_compare(&[0, 1, 2], &[0, 1, 2]))
}
#[test]
fn test_empty_slices() {
assert!(crypto_memcmp(&[], &[]))
assert!(constant_time_compare(&[], &[]))
}
#[test]
fn test_empty_slices_different() {
assert!(!crypto_memcmp(&[], &[0, 1, 2]))
assert!(!constant_time_compare(&[], &[0, 1, 2]))
}
}
-102
View File
@@ -1,102 +0,0 @@
/* 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.
*/
//! `Pkey` and `PkeyCtx` classes for holding asymmetric keys. This module is intended for internal
//! use within this crate only, to create higher-level abstractions suitable to be exposed
//! externally.
use crate::{ec::EcKey, CSliceMut, ForeignType};
use alloc::{borrow::ToOwned, string::String};
pub(crate) struct Pkey {
ptr: *mut bssl_sys::EVP_PKEY,
}
// Safety: Implementation ensures `from_ptr(x).as_ptr == x`
unsafe impl ForeignType for Pkey {
type CType = bssl_sys::EVP_PKEY;
unsafe fn from_ptr(ptr: *mut Self::CType) -> Self {
Self { ptr }
}
fn as_ptr(&self) -> *mut Self::CType {
self.ptr
}
}
impl From<&EcKey> for Pkey {
fn from(eckey: &EcKey) -> Self {
// Safety: EVP_PKEY_new does not have any preconditions
let pkey = unsafe { bssl_sys::EVP_PKEY_new() };
assert!(!pkey.is_null());
// Safety:
// - pkey is just allocated and is null-checked
// - EcKey ensures eckey.ptr is valid during its lifetime
// - EVP_PKEY_set1_EC_KEY doesn't take ownership
let result = unsafe { bssl_sys::EVP_PKEY_set1_EC_KEY(pkey, eckey.as_ptr()) };
assert_eq!(result, 1, "bssl_sys::EVP_PKEY_set1_EC_KEY failed");
Self { ptr: pkey }
}
}
impl Drop for Pkey {
fn drop(&mut self) {
// Safety: `self.ptr` is owned by this struct
unsafe { bssl_sys::EVP_PKEY_free(self.ptr) }
}
}
pub(crate) struct PkeyCtx {
ptr: *mut bssl_sys::EVP_PKEY_CTX,
}
impl PkeyCtx {
pub fn new(pkey: &Pkey) -> Self {
// Safety:
// - `Pkey` ensures `pkey.ptr` is valid, and EVP_PKEY_CTX_new does not take ownership.
let pkeyctx = unsafe { bssl_sys::EVP_PKEY_CTX_new(pkey.ptr, core::ptr::null_mut()) };
assert!(!pkeyctx.is_null());
Self { ptr: pkeyctx }
}
#[allow(clippy::panic)]
pub(crate) fn diffie_hellman(
self,
other_public_key: &Pkey,
mut output: CSliceMut,
) -> Result<(), String> {
let result = unsafe { bssl_sys::EVP_PKEY_derive_init(self.ptr) };
assert_eq!(result, 1, "bssl_sys::EVP_PKEY_derive_init failed");
let result = unsafe { bssl_sys::EVP_PKEY_derive_set_peer(self.ptr, other_public_key.ptr) };
assert_eq!(result, 1, "bssl_sys::EVP_PKEY_derive_set_peer failed");
let result =
unsafe { bssl_sys::EVP_PKEY_derive(self.ptr, output.as_mut_ptr(), &mut output.len()) };
match result {
0 => Err("bssl_sys::EVP_PKEY_derive failed".to_owned()),
1 => Ok(()),
_ => panic!("Unexpected result {result:?} from bssl_sys::EVP_PKEY_derive"),
}
}
}
impl Drop for PkeyCtx {
fn drop(&mut self) {
// Safety: self.ptr is owned by this struct
unsafe { bssl_sys::EVP_PKEY_CTX_free(self.ptr) }
}
}
+36 -9
View File
@@ -13,29 +13,56 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
use crate::CSliceMut;
//! Getting random bytes.
/// Fills buf with random bytes. In the event that sufficient random data can not be obtained,
/// BoringSSL will abort, so the assert will never be hit.
use crate::{with_output_array, FfiMutSlice};
/// Fills `buf` with random bytes.
pub fn rand_bytes(buf: &mut [u8]) {
let mut ffi_buf = CSliceMut::from(buf);
let result = unsafe { bssl_sys::RAND_bytes(ffi_buf.as_mut_ptr(), ffi_buf.len()) };
assert_eq!(result, 1, "BoringSSL RAND_bytes API failed unexpectedly");
// Safety: `RAND_bytes` writes exactly `buf.len()` bytes.
let ret = unsafe { bssl_sys::RAND_bytes(buf.as_mut_ffi_ptr(), buf.len()) };
// BoringSSL's `RAND_bytes` always succeeds returning 1, or crashes the
// address space if the PRNG can not provide random data.
debug_assert!(ret == 1);
}
/// Returns an array of random bytes.
pub fn rand_array<const N: usize>() -> [u8; N] {
unsafe {
with_output_array(|out, out_len| {
// Safety: `RAND_bytes` writes exactly `out_len` bytes, as required.
let ret = bssl_sys::RAND_bytes(out, out_len);
// BoringSSL RAND_bytes always succeeds returning 1, or crashes the
// address space if the PRNG can not provide random data.
debug_assert!(ret == 1);
})
}
}
#[cfg(test)]
mod tests {
use super::rand_bytes;
use super::*;
#[test]
fn test_rand_bytes() {
fn fill() {
let mut buf = [0; 32];
rand_bytes(&mut buf);
}
#[test]
fn test_rand_bytes_empty() {
fn fill_empty() {
let mut buf = [];
rand_bytes(&mut buf);
}
#[test]
fn array() {
let _rand: [u8; 32] = rand_array();
}
#[test]
fn empty_array() {
let _rand: [u8; 0] = rand_array();
}
}
+329
View File
@@ -0,0 +1,329 @@
/* Copyright (c) 2024, 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.
*/
//! RSA signatures.
//!
//! New protocols should not use RSA, but it's still often found in existing
//! protocols. This module implements PKCS#1 signatures (the most common type).
//!
//! Creating a signature:
//!
//! ```
//! use bssl_crypto::{digest, rsa};
//! # use bssl_crypto::rsa::TEST_PKCS8_BYTES;
//!
//! // Generating an RSA private key is slow, so this examples parses it from
//! // PKCS#8 DER.
//! let private_key = rsa::PrivateKey::from_der_private_key_info(TEST_PKCS8_BYTES).unwrap();
//! let signed_msg = b"hello world";
//! let sig = private_key.sign_pkcs1::<digest::Sha256>(signed_msg);
//! ```
//!
//! To verify a signature, publish your _public_ key:
//!
//! ```
//! # use bssl_crypto::{rsa};
//! # use bssl_crypto::rsa::TEST_PKCS8_BYTES;
//! # let private_key = rsa::PrivateKey::from_der_private_key_info(TEST_PKCS8_BYTES).unwrap();
//! let public_key_bytes = private_key.as_public().to_der_subject_public_key_info();
//! ```
//!
//! Then verify the signature from above with it:
//!
//! ```
//! # use bssl_crypto::{digest, rsa};
//! # use bssl_crypto::rsa::TEST_PKCS8_BYTES;
//! # let private_key = rsa::PrivateKey::from_der_private_key_info(TEST_PKCS8_BYTES).unwrap();
//! # let signed_msg = b"hello world";
//! # let mut sig = private_key.sign_pkcs1::<digest::Sha256>(signed_msg);
//! # let public_key_bytes = private_key.as_public().to_der_subject_public_key_info();
//! let public_key = rsa::PublicKey::from_der_subject_public_key_info(public_key_bytes.as_ref())
//! .unwrap();
//! assert!(public_key.verify_pkcs1::<digest::Sha256>(signed_msg, sig.as_slice()).is_ok());
//! sig[0] ^= 1;
//! assert!(public_key.verify_pkcs1::<digest::Sha256>(signed_msg, sig.as_slice()).is_err());
//! ```
use crate::{
cbb_to_buffer, digest, parse_with_cbs, scoped, sealed, with_output_vec, Buffer, FfiSlice,
ForeignTypeRef, InvalidSignatureError,
};
use alloc::vec::Vec;
use core::ptr::null_mut;
/// An RSA public key.
pub struct PublicKey(*mut bssl_sys::RSA);
impl PublicKey {
/// Parse a DER-encoded RSAPublicKey structure (from RFC 8017).
pub fn from_der_rsa_public_key(der: &[u8]) -> Option<Self> {
Some(PublicKey(parse_with_cbs(
der,
// Safety: `ptr` is a non-null result from `RSA_parse_public_key` here.
|ptr| unsafe { bssl_sys::RSA_free(ptr) },
// Safety: cbs is valid per `parse_with_cbs`.
|cbs| unsafe { bssl_sys::RSA_parse_public_key(cbs) },
)?))
}
/// Serialize to a DER-encoded RSAPublicKey structure (from RFC 8017).
pub fn to_der_rsa_public_key(&self) -> Buffer {
cbb_to_buffer(/*initial_capacity=*/ 300, |cbb| unsafe {
// Safety: `self.0` is valid by construction.
assert_eq!(1, bssl_sys::RSA_marshal_public_key(cbb, self.0))
})
}
/// Parse a DER-encoded SubjectPublicKeyInfo. This format is found in,
/// for example, X.509 certificates.
pub fn from_der_subject_public_key_info(spki: &[u8]) -> Option<Self> {
let mut pkey = scoped::EvpPkey::from_ptr(parse_with_cbs(
spki,
// Safety: `pkey` is a non-null result from `EVP_parse_public_key` here.
|pkey| unsafe { bssl_sys::EVP_PKEY_free(pkey) },
// Safety: cbs is valid per `parse_with_cbs`.
|cbs| unsafe { bssl_sys::EVP_parse_public_key(cbs) },
)?);
let rsa = unsafe { bssl_sys::EVP_PKEY_get1_RSA(pkey.as_ffi_ptr()) };
if !rsa.is_null() {
// Safety: `EVP_PKEY_get1_RSA` adds a reference so we are not
// stealing ownership from `pkey`.
Some(PublicKey(rsa))
} else {
None
}
}
/// Serialize to a DER-encoded SubjectPublicKeyInfo. This format is found
/// in, for example, X.509 certificates.
pub fn to_der_subject_public_key_info(&self) -> Buffer {
let mut pkey = scoped::EvpPkey::new();
// Safety: this takes a reference to `self.0` and so doesn't steal ownership.
assert_eq!(1, unsafe {
bssl_sys::EVP_PKEY_set1_RSA(pkey.as_ffi_ptr(), self.0)
});
cbb_to_buffer(384, |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()));
})
}
/// Verify that `signature` is a valid signature of a digest of
/// `signed_msg`, by this public key. The digest of the message will be
/// computed with the specified hash function.
pub fn verify_pkcs1<Hash: digest::Algorithm>(
&self,
signed_msg: &[u8],
signature: &[u8],
) -> Result<(), InvalidSignatureError> {
let digest = Hash::hash_to_vec(signed_msg);
// Safety: `get_md` always returns a valid pointer.
let hash_nid = unsafe { bssl_sys::EVP_MD_nid(Hash::get_md(sealed::Sealed).as_ptr()) };
let result = unsafe {
// Safety: all buffers are valid and `self.0` is valid by construction.
bssl_sys::RSA_verify(
hash_nid,
digest.as_slice().as_ffi_ptr(),
digest.len(),
signature.as_ffi_ptr(),
signature.len(),
self.0,
)
};
if result == 1 {
Ok(())
} else {
Err(InvalidSignatureError)
}
}
}
// Safety:
//
// An `RSA` is safe to use from multiple threads so long as no mutating
// operations are performed. (Reference count changes don't count as mutating.)
// No mutating operations are performed. `RSA_verify` takes a non-const pointer
// but the BoringSSL docs specifically say that "these functions are considered
// non-mutating for thread-safety purposes and may be used concurrently."
unsafe impl Sync for PublicKey {}
unsafe impl Send for PublicKey {}
impl Drop for PublicKey {
fn drop(&mut self) {
// Safety: this object owns `self.0`.
unsafe { bssl_sys::RSA_free(self.0) }
}
}
/// The set of supported RSA key sizes for key generation.
#[allow(missing_docs)]
pub enum KeySize {
Rsa2048 = 2048,
Rsa3072 = 3072,
Rsa4096 = 4096,
}
/// An RSA private key.
pub struct PrivateKey(*mut bssl_sys::RSA);
impl PrivateKey {
/// Generate a fresh RSA private key of the given size.
pub fn generate(size: KeySize) -> Self {
let e = scoped::Bignum::from_u64(bssl_sys::RSA_F4 as u64);
let ptr = unsafe { bssl_sys::RSA_new() };
assert!(!ptr.is_null());
let result = unsafe {
// Safety: `rsa` and `e` are valid and initialized, just above.
bssl_sys::RSA_generate_key_ex(ptr, size as core::ffi::c_int, e.as_ffi_ptr(), null_mut())
};
assert_eq!(1, result);
// Safety: this function owns `ptr` and thus can move ownership here.
Self(ptr)
}
/// Parse a DER-encoded RSAPrivateKey structure (from RFC 8017).
pub fn from_der_rsa_private_key(der: &[u8]) -> Option<Self> {
Some(PrivateKey(parse_with_cbs(
der,
// Safety: `ptr` is a non-null result from `RSA_parse_private_key` here.
|ptr| unsafe { bssl_sys::RSA_free(ptr) },
// Safety: `cbs` is valid per `parse_with_cbs`.
|cbs| unsafe { bssl_sys::RSA_parse_private_key(cbs) },
)?))
}
/// Serialize to a DER-encoded RSAPrivateKey structure (from RFC 8017).
pub fn to_der_rsa_private_key(&self) -> Buffer {
cbb_to_buffer(/*initial_capacity=*/ 512, |cbb| unsafe {
// Safety: `self.0` is valid by construction.
assert_eq!(1, bssl_sys::RSA_marshal_private_key(cbb, self.0))
})
}
/// Parse a DER-encrypted PrivateKeyInfo struct (from RFC 5208). This is often called "PKCS#8 format".
pub fn from_der_private_key_info(der: &[u8]) -> Option<Self> {
let mut pkey = scoped::EvpPkey::from_ptr(parse_with_cbs(
der,
// Safety: `ptr` is a non-null result from `EVP_parse_private_key` here.
|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) },
)?);
// Safety: `pkey` is valid and was created just above.
let rsa = unsafe { bssl_sys::EVP_PKEY_get1_RSA(pkey.as_ffi_ptr()) };
if rsa.is_null() {
return None;
}
Some(Self(rsa))
}
/// Serialize to a DER-encrypted PrivateKeyInfo struct (from RFC 5208). This is often called "PKCS#8 format".
pub fn to_der_private_key_info(&self) -> Buffer {
let mut pkey = scoped::EvpPkey::new();
assert_eq!(1, unsafe {
// Safety: `pkey` was just constructed. This takes a reference and
// so doesn't steal ownership from `self`.
bssl_sys::EVP_PKEY_set1_RSA(pkey.as_ffi_ptr(), self.0)
});
unsafe {
cbb_to_buffer(/*initial_capacity=*/ 384, |cbb| {
// Safety: `pkey` is valid and owned by this function.
assert_eq!(1, bssl_sys::EVP_marshal_private_key(cbb, pkey.as_ffi_ptr()));
})
}
}
/// Compute the signature of the digest of `to_be_signed` with PKCS#1 using
/// this private key. The specified hash function is used to compute the
// digest.
pub fn sign_pkcs1<Hash: digest::Algorithm>(&self, to_be_signed: &[u8]) -> Vec<u8> {
let digest = Hash::hash_to_vec(to_be_signed);
// Safety: `get_md` always returns a valid pointer.
let hash_nid = unsafe { bssl_sys::EVP_MD_nid(Hash::get_md(sealed::Sealed).as_ptr()) };
let max_output = unsafe { bssl_sys::RSA_size(self.0) } as usize;
unsafe {
with_output_vec(max_output, |out_buf| {
let mut out_len: core::ffi::c_uint = 0;
// Safety: `out_buf` points to at least `RSA_size` bytes, as
// required. `self.0` is valid by construction.
let result = bssl_sys::RSA_sign(
hash_nid,
digest.as_slice().as_ffi_ptr(),
digest.len(),
out_buf,
&mut out_len,
self.0,
);
// `RSA_sign` should always be successful unless it's out of
// memory, which this crate doesn't handle.
assert_eq!(1, result);
let out_len = out_len as usize;
assert!(out_len <= max_output);
// Safety: `out_len` bytes have been written.
out_len
})
}
}
/// Return the public key corresponding to this private key.
pub fn as_public(&self) -> PublicKey {
// Safety: `self.0` is valid by construction and `RSA_up_ref` means
// we we can pass an ownership reference to `PublicKey`.
unsafe { bssl_sys::RSA_up_ref(self.0) };
PublicKey(self.0)
}
}
// Safety:
//
// An `RSA` is safe to use from multiple threads so long as no mutating
// operations are performed. (Reference count changes don't count as mutating.)
// No mutating operations are performed. `RSA_sign` takes a non-const pointer
// but the BoringSSL docs specifically say that "these functions are considered
// non-mutating for thread-safety purposes and may be used concurrently."
unsafe impl Sync for PrivateKey {}
unsafe impl Send for PrivateKey {}
impl Drop for PrivateKey {
fn drop(&mut self) {
// Safety: `self.0` is always owned by this struct.
unsafe { bssl_sys::RSA_free(self.0) }
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::digest;
#[test]
fn sign_and_verify() {
let key = PrivateKey::from_der_private_key_info(TEST_PKCS8_BYTES).unwrap();
let signed_msg = b"hello world";
let sig = key.sign_pkcs1::<digest::Sha256>(signed_msg);
assert!(key
.as_public()
.verify_pkcs1::<digest::Sha256>(signed_msg, &sig)
.is_ok());
}
}
// RSA generation is slow, but serialized keys are large. In order to use this
// in doctests, it's included here and public, but undocumented.
#[doc(hidden)]
pub const TEST_PKCS8_BYTES: &[u8] = b"\x30\x82\x04\xbd\x02\x01\x00\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x04\x82\x04\xa7\x30\x82\x04\xa3\x02\x01\x00\x02\x82\x01\x01\x00\x98\x3f\xf5\xc4\x89\xb7\x6f\x12\xc8\xb5\x82\xaa\x98\xe6\x75\x39\xc4\x44\x46\xa0\x45\x62\x42\x43\x21\x81\xa0\x53\x17\x47\xb3\xdc\xfc\x3b\x76\x03\xd6\xd4\xce\x5e\x9d\x22\xe5\xa3\x59\xa2\x47\x0c\xe4\x82\x33\x7a\x21\xa5\x61\x2a\x77\xa2\x6b\xfa\xa3\x45\x41\x50\xc2\xf7\x0d\xe1\xa6\x3a\x83\x5b\xe6\xb8\x1f\x24\x1e\x24\x89\xf8\x8d\xde\x5f\xf1\x50\x27\x0f\x2b\xbe\x58\xaa\x64\x67\xef\x22\x57\x1e\xf4\x3f\x2e\xba\x4b\x2f\xc3\x5e\x67\xcc\xc3\xf6\xdd\x6b\x31\x58\xb9\xbd\x7b\xf9\x23\xac\xf2\xa9\xb6\x8f\x88\x75\x0f\x73\xdf\xd2\x14\xaa\x41\x28\x5c\x9a\xd6\xc4\xab\x6f\xb0\x53\xb9\x0a\x2c\xfb\x56\x6e\x56\x94\xaa\x1a\x25\x29\x3b\x01\x0c\x7e\x44\x1b\xe1\x76\x12\x73\xc4\x16\x62\x64\x3d\xe6\xf7\x9f\x69\x3f\xc9\x3b\x75\xd6\x80\xee\x87\x68\x83\xde\x2d\x18\xe4\x26\xdd\x1a\x02\xd8\xd2\x1d\xb6\xf1\x71\xf5\x63\x62\x0c\xd7\x35\x21\xc6\x75\xb4\xd5\x0f\x89\x08\x17\x13\x24\x07\xc2\x7c\x73\xe2\x17\x00\x12\x8a\xc9\x39\xdb\xf0\xc8\x6f\x1f\xf7\x99\xed\x8c\x67\x9c\xf2\x30\x5c\xd0\xd0\x0d\xc1\x15\x07\xa3\x1d\xf5\xd4\x92\x82\xfd\x9c\x5a\x11\x69\x3b\x02\x03\x01\x00\x01\x02\x82\x01\x00\x44\xe1\x5a\xfd\x8a\x18\xd5\x45\xb8\x4c\x76\x4b\x5c\x55\x97\x5f\x85\x2e\x26\x8d\xc8\x16\x46\x48\x3c\xd6\x7a\x84\x5d\x19\xf1\x83\xdf\x11\xbf\xb8\xc8\xef\x0a\x56\xbf\xdc\xd3\xeb\xed\x57\x7f\xb1\x93\x88\x5c\x65\xba\xe7\x29\x68\x9f\x2b\x7a\x92\xb0\x5f\x5a\xc7\x81\x0d\x68\xd8\x57\xee\x4d\x13\xbc\xf4\x3c\x12\x89\x18\x9a\xdb\x3a\xc4\x0a\xc0\x10\x35\x3b\xa5\xdc\xbe\x1c\x88\xc4\x84\xea\x12\x64\x4c\xb8\x71\x19\x93\x7e\x8e\x73\x1d\x9f\x04\x61\xa1\x97\x27\x82\x2e\xb6\x4d\x6a\x4f\xfb\xa4\xe5\xa7\x54\x94\xb5\xf1\x41\xc8\xa4\x3d\xa1\xe6\x4a\xf0\xdb\xbb\xc2\x91\x26\x9a\x0f\xbf\xdd\x57\x1e\x83\x5c\x9a\x7b\x28\x53\x1d\x2d\x44\x91\x1f\x02\x81\x7b\x6f\xb5\xf7\x48\x7d\xa0\x12\x22\xdb\xbf\xd9\x04\x17\xe4\x97\xf2\xac\x32\xf8\x70\xfa\x75\xe3\x5a\xb0\xef\x1f\x2d\x24\xb9\x26\x83\x33\xe7\x3c\x3c\xfb\x0b\xd8\x70\x33\x76\xb1\x1c\x1d\x38\x06\x0a\xdb\xbd\xd2\x34\x5e\xe6\xb1\x6f\x5d\x8f\x18\xac\x94\xd2\x0d\xee\x39\x0b\xa3\xb4\xcf\xf1\xe1\x91\x30\xcb\xce\xa5\x2f\xa9\xcc\x4f\xee\xe4\xdd\xee\x8a\x77\x0e\xd1\xbd\xcc\xb0\x11\x55\x15\x5e\x99\xf1\x02\x81\x81\x00\xd1\x75\x33\xe4\x31\xc2\xfc\x09\x6c\xf6\x04\x97\xc7\xa3\xb1\x88\x36\x26\xd8\x4e\x86\x2d\xb8\x99\x68\x97\xd8\x0b\xc6\xc3\xe7\x58\x49\xc3\x41\xcd\xcd\x33\x09\xa0\x90\xb2\x77\xfa\xa3\xb6\x71\x09\x33\x43\x0a\x6a\xd8\xc3\x36\xaf\xa9\x11\x54\x64\x77\x82\xf4\xf1\xe0\x12\x5a\xb8\x9f\x5a\x04\xb3\x29\xd4\xc6\xba\x4c\xdc\x04\x97\xfb\xb6\x7e\x1b\x89\x09\x0c\x8a\xb8\x6c\x9f\x2b\x91\x0d\x34\x18\x39\xf3\x38\xf9\xe6\xed\x29\x48\x30\xe4\x3c\x09\x15\x33\xe0\xb8\x2f\xd8\xfa\xf2\x6d\x1f\xf1\xee\x02\xc2\xb4\xf9\xf4\x63\x4b\xa5\x02\x81\x81\x00\xba\x14\x89\xff\x65\xb5\xe6\x52\x45\x23\x37\x5e\x0c\x62\xde\xe9\x7f\xa9\x05\xee\x28\x0d\x91\xb1\x99\xd6\x8b\xf8\x58\x50\x8b\xb1\xee\x57\xbd\x2b\x7b\xf0\x25\x03\xeb\xbc\x87\x73\xc8\xbf\x57\x16\xda\x49\x7a\x79\x82\x25\x99\x46\x9c\xb3\xd2\xd5\xb0\xae\xec\xeb\xbc\xd2\x4b\xae\xd0\x0a\x54\xcd\xad\x44\x90\x74\x79\xa2\x34\x73\x8a\x3a\x6c\x0b\x13\x20\x5d\xa4\xcc\x7b\xb4\x64\xcf\x61\x6e\xdf\xc1\x8c\xd4\x84\x22\xf1\x19\x32\x6d\xf1\x6f\xe1\x1e\xa5\xf6\x20\x6c\xc6\xa8\x9c\x4d\x8d\x59\xdf\x90\x71\x67\x1a\x48\xa3\x4b\x5f\x02\x81\x81\x00\x97\x4d\x8f\x7f\x7e\x86\xb8\x23\x62\xe7\x50\x28\x07\xd9\x72\x4b\xcf\xba\x3d\xb4\x73\x6e\xa1\x93\x87\x9f\x70\x3c\x09\x87\xc8\x1c\xd9\xa3\xc7\x6c\x0f\x97\x97\x93\xba\x12\x81\x62\xb7\x51\xf9\xd3\x48\x89\x5c\x04\x14\xb2\xe7\x54\xfa\xce\xfe\xe4\x58\x04\x6c\x46\x30\xb3\x71\x7f\x3d\xf4\xfb\xc2\x24\x2c\x84\xa5\x5d\x11\xed\xeb\x8f\xb3\xa2\xe2\xe7\x19\x77\x4a\xd9\xaf\xf5\x46\xb6\x50\x10\x5a\x93\xb9\xe3\x65\x79\xef\xc5\x4b\x55\xad\xf8\xc4\x22\xe1\xc7\xa9\xa5\x3e\x9a\xff\xf5\xde\x06\x98\x04\xbc\x7b\x98\xb7\x75\xe6\xd5\x02\x81\x80\x0a\x7f\x38\x1d\xa9\x2e\x2e\xb4\xfb\x63\x76\x2f\x1f\x01\xc0\xd3\x69\x39\x2e\xb5\x75\x9a\xf6\x5a\x0f\x74\x93\xe6\xc9\x8c\x99\xa4\xca\xee\x36\x24\xaa\xd4\x2c\x32\x61\x6c\xfc\x33\x22\xe2\xf0\x55\xc0\xb0\x9e\x71\x16\x4f\x6a\xab\x1a\x11\xe6\xd5\xd9\x26\xb5\x04\xc3\x5d\x15\x99\xe1\xf0\x83\x42\x2b\x01\x10\x29\x11\xe7\x7d\x8f\xfa\xff\x3a\xb3\x11\x3c\x25\x2c\x33\xc0\xd2\xb7\x51\x1f\x8c\xf2\xa0\x67\x82\x61\x85\xdb\x15\xf1\xcb\x53\xf0\x5c\xc1\xae\xd9\x08\x91\x3a\x4f\xae\xa9\x8d\x4c\xc1\x98\xd3\x5c\xde\x95\xb4\x68\x7f\x02\x81\x80\x7d\x3e\x6b\x2c\x16\xe8\x17\x2c\x27\x9c\xc5\xc5\xfb\x30\x1a\xf7\x32\x53\x93\xfe\xc1\xa0\x5d\xac\x7d\x6f\xba\x1b\x56\x7e\x34\xf6\xa7\x91\x1f\x39\x84\x1c\x94\x58\x13\xe2\xb9\xec\xb6\x24\xfe\x76\x35\x1b\xcc\x4f\x8e\x0d\x88\x5b\x5a\x6f\xb6\xa2\x0b\xc3\xb6\x98\x2d\xca\xce\xce\x26\xb4\x36\x37\x42\xa4\xc0\xa9\x85\x57\x4b\x6b\xc2\xed\x14\x96\xe5\xbc\x2b\x83\x32\xe9\x83\x24\x7f\x85\x74\x09\x3c\xfa\x45\xfd\x21\xeb\xd8\xa3\x02\xd2\x70\x0a\x9a\x9d\x7d\xe4\x39\xc4\x59\xc8\x16\x6f\xce\xd5\x1d\xea\x91\x4d\x12\x78\xc3\x30";
+89
View File
@@ -0,0 +1,89 @@
/* Copyright (c) 2024, 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.
*/
//! Helpers to ensure that some temporary objects are always freed.
use crate::initialized_struct;
/// A scoped `EC_KEY`.
pub struct EvpPkey(*mut bssl_sys::EVP_PKEY);
impl EvpPkey {
pub fn new() -> Self {
let ptr = unsafe { bssl_sys::EVP_PKEY_new() };
// `ptr` is only NULL if we're out of memory, which this crate
// doesn't handle.
assert!(!ptr.is_null());
EvpPkey(ptr)
}
pub fn from_ptr(ptr: *mut bssl_sys::EVP_PKEY) -> Self {
EvpPkey(ptr)
}
pub fn as_ffi_ptr(&mut self) -> *mut bssl_sys::EVP_PKEY {
self.0
}
}
impl Drop for EvpPkey {
fn drop(&mut self) {
unsafe { bssl_sys::EVP_PKEY_free(self.0) }
}
}
/// A scoped `EC_KEY`.
pub struct EcKey(*mut bssl_sys::EC_KEY);
impl EcKey {
pub fn new() -> Self {
let ptr = unsafe { bssl_sys::EC_KEY_new() };
// `ptr` is only NULL if we're out of memory, which this crate
// doesn't handle.
assert!(!ptr.is_null());
EcKey(ptr)
}
pub fn as_ffi_ptr(&mut self) -> *mut bssl_sys::EC_KEY {
self.0
}
}
impl Drop for EcKey {
fn drop(&mut self) {
unsafe { bssl_sys::EC_KEY_free(self.0) }
}
}
/// A scoped `BIGNUM`.
pub struct Bignum(bssl_sys::BIGNUM);
impl Bignum {
pub fn from_u64(value: u64) -> Self {
let mut ret = Bignum(unsafe { initialized_struct(|ptr| bssl_sys::BN_init(ptr)) });
assert_eq!(1, unsafe { bssl_sys::BN_set_u64(&mut ret.0, value) });
ret
}
pub unsafe fn as_ffi_ptr(&self) -> *const bssl_sys::BIGNUM {
&self.0
}
}
impl Drop for Bignum {
fn drop(&mut self) {
unsafe { bssl_sys::BN_free(&mut self.0) }
}
}
+1 -1
View File
@@ -76,7 +76,7 @@ impl PrivateKey {
// Safety: `X25519` indeed writes `SHARED_KEY_LEN` bytes.
unsafe {
with_output_array_fallible(|out, _| {
bssl_sys::X25519(out, self.0.as_ffi_ptr(), other_public_key.as_ffi_ptr())
bssl_sys::X25519(out, self.0.as_ffi_ptr(), other_public_key.as_ffi_ptr()) == 1
})
}
}
+1 -1
View File
@@ -4,7 +4,7 @@ version = "0.1.0"
authors = ["Benjamin Brittain <bwb@google.com>"]
edition = "2018"
publish = false
license = "MIT"
license = "ISC"
# This exists to workaround a limitation in cargo:
# https://github.com/rust-lang/cargo/issues/3544
@@ -1287,14 +1287,15 @@ _CET_ENDBR
$L$128_dec_start:
vzeroupper
vmovdqa xmm0,XMMWORD[rdx]
vmovdqu xmm15,XMMWORD[16+rdx]
vpor xmm15,xmm15,XMMWORD[OR_MASK]
mov rax,rdx
lea rax,[32+rax]
lea rcx,[32+rcx]
vmovdqu xmm15,XMMWORD[r9*1+rdi]
vpor xmm15,xmm15,XMMWORD[OR_MASK]
and r9,~15
@@ -2583,14 +2584,15 @@ _CET_ENDBR
$L$256_dec_start:
vzeroupper
vmovdqa xmm0,XMMWORD[rdx]
vmovdqu xmm15,XMMWORD[16+rdx]
vpor xmm15,xmm15,XMMWORD[OR_MASK]
mov rax,rdx
lea rax,[32+rax]
lea rcx,[32+rcx]
vmovdqu xmm15,XMMWORD[r9*1+rdi]
vpor xmm15,xmm15,XMMWORD[OR_MASK]
and r9,~15