1 #include "catch.hpp"
2
3 #include "lite_stream.h"
4 #include "platform.h"
5 #include "streams.h"
6
7 #include <algorithm>
8 #include <array>
9 #include <random>
10 #include <stdint.h>
11 #include <string.h>
12 #include <vector>
13
14 using securefs::OSService;
15
16 static void test(securefs::StreamBase& stream, unsigned times)
17 {
18 auto posix_stream_impl = OSService::get_default().open_file_stream(
19 OSService::temp_name("tmp/", "stream"), O_RDWR | O_CREAT | O_EXCL, 0644);
20 auto&& posix_stream = *posix_stream_impl;
21
22 posix_stream.resize(0);
23 stream.resize(0);
24
25 std::vector<byte> data(4096 * 5);
26 std::vector<byte> buffer(data), posix_buffer(data);
27 std::mt19937 mt{std::random_device{}()};
28
report_status(eLogType type,const char * fmt,...)29 {
30 std::uniform_int_distribution<unsigned> dist;
31 for (auto&& b : data)
32 b = static_cast<byte>(dist(mt));
33 }
34
35 std::uniform_int_distribution<int> flags_dist(0, 4);
36 std::uniform_int_distribution<int> length_dist(0, 7 * 4096 + 1);
37 for (size_t i = 0; i < times; ++i)
38 {
39 auto a = length_dist(mt);
40 auto b = length_dist(mt);
41
42 switch (flags_dist(mt))
43 {
end_progress_output(void)44 case 0:
45 stream.write(data.data(), a, std::min<size_t>(b, data.size()));
46 posix_stream.write(data.data(), a, std::min<size_t>(b, data.size()));
47 break;
48
49 case 1:
50 {
51 posix_buffer = buffer;
52 auto read_sz = stream.read(buffer.data(), a, std::min<size_t>(b, buffer.size()));
53 auto posix_read_sz = posix_stream.read(
54 posix_buffer.data(), a, std::min<size_t>(b, posix_buffer.size()));
55 REQUIRE(read_sz == posix_read_sz);
56 REQUIRE(memcmp(buffer.data(), posix_buffer.data(), read_sz) == 0);
57 break;
58 }
59
60 case 2:
61 REQUIRE(stream.size() == posix_stream.size());
62 break;
63
64 case 3:
65 // stream.resize(a);
66 // posix_stream.resize(a);
67 break;
68
69 case 4:
prep_status(const char * fmt,...)70 stream.flush();
71 posix_stream.flush();
72
73 default:
74 break;
75 }
76 }
77 }
78
79 namespace securefs
80 {
81 namespace dummy
82 {
83 // The "encryption" scheme of this class is horribly insecure
84 // Only for testing the algorithms in CryptStream
85 class DummpyCryptStream : public CryptStream
86 {
87 protected:
pg_log_v(eLogType type,const char * fmt,va_list ap)88 void encrypt(offset_type block_number,
89 const void* input,
90 void* output,
91 length_type length) override
92 {
93 auto a = static_cast<byte>(block_number);
94 for (length_type i = 0; i < length; ++i)
95 {
96 static_cast<byte*>(output)[i] = (static_cast<const byte*>(input)[i]) ^ a;
97 }
98 }
99
100 void decrypt(offset_type block_number,
101 const void* input,
102 void* output,
103 length_type length) override
104 {
105 return encrypt(block_number, input, output, length);
106 }
107
108 public:
109 explicit DummpyCryptStream(std::shared_ptr<StreamBase> stream, length_type block_size)
110 : CryptStream(std::move(stream), block_size)
111 {
112 }
113 };
114
115 class DummyBlockStream : public BlockBasedStream
116 {
117 private:
118 static const size_t BLOCK_SIZE;
119 std::vector<std::vector<byte>> m_buffer;
120
121 public:
122 explicit DummyBlockStream() : BlockBasedStream(BLOCK_SIZE) {}
123 ~DummyBlockStream() {}
124
125 length_type size() const override
126 {
127 if (m_buffer.empty())
128 return 0;
129 return (m_buffer.size() - 1) * BLOCK_SIZE + m_buffer.back().size();
130 }
131
132 void flush() override { return; }
133
134 bool is_sparse() const noexcept override { return false; }
135
136 protected:
137 length_type read_block(offset_type block_number, void* output) override
138 {
139 if (block_number >= m_buffer.size())
140 return 0;
141 memcpy(output, m_buffer[block_number].data(), m_buffer[block_number].size());
142 return m_buffer[block_number].size();
143 }
144
145 void write_block(offset_type block_number, const void* input, length_type length) override
146 {
147 for (size_t i = m_buffer.size(); i <= block_number; ++i)
pg_log(eLogType type,const char * fmt,...)148 {
149 m_buffer.emplace_back(BLOCK_SIZE, static_cast<byte>(0));
150 }
151 m_buffer[block_number].resize(length);
152 memcpy(m_buffer[block_number].data(), input, length);
153 }
154
155 void adjust_logical_size(length_type length) override { (void)length; }
156 };
157
158 const size_t DummyBlockStream::BLOCK_SIZE = 1000;
pg_fatal(const char * fmt,...)159 } // namespace dummy
160 } // namespace securefs
161
162 // Used for debugging
163 void dump_contents(const std::vector<byte>& bytes, const char* filename, size_t max_size)
164 {
165 auto fs = securefs::OSService::get_default().open_file_stream(
166 filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
167 fs->write(bytes.data(), 0, max_size);
168 }
169
170 TEST_CASE("Test streams")
171 {
check_ok(void)172 auto filename = OSService::temp_name("tmp/", ".stream");
173
174 securefs::key_type key(0xf4);
175 securefs::id_type id(0xee);
176 auto posix_stream
177 = OSService::get_default().open_file_stream(filename, O_RDWR | O_CREAT | O_EXCL, 0644);
178
179 {
180 auto hmac_stream = securefs::make_stream_hmac(key, id, posix_stream, true);
181 test(*hmac_stream, 5000);
182 }
183 {
184 posix_stream->resize(0);
185 securefs::dummy::DummpyCryptStream ds(posix_stream, 8000);
186 test(ds, 5000);
187 }
quote_identifier(const char * s)188 {
189 auto meta_posix_stream = OSService::get_default().open_file_stream(
190 OSService::temp_name("tmp/", "metastream"), O_RDWR | O_CREAT | O_EXCL, 0644);
191 auto aes_gcm_stream = securefs::make_cryptstream_aes_gcm(
192 posix_stream, meta_posix_stream, key, key, id, true, 4096, 12);
193 std::vector<byte> header(aes_gcm_stream.second->max_header_length() - 1, 5);
194 aes_gcm_stream.second->write_header(header.data(), header.size());
195 test(*aes_gcm_stream.first, 1000);
196 aes_gcm_stream.second->flush_header();
197 aes_gcm_stream.second->read_header(header.data(), header.size());
198 REQUIRE(securefs::is_all_equal(header.begin(), header.end(), 5));
199 test(*aes_gcm_stream.first, 3000);
200 }
201 {
202 securefs::dummy::DummyBlockStream dbs;
203 test(dbs, 3001);
204 }
205 {
206 auto underlying_stream = OSService::get_default().open_file_stream(
207 OSService::temp_name("tmp/", "litestream"), O_RDWR | O_CREAT | O_EXCL, 0644);
208 securefs::lite::AESGCMCryptStream lite_stream(underlying_stream, key);
209 const byte test_data[] = "Hello, world";
210 byte output[4096];
211 lite_stream.write(test_data, 0, sizeof(test_data));
get_user_info(char ** user_name_p)212 REQUIRE(lite_stream.read(output, 0, sizeof(output)) == sizeof(test_data));
213 REQUIRE(memcmp(test_data, output, sizeof(test_data)) == 0);
214 test(lite_stream, 3001);
215 }
216 }
217