NdsForge.NET 1.0.1
Read, validate, edit, compare, and build Nintendo DS and DSi images from .NET
Loading...
Searching...
No Matches
NdsDsiKeyScrambler.cs
1using System.Text;
2
3namespace NdsForge;
4
9public static class NdsDsiKeyScrambler
10{
12 private static readonly byte[] ScramblerConstant =
13 Convert.FromHexString("FFFEFB4E295902582A680F5F1A4F3E79");
14
22 public static byte[] DeriveNormalKey(ReadOnlySpan<byte> keyX, ReadOnlySpan<byte> keyY)
23 {
24 ValidateKey(keyX, nameof(keyX));
25 ValidateKey(keyY, nameof(keyY));
26 Span<byte> sum = stackalloc byte[16];
27 int carry = 0;
28 for (int index = 0; index < sum.Length; index++)
29 {
30 int value = (keyX[index] ^ keyY[index]) + ScramblerConstant[^(index + 1)] + carry;
31 sum[index] = (byte)value;
32 carry = value >> 8;
33 }
34
35 return RotateLeft(sum, 42);
36 }
37
44 public static byte[] CreateModcryptKeyX(string gameCode)
45 {
46 ArgumentNullException.ThrowIfNull(gameCode);
47 if (gameCode.Length != 4 || gameCode.Any(static value => value is < ' ' or > '~'))
48 {
49 throw new ArgumentException("A modcrypt game code must contain exactly four printable ASCII characters.", nameof(gameCode));
50 }
51
52 var keyX = new byte[16];
53 "Nintendo"u8.CopyTo(keyX);
54 Encoding.ASCII.GetBytes(gameCode, keyX.AsSpan(8, 4));
55 for (int index = 0; index < 4; index++)
56 {
57 keyX[12 + index] = keyX[11 - index];
58 }
59
60 return keyX;
61 }
62
69 public static byte[] DeriveModcryptNormalKey(NdsHeader header)
70 {
71 ArgumentNullException.ThrowIfNull(header);
72 NdsDsiHeader dsi = header.Dsi ??
73 throw new ArgumentException("Secure modcrypt key derivation requires a DSi-family header.", nameof(header));
74 return DeriveNormalKey(CreateModcryptKeyX(header.GameCode), dsi.Arm9iHmac.Span[..16]);
75 }
76
81 private static byte[] RotateLeft(ReadOnlySpan<byte> value, int bits)
82 {
83 int byteShift = bits / 8;
84 int bitShift = bits % 8;
85 var output = new byte[16];
86 for (int index = 0; index < output.Length; index++)
87 {
88 int lowIndex = (index - byteShift) & 15;
89 int highIndex = (lowIndex - 1) & 15;
90 output[index] = (byte)((value[lowIndex] << bitShift) | (value[highIndex] >> (8 - bitShift)));
91 }
92
93 return output;
94 }
95
99 private static void ValidateKey(ReadOnlySpan<byte> value, string parameterName)
100 {
101 if (value.Length != 16)
102 {
103 throw new ArgumentException("A DSi scrambler key component must contain exactly sixteen bytes.", parameterName);
104 }
105 }
106}
Projects DSi security, digest, memory, title, and save metadata while preserving the complete extensi...
ReadOnlyMemory< byte > Arm9iHmac
Contains the 20-byte SHA-1 HMAC authenticating the DSi-mode ARM9i payload.
Implements the DSi 128-bit KeyX/KeyY scrambler in cartridge-register byte order. It contains only the...
static byte[] CreateModcryptKeyX(string gameCode)
Constructs the modcrypt slot's public KeyX from its fixed eight-byte platform prefix followed by the ...
static byte[] DeriveNormalKey(ReadOnlySpan< byte > keyX, ReadOnlySpan< byte > keyY)
Derives the normal AES key produced by DSi hardware from one KeyX/KeyY pair. Input and output arrays ...
static byte[] DeriveModcryptNormalKey(NdsHeader header)
Derives the secure modcrypt normal key from the common game code and the first sixteen ARM9i HMAC byt...
Projects the common cartridge header into typed fields while retaining every byte needed for lossless...
Definition NdsHeader.cs:5
string GameCode
Decodes the four-byte product code used for title identity, region suffixes, and encryption derivatio...
Definition NdsHeader.cs:56