1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
//! The storage seam, and the one rule that makes it safe.
//!
//! # Persist before emit
//!
//! A leaf node MUST have its MLS state durable before it emits a frame whose
//! production advanced that state. The failure this prevents is not a delivery
//! hiccup: a device that answers and then loses power before its ratchet state
//! reaches flash comes back and **reuses an AEAD nonce**, which is a
//! confidentiality failure in a protocol whose whole claim is the AEAD
//! boundary.
//!
//! This crate does not ask firmware to remember that. Every operation that
//! advances state writes through this trait and only then returns the bytes to
//! send, so a store that returns an error produces no frame at all. The
//! ordering is not documented here and implemented elsewhere; it is the reason
//! [`LeafDevice`](crate::LeafDevice) hands back frames rather than exposing
//! the MLS group it seals with.
//!
//! # What an implementation owes
//!
//! [`LeafStore::store`] must be **durable and atomic per entry**: after it
//! returns `Ok`, a power cut must leave the new value readable, and it must
//! never leave a torn one. mls-rs asks the same of its own storage provider,
//! in the same words, for the same reason. A flash driver that buffers a write
//! and reports success satisfies the type and breaks the rule.
//!
//! # What it bounds, and what it does not
//!
//! This crate bounds how much a store *holds*: peers, unspent key packages and
//! prior-epoch records each have a ceiling. It does not bound how often a store
//! is *written*, and an inbound key package costs a handful of writes to a part
//! whose flash has a finite number of them. A device exposed to strangers wants
//! its pairing window controlled by firmware rather than left open, which is
//! the same conclusion the authorization obligation reaches from the other
//! direction.
//!
//! # Where the key material should live
//!
//! Everything written through this trait is secret: the identity private key,
//! MLS group state, key package private keys. On a part with secure key
//! storage this trait is how that storage is reached, which is
//! [R12](https://github.com/Offline-Protocol/offline-protocol-sdk/blob/main/docs/security/threat-model.md)
//! in the threat model. A device that keeps them in general flash yields them
//! to anyone holding the device.
use ;
use Error;
/// Key type for the device's own identity material.
pub const KEY_TYPE_IDENTITY: &str = "identity";
/// Key type for MLS group state.
pub const KEY_TYPE_GROUP_STATE: &str = "group_state";
/// Key type for MLS prior-epoch records.
pub const KEY_TYPE_GROUP_EPOCH: &str = "group_epoch";
/// Key type for key package private material.
pub const KEY_TYPE_KEY_PACKAGE: &str = "key_package";
/// Key type for what a peer told us it can parse.
pub const KEY_TYPE_PEER: &str = "peer";
/// Key type for the list of peers this device holds records for.
///
/// One entry, rewritten as peers are admitted and forgotten. It exists because
/// this seam offers no way to enumerate what is in it, and a bound nobody can
/// count is not a bound.
pub const KEY_TYPE_PEER_INDEX: &str = "peer_index";
/// What a store can fail with.
/// Durable storage for a leaf node's secret material and MLS state.
///
/// The shape mirrors `MlsStorage` in `offline-protocol-mls`, which is the same
/// seam on the phone: a two-part `(key_type, key_id)` key, `&self` methods so
/// one store can be shared by the several places that write through it, and
/// per-entry atomicity. A device implements it over the part's secure key
/// storage, its flash filesystem, or an EEPROM.
///
/// # Errors
///
/// Returning an error is always safe. It aborts the operation before anything
/// is emitted, which is the whole point of the seam.
pub use MemoryStore;