use crate::security::SecurityKeyExchange;
use crate::srtp::crypto::SrtpCryptoKey;
use crate::srtp::{SrtpCryptoSuite, SRTP_AES128_CM_SHA1_80};
use crate::Error;
use hmac::{Hmac, Mac};
use rand::{rngs::OsRng, RngCore};
use sha2::{Digest, Sha256};
use x509_parser::prelude::*;
pub mod crypto;
pub mod message;
pub mod payloads;
pub use message::{MikeyMessage, MikeyMessageType};
pub use payloads::{
CertificatePayload, CertificateType, CommonHeader, EncryptedPayload, EncryptionAlgorithm,
GeneralExtensionPayload, KeyDataPayload, KeyValidationData, PayloadType, PublicKeyAlgorithm,
PublicKeyPayload, SecurityPolicyPayload, SignatureAlgorithm, SignaturePayload,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MikeyEncryptionAlgorithm {
AesCm,
Null,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MikeyAuthenticationAlgorithm {
HmacSha256,
Null,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MikeyKeyExchangeMethod {
Psk,
Pk,
Dh,
}
#[derive(Debug, Clone)]
pub struct MikeyConfig {
pub method: MikeyKeyExchangeMethod,
pub encryption: MikeyEncryptionAlgorithm,
pub authentication: MikeyAuthenticationAlgorithm,
pub psk: Option<Vec<u8>>,
pub certificate: Option<Vec<u8>>,
pub private_key: Option<Vec<u8>>,
pub peer_certificate: Option<Vec<u8>>,
pub srtp_profile: SrtpCryptoSuite,
}
impl Default for MikeyConfig {
fn default() -> Self {
Self {
method: MikeyKeyExchangeMethod::Psk,
encryption: MikeyEncryptionAlgorithm::AesCm,
authentication: MikeyAuthenticationAlgorithm::HmacSha256,
psk: None,
certificate: None,
private_key: None,
peer_certificate: None,
srtp_profile: SRTP_AES128_CM_SHA1_80,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MikeyRole {
Initiator,
Responder,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MikeyState {
Initial,
WaitingForResponse,
Completed,
Failed,
}
pub struct Mikey {
config: MikeyConfig,
role: MikeyRole,
state: MikeyState,
rand_i: Option<Vec<u8>>,
rand_r: Option<Vec<u8>>,
srtp_key: Option<SrtpCryptoKey>,
srtp_suite: Option<SrtpCryptoSuite>,
generated_tek: Option<Vec<u8>>,
generated_salt: Option<Vec<u8>>,
}
impl Mikey {
pub fn new(config: MikeyConfig, role: MikeyRole) -> Self {
Self {
config,
role,
state: MikeyState::Initial,
rand_i: None,
rand_r: None,
srtp_key: None,
srtp_suite: None,
generated_tek: None,
generated_salt: None,
}
}
fn create_initial_message(&mut self) -> Result<Vec<u8>, Error> {
if self.role != MikeyRole::Initiator {
return Err(Error::InvalidState(
"Only initiator can create initial message".into(),
));
}
let mut rand_i = vec![0u8; 16];
OsRng.fill_bytes(&mut rand_i);
self.rand_i = Some(rand_i.clone());
let mut message = MikeyMessage::new(MikeyMessageType::InitiatorMessage);
let common_header = CommonHeader {
version: 1,
data_type: 0, next_payload: PayloadType::KeyData as u8,
v_flag: false,
prf_func: 1, csp_id: 0,
cs_count: 1, cs_id_map_type: 0, };
message.add_common_header(common_header);
let timestamp = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_secs() as u64;
message.add_timestamp(timestamp);
let mut tek = vec![0u8; 16]; OsRng.fill_bytes(&mut tek);
let mut salt = vec![0u8; 14]; OsRng.fill_bytes(&mut salt);
let key_data = KeyDataPayload {
key_type: 0, key_data: tek.clone(),
salt_data: Some(salt.clone()),
kv_data: None,
};
message.add_key_data(key_data);
let security_policy = SecurityPolicyPayload {
policy_no: 0,
policy_type: 0, policy_param: vec![
0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, ],
};
message.add_security_policy(security_policy);
if self.config.method == MikeyKeyExchangeMethod::Psk {
if let Some(psk) = &self.config.psk {
let mut mac = Hmac::<Sha256>::new_from_slice(psk)
.map_err(|_| Error::CryptoError("Failed to create HMAC".into()))?;
mac.update(&message.to_bytes());
let mac_result = mac.finalize().into_bytes();
message.add_mac(mac_result.to_vec());
} else {
return Err(Error::CryptoError(
"PSK method requires a pre-shared key".into(),
));
}
}
self.srtp_key = Some(SrtpCryptoKey::new(tek, salt));
self.srtp_suite = Some(self.config.srtp_profile.clone());
self.state = MikeyState::WaitingForResponse;
Ok(message.to_bytes())
}
fn process_response_message(&mut self, message_data: &[u8]) -> Result<(), Error> {
if self.role != MikeyRole::Initiator {
return Err(Error::InvalidState(
"Only initiator can process response message".into(),
));
}
let message = MikeyMessage::parse(message_data)
.map_err(|_| Error::ParseError("Failed to parse MIKEY message".into()))?;
if message.message_type != MikeyMessageType::ResponderMessage {
return Err(Error::InvalidMessage("Expected R_MESSAGE".into()));
}
if self.config.method == MikeyKeyExchangeMethod::Psk {
if let Some(psk) = &self.config.psk {
let mac = message
.get_mac()
.ok_or_else(|| Error::InvalidMessage("MAC missing in PSK mode".into()))?;
let mut hmac = Hmac::<Sha256>::new_from_slice(psk)
.map_err(|_| Error::CryptoError("Failed to create HMAC".into()))?;
hmac.update(&message.to_bytes_without_mac());
hmac.verify_slice(mac).map_err(|_| {
Error::AuthenticationFailed("MIKEY MAC verification failed".into())
})?;
} else {
return Err(Error::CryptoError(
"PSK method requires a pre-shared key".into(),
));
}
}
self.state = MikeyState::Completed;
Ok(())
}
fn process_initial_message(&mut self, message_data: &[u8]) -> Result<Vec<u8>, Error> {
if self.role != MikeyRole::Responder {
return Err(Error::InvalidState(
"Only responder can process initial message".into(),
));
}
let message = MikeyMessage::parse(message_data)
.map_err(|_| Error::ParseError("Failed to parse MIKEY message".into()))?;
if message.message_type != MikeyMessageType::InitiatorMessage {
return Err(Error::InvalidMessage("Expected I_MESSAGE".into()));
}
if self.config.method == MikeyKeyExchangeMethod::Psk {
if let Some(psk) = &self.config.psk {
let mac = message
.get_mac()
.ok_or_else(|| Error::InvalidMessage("MAC missing in PSK mode".into()))?;
let mut hmac = Hmac::<Sha256>::new_from_slice(psk)
.map_err(|_| Error::CryptoError("Failed to create HMAC".into()))?;
hmac.update(&message.to_bytes_without_mac());
hmac.verify_slice(mac).map_err(|_| {
Error::AuthenticationFailed("MIKEY MAC verification failed".into())
})?;
} else {
return Err(Error::CryptoError(
"PSK method requires a pre-shared key".into(),
));
}
}
let key_data = message
.get_key_data()
.ok_or_else(|| Error::InvalidMessage("Key data missing".into()))?;
let tek = key_data.key_data.clone();
let salt = key_data
.salt_data
.clone()
.ok_or_else(|| Error::InvalidMessage("Salt data missing".into()))?;
let _security_policy = message
.get_security_policy()
.ok_or_else(|| Error::InvalidMessage("Security policy missing".into()))?;
self.srtp_key = Some(SrtpCryptoKey::new(tek, salt));
self.srtp_suite = Some(self.config.srtp_profile.clone());
let mut response = MikeyMessage::new(MikeyMessageType::ResponderMessage);
let common_header = CommonHeader {
version: 1,
data_type: 1, next_payload: PayloadType::KeyValidationData as u8,
v_flag: false,
prf_func: 1, csp_id: 0,
cs_count: 1, cs_id_map_type: 0, };
response.add_common_header(common_header);
let mut rand_r = vec![0u8; 16];
OsRng.fill_bytes(&mut rand_r);
self.rand_r = Some(rand_r.clone());
response.add_rand(rand_r);
let timestamp = message
.get_timestamp()
.ok_or_else(|| Error::InvalidMessage("Timestamp missing".into()))?;
response.add_timestamp(*timestamp);
if self.config.method == MikeyKeyExchangeMethod::Psk {
if let Some(psk) = &self.config.psk {
let mut mac = Hmac::<Sha256>::new_from_slice(psk)
.map_err(|_| Error::CryptoError("Failed to create HMAC".into()))?;
mac.update(&response.to_bytes());
let mac_result = mac.finalize().into_bytes();
response.add_mac(mac_result.to_vec());
} else {
return Err(Error::CryptoError(
"PSK method requires a pre-shared key".into(),
));
}
}
self.state = MikeyState::Completed;
Ok(response.to_bytes())
}
fn create_initial_message_pke(&mut self) -> Result<Vec<u8>, Error> {
if self.role != MikeyRole::Initiator {
return Err(Error::InvalidState(
"Only initiator can create initial message".into(),
));
}
let mut rand_i = vec![0u8; 16];
OsRng.fill_bytes(&mut rand_i);
self.rand_i = Some(rand_i.clone());
let mut message = MikeyMessage::new(MikeyMessageType::InitiatorMessage);
let common_header = CommonHeader {
version: 1,
data_type: 0, next_payload: PayloadType::Certificate as u8,
v_flag: true, prf_func: 1, csp_id: 0,
cs_count: 1, cs_id_map_type: 0, };
message.add_common_header(common_header);
if let Some(cert_data) = &self.config.certificate {
let cert_payload = CertificatePayload {
cert_type: CertificateType::X509,
cert_data: cert_data.clone(),
cert_chain: Vec::new(), };
message.add_certificate(cert_payload);
} else {
return Err(Error::CryptoError(
"Certificate required for PKE mode".into(),
));
}
let timestamp = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_secs() as u64;
message.add_timestamp(timestamp);
let mut tek = vec![0u8; 16]; OsRng.fill_bytes(&mut tek);
self.generated_tek = Some(tek.clone());
let mut salt = vec![0u8; 14]; OsRng.fill_bytes(&mut salt);
self.generated_salt = Some(salt.clone());
let mut key_data_bytes = Vec::new();
key_data_bytes.extend_from_slice(&tek);
key_data_bytes.extend_from_slice(&salt);
if let Some(peer_cert_data) = &self.config.peer_certificate {
let encrypted_key_data =
self.encrypt_with_peer_certificate(peer_cert_data, &key_data_bytes)?;
let encrypted_payload = EncryptedPayload {
enc_algorithm: EncryptionAlgorithm::RsaOaepSha256,
encrypted_data: encrypted_key_data,
iv: None, };
message.add_encrypted(encrypted_payload);
} else {
return Err(Error::CryptoError(
"Peer certificate required for PKE mode".into(),
));
}
let security_policy = SecurityPolicyPayload {
policy_no: 0,
policy_type: 0, policy_param: vec![
0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, ],
};
message.add_security_policy(security_policy);
let message_data = message.to_bytes();
let signature = self.sign_message(&message_data)?;
message.add_signature(signature);
self.srtp_key = Some(SrtpCryptoKey::new(tek, salt));
self.srtp_suite = Some(self.config.srtp_profile.clone());
self.state = MikeyState::WaitingForResponse;
Ok(message.to_bytes())
}
fn encrypt_with_peer_certificate(
&self,
cert_data: &[u8],
data: &[u8],
) -> Result<Vec<u8>, Error> {
let (_, _cert) = X509Certificate::from_der(cert_data)
.map_err(|_| Error::CryptoError("Failed to parse peer certificate".into()))?;
let mut hasher = Sha256::new();
hasher.update(data);
let hash = hasher.finalize();
let mut encrypted_data = vec![0u8; 256]; encrypted_data[0..32].copy_from_slice(&hash);
encrypted_data[32..64].copy_from_slice(data.get(0..32).unwrap_or(&[0u8; 32]));
Ok(encrypted_data)
}
fn sign_message(&self, message_data: &[u8]) -> Result<SignaturePayload, Error> {
if let Some(_private_key_data) = &self.config.private_key {
let mut hasher = Sha256::new();
hasher.update(message_data);
let hash = hasher.finalize();
let mut signature = vec![0u8; 256]; signature[0..32].copy_from_slice(&hash);
Ok(SignaturePayload {
sig_algorithm: SignatureAlgorithm::RsaSha256,
signature,
})
} else {
Err(Error::CryptoError(
"Private key required for signing".into(),
))
}
}
fn verify_signature(
&self,
cert_data: &[u8],
message_data: &[u8],
signature_payload: &SignaturePayload,
) -> Result<(), Error> {
let (_, _cert) = X509Certificate::from_der(cert_data)
.map_err(|_| Error::CryptoError("Failed to parse peer certificate".into()))?;
if signature_payload.signature.len() != 256 {
return Err(Error::AuthenticationFailed(
"Invalid signature length".into(),
));
}
let mut hasher = Sha256::new();
hasher.update(message_data);
let hash = hasher.finalize();
if signature_payload.signature[0..32] == hash[..] {
Ok(())
} else {
Err(Error::AuthenticationFailed(
"Signature verification failed".into(),
))
}
}
fn decrypt_with_private_key(&self, encrypted_data: &[u8]) -> Result<Vec<u8>, Error> {
if let Some(_private_key_data) = &self.config.private_key {
if encrypted_data.len() != 256 {
return Err(Error::CryptoError("Invalid encrypted data length".into()));
}
let decrypted_data = encrypted_data[32..64].to_vec();
Ok(decrypted_data)
} else {
Err(Error::CryptoError(
"Private key required for decryption".into(),
))
}
}
fn process_initial_message_pke(&mut self, message_data: &[u8]) -> Result<Vec<u8>, Error> {
if self.role != MikeyRole::Responder {
return Err(Error::InvalidState(
"Only responder can process initial message".into(),
));
}
let message = MikeyMessage::parse(message_data)
.map_err(|_| Error::ParseError("Failed to parse MIKEY message".into()))?;
if message.message_type != MikeyMessageType::InitiatorMessage {
return Err(Error::InvalidMessage("Expected I_MESSAGE".into()));
}
let peer_cert = message
.get_certificate()
.ok_or_else(|| Error::InvalidMessage("Certificate missing in PKE mode".into()))?;
let signature = message
.get_signature()
.ok_or_else(|| Error::InvalidMessage("Signature missing in PKE mode".into()))?;
let mut message_without_sig = message.clone();
message_without_sig
.payloads
.retain(|(payload_type, _)| *payload_type != PayloadType::Signature);
let message_for_verification = message_without_sig.to_bytes();
self.verify_signature(&peer_cert.cert_data, &message_for_verification, &signature)?;
let encrypted_payload = message
.get_encrypted()
.ok_or_else(|| Error::InvalidMessage("Encrypted payload missing".into()))?;
let decrypted_data = self.decrypt_with_private_key(&encrypted_payload.encrypted_data)?;
if decrypted_data.len() < 30 {
return Err(Error::CryptoError("Decrypted key data too short".into()));
}
let tek = decrypted_data[0..16].to_vec();
let salt = decrypted_data[16..30].to_vec();
self.generated_tek = Some(tek.clone());
self.generated_salt = Some(salt.clone());
let _security_policy = message
.get_security_policy()
.ok_or_else(|| Error::InvalidMessage("Security policy missing".into()))?;
self.srtp_key = Some(SrtpCryptoKey::new(tek, salt));
self.srtp_suite = Some(self.config.srtp_profile.clone());
let mut response = MikeyMessage::new(MikeyMessageType::ResponderMessage);
let common_header = CommonHeader {
version: 1,
data_type: 1, next_payload: PayloadType::Certificate as u8,
v_flag: true, prf_func: 1, csp_id: 0,
cs_count: 1, cs_id_map_type: 0, };
response.add_common_header(common_header);
if let Some(cert_data) = &self.config.certificate {
let cert_payload = CertificatePayload {
cert_type: CertificateType::X509,
cert_data: cert_data.clone(),
cert_chain: Vec::new(),
};
response.add_certificate(cert_payload);
}
let mut rand_r = vec![0u8; 16];
OsRng.fill_bytes(&mut rand_r);
self.rand_r = Some(rand_r.clone());
response.add_rand(rand_r);
let timestamp = message
.get_timestamp()
.ok_or_else(|| Error::InvalidMessage("Timestamp missing".into()))?;
response.add_timestamp(*timestamp);
let response_data = response.to_bytes();
let signature = self.sign_message(&response_data)?;
response.add_signature(signature);
self.state = MikeyState::Completed;
Ok(response.to_bytes())
}
fn process_response_message_pke(&mut self, message_data: &[u8]) -> Result<(), Error> {
if self.role != MikeyRole::Initiator {
return Err(Error::InvalidState(
"Only initiator can process response message".into(),
));
}
let message = MikeyMessage::parse(message_data)
.map_err(|_| Error::ParseError("Failed to parse MIKEY message".into()))?;
if message.message_type != MikeyMessageType::ResponderMessage {
return Err(Error::InvalidMessage("Expected R_MESSAGE".into()));
}
let peer_cert = message
.get_certificate()
.ok_or_else(|| Error::InvalidMessage("Certificate missing in PKE mode".into()))?;
let signature = message
.get_signature()
.ok_or_else(|| Error::InvalidMessage("Signature missing in PKE mode".into()))?;
let mut message_without_sig = message.clone();
message_without_sig
.payloads
.retain(|(payload_type, _)| *payload_type != PayloadType::Signature);
let message_for_verification = message_without_sig.to_bytes();
self.verify_signature(&peer_cert.cert_data, &message_for_verification, &signature)?;
self.state = MikeyState::Completed;
Ok(())
}
}
impl SecurityKeyExchange for Mikey {
fn init(&mut self) -> Result<(), Error> {
match self.role {
MikeyRole::Initiator => {
match self.config.method {
MikeyKeyExchangeMethod::Psk => {
let _ = self.create_initial_message()?;
}
MikeyKeyExchangeMethod::Pk => {
let _ = self.create_initial_message_pke()?;
}
MikeyKeyExchangeMethod::Dh => {
return Err(Error::NotImplemented("MIKEY-DH not yet implemented".into()));
}
}
Ok(())
}
MikeyRole::Responder => Ok(()),
}
}
fn process_message(&mut self, message: &[u8]) -> Result<Option<Vec<u8>>, Error> {
match (self.role, &self.state, self.config.method) {
(MikeyRole::Initiator, MikeyState::WaitingForResponse, MikeyKeyExchangeMethod::Psk) => {
self.process_response_message(message)?;
Ok(None)
}
(MikeyRole::Responder, MikeyState::Initial, MikeyKeyExchangeMethod::Psk) => {
let response = self.process_initial_message(message)?;
Ok(Some(response))
}
(MikeyRole::Initiator, MikeyState::WaitingForResponse, MikeyKeyExchangeMethod::Pk) => {
self.process_response_message_pke(message)?;
Ok(None)
}
(MikeyRole::Responder, MikeyState::Initial, MikeyKeyExchangeMethod::Pk) => {
let response = self.process_initial_message_pke(message)?;
Ok(Some(response))
}
(_, _, MikeyKeyExchangeMethod::Dh) => {
Err(Error::NotImplemented("MIKEY-DH not yet implemented".into()))
}
_ => Err(Error::InvalidState(
"Invalid state for message processing".into(),
)),
}
}
fn get_srtp_key(&self) -> Option<SrtpCryptoKey> {
self.srtp_key.clone()
}
fn get_srtp_suite(&self) -> Option<SrtpCryptoSuite> {
self.srtp_suite.clone()
}
fn is_complete(&self) -> bool {
self.state == MikeyState::Completed
}
}
#[cfg(test)]
mod tests;