summaryrefslogtreecommitdiff
path: root/Source/Core
diff options
context:
space:
mode:
authorLéo Lam <leo@innovatetechnologi.es>2018-05-20 23:11:49 +0200
committerGitHub <noreply@github.com>2018-05-20 23:11:49 +0200
commit3d8e63fffd129ae2a40983d3ac82663698329e37 (patch)
tree7c5b6df0d8672b5fc2f4b3366f1621425e83a6bd /Source/Core
parent79edd57b96efa7a246671646bcf2d9c41e057d7c (diff)
parent4b0f8d9f853499c6d7604168d02baa7205dda460 (diff)
Merge pull request #6914 from leoetlino/verifysign
ES: Implement VerifySign (last remaining unimplemented, actually used ioctlv)
Diffstat (limited to 'Source/Core')
-rw-r--r--Source/Core/Common/Crypto/ec.cpp6
-rw-r--r--Source/Core/Common/Crypto/ec.h7
-rw-r--r--Source/Core/Core/IOS/ES/ES.cpp3
-rw-r--r--Source/Core/Core/IOS/ES/ES.h4
-rw-r--r--Source/Core/Core/IOS/ES/Formats.cpp102
-rw-r--r--Source/Core/Core/IOS/ES/Identity.cpp99
-rw-r--r--Source/Core/Core/IOS/IOSC.cpp8
-rw-r--r--Source/Core/Core/IOS/IOSC.h39
8 files changed, 205 insertions, 63 deletions
diff --git a/Source/Core/Common/Crypto/ec.cpp b/Source/Core/Common/Crypto/ec.cpp
index 9906ccd5e9..b9a0ca6491 100644
--- a/Source/Core/Common/Crypto/ec.cpp
+++ b/Source/Core/Common/Crypto/ec.cpp
@@ -278,8 +278,10 @@ std::array<u8, 60> Sign(const u8* key, const u8* hash)
return signature;
}
-UNUSED static int check_ecdsa(u8* Q, u8* R, u8* S, const u8* hash)
+bool VerifySignature(const u8* public_key, const u8* signature, const u8* hash)
{
+ const u8* R = signature;
+ const u8* S = signature + 30;
u8 Sinv[30];
bn_inv(Sinv, S, ec_N, 30);
@@ -290,7 +292,7 @@ UNUSED static int check_ecdsa(u8* Q, u8* R, u8* S, const u8* hash)
bn_mul(w1, e, Sinv, ec_N, 30);
bn_mul(w2, R, Sinv, ec_N, 30);
- Point r1 = w1 * ec_G + w2 * Point{Q};
+ Point r1 = w1 * ec_G + w2 * Point{public_key};
auto& rx = r1.X().data;
if (bn_compare(rx.data(), ec_N, 30) >= 0)
bn_sub_modulus(rx.data(), ec_N, 30);
diff --git a/Source/Core/Common/Crypto/ec.h b/Source/Core/Common/Crypto/ec.h
index 18b0d48d98..442bbec311 100644
--- a/Source/Core/Common/Crypto/ec.h
+++ b/Source/Core/Common/Crypto/ec.h
@@ -13,6 +13,13 @@ namespace Common::ec
/// Generate a signature using ECDSA.
std::array<u8, 60> Sign(const u8* key, const u8* hash);
+/// Check a signature using ECDSA.
+///
+/// @param public_key 30 byte ECC public key
+/// @param signature 60 byte signature
+/// @param hash Message hash
+bool VerifySignature(const u8* public_key, const u8* signature, const u8* hash);
+
/// Compute a shared secret from a private key (30 bytes) and public key (60 bytes).
std::array<u8, 60> ComputeSharedSecret(const u8* private_key, const u8* public_key);
diff --git a/Source/Core/Core/IOS/ES/ES.cpp b/Source/Core/Core/IOS/ES/ES.cpp
index e75bdfb77d..227f6d78fd 100644
--- a/Source/Core/Core/IOS/ES/ES.cpp
+++ b/Source/Core/Core/IOS/ES/ES.cpp
@@ -524,6 +524,8 @@ IPCCommandResult ES::IOCtlV(const IOCtlVRequest& request)
return GetDeviceCertificate(request);
case IOCTL_ES_SIGN:
return Sign(request);
+ case IOCTL_ES_VERIFYSIGN:
+ return VerifySign(request);
case IOCTL_ES_GETBOOT2VERSION:
return GetBoot2Version(request);
@@ -539,7 +541,6 @@ IPCCommandResult ES::IOCtlV(const IOCtlVRequest& request)
case IOCTL_ES_DELETE_STREAM_KEY:
return DeleteStreamKey(request);
- case IOCTL_ES_VERIFYSIGN:
case IOCTL_ES_UNKNOWN_41:
case IOCTL_ES_UNKNOWN_42:
PanicAlert("IOS-ES: Unimplemented ioctlv 0x%x (%zu in vectors, %zu io vectors)",
diff --git a/Source/Core/Core/IOS/ES/ES.h b/Source/Core/Core/IOS/ES/ES.h
index dd1050c6e6..174d3836e7 100644
--- a/Source/Core/Core/IOS/ES/ES.h
+++ b/Source/Core/Core/IOS/ES/ES.h
@@ -132,6 +132,9 @@ public:
ReturnCode GetDeviceId(u32* device_id) const;
ReturnCode GetTitleId(u64* device_id) const;
+ ReturnCode VerifySign(const std::vector<u8>& hash, const std::vector<u8>& ecc_signature,
+ const std::vector<u8>& certs);
+
// Views
ReturnCode GetV0TicketFromView(const u8* ticket_view, u8* ticket) const;
ReturnCode GetTicketFromView(const u8* ticket_view, u8* ticket, u32* ticket_size) const;
@@ -243,6 +246,7 @@ private:
IPCCommandResult GetDeviceCertificate(const IOCtlVRequest& request);
IPCCommandResult CheckKoreaRegion(const IOCtlVRequest& request);
IPCCommandResult Sign(const IOCtlVRequest& request);
+ IPCCommandResult VerifySign(const IOCtlVRequest& request);
IPCCommandResult Encrypt(u32 uid, const IOCtlVRequest& request);
IPCCommandResult Decrypt(u32 uid, const IOCtlVRequest& request);
diff --git a/Source/Core/Core/IOS/ES/Formats.cpp b/Source/Core/Core/IOS/ES/Formats.cpp
index 6039cacb8c..2af15c0c70 100644
--- a/Source/Core/Core/IOS/ES/Formats.cpp
+++ b/Source/Core/Core/IOS/ES/Formats.cpp
@@ -130,7 +130,7 @@ std::array<u8, 20> SignedBlobReader::GetSha1() const
bool SignedBlobReader::IsSignatureValid() const
{
// Too small for the certificate type.
- if (m_bytes.size() < sizeof(Cert::type))
+ if (m_bytes.size() < sizeof(SignatureType))
return false;
// Too small to contain the whole signature data.
@@ -146,20 +146,23 @@ SignatureType SignedBlobReader::GetSignatureType() const
return static_cast<SignatureType>(Common::swap32(m_bytes.data()));
}
+template <typename T, typename It>
+static std::vector<u8> DetailGetSignatureData(It begin)
+{
+ const auto signature_begin = begin + offsetof(T, sig);
+ return std::vector<u8>(signature_begin, signature_begin + sizeof(T::sig));
+}
+
std::vector<u8> SignedBlobReader::GetSignatureData() const
{
switch (GetSignatureType())
{
case SignatureType::RSA4096:
- {
- const auto signature_begin = m_bytes.begin() + offsetof(SignatureRSA4096, sig);
- return std::vector<u8>(signature_begin, signature_begin + sizeof(SignatureRSA4096::sig));
- }
+ return DetailGetSignatureData<SignatureRSA4096>(m_bytes.cbegin());
case SignatureType::RSA2048:
- {
- const auto signature_begin = m_bytes.begin() + offsetof(SignatureRSA2048, sig);
- return std::vector<u8>(signature_begin, signature_begin + sizeof(SignatureRSA2048::sig));
- }
+ return DetailGetSignatureData<SignatureRSA2048>(m_bytes.cbegin());
+ case SignatureType::ECC:
+ return DetailGetSignatureData<SignatureECC>(m_bytes.cbegin());
default:
return {};
}
@@ -173,27 +176,30 @@ size_t SignedBlobReader::GetSignatureSize() const
return sizeof(SignatureRSA4096);
case SignatureType::RSA2048:
return sizeof(SignatureRSA2048);
+ case SignatureType::ECC:
+ return sizeof(SignatureECC);
default:
return 0;
}
}
+template <typename T>
+static std::string DetailGetIssuer(const u8* bytes)
+{
+ const char* issuer = reinterpret_cast<const char*>(bytes + offsetof(T, issuer));
+ return {issuer, strnlen(issuer, sizeof(T::issuer))};
+}
+
std::string SignedBlobReader::GetIssuer() const
{
switch (GetSignatureType())
{
case SignatureType::RSA4096:
- {
- const char* issuer =
- reinterpret_cast<const char*>(m_bytes.data() + offsetof(SignatureRSA4096, issuer));
- return std::string(issuer, strnlen(issuer, sizeof(SignatureRSA4096::issuer)));
- }
+ return DetailGetIssuer<SignatureRSA4096>(m_bytes.data());
case SignatureType::RSA2048:
- {
- const char* issuer =
- reinterpret_cast<const char*>(m_bytes.data() + offsetof(SignatureRSA2048, issuer));
- return std::string(issuer, strnlen(issuer, sizeof(SignatureRSA2048::issuer)));
- }
+ return DetailGetIssuer<SignatureRSA2048>(m_bytes.data());
+ case SignatureType::ECC:
+ return DetailGetIssuer<SignatureECC>(m_bytes.data());
default:
return "";
}
@@ -677,24 +683,22 @@ CertReader::CertReader(std::vector<u8>&& bytes) : SignedBlobReader(std::move(byt
if (!IsSignatureValid())
return;
- switch (GetSignatureType())
- {
- case SignatureType::RSA4096:
- if (m_bytes.size() < sizeof(CertRSA4096))
- return;
- m_bytes.resize(sizeof(CertRSA4096));
- break;
+ static constexpr std::array<std::tuple<SignatureType, PublicKeyType, size_t>, 4> types{{
+ {SignatureType::RSA4096, PublicKeyType::RSA2048, sizeof(CertRSA4096RSA2048)},
+ {SignatureType::RSA2048, PublicKeyType::RSA2048, sizeof(CertRSA2048RSA2048)},
+ {SignatureType::RSA2048, PublicKeyType::ECC, sizeof(CertRSA2048ECC)},
+ {SignatureType::ECC, PublicKeyType::ECC, sizeof(CertECC)},
+ }};
- case SignatureType::RSA2048:
- if (m_bytes.size() < sizeof(CertRSA2048))
- return;
- m_bytes.resize(sizeof(CertRSA2048));
- break;
+ const auto info = std::find_if(types.cbegin(), types.cend(), [this](const auto& entry) {
+ return m_bytes.size() >= std::get<2>(entry) && std::get<0>(entry) == GetSignatureType() &&
+ std::get<1>(entry) == GetPublicKeyType();
+ });
- default:
+ if (info == types.cend())
return;
- }
+ m_bytes.resize(std::get<2>(*info));
m_is_valid = true;
}
@@ -722,20 +726,28 @@ PublicKeyType CertReader::GetPublicKeyType() const
return static_cast<PublicKeyType>(Common::swap32(m_bytes.data() + offset));
}
-std::vector<u8> CertReader::GetPublicKey() const
+template <typename T, typename It>
+std::vector<u8> DetailGetPublicKey(It begin, size_t extra_data = 0)
{
- static const std::map<SignatureType, std::pair<size_t, size_t>> type_to_key_info = {{
- {SignatureType::RSA4096,
- {offsetof(CertRSA4096, public_key),
- sizeof(CertRSA4096::public_key) + sizeof(CertRSA4096::exponent)}},
- {SignatureType::RSA2048,
- {offsetof(CertRSA2048, public_key),
- sizeof(CertRSA2048::public_key) + sizeof(CertRSA2048::exponent)}},
- }};
+ const auto key_begin = begin + offsetof(T, public_key);
+ return {key_begin, key_begin + sizeof(T::public_key) + extra_data};
+}
- const auto info = type_to_key_info.at(GetSignatureType());
- const auto key_begin = m_bytes.begin() + info.first;
- return std::vector<u8>(key_begin, key_begin + info.second);
+std::vector<u8> CertReader::GetPublicKey() const
+{
+ switch (GetSignatureType())
+ {
+ case SignatureType::RSA4096:
+ return DetailGetPublicKey<CertRSA4096RSA2048>(m_bytes.begin(), 4);
+ case SignatureType::RSA2048:
+ if (GetPublicKeyType() == PublicKeyType::RSA2048)
+ return DetailGetPublicKey<CertRSA2048RSA2048>(m_bytes.begin(), 4);
+ return DetailGetPublicKey<CertRSA2048ECC>(m_bytes.begin());
+ case SignatureType::ECC:
+ return DetailGetPublicKey<CertECC>(m_bytes.begin());
+ default:
+ return {};
+ }
}
std::map<std::string, CertReader> ParseCertChain(const std::vector<u8>& chain)
diff --git a/Source/Core/Core/IOS/ES/Identity.cpp b/Source/Core/Core/IOS/ES/Identity.cpp
index 134ccdc60b..87ff012cb6 100644
--- a/Source/Core/Core/IOS/ES/Identity.cpp
+++ b/Source/Core/Core/IOS/ES/Identity.cpp
@@ -4,10 +4,14 @@
#include "Core/IOS/ES/ES.h"
-#include <cstring>
#include <vector>
+#include <mbedtls/sha1.h>
+
#include "Common/Logging/Log.h"
+#include "Common/ScopeGuard.h"
+#include "Common/StringUtil.h"
+#include "Core/ConfigManager.h"
#include "Core/HW/Memmap.h"
#include "Core/IOS/ES/Formats.h"
#include "Core/IOS/Uids.h"
@@ -114,6 +118,99 @@ IPCCommandResult ES::Sign(const IOCtlVRequest& request)
m_ios.GetIOSC().Sign(sig_out, ap_cert_out, m_title_context.tmd.GetTitleId(), data, data_size);
return GetDefaultReply(IPC_SUCCESS);
}
+
+ReturnCode ES::VerifySign(const std::vector<u8>& hash, const std::vector<u8>& ecc_signature,
+ const std::vector<u8>& certs_bytes)
+{
+ if (!SConfig::GetInstance().m_enable_signature_checks)
+ {
+ WARN_LOG(IOS_ES, "VerifySign: signature checks are disabled. Skipping.");
+ return IPC_SUCCESS;
+ }
+
+ const std::map<std::string, IOS::ES::CertReader> certs = IOS::ES::ParseCertChain(certs_bytes);
+ if (certs.empty())
+ return ES_EINVAL;
+
+ const auto ap_iterator = std::find_if(certs.begin(), certs.end(), [](const auto& entry) {
+ return entry.first.length() > 2 && entry.first.compare(0, 2, "AP") == 0;
+ });
+ if (ap_iterator == certs.end())
+ return ES_UNKNOWN_ISSUER;
+ const IOS::ES::CertReader& ap = ap_iterator->second;
+
+ const auto ap_issuers = SplitString(ap.GetIssuer(), '-');
+ const auto ng_iterator = ap_issuers.size() > 1 ? certs.find(*ap_issuers.rbegin()) : certs.end();
+ if (ng_iterator == certs.end())
+ return ES_UNKNOWN_ISSUER;
+ const IOS::ES::CertReader& ng = ng_iterator->second;
+
+ IOSC& iosc = m_ios.GetIOSC();
+ IOSC::Handle ng_cert;
+ ReturnCode ret = iosc.CreateObject(&ng_cert, IOSC::TYPE_PUBLIC_KEY, IOSC::SUBTYPE_ECC233, PID_ES);
+ if (ret != IPC_SUCCESS)
+ return ret;
+ Common::ScopeGuard handle_guard{[&] { iosc.DeleteObject(ng_cert, PID_ES); }};
+
+ ret = VerifyContainer(VerifyContainerType::Device, VerifyMode::DoNotUpdateCertStore, ng,
+ certs_bytes, ng_cert);
+ if (ret != IPC_SUCCESS)
+ {
+ ERROR_LOG(IOS_ES, "VerifySign: VerifyContainer(ng) failed with error %d", ret);
+ return ret;
+ }
+
+ ret = iosc.VerifyPublicKeySign(ap.GetSha1(), ng_cert, ap.GetSignatureData(), PID_ES);
+ if (ret != IPC_SUCCESS)
+ {
+ ERROR_LOG(IOS_ES, "VerifySign: IOSC_VerifyPublicKeySign(ap) failed with error %d", ret);
+ return ret;
+ }
+
+ IOSC::Handle ap_cert;
+ ret = iosc.CreateObject(&ap_cert, IOSC::TYPE_PUBLIC_KEY, IOSC::SUBTYPE_ECC233, PID_ES);
+ if (ret != IPC_SUCCESS)
+ return ret;
+ Common::ScopeGuard handle2_guard{[&] { iosc.DeleteObject(ap_cert, PID_ES); }};
+
+ ret = iosc.ImportPublicKey(ap_cert, ap.GetPublicKey().data(), nullptr, PID_ES);
+ if (ret != IPC_SUCCESS)
+ {
+ ERROR_LOG(IOS_ES, "VerifySign: IOSC_ImportPublicKey(ap) failed with error %d", ret);
+ return ret;
+ }
+
+ std::array<u8, 20> sha1;
+ mbedtls_sha1(hash.data(), hash.size(), sha1.data());
+ ret = iosc.VerifyPublicKeySign(sha1, ap_cert, ecc_signature, PID_ES);
+ if (ret != IPC_SUCCESS)
+ {
+ ERROR_LOG(IOS_ES, "VerifySign: IOSC_VerifyPublicKeySign(data) failed with error %d", ret);
+ return ret;
+ }
+
+ INFO_LOG(IOS_ES, "VerifySign: all checks passed");
+ return IPC_SUCCESS;
+}
+
+IPCCommandResult ES::VerifySign(const IOCtlVRequest& request)
+{
+ if (!request.HasNumberOfValidVectors(3, 0))
+ return GetDefaultReply(ES_EINVAL);
+ if (request.in_vectors[1].size != sizeof(IOS::ECCSignature))
+ return GetDefaultReply(ES_EINVAL);
+
+ std::vector<u8> hash(request.in_vectors[0].size);
+ Memory::CopyFromEmu(hash.data(), request.in_vectors[0].address, hash.size());
+
+ std::vector<u8> ecc_signature(request.in_vectors[1].size);
+ Memory::CopyFromEmu(ecc_signature.data(), request.in_vectors[1].address, ecc_signature.size());
+
+ std::vector<u8> certs(request.in_vectors[2].size);
+ Memory::CopyFromEmu(certs.data(), request.in_vectors[2].address, certs.size());
+
+ return GetDefaultReply(VerifySign(hash, ecc_signature, certs));
+}
} // namespace Device
} // namespace HLE
} // namespace IOS
diff --git a/Source/Core/Core/IOS/IOSC.cpp b/Source/Core/Core/IOS/IOSC.cpp
index 569568395d..7d791b3f9d 100644
--- a/Source/Core/Core/IOS/IOSC.cpp
+++ b/Source/Core/Core/IOS/IOSC.cpp
@@ -337,8 +337,12 @@ ReturnCode IOSC::VerifyPublicKeySign(const std::array<u8, 20>& sha1, Handle sign
return IPC_SUCCESS;
}
case SUBTYPE_ECC233:
- ERROR_LOG(IOS, "VerifyPublicKeySign: SUBTYPE_ECC233 is unimplemented");
- // [[fallthrough]]
+ {
+ ASSERT(entry->data.size() == sizeof(CertECC::public_key));
+
+ const bool ok = Common::ec::VerifySignature(entry->data.data(), signature.data(), sha1.data());
+ return ok ? IPC_SUCCESS : IOSC_FAIL_CHECKVALUE;
+ }
default:
return IOSC_INVALID_OBJTYPE;
}
diff --git a/Source/Core/Core/IOS/IOSC.h b/Source/Core/Core/IOS/IOSC.h
index c397b85a5c..5207f4c9f0 100644
--- a/Source/Core/Core/IOS/IOSC.h
+++ b/Source/Core/Core/IOS/IOSC.h
@@ -34,6 +34,7 @@ enum class PublicKeyType : u32
{
RSA4096 = 0,
RSA2048 = 1,
+ ECC = 2,
};
#pragma pack(push, 4)
@@ -72,34 +73,48 @@ struct CertHeader
u32 id;
};
-struct CertRSA4096
+using RSA2048PublicKey = std::array<u8, 0x100>;
+using ECCPublicKey = std::array<u8, 60>;
+
+struct CertRSA4096RSA2048
{
SignatureRSA4096 signature;
CertHeader header;
- // The signature is RSA4096, but the key is a RSA2048 public key,
- // so its size is 0x100, not 0x200, as one would expect from the name.
- u8 public_key[0x100];
+ RSA2048PublicKey public_key;
u8 exponent[0x4];
u8 pad[0x34];
};
-static_assert(sizeof(CertRSA4096) == 0x400, "Wrong size for CertRSA4096");
+static_assert(sizeof(CertRSA4096RSA2048) == 0x400, "Wrong size for CertRSA4096RSA2048");
-struct CertRSA2048
+struct CertRSA2048RSA2048
{
SignatureRSA2048 signature;
CertHeader header;
- u8 public_key[0x100];
+ RSA2048PublicKey public_key;
u8 exponent[0x4];
u8 pad[0x34];
};
-static_assert(sizeof(CertRSA2048) == 0x300, "Wrong size for CertRSA2048");
+static_assert(sizeof(CertRSA2048RSA2048) == 0x300, "Wrong size for CertRSA2048RSA2048");
-union Cert
+/// Used for device certificates
+struct CertRSA2048ECC
{
- SignatureType type;
- CertRSA4096 rsa4096;
- CertRSA2048 rsa2048;
+ SignatureRSA2048 signature;
+ CertHeader header;
+ ECCPublicKey public_key;
+ std::array<u8, 60> padding;
+};
+static_assert(sizeof(CertRSA2048ECC) == 0x240, "Wrong size for CertRSA2048ECC");
+
+/// Used for device signed certificates
+struct CertECC
+{
+ SignatureECC signature;
+ CertHeader header;
+ ECCPublicKey public_key;
+ std::array<u8, 60> padding;
};
+static_assert(sizeof(CertECC) == 0x180, "Wrong size for CertECC");
#pragma pack(pop)
using ECCSignature = std::array<u8, 60>;