1 /*---------------------------------------------------------------------------*
2 Project: Horizon
3 File: main.cpp
4
5 Copyright (C)2009-2012 Nintendo Co., Ltd. All rights reserved.
6
7 These coded instructions, statements, and computer programs contain
8 proprietary information of Nintendo of America Inc. and/or Nintendo
9 Company Ltd., and are protected by Federal copyright law. They may
10 not be disclosed to third parties or copied or duplicated in any form,
11 in whole or in part, without the prior written consent of Nintendo.
12
13 $Rev:$
14 *---------------------------------------------------------------------------*/
15
16 #include <memory>
17 #include <nn.h>
18
19
20 namespace
21 {
22 // Data targeted for encryption
23 const char TEST_DATA[] =
24 "abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ";
25
26 // Key used for encryption
27 const bit8 TEST_KEY[nn::crypto::Aes128::KEY_SIZE] =
28 {
29 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
30 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff
31 };
32
33
34 //-----------------------------------------------------------------------------
35
PrintBin(const void * pv,size_t size,const char * indent)36 void PrintBin(const void* pv, size_t size, const char* indent)
37 {
38 const bit8* p = reinterpret_cast<const bit8*>(pv);
39
40 for( size_t i = 0; i < size; ++i )
41 {
42 if( (i % 16) == 0 )
43 {
44 NN_LOG("%s%s", ((i > 0) ? "\n": ""), indent);
45 }
46
47 NN_LOG("%02x ", p[i]);
48 }
49
50 NN_LOG("\n");
51 }
52
53 //-----------------------------------------------------------------------------
54
55 //-------------------------------------------------------------------------------
56 // 1. Perform everything with a single function
57 //
58 class Type1
59 {
60 public:
GetCipherSize1(size_t plainSize)61 size_t GetCipherSize1( size_t plainSize )
62 {
63 return plainSize + nn::crypto::ENCRYPT_HEADER_SIZE;
64 }
GetCipherSize2(size_t plainSize)65 size_t GetCipherSize2( size_t plainSize )
66 {
67 return plainSize + nn::crypto::GENERATE_HEADER_SIZE;
68 }
Encrypt(void * pDst,size_t dstSize,const void * pSrc,size_t srcSize,const void * pKey,size_t keySize)69 void Encrypt(void* pDst, size_t dstSize, const void* pSrc, size_t srcSize, const void* pKey, size_t keySize)
70 {
71 // dstSize and keySize are not referenced
72 NN_UNUSED_VAR(dstSize);
73 NN_UNUSED_VAR(keySize);
74 NN_EQUAL_ASSERT(dstSize, GetCipherSize1(srcSize));
75
76 // Perform everything with a single function
77 nn::crypto::EncryptAes128Ctr(pDst, pSrc, srcSize, pKey);
78 }
Decrypt(void * pDst,size_t dstSize,const void * pSrc,size_t srcSize,const void * pKey,size_t keySize)79 void Decrypt(void* pDst, size_t dstSize, const void* pSrc, size_t srcSize, const void* pKey, size_t keySize)
80 {
81 // dstSize and keySize are not referenced
82 NN_UNUSED_VAR(dstSize);
83 NN_UNUSED_VAR(keySize);
84 NN_EQUAL_ASSERT(GetCipherSize1(dstSize), srcSize);
85
86 // Perform everything with a single function
87 nn::crypto::DecryptAes128Ctr(pDst, pSrc, srcSize, pKey);
88 }
EncryptAndGenerate(void * pDst,size_t dstSize,const void * pSrc,size_t srcSize,const void * pKey,size_t keySize)89 void EncryptAndGenerate(void* pDst, size_t dstSize, const void* pSrc, size_t srcSize, const void* pKey, size_t keySize)
90 {
91 // dstSize and keySize are not referenced
92 NN_UNUSED_VAR(dstSize);
93 NN_UNUSED_VAR(keySize);
94 NN_EQUAL_ASSERT(dstSize, GetCipherSize2(srcSize));
95
96 // Perform everything with a single function
97 nn::crypto::EncryptAndGenerateAes128Ccm(pDst, pSrc, srcSize, pKey);
98 }
DecryptAndVerify(void * pDst,size_t dstSize,const void * pSrc,size_t srcSize,const void * pKey,size_t keySize)99 bool DecryptAndVerify(void* pDst, size_t dstSize, const void* pSrc, size_t srcSize, const void* pKey, size_t keySize)
100 {
101 // dstSize and keySize are not referenced
102 NN_UNUSED_VAR(dstSize);
103 NN_UNUSED_VAR(keySize);
104 NN_EQUAL_ASSERT(GetCipherSize2(dstSize), srcSize);
105
106 // Perform everything with a single function
107 return nn::crypto::DecryptAndVerifyAes128Ccm(pDst, pSrc, srcSize, pKey);
108 }
109 };
110
111 //-------------------------------------------------------------------------------
112 // 2. Perform a batch process combining block cipher and encryption use mode
113 //
114 class Type2
115 {
116 private:
117 static const size_t CCM_IV_SIZE = 12;
118 NN_COMPILER_ASSERT( CCM_IV_SIZE <= nn::crypto::CcmEncryptor::IV_SIZE );
119
120 private:
121 bit8 m_Iv[nn::crypto::CtrEncryptor128::IV_SIZE];
122 bit8 m_Mac[nn::crypto::CcmEncryptor::MAC_SIZE];
123
124 public:
GetCipherSize1(size_t plainSize)125 size_t GetCipherSize1( size_t plainSize ) { return plainSize; }
GetCipherSize2(size_t plainSize)126 size_t GetCipherSize2( size_t plainSize ) { return plainSize; }
127
Encrypt(void * pDst,size_t dstSize,const void * pSrc,size_t srcSize,const void * pKey,size_t keySize)128 void Encrypt(void* pDst, size_t dstSize, const void* pSrc, size_t srcSize, const void* pKey, size_t keySize)
129 {
130 // Randomly generate IV.
131 // IV must use a different value for each encryption
132 // Furthermore, it must be saved separately because it is needed during decryption.
133 nn::crypto::GenerateRandomBytes(m_Iv, sizeof(m_Iv));
134
135 // Select 128 bit AES for the block cipher used for encryption.
136 // The block cipher and key used during encryption and during decryption must be the same.
137 nn::crypto::Aes128 aes;
138 aes.SetKey(pKey, keySize);
139
140 // Performs encryption with CTR mode using 128 bit AES
141 nn::crypto::CtrEncryptor128::Encrypt(pDst, dstSize, pSrc, srcSize, m_Iv, sizeof(m_Iv), aes);
142 }
Decrypt(void * pDst,size_t dstSize,const void * pSrc,size_t srcSize,const void * pKey,size_t keySize)143 void Decrypt(void* pDst, size_t dstSize, const void* pSrc, size_t srcSize, const void* pKey, size_t keySize)
144 {
145 // Select 128 bit AES for the block cipher used for decryption.
146 // The block cipher and key used during encryption and during decryption must be the same.
147 nn::crypto::Aes128 aes;
148 aes.SetKey(pKey, keySize);
149
150 // Performs decryption with CTR mode using 128 bit AES
151 // For IV, the same one used for encryption of the data indicated by pSrc must be specified.
152 nn::crypto::CtrDecryptor128::Decrypt(pDst, dstSize, pSrc, srcSize, m_Iv, sizeof(m_Iv), aes);
153 }
EncryptAndGenerate(void * pDst,size_t dstSize,const void * pSrc,size_t srcSize,const void * pKey,size_t keySize)154 void EncryptAndGenerate(void* pDst, size_t dstSize, const void* pSrc, size_t srcSize, const void* pKey, size_t keySize)
155 {
156 // Randomly generate IV.
157 // IV must use a different value for each encryption
158 // Furthermore, it must be saved separately because it is needed during decryption.
159 NN_COMPILER_ASSERT( CCM_IV_SIZE <= sizeof(m_Iv) );
160 nn::crypto::GenerateRandomBytes(m_Iv, CCM_IV_SIZE);
161
162 // Select 128 bit AES for the block cipher used for encryption.
163 // The block cipher and key used during encryption and during decryption must be the same.
164 nn::crypto::Aes128 aes;
165 aes.SetKey(pKey, keySize);
166
167 // Performs encryption with CCM mode using 128 bit AES
168 // This demo does not use Adata.
169 nn::crypto::CcmEncryptor::EncryptAndGenerate(
170 m_Mac, pDst, dstSize, NULL, 0, pSrc, srcSize, m_Iv, CCM_IV_SIZE, sizeof(m_Mac), aes);
171 }
DecryptAndVerify(void * pDst,size_t dstSize,const void * pSrc,size_t srcSize,const void * pKey,size_t keySize)172 bool DecryptAndVerify(void* pDst, size_t dstSize, const void* pSrc, size_t srcSize, const void* pKey, size_t keySize)
173 {
174 // Select 128 bit AES for the block cipher used for encryption.
175 // The block cipher and key used during encryption and during decryption must be the same.
176 nn::crypto::Aes128 aes;
177 aes.SetKey(pKey, keySize);
178
179 // Performs decryption with CCM mode using 128 bit AES
180 // This demo does not use Adata.
181 // For IV, the same one used for encryption of the data indicated by pSrc must be specified.
182 // MAC specifies the one generated when the data indicated by pSrc is encrypted.
183 return nn::crypto::CcmDecryptor::DecryptAndVerify(
184 pDst, dstSize, NULL, 0, pSrc, srcSize, m_Iv, CCM_IV_SIZE, m_Mac, sizeof(m_Mac), aes);
185 }
186 };
187
188
189 //-------------------------------------------------------------------------------
190 // 3. Processes with streaming by combining block cipher and encryption use mode
191 //
192 class Type3
193 {
194 private:
195 static const size_t CCM_IV_SIZE = 12;
196 NN_COMPILER_ASSERT( CCM_IV_SIZE <= nn::crypto::CcmEncryptor::IV_SIZE );
197
198 private:
199 bit8 m_Iv[nn::crypto::CtrEncryptor128::IV_SIZE];
200 bit8 m_Mac[nn::crypto::CcmEncryptor::MAC_SIZE];
201
202 public:
GetCipherSize1(size_t plainSize)203 size_t GetCipherSize1( size_t plainSize ) { return plainSize; }
GetCipherSize2(size_t plainSize)204 size_t GetCipherSize2( size_t plainSize ) { return plainSize; }
205
Encrypt(void * pDst,size_t dstSize,const void * pSrc,size_t srcSize,const void * pKey,size_t keySize)206 void Encrypt(void* pDst, size_t dstSize, const void* pSrc, size_t srcSize, const void* pKey, size_t keySize)
207 {
208 // Randomly generate IV.
209 // IV must use a different value for each encryption
210 // Furthermore, it must be saved separately because it is needed during decryption.
211 nn::crypto::GenerateRandomBytes(m_Iv, sizeof(m_Iv));
212
213 // Select 128 bit AES for the block cipher used for encryption.
214 // The block cipher and key used during encryption and during decryption must be the same.
215 nn::crypto::Aes128 aes;
216 aes.SetKey(pKey, keySize);
217
218 // Uses CTR mode.
219 nn::crypto::CtrEncryptor128 ctr;
220 ctr.Initialize(aes, m_Iv, sizeof(m_Iv));
221
222 // The input can be passed in small pieces.
223 // In this demo, the input is passed 1 byte at a time.
224 const size_t packetSize = 1;
225 const bit8* pS = reinterpret_cast<const bit8*>(pSrc);
226 bit8* pD = reinterpret_cast<bit8*>(pDst);
227 size_t offset = 0;
228 for( size_t i = 0; i < srcSize; ++i )
229 {
230 const size_t out = ctr.Update(pD + offset, dstSize - offset, &pS[i], packetSize);
231 NN_MAX_ASSERT(out, dstSize - offset);
232
233 offset += out;
234 }
235 {
236 const size_t out = ctr.UpdateFinal(pD + offset, dstSize - offset);
237 NN_MAX_ASSERT(out, dstSize - offset);
238
239 offset += out;
240 NN_EQUAL_ASSERT(dstSize, offset);
241 }
242
243 ctr.Finalize();
244 }
Decrypt(void * pDst,size_t dstSize,const void * pSrc,size_t srcSize,const void * pKey,size_t keySize)245 void Decrypt(void* pDst, size_t dstSize, const void* pSrc, size_t srcSize, const void* pKey, size_t keySize)
246 {
247 // Select 128 bit AES for the block cipher used for decryption.
248 // The block cipher and key used during encryption and during decryption must be the same.
249 nn::crypto::Aes128 aes;
250 aes.SetKey(pKey, keySize);
251
252 // Uses CTR mode.
253 // For IV, the same one used for encryption of the data indicated by pSrc must be specified.
254 nn::crypto::CtrDecryptor128 ctr;
255 ctr.Initialize(aes, m_Iv, sizeof(m_Iv));
256
257 // The input can be passed in small pieces.
258 // In this demo, the input is passed 1 byte at a time.
259 const size_t packetSize = 1;
260 const bit8* pS = reinterpret_cast<const bit8*>(pSrc);
261 bit8* pD = reinterpret_cast<bit8*>(pDst);
262 size_t offset = 0;
263 for( size_t i = 0; i < srcSize; ++i )
264 {
265 const size_t out = ctr.Update(pD + offset, dstSize - offset, &pS[i], packetSize);
266 NN_MAX_ASSERT(out, dstSize - offset);
267
268 offset += out;
269 }
270 {
271 const size_t out = ctr.UpdateFinal(pD + offset, dstSize - offset);
272 NN_MAX_ASSERT(out, dstSize - offset);
273
274 offset += out;
275 NN_EQUAL_ASSERT(dstSize, offset);
276 }
277
278 ctr.Finalize();
279 }
EncryptAndGenerate(void * pDst,size_t dstSize,const void * pSrc,size_t srcSize,const void * pKey,size_t keySize)280 void EncryptAndGenerate(void* pDst, size_t dstSize, const void* pSrc, size_t srcSize, const void* pKey, size_t keySize)
281 {
282 // Randomly generate IV.
283 // IV must use a different value for each encryption
284 // Furthermore, it must be saved separately because it is needed during decryption.
285 NN_COMPILER_ASSERT( CCM_IV_SIZE <= sizeof(m_Iv) );
286 nn::crypto::GenerateRandomBytes(m_Iv, CCM_IV_SIZE);
287
288 // Select 128 bit AES for the block cipher used for encryption.
289 // The block cipher and key used during encryption and during decryption must be the same.
290 nn::crypto::Aes128 aes;
291 aes.SetKey(pKey, keySize);
292
293 // Uses CCM mode.
294 nn::crypto::CcmEncryptor ccm;
295 ccm.Initialize(aes, m_Iv, CCM_IV_SIZE, 0, srcSize, sizeof(m_Mac));
296
297 // This demo does not use Adata.
298 ccm.UpdateAdataFinal();
299
300 // The input can be passed in small pieces.
301 // In this demo, the input is passed 1 byte at a time.
302 const size_t packetSize = 1;
303 const bit8* pS = reinterpret_cast<const bit8*>(pSrc);
304 bit8* pD = reinterpret_cast<bit8*>(pDst);
305 size_t offset = 0;
306 for( size_t i = 0; i < srcSize; ++i )
307 {
308 const size_t out = ccm.UpdatePdata(pD + offset, dstSize - offset, &pS[i], packetSize);
309 NN_MAX_ASSERT(out, dstSize - offset);
310
311 offset += out;
312 }
313 {
314 const size_t out = ccm.UpdatePdataFinal(pD + offset, dstSize - offset);
315 NN_MAX_ASSERT(out, dstSize - offset);
316
317 offset += out;
318 NN_EQUAL_ASSERT(dstSize, offset);
319 }
320
321 // Generates a MAC for verifying validity.
322 ccm.GenerateMac(m_Mac, sizeof(m_Mac));
323
324 ccm.Finalize();
325 }
DecryptAndVerify(void * pDst,size_t dstSize,const void * pSrc,size_t srcSize,const void * pKey,size_t keySize)326 bool DecryptAndVerify(void* pDst, size_t dstSize, const void* pSrc, size_t srcSize, const void* pKey, size_t keySize)
327 {
328 // Select 128 bit AES for the block cipher used for encryption.
329 // The block cipher and key used during encryption and during decryption must be the same.
330 nn::crypto::Aes128 aes;
331 aes.SetKey(pKey, keySize);
332
333 // Uses CCM mode.
334 // For the MAC size and IV, the same one used for encryption of the data indicated by pSrc must be specified.
335 nn::crypto::CcmDecryptor ccm;
336 ccm.Initialize(aes, m_Iv, CCM_IV_SIZE, 0, srcSize, sizeof(m_Mac));
337
338 // This demo does not use Adata.
339 ccm.UpdateAdataFinal();
340
341 // The input can be passed in small pieces.
342 // In this demo, the input is passed 1 byte at a time.
343 const size_t packetSize = 1;
344 const bit8* pS = reinterpret_cast<const bit8*>(pSrc);
345 bit8* pD = reinterpret_cast<bit8*>(pDst);
346 size_t offset = 0;
347 for( size_t i = 0; i < srcSize; ++i )
348 {
349 const size_t out = ccm.UpdateCdata(pD + offset, dstSize - offset, &pS[i], packetSize);
350 NN_MAX_ASSERT(out, dstSize - offset);
351
352 offset += out;
353 }
354 {
355 const size_t out = ccm.UpdateCdataFinal(pD + offset, dstSize - offset);
356 NN_MAX_ASSERT(out, dstSize - offset);
357
358 offset += out;
359 NN_EQUAL_ASSERT(dstSize, offset);
360 }
361
362 // Generates a MAC for verifying validity.
363 bit8 testMac[nn::crypto::CcmEncryptor::MAC_SIZE];
364 ccm.GenerateMac(testMac, sizeof(testMac));
365
366 // Compares the MAC generated during encryption to the MAC generated during decryption.
367 // If they do not match, this indicates that the data has been altered.
368 return std::memcmp(testMac, m_Mac, sizeof(testMac)) == 0;
369 }
370 };
371
372
373 template <typename T>
Demo(T & type)374 void Demo(T& type)
375 {
376 // Demo for processing only encryption
377 NN_LOG(" " "encryption\n");
378 {
379 const size_t plainSize = sizeof(TEST_DATA);
380 const size_t cipherSize = type.GetCipherSize1(plainSize);
381
382 std::auto_ptr<bit8> pEncrypted(new bit8[cipherSize]);
383 std::auto_ptr<bit8> pDecrypted(new bit8[plainSize]);
384 NN_POINTER_ASSERT(pEncrypted.get());
385 NN_POINTER_ASSERT(pDecrypted.get());
386
387 // Displays data before encryption
388 NN_LOG(" " "plain text\n");
389 NN_LOG(" " "----\n");
390 PrintBin(TEST_DATA, plainSize, " ");
391 NN_LOG(" " "----\n");
392
393 // Encryption
394 type.Encrypt(pEncrypted.get(), cipherSize, TEST_DATA, plainSize, TEST_KEY, sizeof(TEST_KEY));
395
396 // Displays data after encryption
397 NN_LOG(" " "encrypted text\n");
398 NN_LOG(" " "----\n");
399 PrintBin(pEncrypted.get(), cipherSize, " ");
400 NN_LOG(" " "----\n");
401
402 // Decryption
403 type.Decrypt(pDecrypted.get(), plainSize, pEncrypted.get(), cipherSize, TEST_KEY, sizeof(TEST_KEY));
404
405 // Displays data after decryption
406 NN_LOG(" " "decrypted text\n");
407 NN_LOG(" " "----\n");
408 PrintBin(pDecrypted.get(), plainSize, " ");
409 NN_LOG(" " "----\n");
410
411 // Compares the data after decryption to the data before encryption
412 bool match = (std::memcmp(TEST_DATA, pDecrypted.get(), plainSize) == 0);
413
414 // Displays the comparison results
415 NN_LOG(" " "decrypted data %s src data\n", (match ? "==": "!="));
416 }
417 NN_LOG("\n");
418
419 // Demo to process encryption with validity testing
420 NN_LOG(" " "encryption and authentication\n");
421 {
422 const size_t plainSize = sizeof(TEST_DATA);
423 const size_t cipherSize = type.GetCipherSize2(plainSize);
424
425 std::auto_ptr<bit8> pEncrypted(new bit8[cipherSize]);
426 std::auto_ptr<bit8> pDecrypted(new bit8[plainSize]);
427 NN_POINTER_ASSERT(pEncrypted.get());
428 NN_POINTER_ASSERT(pDecrypted.get());
429
430 // Displays data before encryption
431 NN_LOG(" " "plain text\n");
432 NN_LOG(" " "----\n");
433 PrintBin(TEST_DATA, plainSize, " ");
434 NN_LOG(" " "----\n");
435
436 // Encryption
437 type.EncryptAndGenerate(pEncrypted.get(), cipherSize, TEST_DATA, plainSize, TEST_KEY, sizeof(TEST_KEY));
438
439 // Displays data after encryption
440 NN_LOG(" " "encrypted text\n");
441 NN_LOG(" " "----\n");
442 PrintBin(pEncrypted.get(), cipherSize, " ");
443 NN_LOG(" " "----\n");
444
445 // Decryption
446 bool ok1 = type.DecryptAndVerify(pDecrypted.get(), plainSize, pEncrypted.get(), cipherSize, TEST_KEY, sizeof(TEST_KEY));
447
448 // Displays the decryption verification results (should be good) and the data after decryption
449 NN_LOG(" " "authentication result = %s\n", (ok1 ? "good": "bad"));
450 NN_LOG(" " "decrypted text\n");
451 NN_LOG(" " "----\n");
452 PrintBin(pDecrypted.get(), plainSize, " ");
453 NN_LOG(" " "----\n");
454
455 // Compares the data after decryption to the data before encryption
456 bool match = (std::memcmp(TEST_DATA, pDecrypted.get(), plainSize) == 0);
457
458 // Displays the comparison results
459 NN_LOG(" " "decrypted data %s src data\n", (match ? "==": "!="));
460
461 // Try decryption after changing 1 bit of encrypted data
462 NN_LOG(" " "edit test\n");
463 pEncrypted.get()[0] ^= 0x01;
464 bool ok2 = type.DecryptAndVerify(pDecrypted.get(), plainSize, pEncrypted.get(), cipherSize, TEST_KEY, sizeof(TEST_KEY));
465
466 // Display the decryption verification results (should be bad)
467 NN_LOG(" " "authentication result = %s\n", (ok2 ? "good": "bad"));
468 }
469 }
470 }
471
472
nnMain()473 void nnMain()
474 {
475 // Call only the nn::applet::Enable function to also allow execution from the HOME Menu
476 // HOME Menu transitions, POWER Button presses, and sleep are all unsupported
477 nn::applet::Enable();
478
479 NN_LOG("BlockCipher\n");
480 NN_LOG("\n");
481
482 Type1 type1;
483 Type2 type2;
484 Type3 type3;
485
486 NN_LOG("Type 1\n");
487 Demo(type1);
488 NN_LOG("\n");
489
490 NN_LOG("Type 2\n");
491 Demo(type2);
492 NN_LOG("\n");
493
494 NN_LOG("Type 3\n");
495 Demo(type3);
496 NN_LOG("\n");
497
498 NN_LOG("end.\n");
499 }
500