From 265147b6786af21d1a5d48bf49b7d70d73305eed Mon Sep 17 00:00:00 2001 From: Xpl0itR Date: Thu, 20 Aug 2020 15:30:47 +0100 Subject: [PATCH] Changes to CryptoOld - Remove AES functions and replace references with functions from the Crypto.Aes class - Edit and move the AES-CMAC function into the Crypto.Aes class - Move BigInteger functions to the Utilities class - Reduce duplication between the RSA functions - Edit and move the Rsa parameter recovery function into the Crypto.Rsa class --- src/LibHac/Crypto/Aes.cs | 79 ++++ src/LibHac/Crypto/Rsa.cs | 143 ++++++- src/LibHac/CryptoOld.cs | 357 +----------------- src/LibHac/FsSystem/AesXtsFileHeader.cs | 10 +- src/LibHac/FsSystem/NcaUtils/Nca.cs | 9 +- src/LibHac/FsSystem/NcaUtils/NcaHeader.cs | 4 +- src/LibHac/FsSystem/Save/Header.cs | 2 +- .../FsSystem/Save/SaveDataFileSystem.cs | 2 +- src/LibHac/Keyset.cs | 5 +- src/LibHac/Utilities.cs | 63 +++- src/LibHac/XciHeader.cs | 6 +- tests/LibHac.Tests/AesCmac.cs | 5 +- 12 files changed, 325 insertions(+), 360 deletions(-) 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); }