削除予定だったけどこのまま置いておく。非常時のために
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using Charlotte.Commons; using Charlotte.Tools; namespace Charlotte.Tests { public class Test0007 { private static readonly string PW = "chocolate.taruto"; private static readonly string BKTRG_DEV_DIR = @"C:\Dev"; private static readonly string BKTRG_MEMO_DIR = @"C:\home\memo"; private static readonly string CLUSTER_FILE = @"C:\temp\S-Nxxx-IIIIIIII.dll.out"; public void Test01() { if (!Directory.Exists(BKTRG_DEV_DIR)) throw null; if (!Directory.Exists(BKTRG_MEMO_DIR)) throw null; using (WorkingDir wd = new WorkingDir()) { string midDir = wd.MakePath(); string midDevDir = Path.Combine(midDir, "Dev"); string midMemoDir = Path.Combine(midDir, "memo"); string midFile_01 = wd.MakePath(); string midFile_02 = wd.MakePath(); SCommon.CreateDir(midDir); SCommon.CreateDir(midDevDir); SCommon.CreateDir(midMemoDir); SCommon.CopyDir(BKTRG_DEV_DIR, midDevDir); SCommon.CopyDir(BKTRG_MEMO_DIR, midMemoDir); RemoveBinaryFiles(midDevDir); DirToClusterFileTools.DirToClusterFile(midDir, midFile_01); using (RingCipher rc = new RingCipher(SCommon.GetSHA512(Encoding.ASCII.GetBytes(PW)))) { byte[] fileData = File.ReadAllBytes(midFile_01); fileData = rc.Encrypt(fileData); File.WriteAllBytes(midFile_02, fileData); } File.Copy(midFile_02, Path.Combine(SCommon.GetOutputDir(), Path.GetFileName(CLUSTER_FILE))); } } private void RemoveBinaryFiles(string devDir) { foreach (string file in Directory.GetFiles(devDir, "*.sln", SearchOption.AllDirectories)) { string solDir = SCommon.ToParentPath(file); string vsDir = Path.Combine(solDir, ".vs"); Console.WriteLine("RM " + vsDir); // cout SCommon.DeletePath(vsDir); foreach (string dir in Directory.GetDirectories(solDir, "bin", SearchOption.AllDirectories) .Concat(Directory.GetDirectories(solDir, "obj", SearchOption.AllDirectories))) { Console.WriteLine("RM " + dir); // cout SCommon.DeletePath(dir); } } foreach (string file in Directory.GetFiles(devDir, "*.exe", SearchOption.AllDirectories)) { Console.WriteLine("RM " + file); // cout File.WriteAllBytes(file, SCommon.EMPTY_BYTES); } } public void Test02() { if (!File.Exists(CLUSTER_FILE)) throw null; using (WorkingDir wd = new WorkingDir()) { string midFile = wd.MakePath(); using (RingCipher rc = new RingCipher(SCommon.GetSHA512(Encoding.ASCII.GetBytes(PW)))) { byte[] fileData = File.ReadAllBytes(CLUSTER_FILE); fileData = rc.Decrypt(fileData); File.WriteAllBytes(midFile, fileData); } DirToClusterFileTools.ClusterFileToDir(midFile, SCommon.NextOutputPath()); } } } }
RingCipher
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Security.Cryptography; using Charlotte.Commons; namespace Charlotte.Tools { /// <summary> /// メモリブロックの暗号化・復号を行う。 /// memo: RingCipher.cs と RingCipherFile.cs は同じ暗号化・復号を行うのでデータ互換性がある。 /// </summary> public class RingCipher : IDisposable { private AESCipher[] Transformers; /// <summary> /// 鍵の分割: /// -- 16 --> 16 /// -- 24 --> 24 /// -- 32 --> 32 /// -- 40 --> 24, 16 /// -- 48 --> 32, 16 /// -- 56 --> 32, 24 /// -- 64 --> 32, 32 /// -- 72 --> 32, 24, 16 /// -- 80 --> 32, 32, 16 /// -- 88 --> 32, 32, 24 /// -- 96 --> 32, 32, 32 /// -- 104 --> 32, 32, 24, 16 /// -- 112 --> 32, 32, 32, 16 /// -- 120 --> 32, 32, 32, 24 /// -- 128 --> 32, 32, 32, 32 /// -- ... /// </summary> /// <param name="rawKey">鍵</param> public RingCipher(byte[] rawKey) { if ( rawKey == null || rawKey.Length < 16 || rawKey.Length % 8 != 0 ) throw new ArgumentException(); List<AESCipher> dest = new List<AESCipher>(); for (int offset = 0; offset < rawKey.Length; ) { int size = rawKey.Length - offset; if (48 <= size) size = 32; else if (size == 40) size = 24; dest.Add(new AESCipher(P_GetBytesRange(rawKey, offset, size))); offset += size; } this.Transformers = dest.ToArray(); } public void Dispose() { if (this.Transformers != null) { foreach (AESCipher transformer in this.Transformers) transformer.Dispose(); this.Transformers = null; } } /// <summary> /// 暗号化を行う。 /// </summary> /// <param name="data">入力データ</param> /// <returns>出力データ</returns> public byte[] Encrypt(byte[] data) { if (data == null) throw new ArgumentException(); data = AddPadding(data); data = AddCRandPart(data, 64); data = AddHash(data); data = AddCRandPart(data, 16); foreach (AESCipher transformer in this.Transformers) EncryptRingCBC(data, transformer); return data; } /// <summary> /// 復号を行う。 /// データの破損や鍵の不一致も含め復号に失敗すると例外を投げる。 /// </summary> /// <param name="data">入力データ</param> /// <returns>出力データ</returns> public byte[] Decrypt(byte[] data) { if ( data == null || data.Length < 16 + 64 + 64 + 16 || // ? AddPadding-したデータ_(最短)16 + cRandPart_64 + hash_64 + cRandPart_16 より短い data.Length % 16 != 0 ) throw new Exception("入力データの破損を検出しました。"); data = P_GetBytesRange(data, 0, data.Length); // 複製 foreach (AESCipher transformer in this.Transformers.Reverse()) DecryptRingCBC(data, transformer); data = RemoveCRandPart(data, 16); data = RemoveHash(data); data = RemoveCRandPart(data, 64); data = RemovePadding(data); return data; } private static byte[] AddPadding(byte[] data) { int size = 16 - data.Length % 16; byte[] padding = SCommon.CRandom.GetBytes(size); size--; padding[size] &= 0xf0; padding[size] |= (byte)size; data = SCommon.Join(new byte[][] { data, padding }); return data; } private static byte[] RemovePadding(byte[] data) { int size = data[data.Length - 1] & 0x0f; size++; data = P_GetBytesRange(data, 0, data.Length - size); return data; } private static byte[] AddCRandPart(byte[] data, int size) { byte[] padding = SCommon.CRandom.GetBytes(size); data = SCommon.Join(new byte[][] { data, padding }); return data; } private static byte[] RemoveCRandPart(byte[] data, int size) { data = P_GetBytesRange(data, 0, data.Length - size); return data; } private const int HASH_SIZE = 64; private static byte[] AddHash(byte[] data) { byte[] hash = SCommon.GetSHA512(data); if (hash.Length != HASH_SIZE) throw null; // never data = SCommon.Join(new byte[][] { data, hash }); return data; } private static byte[] RemoveHash(byte[] data) { byte[] hash = P_GetBytesRange(data, data.Length - HASH_SIZE, HASH_SIZE); data = P_GetBytesRange(data, 0, data.Length - HASH_SIZE); byte[] recalcHash = SCommon.GetSHA512(data); if (SCommon.Comp(hash, recalcHash, SCommon.Comp) != 0) throw new Exception("入力データの破損または鍵の不一致を検出しました。"); return data; } private static void EncryptRingCBC(byte[] data, AESCipher transformer) { byte[] input = new byte[16]; byte[] output = new byte[16]; Array.Copy(data, data.Length - 16, output, 0, 16); for (int offset = 0; offset < data.Length; offset += 16) { Array.Copy(data, offset, input, 0, 16); XorBlock(input, output); transformer.EncryptBlock(input, output); Array.Copy(output, 0, data, offset, 16); } } private static void DecryptRingCBC(byte[] data, AESCipher transformer) { byte[] input = new byte[16]; byte[] output = new byte[16]; Array.Copy(data, data.Length - 16, input, 0, 16); for (int offset = data.Length - 16; 0 <= offset; offset -= 16) { transformer.DecryptBlock(input, output); Array.Copy(data, (offset + data.Length - 16) % data.Length, input, 0, 16); XorBlock(output, input); Array.Copy(output, 0, data, offset, 16); } } private static void XorBlock(byte[] data, byte[] maskData) { for (int index = 0; index < 16; index++) data[index] ^= maskData[index]; } private static byte[] P_GetBytesRange(byte[] src, int offset, int size) { byte[] dest = new byte[size]; Array.Copy(src, offset, dest, 0, size); return dest; } } }
AESCipher
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Security.Cryptography; namespace Charlotte.Tools { public class AESCipher : IDisposable { private AesManaged Aes; private ICryptoTransform Encryptor = null; private ICryptoTransform Decryptor = null; public AESCipher(byte[] rawKey) { if ( rawKey.Length != 16 && rawKey.Length != 24 && rawKey.Length != 32 ) throw new ArgumentException(); this.Aes = new AesManaged(); this.Aes.KeySize = rawKey.Length * 8; this.Aes.BlockSize = 128; this.Aes.Mode = CipherMode.ECB; this.Aes.IV = new byte[16]; // dummy this.Aes.Key = rawKey; this.Aes.Padding = PaddingMode.None; } public void EncryptBlock(byte[] input, byte[] output) { if ( input.Length != 16 || output.Length != 16 ) throw new ArgumentException(); if (this.Encryptor == null) this.Encryptor = this.Aes.CreateEncryptor(); this.Encryptor.TransformBlock(input, 0, 16, output, 0); } public void DecryptBlock(byte[] input, byte[] output) { if ( input.Length != 16 || output.Length != 16 ) throw new ArgumentException(); if (this.Decryptor == null) this.Decryptor = this.Aes.CreateDecryptor(); this.Decryptor.TransformBlock(input, 0, 16, output, 0); } public void Dispose() { if (this.Aes != null) { if (this.Encryptor != null) this.Encryptor.Dispose(); if (this.Decryptor != null) this.Decryptor.Dispose(); this.Aes.Dispose(); this.Aes = null; } } } }
DirToClusterFileTools
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using System.IO.Compression; using Charlotte.Commons; namespace Charlotte.Tools { /// <summary> /// ディレクトリを圧縮して1つのファイルにまとめる。 /// およびその逆を行います。 /// 圧縮ファイルの内容(データ形式)は apps/Compress と同じです。 /// 圧縮ファイルの推奨拡張子 = .cmp.gz /// </summary> public static class DirToClusterFileTools { public static void DirToClusterFile(string rDir, string wFile) { rDir = SCommon.MakeFullPath(rDir); wFile = SCommon.MakeFullPath(wFile); if (!Directory.Exists(rDir)) throw new Exception("no rDir"); if (Directory.Exists(wFile)) throw new Exception("Bad wFile"); ProcMain.WriteLog("DirToClusterFile-ST"); ProcMain.WriteLog("< " + rDir); ProcMain.WriteLog("> " + wFile); using (FileStream fileWriter = new FileStream(wFile, FileMode.Create, FileAccess.Write)) using (GZipStream gz = new GZipStream(fileWriter, CompressionMode.Compress, true)) { Writer = gz; foreach (string dir in Directory.GetDirectories(rDir, "*", SearchOption.AllDirectories)) { ProcMain.WriteLog("< " + dir); string relPath = SCommon.ChangeRoot(dir, rDir); WriteString("D"); // Directory WriteString(relPath); } foreach (string file in Directory.GetFiles(rDir, "*", SearchOption.AllDirectories)) { ProcMain.WriteLog("< " + file); string relPath = SCommon.ChangeRoot(file, rDir); WriteString("F"); // File WriteString(relPath); FileInfo fileInfo = new FileInfo(file); WriteString(new SimpleDateTime(fileInfo.CreationTime).ToTimeStamp().ToString()); WriteString(new SimpleDateTime(fileInfo.LastWriteTime).ToTimeStamp().ToString()); WriteString(new SimpleDateTime(fileInfo.LastAccessTime).ToTimeStamp().ToString()); WriteString(fileInfo.Length.ToString()); using (FileStream reader = new FileStream(file, FileMode.Open, FileAccess.Read)) { SCommon.ReadToEnd(reader.Read, Writer.Write); } } WriteString("E"); // End Writer = null; } ProcMain.WriteLog("DirToClusterFile-ED"); } private static Stream Writer; private static void WriteString(string str) { byte[] bStr = Encoding.UTF8.GetBytes(str); Write(SCommon.UIntToBytes((uint)bStr.Length)); Write(bStr); } private static void Write(byte[] data) { SCommon.Write(Writer, data); } public static void ClusterFileToDir(string rFile, string wDir) { rFile = SCommon.MakeFullPath(rFile); wDir = SCommon.MakeFullPath(wDir); ProcMain.WriteLog("ClusterFileToDir-ST"); ProcMain.WriteLog("< " + rFile); ProcMain.WriteLog("> " + wDir); if (!File.Exists(rFile)) throw new Exception("no rFile"); if (File.Exists(wDir)) throw new Exception("Bad wDir"); SCommon.CreateDir(wDir); using (FileStream fileReader = new FileStream(rFile, FileMode.Open, FileAccess.Read)) using (GZipStream gz = new GZipStream(fileReader, CompressionMode.Decompress, true)) { Reader = gz; for (; ; ) { string label = ReadString(); if (label == "D") // Directory { string relPath = ReadString(); CheckRelPath(relPath); string dir = Path.Combine(wDir, relPath); ProcMain.WriteLog("> " + dir); SCommon.CreateDir(dir); } else if (label == "F") // File { string relPath = ReadString(); CheckRelPath(relPath); string file = Path.Combine(wDir, relPath); ProcMain.WriteLog("> " + file); SimpleDateTime creationTime = SimpleDateTime.FromTimeStamp(long.Parse(ReadString())); SimpleDateTime lastWriteTime = SimpleDateTime.FromTimeStamp(long.Parse(ReadString())); SimpleDateTime lastAccessTime = SimpleDateTime.FromTimeStamp(long.Parse(ReadString())); long fileSize = long.Parse(ReadString()); ProcMain.WriteLog(creationTime); ProcMain.WriteLog(lastWriteTime); ProcMain.WriteLog(lastAccessTime); ProcMain.WriteLog(fileSize); if (fileSize < 0) throw new Exception("Bad fileSize: " + fileSize); using (FileStream writer = new FileStream(file, FileMode.Create, FileAccess.Write)) { for (long count = 0L; count < fileSize; ) { int size = (int)Math.Min(2000000, fileSize - count); SCommon.Write(writer, SCommon.Read(Reader, size)); count += size; } } { FileInfo fileInfo = new FileInfo(file); fileInfo.CreationTime = creationTime.ToDateTime(); fileInfo.LastWriteTime = lastWriteTime.ToDateTime(); fileInfo.LastAccessTime = lastAccessTime.ToDateTime(); } } else if (label == "E") // End { break; } else { throw new Exception("不明なラベル"); } } Reader = null; } ProcMain.WriteLog("ClusterFileToDir-ED"); } private static Stream Reader; private static string ReadString() { int size = (int)SCommon.ToUInt(Read(4)); if (size < 0 || SCommon.IMAX < size) // rough limit throw new Exception("Bad size: " + size); byte[] bStr = Read(size); string str = Encoding.UTF8.GetString(bStr); return str; } private static byte[] Read(int size) { return SCommon.Read(Reader, size); } private static void CheckRelPath(string relPath) { string[] pTkns = SCommon.Tokenize(relPath, "\\"); foreach (string pTkn in pTkns) { if ( pTkn == "" || pTkn == "." || pTkn == ".." || pTkn.Contains(':') ) throw new Exception("Bad pTkn"); } } } }