Skip to content

Commit

Permalink
implement X509_check_private_key
Browse files Browse the repository at this point in the history
Rustls 0.23.11 added a `keys_match()` function to `CertifiedKey`
instances. This commit uses that new capability to implement
`X509_check_private_key()`. The `EvpPkey` and `OpenSslKey` key types
are both updated to enable implementing the new `sign::SigningKey`
trait's optional `public_key()` fn.

Following the precedent set upstream in Rustls we're permissive if
we can't determine a key's SPKI and return `C_INT_SUCCESS` in this case.
  • Loading branch information
cpu committed Jul 19, 2024
1 parent 56910c7 commit a1b855e
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 7 deletions.
1 change: 1 addition & 0 deletions rustls-libssl/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,4 +214,5 @@ const ENTRYPOINTS: &[&str] = &[
"TLS_client_method",
"TLS_method",
"TLS_server_method",
"X509_check_private_key",
];
22 changes: 22 additions & 0 deletions rustls-libssl/src/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ use crate::ffi::{
try_slice, try_slice_int, try_str, Castable, OwnershipArc, OwnershipBox, OwnershipRef,
};
use crate::not_thread_safe::NotThreadSafe;
use crate::sign::OpenSslCertifiedKey;
use crate::x509::{load_certs, OwnedX509, OwnedX509Stack};
use crate::{conf, HandshakeState, ShutdownResult};

Expand Down Expand Up @@ -1840,6 +1841,27 @@ impl Castable for SSL_CONF_CTX {
type RustType = NotThreadSafe<conf::SslConfigCtx>;
}

entry! {
pub fn _X509_check_private_key(cert: *mut X509, pkey: *mut EVP_PKEY) -> c_int {
if cert.is_null() || pkey.is_null() {
return 0;
}

let chain = vec![CertificateDer::from(
OwnedX509::new_incref(cert).der_bytes(),
)];
let Ok(certified_key) = OpenSslCertifiedKey::new(chain, EvpPkey::new_incref(pkey)) else {
return 0;
};

if certified_key.keys_match() {
C_INT_SUCCESS
} else {
0
}
}
}

/// Normal OpenSSL return value convention success indicator.
///
/// Compare [`crate::ffi::MysteriouslyOppositeReturnValue`].
Expand Down
69 changes: 65 additions & 4 deletions rustls-libssl/src/evp_pkey.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
use core::ffi::{c_char, c_int, c_long, CStr};
use core::{fmt, ptr};
use std::slice;

use openssl_sys::{
d2i_AutoPrivateKey, EVP_DigestSign, EVP_DigestSignInit, EVP_MD_CTX_free, EVP_MD_CTX_new,
EVP_PKEY_CTX_set_rsa_padding, EVP_PKEY_CTX_set_rsa_pss_saltlen, EVP_PKEY_CTX_set_signature_md,
EVP_PKEY_free, EVP_PKEY_up_ref, EVP_sha256, EVP_sha384, EVP_sha512, EVP_MD, EVP_MD_CTX,
EVP_PKEY, EVP_PKEY_CTX, RSA_PKCS1_PADDING, RSA_PKCS1_PSS_PADDING,
d2i_AutoPrivateKey, i2d_PUBKEY, EVP_DigestSign, EVP_DigestSignInit, EVP_MD_CTX_free,
EVP_MD_CTX_new, EVP_PKEY_CTX_set_rsa_padding, EVP_PKEY_CTX_set_rsa_pss_saltlen,
EVP_PKEY_CTX_set_signature_md, EVP_PKEY_free, EVP_PKEY_up_ref, EVP_sha256, EVP_sha384,
EVP_sha512, OPENSSL_free, EVP_MD, EVP_MD_CTX, EVP_PKEY, EVP_PKEY_CTX, RSA_PKCS1_PADDING,
RSA_PKCS1_PSS_PADDING,
};
use rustls::pki_types::PrivateKeyDer;

Expand Down Expand Up @@ -60,6 +62,26 @@ impl EvpPkey {
}
}

/// Return the Subject Public Key Info bytes for this key.
pub fn subject_public_key_info(&self) -> Vec<u8> {
let (ptr, len) = unsafe {
let mut ptr = ptr::null_mut();
let len = i2d_PUBKEY(self.pkey, &mut ptr);
(ptr, len)
};

if len <= 0 {
return vec![];
}
let len = len as usize;

let mut v = Vec::with_capacity(len);
v.extend_from_slice(unsafe { slice::from_raw_parts(ptr, len) });

unsafe { OPENSSL_free(ptr as *mut _) };
v
}

/// Caller borrows our reference.
pub fn borrow_ref(&self) -> *mut EVP_PKEY {
self.pkey as *mut EVP_PKEY
Expand Down Expand Up @@ -305,6 +327,7 @@ extern "C" {
#[cfg(all(test, not(miri)))]
mod tests {
use super::*;
use std::io::Cursor;

#[test]
fn supports_rsaencryption_keys() {
Expand Down Expand Up @@ -346,4 +369,42 @@ mod tests {
256
);
}

#[test]
fn pkey_spki() {
for (key_path, cert_path) in &[
("test-ca/rsa/server.key", "test-ca/rsa/server.cert"),
(
"test-ca/ecdsa-p256/server.key",
"test-ca/ecdsa-p256/server.cert",
),
(
"test-ca/ecdsa-p384/server.key",
"test-ca/ecdsa-p384/server.cert",
),
(
"test-ca/ecdsa-p521/server.key",
"test-ca/ecdsa-p521/server.cert",
),
("test-ca/ed25519/server.key", "test-ca/ed25519/server.cert"),
] {
let key_der = std::fs::read(key_path).unwrap();
let cert_der = std::fs::read(cert_path).unwrap();

let key_der = rustls_pemfile::private_key(&mut Cursor::new(key_der))
.unwrap()
.unwrap();
let key = EvpPkey::new_from_der_bytes(key_der).unwrap();

let cert_der = rustls_pemfile::certs(&mut Cursor::new(cert_der))
.next()
.unwrap()
.unwrap();
let parsed_cert = rustls::server::ParsedCertificate::try_from(&cert_der).unwrap();

let cert_spki = parsed_cert.subject_public_key_info();
let key_spki = key.subject_public_key_info();
assert_eq!(&key_spki, cert_spki.as_ref());
}
}
}
32 changes: 29 additions & 3 deletions rustls-libssl/src/sign.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::sync::Arc;

use openssl_sys::{EVP_PKEY, X509};
use rustls::client::ResolvesClientCert;
use rustls::pki_types::CertificateDer;
use rustls::pki_types::{CertificateDer, SubjectPublicKeyInfoDer};
use rustls::server::{ClientHello, ResolvesServerCert};
use rustls::sign;
use rustls::{SignatureAlgorithm, SignatureScheme};
Expand Down Expand Up @@ -88,21 +88,41 @@ impl CertifiedKeySet {
}

#[derive(Clone, Debug)]
struct OpenSslCertifiedKey {
pub(super) struct OpenSslCertifiedKey {
key: EvpPkey,
openssl_chain: OwnedX509Stack,
rustls_chain: Vec<CertificateDer<'static>>,
}

impl OpenSslCertifiedKey {
fn new(chain: Vec<CertificateDer<'static>>, key: EvpPkey) -> Result<Self, error::Error> {
pub(super) fn new(
chain: Vec<CertificateDer<'static>>,
key: EvpPkey,
) -> Result<Self, error::Error> {
Ok(Self {
key,
openssl_chain: OwnedX509Stack::from_rustls(&chain)?,
rustls_chain: chain,
})
}

pub(super) fn keys_match(&self) -> bool {
match sign::CertifiedKey::new(
self.rustls_chain.clone(),
Arc::new(OpenSslKey(self.key.clone())),
)
.keys_match()
{
// Note: we allow "Unknown" to be treated as success here. This is returned
// when it wasn't possible to get the SPKI for the private key, and so we
// aren't certain if it matches or not.
Ok(()) | Err(rustls::Error::InconsistentKeys(rustls::InconsistentKeys::Unknown)) => {
true
}
_ => false,
}
}

fn borrow_cert(&self) -> *mut X509 {
self.openssl_chain.borrow_top_ref()
}
Expand Down Expand Up @@ -245,6 +265,12 @@ impl sign::SigningKey for OpenSslKey {
}
}

fn public_key(&self) -> Option<SubjectPublicKeyInfoDer<'_>> {
Some(SubjectPublicKeyInfoDer::from(
self.0.subject_public_key_info(),
))
}

fn algorithm(&self) -> SignatureAlgorithm {
self.0.algorithm()
}
Expand Down
1 change: 1 addition & 0 deletions rustls-libssl/tests/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ int main(int argc, char **argv) {
TRACE(SSL_CTX_use_PrivateKey_file(ctx, keyfile, SSL_FILETYPE_PEM));
client_key = SSL_CTX_get0_privatekey(ctx);
client_cert = SSL_CTX_get0_certificate(ctx);
TRACE(X509_check_private_key(client_cert, client_key));
}

TRACE(SSL_CTX_set_alpn_protos(ctx, (const uint8_t *)"\x02hi\x05world", 9));
Expand Down
1 change: 1 addition & 0 deletions rustls-libssl/tests/server.c
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ int main(int argc, char **argv) {
TRACE(SSL_CTX_use_PrivateKey_file(ctx, keyfile, SSL_FILETYPE_PEM));
server_key = SSL_CTX_get0_privatekey(ctx);
server_cert = SSL_CTX_get0_certificate(ctx);
TRACE(X509_check_private_key(server_cert, server_key));

printf("SSL_CTX_get_max_early_data default %lu\n",
(unsigned long)SSL_CTX_get_max_early_data(ctx));
Expand Down

0 comments on commit a1b855e

Please sign in to comment.