Added support for loading from Softcam.key
Some checks failed
🚀 Pack skyscraper8 / make-zip (push) Failing after 1m28s

This commit is contained in:
feyris-tan 2025-12-26 14:17:53 +01:00
parent 3bc81db95e
commit cd2055a07a
12 changed files with 618 additions and 8 deletions

View File

@ -35,6 +35,7 @@ using skyscraper8.SatIp.RtspResponses;
using skyscraper8.SimpleServiceDiscoveryProtocol; using skyscraper8.SimpleServiceDiscoveryProtocol;
using skyscraper8.Skyscraper.Math; using skyscraper8.Skyscraper.Math;
using skyscraper8.Skyscraper; using skyscraper8.Skyscraper;
using skyscraper8.Skyscraper.Security.AccessControl;
[assembly: log4net.Config.XmlConfigurator(ConfigFile = "log4net.config")] [assembly: log4net.Config.XmlConfigurator(ConfigFile = "log4net.config")]
namespace skyscraper5 namespace skyscraper5
@ -91,6 +92,7 @@ namespace skyscraper5
logger.DebugFormat("I'm a {0}-bit Process.", Environment.Is64BitProcess ? 64 : 32); logger.DebugFormat("I'm a {0}-bit Process.", Environment.Is64BitProcess ? 64 : 32);
PluginManager.GetInstance(); PluginManager.GetInstance();
SoftcamKeyset.GetInstance().InitializeFromFile();
if (args.Length != 0) if (args.Length != 0)
{ {

View File

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace skyscraper8.Skyscraper.Security.AccessControl
{
public class AccessControlException : SkyscraperSecurityException
{
public AccessControlException()
{
}
public AccessControlException(string message) : base(message)
{
}
public AccessControlException(string message, Exception inner) : base(message, inner)
{
}
}
}

View File

@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace skyscraper8.Skyscraper.Security.AccessControl
{
internal class BissKey
{
public BissKey(uint bissCoordinate, byte[] keyData)
{
Coordinate = bissCoordinate;
Key = keyData;
}
public byte[] Key { get; set; }
public uint Coordinate { get; set; }
protected bool Equals(BissKey other)
{
return Coordinate == other.Coordinate;
}
public override bool Equals(object? obj)
{
if (ReferenceEquals(null, obj))
return false;
if (ReferenceEquals(this, obj))
return true;
if (obj.GetType() != this.GetType())
return false;
return Equals((BissKey)obj);
}
public override int GetHashCode()
{
return (int)Coordinate;
}
}
}

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace skyscraper8.Skyscraper.Security.AccessControl
{
internal interface MultipartKey
{
bool SetHint(string keyType, byte[] key);
}
}

View File

@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace skyscraper8.Skyscraper.Security.AccessControl
{
internal class NagravisionKey : MultipartKey
{
public NagravisionKey(uint nagravisionGroupid)
{
Group = nagravisionGroupid;
}
public uint Group { get; private set; }
public bool SetHint(string keyType, byte[] key)
{
switch (keyType)
{
case "00":
KeyA = key;
return true;
case "01":
KeyB = key;
return true;
case "M1":
Master = key;
return true;
default:
return false;
}
}
public byte[] Master { get; set; }
public byte[] KeyB { get; set; }
public byte[] KeyA { get; set; }
}
}

View File

@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace skyscraper8.Skyscraper.Security.AccessControl
{
internal class PowerVuKeyGroup
{
public uint Coordinate { get; }
public PowerVuKeyGroup(uint coordinate, ushort groupId)
{
Coordinate = coordinate;
GroupId = groupId;
}
public ushort GroupId { get; set; }
public bool SetHint(uint coordinate, uint keyType, byte[] key)
{
if ((coordinate & 0x0000ffff) == 0x0000ffff)
{
if (keyType == 0)
{
EcmA = key;
return true;
}
else if (keyType == 1)
{
EcmB = key;
return true;
}
else
{
return false;
}
}
if (_keys == null)
_keys = new List<Tuple<uint, uint, byte[]>>();
_keys.Add(new Tuple<uint, uint, byte[]>(coordinate, keyType, key));
return true;
}
public byte[] EcmB { get; set; }
public byte[] EcmA { get; set; }
private List<Tuple<uint, uint, byte[]>> _keys;
public IReadOnlyList<Tuple<uint, uint, byte[]>> Keys;
}
}

View File

@ -0,0 +1,217 @@
using log4net;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using skyscraper5.Skyscraper;
namespace skyscraper8.Skyscraper.Security.AccessControl
{
internal class SoftcamKeyset
{
private static readonly ILog logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name);
private static SoftcamKeyset _singleton;
public static SoftcamKeyset GetInstance()
{
if (_singleton == null)
{
_singleton = new SoftcamKeyset();
}
return _singleton;
}
private FileInfo TryFindSoftcamKey()
{
foreach (FileInfo fi in GetPossibleLocations())
{
if (fi.Exists)
{
return fi;
}
}
return null;
}
private const string FILE_NAME = "SoftCam.Key";
private IEnumerable<FileInfo> GetPossibleLocations()
{
FileInfo self = new FileInfo(FILE_NAME);
yield return self;
string assemblyLocation = this.GetType().Assembly.Location;
FileInfo assemblyLocationFileInfo = new FileInfo(assemblyLocation);
DirectoryInfo assemblyLocationDirectory = assemblyLocationFileInfo.Directory;
string assemblyLocationKeyfileName = Path.Combine(assemblyLocationDirectory.FullName, FILE_NAME);
yield return new FileInfo(assemblyLocationKeyfileName);
DirectoryInfo rootDirectory = self.Directory.Root;
DirectoryInfo selfDirectory = self.Directory;
do
{
selfDirectory = selfDirectory.Parent;
if (selfDirectory == null)
break;
string possibleName = Path.Combine(selfDirectory.FullName, FILE_NAME);
yield return new FileInfo(possibleName);
} while (!selfDirectory.Equals(rootDirectory));
yield return new FileInfo("C:\\FT\\SoftCam_Emu\\SoftCam.Key");
}
private int LoadSoftcamKey(FileInfo fi)
{
int result = 0;
StreamReader streamReader = fi.OpenText();
PowerVuKeyGroup currentPowerVuKeyGroup = null;
while (!streamReader.EndOfStream)
{
string line = streamReader.ReadLine();
line = line.Trim();
if (line.Contains(";"))
{
int indexOf = line.IndexOf(";");
line = line.Substring(0, indexOf);
}
if (string.IsNullOrEmpty(line))
continue;
char keyType = line[0];
string[] split = line.Split(' ');
byte[] keyData = null;
switch (keyType)
{
case '#':
case ';': //comment
break;
case '-': //unknown, perhaps seperator?
break;
case 'V': //Viaccess
uint viaccessGroup = split[1].HexToUint32BE();
string viaccessKeyType = split[2];
keyData = split[3].HexToBytes();
if (_viaccessKeys == null)
_viaccessKeys = new List<ViaccessKey>();
ViaccessKey viaccessKey = _viaccessKeys.FirstOrDefault(x => x.Group == viaccessGroup);
if (viaccessKey == null)
{
viaccessKey = new ViaccessKey(viaccessGroup);
_viaccessKeys.Add(viaccessKey);
}
if (viaccessKey.SetHint(viaccessKeyType, keyData))
result++;
break;
case 'N':
uint nagravisionGroupid = split[1].HexToUint32BE();
string nagravisionKeyType = split[2];
keyData = split[3].HexToBytes();
if (_nagravisionKeys == null)
_nagravisionKeys = new List<NagravisionKey>();
NagravisionKey nagravisionKey = _nagravisionKeys.FirstOrDefault(x => x.Group == nagravisionGroupid);
if (nagravisionKey == null)
{
nagravisionKey = new NagravisionKey(nagravisionGroupid);
_nagravisionKeys.Add(nagravisionKey);
}
if (nagravisionKey.SetHint(nagravisionKeyType, keyData))
result++;
break;
case 'T':
ushort tandbergChannelId = split[1].HexToUInt16BE();
byte tandbergStreamId = split[2].HexToUInt8();
keyData = split[3].HexToBytes();
TandbergKey tandbergKey = new TandbergKey(tandbergChannelId, tandbergStreamId, keyData);
if (tandbergKey.Valid)
{
if (_tandbergKeys == null)
_tandbergKeys = new HashSet<TandbergKey>();
_tandbergKeys.Add(tandbergKey);
result++;
}
break;
case 'P':
case 'p':
uint powerVuKeyCoordinate = split[1].HexToUint32BE();
string powerVuKeyType = split[2];
if (powerVuKeyType.Equals("GROUP"))
{
ushort groupId = split[3].HexToUInt16BE();
PowerVuKeyGroup powerVuGroup = new PowerVuKeyGroup(powerVuKeyCoordinate, groupId);
currentPowerVuKeyGroup = powerVuGroup;
if (_powerVuGroups == null)
_powerVuGroups = new List<PowerVuKeyGroup>();
_powerVuGroups.Add(currentPowerVuKeyGroup);
}
else
{
keyData = split[3].HexToBytes();
currentPowerVuKeyGroup.SetHint(powerVuKeyCoordinate, powerVuKeyType.HexToUint32LE(), keyData);
result++;
}
break;
case 'F':
uint bissCoordinate = split[1].HexToUint32BE();
uint bissFixed = split[2].HexToUint32BE();
keyData = split[3].HexToBytes();
if (bissFixed != 0)
{
logger.ErrorFormat("I don't understand this key: {0}", line);
break;
}
if (_bissKeys == null)
_bissKeys = new HashSet<BissKey>();
if (_bissKeys.Add(new BissKey(bissCoordinate, keyData)))
result++;
else
logger.ErrorFormat("Duplicate BISS Key for {0}", split[1]);
break;
default:
logger.ErrorFormat("Unknown Key Type \"{0}\". Abort loading key set.",keyType);
return result;
}
}
return result;
}
private bool doneFileInitalization;
public void InitializeFromFile()
{
if (doneFileInitalization)
{
throw new AccessControlException("Softcam.key Keyset already initalized.");
}
doneFileInitalization = true;
FileInfo keyLocation = TryFindSoftcamKey();
if (keyLocation != null)
{
logger.InfoFormat("Loading keys from: {0}", keyLocation.FullName);
int numKeysLoaded = LoadSoftcamKey(keyLocation);
logger.InfoFormat("I suppose you are legally entitled to use those {0} keys I just loaded, You are, right?",numKeysLoaded);
}
}
private List<ViaccessKey> _viaccessKeys;
public IReadOnlyList<ViaccessKey> ViaccessKeys => _viaccessKeys;
private List<NagravisionKey> _nagravisionKeys;
public IReadOnlyList<NagravisionKey> NagravisionKeys => _nagravisionKeys;
private HashSet<TandbergKey> _tandbergKeys;
public IReadOnlySet<TandbergKey> TandbergKeys => _tandbergKeys;
private List<PowerVuKeyGroup> _powerVuGroups;
public IReadOnlyList<PowerVuKeyGroup> PowerVuGroups => _powerVuGroups;
private HashSet<BissKey> _bissKeys;
public IReadOnlySet<BissKey> BissKeys => _bissKeys;
}
}

View File

@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using skyscraper5.Skyscraper;
namespace skyscraper8.Skyscraper.Security.AccessControl
{
internal class TandbergKey : Validatable
{
public ushort ChannelId { get; }
public byte StreamId { get; }
public byte[] Key { get; }
public TandbergKey(ushort channelId, byte streamId, byte[] key)
{
ChannelId = channelId;
StreamId = streamId;
Key = key;
if (key == null)
Valid = false;
else if (key.Length != 8)
Valid = false;
else
Valid = true;
}
protected bool Equals(TandbergKey other)
{
ulong ourKey = BitConverter.ToUInt64(Key, 0);
ulong theirKey = BitConverter.ToUInt64(other.Key, 0);
return ChannelId == other.ChannelId && StreamId == other.StreamId && ourKey == theirKey;
}
public override bool Equals(object? obj)
{
if (ReferenceEquals(null, obj))
return false;
if (ReferenceEquals(this, obj))
return true;
if (obj.GetType() != this.GetType())
return false;
return Equals((TandbergKey)obj);
}
public override int GetHashCode()
{
ulong numericKey = BitConverter.ToUInt64(Key, 0);
return HashCode.Combine(ChannelId, StreamId, numericKey);
}
}
}

View File

@ -0,0 +1,78 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using skyscraper5.Skyscraper;
namespace skyscraper8.Skyscraper.Security.AccessControl
{
internal class ViaccessKey : MultipartKey
{
public ViaccessKey(uint viaccessGroup)
{
Group = viaccessGroup;
}
public uint Group { get; set; }
public bool SetHint(string viaccessKeyType, byte[] keyData)
{
viaccessKeyType = viaccessKeyType.ToUpper();
switch (viaccessKeyType)
{
case "00":
case "01":
case "02":
case "03":
case "04":
case "05":
case "06":
case "07":
case "08":
case "09":
case "0A":
case "0B":
case "0C":
case "0D":
case "0E":
case "0F":
uint keyNUmber = viaccessKeyType.HexToUint32BE();
keyNUmber >>= 24;
if (Keyset == null)
Keyset = new byte[16][];
Keyset[keyNUmber] = keyData;
return true;
case "D1":
Des1Key = keyData;
return true;
case "X1":
XorArray = keyData;
return true;
case "P1":
PermArray = keyData;
return true;
case "C1":
ChainArray = keyData;
return true;
case "T1":
TransformTable = keyData;
return true;
default:
return false;
}
}
public byte[] TransformTable { get; set; }
public byte[] ChainArray { get; set; }
public byte[] PermArray { get; set; }
public byte[] XorArray { get; set; }
public byte[] Des1Key { get; set; }
public byte[][] Keyset { get; private set; }
}
}

View File

@ -24,7 +24,6 @@ namespace skyscraper8.Skyscraper.Security.Cryptography
public DvbCsa2(EntropyMode mode = EntropyMode.REDUCE_ENTROPY) public DvbCsa2(EntropyMode mode = EntropyMode.REDUCE_ENTROPY)
{ {
SetEntropyMode(mode); SetEntropyMode(mode);
canProcessInPlace(true);
} }
static readonly int[] sbox1 = { static readonly int[] sbox1 = {
@ -75,13 +74,7 @@ namespace skyscraper8.Skyscraper.Security.Cryptography
1,0,3,3,0,1,1,2, 1,0,3,3,0,1,1,2,
2,3,1,0,2,3,0,2 2,3,1,0,2,3,0,2
}; };
private void canProcessInPlace(bool b)
{
throw new NotImplementedException();
}
public void SetEntropyMode(EntropyMode mode) public void SetEntropyMode(EntropyMode mode)
{ {
_mode = mode; _mode = mode;

View File

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace skyscraper8.Skyscraper.Security
{
public class SkyscraperSecurityException : SkyscraperCoreException
{
public SkyscraperSecurityException()
{
}
public SkyscraperSecurityException(string message) : base(message)
{
}
public SkyscraperSecurityException(string message, Exception inner) : base(message, inner)
{
}
}
}

View File

@ -37,5 +37,73 @@ namespace skyscraper5.Skyscraper
return new string(charArray); return new string(charArray);
} }
public static byte[] HexToBytes(this string hex)
{
return Enumerable.Range(0, hex.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
.ToArray();
}
public static uint HexToUint32BE(this string hex)
{
byte[] hexToBytes = hex.HexToBytes();
if (hexToBytes.Length < 4)
{
byte[] better = new byte[4];
Array.Copy(hexToBytes, 0, better, 0, hexToBytes.Length);
hexToBytes = better;
}
(hexToBytes[3], hexToBytes[2], hexToBytes[1], hexToBytes[0]) = (hexToBytes[0], hexToBytes[1], hexToBytes[2], hexToBytes[3]);
return BitConverter.ToUInt32(hexToBytes, 0);
}
public static uint HexToUint32LE(this string hex)
{
byte[] hexToBytes = hex.HexToBytes();
if (hexToBytes.Length < 4)
{
byte[] better = new byte[4];
Array.Copy(hexToBytes, 0, better, 0, hexToBytes.Length);
hexToBytes = better;
}
return BitConverter.ToUInt32(hexToBytes, 0);
}
public static ushort HexToUInt16LE(this string hex)
{
byte[] hexToBytes = hex.HexToBytes();
if (hexToBytes.Length < 2)
{
byte[] better = new byte[2];
Array.Copy(hexToBytes, 0, better, 0, hexToBytes.Length);
hexToBytes = better;
}
return BitConverter.ToUInt16(hexToBytes, 0);
}
public static ushort HexToUInt16BE(this string hex)
{
byte[] hexToBytes = hex.HexToBytes();
if (hexToBytes.Length < 2)
{
byte[] better = new byte[2];
Array.Copy(hexToBytes, 0, better, 0, hexToBytes.Length);
hexToBytes = better;
}
(hexToBytes[1], hexToBytes[0]) = (hexToBytes[0], hexToBytes[1]);
return BitConverter.ToUInt16(hexToBytes, 0);
}
public static byte HexToUInt8(this string hex)
{
byte[] hexToBytes = hex.HexToBytes();
return hexToBytes[0];
}
} }
} }