gitlab.com/SiaPrime/SiaPrime@v1.4.1/modules/host/rpcloop.go (about) 1 package host 2 3 import ( 4 "crypto/cipher" 5 "errors" 6 "net" 7 "time" 8 9 "gitlab.com/NebulousLabs/fastrand" 10 "gitlab.com/SiaPrime/SiaPrime/build" 11 "gitlab.com/SiaPrime/SiaPrime/crypto" 12 "gitlab.com/SiaPrime/SiaPrime/encoding" 13 "gitlab.com/SiaPrime/SiaPrime/modules" 14 "gitlab.com/SiaPrime/SiaPrime/types" 15 "golang.org/x/crypto/chacha20poly1305" 16 ) 17 18 // An rpcSession contains the state of an RPC session with a renter. 19 type rpcSession struct { 20 conn net.Conn 21 aead cipher.AEAD 22 so storageObligation 23 challenge [16]byte 24 } 25 26 // extendDeadline extends the read/write deadline on the underlying connection 27 // by d. 28 func (s *rpcSession) extendDeadline(d time.Duration) { 29 s.conn.SetDeadline(time.Now().Add(d)) 30 } 31 32 // readRequest reads an encrypted RPC request from the renter. 33 func (s *rpcSession) readRequest(resp interface{}, maxLen uint64) error { 34 return modules.ReadRPCRequest(s.conn, s.aead, resp, maxLen) 35 } 36 37 // readResponse reads an encrypted RPC response from the renter. 38 func (s *rpcSession) readResponse(resp interface{}, maxLen uint64) error { 39 return modules.ReadRPCResponse(s.conn, s.aead, resp, maxLen) 40 } 41 42 // writeResponse sends an encrypted RPC response to the renter. 43 func (s *rpcSession) writeResponse(resp interface{}) error { 44 return modules.WriteRPCResponse(s.conn, s.aead, resp, nil) 45 } 46 47 // writeError sends an encrypted RPC error to the renter. 48 func (s *rpcSession) writeError(err error) error { 49 return modules.WriteRPCResponse(s.conn, s.aead, nil, err) 50 } 51 52 // managedRPCLoop reads new RPCs from the renter, each consisting of a single 53 // request and response. The loop terminates when the an RPC encounters an 54 // error or the renter sends modules.RPCLoopExit. 55 func (h *Host) managedRPCLoop(conn net.Conn) error { 56 // read renter's half of key exchange 57 conn.SetDeadline(time.Now().Add(rpcRequestInterval)) 58 var req modules.LoopKeyExchangeRequest 59 if err := encoding.NewDecoder(conn, encoding.DefaultAllocLimit).Decode(&req); err != nil { 60 return err 61 } 62 63 // check for a supported cipher 64 var supportsChaCha bool 65 for _, c := range req.Ciphers { 66 if c == modules.CipherChaCha20Poly1305 { 67 supportsChaCha = true 68 } 69 } 70 if !supportsChaCha { 71 encoding.NewEncoder(conn).Encode(modules.LoopKeyExchangeResponse{ 72 Cipher: modules.CipherNoOverlap, 73 }) 74 return errors.New("no supported ciphers") 75 } 76 77 // generate a session key, sign it, and derive the shared secret 78 xsk, xpk := crypto.GenerateX25519KeyPair() 79 pubkeySig := crypto.SignHash(crypto.HashAll(req.PublicKey, xpk), h.secretKey) 80 cipherKey := crypto.DeriveSharedSecret(xsk, req.PublicKey) 81 82 // send our half of the key exchange 83 resp := modules.LoopKeyExchangeResponse{ 84 Cipher: modules.CipherChaCha20Poly1305, 85 PublicKey: xpk, 86 Signature: pubkeySig[:], 87 } 88 if err := encoding.NewEncoder(conn).Encode(resp); err != nil { 89 return err 90 } 91 92 // use cipherKey to initialize an AEAD cipher 93 aead, err := chacha20poly1305.New(cipherKey[:]) 94 if err != nil { 95 build.Critical("could not create cipher") 96 return err 97 } 98 // create the session object 99 s := &rpcSession{ 100 conn: conn, 101 aead: aead, 102 } 103 fastrand.Read(s.challenge[:]) 104 105 // send encrypted challenge 106 challengeReq := modules.LoopChallengeRequest{ 107 Challenge: s.challenge, 108 } 109 if err := modules.WriteRPCMessage(conn, aead, challengeReq); err != nil { 110 return err 111 } 112 113 // ensure we unlock any locked contracts when protocol ends 114 defer func() { 115 if len(s.so.OriginTransactionSet) != 0 { 116 h.managedUnlockStorageObligation(s.so.id()) 117 } 118 }() 119 120 // enter RPC loop 121 rpcs := map[types.Specifier]func(*rpcSession) error{ 122 modules.RPCLoopLock: h.managedRPCLoopLock, 123 modules.RPCLoopUnlock: h.managedRPCLoopUnlock, 124 modules.RPCLoopSettings: h.managedRPCLoopSettings, 125 modules.RPCLoopFormContract: h.managedRPCLoopFormContract, 126 modules.RPCLoopRenewContract: h.managedRPCLoopRenewContract, 127 modules.RPCLoopWrite: h.managedRPCLoopWrite, 128 modules.RPCLoopRead: h.managedRPCLoopRead, 129 modules.RPCLoopSectorRoots: h.managedRPCLoopSectorRoots, 130 } 131 for { 132 conn.SetDeadline(time.Now().Add(rpcRequestInterval)) 133 id, err := modules.ReadRPCID(conn, aead) 134 if err != nil { 135 h.log.Debugf("WARN: could not read RPC ID: %v", err) 136 s.writeError(err) // try to write, even though this is probably due to a faulty connection 137 return err 138 } else if id == modules.RPCLoopExit { 139 return nil 140 } 141 if rpcFn, ok := rpcs[id]; !ok { 142 return errors.New("invalid or unknown RPC ID: " + id.String()) 143 } else if err := rpcFn(s); err != nil { 144 return extendErr("incoming RPC"+id.String()+" failed: ", err) 145 } 146 } 147 }