use binrw::prelude::*;
use std::io::Cursor;
use crate::{Error, crypto};
use smb_msg::Header;
use smb_transport::IoVec;
#[derive(Debug)]
pub struct MessageSigner {
signing_algo: Box<dyn crypto::SigningAlgo>,
}
impl MessageSigner {
pub fn new(signing_algo: Box<dyn crypto::SigningAlgo>) -> MessageSigner {
MessageSigner { signing_algo }
}
pub fn verify_signature(&mut self, header: &mut Header, data: &IoVec) -> crate::Result<()> {
let calculated_signature = self._calculate_signature(header, data)?;
if calculated_signature != header.signature {
return Err(Error::SignatureVerificationFailed);
}
Ok(())
}
pub fn sign_message(&mut self, header: &mut Header, all_data: &mut IoVec) -> crate::Result<()> {
header.signature = self._calculate_signature(header, all_data)?;
let header_buffer = all_data.get_mut(0).unwrap();
debug_assert!(
header_buffer.len() >= Header::STRUCT_SIZE,
"First buffer must contain the entire header."
);
let mut header_writer = Cursor::new(&mut header_buffer[0..Header::STRUCT_SIZE]);
header.write(&mut header_writer)?;
Ok(())
}
fn _calculate_signature(&mut self, header: &mut Header, data: &IoVec) -> crate::Result<u128> {
let signature_backup = header.signature;
header.signature = 0;
let mut header_bytes = Cursor::new([0; Header::STRUCT_SIZE]);
header.write(&mut header_bytes)?;
header.signature = signature_backup;
self.signing_algo.start(header);
self.signing_algo.update(&header_bytes.into_inner());
if data.first().unwrap().len() >= Header::STRUCT_SIZE {
self.signing_algo
.update(&data.first().unwrap()[Header::STRUCT_SIZE..]);
}
for buf in data.iter().skip(1) {
self.signing_algo.update(buf);
}
Ok(self.signing_algo.finalize())
}
}
impl Clone for MessageSigner {
fn clone(&self) -> Self {
MessageSigner {
signing_algo: self.signing_algo.clone_box(),
}
}
}
#[cfg(all(test, feature = "sign_gmac"))]
mod tests {
use crate::crypto::make_signing_algo;
use super::*;
const TEST_SIGNING_KEY: [u8; 16] = [
0xAC, 0x36, 0xE9, 0x54, 0x3C, 0xD8, 0x88, 0xF0, 0xA8, 0x41, 0x23, 0xE4, 0x6B, 0xB2, 0xA0,
0xD7,
];
#[test]
#[cfg(feature = "sign_gmac")]
fn test_calc_signature() {
use smb_msg::SigningAlgorithmId;
use smb_transport::{IoVec, IoVecBuf};
let header_data = vec![
0xfeu8, 0x53, 0x4d, 0x42, 0x40, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x1, 0x0,
0x18, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x53, 0x20, 0xc, 0x21, 0x0, 0x0, 0x0, 0x0, 0x76,
0x23, 0x4b, 0x3c, 0x81, 0x2f, 0x51, 0xab, 0x8a, 0x5c, 0xf9, 0xfa, 0x43, 0xd4, 0xeb,
0x28,
];
let next_data = vec![0x4, 0x0, 0x0, 0x0];
let mut header = Header::read_le(&mut Cursor::new(
&header_data.as_slice()[..Header::STRUCT_SIZE],
))
.unwrap();
let mut signer = MessageSigner::new(
make_signing_algo(SigningAlgorithmId::AesGmac, &TEST_SIGNING_KEY).unwrap(),
);
let iovec = IoVec::from(vec![IoVecBuf::from(header_data), IoVecBuf::from(next_data)]);
let signature = signer._calculate_signature(&mut header, &iovec).unwrap();
assert_eq!(signature, 0x28ebd443faf95c8aab512f813c4b2376);
}
}