Skip to content

Commit 149fa66

Browse files
Add rappor_implementation.go
1 parent b6cb6c4 commit 149fa66

File tree

1 file changed

+304
-0
lines changed

1 file changed

+304
-0
lines changed

rappor_implementation.go

+304
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,304 @@
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

Comments
 (0)