mirror of
https://github.com/rvtr/ctr_Repair.git
synced 2025-10-31 13:51:08 -04:00
本体初期化時もResultチェックを行うように git-svn-id: file:///Volumes/Transfer/gigaleak_20231201/2020-05-23%20-%20ctr.7z%20+%20svn_v1.068.zip/ctr/svn/ctr_Repair@369 385bec56-5757-e545-9c3a-d8741f4650f1
771 lines
29 KiB
C++
771 lines
29 KiB
C++
/*---------------------------------------------------------------------------*
|
|
Project: Horizon
|
|
File: FileTransfer.cpp
|
|
|
|
Copyright 2009 Nintendo. All rights reserved.
|
|
|
|
These coded instructions, statements, and computer programs contain
|
|
proprietary information of Nintendo of America Inc. and/or Nintendo
|
|
Company Ltd., and are protected by Federal copyright law. They may
|
|
not be disclosed to third parties or copied or duplicated in any form,
|
|
in whole or in part, without the prior written consent of Nintendo.
|
|
|
|
$Rev$
|
|
*---------------------------------------------------------------------------*/
|
|
|
|
#include <vector>
|
|
#include <string>
|
|
|
|
#include <nn/crypto/crypto_AesCmac.h>
|
|
#include <nn/crypto/crypto_SwAesCtrContext.h>
|
|
#include <nn/crypto/crypto_Sha256.h>
|
|
#include <nn/crypto/crypto_SwAesCmac.h>
|
|
|
|
#include "Aes_define.h"
|
|
#include "FileTransfer.h"
|
|
#include "CommonLogger.h"
|
|
#include "common_Types.h"
|
|
#include "FileName.h"
|
|
|
|
namespace common
|
|
{
|
|
|
|
namespace
|
|
{
|
|
|
|
u64 s_TotalFileSize;
|
|
u64 s_FinishedFileSize = 0;
|
|
u64 s_Progress = 0;
|
|
|
|
}
|
|
|
|
bool VerifyMac(nn::fs::FileInputStream* sdFile, nn::fs::FileStream* nandFile, s64 sdFileSize, s64 nandFileSize,
|
|
const wchar_t* nandPath, void* buf, size_t bufSize);
|
|
bool ConfirmFile(nn::fs::FileInputStream* from_file, nn::fs::FileStream* to_file, s64 sdFileSize, s64 nandFileSize,
|
|
void* buf, size_t bufSize, const wchar_t* sdPath, const wchar_t* tmpPath, const wchar_t* truePath);
|
|
void AddPkcsPadding(u8* paddingSize, void* buf, size_t bufSize, s32* readSize);
|
|
bool AddPathNameAndUpdateContext(nn::fs::FileOutputStream* file, const wchar_t *str, s64 fileSize,
|
|
nn::crypto::Sha256Context* context);
|
|
|
|
const char* GetCharStr(const wchar_t* path)
|
|
{
|
|
static char filename[nn::fs::MAX_FILE_PATH_LENGTH];
|
|
std::memset(filename, 0, sizeof(filename));
|
|
std::wcstombs(filename, path, sizeof(filename));
|
|
filename[sizeof(filename) - 1] = '\0';
|
|
return filename;
|
|
}
|
|
|
|
nn::Result CalculateFileNum(std::wstring currentDirectory, u32& fileNum, s64& fileSize)
|
|
{
|
|
nn::fs::Directory dir;
|
|
nn::Result result;
|
|
std::vector<nn::fs::DirectoryEntry> entryList; //カレントディレクトリのエントリ一覧を格納
|
|
std::vector<nn::fs::DirectoryEntry>::iterator entryIndex;
|
|
|
|
result = dir.TryInitialize(currentDirectory.c_str());
|
|
COMMON_LOGGER_RETURN_RESULT_IF_FAILED(result);
|
|
|
|
nn::fs::DirectoryEntry entry;
|
|
s32 numEntry;
|
|
for (;;)
|
|
{
|
|
result = dir.TryRead(&numEntry, &entry, 1);
|
|
COMMON_LOGGER_RETURN_RESULT_IF_FAILED(result);
|
|
if (numEntry == 0)
|
|
{
|
|
// カレントディレクトリを閉じる
|
|
dir.Finalize();
|
|
|
|
// カレントディレクトリの子を開く
|
|
for (entryIndex = entryList.begin(); entryIndex != entryList.end(); entryIndex++)
|
|
{
|
|
if (entryIndex->attributes.isDirectory)
|
|
{
|
|
result = CalculateFileNum(currentDirectory + std::wstring(entryIndex->entryName) + std::wstring(L"/"),
|
|
fileNum, fileSize);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
entryList.push_back(entry);
|
|
fileNum++;
|
|
fileSize += entry.entrySize;
|
|
}
|
|
}
|
|
|
|
bool ExistsInList(ImportDataList* fileList, const wchar_t* path, bool isDirectory)
|
|
{
|
|
std::wstring sdPath(path);
|
|
if(isDirectory)
|
|
{
|
|
sdPath += std::wstring(L"/");
|
|
}
|
|
|
|
char str[nn::fs::MAX_FILE_PATH_LENGTH];
|
|
std::strlcpy(str, GetCharStr(sdPath.c_str()), sizeof(str));
|
|
|
|
bool returnValue = false;
|
|
for(ImportDataList::iterator it = fileList->begin(); it != fileList->end(); it++)
|
|
{
|
|
if(std::strcmp(str, it->fileName.c_str()) == 0)
|
|
{
|
|
returnValue = true;
|
|
NN_LOG("%s exists in FileList.txt\n", str);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return returnValue;
|
|
}
|
|
|
|
bool ExportTwlSaveDirectory(const wchar_t* dirPath, nn::fs::FileOutputStream* list,
|
|
nn::crypto::Sha256Context* listContext)
|
|
{
|
|
NN_LOG("Create Directory %ls\n", dirPath);
|
|
|
|
nn::Result result = nn::fs::TryCreateDirectory(dirPath);
|
|
COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result);
|
|
|
|
return AddPathNameAndUpdateContext(list, dirPath, -1, listContext);
|
|
}
|
|
|
|
bool ExportTwlSaveFile(const wchar_t* from_path, const wchar_t* to_path, void* buf, const size_t bufSize,
|
|
nn::fs::FileOutputStream* list, nn::crypto::Sha256Context* listContext)
|
|
{
|
|
NN_LOG("from = %ls\n", from_path);
|
|
NN_LOG("to = %ls\n", to_path);
|
|
|
|
nn::Result result;
|
|
|
|
// ファイル作成
|
|
nn::fs::FileInputStream from_file;
|
|
nn::fs::FileStream to_file;
|
|
s64 filesize;
|
|
s32 readsize;
|
|
s32 writesize;
|
|
|
|
NN_LOG("Copy File %ls\n", from_path);
|
|
|
|
// 読み込み対象ファイル開く
|
|
result = from_file.TryInitialize(from_path);
|
|
COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result);
|
|
|
|
// 読み込み対象ファイルのサイズ取得
|
|
result = from_file.TryGetSize(&filesize);
|
|
COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result);
|
|
|
|
if (!AddPathNameAndUpdateContext(list, to_path, filesize, listContext))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
nn::crypto::SwAesCtrContext swAesCtrContext;
|
|
swAesCtrContext.Initialize(iv, common::key, sizeof(key));
|
|
|
|
size_t totalReadSize = 0;
|
|
nn::crypto::Sha256Context context;
|
|
context.Initialize();
|
|
|
|
// ファイルサイズをヘッダに書いておく
|
|
// 書き込み対象ファイル作成
|
|
result = nn::fs::TryCreateFile(to_path, filesize);
|
|
COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result);
|
|
result = to_file.TryInitialize(to_path,
|
|
nn::fs::OPEN_MODE_READ | nn::fs::OPEN_MODE_WRITE | nn::fs::OPEN_MODE_CREATE);
|
|
COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result);
|
|
|
|
// フルパスをハッシュに含める
|
|
context.Update(from_path, std::wcslen(from_path) * sizeof(wchar_t));
|
|
|
|
BackupDataHeader header;
|
|
BackupDataHeader enc;
|
|
std::memset(&header, 0, sizeof(header));
|
|
std::memset(&enc, 0, sizeof(enc));
|
|
header.size = filesize;
|
|
result = swAesCtrContext.Encrypt(&enc, &header, sizeof(header));
|
|
COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result);
|
|
context.Update(&enc, sizeof(enc));
|
|
s32 writeSize;
|
|
result = to_file.TryWrite(&writeSize, &enc, sizeof(enc), false);
|
|
COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result);
|
|
|
|
while (1)
|
|
{
|
|
// バッファの後半半分を暗号・復号用に使う
|
|
result = from_file.TryRead(&readsize, buf, bufSize / 2);
|
|
COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result);
|
|
totalReadSize += readsize;
|
|
|
|
COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result);
|
|
|
|
if (readsize == 0)
|
|
{
|
|
NN_LOG("Add CMAC %ls\n", from_path);
|
|
// SHA256を計算してCMACを付加する
|
|
bit8 sha256Hash[nn::crypto::Sha256Context::HASH_SIZE];
|
|
context.GetHash(sha256Hash);
|
|
|
|
bit8 cmac[nn::crypto::AES_CMAC_MAC_SIZE];
|
|
result = nn::crypto::CalculateAesCmacSw(cmac, sha256Hash, nn::crypto::Sha256Context::HASH_SIZE,
|
|
common::cmacKey);
|
|
COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result);
|
|
|
|
result = to_file.TryWrite(&writesize, cmac, sizeof(cmac));
|
|
COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result);
|
|
result = to_file.TryFlush();
|
|
COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result);
|
|
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
NN_LOG("EncryptSize = %d\n", readsize);
|
|
|
|
u8 paddingSize = 0;
|
|
AddPkcsPadding(&paddingSize, reinterpret_cast<bit8*>(buf), bufSize / 2, &readsize);
|
|
|
|
// 暗号化後SHA256を計算しつつ書き込み
|
|
result = swAesCtrContext.Encrypt(reinterpret_cast<bit8*>(buf) + bufSize / 2, buf, readsize);
|
|
COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result);
|
|
context.Update(reinterpret_cast<bit8*>(buf) + bufSize / 2, readsize);
|
|
|
|
result = to_file.TryWrite(&writesize, reinterpret_cast<bit8*>(buf) + bufSize / 2, readsize, false);
|
|
COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result);
|
|
|
|
// 事前計算したファイルサイズに一致させるためパディング分減算
|
|
readsize -= paddingSize;
|
|
|
|
s_FinishedFileSize += readsize;
|
|
s_Progress = s_FinishedFileSize * 100 / s_TotalFileSize;
|
|
NN_LOG( "finish = %lld, total = %lld, progress = %lld\n", s_FinishedFileSize, s_TotalFileSize, s_Progress);
|
|
}
|
|
}
|
|
to_file.Finalize();
|
|
|
|
from_file.Finalize();
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool CopyDirectory(ImportDataList* fileList, const wchar_t * from_path, const wchar_t * to_path, void* buf,
|
|
const size_t bufSize, bool encode, nn::fs::FileOutputStream* list, nn::crypto::Sha256Context* listContext)
|
|
{
|
|
nn::fs::Directory from_dir;
|
|
nn::fs::DirectoryEntry entry;
|
|
s32 numread = 0;
|
|
std::wostringstream target_from;
|
|
std::wostringstream target_to;
|
|
|
|
nn::Result result = from_dir.TryInitialize(from_path);
|
|
COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result);
|
|
|
|
while (1)
|
|
{
|
|
result = from_dir.TryRead(&numread, &entry, 1);
|
|
COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result);
|
|
|
|
if(numread == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (std::wcscmp(entry.entryName, L".") == 0 || std::wcscmp(entry.entryName, L"..") == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
target_from.str(L"");
|
|
target_from.clear(std::stringstream::goodbit);
|
|
target_from << from_path << entry.entryName;
|
|
|
|
target_to.str(L"");
|
|
target_to.clear(std::stringstream::goodbit);
|
|
target_to << to_path << entry.entryName;
|
|
|
|
// NAND書き込みの場合はリストに存在するかチェックする
|
|
if (!encode)
|
|
{
|
|
if (!ExistsInList(fileList, target_from.str().c_str(), entry.attributes.isDirectory))
|
|
{
|
|
NN_LOG("============No such file %ls in FileList.txt. Skip=============\n", target_from.str().c_str());
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// ディレクトリの場合
|
|
if (entry.attributes.isDirectory)
|
|
{
|
|
// ディレクトリ作成
|
|
NN_LOG("Create Directory %ls\n", target_to.str().c_str());
|
|
result = nn::fs::TryCreateDirectory(target_to.str().c_str());
|
|
if (result.IsSuccess() || result.IsFailure() && result <= nn::fs::ResultAlreadyExists())
|
|
{
|
|
target_from << L"/";
|
|
target_to << L"/";
|
|
if(encode)
|
|
{
|
|
if(!AddPathNameAndUpdateContext(list, target_to.str().c_str(), -1, listContext))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// 再帰処理
|
|
if (!CopyDirectory(fileList, target_from.str().c_str(), target_to.str().c_str(), buf, bufSize, encode, list, listContext))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result);
|
|
}
|
|
}
|
|
// ファイルの場合
|
|
else
|
|
{
|
|
std::wostringstream target_tmp;
|
|
target_tmp.str(L"");
|
|
target_tmp.clear(std::stringstream::goodbit);
|
|
|
|
if(!encode)
|
|
{
|
|
target_tmp << to_path << L"_" << entry.entryName;
|
|
}
|
|
else
|
|
{
|
|
target_tmp << target_to.str();
|
|
}
|
|
|
|
// ファイル作成
|
|
nn::fs::FileInputStream from_file;
|
|
nn::fs::FileStream to_file;
|
|
s64 filesize;
|
|
s64 fileSizeWithoutHeaderAndFooter;
|
|
s32 readsize;
|
|
s32 writesize;
|
|
|
|
NN_LOG("Copy File %ls\n", target_from.str().c_str());
|
|
|
|
// 読み込み対象ファイル開く
|
|
result = from_file.TryInitialize(target_from.str().c_str());
|
|
COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result);
|
|
|
|
// 読み込み対象ファイルのサイズ取得
|
|
result = from_file.TryGetSize(&filesize);
|
|
COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result);
|
|
|
|
if (encode)
|
|
{
|
|
if (!AddPathNameAndUpdateContext(list, target_to.str().c_str(), filesize, listContext))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
nn::crypto::SwAesCtrContext swAesCtrContext;
|
|
swAesCtrContext.Initialize(iv, common::key, sizeof(key));
|
|
|
|
size_t totalReadSize = 0;
|
|
nn::crypto::Sha256Context context;
|
|
context.Initialize();
|
|
|
|
// ファイルサイズをヘッダに書いておく
|
|
if (encode)
|
|
{
|
|
// 書き込み対象ファイル作成
|
|
result = nn::fs::TryCreateFile(target_tmp.str().c_str(), filesize);
|
|
COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result);
|
|
result = to_file.TryInitialize(target_tmp.str().c_str(),
|
|
nn::fs::OPEN_MODE_READ | nn::fs::OPEN_MODE_WRITE | nn::fs::OPEN_MODE_CREATE);
|
|
COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result);
|
|
|
|
// フルパスをハッシュに含める
|
|
context.Update(target_from.str().c_str(), target_from.str().size() * sizeof(wchar_t));
|
|
|
|
BackupDataHeader header;
|
|
BackupDataHeader enc;
|
|
std::memset(&header, 0, sizeof(header));
|
|
std::memset(&enc, 0, sizeof(enc));
|
|
header.size = filesize;
|
|
result = swAesCtrContext.Encrypt(&enc, &header, sizeof(header));
|
|
COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result);
|
|
context.Update(&enc, sizeof(enc));
|
|
s32 writeSize;
|
|
result = to_file.TryWrite(&writeSize, &enc, sizeof(enc), false);
|
|
COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result);
|
|
}
|
|
else
|
|
{
|
|
// ヘッダを読む
|
|
// ハッシュの計算は終わっているので復号化のみ
|
|
BackupDataHeader header;
|
|
BackupDataHeader dec;
|
|
std::memset(&header, 0, sizeof(header));
|
|
std::memset(&dec, 0, sizeof(dec));
|
|
s32 readSize;
|
|
result = from_file.TryRead(&readSize, &header, sizeof(header));
|
|
COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result);
|
|
|
|
swAesCtrContext.Decrypt(&dec, &header, sizeof(header));
|
|
COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result);
|
|
fileSizeWithoutHeaderAndFooter = dec.size;
|
|
|
|
// 書き込み対象ファイル作成
|
|
result = nn::fs::TryCreateFile(target_tmp.str().c_str(), fileSizeWithoutHeaderAndFooter);
|
|
COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result);
|
|
result = to_file.TryInitialize(target_tmp.str().c_str(),
|
|
nn::fs::OPEN_MODE_READ | nn::fs::OPEN_MODE_WRITE | nn::fs::OPEN_MODE_CREATE);
|
|
COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result);
|
|
}
|
|
|
|
while (1)
|
|
{
|
|
// バッファの後半半分を暗号・復号用に使う
|
|
result = from_file.TryRead(&readsize, buf, bufSize / 2);
|
|
COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result);
|
|
|
|
totalReadSize += readsize;
|
|
|
|
if (readsize == 0)
|
|
{
|
|
if (encode)
|
|
{
|
|
NN_LOG("Add CMAC %ls\n", target_from.str().c_str());
|
|
// SHA256を計算してCMACを付加する
|
|
bit8 sha256Hash[nn::crypto::Sha256Context::HASH_SIZE];
|
|
context.GetHash(sha256Hash);
|
|
|
|
bit8 cmac[nn::crypto::AES_CMAC_MAC_SIZE];
|
|
result = nn::crypto::CalculateAesCmacSw(cmac, sha256Hash, nn::crypto::Sha256Context::HASH_SIZE,
|
|
common::cmacKey);
|
|
COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result);
|
|
|
|
result = to_file.TryWrite(&writesize, cmac, sizeof(cmac));
|
|
COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result);
|
|
}
|
|
|
|
result = to_file.TryFlush();
|
|
COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result);
|
|
|
|
// 復号済みなら検証する
|
|
if (!encode)
|
|
{
|
|
if (!ConfirmFile(&from_file, &to_file, filesize, fileSizeWithoutHeaderAndFooter, buf, bufSize,
|
|
target_from.str().c_str(), target_tmp.str().c_str(), target_to.str().c_str()))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if (encode)
|
|
{
|
|
NN_LOG("EncryptSize = %d\n", readsize);
|
|
|
|
u8 paddingSize = 0;
|
|
AddPkcsPadding(&paddingSize, reinterpret_cast<bit8*>(buf), bufSize / 2, &readsize);
|
|
|
|
// 暗号化後SHA256を計算しつつ書き込み
|
|
result = swAesCtrContext.Encrypt(reinterpret_cast<bit8*>(buf) + bufSize / 2, buf, readsize);
|
|
COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result);
|
|
context.Update(reinterpret_cast<bit8*>(buf) + bufSize / 2, readsize);
|
|
|
|
result = to_file.TryWrite(&writesize, reinterpret_cast<bit8*>(buf) + bufSize / 2, readsize,
|
|
false);
|
|
COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result);
|
|
|
|
// 事前計算したファイルサイズに一致させるためパディング分減算
|
|
readsize -= paddingSize;
|
|
|
|
s_FinishedFileSize += readsize;
|
|
s_Progress = s_FinishedFileSize * 100 / s_TotalFileSize;
|
|
NN_LOG(
|
|
"finish = %lld, total = %lld, progress = %lld\n", s_FinishedFileSize, s_TotalFileSize, s_Progress);
|
|
}
|
|
else
|
|
{
|
|
// ハッシュ検証は通っているので復号化しつつ書き込み
|
|
// パディング以降は書き込まないよう書き込みサイズを変更する
|
|
|
|
NN_LOG("DecryptSize = %d\n", readsize);
|
|
result = swAesCtrContext.Decrypt(reinterpret_cast<bit8*>(buf) + bufSize / 2, buf, readsize);
|
|
COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result);
|
|
|
|
// パディングまで読んだかどうか
|
|
bool readDone = false;
|
|
// パディングまで読んでいたら書き込みサイズを減らす
|
|
if (fileSizeWithoutHeaderAndFooter < totalReadSize)
|
|
{
|
|
readsize -= totalReadSize - fileSizeWithoutHeaderAndFooter;
|
|
readDone = true;
|
|
}
|
|
|
|
result = to_file.TryWrite(&writesize, reinterpret_cast<bit8*>(buf) + bufSize / 2, readsize,
|
|
false);
|
|
COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result);
|
|
s_FinishedFileSize += readsize;
|
|
s_Progress = s_FinishedFileSize * 100 / s_TotalFileSize;
|
|
NN_LOG(
|
|
"finish = %lld, total = %lld, progress = %lld\n", s_FinishedFileSize, s_TotalFileSize, s_Progress);
|
|
|
|
// 読みきったので次のファイルへ
|
|
if (readDone)
|
|
{
|
|
result = to_file.TryFlush();
|
|
COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result);
|
|
|
|
if (!ConfirmFile(&from_file, &to_file, filesize, fileSizeWithoutHeaderAndFooter, buf,
|
|
bufSize, target_from.str().c_str(), target_tmp.str().c_str(),
|
|
target_to.str().c_str()))
|
|
{
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result);
|
|
|
|
}
|
|
}
|
|
to_file.Finalize();
|
|
|
|
from_file.Finalize();
|
|
}
|
|
}
|
|
|
|
from_dir.Finalize();
|
|
return true;
|
|
}
|
|
|
|
u32 GetProgress()
|
|
{
|
|
return s_Progress;
|
|
}
|
|
|
|
void InitializeTransferProgress(u64 totalSize)
|
|
{
|
|
s_TotalFileSize = totalSize;
|
|
s_FinishedFileSize = 0;
|
|
}
|
|
|
|
bool CalculateAndCompareCmac(nn::crypto::Sha256Context* context, bit8* sdCmac)
|
|
{
|
|
nn::Result result;
|
|
bit8 sha256Hash[nn::crypto::Sha256Context::HASH_SIZE];
|
|
bit8 cmac[nn::crypto::AES_CMAC_MAC_SIZE];
|
|
|
|
context->GetHash(sha256Hash);
|
|
context->Finalize();
|
|
|
|
result = nn::crypto::CalculateAesCmacSw(cmac, sha256Hash, sizeof(sha256Hash), common::cmacKey);
|
|
COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result);
|
|
|
|
return std::memcmp(cmac, sdCmac, sizeof(cmac)) == 0;
|
|
}
|
|
|
|
|
|
bool VerifyMac(nn::fs::FileInputStream* sdFile, nn::fs::FileStream* nandFile, s64 sdFileSize, s64 nandFileSize,
|
|
const wchar_t* nandPath, void* buf, size_t bufSize)
|
|
{
|
|
nn::Result result;
|
|
bit8 sdCmac[nn::crypto::AES_CMAC_MAC_SIZE];
|
|
|
|
// ハッシュが付加されていないとエラー
|
|
if(sdFileSize < nn::crypto::AES_CMAC_MAC_SIZE)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
s32 readSize;
|
|
// ハッシュを取得する
|
|
nn::crypto::Initialize();
|
|
result = sdFile->TrySetPosition(sdFileSize - nn::crypto::AES_CMAC_MAC_SIZE);
|
|
COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result);
|
|
|
|
result = sdFile->TryRead(&readSize, sdCmac, sizeof(sdCmac));
|
|
COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result);
|
|
|
|
sdFile->Finalize();
|
|
|
|
nandFile->SetPosition(0);
|
|
|
|
// ハッシュを計算する
|
|
nn::crypto::SwAesCtrContext swAesCtrContext;
|
|
swAesCtrContext.Initialize(iv, common::key, sizeof(key));
|
|
|
|
nn::crypto::Sha256Context context;
|
|
context.Initialize();
|
|
|
|
// NAND上のフルパスをハッシュに含めている
|
|
context.Update(nandPath, std::wcslen(nandPath) * sizeof(wchar_t));
|
|
|
|
BackupDataHeader header;
|
|
BackupDataHeader enc;
|
|
std::memset(&header, 0, sizeof(header));
|
|
std::memset(&enc, 0, sizeof(enc));
|
|
header.size = nandFileSize;
|
|
result = swAesCtrContext.Encrypt(&enc, &header, sizeof(header));
|
|
COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result);
|
|
context.Update(&enc, sizeof(enc));
|
|
|
|
bool retValue = false;
|
|
|
|
size_t totalReadSize = 0;
|
|
while (1)
|
|
{
|
|
result = nandFile->TryRead(&readSize, buf, bufSize / 2);
|
|
if (result.IsFailure())
|
|
{
|
|
COMMON_LOGGER_RESULT_IF_FAILED_WITH_LINE(result);
|
|
retValue = false;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
totalReadSize += readSize;
|
|
|
|
if (readSize == 0)
|
|
{
|
|
retValue = CalculateAndCompareCmac(&context, sdCmac);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
u8 paddingSize = 0;
|
|
AddPkcsPadding(&paddingSize, reinterpret_cast<bit8*>(buf), bufSize / 2, &readSize);
|
|
result = swAesCtrContext.Encrypt(reinterpret_cast<bit8*>(buf) + bufSize / 2, buf, readSize);
|
|
COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result);
|
|
context.Update(reinterpret_cast<bit8*>(buf) + bufSize / 2 , readSize);
|
|
|
|
if (result.IsFailure())
|
|
{
|
|
COMMON_LOGGER_RESULT_IF_FAILED_WITH_LINE(result);
|
|
retValue = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
nn::crypto::Finalize();
|
|
nandFile->Finalize();
|
|
|
|
return retValue;
|
|
}
|
|
|
|
bool ConfirmFile(nn::fs::FileInputStream* from_file, nn::fs::FileStream* to_file, s64 sdFileSize, s64 nandFileSize,
|
|
void* buf, size_t bufSize, const wchar_t* sdPath, const wchar_t* tmpPath, const wchar_t* truePath)
|
|
{
|
|
nn::Result result;
|
|
|
|
NN_LOG("Verify CMAC %ls\n", sdPath);
|
|
if (!VerifyMac(from_file, to_file, sdFileSize, nandFileSize, truePath, buf, bufSize))
|
|
{
|
|
// 検証に失敗したので削除する
|
|
s_FinishedFileSize -= nandFileSize;
|
|
COMMON_LOGGER("**********Verification Failed %s, Delete**********\n", GetCharStr(sdPath));
|
|
result = nn::fs::TryDeleteFile(tmpPath);
|
|
COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result);
|
|
}
|
|
else
|
|
{
|
|
NN_LOG("Verification Success %s, Rename\n", GetCharStr(sdPath));
|
|
// 書き込み先を削除する
|
|
result = nn::fs::TryDeleteFile(truePath);
|
|
if (result.IsFailure() && !nn::fs::ResultNotFound::Includes(result))
|
|
{
|
|
COMMON_LOGGER("**********Verification Failed %s, Delete**********\n", GetCharStr(sdPath));
|
|
COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result);
|
|
}
|
|
|
|
// 正しいファイル名にリネームする
|
|
result = nn::fs::TryRenameFile(tmpPath, truePath);
|
|
COMMON_LOGGER_RESULT_IF_FAILED_WITH_LINE(result);
|
|
if (result.IsFailure())
|
|
{
|
|
COMMON_LOGGER("**********Verification Failed %s, Delete**********\n", GetCharStr(sdPath));
|
|
s_FinishedFileSize -= nandFileSize;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//! @brief 入力データの末尾16バイトをPKCS5で必要バイト数パディングする
|
|
//! @param[out] paddingSize パディングしたバイト数
|
|
//! @param[in] buf 入力データの入ったバッファ
|
|
//! @param[in] bufSize バッファサイズ
|
|
//! @param[inout] readSize バッファに読み込んだバイト数。書き込み時に参照するためパディングしたら増加させる
|
|
void AddPkcsPadding(u8* paddingSize, void* buf, size_t bufSize, s32* readSize)
|
|
{
|
|
if (*readSize < bufSize)
|
|
{
|
|
if ((*readSize % AES_BLOCK_SIZE) != 0)
|
|
{
|
|
*paddingSize = AES_BLOCK_SIZE - *readSize % AES_BLOCK_SIZE;
|
|
std::memset(reinterpret_cast<bit8*>(buf) + *readSize, *paddingSize, *paddingSize);
|
|
*readSize += *paddingSize;
|
|
}
|
|
}
|
|
}
|
|
|
|
//! @brief パスにnimのセーブデータディレクトリが含まれているかどうかを返します
|
|
//! @param[in] str パス
|
|
//! @return パスにnimのセーブデータディレクトリが含まれているか
|
|
bool ContainsNimSaveDataDir(const wchar_t* str)
|
|
{
|
|
return std::wcsstr(str, common::NIM_SAVEDATA_DIRECTORY_NAME) != NULL;
|
|
}
|
|
|
|
//! @brief ファイルに文字列とサイズをカンマ区切り、改行付きで追加します
|
|
//! @param[in] file 文字列を出力したいファイル
|
|
//! @param[in] str 入力文字列
|
|
//! @param[in] fileSize サイズ
|
|
//! @param[in] context SHA-256計算用コンテキスト
|
|
bool AddPathNameAndUpdateContext(nn::fs::FileOutputStream* file, const wchar_t *str, s64 fileSize,
|
|
nn::crypto::Sha256Context* context)
|
|
{
|
|
nn::Result result;
|
|
s32 writeSize;
|
|
|
|
if(ContainsNimSaveDataDir(str))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
std::string output(GetCharStr(str));
|
|
result = file->TryWrite(&writeSize, output.c_str(), output.size(), true);
|
|
COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result);
|
|
context->Update(output.c_str(), output.size());
|
|
|
|
char comma = ',';
|
|
result = file->TryWrite(&writeSize, &comma, sizeof(comma), true);
|
|
COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result);
|
|
context->Update(&comma, sizeof(comma));
|
|
|
|
char sizeStr[10];
|
|
std::memset(sizeStr, 0, sizeof(sizeStr));
|
|
s32 sizeStrSize = std::snprintf(sizeStr, sizeof(sizeStr), "%lld", fileSize);
|
|
result = file->TryWrite(&writeSize, sizeStr, sizeStrSize, true);
|
|
COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result);
|
|
context->Update(sizeStr, sizeStrSize);
|
|
|
|
char newLine = '\n';
|
|
result = file->TryWrite(&writeSize, &newLine, sizeof(newLine), true);
|
|
COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result);
|
|
context->Update(&newLine, sizeof(newLine));
|
|
|
|
return true;
|
|
}
|
|
|
|
}
|