diff --git a/src/LibHac/Crypto/Aes.cs b/src/LibHac/Crypto/Aes.cs
index f24cb4ef..e9db32e7 100644
--- a/src/LibHac/Crypto/Aes.cs
+++ b/src/LibHac/Crypto/Aes.cs
@@ -265,5 +265,84 @@ 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)
+ {
+ ReadOnlySpan zero = stackalloc byte[16];
+
+ // 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);
+
+ // Step 2, K1 is derived through the following operation:
+ Span k1 = LeftShiftBytes(l);
+ 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);
+ 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.
+
+ 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);
+
+ 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));
+ }
+ 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);
+
+ 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));
+ }
+ }
+
+ /**
+ * 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)
+ {
+ var shifted = new byte[bytes.Length];
+ byte carry = 0;
+
+ for (var i = bytes.Length - 1; i >= 0; i--)
+ {
+ var b = (ushort)(bytes[i] << 1);
+ shifted[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 7370998f..70aaec54 100644
--- a/src/LibHac/Crypto/Rsa.cs
+++ b/src/LibHac/Crypto/Rsa.cs
@@ -1,4 +1,5 @@
using System;
+using System.Numerics;
using System.Security.Cryptography;
namespace LibHac.Crypto
@@ -6,7 +7,15 @@ namespace LibHac.Crypto
public static class Rsa
{
public static bool VerifyRsa2048PssSha256(ReadOnlySpan signature, ReadOnlySpan modulus,
- ReadOnlySpan exponent, ReadOnlySpan message)
+ ReadOnlySpan exponent, ReadOnlySpan message) =>
+ VerifyRsa2048Sha256(signature, modulus, exponent, message, RSASignaturePadding.Pss);
+
+ public static bool VerifyRsa2048Pkcs1Sha256(ReadOnlySpan signature, ReadOnlySpan modulus,
+ ReadOnlySpan exponent, ReadOnlySpan message) =>
+ VerifyRsa2048Sha256(signature, modulus, exponent, message, RSASignaturePadding.Pkcs1);
+
+ private static bool VerifyRsa2048Sha256(ReadOnlySpan signature, ReadOnlySpan modulus,
+ ReadOnlySpan exponent, ReadOnlySpan message, RSASignaturePadding padding)
{
try
{
@@ -14,7 +23,7 @@ namespace LibHac.Crypto
using (var rsa = RSA.Create(param))
{
- return rsa.VerifyData(message, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pss);
+ return rsa.VerifyData(message, signature, HashAlgorithmName.SHA256, padding);
}
}
catch (CryptographicException)
@@ -40,5 +49,135 @@ namespace LibHac.Crypto
return false;
}
}
+
+ /**
+ * 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);
+
+ BigInteger dp = d % (p - BigInteger.One);
+ BigInteger dq = d % (q - BigInteger.One);
+ BigInteger inverseQ = Utilities.ModInverse(q, p);
+
+ int modLen = n.ToByteArray().Length;
+ int halfModLen = (modLen + 1) / 2;
+
+ return new RSAParameters
+ {
+ Modulus = n.GetBytes(modLen),
+ Exponent = e.GetBytes(-1),
+ D = d.GetBytes(modLen),
+ P = p.GetBytes(halfModLen),
+ Q = q.GetBytes(halfModLen),
+ DP = dp.GetBytes(halfModLen),
+ DQ = dq.GetBytes(halfModLen),
+ InverseQ = inverseQ.GetBytes(halfModLen)
+ };
+ }
+
+ /**
+ * 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
+ */
+ private static (BigInteger p, BigInteger q) DeriveRsaPrimeNumberPair(BigInteger n, BigInteger e, BigInteger d)
+ {
+ BigInteger k = d * e - BigInteger.One;
+
+ if (!k.IsEven)
+ {
+ throw new InvalidOperationException("d*e - 1 is odd");
+ }
+
+ BigInteger two = BigInteger.One + BigInteger.One;
+ BigInteger t = BigInteger.One;
+
+ BigInteger r = k / two;
+
+ while (r.IsEven)
+ {
+ t++;
+ r /= two;
+ }
+
+ byte[] rndBuf = n.ToByteArray();
+
+ if (rndBuf[^1] == 0)
+ {
+ rndBuf = new byte[rndBuf.Length - 1];
+ }
+
+ BigInteger nMinusOne = n - BigInteger.One;
+
+ bool cracked = false;
+ BigInteger y = BigInteger.Zero;
+
+ using (var rng = RandomNumberGenerator.Create())
+ {
+ for (int i = 0; i < 100 && !cracked; i++)
+ {
+ BigInteger g;
+
+ do
+ {
+ rng.GetBytes(rndBuf);
+ g = Utilities.GetBigInteger(rndBuf);
+ }
+ while (g >= n);
+
+ y = BigInteger.ModPow(g, r, n);
+
+ if (y.IsOne || y == nMinusOne)
+ {
+ i--;
+ continue;
+ }
+
+ for (BigInteger j = BigInteger.One; j < t; j++)
+ {
+ BigInteger x = BigInteger.ModPow(y, two, n);
+
+ if (x.IsOne)
+ {
+ cracked = true;
+ break;
+ }
+
+ if (x == nMinusOne)
+ {
+ break;
+ }
+
+ y = x;
+ }
+ }
+ }
+
+ if (!cracked)
+ {
+ throw new InvalidOperationException("Prime factors not found");
+ }
+
+ BigInteger p = BigInteger.GreatestCommonDivisor(y - BigInteger.One, n);
+ BigInteger q = n / p;
+
+ return (p, q);
+ }
}
}
diff --git a/src/LibHac/CryptoOld.cs b/src/LibHac/CryptoOld.cs
index 956441c4..82ca8f90 100644
--- a/src/LibHac/CryptoOld.cs
+++ b/src/LibHac/CryptoOld.cs
@@ -1,103 +1,33 @@
using System;
using System.IO;
-using System.Numerics;
using System.Security.Cryptography;
+using LibHac.Crypto;
using LibHac.FsSystem;
+using Aes = LibHac.Crypto.Aes;
+
namespace LibHac
{
public static class CryptoOld
{
- internal const int Aes128Size = 0x10;
-
- public static void DecryptEcb(byte[] key, byte[] src, int srcIndex, byte[] dest, int destIndex, int length)
- {
- using (var aes = Aes.Create())
- {
- if (aes == null) throw new CryptographicException("Unable to create AES object");
- aes.Key = key;
- aes.Mode = CipherMode.ECB;
- aes.Padding = PaddingMode.None;
- Array.Copy(aes.CreateDecryptor().TransformFinalBlock(src, srcIndex, length), 0, dest, destIndex, length);
- }
- }
-
- public static void DecryptEcb(byte[] key, byte[] src, byte[] dest, int length) =>
- DecryptEcb(key, src, 0, dest, 0, length);
-
- public static void EncryptEcb(byte[] key, byte[] src, int srcIndex, byte[] dest, int destIndex, int length)
- {
- using (var aes = Aes.Create())
- {
- if (aes == null) throw new CryptographicException("Unable to create AES object");
- aes.Key = key;
- aes.Mode = CipherMode.ECB;
- aes.Padding = PaddingMode.None;
- Array.Copy(aes.CreateEncryptor().TransformFinalBlock(src, srcIndex, length), 0, dest, destIndex, length);
- }
- }
-
- public static void EncryptEcb(byte[] key, byte[] src, byte[] dest, int length) =>
- EncryptEcb(key, src, 0, dest, 0, length);
-
- public static void DecryptCbc(byte[] key, byte[] iv, byte[] src, int srcIndex, byte[] dest, int destIndex, int length)
- {
- using (var aes = Aes.Create())
- {
- if (aes == null) throw new CryptographicException("Unable to create AES object");
- aes.Key = key;
- aes.IV = iv;
- aes.Mode = CipherMode.CBC;
- aes.Padding = PaddingMode.None;
- Array.Copy(aes.CreateDecryptor().TransformFinalBlock(src, srcIndex, length), 0, dest, destIndex, length);
- }
- }
-
- public static void DecryptCbc(byte[] key, byte[] iv, byte[] src, byte[] dest, int length) =>
- DecryptCbc(key, iv, src, 0, dest, 0, length);
-
- public static void EncryptCbc(byte[] key, byte[] iv, byte[] src, int srcIndex, byte[] dest, int destIndex, int length)
- {
- using (var aes = Aes.Create())
- {
- if (aes == null) throw new CryptographicException("Unable to create AES object");
- aes.Key = key;
- aes.IV = iv;
- aes.Mode = CipherMode.CBC;
- aes.Padding = PaddingMode.None;
- Array.Copy(aes.CreateEncryptor().TransformFinalBlock(src, srcIndex, length), 0, dest, destIndex, length);
- }
- }
-
- public static void EncryptCbc(byte[] key, byte[] iv, byte[] src, byte[] dest, int length) =>
- EncryptCbc(key, iv, src, 0, dest, 0, length);
-
public static void GenerateKek(byte[] key, byte[] src, byte[] dest, byte[] kekSeed, byte[] keySeed)
{
- var kek = new byte[Aes128Size];
- var srcKek = new byte[Aes128Size];
+ var kek = new byte[Aes.KeySize128];
+ var srcKek = new byte[Aes.KeySize128];
- DecryptEcb(key, kekSeed, kek, Aes128Size);
- DecryptEcb(kek, src, srcKek, Aes128Size);
+ Aes.DecryptEcb128(kekSeed, kek, key);
+ Aes.DecryptEcb128(src, srcKek, kek);
if (keySeed != null)
{
- DecryptEcb(srcKek, keySeed, dest, Aes128Size);
+ Aes.DecryptEcb128(keySeed, dest, srcKek);
}
else
{
- Array.Copy(srcKek, dest, Aes128Size);
+ Array.Copy(srcKek, dest, Aes.KeySize128);
}
}
- internal static BigInteger GetBigInteger(ReadOnlySpan bytes)
- {
- var signPadded = new byte[bytes.Length + 1];
- bytes.CopyTo(signPadded.AsSpan(1));
- Array.Reverse(signPadded);
- return new BigInteger(signPadded);
- }
-
public static RSAParameters DecryptRsaKey(byte[] encryptedKey, byte[] kek)
{
var counter = new byte[0x10];
@@ -114,11 +44,7 @@ namespace LibHac
Array.Copy(key, 0x100, n, 0, 0x100);
Array.Copy(key, 0x200, e, 0, 4);
- BigInteger dInt = GetBigInteger(d);
- BigInteger nInt = GetBigInteger(n);
- BigInteger eInt = GetBigInteger(e);
-
- RSAParameters rsaParams = RecoverRsaParameters(nInt, eInt, dInt);
+ RSAParameters rsaParams = Rsa.RecoverParameters(n, e, d);
TestRsaKey(rsaParams);
return rsaParams;
}
@@ -138,43 +64,15 @@ namespace LibHac
}
}
- public static Validity Rsa2048Pkcs1Verify(byte[] data, byte[] signature, byte[] modulus)
- {
- using (var rsa = RSA.Create())
- {
- try
- {
- rsa.ImportParameters(new RSAParameters { Exponent = new byte[] { 1, 0, 1 }, Modulus = modulus });
+ public static Validity Rsa2048Pkcs1Verify(byte[] data, byte[] signature, byte[] modulus) =>
+ Rsa.VerifyRsa2048Pkcs1Sha256(signature, modulus, new byte[] { 1, 0, 1 }, data)
+ ? Validity.Valid
+ : Validity.Invalid;
- return rsa.VerifyData(data, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1)
- ? Validity.Valid
- : Validity.Invalid;
- }
- catch (CryptographicException)
- {
- return Validity.Invalid;
- }
- }
- }
-
- public static Validity Rsa2048PssVerify(byte[] data, byte[] signature, byte[] modulus)
- {
- using (var rsa = RSA.Create())
- {
- try
- {
- rsa.ImportParameters(new RSAParameters { Exponent = new byte[] { 1, 0, 1 }, Modulus = modulus });
-
- return rsa.VerifyData(data, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pss)
- ? Validity.Valid
- : Validity.Invalid;
- }
- catch (CryptographicException)
- {
- return Validity.Invalid;
- }
- }
- }
+ public static Validity Rsa2048PssVerify(byte[] data, byte[] signature, byte[] modulus) =>
+ Rsa.VerifyRsa2048PssSha256(signature, modulus, new byte[] { 1, 0, 1 }, data)
+ ? Validity.Valid
+ : Validity.Invalid;
public static byte[] DecryptRsaOaep(byte[] data, RSAParameters rsaParams)
{
@@ -201,222 +99,5 @@ namespace LibHac
}
}
}
-
- public static RSAParameters RecoverRsaParameters(ReadOnlySpan modulus, ReadOnlySpan exponent)
- {
- BigInteger dInt = GetBigInteger(exponent);
- BigInteger nInt = GetBigInteger(modulus);
- BigInteger eInt = GetBigInteger(new byte[] { 1, 0, 1 });
-
- return RecoverRsaParameters(nInt, eInt, dInt);
- }
-
- private static RSAParameters RecoverRsaParameters(BigInteger n, BigInteger e, BigInteger d)
- {
- using (var rng = RandomNumberGenerator.Create())
- {
- BigInteger k = d * e - 1;
-
- if (!k.IsEven)
- {
- throw new InvalidOperationException("d*e - 1 is odd");
- }
-
- BigInteger two = 2;
- BigInteger t = BigInteger.One;
-
- BigInteger r = k / two;
-
- while (r.IsEven)
- {
- t++;
- r /= two;
- }
-
- byte[] rndBuf = n.ToByteArray();
-
- if (rndBuf[rndBuf.Length - 1] == 0)
- {
- rndBuf = new byte[rndBuf.Length - 1];
- }
-
- BigInteger nMinusOne = n - BigInteger.One;
-
- bool cracked = false;
- BigInteger y = BigInteger.Zero;
-
- for (int i = 0; i < 100 && !cracked; i++)
- {
- BigInteger g;
-
- do
- {
- rng.GetBytes(rndBuf);
- g = GetBigInteger(rndBuf);
- }
- while (g >= n);
-
- y = BigInteger.ModPow(g, r, n);
-
- if (y.IsOne || y == nMinusOne)
- {
- i--;
- continue;
- }
-
- for (BigInteger j = BigInteger.One; j < t; j++)
- {
- BigInteger x = BigInteger.ModPow(y, two, n);
-
- if (x.IsOne)
- {
- cracked = true;
- break;
- }
-
- if (x == nMinusOne)
- {
- break;
- }
-
- y = x;
- }
- }
-
- if (!cracked)
- {
- throw new InvalidOperationException("Prime factors not found");
- }
-
- BigInteger p = BigInteger.GreatestCommonDivisor(y - BigInteger.One, n);
- BigInteger q = n / p;
- BigInteger dp = d % (p - BigInteger.One);
- BigInteger dq = d % (q - BigInteger.One);
- BigInteger inverseQ = ModInverse(q, p);
-
- int modLen = rndBuf.Length;
- int halfModLen = (modLen + 1) / 2;
-
- return new RSAParameters
- {
- Modulus = GetBytes(n, modLen),
- Exponent = GetBytes(e, -1),
- D = GetBytes(d, modLen),
- P = GetBytes(p, halfModLen),
- Q = GetBytes(q, halfModLen),
- DP = GetBytes(dp, halfModLen),
- DQ = GetBytes(dq, halfModLen),
- InverseQ = GetBytes(inverseQ, halfModLen)
- };
- }
- }
-
- private static byte[] GetBytes(BigInteger value, int size)
- {
- byte[] bytes = value.ToByteArray();
-
- if (size == -1)
- {
- size = bytes.Length;
- }
-
- if (bytes.Length > size + 1)
- {
- throw new InvalidOperationException($"Cannot squeeze value {value} to {size} bytes from {bytes.Length}.");
- }
-
- if (bytes.Length == size + 1 && bytes[bytes.Length - 1] != 0)
- {
- throw new InvalidOperationException($"Cannot squeeze value {value} to {size} bytes from {bytes.Length}.");
- }
-
- Array.Resize(ref bytes, size);
- Array.Reverse(bytes);
- return bytes;
- }
-
- private static BigInteger ModInverse(BigInteger e, BigInteger n)
- {
- BigInteger r = n;
- BigInteger newR = e;
- BigInteger t = 0;
- BigInteger newT = 1;
-
- while (newR != 0)
- {
- BigInteger quotient = r / newR;
- BigInteger temp;
-
- temp = t;
- t = newT;
- newT = temp - quotient * newT;
-
- temp = r;
- r = newR;
- newR = temp - quotient * newR;
- }
-
- if (t < 0)
- {
- t = t + n;
- }
-
- return t;
- }
-
- public static void CalculateAesCmac(byte[] key, byte[] src, int srcIndex, byte[] dest, int destIndex, int length)
- {
- var l = new byte[16];
- EncryptCbc(key, l, l, l, 0x10);
- byte[] paddedMessage;
- int paddedLength = length;
-
- byte[] firstSubkey = Rol(l);
- if ((l[0] & 0x80) == 0x80)
- firstSubkey[15] ^= 0x87;
-
- byte[] secondSubkey = Rol(firstSubkey);
- if ((firstSubkey[0] & 0x80) == 0x80)
- secondSubkey[15] ^= 0x87;
-
- if (length != 0 && length % 16 == 0)
- {
- paddedMessage = new byte[paddedLength];
- Array.Copy(src, srcIndex, paddedMessage, 0, length);
-
- for (int j = 0; j < firstSubkey.Length; j++)
- paddedMessage[length - 16 + j] ^= firstSubkey[j];
- }
- else
- {
- paddedLength += 16 - length % 16;
- paddedMessage = new byte[paddedLength];
- paddedMessage[length] = 0x80;
- Array.Copy(src, srcIndex, paddedMessage, 0, length);
-
- for (int j = 0; j < secondSubkey.Length; j++)
- paddedMessage[paddedLength - 16 + j] ^= secondSubkey[j];
- }
-
- var encResult = new byte[paddedMessage.Length];
- EncryptCbc(key, new byte[16], paddedMessage, encResult, paddedLength);
-
- Array.Copy(encResult, paddedLength - 0x10, dest, destIndex, 0x10);
- }
-
- private static byte[] Rol(byte[] b)
- {
- var r = new byte[b.Length];
- byte carry = 0;
-
- for (int i = b.Length - 1; i >= 0; i--)
- {
- ushort u = (ushort)(b[i] << 1);
- r[i] = (byte)((u & 0xff) + carry);
- carry = (byte)((u & 0xff00) >> 8);
- }
-
- return r;
- }
}
-}
+}
\ No newline at end of file
diff --git a/src/LibHac/FsSystem/AesXtsFileHeader.cs b/src/LibHac/FsSystem/AesXtsFileHeader.cs
index 0288b631..eabf2bcc 100644
--- a/src/LibHac/FsSystem/AesXtsFileHeader.cs
+++ b/src/LibHac/FsSystem/AesXtsFileHeader.cs
@@ -5,6 +5,8 @@ using System.Text;
using LibHac.Fs;
using LibHac.Fs.Fsa;
+using Aes = LibHac.Crypto.Aes;
+
namespace LibHac.FsSystem
{
public class AesXtsFileHeader
@@ -79,14 +81,14 @@ namespace LibHac.FsSystem
private void DecryptKeys()
{
- CryptoOld.DecryptEcb(Kek1, EncryptedKey1, DecryptedKey1, 0x10);
- CryptoOld.DecryptEcb(Kek2, EncryptedKey2, DecryptedKey2, 0x10);
+ Aes.DecryptEcb128(EncryptedKey1, DecryptedKey1, Kek1);
+ Aes.DecryptEcb128(EncryptedKey2, DecryptedKey2, Kek2);
}
private void EncryptKeys()
{
- CryptoOld.EncryptEcb(Kek1, DecryptedKey1, EncryptedKey1, 0x10);
- CryptoOld.EncryptEcb(Kek2, DecryptedKey2, EncryptedKey2, 0x10);
+ Aes.EncryptEcb128(DecryptedKey1, EncryptedKey1, Kek1);
+ Aes.EncryptEcb128(DecryptedKey2, EncryptedKey2, Kek2);
}
private void GenerateKek(byte[] kekSeed, string path)
diff --git a/src/LibHac/FsSystem/NcaUtils/Nca.cs b/src/LibHac/FsSystem/NcaUtils/Nca.cs
index 7e55a6f4..a1931d5d 100644
--- a/src/LibHac/FsSystem/NcaUtils/Nca.cs
+++ b/src/LibHac/FsSystem/NcaUtils/Nca.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using LibHac.Common;
+using LibHac.Crypto;
using LibHac.Diag;
using LibHac.Fs;
using LibHac.Fs.Fsa;
@@ -50,9 +51,9 @@ namespace LibHac.FsSystem.NcaUtils
}
byte[] encryptedKey = Header.GetEncryptedKey(index).ToArray();
- var decryptedKey = new byte[CryptoOld.Aes128Size];
+ var decryptedKey = new byte[Aes.KeySize128];
- CryptoOld.DecryptEcb(keyAreaKey, encryptedKey, decryptedKey, CryptoOld.Aes128Size);
+ Aes.DecryptEcb128(encryptedKey, decryptedKey, keyAreaKey);
return decryptedKey;
}
@@ -76,9 +77,9 @@ namespace LibHac.FsSystem.NcaUtils
}
byte[] encryptedKey = accessKey.Value.ToArray();
- var decryptedKey = new byte[CryptoOld.Aes128Size];
+ var decryptedKey = new byte[Aes.KeySize128];
- CryptoOld.DecryptEcb(titleKek, encryptedKey, decryptedKey, CryptoOld.Aes128Size);
+ Aes.DecryptEcb128(encryptedKey, decryptedKey, titleKek);
return decryptedKey;
}
diff --git a/src/LibHac/FsSystem/NcaUtils/NcaHeader.cs b/src/LibHac/FsSystem/NcaUtils/NcaHeader.cs
index b0558233..f52d3d22 100644
--- a/src/LibHac/FsSystem/NcaUtils/NcaHeader.cs
+++ b/src/LibHac/FsSystem/NcaUtils/NcaHeader.cs
@@ -160,8 +160,8 @@ namespace LibHac.FsSystem.NcaUtils
throw new ArgumentOutOfRangeException($"Key index must be between 0 and 3. Actual: {index}");
}
- int offset = NcaHeaderStruct.KeyAreaOffset + CryptoOld.Aes128Size * index;
- return _header.Span.Slice(offset, CryptoOld.Aes128Size);
+ int offset = NcaHeaderStruct.KeyAreaOffset + Aes.KeySize128 * index;
+ return _header.Span.Slice(offset, Aes.KeySize128);
}
public NcaFsHeader GetFsHeader(int index)
diff --git a/src/LibHac/FsSystem/Save/Header.cs b/src/LibHac/FsSystem/Save/Header.cs
index db656cb9..be040695 100644
--- a/src/LibHac/FsSystem/Save/Header.cs
+++ b/src/LibHac/FsSystem/Save/Header.cs
@@ -93,7 +93,7 @@ namespace LibHac.FsSystem.Save
{
var calculatedCmac = new byte[0x10];
- CryptoOld.CalculateAesCmac(keyset.SaveMacKey, Data, 0x100, calculatedCmac, 0, 0x200);
+ Aes.CalculateCmac(keyset.SaveMacKey, Data, 0x100, calculatedCmac, 0, 0x200);
return Utilities.ArraysEqual(calculatedCmac, Cmac) ? Validity.Valid : Validity.Invalid;
}
diff --git a/src/LibHac/FsSystem/Save/SaveDataFileSystem.cs b/src/LibHac/FsSystem/Save/SaveDataFileSystem.cs
index 6ec53e1e..c8db4d54 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);
- CryptoOld.CalculateAesCmac(keyset.SaveMacKey, cmacData, 0, cmac, 0, 0x200);
+ Aes.CalculateCmac(keyset.SaveMacKey, cmacData, 0, cmac, 0, 0x200);
headerStream.Position = 0;
headerStream.Write(cmac, 0, 0x10);
diff --git a/src/LibHac/Keyset.cs b/src/LibHac/Keyset.cs
index 4bbdc83b..c4b981ac 100644
--- a/src/LibHac/Keyset.cs
+++ b/src/LibHac/Keyset.cs
@@ -4,6 +4,7 @@ using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
+using LibHac.Crypto;
using LibHac.Fs;
using LibHac.FsSrv;
using LibHac.Spl;
@@ -81,7 +82,7 @@ namespace LibHac
if (_nca0RsaKeyAreaKey.HasValue)
return _nca0RsaKeyAreaKey.Value;
- _nca0RsaKeyAreaKey = CryptoOld.RecoverRsaParameters(BetaNca0Modulus, BetaNca0Exponent);
+ _nca0RsaKeyAreaKey = Rsa.RecoverParameters(BetaNca0Modulus, BetaNca0Exponent, new byte[] { 1, 0, 1 });
return _nca0RsaKeyAreaKey.Value;
}
}
@@ -349,7 +350,7 @@ namespace LibHac
}
Array.Copy(EncryptedKeyblobs[i], expectedCmac, 0x10);
- CryptoOld.CalculateAesCmac(KeyblobMacKeys[i], EncryptedKeyblobs[i], 0x10, cmac, 0, 0xa0);
+ Aes.CalculateCmac(KeyblobMacKeys[i], EncryptedKeyblobs[i], 0x10, cmac, 0, 0xa0);
if (!Utilities.ArraysEqual(cmac, expectedCmac))
{
diff --git a/src/LibHac/Utilities.cs b/src/LibHac/Utilities.cs
index c34207a4..304c22b6 100644
--- a/src/LibHac/Utilities.cs
+++ b/src/LibHac/Utilities.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.IO;
using System.Numerics;
using System.Runtime.CompilerServices;
@@ -500,6 +500,67 @@ namespace LibHac
return value > 0 && ResetLeastSignificantOneBit(value) == 0;
}
+ public static BigInteger GetBigInteger(this ReadOnlySpan bytes)
+ {
+ var signPadded = new byte[bytes.Length + 1];
+ bytes.CopyTo(signPadded.AsSpan(1));
+ Array.Reverse(signPadded);
+ return new BigInteger(signPadded);
+ }
+
+ public static byte[] GetBytes(this BigInteger value, int size)
+ {
+ byte[] bytes = value.ToByteArray();
+
+ if (size == -1)
+ {
+ size = bytes.Length;
+ }
+
+ if (bytes.Length > size + 1)
+ {
+ throw new InvalidOperationException($"Cannot squeeze value {value} to {size} bytes from {bytes.Length}.");
+ }
+
+ if (bytes.Length == size + 1 && bytes[bytes.Length - 1] != 0)
+ {
+ throw new InvalidOperationException($"Cannot squeeze value {value} to {size} bytes from {bytes.Length}.");
+ }
+
+ Array.Resize(ref bytes, size);
+ Array.Reverse(bytes);
+ return bytes;
+ }
+
+ public static BigInteger ModInverse(BigInteger e, BigInteger n)
+ {
+ BigInteger r = n;
+ BigInteger newR = e;
+ BigInteger t = 0;
+ BigInteger newT = 1;
+
+ while (newR != 0)
+ {
+ BigInteger quotient = r / newR;
+ BigInteger temp;
+
+ temp = t;
+ t = newT;
+ newT = temp - quotient * newT;
+
+ temp = r;
+ r = newR;
+ newR = temp - quotient * newR;
+ }
+
+ if (t < 0)
+ {
+ t = t + n;
+ }
+
+ return t;
+ }
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int ResetLeastSignificantOneBit(int value)
{
diff --git a/src/LibHac/XciHeader.cs b/src/LibHac/XciHeader.cs
index 4bfa3bb0..7fa5fb4c 100644
--- a/src/LibHac/XciHeader.cs
+++ b/src/LibHac/XciHeader.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.IO;
using System.Text;
using LibHac.Crypto;
@@ -96,7 +96,7 @@ namespace LibHac
Flags = (GameCardAttribute)reader.ReadByte();
PackageId = reader.ReadUInt64();
ValidDataEndPage = reader.ReadInt64();
- AesCbcIv = reader.ReadBytes(CryptoOld.Aes128Size);
+ AesCbcIv = reader.ReadBytes(Aes.KeySize128);
Array.Reverse(AesCbcIv);
RootPartitionOffset = reader.ReadInt64();
RootPartitionHeaderSize = reader.ReadInt64();
@@ -111,7 +111,7 @@ namespace LibHac
{
byte[] encHeader = reader.ReadBytes(EncryptedHeaderSize);
var decHeader = new byte[EncryptedHeaderSize];
- CryptoOld.DecryptCbc(keyset.XciHeaderKey, AesCbcIv, encHeader, decHeader, EncryptedHeaderSize);
+ Aes.DecryptCbc128(encHeader, decHeader, keyset.XciHeaderKey, AesCbcIv);
using (var decreader = new BinaryReader(new MemoryStream(decHeader)))
{
diff --git a/tests/LibHac.Tests/AesCmac.cs b/tests/LibHac.Tests/AesCmac.cs
index 9caf2315..d0587a20 100644
--- a/tests/LibHac.Tests/AesCmac.cs
+++ b/tests/LibHac.Tests/AesCmac.cs
@@ -1,4 +1,5 @@
-using Xunit;
+using LibHac.Crypto;
+using Xunit;
namespace LibHac.Tests
{
@@ -57,7 +58,7 @@ namespace LibHac.Tests
{
var actual = new byte[0x10];
- CryptoOld.CalculateAesCmac(data.Key, data.Message, data.Start, actual, 0, data.Length);
+ Aes.CalculateCmac(data.Key, data.Message, data.Start, actual, 0, data.Length);
Assert.Equal(data.Expected, actual);
}