github.com/oskarth/go-ethereum@v1.6.8-0.20191013093314-dac24a9d3494/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 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 var address PssAddress 325 copy(address[:], keymsg.From) 326 sendsymkeyid, err := ctl.pss.setSymmetricKey(sendsymkey, keymsg.Topic, &address, false, false) 327 if err != nil { 328 return err 329 } 330 sendsymkeyids = append(sendsymkeyids, sendsymkeyid) 331 } 332 if len(sendsymkeyids) > 0 { 333 ctl.updateKeys(pubkeyid, &keymsg.Topic, false, sendsymkeyids, keymsg.Limit) 334 335 ctl.alertHandshake(pubkeyid, sendsymkeyids) 336 } 337 } 338 339 // peer request for keys 340 if keymsg.Request > 0 { 341 _, err := ctl.sendKey(pubkeyid, &keymsg.Topic, keymsg.Request) 342 if err != nil { 343 return err 344 } 345 } 346 347 return nil 348 } 349 350 // Send key exchange to peer (public key) valid for `topic` 351 // Will send number of keys specified by `keycount` with 352 // validity limits specified in `msglimit` 353 // If number of valid outgoing keys is less than the ideal/max 354 // amount, a request is sent for the amount of keys to make up 355 // the difference 356 func (ctl *HandshakeController) sendKey(pubkeyid string, topic *Topic, keycount uint8) ([]string, error) { 357 358 var requestcount uint8 359 to := &PssAddress{} 360 if _, ok := ctl.pss.pubKeyPool[pubkeyid]; !ok { 361 return []string{}, errors.New("Invalid public key") 362 } else if psp, ok := ctl.pss.pubKeyPool[pubkeyid][*topic]; ok { 363 to = psp.address 364 } 365 366 recvkeys := make([][]byte, keycount) 367 recvkeyids := make([]string, keycount) 368 ctl.lock.Lock() 369 if _, ok := ctl.handshakes[pubkeyid]; !ok { 370 ctl.handshakes[pubkeyid] = make(map[Topic]*handshake) 371 } 372 ctl.lock.Unlock() 373 374 // check if buffer is not full 375 outkeys := ctl.validKeys(pubkeyid, topic, false) 376 if len(outkeys) < int(ctl.symKeyCapacity) { 377 //requestcount = uint8(self.symKeyCapacity - uint8(len(outkeys))) 378 requestcount = ctl.symKeyCapacity 379 } 380 // return if there's nothing to be accomplished 381 if requestcount == 0 && keycount == 0 { 382 return []string{}, nil 383 } 384 385 // generate new keys to send 386 for i := 0; i < len(recvkeyids); i++ { 387 var err error 388 recvkeyids[i], err = ctl.pss.GenerateSymmetricKey(*topic, to, true) 389 if err != nil { 390 return []string{}, fmt.Errorf("set receive symkey fail (pubkey %x topic %x): %v", pubkeyid, topic, err) 391 } 392 recvkeys[i], err = ctl.pss.GetSymmetricKey(recvkeyids[i]) 393 if err != nil { 394 return []string{}, fmt.Errorf("GET Generated outgoing symkey fail (pubkey %x topic %x): %v", pubkeyid, topic, err) 395 } 396 } 397 ctl.updateKeys(pubkeyid, topic, true, recvkeyids, ctl.symKeySendLimit) 398 399 // encode and send the message 400 recvkeymsg := &handshakeMsg{ 401 From: ctl.pss.BaseAddr(), 402 Keys: recvkeys, 403 Request: requestcount, 404 Limit: ctl.symKeySendLimit, 405 Topic: *topic, 406 } 407 log.Debug("sending our symkeys", "pubkey", pubkeyid, "symkeys", recvkeyids, "limit", ctl.symKeySendLimit, "requestcount", requestcount, "keycount", len(recvkeys)) 408 recvkeybytes, err := rlp.EncodeToBytes(recvkeymsg) 409 if err != nil { 410 return []string{}, fmt.Errorf("rlp keymsg encode fail: %v", err) 411 } 412 // if the send fails it means this public key is not registered for this particular address AND topic 413 err = ctl.pss.SendAsym(pubkeyid, *topic, recvkeybytes) 414 if err != nil { 415 return []string{}, fmt.Errorf("Send symkey failed: %v", err) 416 } 417 return recvkeyids, nil 418 } 419 420 // Enables callback for keys received from a key exchange request 421 func (ctl *HandshakeController) alertHandshake(pubkeyid string, symkeys []string) chan []string { 422 if len(symkeys) > 0 { 423 if _, ok := ctl.keyC[pubkeyid]; ok { 424 ctl.keyC[pubkeyid] <- symkeys 425 close(ctl.keyC[pubkeyid]) 426 delete(ctl.keyC, pubkeyid) 427 } 428 return nil 429 } 430 if _, ok := ctl.keyC[pubkeyid]; !ok { 431 ctl.keyC[pubkeyid] = make(chan []string) 432 } 433 return ctl.keyC[pubkeyid] 434 } 435 436 type HandshakeAPI struct { 437 namespace string 438 ctrl *HandshakeController 439 } 440 441 // Initiate a handshake session for a peer (public key) and topic 442 // combination. 443 // 444 // If `sync` is set, the call will block until keys are received from peer, 445 // or if the handshake request times out 446 // 447 // If `flush` is set, the max amount of keys will be sent to the peer 448 // regardless of how many valid keys that currently exist in the store. 449 // 450 // Returns list of symmetric key ids that can be passed to pss.GetSymmetricKey() 451 // for retrieval of the symmetric key bytes themselves. 452 // 453 // Fails if the incoming symmetric key store is already full (and `flush` is false), 454 // or if the underlying key dispatcher fails 455 func (api *HandshakeAPI) Handshake(pubkeyid string, topic Topic, sync bool, flush bool) (keys []string, err error) { 456 var hsc chan []string 457 var keycount uint8 458 if flush { 459 keycount = api.ctrl.symKeyCapacity 460 } else { 461 validkeys := api.ctrl.validKeys(pubkeyid, &topic, false) 462 keycount = api.ctrl.symKeyCapacity - uint8(len(validkeys)) 463 } 464 if keycount == 0 { 465 return keys, errors.New("Incoming symmetric key store is already full") 466 } 467 if sync { 468 hsc = api.ctrl.alertHandshake(pubkeyid, []string{}) 469 } 470 _, err = api.ctrl.sendKey(pubkeyid, &topic, keycount) 471 if err != nil { 472 return keys, err 473 } 474 if sync { 475 ctx, cancel := context.WithTimeout(context.Background(), api.ctrl.symKeyRequestTimeout) 476 defer cancel() 477 select { 478 case keys = <-hsc: 479 log.Trace("sync handshake response receive", "key", keys) 480 case <-ctx.Done(): 481 return []string{}, errors.New("timeout") 482 } 483 } 484 return keys, nil 485 } 486 487 // Activate handshake functionality on a topic 488 func (api *HandshakeAPI) AddHandshake(topic Topic) error { 489 api.ctrl.deregisterFuncs[topic] = api.ctrl.pss.Register(&topic, api.ctrl.handler) 490 return nil 491 } 492 493 // Deactivate handshake functionality on a topic 494 func (api *HandshakeAPI) RemoveHandshake(topic *Topic) error { 495 if _, ok := api.ctrl.deregisterFuncs[*topic]; ok { 496 api.ctrl.deregisterFuncs[*topic]() 497 } 498 return nil 499 } 500 501 // Returns all valid symmetric keys in store per peer (public key) 502 // and topic. 503 // 504 // The `in` and `out` parameters indicate for which direction(s) 505 // symmetric keys will be returned. 506 // If both are false, no keys (and no error) will be returned. 507 func (api *HandshakeAPI) GetHandshakeKeys(pubkeyid string, topic Topic, in bool, out bool) (keys []string, err error) { 508 if in { 509 for _, inkey := range api.ctrl.validKeys(pubkeyid, &topic, true) { 510 keys = append(keys, *inkey) 511 } 512 } 513 if out { 514 for _, outkey := range api.ctrl.validKeys(pubkeyid, &topic, false) { 515 keys = append(keys, *outkey) 516 } 517 } 518 return keys, nil 519 } 520 521 // Returns the amount of messages the specified symmetric key 522 // is still valid for under the handshake scheme 523 func (api *HandshakeAPI) GetHandshakeKeyCapacity(symkeyid string) (uint16, error) { 524 storekey := api.ctrl.symKeyIndex[symkeyid] 525 if storekey == nil { 526 return 0, fmt.Errorf("invalid symkey id %s", symkeyid) 527 } 528 return storekey.limit - storekey.count, nil 529 } 530 531 // Returns the byte representation of the public key in ascii hex 532 // associated with the given symmetric key 533 func (api *HandshakeAPI) GetHandshakePublicKey(symkeyid string) (string, error) { 534 storekey := api.ctrl.symKeyIndex[symkeyid] 535 if storekey == nil { 536 return "", fmt.Errorf("invalid symkey id %s", symkeyid) 537 } 538 return *storekey.pubKeyID, nil 539 } 540 541 // Manually expire the given symkey 542 // 543 // If `flush` is set, garbage collection will be performed before returning. 544 // 545 // Returns true on successful removal, false otherwise 546 func (api *HandshakeAPI) ReleaseHandshakeKey(pubkeyid string, topic Topic, symkeyid string, flush bool) (removed bool, err error) { 547 removed = api.ctrl.releaseKey(symkeyid, &topic) 548 if removed && flush { 549 api.ctrl.cleanHandshake(pubkeyid, &topic, true, true) 550 } 551 return 552 } 553 554 // Send symmetric message under the handshake scheme 555 // 556 // Overloads the pss.SendSym() API call, adding symmetric key usage count 557 // for message expiry control 558 func (api *HandshakeAPI) SendSym(symkeyid string, topic Topic, msg hexutil.Bytes) (err error) { 559 err = api.ctrl.pss.SendSym(symkeyid, topic, msg[:]) 560 if api.ctrl.symKeyIndex[symkeyid] != nil { 561 if api.ctrl.symKeyIndex[symkeyid].count >= api.ctrl.symKeyIndex[symkeyid].limit { 562 return errors.New("attempted send with expired key") 563 } 564 api.ctrl.symKeyIndex[symkeyid].count++ 565 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()))) 566 } 567 return 568 }