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