Skip to content

Commit

Permalink
Merge pull request #317 from jhelovuo/support-rsa-certificates
Browse files Browse the repository at this point in the history
Support Certificates with RSA algorithm, support RSA signing
  • Loading branch information
ohuopio authored Mar 6, 2024
2 parents 80f139a + fd7b9a9 commit b567f6e
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,7 @@ use byteorder::BigEndian;
use bytes::Bytes;
#[allow(unused_imports)]
use log::{debug, error, info, trace, warn};
use ring::signature;

//use x509_certificate::{
//algorithm::{EcdsaCurve, KeyAlgorithm},
//signing::{InMemorySigningKeyPair, Sign},
//};
use crate::{
security::{
access_control::{
Expand Down Expand Up @@ -38,8 +33,8 @@ use crate::{
};
use super::{
types::{
BuiltinAuthenticatedPeerCredentialToken, BuiltinIdentityToken, DH_MODP_KAGREE_ALGO_NAME,
ECDH_KAGREE_ALGO_NAME,
parse_signature_algo_name_to_ring, BuiltinAuthenticatedPeerCredentialToken,
BuiltinIdentityToken, DH_MODP_KAGREE_ALGO_NAME, ECDH_KAGREE_ALGO_NAME,
},
BuiltinHandshakeState, DHKeys, LocalParticipantInfo, RemoteParticipantInfo,
};
Expand Down Expand Up @@ -177,12 +172,24 @@ impl Authentication for AuthenticationBuiltin {
// Section "9.3.2.1 DDS:Auth:PKI-DH IdentityToken"
// Table 45
//
// TODO: dig out ".algo" values from identity_certificate and identity_ca
let certificate_algorithm = identity_certificate
.key_algorithm()
.ok_or(security_error!(
"Identity Certificate specifies no public key algorithm"
))
.and_then(CertificateAlgorithm::try_from)?;
let ca_algorithm = identity_ca
.key_algorithm()
.ok_or(security_error!(
"CA Certificate specifies no public key algorithm"
))
.and_then(CertificateAlgorithm::try_from)?;

let identity_token = BuiltinIdentityToken {
certificate_subject: Some(identity_certificate.subject_name().clone().serialize()),
certificate_algorithm: Some(CertificateAlgorithm::ECPrime256v1), // TODO: hardwired
certificate_algorithm: Some(certificate_algorithm),
ca_subject: Some(identity_ca.subject_name().clone().serialize()),
ca_algorithm: Some(CertificateAlgorithm::ECPrime256v1), // TODO: hardwired
ca_algorithm: Some(ca_algorithm),
};

let local_identity_handle = self.get_new_identity_handle();
Expand Down Expand Up @@ -397,7 +404,9 @@ impl Authentication for AuthenticationBuiltin {

let pdata_bytes = Bytes::from(serialized_local_participant_data);

let dsign_algo = Bytes::from_static(b"ECDSA-SHA256"); // TODO: do not hardcode this, get from id cert
let dsign_algo = local_info
.identity_certificate
.signature_algorithm_identifier()?;

let kagree_algo = Bytes::from(dh_keys.kagree_algo_name_str());

Expand Down Expand Up @@ -502,7 +511,9 @@ impl Authentication for AuthenticationBuiltin {

let pdata_bytes = Bytes::from(serialized_local_participant_data);

let dsign_algo = Bytes::from_static(b"ECDSA-SHA256"); // TODO: do not hardcode this, get from id cert
let dsign_algo = local_info
.identity_certificate
.signature_algorithm_identifier()?;

// Check which key agreement algorithm the remote has chosen & generate our own
// key pair
Expand Down Expand Up @@ -738,6 +749,8 @@ impl Authentication for AuthenticationBuiltin {
BinaryProperty::with_propagate("hash_c1", Bytes::copy_from_slice(hash_c1.as_ref())),
];

let c2_signature_algorithm = parse_signature_algo_name_to_ring(&reply.c_dsign_algo)?;

// Verify "C2" contents against reply.signature and 2's public key
cert2.verify_signed_data_with_algorithm(
to_bytes::<Vec<BinaryProperty>, BigEndian>(&cc2_properties).map_err(|e| {
Expand All @@ -746,8 +759,7 @@ impl Authentication for AuthenticationBuiltin {
}
})?,
reply.signature,
// TODO: Hardcoded algorithm
&signature::ECDSA_P256_SHA256_ASN1,
c2_signature_algorithm,
)?; // verify ok or exit here

// Verify that the key agreement algo in the reply is as we expect
Expand Down Expand Up @@ -843,8 +855,13 @@ impl Authentication for AuthenticationBuiltin {
// We are the responder, and expect the final message.
// Result is that we do not produce a MassageToken, since this was the final
// message, but we compute the handshake results (shared secret)
let final_token =
BuiltinHandshakeMessageToken::try_from(handshake_message_in)?.extract_final()?;
let handshake_token = BuiltinHandshakeMessageToken::try_from(handshake_message_in)?;
let remote_signature_algo_name = handshake_token
.c_dsign_algo
.clone()
.ok_or_else(|| security_error!("Final token did not specifiy signature algorithm."))?;

let final_token = handshake_token.extract_final()?;

// This is a sanity check
if let Some(received_hash_c1) = final_token.hash_c1 {
Expand Down Expand Up @@ -911,7 +928,9 @@ impl Authentication for AuthenticationBuiltin {

// Now we use the remote certificate, which we verified in the previous (request
// -> reply) step against CA.
// TODO: Hardcoded algorithm
let remote_signature_algorithm =
parse_signature_algo_name_to_ring(&remote_signature_algo_name)?;

remote_id_certificate
.verify_signed_data_with_algorithm(
to_bytes::<Vec<BinaryProperty>, BigEndian>(&cc_final_properties).map_err(|e| {
Expand All @@ -920,7 +939,7 @@ impl Authentication for AuthenticationBuiltin {
}
})?,
final_token.signature,
&signature::ECDSA_P256_SHA256_ASN1,
remote_signature_algorithm,
)
.map_err(|e| {
security_error!("Signature verification failed in process_handshake: {e:?}")
Expand Down
40 changes: 40 additions & 0 deletions src/security/authentication/authentication_builtin/types.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use bytes::Bytes;
use log::debug;
use x509_certificate::{self, KeyAlgorithm};

use crate::{
security::{
Expand Down Expand Up @@ -59,6 +60,21 @@ impl TryFrom<String> for CertificateAlgorithm {
}
}

// Convert public-key algortihm identifiers from x509_certficate crate to
// our representation.
impl TryFrom<x509_certificate::KeyAlgorithm> for CertificateAlgorithm {
type Error = SecurityError;
fn try_from(value: KeyAlgorithm) -> Result<Self, Self::Error> {
match value {
KeyAlgorithm::Rsa => Ok(CertificateAlgorithm::RSA2048),
KeyAlgorithm::Ecdsa(x509_certificate::algorithm::EcdsaCurve::Secp256r1) => {
Ok(CertificateAlgorithm::ECPrime256v1)
}
x => Err(security_error!("Unsuppored certificate algorithm: {:?}", x)),
}
}
}

/// DDS:Auth:PKI-DH IdentityToken type from section 9.3.2.1 of the
/// Security specification (v. 1.1)
///
Expand Down Expand Up @@ -269,6 +285,30 @@ pub const HANDSHAKE_FINAL_CLASS_ID: &[u8] = b"DDS:Auth:PKI-DH:1.0+Final";
pub const DH_MODP_KAGREE_ALGO_NAME: &str = "DH+MODP-2048-256";
pub const ECDH_KAGREE_ALGO_NAME: &str = "ECDH+prime256v1-CEUM";

// Standard values for "signature algorithm"
// as a byte string accrding to Table 49
// in DDS Security Spec v1.1 Section "9.3.2.5.1 HandshakeRequestMessageToken
// objects"
pub const RSA_SIGNATURE_ALGO_NAME: &[u8] = b"RSASSA-PSS-SHA256";
pub const ECDSA_SIGNATURE_ALGO_NAME: &[u8] = b"ECDSA-SHA256";

// Recognize standard string constatns and convert to
// corresponging algorithm identifiers in ring library.
pub(crate) fn parse_signature_algo_name_to_ring(
algo_name: &[u8],
) -> SecurityResult<&'static dyn ring::signature::VerificationAlgorithm> {
match algo_name {
RSA_SIGNATURE_ALGO_NAME => Ok(&ring::signature::ECDSA_P256_SHA256_ASN1),
ECDSA_SIGNATURE_ALGO_NAME => Ok(&ring::signature::RSA_PSS_2048_8192_SHA256),
_other =>
// TODO: Log the algorithm name, but be careful,
// the name is is arbitrary binary data from an unknown third party.
{
Err(security_error!("Unknown signature algorithm name"))
}
}
}

/// DDS:Auth:PKI-DH HandshakeMessageToken type from section 9.3.2.5 of the
/// Security specification (v. 1.1)
/// Works as all three token formats: HandshakeRequestMessageToken,
Expand Down
31 changes: 28 additions & 3 deletions src/security/certificate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,20 @@

use bytes::Bytes;
use x509_certificate::{
certificate::CapturedX509Certificate, signing::InMemorySigningKeyPair, EcdsaCurve, KeyAlgorithm,
Signer,
self, certificate::CapturedX509Certificate, signing::InMemorySigningKeyPair, EcdsaCurve,
KeyAlgorithm, SignatureAlgorithm, Signer,
};
use der::Decode;
use bcder::{encode::Values, Mode};

use crate::security::{
authentication::authentication_builtin::types::{CertificateAlgorithm, RSA_2048_KEY_LENGTH},
authentication::authentication_builtin::types::{
CertificateAlgorithm, ECDSA_SIGNATURE_ALGO_NAME, RSA_2048_KEY_LENGTH, RSA_SIGNATURE_ALGO_NAME,
},
config::{to_config_error_other, to_config_error_parse, ConfigError},
types::{security_error, SecurityResult},
};
//use crate::security_error;

// This is mostly a wrapper around
// x509_certificate::certificate::CapturedX509Certificate
Expand Down Expand Up @@ -57,6 +60,28 @@ impl Certificate {
self.cert.encode_pem()
}

// public key algorithm
pub fn key_algorithm(&self) -> Option<x509_certificate::KeyAlgorithm> {
self.cert.key_algorithm()
}

// name of the signature algoritm as a byte string accrding to Table 49
// in DDS Security Spec v1.1 Section "9.3.2.5.1 HandshakeRequestMessageToken
// objects"
pub fn signature_algorithm_identifier(&self) -> SecurityResult<Bytes> {
match self.cert.signature_algorithm() {
None => Err(security_error(
"Certificate has no known signature algorithm?!",
)),
Some(SignatureAlgorithm::RsaSha256) => Ok(Bytes::from_static(RSA_SIGNATURE_ALGO_NAME)),
Some(SignatureAlgorithm::EcdsaSha256) => Ok(Bytes::from_static(ECDSA_SIGNATURE_ALGO_NAME)),
Some(x) => Err(security_error(&format!(
"Certificate has out-of-spec signature algorithm {:?}",
x
))),
}
}

pub fn subject_name(&self) -> &DistinguishedName {
&self.subject_name
}
Expand Down

0 comments on commit b567f6e

Please sign in to comment.