1 /*---------------------------------------------------------------------------*
2   Project:  Horizon
3   File:     FsSampleSecureSave.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: 47440 $
14  *---------------------------------------------------------------------------*/
15 
16 #include <nn.h>
17 #include "demo.h"
18 
19 #include "../Common/FsSampleCommon.h"
20 #include "FsSampleSecureSave.h"
21 
22 using namespace nn;
23 
24 /*
25  * Overview
26  *
27  Because download applications and other applications write save data to an SD card, users can easily rollback save data by simply backing up the data on their PC.
28  *
29  *
30  * This sample illustrates a method to detect this type of rollback.
31  *
32  * The A button prevents rollback the current state while saving.
33  * The Y button reads save data and checks, at this time, if rollback has been done and displays those results.
34  *
35  */
36 
37 /*
38  * Basic Flow
39  *
40  * By using the SetSaveDataSecureValue and VerifySaveDataSecureValue functions, 64-bit values can be written to and read from the system NAND memory.
41  *
42  *
43  * In a normal sequence, the same value is saved to save data and the system NAND memory.
44  * If data is rolled back, save data values will be affected, but values saved in system NAND memory will not.
45  *
46  * By determining if these values match, you can detect whether rollback has been performed.
47  */
48 
49 namespace sample { namespace fs { namespace securesave {
50 
51 namespace
52 {
53     const char SAVE_DATA_ARCHIVE[] = "data:";
54 } // Namespace
55 
56 // Resets the save data.
Reset()57 void SaveData::Reset()
58 {
59     // Use a random value to initialize the rollback prevention value.
60     version = 0;
61     UpdateVersion();
62 
63     // Resets the data.
64     userData.data1 = 0;
65     userData.data2 = 3;
66 }
67 
68 // Update the rollback prevention value.
UpdateVersion()69 void SaveData::UpdateVersion()
70 {
71     bit64 old = version;
72     do
73     {
74         nn::crypto::GenerateRandomBytes(&version, sizeof(version));
75         version &= SECURE_VALUE_MASK;
76 
77         // The value 0 is obtained if SetSaveDataSecureValue has never been called.
78         // To distinguish this from the case when it has never been called, the value 0 is not used.
79         if (version == 0)
80         {
81             version = 1;
82         }
83     } while (old == version);
84 }
85 
86 // Generate a file name.
GetFileName(char * pFileName,size_t bufferSize,u32 fileIndex)87 void GetFileName(char* pFileName, size_t bufferSize, u32 fileIndex)
88 {
89     nstd::TSNPrintf(pFileName, bufferSize, "%s/Data%d", SAVE_DATA_ARCHIVE, fileIndex);
90 }
91 
92 // Writes data while updating the rollback prevention counter.
Save(SaveData & saveData,u32 fileIndex)93 void Save(SaveData &saveData, u32 fileIndex)
94 {
95     // Update the rollback prevention value.
96     saveData.UpdateVersion();
97 
98     NN_ERR_THROW_FATAL_ALL(nn::fs::MountSaveData(SAVE_DATA_ARCHIVE));
99 
100     // Write the rollback prevention value and data used by the application to save data.
101     // We skip error handling because this is just a sample.
102     // In code intended for actual products, be sure to carry out proper error handling.
103     {
104         char fileName[0x20];
105         GetFileName(fileName, 0x20, fileIndex);
106         nn::fs::FileOutputStream file(fileName, false);
107         file.Write(&saveData, sizeof(saveData), true);
108     }
109 
110     // Be sure to prohibit sleep from the point CommitSaveData is called until SetSecureSaveData has completed execution.
111     //  (Here, prohibition is not used to keep the code simple)
112     //
113     // Regardless of whether CommitSaveData has been called and save data has been rewritten, the comp;arison of values when data is loaded will fail if SetSaveDataSecureValue is not called and the SecureSaveData value is not updated.
114     //
115     // This is because save data can be lost if sleep is done during this time, and during sleep the power is turned off.
116     //
117     // For the same reason, the save data is lost if the media where the save data is to be saved is removed during this time.
118     // To make this time as short as possible, continuously call CommitSaveData and SetSaveDataSecureValue.
119 
120     NN_ERR_THROW_FATAL_ALL(nn::fs::CommitSaveData(SAVE_DATA_ARCHIVE));
121     u32 shift = SECURE_VALUE_SHIFT * fileIndex;
122     nn::fs::SetSaveDataSecureValue(saveData.version << shift, SECURE_VALUE_MASK << shift);
123     nn::fs::Unmount(SAVE_DATA_ARCHIVE);
124 }
125 
126 // Loads the save data.
127 // Performs the rewind check at the same time and returns false if rewind was done.
Load(SaveData & saveData,u32 fileIndex)128 bool Load(SaveData &saveData, u32 fileIndex)
129 {
130     NN_ERR_THROW_FATAL_ALL(nn::fs::MountSaveData(SAVE_DATA_ARCHIVE));
131 
132     // Reads the rollback prevention counter.
133     // We skip error handling because this is just a sample.
134     // In code intended for actual products, be sure to carry out proper error handling.
135     {
136         char fileName[0x20];
137         GetFileName(fileName, 0x20, fileIndex);
138         nn::fs::FileInputStream file(fileName);
139         file.Read(&saveData, sizeof(saveData));
140     }
141 
142     nn::fs::Unmount(SAVE_DATA_ARCHIVE);
143 
144     NN_LOG("SecureValue on SaveDataStorage: %llu\n", saveData.version);
145 
146     // A determination is made that data has been rolled back if the value is invalid or does not match the system NAND value upon comparison.
147     u32 shift = SECURE_VALUE_SHIFT * fileIndex;
148     if ( saveData.version == (saveData.version & SECURE_VALUE_MASK) &&
149         nn::fs::VerifySaveDataSecureValue(saveData.version << shift, SECURE_VALUE_MASK << shift) )
150     {
151         return true;
152     }
153     else
154     {
155         NN_LOG("SecureValue verification failure.\n");
156         // Resets the save data.
157         saveData.Reset();
158         return false;
159     }
160 }
161 
162 // Initializes the save data.
FormatSaveData()163 void FormatSaveData()
164 {
165     const size_t    maxFiles        = 8;
166     const size_t    maxDirectories  = 8;
167     const bool      isDuplicateAll  = true;
168 
169     // Format the save data region
170     Result result = nn::fs::FormatSaveData(maxFiles, maxDirectories, isDuplicateAll);
171     if (result.IsFailure())
172     {
173         NN_LOG("FormatSaveData() failed.\n");
174         NN_ERR_THROW_FATAL_ALL(result);
175     }
176 
177     // Mount in order to compose the data default state
178     result = nn::fs::MountSaveData(SAVE_DATA_ARCHIVE);
179     if (result.IsFailure())
180     {
181         NN_LOG("MountSaveData() failed : Cannot use save data!\n");
182         NN_ERR_THROW_FATAL_ALL(result);
183     }
184 
185     // Create each file
186     for ( u32 i = 0; i < DATA_FILE_NUM; i++ )
187     {
188         char fileName[0x20];
189         GetFileName(fileName, 0x20, i);
190         nn::fs::CreateFile(fileName, sizeof(SaveData));
191     }
192 
193     // Commit
194     NN_ERR_THROW_FATAL_ALL(nn::fs::CommitSaveData(SAVE_DATA_ARCHIVE));
195 
196     // Unmount
197     nn::fs::Unmount(SAVE_DATA_ARCHIVE);
198 }
199 
200 // Save data check when starting the application
201 // Returns true if initialized.
InitializeSaveData()202 bool InitializeSaveData()
203 {
204     Result result = nn::fs::MountSaveData(SAVE_DATA_ARCHIVE);
205     if (result.IsFailure())
206     {
207         NN_DBG_PRINT_RESULT(result);
208 
209         if ((result <= nn::fs::ResultNotFormatted()) ||
210             (result <= nn::fs::ResultBadFormat()) ||
211             (result <= nn::fs::ResultVerificationFailed()))
212         {
213             // Save data needs to be initialized
214 
215             // If a rollback prevention value has already been written to the system NAND memory region, we recommend giving a warning.
216             //   If an attempt is made to play an application saved onto different SD cards on the same system and the rollback prevention value is reset because there was no save data on one of them, the save data on the other will also become unusable.
217             //
218             //
219             if (!nn::fs::VerifySaveDataSecureValue(0))
220             {
221             }
222 
223             FormatSaveData();
224 
225             // Write the data default value
226             for ( u32 i = 0; i < DATA_FILE_NUM; i++ )
227             {
228                 NN_LOG("Initializing Save Data %d...", i);
229                 SaveData saveData;
230                 saveData.Reset();
231                 Save(saveData, i);
232                 NN_LOG("done.\n");
233             }
234             return true;
235         }
236         else
237         {
238             // Unexpected errors
239             NN_ERR_THROW_FATAL_ALL(result);
240         }
241     }
242     else
243     {
244         nn::fs::Unmount(SAVE_DATA_ARCHIVE);
245     }
246     return false;
247 }
248 
249 }}}
250