diff --git a/src/LibHac/Common/FixedArrays/Arrays.cs b/src/LibHac/Common/FixedArrays/Arrays.cs index 64cf3016..990a6fd4 100644 --- a/src/LibHac/Common/FixedArrays/Arrays.cs +++ b/src/LibHac/Common/FixedArrays/Arrays.cs @@ -50,6 +50,7 @@ namespace LibHac.Common.FixedArrays; [InlineArray(640)] public struct Array640 { public readonly int Length => 640; private T _0; } [InlineArray(768)] public struct Array768 { public readonly int Length => 768; private T _0; } [InlineArray(769)] public struct Array769 { public readonly int Length => 769; private T _0; } +[InlineArray(1024)] public struct Array1024 { public readonly int Length => 1024; private T _0; } [InlineArray(3000)] public struct Array3000 { public readonly int Length => 3000; private T _0; } [InlineArray(3438)] public struct Array3438 { public readonly int Length => 3438; private T _0; } [InlineArray(5366)] public struct Array5366 { public readonly int Length => 5366; private T _0; } diff --git a/src/LibHac/Gc/GameCardEmulated.cs b/src/LibHac/Gc/GameCardEmulated.cs index 08cd34ca..7e470074 100644 --- a/src/LibHac/Gc/GameCardEmulated.cs +++ b/src/LibHac/Gc/GameCardEmulated.cs @@ -428,4 +428,12 @@ public sealed class GameCardEmulated : IGcApi outErrorReportInfo = default; return Result.Success; } + + public Result GetAsicCertificate(out GameCardAsicCertificateSet outCertificateSet) + { + Abort.DoAbortUnless(_initialized, LibNotInitializedMessage); + + outCertificateSet = default; + return Result.Success; + } } \ No newline at end of file diff --git a/src/LibHac/Gc/GameCardTypes.cs b/src/LibHac/Gc/GameCardTypes.cs index 9ebd4e77..a63fbb11 100644 --- a/src/LibHac/Gc/GameCardTypes.cs +++ b/src/LibHac/Gc/GameCardTypes.cs @@ -30,4 +30,12 @@ public struct GameCardIdSet public CardId1 Id1; public CardId2 Id2; public CardId3 Id3; +} + +public struct GameCardAsicCertificateSet +{ + public Array1024 Certificate; + public Array16 SerialNumber; + public Array256 PublicKeyModulus; + public Array3 PublicKeyExponent; } \ No newline at end of file diff --git a/src/LibHac/Gc/GameCardValues.cs b/src/LibHac/Gc/GameCardValues.cs index 066a6757..aa66ff73 100644 --- a/src/LibHac/Gc/GameCardValues.cs +++ b/src/LibHac/Gc/GameCardValues.cs @@ -5,17 +5,61 @@ namespace LibHac.Gc; public static class Values { public const int GcPageSize = 0x200; - public const int GcAsicFirmwareSize = 1024 * 30; // 30 KiB - public const int GcCardDeviceIdSize = 0x10; - public const int GcChallengeCardExistenceResponseSize = 0x58; - public const int GcCardImageHashSize = 0x20; - public const int GcDeviceCertificateSize = 0x200; - public const int GcCardKeyAreaSize = GcCardKeyAreaPageCount * GcPageSize; + public const int GcCardKeyAreaPageCount = 8; - public const int GcCertAreaPageAddress = 56; + public const int GcCardKeyAreaSize = GcCardKeyAreaPageCount * GcPageSize; + + public const int GcCardHeaderPageAddress = 0; + public const int GcCardHeaderPageCount = 1; + + public const int GcReservedAreaPageCount = 0x37; + + public const int GcCertAreaPageAddress = GcCardHeaderPageCount + GcReservedAreaPageCount; + public const int GcDeviceCertificatePageCount = 1; + public const int GcCertAreaPageCount = 0x40; + + public const int GcPackageIdSize = 8; + public const int GcPartitionFsHeaderHashSize = 0x20; + public const int GcCardDeviceIdSize = 0x10; + public const int GcDeviceCertificateSize = 0x200; + public const int GcCardImageHashSize = 0x20; + + public const int GcChallengeCardExistenceResponseSize = 0x58; + public const int GcChallengeCardExistenceSeedSize = 0xF; + public const int GcChallengeCardExistenceValueSize = 0x10; + + public const int GcMmcCmd60DataSize = 0x40; + + public const int GcAsicFirmwareSize = 1024 * 30; // 30 KiB + + public const int GcAsicSerialNumberLength = 0x10; + + public const int GcRandomValueSize = 32; + public const int GcRandomValueForKeyUpdateSocSize = 31; + public const int GcRsaOaepSeedSize = 32; + + public const int GcAesBlockLength = Aes.BlockSize; + + public const int GcRsaKeyLength = Rsa.ModulusSize2048Pss; + public const int GcRsaPublicExponentLength = Rsa.MaximumExponentSize2048Pss; + public const int GcAesKeyLength = Aes.KeySize128; public const int GcAesCbcIvLength = Aes.KeySize128; + public const int GcHmacKeyLength = 0x20; + public const int GcCvConstLength = 0x10; + public const int GcTitleKeyKekIndexMax = 0x10; + public const int GcSha256HashLength = Sha256.DigestSize; + + public const int GcSendCommandMaxCount = 3; + + public const byte GcPaddingU8 = 0xFF; + public const int GcCertificateSize = 0x400; + public const int GcSocModulusOffset = 0x130; + public const int GcCertificateSetSize = GcCertificateSize + GcAsicSerialNumberLength + GcRsaKeyLength + GcRsaPublicExponentLength; public const long UnusedAreaSizeBase = 1024 * 1024 * 72; // 72 MiB public const long MemorySizeBase = 1024 * 1024 * 1024; // 1 GiB public const long AvailableSizeBase = MemorySizeBase - UnusedAreaSizeBase; + + public const int Cmd60DefaultTimeOutMilliSeconds = 3500; + public const int EraseTimeOutMilliSeconds = 10 * 1000; } \ No newline at end of file diff --git a/src/LibHac/Gc/IGcApi.cs b/src/LibHac/Gc/IGcApi.cs index 4cc92862..ab776aed 100644 --- a/src/LibHac/Gc/IGcApi.cs +++ b/src/LibHac/Gc/IGcApi.cs @@ -35,4 +35,5 @@ public interface IGcApi void UnregisterDetectionEventCallback(); Result GetCardHeader(Span destBuffer); Result GetErrorInfo(out GameCardErrorReportInfo outErrorReportInfo); + Result GetAsicCertificate(out GameCardAsicCertificateSet outCertificateSet); } \ No newline at end of file diff --git a/src/LibHac/GcSrv/GameCardManager.cs b/src/LibHac/GcSrv/GameCardManager.cs index 8e346ee1..d2c6c3b8 100644 --- a/src/LibHac/GcSrv/GameCardManager.cs +++ b/src/LibHac/GcSrv/GameCardManager.cs @@ -768,6 +768,17 @@ public class GameCardManager : IStorageDeviceManager, IStorageDeviceOperator, IG bytesWritten = Unsafe.SizeOf(); return Result.Success; } + case GameCardManagerOperationIdValue.GetGameCardAsicCertificate: + { + if (buffer.Size < Unsafe.SizeOf()) + return ResultFs.InvalidArgument.Log(); + + res = GetGameCardAsicCertificate(out buffer.As()); + if (res.IsFailure()) return res.Miss(); + + bytesWritten = Unsafe.SizeOf(); + return Result.Success; + } default: return ResultFs.InvalidArgument.Log(); @@ -1042,6 +1053,16 @@ public class GameCardManager : IStorageDeviceManager, IStorageDeviceOperator, IG return Result.Success; } + private Result GetGameCardAsicCertificate(out GameCardAsicCertificateSet outCertificateSet) + { + UnsafeHelpers.SkipParamInit(out outCertificateSet); + + Result res = InitializeGcLibrary(); + if (res.IsFailure()) return res.Miss(); + + return _gc.GetAsicCertificate(out outCertificateSet).Ret(); + } + public Result AcquireReadLock(ref SharedLock outLock, GameCardHandle handle) { using (var readLock = new SharedLock(_rwLock)) diff --git a/src/LibHac/GcSrv/GcSrvEnums.cs b/src/LibHac/GcSrv/GcSrvEnums.cs index e0d83f1e..09560255 100644 --- a/src/LibHac/GcSrv/GcSrvEnums.cs +++ b/src/LibHac/GcSrv/GcSrvEnums.cs @@ -19,7 +19,8 @@ public enum GameCardManagerOperationIdValue ReadParamDirectly = 11, WriteToGameCardDirectly = 12, ForceErase = 13, - SimulateDetectionEventSignaled = 14 + SimulateDetectionEventSignaled = 14, + GetGameCardAsicCertificate = 15 } /// diff --git a/tests/LibHac.Tests/Gc/TypeLayoutTests.cs b/tests/LibHac.Tests/Gc/TypeLayoutTests.cs index 85a59a7a..58820055 100644 --- a/tests/LibHac.Tests/Gc/TypeLayoutTests.cs +++ b/tests/LibHac.Tests/Gc/TypeLayoutTests.cs @@ -87,6 +87,24 @@ public class TypeLayoutTests Assert.Equal(0, GetOffset(in s, in s.Reserved)); } + [Fact] + public static void GameCardAsicCertificateSet_Layout() + { + var s = new GameCardAsicCertificateSet(); + + Assert.Equal(Values.GcCertificateSetSize, Unsafe.SizeOf()); + + Assert.Equal(0x000, GetOffset(in s, in s.Certificate)); + Assert.Equal(0x400, GetOffset(in s, in s.SerialNumber)); + Assert.Equal(0x410, GetOffset(in s, in s.PublicKeyModulus)); + Assert.Equal(0x510, GetOffset(in s, in s.PublicKeyExponent)); + + Assert.Equal(Values.GcCertificateSize, s.Certificate.Length); + Assert.Equal(Values.GcAsicSerialNumberLength, s.SerialNumber.Length); + Assert.Equal(Values.GcRsaKeyLength, s.PublicKeyModulus.Length); + Assert.Equal(Values.GcRsaPublicExponentLength, s.PublicKeyExponent.Length); + } + [Fact] public static void CardInitialDataPayload_Layout() {