1
+ package main
2
+
3
+ import (
4
+ "fmt"
5
+ "crypto/rand"
6
+ "encoding/binary"
7
+ "crypto/md5"
8
+ "io"
9
+ "crypto/hmac"
10
+ "crypto/sha256"
11
+ )
12
+
13
+ /*
14
+ RAPPOR encoding parameters.
15
+ These affect privacy/anonymity. See paper for details.
16
+ */
17
+ type Params struct {
18
+ //k
19
+ numBloomBits int
20
+ //h
21
+ numHashes int
22
+ //m
23
+ numCohorts int
24
+ //p
25
+ probP float64
26
+ //q
27
+ probQ float64
28
+ //f
29
+ probF float64
30
+ }
31
+
32
+ func (p * Params ) init (){
33
+ p .numBloomBits = 16
34
+ p .numHashes = 2
35
+ p .numCohorts = 64
36
+ p .probP = 0.50
37
+ p .probQ = 0.75
38
+ p .probF = 0.25
39
+ }
40
+
41
+ type SecureRandom struct {
42
+ probOne float64
43
+ numBits int
44
+ }
45
+
46
+ func (s * SecureRandom ) setBits () (int , error ) {
47
+ p := s .probOne
48
+ r := 0
49
+ b := make ([]byte , s .numBits )
50
+ _ , err := rand .Read (b )
51
+ if err != nil {
52
+ fmt .Println ("error:" , err )
53
+ return 0 , err
54
+ }
55
+ var i uint
56
+ for i = 0 ; i < uint (s .numBits ); i ++ {
57
+ //fmt.Println("b[i]: ", uint(b[i]), ", p*256: ", uint(p*256))
58
+ if uint (b [i ]) < uint (p * 256 ) {
59
+ r |= 0x1 << i
60
+ }
61
+ //fmt.Printf("r: %04b\n", r)
62
+ }
63
+ return r , nil
64
+
65
+ }
66
+
67
+ func (s * SecureRandom ) init (probOne float64 , numBits int ) (int , error ){
68
+ s .probOne = probOne
69
+ s .numBits = numBits
70
+ r , err := s .setBits ()
71
+ if err != nil {
72
+ return 0 , err
73
+ }
74
+ //fmt.Println(r, err)
75
+ return r , nil
76
+ }
77
+
78
+ type SecureIrrRand struct {
79
+ pGen int
80
+ qGen int
81
+ numBits int
82
+ }
83
+
84
+ func (s * SecureIrrRand ) init (params Params ) {
85
+ s .numBits = params .numBloomBits
86
+ var sr SecureRandom
87
+ pGen , err := sr .init (params .probP , s .numBits )
88
+ if err != nil {
89
+ fmt .Println ("Error generating pGen" )
90
+ } else {
91
+ s .pGen = pGen
92
+ }
93
+ qGen , err := sr .init (params .probQ , s .numBits )
94
+ if err != nil {
95
+ fmt .Println ("Error generating qGen" )
96
+ } else {
97
+ s .qGen = qGen
98
+ }
99
+ }
100
+
101
+ func toBigEndian (i int64 ) []byte {
102
+ /*Convert integer to 4-byte big endian string*/
103
+ buf := make ([]byte , binary .MaxVarintLen64 )
104
+ binary .PutVarint (buf , i )
105
+ //fmt.Printf("%x\n", buf[:n])
106
+ return buf
107
+ }
108
+
109
+ func getBloomBits (word []byte , cohort int , numHashes int , numBloombits int ) []int {
110
+ /*
111
+ Return an array of bits to set in the bloom filter.
112
+ In the real report, we bitwise-OR them together. In hash candidates, we put
113
+ them in separate entries in the "map" matrix.
114
+ */
115
+ //Cohort is 4 byte prefix
116
+ //Need variadic ellipsis because word is []byte, not byte
117
+ value := append (toBigEndian (int64 (cohort )), word ... )
118
+ h := md5 .New ()
119
+ io .WriteString (h , string (value ))
120
+ digest := h .Sum (nil )
121
+
122
+ // Each hash is a byte, which means we could have up to 256 bit Bloom filters.
123
+ // There are 16 bytes in an MD5, in which case we can have up to 16 hash
124
+ // functions per Bloom filter.
125
+ if numHashes > len (digest ) {
126
+ fmt .Println ("Error: can't have more than " , len (digest ), " hashes" )
127
+ return []int {}
128
+ }
129
+ var retVal []int
130
+ for i := 0 ; i < numHashes ; i ++ {
131
+ retVal = append (retVal , int (digest [i ])% numBloombits )
132
+ }
133
+ return retVal
134
+ }
135
+
136
+ func getPrrMasks (secret string , word string , probF float64 ) (int , int ){
137
+ key := []byte (secret )
138
+ hash := hmac .New (sha256 .New , key )
139
+ hash .Write ([]byte (word ))
140
+ digestBytes := hash .Sum (nil )
141
+ if len (digestBytes ) != 32 {
142
+ panic ("digest_bytes needs to be 32 bytes long!" )
143
+ }
144
+ threshold128 := probF * 128
145
+ uniform := 0
146
+ fMask := 0
147
+
148
+ for i , ch := range digestBytes {
149
+ byt := int (ch )
150
+ uBit := byt & 0x01 //1 bit of entropy
151
+ uniform |= uBit << uint (i ) //maybe set bit in mask
152
+ rand128 := byt >> 1 //7 bits of entropy
153
+ var noiseBit int
154
+ if float64 (rand128 ) < threshold128 {
155
+ noiseBit = 0x01
156
+ } else {
157
+ noiseBit = 0x00
158
+ }
159
+ fMask |= noiseBit << uint (i )
160
+ }
161
+ return uniform , fMask
162
+ }
163
+
164
+ type Encoder struct {
165
+ params Params
166
+ cohort int
167
+ secret string
168
+ irrRand * SecureIrrRand
169
+ }
170
+
171
+ func (e * Encoder ) init (params Params , cohort int , secret string , irrRand * SecureIrrRand ) {
172
+ /*
173
+ Args:
174
+ params: RAPPOR Params() controlling privacy
175
+ cohort: integer cohort, for Bloom hashing.
176
+ secret: secret string, for the PRR to be a deterministic function of the
177
+ reported value.
178
+ irr_rand: IRR randomness interface.
179
+ */
180
+ // RAPPOR params. NOTE: num_cohorts isn't used. p and q are used by irr_rand.
181
+ e .params = params
182
+ e .cohort = cohort
183
+ e .secret = secret
184
+ e .irrRand = irrRand
185
+ }
186
+
187
+ func (e * Encoder ) internalEncodeBits (bits int ) (int , int ){
188
+ /*
189
+ Helper function for simulation / testing.
190
+ Returns:
191
+ The PRR and IRR. The PRR should never be sent over the network.
192
+ */
193
+ // Compute Permanent Randomized Response (PRR). Uniform and fMask are 32 bits long.
194
+ uniform , fMask := getPrrMasks (e .secret , string (toBigEndian (int64 (bits ))), e .params .probF )
195
+ /*
196
+ Suppose bit i of the Bloom filter is B_i. Then bit i of the PRR is
197
+ defined as:
198
+ 1 with prob f/2
199
+ 0 with prob f/2
200
+ B_i with prob 1-f
201
+
202
+ Uniform bits are 1 with probability 1/2, and f_mask bits are 1 with
203
+ probability f. So in the expression below:
204
+
205
+ - Bits in (uniform & f_mask) are 1 with probability f/2.
206
+ - (bloom_bits & ~f_mask) clears a bloom filter bit with probability
207
+ f, so we get B_i with probability 1-f.
208
+ - The remaining bits are 0, with remaining probability f/2.
209
+ */
210
+ //TODO: Make certain that ^x in Go === ~x in Python
211
+ prr := (int (bits ) & ^ fMask ) | (uniform & fMask )
212
+
213
+ // Compute Instantaneous Randomized Response (IRR).
214
+ // If PRR bit is 0, IRR bit is 1 with probability p.
215
+ // If PRR bit is 1, IRR bit is 1 with probability q.
216
+ e .irrRand .init (e .params )
217
+ pBits := e .irrRand .pGen
218
+ qBits := e .irrRand .qGen
219
+
220
+ irr := (pBits & ^ prr ) | (qBits & prr )
221
+
222
+ return prr , irr // IRR is the rappor
223
+ }
224
+
225
+ func (e * Encoder ) internalEncode (word []byte ) (int , int , int ) {
226
+ /*
227
+ Helper function for simulation / testing.
228
+ Returns:
229
+ The Bloom filter bits, PRR, and IRR. The first two values should never
230
+ be sent over the network.
231
+ */
232
+ bloomBits := getBloomBits (word , e .cohort , e .params .numHashes , e .params .numBloomBits )
233
+ bloom := 0
234
+ for bitToSet := range bloomBits {
235
+ bloom |= 1 << uint (bitToSet )
236
+ }
237
+ prr , irr := e .internalEncodeBits (bloom )
238
+ return bloom , prr , irr
239
+ }
240
+
241
+ func (e * Encoder ) encodeBits (bits int ) int {
242
+ /*
243
+ Encode a string with RAPPOR.
244
+ Args:
245
+ bits: An integer representing bits to encode.
246
+ Returns:
247
+ An integer that is the IRR (Instantaneous Randomized Response).
248
+ */
249
+ _ , irr := e .internalEncodeBits (bits )
250
+ return irr
251
+ }
252
+
253
+ func (e * Encoder ) Encode (word []byte ) int {
254
+ /*Encode a string with RAPPOR.
255
+ Args:
256
+ word: the string that should be privately transmitted.
257
+ Returns:
258
+ An integer that is the IRR (Instantaneous Randomized Response).
259
+ */
260
+ _ , _ , irr := e .internalEncode (word )
261
+ return irr
262
+ }
263
+
264
+ func estimateSetBits (reports []int , params Params ) []float64 {
265
+ /*
266
+ Estimate which bits were truly set in B for a particular cohort.
267
+ Returns: Y, a slice of the number of times each bit was estimated
268
+ to have been truly set in B.
269
+ This function will be called for each cohort. j represents the jth
270
+ cohort and is included only to keep variable names matching the paper.
271
+ */
272
+ p := params .probP
273
+ q := params .probQ
274
+ f := params .probF
275
+ Nj := float64 (len (reports ))
276
+ var Y_j []float64
277
+ for i := 0 ; i < 32 ; i ++ {
278
+ c_ij := 0.0
279
+ for _ , rep := range reports {
280
+ if rep & (1 << uint (i )) == 1 {
281
+ c_ij += 1.0
282
+ }
283
+ }
284
+ t_ij := c_ij - (p + 0.5 * f * q - 0.5 * f * p )* Nj / ((1 - f )* (q - p ))
285
+ Y_j = append (Y_j , t_ij )
286
+ }
287
+ return Y_j
288
+ }
289
+
290
+ /*func main() {
291
+ //var s SecureRandom
292
+ //s.init(0.5,4)
293
+ //s.setBits()
294
+ var s SecureIrrRand
295
+ var p Params
296
+ p.init()
297
+ s.init(p)
298
+ getBloomBits([]byte("asdf"), 0, 0, 0)
299
+ var e Encoder
300
+ e.init(p, 1,"my secret string is very secret", &s)
301
+ rep1 := e.encode([]byte("google.com"))
302
+ rep2 := e.encode([]byte("facebook.com"))
303
+ fmt.Println(rep1, ", ", rep2)
304
+ }*/
0 commit comments