Просмотр исходного кода

Client verification with server nonce and cached attestation TTL

Eugene Fryntov 9 лет назад
Родитель
Сommit
135bb89330
3 измененных файлов с 53 добавлено и 23 удалено
  1. 5 3
      psiphon/notice.go
  2. 47 17
      psiphon/serverApi.go
  3. 1 3
      psiphon/tunnel.go

+ 5 - 3
psiphon/notice.go

@@ -214,9 +214,11 @@ func NoticeHomepage(url string) {
 }
 
 // NoticeClientVerificationRequired indicates that client verification is required, as
-// indicated bythe handshake. The client should submit a client verification payload.
-func NoticeClientVerificationRequired() {
-	outputNotice("ClientVerificationRequired", 0)
+// indicated by the handshake. The client should submit a client verification payload.
+// Empty nonce is allowed, if ttlSeconds is 0 the client should not send verification
+// payload to the server.
+func NoticeClientVerificationRequired(nonce string, ttlSeconds int) {
+	outputNotice("ClientVerificationRequired", 0, "nonce", nonce, "ttlSeconds", ttlSeconds)
 }
 
 // NoticeClientRegion is the client's region, as determined by the server and

+ 47 - 17
psiphon/serverApi.go

@@ -173,14 +173,16 @@ func (serverContext *ServerContext) doHandshakeRequest() error {
 	// - 'preemptive_reconnect_lifetime_milliseconds' is currently unused
 	// - 'ssh_session_id' is ignored; client session ID is used instead
 	var handshakeResponse struct {
-		Homepages                  []string            `json:"homepages"`
-		UpgradeClientVersion       string              `json:"upgrade_client_version"`
-		PageViewRegexes            []map[string]string `json:"page_view_regexes"`
-		HttpsRequestRegexes        []map[string]string `json:"https_request_regexes"`
-		EncodedServerList          []string            `json:"encoded_server_list"`
-		ClientRegion               string              `json:"client_region"`
-		ServerTimestamp            string              `json:"server_timestamp"`
-		ClientVerificationRequired bool                `json:"client_verification_required"`
+		Homepages                    []string            `json:"homepages"`
+		UpgradeClientVersion         string              `json:"upgrade_client_version"`
+		PageViewRegexes              []map[string]string `json:"page_view_regexes"`
+		HttpsRequestRegexes          []map[string]string `json:"https_request_regexes"`
+		EncodedServerList            []string            `json:"encoded_server_list"`
+		ClientRegion                 string              `json:"client_region"`
+		ServerTimestamp              string              `json:"server_timestamp"`
+		ClientVerificationRequired   bool                `json:"client_verification_required"`
+		ClientVerificationNonce      string              `json:"client_verification_nonce"`
+		ClientVerificationTTLSeconds int                 `json:"client_verification_ttl_seconds"`
 	}
 	err := json.Unmarshal(response, &handshakeResponse)
 	if err != nil {
@@ -247,7 +249,8 @@ func (serverContext *ServerContext) doHandshakeRequest() error {
 	serverContext.serverHandshakeTimestamp = handshakeResponse.ServerTimestamp
 
 	if handshakeResponse.ClientVerificationRequired {
-		NoticeClientVerificationRequired()
+		NoticeClientVerificationRequired(handshakeResponse.ClientVerificationNonce,
+			handshakeResponse.ClientVerificationTTLSeconds)
 	}
 
 	return nil
@@ -350,7 +353,7 @@ func (serverContext *ServerContext) DoStatusRequest(tunnel *Tunnel) error {
 	} else {
 
 		// Legacy web service API request
-		err = serverContext.doPostRequest(
+		_, err = serverContext.doPostRequest(
 			makeRequestUrl(serverContext.tunnel, "", "status", params),
 			"application/json",
 			bytes.NewReader(statusPayload))
@@ -641,9 +644,11 @@ func RecordTunnelStats(
 // traffic. The proof-of-validity is platform-specific and the payload
 // is opaque to this function but assumed to be JSON.
 func (serverContext *ServerContext) DoClientVerificationRequest(
-	verificationPayload string) error {
+	verificationPayload string, serverIP string) error {
 
 	params := serverContext.getBaseParams()
+	var response []byte
+	var err error
 
 	if serverContext.psiphonHttpsClient == nil {
 
@@ -655,7 +660,7 @@ func (serverContext *ServerContext) DoClientVerificationRequest(
 			return ContextError(err)
 		}
 
-		_, err = serverContext.tunnel.SendAPIRequest(
+		response, err = serverContext.tunnel.SendAPIRequest(
 			SERVER_API_CLIENT_VERIFICATION_REQUEST_NAME, request)
 		if err != nil {
 			return ContextError(err)
@@ -664,7 +669,7 @@ func (serverContext *ServerContext) DoClientVerificationRequest(
 	} else {
 
 		// Legacy web service API request
-		err := serverContext.doPostRequest(
+		response, err = serverContext.doPostRequest(
 			makeRequestUrl(serverContext.tunnel, "", "client_verification", params),
 			"application/json",
 			bytes.NewReader([]byte(verificationPayload)))
@@ -673,6 +678,27 @@ func (serverContext *ServerContext) DoClientVerificationRequest(
 		}
 	}
 
+	// Server may request a new verification to be performed,
+	// for example, if the payload timestamp is too old, etc.
+
+	var clientVerificationResponse struct {
+		ClientVerificationNonce      string `json:"client_verification_nonce"`
+		ClientVerificationTTLSeconds int    `json:"client_verification_ttl_seconds"`
+	}
+
+	err = json.Unmarshal(response, &clientVerificationResponse)
+	if err != nil {
+		return ContextError(err)
+	}
+
+	if clientVerificationResponse.ClientVerificationTTLSeconds > 0 {
+		NoticeClientVerificationRequired(
+			clientVerificationResponse.ClientVerificationNonce,
+			clientVerificationResponse.ClientVerificationTTLSeconds)
+	} else {
+		NoticeClientVerificationRequestCompleted(serverIP)
+	}
+
 	return nil
 }
 
@@ -699,7 +725,7 @@ func (serverContext *ServerContext) doGetRequest(
 
 // doPostRequest makes a tunneled HTTPS POST request.
 func (serverContext *ServerContext) doPostRequest(
-	requestUrl string, bodyType string, body io.Reader) (err error) {
+	requestUrl string, bodyType string, body io.Reader) (responseBody []byte, err error) {
 
 	response, err := serverContext.psiphonHttpsClient.Post(requestUrl, bodyType, body)
 	if err == nil && response.StatusCode != http.StatusOK {
@@ -708,10 +734,14 @@ func (serverContext *ServerContext) doPostRequest(
 	}
 	if err != nil {
 		// Trim this error since it may include long URLs
-		return ContextError(TrimError(err))
+		return nil, ContextError(TrimError(err))
 	}
-	response.Body.Close()
-	return nil
+	defer response.Body.Close()
+	responseBody, err = ioutil.ReadAll(response.Body)
+	if err != nil {
+		return nil, ContextError(err)
+	}
+	return responseBody, nil
 }
 
 type requestJSONObject map[string]interface{}

+ 1 - 3
psiphon/tunnel.go

@@ -1103,11 +1103,9 @@ func sendClientVerification(tunnel *Tunnel, clientVerificationPayload string) bo
 		return true
 	}
 
-	err := tunnel.serverContext.DoClientVerificationRequest(clientVerificationPayload)
+	err := tunnel.serverContext.DoClientVerificationRequest(clientVerificationPayload, tunnel.serverEntry.IpAddress)
 	if err != nil {
 		NoticeAlert("DoClientVerificationRequest failed for %s: %s", tunnel.serverEntry.IpAddress, err)
-	} else {
-		NoticeClientVerificationRequestCompleted(tunnel.serverEntry.IpAddress)
 	}
 
 	return err == nil