github.com/jincm/wesharechain@v0.0.0-20210122032815-1537409ce26a/chain/swarm/pss/handshake.go (about)

     1  // Copyright 2018 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  // +build !nopsshandshake
    18  
    19  package pss
    20  
    21  import (
    22  	"context"
    23  	"errors"
    24  	"fmt"
    25  	"sync"
    26  	"time"
    27  
    28  	"github.com/ethereum/go-ethereum/common"
    29  	"github.com/ethereum/go-ethereum/common/hexutil"
    30  	"github.com/ethereum/go-ethereum/crypto"
    31  	"github.com/ethereum/go-ethereum/p2p"
    32  	"github.com/ethereum/go-ethereum/rlp"
    33  	"github.com/ethereum/go-ethereum/rpc"
    34  	"github.com/ethereum/go-ethereum/swarm/log"
    35  )
    36  
    37  const (
    38  	IsActiveHandshake = true
    39  )
    40  
    41  var (
    42  	ctrlSingleton *HandshakeController
    43  )
    44  
    45  const (
    46  	defaultSymKeyRequestTimeout = 1000 * 8  // max wait ms to receive a response to a handshake symkey request
    47  	defaultSymKeyExpiryTimeout  = 1000 * 10 // ms to wait before allowing garbage collection of an expired symkey
    48  	defaultSymKeySendLimit      = 256       // amount of messages a symkey is valid for
    49  	defaultSymKeyCapacity       = 4         // max number of symkeys to store/send simultaneously
    50  )
    51  
    52  // symmetric key exchange message payload
    53  type handshakeMsg struct {
    54  	From    []byte
    55  	Limit   uint16
    56  	Keys    [][]byte
    57  	Request uint8
    58  	Topic   Topic
    59  }
    60  
    61  // internal representation of an individual symmetric key
    62  type handshakeKey struct {
    63  	symKeyID  *string
    64  	pubKeyID  *string
    65  	limit     uint16
    66  	count     uint16
    67  	expiredAt time.Time
    68  }
    69  
    70  // container for all in- and outgoing keys
    71  // for one particular peer (public key) and topic
    72  type handshake struct {
    73  	outKeys []handshakeKey
    74  	inKeys  []handshakeKey
    75  }
    76  
    77  // Initialization parameters for the HandshakeController
    78  //
    79  // SymKeyRequestExpiry: Timeout for waiting for a handshake reply
    80  // (default 8000 ms)
    81  //
    82  // SymKeySendLimit: Amount of messages symmetric keys issues by
    83  // this node is valid for (default 256)
    84  //
    85  // SymKeyCapacity: Ideal (and maximum) amount of symmetric keys
    86  // held per direction per peer (default 4)
    87  type HandshakeParams struct {
    88  	SymKeyRequestTimeout time.Duration
    89  	SymKeyExpiryTimeout  time.Duration
    90  	SymKeySendLimit      uint16
    91  	SymKeyCapacity       uint8
    92  }
    93  
    94  // Sane defaults for HandshakeController initialization
    95  func NewHandshakeParams() *HandshakeParams {
    96  	return &HandshakeParams{
    97  		SymKeyRequestTimeout: defaultSymKeyRequestTimeout * time.Millisecond,
    98  		SymKeyExpiryTimeout:  defaultSymKeyExpiryTimeout * time.Millisecond,
    99  		SymKeySendLimit:      defaultSymKeySendLimit,
   100  		SymKeyCapacity:       defaultSymKeyCapacity,
   101  	}
   102  }
   103  
   104  // Singleton object enabling semi-automatic Diffie-Hellman
   105  // exchange of ephemeral symmetric keys
   106  type HandshakeController struct {
   107  	pss                  *Pss
   108  	keyC                 map[string]chan []string // adds a channel to report when a handshake succeeds
   109  	keyCMu               sync.Mutex               // protects keyC map
   110  	lock                 sync.Mutex
   111  	symKeyRequestTimeout time.Duration
   112  	symKeyExpiryTimeout  time.Duration
   113  	symKeySendLimit      uint16
   114  	symKeyCapacity       uint8
   115  	symKeyIndex          map[string]*handshakeKey
   116  	handshakes           map[string]map[Topic]*handshake
   117  	deregisterFuncs      map[Topic]func()
   118  }
   119  
   120  // Attach HandshakeController to pss node
   121  //
   122  // Must be called before starting the pss node service
   123  func SetHandshakeController(pss *Pss, params *HandshakeParams) error {
   124  	ctrl := &HandshakeController{
   125  		pss:                  pss,
   126  		keyC:                 make(map[string]chan []string),
   127  		symKeyRequestTimeout: params.SymKeyRequestTimeout,
   128  		symKeyExpiryTimeout:  params.SymKeyExpiryTimeout,
   129  		symKeySendLimit:      params.SymKeySendLimit,
   130  		symKeyCapacity:       params.SymKeyCapacity,
   131  		symKeyIndex:          make(map[string]*handshakeKey),
   132  		handshakes:           make(map[string]map[Topic]*handshake),
   133  		deregisterFuncs:      make(map[Topic]func()),
   134  	}
   135  	api := &HandshakeAPI{
   136  		namespace: "pss",
   137  		ctrl:      ctrl,
   138  	}
   139  	pss.addAPI(rpc.API{
   140  		Namespace: api.namespace,
   141  		Version:   "0.2",
   142  		Service:   api,
   143  		Public:    true,
   144  	})
   145  	ctrlSingleton = ctrl
   146  	return nil
   147  }
   148  
   149  // Return all unexpired symmetric keys from store by
   150  // peer (public key), topic and specified direction
   151  func (ctl *HandshakeController) validKeys(pubkeyid string, topic *Topic, in bool) (validkeys []*string) {
   152  	ctl.lock.Lock()
   153  	defer ctl.lock.Unlock()
   154  	now := time.Now()
   155  	if _, ok := ctl.handshakes[pubkeyid]; !ok {
   156  		return []*string{}
   157  	} else if _, ok := ctl.handshakes[pubkeyid][*topic]; !ok {
   158  		return []*string{}
   159  	}
   160  	var keystore *[]handshakeKey
   161  	if in {
   162  		keystore = &(ctl.handshakes[pubkeyid][*topic].inKeys)
   163  	} else {
   164  		keystore = &(ctl.handshakes[pubkeyid][*topic].outKeys)
   165  	}
   166  
   167  	for _, key := range *keystore {
   168  		if key.limit <= key.count {
   169  			ctl.releaseKeyNoLock(*key.symKeyID, topic)
   170  		} else if !key.expiredAt.IsZero() && key.expiredAt.Before(now) {
   171  			ctl.releaseKeyNoLock(*key.symKeyID, topic)
   172  		} else {
   173  			validkeys = append(validkeys, key.symKeyID)
   174  		}
   175  	}
   176  	return
   177  }
   178  
   179  // Add all given symmetric keys with validity limits to store by
   180  // peer (public key), topic and specified direction
   181  func (ctl *HandshakeController) updateKeys(pubkeyid string, topic *Topic, in bool, symkeyids []string, limit uint16) {
   182  	ctl.lock.Lock()
   183  	defer ctl.lock.Unlock()
   184  	if _, ok := ctl.handshakes[pubkeyid]; !ok {
   185  		ctl.handshakes[pubkeyid] = make(map[Topic]*handshake)
   186  
   187  	}
   188  	if ctl.handshakes[pubkeyid][*topic] == nil {
   189  		ctl.handshakes[pubkeyid][*topic] = &handshake{}
   190  	}
   191  	var keystore *[]handshakeKey
   192  	expire := time.Now()
   193  	if in {
   194  		keystore = &(ctl.handshakes[pubkeyid][*topic].inKeys)
   195  	} else {
   196  		keystore = &(ctl.handshakes[pubkeyid][*topic].outKeys)
   197  		expire = expire.Add(time.Millisecond * ctl.symKeyExpiryTimeout)
   198  	}
   199  	for _, storekey := range *keystore {
   200  		storekey.expiredAt = expire
   201  	}
   202  	for i := 0; i < len(symkeyids); i++ {
   203  		storekey := handshakeKey{
   204  			symKeyID: &symkeyids[i],
   205  			pubKeyID: &pubkeyid,
   206  			limit:    limit,
   207  		}
   208  		*keystore = append(*keystore, storekey)
   209  		ctl.pss.mx.Lock()
   210  		ctl.pss.symKeyPool[*storekey.symKeyID][*topic].protected = true
   211  		ctl.pss.mx.Unlock()
   212  	}
   213  	for i := 0; i < len(*keystore); i++ {
   214  		ctl.symKeyIndex[*(*keystore)[i].symKeyID] = &((*keystore)[i])
   215  	}
   216  }
   217  
   218  func (ctl *HandshakeController) releaseKey(symkeyid string, topic *Topic) bool {
   219  	ctl.lock.Lock()
   220  	defer ctl.lock.Unlock()
   221  	return ctl.releaseKeyNoLock(symkeyid, topic)
   222  }
   223  
   224  // Expire a symmetric key, making it eligible for garbage collection
   225  func (ctl *HandshakeController) releaseKeyNoLock(symkeyid string, topic *Topic) bool {
   226  	if ctl.symKeyIndex[symkeyid] == nil {
   227  		log.Debug("no symkey", "symkeyid", symkeyid)
   228  		return false
   229  	}
   230  	ctl.symKeyIndex[symkeyid].expiredAt = time.Now()
   231  	log.Debug("handshake release", "symkeyid", symkeyid)
   232  	return true
   233  }
   234  
   235  // Checks all symmetric keys in given direction(s) by
   236  // specified peer (public key) and topic for expiry.
   237  // Expired means:
   238  // - expiry timestamp is set, and grace period is exceeded
   239  // - message validity limit is reached
   240  func (ctl *HandshakeController) cleanHandshake(pubkeyid string, topic *Topic, in bool, out bool) int {
   241  	ctl.lock.Lock()
   242  	defer ctl.lock.Unlock()
   243  	var deletecount int
   244  	var deletes []string
   245  	now := time.Now()
   246  	handshake := ctl.handshakes[pubkeyid][*topic]
   247  	log.Debug("handshake clean", "pubkey", pubkeyid, "topic", topic)
   248  	if in {
   249  		for i, key := range handshake.inKeys {
   250  			if key.expiredAt.Before(now) || (key.expiredAt.IsZero() && key.limit <= key.count) {
   251  				log.Trace("handshake in clean remove", "symkeyid", *key.symKeyID)
   252  				deletes = append(deletes, *key.symKeyID)
   253  				handshake.inKeys[deletecount] = handshake.inKeys[i]
   254  				deletecount++
   255  			}
   256  		}
   257  		handshake.inKeys = handshake.inKeys[:len(handshake.inKeys)-deletecount]
   258  	}
   259  	if out {
   260  		deletecount = 0
   261  		for i, key := range handshake.outKeys {
   262  			if key.expiredAt.Before(now) && (key.expiredAt.IsZero() && key.limit <= key.count) {
   263  				log.Trace("handshake out clean remove", "symkeyid", *key.symKeyID)
   264  				deletes = append(deletes, *key.symKeyID)
   265  				handshake.outKeys[deletecount] = handshake.outKeys[i]
   266  				deletecount++
   267  			}
   268  		}
   269  		handshake.outKeys = handshake.outKeys[:len(handshake.outKeys)-deletecount]
   270  	}
   271  	for _, keyid := range deletes {
   272  		delete(ctl.symKeyIndex, keyid)
   273  		ctl.pss.symKeyPool[keyid][*topic].protected = false
   274  	}
   275  	return len(deletes)
   276  }
   277  
   278  // Runs cleanHandshake() on all peers and topics
   279  func (ctl *HandshakeController) clean() {
   280  	peerpubkeys := ctl.handshakes
   281  	for pubkeyid, peertopics := range peerpubkeys {
   282  		for topic := range peertopics {
   283  			ctl.cleanHandshake(pubkeyid, &topic, true, true)
   284  		}
   285  	}
   286  }
   287  
   288  func (ctl *HandshakeController) getSymKey(symkeyid string) *handshakeKey {
   289  	ctl.lock.Lock()
   290  	defer ctl.lock.Unlock()
   291  	return ctl.symKeyIndex[symkeyid]
   292  }
   293  
   294  // Passed as a PssMsg handler for the topic handshake is activated on
   295  // Handles incoming key exchange messages and
   296  // counts message usage by symmetric key (expiry limit control)
   297  // Only returns error if key handler fails
   298  func (ctl *HandshakeController) handler(msg []byte, p *p2p.Peer, asymmetric bool, symkeyid string) error {
   299  	if asymmetric {
   300  		keymsg := &handshakeMsg{}
   301  		err := rlp.DecodeBytes(msg, keymsg)
   302  		if err == nil {
   303  			err := ctl.handleKeys(symkeyid, keymsg)
   304  			if err != nil {
   305  				log.Error("handlekeys fail", "error", err)
   306  			}
   307  			return err
   308  		}
   309  		return nil
   310  	}
   311  	return ctl.registerSymKeyUse(symkeyid)
   312  }
   313  
   314  func (ctl *HandshakeController) registerSymKeyUse(symkeyid string) error {
   315  	ctl.lock.Lock()
   316  	defer ctl.lock.Unlock()
   317  
   318  	symKey, ok := ctl.symKeyIndex[symkeyid]
   319  	if !ok {
   320  		return nil
   321  	}
   322  
   323  	if symKey.count >= symKey.limit {
   324  		return fmt.Errorf("symetric key expired (id: %s)", symkeyid)
   325  	}
   326  	symKey.count++
   327  
   328  	receiver := common.ToHex(crypto.FromECDSAPub(ctl.pss.PublicKey()))
   329  	log.Trace("increment symkey recv use", "symsymkeyid", symkeyid, "count", symKey.count, "limit", symKey.limit, "receiver", receiver)
   330  
   331  	return nil
   332  }
   333  
   334  // Handle incoming key exchange message
   335  // Add keys received from peer to store
   336  // and enerate and send the amount of keys requested by peer
   337  //
   338  // TODO:
   339  // - flood guard
   340  // - keylength check
   341  // - update address hint if:
   342  //   1) leftmost bytes in new address do not match stored
   343  //   2) else, if new address is longer
   344  func (ctl *HandshakeController) handleKeys(pubkeyid string, keymsg *handshakeMsg) error {
   345  	// new keys from peer
   346  	if len(keymsg.Keys) > 0 {
   347  		log.Debug("received handshake keys", "pubkeyid", pubkeyid, "from", keymsg.From, "count", len(keymsg.Keys))
   348  		var sendsymkeyids []string
   349  		for _, key := range keymsg.Keys {
   350  			sendsymkey := make([]byte, len(key))
   351  			copy(sendsymkey, key)
   352  			sendsymkeyid, err := ctl.pss.setSymmetricKey(sendsymkey, keymsg.Topic, PssAddress(keymsg.From), false, false)
   353  			if err != nil {
   354  				return err
   355  			}
   356  			sendsymkeyids = append(sendsymkeyids, sendsymkeyid)
   357  		}
   358  		if len(sendsymkeyids) > 0 {
   359  			ctl.updateKeys(pubkeyid, &keymsg.Topic, false, sendsymkeyids, keymsg.Limit)
   360  
   361  			ctl.alertHandshake(pubkeyid, sendsymkeyids)
   362  		}
   363  	}
   364  
   365  	// peer request for keys
   366  	if keymsg.Request > 0 {
   367  		_, err := ctl.sendKey(pubkeyid, &keymsg.Topic, keymsg.Request)
   368  		if err != nil {
   369  			return err
   370  		}
   371  	}
   372  
   373  	return nil
   374  }
   375  
   376  // Send key exchange to peer (public key) valid for `topic`
   377  // Will send number of keys specified by `keycount` with
   378  // validity limits specified in `msglimit`
   379  // If number of valid outgoing keys is less than the ideal/max
   380  // amount, a request is sent for the amount of keys to make up
   381  // the difference
   382  func (ctl *HandshakeController) sendKey(pubkeyid string, topic *Topic, keycount uint8) ([]string, error) {
   383  
   384  	var requestcount uint8
   385  	to := PssAddress{}
   386  	if _, ok := ctl.pss.pubKeyPool[pubkeyid]; !ok {
   387  		return []string{}, errors.New("Invalid public key")
   388  	} else if psp, ok := ctl.pss.pubKeyPool[pubkeyid][*topic]; ok {
   389  		to = psp.address
   390  	}
   391  
   392  	recvkeys := make([][]byte, keycount)
   393  	recvkeyids := make([]string, keycount)
   394  	ctl.lock.Lock()
   395  	if _, ok := ctl.handshakes[pubkeyid]; !ok {
   396  		ctl.handshakes[pubkeyid] = make(map[Topic]*handshake)
   397  	}
   398  	ctl.lock.Unlock()
   399  
   400  	// check if buffer is not full
   401  	outkeys := ctl.validKeys(pubkeyid, topic, false)
   402  	if len(outkeys) < int(ctl.symKeyCapacity) {
   403  		//requestcount = uint8(self.symKeyCapacity - uint8(len(outkeys)))
   404  		requestcount = ctl.symKeyCapacity
   405  	}
   406  	// return if there's nothing to be accomplished
   407  	if requestcount == 0 && keycount == 0 {
   408  		return []string{}, nil
   409  	}
   410  
   411  	// generate new keys to send
   412  	for i := 0; i < len(recvkeyids); i++ {
   413  		var err error
   414  		recvkeyids[i], err = ctl.pss.GenerateSymmetricKey(*topic, to, true)
   415  		if err != nil {
   416  			return []string{}, fmt.Errorf("set receive symkey fail (pubkey %x topic %x): %v", pubkeyid, topic, err)
   417  		}
   418  		recvkeys[i], err = ctl.pss.GetSymmetricKey(recvkeyids[i])
   419  		if err != nil {
   420  			return []string{}, fmt.Errorf("GET Generated outgoing symkey fail (pubkey %x topic %x): %v", pubkeyid, topic, err)
   421  		}
   422  	}
   423  	ctl.updateKeys(pubkeyid, topic, true, recvkeyids, ctl.symKeySendLimit)
   424  
   425  	// encode and send the message
   426  	recvkeymsg := &handshakeMsg{
   427  		From:    ctl.pss.BaseAddr(),
   428  		Keys:    recvkeys,
   429  		Request: requestcount,
   430  		Limit:   ctl.symKeySendLimit,
   431  		Topic:   *topic,
   432  	}
   433  	log.Debug("sending our symkeys", "pubkey", pubkeyid, "symkeys", recvkeyids, "limit", ctl.symKeySendLimit, "requestcount", requestcount, "keycount", len(recvkeys))
   434  	recvkeybytes, err := rlp.EncodeToBytes(recvkeymsg)
   435  	if err != nil {
   436  		return []string{}, fmt.Errorf("rlp keymsg encode fail: %v", err)
   437  	}
   438  	// if the send fails it means this public key is not registered for this particular address AND topic
   439  	err = ctl.pss.SendAsym(pubkeyid, *topic, recvkeybytes)
   440  	if err != nil {
   441  		return []string{}, fmt.Errorf("Send symkey failed: %v", err)
   442  	}
   443  	return recvkeyids, nil
   444  }
   445  
   446  // Enables callback for keys received from a key exchange request
   447  func (ctl *HandshakeController) alertHandshake(pubkeyid string, symkeys []string) chan []string {
   448  	ctl.keyCMu.Lock()
   449  	defer ctl.keyCMu.Unlock()
   450  	if len(symkeys) > 0 {
   451  		if _, ok := ctl.keyC[pubkeyid]; ok {
   452  			ctl.keyC[pubkeyid] <- symkeys
   453  			close(ctl.keyC[pubkeyid])
   454  			delete(ctl.keyC, pubkeyid)
   455  		}
   456  		return nil
   457  	}
   458  	if _, ok := ctl.keyC[pubkeyid]; !ok {
   459  		ctl.keyC[pubkeyid] = make(chan []string)
   460  	}
   461  	return ctl.keyC[pubkeyid]
   462  }
   463  
   464  type HandshakeAPI struct {
   465  	namespace string
   466  	ctrl      *HandshakeController
   467  }
   468  
   469  // Initiate a handshake session for a peer (public key) and topic
   470  // combination.
   471  //
   472  // If `sync` is set, the call will block until keys are received from peer,
   473  // or if the handshake request times out
   474  //
   475  // If `flush` is set, the max amount of keys will be sent to the peer
   476  // regardless of how many valid keys that currently exist in the store.
   477  //
   478  // Returns list of symmetric key ids that can be passed to pss.GetSymmetricKey()
   479  // for retrieval of the symmetric key bytes themselves.
   480  //
   481  // Fails if the incoming symmetric key store is already full (and `flush` is false),
   482  // or if the underlying key dispatcher fails
   483  func (api *HandshakeAPI) Handshake(pubkeyid string, topic Topic, sync bool, flush bool) (keys []string, err error) {
   484  	var hsc chan []string
   485  	var keycount uint8
   486  	if flush {
   487  		keycount = api.ctrl.symKeyCapacity
   488  	} else {
   489  		validkeys := api.ctrl.validKeys(pubkeyid, &topic, false)
   490  		keycount = api.ctrl.symKeyCapacity - uint8(len(validkeys))
   491  	}
   492  	if keycount == 0 {
   493  		return keys, errors.New("Incoming symmetric key store is already full")
   494  	}
   495  	if sync {
   496  		hsc = api.ctrl.alertHandshake(pubkeyid, []string{})
   497  	}
   498  	_, err = api.ctrl.sendKey(pubkeyid, &topic, keycount)
   499  	if err != nil {
   500  		return keys, err
   501  	}
   502  	if sync {
   503  		ctx, cancel := context.WithTimeout(context.Background(), api.ctrl.symKeyRequestTimeout)
   504  		defer cancel()
   505  		select {
   506  		case keys = <-hsc:
   507  			log.Trace("sync handshake response receive", "key", keys)
   508  		case <-ctx.Done():
   509  			return []string{}, errors.New("timeout")
   510  		}
   511  	}
   512  	return keys, nil
   513  }
   514  
   515  // Activate handshake functionality on a topic
   516  func (api *HandshakeAPI) AddHandshake(topic Topic) error {
   517  	api.ctrl.deregisterFuncs[topic] = api.ctrl.pss.Register(&topic, NewHandler(api.ctrl.handler))
   518  	return nil
   519  }
   520  
   521  // Deactivate handshake functionality on a topic
   522  func (api *HandshakeAPI) RemoveHandshake(topic *Topic) error {
   523  	if _, ok := api.ctrl.deregisterFuncs[*topic]; ok {
   524  		api.ctrl.deregisterFuncs[*topic]()
   525  	}
   526  	return nil
   527  }
   528  
   529  // Returns all valid symmetric keys in store per peer (public key)
   530  // and topic.
   531  //
   532  // The `in` and `out` parameters indicate for which direction(s)
   533  // symmetric keys will be returned.
   534  // If both are false, no keys (and no error) will be returned.
   535  func (api *HandshakeAPI) GetHandshakeKeys(pubkeyid string, topic Topic, in bool, out bool) (keys []string, err error) {
   536  	if in {
   537  		for _, inkey := range api.ctrl.validKeys(pubkeyid, &topic, true) {
   538  			keys = append(keys, *inkey)
   539  		}
   540  	}
   541  	if out {
   542  		for _, outkey := range api.ctrl.validKeys(pubkeyid, &topic, false) {
   543  			keys = append(keys, *outkey)
   544  		}
   545  	}
   546  	return keys, nil
   547  }
   548  
   549  // Returns the amount of messages the specified symmetric key
   550  // is still valid for under the handshake scheme
   551  func (api *HandshakeAPI) GetHandshakeKeyCapacity(symkeyid string) (uint16, error) {
   552  	storekey := api.ctrl.getSymKey(symkeyid)
   553  	if storekey == nil {
   554  		return 0, fmt.Errorf("invalid symkey id %s", symkeyid)
   555  	}
   556  	return storekey.limit - storekey.count, nil
   557  }
   558  
   559  // Returns the byte representation of the public key in ascii hex
   560  // associated with the given symmetric key
   561  func (api *HandshakeAPI) GetHandshakePublicKey(symkeyid string) (string, error) {
   562  	storekey := api.ctrl.getSymKey(symkeyid)
   563  	if storekey == nil {
   564  		return "", fmt.Errorf("invalid symkey id %s", symkeyid)
   565  	}
   566  	return *storekey.pubKeyID, nil
   567  }
   568  
   569  // Manually expire the given symkey
   570  //
   571  // If `flush` is set, garbage collection will be performed before returning.
   572  //
   573  // Returns true on successful removal, false otherwise
   574  func (api *HandshakeAPI) ReleaseHandshakeKey(pubkeyid string, topic Topic, symkeyid string, flush bool) (removed bool, err error) {
   575  	removed = api.ctrl.releaseKey(symkeyid, &topic)
   576  	if removed && flush {
   577  		api.ctrl.cleanHandshake(pubkeyid, &topic, true, true)
   578  	}
   579  	return
   580  }
   581  
   582  // Send symmetric message under the handshake scheme
   583  //
   584  // Overloads the pss.SendSym() API call, adding symmetric key usage count
   585  // for message expiry control
   586  func (api *HandshakeAPI) SendSym(symkeyid string, topic Topic, msg hexutil.Bytes) (err error) {
   587  	err = api.ctrl.pss.SendSym(symkeyid, topic, msg[:])
   588  	if otherErr := api.ctrl.registerSymKeyUse(symkeyid); otherErr != nil {
   589  		return otherErr
   590  	}
   591  	return err
   592  }