github.com/braveheart12/insolar-09-08-19@v0.8.7/network/controller/bootstrap/challenge_response.go (about) 1 /* 2 * The Clear BSD License 3 * 4 * Copyright (c) 2019 Insolar Technologies 5 * 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without modification, are permitted (subject to the limitations in the disclaimer below) provided that the following conditions are met: 9 * 10 * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 11 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 12 * Neither the name of Insolar Technologies nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 13 * 14 * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 15 * 16 */ 17 18 package bootstrap 19 20 import ( 21 "context" 22 "encoding/gob" 23 24 "github.com/insolar/insolar/component" 25 "github.com/insolar/insolar/core" 26 "github.com/insolar/insolar/instrumentation/inslogger" 27 "github.com/insolar/insolar/instrumentation/instracer" 28 "github.com/insolar/insolar/log" 29 "github.com/insolar/insolar/network" 30 "github.com/insolar/insolar/network/controller/common" 31 "github.com/insolar/insolar/network/transport/host" 32 "github.com/insolar/insolar/network/transport/packet/types" 33 base58 "github.com/jbenet/go-base58" 34 "github.com/pkg/errors" 35 ) 36 37 type ChallengeResponseController interface { 38 component.Initer 39 40 Execute(ctx context.Context, discoveryNode *DiscoveryNode, sessionID SessionID) (*ChallengePayload, error) 41 } 42 43 type challengeResponseController struct { 44 SessionManager SessionManager `inject:""` 45 Cryptography core.CryptographyService `inject:""` 46 NodeKeeper network.NodeKeeper `inject:""` 47 48 options *common.Options 49 transport network.InternalTransport 50 } 51 52 type Nonce []byte 53 type SignedNonce []byte 54 55 // Node Discovery Node 56 // 1| ------ ChallengeRequest -----> | 57 // 2| <-- SignedChallengeResponse -- | 58 // 3| --- SignedChallengeRequest --> | 59 // 4| <----- ChallengeResponse ----- | 60 // ------------------------------------ 61 62 type ChallengeResponseHeader struct { 63 Success bool 64 Error string 65 } 66 67 type ChallengeRequest struct { 68 SessionID SessionID 69 70 Nonce Nonce 71 } 72 73 type SignedChallengeResponse struct { 74 Header ChallengeResponseHeader 75 Payload *SignedChallengePayload 76 } 77 78 type SignedChallengePayload struct { 79 SignedNonce SignedNonce 80 XorDiscoveryNonce Nonce 81 DiscoveryNonce Nonce 82 } 83 84 type SignedChallengeRequest struct { 85 SessionID SessionID 86 87 SignedDiscoveryNonce SignedNonce 88 XorNonce Nonce 89 } 90 91 type ChallengeResponse struct { 92 Header ChallengeResponseHeader 93 Payload *ChallengePayload 94 } 95 96 type ChallengePayload struct { 97 // CurrentPulse core.Pulse 98 // State core.NetworkState 99 AssignShortID core.ShortNodeID 100 } 101 102 func init() { 103 gob.Register(&ChallengeRequest{}) 104 gob.Register(&SignedChallengeResponse{}) 105 gob.Register(&SignedChallengeRequest{}) 106 gob.Register(&ChallengeResponse{}) 107 } 108 109 func (cr *challengeResponseController) processChallenge1(ctx context.Context, request network.Request) (network.Response, error) { 110 ctx, span := instracer.StartSpan(ctx, "ChallengeResponseController.processChallenge1") 111 defer span.End() 112 data := request.GetData().(*ChallengeRequest) 113 // CheckSession is performed in SetDiscoveryNonce too, but we want to return early if the request is invalid 114 err := cr.SessionManager.CheckSession(data.SessionID, Authorized) 115 if err != nil { 116 return cr.buildChallenge1ErrorResponse(ctx, request, err.Error()), nil 117 } 118 xorNonce, err := GenerateNonce() 119 if err != nil { 120 return cr.buildChallenge1ErrorResponse(ctx, request, "error generating discovery xor nonce: "+err.Error()), nil 121 } 122 sign, err := cr.Cryptography.Sign(Xor(data.Nonce, xorNonce)) 123 if err != nil { 124 return cr.buildChallenge1ErrorResponse(ctx, request, "error signing nonce: "+err.Error()), nil 125 } 126 discoveryNonce, err := GenerateNonce() 127 if err != nil { 128 return cr.buildChallenge1ErrorResponse(ctx, request, "error generating discovery nonce: "+err.Error()), nil 129 } 130 err = cr.SessionManager.SetDiscoveryNonce(data.SessionID, discoveryNonce) 131 if err != nil { 132 return cr.buildChallenge1ErrorResponse(ctx, request, err.Error()), nil 133 } 134 response := cr.transport.BuildResponse(ctx, request, &SignedChallengeResponse{ 135 Header: ChallengeResponseHeader{ 136 Success: true, 137 }, 138 Payload: &SignedChallengePayload{ 139 SignedNonce: sign.Bytes(), 140 XorDiscoveryNonce: xorNonce, 141 DiscoveryNonce: discoveryNonce, 142 }, 143 }) 144 return response, nil 145 } 146 147 func (cr *challengeResponseController) buildChallenge1ErrorResponse(ctx context.Context, request network.Request, err string) network.Response { 148 log.Warn(err) 149 return cr.transport.BuildResponse(ctx, request, &ChallengeResponse{ 150 Header: ChallengeResponseHeader{ 151 Success: false, 152 Error: err, 153 }, 154 }) 155 } 156 157 func (cr *challengeResponseController) processChallenge2(ctx context.Context, request network.Request) (network.Response, error) { 158 ctx, span := instracer.StartSpan(ctx, "ChallengeResponseController.processChallenge2") 159 defer span.End() 160 data := request.GetData().(*SignedChallengeRequest) 161 cert, discoveryNonce, err := cr.SessionManager.GetChallengeData(data.SessionID) 162 if err != nil { 163 return cr.buildChallenge2ErrorResponse(ctx, request, err.Error()), nil 164 } 165 sign := core.SignatureFromBytes(data.SignedDiscoveryNonce) 166 success := cr.Cryptography.Verify(cert.GetPublicKey(), sign, Xor(data.XorNonce, discoveryNonce)) 167 if !success { 168 return cr.buildChallenge2ErrorResponse(ctx, request, "node %s signature check failed"), nil 169 } 170 err = cr.SessionManager.ChallengePassed(data.SessionID) 171 if err != nil { 172 return cr.buildChallenge2ErrorResponse(ctx, request, err.Error()), nil 173 } 174 response := cr.transport.BuildResponse(ctx, request, &ChallengeResponse{ 175 Header: ChallengeResponseHeader{ 176 Success: true, 177 }, 178 Payload: &ChallengePayload{ 179 AssignShortID: GenerateShortID(cr.NodeKeeper, *cert.GetNodeRef()), 180 }, 181 }) 182 return response, nil 183 } 184 185 func (cr *challengeResponseController) buildChallenge2ErrorResponse(ctx context.Context, request network.Request, err string) network.Response { 186 log.Warn(err) 187 return cr.transport.BuildResponse(ctx, request, &SignedChallengeResponse{ 188 Header: ChallengeResponseHeader{ 189 Success: false, 190 Error: err, 191 }, 192 }) 193 } 194 195 func (cr *challengeResponseController) Init(ctx context.Context) error { 196 cr.transport.RegisterPacketHandler(types.Challenge1, cr.processChallenge1) 197 cr.transport.RegisterPacketHandler(types.Challenge2, cr.processChallenge2) 198 return nil 199 } 200 201 func (cr *challengeResponseController) sendRequest1(ctx context.Context, discoveryHost *host.Host, 202 sessionID SessionID, nonce Nonce) (*SignedChallengePayload, error) { 203 204 ctx, span := instracer.StartSpan(ctx, "ChallengeResponseController.sendRequest1") 205 defer span.End() 206 request := cr.transport.NewRequestBuilder().Type(types.Challenge1).Data(&ChallengeRequest{ 207 SessionID: sessionID, Nonce: nonce}).Build() 208 future, err := cr.transport.SendRequestPacket(ctx, request, discoveryHost) 209 if err != nil { 210 return nil, errors.Wrap(err, "Error sending challenge request") 211 } 212 response, err := future.GetResponse(cr.options.PacketTimeout) 213 if err != nil { 214 return nil, errors.Wrap(err, "Error getting response for challenge request") 215 } 216 data := response.GetData().(*SignedChallengeResponse) 217 if !data.Header.Success { 218 return nil, errors.Wrap(err, "Discovery node returned error for challenge request: "+data.Header.Error) 219 } 220 return data.Payload, nil 221 } 222 223 func (cr *challengeResponseController) sendRequest2(ctx context.Context, discoveryHost *host.Host, 224 sessionID SessionID, signedDiscoveryNonce SignedNonce, xorNonce Nonce) (*ChallengePayload, error) { 225 226 ctx, span := instracer.StartSpan(ctx, "ChallengeResponseController.sendRequest2") 227 defer span.End() 228 request := cr.transport.NewRequestBuilder().Type(types.Challenge2).Data(&SignedChallengeRequest{ 229 SessionID: sessionID, XorNonce: xorNonce, SignedDiscoveryNonce: signedDiscoveryNonce}).Build() 230 future, err := cr.transport.SendRequestPacket(ctx, request, discoveryHost) 231 if err != nil { 232 return nil, errors.Wrap(err, "Error sending challenge request") 233 } 234 response, err := future.GetResponse(cr.options.PacketTimeout) 235 if err != nil { 236 return nil, errors.Wrap(err, "Error getting response for challenge request") 237 } 238 data := response.GetData().(*ChallengeResponse) 239 if !data.Header.Success { 240 return nil, errors.Wrap(err, "Discovery node returned error for challenge request: "+data.Header.Error) 241 } 242 return data.Payload, nil 243 } 244 245 // Execute double challenge response between the node and the discovery node (step 3 of the bootstrap process) 246 func (cr *challengeResponseController) Execute(ctx context.Context, discoveryNode *DiscoveryNode, sessionID SessionID) (*ChallengePayload, error) { 247 ctx, span := instracer.StartSpan(ctx, "ChallengeResponseController.Execute") 248 defer span.End() 249 nonce, err := GenerateNonce() 250 if err != nil { 251 return nil, errors.Wrap(err, "error generating nonce") 252 } 253 inslogger.FromContext(ctx).Debugf("Generated nonce: %s", base58.Encode(nonce)) 254 255 data, err := cr.sendRequest1(ctx, discoveryNode.Host, sessionID, nonce) 256 if err != nil { 257 return nil, errors.Wrap(err, "error executing challenge response (step 1)") 258 } 259 260 inslogger.FromContext(ctx).Debugf("Discovery SignedNonce: %s", base58.Encode(data.SignedNonce)) 261 inslogger.FromContext(ctx).Debugf("Discovery DiscoveryNonce: %s", base58.Encode(data.DiscoveryNonce)) 262 inslogger.FromContext(ctx).Debugf("Discovery XorDiscoveryNonce: %s", base58.Encode(data.XorDiscoveryNonce)) 263 264 sign := core.SignatureFromBytes(data.SignedNonce) 265 success := cr.Cryptography.Verify(discoveryNode.Node.GetPublicKey(), sign, Xor(nonce, data.XorDiscoveryNonce)) 266 if !success { 267 return nil, errors.New("Error checking signed nonce from discovery node") 268 } 269 270 xorNonce, err := GenerateNonce() 271 if err != nil { 272 return nil, errors.Wrap(err, "error generating xor nonce") 273 } 274 signedDiscoveryNonce, err := cr.Cryptography.Sign(Xor(xorNonce, data.DiscoveryNonce)) 275 if err != nil { 276 return nil, errors.Wrap(err, "error signing discovery nonce") 277 } 278 payload, err := cr.sendRequest2(ctx, discoveryNode.Host, sessionID, signedDiscoveryNonce.Bytes(), xorNonce) 279 if err != nil { 280 return nil, errors.Wrap(err, "error executing challenge response (step 2)") 281 } 282 return payload, nil 283 } 284 285 func NewChallengeResponseController(options *common.Options, transport network.InternalTransport) ChallengeResponseController { 286 return &challengeResponseController{ 287 options: options, 288 transport: transport, 289 } 290 }