1 module smimeasym.test;
2 
3 import std.algorithm.comparison : equal;
4 import std.conv : to;
5 import std.exception : assertThrown;
6 import std.file : read, readText, deleteme, write;
7 import std.format : format;
8 import std.process : executeShell;
9 import std.stdio;
10 
11 import smimeasym;
12 
13 private void deleteThat(string fn) {
14 	import std.file : exists, remove;
15 	if(exists(fn)) {
16 		remove(fn);
17 	}
18 }
19 
20 unittest {
21 	string[3] pubKeys =
22 		[ "./alice.pub"
23 		, "./bob.pub"
24 		, "./frank.pub"
25 		];
26 
27 	string[3] privKeys =
28 		[ "./alice.key"
29 		, "./bob.key"
30 		, "./frank.key"
31 		];
32 
33 	// the unencrypted data written to a file
34 	string data = "Hello openssl world";
35 	string dataFilename = deleteme ~ ".orig";
36 	write(dataFilename, data);
37 	scope(success) {
38 		deleteThat(dataFilename);
39 	}
40 
41 	string encFilenameFromShell = deleteme ~ ".sh.src";
42 
43 	// encrypt the data with the openssl cli
44 	string encFromShell = format(
45 			"openssl smime -encrypt -aes256 -in %s -out %s -outform PEM %--(%s %)"
46 			, dataFilename, encFilenameFromShell, pubKeys);
47 	auto encSh = executeShell(encFromShell);
48 	assert(encSh.status == 0, format("%s\n%s\n%s", encSh.status, encSh.output
49 			, encSh));
50 	scope(success) {
51 		deleteThat(encFilenameFromShell);
52 	}
53 
54 	// decrypt data encrypt by the openssl cli
55 	ubyte[] encFileFromCli = cast(ubyte[])read(encFilenameFromShell);
56 	foreach(privKey; privKeys) {
57 		ubyte[] decrp = smimeDecryption(encFileFromCli, privKey);
58 		string decrpStr = cast(string)decrp;
59 		assert(data == decrpStr, format("\norig: %s\ndecr: %s", data, decrpStr));
60 	}
61 
62 	X509*[] keys;
63 	X509*[] keys2;
64 	assertThrown(smimeEncryptionWithCerts(cast(ubyte[])data, keys));
65 	keys ~= null;
66 	assertThrown(smimeEncryptionWithCerts(cast(ubyte[])data, keys));
67 	keys = [];
68 
69 	EVP_PKEY*[] privKeysPtr;
70 	foreach(pk; privKeys) {
71 		privKeysPtr ~= loadKey(pk);
72 	}
73 
74 	EVP_PKEY*[] privKeysPtr2;
75 	foreach(pk; privKeys) {
76 		privKeysPtr2 ~= loadKeyFromString(readText(pk));
77 	}
78 
79 
80 	foreach(pubKey; pubKeys) {
81 		auto t = loadCert(pubKey);
82 		assert(t != null, pubKey);
83 		keys ~= t;
84 	}
85 	foreach(pubKey; pubKeys) {
86 		string c = readText(pubKey);
87 		keys2 ~= loadCertFromString(c);
88 	}
89 	ubyte[] encArray = smimeEncryptionWithCerts(cast(ubyte[])data, keys);
90 	ubyte[] encArray2 = smimeEncryptionWithCerts(cast(ubyte[])data, keys2);
91 
92 	// encrypt with this library and write to disk
93 	string encFilename = deleteme ~ ".src";
94 	ubyte[] enc = smimeEncryption(cast(ubyte[])data, pubKeys);
95 
96 	// as the aes key should be random these shouldn't match
97 	assert(!equal(enc, encArray));
98 
99 	write(encFilename, enc);
100 	scope(success) {
101 		deleteThat(encFilename);
102 	}
103 
104 	// decrypt with private keys
105 	foreach(privKey; privKeys) {
106 		ubyte[] decrp = smimeDecryption(enc, privKey);
107 		string decrpStr = cast(string)decrp;
108 		assert(data == decrpStr, format("\norig: %s\ndecr: %s", data, decrpStr));
109 	}
110 
111 	foreach(privKey; privKeys) {
112 		ubyte[] decrp = smimeDecryption(encArray2, privKey);
113 		string decrpStr = cast(string)decrp;
114 		assert(data == decrpStr, format("\norig: %s\ndecr: %s", data, decrpStr));
115 	}
116 
117 	// decrypt with private keys in memory
118 	foreach(privKey; privKeysPtr) {
119 		ubyte[] decrp = smimeDecryptionWithKey(encArray2, privKey);
120 		string decrpStr = cast(string)decrp;
121 		assert(data == decrpStr, format("\norig: %s\ndecr: %s", data, decrpStr));
122 	}
123 
124 	// decrypt with private keys in memory
125 	foreach(privKey; privKeysPtr2) {
126 		ubyte[] decrp = smimeDecryptionWithKey(encArray2, privKey);
127 		string decrpStr = cast(string)decrp;
128 		assert(data == decrpStr, format("\norig: %s\ndecr: %s", data, decrpStr));
129 	}
130 
131 	// decrypt with private keys on cli
132 	foreach(idx, privKey; privKeys) {
133 		string decLibFilename = format("%s.%d.enc.lib", deleteme, idx);
134 		string decFromShell = format(
135 				"openssl smime -decrypt -in %s -out %s -inform PEM -inkey %s"
136 				, encFilename, decLibFilename, privKey);
137 		auto decSH = executeShell(decFromShell);
138 		assert(decSH.status == 0, format("%s\n%s\n%s", decSH.status, decSH.output
139 				, decFromShell));
140 		ubyte[] decFileFromCli = cast(ubyte[])read(decLibFilename);
141 		string decrpStr = cast(string)decFileFromCli;
142 		assert(data == decrpStr, format("\norig: %s\ndecr: %s", data, decrpStr));
143 		scope(success) {
144 			deleteThat(decLibFilename);
145 		}
146 	}
147 	foreach(cert; keys) {
148 		freeCert(cert);
149 	}
150 	foreach(cert; keys2) {
151 		freeCert(cert);
152 	}
153 	foreach(cert; privKeysPtr) {
154 		freePrivKey(cert);
155 	}
156 	foreach(cert; privKeysPtr2) {
157 		freePrivKey(cert);
158 	}
159 }
160 
161 unittest {
162 	string data = "Hello openssl world";
163 	string[] pubKeys = [ "dub.json"];
164 	assertThrown(smimeEncryption(cast(ubyte[])data, pubKeys));
165 }
166 
167 unittest {
168 	string data = "Hello openssl world";
169 	string privKeys = "dub.json";
170 	assertThrown(smimeDecryption(cast(ubyte[])data, privKeys));
171 }
172 
173 unittest {
174 	string data = "Hello openssl world";
175 	string privKeys = "alice.key";
176 	assertThrown(smimeDecryption(cast(ubyte[])data, privKeys));
177 }
178 
179 unittest {
180 	string data = "Hello openssl world";
181 	string privKeys = "source";
182 	assertThrown(smimeDecryption(cast(ubyte[])data, privKeys));
183 }
184 
185 unittest {
186 	string data = "Hello openssl world";
187 	string privKeys = "doesnotexist";
188 	assertThrown(smimeDecryption(cast(ubyte[])data, privKeys));
189 }
190 
191 unittest {
192 	string notAKey = "Not a key";
193 	auto k = loadCertFromString(notAKey);
194 	assert(k is null);
195 }
196 
197 unittest {
198 	auto r = cast(ubyte[])read("README.md");
199 	auto p = loadCert("./alice.pub");
200 	auto e = smimeEncryptionWithCerts(r, [p]);
201 	auto d = smimeDecryption(e, "./alice.key");
202 	write("textOrBinary.enc", e);
203 	assert(r == d, format("%.3s %.3s\n%s\n\n%s\n%s", r.length, d.length, r, d,
204 				cast(string)d));
205 }
206 
207 unittest {
208 	string data = "Hello openssl world";
209 	ubyte[] r = smimeEncryption(cast(ubyte[])data, ["frank_with_pass.pub"]);
210 	EVP_PKEY* privKey = loadKeyFromString(readText("frank_with_pass.key")
211 			, "foobar");
212 	assert(privKey != null);
213 	ubyte[] d = smimeDecryptionWithKey(r, privKey);
214 	string rslt = cast(string)d;
215 	assert(data == rslt, rslt);
216 	freePrivKey(privKey);
217 }