ctr_test_tools/TwlBkpCheck/Windows/FalsifyTwlBackup/main.cs
n2460 f20e008a2d FalsifyTwlBackup:カテゴリ毎に関数分けを行った。
git-svn-id: file:///Volumes/Transfer/gigaleak_20231201/2020-09-30%20-%20paladin.7z/paladin/ctr_test_tools@29 6b0af911-cb57-b745-895f-eec5701120e1
2011-10-20 02:37:08 +00:00

678 lines
26 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Linq;
using System.Text;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using TwlBackupBlock;
// TODO もっとアサートを入れましょう
namespace FalsifyTwlBackup
{
partial class Program
{
// ブロックのインデックス
private const int INDEX_BANNER = 0;
private const int INDEX_HEADER = 1;
private const int INDEX_SIGNATURE = 2;
private const int INDEX_TMD = 3;
private const int INDEX_CONTENT = 4;
private const int INDEX_PUBLIC_SAVE = 5;
private const int INDEX_SUB_BANNER = 6;
// データの挿入位置
private const int NONE = -1;
private const int FRONT_BANNER = 10; // ブロック内データ本体の前
private const int FRONT_HEADER = 11;
private const int FRONT_SIGNATURE = 12;
private const int FRONT_TMD = 13;
private const int FRONT_CONTENT = 14;
private const int FRONT_PUBLIC_SAVE = 15;
private const int FRONT_SUB_BANNER = 16;
private const int REAR_BANNER = 20; // ブロック内データ本体の後
private const int REAR_HEADER = 21;
private const int REAR_SIGNATURE = 22;
private const int REAR_TMD = 23;
private const int REAR_CONTENT = 24;
private const int REAR_PUBLIC_SAVE = 25;
private const int REAR_SUB_BANNER = 26;
private const int REAR_DATA = 99; // データ全体の後
// 改ざんパターンの引き数
private const int ALL = 0;
private const int CAT100 = 1;
private const int CAT200 = 2;
private const int CAT300 = 3;
private const int CAT400 = 4;
private const int CAT500 = 5;
private const int VERIFY = 9; // TORIAEZU : 上記が Mode == Pattern なのが引っかかる・・・
// 各カテゴリでの最小番号
private const int MIN_OF_CAT100 = 100;
private const int MIN_OF_CAT200 = 200;
private const int MIN_OF_CAT300 = 300;
private const int MIN_OF_CAT400 = 400;
private const int MIN_OF_CAT500 = 500;
// 各カテゴリでの最大番号
private const int MAX_OF_CAT100 = 118;
private const int MAX_OF_CAT200 = 214;
private const int MAX_OF_CAT300 = 303;
private const int MAX_OF_CAT400 = 415;
private const int MAX_OF_CAT500 = 515;
private static readonly int[,] NUM_OF_PATTERN = new int[,]
{
{100, MAX_OF_CAT100},
{200, MAX_OF_CAT200},
{300, MAX_OF_CAT300},
{400, MAX_OF_CAT400},
{500, MAX_OF_CAT500}
};
// MACとハッシュセット更新の有無
private const int NC_MAC_AND_HASHSET = 0;
private const int CHG_MAC_AND_HASHSET = 1;
private const int BLOCK_NUM = 7; // バックアップデータのブロックの個数
private const int IMPROPER_DATA_SIZE = 16 * 1024; // 不正なデータのサイズ(16 KB)
private const int KEY_SIZE = 16; // 鍵のサイズ(16 Byte)
private const int HASH_SIZE = 32; // ハッシュのサイズ(32 Byte)
private const int AES_BLOCK_SIZE = 16 * 8; // 暗号化のブロックサイズ(16Byte)
private const string EXTENSION = ".bin"; // TwlBackupファイルの拡張子
/// <summary>
/// 使用方法を表示します。
/// </summary>
static void Usage()
{
Console.WriteLine("Usage: ");
Console.WriteLine("./FalsifyingTwlBackup.exe BACKUPFILE_NAME KEYFILE_NAME MACKEYFALE_NAME MODE");
Console.WriteLine(" BACKUPFILE_NAME : *.bin");
Console.WriteLine(" KEYFILE_NAME : *.txt");
Console.WriteLine(" MACKEYFALE_NAME : *.txt\n");
Console.WriteLine(" MODE");
Console.WriteLine(" -all (default) : output all falsifying pattern");
Console.WriteLine(" -cat CAT_NUM : output all pattern of CAT_NUM category");
Console.WriteLine(" ex) -cat 100 -> falsifying 100,101,102....");
Console.WriteLine(" -each PAT_NUM : output PAT_NUM pattern");
Console.WriteLine(" ex) -each 204 -> falsifying 204 only");
Console.WriteLine(" -verify : verify backup file");
Environment.Exit(-1);
}
/// <summary>
/// 拡張子の判別を行います。
/// </summary>
/// <param name="args">確認対象となるファイル名</param>
/// <param name="extension">拡張子</param>
static void CheckExtension(string args, string extension)
{
Debug.Assert(args.Length > extension.Length);
if (args.ToLower().EndsWith(extension) == false)
{
Console.WriteLine("{0} is not [\"{1}\"] file!", args, extension);
Usage();
}
return;
}
/// <summary>
/// ファイルの有無を確認します。
/// </summary>
/// <param name="args">確認対象となるファイルのパス</param>
static void CheckFileExists(string args)
{
Debug.Assert(args.Length != 0);
if (!File.Exists(args))
{
Console.WriteLine("\"{0}\" is not exists!\n", args);
Usage();
}
}
/// <summary>
/// コマンドライン引数のオプションから処理を決定するためのフラグを立てる
/// </summary>
/// <param name="args">コマンドライン引数の格納された文字列</param>
/// <returns>処理判別のためのフラグ</returns>
static int GetFalsifyingType(string[] args)
{
Debug.Assert(args.Length >= 3);
if (args.Length > 3)
{
// -allのとき
if (args[3].ToUpper().TrimStart('-') == "ALL")
{
return ALL;
}
// -catのとき
else if (args[3].ToUpper().TrimStart('-') == "CAT")
{
if (4 >= args.Length) // CATなのに次の引数が無かったら帰れ
{
Usage();
}
switch (args[4])
{
case "100": return CAT100;
case "200": return CAT200;
case "300": return CAT300;
case "400": return CAT400;
case "500": return CAT500;
default: Usage(); break; // never reach
}
}
// -eachのとき
else if (args[3].ToUpper().TrimStart('-') == "EACH")
{
if (4 >= args.Length) // EACHなのに次の引数が無かったら帰れ
{
Usage();
}
// TODO 存在しない処理番号をはじくように
else
{
return Convert.ToInt16(args[4]);
}
}
// -verify
else if (args[3].ToUpper().TrimStart('-') == "VERIFY")
{
return VERIFY;
}
}
// それ以外
return ALL;
}
/// <summary>
/// 指定されたbyteサイズで、0のみのbyte列を作成します。
/// </summary>
/// <param name="dataSize">作成するデータのサイズ[単位:byte]</param>
/// <returns>作成したデータのbyte列</returns>
// TODO 不正データの作成方法を選べるようにしてもいいかも
static byte[] CreateImproperData(int dataSize)
{
byte[] data = new byte[dataSize];
for (int i = 0; i < data.Length; i++)
{
//data[i] = (byte)(i);
data[i] = 0;
}
return data;
}
/// <summary>
/// データの差し替えを行います。
/// </summary>
/// <param name="dataBlocks">上書き対象となるデータ本体</param>
/// <param name="impData">上書きする不正データ</param>
static void ReplaceImproperData(AbstractBody dataBlocks, byte[] impData)
{
for (int i = 0; i < impData.Length; i++)
{
if (i < dataBlocks.Length) // 上書き先のデータサイズを超えませんように
{
dataBlocks[i] = impData[i];
}
}
return;
}
/// <summary>
/// ※未使用
/// 暗号化に使用するIVを作成します。
/// AES方式で、ブロックサイズはAES_BLOCK_SIZE[bit]と同じです。
/// </summary>
/// <returns>生成したivのbyte列</returns>
static byte[] GenerateNewIv()
{
// 暗号化用IV作成
/*
* [参考] C#.NET FrameworkのAESを使ってファイルを暗号化してみる
* http://hibara.sblo.jp/article/43505136.html
*/
System.Security.Cryptography.AesCryptoServiceProvider
aes = new System.Security.Cryptography.AesCryptoServiceProvider();
aes.BlockSize = AES_BLOCK_SIZE; // AESはブロックサイズ、キー長ともに16byte
aes.GenerateIV(); // IVの設定ブロックサイズと同サイズ = 16byte
byte[] byteIV = aes.IV; // IVをバイト列に
return byteIV;
}
/// <summary>
/// 署名のハッシュセットを変更します。
/// </summary>
/// <param name="dataBlocks">変更対象となる署名ブロックを有するBlocks</param>
/// <param name="hash">新しいハッシュ値</param>
/// <param name="index">変更するブロックのインデックス</param>
static void ChangeHashSet(Blocks dataBlocks, byte[] hash, int index)
{
Debug.Assert((index <= BLOCK_NUM - 1) && (index >= 0));
SignatureBody signature;
signature = (SignatureBody)dataBlocks.signature.body;
switch (index)
{
case INDEX_BANNER:
hash.CopyTo(signature.digest.banner, 0);
break;
case INDEX_HEADER:
hash.CopyTo(signature.digest.header, 0);
break;
case INDEX_TMD:
hash.CopyTo(signature.digest.section[(int)Utility.SectionIndex.TMD], 0);
break;
// TODO : マルチコンテンツ対応
case INDEX_CONTENT:
hash.CopyTo(signature.digest.section[(int)Utility.SectionIndex.CONTENT], 0);
break;
case INDEX_PUBLIC_SAVE:
hash.CopyTo(signature.digest.section[(int)Utility.SectionIndex.PUBLIC_SAVE], 0);
break;
case INDEX_SUB_BANNER:
hash.CopyTo(signature.digest.section[(int)Utility.SectionIndex.SUB_BANNER], 0);
break;
// TODO : Private Save 追加
default:
Debug.Assert(false);
break;
}
}
/// <summary>
/// byte列を結合します。
/// </summary>
/// <param name="front">前半部分に来るbyte列</param>
/// <param name="rear">後半部分に来るbyte列</param>
/// <returns>連結後のbyte列</returns>
static byte[] MergeByteArray(byte[] front, byte[] rear)
{
return front.Concat(rear).ToArray();
}
/// <summary>
/// byte列をbinファイルに出力します。
/// </summary>
/// <param name="improperNo">ファイル名となる改ざんパターンの番号</param>
/// <param name="outFolderPath">出力ディレクトリのパス</param>
/// <param name="data">ファイルに出力するデータ</param>
static void OutputFalsifiedData(int improperNo, string outFolderPath, byte[] data)
{
string outFalsifiedDataPath = Convert.ToString(improperNo) + EXTENSION; // ファイル名を改ざん番号から生成
outFalsifiedDataPath = Path.Combine(outFolderPath, outFalsifiedDataPath); // 出力ディレクトリとファイル名を結合
File.WriteAllBytes(outFalsifiedDataPath, data); // 出力
}
/// <summary>
/// データ本体とMACとIVをひとつのブロックに結合します。
/// 必要に応じて不正データを挿入します。
/// </summary>
/// <param name="dataBlocks">暗号化するBlockを持つBlocks</param>
/// <param name="index">暗号化するBlockのインデックス</param>
/// <param name="prop">バックアップデータに関するインスタンス</param>
/// <param name="impData">挿入する不正データ</param>
/// <param name="insertPos">挿入する位置</param>
/// <param name="byteMac">格納するMAC</param>
/// <param name="byteHash">MACを算出するためのハッシュ値</param>
/// <returns>データ本体、MAC、IVを順に結合したbyte列</returns>
static byte[] EncryptBlock(Blocks dataBlocks, int index, Properties prop, byte[] impData, int insertPos, byte[] byteMac, byte[] byteHash = null)
{
byte[] encryptedBlock = new byte[0];
// (データ本体の前に不正データ挿入)
if (insertPos >= FRONT_BANNER && insertPos <= FRONT_SUB_BANNER)
{
if (index == insertPos - 10)
{
encryptedBlock = MergeByteArray(encryptedBlock, impData);
}
}
// データ本体を暗号化
encryptedBlock = MergeByteArray(encryptedBlock, dataBlocks[index].body.GetBytes()); // bodyを暗号化
// (データ本体の後に不正データ挿入)
if (insertPos >= REAR_BANNER && insertPos <= REAR_SUB_BANNER)
{
if (index == insertPos - 20)
{
encryptedBlock = MergeByteArray(encryptedBlock, impData);
}
}
if (byteHash != null)
{
byte[] buff = Utility.GetSha256(encryptedBlock); // 平文からSHA256ハッシュ算出
buff.CopyTo(byteHash, 0);
byteMac = Utility.GetAesCmac(byteHash, prop.macKeyData); // SHA256ハッシュからMACを算出
}
encryptedBlock = Utility.EncryptBody(encryptedBlock, prop.keyData, dataBlocks[index].iv); // データ本体を暗号化
encryptedBlock = MergeByteArray(encryptedBlock, byteMac); // MACを結合
encryptedBlock = MergeByteArray(encryptedBlock, dataBlocks[index].iv); // IVを結合
// (データブロックの後に不正データ挿入)
if (index == INDEX_SUB_BANNER && insertPos == REAR_DATA)
{
encryptedBlock = MergeByteArray(encryptedBlock, impData);
}
return encryptedBlock;
}
/// <summary>
/// 各ブロックを暗号化した後、1つの byte 列に結合します。
/// </summary>
/// <param name="dataBlocks">暗号化するデータ</param>
/// <param name="prop">バックアップデータに関するインスタンス</param>
/// <param name="impData">不正データ</param>
/// <param name="insertPos">不正データを挿入する位置</param>
/// <param name="chgMacHash">ハッシュセット更新の有無</param>
/// <returns>全てのBlockを暗号化し、連結したbyte列</returns>
static byte[] MergeEncryptBody(Blocks dataBlocks, Properties prop, byte[] impData, int insertPos, int chgMacHash)
{
byte[] outData = new byte[0];
byte[][] encryptedBlocks = new byte[BLOCK_NUM][];
byte[][] byteHash = new byte[BLOCK_NUM][];
byte[][] byteMac = new byte[BLOCK_NUM][];
//----------------------
// 各ブロックを暗号化
// ( MAC & HASH SET更新する場合 )
if (chgMacHash == CHG_MAC_AND_HASHSET)
{
for (int i = 0; i < BLOCK_NUM; i++) // 署名部以外を暗号化byte列に結合
{
byteHash[i] = new byte[HASH_SIZE];
if (i != INDEX_SIGNATURE)
{
encryptedBlocks[i] = EncryptBlock(dataBlocks, i, prop, impData, insertPos, byteMac[i], byteHash[i]); // ブロックを暗号化&結合
ChangeHashSet(dataBlocks, byteHash[i], i); // ハッシュセットを更新
}
}
// 最後に署名部を暗号化byte列に結合
byteHash[INDEX_SIGNATURE] = Utility.GetSha256(dataBlocks[INDEX_SIGNATURE].body.GetBytes()); // 平文からSHA256ハッシュ算出
byteMac[INDEX_SIGNATURE] = Utility.GetAesCmac(byteHash[INDEX_SIGNATURE], prop.macKeyData); // SHA256ハッシュからMACを算出
encryptedBlocks[INDEX_SIGNATURE] = EncryptBlock(dataBlocks, INDEX_SIGNATURE, prop, impData, insertPos, byteMac[INDEX_SIGNATURE]);
}
// ( MAC & HASH SET更新しない場合 )
else
{
for (int i = 0; i < BLOCK_NUM; i++)
{
encryptedBlocks[i] = EncryptBlock(dataBlocks, i, prop, impData, insertPos, dataBlocks[i].mac); // ブロックを暗号化&結合
}
}
//----------------------
// 暗号化した各ブロックを結合
for (int i = 0; i < BLOCK_NUM; i++)
{
outData = MergeByteArray(outData, encryptedBlocks[i]);
}
return outData;
}
// static void FalsifyingData(int improperNo, Properties prop, byte[] improperData)
//------------------------------------------------------------
// パターン番号に応じた改ざんを実行
// [in] int improperNo : 改ざんパターン番号
// [in] Properties prop : バックアップデータに関するインスタンス
// [in] byte[] improperData : 改ざんに使用する不正データ
//------------------------------------------------------------
/// <summary>
/// パターン番号に応じた改ざんを実行
/// </summary>
/// <param name="improperNo"></param>
/// <param name="prop"></param>
/// <param name="improperData"></param>
static void FalsifyingData(int improperNo, Properties prop, byte[] improperData)
{
if (MIN_OF_CAT100 <= improperNo && improperNo <= MAX_OF_CAT100)
{
FalsifyingDataCategory1xx(improperNo, prop, improperData);
}
else if (MIN_OF_CAT200 <= improperNo && improperNo <= MAX_OF_CAT200)
{
FalsifyingDataCategory2xx(improperNo, prop, improperData);
}
else if (MIN_OF_CAT300 <= improperNo && improperNo <= MAX_OF_CAT300)
{
FalsifyingDataCategory3xx(improperNo, prop, improperData);
}
else if (MIN_OF_CAT400 <= improperNo && improperNo <= MAX_OF_CAT400)
{
FalsifyingDataCategory4xx(improperNo, prop, improperData);
}
else if (MIN_OF_CAT500 <= improperNo && improperNo <= MAX_OF_CAT500)
{
FalsifyingDataCategory5xx(improperNo, prop, improperData);
}
else
{
FalsifyingDataCategory9xx(improperNo, prop, improperData);
}
}
// static void SwitchFalsifyingPattern(int falsifyingType, Properties prop)
//------------------------------------------------------------
// 作成する改ざんデータのパターンを決定。
// [in] int falsifyingType : 出力する改ざんデータのパターン
// [in] Properties prop : バックアップデータに関するインスタンス
//------------------------------------------------------------
/// <summary>
///
/// </summary>
/// <param name="falsifyingType"></param>
/// <param name="prop"></param>
static void SwitchFalsifyingPattern(int falsifyingType, Properties prop)
{
// 改ざんに使用する不正なデータ作成
byte[] improperData = CreateImproperData(IMPROPER_DATA_SIZE);
switch (falsifyingType)
{
case ALL: // 全パターン
Console.WriteLine(" Falsifying \"all pattern\".");
for (int i = 0; i < NUM_OF_PATTERN.GetLength(0); i++)
{
for (int j = NUM_OF_PATTERN[i, 0]; j <= NUM_OF_PATTERN[i, 1]; j++)
{
FalsifyingData(j, prop, improperData);
Console.WriteLine(" Complete falsifying No.{0}.", j);
}
Console.WriteLine(" Complete falsifying Cat.{0}.\n", NUM_OF_PATTERN[i, 0]);
}
break;
case CAT100: // 1xx系列全て
Console.WriteLine(" Falsifying category No.{0}.", NUM_OF_PATTERN[CAT100 - 1, 0]);
for (int i = 100; i <= MAX_OF_CAT100; i++)
{
FalsifyingData(i, prop, improperData);
Console.WriteLine(" Complete falsifying No.{0}.", i);
}
Console.WriteLine(" Complete falsifying Cat.{0}.\n", NUM_OF_PATTERN[CAT100 - 1, 0]);
break;
case CAT200: // 2xx系列全て
Console.WriteLine(" Falsifying category No.{0}.", NUM_OF_PATTERN[CAT200 - 1, 0]);
for (int i = 200; i <= MAX_OF_CAT200; i++)
{
FalsifyingData(i, prop, improperData);
Console.WriteLine(" Complete falsifying No.{0}.", i);
}
Console.WriteLine(" Complete falsifying Cat.{0}.\n", NUM_OF_PATTERN[CAT200 - 1, 0]);
break;
case CAT300: // 3xx系列全て
Console.WriteLine(" Falsifying category No.{0}.", NUM_OF_PATTERN[CAT300 - 1, 0]);
for (int i = 300; i <= MAX_OF_CAT300; i++)
{
FalsifyingData(i, prop, improperData);
Console.WriteLine(" Complete falsifying No.{0}.", i);
}
Console.WriteLine(" Complete falsifying Cat.{0}.\n", NUM_OF_PATTERN[CAT300 - 1, 0]);
break;
case CAT400: // 4xx系列全て
Console.WriteLine(" Falsifying category No.{0}.", NUM_OF_PATTERN[CAT400 - 1, 0]);
for (int i = 400; i <= MAX_OF_CAT400; i++)
{
FalsifyingData(i, prop, improperData);
Console.WriteLine(" Complete falsifying No.{0}.", i);
}
Console.WriteLine(" Complete falsifying Cat.{0}.\n", NUM_OF_PATTERN[CAT400 - 1, 0]);
break;
case CAT500: // 5xx系列全て
Console.WriteLine(" Falsifying category No.{0}.", NUM_OF_PATTERN[CAT500 - 1, 0]);
for (int i = 500; i <= MAX_OF_CAT500; i++)
{
FalsifyingData(i, prop, improperData);
Console.WriteLine(" Complete falsifying No.{0}.", i);
}
Console.WriteLine(" Complete falsifying Cat.{0}.\n", NUM_OF_PATTERN[CAT500 - 1, 0]);
break;
default: // 1パターンのみを個別指定
Console.WriteLine(" Falsifying pattern No.{0}.", falsifyingType);
FalsifyingData(falsifyingType, prop, improperData);
Console.WriteLine(" Complete falsifying No.{0}.\n", falsifyingType);
break;
}
}
//==================================================
// メイン関数
//==================================================
static void Main(string[] args)
{
// コマンドライン引数の数チェック
if (args.Length < 3) // データ、ブロック鍵、MAC鍵
{
Usage();
return;
}
else
{
// 拡張子の判定
CheckExtension(args[0], ".bin"); // 入力ファイル名
CheckExtension(args[1], ".txt"); // ブロック鍵ファイル名
CheckExtension(args[2], ".txt"); // MAC鍵ファイル名
// ファイルが存在するかを確認
for (int i = 0; i < 3; i++)
{
CheckFileExists(args[i]);
}
}
string twlBackupName = args[0];
string keyName = args[1];
string macKeyData = args[2];
int falsifyingType = GetFalsifyingType(args); // オプションがあれば、改ざんのパターン決定
Properties prop = new Properties(); // インスタンスの作成
byte[] twlBackupData = File.ReadAllBytes(twlBackupName); // バックアップデータの読み込み
// 鍵データの読み込み
string[] keyString = File.ReadAllLines(keyName);
prop.keyData = new byte[KEY_SIZE];
for (int i = 0; i < prop.keyData.Length; i++)
{
prop.keyData[i] = Convert.ToByte(keyString[i], 16);
}
// MAC鍵データの読み込み
string[] macKeyString = File.ReadAllLines(macKeyData);
prop.macKeyData = new byte[KEY_SIZE];
for (int i = 0; i < prop.keyData.Length; i++)
{
prop.macKeyData[i] = Convert.ToByte(macKeyString[i], 16);
}
// ファイル名の出力 + 鍵データの dump
Console.WriteLine("Twl Backup File : {0}", twlBackupName);
Console.WriteLine("Block Key File : {0} ({1})", keyName, dumpArray(prop.keyData));
Console.WriteLine("MAC Key File : {0} ({1})", macKeyData, dumpArray(prop.macKeyData));
// TORIAEZU : ひとまず VERIFY だったら検証して終了
if (falsifyingType == VERIFY)
{
if (Utility.Verify(twlBackupData, prop.keyData, prop.macKeyData))
{
Console.WriteLine("{0} は検証に成功しました。", twlBackupName);
}
Environment.Exit(0);
}
// バックアップデータを復号化
prop.decryptedBlocks = Utility.DecryptBackupData(twlBackupData, prop.keyData);
// 出力フォルダを作成
prop.SetOutputFolderPath(twlBackupName);
//==========================================
// 改ざん&ファイル出力
Console.WriteLine("Start ------------");
SwitchFalsifyingPattern(falsifyingType, prop);
Console.WriteLine("Complete --------");
Console.WriteLine("Output Dir: {0}", prop.outFolderPath);
}
// byte 配列の 16 進表示
static string dumpArray(byte[] array)
{
string str = "";
foreach (byte b in array)
{
str += String.Format("{0,0:X2} ", b);
}
return str.TrimEnd();
}
}
}