1use crate::{Result, SmbError};
4
5pub mod cmd {
6 pub const NEGOTIATE: u16 = 0x0000;
7 pub const SESSION_SETUP: u16 = 0x0001;
8 pub const TREE_CONNECT: u16 = 0x0003;
9 pub const CREATE: u16 = 0x0005;
10 pub const CLOSE: u16 = 0x0006;
11 pub const READ: u16 = 0x0008;
12 pub const WRITE: u16 = 0x0009;
13 pub const IOCTL: u16 = 0x000B;
14 pub const QUERY_DIRECTORY: u16 = 0x000E;
15}
16
17pub const FLAGS_SIGNED: u32 = 0x0000_0008;
18const PROTOCOL_ID: [u8; 4] = [0xFE, b'S', b'M', b'B'];
19
20#[allow(clippy::too_many_arguments)]
22pub fn build(
23 command: u16,
24 message_id: u64,
25 session_id: u64,
26 tree_id: u32,
27 signed: bool,
28) -> Vec<u8> {
29 let mut h = vec![0u8; 64];
30 h[0..4].copy_from_slice(&PROTOCOL_ID);
31 h[4..6].copy_from_slice(&64u16.to_le_bytes()); h[6..8].copy_from_slice(&1u16.to_le_bytes()); h[12..14].copy_from_slice(&command.to_le_bytes());
35 h[14..16].copy_from_slice(&1u16.to_le_bytes()); let flags = if signed { FLAGS_SIGNED } else { 0 };
37 h[16..20].copy_from_slice(&flags.to_le_bytes());
38 h[24..32].copy_from_slice(&message_id.to_le_bytes());
40 h[36..40].copy_from_slice(&tree_id.to_le_bytes());
42 h[40..48].copy_from_slice(&session_id.to_le_bytes());
43 h
45}
46
47#[derive(Clone, Copy, Debug)]
49pub struct Parsed {
50 pub command: u16,
51 pub status: u32,
52 pub session_id: u64,
53 pub tree_id: u32,
54}
55
56pub fn parse(buf: &[u8]) -> Result<Parsed> {
57 if buf.len() < 64 {
58 return Err(SmbError::Truncated);
59 }
60 if buf[0..4] != PROTOCOL_ID {
61 return Err(SmbError::BadProtocol);
62 }
63 Ok(Parsed {
64 status: u32::from_le_bytes(buf[8..12].try_into().unwrap()),
65 command: u16::from_le_bytes(buf[12..14].try_into().unwrap()),
66 tree_id: u32::from_le_bytes(buf[36..40].try_into().unwrap()),
67 session_id: u64::from_le_bytes(buf[40..48].try_into().unwrap()),
68 })
69}
70
71pub fn sign(message: &mut [u8], key: &[u8; 16]) {
74 use hmac::{Hmac, Mac};
75 use sha2::Sha256;
76 for b in &mut message[48..64] {
77 *b = 0;
78 }
79 let mut mac = <Hmac<Sha256>>::new_from_slice(key).expect("hmac key");
80 mac.update(message);
81 let sig = mac.finalize().into_bytes();
82 message[48..64].copy_from_slice(&sig[..16]);
83}
84
85pub fn sign_v3(message: &mut [u8], signing_key: &[u8; 16]) {
88 use aes::Aes128;
89 use cmac::{Cmac, Mac};
90 for b in &mut message[48..64] {
91 *b = 0;
92 }
93 let mut mac = <Cmac<Aes128>>::new_from_slice(signing_key).expect("cmac key");
94 mac.update(message);
95 let sig = mac.finalize().into_bytes();
96 message[48..64].copy_from_slice(&sig[..16]);
97}
98
99pub fn kdf_signing_key(session_key: &[u8; 16]) -> [u8; 16] {
102 use hmac::{Hmac, Mac};
103 use sha2::Sha256;
104 let mut input = Vec::new();
105 input.extend_from_slice(&1u32.to_be_bytes()); input.extend_from_slice(b"SMB2AESCMAC\0"); input.extend_from_slice(b"SmbSign\0"); input.extend_from_slice(&128u32.to_be_bytes()); let mut mac = <Hmac<Sha256>>::new_from_slice(session_key).expect("hmac key");
110 mac.update(&input);
111 let out = mac.finalize().into_bytes();
112 let mut k = [0u8; 16];
113 k.copy_from_slice(&out[..16]);
114 k
115}
116
117#[cfg(test)]
118mod tests {
119 use super::*;
120
121 #[test]
122 fn header_is_64_bytes_and_parses() {
123 let h = build(cmd::NEGOTIATE, 3, 0xAABB, 0x11, true);
124 assert_eq!(h.len(), 64);
125 let p = parse(&h).unwrap();
126 assert_eq!(p.command, cmd::NEGOTIATE);
127 assert_eq!(p.session_id, 0xAABB);
128 assert_eq!(p.tree_id, 0x11);
129 assert_eq!(
130 u32::from_le_bytes(h[16..20].try_into().unwrap()),
131 FLAGS_SIGNED
132 );
133 }
134}