use std::io::{Read, Write};
use opcua_types::{
ByteString, DecodingOptions, EncodingResult, Error, SimpleBinaryDecodable,
SimpleBinaryEncodable, UAString,
};
use opcua_types::{constants, status_code::StatusCode};
use opcua_crypto::{SecurityPolicy, Thumbprint, X509};
#[derive(Debug, Clone, PartialEq)]
pub enum SecurityHeader {
Asymmetric(AsymmetricSecurityHeader),
Symmetric(SymmetricSecurityHeader),
}
impl SimpleBinaryEncodable for SecurityHeader {
fn byte_len(&self) -> usize {
match self {
SecurityHeader::Asymmetric(value) => value.byte_len(),
SecurityHeader::Symmetric(value) => value.byte_len(),
}
}
fn encode<S: Write + ?Sized>(&self, stream: &mut S) -> EncodingResult<()> {
match self {
SecurityHeader::Asymmetric(value) => value.encode(stream),
SecurityHeader::Symmetric(value) => value.encode(stream),
}
}
}
impl SecurityHeader {
pub fn decode_from_stream<S: Read + ?Sized>(
stream: &mut S,
is_open_secure_channel: bool,
decoding_options: &DecodingOptions,
) -> EncodingResult<Self> {
if is_open_secure_channel {
let security_header = AsymmetricSecurityHeader::decode(stream, decoding_options)?;
let security_policy = if security_header.security_policy_uri.is_empty() {
SecurityPolicy::None
} else {
SecurityPolicy::from_uri(security_header.security_policy_uri.as_ref())
};
if security_policy == SecurityPolicy::Unknown {
return Err(Error::new(
StatusCode::BadSecurityPolicyRejected,
format!(
"Security policy of chunk is unknown, policy = {:?}",
security_header.security_policy_uri
),
));
}
Ok(SecurityHeader::Asymmetric(security_header))
} else {
let security_header = SymmetricSecurityHeader::decode(stream, decoding_options)?;
Ok(SecurityHeader::Symmetric(security_header))
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct SymmetricSecurityHeader {
pub token_id: u32,
}
impl SimpleBinaryEncodable for SymmetricSecurityHeader {
fn byte_len(&self) -> usize {
4
}
fn encode<S: Write + ?Sized>(&self, stream: &mut S) -> EncodingResult<()> {
self.token_id.encode(stream)
}
}
impl SimpleBinaryDecodable for SymmetricSecurityHeader {
fn decode<S: Read + ?Sized>(
stream: &mut S,
decoding_options: &DecodingOptions,
) -> EncodingResult<Self> {
let token_id = u32::decode(stream, decoding_options)?;
Ok(SymmetricSecurityHeader { token_id })
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct AsymmetricSecurityHeader {
pub security_policy_uri: UAString,
pub sender_certificate: ByteString,
pub receiver_certificate_thumbprint: ByteString,
}
impl SimpleBinaryEncodable for AsymmetricSecurityHeader {
fn byte_len(&self) -> usize {
let mut size = 0;
size += self.security_policy_uri.byte_len();
size += self.sender_certificate.byte_len();
size += self.receiver_certificate_thumbprint.byte_len();
size
}
fn encode<S: Write + ?Sized>(&self, stream: &mut S) -> EncodingResult<()> {
self.security_policy_uri.encode(stream)?;
self.sender_certificate.encode(stream)?;
self.receiver_certificate_thumbprint.encode(stream)?;
Ok(())
}
}
impl SimpleBinaryDecodable for AsymmetricSecurityHeader {
fn decode<S: Read + ?Sized>(
stream: &mut S,
decoding_options: &DecodingOptions,
) -> EncodingResult<Self> {
let security_policy_uri = UAString::decode(stream, decoding_options)?;
let sender_certificate = ByteString::decode(stream, decoding_options)?;
let receiver_certificate_thumbprint = ByteString::decode(stream, decoding_options)?;
if sender_certificate
.value
.as_ref()
.is_some_and(|v| v.len() >= constants::MAX_CERTIFICATE_LENGTH)
{
Err(Error::new(
StatusCode::BadEncodingLimitsExceeded,
format!(
"Sender certificate has length {}, which exceeds max certificate size {}",
sender_certificate
.value
.as_ref()
.map(|v| v.len())
.unwrap_or_default(),
constants::MAX_CERTIFICATE_LENGTH
),
))
} else {
let thumbprint_len = if let Some(value) = &receiver_certificate_thumbprint.value {
value.len()
} else {
0
};
if thumbprint_len > 0 && thumbprint_len != Thumbprint::THUMBPRINT_SIZE {
Err(Error::decoding(format!(
"Receiver certificate thumbprint is not 20 bytes long, {} bytes",
receiver_certificate_thumbprint
.value
.as_ref()
.unwrap()
.len(),
)))
} else {
Ok(AsymmetricSecurityHeader {
security_policy_uri,
sender_certificate,
receiver_certificate_thumbprint,
})
}
}
}
}
impl AsymmetricSecurityHeader {
pub fn none() -> AsymmetricSecurityHeader {
AsymmetricSecurityHeader {
security_policy_uri: UAString::from(SecurityPolicy::None.to_uri()),
sender_certificate: ByteString::null(),
receiver_certificate_thumbprint: ByteString::null(),
}
}
pub fn new(
security_policy: SecurityPolicy,
sender_certificate: &X509,
receiver_certificate_thumbprint: ByteString,
) -> AsymmetricSecurityHeader {
AsymmetricSecurityHeader {
security_policy_uri: UAString::from(security_policy.to_uri()),
sender_certificate: sender_certificate.as_byte_string(),
receiver_certificate_thumbprint,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct SequenceHeader {
pub sequence_number: u32,
pub request_id: u32,
}
impl SimpleBinaryEncodable for SequenceHeader {
fn byte_len(&self) -> usize {
8
}
fn encode<S: Write + ?Sized>(&self, stream: &mut S) -> EncodingResult<()> {
self.sequence_number.encode(stream)?;
self.request_id.encode(stream)?;
Ok(())
}
}
impl SimpleBinaryDecodable for SequenceHeader {
fn decode<S: Read + ?Sized>(
stream: &mut S,
decoding_options: &DecodingOptions,
) -> EncodingResult<Self> {
let sequence_number = u32::decode(stream, decoding_options)?;
let request_id = u32::decode(stream, decoding_options)?;
Ok(SequenceHeader {
sequence_number,
request_id,
})
}
}