SP_memo

何処にも披露する見込みの無いものを書き捨てる場所

自分用

削除予定だったけどこのまま置いておく。非常時のために

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");
			}
		}
	}
}