-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcore.go
More file actions
140 lines (130 loc) · 4.07 KB
/
Copy pathcore.go
File metadata and controls
140 lines (130 loc) · 4.07 KB
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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package cpace
import (
"crypto/hmac"
"crypto/rand"
"io"
"github.com/gtank/ristretto255"
)
type initiatorCore struct {
scalar *ristretto255.Scalar // persistent secret — owned by clear()
sid []byte
ya []byte
ada []byte
peerID []byte
}
type responderCore struct {
isk []byte // persistent secret — owned by clear()
transcript irTranscript // public wire data — zeroed alongside isk as hygiene
sid []byte
peerID []byte
}
func newInitiatorCore(nc normalizedInput, random io.Reader) (*initiatorCore, []byte, error) {
if random == nil {
random = rand.Reader
}
g := calculateGenerator(nc.password, nc.ci, nc.sid)
defer clearElement(g)
// Early-clear the password so its residency is bounded by the generator
// derivation, not the full constructor lifetime. nc is a by-value copy:
// clearBytes zeroes the shared backing array, and the shell's deferred
// nc.wipe() re-covers the field on every exit path, including this
// constructor's error returns and panics.
clearBytes(nc.password)
nc.password = nil
y, err := sampleScalar(random)
if err != nil {
return nil, nil, err
}
ya := scalarMult(y, g)
return &initiatorCore{
scalar: y,
sid: clone(nc.sid),
ya: clone(ya),
ada: clone(nc.ad),
peerID: clone(nc.responderID),
}, ya, nil
}
func (c *initiatorCore) finish(peerYb, peerAdb, peerTag []byte) ([]byte, *Session, error) {
k, err := responderPeerShare.sharedSecret(c.scalar, peerYb)
defer clearBytes(k)
if err != nil {
return nil, nil, err
}
tr := newIRTranscript(c.ya, c.ada, peerYb, peerAdb)
isk := tr.deriveISK(c.sid, k)
// Scratch — the initiator's finish-local ISK. The deferred wipe covers
// the tag computations below, including panic paths; newSession clones
// isk, so the returned Session is unaffected.
defer clearBytes(isk)
expectedB := tr.responderConfirmationTag(isk, c.sid)
if !hmac.Equal(expectedB, peerTag) {
return nil, nil, ErrConfirmationFailed
}
tagA := tr.initiatorConfirmationTag(isk, c.sid)
return tagA, newSession(isk, tr.transcriptID(), peerAdb, c.peerID), nil
}
func newResponderCore(nc normalizedInput, peerYa, peerAda []byte, random io.Reader) (*responderCore, []byte, []byte, error) {
if random == nil {
random = rand.Reader
}
// Validate Ya FIRST before generator derivation and scalar sampling;
// the ordering is pinned by
// TestResponderPrevalidatesInvalidInitiatorShareBeforeRandomness.
peerYaElement, err := initiatorPeerShare.decode(peerYa)
if err != nil {
return nil, nil, nil, err
}
g := calculateGenerator(nc.password, nc.ci, nc.sid)
defer clearElement(g)
// Early-clear the password as in newInitiatorCore; the shell's deferred
// nc.wipe() re-covers the field on exit.
clearBytes(nc.password)
nc.password = nil
y, err := sampleScalar(random)
if err != nil {
return nil, nil, nil, err
}
defer clearScalar(y)
yb := scalarMult(y, g)
k, err := initiatorPeerShare.sharedSecretElement(y, peerYaElement)
defer clearBytes(k)
if err != nil {
return nil, nil, nil, err
}
tr := newIRTranscript(peerYa, peerAda, yb, nc.ad)
isk := tr.deriveISK(nc.sid, k)
tagB := tr.responderConfirmationTag(isk, nc.sid)
return &responderCore{
isk: isk,
transcript: tr,
sid: clone(nc.sid),
peerID: clone(nc.initiatorID),
}, yb, tagB, nil
}
func (c *responderCore) finish(peerTagC []byte) (*Session, error) {
expectedA := c.transcript.initiatorConfirmationTag(c.isk, c.sid)
if !hmac.Equal(expectedA, peerTagC) {
return nil, ErrConfirmationFailed
}
return newSession(c.isk, c.transcript.transcriptID(), c.transcript.initiatorAD(), c.peerID), nil
}
// clear zeroes then nils each persistent-secret field; a second call finds
// nil and is a safe no-op. Safe on a nil receiver.
func (c *initiatorCore) clear() {
if c == nil {
return
}
clearScalar(c.scalar)
c.scalar = nil
}
// clear zeroes then nils the persistent ISK and wipes the stored transcript
// bytes as hygiene; a second call finds nil and is a safe no-op. Safe on a nil
// receiver.
func (c *responderCore) clear() {
if c == nil {
return
}
clearBytes(c.isk)
c.isk = nil
c.transcript.clear()
}