Documentation ¶
Overview ¶
Package store provides secure storage of map[string]string objects. It combines an AEAD ("authenticated encryption with associated data") scheme and a data structure for representing functions called a Bloomier filter. It is suited client/server protocols in which the server is trusted only to provide storage. The client possesses a secret key K and data M (of type map[string]string). It executes:
pub, priv, err := store.NewStore(K, M)
and transmits pub, the public representation of M, to the server. To compute M[input], the client executes:
x, y, err := priv.GetIdx(input)
and sends x and y to the server. These are integers corresponding to rows in a table encoded by pub. The server executes:
pubShare, err := pub.GetShare(x, y)
and sends pubShare (of type []byte) to the client. (This is the XOR of the two rows requested by the client.) Finally, the client executes:
output, err := priv.GetOutput(input, pubShare)
This combines pubShare with the private share computed from input and the key. The result is M[input]. Note that the server is not entrusted with the key; its only job is to look up the rows of the table requested by the client. The data structure is designed so that no information about input or output (except for the length of the output) is leaked to any party not in possession of the client's secret key.
At the core of data structure is a Bloomier filter, a variant of a technique of Charles and Chellapilla for representing functions. (See "Bloomier Filters: A second look", appearing at ESA 2008.) It is implemented in C, but this package provides Go bindings. You can use it similarly to Store:
pub, priv, err := store.NewDict(K, M) x, y, err := priv.GetIdx(input) pubShare, err := pub.GetShare(x, y) output, err := priv.GetOutput(input, pubShare)
However, the length of the outputs is limited to 60 bytes, a limitation we now explain. The idea of Dict is that each input is mapped to two rows of a table so that, when these rows are added together (i.e., their bitwise-XOR is computed), the result is equal to the output. The look up is performed using hash functions. The table has L rows and each row is 64 bytes in length. Roughly speaking, the query is evaluated as follows:
x := H1(input) // An integer in range [1..L] y := H2(input) // An integer in range [1..L] pad := H3(input) // A string of length 64 bytes output := Table[x] ^ Tabley[y] ^ pad
(Note that the above is pseudocode; the functions H1, H2, and H3 are not provided.) In our setting, the hash functions are implemented using HMAC-SHA512, which is a keyed, pseudorandom function. The output of HMAC-SHA512 is 64 bytes in length.
If some query is not in the table, then the result of the query should indicate as much. This is accomplished by appending a tag to the output. After adding up the pad and table rows, we check if the last few bytes are 0; if so, then the input/output pair is in the map; otherwise the input/output pair is not in the map. 4 bytes of each row are allocated for the tag and padding of the output; hence, each output must be at most 60 bytes long. Note that this makes the data structure probabilistic, since there is a small chance that, when the query is evaluated, the tag bytes will all equal 0, even though the input is not correct.
NOTE: Dict does not on its own provide integrity protection, as Store does. It's meant to be extremely light weight, and in fact is a core component of Store.
NOTE: The underlying data structure is implemented in C. The source can be found in github.com/cjpatton/store/c; refer to github.com/cjpatton/store/README.md for installation instructions.
Index ¶
- Constants
- func DeriveKeyFromPassword(password, salt []byte) []byte
- func GenerateDictKey() []byte
- func GenerateKey() []byte
- func NewDict(K []byte, M map[string]string) (*PubDict, *PrivDict, error)
- func NewStore(K []byte, M map[string]string) (pub *PubStore, priv *PrivStore, err error)
- type Error
- type PrivDict
- type PrivStore
- type PubDict
- type PubStore
Examples ¶
Constants ¶
const DictKeyBytes = C.HMAC_KEY_BYTES
Length of the HMAC key. HMAC_KEY_BYTES is defined in c/const.h.
const ErrorIdx = Error("index out of range")
Returned by pub.GetShare() in case x or y is not in the table index.
const ErrorMapTooLarge = Error("input map is too large")
Returned by NewStore() in case the number of elements in the input exceeds the number of unique counters.
const ItemNotFound = Error("item not found")
Returned by Get() and priv.GetOutput() if the input was not found in the map.
const KeyBytes = DictKeyBytes + SealKeyBytes
Length of the store key.
const MaxOutputBytes = MaxRowBytes - TagBytes - 1
The maximum length of the outputs. 1 byte of each row is allocated for padding the output string.
const MaxRowBytes = C.HASH_BYTES
The maximum length of the row. In general, the length of the row depends on the length of the longest output in the map. HASH_BYTES is defined in c/const.h.
const SaltBytes = 8
Number of bytes to use for the salt, a random string used to construct the table. It is prepended to the input of each HMAC call.
const SealKeyBytes = 16
Length of key for sealing the outputs. (AEAD is AES128-GCM.)
const TagBytes = 2
Number of row bytes allocated for the tag.
Variables ¶
This section is empty.
Functions ¶
func DeriveKeyFromPassword ¶
DeriveKeyFromPassword derives a key from a password and (optional) salt and returns it.
If salt == nil, then no salt is used. Note that the salt is not the same as pb.Params.Salt. pb.Params.Salt is generated by NewDict(), which in turn depends on the key.
Example ¶
password := []byte("A really secure password") salt := []byte("Optional salt, useful in many applications") K := DeriveKeyFromPassword(password, salt) fmt.Println(len(K))
Output: 32
func GenerateDictKey ¶
func GenerateDictKey() []byte
GenerateKey generates a fresh, random key and returns it.
func GenerateKey ¶
func GenerateKey() []byte
GenerateKey generates a fresh, random key and returns it.
Example ¶
K := GenerateKey() fmt.Println(len(K))
Output: 32
func NewDict ¶
New generates a new structure (pub, priv) for the map M and key K.
You must call pub.Free() and priv.Free() before these variables go out of scope. These structures contain C types that were allocated on the heap and must be freed before losing a reference to them.
func NewStore ¶
NewStore creates a new store for key K and map M.
You must call pub.Free() and priv.Free() before these variables go out of scope. This is necessary because these structures contain memory allocated from the heap in C.
Example ¶
K := GenerateKey() M := map[string]string{"Out": "of this world!"} pub, priv, err := NewStore(K, M) if err != nil { fmt.Println("NewStore() error:", err) return } defer pub.Free() defer priv.Free() x, y, err := priv.GetIdx("Out") if err != nil { fmt.Println("priv.GetIdx() error:", err) return } pubShare, err := pub.GetShare(x, y) if err != nil { fmt.Println("pub.GetShare() error:", err) return } out, err := priv.GetOutput("Out", pubShare) if err != nil { fmt.Println("priv.GetOutput() error:", err) return } fmt.Println(out)
Output: of this world!
Types ¶
type PrivDict ¶
type PrivDict struct {
// contains filtered or unexported fields
}
The private state required for evaluation queries.
func NewPrivDict ¶
NewPrivDict creates a new *PrivDict from a key and parameters.
You must destroy this with priv.Free().
func (*PrivDict) Free ¶
func (priv *PrivDict) Free()
Free deallocates moemory associated with the C implementation of the underlying data structure.
func (*PrivDict) Get ¶
Get queries input on the structure (pub, priv). The result is M[input] = output, where M is the map represented by (pub, priv).
func (*PrivDict) GetIdx ¶
GetIdx computes the two indices of the table associated with input and returns them.
type PrivStore ¶
type PrivStore struct {
// contains filtered or unexported fields
}
Stores the private context used to query the map.
func NewPrivStore ¶
NewPrivStore creates a new private store context from a key and parameters.
You must call priv.Free() before priv goes out of scope.
Example ¶
K := GenerateKey() M := map[string]string{"Out": "of this world!"} pub, priv, err := NewStore(K, M) if err != nil { fmt.Println("NewStore() error:", err) return } defer pub.Free() defer priv.Free() privFromKeyAndPrivParams, err := NewPrivStore(K, priv.GetParams()) if err != nil { fmt.Println("NewPrivStore() error:", err) } defer privFromKeyAndPrivParams.Free() privFromKeyAndPubParams, err := NewPrivStore(K, pub.GetProto().GetDict().GetParams()) if err != nil { fmt.Println("NewPrivStore() error:", err) } defer privFromKeyAndPubParams.Free()
Output:
func (*PrivStore) Free ¶
func (priv *PrivStore) Free()
Free releases memory allocated to the private context's internal representation.
func (*PrivStore) GetOutput ¶
GetOutput computes the final output from input and the public share.
The nonce is the constructed from combining the table public share with the private share and concatenating the result to the salt. The associated data is the input. Returns ItemNotFound if unsealing the output fails.
type PubDict ¶
type PubDict struct {
// contains filtered or unexported fields
}
The public representation of the map.
func NewPubDictFromProto ¶
NewPubDictFromProto creates a new *PubDict from a *pb.Dict.
You must destroy with pub.Free().
func (*PubDict) Free ¶
func (pub *PubDict) Free()
Free deallocates memory associated with the underlying C implementation of the data structure.
type PubStore ¶
type PubStore struct {
// contains filtered or unexported fields
}
Stores the public representation of the map.
func NewPubStoreFromProto ¶
NewPubStoreFromProto creates a public store from its protobuf representation.
You must call pub.Free() before pub goes out of scope.
func (*PubStore) Free ¶
func (pub *PubStore) Free()
Free releases memory allocated to the public store's internal representation.
func (*PubStore) GetProto ¶
GetProto creates a protobuf representation of the public store.
This is a compact representation suitable for transmission.
Directories ¶
Path | Synopsis |
---|---|
hadee
|
|
client
hadee_client is a toy client that makes RPC requests to hadee_server.
|
hadee_client is a toy client that makes RPC requests to hadee_server. |
gen
hadee_gen generates a sample store from a password.
|
hadee_gen generates a sample store from a password. |
server
hadee_serv is a toy server implementing the StoreProvider RPC specified in store.proto.
|
hadee_serv is a toy server implementing the StoreProvider RPC specified in store.proto. |
Package pb is a generated protocol buffer package.
|
Package pb is a generated protocol buffer package. |