Add save file signing capability

This commit is contained in:
Alex Barney 2018-09-15 22:31:06 -05:00
parent e651eb731c
commit 087008f7f4
6 changed files with 47 additions and 4 deletions

View file

@ -95,6 +95,8 @@ bis_key_source_01 = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
bis_key_source_02 = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX bis_key_source_02 = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
bis_kek_source = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX bis_kek_source = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
save_mac_kek_source = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
save_mac_key_source = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
``` ```
### Console-unique keys ### Console-unique keys
@ -141,6 +143,8 @@ bis_kek_source
bis_key_source_00 bis_key_source_00
bis_key_source_01 bis_key_source_01
bis_key_source_02 bis_key_source_02
save_mac_kek_source
save_mac_key_source
header_key header_key
xci_header_key xci_header_key

View file

@ -9,6 +9,7 @@ namespace LibHac.Savefile
{ {
public Header Header { get; } public Header Header { get; }
private RemapStream FileRemap { get; } private RemapStream FileRemap { get; }
public SharedStreamSource SavefileSource { get; }
public SharedStreamSource FileRemapSource { get; } public SharedStreamSource FileRemapSource { get; }
private RemapStream MetaRemap { get; } private RemapStream MetaRemap { get; }
public SharedStreamSource MetaRemapSource { get; } public SharedStreamSource MetaRemapSource { get; }
@ -39,12 +40,15 @@ namespace LibHac.Savefile
public Savefile(Keyset keyset, Stream file, IProgressReport logger = null) public Savefile(Keyset keyset, Stream file, IProgressReport logger = null)
{ {
using (var reader = new BinaryReader(file, Encoding.Default, true)) SavefileSource = new SharedStreamSource(file);
using (var reader = new BinaryReader(SavefileSource.CreateStream(), Encoding.Default, true))
{ {
Header = new Header(keyset, reader, logger); Header = new Header(keyset, reader, logger);
var layout = Header.Layout; FsLayout layout = Header.Layout;
FileRemap = new RemapStream( FileRemap = new RemapStream(
new SubStream(file, layout.FileMapDataOffset, layout.FileMapDataSize), SavefileSource.CreateStream(layout.FileMapDataOffset, layout.FileMapDataSize),
Header.FileMapEntries, Header.FileRemap.MapSegmentCount); Header.FileMapEntries, Header.FileRemap.MapSegmentCount);
FileRemapSource = new SharedStreamSource(FileRemap); FileRemapSource = new SharedStreamSource(FileRemap);
@ -220,6 +224,25 @@ namespace LibHac.Savefile
return entries; return entries;
} }
public bool SignHeader(Keyset keyset)
{
if (keyset.SaveMacKey.IsEmpty()) return false;
var data = new byte[0x200];
var cmac = new byte[0x10];
var headerStream = SavefileSource.CreateStream();
headerStream.Position = 0x100;
headerStream.Read(data, 0, 0x200);
Crypto.CalculateAesCmac(keyset.SaveMacKey, data, 0, cmac, 0, 0x200);
headerStream.Position = 0;
headerStream.Write(cmac, 0, 0x10);
return true;
}
} }
public static class SavefileExtensions public static class SavefileExtensions

View file

@ -65,6 +65,7 @@ Switch FS options:
Savefile options: Savefile options:
--outdir <dir> Specify directory path to save contents to. --outdir <dir> Specify directory path to save contents to.
--debugoutdir <dir> Specify directory path to save intermediate data to for debugging. --debugoutdir <dir> Specify directory path to save intermediate data to for debugging.
--sign Sign the save file. (Requires device_key in key file)
``` ```
## Examples ## Examples

View file

@ -43,6 +43,7 @@ namespace hactoolnet
new CliOption("listapps", 0, (o, a) => o.ListApps = true), new CliOption("listapps", 0, (o, a) => o.ListApps = true),
new CliOption("listtitles", 0, (o, a) => o.ListTitles = true), new CliOption("listtitles", 0, (o, a) => o.ListTitles = true),
new CliOption("listromfs", 0, (o, a) => o.ListRomFs = true), new CliOption("listromfs", 0, (o, a) => o.ListRomFs = true),
new CliOption("sign", 0, (o, a) => o.SignSave = true),
new CliOption("title", 1, (o, a) => o.TitleId = ParseTitleId(a[0])), new CliOption("title", 1, (o, a) => o.TitleId = ParseTitleId(a[0])),
}; };
@ -194,6 +195,7 @@ namespace hactoolnet
sb.AppendLine("Savefile options:"); sb.AppendLine("Savefile options:");
sb.AppendLine(" --outdir <dir> Specify directory path to save contents to."); sb.AppendLine(" --outdir <dir> Specify directory path to save contents to.");
sb.AppendLine(" --debugoutdir <dir> Specify directory path to save intermediate data to for debugging."); sb.AppendLine(" --debugoutdir <dir> Specify directory path to save intermediate data to for debugging.");
sb.AppendLine(" --sign Sign the save file. (Requires device_key in key file)");
return sb.ToString(); return sb.ToString();
} }

View file

@ -33,6 +33,7 @@ namespace hactoolnet
public bool ListApps; public bool ListApps;
public bool ListTitles; public bool ListTitles;
public bool ListRomFs; public bool ListRomFs;
public bool SignSave;
public ulong TitleId; public ulong TitleId;
} }

View file

@ -93,7 +93,7 @@ namespace hactoolnet
private static void ProcessSave(Context ctx) private static void ProcessSave(Context ctx)
{ {
using (var file = new FileStream(ctx.Options.InFile, FileMode.Open, FileAccess.Read)) using (var file = new FileStream(ctx.Options.InFile, FileMode.Open, FileAccess.ReadWrite))
{ {
var save = new Savefile(ctx.Keyset, file, ctx.Logger); var save = new Savefile(ctx.Keyset, file, ctx.Logger);
@ -129,6 +129,18 @@ namespace hactoolnet
save.JournalStreamSource.CreateStream().WriteAllBytes(Path.Combine(dir, "L3_0_SaveData"), ctx.Logger); save.JournalStreamSource.CreateStream().WriteAllBytes(Path.Combine(dir, "L3_0_SaveData"), ctx.Logger);
} }
if (ctx.Options.SignSave)
{
if (save.SignHeader(ctx.Keyset))
{
ctx.Logger.LogMessage("Successfully signed save file");
}
else
{
ctx.Logger.LogMessage("Unable to sign save file. Do you have all the required keys?");
}
}
} }
} }