all repos — lindell @ e00f6dc9ec081228497b2ba182b7b061f51b324d

Anonymous Authentication after Lindell

main.go (view raw)

  1package main
  2
  3import (
  4	"bytes"
  5	"crypto/rand"
  6	"crypto/sha512"
  7	"errors"
  8	"fmt"
  9	"golang.org/x/crypto/nacl/box"
 10	"io"
 11	mathRand "math/rand"
 12)
 13
 14func main() {
 15	numberParticipants := 4
 16
 17	// Generate n public/private key pairs
 18	pubs, privs, err := genKeyPairs(numberParticipants)
 19	if err != nil {
 20		fmt.Printf("generating key pairs: %s", err.Error())
 21		return
 22	}
 23
 24	// Protocol step 1
 25	// Generate a random string w
 26	w := make([]byte, 8)
 27	_, err = rand.Read(w)
 28	if err != nil {
 29		fmt.Printf("generating random w: %s", err.Error())
 30		return
 31	}
 32	// add magic bytes to identify correct w on client side
 33	w = append([]byte("li"), w...)
 34	fmt.Printf("Server random string w: %x\n", w)
 35
 36	// For every participant, generate a random coin
 37	coins := make([][]byte, numberParticipants)
 38	for i := 0; i < numberParticipants; i++ {
 39		c := make([]byte, 8)
 40		_, err = rand.Read(c)
 41		if err != nil {
 42			fmt.Printf("generating random coin: %s", err.Error())
 43			return
 44		}
 45		coins[i] = c
 46	}
 47
 48	// Encrypt w with coin for every public key
 49	encMessages, err := encrypt(pubs, w, coins)
 50	if err != nil {
 51		fmt.Printf("generating random w: %s\n", err.Error())
 52		return
 53	}
 54
 55	// Protocol step 2
 56	// prepare client
 57	c := client{
 58		allPublicKeys: pubs,
 59		privateKey:    privs[mathRand.Intn(numberParticipants)], // Choose random participant to proof anonymity
 60		encMessages:   encMessages,
 61	}
 62
 63	wDash, err := c.decrypt()
 64	if err != nil {
 65		fmt.Printf("client-side decrypt: %s\n", err.Error())
 66		return
 67	}
 68	fmt.Printf("Client w': %x\n", wDash)
 69
 70	// Protocol step 3
 71	// compare w and wDash
 72	if !bytes.Equal(w, wDash) {
 73		fmt.Println("Access denied")
 74		return
 75	}
 76	fmt.Println("Access granted")
 77
 78	// Protocol step 4
 79	// Verify server did not cheat on client
 80	// insert the following code snippet to pretend a cheat
 81	// coins[0] = []byte("cheat")
 82	err = c.verify(coins)
 83	if err != nil {
 84		fmt.Printf("client verification: %s\n", err.Error())
 85		return
 86	}
 87	fmt.Println("Verification successful")
 88}
 89
 90type client struct {
 91	allPublicKeys []*[32]byte
 92	privateKey    *[32]byte
 93	encMessages   [][]byte
 94	wDash         []byte
 95}
 96
 97func genKeyPairs(n int) ([]*[32]byte, []*[32]byte, error) {
 98	pubOut := make([]*[32]byte, n)
 99	privOut := make([]*[32]byte, n)
100	for i := 0; i < n; i++ {
101		pub, priv, err := box.GenerateKey(rand.Reader)
102		if err != nil {
103			return nil, nil, err
104		}
105		pubOut[i] = pub
106		privOut[i] = priv
107	}
108	return pubOut, privOut, nil
109}
110
111func encrypt(pubs []*[32]byte, w []byte, coins [][]byte) ([][]byte, error) {
112	if len(pubs) != len(coins) {
113		return nil, errors.New("length public keys and coins do not match")
114	}
115
116	out := make([][]byte, len(pubs))
117	for cnt, p := range pubs {
118		enc, err := box.SealAnonymous(nil, append(w, coins[cnt]...), p, newDetermRand([]byte("deterministic")))
119		if err != nil {
120			return nil, err
121		}
122		out[cnt] = enc
123	}
124
125	return out, nil
126}
127
128func (c *client) decrypt() ([]byte, error) {
129	if len(c.allPublicKeys) != len(c.encMessages) {
130		return nil, errors.New("length of public keys and encrypted messages do not match")
131	}
132
133	for cnt, p := range c.allPublicKeys {
134		decrypted, ok := box.OpenAnonymous(nil, c.encMessages[cnt], p, c.privateKey)
135		if !ok {
136			continue
137		}
138		// Verify magic byte
139		if bytes.HasPrefix(decrypted, []byte("li")) {
140			c.wDash = decrypted[:10]
141			return decrypted[:10], nil
142		}
143	}
144
145	return nil, errors.New("decryption not possible")
146}
147
148func (c client) verify(coins [][]byte) error {
149	if len(c.allPublicKeys) != len(coins) || len(coins) != len(c.encMessages) {
150		return errors.New("length of public keys and encrypted messages do not match")
151	}
152
153	for cnt, p := range c.allPublicKeys {
154		enc, err := box.SealAnonymous(nil, append(c.wDash, coins[cnt]...), p, newDetermRand([]byte("deterministic")))
155		if err != nil {
156			return err
157		}
158		if !bytes.Equal(enc, c.encMessages[cnt]) {
159			fmt.Println("CHEAT")
160			return errors.New("it appears the server cheated on us")
161		}
162	}
163	return nil
164}
165
166// As suggested by jpillora https://gist.github.com/jpillora/5a0471b246d541b984ab
167func newDetermRand(seed []byte) io.Reader {
168	return &determRand{next: seed}
169}
170
171type determRand struct {
172	next []byte
173}
174
175func (d *determRand) cycle() []byte {
176	result := sha512.Sum512(d.next)
177	d.next = result[:sha512.Size/2]
178	return result[sha512.Size/2:]
179}
180
181func (d *determRand) Read(b []byte) (int, error) {
182	n := 0
183	for n < len(b) {
184		out := d.cycle()
185		n += copy(b[n:], out)
186	}
187	return n, nil
188}