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, ¶meters); - 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);