Add ExternalKeySet. Use C# 8.0

This commit is contained in:
Alex Barney 2019-10-02 13:45:58 -05:00
parent d7f3e94577
commit d291500b28
16 changed files with 266 additions and 59 deletions

View file

@ -1,4 +1,5 @@
using System; using System;
using System.Diagnostics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace LibHac.Common namespace LibHac.Common
@ -6,13 +7,14 @@ namespace LibHac.Common
/// <summary> /// <summary>
/// A generic 128-bit ID value. /// A generic 128-bit ID value.
/// </summary> /// </summary>
[DebuggerDisplay("{ToString()}")]
[StructLayout(LayoutKind.Sequential, Size = 0x10)] [StructLayout(LayoutKind.Sequential, Size = 0x10)]
public struct Id128 : IEquatable<Id128>, IComparable<Id128>, IComparable public struct Id128 : IEquatable<Id128>, IComparable<Id128>, IComparable
{ {
public readonly ulong High; public readonly ulong High;
public readonly ulong Low; public readonly ulong Low;
public static readonly Id128 InvalidId = new Id128(0, 0); public static Id128 Zero => default;
public Id128(ulong high, ulong low) public Id128(ulong high, ulong low)
{ {
@ -28,6 +30,8 @@ namespace LibHac.Common
Low = longs[1]; Low = longs[1];
} }
public override string ToString() => AsBytes().ToHexString();
public bool Equals(Id128 other) public bool Equals(Id128 other)
{ {
return High == other.High && Low == other.Low; return High == other.High && Low == other.Low;
@ -48,11 +52,9 @@ namespace LibHac.Common
public int CompareTo(Id128 other) public int CompareTo(Id128 other)
{ {
// ReSharper disable ImpureMethodCallOnReadonlyValueField
int highComparison = High.CompareTo(other.High); int highComparison = High.CompareTo(other.High);
if (highComparison != 0) return highComparison; if (highComparison != 0) return highComparison;
return Low.CompareTo(other.Low); return Low.CompareTo(other.Low);
// ReSharper restore ImpureMethodCallOnReadonlyValueField
} }
public int CompareTo(object obj) public int CompareTo(object obj)
@ -61,7 +63,7 @@ namespace LibHac.Common
return obj is Id128 other ? CompareTo(other) : throw new ArgumentException($"Object must be of type {nameof(Id128)}"); return obj is Id128 other ? CompareTo(other) : throw new ArgumentException($"Object must be of type {nameof(Id128)}");
} }
public void ToBytes(Span<byte> output) public readonly void ToBytes(Span<byte> output)
{ {
Span<ulong> longs = MemoryMarshal.Cast<byte, ulong>(output); Span<ulong> longs = MemoryMarshal.Cast<byte, ulong>(output);
@ -69,33 +71,17 @@ namespace LibHac.Common
longs[1] = Low; longs[1] = Low;
} }
public static bool operator ==(Id128 left, Id128 right) public ReadOnlySpan<byte> AsBytes()
{ {
return left.Equals(right); return SpanHelpers.AsByteSpan(ref this);
} }
public static bool operator !=(Id128 left, Id128 right) public static bool operator ==(Id128 left, Id128 right) => left.Equals(right);
{ public static bool operator !=(Id128 left, Id128 right) => !left.Equals(right);
return !left.Equals(right);
}
public static bool operator <(Id128 left, Id128 right)
{
return left.CompareTo(right) < 0;
}
public static bool operator >(Id128 left, Id128 right) public static bool operator <(Id128 left, Id128 right) => left.CompareTo(right) < 0;
{ public static bool operator >(Id128 left, Id128 right) => left.CompareTo(right) > 0;
return left.CompareTo(right) > 0; public static bool operator <=(Id128 left, Id128 right) => left.CompareTo(right) <= 0;
} public static bool operator >=(Id128 left, Id128 right) => left.CompareTo(right) >= 0;
public static bool operator <=(Id128 left, Id128 right)
{
return left.CompareTo(right) <= 0;
}
public static bool operator >=(Id128 left, Id128 right)
{
return left.CompareTo(right) >= 0;
}
} }
} }

View file

@ -0,0 +1,48 @@
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace LibHac.Common
{
[DebuggerDisplay("{ToString()}")]
[StructLayout(LayoutKind.Sequential, Size = 0x10)]
public struct Key128 : IEquatable<Key128>
{
private readonly ulong _dummy1;
private readonly ulong _dummy2;
public Span<byte> Value => SpanHelpers.AsByteSpan(ref this);
public Key128(ReadOnlySpan<byte> bytes)
{
ReadOnlySpan<ulong> longs = MemoryMarshal.Cast<byte, ulong>(bytes);
_dummy1 = longs[0];
_dummy2 = longs[1];
}
public override string ToString() => Value.ToHexString();
public override bool Equals(object obj)
{
return obj is Key128 key && Equals(key);
}
public bool Equals(Key128 other)
{
return _dummy1 == other._dummy1 &&
_dummy2 == other._dummy2;
}
public override int GetHashCode()
{
int hashCode = -1653217991;
hashCode = hashCode * -1521134295 + _dummy1.GetHashCode();
hashCode = hashCode * -1521134295 + _dummy2.GetHashCode();
return hashCode;
}
public static bool operator ==(Key128 left, Key128 right) => left.Equals(right);
public static bool operator !=(Key128 left, Key128 right) => !(left == right);
}
}

View file

@ -121,7 +121,7 @@ namespace LibHac.Fs
if (!openMode.HasFlag(OpenMode.AllowAppend)) if (!openMode.HasFlag(OpenMode.AllowAppend))
{ {
return ResultFs.AllowAppendRequiredForImplicitExtension.Log(); return ResultFs.FileExtensionWithoutOpenModeAllowAppend.Log();
} }
} }

View file

@ -11,7 +11,9 @@
public static Result InsufficientFreeSpace => new Result(ModuleFs, 30); public static Result InsufficientFreeSpace => new Result(ModuleFs, 30);
public static Result MountNameAlreadyExists => new Result(ModuleFs, 60); public static Result MountNameAlreadyExists => new Result(ModuleFs, 60);
public static Result PartitionNotFound => new Result(ModuleFs, 1001);
public static Result TargetNotFound => new Result(ModuleFs, 1002); public static Result TargetNotFound => new Result(ModuleFs, 1002);
public static Result ExternalKeyNotFound => new Result(ModuleFs, 1004);
public static Result NotImplemented => new Result(ModuleFs, 3001); public static Result NotImplemented => new Result(ModuleFs, 3001);
public static Result Result3002 => new Result(ModuleFs, 3002); public static Result Result3002 => new Result(ModuleFs, 3002);
@ -80,9 +82,11 @@
public static Result InvalidSize => new Result(ModuleFs, 6062); public static Result InvalidSize => new Result(ModuleFs, 6062);
public static Result NullArgument => new Result(ModuleFs, 6063); public static Result NullArgument => new Result(ModuleFs, 6063);
public static Result InvalidMountName => new Result(ModuleFs, 6065); public static Result InvalidMountName => new Result(ModuleFs, 6065);
public static Result ExtensionSizeTooLarge => new Result(ModuleFs, 6066);
public static Result ExtensionSizeInvalid => new Result(ModuleFs, 6067);
public static Result InvalidOpenModeOperation => new Result(ModuleFs, 6200); public static Result InvalidOpenModeOperation => new Result(ModuleFs, 6200);
public static Result AllowAppendRequiredForImplicitExtension => new Result(ModuleFs, 6201); public static Result FileExtensionWithoutOpenModeAllowAppend => new Result(ModuleFs, 6201);
public static Result InvalidOpenModeForRead => new Result(ModuleFs, 6202); public static Result InvalidOpenModeForRead => new Result(ModuleFs, 6202);
public static Result InvalidOpenModeForWrite => new Result(ModuleFs, 6203); public static Result InvalidOpenModeForWrite => new Result(ModuleFs, 6203);
@ -105,11 +109,16 @@
public static Result UnsupportedOperationInPartitionFileSetSize => new Result(ModuleFs, 6376); public static Result UnsupportedOperationInPartitionFileSetSize => new Result(ModuleFs, 6376);
public static Result PermissionDenied => new Result(ModuleFs, 6400); public static Result PermissionDenied => new Result(ModuleFs, 6400);
public static Result ExternalKeyAlreadyRegistered => new Result(ModuleFs, 6452);
public static Result WriteStateUnflushed => new Result(ModuleFs, 6454); public static Result WriteStateUnflushed => new Result(ModuleFs, 6454);
public static Result WritableFileOpen => new Result(ModuleFs, 6457); public static Result WritableFileOpen => new Result(ModuleFs, 6457);
public static Result MappingTableFull => new Result(ModuleFs, 6706);
public static Result AllocationTableInsufficientFreeBlocks => new Result(ModuleFs, 6707); public static Result AllocationTableInsufficientFreeBlocks => new Result(ModuleFs, 6707);
public static Result OpenCountLimit => new Result(ModuleFs, 6709);
public static Result RemapStorageMapFull => new Result(ModuleFs, 6811);
public static Result Result6902 => new Result(ModuleFs, 6902); public static Result Result6902 => new Result(ModuleFs, 6902);
public static Result MountNameNotFound => new Result(ModuleFs, 6905); public static Result MountNameNotFound => new Result(ModuleFs, 6905);

View file

@ -1,9 +1,11 @@
using System; using System;
using System.Diagnostics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using LibHac.Common; using LibHac.Common;
namespace LibHac.Fs namespace LibHac.Fs
{ {
[DebuggerDisplay("{DebugDisplay(),nq}")]
[StructLayout(LayoutKind.Sequential, Size = 0x10)] [StructLayout(LayoutKind.Sequential, Size = 0x10)]
public struct RightsId : IEquatable<RightsId>, IComparable<RightsId>, IComparable public struct RightsId : IEquatable<RightsId>, IComparable<RightsId>, IComparable
{ {
@ -19,12 +21,21 @@ namespace LibHac.Fs
Id = new Id128(uid); Id = new Id128(uid);
} }
public override string ToString() => Id.ToString();
public string DebugDisplay()
{
ReadOnlySpan<byte> highBytes = AsBytes().Slice(0, 8);
ReadOnlySpan<byte> lowBytes = AsBytes().Slice(8, 8);
return $"{highBytes.ToHexString()} {lowBytes.ToHexString()}";
}
public bool Equals(RightsId other) => Id == other.Id; public bool Equals(RightsId other) => Id == other.Id;
public override bool Equals(object obj) => obj is RightsId other && Equals(other); public override bool Equals(object obj) => obj is RightsId other && Equals(other);
public override int GetHashCode() => Id.GetHashCode(); public override int GetHashCode() => Id.GetHashCode();
// ReSharper disable once ImpureMethodCallOnReadonlyValueField
public int CompareTo(RightsId other) => Id.CompareTo(other.Id); public int CompareTo(RightsId other) => Id.CompareTo(other.Id);
public int CompareTo(object obj) public int CompareTo(object obj)
@ -33,9 +44,13 @@ namespace LibHac.Fs
return obj is RightsId other ? CompareTo(other) : throw new ArgumentException($"Object must be of type {nameof(RightsId)}"); return obj is RightsId other ? CompareTo(other) : throw new ArgumentException($"Object must be of type {nameof(RightsId)}");
} }
// ReSharper disable once ImpureMethodCallOnReadonlyValueField
public void ToBytes(Span<byte> output) => Id.ToBytes(output); public void ToBytes(Span<byte> output) => Id.ToBytes(output);
public ReadOnlySpan<byte> AsBytes()
{
return SpanHelpers.AsByteSpan(ref this);
}
public static bool operator ==(RightsId left, RightsId right) => left.Equals(right); public static bool operator ==(RightsId left, RightsId right) => left.Equals(right);
public static bool operator !=(RightsId left, RightsId right) => !left.Equals(right); public static bool operator !=(RightsId left, RightsId right) => !left.Equals(right);

View file

@ -7,7 +7,7 @@ namespace LibHac.Fs
{ {
public static Result MountSystemSaveData(this FileSystemClient fs, U8Span mountName, SaveDataSpaceId spaceId, ulong saveDataId) public static Result MountSystemSaveData(this FileSystemClient fs, U8Span mountName, SaveDataSpaceId spaceId, ulong saveDataId)
{ {
return MountSystemSaveData(fs, mountName, spaceId, saveDataId, UserId.EmptyId); return MountSystemSaveData(fs, mountName, spaceId, saveDataId, UserId.Zero);
} }
public static Result MountSystemSaveData(this FileSystemClient fs, U8Span mountName, public static Result MountSystemSaveData(this FileSystemClient fs, U8Span mountName,
@ -72,19 +72,19 @@ namespace LibHac.Fs
public static Result CreateSystemSaveData(this FileSystemClient fs, ulong saveDataId, ulong ownerId, long size, public static Result CreateSystemSaveData(this FileSystemClient fs, ulong saveDataId, ulong ownerId, long size,
long journalSize, uint flags) long journalSize, uint flags)
{ {
return CreateSystemSaveData(fs, SaveDataSpaceId.System, saveDataId, UserId.EmptyId, ownerId, size, journalSize, flags); return CreateSystemSaveData(fs, SaveDataSpaceId.System, saveDataId, UserId.Zero, ownerId, size, journalSize, flags);
} }
public static Result CreateSystemSaveData(this FileSystemClient fs, ulong saveDataId, long size, public static Result CreateSystemSaveData(this FileSystemClient fs, ulong saveDataId, long size,
long journalSize, uint flags) long journalSize, uint flags)
{ {
return CreateSystemSaveData(fs, SaveDataSpaceId.System, saveDataId, UserId.EmptyId, 0, size, journalSize, flags); return CreateSystemSaveData(fs, SaveDataSpaceId.System, saveDataId, UserId.Zero, 0, size, journalSize, flags);
} }
public static Result CreateSystemSaveData(this FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId, public static Result CreateSystemSaveData(this FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId,
ulong ownerId, long size, long journalSize, uint flags) ulong ownerId, long size, long journalSize, uint flags)
{ {
return CreateSystemSaveData(fs, spaceId, saveDataId, UserId.EmptyId, ownerId, size, journalSize, flags); return CreateSystemSaveData(fs, spaceId, saveDataId, UserId.Zero, ownerId, size, journalSize, flags);
} }
public static Result DeleteSaveData(this FileSystemClient fs, ulong saveDataId) public static Result DeleteSaveData(this FileSystemClient fs, ulong saveDataId)

View file

@ -1,13 +1,15 @@
using System; using System;
using System.Diagnostics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using LibHac.Common; using LibHac.Common;
namespace LibHac.Fs namespace LibHac.Fs
{ {
[DebuggerDisplay("{ToString(),nq}")]
[StructLayout(LayoutKind.Sequential, Size = 0x10)] [StructLayout(LayoutKind.Sequential, Size = 0x10)]
public struct UserId : IEquatable<UserId>, IComparable<UserId>, IComparable public struct UserId : IEquatable<UserId>, IComparable<UserId>, IComparable
{ {
public static readonly UserId EmptyId = new UserId(0, 0); public static UserId Zero => default;
public readonly Id128 Id; public readonly Id128 Id;
@ -21,12 +23,16 @@ namespace LibHac.Fs
Id = new Id128(uid); Id = new Id128(uid);
} }
public override string ToString()
{
return $"0x{Id.High:x8}{Id.Low:x8}";
}
public bool Equals(UserId other) => Id == other.Id; public bool Equals(UserId other) => Id == other.Id;
public override bool Equals(object obj) => obj is UserId other && Equals(other); public override bool Equals(object obj) => obj is UserId other && Equals(other);
public override int GetHashCode() => Id.GetHashCode(); public override int GetHashCode() => Id.GetHashCode();
// ReSharper disable once ImpureMethodCallOnReadonlyValueField
public int CompareTo(UserId other) => Id.CompareTo(other.Id); public int CompareTo(UserId other) => Id.CompareTo(other.Id);
public int CompareTo(object obj) public int CompareTo(object obj)
@ -35,9 +41,13 @@ namespace LibHac.Fs
return obj is UserId other ? CompareTo(other) : throw new ArgumentException($"Object must be of type {nameof(UserId)}"); return obj is UserId other ? CompareTo(other) : throw new ArgumentException($"Object must be of type {nameof(UserId)}");
} }
// ReSharper disable once ImpureMethodCallOnReadonlyValueField
public void ToBytes(Span<byte> output) => Id.ToBytes(output); public void ToBytes(Span<byte> output) => Id.ToBytes(output);
public ReadOnlySpan<byte> AsBytes()
{
return SpanHelpers.AsByteSpan(ref this);
}
public static bool operator ==(UserId left, UserId right) => left.Equals(right); public static bool operator ==(UserId left, UserId right) => left.Equals(right);
public static bool operator !=(UserId left, UserId right) => !left.Equals(right); public static bool operator !=(UserId left, UserId right) => !left.Equals(right);

View file

@ -0,0 +1,108 @@
using System;
using System.Collections.Generic;
using LibHac.Fs;
using LibHac.Spl;
namespace LibHac.FsService
{
public class ExternalKeySet
{
private readonly object _locker = new object();
private Dictionary<RightsId, AccessKey> ExternalKeys { get; set; } = new Dictionary<RightsId, AccessKey>();
public Result Add(RightsId rightsId, AccessKey key)
{
lock (_locker)
{
if (ExternalKeys.TryGetValue(rightsId, out AccessKey existingKey))
{
if (key == existingKey)
{
return Result.Success;
}
return ResultFs.ExternalKeyAlreadyRegistered.Log();
}
ExternalKeys.Add(rightsId, key);
}
return Result.Success;
}
public Result Get(RightsId rightsId, out AccessKey key)
{
lock (_locker)
{
if (ExternalKeys.TryGetValue(rightsId, out key))
{
return Result.Success;
}
return ResultFs.ExternalKeyNotFound.Log();
}
}
public bool Contains(RightsId rightsId)
{
lock (_locker)
{
return ExternalKeys.ContainsKey(rightsId);
}
}
public bool Remove(RightsId rightsId)
{
lock (_locker)
{
return ExternalKeys.Remove(rightsId);
}
}
public void Clear()
{
lock (_locker)
{
ExternalKeys.Clear();
}
}
public List<(RightsId rightsId, AccessKey key)> ToList()
{
lock (_locker)
{
var list = new List<(RightsId rightsId, AccessKey key)>(ExternalKeys.Count);
foreach (KeyValuePair<RightsId, AccessKey> kvp in ExternalKeys)
{
list.Add((kvp.Key, kvp.Value));
}
return list;
}
}
public void TrimExcess() => TrimExcess(0);
public void TrimExcess(int capacity)
{
lock (_locker)
{
int newCapacity = Math.Max(capacity, ExternalKeys.Count);
#if NETCOREAPP
ExternalKeys.TrimExcess(newCapacity);
#else
var trimmedDict = new Dictionary<RightsId, AccessKey>(newCapacity);
foreach (KeyValuePair<RightsId, AccessKey> kvp in ExternalKeys)
{
trimmedDict.Add(kvp.Key, kvp.Value);
}
ExternalKeys = trimmedDict;
#endif
}
}
}
}

View file

@ -147,7 +147,7 @@ namespace LibHac.FsService
private Result OpenSaveDataFileSystemImpl(out IFileSystem fileSystem, out ulong saveDataId, private Result OpenSaveDataFileSystemImpl(out IFileSystem fileSystem, out ulong saveDataId,
SaveDataSpaceId spaceId, ref SaveDataAttribute attribute, bool openReadOnly, bool cacheExtraData) SaveDataSpaceId spaceId, ref SaveDataAttribute attribute, bool openReadOnly, bool cacheExtraData)
{ {
bool hasFixedId = attribute.SaveDataId != 0 && attribute.UserId.Id == Id128.InvalidId; bool hasFixedId = attribute.SaveDataId != 0 && attribute.UserId == UserId.Zero;
if (hasFixedId) if (hasFixedId)
{ {

View file

@ -4,6 +4,7 @@ using System.Diagnostics;
using System.IO; using System.IO;
using LibHac.Fs; using LibHac.Fs;
using LibHac.FsSystem.RomFs; using LibHac.FsSystem.RomFs;
using LibHac.Spl;
namespace LibHac.FsSystem.NcaUtils namespace LibHac.FsSystem.NcaUtils
{ {
@ -47,9 +48,9 @@ namespace LibHac.FsSystem.NcaUtils
int keyRevision = Util.GetMasterKeyRevision(Header.KeyGeneration); int keyRevision = Util.GetMasterKeyRevision(Header.KeyGeneration);
byte[] titleKek = Keyset.TitleKeks[keyRevision]; byte[] titleKek = Keyset.TitleKeks[keyRevision];
if (!Keyset.TitleKeys.TryGetValue(Header.RightsId.ToArray(), out byte[] encryptedKey)) if (Keyset.ExternalKeySet.Get(new RightsId(Header.RightsId), out AccessKey accessKey).IsFailure())
{ {
throw new MissingKeyException("Missing NCA title key.", Header.RightsId.ToHexString(), KeyType.Title); throw new MissingKeyException("Missing NCA title key.", Header.RightsId.ToString(), KeyType.Title);
} }
if (titleKek.IsEmpty()) if (titleKek.IsEmpty())
@ -58,6 +59,7 @@ namespace LibHac.FsSystem.NcaUtils
throw new MissingKeyException("Unable to decrypt title key.", keyName, KeyType.Common); throw new MissingKeyException("Unable to decrypt title key.", keyName, KeyType.Common);
} }
byte[] encryptedKey = accessKey.Value.ToArray();
var decryptedKey = new byte[Crypto.Aes128Size]; var decryptedKey = new byte[Crypto.Aes128Size];
Crypto.DecryptEcb(titleKek, encryptedKey, decryptedKey, Crypto.Aes128Size); Crypto.DecryptEcb(titleKek, encryptedKey, decryptedKey, Crypto.Aes128Size);
@ -89,7 +91,7 @@ namespace LibHac.FsSystem.NcaUtils
if (Header.HasRightsId) if (Header.HasRightsId)
{ {
return Keyset.TitleKeys.ContainsKey(Header.RightsId.ToArray()) && return Keyset.ExternalKeySet.Contains(new RightsId(Header.RightsId)) &&
!Keyset.TitleKeks[keyRevision].IsEmpty(); !Keyset.TitleKeks[keyRevision].IsEmpty();
} }

View file

@ -4,7 +4,10 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
using LibHac.Fs;
using LibHac.FsService;
using LibHac.FsSystem; using LibHac.FsSystem;
using LibHac.Spl;
namespace LibHac namespace LibHac
{ {
@ -129,7 +132,7 @@ namespace LibHac
0x49, 0x50, 0x95, 0x8C, 0x55, 0x80, 0x7E, 0x39, 0xB1, 0x48, 0x05, 0x1E, 0x21, 0xC7, 0x24, 0x4F 0x49, 0x50, 0x95, 0x8C, 0x55, 0x80, 0x7E, 0x39, 0xB1, 0x48, 0x05, 0x1E, 0x21, 0xC7, 0x24, 0x4F
}; };
public Dictionary<byte[], byte[]> TitleKeys { get; } = new Dictionary<byte[], byte[]>(new ByteArray128BitComparer()); public ExternalKeySet ExternalKeySet { get; } = new ExternalKeySet();
public void SetSdSeed(byte[] sdseed) public void SetSdSeed(byte[] sdseed)
{ {
@ -402,6 +405,7 @@ namespace LibHac
if (filename != null) ReadMainKeys(keyset, filename, AllKeyDict, logger); if (filename != null) ReadMainKeys(keyset, filename, AllKeyDict, logger);
if (consoleKeysFilename != null) ReadMainKeys(keyset, consoleKeysFilename, AllKeyDict, logger); if (consoleKeysFilename != null) ReadMainKeys(keyset, consoleKeysFilename, AllKeyDict, logger);
if (titleKeysFilename != null) ReadTitleKeys(keyset, titleKeysFilename, logger); if (titleKeysFilename != null) ReadTitleKeys(keyset, titleKeysFilename, logger);
keyset.ExternalKeySet.TrimExcess();
keyset.DeriveKeys(logger); keyset.DeriveKeys(logger);
} }
@ -507,7 +511,7 @@ namespace LibHac
continue; continue;
} }
keyset.TitleKeys[rightsId] = titleKey; keyset.ExternalKeySet.Add(new RightsId(rightsId), new AccessKey(titleKey)).ThrowIfFailure();
} }
} }
} }
@ -557,9 +561,9 @@ namespace LibHac
{ {
var sb = new StringBuilder(); var sb = new StringBuilder();
foreach (KeyValuePair<byte[], byte[]> kv in keyset.TitleKeys.OrderBy(x => x.Key.ToHexString())) foreach ((RightsId rightsId, AccessKey key) kv in keyset.ExternalKeySet.ToList().OrderBy(x => x.rightsId.ToString()))
{ {
string line = $"{kv.Key.ToHexString()} = {kv.Value.ToHexString()}"; string line = $"{kv.rightsId} = {kv.key}";
sb.AppendLine(line); sb.AppendLine(line);
} }

View file

@ -3,7 +3,7 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<TargetFrameworks>netcoreapp2.1;netstandard2.0;net46</TargetFrameworks> <TargetFrameworks>netcoreapp2.1;netstandard2.0;net46</TargetFrameworks>
<LangVersion>7.3</LangVersion> <LangVersion>8.0</LangVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>

View file

@ -1,9 +1,11 @@
using System; using System;
using System.Diagnostics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using LibHac.Common; using LibHac.Common;
namespace LibHac.Ncm namespace LibHac.Ncm
{ {
[DebuggerDisplay("{ToString()}")]
[StructLayout(LayoutKind.Sequential, Size = 0x10)] [StructLayout(LayoutKind.Sequential, Size = 0x10)]
public struct ContentId : IEquatable<ContentId>, IComparable<ContentId>, IComparable public struct ContentId : IEquatable<ContentId>, IComparable<ContentId>, IComparable
{ {
@ -19,12 +21,13 @@ namespace LibHac.Ncm
Id = new Id128(uid); Id = new Id128(uid);
} }
public override string ToString() => Id.ToString();
public bool Equals(ContentId other) => Id == other.Id; public bool Equals(ContentId other) => Id == other.Id;
public override bool Equals(object obj) => obj is ContentId other && Equals(other); public override bool Equals(object obj) => obj is ContentId other && Equals(other);
public override int GetHashCode() => Id.GetHashCode(); public override int GetHashCode() => Id.GetHashCode();
// ReSharper disable once ImpureMethodCallOnReadonlyValueField
public int CompareTo(ContentId other) => Id.CompareTo(other.Id); public int CompareTo(ContentId other) => Id.CompareTo(other.Id);
public int CompareTo(object obj) public int CompareTo(object obj)
@ -33,9 +36,13 @@ namespace LibHac.Ncm
return obj is ContentId other ? CompareTo(other) : throw new ArgumentException($"Object must be of type {nameof(ContentId)}"); return obj is ContentId other ? CompareTo(other) : throw new ArgumentException($"Object must be of type {nameof(ContentId)}");
} }
// ReSharper disable once ImpureMethodCallOnReadonlyValueField
public void ToBytes(Span<byte> output) => Id.ToBytes(output); public void ToBytes(Span<byte> output) => Id.ToBytes(output);
public ReadOnlySpan<byte> AsBytes()
{
return SpanHelpers.AsByteSpan(ref this);
}
public static bool operator ==(ContentId left, ContentId right) => left.Equals(right); public static bool operator ==(ContentId left, ContentId right) => left.Equals(right);
public static bool operator !=(ContentId left, ContentId right) => !left.Equals(right); public static bool operator !=(ContentId left, ContentId right) => !left.Equals(right);

View file

@ -1,9 +1,11 @@
using System; using System;
using System.Diagnostics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using LibHac.Common; using LibHac.Common;
namespace LibHac.Ncm namespace LibHac.Ncm
{ {
[DebuggerDisplay("{ToString()}")]
[StructLayout(LayoutKind.Sequential, Size = 0x10)] [StructLayout(LayoutKind.Sequential, Size = 0x10)]
public struct PlaceHolderId : IEquatable<PlaceHolderId>, IComparable<PlaceHolderId>, IComparable public struct PlaceHolderId : IEquatable<PlaceHolderId>, IComparable<PlaceHolderId>, IComparable
{ {
@ -19,12 +21,13 @@ namespace LibHac.Ncm
Id = new Id128(uid); Id = new Id128(uid);
} }
public override string ToString() => Id.ToString();
public bool Equals(PlaceHolderId other) => Id == other.Id; public bool Equals(PlaceHolderId other) => Id == other.Id;
public override bool Equals(object obj) => obj is PlaceHolderId other && Equals(other); public override bool Equals(object obj) => obj is PlaceHolderId other && Equals(other);
public override int GetHashCode() => Id.GetHashCode(); public override int GetHashCode() => Id.GetHashCode();
// ReSharper disable once ImpureMethodCallOnReadonlyValueField
public int CompareTo(PlaceHolderId other) => Id.CompareTo(other.Id); public int CompareTo(PlaceHolderId other) => Id.CompareTo(other.Id);
public int CompareTo(object obj) public int CompareTo(object obj)
@ -33,9 +36,13 @@ namespace LibHac.Ncm
return obj is PlaceHolderId other ? CompareTo(other) : throw new ArgumentException($"Object must be of type {nameof(PlaceHolderId)}"); return obj is PlaceHolderId other ? CompareTo(other) : throw new ArgumentException($"Object must be of type {nameof(PlaceHolderId)}");
} }
// ReSharper disable once ImpureMethodCallOnReadonlyValueField
public void ToBytes(Span<byte> output) => Id.ToBytes(output); public void ToBytes(Span<byte> output) => Id.ToBytes(output);
public ReadOnlySpan<byte> AsBytes()
{
return SpanHelpers.AsByteSpan(ref this);
}
public static bool operator ==(PlaceHolderId left, PlaceHolderId right) => left.Equals(right); public static bool operator ==(PlaceHolderId left, PlaceHolderId right) => left.Equals(right);
public static bool operator !=(PlaceHolderId left, PlaceHolderId right) => !left.Equals(right); public static bool operator !=(PlaceHolderId left, PlaceHolderId right) => !left.Equals(right);

View file

@ -1,6 +1,5 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using LibHac.Common; using LibHac.Common;
@ -8,13 +7,23 @@ namespace LibHac.Spl
{ {
[DebuggerDisplay("{ToString()}")] [DebuggerDisplay("{ToString()}")]
[StructLayout(LayoutKind.Sequential, Size = 0x10)] [StructLayout(LayoutKind.Sequential, Size = 0x10)]
public struct AccessKey public struct AccessKey : IEquatable<AccessKey>
{ {
private long _dummy1; private readonly Key128 Key;
private long _dummy2;
public Span<byte> Key => SpanHelpers.CreateSpan(ref Unsafe.As<long, byte>(ref _dummy1), 0x10); public ReadOnlySpan<byte> Value => SpanHelpers.AsByteSpan(ref this);
public override string ToString() => Key.ToHexString(); public AccessKey(ReadOnlySpan<byte> bytes)
{
Key = new Key128(bytes);
}
public override string ToString() => Key.ToString();
public override bool Equals(object obj) => obj is AccessKey key && Equals(key);
public bool Equals(AccessKey other) => Key.Equals(other.Key);
public override int GetHashCode() => Key.GetHashCode();
public static bool operator ==(AccessKey left, AccessKey right) => left.Equals(right);
public static bool operator !=(AccessKey left, AccessKey right) => !(left == right);
} }
} }

View file

@ -354,7 +354,9 @@ namespace LibHac
public static string ToHexString(this byte[] bytes) => ToHexString(bytes.AsSpan()); public static string ToHexString(this byte[] bytes) => ToHexString(bytes.AsSpan());
public static string ToHexString(this Span<byte> bytes) public static string ToHexString(this Span<byte> bytes) => ToHexString((ReadOnlySpan<byte>)bytes);
public static string ToHexString(this ReadOnlySpan<byte> bytes)
{ {
uint[] lookup32 = Lookup32; uint[] lookup32 = Lookup32;
var result = new char[bytes.Length * 2]; var result = new char[bytes.Length * 2];