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