all repos — sas @ 9026bf286aeb343b07fba2cc4b45e0b6aaa88e9b

SAS peer-to-peer authenticated communication over an insecure channel

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}