mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
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
This commit is contained in:
parent
49a8ffd3e2
commit
265147b678
12 changed files with 325 additions and 360 deletions
|
@ -265,5 +265,84 @@ namespace LibHac.Crypto
|
||||||
|
|
||||||
cipher.Transform(input, output);
|
cipher.Transform(input, output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <param name="k">A byte span containing the 128-bit key used in the AES-CBC-128 steps</param>
|
||||||
|
* <param name="m">A byte span containing the message to be authenticated</param>
|
||||||
|
* <param name="mIndex">The offset within the byte span at which the message will be read from</param>
|
||||||
|
* <param name="t">A byte span to output the message authentication code into</param>
|
||||||
|
* <param name="tIndex">The offset within the byte span at which the authentication code will be written to</param>
|
||||||
|
* <param name="len">The length of the message</param>
|
||||||
|
* <remarks>https://tools.ietf.org/html/rfc4493</remarks>
|
||||||
|
*/
|
||||||
|
public static void CalculateCmac(Span<byte> k, Span<byte> m, int mIndex, Span<byte> t, int tIndex, int len)
|
||||||
|
{
|
||||||
|
ReadOnlySpan<byte> zero = stackalloc byte[16];
|
||||||
|
|
||||||
|
// Step 1, AES-128 with key K is applied to an all-zero input block.
|
||||||
|
Span<byte> l = stackalloc byte[16];
|
||||||
|
|
||||||
|
EncryptCbc128(zero, l, k, zero);
|
||||||
|
|
||||||
|
// Step 2, K1 is derived through the following operation:
|
||||||
|
Span<byte> 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<byte> 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<byte> 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<byte> 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<byte> 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<byte> 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <param name="k">A byte span containing the 128-bit key used in the AES-CBC-128 steps</param>
|
||||||
|
* <param name="m">A byte span containing the message to be authenticated</param>
|
||||||
|
* <param name="t">A byte span to output the message authentication code into</param>
|
||||||
|
* <remarks>https://tools.ietf.org/html/rfc4493</remarks>
|
||||||
|
*/
|
||||||
|
public static void CalculateCmac(Span<byte> k, Span<byte> m, Span<byte> t) =>
|
||||||
|
CalculateCmac(k, m, 0, t, 0, m.Length);
|
||||||
|
|
||||||
|
private static byte[] LeftShiftBytes(Span<byte> 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Numerics;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
|
|
||||||
namespace LibHac.Crypto
|
namespace LibHac.Crypto
|
||||||
|
@ -6,7 +7,15 @@ namespace LibHac.Crypto
|
||||||
public static class Rsa
|
public static class Rsa
|
||||||
{
|
{
|
||||||
public static bool VerifyRsa2048PssSha256(ReadOnlySpan<byte> signature, ReadOnlySpan<byte> modulus,
|
public static bool VerifyRsa2048PssSha256(ReadOnlySpan<byte> signature, ReadOnlySpan<byte> modulus,
|
||||||
ReadOnlySpan<byte> exponent, ReadOnlySpan<byte> message)
|
ReadOnlySpan<byte> exponent, ReadOnlySpan<byte> message) =>
|
||||||
|
VerifyRsa2048Sha256(signature, modulus, exponent, message, RSASignaturePadding.Pss);
|
||||||
|
|
||||||
|
public static bool VerifyRsa2048Pkcs1Sha256(ReadOnlySpan<byte> signature, ReadOnlySpan<byte> modulus,
|
||||||
|
ReadOnlySpan<byte> exponent, ReadOnlySpan<byte> message) =>
|
||||||
|
VerifyRsa2048Sha256(signature, modulus, exponent, message, RSASignaturePadding.Pkcs1);
|
||||||
|
|
||||||
|
private static bool VerifyRsa2048Sha256(ReadOnlySpan<byte> signature, ReadOnlySpan<byte> modulus,
|
||||||
|
ReadOnlySpan<byte> exponent, ReadOnlySpan<byte> message, RSASignaturePadding padding)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -14,7 +23,7 @@ namespace LibHac.Crypto
|
||||||
|
|
||||||
using (var rsa = RSA.Create(param))
|
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)
|
catch (CryptographicException)
|
||||||
|
@ -40,5 +49,135 @@ namespace LibHac.Crypto
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <param name="n">The RSA Modulus (n)</param>
|
||||||
|
* <param name="e">The RSA Public Exponent (e)</param>
|
||||||
|
* <param name="d">The RSA Private Exponent (d)</param>
|
||||||
|
*/
|
||||||
|
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)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <param name="n">The RSA Modulus (n)</param>
|
||||||
|
* <param name="e">The RSA Public Exponent (e)</param>
|
||||||
|
* <param name="d">The RSA Private Exponent (d)</param>
|
||||||
|
*/
|
||||||
|
public static RSAParameters RecoverParameters(ReadOnlySpan<byte> n, ReadOnlySpan<byte> e, ReadOnlySpan<byte> d) =>
|
||||||
|
RecoverParameters(n.GetBigInteger(), e.GetBigInteger(), d.GetBigInteger());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <summary>
|
||||||
|
* Derive RSA Prime Number Pair (p, q) from RSA Modulus (n), RSA Public Exponent (e) and RSA Private Exponent (d)
|
||||||
|
* </summary>
|
||||||
|
* <param name="n">The RSA Modulus (n)</param>
|
||||||
|
* <param name="e">The RSA Public Exponent (e)</param>
|
||||||
|
* <param name="d">The RSA Private Exponent (d)</param>
|
||||||
|
* <returns>RSA Prime Number Pair</returns>
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,103 +1,33 @@
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Numerics;
|
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
|
using LibHac.Crypto;
|
||||||
using LibHac.FsSystem;
|
using LibHac.FsSystem;
|
||||||
|
|
||||||
|
using Aes = LibHac.Crypto.Aes;
|
||||||
|
|
||||||
namespace LibHac
|
namespace LibHac
|
||||||
{
|
{
|
||||||
public static class CryptoOld
|
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)
|
public static void GenerateKek(byte[] key, byte[] src, byte[] dest, byte[] kekSeed, byte[] keySeed)
|
||||||
{
|
{
|
||||||
var kek = new byte[Aes128Size];
|
var kek = new byte[Aes.KeySize128];
|
||||||
var srcKek = new byte[Aes128Size];
|
var srcKek = new byte[Aes.KeySize128];
|
||||||
|
|
||||||
DecryptEcb(key, kekSeed, kek, Aes128Size);
|
Aes.DecryptEcb128(kekSeed, kek, key);
|
||||||
DecryptEcb(kek, src, srcKek, Aes128Size);
|
Aes.DecryptEcb128(src, srcKek, kek);
|
||||||
|
|
||||||
if (keySeed != null)
|
if (keySeed != null)
|
||||||
{
|
{
|
||||||
DecryptEcb(srcKek, keySeed, dest, Aes128Size);
|
Aes.DecryptEcb128(keySeed, dest, srcKek);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Array.Copy(srcKek, dest, Aes128Size);
|
Array.Copy(srcKek, dest, Aes.KeySize128);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static BigInteger GetBigInteger(ReadOnlySpan<byte> 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)
|
public static RSAParameters DecryptRsaKey(byte[] encryptedKey, byte[] kek)
|
||||||
{
|
{
|
||||||
var counter = new byte[0x10];
|
var counter = new byte[0x10];
|
||||||
|
@ -114,11 +44,7 @@ namespace LibHac
|
||||||
Array.Copy(key, 0x100, n, 0, 0x100);
|
Array.Copy(key, 0x100, n, 0, 0x100);
|
||||||
Array.Copy(key, 0x200, e, 0, 4);
|
Array.Copy(key, 0x200, e, 0, 4);
|
||||||
|
|
||||||
BigInteger dInt = GetBigInteger(d);
|
RSAParameters rsaParams = Rsa.RecoverParameters(n, e, d);
|
||||||
BigInteger nInt = GetBigInteger(n);
|
|
||||||
BigInteger eInt = GetBigInteger(e);
|
|
||||||
|
|
||||||
RSAParameters rsaParams = RecoverRsaParameters(nInt, eInt, dInt);
|
|
||||||
TestRsaKey(rsaParams);
|
TestRsaKey(rsaParams);
|
||||||
return rsaParams;
|
return rsaParams;
|
||||||
}
|
}
|
||||||
|
@ -138,43 +64,15 @@ namespace LibHac
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Validity Rsa2048Pkcs1Verify(byte[] data, byte[] signature, byte[] modulus)
|
public static Validity Rsa2048Pkcs1Verify(byte[] data, byte[] signature, byte[] modulus) =>
|
||||||
{
|
Rsa.VerifyRsa2048Pkcs1Sha256(signature, modulus, new byte[] { 1, 0, 1 }, data)
|
||||||
using (var rsa = RSA.Create())
|
? Validity.Valid
|
||||||
{
|
: Validity.Invalid;
|
||||||
try
|
|
||||||
{
|
|
||||||
rsa.ImportParameters(new RSAParameters { Exponent = new byte[] { 1, 0, 1 }, Modulus = modulus });
|
|
||||||
|
|
||||||
return rsa.VerifyData(data, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1)
|
public static Validity Rsa2048PssVerify(byte[] data, byte[] signature, byte[] modulus) =>
|
||||||
? Validity.Valid
|
Rsa.VerifyRsa2048PssSha256(signature, modulus, new byte[] { 1, 0, 1 }, data)
|
||||||
: Validity.Invalid;
|
? 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 byte[] DecryptRsaOaep(byte[] data, RSAParameters rsaParams)
|
public static byte[] DecryptRsaOaep(byte[] data, RSAParameters rsaParams)
|
||||||
{
|
{
|
||||||
|
@ -201,222 +99,5 @@ namespace LibHac
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static RSAParameters RecoverRsaParameters(ReadOnlySpan<byte> modulus, ReadOnlySpan<byte> 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -5,6 +5,8 @@ using System.Text;
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
using LibHac.Fs.Fsa;
|
using LibHac.Fs.Fsa;
|
||||||
|
|
||||||
|
using Aes = LibHac.Crypto.Aes;
|
||||||
|
|
||||||
namespace LibHac.FsSystem
|
namespace LibHac.FsSystem
|
||||||
{
|
{
|
||||||
public class AesXtsFileHeader
|
public class AesXtsFileHeader
|
||||||
|
@ -79,14 +81,14 @@ namespace LibHac.FsSystem
|
||||||
|
|
||||||
private void DecryptKeys()
|
private void DecryptKeys()
|
||||||
{
|
{
|
||||||
CryptoOld.DecryptEcb(Kek1, EncryptedKey1, DecryptedKey1, 0x10);
|
Aes.DecryptEcb128(EncryptedKey1, DecryptedKey1, Kek1);
|
||||||
CryptoOld.DecryptEcb(Kek2, EncryptedKey2, DecryptedKey2, 0x10);
|
Aes.DecryptEcb128(EncryptedKey2, DecryptedKey2, Kek2);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void EncryptKeys()
|
private void EncryptKeys()
|
||||||
{
|
{
|
||||||
CryptoOld.EncryptEcb(Kek1, DecryptedKey1, EncryptedKey1, 0x10);
|
Aes.EncryptEcb128(DecryptedKey1, EncryptedKey1, Kek1);
|
||||||
CryptoOld.EncryptEcb(Kek2, DecryptedKey2, EncryptedKey2, 0x10);
|
Aes.EncryptEcb128(DecryptedKey2, EncryptedKey2, Kek2);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GenerateKek(byte[] kekSeed, string path)
|
private void GenerateKek(byte[] kekSeed, string path)
|
||||||
|
|
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
|
using LibHac.Crypto;
|
||||||
using LibHac.Diag;
|
using LibHac.Diag;
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
using LibHac.Fs.Fsa;
|
using LibHac.Fs.Fsa;
|
||||||
|
@ -50,9 +51,9 @@ namespace LibHac.FsSystem.NcaUtils
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] encryptedKey = Header.GetEncryptedKey(index).ToArray();
|
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;
|
return decryptedKey;
|
||||||
}
|
}
|
||||||
|
@ -76,9 +77,9 @@ namespace LibHac.FsSystem.NcaUtils
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] encryptedKey = accessKey.Value.ToArray();
|
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;
|
return decryptedKey;
|
||||||
}
|
}
|
||||||
|
|
|
@ -160,8 +160,8 @@ namespace LibHac.FsSystem.NcaUtils
|
||||||
throw new ArgumentOutOfRangeException($"Key index must be between 0 and 3. Actual: {index}");
|
throw new ArgumentOutOfRangeException($"Key index must be between 0 and 3. Actual: {index}");
|
||||||
}
|
}
|
||||||
|
|
||||||
int offset = NcaHeaderStruct.KeyAreaOffset + CryptoOld.Aes128Size * index;
|
int offset = NcaHeaderStruct.KeyAreaOffset + Aes.KeySize128 * index;
|
||||||
return _header.Span.Slice(offset, CryptoOld.Aes128Size);
|
return _header.Span.Slice(offset, Aes.KeySize128);
|
||||||
}
|
}
|
||||||
|
|
||||||
public NcaFsHeader GetFsHeader(int index)
|
public NcaFsHeader GetFsHeader(int index)
|
||||||
|
|
|
@ -93,7 +93,7 @@ namespace LibHac.FsSystem.Save
|
||||||
{
|
{
|
||||||
var calculatedCmac = new byte[0x10];
|
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;
|
return Utilities.ArraysEqual(calculatedCmac, Cmac) ? Validity.Valid : Validity.Invalid;
|
||||||
}
|
}
|
||||||
|
|
|
@ -269,7 +269,7 @@ namespace LibHac.FsSystem.Save
|
||||||
headerStream.Position = 0x100;
|
headerStream.Position = 0x100;
|
||||||
headerStream.Read(cmacData, 0, 0x200);
|
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.Position = 0;
|
||||||
headerStream.Write(cmac, 0, 0x10);
|
headerStream.Write(cmac, 0, 0x10);
|
||||||
|
|
|
@ -4,6 +4,7 @@ using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using LibHac.Crypto;
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
using LibHac.FsSrv;
|
using LibHac.FsSrv;
|
||||||
using LibHac.Spl;
|
using LibHac.Spl;
|
||||||
|
@ -81,7 +82,7 @@ namespace LibHac
|
||||||
if (_nca0RsaKeyAreaKey.HasValue)
|
if (_nca0RsaKeyAreaKey.HasValue)
|
||||||
return _nca0RsaKeyAreaKey.Value;
|
return _nca0RsaKeyAreaKey.Value;
|
||||||
|
|
||||||
_nca0RsaKeyAreaKey = CryptoOld.RecoverRsaParameters(BetaNca0Modulus, BetaNca0Exponent);
|
_nca0RsaKeyAreaKey = Rsa.RecoverParameters(BetaNca0Modulus, BetaNca0Exponent, new byte[] { 1, 0, 1 });
|
||||||
return _nca0RsaKeyAreaKey.Value;
|
return _nca0RsaKeyAreaKey.Value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -349,7 +350,7 @@ namespace LibHac
|
||||||
}
|
}
|
||||||
|
|
||||||
Array.Copy(EncryptedKeyblobs[i], expectedCmac, 0x10);
|
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))
|
if (!Utilities.ArraysEqual(cmac, expectedCmac))
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
@ -500,6 +500,67 @@ namespace LibHac
|
||||||
return value > 0 && ResetLeastSignificantOneBit(value) == 0;
|
return value > 0 && ResetLeastSignificantOneBit(value) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static BigInteger GetBigInteger(this ReadOnlySpan<byte> 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)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static int ResetLeastSignificantOneBit(int value)
|
private static int ResetLeastSignificantOneBit(int value)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using LibHac.Crypto;
|
using LibHac.Crypto;
|
||||||
|
@ -96,7 +96,7 @@ namespace LibHac
|
||||||
Flags = (GameCardAttribute)reader.ReadByte();
|
Flags = (GameCardAttribute)reader.ReadByte();
|
||||||
PackageId = reader.ReadUInt64();
|
PackageId = reader.ReadUInt64();
|
||||||
ValidDataEndPage = reader.ReadInt64();
|
ValidDataEndPage = reader.ReadInt64();
|
||||||
AesCbcIv = reader.ReadBytes(CryptoOld.Aes128Size);
|
AesCbcIv = reader.ReadBytes(Aes.KeySize128);
|
||||||
Array.Reverse(AesCbcIv);
|
Array.Reverse(AesCbcIv);
|
||||||
RootPartitionOffset = reader.ReadInt64();
|
RootPartitionOffset = reader.ReadInt64();
|
||||||
RootPartitionHeaderSize = reader.ReadInt64();
|
RootPartitionHeaderSize = reader.ReadInt64();
|
||||||
|
@ -111,7 +111,7 @@ namespace LibHac
|
||||||
{
|
{
|
||||||
byte[] encHeader = reader.ReadBytes(EncryptedHeaderSize);
|
byte[] encHeader = reader.ReadBytes(EncryptedHeaderSize);
|
||||||
var decHeader = new byte[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)))
|
using (var decreader = new BinaryReader(new MemoryStream(decHeader)))
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using Xunit;
|
using LibHac.Crypto;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
namespace LibHac.Tests
|
namespace LibHac.Tests
|
||||||
{
|
{
|
||||||
|
@ -57,7 +58,7 @@ namespace LibHac.Tests
|
||||||
{
|
{
|
||||||
var actual = new byte[0x10];
|
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);
|
Assert.Equal(data.Expected, actual);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue