public static byte[] Decrypt(byte[] bytes, Dictionary<ulong, Smb2CryptoInfo> cryptoInfoTable, Smb2Role role)
        {
            Transform_Header transformHeader = Smb2Utility.UnmarshalStructure<Transform_Header>(bytes);

            if (transformHeader.SessionId == 0 || !cryptoInfoTable.ContainsKey(transformHeader.SessionId))
                throw new InvalidOperationException("Invalid SessionId in TRANSFORM_HEADER.");

            Smb2CryptoInfo cryptoInfo = cryptoInfoTable[transformHeader.SessionId];

            using (var bcrypt = new BCryptAlgorithm("AES"))
            {
                int nonceLength = 0;
                BCryptCipherMode mode = BCryptCipherMode.NotAvailable;
                GetCryptoParams(cryptoInfo, CryptoOperationType.Decrypt, out mode, out nonceLength);
                bcrypt.Mode = mode;
                bcrypt.Key = role == Smb2Role.Server ? cryptoInfo.ServerInKey : cryptoInfo.ServerOutKey;
                return bcrypt.Decrypt(bytes.Skip(52).ToArray(), transformHeader.Nonce.ToByteArray().Take(nonceLength).ToArray(), bytes.Skip(20).Take(32).ToArray(), transformHeader.Signature);
            }
        }
        public Smb2Packet DecodeTransportPayload(
            byte[] messageBytes,
            Smb2Role role,
            Smb2TransportType transportType,
            bool ignoreCompoundFlag,
            out int consumedLength,
            out int expectedLength
            )
        {
            //tcp transport will prefix 4 bytes length in the beginning. and netbios won't do this.
            if (transportType == Smb2TransportType.Tcp)
            {
                if (messageBytes.Length < Smb2Consts.TcpPrefixedLenByteCount)
                {
                    consumedLength = 0;
                    expectedLength = 4;
                    return null;
                }

                //in the header of tcp payload, there are 4 bytes(in fact only 3 bytes are used) which indicate
                //the length of smb2
                int dataLenShouldHave = (messageBytes[1] << 16) + (messageBytes[2] << 8) + messageBytes[3];

                if (dataLenShouldHave > (messageBytes.Length - Smb2Consts.TcpPrefixedLenByteCount))
                {
                    consumedLength = 0;
                    expectedLength = Smb2Consts.TcpPrefixedLenByteCount + dataLenShouldHave;
                    return null;
                }

                byte[] smb2Message = new byte[dataLenShouldHave];

                Array.Copy(messageBytes, Smb2Consts.TcpPrefixedLenByteCount, smb2Message, 0, smb2Message.Length);

                Smb2Packet packet = DecodeCompletePacket(
                    smb2Message,
                    role,
                    ignoreCompoundFlag,
                    0,
                    0,
                    out consumedLength,
                    out expectedLength);

                // Here we ignore the consumedLength returned by DecodeCompletePacket(), there may be some tcp padding data
                // at the end which we are not interested.
                consumedLength = dataLenShouldHave + Smb2Consts.TcpPrefixedLenByteCount;

                return packet;
            }
            else
            {
                Smb2Packet packet = DecodeCompletePacket(
                    messageBytes,
                    role,
                    ignoreCompoundFlag,
                    0,
                    0,
                    out consumedLength,
                    out expectedLength);

                //Some packet has unknown padding data at the end.
                consumedLength = messageBytes.Length;

                return packet;
            }
        }
        /// <summary>
        /// Decode the message as smb2 compound packet
        /// </summary>
        /// <param name="messageBytes">The received packet</param>
        /// <param name="role">The role of this decoder, client or server</param>
        /// <param name="consumedLength">[OUT]The consumed length of the message</param>
        /// <param name="expectedLength">[OUT]The expected length</param>
        /// <returns>A Smb2Packet</returns>
        public Smb2Packet DecodeCompoundPacket(
            byte[] messageBytes,
            Smb2Role role,
            out int consumedLength,
            out int expectedLength
            )
        {
            Smb2CompoundPacket compoundPacket = new Smb2CompoundPacket();
            compoundPacket.decoder = this;

            compoundPacket.FromBytes(messageBytes, out consumedLength, out expectedLength);

            return compoundPacket;
        }
예제 #4
0
        private static CompressionAlgorithm GetCompressionAlgorithm(Smb2CompressiblePacket packet, Smb2CompressionInfo compressionInfo, Smb2Role role)
        {
            bool supportCompression = compressionInfo.CompressionIds.Any(compressionAlgorithm => compressionAlgorithm != CompressionAlgorithm.NONE);
            if (!supportCompression)
            {
                return CompressionAlgorithm.NONE;
            }

            bool needCompression = false;
            switch (role)
            {
                case Smb2Role.Client:
                    {
                        // Client will compress outgoing packets when:
                        // 1. EligibleForCompression is set for write request. (when user hopes write request to be compressed)
                        // 2. CompressAllPackets is set. (when user hopes all request to be compressed)
                        if (compressionInfo.CompressAllPackets)
                        {
                            needCompression = true;
                        }
                        else if (packet is Smb2WriteRequestPacket)
                        {
                            needCompression = packet.EligibleForCompression;
                        }
                    }
                    break;
                case Smb2Role.Server:
                    {
                        // Server will compress outgoing packets when:
                        // 1. CompressAllPackets is set and EligibleForCompression. (when server hopes all responses to be compressed, and request is compressed)
                        // 2. EligibleForCompression is set for read response.  (when compress read is specified in read request)
                        if (compressionInfo.CompressAllPackets || packet is Smb2ReadResponsePacket)
                        {
                            needCompression = packet.EligibleForCompression;
                        }
                    }
                    break;
                default:
                    {
                        throw new InvalidOperationException("Unknown SMB2 role!");
                    }
            }

            if (!needCompression)
            {
                return CompressionAlgorithm.NONE;
            }

            if (compressionInfo.PreferredCompressionAlgorithm == CompressionAlgorithm.NONE)
            {
                return compressionInfo.CompressionIds.First(compressionAlgorithm => compressionAlgorithm != CompressionAlgorithm.NONE);
            }
            else
            {
                if (!compressionInfo.CompressionIds.Contains(compressionInfo.PreferredCompressionAlgorithm))
                {
                    throw new InvalidOperationException("Specified preferred compression algorithm is not supported by SUT!");
                }
                return compressionInfo.PreferredCompressionAlgorithm;
            }
        }
        /// <summary>
        /// Sign and encrypt for Single or Compound packet.
        /// </summary>
        public static byte[] SignAndEncrypt(Smb2Packet originalPacket, Dictionary <ulong, Smb2CryptoInfo> cryptoInfoTable, Smb2Role role)
        {
            ulong sessionId;
            bool  isCompound        = false;
            bool  notEncryptNotSign = false;
            bool  notEncrypt        = false;

            if (originalPacket is Smb2SinglePacket)
            {
                Smb2SinglePacket singlePacket = originalPacket as Smb2SinglePacket;
                sessionId = singlePacket.Header.SessionId;
                // [MS-SMB2] Section 3.2.4.1.8, the request being sent is SMB2 NEGOTIATE,
                // or the request being sent is SMB2 SESSION_SETUP with the SMB2_SESSION_FLAG_BINDING bit set in the Flags field,
                // the client MUST NOT encrypt the message
                if (sessionId == 0 ||
                    (singlePacket.Header.Command == Smb2Command.NEGOTIATE && (singlePacket is Smb2NegotiateRequestPacket)))
                {
                    notEncryptNotSign = true;
                }
                else if ((singlePacket.Header.Command == Smb2Command.SESSION_SETUP && (singlePacket is Smb2SessionSetupRequestPacket) &&
                          (singlePacket as Smb2SessionSetupRequestPacket).PayLoad.Flags == SESSION_SETUP_Request_Flags.SESSION_FLAG_BINDING))
                {
                    notEncrypt = true;
                }
            }
            else if (originalPacket is Smb2CompoundPacket)
            {
                isCompound = true;
                // The subsequent request in compound packet should use the SessionId of the first request for encryption
                sessionId = (originalPacket as Smb2CompoundPacket).Packets[0].Header.SessionId;
            }
            else
            {
                throw new NotImplementedException(string.Format("Signing and encryption are not implemented for packet: {0}", originalPacket.ToString()));
            }

            if (sessionId == 0 || notEncryptNotSign || !cryptoInfoTable.ContainsKey(sessionId))
            {
                return(originalPacket.ToBytes());
            }

            Smb2CryptoInfo cryptoInfo = cryptoInfoTable[sessionId];

            #region Encrypt
            // Try to encrypt the message whenever the encryption is supported or not except for sesstion setup.
            // If it's not supported, do it for negative test.
            // For compound packet, the encryption is done for the entire message.
            if (!notEncrypt)
            {
                byte[] encryptedBinary = Encrypt(sessionId, cryptoInfo, role, originalPacket);
                if (encryptedBinary != null)
                {
                    return(encryptedBinary);
                }
            }
            #endregion

            #region Sign
            if (cryptoInfo.EnableSessionSigning)
            {
                if (isCompound)
                {
                    // Calculate signature for every packet in the chain
                    foreach (Smb2SinglePacket packet in (originalPacket as Smb2CompoundPacket).Packets)
                    {
                        // If the packet is the first one in the chain or the unralated one, use its own SessionId for sign and encrypt
                        // If it's not the first one and it's the related one, use the SessionId of the first request for sign and encrypt
                        if (!packet.Header.Flags.HasFlag(Packet_Header_Flags_Values.FLAGS_RELATED_OPERATIONS))
                        {
                            sessionId  = packet.Header.SessionId;
                            cryptoInfo = cryptoInfoTable[sessionId];
                        }

                        packet.Header.Signature = Sign(cryptoInfo, packet.ToBytes());
                    }
                }
                else
                {
                    (originalPacket as Smb2SinglePacket).Header.Signature = Sign(cryptoInfo, originalPacket.ToBytes());
                }
            }
            #endregion
            return(originalPacket.ToBytes());
        }
        public static byte[] Decrypt(byte[] bytes, Dictionary <ulong, Smb2CryptoInfo> cryptoInfoTable, Smb2Role role)
        {
            // Client: If the size of the message received from the server is not greater than the size of SMB2 TRANSFORM_HEADER as specified, the client MUST discard the message.
            // Server: If the size of the message received from the client is not greater than the size of the SMB2 TRANSFORM_HEADER, the server MUST disconnect the connection.
            int minimumLength = Marshal.SizeOf(typeof(Transform_Header));

            if (bytes.Length < minimumLength)
            {
                throw new InvalidOperationException(
                          String.Format(
                              "Too less data for encrypted message. Expected length more than {0}, actual {1}.",
                              minimumLength,
                              bytes.Length
                              )
                          );
            }

            Transform_Header transformHeader = Smb2Utility.UnmarshalStructure <Transform_Header>(bytes);

            // Client: If the Flags/EncryptionAlgorithm in the SMB2 TRANSFORM_HEADER is not 0x0001, the client MUST discard the message.
            // Server: If the Flags/EncryptionAlgorithm in the SMB2 TRANSFORM_HEADER is not 0x0001, the server MUST disconnect the connection.
            if (transformHeader.Flags != TransformHeaderFlags.Encrypted)
            {
                throw new InvalidOperationException(
                          String.Format(
                              "Flags/EncryptionAlgorithm field is invalid for encrypted message. Expected value 0x0001, actual {0}.",
                              (ushort)transformHeader.Flags
                              )
                          );
            }

            if (transformHeader.SessionId == 0 || !cryptoInfoTable.ContainsKey(transformHeader.SessionId))
            {
                throw new InvalidOperationException("Invalid SessionId in TRANSFORM_HEADER.");
            }

            Smb2CryptoInfo cryptoInfo = cryptoInfoTable[transformHeader.SessionId];

            using (var bcrypt = new BCryptAlgorithm("AES"))
            {
                int nonceLength       = 0;
                BCryptCipherMode mode = BCryptCipherMode.NotAvailable;
                GetCryptoParams(cryptoInfo, CryptoOperationType.Decrypt, out mode, out nonceLength);
                bcrypt.Mode = mode;
                bcrypt.Key  = role == Smb2Role.Server ? cryptoInfo.ServerInKey : cryptoInfo.ServerOutKey;
                return(bcrypt.Decrypt(bytes.Skip(52).ToArray(), transformHeader.Nonce.ToByteArray().Take(nonceLength).ToArray(), bytes.Skip(20).Take(32).ToArray(), transformHeader.Signature));
            }
        }
        private void VerifyCompoundPacket(Smb2CompoundPacket compoundPacket, Smb2Role role, Transform_Header?transformHeader)
        {
            for (int i = 0; i < compoundPacket.Packets.Count; i++)
            {
                var packet = compoundPacket.Packets[i];

                if (packet.Header.NextCommand % 8 != 0)
                {
                    throw new InvalidOperationException("NextCommand is not a 8-byte aligned offset!");
                }

                switch (role)
                {
                case Smb2Role.Client:
                {
                    if (transformHeader != null)
                    {
                        // For each response in a compounded response, if the SessionId field of SMB2 header is not equal to the SessionId field in the SMB2 TRANSFORM_HEADER,
                        // the client SHOULD<139> discard the entire compounded response and stop processing.
                        if (packet.Header.SessionId != transformHeader.Value.SessionId)
                        {
                            throw new InvalidOperationException("SessionId is inconsistent for encrypted compounded response.");
                        }
                    }
                }
                break;

                case Smb2Role.Server:
                {
                    // The server MUST verify if any of the following conditions returns TRUE and, if so, the server MUST disconnect the connection:
                    // For the first operation of a compounded request,
                    // - SMB2_FLAGS_RELATED_OPERATIONS is set in the Flags field of the SMB2 header of the request
                    // - The SessionId field in the SMB2 header of the request is not equal to Request.TransformSessionId.
                    // In a compounded request, for each operation in the compounded chain except the first one,
                    // - SMB2_FLAGS_RELATED_OPERATIONS is not set in the Flags field of the SMB2 header of the operation and SessionId in the SMB2 header of the operation is not equal to Request.TransformSessionId.
                    if (i == 0)
                    {
                        if (packet.Header.Flags.HasFlag(Packet_Header_Flags_Values.FLAGS_RELATED_OPERATIONS))
                        {
                            throw new InvalidOperationException("FLAGS_RELATED_OPERATIONS should not be set for the first compounded request.");
                        }
                        if (transformHeader != null)
                        {
                            if (packet.Header.SessionId != transformHeader.Value.SessionId)
                            {
                                throw new InvalidOperationException("SessionId is inconsistent for encrypted compounded request.");
                            }
                        }
                    }
                    else
                    {
                        if (transformHeader != null)
                        {
                            if (!packet.Header.Flags.HasFlag(Packet_Header_Flags_Values.FLAGS_RELATED_OPERATIONS))
                            {
                                if (packet.Header.SessionId != transformHeader.Value.SessionId)
                                {
                                    throw new InvalidOperationException("SessionId is inconsistent for encrypted compounded request.");
                                }
                            }
                        }
                    }
                }
                break;
                }
            }
        }
        private static Smb2Packet CompressForNonChained(Smb2CompressiblePacket packet, Smb2CompressionInfo compressionInfo, Smb2Role role)
        {
            var compressionAlgorithm = GetCompressionAlgorithm(packet, compressionInfo, role);

            if (compressionAlgorithm == CompressionAlgorithm.NONE)
            {
                return(packet);
            }

            var packetBytes = packet.ToBytes();

            var compressor = GetCompressor(compressionAlgorithm);

            uint offset = 0;

            if (compressionInfo.CompressBufferOnly)
            {
                offset = (packet as IPacketBuffer).BufferOffset;
            }

            var compressedPacket = new Smb2NonChainedCompressedPacket();

            compressedPacket.Header.ProtocolId = Smb2Consts.ProtocolIdInCompressionTransformHeader;
            compressedPacket.Header.OriginalCompressedSegmentSize = (uint)packetBytes.Length;
            compressedPacket.Header.CompressionAlgorithm          = compressionAlgorithm;
            compressedPacket.Header.Flags     = Compression_Transform_Header_Flags.SMB2_COMPRESSION_FLAG_NONE;
            compressedPacket.Header.Offset    = offset;
            compressedPacket.UncompressedData = packetBytes.Take((int)offset).ToArray();
            compressedPacket.CompressedData   = compressor.Compress(packetBytes.Skip((int)offset).ToArray());

            compressedPacket.OriginalPacket = packet;

            return(compressedPacket);
        }
        public static byte[] Decrypt(byte[] bytes, Dictionary <ulong, Smb2CryptoInfo> cryptoInfoTable, Smb2Role role)
        {
            Transform_Header transformHeader = Smb2Utility.UnmarshalStructure <Transform_Header>(bytes);

            if (transformHeader.SessionId == 0 || !cryptoInfoTable.ContainsKey(transformHeader.SessionId))
            {
                throw new InvalidOperationException("Invalid SessionId in TRANSFORM_HEADER.");
            }

            Smb2CryptoInfo cryptoInfo = cryptoInfoTable[transformHeader.SessionId];

            using (var bcrypt = new BCryptAlgorithm("AES"))
            {
                int nonceLength       = 0;
                BCryptCipherMode mode = BCryptCipherMode.NotAvailable;
                GetCryptoParams(cryptoInfo, CryptoOperationType.Decrypt, out mode, out nonceLength);
                bcrypt.Mode = mode;
                bcrypt.Key  = role == Smb2Role.Server ? cryptoInfo.ServerInKey : cryptoInfo.ServerOutKey;
                return(bcrypt.Decrypt(bytes.Skip(52).ToArray(), transformHeader.Nonce.ToByteArray().Take(nonceLength).ToArray(), bytes.Skip(20).Take(32).ToArray(), transformHeader.Signature));
            }
        }
        /// <summary>
        /// Sign and encrypt for Single or Compound packet.
        /// </summary>
        public static byte[] SignAndEncrypt(Smb2Packet originalPacket, Dictionary <ulong, Smb2CryptoInfo> cryptoInfoTable, Smb2Role role)
        {
            ulong sessionId;
            bool  isCompound = false;

            if (originalPacket is Smb2SinglePacket)
            {
                sessionId = (originalPacket as Smb2SinglePacket).Header.SessionId;
            }
            else if (originalPacket is Smb2CompoundPacket)
            {
                isCompound = true;
                // The subsequent request in compound packet should use the SessionId of the first request for encryption
                sessionId = (originalPacket as Smb2CompoundPacket).Packets[0].Header.SessionId;
            }
            else
            {
                throw new NotImplementedException(string.Format("Signing and encryption are not implemented for packet: {0}", originalPacket.ToString()));
            }

            // Signing and encryption not required if session id is not available yet
            if (sessionId == 0 || !cryptoInfoTable.ContainsKey(sessionId))
            {
                return(originalPacket.ToBytes());
            }

            Smb2CryptoInfo cryptoInfo = cryptoInfoTable[sessionId];

            #region Encrypt
            // Try to encrypt the message whenever the encryption is supported or not.
            // If it's not supported, do it for negative test.
            // For compound packet, the encryption is done for the entire message.
            byte[] encryptedBinary = Encrypt(sessionId, cryptoInfo, role, originalPacket);
            if (encryptedBinary != null)
            {
                return(encryptedBinary);
            }
            #endregion

            #region Sign
            if (cryptoInfo.EnableSessionSigning)
            {
                if (isCompound)
                {
                    // Calculate signature for every packet in the chain
                    foreach (Smb2SinglePacket packet in (originalPacket as Smb2CompoundPacket).Packets)
                    {
                        // If the packet is the first one in the chain or the unralated one, use its own SessionId for sign and encrypt
                        // If it's not the first one and it's the related one, use the SessionId of the first request for sign and encrypt
                        if (!packet.Header.Flags.HasFlag(Packet_Header_Flags_Values.FLAGS_RELATED_OPERATIONS))
                        {
                            sessionId  = packet.Header.SessionId;
                            cryptoInfo = cryptoInfoTable[sessionId];
                        }

                        packet.Header.Signature = Sign(cryptoInfo, packet.ToBytes());
                    }
                }
                else
                {
                    (originalPacket as Smb2SinglePacket).Header.Signature = Sign(cryptoInfo, originalPacket.ToBytes());
                }
            }
            #endregion

            return(originalPacket.ToBytes());
        }
        /// <summary>
        /// Compress SMB2 packet.
        /// </summary>
        /// <param name="packet">The SMB2 packet.</param>
        /// <param name="compressionInfo">Compression info.</param>
        /// <param name="role">SMB2 role.</param>
        /// <returns>The compressed packet, or original packet if compression is not applicable.</returns>
        public static Smb2Packet Compress(Smb2CompressiblePacket packet, Smb2CompressionInfo compressionInfo, Smb2Role role)
        {
            Smb2Packet compressed;

            if (compressionInfo.SupportChainedCompression)
            {
                compressed = CompressForChained(packet, compressionInfo, role);
            }
            else
            {
                compressed = CompressForNonChained(packet, compressionInfo, role);
            }

            if (compressed == packet)
            {
                // Compression is not applicable.
                return(packet);
            }

            var originalBytes = packet.ToBytes();

            var compressedBytes = compressed.ToBytes();

            // Check whether compression shrinks the on-wire packet size
            if (compressedBytes.Length < originalBytes.Length)
            {
                return(compressed);
            }
            else
            {
                return(packet);
            }
        }
        private static CompressionAlgorithm GetCompressionAlgorithm(Smb2CompressiblePacket packet, Smb2CompressionInfo compressionInfo, Smb2Role role)
        {
            bool needCompression = IsCompressionNeeded(packet, compressionInfo, role);

            if (!needCompression)
            {
                return(CompressionAlgorithm.NONE);
            }

            var result = GetPreferredCompressionAlgorithm(compressionInfo);

            return(result);
        }
        private static bool IsCompressionNeeded(Smb2CompressiblePacket packet, Smb2CompressionInfo compressionInfo, Smb2Role role)
        {
            bool supportCompression = compressionInfo.CompressionIds.Any(compressionAlgorithm => compressionAlgorithm != CompressionAlgorithm.NONE);

            if (!supportCompression)
            {
                return(false);
            }

            bool needCompression = false;

            switch (role)
            {
            case Smb2Role.Client:
            {
                // Client will compress outgoing packets when:
                // 1. EligibleForCompression is set for write request. (when user hopes write request to be compressed)
                // 2. CompressAllPackets is set. (when user hopes all request to be compressed)
                if (compressionInfo.CompressAllPackets)
                {
                    needCompression = true;
                }
                else if (packet is Smb2WriteRequestPacket)
                {
                    needCompression = packet.EligibleForCompression;
                }
            }
            break;

            case Smb2Role.Server:
            {
                // Server will compress outgoing packets when:
                // 1. CompressAllPackets is set and EligibleForCompression. (when server hopes all responses to be compressed, and request is compressed)
                // 2. EligibleForCompression is set for read response.  (when compress read is specified in read request)
                if (compressionInfo.CompressAllPackets || packet is Smb2ReadResponsePacket)
                {
                    needCompression = packet.EligibleForCompression;
                }
            }
            break;

            default:
            {
                throw new InvalidOperationException("Unknown SMB2 role!");
            }
            }

            if (needCompression && compressionInfo.CompressBufferOnly)
            {
                // Not compress packet if it does not contain buffer.
                if (!(packet is IPacketBuffer) || (packet as IPacketBuffer).BufferLength == 0)
                {
                    needCompression = false;
                }
            }

            return(needCompression);
        }
        /// <summary>
        /// Decompress the Smb2CompressedPacket.
        /// </summary>
        /// <param name="packet">The compressed packet.</param>
        /// <param name="compressionInfo">Compression info.</param>
        /// <param name="role">SMB2 role.</param>
        /// <returns>Byte array containing the decompressed packet.</returns>
        public static byte[] Decompress(Smb2CompressedPacket packet, Smb2CompressionInfo compressionInfo, Smb2Role role)
        {
            bool isChained = packet.Header.Flags.HasFlag(Compression_Transform_Header_Flags.SMB2_COMPRESSION_FLAG_CHAINED);

            byte[] decompressedData;

            if (isChained)
            {
                decompressedData = DecompressForChained(packet, compressionInfo, role);
            }
            else
            {
                decompressedData = DecompressForNonChained(packet, compressionInfo, role);
            }

            if (decompressedData.Length != packet.Header.OriginalCompressedSegmentSize)
            {
                throw new InvalidOperationException($"The length of decompressed data (0x{decompressedData.Length:X08}) is inconsistent with compression header (0x{packet.Header.OriginalCompressedSegmentSize:X08}).");
            }

            return(decompressedData);
        }
 private Smb2Packet DecodeEncryptedSmb2Packet(
     byte[] messageBytes,
     Smb2Role role,
     bool ignoreCompoundFlag,
     ulong realSessionId,
     uint realTreeId,
     out int consumedLength,
     out int expectedLength
     )
 {
     return DecodeSmb2Packet(
             Smb2Crypto.Decrypt(messageBytes, cryptoInfoTable, decodeRole),
             role,
             ignoreCompoundFlag,
             realSessionId,
             realTreeId,
             out consumedLength,
             out expectedLength);
 }
        /// <summary>
        /// Decode the message as smb2 packet
        /// </summary>
        /// <param name="messageBytes">The received packet</param>
        /// <param name="role">The role of this decoder, client or server</param>
        /// <param name="ignoreCompoundFlag">indicate whether decode the packet as a single packet or a compound packet
        /// when compound flag is set</param>
        /// <param name="realSessionId">The real sessionId for this packet</param>
        /// <param name="realTreeId">The real treeId for this packet</param>
        /// <param name="consumedLength">[OUT]The consumed length of the message</param>
        /// <param name="expectedLength">[OUT]The expected length</param>
        /// <returns>A Smb2Packet</returns>
        private Smb2Packet DecodeSmb2Packet(
            byte[] messageBytes,
            Smb2Role role,
            bool ignoreCompoundFlag,
            ulong realSessionId,
            uint realTreeId,
            out int consumedLength,
            out int expectedLength
            )
        {
            Packet_Header smb2Header;

            smb2Header = TypeMarshal.ToStruct<Packet_Header>(messageBytes);

            if ((smb2Header.NextCommand != 0) && !ignoreCompoundFlag)
            {
                return DecodeCompoundPacket(messageBytes, role, out consumedLength, out expectedLength);
            }
            else
            {
                return DecodeSinglePacket(
                    messageBytes,
                    role,
                    ignoreCompoundFlag,
                    realSessionId,
                    realTreeId,
                    out consumedLength,
                    out expectedLength
                    );
            }
        }
 /// <summary>
 /// Constructor
 /// </summary>
 /// <param name="decodeRole">The decode role, client or server</param>
 /// <param name="cryptoInfoTable">Crypto info table indexed by session ID</param>
 public Smb2Decoder(Smb2Role decodeRole, Dictionary <ulong, Smb2CryptoInfo> cryptoInfoTable)
 {
     this.decodeRole      = decodeRole;
     this.cryptoInfoTable = cryptoInfoTable;
 }
        /// <summary>
        /// Decode the  message except length field which may exist if transport is tcp
        /// </summary>
        /// <param name="messageBytes">The received packet</param>
        /// <param name="role">The role of this decoder, client or server</param>
        /// <param name="realSessionId">The real sessionId for this packet</param>
        /// <param name="realTreeId">The real treeId for this packet</param>
        /// <param name="consumedLength">[OUT]The consumed length of the message</param>
        /// <param name="expectedLength">[OUT]The expected length</param>
        /// <returns>A Smb2Packet</returns>
        public Smb2Packet DecodeCompletePacket(
            byte[] messageBytes,
            Smb2Role role,
            ulong realSessionId,
            uint realTreeId,
            out int consumedLength,
            out int expectedLength
            )
        {
            //protocol version is of 4 bytes len
            byte[] protocolVersion = new byte[sizeof(uint)];
            Array.Copy(messageBytes, 0, protocolVersion, 0, protocolVersion.Length);

            SmbVersion version = DecodeVersion(protocolVersion);

            if (version == SmbVersion.Version1)
            {
                // SMB Negotiate packet
                return(DecodeSmbPacket(messageBytes, role, out consumedLength, out expectedLength));
            }
            else if (version == SmbVersion.Version2Encrypted)
            {
                // SMB2 encrypted packet
                return(DecodeEncryptedSmb2Packet(
                           messageBytes,
                           role,
                           realSessionId,
                           realTreeId,
                           out consumedLength,
                           out expectedLength
                           ));
            }
            else if (version == SmbVersion.Version2Compressed)
            {
                var decompressedPacket = DecodeCompressedSmb2Packet(
                    messageBytes,
                    role,
                    realSessionId,
                    realTreeId,
                    out consumedLength,
                    out expectedLength,
                    null
                    );

                //For single packet signature verification
                if (decompressedPacket is Smb2SinglePacket)
                {
                    //verify signature of a single packet
                    Smb2SinglePacket singlePacket = decompressedPacket as Smb2SinglePacket;

                    TryVerifySignatureExceptSessionSetupResponse(singlePacket, singlePacket.Header.SessionId, messageBytes);
                }
                else if (decompressedPacket is Smb2CompoundPacket)//For Compound packet signature verification
                {
                    //verify signature of the compound packet
                    TryVerifySignature(decompressedPacket as Smb2CompoundPacket, messageBytes);
                }

                return(decompressedPacket);
            }
            else
            {
                // SMB2 packet not encrypted
                Smb2Packet decodedPacket = DecodeSmb2Packet(
                    messageBytes,
                    role,
                    realSessionId,
                    realTreeId,
                    out consumedLength,
                    out expectedLength,
                    null
                    );

                //For single packet signature verification
                if (decodedPacket is Smb2SinglePacket)
                {
                    //verify signature of a single packet
                    Smb2SinglePacket singlePacket = decodedPacket as Smb2SinglePacket;

                    TryVerifySignatureExceptSessionSetupResponse(singlePacket, singlePacket.Header.SessionId, messageBytes);
                }
                else if (decodedPacket is Smb2CompoundPacket)//For Compound packet signature verification
                {
                    //verify signature of the compound packet
                    TryVerifySignature(decodedPacket as Smb2CompoundPacket, messageBytes);
                }

                return(decodedPacket);
            }
        }
        private static byte[] Encrypt(ulong sessionId, Smb2CryptoInfo cryptoInfo, Smb2Role role, Smb2Packet originalPacket)
        {
            Packet_Header header;
            if (originalPacket is Smb2SinglePacket)
            {
                header = (originalPacket as Smb2SinglePacket).Header;
            }
            else
            {
                header = (originalPacket as Smb2CompoundPacket).Packets[0].Header;
            }

            // Encrypt all messages after session setup if global encryption enabled.
            // Encrypt all messages after tree connect if global encryption disabled but share encryption enabled.
            if (header.Command != Smb2Command.NEGOTIATE
             && header.Command != Smb2Command.SESSION_SETUP
             && (cryptoInfo.EnableSessionEncryption
                 || (cryptoInfo.EnableTreeEncryption.Contains(header.TreeId)
                     && header.Command != Smb2Command.TREE_CONNECT
                     )
                 )
             )
            {
                using (var bcrypt = new BCryptAlgorithm("AES"))
                {
                    byte[] originalBinary = originalPacket.ToBytes();
                    Transform_Header transformHeader = new Transform_Header
                    {
                        ProtocolId = Smb2Consts.ProtocolIdInTransformHeader,
                        OriginalMessageSize = (uint)originalBinary.Length,
                        SessionId = sessionId,
                        Signature = new byte[16]
                    };

                    if (cryptoInfo.Dialect == DialectRevision.Smb311)
                    {
                        transformHeader.Flags = TransformHeaderFlags.Encrypted;
                    }
                    else
                    {
                        transformHeader.EncryptionAlgorithm = EncryptionAlgorithm.ENCRYPTION_AES128_CCM;
                    }

                    byte[] tag;

                    int nonceLength = 0;
                    BCryptCipherMode mode = BCryptCipherMode.NotAvailable;
                    GetCryptoParams(cryptoInfo, CryptoOperationType.Encrypt, out mode, out nonceLength);
                    bcrypt.Mode = mode;
                    bcrypt.Key = role == Smb2Role.Server ? cryptoInfo.ServerOutKey : cryptoInfo.ServerInKey;
                    // The reserved field (5 bytes for CCM, 4 bytes for GCM) must be set to zero.
                    byte[] nonce = new byte[16];
                    Buffer.BlockCopy(Guid.NewGuid().ToByteArray(), 0, nonce, 0, nonceLength);
                    transformHeader.Nonce = new Guid(nonce);

                    byte[] output = bcrypt.Encrypt(
                        originalBinary,
                        transformHeader.Nonce.ToByteArray().Take(nonceLength).ToArray(),
                        // Use the fields including and after Nonce field as auth data
                        Smb2Utility.MarshalStructure(transformHeader).Skip(20).ToArray(),
                        // Signature is 16 bytes in length
                        16,
                        out tag);
                    transformHeader.Signature = tag;
                    return Smb2Utility.MarshalStructure(transformHeader).Concat(output).ToArray();
                }
            }

            // Return null if the message is not required to be encrypted.
            return null;
        }
 /// <summary>
 /// Constructor
 /// </summary>
 /// <param name="decodeRole">The decode role, client or server</param>
 /// <param name="cryptoInfoTable">Crypto info table indexed by session ID</param>
 /// <param name="compressionInfo">Compression information for SMB2 connection.</param>
 public Smb2Decoder(Smb2Role decodeRole, Dictionary <ulong, Smb2CryptoInfo> cryptoInfoTable, Smb2CompressionInfo compressionInfo)
 {
     this.decodeRole      = decodeRole;
     this.cryptoInfoTable = cryptoInfoTable;
     this.compressionInfo = compressionInfo;
 }
        /// <summary>
        /// Sign and encrypt for Single or Compound packet.
        /// </summary>
        public static byte[] SignAndEncrypt(Smb2Packet originalPacket, Dictionary<ulong, Smb2CryptoInfo> cryptoInfoTable, Smb2Role role)
        {
            ulong sessionId;
            bool isCompound = false;

            if (originalPacket is Smb2SinglePacket)
            {
                sessionId = (originalPacket as Smb2SinglePacket).Header.SessionId;
            }
            else if (originalPacket is Smb2CompoundPacket)
            {
                isCompound = true;
                // The subsequent request in compound packet should use the SessionId of the first request for encryption
                sessionId = (originalPacket as Smb2CompoundPacket).Packets[0].Header.SessionId;
            }
            else
            {
                throw new NotImplementedException(string.Format("Signing and encryption are not implemented for packet: {0}", originalPacket.ToString()));
            }

            // Signing and encryption not required if session id is not available yet
            if (sessionId == 0 || !cryptoInfoTable.ContainsKey(sessionId))
                return originalPacket.ToBytes();

            Smb2CryptoInfo cryptoInfo = cryptoInfoTable[sessionId];

            #region Encrypt
            // Try to encrypt the message whenever the encryption is supported or not.
            // If it's not supported, do it for negative test.
            // For compound packet, the encryption is done for the entire message.
            byte[] encryptedBinary = Encrypt(sessionId, cryptoInfo, role, originalPacket);
            if (encryptedBinary != null)
                return encryptedBinary;
            #endregion

            #region Sign
            if (cryptoInfo.EnableSessionSigning)
            {
                if (isCompound)
                {
                    // Calculate signature for every packet in the chain
                    foreach (Smb2SinglePacket packet in (originalPacket as Smb2CompoundPacket).Packets)
                    {
                        // If the packet is the first one in the chain or the unralated one, use its own SessionId for sign and encrypt
                        // If it's not the first one and it's the related one, use the SessionId of the first request for sign and encrypt
                        if (!packet.Header.Flags.HasFlag(Packet_Header_Flags_Values.FLAGS_RELATED_OPERATIONS))
                        {
                            sessionId = packet.Header.SessionId;
                            cryptoInfo = cryptoInfoTable[sessionId];
                        }

                        packet.Header.Signature = Sign(cryptoInfo, packet.ToBytes());
                    }
                }
                else
                {
                    (originalPacket as Smb2SinglePacket).Header.Signature = Sign(cryptoInfo, originalPacket.ToBytes());
                }
            }
            #endregion

            return originalPacket.ToBytes();
        }
        private static byte[] Encrypt(ulong sessionId, Smb2CryptoInfo cryptoInfo, Smb2Role role, Smb2Packet originalPacket)
        {
            Packet_Header header;

            if (originalPacket is Smb2SinglePacket)
            {
                header = (originalPacket as Smb2SinglePacket).Header;
            }
            else
            {
                header = (originalPacket as Smb2CompoundPacket).Packets[0].Header;
            }

            // Encrypt all messages after session setup if global encryption enabled.
            // Encrypt all messages after tree connect if global encryption disabled but share encryption enabled.
            if ((cryptoInfo.EnableSessionEncryption ||
                 (cryptoInfo.EnableTreeEncryption.Contains(header.TreeId) &&
                  header.Command != Smb2Command.TREE_CONNECT
                 )
                 )
                )
            {
                using (var bcrypt = new BCryptAlgorithm("AES"))
                {
                    byte[]           originalBinary  = originalPacket.ToBytes();
                    Transform_Header transformHeader = new Transform_Header
                    {
                        ProtocolId          = Smb2Consts.ProtocolIdInTransformHeader,
                        OriginalMessageSize = (uint)originalBinary.Length,
                        SessionId           = sessionId,
                        Signature           = new byte[16]
                    };

                    if (cryptoInfo.Dialect == DialectRevision.Smb311)
                    {
                        transformHeader.Flags = TransformHeaderFlags.Encrypted;
                    }
                    else
                    {
                        transformHeader.EncryptionAlgorithm = EncryptionAlgorithm.ENCRYPTION_AES128_CCM;
                    }

                    byte[] tag;

                    int nonceLength       = 0;
                    BCryptCipherMode mode = BCryptCipherMode.NotAvailable;
                    GetCryptoParams(cryptoInfo, CryptoOperationType.Encrypt, out mode, out nonceLength);
                    bcrypt.Mode = mode;
                    bcrypt.Key  = role == Smb2Role.Server ? cryptoInfo.ServerOutKey : cryptoInfo.ServerInKey;
                    // The reserved field (5 bytes for CCM, 4 bytes for GCM) must be set to zero.
                    byte[] nonce = new byte[16];
                    Buffer.BlockCopy(Guid.NewGuid().ToByteArray(), 0, nonce, 0, nonceLength);
                    transformHeader.Nonce = new Guid(nonce);

                    byte[] output = bcrypt.Encrypt(
                        originalBinary,
                        transformHeader.Nonce.ToByteArray().Take(nonceLength).ToArray(),
                        // Use the fields including and after Nonce field as auth data
                        Smb2Utility.MarshalStructure(transformHeader).Skip(20).ToArray(),
                        // Signature is 16 bytes in length
                        16,
                        out tag);
                    transformHeader.Signature = tag;
                    return(Smb2Utility.MarshalStructure(transformHeader).Concat(output).ToArray());
                }
            }

            // Return null if the message is not required to be encrypted.
            return(null);
        }
예제 #23
0
        private static byte[] Sign(Smb2CryptoInfo cryptoInfo, Smb2SinglePacket original, Smb2Role role)
        {
            if (Smb2Utility.IsSmb2Family(cryptoInfo.Dialect))
            {
                // [MS-SMB2] 3.1.4.1
                // 3. If Connection.Dialect is "2.002" or "2.100", the sender MUST compute a 32-byte hash using HMAC-SHA256 over the entire message,
                HMACSHA256 hmacSha = new HMACSHA256(cryptoInfo.SigningKey);
                return(hmacSha.ComputeHash(original.ToBytes()));
            }
            else if (cryptoInfo.SigningId == SigningAlgorithm.AES_GMAC)
            {
                // [MS-SMB2] 3.1.4.1
                // 1. If Connection.Dialect belongs to the SMB 3.x dialect family and Connection.SigningAlgorithmId is AES-GMAC,
                // compute a 16-byte hash using the AES-GMAC over the entire message using nonce as specified
                var nonce = Smb2Utility.ComputeNonce(original, role);
                var(_, tag) = AesGmac.ComputeHash(cryptoInfo.SigningKey, nonce, original.ToBytes());

                return(tag);
            }
            else
            {
                // [MS-SMB2] 3.1.4.1
                // 2. If Connection.Dialect belongs to the SMB 3.x dialect family, the sender MUST compute a 16-byte hash using AES-128-CMAC over the entire message
                return(AesCmac128.ComputeHash(cryptoInfo.SigningKey, original.ToBytes()));
            }
        }
예제 #24
0
        /// <summary>
        /// Decompress the Smb2CompressedPacket.
        /// </summary>
        /// <param name="packet">The compressed packet.</param>
        /// <param name="compressionInfo">Compression info.</param>
        /// <param name="role">SMB2 role.</param>
        /// <returns>Byte array containing the decompressed packet.</returns>
        public static byte[] Decompress(Smb2CompressedPacket packet, Smb2CompressionInfo compressionInfo, Smb2Role role)
        {
            if (packet.Header.CompressionAlgorithm == CompressionAlgorithm.NONE)
            {
                throw new InvalidOperationException("Invalid CompressionAlgorithm in header!");
            }

            if (!compressionInfo.CompressionIds.Any(compressionAlgorithm => compressionAlgorithm == packet.Header.CompressionAlgorithm))
            {
                throw new InvalidOperationException("The CompressionAlgorithm is not supported!");
            }

            var decompressor = GetDecompressor(packet.Header.CompressionAlgorithm);

            var decompressedBytes = decompressor.Decompress(packet.CompressedData);

            var originalPacketBytes = packet.UncompressedData.Concat(decompressedBytes).ToArray();

            return originalPacketBytes;
        }
예제 #25
0
        public static void SignByteArray(Smb2CryptoInfo cryptoInfo, byte[] original, out byte[] nonce, out byte[] signature, Smb2Role role, Smb2Command smb2Command, UInt64 messageId = 1)
        {
            if (Smb2Utility.IsSmb2Family(cryptoInfo.Dialect))
            {
                // [MS-SMB2] 3.1.4.1
                // 3. If Connection.Dialect is "2.02" or "2.1", the sender MUST compute a 32-byte hash using HMAC-SHA256 over the entire message,
                HMACSHA256 hmacSha = new HMACSHA256(cryptoInfo.SigningKey);
                signature = hmacSha.ComputeHash(original);
                nonce     = Array.Empty <byte>();
            }
            else if (Smb2Utility.IsSmb3xFamily(cryptoInfo.Dialect))
            {
                if (cryptoInfo.SigningId == SigningAlgorithm.AES_GMAC)
                {
                    // [MS-SMB2] 3.1.4.1
                    // 1. If Connection.Dialect belongs to the SMB 3.x dialect family and Connection.SigningAlgorithmId is AES-GMAC,
                    // compute a 16-byte hash using the AES-GMAC over the entire message using nonce as specified
                    nonce       = Smb2Utility.ComputeNonce(messageId, role, smb2Command);
                    var(_, tag) = AesGmac.ComputeHash(cryptoInfo.SigningKey, nonce, original);

                    signature = tag;
                }
                else
                {
                    // [MS-SMB2] 3.1.4.1
                    // 2. If Connection.Dialect belongs to the SMB 3.x dialect family, the sender MUST compute a 16-byte hash using AES-128-CMAC over the entire message
                    signature = AesCmac128.ComputeHash(cryptoInfo.SigningKey, original);
                    nonce     = Array.Empty <byte>();
                }
            }
            else
            {
                nonce     = Array.Empty <byte>();
                signature = Array.Empty <byte>();
            }
        }
예제 #26
0
        /// <summary>
        /// Compress SMB2 packet.
        /// </summary>
        /// <param name="packet">The SMB2 packet.</param>
        /// <param name="compressionInfo">Compression info.</param>
        /// <param name="role">SMB2 role.</param>
        /// <param name="offset">The offset where compression start, default zero.</param>
        /// <returns></returns>
        public static Smb2Packet Compress(Smb2CompressiblePacket packet, Smb2CompressionInfo compressionInfo, Smb2Role role, uint offset = 0)
        {
            var compressionAlgorithm = GetCompressionAlgorithm(packet, compressionInfo, role);

            if (compressionAlgorithm == CompressionAlgorithm.NONE)
            {
                return packet;
            }

            var packetBytes = packet.ToBytes();

            var compressor = GetCompressor(compressionAlgorithm);

            var compressedPacket = new Smb2CompressedPacket();
            compressedPacket.Header.ProtocolId = Smb2Consts.ProtocolIdInCompressionTransformHeader;
            compressedPacket.Header.OriginalCompressedSegmentSize = (uint)packetBytes.Length;
            compressedPacket.Header.CompressionAlgorithm = compressionAlgorithm;
            compressedPacket.Header.Reserved = 0;
            compressedPacket.Header.Offset = offset;
            compressedPacket.UncompressedData = packetBytes.Take((int)offset).ToArray();
            compressedPacket.CompressedData = compressor.Compress(packetBytes.Skip((int)offset).ToArray());

            var compressedPackectBytes = compressedPacket.ToBytes();

            // Check whether compression shrinks the on-wire packet size
            if (compressedPackectBytes.Length < packetBytes.Length)
            {
                compressedPacket.OriginalPacket = packet;
                return compressedPacket;
            }
            else
            {
                return packet;
            }
        }
예제 #27
0
        public static byte[] Decrypt(byte[] bytes, Dictionary <ulong, Smb2CryptoInfo> cryptoInfoTable, Smb2Role role, out Transform_Header transformHeader)
        {
            var encryptedPacket = new Smb2EncryptedPacket();

            int consumedLen;
            int expectedLen;

            encryptedPacket.FromBytes(bytes, out consumedLen, out expectedLen);

            transformHeader = encryptedPacket.Header;

            // For client: If the Flags/EncryptionAlgorithm in the SMB2 TRANSFORM_HEADER is not 0x0001, the client MUST discard the message.
            // For server: If the Flags/EncryptionAlgorithm in the SMB2 TRANSFORM_HEADER is not 0x0001, the server MUST disconnect the connection.
            if (transformHeader.Flags != TransformHeaderFlags.Encrypted)
            {
                throw new InvalidOperationException(
                          String.Format(
                              "Flags/EncryptionAlgorithm field is invalid for encrypted message. Expected value 0x0001, actual {0}.",
                              (ushort)transformHeader.Flags
                              )
                          );
            }

            if (transformHeader.SessionId == 0 || !cryptoInfoTable.ContainsKey(transformHeader.SessionId))
            {
                throw new InvalidOperationException("Invalid SessionId in TRANSFORM_HEADER.");
            }

            Smb2CryptoInfo cryptoInfo = cryptoInfoTable[transformHeader.SessionId];

            using (var bcrypt = new BCryptAlgorithm("AES"))
            {
                int nonceLength       = 0;
                BCryptCipherMode mode = BCryptCipherMode.NotAvailable;
                GetCryptoParams(cryptoInfo, CryptoOperationType.Decrypt, out mode, out nonceLength);
                bcrypt.Mode = mode;
                bcrypt.Key  = role == Smb2Role.Server ? cryptoInfo.ServerInKey : cryptoInfo.ServerOutKey;
                // Auth data is Transform_Header start from Nonce, excluding ProtocolId and Signature.
                var authData = Smb2Utility.MarshalStructure(transformHeader).Skip((int)Marshal.OffsetOf <Transform_Header>("Nonce")).ToArray();
                return(bcrypt.Decrypt(encryptedPacket.EncryptdData, transformHeader.Nonce.ToByteArray().Take(nonceLength).ToArray(), authData, transformHeader.Signature));
            }
        }
 /// <summary>
 /// Decode the message as smb2 single packet
 /// </summary>
 /// <param name="messageBytes">The received packet</param>
 /// <param name="role">The role of this decoder, client or server</param>
 /// <param name="ignoreCompoundFlag">indicate whether decode the packet as a single packet or a compound packet
 /// when compound flag is set</param>
 /// <param name="realSessionId">The real sessionId for this packet</param>
 /// <param name="realTreeId">The real treeId for this packet</param>
 /// <param name="consumedLength">[OUT]The consumed length of the message</param>
 /// <param name="expectedLength">[OUT]The expected length</param>
 /// <returns>A Smb2Packet</returns>
 public Smb2Packet DecodeSinglePacket(
     byte[] messageBytes,
     Smb2Role role,
     bool ignoreCompoundFlag,
     ulong realSessionId,
     uint realTreeId,
     out int consumedLength,
     out int expectedLength
     )
 {
     if (role == Smb2Role.Client)
     {
         return DecodeSingleResponsePacket(
             messageBytes,
             ignoreCompoundFlag,
             realSessionId,
             realTreeId,
             out consumedLength,
             out expectedLength
             );
     }
     else if (role == Smb2Role.Server)
     {
         return DecodeSingleRequestPacket(messageBytes, out consumedLength, out expectedLength);
     }
     else
     {
         throw new ArgumentException("role should be client or server", "role");
     }
 }
예제 #29
0
        public static byte[] Decrypt(byte[] bytes, Dictionary <ulong, Smb2CryptoInfo> cryptoInfoTable, Smb2Role role, out Transform_Header transformHeader)
        {
            var encryptedPacket = new Smb2EncryptedPacket();

            int consumedLen;
            int expectedLen;

            encryptedPacket.FromBytes(bytes, out consumedLen, out expectedLen);

            transformHeader = encryptedPacket.Header;

            // For client: If the Flags/EncryptionAlgorithm in the SMB2 TRANSFORM_HEADER is not 0x0001, the client MUST discard the message.
            // For server: If the Flags/EncryptionAlgorithm in the SMB2 TRANSFORM_HEADER is not 0x0001, the server MUST disconnect the connection.
            if (transformHeader.Flags != TransformHeaderFlags.Encrypted)
            {
                throw new InvalidOperationException(
                          String.Format(
                              "Flags/EncryptionAlgorithm field is invalid for encrypted message. Expected value 0x0001, actual {0}.",
                              (ushort)transformHeader.Flags
                              )
                          );
            }

            if (transformHeader.SessionId == 0 || !cryptoInfoTable.ContainsKey(transformHeader.SessionId))
            {
                throw new InvalidOperationException("Invalid SessionId in TRANSFORM_HEADER.");
            }

            Smb2CryptoInfo cryptoInfo = cryptoInfoTable[transformHeader.SessionId];

            byte[] decrypted = new byte[encryptedPacket.EncryptdData.Length];
            byte[] key       = role == Smb2Role.Server ? cryptoInfo.ServerInKey : cryptoInfo.ServerOutKey;
            // Auth data is Transform_Header start from Nonce, excluding ProtocolId and Signature.
            var authData = Smb2Utility.MarshalStructure(transformHeader).Skip((int)Marshal.OffsetOf <Transform_Header>("Nonce")).ToArray();

            int nonceLength;

            if (cryptoInfo.CipherId == EncryptionAlgorithm.ENCRYPTION_AES128_CCM)
            {
                nonceLength = Smb2Consts.AES128CCM_Nonce_Length;
            }
            else if (cryptoInfo.CipherId == EncryptionAlgorithm.ENCRYPTION_AES128_GCM)
            {
                nonceLength = Smb2Consts.AES128GCM_Nonce_Length;
            }
            else if (cryptoInfo.CipherId == EncryptionAlgorithm.ENCRYPTION_AES256_CCM)
            {
                nonceLength = Smb2Consts.AES256CCM_Nonce_Length;
            }
            else if (cryptoInfo.CipherId == EncryptionAlgorithm.ENCRYPTION_AES256_GCM)
            {
                nonceLength = Smb2Consts.AES256GCM_Nonce_Length;
            }
            else
            {
                throw new InvalidOperationException(String.Format(
                                                        "Invalid encryption algorithm is set in Smb2CryptoInfo when decrypting: {0}", cryptoInfo.CipherId));
            }

            if (cryptoInfo.CipherId == EncryptionAlgorithm.ENCRYPTION_AES128_CCM ||
                cryptoInfo.CipherId == EncryptionAlgorithm.ENCRYPTION_AES256_CCM)
            {
                using (var cipher = new AesCcm(key))
                {
                    cipher.Decrypt(
                        transformHeader.Nonce.ToByteArray().Take(nonceLength).ToArray(),
                        encryptedPacket.EncryptdData,
                        transformHeader.Signature,
                        decrypted,
                        authData);
                }
            }
            else
            {
                using (var cipher = new AesGcm(key))
                {
                    cipher.Decrypt(
                        transformHeader.Nonce.ToByteArray().Take(nonceLength).ToArray(),
                        encryptedPacket.EncryptdData,
                        transformHeader.Signature,
                        decrypted,
                        authData);
                }
            }


            return(decrypted);
        }
 /// <summary>
 /// Decode the packet as smb packet
 /// </summary>
 /// <param name="messageBytes">The received packet</param>
 /// <param name="role">The role of this decoder, client or server</param>
 /// <param name="consumedLen">[OUT]The consumed length of the message</param>
 /// <param name="expectedLen">[OUT]The expected length</param>
 /// <returns>A smb2Packet</returns>
 private static Smb2Packet DecodeSmbPacket(byte[] messageBytes, Smb2Role role, out int consumedLen, out int expectedLen)
 {
     // SMB2 only uses smb negotiate packet
     Smb2Packet packet;
     if (role == Smb2Role.Client)
     {
         packet = new SmbNegotiateResponsePacket();
     }
     else
     {
         packet = new SmbNegotiateRequestPacket();
     }
     packet.FromBytes(messageBytes, out consumedLen, out expectedLen);
     return packet;
 }
예제 #31
0
        private static Smb2EncryptedPacket Encrypt(ulong sessionId, Smb2CryptoInfo cryptoInfo, Smb2Role role, Smb2Packet packet, Smb2Packet packetBeforCompression)
        {
            Packet_Header header;

            if (packetBeforCompression is Smb2SinglePacket)
            {
                header = (packetBeforCompression as Smb2SinglePacket).Header;
            }
            else if (packetBeforCompression is Smb2CompoundPacket)
            {
                header = (packetBeforCompression as Smb2CompoundPacket).Packets[0].Header;
            }
            else
            {
                throw new InvalidOperationException("Unsupported SMB2 packet type!");
            }

            // Encrypt all messages after session setup if global encryption enabled.
            // Encrypt all messages after tree connect if global encryption disabled but share encryption enabled.
            if ((cryptoInfo.EnableSessionEncryption ||
                 (cryptoInfo.EnableTreeEncryption.Contains(header.TreeId) &&
                  header.Command != Smb2Command.TREE_CONNECT
                 )
                 )
                )
            {
                byte[]           originalBinary  = packet.ToBytes();
                Transform_Header transformHeader = new Transform_Header
                {
                    ProtocolId          = Smb2Consts.ProtocolIdInTransformHeader,
                    OriginalMessageSize = (uint)originalBinary.Length,
                    SessionId           = sessionId,
                    Signature           = new byte[16]
                };

                if (cryptoInfo.Dialect == DialectRevision.Smb311)
                {
                    transformHeader.Flags = TransformHeaderFlags.Encrypted;
                }
                else
                {
                    transformHeader.EncryptionAlgorithm = EncryptionAlgorithm.ENCRYPTION_AES128_CCM;
                }

                byte[] encrypted = new byte[originalBinary.Length];
                byte[] tag       = new byte[16];
                byte[] key       = role == Smb2Role.Server ? cryptoInfo.ServerOutKey : cryptoInfo.ServerInKey;

                // The reserved field (5 bytes for CCM, 4 bytes for GCM) must be set to zero.
                byte[] nonce = new byte[16];

                if (cryptoInfo.CipherId == EncryptionAlgorithm.ENCRYPTION_AES128_CCM)
                {
                    int nonceLength = Smb2Consts.AES128CCM_Nonce_Length;
                    Buffer.BlockCopy(Guid.NewGuid().ToByteArray(), 0, nonce, 0, nonceLength);
                    transformHeader.Nonce = new Guid(nonce);

                    using (var cipher = new AesCcm(key))
                    {
                        cipher.Encrypt(
                            transformHeader.Nonce.ToByteArray().Take(nonceLength).ToArray(),
                            originalBinary,
                            encrypted,
                            tag,
                            // Use the fields including and after Nonce field as auth data
                            Smb2Utility.MarshalStructure(transformHeader).Skip(20).ToArray());
                    }
                }
                else if (cryptoInfo.CipherId == EncryptionAlgorithm.ENCRYPTION_AES128_GCM)
                {
                    int nonceLength = Smb2Consts.AES128GCM_Nonce_Length;
                    Buffer.BlockCopy(Guid.NewGuid().ToByteArray(), 0, nonce, 0, nonceLength);
                    transformHeader.Nonce = new Guid(nonce);

                    using (var cipher = new AesGcm(key))
                    {
                        cipher.Encrypt(
                            transformHeader.Nonce.ToByteArray().Take(nonceLength).ToArray(),
                            originalBinary,
                            encrypted,
                            tag,
                            // Use the fields including and after Nonce field as auth data
                            Smb2Utility.MarshalStructure(transformHeader).Skip(20).ToArray());
                    }
                }
                else if (cryptoInfo.CipherId == EncryptionAlgorithm.ENCRYPTION_AES256_CCM)
                {
                    int nonceLength = Smb2Consts.AES256CCM_Nonce_Length;
                    Buffer.BlockCopy(Guid.NewGuid().ToByteArray(), 0, nonce, 0, nonceLength);
                    transformHeader.Nonce = new Guid(nonce);

                    using (var cipher = new AesCcm(key))
                    {
                        cipher.Encrypt(
                            transformHeader.Nonce.ToByteArray().Take(nonceLength).ToArray(),
                            originalBinary,
                            encrypted,
                            tag,
                            // Use the fields including and after Nonce field as auth data
                            Smb2Utility.MarshalStructure(transformHeader).Skip(20).ToArray());
                    }
                }
                else if (cryptoInfo.CipherId == EncryptionAlgorithm.ENCRYPTION_AES256_GCM)
                {
                    int nonceLength = Smb2Consts.AES256GCM_Nonce_Length;
                    Buffer.BlockCopy(Guid.NewGuid().ToByteArray(), 0, nonce, 0, nonceLength);
                    transformHeader.Nonce = new Guid(nonce);

                    using (var cipher = new AesGcm(key))
                    {
                        cipher.Encrypt(
                            transformHeader.Nonce.ToByteArray().Take(nonceLength).ToArray(),
                            originalBinary,
                            encrypted,
                            tag,
                            // Use the fields including and after Nonce field as auth data
                            Smb2Utility.MarshalStructure(transformHeader).Skip(20).ToArray());
                    }
                }
                else
                {
                    throw new InvalidOperationException(String.Format(
                                                            "Invalid encryption algorithm is set in Smb2CryptoInfo when encrypting: {0}", cryptoInfo.CipherId));
                }

                transformHeader.Signature = tag;

                var encryptedPacket = new Smb2EncryptedPacket();
                encryptedPacket.Header       = transformHeader;
                encryptedPacket.EncryptdData = encrypted;

                return(encryptedPacket);
            }

            // Return null if the message is not required to be encrypted.
            return(null);
        }
 /// <summary>
 /// Constructor
 /// </summary>
 /// <param name="decodeRole">The decode role, client or server</param>
 /// <param name="cryptoInfoTable">Crypto info table indexed by session ID</param>
 public Smb2Decoder(Smb2Role decodeRole, Dictionary<ulong, Smb2CryptoInfo> cryptoInfoTable)
 {
     this.decodeRole = decodeRole;
     this.cryptoInfoTable = cryptoInfoTable;
 }
        public Smb2Packet DecodeTransportPayload(
            byte[] messageBytes,
            Smb2Role role,
            Smb2TransportType transportType,
            out int consumedLength,
            out int expectedLength
            )
        {
            //tcp transport will prefix 4 bytes length in the beginning. and netbios won't do this.
            if (transportType == Smb2TransportType.Tcp)
            {
                if (messageBytes.Length < Smb2Consts.TcpPrefixedLenByteCount)
                {
                    consumedLength = 0;
                    expectedLength = 4;
                    return(null);
                }

                //in the header of tcp payload, there are 4 bytes(in fact only 3 bytes are used) which indicate
                //the length of smb2
                int dataLenShouldHave = (messageBytes[1] << 16) + (messageBytes[2] << 8) + messageBytes[3];

                if (dataLenShouldHave > (messageBytes.Length - Smb2Consts.TcpPrefixedLenByteCount))
                {
                    consumedLength = 0;
                    expectedLength = Smb2Consts.TcpPrefixedLenByteCount + dataLenShouldHave;
                    return(null);
                }

                byte[] smb2Message = new byte[dataLenShouldHave];

                Array.Copy(messageBytes, Smb2Consts.TcpPrefixedLenByteCount, smb2Message, 0, smb2Message.Length);

                Smb2Packet packet = DecodeCompletePacket(
                    smb2Message,
                    role,
                    0,
                    0,
                    out consumedLength,
                    out expectedLength);

                // Here we ignore the consumedLength returned by DecodeCompletePacket(), there may be some tcp padding data
                // at the end which we are not interested.
                consumedLength = dataLenShouldHave + Smb2Consts.TcpPrefixedLenByteCount;

                return(packet);
            }
            else
            {
                Smb2Packet packet = DecodeCompletePacket(
                    messageBytes,
                    role,
                    0,
                    0,
                    out consumedLength,
                    out expectedLength);

                //Some packet has unknown padding data at the end.
                consumedLength = messageBytes.Length;

                return(packet);
            }
        }
        /// <summary>
        /// Decode the  message except length field which may exist if transport is tcp
        /// </summary>
        /// <param name="messageBytes">The received packet</param>
        /// <param name="role">The role of this decoder, client or server</param>
        /// <param name="ignoreCompoundFlag">indicate whether decode the packet as a single packet or a compound packet
        /// when compound flag is set</param>
        /// <param name="realSessionId">The real sessionId for this packet</param>
        /// <param name="realTreeId">The real treeId for this packet</param>
        /// <param name="consumedLength">[OUT]The consumed length of the message</param>
        /// <param name="expectedLength">[OUT]The expected length</param>
        /// <returns>A Smb2Packet</returns>
        public Smb2Packet DecodeCompletePacket(
            byte[] messageBytes,
            Smb2Role role,
            bool ignoreCompoundFlag,
            ulong realSessionId,
            uint realTreeId,
            out int consumedLength,
            out int expectedLength
            )
        {
            //protocol version is of 4 bytes len
            byte[] protocolVersion = new byte[sizeof(uint)];
            Array.Copy(messageBytes, 0, protocolVersion, 0, protocolVersion.Length);

            SmbVersion version = DecodeVersion(protocolVersion);

            if (version == SmbVersion.Version1)
            {
                // SMB Negotiate packet
                return DecodeSmbPacket(messageBytes, role, out consumedLength, out expectedLength);
            }
            else if (version == SmbVersion.Version2Encrypted)
            {
                // SMB2 encrypted packet
                return DecodeEncryptedSmb2Packet(
                    messageBytes,
                    role,
                    ignoreCompoundFlag,
                    realSessionId,
                    realTreeId,
                    out consumedLength,
                    out expectedLength
                    );
            }
            else
            {
                // SMB2 packet not encrypted
                Smb2Packet decodedPacket = DecodeSmb2Packet(
                    messageBytes,
                    role,
                    ignoreCompoundFlag,
                    realSessionId,
                    realTreeId,
                    out consumedLength,
                    out expectedLength
                    );

                //For single packet signature verification
                if (decodedPacket is Smb2SinglePacket)
                {
                    //verify signature of a single packet
                    Smb2SinglePacket singlePacket = decodedPacket as Smb2SinglePacket;

                    TryVerifySignature(singlePacket, singlePacket.Header.SessionId, messageBytes);
                }
                else if (decodedPacket is Smb2CompoundPacket)//For Compound packet signature verification
                {
                    //verify signature of the compound packet
                    TryVerifySignature(decodedPacket as Smb2CompoundPacket, messageBytes);
                }

                return decodedPacket;
            }
        }
        /// <summary>
        /// Decode the  message except length field which may exist if transport is tcp
        /// </summary>
        /// <param name="messageBytes">The received packet</param>
        /// <param name="role">The role of this decoder, client or server</param>
        /// <param name="ignoreCompoundFlag">indicate whether decode the packet as a single packet or a compound packet
        /// when compound flag is set</param>
        /// <param name="realSessionId">The real sessionId for this packet</param>
        /// <param name="realTreeId">The real treeId for this packet</param>
        /// <param name="consumedLength">[OUT]The consumed length of the message</param>
        /// <param name="expectedLength">[OUT]The expected length</param>
        /// <returns>A Smb2Packet</returns>
        public Smb2Packet DecodeCompletePacket(
            byte[] messageBytes,
            Smb2Role role,
            bool ignoreCompoundFlag,
            ulong realSessionId,
            uint realTreeId,
            out int consumedLength,
            out int expectedLength
            )
        {
            //protocol version is of 4 bytes len
            byte[] protocolVersion = new byte[sizeof(uint)];
            Array.Copy(messageBytes, 0, protocolVersion, 0, protocolVersion.Length);

            SmbVersion version = DecodeVersion(protocolVersion);

            if (version == SmbVersion.Version1)
            {
                // SMB Negotiate packet
                return(DecodeSmbPacket(messageBytes, role, out consumedLength, out expectedLength));
            }
            else if (version == SmbVersion.Version2Encrypted)
            {
                // SMB2 encrypted packet
                return(DecodeEncryptedSmb2Packet(
                           messageBytes,
                           role,
                           ignoreCompoundFlag,
                           realSessionId,
                           realTreeId,
                           out consumedLength,
                           out expectedLength
                           ));
            }
            else
            {
                // SMB2 packet not encrypted
                Smb2Packet decodedPacket = DecodeSmb2Packet(
                    messageBytes,
                    role,
                    ignoreCompoundFlag,
                    realSessionId,
                    realTreeId,
                    out consumedLength,
                    out expectedLength
                    );

                //For single packet signature verification
                if (decodedPacket is Smb2SinglePacket)
                {
                    //verify signature of a single packet
                    Smb2SinglePacket singlePacket = decodedPacket as Smb2SinglePacket;

                    if (!TryVerifySignature(singlePacket, singlePacket.Header.SessionId))
                    {
                        throw new Exception("Error happened during signature verification of single packet: " + singlePacket.ToString());
                    }
                }
                else if (decodedPacket is Smb2CompoundPacket)//For Compound packet signature verification
                {
                    //verify signature of the compound packet
                    if (!TryVerifySignature(decodedPacket as Smb2CompoundPacket))
                    {
                        throw new Exception("Error happened during signature verification of compound packet: " + decodedPacket.ToString());
                    }
                }

                return(decodedPacket);
            }
        }