-
Notifications
You must be signed in to change notification settings - Fork 1
/
cpace.go
124 lines (98 loc) · 3.06 KB
/
cpace.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
package cpace
import (
"github.com/bytemare/cryptotools/group"
"github.com/bytemare/cryptotools/utils"
)
// Role in the protocol.
type Role bool
const (
// Initiator is the role that initiates (starts) the protocol.
Initiator Role = true
// Responder is the role that receives the request.
Responder Role = false
cpace = "CPace"
minSidLength = 16
)
// CPace holds information about the party's state, and offers the protocol functions.
type CPace struct {
role Role
group group.Group
parameters *Parameters
epk []byte
scalar group.Scalar
}
func (c *CPace) sessionKey(peerElement []byte) ([]byte, error) {
peer, err := c.group.NewElement().Decode(peerElement)
if err != nil {
return nil, errPeerElementInvalid
}
k := peer.Mult(c.scalar)
if k.IsIdentity() {
return nil, errPeerElementIdentity
}
t := c.transcript(k.Bytes(), peerElement)
h := c.parameters.Hash.Get()
return h.Hash(h.OutputSize(), t), err
}
func (c *CPace) transcript(k, peerElement []byte) []byte {
var epki, epkr []byte
switch c.role {
case Initiator:
epki = c.epk
epkr = peerElement
case Responder:
epki = peerElement
epkr = c.epk
}
tLen := len(c.parameters.Dsi2) + len(k) + len(c.epk) + len(peerElement)
return utils.Concatenate(tLen, c.parameters.Dsi2, k, epki, epkr)
}
// checkSid verifies the session id, and generates one for the initiator if none provided.
func checkSid(role Role, sid []byte) ([]byte, error) {
switch l := len(sid); {
case l == 0:
// If none is given for the Responder, we'll take it from the initiator's first message
if role == Initiator {
return utils.RandomBytes(minSidLength), nil
}
return nil, errSetupSIDNil
case l < minSidLength:
return nil, errSetupSIDTooShort
}
return sid, nil
}
// Start creates a secret scalar and uses it to derive a public share with the password and sid.
// If sid is nil, and the caller is Initiator, a new random sid is created.
func (c *CPace) Start(password, sid []byte) (epk, ssid []byte, err error) {
sid, err = checkSid(c.role, sid)
if err != nil {
return nil, nil, err
}
if c.scalar == nil {
c.scalar = c.group.NewScalar().Random()
}
m := c.group.HashToGroup(c.parameters.Dsi1, password, sid, c.parameters.Ida, c.parameters.Idb, c.parameters.Ad)
c.epk = m.Mult(c.scalar).Bytes()
return c.epk, sid, nil
}
// Finish uses the peerElement and the internal state to derive and return the session secret.
func (c *CPace) Finish(peerElement []byte) ([]byte, error) {
if len(c.epk) == 0 {
return nil, errNoEphemeralPubKey
}
if len(peerElement) == 0 {
return nil, errPeerElementNil
}
return c.sessionKey(peerElement)
}
// SetScalar sets the internal secret scalar to s. If s is not successfully deserialized to the set group, this function
// returns an error.
func (c *CPace) SetScalar(s []byte) (err error) {
c.scalar, err = c.group.NewScalar().Decode(s)
return err
}
// Scalar returns the internal secret scalar generated in Start(). If Start() hasn't been called or didn't succeed,
// this function returns nil.
func (c *CPace) Scalar() []byte {
return c.scalar.Bytes()
}