diff --git a/Source/Core/Core/IOS/ES/ES.cpp b/Source/Core/Core/IOS/ES/ES.cpp index 0743dc7efa..e75bdfb77d 100644 --- a/Source/Core/Core/IOS/ES/ES.cpp +++ b/Source/Core/Core/IOS/ES/ES.cpp @@ -11,8 +11,6 @@ #include #include -#include - #include "Common/ChunkFile.h" #include "Common/Logging/Log.h" #include "Common/MsgHandler.h" @@ -890,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& cert_chain, u32 iosc_handle) + const std::vector& cert_chain, u32* issuer_handle_out) { if (!SConfig::GetInstance().m_enable_signature_checks) return IPC_SUCCESS; @@ -929,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); @@ -943,23 +941,16 @@ 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); return ret; } - // Calculate the SHA1 of the signed blob. - const size_t skip = type == VerifyContainerType::Device ? offsetof(SignatureECC, issuer) : - offsetof(SignatureRSA2048, issuer); - std::array sha1; - mbedtls_sha1(signed_blob.GetBytes().data() + skip, signed_blob.GetBytes().size() - skip, - sha1.data()); - // Verify the signature. const std::vector signature = signed_blob.GetSignatureData(); - ret = iosc.VerifyPublicKeySign(sha1, 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); @@ -977,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& 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* 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& cert_chain, u32 iosc_handle = 0); + const std::vector& cert_chain, u32* issuer_handle = nullptr); + ReturnCode VerifyContainer(VerifyContainerType type, VerifyMode mode, + const IOS::ES::CertReader& certificate, + const std::vector& cert_chain, u32 certificate_iosc_handle); // Start a title import. bool InitImport(const IOS::ES::TMDReader& tmd); 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 #include +#include + #include "Common/Assert.h" #include "Common/ChunkFile.h" #include "Common/CommonTypes.h" @@ -102,6 +104,29 @@ void SignedBlobReader::SetBytes(std::vector&& 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 SignedBlobReader::GetSha1() const +{ + std::array 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& bytes); void SetBytes(std::vector&& bytes); + /// Get the SHA1 hash for this signed blob (starting at the issuer). + std::array GetSha1() const; + // Only checks whether the signature data could be parsed. The signature is not verified. bool IsSignatureValid() const; diff --git a/Source/Core/Core/IOS/IOSC.cpp b/Source/Core/Core/IOS/IOSC.cpp index 5df9f49672..7f47421ca8 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& sha1, Handle signer_handle, - const u8* signature, u32 pid) const + const std::vector& signature, u32 pid) const { if (!HasOwnership(signer_handle, pid)) return IOSC_EACCES; @@ -315,6 +316,7 @@ ReturnCode IOSC::VerifyPublicKeySign(const std::array& 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); @@ -325,7 +327,7 @@ ReturnCode IOSC::VerifyPublicKeySign(const std::array& 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 +344,8 @@ ReturnCode IOSC::VerifyPublicKeySign(const std::array& 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(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(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 +358,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, ¶meters); - if (ret != IPC_SUCCESS) - return ret; + if (!cert.IsValid()) + return IOSC_INVALID_FORMAT; - std::array 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 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 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& sha1, Handle signer_handle, - const u8* signature, u32 pid) const; + const std::vector& 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;