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  }