Library
Module
Module type
Parameter
Class
Class type
This library provides an encrypted layer on top of the Dbm and Cryptokit packages. The improvements over Dbm are:
As a quick example, the following uncrypted bindings (key => data):
"john-doe" => "age 36" "some secret" => "The cake is a lie." "Motto" => "For relaxing times, make it Suntory time"
are stored as follows in the encrypted file (with variations depending on the password, and other parameters):
[S~j....O.Q..tk^.2] => [...F...).Hsl..tB] [...y;....~.:.6V.2] => [....I...JR..w.E9..G..q=...K....b] [..'.C...F.x.3K.y2] => [1.)9q..M...et.b.] [S.....5 Y....8..2] => [.D........2..u...q.......}Z.b..z.zo.}.l3l.....>.] [...xD;@.8..wV..P1....e}....u..`.2] => [hb..2.._B....Y?0....|.....tM....] [K.#i.7j..H.ZZ.^.2] => [..z....,........]
Including several subtables in the same database file avoids having to deal with multiple files to store related information, and also prevents information leak through the number and sizes of a set of database files.
This library was primarily designed to store encrypted exam files on a university server. A common layout consists in several subtables encrypted with a global password, as well as an uncrypted subtable containing (public) meta-information.
Install and compile
Install using opam: opam install cryptodbm
Compile with ocamlbuild by adding the following to your _tags file:
<**/*> : package(cryptodbm)
Performance
I have not benchmarked this library. Keep in mind that every access (reading or writing a binding) requires to encrypt the key, and encrypt the data (when writing) or decrypt the data (when reading). Don't be pessimistic, though: it seems all right for non critical applications.
Also, there is only a global key-index for the whole table, no key-index for individual subtables. As a consequence, subtable iterators actually iterate over the whole table index (selecting only the expected subtable indexes).
A few technical details
When a database file is encrypted,
See also the project homepage.
Contact: D. Le Botlan (github.lebotlan@dfgh.met where you replace .met by .net.)
let table = open_append ~file:"/path/to/myfile" ~passwd:"my-secret-passwd" in
let subtable = append_subtable table ~name:"here the subtable name" () in
add subtable ~key:"key1" ~data:"data1" () ;
add subtable ~key:"key2" ~data:"data2" () ;
close table ;
()
module Error : sig ... end
All errors that may occur.
Notice that all functions may raise Error(DB_Error)
when accessing the underlying database.
The type of encrypted-dbm file descriptors. 'a is a phantom type precising the permission: read-only or full access.
The database can be opened in three modes: read mode, write (create) mode, and append mode.
Note that operations are not thread-safe at the library level: do not share a table or subtable handler between threads. However, multiple processes might access the same database, whenever the low-level dbm permits it (which depends on the low-level dbm library actually used). gdbm allows many readers in parallel, or only one writer and no reader.
val open_read :
?iterations:int ->
file:string ->
passwd:string ->
signwd:string ->
unit ->
read table
Opens an encrypted-dbm file for reading.
val open_append :
?iterations:int ->
file:string ->
passwd:string ->
signwd:string ->
check_signature:bool ->
unit ->
full table
Opens an existing encrypted-dbm file in append mode.
val open_create :
file:string ->
?overwrite:bool ->
?iterations:int ->
passwd:string ->
signwd:string ->
?max_extra_key:int ->
?max_extra_data:int ->
?max_extra_bindings:int ->
perm:int ->
unit ->
full table
Creates a new encrypted-dbm file.
Opens a table to access only uncrypted subtables.
val close : 'a table -> unit
If in write mode, sign if necessary, add extra bindings if required, then flush and close the file. In read mode, just close the file. All the subtables are automatically closed.
Sign if necessary, and flush. Optionally make a backup, that is, a copy of the current database file is made (default name is: 'filename'-backup-'date').
val get_rootfile : 'a table -> string
Returns the root filename (without the .pag or .dir extension).
val create_subtable :
full table ->
name:string ->
?iterations:int ->
passwd:string ->
signwd:string ->
?max_extra_key:int ->
?max_extra_data:int ->
unit ->
full subtable
Creates a standard subtable for writing.
val create_uncrypted_subtable :
?iterations:int ->
full table ->
name:string ->
signwd:string ->
unit ->
full subtable
Creates an uncrypted subtable (even when the global table is encrypted).
val open_subtable :
'a table ->
name:string ->
?iterations:int ->
passwd:string ->
signwd:string ->
unit ->
read subtable
Open a standard subtable for reading.
val open_uncrypted_subtable :
?iterations:int ->
'a table ->
name:string ->
signwd:string ->
unit ->
read subtable
Open an uncrypted subtable for reading.
val append_subtable :
full table ->
name:string ->
?iterations:int ->
passwd:string ->
signwd:string ->
check_signature:bool ->
unit ->
full subtable
Open a standard subtable for appending bindings.
val append_uncrypted_subtable :
?iterations:int ->
full table ->
name:string ->
signwd:string ->
check_signature:bool ->
unit ->
full subtable
Open an uncrypted subtable for appending bindings.
val close_subtable : 'a subtable -> unit
If in write mode, sign if necessary, then flush and close the subtable. In read mode, just close the subtable.
val get_number : 'a subtable -> int
Returns this subtable's identifier (a number).
val get_name : 'a subtable -> string
Returns this subtable's name.
val iter_subtables : 'a table -> (string -> int -> unit) -> unit
Iterate over standard subtables. The function is applied to the subtable name and number.
val iter_uncrypted_subtables : 'a table -> (string -> int -> unit) -> unit
Iterate over uncrypted subtables.
add subt key data
binds key
to data
in subtable subt
. By default, overwriting an existing binding is forbidden.
val find : 'a subtable -> string -> string
find subt key
returns the data associated to key
in subtable subt
.
delete subt key
removes the binding associated to key
in subtable subt
.
val iter : 'a subtable -> (string -> string -> unit) -> unit
Iterate over all pairs (key, data) of the given subtable.
val iterkey : 'a subtable -> (string -> unit) -> unit
Iterate over all keys of the given subtable. Faster than iter
since data is not decrypted.
val fold : 'a subtable -> 'b -> (string -> string -> 'b -> 'b) -> 'b
See iter.
val iter_extra_bindings : 'a table -> (string -> string -> unit) -> unit
For debugging.
The underlying database file is managed by the dbm library actually installed on the runtime platform. For portability, or to ensure that your data does not depend on a particular version of the dbm library, you can use these functions which convert dbm files to and from an ad-hoc, very simple binary format.
Exports a dbfile to a durable binary format. The dbfile must be opened for reading (open_only_uncrypted suffices, in case the passwords are not known). Note that the binary file just mirrors the dbfile (that is, they both contain the same encrypted & uncrypted data).