mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2024-11-15 05:47:56 -07:00
Merge pull request #6898 from leoetlino/certreader
ES/Formats: Deduplicate signed blob reading code
This commit is contained in:
commit
fc525bdf8f
@ -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"
|
||||
@ -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<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;
|
||||
@ -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<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, 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<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
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
@ -315,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);
|
||||
@ -325,7 +327,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 +344,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 +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<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
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user