-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add 'extract-embedded-xvd' command, move commands into Commands/ subf…
…older
- Loading branch information
Showing
12 changed files
with
418 additions
and
283 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
using System.Diagnostics; | ||
using Spectre.Console; | ||
using Spectre.Console.Cli; | ||
|
||
namespace XvdTool.Streaming.Commands; | ||
|
||
internal abstract class CryptoCommand<T> : XvdCommand<T> where T : CryptoCommandSettings | ||
{ | ||
protected KeyManager KeyManager = default!; | ||
|
||
protected bool Initialize(CryptoCommandSettings settings, out KeyEntry entry) | ||
{ | ||
Initialize(settings, requiresWriting: true); | ||
|
||
Debug.Assert(XvdFile != null, "XvdFile != null"); | ||
|
||
entry = default; | ||
|
||
KeyManager = new KeyManager(); | ||
|
||
if (settings.DeviceKey != null) | ||
{ | ||
KeyManager.LoadDeviceKey(Convert.FromHexString(settings.DeviceKey)); | ||
} | ||
|
||
if (settings.CikPath != null) | ||
{ | ||
entry = KeyManager.LoadCik(settings.CikPath); | ||
} | ||
else | ||
{ | ||
KeyManager.LoadCachedKeys(); | ||
|
||
var keyId = XvdFile.GetKeyId(); | ||
if (keyId != Guid.Empty) | ||
{ | ||
if (!KeyManager.TryGetKey(keyId, out entry)) | ||
{ | ||
ConsoleLogger.WriteErrLine($"Could not find key [bold]{keyId}[/] loaded in key storage."); | ||
|
||
return false; | ||
} | ||
} | ||
} | ||
|
||
return true; | ||
} | ||
|
||
public override ValidationResult Validate(CommandContext context, T settings) | ||
{ | ||
var result = base.Validate(context, settings); | ||
|
||
if (!result.Successful) | ||
return result; | ||
|
||
if (settings.CikPath != null && !File.Exists(settings.CikPath)) | ||
return ValidationResult.Error("Provided .cik file does not exist."); | ||
|
||
if (settings.DeviceKey != null && (settings.DeviceKey.Length != 32 || | ||
settings.DeviceKey.All("0123456789ABCDEFabcdef".Contains))) | ||
return ValidationResult.Error("Provided device key is invalid. Must be 32 hex characters long."); | ||
|
||
return ValidationResult.Success(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
using System.ComponentModel; | ||
using Spectre.Console.Cli; | ||
|
||
namespace XvdTool.Streaming.Commands; | ||
|
||
internal abstract class CryptoCommandSettings : XvdCommandSettings | ||
{ | ||
[Description("Path to the .cik file to be used regardless of the header key ID.")] | ||
[CommandOption("-c|--cik")] | ||
public string? CikPath { get; init; } | ||
|
||
[Description("Device key used to decrypt UWP licenses.")] | ||
[CommandOption("-d|--device-key")] | ||
public string? DeviceKey { get; init; } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
using System.ComponentModel; | ||
using System.Diagnostics; | ||
using Spectre.Console; | ||
using Spectre.Console.Cli; | ||
|
||
namespace XvdTool.Streaming.Commands; | ||
|
||
internal sealed class DecryptCommand : CryptoCommand<DecryptCommand.Settings> | ||
{ | ||
public sealed class Settings : CryptoCommandSettings | ||
{ | ||
[Description("Skips recalculating the hashes after decryption.\nSpeeds up the process, but makes subsequent hash checks on the file fail.")] | ||
[CommandOption("-n|--no-hash-calc")] | ||
public bool SkipHashCalculation { get; init; } | ||
} | ||
|
||
public override int Execute(CommandContext context, Settings settings) | ||
{ | ||
if (!Initialize(settings, out var keyEntry)) | ||
{ | ||
return -1; | ||
} | ||
|
||
using (XvdFile) | ||
{ | ||
XvdFile.DecryptData(keyEntry, false); | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
public override ValidationResult Validate(CommandContext context, Settings settings) | ||
{ | ||
base.Validate(context, settings); | ||
|
||
Debug.Assert(settings.XvcPath != null, "settings.XvcPath != null"); | ||
|
||
if (!settings.SkipHashCalculation) | ||
return ValidationResult.Error( | ||
"Hash recalculation is not yet supported. Please use the 'extract' command instead, or specify '--no-hash-calc' to skip recomputing the hash table."); | ||
|
||
if (settings.XvcPath.StartsWith("http")) | ||
return ValidationResult.Error("Only local files are supported for integrity verification."); | ||
|
||
if (!File.Exists(settings.XvcPath)) | ||
return ValidationResult.Error("Provided file does not exist."); | ||
|
||
return ValidationResult.Success(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
using System.ComponentModel; | ||
using System.Diagnostics; | ||
using Spectre.Console; | ||
using Spectre.Console.Cli; | ||
|
||
namespace XvdTool.Streaming.Commands; | ||
|
||
internal sealed class ExtractCommand : CryptoCommand<ExtractCommand.Settings> | ||
{ | ||
public sealed class Settings : CryptoCommandSettings | ||
{ | ||
[DefaultValue("output")] | ||
[Description("Output directory to extract the files into.")] | ||
[CommandOption("-o|--output")] | ||
public string? OutputDirectory { get; init; } | ||
|
||
[Description("List of regions to skip downloading. Defaults to none.")] | ||
[CommandOption("-b|--skip-region")] | ||
public uint[]? SkipRegions { get; init; } | ||
|
||
[Description("List of regions to download. Defaults to all.")] | ||
[CommandOption("-w|--download-region")] | ||
public uint[]? DownloadRegions { get; init; } | ||
|
||
[Description("Skips performing hash verification on the pages prior to decryption.\nMassively improves performance at the cost of integrity.\nOnly use this if you know the file is not corrupt!")] | ||
[CommandOption("-n|--no-hash-check")] | ||
public bool SkipHashCheck { get; init; } | ||
} | ||
|
||
public override int Execute(CommandContext context, Settings settings) | ||
{ | ||
Debug.Assert(settings.OutputDirectory != null, "settings.OutputDirectory != null"); | ||
|
||
if (!Initialize(settings, out var keyEntry)) | ||
{ | ||
return -1; | ||
} | ||
|
||
var outputPath = Path.GetFullPath(settings.OutputDirectory); | ||
|
||
var hashStatus = settings.SkipHashCheck ? "[red]disabled[/]" : "[green]enabled[/]"; | ||
|
||
ConsoleLogger.WriteInfoLine($"Extracting files into [green bold]{outputPath}[/]. (Hash check {hashStatus})"); | ||
|
||
using (XvdFile) | ||
{ | ||
XvdFile.ExtractFiles(outputPath, keyEntry, settings.SkipHashCheck, settings.SkipRegions, settings.DownloadRegions); | ||
} | ||
|
||
ConsoleLogger.WriteInfoLine("[green bold]Successfully[/] extracted files."); | ||
|
||
return 0; | ||
} | ||
|
||
public override ValidationResult Validate(CommandContext context, Settings settings) | ||
{ | ||
if (settings is { DownloadRegions: not null, SkipRegions: not null }) | ||
return ValidationResult.Error("'--skip-region' and '--download-region' cannot be used together."); | ||
|
||
return ValidationResult.Success(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
using System.ComponentModel; | ||
using System.Diagnostics; | ||
using Spectre.Console; | ||
using Spectre.Console.Cli; | ||
|
||
namespace XvdTool.Streaming.Commands; | ||
|
||
internal sealed class ExtractEmbeddedXvdCommand : XvdCommand<ExtractEmbeddedXvdCommand.Settings> | ||
{ | ||
internal sealed class Settings : XvdCommandSettings | ||
{ | ||
[CommandOption("-o|--output")] | ||
[Description("Output path of the embedded XVD.")] | ||
[DefaultValue("embedded.xvd")] | ||
public string EmbeddedXvdOutputPath { get; set; } = null!; | ||
} | ||
|
||
public override int Execute(CommandContext context, Settings settings) | ||
{ | ||
Initialize(settings, requiresWriting: false); | ||
|
||
Debug.Assert(XvdFile != null, "XvdFile != null"); | ||
|
||
var directory = Path.GetDirectoryName(settings.EmbeddedXvdOutputPath); | ||
if (!string.IsNullOrEmpty(directory)) | ||
Directory.CreateDirectory(directory); | ||
|
||
using (XvdFile) | ||
{ | ||
XvdFile.ExtractEmbeddedXvd(settings.EmbeddedXvdOutputPath); | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
public override ValidationResult Validate(CommandContext context, Settings settings) | ||
{ | ||
if (Directory.Exists(settings.EmbeddedXvdOutputPath)) | ||
return ValidationResult.Error("The embedded XVD output path is a directory."); | ||
|
||
return base.Validate(context, settings); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
using System.ComponentModel; | ||
using System.Diagnostics; | ||
using Spectre.Console.Cli; | ||
|
||
namespace XvdTool.Streaming.Commands; | ||
|
||
internal sealed class InfoCommand : XvdCommand<InfoCommand.Settings> | ||
{ | ||
public sealed class Settings : XvdCommandSettings | ||
{ | ||
[Description("File path to save the output into.")] | ||
[CommandOption("-o|--output")] | ||
public string? OutputPath { get; init; } | ||
|
||
[Description("If all files should be printed.\nIf unset, only the first 4096 files will be printed.")] | ||
[CommandOption("-a|--show-all-files")] | ||
public bool ShowAllFiles { get; set; } | ||
} | ||
|
||
public override int Execute(CommandContext context, Settings settings) | ||
{ | ||
Initialize(settings, requiresWriting: false); | ||
|
||
Debug.Assert(XvdFile != null, "XvdFile != null"); | ||
|
||
using (XvdFile) | ||
{ | ||
var infoOutput = XvdFile.PrintInfo(settings.ShowAllFiles); | ||
if (settings.OutputPath != null) | ||
{ | ||
var directory = Path.GetDirectoryName(settings.OutputPath); | ||
if (!string.IsNullOrEmpty(directory)) | ||
Directory.CreateDirectory(directory); | ||
|
||
File.WriteAllText(settings.OutputPath, infoOutput); | ||
} | ||
} | ||
|
||
return 0; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
using System.Diagnostics; | ||
using Spectre.Console; | ||
using Spectre.Console.Cli; | ||
|
||
namespace XvdTool.Streaming.Commands; | ||
|
||
internal sealed class VerifyCommand : XvdCommand<VerifyCommand.Settings> | ||
{ | ||
public sealed class Settings : XvdCommandSettings; | ||
|
||
public override int Execute(CommandContext context, Settings settings) | ||
{ | ||
Initialize(settings, requiresWriting: false); | ||
|
||
Debug.Assert(XvdFile != null, "XvdFile != null"); | ||
|
||
using (XvdFile) | ||
{ | ||
var result = XvdFile.VerifyDataHashes(); | ||
|
||
ConsoleLogger.WriteInfoLine(result | ||
? "Integrity check [green bold]successful[/]." | ||
: "Integrity check [red bold]failed[/]."); | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
public override ValidationResult Validate(CommandContext context, Settings settings) | ||
{ | ||
base.Validate(context, settings); | ||
|
||
Debug.Assert(settings.XvcPath != null, "settings.XvcPath != null"); | ||
|
||
if (settings.XvcPath.StartsWith("http")) | ||
return ValidationResult.Error("Only local files are supported for integrity verification."); | ||
|
||
if (!File.Exists(settings.XvcPath)) | ||
return ValidationResult.Error("Provided file does not exist."); | ||
|
||
return ValidationResult.Success(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
using System.Diagnostics; | ||
using Spectre.Console; | ||
using Spectre.Console.Cli; | ||
|
||
namespace XvdTool.Streaming.Commands; | ||
|
||
internal abstract class XvdCommand<T> : Command<T> where T : XvdCommandSettings | ||
{ | ||
protected StreamedXvdFile XvdFile = default!; | ||
|
||
protected void Initialize(XvdCommandSettings settings, bool requiresWriting) | ||
{ | ||
Debug.Assert(settings.XvcPath != null, "settings.XvcPath != null"); | ||
|
||
var path = settings.XvcPath; | ||
|
||
XvdFile = path.StartsWith("http") | ||
? StreamedXvdFile.OpenFromUrl(path) | ||
: StreamedXvdFile.OpenFromFile(path, requiresWriting); | ||
|
||
XvdFile.Parse(); | ||
} | ||
|
||
public override ValidationResult Validate(CommandContext context, T settings) | ||
{ | ||
if (settings.XvcPath != null && !settings.XvcPath.StartsWith("http") && !File.Exists(settings.XvcPath)) | ||
return ValidationResult.Error("Provided file does not exist."); | ||
|
||
return ValidationResult.Success(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
using System.ComponentModel; | ||
using Spectre.Console.Cli; | ||
|
||
namespace XvdTool.Streaming.Commands; | ||
|
||
internal abstract class XvdCommandSettings : CommandSettings | ||
{ | ||
[Description("File Path / URL to the XVC.")] | ||
[CommandArgument(0, "<path/url>")] | ||
public string? XvcPath { get; init; } | ||
} |
Oops, something went wrong.