-
Notifications
You must be signed in to change notification settings - Fork 241
/
sharedsecret.go
128 lines (103 loc) · 3.48 KB
/
sharedsecret.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
125
126
127
128
package sharedsecret
import (
"bytes"
"crypto/ecdsa"
"database/sql"
"errors"
"go.uber.org/zap"
"github.com/status-im/status-go/eth-node/crypto"
"github.com/status-im/status-go/eth-node/crypto/ecies"
)
const sskLen = 16
type Secret struct {
Identity *ecdsa.PublicKey
Key []byte
}
// SharedSecret generates and manages negotiated secrets.
// Identities (public keys) stored by SharedSecret
// are compressed.
// TODO: make compression of public keys a responsibility of sqlitePersistence instead of SharedSecret.
type SharedSecret struct {
persistence *sqlitePersistence
logger *zap.Logger
}
func New(db *sql.DB, logger *zap.Logger) *SharedSecret {
if logger == nil {
logger = zap.NewNop()
}
return &SharedSecret{
persistence: newSQLitePersistence(db),
logger: logger.With(zap.Namespace("SharedSecret")),
}
}
func (s *SharedSecret) generate(myPrivateKey *ecdsa.PrivateKey, theirPublicKey *ecdsa.PublicKey, installationID string) (*Secret, error) {
sharedKey, err := ecies.ImportECDSA(myPrivateKey).GenerateShared(
ecies.ImportECDSAPublic(theirPublicKey),
sskLen,
sskLen,
)
if err != nil {
return nil, err
}
logger := s.logger.With(zap.String("site", "generate"))
logger.Debug(
"saving a shared key",
zap.Binary("their-public-key", crypto.FromECDSAPub(theirPublicKey)),
zap.String("installation-id", installationID),
)
theirIdentity := crypto.CompressPubkey(theirPublicKey)
if err = s.persistence.Add(theirIdentity, sharedKey, installationID); err != nil {
return nil, err
}
return &Secret{Key: sharedKey, Identity: theirPublicKey}, err
}
// Generate will generate a shared secret for a given identity, and return it.
func (s *SharedSecret) Generate(myPrivateKey *ecdsa.PrivateKey, theirPublicKey *ecdsa.PublicKey, installationID string) (*Secret, error) {
return s.generate(myPrivateKey, theirPublicKey, installationID)
}
// Agreed returns true if a secret has been acknowledged by all the installationIDs.
func (s *SharedSecret) Agreed(myPrivateKey *ecdsa.PrivateKey, myInstallationID string, theirPublicKey *ecdsa.PublicKey, theirInstallationIDs []string) (*Secret, bool, error) {
logger := s.logger.With(zap.String("site", "Agreed"))
logger.Debug(
"checking if shared secret is acknowledged",
zap.Binary("their-public-key", crypto.FromECDSAPub(theirPublicKey)),
zap.Strings("their-installation-ids", theirInstallationIDs),
)
secret, err := s.generate(myPrivateKey, theirPublicKey, myInstallationID)
if err != nil {
return nil, false, err
}
if len(theirInstallationIDs) == 0 {
return secret, false, nil
}
theirIdentity := crypto.CompressPubkey(theirPublicKey)
response, err := s.persistence.Get(theirIdentity, theirInstallationIDs)
if err != nil {
return nil, false, err
}
for _, installationID := range theirInstallationIDs {
if !response.installationIDs[installationID] {
logger.Debug("no shared secret for installation", zap.String("installation-id", installationID))
return secret, false, nil
}
}
if !bytes.Equal(secret.Key, response.secret) {
return nil, false, errors.New("computed and saved secrets are different for a given identity")
}
return secret, true, nil
}
func (s *SharedSecret) All() ([]*Secret, error) {
var secrets []*Secret
tuples, err := s.persistence.All()
if err != nil {
return nil, err
}
for _, tuple := range tuples {
key, err := crypto.DecompressPubkey(tuple[0])
if err != nil {
return nil, err
}
secrets = append(secrets, &Secret{Identity: key, Key: tuple[1]})
}
return secrets, nil
}