From f283baea6940de63a4e78e97387c0a5562305dc5 Mon Sep 17 00:00:00 2001 From: Julien Gaulon Date: Wed, 20 Oct 2021 15:29:06 +0200 Subject: [PATCH 1/3] try to decode ESP frames, assuming Null Ciphering and 96 bits Integrity algo --- PacketDotNet/EspFields.cs | 32 +++++++ PacketDotNet/EspPacket.cs | 156 +++++++++++++++++++++++++++++++ PacketDotNet/IPPacket.cs | 5 + Test/CaptureFiles/ipv6_esp.pcap | Bin 0 -> 144 bytes Test/PacketType/EspPacketTest.cs | 59 ++++++++++++ 5 files changed, 252 insertions(+) create mode 100644 PacketDotNet/EspFields.cs create mode 100644 PacketDotNet/EspPacket.cs create mode 100644 Test/CaptureFiles/ipv6_esp.pcap create mode 100644 Test/PacketType/EspPacketTest.cs diff --git a/PacketDotNet/EspFields.cs b/PacketDotNet/EspFields.cs new file mode 100644 index 00000000..569d45be --- /dev/null +++ b/PacketDotNet/EspFields.cs @@ -0,0 +1,32 @@ +/* +This file is part of PacketDotNet. + +This Source Code Form is subject to the terms of the Mozilla Public +License, v. 2.0. If a copy of the MPL was not distributed with this +file, You can obtain one at https://mozilla.org/MPL/2.0/. +*/ +/* + * Copyright 2009 Chris Morgan + */ + +namespace PacketDotNet +{ + public struct EspFields + { + /// Length of the Base Header in bytes. + public static readonly int HeaderLength = 8; + + /// Length of the Security Parameters Index (SPI) in bytes. + public static readonly int SecurityParametersIndexLength = 4; + + /// Length of the Sequence Number in bytes. + public static readonly int SequenceNumberLength = 4; + + /// Length of the Pad Length in bytes. + public static readonly int PadLengthLength = 1; + + /// Length of the Next Header in bytes. + public static readonly int NextHeaderLength = 1; + + } +} diff --git a/PacketDotNet/EspPacket.cs b/PacketDotNet/EspPacket.cs new file mode 100644 index 00000000..7e5cd7e7 --- /dev/null +++ b/PacketDotNet/EspPacket.cs @@ -0,0 +1,156 @@ +/* +This file is part of PacketDotNet. + +This Source Code Form is subject to the terms of the Mozilla Public +License, v. 2.0. If a copy of the MPL was not distributed with this +file, You can obtain one at https://mozilla.org/MPL/2.0/. +*/ +/* + * Copyright 2010 Chris Morgan + */ + + +using System; +using PacketDotNet.Utils; +using PacketDotNet.Utils.Converters; +#if DEBUG +using log4net; +using System.Reflection; +#endif + +namespace PacketDotNet +{ + /// + /// EspPacket + /// See: https://en.wikipedia.org/wiki/IPsec#Encapsulating_Security_Payload + /// + public sealed class EspPacket : Packet + { +#if DEBUG + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); +#else +// NOTE: No need to warn about lack of use, the compiler won't +// put any calls to 'log' here but we need 'log' to exist to compile +#pragma warning disable 0169, 0649 + private static readonly ILogInactive Log; +#pragma warning restore 0169, 0649 +#endif + + /// + /// Create from values + /// + public EspPacket() + { + Log.Debug(""); + + // allocate memory for this packet + var length = EspFields.HeaderLength; + var headerBytes = new byte[length]; + Header = new ByteArraySegment(headerBytes, 0, length); + } + + /// + /// Constructor + /// + /// + /// A + /// + /// + /// A + /// + public EspPacket(ByteArraySegment byteArraySegment, Packet parentPacket) + { + Log.Debug(""); + + // set the header field, header field values are retrieved from this byte array + Header = new ByteArraySegment(byteArraySegment) {Length = EspFields.HeaderLength}; + + ParentPacket = parentPacket; + + var next = Header.NextSegment(); + + // try to decode, assuming a Null ciphering. Get first the last 96 bits (12 bytes) for the Authentication Data + // + 1 byte for the Next Header + 1 byte for the pad length + if (next.Length > 14) + { + AuthenticationData = new byte[12]; + + // copy the last 12 bytes + Array.Copy(next.Bytes, next.BytesLength - 12, AuthenticationData, 0, 12); + var nextHeader = next.Bytes[next.BytesLength - 13]; + // Continue only if next header is Tcp or Udp + if (Enum.IsDefined(typeof(ProtocolType), nextHeader) && ((ProtocolType)nextHeader == ProtocolType.Tcp || (ProtocolType)nextHeader == ProtocolType.Udp)) + { + NextHeader = (ProtocolType) nextHeader; + PadLength = next.Bytes[next.BytesLength - 14]; + + if (next.Length > 14 + PadLength) + { + // so far ok, continue + Pad = new byte[PadLength]; + Array.Copy(next.Bytes, next.BytesLength - 14 - PadLength, Pad, 0, PadLength); + + var startingOffset = Header.Offset + Header.Length; + var segmentLength = Math.Max(0, next.Length - 14 - PadLength); + var payloadData = new byte[segmentLength]; + Array.Copy(next.Bytes, startingOffset, payloadData, 0, segmentLength); + var payload = new ByteArraySegment(payloadData, 0, segmentLength, segmentLength); + + if (NextHeader == ProtocolType.Tcp && segmentLength > 0) + { + PayloadPacketOrData = new LazySlim(() => new PacketOrByteArraySegment + { + Packet = new TcpPacket(payload, this) + }); + + return; + } + } + } + } + + // store the payload bytes + PayloadPacketOrData = new LazySlim(() => + { + var result = new PacketOrByteArraySegment {ByteArraySegment = Header.NextSegment()}; + return result; + }); + } + + /// + /// Gets or sets the SecurityParametersIndex (SPI) + /// + public uint SecurityParametersIndex + { + get => EndianBitConverter.Big.ToUInt32(Header.Bytes, Header.Offset); + set => EndianBitConverter.Big.CopyBytes(value, Header.Bytes, Header.Offset); + } + + /// + /// Gets or sets the SecurityParametersIndex (SPI) + /// + public uint SequenceNumber + { + get => EndianBitConverter.Big.ToUInt32(Header.Bytes, Header.Offset + EspFields.SecurityParametersIndexLength); + set => EndianBitConverter.Big.CopyBytes(value, Header.Bytes, Header.Offset + EspFields.SecurityParametersIndexLength); + } + + /// + /// Identifies the next header field, which is the protocol of the encapsulated in packet unless there are extended headers. + /// + public ProtocolType NextHeader { get; set; } + + /// Pad length + public int PadLength { get; private set; } + + /// + /// Gets or sets the Authentication Data + /// + public byte[] AuthenticationData { get; set; } + + /// + /// Gets or sets the Pad + /// + public byte[] Pad { get; private set; } + } +} diff --git a/PacketDotNet/IPPacket.cs b/PacketDotNet/IPPacket.cs index 784d4aa7..e427a42c 100644 --- a/PacketDotNet/IPPacket.cs +++ b/PacketDotNet/IPPacket.cs @@ -282,6 +282,11 @@ protected static PacketOrByteArraySegment ParseNextSegment payloadPacketOrData.Packet = new GrePacket(payload, parentPacket); break; } + case ProtocolType.IPSecEncapsulatingSecurityPayload: + { + payloadPacketOrData.Packet = new EspPacket(payload, parentPacket); + break; + } // NOTE: new payload parsing entries go here default: { diff --git a/Test/CaptureFiles/ipv6_esp.pcap b/Test/CaptureFiles/ipv6_esp.pcap new file mode 100644 index 0000000000000000000000000000000000000000..ba7813f0847c09da95a3bae5872cabea7b34579d GIT binary patch literal 144 zcmca|c+)~A1{MYcU|~oFa;93bGjy)tX2<}tK{%}eNH`cd{AFCd`s~^F586Uk`zFnN zEc%!6)IXpM3@~zraV`XM7!OAapU}OuF9Ia7fXS|bot1%siG^hY3llRl0|ytwk@l+~ XDMm&nCbr+clUw#L_A7NiyQduhxMn1Q literal 0 HcmV?d00001 diff --git a/Test/PacketType/EspPacketTest.cs b/Test/PacketType/EspPacketTest.cs new file mode 100644 index 00000000..b3998a7e --- /dev/null +++ b/Test/PacketType/EspPacketTest.cs @@ -0,0 +1,59 @@ +/* +This file is part of PacketDotNet. + +This Source Code Form is subject to the terms of the Mozilla Public +License, v. 2.0. If a copy of the MPL was not distributed with this +file, You can obtain one at https://mozilla.org/MPL/2.0/. +*/ + +using System; +using NUnit.Framework; +using PacketDotNet; +using SharpPcap; +using SharpPcap.LibPcap; + +namespace Test.PacketType +{ + [TestFixture] + public class EspPacketTest + { + [Test] + public void EspParsing() + { + var dev = new CaptureFileReaderDevice(NUnitSetupClass.CaptureDirectory + "ipv6_esp.pcap"); + dev.Open(); + PacketCapture c; + dev.GetNextPacket(out c); + var rawCapture = c.GetPacket(); + dev.Close(); + + var p = Packet.ParsePacket(rawCapture.GetLinkLayers(), rawCapture.Data); + + Assert.IsNotNull(p); + var esp = p.Extract(); + Assert.IsNotNull(esp); + Console.WriteLine(esp.GetType()); + Assert.AreEqual(156633505, esp.SecurityParametersIndex); + Assert.AreEqual(1, esp.SequenceNumber); + Assert.AreEqual(ProtocolType.Tcp, esp.NextHeader); + Assert.AreEqual(2, esp.PadLength); + Assert.IsNotNull(esp.PayloadPacket); + var tcp = p.Extract(); + Assert.IsNotNull(tcp); + Assert.AreEqual(0, tcp.UrgentPointer); + Assert.AreEqual(16000, tcp.WindowSize); + Assert.AreEqual(4, tcp.OptionsCollection.Count); + } + + [Test] + public void ConstructEspPacketFromValues() + { + var esp = new EspPacket + { + SequenceNumber = 1, + SecurityParametersIndex = 156633505, + NextHeader = ProtocolType.Tcp, + }; + } + } +} From 60e5b3b58b39704535bc6ec71f2dc820c9562f4b Mon Sep 17 00:00:00 2001 From: Julien Gaulon Date: Fri, 22 Oct 2021 11:16:05 +0200 Subject: [PATCH 2/3] commit after review --- PacketDotNet/EspPacket.cs | 16 ++++++++-------- Test/PacketType/EspPacketTest.cs | 3 +++ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/PacketDotNet/EspPacket.cs b/PacketDotNet/EspPacket.cs index 7e5cd7e7..924eafc7 100644 --- a/PacketDotNet/EspPacket.cs +++ b/PacketDotNet/EspPacket.cs @@ -37,7 +37,7 @@ public sealed class EspPacket : Packet #endif /// - /// Create from values + /// Create from values. /// public EspPacket() { @@ -50,7 +50,7 @@ public EspPacket() } /// - /// Constructor + /// Constructor. /// /// /// A @@ -79,7 +79,7 @@ public EspPacket(ByteArraySegment byteArraySegment, Packet parentPacket) Array.Copy(next.Bytes, next.BytesLength - 12, AuthenticationData, 0, 12); var nextHeader = next.Bytes[next.BytesLength - 13]; // Continue only if next header is Tcp or Udp - if (Enum.IsDefined(typeof(ProtocolType), nextHeader) && ((ProtocolType)nextHeader == ProtocolType.Tcp || (ProtocolType)nextHeader == ProtocolType.Udp)) + if ((ProtocolType)nextHeader == ProtocolType.Tcp || (ProtocolType)nextHeader == ProtocolType.Udp) { NextHeader = (ProtocolType) nextHeader; PadLength = next.Bytes[next.BytesLength - 14]; @@ -118,7 +118,7 @@ public EspPacket(ByteArraySegment byteArraySegment, Packet parentPacket) } /// - /// Gets or sets the SecurityParametersIndex (SPI) + /// Gets or sets the SecurityParametersIndex (SPI). /// public uint SecurityParametersIndex { @@ -127,7 +127,7 @@ public uint SecurityParametersIndex } /// - /// Gets or sets the SecurityParametersIndex (SPI) + /// Gets or sets the SecurityParametersIndex (SPI). /// public uint SequenceNumber { @@ -140,16 +140,16 @@ public uint SequenceNumber /// public ProtocolType NextHeader { get; set; } - /// Pad length + /// Pad length. public int PadLength { get; private set; } /// - /// Gets or sets the Authentication Data + /// Gets or sets the Authentication Data. /// public byte[] AuthenticationData { get; set; } /// - /// Gets or sets the Pad + /// Gets or sets the Pad. /// public byte[] Pad { get; private set; } } diff --git a/Test/PacketType/EspPacketTest.cs b/Test/PacketType/EspPacketTest.cs index b3998a7e..3fccea54 100644 --- a/Test/PacketType/EspPacketTest.cs +++ b/Test/PacketType/EspPacketTest.cs @@ -54,6 +54,9 @@ public void ConstructEspPacketFromValues() SecurityParametersIndex = 156633505, NextHeader = ProtocolType.Tcp, }; + + Assert.NotNull(esp); + Assert.AreEqual(ProtocolType.Tcp, esp.NextHeader); } } } From e8cd1165b1b31000f47350fbe21cc40028a2c5d2 Mon Sep 17 00:00:00 2001 From: Julien Gaulon Date: Thu, 28 Oct 2021 15:53:29 +0200 Subject: [PATCH 3/3] Set AuthenticationData and Padding bytes --- PacketDotNet/EspPacket.cs | 29 ++++++++++++++++++++++++++--- Test/PacketType/EspPacketTest.cs | 13 ++++--------- 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/PacketDotNet/EspPacket.cs b/PacketDotNet/EspPacket.cs index 924eafc7..f9ed5520 100644 --- a/PacketDotNet/EspPacket.cs +++ b/PacketDotNet/EspPacket.cs @@ -39,7 +39,7 @@ public sealed class EspPacket : Packet /// /// Create from values. /// - public EspPacket() + public EspPacket(TransportPacket payloadPacket, byte[] authenticationData) { Log.Debug(""); @@ -47,6 +47,19 @@ public EspPacket() var length = EspFields.HeaderLength; var headerBytes = new byte[length]; Header = new ByteArraySegment(headerBytes, 0, length); + NextHeader = payloadPacket is TcpPacket ? ProtocolType.Tcp : ProtocolType.Udp; + payloadPacket.ParentPacket = this; + this.AuthenticationData = authenticationData; + this.PadLength = (payloadPacket.TotalPacketLength + 2) % 4; + this.Pad = new byte[this.PadLength]; + for (var i = 0; i < this.PadLength; i++) + { + this.Pad[i] = (byte) (i + 1); + } + PayloadPacketOrData = new LazySlim(() => new PacketOrByteArraySegment + { + Packet = payloadPacket + }); } /// @@ -105,6 +118,16 @@ public EspPacket(ByteArraySegment byteArraySegment, Packet parentPacket) return; } + + if (NextHeader == ProtocolType.Udp && segmentLength > 0) + { + PayloadPacketOrData = new LazySlim(() => new PacketOrByteArraySegment + { + Packet = new UdpPacket(payload, this) + }); + + return; + } } } } @@ -141,7 +164,7 @@ public uint SequenceNumber public ProtocolType NextHeader { get; set; } /// Pad length. - public int PadLength { get; private set; } + public int PadLength { get; } /// /// Gets or sets the Authentication Data. @@ -151,6 +174,6 @@ public uint SequenceNumber /// /// Gets or sets the Pad. /// - public byte[] Pad { get; private set; } + public byte[] Pad { get; } } } diff --git a/Test/PacketType/EspPacketTest.cs b/Test/PacketType/EspPacketTest.cs index 3fccea54..368fc332 100644 --- a/Test/PacketType/EspPacketTest.cs +++ b/Test/PacketType/EspPacketTest.cs @@ -43,20 +43,15 @@ public void EspParsing() Assert.AreEqual(0, tcp.UrgentPointer); Assert.AreEqual(16000, tcp.WindowSize); Assert.AreEqual(4, tcp.OptionsCollection.Count); - } - [Test] - public void ConstructEspPacketFromValues() - { - var esp = new EspPacket + var espCreated = new EspPacket(tcp, esp.AuthenticationData) { SequenceNumber = 1, - SecurityParametersIndex = 156633505, - NextHeader = ProtocolType.Tcp, + SecurityParametersIndex = 156633505 }; - Assert.NotNull(esp); - Assert.AreEqual(ProtocolType.Tcp, esp.NextHeader); + Assert.NotNull(espCreated); + Assert.AreEqual(ProtocolType.Tcp, espCreated.NextHeader); } } }