Skip to content

Commit 55a38c1

Browse files
committed
Split off key extraction into own application
1 parent f414e58 commit 55a38c1

File tree

10 files changed

+309
-102
lines changed

10 files changed

+309
-102
lines changed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>netcoreapp2.0</TargetFramework>
6+
</PropertyGroup>
7+
<ItemGroup>
8+
<PackageReference Include="NDesk.Options.Patched" Version="0.3.1" />
9+
</ItemGroup>
10+
<ItemGroup>
11+
<ProjectReference Include="..\LibXboxOne\LibXboxOne.csproj" />
12+
</ItemGroup>
13+
</Project>

DurangoKeyExtractor/Extractor.cs

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using LibXboxOne;
5+
using LibXboxOne.Keystore;
6+
7+
namespace DurangoKeyExtractor
8+
{
9+
public class KeyExtractor
10+
{
11+
public readonly Dictionary<string,byte[]> FoundCikKeys;
12+
public readonly Dictionary<string,byte[]> FoundOdkKeys;
13+
public readonly Dictionary<string,byte[]> FoundXvdSignKeys;
14+
public string FilePath { get; private set; }
15+
16+
public KeyExtractor(string filePath)
17+
{
18+
if (!File.Exists(filePath))
19+
throw new InvalidOperationException($"File {filePath} does not exist");
20+
21+
FilePath = filePath;
22+
FoundCikKeys = new Dictionary<string, byte[]>();
23+
FoundOdkKeys = new Dictionary<string, byte[]>();
24+
FoundXvdSignKeys = new Dictionary<string, byte[]>();
25+
}
26+
27+
void StoreKey(string keyName, IKeyEntry keyEntry, byte[] keyData)
28+
{
29+
30+
switch (keyEntry.KeyType)
31+
{
32+
case KeyType.CikKey:
33+
// Assemble GUID + keyData blob
34+
var assembledKey = new byte[0x30];
35+
Array.Copy(((CikKeyEntry)keyEntry).KeyId.ToByteArray(), 0, assembledKey, 0, 16);
36+
Array.Copy(keyData, 0, assembledKey, 16, 32);
37+
38+
FoundCikKeys[keyName] = assembledKey;
39+
break;
40+
case KeyType.OdkKey:
41+
FoundOdkKeys[keyName] = keyData;
42+
break;
43+
case KeyType.XvdSigningKey:
44+
FoundXvdSignKeys[keyName] = keyData;
45+
break;
46+
default:
47+
throw new InvalidDataException("Invalid KeyType provided");
48+
}
49+
}
50+
51+
public int PullKeysFromFile()
52+
{
53+
var exeData = File.ReadAllBytes(FilePath);
54+
55+
int foundCount = 0;
56+
for (int i = 0; i < exeData.Length - 32; i += 8)
57+
{
58+
byte[] hash32 = HashUtils.ComputeSha256(exeData, i, 32);
59+
foreach(var kvp in DurangoKeys.KnownKeys)
60+
{
61+
string keyName = kvp.Key;
62+
IKeyEntry keyEntry = kvp.Value;
63+
64+
if ((keyEntry.DataSize == 32 && hash32.IsEqualTo(keyEntry.SHA256Hash)) ||
65+
(keyEntry.DataSize != 32 && keyEntry.DataSize <= (exeData.Length - i) &&
66+
keyEntry.SHA256Hash.IsEqualTo(HashUtils.ComputeSha256(exeData, i, keyEntry.DataSize))))
67+
{
68+
Console.WriteLine($"Found {keyEntry.KeyType} \"{keyName}\" at offset 0x{i:X}");
69+
70+
byte[] keyData = new byte[keyEntry.DataSize];
71+
Array.Copy(exeData, i, keyData, 0, keyData.Length);
72+
StoreKey(keyName, keyEntry, keyData);
73+
foundCount++;
74+
}
75+
}
76+
}
77+
78+
return foundCount;
79+
}
80+
81+
public bool SaveFoundKeys(string destinationDirectory)
82+
{
83+
if (!Directory.Exists(destinationDirectory))
84+
{
85+
Directory.CreateDirectory(destinationDirectory);
86+
}
87+
88+
foreach(var entry in FoundCikKeys)
89+
{
90+
var path = Path.Combine(destinationDirectory, $"CIK_{entry.Key}.bin");
91+
File.WriteAllBytes(path, entry.Value);
92+
}
93+
94+
foreach(var entry in FoundOdkKeys)
95+
{
96+
var path = Path.Combine(destinationDirectory, $"ODK_{entry.Key}.bin");
97+
File.WriteAllBytes(path, entry.Value);
98+
}
99+
100+
foreach(var entry in FoundXvdSignKeys)
101+
{
102+
var path = Path.Combine(destinationDirectory, $"XVDSIGN_{entry.Key}.bin");
103+
File.WriteAllBytes(path, entry.Value);
104+
}
105+
106+
return true;
107+
}
108+
}
109+
}

DurangoKeyExtractor/Program.cs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
using System;
2+
using NDesk.Options;
3+
using LibXboxOne;
4+
using System.IO;
5+
6+
namespace DurangoKeyExtractor
7+
{
8+
class Program
9+
{
10+
static void Main(string[] args)
11+
{
12+
const string fmt = " ";
13+
14+
var printHelp = false;
15+
var outputFolder = String.Empty;
16+
17+
var p = new OptionSet {
18+
{ "h|?|help", v => printHelp = v != null },
19+
{ "o|output", v => outputFolder = v}
20+
};
21+
22+
var extraArgs = p.Parse(args);
23+
24+
Console.WriteLine("durangokeyextractor 0.1: Durango key extractor");
25+
26+
if (printHelp || extraArgs.Count <= 0)
27+
{
28+
Console.WriteLine("Usage : durangokeyextractor.exe [file path]");
29+
Console.WriteLine();
30+
Console.WriteLine("Parameters:");
31+
Console.WriteLine(fmt + "-h (-help) - print program usage");
32+
Console.WriteLine(fmt + "-o (-output) - Output folder to store extracted keys");
33+
Console.WriteLine();
34+
return;
35+
}
36+
37+
if (outputFolder == String.Empty)
38+
{
39+
outputFolder = Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "extracted");
40+
}
41+
42+
var filePath = extraArgs[0];
43+
KeyExtractor extractor;
44+
45+
try
46+
{
47+
extractor = new KeyExtractor(filePath);
48+
}
49+
catch (Exception e)
50+
{
51+
Console.WriteLine($"Error setting up KeyExtractor: {e.Message}");
52+
return;
53+
}
54+
55+
Console.WriteLine($"Scanning {filePath} for known keys...");
56+
int foundCount = extractor.PullKeysFromFile();
57+
if (foundCount == 0)
58+
{
59+
Console.WriteLine("No keys found :(");
60+
return;
61+
}
62+
63+
Console.WriteLine($"Found {foundCount} keys :)");
64+
Console.WriteLine($"Saving keys to \"{outputFolder}\"...");
65+
extractor.SaveFoundKeys(outputFolder);
66+
}
67+
}
68+
}

LibXboxOne.Tests/XvdFileTests.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ public void Unsigned_XVD_Encrypt_Test()
233233
Assert.True(generatedHash.IsEqualTo(expectedHash));
234234
}
235235

236+
/*
236237
[Fact(Skip="Relies on xvd data blob")]
237238
public void XvdSign_Key_Extract()
238239
{
@@ -266,5 +267,6 @@ public void XvdSign_Key_Extract()
266267
XvdFile.OdkKeyLoaded = false;
267268
XvdFile.SignKeyLoaded = false;
268269
}
270+
*/
269271
}
270272
}

LibXboxOne/Keystore/DurangoKeys.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using System;
2+
using System.Collections.Generic;
3+
4+
namespace LibXboxOne.Keystore
5+
{
6+
public static class DurangoKeys
7+
{
8+
public static readonly Dictionary<string,IKeyEntry> KnownKeys = new Dictionary<string, IKeyEntry>(){
9+
// Cik keys
10+
{"TestCIK", new CikKeyEntry(new Guid("33EC8436-5A0E-4F0D-B1CE-3F29C3955039"), sha256Hash: "6786C11B788ED5CCE3C7695425CB82970347180650893D1B5613B2EFB33F9F4E", dataSize: 0x20)},
11+
{"Unknown0CIK", new CikKeyEntry(new Guid("F0522B7C-7FC1-D806-43E3-68A5DAAB06DA"), sha256Hash: "B767CE5F83224375E663A1E01044EA05E8022C033D96BED952475D87F0566642", dataSize: 0x20)},
12+
13+
// Odk keys
14+
{"RedODK", new OdkKeyEntry(2, sha256Hash: "CA37132DFB4B811506AE4DC45F45970FED8FE5E58C1BACB259F1B96145B0EBC6", dataSize: 0x20)},
15+
16+
// Xvd signing keys
17+
{"RedXvdPrivateKey", new XvdSignKeyEntry(4096, privateKey: true, sha256Hash: "8E2B60377006D87EE850334C42FC200081386A838C65D96D1EA52032AA9628C5", dataSize: 0x91B)},
18+
{"GreenXvdPublicKey", new XvdSignKeyEntry(4096, privateKey: false, sha256Hash: "618C5FB1193040AF8BC1C0199B850B4B5C42E43CE388129180284E4EF0B18082", dataSize: 0x21B)},
19+
{"GreenGamesPublicKey", new XvdSignKeyEntry(4096, privateKey: false, sha256Hash: "183F0AE05431E4AD91554E88946967C872997227DBE6C85116F5FD2FD2D1229E", dataSize: 0x21B)},
20+
};
21+
}
22+
}

LibXboxOne/Keystore/KeyEntry.cs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
using System;
2+
3+
namespace LibXboxOne.Keystore
4+
{
5+
public interface IKeyEntry
6+
{
7+
int DataSize { get; }
8+
byte[] SHA256Hash { get; }
9+
KeyType KeyType { get; }
10+
}
11+
12+
public class CikKeyEntry : IKeyEntry
13+
{
14+
public Guid KeyId { get; }
15+
public byte[] SHA256Hash { get; }
16+
public int DataSize { get; }
17+
public KeyType KeyType => KeyType.CikKey;
18+
19+
public CikKeyEntry(Guid keyId, string sha256Hash, int dataSize)
20+
{
21+
KeyId = keyId;
22+
SHA256Hash = sha256Hash.ToBytes();
23+
DataSize = dataSize;
24+
if (SHA256Hash.Length != 0x20)
25+
throw new DataMisalignedException("Invalid length for SHA256Hash");
26+
}
27+
}
28+
29+
public class OdkKeyEntry : IKeyEntry
30+
{
31+
public uint KeyId { get; }
32+
public byte[] SHA256Hash { get; }
33+
public int DataSize { get; }
34+
public KeyType KeyType => KeyType.OdkKey;
35+
36+
public OdkKeyEntry(uint keyId, string sha256Hash, int dataSize)
37+
{
38+
KeyId = keyId;
39+
SHA256Hash = sha256Hash.ToBytes();
40+
DataSize = dataSize;
41+
if (SHA256Hash.Length != 0x20)
42+
throw new DataMisalignedException("Invalid length for SHA256Hash");
43+
}
44+
}
45+
46+
public class XvdSignKeyEntry : IKeyEntry
47+
{
48+
public int KeyStrengthBits { get; }
49+
public bool IsPrivate { get; }
50+
public byte[] SHA256Hash { get; }
51+
public int DataSize { get; }
52+
public KeyType KeyType => KeyType.XvdSigningKey;
53+
54+
public XvdSignKeyEntry(int keyStrengthBits, bool privateKey, string sha256Hash, int dataSize)
55+
{
56+
KeyStrengthBits = keyStrengthBits;
57+
IsPrivate = privateKey;
58+
SHA256Hash = sha256Hash.ToBytes();
59+
DataSize = dataSize;
60+
if (SHA256Hash.Length != 0x20)
61+
throw new DataMisalignedException("Invalid length for SHA256Hash");
62+
}
63+
}
64+
}

LibXboxOne/Keystore/KeyType.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using System;
2+
3+
namespace LibXboxOne.Keystore
4+
{
5+
public enum KeyType
6+
{
7+
XvdSigningKey,
8+
OdkKey, // Offline Distribution Key, AES256 (32 bytes) key
9+
CikKey // Content Instance Key, Guid (16 bytes) + AES256 (32 bytes) key
10+
}
11+
}

LibXboxOne/Shared.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,16 @@ public static string ToHexString(this ushort[] array)
219219
return array.Aggregate("", (current, b) => current + "0x" + (b.ToString("X4") + " "));
220220
}
221221

222+
public static byte[] ToBytes(this string hexString)
223+
{
224+
hexString = hexString.Replace(" ", "");
225+
226+
byte[] retval = new byte[hexString.Length / 2];
227+
for (int i = 0; i < hexString.Length; i += 2)
228+
retval[i / 2] = Convert.ToByte(hexString.Substring(i, 2), 16);
229+
return retval;
230+
}
231+
222232
public static bool IsArrayEmpty(this byte[] bytes)
223233
{
224234
return bytes.All(b => b == 0);

0 commit comments

Comments
 (0)