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
|
/*
* Copyright (c) 2018, Henry Corrigan-Gibbs
*
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __PRIO_H__
#define __PRIO_H__
#ifdef __cplusplus
extern "C" {
#endif
#include <blapit.h>
#include <msgpack.h>
#include <pk11pub.h>
#include <seccomon.h>
#include <stdbool.h>
#include <stddef.h>
/* Seed for a pseudo-random generator (PRG). */
#define PRG_SEED_LENGTH AES_128_KEY_LENGTH
typedef unsigned char PrioPRGSeed[PRG_SEED_LENGTH];
/* Length of a raw curve25519 public key, in bytes. */
#define CURVE25519_KEY_LEN 32
/* Length of a hex-encoded curve25519 public key, in bytes. */
#define CURVE25519_KEY_LEN_HEX 64
/*
* Type for each of the two servers.
*/
typedef enum { PRIO_SERVER_A, PRIO_SERVER_B } PrioServerId;
/*
* Opaque types
*/
typedef struct prio_config* PrioConfig;
typedef const struct prio_config* const_PrioConfig;
typedef struct prio_server* PrioServer;
typedef const struct prio_server* const_PrioServer;
typedef struct prio_verifier* PrioVerifier;
typedef const struct prio_verifier* const_PrioVerifier;
typedef struct prio_packet_verify1* PrioPacketVerify1;
typedef const struct prio_packet_verify1* const_PrioPacketVerify1;
typedef struct prio_packet_verify2* PrioPacketVerify2;
typedef const struct prio_packet_verify2* const_PrioPacketVerify2;
typedef struct prio_total_share* PrioTotalShare;
typedef const struct prio_total_share* const_PrioTotalShare;
typedef SECKEYPublicKey* PublicKey;
typedef const SECKEYPublicKey* const_PublicKey;
typedef SECKEYPrivateKey* PrivateKey;
typedef const SECKEYPrivateKey* const_PrivateKey;
/*
* Initialize and clear random number generator state.
* You must call Prio_init() before using the library.
* To avoid memory leaks, call Prio_clear() afterwards.
*/
SECStatus Prio_init();
void Prio_clear();
/*
* PrioConfig holds the system parameters. The two relevant things determined
* by the config object are:
* (1) the number of data fields we are collecting, and
* (2) the modulus we use for modular arithmetic.
* The default configuration uses an 87-bit modulus.
*
* The value `nFields` must be in the range `0 < nFields <= max`, where `max`
* is the value returned by the function `PrioConfig_maxDataFields()` below.
*
* The `batch_id` field specifies which "batch" of aggregate statistics we are
* computing. For example, if the aggregate statistics are computed every 24
* hours, the `batch_id` might be set to an encoding of the date. The clients
* and servers must all use the same `batch_id` for each run of the protocol.
* Each set of aggregate statistics should use a different `batch_id`.
*
* `PrioConfig_new` does not keep a pointer to the `batch_id` string that the
* caller passes in, so you may free the `batch_id` string as soon as
* `PrioConfig_new` returns.
*/
PrioConfig PrioConfig_new(int nFields, PublicKey serverA, PublicKey serverB,
const unsigned char* batchId,
unsigned int batchIdLen);
void PrioConfig_clear(PrioConfig cfg);
int PrioConfig_numDataFields(const_PrioConfig cfg);
/*
* Return the maximum number of data fields that the implementation supports.
*/
int PrioConfig_maxDataFields(void);
/*
* Create a PrioConfig object with no encryption keys. This routine is
* useful for testing, but PrioClient_encode() will always fail when used with
* this config.
*/
PrioConfig PrioConfig_newTest(int nFields);
/*
* We use the PublicKey and PrivateKey objects for public-key encryption. Each
* Prio server has an associated public key, and the clients use these keys to
* encrypt messages to the servers.
*/
SECStatus Keypair_new(PrivateKey* pvtkey, PublicKey* pubkey);
/*
* Import a new curve25519 public/private key from the raw bytes given. When
* importing a private key, you must pass in the corresponding public key as
* well. The byte arrays given as input should be of length
* `CURVE25519_KEY_LEN`.
*
* These functions will allocate a new `PublicKey`/`PrivateKey` object, which
* the caller must free using `PublicKey_clear`/`PrivateKey_clear`.
*/
SECStatus PublicKey_import(PublicKey* pk, const unsigned char* data,
unsigned int dataLen);
SECStatus PrivateKey_import(PrivateKey* sk, const unsigned char* privData,
unsigned int privDataLen,
const unsigned char* pubData,
unsigned int pubDataLen);
/*
* Import a new curve25519 public/private key from a hex string that contains
* only the characters 0-9a-fA-F.
*
* The hex strings passed in must each be of length `CURVE25519_KEY_LEN_HEX`.
* These functions will allocate a new `PublicKey`/`PrivateKey` object, which
* the caller must free using `PublicKey_clear`/`PrivateKey_clear`.
*/
SECStatus PublicKey_import_hex(PublicKey* pk, const unsigned char* hexData,
unsigned int dataLen);
SECStatus PrivateKey_import_hex(PrivateKey* sk,
const unsigned char* privHexData,
unsigned int privDataLen,
const unsigned char* pubHexData,
unsigned int pubDataLen);
/*
* Export a curve25519 key as a raw byte-array.
*
* The output buffer `data` must have length exactly `CURVE25519_KEY_LEN`.
*/
SECStatus PublicKey_export(const_PublicKey pk, unsigned char* data,
unsigned int dataLen);
SECStatus PrivateKey_export(PrivateKey sk, unsigned char* data,
unsigned int dataLen);
/*
* Export a curve25519 key as a NULL-terminated hex string.
*
* The output buffer `data` must have length exactly `CURVE25519_KEY_LEN_HEX +
* 1`.
*/
SECStatus PublicKey_export_hex(const_PublicKey pk, unsigned char* data,
unsigned int dataLen);
SECStatus PrivateKey_export_hex(PrivateKey sk, unsigned char* data,
unsigned int dataLen);
void PublicKey_clear(PublicKey pubkey);
void PrivateKey_clear(PrivateKey pvtkey);
/*
* PrioPacketClient_encode
*
* Takes as input a pointer to an array (`data_in`) of boolean values
* whose length is equal to the number of data fields specified in
* the config. It then encodes the data for servers A and B into a
* string.
*
* NOTE: The caller must free() the strings `for_server_a` and
* `for_server_b` to avoid memory leaks.
*/
SECStatus PrioClient_encode(const_PrioConfig cfg, const bool* data_in,
unsigned char** forServerA, unsigned int* aLen,
unsigned char** forServerB, unsigned int* bLen);
/*
* Generate a new PRG seed using the NSS global randomness source.
* Use this routine to initialize the secret that the two Prio servers
* share.
*/
SECStatus PrioPRGSeed_randomize(PrioPRGSeed* seed);
/*
* The PrioServer object holds the state of the Prio servers.
* Pass in the _same_ secret PRGSeed when initializing the two servers.
* The PRGSeed must remain secret to the two servers.
*/
PrioServer PrioServer_new(const_PrioConfig cfg, PrioServerId serverIdx,
PrivateKey serverPriv,
const PrioPRGSeed serverSharedSecret);
void PrioServer_clear(PrioServer s);
/*
* After receiving a client packet, each of the servers generate
* a PrioVerifier object that they use to check whether the client's
* encoded packet is well formed.
*/
PrioVerifier PrioVerifier_new(PrioServer s);
void PrioVerifier_clear(PrioVerifier v);
/*
* Read in encrypted data from the client, decrypt it, and prepare to check the
* request for validity.
*/
SECStatus PrioVerifier_set_data(PrioVerifier v, unsigned char* data,
unsigned int dataLen);
/*
* Generate the first packet that servers need to exchange to verify the
* client's submission. This should be sent over a TLS connection between the
* servers.
*/
PrioPacketVerify1 PrioPacketVerify1_new(void);
void PrioPacketVerify1_clear(PrioPacketVerify1 p1);
SECStatus PrioPacketVerify1_set_data(PrioPacketVerify1 p1,
const_PrioVerifier v);
SECStatus PrioPacketVerify1_write(const_PrioPacketVerify1 p,
msgpack_packer* pk);
SECStatus PrioPacketVerify1_read(PrioPacketVerify1 p, msgpack_unpacker* upk,
const_PrioConfig cfg);
/*
* Generate the second packet that the servers need to exchange to verify the
* client's submission. The routine takes as input the PrioPacketVerify1
* packets from both server A and server B.
*
* This should be sent over a TLS connection between the servers.
*/
PrioPacketVerify2 PrioPacketVerify2_new(void);
void PrioPacketVerify2_clear(PrioPacketVerify2 p);
SECStatus PrioPacketVerify2_set_data(PrioPacketVerify2 p2, const_PrioVerifier v,
const_PrioPacketVerify1 p1A,
const_PrioPacketVerify1 p1B);
SECStatus PrioPacketVerify2_write(const_PrioPacketVerify2 p,
msgpack_packer* pk);
SECStatus PrioPacketVerify2_read(PrioPacketVerify2 p, msgpack_unpacker* upk,
const_PrioConfig cfg);
/*
* Use the PrioPacketVerify2s from both servers to check whether
* the client's submission is well formed.
*/
SECStatus PrioVerifier_isValid(const_PrioVerifier v, const_PrioPacketVerify2 pA,
const_PrioPacketVerify2 pB);
/*
* Each of the two servers calls this routine to aggregate the data
* submission from a client that is included in the PrioVerifier object.
*
* IMPORTANT: This routine does *not* check the validity of the client's
* data packet. The servers must execute the verification checks
* above before aggregating any client data.
*/
SECStatus PrioServer_aggregate(PrioServer s, PrioVerifier v);
/*
* After the servers have aggregated data packets from "enough" clients
* (this determines the anonymity set size), each server runs this routine
* to get a share of the aggregate statistics.
*/
PrioTotalShare PrioTotalShare_new(void);
void PrioTotalShare_clear(PrioTotalShare t);
SECStatus PrioTotalShare_set_data(PrioTotalShare t, const_PrioServer s);
SECStatus PrioTotalShare_write(const_PrioTotalShare t, msgpack_packer* pk);
SECStatus PrioTotalShare_read(PrioTotalShare t, msgpack_unpacker* upk,
const_PrioConfig cfg);
/*
* Read the output data into an array of unsigned longs. You should
* be sure that each data value can fit into a single `unsigned long`
* and that the pointer `output` points to a buffer large enough to
* store one long per data field.
*
* This function returns failure if some final data value is too
* long to fit in an `unsigned long`.
*/
SECStatus PrioTotalShare_final(const_PrioConfig cfg, unsigned long long* output,
const_PrioTotalShare tA,
const_PrioTotalShare tB);
#endif /* __PRIO_H__ */
#ifdef __cplusplus
}
#endif
|