Skip to content

Commit c1066de

Browse files
authored
Merge pull request #60 from cisco/no-base
Simplify the public API
2 parents cd5fc9e + 54b5fa2 commit c1066de

File tree

8 files changed

+292
-311
lines changed

8 files changed

+292
-311
lines changed

include/sframe/map.h

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#pragma once
2+
3+
#include <sframe/vector.h>
4+
5+
template<typename K, typename V, size_t N>
6+
class map : private vector<std::optional<std::pair<K, V>>, N>
7+
{
8+
public:
9+
template<class... Args>
10+
void emplace(Args&&... args)
11+
{
12+
const auto pos = std::find_if(
13+
this->begin(), this->end(), [&](const auto& pair) { return !pair; });
14+
if (pos == this->end()) {
15+
throw std::out_of_range("map out of space");
16+
}
17+
18+
pos->emplace(args...);
19+
}
20+
21+
auto find(const K& key)
22+
{
23+
return std::find_if(this->begin(), this->end(), [&](const auto& pair) {
24+
return pair && pair.value().first == key;
25+
});
26+
}
27+
28+
auto find(const K& key) const
29+
{
30+
return std::find_if(this->begin(), this->end(), [&](const auto& pair) {
31+
return pair && pair.value().first == key;
32+
});
33+
}
34+
35+
bool contains(const K& key) const { return find(key) != this->end(); }
36+
37+
const V& at(const K& key) const
38+
{
39+
const auto pos = find(key);
40+
if (pos == this->end()) {
41+
throw std::out_of_range("map key not found");
42+
}
43+
44+
return pos->value().second;
45+
}
46+
47+
V& at(const K& key)
48+
{
49+
auto pos = find(key);
50+
if (pos == this->end()) {
51+
throw std::out_of_range("map key not found");
52+
}
53+
54+
return pos->value().second;
55+
}
56+
57+
template<typename F>
58+
void erase_if_key(F&& f)
59+
{
60+
const auto to_erase = [&f](const auto& maybe_pair) {
61+
return maybe_pair && f(maybe_pair.value().first);
62+
};
63+
64+
std::replace_if(this->begin(), this->end(), to_erase, std::nullopt);
65+
}
66+
};

include/sframe/sframe.h

Lines changed: 28 additions & 211 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
#include <gsl/gsl-lite.hpp>
44
#include <optional>
55

6+
#include <sframe/map.h>
7+
#include <sframe/vector.h>
8+
69
namespace sframe {
710

811
struct crypto_error : std::runtime_error
@@ -47,196 +50,29 @@ enum class CipherSuite : uint16_t
4750
AES_GCM_256_SHA512 = 5,
4851
};
4952

50-
constexpr size_t max_overhead = 17 + 16;
51-
5253
using input_bytes = gsl::span<const uint8_t>;
5354
using output_bytes = gsl::span<uint8_t>;
5455

55-
using KeyID = uint64_t;
56-
using Counter = uint64_t;
57-
58-
template<typename T, size_t N>
59-
class vector
60-
{
61-
private:
62-
std::array<T, N> _data;
63-
size_t _size;
64-
65-
public:
66-
constexpr vector()
67-
: _size(N)
68-
{
69-
std::fill(_data.begin(), _data.end(), T());
70-
}
71-
72-
constexpr vector(size_t size)
73-
{
74-
std::fill(_data.begin(), _data.end(), T());
75-
resize(size);
76-
}
77-
78-
constexpr vector(std::initializer_list<uint8_t> content)
79-
{
80-
resize(content.size());
81-
std::copy(content.begin(), content.end(), _data.begin());
82-
}
83-
84-
constexpr vector(gsl::span<const T> content)
85-
{
86-
resize(content.size());
87-
std::copy(content.begin(), content.end(), _data.begin());
88-
}
89-
90-
// XXX(RLB) This constructor seems redundant with the prior one, but for some
91-
// reason the compiler won't auto-convert from vector to span.
92-
template<size_t M>
93-
constexpr vector(const vector<T, M>& content)
94-
{
95-
resize(content.size());
96-
std::copy(content.begin(), content.end(), _data.begin());
97-
}
98-
99-
uint8_t* data() { return _data.data(); }
100-
101-
auto begin() const { return _data.begin(); }
102-
auto begin() { return _data.begin(); }
103-
104-
auto end() const { return _data.begin() + _size; }
105-
auto end() { return _data.begin() + _size; }
106-
107-
auto size() const { return _size; }
108-
auto capacity() const { return N; }
109-
void resize(size_t size)
110-
{
111-
if (size > N) {
112-
throw std::out_of_range("vector out of space");
113-
}
114-
115-
_size = size;
116-
}
117-
118-
void push(T&& item)
119-
{
120-
resize(_size + 1);
121-
_data.at(_size - 1) = item;
122-
}
123-
124-
void append(input_bytes content)
125-
{
126-
const auto start = _size;
127-
resize(_size + content.size());
128-
std::copy(content.begin(), content.end(), begin() + start);
129-
}
130-
131-
auto& operator[](size_t i) { return _data.at(i); }
132-
const auto& operator[](size_t i) const { return _data.at(i); }
133-
134-
operator gsl::span<const T>() const { return gsl::span(_data).first(_size); }
135-
operator gsl::span<T>() { return gsl::span(_data).first(_size); }
136-
};
137-
138-
template<typename K, typename V, size_t N>
139-
class map : private vector<std::optional<std::pair<K, V>>, N>
140-
{
141-
public:
142-
template<class... Args>
143-
void emplace(Args&&... args)
144-
{
145-
const auto pos = std::find_if(
146-
this->begin(), this->end(), [&](const auto& pair) { return !pair; });
147-
if (pos == this->end()) {
148-
throw std::out_of_range("map out of space");
149-
}
150-
151-
pos->emplace(args...);
152-
}
153-
154-
auto find(const K& key)
155-
{
156-
return std::find_if(this->begin(), this->end(), [&](const auto& pair) {
157-
return pair && pair.value().first == key;
158-
});
159-
}
160-
161-
auto find(const K& key) const
162-
{
163-
return std::find_if(this->begin(), this->end(), [&](const auto& pair) {
164-
return pair && pair.value().first == key;
165-
});
166-
}
167-
168-
bool contains(const K& key) const { return find(key) != this->end(); }
169-
170-
const V& at(const K& key) const
171-
{
172-
const auto pos = find(key);
173-
if (pos == this->end()) {
174-
throw std::out_of_range("map key not found");
175-
}
176-
177-
return pos->value().second;
178-
}
179-
180-
V& at(const K& key)
181-
{
182-
auto pos = find(key);
183-
if (pos == this->end()) {
184-
throw std::out_of_range("map key not found");
185-
}
186-
187-
return pos->value().second;
188-
}
189-
190-
template<typename F>
191-
void erase_if_key(F&& f)
192-
{
193-
const auto to_erase = [&f](const auto& maybe_pair) {
194-
return maybe_pair && f(maybe_pair.value().first);
195-
};
196-
197-
std::replace_if(this->begin(), this->end(), to_erase, std::nullopt);
198-
}
199-
};
200-
20156
template<size_t N>
20257
using owned_bytes = vector<uint8_t, N>;
20358

204-
class Header
205-
{
206-
public:
207-
const KeyID key_id;
208-
const Counter counter;
209-
210-
Header(KeyID key_id_in, Counter counter_in);
211-
static Header parse(input_bytes buffer);
212-
213-
input_bytes encoded() const { return _encoded; }
214-
size_t size() const { return _encoded.size(); }
215-
216-
// Configuration byte plus 8-byte KID and CTR
217-
static constexpr size_t max_size = 1 + 8 + 8;
218-
219-
private:
220-
// Just the configuration byte
221-
static constexpr size_t min_size = 1;
59+
using KeyID = uint64_t;
60+
using Counter = uint64_t;
22261

223-
owned_bytes<max_size> _encoded;
224-
225-
Header(KeyID key_id_in, Counter counter_in, input_bytes encoded_in);
226-
};
62+
class Header;
22763

22864
enum struct KeyUsage
22965
{
23066
protect,
23167
unprotect,
23268
};
23369

234-
struct KeyAndSalt
70+
struct KeyRecord
23571
{
236-
static KeyAndSalt from_base_key(CipherSuite suite,
237-
KeyID key_id,
238-
KeyUsage usage,
239-
input_bytes base_key);
72+
static KeyRecord from_base_key(CipherSuite suite,
73+
KeyID key_id,
74+
KeyUsage usage,
75+
input_bytes base_key);
24076

24177
static constexpr size_t max_key_size = 48;
24278
static constexpr size_t max_salt_size = 12;
@@ -247,43 +83,10 @@ struct KeyAndSalt
24783
Counter counter;
24884
};
24985

250-
// ContextBase represents the core SFrame encryption logic. It remembers a set
251-
// of keys and salts identified by key IDs, and uses them to protect and
252-
// unprotect payloads. The SFrame header is **not** written by the protect
253-
// method or read by the unprotect method. It is assumed that the application
254-
// carries the header values in some other way.
255-
//
256-
// In general, you should prefer Context to ContextBase.
257-
class ContextBase
258-
{
259-
public:
260-
ContextBase(CipherSuite suite_in);
261-
virtual ~ContextBase();
262-
263-
void add_key(KeyID kid, KeyUsage usage, input_bytes key);
264-
265-
output_bytes protect(const Header& header,
266-
output_bytes ciphertext,
267-
input_bytes plaintext,
268-
input_bytes metadata);
269-
output_bytes unprotect(const Header& header,
270-
output_bytes ciphertext,
271-
input_bytes plaintext,
272-
input_bytes metadata);
273-
274-
static constexpr size_t max_aad_size = Header::max_size + 512;
275-
276-
protected:
277-
CipherSuite suite;
278-
279-
static constexpr size_t max_keys = 200;
280-
map<KeyID, KeyAndSalt, max_keys> keys;
281-
};
282-
28386
// Context applies the full SFrame transform. It tracks a counter for each key
28487
// to ensure nonce uniqueness, adds the SFrame header on protect, and
28588
// reads/strips the SFrame header on unprotect.
286-
class Context : protected ContextBase
89+
class Context
28790
{
28891
public:
28992
Context(CipherSuite suite);
@@ -299,9 +102,23 @@ class Context : protected ContextBase
299102
input_bytes ciphertext,
300103
input_bytes metadata);
301104

105+
static constexpr size_t max_overhead = 17 + 16;
106+
static constexpr size_t max_metadata_size = 512;
107+
302108
protected:
303-
static constexpr size_t max_counters = 200;
304-
map<KeyID, Counter, max_counters> counters;
109+
static constexpr size_t max_keys = 200;
110+
111+
CipherSuite suite;
112+
map<KeyID, KeyRecord, max_keys> keys;
113+
114+
output_bytes protect_inner(const Header& header,
115+
output_bytes ciphertext,
116+
input_bytes plaintext,
117+
input_bytes metadata);
118+
output_bytes unprotect_inner(const Header& header,
119+
output_bytes ciphertext,
120+
input_bytes plaintext,
121+
input_bytes metadata);
305122
};
306123

307124
// MLSContext augments Context with logic for deriving keys from MLS. Instead

0 commit comments

Comments
 (0)