├── Untrue
├── TestCases
│ ├── keyfiles_3.txt
│ ├── keyfiles_1.txt
│ ├── keyfiles_2.txt
│ ├── volume_header_enc_1.hex
│ ├── volume_header_enc_2.hex
│ ├── volume_header_enc_3.hex
│ ├── volume_header_enc_4.hex
│ ├── keyfiles
│ │ ├── keyfile_1.txt
│ │ └── keyfile_2.txt
│ ├── ForgotPasswordCombinationCheck.bat
│ ├── RunTestCases.bat
│ └── README_TestCases.txt
├── lib
│ ├── NDesk.Options.dll
│ └── BouncyCastle.CryptoExt.dll
├── app.config
├── VolumeHeaderResult.cs
├── Properties
│ └── AssemblyInfo.cs
├── Untrue.csproj
├── PBKDF2.cs
├── Utils.cs
└── Program.cs
├── README.md
├── .gitignore
├── Untrue.sln
└── LICENSE
/Untrue/TestCases/keyfiles_3.txt:
--------------------------------------------------------------------------------
1 | keyfiles
--------------------------------------------------------------------------------
/Untrue/TestCases/keyfiles_1.txt:
--------------------------------------------------------------------------------
1 | keyfiles/keyfile_1.txt
--------------------------------------------------------------------------------
/Untrue/TestCases/keyfiles_2.txt:
--------------------------------------------------------------------------------
1 | keyfiles/keyfile_2.txt
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nccgroup/Untrue/HEAD/README.md
--------------------------------------------------------------------------------
/Untrue/lib/NDesk.Options.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nccgroup/Untrue/HEAD/Untrue/lib/NDesk.Options.dll
--------------------------------------------------------------------------------
/Untrue/lib/BouncyCastle.CryptoExt.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nccgroup/Untrue/HEAD/Untrue/lib/BouncyCastle.CryptoExt.dll
--------------------------------------------------------------------------------
/Untrue/TestCases/volume_header_enc_1.hex:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nccgroup/Untrue/HEAD/Untrue/TestCases/volume_header_enc_1.hex
--------------------------------------------------------------------------------
/Untrue/TestCases/volume_header_enc_2.hex:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nccgroup/Untrue/HEAD/Untrue/TestCases/volume_header_enc_2.hex
--------------------------------------------------------------------------------
/Untrue/TestCases/volume_header_enc_3.hex:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nccgroup/Untrue/HEAD/Untrue/TestCases/volume_header_enc_3.hex
--------------------------------------------------------------------------------
/Untrue/TestCases/volume_header_enc_4.hex:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nccgroup/Untrue/HEAD/Untrue/TestCases/volume_header_enc_4.hex
--------------------------------------------------------------------------------
/Untrue/app.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | #######################
2 | # Visual Studio files #
3 | #######################
4 | *.suo
5 | *.user
6 |
7 | ##################
8 | # Compiled files #
9 | ##################
10 | bin/
11 | obj/
12 |
13 |
--------------------------------------------------------------------------------
/Untrue/TestCases/keyfiles/keyfile_1.txt:
--------------------------------------------------------------------------------
1 | b1f51a511f1da0cd348b8f8598db32e61cb963e5fc69e2b41485bf99590ed75a1b4c9133da73a711322404314402765ab0d23fd362a167d6f0c65bb215113d94c685a2c9bab235ccdd2ab0ea92281a521c8aaf37895493d080070ea00fc7f5d7
--------------------------------------------------------------------------------
/Untrue/TestCases/keyfiles/keyfile_2.txt:
--------------------------------------------------------------------------------
1 | c685a2c9bab235ccdd2ab0ea92281a521c8aaf37895493d080070ea00fc7f5d71b4c9133da73a711322404314402765ab0d23fd362a167d6f0c65bb215113d94b1f51a511f1da0cd348b8f8598db32e61cb963e5fc69e2b41485bf99590ed75a
--------------------------------------------------------------------------------
/Untrue/TestCases/ForgotPasswordCombinationCheck.bat:
--------------------------------------------------------------------------------
1 | untrue --password_check_only -p "orangeyellowred" --volume_header_file=volume_header_enc_4.hex
2 | untrue --password_check_only -p "redyelloworange" --volume_header_file=volume_header_enc_4.hex
3 | untrue --password_check_only -p "yelloworangered" --volume_header_file=volume_header_enc_4.hex
4 | untrue --password_check_only -p "orangeredyellow" --volume_header_file=volume_header_enc_4.hex
5 | pause
--------------------------------------------------------------------------------
/Untrue/TestCases/RunTestCases.bat:
--------------------------------------------------------------------------------
1 | untrue --password_check_only -p "redorangeyellow" --password_key_files=keyfiles_1.txt --volume_header_file=volume_header_enc_1.hex
2 | untrue --password_check_only -p "yelloworangered" --password_key_files=keyfiles_2.txt --volume_header_file=volume_header_enc_2.hex
3 | untrue --password_check_only -p "orangeyellowred" --password_key_files=keyfiles_3.txt --volume_header_file=volume_header_enc_3.hex
4 | untrue --password_check_only -p "orangeredyellow" --volume_header_file=volume_header_enc_4.hex
5 | pause
--------------------------------------------------------------------------------
/Untrue.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 11.00
3 | # Visual Studio 2010
4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Untrue", "Untrue\Untrue.csproj", "{B71C781A-DEAF-4E64-A068-79CFECA0CCBC}"
5 | EndProject
6 | Global
7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
8 | Debug|x86 = Debug|x86
9 | Release|x86 = Release|x86
10 | EndGlobalSection
11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
12 | {B71C781A-DEAF-4E64-A068-79CFECA0CCBC}.Debug|x86.ActiveCfg = Debug|x86
13 | {B71C781A-DEAF-4E64-A068-79CFECA0CCBC}.Debug|x86.Build.0 = Debug|x86
14 | {B71C781A-DEAF-4E64-A068-79CFECA0CCBC}.Release|x86.ActiveCfg = Release|x86
15 | {B71C781A-DEAF-4E64-A068-79CFECA0CCBC}.Release|x86.Build.0 = Release|x86
16 | EndGlobalSection
17 | GlobalSection(SolutionProperties) = preSolution
18 | HideSolutionNode = FALSE
19 | EndGlobalSection
20 | EndGlobal
21 |
--------------------------------------------------------------------------------
/Untrue/VolumeHeaderResult.cs:
--------------------------------------------------------------------------------
1 | //Released as open source by NCC Group Plc - http://www.nccgroup.com/
2 | //
3 | //Developed by Richard Turnbull, Richard [dot] Turnbull [at] nccgroup [dot] com
4 | //
5 | //http://www.github.com/nccgroup/untrue
6 | //
7 | //Released under AGPL see LICENSE for more information
8 |
9 | using System;
10 | using System.Collections.Generic;
11 | using System.Linq;
12 | using System.Text;
13 |
14 | namespace Untrue
15 | {
16 | public class VolumeHeaderResult
17 | {
18 | public bool Success
19 | {
20 | get;
21 | set;
22 | }
23 |
24 | public TDHashAlgorithm ha
25 | {
26 | get;
27 | set;
28 | }
29 |
30 | public TDEncryptionAlgorithm ea
31 | {
32 | get;
33 | set;
34 | }
35 |
36 | public byte[] VolumeKey
37 | {
38 | get;
39 | set;
40 | }
41 |
42 | public long CiphertextOffset
43 | {
44 | get;
45 | set;
46 | }
47 |
48 | public long CiphertextLength
49 | {
50 | get;
51 | set;
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/Untrue/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("Untrue")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("NCC Group")]
12 | [assembly: AssemblyProduct("Untrue")]
13 | [assembly: AssemblyCopyright("Copyright © 2015")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("b6e24c32-4464-4309-b013-11dd3d8450d9")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("0.9.0.0")]
36 | [assembly: AssemblyFileVersion("0.9.0.0")]
37 |
--------------------------------------------------------------------------------
/Untrue/TestCases/README_TestCases.txt:
--------------------------------------------------------------------------------
1 | Volume Header Test Cases
2 |
3 | Folder Structure:
4 | volume_header_enc_1.hex
5 | volume_header_enc_2.hex
6 | volume_header_enc_3.hex
7 | keyfiles_1.txt
8 | keyfiles_2.txt
9 | keyfiles_3.txt
10 | keyfiles/keyfile_1.txt
11 | keyfiles/keyfile_2.txt
12 | RunTestCases.bat
13 | ForgotPasswordCombinationCheck.bat
14 | README_TestCases.txt
15 |
16 | Test Case 1:
17 | password is "redorangeyellow"
18 |
19 | volume_header_enc_1.hex
20 | keyfiles_1.txt
21 | keyfiles/keyfile_1.txt
22 |
23 | keyfile_1.txt is SHA-256(red), SHA-256(orange), SHA-256(yellow)
24 |
25 | Hash Algorithm: RIPEMD-160
26 | Volume Encryption Algorithm: AES
27 |
28 | Test case 2;
29 | password is "yelloworangered"
30 |
31 | volume_header_enc_2.hex
32 | keyfiles_2.txt
33 | keyfiles/keyfile_2.txt
34 |
35 | keyfile_1.txt is concatenated SHA-256(red), SHA-256(orange), SHA-256(yellow)
36 | keyfile_2.txt is concatenated SHA-256(yellow), SHA-256(orange), SHA-256(red)
37 |
38 | Hash Algorithm: Whirlpool
39 | Volume Encryption Algorithm: AES-Twofish-Serpent
40 |
41 | Test Case 3:
42 | password is "orangeyellowred"
43 |
44 | volume_header_enc_3.hex
45 | keyfiles_3.txt
46 | keyfiles/keyfile_1.txt
47 | keyfiles/keyfile_2.txt
48 |
49 | keyfiles_3.txt is path to keyfile_1.txt and keyfile_2.txt
50 |
51 | Hash Algorithm: SHA-512
52 | Volume Encryption Algorithm: Twofish-Serpent
53 |
54 | Test Case 4:
55 | password is "orangeredyellow"
56 |
57 | volume_header_enc_4.hex
58 | no keyfiles supplied
59 |
60 | Hash Algorithm: SHA-512
61 | Volume Encryption Algorithm: Serpent-Twofish-AES
62 |
63 | Execution:
64 | Copy the Untrue.exe and dll's to the TestCases folder.
65 | Run the RunTestCases.bat batch file
66 |
67 | Notes:
68 | The --password_key_files command line argument accepts a text file listing combinations of path and/or files.
69 | Specify the full path in the text file if the paths and/or files are not relative to the untrue executable.
70 |
71 | The RunTestCases.bat batch file runs each of the four test cases to decrypt the volume header.
72 | The ForgotPasswordCombinationCheck.bat is an example of iterating through some password combinations.
73 | **I had forgotten my password details but knew the general structure and it was easy enough to iterate those combinations.
74 | **The volume header can be extracted directly from the volume into the hex file using 'secinspect'.
75 |
76 |
--------------------------------------------------------------------------------
/Untrue/Untrue.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | x86
6 | 8.0.30703
7 | 2.0
8 | {B71C781A-DEAF-4E64-A068-79CFECA0CCBC}
9 | Exe
10 | Properties
11 | Untrue
12 | Untrue
13 | v4.0
14 |
15 |
16 | 512
17 |
18 |
19 | x86
20 | true
21 | full
22 | false
23 | bin\Debug\
24 | DEBUG;TRACE
25 | prompt
26 | 4
27 |
28 |
29 | x86
30 | pdbonly
31 | true
32 | bin\Release\
33 | TRACE
34 | prompt
35 | 4
36 |
37 |
38 |
39 | lib\BouncyCastle.CryptoExt.dll
40 |
41 |
42 | lib\NDesk.Options.dll
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
70 |
--------------------------------------------------------------------------------
/Untrue/PBKDF2.cs:
--------------------------------------------------------------------------------
1 | //Released as open source by NCC Group Plc - http://www.nccgroup.com/
2 | //
3 | //Developed by Richard Turnbull, Richard [dot] Turnbull [at] nccgroup [dot] com
4 | //
5 | //http://www.github.com/nccgroup/untrue
6 | //
7 | //Released under AGPL see LICENSE for more information
8 |
9 | using System;
10 | using System.Collections.Generic;
11 | using System.Linq;
12 | using System.Text;
13 | using Org.BouncyCastle.Crypto;
14 | using Org.BouncyCastle.Crypto.Parameters;
15 | using Org.BouncyCastle.Crypto.Macs;
16 |
17 | namespace Untrue
18 | {
19 | // this is a modified version of the Bouncy Castle PBKDF2 routine (which only supports SHA-1 digests - we need more flexibility here)
20 | // based on code from https://stackoverflow.com/questions/3210795/pbkdf2-in-bouncy-castle-c-sharp
21 | public class PBKDF2
22 | {
23 | private IMac hMac;
24 |
25 | public PBKDF2(IDigest digest)
26 | {
27 | hMac = new HMac(digest);
28 | }
29 |
30 | private void F(
31 | byte[] P,
32 | byte[] S,
33 | int c,
34 | byte[] iBuf,
35 | byte[] outBytes,
36 | int outOff)
37 | {
38 | byte[] state = new byte[hMac.GetMacSize()];
39 | ICipherParameters param = new KeyParameter(P);
40 |
41 | hMac.Init(param);
42 |
43 | if (S != null)
44 | {
45 | hMac.BlockUpdate(S, 0, S.Length);
46 | }
47 |
48 | hMac.BlockUpdate(iBuf, 0, iBuf.Length);
49 |
50 | hMac.DoFinal(state, 0);
51 |
52 | Array.Copy(state, 0, outBytes, outOff, state.Length);
53 |
54 | for (int count = 1; count != c; count++)
55 | {
56 | hMac.Init(param);
57 | hMac.BlockUpdate(state, 0, state.Length);
58 | hMac.DoFinal(state, 0);
59 |
60 | for (int j = 0; j != state.Length; j++)
61 | {
62 | outBytes[outOff + j] ^= state[j];
63 | }
64 | }
65 | }
66 |
67 | private static void IntToOctet(
68 | byte[] Buffer,
69 | int i)
70 | {
71 | Buffer[0] = (byte)((uint)i >> 24);
72 | Buffer[1] = (byte)((uint)i >> 16);
73 | Buffer[2] = (byte)((uint)i >> 8);
74 | Buffer[3] = (byte)i;
75 | }
76 |
77 | public byte[] GetBytes(int outputLength, byte[] password, byte[] salt, int iterationsCount)
78 | {
79 | int hLen = hMac.GetMacSize();
80 | int l = (outputLength + hLen - 1) / hLen;
81 | byte[] iBuf = new byte[4];
82 | byte[] keyBuffer = new byte[l * hLen];
83 |
84 | for (int ii = 1; ii <= l; ii++)
85 | {
86 | IntToOctet(iBuf, ii);
87 |
88 | F(password, salt, iterationsCount, iBuf, keyBuffer, (ii - 1) * hLen);
89 | }
90 |
91 | byte[] outBytes = new byte[outputLength];
92 | Array.Copy(keyBuffer, outBytes, outputLength);
93 |
94 | return outBytes;
95 | }
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/Untrue/Utils.cs:
--------------------------------------------------------------------------------
1 | //Released as open source by NCC Group Plc - http://www.nccgroup.com/
2 | //
3 | //Developed by Richard Turnbull, Richard [dot] Turnbull [at] nccgroup [dot] com
4 | //
5 | //http://www.github.com/nccgroup/untrue
6 | //
7 | //Released under AGPL see LICENSE for more information
8 |
9 | using System;
10 | using System.Collections.Generic;
11 | using System.Linq;
12 | using System.Text;
13 | using System.Text.RegularExpressions;
14 | using System.IO;
15 |
16 | namespace Untrue
17 | {
18 | public class Utils
19 | {
20 | public static int SECTOR_SIZE = 0x200;
21 |
22 | // check if the given string represents valid hex-encoded data
23 | public static bool IsValidHexString(string s)
24 | {
25 | // remove all whitespace (we don't care if hex inputs contain whitespace)
26 | s = Regex.Replace(s, @"\s", "");
27 |
28 | // we allow hyphen characters, but only on byte boundaries (where obviously a byte is represented by two hex digits)
29 | int hyphenIndex;
30 | int hyphensSoFar = 0;
31 | while ((hyphenIndex = s.IndexOf('-')) != -1)
32 | {
33 | if (((hyphenIndex - hyphensSoFar) % 2) != 0)
34 | {
35 | return false;
36 | }
37 | }
38 | s = s.Replace("-", "");
39 |
40 | // check we have an even number of characters
41 | if (s.Length % 2 != 0)
42 | {
43 | return false;
44 | }
45 |
46 | // now check it's all hex digits
47 | s = s.ToLower();
48 | foreach (char c in s)
49 | {
50 | if (!Char.IsLetterOrDigit(c))
51 | {
52 | return false;
53 | }
54 | if (c > 'f')
55 | {
56 | return false;
57 | }
58 | }
59 |
60 | return true;
61 | }
62 |
63 | public static byte[] ConvertHexStringToBytes(string s)
64 | {
65 | if (!IsValidHexString(s))
66 | {
67 | throw new FormatException("Hex string passed to ConvertHexStringToBytes() was invalid");
68 | }
69 |
70 | // remove all whitespace (we don't care if hex inputs contain whitespace)
71 | s = Regex.Replace(s, @"\s", "");
72 | s = s.Replace("-", "");
73 |
74 | byte[] b = new byte[s.Length / 2];
75 |
76 | for (int ii = 0; ii < s.Length; ii += 2)
77 | {
78 | b[ii / 2] = Convert.ToByte(s.Substring(ii, 2), 16);
79 | }
80 |
81 | return b;
82 | }
83 |
84 | public static string ByteArrayToHexString(byte[] bytes)
85 | {
86 | string s = "";
87 | foreach (byte b in bytes)
88 | {
89 | s += String.Format("{0:x2}", b);
90 | }
91 | return s;
92 | }
93 |
94 | public static byte[] ReadSector(FileStream fs, long sectorNumber)
95 | {
96 | fs.Seek(sectorNumber * SECTOR_SIZE, SeekOrigin.Begin);
97 | byte[] data = new byte[SECTOR_SIZE];
98 |
99 | int bytesRead = fs.Read(data, 0, (int)SECTOR_SIZE);
100 |
101 | if (bytesRead < SECTOR_SIZE)
102 | {
103 | throw new Exception("Failed to read full sector in ReadSector");
104 | }
105 |
106 | return data;
107 | }
108 |
109 | public static long ConvertLongParameter(string input)
110 | {
111 | input = input.Trim();
112 | if (input.StartsWith("0x"))
113 | {
114 | return Convert.ToInt64(input, 16);
115 | }
116 | else
117 | {
118 | return Convert.ToInt64(input);
119 | }
120 | }
121 |
122 | public static long ReadLongFromByteArray(byte[] input, int offset)
123 | {
124 | long ret = 0;
125 | ret += (((long)input[offset]) << 56);
126 | ret += (((long)input[offset + 1]) << 48);
127 | ret += (((long)input[offset + 2]) << 40);
128 | ret += (((long)input[offset + 3]) << 32);
129 | ret += (((long)input[offset + 4]) << 24);
130 | ret += (((long)input[offset + 5]) << 16);
131 | ret += (((long)input[offset + 6]) << 8);
132 | ret += (((long)input[offset + 7]));
133 |
134 | return ret;
135 | }
136 |
137 | public static double CalculateChiSquareTestStatisticOnBytes(byte[] bytes)
138 | {
139 | int[] counts = new int[256];
140 | for (int ii = 0; ii < 256; ii++)
141 | {
142 | counts[ii] = 0;
143 | }
144 | foreach (byte b in bytes)
145 | {
146 | counts[b]++;
147 | }
148 |
149 | double Ei = ((double)bytes.Length) / 256.0;
150 |
151 | double total = 0.0;
152 |
153 | for (int ii = 0; ii < 256; ii++)
154 | {
155 | double zz = ((double)counts[ii]) - Ei;
156 | total += (zz * zz);
157 | }
158 |
159 | return total / Ei;
160 | }
161 |
162 | public static byte[] ByteArrayReverse(byte[] input)
163 | {
164 | byte[] output = new byte[input.Length];
165 |
166 | for (int ii = 0; ii < input.Length; ii++)
167 | {
168 | output[output.Length - 1 - ii] = input[ii];
169 | }
170 |
171 | return output;
172 | }
173 |
174 | public static bool? IsDirFile(string path)
175 | {
176 | if (!Directory.Exists(path) && !File.Exists(path))
177 | return null;
178 | var fileAttr = File.GetAttributes(path);
179 | return !fileAttr.HasFlag(FileAttributes.Directory);
180 | }
181 |
182 | }
183 | }
184 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU AFFERO GENERAL PUBLIC LICENSE
2 | Version 3, 19 November 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU Affero General Public License is a free, copyleft license for
11 | software and other kinds of works, specifically designed to ensure
12 | cooperation with the community in the case of network server software.
13 |
14 | The licenses for most software and other practical works are designed
15 | to take away your freedom to share and change the works. By contrast,
16 | our General Public Licenses are intended to guarantee your freedom to
17 | share and change all versions of a program--to make sure it remains free
18 | software for all its users.
19 |
20 | When we speak of free software, we are referring to freedom, not
21 | price. Our General Public Licenses are designed to make sure that you
22 | have the freedom to distribute copies of free software (and charge for
23 | them if you wish), that you receive source code or can get it if you
24 | want it, that you can change the software or use pieces of it in new
25 | free programs, and that you know you can do these things.
26 |
27 | Developers that use our General Public Licenses protect your rights
28 | with two steps: (1) assert copyright on the software, and (2) offer
29 | you this License which gives you legal permission to copy, distribute
30 | and/or modify the software.
31 |
32 | A secondary benefit of defending all users' freedom is that
33 | improvements made in alternate versions of the program, if they
34 | receive widespread use, become available for other developers to
35 | incorporate. Many developers of free software are heartened and
36 | encouraged by the resulting cooperation. However, in the case of
37 | software used on network servers, this result may fail to come about.
38 | The GNU General Public License permits making a modified version and
39 | letting the public access it on a server without ever releasing its
40 | source code to the public.
41 |
42 | The GNU Affero General Public License is designed specifically to
43 | ensure that, in such cases, the modified source code becomes available
44 | to the community. It requires the operator of a network server to
45 | provide the source code of the modified version running there to the
46 | users of that server. Therefore, public use of a modified version, on
47 | a publicly accessible server, gives the public access to the source
48 | code of the modified version.
49 |
50 | An older license, called the Affero General Public License and
51 | published by Affero, was designed to accomplish similar goals. This is
52 | a different license, not a version of the Affero GPL, but Affero has
53 | released a new version of the Affero GPL which permits relicensing under
54 | this license.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | TERMS AND CONDITIONS
60 |
61 | 0. Definitions.
62 |
63 | "This License" refers to version 3 of the GNU Affero General Public License.
64 |
65 | "Copyright" also means copyright-like laws that apply to other kinds of
66 | works, such as semiconductor masks.
67 |
68 | "The Program" refers to any copyrightable work licensed under this
69 | License. Each licensee is addressed as "you". "Licensees" and
70 | "recipients" may be individuals or organizations.
71 |
72 | To "modify" a work means to copy from or adapt all or part of the work
73 | in a fashion requiring copyright permission, other than the making of an
74 | exact copy. The resulting work is called a "modified version" of the
75 | earlier work or a work "based on" the earlier work.
76 |
77 | A "covered work" means either the unmodified Program or a work based
78 | on the Program.
79 |
80 | To "propagate" a work means to do anything with it that, without
81 | permission, would make you directly or secondarily liable for
82 | infringement under applicable copyright law, except executing it on a
83 | computer or modifying a private copy. Propagation includes copying,
84 | distribution (with or without modification), making available to the
85 | public, and in some countries other activities as well.
86 |
87 | To "convey" a work means any kind of propagation that enables other
88 | parties to make or receive copies. Mere interaction with a user through
89 | a computer network, with no transfer of a copy, is not conveying.
90 |
91 | An interactive user interface displays "Appropriate Legal Notices"
92 | to the extent that it includes a convenient and prominently visible
93 | feature that (1) displays an appropriate copyright notice, and (2)
94 | tells the user that there is no warranty for the work (except to the
95 | extent that warranties are provided), that licensees may convey the
96 | work under this License, and how to view a copy of this License. If
97 | the interface presents a list of user commands or options, such as a
98 | menu, a prominent item in the list meets this criterion.
99 |
100 | 1. Source Code.
101 |
102 | The "source code" for a work means the preferred form of the work
103 | for making modifications to it. "Object code" means any non-source
104 | form of a work.
105 |
106 | A "Standard Interface" means an interface that either is an official
107 | standard defined by a recognized standards body, or, in the case of
108 | interfaces specified for a particular programming language, one that
109 | is widely used among developers working in that language.
110 |
111 | The "System Libraries" of an executable work include anything, other
112 | than the work as a whole, that (a) is included in the normal form of
113 | packaging a Major Component, but which is not part of that Major
114 | Component, and (b) serves only to enable use of the work with that
115 | Major Component, or to implement a Standard Interface for which an
116 | implementation is available to the public in source code form. A
117 | "Major Component", in this context, means a major essential component
118 | (kernel, window system, and so on) of the specific operating system
119 | (if any) on which the executable work runs, or a compiler used to
120 | produce the work, or an object code interpreter used to run it.
121 |
122 | The "Corresponding Source" for a work in object code form means all
123 | the source code needed to generate, install, and (for an executable
124 | work) run the object code and to modify the work, including scripts to
125 | control those activities. However, it does not include the work's
126 | System Libraries, or general-purpose tools or generally available free
127 | programs which are used unmodified in performing those activities but
128 | which are not part of the work. For example, Corresponding Source
129 | includes interface definition files associated with source files for
130 | the work, and the source code for shared libraries and dynamically
131 | linked subprograms that the work is specifically designed to require,
132 | such as by intimate data communication or control flow between those
133 | subprograms and other parts of the work.
134 |
135 | The Corresponding Source need not include anything that users
136 | can regenerate automatically from other parts of the Corresponding
137 | Source.
138 |
139 | The Corresponding Source for a work in source code form is that
140 | same work.
141 |
142 | 2. Basic Permissions.
143 |
144 | All rights granted under this License are granted for the term of
145 | copyright on the Program, and are irrevocable provided the stated
146 | conditions are met. This License explicitly affirms your unlimited
147 | permission to run the unmodified Program. The output from running a
148 | covered work is covered by this License only if the output, given its
149 | content, constitutes a covered work. This License acknowledges your
150 | rights of fair use or other equivalent, as provided by copyright law.
151 |
152 | You may make, run and propagate covered works that you do not
153 | convey, without conditions so long as your license otherwise remains
154 | in force. You may convey covered works to others for the sole purpose
155 | of having them make modifications exclusively for you, or provide you
156 | with facilities for running those works, provided that you comply with
157 | the terms of this License in conveying all material for which you do
158 | not control copyright. Those thus making or running the covered works
159 | for you must do so exclusively on your behalf, under your direction
160 | and control, on terms that prohibit them from making any copies of
161 | your copyrighted material outside their relationship with you.
162 |
163 | Conveying under any other circumstances is permitted solely under
164 | the conditions stated below. Sublicensing is not allowed; section 10
165 | makes it unnecessary.
166 |
167 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
168 |
169 | No covered work shall be deemed part of an effective technological
170 | measure under any applicable law fulfilling obligations under article
171 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
172 | similar laws prohibiting or restricting circumvention of such
173 | measures.
174 |
175 | When you convey a covered work, you waive any legal power to forbid
176 | circumvention of technological measures to the extent such circumvention
177 | is effected by exercising rights under this License with respect to
178 | the covered work, and you disclaim any intention to limit operation or
179 | modification of the work as a means of enforcing, against the work's
180 | users, your or third parties' legal rights to forbid circumvention of
181 | technological measures.
182 |
183 | 4. Conveying Verbatim Copies.
184 |
185 | You may convey verbatim copies of the Program's source code as you
186 | receive it, in any medium, provided that you conspicuously and
187 | appropriately publish on each copy an appropriate copyright notice;
188 | keep intact all notices stating that this License and any
189 | non-permissive terms added in accord with section 7 apply to the code;
190 | keep intact all notices of the absence of any warranty; and give all
191 | recipients a copy of this License along with the Program.
192 |
193 | You may charge any price or no price for each copy that you convey,
194 | and you may offer support or warranty protection for a fee.
195 |
196 | 5. Conveying Modified Source Versions.
197 |
198 | You may convey a work based on the Program, or the modifications to
199 | produce it from the Program, in the form of source code under the
200 | terms of section 4, provided that you also meet all of these conditions:
201 |
202 | a) The work must carry prominent notices stating that you modified
203 | it, and giving a relevant date.
204 |
205 | b) The work must carry prominent notices stating that it is
206 | released under this License and any conditions added under section
207 | 7. This requirement modifies the requirement in section 4 to
208 | "keep intact all notices".
209 |
210 | c) You must license the entire work, as a whole, under this
211 | License to anyone who comes into possession of a copy. This
212 | License will therefore apply, along with any applicable section 7
213 | additional terms, to the whole of the work, and all its parts,
214 | regardless of how they are packaged. This License gives no
215 | permission to license the work in any other way, but it does not
216 | invalidate such permission if you have separately received it.
217 |
218 | d) If the work has interactive user interfaces, each must display
219 | Appropriate Legal Notices; however, if the Program has interactive
220 | interfaces that do not display Appropriate Legal Notices, your
221 | work need not make them do so.
222 |
223 | A compilation of a covered work with other separate and independent
224 | works, which are not by their nature extensions of the covered work,
225 | and which are not combined with it such as to form a larger program,
226 | in or on a volume of a storage or distribution medium, is called an
227 | "aggregate" if the compilation and its resulting copyright are not
228 | used to limit the access or legal rights of the compilation's users
229 | beyond what the individual works permit. Inclusion of a covered work
230 | in an aggregate does not cause this License to apply to the other
231 | parts of the aggregate.
232 |
233 | 6. Conveying Non-Source Forms.
234 |
235 | You may convey a covered work in object code form under the terms
236 | of sections 4 and 5, provided that you also convey the
237 | machine-readable Corresponding Source under the terms of this License,
238 | in one of these ways:
239 |
240 | a) Convey the object code in, or embodied in, a physical product
241 | (including a physical distribution medium), accompanied by the
242 | Corresponding Source fixed on a durable physical medium
243 | customarily used for software interchange.
244 |
245 | b) Convey the object code in, or embodied in, a physical product
246 | (including a physical distribution medium), accompanied by a
247 | written offer, valid for at least three years and valid for as
248 | long as you offer spare parts or customer support for that product
249 | model, to give anyone who possesses the object code either (1) a
250 | copy of the Corresponding Source for all the software in the
251 | product that is covered by this License, on a durable physical
252 | medium customarily used for software interchange, for a price no
253 | more than your reasonable cost of physically performing this
254 | conveying of source, or (2) access to copy the
255 | Corresponding Source from a network server at no charge.
256 |
257 | c) Convey individual copies of the object code with a copy of the
258 | written offer to provide the Corresponding Source. This
259 | alternative is allowed only occasionally and noncommercially, and
260 | only if you received the object code with such an offer, in accord
261 | with subsection 6b.
262 |
263 | d) Convey the object code by offering access from a designated
264 | place (gratis or for a charge), and offer equivalent access to the
265 | Corresponding Source in the same way through the same place at no
266 | further charge. You need not require recipients to copy the
267 | Corresponding Source along with the object code. If the place to
268 | copy the object code is a network server, the Corresponding Source
269 | may be on a different server (operated by you or a third party)
270 | that supports equivalent copying facilities, provided you maintain
271 | clear directions next to the object code saying where to find the
272 | Corresponding Source. Regardless of what server hosts the
273 | Corresponding Source, you remain obligated to ensure that it is
274 | available for as long as needed to satisfy these requirements.
275 |
276 | e) Convey the object code using peer-to-peer transmission, provided
277 | you inform other peers where the object code and Corresponding
278 | Source of the work are being offered to the general public at no
279 | charge under subsection 6d.
280 |
281 | A separable portion of the object code, whose source code is excluded
282 | from the Corresponding Source as a System Library, need not be
283 | included in conveying the object code work.
284 |
285 | A "User Product" is either (1) a "consumer product", which means any
286 | tangible personal property which is normally used for personal, family,
287 | or household purposes, or (2) anything designed or sold for incorporation
288 | into a dwelling. In determining whether a product is a consumer product,
289 | doubtful cases shall be resolved in favor of coverage. For a particular
290 | product received by a particular user, "normally used" refers to a
291 | typical or common use of that class of product, regardless of the status
292 | of the particular user or of the way in which the particular user
293 | actually uses, or expects or is expected to use, the product. A product
294 | is a consumer product regardless of whether the product has substantial
295 | commercial, industrial or non-consumer uses, unless such uses represent
296 | the only significant mode of use of the product.
297 |
298 | "Installation Information" for a User Product means any methods,
299 | procedures, authorization keys, or other information required to install
300 | and execute modified versions of a covered work in that User Product from
301 | a modified version of its Corresponding Source. The information must
302 | suffice to ensure that the continued functioning of the modified object
303 | code is in no case prevented or interfered with solely because
304 | modification has been made.
305 |
306 | If you convey an object code work under this section in, or with, or
307 | specifically for use in, a User Product, and the conveying occurs as
308 | part of a transaction in which the right of possession and use of the
309 | User Product is transferred to the recipient in perpetuity or for a
310 | fixed term (regardless of how the transaction is characterized), the
311 | Corresponding Source conveyed under this section must be accompanied
312 | by the Installation Information. But this requirement does not apply
313 | if neither you nor any third party retains the ability to install
314 | modified object code on the User Product (for example, the work has
315 | been installed in ROM).
316 |
317 | The requirement to provide Installation Information does not include a
318 | requirement to continue to provide support service, warranty, or updates
319 | for a work that has been modified or installed by the recipient, or for
320 | the User Product in which it has been modified or installed. Access to a
321 | network may be denied when the modification itself materially and
322 | adversely affects the operation of the network or violates the rules and
323 | protocols for communication across the network.
324 |
325 | Corresponding Source conveyed, and Installation Information provided,
326 | in accord with this section must be in a format that is publicly
327 | documented (and with an implementation available to the public in
328 | source code form), and must require no special password or key for
329 | unpacking, reading or copying.
330 |
331 | 7. Additional Terms.
332 |
333 | "Additional permissions" are terms that supplement the terms of this
334 | License by making exceptions from one or more of its conditions.
335 | Additional permissions that are applicable to the entire Program shall
336 | be treated as though they were included in this License, to the extent
337 | that they are valid under applicable law. If additional permissions
338 | apply only to part of the Program, that part may be used separately
339 | under those permissions, but the entire Program remains governed by
340 | this License without regard to the additional permissions.
341 |
342 | When you convey a copy of a covered work, you may at your option
343 | remove any additional permissions from that copy, or from any part of
344 | it. (Additional permissions may be written to require their own
345 | removal in certain cases when you modify the work.) You may place
346 | additional permissions on material, added by you to a covered work,
347 | for which you have or can give appropriate copyright permission.
348 |
349 | Notwithstanding any other provision of this License, for material you
350 | add to a covered work, you may (if authorized by the copyright holders of
351 | that material) supplement the terms of this License with terms:
352 |
353 | a) Disclaiming warranty or limiting liability differently from the
354 | terms of sections 15 and 16 of this License; or
355 |
356 | b) Requiring preservation of specified reasonable legal notices or
357 | author attributions in that material or in the Appropriate Legal
358 | Notices displayed by works containing it; or
359 |
360 | c) Prohibiting misrepresentation of the origin of that material, or
361 | requiring that modified versions of such material be marked in
362 | reasonable ways as different from the original version; or
363 |
364 | d) Limiting the use for publicity purposes of names of licensors or
365 | authors of the material; or
366 |
367 | e) Declining to grant rights under trademark law for use of some
368 | trade names, trademarks, or service marks; or
369 |
370 | f) Requiring indemnification of licensors and authors of that
371 | material by anyone who conveys the material (or modified versions of
372 | it) with contractual assumptions of liability to the recipient, for
373 | any liability that these contractual assumptions directly impose on
374 | those licensors and authors.
375 |
376 | All other non-permissive additional terms are considered "further
377 | restrictions" within the meaning of section 10. If the Program as you
378 | received it, or any part of it, contains a notice stating that it is
379 | governed by this License along with a term that is a further
380 | restriction, you may remove that term. If a license document contains
381 | a further restriction but permits relicensing or conveying under this
382 | License, you may add to a covered work material governed by the terms
383 | of that license document, provided that the further restriction does
384 | not survive such relicensing or conveying.
385 |
386 | If you add terms to a covered work in accord with this section, you
387 | must place, in the relevant source files, a statement of the
388 | additional terms that apply to those files, or a notice indicating
389 | where to find the applicable terms.
390 |
391 | Additional terms, permissive or non-permissive, may be stated in the
392 | form of a separately written license, or stated as exceptions;
393 | the above requirements apply either way.
394 |
395 | 8. Termination.
396 |
397 | You may not propagate or modify a covered work except as expressly
398 | provided under this License. Any attempt otherwise to propagate or
399 | modify it is void, and will automatically terminate your rights under
400 | this License (including any patent licenses granted under the third
401 | paragraph of section 11).
402 |
403 | However, if you cease all violation of this License, then your
404 | license from a particular copyright holder is reinstated (a)
405 | provisionally, unless and until the copyright holder explicitly and
406 | finally terminates your license, and (b) permanently, if the copyright
407 | holder fails to notify you of the violation by some reasonable means
408 | prior to 60 days after the cessation.
409 |
410 | Moreover, your license from a particular copyright holder is
411 | reinstated permanently if the copyright holder notifies you of the
412 | violation by some reasonable means, this is the first time you have
413 | received notice of violation of this License (for any work) from that
414 | copyright holder, and you cure the violation prior to 30 days after
415 | your receipt of the notice.
416 |
417 | Termination of your rights under this section does not terminate the
418 | licenses of parties who have received copies or rights from you under
419 | this License. If your rights have been terminated and not permanently
420 | reinstated, you do not qualify to receive new licenses for the same
421 | material under section 10.
422 |
423 | 9. Acceptance Not Required for Having Copies.
424 |
425 | You are not required to accept this License in order to receive or
426 | run a copy of the Program. Ancillary propagation of a covered work
427 | occurring solely as a consequence of using peer-to-peer transmission
428 | to receive a copy likewise does not require acceptance. However,
429 | nothing other than this License grants you permission to propagate or
430 | modify any covered work. These actions infringe copyright if you do
431 | not accept this License. Therefore, by modifying or propagating a
432 | covered work, you indicate your acceptance of this License to do so.
433 |
434 | 10. Automatic Licensing of Downstream Recipients.
435 |
436 | Each time you convey a covered work, the recipient automatically
437 | receives a license from the original licensors, to run, modify and
438 | propagate that work, subject to this License. You are not responsible
439 | for enforcing compliance by third parties with this License.
440 |
441 | An "entity transaction" is a transaction transferring control of an
442 | organization, or substantially all assets of one, or subdividing an
443 | organization, or merging organizations. If propagation of a covered
444 | work results from an entity transaction, each party to that
445 | transaction who receives a copy of the work also receives whatever
446 | licenses to the work the party's predecessor in interest had or could
447 | give under the previous paragraph, plus a right to possession of the
448 | Corresponding Source of the work from the predecessor in interest, if
449 | the predecessor has it or can get it with reasonable efforts.
450 |
451 | You may not impose any further restrictions on the exercise of the
452 | rights granted or affirmed under this License. For example, you may
453 | not impose a license fee, royalty, or other charge for exercise of
454 | rights granted under this License, and you may not initiate litigation
455 | (including a cross-claim or counterclaim in a lawsuit) alleging that
456 | any patent claim is infringed by making, using, selling, offering for
457 | sale, or importing the Program or any portion of it.
458 |
459 | 11. Patents.
460 |
461 | A "contributor" is a copyright holder who authorizes use under this
462 | License of the Program or a work on which the Program is based. The
463 | work thus licensed is called the contributor's "contributor version".
464 |
465 | A contributor's "essential patent claims" are all patent claims
466 | owned or controlled by the contributor, whether already acquired or
467 | hereafter acquired, that would be infringed by some manner, permitted
468 | by this License, of making, using, or selling its contributor version,
469 | but do not include claims that would be infringed only as a
470 | consequence of further modification of the contributor version. For
471 | purposes of this definition, "control" includes the right to grant
472 | patent sublicenses in a manner consistent with the requirements of
473 | this License.
474 |
475 | Each contributor grants you a non-exclusive, worldwide, royalty-free
476 | patent license under the contributor's essential patent claims, to
477 | make, use, sell, offer for sale, import and otherwise run, modify and
478 | propagate the contents of its contributor version.
479 |
480 | In the following three paragraphs, a "patent license" is any express
481 | agreement or commitment, however denominated, not to enforce a patent
482 | (such as an express permission to practice a patent or covenant not to
483 | sue for patent infringement). To "grant" such a patent license to a
484 | party means to make such an agreement or commitment not to enforce a
485 | patent against the party.
486 |
487 | If you convey a covered work, knowingly relying on a patent license,
488 | and the Corresponding Source of the work is not available for anyone
489 | to copy, free of charge and under the terms of this License, through a
490 | publicly available network server or other readily accessible means,
491 | then you must either (1) cause the Corresponding Source to be so
492 | available, or (2) arrange to deprive yourself of the benefit of the
493 | patent license for this particular work, or (3) arrange, in a manner
494 | consistent with the requirements of this License, to extend the patent
495 | license to downstream recipients. "Knowingly relying" means you have
496 | actual knowledge that, but for the patent license, your conveying the
497 | covered work in a country, or your recipient's use of the covered work
498 | in a country, would infringe one or more identifiable patents in that
499 | country that you have reason to believe are valid.
500 |
501 | If, pursuant to or in connection with a single transaction or
502 | arrangement, you convey, or propagate by procuring conveyance of, a
503 | covered work, and grant a patent license to some of the parties
504 | receiving the covered work authorizing them to use, propagate, modify
505 | or convey a specific copy of the covered work, then the patent license
506 | you grant is automatically extended to all recipients of the covered
507 | work and works based on it.
508 |
509 | A patent license is "discriminatory" if it does not include within
510 | the scope of its coverage, prohibits the exercise of, or is
511 | conditioned on the non-exercise of one or more of the rights that are
512 | specifically granted under this License. You may not convey a covered
513 | work if you are a party to an arrangement with a third party that is
514 | in the business of distributing software, under which you make payment
515 | to the third party based on the extent of your activity of conveying
516 | the work, and under which the third party grants, to any of the
517 | parties who would receive the covered work from you, a discriminatory
518 | patent license (a) in connection with copies of the covered work
519 | conveyed by you (or copies made from those copies), or (b) primarily
520 | for and in connection with specific products or compilations that
521 | contain the covered work, unless you entered into that arrangement,
522 | or that patent license was granted, prior to 28 March 2007.
523 |
524 | Nothing in this License shall be construed as excluding or limiting
525 | any implied license or other defenses to infringement that may
526 | otherwise be available to you under applicable patent law.
527 |
528 | 12. No Surrender of Others' Freedom.
529 |
530 | If conditions are imposed on you (whether by court order, agreement or
531 | otherwise) that contradict the conditions of this License, they do not
532 | excuse you from the conditions of this License. If you cannot convey a
533 | covered work so as to satisfy simultaneously your obligations under this
534 | License and any other pertinent obligations, then as a consequence you may
535 | not convey it at all. For example, if you agree to terms that obligate you
536 | to collect a royalty for further conveying from those to whom you convey
537 | the Program, the only way you could satisfy both those terms and this
538 | License would be to refrain entirely from conveying the Program.
539 |
540 | 13. Remote Network Interaction; Use with the GNU General Public License.
541 |
542 | Notwithstanding any other provision of this License, if you modify the
543 | Program, your modified version must prominently offer all users
544 | interacting with it remotely through a computer network (if your version
545 | supports such interaction) an opportunity to receive the Corresponding
546 | Source of your version by providing access to the Corresponding Source
547 | from a network server at no charge, through some standard or customary
548 | means of facilitating copying of software. This Corresponding Source
549 | shall include the Corresponding Source for any work covered by version 3
550 | of the GNU General Public License that is incorporated pursuant to the
551 | following paragraph.
552 |
553 | Notwithstanding any other provision of this License, you have
554 | permission to link or combine any covered work with a work licensed
555 | under version 3 of the GNU General Public License into a single
556 | combined work, and to convey the resulting work. The terms of this
557 | License will continue to apply to the part which is the covered work,
558 | but the work with which it is combined will remain governed by version
559 | 3 of the GNU General Public License.
560 |
561 | 14. Revised Versions of this License.
562 |
563 | The Free Software Foundation may publish revised and/or new versions of
564 | the GNU Affero General Public License from time to time. Such new versions
565 | will be similar in spirit to the present version, but may differ in detail to
566 | address new problems or concerns.
567 |
568 | Each version is given a distinguishing version number. If the
569 | Program specifies that a certain numbered version of the GNU Affero General
570 | Public License "or any later version" applies to it, you have the
571 | option of following the terms and conditions either of that numbered
572 | version or of any later version published by the Free Software
573 | Foundation. If the Program does not specify a version number of the
574 | GNU Affero General Public License, you may choose any version ever published
575 | by the Free Software Foundation.
576 |
577 | If the Program specifies that a proxy can decide which future
578 | versions of the GNU Affero General Public License can be used, that proxy's
579 | public statement of acceptance of a version permanently authorizes you
580 | to choose that version for the Program.
581 |
582 | Later license versions may give you additional or different
583 | permissions. However, no additional obligations are imposed on any
584 | author or copyright holder as a result of your choosing to follow a
585 | later version.
586 |
587 | 15. Disclaimer of Warranty.
588 |
589 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
590 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
591 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
592 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
593 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
594 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
595 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
596 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
597 |
598 | 16. Limitation of Liability.
599 |
600 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
601 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
602 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
603 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
604 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
605 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
606 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
607 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
608 | SUCH DAMAGES.
609 |
610 | 17. Interpretation of Sections 15 and 16.
611 |
612 | If the disclaimer of warranty and limitation of liability provided
613 | above cannot be given local legal effect according to their terms,
614 | reviewing courts shall apply local law that most closely approximates
615 | an absolute waiver of all civil liability in connection with the
616 | Program, unless a warranty or assumption of liability accompanies a
617 | copy of the Program in return for a fee.
618 |
619 | END OF TERMS AND CONDITIONS
620 |
621 | How to Apply These Terms to Your New Programs
622 |
623 | If you develop a new program, and you want it to be of the greatest
624 | possible use to the public, the best way to achieve this is to make it
625 | free software which everyone can redistribute and change under these terms.
626 |
627 | To do so, attach the following notices to the program. It is safest
628 | to attach them to the start of each source file to most effectively
629 | state the exclusion of warranty; and each file should have at least
630 | the "copyright" line and a pointer to where the full notice is found.
631 |
632 |
633 | Copyright (C)
634 |
635 | This program is free software: you can redistribute it and/or modify
636 | it under the terms of the GNU Affero General Public License as published
637 | by the Free Software Foundation, either version 3 of the License, or
638 | (at your option) any later version.
639 |
640 | This program is distributed in the hope that it will be useful,
641 | but WITHOUT ANY WARRANTY; without even the implied warranty of
642 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
643 | GNU Affero General Public License for more details.
644 |
645 | You should have received a copy of the GNU Affero General Public License
646 | along with this program. If not, see .
647 |
648 | Also add information on how to contact you by electronic and paper mail.
649 |
650 | If your software can interact with users remotely through a computer
651 | network, you should also make sure that it provides a way for users to
652 | get its source. For example, if your program is a web application, its
653 | interface could display a "Source" link that leads users to an archive
654 | of the code. There are many ways you could offer source, and different
655 | solutions will be better for different programs; see section 13 for the
656 | specific requirements.
657 |
658 | You should also get your employer (if you work as a programmer) or school,
659 | if any, to sign a "copyright disclaimer" for the program, if necessary.
660 | For more information on this, and how to apply and follow the GNU AGPL, see
661 | .
662 |
--------------------------------------------------------------------------------
/Untrue/Program.cs:
--------------------------------------------------------------------------------
1 | //Released as open source by NCC Group Plc - http://www.nccgroup.com/
2 | //
3 | //Developed by Richard Turnbull, Richard [dot] Turnbull [at] nccgroup [dot] com
4 | //
5 | //http://www.github.com/nccgroup/untrue
6 | //
7 | //Released under AGPL see LICENSE for more information
8 |
9 | using System;
10 | using System.Collections.Generic;
11 | using System.Linq;
12 | using System.Text;
13 | using NDesk.Options;
14 | using System.IO;
15 | using System.Text.RegularExpressions;
16 | using Org.BouncyCastle.Crypto;
17 | using Org.BouncyCastle.Crypto.Macs;
18 | using Org.BouncyCastle.Crypto.Parameters;
19 | using Org.BouncyCastle.Crypto.Digests;
20 | using System.Security.Cryptography;
21 | using Org.BouncyCastle.Crypto.Engines;
22 |
23 | namespace Untrue
24 | {
25 |
26 | public enum TDHashAlgorithm { RIPEMD, RIPEMD_SYSTEM, WHIRLPOOL, SHA512 };
27 | public enum TDEncryptionAlgorithm { AES, SERPENT, TWOFISH, AES_TWOFISH, AES_TWOFISH_SERPENT, SERPENT_AES, SERPENT_TWOFISH_AES, TWOFISH_SERPENT };
28 | public enum SectorGuess { ZEROS, TRUECRYPT_MBR, STANDARD_MBR, FAT_HEADER, NTFS_HEADER, CIPHERTEXT, PLAINTEXT };
29 |
30 | class Program
31 | {
32 | public static string UNTRUE_VERSION = "0.9";
33 | public static int SECTOR_SIZE = 0x200;
34 | public static int VOLUME_HEADER_SIZE = SECTOR_SIZE;
35 | public static int TRUECRYPT_SALT_LENGTH = 64;
36 | public static int VOLUME_HEADER_LENGTH_WITHOUT_SALT = VOLUME_HEADER_SIZE - TRUECRYPT_SALT_LENGTH;
37 | public static int PBKDF2_OUTPUT_LENGTH = 3 * 32 * 2;
38 | public static int TRUECRYPT_KEYPOOL_LENGTH = 64;
39 | public static int TRUECRYPT_KEYPOOL_CHUNKSIZE = 64 * 1024;
40 | public static int TRUECRYPT_KEYPOOL_MAXSIZE = 1024 * 1024;
41 |
42 | public static int RIPEMD_ITERATIONS = 2000;
43 | public static int RIPEMD_SYSTEM_ITERATIONS = 1000;
44 | public static int SHA512_ITERATIONS = 1000;
45 | public static int WHIRLPOOL_ITERATIONS = 1000;
46 |
47 | public static int VOLUME_HEADER_SECTOR_ON_RESCUE_DISK = 0xA6; // rescue disk image starts with 0x68 blank sectors, then a copy of the first track with volume header at sector 0x3E
48 | public static int VOLUME_HEADER_SECTOR_ON_FIRST_TRACK = 0x3E;
49 |
50 | static int verbosity = 1;
51 |
52 | // x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x + 1 IEEE802.3 crc polynomial
53 | // 0000 0100 1100 0001 0001 1101 1011 0111
54 | // 0x04c11db7
55 | public static UInt32[] crc_32_tab =
56 | {
57 | 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
58 | 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
59 | 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
60 | 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
61 | 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
62 | 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
63 | 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
64 | 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
65 | 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
66 | 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
67 | 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
68 | 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
69 | 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
70 | 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
71 | 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
72 | 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
73 | 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
74 | 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
75 | 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
76 | 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
77 | 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
78 | 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
79 | 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
80 | 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
81 | 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
82 | 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
83 | 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
84 | 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
85 | 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
86 | 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
87 | 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
88 | 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
89 | };
90 |
91 | static FileStream passwordKeyFile = null;
92 | static FileStream keyFile = null;
93 | static FileStream volumeHeaderFile = null;
94 | static FileStream inputFile = null;
95 | static FileStream outputFile = null;
96 |
97 | static int KeyLengthForEncryptionAlgorithm(TDEncryptionAlgorithm ea)
98 | {
99 | switch (ea)
100 | {
101 | case TDEncryptionAlgorithm.AES:
102 | case TDEncryptionAlgorithm.SERPENT:
103 | case TDEncryptionAlgorithm.TWOFISH:
104 | return 64; // 256 bits + 256 bits
105 | case TDEncryptionAlgorithm.AES_TWOFISH:
106 | case TDEncryptionAlgorithm.SERPENT_AES:
107 | case TDEncryptionAlgorithm.TWOFISH_SERPENT:
108 | return 128;
109 | case TDEncryptionAlgorithm.AES_TWOFISH_SERPENT:
110 | case TDEncryptionAlgorithm.SERPENT_TWOFISH_AES:
111 | return 192;
112 | }
113 |
114 | return -1;
115 | }
116 |
117 | static string HashAlgorithmFriendlyName(TDHashAlgorithm ha)
118 | {
119 | switch (ha)
120 | {
121 | case TDHashAlgorithm.SHA512:
122 | return "SHA-512";
123 | case TDHashAlgorithm.WHIRLPOOL:
124 | return "Whirlpool";
125 | case TDHashAlgorithm.RIPEMD:
126 | return "RIPEMD-160 (standard version with 2000 iterations)";
127 | case TDHashAlgorithm.RIPEMD_SYSTEM:
128 | return "RIPEMD-160 (system encryption version with 1000 iterations)";
129 | }
130 |
131 | return "";
132 | }
133 |
134 | static string EncryptionAlgorithmFriendlyName(TDEncryptionAlgorithm ea)
135 | {
136 | switch (ea)
137 | {
138 | case TDEncryptionAlgorithm.AES:
139 | return "AES";
140 | case TDEncryptionAlgorithm.SERPENT:
141 | return "Serpent";
142 | case TDEncryptionAlgorithm.TWOFISH:
143 | return "Twofish";
144 | case TDEncryptionAlgorithm.AES_TWOFISH:
145 | return "AES-Twofish";
146 | case TDEncryptionAlgorithm.AES_TWOFISH_SERPENT:
147 | return "AES-Twofish-Serpent";
148 | case TDEncryptionAlgorithm.SERPENT_AES:
149 | return "Serpent-AES";
150 | case TDEncryptionAlgorithm.SERPENT_TWOFISH_AES:
151 | return "Serpent-Twofish-AES";
152 | case TDEncryptionAlgorithm.TWOFISH_SERPENT:
153 | return "Twofish-Serpent";
154 | }
155 |
156 | return "";
157 | }
158 |
159 | static string EncryptionEngineFriendlyName(IBlockCipher bc)
160 | {
161 | if (bc is RijndaelEngine)
162 | {
163 | return "AES";
164 | }
165 | else if (bc is TwofishEngine)
166 | {
167 | return "Twofish";
168 | }
169 | else if (bc is SerpentEngine)
170 | {
171 | return "Serpent";
172 | }
173 |
174 | return "";
175 | }
176 |
177 |
178 |
179 | static void ShowHelp(OptionSet p)
180 | {
181 | Console.WriteLine("Usage: Untrue.exe");
182 | Console.WriteLine();
183 | Console.WriteLine("Options:");
184 | p.WriteOptionDescriptions(Console.Out);
185 | }
186 |
187 | static void ShowVersion()
188 | {
189 | Console.WriteLine("Untrue version {0}", UNTRUE_VERSION);
190 | Console.WriteLine("by Richard Turnbull, NCC Group");
191 | }
192 |
193 | static void UsageWarning(string s)
194 | {
195 | Console.WriteLine("Warning: {0}", s);
196 | }
197 |
198 | static void UsageError(string s)
199 | {
200 | Console.WriteLine("Error: {0}", s);
201 | if (inputFile != null) inputFile.Close();
202 | if (outputFile != null) outputFile.Close();
203 | if (keyFile != null) outputFile.Close();
204 | if (volumeHeaderFile != null) volumeHeaderFile.Close();
205 | System.Environment.Exit(1);
206 | }
207 |
208 | static SectorGuess GuessSectorFormat(byte[] sector)
209 | {
210 | bool allZeros = true;
211 |
212 | for (int ii = 0; ii < SECTOR_SIZE; ii++)
213 | {
214 | if (sector[ii] != 0)
215 | {
216 | allZeros = false;
217 | break;
218 | }
219 | }
220 |
221 | if (allZeros)
222 | {
223 | return SectorGuess.ZEROS;
224 | }
225 |
226 | string s = System.Text.Encoding.ASCII.GetString( sector);
227 |
228 | if (s.Contains("TrueCrypt Boot Loader"))
229 | {
230 | return SectorGuess.TRUECRYPT_MBR;
231 | }
232 |
233 | double score = Utils.CalculateChiSquareTestStatisticOnBytes(sector);
234 |
235 | // P(false_plaintext_positive) = 0.0001
236 | if (score < 347.6539)
237 | {
238 | return SectorGuess.CIPHERTEXT;
239 | }
240 | else
241 | {
242 | if (sector[SECTOR_SIZE - 2] == 0x55 && sector[SECTOR_SIZE - 1] == 0xAA)
243 | {
244 | if (s.Contains("NTFS"))
245 | {
246 | return SectorGuess.NTFS_HEADER;
247 | }
248 | else if (s.Contains("FAT"))
249 | {
250 | return SectorGuess.FAT_HEADER;
251 | }
252 | else
253 | {
254 | return SectorGuess.STANDARD_MBR;
255 | }
256 | }
257 | else
258 | {
259 | return SectorGuess.PLAINTEXT;
260 | }
261 | }
262 | }
263 |
264 | public static void XorBlockInto(byte[] block1, int offset1, int count, byte[] block2, int offset2, byte[] output, int outputOffset)
265 | {
266 | if ((offset1 + count > block1.Length) || (offset2 + count > block2.Length) || (outputOffset + count > output.Length))
267 | {
268 | throw new ArgumentException("Bad block lengths passed in to xorBlock()");
269 | }
270 |
271 | for (int ii = 0; ii < count; ii++)
272 | {
273 | output[outputOffset + ii] = (byte)(block1[ii + offset1] ^ block2[ii + offset2]);
274 | }
275 | }
276 |
277 | private static byte[] XtsTransformSector(
278 | ICryptoTransform primaryKeyEncrypt,
279 | ICryptoTransform primaryKeyDecrypt,
280 | ICryptoTransform secondaryKeyEncrypt,
281 | byte[] ciphertext,
282 | long sectorNumber,
283 | bool isDecrypt)
284 | {
285 | int blockLengthInBytes = 16;
286 | byte[] IV = new byte[blockLengthInBytes];
287 |
288 |
289 | for (int ii = 15; ii >= 8; ii--)
290 | {
291 | IV[15 - ii] = (byte)((sectorNumber >> (8 * (15 - ii))) & 0xff);
292 | }
293 |
294 | if (isDecrypt)
295 | {
296 | byte[] EK2 = secondaryKeyEncrypt.TransformFinalBlock(IV, 0, IV.Length);
297 |
298 | byte[] plaintext = new byte[ciphertext.Length];
299 | byte[] buffer = new byte[blockLengthInBytes];
300 | byte[] buffer2;
301 |
302 | for (int ii = 0; ii < ciphertext.Length; ii += blockLengthInBytes)
303 | {
304 | XorBlockInto(ciphertext, ii, blockLengthInBytes, EK2, 0, buffer, 0);
305 | buffer2 = primaryKeyDecrypt.TransformFinalBlock(buffer, 0, buffer.Length);
306 | XorBlockInto(buffer2, 0, blockLengthInBytes, EK2, 0, plaintext, ii);
307 |
308 | UpdateEK2(EK2);
309 | }
310 |
311 | return plaintext;
312 | }
313 | else
314 | {
315 | byte[] EK2 = secondaryKeyEncrypt.TransformFinalBlock(IV, 0, IV.Length);
316 |
317 | byte[] plaintext = new byte[ciphertext.Length];
318 | byte[] buffer = new byte[blockLengthInBytes];
319 | byte[] buffer2;
320 |
321 | for (int ii = 0; ii < ciphertext.Length; ii += blockLengthInBytes)
322 | {
323 | XorBlockInto(ciphertext, ii, blockLengthInBytes, EK2, 0, buffer, 0);
324 | buffer2 = primaryKeyEncrypt.TransformFinalBlock(buffer, 0, buffer.Length);
325 | XorBlockInto(buffer2, 0, blockLengthInBytes, EK2, 0, plaintext, ii);
326 |
327 | UpdateEK2(EK2);
328 | }
329 |
330 | return plaintext;
331 | }
332 | }
333 |
334 | private static byte[] XtsTransformSectorBC(
335 | IBlockCipher primaryTransform,
336 | IBlockCipher secondaryTransform,
337 | byte[] ciphertext,
338 | long sectorNumber)
339 | {
340 | bool isSerpent = (primaryTransform is SerpentEngine);
341 |
342 | int blockLengthInBytes = 16;
343 | byte[] IV = new byte[blockLengthInBytes];
344 |
345 | for (int ii = 15; ii >= 8; ii--)
346 | {
347 | IV[15 - ii] = (byte)((sectorNumber >> (8 * (15 - ii))) & 0xff);
348 | }
349 |
350 | byte[] EK2 = new byte[blockLengthInBytes];
351 |
352 | if( isSerpent) IV = Utils.ByteArrayReverse(IV);
353 |
354 | secondaryTransform.ProcessBlock(IV, 0, EK2, 0);
355 |
356 | if (isSerpent) EK2 = Utils.ByteArrayReverse(EK2);
357 |
358 | byte[] plaintext = new byte[ciphertext.Length];
359 | byte[] buffer = new byte[blockLengthInBytes];
360 | byte[] buffer2 = new byte[blockLengthInBytes];
361 |
362 | for (int ii = 0; ii < ciphertext.Length; ii += blockLengthInBytes)
363 | {
364 | XorBlockInto(ciphertext, ii, blockLengthInBytes, EK2, 0, buffer, 0);
365 | if (isSerpent) buffer = Utils.ByteArrayReverse(buffer);
366 | primaryTransform.ProcessBlock(buffer, 0, buffer2, 0);
367 | if (isSerpent) buffer2 = Utils.ByteArrayReverse(buffer2);
368 | XorBlockInto(buffer2, 0, blockLengthInBytes, EK2, 0, plaintext, ii);
369 |
370 | UpdateEK2(EK2);
371 | }
372 |
373 | return plaintext;
374 | }
375 |
376 | private static void UpdateEK2(byte[] EK2)
377 | {
378 | byte carriedBit = 0, topBit = 0;
379 |
380 | for (int ii = 0; ii < 16; ii++)
381 | {
382 | topBit = (byte)(EK2[ii] >> 7);
383 | EK2[ii] = (byte)(((EK2[ii] << 1) | carriedBit) & 0xFF);
384 | carriedBit = topBit;
385 | }
386 |
387 | if (topBit > 0)
388 | {
389 | EK2[0] ^= 0x87;
390 | }
391 | }
392 |
393 | private static bool VolumeHeaderIsValid(byte[] volumeHeaderBytes)
394 | {
395 | if ((volumeHeaderBytes[0] == (byte)'T') &&
396 | (volumeHeaderBytes[1] == (byte)'R') &&
397 | (volumeHeaderBytes[2] == (byte)'U') &&
398 | (volumeHeaderBytes[3] == (byte)'E'))
399 | {
400 | return true;
401 | }
402 |
403 | return false;
404 | }
405 |
406 | private static bool KeyFileProcess(byte[] keyPool, string kf)
407 | {
408 | byte[] data = new byte[TRUECRYPT_KEYPOOL_CHUNKSIZE];
409 | UInt32 crc = 0xffffffff;
410 | Int32 chunkNumber = 0;
411 | int bytesRead, writePos, totalRead;
412 |
413 | writePos = 0;
414 | totalRead = 0;
415 |
416 | try
417 | {
418 | passwordKeyFile = File.Open(kf, FileMode.Open, FileAccess.Read);
419 | passwordKeyFile.Seek(chunkNumber * TRUECRYPT_KEYPOOL_CHUNKSIZE, SeekOrigin.Begin);
420 |
421 | bool fileKeyPoolDone = false;
422 | while (((bytesRead = passwordKeyFile.Read(data, 0, TRUECRYPT_KEYPOOL_CHUNKSIZE)) > 0) && (!fileKeyPoolDone))
423 | {
424 | for (Int32 i = 0; ((i < bytesRead) && (!fileKeyPoolDone)); i++)
425 | {
426 | crc = crc_32_tab[(crc ^ data[i]) & 0xff] ^ (crc >> 8);
427 | keyPool[writePos++] += (byte)(crc >> 24);
428 | keyPool[writePos++] += (byte)(crc >> 16);
429 | keyPool[writePos++] += (byte)(crc >> 8);
430 | keyPool[writePos++] += (byte)(crc);
431 | if (writePos >= TRUECRYPT_KEYPOOL_LENGTH)
432 | {
433 | writePos = 0;
434 | }
435 | if (++totalRead >= TRUECRYPT_KEYPOOL_MAXSIZE)
436 | {
437 | fileKeyPoolDone = true;
438 | }
439 | }
440 | }
441 | passwordKeyFile.Close();
442 | }
443 | catch (IOException)
444 | {
445 | UsageWarning(String.Format("Couldn't open password key file {0} for reading", kf));
446 | return false;
447 | }
448 | return true;
449 | }
450 |
451 | private static VolumeHeaderResult DecryptVolumeHeader(byte[] volumeHeaderBytes, string password, string passwordKeyFiles, Dictionary hashAlgorithmsEnabled, Dictionary encryptionAlgorithmsEnabled)
452 | {
453 | VolumeHeaderResult result = new VolumeHeaderResult();
454 | result.Success = false;
455 |
456 | Log(2, "Trying to decrypt volume header...");
457 |
458 | Int32 passwordLen = password.Length;
459 | byte[] passwordTempBytes = new byte[TRUECRYPT_KEYPOOL_LENGTH];
460 | System.Text.Encoding.ASCII.GetBytes(password).CopyTo(passwordTempBytes, 0);
461 | if (passwordKeyFiles != null)
462 | {
463 | try
464 | {
465 | byte[] keyPool = new byte[TRUECRYPT_KEYPOOL_LENGTH];
466 | bool processedFile = false;
467 | int isEof;
468 |
469 | StreamReader passwordKeyFileList = new StreamReader(passwordKeyFiles);
470 | while ((isEof = passwordKeyFileList.Peek()) >= 0)
471 | {
472 | bool flagErr;
473 |
474 | flagErr = false;
475 | processedFile = true;
476 | string fn = passwordKeyFileList.ReadLine();
477 | bool? isfile;
478 | if ((isfile = Utils.IsDirFile(fn)) == null)
479 | {
480 | flagErr = true;
481 | UsageWarning(String.Format("The password_key_files is missing a file or has an incorrect file specified."));
482 | }
483 | else if (isfile == true)
484 | {
485 | Log(3, "keyfile: " + fn);
486 | if (!KeyFileProcess(keyPool, fn))
487 | {
488 | flagErr = true;
489 | UsageWarning(String.Format("The password_key_files is missing a file or has an incorrect file specified."));
490 | }
491 | }
492 | else
493 | {
494 | string[] filePaths = Directory.GetFiles(fn);
495 | for (Int32 i = 0; i < filePaths.Length; i++)
496 | {
497 | fn = filePaths[i];
498 | bool? isFileInPath;
499 | if ((isFileInPath = Utils.IsDirFile(fn)) == null)
500 | {
501 | flagErr = true;
502 | }
503 | else if (isFileInPath == true)
504 | {
505 | var fileAttr = File.GetAttributes(fn);
506 | if (!fileAttr.HasFlag(FileAttributes.Hidden))
507 | {
508 | Log(3, "keyfile: " + fn);
509 | if (!KeyFileProcess(keyPool, fn))
510 | {
511 | flagErr = true;
512 | UsageWarning(String.Format("A key file on the specified path failed."));
513 | }
514 | }
515 | else
516 | {
517 | UsageWarning(String.Format("Warning: Hidden files are on the key file path."));
518 | }
519 | }
520 | else
521 | {
522 | // Do not recurse paths
523 | }
524 | }
525 |
526 | }
527 | if (flagErr)
528 | {
529 | passwordKeyFileList.Close();
530 | throw new IOException("");
531 | }
532 | }
533 | if (!processedFile)
534 | {
535 | passwordKeyFileList.Close();
536 | throw new IOException("");
537 | }
538 | passwordKeyFileList.Close();
539 |
540 | for (Int32 i = 0; i < TRUECRYPT_KEYPOOL_LENGTH; i++)
541 | {
542 | passwordTempBytes[i] += keyPool[i];
543 | }
544 | passwordLen = TRUECRYPT_KEYPOOL_LENGTH;
545 |
546 | }
547 | catch (IOException)
548 | {
549 | UsageError(String.Format("password_key_files is specified on command line but no file, incorrect file or empty file was specified."));
550 | }
551 | }
552 | byte[] passwordBytes = new byte[passwordLen];
553 | Array.Copy(passwordTempBytes, passwordBytes, passwordLen);
554 |
555 | byte[] salt = new byte[TRUECRYPT_SALT_LENGTH];
556 | Array.Copy(volumeHeaderBytes, salt, TRUECRYPT_SALT_LENGTH);
557 |
558 | Log(3, "\n\tSalt : " + Utils.ByteArrayToHexString(salt));
559 |
560 | byte[] encryptedVolumeHeader = new byte[VOLUME_HEADER_LENGTH_WITHOUT_SALT];
561 | Array.Copy(volumeHeaderBytes, TRUECRYPT_SALT_LENGTH, encryptedVolumeHeader, 0, VOLUME_HEADER_LENGTH_WITHOUT_SALT);
562 |
563 | Log(3, "\tEncrypted header: " + Utils.ByteArrayToHexString(encryptedVolumeHeader));
564 |
565 | Log(3, "\tPassword bytes: " + Utils.ByteArrayToHexString(passwordBytes) + "\n");
566 |
567 | foreach( TDHashAlgorithm ha in Enum.GetValues(typeof(TDHashAlgorithm)))
568 | {
569 | if (!hashAlgorithmsEnabled[ha])
570 | {
571 | continue;
572 | }
573 |
574 | PBKDF2 pbkdf2 = null;
575 | int iterations = 0;
576 |
577 | switch (ha)
578 | {
579 | case TDHashAlgorithm.RIPEMD:
580 | pbkdf2 = new PBKDF2(new RipeMD160Digest());
581 | iterations = RIPEMD_ITERATIONS;
582 | break;
583 | case TDHashAlgorithm.RIPEMD_SYSTEM:
584 | pbkdf2 = new PBKDF2(new RipeMD160Digest());
585 | iterations = RIPEMD_SYSTEM_ITERATIONS;
586 | break;
587 | case TDHashAlgorithm.WHIRLPOOL:
588 | pbkdf2 = new PBKDF2(new WhirlpoolDigest());
589 | iterations = WHIRLPOOL_ITERATIONS;
590 | break;
591 | case TDHashAlgorithm.SHA512:
592 | pbkdf2 = new PBKDF2(new Sha512Digest());
593 | iterations = SHA512_ITERATIONS;
594 | break;
595 | }
596 |
597 | Log(2, "\tTrying hash algorithm: " + HashAlgorithmFriendlyName(ha));
598 |
599 | byte[] key = pbkdf2.GetBytes(PBKDF2_OUTPUT_LENGTH, passwordBytes, salt, iterations);
600 |
601 | Log(3, "\t\tDerived key: " + Utils.ByteArrayToHexString(key));
602 |
603 | byte[] primaryKey1 = new byte[32];
604 | byte[] secondaryKey1 = new byte[32];
605 | byte[] primaryKey2 = new byte[32];
606 | byte[] secondaryKey2 = new byte[32];
607 | byte[] primaryKey3 = new byte[32];
608 | byte[] secondaryKey3 = new byte[32];
609 |
610 | foreach (TDEncryptionAlgorithm ea in Enum.GetValues(typeof(TDEncryptionAlgorithm)))
611 | {
612 |
613 | IBlockCipher primaryTransform1 = null;
614 | IBlockCipher secondaryTransform1 = null;
615 | IBlockCipher primaryTransform2 = null;
616 | IBlockCipher secondaryTransform2 = null;
617 | IBlockCipher primaryTransform3 = null;
618 | IBlockCipher secondaryTransform3 = null;
619 |
620 | if (!encryptionAlgorithmsEnabled[ea])
621 | {
622 | continue;
623 | }
624 |
625 | switch (ea)
626 | {
627 | case TDEncryptionAlgorithm.AES:
628 | primaryTransform1 = new RijndaelEngine();
629 | secondaryTransform1 = new RijndaelEngine();
630 | break;
631 | case TDEncryptionAlgorithm.SERPENT:
632 | primaryTransform1 = new SerpentEngine();
633 | secondaryTransform1 = new SerpentEngine();
634 | primaryKey1 = Utils.ByteArrayReverse(primaryKey1);
635 | secondaryKey1 = Utils.ByteArrayReverse(secondaryKey1);
636 | break;
637 | case TDEncryptionAlgorithm.TWOFISH:
638 | primaryTransform1 = new TwofishEngine();
639 | secondaryTransform1 = new TwofishEngine();
640 | break;
641 | case TDEncryptionAlgorithm.AES_TWOFISH:
642 | primaryTransform2 = new RijndaelEngine();
643 | secondaryTransform2 = new RijndaelEngine();
644 | primaryTransform1 = new TwofishEngine();
645 | secondaryTransform1 = new TwofishEngine();
646 | break;
647 | case TDEncryptionAlgorithm.AES_TWOFISH_SERPENT:
648 | primaryTransform3 = new RijndaelEngine();
649 | secondaryTransform3 = new RijndaelEngine();
650 | primaryTransform2 = new TwofishEngine();
651 | secondaryTransform2 = new TwofishEngine();
652 | primaryTransform1 = new SerpentEngine();
653 | secondaryTransform1 = new SerpentEngine();
654 | break;
655 | case TDEncryptionAlgorithm.SERPENT_AES:
656 | primaryTransform1 = new RijndaelEngine();
657 | secondaryTransform1 = new RijndaelEngine();
658 | primaryTransform2 = new SerpentEngine();
659 | secondaryTransform2 = new SerpentEngine();
660 | break;
661 | case TDEncryptionAlgorithm.SERPENT_TWOFISH_AES:
662 | primaryTransform1 = new RijndaelEngine();
663 | secondaryTransform1 = new RijndaelEngine();
664 | primaryTransform2 = new TwofishEngine();
665 | secondaryTransform2 = new TwofishEngine();
666 | primaryTransform3 = new SerpentEngine();
667 | secondaryTransform3 = new SerpentEngine();
668 | break;
669 | case TDEncryptionAlgorithm.TWOFISH_SERPENT:
670 | primaryTransform2 = new TwofishEngine();
671 | secondaryTransform2 = new TwofishEngine();
672 | primaryTransform1 = new SerpentEngine();
673 | secondaryTransform1 = new SerpentEngine();
674 | break;
675 | }
676 |
677 | Log(2, "\t\tTrying encryption algorithm: " + EncryptionAlgorithmFriendlyName(ea));
678 |
679 | if (primaryTransform2 == null)
680 | {
681 | Array.Copy(key, 0, primaryKey1, 0, 32);
682 | Array.Copy(key, 32, secondaryKey1, 0, 32);
683 | }
684 | else if (primaryTransform3 == null)
685 | {
686 | Array.Copy(key, 0, primaryKey1, 0, 32);
687 | Array.Copy(key, 32, primaryKey2, 0, 32);
688 | Array.Copy(key, 64, secondaryKey1, 0, 32);
689 | Array.Copy(key, 96, secondaryKey2, 0, 32);
690 | }
691 | else
692 | {
693 | Array.Copy(key, 0, primaryKey1, 0, 32);
694 | Array.Copy(key, 32, primaryKey2, 0, 32);
695 | Array.Copy(key, 64, primaryKey3, 0, 32);
696 | Array.Copy(key, 96, secondaryKey1, 0, 32);
697 | Array.Copy(key, 128, secondaryKey2, 0, 32);
698 | Array.Copy(key, 160, secondaryKey3, 0, 32);
699 | }
700 |
701 | Log(3, "\t\t\tInitialising " + EncryptionEngineFriendlyName(primaryTransform1) + " with primary key: " + Utils.ByteArrayToHexString(primaryKey1));
702 | Log(3, "\t\t\tInitialising " + EncryptionEngineFriendlyName(primaryTransform1) + " with secondary key: " + Utils.ByteArrayToHexString(secondaryKey1));
703 | if (primaryTransform1 is SerpentEngine)
704 | {
705 | primaryKey1 = Utils.ByteArrayReverse(primaryKey1);
706 | secondaryKey1 = Utils.ByteArrayReverse(secondaryKey1);
707 | }
708 | primaryTransform1.Init(false, new KeyParameter(primaryKey1));
709 | secondaryTransform1.Init(true, new KeyParameter(secondaryKey1));
710 |
711 | if (primaryTransform2 != null)
712 | {
713 | Log(3, "\t\t\tInitialising " + EncryptionEngineFriendlyName(primaryTransform2) + " with primary key: " + Utils.ByteArrayToHexString(primaryKey2));
714 | Log(3, "\t\t\tInitialising " + EncryptionEngineFriendlyName(primaryTransform2) + " with secondary key: " + Utils.ByteArrayToHexString(secondaryKey2));
715 | if (primaryTransform2 is SerpentEngine)
716 | {
717 | primaryKey2 = Utils.ByteArrayReverse(primaryKey2);
718 | secondaryKey2 = Utils.ByteArrayReverse(secondaryKey2);
719 | }
720 | primaryTransform2.Init(false, new KeyParameter(primaryKey2));
721 | secondaryTransform2.Init(true, new KeyParameter(secondaryKey2));
722 | }
723 | if (primaryTransform3 != null)
724 | {
725 | Log(3, "\t\t\tInitialising " + EncryptionEngineFriendlyName(primaryTransform3) + " with primary key: " + Utils.ByteArrayToHexString(primaryKey3));
726 | Log(3, "\t\t\tInitialising " + EncryptionEngineFriendlyName(primaryTransform3) + " with secondary key: " + Utils.ByteArrayToHexString(secondaryKey3));
727 | if (primaryTransform3 is SerpentEngine)
728 | {
729 | primaryKey3 = Utils.ByteArrayReverse(primaryKey3);
730 | secondaryKey3 = Utils.ByteArrayReverse(secondaryKey3);
731 | }
732 | primaryTransform3.Init(false, new KeyParameter(primaryKey3));
733 | secondaryTransform3.Init(true, new KeyParameter(secondaryKey3));
734 | }
735 |
736 | byte[] decryptedVolumeHeader = null;
737 |
738 | if (primaryTransform2 == null)
739 | {
740 | Log(3, "\t\t\tRunning " + EncryptionEngineFriendlyName(primaryTransform1) + " decryption...");
741 |
742 | decryptedVolumeHeader = XtsTransformSectorBC(
743 | primaryTransform1,
744 | secondaryTransform1,
745 | encryptedVolumeHeader,
746 | 0);
747 |
748 | Log(3, "\t\t\tDecrypted header: " + Utils.ByteArrayToHexString(decryptedVolumeHeader));
749 | }
750 | else if (primaryTransform3 == null)
751 | {
752 | Log(3, "\t\t\tRunning " + EncryptionEngineFriendlyName(primaryTransform2) + " decryption...");
753 |
754 | decryptedVolumeHeader = XtsTransformSectorBC(
755 | primaryTransform2,
756 | secondaryTransform2,
757 | encryptedVolumeHeader,
758 | 0);
759 |
760 | Log(3, "\t\t\tIntermediate output: " + Utils.ByteArrayToHexString(decryptedVolumeHeader));
761 |
762 | Log(3, "\t\t\tRunning " + EncryptionEngineFriendlyName(primaryTransform1) + " decryption...");
763 |
764 | decryptedVolumeHeader = XtsTransformSectorBC(
765 | primaryTransform1,
766 | secondaryTransform1,
767 | decryptedVolumeHeader,
768 | 0);
769 |
770 | Log(3, "\t\t\tDecrypted header: " + Utils.ByteArrayToHexString(decryptedVolumeHeader));
771 | }
772 | else
773 | {
774 | Log(3, "\t\t\tRunning " + EncryptionEngineFriendlyName(primaryTransform3) + " decryption...");
775 |
776 | decryptedVolumeHeader = XtsTransformSectorBC(
777 | primaryTransform3,
778 | secondaryTransform3,
779 | encryptedVolumeHeader,
780 | 0);
781 |
782 | Log(3, "\t\t\tIntermediate output: " + Utils.ByteArrayToHexString(decryptedVolumeHeader));
783 |
784 | Log(3, "\t\t\tRunning " + EncryptionEngineFriendlyName(primaryTransform2) + " decryption...");
785 |
786 | decryptedVolumeHeader = XtsTransformSectorBC(
787 | primaryTransform2,
788 | secondaryTransform2,
789 | decryptedVolumeHeader,
790 | 0);
791 |
792 | Log(3, "\t\t\tIntermediate output: " + Utils.ByteArrayToHexString(decryptedVolumeHeader));
793 |
794 | Log(3, "\t\t\tRunning " + EncryptionEngineFriendlyName(primaryTransform1) + " decryption...");
795 |
796 | decryptedVolumeHeader = XtsTransformSectorBC(
797 | primaryTransform1,
798 | secondaryTransform1,
799 | decryptedVolumeHeader,
800 | 0);
801 |
802 | Log(3, "\t\t\tDecrypted header: " + Utils.ByteArrayToHexString(decryptedVolumeHeader));
803 | }
804 |
805 | if (VolumeHeaderIsValid(decryptedVolumeHeader))
806 | {
807 | Log(2, "\t\t\tSuccess!");
808 |
809 | result.Success = true;
810 | result.ea = ea;
811 | result.ha = ha;
812 | result.CiphertextOffset = Utils.ReadLongFromByteArray(decryptedVolumeHeader, 0x2C);
813 | result.CiphertextLength = Utils.ReadLongFromByteArray(decryptedVolumeHeader, 0x24);
814 | result.VolumeKey = new byte[PBKDF2_OUTPUT_LENGTH];
815 | Array.Copy(decryptedVolumeHeader, 0xC0, result.VolumeKey, 0, PBKDF2_OUTPUT_LENGTH);
816 |
817 | Log(2, "Encryption algorithm for volume: " + EncryptionAlgorithmFriendlyName(result.ea));
818 | Log(2, String.Format("Ciphertext offset (sector) : 0x{0:x}", result.CiphertextOffset / SECTOR_SIZE));
819 | Log(2, String.Format("Ciphertext length (sectors) : 0x{0:x}", result.CiphertextLength / SECTOR_SIZE));
820 | Log(3, "Volume encryption key : " + Utils.ByteArrayToHexString(result.VolumeKey));
821 |
822 | return result;
823 | }
824 | else
825 | {
826 | Log(2, "\t\t\tFailure");
827 | }
828 | }
829 | }
830 |
831 | return result;
832 | }
833 |
834 | static byte[] ProcessDiskSector(
835 | byte[] ciphertext,
836 | long sectorNumber,
837 | IBlockCipher primaryTransform1,
838 | IBlockCipher secondaryTransform1,
839 | IBlockCipher primaryTransform2,
840 | IBlockCipher secondaryTransform2,
841 | IBlockCipher primaryTransform3,
842 | IBlockCipher secondaryTransform3,
843 | bool log,
844 | bool decrypting)
845 | {
846 | byte[] plaintext = new byte[ciphertext.Length];
847 |
848 | if (log) Log(3, (decrypting ? "Ciphertext" : "Plaintext") + ": " + Utils.ByteArrayToHexString(ciphertext));
849 |
850 | if (primaryTransform3 != null)
851 | {
852 | if( log) Log(3, "Running " + EncryptionEngineFriendlyName(primaryTransform1) + " " + (decrypting ? "decryption" : "encryption"));
853 | plaintext = XtsTransformSectorBC(primaryTransform3, secondaryTransform3, ciphertext, sectorNumber);
854 | if (log) Log(3, "Intermediate output: " + Utils.ByteArrayToHexString(plaintext));
855 | if (log) Log(3, "Running " + EncryptionEngineFriendlyName(primaryTransform2) + " " + (decrypting ? "decryption" : "encryption"));
856 | plaintext = XtsTransformSectorBC(primaryTransform2, secondaryTransform2, plaintext, sectorNumber);
857 | if (log) Log(3, "Intermediate output: " + Utils.ByteArrayToHexString(plaintext));
858 | if (log) Log(3, "Running " + EncryptionEngineFriendlyName(primaryTransform1) + " " + (decrypting ? "decryption" : "encryption"));
859 | plaintext = XtsTransformSectorBC(primaryTransform1, secondaryTransform1, plaintext, sectorNumber);
860 | }
861 | else if (primaryTransform2 != null)
862 | {
863 | if (log) Log(3, "Running " + EncryptionEngineFriendlyName(primaryTransform2) + " " + (decrypting ? "decryption" : "encryption"));
864 | plaintext = XtsTransformSectorBC(primaryTransform2, secondaryTransform2, ciphertext, sectorNumber);
865 | if (log) Log(3, "Intermediate output: " + Utils.ByteArrayToHexString(plaintext));
866 | if (log) Log(3, "Running " + EncryptionEngineFriendlyName(primaryTransform1) + " " + (decrypting ? "decryption" : "encryption"));
867 | plaintext = XtsTransformSectorBC(primaryTransform1, secondaryTransform1, plaintext, sectorNumber);
868 | }
869 | else
870 | {
871 | if (log) Log(3, "Running " + EncryptionEngineFriendlyName(primaryTransform1) + " " + (decrypting ? "decryption" : "encryption"));
872 | plaintext = XtsTransformSectorBC(primaryTransform1, secondaryTransform1, ciphertext, sectorNumber);
873 | }
874 |
875 | if (log) Log(3, (decrypting ? "Plaintext" : "Ciphertext") + ": " + Utils.ByteArrayToHexString(plaintext));
876 |
877 | return plaintext;
878 | }
879 |
880 | static void ProcessSectors(FileStream inputFile, FileStream outputFile, long startOffset, long startSectorNumber, long sectorsToProcess, byte[] key, TDEncryptionAlgorithm ea, bool decrypt)
881 | {
882 |
883 | IBlockCipher primaryTransform1 = null;
884 | IBlockCipher secondaryTransform1 = null;
885 | IBlockCipher primaryTransform2 = null;
886 | IBlockCipher secondaryTransform2 = null;
887 | IBlockCipher primaryTransform3 = null;
888 | IBlockCipher secondaryTransform3 = null;
889 |
890 | byte[] primaryKey1 = new byte[32];
891 | byte[] secondaryKey1 = new byte[32];
892 | byte[] primaryKey2 = new byte[32];
893 | byte[] secondaryKey2 = new byte[32];
894 | byte[] primaryKey3 = new byte[32];
895 | byte[] secondaryKey3 = new byte[32];
896 |
897 | Log(2, "Preparing to " + (decrypt ? "decrypt" : "encrypt") + " " + sectorsToProcess.ToString() + " sectors...");
898 |
899 | switch (ea)
900 | {
901 | case TDEncryptionAlgorithm.AES:
902 | primaryTransform1 = new RijndaelEngine();
903 | secondaryTransform1 = new RijndaelEngine();
904 | break;
905 | case TDEncryptionAlgorithm.SERPENT:
906 | primaryTransform1 = new SerpentEngine();
907 | secondaryTransform1 = new SerpentEngine();
908 | primaryKey1 = Utils.ByteArrayReverse(primaryKey1);
909 | secondaryKey1 = Utils.ByteArrayReverse(secondaryKey1);
910 | break;
911 | case TDEncryptionAlgorithm.TWOFISH:
912 | primaryTransform1 = new TwofishEngine();
913 | secondaryTransform1 = new TwofishEngine();
914 | break;
915 | case TDEncryptionAlgorithm.AES_TWOFISH:
916 | primaryTransform2 = new RijndaelEngine();
917 | secondaryTransform2 = new RijndaelEngine();
918 | primaryTransform1 = new TwofishEngine();
919 | secondaryTransform1 = new TwofishEngine();
920 | break;
921 | case TDEncryptionAlgorithm.AES_TWOFISH_SERPENT:
922 | primaryTransform3 = new RijndaelEngine();
923 | secondaryTransform3 = new RijndaelEngine();
924 | primaryTransform2 = new TwofishEngine();
925 | secondaryTransform2 = new TwofishEngine();
926 | primaryTransform1 = new SerpentEngine();
927 | secondaryTransform1 = new SerpentEngine();
928 | break;
929 | case TDEncryptionAlgorithm.SERPENT_AES:
930 | primaryTransform1 = new RijndaelEngine();
931 | secondaryTransform1 = new RijndaelEngine();
932 | primaryTransform2 = new SerpentEngine();
933 | secondaryTransform2 = new SerpentEngine();
934 | break;
935 | case TDEncryptionAlgorithm.SERPENT_TWOFISH_AES:
936 | primaryTransform1 = new RijndaelEngine();
937 | secondaryTransform1 = new RijndaelEngine();
938 | primaryTransform2 = new TwofishEngine();
939 | secondaryTransform2 = new TwofishEngine();
940 | primaryTransform3 = new SerpentEngine();
941 | secondaryTransform3 = new SerpentEngine();
942 | break;
943 | case TDEncryptionAlgorithm.TWOFISH_SERPENT:
944 | primaryTransform2 = new TwofishEngine();
945 | secondaryTransform2 = new TwofishEngine();
946 | primaryTransform1 = new SerpentEngine();
947 | secondaryTransform1 = new SerpentEngine();
948 | break;
949 | }
950 |
951 | if (primaryTransform2 == null)
952 | {
953 | Array.Copy(key, 0, primaryKey1, 0, 32);
954 | Array.Copy(key, 32, secondaryKey1, 0, 32);
955 | }
956 | else if (primaryTransform3 == null)
957 | {
958 | Array.Copy(key, 0, primaryKey1, 0, 32);
959 | Array.Copy(key, 32, primaryKey2, 0, 32);
960 | Array.Copy(key, 64, secondaryKey1, 0, 32);
961 | Array.Copy(key, 96, secondaryKey2, 0, 32);
962 | }
963 | else
964 | {
965 | Array.Copy(key, 0, primaryKey1, 0, 32);
966 | Array.Copy(key, 32, primaryKey2, 0, 32);
967 | Array.Copy(key, 64, primaryKey3, 0, 32);
968 | Array.Copy(key, 96, secondaryKey1, 0, 32);
969 | Array.Copy(key, 128, secondaryKey2, 0, 32);
970 | Array.Copy(key, 160, secondaryKey3, 0, 32);
971 | }
972 |
973 | Log(3, "Initialising " + EncryptionEngineFriendlyName(primaryTransform1) + " for " + (decrypt ? "decryption" : "encryption") + " with primary key: " + Utils.ByteArrayToHexString(primaryKey1));
974 | Log(3, "Initialising " + EncryptionEngineFriendlyName(primaryTransform1) + " with secondary key: " + Utils.ByteArrayToHexString(secondaryKey1));
975 | if (primaryTransform1 is SerpentEngine)
976 | {
977 | primaryKey1 = Utils.ByteArrayReverse(primaryKey1);
978 | secondaryKey1 = Utils.ByteArrayReverse(secondaryKey1);
979 | }
980 | primaryTransform1.Init(!decrypt, new KeyParameter(primaryKey1));
981 | secondaryTransform1.Init(true, new KeyParameter(secondaryKey1));
982 |
983 | if (primaryTransform2 != null)
984 | {
985 | Log(3, "Initialising " + EncryptionEngineFriendlyName(primaryTransform2) + " for " + (decrypt ? "decryption" : "encryption") + " with primary key: " + Utils.ByteArrayToHexString(primaryKey2));
986 | Log(3, "Initialising " + EncryptionEngineFriendlyName(primaryTransform2) + " with secondary key: " + Utils.ByteArrayToHexString(secondaryKey2));
987 | if (primaryTransform2 is SerpentEngine)
988 | {
989 | primaryKey2 = Utils.ByteArrayReverse(primaryKey2);
990 | secondaryKey2 = Utils.ByteArrayReverse(secondaryKey2);
991 | }
992 | primaryTransform2.Init(!decrypt, new KeyParameter(primaryKey2));
993 | secondaryTransform2.Init(true, new KeyParameter(secondaryKey2));
994 | }
995 | if (primaryTransform3 != null)
996 | {
997 | Log(3, "Initialising " + EncryptionEngineFriendlyName(primaryTransform3) + " for " + (decrypt ? "decryption" : "encryption") + " with primary key: " + Utils.ByteArrayToHexString(primaryKey3));
998 | Log(3, "Initialising " + EncryptionEngineFriendlyName(primaryTransform3) + " with secondary key: " + Utils.ByteArrayToHexString(secondaryKey3));
999 |
1000 | if (primaryTransform3 is SerpentEngine)
1001 | {
1002 | primaryKey3 = Utils.ByteArrayReverse(primaryKey3);
1003 | secondaryKey3 = Utils.ByteArrayReverse(secondaryKey3);
1004 | }
1005 | primaryTransform3.Init(!decrypt, new KeyParameter(primaryKey3));
1006 | secondaryTransform3.Init(true, new KeyParameter(secondaryKey3));
1007 | }
1008 |
1009 | if (!decrypt)
1010 | {
1011 | if (primaryTransform3 != null)
1012 | {
1013 | IBlockCipher tmp = primaryTransform1;
1014 | primaryTransform1 = primaryTransform3;
1015 | primaryTransform3 = tmp;
1016 | tmp = secondaryTransform1;
1017 | secondaryTransform1 = secondaryTransform3;
1018 | secondaryTransform3 = tmp;
1019 | }
1020 | else if (primaryTransform2 != null)
1021 | {
1022 | IBlockCipher tmp = primaryTransform1;
1023 | primaryTransform1 = primaryTransform2;
1024 | primaryTransform2 = tmp;
1025 | tmp = secondaryTransform1;
1026 | secondaryTransform1 = secondaryTransform2;
1027 | secondaryTransform2 = tmp;
1028 | }
1029 | }
1030 |
1031 | inputFile.Seek(startOffset * 0x200, SeekOrigin.Begin);
1032 |
1033 | long sectorsDone = 0;
1034 |
1035 | long outerStep = 20480;
1036 |
1037 | byte[] ciphertext = new byte[outerStep * 0x200];
1038 | byte[] plaintext = new byte[outerStep * 0x200];
1039 | byte[] innerCiphertext = new byte[0x200];
1040 |
1041 | for (long outerCount = 0; outerCount < sectorsToProcess; outerCount += outerStep)
1042 | {
1043 | long todoThisTime = Math.Min(outerStep, sectorsToProcess - sectorsDone);
1044 | int bytesRead = inputFile.Read(ciphertext, 0, (int)(todoThisTime * 0x200));
1045 |
1046 | if (bytesRead < ((int)(todoThisTime * 0x200)))
1047 | {
1048 | break;
1049 | }
1050 |
1051 | for (int innerCount = 0; innerCount < todoThisTime; innerCount++)
1052 | {
1053 | Array.Copy(ciphertext, innerCount * 0x200, innerCiphertext, 0, 0x200);
1054 |
1055 | bool v = false;
1056 |
1057 | if (outerCount == 0 && innerCount == 0)
1058 | {
1059 | Log(3, "Processing first sector...");
1060 | v = true;
1061 | }
1062 |
1063 | byte[] innerPlaintext = ProcessDiskSector(innerCiphertext, startSectorNumber + outerCount + innerCount, primaryTransform1, secondaryTransform1, primaryTransform2, secondaryTransform2, primaryTransform3, secondaryTransform3, v, decrypt);
1064 | Array.Copy(innerPlaintext, 0, plaintext, innerCount * 0x200, 0x200);
1065 | }
1066 |
1067 | outputFile.Write(plaintext, 0, (int)(todoThisTime * 0x200));
1068 |
1069 | sectorsDone += todoThisTime;
1070 | Log(1, "Sectors done: " + sectorsDone + " out of " + sectorsToProcess);
1071 | }
1072 | }
1073 |
1074 | static void Log(int logLevel, string s)
1075 | {
1076 | if (logLevel <= verbosity)
1077 | {
1078 | Console.WriteLine(s);
1079 | }
1080 | }
1081 |
1082 | static void Main(string[] args)
1083 | {
1084 | bool showHelp = false;
1085 | bool showVersion = false;
1086 | string password = null;
1087 | string hexKey = null;
1088 | string keyFilename = null;
1089 | bool keyProvided = false;
1090 | bool passwordCheckOnly = false;
1091 | bool checkingPassword = true;
1092 | string passwordKeyFiles = null;
1093 | bool decrypting = true;
1094 | string volumeHeaderFilename = null;
1095 | string volumeHeaderHex = null;
1096 | long volumeHeaderLocation = -1;
1097 | string inputFilename = null;
1098 | string outputFilename = null;
1099 | long firstDecryptSector = -1;
1100 | long firstSectorOffset = -1;
1101 | long sectorsToProcess = -1;
1102 | bool decryptDirection = true;
1103 | byte[] volumeHeaderBytes = null;
1104 |
1105 | VolumeHeaderResult passphraseCheckResult = null;
1106 | bool encryptionAlgorithmSpecifiedOnCommandLine = false;
1107 | bool hashAlgorithmSpecifiedOnCommandLine = false;
1108 | Dictionary encryptionAlgorithmsEnabledCommandLine = new Dictionary();
1109 | Dictionary encryptionAlgorithmsEnabled = new Dictionary();
1110 | Dictionary hashAlgorithmsEnabledCommandLine = new Dictionary();
1111 | Dictionary hashAlgorithmsEnabled = new Dictionary();
1112 | TDEncryptionAlgorithm encryptionAlgorithmForProcessing = TDEncryptionAlgorithm.AES;
1113 |
1114 | foreach (TDHashAlgorithm ha in Enum.GetValues(typeof(TDHashAlgorithm)))
1115 | {
1116 | hashAlgorithmsEnabledCommandLine[ha] = false;
1117 | hashAlgorithmsEnabled[ha] = false;
1118 | }
1119 | foreach (TDEncryptionAlgorithm ea in Enum.GetValues(typeof(TDEncryptionAlgorithm)))
1120 | {
1121 | encryptionAlgorithmsEnabledCommandLine[ea] = false;
1122 | encryptionAlgorithmsEnabled[ea] = false;
1123 | }
1124 |
1125 | var p = new OptionSet()
1126 | {
1127 | { "h|help", "Show usage information and exit", v => showHelp = v != null },
1128 | { "V|version", "Show version and exit", v => showVersion = v != null },
1129 | { "v|verbose", "Verbose mode", v => verbosity = 2 },
1130 | { "d|debug", "Debug mode", v => verbosity = 3 },
1131 | { "q|quiet", "Quiet mode", v => verbosity = 0 },
1132 | { "p|password=", "TrueCrypt password", v => password = v},
1133 | { "k|hex_key=", "Volume key as a hex string", v => hexKey = v},
1134 | { "key_file=", "File containing volume key", v => keyFilename = v},
1135 | { "i|input_file=", "Volume file for decryption", v => inputFilename = v},
1136 | { "o|output_file=", "Destination file for decrypted data", v => outputFilename = v},
1137 | { "e|encrypt", "Run in encryption direction instead", v => decryptDirection = false},
1138 | { "password_check_only", "Check password but don't decrypt volume", v => passwordCheckOnly = v != null},
1139 | { "password_key_files=", "File containing password key files", v => passwordKeyFiles = v},
1140 | { "volume_header_file=", "File containing volume header", v => volumeHeaderFilename = v},
1141 | { "volume_header_hex=", "Volume header as a hex string", v => volumeHeaderHex = v},
1142 | { "volume_header_location=", "Sector number of volume header in volume header file", v => volumeHeaderLocation = Utils.ConvertLongParameter( v)},
1143 | { "first_decrypt_sector=", "Sector number in input file where decryption should begin", v => firstDecryptSector = Utils.ConvertLongParameter( v)},
1144 | { "first_sector_offset=", "TrueCrypt logical sector number for first decrypted sector", v => firstSectorOffset = Utils.ConvertLongParameter( v)},
1145 | { "sectors_to_process=", "Number of sectors to decrypt", v => sectorsToProcess = Utils.ConvertLongParameter( v)},
1146 | { "aes", "Try AES encryption algorithm", v => {encryptionAlgorithmsEnabledCommandLine[TDEncryptionAlgorithm.AES] = true; encryptionAlgorithmSpecifiedOnCommandLine = true;}},
1147 | { "serpent", "Try Serpent encryption algorithm", v => {encryptionAlgorithmsEnabledCommandLine[TDEncryptionAlgorithm.SERPENT] = true; encryptionAlgorithmSpecifiedOnCommandLine = true;}},
1148 | { "twofish", "Try Twofish encryption algorithm", v => {encryptionAlgorithmsEnabledCommandLine[TDEncryptionAlgorithm.TWOFISH] = true; encryptionAlgorithmSpecifiedOnCommandLine = true;}},
1149 | { "aes_twofish", "Try AES-Twofish encryption cascade", v => {encryptionAlgorithmsEnabledCommandLine[TDEncryptionAlgorithm.AES_TWOFISH] = true; encryptionAlgorithmSpecifiedOnCommandLine = true;}},
1150 | { "aes_twofish_serpent", "Try AES-Twofish-Serpent encryption cascade", v => {encryptionAlgorithmsEnabledCommandLine[TDEncryptionAlgorithm.AES_TWOFISH_SERPENT] = true; encryptionAlgorithmSpecifiedOnCommandLine = true;}},
1151 | { "serpent_aes", "Try Serpent-AES encryption cascade", v => {encryptionAlgorithmsEnabledCommandLine[TDEncryptionAlgorithm.SERPENT_AES] = true; encryptionAlgorithmSpecifiedOnCommandLine = true;}},
1152 | { "serpent_twofish_aes", "Try Serpent-Twofish-AES encryption cascade", v => {encryptionAlgorithmsEnabledCommandLine[TDEncryptionAlgorithm.SERPENT_TWOFISH_AES] = true; encryptionAlgorithmSpecifiedOnCommandLine = true;}},
1153 | { "twofish_serpent", "Try Twofish-Serpent encryption cascade", v => {encryptionAlgorithmsEnabledCommandLine[TDEncryptionAlgorithm.TWOFISH_SERPENT] = true; encryptionAlgorithmSpecifiedOnCommandLine = true;}},
1154 | { "all_encryption_algorithms", "Try all encryption algorithms", v => { foreach (TDEncryptionAlgorithm ea in Enum.GetValues(typeof(TDEncryptionAlgorithm))) { encryptionAlgorithmsEnabledCommandLine[ea] = true; } encryptionAlgorithmSpecifiedOnCommandLine = true;}},
1155 | { "ripemd160", "Try RIPEMD-160 hash algorithm", v => {hashAlgorithmsEnabledCommandLine[TDHashAlgorithm.RIPEMD] = true; hashAlgorithmSpecifiedOnCommandLine = true;}},
1156 | { "ripemd160_system", "Try RIPEMD-160 hash algorithm (system encryption variant)", v => {hashAlgorithmsEnabledCommandLine[TDHashAlgorithm.RIPEMD_SYSTEM] = true; hashAlgorithmSpecifiedOnCommandLine = true;}},
1157 | { "whirlpool", "Try Whirlpool hash algorithm", v => {hashAlgorithmsEnabledCommandLine[TDHashAlgorithm.WHIRLPOOL] = true; hashAlgorithmSpecifiedOnCommandLine = true;}},
1158 | { "sha512", "Try SHA-512 hash algorithm", v => {hashAlgorithmsEnabledCommandLine[TDHashAlgorithm.SHA512] = true; hashAlgorithmSpecifiedOnCommandLine = true;}},
1159 | { "all_hash_algorithms", "Try all hash algorithms", v => { foreach (TDHashAlgorithm ea in Enum.GetValues(typeof(TDHashAlgorithm))) { hashAlgorithmsEnabledCommandLine[ea] = true; } hashAlgorithmSpecifiedOnCommandLine = true;}},
1160 |
1161 | };
1162 |
1163 | List extra;
1164 |
1165 | try
1166 | {
1167 | extra = p.Parse(args);
1168 |
1169 | foreach (string ee in extra)
1170 | {
1171 | UsageError("Unrecognised option \"" + ee + "\"");
1172 | }
1173 | }
1174 | catch (OptionException e)
1175 | {
1176 | UsageError(e.Message + "\n" + "Try 'Untrue --help' for more information");
1177 | }
1178 |
1179 | if (showHelp)
1180 | {
1181 | ShowHelp(p);
1182 | return;
1183 | }
1184 |
1185 | if (showVersion)
1186 | {
1187 | ShowVersion();
1188 | return;
1189 | }
1190 |
1191 | if (hexKey != null || keyFilename != null)
1192 | {
1193 | keyProvided = true;
1194 | }
1195 |
1196 | if (hexKey != null && keyFilename != null)
1197 | {
1198 | UsageError("You have provided a key in hex form and also a file containing the key. Please specify only one of these.");
1199 | }
1200 |
1201 | if (keyProvided && passwordCheckOnly)
1202 | {
1203 | UsageWarning("You have asked to check the password only, but have also provided a volume key - the key will be ignored.");
1204 | hexKey = keyFilename = null;
1205 | keyProvided = false;
1206 | }
1207 |
1208 | if (keyProvided && password != null)
1209 | {
1210 | UsageWarning("You have provided a volume key, but also provided a password. The password will be ignored.");
1211 | password = null;
1212 | }
1213 |
1214 | if (keyProvided)
1215 | {
1216 | checkingPassword = false;
1217 | }
1218 |
1219 | if (passwordCheckOnly)
1220 | {
1221 | decrypting = false;
1222 | }
1223 |
1224 | if (!checkingPassword && volumeHeaderFilename != null)
1225 | {
1226 | UsageWarning("Key has been supplied so volume_header_file parameter will be ignored.");
1227 | volumeHeaderFilename = null;
1228 | }
1229 |
1230 | if (!checkingPassword && volumeHeaderHex != null)
1231 | {
1232 | UsageWarning("Key has been supplied so volume_header_hex parameter will be ignored.");
1233 | volumeHeaderHex = null;
1234 | }
1235 |
1236 | if (!checkingPassword && volumeHeaderLocation != -1)
1237 | {
1238 | UsageWarning("Key has been supplied so volume_header_location parameter will be ignored.");
1239 | volumeHeaderLocation = -1;
1240 | }
1241 |
1242 | if (checkingPassword && volumeHeaderFilename != null && volumeHeaderHex != null)
1243 | {
1244 | UsageError("You have provided the volume header in hex form and also a file containing the volume header. Please specify only one of these.");
1245 | }
1246 |
1247 | if (!decrypting && inputFilename != null)
1248 | {
1249 | UsageWarning("Not performing decryption (password_check_only) so input_file parameter will be ignored (use volume_header_file instead).");
1250 | inputFilename = null;
1251 | }
1252 |
1253 | if (!decrypting && outputFilename != null)
1254 | {
1255 | UsageWarning("Not performing decryption (password_check_only) so output_file parameter will be ignored (no decrypted output is generated).");
1256 | outputFilename = null;
1257 | }
1258 |
1259 | if (!decrypting && firstDecryptSector != -1)
1260 | {
1261 | UsageWarning("Not performing decryption (password_check_only) so first_decrypt_sector parameter will be ignored.");
1262 | firstDecryptSector = -1;
1263 | }
1264 |
1265 | if (!decrypting && firstSectorOffset != -1)
1266 | {
1267 | UsageWarning("Not performing decryption so first_sector_offset parameter will be ignored.");
1268 | firstSectorOffset = -1;
1269 | }
1270 |
1271 | if (checkingPassword)
1272 | {
1273 | if (!encryptionAlgorithmSpecifiedOnCommandLine)
1274 | {
1275 | Log(2, "No encryption algorithms specified on command line, so trying them all");
1276 | foreach (TDEncryptionAlgorithm ea in Enum.GetValues(typeof(TDEncryptionAlgorithm)))
1277 | {
1278 | encryptionAlgorithmsEnabled[ea] = true;
1279 | }
1280 | }
1281 | else
1282 | {
1283 | foreach (TDEncryptionAlgorithm ea in Enum.GetValues(typeof(TDEncryptionAlgorithm)))
1284 | {
1285 | encryptionAlgorithmsEnabled[ea] = encryptionAlgorithmsEnabledCommandLine[ea];
1286 | }
1287 | }
1288 | if (!hashAlgorithmSpecifiedOnCommandLine)
1289 | {
1290 | Log(2, "No hash algorithms specified on command line, so trying them all");
1291 | foreach (TDHashAlgorithm ha in Enum.GetValues(typeof(TDHashAlgorithm)))
1292 | {
1293 | hashAlgorithmsEnabled[ha] = true;
1294 | }
1295 | }
1296 | else
1297 | {
1298 | foreach (TDHashAlgorithm ea in Enum.GetValues(typeof(TDHashAlgorithm)))
1299 | {
1300 | hashAlgorithmsEnabled[ea] = hashAlgorithmsEnabledCommandLine[ea];
1301 | }
1302 | }
1303 | }
1304 |
1305 | if (decrypting)
1306 | {
1307 | // sort out encryption algorithms
1308 | if (!checkingPassword)
1309 | {
1310 | if (!encryptionAlgorithmSpecifiedOnCommandLine)
1311 | {
1312 | Log(2, "No encryption algorithm specified on command line, so using the default (AES)");
1313 | encryptionAlgorithmsEnabled[TDEncryptionAlgorithm.AES] = true;
1314 | }
1315 | else
1316 | {
1317 | int count = 0;
1318 | foreach (TDEncryptionAlgorithm ea in Enum.GetValues(typeof(TDEncryptionAlgorithm)))
1319 | {
1320 | if (encryptionAlgorithmsEnabledCommandLine[ea])
1321 | {
1322 | encryptionAlgorithmsEnabled[ea] = true;
1323 | encryptionAlgorithmForProcessing = ea;
1324 | count += 1;
1325 | }
1326 | }
1327 |
1328 | if (count > 1)
1329 | {
1330 | UsageError("Please specify only one encryption algorithm");
1331 | }
1332 |
1333 | if (hashAlgorithmSpecifiedOnCommandLine)
1334 | {
1335 | UsageWarning("Hash algorithms specified on command line will be ignored when not checking passwords.");
1336 | }
1337 | }
1338 | }
1339 |
1340 | if( !checkingPassword && !keyProvided)
1341 | {
1342 | UsageError("No volume key provided (use --hex_key or --key_file)");
1343 | }
1344 |
1345 | if( !checkingPassword && keyFilename != null)
1346 | {
1347 | try
1348 | {
1349 | keyFile = File.Open(keyFilename, FileMode.Open, FileAccess.Read);
1350 | }
1351 | catch (IOException)
1352 | {
1353 | UsageError(String.Format( "Couldn't open key file {0} for reading", keyFilename));
1354 | }
1355 | }
1356 |
1357 | if (inputFilename == null)
1358 | {
1359 | UsageError("No input file specified (use --input_file)");
1360 | }
1361 | else
1362 | {
1363 | try
1364 | {
1365 | inputFile = File.Open(inputFilename, FileMode.Open, FileAccess.Read);
1366 | }
1367 | catch (IOException)
1368 | {
1369 | UsageError(String.Format( "Couldn't open input file {0} for reading", inputFilename));
1370 | }
1371 | }
1372 |
1373 | if (outputFilename == null)
1374 | {
1375 | UsageError("No output file specified (use --output_file)");
1376 | }
1377 | else
1378 | {
1379 | if (File.Exists(outputFilename))
1380 | {
1381 | UsageError(String.Format("Output file {0} already exists, won't write to existing file.", outputFilename));
1382 | }
1383 | }
1384 |
1385 | if (!checkingPassword && firstDecryptSector == -1) // if we *are* checking the password then default to obtaining first decrypt sector from decrypted volume header
1386 | {
1387 | firstDecryptSector = 0;
1388 | }
1389 |
1390 | if (!checkingPassword && firstSectorOffset == -1)
1391 | {
1392 | Log(1, "Setting XTS secondary key offset to " + firstDecryptSector.ToString() + ". This might be right and it might not. Set it explicitly (using --first_sector_offset) if your input file doesn't contain the entire encrypted volume");
1393 | firstSectorOffset = firstDecryptSector;
1394 | }
1395 |
1396 | if (!checkingPassword && sectorsToProcess == -1) // we'll just process to the end of the input file... but if we *are* checking the password then we're going to default to obtaining number of sectors from decrypted volume header
1397 | {
1398 | sectorsToProcess = (inputFile.Length / SECTOR_SIZE) - firstDecryptSector;
1399 | }
1400 |
1401 | if (sectorsToProcess != -1 && (sectorsToProcess > ((inputFile.Length / SECTOR_SIZE) - firstDecryptSector)))
1402 | {
1403 | UsageError("Number of specified sectors to process will go beyond end of input file.");
1404 | }
1405 | }
1406 |
1407 | if (checkingPassword)
1408 | {
1409 | if (password == null)
1410 | {
1411 | UsageError("No volume password supplied.");
1412 | }
1413 |
1414 | if (volumeHeaderHex == null)
1415 | {
1416 | if (volumeHeaderFilename == null)
1417 | {
1418 | if (inputFile != null)
1419 | {
1420 | volumeHeaderFile = inputFile;
1421 | }
1422 | else
1423 | {
1424 | UsageError("No volume header file specified (and no input file).");
1425 | }
1426 | }
1427 | else
1428 | {
1429 | try
1430 | {
1431 | volumeHeaderFile = File.Open(volumeHeaderFilename, FileMode.Open, FileAccess.Read);
1432 | }
1433 | catch (IOException)
1434 | {
1435 | UsageError(String.Format("Couldn't open volume header file {0} for reading", volumeHeaderFilename));
1436 | }
1437 | }
1438 | }
1439 |
1440 | if (volumeHeaderHex == null && volumeHeaderLocation == -1)
1441 | {
1442 | byte[] volumeHeaderSector = Utils.ReadSector(volumeHeaderFile, 0);
1443 |
1444 | SectorGuess guess = GuessSectorFormat(volumeHeaderSector);
1445 |
1446 | string s = volumeHeaderFilename == null ? "input file" : "volume header file";
1447 |
1448 | if (guess == SectorGuess.CIPHERTEXT)
1449 | {
1450 | Log(1, "Sector 0 of " + s + " appears to be encrypted - trying this as the volume header");
1451 | volumeHeaderLocation = 0;
1452 | }
1453 | else if (guess == SectorGuess.ZEROS)
1454 | {
1455 | Log(1, "Sector 0 of " + s + " is all zeros - assuming this to be a rescue file image, and reading volume header from sector " + VOLUME_HEADER_SECTOR_ON_RESCUE_DISK.ToString());
1456 | volumeHeaderLocation = VOLUME_HEADER_SECTOR_ON_RESCUE_DISK;
1457 | }
1458 | else if (guess == SectorGuess.TRUECRYPT_MBR)
1459 | {
1460 | Log(1, "Sector 0 of " + s + " is a TrueCrypt MBR - reading volume header from sector " + VOLUME_HEADER_SECTOR_ON_FIRST_TRACK);
1461 | volumeHeaderLocation = VOLUME_HEADER_SECTOR_ON_FIRST_TRACK;
1462 | }
1463 | else
1464 | {
1465 | Log(1, "Trying sector 0 of " + s + " as the volume header, although it does not appear to be encrypted and its format is not recognised");
1466 | volumeHeaderLocation = 0;
1467 | }
1468 | }
1469 | else
1470 | {
1471 | byte[] volumeHeaderSector = Utils.ReadSector(volumeHeaderFile, volumeHeaderLocation);
1472 | SectorGuess guess = GuessSectorFormat(volumeHeaderSector);
1473 | string s = volumeHeaderFilename == null ? "input file" : "volume header file";
1474 |
1475 | if (guess != SectorGuess.CIPHERTEXT)
1476 | {
1477 | Log(1, "Specified volume header sector does not appear to be encrypted - trying it anyway. Note that Untrue will attempt to locate volume header sector itself if you omit the volume_header_location parameter");
1478 | }
1479 | }
1480 |
1481 | if (volumeHeaderHex != null)
1482 | {
1483 | try
1484 | {
1485 | volumeHeaderBytes = Utils.ConvertHexStringToBytes(volumeHeaderHex);
1486 | }
1487 | catch (FormatException)
1488 | {
1489 | UsageError("Failed to parse volume_header_hex as hex string");
1490 | }
1491 |
1492 | if (volumeHeaderBytes.Length != VOLUME_HEADER_SIZE)
1493 | {
1494 | UsageError(String.Format("volume_header_hex should be a hex string of exactly {0} bytes", VOLUME_HEADER_SIZE));
1495 | }
1496 |
1497 | SectorGuess guess = GuessSectorFormat(volumeHeaderBytes);
1498 |
1499 | if (guess != SectorGuess.CIPHERTEXT)
1500 | {
1501 | Log(1, "Volume header provided does not appear to be an encrypted sector - trying it anyway");
1502 | }
1503 | }
1504 | else
1505 | {
1506 | if (volumeHeaderLocation * SECTOR_SIZE + VOLUME_HEADER_SIZE > volumeHeaderFile.Length)
1507 | {
1508 | UsageError("Specified volume header location will go beyond end of volume header file.");
1509 | }
1510 |
1511 | try
1512 | {
1513 | volumeHeaderBytes = Utils.ReadSector(volumeHeaderFile, volumeHeaderLocation);
1514 | }
1515 | catch
1516 | {
1517 | UsageError("Failed to read volume header from file");
1518 | }
1519 | }
1520 |
1521 | passphraseCheckResult = DecryptVolumeHeader(volumeHeaderBytes, password, passwordKeyFiles, hashAlgorithmsEnabled, encryptionAlgorithmsEnabled);
1522 | encryptionAlgorithmForProcessing = passphraseCheckResult.ea;
1523 |
1524 | if (passphraseCheckResult.Success)
1525 | {
1526 | if (passwordKeyFiles == null)
1527 | {
1528 | Log(1, "Passphrase \"" + password + "\" was correct");
1529 | }
1530 | else
1531 | {
1532 | Log(1, "Passphrase \"" + password + "\" and keyfiles were correct");
1533 | }
1534 | Log(1, "Encryption algorithm was " + EncryptionAlgorithmFriendlyName(passphraseCheckResult.ea));
1535 | Log(1, "Hash algorithm was " + HashAlgorithmFriendlyName(passphraseCheckResult.ha));
1536 | }
1537 | else
1538 | {
1539 | Log(1, "Failed to decrypt volume header - incorrect passphrase?");
1540 | if (decrypting)
1541 | {
1542 | Log(1, (decrypting ? "Decryption" : "Encryption") + " cannot be performed.");
1543 | }
1544 | }
1545 | }
1546 |
1547 | if (decrypting && (passphraseCheckResult == null || passphraseCheckResult.Success))
1548 | {
1549 | if (firstDecryptSector == -1 && passphraseCheckResult != null && passphraseCheckResult.Success)
1550 | {
1551 |
1552 | firstDecryptSector = passphraseCheckResult.CiphertextOffset / SECTOR_SIZE;
1553 | }
1554 |
1555 | if (firstSectorOffset == -1 && passphraseCheckResult != null && passphraseCheckResult.Success)
1556 | {
1557 | Log(2, String.Format("Setting first sector offset to 0x{0:x}", firstDecryptSector));
1558 | firstSectorOffset = firstDecryptSector;
1559 | }
1560 |
1561 | if (sectorsToProcess == -1 && passphraseCheckResult != null && passphraseCheckResult.Success)
1562 | {
1563 | Log(2, String.Format("Setting sectors to process to 0x{0:x}", Math.Min(passphraseCheckResult.CiphertextLength / SECTOR_SIZE, (inputFile.Length - firstDecryptSector * SECTOR_SIZE) / SECTOR_SIZE)));
1564 | sectorsToProcess = Math.Min(passphraseCheckResult.CiphertextLength / SECTOR_SIZE, (inputFile.Length - firstDecryptSector * SECTOR_SIZE) / SECTOR_SIZE);
1565 | }
1566 |
1567 | if ((firstDecryptSector + sectorsToProcess) * SECTOR_SIZE > inputFile.Length)
1568 | {
1569 | UsageError("Going to read beyond end of input file, use a smaller sectors_to_process value");
1570 | return;
1571 | }
1572 |
1573 | byte[] firstCiphertextSector = Utils.ReadSector(inputFile, firstDecryptSector);
1574 | SectorGuess guess = GuessSectorFormat(firstCiphertextSector);
1575 |
1576 | if (guess != SectorGuess.CIPHERTEXT)
1577 | {
1578 | Log(1, "First sector for processing does not appear to be encrypted, trying anyway");
1579 | }
1580 |
1581 | int keyLengthRequired = KeyLengthForEncryptionAlgorithm(encryptionAlgorithmForProcessing);
1582 | byte[] volumeKey = new byte[keyLengthRequired];
1583 |
1584 | if (passphraseCheckResult != null)
1585 | {
1586 | volumeKey = passphraseCheckResult.VolumeKey;
1587 | }
1588 | else
1589 | {
1590 | if (hexKey != null)
1591 | {
1592 | if (hexKey.Length != keyLengthRequired * 2)
1593 | {
1594 | UsageError( "Hexadecimal key should be " + keyLengthRequired + " bytes");
1595 | }
1596 | else
1597 | {
1598 | volumeKey = Utils.ConvertHexStringToBytes(hexKey);
1599 | }
1600 | }
1601 | else
1602 | {
1603 | int keyBytesRead = keyFile.Read( volumeKey, 0, keyLengthRequired);
1604 |
1605 | if( keyBytesRead < keyLengthRequired)
1606 | {
1607 | UsageError( "Failed to read " + keyLengthRequired + " key bytes from file");
1608 | }
1609 | }
1610 | }
1611 |
1612 | try
1613 | {
1614 | outputFile = File.Open(outputFilename, FileMode.CreateNew, FileAccess.Write);
1615 | }
1616 | catch (IOException)
1617 | {
1618 | UsageError(String.Format("Couldn't open output file {0} for writing", outputFilename));
1619 | }
1620 |
1621 | ProcessSectors(inputFile, outputFile, firstDecryptSector, firstSectorOffset, sectorsToProcess, volumeKey, encryptionAlgorithmForProcessing, decryptDirection);
1622 | }
1623 | }
1624 | }
1625 | }
1626 |
--------------------------------------------------------------------------------