-
Notifications
You must be signed in to change notification settings - Fork 2
/
tf_crypt.adb
219 lines (170 loc) · 8.61 KB
/
tf_crypt.adb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
-- Encryptuion/dectryption of files with the Threefish cipher for blocks of 256 bits (Threefish-256)
-- Copyright (C) 2022 by PragmAda Software Engineering
-- Released under the terms of the GPL license version 3; see https://opensource.org/licenses
with Ada.Command_Line;
with Ada.Directories;
with Ada.Numerics.Discrete_Random;
with Ada.Sequential_IO;
with Ada.Text_IO;
with Ada.Unchecked_Conversion;
with Password_Line;
with PragmARC.Encryption.Threefish.Block_256;
procedure TF_Crypt is
use PragmARC.Encryption;
package Byte_IO is new Ada.Sequential_IO (Element_Type => Threefish.Byte);
Bytes_Per_Block : constant := 32;
Bytes_Per_Couple : constant := 16;
subtype Block_As_Bytes is Threefish.Block_256.Block_As_Bytes;
subtype Block_As_String is String (1 .. Bytes_Per_Block);
subtype Couple_As_String is String (1 .. Bytes_Per_Couple);
function To_Block (Source : Block_As_String) return Threefish.Block_256.Block;
function To_Couple (Source : Couple_As_String) return Threefish.Couple;
procedure Usage; -- Displays usage instructions
procedure Encrypt (KS: in Threefish.Block_256.Key_Schedule_Handle; Name : in String); -- Encrypts Name using KS
procedure Decrypt (KS: in Threefish.Block_256.Key_Schedule_Handle; Name : in String); -- Decrypts Name using KS
function To_Block (Source : Block_As_String) return Threefish.Block_256.Block is
function To_List is new Ada.Unchecked_Conversion (Source => Block_As_String, Target => Block_As_Bytes);
List : constant Block_As_Bytes := To_List (Source);
begin -- To_Block
return Threefish.Block_256.Block_From_Bytes (List);
end To_Block;
function To_Couple (Source : Couple_As_String) return Threefish.Couple is
subtype Couple_As_Bytes is Threefish.Byte_List (1 .. Source'Length);
function To_List is new Ada.Unchecked_Conversion (Source => Couple_As_String, Target => Couple_As_Bytes);
List : constant Couple_As_Bytes := To_List (Source);
Result : Threefish.Couple;
begin -- To_Couple
Result (Result'First) := Threefish.Word_From_Bytes (List (List'First .. List'First + Threefish.Word_As_Bytes'Length - 1) );
Result (Result'Last) := Threefish.Word_From_Bytes (List (List'First + Threefish.Word_As_Bytes'Length .. List'Last) );
return Result;
end To_Couple;
procedure Usage is
-- Empty
begin -- Usage
Ada.Text_IO.Put_Line (Item => "usage: tf_crypt [-d] <file name>");
Ada.Text_IO.Put_Line (Item => " prompts the user to enter a passphrase");
Ada.Text_IO.Put_Line (Item => " if -d given, decrypts <file name>, else encrypts <file name>");
Ada.Text_IO.Put_Line (Item => " the passphrase should be 48 characters, but it will be truncated");
Ada.Text_IO.Put_Line (Item => " or padded with X to achieve that length");
Ada.Text_IO.Put_Line (Item => " the output file when encrypting, will have the same name as the input, with .tfe appended");
Ada.Text_IO.Put_Line (Item => " when decrypting,");
Ada.Text_IO.Put_Line (Item => " if <file name> ends with .tfe, the output file will be the input with .tfe removed");
Ada.Text_IO.Put_Line (Item => " else the output file will be the same name as the input, with .tfd appended");
end Usage;
procedure Encrypt (KS: in Threefish.Block_256.Key_Schedule_Handle; Name : in String) is
Input : Byte_IO.File_Type;
Output : Byte_IO.File_Type;
Length : Threefish.Word_As_Bytes;
Byte_Block : Block_As_Bytes;
Word_Block : Threefish.Block_256.Block;
Size : Threefish.Word;
Written : Threefish.Word := 0;
use type Threefish.Word;
begin -- Encrypt
Byte_IO.Open (File => Input, Mode => Byte_IO.In_File, Name => Name);
Byte_IO.Create (File => Output, Name => Name & ".tfe");
Size := Threefish.Word (Ada.Directories.Size (Name) );
Length := Threefish.Bytes_From_Word (Size);
Write_Length : for I in Length'Range loop
Byte_IO.Write (File => Output, Item => Length (I) );
end loop Write_Length;
All_Blocks : loop
exit All_Blocks when Byte_IO.End_Of_File (Input);
if Size - Written < Bytes_Per_Block then -- Last block has unused bytes; fill them with random values
Random_Fill : declare
package Random is new Ada.Numerics.Discrete_Random (Result_Subtype => Threefish.Byte);
Gen : Random.Generator;
begin -- Random_Fill
Random.Reset (Gen => Gen);
Byte_Block := (others => Random.Random (Gen) );
end Random_Fill;
end if;
One_Block : for I in Byte_Block'Range loop
exit One_Block when Byte_IO.End_Of_File (Input);
Byte_IO.Read (File => Input, Item => Byte_Block (I) );
end loop One_Block;
Word_Block := Threefish.Block_256.Block_From_Bytes (Byte_Block);
Threefish.Block_256.Encrypt (Key_Schedule => KS, Text => Word_Block);
Byte_Block := Threefish.Block_256.Bytes_From_Block (Word_Block);
Write_Block : for I in Byte_Block'Range loop
Byte_IO.Write (File => Output, Item => Byte_Block (I) );
Written := Written + 1;
end loop Write_Block;
end loop All_Blocks;
Byte_IO.Close (File => Input);
Byte_IO.Close (File => Output);
end Encrypt;
procedure Decrypt (KS: in Threefish.Block_256.Key_Schedule_Handle; Name : in String) is
Input : Byte_IO.File_Type;
Output : Byte_IO.File_Type;
Len_Bytes : Threefish.Word_As_Bytes;
Length : Threefish.Word;
Count : Threefish.Word := 0;
Byte_Block : Block_As_Bytes;
Word_Block : Threefish.Block_256.Block;
use type Threefish.Word;
begin -- Decrypt
Byte_IO.Open (File => Input, Mode => Byte_IO.In_File, Name => Name);
if Name'Length > 4 and then Name (Name'Last - 3 .. Name'Last) = ".tfe" then
Byte_IO.Create (File => Output, Name => Name (Name'First .. Name'Last - 4) );
else
Byte_IO.Create (File => Output, Name => Name & ".tfd");
end if;
Read_Length : for I in Len_Bytes'Range loop
Byte_IO.Read (File => Input, Item => Len_Bytes (I) );
end loop Read_Length;
Length := Threefish.Word_From_Bytes (Len_Bytes);
All_Blocks : loop
exit All_Blocks when Byte_IO.End_Of_File (Input);
Read_Block : for I in Byte_Block'Range loop
Byte_IO.Read (File => Input, Item => Byte_Block (I) );
end loop Read_Block;
Word_Block := Threefish.Block_256.Block_From_Bytes (Byte_Block);
Threefish.Block_256.Decrypt (Key_Schedule => KS, Text => Word_Block);
Byte_Block := Threefish.Block_256.Bytes_From_Block (Word_Block);
Write_Bytes : for I in Byte_Block'Range loop
exit Write_Bytes when Count >= Length;
Byte_IO.Write (File => Output, Item => Byte_Block (I) );
Count := Count + 1;
end loop Write_Bytes;
end loop All_Blocks;
Byte_IO.Close (File => Input);
Byte_IO.Close (File => Output);
end Decrypt;
Encrypting : Boolean := True;
Name_Arg : Positive := 1;
Key : Threefish.Block_256.Block;
Tweak : Threefish.Couple;
KS : Threefish.Block_256.Key_Schedule_Handle;
use type Ada.Directories.File_Kind;
begin -- TF_Crypt
if Ada.Command_Line.Argument_Count = 0 then
Usage;
return;
end if;
if Ada.Command_Line.Argument_Count >= 2 and then Ada.Command_Line.Argument (1) = "-d" then
Encrypting := False;
Name_Arg := 2;
end if;
if not Ada.Directories.Exists (Ada.Command_Line.Argument (Name_Arg) ) or else
Ada.Directories.Kind (Ada.Command_Line.Argument (Name_Arg) ) /= Ada.Directories.Ordinary_File
then
Ada.Text_IO.Put_Line (Item => Ada.Command_Line.Argument (Name_Arg) & " cannot be read");
Usage;
return;
end if;
Ada.Text_IO.Put_Line (Item => "Enter passphrase:");
Get_Passphrase : declare
Line : constant String := Password_Line;
Pass : constant String := Line & (Line'Last + 1 .. Line'First + Bytes_Per_Block + Bytes_Per_Couple - 1 => 'X');
begin -- Get_Passphrase
Key := To_Block (Pass (Pass'First .. Pass'First + Bytes_Per_Block - 1) );
Tweak := To_Couple (Pass (Pass'First + Bytes_Per_Block .. Pass'First + Bytes_Per_Block + Bytes_Per_Couple - 1) );
end Get_Passphrase;
Threefish.Block_256.Create_Key_Schedule (Key => Key, Tweak => Tweak, Key_Schedule => KS);
if Encrypting then
Encrypt (KS => KS, Name => Ada.Command_Line.Argument (Name_Arg) );
else
Decrypt (KS => KS, Name => Ada.Command_Line.Argument (Name_Arg) );
end if;
end TF_Crypt;