-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpeer_share.go
More file actions
94 lines (83 loc) · 2.9 KB
/
Copy pathpeer_share.go
File metadata and controls
94 lines (83 loc) · 2.9 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
package cpace
import (
"crypto/hmac"
"errors"
"fmt"
"github.com/gtank/ristretto255"
)
type peerShareRole string
const (
initiatorPeerShare peerShareRole = "initiator"
responderPeerShare peerShareRole = "responder"
)
func (r peerShareRole) decode(encoded []byte) (*ristretto255.Element, error) {
p, err := decodePublicShare(encoded)
if err != nil {
return nil, r.wrapError(err)
}
return p, nil
}
func (r peerShareRole) sharedSecret(s *ristretto255.Scalar, encoded []byte) ([]byte, error) {
k, err := scalarMultVFY(s, encoded)
if err != nil {
return nil, r.wrapError(err)
}
return k, nil
}
func (r peerShareRole) sharedSecretElement(s *ristretto255.Scalar, p *ristretto255.Element) ([]byte, error) {
k, err := scalarMultVFYElement(s, p)
if err != nil {
return nil, r.wrapError(err)
}
return k, nil
}
// wrapError applies the ADR-0003 call-site sentinel mapping: the two exported
// peer-share sentinels are rewrapped from the plain sentinel, never from the
// helper's already-ErrAbort-wrapped error, with role context added.
// Non-sentinel defensive errors pass through unchanged.
// A new peer-share sentinel added in decodePublicShare must get a case here,
// or it surfaces without role context.
func (r peerShareRole) wrapError(err error) error {
switch {
case errors.Is(err, ErrPeerShareEncoding):
return fmt.Errorf("%w: invalid %s share: %w", ErrAbort, r, ErrPeerShareEncoding)
case errors.Is(err, ErrPeerShareIdentity):
return fmt.Errorf("%w: invalid %s share: %w", ErrAbort, r, ErrPeerShareIdentity)
default:
return err
}
}
func scalarMultVFY(s *ristretto255.Scalar, encoded []byte) ([]byte, error) {
p, err := decodePublicShare(encoded)
if err != nil {
return nil, err
}
return scalarMultVFYElement(s, p)
}
func scalarMultVFYElement(s *ristretto255.Scalar, p *ristretto255.Element) ([]byte, error) {
out := ristretto255.NewIdentityElement().ScalarMult(s, p).Bytes()
if hmac.Equal(out, identityEncoding) {
// Unreachable in production for prime-order Ristretto255: every
// scalar sampleScalar can return is non-zero mod the group order, so
// s*p is non-identity for any decoded non-identity p. Kept as
// defense-in-depth; tests exercise it with a zero scalar.
return nil, fmt.Errorf("%w: neutral-element shared secret", ErrAbort)
}
return out, nil
}
func decodePublicShare(encoded []byte) (*ristretto255.Element, error) {
// Defensive for internal callers; public message decoders enforce
// pointSize, so malformed wire lengths surface as ErrMessage from framing
// and never reach this branch.
if len(encoded) != pointSize {
return nil, fmt.Errorf("%w: invalid peer share length", ErrAbort)
}
p, err := ristretto255.NewIdentityElement().SetCanonicalBytes(encoded)
if err != nil {
return nil, fmt.Errorf("%w: %w", ErrAbort, ErrPeerShareEncoding)
}
if hmac.Equal(p.Bytes(), identityEncoding) {
return nil, fmt.Errorf("%w: %w", ErrAbort, ErrPeerShareIdentity)
}
return p, nil
}