summaryrefslogtreecommitdiff
path: root/src/core/hw/aes/ccm.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hw/aes/ccm.cpp')
-rw-r--r--src/core/hw/aes/ccm.cpp95
1 files changed, 95 insertions, 0 deletions
diff --git a/src/core/hw/aes/ccm.cpp b/src/core/hw/aes/ccm.cpp
new file mode 100644
index 000000000..dc7035ab6
--- /dev/null
+++ b/src/core/hw/aes/ccm.cpp
@@ -0,0 +1,95 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <cryptopp/aes.h>
7#include <cryptopp/ccm.h>
8#include <cryptopp/cryptlib.h>
9#include <cryptopp/filters.h>
10#include "common/alignment.h"
11#include "common/logging/log.h"
12#include "core/hw/aes/ccm.h"
13#include "core/hw/aes/key.h"
14
15namespace HW {
16namespace AES {
17
18namespace {
19
20// 3DS uses a non-standard AES-CCM algorithm, so we need to derive a sub class from the standard one
21// and override with the non-standard part.
22using CryptoPP::lword;
23using CryptoPP::AES;
24using CryptoPP::CCM_Final;
25using CryptoPP::CCM_Base;
26template <bool T_IsEncryption>
27class CCM_3DSVariant_Final : public CCM_Final<AES, CCM_MAC_SIZE, T_IsEncryption> {
28public:
29 void UncheckedSpecifyDataLengths(lword header_length, lword message_length,
30 lword footer_length) override {
31 // 3DS uses the aligned size to generate B0 for authentication, instead of the original size
32 lword aligned_message_length = Common::AlignUp(message_length, AES_BLOCK_SIZE);
33 CCM_Base::UncheckedSpecifyDataLengths(header_length, aligned_message_length, footer_length);
34 CCM_Base::m_messageLength = message_length; // restore the actual message size
35 }
36};
37
38class CCM_3DSVariant {
39public:
40 using Encryption = CCM_3DSVariant_Final<true>;
41 using Decryption = CCM_3DSVariant_Final<false>;
42};
43
44} // namespace
45
46std::vector<u8> EncryptSignCCM(const std::vector<u8>& pdata, const CCMNonce& nonce,
47 size_t slot_id) {
48 if (!IsNormalKeyAvailable(slot_id)) {
49 LOG_ERROR(HW_AES, "Key slot %d not available. Will use zero key.", slot_id);
50 }
51 const AESKey normal = GetNormalKey(slot_id);
52 std::vector<u8> cipher(pdata.size() + CCM_MAC_SIZE);
53
54 try {
55 CCM_3DSVariant::Encryption e;
56 e.SetKeyWithIV(normal.data(), AES_BLOCK_SIZE, nonce.data(), CCM_NONCE_SIZE);
57 e.SpecifyDataLengths(0, pdata.size(), 0);
58 CryptoPP::ArraySource as(pdata.data(), pdata.size(), true,
59 new CryptoPP::AuthenticatedEncryptionFilter(
60 e, new CryptoPP::ArraySink(cipher.data(), cipher.size())));
61 } catch (const CryptoPP::Exception& e) {
62 LOG_ERROR(HW_AES, "FAILED with: %s", e.what());
63 }
64 return cipher;
65}
66
67std::vector<u8> DecryptVerifyCCM(const std::vector<u8>& cipher, const CCMNonce& nonce,
68 size_t slot_id) {
69 if (!IsNormalKeyAvailable(slot_id)) {
70 LOG_ERROR(HW_AES, "Key slot %d not available. Will use zero key.", slot_id);
71 }
72 const AESKey normal = GetNormalKey(slot_id);
73 const std::size_t pdata_size = cipher.size() - CCM_MAC_SIZE;
74 std::vector<u8> pdata(pdata_size);
75
76 try {
77 CCM_3DSVariant::Decryption d;
78 d.SetKeyWithIV(normal.data(), AES_BLOCK_SIZE, nonce.data(), CCM_NONCE_SIZE);
79 d.SpecifyDataLengths(0, pdata_size, 0);
80 CryptoPP::AuthenticatedDecryptionFilter df(
81 d, new CryptoPP::ArraySink(pdata.data(), pdata_size));
82 CryptoPP::ArraySource as(cipher.data(), cipher.size(), true, new CryptoPP::Redirector(df));
83 if (!df.GetLastResult()) {
84 LOG_ERROR(HW_AES, "FAILED");
85 return {};
86 }
87 } catch (const CryptoPP::Exception& e) {
88 LOG_ERROR(HW_AES, "FAILED with: %s", e.what());
89 return {};
90 }
91 return pdata;
92}
93
94} // namespace AES
95} // namespace HW