Skip to content

Commit

Permalink
Add support for setting the nonce type and digest on a PKEY_CTX
Browse files Browse the repository at this point in the history
  • Loading branch information
facutuesca committed Feb 5, 2024
1 parent 745b200 commit 8f11103
Show file tree
Hide file tree
Showing 4 changed files with 186 additions and 0 deletions.
19 changes: 19 additions & 0 deletions openssl-sys/src/handwritten/evp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,12 @@ extern "C" {
#[cfg(ossl300)]
pub fn EVP_PKEY_CTX_set_signature_md(ctx: *mut EVP_PKEY_CTX, md: *const EVP_MD) -> c_int;

#[cfg(ossl300)]
pub fn EVP_PKEY_CTX_set_params(ctx: *mut EVP_PKEY_CTX, params: *const OSSL_PARAM) -> c_int;

#[cfg(ossl300)]
pub fn EVP_PKEY_CTX_get_params(ctx: *mut EVP_PKEY_CTX, params: *mut OSSL_PARAM) -> c_int;

pub fn EVP_PKEY_new_mac_key(
type_: c_int,
e: *mut ENGINE,
Expand Down Expand Up @@ -646,3 +652,16 @@ extern "C" {
pub fn EVP_EncodeBlock(dst: *mut c_uchar, src: *const c_uchar, src_len: c_int) -> c_int;
pub fn EVP_DecodeBlock(dst: *mut c_uchar, src: *const c_uchar, src_len: c_int) -> c_int;
}

extern "C" {
#[cfg(ossl300)]
pub fn OSSL_PARAM_construct_uint(key: *const c_char, buf: *mut c_uint) -> OSSL_PARAM;
#[cfg(ossl300)]
pub fn OSSL_PARAM_construct_utf8_string(
key: *const c_char,
buf: *mut c_char,
bsize: size_t,
) -> OSSL_PARAM;
#[cfg(ossl300)]
pub fn OSSL_PARAM_construct_end() -> OSSL_PARAM;
}
10 changes: 10 additions & 0 deletions openssl-sys/src/handwritten/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1093,3 +1093,13 @@ pub enum OSSL_PROVIDER {}

#[cfg(ossl300)]
pub enum OSSL_LIB_CTX {}

#[cfg(ossl300)]
#[repr(C)]
pub struct OSSL_PARAM {
key: *const c_char,
data_type: c_uchar,
data: *mut c_void,
data_size: size_t,
return_size: size_t,
}
3 changes: 3 additions & 0 deletions openssl/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@

* `Cipher::aes_128_ofb` is now available on BoringSSL
* `Nid::{BRAINPOOL_P256R1,BRAINPOOL_P320R1,BRAINPOOL_P384R1,BRAINPOOL_P512R1}` are now available on LibreSSL.
* Added `PkeyCtxRef::{digest, set_digest, nonce_type, set_nonce_type}`.
* Added `OSSL_PARAM`, `OSSL_PARAM_construct_uint` , `OSSL_PARAM_construct_utf8_string`, `OSSL_PARAM_construct_end` to openssl-sys.
* Added `EVP_PKEY_CTX_set_params` and `EVP_PKEY_CTX_get_params` to openssl-sys.

## [v0.10.62] - 2023-12-22

Expand Down
154 changes: 154 additions & 0 deletions openssl/src/pkey_ctx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ let cmac_key = ctx.keygen().unwrap();
#[cfg(not(boringssl))]
use crate::cipher::CipherRef;
use crate::error::ErrorStack;
#[cfg(ossl300)]
use crate::hash::MessageDigest;
use crate::md::MdRef;
use crate::pkey::{HasPrivate, HasPublic, Id, PKey, PKeyRef, Private};
use crate::rsa::Padding;
Expand All @@ -75,8 +77,12 @@ use crate::{cvt, cvt_p};
use foreign_types::{ForeignType, ForeignTypeRef};
#[cfg(not(boringssl))]
use libc::c_int;
#[cfg(ossl320)]
use libc::c_uint;
use openssl_macros::corresponds;
use std::convert::TryFrom;
#[cfg(ossl300)]
use std::ffi::CString;
use std::ptr;

/// HKDF modes of operation.
Expand Down Expand Up @@ -105,6 +111,21 @@ impl HkdfMode {
pub const EXPAND_ONLY: Self = HkdfMode(ffi::EVP_PKEY_HKDEF_MODE_EXPAND_ONLY);
}

/// Nonce type for ECDSA and DSA.
#[cfg(ossl320)]
#[derive(Debug, PartialEq)]
pub struct NonceType(c_uint);

#[cfg(ossl320)]
impl NonceType {
/// This is the default mode. It uses a random value for the nonce k as defined in FIPS 186-4 Section 6.3
/// “Secret Number Generation”.
pub const RANDOM_K: Self = NonceType(0);

/// Uses a deterministic value for the nonce k as defined in RFC #6979 (See Section 3.2 “Generation of k”).
pub const DETERMINISTIC_K: Self = NonceType(1);
}

generic_foreign_type_and_impl_send_sync! {
type CType = ffi::EVP_PKEY_CTX;
fn drop = ffi::EVP_PKEY_CTX_free;
Expand Down Expand Up @@ -714,6 +735,109 @@ impl<T> PkeyCtxRef<T> {
Ok(PKey::from_ptr(key))
}
}

/// Sets the digest algorithm for a private key context.
///
/// Requires OpenSSL 3.0.0 or newer.
#[cfg(ossl300)]
#[corresponds(EVP_PKEY_CTX_set_params)]
pub fn set_digest(&mut self, hash_algorithm: MessageDigest) -> Result<(), ErrorStack> {
let digest_name = hash_algorithm.type_().short_name()?;
let digest = CString::new(digest_name).unwrap().into_raw();
let digest_field_name = CString::new("digest").unwrap();
unsafe {
let param_digest = ffi::OSSL_PARAM_construct_utf8_string(
digest_field_name.as_ptr(),
digest,
digest_name.len(),
);
let param_end = ffi::OSSL_PARAM_construct_end();

let params = [param_digest, param_end];
cvt(ffi::EVP_PKEY_CTX_set_params(self.as_ptr(), params.as_ptr()))?;

// retake pointer to free memory
let _ = CString::from_raw(digest);
}
Ok(())
}

/// Gets the digest algorithm for a private key context.
///
/// Requires OpenSSL 3.0.0 or newer.
#[cfg(ossl300)]
#[corresponds(EVP_PKEY_CTX_get_params)]
pub fn digest(&mut self) -> Result<Option<MessageDigest>, ErrorStack> {
use libc::c_char;
// From openssl/internal/sizes.h
let ossl_max_name_size = 50usize;
let digest_field_name = CString::new("digest").unwrap();
let digest: *mut c_char = CString::new(vec![1; ossl_max_name_size])
.unwrap()
.into_raw();
unsafe {
let param_digest = ffi::OSSL_PARAM_construct_utf8_string(
digest_field_name.as_ptr(),
digest,
ossl_max_name_size,
);
let param_end = ffi::OSSL_PARAM_construct_end();
let mut params = [param_digest, param_end];
cvt(ffi::EVP_PKEY_CTX_get_params(
self.as_ptr(),
params.as_mut_ptr(),
))?;
let digest_str = CString::from_raw(digest);
Ok(MessageDigest::from_name(digest_str.to_str().unwrap()))
}
}

/// Sets the nonce type for a private key context.
///
/// The nonce for DSA and ECDSA can be either random (the default) or deterministic (as defined by RFC 6979).
///
/// This is only useful for DSA and ECDSA.
/// Requires OpenSSL 3.2.0 or newer.
#[cfg(ossl320)]
#[corresponds(EVP_PKEY_CTX_set_params)]
pub fn set_nonce_type(&mut self, nonce_type: NonceType) -> Result<(), ErrorStack> {
let nonce_field_name = CString::new("nonce-type").unwrap();
let mut nonce_type = nonce_type.0;
unsafe {
let param_nonce =
ffi::OSSL_PARAM_construct_uint(nonce_field_name.as_ptr(), &mut nonce_type);
let param_end = ffi::OSSL_PARAM_construct_end();

let params = [param_nonce, param_end];
cvt(ffi::EVP_PKEY_CTX_set_params(self.as_ptr(), params.as_ptr()))?;
}
Ok(())
}

/// Gets the nonce type for a private key context.
///
/// The nonce for DSA and ECDSA can be either random (the default) or deterministic (as defined by RFC 6979).
///
/// This is only useful for DSA and ECDSA.
/// Requires OpenSSL 3.2.0 or newer.
#[cfg(ossl320)]
#[corresponds(EVP_PKEY_CTX_get_params)]
pub fn nonce_type(&mut self) -> Result<NonceType, ErrorStack> {
let nonce_field_name = CString::new("nonce-type").unwrap();
let mut nonce_type: c_uint = 0;
unsafe {
let param_nonce =
ffi::OSSL_PARAM_construct_uint(nonce_field_name.as_ptr(), &mut nonce_type);
let param_end = ffi::OSSL_PARAM_construct_end();

let mut params = [param_nonce, param_end];
cvt(ffi::EVP_PKEY_CTX_get_params(
self.as_ptr(),
params.as_mut_ptr(),
))?;
}
Ok(NonceType(nonce_type))
}
}

#[cfg(test)]
Expand Down Expand Up @@ -999,4 +1123,34 @@ mod test {
// The digest is the end of the DigestInfo structure.
assert_eq!(result_buf[length - digest.len()..length], digest);
}

#[test]
#[cfg(ossl300)]
fn set_digest() {
let key1 =
EcKey::generate(&EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap()).unwrap();
let key1 = PKey::from_ec_key(key1).unwrap();

let mut ctx = PkeyCtx::new(&key1).unwrap();
ctx.sign_init().unwrap();
ctx.set_digest(MessageDigest::sha224()).unwrap();
let digest_name = ctx.digest().unwrap().unwrap().type_();
assert_eq!(digest_name, MessageDigest::sha224().type_());
assert!(ErrorStack::get().errors().is_empty());
}

#[test]
#[cfg(ossl320)]
fn set_nonce_type() {
let key1 =
EcKey::generate(&EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap()).unwrap();
let key1 = PKey::from_ec_key(key1).unwrap();

let mut ctx = PkeyCtx::new(&key1).unwrap();
ctx.sign_init().unwrap();
ctx.set_nonce_type(NonceType::DETERMINISTIC_K).unwrap();
let nonce_type = ctx.nonce_type().unwrap();
assert_eq!(nonce_type, NonceType::DETERMINISTIC_K);
assert!(ErrorStack::get().errors().is_empty());
}
}

0 comments on commit 8f11103

Please sign in to comment.