120
vendor/github.com/status-im/status-go/protocol/encryption/sharedsecret/persistence.go
generated
vendored
Normal file
120
vendor/github.com/status-im/status-go/protocol/encryption/sharedsecret/persistence.go
generated
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
package sharedsecret
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Response struct {
|
||||
secret []byte
|
||||
installationIDs map[string]bool
|
||||
}
|
||||
|
||||
type sqlitePersistence struct {
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
func newSQLitePersistence(db *sql.DB) *sqlitePersistence {
|
||||
return &sqlitePersistence{db: db}
|
||||
}
|
||||
|
||||
func (s *sqlitePersistence) Add(identity []byte, secret []byte, installationID string) error {
|
||||
tx, err := s.db.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
insertSecretStmt, err := tx.Prepare("INSERT INTO secrets(identity, secret) VALUES (?, ?)")
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
return err
|
||||
}
|
||||
defer insertSecretStmt.Close()
|
||||
|
||||
_, err = insertSecretStmt.Exec(identity, secret)
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
insertInstallationIDStmt, err := tx.Prepare("INSERT INTO secret_installation_ids(id, identity_id) VALUES (?, ?)")
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
return err
|
||||
}
|
||||
defer insertInstallationIDStmt.Close()
|
||||
|
||||
_, err = insertInstallationIDStmt.Exec(installationID, identity)
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
return err
|
||||
}
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
func (s *sqlitePersistence) Get(identity []byte, installationIDs []string) (*Response, error) {
|
||||
response := &Response{
|
||||
installationIDs: make(map[string]bool),
|
||||
}
|
||||
args := make([]interface{}, len(installationIDs)+1)
|
||||
args[0] = identity
|
||||
for i, installationID := range installationIDs {
|
||||
args[i+1] = installationID
|
||||
}
|
||||
|
||||
/* #nosec */
|
||||
query := `SELECT secret, id
|
||||
FROM secrets t
|
||||
JOIN
|
||||
secret_installation_ids tid
|
||||
ON t.identity = tid.identity_id
|
||||
WHERE
|
||||
t.identity = ?
|
||||
AND
|
||||
tid.id IN (?` + strings.Repeat(",?", len(installationIDs)-1) + `)`
|
||||
|
||||
rows, err := s.db.Query(query, args...)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var installationID string
|
||||
var secret []byte
|
||||
err = rows.Scan(&secret, &installationID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
response.secret = secret
|
||||
response.installationIDs[installationID] = true
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (s *sqlitePersistence) All() ([][][]byte, error) {
|
||||
query := "SELECT identity, secret FROM secrets"
|
||||
|
||||
var secrets [][][]byte
|
||||
|
||||
rows, err := s.db.Query(query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var secret []byte
|
||||
var identity []byte
|
||||
err = rows.Scan(&identity, &secret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
secrets = append(secrets, [][]byte{identity, secret})
|
||||
}
|
||||
|
||||
return secrets, nil
|
||||
}
|
||||
111
vendor/github.com/status-im/status-go/protocol/encryption/sharedsecret/sharedsecret.go
generated
vendored
Normal file
111
vendor/github.com/status-im/status-go/protocol/encryption/sharedsecret/sharedsecret.go
generated
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
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
|
||||
}
|
||||
|
||||
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) {
|
||||
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] {
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user