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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
open Types
open Kinds
type 'a sub = {
mutable status : full Operations.handler status ;
handler : 'a Operations.handler option ;
name: string ;
subt: int ;
sub_salt: string ;
user_key_kind: key_kind ;
builtin_key_kind: key_kind ;
max_extra_data: int ;
subsignwd: Cipher.passwd ;
}
let error err sub = raiserror (err (Subtable (sub.name, sub.subt)))
let get_number sub = sub.subt
let get_name sub = sub.name
let phony_kind = Kinds.(mk_key Table_Builtin Uncrypted)
let empty name subt = {
status = Read ;
handler = None ;
name ;
subt ;
sub_salt = "" ;
user_key_kind = phony_kind ;
builtin_key_kind = phony_kind ;
max_extra_data = 0 ;
subsignwd = Cipher.empty_passwd ;
}
let get_table_passwds sub =
assert (sub.handler <> None) ;
match sub.builtin_key_kind.key_how with
| Uncrypted -> (Cipher.empty_passwd, Cipher.empty_passwd)
| Encrypted (pw, sw, _) -> (pw, sw)
let get_salted_key_how ~iterations salt = function
| Uncrypted -> Uncrypted
| Encrypted (tab_pw, "") -> Encrypted (tab_pw, Cipher.empty_passwd, max_extra_key)
| Encrypted (tab_pw, sub_pw) -> Encrypted (tab_pw, Cipher.mk_passwd ~iterations (Config.add_salt salt sub_pw), max_extra_key)
let get_semi_uncrypted = function
| Uncrypted -> Uncrypted
| Encrypted (tab_pw, _) ->
if tab_pw == Cipher.empty_passwd then Uncrypted
else Encrypted (tab_pw, Cipher.empty_passwd, 0)
let sign sub =
match sub.status with
| Full handler ->
if sub.subsignwd != Cipher.empty_passwd then
begin
let (passwd, _) = get_table_passwds sub in
Signature.sign_subtable handler ~subtable_salt:sub.sub_salt ~passwd sub.builtin_key_kind
~subt:sub.subt ~signwd:sub.subsignwd
end
| Read -> ()
| Closed -> error is_closed sub
let close sub =
sign sub ;
sub.status <- Closed ;
()
let builtin_get loc ~bad_passwd handler kind ~key =
try Operations.get handler kind ~key
with
| Error (Unbound (_, Any) | Bad_password Any) ->
if bad_passwd then raiserror (Bad_password loc)
else raiserror (Corrupted (loc, "Missing builtin binding for key: " ^ key))
let compute_and_check_signature subtable =
if subtable.subsignwd == Cipher.empty_passwd then ()
else
match subtable.handler with
| None -> assert false
| Some handler ->
let (table_passwd, _) = get_table_passwds subtable in
let signature = Signature.subtable_signature handler ~subtable_salt:subtable.sub_salt
~passwd:table_passwd ~subt:subtable.subt ~signwd:subtable.subsignwd
in
let read_signature =
try Signature.read_subtable_signature handler subtable.builtin_key_kind ~subt:subtable.subt
with Error (Unbound (_, Any)) -> error no_signature subtable
in
if Signature.equal signature read_signature then ()
else error bad_signature subtable
let open_aux handler status ~name ~subt ~iterations ~how ~signwd ~check_signature =
let salt_key_kind = mk_key (Subtable_Builtin subt) (get_semi_uncrypted how) in
let loc = Subtable (name, subt) in
let sub_salt = builtin_get loc ~bad_passwd:false handler salt_key_kind ~key:Config.salt_key
and =
let sval = builtin_get loc ~bad_passwd:false handler salt_key_kind ~key:Config.max_extra_key_key in
try int_of_string sval
with Failure _ -> raiserror (Corrupted (loc, Printf.sprintf "Max_key_pad is not bound to a number as expected. Found: %s" sval))
and =
if status = Read then 0
else
let sval = builtin_get loc ~bad_passwd:false handler salt_key_kind ~key:Config.max_extra_data_key in
try int_of_string sval
with Failure _ -> raiserror (Corrupted (loc, Printf.sprintf "Max_data_pad is not bound to a number as expected. Found: %s" sval))
in
let signwd =
if signwd = "" then Cipher.empty_passwd
else Cipher.mk_passwd ~iterations (Config.add_salt sub_salt signwd)
in
let how = get_salted_key_how ~iterations sub_salt max_extra_key how in
let builtin_key_kind = mk_key (Subtable_Builtin subt) how
and user_key_kind = mk_key (Subtable_User subt) how in
let subtable =
{ status ;
handler = Some handler ;
name ;
subt ;
sub_salt ;
max_extra_data ;
user_key_kind ;
builtin_key_kind ;
subsignwd = signwd }
in
let data = builtin_get loc ~bad_passwd:true handler builtin_key_kind ~key:Config.test_key in
if data <> Config.test_data then
raiserror (Corrupted (loc, Printf.sprintf "Corrupted test binding: found %s instead of %s." data Config.test_data)) ;
if check_signature then compute_and_check_signature subtable ;
subtable
let open_read handler ~name ~subt ~iterations ~how ~signwd =
open_aux (Operations.cast handler) Read ~name ~subt ~iterations ~how ~signwd ~check_signature:true
let open_append handler ~name ~subt ~iterations ~how ~signwd ~check_signature =
open_aux handler (Full handler) ~name ~subt ~iterations ~how ~signwd ~check_signature
let open_full handler ~name ~subt ~iterations ~how ~signwd ~ ~ =
if subt > Kinds.max_subtable then raiserror Subtable_overflow ;
let salt_key_kind = mk_key (Subtable_Builtin subt) (get_semi_uncrypted how) in
let sub_salt = Utils.random_string Utils.gen Config.salt_size in
Operations.add ~may_overwrite:false handler salt_key_kind ~max_extra_data:0 ~key:Config.salt_key ~data:sub_salt ;
Operations.add ~may_overwrite:false handler salt_key_kind ~max_extra_data:0 ~key:Config.max_extra_key_key ~data:(string_of_int max_extra_key) ;
Operations.add ~may_overwrite:false handler salt_key_kind ~max_extra_data:0 ~key:Config.max_extra_data_key ~data:(string_of_int max_extra_data) ;
let signwd =
if signwd = "" then Cipher.empty_passwd
else Cipher.mk_passwd ~iterations (Config.add_salt sub_salt signwd)
in
let how = get_salted_key_how ~iterations sub_salt max_extra_key how in
let builtin_key_kind = mk_key (Subtable_Builtin subt) how
and user_key_kind = mk_key (Subtable_User subt) how in
let subtable =
{ status = Full handler ;
handler = Some handler ;
name ;
subt ;
sub_salt ;
max_extra_data ;
builtin_key_kind ;
user_key_kind ;
subsignwd = signwd }
in
Operations.add ~may_overwrite:false handler builtin_key_kind ~max_extra_data ~key:Config.test_key ~data:Config.test_data ;
subtable
let check_writeable sub =
match sub.status with
| Closed -> error is_closed sub
| Read -> assert false
| Full _ -> ()
let add ?may_overwrite sub ~key ~data =
check_writeable sub ;
match sub.handler with
| None -> assert false
| Some handler ->
try Operations.add ?may_overwrite handler sub.user_key_kind ~max_extra_data:sub.max_extra_data ~key ~data
with Error (Overwrite (key, Any)) -> raiserror (Overwrite (key, Subtable (sub.name, sub.subt)))
let find sub key =
if sub.status = Closed then error is_closed sub ;
match sub.handler with
| None -> raiserror (Unbound (key, Subtable (sub.name, sub.subt)))
| Some handler ->
try Operations.get handler sub.user_key_kind ~key
with Error (Unbound (key, Any)) -> raiserror (Unbound (key, Subtable (sub.name, sub.subt)))
let delete sub key =
check_writeable sub ;
match sub.handler with
| None -> assert false
| Some handler ->
try Operations.remove handler sub.user_key_kind ~key
with Error (Unbound (_, Any)) -> raiserror (Unbound (key, Subtable (sub.name, sub.subt)))
let is_bound sub key =
try let _ = find sub key in true with Error (Unbound _) -> false
let iterkey sub f =
if sub.status = Closed then error is_closed sub ;
match sub.handler with
| None -> ()
| Some handler ->
let (table_passwd, subpass) = get_table_passwds sub in
Operations.iter_subtable handler table_passwd ~subt:sub.subt ~subpass
begin fun loc key ->
match loc with
| Kinds.Subtable_User _ -> f key
| _ -> ()
end
let iter sub f = iterkey sub (fun key -> f key (try find sub key with _ -> assert false))
let fold sub acu f =
let acu = ref acu in
iter sub (fun key data -> acu := f key data !acu) ;
!acu
let clear sub =
check_writeable sub ;
let (table_passwd, _) = get_table_passwds sub in
let all_keys = ref (Setp.empty Pervasives.compare) in
match sub.handler with
| None -> assert false
| Some handler ->
Operations.iter_subtable_encrypted handler table_passwd ~subt:sub.subt
begin fun loc encoded_key ->
match loc with
| Kinds.Table_Builtin | Kinds.Subtable_Builtin _ -> ()
| Subtable_User n ->
assert (n = sub.subt) ;
all_keys := Setp.add encoded_key !all_keys ;
end ;
Setp.iter (Operations.remove_encrypted handler) !all_keys ;
()
let remove_signature sub =
match sub.handler with
| None -> assert false
| Some handler ->
Signature.remove_subtable_signature handler sub.builtin_key_kind ~subt:sub.subt