diff --git a/src/LibHac/Crypto/Aes.cs b/src/LibHac/Crypto/Aes.cs
index e9db32e7..17652cf4 100644
--- a/src/LibHac/Crypto/Aes.cs
+++ b/src/LibHac/Crypto/Aes.cs
@@ -1,6 +1,6 @@
// ReSharper disable AssignmentIsFullyDiscarded
using System;
-
+using LibHac.Diag;
#if HAS_INTRINSICS
using LibHac.Crypto.Detail;
@@ -266,83 +266,72 @@ namespace LibHac.Crypto
cipher.Transform(input, output);
}
- /**
- * A byte span containing the 128-bit key used in the AES-CBC-128 steps
- * A byte span containing the message to be authenticated
- * The offset within the byte span at which the message will be read from
- * A byte span to output the message authentication code into
- * The offset within the byte span at which the authentication code will be written to
- * The length of the message
- * https://tools.ietf.org/html/rfc4493
- */
- public static void CalculateCmac(Span k, Span m, int mIndex, Span t, int tIndex, int len)
+ ///
+ /// Computes the CMAC of the provided data using AES-128.
+ ///
+ /// The buffer where the generated MAC will be placed. Must be at least 16 bytes long.
+ /// The message on which the MAC will be calculated.
+ /// The 128-bit AES key used to calculate the MAC.
+ /// https://tools.ietf.org/html/rfc4493
+ public static void CalculateCmac(Span mac, ReadOnlySpan data, ReadOnlySpan key)
{
ReadOnlySpan zero = stackalloc byte[16];
+ int len = data.Length;
// Step 1, AES-128 with key K is applied to an all-zero input block.
Span l = stackalloc byte[16];
-
- EncryptCbc128(zero, l, k, zero);
+ EncryptCbc128(zero, l, key, zero);
// Step 2, K1 is derived through the following operation:
- Span k1 = LeftShiftBytes(l);
+ Span k1 = stackalloc byte[16];
+ LeftShiftBytes(l, k1);
if ((l[0] & 0x80) == 0x80) // If the most significant bit of L is equal to 0, K1 is the left-shift of L by 1 bit.
k1[15] ^= 0x87; // Otherwise, K1 is the XOR of const_Rb and the left-shift of L by 1 bit.
// Step 3, K2 is derived through the following operation:
- Span k2 = LeftShiftBytes(k1);
+ Span k2 = stackalloc byte[16];
+ LeftShiftBytes(k1, k2);
if ((k1[0] & 0x80) == 0x80) // If the most significant bit of K1 is equal to 0, K2 is the left-shift of K1 by 1 bit.
- k2[15] ^= 0x87; // Otherwise, K2 is the XOR of const_Rb and the left-shift of K1 by 1 bit.
+ k2[15] ^= 0x87; // Otherwise, K2 is the XOR of const_Rb and the left-shift of K1 by 1 bit.
+
+ // ReSharper disable once RedundantAssignment
+ Span paddedMessage = l;
if (len != 0 && len % 16 == 0) // If the size of the input message block is equal to a positive multiple of the block size (namely, 128 bits),
{ // the last block shall be XOR'ed with K1 before processing
- Span message = stackalloc byte[len];
- m.Slice(mIndex, len).CopyTo(message);
+ paddedMessage = len < 0x800 ? stackalloc byte[len] : new byte[len];
+ data.CopyTo(paddedMessage);
for (int j = 0; j < k1.Length; j++)
- message[message.Length - 16 + j] ^= k1[j];
-
- Span encResult = stackalloc byte[message.Length];
- EncryptCbc128(message, encResult, k, zero); // The result of the previous process will be the input of the last encryption.
- encResult.Slice(message.Length - 0x10).CopyTo(t.Slice(tIndex));
+ paddedMessage[paddedMessage.Length - 16 + j] ^= k1[j];
}
else // Otherwise, the last block shall be padded with 10^i and XOR'ed with K2.
{
- Span message = stackalloc byte[len + (16 - len % 16)];
- message[len] = 0x80;
- m.Slice(mIndex, len).CopyTo(message);
+ int paddedLength = len + (16 - len % 16);
+ paddedMessage = paddedLength < 0x800 ? stackalloc byte[paddedLength] : new byte[paddedLength];
+ paddedMessage[len] = 0x80;
+ data.CopyTo(paddedMessage);
for (int j = 0; j < k2.Length; j++)
- message[message.Length - 16 + j] ^= k2[j];
-
- Span encResult = stackalloc byte[message.Length];
- EncryptCbc128(message, encResult, k, zero); // The result of the previous process will be the input of the last encryption.
- encResult.Slice(message.Length - 0x10).CopyTo(t.Slice(tIndex));
+ paddedMessage[paddedMessage.Length - 16 + j] ^= k2[j];
}
+
+ EncryptCbc128(paddedMessage, paddedMessage, key, zero); // The result of the previous process will be the input of the last encryption.
+ paddedMessage.Slice(paddedMessage.Length - 0x10).CopyTo(mac);
}
- /**
- * A byte span containing the 128-bit key used in the AES-CBC-128 steps
- * A byte span containing the message to be authenticated
- * A byte span to output the message authentication code into
- * https://tools.ietf.org/html/rfc4493
- */
- public static void CalculateCmac(Span k, Span m, Span t) =>
- CalculateCmac(k, m, 0, t, 0, m.Length);
-
- private static byte[] LeftShiftBytes(Span bytes)
+ private static void LeftShiftBytes(ReadOnlySpan input, Span output)
{
- var shifted = new byte[bytes.Length];
+ Assert.AssertTrue(output.Length >= input.Length);
+
byte carry = 0;
- for (var i = bytes.Length - 1; i >= 0; i--)
+ for (int i = input.Length - 1; i >= 0; i--)
{
- var b = (ushort)(bytes[i] << 1);
- shifted[i] = (byte)((b & 0xff) + carry);
+ ushort b = (ushort)(input[i] << 1);
+ output[i] = (byte)((b & 0xff) + carry);
carry = (byte)((b & 0xff00) >> 8);
}
-
- return shifted;
}
}
}
diff --git a/src/LibHac/Crypto/Rsa.cs b/src/LibHac/Crypto/Rsa.cs
index 70aaec54..2ecbf826 100644
--- a/src/LibHac/Crypto/Rsa.cs
+++ b/src/LibHac/Crypto/Rsa.cs
@@ -50,11 +50,9 @@ namespace LibHac.Crypto
}
}
- /**
- * The RSA Modulus (n)
- * The RSA Public Exponent (e)
- * The RSA Private Exponent (d)
- */
+ /// The RSA Modulus (n)
+ /// The RSA Public Exponent (e)
+ /// The RSA Private Exponent (d)
public static RSAParameters RecoverParameters(BigInteger n, BigInteger e, BigInteger d)
{
(BigInteger p, BigInteger q) = DeriveRsaPrimeNumberPair(n, e, d);
@@ -79,23 +77,19 @@ namespace LibHac.Crypto
};
}
- /**
- * The RSA Modulus (n)
- * The RSA Public Exponent (e)
- * The RSA Private Exponent (d)
- */
+ /// The RSA Modulus (n)
+ /// The RSA Public Exponent (e)
+ /// The RSA Private Exponent (d)
public static RSAParameters RecoverParameters(ReadOnlySpan n, ReadOnlySpan e, ReadOnlySpan d) =>
RecoverParameters(n.GetBigInteger(), e.GetBigInteger(), d.GetBigInteger());
- /**
- *
- * Derive RSA Prime Number Pair (p, q) from RSA Modulus (n), RSA Public Exponent (e) and RSA Private Exponent (d)
- *
- * The RSA Modulus (n)
- * The RSA Public Exponent (e)
- * The RSA Private Exponent (d)
- * RSA Prime Number Pair
- */
+ ///
+ /// Derive RSA Prime Number Pair (p, q) from RSA Modulus (n), RSA Public Exponent (e) and RSA Private Exponent (d)
+ ///
+ /// The RSA Modulus (n)
+ /// The RSA Public Exponent (e)
+ /// The RSA Private Exponent (d)
+ /// RSA Prime Number Pair
private static (BigInteger p, BigInteger q) DeriveRsaPrimeNumberPair(BigInteger n, BigInteger e, BigInteger d)
{
BigInteger k = d * e - BigInteger.One;
diff --git a/src/LibHac/FsSystem/Save/Header.cs b/src/LibHac/FsSystem/Save/Header.cs
index be040695..6c8b4250 100644
--- a/src/LibHac/FsSystem/Save/Header.cs
+++ b/src/LibHac/FsSystem/Save/Header.cs
@@ -91,11 +91,11 @@ namespace LibHac.FsSystem.Save
private Validity ValidateSignature(Keyset keyset)
{
- var calculatedCmac = new byte[0x10];
+ Span calculatedCmac = stackalloc byte[0x10];
- Aes.CalculateCmac(keyset.SaveMacKey, Data, 0x100, calculatedCmac, 0, 0x200);
+ Aes.CalculateCmac(calculatedCmac, Data.AsSpan(0x100, 0x200), keyset.SaveMacKey);
- return Utilities.ArraysEqual(calculatedCmac, Cmac) ? Validity.Valid : Validity.Invalid;
+ return CryptoUtil.IsSameBytes(calculatedCmac, Cmac, Aes.BlockSize) ? Validity.Valid : Validity.Invalid;
}
}
diff --git a/src/LibHac/FsSystem/Save/SaveDataFileSystem.cs b/src/LibHac/FsSystem/Save/SaveDataFileSystem.cs
index c8db4d54..f6d4a37a 100644
--- a/src/LibHac/FsSystem/Save/SaveDataFileSystem.cs
+++ b/src/LibHac/FsSystem/Save/SaveDataFileSystem.cs
@@ -269,7 +269,7 @@ namespace LibHac.FsSystem.Save
headerStream.Position = 0x100;
headerStream.Read(cmacData, 0, 0x200);
- Aes.CalculateCmac(keyset.SaveMacKey, cmacData, 0, cmac, 0, 0x200);
+ Aes.CalculateCmac(cmac, cmacData, keyset.SaveMacKey);
headerStream.Position = 0;
headerStream.Write(cmac, 0, 0x10);
diff --git a/src/LibHac/FsSystem/StorageExtensions.cs b/src/LibHac/FsSystem/StorageExtensions.cs
index 495d46ce..611cc7b8 100644
--- a/src/LibHac/FsSystem/StorageExtensions.cs
+++ b/src/LibHac/FsSystem/StorageExtensions.cs
@@ -83,10 +83,12 @@ namespace LibHac.FsSystem
long pos = 0;
using var buffer = new RentedArray(bufferSize);
+ int rentedBufferSize = buffer.Array.Length;
+
while (remaining > 0)
{
- int toCopy = (int)Math.Min(bufferSize, remaining);
- Span buf = buffer.Span.Slice(0, toCopy);
+ int toCopy = (int)Math.Min(rentedBufferSize, remaining);
+ Span buf = buffer.Array.AsSpan(0, toCopy);
input.Read(pos, buf);
output.Write(pos, buf);
@@ -132,12 +134,14 @@ namespace LibHac.FsSystem
long pos = offset;
using var buffer = new RentedArray(bufferSize);
- buffer.Span.Slice(0, (int)Math.Min(remaining, bufferSize)).Fill(value);
+ int rentedBufferSize = buffer.Array.Length;
+
+ buffer.Array.AsSpan(0, (int)Math.Min(remaining, rentedBufferSize)).Fill(value);
while (remaining > 0)
{
- int toFill = (int)Math.Min(bufferSize, remaining);
- Span buf = buffer.Span.Slice(0, toFill);
+ int toFill = (int)Math.Min(rentedBufferSize, remaining);
+ Span buf = buffer.Array.AsSpan(0, toFill);
input.Write(pos, buf);
@@ -188,14 +192,16 @@ namespace LibHac.FsSystem
{
long remaining = length;
long inOffset = 0;
+
using var buffer = new RentedArray(bufferSize);
+ int rentedBufferSize = buffer.Array.Length;
progress?.SetTotal(length);
while (remaining > 0)
{
- int toWrite = (int)Math.Min(bufferSize, remaining);
- input.Read(inOffset, buffer.Span.Slice(0, toWrite));
+ int toWrite = (int)Math.Min(rentedBufferSize, remaining);
+ input.Read(inOffset, buffer.Array.AsSpan(0, toWrite));
output.Write(buffer.Array, 0, toWrite);
remaining -= toWrite;
diff --git a/src/LibHac/Keyset.cs b/src/LibHac/Keyset.cs
index c4b981ac..20006594 100644
--- a/src/LibHac/Keyset.cs
+++ b/src/LibHac/Keyset.cs
@@ -350,7 +350,7 @@ namespace LibHac
}
Array.Copy(EncryptedKeyblobs[i], expectedCmac, 0x10);
- Aes.CalculateCmac(KeyblobMacKeys[i], EncryptedKeyblobs[i], 0x10, cmac, 0, 0xa0);
+ Aes.CalculateCmac(cmac, EncryptedKeyblobs[i].AsSpan(0x10, 0xA0), KeyblobMacKeys[i]);
if (!Utilities.ArraysEqual(cmac, expectedCmac))
{
diff --git a/src/hactoolnet/EnumStrings.cs b/src/hactoolnet/EnumStrings.cs
index bb21681f..77c4d681 100644
--- a/src/hactoolnet/EnumStrings.cs
+++ b/src/hactoolnet/EnumStrings.cs
@@ -1,6 +1,8 @@
using LibHac;
+using LibHac.Fs;
using LibHac.FsSystem.NcaUtils;
using LibHac.Ncm;
+using ContentType = LibHac.Ncm.ContentType;
namespace hactoolnet
{
@@ -96,5 +98,20 @@ namespace hactoolnet
_ => value.ToString()
};
}
+
+ public static string Print(this SaveDataType value)
+ {
+ return value switch
+ {
+ SaveDataType.System => nameof(SaveDataType.System),
+ SaveDataType.Account => nameof(SaveDataType.Account),
+ SaveDataType.Bcat => nameof(SaveDataType.Bcat),
+ SaveDataType.Device => nameof(SaveDataType.Device),
+ SaveDataType.Temporary => nameof(SaveDataType.Temporary),
+ SaveDataType.Cache => nameof(SaveDataType.Cache),
+ SaveDataType.SystemBcat => nameof(SaveDataType.SystemBcat),
+ _ => value.ToString()
+ };
+ }
}
}
diff --git a/src/hactoolnet/ProcessSave.cs b/src/hactoolnet/ProcessSave.cs
index 8d6c928e..0ea5e29c 100644
--- a/src/hactoolnet/ProcessSave.cs
+++ b/src/hactoolnet/ProcessSave.cs
@@ -330,7 +330,7 @@ namespace hactoolnet
PrintItem(sb, colLen, "Title ID:", $"{save.Header.ExtraData.TitleId:x16}");
PrintItem(sb, colLen, "User ID:", save.Header.ExtraData.UserId);
PrintItem(sb, colLen, "Save ID:", $"{save.Header.ExtraData.SaveId:x16}");
- PrintItem(sb, colLen, "Save Type:", $"{save.Header.ExtraData.Type}");
+ PrintItem(sb, colLen, "Save Type:", $"{save.Header.ExtraData.Type.Print()}");
PrintItem(sb, colLen, "Owner ID:", $"{save.Header.ExtraData.SaveOwnerId:x16}");
PrintItem(sb, colLen, "Timestamp:", $"{DateTimeOffset.FromUnixTimeSeconds(save.Header.ExtraData.Timestamp):yyyy-MM-dd HH:mm:ss} UTC");
PrintItem(sb, colLen, "Save Data Size:", $"0x{save.Header.ExtraData.DataSize:x16} ({Utilities.GetBytesReadable(save.Header.ExtraData.DataSize)})");
diff --git a/tests/LibHac.Tests/AesCmac.cs b/tests/LibHac.Tests/AesCmac.cs
index d0587a20..52e2dd2d 100644
--- a/tests/LibHac.Tests/AesCmac.cs
+++ b/tests/LibHac.Tests/AesCmac.cs
@@ -1,4 +1,5 @@
-using LibHac.Crypto;
+using System;
+using LibHac.Crypto;
using Xunit;
namespace LibHac.Tests
@@ -58,7 +59,7 @@ namespace LibHac.Tests
{
var actual = new byte[0x10];
- Aes.CalculateCmac(data.Key, data.Message, data.Start, actual, 0, data.Length);
+ Aes.CalculateCmac(actual, data.Message.AsSpan(data.Start, data.Length), data.Key);
Assert.Equal(data.Expected, actual);
}