mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Embed key sources in the library
Embedded keys are generated at build-time from the IncludedKeys.txt file under the build directory. A separate codegen build project was created because generating the embedded keys requires the same LibHac that is being built. _buildCodeGen.csproj is located under CodeGen because NUKE doesn't like having two build projects in the same directory.
This commit is contained in:
parent
770406e9c2
commit
bac541947f
21 changed files with 1342 additions and 594 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -267,4 +267,7 @@ global.json
|
||||||
!tests/LibHac.Tests/CryptoTests/TestVectors/*
|
!tests/LibHac.Tests/CryptoTests/TestVectors/*
|
||||||
**/DisasmoBin/
|
**/DisasmoBin/
|
||||||
|
|
||||||
|
# Files generated at build time
|
||||||
|
ResultNameResolver.Generated.cs
|
||||||
|
DefaultKeySet.Generated.cs
|
||||||
ResultNameResolver.Generated.cs
|
ResultNameResolver.Generated.cs
|
|
@ -11,6 +11,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibHac.Tests", "tests\LibHa
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "_build", "build\_build.csproj", "{C7150117-90B8-4083-8141-BBC35C9F44F6}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "_build", "build\_build.csproj", "{C7150117-90B8-4083-8141-BBC35C9F44F6}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "_buildCodeGen", "build\CodeGen\_buildCodeGen.csproj", "{93B175E1-F12F-4A8C-85CA-CAC74691102A}"
|
||||||
|
ProjectSection(ProjectDependencies) = postProject
|
||||||
|
{FFCA6C31-D9D4-4ED8-A06D-0CC6B94422B8} = {FFCA6C31-D9D4-4ED8-A06D-0CC6B94422B8}
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
@ -31,6 +36,8 @@ Global
|
||||||
{679C89BD-5FDF-4CC2-9129-ABABD759035B}.Release|Any CPU.Build.0 = Release|Any CPU
|
{679C89BD-5FDF-4CC2-9129-ABABD759035B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{C7150117-90B8-4083-8141-BBC35C9F44F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{C7150117-90B8-4083-8141-BBC35C9F44F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{C7150117-90B8-4083-8141-BBC35C9F44F6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{C7150117-90B8-4083-8141-BBC35C9F44F6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{93B175E1-F12F-4A8C-85CA-CAC74691102A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{93B175E1-F12F-4A8C-85CA-CAC74691102A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|
|
@ -8,7 +8,7 @@ using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using ICSharpCode.SharpZipLib.Zip;
|
using ICSharpCode.SharpZipLib.Zip;
|
||||||
using LibHacBuild.CodeGen;
|
using LibHacBuild.CodeGen.Stage1;
|
||||||
using Nuke.Common;
|
using Nuke.Common;
|
||||||
using Nuke.Common.CI.AppVeyor;
|
using Nuke.Common.CI.AppVeyor;
|
||||||
using Nuke.Common.Git;
|
using Nuke.Common.Git;
|
||||||
|
@ -54,6 +54,8 @@ namespace LibHacBuild
|
||||||
Project LibHacTestProject => _solution.GetProject("LibHac.Tests").NotNull();
|
Project LibHacTestProject => _solution.GetProject("LibHac.Tests").NotNull();
|
||||||
Project HactoolnetProject => _solution.GetProject("hactoolnet").NotNull();
|
Project HactoolnetProject => _solution.GetProject("hactoolnet").NotNull();
|
||||||
|
|
||||||
|
Project CodeGenProject => _solution.GetProject("_buildCodeGen").NotNull();
|
||||||
|
|
||||||
private bool HasGitDir { get; set; }
|
private bool HasGitDir { get; set; }
|
||||||
|
|
||||||
private string NativeRuntime { get; set; }
|
private string NativeRuntime { get; set; }
|
||||||
|
@ -196,6 +198,7 @@ namespace LibHacBuild
|
||||||
.Executes(() =>
|
.Executes(() =>
|
||||||
{
|
{
|
||||||
ResultCodeGen.Run();
|
ResultCodeGen.Run();
|
||||||
|
RunCodegenStage2();
|
||||||
});
|
});
|
||||||
|
|
||||||
Target Compile => _ => _
|
Target Compile => _ => _
|
||||||
|
@ -668,5 +671,24 @@ namespace LibHacBuild
|
||||||
{
|
{
|
||||||
return XmlTasks.XmlPeekSingle(LibHacProject.Path, "/Project/PropertyGroup/VersionPrefix", null);
|
return XmlTasks.XmlPeekSingle(LibHacProject.Path, "/Project/PropertyGroup/VersionPrefix", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void RunCodegenStage2()
|
||||||
|
{
|
||||||
|
Logger.Normal("\nBuilding stage 2 codegen project.");
|
||||||
|
|
||||||
|
DotNetRunSettings settings = new DotNetRunSettings()
|
||||||
|
.SetProjectFile(CodeGenProject.Path);
|
||||||
|
// .SetLogOutput(false);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
DotNetRun(settings);
|
||||||
|
Logger.Normal();
|
||||||
|
}
|
||||||
|
catch (ProcessException)
|
||||||
|
{
|
||||||
|
Logger.Error("\nError running stage 2 codegen. Skipping...\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
100
build/CodeGen/Common.cs
Normal file
100
build/CodeGen/Common.cs
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using Nuke.Common;
|
||||||
|
|
||||||
|
namespace LibHacBuild.CodeGen
|
||||||
|
{
|
||||||
|
public static class Common
|
||||||
|
{
|
||||||
|
public static string GetHeader()
|
||||||
|
{
|
||||||
|
string nl = Environment.NewLine;
|
||||||
|
return
|
||||||
|
"//-----------------------------------------------------------------------------" + nl +
|
||||||
|
"// This file was automatically generated." + nl +
|
||||||
|
"// Changes to this file will be lost when the file is regenerated." + nl +
|
||||||
|
"//" + nl +
|
||||||
|
"// To change this file, modify /build/CodeGen/results.csv at the root of this" + nl +
|
||||||
|
"// repo and run the build script." + nl +
|
||||||
|
"//" + nl +
|
||||||
|
"// The script can be run with the \"codegen\" option to run only the" + nl +
|
||||||
|
"// code generation portion of the build." + nl +
|
||||||
|
"//-----------------------------------------------------------------------------";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the file only if it has changed
|
||||||
|
// Preserve the UTF-8 BOM usage if the file already exists
|
||||||
|
public static void WriteOutput(string relativePath, string text)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(relativePath))
|
||||||
|
return;
|
||||||
|
|
||||||
|
string rootPath = FindProjectDirectory();
|
||||||
|
string fullPath = Path.Combine(rootPath, relativePath);
|
||||||
|
|
||||||
|
// Default is true because Visual Studio saves .cs files with the BOM by default
|
||||||
|
bool hasBom = true;
|
||||||
|
byte[] bom = Encoding.UTF8.GetPreamble();
|
||||||
|
byte[] oldFile = null;
|
||||||
|
|
||||||
|
if (File.Exists(fullPath))
|
||||||
|
{
|
||||||
|
oldFile = File.ReadAllBytes(fullPath);
|
||||||
|
|
||||||
|
if (oldFile.Length >= 3)
|
||||||
|
hasBom = oldFile.AsSpan(0, 3).SequenceEqual(bom);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make line endings the same on Windows and Unix
|
||||||
|
if (Environment.NewLine == "\n")
|
||||||
|
{
|
||||||
|
text = text.Replace("\n", "\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] newFile = (hasBom ? bom : new byte[0]).Concat(Encoding.UTF8.GetBytes(text)).ToArray();
|
||||||
|
|
||||||
|
if (oldFile?.SequenceEqual(newFile) == true)
|
||||||
|
{
|
||||||
|
Logger.Normal($"{relativePath} is already up-to-date");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.Normal($"Generated file {relativePath}");
|
||||||
|
File.WriteAllBytes(fullPath, newFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Stream GetResource(string name)
|
||||||
|
{
|
||||||
|
var assembly = Assembly.GetExecutingAssembly();
|
||||||
|
string path = $"LibHacBuild.CodeGen.{name}";
|
||||||
|
|
||||||
|
Stream stream = assembly.GetManifestResourceStream(path);
|
||||||
|
if (stream == null) throw new FileNotFoundException($"Resource {path} was not found.");
|
||||||
|
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string FindProjectDirectory()
|
||||||
|
{
|
||||||
|
string currentDir = Environment.CurrentDirectory;
|
||||||
|
|
||||||
|
while (currentDir != null)
|
||||||
|
{
|
||||||
|
if (File.Exists(Path.Combine(currentDir, "LibHac.sln")))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentDir = Path.GetDirectoryName(currentDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentDir == null)
|
||||||
|
throw new DirectoryNotFoundException("Unable to find project directory.");
|
||||||
|
|
||||||
|
return Path.Combine(currentDir, "src");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
56
build/CodeGen/IncludedKeys.txt
Normal file
56
build/CodeGen/IncludedKeys.txt
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
keyblob_mac_key_source = 59C7FB6FBE9BBE87656B15C0537336A5
|
||||||
|
keyblob_key_source_00 = DF206F594454EFDC7074483B0DED9FD3
|
||||||
|
keyblob_key_source_01 = 0C25615D684CEB421C2379EA822512AC
|
||||||
|
keyblob_key_source_02 = 337685EE884AAE0AC28AFD7D63C0433B
|
||||||
|
keyblob_key_source_03 = 2D1F4880EDECED3E3CF248B5657DF7BE
|
||||||
|
keyblob_key_source_04 = BB5A01F988AFF5FC6CFF079E133C3980
|
||||||
|
keyblob_key_source_05 = D8CCE1266A353FCC20F32D3B517DE9C0
|
||||||
|
|
||||||
|
master_kek_source_06 = 374B772959B4043081F6E58C6D36179A
|
||||||
|
master_kek_source_07 = 9A3EA9ABFD56461C9BF6487F5CFA095C
|
||||||
|
master_kek_source_08 = DEDCE339308816F8AE97ADEC642D4141
|
||||||
|
master_kek_source_09 = 1AEC11822B32387A2BEDBA01477E3B67
|
||||||
|
master_kek_source_0a = 303F027ED838ECD7932534B530EBCA7A
|
||||||
|
|
||||||
|
mariko_master_kek_source_06 = 1E80B8173EC060AA11BE1A4AA66FE4AE
|
||||||
|
mariko_master_kek_source_07 = 940867BD0A00388411D31ADBDD8DF18A
|
||||||
|
mariko_master_kek_source_08 = 5C24E3B8B4F700C23CFD0ACE13C3DC23
|
||||||
|
mariko_master_kek_source_09 = 8669F00987C805AEB57B4874DE62A613
|
||||||
|
mariko_master_kek_source_0a = 0E440CEDB436C03FAA1DAEBF62B10982
|
||||||
|
|
||||||
|
mariko_master_kek_source_dev_0a = F937CF9ABD86BBA99C9E03C4FCBC3BCE
|
||||||
|
|
||||||
|
master_key_source = D8A2410AC6C59001C61D6A267C513F3C
|
||||||
|
|
||||||
|
package2_key_source = FB8B6A9C7900C849EFD24D854D30A0C7
|
||||||
|
|
||||||
|
bis_kek_source = 34C1A0C48258F8B4FA9E5E6ADAFC7E4F
|
||||||
|
bis_key_source_00 = F83F386E2CD2CA32A89AB9AA29BFC7487D92B03AA8BFDEE1A74C3B6E35CB7106
|
||||||
|
bis_key_source_01 = 41003049DDCCC065647A7EB41EED9C5F44424EDAB49DFCD98777249ADC9F7CA4
|
||||||
|
bis_key_source_02 = 52C2E9EB09E3EE2932A10C1FB6A0926C4D12E14B2A474C1C09CB0359F015F4E4
|
||||||
|
bis_key_source_03 = 52C2E9EB09E3EE2932A10C1FB6A0926C4D12E14B2A474C1C09CB0359F015F4E4
|
||||||
|
|
||||||
|
per_console_key_source = 4F025F0EB66D110EDC327D4186C2F478
|
||||||
|
retail_specific_aes_key_source = E2D6B87A119CB880E822888A46FBA195
|
||||||
|
aes_kek_generation_source = 4D870986C45D20722FBA1053DA92E8A9
|
||||||
|
aes_key_generation_source = 89615EE05C31B6805FE58F3DA24F7AA8
|
||||||
|
titlekek_source = 1EDC7B3B60E6B4D878B81715985E629B
|
||||||
|
|
||||||
|
header_kek_source = 1F12913A4ACBF00D4CDE3AF6D523882A
|
||||||
|
header_key_source = 5A3ED84FDEC0D82631F7E25D197BF5D01C9B7BFAF628183D71F64D73F150B9D2
|
||||||
|
|
||||||
|
key_area_key_application_source = 7F59971E629F36A13098066F2144C30D
|
||||||
|
key_area_key_ocean_source = 327D36085AD1758DAB4E6FBAA555D882
|
||||||
|
key_area_key_system_source = 8745F1BBA6BE79647D048BA67B5FDA4A
|
||||||
|
|
||||||
|
save_mac_kek_source = D89C236EC9124E43C82B038743F9CF1B
|
||||||
|
save_mac_key_source_00 = E4CD3D4AD50F742845A487E5A063EA1F
|
||||||
|
save_mac_key_source_01 = EC249895656ADF4AA066B9880AC82C4C
|
||||||
|
|
||||||
|
save_mac_sd_card_kek_source = 0489EF5D326E1A59C4B7AB8C367AAB17
|
||||||
|
save_mac_sd_card_key_source = 6F645947C56146F9FFA045D595332918
|
||||||
|
|
||||||
|
sd_card_kek_source = 88358D9C629BA1A00147DBE0621B5432
|
||||||
|
sd_card_save_key_source = 2449B722726703A81965E6E3EA582FDD9A951517B16E8F7F1F68263152EA296A
|
||||||
|
sd_card_nca_key_source = 5841A284935B56278B8E1FC518E99F2B67C793F0F24FDED075495DCA006D99C2
|
||||||
|
sd_card_custom_storage_key_source = 370C345E12E4CEFE21B58E64DB52AF354F2CA5A3FC999A47C03EE004485B2FD0
|
|
@ -4,15 +4,14 @@ using System.Diagnostics;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using CsvHelper;
|
using CsvHelper;
|
||||||
using CsvHelper.Configuration;
|
using CsvHelper.Configuration;
|
||||||
using Nuke.Common;
|
using static LibHacBuild.CodeGen.Common;
|
||||||
|
|
||||||
namespace LibHacBuild.CodeGen
|
namespace LibHacBuild.CodeGen.Stage1
|
||||||
{
|
{
|
||||||
public static class ResultCodeGen
|
public static class ResultCodeGen
|
||||||
{
|
{
|
||||||
|
@ -282,63 +281,6 @@ namespace LibHacBuild.CodeGen
|
||||||
return doc;
|
return doc;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetHeader()
|
|
||||||
{
|
|
||||||
string nl = Environment.NewLine;
|
|
||||||
return
|
|
||||||
"//-----------------------------------------------------------------------------" + nl +
|
|
||||||
"// This file was automatically generated." + nl +
|
|
||||||
"// Changes to this file will be lost when the file is regenerated." + nl +
|
|
||||||
"//" + nl +
|
|
||||||
"// To change this file, modify /build/CodeGen/results.csv at the root of this" + nl +
|
|
||||||
"// repo and run the build script." + nl +
|
|
||||||
"//" + nl +
|
|
||||||
"// The script can be run with the \"codegen\" option to run only the" + nl +
|
|
||||||
"// code generation portion of the build." + nl +
|
|
||||||
"//-----------------------------------------------------------------------------";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write the file only if it has changed
|
|
||||||
// Preserve the UTF-8 BOM usage if the file already exists
|
|
||||||
private static void WriteOutput(string relativePath, string text)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(relativePath))
|
|
||||||
return;
|
|
||||||
|
|
||||||
string rootPath = FindProjectDirectory();
|
|
||||||
string fullPath = Path.Combine(rootPath, relativePath);
|
|
||||||
|
|
||||||
// Default is true because Visual Studio saves .cs files with the BOM by default
|
|
||||||
bool hasBom = true;
|
|
||||||
byte[] bom = Encoding.UTF8.GetPreamble();
|
|
||||||
byte[] oldFile = null;
|
|
||||||
|
|
||||||
if (File.Exists(fullPath))
|
|
||||||
{
|
|
||||||
oldFile = File.ReadAllBytes(fullPath);
|
|
||||||
|
|
||||||
if (oldFile.Length >= 3)
|
|
||||||
hasBom = oldFile.AsSpan(0, 3).SequenceEqual(bom);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make line endings the same on Windows and Unix
|
|
||||||
if (Environment.NewLine == "\n")
|
|
||||||
{
|
|
||||||
text = text.Replace("\n", "\r\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] newFile = (hasBom ? bom : new byte[0]).Concat(Encoding.UTF8.GetBytes(text)).ToArray();
|
|
||||||
|
|
||||||
if (oldFile?.SequenceEqual(newFile) == true)
|
|
||||||
{
|
|
||||||
Logger.Normal($"{relativePath} is already up-to-date");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger.Normal($"Generated file {relativePath}");
|
|
||||||
File.WriteAllBytes(fullPath, newFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static byte[] BuildArchive(ModuleInfo[] modules)
|
private static byte[] BuildArchive(ModuleInfo[] modules)
|
||||||
{
|
{
|
||||||
var builder = new ResultArchiveBuilder();
|
var builder = new ResultArchiveBuilder();
|
||||||
|
@ -409,37 +351,6 @@ namespace LibHacBuild.CodeGen
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Stream GetResource(string name)
|
|
||||||
{
|
|
||||||
var assembly = Assembly.GetExecutingAssembly();
|
|
||||||
string path = $"LibHacBuild.CodeGen.{name}";
|
|
||||||
|
|
||||||
Stream stream = assembly.GetManifestResourceStream(path);
|
|
||||||
if (stream == null) throw new FileNotFoundException($"Resource {path} was not found.");
|
|
||||||
|
|
||||||
return stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string FindProjectDirectory()
|
|
||||||
{
|
|
||||||
string currentDir = Environment.CurrentDirectory;
|
|
||||||
|
|
||||||
while (currentDir != null)
|
|
||||||
{
|
|
||||||
if (File.Exists(Path.Combine(currentDir, "LibHac.sln")))
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
currentDir = Path.GetDirectoryName(currentDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentDir == null)
|
|
||||||
throw new DirectoryNotFoundException("Unable to find project directory.");
|
|
||||||
|
|
||||||
return Path.Combine(currentDir, "src");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int EstimateCilSize(ResultInfo result)
|
private static int EstimateCilSize(ResultInfo result)
|
||||||
{
|
{
|
||||||
int size = 0;
|
int size = 0;
|
393
build/CodeGen/Stage2/KeysCodeGen.cs
Normal file
393
build/CodeGen/Stage2/KeysCodeGen.cs
Normal file
|
@ -0,0 +1,393 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using LibHac;
|
||||||
|
using LibHac.Common;
|
||||||
|
using LibHac.Common.Keys;
|
||||||
|
using LibHac.Crypto;
|
||||||
|
using static LibHacBuild.CodeGen.Common;
|
||||||
|
|
||||||
|
namespace LibHacBuild.CodeGen.Stage2
|
||||||
|
{
|
||||||
|
public static class KeysCodeGen
|
||||||
|
{
|
||||||
|
private static string InputMainKeyFileName = "IncludedKeys.txt";
|
||||||
|
private static string GeneratedFilePath = "LibHac/Common/Keys/DefaultKeySet.Generated.cs";
|
||||||
|
|
||||||
|
public static void Run()
|
||||||
|
{
|
||||||
|
KeySet keySet = CreateKeySet();
|
||||||
|
|
||||||
|
WriteOutput(GeneratedFilePath, BuildDefaultKeySetFile(keySet));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string BuildDefaultKeySetFile(KeySet keySet)
|
||||||
|
{
|
||||||
|
var sb = new IndentingStringBuilder();
|
||||||
|
|
||||||
|
sb.AppendLine(GetHeader());
|
||||||
|
sb.AppendLine();
|
||||||
|
|
||||||
|
sb.AppendLine("using System;");
|
||||||
|
sb.AppendLine();
|
||||||
|
|
||||||
|
sb.AppendLine("namespace LibHac.Common.Keys");
|
||||||
|
sb.AppendLineAndIncrease("{");
|
||||||
|
|
||||||
|
sb.AppendLine("internal static partial class DefaultKeySet");
|
||||||
|
sb.AppendLineAndIncrease("{");
|
||||||
|
|
||||||
|
BuildArray(sb, "RootKeysDev", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct._rootKeysDev));
|
||||||
|
BuildArray(sb, "RootKeysProd", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct._rootKeysProd));
|
||||||
|
BuildArray(sb, "KeySeeds", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct._keySeeds));
|
||||||
|
BuildArray(sb, "StoredKeysDev", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct._storedKeysDev));
|
||||||
|
BuildArray(sb, "StoredKeysProd", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct._storedKeysProd));
|
||||||
|
BuildArray(sb, "DerivedKeysDev", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct._derivedKeysDev));
|
||||||
|
BuildArray(sb, "DerivedKeysProd", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct._derivedKeysProd));
|
||||||
|
BuildArray(sb, "DeviceKeys", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct._deviceKeys));
|
||||||
|
BuildArray(sb, "RsaSigningKeysDev", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct._rsaSigningKeysDev));
|
||||||
|
BuildArray(sb, "RsaSigningKeysProd", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct._rsaSigningKeysProd));
|
||||||
|
BuildArray(sb, "RsaKeys", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct._rsaKeys));
|
||||||
|
|
||||||
|
sb.DecreaseAndAppendLine("}");
|
||||||
|
sb.DecreaseAndAppendLine("}");
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void BuildArray(IndentingStringBuilder sb, string name, ReadOnlySpan<byte> data)
|
||||||
|
{
|
||||||
|
sb.AppendSpacerLine();
|
||||||
|
sb.Append($"private static ReadOnlySpan<byte> {name} => new byte[]");
|
||||||
|
|
||||||
|
if (data.IsEmpty())
|
||||||
|
{
|
||||||
|
sb.AppendLine(" { };");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.AppendLine();
|
||||||
|
sb.AppendLineAndIncrease("{");
|
||||||
|
|
||||||
|
for (int i = 0; i < data.Length; i++)
|
||||||
|
{
|
||||||
|
if (i % 16 != 0) sb.Append(" ");
|
||||||
|
sb.Append($"0x{data[i]:x2}");
|
||||||
|
|
||||||
|
if (i != data.Length - 1)
|
||||||
|
{
|
||||||
|
sb.Append(",");
|
||||||
|
if (i % 16 == 15) sb.AppendLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.AppendLine();
|
||||||
|
sb.DecreaseAndAppendLine("};");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static KeySet CreateKeySet()
|
||||||
|
{
|
||||||
|
var keySet = new KeySet();
|
||||||
|
|
||||||
|
// Populate the key set with all the keys in IncludedKeys.txt
|
||||||
|
using (var reader = new StreamReader(Common.GetResource(InputMainKeyFileName)))
|
||||||
|
{
|
||||||
|
List<ExternalKeyReader.KeyInfo> list = ExternalKeyReader.CreateKeyList();
|
||||||
|
ExternalKeyReader.ReadMainKeys(keySet, reader, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recover all the RSA key parameters and write the key to the key set
|
||||||
|
RSAParameters betaNca0Params =
|
||||||
|
Rsa.RecoverParameters(BetaNca0Modulus, StandardPublicExponent, BetaNca0Exponent);
|
||||||
|
|
||||||
|
betaNca0Params.D.AsSpan().CopyTo(keySet.BetaNca0KeyAreaKey.PrivateExponent.Data);
|
||||||
|
betaNca0Params.DP.AsSpan().CopyTo(keySet.BetaNca0KeyAreaKey.Dp.Data);
|
||||||
|
betaNca0Params.DQ.AsSpan().CopyTo(keySet.BetaNca0KeyAreaKey.Dq.Data);
|
||||||
|
betaNca0Params.Exponent.AsSpan().CopyTo(keySet.BetaNca0KeyAreaKey.PublicExponent.Data);
|
||||||
|
betaNca0Params.InverseQ.AsSpan().CopyTo(keySet.BetaNca0KeyAreaKey.InverseQ.Data);
|
||||||
|
betaNca0Params.Modulus.AsSpan().CopyTo(keySet.BetaNca0KeyAreaKey.Modulus.Data);
|
||||||
|
betaNca0Params.P.AsSpan().CopyTo(keySet.BetaNca0KeyAreaKey.P.Data);
|
||||||
|
betaNca0Params.Q.AsSpan().CopyTo(keySet.BetaNca0KeyAreaKey.Q.Data);
|
||||||
|
|
||||||
|
// First populate the prod RSA keys
|
||||||
|
keySet.SetMode(KeySet.Mode.Prod);
|
||||||
|
|
||||||
|
StandardPublicExponent.CopyTo(keySet.NcaHeaderSigningKeys[0].PublicExponent.Data);
|
||||||
|
StandardPublicExponent.CopyTo(keySet.NcaHeaderSigningKeys[1].PublicExponent.Data);
|
||||||
|
NcaHdrFixedKeyModulus0Prod.CopyTo(keySet.NcaHeaderSigningKeys[0].Modulus.Data);
|
||||||
|
NcaHdrFixedKeyModulus1Prod.CopyTo(keySet.NcaHeaderSigningKeys[1].Modulus.Data);
|
||||||
|
|
||||||
|
StandardPublicExponent.CopyTo(keySet.AcidSigningKeys[0].PublicExponent.Data);
|
||||||
|
StandardPublicExponent.CopyTo(keySet.AcidSigningKeys[1].PublicExponent.Data);
|
||||||
|
AcidFixedKeyModulus0Prod.CopyTo(keySet.AcidSigningKeys[0].Modulus.Data);
|
||||||
|
AcidFixedKeyModulus1Prod.CopyTo(keySet.AcidSigningKeys[1].Modulus.Data);
|
||||||
|
|
||||||
|
StandardPublicExponent.CopyTo(keySet.Package2SigningKey.PublicExponent.Data);
|
||||||
|
Package2FixedKeyModulusProd.CopyTo(keySet.Package2SigningKey.Modulus.Data);
|
||||||
|
|
||||||
|
// Populate the dev RSA keys
|
||||||
|
keySet.SetMode(KeySet.Mode.Dev);
|
||||||
|
|
||||||
|
StandardPublicExponent.CopyTo(keySet.NcaHeaderSigningKeys[0].PublicExponent.Data);
|
||||||
|
StandardPublicExponent.CopyTo(keySet.NcaHeaderSigningKeys[1].PublicExponent.Data);
|
||||||
|
NcaHdrFixedKeyModulus0Dev.CopyTo(keySet.NcaHeaderSigningKeys[0].Modulus.Data);
|
||||||
|
NcaHdrFixedKeyModulus1Dev.CopyTo(keySet.NcaHeaderSigningKeys[1].Modulus.Data);
|
||||||
|
|
||||||
|
StandardPublicExponent.CopyTo(keySet.AcidSigningKeys[0].PublicExponent.Data);
|
||||||
|
StandardPublicExponent.CopyTo(keySet.AcidSigningKeys[1].PublicExponent.Data);
|
||||||
|
AcidFixedKeyModulus0Dev.CopyTo(keySet.AcidSigningKeys[0].Modulus.Data);
|
||||||
|
AcidFixedKeyModulus1Dev.CopyTo(keySet.AcidSigningKeys[1].Modulus.Data);
|
||||||
|
|
||||||
|
StandardPublicExponent.CopyTo(keySet.Package2SigningKey.PublicExponent.Data);
|
||||||
|
Package2FixedKeyModulusDev.CopyTo(keySet.Package2SigningKey.Modulus.Data);
|
||||||
|
|
||||||
|
return keySet;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> StandardPublicExponent => new byte[]
|
||||||
|
{
|
||||||
|
0x01, 0x00, 0x01
|
||||||
|
};
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> BetaNca0Modulus => new byte[]
|
||||||
|
{
|
||||||
|
0xAD, 0x58, 0xEE, 0x97, 0xF9, 0x47, 0x90, 0x7D, 0xF9, 0x29, 0x5F, 0x1F, 0x39, 0x68, 0xEE, 0x49,
|
||||||
|
0x4C, 0x1E, 0x8D, 0x84, 0x91, 0x31, 0x5D, 0xE5, 0x96, 0x27, 0xB2, 0xB3, 0x59, 0x7B, 0xDE, 0xFD,
|
||||||
|
0xB7, 0xEB, 0x40, 0xA1, 0xE7, 0xEB, 0xDC, 0x60, 0xD0, 0x3D, 0xC5, 0x50, 0x92, 0xAD, 0x3D, 0xC4,
|
||||||
|
0x8C, 0x17, 0xD2, 0x37, 0x66, 0xE3, 0xF7, 0x14, 0x34, 0x38, 0x6B, 0xA7, 0x2B, 0x21, 0x10, 0x9B,
|
||||||
|
0x73, 0x49, 0x15, 0xD9, 0x2A, 0x90, 0x86, 0x76, 0x81, 0x6A, 0x10, 0xBD, 0x74, 0xC4, 0x20, 0x55,
|
||||||
|
0x25, 0xA8, 0x02, 0xC5, 0xA0, 0x34, 0x36, 0x7B, 0x66, 0x47, 0x2C, 0x7E, 0x47, 0x82, 0xA5, 0xD4,
|
||||||
|
0xA3, 0x42, 0x45, 0xE8, 0xFD, 0x65, 0x72, 0x48, 0xA1, 0xB0, 0x44, 0x10, 0xEF, 0xAC, 0x1D, 0x0F,
|
||||||
|
0xB5, 0x12, 0x19, 0xA8, 0x41, 0x0B, 0x76, 0x3B, 0xBC, 0xF1, 0x4A, 0x10, 0x46, 0x22, 0xB8, 0xF1,
|
||||||
|
0xBC, 0x21, 0x81, 0x69, 0x9B, 0x63, 0x6F, 0xD7, 0xB9, 0x60, 0x2A, 0x9A, 0xE5, 0x2C, 0x47, 0x72,
|
||||||
|
0x59, 0x65, 0xA2, 0x21, 0x60, 0xC4, 0xFC, 0xB0, 0xD7, 0x6F, 0x42, 0xC9, 0x0C, 0xF5, 0x76, 0x7D,
|
||||||
|
0xF2, 0x5C, 0xE0, 0x80, 0x0F, 0xEE, 0x45, 0x7E, 0x4E, 0x3A, 0x8D, 0x9C, 0x5B, 0x5B, 0xD9, 0xD1,
|
||||||
|
0x43, 0x94, 0x2C, 0xC7, 0x2E, 0xB9, 0x4A, 0xE5, 0x3E, 0x15, 0xDD, 0x43, 0x00, 0xF7, 0x78, 0xE7,
|
||||||
|
0x7C, 0x39, 0xB0, 0x4D, 0xC5, 0xD1, 0x1C, 0xF2, 0xB4, 0x7A, 0x2A, 0xEA, 0x0A, 0x8E, 0xB9, 0x13,
|
||||||
|
0xB4, 0x4F, 0xD7, 0x5B, 0x4D, 0x7B, 0x43, 0xB0, 0x3A, 0x9A, 0x60, 0x22, 0x47, 0x91, 0x78, 0xC7,
|
||||||
|
0x10, 0x64, 0xE0, 0x2C, 0x69, 0xD1, 0x66, 0x3C, 0x42, 0x2E, 0xEF, 0x19, 0x21, 0x89, 0x8E, 0xE1,
|
||||||
|
0xB0, 0xB4, 0xD0, 0x17, 0xA1, 0x0F, 0x73, 0x98, 0x5A, 0xF6, 0xEE, 0xC0, 0x2F, 0x9E, 0xCE, 0xC5
|
||||||
|
};
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> BetaNca0Exponent => new byte[]
|
||||||
|
{
|
||||||
|
0x3C, 0x66, 0x37, 0x44, 0x26, 0xAC, 0x63, 0xD1, 0x30, 0xE6, 0xD4, 0x68, 0xF9, 0xC4, 0xF0, 0xFA,
|
||||||
|
0x03, 0x16, 0xC6, 0x32, 0x81, 0xB0, 0x94, 0xC9, 0xF1, 0x26, 0xC5, 0xE2, 0x2D, 0xF4, 0xB6, 0x3E,
|
||||||
|
0xEB, 0x3D, 0x82, 0x18, 0xA7, 0xC9, 0x8B, 0xD1, 0x03, 0xDD, 0xF2, 0x09, 0x60, 0x02, 0x12, 0xFA,
|
||||||
|
0x8F, 0xE1, 0xA0, 0xF2, 0x82, 0xDC, 0x3D, 0x74, 0x01, 0xBA, 0x02, 0xF0, 0x8D, 0x5B, 0x89, 0x00,
|
||||||
|
0xD1, 0x0B, 0x8F, 0x1C, 0x4A, 0xF3, 0x6E, 0x96, 0x8E, 0x03, 0x19, 0xF0, 0x19, 0x66, 0x58, 0xE9,
|
||||||
|
0xB2, 0x24, 0x37, 0x4B, 0x0A, 0xC6, 0x06, 0x91, 0xBA, 0x92, 0x64, 0x13, 0x5F, 0xF1, 0x4A, 0xBC,
|
||||||
|
0xAB, 0x61, 0xE5, 0x20, 0x08, 0x62, 0xB7, 0x8E, 0x4D, 0x20, 0x30, 0xA7, 0x42, 0x0B, 0x53, 0x58,
|
||||||
|
0xEC, 0xBB, 0x70, 0xCB, 0x2A, 0x56, 0xC7, 0x0C, 0x8B, 0x89, 0xFB, 0x47, 0x6E, 0x58, 0x9C, 0xDD,
|
||||||
|
0xB2, 0xE5, 0x4F, 0x49, 0x52, 0x0B, 0xD9, 0x96, 0x30, 0x8D, 0xDE, 0xC9, 0x0F, 0x6A, 0x82, 0xC7,
|
||||||
|
0xE8, 0x20, 0xB6, 0xB3, 0x95, 0xDD, 0xEB, 0xDF, 0xF7, 0x25, 0x23, 0x6B, 0xF8, 0x5B, 0xD4, 0x81,
|
||||||
|
0x7A, 0xBC, 0x94, 0x13, 0x30, 0x59, 0x28, 0xC8, 0xC9, 0x3A, 0x5D, 0xCC, 0x8D, 0xFD, 0x1A, 0xE1,
|
||||||
|
0xCB, 0xA4, 0x1D, 0xD4, 0x45, 0xF1, 0xBF, 0x87, 0x6C, 0x0E, 0xB1, 0x44, 0xC7, 0x88, 0x62, 0x2B,
|
||||||
|
0x43, 0xAD, 0x75, 0xE6, 0x69, 0xFF, 0xD3, 0x39, 0xF5, 0x7F, 0x2A, 0xA2, 0x5F, 0x7A, 0x5E, 0xE6,
|
||||||
|
0xEF, 0xCB, 0x2F, 0x2C, 0x90, 0xE6, 0x4B, 0x2D, 0x94, 0x62, 0xE8, 0xEC, 0x54, 0x7B, 0x94, 0xB7,
|
||||||
|
0xFB, 0x72, 0x05, 0xFB, 0xB3, 0x23, 0xCA, 0xF8, 0xD4, 0x5C, 0xF6, 0xAC, 0x7D, 0xEC, 0x47, 0xC6,
|
||||||
|
0xD3, 0x22, 0x5D, 0x7C, 0x15, 0xDD, 0x48, 0xE9, 0xBF, 0xA8, 0x99, 0x33, 0x02, 0x79, 0xD3, 0x65
|
||||||
|
};
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> NcaHdrFixedKeyModulus0Prod => new byte[]
|
||||||
|
{
|
||||||
|
0xBF, 0xBE, 0x40, 0x6C, 0xF4, 0xA7, 0x80, 0xE9, 0xF0, 0x7D, 0x0C, 0x99, 0x61, 0x1D, 0x77, 0x2F,
|
||||||
|
0x96, 0xBC, 0x4B, 0x9E, 0x58, 0x38, 0x1B, 0x03, 0xAB, 0xB1, 0x75, 0x49, 0x9F, 0x2B, 0x4D, 0x58,
|
||||||
|
0x34, 0xB0, 0x05, 0xA3, 0x75, 0x22, 0xBE, 0x1A, 0x3F, 0x03, 0x73, 0xAC, 0x70, 0x68, 0xD1, 0x16,
|
||||||
|
0xB9, 0x04, 0x46, 0x5E, 0xB7, 0x07, 0x91, 0x2F, 0x07, 0x8B, 0x26, 0xDE, 0xF6, 0x00, 0x07, 0xB2,
|
||||||
|
0xB4, 0x51, 0xF8, 0x0D, 0x0A, 0x5E, 0x58, 0xAD, 0xEB, 0xBC, 0x9A, 0xD6, 0x49, 0xB9, 0x64, 0xEF,
|
||||||
|
0xA7, 0x82, 0xB5, 0xCF, 0x6D, 0x70, 0x13, 0xB0, 0x0F, 0x85, 0xF6, 0xA9, 0x08, 0xAA, 0x4D, 0x67,
|
||||||
|
0x66, 0x87, 0xFA, 0x89, 0xFF, 0x75, 0x90, 0x18, 0x1E, 0x6B, 0x3D, 0xE9, 0x8A, 0x68, 0xC9, 0x26,
|
||||||
|
0x04, 0xD9, 0x80, 0xCE, 0x3F, 0x5E, 0x92, 0xCE, 0x01, 0xFF, 0x06, 0x3B, 0xF2, 0xC1, 0xA9, 0x0C,
|
||||||
|
0xCE, 0x02, 0x6F, 0x16, 0xBC, 0x92, 0x42, 0x0A, 0x41, 0x64, 0xCD, 0x52, 0xB6, 0x34, 0x4D, 0xAE,
|
||||||
|
0xC0, 0x2E, 0xDE, 0xA4, 0xDF, 0x27, 0x68, 0x3C, 0xC1, 0xA0, 0x60, 0xAD, 0x43, 0xF3, 0xFC, 0x86,
|
||||||
|
0xC1, 0x3E, 0x6C, 0x46, 0xF7, 0x7C, 0x29, 0x9F, 0xFA, 0xFD, 0xF0, 0xE3, 0xCE, 0x64, 0xE7, 0x35,
|
||||||
|
0xF2, 0xF6, 0x56, 0x56, 0x6F, 0x6D, 0xF1, 0xE2, 0x42, 0xB0, 0x83, 0x40, 0xA5, 0xC3, 0x20, 0x2B,
|
||||||
|
0xCC, 0x9A, 0xAE, 0xCA, 0xED, 0x4D, 0x70, 0x30, 0xA8, 0x70, 0x1C, 0x70, 0xFD, 0x13, 0x63, 0x29,
|
||||||
|
0x02, 0x79, 0xEA, 0xD2, 0xA7, 0xAF, 0x35, 0x28, 0x32, 0x1C, 0x7B, 0xE6, 0x2F, 0x1A, 0xAA, 0x40,
|
||||||
|
0x7E, 0x32, 0x8C, 0x27, 0x42, 0xFE, 0x82, 0x78, 0xEC, 0x0D, 0xEB, 0xE6, 0x83, 0x4B, 0x6D, 0x81,
|
||||||
|
0x04, 0x40, 0x1A, 0x9E, 0x9A, 0x67, 0xF6, 0x72, 0x29, 0xFA, 0x04, 0xF0, 0x9D, 0xE4, 0xF4, 0x03
|
||||||
|
};
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> NcaHdrFixedKeyModulus1Prod => new byte[]
|
||||||
|
{
|
||||||
|
0xAD, 0xE3, 0xE1, 0xFA, 0x04, 0x35, 0xE5, 0xB6, 0xDD, 0x49, 0xEA, 0x89, 0x29, 0xB1, 0xFF, 0xB6,
|
||||||
|
0x43, 0xDF, 0xCA, 0x96, 0xA0, 0x4A, 0x13, 0xDF, 0x43, 0xD9, 0x94, 0x97, 0x96, 0x43, 0x65, 0x48,
|
||||||
|
0x70, 0x58, 0x33, 0xA2, 0x7D, 0x35, 0x7B, 0x96, 0x74, 0x5E, 0x0B, 0x5C, 0x32, 0x18, 0x14, 0x24,
|
||||||
|
0xC2, 0x58, 0xB3, 0x6C, 0x22, 0x7A, 0xA1, 0xB7, 0xCB, 0x90, 0xA7, 0xA3, 0xF9, 0x7D, 0x45, 0x16,
|
||||||
|
0xA5, 0xC8, 0xED, 0x8F, 0xAD, 0x39, 0x5E, 0x9E, 0x4B, 0x51, 0x68, 0x7D, 0xF8, 0x0C, 0x35, 0xC6,
|
||||||
|
0x3F, 0x91, 0xAE, 0x44, 0xA5, 0x92, 0x30, 0x0D, 0x46, 0xF8, 0x40, 0xFF, 0xD0, 0xFF, 0x06, 0xD2,
|
||||||
|
0x1C, 0x7F, 0x96, 0x18, 0xDC, 0xB7, 0x1D, 0x66, 0x3E, 0xD1, 0x73, 0xBC, 0x15, 0x8A, 0x2F, 0x94,
|
||||||
|
0xF3, 0x00, 0xC1, 0x83, 0xF1, 0xCD, 0xD7, 0x81, 0x88, 0xAB, 0xDF, 0x8C, 0xEF, 0x97, 0xDD, 0x1B,
|
||||||
|
0x17, 0x5F, 0x58, 0xF6, 0x9A, 0xE9, 0xE8, 0xC2, 0x2F, 0x38, 0x15, 0xF5, 0x21, 0x07, 0xF8, 0x37,
|
||||||
|
0x90, 0x5D, 0x2E, 0x02, 0x40, 0x24, 0x15, 0x0D, 0x25, 0xB7, 0x26, 0x5D, 0x09, 0xCC, 0x4C, 0xF4,
|
||||||
|
0xF2, 0x1B, 0x94, 0x70, 0x5A, 0x9E, 0xEE, 0xED, 0x77, 0x77, 0xD4, 0x51, 0x99, 0xF5, 0xDC, 0x76,
|
||||||
|
0x1E, 0xE3, 0x6C, 0x8C, 0xD1, 0x12, 0xD4, 0x57, 0xD1, 0xB6, 0x83, 0xE4, 0xE4, 0xFE, 0xDA, 0xE9,
|
||||||
|
0xB4, 0x3B, 0x33, 0xE5, 0x37, 0x8A, 0xDF, 0xB5, 0x7F, 0x89, 0xF1, 0x9B, 0x9E, 0xB0, 0x15, 0xB2,
|
||||||
|
0x3A, 0xFE, 0xEA, 0x61, 0x84, 0x5B, 0x7D, 0x4B, 0x23, 0x12, 0x0B, 0x83, 0x12, 0xF2, 0x22, 0x6B,
|
||||||
|
0xB9, 0x22, 0x96, 0x4B, 0x26, 0x0B, 0x63, 0x5E, 0x96, 0x57, 0x52, 0xA3, 0x67, 0x64, 0x22, 0xCA,
|
||||||
|
0xD0, 0x56, 0x3E, 0x74, 0xB5, 0x98, 0x1F, 0x0D, 0xF8, 0xB3, 0x34, 0xE6, 0x98, 0x68, 0x5A, 0xAD
|
||||||
|
};
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> AcidFixedKeyModulus0Prod => new byte[]
|
||||||
|
{
|
||||||
|
0xDD, 0xC8, 0xDD, 0xF2, 0x4E, 0x6D, 0xF0, 0xCA, 0x9E, 0xC7, 0x5D, 0xC7, 0x7B, 0xAD, 0xFE, 0x7D,
|
||||||
|
0x23, 0x89, 0x69, 0xB6, 0xF2, 0x06, 0xA2, 0x02, 0x88, 0xE1, 0x55, 0x91, 0xAB, 0xCB, 0x4D, 0x50,
|
||||||
|
0x2E, 0xFC, 0x9D, 0x94, 0x76, 0xD6, 0x4C, 0xD8, 0xFF, 0x10, 0xFA, 0x5E, 0x93, 0x0A, 0xB4, 0x57,
|
||||||
|
0xAC, 0x51, 0xC7, 0x16, 0x66, 0xF4, 0x1A, 0x54, 0xC2, 0xC5, 0x04, 0x3D, 0x1B, 0xFE, 0x30, 0x20,
|
||||||
|
0x8A, 0xAC, 0x6F, 0x6F, 0xF5, 0xC7, 0xB6, 0x68, 0xB8, 0xC9, 0x40, 0x6B, 0x42, 0xAD, 0x11, 0x21,
|
||||||
|
0xE7, 0x8B, 0xE9, 0x75, 0x01, 0x86, 0xE4, 0x48, 0x9B, 0x0A, 0x0A, 0xF8, 0x7F, 0xE8, 0x87, 0xF2,
|
||||||
|
0x82, 0x01, 0xE6, 0xA3, 0x0F, 0xE4, 0x66, 0xAE, 0x83, 0x3F, 0x4E, 0x9F, 0x5E, 0x01, 0x30, 0xA4,
|
||||||
|
0x00, 0xB9, 0x9A, 0xAE, 0x5F, 0x03, 0xCC, 0x18, 0x60, 0xE5, 0xEF, 0x3B, 0x5E, 0x15, 0x16, 0xFE,
|
||||||
|
0x1C, 0x82, 0x78, 0xB5, 0x2F, 0x47, 0x7C, 0x06, 0x66, 0x88, 0x5D, 0x35, 0xA2, 0x67, 0x20, 0x10,
|
||||||
|
0xE7, 0x6C, 0x43, 0x68, 0xD3, 0xE4, 0x5A, 0x68, 0x2A, 0x5A, 0xE2, 0x6D, 0x73, 0xB0, 0x31, 0x53,
|
||||||
|
0x1C, 0x20, 0x09, 0x44, 0xF5, 0x1A, 0x9D, 0x22, 0xBE, 0x12, 0xA1, 0x77, 0x11, 0xE2, 0xA1, 0xCD,
|
||||||
|
0x40, 0x9A, 0xA2, 0x8B, 0x60, 0x9B, 0xEF, 0xA0, 0xD3, 0x48, 0x63, 0xA2, 0xF8, 0xA3, 0x2C, 0x08,
|
||||||
|
0x56, 0x52, 0x2E, 0x60, 0x19, 0x67, 0x5A, 0xA7, 0x9F, 0xDC, 0x3F, 0x3F, 0x69, 0x2B, 0x31, 0x6A,
|
||||||
|
0xB7, 0x88, 0x4A, 0x14, 0x84, 0x80, 0x33, 0x3C, 0x9D, 0x44, 0xB7, 0x3F, 0x4C, 0xE1, 0x75, 0xEA,
|
||||||
|
0x37, 0xEA, 0xE8, 0x1E, 0x7C, 0x77, 0xB7, 0xC6, 0x1A, 0xA2, 0xF0, 0x9F, 0x10, 0x61, 0xCD, 0x7B,
|
||||||
|
0x5B, 0x32, 0x4C, 0x37, 0xEF, 0xB1, 0x71, 0x68, 0x53, 0x0A, 0xED, 0x51, 0x7D, 0x35, 0x22, 0xFD
|
||||||
|
};
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> AcidFixedKeyModulus1Prod => new byte[]
|
||||||
|
{
|
||||||
|
0xE7, 0xAA, 0x25, 0xC8, 0x01, 0xA5, 0x14, 0x6B, 0x01, 0x60, 0x3E, 0xD9, 0x96, 0x5A, 0xBF, 0x90,
|
||||||
|
0xAC, 0xA7, 0xFD, 0x9B, 0x5B, 0xBD, 0x8A, 0x26, 0xB0, 0xCB, 0x20, 0x28, 0x9A, 0x72, 0x12, 0xF5,
|
||||||
|
0x20, 0x65, 0xB3, 0xB9, 0x84, 0x58, 0x1F, 0x27, 0xBC, 0x7C, 0xA2, 0xC9, 0x9E, 0x18, 0x95, 0xCF,
|
||||||
|
0xC2, 0x73, 0x2E, 0x74, 0x8C, 0x66, 0xE5, 0x9E, 0x79, 0x2B, 0xB8, 0x07, 0x0C, 0xB0, 0x4E, 0x8E,
|
||||||
|
0xAB, 0x85, 0x21, 0x42, 0xC4, 0xC5, 0x6D, 0x88, 0x9C, 0xDB, 0x15, 0x95, 0x3F, 0x80, 0xDB, 0x7A,
|
||||||
|
0x9A, 0x7D, 0x41, 0x56, 0x25, 0x17, 0x18, 0x42, 0x4D, 0x8C, 0xAC, 0xA5, 0x7B, 0xDB, 0x42, 0x5D,
|
||||||
|
0x59, 0x35, 0x45, 0x5D, 0x8A, 0x02, 0xB5, 0x70, 0xC0, 0x72, 0x35, 0x46, 0xD0, 0x1D, 0x60, 0x01,
|
||||||
|
0x4A, 0xCC, 0x1C, 0x46, 0xD3, 0xD6, 0x35, 0x52, 0xD6, 0xE1, 0xF8, 0x3B, 0x5D, 0xEA, 0xDD, 0xB8,
|
||||||
|
0xFE, 0x7D, 0x50, 0xCB, 0x35, 0x23, 0x67, 0x8B, 0xB6, 0xE4, 0x74, 0xD2, 0x60, 0xFC, 0xFD, 0x43,
|
||||||
|
0xBF, 0x91, 0x08, 0x81, 0xC5, 0x4F, 0x5D, 0x16, 0x9A, 0xC4, 0x9A, 0xC6, 0xF6, 0xF3, 0xE1, 0xF6,
|
||||||
|
0x5C, 0x07, 0xAA, 0x71, 0x6C, 0x13, 0xA4, 0xB1, 0xB3, 0x66, 0xBF, 0x90, 0x4C, 0x3D, 0xA2, 0xC4,
|
||||||
|
0x0B, 0xB8, 0x3D, 0x7A, 0x8C, 0x19, 0xFA, 0xFF, 0x6B, 0xB9, 0x1F, 0x02, 0xCC, 0xB6, 0xD3, 0x0C,
|
||||||
|
0x7D, 0x19, 0x1F, 0x47, 0xF9, 0xC7, 0x40, 0x01, 0xFA, 0x46, 0xEA, 0x0B, 0xD4, 0x02, 0xE0, 0x3D,
|
||||||
|
0x30, 0x9A, 0x1A, 0x0F, 0xEA, 0xA7, 0x66, 0x55, 0xF7, 0xCB, 0x28, 0xE2, 0xBB, 0x99, 0xE4, 0x83,
|
||||||
|
0xC3, 0x43, 0x03, 0xEE, 0xDC, 0x1F, 0x02, 0x23, 0xDD, 0xD1, 0x2D, 0x39, 0xA4, 0x65, 0x75, 0x03,
|
||||||
|
0xEF, 0x37, 0x9C, 0x06, 0xD6, 0xFA, 0xA1, 0x15, 0xF0, 0xDB, 0x17, 0x47, 0x26, 0x4F, 0x49, 0x03
|
||||||
|
};
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> Package2FixedKeyModulusProd => new byte[]
|
||||||
|
{
|
||||||
|
0x8D, 0x13, 0xA7, 0x77, 0x6A, 0xE5, 0xDC, 0xC0, 0x3B, 0x25, 0xD0, 0x58, 0xE4, 0x20, 0x69, 0x59,
|
||||||
|
0x55, 0x4B, 0xAB, 0x70, 0x40, 0x08, 0x28, 0x07, 0xA8, 0xA7, 0xFD, 0x0F, 0x31, 0x2E, 0x11, 0xFE,
|
||||||
|
0x47, 0xA0, 0xF9, 0x9D, 0xDF, 0x80, 0xDB, 0x86, 0x5A, 0x27, 0x89, 0xCD, 0x97, 0x6C, 0x85, 0xC5,
|
||||||
|
0x6C, 0x39, 0x7F, 0x41, 0xF2, 0xFF, 0x24, 0x20, 0xC3, 0x95, 0xA6, 0xF7, 0x9D, 0x4A, 0x45, 0x74,
|
||||||
|
0x8B, 0x5D, 0x28, 0x8A, 0xC6, 0x99, 0x35, 0x68, 0x85, 0xA5, 0x64, 0x32, 0x80, 0x9F, 0xD3, 0x48,
|
||||||
|
0x39, 0xA2, 0x1D, 0x24, 0x67, 0x69, 0xDF, 0x75, 0xAC, 0x12, 0xB5, 0xBD, 0xC3, 0x29, 0x90, 0xBE,
|
||||||
|
0x37, 0xE4, 0xA0, 0x80, 0x9A, 0xBE, 0x36, 0xBF, 0x1F, 0x2C, 0xAB, 0x2B, 0xAD, 0xF5, 0x97, 0x32,
|
||||||
|
0x9A, 0x42, 0x9D, 0x09, 0x8B, 0x08, 0xF0, 0x63, 0x47, 0xA3, 0xE9, 0x1B, 0x36, 0xD8, 0x2D, 0x8A,
|
||||||
|
0xD7, 0xE1, 0x54, 0x11, 0x95, 0xE4, 0x45, 0x88, 0x69, 0x8A, 0x2B, 0x35, 0xCE, 0xD0, 0xA5, 0x0B,
|
||||||
|
0xD5, 0x5D, 0xAC, 0xDB, 0xAF, 0x11, 0x4D, 0xCA, 0xB8, 0x1E, 0xE7, 0x01, 0x9E, 0xF4, 0x46, 0xA3,
|
||||||
|
0x8A, 0x94, 0x6D, 0x76, 0xBD, 0x8A, 0xC8, 0x3B, 0xD2, 0x31, 0x58, 0x0C, 0x79, 0xA8, 0x26, 0xE9,
|
||||||
|
0xD1, 0x79, 0x9C, 0xCB, 0xD4, 0x2B, 0x6A, 0x4F, 0xC6, 0xCC, 0xCF, 0x90, 0xA7, 0xB9, 0x98, 0x47,
|
||||||
|
0xFD, 0xFA, 0x4C, 0x6C, 0x6F, 0x81, 0x87, 0x3B, 0xCA, 0xB8, 0x50, 0xF6, 0x3E, 0x39, 0x5D, 0x4D,
|
||||||
|
0x97, 0x3F, 0x0F, 0x35, 0x39, 0x53, 0xFB, 0xFA, 0xCD, 0xAB, 0xA8, 0x7A, 0x62, 0x9A, 0x3F, 0xF2,
|
||||||
|
0x09, 0x27, 0x96, 0x3F, 0x07, 0x9A, 0x91, 0xF7, 0x16, 0xBF, 0xC6, 0x3A, 0x82, 0x5A, 0x4B, 0xCF,
|
||||||
|
0x49, 0x50, 0x95, 0x8C, 0x55, 0x80, 0x7E, 0x39, 0xB1, 0x48, 0x05, 0x1E, 0x21, 0xC7, 0x24, 0x4F
|
||||||
|
};
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> NcaHdrFixedKeyModulus0Dev => new byte[]
|
||||||
|
{
|
||||||
|
0xD8, 0xF1, 0x18, 0xEF, 0x32, 0x72, 0x4C, 0xA7, 0x47, 0x4C, 0xB9, 0xEA, 0xB3, 0x04, 0xA8, 0xA4,
|
||||||
|
0xAC, 0x99, 0x08, 0x08, 0x04, 0xBF, 0x68, 0x57, 0xB8, 0x43, 0x94, 0x2B, 0xC7, 0xB9, 0x66, 0x49,
|
||||||
|
0x85, 0xE5, 0x8A, 0x9B, 0xC1, 0x00, 0x9A, 0x6A, 0x8D, 0xD0, 0xEF, 0xCE, 0xFF, 0x86, 0xC8, 0x5C,
|
||||||
|
0x5D, 0xE9, 0x53, 0x7B, 0x19, 0x2A, 0xA8, 0xC0, 0x22, 0xD1, 0xF3, 0x22, 0x0A, 0x50, 0xF2, 0x2B,
|
||||||
|
0x65, 0x05, 0x1B, 0x9E, 0xEC, 0x61, 0xB5, 0x63, 0xA3, 0x6F, 0x3B, 0xBA, 0x63, 0x3A, 0x53, 0xF4,
|
||||||
|
0x49, 0x2F, 0xCF, 0x03, 0xCC, 0xD7, 0x50, 0x82, 0x1B, 0x29, 0x4F, 0x08, 0xDE, 0x1B, 0x6D, 0x47,
|
||||||
|
0x4F, 0xA8, 0xB6, 0x6A, 0x26, 0xA0, 0x83, 0x3F, 0x1A, 0xAF, 0x83, 0x8F, 0x0E, 0x17, 0x3F, 0xFE,
|
||||||
|
0x44, 0x1C, 0x56, 0x94, 0x2E, 0x49, 0x83, 0x83, 0x03, 0xE9, 0xB6, 0xAD, 0xD5, 0xDE, 0xE3, 0x2D,
|
||||||
|
0xA1, 0xD9, 0x66, 0x20, 0x5D, 0x1F, 0x5E, 0x96, 0x5D, 0x5B, 0x55, 0x0D, 0xD4, 0xB4, 0x77, 0x6E,
|
||||||
|
0xAE, 0x1B, 0x69, 0xF3, 0xA6, 0x61, 0x0E, 0x51, 0x62, 0x39, 0x28, 0x63, 0x75, 0x76, 0xBF, 0xB0,
|
||||||
|
0xD2, 0x22, 0xEF, 0x98, 0x25, 0x02, 0x05, 0xC0, 0xD7, 0x6A, 0x06, 0x2C, 0xA5, 0xD8, 0x5A, 0x9D,
|
||||||
|
0x7A, 0xA4, 0x21, 0x55, 0x9F, 0xF9, 0x3E, 0xBF, 0x16, 0xF6, 0x07, 0xC2, 0xB9, 0x6E, 0x87, 0x9E,
|
||||||
|
0xB5, 0x1C, 0xBE, 0x97, 0xFA, 0x82, 0x7E, 0xED, 0x30, 0xD4, 0x66, 0x3F, 0xDE, 0xD8, 0x1B, 0x4B,
|
||||||
|
0x15, 0xD9, 0xFB, 0x2F, 0x50, 0xF0, 0x9D, 0x1D, 0x52, 0x4C, 0x1C, 0x4D, 0x8D, 0xAE, 0x85, 0x1E,
|
||||||
|
0xEA, 0x7F, 0x86, 0xF3, 0x0B, 0x7B, 0x87, 0x81, 0x98, 0x23, 0x80, 0x63, 0x4F, 0x2F, 0xB0, 0x62,
|
||||||
|
0xCC, 0x6E, 0xD2, 0x46, 0x13, 0x65, 0x2B, 0xD6, 0x44, 0x33, 0x59, 0xB5, 0x8F, 0xB9, 0x4A, 0xA9
|
||||||
|
};
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> NcaHdrFixedKeyModulus1Dev => new byte[]
|
||||||
|
{
|
||||||
|
0x9A, 0xBC, 0x88, 0xBD, 0x0A, 0xBE, 0xD7, 0x0C, 0x9B, 0x42, 0x75, 0x65, 0x38, 0x5E, 0xD1, 0x01,
|
||||||
|
0xCD, 0x12, 0xAE, 0xEA, 0xE9, 0x4B, 0xDB, 0xB4, 0x5E, 0x36, 0x10, 0x96, 0xDA, 0x3D, 0x2E, 0x66,
|
||||||
|
0xD3, 0x99, 0x13, 0x8A, 0xBE, 0x67, 0x41, 0xC8, 0x93, 0xD9, 0x3E, 0x42, 0xCE, 0x34, 0xCE, 0x96,
|
||||||
|
0xFA, 0x0B, 0x23, 0xCC, 0x2C, 0xDF, 0x07, 0x3F, 0x3B, 0x24, 0x4B, 0x12, 0x67, 0x3A, 0x29, 0x36,
|
||||||
|
0xA3, 0xAA, 0x06, 0xF0, 0x65, 0xA5, 0x85, 0xBA, 0xFD, 0x12, 0xEC, 0xF1, 0x60, 0x67, 0xF0, 0x8F,
|
||||||
|
0xD3, 0x5B, 0x01, 0x1B, 0x1E, 0x84, 0xA3, 0x5C, 0x65, 0x36, 0xF9, 0x23, 0x7E, 0xF3, 0x26, 0x38,
|
||||||
|
0x64, 0x98, 0xBA, 0xE4, 0x19, 0x91, 0x4C, 0x02, 0xCF, 0xC9, 0x6D, 0x86, 0xEC, 0x1D, 0x41, 0x69,
|
||||||
|
0xDD, 0x56, 0xEA, 0x5C, 0xA3, 0x2A, 0x58, 0xB4, 0x39, 0xCC, 0x40, 0x31, 0xFD, 0xFB, 0x42, 0x74,
|
||||||
|
0xF8, 0xEC, 0xEA, 0x00, 0xF0, 0xD9, 0x28, 0xEA, 0xFA, 0x2D, 0x00, 0xE1, 0x43, 0x53, 0xC6, 0x32,
|
||||||
|
0xF4, 0xA2, 0x07, 0xD4, 0x5F, 0xD4, 0xCB, 0xAC, 0xCA, 0xFF, 0xDF, 0x84, 0xD2, 0x86, 0x14, 0x3C,
|
||||||
|
0xDE, 0x22, 0x75, 0xA5, 0x73, 0xFF, 0x68, 0x07, 0x4A, 0xF9, 0x7C, 0x2C, 0xCC, 0xDE, 0x45, 0xB6,
|
||||||
|
0x54, 0x82, 0x90, 0x36, 0x1F, 0x2C, 0x51, 0x96, 0xC5, 0x0A, 0x53, 0x5B, 0xF0, 0x8B, 0x4A, 0xAA,
|
||||||
|
0x3B, 0x68, 0x97, 0x19, 0x17, 0x1F, 0x01, 0xB8, 0xED, 0xB9, 0x9A, 0x5E, 0x08, 0xC5, 0x20, 0x1E,
|
||||||
|
0x6A, 0x09, 0xF0, 0xE9, 0x73, 0xA3, 0xBE, 0x10, 0x06, 0x02, 0xE9, 0xFB, 0x85, 0xFA, 0x5F, 0x01,
|
||||||
|
0xAC, 0x60, 0xE0, 0xED, 0x7D, 0xB9, 0x49, 0xA8, 0x9E, 0x98, 0x7D, 0x91, 0x40, 0x05, 0xCF, 0xF9,
|
||||||
|
0x1A, 0xFC, 0x40, 0x22, 0xA8, 0x96, 0x5B, 0xB0, 0xDC, 0x7A, 0xF5, 0xB7, 0xE9, 0x91, 0x4C, 0x49
|
||||||
|
};
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> AcidFixedKeyModulus0Dev => new byte[]
|
||||||
|
{
|
||||||
|
0xD6, 0x34, 0xA5, 0x78, 0x6C, 0x68, 0xCE, 0x5A, 0xC2, 0x37, 0x17, 0xF3, 0x82, 0x45, 0xC6, 0x89,
|
||||||
|
0xE1, 0x2D, 0x06, 0x67, 0xBF, 0xB4, 0x06, 0x19, 0x55, 0x6B, 0x27, 0x66, 0x0C, 0xA4, 0xB5, 0x87,
|
||||||
|
0x81, 0x25, 0xF4, 0x30, 0xBC, 0x53, 0x08, 0x68, 0xA2, 0x48, 0x49, 0x8C, 0x3F, 0x38, 0x40, 0x9C,
|
||||||
|
0xC4, 0x26, 0xF4, 0x79, 0xE2, 0xA1, 0x85, 0xF5, 0x5C, 0x7F, 0x58, 0xBA, 0xA6, 0x1C, 0xA0, 0x8B,
|
||||||
|
0x84, 0x16, 0x14, 0x6F, 0x85, 0xD9, 0x7C, 0xE1, 0x3C, 0x67, 0x22, 0x1E, 0xFB, 0xD8, 0xA7, 0xA5,
|
||||||
|
0x9A, 0xBF, 0xEC, 0x0E, 0xCF, 0x96, 0x7E, 0x85, 0xC2, 0x1D, 0x49, 0x5D, 0x54, 0x26, 0xCB, 0x32,
|
||||||
|
0x7C, 0xF6, 0xBB, 0x58, 0x03, 0x80, 0x2B, 0x5D, 0xF7, 0xFB, 0xD1, 0x9D, 0xC7, 0xC6, 0x2E, 0x53,
|
||||||
|
0xC0, 0x6F, 0x39, 0x2C, 0x1F, 0xA9, 0x92, 0xF2, 0x4D, 0x7D, 0x4E, 0x74, 0xFF, 0xE4, 0xEF, 0xE4,
|
||||||
|
0x7C, 0x3D, 0x34, 0x2A, 0x71, 0xA4, 0x97, 0x59, 0xFF, 0x4F, 0xA2, 0xF4, 0x66, 0x78, 0xD8, 0xBA,
|
||||||
|
0x99, 0xE3, 0xE6, 0xDB, 0x54, 0xB9, 0xE9, 0x54, 0xA1, 0x70, 0xFC, 0x05, 0x1F, 0x11, 0x67, 0x4B,
|
||||||
|
0x26, 0x8C, 0x0C, 0x3E, 0x03, 0xD2, 0xA3, 0x55, 0x5C, 0x7D, 0xC0, 0x5D, 0x9D, 0xFF, 0x13, 0x2F,
|
||||||
|
0xFD, 0x19, 0xBF, 0xED, 0x44, 0xC3, 0x8C, 0xA7, 0x28, 0xCB, 0xE5, 0xE0, 0xB1, 0xA7, 0x9C, 0x33,
|
||||||
|
0x8D, 0xB8, 0x6E, 0xDE, 0x87, 0x18, 0x22, 0x60, 0xC4, 0xAE, 0xF2, 0x87, 0x9F, 0xCE, 0x09, 0x5C,
|
||||||
|
0xB5, 0x99, 0xA5, 0x9F, 0x49, 0xF2, 0xD7, 0x58, 0xFA, 0xF9, 0xC0, 0x25, 0x7D, 0xD6, 0xCB, 0xF3,
|
||||||
|
0xD8, 0x6C, 0xA2, 0x69, 0x91, 0x68, 0x73, 0xB1, 0x94, 0x6F, 0xA3, 0xF3, 0xB9, 0x7D, 0xF8, 0xE0,
|
||||||
|
0x72, 0x9E, 0x93, 0x7B, 0x7A, 0xA2, 0x57, 0x60, 0xB7, 0x5B, 0xA9, 0x84, 0xAE, 0x64, 0x88, 0x69
|
||||||
|
};
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> AcidFixedKeyModulus1Dev => new byte[]
|
||||||
|
{
|
||||||
|
0xBC, 0xA5, 0x6A, 0x7E, 0xEA, 0x38, 0x34, 0x62, 0xA6, 0x10, 0x18, 0x3C, 0xE1, 0x63, 0x7B, 0xF0,
|
||||||
|
0xD3, 0x08, 0x8C, 0xF5, 0xC5, 0xC4, 0xC7, 0x93, 0xE9, 0xD9, 0xE6, 0x32, 0xF3, 0xA0, 0xF6, 0x6E,
|
||||||
|
0x8A, 0x98, 0x76, 0x47, 0x33, 0x47, 0x65, 0x02, 0x70, 0xDC, 0x86, 0x5F, 0x3D, 0x61, 0x5A, 0x70,
|
||||||
|
0xBC, 0x5A, 0xCA, 0xCA, 0x50, 0xAD, 0x61, 0x7E, 0xC9, 0xEC, 0x27, 0xFF, 0xE8, 0x64, 0x42, 0x9A,
|
||||||
|
0xEE, 0xBE, 0xC3, 0xD1, 0x0B, 0xC0, 0xE9, 0xBF, 0x83, 0x8D, 0xC0, 0x0C, 0xD8, 0x00, 0x5B, 0x76,
|
||||||
|
0x90, 0xD2, 0x4B, 0x30, 0x84, 0x35, 0x8B, 0x1E, 0x20, 0xB7, 0xE4, 0xDC, 0x63, 0xE5, 0xDF, 0xCD,
|
||||||
|
0x00, 0x5F, 0x81, 0x5F, 0x67, 0xC5, 0x8B, 0xDF, 0xFC, 0xE1, 0x37, 0x5F, 0x07, 0xD9, 0xDE, 0x4F,
|
||||||
|
0xE6, 0x7B, 0xF1, 0xFB, 0xA1, 0x5A, 0x71, 0x40, 0xFE, 0xBA, 0x1E, 0xAE, 0x13, 0x22, 0xD2, 0xFE,
|
||||||
|
0x37, 0xA2, 0xB6, 0x8B, 0xAB, 0xEB, 0x84, 0x81, 0x4E, 0x7C, 0x1E, 0x02, 0xD1, 0xFB, 0xD7, 0x5D,
|
||||||
|
0x11, 0x84, 0x64, 0xD2, 0x4D, 0xBB, 0x50, 0x00, 0x67, 0x54, 0xE2, 0x77, 0x89, 0xBA, 0x0B, 0xE7,
|
||||||
|
0x05, 0x57, 0x9A, 0x22, 0x5A, 0xEC, 0x76, 0x1C, 0xFD, 0xE8, 0xA8, 0x18, 0x16, 0x41, 0x65, 0x03,
|
||||||
|
0xFA, 0xC4, 0xA6, 0x31, 0x5C, 0x1A, 0x7F, 0xAB, 0x11, 0xC8, 0x4A, 0x99, 0xB9, 0xE6, 0xCF, 0x62,
|
||||||
|
0x21, 0xA6, 0x72, 0x47, 0xDB, 0xBA, 0x96, 0x26, 0x4E, 0x2E, 0xD4, 0x8C, 0x46, 0xD6, 0xA7, 0x1A,
|
||||||
|
0x6C, 0x32, 0xA7, 0xDF, 0x85, 0x1C, 0x03, 0xC3, 0x6D, 0xA9, 0xE9, 0x68, 0xF4, 0x17, 0x1E, 0xB2,
|
||||||
|
0x70, 0x2A, 0xA1, 0xE5, 0xE1, 0xF3, 0x8F, 0x6F, 0x63, 0xAC, 0xEB, 0x72, 0x0B, 0x4C, 0x4A, 0x36,
|
||||||
|
0x3C, 0x60, 0x91, 0x9F, 0x6E, 0x1C, 0x71, 0xEA, 0xD0, 0x78, 0x78, 0xA0, 0x2E, 0xC6, 0x32, 0x6B
|
||||||
|
};
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> Package2FixedKeyModulusDev => new byte[]
|
||||||
|
{
|
||||||
|
0xB3, 0x65, 0x54, 0xFB, 0x0A, 0xB0, 0x1E, 0x85, 0xA7, 0xF6, 0xCF, 0x91, 0x8E, 0xBA, 0x96, 0x99,
|
||||||
|
0x0D, 0x8B, 0x91, 0x69, 0x2A, 0xEE, 0x01, 0x20, 0x4F, 0x34, 0x5C, 0x2C, 0x4F, 0x4E, 0x37, 0xC7,
|
||||||
|
0xF1, 0x0B, 0xD4, 0xCD, 0xA1, 0x7F, 0x93, 0xF1, 0x33, 0x59, 0xCE, 0xB1, 0xE9, 0xDD, 0x26, 0xE6,
|
||||||
|
0xF3, 0xBB, 0x77, 0x87, 0x46, 0x7A, 0xD6, 0x4E, 0x47, 0x4A, 0xD1, 0x41, 0xB7, 0x79, 0x4A, 0x38,
|
||||||
|
0x06, 0x6E, 0xCF, 0x61, 0x8F, 0xCD, 0xC1, 0x40, 0x0B, 0xFA, 0x26, 0xDC, 0xC0, 0x34, 0x51, 0x83,
|
||||||
|
0xD9, 0x3B, 0x11, 0x54, 0x3B, 0x96, 0x27, 0x32, 0x9A, 0x95, 0xBE, 0x1E, 0x68, 0x11, 0x50, 0xA0,
|
||||||
|
0x6B, 0x10, 0xA8, 0x83, 0x8B, 0xF5, 0xFC, 0xBC, 0x90, 0x84, 0x7A, 0x5A, 0x5C, 0x43, 0x52, 0xE6,
|
||||||
|
0xC8, 0x26, 0xE9, 0xFE, 0x06, 0xA0, 0x8B, 0x53, 0x0F, 0xAF, 0x1E, 0xC4, 0x1C, 0x0B, 0xCF, 0x50,
|
||||||
|
0x1A, 0xA4, 0xF3, 0x5C, 0xFB, 0xF0, 0x97, 0xE4, 0xDE, 0x32, 0x0A, 0x9F, 0xE3, 0x5A, 0xAA, 0xB7,
|
||||||
|
0x44, 0x7F, 0x5C, 0x33, 0x60, 0xB9, 0x0F, 0x22, 0x2D, 0x33, 0x2A, 0xE9, 0x69, 0x79, 0x31, 0x42,
|
||||||
|
0x8F, 0xE4, 0x3A, 0x13, 0x8B, 0xE7, 0x26, 0xBD, 0x08, 0x87, 0x6C, 0xA6, 0xF2, 0x73, 0xF6, 0x8E,
|
||||||
|
0xA7, 0xF2, 0xFE, 0xFB, 0x6C, 0x28, 0x66, 0x0D, 0xBD, 0xD7, 0xEB, 0x42, 0xA8, 0x78, 0xE6, 0xB8,
|
||||||
|
0x6B, 0xAE, 0xC7, 0xA9, 0xE2, 0x40, 0x6E, 0x89, 0x20, 0x82, 0x25, 0x8E, 0x3C, 0x6A, 0x60, 0xD7,
|
||||||
|
0xF3, 0x56, 0x8E, 0xEC, 0x8D, 0x51, 0x8A, 0x63, 0x3C, 0x04, 0x78, 0x23, 0x0E, 0x90, 0x0C, 0xB4,
|
||||||
|
0xE7, 0x86, 0x3B, 0x4F, 0x8E, 0x13, 0x09, 0x47, 0x32, 0x0E, 0x04, 0xB8, 0x4D, 0x5B, 0xB0, 0x46,
|
||||||
|
0x71, 0xB0, 0x5C, 0xF4, 0xAD, 0x63, 0x4F, 0xC5, 0xE2, 0xAC, 0x1E, 0xC4, 0x33, 0x96, 0x09, 0x7B
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
26
build/CodeGen/Stage2/RunStage2.cs
Normal file
26
build/CodeGen/Stage2/RunStage2.cs
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using Octokit;
|
||||||
|
|
||||||
|
namespace LibHacBuild.CodeGen.Stage2
|
||||||
|
{
|
||||||
|
// Some codegen depends on classes in LibHac.
|
||||||
|
// The part that does is split out into a separate project so the main build project
|
||||||
|
// doesn't depend on LibHac.
|
||||||
|
public static class RunStage2
|
||||||
|
{
|
||||||
|
private const string SolutionFileName = "LibHac.sln";
|
||||||
|
public static int Main(string[] args)
|
||||||
|
{
|
||||||
|
if (!File.Exists(SolutionFileName))
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine($"Could not find the solution file {SolutionFileName}.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
KeysCodeGen.Run();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
27
build/CodeGen/_buildCodeGen.csproj
Normal file
27
build/CodeGen/_buildCodeGen.csproj
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||||
|
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||||
|
<RootNamespace>LibHacBuild.CodeGen</RootNamespace>
|
||||||
|
<IsPackable>False</IsPackable>
|
||||||
|
<NoWarn>CS0649;CS0169</NoWarn>
|
||||||
|
<EnableDefaultItems>false</EnableDefaultItems>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="*.cs" />
|
||||||
|
<Compile Include="Stage2\*.cs" />
|
||||||
|
<EmbeddedResource Include="*.txt" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Nuke.Common" Version="0.24.11" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\src\LibHac\LibHac.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
|
@ -22,6 +22,9 @@
|
||||||
<NukeExternalFiles Include="**\*.*.ext" Exclude="bin\**;obj\**" />
|
<NukeExternalFiles Include="**\*.*.ext" Exclude="bin\**;obj\**" />
|
||||||
<None Remove="*.csproj.DotSettings;*.ref.*.txt" />
|
<None Remove="*.csproj.DotSettings;*.ref.*.txt" />
|
||||||
<EmbeddedResource Include="CodeGen\*.csv" />
|
<EmbeddedResource Include="CodeGen\*.csv" />
|
||||||
|
<Compile Remove="CodeGen\Stage2\**" />
|
||||||
|
<Compile Remove="CodeGen\bin\**;CodeGen\obj\**" />
|
||||||
|
<None Remove="CodeGen\bin\**;CodeGen\obj\**" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -143,7 +143,7 @@ namespace LibHac.Boot
|
||||||
Result rc = _storage.Read(Package2Header.SignatureSize, metaBytes);
|
Result rc = _storage.Read(Package2Header.SignatureSize, metaBytes);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
return _header.VerifySignature(_keySet.Package2SigningKey.Modulus, metaBytes);
|
return _header.VerifySignature(_keySet.Package2SigningKeyParams.Modulus, metaBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
19
src/LibHac/Common/Keys/DefaultKeySet.Empty.cs
Normal file
19
src/LibHac/Common/Keys/DefaultKeySet.Empty.cs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace LibHac.Common.Keys
|
||||||
|
{
|
||||||
|
internal static partial class DefaultKeySet
|
||||||
|
{
|
||||||
|
private static ReadOnlySpan<byte> RootKeysDev => new byte[] { };
|
||||||
|
private static ReadOnlySpan<byte> RootKeysProd => new byte[] { };
|
||||||
|
private static ReadOnlySpan<byte> KeySeeds => new byte[] { };
|
||||||
|
private static ReadOnlySpan<byte> StoredKeysDev => new byte[] { };
|
||||||
|
private static ReadOnlySpan<byte> StoredKeysProd => new byte[] { };
|
||||||
|
private static ReadOnlySpan<byte> DerivedKeysDev => new byte[] { };
|
||||||
|
private static ReadOnlySpan<byte> DerivedKeysProd => new byte[] { };
|
||||||
|
private static ReadOnlySpan<byte> DeviceKeys => new byte[] { };
|
||||||
|
private static ReadOnlySpan<byte> RsaSigningKeysDev => new byte[] { };
|
||||||
|
private static ReadOnlySpan<byte> RsaSigningKeysProd => new byte[] { };
|
||||||
|
private static ReadOnlySpan<byte> RsaKeys => new byte[] { };
|
||||||
|
}
|
||||||
|
}
|
71
src/LibHac/Common/Keys/DefaultKeySet.cs
Normal file
71
src/LibHac/Common/Keys/DefaultKeySet.cs
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace LibHac.Common.Keys
|
||||||
|
{
|
||||||
|
internal static partial class DefaultKeySet
|
||||||
|
{
|
||||||
|
public static KeySet CreateDefaultKeySet()
|
||||||
|
{
|
||||||
|
var keySet = new KeySet();
|
||||||
|
|
||||||
|
// Fill the key set with any key structs included in the library.
|
||||||
|
if (RootKeysDev.Length == Unsafe.SizeOf<RootKeys>())
|
||||||
|
{
|
||||||
|
keySet.KeyStruct._rootKeysDev = MemoryMarshal.Cast<byte, RootKeys>(RootKeysDev)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (RootKeysProd.Length == Unsafe.SizeOf<RootKeys>())
|
||||||
|
{
|
||||||
|
keySet.KeyStruct._rootKeysProd = MemoryMarshal.Cast<byte, RootKeys>(RootKeysProd)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (KeySeeds.Length == Unsafe.SizeOf<KeySeeds>())
|
||||||
|
{
|
||||||
|
keySet.KeyStruct._keySeeds = MemoryMarshal.Cast<byte, KeySeeds>(KeySeeds)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StoredKeysDev.Length == Unsafe.SizeOf<StoredKeys>())
|
||||||
|
{
|
||||||
|
keySet.KeyStruct._storedKeysDev = MemoryMarshal.Cast<byte, StoredKeys>(StoredKeysDev)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StoredKeysProd.Length == Unsafe.SizeOf<StoredKeys>())
|
||||||
|
{
|
||||||
|
keySet.KeyStruct._storedKeysProd = MemoryMarshal.Cast<byte, StoredKeys>(StoredKeysProd)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DerivedKeysDev.Length == Unsafe.SizeOf<DerivedKeys>())
|
||||||
|
{
|
||||||
|
keySet.KeyStruct._derivedKeysDev = MemoryMarshal.Cast<byte, DerivedKeys>(DerivedKeysDev)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DerivedKeysProd.Length == Unsafe.SizeOf<DerivedKeys>())
|
||||||
|
{
|
||||||
|
keySet.KeyStruct._derivedKeysProd = MemoryMarshal.Cast<byte, DerivedKeys>(DerivedKeysProd)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DeviceKeys.Length == Unsafe.SizeOf<DeviceKeys>())
|
||||||
|
{
|
||||||
|
keySet.KeyStruct._deviceKeys = MemoryMarshal.Cast<byte, DeviceKeys>(DeviceKeys)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (RsaSigningKeysDev.Length == Unsafe.SizeOf<RsaSigningKeys>())
|
||||||
|
{
|
||||||
|
keySet.KeyStruct._rsaSigningKeysDev = MemoryMarshal.Cast<byte, RsaSigningKeys>(RsaSigningKeysDev)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (RsaSigningKeysProd.Length == Unsafe.SizeOf<RsaSigningKeys>())
|
||||||
|
{
|
||||||
|
keySet.KeyStruct._rsaSigningKeysProd = MemoryMarshal.Cast<byte, RsaSigningKeys>(RsaSigningKeysProd)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (RsaKeys.Length == Unsafe.SizeOf<RsaKeys>())
|
||||||
|
{
|
||||||
|
keySet.KeyStruct._rsaKeys = MemoryMarshal.Cast<byte, RsaKeys>(RsaKeys)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return keySet;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -192,17 +192,41 @@ namespace LibHac.Common.Keys
|
||||||
|
|
||||||
private const int TitleKeySize = 0x10;
|
private const int TitleKeySize = 0x10;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Loads keys from key files into an existing <see cref="KeySet"/>. Missing keys will be
|
||||||
|
/// derived from existing keys if possible. Any <see langword="null"/> file names will be skipped.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="keySet">The <see cref="KeySet"/> where the loaded keys will be placed.</param>
|
||||||
|
/// <param name="filename">The path of the file containing common keys. Can be <see langword="null"/>.</param>
|
||||||
|
/// <param name="titleKeysFilename">The path of the file containing title keys. Can be <see langword="null"/>.</param>
|
||||||
|
/// <param name="consoleKeysFilename">The path of the file containing device-unique keys. Can be <see langword="null"/>.</param>
|
||||||
|
/// <param name="logger">An optional logger that key-parsing errors will be written to.</param>
|
||||||
public static void ReadKeyFile(KeySet keySet, string filename, string titleKeysFilename = null,
|
public static void ReadKeyFile(KeySet keySet, string filename, string titleKeysFilename = null,
|
||||||
string consoleKeysFilename = null, IProgressReport logger = null)
|
string consoleKeysFilename = null, IProgressReport logger = null)
|
||||||
{
|
{
|
||||||
List<KeyInfo> keyInfos = CreateKeyList();
|
List<KeyInfo> keyInfos = CreateKeyList();
|
||||||
|
|
||||||
if (filename != null) ReadMainKeys(keySet, filename, keyInfos, logger);
|
if (filename != null)
|
||||||
if (consoleKeysFilename != null) ReadMainKeys(keySet, consoleKeysFilename, keyInfos, logger);
|
{
|
||||||
if (titleKeysFilename != null) ReadTitleKeys(keySet, titleKeysFilename, logger);
|
using var reader = new StreamReader(new FileStream(filename, FileMode.Open, FileAccess.Read));
|
||||||
|
ReadMainKeys(keySet, reader, keyInfos, logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (consoleKeysFilename != null)
|
||||||
|
{
|
||||||
|
using var reader = new StreamReader(new FileStream(consoleKeysFilename, FileMode.Open, FileAccess.Read));
|
||||||
|
ReadMainKeys(keySet, reader, keyInfos, logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (titleKeysFilename != null)
|
||||||
|
{
|
||||||
|
using var reader = new StreamReader(new FileStream(titleKeysFilename, FileMode.Open, FileAccess.Read));
|
||||||
|
ReadTitleKeys(keySet, reader, logger);
|
||||||
|
}
|
||||||
|
|
||||||
keySet.DeriveKeys(logger);
|
keySet.DeriveKeys(logger);
|
||||||
|
|
||||||
|
// Dev keys can read from prod key files, so derive any missing keys if necessary.
|
||||||
if (keySet.CurrentMode == KeySet.Mode.Prod)
|
if (keySet.CurrentMode == KeySet.Mode.Prod)
|
||||||
{
|
{
|
||||||
keySet.SetMode(KeySet.Mode.Dev);
|
keySet.SetMode(KeySet.Mode.Dev);
|
||||||
|
@ -211,10 +235,20 @@ namespace LibHac.Common.Keys
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="KeySet"/> initialized with the key files specified and any keys included in the library.
|
||||||
|
/// Missing keys will be derived from existing keys if possible. Any <see langword="null"/> file names will be skipped.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="filename">The path of the file containing common keys. Can be <see langword="null"/>.</param>
|
||||||
|
/// <param name="titleKeysFilename">The path of the file containing title keys. Can be <see langword="null"/>.</param>
|
||||||
|
/// <param name="consoleKeysFilename">The path of the file containing device-unique keys. Can be <see langword="null"/>.</param>
|
||||||
|
/// <param name="logger">An optional logger that key-parsing errors will be written to.</param>
|
||||||
|
/// <param name="mode">Specifies whether the keys being read are dev or prod keys.</param>
|
||||||
|
/// <returns>The created <see cref="KeySet"/>.</returns>
|
||||||
public static KeySet ReadKeyFile(string filename, string titleKeysFilename = null,
|
public static KeySet ReadKeyFile(string filename, string titleKeysFilename = null,
|
||||||
string consoleKeysFilename = null, IProgressReport logger = null, KeySet.Mode mode = KeySet.Mode.Prod)
|
string consoleKeysFilename = null, IProgressReport logger = null, KeySet.Mode mode = KeySet.Mode.Prod)
|
||||||
{
|
{
|
||||||
var keySet = new KeySet();
|
var keySet = KeySet.CreateDefaultKeySet();
|
||||||
keySet.SetMode(mode);
|
keySet.SetMode(mode);
|
||||||
|
|
||||||
ReadKeyFile(keySet, filename, titleKeysFilename, consoleKeysFilename, logger);
|
ReadKeyFile(keySet, filename, titleKeysFilename, consoleKeysFilename, logger);
|
||||||
|
@ -222,106 +256,119 @@ namespace LibHac.Common.Keys
|
||||||
return keySet;
|
return keySet;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ReadMainKeys(KeySet keySet, string filename, List<KeyInfo> keyList, IProgressReport logger = null)
|
/// <summary>
|
||||||
|
/// Loads non-title keys from a <see cref="TextReader"/> into an existing <see cref="KeySet"/>.
|
||||||
|
/// Missing keys will not be derived.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="keySet">The <see cref="KeySet"/> where the loaded keys will be placed.</param>
|
||||||
|
/// <param name="keyFileReader">A <see cref="TextReader"/> containing the keys to load.</param>
|
||||||
|
/// <param name="keyList">A list of all the keys that will be loaded into the key set.
|
||||||
|
/// <see cref="CreateKeyList"/> will create a list containing all loadable keys.</param>
|
||||||
|
/// <param name="logger">An optional logger that key-parsing errors will be written to.</param>
|
||||||
|
public static void ReadMainKeys(KeySet keySet, TextReader keyFileReader, List<KeyInfo> keyList,
|
||||||
|
IProgressReport logger = null)
|
||||||
{
|
{
|
||||||
if (filename == null) return;
|
if (keyFileReader == null) return;
|
||||||
|
|
||||||
using (var reader = new StreamReader(new FileStream(filename, FileMode.Open, FileAccess.Read)))
|
// Todo: Improve key parsing
|
||||||
|
string line;
|
||||||
|
while ((line = keyFileReader.ReadLine()) != null)
|
||||||
{
|
{
|
||||||
string line;
|
string[] a = line.Split(',', '=');
|
||||||
while ((line = reader.ReadLine()) != null)
|
if (a.Length != 2) continue;
|
||||||
|
|
||||||
|
string keyName = a[0].Trim();
|
||||||
|
string valueStr = a[1].Trim();
|
||||||
|
|
||||||
|
if (!TryGetKeyInfo(out SpecificKeyInfo info, keyList, keyName))
|
||||||
{
|
{
|
||||||
string[] a = line.Split(',', '=');
|
logger?.LogMessage($"Failed to match key {keyName}");
|
||||||
if (a.Length != 2) continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
string keyName = a[0].Trim();
|
Span<byte> key;
|
||||||
string valueStr = a[1].Trim();
|
|
||||||
|
|
||||||
if (!TryGetKeyInfo(out SpecificKeyInfo info, keyList, keyName))
|
// Get the dev key in the key set if needed.
|
||||||
{
|
if (info.IsDev && keySet.CurrentMode == KeySet.Mode.Prod)
|
||||||
logger?.LogMessage($"Failed to match key {keyName}");
|
{
|
||||||
continue;
|
keySet.SetMode(KeySet.Mode.Dev);
|
||||||
}
|
key = info.Key.Getter(keySet, info.Index);
|
||||||
|
keySet.SetMode(KeySet.Mode.Prod);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
key = info.Key.Getter(keySet, info.Index);
|
||||||
|
}
|
||||||
|
|
||||||
Span<byte> key;
|
if (valueStr.Length != key.Length * 2)
|
||||||
|
{
|
||||||
|
logger?.LogMessage(
|
||||||
|
$"Key {keyName} had incorrect size {valueStr.Length}. Must be {key.Length * 2} hex digits.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Get the dev key in the key set if needed.
|
if (!Utilities.TryToBytes(valueStr, key))
|
||||||
if (info.IsDev && keySet.CurrentMode == KeySet.Mode.Prod)
|
{
|
||||||
{
|
key.Clear();
|
||||||
keySet.SetMode(KeySet.Mode.Dev);
|
|
||||||
key = info.Key.Getter(keySet, info.Index);
|
|
||||||
keySet.SetMode(KeySet.Mode.Prod);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
key = info.Key.Getter(keySet, info.Index);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (valueStr.Length != key.Length * 2)
|
logger?.LogMessage($"Key {keyName} had an invalid value. Must be {key.Length * 2} hex digits.");
|
||||||
{
|
|
||||||
logger?.LogMessage($"Key {keyName} had incorrect size {valueStr.Length}. Must be {key.Length * 2} hex digits.");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Utilities.TryToBytes(valueStr, key))
|
|
||||||
{
|
|
||||||
key.Clear();
|
|
||||||
|
|
||||||
logger?.LogMessage($"Key {keyName} had an invalid value. Must be {key.Length * 2} hex digits.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ReadTitleKeys(KeySet keySet, string filename, IProgressReport progress = null)
|
/// <summary>
|
||||||
|
/// Loads title keys from a <see cref="TextReader"/> into an existing <see cref="KeySet"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="keySet">The <see cref="KeySet"/> where the loaded keys will be placed.</param>
|
||||||
|
/// <param name="keyFileReader">A <see cref="TextReader"/> containing the keys to load.</param>
|
||||||
|
/// <param name="logger">An optional logger that key-parsing errors will be written to.</param>
|
||||||
|
public static void ReadTitleKeys(KeySet keySet, TextReader keyFileReader, IProgressReport logger = null)
|
||||||
{
|
{
|
||||||
if (filename == null) return;
|
if (keyFileReader == null) return;
|
||||||
|
|
||||||
using (var reader = new StreamReader(new FileStream(filename, FileMode.Open, FileAccess.Read)))
|
// Todo: Improve key parsing
|
||||||
|
string line;
|
||||||
|
while ((line = keyFileReader.ReadLine()) != null)
|
||||||
{
|
{
|
||||||
string line;
|
string[] splitLine;
|
||||||
while ((line = reader.ReadLine()) != null)
|
|
||||||
|
// Some people use pipes as delimiters
|
||||||
|
if (line.Contains('|'))
|
||||||
{
|
{
|
||||||
string[] splitLine;
|
splitLine = line.Split('|');
|
||||||
|
|
||||||
// Some people use pipes as delimiters
|
|
||||||
if (line.Contains('|'))
|
|
||||||
{
|
|
||||||
splitLine = line.Split('|');
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
splitLine = line.Split(',', '=');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (splitLine.Length < 2) continue;
|
|
||||||
|
|
||||||
if (!splitLine[0].Trim().TryToBytes(out byte[] rightsId))
|
|
||||||
{
|
|
||||||
progress?.LogMessage($"Invalid rights ID \"{splitLine[0].Trim()}\" in title key file");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!splitLine[1].Trim().TryToBytes(out byte[] titleKey))
|
|
||||||
{
|
|
||||||
progress?.LogMessage($"Invalid title key \"{splitLine[1].Trim()}\" in title key file");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rightsId.Length != TitleKeySize)
|
|
||||||
{
|
|
||||||
progress?.LogMessage($"Rights ID {rightsId.ToHexString()} had incorrect size {rightsId.Length}. (Expected {TitleKeySize})");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (titleKey.Length != TitleKeySize)
|
|
||||||
{
|
|
||||||
progress?.LogMessage($"Title key {titleKey.ToHexString()} had incorrect size {titleKey.Length}. (Expected {TitleKeySize})");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
keySet.ExternalKeySet.Add(new RightsId(rightsId), new AccessKey(titleKey)).ThrowIfFailure();
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
splitLine = line.Split(',', '=');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (splitLine.Length < 2) continue;
|
||||||
|
|
||||||
|
if (!splitLine[0].Trim().TryToBytes(out byte[] rightsId))
|
||||||
|
{
|
||||||
|
logger?.LogMessage($"Invalid rights ID \"{splitLine[0].Trim()}\" in title key file");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!splitLine[1].Trim().TryToBytes(out byte[] titleKey))
|
||||||
|
{
|
||||||
|
logger?.LogMessage($"Invalid title key \"{splitLine[1].Trim()}\" in title key file");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rightsId.Length != TitleKeySize)
|
||||||
|
{
|
||||||
|
logger?.LogMessage($"Rights ID {rightsId.ToHexString()} had incorrect size {rightsId.Length}. (Expected {TitleKeySize})");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (titleKey.Length != TitleKeySize)
|
||||||
|
{
|
||||||
|
logger?.LogMessage($"Title key {titleKey.ToHexString()} had incorrect size {titleKey.Length}. (Expected {TitleKeySize})");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
keySet.ExternalKeySet.Add(new RightsId(rightsId), new AccessKey(titleKey)).ThrowIfFailure();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
396
src/LibHac/Common/Keys/KeyDerivation.cs
Normal file
396
src/LibHac/Common/Keys/KeyDerivation.cs
Normal file
|
@ -0,0 +1,396 @@
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using LibHac.Crypto;
|
||||||
|
|
||||||
|
namespace LibHac.Common.Keys
|
||||||
|
{
|
||||||
|
internal static class KeyDerivation
|
||||||
|
{
|
||||||
|
public static void DeriveAllKeys(KeySet keySet, IProgressReport logger = null)
|
||||||
|
{
|
||||||
|
DeriveKeyBlobKeys(keySet);
|
||||||
|
DecryptKeyBlobs(keySet, logger);
|
||||||
|
ReadKeyBlobs(keySet);
|
||||||
|
|
||||||
|
Derive620Keys(keySet);
|
||||||
|
Derive620MasterKeks(keySet);
|
||||||
|
DeriveMarikoMasterKeks(keySet);
|
||||||
|
DeriveMasterKeys(keySet);
|
||||||
|
PopulateOldMasterKeys(keySet);
|
||||||
|
|
||||||
|
DerivePerConsoleKeys(keySet);
|
||||||
|
DerivePerFirmwareKeys(keySet);
|
||||||
|
DeriveNcaHeaderKey(keySet);
|
||||||
|
DeriveSdCardKeys(keySet);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DeriveKeyBlobKeys(KeySet s)
|
||||||
|
{
|
||||||
|
if (s.SecureBootKey.IsEmpty() || s.TsecKey.IsEmpty()) return;
|
||||||
|
|
||||||
|
bool haveKeyBlobMacKeySource = !s.MasterKeySource.IsEmpty();
|
||||||
|
var temp = new AesKey();
|
||||||
|
|
||||||
|
for (int i = 0; i < KeySet.UsedKeyBlobCount; i++)
|
||||||
|
{
|
||||||
|
if (s.KeyBlobKeySources[i].IsEmpty()) continue;
|
||||||
|
|
||||||
|
Aes.DecryptEcb128(s.KeyBlobKeySources[i], temp, s.TsecKey);
|
||||||
|
Aes.DecryptEcb128(temp, s.KeyBlobKeys[i], s.SecureBootKey);
|
||||||
|
|
||||||
|
if (!haveKeyBlobMacKeySource) continue;
|
||||||
|
|
||||||
|
Aes.DecryptEcb128(s.KeyBlobMacKeySource, s.KeyBlobMacKeys[i], s.KeyBlobKeys[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DecryptKeyBlobs(KeySet s, IProgressReport logger = null)
|
||||||
|
{
|
||||||
|
var cmac = new AesCmac();
|
||||||
|
|
||||||
|
for (int i = 0; i < KeySet.UsedKeyBlobCount; i++)
|
||||||
|
{
|
||||||
|
if (s.KeyBlobKeys[i].IsEmpty() || s.KeyBlobMacKeys[i].IsEmpty() || s.EncryptedKeyBlobs[i].IsEmpty())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Aes.CalculateCmac(cmac, s.EncryptedKeyBlobs[i].Bytes.Slice(0x10, 0xA0), s.KeyBlobMacKeys[i]);
|
||||||
|
|
||||||
|
if (!Utilities.SpansEqual<byte>(cmac, s.EncryptedKeyBlobs[i].Cmac))
|
||||||
|
{
|
||||||
|
logger?.LogMessage($"Warning: Keyblob MAC {i:x2} is invalid. Are SBK/TSEC key correct?");
|
||||||
|
}
|
||||||
|
|
||||||
|
Aes.DecryptCtr128(s.EncryptedKeyBlobs[i].Bytes.Slice(0x20), s.KeyBlobs[i].Bytes, s.KeyBlobKeys[i],
|
||||||
|
s.EncryptedKeyBlobs[i].Counter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ReadKeyBlobs(KeySet s)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < KeySet.UsedKeyBlobCount; i++)
|
||||||
|
{
|
||||||
|
if (s.KeyBlobs[i].IsEmpty()) continue;
|
||||||
|
|
||||||
|
s.MasterKeks[i] = s.KeyBlobs[i].MasterKek;
|
||||||
|
s.Package1Keys[i] = s.KeyBlobs[i].Package1Key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void Derive620Keys(KeySet s)
|
||||||
|
{
|
||||||
|
bool haveTsecRootKek = !s.TsecRootKek.IsEmpty();
|
||||||
|
bool havePackage1MacKek = !s.Package1MacKek.IsEmpty();
|
||||||
|
bool havePackage1Kek = !s.Package1Kek.IsEmpty();
|
||||||
|
|
||||||
|
for (int i = KeySet.UsedKeyBlobCount; i < KeySet.KeyRevisionCount; i++)
|
||||||
|
{
|
||||||
|
if (s.TsecAuthSignatures[i - KeySet.UsedKeyBlobCount].IsEmpty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (haveTsecRootKek)
|
||||||
|
{
|
||||||
|
Aes.EncryptEcb128(s.TsecAuthSignatures[i - KeySet.UsedKeyBlobCount],
|
||||||
|
s.TsecRootKeys[i - KeySet.UsedKeyBlobCount], s.TsecRootKek);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (havePackage1MacKek)
|
||||||
|
{
|
||||||
|
Aes.EncryptEcb128(s.TsecAuthSignatures[i - KeySet.UsedKeyBlobCount], s.Package1MacKeys[i],
|
||||||
|
s.Package1MacKek);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (havePackage1Kek)
|
||||||
|
{
|
||||||
|
Aes.EncryptEcb128(s.TsecAuthSignatures[i - KeySet.UsedKeyBlobCount], s.Package1Keys[i], s.Package1Kek);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void Derive620MasterKeks(KeySet s)
|
||||||
|
{
|
||||||
|
for (int i = KeySet.UsedKeyBlobCount; i < KeySet.KeyRevisionCount; i++)
|
||||||
|
{
|
||||||
|
// Key revisions >= 8 all use the same TSEC root key
|
||||||
|
int tsecRootKeyIndex = Math.Min(i, 8) - KeySet.UsedKeyBlobCount;
|
||||||
|
if (s.TsecRootKeys[tsecRootKeyIndex].IsEmpty() || s.MasterKekSources[i].IsEmpty()) continue;
|
||||||
|
|
||||||
|
Aes.DecryptEcb128(s.MasterKekSources[i], s.MasterKeks[i], s.TsecRootKeys[tsecRootKeyIndex]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DeriveMarikoMasterKeks(KeySet s)
|
||||||
|
{
|
||||||
|
if (s.MarikoKek.IsEmpty()) return;
|
||||||
|
|
||||||
|
for (int i = 0; i < KeySet.KeyRevisionCount; i++)
|
||||||
|
{
|
||||||
|
if (s.MarikoMasterKekSources[i].IsEmpty()) continue;
|
||||||
|
|
||||||
|
Aes.DecryptEcb128(s.MarikoMasterKekSources[i], s.MasterKeks[i], s.MarikoKek);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DeriveMasterKeys(KeySet s)
|
||||||
|
{
|
||||||
|
if (s.MasterKeySource.IsEmpty()) return;
|
||||||
|
|
||||||
|
for (int i = 0; i < KeySet.KeyRevisionCount; i++)
|
||||||
|
{
|
||||||
|
if (s.MasterKeks[i].IsEmpty()) continue;
|
||||||
|
|
||||||
|
Aes.DecryptEcb128(s.MasterKeySource, s.MasterKeys[i], s.MasterKeks[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void PopulateOldMasterKeys(KeySet s)
|
||||||
|
{
|
||||||
|
ReadOnlySpan<AesKey> keyVectors = MasterKeyVectors(s);
|
||||||
|
|
||||||
|
// Find the newest master key we have
|
||||||
|
int newestMasterKey = -1;
|
||||||
|
|
||||||
|
for (int i = keyVectors.Length - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
if (!s.MasterKeys[i].IsEmpty())
|
||||||
|
{
|
||||||
|
newestMasterKey = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newestMasterKey == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Don't populate old master keys unless the newest master key is valid
|
||||||
|
if (!TestKeyGeneration(s, newestMasterKey))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Decrypt all previous master keys
|
||||||
|
for (int i = newestMasterKey; i > 0; i--)
|
||||||
|
{
|
||||||
|
Aes.DecryptEcb128(keyVectors[i], s.MasterKeys[i - 1], s.MasterKeys[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if the master key of the specified generation is correct.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="s">The <see cref="KeySet"/> to test.</param>
|
||||||
|
/// <param name="generation">The generation to test.</param>
|
||||||
|
/// <returns><see langword="true"/> if the key is correct.</returns>
|
||||||
|
private static bool TestKeyGeneration(KeySet s, int generation)
|
||||||
|
{
|
||||||
|
ReadOnlySpan<AesKey> keyVectors = MasterKeyVectors(s);
|
||||||
|
|
||||||
|
// Decrypt the vector chain until we get Master Key 0
|
||||||
|
AesKey key = s.MasterKeys[generation];
|
||||||
|
|
||||||
|
for (int i = generation; i > 0; i--)
|
||||||
|
{
|
||||||
|
Aes.DecryptEcb128(keyVectors[i], key, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrypt the zeros with Master Key 0
|
||||||
|
Aes.DecryptEcb128(keyVectors[0], key, key);
|
||||||
|
|
||||||
|
// If we don't get zeros, MasterKeys[generation] is incorrect
|
||||||
|
return key.IsEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ReadOnlySpan<AesKey> MasterKeyVectors(KeySet s) =>
|
||||||
|
MemoryMarshal.Cast<byte, AesKey>(s.CurrentMode == KeySet.Mode.Dev
|
||||||
|
? MasterKeyVectorsDev
|
||||||
|
: MasterKeyVectorsProd);
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> MasterKeyVectorsDev => new byte[]
|
||||||
|
{
|
||||||
|
0x46, 0x22, 0xB4, 0x51, 0x9A, 0x7E, 0xA7, 0x7F, 0x62, 0xA1, 0x1F, 0x8F, 0xC5, 0x3A, 0xDB, 0xFE, // Zeroes encrypted with Master Key 00.
|
||||||
|
0x39, 0x33, 0xF9, 0x31, 0xBA, 0xE4, 0xA7, 0x21, 0x2C, 0xDD, 0xB7, 0xD8, 0xB4, 0x4E, 0x37, 0x23, // Master key 00 encrypted with Master key 01.
|
||||||
|
0x97, 0x29, 0xB0, 0x32, 0x43, 0x14, 0x8C, 0xA6, 0x85, 0xE9, 0x5A, 0x94, 0x99, 0x39, 0xAC, 0x5D, // Master key 01 encrypted with Master key 02.
|
||||||
|
0x2C, 0xCA, 0x9C, 0x31, 0x1E, 0x07, 0xB0, 0x02, 0x97, 0x0A, 0xD8, 0x03, 0xA2, 0x76, 0x3F, 0xA3, // Master key 02 encrypted with Master key 03.
|
||||||
|
0x9B, 0x84, 0x76, 0x14, 0x72, 0x94, 0x52, 0xCB, 0x54, 0x92, 0x9B, 0xC4, 0x8C, 0x5B, 0x0F, 0xBA, // Master key 03 encrypted with Master key 04.
|
||||||
|
0x78, 0xD5, 0xF1, 0x20, 0x3D, 0x16, 0xE9, 0x30, 0x32, 0x27, 0x34, 0x6F, 0xCF, 0xE0, 0x27, 0xDC, // Master key 04 encrypted with Master key 05.
|
||||||
|
0x6F, 0xD2, 0x84, 0x1D, 0x05, 0xEC, 0x40, 0x94, 0x5F, 0x18, 0xB3, 0x81, 0x09, 0x98, 0x8D, 0x4E, // Master key 05 encrypted with Master key 06.
|
||||||
|
0x37, 0xAF, 0xAB, 0x35, 0x79, 0x09, 0xD9, 0x48, 0x29, 0xD2, 0xDB, 0xA5, 0xA5, 0xF5, 0x30, 0x19, // Master key 06 encrypted with Master key 07.
|
||||||
|
0xEC, 0xE1, 0x46, 0x89, 0x37, 0xFD, 0xD2, 0x15, 0x8C, 0x3F, 0x24, 0x82, 0xEF, 0x49, 0x68, 0x04, // Master key 07 encrypted with Master key 08.
|
||||||
|
0x43, 0x3D, 0xC5, 0x3B, 0xEF, 0x91, 0x02, 0x21, 0x61, 0x54, 0x63, 0x8A, 0x35, 0xE7, 0xCA, 0xEE, // Master key 08 encrypted with Master key 09.
|
||||||
|
0x6C, 0x2E, 0xCD, 0xB3, 0x34, 0x61, 0x77, 0xF5, 0xF9, 0xB1, 0xDD, 0x61, 0x98, 0x19, 0x3E, 0xD4 // Master key 09 encrypted with Master key 0A.
|
||||||
|
};
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> MasterKeyVectorsProd => new byte[]
|
||||||
|
{
|
||||||
|
0x0C, 0xF0, 0x59, 0xAC, 0x85, 0xF6, 0x26, 0x65, 0xE1, 0xE9, 0x19, 0x55, 0xE6, 0xF2, 0x67, 0x3D, // Zeroes encrypted with Master Key 00.
|
||||||
|
0x29, 0x4C, 0x04, 0xC8, 0xEB, 0x10, 0xED, 0x9D, 0x51, 0x64, 0x97, 0xFB, 0xF3, 0x4D, 0x50, 0xDD, // Master key 00 encrypted with Master key 01.
|
||||||
|
0xDE, 0xCF, 0xEB, 0xEB, 0x10, 0xAE, 0x74, 0xD8, 0xAD, 0x7C, 0xF4, 0x9E, 0x62, 0xE0, 0xE8, 0x72, // Master key 01 encrypted with Master key 02.
|
||||||
|
0x0A, 0x0D, 0xDF, 0x34, 0x22, 0x06, 0x6C, 0xA4, 0xE6, 0xB1, 0xEC, 0x71, 0x85, 0xCA, 0x4E, 0x07, // Master key 02 encrypted with Master key 03.
|
||||||
|
0x6E, 0x7D, 0x2D, 0xC3, 0x0F, 0x59, 0xC8, 0xFA, 0x87, 0xA8, 0x2E, 0xD5, 0x89, 0x5E, 0xF3, 0xE9, // Master key 03 encrypted with Master key 04.
|
||||||
|
0xEB, 0xF5, 0x6F, 0x83, 0x61, 0x9E, 0xF8, 0xFA, 0xE0, 0x87, 0xD7, 0xA1, 0x4E, 0x25, 0x36, 0xEE, // Master key 04 encrypted with Master key 05.
|
||||||
|
0x1E, 0x1E, 0x22, 0xC0, 0x5A, 0x33, 0x3C, 0xB9, 0x0B, 0xA9, 0x03, 0x04, 0xBA, 0xDB, 0x07, 0x57, // Master key 05 encrypted with Master key 06.
|
||||||
|
0xA4, 0xD4, 0x52, 0x6F, 0xD1, 0xE4, 0x36, 0xAA, 0x9F, 0xCB, 0x61, 0x27, 0x1C, 0x67, 0x65, 0x1F, // Master key 06 encrypted with Master key 07.
|
||||||
|
0xEA, 0x60, 0xB3, 0xEA, 0xCE, 0x8F, 0x24, 0x46, 0x7D, 0x33, 0x9C, 0xD1, 0xBC, 0x24, 0x98, 0x29, // Master key 07 encrypted with Master key 08.
|
||||||
|
0x4D, 0xD9, 0x98, 0x42, 0x45, 0x0D, 0xB1, 0x3C, 0x52, 0x0C, 0x9A, 0x44, 0xBB, 0xAD, 0xAF, 0x80, // Master key 08 encrypted with Master key 09.
|
||||||
|
0xB8, 0x96, 0x9E, 0x4A, 0x00, 0x0D, 0xD6, 0x28, 0xB3, 0xD1, 0xDB, 0x68, 0x5F, 0xFB, 0xE1, 0x2A // Master key 09 encrypted with Master key 0A.
|
||||||
|
};
|
||||||
|
|
||||||
|
private static void DerivePerConsoleKeys(KeySet s)
|
||||||
|
{
|
||||||
|
// Todo: Dev and newer key generations
|
||||||
|
var kek = new AesKey();
|
||||||
|
|
||||||
|
// Derive the device key
|
||||||
|
if (!s.PerConsoleKeySource.IsEmpty() && !s.KeyBlobKeys[0].IsEmpty())
|
||||||
|
{
|
||||||
|
Aes.DecryptEcb128(s.PerConsoleKeySource, s.DeviceKey, s.KeyBlobKeys[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Derive device-unique save keys
|
||||||
|
for (int i = 0; i < s.DeviceUniqueSaveMacKeySources.Length; i++)
|
||||||
|
{
|
||||||
|
if (!s.DeviceUniqueSaveMacKekSource.IsEmpty() && !s.DeviceUniqueSaveMacKeySources[i].IsEmpty() &&
|
||||||
|
!s.DeviceKey.IsEmpty())
|
||||||
|
{
|
||||||
|
GenerateKek(s.DeviceKey, s.DeviceUniqueSaveMacKekSource, kek, s.AesKekGenerationSource, null);
|
||||||
|
Aes.DecryptEcb128(s.DeviceUniqueSaveMacKeySources[i], s.DeviceUniqueSaveMacKeys[i], kek);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Derive BIS keys
|
||||||
|
if (s.DeviceKey.IsEmpty()
|
||||||
|
|| s.BisKekSource.IsEmpty()
|
||||||
|
|| s.AesKekGenerationSource.IsEmpty()
|
||||||
|
|| s.AesKeyGenerationSource.IsEmpty()
|
||||||
|
|| s.RetailSpecificAesKeySource.IsEmpty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the user doesn't provide bis_key_source_03 we can assume it's the same as bis_key_source_02
|
||||||
|
if (s.BisKeySources[3].IsEmpty() && !s.BisKeySources[2].IsEmpty())
|
||||||
|
{
|
||||||
|
s.BisKeySources[3] = s.BisKeySources[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
Aes.DecryptEcb128(s.RetailSpecificAesKeySource, kek, s.DeviceKey);
|
||||||
|
if (!s.BisKeySources[0].IsEmpty()) Aes.DecryptEcb128(s.BisKeySources[0], s.BisKeys[0], kek);
|
||||||
|
|
||||||
|
GenerateKek(s.DeviceKey, s.BisKekSource, kek, s.AesKekGenerationSource, s.AesKeyGenerationSource);
|
||||||
|
|
||||||
|
for (int i = 1; i < 4; i++)
|
||||||
|
{
|
||||||
|
if (!s.BisKeySources[i].IsEmpty())
|
||||||
|
Aes.DecryptEcb128(s.BisKeySources[i], s.BisKeys[i], kek);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DerivePerFirmwareKeys(KeySet s)
|
||||||
|
{
|
||||||
|
bool haveKakSource0 = !s.KeyAreaKeyApplicationSource.IsEmpty();
|
||||||
|
bool haveKakSource1 = !s.KeyAreaKeyOceanSource.IsEmpty();
|
||||||
|
bool haveKakSource2 = !s.KeyAreaKeySystemSource.IsEmpty();
|
||||||
|
bool haveTitleKekSource = !s.TitleKekSource.IsEmpty();
|
||||||
|
bool havePackage2KeySource = !s.Package2KeySource.IsEmpty();
|
||||||
|
|
||||||
|
for (int i = 0; i < KeySet.KeyRevisionCount; i++)
|
||||||
|
{
|
||||||
|
if (s.MasterKeys[i].IsEmpty())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (haveKakSource0)
|
||||||
|
{
|
||||||
|
GenerateKek(s.MasterKeys[i], s.KeyAreaKeyApplicationSource, s.KeyAreaKeys[i][0],
|
||||||
|
s.AesKekGenerationSource, s.AesKeyGenerationSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (haveKakSource1)
|
||||||
|
{
|
||||||
|
GenerateKek(s.MasterKeys[i], s.KeyAreaKeyOceanSource, s.KeyAreaKeys[i][1], s.AesKekGenerationSource,
|
||||||
|
s.AesKeyGenerationSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (haveKakSource2)
|
||||||
|
{
|
||||||
|
GenerateKek(s.MasterKeys[i], s.KeyAreaKeySystemSource, s.KeyAreaKeys[i][2],
|
||||||
|
s.AesKekGenerationSource, s.AesKeyGenerationSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (haveTitleKekSource)
|
||||||
|
{
|
||||||
|
Aes.DecryptEcb128(s.TitleKekSource, s.TitleKeks[i], s.MasterKeys[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (havePackage2KeySource)
|
||||||
|
{
|
||||||
|
Aes.DecryptEcb128(s.Package2KeySource, s.Package2Keys[i], s.MasterKeys[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DeriveNcaHeaderKey(KeySet s)
|
||||||
|
{
|
||||||
|
if (s.HeaderKekSource.IsEmpty() || s.HeaderKeySource.IsEmpty() || s.MasterKeys[0].IsEmpty()) return;
|
||||||
|
|
||||||
|
var headerKek = new AesKey();
|
||||||
|
|
||||||
|
GenerateKek(s.MasterKeys[0], s.HeaderKekSource, headerKek, s.AesKekGenerationSource,
|
||||||
|
s.AesKeyGenerationSource);
|
||||||
|
Aes.DecryptEcb128(s.HeaderKeySource, s.HeaderKey, headerKek);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void DeriveSdCardKeys(KeySet s)
|
||||||
|
{
|
||||||
|
var sdKek = new AesKey();
|
||||||
|
var tempKey = new AesXtsKey();
|
||||||
|
GenerateKek(s.MasterKeys[0], s.SdCardKekSource, sdKek, s.AesKekGenerationSource, s.AesKeyGenerationSource);
|
||||||
|
|
||||||
|
for (int k = 0; k < KeySet.SdCardKeyIdCount; k++)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
tempKey.Data64[i] = s.SdCardKeySources[k].Data64[i] ^ s.SdCardEncryptionSeed.Data64[i & 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
tempKey.Data64[0] = s.SdCardKeySources[k].Data64[0] ^ s.SdCardEncryptionSeed.Data64[0];
|
||||||
|
tempKey.Data64[1] = s.SdCardKeySources[k].Data64[1] ^ s.SdCardEncryptionSeed.Data64[1];
|
||||||
|
tempKey.Data64[2] = s.SdCardKeySources[k].Data64[2] ^ s.SdCardEncryptionSeed.Data64[0];
|
||||||
|
tempKey.Data64[3] = s.SdCardKeySources[k].Data64[3] ^ s.SdCardEncryptionSeed.Data64[1];
|
||||||
|
|
||||||
|
Aes.DecryptEcb128(tempKey, s.SdCardEncryptionKeys[k], sdKek);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Derive sd card save key
|
||||||
|
if (!s.SeedUniqueSaveMacKekSource.IsEmpty() && !s.SeedUniqueSaveMacKeySource.IsEmpty())
|
||||||
|
{
|
||||||
|
var keySource = new AesKey();
|
||||||
|
|
||||||
|
keySource.Data64[0] = s.SeedUniqueSaveMacKeySource.Data64[0] ^ s.SdCardEncryptionSeed.Data64[0];
|
||||||
|
keySource.Data64[1] = s.SeedUniqueSaveMacKeySource.Data64[1] ^ s.SdCardEncryptionSeed.Data64[1];
|
||||||
|
|
||||||
|
GenerateKek(s.MasterKeys[0], s.SeedUniqueSaveMacKekSource, sdKek, s.AesKekGenerationSource, null);
|
||||||
|
Aes.DecryptEcb128(keySource, s.SeedUniqueSaveMacKey, sdKek);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void GenerateKek(ReadOnlySpan<byte> key, ReadOnlySpan<byte> src, Span<byte> dest,
|
||||||
|
ReadOnlySpan<byte> kekSeed, ReadOnlySpan<byte> keySeed)
|
||||||
|
{
|
||||||
|
var kek = new AesKey();
|
||||||
|
var srcKek = new AesKey();
|
||||||
|
|
||||||
|
Aes.DecryptEcb128(kekSeed, kek, key);
|
||||||
|
Aes.DecryptEcb128(src, srcKek, kek);
|
||||||
|
|
||||||
|
if (!keySeed.IsEmpty)
|
||||||
|
{
|
||||||
|
Aes.DecryptEcb128(keySeed, dest, srcKek);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
srcKek.Data.CopyTo(dest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,7 +5,7 @@ using LibHac.Boot;
|
||||||
using LibHac.Common.FixedArrays;
|
using LibHac.Common.FixedArrays;
|
||||||
using LibHac.Crypto;
|
using LibHac.Crypto;
|
||||||
using LibHac.FsSrv;
|
using LibHac.FsSrv;
|
||||||
using Aes = LibHac.Crypto.Aes;
|
using LibHac.Util;
|
||||||
|
|
||||||
namespace LibHac.Common.Keys
|
namespace LibHac.Common.Keys
|
||||||
{
|
{
|
||||||
|
@ -20,9 +20,9 @@ namespace LibHac.Common.Keys
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The number of keyblobs that were used for < 6.2.0 crypto
|
/// The number of keyblobs that were used for < 6.2.0 crypto
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const int UsedKeyBlobCount = 6;
|
internal const int UsedKeyBlobCount = 6;
|
||||||
private const int SdCardKeyIdCount = 3;
|
internal const int SdCardKeyIdCount = 3;
|
||||||
private const int KeyRevisionCount = 0x20;
|
internal const int KeyRevisionCount = 0x20;
|
||||||
|
|
||||||
private AllKeys _keys;
|
private AllKeys _keys;
|
||||||
private Mode _mode = Mode.Prod;
|
private Mode _mode = Mode.Prod;
|
||||||
|
@ -105,75 +105,79 @@ namespace LibHac.Common.Keys
|
||||||
// Todo: Make a separate type? Not actually an AES-XTS key, but it's still the same shape.
|
// Todo: Make a separate type? Not actually an AES-XTS key, but it's still the same shape.
|
||||||
public Span<AesXtsKey> SdCardEncryptionKeys => _keys._deviceKeys.SdCardEncryptionKeys.Items;
|
public Span<AesXtsKey> SdCardEncryptionKeys => _keys._deviceKeys.SdCardEncryptionKeys.Items;
|
||||||
|
|
||||||
|
public Span<RsaKey> NcaHeaderSigningKeys => RsaSigningKeys.NcaHeaderSigningKeys.Items;
|
||||||
|
public Span<RsaKey> AcidSigningKeys => RsaSigningKeys.AcidSigningKeys.Items;
|
||||||
|
public ref RsaKey Package2SigningKey => ref RsaSigningKeys.Package2SigningKey;
|
||||||
|
public ref RsaFullKey BetaNca0KeyAreaKey => ref RsaKeys.BetaNca0KeyAreaKey;
|
||||||
|
|
||||||
private RsaSigningKeyParameters _rsaSigningKeyParamsDev;
|
private RsaSigningKeyParameters _rsaSigningKeyParamsDev;
|
||||||
private RsaSigningKeyParameters _rsaSigningKeyParamsProd;
|
private RsaSigningKeyParameters _rsaSigningKeyParamsProd;
|
||||||
private RsaKeyParameters _rsaKeyParams;
|
private RsaKeyParameters _rsaKeyParams;
|
||||||
|
|
||||||
public RSAParameters ETicketExtKeyRsa { get; set; }
|
public RSAParameters ETicketExtKeyRsa { get; set; }
|
||||||
|
|
||||||
public Span<RSAParameters> NcaHeaderSigningKeys
|
public Span<RSAParameters> NcaHeaderSigningKeyParams
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
ref Array2<RSAParameters>? keys = ref RsaSigningKeyParams.NcaHeaderSigningKeys;
|
ref Optional<Array2<RSAParameters>> keys = ref RsaSigningKeyParams.NcaHeaderSigningKeys;
|
||||||
|
|
||||||
if (keys is null)
|
if (!keys.HasValue)
|
||||||
{
|
{
|
||||||
keys = new Array2<RSAParameters>();
|
keys.Set(new Array2<RSAParameters>());
|
||||||
keys.Value[0] = CreateRsaParameters(in RsaSigningKeys.NcaHeaderSigningKeys[0]);
|
keys.Value[0] = CreateRsaParameters(in NcaHeaderSigningKeys[0]);
|
||||||
keys.Value[1] = CreateRsaParameters(in RsaSigningKeys.NcaHeaderSigningKeys[1]);
|
keys.Value[1] = CreateRsaParameters(in NcaHeaderSigningKeys[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return keys.Value.Items;
|
return keys.Value.Items;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Span<RSAParameters> AcidSigningKeys
|
public Span<RSAParameters> AcidSigningKeyParams
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
ref Array2<RSAParameters>? keys = ref RsaSigningKeyParams.AcidSigningKeys;
|
ref Optional<Array2<RSAParameters>> keys = ref RsaSigningKeyParams.AcidSigningKeys;
|
||||||
|
|
||||||
if (keys is null)
|
if (!keys.HasValue)
|
||||||
{
|
{
|
||||||
keys = new Array2<RSAParameters>();
|
keys.Set(new Array2<RSAParameters>());
|
||||||
keys.Value[0] = CreateRsaParameters(in RsaSigningKeys.AcidSigningKeys[0]);
|
keys.Value[0] = CreateRsaParameters(in AcidSigningKeys[0]);
|
||||||
keys.Value[1] = CreateRsaParameters(in RsaSigningKeys.AcidSigningKeys[1]);
|
keys.Value[1] = CreateRsaParameters(in AcidSigningKeys[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return keys.Value.Items;
|
return keys.Value.Items;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ref RSAParameters Package2SigningKey
|
public ref RSAParameters Package2SigningKeyParams
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
ref Array1<RSAParameters>? keys = ref RsaSigningKeyParams.Package2SigningKey;
|
ref Optional<RSAParameters> keys = ref RsaSigningKeyParams.Package2SigningKey;
|
||||||
|
|
||||||
if (keys is null)
|
if (!keys.HasValue)
|
||||||
{
|
{
|
||||||
keys = new Array1<RSAParameters>();
|
keys.Set(new RSAParameters());
|
||||||
keys.Value[0] = CreateRsaParameters(in RsaSigningKeys.Package2SigningKey);
|
keys.Value = CreateRsaParameters(in Package2SigningKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ref keys.Value[0];
|
return ref keys.Value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ref RSAParameters BetaNca0KeyAreaKey
|
public ref RSAParameters BetaNca0KeyAreaKeyParams
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
ref Array1<RSAParameters>? keys = ref _rsaKeyParams.BetaNca0KeyAreaKey;
|
ref Optional<RSAParameters> keys = ref _rsaKeyParams.BetaNca0KeyAreaKey;
|
||||||
|
|
||||||
if (keys is null)
|
if (!keys.HasValue)
|
||||||
{
|
{
|
||||||
keys = new Array1<RSAParameters>();
|
keys.Set(CreateRsaParameters(in BetaNca0KeyAreaKey));
|
||||||
keys.Value[0] = CreateRsaParameters(in RsaKeys.BetaNca0KeyAreaKey);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ref keys.Value[0];
|
return ref keys.Value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,391 +190,19 @@ namespace LibHac.Common.Keys
|
||||||
DeriveSdCardKeys();
|
DeriveSdCardKeys();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetMode(Mode mode)
|
public void SetMode(Mode mode) => _mode = mode;
|
||||||
{
|
|
||||||
_mode = mode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void DeriveKeys(IProgressReport logger = null)
|
|
||||||
{
|
|
||||||
DeriveKeyBlobKeys();
|
|
||||||
DecryptKeyBlobs(logger);
|
|
||||||
ReadKeyBlobs();
|
|
||||||
|
|
||||||
Derive620Keys();
|
|
||||||
Derive620MasterKeks();
|
|
||||||
DeriveMarikoMasterKeks();
|
|
||||||
DeriveMasterKeys();
|
|
||||||
PopulateOldMasterKeys();
|
|
||||||
|
|
||||||
DerivePerConsoleKeys();
|
|
||||||
DerivePerFirmwareKeys();
|
|
||||||
DeriveNcaHeaderKey();
|
|
||||||
DeriveSdCardKeys();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DeriveKeyBlobKeys()
|
|
||||||
{
|
|
||||||
if (SecureBootKey.IsEmpty() || TsecKey.IsEmpty()) return;
|
|
||||||
|
|
||||||
bool haveKeyBlobMacKeySource = !MasterKeySource.IsEmpty();
|
|
||||||
var temp = new AesKey();
|
|
||||||
|
|
||||||
for (int i = 0; i < UsedKeyBlobCount; i++)
|
|
||||||
{
|
|
||||||
if (KeyBlobKeySources[i].IsEmpty()) continue;
|
|
||||||
|
|
||||||
Aes.DecryptEcb128(KeyBlobKeySources[i], temp, TsecKey);
|
|
||||||
Aes.DecryptEcb128(temp, KeyBlobKeys[i], SecureBootKey);
|
|
||||||
|
|
||||||
if (!haveKeyBlobMacKeySource) continue;
|
|
||||||
|
|
||||||
Aes.DecryptEcb128(KeyBlobMacKeySource, KeyBlobMacKeys[i], KeyBlobKeys[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DecryptKeyBlobs(IProgressReport logger = null)
|
|
||||||
{
|
|
||||||
var cmac = new AesCmac();
|
|
||||||
|
|
||||||
for (int i = 0; i < UsedKeyBlobCount; i++)
|
|
||||||
{
|
|
||||||
if (KeyBlobKeys[i].IsEmpty() || KeyBlobMacKeys[i].IsEmpty() || EncryptedKeyBlobs[i].IsEmpty())
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Aes.CalculateCmac(cmac, EncryptedKeyBlobs[i].Bytes.Slice(0x10, 0xA0), KeyBlobMacKeys[i]);
|
|
||||||
|
|
||||||
if (!Utilities.SpansEqual<byte>(cmac, EncryptedKeyBlobs[i].Cmac))
|
|
||||||
{
|
|
||||||
logger?.LogMessage($"Warning: Keyblob MAC {i:x2} is invalid. Are SBK/TSEC key correct?");
|
|
||||||
}
|
|
||||||
|
|
||||||
Aes.DecryptCtr128(EncryptedKeyBlobs[i].Bytes.Slice(0x20), KeyBlobs[i].Bytes, KeyBlobKeys[i],
|
|
||||||
EncryptedKeyBlobs[i].Counter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ReadKeyBlobs()
|
|
||||||
{
|
|
||||||
for (int i = 0; i < UsedKeyBlobCount; i++)
|
|
||||||
{
|
|
||||||
if (KeyBlobs[i].IsEmpty()) continue;
|
|
||||||
|
|
||||||
MasterKeks[i] = KeyBlobs[i].MasterKek;
|
|
||||||
Package1Keys[i] = KeyBlobs[i].Package1Key;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Derive620Keys()
|
|
||||||
{
|
|
||||||
bool haveTsecRootKek = !TsecRootKek.IsEmpty();
|
|
||||||
bool havePackage1MacKek = !Package1MacKek.IsEmpty();
|
|
||||||
bool havePackage1Kek = !Package1Kek.IsEmpty();
|
|
||||||
|
|
||||||
for (int i = UsedKeyBlobCount; i < KeyRevisionCount; i++)
|
|
||||||
{
|
|
||||||
if (TsecAuthSignatures[i - UsedKeyBlobCount].IsEmpty())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (haveTsecRootKek)
|
|
||||||
{
|
|
||||||
Aes.EncryptEcb128(TsecAuthSignatures[i - UsedKeyBlobCount],
|
|
||||||
TsecRootKeys[i - UsedKeyBlobCount], TsecRootKek);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (havePackage1MacKek)
|
|
||||||
{
|
|
||||||
Aes.EncryptEcb128(TsecAuthSignatures[i - UsedKeyBlobCount],
|
|
||||||
Package1MacKeys[i], Package1MacKek);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (havePackage1Kek)
|
|
||||||
{
|
|
||||||
Aes.EncryptEcb128(TsecAuthSignatures[i - UsedKeyBlobCount],
|
|
||||||
Package1Keys[i], Package1Kek);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Derive620MasterKeks()
|
|
||||||
{
|
|
||||||
for (int i = UsedKeyBlobCount; i < KeyRevisionCount; i++)
|
|
||||||
{
|
|
||||||
// Key revisions >= 8 all use the same TSEC root key
|
|
||||||
int tsecRootKeyIndex = Math.Min(i, 8) - UsedKeyBlobCount;
|
|
||||||
if (TsecRootKeys[tsecRootKeyIndex].IsEmpty() || MasterKekSources[i].IsEmpty()) continue;
|
|
||||||
|
|
||||||
Aes.DecryptEcb128(MasterKekSources[i], MasterKeks[i], TsecRootKeys[tsecRootKeyIndex]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DeriveMarikoMasterKeks()
|
|
||||||
{
|
|
||||||
if (MarikoKek.IsEmpty()) return;
|
|
||||||
|
|
||||||
for (int i = 0; i < KeyRevisionCount; i++)
|
|
||||||
{
|
|
||||||
if (MarikoMasterKekSources[i].IsEmpty()) continue;
|
|
||||||
|
|
||||||
Aes.DecryptEcb128(MarikoMasterKekSources[i], MasterKeks[i], MarikoKek);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DeriveMasterKeys()
|
|
||||||
{
|
|
||||||
if (MasterKeySource.IsEmpty()) return;
|
|
||||||
|
|
||||||
for (int i = 0; i < KeyRevisionCount; i++)
|
|
||||||
{
|
|
||||||
if (MasterKeks[i].IsEmpty()) continue;
|
|
||||||
|
|
||||||
Aes.DecryptEcb128(MasterKeySource, MasterKeys[i], MasterKeks[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void PopulateOldMasterKeys()
|
|
||||||
{
|
|
||||||
// Find the newest master key we have
|
|
||||||
int newestMasterKey = -1;
|
|
||||||
|
|
||||||
for (int i = MasterKeyVectors.Length - 1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
if (!MasterKeys[i].IsEmpty())
|
|
||||||
{
|
|
||||||
newestMasterKey = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newestMasterKey == -1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Don't populate old master keys unless the newest master key is valid
|
|
||||||
if (!TestKeyGeneration(newestMasterKey))
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Decrypt all previous master keys
|
|
||||||
for (int i = newestMasterKey; i > 0; i--)
|
|
||||||
{
|
|
||||||
Aes.DecryptEcb128(MasterKeyVectors[i], MasterKeys[i - 1], MasterKeys[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Check if the master key of the specified generation is correct.
|
/// Returns a new <see cref="KeySet"/> containing any keys that have been compiled into the library.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="generation">The generation to test.</param>
|
/// <returns>The created <see cref="KeySet"/>,</returns>
|
||||||
/// <returns><see langword="true"/> if the key is correct.</returns>
|
public static KeySet CreateDefaultKeySet()
|
||||||
private bool TestKeyGeneration(int generation)
|
|
||||||
{
|
{
|
||||||
// Decrypt the vector chain until we get Master Key 0
|
return DefaultKeySet.CreateDefaultKeySet();
|
||||||
AesKey key = MasterKeys[generation];
|
|
||||||
|
|
||||||
for (int i = generation; i > 0; i--)
|
|
||||||
{
|
|
||||||
Aes.DecryptEcb128(MasterKeyVectors[i], key, key);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decrypt the zeros with Master Key 0
|
|
||||||
Aes.DecryptEcb128(MasterKeyVectors[0], key, key);
|
|
||||||
|
|
||||||
// If we don't get zeros, MasterKeys[generation] is incorrect
|
|
||||||
return key.IsEmpty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ReadOnlySpan<AesKey> MasterKeyVectors =>
|
public void DeriveKeys(IProgressReport logger = null) => KeyDerivation.DeriveAllKeys(this, logger);
|
||||||
MemoryMarshal.Cast<byte, AesKey>(_mode == Mode.Dev ? MasterKeyVectorsDev : MasterKeyVectorsProd);
|
public void DeriveSdCardKeys() => KeyDerivation.DeriveSdCardKeys(this);
|
||||||
|
|
||||||
private static ReadOnlySpan<byte> MasterKeyVectorsDev => new byte[]
|
|
||||||
{
|
|
||||||
0x46, 0x22, 0xB4, 0x51, 0x9A, 0x7E, 0xA7, 0x7F, 0x62, 0xA1, 0x1F, 0x8F, 0xC5, 0x3A, 0xDB, 0xFE, // Zeroes encrypted with Master Key 00.
|
|
||||||
0x39, 0x33, 0xF9, 0x31, 0xBA, 0xE4, 0xA7, 0x21, 0x2C, 0xDD, 0xB7, 0xD8, 0xB4, 0x4E, 0x37, 0x23, // Master key 00 encrypted with Master key 01.
|
|
||||||
0x97, 0x29, 0xB0, 0x32, 0x43, 0x14, 0x8C, 0xA6, 0x85, 0xE9, 0x5A, 0x94, 0x99, 0x39, 0xAC, 0x5D, // Master key 01 encrypted with Master key 02.
|
|
||||||
0x2C, 0xCA, 0x9C, 0x31, 0x1E, 0x07, 0xB0, 0x02, 0x97, 0x0A, 0xD8, 0x03, 0xA2, 0x76, 0x3F, 0xA3, // Master key 02 encrypted with Master key 03.
|
|
||||||
0x9B, 0x84, 0x76, 0x14, 0x72, 0x94, 0x52, 0xCB, 0x54, 0x92, 0x9B, 0xC4, 0x8C, 0x5B, 0x0F, 0xBA, // Master key 03 encrypted with Master key 04.
|
|
||||||
0x78, 0xD5, 0xF1, 0x20, 0x3D, 0x16, 0xE9, 0x30, 0x32, 0x27, 0x34, 0x6F, 0xCF, 0xE0, 0x27, 0xDC, // Master key 04 encrypted with Master key 05.
|
|
||||||
0x6F, 0xD2, 0x84, 0x1D, 0x05, 0xEC, 0x40, 0x94, 0x5F, 0x18, 0xB3, 0x81, 0x09, 0x98, 0x8D, 0x4E, // Master key 05 encrypted with Master key 06.
|
|
||||||
0x37, 0xAF, 0xAB, 0x35, 0x79, 0x09, 0xD9, 0x48, 0x29, 0xD2, 0xDB, 0xA5, 0xA5, 0xF5, 0x30, 0x19, // Master key 06 encrypted with Master key 07.
|
|
||||||
0xEC, 0xE1, 0x46, 0x89, 0x37, 0xFD, 0xD2, 0x15, 0x8C, 0x3F, 0x24, 0x82, 0xEF, 0x49, 0x68, 0x04, // Master key 07 encrypted with Master key 08.
|
|
||||||
0x43, 0x3D, 0xC5, 0x3B, 0xEF, 0x91, 0x02, 0x21, 0x61, 0x54, 0x63, 0x8A, 0x35, 0xE7, 0xCA, 0xEE, // Master key 08 encrypted with Master key 09.
|
|
||||||
0x6C, 0x2E, 0xCD, 0xB3, 0x34, 0x61, 0x77, 0xF5, 0xF9, 0xB1, 0xDD, 0x61, 0x98, 0x19, 0x3E, 0xD4 // Master key 09 encrypted with Master key 0A.
|
|
||||||
};
|
|
||||||
|
|
||||||
private static ReadOnlySpan<byte> MasterKeyVectorsProd => new byte[]
|
|
||||||
{
|
|
||||||
0x0C, 0xF0, 0x59, 0xAC, 0x85, 0xF6, 0x26, 0x65, 0xE1, 0xE9, 0x19, 0x55, 0xE6, 0xF2, 0x67, 0x3D, // Zeroes encrypted with Master Key 00.
|
|
||||||
0x29, 0x4C, 0x04, 0xC8, 0xEB, 0x10, 0xED, 0x9D, 0x51, 0x64, 0x97, 0xFB, 0xF3, 0x4D, 0x50, 0xDD, // Master key 00 encrypted with Master key 01.
|
|
||||||
0xDE, 0xCF, 0xEB, 0xEB, 0x10, 0xAE, 0x74, 0xD8, 0xAD, 0x7C, 0xF4, 0x9E, 0x62, 0xE0, 0xE8, 0x72, // Master key 01 encrypted with Master key 02.
|
|
||||||
0x0A, 0x0D, 0xDF, 0x34, 0x22, 0x06, 0x6C, 0xA4, 0xE6, 0xB1, 0xEC, 0x71, 0x85, 0xCA, 0x4E, 0x07, // Master key 02 encrypted with Master key 03.
|
|
||||||
0x6E, 0x7D, 0x2D, 0xC3, 0x0F, 0x59, 0xC8, 0xFA, 0x87, 0xA8, 0x2E, 0xD5, 0x89, 0x5E, 0xF3, 0xE9, // Master key 03 encrypted with Master key 04.
|
|
||||||
0xEB, 0xF5, 0x6F, 0x83, 0x61, 0x9E, 0xF8, 0xFA, 0xE0, 0x87, 0xD7, 0xA1, 0x4E, 0x25, 0x36, 0xEE, // Master key 04 encrypted with Master key 05.
|
|
||||||
0x1E, 0x1E, 0x22, 0xC0, 0x5A, 0x33, 0x3C, 0xB9, 0x0B, 0xA9, 0x03, 0x04, 0xBA, 0xDB, 0x07, 0x57, // Master key 05 encrypted with Master key 06.
|
|
||||||
0xA4, 0xD4, 0x52, 0x6F, 0xD1, 0xE4, 0x36, 0xAA, 0x9F, 0xCB, 0x61, 0x27, 0x1C, 0x67, 0x65, 0x1F, // Master key 06 encrypted with Master key 07.
|
|
||||||
0xEA, 0x60, 0xB3, 0xEA, 0xCE, 0x8F, 0x24, 0x46, 0x7D, 0x33, 0x9C, 0xD1, 0xBC, 0x24, 0x98, 0x29, // Master key 07 encrypted with Master key 08.
|
|
||||||
0x4D, 0xD9, 0x98, 0x42, 0x45, 0x0D, 0xB1, 0x3C, 0x52, 0x0C, 0x9A, 0x44, 0xBB, 0xAD, 0xAF, 0x80, // Master key 08 encrypted with Master key 09.
|
|
||||||
0xB8, 0x96, 0x9E, 0x4A, 0x00, 0x0D, 0xD6, 0x28, 0xB3, 0xD1, 0xDB, 0x68, 0x5F, 0xFB, 0xE1, 0x2A // Master key 09 encrypted with Master key 0A.
|
|
||||||
};
|
|
||||||
|
|
||||||
private void DerivePerConsoleKeys()
|
|
||||||
{
|
|
||||||
// Todo: Dev and newer key generations
|
|
||||||
var kek = new AesKey();
|
|
||||||
|
|
||||||
// Derive the device key
|
|
||||||
if (!PerConsoleKeySource.IsEmpty() && !KeyBlobKeys[0].IsEmpty())
|
|
||||||
{
|
|
||||||
Aes.DecryptEcb128(PerConsoleKeySource, DeviceKey, KeyBlobKeys[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Derive device-unique save keys
|
|
||||||
for (int i = 0; i < DeviceUniqueSaveMacKeySources.Length; i++)
|
|
||||||
{
|
|
||||||
if (!DeviceUniqueSaveMacKekSource.IsEmpty() && !DeviceUniqueSaveMacKeySources[i].IsEmpty() &&
|
|
||||||
!DeviceKey.IsEmpty())
|
|
||||||
{
|
|
||||||
GenerateKek(DeviceKey, DeviceUniqueSaveMacKekSource, kek, AesKekGenerationSource, null);
|
|
||||||
Aes.DecryptEcb128(DeviceUniqueSaveMacKeySources[i], DeviceUniqueSaveMacKeys[i], kek);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Derive BIS keys
|
|
||||||
if (DeviceKey.IsEmpty()
|
|
||||||
|| BisKekSource.IsEmpty()
|
|
||||||
|| AesKekGenerationSource.IsEmpty()
|
|
||||||
|| AesKeyGenerationSource.IsEmpty()
|
|
||||||
|| RetailSpecificAesKeySource.IsEmpty())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the user doesn't provide bis_key_source_03 we can assume it's the same as bis_key_source_02
|
|
||||||
if (BisKeySources[3].IsEmpty() && !BisKeySources[2].IsEmpty())
|
|
||||||
{
|
|
||||||
BisKeySources[3] = BisKeySources[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
Aes.DecryptEcb128(RetailSpecificAesKeySource, kek, DeviceKey);
|
|
||||||
if (!BisKeySources[0].IsEmpty()) Aes.DecryptEcb128(BisKeySources[0], BisKeys[0], kek);
|
|
||||||
|
|
||||||
GenerateKek(DeviceKey, BisKekSource, kek, AesKekGenerationSource, AesKeyGenerationSource);
|
|
||||||
|
|
||||||
for (int i = 1; i < 4; i++)
|
|
||||||
{
|
|
||||||
if (!BisKeySources[i].IsEmpty())
|
|
||||||
Aes.DecryptEcb128(BisKeySources[i], BisKeys[i], kek);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DerivePerFirmwareKeys()
|
|
||||||
{
|
|
||||||
bool haveKakSource0 = !KeyAreaKeyApplicationSource.IsEmpty();
|
|
||||||
bool haveKakSource1 = !KeyAreaKeyOceanSource.IsEmpty();
|
|
||||||
bool haveKakSource2 = !KeyAreaKeySystemSource.IsEmpty();
|
|
||||||
bool haveTitleKekSource = !TitleKekSource.IsEmpty();
|
|
||||||
bool havePackage2KeySource = !Package2KeySource.IsEmpty();
|
|
||||||
|
|
||||||
for (int i = 0; i < KeyRevisionCount; i++)
|
|
||||||
{
|
|
||||||
if (MasterKeys[i].IsEmpty())
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (haveKakSource0)
|
|
||||||
{
|
|
||||||
GenerateKek(MasterKeys[i], KeyAreaKeyApplicationSource, KeyAreaKeys[i][0],
|
|
||||||
AesKekGenerationSource, AesKeyGenerationSource);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (haveKakSource1)
|
|
||||||
{
|
|
||||||
GenerateKek(MasterKeys[i], KeyAreaKeyOceanSource, KeyAreaKeys[i][1],
|
|
||||||
AesKekGenerationSource, AesKeyGenerationSource);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (haveKakSource2)
|
|
||||||
{
|
|
||||||
GenerateKek(MasterKeys[i], KeyAreaKeySystemSource, KeyAreaKeys[i][2],
|
|
||||||
AesKekGenerationSource, AesKeyGenerationSource);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (haveTitleKekSource)
|
|
||||||
{
|
|
||||||
Aes.DecryptEcb128(TitleKekSource, TitleKeks[i], MasterKeys[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (havePackage2KeySource)
|
|
||||||
{
|
|
||||||
Aes.DecryptEcb128(Package2KeySource, Package2Keys[i], MasterKeys[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DeriveNcaHeaderKey()
|
|
||||||
{
|
|
||||||
if (HeaderKekSource.IsEmpty() || HeaderKeySource.IsEmpty() || MasterKeys[0].IsEmpty()) return;
|
|
||||||
|
|
||||||
var headerKek = new AesKey();
|
|
||||||
|
|
||||||
GenerateKek(MasterKeys[0], HeaderKekSource, headerKek, AesKekGenerationSource,
|
|
||||||
AesKeyGenerationSource);
|
|
||||||
Aes.DecryptEcb128(HeaderKeySource, HeaderKey, headerKek);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void DeriveSdCardKeys()
|
|
||||||
{
|
|
||||||
var sdKek = new AesKey();
|
|
||||||
var tempKey = new AesXtsKey();
|
|
||||||
GenerateKek(MasterKeys[0], SdCardKekSource, sdKek, AesKekGenerationSource, AesKeyGenerationSource);
|
|
||||||
|
|
||||||
for (int k = 0; k < SdCardKeyIdCount; k++)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < 4; i++)
|
|
||||||
{
|
|
||||||
tempKey.Data64[i] = SdCardKeySources[k].Data64[i] ^ SdCardEncryptionSeed.Data64[i & 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
tempKey.Data64[0] = SdCardKeySources[k].Data64[0] ^ SdCardEncryptionSeed.Data64[0];
|
|
||||||
tempKey.Data64[1] = SdCardKeySources[k].Data64[1] ^ SdCardEncryptionSeed.Data64[1];
|
|
||||||
tempKey.Data64[2] = SdCardKeySources[k].Data64[2] ^ SdCardEncryptionSeed.Data64[0];
|
|
||||||
tempKey.Data64[3] = SdCardKeySources[k].Data64[3] ^ SdCardEncryptionSeed.Data64[1];
|
|
||||||
|
|
||||||
Aes.DecryptEcb128(tempKey, SdCardEncryptionKeys[k], sdKek);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Derive sd card save key
|
|
||||||
if (!SeedUniqueSaveMacKekSource.IsEmpty() && !SeedUniqueSaveMacKeySource.IsEmpty())
|
|
||||||
{
|
|
||||||
var keySource = new AesKey();
|
|
||||||
|
|
||||||
keySource.Data64[0] = SeedUniqueSaveMacKeySource.Data64[0] ^ SdCardEncryptionSeed.Data64[0];
|
|
||||||
keySource.Data64[1] = SeedUniqueSaveMacKeySource.Data64[1] ^ SdCardEncryptionSeed.Data64[1];
|
|
||||||
|
|
||||||
GenerateKek(MasterKeys[0], SeedUniqueSaveMacKekSource, sdKek, AesKekGenerationSource, null);
|
|
||||||
Aes.DecryptEcb128(keySource, SeedUniqueSaveMacKey, sdKek);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void GenerateKek(ReadOnlySpan<byte> key, ReadOnlySpan<byte> src, Span<byte> dest,
|
|
||||||
ReadOnlySpan<byte> kekSeed, ReadOnlySpan<byte> keySeed)
|
|
||||||
{
|
|
||||||
var kek = new AesKey();
|
|
||||||
var srcKek = new AesKey();
|
|
||||||
|
|
||||||
Aes.DecryptEcb128(kekSeed, kek, key);
|
|
||||||
Aes.DecryptEcb128(src, srcKek, kek);
|
|
||||||
|
|
||||||
if (!keySeed.IsEmpty)
|
|
||||||
{
|
|
||||||
Aes.DecryptEcb128(keySeed, dest, srcKek);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
srcKek.Data.CopyTo(dest);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static RSAParameters CreateRsaParameters(in RsaKey key)
|
private static RSAParameters CreateRsaParameters(in RsaKey key)
|
||||||
{
|
{
|
||||||
|
@ -598,14 +230,14 @@ namespace LibHac.Common.Keys
|
||||||
|
|
||||||
private struct RsaSigningKeyParameters
|
private struct RsaSigningKeyParameters
|
||||||
{
|
{
|
||||||
public Array2<RSAParameters>? NcaHeaderSigningKeys;
|
public Optional<Array2<RSAParameters>> NcaHeaderSigningKeys;
|
||||||
public Array2<RSAParameters>? AcidSigningKeys;
|
public Optional<Array2<RSAParameters>> AcidSigningKeys;
|
||||||
public Array1<RSAParameters>? Package2SigningKey;
|
public Optional<RSAParameters> Package2SigningKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct RsaKeyParameters
|
private struct RsaKeyParameters
|
||||||
{
|
{
|
||||||
public Array1<RSAParameters>? BetaNca0KeyAreaKey;
|
public Optional<RSAParameters> BetaNca0KeyAreaKey;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -633,7 +633,7 @@ namespace LibHac.FsSystem.NcaUtils
|
||||||
Span<byte> keyArea = Header.GetKeyArea();
|
Span<byte> keyArea = Header.GetKeyArea();
|
||||||
var decKeyArea = new byte[0x100];
|
var decKeyArea = new byte[0x100];
|
||||||
|
|
||||||
if (CryptoOld.DecryptRsaOaep(keyArea, decKeyArea, KeySet.BetaNca0KeyAreaKey, out _))
|
if (CryptoOld.DecryptRsaOaep(keyArea, decKeyArea, KeySet.BetaNca0KeyAreaKeyParams, out _))
|
||||||
{
|
{
|
||||||
Nca0KeyArea = decKeyArea;
|
Nca0KeyArea = decKeyArea;
|
||||||
}
|
}
|
||||||
|
@ -711,7 +711,7 @@ namespace LibHac.FsSystem.NcaUtils
|
||||||
|
|
||||||
public Validity VerifyHeaderSignature()
|
public Validity VerifyHeaderSignature()
|
||||||
{
|
{
|
||||||
return Header.VerifySignature1(KeySet.NcaHeaderSigningKeys[0].Modulus);
|
return Header.VerifySignature1(KeySet.NcaHeaderSigningKeyParams[0].Modulus);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void GenerateAesCounter(int sectionIndex, Ncm.ContentType type, int minorVersion)
|
internal void GenerateAesCounter(int sectionIndex, Ncm.ContentType type, int minorVersion)
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Condition="Exists('ResultNameResolver.Generated.cs')" Remove="ResultNameResolver.Archive.cs" />
|
<Compile Condition="Exists('ResultNameResolver.Generated.cs')" Remove="ResultNameResolver.Archive.cs" />
|
||||||
|
<Compile Condition="Exists('Common\Keys\DefaultKeySet.Generated.cs')" Remove="Common\Keys\DefaultKeySet.Empty.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -46,7 +46,7 @@ namespace LibHac.Npdm
|
||||||
reader.BaseStream.Position = offset + 0x100;
|
reader.BaseStream.Position = offset + 0x100;
|
||||||
byte[] signatureData = reader.ReadBytes(Size);
|
byte[] signatureData = reader.ReadBytes(Size);
|
||||||
SignatureValidity =
|
SignatureValidity =
|
||||||
CryptoOld.Rsa2048PssVerify(signatureData, Rsa2048Signature, keySet.AcidSigningKeys[0].Modulus);
|
CryptoOld.Rsa2048PssVerify(signatureData, Rsa2048Signature, keySet.AcidSigningKeyParams[0].Modulus);
|
||||||
}
|
}
|
||||||
|
|
||||||
reader.BaseStream.Position = offset + 0x208;
|
reader.BaseStream.Position = offset + 0x208;
|
||||||
|
|
|
@ -154,7 +154,7 @@ namespace LibHac
|
||||||
|
|
||||||
Signature = reader.ReadBytes(0x100);
|
Signature = reader.ReadBytes(0x100);
|
||||||
byte[] sigData = reader.ReadBytes(0x100);
|
byte[] sigData = reader.ReadBytes(0x100);
|
||||||
SignatureValidity = CryptoOld.Rsa2048PssVerify(sigData, Signature, keySet.Package2SigningKey.Modulus);
|
SignatureValidity = CryptoOld.Rsa2048PssVerify(sigData, Signature, keySet.Package2SigningKeyParams.Modulus);
|
||||||
|
|
||||||
reader.BaseStream.Position -= 0x100;
|
reader.BaseStream.Position -= 0x100;
|
||||||
Counter = reader.ReadBytes(0x10);
|
Counter = reader.ReadBytes(0x10);
|
||||||
|
|
34
src/LibHac/Util/Optional.cs
Normal file
34
src/LibHac/Util/Optional.cs
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using LibHac.Diag;
|
||||||
|
|
||||||
|
namespace LibHac.Util
|
||||||
|
{
|
||||||
|
public struct Optional<T>
|
||||||
|
{
|
||||||
|
private bool _hasValue;
|
||||||
|
private T _value;
|
||||||
|
|
||||||
|
public bool HasValue => _hasValue;
|
||||||
|
public ref T Value
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
Assert.AssertTrue(_hasValue);
|
||||||
|
// It's beautiful
|
||||||
|
return ref MemoryMarshal.CreateSpan(ref _value, 1)[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Set(in T value)
|
||||||
|
{
|
||||||
|
_value = value;
|
||||||
|
_hasValue = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
_hasValue = false;
|
||||||
|
_value = default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue