main.go (view raw)
1package main
2
3import (
4 "crypto/aes"
5 "crypto/hmac"
6 "crypto/rand"
7 "crypto/sha256"
8 "errors"
9 "fmt"
10)
11
12// Vaudenay SAS protocol (https://www.iacr.org/archive/crypto2005/36210303/36210303.pdf)
13// Improved by MA-3 protocol (https://eprint.iacr.org/2005/424.pdf)
14// Based on an Ideal Commitment Model.
15// The exchanged messages could be the hashes of public keys.
16
17func main() {
18 alice := newPerson("AlicePublicKey")
19 bob := newPerson("BobPublicKey")
20
21 // Alice sends commitment and message to Bob
22 alice.commitTo(bob)
23
24 // Bob sends message and nonce to Alice
25 bob.sendTo(alice)
26
27 // Alice reveals the nonce to Bob
28 alice.commitment.decommit()
29
30 // Both parties calculate the pin
31 fmt.Println(alice.initiatorCheck())
32 fmt.Println(bob.secondaryCheck())
33}
34
35type person struct {
36 r []byte
37 msg []byte
38 commitment *commitment
39 other *person
40}
41
42func newPerson(msg string) *person {
43 r := make([]byte, aes.BlockSize)
44 _, err := rand.Read(r)
45 if err != nil {
46 panic(err.Error())
47 }
48
49 return &person{
50 r: r,
51 msg: []byte(msg),
52 other: &person{},
53 }
54}
55
56// commitTo sends the message and the newly created commitment to the other person
57func (p *person) commitTo(the *person) {
58 p.commitment = newCommitment(p.r)
59 the.other.commitment = p.commitment
60 the.other.msg = p.msg
61}
62
63// sendTo sends the message and the nonce to the other person
64func (p *person) sendTo(the *person) {
65 the.other.msg = p.msg
66 the.other.r = p.r
67}
68
69// initiatorCheck calculates the pin for the initiator of the protocol
70func (p *person) initiatorCheck() string {
71 c, err := aes.NewCipher(p.r)
72 if err != nil {
73 panic(err.Error())
74 }
75
76 aKey := make([]byte, c.BlockSize())
77 c.Encrypt(aKey, p.other.r)
78
79 aCheck := hmac.New(sha256.New, aKey)
80 aCheck.Write(p.msg)
81 aCheck.Write(p.other.msg)
82 return retrievePin(aCheck.Sum(nil), 8)
83}
84
85// secondaryCheck calculates the pin for the secondary of the protocol
86func (p *person) secondaryCheck() string {
87 aRand, err := p.other.commitment.open()
88 if err != nil {
89 panic(err.Error())
90 }
91
92 c, err := aes.NewCipher(aRand)
93 if err != nil {
94 panic(err.Error())
95 }
96
97 bKey := make([]byte, c.BlockSize())
98 c.Encrypt(bKey, p.r)
99
100 bCheck := hmac.New(sha256.New, bKey)
101 bCheck.Write(p.other.msg)
102 bCheck.Write(p.msg)
103 return retrievePin(bCheck.Sum(nil), 8)
104}
105
106// retrievePin returns a pin of the specified length based on the given (random) bytes
107func retrievePin(r []byte, maxLength int) string {
108 // Create a string containing only the digits 0-9
109 digitString := ""
110 for _, b := range r {
111 digitString += fmt.Sprintf("%d", int(b)%10)
112 if len(digitString) == maxLength {
113 break
114 }
115 }
116
117 return digitString
118}
119
120// commitment models an ideal commitment scheme
121type commitment struct {
122 message []byte
123 isProtected bool
124}
125
126// open returns the message if the commitment is not protected
127func (c *commitment) open() ([]byte, error) {
128 if c.isProtected {
129 return nil, errors.New("commitment is protected")
130 }
131 return c.message, nil
132}
133
134// decommit makes the commitment unprotected
135func (c *commitment) decommit() {
136 c.isProtected = false
137}
138
139// newCommitment creates a new protected commitment
140func newCommitment(c []byte) *commitment {
141 return &commitment{
142 message: c,
143 isProtected: true,
144 }
145}