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