diff --git a/Source/Core/Core/IOS/IOSC.cpp b/Source/Core/Core/IOS/IOSC.cpp index e4bd241f0a..17dbe4471d 100644 --- a/Source/Core/Core/IOS/IOSC.cpp +++ b/Source/Core/Core/IOS/IOSC.cpp @@ -19,6 +19,7 @@ #include "Common/Crypto/AES.h" #include "Common/Crypto/ec.h" #include "Common/ScopeGuard.h" +#include "Common/Swap.h" #include "Core/IOS/Device.h" #include "Core/IOS/IOSC.h" #include "Core/ec_wii.h" @@ -243,6 +244,83 @@ 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) +{ + if (!HasOwnership(signer_handle, pid) || !HasOwnership(dest_handle, pid)) + return IOSC_EACCES; + + const KeyEntry* signer_entry = FindEntry(signer_handle, SearchMode::IncludeRootKey); + const KeyEntry* dest_entry = FindEntry(dest_handle, SearchMode::IncludeRootKey); + if (!signer_entry || !dest_entry) + return IOSC_EINVAL; + + 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; + + std::array sha1; + mbedtls_sha1(cert + parameters.offset, parameters.size, sha1.data()); + + if (VerifyPublicKeySign(sha1, signer_handle, cert + parameters.signature_offset, pid) != + IPC_SUCCESS) + { + return IOSC_FAIL_CHECKVALUE; + } + + return ImportPublicKey(dest_handle, cert + parameters.public_key_offset, + cert + parameters.public_key_exponent_offset, pid); +} + ReturnCode IOSC::GetOwnership(Handle handle, u32* owner) const { const KeyEntry* entry = FindEntry(handle); diff --git a/Source/Core/Core/IOS/IOSC.h b/Source/Core/Core/IOS/IOSC.h index 17a43e9b5f..653b7a3a01 100644 --- a/Source/Core/Core/IOS/IOSC.h +++ b/Source/Core/Core/IOS/IOSC.h @@ -187,6 +187,8 @@ public: ReturnCode VerifyPublicKeySign(const std::array& sha1, Handle signer_handle, const 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); // Ownership ReturnCode GetOwnership(Handle handle, u32* owner) const;