diff --git a/NandReader/NandReader.csproj b/NandReader/NandReader.csproj
index 9d179dd1..c3a0040d 100644
--- a/NandReader/NandReader.csproj
+++ b/NandReader/NandReader.csproj
@@ -2,7 +2,7 @@
Exe
- netcoreapp2.1;net45
+ netcoreapp2.1;net46
diff --git a/hactoolnet/hactoolnet.csproj b/hactoolnet/hactoolnet.csproj
index df0aed13..2704751a 100644
--- a/hactoolnet/hactoolnet.csproj
+++ b/hactoolnet/hactoolnet.csproj
@@ -2,7 +2,7 @@
Exe
- netcoreapp2.1;net45
+ netcoreapp2.1;net46
7.3
diff --git a/libhac.Nand/libhac.Nand.csproj b/libhac.Nand/libhac.Nand.csproj
index cb03397e..40581c82 100644
--- a/libhac.Nand/libhac.Nand.csproj
+++ b/libhac.Nand/libhac.Nand.csproj
@@ -1,7 +1,7 @@
- netcoreapp2.1;net45
+ netcoreapp2.1;net46
diff --git a/libhac/Crypto.cs b/libhac/Crypto.cs
index 64191c76..f9e35273 100644
--- a/libhac/Crypto.cs
+++ b/libhac/Crypto.cs
@@ -6,7 +6,7 @@ using libhac.XTSSharp;
namespace libhac
{
- public class Crypto
+ public static class Crypto
{
internal const int Aes128Size = 0x10;
internal const int Sha256DigestSize = 0x20;
@@ -67,15 +67,15 @@ namespace libhac
}
}
- internal static BigInteger GetBigInteger(byte[] bytes)
+ private static BigInteger GetBigInteger(byte[] bytes)
{
- byte[] signPadded = new byte[bytes.Length + 1];
+ var signPadded = new byte[bytes.Length + 1];
Buffer.BlockCopy(bytes, 0, signPadded, 1, bytes.Length);
Array.Reverse(signPadded);
return new BigInteger(signPadded);
}
- public static RsaKey DecryptRsaKey(byte[] encryptedKey, byte[] kek)
+ public static RSAParameters DecryptRsaKey(byte[] encryptedKey, byte[] kek)
{
var counter = new byte[0x10];
Array.Copy(encryptedKey, counter, 0x10);
@@ -99,120 +99,188 @@ namespace libhac
var nInt = GetBigInteger(n);
var eInt = GetBigInteger(e);
- var test = new BigInteger(12345678);
- var testEnc = BigInteger.ModPow(test, dInt, nInt);
- var testDec = BigInteger.ModPow(testEnc, eInt, nInt);
+ RSAParameters rsaParams = RecoverRsaParameters(nInt, eInt, dInt);
+ TestRsaKey(rsaParams);
+ return rsaParams;
+ }
- if (test != testDec)
+ private static void TestRsaKey(RSAParameters keyParams)
+ {
+ var rsa = new RSACryptoServiceProvider();
+ rsa.ImportParameters(keyParams);
+
+ var test = new byte[] {12, 34, 56, 78};
+ byte[] testEnc = rsa.Encrypt(test, false);
+ byte[] testDec = rsa.Decrypt(testEnc, false);
+
+ if (!Util.ArraysEqual(test, testDec))
{
throw new InvalidDataException("Could not verify RSA key pair");
}
-
- return new RsaKey(n, e, d);
}
- public static byte[] DecryptTitleKey(byte[] titleKeyblock, RsaKey rsaKey)
+ public static byte[] DecryptTitleKey(byte[] titleKeyblock, RSAParameters rsaParams)
{
- if (rsaKey == null) return new byte[0x10];
-
- var tikInt = GetBigInteger(titleKeyblock);
- var decInt = BigInteger.ModPow(tikInt, rsaKey.DInt, rsaKey.NInt);
- var decBytes = decInt.ToByteArray();
- Array.Reverse(decBytes);
- var decBlock = new byte[0x100];
- Array.Copy(decBytes, 0, decBlock, decBlock.Length - decBytes.Length, decBytes.Length);
- return UnwrapTitleKey(decBlock);
+#if USE_RSA_CNG
+ RSA rsa = new RSACng();
+#else
+ RSA rsa = RSA.Create();
+#endif
+ rsa.ImportParameters(rsaParams);
+ return rsa.Decrypt(titleKeyblock, RSAEncryptionPadding.OaepSHA256);
}
- public static byte[] UnwrapTitleKey(byte[] data)
+ private static RSAParameters RecoverRsaParameters(BigInteger n, BigInteger e, BigInteger d)
{
- var expectedLabelHash = Ticket.LabelHash;
- var salt = new byte[0x20];
- var db = new byte[0xdf];
- Array.Copy(data, 1, salt, 0, salt.Length);
- Array.Copy(data, 0x21, db, 0, db.Length);
-
- CalculateMgf1AndXor(salt, db);
- CalculateMgf1AndXor(db, salt);
-
- for (int i = 0; i < 0x20; i++)
+ using (RandomNumberGenerator rng = RandomNumberGenerator.Create())
{
- if (expectedLabelHash[i] != db[i])
+ BigInteger k = d * e - 1;
+
+ if (!k.IsEven)
{
- return null;
- }
- }
-
- int keyOffset = 0x20;
- while (keyOffset < 0xdf)
- {
- var value = db[keyOffset++];
- if (value == 1)
- {
- break;
- }
- if (value != 0)
- {
- return null;
- }
- }
-
- if (keyOffset + 0x10 > db.Length) return null;
-
- var key = new byte[0x10];
- Array.Copy(db, keyOffset, key, 0, 0x10);
-
- return key;
- }
-
- private static void CalculateMgf1AndXor(byte[] masked, byte[] seed)
- {
- SHA256 hash = SHA256.Create();
- var hashBuf = new byte[seed.Length + 4];
- Array.Copy(seed, hashBuf, seed.Length);
-
- int maskedSize = masked.Length;
- int roundNum = 0;
- int pOut = 0;
-
- while (maskedSize != 0)
- {
- hashBuf[hashBuf.Length - 4] = (byte)(roundNum >> 24);
- hashBuf[hashBuf.Length - 3] = (byte)(roundNum >> 16);
- hashBuf[hashBuf.Length - 2] = (byte)(roundNum >> 8);
- hashBuf[hashBuf.Length - 1] = (byte)roundNum;
- roundNum++;
-
- byte[] mask = hash.ComputeHash(hashBuf);
- int curSize = Math.Min(maskedSize, 0x20);
-
- for (int i = 0; i < curSize; i++, pOut++)
- {
- masked[pOut] ^= mask[i];
+ throw new InvalidOperationException("d*e - 1 is odd");
}
- maskedSize -= curSize;
+ 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),
+ };
}
}
- }
- public class RsaKey
- {
- public byte[] N { get; }
- public byte[] E { get; }
- public byte[] D { get; }
- public BigInteger NInt { get; }
- public BigInteger EInt { get; }
- public BigInteger DInt { get; }
-
- public RsaKey(byte[] n, byte[] e, byte[] d)
+ private static byte[] GetBytes(BigInteger value, int size)
{
- N = n;
- E = e;
- D = d;
- NInt = Crypto.GetBigInteger(n);
- EInt = Crypto.GetBigInteger(e);
- DInt = Crypto.GetBigInteger(d);
+ 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;
}
}
}
diff --git a/libhac/Keyset.cs b/libhac/Keyset.cs
index b58681cd..c3e04338 100644
--- a/libhac/Keyset.cs
+++ b/libhac/Keyset.cs
@@ -4,6 +4,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Security.Cryptography;
namespace libhac
{
@@ -46,7 +47,7 @@ namespace libhac
public byte[] device_key { get; set; } = new byte[0x10];
public byte[][] bis_keys { get; set; } = Util.CreateJaggedArray(4, 0x20);
public byte[] sd_seed { get; set; } = new byte[0x10];
- public RsaKey eticket_ext_key_rsa { get; set; }
+ public RSAParameters eticket_ext_key_rsa { get; set; }
public Dictionary TitleKeys { get; } = new Dictionary(new ByteArray128BitComparer());
diff --git a/libhac/libhac.csproj b/libhac/libhac.csproj
index fa64656b..b555bb87 100644
--- a/libhac/libhac.csproj
+++ b/libhac/libhac.csproj
@@ -2,12 +2,16 @@
Library
- netcoreapp2.1;net45
+ netcoreapp2.1;net46
7.3
+
+ $(DefineConstants);USE_RSA_CNG
+
+
-
+