use std::cmp::Ordering;
use byteorder::BigEndian;
use bytes::Bytes;
#[allow(unused_imports)]
use log::{debug, error, info, trace, warn};
use crate::{
create_security_error_and_log, discovery,
security::{
access_control::{access_control_builtin::types::BuiltinPermissionsCredentialToken, *},
authentication::{
authentication_builtin::{
types::{
BuiltinHandshakeMessageToken, CertificateAlgorithm, HANDSHAKE_FINAL_CLASS_ID,
HANDSHAKE_REPLY_CLASS_ID, HANDSHAKE_REQUEST_CLASS_ID, IDENTITY_TOKEN_CLASS_ID,
},
HandshakeInfo,
},
*,
},
certificate::*,
config::*,
*,
},
serialization::{pl_cdr_adapters::PlCdrDeserialize, to_vec},
structure::guid::GuidPrefix,
QosPolicies, RepresentationIdentifier, GUID,
};
use super::{
types::{
parse_signature_algo_name_to_ring, BuiltinAuthenticatedPeerCredentialToken,
BuiltinIdentityToken, DH_MODP_KAGREE_ALGO_NAME, ECDH_KAGREE_ALGO_NAME,
QOS_IDENTITY_CA_PROPERTY_NAME, QOS_IDENTITY_CERTIFICATE_PROPERTY_NAME,
QOS_PASSWORD_PROPERTY_NAME, QOS_PRIVATE_KEY_PROPERTY_NAME,
},
BuiltinHandshakeState, DHKeys, LocalParticipantInfo, RemoteParticipantInfo,
};
fn guid_start_from_certificate(identity_cert: &Certificate) -> SecurityResult<[u8; 6]> {
let subject_name_der = identity_cert.subject_name_der()?;
let subject_name_der_hash = Sha256::hash(&subject_name_der);
let bytes_from_subject_name =
&((u64::from_be_bytes(subject_name_der_hash.as_ref()[0..8].try_into().unwrap()) >> 1)
| 0x8000_0000_0000_0000u64)
.to_be_bytes()[0..6];
let mut guid_start = [0u8; 6];
guid_start.copy_from_slice(&bytes_from_subject_name[..6]);
Ok(guid_start)
}
fn validate_remote_guid(
remote_guid: GUID,
remote_identity_cert: &Certificate,
) -> SecurityResult<()> {
let actual_guid_start = &remote_guid.prefix.as_ref()[0..6];
let expected_guid_start = guid_start_from_certificate(remote_identity_cert).map_err(|e| {
create_security_error_and_log!("Could not determine the expected GUID start: {e}")
})?;
if actual_guid_start == expected_guid_start {
Ok(())
} else {
Err(create_security_error_and_log!(
"GUID start {:?} is not the expected {:?}",
actual_guid_start,
expected_guid_start
))
}
}
impl Authentication for AuthenticationBuiltin {
fn validate_local_identity(
&mut self,
_domain_id: u16, participant_qos: &QosPolicies,
candidate_participant_guid: GUID,
) -> SecurityResult<(ValidationOutcome, IdentityHandle, GUID)> {
let identity_ca = participant_qos
.get_property(QOS_IDENTITY_CA_PROPERTY_NAME)
.and_then(|certificate_uri| {
read_uri(&certificate_uri).map_err(|conf_err| {
create_security_error_and_log!(
"Failed to read the identity CA certificate from {}: {:?}",
certificate_uri,
conf_err
)
})
})
.and_then(|certificate_contents_pem| {
Certificate::from_pem(certificate_contents_pem)
.map_err(|e| create_security_error_and_log!("{e:?}"))
})?;
let identity_certificate = participant_qos
.get_property(QOS_IDENTITY_CERTIFICATE_PROPERTY_NAME)
.and_then(|certificate_uri| {
read_uri(&certificate_uri).map_err(|conf_err| {
create_security_error_and_log!(
"Failed to read the DomainParticipant identity certificate from {}: {:?}",
certificate_uri,
conf_err
)
})
})
.and_then(|certificate_contents_pem| {
Certificate::from_pem(certificate_contents_pem)
.map_err(|e| create_security_error_and_log!("{e:?}"))
})?;
let _password = participant_qos.get_optional_property(QOS_PASSWORD_PROPERTY_NAME);
let id_cert_algorithm = identity_certificate.algorithm().ok_or_else(|| {
create_security_error_and_log!("Cannot recognize identity certificate algorithm.")
})?;
let id_cert_private_key = participant_qos
.get_property(QOS_PRIVATE_KEY_PROPERTY_NAME)
.and_then(|pem_uri| {
read_uri_to_private_key(&pem_uri, id_cert_algorithm).map_err(|conf_err| {
create_security_error_and_log!(
"Failed to read the DomainParticipant identity private key from {}: {:?}",
pem_uri,
conf_err
)
})
})?;
identity_certificate
.verify_signed_by_certificate(&identity_ca)
.map_err(|_e| {
create_security_error_and_log!(
"My own identity certificate does not verify against identity CA."
)
})?;
let guid_start = guid_start_from_certificate(&identity_certificate)?;
let candidate_guid_hash = Sha256::hash(&candidate_participant_guid.to_bytes());
let prefix_bytes = [&guid_start, &candidate_guid_hash.as_ref()[..6]].concat();
let adjusted_guid = GUID::new(
GuidPrefix::new(&prefix_bytes),
candidate_participant_guid.entity_id,
);
let certificate_algorithm = identity_certificate
.key_algorithm()
.ok_or_else(|| {
create_security_error_and_log!("Identity Certificate specifies no public key algorithm")
})
.and_then(CertificateAlgorithm::try_from)?;
let ca_algorithm = identity_ca
.key_algorithm()
.ok_or_else(|| {
create_security_error_and_log!("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(certificate_algorithm),
ca_subject: Some(identity_ca.subject_name().clone().serialize()),
ca_algorithm: Some(ca_algorithm),
};
let local_identity_handle = self.get_new_identity_handle();
let local_participant_info = LocalParticipantInfo {
identity_handle: local_identity_handle,
identity_token,
guid: adjusted_guid,
identity_certificate,
id_cert_private_key,
identity_ca,
signed_permissions_document_xml: Bytes::new(),
local_permissions_token: None, };
self.local_participant_info = Some(local_participant_info);
let random_bytes1 = self.generate_random_32_bytes()?;
let random_bytes2 = self.generate_random_32_bytes()?;
let random_bytes3 = self.generate_random_32_bytes()?;
let self_remote_info = RemoteParticipantInfo {
identity_certificate_opt: None,
signed_permissions_xml_opt: None,
handshake: HandshakeInfo {
state: BuiltinHandshakeState::CompletedWithFinalMessageReceived {
challenge1: Challenge::from(random_bytes1),
challenge2: Challenge::from(random_bytes2),
shared_secret: SharedSecret::from(random_bytes3),
},
},
};
self
.remote_participant_infos
.insert(local_identity_handle, self_remote_info);
Ok((ValidationOutcome::Ok, local_identity_handle, adjusted_guid))
}
fn get_identity_token(&self, handle: IdentityHandle) -> SecurityResult<IdentityToken> {
let local_info = self.get_local_participant_info()?;
if handle != local_info.identity_handle {
return Err(create_security_error_and_log!(
"The given handle does not correspond to the local identity handle"
));
}
Ok(local_info.identity_token.clone().into())
}
fn get_identity_status_token(
&self,
_handle: IdentityHandle,
) -> SecurityResult<IdentityStatusToken> {
Ok(IdentityStatusToken::dummy())
}
fn set_permissions_credential_and_token(
&mut self,
handle: IdentityHandle,
permissions_credential_token: PermissionsCredentialToken,
permissions_token: PermissionsToken,
) -> SecurityResult<()> {
let local_info = self.get_local_participant_info_mutable()?;
if handle != local_info.identity_handle {
return Err(create_security_error_and_log!(
"The parameter local_identity_handle is not the correct local handle"
));
}
let builtin_token = BuiltinPermissionsCredentialToken::try_from(permissions_credential_token)?;
local_info.signed_permissions_document_xml = builtin_token.permissions_document;
local_info.local_permissions_token = Some(permissions_token);
Ok(())
}
fn validate_remote_identity(
&mut self,
_remote_auth_request_token: Option<AuthRequestMessageToken>, local_identity_handle: IdentityHandle,
remote_identity_token: IdentityToken,
remote_participant_guidp: GuidPrefix,
) -> SecurityResult<(
ValidationOutcome,
IdentityHandle,
Option<AuthRequestMessageToken>,
)> {
let local_info = self.get_local_participant_info()?;
if local_identity_handle != local_info.identity_handle {
return Err(create_security_error_and_log!(
"The parameter local_identity_handle is not the correct local handle"
));
}
if remote_identity_token.class_id() != IDENTITY_TOKEN_CLASS_ID {
return Err(create_security_error_and_log!(
"Remote identity class_id is {:?}",
remote_identity_token.class_id()
));
}
let auth_request_token = None;
let (handshake_state, validation_outcome) =
match local_info.guid.prefix.cmp(&remote_participant_guidp) {
Ordering::Less => {
(
BuiltinHandshakeState::PendingRequestSend,
ValidationOutcome::PendingHandshakeRequest,
)
}
Ordering::Greater => {
(
BuiltinHandshakeState::PendingRequestMessage,
ValidationOutcome::PendingHandshakeMessage,
)
}
Ordering::Equal => {
return Err(create_security_error_and_log!(
"Remote GUID is equal to the local GUID"
));
}
};
let remote_identity_handle = self.get_new_identity_handle();
let remote_info = RemoteParticipantInfo {
identity_certificate_opt: None, signed_permissions_xml_opt: None, handshake: HandshakeInfo {
state: handshake_state,
},
};
self
.remote_participant_infos
.insert(remote_identity_handle, remote_info);
Ok((
validation_outcome,
remote_identity_handle,
auth_request_token,
))
}
fn begin_handshake_request(
&mut self,
initiator_identity_handle: IdentityHandle, replier_identity_handle: IdentityHandle, serialized_local_participant_data: Vec<u8>,
) -> SecurityResult<(ValidationOutcome, HandshakeHandle, HandshakeMessageToken)> {
let local_info = self.get_local_participant_info()?;
if initiator_identity_handle != local_info.identity_handle {
return Err(create_security_error_and_log!(
"The parameter initiator_identity_handle is not the correct local handle"
));
}
let my_id_certificate_text = Bytes::from(local_info.identity_certificate.to_pem());
let my_permissions_doc_text = local_info.signed_permissions_document_xml.clone();
let remote_info = self.get_remote_participant_info(&replier_identity_handle)?;
if let BuiltinHandshakeState::PendingRequestSend = remote_info.handshake.state {
} else {
return Err(create_security_error_and_log!(
"We are not expecting to send a handshake request. Handshake state: {:?}",
remote_info.handshake.state
));
}
let dh_keys = DHKeys::new_ec_keys(&self.secure_random_generator)?;
let pdata_bytes = Bytes::from(serialized_local_participant_data);
let dsign_algo = local_info
.identity_certificate
.signature_algorithm_identifier()?;
let kagree_algo = Bytes::from(dh_keys.kagree_algo_name_str());
let c_properties: Vec<BinaryProperty> = vec![
BinaryProperty::with_propagate("c.id", my_id_certificate_text.clone()),
BinaryProperty::with_propagate("c.perm", my_permissions_doc_text.clone()),
BinaryProperty::with_propagate("c.pdata", pdata_bytes.clone()),
BinaryProperty::with_propagate("c.dsign_algo", dsign_algo.clone()),
BinaryProperty::with_propagate("c.kagree_algo", kagree_algo.clone()),
];
let hash_c1 = Sha256::hash(
&to_vec::<Vec<BinaryProperty>, BigEndian>(&c_properties).map_err(|e| SecurityError {
msg: format!("Error serializing C1: {e}"),
})?,
);
let random_bytes = self.generate_random_32_bytes()?;
let challenge1 = Challenge::from(random_bytes);
let handshake_request_builtin = BuiltinHandshakeMessageToken {
class_id: Bytes::copy_from_slice(HANDSHAKE_REQUEST_CLASS_ID),
c_id: Some(my_id_certificate_text),
c_perm: Some(my_permissions_doc_text),
c_pdata: Some(pdata_bytes),
c_dsign_algo: Some(dsign_algo),
c_kagree_algo: Some(kagree_algo),
ocsp_status: None, hash_c1: Some(Bytes::copy_from_slice(hash_c1.as_ref())),
dh1: Some(dh_keys.public_key_bytes()?),
hash_c2: None, dh2: None, challenge1: Some(Bytes::copy_from_slice(challenge1.as_ref())),
challenge2: None, signature: None, };
let handshake_request = HandshakeMessageToken::from(handshake_request_builtin);
let remote_info = self.get_remote_participant_info_mutable(&replier_identity_handle)?;
remote_info.handshake.state = BuiltinHandshakeState::PendingReplyMessage {
dh1: dh_keys,
challenge1,
hash_c1,
};
let new_handshake_handle = self.get_new_handshake_handle();
self
.handshake_to_identity_handle_map
.insert(new_handshake_handle, replier_identity_handle);
Ok((
ValidationOutcome::PendingHandshakeMessage,
new_handshake_handle,
handshake_request,
))
}
fn begin_handshake_reply(
&mut self,
handshake_message_in: HandshakeMessageToken,
initiator_identity_handle: IdentityHandle, replier_identity_handle: IdentityHandle, serialized_local_participant_data: Vec<u8>,
) -> SecurityResult<(ValidationOutcome, HandshakeHandle, HandshakeMessageToken)> {
let local_info = self.get_local_participant_info()?;
if replier_identity_handle != local_info.identity_handle {
return Err(create_security_error_and_log!(
"The parameter replier_identity_handle is not the correct local handle"
));
}
let my_id_certificate_text = Bytes::from(local_info.identity_certificate.to_pem());
let my_permissions_doc_text = local_info.signed_permissions_document_xml.clone();
let remote_info = self.get_remote_participant_info(&initiator_identity_handle)?;
if let BuiltinHandshakeState::PendingRequestMessage = remote_info.handshake.state {
} else {
return Err(create_security_error_and_log!(
"We are not expecting to receive a handshake request. Handshake state: {:?}",
remote_info.handshake.state
));
}
let request =
BuiltinHandshakeMessageToken::try_from(handshake_message_in)?.extract_request()?;
let cert1 = Certificate::from_pem(request.c_id.as_ref())?;
cert1.verify_signed_by_certificate(&local_info.identity_ca)?;
let remote_pdata =
discovery::spdp_participant_data::SpdpDiscoveredParticipantData::from_pl_cdr_bytes(
&request.c_pdata,
RepresentationIdentifier::CDR_BE,
)
.map_err(|e| {
create_security_error_and_log!(
"Failed to deserialize SpdpDiscoveredParticipantData from remote: {e}"
)
})?;
validate_remote_guid(remote_pdata.participant_guid, &cert1).map_err(|e| {
create_security_error_and_log!("Remote GUID does not comply with the spec: {e}")
})?;
let dh2_keys = if request.c_kagree_algo == *DH_MODP_KAGREE_ALGO_NAME {
DHKeys::new_modp_keys()?
} else if request.c_kagree_algo == *ECDH_KAGREE_ALGO_NAME {
DHKeys::new_ec_keys(&self.secure_random_generator)?
} else {
return Err(create_security_error_and_log!(
"Unexpected c_kagree_algo in handshake request: {:?}",
request.c_kagree_algo
));
};
let kagree_algo = Bytes::from(dh2_keys.kagree_algo_name_str());
let c_properties: Vec<BinaryProperty> = vec![
BinaryProperty::with_propagate("c.id", request.c_id.clone()),
BinaryProperty::with_propagate("c.perm", request.c_perm.clone()),
BinaryProperty::with_propagate("c.pdata", request.c_pdata.clone()),
BinaryProperty::with_propagate("c.dsign_algo", request.c_dsign_algo.clone()),
BinaryProperty::with_propagate("c.kagree_algo", request.c_kagree_algo.clone()),
];
let computed_c1_hash = Sha256::hash(
&to_vec::<Vec<BinaryProperty>, BigEndian>(&c_properties).map_err(|e| SecurityError {
msg: format!("Error serializing C1: {e}"),
})?,
);
if let Some(received_hash_c1) = request.hash_c1 {
if received_hash_c1 == computed_c1_hash {
} else {
return Err(create_security_error_and_log!(
"begin_handshake_reply: hash_c1 mismatch"
));
}
} else {
info!("Cannot compare hashes in begin_handshake_reply. Request did not have any.");
}
let random_bytes = self.generate_random_32_bytes()?;
let challenge2 = Challenge::from(random_bytes);
let dh2_public_key = dh2_keys.public_key_bytes()?;
let my_dsign_algo = local_info
.identity_certificate
.signature_algorithm_identifier()?;
let pdata_bytes = Bytes::from(serialized_local_participant_data);
let c2_properties: Vec<BinaryProperty> = vec![
BinaryProperty::with_propagate("c.id", my_id_certificate_text.clone()),
BinaryProperty::with_propagate("c.perm", my_permissions_doc_text.clone()),
BinaryProperty::with_propagate("c.pdata", pdata_bytes.clone()),
BinaryProperty::with_propagate("c.dsign_algo", my_dsign_algo.clone()),
BinaryProperty::with_propagate("c.kagree_algo", kagree_algo.clone()),
];
let c2_hash = Sha256::hash(
&to_vec::<Vec<BinaryProperty>, BigEndian>(&c2_properties).map_err(|e| SecurityError {
msg: format!("Error serializing C2: {e}"),
})?,
);
let cc2_properties: Vec<BinaryProperty> = vec![
BinaryProperty::with_propagate("hash_c2", Bytes::copy_from_slice(c2_hash.as_ref())),
BinaryProperty::with_propagate("challenge2", Bytes::copy_from_slice(challenge2.as_ref())),
BinaryProperty::with_propagate("dh2", Bytes::copy_from_slice(dh2_public_key.as_ref())),
BinaryProperty::with_propagate(
"challenge1",
Bytes::copy_from_slice(request.challenge1.as_ref()),
),
BinaryProperty::with_propagate("dh1", Bytes::copy_from_slice(request.dh1.as_ref())),
BinaryProperty::with_propagate("hash_c1", Bytes::copy_from_slice(computed_c1_hash.as_ref())),
];
let contents_signature = local_info.id_cert_private_key.sign(
&to_vec::<Vec<BinaryProperty>, BigEndian>(&cc2_properties).map_err(|e| SecurityError {
msg: format!("Error serializing CC2: {e}"),
})?,
)?;
let reply_token = BuiltinHandshakeMessageToken {
class_id: Bytes::copy_from_slice(HANDSHAKE_REPLY_CLASS_ID),
c_id: Some(my_id_certificate_text),
c_perm: Some(my_permissions_doc_text),
c_pdata: Some(pdata_bytes),
c_dsign_algo: Some(my_dsign_algo),
c_kagree_algo: Some(kagree_algo),
ocsp_status: None, hash_c1: Some(Bytes::copy_from_slice(computed_c1_hash.as_ref())),
dh1: Some(request.dh1.clone()),
hash_c2: Some(Bytes::copy_from_slice(c2_hash.as_ref())),
dh2: Some(dh2_public_key),
challenge1: Some(Bytes::copy_from_slice(request.challenge1.as_ref())),
challenge2: Some(Bytes::copy_from_slice(challenge2.as_ref())),
signature: Some(contents_signature),
};
let remote_info = self.get_remote_participant_info_mutable(&initiator_identity_handle)?;
remote_info.handshake.state = BuiltinHandshakeState::PendingFinalMessage {
hash_c1: computed_c1_hash,
hash_c2: c2_hash,
dh1_public: request.dh1,
challenge1: request.challenge1,
dh2: dh2_keys,
challenge2,
remote_id_certificate: cert1.clone(),
};
remote_info.identity_certificate_opt = Some(cert1);
remote_info.signed_permissions_xml_opt = Some(request.c_perm);
let new_handshake_handle = self.get_new_handshake_handle();
self
.handshake_to_identity_handle_map
.insert(new_handshake_handle, initiator_identity_handle);
Ok((
ValidationOutcome::PendingHandshakeMessage,
new_handshake_handle,
reply_token.into(),
))
}
fn process_handshake(
&mut self,
handshake_message_in: HandshakeMessageToken,
handshake_handle: HandshakeHandle,
) -> SecurityResult<(ValidationOutcome, Option<HandshakeMessageToken>)> {
let remote_identity_handle = *self.handshake_handle_to_identity_handle(&handshake_handle)?;
let remote_info = self.get_remote_participant_info_mutable(&remote_identity_handle)?;
let mut state = BuiltinHandshakeState::PendingRequestSend; std::mem::swap(&mut remote_info.handshake.state, &mut state);
let local_info = self.get_local_participant_info()?;
match state {
BuiltinHandshakeState::PendingReplyMessage {
dh1,
challenge1,
hash_c1,
} => {
let reply =
BuiltinHandshakeMessageToken::try_from(handshake_message_in)?.extract_reply()?;
let cert2 = Certificate::from_pem(reply.c_id.as_ref())?;
cert2.verify_signed_by_certificate(&local_info.identity_ca)?;
let remote_pdata =
discovery::spdp_participant_data::SpdpDiscoveredParticipantData::from_pl_cdr_bytes(
&reply.c_pdata,
RepresentationIdentifier::CDR_BE,
)
.map_err(|e| {
create_security_error_and_log!(
"Failed to deserialize SpdpDiscoveredParticipantData from remote: {e}"
)
})?;
validate_remote_guid(remote_pdata.participant_guid, &cert2).map_err(|e| {
create_security_error_and_log!("Remote GUID does not comply with the spec: {e}")
})?;
if challenge1 != reply.challenge1 {
return Err(create_security_error_and_log!(
"Challenge 1 mismatch on authentication reply"
));
}
if let Some(received_hash_c1) = reply.hash_c1 {
if hash_c1 != received_hash_c1 {
return Err(create_security_error_and_log!(
"Hash C1 mismatch on authentication reply"
));
} else {
}
} else {
debug!("Cannot compare hash C1 in process_handshake. Reply did not have any.");
}
let c2_properties: Vec<BinaryProperty> = vec![
BinaryProperty::with_propagate("c.id", reply.c_id.clone()),
BinaryProperty::with_propagate("c.perm", reply.c_perm.clone()),
BinaryProperty::with_propagate("c.pdata", reply.c_pdata.clone()),
BinaryProperty::with_propagate("c.dsign_algo", reply.c_dsign_algo.clone()),
BinaryProperty::with_propagate("c.kagree_algo", reply.c_kagree_algo.clone()),
];
let c2_hash_recomputed = Sha256::hash(
&to_vec::<Vec<BinaryProperty>, BigEndian>(&c2_properties).map_err(|e| SecurityError {
msg: format!("Error serializing C2: {e}"),
})?,
);
if let Some(received_hash_c2) = reply.hash_c2 {
if received_hash_c2.as_ref() == c2_hash_recomputed.as_ref() {
} else {
return Err(create_security_error_and_log!(
"process_handshake: hash_c2 mismatch"
));
}
} else {
debug!("Cannot compare hashes in process_handshake. Reply did not have any.");
}
let cc2_properties: Vec<BinaryProperty> = vec![
BinaryProperty::with_propagate(
"hash_c2",
Bytes::copy_from_slice(c2_hash_recomputed.as_ref()),
),
BinaryProperty::with_propagate(
"challenge2",
Bytes::copy_from_slice(reply.challenge2.as_ref()),
),
BinaryProperty::with_propagate("dh2", Bytes::copy_from_slice(reply.dh2.as_ref())),
BinaryProperty::with_propagate(
"challenge1",
Bytes::copy_from_slice(reply.challenge1.as_ref()),
),
BinaryProperty::with_propagate("dh1", Bytes::copy_from_slice(reply.dh1.as_ref())),
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)?;
cert2.verify_signed_data_with_algorithm(
to_vec::<Vec<BinaryProperty>, BigEndian>(&cc2_properties).map_err(|e| SecurityError {
msg: format!("Error serializing CC2: {e}"),
})?,
reply.signature,
c2_signature_algorithm,
)?;
let kagree_algo_in_reply = reply.c_kagree_algo;
let expected_kagree_algo = dh1.kagree_algo_name_str();
if kagree_algo_in_reply != expected_kagree_algo {
return Err(create_security_error_and_log!(
"Unexpected key agreement algorithm: {kagree_algo_in_reply:?} in \
HandshakeReplyMessageToken. Expected {expected_kagree_algo}"
));
}
let dh1_public_key = dh1.public_key_bytes()?;
let shared_secret = dh1.compute_shared_secret(reply.dh2.clone())?;
let cc_final_properties: Vec<BinaryProperty> = vec![
BinaryProperty::with_propagate("hash_c1", Bytes::copy_from_slice(hash_c1.as_ref())),
BinaryProperty::with_propagate("challenge1", Bytes::copy_from_slice(challenge1.as_ref())),
BinaryProperty::with_propagate("dh1", Bytes::copy_from_slice(dh1_public_key.as_ref())),
BinaryProperty::with_propagate(
"challenge2",
Bytes::copy_from_slice(reply.challenge2.as_ref()),
),
BinaryProperty::with_propagate("dh2", Bytes::copy_from_slice(reply.dh2.as_ref())),
BinaryProperty::with_propagate(
"hash_c2",
Bytes::copy_from_slice(c2_hash_recomputed.as_ref()),
),
];
let final_contents_signature = local_info.id_cert_private_key.sign(
&to_vec::<Vec<BinaryProperty>, BigEndian>(&cc_final_properties).map_err(|e| {
SecurityError {
msg: format!("Error serializing CC_final: {e}"),
}
})?,
)?;
let final_message_token = BuiltinHandshakeMessageToken {
class_id: Bytes::copy_from_slice(HANDSHAKE_FINAL_CLASS_ID),
c_id: None,
c_perm: None,
c_pdata: None,
c_dsign_algo: None,
c_kagree_algo: None,
ocsp_status: None, hash_c1: Some(Bytes::copy_from_slice(hash_c1.as_ref())), dh1: Some(dh1_public_key), hash_c2: Some(Bytes::copy_from_slice(c2_hash_recomputed.as_ref())), dh2: Some(reply.dh2),
challenge1: Some(Bytes::copy_from_slice(reply.challenge1.as_ref())),
challenge2: Some(Bytes::copy_from_slice(reply.challenge2.as_ref())),
signature: Some(final_contents_signature),
};
let remote_info = self.get_remote_participant_info_mutable(&remote_identity_handle)?;
remote_info.handshake.state = BuiltinHandshakeState::CompletedWithFinalMessageSent {
challenge1,
challenge2: reply.challenge2,
shared_secret,
};
remote_info.identity_certificate_opt = Some(cert2);
remote_info.signed_permissions_xml_opt = Some(reply.c_perm);
Ok((
ValidationOutcome::OkFinalMessage,
Some(HandshakeMessageToken::from(final_message_token)),
))
}
BuiltinHandshakeState::PendingFinalMessage {
hash_c1,
hash_c2,
dh1_public,
dh2,
challenge1,
challenge2,
remote_id_certificate,
} => {
let handshake_token = BuiltinHandshakeMessageToken::try_from(handshake_message_in)?;
let final_token = handshake_token.extract_final()?;
if let Some(received_hash_c1) = final_token.hash_c1 {
if hash_c1 != received_hash_c1 {
return Err(create_security_error_and_log!(
"Hash C1 mismatch on authentication final receive"
));
}
}
if let Some(received_hash_c2) = final_token.hash_c2 {
if hash_c2 != received_hash_c2 {
return Err(create_security_error_and_log!(
"Hash C2 mismatch on authentication final receive"
));
}
}
if dh1_public != final_token.dh1 {
return Err(create_security_error_and_log!(
"Diffie-Hellman parameter DH1 mismatch on authentication final receive"
));
}
let dh2_public_key = dh2.public_key_bytes()?;
if dh2_public_key.as_ref() != final_token.dh2.as_ref() {
return Err(create_security_error_and_log!(
"Diffie-Hellman parameter DH2 mismatch on authentication final receive"
));
}
if challenge1 != final_token.challenge1 {
return Err(create_security_error_and_log!(
"process_handshake: Final token challenge1 mismatch"
));
}
if challenge2 != final_token.challenge2 {
return Err(create_security_error_and_log!(
"process_handshake: Final token challenge2 mismatch"
));
}
let cc_final_properties: Vec<BinaryProperty> = vec![
BinaryProperty::with_propagate("hash_c1", Bytes::copy_from_slice(hash_c1.as_ref())),
BinaryProperty::with_propagate("challenge1", Bytes::copy_from_slice(challenge1.as_ref())),
BinaryProperty::with_propagate("dh1", Bytes::copy_from_slice(dh1_public.as_ref())),
BinaryProperty::with_propagate("challenge2", Bytes::copy_from_slice(challenge2.as_ref())),
BinaryProperty::with_propagate("dh2", Bytes::copy_from_slice(dh2_public_key.as_ref())),
BinaryProperty::with_propagate("hash_c2", Bytes::copy_from_slice(hash_c2.as_ref())),
];
let remote_signature_algo_name = remote_id_certificate.signature_algorithm_identifier()?;
let remote_signature_algorithm =
parse_signature_algo_name_to_ring(&remote_signature_algo_name)?;
remote_id_certificate
.verify_signed_data_with_algorithm(
to_vec::<Vec<BinaryProperty>, BigEndian>(&cc_final_properties).map_err(|e| {
SecurityError {
msg: format!("Error serializing CC_final: {e}"),
}
})?,
final_token.signature,
remote_signature_algorithm,
)
.map_err(|e| {
create_security_error_and_log!(
"Signature verification failed in process_handshake: {e:?}"
)
})?;
let shared_secret = dh2.compute_shared_secret(dh1_public)?;
let remote_info = self.get_remote_participant_info_mutable(&remote_identity_handle)?;
remote_info.handshake.state = BuiltinHandshakeState::CompletedWithFinalMessageReceived {
challenge1,
challenge2,
shared_secret,
};
Ok((ValidationOutcome::Ok, None))
}
other_state => Err(create_security_error_and_log!(
"Unexpected handshake state: {:?}",
other_state
)),
}
}
fn get_shared_secret(
&self,
remote_identity_handle: IdentityHandle,
) -> SecurityResult<SharedSecretHandle> {
let remote_info = self.get_remote_participant_info(&remote_identity_handle)?;
match &remote_info.handshake.state {
BuiltinHandshakeState::CompletedWithFinalMessageSent {
challenge1,
challenge2,
shared_secret,
} => Ok(SharedSecretHandle {
challenge1: challenge1.clone(),
challenge2: challenge2.clone(),
shared_secret: shared_secret.clone(),
}),
BuiltinHandshakeState::CompletedWithFinalMessageReceived {
challenge1,
challenge2,
shared_secret,
} => Ok(SharedSecretHandle {
challenge1: challenge1.clone(),
challenge2: challenge2.clone(),
shared_secret: shared_secret.clone(),
}),
wrong_state => Err(create_security_error_and_log!(
"get_shared_secret called with wrong state {wrong_state:?}"
)),
}
}
fn get_authenticated_peer_credential_token(
&self,
handshake_handle: HandshakeHandle,
) -> SecurityResult<AuthenticatedPeerCredentialToken> {
let identity_handle = self.handshake_handle_to_identity_handle(&handshake_handle)?;
let remote_info = self.get_remote_participant_info(identity_handle)?;
let id_cert = remote_info
.identity_certificate_opt
.clone()
.ok_or_else(|| {
security_error(
"Remote's identity certificate missing. It should have been stored from authentication \
handshake messages",
)
})?;
let permissions_doc = remote_info
.signed_permissions_xml_opt
.clone()
.ok_or_else(|| {
security_error(
"Remote's permissions document missing. It should have been stored from authentication \
handshake messages",
)
})?;
let builtin_token = BuiltinAuthenticatedPeerCredentialToken {
c_id: Bytes::from(id_cert.to_pem()),
c_perm: permissions_doc,
};
Ok(AuthenticatedPeerCredentialToken::from(builtin_token))
}
fn set_listener(&self) -> SecurityResult<()> {
Err(create_security_error_and_log!(
"set_listener not supported. Use status events in DataReader/DataWriter instead."
))
}
}
#[cfg(test)]
mod tests {
use crate::structure::guid::EntityKind;
use super::*;
#[test]
pub fn validating_invalid_remote_guid_fails() {
let cert_pem = r#"-----BEGIN CERTIFICATE-----
MIIBOzCB4qADAgECAhR361786/qVPfJWWDw4Wg5cmJUwBTAKBggqhkjOPQQDAjAS
MRAwDgYDVQQDDAdzcm9zMkNBMB4XDTIzMDcyMzA4MjgzNloXDTMzMDcyMTA4Mjgz
NlowEjEQMA4GA1UEAwwHc3JvczJDQTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IA
BMpvJQ/91ZqnmRRteTL2qaEFz2d7SGAQQk9PIhhZCV1tlLwYf/hI4xWLJaEv8FxJ
TjxXRGJ1U+/IqqqIvJVpWaSjFjAUMBIGA1UdEwEB/wQIMAYBAf8CAQEwCgYIKoZI
zj0EAwIDSAAwRQIgEiyVGRc664+/TE/HImA4WNwsSi/alHqPYB58BWINj34CIQDD
iHhbVPRB9Uxts9CwglxYgZoUdGUAxreYIIaLO4yLqw==
-----END CERTIFICATE-----
"#;
let invalid_guid = GUID::dummy_test_guid(EntityKind::PARTICIPANT_BUILT_IN);
let some_certificate = Certificate::from_pem(cert_pem).unwrap();
let validation_res = validate_remote_guid(invalid_guid, &some_certificate);
assert!(
validation_res.is_err(),
"Validating an invalid GUID passed!"
);
}
}