github.com/status-im/status-go@v1.1.0/protocol/encryption/sharedsecret/sharedsecret.go (about) 1 package sharedsecret 2 3 import ( 4 "bytes" 5 "crypto/ecdsa" 6 "database/sql" 7 "errors" 8 9 "go.uber.org/zap" 10 11 "github.com/status-im/status-go/eth-node/crypto" 12 "github.com/status-im/status-go/eth-node/crypto/ecies" 13 ) 14 15 const sskLen = 16 16 17 type Secret struct { 18 Identity *ecdsa.PublicKey 19 Key []byte 20 } 21 22 // SharedSecret generates and manages negotiated secrets. 23 // Identities (public keys) stored by SharedSecret 24 // are compressed. 25 // TODO: make compression of public keys a responsibility of sqlitePersistence instead of SharedSecret. 26 type SharedSecret struct { 27 persistence *sqlitePersistence 28 logger *zap.Logger 29 } 30 31 func New(db *sql.DB, logger *zap.Logger) *SharedSecret { 32 if logger == nil { 33 logger = zap.NewNop() 34 } 35 36 return &SharedSecret{ 37 persistence: newSQLitePersistence(db), 38 logger: logger.With(zap.Namespace("SharedSecret")), 39 } 40 } 41 42 func (s *SharedSecret) generate(myPrivateKey *ecdsa.PrivateKey, theirPublicKey *ecdsa.PublicKey, installationID string) (*Secret, error) { 43 sharedKey, err := ecies.ImportECDSA(myPrivateKey).GenerateShared( 44 ecies.ImportECDSAPublic(theirPublicKey), 45 sskLen, 46 sskLen, 47 ) 48 if err != nil { 49 return nil, err 50 } 51 52 theirIdentity := crypto.CompressPubkey(theirPublicKey) 53 if err = s.persistence.Add(theirIdentity, sharedKey, installationID); err != nil { 54 return nil, err 55 } 56 57 return &Secret{Key: sharedKey, Identity: theirPublicKey}, err 58 } 59 60 // Generate will generate a shared secret for a given identity, and return it. 61 func (s *SharedSecret) Generate(myPrivateKey *ecdsa.PrivateKey, theirPublicKey *ecdsa.PublicKey, installationID string) (*Secret, error) { 62 return s.generate(myPrivateKey, theirPublicKey, installationID) 63 } 64 65 // Agreed returns true if a secret has been acknowledged by all the installationIDs. 66 func (s *SharedSecret) Agreed(myPrivateKey *ecdsa.PrivateKey, myInstallationID string, theirPublicKey *ecdsa.PublicKey, theirInstallationIDs []string) (*Secret, bool, error) { 67 secret, err := s.generate(myPrivateKey, theirPublicKey, myInstallationID) 68 if err != nil { 69 return nil, false, err 70 } 71 72 if len(theirInstallationIDs) == 0 { 73 return secret, false, nil 74 } 75 76 theirIdentity := crypto.CompressPubkey(theirPublicKey) 77 response, err := s.persistence.Get(theirIdentity, theirInstallationIDs) 78 if err != nil { 79 return nil, false, err 80 } 81 82 for _, installationID := range theirInstallationIDs { 83 if !response.installationIDs[installationID] { 84 return secret, false, nil 85 } 86 } 87 88 if !bytes.Equal(secret.Key, response.secret) { 89 return nil, false, errors.New("computed and saved secrets are different for a given identity") 90 } 91 92 return secret, true, nil 93 } 94 95 func (s *SharedSecret) All() ([]*Secret, error) { 96 var secrets []*Secret 97 tuples, err := s.persistence.All() 98 if err != nil { 99 return nil, err 100 } 101 102 for _, tuple := range tuples { 103 key, err := crypto.DecompressPubkey(tuple[0]) 104 if err != nil { 105 return nil, err 106 } 107 secrets = append(secrets, &Secret{Identity: key, Key: tuple[1]}) 108 } 109 110 return secrets, nil 111 }