From d29f7b27a7d5bccf40f99893a1b089608174ae68 Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Wed, 19 Dec 2018 20:06:04 -0600 Subject: [PATCH] Add Mono compatibility for RSA-PSS --- src/LibHac/Compat.cs | 62 ++++++++++++++++++++++++++++++++++++++++++++ src/LibHac/Crypto.cs | 12 ++++++++- 2 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 src/LibHac/Compat.cs diff --git a/src/LibHac/Compat.cs b/src/LibHac/Compat.cs new file mode 100644 index 00000000..13b6b263 --- /dev/null +++ b/src/LibHac/Compat.cs @@ -0,0 +1,62 @@ +#if NETFRAMEWORK + +using System; +using System.Numerics; +using System.Security.Cryptography; + +namespace LibHac +{ + internal class Compat + { + public static bool IsMono { get; } = Type.GetType("Mono.Runtime") != null; + + public static bool Rsa2048PssVerifyMono(byte[] data, byte[] signature, byte[] modulus) + { + const int rsaLen = 0x100; + const int digestLen = 0x20; + + const int hashOffset = rsaLen - digestLen - 1; + const int saltOffset = hashOffset - digestLen; + const int padEnd = saltOffset - 1; + + SHA256 sha = SHA256.Create(); + var message = new byte[rsaLen]; + + BigInteger decInt = BigInteger.ModPow(Crypto.GetBigInteger(signature), new BigInteger(65537), Crypto.GetBigInteger(modulus)); + byte[] decBytes = decInt.ToByteArray(); + + if (decBytes[0] != 0xBC) return false; + + Array.Reverse(decBytes); + Array.Copy(decBytes, 0, message, message.Length - decBytes.Length, decBytes.Length); + + var hashBuf = new byte[0x24]; + Array.Copy(message, hashOffset, hashBuf, 0, digestLen); + + ref byte seed = ref hashBuf[0x23]; + + for (int i = 0; i < hashOffset; i += 0x20) + { + Util.XorArrays(message.AsSpan(i, digestLen), sha.ComputeHash(hashBuf)); + seed++; + } + + message[0] &= 0x7F; + + if (!Util.IsEmpty(message.AsSpan(0, padEnd)) || message[padEnd] != 1) + { + return false; + } + + var prefix = new byte[8]; + byte[] digest = sha.ComputeHash(data); + + sha.TransformBlock(prefix, 0, prefix.Length, null, 0); + sha.TransformBlock(digest, 0, digestLen, null, 0); + sha.TransformFinalBlock(message, saltOffset, digestLen); + + return Util.SpansEqual(hashBuf.AsSpan(0, 0x20), sha.Hash); + } + } +} +#endif diff --git a/src/LibHac/Crypto.cs b/src/LibHac/Crypto.cs index 1c0aaf48..caa6b516 100644 --- a/src/LibHac/Crypto.cs +++ b/src/LibHac/Crypto.cs @@ -94,7 +94,7 @@ namespace LibHac } } - private static BigInteger GetBigInteger(byte[] bytes) + internal static BigInteger GetBigInteger(byte[] bytes) { var signPadded = new byte[bytes.Length + 1]; Buffer.BlockCopy(bytes, 0, signPadded, 1, bytes.Length); @@ -156,6 +156,15 @@ namespace LibHac public static Validity Rsa2048PssVerify(byte[] data, byte[] signature, byte[] modulus) { +#if NETFRAMEWORK + if (true) + { + return Compat.Rsa2048PssVerifyMono(data, signature, modulus) + ? Validity.Valid + : Validity.Invalid; + } +#endif + #if USE_RSA_CNG using (RSA rsa = new RSACng()) #else @@ -172,6 +181,7 @@ namespace LibHac public static byte[] DecryptTitleKey(byte[] titleKeyblock, RSAParameters rsaParams) { + // todo: Does this work on Mono? #if USE_RSA_CNG RSA rsa = new RSACng(); #else