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

Removed SafetyNet verification API

Eugene Fryntov 7 лет назад
Родитель
Сommit
3bbc7a5a12

+ 0 - 13
MobileLibrary/Android/PsiphonTunnel/PsiphonTunnel.java

@@ -91,7 +91,6 @@ public class PsiphonTunnel extends Psi.PsiphonProvider.Stub {
         public void onUntunneledAddress(String address);
         public void onBytesTransferred(long sent, long received);
         public void onStartedWaitingForNetworkConnectivity();
-        public void onClientVerificationRequired(String serverNonce, int ttlSeconds, boolean resetCache);
         public void onActiveAuthorizationIDs(List<String> authorizations);
         public void onExiting();
     }
@@ -186,15 +185,6 @@ public class PsiphonTunnel extends Psi.PsiphonProvider.Stub {
         startPsiphon("");
     }
 
-    // Call through to tunnel-core Controller.SetClientVerificationPayload. See description in
-    // Controller.SetClientVerificationPayload.
-    // Note: same deadlock note as stop().
-    // Note: this function only has an effect after Psi.Start() and before Psi.Stop(),
-    // so call it after startTunneling() and before stop().
-    public synchronized void setClientVerificationPayload (String requestPayload) {
-        Psi.SetClientVerificationPayload(requestPayload);
-    }
-
     //----------------------------------------------------------------------------------------------
     // VPN Routing
     //----------------------------------------------------------------------------------------------
@@ -629,9 +619,6 @@ public class PsiphonTunnel extends Psi.PsiphonProvider.Stub {
                 mHostService.onActiveAuthorizationIDs(authorizations);
             } else if (noticeType.equals("Exiting")) {
                 mHostService.onExiting();
-            } else if(noticeType.equals("ClientVerificationRequired")) {
-                JSONObject data = notice.getJSONObject("data");
-                mHostService.onClientVerificationRequired(data.getString("nonce"), data.getInt("ttlSeconds"), data.getBoolean("resetCache"));
             } else if (noticeType.equals("ActiveTunnel")) {
                 if (isVpnMode()) {
                     if (notice.getJSONObject("data").getBoolean("isTCS")) {

+ 0 - 5
MobileLibrary/Android/SampleApps/TunneledWebView/app/src/main/java/ca/psiphon/tunneledwebview/MainActivity.java

@@ -282,11 +282,6 @@ public class MainActivity extends ActionBarActivity
         logMessage("waiting for network connectivity...");
     }
 
-    @Override
-    public void onClientVerificationRequired(String s, int i, boolean b) {
-
-    }
-
     @Override
     public void onActiveAuthorizationIDs(List<String> authorizations) {
 

+ 2 - 15
MobileLibrary/psi/psi.go

@@ -31,11 +31,12 @@ import (
 	"strings"
 	"sync"
 
+	"os"
+
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/protocol"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/tun"
-	"os"
 )
 
 type PsiphonProvider interface {
@@ -183,20 +184,6 @@ func ReconnectTunnel() {
 	}
 }
 
-// SetClientVerificationPayload is a passthrough to
-// Controller.SetClientVerificationPayloadForActiveTunnels.
-// Note: should only be called after Start() and before Stop(); otherwise,
-// will silently take no action.
-func SetClientVerificationPayload(clientVerificationPayload string) {
-
-	controllerMutex.Lock()
-	defer controllerMutex.Unlock()
-
-	if controller != nil {
-		controller.SetClientVerificationPayloadForActiveTunnels(clientVerificationPayload)
-	}
-}
-
 // Encrypt and upload feedback.
 func SendFeedback(configJson, diagnosticsJson, b64EncodedPublicKey, uploadServer, uploadPath, uploadServerHeaders string) error {
 	return psiphon.SendFeedback(configJson, diagnosticsJson, b64EncodedPublicKey, uploadServer, uploadPath, uploadServerHeaders)

+ 87 - 92
psiphon/common/parameters/clientParameters.go

@@ -65,95 +65,93 @@ import (
 )
 
 const (
-	NetworkLatencyMultiplier                       = "NetworkLatencyMultiplier"
-	TacticsWaitPeriod                              = "TacticsWaitPeriod"
-	TacticsRetryPeriod                             = "TacticsRetryPeriod"
-	TacticsRetryPeriodJitter                       = "TacticsRetryPeriodJitter"
-	TacticsTimeout                                 = "TacticsTimeout"
-	ConnectionWorkerPoolSize                       = "ConnectionWorkerPoolSize"
-	TunnelConnectTimeout                           = "TunnelConnectTimeout"
-	EstablishTunnelTimeout                         = "EstablishTunnelTimeout"
-	EstablishTunnelWorkTime                        = "EstablishTunnelWorkTime"
-	EstablishTunnelPausePeriod                     = "EstablishTunnelPausePeriod"
-	EstablishTunnelPausePeriodJitter               = "EstablishTunnelPausePeriodJitter"
-	EstablishTunnelServerAffinityGracePeriod       = "EstablishTunnelServerAffinityGracePeriod"
-	StaggerConnectionWorkersPeriod                 = "StaggerConnectionWorkersPeriod"
-	StaggerConnectionWorkersJitter                 = "StaggerConnectionWorkersJitter"
-	LimitMeekConnectionWorkers                     = "LimitMeekConnectionWorkers"
-	IgnoreHandshakeStatsRegexps                    = "IgnoreHandshakeStatsRegexps"
-	PrioritizeTunnelProtocols                      = "PrioritizeTunnelProtocols"
-	PrioritizeTunnelProtocolsCandidateCount        = "PrioritizeTunnelProtocolsCandidateCount"
-	LimitTunnelProtocols                           = "LimitTunnelProtocols"
-	TunnelOperateShutdownTimeout                   = "TunnelOperateShutdownTimeout"
-	TunnelPortForwardDialTimeout                   = "TunnelPortForwardDialTimeout"
-	TunnelRateLimits                               = "TunnelRateLimits"
-	AdditionalCustomHeaders                        = "AdditionalCustomHeaders"
-	SpeedTestPaddingMinBytes                       = "SpeedTestPaddingMinBytes"
-	SpeedTestPaddingMaxBytes                       = "SpeedTestPaddingMaxBytes"
-	SpeedTestMaxSampleCount                        = "SpeedTestMaxSampleCount"
-	SSHKeepAliveSpeedTestSampleProbability         = "SSHKeepAliveSpeedTestSampleProbability"
-	SSHKeepAlivePaddingMinBytes                    = "SSHKeepAlivePaddingMinBytes"
-	SSHKeepAlivePaddingMaxBytes                    = "SSHKeepAlivePaddingMaxBytes"
-	SSHKeepAlivePeriodMin                          = "SSHKeepAlivePeriodMin"
-	SSHKeepAlivePeriodMax                          = "SSHKeepAlivePeriodMax"
-	SSHKeepAlivePeriodicTimeout                    = "SSHKeepAlivePeriodicTimeout"
-	SSHKeepAlivePeriodicInactivePeriod             = "SSHKeepAlivePeriodicInactivePeriod"
-	SSHKeepAliveProbeTimeout                       = "SSHKeepAliveProbeTimeout"
-	SSHKeepAliveProbeInactivePeriod                = "SSHKeepAliveProbeInactivePeriod"
-	HTTPProxyOriginServerTimeout                   = "HTTPProxyOriginServerTimeout"
-	HTTPProxyMaxIdleConnectionsPerHost             = "HTTPProxyMaxIdleConnectionsPerHost"
-	FetchRemoteServerListTimeout                   = "FetchRemoteServerListTimeout"
-	FetchRemoteServerListRetryPeriod               = "FetchRemoteServerListRetryPeriod"
-	FetchRemoteServerListStalePeriod               = "FetchRemoteServerListStalePeriod"
-	RemoteServerListSignaturePublicKey             = "RemoteServerListSignaturePublicKey"
-	RemoteServerListURLs                           = "RemoteServerListURLs"
-	ObfuscatedServerListRootURLs                   = "ObfuscatedServerListRootURLs"
-	PsiphonAPIRequestTimeout                       = "PsiphonAPIRequestTimeout"
-	PsiphonAPIStatusRequestPeriodMin               = "PsiphonAPIStatusRequestPeriodMin"
-	PsiphonAPIStatusRequestPeriodMax               = "PsiphonAPIStatusRequestPeriodMax"
-	PsiphonAPIStatusRequestShortPeriodMin          = "PsiphonAPIStatusRequestShortPeriodMin"
-	PsiphonAPIStatusRequestShortPeriodMax          = "PsiphonAPIStatusRequestShortPeriodMax"
-	PsiphonAPIStatusRequestPaddingMinBytes         = "PsiphonAPIStatusRequestPaddingMinBytes"
-	PsiphonAPIStatusRequestPaddingMaxBytes         = "PsiphonAPIStatusRequestPaddingMaxBytes"
-	PsiphonAPIPersistentStatsMaxCount              = "PsiphonAPIPersistentStatsMaxCount"
-	PsiphonAPIConnectedRequestPeriod               = "PsiphonAPIConnectedRequestPeriod"
-	PsiphonAPIConnectedRequestRetryPeriod          = "PsiphonAPIConnectedRequestRetryPeriod"
-	PsiphonAPIClientVerificationRequestRetryPeriod = "PsiphonAPIClientVerificationRequestRetryPeriod"
-	PsiphonAPIClientVerificationRequestMaxRetries  = "PsiphonAPIClientVerificationRequestMaxRetries"
-	FetchSplitTunnelRoutesTimeout                  = "FetchSplitTunnelRoutesTimeout"
-	SplitTunnelRoutesURLFormat                     = "SplitTunnelRoutesURLFormat"
-	SplitTunnelRoutesSignaturePublicKey            = "SplitTunnelRoutesSignaturePublicKey"
-	SplitTunnelDNSServer                           = "SplitTunnelDNSServer"
-	FetchUpgradeTimeout                            = "FetchUpgradeTimeout"
-	FetchUpgradeRetryPeriod                        = "FetchUpgradeRetryPeriod"
-	FetchUpgradeStalePeriod                        = "FetchUpgradeStalePeriod"
-	UpgradeDownloadURLs                            = "UpgradeDownloadURLs"
-	UpgradeDownloadClientVersionHeader             = "UpgradeDownloadClientVersionHeader"
-	ImpairedProtocolClassificationDuration         = "ImpairedProtocolClassificationDuration"
-	ImpairedProtocolClassificationThreshold        = "ImpairedProtocolClassificationThreshold"
-	TotalBytesTransferredNoticePeriod              = "TotalBytesTransferredNoticePeriod"
-	MeekDialDomainsOnly                            = "MeekDialDomainsOnly"
-	MeekLimitBufferSizes                           = "MeekLimitBufferSizes"
-	MeekCookieMaxPadding                           = "MeekCookieMaxPadding"
-	MeekFullReceiveBufferLength                    = "MeekFullReceiveBufferLength"
-	MeekReadPayloadChunkLength                     = "MeekReadPayloadChunkLength"
-	MeekLimitedFullReceiveBufferLength             = "MeekLimitedFullReceiveBufferLength"
-	MeekLimitedReadPayloadChunkLength              = "MeekLimitedReadPayloadChunkLength"
-	MeekMinPollInterval                            = "MeekMinPollInterval"
-	MeekMinPollIntervalJitter                      = "MeekMinPollIntervalJitter"
-	MeekMaxPollInterval                            = "MeekMaxPollInterval"
-	MeekMaxPollIntervalJitter                      = "MeekMaxPollIntervalJitter"
-	MeekPollIntervalMultiplier                     = "MeekPollIntervalMultiplier"
-	MeekPollIntervalJitter                         = "MeekPollIntervalJitter"
-	MeekApplyPollIntervalMultiplierProbability     = "MeekApplyPollIntervalMultiplierProbability"
-	MeekRoundTripRetryDeadline                     = "MeekRoundTripRetryDeadline"
-	MeekRoundTripRetryMinDelay                     = "MeekRoundTripRetryMinDelay"
-	MeekRoundTripRetryMaxDelay                     = "MeekRoundTripRetryMaxDelay"
-	MeekRoundTripRetryMultiplier                   = "MeekRoundTripRetryMultiplier"
-	MeekRoundTripTimeout                           = "MeekRoundTripTimeout"
-	SelectAndroidTLSProbability                    = "SelectAndroidTLSProbability"
-	TransformHostNameProbability                   = "TransformHostNameProbability"
-	PickUserAgentProbability                       = "PickUserAgentProbability"
+	NetworkLatencyMultiplier                   = "NetworkLatencyMultiplier"
+	TacticsWaitPeriod                          = "TacticsWaitPeriod"
+	TacticsRetryPeriod                         = "TacticsRetryPeriod"
+	TacticsRetryPeriodJitter                   = "TacticsRetryPeriodJitter"
+	TacticsTimeout                             = "TacticsTimeout"
+	ConnectionWorkerPoolSize                   = "ConnectionWorkerPoolSize"
+	TunnelConnectTimeout                       = "TunnelConnectTimeout"
+	EstablishTunnelTimeout                     = "EstablishTunnelTimeout"
+	EstablishTunnelWorkTime                    = "EstablishTunnelWorkTime"
+	EstablishTunnelPausePeriod                 = "EstablishTunnelPausePeriod"
+	EstablishTunnelPausePeriodJitter           = "EstablishTunnelPausePeriodJitter"
+	EstablishTunnelServerAffinityGracePeriod   = "EstablishTunnelServerAffinityGracePeriod"
+	StaggerConnectionWorkersPeriod             = "StaggerConnectionWorkersPeriod"
+	StaggerConnectionWorkersJitter             = "StaggerConnectionWorkersJitter"
+	LimitMeekConnectionWorkers                 = "LimitMeekConnectionWorkers"
+	IgnoreHandshakeStatsRegexps                = "IgnoreHandshakeStatsRegexps"
+	PrioritizeTunnelProtocols                  = "PrioritizeTunnelProtocols"
+	PrioritizeTunnelProtocolsCandidateCount    = "PrioritizeTunnelProtocolsCandidateCount"
+	LimitTunnelProtocols                       = "LimitTunnelProtocols"
+	TunnelOperateShutdownTimeout               = "TunnelOperateShutdownTimeout"
+	TunnelPortForwardDialTimeout               = "TunnelPortForwardDialTimeout"
+	TunnelRateLimits                           = "TunnelRateLimits"
+	AdditionalCustomHeaders                    = "AdditionalCustomHeaders"
+	SpeedTestPaddingMinBytes                   = "SpeedTestPaddingMinBytes"
+	SpeedTestPaddingMaxBytes                   = "SpeedTestPaddingMaxBytes"
+	SpeedTestMaxSampleCount                    = "SpeedTestMaxSampleCount"
+	SSHKeepAliveSpeedTestSampleProbability     = "SSHKeepAliveSpeedTestSampleProbability"
+	SSHKeepAlivePaddingMinBytes                = "SSHKeepAlivePaddingMinBytes"
+	SSHKeepAlivePaddingMaxBytes                = "SSHKeepAlivePaddingMaxBytes"
+	SSHKeepAlivePeriodMin                      = "SSHKeepAlivePeriodMin"
+	SSHKeepAlivePeriodMax                      = "SSHKeepAlivePeriodMax"
+	SSHKeepAlivePeriodicTimeout                = "SSHKeepAlivePeriodicTimeout"
+	SSHKeepAlivePeriodicInactivePeriod         = "SSHKeepAlivePeriodicInactivePeriod"
+	SSHKeepAliveProbeTimeout                   = "SSHKeepAliveProbeTimeout"
+	SSHKeepAliveProbeInactivePeriod            = "SSHKeepAliveProbeInactivePeriod"
+	HTTPProxyOriginServerTimeout               = "HTTPProxyOriginServerTimeout"
+	HTTPProxyMaxIdleConnectionsPerHost         = "HTTPProxyMaxIdleConnectionsPerHost"
+	FetchRemoteServerListTimeout               = "FetchRemoteServerListTimeout"
+	FetchRemoteServerListRetryPeriod           = "FetchRemoteServerListRetryPeriod"
+	FetchRemoteServerListStalePeriod           = "FetchRemoteServerListStalePeriod"
+	RemoteServerListSignaturePublicKey         = "RemoteServerListSignaturePublicKey"
+	RemoteServerListURLs                       = "RemoteServerListURLs"
+	ObfuscatedServerListRootURLs               = "ObfuscatedServerListRootURLs"
+	PsiphonAPIRequestTimeout                   = "PsiphonAPIRequestTimeout"
+	PsiphonAPIStatusRequestPeriodMin           = "PsiphonAPIStatusRequestPeriodMin"
+	PsiphonAPIStatusRequestPeriodMax           = "PsiphonAPIStatusRequestPeriodMax"
+	PsiphonAPIStatusRequestShortPeriodMin      = "PsiphonAPIStatusRequestShortPeriodMin"
+	PsiphonAPIStatusRequestShortPeriodMax      = "PsiphonAPIStatusRequestShortPeriodMax"
+	PsiphonAPIStatusRequestPaddingMinBytes     = "PsiphonAPIStatusRequestPaddingMinBytes"
+	PsiphonAPIStatusRequestPaddingMaxBytes     = "PsiphonAPIStatusRequestPaddingMaxBytes"
+	PsiphonAPIPersistentStatsMaxCount          = "PsiphonAPIPersistentStatsMaxCount"
+	PsiphonAPIConnectedRequestPeriod           = "PsiphonAPIConnectedRequestPeriod"
+	PsiphonAPIConnectedRequestRetryPeriod      = "PsiphonAPIConnectedRequestRetryPeriod"
+	FetchSplitTunnelRoutesTimeout              = "FetchSplitTunnelRoutesTimeout"
+	SplitTunnelRoutesURLFormat                 = "SplitTunnelRoutesURLFormat"
+	SplitTunnelRoutesSignaturePublicKey        = "SplitTunnelRoutesSignaturePublicKey"
+	SplitTunnelDNSServer                       = "SplitTunnelDNSServer"
+	FetchUpgradeTimeout                        = "FetchUpgradeTimeout"
+	FetchUpgradeRetryPeriod                    = "FetchUpgradeRetryPeriod"
+	FetchUpgradeStalePeriod                    = "FetchUpgradeStalePeriod"
+	UpgradeDownloadURLs                        = "UpgradeDownloadURLs"
+	UpgradeDownloadClientVersionHeader         = "UpgradeDownloadClientVersionHeader"
+	ImpairedProtocolClassificationDuration     = "ImpairedProtocolClassificationDuration"
+	ImpairedProtocolClassificationThreshold    = "ImpairedProtocolClassificationThreshold"
+	TotalBytesTransferredNoticePeriod          = "TotalBytesTransferredNoticePeriod"
+	MeekDialDomainsOnly                        = "MeekDialDomainsOnly"
+	MeekLimitBufferSizes                       = "MeekLimitBufferSizes"
+	MeekCookieMaxPadding                       = "MeekCookieMaxPadding"
+	MeekFullReceiveBufferLength                = "MeekFullReceiveBufferLength"
+	MeekReadPayloadChunkLength                 = "MeekReadPayloadChunkLength"
+	MeekLimitedFullReceiveBufferLength         = "MeekLimitedFullReceiveBufferLength"
+	MeekLimitedReadPayloadChunkLength          = "MeekLimitedReadPayloadChunkLength"
+	MeekMinPollInterval                        = "MeekMinPollInterval"
+	MeekMinPollIntervalJitter                  = "MeekMinPollIntervalJitter"
+	MeekMaxPollInterval                        = "MeekMaxPollInterval"
+	MeekMaxPollIntervalJitter                  = "MeekMaxPollIntervalJitter"
+	MeekPollIntervalMultiplier                 = "MeekPollIntervalMultiplier"
+	MeekPollIntervalJitter                     = "MeekPollIntervalJitter"
+	MeekApplyPollIntervalMultiplierProbability = "MeekApplyPollIntervalMultiplierProbability"
+	MeekRoundTripRetryDeadline                 = "MeekRoundTripRetryDeadline"
+	MeekRoundTripRetryMinDelay                 = "MeekRoundTripRetryMinDelay"
+	MeekRoundTripRetryMaxDelay                 = "MeekRoundTripRetryMaxDelay"
+	MeekRoundTripRetryMultiplier               = "MeekRoundTripRetryMultiplier"
+	MeekRoundTripTimeout                       = "MeekRoundTripTimeout"
+	SelectAndroidTLSProbability                = "SelectAndroidTLSProbability"
+	TransformHostNameProbability               = "TransformHostNameProbability"
+	PickUserAgentProbability                   = "PickUserAgentProbability"
 )
 
 const (
@@ -256,9 +254,6 @@ var defaultClientParameters = map[string]struct {
 
 	PsiphonAPIConnectedRequestRetryPeriod: {value: 5 * time.Second, minimum: 1 * time.Millisecond},
 
-	PsiphonAPIClientVerificationRequestRetryPeriod: {value: 5 * time.Second, minimum: 1 * time.Millisecond},
-	PsiphonAPIClientVerificationRequestMaxRetries:  {value: 10, minimum: 0},
-
 	FetchSplitTunnelRoutesTimeout:       {value: 60 * time.Second, minimum: 1 * time.Second, flags: useNetworkLatencyMultiplier},
 	SplitTunnelRoutesURLFormat:          {value: ""},
 	SplitTunnelRoutesSignaturePublicKey: {value: ""},

+ 4 - 5
psiphon/common/protocol/protocol.go

@@ -47,11 +47,10 @@ const (
 
 	CLIENT_CAPABILITY_SERVER_REQUESTS = "server-requests"
 
-	PSIPHON_API_HANDSHAKE_REQUEST_NAME           = "psiphon-handshake"
-	PSIPHON_API_CONNECTED_REQUEST_NAME           = "psiphon-connected"
-	PSIPHON_API_STATUS_REQUEST_NAME              = "psiphon-status"
-	PSIPHON_API_CLIENT_VERIFICATION_REQUEST_NAME = "psiphon-client-verification"
-	PSIPHON_API_OSL_REQUEST_NAME                 = "psiphon-osl"
+	PSIPHON_API_HANDSHAKE_REQUEST_NAME = "psiphon-handshake"
+	PSIPHON_API_CONNECTED_REQUEST_NAME = "psiphon-connected"
+	PSIPHON_API_STATUS_REQUEST_NAME    = "psiphon-status"
+	PSIPHON_API_OSL_REQUEST_NAME       = "psiphon-osl"
 
 	PSIPHON_API_CLIENT_SESSION_ID_LENGTH = 16
 

+ 0 - 45
psiphon/controller.go

@@ -74,7 +74,6 @@ type Controller struct {
 	impairedProtocolClassification     map[string]int
 	signalReportConnected              chan struct{}
 	serverAffinityDoneBroadcast        chan struct{}
-	newClientVerificationPayload       chan string
 	packetTunnelClient                 *tun.Client
 	packetTunnelTransport              *PacketTunnelTransport
 }
@@ -129,9 +128,6 @@ func NewController(config *Config) (controller *Controller, err error) {
 		signalFetchObfuscatedServerLists:  make(chan struct{}),
 		signalDownloadUpgrade:             make(chan string),
 		signalReportConnected:             make(chan struct{}),
-		// Buffer allows SetClientVerificationPayloadForActiveTunnels to submit one
-		// new payload without blocking or dropping it.
-		newClientVerificationPayload: make(chan string, 1),
 	}
 
 	controller.splitTunnelClassifier = NewSplitTunnelClassifier(config, controller)
@@ -279,29 +275,6 @@ func (controller *Controller) SignalComponentFailure() {
 	controller.stopRunning()
 }
 
-// SetClientVerificationPayloadForActiveTunnels sets the client verification
-// payload that is to be sent in client verification requests to all established
-// tunnels.
-//
-// Client verification is used to verify that the client is a
-// valid Psiphon client, which will determine how the server treats
-// the client traffic. The proof-of-validity is platform-specific
-// and the payload is opaque to this function but assumed to be JSON.
-//
-// Since, in some cases, verification payload cannot be determined until
-// after tunnel-core starts, the payload cannot be simply specified in
-// the Config.
-//
-// SetClientVerificationPayloadForActiveTunnels will not block enqueuing a new verification
-// payload. One new payload can be enqueued, after which additional payloads
-// will be dropped if a payload is still enqueued.
-func (controller *Controller) SetClientVerificationPayloadForActiveTunnels(clientVerificationPayload string) {
-	select {
-	case controller.newClientVerificationPayload <- clientVerificationPayload:
-	default:
-	}
-}
-
 // remoteServerListFetcher fetches an out-of-band list of server entries
 // for more tunnel candidates. It fetches when signalled, with retries
 // on failure.
@@ -587,8 +560,6 @@ downloadLoop:
 func (controller *Controller) runTunnels() {
 	defer controller.runWaitGroup.Done()
 
-	var clientVerificationPayload string
-
 	// Start running
 
 	controller.startEstablishing()
@@ -764,9 +735,6 @@ loop:
 				controller.stopEstablishing()
 			}
 
-		case clientVerificationPayload = <-controller.newClientVerificationPayload:
-			controller.setClientVerificationPayloadForActiveTunnels(clientVerificationPayload)
-
 		case <-controller.runCtx.Done():
 			break loop
 		}
@@ -1046,19 +1014,6 @@ func (controller *Controller) isActiveTunnelServerEntry(
 	return false
 }
 
-// setClientVerificationPayloadForActiveTunnels triggers the client verification
-// request for all active tunnels.
-func (controller *Controller) setClientVerificationPayloadForActiveTunnels(
-	clientVerificationPayload string) {
-
-	controller.tunnelMutex.Lock()
-	defer controller.tunnelMutex.Unlock()
-
-	for _, activeTunnel := range controller.tunnels {
-		activeTunnel.SetClientVerificationPayload(clientVerificationPayload)
-	}
-}
-
 // Dial selects an active tunnel and establishes a port forward
 // connection through the selected tunnel. Failure to connect is considered
 // a port forward failure, for the purpose of monitoring tunnel health.

+ 0 - 20
psiphon/notice.go

@@ -542,19 +542,6 @@ func NoticeHomepages(urls []string) {
 	}
 }
 
-// NoticeClientVerificationRequired indicates that client verification is required, as
-// 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. If resetCache is set the client must always perform a new
-// verification and update its cache
-func NoticeClientVerificationRequired(nonce string, ttlSeconds int, resetCache bool) {
-	singletonNoticeLogger.outputNotice(
-		"ClientVerificationRequired", 0,
-		"nonce", nonce,
-		"ttlSeconds", ttlSeconds,
-		"resetCache", resetCache)
-}
-
 // NoticeClientRegion is the client's region, as determined by the server and
 // reported to the client in the handshake.
 func NoticeClientRegion(region string) {
@@ -701,13 +688,6 @@ func NoticeRemoteServerListResourceDownloaded(url string) {
 		"url", url)
 }
 
-func NoticeClientVerificationRequestCompleted(ipAddress string) {
-	// TODO: remove "Notice" prefix
-	singletonNoticeLogger.outputNotice(
-		"NoticeClientVerificationRequestCompleted", noticeIsDiagnostic,
-		"ipAddress", ipAddress)
-}
-
 // NoticeSLOKSeeded indicates that the SLOK with the specified ID was received from
 // the Psiphon server. The "duplicate" flags indicates whether the SLOK was previously known.
 func NoticeSLOKSeeded(slokID string, duplicate bool) {

+ 0 - 72
psiphon/server/api.go

@@ -39,15 +39,11 @@ import (
 const (
 	MAX_API_PARAMS_SIZE = 256 * 1024 // 256KB
 
-	CLIENT_VERIFICATION_TTL_SECONDS = 60 * 60 * 24 * 7 // 7 days
-
 	CLIENT_PLATFORM_ANDROID = "Android"
 	CLIENT_PLATFORM_WINDOWS = "Windows"
 	CLIENT_PLATFORM_IOS     = "iOS"
 )
 
-var CLIENT_VERIFICATION_REQUIRED = false
-
 // sshAPIRequestHandler routes Psiphon API requests transported as
 // JSON objects via the SSH request mechanism.
 //
@@ -169,8 +165,6 @@ func dispatchAPIRequestHandler(
 		return connectedAPIRequestHandler(support, geoIPData, authorizedAccessTypes, params)
 	case protocol.PSIPHON_API_STATUS_REQUEST_NAME:
 		return statusAPIRequestHandler(support, geoIPData, authorizedAccessTypes, params)
-	case protocol.PSIPHON_API_CLIENT_VERIFICATION_REQUEST_NAME:
-		return clientVerificationAPIRequestHandler(support, geoIPData, authorizedAccessTypes, params)
 	}
 
 	return nil, common.ContextError(fmt.Errorf("invalid request name: %s", name))
@@ -467,72 +461,6 @@ func statusAPIRequestHandler(
 	return make([]byte, 0), nil
 }
 
-// clientVerificationAPIRequestHandler implements the
-// "client verification" API request. Clients make the client
-// verification request once per tunnel connection. The payload
-// attests that client is a legitimate Psiphon client.
-func clientVerificationAPIRequestHandler(
-	support *SupportServices,
-	geoIPData GeoIPData,
-	authorizedAccessTypes []string,
-	params common.APIParameters) ([]byte, error) {
-
-	err := validateRequestParams(support.Config, params, baseRequestParams)
-	if err != nil {
-		return nil, common.ContextError(err)
-	}
-
-	// Ignoring error as params are validated
-	clientPlatform, _ := getStringRequestParam(params, "client_platform")
-
-	// Client sends empty payload to receive TTL
-	// NOTE: these events are not currently logged
-	if params["verificationData"] == nil {
-		if CLIENT_VERIFICATION_REQUIRED {
-
-			var clientVerificationResponse struct {
-				ClientVerificationTTLSeconds int `json:"client_verification_ttl_seconds"`
-			}
-			clientVerificationResponse.ClientVerificationTTLSeconds = CLIENT_VERIFICATION_TTL_SECONDS
-
-			responsePayload, err := json.Marshal(clientVerificationResponse)
-			if err != nil {
-				return nil, common.ContextError(err)
-			}
-
-			return responsePayload, nil
-		}
-		return make([]byte, 0), nil
-	} else {
-		verificationData, err := getJSONObjectRequestParam(params, "verificationData")
-		if err != nil {
-			return nil, common.ContextError(err)
-		}
-
-		logFields := getRequestLogFields(
-			"client_verification",
-			geoIPData,
-			authorizedAccessTypes,
-			params,
-			baseRequestParams)
-
-		var verified bool
-		var safetyNetCheckLogs LogFields
-		switch normalizeClientPlatform(clientPlatform) {
-		case CLIENT_PLATFORM_ANDROID:
-			verified, safetyNetCheckLogs = verifySafetyNetPayload(verificationData)
-			logFields["safetynet_check"] = safetyNetCheckLogs
-		}
-
-		log.LogRawFieldsWithTimestamp(logFields)
-
-		if verified {
-			// TODO: change throttling treatment
-		}
-		return make([]byte, 0), nil
-	}
-}
-
 var tacticsParams = []requestParamSpec{
 	{tactics.STORED_TACTICS_TAG_PARAMETER_NAME, isAnyString, requestParamOptional},
 	{tactics.SPEED_TEST_SAMPLES_PARAMETER_NAME, nil, requestParamOptional | requestParamJSON},

+ 0 - 371
psiphon/server/safetyNet.go

@@ -1,371 +0,0 @@
-/*
- * Copyright (c) 2016, Psiphon Inc.
- * All rights reserved.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-package server
-
-import (
-	"crypto/x509"
-	"encoding/base64"
-	"encoding/json"
-	"errors"
-	"fmt"
-	"reflect"
-	"strconv"
-	"strings"
-	"time"
-
-	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
-)
-
-const (
-	// Cert of the root certificate authority (GeoTrust Global CA)
-	// which signs the intermediate certificate from Google (GIAG2)
-	geotrustCert      = "-----BEGIN CERTIFICATE-----\nMIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT\nMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i\nYWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG\nEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg\nR2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9\n9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq\nfnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv\niS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU\n1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+\nbw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW\nMPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA\nephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l\nuMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn\nZ57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS\ntQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF\nPseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un\nhw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV\n5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw==\n-----END CERTIFICATE-----\n"
-	maxLogFieldSize   = 64
-	maxLogPayloadSize = 6144
-	// base64 encoded sha256 hash of the license used to sign the android
-	// client (.apk) https://psiphon.ca/en/faq.html#authentic-android
-	//
-	// keytool -printcert -file CERT.RSA
-	// SHA256: 76:DB:EF:15:F6:77:26:D4:51:A1:23:59:B8:57:9C:0D:7A:9F:63:5D:52:6A:A3:74:24:DF:13:16:32:F1:78:10
-	//
-	// echo dtvvFfZ3JtRRoSNZuFecDXqfY11SaqN0JN8TFjLxeBA= | base64 -d | hexdump  -e '32/1 "%02X " "\n"'
-	// 76 DB EF 15 F6 77 26 D4 51 A1 23 59 B8 57 9C 0D 7A 9F 63 5D 52 6A A3 74 24 DF 13 16 32 F1 78 10
-	psiphon3Base64CertHash = "dtvvFfZ3JtRRoSNZuFecDXqfY11SaqN0JN8TFjLxeBA="
-	safetynetCN            = "attest.android.com"
-)
-
-var psiphonApkPackagenames = []string{"com.psiphon3", "com.psiphon3.subscription"}
-
-type X5C []string
-
-type jwt struct {
-	status  int
-	payload string
-}
-
-func newJwt(token common.APIParameters) (jwt, error) {
-	jwtObj := jwt{}
-
-	if token["status"] == nil {
-		return jwtObj, errors.New("Absent JWT status field")
-	}
-	status, ok := token["status"].(float64)
-	if !ok {
-		return jwtObj, errors.New("Malformed JWT status field. Expected float64 got " + reflect.TypeOf(token["status"]).String())
-	}
-
-	if token["payload"] == nil {
-		return jwtObj, errors.New("Absent JWT payload field")
-	}
-	payload, ok := token["payload"].(string)
-	if !ok {
-		return jwtObj, errors.New("Malformed JWT payload field. Expected string got " + reflect.TypeOf(token["payload"]).String())
-	}
-
-	if len(payload) > maxLogPayloadSize {
-		return jwtObj, errors.New("JWT of length " + strconv.Itoa(len(payload)) + " exceeds maximum expected length of " + strconv.Itoa(maxLogPayloadSize))
-	}
-
-	jwtObj.status = int(status)
-	jwtObj.payload = payload
-	return jwtObj, nil
-}
-
-type jwtHeader struct {
-	Algorithm string `json:"alg"`
-	CertChain X5C    `json:"x5c"`
-}
-
-func newJwtHeader(jsonBytes []byte) (jwtHeader, error) {
-	var header jwtHeader
-	err := json.Unmarshal(jsonBytes, &header)
-	return header, err
-}
-
-type jwtBody struct {
-	// Pointers are used because these fields may not
-	// exist and the default values assigned when
-	// unmarshalling into the corresponding non-pointer
-	// struct would cause non-existing fields to be logged
-	// in a manner that would make it impossible to distinguish
-	// between a non-existing field and field of default value
-	BasicIntegrity             *bool     `json:"basicIntegrity"`
-	CtsProfileMatch            *bool     `json:"ctsProfileMatch"`
-	TimestampMs                *int      `json:"timestampMs"`
-	ApkDigestSha256            *string   `json:"apkDigestSha256"`
-	ApkPackageName             *string   `json:"apkPackageName"`
-	Error                      *string   `json:"error"`
-	Extension                  *string   `json:"extension"`
-	Nonce                      *string   `json:"nonce"`
-	ApkCertificateDigestSha256 *[]string `json:"apkCertificateDigestSha256"`
-}
-
-func newJwtBody(jsonBytes []byte) (jwtBody, error) {
-	var body jwtBody
-	err := json.Unmarshal(jsonBytes, &body)
-
-	return body, err
-}
-
-// Verify x509 certificate chain
-func (x5c X5C) verifyCertChain() (leaf *x509.Certificate, validCN bool, err error) {
-	if len(x5c) == 0 || len(x5c) > 10 {
-		// OpenSSL's default maximum chain length is 10
-		return nil, false, fmt.Errorf("Invalid certchain length of %d\n", len(x5c))
-	}
-
-	// Parse leaf certificate
-	leafCertDer, err := base64.StdEncoding.DecodeString(x5c[0])
-	if err != nil {
-		return nil, false, err
-	}
-	leafCert, err := x509.ParseCertificate(leafCertDer)
-	if err != nil {
-		return nil, false, err
-	}
-
-	// Verify CN
-	if leafCert.Subject.CommonName == safetynetCN {
-		validCN = true
-	}
-
-	// Parse and add intermediate certificates
-	intermediates := x509.NewCertPool()
-	for i := 1; i < len(x5c); i++ {
-		intermediateCertDer, err := base64.StdEncoding.DecodeString(x5c[i])
-		if err != nil {
-			return leafCert, false, err
-		}
-
-		intermediateCert, err := x509.ParseCertificate(intermediateCertDer)
-		if err != nil {
-			return leafCert, false, err
-		}
-		intermediates.AddCert(intermediateCert)
-	}
-
-	// Parse and verify root cert
-	roots := x509.NewCertPool()
-	ok := roots.AppendCertsFromPEM([]byte(geotrustCert))
-	if !ok {
-		return leafCert, false, fmt.Errorf("Failed to append GEOTRUST cert\n")
-	}
-
-	// Verify leaf certificate
-	storeCtx := x509.VerifyOptions{
-		Intermediates: intermediates,
-		Roots:         roots,
-		KeyUsages:     []x509.ExtKeyUsage{x509.ExtKeyUsageAny},
-	}
-	_, err = leafCert.Verify(storeCtx)
-	if err != nil {
-		return leafCert, false, err
-	}
-
-	return leafCert, validCN, nil
-}
-
-func (body *jwtBody) verifyJWTBody() (validApkCert, validApkPackageName bool) {
-	// Verify apk certificate digest
-	if body.ApkCertificateDigestSha256 != nil {
-		if len(*body.ApkCertificateDigestSha256) > 0 && (*body.ApkCertificateDigestSha256)[0] == psiphon3Base64CertHash {
-			validApkCert = true
-		}
-	}
-
-	// Verify apk package name
-	if body.ApkPackageName != nil && common.Contains(psiphonApkPackagenames, *body.ApkPackageName) {
-		validApkPackageName = true
-	}
-
-	return
-}
-
-// Form log fields for debugging
-func errorLogFields(err error, params common.APIParameters) LogFields {
-	logFields := LogFields{
-		// Must sanitize string. JSON unmarshalling exceptions
-		// include the value of the field which failed to unmarshal.
-		"error_message": sanitizeJwtString(err.Error()),
-	}
-
-	// Sanitize payload for logging
-	payload, ok := params["payload"].(string)
-	// Only log payload if it exists
-	if ok {
-		if len(payload) > maxLogPayloadSize {
-			// Truncate if payload exceedingly long
-			payload = payload[:maxLogPayloadSize]
-			payload += ".."
-		}
-		logFields["payload"] = payload
-	}
-
-	return logFields
-}
-
-// Convert error to string for logging
-func getError(err error) string {
-	if err == nil {
-		return ""
-	}
-	return err.Error()
-}
-
-// Sanitize client / safetynet provided strings for logging
-func sanitizeJwtString(s string) string {
-	if len(s) > maxLogFieldSize {
-		return s[:maxLogFieldSize]
-	}
-	return s
-}
-
-// Add log field if it exists (see comment accompanying jwtBody struct)
-func (l LogFields) addJwtField(field string, input interface{}) {
-	switch val := input.(type) {
-	case *bool:
-		if val != nil {
-			l[field] = *val
-		}
-	case *int:
-		if val != nil {
-			if field == "verification_timestamp" {
-				l[field] = time.Unix(0, int64(*val)*1e6).UTC().Format(time.RFC3339)
-			} else {
-				l[field] = *val
-			}
-		}
-	case *string:
-		if val != nil {
-			l[field] = sanitizeJwtString(*val)
-		}
-	case *[]string:
-		// Only concerned with ApkCertificateDigestSha256[0] for now
-		if val != nil && len(*val) > 0 {
-			l[field] = sanitizeJwtString((*val)[0])
-		}
-	default:
-		// Do nothing
-	}
-}
-
-// Validate JWT produced by safetynet
-func verifySafetyNetPayload(params common.APIParameters) (bool, LogFields) {
-
-	jwt, err := newJwt(params)
-	if err != nil {
-		// Malformed JWT
-		return false, errorLogFields(err, params)
-	}
-
-	statusStrings := map[int]string{
-		0: "API_REQUEST_OK",
-		1: "API_REQUEST_FAILED",
-		2: "API_CONNECT_FAILED",
-	}
-
-	statusString, ok := statusStrings[jwt.status]
-	if !ok {
-		statusString = "Expected status in range 0-2. Got " + strconv.Itoa(jwt.status)
-	}
-
-	// SafetyNet check failed
-	if jwt.status != 0 {
-		return false, errorLogFields(errors.New(statusString), params)
-	}
-
-	// Split into base64 encoded header, body, signature
-	jwtParts := strings.Split(jwt.payload, ".")
-	if len(jwtParts) != 3 {
-		// Malformed payload
-		return false, errorLogFields(errors.New("JWT does not have 3 parts"), params)
-	}
-
-	// Decode header, body, signature
-	headerJson, err := base64.RawURLEncoding.DecodeString(jwtParts[0])
-	if err != nil {
-		return false, errorLogFields(err, params)
-	}
-	bodyJson, err := base64.RawURLEncoding.DecodeString(jwtParts[1])
-	if err != nil {
-		return false, errorLogFields(err, params)
-	}
-	signature, err := base64.RawURLEncoding.DecodeString(jwtParts[2])
-	if err != nil {
-		return false, errorLogFields(err, params)
-	}
-
-	// Extract header from json
-	header, err := newJwtHeader(headerJson)
-	if err != nil {
-		return false, errorLogFields(err, params)
-	}
-
-	// Verify certchain in header
-	leafCert, validCN, certChainErrors := header.CertChain.verifyCertChain()
-
-	var signatureErrors error
-	if leafCert == nil {
-		signatureErrors = errors.New("Failed to parse leaf certificate")
-	} else {
-		// Verify signature over header and body
-		signatureErrors = leafCert.CheckSignature(x509.SHA256WithRSA, []byte(jwtParts[0]+"."+jwtParts[1]), signature)
-	}
-
-	// Extract body from json
-	body, err := newJwtBody(bodyJson)
-	if err != nil {
-		return false, errorLogFields(err, params)
-	}
-
-	// Validate jwt payload
-	validApkCert, validApkPackageName := body.verifyJWTBody()
-
-	validCertChain := certChainErrors == nil
-	validSignature := signatureErrors == nil
-	verified := validCN && validApkCert && validApkPackageName && validCertChain && validSignature
-
-	// Add server generated fields for logging
-	logFields := LogFields{
-		"certchain_errors":      getError(certChainErrors),
-		"signature_errors":      getError(signatureErrors),
-		"status":                strconv.Itoa(jwt.status),
-		"status_string":         statusString,
-		"valid_cn":              validCN,
-		"valid_apk_cert":        validApkCert,
-		"valid_apk_packagename": validApkPackageName,
-		"valid_certchain":       validCertChain,
-		"valid_signature":       validSignature,
-		"verified":              verified,
-	}
-
-	// Add client / safetynet generated fields for logging
-	logFields.addJwtField("apk_certificate_digest_sha256", body.ApkCertificateDigestSha256)
-	logFields.addJwtField("apk_digest_sha256", body.ApkDigestSha256)
-	logFields.addJwtField("apk_package_name", body.ApkPackageName)
-	logFields.addJwtField("basic_integrity", body.BasicIntegrity)
-	logFields.addJwtField("cts_profile_match", body.CtsProfileMatch)
-	logFields.addJwtField("error", body.Error)
-	logFields.addJwtField("extension", body.Extension)
-	logFields.addJwtField("nonce", body.Nonce)
-	logFields.addJwtField("verification_timestamp", body.TimestampMs)
-
-	return verified, logFields
-}

+ 0 - 42
psiphon/server/server_test.go

@@ -82,8 +82,6 @@ func TestMain(m *testing.M) {
 
 	psiphon.SetEmitDiagnosticNotices(true)
 
-	CLIENT_VERIFICATION_REQUIRED = true
-
 	mockWebServerURL, mockWebServerExpectedResponse = runMockWebServer()
 
 	os.Exit(m.Run())
@@ -130,7 +128,6 @@ func TestSSH(t *testing.T) {
 			denyTrafficRules:     false,
 			requireAuthorization: true,
 			omitAuthorization:    false,
-			doClientVerification: true,
 			doTunneledWebRequest: true,
 			doTunneledNTPRequest: true,
 		})
@@ -146,7 +143,6 @@ func TestOSSH(t *testing.T) {
 			denyTrafficRules:     false,
 			requireAuthorization: true,
 			omitAuthorization:    false,
-			doClientVerification: false,
 			doTunneledWebRequest: true,
 			doTunneledNTPRequest: true,
 		})
@@ -162,7 +158,6 @@ func TestUnfrontedMeek(t *testing.T) {
 			denyTrafficRules:     false,
 			requireAuthorization: true,
 			omitAuthorization:    false,
-			doClientVerification: false,
 			doTunneledWebRequest: true,
 			doTunneledNTPRequest: true,
 		})
@@ -178,7 +173,6 @@ func TestUnfrontedMeekHTTPS(t *testing.T) {
 			denyTrafficRules:     false,
 			requireAuthorization: true,
 			omitAuthorization:    false,
-			doClientVerification: false,
 			doTunneledWebRequest: true,
 			doTunneledNTPRequest: true,
 		})
@@ -194,7 +188,6 @@ func TestUnfrontedMeekSessionTicket(t *testing.T) {
 			denyTrafficRules:     false,
 			requireAuthorization: true,
 			omitAuthorization:    false,
-			doClientVerification: false,
 			doTunneledWebRequest: true,
 			doTunneledNTPRequest: true,
 		})
@@ -210,7 +203,6 @@ func TestWebTransportAPIRequests(t *testing.T) {
 			denyTrafficRules:     false,
 			requireAuthorization: false,
 			omitAuthorization:    true,
-			doClientVerification: true,
 			doTunneledWebRequest: true,
 			doTunneledNTPRequest: true,
 		})
@@ -226,7 +218,6 @@ func TestHotReload(t *testing.T) {
 			denyTrafficRules:     false,
 			requireAuthorization: true,
 			omitAuthorization:    false,
-			doClientVerification: false,
 			doTunneledWebRequest: true,
 			doTunneledNTPRequest: true,
 		})
@@ -242,7 +233,6 @@ func TestDefaultSessionID(t *testing.T) {
 			denyTrafficRules:     false,
 			requireAuthorization: true,
 			omitAuthorization:    false,
-			doClientVerification: false,
 			doTunneledWebRequest: true,
 			doTunneledNTPRequest: true,
 		})
@@ -258,7 +248,6 @@ func TestDenyTrafficRules(t *testing.T) {
 			denyTrafficRules:     true,
 			requireAuthorization: true,
 			omitAuthorization:    false,
-			doClientVerification: false,
 			doTunneledWebRequest: true,
 			doTunneledNTPRequest: true,
 		})
@@ -274,7 +263,6 @@ func TestOmitAuthorization(t *testing.T) {
 			denyTrafficRules:     false,
 			requireAuthorization: true,
 			omitAuthorization:    true,
-			doClientVerification: false,
 			doTunneledWebRequest: true,
 			doTunneledNTPRequest: true,
 		})
@@ -290,7 +278,6 @@ func TestNoAuthorization(t *testing.T) {
 			denyTrafficRules:     false,
 			requireAuthorization: false,
 			omitAuthorization:    true,
-			doClientVerification: false,
 			doTunneledWebRequest: true,
 			doTunneledNTPRequest: true,
 		})
@@ -306,7 +293,6 @@ func TestUnusedAuthorization(t *testing.T) {
 			denyTrafficRules:     false,
 			requireAuthorization: false,
 			omitAuthorization:    false,
-			doClientVerification: false,
 			doTunneledWebRequest: true,
 			doTunneledNTPRequest: true,
 		})
@@ -322,7 +308,6 @@ func TestTCPOnlySLOK(t *testing.T) {
 			denyTrafficRules:     false,
 			requireAuthorization: true,
 			omitAuthorization:    false,
-			doClientVerification: false,
 			doTunneledWebRequest: true,
 			doTunneledNTPRequest: false,
 		})
@@ -338,7 +323,6 @@ func TestUDPOnlySLOK(t *testing.T) {
 			denyTrafficRules:     false,
 			requireAuthorization: true,
 			omitAuthorization:    false,
-			doClientVerification: false,
 			doTunneledWebRequest: false,
 			doTunneledNTPRequest: true,
 		})
@@ -352,7 +336,6 @@ type runServerConfig struct {
 	denyTrafficRules     bool
 	requireAuthorization bool
 	omitAuthorization    bool
-	doClientVerification bool
 	doTunneledWebRequest bool
 	doTunneledNTPRequest bool
 }
@@ -570,10 +553,6 @@ func runServer(t *testing.T, runConfig *runServerConfig) {
 	clientConfig.LocalHttpProxyPort = localHTTPProxyPort
 	clientConfig.EmitSLOKs = true
 
-	if runConfig.doClientVerification {
-		clientConfig.ClientPlatform = "Android"
-	}
-
 	if !runConfig.omitAuthorization {
 		clientConfig.Authorizations = []string{clientAuthorization}
 	}
@@ -613,8 +592,6 @@ func runServer(t *testing.T, runConfig *runServerConfig) {
 	tunnelsEstablished := make(chan struct{}, 1)
 	homepageReceived := make(chan struct{}, 1)
 	slokSeeded := make(chan struct{}, 1)
-	verificationRequired := make(chan struct{}, 1)
-	verificationCompleted := make(chan struct{}, 1)
 
 	psiphon.SetNoticeWriter(psiphon.NewNoticeReceiver(
 		func(notice []byte) {
@@ -628,9 +605,6 @@ func runServer(t *testing.T, runConfig *runServerConfig) {
 
 			switch noticeType {
 			case "Tunnels":
-				// Do not set verification payload until tunnel is
-				// established. Otherwise will silently take no action.
-				controller.SetClientVerificationPayloadForActiveTunnels("")
 				count := int(payload["count"].(float64))
 				if count >= numTunnels {
 					sendNotificationReceived(tunnelsEstablished)
@@ -644,11 +618,6 @@ func runServer(t *testing.T, runConfig *runServerConfig) {
 				sendNotificationReceived(homepageReceived)
 			case "SLOKSeeded":
 				sendNotificationReceived(slokSeeded)
-			case "ClientVerificationRequired":
-				sendNotificationReceived(verificationRequired)
-				controller.SetClientVerificationPayloadForActiveTunnels(dummyClientVerificationPayload)
-			case "NoticeClientVerificationRequestCompleted":
-				sendNotificationReceived(verificationCompleted)
 			}
 		}))
 
@@ -693,11 +662,6 @@ func runServer(t *testing.T, runConfig *runServerConfig) {
 	waitOnNotification(t, tunnelsEstablished, timeoutSignal, "tunnel establish timeout exceeded")
 	waitOnNotification(t, homepageReceived, timeoutSignal, "homepage received timeout exceeded")
 
-	if runConfig.doClientVerification {
-		waitOnNotification(t, verificationRequired, timeoutSignal, "verification required timeout exceeded")
-		waitOnNotification(t, verificationCompleted, timeoutSignal, "verification completed timeout exceeded")
-	}
-
 	expectTrafficFailure := runConfig.denyTrafficRules || (runConfig.omitAuthorization && runConfig.requireAuthorization)
 
 	if runConfig.doTunneledWebRequest {
@@ -1244,12 +1208,6 @@ func waitOnNotification(t *testing.T, c, timeoutSignal <-chan struct{}, timeoutM
 	}
 }
 
-const dummyClientVerificationPayload = `
-{
-	"status": 0,
-	"payload": ""
-}`
-
 type testNetworkGetter struct {
 }
 

+ 0 - 26
psiphon/server/webServer.go

@@ -69,7 +69,6 @@ func RunWebServer(
 	serveMux.HandleFunc("/handshake", webServer.handshakeHandler)
 	serveMux.HandleFunc("/connected", webServer.connectedHandler)
 	serveMux.HandleFunc("/status", webServer.statusHandler)
-	serveMux.HandleFunc("/client_verification", webServer.clientVerificationHandler)
 
 	certificate, err := tls.X509KeyPair(
 		[]byte(support.Config.WebServerCertificate),
@@ -307,28 +306,3 @@ func (webServer *webServer) statusHandler(w http.ResponseWriter, r *http.Request
 	w.WriteHeader(http.StatusOK)
 	w.Write(responsePayload)
 }
-
-func (webServer *webServer) clientVerificationHandler(w http.ResponseWriter, r *http.Request) {
-
-	params, err := convertHTTPRequestToAPIRequest(w, r, "verificationData")
-
-	var responsePayload []byte
-	if err == nil {
-		responsePayload, err = dispatchAPIRequestHandler(
-			webServer.support,
-			protocol.PSIPHON_WEB_API_PROTOCOL,
-			webServer.lookupGeoIPData(params),
-			nil, // authorizedAccessTypes not logged in web API transport
-			protocol.PSIPHON_API_CLIENT_VERIFICATION_REQUEST_NAME,
-			params)
-	}
-
-	if err != nil {
-		log.WithContextFields(LogFields{"error": err}).Warning("failed")
-		w.WriteHeader(http.StatusNotFound)
-		return
-	}
-
-	w.WriteHeader(http.StatusOK)
-	w.Write(responsePayload)
-}

+ 0 - 72
psiphon/serverApi.go

@@ -607,78 +607,6 @@ func RecordRemoteServerListStat(
 		PERSISTENT_STAT_TYPE_REMOTE_SERVER_LIST, remoteServerListStatJson)
 }
 
-// DoClientVerificationRequest performs the "client_verification" API
-// request. This request is used to verify that the client is a valid
-// Psiphon client, which will determine how the server treats the client
-// 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, serverIP string) error {
-
-	params := serverContext.getBaseAPIParameters()
-	var response []byte
-	var err error
-
-	if serverContext.psiphonHttpsClient == nil {
-
-		// Empty verification payload signals desire to
-		// query the server for current TTL. This is
-		// indicated to the server by the absence of the
-		// verificationData field.
-		if verificationPayload != "" {
-			rawMessage := json.RawMessage(verificationPayload)
-			params["verificationData"] = &rawMessage
-		}
-
-		request, err := makeSSHAPIRequestPayload(params)
-		if err != nil {
-			return common.ContextError(err)
-		}
-
-		response, err = serverContext.tunnel.SendAPIRequest(
-			protocol.PSIPHON_API_CLIENT_VERIFICATION_REQUEST_NAME, request)
-		if err != nil {
-			return common.ContextError(err)
-		}
-
-	} else {
-
-		// Legacy web service API request
-		response, err = serverContext.doPostRequest(
-			makeRequestUrl(serverContext.tunnel, "", "client_verification", params),
-			"application/json",
-			bytes.NewReader([]byte(verificationPayload)))
-		if err != nil {
-			return common.ContextError(err)
-		}
-	}
-
-	// Server may request a new verification to be performed,
-	// for example, if the payload timestamp is too old, etc.
-
-	var clientVerificationResponse struct {
-		ClientVerificationServerNonce string `json:"client_verification_server_nonce"`
-		ClientVerificationTTLSeconds  int    `json:"client_verification_ttl_seconds"`
-		ClientVerificationResetCache  bool   `json:"client_verification_reset_cache"`
-	}
-
-	// In case of empty response body the json.Unmarshal will fail
-	// and clientVerificationResponse will be initialized with default values
-
-	_ = json.Unmarshal(response, &clientVerificationResponse)
-
-	if clientVerificationResponse.ClientVerificationTTLSeconds > 0 {
-		NoticeClientVerificationRequired(
-			clientVerificationResponse.ClientVerificationServerNonce,
-			clientVerificationResponse.ClientVerificationTTLSeconds,
-			clientVerificationResponse.ClientVerificationResetCache)
-	} else {
-		NoticeClientVerificationRequestCompleted(serverIP)
-	}
-
-	return nil
-}
-
 // doGetRequest makes a tunneled HTTPS request and returns the response body.
 func (serverContext *ServerContext) doGetRequest(
 	requestUrl string) (responseBody []byte, err error) {

+ 21 - 111
psiphon/tunnel.go

@@ -78,28 +78,27 @@ type TunnelOwner interface {
 // tunnel includes a network connection to the specified server
 // and an SSH session built on top of that transport.
 type Tunnel struct {
-	mutex                        *sync.Mutex
-	config                       *Config
-	isActivated                  bool
-	isDiscarded                  bool
-	isClosed                     bool
-	sessionId                    string
-	serverEntry                  *protocol.ServerEntry
-	serverContext                *ServerContext
-	protocol                     string
-	conn                         *common.ActivityMonitoredConn
-	sshClient                    *ssh.Client
-	sshServerRequests            <-chan *ssh.Request
-	operateWaitGroup             *sync.WaitGroup
-	operateCtx                   context.Context
-	stopOperate                  context.CancelFunc
-	signalPortForwardFailure     chan struct{}
-	totalPortForwardFailures     int
-	adjustedEstablishStartTime   monotime.Time
-	establishDuration            time.Duration
-	establishedTime              monotime.Time
-	dialStats                    *DialStats
-	newClientVerificationPayload chan string
+	mutex                      *sync.Mutex
+	config                     *Config
+	isActivated                bool
+	isDiscarded                bool
+	isClosed                   bool
+	sessionId                  string
+	serverEntry                *protocol.ServerEntry
+	serverContext              *ServerContext
+	protocol                   string
+	conn                       *common.ActivityMonitoredConn
+	sshClient                  *ssh.Client
+	sshServerRequests          <-chan *ssh.Request
+	operateWaitGroup           *sync.WaitGroup
+	operateCtx                 context.Context
+	stopOperate                context.CancelFunc
+	signalPortForwardFailure   chan struct{}
+	totalPortForwardFailures   int
+	adjustedEstablishStartTime monotime.Time
+	establishDuration          time.Duration
+	establishedTime            monotime.Time
+	dialStats                  *DialStats
 }
 
 // DialStats records additional dial config that is sent to the server for
@@ -185,9 +184,6 @@ func ConnectTunnel(
 		signalPortForwardFailure:   make(chan struct{}, 1),
 		adjustedEstablishStartTime: adjustedEstablishStartTime,
 		dialStats:                  dialResult.dialStats,
-		// Buffer allows SetClientVerificationPayload to submit one new payload
-		// without blocking or dropping it.
-		newClientVerificationPayload: make(chan string, 1),
 	}, nil
 }
 
@@ -484,17 +480,6 @@ func (tunnel *Tunnel) SignalComponentFailure() {
 	tunnel.Close(false)
 }
 
-// SetClientVerificationPayload triggers a client verification request, for this
-// tunnel, with the specified verifiction payload. If the tunnel is not yet established,
-// the payload/request is enqueued. If a payload/request is already eneueued, the
-// new payload is dropped.
-func (tunnel *Tunnel) SetClientVerificationPayload(clientVerificationPayload string) {
-	select {
-	case tunnel.newClientVerificationPayload <- clientVerificationPayload:
-	default:
-	}
-}
-
 // TunneledConn implements net.Conn and wraps a port forward connection.
 // It is used to hook into Read and Write to observe I/O errors and
 // report these errors back to the tunnel monitor as port forward failures.
@@ -1232,58 +1217,6 @@ func (tunnel *Tunnel) operateTunnel(tunnelOwner TunnelOwner) {
 		}
 	}()
 
-	requestsWaitGroup.Add(1)
-	signalStopClientVerificationRequests := make(chan struct{})
-	go func() {
-		defer requestsWaitGroup.Done()
-
-		clientVerificationRequestSuccess := true
-		clientVerificationPayload := ""
-		failCount := 0
-		for {
-			// TODO: use reflect.SelectCase?
-			if clientVerificationRequestSuccess == true {
-				failCount = 0
-				select {
-				case clientVerificationPayload = <-tunnel.newClientVerificationPayload:
-				case <-signalStopClientVerificationRequests:
-					return
-				}
-			} else {
-
-				maxRetries := clientParameters.Get().Int(
-					parameters.PsiphonAPIClientVerificationRequestMaxRetries)
-
-				// If sendClientVerification failed to send the payload we
-				// will retry after a delay. Will use a new payload instead
-				// if that arrives in the meantime.
-				failCount += 1
-				if failCount > maxRetries {
-					return
-				}
-
-				timeout := clientParameters.Get().Duration(
-					parameters.PsiphonAPIClientVerificationRequestRetryPeriod)
-
-				timer := time.NewTimer(timeout)
-
-				doReturn := false
-				select {
-				case <-timer.C:
-				case clientVerificationPayload = <-tunnel.newClientVerificationPayload:
-				case <-signalStopClientVerificationRequests:
-					doReturn = true
-				}
-				timer.Stop()
-				if doReturn {
-					return
-				}
-			}
-
-			clientVerificationRequestSuccess = sendClientVerification(tunnel, clientVerificationPayload)
-		}
-	}()
-
 	shutdown := false
 	var err error
 	for !shutdown && err == nil {
@@ -1377,7 +1310,6 @@ func (tunnel *Tunnel) operateTunnel(tunnelOwner TunnelOwner) {
 
 	close(signalSshKeepAlive)
 	close(signalStatusRequest)
-	close(signalStopClientVerificationRequests)
 	requestsWaitGroup.Wait()
 
 	// Capture bytes transferred since the last noticeBytesTransferredTicker tick
@@ -1504,25 +1436,3 @@ func sendStats(tunnel *Tunnel) bool {
 
 	return err == nil
 }
-
-// sendClientVerification is a helper for sending a client verification request
-// to the server.
-func sendClientVerification(tunnel *Tunnel, clientVerificationPayload string) bool {
-
-	// Tunnel does not have a serverContext when DisableApi is set
-	if tunnel.serverContext == nil {
-		return true
-	}
-
-	// Skip when tunnel is discarded
-	if tunnel.IsDiscarded() {
-		return true
-	}
-
-	err := tunnel.serverContext.DoClientVerificationRequest(clientVerificationPayload, tunnel.serverEntry.IpAddress)
-	if err != nil {
-		NoticeAlert("DoClientVerificationRequest failed for %s: %s", tunnel.serverEntry.IpAddress, err)
-	}
-
-	return err == nil
-}