module RubySMB module SMB2 module Packet # An SMB2 TRANSFORM_HEADER Packet as defined in # [2.2.41 SMB2 TRANSFORM_HEADER](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/d6ce2327-a4c9-4793-be66-7b5bad2175fa) class TransformHeader < BinData::Record endian :little hide :reserved0 endian :little bit32 :protocol, label: 'Protocol ID Field', initial_value: RubySMB::SMB2::SMB2_TRANSFORM_PROTOCOL_ID string :signature, label: 'Signature', length: 16 string :nonce, label: 'Nonce', length: 16 uint32 :original_message_size, label: 'Original Message Size' uint16 :reserved0 uint16 :flags, label: 'Flags / Encryption Algorithm' uint64 :session_id, label: 'Session ID' array :encrypted_data, label: 'Encrypted Data', type: :uint8, read_until: :eof def decrypt(key, algorithm: 'AES-128-GCM') auth_data = self.to_binary_s[20...52] encrypted_data = self.encrypted_data.to_ary.pack('C*') case algorithm when 'AES-128-CCM', 'AES-256-CCM' cipher = OpenSSL::CCM.new('AES', key, 16) unencrypted_data = cipher.decrypt(encrypted_data + self.signature, self.nonce[0...11], auth_data) unless unencrypted_data.length > 0 raise OpenSSL::Cipher::CipherError # raised for consistency with GCM mode end when 'AES-128-GCM', 'AES-256-GCM' cipher = OpenSSL::Cipher.new(algorithm).decrypt cipher.key = key cipher.iv = self.nonce[0...12] cipher.auth_data = auth_data cipher.auth_tag = self.signature unencrypted_data = cipher.update(encrypted_data) cipher.final # raises OpenSSL::Cipher::CipherError on signature failure else raise ArgumentError.new('Invalid algorithm, must be one of AES-128-CCM, AES-128-GCM, AES-256-CCM, or AES-256-GCM') end unencrypted_data[0...self.original_message_size] rescue Exception => e raise RubySMB::Error::EncryptionError, "Error while decrypting with '#{algorithm}' (#{e.class}: #{e})" end def encrypt(unencrypted_data, key, algorithm: 'AES-128-GCM') if unencrypted_data.is_a? BinData::Record unencrypted_data = unencrypted_data.to_binary_s end self.original_message_size.assign(unencrypted_data.length) case algorithm when 'AES-128-CCM', 'AES-256-CCM' cipher = OpenSSL::CCM.new('AES', key, 16) random_iv = OpenSSL::Random.random_bytes(11) self.nonce.assign(random_iv) result = cipher.encrypt(unencrypted_data, random_iv, self.to_binary_s[20...52]) encrypted_data = result[0...-16] auth_tag = result[-16..-1] when 'AES-128-GCM', 'AES-256-GCM' cipher = OpenSSL::Cipher.new(algorithm).encrypt cipher.iv_len = 12 cipher.key = key self.nonce.assign(cipher.random_iv) cipher.auth_data = self.to_binary_s[20...52] encrypted_data = cipher.update(unencrypted_data) + cipher.final auth_tag = cipher.auth_tag else raise ArgumentError.new('Invalid algorithm, must be one of AES-128-CCM, AES-128-GCM, AES-256-CCM, or AES-256-GCM') end self.encrypted_data.assign(encrypted_data.bytes) self.signature.assign(auth_tag) nil rescue Exception => e raise RubySMB::Error::EncryptionError, "Error while encrypting with '#{algorithm}' (#{e.class}: #{e})" end end end end end