1 /*
2 This file is part of Telegram Desktop,
3 the official desktop application for the Telegram messaging service.
4 
5 For license and copyright information please follow this link:
6 https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
7 */
8 #include "catch.hpp"
9 
10 #include "storage/storage_encrypted_file.h"
11 
12 #include <QtCore/QThread>
13 #include <QtCore/QCoreApplication>
14 
15 #ifdef Q_OS_WIN
16 #include "platform/win/windows_dlls.h"
17 #endif // Q_OS_WIN
18 
19 #include <QtCore/QProcess>
20 
21 #include <thread>
22 #ifdef Q_OS_MAC
23 #include <mach-o/dyld.h>
24 #elif defined Q_OS_UNIX // Q_OS_MAC
25 #include <unistd.h>
26 #endif // Q_OS_MAC || Q_OS_UNIX
27 
28 extern int (*TestForkedMethod)();
29 
30 const auto Key = Storage::EncryptionKey(bytes::make_vector(
31 	bytes::make_span("\
32 abcdefgh01234567abcdefgh01234567abcdefgh01234567abcdefgh01234567\
33 abcdefgh01234567abcdefgh01234567abcdefgh01234567abcdefgh01234567\
34 abcdefgh01234567abcdefgh01234567abcdefgh01234567abcdefgh01234567\
35 abcdefgh01234567abcdefgh01234567abcdefgh01234567abcdefgh01234567\
36 ").subspan(0, Storage::EncryptionKey::kSize)));
37 
38 const auto Name = QString("test.file");
39 
40 const auto Test1 = bytes::make_span("testbytetestbyte").subspan(0, 16);
41 const auto Test2 = bytes::make_span("bytetestbytetest").subspan(0, 16);
42 
43 struct ForkInit {
MethodForkInit44 	static int Method() {
45 		Storage::File file;
46 		const auto result = file.open(
47 			Name,
48 			Storage::File::Mode::ReadAppend,
49 			Key);
50 		if (result != Storage::File::Result::Success) {
51 			return -1;
52 		}
53 
54 		auto data = bytes::vector(16);
55 		const auto read = file.read(data);
56 		if (read != data.size()) {
57 			return -1;
58 		} else if (data != bytes::make_vector(Test1)) {
59 			return -1;
60 		}
61 
62 		if (!file.write(data) || !file.flush()) {
63 			return -1;
64 		}
65 #ifdef _DEBUG
66 		while (true) {
67 			std::this_thread::sleep_for(std::chrono::seconds(1));
68 		}
69 #else // _DEBUG
70 		std::this_thread::sleep_for(std::chrono::seconds(1));
71 		return 0;
72 #endif // _DEBUG
73 	}
ForkInitForkInit74 	ForkInit() {
75 #ifdef Q_OS_WIN
76 		Platform::Dlls::start();
77 #endif // Q_OS_WIN
78 
79 		TestForkedMethod = &ForkInit::Method;
80 	}
81 
82 };
83 
84 ForkInit ForkInitializer;
85 QProcess ForkProcess;
86 
87 TEST_CASE("simple encrypted file", "[storage_encrypted_file]") {
88 	SECTION("writing file") {
89 		Storage::File file;
90 		const auto result = file.open(
91 			Name,
92 			Storage::File::Mode::Write,
93 			Key);
94 		REQUIRE(result == Storage::File::Result::Success);
95 
96 		auto data = bytes::make_vector(Test1);
97 		const auto success = file.write(data);
98 		REQUIRE(success);
99 	}
100 	SECTION("reading and writing file") {
101 		Storage::File file;
102 		const auto result = file.open(
103 			Name,
104 			Storage::File::Mode::ReadAppend,
105 			Key);
106 		REQUIRE(result == Storage::File::Result::Success);
107 
108 		auto data = bytes::vector(Test1.size());
109 		const auto read = file.read(data);
110 		REQUIRE(read == data.size());
111 		REQUIRE(data == bytes::make_vector(Test1));
112 
113 		data = bytes::make_vector(Test2);
114 		const auto success = file.write(data);
115 		REQUIRE(success);
116 	}
117 	SECTION("offset and seek") {
118 		Storage::File file;
119 		const auto result = file.open(
120 			Name,
121 			Storage::File::Mode::ReadAppend,
122 			Key);
123 		REQUIRE(result == Storage::File::Result::Success);
124 		REQUIRE(file.offset() == 0);
125 		REQUIRE(file.size() == Test1.size() + Test2.size());
126 
127 		const auto success1 = file.seek(Test1.size());
128 		REQUIRE(success1);
129 		REQUIRE(file.offset() == Test1.size());
130 
131 		auto data = bytes::vector(Test2.size());
132 		const auto read = file.read(data);
133 		REQUIRE(read == data.size());
134 		REQUIRE(data == bytes::make_vector(Test2));
135 		REQUIRE(file.offset() == Test1.size() + Test2.size());
136 		REQUIRE(file.size() == Test1.size() + Test2.size());
137 
138 		const auto success2 = file.seek(Test1.size());
139 		REQUIRE(success2);
140 		REQUIRE(file.offset() == Test1.size());
141 
142 		data = bytes::make_vector(Test1);
143 		const auto success3 = file.write(data) && file.write(data);
144 		REQUIRE(success3);
145 
146 		REQUIRE(file.offset() == 3 * Test1.size());
147 		REQUIRE(file.size() == 3 * Test1.size());
148 	}
149 	SECTION("reading file") {
150 		Storage::File file;
151 
152 		const auto result = file.open(
153 			Name,
154 			Storage::File::Mode::Read,
155 			Key);
156 		REQUIRE(result == Storage::File::Result::Success);
157 
158 		auto data = bytes::vector(32);
159 		const auto read = file.read(data);
160 		REQUIRE(read == data.size());
161 		REQUIRE(data == bytes::concatenate(Test1, Test1));
162 	}
163 	SECTION("moving file") {
164 		const auto result = Storage::File::Move(Name, "other.file");
165 		REQUIRE(result);
166 	}
167 }
168 
169 TEST_CASE("two process encrypted file", "[storage_encrypted_file]") {
170 	SECTION("writing file") {
171 		Storage::File file;
172 		const auto result = file.open(
173 			Name,
174 			Storage::File::Mode::Write,
175 			Key);
176 		REQUIRE(result == Storage::File::Result::Success);
177 
178 		auto data = bytes::make_vector(Test1);
179 		const auto success = file.write(data);
180 		REQUIRE(success);
181 	}
182 	SECTION("access from subprocess") {
183 		SECTION("start subprocess") {
__anona6ee3d2f0102() 184 			const auto application = []() -> QString {
185 #ifdef Q_OS_WIN
186 				return "tests_storage.exe";
187 #else // Q_OS_WIN
188 				constexpr auto kMaxPath = 1024;
189 				char result[kMaxPath] = { 0 };
190 				uint32_t size = kMaxPath;
191 #ifdef Q_OS_MAC
192 				if (_NSGetExecutablePath(result, &size) == 0) {
193 					return result;
194 				}
195 #else // Q_OS_MAC
196 				auto count = readlink("/proc/self/exe", result, size);
197 				if (count > 0) {
198 					return result;
199 				}
200 #endif // Q_OS_MAC
201 				return "tests_storage";
202 #endif // Q_OS_WIN
203 			}();
204 
205 			ForkProcess.start(application + " --forked");
206 			const auto started = ForkProcess.waitForStarted();
207 			REQUIRE(started);
208 		}
209 		SECTION("read subprocess result") {
210 			std::this_thread::sleep_for(std::chrono::milliseconds(500));
211 
212 			Storage::File file;
213 
214 			const auto result = file.open(
215 				Name,
216 				Storage::File::Mode::Read,
217 				Key);
218 			REQUIRE(result == Storage::File::Result::Success);
219 
220 			auto data = bytes::vector(32);
221 			const auto read = file.read(data);
222 			REQUIRE(read == data.size());
223 			REQUIRE(data == bytes::concatenate(Test1, Test1));
224 		}
225 		SECTION("take subprocess result") {
226 			REQUIRE(ForkProcess.state() == QProcess::Running);
227 
228 			Storage::File file;
229 
230 			const auto result = file.open(
231 				Name,
232 				Storage::File::Mode::ReadAppend,
233 				Key);
234 			REQUIRE(result == Storage::File::Result::Success);
235 
236 			auto data = bytes::vector(32);
237 			const auto read = file.read(data);
238 			REQUIRE(read == data.size());
239 			REQUIRE(data == bytes::concatenate(Test1, Test1));
240 
241 			const auto finished = ForkProcess.waitForFinished(0);
242 			REQUIRE(finished);
243 			REQUIRE(ForkProcess.state() == QProcess::NotRunning);
244 		}
245 	}
246 
247 }
248