From 376201cfa55c3fd6520f725e57486b34b9ef5892 Mon Sep 17 00:00:00 2001 From: feyris-tan <4116042+feyris-tan@users.noreply.github.com> Date: Sun, 28 Dec 2025 09:49:07 +0100 Subject: [PATCH] Made preparations for unscrambling TS Packets in case the key is known. --- .../PacketFilter/BissDescrambleFilter.cs | 62 ++++++++ skyscraper8/Mpeg2/TsPacket.cs | 26 ++-- skyscraper8/Program.cs | 65 ++++----- .../Security/AccessControl/BissKey.cs | 5 + .../Security/AccessControl/SoftcamKeyset.cs | 17 +++ .../AccessControl/SoftcamTestProgram.cs | 33 +++++ .../Security/Cryptography/DvbCsa2.cs | 132 +++++++++++++++++- 7 files changed, 293 insertions(+), 47 deletions(-) create mode 100644 skyscraper8/Mpeg2/PacketFilter/BissDescrambleFilter.cs create mode 100644 skyscraper8/Skyscraper/Security/AccessControl/SoftcamTestProgram.cs diff --git a/skyscraper8/Mpeg2/PacketFilter/BissDescrambleFilter.cs b/skyscraper8/Mpeg2/PacketFilter/BissDescrambleFilter.cs new file mode 100644 index 0000000..69195a0 --- /dev/null +++ b/skyscraper8/Mpeg2/PacketFilter/BissDescrambleFilter.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using skyscraper5.Mpeg2; +using skyscraper5.src.Mpeg2.PacketFilter; +using skyscraper8.Skyscraper.Security.Cryptography; + +namespace skyscraper8.Mpeg2.PacketFilter +{ + internal class BissDescrambleFilter : IPacketFilter + { + public BissDescrambleFilter(byte[] key, params uint[] pids) + { + this.key = key; + if (pids != null) + { + if (pids.Length > 0) + { + _pidsToDescramble = new List(); + _pidsToDescramble.AddRange(pids); + } + } + } + + private byte[] key; + private List _pidsToDescramble; + private bool CheckPid(uint pid) + { + if (_pidsToDescramble == null) + return true; + else + return _pidsToDescramble.Contains(pid); + } + + private DvbCsa2 csa2; + + public bool PassPacket(TsPacket packet) + { + if (packet.TSC == 0) + return true; + + if (!CheckPid(packet.PID)) + return true; + + //Okay, we actually need to do something... + if (csa2 == null) + { + csa2 = new DvbCsa2(key.Length == 8 ? DvbCsa2.EntropyMode.FULL_CW : DvbCsa2.EntropyMode.REDUCE_ENTROPY); + csa2.SetKeyImpl(key); + } + + int retsize = 0; + byte[] unscrambled = new byte[packet.Payload.Length]; + csa2.DecryptImpl(packet.Payload, packet.Payload.Length, unscrambled, unscrambled.Length, ref retsize); + + packet.SetUnscrambled(unscrambled); + return true; + } + } +} diff --git a/skyscraper8/Mpeg2/TsPacket.cs b/skyscraper8/Mpeg2/TsPacket.cs index 4fa85c0..7d94cd7 100644 --- a/skyscraper8/Mpeg2/TsPacket.cs +++ b/skyscraper8/Mpeg2/TsPacket.cs @@ -48,18 +48,18 @@ namespace skyscraper5.Mpeg2 //Unit start } - int payloadOffset = 4; + PayloadOffset = 4; if (PayloadUnitStart) { PayloadStartOffset = buffer[4] & 0xff; - payloadOffset++; + PayloadOffset++; } switch (AdaptionFieldControl) { case 1: - br.Position = payloadOffset; - long PayloadLengthB = 188 - payloadOffset; + br.Position = PayloadOffset; + long PayloadLengthB = 188 - PayloadOffset; Payload = br.ReadBytes(PayloadLengthB); break; case 2: @@ -74,9 +74,9 @@ namespace skyscraper5.Mpeg2 TEI = true; break; } - payloadOffset += Adaption.Length; - br.Position = payloadOffset; - long PayloadLength = (188 - payloadOffset); + PayloadOffset += Adaption.Length; + br.Position = PayloadOffset; + long PayloadLength = (188 - PayloadOffset); Payload = br.ReadBytes((int)PayloadLength); break; default: @@ -99,7 +99,7 @@ namespace skyscraper5.Mpeg2 /// 2 = scrambled, even key /// 3 = scrambled, odd key /// - public uint TSC { get; private set; } + public uint TSC { get; internal set; } public uint AdaptionFieldControl { get; private set; } public uint Continuity { get; private set; } public int PayloadStartOffset { get; private set; } @@ -108,6 +108,8 @@ namespace skyscraper5.Mpeg2 public byte[] RawPacket { get; private set; } public ulong Serial { get; private set; } + public int PayloadOffset { get; private set; } + /// /// Retrieving the raw packet, including adaption field and Payload, but without the four byte header. /// @@ -118,5 +120,13 @@ namespace skyscraper5.Mpeg2 Array.Copy(RawPacket, 4, buffer, 0, 184); return buffer; } + + public void SetUnscrambled(byte[] unscrambledPayload) + { + TSC = 0; + Payload = unscrambledPayload; + Array.Copy(unscrambledPayload, 0, RawPacket, PayloadOffset, unscrambledPayload.Length); + RawPacket[3] &= 0x3f; + } } } diff --git a/skyscraper8/Program.cs b/skyscraper8/Program.cs index 31c10a1..810207b 100644 --- a/skyscraper8/Program.cs +++ b/skyscraper8/Program.cs @@ -11,7 +11,6 @@ using skyscraper5.Skyscraper.IO.TunerInterface; using skyscraper5.Skyscraper.Plugins; using skyscraper5.Skyscraper.RecordingImporter; using skyscraper5.Skyscraper.Scraper; -using skyscraper5.Skyscraper.Scraper.FrameGrabber; using skyscraper5.Skyscraper.Scraper.Storage.Filesystem; using skyscraper5.Skyscraper.Scraper.Storage.InMemory; using skyscraper5.Skyscraper.Webserver; @@ -24,15 +23,9 @@ using System.Diagnostics; using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; -using System.Reflection; using System.Runtime.InteropServices; -using skyscraper5.Mpeg2.Descriptors; -using skyscraper5.Skyscraper.Scraper.StreamAutodetection; using skyscraper8; using skyscraper8.GSE; -using skyscraper8.SatIp; -using skyscraper8.SatIp.RtspResponses; -using skyscraper8.SimpleServiceDiscoveryProtocol; using skyscraper8.Skyscraper.Math; using skyscraper8.Skyscraper; using skyscraper8.Skyscraper.Security.AccessControl; @@ -46,40 +39,42 @@ namespace skyscraper5 private static void IntegrationTest() { - /*List ssdpDevices = SsdpClient.GetSsdpDevices(1000).ToList(); - foreach (SsdpDevice ssdpDevice in ssdpDevices) - { - Console.WriteLine("SSDP device: {0}", ssdpDevice.Server); - }*/ + SoftcamTestProgram softcamTestProgram = new SoftcamTestProgram(); + softcamTestProgram.Run(); + /*List ssdpDevices = SsdpClient.GetSsdpDevices(1000).ToList(); + foreach (SsdpDevice ssdpDevice in ssdpDevices) + { + Console.WriteLine("SSDP device: {0}", ssdpDevice.Server); + }*/ - //"urn:ses-com:device:SatIPServer:1" - /*PluginManager pluginManager = PluginManager.GetInstance(); - StorageConnectionManager storageConnectionManager = StorageConnectionManager.GetInstance(); - ObjectStorageFactory objectStorageFactory = storageConnectionManager.GetDefaultObjectStorageFactory(); - ObjectStorage objectStorage = objectStorageFactory.CreateObjectStorage();*/ + //"urn:ses-com:device:SatIPServer:1" + /*PluginManager pluginManager = PluginManager.GetInstance(); + StorageConnectionManager storageConnectionManager = StorageConnectionManager.GetInstance(); + ObjectStorageFactory objectStorageFactory = storageConnectionManager.GetDefaultObjectStorageFactory(); + ObjectStorage objectStorage = objectStorageFactory.CreateObjectStorage();*/ - /*url = RtspClient.MakeUrl(DiSEqC_Opcode.DISEQC_OPTION_A | DiSEqC_Opcode.DISEQC_POSITION_A | DiSEqC_Opcode.DISEQC_HORIZONTAL, 11141, true, 23500); - describe = rtspClient.GetDescribe(url); - sessionDescriptionProtocol = describe.GetSessionDescriptionProtocol(); + /*url = RtspClient.MakeUrl(DiSEqC_Opcode.DISEQC_OPTION_A | DiSEqC_Opcode.DISEQC_POSITION_A | DiSEqC_Opcode.DISEQC_HORIZONTAL, 11141, true, 23500); + describe = rtspClient.GetDescribe(url); + sessionDescriptionProtocol = describe.GetSessionDescriptionProtocol(); - rtcps = 0; - rtps = 0; + rtcps = 0; + rtps = 0; - setup = rtspClient.GetSetup(url); - setup.OnRtcpPacket += ((data, length) => - rtcps++); - setup.OnRtpPacket += (data, length) => - rtps++; + setup = rtspClient.GetSetup(url); + setup.OnRtcpPacket += ((data, length) => + rtcps++); + setup.OnRtpPacket += (data, length) => + rtps++; - play = rtspClient.GetPlay(setup); + play = rtspClient.GetPlay(setup); - Thread.Sleep(5000); + Thread.Sleep(5000); - rtspClient.AutoReconnect = false; - rtspClient.GetTeardown(setup); - Console.WriteLine("{0} RTCPs", rtcps); - Console.WriteLine("{0} RTPs", rtps);*/ - //rtspClient.Dispose(); + rtspClient.AutoReconnect = false; + rtspClient.GetTeardown(setup); + Console.WriteLine("{0} RTCPs", rtcps); + Console.WriteLine("{0} RTPs", rtps);*/ + //rtspClient.Dispose(); } static void Main(string[] args) @@ -365,7 +360,7 @@ namespace skyscraper5 catalogueGenerator.Dispose(); return; } - } + } /*Passing passing = new Passing(); if (!passing.Boot()) diff --git a/skyscraper8/Skyscraper/Security/AccessControl/BissKey.cs b/skyscraper8/Skyscraper/Security/AccessControl/BissKey.cs index 515101c..5ac6c36 100644 --- a/skyscraper8/Skyscraper/Security/AccessControl/BissKey.cs +++ b/skyscraper8/Skyscraper/Security/AccessControl/BissKey.cs @@ -38,5 +38,10 @@ namespace skyscraper8.Skyscraper.Security.AccessControl { return (int)Coordinate; } + + public override string ToString() + { + return String.Format("F {0:X8} 00000000 {1}", Coordinate, BitConverter.ToString(Key).Replace("-", "")); + } } } diff --git a/skyscraper8/Skyscraper/Security/AccessControl/SoftcamKeyset.cs b/skyscraper8/Skyscraper/Security/AccessControl/SoftcamKeyset.cs index 2d6069f..c140168 100644 --- a/skyscraper8/Skyscraper/Security/AccessControl/SoftcamKeyset.cs +++ b/skyscraper8/Skyscraper/Security/AccessControl/SoftcamKeyset.cs @@ -213,5 +213,22 @@ namespace skyscraper8.Skyscraper.Security.AccessControl private HashSet _bissKeys; public IReadOnlySet BissKeys => _bissKeys; + + public byte[] FindBissKey(ushort transportStreamId, ushort networkId) + { + if (_bissKeys == null) + return null; + + byte[] transportStreamIdBytes = BitConverter.GetBytes(transportStreamId); + byte[] networkIdBytes = BitConverter.GetBytes(networkId); + byte[] keyBytes = new byte[] { networkIdBytes[0], networkIdBytes[1], transportStreamIdBytes[0], transportStreamIdBytes[1] }; + uint coordinate = BitConverter.ToUInt32(keyBytes); + + BissKey bissKey = _bissKeys.FirstOrDefault(x => x.Coordinate == coordinate); + if (bissKey != null) + return bissKey.Key; + else + return null; + } } } diff --git a/skyscraper8/Skyscraper/Security/AccessControl/SoftcamTestProgram.cs b/skyscraper8/Skyscraper/Security/AccessControl/SoftcamTestProgram.cs new file mode 100644 index 0000000..af9294c --- /dev/null +++ b/skyscraper8/Skyscraper/Security/AccessControl/SoftcamTestProgram.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using skyscraper5.Mpeg2; +using skyscraper5.Skyscraper.Scraper; +using skyscraper8.Mpeg2.PacketFilter; + +namespace skyscraper8.Skyscraper.Security.AccessControl +{ + internal class SoftcamTestProgram + { + public void Run() + { + SoftcamKeyset softcamKeyset = SoftcamKeyset.GetInstance(); + softcamKeyset.InitializeFromFile(); + byte[] bissKey = softcamKeyset.FindBissKey(2, 85); + + BissDescrambleFilter descrambler = new BissDescrambleFilter(bissKey); + + FileInfo inputFile = new FileInfo("C:\\devel\\12380h_filtered.ts"); + FileStream fileStream = inputFile.OpenRead(); + byte[] buffer = new byte[188]; + for (long i = 0; i < fileStream.Length; i += 188) + { + fileStream.Read(buffer, 0, 188); + TsPacket packet = new TsPacket(buffer); + descrambler.PassPacket(packet); + } + } + } +} diff --git a/skyscraper8/Skyscraper/Security/Cryptography/DvbCsa2.cs b/skyscraper8/Skyscraper/Security/Cryptography/DvbCsa2.cs index 43ee0ad..97a0bd2 100644 --- a/skyscraper8/Skyscraper/Security/Cryptography/DvbCsa2.cs +++ b/skyscraper8/Skyscraper/Security/Cryptography/DvbCsa2.cs @@ -14,6 +14,7 @@ namespace skyscraper8.Skyscraper.Security.Cryptography public const int KEY_BITS = 64; //!< DVB CSA-2 control words size in bits. public const int KEY_SIZE = KEY_BITS / 8; //!< DVB CSA-2 control words size in bytes. public const int BLOCK_SIZE = 8; + public const int MAX_NBLOCKS = (184 / 8); public enum EntropyMode { @@ -101,7 +102,7 @@ namespace skyscraper8.Skyscraper.Security.Cryptography return new Tuple("DVB-CSA2", BLOCK_SIZE, KEY_SIZE); } - protected bool SetKeyImpl(byte[] newKey) + public bool SetKeyImpl(byte[] newKey) { // Only one possible key size. if (newKey.Length != KEY_SIZE) @@ -132,16 +133,134 @@ namespace skyscraper8.Skyscraper.Security.Cryptography return true; } - protected static bool EncryptImpl(Span plain, int plain_length, Span cipher, int cipher_maxsize, ref int cipher_length) + protected bool EncryptImpl(Span input, int inputLength, Span output, int size, ref int retsize) + { + if (size < inputLength) + return false; + + size = inputLength; + if (retsize != 0) + retsize = size; + + if (input != output) + { + input.CopyTo(output); + } + + Span data = output; + int nblocks = size / 8; + int rsize = size % 8; + + if (data == null || nblocks > MAX_NBLOCKS || !_init) + return false; + + if (size < 8) + return true; + + DvbStreamCipher stream_ctx; + byte[] iblock = new byte[8]; + byte[][] ib = new byte[MAX_NBLOCKS + 1][]; + for (int i = 0; i < ib.Length; i++) + ib[i] = new byte[8]; + byte[] ostream = new byte[8]; + + Array.Clear(ib[nblocks]); + for (int i = nblocks - 1; i >= 0; i--) + { + xor_8(iblock, data.Slice(8 * i), ib[i + 1]); + _block.Encipher(iblock, ib[i]); + } + + memcpy_8(data, ib[0]); + + stream_ctx = _stream; + stream_ctx.Cipher(ib[0], ostream); + + for (int i = 1; i < nblocks; i++) + { + stream_ctx.Cipher(null, ostream); + xor_8(data.Slice(8 * i), ib[i], ostream); + } + + if (rsize > 0) + { + stream_ctx.Cipher(null, ostream); + for (int i = 0; i < rsize; i++) + { + data[8 * nblocks + i] ^= ostream[i]; + } + } + + return true; + } + + public bool DecryptImpl(Span input, int inputLength, Span output, int size, ref int retsize) + { + if (size < inputLength) + return false; + + size = inputLength; + if (retsize != 0) + retsize = size; + + if (input != output) + input.CopyTo(output); + + Span data = output; + int nblocks = size / 8; + int rsize = size % 8; + + if (data == null || nblocks > MAX_NBLOCKS || !_init) + return false; + + if (size < 8) + return true; + + DvbStreamCipher stream_ctx; + byte[] ostream = new byte[8]; + byte[] ib = new byte[8]; + byte[] oblock = new byte[8]; + + stream_ctx = _stream; + stream_ctx.Cipher(data, ib); + + for (int i = 1; i < nblocks; i++) + { + _block.Decipher(ib, oblock); + stream_ctx.Cipher(null, ostream); + xor_8(ib, data.Slice(8 * i), ostream); + xor_8(data.Slice(8 * (i - 1)), ib, oblock); + } + + _block.Decipher(ib, data.Slice(8 * (nblocks - 1))); + + if (rsize > 0) + { + stream_ctx.Cipher(null, ostream); + for (int i = 0; i < rsize; i++) + { + data[8 * nblocks + 1] ^= ostream[i]; + } + } + + return true; + } + + private void xor_8(Span iblock, byte[] slice, byte[] ostream) { throw new NotImplementedException(); } - protected bool DecryptImpl(Span cipher, int cipher_length, Span plain, int plain_maxsize, ref int plain_length) + private void memcpy_8(Span data, byte[] bytes) { throw new NotImplementedException(); } - + + private void xor_8(byte[] res, Span a, byte[] b) + { + res[0] = (byte)(a[0] ^ b[0]); + } + private class DvbBlockCipher { private readonly byte[] key_perm = { @@ -235,6 +354,8 @@ namespace skyscraper8.Skyscraper.Security.Cryptography private int[] _kk; //57 items public void Init(Span key) { + _kk = new int[57]; + int i, j, k; int[] bit = new int[64]; int[] newbit = new int[64]; @@ -394,6 +515,9 @@ namespace skyscraper8.Skyscraper.Security.Cryptography public void Init(Span key) { + A = new int[11]; + B = new int[11]; + A[1] = (key[0] >> 4) & 0x0F; A[2] = (key[0] >> 0) & 0x0F; A[3] = (key[1] >> 4) & 0x0F;