use std::{
collections::HashMap,
fmt::{self, Formatter},
};
use bytes::Bytes;
use openssl::{bn::BigNum, pkey::Private};
use ring::agreement;
use crate::{
create_security_error_and_log,
security::{
access_control::PermissionsToken, certificate, private_key, security_error, SecurityError,
SecurityResult,
},
GUID,
};
use self::types::{DH_MODP_KAGREE_ALGO_NAME, ECDH_KAGREE_ALGO_NAME};
use super::{
authentication_builtin::types::BuiltinIdentityToken, Challenge, HandshakeHandle, IdentityHandle,
Sha256, SharedSecret,
};
mod authentication;
pub(in crate::security) mod types;
#[derive(Debug)]
#[allow(clippy::large_enum_variant)] pub(crate) enum BuiltinHandshakeState {
PendingRequestSend, PendingRequestMessage, PendingReplyMessage {
dh1: DHKeys, challenge1: Challenge, hash_c1: Sha256, },
PendingFinalMessage {
hash_c1: Sha256,
hash_c2: Sha256,
dh1_public: Bytes, challenge1: Challenge, dh2: DHKeys, challenge2: Challenge, remote_id_certificate: certificate::Certificate,
},
CompletedWithFinalMessageSent {
challenge1: Challenge, challenge2: Challenge, shared_secret: SharedSecret,
},
CompletedWithFinalMessageReceived {
challenge1: Challenge, challenge2: Challenge, shared_secret: SharedSecret,
},
}
#[derive(Clone, Copy, PartialEq, Debug)]
pub(crate) enum DiscHandshakeState {
PendingRequestSend,
PendingRequestMessage,
PendingReplyMessage,
PendingFinalMessage,
CompletedWithFinalMessageSent,
CompletedWithFinalMessageReceived,
}
struct LocalParticipantInfo {
identity_handle: IdentityHandle,
identity_token: BuiltinIdentityToken,
guid: GUID,
id_cert_private_key: private_key::PrivateKey, identity_certificate: certificate::Certificate, identity_ca: certificate::Certificate,
signed_permissions_document_xml: Bytes, local_permissions_token: Option<PermissionsToken>,
}
struct RemoteParticipantInfo {
identity_certificate_opt: Option<certificate::Certificate>,
signed_permissions_xml_opt: Option<Bytes>,
handshake: HandshakeInfo,
}
struct HandshakeInfo {
state: BuiltinHandshakeState,
}
pub enum DHKeys {
Modp(openssl::dh::Dh<Private>), EC(ring::agreement::EphemeralPrivateKey), }
impl DHKeys {
fn new_modp_keys() -> SecurityResult<Self> {
let dh_params = openssl::dh::Dh::get_2048_256()?;
let modp_keys = dh_params.generate_key()?;
Ok(Self::Modp(modp_keys))
}
fn new_ec_keys(secure_rng: &ring::rand::SystemRandom) -> SecurityResult<Self> {
let ec_keys = agreement::EphemeralPrivateKey::generate(&agreement::ECDH_P256, secure_rng)?;
Ok(Self::EC(ec_keys))
}
fn public_key_bytes(&self) -> SecurityResult<Bytes> {
let vec = match self {
DHKeys::Modp(openssl_dh) => openssl_dh.public_key().to_vec(),
DHKeys::EC(ring_dh) => {
let ring_pub_key = ring_dh.compute_public_key()?;
Vec::from(ring_pub_key.as_ref())
}
};
Ok(Bytes::from(vec))
}
fn compute_shared_secret(self, remote_dh_public_key: Bytes) -> SecurityResult<SharedSecret> {
let shared_secret = match self {
DHKeys::Modp(openssl_dh) => {
let remote_public = BigNum::from_slice(&remote_dh_public_key)?;
let secret_key = openssl_dh.compute_key(&remote_public)?;
SharedSecret::from(Sha256::hash(&secret_key))
}
DHKeys::EC(ring_dh) => {
let unparsed_remote_dh_public_key =
agreement::UnparsedPublicKey::new(&agreement::ECDH_P256, remote_dh_public_key);
agreement::agree_ephemeral(
ring_dh,
&unparsed_remote_dh_public_key,
|raw_shared_secret| SharedSecret::from(Sha256::hash(raw_shared_secret)),
)?
}
};
Ok(shared_secret)
}
fn kagree_algo_name_str(&self) -> &'static str {
match self {
DHKeys::Modp(_) => DH_MODP_KAGREE_ALGO_NAME,
DHKeys::EC(_) => ECDH_KAGREE_ALGO_NAME,
}
}
}
impl std::fmt::Debug for DHKeys {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
DHKeys::Modp(_dh) => f.debug_struct("Dh<Private>: TODO Debug print").finish(),
DHKeys::EC(dh) => dh.fmt(f),
}
}
}
pub struct AuthenticationBuiltin {
local_participant_info: Option<LocalParticipantInfo>,
remote_participant_infos: HashMap<IdentityHandle, RemoteParticipantInfo>,
handshake_to_identity_handle_map: HashMap<HandshakeHandle, IdentityHandle>,
next_identity_handle: IdentityHandle,
next_handshake_handle: HandshakeHandle,
secure_random_generator: ring::rand::SystemRandom,
}
impl AuthenticationBuiltin {
pub fn new() -> Self {
Self {
local_participant_info: None, remote_participant_infos: HashMap::new(),
handshake_to_identity_handle_map: HashMap::new(),
next_identity_handle: 0,
next_handshake_handle: 0,
secure_random_generator: ring::rand::SystemRandom::new(),
}
}
fn get_new_identity_handle(&mut self) -> IdentityHandle {
let new_handle = self.next_identity_handle;
self.next_identity_handle += 1;
new_handle
}
fn get_new_handshake_handle(&mut self) -> HandshakeHandle {
let new_handle = self.next_handshake_handle;
self.next_handshake_handle += 1;
new_handle
}
fn get_local_participant_info(&self) -> SecurityResult<&LocalParticipantInfo> {
self.local_participant_info.as_ref().ok_or_else(|| {
create_security_error_and_log!(
"Local participant info not found. Has the local identity been validated?"
)
})
}
fn get_local_participant_info_mutable(&mut self) -> SecurityResult<&mut LocalParticipantInfo> {
self.local_participant_info.as_mut().ok_or_else(|| {
create_security_error_and_log!(
"Local participant info not found. Has the local identity been validated?"
)
})
}
fn get_remote_participant_info(
&self,
identity_handle: &IdentityHandle,
) -> SecurityResult<&RemoteParticipantInfo> {
self
.remote_participant_infos
.get(identity_handle)
.ok_or_else(|| create_security_error_and_log!("Remote participant info not found"))
}
fn get_remote_participant_info_mutable(
&mut self,
identity_handle: &IdentityHandle,
) -> SecurityResult<&mut RemoteParticipantInfo> {
self
.remote_participant_infos
.get_mut(identity_handle)
.ok_or_else(|| create_security_error_and_log!("Remote participant info not found"))
}
fn handshake_handle_to_identity_handle(
&self,
hs_handle: &HandshakeHandle,
) -> SecurityResult<&IdentityHandle> {
self
.handshake_to_identity_handle_map
.get(hs_handle)
.ok_or_else(|| {
create_security_error_and_log!("Identity handle not found with handshake handle")
})
}
fn generate_random_32_bytes(&self) -> SecurityResult<[u8; 32]> {
ring::rand::generate::<[u8; 32]>(&self.secure_random_generator)
.map(|random| random.expose())
.map_err(|e| security_error(&format!("Failed to generate random bytes: {e}")))
}
}