From 90e86fa9a60ab86ea1de163b642a7fed51ddabaa Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?L=C3=A9o=20Lam?= <leo@innovatetechnologi.es>
Date: Fri, 18 May 2018 21:20:43 +0200
Subject: [PATCH 1/3] ES/Formats: Move sha1 calculation to SignedBlobReader

---
 Source/Core/Core/IOS/ES/ES.cpp      | 11 +----------
 Source/Core/Core/IOS/ES/Formats.cpp | 25 +++++++++++++++++++++++++
 Source/Core/Core/IOS/ES/Formats.h   |  3 +++
 3 files changed, 29 insertions(+), 10 deletions(-)

diff --git a/Source/Core/Core/IOS/ES/ES.cpp b/Source/Core/Core/IOS/ES/ES.cpp
index 0743dc7efa..569d130389 100644
--- a/Source/Core/Core/IOS/ES/ES.cpp
+++ b/Source/Core/Core/IOS/ES/ES.cpp
@@ -11,8 +11,6 @@
 #include <utility>
 #include <vector>
 
-#include <mbedtls/sha1.h>
-
 #include "Common/ChunkFile.h"
 #include "Common/Logging/Log.h"
 #include "Common/MsgHandler.h"
@@ -950,16 +948,9 @@ ReturnCode ES::VerifyContainer(VerifyContainerType type, VerifyMode mode,
     return ret;
   }
 
-  // Calculate the SHA1 of the signed blob.
-  const size_t skip = type == VerifyContainerType::Device ? offsetof(SignatureECC, issuer) :
-                                                            offsetof(SignatureRSA2048, issuer);
-  std::array<u8, 20> sha1;
-  mbedtls_sha1(signed_blob.GetBytes().data() + skip, signed_blob.GetBytes().size() - skip,
-               sha1.data());
-
   // Verify the signature.
   const std::vector<u8> signature = signed_blob.GetSignatureData();
-  ret = iosc.VerifyPublicKeySign(sha1, issuer_handle, signature.data(), PID_ES);
+  ret = iosc.VerifyPublicKeySign(signed_blob.GetSha1(), issuer_handle, signature.data(), PID_ES);
   if (ret != IPC_SUCCESS)
   {
     ERROR_LOG(IOS_ES, "VerifyContainer: IOSC_VerifyPublicKeySign failed with error %d", ret);
diff --git a/Source/Core/Core/IOS/ES/Formats.cpp b/Source/Core/Core/IOS/ES/Formats.cpp
index 2cc39198ea..6039cacb8c 100644
--- a/Source/Core/Core/IOS/ES/Formats.cpp
+++ b/Source/Core/Core/IOS/ES/Formats.cpp
@@ -16,6 +16,8 @@
 #include <utility>
 #include <vector>
 
+#include <mbedtls/sha1.h>
+
 #include "Common/Assert.h"
 #include "Common/ChunkFile.h"
 #include "Common/CommonTypes.h"
@@ -102,6 +104,29 @@ void SignedBlobReader::SetBytes(std::vector<u8>&& bytes)
   m_bytes = std::move(bytes);
 }
 
+static size_t GetIssuerOffset(SignatureType signature_type)
+{
+  switch (signature_type)
+  {
+  case SignatureType::RSA2048:
+    return offsetof(SignatureRSA2048, issuer);
+  case SignatureType::RSA4096:
+    return offsetof(SignatureRSA4096, issuer);
+  case SignatureType::ECC:
+    return offsetof(SignatureECC, issuer);
+  default:
+    return 0;
+  }
+}
+
+std::array<u8, 20> SignedBlobReader::GetSha1() const
+{
+  std::array<u8, 20> sha1;
+  const size_t skip = GetIssuerOffset(GetSignatureType());
+  mbedtls_sha1(m_bytes.data() + skip, m_bytes.size() - skip, sha1.data());
+  return sha1;
+}
+
 bool SignedBlobReader::IsSignatureValid() const
 {
   // Too small for the certificate type.
diff --git a/Source/Core/Core/IOS/ES/Formats.h b/Source/Core/Core/IOS/ES/Formats.h
index 1b1c1bbcb0..6afbf3407a 100644
--- a/Source/Core/Core/IOS/ES/Formats.h
+++ b/Source/Core/Core/IOS/ES/Formats.h
@@ -162,6 +162,9 @@ public:
   void SetBytes(const std::vector<u8>& bytes);
   void SetBytes(std::vector<u8>&& bytes);
 
+  /// Get the SHA1 hash for this signed blob (starting at the issuer).
+  std::array<u8, 20> GetSha1() const;
+
   // Only checks whether the signature data could be parsed. The signature is not verified.
   bool IsSignatureValid() const;
 

From 964d00447df796aa7a90da13ca776ce7eed0fec4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?L=C3=A9o=20Lam?= <leo@innovatetechnologi.es>
Date: Fri, 18 May 2018 21:43:26 +0200
Subject: [PATCH 2/3] IOSC: Reuse CertReader for cert imports

---
 Source/Core/Core/IOS/ES/ES.cpp | 30 ++++++++++----
 Source/Core/Core/IOS/ES/ES.h   |  7 +++-
 Source/Core/Core/IOS/IOSC.cpp  | 75 ++++++----------------------------
 Source/Core/Core/IOS/IOSC.h    | 10 ++++-
 4 files changed, 49 insertions(+), 73 deletions(-)

diff --git a/Source/Core/Core/IOS/ES/ES.cpp b/Source/Core/Core/IOS/ES/ES.cpp
index 569d130389..e75bdfb77d 100644
--- a/Source/Core/Core/IOS/ES/ES.cpp
+++ b/Source/Core/Core/IOS/ES/ES.cpp
@@ -888,7 +888,7 @@ ReturnCode ES::WriteNewCertToStore(const IOS::ES::CertReader& cert)
 
 ReturnCode ES::VerifyContainer(VerifyContainerType type, VerifyMode mode,
                                const IOS::ES::SignedBlobReader& signed_blob,
-                               const std::vector<u8>& cert_chain, u32 iosc_handle)
+                               const std::vector<u8>& cert_chain, u32* issuer_handle_out)
 {
   if (!SConfig::GetInstance().m_enable_signature_checks)
     return IPC_SUCCESS;
@@ -927,7 +927,7 @@ ReturnCode ES::VerifyContainer(VerifyContainerType type, VerifyMode mode,
   if (ret != IPC_SUCCESS)
     return ret;
   Common::ScopeGuard ca_guard{[&] { iosc.DeleteObject(handle, PID_ES); }};
-  ret = iosc.ImportCertificate(ca_cert.GetBytes().data(), IOSC::HANDLE_ROOT_KEY, handle, PID_ES);
+  ret = iosc.ImportCertificate(ca_cert, IOSC::HANDLE_ROOT_KEY, handle, PID_ES);
   if (ret != IPC_SUCCESS)
   {
     ERROR_LOG(IOS_ES, "VerifyContainer: IOSC_ImportCertificate(ca) failed with error %d", ret);
@@ -941,7 +941,7 @@ ReturnCode ES::VerifyContainer(VerifyContainerType type, VerifyMode mode,
   if (ret != IPC_SUCCESS)
     return ret;
   Common::ScopeGuard issuer_guard{[&] { iosc.DeleteObject(issuer_handle, PID_ES); }};
-  ret = iosc.ImportCertificate(issuer_cert.GetBytes().data(), handle, issuer_handle, PID_ES);
+  ret = iosc.ImportCertificate(issuer_cert, handle, issuer_handle, PID_ES);
   if (ret != IPC_SUCCESS)
   {
     ERROR_LOG(IOS_ES, "VerifyContainer: IOSC_ImportCertificate(issuer) failed with error %d", ret);
@@ -950,7 +950,7 @@ ReturnCode ES::VerifyContainer(VerifyContainerType type, VerifyMode mode,
 
   // Verify the signature.
   const std::vector<u8> signature = signed_blob.GetSignatureData();
-  ret = iosc.VerifyPublicKeySign(signed_blob.GetSha1(), issuer_handle, signature.data(), PID_ES);
+  ret = iosc.VerifyPublicKeySign(signed_blob.GetSha1(), issuer_handle, signature, PID_ES);
   if (ret != IPC_SUCCESS)
   {
     ERROR_LOG(IOS_ES, "VerifyContainer: IOSC_VerifyPublicKeySign failed with error %d", ret);
@@ -968,15 +968,29 @@ ReturnCode ES::VerifyContainer(VerifyContainerType type, VerifyMode mode,
       ERROR_LOG(IOS_ES, "VerifyContainer: Writing the CA cert failed with return code %d", ret);
   }
 
-  // Import the signed blob to iosc_handle (if a handle was passed to us).
-  if (ret == IPC_SUCCESS && iosc_handle)
+  if (ret == IPC_SUCCESS && issuer_handle_out)
   {
-    ret = iosc.ImportCertificate(signed_blob.GetBytes().data(), issuer_handle, iosc_handle, PID_ES);
-    ERROR_LOG(IOS_ES, "VerifyContainer: IOSC_ImportCertificate(final) failed with error %d", ret);
+    *issuer_handle_out = issuer_handle;
+    issuer_guard.Dismiss();
   }
 
   return ret;
 }
+
+ReturnCode ES::VerifyContainer(VerifyContainerType type, VerifyMode mode,
+                               const IOS::ES::CertReader& cert, const std::vector<u8>& cert_chain,
+                               u32 certificate_iosc_handle)
+{
+  IOSC::Handle issuer_handle;
+  ReturnCode ret = VerifyContainer(type, mode, cert, cert_chain, &issuer_handle);
+  // Import the signed blob.
+  if (ret == IPC_SUCCESS)
+  {
+    ret = m_ios.GetIOSC().ImportCertificate(cert, issuer_handle, certificate_iosc_handle, PID_ES);
+    m_ios.GetIOSC().DeleteObject(issuer_handle, PID_ES);
+  }
+  return ret;
+}
 }  // namespace Device
 }  // namespace HLE
 }  // namespace IOS
diff --git a/Source/Core/Core/IOS/ES/ES.h b/Source/Core/Core/IOS/ES/ES.h
index 9e31ae83b7..dd1050c6e6 100644
--- a/Source/Core/Core/IOS/ES/ES.h
+++ b/Source/Core/Core/IOS/ES/ES.h
@@ -323,9 +323,14 @@ private:
   bool IsIssuerCorrect(VerifyContainerType type, const IOS::ES::CertReader& issuer_cert) const;
   ReturnCode ReadCertStore(std::vector<u8>* buffer) const;
   ReturnCode WriteNewCertToStore(const IOS::ES::CertReader& cert);
+  // On success, if issuer_handle is non-null, the IOSC object for the issuer will be written to it.
+  // The caller is responsible for using IOSC_DeleteObject.
   ReturnCode VerifyContainer(VerifyContainerType type, VerifyMode mode,
                              const IOS::ES::SignedBlobReader& signed_blob,
-                             const std::vector<u8>& cert_chain, u32 iosc_handle = 0);
+                             const std::vector<u8>& cert_chain, u32* issuer_handle = nullptr);
+  ReturnCode VerifyContainer(VerifyContainerType type, VerifyMode mode,
+                             const IOS::ES::CertReader& certificate,
+                             const std::vector<u8>& cert_chain, u32 certificate_iosc_handle);
 
   // Start a title import.
   bool InitImport(const IOS::ES::TMDReader& tmd);
diff --git a/Source/Core/Core/IOS/IOSC.cpp b/Source/Core/Core/IOS/IOSC.cpp
index 5df9f49672..c2bbb292f3 100644
--- a/Source/Core/Core/IOS/IOSC.cpp
+++ b/Source/Core/Core/IOS/IOSC.cpp
@@ -27,6 +27,7 @@
 #include "Common/StringUtil.h"
 #include "Common/Swap.h"
 #include "Core/IOS/Device.h"
+#include "Core/IOS/ES/Formats.h"
 
 namespace
 {
@@ -295,7 +296,7 @@ ReturnCode IOSC::Decrypt(Handle key_handle, u8* iv, const u8* input, size_t size
 }
 
 ReturnCode IOSC::VerifyPublicKeySign(const std::array<u8, 20>& sha1, Handle signer_handle,
-                                     const u8* signature, u32 pid) const
+                                     const std::vector<u8>& signature, u32 pid) const
 {
   if (!HasOwnership(signer_handle, pid))
     return IOSC_EACCES;
@@ -325,7 +326,7 @@ ReturnCode IOSC::VerifyPublicKeySign(const std::array<u8, 20>& sha1, Handle sign
     rsa.len = entry->data.size();
 
     const int ret = mbedtls_rsa_pkcs1_verify(&rsa, nullptr, nullptr, MBEDTLS_RSA_PUBLIC,
-                                             MBEDTLS_MD_SHA1, 0, sha1.data(), signature);
+                                             MBEDTLS_MD_SHA1, 0, sha1.data(), signature.data());
     if (ret != 0)
     {
       WARN_LOG(IOS, "VerifyPublicKeySign: RSA verification failed (error %d)", ret);
@@ -342,53 +343,8 @@ ReturnCode IOSC::VerifyPublicKeySign(const std::array<u8, 20>& sha1, Handle sign
   }
 }
 
-struct ImportCertParameters
-{
-  size_t offset;
-  size_t size;
-  size_t signature_offset;
-  size_t public_key_offset;
-  size_t public_key_exponent_offset;
-};
-
-static ReturnCode GetImportCertParameters(const u8* cert, ImportCertParameters* parameters)
-{
-  // TODO: Add support for ECC signature type.
-  const u32 signature_type = Common::swap32(cert + offsetof(Cert, type));
-  switch (static_cast<SignatureType>(signature_type))
-  {
-  case SignatureType::RSA2048:
-  {
-    const u32 key_type = Common::swap32(cert + offsetof(Cert, rsa2048.header.public_key_type));
-
-    // TODO: Add support for ECC public key type.
-    if (static_cast<PublicKeyType>(key_type) != PublicKeyType::RSA2048)
-      return IOSC_INVALID_FORMAT;
-
-    parameters->offset = offsetof(Cert, rsa2048.signature.issuer);
-    parameters->size = sizeof(Cert::rsa2048) - parameters->offset;
-    parameters->signature_offset = offsetof(Cert, rsa2048.signature.sig);
-    parameters->public_key_offset = offsetof(Cert, rsa2048.public_key);
-    parameters->public_key_exponent_offset = offsetof(Cert, rsa2048.exponent);
-    return IPC_SUCCESS;
-  }
-  case SignatureType::RSA4096:
-  {
-    parameters->offset = offsetof(Cert, rsa4096.signature.issuer);
-    parameters->size = sizeof(Cert::rsa4096) - parameters->offset;
-    parameters->signature_offset = offsetof(Cert, rsa4096.signature.sig);
-    parameters->public_key_offset = offsetof(Cert, rsa4096.public_key);
-    parameters->public_key_exponent_offset = offsetof(Cert, rsa4096.exponent);
-    return IPC_SUCCESS;
-  }
-  default:
-    WARN_LOG(IOS, "Unknown signature type: %08x", signature_type);
-    return IOSC_INVALID_FORMAT;
-  }
-}
-
-ReturnCode IOSC::ImportCertificate(const u8* cert, Handle signer_handle, Handle dest_handle,
-                                   u32 pid)
+ReturnCode IOSC::ImportCertificate(const IOS::ES::CertReader& cert, Handle signer_handle,
+                                   Handle dest_handle, u32 pid)
 {
   if (!HasOwnership(signer_handle, pid) || !HasOwnership(dest_handle, pid))
     return IOSC_EACCES;
@@ -401,22 +357,17 @@ ReturnCode IOSC::ImportCertificate(const u8* cert, Handle signer_handle, Handle
   if (signer_entry->type != TYPE_PUBLIC_KEY || dest_entry->type != TYPE_PUBLIC_KEY)
     return IOSC_INVALID_OBJTYPE;
 
-  ImportCertParameters parameters;
-  const ReturnCode ret = GetImportCertParameters(cert, &parameters);
-  if (ret != IPC_SUCCESS)
-    return ret;
+  if (!cert.IsValid())
+    return IOSC_INVALID_FORMAT;
 
-  std::array<u8, 20> sha1;
-  mbedtls_sha1(cert + parameters.offset, parameters.size, sha1.data());
-
-  if (VerifyPublicKeySign(sha1, signer_handle, cert + parameters.signature_offset, pid) !=
-      IPC_SUCCESS)
-  {
+  const std::vector<u8> signature = cert.GetSignatureData();
+  if (VerifyPublicKeySign(cert.GetSha1(), signer_handle, signature, pid) != IPC_SUCCESS)
     return IOSC_FAIL_CHECKVALUE;
-  }
 
-  return ImportPublicKey(dest_handle, cert + parameters.public_key_offset,
-                         cert + parameters.public_key_exponent_offset, pid);
+  const std::vector<u8> public_key = cert.GetPublicKey();
+  const bool is_rsa = cert.GetSignatureType() != SignatureType::ECC;
+  const u8* exponent = is_rsa ? (public_key.data() + public_key.size() - 4) : nullptr;
+  return ImportPublicKey(dest_handle, public_key.data(), exponent, pid);
 }
 
 ReturnCode IOSC::GetOwnership(Handle handle, u32* owner) const
diff --git a/Source/Core/Core/IOS/IOSC.h b/Source/Core/Core/IOS/IOSC.h
index ce56921937..c397b85a5c 100644
--- a/Source/Core/Core/IOS/IOSC.h
+++ b/Source/Core/Core/IOS/IOSC.h
@@ -18,6 +18,11 @@ class PointerWrap;
 
 namespace IOS
 {
+namespace ES
+{
+class CertReader;
+}  // namespace ES
+
 enum class SignatureType : u32
 {
   RSA4096 = 0x00010000,
@@ -192,9 +197,10 @@ public:
                      u32 pid) const;
 
   ReturnCode VerifyPublicKeySign(const std::array<u8, 20>& sha1, Handle signer_handle,
-                                 const u8* signature, u32 pid) const;
+                                 const std::vector<u8>& signature, u32 pid) const;
   // Import a certificate (signed by the certificate in signer_handle) into dest_handle.
-  ReturnCode ImportCertificate(const u8* cert, Handle signer_handle, Handle dest_handle, u32 pid);
+  ReturnCode ImportCertificate(const IOS::ES::CertReader& cert, Handle signer_handle,
+                               Handle dest_handle, u32 pid);
 
   // Ownership
   ReturnCode GetOwnership(Handle handle, u32* owner) const;

From 33c5fd6f5ade4e9bac2af1c2ca5755da1b0bc705 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?L=C3=A9o=20Lam?= <leo@innovatetechnologi.es>
Date: Fri, 18 May 2018 22:02:55 +0200
Subject: [PATCH 3/3] IOSC: Verify that RSA signature size is correct

---
 Source/Core/Core/IOS/IOSC.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Source/Core/Core/IOS/IOSC.cpp b/Source/Core/Core/IOS/IOSC.cpp
index c2bbb292f3..7f47421ca8 100644
--- a/Source/Core/Core/IOS/IOSC.cpp
+++ b/Source/Core/Core/IOS/IOSC.cpp
@@ -316,6 +316,7 @@ ReturnCode IOSC::VerifyPublicKeySign(const std::array<u8, 20>& sha1, Handle sign
   {
     const size_t expected_key_size = entry->subtype == SUBTYPE_RSA2048 ? 0x100 : 0x200;
     ASSERT(entry->data.size() == expected_key_size);
+    ASSERT(signature.size() == expected_key_size);
 
     mbedtls_rsa_context rsa;
     mbedtls_rsa_init(&rsa, MBEDTLS_RSA_PKCS_V15, 0);