github.com/shyftnetwork/go-empyrean@v1.8.3-0.20191127201940-fbfca9338f04/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/ShyftNetwork/go-empyrean/common" 29 "github.com/ShyftNetwork/go-empyrean/common/hexutil" 30 "github.com/ShyftNetwork/go-empyrean/crypto" 31 "github.com/ShyftNetwork/go-empyrean/p2p" 32 "github.com/ShyftNetwork/go-empyrean/rlp" 33 "github.com/ShyftNetwork/go-empyrean/rpc" 34 "github.com/ShyftNetwork/go-empyrean/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 lock sync.Mutex 110 symKeyRequestTimeout time.Duration 111 symKeyExpiryTimeout time.Duration 112 symKeySendLimit uint16 113 symKeyCapacity uint8 114 symKeyIndex map[string]*handshakeKey 115 handshakes map[string]map[Topic]*handshake 116 deregisterFuncs map[Topic]func() 117 } 118 119 // Attach HandshakeController to pss node 120 // 121 // Must be called before starting the pss node service 122 func SetHandshakeController(pss *Pss, params *HandshakeParams) error { 123 ctrl := &HandshakeController{ 124 pss: pss, 125 keyC: make(map[string]chan []string), 126 symKeyRequestTimeout: params.SymKeyRequestTimeout, 127 symKeyExpiryTimeout: params.SymKeyExpiryTimeout, 128 symKeySendLimit: params.SymKeySendLimit, 129 symKeyCapacity: params.SymKeyCapacity, 130 symKeyIndex: make(map[string]*handshakeKey), 131 handshakes: make(map[string]map[Topic]*handshake), 132 deregisterFuncs: make(map[Topic]func()), 133 } 134 api := &HandshakeAPI{ 135 namespace: "pss", 136 ctrl: ctrl, 137 } 138 pss.addAPI(rpc.API{ 139 Namespace: api.namespace, 140 Version: "0.2", 141 Service: api, 142 Public: true, 143 }) 144 ctrlSingleton = ctrl 145 return nil 146 } 147 148 // Return all unexpired symmetric keys from store by 149 // peer (public key), topic and specified direction 150 func (ctl *HandshakeController) validKeys(pubkeyid string, topic *Topic, in bool) (validkeys []*string) { 151 ctl.lock.Lock() 152 defer ctl.lock.Unlock() 153 now := time.Now() 154 if _, ok := ctl.handshakes[pubkeyid]; !ok { 155 return []*string{} 156 } else if _, ok := ctl.handshakes[pubkeyid][*topic]; !ok { 157 return []*string{} 158 } 159 var keystore *[]handshakeKey 160 if in { 161 keystore = &(ctl.handshakes[pubkeyid][*topic].inKeys) 162 } else { 163 keystore = &(ctl.handshakes[pubkeyid][*topic].outKeys) 164 } 165 166 for _, key := range *keystore { 167 if key.limit <= key.count { 168 ctl.releaseKey(*key.symKeyID, topic) 169 } else if !key.expiredAt.IsZero() && key.expiredAt.Before(now) { 170 ctl.releaseKey(*key.symKeyID, topic) 171 } else { 172 validkeys = append(validkeys, key.symKeyID) 173 } 174 } 175 return 176 } 177 178 // Add all given symmetric keys with validity limits to store by 179 // peer (public key), topic and specified direction 180 func (ctl *HandshakeController) updateKeys(pubkeyid string, topic *Topic, in bool, symkeyids []string, limit uint16) { 181 ctl.lock.Lock() 182 defer ctl.lock.Unlock() 183 if _, ok := ctl.handshakes[pubkeyid]; !ok { 184 ctl.handshakes[pubkeyid] = make(map[Topic]*handshake) 185 186 } 187 if ctl.handshakes[pubkeyid][*topic] == nil { 188 ctl.handshakes[pubkeyid][*topic] = &handshake{} 189 } 190 var keystore *[]handshakeKey 191 expire := time.Now() 192 if in { 193 keystore = &(ctl.handshakes[pubkeyid][*topic].inKeys) 194 } else { 195 keystore = &(ctl.handshakes[pubkeyid][*topic].outKeys) 196 expire = expire.Add(time.Millisecond * ctl.symKeyExpiryTimeout) 197 } 198 for _, storekey := range *keystore { 199 storekey.expiredAt = expire 200 } 201 for i := 0; i < len(symkeyids); i++ { 202 storekey := handshakeKey{ 203 symKeyID: &symkeyids[i], 204 pubKeyID: &pubkeyid, 205 limit: limit, 206 } 207 *keystore = append(*keystore, storekey) 208 ctl.pss.symKeyPool[*storekey.symKeyID][*topic].protected = true 209 } 210 for i := 0; i < len(*keystore); i++ { 211 ctl.symKeyIndex[*(*keystore)[i].symKeyID] = &((*keystore)[i]) 212 } 213 } 214 215 // Expire a symmetric key, making it elegible for garbage collection 216 func (ctl *HandshakeController) releaseKey(symkeyid string, topic *Topic) bool { 217 if ctl.symKeyIndex[symkeyid] == nil { 218 log.Debug("no symkey", "symkeyid", symkeyid) 219 return false 220 } 221 ctl.symKeyIndex[symkeyid].expiredAt = time.Now() 222 log.Debug("handshake release", "symkeyid", symkeyid) 223 return true 224 } 225 226 // Checks all symmetric keys in given direction(s) by 227 // specified peer (public key) and topic for expiry. 228 // Expired means: 229 // - expiry timestamp is set, and grace period is exceeded 230 // - message validity limit is reached 231 func (ctl *HandshakeController) cleanHandshake(pubkeyid string, topic *Topic, in bool, out bool) int { 232 ctl.lock.Lock() 233 defer ctl.lock.Unlock() 234 var deletecount int 235 var deletes []string 236 now := time.Now() 237 handshake := ctl.handshakes[pubkeyid][*topic] 238 log.Debug("handshake clean", "pubkey", pubkeyid, "topic", topic) 239 if in { 240 for i, key := range handshake.inKeys { 241 if key.expiredAt.Before(now) || (key.expiredAt.IsZero() && key.limit <= key.count) { 242 log.Trace("handshake in clean remove", "symkeyid", *key.symKeyID) 243 deletes = append(deletes, *key.symKeyID) 244 handshake.inKeys[deletecount] = handshake.inKeys[i] 245 deletecount++ 246 } 247 } 248 handshake.inKeys = handshake.inKeys[:len(handshake.inKeys)-deletecount] 249 } 250 if out { 251 deletecount = 0 252 for i, key := range handshake.outKeys { 253 if key.expiredAt.Before(now) && (key.expiredAt.IsZero() && key.limit <= key.count) { 254 log.Trace("handshake out clean remove", "symkeyid", *key.symKeyID) 255 deletes = append(deletes, *key.symKeyID) 256 handshake.outKeys[deletecount] = handshake.outKeys[i] 257 deletecount++ 258 } 259 } 260 handshake.outKeys = handshake.outKeys[:len(handshake.outKeys)-deletecount] 261 } 262 for _, keyid := range deletes { 263 delete(ctl.symKeyIndex, keyid) 264 ctl.pss.symKeyPool[keyid][*topic].protected = false 265 } 266 return len(deletes) 267 } 268 269 // Runs cleanHandshake() on all peers and topics 270 func (ctl *HandshakeController) clean() { 271 peerpubkeys := ctl.handshakes 272 for pubkeyid, peertopics := range peerpubkeys { 273 for topic := range peertopics { 274 ctl.cleanHandshake(pubkeyid, &topic, true, true) 275 } 276 } 277 } 278 279 // Passed as a PssMsg handler for the topic handshake is activated on 280 // Handles incoming key exchange messages and 281 // ccunts message usage by symmetric key (expiry limit control) 282 // Only returns error if key handler fails 283 func (ctl *HandshakeController) handler(msg []byte, p *p2p.Peer, asymmetric bool, symkeyid string) error { 284 if !asymmetric { 285 if ctl.symKeyIndex[symkeyid] != nil { 286 if ctl.symKeyIndex[symkeyid].count >= ctl.symKeyIndex[symkeyid].limit { 287 return fmt.Errorf("discarding message using expired key: %s", symkeyid) 288 } 289 ctl.symKeyIndex[symkeyid].count++ 290 log.Trace("increment symkey recv use", "symsymkeyid", symkeyid, "count", ctl.symKeyIndex[symkeyid].count, "limit", ctl.symKeyIndex[symkeyid].limit, "receiver", common.ToHex(crypto.FromECDSAPub(ctl.pss.PublicKey()))) 291 } 292 return nil 293 } 294 keymsg := &handshakeMsg{} 295 err := rlp.DecodeBytes(msg, keymsg) 296 if err == nil { 297 err := ctl.handleKeys(symkeyid, keymsg) 298 if err != nil { 299 log.Error("handlekeys fail", "error", err) 300 } 301 return err 302 } 303 return nil 304 } 305 306 // Handle incoming key exchange message 307 // Add keys received from peer to store 308 // and enerate and send the amount of keys requested by peer 309 // 310 // TODO: 311 // - flood guard 312 // - keylength check 313 // - update address hint if: 314 // 1) leftmost bytes in new address do not match stored 315 // 2) else, if new address is longer 316 func (ctl *HandshakeController) handleKeys(pubkeyid string, keymsg *handshakeMsg) error { 317 // new keys from peer 318 if len(keymsg.Keys) > 0 { 319 log.Debug("received handshake keys", "pubkeyid", pubkeyid, "from", keymsg.From, "count", len(keymsg.Keys)) 320 var sendsymkeyids []string 321 for _, key := range keymsg.Keys { 322 sendsymkey := make([]byte, len(key)) 323 copy(sendsymkey, key) 324 sendsymkeyid, err := ctl.pss.setSymmetricKey(sendsymkey, keymsg.Topic, PssAddress(keymsg.From), false, false) 325 if err != nil { 326 return err 327 } 328 sendsymkeyids = append(sendsymkeyids, sendsymkeyid) 329 } 330 if len(sendsymkeyids) > 0 { 331 ctl.updateKeys(pubkeyid, &keymsg.Topic, false, sendsymkeyids, keymsg.Limit) 332 333 ctl.alertHandshake(pubkeyid, sendsymkeyids) 334 } 335 } 336 337 // peer request for keys 338 if keymsg.Request > 0 { 339 _, err := ctl.sendKey(pubkeyid, &keymsg.Topic, keymsg.Request) 340 if err != nil { 341 return err 342 } 343 } 344 345 return nil 346 } 347 348 // Send key exchange to peer (public key) valid for `topic` 349 // Will send number of keys specified by `keycount` with 350 // validity limits specified in `msglimit` 351 // If number of valid outgoing keys is less than the ideal/max 352 // amount, a request is sent for the amount of keys to make up 353 // the difference 354 func (ctl *HandshakeController) sendKey(pubkeyid string, topic *Topic, keycount uint8) ([]string, error) { 355 356 var requestcount uint8 357 to := PssAddress{} 358 if _, ok := ctl.pss.pubKeyPool[pubkeyid]; !ok { 359 return []string{}, errors.New("Invalid public key") 360 } else if psp, ok := ctl.pss.pubKeyPool[pubkeyid][*topic]; ok { 361 to = psp.address 362 } 363 364 recvkeys := make([][]byte, keycount) 365 recvkeyids := make([]string, keycount) 366 ctl.lock.Lock() 367 if _, ok := ctl.handshakes[pubkeyid]; !ok { 368 ctl.handshakes[pubkeyid] = make(map[Topic]*handshake) 369 } 370 ctl.lock.Unlock() 371 372 // check if buffer is not full 373 outkeys := ctl.validKeys(pubkeyid, topic, false) 374 if len(outkeys) < int(ctl.symKeyCapacity) { 375 //requestcount = uint8(self.symKeyCapacity - uint8(len(outkeys))) 376 requestcount = ctl.symKeyCapacity 377 } 378 // return if there's nothing to be accomplished 379 if requestcount == 0 && keycount == 0 { 380 return []string{}, nil 381 } 382 383 // generate new keys to send 384 for i := 0; i < len(recvkeyids); i++ { 385 var err error 386 recvkeyids[i], err = ctl.pss.GenerateSymmetricKey(*topic, to, true) 387 if err != nil { 388 return []string{}, fmt.Errorf("set receive symkey fail (pubkey %x topic %x): %v", pubkeyid, topic, err) 389 } 390 recvkeys[i], err = ctl.pss.GetSymmetricKey(recvkeyids[i]) 391 if err != nil { 392 return []string{}, fmt.Errorf("GET Generated outgoing symkey fail (pubkey %x topic %x): %v", pubkeyid, topic, err) 393 } 394 } 395 ctl.updateKeys(pubkeyid, topic, true, recvkeyids, ctl.symKeySendLimit) 396 397 // encode and send the message 398 recvkeymsg := &handshakeMsg{ 399 From: ctl.pss.BaseAddr(), 400 Keys: recvkeys, 401 Request: requestcount, 402 Limit: ctl.symKeySendLimit, 403 Topic: *topic, 404 } 405 log.Debug("sending our symkeys", "pubkey", pubkeyid, "symkeys", recvkeyids, "limit", ctl.symKeySendLimit, "requestcount", requestcount, "keycount", len(recvkeys)) 406 recvkeybytes, err := rlp.EncodeToBytes(recvkeymsg) 407 if err != nil { 408 return []string{}, fmt.Errorf("rlp keymsg encode fail: %v", err) 409 } 410 // if the send fails it means this public key is not registered for this particular address AND topic 411 err = ctl.pss.SendAsym(pubkeyid, *topic, recvkeybytes) 412 if err != nil { 413 return []string{}, fmt.Errorf("Send symkey failed: %v", err) 414 } 415 return recvkeyids, nil 416 } 417 418 // Enables callback for keys received from a key exchange request 419 func (ctl *HandshakeController) alertHandshake(pubkeyid string, symkeys []string) chan []string { 420 if len(symkeys) > 0 { 421 if _, ok := ctl.keyC[pubkeyid]; ok { 422 ctl.keyC[pubkeyid] <- symkeys 423 close(ctl.keyC[pubkeyid]) 424 delete(ctl.keyC, pubkeyid) 425 } 426 return nil 427 } 428 if _, ok := ctl.keyC[pubkeyid]; !ok { 429 ctl.keyC[pubkeyid] = make(chan []string) 430 } 431 return ctl.keyC[pubkeyid] 432 } 433 434 type HandshakeAPI struct { 435 namespace string 436 ctrl *HandshakeController 437 } 438 439 // Initiate a handshake session for a peer (public key) and topic 440 // combination. 441 // 442 // If `sync` is set, the call will block until keys are received from peer, 443 // or if the handshake request times out 444 // 445 // If `flush` is set, the max amount of keys will be sent to the peer 446 // regardless of how many valid keys that currently exist in the store. 447 // 448 // Returns list of symmetric key ids that can be passed to pss.GetSymmetricKey() 449 // for retrieval of the symmetric key bytes themselves. 450 // 451 // Fails if the incoming symmetric key store is already full (and `flush` is false), 452 // or if the underlying key dispatcher fails 453 func (api *HandshakeAPI) Handshake(pubkeyid string, topic Topic, sync bool, flush bool) (keys []string, err error) { 454 var hsc chan []string 455 var keycount uint8 456 if flush { 457 keycount = api.ctrl.symKeyCapacity 458 } else { 459 validkeys := api.ctrl.validKeys(pubkeyid, &topic, false) 460 keycount = api.ctrl.symKeyCapacity - uint8(len(validkeys)) 461 } 462 if keycount == 0 { 463 return keys, errors.New("Incoming symmetric key store is already full") 464 } 465 if sync { 466 hsc = api.ctrl.alertHandshake(pubkeyid, []string{}) 467 } 468 _, err = api.ctrl.sendKey(pubkeyid, &topic, keycount) 469 if err != nil { 470 return keys, err 471 } 472 if sync { 473 ctx, cancel := context.WithTimeout(context.Background(), api.ctrl.symKeyRequestTimeout) 474 defer cancel() 475 select { 476 case keys = <-hsc: 477 log.Trace("sync handshake response receive", "key", keys) 478 case <-ctx.Done(): 479 return []string{}, errors.New("timeout") 480 } 481 } 482 return keys, nil 483 } 484 485 // Activate handshake functionality on a topic 486 func (api *HandshakeAPI) AddHandshake(topic Topic) error { 487 api.ctrl.deregisterFuncs[topic] = api.ctrl.pss.Register(&topic, NewHandler(api.ctrl.handler)) 488 return nil 489 } 490 491 // Deactivate handshake functionality on a topic 492 func (api *HandshakeAPI) RemoveHandshake(topic *Topic) error { 493 if _, ok := api.ctrl.deregisterFuncs[*topic]; ok { 494 api.ctrl.deregisterFuncs[*topic]() 495 } 496 return nil 497 } 498 499 // Returns all valid symmetric keys in store per peer (public key) 500 // and topic. 501 // 502 // The `in` and `out` parameters indicate for which direction(s) 503 // symmetric keys will be returned. 504 // If both are false, no keys (and no error) will be returned. 505 func (api *HandshakeAPI) GetHandshakeKeys(pubkeyid string, topic Topic, in bool, out bool) (keys []string, err error) { 506 if in { 507 for _, inkey := range api.ctrl.validKeys(pubkeyid, &topic, true) { 508 keys = append(keys, *inkey) 509 } 510 } 511 if out { 512 for _, outkey := range api.ctrl.validKeys(pubkeyid, &topic, false) { 513 keys = append(keys, *outkey) 514 } 515 } 516 return keys, nil 517 } 518 519 // Returns the amount of messages the specified symmetric key 520 // is still valid for under the handshake scheme 521 func (api *HandshakeAPI) GetHandshakeKeyCapacity(symkeyid string) (uint16, error) { 522 storekey := api.ctrl.symKeyIndex[symkeyid] 523 if storekey == nil { 524 return 0, fmt.Errorf("invalid symkey id %s", symkeyid) 525 } 526 return storekey.limit - storekey.count, nil 527 } 528 529 // Returns the byte representation of the public key in ascii hex 530 // associated with the given symmetric key 531 func (api *HandshakeAPI) GetHandshakePublicKey(symkeyid string) (string, error) { 532 storekey := api.ctrl.symKeyIndex[symkeyid] 533 if storekey == nil { 534 return "", fmt.Errorf("invalid symkey id %s", symkeyid) 535 } 536 return *storekey.pubKeyID, nil 537 } 538 539 // Manually expire the given symkey 540 // 541 // If `flush` is set, garbage collection will be performed before returning. 542 // 543 // Returns true on successful removal, false otherwise 544 func (api *HandshakeAPI) ReleaseHandshakeKey(pubkeyid string, topic Topic, symkeyid string, flush bool) (removed bool, err error) { 545 removed = api.ctrl.releaseKey(symkeyid, &topic) 546 if removed && flush { 547 api.ctrl.cleanHandshake(pubkeyid, &topic, true, true) 548 } 549 return 550 } 551 552 // Send symmetric message under the handshake scheme 553 // 554 // Overloads the pss.SendSym() API call, adding symmetric key usage count 555 // for message expiry control 556 func (api *HandshakeAPI) SendSym(symkeyid string, topic Topic, msg hexutil.Bytes) (err error) { 557 err = api.ctrl.pss.SendSym(symkeyid, topic, msg[:]) 558 if api.ctrl.symKeyIndex[symkeyid] != nil { 559 if api.ctrl.symKeyIndex[symkeyid].count >= api.ctrl.symKeyIndex[symkeyid].limit { 560 return errors.New("attempted send with expired key") 561 } 562 api.ctrl.symKeyIndex[symkeyid].count++ 563 log.Trace("increment symkey send use", "symkeyid", symkeyid, "count", api.ctrl.symKeyIndex[symkeyid].count, "limit", api.ctrl.symKeyIndex[symkeyid].limit, "receiver", common.ToHex(crypto.FromECDSAPub(api.ctrl.pss.PublicKey()))) 564 } 565 return err 566 }