Add save file signing capability
This commit is contained in:
parent
e651eb731c
commit
087008f7f4
4
KEYS.md
4
KEYS.md
@ -95,6 +95,8 @@ bis_key_source_01 = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||
bis_key_source_02 = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||
bis_kek_source = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||
|
||||
save_mac_kek_source = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||
save_mac_key_source = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||
```
|
||||
|
||||
### Console-unique keys
|
||||
@ -141,6 +143,8 @@ bis_kek_source
|
||||
bis_key_source_00
|
||||
bis_key_source_01
|
||||
bis_key_source_02
|
||||
save_mac_kek_source
|
||||
save_mac_key_source
|
||||
|
||||
header_key
|
||||
xci_header_key
|
||||
|
@ -9,6 +9,7 @@ namespace LibHac.Savefile
|
||||
{
|
||||
public Header Header { get; }
|
||||
private RemapStream FileRemap { get; }
|
||||
public SharedStreamSource SavefileSource { get; }
|
||||
public SharedStreamSource FileRemapSource { get; }
|
||||
private RemapStream MetaRemap { get; }
|
||||
public SharedStreamSource MetaRemapSource { get; }
|
||||
@ -39,12 +40,15 @@ namespace LibHac.Savefile
|
||||
|
||||
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);
|
||||
var layout = Header.Layout;
|
||||
FsLayout layout = Header.Layout;
|
||||
|
||||
FileRemap = new RemapStream(
|
||||
new SubStream(file, layout.FileMapDataOffset, layout.FileMapDataSize),
|
||||
SavefileSource.CreateStream(layout.FileMapDataOffset, layout.FileMapDataSize),
|
||||
Header.FileMapEntries, Header.FileRemap.MapSegmentCount);
|
||||
|
||||
FileRemapSource = new SharedStreamSource(FileRemap);
|
||||
@ -220,6 +224,25 @@ namespace LibHac.Savefile
|
||||
|
||||
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
|
||||
|
@ -65,6 +65,7 @@ Switch FS options:
|
||||
Savefile options:
|
||||
--outdir <dir> Specify directory path to save contents to.
|
||||
--debugoutdir <dir> Specify directory path to save intermediate data to for debugging.
|
||||
--sign Sign the save file. (Requires device_key in key file)
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
@ -43,6 +43,7 @@ namespace hactoolnet
|
||||
new CliOption("listapps", 0, (o, a) => o.ListApps = true),
|
||||
new CliOption("listtitles", 0, (o, a) => o.ListTitles = 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])),
|
||||
};
|
||||
|
||||
@ -194,6 +195,7 @@ namespace hactoolnet
|
||||
sb.AppendLine("Savefile options:");
|
||||
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(" --sign Sign the save file. (Requires device_key in key file)");
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ namespace hactoolnet
|
||||
public bool ListApps;
|
||||
public bool ListTitles;
|
||||
public bool ListRomFs;
|
||||
public bool SignSave;
|
||||
public ulong TitleId;
|
||||
}
|
||||
|
||||
|
@ -93,7 +93,7 @@ namespace hactoolnet
|
||||
|
||||
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);
|
||||
|
||||
@ -129,6 +129,18 @@ namespace hactoolnet
|
||||
|
||||
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?");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user