// Copyright 2017 Citra Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #include #include #include #include #include #include "common/alignment.h" #include "common/logging/log.h" #include "core/hw/aes/ccm.h" #include "core/hw/aes/key.h" namespace HW { namespace AES { namespace { // 3DS uses a non-standard AES-CCM algorithm, so we need to derive a sub class from the standard one // and override with the non-standard part. using CryptoPP::lword; using CryptoPP::AES; using CryptoPP::CCM_Final; using CryptoPP::CCM_Base; template class CCM_3DSVariant_Final : public CCM_Final { public: void UncheckedSpecifyDataLengths(lword header_length, lword message_length, lword footer_length) override { // 3DS uses the aligned size to generate B0 for authentication, instead of the original size lword aligned_message_length = Common::AlignUp(message_length, AES_BLOCK_SIZE); CCM_Base::UncheckedSpecifyDataLengths(header_length, aligned_message_length, footer_length); CCM_Base::m_messageLength = message_length; // restore the actual message size } }; class CCM_3DSVariant { public: using Encryption = CCM_3DSVariant_Final; using Decryption = CCM_3DSVariant_Final; }; } // namespace std::vector EncryptSignCCM(const std::vector& pdata, const CCMNonce& nonce, size_t slot_id) { if (!IsNormalKeyAvailable(slot_id)) { LOG_ERROR(HW_AES, "Key slot %d not available. Will use zero key.", slot_id); } const AESKey normal = GetNormalKey(slot_id); std::vector cipher(pdata.size() + CCM_MAC_SIZE); try { CCM_3DSVariant::Encryption e; e.SetKeyWithIV(normal.data(), AES_BLOCK_SIZE, nonce.data(), CCM_NONCE_SIZE); e.SpecifyDataLengths(0, pdata.size(), 0); CryptoPP::ArraySource as(pdata.data(), pdata.size(), true, new CryptoPP::AuthenticatedEncryptionFilter( e, new CryptoPP::ArraySink(cipher.data(), cipher.size()))); } catch (const CryptoPP::Exception& e) { LOG_ERROR(HW_AES, "FAILED with: %s", e.what()); } return cipher; } std::vector DecryptVerifyCCM(const std::vector& cipher, const CCMNonce& nonce, size_t slot_id) { if (!IsNormalKeyAvailable(slot_id)) { LOG_ERROR(HW_AES, "Key slot %d not available. Will use zero key.", slot_id); } const AESKey normal = GetNormalKey(slot_id); const std::size_t pdata_size = cipher.size() - CCM_MAC_SIZE; std::vector pdata(pdata_size); try { CCM_3DSVariant::Decryption d; d.SetKeyWithIV(normal.data(), AES_BLOCK_SIZE, nonce.data(), CCM_NONCE_SIZE); d.SpecifyDataLengths(0, pdata_size, 0); CryptoPP::AuthenticatedDecryptionFilter df( d, new CryptoPP::ArraySink(pdata.data(), pdata_size)); CryptoPP::ArraySource as(cipher.data(), cipher.size(), true, new CryptoPP::Redirector(df)); if (!df.GetLastResult()) { LOG_ERROR(HW_AES, "FAILED"); return {}; } } catch (const CryptoPP::Exception& e) { LOG_ERROR(HW_AES, "FAILED with: %s", e.what()); return {}; } return pdata; } } // namespace AES } // namespace HW