internal static bool Decrypt( DecMPEFileInfo decMpeFileInfo, string[] passwords, string outputDir, IProgress <string> progress = null) { int nQuestions = decMpeFileInfo._nQuestions; int nRequiredPasswords = decMpeFileInfo._nRequiredPasswords; int nDecryptedQuestionKeys = 0; // Number of QuestionKeys that could be decoded. (J)複号できたQuestionKeyの数 int[] questionKeyIndexes = new int[nRequiredPasswords]; // Index of QuestionKeys that could be decoded. (J)複号できたQuestionKeyのIndex byte[] questionKeys = new byte[nRequiredPasswords * Crypto.AES_KEY_SIZE_BYTE]; // Combined Question Key (for decrypt dataKey) // check password for (int i = 0; i < nQuestions && nDecryptedQuestionKeys < nRequiredPasswords; ++i) { var passwordTxt = passwords[i]; if (passwordTxt == "") { continue; } var question = decMpeFileInfo._decQuestions[i]; byte[] password = Encoding.UTF8.GetBytes(passwordTxt); for (int j = 0; j < question.nPasswords; ++j) { var result = Crypto.decryptByte(question.encryptedQuestionKeys, j * Crypto.AES_KEY_SIZE_BYTE_WITH_PADDING, Crypto.AES_KEY_SIZE_BYTE_WITH_PADDING, password, Crypto.createSalt(i, j)); if (result != null) { if (result.Length != Crypto.AES_KEY_SIZE_BYTE) { // assertion throw new Exception("broken file"); } // store question key Buffer.BlockCopy(result, 0, questionKeys, nDecryptedQuestionKeys * Crypto.AES_KEY_SIZE_BYTE, Crypto.AES_KEY_SIZE_BYTE); questionKeyIndexes[nDecryptedQuestionKeys] = i; nDecryptedQuestionKeys += 1; break; } } } if (nDecryptedQuestionKeys < nRequiredPasswords) { return(false); // fail. number of passwords is not enough. } // decrypt data byte[] dataKey = null; int[][] combinaitons = Combination.enumerateCombination(nQuestions, nRequiredPasswords); for (int i = 0; i < combinaitons.Length; ++i) { if (combinaitons[i].SequenceEqual(questionKeyIndexes)) { dataKey = Crypto.decryptByte(decMpeFileInfo._encryptedDataKeys, i * Crypto.AES_KEY_SIZE_BYTE_WITH_PADDING, Crypto.AES_KEY_SIZE_BYTE_WITH_PADDING, questionKeys); break; } } if (dataKey == null) { throw new Exception("broken file"); } progress?.Report("(1/3) decripting archive."); // OK var tmpFileCompress = System.IO.Path.GetRandomFileName(); try { using (FileStream zipFS = new FileStream(tmpFileCompress, FileMode.Create)) { decMpeFileInfo._encryptedFS.Position = decMpeFileInfo._dataStartPosition; Crypto.decryptStream(decMpeFileInfo._encryptedFS, zipFS, dataKey); } // ZipFile.ExtractToDirectory can not read files unless Close() is done on zipFS //(J) zipFS を Close() しないと ZipFile.ExtractToDirectory はファイルを読み込めない try { progress?.Report("(2/3) creating output file(s)."); System.IO.Compression.ZipFile.ExtractToDirectory(tmpFileCompress, outputDir); } catch (Exception ex) { throw ex; } } finally { File.Delete(tmpFileCompress); } progress?.Report("(3/3) finished."); return(true); // succeed }
/** * encryption */ internal static void Encrypt_2( FileStream encryptedFS, string[] hintList, List <string>[] passwordList, int nRequiredPasswords, AppConst.MPE_Flag flag, RNGCryptoServiceProvider r, byte[] dataKey, FileStream outputFS, IProgress <string> progress = null) { // generate questionKeys, encryptedQuestionKeys int nQuestions = hintList.Length; byte[][] questionKeys = new byte[nQuestions][]; byte[][][] encryptedQuestionKeys = new byte[nQuestions][][]; for (int i = 0; i < nQuestions; ++i) { questionKeys[i] = new byte[Crypto.AES_KEY_SIZE_BYTE]; // Encryption key for dataKey r.GetBytes(questionKeys[i]); // encrypt questionKey // Based on the number of passwords, assign the location of the password to encrypt questionKeys. In order to hide the number of passwords, dummy indexes are also included. // For example, when three passwords are set in the Question being processed, the following values are randomly set in passwordEncryptIndexes. // passwordEncryptIndexes = {1, -1, 0, 2, -1} // In this case, encryptedQuestionKeys is [question 0] encrypted questionKey with password 1, [1]: dummy, [2]: questionKey encrypted with password 0, [3]: questionKey encrypted with password 2 , [4]: Dummy is stored. //(J) パスワードの個数をもとに、questionKeysを暗号化するパスワードの位置を割り当てる。パスワード個数を隠蔽するため、ダミーのインデクスも含まれる。 //(J) たとえば、処理中の Question にパスワードが3つ設定されている場合に、passwordEncryptIndexes には以下のような値をランダムに設定する。 //(J) passwordEncryptIndexes = {1, -1, 0, 2, -1} //(J) この場合はencryptedQuestionKeys は、[0]:パスワード1で暗号化されたquestionKey, [1]:ダミー, [2]:パスワード0で暗号化されたquestionKey, [3]:パスワード2で暗号化されたquestionKey, [4]:ダミー が格納される。 int[] passwordEncryptIndexes = alllocPasswordEncryptIndexes(passwordList[i].Count); encryptedQuestionKeys[i] = new byte[passwordEncryptIndexes.Length][]; for (int j = 0; j < passwordEncryptIndexes.Length; ++j) { if (passwordEncryptIndexes[j] >= 0) { // real password String password = passwordList[i][passwordEncryptIndexes[j]]; byte[] passwordBytes = Encoding.UTF8.GetBytes(password); encryptedQuestionKeys[i][j] = Crypto.encryptByte(questionKeys[i], passwordBytes, Crypto.createSalt(i, j)); if (encryptedQuestionKeys[i][j].Length != Crypto.AES_KEY_SIZE_BYTE_WITH_PADDING) { // assertion throw new Exception("system error"); //! } } else { // dummy key byte[] dummyKey = new byte[Crypto.AES_KEY_SIZE_BYTE_WITH_PADDING]; r.GetBytes(dummyKey); encryptedQuestionKeys[i][j] = dummyKey; } } } // generate encryptedDataKey int[][] combinaiton = Combination.enumerateCombination(nQuestions, nRequiredPasswords); byte[] combinedQuestionKey = new byte[nRequiredPasswords * Crypto.AES_KEY_SIZE_BYTE]; byte[][] encryptedDataKey = new byte[combinaiton.Length][]; for (int i = 0; i < combinaiton.Length; ++i) { // Create a password for dataKey from multiple question keys. //(J) 複数のquestion key から dataKey 用のパスワードを作成する for (int j = 0; j < combinaiton[i].Length; ++j) { int qkIdx = combinaiton[i][j]; Buffer.BlockCopy(questionKeys[qkIdx], 0, combinedQuestionKey, j * Crypto.AES_KEY_SIZE_BYTE, Crypto.AES_KEY_SIZE_BYTE); } encryptedDataKey[i] = Crypto.encryptByte(dataKey, combinedQuestionKey); if (encryptedDataKey[i].Length != Crypto.AES_KEY_SIZE_BYTE_WITH_PADDING) { // assertion throw new Exception("system error"); //! } } progress?.Report("(3/4) creating output file."); // create question Header (for MPE File) using (var questionHeaderMS = new MemoryStream()) { byte[] bytes; // flag 1 questionHeaderMS.WriteByte((byte)flag); // nQuestions 4 bytes = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(nQuestions)); questionHeaderMS.Write(bytes, 0, bytes.Length); // nRequiredPasswords 4 bytes = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(nRequiredPasswords)); questionHeaderMS.Write(bytes, 0, bytes.Length); // nPasswordCombinations 4 bytes = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(encryptedDataKey.Length)); questionHeaderMS.Write(bytes, 0, bytes.Length); // encryptedDataKeys n * 48 for (int i = 0; i < encryptedDataKey.Length; ++i) { questionHeaderMS.Write(encryptedDataKey[i], 0, encryptedDataKey[i].Length); } // Question n for (int i = 0; i < nQuestions; ++i) { // hint string len 4 byte[] hintStrBytes = Encoding.UTF8.GetBytes(hintList[i]); bytes = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(hintStrBytes.Length)); questionHeaderMS.Write(bytes, 0, bytes.Length); // hint string n questionHeaderMS.Write(hintStrBytes, 0, hintStrBytes.Length); // nPasswords 4 bytes = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(encryptedQuestionKeys[i].Length)); questionHeaderMS.Write(bytes, 0, bytes.Length); // encryptedQuestionKeys n * 48 for (int j = 0; j < encryptedQuestionKeys[i].Length; ++j) { questionHeaderMS.Write(encryptedQuestionKeys[i][j], 0, encryptedQuestionKeys[i][j].Length); } } Encrypt_3(questionHeaderMS, encryptedFS, outputFS); } }