Skip to content

Commit 4e778bc

Browse files
committed
Sync back fixes from IO
1 parent ccd04cb commit 4e778bc

File tree

4 files changed

+113
-57
lines changed

4 files changed

+113
-57
lines changed

NDecrypt.Core/CommonOperations.cs

Lines changed: 88 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -126,65 +126,98 @@ public static void PerformAESOperation(uint size,
126126

127127
#endregion
128128

129+
// TODO: Remove when IO updated
129130
#region Byte Arrays
130131

131132
/// <summary>
132133
/// Add an integer value to a number represented by a byte array
133134
/// </summary>
134-
/// <param name="input">Byte array to add to</param>
135+
/// <param name="self">Byte array to add to</param>
135136
/// <param name="add">Amount to add</param>
136137
/// <returns>Byte array representing the new value</returns>
137-
public static byte[] Add(byte[] input, uint add)
138+
/// <remarks>Assumes array values are in big-endian format</remarks>
139+
public static byte[] Add(this byte[] self, uint add)
138140
{
141+
// If nothing is being added, just return
142+
if (add == 0)
143+
return self;
144+
145+
// Get the big-endian representation of the value
139146
byte[] addBytes = BitConverter.GetBytes(add);
140147
Array.Reverse(addBytes);
148+
149+
// Pad the array out to 16 bytes
141150
byte[] paddedBytes = new byte[16];
142151
Array.Copy(addBytes, 0, paddedBytes, 12, 4);
143-
return Add(input, paddedBytes);
152+
153+
// If the input is empty, just return the added value
154+
if (self.Length == 0)
155+
return paddedBytes;
156+
157+
return self.Add(paddedBytes);
144158
}
145159

146160
/// <summary>
147161
/// Add two numbers represented by byte arrays
148162
/// </summary>
149-
/// <param name="left">Byte array to add to</param>
150-
/// <param name="right">Amount to add</param>
163+
/// <param name="self">Byte array to add to</param>
164+
/// <param name="add">Amount to add</param>
151165
/// <returns>Byte array representing the new value</returns>
152-
public static byte[] Add(byte[] left, byte[] right)
166+
/// <remarks>Assumes array values are in big-endian format</remarks>
167+
public static byte[] Add(this byte[] self, byte[] add)
153168
{
154-
int addBytes = Math.Min(left.Length, right.Length);
155-
int outLength = Math.Max(left.Length, right.Length);
156-
169+
// If either input is empty
170+
if (self.Length == 0 && add.Length == 0)
171+
return [];
172+
else if (self.Length > 0 && add.Length == 0)
173+
return self;
174+
else if (self.Length == 0 && add.Length > 0)
175+
return add;
176+
177+
// Setup the output array
178+
int outLength = Math.Max(self.Length, add.Length);
157179
byte[] output = new byte[outLength];
158180

181+
// Loop adding with carry
159182
uint carry = 0;
160-
for (int i = addBytes - 1; i >= 0; i--)
183+
for (int i = 0; i < outLength; i++)
161184
{
162-
uint addValue = (uint)(left[i] + right[i]) + carry;
163-
output[i] = (byte)addValue;
164-
carry = addValue >> 8;
165-
}
185+
int selfIndex = self.Length - i - 1;
186+
uint selfValue = selfIndex >= 0 ? self[selfIndex] : 0u;
166187

167-
if (outLength != addBytes && left.Length == outLength)
168-
Array.Copy(left, addBytes, output, addBytes, outLength - addBytes);
169-
else if (outLength != addBytes && right.Length == outLength)
170-
Array.Copy(right, addBytes, output, addBytes, outLength - addBytes);
188+
int addIndex = add.Length - i - 1;
189+
uint addValue = addIndex >= 0 ? add[addIndex] : 0u;
190+
191+
uint next = selfValue + addValue + carry;
192+
carry = next >> 8;
193+
194+
int outputIndex = output.Length - i - 1;
195+
output[outputIndex] = (byte)(next & 0xFF);
196+
}
171197

172198
return output;
173199
}
174200

175201
/// <summary>
176202
/// Perform a rotate left on a byte array
177203
/// </summary>
178-
/// <param name="val">Byte array value to rotate</param>
179-
/// <param name="r_bits">Number of bits to rotate</param>
204+
/// <param name="self">Byte array value to rotate</param>
205+
/// <param name="numBits">Number of bits to rotate</param>
180206
/// <returns>Rotated byte array value</returns>
181-
public static byte[] RotateLeft(byte[] val, int r_bits)
207+
/// <remarks>Assumes array values are in big-endian format</remarks>
208+
public static byte[] RotateLeft(this byte[] self, int numBits)
182209
{
183-
byte[] output = new byte[val.Length];
184-
Array.Copy(val, output, output.Length);
210+
// If either input is empty
211+
if (self.Length == 0)
212+
return [];
213+
else if (numBits == 0)
214+
return self;
215+
216+
byte[] output = new byte[self.Length];
217+
Array.Copy(self, output, output.Length);
185218

186219
// Shift by bytes
187-
while (r_bits >= 8)
220+
while (numBits >= 8)
188221
{
189222
byte temp = output[0];
190223
for (int i = 0; i < output.Length - 1; i++)
@@ -193,13 +226,13 @@ public static byte[] RotateLeft(byte[] val, int r_bits)
193226
}
194227

195228
output[output.Length - 1] = temp;
196-
r_bits -= 8;
229+
numBits -= 8;
197230
}
198231

199232
// Shift by bits
200-
if (r_bits > 0)
233+
if (numBits > 0)
201234
{
202-
byte bitMask = (byte)(8 - r_bits), carry, wrap = 0;
235+
byte bitMask = (byte)(8 - numBits), carry, wrap = 0;
203236
for (int i = 0; i < output.Length; i++)
204237
{
205238
carry = (byte)((255 << bitMask & output[i]) >> bitMask);
@@ -213,7 +246,7 @@ public static byte[] RotateLeft(byte[] val, int r_bits)
213246
output[i - 1] |= carry;
214247

215248
// Shift the current bits
216-
output[i] <<= r_bits;
249+
output[i] <<= numBits;
217250
}
218251

219252
// Make sure the wrap happens
@@ -226,24 +259,38 @@ public static byte[] RotateLeft(byte[] val, int r_bits)
226259
/// <summary>
227260
/// XOR two numbers represented by byte arrays
228261
/// </summary>
229-
/// <param name="left">Byte array to XOR to</param>
230-
/// <param name="right">Amount to XOR</param>
262+
/// <param name="self">Byte array to XOR to</param>
263+
/// <param name="xor">Amount to XOR</param>
231264
/// <returns>Byte array representing the new value</returns>
232-
public static byte[] Xor(byte[] left, byte[] right)
265+
/// <remarks>Assumes array values are in big-endian format</remarks>
266+
public static byte[] Xor(this byte[] self, byte[] xor)
233267
{
234-
int xorBytes = Math.Min(left.Length, right.Length);
235-
int outLength = Math.Max(left.Length, right.Length);
236-
268+
// If either input is empty
269+
if (self.Length == 0 && xor.Length == 0)
270+
return [];
271+
else if (self.Length > 0 && xor.Length == 0)
272+
return self;
273+
else if (self.Length == 0 && xor.Length > 0)
274+
return xor;
275+
276+
// Setup the output array
277+
int outLength = Math.Max(self.Length, xor.Length);
237278
byte[] output = new byte[outLength];
238-
for (int i = 0; i < xorBytes; i++)
279+
280+
// Loop XOR
281+
for (int i = 0; i < outLength; i++)
239282
{
240-
output[i] = (byte)(left[i] ^ right[i]);
241-
}
283+
int selfIndex = self.Length - i - 1;
284+
uint selfValue = selfIndex >= 0 ? self[selfIndex] : 0u;
285+
286+
int xorIndex = xor.Length - i - 1;
287+
uint xorValue = xorIndex >= 0 ? xor[xorIndex] : 0u;
242288

243-
if (outLength != xorBytes && left.Length == outLength)
244-
Array.Copy(left, xorBytes, output, xorBytes, outLength - xorBytes);
245-
else if (outLength != xorBytes && right.Length == outLength)
246-
Array.Copy(right, xorBytes, output, xorBytes, outLength - xorBytes);
289+
uint next = selfValue ^ xorValue;
290+
291+
int outputIndex = output.Length - i - 1;
292+
output[outputIndex] = (byte)(next & 0xFF);
293+
}
247294

248295
return output;
249296
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#if NET20
2+
3+
namespace System.Runtime.CompilerServices
4+
{
5+
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Method)]
6+
internal sealed class ExtensionAttribute : Attribute {}
7+
}
8+
9+
#endif

NDecrypt.Core/PartitionKeys.cs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,10 @@ public PartitionKeys(DecryptArgs args, byte[]? signature, BitMasks masks, Crypto
6161
// Set the standard normal key values
6262
NormalKey = new byte[16];
6363

64-
NormalKey2C = RotateLeft(KeyX2C, 2);
65-
NormalKey2C = Xor(NormalKey2C, KeyY);
66-
NormalKey2C = Add(NormalKey2C, args.AESHardwareConstant);
67-
NormalKey2C = RotateLeft(NormalKey2C, 87);
64+
NormalKey2C = KeyX2C.RotateLeft(2);
65+
NormalKey2C = NormalKey2C.Xor(KeyY);
66+
NormalKey2C = NormalKey2C.Add(add: args.AESHardwareConstant);
67+
NormalKey2C = NormalKey2C.RotateLeft(87);
6868

6969
// Special case for zero-key
7070
#if NET20 || NET35
@@ -104,10 +104,10 @@ public PartitionKeys(DecryptArgs args, byte[]? signature, BitMasks masks, Crypto
104104
}
105105

106106
// Set the normal key based on the new KeyX value
107-
NormalKey = RotateLeft(KeyX, 2);
108-
NormalKey = Xor(NormalKey, KeyY);
109-
NormalKey = Add(NormalKey, args.AESHardwareConstant);
110-
NormalKey = RotateLeft(NormalKey, 87);
107+
NormalKey = KeyX.RotateLeft(2);
108+
NormalKey = NormalKey.Xor(KeyY);
109+
NormalKey = NormalKey.Add(args.AESHardwareConstant);
110+
NormalKey = NormalKey.RotateLeft(87);
111111
}
112112

113113
/// <summary>
@@ -129,10 +129,10 @@ public void SetRomFSValues(BitMasks masks)
129129
// Encrypting RomFS for partitions 1 and up always use Key0x2C
130130
KeyX = _development ? _decryptArgs.DevKeyX0x2C : _decryptArgs.KeyX0x2C;
131131

132-
NormalKey = RotateLeft(KeyX, 2);
133-
NormalKey = Xor(NormalKey, KeyY);
134-
NormalKey = Add(NormalKey, _decryptArgs.AESHardwareConstant);
135-
NormalKey = RotateLeft(NormalKey, 87);
132+
NormalKey = KeyX.RotateLeft(2);
133+
NormalKey = NormalKey.Xor(KeyY);
134+
NormalKey = NormalKey.Add(_decryptArgs.AESHardwareConstant);
135+
NormalKey = NormalKey.RotateLeft(87);
136136
}
137137
}
138138
}

NDecrypt.Core/ThreeDSTool.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ private bool DecryptExeFS(N3DS cart, int index, Stream reader, Stream writer)
264264

265265
// Create the ExeFS AES cipher for this partition
266266
uint ctroffsetE = cart.MediaUnitSize / 0x10;
267-
byte[] exefsIVWithOffset = Add(cart.ExeFSIV(index), ctroffsetE);
267+
byte[] exefsIVWithOffset = cart.ExeFSIV(index).Add(ctroffsetE);
268268
var cipher = CreateAESDecryptionCipher(_keysMap[index].NormalKey2C, exefsIVWithOffset);
269269

270270
// Setup and perform the decryption
@@ -361,7 +361,7 @@ private void DecryptExeFSFileEntries(N3DS cart, int index, Stream reader, Stream
361361

362362
// Create the ExeFS AES ciphers for this partition
363363
uint ctroffset = (fileHeader.FileOffset + cart.MediaUnitSize) / 0x10;
364-
byte[] exefsIVWithOffsetForHeader = Add(cart.ExeFSIV(index), ctroffset);
364+
byte[] exefsIVWithOffsetForHeader = cart.ExeFSIV(index).Add(ctroffset);
365365
var firstCipher = CreateAESDecryptionCipher(_keysMap[index].NormalKey, exefsIVWithOffsetForHeader);
366366
var secondCipher = CreateAESEncryptionCipher(_keysMap[index].NormalKey2C, exefsIVWithOffsetForHeader);
367367

@@ -690,7 +690,7 @@ private bool EncryptExeFS(N3DS cart, int index, Stream reader, Stream writer)
690690

691691
// Create the ExeFS AES cipher for this partition
692692
uint ctroffsetE = cart.MediaUnitSize / 0x10;
693-
byte[] exefsIVWithOffset = Add(cart.ExeFSIV(index), ctroffsetE);
693+
byte[] exefsIVWithOffset = cart.ExeFSIV(index).Add(ctroffsetE);
694694
var cipher = CreateAESEncryptionCipher(_keysMap[index].NormalKey2C, exefsIVWithOffset);
695695

696696
// Setup and perform the encryption
@@ -784,7 +784,7 @@ private void EncryptExeFSFileEntries(N3DS cart, int index, Stream reader, Stream
784784

785785
// Create the ExeFS AES ciphers for this partition
786786
uint ctroffset = (fileHeader.FileOffset + cart.MediaUnitSize) / 0x10;
787-
byte[] exefsIVWithOffsetForHeader = Add(cart.ExeFSIV(index), ctroffset);
787+
byte[] exefsIVWithOffsetForHeader = cart.ExeFSIV(index).Add(ctroffset);
788788
var firstCipher = CreateAESEncryptionCipher(_keysMap[index].NormalKey, exefsIVWithOffsetForHeader);
789789
var secondCipher = CreateAESDecryptionCipher(_keysMap[index].NormalKey2C, exefsIVWithOffsetForHeader);
790790

0 commit comments

Comments
 (0)