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

Merge pull request #493 from rod-hynes/master

Upgrade "ssh"; plus other minor enhancements
Rod Hynes 7 лет назад
Родитель
Сommit
cbc67fd893
100 измененных файлов с 8261 добавлено и 1860 удалено
  1. 23 7
      ConsoleClient/main.go
  2. 14 1
      MobileLibrary/psi/psi.go
  3. 5 10
      psiphon/common/crypto/CONTRIBUTING.md
  4. 154 318
      psiphon/common/crypto/acme/acme.go
  5. 140 173
      psiphon/common/crypto/acme/acme_test.go
  6. 484 164
      psiphon/common/crypto/acme/autocert/autocert.go
  7. 671 87
      psiphon/common/crypto/acme/autocert/autocert_test.go
  8. 3 3
      psiphon/common/crypto/acme/autocert/cache.go
  9. 4 4
      psiphon/common/crypto/acme/autocert/example_test.go
  10. 416 0
      psiphon/common/crypto/acme/autocert/internal/acmetest/ca.go
  11. 2 5
      psiphon/common/crypto/acme/autocert/listener.go
  12. 31 14
      psiphon/common/crypto/acme/autocert/renewal.go
  13. 158 20
      psiphon/common/crypto/acme/autocert/renewal_test.go
  14. 281 0
      psiphon/common/crypto/acme/http.go
  15. 209 0
      psiphon/common/crypto/acme/http_test.go
  16. 35 1
      psiphon/common/crypto/acme/types.go
  17. 285 0
      psiphon/common/crypto/argon2/argon2.go
  18. 233 0
      psiphon/common/crypto/argon2/argon2_test.go
  19. 53 0
      psiphon/common/crypto/argon2/blake2b.go
  20. 60 0
      psiphon/common/crypto/argon2/blamka_amd64.go
  21. 243 0
      psiphon/common/crypto/argon2/blamka_amd64.s
  22. 163 0
      psiphon/common/crypto/argon2/blamka_generic.go
  23. 15 0
      psiphon/common/crypto/argon2/blamka_ref.go
  24. 2 2
      psiphon/common/crypto/bcrypt/bcrypt.go
  25. 83 1
      psiphon/common/crypto/blake2b/blake2b.go
  26. 10 16
      psiphon/common/crypto/blake2b/blake2bAVX2_amd64.go
  27. 0 12
      psiphon/common/crypto/blake2b/blake2bAVX2_amd64.s
  28. 3 4
      psiphon/common/crypto/blake2b/blake2b_amd64.go
  29. 0 9
      psiphon/common/crypto/blake2b/blake2b_amd64.s
  30. 50 1
      psiphon/common/crypto/blake2b/blake2b_test.go
  31. 57 0
      psiphon/common/crypto/blake2s/blake2s.go
  32. 8 11
      psiphon/common/crypto/blake2s/blake2s_386.go
  33. 0 25
      psiphon/common/crypto/blake2s/blake2s_386.s
  34. 10 13
      psiphon/common/crypto/blake2s/blake2s_amd64.go
  35. 0 25
      psiphon/common/crypto/blake2s/blake2s_amd64.s
  36. 49 1
      psiphon/common/crypto/blake2s/blake2s_test.go
  37. 26 14
      psiphon/common/crypto/bn256/bn256.go
  38. 9 0
      psiphon/common/crypto/bn256/curve.go
  39. 9 0
      psiphon/common/crypto/bn256/twist.go
  40. 23 5
      psiphon/common/crypto/chacha20poly1305/chacha20poly1305.go
  41. 24 65
      psiphon/common/crypto/chacha20poly1305/chacha20poly1305_amd64.go
  42. 0 19
      psiphon/common/crypto/chacha20poly1305/chacha20poly1305_amd64.s
  43. 25 14
      psiphon/common/crypto/chacha20poly1305/chacha20poly1305_generic.go
  44. 146 73
      psiphon/common/crypto/chacha20poly1305/chacha20poly1305_test.go
  45. 394 0
      psiphon/common/crypto/chacha20poly1305/chacha20poly1305_vectors_test.go
  46. 0 199
      psiphon/common/crypto/chacha20poly1305/internal/chacha20/chacha_generic.go
  47. 0 33
      psiphon/common/crypto/chacha20poly1305/internal/chacha20/chacha_test.go
  48. 104 0
      psiphon/common/crypto/chacha20poly1305/xchacha20poly1305.go
  49. 222 75
      psiphon/common/crypto/cryptobyte/asn1.go
  50. 46 0
      psiphon/common/crypto/cryptobyte/asn1/asn1.go
  51. 63 15
      psiphon/common/crypto/cryptobyte/asn1_test.go
  52. 69 15
      psiphon/common/crypto/cryptobyte/builder.go
  53. 49 0
      psiphon/common/crypto/cryptobyte/cryptobyte_test.go
  54. 42 8
      psiphon/common/crypto/cryptobyte/example_test.go
  55. 27 18
      psiphon/common/crypto/cryptobyte/string.go
  56. 1 1
      psiphon/common/crypto/curve25519/curve25519.go
  57. 50 14
      psiphon/common/crypto/ed25519/ed25519.go
  58. 37 0
      psiphon/common/crypto/ed25519/ed25519_test.go
  59. 22 0
      psiphon/common/crypto/ed25519/internal/edwards25519/edwards25519.go
  60. 264 0
      psiphon/common/crypto/internal/chacha20/chacha_generic.go
  61. 16 0
      psiphon/common/crypto/internal/chacha20/chacha_noasm.go
  62. 30 0
      psiphon/common/crypto/internal/chacha20/chacha_s390x.go
  63. 283 0
      psiphon/common/crypto/internal/chacha20/chacha_s390x.s
  64. 225 0
      psiphon/common/crypto/internal/chacha20/chacha_test.go
  65. 578 0
      psiphon/common/crypto/internal/chacha20/vectors_test.go
  66. 43 0
      psiphon/common/crypto/internal/chacha20/xor.go
  67. 32 0
      psiphon/common/crypto/internal/subtle/aliasing.go
  68. 35 0
      psiphon/common/crypto/internal/subtle/aliasing_appengine.go
  69. 50 0
      psiphon/common/crypto/internal/subtle/aliasing_test.go
  70. 58 0
      psiphon/common/crypto/nacl/auth/auth.go
  71. 172 0
      psiphon/common/crypto/nacl/auth/auth_test.go
  72. 36 0
      psiphon/common/crypto/nacl/auth/example_test.go
  73. 19 2
      psiphon/common/crypto/nacl/box/box.go
  74. 25 1
      psiphon/common/crypto/nacl/secretbox/secretbox.go
  75. 90 0
      psiphon/common/crypto/nacl/sign/sign.go
  76. 74 0
      psiphon/common/crypto/nacl/sign/sign_test.go
  77. 20 17
      psiphon/common/crypto/ocsp/ocsp.go
  78. 6 6
      psiphon/common/crypto/ocsp/ocsp_test.go
  79. 49 26
      psiphon/common/crypto/openpgp/clearsign/clearsign.go
  80. 69 1
      psiphon/common/crypto/openpgp/clearsign/clearsign_test.go
  81. 112 56
      psiphon/common/crypto/openpgp/keys.go
  82. 198 9
      psiphon/common/crypto/openpgp/keys_test.go
  83. 8 1
      psiphon/common/crypto/openpgp/packet/encrypted_key.go
  84. 39 34
      psiphon/common/crypto/openpgp/packet/encrypted_key_test.go
  85. 22 10
      psiphon/common/crypto/openpgp/packet/packet.go
  86. 7 0
      psiphon/common/crypto/openpgp/packet/private_key.go
  87. 3 20
      psiphon/common/crypto/openpgp/packet/private_key_test.go
  88. 8 3
      psiphon/common/crypto/openpgp/packet/public_key.go
  89. 26 0
      psiphon/common/crypto/openpgp/packet/public_key_test.go
  90. 3 3
      psiphon/common/crypto/openpgp/packet/symmetric_key_encrypted.go
  91. 48 34
      psiphon/common/crypto/openpgp/packet/symmetric_key_encrypted_test.go
  92. 1 1
      psiphon/common/crypto/openpgp/read_test.go
  93. 105 67
      psiphon/common/crypto/openpgp/write.go
  94. 89 0
      psiphon/common/crypto/openpgp/write_test.go
  95. 19 0
      psiphon/common/crypto/pbkdf2/pbkdf2_test.go
  96. 1 1
      psiphon/common/crypto/pkcs12/crypto.go
  97. 0 3
      psiphon/common/crypto/pkcs12/internal/rc2/rc2.go
  98. 0 1
      psiphon/common/crypto/pkcs12/internal/rc2/rc2_test.go
  99. 42 69
      psiphon/common/crypto/poly1305/poly1305_test.go
  100. 14 0
      psiphon/common/crypto/poly1305/sum_noasm.go

+ 23 - 7
ConsoleClient/main.go

@@ -31,6 +31,7 @@ import (
 	"os/signal"
 	"sort"
 	"sync"
+	"syscall"
 
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
@@ -290,15 +291,30 @@ func main() {
 	systemStopSignal := make(chan os.Signal, 1)
 	signal.Notify(systemStopSignal, os.Interrupt, os.Kill)
 
+	writeProfilesSignal := make(chan os.Signal, 1)
+	signal.Notify(writeProfilesSignal, syscall.SIGUSR2)
+
 	// Wait for an OS signal or a Run stop signal, then stop Psiphon and exit
 
-	select {
-	case <-systemStopSignal:
-		psiphon.NoticeInfo("shutdown by system")
-		stopController()
-		controllerWaitGroup.Wait()
-	case <-controllerCtx.Done():
-		psiphon.NoticeInfo("shutdown by controller")
+	for exit := false; !exit; {
+		select {
+		case <-writeProfilesSignal:
+			psiphon.NoticeInfo("write profiles")
+			profileSampleDurationSeconds := 5
+			common.WriteRuntimeProfiles(
+				psiphon.NoticeCommonLogger(),
+				config.DataStoreDirectory,
+				profileSampleDurationSeconds,
+				profileSampleDurationSeconds)
+		case <-systemStopSignal:
+			psiphon.NoticeInfo("shutdown by system")
+			stopController()
+			controllerWaitGroup.Wait()
+			exit = true
+		case <-controllerCtx.Done():
+			psiphon.NoticeInfo("shutdown by controller")
+			exit = true
+		}
 	}
 }
 

+ 14 - 1
MobileLibrary/psi/psi.go

@@ -94,7 +94,7 @@ func Start(
 	psiphon.DoGarbageCollection()
 
 	// Wrap the provider in a layer that locks a mutex before calling a provider function.
-	// The the provider callbacks are Java/Obj-C via gomobile, they are cgo calls that
+	// As the provider callbacks are Java/Obj-C via gomobile, they are cgo calls that
 	// can cause OS threads to be spawned. The mutex prevents many calling goroutines from
 	// causing unbounded numbers of OS threads to be spawned.
 	// TODO: replace the mutex with a semaphore, to allow a larger but still bounded concurrent
@@ -239,6 +239,19 @@ func GetPacketTunnelDNSResolverIPv6Address() string {
 	return tun.GetTransparentDNSResolverIPv6Address().String()
 }
 
+// WriteRuntimeProfiles writes Go runtime profile information to a set of
+// files in the specified output directory. See common.WriteRuntimeProfiles
+// for more details.
+//
+// If called before Start, log notices will emit to stderr.
+func WriteRuntimeProfiles(outputDirectory string, cpuSampleDurationSeconds, blockSampleDurationSeconds int) {
+	common.WriteRuntimeProfiles(
+		psiphon.NoticeCommonLogger(),
+		outputDirectory,
+		cpuSampleDurationSeconds,
+		blockSampleDurationSeconds)
+}
+
 // Helper function to store a list of server entries.
 // if embeddedServerEntryListFilename is not empty, embeddedServerEntryList will be ignored.
 func storeServerEntries(

+ 5 - 10
psiphon/common/crypto/CONTRIBUTING.md

@@ -4,16 +4,15 @@ Go is an open source project.
 
 It is the work of hundreds of contributors. We appreciate your help!
 
-
 ## Filing issues
 
 When [filing an issue](https://golang.org/issue/new), make sure to answer these five questions:
 
-1. What version of Go are you using (`go version`)?
-2. What operating system and processor architecture are you using?
-3. What did you do?
-4. What did you expect to see?
-5. What did you see instead?
+1.  What version of Go are you using (`go version`)?
+2.  What operating system and processor architecture are you using?
+3.  What did you do?
+4.  What did you expect to see?
+5.  What did you see instead?
 
 General questions should go to the [golang-nuts mailing list](https://groups.google.com/group/golang-nuts) instead of the issue tracker.
 The gophers there will answer or ask you to file an issue if you've tripped over a bug.
@@ -23,9 +22,5 @@ The gophers there will answer or ask you to file an issue if you've tripped over
 Please read the [Contribution Guidelines](https://golang.org/doc/contribute.html)
 before sending patches.
 
-**We do not accept GitHub pull requests**
-(we use [Gerrit](https://code.google.com/p/gerrit/) instead for code review).
-
 Unless otherwise noted, the Go source files are distributed under
 the BSD-style license found in the LICENSE file.
-

+ 154 - 318
psiphon/common/crypto/acme/acme.go

@@ -14,7 +14,6 @@
 package acme
 
 import (
-	"bytes"
 	"context"
 	"crypto"
 	"crypto/ecdsa"
@@ -23,6 +22,8 @@ import (
 	"crypto/sha256"
 	"crypto/tls"
 	"crypto/x509"
+	"crypto/x509/pkix"
+	"encoding/asn1"
 	"encoding/base64"
 	"encoding/hex"
 	"encoding/json"
@@ -33,14 +34,26 @@ import (
 	"io/ioutil"
 	"math/big"
 	"net/http"
-	"strconv"
 	"strings"
 	"sync"
 	"time"
 )
 
-// LetsEncryptURL is the Directory endpoint of Let's Encrypt CA.
-const LetsEncryptURL = "https://acme-v01.api.letsencrypt.org/directory"
+const (
+	// LetsEncryptURL is the Directory endpoint of Let's Encrypt CA.
+	LetsEncryptURL = "https://acme-v01.api.letsencrypt.org/directory"
+
+	// ALPNProto is the ALPN protocol name used by a CA server when validating
+	// tls-alpn-01 challenges.
+	//
+	// Package users must ensure their servers can negotiate the ACME ALPN in
+	// order for tls-alpn-01 challenge verifications to succeed.
+	// See the crypto/tls package's Config.NextProtos field.
+	ALPNProto = "acme-tls/1"
+)
+
+// idPeACMEIdentifierV1 is the OID for the ACME extension for the TLS-ALPN challenge.
+var idPeACMEIdentifierV1 = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 30, 1}
 
 const (
 	maxChainLen = 5       // max depth and breadth of a certificate chain
@@ -51,38 +64,6 @@ const (
 	maxNonces = 100
 )
 
-// CertOption is an optional argument type for Client methods which manipulate
-// certificate data.
-type CertOption interface {
-	privateCertOpt()
-}
-
-// WithKey creates an option holding a private/public key pair.
-// The private part signs a certificate, and the public part represents the signee.
-func WithKey(key crypto.Signer) CertOption {
-	return &certOptKey{key}
-}
-
-type certOptKey struct {
-	key crypto.Signer
-}
-
-func (*certOptKey) privateCertOpt() {}
-
-// WithTemplate creates an option for specifying a certificate template.
-// See x509.CreateCertificate for template usage details.
-//
-// In TLSSNIxChallengeCert methods, the template is also used as parent,
-// resulting in a self-signed certificate.
-// The DNSNames field of t is always overwritten for tls-sni challenge certs.
-func WithTemplate(t *x509.Certificate) CertOption {
-	return (*certOptTemplate)(t)
-}
-
-type certOptTemplate x509.Certificate
-
-func (*certOptTemplate) privateCertOpt() {}
-
 // Client is an ACME client.
 // The only required field is Key. An example of creating a client with a new key
 // is as follows:
@@ -108,6 +89,22 @@ type Client struct {
 	// will have no effect.
 	DirectoryURL string
 
+	// RetryBackoff computes the duration after which the nth retry of a failed request
+	// should occur. The value of n for the first call on failure is 1.
+	// The values of r and resp are the request and response of the last failed attempt.
+	// If the returned value is negative or zero, no more retries are done and an error
+	// is returned to the caller of the original method.
+	//
+	// Requests which result in a 4xx client error are not retried,
+	// except for 400 Bad Request due to "bad nonce" errors and 429 Too Many Requests.
+	//
+	// If RetryBackoff is nil, a truncated exponential backoff algorithm
+	// with the ceiling of 10 seconds is used, where each subsequent retry n
+	// is done after either ("Retry-After" + jitter) or (2^n seconds + jitter),
+	// preferring the former if "Retry-After" header is found in the resp.
+	// The jitter is a random value up to 1 second.
+	RetryBackoff func(n int, r *http.Request, resp *http.Response) time.Duration
+
 	dirMu sync.Mutex // guards writes to dir
 	dir   *Directory // cached result of Client's Discover method
 
@@ -131,15 +128,12 @@ func (c *Client) Discover(ctx context.Context) (Directory, error) {
 	if dirURL == "" {
 		dirURL = LetsEncryptURL
 	}
-	res, err := c.get(ctx, dirURL)
+	res, err := c.get(ctx, dirURL, wantStatus(http.StatusOK))
 	if err != nil {
 		return Directory{}, err
 	}
 	defer res.Body.Close()
 	c.addNonce(res.Header)
-	if res.StatusCode != http.StatusOK {
-		return Directory{}, responseError(res)
-	}
 
 	var v struct {
 		Reg    string `json:"new-reg"`
@@ -174,7 +168,7 @@ func (c *Client) Discover(ctx context.Context) (Directory, error) {
 //
 // In the case where CA server does not provide the issued certificate in the response,
 // CreateCert will poll certURL using c.FetchCert, which will result in additional round-trips.
-// In such scenario the caller can cancel the polling with ctx.
+// In such a scenario, the caller can cancel the polling with ctx.
 //
 // CreateCert returns an error if the CA's response or chain was unreasonably large.
 // Callers are encouraged to parse the returned value to ensure the certificate is valid and has the expected features.
@@ -198,14 +192,11 @@ func (c *Client) CreateCert(ctx context.Context, csr []byte, exp time.Duration,
 		req.NotAfter = now.Add(exp).Format(time.RFC3339)
 	}
 
-	res, err := c.retryPostJWS(ctx, c.Key, c.dir.CertURL, req)
+	res, err := c.post(ctx, c.Key, c.dir.CertURL, req, wantStatus(http.StatusCreated))
 	if err != nil {
 		return nil, "", err
 	}
 	defer res.Body.Close()
-	if res.StatusCode != http.StatusCreated {
-		return nil, "", responseError(res)
-	}
 
 	curl := res.Header.Get("Location") // cert permanent URL
 	if res.ContentLength == 0 {
@@ -228,26 +219,11 @@ func (c *Client) CreateCert(ctx context.Context, csr []byte, exp time.Duration,
 // Callers are encouraged to parse the returned value to ensure the certificate is valid
 // and has expected features.
 func (c *Client) FetchCert(ctx context.Context, url string, bundle bool) ([][]byte, error) {
-	for {
-		res, err := c.get(ctx, url)
-		if err != nil {
-			return nil, err
-		}
-		defer res.Body.Close()
-		if res.StatusCode == http.StatusOK {
-			return c.responseCert(ctx, res, bundle)
-		}
-		if res.StatusCode > 299 {
-			return nil, responseError(res)
-		}
-		d := retryAfter(res.Header.Get("Retry-After"), 3*time.Second)
-		select {
-		case <-time.After(d):
-			// retry
-		case <-ctx.Done():
-			return nil, ctx.Err()
-		}
+	res, err := c.get(ctx, url, wantStatus(http.StatusOK))
+	if err != nil {
+		return nil, err
 	}
+	return c.responseCert(ctx, res, bundle)
 }
 
 // RevokeCert revokes a previously issued certificate cert, provided in DER format.
@@ -273,14 +249,11 @@ func (c *Client) RevokeCert(ctx context.Context, key crypto.Signer, cert []byte,
 	if key == nil {
 		key = c.Key
 	}
-	res, err := c.retryPostJWS(ctx, key, c.dir.RevokeURL, body)
+	res, err := c.post(ctx, key, c.dir.RevokeURL, body, wantStatus(http.StatusOK))
 	if err != nil {
 		return err
 	}
 	defer res.Body.Close()
-	if res.StatusCode != http.StatusOK {
-		return responseError(res)
-	}
 	return nil
 }
 
@@ -289,7 +262,7 @@ func (c *Client) RevokeCert(ctx context.Context, key crypto.Signer, cert []byte,
 func AcceptTOS(tosURL string) bool { return true }
 
 // Register creates a new account registration by following the "new-reg" flow.
-// It returns registered account. The a argument is not modified.
+// It returns the registered account. The account is not modified.
 //
 // The registration may require the caller to agree to the CA's Terms of Service (TOS).
 // If so, and the account has not indicated the acceptance of the terms (see Account for details),
@@ -361,14 +334,11 @@ func (c *Client) Authorize(ctx context.Context, domain string) (*Authorization,
 		Resource:   "new-authz",
 		Identifier: authzID{Type: "dns", Value: domain},
 	}
-	res, err := c.retryPostJWS(ctx, c.Key, c.dir.AuthzURL, req)
+	res, err := c.post(ctx, c.Key, c.dir.AuthzURL, req, wantStatus(http.StatusCreated))
 	if err != nil {
 		return nil, err
 	}
 	defer res.Body.Close()
-	if res.StatusCode != http.StatusCreated {
-		return nil, responseError(res)
-	}
 
 	var v wireAuthz
 	if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
@@ -385,14 +355,11 @@ func (c *Client) Authorize(ctx context.Context, domain string) (*Authorization,
 // If a caller needs to poll an authorization until its status is final,
 // see the WaitAuthorization method.
 func (c *Client) GetAuthorization(ctx context.Context, url string) (*Authorization, error) {
-	res, err := c.get(ctx, url)
+	res, err := c.get(ctx, url, wantStatus(http.StatusOK, http.StatusAccepted))
 	if err != nil {
 		return nil, err
 	}
 	defer res.Body.Close()
-	if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusAccepted {
-		return nil, responseError(res)
-	}
 	var v wireAuthz
 	if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
 		return nil, fmt.Errorf("acme: invalid response: %v", err)
@@ -419,56 +386,58 @@ func (c *Client) RevokeAuthorization(ctx context.Context, url string) error {
 		Status:   "deactivated",
 		Delete:   true,
 	}
-	res, err := c.retryPostJWS(ctx, c.Key, url, req)
+	res, err := c.post(ctx, c.Key, url, req, wantStatus(http.StatusOK))
 	if err != nil {
 		return err
 	}
 	defer res.Body.Close()
-	if res.StatusCode != http.StatusOK {
-		return responseError(res)
-	}
 	return nil
 }
 
 // WaitAuthorization polls an authorization at the given URL
 // until it is in one of the final states, StatusValid or StatusInvalid,
-// or the context is done.
+// the ACME CA responded with a 4xx error code, or the context is done.
 //
 // It returns a non-nil Authorization only if its Status is StatusValid.
 // In all other cases WaitAuthorization returns an error.
 // If the Status is StatusInvalid, the returned error is of type *AuthorizationError.
 func (c *Client) WaitAuthorization(ctx context.Context, url string) (*Authorization, error) {
-	sleep := sleeper(ctx)
 	for {
-		res, err := c.get(ctx, url)
+		res, err := c.get(ctx, url, wantStatus(http.StatusOK, http.StatusAccepted))
 		if err != nil {
 			return nil, err
 		}
-		retry := res.Header.Get("Retry-After")
-		if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusAccepted {
-			res.Body.Close()
-			if err := sleep(retry, 1); err != nil {
-				return nil, err
-			}
-			continue
-		}
+
 		var raw wireAuthz
 		err = json.NewDecoder(res.Body).Decode(&raw)
 		res.Body.Close()
-		if err != nil {
-			if err := sleep(retry, 0); err != nil {
-				return nil, err
-			}
-			continue
-		}
-		if raw.Status == StatusValid {
+		switch {
+		case err != nil:
+			// Skip and retry.
+		case raw.Status == StatusValid:
 			return raw.authorization(url), nil
-		}
-		if raw.Status == StatusInvalid {
+		case raw.Status == StatusInvalid:
 			return nil, raw.error(url)
 		}
-		if err := sleep(retry, 0); err != nil {
-			return nil, err
+
+		// Exponential backoff is implemented in c.get above.
+		// This is just to prevent continuously hitting the CA
+		// while waiting for a final authorization status.
+		d := retryAfter(res.Header.Get("Retry-After"))
+		if d == 0 {
+			// Given that the fastest challenges TLS-SNI and HTTP-01
+			// require a CA to make at least 1 network round trip
+			// and most likely persist a challenge state,
+			// this default delay seems reasonable.
+			d = time.Second
+		}
+		t := time.NewTimer(d)
+		select {
+		case <-ctx.Done():
+			t.Stop()
+			return nil, ctx.Err()
+		case <-t.C:
+			// Retry.
 		}
 	}
 }
@@ -477,14 +446,11 @@ func (c *Client) WaitAuthorization(ctx context.Context, url string) (*Authorizat
 //
 // A client typically polls a challenge status using this method.
 func (c *Client) GetChallenge(ctx context.Context, url string) (*Challenge, error) {
-	res, err := c.get(ctx, url)
+	res, err := c.get(ctx, url, wantStatus(http.StatusOK, http.StatusAccepted))
 	if err != nil {
 		return nil, err
 	}
 	defer res.Body.Close()
-	if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusAccepted {
-		return nil, responseError(res)
-	}
 	v := wireChallenge{URI: url}
 	if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
 		return nil, fmt.Errorf("acme: invalid response: %v", err)
@@ -511,16 +477,14 @@ func (c *Client) Accept(ctx context.Context, chal *Challenge) (*Challenge, error
 		Type:     chal.Type,
 		Auth:     auth,
 	}
-	res, err := c.retryPostJWS(ctx, c.Key, chal.URI, req)
+	res, err := c.post(ctx, c.Key, chal.URI, req, wantStatus(
+		http.StatusOK,       // according to the spec
+		http.StatusAccepted, // Let's Encrypt: see https://goo.gl/WsJ7VT (acme-divergences.md)
+	))
 	if err != nil {
 		return nil, err
 	}
 	defer res.Body.Close()
-	// Note: the protocol specifies 200 as the expected response code, but
-	// letsencrypt seems to be returning 202.
-	if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusAccepted {
-		return nil, responseError(res)
-	}
 
 	var v wireChallenge
 	if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
@@ -577,7 +541,7 @@ func (c *Client) HTTP01ChallengePath(token string) string {
 // If no WithKey option is provided, a new ECDSA key is generated using P-256 curve.
 //
 // The returned certificate is valid for the next 24 hours and must be presented only when
-// the server name of the client hello matches exactly the returned name value.
+// the server name of the TLS ClientHello matches exactly the returned name value.
 func (c *Client) TLSSNI01ChallengeCert(token string, opt ...CertOption) (cert tls.Certificate, name string, err error) {
 	ka, err := keyAuth(c.Key.Public(), token)
 	if err != nil {
@@ -604,7 +568,7 @@ func (c *Client) TLSSNI01ChallengeCert(token string, opt ...CertOption) (cert tl
 // If no WithKey option is provided, a new ECDSA key is generated using P-256 curve.
 //
 // The returned certificate is valid for the next 24 hours and must be presented only when
-// the server name in the client hello matches exactly the returned name value.
+// the server name in the TLS ClientHello matches exactly the returned name value.
 func (c *Client) TLSSNI02ChallengeCert(token string, opt ...CertOption) (cert tls.Certificate, name string, err error) {
 	b := sha256.Sum256([]byte(token))
 	h := hex.EncodeToString(b[:])
@@ -625,6 +589,52 @@ func (c *Client) TLSSNI02ChallengeCert(token string, opt ...CertOption) (cert tl
 	return cert, sanA, nil
 }
 
+// TLSALPN01ChallengeCert creates a certificate for TLS-ALPN-01 challenge response.
+// Servers can present the certificate to validate the challenge and prove control
+// over a domain name. For more details on TLS-ALPN-01 see
+// https://tools.ietf.org/html/draft-shoemaker-acme-tls-alpn-00#section-3
+//
+// The token argument is a Challenge.Token value.
+// If a WithKey option is provided, its private part signs the returned cert,
+// and the public part is used to specify the signee.
+// If no WithKey option is provided, a new ECDSA key is generated using P-256 curve.
+//
+// The returned certificate is valid for the next 24 hours and must be presented only when
+// the server name in the TLS ClientHello matches the domain, and the special acme-tls/1 ALPN protocol
+// has been specified.
+func (c *Client) TLSALPN01ChallengeCert(token, domain string, opt ...CertOption) (cert tls.Certificate, err error) {
+	ka, err := keyAuth(c.Key.Public(), token)
+	if err != nil {
+		return tls.Certificate{}, err
+	}
+	shasum := sha256.Sum256([]byte(ka))
+	extValue, err := asn1.Marshal(shasum[:])
+	if err != nil {
+		return tls.Certificate{}, err
+	}
+	acmeExtension := pkix.Extension{
+		Id:       idPeACMEIdentifierV1,
+		Critical: true,
+		Value:    extValue,
+	}
+
+	tmpl := defaultTLSChallengeCertTemplate()
+
+	var newOpt []CertOption
+	for _, o := range opt {
+		switch o := o.(type) {
+		case *certOptTemplate:
+			t := *(*x509.Certificate)(o) // shallow copy is ok
+			tmpl = &t
+		default:
+			newOpt = append(newOpt, o)
+		}
+	}
+	tmpl.ExtraExtensions = append(tmpl.ExtraExtensions, acmeExtension)
+	newOpt = append(newOpt, WithTemplate(tmpl))
+	return tlsChallengeCert([]string{domain}, newOpt)
+}
+
 // doReg sends all types of registration requests.
 // The type of request is identified by typ argument, which is a "resource"
 // in the ACME spec terms.
@@ -644,14 +654,15 @@ func (c *Client) doReg(ctx context.Context, url string, typ string, acct *Accoun
 		req.Contact = acct.Contact
 		req.Agreement = acct.AgreedTerms
 	}
-	res, err := c.retryPostJWS(ctx, c.Key, url, req)
+	res, err := c.post(ctx, c.Key, url, req, wantStatus(
+		http.StatusOK,       // updates and deletes
+		http.StatusCreated,  // new account creation
+		http.StatusAccepted, // Let's Encrypt divergent implementation
+	))
 	if err != nil {
 		return nil, err
 	}
 	defer res.Body.Close()
-	if res.StatusCode < 200 || res.StatusCode > 299 {
-		return nil, responseError(res)
-	}
 
 	var v struct {
 		Contact        []string
@@ -681,59 +692,6 @@ func (c *Client) doReg(ctx context.Context, url string, typ string, acct *Accoun
 	}, nil
 }
 
-// retryPostJWS will retry calls to postJWS if there is a badNonce error,
-// clearing the stored nonces after each error.
-// If the response was 4XX-5XX, then responseError is called on the body,
-// the body is closed, and the error returned.
-func (c *Client) retryPostJWS(ctx context.Context, key crypto.Signer, url string, body interface{}) (*http.Response, error) {
-	sleep := sleeper(ctx)
-	for {
-		res, err := c.postJWS(ctx, key, url, body)
-		if err != nil {
-			return nil, err
-		}
-		// handle errors 4XX-5XX with responseError
-		if res.StatusCode >= 400 && res.StatusCode <= 599 {
-			err := responseError(res)
-			res.Body.Close()
-			// according to spec badNonce is urn:ietf:params:acme:error:badNonce
-			// however, acme servers in the wild return their version of the error
-			// https://tools.ietf.org/html/draft-ietf-acme-acme-02#section-5.4
-			if ae, ok := err.(*Error); ok && strings.HasSuffix(strings.ToLower(ae.ProblemType), ":badnonce") {
-				// clear any nonces that we might've stored that might now be
-				// considered bad
-				c.clearNonces()
-				retry := res.Header.Get("Retry-After")
-				if err := sleep(retry, 1); err != nil {
-					return nil, err
-				}
-				continue
-			}
-			return nil, err
-		}
-		return res, nil
-	}
-}
-
-// postJWS signs the body with the given key and POSTs it to the provided url.
-// The body argument must be JSON-serializable.
-func (c *Client) postJWS(ctx context.Context, key crypto.Signer, url string, body interface{}) (*http.Response, error) {
-	nonce, err := c.popNonce(ctx, url)
-	if err != nil {
-		return nil, err
-	}
-	b, err := jwsEncodeJSON(body, key, nonce)
-	if err != nil {
-		return nil, err
-	}
-	res, err := c.post(ctx, url, "application/jose+json", bytes.NewReader(b))
-	if err != nil {
-		return nil, err
-	}
-	c.addNonce(res.Header)
-	return res, nil
-}
-
 // popNonce returns a nonce value previously stored with c.addNonce
 // or fetches a fresh one from the given URL.
 func (c *Client) popNonce(ctx context.Context, url string) (string, error) {
@@ -774,58 +732,12 @@ func (c *Client) addNonce(h http.Header) {
 	c.nonces[v] = struct{}{}
 }
 
-func (c *Client) httpClient() *http.Client {
-	if c.HTTPClient != nil {
-		return c.HTTPClient
-	}
-	return http.DefaultClient
-}
-
-func (c *Client) get(ctx context.Context, urlStr string) (*http.Response, error) {
-	req, err := http.NewRequest("GET", urlStr, nil)
-	if err != nil {
-		return nil, err
-	}
-	return c.do(ctx, req)
-}
-
-func (c *Client) head(ctx context.Context, urlStr string) (*http.Response, error) {
-	req, err := http.NewRequest("HEAD", urlStr, nil)
-	if err != nil {
-		return nil, err
-	}
-	return c.do(ctx, req)
-}
-
-func (c *Client) post(ctx context.Context, urlStr, contentType string, body io.Reader) (*http.Response, error) {
-	req, err := http.NewRequest("POST", urlStr, body)
-	if err != nil {
-		return nil, err
-	}
-	req.Header.Set("Content-Type", contentType)
-	return c.do(ctx, req)
-}
-
-func (c *Client) do(ctx context.Context, req *http.Request) (*http.Response, error) {
-	res, err := c.httpClient().Do(req.WithContext(ctx))
+func (c *Client) fetchNonce(ctx context.Context, url string) (string, error) {
+	r, err := http.NewRequest("HEAD", url, nil)
 	if err != nil {
-		select {
-		case <-ctx.Done():
-			// Prefer the unadorned context error.
-			// (The acme package had tests assuming this, previously from ctxhttp's
-			// behavior, predating net/http supporting contexts natively)
-			// TODO(bradfitz): reconsider this in the future. But for now this
-			// requires no test updates.
-			return nil, ctx.Err()
-		default:
-			return nil, err
-		}
+		return "", err
 	}
-	return res, nil
-}
-
-func (c *Client) fetchNonce(ctx context.Context, url string) (string, error) {
-	resp, err := c.head(ctx, url)
+	resp, err := c.doNoRetry(ctx, r)
 	if err != nil {
 		return "", err
 	}
@@ -877,24 +789,6 @@ func (c *Client) responseCert(ctx context.Context, res *http.Response, bundle bo
 	return cert, nil
 }
 
-// responseError creates an error of Error type from resp.
-func responseError(resp *http.Response) error {
-	// don't care if ReadAll returns an error:
-	// json.Unmarshal will fail in that case anyway
-	b, _ := ioutil.ReadAll(resp.Body)
-	e := &wireError{Status: resp.StatusCode}
-	if err := json.Unmarshal(b, e); err != nil {
-		// this is not a regular error response:
-		// populate detail with anything we received,
-		// e.Status will already contain HTTP response code value
-		e.Detail = string(b)
-		if e.Detail == "" {
-			e.Detail = resp.Status
-		}
-	}
-	return e.error(resp.Header)
-}
-
 // chainCert fetches CA certificate chain recursively by following "up" links.
 // Each recursive call increments the depth by 1, resulting in an error
 // if the recursion level reaches maxChainLen.
@@ -905,14 +799,11 @@ func (c *Client) chainCert(ctx context.Context, url string, depth int) ([][]byte
 		return nil, errors.New("acme: certificate chain is too deep")
 	}
 
-	res, err := c.get(ctx, url)
+	res, err := c.get(ctx, url, wantStatus(http.StatusOK))
 	if err != nil {
 		return nil, err
 	}
 	defer res.Body.Close()
-	if res.StatusCode != http.StatusOK {
-		return nil, responseError(res)
-	}
 	b, err := ioutil.ReadAll(io.LimitReader(res.Body, maxCertSize+1))
 	if err != nil {
 		return nil, err
@@ -957,65 +848,6 @@ func linkHeader(h http.Header, rel string) []string {
 	return links
 }
 
-// sleeper returns a function that accepts the Retry-After HTTP header value
-// and an increment that's used with backoff to increasingly sleep on
-// consecutive calls until the context is done. If the Retry-After header
-// cannot be parsed, then backoff is used with a maximum sleep time of 10
-// seconds.
-func sleeper(ctx context.Context) func(ra string, inc int) error {
-	var count int
-	return func(ra string, inc int) error {
-		count += inc
-		d := backoff(count, 10*time.Second)
-		d = retryAfter(ra, d)
-		wakeup := time.NewTimer(d)
-		defer wakeup.Stop()
-		select {
-		case <-ctx.Done():
-			return ctx.Err()
-		case <-wakeup.C:
-			return nil
-		}
-	}
-}
-
-// retryAfter parses a Retry-After HTTP header value,
-// trying to convert v into an int (seconds) or use http.ParseTime otherwise.
-// It returns d if v cannot be parsed.
-func retryAfter(v string, d time.Duration) time.Duration {
-	if i, err := strconv.Atoi(v); err == nil {
-		return time.Duration(i) * time.Second
-	}
-	t, err := http.ParseTime(v)
-	if err != nil {
-		return d
-	}
-	return t.Sub(timeNow())
-}
-
-// backoff computes a duration after which an n+1 retry iteration should occur
-// using truncated exponential backoff algorithm.
-//
-// The n argument is always bounded between 0 and 30.
-// The max argument defines upper bound for the returned value.
-func backoff(n int, max time.Duration) time.Duration {
-	if n < 0 {
-		n = 0
-	}
-	if n > 30 {
-		n = 30
-	}
-	var d time.Duration
-	if x, err := rand.Int(rand.Reader, big.NewInt(1000)); err == nil {
-		d = time.Duration(x.Int64()) * time.Millisecond
-	}
-	d += time.Duration(1<<uint(n)) * time.Second
-	if d > max {
-		return max
-	}
-	return d
-}
-
 // keyAuth generates a key authorization string for a given token.
 func keyAuth(pub crypto.PublicKey, token string) (string, error) {
 	th, err := JWKThumbprint(pub)
@@ -1025,14 +857,25 @@ func keyAuth(pub crypto.PublicKey, token string) (string, error) {
 	return fmt.Sprintf("%s.%s", token, th), nil
 }
 
+// defaultTLSChallengeCertTemplate is a template used to create challenge certs for TLS challenges.
+func defaultTLSChallengeCertTemplate() *x509.Certificate {
+	return &x509.Certificate{
+		SerialNumber:          big.NewInt(1),
+		NotBefore:             time.Now(),
+		NotAfter:              time.Now().Add(24 * time.Hour),
+		BasicConstraintsValid: true,
+		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
+		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
+	}
+}
+
 // tlsChallengeCert creates a temporary certificate for TLS-SNI challenges
 // with the given SANs and auto-generated public/private key pair.
+// The Subject Common Name is set to the first SAN to aid debugging.
 // To create a cert with a custom key pair, specify WithKey option.
 func tlsChallengeCert(san []string, opt []CertOption) (tls.Certificate, error) {
-	var (
-		key  crypto.Signer
-		tmpl *x509.Certificate
-	)
+	var key crypto.Signer
+	tmpl := defaultTLSChallengeCertTemplate()
 	for _, o := range opt {
 		switch o := o.(type) {
 		case *certOptKey:
@@ -1041,7 +884,7 @@ func tlsChallengeCert(san []string, opt []CertOption) (tls.Certificate, error) {
 			}
 			key = o.key
 		case *certOptTemplate:
-			var t = *(*x509.Certificate)(o) // shallow copy is ok
+			t := *(*x509.Certificate)(o) // shallow copy is ok
 			tmpl = &t
 		default:
 			// package's fault, if we let this happen:
@@ -1054,17 +897,10 @@ func tlsChallengeCert(san []string, opt []CertOption) (tls.Certificate, error) {
 			return tls.Certificate{}, err
 		}
 	}
-	if tmpl == nil {
-		tmpl = &x509.Certificate{
-			SerialNumber:          big.NewInt(1),
-			NotBefore:             time.Now(),
-			NotAfter:              time.Now().Add(24 * time.Hour),
-			BasicConstraintsValid: true,
-			KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
-			ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
-		}
-	}
 	tmpl.DNSNames = san
+	if len(san) > 0 {
+		tmpl.Subject.CommonName = san[0]
+	}
 
 	der, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, key.Public(), key)
 	if err != nil {

+ 140 - 173
psiphon/common/crypto/acme/acme_test.go

@@ -13,9 +13,9 @@ import (
 	"crypto/x509"
 	"crypto/x509/pkix"
 	"encoding/base64"
+	"encoding/hex"
 	"encoding/json"
 	"fmt"
-	"io/ioutil"
 	"math/big"
 	"net/http"
 	"net/http/httptest"
@@ -485,95 +485,98 @@ func TestGetAuthorization(t *testing.T) {
 }
 
 func TestWaitAuthorization(t *testing.T) {
-	var count int
-	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		count++
-		w.Header().Set("Retry-After", "0")
-		if count > 1 {
-			fmt.Fprintf(w, `{"status":"valid"}`)
-			return
+	t.Run("wait loop", func(t *testing.T) {
+		var count int
+		authz, err := runWaitAuthorization(context.Background(), t, func(w http.ResponseWriter, r *http.Request) {
+			count++
+			w.Header().Set("Retry-After", "0")
+			if count > 1 {
+				fmt.Fprintf(w, `{"status":"valid"}`)
+				return
+			}
+			fmt.Fprintf(w, `{"status":"pending"}`)
+		})
+		if err != nil {
+			t.Fatalf("non-nil error: %v", err)
 		}
-		fmt.Fprintf(w, `{"status":"pending"}`)
-	}))
-	defer ts.Close()
-
-	type res struct {
-		authz *Authorization
-		err   error
-	}
-	done := make(chan res)
-	defer close(done)
-	go func() {
-		var client Client
-		a, err := client.WaitAuthorization(context.Background(), ts.URL)
-		done <- res{a, err}
-	}()
-
-	select {
-	case <-time.After(5 * time.Second):
-		t.Fatal("WaitAuthz took too long to return")
-	case res := <-done:
-		if res.err != nil {
-			t.Fatalf("res.err =  %v", res.err)
+		if authz == nil {
+			t.Fatal("authz is nil")
 		}
-		if res.authz == nil {
-			t.Fatal("res.authz is nil")
+	})
+	t.Run("invalid status", func(t *testing.T) {
+		_, err := runWaitAuthorization(context.Background(), t, func(w http.ResponseWriter, r *http.Request) {
+			fmt.Fprintf(w, `{"status":"invalid"}`)
+		})
+		if _, ok := err.(*AuthorizationError); !ok {
+			t.Errorf("err is %v (%T); want non-nil *AuthorizationError", err, err)
 		}
+	})
+	t.Run("non-retriable error", func(t *testing.T) {
+		const code = http.StatusBadRequest
+		_, err := runWaitAuthorization(context.Background(), t, func(w http.ResponseWriter, r *http.Request) {
+			w.WriteHeader(code)
+		})
+		res, ok := err.(*Error)
+		if !ok {
+			t.Fatalf("err is %v (%T); want a non-nil *Error", err, err)
+		}
+		if res.StatusCode != code {
+			t.Errorf("res.StatusCode = %d; want %d", res.StatusCode, code)
+		}
+	})
+	for _, code := range []int{http.StatusTooManyRequests, http.StatusInternalServerError} {
+		t.Run(fmt.Sprintf("retriable %d error", code), func(t *testing.T) {
+			var count int
+			authz, err := runWaitAuthorization(context.Background(), t, func(w http.ResponseWriter, r *http.Request) {
+				count++
+				w.Header().Set("Retry-After", "0")
+				if count > 1 {
+					fmt.Fprintf(w, `{"status":"valid"}`)
+					return
+				}
+				w.WriteHeader(code)
+			})
+			if err != nil {
+				t.Fatalf("non-nil error: %v", err)
+			}
+			if authz == nil {
+				t.Fatal("authz is nil")
+			}
+		})
 	}
-}
-
-func TestWaitAuthorizationInvalid(t *testing.T) {
-	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		fmt.Fprintf(w, `{"status":"invalid"}`)
-	}))
-	defer ts.Close()
-
-	res := make(chan error)
-	defer close(res)
-	go func() {
-		var client Client
-		_, err := client.WaitAuthorization(context.Background(), ts.URL)
-		res <- err
-	}()
-
-	select {
-	case <-time.After(3 * time.Second):
-		t.Fatal("WaitAuthz took too long to return")
-	case err := <-res:
+	t.Run("context cancel", func(t *testing.T) {
+		ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond)
+		defer cancel()
+		_, err := runWaitAuthorization(ctx, t, func(w http.ResponseWriter, r *http.Request) {
+			w.Header().Set("Retry-After", "60")
+			fmt.Fprintf(w, `{"status":"pending"}`)
+		})
 		if err == nil {
 			t.Error("err is nil")
 		}
-		if _, ok := err.(*AuthorizationError); !ok {
-			t.Errorf("err is %T; want *AuthorizationError", err)
-		}
-	}
+	})
 }
-
-func TestWaitAuthorizationCancel(t *testing.T) {
-	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		w.Header().Set("Retry-After", "60")
-		fmt.Fprintf(w, `{"status":"pending"}`)
-	}))
+func runWaitAuthorization(ctx context.Context, t *testing.T, h http.HandlerFunc) (*Authorization, error) {
+	t.Helper()
+	ts := httptest.NewServer(h)
 	defer ts.Close()
-
-	res := make(chan error)
-	defer close(res)
+	type res struct {
+		authz *Authorization
+		err   error
+	}
+	ch := make(chan res, 1)
 	go func() {
 		var client Client
-		ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond)
-		defer cancel()
-		_, err := client.WaitAuthorization(ctx, ts.URL)
-		res <- err
+		a, err := client.WaitAuthorization(ctx, ts.URL)
+		ch <- res{a, err}
 	}()
-
 	select {
-	case <-time.After(time.Second):
-		t.Fatal("WaitAuthz took too long to return")
-	case err := <-res:
-		if err == nil {
-			t.Error("err is nil")
-		}
+	case <-time.After(3 * time.Second):
+		t.Fatal("WaitAuthorization took too long to return")
+	case v := <-ch:
+		return v.authz, v.err
 	}
+	panic("runWaitAuthorization: out of select")
 }
 
 func TestRevokeAuthorization(t *testing.T) {
@@ -600,7 +603,7 @@ func TestRevokeAuthorization(t *testing.T) {
 				t.Errorf("req.Delete is false")
 			}
 		case "/2":
-			w.WriteHeader(http.StatusInternalServerError)
+			w.WriteHeader(http.StatusBadRequest)
 		}
 	}))
 	defer ts.Close()
@@ -821,7 +824,7 @@ func TestFetchCertRetry(t *testing.T) {
 	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		if count < 1 {
 			w.Header().Set("Retry-After", "0")
-			w.WriteHeader(http.StatusAccepted)
+			w.WriteHeader(http.StatusTooManyRequests)
 			count++
 			return
 		}
@@ -946,7 +949,7 @@ func TestNonce_add(t *testing.T) {
 	c.addNonce(http.Header{"Replay-Nonce": {}})
 	c.addNonce(http.Header{"Replay-Nonce": {"nonce"}})
 
-	nonces := map[string]struct{}{"nonce": struct{}{}}
+	nonces := map[string]struct{}{"nonce": {}}
 	if !reflect.DeepEqual(c.nonces, nonces) {
 		t.Errorf("c.nonces = %q; want %q", c.nonces, nonces)
 	}
@@ -1068,44 +1071,6 @@ func TestNonce_postJWS(t *testing.T) {
 	}
 }
 
-func TestRetryPostJWS(t *testing.T) {
-	var count int
-	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		count++
-		w.Header().Set("Replay-Nonce", fmt.Sprintf("nonce%d", count))
-		if r.Method == "HEAD" {
-			// We expect the client to do 2 head requests to fetch
-			// nonces, one to start and another after getting badNonce
-			return
-		}
-
-		head, err := decodeJWSHead(r)
-		if err != nil {
-			t.Errorf("decodeJWSHead: %v", err)
-		} else if head.Nonce == "" {
-			t.Error("head.Nonce is empty")
-		} else if head.Nonce == "nonce1" {
-			// return a badNonce error to force the call to retry
-			w.WriteHeader(http.StatusBadRequest)
-			w.Write([]byte(`{"type":"urn:ietf:params:acme:error:badNonce"}`))
-			return
-		}
-		// Make client.Authorize happy; we're not testing its result.
-		w.WriteHeader(http.StatusCreated)
-		w.Write([]byte(`{"status":"valid"}`))
-	}))
-	defer ts.Close()
-
-	client := Client{Key: testKey, dir: &Directory{AuthzURL: ts.URL}}
-	// This call will fail with badNonce, causing a retry
-	if _, err := client.Authorize(context.Background(), "example.com"); err != nil {
-		t.Errorf("client.Authorize 1: %v", err)
-	}
-	if count != 4 {
-		t.Errorf("total requests count: %d; want 4", count)
-	}
-}
-
 func TestLinkHeader(t *testing.T) {
 	h := http.Header{"Link": {
 		`<https://example.com/acme/new-authz>;rel="next"`,
@@ -1129,37 +1094,6 @@ func TestLinkHeader(t *testing.T) {
 	}
 }
 
-func TestErrorResponse(t *testing.T) {
-	s := `{
-		"status": 400,
-		"type": "urn:acme:error:xxx",
-		"detail": "text"
-	}`
-	res := &http.Response{
-		StatusCode: 400,
-		Status:     "400 Bad Request",
-		Body:       ioutil.NopCloser(strings.NewReader(s)),
-		Header:     http.Header{"X-Foo": {"bar"}},
-	}
-	err := responseError(res)
-	v, ok := err.(*Error)
-	if !ok {
-		t.Fatalf("err = %+v (%T); want *Error type", err, err)
-	}
-	if v.StatusCode != 400 {
-		t.Errorf("v.StatusCode = %v; want 400", v.StatusCode)
-	}
-	if v.ProblemType != "urn:acme:error:xxx" {
-		t.Errorf("v.ProblemType = %q; want urn:acme:error:xxx", v.ProblemType)
-	}
-	if v.Detail != "text" {
-		t.Errorf("v.Detail = %q; want text", v.Detail)
-	}
-	if !reflect.DeepEqual(v.Header, res.Header) {
-		t.Errorf("v.Header = %+v; want %+v", v.Header, res.Header)
-	}
-}
-
 func TestTLSSNI01ChallengeCert(t *testing.T) {
 	const (
 		token = "evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA"
@@ -1186,6 +1120,9 @@ func TestTLSSNI01ChallengeCert(t *testing.T) {
 	if cert.DNSNames[0] != name {
 		t.Errorf("cert.DNSNames[0] != name: %q vs %q", cert.DNSNames[0], name)
 	}
+	if cn := cert.Subject.CommonName; cn != san {
+		t.Errorf("cert.Subject.CommonName = %q; want %q", cn, san)
+	}
 }
 
 func TestTLSSNI02ChallengeCert(t *testing.T) {
@@ -1219,6 +1156,61 @@ func TestTLSSNI02ChallengeCert(t *testing.T) {
 	if i >= len(cert.DNSNames) || cert.DNSNames[i] != name {
 		t.Errorf("%v doesn't have %q", cert.DNSNames, name)
 	}
+	if cn := cert.Subject.CommonName; cn != sanA {
+		t.Errorf("CommonName = %q; want %q", cn, sanA)
+	}
+}
+
+func TestTLSALPN01ChallengeCert(t *testing.T) {
+	const (
+		token   = "evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA"
+		keyAuth = "evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA." + testKeyECThumbprint
+		// echo -n <token.testKeyECThumbprint> | shasum -a 256
+		h      = "0420dbbd5eefe7b4d06eb9d1d9f5acb4c7cda27d320e4b30332f0b6cb441734ad7b0"
+		domain = "example.com"
+	)
+
+	extValue, err := hex.DecodeString(h)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	client := &Client{Key: testKeyEC}
+	tlscert, err := client.TLSALPN01ChallengeCert(token, domain)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if n := len(tlscert.Certificate); n != 1 {
+		t.Fatalf("len(tlscert.Certificate) = %d; want 1", n)
+	}
+	cert, err := x509.ParseCertificate(tlscert.Certificate[0])
+	if err != nil {
+		t.Fatal(err)
+	}
+	names := []string{domain}
+	if !reflect.DeepEqual(cert.DNSNames, names) {
+		t.Fatalf("cert.DNSNames = %v;\nwant %v", cert.DNSNames, names)
+	}
+	if cn := cert.Subject.CommonName; cn != domain {
+		t.Errorf("CommonName = %q; want %q", cn, domain)
+	}
+	acmeExts := []pkix.Extension{}
+	for _, ext := range cert.Extensions {
+		if idPeACMEIdentifierV1.Equal(ext.Id) {
+			acmeExts = append(acmeExts, ext)
+		}
+	}
+	if len(acmeExts) != 1 {
+		t.Errorf("acmeExts = %v; want exactly one", acmeExts)
+	}
+	if !acmeExts[0].Critical {
+		t.Errorf("acmeExt.Critical = %v; want true", acmeExts[0].Critical)
+	}
+	if bytes.Compare(acmeExts[0].Value, extValue) != 0 {
+		t.Errorf("acmeExt.Value = %v; want %v", acmeExts[0].Value, extValue)
+	}
+
 }
 
 func TestTLSChallengeCertOpt(t *testing.T) {
@@ -1319,28 +1311,3 @@ func TestDNS01ChallengeRecord(t *testing.T) {
 		t.Errorf("val = %q; want %q", val, value)
 	}
 }
-
-func TestBackoff(t *testing.T) {
-	tt := []struct{ min, max time.Duration }{
-		{time.Second, 2 * time.Second},
-		{2 * time.Second, 3 * time.Second},
-		{4 * time.Second, 5 * time.Second},
-		{8 * time.Second, 9 * time.Second},
-	}
-	for i, test := range tt {
-		d := backoff(i, time.Minute)
-		if d < test.min || test.max < d {
-			t.Errorf("%d: d = %v; want between %v and %v", i, d, test.min, test.max)
-		}
-	}
-
-	min, max := time.Second, 2*time.Second
-	if d := backoff(-1, time.Minute); d < min || max < d {
-		t.Errorf("d = %v; want between %v and %v", d, min, max)
-	}
-
-	bound := 10 * time.Second
-	if d := backoff(100, bound); d != bound {
-		t.Errorf("d = %v; want %v", d, bound)
-	}
-}

+ 484 - 164
psiphon/common/crypto/acme/autocert/autocert.go

@@ -24,8 +24,9 @@ import (
 	"fmt"
 	"io"
 	mathrand "math/rand"
+	"net"
 	"net/http"
-	"strconv"
+	"path"
 	"strings"
 	"sync"
 	"time"
@@ -43,7 +44,7 @@ var createCertRetryAfter = time.Minute
 var pseudoRand *lockedMathRand
 
 func init() {
-	src := mathrand.NewSource(timeNow().UnixNano())
+	src := mathrand.NewSource(time.Now().UnixNano())
 	pseudoRand = &lockedMathRand{rnd: mathrand.New(src)}
 }
 
@@ -80,11 +81,14 @@ func defaultHostPolicy(context.Context, string) error {
 }
 
 // Manager is a stateful certificate manager built on top of acme.Client.
-// It obtains and refreshes certificates automatically,
+// It obtains and refreshes certificates automatically using "tls-alpn-01",
+// "tls-sni-01", "tls-sni-02" and "http-01" challenge types,
 // as well as providing them to a TLS server via tls.Config.
 //
-// To preserve issued certificates and improve overall performance,
-// use a cache implementation of Cache. For instance, DirCache.
+// You must specify a cache implementation, such as DirCache,
+// to reuse obtained certificates across program restarts.
+// Otherwise your server is very likely to exceed the certificate
+// issuer's request rate limits.
 type Manager struct {
 	// Prompt specifies a callback function to conditionally accept a CA's Terms of Service (TOS).
 	// The registration may require the caller to agree to the CA's TOS.
@@ -94,11 +98,11 @@ type Manager struct {
 	// To always accept the terms, the callers can use AcceptTOS.
 	Prompt func(tosURL string) bool
 
-	// Cache optionally stores and retrieves previously-obtained certificates.
-	// If nil, certs will only be cached for the lifetime of the Manager.
+	// Cache optionally stores and retrieves previously-obtained certificates
+	// and other state. If nil, certs will only be cached for the lifetime of
+	// the Manager. Multiple Managers can share the same Cache.
 	//
-	// Manager passes the Cache certificates data encoded in PEM, with private/public
-	// parts combined in a single Cache.Put call, private key first.
+	// Using a persistent Cache, such as DirCache, is strongly recommended.
 	Cache Cache
 
 	// HostPolicy controls which domains the Manager will attempt
@@ -123,8 +127,10 @@ type Manager struct {
 
 	// Client is used to perform low-level operations, such as account registration
 	// and requesting new certificates.
+	//
 	// If Client is nil, a zero-value acme.Client is used with acme.LetsEncryptURL
-	// directory endpoint and a newly-generated ECDSA P-256 key.
+	// as directory endpoint. If the Client.Key is nil, a new ECDSA P-256 key is
+	// generated and, if Cache is not nil, stored in cache.
 	//
 	// Mutating the field after the first call of GetCertificate method will have no effect.
 	Client *acme.Client
@@ -136,37 +142,95 @@ type Manager struct {
 	// If the Client's account key is already registered, Email is not used.
 	Email string
 
-	// ForceRSA makes the Manager generate certificates with 2048-bit RSA keys.
+	// ForceRSA used to make the Manager generate RSA certificates. It is now ignored.
 	//
-	// If false, a default is used. Currently the default
-	// is EC-based keys using the P-256 curve.
+	// Deprecated: the Manager will request the correct type of certificate based
+	// on what each client supports.
 	ForceRSA bool
 
+	// ExtraExtensions are used when generating a new CSR (Certificate Request),
+	// thus allowing customization of the resulting certificate.
+	// For instance, TLS Feature Extension (RFC 7633) can be used
+	// to prevent an OCSP downgrade attack.
+	//
+	// The field value is passed to crypto/x509.CreateCertificateRequest
+	// in the template's ExtraExtensions field as is.
+	ExtraExtensions []pkix.Extension
+
 	clientMu sync.Mutex
 	client   *acme.Client // initialized by acmeClient method
 
 	stateMu sync.Mutex
-	state   map[string]*certState // keyed by domain name
-
-	// tokenCert is keyed by token domain name, which matches server name
-	// of ClientHello. Keys always have ".acme.invalid" suffix.
-	tokenCertMu sync.RWMutex
-	tokenCert   map[string]*tls.Certificate
+	state   map[certKey]*certState
 
 	// renewal tracks the set of domains currently running renewal timers.
-	// It is keyed by domain name.
 	renewalMu sync.Mutex
-	renewal   map[string]*domainRenewal
+	renewal   map[certKey]*domainRenewal
+
+	// tokensMu guards the rest of the fields: tryHTTP01, certTokens and httpTokens.
+	tokensMu sync.RWMutex
+	// tryHTTP01 indicates whether the Manager should try "http-01" challenge type
+	// during the authorization flow.
+	tryHTTP01 bool
+	// httpTokens contains response body values for http-01 challenges
+	// and is keyed by the URL path at which a challenge response is expected
+	// to be provisioned.
+	// The entries are stored for the duration of the authorization flow.
+	httpTokens map[string][]byte
+	// certTokens contains temporary certificates for tls-sni and tls-alpn challenges
+	// and is keyed by token domain name, which matches server name of ClientHello.
+	// Keys always have ".acme.invalid" suffix for tls-sni. Otherwise, they are domain names
+	// for tls-alpn.
+	// The entries are stored for the duration of the authorization flow.
+	certTokens map[string]*tls.Certificate
+	// nowFunc, if not nil, returns the current time. This may be set for
+	// testing purposes.
+	nowFunc func() time.Time
+}
+
+// certKey is the key by which certificates are tracked in state, renewal and cache.
+type certKey struct {
+	domain  string // without trailing dot
+	isRSA   bool   // RSA cert for legacy clients (as opposed to default ECDSA)
+	isToken bool   // tls-based challenge token cert; key type is undefined regardless of isRSA
+}
+
+func (c certKey) String() string {
+	if c.isToken {
+		return c.domain + "+token"
+	}
+	if c.isRSA {
+		return c.domain + "+rsa"
+	}
+	return c.domain
+}
+
+// TLSConfig creates a new TLS config suitable for net/http.Server servers,
+// supporting HTTP/2 and the tls-alpn-01 ACME challenge type.
+func (m *Manager) TLSConfig() *tls.Config {
+	return &tls.Config{
+		GetCertificate: m.GetCertificate,
+		NextProtos: []string{
+			"h2", "http/1.1", // enable HTTP/2
+			acme.ALPNProto, // enable tls-alpn ACME challenges
+		},
+	}
 }
 
 // GetCertificate implements the tls.Config.GetCertificate hook.
 // It provides a TLS certificate for hello.ServerName host, including answering
-// *.acme.invalid (TLS-SNI) challenges. All other fields of hello are ignored.
+// tls-alpn-01 and *.acme.invalid (tls-sni-01 and tls-sni-02) challenges.
+// All other fields of hello are ignored.
 //
 // If m.HostPolicy is non-nil, GetCertificate calls the policy before requesting
 // a new cert. A non-nil error returned from m.HostPolicy halts TLS negotiation.
 // The error is propagated back to the caller of GetCertificate and is user-visible.
 // This does not affect cached certs. See HostPolicy field description for more details.
+//
+// If GetCertificate is used directly, instead of via Manager.TLSConfig, package users will
+// also have to add acme.ALPNProto to NextProtos for tls-alpn-01, or use HTTPHandler
+// for http-01. (The tls-sni-* challenges have been deprecated by popular ACME providers
+// due to security issues in the ecosystem.)
 func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
 	if m.Prompt == nil {
 		return nil, errors.New("acme/autocert: Manager.Prompt not set")
@@ -179,21 +243,26 @@ func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate,
 	if !strings.Contains(strings.Trim(name, "."), ".") {
 		return nil, errors.New("acme/autocert: server name component count invalid")
 	}
-	if strings.ContainsAny(name, `/\`) {
+	if strings.ContainsAny(name, `+/\`) {
 		return nil, errors.New("acme/autocert: server name contains invalid character")
 	}
 
+	// In the worst-case scenario, the timeout needs to account for caching, host policy,
+	// domain ownership verification and certificate issuance.
 	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
 	defer cancel()
 
-	// check whether this is a token cert requested for TLS-SNI challenge
-	if strings.HasSuffix(name, ".acme.invalid") {
-		m.tokenCertMu.RLock()
-		defer m.tokenCertMu.RUnlock()
-		if cert := m.tokenCert[name]; cert != nil {
+	// Check whether this is a token cert requested for TLS-SNI or TLS-ALPN challenge.
+	if wantsTokenCert(hello) {
+		m.tokensMu.RLock()
+		defer m.tokensMu.RUnlock()
+		// It's ok to use the same token cert key for both tls-sni and tls-alpn
+		// because there's always at most 1 token cert per on-going domain authorization.
+		// See m.verify for details.
+		if cert := m.certTokens[name]; cert != nil {
 			return cert, nil
 		}
-		if cert, err := m.cacheGet(ctx, name); err == nil {
+		if cert, err := m.cacheGet(ctx, certKey{domain: name, isToken: true}); err == nil {
 			return cert, nil
 		}
 		// TODO: cache error results?
@@ -201,8 +270,11 @@ func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate,
 	}
 
 	// regular domain
-	name = strings.TrimSuffix(name, ".") // golang.org/issue/18114
-	cert, err := m.cert(ctx, name)
+	ck := certKey{
+		domain: strings.TrimSuffix(name, "."), // golang.org/issue/18114
+		isRSA:  !supportsECDSA(hello),
+	}
+	cert, err := m.cert(ctx, ck)
 	if err == nil {
 		return cert, nil
 	}
@@ -214,27 +286,146 @@ func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate,
 	if err := m.hostPolicy()(ctx, name); err != nil {
 		return nil, err
 	}
-	cert, err = m.createCert(ctx, name)
+	cert, err = m.createCert(ctx, ck)
 	if err != nil {
 		return nil, err
 	}
-	m.cachePut(ctx, name, cert)
+	m.cachePut(ctx, ck, cert)
 	return cert, nil
 }
 
+// wantsTokenCert reports whether a TLS request with SNI is made by a CA server
+// for a challenge verification.
+func wantsTokenCert(hello *tls.ClientHelloInfo) bool {
+	// tls-alpn-01
+	if len(hello.SupportedProtos) == 1 && hello.SupportedProtos[0] == acme.ALPNProto {
+		return true
+	}
+	// tls-sni-xx
+	return strings.HasSuffix(hello.ServerName, ".acme.invalid")
+}
+
+func supportsECDSA(hello *tls.ClientHelloInfo) bool {
+	// The "signature_algorithms" extension, if present, limits the key exchange
+	// algorithms allowed by the cipher suites. See RFC 5246, section 7.4.1.4.1.
+	if hello.SignatureSchemes != nil {
+		ecdsaOK := false
+	schemeLoop:
+		for _, scheme := range hello.SignatureSchemes {
+			const tlsECDSAWithSHA1 tls.SignatureScheme = 0x0203 // constant added in Go 1.10
+			switch scheme {
+			case tlsECDSAWithSHA1, tls.ECDSAWithP256AndSHA256,
+				tls.ECDSAWithP384AndSHA384, tls.ECDSAWithP521AndSHA512:
+				ecdsaOK = true
+				break schemeLoop
+			}
+		}
+		if !ecdsaOK {
+			return false
+		}
+	}
+	if hello.SupportedCurves != nil {
+		ecdsaOK := false
+		for _, curve := range hello.SupportedCurves {
+			if curve == tls.CurveP256 {
+				ecdsaOK = true
+				break
+			}
+		}
+		if !ecdsaOK {
+			return false
+		}
+	}
+	for _, suite := range hello.CipherSuites {
+		switch suite {
+		case tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
+			tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
+			tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
+			tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
+			tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+			tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
+			tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305:
+			return true
+		}
+	}
+	return false
+}
+
+// HTTPHandler configures the Manager to provision ACME "http-01" challenge responses.
+// It returns an http.Handler that responds to the challenges and must be
+// running on port 80. If it receives a request that is not an ACME challenge,
+// it delegates the request to the optional fallback handler.
+//
+// If fallback is nil, the returned handler redirects all GET and HEAD requests
+// to the default TLS port 443 with 302 Found status code, preserving the original
+// request path and query. It responds with 400 Bad Request to all other HTTP methods.
+// The fallback is not protected by the optional HostPolicy.
+//
+// Because the fallback handler is run with unencrypted port 80 requests,
+// the fallback should not serve TLS-only requests.
+//
+// If HTTPHandler is never called, the Manager will only use the "tls-alpn-01"
+// challenge for domain verification.
+func (m *Manager) HTTPHandler(fallback http.Handler) http.Handler {
+	m.tokensMu.Lock()
+	defer m.tokensMu.Unlock()
+	m.tryHTTP01 = true
+
+	if fallback == nil {
+		fallback = http.HandlerFunc(handleHTTPRedirect)
+	}
+	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		if !strings.HasPrefix(r.URL.Path, "/.well-known/acme-challenge/") {
+			fallback.ServeHTTP(w, r)
+			return
+		}
+		// A reasonable context timeout for cache and host policy only,
+		// because we don't wait for a new certificate issuance here.
+		ctx, cancel := context.WithTimeout(r.Context(), time.Minute)
+		defer cancel()
+		if err := m.hostPolicy()(ctx, r.Host); err != nil {
+			http.Error(w, err.Error(), http.StatusForbidden)
+			return
+		}
+		data, err := m.httpToken(ctx, r.URL.Path)
+		if err != nil {
+			http.Error(w, err.Error(), http.StatusNotFound)
+			return
+		}
+		w.Write(data)
+	})
+}
+
+func handleHTTPRedirect(w http.ResponseWriter, r *http.Request) {
+	if r.Method != "GET" && r.Method != "HEAD" {
+		http.Error(w, "Use HTTPS", http.StatusBadRequest)
+		return
+	}
+	target := "https://" + stripPort(r.Host) + r.URL.RequestURI()
+	http.Redirect(w, r, target, http.StatusFound)
+}
+
+func stripPort(hostport string) string {
+	host, _, err := net.SplitHostPort(hostport)
+	if err != nil {
+		return hostport
+	}
+	return net.JoinHostPort(host, "443")
+}
+
 // cert returns an existing certificate either from m.state or cache.
 // If a certificate is found in cache but not in m.state, the latter will be filled
 // with the cached value.
-func (m *Manager) cert(ctx context.Context, name string) (*tls.Certificate, error) {
+func (m *Manager) cert(ctx context.Context, ck certKey) (*tls.Certificate, error) {
 	m.stateMu.Lock()
-	if s, ok := m.state[name]; ok {
+	if s, ok := m.state[ck]; ok {
 		m.stateMu.Unlock()
 		s.RLock()
 		defer s.RUnlock()
 		return s.tlscert()
 	}
 	defer m.stateMu.Unlock()
-	cert, err := m.cacheGet(ctx, name)
+	cert, err := m.cacheGet(ctx, ck)
 	if err != nil {
 		return nil, err
 	}
@@ -243,25 +434,25 @@ func (m *Manager) cert(ctx context.Context, name string) (*tls.Certificate, erro
 		return nil, errors.New("acme/autocert: private key cannot sign")
 	}
 	if m.state == nil {
-		m.state = make(map[string]*certState)
+		m.state = make(map[certKey]*certState)
 	}
 	s := &certState{
 		key:  signer,
 		cert: cert.Certificate,
 		leaf: cert.Leaf,
 	}
-	m.state[name] = s
-	go m.renew(name, s.key, s.leaf.NotAfter)
+	m.state[ck] = s
+	go m.renew(ck, s.key, s.leaf.NotAfter)
 	return cert, nil
 }
 
 // cacheGet always returns a valid certificate, or an error otherwise.
-// If a cached certficate exists but is not valid, ErrCacheMiss is returned.
-func (m *Manager) cacheGet(ctx context.Context, domain string) (*tls.Certificate, error) {
+// If a cached certificate exists but is not valid, ErrCacheMiss is returned.
+func (m *Manager) cacheGet(ctx context.Context, ck certKey) (*tls.Certificate, error) {
 	if m.Cache == nil {
 		return nil, ErrCacheMiss
 	}
-	data, err := m.Cache.Get(ctx, domain)
+	data, err := m.Cache.Get(ctx, ck.String())
 	if err != nil {
 		return nil, err
 	}
@@ -292,7 +483,7 @@ func (m *Manager) cacheGet(ctx context.Context, domain string) (*tls.Certificate
 	}
 
 	// verify and create TLS cert
-	leaf, err := validCert(domain, pubDER, privKey)
+	leaf, err := validCert(ck, pubDER, privKey, m.now())
 	if err != nil {
 		return nil, ErrCacheMiss
 	}
@@ -304,7 +495,7 @@ func (m *Manager) cacheGet(ctx context.Context, domain string) (*tls.Certificate
 	return tlscert, nil
 }
 
-func (m *Manager) cachePut(ctx context.Context, domain string, tlscert *tls.Certificate) error {
+func (m *Manager) cachePut(ctx context.Context, ck certKey, tlscert *tls.Certificate) error {
 	if m.Cache == nil {
 		return nil
 	}
@@ -336,7 +527,7 @@ func (m *Manager) cachePut(ctx context.Context, domain string, tlscert *tls.Cert
 		}
 	}
 
-	return m.Cache.Put(ctx, domain, buf.Bytes())
+	return m.Cache.Put(ctx, ck.String(), buf.Bytes())
 }
 
 func encodeECDSAKey(w io.Writer, key *ecdsa.PrivateKey) error {
@@ -353,9 +544,9 @@ func encodeECDSAKey(w io.Writer, key *ecdsa.PrivateKey) error {
 //
 // If the domain is already being verified, it waits for the existing verification to complete.
 // Either way, createCert blocks for the duration of the whole process.
-func (m *Manager) createCert(ctx context.Context, domain string) (*tls.Certificate, error) {
+func (m *Manager) createCert(ctx context.Context, ck certKey) (*tls.Certificate, error) {
 	// TODO: maybe rewrite this whole piece using sync.Once
-	state, err := m.certState(domain)
+	state, err := m.certState(ck)
 	if err != nil {
 		return nil, err
 	}
@@ -369,48 +560,48 @@ func (m *Manager) createCert(ctx context.Context, domain string) (*tls.Certifica
 
 	// We are the first; state is locked.
 	// Unblock the readers when domain ownership is verified
-	// and the we got the cert or the process failed.
+	// and we got the cert or the process failed.
 	defer state.Unlock()
 	state.locked = false
 
-	der, leaf, err := m.authorizedCert(ctx, state.key, domain)
+	der, leaf, err := m.authorizedCert(ctx, state.key, ck)
 	if err != nil {
 		// Remove the failed state after some time,
 		// making the manager call createCert again on the following TLS hello.
 		time.AfterFunc(createCertRetryAfter, func() {
-			defer testDidRemoveState(domain)
+			defer testDidRemoveState(ck)
 			m.stateMu.Lock()
 			defer m.stateMu.Unlock()
 			// Verify the state hasn't changed and it's still invalid
 			// before deleting.
-			s, ok := m.state[domain]
+			s, ok := m.state[ck]
 			if !ok {
 				return
 			}
-			if _, err := validCert(domain, s.cert, s.key); err == nil {
+			if _, err := validCert(ck, s.cert, s.key, m.now()); err == nil {
 				return
 			}
-			delete(m.state, domain)
+			delete(m.state, ck)
 		})
 		return nil, err
 	}
 	state.cert = der
 	state.leaf = leaf
-	go m.renew(domain, state.key, state.leaf.NotAfter)
+	go m.renew(ck, state.key, state.leaf.NotAfter)
 	return state.tlscert()
 }
 
 // certState returns a new or existing certState.
 // If a new certState is returned, state.exist is false and the state is locked.
 // The returned error is non-nil only in the case where a new state could not be created.
-func (m *Manager) certState(domain string) (*certState, error) {
+func (m *Manager) certState(ck certKey) (*certState, error) {
 	m.stateMu.Lock()
 	defer m.stateMu.Unlock()
 	if m.state == nil {
-		m.state = make(map[string]*certState)
+		m.state = make(map[certKey]*certState)
 	}
 	// existing state
-	if state, ok := m.state[domain]; ok {
+	if state, ok := m.state[ck]; ok {
 		return state, nil
 	}
 
@@ -419,7 +610,7 @@ func (m *Manager) certState(domain string) (*certState, error) {
 		err error
 		key crypto.Signer
 	)
-	if m.ForceRSA {
+	if ck.isRSA {
 		key, err = rsa.GenerateKey(rand.Reader, 2048)
 	} else {
 		key, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
@@ -433,21 +624,22 @@ func (m *Manager) certState(domain string) (*certState, error) {
 		locked: true,
 	}
 	state.Lock() // will be unlocked by m.certState caller
-	m.state[domain] = state
+	m.state[ck] = state
 	return state, nil
 }
 
-// authorizedCert starts domain ownership verification process and requests a new cert upon success.
+// authorizedCert starts the domain ownership verification process and requests a new cert upon success.
 // The key argument is the certificate private key.
-func (m *Manager) authorizedCert(ctx context.Context, key crypto.Signer, domain string) (der [][]byte, leaf *x509.Certificate, err error) {
-	if err := m.verify(ctx, domain); err != nil {
-		return nil, nil, err
-	}
+func (m *Manager) authorizedCert(ctx context.Context, key crypto.Signer, ck certKey) (der [][]byte, leaf *x509.Certificate, err error) {
 	client, err := m.acmeClient(ctx)
 	if err != nil {
 		return nil, nil, err
 	}
-	csr, err := certRequest(key, domain)
+
+	if err := m.verify(ctx, client, ck.domain); err != nil {
+		return nil, nil, err
+	}
+	csr, err := certRequest(key, ck.domain, m.ExtraExtensions)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -455,105 +647,227 @@ func (m *Manager) authorizedCert(ctx context.Context, key crypto.Signer, domain
 	if err != nil {
 		return nil, nil, err
 	}
-	leaf, err = validCert(domain, der, key)
+	leaf, err = validCert(ck, der, key, m.now())
 	if err != nil {
 		return nil, nil, err
 	}
 	return der, leaf, nil
 }
 
-// verify starts a new identifier (domain) authorization flow.
-// It prepares a challenge response and then blocks until the authorization
-// is marked as "completed" by the CA (either succeeded or failed).
-//
-// verify returns nil iff the verification was successful.
-func (m *Manager) verify(ctx context.Context, domain string) error {
+// revokePendingAuthz revokes all authorizations idenfied by the elements of uri slice.
+// It ignores revocation errors.
+func (m *Manager) revokePendingAuthz(ctx context.Context, uri []string) {
 	client, err := m.acmeClient(ctx)
 	if err != nil {
-		return err
+		return
 	}
-
-	// start domain authorization and get the challenge
-	authz, err := client.Authorize(ctx, domain)
-	if err != nil {
-		return err
+	for _, u := range uri {
+		client.RevokeAuthorization(ctx, u)
 	}
-	// maybe don't need to at all
-	if authz.Status == acme.StatusValid {
-		return nil
+}
+
+// verify runs the identifier (domain) authorization flow
+// using each applicable ACME challenge type.
+func (m *Manager) verify(ctx context.Context, client *acme.Client, domain string) error {
+	// The list of challenge types we'll try to fulfill
+	// in this specific order.
+	challengeTypes := []string{"tls-alpn-01", "tls-sni-02", "tls-sni-01"}
+	m.tokensMu.RLock()
+	if m.tryHTTP01 {
+		challengeTypes = append(challengeTypes, "http-01")
 	}
+	m.tokensMu.RUnlock()
 
-	// pick a challenge: prefer tls-sni-02 over tls-sni-01
-	// TODO: consider authz.Combinations
-	var chal *acme.Challenge
-	for _, c := range authz.Challenges {
-		if c.Type == "tls-sni-02" {
-			chal = c
-			break
+	// Keep track of pending authzs and revoke the ones that did not validate.
+	pendingAuthzs := make(map[string]bool)
+	defer func() {
+		var uri []string
+		for k, pending := range pendingAuthzs {
+			if pending {
+				uri = append(uri, k)
+			}
 		}
-		if c.Type == "tls-sni-01" {
-			chal = c
+		if len(uri) > 0 {
+			// Use "detached" background context.
+			// The revocations need not happen in the current verification flow.
+			go m.revokePendingAuthz(context.Background(), uri)
 		}
+	}()
+
+	// errs accumulates challenge failure errors, printed if all fail
+	errs := make(map[*acme.Challenge]error)
+	var nextTyp int // challengeType index of the next challenge type to try
+	for {
+		// Start domain authorization and get the challenge.
+		authz, err := client.Authorize(ctx, domain)
+		if err != nil {
+			return err
+		}
+		// No point in accepting challenges if the authorization status
+		// is in a final state.
+		switch authz.Status {
+		case acme.StatusValid:
+			return nil // already authorized
+		case acme.StatusInvalid:
+			return fmt.Errorf("acme/autocert: invalid authorization %q", authz.URI)
+		}
+
+		pendingAuthzs[authz.URI] = true
+
+		// Pick the next preferred challenge.
+		var chal *acme.Challenge
+		for chal == nil && nextTyp < len(challengeTypes) {
+			chal = pickChallenge(challengeTypes[nextTyp], authz.Challenges)
+			nextTyp++
+		}
+		if chal == nil {
+			errorMsg := fmt.Sprintf("acme/autocert: unable to authorize %q", domain)
+			for chal, err := range errs {
+				errorMsg += fmt.Sprintf("; challenge %q failed with error: %v", chal.Type, err)
+			}
+			return errors.New(errorMsg)
+		}
+		cleanup, err := m.fulfill(ctx, client, chal, domain)
+		if err != nil {
+			errs[chal] = err
+			continue
+		}
+		defer cleanup()
+		if _, err := client.Accept(ctx, chal); err != nil {
+			errs[chal] = err
+			continue
+		}
+
+		// A challenge is fulfilled and accepted: wait for the CA to validate.
+		if _, err := client.WaitAuthorization(ctx, authz.URI); err != nil {
+			errs[chal] = err
+			continue
+		}
+		delete(pendingAuthzs, authz.URI)
+		return nil
 	}
-	if chal == nil {
-		return errors.New("acme/autocert: no supported challenge type found")
-	}
+}
 
-	// create a token cert for the challenge response
-	var (
-		cert tls.Certificate
-		name string
-	)
+// fulfill provisions a response to the challenge chal.
+// The cleanup is non-nil only if provisioning succeeded.
+func (m *Manager) fulfill(ctx context.Context, client *acme.Client, chal *acme.Challenge, domain string) (cleanup func(), err error) {
 	switch chal.Type {
+	case "tls-alpn-01":
+		cert, err := client.TLSALPN01ChallengeCert(chal.Token, domain)
+		if err != nil {
+			return nil, err
+		}
+		m.putCertToken(ctx, domain, &cert)
+		return func() { go m.deleteCertToken(domain) }, nil
 	case "tls-sni-01":
-		cert, name, err = client.TLSSNI01ChallengeCert(chal.Token)
+		cert, name, err := client.TLSSNI01ChallengeCert(chal.Token)
+		if err != nil {
+			return nil, err
+		}
+		m.putCertToken(ctx, name, &cert)
+		return func() { go m.deleteCertToken(name) }, nil
 	case "tls-sni-02":
-		cert, name, err = client.TLSSNI02ChallengeCert(chal.Token)
-	default:
-		err = fmt.Errorf("acme/autocert: unknown challenge type %q", chal.Type)
+		cert, name, err := client.TLSSNI02ChallengeCert(chal.Token)
+		if err != nil {
+			return nil, err
+		}
+		m.putCertToken(ctx, name, &cert)
+		return func() { go m.deleteCertToken(name) }, nil
+	case "http-01":
+		resp, err := client.HTTP01ChallengeResponse(chal.Token)
+		if err != nil {
+			return nil, err
+		}
+		p := client.HTTP01ChallengePath(chal.Token)
+		m.putHTTPToken(ctx, p, resp)
+		return func() { go m.deleteHTTPToken(p) }, nil
 	}
-	if err != nil {
-		return err
+	return nil, fmt.Errorf("acme/autocert: unknown challenge type %q", chal.Type)
+}
+
+func pickChallenge(typ string, chal []*acme.Challenge) *acme.Challenge {
+	for _, c := range chal {
+		if c.Type == typ {
+			return c
+		}
 	}
-	m.putTokenCert(ctx, name, &cert)
-	defer func() {
-		// verification has ended at this point
-		// don't need token cert anymore
-		go m.deleteTokenCert(name)
-	}()
+	return nil
+}
 
-	// ready to fulfill the challenge
-	if _, err := client.Accept(ctx, chal); err != nil {
-		return err
+// putCertToken stores the token certificate with the specified name
+// in both m.certTokens map and m.Cache.
+func (m *Manager) putCertToken(ctx context.Context, name string, cert *tls.Certificate) {
+	m.tokensMu.Lock()
+	defer m.tokensMu.Unlock()
+	if m.certTokens == nil {
+		m.certTokens = make(map[string]*tls.Certificate)
 	}
-	// wait for the CA to validate
-	_, err = client.WaitAuthorization(ctx, authz.URI)
-	return err
+	m.certTokens[name] = cert
+	m.cachePut(ctx, certKey{domain: name, isToken: true}, cert)
 }
 
-// putTokenCert stores the cert under the named key in both m.tokenCert map
-// and m.Cache.
-func (m *Manager) putTokenCert(ctx context.Context, name string, cert *tls.Certificate) {
-	m.tokenCertMu.Lock()
-	defer m.tokenCertMu.Unlock()
-	if m.tokenCert == nil {
-		m.tokenCert = make(map[string]*tls.Certificate)
+// deleteCertToken removes the token certificate with the specified name
+// from both m.certTokens map and m.Cache.
+func (m *Manager) deleteCertToken(name string) {
+	m.tokensMu.Lock()
+	defer m.tokensMu.Unlock()
+	delete(m.certTokens, name)
+	if m.Cache != nil {
+		ck := certKey{domain: name, isToken: true}
+		m.Cache.Delete(context.Background(), ck.String())
 	}
-	m.tokenCert[name] = cert
-	m.cachePut(ctx, name, cert)
 }
 
-// deleteTokenCert removes the token certificate for the specified domain name
-// from both m.tokenCert map and m.Cache.
-func (m *Manager) deleteTokenCert(name string) {
-	m.tokenCertMu.Lock()
-	defer m.tokenCertMu.Unlock()
-	delete(m.tokenCert, name)
+// httpToken retrieves an existing http-01 token value from an in-memory map
+// or the optional cache.
+func (m *Manager) httpToken(ctx context.Context, tokenPath string) ([]byte, error) {
+	m.tokensMu.RLock()
+	defer m.tokensMu.RUnlock()
+	if v, ok := m.httpTokens[tokenPath]; ok {
+		return v, nil
+	}
+	if m.Cache == nil {
+		return nil, fmt.Errorf("acme/autocert: no token at %q", tokenPath)
+	}
+	return m.Cache.Get(ctx, httpTokenCacheKey(tokenPath))
+}
+
+// putHTTPToken stores an http-01 token value using tokenPath as key
+// in both in-memory map and the optional Cache.
+//
+// It ignores any error returned from Cache.Put.
+func (m *Manager) putHTTPToken(ctx context.Context, tokenPath, val string) {
+	m.tokensMu.Lock()
+	defer m.tokensMu.Unlock()
+	if m.httpTokens == nil {
+		m.httpTokens = make(map[string][]byte)
+	}
+	b := []byte(val)
+	m.httpTokens[tokenPath] = b
 	if m.Cache != nil {
-		m.Cache.Delete(context.Background(), name)
+		m.Cache.Put(ctx, httpTokenCacheKey(tokenPath), b)
 	}
 }
 
+// deleteHTTPToken removes an http-01 token value from both in-memory map
+// and the optional Cache, ignoring any error returned from the latter.
+//
+// If m.Cache is non-nil, it blocks until Cache.Delete returns without a timeout.
+func (m *Manager) deleteHTTPToken(tokenPath string) {
+	m.tokensMu.Lock()
+	defer m.tokensMu.Unlock()
+	delete(m.httpTokens, tokenPath)
+	if m.Cache != nil {
+		m.Cache.Delete(context.Background(), httpTokenCacheKey(tokenPath))
+	}
+}
+
+// httpTokenCacheKey returns a key at which an http-01 token value may be stored
+// in the Manager's optional Cache.
+func httpTokenCacheKey(tokenPath string) string {
+	return path.Base(tokenPath) + "+http-01"
+}
+
 // renew starts a cert renewal timer loop, one per domain.
 //
 // The loop is scheduled in two cases:
@@ -562,18 +876,18 @@ func (m *Manager) deleteTokenCert(name string) {
 //
 // The key argument is a certificate private key.
 // The exp argument is the cert expiration time (NotAfter).
-func (m *Manager) renew(domain string, key crypto.Signer, exp time.Time) {
+func (m *Manager) renew(ck certKey, key crypto.Signer, exp time.Time) {
 	m.renewalMu.Lock()
 	defer m.renewalMu.Unlock()
-	if m.renewal[domain] != nil {
+	if m.renewal[ck] != nil {
 		// another goroutine is already on it
 		return
 	}
 	if m.renewal == nil {
-		m.renewal = make(map[string]*domainRenewal)
+		m.renewal = make(map[certKey]*domainRenewal)
 	}
-	dr := &domainRenewal{m: m, domain: domain, key: key}
-	m.renewal[domain] = dr
+	dr := &domainRenewal{m: m, ck: ck, key: key}
+	m.renewal[ck] = dr
 	dr.start(exp)
 }
 
@@ -589,7 +903,10 @@ func (m *Manager) stopRenew() {
 }
 
 func (m *Manager) accountKey(ctx context.Context) (crypto.Signer, error) {
-	const keyName = "acme_account.key"
+	const keyName = "acme_account+key"
+
+	// Previous versions of autocert stored the value under a different key.
+	const legacyKeyName = "acme_account.key"
 
 	genKey := func() (*ecdsa.PrivateKey, error) {
 		return ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
@@ -600,6 +917,9 @@ func (m *Manager) accountKey(ctx context.Context) (crypto.Signer, error) {
 	}
 
 	data, err := m.Cache.Get(ctx, keyName)
+	if err == ErrCacheMiss {
+		data, err = m.Cache.Get(ctx, legacyKeyName)
+	}
 	if err == ErrCacheMiss {
 		key, err := genKey()
 		if err != nil {
@@ -671,6 +991,13 @@ func (m *Manager) renewBefore() time.Duration {
 	return 720 * time.Hour // 30 days
 }
 
+func (m *Manager) now() time.Time {
+	if m.nowFunc != nil {
+		return m.nowFunc()
+	}
+	return time.Now()
+}
+
 // certState is ready when its mutex is unlocked for reading.
 type certState struct {
 	sync.RWMutex
@@ -696,12 +1023,12 @@ func (s *certState) tlscert() (*tls.Certificate, error) {
 	}, nil
 }
 
-// certRequest creates a certificate request for the given common name cn
-// and optional SANs.
-func certRequest(key crypto.Signer, cn string, san ...string) ([]byte, error) {
+// certRequest generates a CSR for the given common name cn and optional SANs.
+func certRequest(key crypto.Signer, cn string, ext []pkix.Extension, san ...string) ([]byte, error) {
 	req := &x509.CertificateRequest{
-		Subject:  pkix.Name{CommonName: cn},
-		DNSNames: san,
+		Subject:         pkix.Name{CommonName: cn},
+		DNSNames:        san,
+		ExtraExtensions: ext,
 	}
 	return x509.CreateCertificateRequest(rand.Reader, req, key)
 }
@@ -732,12 +1059,12 @@ func parsePrivateKey(der []byte) (crypto.Signer, error) {
 	return nil, errors.New("acme/autocert: failed to parse private key")
 }
 
-// validCert parses a cert chain provided as der argument and verifies the leaf, der[0],
-// corresponds to the private key, as well as the domain match and expiration dates.
-// It doesn't do any revocation checking.
+// validCert parses a cert chain provided as der argument and verifies the leaf and der[0]
+// correspond to the private key, the domain and key type match, and expiration dates
+// are valid. It doesn't do any revocation checking.
 //
 // The returned value is the verified leaf cert.
-func validCert(domain string, der [][]byte, key crypto.Signer) (leaf *x509.Certificate, err error) {
+func validCert(ck certKey, der [][]byte, key crypto.Signer, now time.Time) (leaf *x509.Certificate, err error) {
 	// parse public part(s)
 	var n int
 	for _, b := range der {
@@ -749,22 +1076,21 @@ func validCert(domain string, der [][]byte, key crypto.Signer) (leaf *x509.Certi
 		n += copy(pub[n:], b)
 	}
 	x509Cert, err := x509.ParseCertificates(pub)
-	if len(x509Cert) == 0 {
+	if err != nil || len(x509Cert) == 0 {
 		return nil, errors.New("acme/autocert: no public key found")
 	}
 	// verify the leaf is not expired and matches the domain name
 	leaf = x509Cert[0]
-	now := timeNow()
 	if now.Before(leaf.NotBefore) {
 		return nil, errors.New("acme/autocert: certificate is not valid yet")
 	}
 	if now.After(leaf.NotAfter) {
 		return nil, errors.New("acme/autocert: expired certificate")
 	}
-	if err := leaf.VerifyHostname(domain); err != nil {
+	if err := leaf.VerifyHostname(ck.domain); err != nil {
 		return nil, err
 	}
-	// ensure the leaf corresponds to the private key
+	// ensure the leaf corresponds to the private key and matches the certKey type
 	switch pub := leaf.PublicKey.(type) {
 	case *rsa.PublicKey:
 		prv, ok := key.(*rsa.PrivateKey)
@@ -774,6 +1100,9 @@ func validCert(domain string, der [][]byte, key crypto.Signer) (leaf *x509.Certi
 		if pub.N.Cmp(prv.N) != 0 {
 			return nil, errors.New("acme/autocert: private key does not match public key")
 		}
+		if !ck.isRSA && !ck.isToken {
+			return nil, errors.New("acme/autocert: key type does not match expected value")
+		}
 	case *ecdsa.PublicKey:
 		prv, ok := key.(*ecdsa.PrivateKey)
 		if !ok {
@@ -782,22 +1111,15 @@ func validCert(domain string, der [][]byte, key crypto.Signer) (leaf *x509.Certi
 		if pub.X.Cmp(prv.X) != 0 || pub.Y.Cmp(prv.Y) != 0 {
 			return nil, errors.New("acme/autocert: private key does not match public key")
 		}
+		if ck.isRSA && !ck.isToken {
+			return nil, errors.New("acme/autocert: key type does not match expected value")
+		}
 	default:
 		return nil, errors.New("acme/autocert: unknown public key algorithm")
 	}
 	return leaf, nil
 }
 
-func retryAfter(v string) time.Duration {
-	if i, err := strconv.Atoi(v); err == nil {
-		return time.Duration(i) * time.Second
-	}
-	if t, err := http.ParseTime(v); err == nil {
-		return t.Sub(timeNow())
-	}
-	return time.Second
-}
-
 type lockedMathRand struct {
 	sync.Mutex
 	rnd *mathrand.Rand
@@ -812,8 +1134,6 @@ func (r *lockedMathRand) int63n(max int64) int64 {
 
 // For easier testing.
 var (
-	timeNow = time.Now
-
 	// Called when a state is removed.
-	testDidRemoveState = func(domain string) {}
+	testDidRemoveState = func(certKey) {}
 )

+ 671 - 87
psiphon/common/crypto/acme/autocert/autocert_test.go

@@ -5,6 +5,7 @@
 package autocert
 
 import (
+	"bytes"
 	"context"
 	"crypto"
 	"crypto/ecdsa"
@@ -14,20 +15,31 @@ import (
 	"crypto/tls"
 	"crypto/x509"
 	"crypto/x509/pkix"
+	"encoding/asn1"
 	"encoding/base64"
 	"encoding/json"
 	"fmt"
 	"html/template"
 	"io"
+	"io/ioutil"
 	"math/big"
 	"net/http"
 	"net/http/httptest"
 	"reflect"
+	"strings"
 	"sync"
 	"testing"
 	"time"
 
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/acme"
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/acme/autocert/internal/acmetest"
+)
+
+var (
+	exampleDomain     = "example.org"
+	exampleCertKey    = certKey{domain: exampleDomain}
+	exampleCertKeyRSA = certKey{domain: exampleDomain, isRSA: true}
+>>>>>>> 3b6eb8e6906b2b3239b795cf8996226cf4771fa4
 )
 
 var discoTmpl = template.Must(template.New("disco").Parse(`{
@@ -48,11 +60,22 @@ var authzTmpl = template.Must(template.New("authz").Parse(`{
 			"uri": "{{.}}/challenge/2",
 			"type": "tls-sni-02",
 			"token": "token-02"
+		},
+		{
+			"uri": "{{.}}/challenge/dns-01",
+			"type": "dns-01",
+			"token": "token-dns-01"
+		},
+		{
+			"uri": "{{.}}/challenge/http-01",
+			"type": "http-01",
+			"token": "token-http-01"
 		}
 	]
 }`))
 
 type memCache struct {
+	t       *testing.T
 	mu      sync.Mutex
 	keyData map[string][]byte
 }
@@ -68,7 +91,26 @@ func (m *memCache) Get(ctx context.Context, key string) ([]byte, error) {
 	return v, nil
 }
 
+// filenameSafe returns whether all characters in s are printable ASCII
+// and safe to use in a filename on most filesystems.
+func filenameSafe(s string) bool {
+	for _, c := range s {
+		if c < 0x20 || c > 0x7E {
+			return false
+		}
+		switch c {
+		case '\\', '/', ':', '*', '?', '"', '<', '>', '|':
+			return false
+		}
+	}
+	return true
+}
+
 func (m *memCache) Put(ctx context.Context, key string, data []byte) error {
+	if !filenameSafe(key) {
+		m.t.Errorf("invalid characters in cache key %q", key)
+	}
+
 	m.mu.Lock()
 	defer m.mu.Unlock()
 
@@ -84,12 +126,29 @@ func (m *memCache) Delete(ctx context.Context, key string) error {
 	return nil
 }
 
-func newMemCache() *memCache {
+func newMemCache(t *testing.T) *memCache {
 	return &memCache{
+		t:       t,
 		keyData: make(map[string][]byte),
 	}
 }
 
+func (m *memCache) numCerts() int {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+
+	res := 0
+	for key := range m.keyData {
+		if strings.HasSuffix(key, "+token") ||
+			strings.HasSuffix(key, "+key") ||
+			strings.HasSuffix(key, "+http-01") {
+			continue
+		}
+		res++
+	}
+	return res
+}
+
 func dummyCert(pub interface{}, san ...string) ([]byte, error) {
 	return dateDummyCert(pub, time.Now(), time.Now().Add(90*24*time.Hour), san...)
 }
@@ -126,53 +185,58 @@ func decodePayload(v interface{}, r io.Reader) error {
 	return json.Unmarshal(payload, v)
 }
 
+func clientHelloInfo(sni string, ecdsaSupport bool) *tls.ClientHelloInfo {
+	hello := &tls.ClientHelloInfo{
+		ServerName:   sni,
+		CipherSuites: []uint16{tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305},
+	}
+	if ecdsaSupport {
+		hello.CipherSuites = append(hello.CipherSuites, tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305)
+	}
+	return hello
+}
+
 func TestGetCertificate(t *testing.T) {
 	man := &Manager{Prompt: AcceptTOS}
 	defer man.stopRenew()
-	hello := &tls.ClientHelloInfo{ServerName: "example.org"}
+	hello := clientHelloInfo("example.org", true)
 	testGetCertificate(t, man, "example.org", hello)
 }
 
 func TestGetCertificate_trailingDot(t *testing.T) {
 	man := &Manager{Prompt: AcceptTOS}
 	defer man.stopRenew()
-	hello := &tls.ClientHelloInfo{ServerName: "example.org."}
+	hello := clientHelloInfo("example.org.", true)
 	testGetCertificate(t, man, "example.org", hello)
 }
 
 func TestGetCertificate_ForceRSA(t *testing.T) {
 	man := &Manager{
 		Prompt:   AcceptTOS,
-		Cache:    newMemCache(),
+		Cache:    newMemCache(t),
 		ForceRSA: true,
 	}
 	defer man.stopRenew()
-	hello := &tls.ClientHelloInfo{ServerName: "example.org"}
-	testGetCertificate(t, man, "example.org", hello)
+	hello := clientHelloInfo(exampleDomain, true)
+	testGetCertificate(t, man, exampleDomain, hello)
 
-	cert, err := man.cacheGet(context.Background(), "example.org")
+	// ForceRSA was deprecated and is now ignored.
+	cert, err := man.cacheGet(context.Background(), exampleCertKey)
 	if err != nil {
 		t.Fatalf("man.cacheGet: %v", err)
 	}
-	if _, ok := cert.PrivateKey.(*rsa.PrivateKey); !ok {
-		t.Errorf("cert.PrivateKey is %T; want *rsa.PrivateKey", cert.PrivateKey)
+	if _, ok := cert.PrivateKey.(*ecdsa.PrivateKey); !ok {
+		t.Errorf("cert.PrivateKey is %T; want *ecdsa.PrivateKey", cert.PrivateKey)
 	}
 }
 
 func TestGetCertificate_nilPrompt(t *testing.T) {
 	man := &Manager{}
 	defer man.stopRenew()
-	url, finish := startACMEServerStub(t, man, "example.org")
+	url, finish := startACMEServerStub(t, getCertificateFromManager(man, true), "example.org")
 	defer finish()
-	key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
-	if err != nil {
-		t.Fatal(err)
-	}
-	man.Client = &acme.Client{
-		Key:          key,
-		DirectoryURL: url,
-	}
-	hello := &tls.ClientHelloInfo{ServerName: "example.org"}
+	man.Client = &acme.Client{DirectoryURL: url}
+	hello := clientHelloInfo("example.org", true)
 	if _, err := man.GetCertificate(hello); err == nil {
 		t.Error("got certificate for example.org; wanted error")
 	}
@@ -186,7 +250,7 @@ func TestGetCertificate_expiredCache(t *testing.T) {
 	}
 	tmpl := &x509.Certificate{
 		SerialNumber: big.NewInt(1),
-		Subject:      pkix.Name{CommonName: "example.org"},
+		Subject:      pkix.Name{CommonName: exampleDomain},
 		NotAfter:     time.Now(),
 	}
 	pub, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, &pk.PublicKey, pk)
@@ -198,16 +262,16 @@ func TestGetCertificate_expiredCache(t *testing.T) {
 		PrivateKey:  pk,
 	}
 
-	man := &Manager{Prompt: AcceptTOS, Cache: newMemCache()}
+	man := &Manager{Prompt: AcceptTOS, Cache: newMemCache(t)}
 	defer man.stopRenew()
-	if err := man.cachePut(context.Background(), "example.org", tlscert); err != nil {
+	if err := man.cachePut(context.Background(), exampleCertKey, tlscert); err != nil {
 		t.Fatalf("man.cachePut: %v", err)
 	}
 
 	// The expired cached cert should trigger a new cert issuance
 	// and return without an error.
-	hello := &tls.ClientHelloInfo{ServerName: "example.org"}
-	testGetCertificate(t, man, "example.org", hello)
+	hello := clientHelloInfo(exampleDomain, true)
+	testGetCertificate(t, man, exampleDomain, hello)
 }
 
 func TestGetCertificate_failedAttempt(t *testing.T) {
@@ -216,7 +280,6 @@ func TestGetCertificate_failedAttempt(t *testing.T) {
 	}))
 	defer ts.Close()
 
-	const example = "example.org"
 	d := createCertRetryAfter
 	f := testDidRemoveState
 	defer func() {
@@ -225,51 +288,168 @@ func TestGetCertificate_failedAttempt(t *testing.T) {
 	}()
 	createCertRetryAfter = 0
 	done := make(chan struct{})
-	testDidRemoveState = func(domain string) {
-		if domain != example {
-			t.Errorf("testDidRemoveState: domain = %q; want %q", domain, example)
+	testDidRemoveState = func(ck certKey) {
+		if ck != exampleCertKey {
+			t.Errorf("testDidRemoveState: domain = %v; want %v", ck, exampleCertKey)
 		}
 		close(done)
 	}
 
-	key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
-	if err != nil {
-		t.Fatal(err)
-	}
 	man := &Manager{
 		Prompt: AcceptTOS,
 		Client: &acme.Client{
-			Key:          key,
 			DirectoryURL: ts.URL,
 		},
 	}
 	defer man.stopRenew()
-	hello := &tls.ClientHelloInfo{ServerName: example}
+	hello := clientHelloInfo(exampleDomain, true)
 	if _, err := man.GetCertificate(hello); err == nil {
 		t.Error("GetCertificate: err is nil")
 	}
 	select {
 	case <-time.After(5 * time.Second):
-		t.Errorf("took too long to remove the %q state", example)
+		t.Errorf("took too long to remove the %q state", exampleCertKey)
 	case <-done:
 		man.stateMu.Lock()
 		defer man.stateMu.Unlock()
-		if v, exist := man.state[example]; exist {
-			t.Errorf("state exists for %q: %+v", example, v)
+		if v, exist := man.state[exampleCertKey]; exist {
+			t.Errorf("state exists for %v: %+v", exampleCertKey, v)
 		}
 	}
 }
 
+// testGetCertificate_tokenCache tests the fallback of token certificate fetches
+// to cache when Manager.certTokens misses. ecdsaSupport refers to the CA when
+// verifying the certificate token.
+func testGetCertificate_tokenCache(t *testing.T, ecdsaSupport bool) {
+	man1 := &Manager{
+		Cache:  newMemCache(t),
+		Prompt: AcceptTOS,
+	}
+	defer man1.stopRenew()
+	man2 := &Manager{
+		Cache:  man1.Cache,
+		Prompt: AcceptTOS,
+	}
+	defer man2.stopRenew()
+
+	// Send the verification request to a different Manager from the one that
+	// initiated the authorization, when they share caches.
+	url, finish := startACMEServerStub(t, getCertificateFromManager(man2, ecdsaSupport), "example.org")
+	defer finish()
+	man1.Client = &acme.Client{DirectoryURL: url}
+	hello := clientHelloInfo("example.org", true)
+	if _, err := man1.GetCertificate(hello); err != nil {
+		t.Error(err)
+	}
+	if _, err := man2.GetCertificate(hello); err != nil {
+		t.Error(err)
+	}
+}
+
+func TestGetCertificate_tokenCache(t *testing.T) {
+	t.Run("ecdsaSupport=true", func(t *testing.T) {
+		testGetCertificate_tokenCache(t, true)
+	})
+	t.Run("ecdsaSupport=false", func(t *testing.T) {
+		testGetCertificate_tokenCache(t, false)
+	})
+}
+
+func TestGetCertificate_ecdsaVsRSA(t *testing.T) {
+	cache := newMemCache(t)
+	man := &Manager{Prompt: AcceptTOS, Cache: cache}
+	defer man.stopRenew()
+	url, finish := startACMEServerStub(t, getCertificateFromManager(man, true), "example.org")
+	defer finish()
+	man.Client = &acme.Client{DirectoryURL: url}
+
+	cert, err := man.GetCertificate(clientHelloInfo("example.org", true))
+	if err != nil {
+		t.Error(err)
+	}
+	if _, ok := cert.Leaf.PublicKey.(*ecdsa.PublicKey); !ok {
+		t.Error("an ECDSA client was served a non-ECDSA certificate")
+	}
+
+	cert, err = man.GetCertificate(clientHelloInfo("example.org", false))
+	if err != nil {
+		t.Error(err)
+	}
+	if _, ok := cert.Leaf.PublicKey.(*rsa.PublicKey); !ok {
+		t.Error("a RSA client was served a non-RSA certificate")
+	}
+
+	if _, err := man.GetCertificate(clientHelloInfo("example.org", true)); err != nil {
+		t.Error(err)
+	}
+	if _, err := man.GetCertificate(clientHelloInfo("example.org", false)); err != nil {
+		t.Error(err)
+	}
+	if numCerts := cache.numCerts(); numCerts != 2 {
+		t.Errorf("found %d certificates in cache; want %d", numCerts, 2)
+	}
+}
+
+func TestGetCertificate_wrongCacheKeyType(t *testing.T) {
+	cache := newMemCache(t)
+	man := &Manager{Prompt: AcceptTOS, Cache: cache}
+	defer man.stopRenew()
+	url, finish := startACMEServerStub(t, getCertificateFromManager(man, true), exampleDomain)
+	defer finish()
+	man.Client = &acme.Client{DirectoryURL: url}
+
+	// Make an RSA cert and cache it without suffix.
+	pk, err := rsa.GenerateKey(rand.Reader, 512)
+	if err != nil {
+		t.Fatal(err)
+	}
+	tmpl := &x509.Certificate{
+		SerialNumber: big.NewInt(1),
+		Subject:      pkix.Name{CommonName: exampleDomain},
+		NotAfter:     time.Now().Add(90 * 24 * time.Hour),
+	}
+	pub, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, &pk.PublicKey, pk)
+	if err != nil {
+		t.Fatal(err)
+	}
+	rsaCert := &tls.Certificate{
+		Certificate: [][]byte{pub},
+		PrivateKey:  pk,
+	}
+	if err := man.cachePut(context.Background(), exampleCertKey, rsaCert); err != nil {
+		t.Fatalf("man.cachePut: %v", err)
+	}
+
+	// The RSA cached cert should be silently ignored and replaced.
+	cert, err := man.GetCertificate(clientHelloInfo(exampleDomain, true))
+	if err != nil {
+		t.Error(err)
+	}
+	if _, ok := cert.Leaf.PublicKey.(*ecdsa.PublicKey); !ok {
+		t.Error("an ECDSA client was served a non-ECDSA certificate")
+	}
+	if numCerts := cache.numCerts(); numCerts != 1 {
+		t.Errorf("found %d certificates in cache; want %d", numCerts, 1)
+	}
+}
+
+func getCertificateFromManager(man *Manager, ecdsaSupport bool) func(string) error {
+	return func(sni string) error {
+		_, err := man.GetCertificate(clientHelloInfo(sni, ecdsaSupport))
+		return err
+	}
+}
+
 // startACMEServerStub runs an ACME server
 // The domain argument is the expected domain name of a certificate request.
-func startACMEServerStub(t *testing.T, man *Manager, domain string) (url string, finish func()) {
+// TODO: Drop this in favour of x/crypto/acme/autocert/internal/acmetest.
+func startACMEServerStub(t *testing.T, getCertificate func(string) error, domain string) (url string, finish func()) {
 	// echo token-02 | shasum -a 256
 	// then divide result in 2 parts separated by dot
 	tokenCertName := "4e8eb87631187e9ff2153b56b13a4dec.13a35d002e485d60ff37354b32f665d9.token.acme.invalid"
 	verifyTokenCert := func() {
-		hello := &tls.ClientHelloInfo{ServerName: tokenCertName}
-		_, err := man.GetCertificate(hello)
-		if err != nil {
+		if err := getCertificate(tokenCertName); err != nil {
 			t.Errorf("verifyTokenCert: GetCertificate(%q): %v", tokenCertName, err)
 			return
 		}
@@ -351,8 +531,7 @@ func startACMEServerStub(t *testing.T, man *Manager, domain string) (url string,
 			tick := time.NewTicker(100 * time.Millisecond)
 			defer tick.Stop()
 			for {
-				hello := &tls.ClientHelloInfo{ServerName: tokenCertName}
-				if _, err := man.GetCertificate(hello); err != nil {
+				if err := getCertificate(tokenCertName); err != nil {
 					return
 				}
 				select {
@@ -376,21 +555,13 @@ func startACMEServerStub(t *testing.T, man *Manager, domain string) (url string,
 // tests man.GetCertificate flow using the provided hello argument.
 // The domain argument is the expected domain name of a certificate request.
 func testGetCertificate(t *testing.T, man *Manager, domain string, hello *tls.ClientHelloInfo) {
-	url, finish := startACMEServerStub(t, man, domain)
+	url, finish := startACMEServerStub(t, getCertificateFromManager(man, true), domain)
 	defer finish()
-
-	// use EC key to run faster on 386
-	key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
-	if err != nil {
-		t.Fatal(err)
-	}
-	man.Client = &acme.Client{
-		Key:          key,
-		DirectoryURL: url,
-	}
+	man.Client = &acme.Client{DirectoryURL: url}
 
 	// simulate tls.Config.GetCertificate
 	var tlscert *tls.Certificate
+	var err error
 	done := make(chan struct{})
 	go func() {
 		tlscert, err = man.GetCertificate(hello)
@@ -419,8 +590,253 @@ func testGetCertificate(t *testing.T, man *Manager, domain string, hello *tls.Cl
 
 }
 
+func TestVerifyHTTP01(t *testing.T) {
+	var (
+		http01 http.Handler
+
+		authzCount      int // num. of created authorizations
+		didAcceptHTTP01 bool
+	)
+
+	verifyHTTPToken := func() {
+		r := httptest.NewRequest("GET", "/.well-known/acme-challenge/token-http-01", nil)
+		w := httptest.NewRecorder()
+		http01.ServeHTTP(w, r)
+		if w.Code != http.StatusOK {
+			t.Errorf("http token: w.Code = %d; want %d", w.Code, http.StatusOK)
+		}
+		if v := w.Body.String(); !strings.HasPrefix(v, "token-http-01.") {
+			t.Errorf("http token value = %q; want 'token-http-01.' prefix", v)
+		}
+	}
+
+	// ACME CA server stub, only the needed bits.
+	// TODO: Replace this with x/crypto/acme/autocert/internal/acmetest.
+	var ca *httptest.Server
+	ca = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		w.Header().Set("Replay-Nonce", "nonce")
+		if r.Method == "HEAD" {
+			// a nonce request
+			return
+		}
+
+		switch r.URL.Path {
+		// Discovery.
+		case "/":
+			if err := discoTmpl.Execute(w, ca.URL); err != nil {
+				t.Errorf("discoTmpl: %v", err)
+			}
+		// Client key registration.
+		case "/new-reg":
+			w.Write([]byte("{}"))
+		// New domain authorization.
+		case "/new-authz":
+			authzCount++
+			w.Header().Set("Location", fmt.Sprintf("%s/authz/%d", ca.URL, authzCount))
+			w.WriteHeader(http.StatusCreated)
+			if err := authzTmpl.Execute(w, ca.URL); err != nil {
+				t.Errorf("authzTmpl: %v", err)
+			}
+		// Accept tls-sni-02.
+		case "/challenge/2":
+			w.Write([]byte("{}"))
+		// Reject tls-sni-01.
+		case "/challenge/1":
+			http.Error(w, "won't accept tls-sni-01", http.StatusBadRequest)
+		// Should not accept dns-01.
+		case "/challenge/dns-01":
+			t.Errorf("dns-01 challenge was accepted")
+			http.Error(w, "won't accept dns-01", http.StatusBadRequest)
+		// Accept http-01.
+		case "/challenge/http-01":
+			didAcceptHTTP01 = true
+			verifyHTTPToken()
+			w.Write([]byte("{}"))
+		// Authorization statuses.
+		// Make tls-sni-xxx invalid.
+		case "/authz/1", "/authz/2":
+			w.Write([]byte(`{"status": "invalid"}`))
+		case "/authz/3", "/authz/4":
+			w.Write([]byte(`{"status": "valid"}`))
+		default:
+			http.NotFound(w, r)
+			t.Errorf("unrecognized r.URL.Path: %s", r.URL.Path)
+		}
+	}))
+	defer ca.Close()
+
+	m := &Manager{
+		Client: &acme.Client{
+			DirectoryURL: ca.URL,
+		},
+	}
+	http01 = m.HTTPHandler(nil)
+	ctx := context.Background()
+	client, err := m.acmeClient(ctx)
+	if err != nil {
+		t.Fatalf("m.acmeClient: %v", err)
+	}
+	if err := m.verify(ctx, client, "example.org"); err != nil {
+		t.Errorf("m.verify: %v", err)
+	}
+	// Only tls-sni-01, tls-sni-02 and http-01 must be accepted
+	// The dns-01 challenge is unsupported.
+	if authzCount != 3 {
+		t.Errorf("authzCount = %d; want 3", authzCount)
+	}
+	if !didAcceptHTTP01 {
+		t.Error("did not accept http-01 challenge")
+	}
+}
+
+func TestRevokeFailedAuthz(t *testing.T) {
+	// Prefill authorization URIs expected to be revoked.
+	// The challenges are selected in a specific order,
+	// each tried within a newly created authorization.
+	// This means each authorization URI corresponds to a different challenge type.
+	revokedAuthz := map[string]bool{
+		"/authz/0": false, // tls-sni-02
+		"/authz/1": false, // tls-sni-01
+		"/authz/2": false, // no viable challenge, but authz is created
+	}
+
+	var authzCount int          // num. of created authorizations
+	var revokeCount int         // num. of revoked authorizations
+	done := make(chan struct{}) // closed when revokeCount is 3
+
+	// ACME CA server stub, only the needed bits.
+	// TODO: Replace this with x/crypto/acme/autocert/internal/acmetest.
+	var ca *httptest.Server
+	ca = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		w.Header().Set("Replay-Nonce", "nonce")
+		if r.Method == "HEAD" {
+			// a nonce request
+			return
+		}
+
+		switch r.URL.Path {
+		// Discovery.
+		case "/":
+			if err := discoTmpl.Execute(w, ca.URL); err != nil {
+				t.Errorf("discoTmpl: %v", err)
+			}
+		// Client key registration.
+		case "/new-reg":
+			w.Write([]byte("{}"))
+		// New domain authorization.
+		case "/new-authz":
+			w.Header().Set("Location", fmt.Sprintf("%s/authz/%d", ca.URL, authzCount))
+			w.WriteHeader(http.StatusCreated)
+			if err := authzTmpl.Execute(w, ca.URL); err != nil {
+				t.Errorf("authzTmpl: %v", err)
+			}
+			authzCount++
+		// tls-sni-02 challenge "accept" request.
+		case "/challenge/2":
+			// Refuse.
+			http.Error(w, "won't accept tls-sni-02 challenge", http.StatusBadRequest)
+		// tls-sni-01 challenge "accept" request.
+		case "/challenge/1":
+			// Accept but the authorization will be "expired".
+			w.Write([]byte("{}"))
+		// Authorization requests.
+		case "/authz/0", "/authz/1", "/authz/2":
+			// Revocation requests.
+			if r.Method == "POST" {
+				var req struct{ Status string }
+				if err := decodePayload(&req, r.Body); err != nil {
+					t.Errorf("%s: decodePayload: %v", r.URL, err)
+				}
+				switch req.Status {
+				case "deactivated":
+					revokedAuthz[r.URL.Path] = true
+					revokeCount++
+					if revokeCount >= 3 {
+						// Last authorization is revoked.
+						defer close(done)
+					}
+				default:
+					t.Errorf("%s: req.Status = %q; want 'deactivated'", r.URL, req.Status)
+				}
+				w.Write([]byte(`{"status": "invalid"}`))
+				return
+			}
+			// Authorization status requests.
+			// Simulate abandoned authorization, deleted by the CA.
+			w.WriteHeader(http.StatusNotFound)
+		default:
+			http.NotFound(w, r)
+			t.Errorf("unrecognized r.URL.Path: %s", r.URL.Path)
+		}
+	}))
+	defer ca.Close()
+
+	m := &Manager{
+		Client: &acme.Client{DirectoryURL: ca.URL},
+	}
+	// Should fail and revoke 3 authorizations.
+	// The first 2 are tsl-sni-02 and tls-sni-01 challenges.
+	// The third time an authorization is created but no viable challenge is found.
+	// See revokedAuthz above for more explanation.
+	if _, err := m.createCert(context.Background(), exampleCertKey); err == nil {
+		t.Errorf("m.createCert returned nil error")
+	}
+	select {
+	case <-time.After(3 * time.Second):
+		t.Error("revocations took too long")
+	case <-done:
+		// revokeCount is at least 3.
+	}
+	for uri, ok := range revokedAuthz {
+		if !ok {
+			t.Errorf("%q authorization was not revoked", uri)
+		}
+	}
+}
+
+func TestHTTPHandlerDefaultFallback(t *testing.T) {
+	tt := []struct {
+		method, url  string
+		wantCode     int
+		wantLocation string
+	}{
+		{"GET", "http://example.org", 302, "https://example.org/"},
+		{"GET", "http://example.org/foo", 302, "https://example.org/foo"},
+		{"GET", "http://example.org/foo/bar/", 302, "https://example.org/foo/bar/"},
+		{"GET", "http://example.org/?a=b", 302, "https://example.org/?a=b"},
+		{"GET", "http://example.org/foo?a=b", 302, "https://example.org/foo?a=b"},
+		{"GET", "http://example.org:80/foo?a=b", 302, "https://example.org:443/foo?a=b"},
+		{"GET", "http://example.org:80/foo%20bar", 302, "https://example.org:443/foo%20bar"},
+		{"GET", "http://[2602:d1:xxxx::c60a]:1234", 302, "https://[2602:d1:xxxx::c60a]:443/"},
+		{"GET", "http://[2602:d1:xxxx::c60a]", 302, "https://[2602:d1:xxxx::c60a]/"},
+		{"GET", "http://[2602:d1:xxxx::c60a]/foo?a=b", 302, "https://[2602:d1:xxxx::c60a]/foo?a=b"},
+		{"HEAD", "http://example.org", 302, "https://example.org/"},
+		{"HEAD", "http://example.org/foo", 302, "https://example.org/foo"},
+		{"HEAD", "http://example.org/foo/bar/", 302, "https://example.org/foo/bar/"},
+		{"HEAD", "http://example.org/?a=b", 302, "https://example.org/?a=b"},
+		{"HEAD", "http://example.org/foo?a=b", 302, "https://example.org/foo?a=b"},
+		{"POST", "http://example.org", 400, ""},
+		{"PUT", "http://example.org", 400, ""},
+		{"GET", "http://example.org/.well-known/acme-challenge/x", 404, ""},
+	}
+	var m Manager
+	h := m.HTTPHandler(nil)
+	for i, test := range tt {
+		r := httptest.NewRequest(test.method, test.url, nil)
+		w := httptest.NewRecorder()
+		h.ServeHTTP(w, r)
+		if w.Code != test.wantCode {
+			t.Errorf("%d: w.Code = %d; want %d", i, w.Code, test.wantCode)
+			t.Errorf("%d: body: %s", i, w.Body.Bytes())
+		}
+		if v := w.Header().Get("Location"); v != test.wantLocation {
+			t.Errorf("%d: Location = %q; want %q", i, v, test.wantLocation)
+		}
+	}
+}
+
 func TestAccountKeyCache(t *testing.T) {
-	m := Manager{Cache: newMemCache()}
+	m := Manager{Cache: newMemCache(t)}
 	ctx := context.Background()
 	k1, err := m.accountKey(ctx)
 	if err != nil {
@@ -436,36 +852,57 @@ func TestAccountKeyCache(t *testing.T) {
 }
 
 func TestCache(t *testing.T) {
-	privKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
+	ecdsaKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
 	if err != nil {
 		t.Fatal(err)
 	}
-	tmpl := &x509.Certificate{
-		SerialNumber: big.NewInt(1),
-		Subject:      pkix.Name{CommonName: "example.org"},
-		NotAfter:     time.Now().Add(time.Hour),
+	cert, err := dummyCert(ecdsaKey.Public(), exampleDomain)
+	if err != nil {
+		t.Fatal(err)
 	}
-	pub, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, &privKey.PublicKey, privKey)
+	ecdsaCert := &tls.Certificate{
+		Certificate: [][]byte{cert},
+		PrivateKey:  ecdsaKey,
+	}
+
+	rsaKey, err := rsa.GenerateKey(rand.Reader, 512)
 	if err != nil {
 		t.Fatal(err)
 	}
-	tlscert := &tls.Certificate{
-		Certificate: [][]byte{pub},
-		PrivateKey:  privKey,
+	cert, err = dummyCert(rsaKey.Public(), exampleDomain)
+	if err != nil {
+		t.Fatal(err)
+	}
+	rsaCert := &tls.Certificate{
+		Certificate: [][]byte{cert},
+		PrivateKey:  rsaKey,
 	}
 
-	man := &Manager{Cache: newMemCache()}
+	man := &Manager{Cache: newMemCache(t)}
 	defer man.stopRenew()
 	ctx := context.Background()
-	if err := man.cachePut(ctx, "example.org", tlscert); err != nil {
+
+	if err := man.cachePut(ctx, exampleCertKey, ecdsaCert); err != nil {
 		t.Fatalf("man.cachePut: %v", err)
 	}
-	res, err := man.cacheGet(ctx, "example.org")
+	if err := man.cachePut(ctx, exampleCertKeyRSA, rsaCert); err != nil {
+		t.Fatalf("man.cachePut: %v", err)
+	}
+
+	res, err := man.cacheGet(ctx, exampleCertKey)
 	if err != nil {
 		t.Fatalf("man.cacheGet: %v", err)
 	}
-	if res == nil {
-		t.Fatal("res is nil")
+	if res == nil || !bytes.Equal(res.Certificate[0], ecdsaCert.Certificate[0]) {
+		t.Errorf("man.cacheGet = %+v; want %+v", res, ecdsaCert)
+	}
+
+	res, err = man.cacheGet(ctx, exampleCertKeyRSA)
+	if err != nil {
+		t.Fatalf("man.cacheGet: %v", err)
+	}
+	if res == nil || !bytes.Equal(res.Certificate[0], rsaCert.Certificate[0]) {
+		t.Errorf("man.cacheGet = %+v; want %+v", res, rsaCert)
 	}
 }
 
@@ -529,26 +966,28 @@ func TestValidCert(t *testing.T) {
 	}
 
 	tt := []struct {
-		domain string
-		key    crypto.Signer
-		cert   [][]byte
-		ok     bool
+		ck   certKey
+		key  crypto.Signer
+		cert [][]byte
+		ok   bool
 	}{
-		{"example.org", key1, [][]byte{cert1}, true},
-		{"example.org", key3, [][]byte{cert3}, true},
-		{"example.org", key1, [][]byte{cert1, cert2, cert3}, true},
-		{"example.org", key1, [][]byte{cert1, {1}}, false},
-		{"example.org", key1, [][]byte{{1}}, false},
-		{"example.org", key1, [][]byte{cert2}, false},
-		{"example.org", key2, [][]byte{cert1}, false},
-		{"example.org", key1, [][]byte{cert3}, false},
-		{"example.org", key3, [][]byte{cert1}, false},
-		{"example.net", key1, [][]byte{cert1}, false},
-		{"example.org", key1, [][]byte{early}, false},
-		{"example.org", key1, [][]byte{expired}, false},
+		{certKey{domain: "example.org"}, key1, [][]byte{cert1}, true},
+		{certKey{domain: "example.org", isRSA: true}, key3, [][]byte{cert3}, true},
+		{certKey{domain: "example.org"}, key1, [][]byte{cert1, cert2, cert3}, true},
+		{certKey{domain: "example.org"}, key1, [][]byte{cert1, {1}}, false},
+		{certKey{domain: "example.org"}, key1, [][]byte{{1}}, false},
+		{certKey{domain: "example.org"}, key1, [][]byte{cert2}, false},
+		{certKey{domain: "example.org"}, key2, [][]byte{cert1}, false},
+		{certKey{domain: "example.org"}, key1, [][]byte{cert3}, false},
+		{certKey{domain: "example.org"}, key3, [][]byte{cert1}, false},
+		{certKey{domain: "example.net"}, key1, [][]byte{cert1}, false},
+		{certKey{domain: "example.org"}, key1, [][]byte{early}, false},
+		{certKey{domain: "example.org"}, key1, [][]byte{expired}, false},
+		{certKey{domain: "example.org", isRSA: true}, key1, [][]byte{cert1}, false},
+		{certKey{domain: "example.org"}, key3, [][]byte{cert3}, false},
 	}
 	for i, test := range tt {
-		leaf, err := validCert(test.domain, test.cert, test.key)
+		leaf, err := validCert(test.ck, test.cert, test.key, now)
 		if err != nil && test.ok {
 			t.Errorf("%d: err = %v", i, err)
 		}
@@ -597,10 +1036,155 @@ func TestManagerGetCertificateBogusSNI(t *testing.T) {
 		{"fo.o", "cache.Get of fo.o"},
 	}
 	for _, tt := range tests {
-		_, err := m.GetCertificate(&tls.ClientHelloInfo{ServerName: tt.name})
+		_, err := m.GetCertificate(clientHelloInfo(tt.name, true))
 		got := fmt.Sprint(err)
 		if got != tt.wantErr {
 			t.Errorf("GetCertificate(SNI = %q) = %q; want %q", tt.name, got, tt.wantErr)
 		}
 	}
 }
+
+func TestCertRequest(t *testing.T) {
+	key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
+	if err != nil {
+		t.Fatal(err)
+	}
+	// An extension from RFC7633. Any will do.
+	ext := pkix.Extension{
+		Id:    asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1},
+		Value: []byte("dummy"),
+	}
+	b, err := certRequest(key, "example.org", []pkix.Extension{ext}, "san.example.org")
+	if err != nil {
+		t.Fatalf("certRequest: %v", err)
+	}
+	r, err := x509.ParseCertificateRequest(b)
+	if err != nil {
+		t.Fatalf("ParseCertificateRequest: %v", err)
+	}
+	var found bool
+	for _, v := range r.Extensions {
+		if v.Id.Equal(ext.Id) {
+			found = true
+			break
+		}
+	}
+	if !found {
+		t.Errorf("want %v in Extensions: %v", ext, r.Extensions)
+	}
+}
+
+func TestSupportsECDSA(t *testing.T) {
+	tests := []struct {
+		CipherSuites     []uint16
+		SignatureSchemes []tls.SignatureScheme
+		SupportedCurves  []tls.CurveID
+		ecdsaOk          bool
+	}{
+		{[]uint16{
+			tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+		}, nil, nil, false},
+		{[]uint16{
+			tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+		}, nil, nil, true},
+
+		// SignatureSchemes limits, not extends, CipherSuites
+		{[]uint16{
+			tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+		}, []tls.SignatureScheme{
+			tls.PKCS1WithSHA256, tls.ECDSAWithP256AndSHA256,
+		}, nil, false},
+		{[]uint16{
+			tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+		}, []tls.SignatureScheme{
+			tls.PKCS1WithSHA256,
+		}, nil, false},
+		{[]uint16{
+			tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+		}, []tls.SignatureScheme{
+			tls.PKCS1WithSHA256, tls.ECDSAWithP256AndSHA256,
+		}, nil, true},
+
+		{[]uint16{
+			tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+		}, []tls.SignatureScheme{
+			tls.PKCS1WithSHA256, tls.ECDSAWithP256AndSHA256,
+		}, []tls.CurveID{
+			tls.CurveP521,
+		}, false},
+		{[]uint16{
+			tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+		}, []tls.SignatureScheme{
+			tls.PKCS1WithSHA256, tls.ECDSAWithP256AndSHA256,
+		}, []tls.CurveID{
+			tls.CurveP256,
+			tls.CurveP521,
+		}, true},
+	}
+	for i, tt := range tests {
+		result := supportsECDSA(&tls.ClientHelloInfo{
+			CipherSuites:     tt.CipherSuites,
+			SignatureSchemes: tt.SignatureSchemes,
+			SupportedCurves:  tt.SupportedCurves,
+		})
+		if result != tt.ecdsaOk {
+			t.Errorf("%d: supportsECDSA = %v; want %v", i, result, tt.ecdsaOk)
+		}
+	}
+}
+
+// TODO: add same end-to-end for http-01 challenge type.
+func TestEndToEnd(t *testing.T) {
+	const domain = "example.org"
+
+	// ACME CA server
+	ca := acmetest.NewCAServer([]string{"tls-alpn-01"}, []string{domain})
+	defer ca.Close()
+
+	// User dummy server.
+	m := &Manager{
+		Prompt: AcceptTOS,
+		Client: &acme.Client{DirectoryURL: ca.URL},
+	}
+	us := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		w.Write([]byte("OK"))
+	}))
+	us.TLS = &tls.Config{
+		NextProtos: []string{"http/1.1", acme.ALPNProto},
+		GetCertificate: func(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
+			cert, err := m.GetCertificate(hello)
+			if err != nil {
+				t.Errorf("m.GetCertificate: %v", err)
+			}
+			return cert, err
+		},
+	}
+	us.StartTLS()
+	defer us.Close()
+	// In TLS-ALPN challenge verification, CA connects to the domain:443 in question.
+	// Because the domain won't resolve in tests, we need to tell the CA
+	// where to dial to instead.
+	ca.Resolve(domain, strings.TrimPrefix(us.URL, "https://"))
+
+	// A client visiting user dummy server.
+	tr := &http.Transport{
+		TLSClientConfig: &tls.Config{
+			RootCAs:    ca.Roots,
+			ServerName: domain,
+		},
+	}
+	client := &http.Client{Transport: tr}
+	res, err := client.Get(us.URL)
+	if err != nil {
+		t.Logf("CA errors: %v", ca.Errors())
+		t.Fatal(err)
+	}
+	defer res.Body.Close()
+	b, err := ioutil.ReadAll(res.Body)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if v := string(b); v != "OK" {
+		t.Errorf("user server response: %q; want 'OK'", v)
+	}
+}

+ 3 - 3
psiphon/common/crypto/acme/autocert/cache.go

@@ -16,10 +16,10 @@ import (
 var ErrCacheMiss = errors.New("acme/autocert: certificate cache miss")
 
 // Cache is used by Manager to store and retrieve previously obtained certificates
-// as opaque data.
+// and other account data as opaque blobs.
 //
-// The key argument of the methods refers to a domain name but need not be an FQDN.
-// Cache implementations should not rely on the key naming pattern.
+// Cache implementations should not rely on the key naming pattern. Keys can
+// include any printable ASCII characters, except the following: \/:*?"<>|
 type Cache interface {
 	// Get returns a certificate data for the specified key.
 	// If there's no such key, Get returns ErrCacheMiss.

+ 4 - 4
psiphon/common/crypto/acme/autocert/example_test.go

@@ -5,7 +5,6 @@
 package autocert_test
 
 import (
-	"crypto/tls"
 	"fmt"
 	"log"
 	"net/http"
@@ -22,13 +21,14 @@ func ExampleNewListener() {
 }
 
 func ExampleManager() {
-	m := autocert.Manager{
+	m := &autocert.Manager{
+		Cache:      autocert.DirCache("secret-dir"),
 		Prompt:     autocert.AcceptTOS,
-		HostPolicy: autocert.HostWhitelist("example.org"),
+		HostPolicy: autocert.HostWhitelist("example.org", "www.example.org"),
 	}
 	s := &http.Server{
 		Addr:      ":https",
-		TLSConfig: &tls.Config{GetCertificate: m.GetCertificate},
+		TLSConfig: m.TLSConfig(),
 	}
 	s.ListenAndServeTLS("", "")
 }

+ 416 - 0
psiphon/common/crypto/acme/autocert/internal/acmetest/ca.go

@@ -0,0 +1,416 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package acmetest provides types for testing acme and autocert packages.
+//
+// TODO: Consider moving this to x/crypto/acme/internal/acmetest for acme tests as well.
+package acmetest
+
+import (
+	"crypto"
+	"crypto/ecdsa"
+	"crypto/elliptic"
+	"crypto/rand"
+	"crypto/tls"
+	"crypto/x509"
+	"crypto/x509/pkix"
+	"encoding/base64"
+	"encoding/json"
+	"fmt"
+	"io"
+	"math/big"
+	"net/http"
+	"net/http/httptest"
+	"sort"
+	"strings"
+	"sync"
+	"time"
+)
+
+// CAServer is a simple test server which implements ACME spec bits needed for testing.
+type CAServer struct {
+	URL   string         // server URL after it has been started
+	Roots *x509.CertPool // CA root certificates; initialized in NewCAServer
+
+	rootKey      crypto.Signer
+	rootCert     []byte // DER encoding
+	rootTemplate *x509.Certificate
+
+	server           *httptest.Server
+	challengeTypes   []string // supported challenge types
+	domainsWhitelist []string // only these domains are valid for issuing, unless empty
+
+	mu             sync.Mutex
+	certCount      int                       // number of issued certs
+	domainAddr     map[string]string         // domain name to addr:port resolution
+	authorizations map[string]*authorization // keyed by domain name
+	errors         []error                   // encountered client errors
+}
+
+// NewCAServer creates a new ACME test server and starts serving requests.
+// The returned CAServer issues certs signed with the CA roots
+// available in the Roots field.
+//
+// The challengeTypes argument defines the supported ACME challenge types
+// sent to a client in a response for a domain authorization.
+// If domainsWhitelist is non-empty, the certs will be issued only for the specified
+// list of domains. Otherwise, any domain name is allowed.
+func NewCAServer(challengeTypes []string, domainsWhitelist []string) *CAServer {
+	var whitelist []string
+	for _, name := range domainsWhitelist {
+		whitelist = append(whitelist, name)
+	}
+	sort.Strings(whitelist)
+	ca := &CAServer{
+		challengeTypes:   challengeTypes,
+		domainsWhitelist: whitelist,
+		domainAddr:       make(map[string]string),
+		authorizations:   make(map[string]*authorization),
+	}
+
+	key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
+	if err != nil {
+		panic(fmt.Sprintf("ecdsa.GenerateKey: %v", err))
+	}
+	tmpl := &x509.Certificate{
+		SerialNumber: big.NewInt(1),
+		Subject: pkix.Name{
+			Organization: []string{"Test Acme Co"},
+			CommonName:   "Root CA",
+		},
+		NotBefore:             time.Now(),
+		NotAfter:              time.Now().Add(365 * 24 * time.Hour),
+		KeyUsage:              x509.KeyUsageCertSign,
+		BasicConstraintsValid: true,
+		IsCA: true,
+	}
+	der, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, &key.PublicKey, key)
+	if err != nil {
+		panic(fmt.Sprintf("x509.CreateCertificate: %v", err))
+	}
+	cert, err := x509.ParseCertificate(der)
+	if err != nil {
+		panic(fmt.Sprintf("x509.ParseCertificate: %v", err))
+	}
+	ca.Roots = x509.NewCertPool()
+	ca.Roots.AddCert(cert)
+	ca.rootKey = key
+	ca.rootCert = der
+	ca.rootTemplate = tmpl
+
+	ca.server = httptest.NewServer(http.HandlerFunc(ca.handle))
+	ca.URL = ca.server.URL
+	return ca
+}
+
+// Close shuts down the server and blocks until all outstanding
+// requests on this server have completed.
+func (ca *CAServer) Close() {
+	ca.server.Close()
+}
+
+// Errors returns all client errors.
+func (ca *CAServer) Errors() []error {
+	ca.mu.Lock()
+	defer ca.mu.Unlock()
+	return ca.errors
+}
+
+// Resolve adds a domain to address resolution for the ca to dial to
+// when validating challenges for the domain authorization.
+func (ca *CAServer) Resolve(domain, addr string) {
+	ca.mu.Lock()
+	defer ca.mu.Unlock()
+	ca.domainAddr[domain] = addr
+}
+
+type discovery struct {
+	NewReg   string `json:"new-reg"`
+	NewAuthz string `json:"new-authz"`
+	NewCert  string `json:"new-cert"`
+}
+
+type challenge struct {
+	URI   string `json:"uri"`
+	Type  string `json:"type"`
+	Token string `json:"token"`
+}
+
+type authorization struct {
+	Status     string      `json:"status"`
+	Challenges []challenge `json:"challenges"`
+
+	id     int
+	domain string
+}
+
+func (ca *CAServer) handle(w http.ResponseWriter, r *http.Request) {
+	w.Header().Set("Replay-Nonce", "nonce")
+	if r.Method == "HEAD" {
+		// a nonce request
+		return
+	}
+
+	// TODO: Verify nonce header for all POST requests.
+
+	switch {
+	default:
+		err := fmt.Errorf("unrecognized r.URL.Path: %s", r.URL.Path)
+		ca.addError(err)
+		http.Error(w, err.Error(), http.StatusBadRequest)
+
+	// Discovery request.
+	case r.URL.Path == "/":
+		resp := &discovery{
+			NewReg:   ca.serverURL("/new-reg"),
+			NewAuthz: ca.serverURL("/new-authz"),
+			NewCert:  ca.serverURL("/new-cert"),
+		}
+		if err := json.NewEncoder(w).Encode(resp); err != nil {
+			panic(fmt.Sprintf("discovery response: %v", err))
+		}
+
+	// Client key registration request.
+	case r.URL.Path == "/new-reg":
+		// TODO: Check the user account key against a ca.accountKeys?
+		w.Write([]byte("{}"))
+
+	// Domain authorization request.
+	case r.URL.Path == "/new-authz":
+		var req struct {
+			Identifier struct{ Value string }
+		}
+		if err := decodePayload(&req, r.Body); err != nil {
+			ca.addError(err)
+			http.Error(w, err.Error(), http.StatusBadRequest)
+			return
+		}
+		ca.mu.Lock()
+		defer ca.mu.Unlock()
+		authz, ok := ca.authorizations[req.Identifier.Value]
+		if !ok {
+			authz = &authorization{
+				domain: req.Identifier.Value,
+				Status: "pending",
+			}
+			for _, typ := range ca.challengeTypes {
+				authz.Challenges = append(authz.Challenges, challenge{
+					Type:  typ,
+					URI:   ca.serverURL("/challenge/%s/%s", typ, authz.domain),
+					Token: challengeToken(authz.domain, typ),
+				})
+			}
+			ca.authorizations[authz.domain] = authz
+		}
+		w.Header().Set("Location", ca.serverURL("/authz/%s", authz.domain))
+		w.WriteHeader(http.StatusCreated)
+		if err := json.NewEncoder(w).Encode(authz); err != nil {
+			panic(fmt.Sprintf("new authz response: %v", err))
+		}
+
+	// Accept tls-alpn-01 challenge type requests.
+	// TODO: Add http-01 and dns-01 handlers.
+	case strings.HasPrefix(r.URL.Path, "/challenge/tls-alpn-01/"):
+		domain := strings.TrimPrefix(r.URL.Path, "/challenge/tls-alpn-01/")
+		ca.mu.Lock()
+		defer ca.mu.Unlock()
+		if _, ok := ca.authorizations[domain]; !ok {
+			err := fmt.Errorf("challenge accept: no authz for %q", domain)
+			ca.addError(err)
+			http.Error(w, err.Error(), http.StatusNotFound)
+			return
+		}
+		go func(domain string) {
+			err := ca.verifyALPNChallenge(domain)
+			ca.mu.Lock()
+			defer ca.mu.Unlock()
+			authz := ca.authorizations[domain]
+			if err != nil {
+				authz.Status = "invalid"
+				return
+			}
+			authz.Status = "valid"
+
+		}(domain)
+		w.Write([]byte("{}"))
+
+	// Get authorization status requests.
+	case strings.HasPrefix(r.URL.Path, "/authz/"):
+		domain := strings.TrimPrefix(r.URL.Path, "/authz/")
+		ca.mu.Lock()
+		defer ca.mu.Unlock()
+		authz, ok := ca.authorizations[domain]
+		if !ok {
+			http.Error(w, fmt.Sprintf("no authz for %q", domain), http.StatusNotFound)
+			return
+		}
+		if err := json.NewEncoder(w).Encode(authz); err != nil {
+			panic(fmt.Sprintf("get authz for %q response: %v", domain, err))
+		}
+
+	// Cert issuance request.
+	case r.URL.Path == "/new-cert":
+		var req struct {
+			CSR string `json:"csr"`
+		}
+		decodePayload(&req, r.Body)
+		b, _ := base64.RawURLEncoding.DecodeString(req.CSR)
+		csr, err := x509.ParseCertificateRequest(b)
+		if err != nil {
+			ca.addError(err)
+			http.Error(w, err.Error(), http.StatusBadRequest)
+			return
+		}
+		names := unique(append(csr.DNSNames, csr.Subject.CommonName))
+		if err := ca.matchWhitelist(names); err != nil {
+			ca.addError(err)
+			http.Error(w, err.Error(), http.StatusUnauthorized)
+			return
+		}
+		if err := ca.authorized(names); err != nil {
+			ca.addError(err)
+			http.Error(w, err.Error(), http.StatusUnauthorized)
+			return
+		}
+		der, err := ca.leafCert(csr)
+		if err != nil {
+			err = fmt.Errorf("new-cert response: ca.leafCert: %v", err)
+			ca.addError(err)
+			http.Error(w, err.Error(), http.StatusBadRequest)
+		}
+		w.Header().Set("Link", fmt.Sprintf("<%s>; rel=up", ca.serverURL("/ca-cert")))
+		w.WriteHeader(http.StatusCreated)
+		w.Write(der)
+
+	// CA chain cert request.
+	case r.URL.Path == "/ca-cert":
+		w.Write(ca.rootCert)
+	}
+}
+
+func (ca *CAServer) addError(err error) {
+	ca.mu.Lock()
+	defer ca.mu.Unlock()
+	ca.errors = append(ca.errors, err)
+}
+
+func (ca *CAServer) serverURL(format string, arg ...interface{}) string {
+	return ca.server.URL + fmt.Sprintf(format, arg...)
+}
+
+func (ca *CAServer) matchWhitelist(dnsNames []string) error {
+	if len(ca.domainsWhitelist) == 0 {
+		return nil
+	}
+	var nomatch []string
+	for _, name := range dnsNames {
+		i := sort.SearchStrings(ca.domainsWhitelist, name)
+		if i == len(ca.domainsWhitelist) || ca.domainsWhitelist[i] != name {
+			nomatch = append(nomatch, name)
+		}
+	}
+	if len(nomatch) > 0 {
+		return fmt.Errorf("matchWhitelist: some domains don't match: %q", nomatch)
+	}
+	return nil
+}
+
+func (ca *CAServer) authorized(dnsNames []string) error {
+	ca.mu.Lock()
+	defer ca.mu.Unlock()
+	var noauthz []string
+	for _, name := range dnsNames {
+		authz, ok := ca.authorizations[name]
+		if !ok || authz.Status != "valid" {
+			noauthz = append(noauthz, name)
+		}
+	}
+	if len(noauthz) > 0 {
+		return fmt.Errorf("CAServer: no authz for %q", noauthz)
+	}
+	return nil
+}
+
+func (ca *CAServer) leafCert(csr *x509.CertificateRequest) (der []byte, err error) {
+	ca.mu.Lock()
+	defer ca.mu.Unlock()
+	ca.certCount++ // next leaf cert serial number
+	leaf := &x509.Certificate{
+		SerialNumber:          big.NewInt(int64(ca.certCount)),
+		Subject:               pkix.Name{Organization: []string{"Test Acme Co"}},
+		NotBefore:             time.Now(),
+		NotAfter:              time.Now().Add(90 * 24 * time.Hour),
+		KeyUsage:              x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
+		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
+		DNSNames:              csr.DNSNames,
+		BasicConstraintsValid: true,
+	}
+	if len(csr.DNSNames) == 0 {
+		leaf.DNSNames = []string{csr.Subject.CommonName}
+	}
+	return x509.CreateCertificate(rand.Reader, leaf, ca.rootTemplate, csr.PublicKey, ca.rootKey)
+}
+
+func (ca *CAServer) addr(domain string) (string, error) {
+	ca.mu.Lock()
+	defer ca.mu.Unlock()
+	addr, ok := ca.domainAddr[domain]
+	if !ok {
+		return "", fmt.Errorf("CAServer: no addr resolution for %q", domain)
+	}
+	return addr, nil
+}
+
+func (ca *CAServer) verifyALPNChallenge(domain string) error {
+	const acmeALPNProto = "acme-tls/1"
+
+	addr, err := ca.addr(domain)
+	if err != nil {
+		return err
+	}
+	conn, err := tls.Dial("tcp", addr, &tls.Config{
+		ServerName:         domain,
+		InsecureSkipVerify: true,
+		NextProtos:         []string{acmeALPNProto},
+	})
+	if err != nil {
+		return err
+	}
+	if v := conn.ConnectionState().NegotiatedProtocol; v != acmeALPNProto {
+		return fmt.Errorf("CAServer: verifyALPNChallenge: negotiated proto is %q; want %q", v, acmeALPNProto)
+	}
+	if n := len(conn.ConnectionState().PeerCertificates); n != 1 {
+		return fmt.Errorf("len(PeerCertificates) = %d; want 1", n)
+	}
+	// TODO: verify conn.ConnectionState().PeerCertificates[0]
+	return nil
+}
+
+func decodePayload(v interface{}, r io.Reader) error {
+	var req struct{ Payload string }
+	if err := json.NewDecoder(r).Decode(&req); err != nil {
+		return err
+	}
+	payload, err := base64.RawURLEncoding.DecodeString(req.Payload)
+	if err != nil {
+		return err
+	}
+	return json.Unmarshal(payload, v)
+}
+
+func challengeToken(domain, challType string) string {
+	return fmt.Sprintf("token-%s-%s", domain, challType)
+}
+
+func unique(a []string) []string {
+	seen := make(map[string]bool)
+	var res []string
+	for _, s := range a {
+		if s != "" && !seen[s] {
+			seen[s] = true
+			res = append(res, s)
+		}
+	}
+	return res
+}

+ 2 - 5
psiphon/common/crypto/acme/autocert/listener.go

@@ -72,11 +72,8 @@ func NewListener(domains ...string) net.Listener {
 // the Manager m's Prompt, Cache, HostPolicy, and other desired options.
 func (m *Manager) Listener() net.Listener {
 	ln := &listener{
-		m: m,
-		conf: &tls.Config{
-			GetCertificate: m.GetCertificate,           // bonus: panic on nil m
-			NextProtos:     []string{"h2", "http/1.1"}, // Enable HTTP/2
-		},
+		m:    m,
+		conf: m.TLSConfig(),
 	}
 	ln.tcpListener, ln.tcpListenErr = net.Listen("tcp", ":443")
 	return ln

+ 31 - 14
psiphon/common/crypto/acme/autocert/renewal.go

@@ -17,9 +17,9 @@ const renewJitter = time.Hour
 // domainRenewal tracks the state used by the periodic timers
 // renewing a single domain's cert.
 type domainRenewal struct {
-	m      *Manager
-	domain string
-	key    crypto.Signer
+	m   *Manager
+	ck  certKey
+	key crypto.Signer
 
 	timerMu sync.Mutex
 	timer   *time.Timer
@@ -71,25 +71,43 @@ func (dr *domainRenewal) renew() {
 	testDidRenewLoop(next, err)
 }
 
+// updateState locks and replaces the relevant Manager.state item with the given
+// state. It additionally updates dr.key with the given state's key.
+func (dr *domainRenewal) updateState(state *certState) {
+	dr.m.stateMu.Lock()
+	defer dr.m.stateMu.Unlock()
+	dr.key = state.key
+	dr.m.state[dr.ck] = state
+}
+
 // do is similar to Manager.createCert but it doesn't lock a Manager.state item.
 // Instead, it requests a new certificate independently and, upon success,
 // replaces dr.m.state item with a new one and updates cache for the given domain.
 //
-// It may return immediately if the expiration date of the currently cached cert
-// is far enough in the future.
+// It may lock and update the Manager.state if the expiration date of the currently
+// cached cert is far enough in the future.
 //
 // The returned value is a time interval after which the renewal should occur again.
 func (dr *domainRenewal) do(ctx context.Context) (time.Duration, error) {
 	// a race is likely unavoidable in a distributed environment
 	// but we try nonetheless
-	if tlscert, err := dr.m.cacheGet(ctx, dr.domain); err == nil {
+	if tlscert, err := dr.m.cacheGet(ctx, dr.ck); err == nil {
 		next := dr.next(tlscert.Leaf.NotAfter)
 		if next > dr.m.renewBefore()+renewJitter {
-			return next, nil
+			signer, ok := tlscert.PrivateKey.(crypto.Signer)
+			if ok {
+				state := &certState{
+					key:  signer,
+					cert: tlscert.Certificate,
+					leaf: tlscert.Leaf,
+				}
+				dr.updateState(state)
+				return next, nil
+			}
 		}
 	}
 
-	der, leaf, err := dr.m.authorizedCert(ctx, dr.key, dr.domain)
+	der, leaf, err := dr.m.authorizedCert(ctx, dr.key, dr.ck)
 	if err != nil {
 		return 0, err
 	}
@@ -102,16 +120,15 @@ func (dr *domainRenewal) do(ctx context.Context) (time.Duration, error) {
 	if err != nil {
 		return 0, err
 	}
-	dr.m.cachePut(ctx, dr.domain, tlscert)
-	dr.m.stateMu.Lock()
-	defer dr.m.stateMu.Unlock()
-	// m.state is guaranteed to be non-nil at this point
-	dr.m.state[dr.domain] = state
+	if err := dr.m.cachePut(ctx, dr.ck, tlscert); err != nil {
+		return 0, err
+	}
+	dr.updateState(state)
 	return dr.next(leaf.NotAfter), nil
 }
 
 func (dr *domainRenewal) next(expiry time.Time) time.Duration {
-	d := expiry.Sub(timeNow()) - dr.m.renewBefore()
+	d := expiry.Sub(dr.m.now()) - dr.m.renewBefore()
 	// add a bit of randomness to renew deadline
 	n := pseudoRand.int63n(int64(renewJitter))
 	d -= time.Duration(n)

+ 158 - 20
psiphon/common/crypto/acme/autocert/renewal_test.go

@@ -23,10 +23,10 @@ import (
 
 func TestRenewalNext(t *testing.T) {
 	now := time.Now()
-	timeNow = func() time.Time { return now }
-	defer func() { timeNow = time.Now }()
-
-	man := &Manager{RenewBefore: 7 * 24 * time.Hour}
+	man := &Manager{
+		RenewBefore: 7 * 24 * time.Hour,
+		nowFunc:     func() time.Time { return now },
+	}
 	defer man.stopRenew()
 	tt := []struct {
 		expiry   time.Time
@@ -48,8 +48,6 @@ func TestRenewalNext(t *testing.T) {
 }
 
 func TestRenewFromCache(t *testing.T) {
-	const domain = "example.org"
-
 	// ACME CA server stub
 	var ca *httptest.Server
 	ca = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@@ -84,7 +82,7 @@ func TestRenewFromCache(t *testing.T) {
 			if err != nil {
 				t.Fatalf("new-cert: CSR: %v", err)
 			}
-			der, err := dummyCert(csr.PublicKey, domain)
+			der, err := dummyCert(csr.PublicKey, exampleDomain)
 			if err != nil {
 				t.Fatalf("new-cert: dummyCert: %v", err)
 			}
@@ -105,30 +103,28 @@ func TestRenewFromCache(t *testing.T) {
 	}))
 	defer ca.Close()
 
-	// use EC key to run faster on 386
-	key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
-	if err != nil {
-		t.Fatal(err)
-	}
 	man := &Manager{
 		Prompt:      AcceptTOS,
-		Cache:       newMemCache(),
+		Cache:       newMemCache(t),
 		RenewBefore: 24 * time.Hour,
 		Client: &acme.Client{
-			Key:          key,
 			DirectoryURL: ca.URL,
 		},
 	}
 	defer man.stopRenew()
 
 	// cache an almost expired cert
+	key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
+	if err != nil {
+		t.Fatal(err)
+	}
 	now := time.Now()
-	cert, err := dateDummyCert(key.Public(), now.Add(-2*time.Hour), now.Add(time.Minute), domain)
+	cert, err := dateDummyCert(key.Public(), now.Add(-2*time.Hour), now.Add(time.Minute), exampleDomain)
 	if err != nil {
 		t.Fatal(err)
 	}
 	tlscert := &tls.Certificate{PrivateKey: key, Certificate: [][]byte{cert}}
-	if err := man.cachePut(context.Background(), domain, tlscert); err != nil {
+	if err := man.cachePut(context.Background(), exampleCertKey, tlscert); err != nil {
 		t.Fatal(err)
 	}
 
@@ -152,7 +148,7 @@ func TestRenewFromCache(t *testing.T) {
 
 		// ensure the new cert is cached
 		after := time.Now().Add(future)
-		tlscert, err := man.cacheGet(context.Background(), domain)
+		tlscert, err := man.cacheGet(context.Background(), exampleCertKey)
 		if err != nil {
 			t.Fatalf("man.cacheGet: %v", err)
 		}
@@ -163,9 +159,9 @@ func TestRenewFromCache(t *testing.T) {
 		// verify the old cert is also replaced in memory
 		man.stateMu.Lock()
 		defer man.stateMu.Unlock()
-		s := man.state[domain]
+		s := man.state[exampleCertKey]
 		if s == nil {
-			t.Fatalf("m.state[%q] is nil", domain)
+			t.Fatalf("m.state[%q] is nil", exampleCertKey)
 		}
 		tlscert, err = s.tlscert()
 		if err != nil {
@@ -177,7 +173,7 @@ func TestRenewFromCache(t *testing.T) {
 	}
 
 	// trigger renew
-	hello := &tls.ClientHelloInfo{ServerName: domain}
+	hello := clientHelloInfo(exampleDomain, true)
 	if _, err := man.GetCertificate(hello); err != nil {
 		t.Fatal(err)
 	}
@@ -189,3 +185,145 @@ func TestRenewFromCache(t *testing.T) {
 	case <-done:
 	}
 }
+
+func TestRenewFromCacheAlreadyRenewed(t *testing.T) {
+	man := &Manager{
+		Prompt:      AcceptTOS,
+		Cache:       newMemCache(t),
+		RenewBefore: 24 * time.Hour,
+		Client: &acme.Client{
+			DirectoryURL: "invalid",
+		},
+	}
+	defer man.stopRenew()
+
+	// cache a recently renewed cert with a different private key
+	newKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
+	if err != nil {
+		t.Fatal(err)
+	}
+	now := time.Now()
+	newCert, err := dateDummyCert(newKey.Public(), now.Add(-2*time.Hour), now.Add(time.Hour*24*90), exampleDomain)
+	if err != nil {
+		t.Fatal(err)
+	}
+	newLeaf, err := validCert(exampleCertKey, [][]byte{newCert}, newKey, now)
+	if err != nil {
+		t.Fatal(err)
+	}
+	newTLSCert := &tls.Certificate{PrivateKey: newKey, Certificate: [][]byte{newCert}, Leaf: newLeaf}
+	if err := man.cachePut(context.Background(), exampleCertKey, newTLSCert); err != nil {
+		t.Fatal(err)
+	}
+
+	// set internal state to an almost expired cert
+	key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
+	if err != nil {
+		t.Fatal(err)
+	}
+	oldCert, err := dateDummyCert(key.Public(), now.Add(-2*time.Hour), now.Add(time.Minute), exampleDomain)
+	if err != nil {
+		t.Fatal(err)
+	}
+	oldLeaf, err := validCert(exampleCertKey, [][]byte{oldCert}, key, now)
+	if err != nil {
+		t.Fatal(err)
+	}
+	man.stateMu.Lock()
+	if man.state == nil {
+		man.state = make(map[certKey]*certState)
+	}
+	s := &certState{
+		key:  key,
+		cert: [][]byte{oldCert},
+		leaf: oldLeaf,
+	}
+	man.state[exampleCertKey] = s
+	man.stateMu.Unlock()
+
+	// veriy the renewal accepted the newer cached cert
+	defer func() {
+		testDidRenewLoop = func(next time.Duration, err error) {}
+	}()
+	done := make(chan struct{})
+	testDidRenewLoop = func(next time.Duration, err error) {
+		defer close(done)
+		if err != nil {
+			t.Errorf("testDidRenewLoop: %v", err)
+		}
+		// Next should be about 90 days
+		// Previous expiration was within 1 min.
+		future := 88 * 24 * time.Hour
+		if next < future {
+			t.Errorf("testDidRenewLoop: next = %v; want >= %v", next, future)
+		}
+
+		// ensure the cached cert was not modified
+		tlscert, err := man.cacheGet(context.Background(), exampleCertKey)
+		if err != nil {
+			t.Fatalf("man.cacheGet: %v", err)
+		}
+		if !tlscert.Leaf.NotAfter.Equal(newLeaf.NotAfter) {
+			t.Errorf("cache leaf.NotAfter = %v; want == %v", tlscert.Leaf.NotAfter, newLeaf.NotAfter)
+		}
+
+		// verify the old cert is also replaced in memory
+		man.stateMu.Lock()
+		defer man.stateMu.Unlock()
+		s := man.state[exampleCertKey]
+		if s == nil {
+			t.Fatalf("m.state[%q] is nil", exampleCertKey)
+		}
+		stateKey := s.key.Public().(*ecdsa.PublicKey)
+		if stateKey.X.Cmp(newKey.X) != 0 || stateKey.Y.Cmp(newKey.Y) != 0 {
+			t.Fatalf("state key was not updated from cache x: %v y: %v; want x: %v y: %v", stateKey.X, stateKey.Y, newKey.X, newKey.Y)
+		}
+		tlscert, err = s.tlscert()
+		if err != nil {
+			t.Fatalf("s.tlscert: %v", err)
+		}
+		if !tlscert.Leaf.NotAfter.Equal(newLeaf.NotAfter) {
+			t.Errorf("state leaf.NotAfter = %v; want == %v", tlscert.Leaf.NotAfter, newLeaf.NotAfter)
+		}
+
+		// verify the private key is replaced in the renewal state
+		r := man.renewal[exampleCertKey]
+		if r == nil {
+			t.Fatalf("m.renewal[%q] is nil", exampleCertKey)
+		}
+		renewalKey := r.key.Public().(*ecdsa.PublicKey)
+		if renewalKey.X.Cmp(newKey.X) != 0 || renewalKey.Y.Cmp(newKey.Y) != 0 {
+			t.Fatalf("renewal private key was not updated from cache x: %v y: %v; want x: %v y: %v", renewalKey.X, renewalKey.Y, newKey.X, newKey.Y)
+		}
+
+	}
+
+	// assert the expiring cert is returned from state
+	hello := clientHelloInfo(exampleDomain, true)
+	tlscert, err := man.GetCertificate(hello)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if !oldLeaf.NotAfter.Equal(tlscert.Leaf.NotAfter) {
+		t.Errorf("state leaf.NotAfter = %v; want == %v", tlscert.Leaf.NotAfter, oldLeaf.NotAfter)
+	}
+
+	// trigger renew
+	go man.renew(exampleCertKey, s.key, s.leaf.NotAfter)
+
+	// wait for renew loop
+	select {
+	case <-time.After(10 * time.Second):
+		t.Fatal("renew took too long to occur")
+	case <-done:
+		// assert the new cert is returned from state after renew
+		hello := clientHelloInfo(exampleDomain, true)
+		tlscert, err := man.GetCertificate(hello)
+		if err != nil {
+			t.Fatal(err)
+		}
+		if !newTLSCert.Leaf.NotAfter.Equal(tlscert.Leaf.NotAfter) {
+			t.Errorf("state leaf.NotAfter = %v; want == %v", tlscert.Leaf.NotAfter, newTLSCert.Leaf.NotAfter)
+		}
+	}
+}

+ 281 - 0
psiphon/common/crypto/acme/http.go

@@ -0,0 +1,281 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package acme
+
+import (
+	"bytes"
+	"context"
+	"crypto"
+	"crypto/rand"
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"math/big"
+	"net/http"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// retryTimer encapsulates common logic for retrying unsuccessful requests.
+// It is not safe for concurrent use.
+type retryTimer struct {
+	// backoffFn provides backoff delay sequence for retries.
+	// See Client.RetryBackoff doc comment.
+	backoffFn func(n int, r *http.Request, res *http.Response) time.Duration
+	// n is the current retry attempt.
+	n int
+}
+
+func (t *retryTimer) inc() {
+	t.n++
+}
+
+// backoff pauses the current goroutine as described in Client.RetryBackoff.
+func (t *retryTimer) backoff(ctx context.Context, r *http.Request, res *http.Response) error {
+	d := t.backoffFn(t.n, r, res)
+	if d <= 0 {
+		return fmt.Errorf("acme: no more retries for %s; tried %d time(s)", r.URL, t.n)
+	}
+	wakeup := time.NewTimer(d)
+	defer wakeup.Stop()
+	select {
+	case <-ctx.Done():
+		return ctx.Err()
+	case <-wakeup.C:
+		return nil
+	}
+}
+
+func (c *Client) retryTimer() *retryTimer {
+	f := c.RetryBackoff
+	if f == nil {
+		f = defaultBackoff
+	}
+	return &retryTimer{backoffFn: f}
+}
+
+// defaultBackoff provides default Client.RetryBackoff implementation
+// using a truncated exponential backoff algorithm,
+// as described in Client.RetryBackoff.
+//
+// The n argument is always bounded between 1 and 30.
+// The returned value is always greater than 0.
+func defaultBackoff(n int, r *http.Request, res *http.Response) time.Duration {
+	const max = 10 * time.Second
+	var jitter time.Duration
+	if x, err := rand.Int(rand.Reader, big.NewInt(1000)); err == nil {
+		// Set the minimum to 1ms to avoid a case where
+		// an invalid Retry-After value is parsed into 0 below,
+		// resulting in the 0 returned value which would unintentionally
+		// stop the retries.
+		jitter = (1 + time.Duration(x.Int64())) * time.Millisecond
+	}
+	if v, ok := res.Header["Retry-After"]; ok {
+		return retryAfter(v[0]) + jitter
+	}
+
+	if n < 1 {
+		n = 1
+	}
+	if n > 30 {
+		n = 30
+	}
+	d := time.Duration(1<<uint(n-1))*time.Second + jitter
+	if d > max {
+		return max
+	}
+	return d
+}
+
+// retryAfter parses a Retry-After HTTP header value,
+// trying to convert v into an int (seconds) or use http.ParseTime otherwise.
+// It returns zero value if v cannot be parsed.
+func retryAfter(v string) time.Duration {
+	if i, err := strconv.Atoi(v); err == nil {
+		return time.Duration(i) * time.Second
+	}
+	t, err := http.ParseTime(v)
+	if err != nil {
+		return 0
+	}
+	return t.Sub(timeNow())
+}
+
+// resOkay is a function that reports whether the provided response is okay.
+// It is expected to keep the response body unread.
+type resOkay func(*http.Response) bool
+
+// wantStatus returns a function which reports whether the code
+// matches the status code of a response.
+func wantStatus(codes ...int) resOkay {
+	return func(res *http.Response) bool {
+		for _, code := range codes {
+			if code == res.StatusCode {
+				return true
+			}
+		}
+		return false
+	}
+}
+
+// get issues an unsigned GET request to the specified URL.
+// It returns a non-error value only when ok reports true.
+//
+// get retries unsuccessful attempts according to c.RetryBackoff
+// until the context is done or a non-retriable error is received.
+func (c *Client) get(ctx context.Context, url string, ok resOkay) (*http.Response, error) {
+	retry := c.retryTimer()
+	for {
+		req, err := http.NewRequest("GET", url, nil)
+		if err != nil {
+			return nil, err
+		}
+		res, err := c.doNoRetry(ctx, req)
+		switch {
+		case err != nil:
+			return nil, err
+		case ok(res):
+			return res, nil
+		case isRetriable(res.StatusCode):
+			retry.inc()
+			resErr := responseError(res)
+			res.Body.Close()
+			// Ignore the error value from retry.backoff
+			// and return the one from last retry, as received from the CA.
+			if retry.backoff(ctx, req, res) != nil {
+				return nil, resErr
+			}
+		default:
+			defer res.Body.Close()
+			return nil, responseError(res)
+		}
+	}
+}
+
+// post issues a signed POST request in JWS format using the provided key
+// to the specified URL.
+// It returns a non-error value only when ok reports true.
+//
+// post retries unsuccessful attempts according to c.RetryBackoff
+// until the context is done or a non-retriable error is received.
+// It uses postNoRetry to make individual requests.
+func (c *Client) post(ctx context.Context, key crypto.Signer, url string, body interface{}, ok resOkay) (*http.Response, error) {
+	retry := c.retryTimer()
+	for {
+		res, req, err := c.postNoRetry(ctx, key, url, body)
+		if err != nil {
+			return nil, err
+		}
+		if ok(res) {
+			return res, nil
+		}
+		resErr := responseError(res)
+		res.Body.Close()
+		switch {
+		// Check for bad nonce before isRetriable because it may have been returned
+		// with an unretriable response code such as 400 Bad Request.
+		case isBadNonce(resErr):
+			// Consider any previously stored nonce values to be invalid.
+			c.clearNonces()
+		case !isRetriable(res.StatusCode):
+			return nil, resErr
+		}
+		retry.inc()
+		// Ignore the error value from retry.backoff
+		// and return the one from last retry, as received from the CA.
+		if err := retry.backoff(ctx, req, res); err != nil {
+			return nil, resErr
+		}
+	}
+}
+
+// postNoRetry signs the body with the given key and POSTs it to the provided url.
+// The body argument must be JSON-serializable.
+// It is used by c.post to retry unsuccessful attempts.
+func (c *Client) postNoRetry(ctx context.Context, key crypto.Signer, url string, body interface{}) (*http.Response, *http.Request, error) {
+	nonce, err := c.popNonce(ctx, url)
+	if err != nil {
+		return nil, nil, err
+	}
+	b, err := jwsEncodeJSON(body, key, nonce)
+	if err != nil {
+		return nil, nil, err
+	}
+	req, err := http.NewRequest("POST", url, bytes.NewReader(b))
+	if err != nil {
+		return nil, nil, err
+	}
+	req.Header.Set("Content-Type", "application/jose+json")
+	res, err := c.doNoRetry(ctx, req)
+	if err != nil {
+		return nil, nil, err
+	}
+	c.addNonce(res.Header)
+	return res, req, nil
+}
+
+// doNoRetry issues a request req, replacing its context (if any) with ctx.
+func (c *Client) doNoRetry(ctx context.Context, req *http.Request) (*http.Response, error) {
+	res, err := c.httpClient().Do(req.WithContext(ctx))
+	if err != nil {
+		select {
+		case <-ctx.Done():
+			// Prefer the unadorned context error.
+			// (The acme package had tests assuming this, previously from ctxhttp's
+			// behavior, predating net/http supporting contexts natively)
+			// TODO(bradfitz): reconsider this in the future. But for now this
+			// requires no test updates.
+			return nil, ctx.Err()
+		default:
+			return nil, err
+		}
+	}
+	return res, nil
+}
+
+func (c *Client) httpClient() *http.Client {
+	if c.HTTPClient != nil {
+		return c.HTTPClient
+	}
+	return http.DefaultClient
+}
+
+// isBadNonce reports whether err is an ACME "badnonce" error.
+func isBadNonce(err error) bool {
+	// According to the spec badNonce is urn:ietf:params:acme:error:badNonce.
+	// However, ACME servers in the wild return their versions of the error.
+	// See https://tools.ietf.org/html/draft-ietf-acme-acme-02#section-5.4
+	// and https://github.com/letsencrypt/boulder/blob/0e07eacb/docs/acme-divergences.md#section-66.
+	ae, ok := err.(*Error)
+	return ok && strings.HasSuffix(strings.ToLower(ae.ProblemType), ":badnonce")
+}
+
+// isRetriable reports whether a request can be retried
+// based on the response status code.
+//
+// Note that a "bad nonce" error is returned with a non-retriable 400 Bad Request code.
+// Callers should parse the response and check with isBadNonce.
+func isRetriable(code int) bool {
+	return code <= 399 || code >= 500 || code == http.StatusTooManyRequests
+}
+
+// responseError creates an error of Error type from resp.
+func responseError(resp *http.Response) error {
+	// don't care if ReadAll returns an error:
+	// json.Unmarshal will fail in that case anyway
+	b, _ := ioutil.ReadAll(resp.Body)
+	e := &wireError{Status: resp.StatusCode}
+	if err := json.Unmarshal(b, e); err != nil {
+		// this is not a regular error response:
+		// populate detail with anything we received,
+		// e.Status will already contain HTTP response code value
+		e.Detail = string(b)
+		if e.Detail == "" {
+			e.Detail = resp.Status
+		}
+	}
+	return e.error(resp.Header)
+}

+ 209 - 0
psiphon/common/crypto/acme/http_test.go

@@ -0,0 +1,209 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package acme
+
+import (
+	"context"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+	"net/http/httptest"
+	"reflect"
+	"strings"
+	"testing"
+	"time"
+)
+
+func TestDefaultBackoff(t *testing.T) {
+	tt := []struct {
+		nretry     int
+		retryAfter string        // Retry-After header
+		out        time.Duration // expected min; max = min + jitter
+	}{
+		{-1, "", time.Second},       // verify the lower bound is 1
+		{0, "", time.Second},        // verify the lower bound is 1
+		{100, "", 10 * time.Second}, // verify the ceiling
+		{1, "3600", time.Hour},      // verify the header value is used
+		{1, "", 1 * time.Second},
+		{2, "", 2 * time.Second},
+		{3, "", 4 * time.Second},
+		{4, "", 8 * time.Second},
+	}
+	for i, test := range tt {
+		r := httptest.NewRequest("GET", "/", nil)
+		resp := &http.Response{Header: http.Header{}}
+		if test.retryAfter != "" {
+			resp.Header.Set("Retry-After", test.retryAfter)
+		}
+		d := defaultBackoff(test.nretry, r, resp)
+		max := test.out + time.Second // + max jitter
+		if d < test.out || max < d {
+			t.Errorf("%d: defaultBackoff(%v) = %v; want between %v and %v", i, test.nretry, d, test.out, max)
+		}
+	}
+}
+
+func TestErrorResponse(t *testing.T) {
+	s := `{
+		"status": 400,
+		"type": "urn:acme:error:xxx",
+		"detail": "text"
+	}`
+	res := &http.Response{
+		StatusCode: 400,
+		Status:     "400 Bad Request",
+		Body:       ioutil.NopCloser(strings.NewReader(s)),
+		Header:     http.Header{"X-Foo": {"bar"}},
+	}
+	err := responseError(res)
+	v, ok := err.(*Error)
+	if !ok {
+		t.Fatalf("err = %+v (%T); want *Error type", err, err)
+	}
+	if v.StatusCode != 400 {
+		t.Errorf("v.StatusCode = %v; want 400", v.StatusCode)
+	}
+	if v.ProblemType != "urn:acme:error:xxx" {
+		t.Errorf("v.ProblemType = %q; want urn:acme:error:xxx", v.ProblemType)
+	}
+	if v.Detail != "text" {
+		t.Errorf("v.Detail = %q; want text", v.Detail)
+	}
+	if !reflect.DeepEqual(v.Header, res.Header) {
+		t.Errorf("v.Header = %+v; want %+v", v.Header, res.Header)
+	}
+}
+
+func TestPostWithRetries(t *testing.T) {
+	var count int
+	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		count++
+		w.Header().Set("Replay-Nonce", fmt.Sprintf("nonce%d", count))
+		if r.Method == "HEAD" {
+			// We expect the client to do 2 head requests to fetch
+			// nonces, one to start and another after getting badNonce
+			return
+		}
+
+		head, err := decodeJWSHead(r)
+		switch {
+		case err != nil:
+			t.Errorf("decodeJWSHead: %v", err)
+		case head.Nonce == "":
+			t.Error("head.Nonce is empty")
+		case head.Nonce == "nonce1":
+			// Return a badNonce error to force the call to retry.
+			w.Header().Set("Retry-After", "0")
+			w.WriteHeader(http.StatusBadRequest)
+			w.Write([]byte(`{"type":"urn:ietf:params:acme:error:badNonce"}`))
+			return
+		}
+		// Make client.Authorize happy; we're not testing its result.
+		w.WriteHeader(http.StatusCreated)
+		w.Write([]byte(`{"status":"valid"}`))
+	}))
+	defer ts.Close()
+
+	client := &Client{Key: testKey, dir: &Directory{AuthzURL: ts.URL}}
+	// This call will fail with badNonce, causing a retry
+	if _, err := client.Authorize(context.Background(), "example.com"); err != nil {
+		t.Errorf("client.Authorize 1: %v", err)
+	}
+	if count != 4 {
+		t.Errorf("total requests count: %d; want 4", count)
+	}
+}
+
+func TestRetryErrorType(t *testing.T) {
+	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		w.Header().Set("Replay-Nonce", "nonce")
+		w.WriteHeader(http.StatusTooManyRequests)
+		w.Write([]byte(`{"type":"rateLimited"}`))
+	}))
+	defer ts.Close()
+
+	client := &Client{
+		Key: testKey,
+		RetryBackoff: func(n int, r *http.Request, res *http.Response) time.Duration {
+			// Do no retries.
+			return 0
+		},
+		dir: &Directory{AuthzURL: ts.URL},
+	}
+
+	t.Run("post", func(t *testing.T) {
+		testRetryErrorType(t, func() error {
+			_, err := client.Authorize(context.Background(), "example.com")
+			return err
+		})
+	})
+	t.Run("get", func(t *testing.T) {
+		testRetryErrorType(t, func() error {
+			_, err := client.GetAuthorization(context.Background(), ts.URL)
+			return err
+		})
+	})
+}
+
+func testRetryErrorType(t *testing.T, callClient func() error) {
+	t.Helper()
+	err := callClient()
+	if err == nil {
+		t.Fatal("client.Authorize returned nil error")
+	}
+	acmeErr, ok := err.(*Error)
+	if !ok {
+		t.Fatalf("err is %v (%T); want *Error", err, err)
+	}
+	if acmeErr.StatusCode != http.StatusTooManyRequests {
+		t.Errorf("acmeErr.StatusCode = %d; want %d", acmeErr.StatusCode, http.StatusTooManyRequests)
+	}
+	if acmeErr.ProblemType != "rateLimited" {
+		t.Errorf("acmeErr.ProblemType = %q; want 'rateLimited'", acmeErr.ProblemType)
+	}
+}
+
+func TestRetryBackoffArgs(t *testing.T) {
+	const resCode = http.StatusInternalServerError
+	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		w.Header().Set("Replay-Nonce", "test-nonce")
+		w.WriteHeader(resCode)
+	}))
+	defer ts.Close()
+
+	// Canceled in backoff.
+	ctx, cancel := context.WithCancel(context.Background())
+
+	var nretry int
+	backoff := func(n int, r *http.Request, res *http.Response) time.Duration {
+		nretry++
+		if n != nretry {
+			t.Errorf("n = %d; want %d", n, nretry)
+		}
+		if nretry == 3 {
+			cancel()
+		}
+
+		if r == nil {
+			t.Error("r is nil")
+		}
+		if res.StatusCode != resCode {
+			t.Errorf("res.StatusCode = %d; want %d", res.StatusCode, resCode)
+		}
+		return time.Millisecond
+	}
+
+	client := &Client{
+		Key:          testKey,
+		RetryBackoff: backoff,
+		dir:          &Directory{AuthzURL: ts.URL},
+	}
+	if _, err := client.Authorize(ctx, "example.com"); err == nil {
+		t.Error("err is nil")
+	}
+	if nretry != 3 {
+		t.Errorf("nretry = %d; want 3", nretry)
+	}
+}

+ 35 - 1
psiphon/common/crypto/acme/types.go

@@ -5,6 +5,8 @@
 package acme
 
 import (
+	"crypto"
+	"crypto/x509"
 	"errors"
 	"fmt"
 	"net/http"
@@ -102,7 +104,7 @@ func RateLimit(err error) (time.Duration, bool) {
 	if e.Header == nil {
 		return 0, true
 	}
-	return retryAfter(e.Header.Get("Retry-After"), 0), true
+	return retryAfter(e.Header.Get("Retry-After")), true
 }
 
 // Account is a user account. It is associated with a private key.
@@ -293,3 +295,35 @@ func (e *wireError) error(h http.Header) *Error {
 		Header:      h,
 	}
 }
+
+// CertOption is an optional argument type for the TLS ChallengeCert methods for
+// customizing a temporary certificate for TLS-based challenges.
+type CertOption interface {
+	privateCertOpt()
+}
+
+// WithKey creates an option holding a private/public key pair.
+// The private part signs a certificate, and the public part represents the signee.
+func WithKey(key crypto.Signer) CertOption {
+	return &certOptKey{key}
+}
+
+type certOptKey struct {
+	key crypto.Signer
+}
+
+func (*certOptKey) privateCertOpt() {}
+
+// WithTemplate creates an option for specifying a certificate template.
+// See x509.CreateCertificate for template usage details.
+//
+// In TLS ChallengeCert methods, the template is also used as parent,
+// resulting in a self-signed certificate.
+// The DNSNames field of t is always overwritten for tls-sni challenge certs.
+func WithTemplate(t *x509.Certificate) CertOption {
+	return (*certOptTemplate)(t)
+}
+
+type certOptTemplate x509.Certificate
+
+func (*certOptTemplate) privateCertOpt() {}

+ 285 - 0
psiphon/common/crypto/argon2/argon2.go

@@ -0,0 +1,285 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package argon2 implements the key derivation function Argon2.
+// Argon2 was selected as the winner of the Password Hashing Competition and can
+// be used to derive cryptographic keys from passwords.
+//
+// For a detailed specification of Argon2 see [1].
+//
+// If you aren't sure which function you need, use Argon2id (IDKey) and
+// the parameter recommendations for your scenario.
+//
+//
+// Argon2i
+//
+// Argon2i (implemented by Key) is the side-channel resistant version of Argon2.
+// It uses data-independent memory access, which is preferred for password
+// hashing and password-based key derivation. Argon2i requires more passes over
+// memory than Argon2id to protect from trade-off attacks. The recommended
+// parameters (taken from [2]) for non-interactive operations are time=3 and to
+// use the maximum available memory.
+//
+//
+// Argon2id
+//
+// Argon2id (implemented by IDKey) is a hybrid version of Argon2 combining
+// Argon2i and Argon2d. It uses data-independent memory access for the first
+// half of the first iteration over the memory and data-dependent memory access
+// for the rest. Argon2id is side-channel resistant and provides better brute-
+// force cost savings due to time-memory tradeoffs than Argon2i. The recommended
+// parameters for non-interactive operations (taken from [2]) are time=1 and to
+// use the maximum available memory.
+//
+// [1] https://github.com/P-H-C/phc-winner-argon2/blob/master/argon2-specs.pdf
+// [2] https://tools.ietf.org/html/draft-irtf-cfrg-argon2-03#section-9.3
+package argon2
+
+import (
+	"encoding/binary"
+	"sync"
+
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/blake2b"
+)
+
+// The Argon2 version implemented by this package.
+const Version = 0x13
+
+const (
+	argon2d = iota
+	argon2i
+	argon2id
+)
+
+// Key derives a key from the password, salt, and cost parameters using Argon2i
+// returning a byte slice of length keyLen that can be used as cryptographic
+// key. The CPU cost and parallelism degree must be greater than zero.
+//
+// For example, you can get a derived key for e.g. AES-256 (which needs a
+// 32-byte key) by doing:
+//
+//      key := argon2.Key([]byte("some password"), salt, 3, 32*1024, 4, 32)
+//
+// The draft RFC recommends[2] time=3, and memory=32*1024 is a sensible number.
+// If using that amount of memory (32 MB) is not possible in some contexts then
+// the time parameter can be increased to compensate.
+//
+// The time parameter specifies the number of passes over the memory and the
+// memory parameter specifies the size of the memory in KiB. For example
+// memory=32*1024 sets the memory cost to ~32 MB. The number of threads can be
+// adjusted to the number of available CPUs. The cost parameters should be
+// increased as memory latency and CPU parallelism increases. Remember to get a
+// good random salt.
+func Key(password, salt []byte, time, memory uint32, threads uint8, keyLen uint32) []byte {
+	return deriveKey(argon2i, password, salt, nil, nil, time, memory, threads, keyLen)
+}
+
+// IDKey derives a key from the password, salt, and cost parameters using
+// Argon2id returning a byte slice of length keyLen that can be used as
+// cryptographic key. The CPU cost and parallelism degree must be greater than
+// zero.
+//
+// For example, you can get a derived key for e.g. AES-256 (which needs a
+// 32-byte key) by doing:
+//
+//      key := argon2.IDKey([]byte("some password"), salt, 1, 64*1024, 4, 32)
+//
+// The draft RFC recommends[2] time=1, and memory=64*1024 is a sensible number.
+// If using that amount of memory (64 MB) is not possible in some contexts then
+// the time parameter can be increased to compensate.
+//
+// The time parameter specifies the number of passes over the memory and the
+// memory parameter specifies the size of the memory in KiB. For example
+// memory=64*1024 sets the memory cost to ~64 MB. The number of threads can be
+// adjusted to the numbers of available CPUs. The cost parameters should be
+// increased as memory latency and CPU parallelism increases. Remember to get a
+// good random salt.
+func IDKey(password, salt []byte, time, memory uint32, threads uint8, keyLen uint32) []byte {
+	return deriveKey(argon2id, password, salt, nil, nil, time, memory, threads, keyLen)
+}
+
+func deriveKey(mode int, password, salt, secret, data []byte, time, memory uint32, threads uint8, keyLen uint32) []byte {
+	if time < 1 {
+		panic("argon2: number of rounds too small")
+	}
+	if threads < 1 {
+		panic("argon2: parallelism degree too low")
+	}
+	h0 := initHash(password, salt, secret, data, time, memory, uint32(threads), keyLen, mode)
+
+	memory = memory / (syncPoints * uint32(threads)) * (syncPoints * uint32(threads))
+	if memory < 2*syncPoints*uint32(threads) {
+		memory = 2 * syncPoints * uint32(threads)
+	}
+	B := initBlocks(&h0, memory, uint32(threads))
+	processBlocks(B, time, memory, uint32(threads), mode)
+	return extractKey(B, memory, uint32(threads), keyLen)
+}
+
+const (
+	blockLength = 128
+	syncPoints  = 4
+)
+
+type block [blockLength]uint64
+
+func initHash(password, salt, key, data []byte, time, memory, threads, keyLen uint32, mode int) [blake2b.Size + 8]byte {
+	var (
+		h0     [blake2b.Size + 8]byte
+		params [24]byte
+		tmp    [4]byte
+	)
+
+	b2, _ := blake2b.New512(nil)
+	binary.LittleEndian.PutUint32(params[0:4], threads)
+	binary.LittleEndian.PutUint32(params[4:8], keyLen)
+	binary.LittleEndian.PutUint32(params[8:12], memory)
+	binary.LittleEndian.PutUint32(params[12:16], time)
+	binary.LittleEndian.PutUint32(params[16:20], uint32(Version))
+	binary.LittleEndian.PutUint32(params[20:24], uint32(mode))
+	b2.Write(params[:])
+	binary.LittleEndian.PutUint32(tmp[:], uint32(len(password)))
+	b2.Write(tmp[:])
+	b2.Write(password)
+	binary.LittleEndian.PutUint32(tmp[:], uint32(len(salt)))
+	b2.Write(tmp[:])
+	b2.Write(salt)
+	binary.LittleEndian.PutUint32(tmp[:], uint32(len(key)))
+	b2.Write(tmp[:])
+	b2.Write(key)
+	binary.LittleEndian.PutUint32(tmp[:], uint32(len(data)))
+	b2.Write(tmp[:])
+	b2.Write(data)
+	b2.Sum(h0[:0])
+	return h0
+}
+
+func initBlocks(h0 *[blake2b.Size + 8]byte, memory, threads uint32) []block {
+	var block0 [1024]byte
+	B := make([]block, memory)
+	for lane := uint32(0); lane < threads; lane++ {
+		j := lane * (memory / threads)
+		binary.LittleEndian.PutUint32(h0[blake2b.Size+4:], lane)
+
+		binary.LittleEndian.PutUint32(h0[blake2b.Size:], 0)
+		blake2bHash(block0[:], h0[:])
+		for i := range B[j+0] {
+			B[j+0][i] = binary.LittleEndian.Uint64(block0[i*8:])
+		}
+
+		binary.LittleEndian.PutUint32(h0[blake2b.Size:], 1)
+		blake2bHash(block0[:], h0[:])
+		for i := range B[j+1] {
+			B[j+1][i] = binary.LittleEndian.Uint64(block0[i*8:])
+		}
+	}
+	return B
+}
+
+func processBlocks(B []block, time, memory, threads uint32, mode int) {
+	lanes := memory / threads
+	segments := lanes / syncPoints
+
+	processSegment := func(n, slice, lane uint32, wg *sync.WaitGroup) {
+		var addresses, in, zero block
+		if mode == argon2i || (mode == argon2id && n == 0 && slice < syncPoints/2) {
+			in[0] = uint64(n)
+			in[1] = uint64(lane)
+			in[2] = uint64(slice)
+			in[3] = uint64(memory)
+			in[4] = uint64(time)
+			in[5] = uint64(mode)
+		}
+
+		index := uint32(0)
+		if n == 0 && slice == 0 {
+			index = 2 // we have already generated the first two blocks
+			if mode == argon2i || mode == argon2id {
+				in[6]++
+				processBlock(&addresses, &in, &zero)
+				processBlock(&addresses, &addresses, &zero)
+			}
+		}
+
+		offset := lane*lanes + slice*segments + index
+		var random uint64
+		for index < segments {
+			prev := offset - 1
+			if index == 0 && slice == 0 {
+				prev += lanes // last block in lane
+			}
+			if mode == argon2i || (mode == argon2id && n == 0 && slice < syncPoints/2) {
+				if index%blockLength == 0 {
+					in[6]++
+					processBlock(&addresses, &in, &zero)
+					processBlock(&addresses, &addresses, &zero)
+				}
+				random = addresses[index%blockLength]
+			} else {
+				random = B[prev][0]
+			}
+			newOffset := indexAlpha(random, lanes, segments, threads, n, slice, lane, index)
+			processBlockXOR(&B[offset], &B[prev], &B[newOffset])
+			index, offset = index+1, offset+1
+		}
+		wg.Done()
+	}
+
+	for n := uint32(0); n < time; n++ {
+		for slice := uint32(0); slice < syncPoints; slice++ {
+			var wg sync.WaitGroup
+			for lane := uint32(0); lane < threads; lane++ {
+				wg.Add(1)
+				go processSegment(n, slice, lane, &wg)
+			}
+			wg.Wait()
+		}
+	}
+
+}
+
+func extractKey(B []block, memory, threads, keyLen uint32) []byte {
+	lanes := memory / threads
+	for lane := uint32(0); lane < threads-1; lane++ {
+		for i, v := range B[(lane*lanes)+lanes-1] {
+			B[memory-1][i] ^= v
+		}
+	}
+
+	var block [1024]byte
+	for i, v := range B[memory-1] {
+		binary.LittleEndian.PutUint64(block[i*8:], v)
+	}
+	key := make([]byte, keyLen)
+	blake2bHash(key, block[:])
+	return key
+}
+
+func indexAlpha(rand uint64, lanes, segments, threads, n, slice, lane, index uint32) uint32 {
+	refLane := uint32(rand>>32) % threads
+	if n == 0 && slice == 0 {
+		refLane = lane
+	}
+	m, s := 3*segments, ((slice+1)%syncPoints)*segments
+	if lane == refLane {
+		m += index
+	}
+	if n == 0 {
+		m, s = slice*segments, 0
+		if slice == 0 || lane == refLane {
+			m += index
+		}
+	}
+	if index == 0 || lane == refLane {
+		m--
+	}
+	return phi(rand, uint64(m), uint64(s), refLane, lanes)
+}
+
+func phi(rand, m, s uint64, lane, lanes uint32) uint32 {
+	p := rand & 0xFFFFFFFF
+	p = (p * p) >> 32
+	p = (p * m) >> 32
+	return lane*lanes + uint32((s+m-(p+1))%uint64(lanes))
+}

+ 233 - 0
psiphon/common/crypto/argon2/argon2_test.go

@@ -0,0 +1,233 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package argon2
+
+import (
+	"bytes"
+	"encoding/hex"
+	"testing"
+)
+
+var (
+	genKatPassword = []byte{
+		0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+		0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+		0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+		0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+	}
+	genKatSalt   = []byte{0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02}
+	genKatSecret = []byte{0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03}
+	genKatAAD    = []byte{0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04}
+)
+
+func TestArgon2(t *testing.T) {
+	defer func(sse4 bool) { useSSE4 = sse4 }(useSSE4)
+
+	if useSSE4 {
+		t.Log("SSE4.1 version")
+		testArgon2i(t)
+		testArgon2d(t)
+		testArgon2id(t)
+		useSSE4 = false
+	}
+	t.Log("generic version")
+	testArgon2i(t)
+	testArgon2d(t)
+	testArgon2id(t)
+}
+
+func testArgon2d(t *testing.T) {
+	want := []byte{
+		0x51, 0x2b, 0x39, 0x1b, 0x6f, 0x11, 0x62, 0x97,
+		0x53, 0x71, 0xd3, 0x09, 0x19, 0x73, 0x42, 0x94,
+		0xf8, 0x68, 0xe3, 0xbe, 0x39, 0x84, 0xf3, 0xc1,
+		0xa1, 0x3a, 0x4d, 0xb9, 0xfa, 0xbe, 0x4a, 0xcb,
+	}
+	hash := deriveKey(argon2d, genKatPassword, genKatSalt, genKatSecret, genKatAAD, 3, 32, 4, 32)
+	if !bytes.Equal(hash, want) {
+		t.Errorf("derived key does not match - got: %s , want: %s", hex.EncodeToString(hash), hex.EncodeToString(want))
+	}
+}
+
+func testArgon2i(t *testing.T) {
+	want := []byte{
+		0xc8, 0x14, 0xd9, 0xd1, 0xdc, 0x7f, 0x37, 0xaa,
+		0x13, 0xf0, 0xd7, 0x7f, 0x24, 0x94, 0xbd, 0xa1,
+		0xc8, 0xde, 0x6b, 0x01, 0x6d, 0xd3, 0x88, 0xd2,
+		0x99, 0x52, 0xa4, 0xc4, 0x67, 0x2b, 0x6c, 0xe8,
+	}
+	hash := deriveKey(argon2i, genKatPassword, genKatSalt, genKatSecret, genKatAAD, 3, 32, 4, 32)
+	if !bytes.Equal(hash, want) {
+		t.Errorf("derived key does not match - got: %s , want: %s", hex.EncodeToString(hash), hex.EncodeToString(want))
+	}
+}
+
+func testArgon2id(t *testing.T) {
+	want := []byte{
+		0x0d, 0x64, 0x0d, 0xf5, 0x8d, 0x78, 0x76, 0x6c,
+		0x08, 0xc0, 0x37, 0xa3, 0x4a, 0x8b, 0x53, 0xc9,
+		0xd0, 0x1e, 0xf0, 0x45, 0x2d, 0x75, 0xb6, 0x5e,
+		0xb5, 0x25, 0x20, 0xe9, 0x6b, 0x01, 0xe6, 0x59,
+	}
+	hash := deriveKey(argon2id, genKatPassword, genKatSalt, genKatSecret, genKatAAD, 3, 32, 4, 32)
+	if !bytes.Equal(hash, want) {
+		t.Errorf("derived key does not match - got: %s , want: %s", hex.EncodeToString(hash), hex.EncodeToString(want))
+	}
+}
+
+func TestVectors(t *testing.T) {
+	password, salt := []byte("password"), []byte("somesalt")
+	for i, v := range testVectors {
+		want, err := hex.DecodeString(v.hash)
+		if err != nil {
+			t.Fatalf("Test %d: failed to decode hash: %v", i, err)
+		}
+		hash := deriveKey(v.mode, password, salt, nil, nil, v.time, v.memory, v.threads, uint32(len(want)))
+		if !bytes.Equal(hash, want) {
+			t.Errorf("Test %d - got: %s want: %s", i, hex.EncodeToString(hash), hex.EncodeToString(want))
+		}
+	}
+}
+
+func benchmarkArgon2(mode int, time, memory uint32, threads uint8, keyLen uint32, b *testing.B) {
+	password := []byte("password")
+	salt := []byte("choosing random salts is hard")
+	b.ReportAllocs()
+	for i := 0; i < b.N; i++ {
+		deriveKey(mode, password, salt, nil, nil, time, memory, threads, keyLen)
+	}
+}
+
+func BenchmarkArgon2i(b *testing.B) {
+	b.Run(" Time: 3 Memory: 32 MB, Threads: 1", func(b *testing.B) { benchmarkArgon2(argon2i, 3, 32*1024, 1, 32, b) })
+	b.Run(" Time: 4 Memory: 32 MB, Threads: 1", func(b *testing.B) { benchmarkArgon2(argon2i, 4, 32*1024, 1, 32, b) })
+	b.Run(" Time: 5 Memory: 32 MB, Threads: 1", func(b *testing.B) { benchmarkArgon2(argon2i, 5, 32*1024, 1, 32, b) })
+	b.Run(" Time: 3 Memory: 64 MB, Threads: 4", func(b *testing.B) { benchmarkArgon2(argon2i, 3, 64*1024, 4, 32, b) })
+	b.Run(" Time: 4 Memory: 64 MB, Threads: 4", func(b *testing.B) { benchmarkArgon2(argon2i, 4, 64*1024, 4, 32, b) })
+	b.Run(" Time: 5 Memory: 64 MB, Threads: 4", func(b *testing.B) { benchmarkArgon2(argon2i, 5, 64*1024, 4, 32, b) })
+}
+
+func BenchmarkArgon2d(b *testing.B) {
+	b.Run(" Time: 3, Memory: 32 MB, Threads: 1", func(b *testing.B) { benchmarkArgon2(argon2d, 3, 32*1024, 1, 32, b) })
+	b.Run(" Time: 4, Memory: 32 MB, Threads: 1", func(b *testing.B) { benchmarkArgon2(argon2d, 4, 32*1024, 1, 32, b) })
+	b.Run(" Time: 5, Memory: 32 MB, Threads: 1", func(b *testing.B) { benchmarkArgon2(argon2d, 5, 32*1024, 1, 32, b) })
+	b.Run(" Time: 3, Memory: 64 MB, Threads: 4", func(b *testing.B) { benchmarkArgon2(argon2d, 3, 64*1024, 4, 32, b) })
+	b.Run(" Time: 4, Memory: 64 MB, Threads: 4", func(b *testing.B) { benchmarkArgon2(argon2d, 4, 64*1024, 4, 32, b) })
+	b.Run(" Time: 5, Memory: 64 MB, Threads: 4", func(b *testing.B) { benchmarkArgon2(argon2d, 5, 64*1024, 4, 32, b) })
+}
+
+func BenchmarkArgon2id(b *testing.B) {
+	b.Run(" Time: 3, Memory: 32 MB, Threads: 1", func(b *testing.B) { benchmarkArgon2(argon2id, 3, 32*1024, 1, 32, b) })
+	b.Run(" Time: 4, Memory: 32 MB, Threads: 1", func(b *testing.B) { benchmarkArgon2(argon2id, 4, 32*1024, 1, 32, b) })
+	b.Run(" Time: 5, Memory: 32 MB, Threads: 1", func(b *testing.B) { benchmarkArgon2(argon2id, 5, 32*1024, 1, 32, b) })
+	b.Run(" Time: 3, Memory: 64 MB, Threads: 4", func(b *testing.B) { benchmarkArgon2(argon2id, 3, 64*1024, 4, 32, b) })
+	b.Run(" Time: 4, Memory: 64 MB, Threads: 4", func(b *testing.B) { benchmarkArgon2(argon2id, 4, 64*1024, 4, 32, b) })
+	b.Run(" Time: 5, Memory: 64 MB, Threads: 4", func(b *testing.B) { benchmarkArgon2(argon2id, 5, 64*1024, 4, 32, b) })
+}
+
+// Generated with the CLI of https://github.com/P-H-C/phc-winner-argon2/blob/master/argon2-specs.pdf
+var testVectors = []struct {
+	mode         int
+	time, memory uint32
+	threads      uint8
+	hash         string
+}{
+	{
+		mode: argon2i, time: 1, memory: 64, threads: 1,
+		hash: "b9c401d1844a67d50eae3967dc28870b22e508092e861a37",
+	},
+	{
+		mode: argon2d, time: 1, memory: 64, threads: 1,
+		hash: "8727405fd07c32c78d64f547f24150d3f2e703a89f981a19",
+	},
+	{
+		mode: argon2id, time: 1, memory: 64, threads: 1,
+		hash: "655ad15eac652dc59f7170a7332bf49b8469be1fdb9c28bb",
+	},
+	{
+		mode: argon2i, time: 2, memory: 64, threads: 1,
+		hash: "8cf3d8f76a6617afe35fac48eb0b7433a9a670ca4a07ed64",
+	},
+	{
+		mode: argon2d, time: 2, memory: 64, threads: 1,
+		hash: "3be9ec79a69b75d3752acb59a1fbb8b295a46529c48fbb75",
+	},
+	{
+		mode: argon2id, time: 2, memory: 64, threads: 1,
+		hash: "068d62b26455936aa6ebe60060b0a65870dbfa3ddf8d41f7",
+	},
+	{
+		mode: argon2i, time: 2, memory: 64, threads: 2,
+		hash: "2089f3e78a799720f80af806553128f29b132cafe40d059f",
+	},
+	{
+		mode: argon2d, time: 2, memory: 64, threads: 2,
+		hash: "68e2462c98b8bc6bb60ec68db418ae2c9ed24fc6748a40e9",
+	},
+	{
+		mode: argon2id, time: 2, memory: 64, threads: 2,
+		hash: "350ac37222f436ccb5c0972f1ebd3bf6b958bf2071841362",
+	},
+	{
+		mode: argon2i, time: 3, memory: 256, threads: 2,
+		hash: "f5bbf5d4c3836af13193053155b73ec7476a6a2eb93fd5e6",
+	},
+	{
+		mode: argon2d, time: 3, memory: 256, threads: 2,
+		hash: "f4f0669218eaf3641f39cc97efb915721102f4b128211ef2",
+	},
+	{
+		mode: argon2id, time: 3, memory: 256, threads: 2,
+		hash: "4668d30ac4187e6878eedeacf0fd83c5a0a30db2cc16ef0b",
+	},
+	{
+		mode: argon2i, time: 4, memory: 4096, threads: 4,
+		hash: "a11f7b7f3f93f02ad4bddb59ab62d121e278369288a0d0e7",
+	},
+	{
+		mode: argon2d, time: 4, memory: 4096, threads: 4,
+		hash: "935598181aa8dc2b720914aa6435ac8d3e3a4210c5b0fb2d",
+	},
+	{
+		mode: argon2id, time: 4, memory: 4096, threads: 4,
+		hash: "145db9733a9f4ee43edf33c509be96b934d505a4efb33c5a",
+	},
+	{
+		mode: argon2i, time: 4, memory: 1024, threads: 8,
+		hash: "0cdd3956aa35e6b475a7b0c63488822f774f15b43f6e6e17",
+	},
+	{
+		mode: argon2d, time: 4, memory: 1024, threads: 8,
+		hash: "83604fc2ad0589b9d055578f4d3cc55bc616df3578a896e9",
+	},
+	{
+		mode: argon2id, time: 4, memory: 1024, threads: 8,
+		hash: "8dafa8e004f8ea96bf7c0f93eecf67a6047476143d15577f",
+	},
+	{
+		mode: argon2i, time: 2, memory: 64, threads: 3,
+		hash: "5cab452fe6b8479c8661def8cd703b611a3905a6d5477fe6",
+	},
+	{
+		mode: argon2d, time: 2, memory: 64, threads: 3,
+		hash: "22474a423bda2ccd36ec9afd5119e5c8949798cadf659f51",
+	},
+	{
+		mode: argon2id, time: 2, memory: 64, threads: 3,
+		hash: "4a15b31aec7c2590b87d1f520be7d96f56658172deaa3079",
+	},
+	{
+		mode: argon2i, time: 3, memory: 1024, threads: 6,
+		hash: "d236b29c2b2a09babee842b0dec6aa1e83ccbdea8023dced",
+	},
+	{
+		mode: argon2d, time: 3, memory: 1024, threads: 6,
+		hash: "a3351b0319a53229152023d9206902f4ef59661cdca89481",
+	},
+	{
+		mode: argon2id, time: 3, memory: 1024, threads: 6,
+		hash: "1640b932f4b60e272f5d2207b9a9c626ffa1bd88d2349016",
+	},
+}

+ 53 - 0
psiphon/common/crypto/argon2/blake2b.go

@@ -0,0 +1,53 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package argon2
+
+import (
+	"encoding/binary"
+	"hash"
+
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/blake2b"
+)
+
+// blake2bHash computes an arbitrary long hash value of in
+// and writes the hash to out.
+func blake2bHash(out []byte, in []byte) {
+	var b2 hash.Hash
+	if n := len(out); n < blake2b.Size {
+		b2, _ = blake2b.New(n, nil)
+	} else {
+		b2, _ = blake2b.New512(nil)
+	}
+
+	var buffer [blake2b.Size]byte
+	binary.LittleEndian.PutUint32(buffer[:4], uint32(len(out)))
+	b2.Write(buffer[:4])
+	b2.Write(in)
+
+	if len(out) <= blake2b.Size {
+		b2.Sum(out[:0])
+		return
+	}
+
+	outLen := len(out)
+	b2.Sum(buffer[:0])
+	b2.Reset()
+	copy(out, buffer[:32])
+	out = out[32:]
+	for len(out) > blake2b.Size {
+		b2.Write(buffer[:])
+		b2.Sum(buffer[:0])
+		copy(out, buffer[:32])
+		out = out[32:]
+		b2.Reset()
+	}
+
+	if outLen%blake2b.Size > 0 { // outLen > 64
+		r := ((outLen + 31) / 32) - 2 // ⌈τ /32⌉-2
+		b2, _ = blake2b.New(outLen-32*r, nil)
+	}
+	b2.Write(buffer[:])
+	b2.Sum(out[:0])
+}

+ 60 - 0
psiphon/common/crypto/argon2/blamka_amd64.go

@@ -0,0 +1,60 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build amd64,!gccgo,!appengine
+
+package argon2
+
+import "golang.org/x/sys/cpu"
+
+func init() {
+	useSSE4 = cpu.X86.HasSSE41
+}
+
+//go:noescape
+func mixBlocksSSE2(out, a, b, c *block)
+
+//go:noescape
+func xorBlocksSSE2(out, a, b, c *block)
+
+//go:noescape
+func blamkaSSE4(b *block)
+
+func processBlockSSE(out, in1, in2 *block, xor bool) {
+	var t block
+	mixBlocksSSE2(&t, in1, in2, &t)
+	if useSSE4 {
+		blamkaSSE4(&t)
+	} else {
+		for i := 0; i < blockLength; i += 16 {
+			blamkaGeneric(
+				&t[i+0], &t[i+1], &t[i+2], &t[i+3],
+				&t[i+4], &t[i+5], &t[i+6], &t[i+7],
+				&t[i+8], &t[i+9], &t[i+10], &t[i+11],
+				&t[i+12], &t[i+13], &t[i+14], &t[i+15],
+			)
+		}
+		for i := 0; i < blockLength/8; i += 2 {
+			blamkaGeneric(
+				&t[i], &t[i+1], &t[16+i], &t[16+i+1],
+				&t[32+i], &t[32+i+1], &t[48+i], &t[48+i+1],
+				&t[64+i], &t[64+i+1], &t[80+i], &t[80+i+1],
+				&t[96+i], &t[96+i+1], &t[112+i], &t[112+i+1],
+			)
+		}
+	}
+	if xor {
+		xorBlocksSSE2(out, in1, in2, &t)
+	} else {
+		mixBlocksSSE2(out, in1, in2, &t)
+	}
+}
+
+func processBlock(out, in1, in2 *block) {
+	processBlockSSE(out, in1, in2, false)
+}
+
+func processBlockXOR(out, in1, in2 *block) {
+	processBlockSSE(out, in1, in2, true)
+}

+ 243 - 0
psiphon/common/crypto/argon2/blamka_amd64.s

@@ -0,0 +1,243 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build amd64,!gccgo,!appengine
+
+#include "textflag.h"
+
+DATA ·c40<>+0x00(SB)/8, $0x0201000706050403
+DATA ·c40<>+0x08(SB)/8, $0x0a09080f0e0d0c0b
+GLOBL ·c40<>(SB), (NOPTR+RODATA), $16
+
+DATA ·c48<>+0x00(SB)/8, $0x0100070605040302
+DATA ·c48<>+0x08(SB)/8, $0x09080f0e0d0c0b0a
+GLOBL ·c48<>(SB), (NOPTR+RODATA), $16
+
+#define SHUFFLE(v2, v3, v4, v5, v6, v7, t1, t2) \
+	MOVO       v4, t1; \
+	MOVO       v5, v4; \
+	MOVO       t1, v5; \
+	MOVO       v6, t1; \
+	PUNPCKLQDQ v6, t2; \
+	PUNPCKHQDQ v7, v6; \
+	PUNPCKHQDQ t2, v6; \
+	PUNPCKLQDQ v7, t2; \
+	MOVO       t1, v7; \
+	MOVO       v2, t1; \
+	PUNPCKHQDQ t2, v7; \
+	PUNPCKLQDQ v3, t2; \
+	PUNPCKHQDQ t2, v2; \
+	PUNPCKLQDQ t1, t2; \
+	PUNPCKHQDQ t2, v3
+
+#define SHUFFLE_INV(v2, v3, v4, v5, v6, v7, t1, t2) \
+	MOVO       v4, t1; \
+	MOVO       v5, v4; \
+	MOVO       t1, v5; \
+	MOVO       v2, t1; \
+	PUNPCKLQDQ v2, t2; \
+	PUNPCKHQDQ v3, v2; \
+	PUNPCKHQDQ t2, v2; \
+	PUNPCKLQDQ v3, t2; \
+	MOVO       t1, v3; \
+	MOVO       v6, t1; \
+	PUNPCKHQDQ t2, v3; \
+	PUNPCKLQDQ v7, t2; \
+	PUNPCKHQDQ t2, v6; \
+	PUNPCKLQDQ t1, t2; \
+	PUNPCKHQDQ t2, v7
+
+#define HALF_ROUND(v0, v1, v2, v3, v4, v5, v6, v7, t0, c40, c48) \
+	MOVO    v0, t0;        \
+	PMULULQ v2, t0;        \
+	PADDQ   v2, v0;        \
+	PADDQ   t0, v0;        \
+	PADDQ   t0, v0;        \
+	PXOR    v0, v6;        \
+	PSHUFD  $0xB1, v6, v6; \
+	MOVO    v4, t0;        \
+	PMULULQ v6, t0;        \
+	PADDQ   v6, v4;        \
+	PADDQ   t0, v4;        \
+	PADDQ   t0, v4;        \
+	PXOR    v4, v2;        \
+	PSHUFB  c40, v2;       \
+	MOVO    v0, t0;        \
+	PMULULQ v2, t0;        \
+	PADDQ   v2, v0;        \
+	PADDQ   t0, v0;        \
+	PADDQ   t0, v0;        \
+	PXOR    v0, v6;        \
+	PSHUFB  c48, v6;       \
+	MOVO    v4, t0;        \
+	PMULULQ v6, t0;        \
+	PADDQ   v6, v4;        \
+	PADDQ   t0, v4;        \
+	PADDQ   t0, v4;        \
+	PXOR    v4, v2;        \
+	MOVO    v2, t0;        \
+	PADDQ   v2, t0;        \
+	PSRLQ   $63, v2;       \
+	PXOR    t0, v2;        \
+	MOVO    v1, t0;        \
+	PMULULQ v3, t0;        \
+	PADDQ   v3, v1;        \
+	PADDQ   t0, v1;        \
+	PADDQ   t0, v1;        \
+	PXOR    v1, v7;        \
+	PSHUFD  $0xB1, v7, v7; \
+	MOVO    v5, t0;        \
+	PMULULQ v7, t0;        \
+	PADDQ   v7, v5;        \
+	PADDQ   t0, v5;        \
+	PADDQ   t0, v5;        \
+	PXOR    v5, v3;        \
+	PSHUFB  c40, v3;       \
+	MOVO    v1, t0;        \
+	PMULULQ v3, t0;        \
+	PADDQ   v3, v1;        \
+	PADDQ   t0, v1;        \
+	PADDQ   t0, v1;        \
+	PXOR    v1, v7;        \
+	PSHUFB  c48, v7;       \
+	MOVO    v5, t0;        \
+	PMULULQ v7, t0;        \
+	PADDQ   v7, v5;        \
+	PADDQ   t0, v5;        \
+	PADDQ   t0, v5;        \
+	PXOR    v5, v3;        \
+	MOVO    v3, t0;        \
+	PADDQ   v3, t0;        \
+	PSRLQ   $63, v3;       \
+	PXOR    t0, v3
+
+#define LOAD_MSG_0(block, off) \
+	MOVOU 8*(off+0)(block), X0;  \
+	MOVOU 8*(off+2)(block), X1;  \
+	MOVOU 8*(off+4)(block), X2;  \
+	MOVOU 8*(off+6)(block), X3;  \
+	MOVOU 8*(off+8)(block), X4;  \
+	MOVOU 8*(off+10)(block), X5; \
+	MOVOU 8*(off+12)(block), X6; \
+	MOVOU 8*(off+14)(block), X7
+
+#define STORE_MSG_0(block, off) \
+	MOVOU X0, 8*(off+0)(block);  \
+	MOVOU X1, 8*(off+2)(block);  \
+	MOVOU X2, 8*(off+4)(block);  \
+	MOVOU X3, 8*(off+6)(block);  \
+	MOVOU X4, 8*(off+8)(block);  \
+	MOVOU X5, 8*(off+10)(block); \
+	MOVOU X6, 8*(off+12)(block); \
+	MOVOU X7, 8*(off+14)(block)
+
+#define LOAD_MSG_1(block, off) \
+	MOVOU 8*off+0*8(block), X0;  \
+	MOVOU 8*off+16*8(block), X1; \
+	MOVOU 8*off+32*8(block), X2; \
+	MOVOU 8*off+48*8(block), X3; \
+	MOVOU 8*off+64*8(block), X4; \
+	MOVOU 8*off+80*8(block), X5; \
+	MOVOU 8*off+96*8(block), X6; \
+	MOVOU 8*off+112*8(block), X7
+
+#define STORE_MSG_1(block, off) \
+	MOVOU X0, 8*off+0*8(block);  \
+	MOVOU X1, 8*off+16*8(block); \
+	MOVOU X2, 8*off+32*8(block); \
+	MOVOU X3, 8*off+48*8(block); \
+	MOVOU X4, 8*off+64*8(block); \
+	MOVOU X5, 8*off+80*8(block); \
+	MOVOU X6, 8*off+96*8(block); \
+	MOVOU X7, 8*off+112*8(block)
+
+#define BLAMKA_ROUND_0(block, off, t0, t1, c40, c48) \
+	LOAD_MSG_0(block, off);                                   \
+	HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, t0, c40, c48); \
+	SHUFFLE(X2, X3, X4, X5, X6, X7, t0, t1);                  \
+	HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, t0, c40, c48); \
+	SHUFFLE_INV(X2, X3, X4, X5, X6, X7, t0, t1);              \
+	STORE_MSG_0(block, off)
+
+#define BLAMKA_ROUND_1(block, off, t0, t1, c40, c48) \
+	LOAD_MSG_1(block, off);                                   \
+	HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, t0, c40, c48); \
+	SHUFFLE(X2, X3, X4, X5, X6, X7, t0, t1);                  \
+	HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, t0, c40, c48); \
+	SHUFFLE_INV(X2, X3, X4, X5, X6, X7, t0, t1);              \
+	STORE_MSG_1(block, off)
+
+// func blamkaSSE4(b *block)
+TEXT ·blamkaSSE4(SB), 4, $0-8
+	MOVQ b+0(FP), AX
+
+	MOVOU ·c40<>(SB), X10
+	MOVOU ·c48<>(SB), X11
+
+	BLAMKA_ROUND_0(AX, 0, X8, X9, X10, X11)
+	BLAMKA_ROUND_0(AX, 16, X8, X9, X10, X11)
+	BLAMKA_ROUND_0(AX, 32, X8, X9, X10, X11)
+	BLAMKA_ROUND_0(AX, 48, X8, X9, X10, X11)
+	BLAMKA_ROUND_0(AX, 64, X8, X9, X10, X11)
+	BLAMKA_ROUND_0(AX, 80, X8, X9, X10, X11)
+	BLAMKA_ROUND_0(AX, 96, X8, X9, X10, X11)
+	BLAMKA_ROUND_0(AX, 112, X8, X9, X10, X11)
+
+	BLAMKA_ROUND_1(AX, 0, X8, X9, X10, X11)
+	BLAMKA_ROUND_1(AX, 2, X8, X9, X10, X11)
+	BLAMKA_ROUND_1(AX, 4, X8, X9, X10, X11)
+	BLAMKA_ROUND_1(AX, 6, X8, X9, X10, X11)
+	BLAMKA_ROUND_1(AX, 8, X8, X9, X10, X11)
+	BLAMKA_ROUND_1(AX, 10, X8, X9, X10, X11)
+	BLAMKA_ROUND_1(AX, 12, X8, X9, X10, X11)
+	BLAMKA_ROUND_1(AX, 14, X8, X9, X10, X11)
+	RET
+
+// func mixBlocksSSE2(out, a, b, c *block)
+TEXT ·mixBlocksSSE2(SB), 4, $0-32
+	MOVQ out+0(FP), DX
+	MOVQ a+8(FP), AX
+	MOVQ b+16(FP), BX
+	MOVQ a+24(FP), CX
+	MOVQ $128, BP
+
+loop:
+	MOVOU 0(AX), X0
+	MOVOU 0(BX), X1
+	MOVOU 0(CX), X2
+	PXOR  X1, X0
+	PXOR  X2, X0
+	MOVOU X0, 0(DX)
+	ADDQ  $16, AX
+	ADDQ  $16, BX
+	ADDQ  $16, CX
+	ADDQ  $16, DX
+	SUBQ  $2, BP
+	JA    loop
+	RET
+
+// func xorBlocksSSE2(out, a, b, c *block)
+TEXT ·xorBlocksSSE2(SB), 4, $0-32
+	MOVQ out+0(FP), DX
+	MOVQ a+8(FP), AX
+	MOVQ b+16(FP), BX
+	MOVQ a+24(FP), CX
+	MOVQ $128, BP
+
+loop:
+	MOVOU 0(AX), X0
+	MOVOU 0(BX), X1
+	MOVOU 0(CX), X2
+	MOVOU 0(DX), X3
+	PXOR  X1, X0
+	PXOR  X2, X0
+	PXOR  X3, X0
+	MOVOU X0, 0(DX)
+	ADDQ  $16, AX
+	ADDQ  $16, BX
+	ADDQ  $16, CX
+	ADDQ  $16, DX
+	SUBQ  $2, BP
+	JA    loop
+	RET

+ 163 - 0
psiphon/common/crypto/argon2/blamka_generic.go

@@ -0,0 +1,163 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package argon2
+
+var useSSE4 bool
+
+func processBlockGeneric(out, in1, in2 *block, xor bool) {
+	var t block
+	for i := range t {
+		t[i] = in1[i] ^ in2[i]
+	}
+	for i := 0; i < blockLength; i += 16 {
+		blamkaGeneric(
+			&t[i+0], &t[i+1], &t[i+2], &t[i+3],
+			&t[i+4], &t[i+5], &t[i+6], &t[i+7],
+			&t[i+8], &t[i+9], &t[i+10], &t[i+11],
+			&t[i+12], &t[i+13], &t[i+14], &t[i+15],
+		)
+	}
+	for i := 0; i < blockLength/8; i += 2 {
+		blamkaGeneric(
+			&t[i], &t[i+1], &t[16+i], &t[16+i+1],
+			&t[32+i], &t[32+i+1], &t[48+i], &t[48+i+1],
+			&t[64+i], &t[64+i+1], &t[80+i], &t[80+i+1],
+			&t[96+i], &t[96+i+1], &t[112+i], &t[112+i+1],
+		)
+	}
+	if xor {
+		for i := range t {
+			out[i] ^= in1[i] ^ in2[i] ^ t[i]
+		}
+	} else {
+		for i := range t {
+			out[i] = in1[i] ^ in2[i] ^ t[i]
+		}
+	}
+}
+
+func blamkaGeneric(t00, t01, t02, t03, t04, t05, t06, t07, t08, t09, t10, t11, t12, t13, t14, t15 *uint64) {
+	v00, v01, v02, v03 := *t00, *t01, *t02, *t03
+	v04, v05, v06, v07 := *t04, *t05, *t06, *t07
+	v08, v09, v10, v11 := *t08, *t09, *t10, *t11
+	v12, v13, v14, v15 := *t12, *t13, *t14, *t15
+
+	v00 += v04 + 2*uint64(uint32(v00))*uint64(uint32(v04))
+	v12 ^= v00
+	v12 = v12>>32 | v12<<32
+	v08 += v12 + 2*uint64(uint32(v08))*uint64(uint32(v12))
+	v04 ^= v08
+	v04 = v04>>24 | v04<<40
+
+	v00 += v04 + 2*uint64(uint32(v00))*uint64(uint32(v04))
+	v12 ^= v00
+	v12 = v12>>16 | v12<<48
+	v08 += v12 + 2*uint64(uint32(v08))*uint64(uint32(v12))
+	v04 ^= v08
+	v04 = v04>>63 | v04<<1
+
+	v01 += v05 + 2*uint64(uint32(v01))*uint64(uint32(v05))
+	v13 ^= v01
+	v13 = v13>>32 | v13<<32
+	v09 += v13 + 2*uint64(uint32(v09))*uint64(uint32(v13))
+	v05 ^= v09
+	v05 = v05>>24 | v05<<40
+
+	v01 += v05 + 2*uint64(uint32(v01))*uint64(uint32(v05))
+	v13 ^= v01
+	v13 = v13>>16 | v13<<48
+	v09 += v13 + 2*uint64(uint32(v09))*uint64(uint32(v13))
+	v05 ^= v09
+	v05 = v05>>63 | v05<<1
+
+	v02 += v06 + 2*uint64(uint32(v02))*uint64(uint32(v06))
+	v14 ^= v02
+	v14 = v14>>32 | v14<<32
+	v10 += v14 + 2*uint64(uint32(v10))*uint64(uint32(v14))
+	v06 ^= v10
+	v06 = v06>>24 | v06<<40
+
+	v02 += v06 + 2*uint64(uint32(v02))*uint64(uint32(v06))
+	v14 ^= v02
+	v14 = v14>>16 | v14<<48
+	v10 += v14 + 2*uint64(uint32(v10))*uint64(uint32(v14))
+	v06 ^= v10
+	v06 = v06>>63 | v06<<1
+
+	v03 += v07 + 2*uint64(uint32(v03))*uint64(uint32(v07))
+	v15 ^= v03
+	v15 = v15>>32 | v15<<32
+	v11 += v15 + 2*uint64(uint32(v11))*uint64(uint32(v15))
+	v07 ^= v11
+	v07 = v07>>24 | v07<<40
+
+	v03 += v07 + 2*uint64(uint32(v03))*uint64(uint32(v07))
+	v15 ^= v03
+	v15 = v15>>16 | v15<<48
+	v11 += v15 + 2*uint64(uint32(v11))*uint64(uint32(v15))
+	v07 ^= v11
+	v07 = v07>>63 | v07<<1
+
+	v00 += v05 + 2*uint64(uint32(v00))*uint64(uint32(v05))
+	v15 ^= v00
+	v15 = v15>>32 | v15<<32
+	v10 += v15 + 2*uint64(uint32(v10))*uint64(uint32(v15))
+	v05 ^= v10
+	v05 = v05>>24 | v05<<40
+
+	v00 += v05 + 2*uint64(uint32(v00))*uint64(uint32(v05))
+	v15 ^= v00
+	v15 = v15>>16 | v15<<48
+	v10 += v15 + 2*uint64(uint32(v10))*uint64(uint32(v15))
+	v05 ^= v10
+	v05 = v05>>63 | v05<<1
+
+	v01 += v06 + 2*uint64(uint32(v01))*uint64(uint32(v06))
+	v12 ^= v01
+	v12 = v12>>32 | v12<<32
+	v11 += v12 + 2*uint64(uint32(v11))*uint64(uint32(v12))
+	v06 ^= v11
+	v06 = v06>>24 | v06<<40
+
+	v01 += v06 + 2*uint64(uint32(v01))*uint64(uint32(v06))
+	v12 ^= v01
+	v12 = v12>>16 | v12<<48
+	v11 += v12 + 2*uint64(uint32(v11))*uint64(uint32(v12))
+	v06 ^= v11
+	v06 = v06>>63 | v06<<1
+
+	v02 += v07 + 2*uint64(uint32(v02))*uint64(uint32(v07))
+	v13 ^= v02
+	v13 = v13>>32 | v13<<32
+	v08 += v13 + 2*uint64(uint32(v08))*uint64(uint32(v13))
+	v07 ^= v08
+	v07 = v07>>24 | v07<<40
+
+	v02 += v07 + 2*uint64(uint32(v02))*uint64(uint32(v07))
+	v13 ^= v02
+	v13 = v13>>16 | v13<<48
+	v08 += v13 + 2*uint64(uint32(v08))*uint64(uint32(v13))
+	v07 ^= v08
+	v07 = v07>>63 | v07<<1
+
+	v03 += v04 + 2*uint64(uint32(v03))*uint64(uint32(v04))
+	v14 ^= v03
+	v14 = v14>>32 | v14<<32
+	v09 += v14 + 2*uint64(uint32(v09))*uint64(uint32(v14))
+	v04 ^= v09
+	v04 = v04>>24 | v04<<40
+
+	v03 += v04 + 2*uint64(uint32(v03))*uint64(uint32(v04))
+	v14 ^= v03
+	v14 = v14>>16 | v14<<48
+	v09 += v14 + 2*uint64(uint32(v09))*uint64(uint32(v14))
+	v04 ^= v09
+	v04 = v04>>63 | v04<<1
+
+	*t00, *t01, *t02, *t03 = v00, v01, v02, v03
+	*t04, *t05, *t06, *t07 = v04, v05, v06, v07
+	*t08, *t09, *t10, *t11 = v08, v09, v10, v11
+	*t12, *t13, *t14, *t15 = v12, v13, v14, v15
+}

+ 15 - 0
psiphon/common/crypto/argon2/blamka_ref.go

@@ -0,0 +1,15 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !amd64 appengine gccgo
+
+package argon2
+
+func processBlock(out, in1, in2 *block) {
+	processBlockGeneric(out, in1, in2, false)
+}
+
+func processBlockXOR(out, in1, in2 *block) {
+	processBlockGeneric(out, in1, in2, true)
+}

+ 2 - 2
psiphon/common/crypto/bcrypt/bcrypt.go

@@ -241,11 +241,11 @@ func (p *hashed) Hash() []byte {
 		n = 3
 	}
 	arr[n] = '$'
-	n += 1
+	n++
 	copy(arr[n:], []byte(fmt.Sprintf("%02d", p.cost)))
 	n += 2
 	arr[n] = '$'
-	n += 1
+	n++
 	copy(arr[n:], p.salt)
 	n += encodedSaltSize
 	copy(arr[n:], p.hash)

+ 83 - 1
psiphon/common/crypto/blake2b/blake2b.go

@@ -39,7 +39,10 @@ var (
 	useSSE4 bool
 )
 
-var errKeySize = errors.New("blake2b: invalid key size")
+var (
+	errKeySize  = errors.New("blake2b: invalid key size")
+	errHashSize = errors.New("blake2b: invalid hash size")
+)
 
 var iv = [8]uint64{
 	0x6a09e667f3bcc908, 0xbb67ae8584caa73b, 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1,
@@ -83,7 +86,20 @@ func New384(key []byte) (hash.Hash, error) { return newDigest(Size384, key) }
 // key turns the hash into a MAC. The key must between zero and 64 bytes long.
 func New256(key []byte) (hash.Hash, error) { return newDigest(Size256, key) }
 
+// New returns a new hash.Hash computing the BLAKE2b checksum with a custom length.
+// A non-nil key turns the hash into a MAC. The key must between zero and 64 bytes long.
+// The hash size can be a value between 1 and 64 but it is highly recommended to use
+// values equal or greater than:
+// - 32 if BLAKE2b is used as a hash function (The key is zero bytes long).
+// - 16 if BLAKE2b is used as a MAC function (The key is at least 16 bytes long).
+// When the key is nil, the returned hash.Hash implements BinaryMarshaler
+// and BinaryUnmarshaler for state (de)serialization as documented by hash.Hash.
+func New(size int, key []byte) (hash.Hash, error) { return newDigest(size, key) }
+
 func newDigest(hashSize int, key []byte) (*digest, error) {
+	if hashSize < 1 || hashSize > Size {
+		return nil, errHashSize
+	}
 	if len(key) > Size {
 		return nil, errKeySize
 	}
@@ -136,6 +152,50 @@ type digest struct {
 	keyLen int
 }
 
+const (
+	magic         = "b2b"
+	marshaledSize = len(magic) + 8*8 + 2*8 + 1 + BlockSize + 1
+)
+
+func (d *digest) MarshalBinary() ([]byte, error) {
+	if d.keyLen != 0 {
+		return nil, errors.New("crypto/blake2b: cannot marshal MACs")
+	}
+	b := make([]byte, 0, marshaledSize)
+	b = append(b, magic...)
+	for i := 0; i < 8; i++ {
+		b = appendUint64(b, d.h[i])
+	}
+	b = appendUint64(b, d.c[0])
+	b = appendUint64(b, d.c[1])
+	// Maximum value for size is 64
+	b = append(b, byte(d.size))
+	b = append(b, d.block[:]...)
+	b = append(b, byte(d.offset))
+	return b, nil
+}
+
+func (d *digest) UnmarshalBinary(b []byte) error {
+	if len(b) < len(magic) || string(b[:len(magic)]) != magic {
+		return errors.New("crypto/blake2b: invalid hash state identifier")
+	}
+	if len(b) != marshaledSize {
+		return errors.New("crypto/blake2b: invalid hash state size")
+	}
+	b = b[len(magic):]
+	for i := 0; i < 8; i++ {
+		b, d.h[i] = consumeUint64(b)
+	}
+	b, d.c[0] = consumeUint64(b)
+	b, d.c[1] = consumeUint64(b)
+	d.size = int(b[0])
+	b = b[1:]
+	copy(d.block[:], b[:BlockSize])
+	b = b[BlockSize:]
+	d.offset = int(b[0])
+	return nil
+}
+
 func (d *digest) BlockSize() int { return BlockSize }
 
 func (d *digest) Size() int { return d.size }
@@ -205,3 +265,25 @@ func (d *digest) finalize(hash *[Size]byte) {
 		binary.LittleEndian.PutUint64(hash[8*i:], v)
 	}
 }
+
+func appendUint64(b []byte, x uint64) []byte {
+	var a [8]byte
+	binary.BigEndian.PutUint64(a[:], x)
+	return append(b, a[:]...)
+}
+
+func appendUint32(b []byte, x uint32) []byte {
+	var a [4]byte
+	binary.BigEndian.PutUint32(a[:], x)
+	return append(b, a[:]...)
+}
+
+func consumeUint64(b []byte) ([]byte, uint64) {
+	x := binary.BigEndian.Uint64(b)
+	return b[8:], x
+}
+
+func consumeUint32(b []byte) ([]byte, uint32) {
+	x := binary.BigEndian.Uint32(b)
+	return b[4:], x
+}

+ 10 - 16
psiphon/common/crypto/blake2b/blake2bAVX2_amd64.go

@@ -6,21 +6,14 @@
 
 package blake2b
 
+import "golang.org/x/sys/cpu"
+
 func init() {
-	useAVX2 = supportsAVX2()
-	useAVX = supportsAVX()
-	useSSE4 = supportsSSE4()
+	useAVX2 = cpu.X86.HasAVX2
+	useAVX = cpu.X86.HasAVX
+	useSSE4 = cpu.X86.HasSSE41
 }
 
-//go:noescape
-func supportsSSE4() bool
-
-//go:noescape
-func supportsAVX() bool
-
-//go:noescape
-func supportsAVX2() bool
-
 //go:noescape
 func hashBlocksAVX2(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte)
 
@@ -31,13 +24,14 @@ func hashBlocksAVX(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte)
 func hashBlocksSSE4(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte)
 
 func hashBlocks(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte) {
-	if useAVX2 {
+	switch {
+	case useAVX2:
 		hashBlocksAVX2(h, c, flag, blocks)
-	} else if useAVX {
+	case useAVX:
 		hashBlocksAVX(h, c, flag, blocks)
-	} else if useSSE4 {
+	case useSSE4:
 		hashBlocksSSE4(h, c, flag, blocks)
-	} else {
+	default:
 		hashBlocksGeneric(h, c, flag, blocks)
 	}
 }

+ 0 - 12
psiphon/common/crypto/blake2b/blake2bAVX2_amd64.s

@@ -748,15 +748,3 @@ noinc:
 
 	MOVQ BP, SP
 	RET
-
-// func supportsAVX2() bool
-TEXT ·supportsAVX2(SB), 4, $0-1
-	MOVQ runtime·support_avx2(SB), AX
-	MOVB AX, ret+0(FP)
-	RET
-
-// func supportsAVX() bool
-TEXT ·supportsAVX(SB), 4, $0-1
-	MOVQ runtime·support_avx(SB), AX
-	MOVB AX, ret+0(FP)
-	RET

+ 3 - 4
psiphon/common/crypto/blake2b/blake2b_amd64.go

@@ -6,13 +6,12 @@
 
 package blake2b
 
+import "golang.org/x/sys/cpu"
+
 func init() {
-	useSSE4 = supportsSSE4()
+	useSSE4 = cpu.X86.HasSSE41
 }
 
-//go:noescape
-func supportsSSE4() bool
-
 //go:noescape
 func hashBlocksSSE4(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte)
 

+ 0 - 9
psiphon/common/crypto/blake2b/blake2b_amd64.s

@@ -279,12 +279,3 @@ noinc:
 
 	MOVQ BP, SP
 	RET
-
-// func supportsSSE4() bool
-TEXT ·supportsSSE4(SB), 4, $0-1
-	MOVL $1, AX
-	CPUID
-	SHRL $19, CX  // Bit 19 indicates SSE4 support
-	ANDL $1, CX  // CX != 0 if support SSE4
-	MOVB CX, ret+0(FP)
-	RET

+ 50 - 1
psiphon/common/crypto/blake2b/blake2b_test.go

@@ -6,6 +6,7 @@ package blake2b
 
 import (
 	"bytes"
+	"encoding"
 	"encoding/hex"
 	"fmt"
 	"hash"
@@ -69,6 +70,54 @@ func TestHashes2X(t *testing.T) {
 	testHashes2X(t)
 }
 
+func TestMarshal(t *testing.T) {
+	input := make([]byte, 255)
+	for i := range input {
+		input[i] = byte(i)
+	}
+	for _, size := range []int{Size, Size256, Size384, 12, 25, 63} {
+		for i := 0; i < 256; i++ {
+			h, err := New(size, nil)
+			if err != nil {
+				t.Fatalf("size=%d, len(input)=%d: error from New(%v, nil): %v", size, i, size, err)
+			}
+			h2, err := New(size, nil)
+			if err != nil {
+				t.Fatalf("size=%d, len(input)=%d: error from New(%v, nil): %v", size, i, size, err)
+			}
+
+			h.Write(input[:i/2])
+			halfstate, err := h.(encoding.BinaryMarshaler).MarshalBinary()
+			if err != nil {
+				t.Fatalf("size=%d, len(input)=%d: could not marshal: %v", size, i, err)
+			}
+			err = h2.(encoding.BinaryUnmarshaler).UnmarshalBinary(halfstate)
+			if err != nil {
+				t.Fatalf("size=%d, len(input)=%d: could not unmarshal: %v", size, i, err)
+			}
+
+			h.Write(input[i/2 : i])
+			sum := h.Sum(nil)
+			h2.Write(input[i/2 : i])
+			sum2 := h2.Sum(nil)
+
+			if !bytes.Equal(sum, sum2) {
+				t.Fatalf("size=%d, len(input)=%d: results do not match; sum = %v, sum2 = %v", size, i, sum, sum2)
+			}
+
+			h3, err := New(size, nil)
+			if err != nil {
+				t.Fatalf("size=%d, len(input)=%d: error from New(%v, nil): %v", size, i, size, err)
+			}
+			h3.Write(input[:i])
+			sum3 := h3.Sum(nil)
+			if !bytes.Equal(sum, sum3) {
+				t.Fatalf("size=%d, len(input)=%d: sum = %v, want %v", size, i, sum, sum3)
+			}
+		}
+	}
+}
+
 func testHashes(t *testing.T) {
 	key, _ := hex.DecodeString("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f")
 
@@ -126,7 +175,7 @@ func testHashes2X(t *testing.T) {
 			t.Fatalf("#%d (single write): error from Read: %v", i, err)
 		}
 		if n, err := h.Read(sum); n != 0 || err != io.EOF {
-			t.Fatalf("#%d (single write): Read did not return (0, os.EOF) after exhaustion, got (%v, %v)", i, n, err)
+			t.Fatalf("#%d (single write): Read did not return (0, io.EOF) after exhaustion, got (%v, %v)", i, n, err)
 		}
 		if gotHex := fmt.Sprintf("%x", sum); gotHex != expectedHex {
 			t.Fatalf("#%d (single write): got %s, wanted %s", i, gotHex, expectedHex)

+ 57 - 0
psiphon/common/crypto/blake2s/blake2s.go

@@ -49,6 +49,8 @@ func Sum256(data []byte) [Size]byte {
 
 // New256 returns a new hash.Hash computing the BLAKE2s-256 checksum. A non-nil
 // key turns the hash into a MAC. The key must between zero and 32 bytes long.
+// When the key is nil, the returned hash.Hash implements BinaryMarshaler
+// and BinaryUnmarshaler for state (de)serialization as documented by hash.Hash.
 func New256(key []byte) (hash.Hash, error) { return newDigest(Size, key) }
 
 // New128 returns a new hash.Hash computing the BLAKE2s-128 checksum given a
@@ -120,6 +122,50 @@ type digest struct {
 	keyLen int
 }
 
+const (
+	magic         = "b2s"
+	marshaledSize = len(magic) + 8*4 + 2*4 + 1 + BlockSize + 1
+)
+
+func (d *digest) MarshalBinary() ([]byte, error) {
+	if d.keyLen != 0 {
+		return nil, errors.New("crypto/blake2s: cannot marshal MACs")
+	}
+	b := make([]byte, 0, marshaledSize)
+	b = append(b, magic...)
+	for i := 0; i < 8; i++ {
+		b = appendUint32(b, d.h[i])
+	}
+	b = appendUint32(b, d.c[0])
+	b = appendUint32(b, d.c[1])
+	// Maximum value for size is 32
+	b = append(b, byte(d.size))
+	b = append(b, d.block[:]...)
+	b = append(b, byte(d.offset))
+	return b, nil
+}
+
+func (d *digest) UnmarshalBinary(b []byte) error {
+	if len(b) < len(magic) || string(b[:len(magic)]) != magic {
+		return errors.New("crypto/blake2s: invalid hash state identifier")
+	}
+	if len(b) != marshaledSize {
+		return errors.New("crypto/blake2s: invalid hash state size")
+	}
+	b = b[len(magic):]
+	for i := 0; i < 8; i++ {
+		b, d.h[i] = consumeUint32(b)
+	}
+	b, d.c[0] = consumeUint32(b)
+	b, d.c[1] = consumeUint32(b)
+	d.size = int(b[0])
+	b = b[1:]
+	copy(d.block[:], b[:BlockSize])
+	b = b[BlockSize:]
+	d.offset = int(b[0])
+	return nil
+}
+
 func (d *digest) BlockSize() int { return BlockSize }
 
 func (d *digest) Size() int { return d.size }
@@ -185,3 +231,14 @@ func (d *digest) finalize(hash *[Size]byte) {
 		binary.LittleEndian.PutUint32(hash[4*i:], v)
 	}
 }
+
+func appendUint32(b []byte, x uint32) []byte {
+	var a [4]byte
+	binary.BigEndian.PutUint32(a[:], x)
+	return append(b, a[:]...)
+}
+
+func consumeUint32(b []byte) ([]byte, uint32) {
+	x := binary.BigEndian.Uint32(b)
+	return b[4:], x
+}

+ 8 - 11
psiphon/common/crypto/blake2s/blake2s_386.go

@@ -6,18 +6,14 @@
 
 package blake2s
 
+import "golang.org/x/sys/cpu"
+
 var (
 	useSSE4  = false
-	useSSSE3 = supportSSSE3()
-	useSSE2  = supportSSE2()
+	useSSSE3 = cpu.X86.HasSSSE3
+	useSSE2  = cpu.X86.HasSSE2
 )
 
-//go:noescape
-func supportSSE2() bool
-
-//go:noescape
-func supportSSSE3() bool
-
 //go:noescape
 func hashBlocksSSE2(h *[8]uint32, c *[2]uint32, flag uint32, blocks []byte)
 
@@ -25,11 +21,12 @@ func hashBlocksSSE2(h *[8]uint32, c *[2]uint32, flag uint32, blocks []byte)
 func hashBlocksSSSE3(h *[8]uint32, c *[2]uint32, flag uint32, blocks []byte)
 
 func hashBlocks(h *[8]uint32, c *[2]uint32, flag uint32, blocks []byte) {
-	if useSSSE3 {
+	switch {
+	case useSSSE3:
 		hashBlocksSSSE3(h, c, flag, blocks)
-	} else if useSSE2 {
+	case useSSE2:
 		hashBlocksSSE2(h, c, flag, blocks)
-	} else {
+	default:
 		hashBlocksGeneric(h, c, flag, blocks)
 	}
 }

+ 0 - 25
psiphon/common/crypto/blake2s/blake2s_386.s

@@ -433,28 +433,3 @@ loop:
 
 	MOVL BP, SP
 	RET
-
-// func supportSSSE3() bool
-TEXT ·supportSSSE3(SB), 4, $0-1
-	MOVL $1, AX
-	CPUID
-	MOVL CX, BX
-	ANDL $0x1, BX      // supports SSE3
-	JZ   FALSE
-	ANDL $0x200, CX    // supports SSSE3
-	JZ   FALSE
-	MOVB $1, ret+0(FP)
-	RET
-
-FALSE:
-	MOVB $0, ret+0(FP)
-	RET
-
-// func supportSSE2() bool
-TEXT ·supportSSE2(SB), 4, $0-1
-	MOVL $1, AX
-	CPUID
-	SHRL $26, DX
-	ANDL $1, DX        // DX != 0 if support SSE2
-	MOVB DX, ret+0(FP)
-	RET

+ 10 - 13
psiphon/common/crypto/blake2s/blake2s_amd64.go

@@ -6,18 +6,14 @@
 
 package blake2s
 
+import "golang.org/x/sys/cpu"
+
 var (
-	useSSE4  = supportSSE4()
-	useSSSE3 = supportSSSE3()
-	useSSE2  = true // Always available on amd64
+	useSSE4  = cpu.X86.HasSSE41
+	useSSSE3 = cpu.X86.HasSSSE3
+	useSSE2  = cpu.X86.HasSSE2
 )
 
-//go:noescape
-func supportSSSE3() bool
-
-//go:noescape
-func supportSSE4() bool
-
 //go:noescape
 func hashBlocksSSE2(h *[8]uint32, c *[2]uint32, flag uint32, blocks []byte)
 
@@ -28,13 +24,14 @@ func hashBlocksSSSE3(h *[8]uint32, c *[2]uint32, flag uint32, blocks []byte)
 func hashBlocksSSE4(h *[8]uint32, c *[2]uint32, flag uint32, blocks []byte)
 
 func hashBlocks(h *[8]uint32, c *[2]uint32, flag uint32, blocks []byte) {
-	if useSSE4 {
+	switch {
+	case useSSE4:
 		hashBlocksSSE4(h, c, flag, blocks)
-	} else if useSSSE3 {
+	case useSSSE3:
 		hashBlocksSSSE3(h, c, flag, blocks)
-	} else if useSSE2 {
+	case useSSE2:
 		hashBlocksSSE2(h, c, flag, blocks)
-	} else {
+	default:
 		hashBlocksGeneric(h, c, flag, blocks)
 	}
 }

+ 0 - 25
psiphon/common/crypto/blake2s/blake2s_amd64.s

@@ -436,28 +436,3 @@ TEXT ·hashBlocksSSSE3(SB), 0, $672-48 // frame = 656 + 16 byte alignment
 TEXT ·hashBlocksSSE4(SB), 0, $32-48 // frame = 16 + 16 byte alignment
 	HASH_BLOCKS(h+0(FP), c+8(FP), flag+16(FP), blocks_base+24(FP), blocks_len+32(FP), BLAKE2s_SSE4)
 	RET
-
-// func supportSSE4() bool
-TEXT ·supportSSE4(SB), 4, $0-1
-	MOVL $1, AX
-	CPUID
-	SHRL $19, CX       // Bit 19 indicates SSE4.1.
-	ANDL $1, CX
-	MOVB CX, ret+0(FP)
-	RET
-
-// func supportSSSE3() bool
-TEXT ·supportSSSE3(SB), 4, $0-1
-	MOVL $1, AX
-	CPUID
-	MOVL CX, BX
-	ANDL $0x1, BX      // Bit zero indicates SSE3 support.
-	JZ   FALSE
-	ANDL $0x200, CX    // Bit nine indicates SSSE3 support.
-	JZ   FALSE
-	MOVB $1, ret+0(FP)
-	RET
-
-FALSE:
-	MOVB $0, ret+0(FP)
-	RET

+ 49 - 1
psiphon/common/crypto/blake2s/blake2s_test.go

@@ -5,6 +5,8 @@
 package blake2s
 
 import (
+	"bytes"
+	"encoding"
 	"encoding/hex"
 	"fmt"
 	"testing"
@@ -64,6 +66,52 @@ func TestHashes2X(t *testing.T) {
 	testHashes2X(t)
 }
 
+func TestMarshal(t *testing.T) {
+	input := make([]byte, 255)
+	for i := range input {
+		input[i] = byte(i)
+	}
+	for i := 0; i < 256; i++ {
+		h, err := New256(nil)
+		if err != nil {
+			t.Fatalf("len(input)=%d: error from New256(nil): %v", i, err)
+		}
+		h2, err := New256(nil)
+		if err != nil {
+			t.Fatalf("len(input)=%d: error from New256(nil): %v", i, err)
+		}
+
+		h.Write(input[:i/2])
+		halfstate, err := h.(encoding.BinaryMarshaler).MarshalBinary()
+		if err != nil {
+			t.Fatalf("len(input)=%d: could not marshal: %v", i, err)
+		}
+		err = h2.(encoding.BinaryUnmarshaler).UnmarshalBinary(halfstate)
+		if err != nil {
+			t.Fatalf("len(input)=%d: could not unmarshal: %v", i, err)
+		}
+
+		h.Write(input[i/2 : i])
+		sum := h.Sum(nil)
+		h2.Write(input[i/2 : i])
+		sum2 := h2.Sum(nil)
+
+		if !bytes.Equal(sum, sum2) {
+			t.Fatalf("len(input)=%d: results do not match; sum = %v, sum2 = %v", i, sum, sum2)
+		}
+
+		h3, err := New256(nil)
+		if err != nil {
+			t.Fatalf("len(input)=%d: error from New256(nil): %v", i, err)
+		}
+		h3.Write(input[:i])
+		sum3 := h3.Sum(nil)
+		if !bytes.Equal(sum, sum3) {
+			t.Fatalf("len(input)=%d: sum = %v, want %v", i, sum, sum3)
+		}
+	}
+}
+
 func testHashes(t *testing.T) {
 	key, _ := hex.DecodeString("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f")
 
@@ -185,7 +233,7 @@ func testHashes2X(t *testing.T) {
 	if n, err := h.Read(result[:]); err != nil {
 		t.Fatalf("#unknown length: error from Read: %v", err)
 	} else if n != len(result) {
-		t.Fatalf("#unknown length: Read returned %d bytes, want %d: %v", n, len(result))
+		t.Fatalf("#unknown length: Read returned %d bytes, want %d", n, len(result))
 	}
 
 	const expected = "2a9a6977d915a2c4dd07dbcafe1918bf1682e56d9c8e567ecd19bfd7cd93528833c764d12b34a5e2a219c9fd463dab45e972c5574d73f45de5b2e23af72530d8"

+ 26 - 14
psiphon/common/crypto/bn256/bn256.go

@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Package bn256 implements a particular bilinear group at the 128-bit security level.
+// Package bn256 implements a particular bilinear group.
 //
 // Bilinear groups are the basis of many of the new cryptographic protocols
 // that have been proposed over the past decade. They consist of a triplet of
@@ -14,6 +14,10 @@
 // Barreto-Naehrig curve as described in
 // http://cryptojedi.org/papers/dclxvi-20100714.pdf. Its output is compatible
 // with the implementation described in that paper.
+//
+// (This package previously claimed to operate at a 128-bit security level.
+// However, recent improvements in attacks mean that is no longer true. See
+// https://moderncrypto.org/mail-archive/curves/2016/000740.html.)
 package bn256 // import "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/bn256"
 
 import (
@@ -49,8 +53,8 @@ func RandomG1(r io.Reader) (*big.Int, *G1, error) {
 	return k, new(G1).ScalarBaseMult(k), nil
 }
 
-func (g *G1) String() string {
-	return "bn256.G1" + g.p.String()
+func (e *G1) String() string {
+	return "bn256.G1" + e.p.String()
 }
 
 // ScalarBaseMult sets e to g*k where g is the generator of the group and
@@ -92,15 +96,19 @@ func (e *G1) Neg(a *G1) *G1 {
 }
 
 // Marshal converts n to a byte slice.
-func (n *G1) Marshal() []byte {
-	n.p.MakeAffine(nil)
-
-	xBytes := new(big.Int).Mod(n.p.x, p).Bytes()
-	yBytes := new(big.Int).Mod(n.p.y, p).Bytes()
-
+func (e *G1) Marshal() []byte {
 	// Each value is a 256-bit number.
 	const numBytes = 256 / 8
 
+	if e.p.IsInfinity() {
+		return make([]byte, numBytes*2)
+	}
+
+	e.p.MakeAffine(nil)
+
+	xBytes := new(big.Int).Mod(e.p.x, p).Bytes()
+	yBytes := new(big.Int).Mod(e.p.y, p).Bytes()
+
 	ret := make([]byte, numBytes*2)
 	copy(ret[1*numBytes-len(xBytes):], xBytes)
 	copy(ret[2*numBytes-len(yBytes):], yBytes)
@@ -166,8 +174,8 @@ func RandomG2(r io.Reader) (*big.Int, *G2, error) {
 	return k, new(G2).ScalarBaseMult(k), nil
 }
 
-func (g *G2) String() string {
-	return "bn256.G2" + g.p.String()
+func (e *G2) String() string {
+	return "bn256.G2" + e.p.String()
 }
 
 // ScalarBaseMult sets e to g*k where g is the generator of the group and
@@ -201,6 +209,13 @@ func (e *G2) Add(a, b *G2) *G2 {
 
 // Marshal converts n into a byte slice.
 func (n *G2) Marshal() []byte {
+	// Each value is a 256-bit number.
+	const numBytes = 256 / 8
+
+	if n.p.IsInfinity() {
+		return make([]byte, numBytes*4)
+	}
+
 	n.p.MakeAffine(nil)
 
 	xxBytes := new(big.Int).Mod(n.p.x.x, p).Bytes()
@@ -208,9 +223,6 @@ func (n *G2) Marshal() []byte {
 	yxBytes := new(big.Int).Mod(n.p.y.x, p).Bytes()
 	yyBytes := new(big.Int).Mod(n.p.y.y, p).Bytes()
 
-	// Each value is a 256-bit number.
-	const numBytes = 256 / 8
-
 	ret := make([]byte, numBytes*4)
 	copy(ret[1*numBytes-len(xxBytes):], xxBytes)
 	copy(ret[2*numBytes-len(xyBytes):], xyBytes)

+ 9 - 0
psiphon/common/crypto/bn256/curve.go

@@ -245,10 +245,19 @@ func (c *curvePoint) Mul(a *curvePoint, scalar *big.Int, pool *bnPool) *curvePoi
 	return c
 }
 
+// MakeAffine converts c to affine form and returns c. If c is ∞, then it sets
+// c to 0 : 1 : 0.
 func (c *curvePoint) MakeAffine(pool *bnPool) *curvePoint {
 	if words := c.z.Bits(); len(words) == 1 && words[0] == 1 {
 		return c
 	}
+	if c.IsInfinity() {
+		c.x.SetInt64(0)
+		c.y.SetInt64(1)
+		c.z.SetInt64(0)
+		c.t.SetInt64(0)
+		return c
+	}
 
 	zInv := pool.Get().ModInverse(c.z, p)
 	t := pool.Get().Mul(c.y, zInv)

+ 9 - 0
psiphon/common/crypto/bn256/twist.go

@@ -219,10 +219,19 @@ func (c *twistPoint) Mul(a *twistPoint, scalar *big.Int, pool *bnPool) *twistPoi
 	return c
 }
 
+// MakeAffine converts c to affine form and returns c. If c is ∞, then it sets
+// c to 0 : 1 : 0.
 func (c *twistPoint) MakeAffine(pool *bnPool) *twistPoint {
 	if c.z.IsOne() {
 		return c
 	}
+	if c.IsInfinity() {
+		c.x.SetZero()
+		c.y.SetOne()
+		c.z.SetZero()
+		c.t.SetZero()
+		return c
+	}
 
 	zInv := newGFp2(pool).Invert(c.z, pool)
 	t := newGFp2(pool).Mul(c.y, zInv, pool)

+ 23 - 5
psiphon/common/crypto/chacha20poly1305/chacha20poly1305.go

@@ -2,32 +2,50 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Package chacha20poly1305 implements the ChaCha20-Poly1305 AEAD as specified in RFC 7539.
+// Package chacha20poly1305 implements the ChaCha20-Poly1305 AEAD as specified in RFC 7539,
+// and its extended nonce variant XChaCha20-Poly1305.
 package chacha20poly1305 // import "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/chacha20poly1305"
 
 import (
 	"crypto/cipher"
+	"encoding/binary"
 	"errors"
 )
 
 const (
 	// KeySize is the size of the key used by this AEAD, in bytes.
 	KeySize = 32
-	// NonceSize is the size of the nonce used with this AEAD, in bytes.
+
+	// NonceSize is the size of the nonce used with the standard variant of this
+	// AEAD, in bytes.
+	//
+	// Note that this is too short to be safely generated at random if the same
+	// key is reused more than 2³² times.
 	NonceSize = 12
+
+	// NonceSizeX is the size of the nonce used with the XChaCha20-Poly1305
+	// variant of this AEAD, in bytes.
+	NonceSizeX = 24
 )
 
 type chacha20poly1305 struct {
-	key [32]byte
+	key [8]uint32
 }
 
-// New returns a ChaCha20-Poly1305 AEAD that uses the given, 256-bit key.
+// New returns a ChaCha20-Poly1305 AEAD that uses the given 256-bit key.
 func New(key []byte) (cipher.AEAD, error) {
 	if len(key) != KeySize {
 		return nil, errors.New("chacha20poly1305: bad key length")
 	}
 	ret := new(chacha20poly1305)
-	copy(ret.key[:], key)
+	ret.key[0] = binary.LittleEndian.Uint32(key[0:4])
+	ret.key[1] = binary.LittleEndian.Uint32(key[4:8])
+	ret.key[2] = binary.LittleEndian.Uint32(key[8:12])
+	ret.key[3] = binary.LittleEndian.Uint32(key[12:16])
+	ret.key[4] = binary.LittleEndian.Uint32(key[16:20])
+	ret.key[5] = binary.LittleEndian.Uint32(key[20:24])
+	ret.key[6] = binary.LittleEndian.Uint32(key[24:28])
+	ret.key[7] = binary.LittleEndian.Uint32(key[28:32])
 	return ret, nil
 }
 

+ 24 - 65
psiphon/common/crypto/chacha20poly1305/chacha20poly1305_amd64.go

@@ -6,7 +6,12 @@
 
 package chacha20poly1305
 
-import "encoding/binary"
+import (
+	"encoding/binary"
+
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/internal/subtle"
+	"golang.org/x/sys/cpu"
+)
 
 //go:noescape
 func chacha20Poly1305Open(dst []byte, key []uint32, src, ad []byte) bool
@@ -14,78 +19,26 @@ func chacha20Poly1305Open(dst []byte, key []uint32, src, ad []byte) bool
 //go:noescape
 func chacha20Poly1305Seal(dst []byte, key []uint32, src, ad []byte)
 
-// cpuid is implemented in chacha20poly1305_amd64.s.
-func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32)
-
-// xgetbv with ecx = 0 is implemented in chacha20poly1305_amd64.s.
-func xgetbv() (eax, edx uint32)
-
 var (
-	useASM  bool
-	useAVX2 bool
+	useAVX2 = cpu.X86.HasAVX2 && cpu.X86.HasBMI2
 )
 
-func init() {
-	detectCPUFeatures()
-}
-
-// detectCPUFeatures is used to detect if cpu instructions
-// used by the functions implemented in assembler in
-// chacha20poly1305_amd64.s are supported.
-func detectCPUFeatures() {
-	maxID, _, _, _ := cpuid(0, 0)
-	if maxID < 1 {
-		return
-	}
-
-	_, _, ecx1, _ := cpuid(1, 0)
-
-	haveSSSE3 := isSet(9, ecx1)
-	useASM = haveSSSE3
-
-	haveOSXSAVE := isSet(27, ecx1)
-
-	osSupportsAVX := false
-	// For XGETBV, OSXSAVE bit is required and sufficient.
-	if haveOSXSAVE {
-		eax, _ := xgetbv()
-		// Check if XMM and YMM registers have OS support.
-		osSupportsAVX = isSet(1, eax) && isSet(2, eax)
-	}
-	haveAVX := isSet(28, ecx1) && osSupportsAVX
-
-	if maxID < 7 {
-		return
-	}
-
-	_, ebx7, _, _ := cpuid(7, 0)
-	haveAVX2 := isSet(5, ebx7) && haveAVX
-	haveBMI2 := isSet(8, ebx7)
-
-	useAVX2 = haveAVX2 && haveBMI2
-}
-
-// isSet checks if bit at bitpos is set in value.
-func isSet(bitpos uint, value uint32) bool {
-	return value&(1<<bitpos) != 0
-}
-
 // setupState writes a ChaCha20 input matrix to state. See
 // https://tools.ietf.org/html/rfc7539#section-2.3.
-func setupState(state *[16]uint32, key *[32]byte, nonce []byte) {
+func setupState(state *[16]uint32, key *[8]uint32, nonce []byte) {
 	state[0] = 0x61707865
 	state[1] = 0x3320646e
 	state[2] = 0x79622d32
 	state[3] = 0x6b206574
 
-	state[4] = binary.LittleEndian.Uint32(key[:4])
-	state[5] = binary.LittleEndian.Uint32(key[4:8])
-	state[6] = binary.LittleEndian.Uint32(key[8:12])
-	state[7] = binary.LittleEndian.Uint32(key[12:16])
-	state[8] = binary.LittleEndian.Uint32(key[16:20])
-	state[9] = binary.LittleEndian.Uint32(key[20:24])
-	state[10] = binary.LittleEndian.Uint32(key[24:28])
-	state[11] = binary.LittleEndian.Uint32(key[28:32])
+	state[4] = key[0]
+	state[5] = key[1]
+	state[6] = key[2]
+	state[7] = key[3]
+	state[8] = key[4]
+	state[9] = key[5]
+	state[10] = key[6]
+	state[11] = key[7]
 
 	state[12] = 0
 	state[13] = binary.LittleEndian.Uint32(nonce[:4])
@@ -94,7 +47,7 @@ func setupState(state *[16]uint32, key *[32]byte, nonce []byte) {
 }
 
 func (c *chacha20poly1305) seal(dst, nonce, plaintext, additionalData []byte) []byte {
-	if !useASM {
+	if !cpu.X86.HasSSSE3 {
 		return c.sealGeneric(dst, nonce, plaintext, additionalData)
 	}
 
@@ -102,12 +55,15 @@ func (c *chacha20poly1305) seal(dst, nonce, plaintext, additionalData []byte) []
 	setupState(&state, &c.key, nonce)
 
 	ret, out := sliceForAppend(dst, len(plaintext)+16)
+	if subtle.InexactOverlap(out, plaintext) {
+		panic("chacha20poly1305: invalid buffer overlap")
+	}
 	chacha20Poly1305Seal(out[:], state[:], plaintext, additionalData)
 	return ret
 }
 
 func (c *chacha20poly1305) open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
-	if !useASM {
+	if !cpu.X86.HasSSSE3 {
 		return c.openGeneric(dst, nonce, ciphertext, additionalData)
 	}
 
@@ -116,6 +72,9 @@ func (c *chacha20poly1305) open(dst, nonce, ciphertext, additionalData []byte) (
 
 	ciphertext = ciphertext[:len(ciphertext)-16]
 	ret, out := sliceForAppend(dst, len(ciphertext))
+	if subtle.InexactOverlap(out, ciphertext) {
+		panic("chacha20poly1305: invalid buffer overlap")
+	}
 	if !chacha20Poly1305Open(out, state[:], ciphertext, additionalData) {
 		for i := range out {
 			out[i] = 0

+ 0 - 19
psiphon/common/crypto/chacha20poly1305/chacha20poly1305_amd64.s

@@ -2693,22 +2693,3 @@ sealAVX2Tail512LoopB:
 	VPERM2I128 $0x13, tmpStoreAVX2, DD3, DD0
 
 	JMP sealAVX2SealHash
-
-// func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32)
-TEXT ·cpuid(SB), NOSPLIT, $0-24
-	MOVL eaxArg+0(FP), AX
-	MOVL ecxArg+4(FP), CX
-	CPUID
-	MOVL AX, eax+8(FP)
-	MOVL BX, ebx+12(FP)
-	MOVL CX, ecx+16(FP)
-	MOVL DX, edx+20(FP)
-	RET
-
-// func xgetbv() (eax, edx uint32)
-TEXT ·xgetbv(SB),NOSPLIT,$0-8
-	MOVL $0, CX
-	XGETBV
-	MOVL AX, eax+0(FP)
-	MOVL DX, edx+4(FP)
-	RET

+ 25 - 14
psiphon/common/crypto/chacha20poly1305/chacha20poly1305_generic.go

@@ -7,7 +7,8 @@ package chacha20poly1305
 import (
 	"encoding/binary"
 
-	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/chacha20poly1305/internal/chacha20"
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/internal/chacha20"
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/internal/subtle"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/poly1305"
 )
 
@@ -16,15 +17,20 @@ func roundTo16(n int) int {
 }
 
 func (c *chacha20poly1305) sealGeneric(dst, nonce, plaintext, additionalData []byte) []byte {
-	var counter [16]byte
-	copy(counter[4:], nonce)
+	ret, out := sliceForAppend(dst, len(plaintext)+poly1305.TagSize)
+	if subtle.InexactOverlap(out, plaintext) {
+		panic("chacha20poly1305: invalid buffer overlap")
+	}
 
 	var polyKey [32]byte
-	chacha20.XORKeyStream(polyKey[:], polyKey[:], &counter, &c.key)
-
-	ret, out := sliceForAppend(dst, len(plaintext)+poly1305.TagSize)
-	counter[0] = 1
-	chacha20.XORKeyStream(out, plaintext, &counter, &c.key)
+	s := chacha20.New(c.key, [3]uint32{
+		binary.LittleEndian.Uint32(nonce[0:4]),
+		binary.LittleEndian.Uint32(nonce[4:8]),
+		binary.LittleEndian.Uint32(nonce[8:12]),
+	})
+	s.XORKeyStream(polyKey[:], polyKey[:])
+	s.Advance() // skip the next 32 bytes
+	s.XORKeyStream(out, plaintext)
 
 	polyInput := make([]byte, roundTo16(len(additionalData))+roundTo16(len(plaintext))+8+8)
 	copy(polyInput, additionalData)
@@ -44,11 +50,14 @@ func (c *chacha20poly1305) openGeneric(dst, nonce, ciphertext, additionalData []
 	copy(tag[:], ciphertext[len(ciphertext)-16:])
 	ciphertext = ciphertext[:len(ciphertext)-16]
 
-	var counter [16]byte
-	copy(counter[4:], nonce)
-
 	var polyKey [32]byte
-	chacha20.XORKeyStream(polyKey[:], polyKey[:], &counter, &c.key)
+	s := chacha20.New(c.key, [3]uint32{
+		binary.LittleEndian.Uint32(nonce[0:4]),
+		binary.LittleEndian.Uint32(nonce[4:8]),
+		binary.LittleEndian.Uint32(nonce[8:12]),
+	})
+	s.XORKeyStream(polyKey[:], polyKey[:])
+	s.Advance() // skip the next 32 bytes
 
 	polyInput := make([]byte, roundTo16(len(additionalData))+roundTo16(len(ciphertext))+8+8)
 	copy(polyInput, additionalData)
@@ -57,6 +66,9 @@ func (c *chacha20poly1305) openGeneric(dst, nonce, ciphertext, additionalData []
 	binary.LittleEndian.PutUint64(polyInput[len(polyInput)-8:], uint64(len(ciphertext)))
 
 	ret, out := sliceForAppend(dst, len(ciphertext))
+	if subtle.InexactOverlap(out, ciphertext) {
+		panic("chacha20poly1305: invalid buffer overlap")
+	}
 	if !poly1305.Verify(&tag, polyInput, &polyKey) {
 		for i := range out {
 			out[i] = 0
@@ -64,7 +76,6 @@ func (c *chacha20poly1305) openGeneric(dst, nonce, ciphertext, additionalData []
 		return nil, errOpen
 	}
 
-	counter[0] = 1
-	chacha20.XORKeyStream(out, ciphertext, &counter, &c.key)
+	s.XORKeyStream(out, ciphertext)
 	return ret, nil
 }

+ 146 - 73
psiphon/common/crypto/chacha20poly1305/chacha20poly1305_test.go

@@ -6,9 +6,13 @@ package chacha20poly1305
 
 import (
 	"bytes"
-	cr "crypto/rand"
+	"crypto/cipher"
+	cryptorand "crypto/rand"
 	"encoding/hex"
-	mr "math/rand"
+	"fmt"
+	"log"
+	mathrand "math/rand"
+	"strconv"
 	"testing"
 )
 
@@ -19,7 +23,18 @@ func TestVectors(t *testing.T) {
 		ad, _ := hex.DecodeString(test.aad)
 		plaintext, _ := hex.DecodeString(test.plaintext)
 
-		aead, err := New(key)
+		var (
+			aead cipher.AEAD
+			err  error
+		)
+		switch len(nonce) {
+		case NonceSize:
+			aead, err = New(key)
+		case NonceSizeX:
+			aead, err = NewX(key)
+		default:
+			t.Fatalf("#%d: wrong nonce length: %d", i, len(nonce))
+		}
 		if err != nil {
 			t.Fatal(err)
 		}
@@ -42,7 +57,7 @@ func TestVectors(t *testing.T) {
 		}
 
 		if len(ad) > 0 {
-			alterAdIdx := mr.Intn(len(ad))
+			alterAdIdx := mathrand.Intn(len(ad))
 			ad[alterAdIdx] ^= 0x80
 			if _, err := aead.Open(nil, nonce, ct, ad); err == nil {
 				t.Errorf("#%d: Open was successful after altering additional data", i)
@@ -50,14 +65,14 @@ func TestVectors(t *testing.T) {
 			ad[alterAdIdx] ^= 0x80
 		}
 
-		alterNonceIdx := mr.Intn(aead.NonceSize())
+		alterNonceIdx := mathrand.Intn(aead.NonceSize())
 		nonce[alterNonceIdx] ^= 0x80
 		if _, err := aead.Open(nil, nonce, ct, ad); err == nil {
 			t.Errorf("#%d: Open was successful after altering nonce", i)
 		}
 		nonce[alterNonceIdx] ^= 0x80
 
-		alterCtIdx := mr.Intn(len(ct))
+		alterCtIdx := mathrand.Intn(len(ct))
 		ct[alterCtIdx] ^= 0x80
 		if _, err := aead.Open(nil, nonce, ct, ad); err == nil {
 			t.Errorf("#%d: Open was successful after altering ciphertext", i)
@@ -68,87 +83,117 @@ func TestVectors(t *testing.T) {
 
 func TestRandom(t *testing.T) {
 	// Some random tests to verify Open(Seal) == Plaintext
-	for i := 0; i < 256; i++ {
-		var nonce [12]byte
-		var key [32]byte
-
-		al := mr.Intn(128)
-		pl := mr.Intn(16384)
-		ad := make([]byte, al)
-		plaintext := make([]byte, pl)
-		cr.Read(key[:])
-		cr.Read(nonce[:])
-		cr.Read(ad)
-		cr.Read(plaintext)
-
-		aead, err := New(key[:])
-		if err != nil {
-			t.Fatal(err)
-		}
+	f := func(t *testing.T, nonceSize int) {
+		for i := 0; i < 256; i++ {
+			var nonce = make([]byte, nonceSize)
+			var key [32]byte
+
+			al := mathrand.Intn(128)
+			pl := mathrand.Intn(16384)
+			ad := make([]byte, al)
+			plaintext := make([]byte, pl)
+			cryptorand.Read(key[:])
+			cryptorand.Read(nonce[:])
+			cryptorand.Read(ad)
+			cryptorand.Read(plaintext)
+
+			var (
+				aead cipher.AEAD
+				err  error
+			)
+			switch len(nonce) {
+			case NonceSize:
+				aead, err = New(key[:])
+			case NonceSizeX:
+				aead, err = NewX(key[:])
+			default:
+				t.Fatalf("#%d: wrong nonce length: %d", i, len(nonce))
+			}
+			if err != nil {
+				t.Fatal(err)
+			}
 
-		ct := aead.Seal(nil, nonce[:], plaintext, ad)
+			ct := aead.Seal(nil, nonce[:], plaintext, ad)
 
-		plaintext2, err := aead.Open(nil, nonce[:], ct, ad)
-		if err != nil {
-			t.Errorf("Random #%d: Open failed", i)
-			continue
-		}
+			plaintext2, err := aead.Open(nil, nonce[:], ct, ad)
+			if err != nil {
+				t.Errorf("Random #%d: Open failed", i)
+				continue
+			}
 
-		if !bytes.Equal(plaintext, plaintext2) {
-			t.Errorf("Random #%d: plaintext's don't match: got %x vs %x", i, plaintext2, plaintext)
-			continue
-		}
+			if !bytes.Equal(plaintext, plaintext2) {
+				t.Errorf("Random #%d: plaintext's don't match: got %x vs %x", i, plaintext2, plaintext)
+				continue
+			}
 
-		if len(ad) > 0 {
-			alterAdIdx := mr.Intn(len(ad))
-			ad[alterAdIdx] ^= 0x80
-			if _, err := aead.Open(nil, nonce[:], ct, ad); err == nil {
-				t.Errorf("Random #%d: Open was successful after altering additional data", i)
+			if len(ad) > 0 {
+				alterAdIdx := mathrand.Intn(len(ad))
+				ad[alterAdIdx] ^= 0x80
+				if _, err := aead.Open(nil, nonce[:], ct, ad); err == nil {
+					t.Errorf("Random #%d: Open was successful after altering additional data", i)
+				}
+				ad[alterAdIdx] ^= 0x80
 			}
-			ad[alterAdIdx] ^= 0x80
-		}
 
-		alterNonceIdx := mr.Intn(aead.NonceSize())
-		nonce[alterNonceIdx] ^= 0x80
-		if _, err := aead.Open(nil, nonce[:], ct, ad); err == nil {
-			t.Errorf("Random #%d: Open was successful after altering nonce", i)
-		}
-		nonce[alterNonceIdx] ^= 0x80
+			alterNonceIdx := mathrand.Intn(aead.NonceSize())
+			nonce[alterNonceIdx] ^= 0x80
+			if _, err := aead.Open(nil, nonce[:], ct, ad); err == nil {
+				t.Errorf("Random #%d: Open was successful after altering nonce", i)
+			}
+			nonce[alterNonceIdx] ^= 0x80
 
-		alterCtIdx := mr.Intn(len(ct))
-		ct[alterCtIdx] ^= 0x80
-		if _, err := aead.Open(nil, nonce[:], ct, ad); err == nil {
-			t.Errorf("Random #%d: Open was successful after altering ciphertext", i)
+			alterCtIdx := mathrand.Intn(len(ct))
+			ct[alterCtIdx] ^= 0x80
+			if _, err := aead.Open(nil, nonce[:], ct, ad); err == nil {
+				t.Errorf("Random #%d: Open was successful after altering ciphertext", i)
+			}
+			ct[alterCtIdx] ^= 0x80
 		}
-		ct[alterCtIdx] ^= 0x80
 	}
+	t.Run("Standard", func(t *testing.T) { f(t, NonceSize) })
+	t.Run("X", func(t *testing.T) { f(t, NonceSizeX) })
 }
 
-func benchamarkChaCha20Poly1305Seal(b *testing.B, buf []byte) {
+func benchamarkChaCha20Poly1305Seal(b *testing.B, buf []byte, nonceSize int) {
+	b.ReportAllocs()
 	b.SetBytes(int64(len(buf)))
 
 	var key [32]byte
-	var nonce [12]byte
+	var nonce = make([]byte, nonceSize)
 	var ad [13]byte
 	var out []byte
 
-	aead, _ := New(key[:])
+	var aead cipher.AEAD
+	switch len(nonce) {
+	case NonceSize:
+		aead, _ = New(key[:])
+	case NonceSizeX:
+		aead, _ = NewX(key[:])
+	}
+
 	b.ResetTimer()
 	for i := 0; i < b.N; i++ {
 		out = aead.Seal(out[:0], nonce[:], buf[:], ad[:])
 	}
 }
 
-func benchamarkChaCha20Poly1305Open(b *testing.B, buf []byte) {
+func benchamarkChaCha20Poly1305Open(b *testing.B, buf []byte, nonceSize int) {
+	b.ReportAllocs()
 	b.SetBytes(int64(len(buf)))
 
 	var key [32]byte
-	var nonce [12]byte
+	var nonce = make([]byte, nonceSize)
 	var ad [13]byte
 	var ct []byte
 	var out []byte
 
-	aead, _ := New(key[:])
+	var aead cipher.AEAD
+	switch len(nonce) {
+	case NonceSize:
+		aead, _ = New(key[:])
+	case NonceSizeX:
+		aead, _ = NewX(key[:])
+	}
 	ct = aead.Seal(ct[:0], nonce[:], buf[:], ad[:])
 
 	b.ResetTimer()
@@ -157,26 +202,54 @@ func benchamarkChaCha20Poly1305Open(b *testing.B, buf []byte) {
 	}
 }
 
-func BenchmarkChacha20Poly1305Open_64(b *testing.B) {
-	benchamarkChaCha20Poly1305Open(b, make([]byte, 64))
+func BenchmarkChacha20Poly1305(b *testing.B) {
+	for _, length := range []int{64, 1350, 8 * 1024} {
+		b.Run("Open-"+strconv.Itoa(length), func(b *testing.B) {
+			benchamarkChaCha20Poly1305Open(b, make([]byte, length), NonceSize)
+		})
+		b.Run("Seal-"+strconv.Itoa(length), func(b *testing.B) {
+			benchamarkChaCha20Poly1305Seal(b, make([]byte, length), NonceSize)
+		})
+
+		b.Run("Open-"+strconv.Itoa(length)+"-X", func(b *testing.B) {
+			benchamarkChaCha20Poly1305Open(b, make([]byte, length), NonceSizeX)
+		})
+		b.Run("Seal-"+strconv.Itoa(length)+"-X", func(b *testing.B) {
+			benchamarkChaCha20Poly1305Seal(b, make([]byte, length), NonceSizeX)
+		})
+	}
 }
 
-func BenchmarkChacha20Poly1305Seal_64(b *testing.B) {
-	benchamarkChaCha20Poly1305Seal(b, make([]byte, 64))
-}
+var key = make([]byte, KeySize)
 
-func BenchmarkChacha20Poly1305Open_1350(b *testing.B) {
-	benchamarkChaCha20Poly1305Open(b, make([]byte, 1350))
-}
+func ExampleNewX() {
+	aead, err := NewX(key)
+	if err != nil {
+		log.Fatalln("Failed to instantiate XChaCha20-Poly1305:", err)
+	}
 
-func BenchmarkChacha20Poly1305Seal_1350(b *testing.B) {
-	benchamarkChaCha20Poly1305Seal(b, make([]byte, 1350))
-}
+	for _, msg := range []string{
+		"Attack at dawn.",
+		"The eagle has landed.",
+		"Gophers, gophers, gophers everywhere!",
+	} {
+		// Encryption.
+		nonce := make([]byte, NonceSizeX)
+		if _, err := cryptorand.Read(nonce); err != nil {
+			panic(err)
+		}
+		ciphertext := aead.Seal(nil, nonce, []byte(msg), nil)
 
-func BenchmarkChacha20Poly1305Open_8K(b *testing.B) {
-	benchamarkChaCha20Poly1305Open(b, make([]byte, 8*1024))
-}
+		// Decryption.
+		plaintext, err := aead.Open(nil, nonce, ciphertext, nil)
+		if err != nil {
+			log.Fatalln("Failed to decrypt or authenticate message:", err)
+		}
+
+		fmt.Printf("%s\n", plaintext)
+	}
 
-func BenchmarkChacha20Poly1305Seal_8K(b *testing.B) {
-	benchamarkChaCha20Poly1305Seal(b, make([]byte, 8*1024))
+	// Output: Attack at dawn.
+	// The eagle has landed.
+	// Gophers, gophers, gophers everywhere!
 }

+ 394 - 0
psiphon/common/crypto/chacha20poly1305/chacha20poly1305_vectors_test.go

@@ -7,6 +7,13 @@ package chacha20poly1305
 var chacha20Poly1305Tests = []struct {
 	plaintext, aad, key, nonce, out string
 }{
+	{
+		"",
+		"",
+		"808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f",
+		"070000004041424344454647",
+		"a0784d7a4716f3feb4f64e7f4b39bf04",
+	},
 	{
 		"4c616469657320616e642047656e746c656d656e206f662074686520636c617373206f66202739393a204966204920636f756c64206f6666657220796f75206f6e6c79206f6e652074697020666f7220746865206675747572652c2073756e73637265656e20776f756c642062652069742e",
 		"50515253c0c1c2c3c4c5c6c7",
@@ -329,4 +336,391 @@ var chacha20Poly1305Tests = []struct {
 		"129039b5572e8a7a8131f76a",
 		"2c125232a59879aee36cacc4aca5085a4688c4f776667a8fbd86862b5cfb1d57c976688fdd652eafa2b88b1b8e358aa2110ff6ef13cdc1ceca9c9f087c35c38d89d6fbd8de89538070f17916ecb19ca3ef4a1c834f0bdaa1df62aaabef2e117106787056c909e61ecd208357dd5c363f11c5d6cf24992cc873cf69f59360a820fcf290bd90b2cab24c47286acb4e1033962b6d41e562a206a94796a8ab1c6b8bade804ff9bdf5ba6062d2c1f8fe0f4dfc05720bd9a612b92c26789f9f6a7ce43f5e8e3aee99a9cd7d6c11eaa611983c36935b0dda57d898a60a0ab7c4b54",
 	},
+
+	// XChaCha20-Poly1305 vectors
+	{
+		"000000000000000000000000000000",
+		"",
+		"0000000000000000000000000000000000000000000000000000000000000000",
+		"000000000000000000000000000000000000000000000000",
+		"789e9689e5208d7fd9e1f3c5b5341fb2f7033812ac9ebd3745e2c99c7bbfeb",
+	},
+	{
+		"02dc819b71875e49f5e1e5a768141cfd3f14307ae61a34d81decd9a3367c00c7",
+		"",
+		"b7bbfe61b8041658ddc95d5cbdc01bbe7626d24f3a043b70ddee87541234cff7",
+		"e293239d4c0a07840c5f83cb515be7fd59c333933027e99c",
+		"7a51f271bd2e547943c7be3316c05519a5d16803712289aa2369950b1504dd8267222e47b13280077ecada7b8795d535",
+	},
+	{
+		"7afc5f3f24155002e17dc176a8f1f3a097ff5a991b02ff4640f70b90db0c15c328b696d6998ea7988edfe3b960e47824e4ae002fbe589be57896a9b7bf5578599c6ba0153c7c",
+		"d499bb9758debe59a93783c61974b7",
+		"4ea8fab44a07f7ffc0329b2c2f8f994efdb6d505aec32113ae324def5d929ba1",
+		"404d5086271c58bf27b0352a205d21ce4367d7b6a7628961",
+		"26d2b46ad58b6988e2dcf1d09ba8ab6f532dc7e0847cdbc0ed00284225c02bbdb278ee8381ebd127a06926107d1b731cfb1521b267168926492e8f77219ad922257a5be2c5e52e6183ca4dfd0ad3912d7bd1ec968065",
+	},
+	{
+		"",
+		"",
+		"48d8bd02c2e9947eae58327114d35e055407b5519c8019535efcb4fc875b5e2b",
+		"cc0a587a475caba06f8dbc09afec1462af081fe1908c2cba",
+		"fc3322d0a9d6fac3eb4a9e09b00b361e",
+	},
+	{
+		"e0862731e5",
+		"",
+		"6579e7ee96151131a1fcd06fe0d52802c0021f214960ecceec14b2b8591f62cd",
+		"e2230748649bc22e2b71e46a7814ecabe3a7005e949bd491",
+		"e991efb85d8b1cfa3f92cb72b8d3c882e88f4529d9",
+	},
+	{
+		"00c7dd8f440af1530b44",
+		"",
+		"ffb733657c849d50ab4ab40c4ae18f8ee2f0acf7c907afefdc04dff3537fdff3",
+		"02c6fd8032a8d89edbedcd1db024c09d29f08b1e74325085",
+		"13dbcdb8c60c3ed28449a57688edfaea89e309ab4faa6d51e532",
+	},
+	{
+		"7422f311ea476cf819cb8b3c77369f",
+		"",
+		"ef0d05d028d6abdd5e99d1761d2028de75ee6eb376ff0dc8036e9a8e10743876",
+		"f772745200b0f92e38f1d8dae79bf8138e84b301f0be74df",
+		"d5f992f9834df1be86b580ac59c7eae063a68072829c51bc8a26970dd3d310",
+	},
+	{
+		"ba09ca69450e6c7bece31a7a3f216e3b9ed0e536",
+		"",
+		"8d93e31abfe22a63faf45cbea91877050718f13fef6e2664a1892d7f23007ccf",
+		"260b7b3554a7e6ff8aae7dd6234077ca539689a20c1610a8",
+		"c99e9a768eb2ec8569bdff8a37295069552faebcafb1a76e98bc7c5b6b778b3d1b6291f0",
+	},
+	{
+		"424ec5f98a0fdc5a7388532d11ab0edb26733505627b7f2d1f",
+		"",
+		"b68d5e6c46cdbb0060445522bdc5c562ae803b6aaaf1e103c146e93527a59299",
+		"80bb5dc1dd44a35ec4f91307f1a95b4ca31183a1a596fb7c",
+		"29d4eed0fff0050d4bb40de3b055d836206e7cbd62de1a63904f0cf731129ba3f9c2b9d46251a6de89",
+	},
+	{
+		"e7e4515cc0a6ef0491af983eaac4f862d6e726758a3c657f4ec444841e42",
+		"",
+		"e31a1d3af650e8e2848bd78432d89ecd1fdece9842dc2792e7bda080f537b17b",
+		"f3f09905e9a871e757348834f483ed71be9c0f437c8d74b0",
+		"f5c69528963e17db725a28885d30a45194f12848b8b7644c7bded47a2ee83e6d4ef34006305cfdf82effdced461d",
+	},
+	{
+		"0f5ca45a54875d1d19e952e53caeaa19389342f776dab11723535503338d6f77202a37",
+		"",
+		"1031bc920d4fcb4434553b1bf2d25ab375200643bf523ff037bf8914297e8dca",
+		"4cc77e2ef5445e07b5f44de2dc5bf62d35b8c6f69502d2bf",
+		"7aa8669e1bfe8b0688899cdddbb8cee31265928c66a69a5090478da7397573b1cc0f64121e7d8bff8db0ddd3c17460d7f29a12",
+	},
+	{
+		"c45578c04c194994e89025c7ffb015e5f138be3cd1a93640af167706aee2ad25ad38696df41ad805",
+		"",
+		"ac8648b7c94328419c668ce1c57c71893adf73abbb98892a4fc8da17400e3a5e",
+		"4ad637facf97af5fc03207ae56219da9972858b7430b3611",
+		"49e093fcd074fb67a755669119b8bd430d98d9232ca988882deeb3508bde7c00160c35cea89092db864dcb6d440aefa5aacb8aa7b9c04cf0",
+	},
+	{
+		"b877bfa192ea7e4c7569b9ee973f89924d45f9d8ed03c7098ad0cad6e7880906befedcaf6417bb43efabca7a2f",
+		"",
+		"125e331d5da423ecabc8adf693cdbc2fc3d3589740d40a3894f914db86c02492",
+		"913f8b2f08006e6260de41ec3ee01d938a3e68fb12dc44c4",
+		"1be334253423c90fc8ea885ee5cd3a54268c035ba8a2119e5bd4f7822cd7bf9cb4cec568d5b6d6292606d32979e044df3504e6eb8c0b2fc7e2a0e17d62",
+	},
+	{
+		"d946484a1df5f85ff72c92ff9e192660cde5074bd0ddd5de900c35eb10ed991113b1b19884631bc8ceb386bcd83908061ce9",
+		"",
+		"b7e83276373dcf8929b6a6ea80314c9de871f5f241c9144189ee4caf62726332",
+		"f59f9d6e3e6c00720dc20dc21586e8330431ebf42cf9180e",
+		"a38a662b18c2d15e1b7b14443cc23267a10bee23556b084b6254226389c414069b694159a4d0b5abbe34de381a0e2c88b947b4cfaaebf50c7a1ad6c656e386280ad7",
+	},
+	{
+		"d266927ca40b2261d5a4722f3b4da0dd5bec74e103fab431702309fd0d0f1a259c767b956aa7348ca923d64c04f0a2e898b0670988b15e",
+		"",
+		"a60e09cd0bea16f26e54b62b2908687aa89722c298e69a3a22cf6cf1c46b7f8a",
+		"92da9d67854c53597fc099b68d955be32df2f0d9efe93614",
+		"9dd6d05832f6b4d7f555a5a83930d6aed5423461d85f363efb6c474b6c4c8261b680dea393e24c2a3c8d1cc9db6df517423085833aa21f9ab5b42445b914f2313bcd205d179430",
+	},
+	{
+		"f7e11b4d372ed7cb0c0e157f2f9488d8efea0f9bbe089a345f51bdc77e30d1392813c5d22ca7e2c7dfc2e2d0da67efb2a559058d4de7a11bd2a2915e",
+		"",
+		"194b1190fa31d483c222ec475d2d6117710dd1ac19a6f1a1e8e894885b7fa631",
+		"6b07ea26bb1f2d92e04207b447f2fd1dd2086b442a7b6852",
+		"25ae14585790d71d39a6e88632228a70b1f6a041839dc89a74701c06bfa7c4de3288b7772cb2919818d95777ab58fe5480d6e49958f5d2481431014a8f88dab8f7e08d2a9aebbe691430011d",
+	},
+	{
+		"",
+		"1e2b11e3",
+		"70cd96817da85ede0efdf03a358103a84561b25453dee73735e5fb0161b0d493",
+		"5ddeba49f7266d11827a43931d1c300dd47a3c33f9f8bf9b",
+		"592fc4c19f3cddec517b2a00f9df9665",
+	},
+	{
+		"81b3cb7eb3",
+		"efcfd0cf",
+		"a977412f889281a6d75c24186f1bfaa00dcc5132f0929f20ef15bbf9e63c4c91",
+		"3f26ca997fb9166d9c615babe3e543ca43ab7cab20634ac5",
+		"8e4ade3e254cf52e93eace5c46667f150832725594",
+	},
+	{
+		"556f97f2ebdb4e949923",
+		"f7cee2e0",
+		"787b3e86546a51028501c801dadf8d5b996fd6f6f2363d5d0f900c44f6a2f4c2",
+		"7fa6af59a779657d1cada847439ea5b92a1337cfbebbc3b1",
+		"608ec22dae5f48b89d6f0d2a940d5a7661e0a8e68aaee4ad2d96",
+	},
+	{
+		"c06847a36ad031595b60edd44dc245",
+		"d4175e1f",
+		"16de31e534dd5af32801b1acd0ec541d1f8d82bcbc3af25ec815f3575b7aca73",
+		"29f6656972838f56c1684f6a278f9e4e207b51d68706fc25",
+		"836082cc51303e500fceade0b1a18f1d97d64ff41cc81754c07d6231b9fd1b",
+	},
+	{
+		"0d03c22ced7b29c6741e72166cd61792028dfc80",
+		"e505dad0",
+		"ac2b426e5c5c8e00666180a3410e8a2f6e52247a43aecea9622163e8433c93b2",
+		"c1123430468228625967bbc0fbd0f963e674372259ff2deb",
+		"bf09979bf4fed2eec6c97f6e1bcfac35eeffc6d54a55cc1d83d8767ae74db2d7cdfbc371",
+	},
+	{
+		"05bf00e1707cffe7ccbd06a9f846d0fd471a700ed43b4facb8",
+		"d863bebe",
+		"66c121f0f84b95ba1e6d29e7d81900bc96a642421b9b6105ae5eb5f2e7b07577",
+		"8ed6ae211a661e967995b71f7316ba88f44322bb62b4187b",
+		"b2c5c85d087e0305e9058fba52b661fb3d7f21cb4d4915ae048bc9e5d66a2f921dd4a1c1b030f442c9",
+	},
+	{
+		"5f2b91a9be8bfaa21451ddc6c5cf28d1cc00b046b76270b95cda3c280c83",
+		"a8750275",
+		"39592eb276877fca9dd11e2181c0b23127328407e3cc11e315e5d748f43529cc",
+		"1084bebd756f193d9eea608b3a0193a5028f8ced19684821",
+		"eaee1f49ac8468154c601a5dd8b84d597602e5a73534b5fad5664f97d0f017dd114752be969679cf610340c6a312",
+	},
+	{
+		"01e8e269b5376943f3b2d245483a76461dc8b7634868b559165f5dbb20839029fae9bb",
+		"a1e96da0",
+		"b8386123b87e50d9d046242cf1bf141fce7f65aff0fba76861a2bc72582d6ff0",
+		"0fbe2a13a89bea031de96d78f9f11358ba7b6a5e724b4392",
+		"705ec3f910ec85c6005baa99641de6ca43332ff52b5466df6af4ffbe4ef2a376a8f871d1eae503b5896601fee005cdc1f4c1c6",
+	},
+	{
+		"706daba66e2edb1f828f3c0051e3cc214b12210bde0587bba02580f741a4c83e84d4e9fe961120cd",
+		"87663c5a",
+		"d519d82ba8a3f0c3af9efe36682b62e285167be101a526c1d73000f169c2a486",
+		"ad651aac536978e2bc1a54816345ac5e9a9b43b3d9cc0bfc",
+		"07051b5e72da9c4811beb07ff9f95aece67eae18420eb3f0e8bb8a5e26d4b483fa40eb063a2354842d0c8a41d981cc2b77c530b496db01c8",
+	},
+	{
+		"1f6b24f2f0d9eb460d726bed953d66fcc4ecc29da6ed2fd711358eac3b2609d74ba3e21885156cde3cbe6d9b6f",
+		"f5efbc4e",
+		"86068a00544f749ad4ad15bb8e427ae78577ae22f4ca9778efff828ba10f6b20",
+		"c8420412c9626dcd34ece14593730f6aa2d01ec51cacd59f",
+		"a99f6c88eac35bb34439e34b292fe9db8192446dcdc81e2192060ec36d98b47de2bee12bf0f67cb24fb0949c07733a6781cd9455cdc61123f506886b04",
+	},
+	{
+		"d69389d83362be8c0ddb738659a6cc4bd65d88cb5b525232f4d59a7d4751a7203c254923ecb6873e803220aab19664789a63",
+		"bc35fb1c",
+		"835855b326a98682b3075b4d7f1b89059c3cdfc547d4296c80ce7a77ba6434e3",
+		"c27cb75fc319ba431cbaeb120341d0c4745d883eb47e92bc",
+		"db6dc3f9a0f4f1a6df2495a88910550c2c6205478bfc1e81282e34b5b36d984c72c0509c522c987c61d2e640ced69402a6d33aa10d3d0b81e680b3c19bc142e81923",
+	},
+	{
+		"a66a7f089115ed9e2d5bb5d33d7282a7afe401269b00f2a233a59c04b794a42901d862140b61d18d7c7f0ad5da040613e557f8abc74219",
+		"2c060aaf",
+		"99758aa7714fd707931f71803eefe04a06955041308a0b2a1104313b270ccf34",
+		"63f690d8926408c7a34fe8ddd505a8dc58769dc74e8d5da6",
+		"92b21ee85afcd8996ac28f3aed1047ad814d6e4ffbca3159af16f26eded83e4abda9e4275eb3ff0ad90dffe09f2d443b628f824f680b46527ce0128e8de1920f7c44350ebe7913",
+	},
+	{
+		"f955183b1f762d4536d3f6885ea7f5ac27414caf46c2e24a2fd3bd56b91c53d840fb657224565e0a6f686f8ba320e04a401057399d9a3d995ab17c13",
+		"c372ddc5",
+		"a188be3795b2ca2e69b6aa263244f0963c492d694cf6c9b705a1d7045f3f2a26",
+		"51bb484ea094ee140474681e1c838e4442fd148de2cc345a",
+		"48759a5ddfdd829d11de8e0c538ce4a9c475faab6912039b568ad92d737d172fc1eb0c00c3793de6dddbfacfdbbc7f44aeba33684e18005aa982b6fc6c556e63bb90ff7a1dde8153a63eabe0",
+	},
+	{
+		"",
+		"e013cd0bfafd486d",
+		"af3d3ba094d38299ecb91c17bfe3d085da5bd42e11acf8acb5bc26a4be9a7583",
+		"7dd63c14173831f109761b1c1abe18f6ba937d825957011b",
+		"8bc685a7d9d501952295cd25d8c92517",
+	},
+	{
+		"284b64597e",
+		"31d013e53aa3ea79",
+		"93c77409d7f805f97fe683b2dd6ee06152a5e918b3eed5b731acccffdcb2cc04",
+		"3d331e90c4597cf0c30d1b7cfbd07bcb6ab927eda056873c",
+		"3538a449d6c18d148a8c6cb76f1bc288657ac7036a",
+	},
+	{
+		"9fe67f5c78180ede8274",
+		"188608d230d75860",
+		"b7cca89a82640aea6f80b458c9e633d88594fb498959d39787be87030892d48f",
+		"ef891d50e8c08958f814590fdb7a9f16c61cc2aae1682109",
+		"bbb40c30f3d1391a5b38df480cbbf964b71e763e8140751f4e28",
+	},
+	{
+		"3a2826b6f7e3d542e4ded8f23c9aa4",
+		"260033e789c4676a",
+		"7fe2731214f2b4b42f93217d43f1776498413725e4f6cfe62b756e5a52df10ea",
+		"888728219ebf761547f5e2218532714403020e5a8b7a49d0",
+		"fe0328f883fcd88930ae017c0f54ed90f883041efc020e959125af370c1d47",
+	},
+	{
+		"91858bf7b969005d7164acbd5678052b651c53e0",
+		"f3cc53ecafcbadb3",
+		"d69c04e9726b22d51f97bc9da0f0fda86736e6b78e8ef9f6f0000f79890d6d43",
+		"6de3c45161b434e05445cf6bf69eef7bddf595fc6d8836bd",
+		"a8869dd578c0835e120c843bb7dedc7a1e9eae24ffd742be6bf5b74088a8a2c550976fcb",
+	},
+	{
+		"b3b1a4d6b2a2b9c5a1ca6c1efaec34dcfa1acbe7074d5e10cc",
+		"d0f72bd16cda3bae",
+		"2b317857b089c9305c49b83019f6e158bc4ecc3339b39ade02ee10c37c268da0",
+		"cb5fa6d1e14a0b4bdf350cd10c8a7bd638102911ec74be09",
+		"e6372f77c14343650074e07a2b7223c37b29242224b722b24d63b5956f27aa64ce7ce4e39cd14a2787",
+	},
+	{
+		"057d3e9f865be7dff774938cab6d080e50cf9a1593f53c0063201e0bb7ae",
+		"fd3881e505c8b12d",
+		"36e42b1ef1ee8d068f09b5fad3ee43d98d34aa3e3f994f2055aee139da71de9d",
+		"24124da36473d01bdca30297c9eef4fe61955525a453da17",
+		"a8b28139524c98c1f8776f442eac4c22766fe6aac83224641c58bf021fc9cb709ec4706f49c2d0c1828acf2bfe8d",
+	},
+	{
+		"bd8f13e928c34d67a6c70c3c7efdf2982ecc31d8cee68f9cbddc75912cd828ac93d28b",
+		"193206c8fcc5b19b",
+		"6e47c40c9d7b757c2efca4d73890e4c73f3c859aab4fdc64b564b8480dd84e72",
+		"ca31340ae20d30fe488be355cb36652c5db7c9d6265a3e95",
+		"a121efc5e1843deade4b8adbfef1808de4eda222f176630ad34fb476fca19e0299e4a13668e53cf13882035ba4f04f47c8b4e3",
+	},
+	{
+		"23067a196e977d10039c14ff358061c918d2148d31961bb3e12c27c5122383cb25c4d1d79c775720",
+		"62338d02fff78a00",
+		"2c5c79c92d91fb40ef7d0a77e8033f7b265e3bab998b8116d17b2e62bb4f8a09",
+		"024736adb1d5c01006dffd8158b57936d158d5b42054336d",
+		"46d0905473a995d38c7cdbb8ef3da96ecc82a22c5b3c6c9d1c4a61ae7a17db53cb88c5f7eccf2da1d0c417c300f989b4273470e36f03542f",
+	},
+	{
+		"252e966c680329eb687bff813b78fea3bfd3505333f106c6f9f45ba69896723c41bb763793d9b266e897d05557",
+		"1e93e0cfe6523380",
+		"9ec6fd1baa13ee16aec3fac16718a2baccf18a403cec467c25b7448e9b321110",
+		"e7120b1018ab363a36e61102eedbcbe9847a6cbacaa9c328",
+		"2934f034587d4144bb11182679cd2cd1c99c8088d18e233379e9bc9c41107a1f57a2723ecc7b9ba4e6ee198adf0fd766738e828827dc73136fc5b996e9",
+	},
+	{
+		"6744aefcb318f12bc6eeb59d4d62f7eb95f347cea14bd5158415f07f84e4e3baa3de07512d9b76095ac1312cfcb1bb77f499",
+		"608d2a33ce5d0b04",
+		"0f665cbdaaa40f4f5a00c53d951b0a98aac2342be259a52670f650a783be7aab",
+		"378bdb57e957b8c2e1500c9513052a3b02ff5b7edbd4a3a7",
+		"341c60fcb374b394f1b01a4a80aedef49ab0b67ec963675e6eec43ef106f7003be87dbf4a8976709583dccc55abc7f979c4721837e8664a69804ea31736aa2af615a",
+	},
+	{
+		"bcf1004f988220b7ce063ef2ec4e276ffd074f0a90aa807de1532679d2a1505568eaa4192d9a6ea52cc500322343ce9f8e68cc2c606d83",
+		"e64bd00126c8792c",
+		"58e65150d6a15dcefbc14a171998987ad0d709fb06a17d68d6a778759681c308",
+		"106d2bd120b06e4eb10bc674fe55c77a3742225268319303",
+		"a28052a6686a1e9435fee8702f7da563a7b3d7b5d3e9e27f11abf73db309cd1f39a34756258c1c5c7f2fb12cf15eb20175c2a08fc93dd19c5e482ef3fbef3d8404a3cfd54a7baf",
+	},
+	{
+		"acd08d4938a224b4cb2d723bf75420f3ea27b698fadd815bb7db9548a05651398644354334e69f8e4e5503bf1a6f92b38e860044a7edca6874038ce1",
+		"28a137808d0225b8",
+		"a031203b963a395b08be55844d81af39d19b23b7cc24b21afa31edc1eea6edd6",
+		"e8b31c52b6690f10f4ae62ba9d50ba39fb5edcfb78400e35",
+		"35cf39ba31da95ac9b661cdbd5e9c9655d13b8ff065c4ec10c810833a47a87d8057dd1948a7801bfe6904b49fed0aabfb3cd755a1a262d372786908ddcf64cae9f71cb9ed199c3ddacc50116",
+	},
+	{
+		"",
+		"cda7ee2857e09e9054ef6806",
+		"d91dffb18132d8dd3d144a2f10ba28bc5df36cb60369f3b19893ec91db3cf904",
+		"ee56f19c62b0438da6a0d9e01844313902be44f84a6a4ce7",
+		"ccd48b61a5683c195d4424009eb1d147",
+	},
+	{
+		"350f4c7ac2",
+		"7c104b539c1d2ae022434cd6",
+		"cbb61e369117f9250f68fa707240c554359262a4d66c757f80e3aeb6920894fb",
+		"fbb14c9943444eac5413c6f5c8095451eddece02c9461043",
+		"b5c6a35865ed8e5216ff6c77339ee1ab570de50e51",
+	},
+	{
+		"4f0d61d3ea03a44a8df0",
+		"51c20a8ae9e9794da931fe23",
+		"ba6ced943aa62f9261d7513b822e02054e099acafb5360f0d850064da48b5a4f",
+		"04c68cb50cdbb0ec03f8381cf59b886e64c40548bf8e3f82",
+		"ea45a73957e2a853655623f2a3bb58791f7ea36dd2957ed66ffa",
+	},
+	{
+		"4fbdd4d4293a8f34fdbc8f3ad44cf6",
+		"8212f315e3759c3253c588bb",
+		"5354791bc2370415811818e913e310dd12e6a0cf5dcab2b6424816eecccf4b65",
+		"7ee6353c2fbc73c9ebc652270bc86e4008e09583e623e679",
+		"50a354811a918e1801fb567621a8924baf8dd79da6d36702855d3753f1319c",
+	},
+	{
+		"5a6f68b5a9a9920ca9c6edf5be7c0af150a063c4",
+		"9a524aa62938fb7a1e50ed06",
+		"fd91605a6ad85d8ba7a71b08dce1032aa9992bf4f28d407a53ddda04c043cada",
+		"46791d99d6de33e79025bf9e97c198e7cf409614c6284b4d",
+		"648033c1eb615467e90b7d3ac24202d8b849549141f9bab03e9e910c29b8eab3d4fb3f2c",
+	},
+	{
+		"d9318c2c0d9ed89e35d242a6b1d496e7e0c5bbdf77eba14c56",
+		"a16053c35fbe8dc93c14a81f",
+		"f21406aec83134ebf7bc48c6d0f45acb5f341fbc7d3b5a9bff3ea1333c916af7",
+		"de6b977be450d5efa7777e006802ddbb10814a22da1c3cd9",
+		"8d3dad487d5161663da830b71c3e24ec5cdb74d858cbb73b084ed0902198532aad3a18416966bff223",
+	},
+	{
+		"68d0ee08d38cb4bcc9268fee3030666e70e41fcabf6fe06536eeec43eec5",
+		"11e09447d40b22dc98070eec",
+		"da5ee1ec02eab13220fcb94f16efec848a8dd57c0f4d67955423f5d17fde5aa3",
+		"8f13e61d773a250810f75d46bf163a3f9205be5751f6049a",
+		"92a103b03764c1ad1f88500d22eeae5c0fe1044c872987c0b97affc5e8c3d783f8cc28a11dc91990ea22dd1bad74",
+	},
+	{
+		"a1d960bda08efcf19e136dc1e8b05b6b381c820eda5f9a8047e1a2dd1803a1e4d11a7f",
+		"aa73d8d4aaa0cfd9d80a9ae8",
+		"08028833d617c28ba75b48f177cb5da87189189abb68dcb8974eca9230c25945",
+		"f7b6f34a910fd11588f567de8555932291f7df05f6e2b193",
+		"99cfc4cca193998bae153b744e6c94a82a2867780aa0f43acddb7c433fcb297311313ec2199f00d7ca7da0646b40113c60e935",
+	},
+	{
+		"3b4ae39a745b6247ce5baf675ec36c5065b1bf76c8379eab4b769961d43a753896d068938017777e",
+		"128c017a985052f8cdbc6b28",
+		"4683d5caff613187a9b16af897253848e9c54fc0ec319de62452a86961d3cbb2",
+		"5612a13c2da003b91188921cbac3fa093eba99d8cbbb51ff",
+		"91a98b93b2174257175f7c882b45cc252e0db8667612bd270c1c12fe28b6bf209760bf8f370318f92ae3f88a5d4773b05714132cc28dddb8",
+	},
+	{
+		"22ccf680d2995ef6563de281cff76882a036a59ad73f250e710b3040590d69bccde8a8411abe8b0d3cb728ca82",
+		"13a97d0a167a61aa21e531ec",
+		"9e140762eed274948b66de25e6e8f36ab65dc730b0cb096ef15aaba900a5588c",
+		"d0e9594cfd42ab72553bf34062a263f588bb8f1fc86a19f5",
+		"f194fc866dfba30e42c4508b7d90b3fa3f8983831ede713334563e36aa861f2f885b40be1dbe20ba2d10958a12823588d4bbbefb81a87d87315204f5e3",
+	},
+	{
+		"a65f5d10c482b3381af296e631eb605eba6a11ccec6ceab021460d0bd35feb676ec6dbba5d4ad6c9f4d683ea541035bc80fa",
+		"f15ae71ffed50a8fcc4996b0",
+		"f535d60e8b75ac7e526041eed86eb4d65ae7e315eff15dba6c0133acc2a6a4bf",
+		"01ba61691ebb3c66d2f94c1b1c597ecd7b5ff7d2a30be405",
+		"d79e7c3893df5a5879c2f0a3f7ca619f08e4540f3ac7db35790b4211b9d47ae735adadf35fd47252a4763e3fd2b2cd8157f6ea7986108a53437962670a97d68ee281",
+	},
+	{
+		"8c014655b97f6da76b0b168b565fd62de874c164fd7e227346a0ec22c908bed1e2a0b429620e6f3a68dd518f13a2c0250608a1cb08a7c3",
+		"10a7eff999029c5040c1b3bd",
+		"bf11af23e88c350a443493f6fa0eb34f234f4daa2676e26f0701bce5642d13f4",
+		"f14c97392afd2e32e2c625910ca029f9b6e81676c79cc42f",
+		"78d5226f372d5d60681dbfc749d12df74249f196b0cbf14fa65a3a59dc65ae458455ec39baa1df3397afe752bb06f6f13bf03c99abda7a95c1d0b73fd92d5f888a5f6f889a9aea",
+	},
+	{
+		"66234d7a5b71eef134d60eccf7d5096ee879a33983d6f7a575e3a5e3a4022edccffe7865dde20b5b0a37252e31cb9a3650c63e35b057a1bc200a5b5b",
+		"ccc2406f997bcae737ddd0f5",
+		"d009eeb5b9b029577b14d200b7687b655eedb7d74add488f092681787999d66d",
+		"99319712626b400f9458dbb7a9abc9f5810f25b47fc90b39",
+		"543a2bbf52fd999027ae7c297353f3ce986f810bc2382583d0a81fda5939e4c87b6e8d262790cd614d6f753d8035b32adf43acc7f6d4c2c44289538928564b6587c2fcb99de1d8e34ffff323",
+	},
 }

+ 0 - 199
psiphon/common/crypto/chacha20poly1305/internal/chacha20/chacha_generic.go

@@ -1,199 +0,0 @@
-// Copyright 2016 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Package ChaCha20 implements the core ChaCha20 function as specified in https://tools.ietf.org/html/rfc7539#section-2.3.
-package chacha20
-
-import "encoding/binary"
-
-const rounds = 20
-
-// core applies the ChaCha20 core function to 16-byte input in, 32-byte key k,
-// and 16-byte constant c, and puts the result into 64-byte array out.
-func core(out *[64]byte, in *[16]byte, k *[32]byte) {
-	j0 := uint32(0x61707865)
-	j1 := uint32(0x3320646e)
-	j2 := uint32(0x79622d32)
-	j3 := uint32(0x6b206574)
-	j4 := binary.LittleEndian.Uint32(k[0:4])
-	j5 := binary.LittleEndian.Uint32(k[4:8])
-	j6 := binary.LittleEndian.Uint32(k[8:12])
-	j7 := binary.LittleEndian.Uint32(k[12:16])
-	j8 := binary.LittleEndian.Uint32(k[16:20])
-	j9 := binary.LittleEndian.Uint32(k[20:24])
-	j10 := binary.LittleEndian.Uint32(k[24:28])
-	j11 := binary.LittleEndian.Uint32(k[28:32])
-	j12 := binary.LittleEndian.Uint32(in[0:4])
-	j13 := binary.LittleEndian.Uint32(in[4:8])
-	j14 := binary.LittleEndian.Uint32(in[8:12])
-	j15 := binary.LittleEndian.Uint32(in[12:16])
-
-	x0, x1, x2, x3, x4, x5, x6, x7 := j0, j1, j2, j3, j4, j5, j6, j7
-	x8, x9, x10, x11, x12, x13, x14, x15 := j8, j9, j10, j11, j12, j13, j14, j15
-
-	for i := 0; i < rounds; i += 2 {
-		x0 += x4
-		x12 ^= x0
-		x12 = (x12 << 16) | (x12 >> (16))
-		x8 += x12
-		x4 ^= x8
-		x4 = (x4 << 12) | (x4 >> (20))
-		x0 += x4
-		x12 ^= x0
-		x12 = (x12 << 8) | (x12 >> (24))
-		x8 += x12
-		x4 ^= x8
-		x4 = (x4 << 7) | (x4 >> (25))
-		x1 += x5
-		x13 ^= x1
-		x13 = (x13 << 16) | (x13 >> 16)
-		x9 += x13
-		x5 ^= x9
-		x5 = (x5 << 12) | (x5 >> 20)
-		x1 += x5
-		x13 ^= x1
-		x13 = (x13 << 8) | (x13 >> 24)
-		x9 += x13
-		x5 ^= x9
-		x5 = (x5 << 7) | (x5 >> 25)
-		x2 += x6
-		x14 ^= x2
-		x14 = (x14 << 16) | (x14 >> 16)
-		x10 += x14
-		x6 ^= x10
-		x6 = (x6 << 12) | (x6 >> 20)
-		x2 += x6
-		x14 ^= x2
-		x14 = (x14 << 8) | (x14 >> 24)
-		x10 += x14
-		x6 ^= x10
-		x6 = (x6 << 7) | (x6 >> 25)
-		x3 += x7
-		x15 ^= x3
-		x15 = (x15 << 16) | (x15 >> 16)
-		x11 += x15
-		x7 ^= x11
-		x7 = (x7 << 12) | (x7 >> 20)
-		x3 += x7
-		x15 ^= x3
-		x15 = (x15 << 8) | (x15 >> 24)
-		x11 += x15
-		x7 ^= x11
-		x7 = (x7 << 7) | (x7 >> 25)
-		x0 += x5
-		x15 ^= x0
-		x15 = (x15 << 16) | (x15 >> 16)
-		x10 += x15
-		x5 ^= x10
-		x5 = (x5 << 12) | (x5 >> 20)
-		x0 += x5
-		x15 ^= x0
-		x15 = (x15 << 8) | (x15 >> 24)
-		x10 += x15
-		x5 ^= x10
-		x5 = (x5 << 7) | (x5 >> 25)
-		x1 += x6
-		x12 ^= x1
-		x12 = (x12 << 16) | (x12 >> 16)
-		x11 += x12
-		x6 ^= x11
-		x6 = (x6 << 12) | (x6 >> 20)
-		x1 += x6
-		x12 ^= x1
-		x12 = (x12 << 8) | (x12 >> 24)
-		x11 += x12
-		x6 ^= x11
-		x6 = (x6 << 7) | (x6 >> 25)
-		x2 += x7
-		x13 ^= x2
-		x13 = (x13 << 16) | (x13 >> 16)
-		x8 += x13
-		x7 ^= x8
-		x7 = (x7 << 12) | (x7 >> 20)
-		x2 += x7
-		x13 ^= x2
-		x13 = (x13 << 8) | (x13 >> 24)
-		x8 += x13
-		x7 ^= x8
-		x7 = (x7 << 7) | (x7 >> 25)
-		x3 += x4
-		x14 ^= x3
-		x14 = (x14 << 16) | (x14 >> 16)
-		x9 += x14
-		x4 ^= x9
-		x4 = (x4 << 12) | (x4 >> 20)
-		x3 += x4
-		x14 ^= x3
-		x14 = (x14 << 8) | (x14 >> 24)
-		x9 += x14
-		x4 ^= x9
-		x4 = (x4 << 7) | (x4 >> 25)
-	}
-
-	x0 += j0
-	x1 += j1
-	x2 += j2
-	x3 += j3
-	x4 += j4
-	x5 += j5
-	x6 += j6
-	x7 += j7
-	x8 += j8
-	x9 += j9
-	x10 += j10
-	x11 += j11
-	x12 += j12
-	x13 += j13
-	x14 += j14
-	x15 += j15
-
-	binary.LittleEndian.PutUint32(out[0:4], x0)
-	binary.LittleEndian.PutUint32(out[4:8], x1)
-	binary.LittleEndian.PutUint32(out[8:12], x2)
-	binary.LittleEndian.PutUint32(out[12:16], x3)
-	binary.LittleEndian.PutUint32(out[16:20], x4)
-	binary.LittleEndian.PutUint32(out[20:24], x5)
-	binary.LittleEndian.PutUint32(out[24:28], x6)
-	binary.LittleEndian.PutUint32(out[28:32], x7)
-	binary.LittleEndian.PutUint32(out[32:36], x8)
-	binary.LittleEndian.PutUint32(out[36:40], x9)
-	binary.LittleEndian.PutUint32(out[40:44], x10)
-	binary.LittleEndian.PutUint32(out[44:48], x11)
-	binary.LittleEndian.PutUint32(out[48:52], x12)
-	binary.LittleEndian.PutUint32(out[52:56], x13)
-	binary.LittleEndian.PutUint32(out[56:60], x14)
-	binary.LittleEndian.PutUint32(out[60:64], x15)
-}
-
-// XORKeyStream crypts bytes from in to out using the given key and counters.
-// In and out may be the same slice but otherwise should not overlap. Counter
-// contains the raw ChaCha20 counter bytes (i.e. block counter followed by
-// nonce).
-func XORKeyStream(out, in []byte, counter *[16]byte, key *[32]byte) {
-	var block [64]byte
-	var counterCopy [16]byte
-	copy(counterCopy[:], counter[:])
-
-	for len(in) >= 64 {
-		core(&block, &counterCopy, key)
-		for i, x := range block {
-			out[i] = in[i] ^ x
-		}
-		u := uint32(1)
-		for i := 0; i < 4; i++ {
-			u += uint32(counterCopy[i])
-			counterCopy[i] = byte(u)
-			u >>= 8
-		}
-		in = in[64:]
-		out = out[64:]
-	}
-
-	if len(in) > 0 {
-		core(&block, &counterCopy, key)
-		for i, v := range in {
-			out[i] = v ^ block[i]
-		}
-	}
-}

+ 0 - 33
psiphon/common/crypto/chacha20poly1305/internal/chacha20/chacha_test.go

@@ -1,33 +0,0 @@
-// Copyright 2016 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package chacha20
-
-import (
-	"encoding/hex"
-	"testing"
-)
-
-func TestCore(t *testing.T) {
-	// This is just a smoke test that checks the example from
-	// https://tools.ietf.org/html/rfc7539#section-2.3.2. The
-	// chacha20poly1305 package contains much more extensive tests of this
-	// code.
-	var key [32]byte
-	for i := range key {
-		key[i] = byte(i)
-	}
-
-	var input [16]byte
-	input[0] = 1
-	input[7] = 9
-	input[11] = 0x4a
-
-	var out [64]byte
-	XORKeyStream(out[:], out[:], &input, &key)
-	const expected = "10f1e7e4d13b5915500fdd1fa32071c4c7d1f4c733c068030422aa9ac3d46c4ed2826446079faa0914c2d705d98b02a2b5129cd1de164eb9cbd083e8a2503c4e"
-	if result := hex.EncodeToString(out[:]); result != expected {
-		t.Errorf("wanted %x but got %x", expected, result)
-	}
-}

+ 104 - 0
psiphon/common/crypto/chacha20poly1305/xchacha20poly1305.go

@@ -0,0 +1,104 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package chacha20poly1305
+
+import (
+	"crypto/cipher"
+	"encoding/binary"
+	"errors"
+
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/internal/chacha20"
+)
+
+type xchacha20poly1305 struct {
+	key [8]uint32
+}
+
+// NewX returns a XChaCha20-Poly1305 AEAD that uses the given 256-bit key.
+//
+// XChaCha20-Poly1305 is a ChaCha20-Poly1305 variant that takes a longer nonce,
+// suitable to be generated randomly without risk of collisions. It should be
+// preferred when nonce uniqueness cannot be trivially ensured, or whenever
+// nonces are randomly generated.
+func NewX(key []byte) (cipher.AEAD, error) {
+	if len(key) != KeySize {
+		return nil, errors.New("chacha20poly1305: bad key length")
+	}
+	ret := new(xchacha20poly1305)
+	ret.key[0] = binary.LittleEndian.Uint32(key[0:4])
+	ret.key[1] = binary.LittleEndian.Uint32(key[4:8])
+	ret.key[2] = binary.LittleEndian.Uint32(key[8:12])
+	ret.key[3] = binary.LittleEndian.Uint32(key[12:16])
+	ret.key[4] = binary.LittleEndian.Uint32(key[16:20])
+	ret.key[5] = binary.LittleEndian.Uint32(key[20:24])
+	ret.key[6] = binary.LittleEndian.Uint32(key[24:28])
+	ret.key[7] = binary.LittleEndian.Uint32(key[28:32])
+	return ret, nil
+}
+
+func (*xchacha20poly1305) NonceSize() int {
+	return NonceSizeX
+}
+
+func (*xchacha20poly1305) Overhead() int {
+	return 16
+}
+
+func (x *xchacha20poly1305) Seal(dst, nonce, plaintext, additionalData []byte) []byte {
+	if len(nonce) != NonceSizeX {
+		panic("chacha20poly1305: bad nonce length passed to Seal")
+	}
+
+	// XChaCha20-Poly1305 technically supports a 64-bit counter, so there is no
+	// size limit. However, since we reuse the ChaCha20-Poly1305 implementation,
+	// the second half of the counter is not available. This is unlikely to be
+	// an issue because the cipher.AEAD API requires the entire message to be in
+	// memory, and the counter overflows at 256 GB.
+	if uint64(len(plaintext)) > (1<<38)-64 {
+		panic("chacha20poly1305: plaintext too large")
+	}
+
+	hNonce := [4]uint32{
+		binary.LittleEndian.Uint32(nonce[0:4]),
+		binary.LittleEndian.Uint32(nonce[4:8]),
+		binary.LittleEndian.Uint32(nonce[8:12]),
+		binary.LittleEndian.Uint32(nonce[12:16]),
+	}
+	c := &chacha20poly1305{
+		key: chacha20.HChaCha20(&x.key, &hNonce),
+	}
+	// The first 4 bytes of the final nonce are unused counter space.
+	cNonce := make([]byte, NonceSize)
+	copy(cNonce[4:12], nonce[16:24])
+
+	return c.seal(dst, cNonce[:], plaintext, additionalData)
+}
+
+func (x *xchacha20poly1305) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
+	if len(nonce) != NonceSizeX {
+		panic("chacha20poly1305: bad nonce length passed to Open")
+	}
+	if len(ciphertext) < 16 {
+		return nil, errOpen
+	}
+	if uint64(len(ciphertext)) > (1<<38)-48 {
+		panic("chacha20poly1305: ciphertext too large")
+	}
+
+	hNonce := [4]uint32{
+		binary.LittleEndian.Uint32(nonce[0:4]),
+		binary.LittleEndian.Uint32(nonce[4:8]),
+		binary.LittleEndian.Uint32(nonce[8:12]),
+		binary.LittleEndian.Uint32(nonce[12:16]),
+	}
+	c := &chacha20poly1305{
+		key: chacha20.HChaCha20(&x.key, &hNonce),
+	}
+	// The first 4 bytes of the final nonce are unused counter space.
+	cNonce := make([]byte, NonceSize)
+	copy(cNonce[4:12], nonce[16:24])
+
+	return c.open(dst, cNonce[:], ciphertext, additionalData)
+}

+ 222 - 75
psiphon/common/crypto/cryptobyte/asn1.go

@@ -5,40 +5,36 @@
 package cryptobyte
 
 import (
-	"encoding/asn1"
+	encoding_asn1 "encoding/asn1"
 	"fmt"
 	"math/big"
 	"reflect"
 	"time"
+
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/cryptobyte/asn1"
 )
 
 // This file contains ASN.1-related methods for String and Builder.
 
-// Tag represents an ASN.1 tag number and class (together also referred to as
-// identifier octets). Methods in this package only support the low-tag-number
-// form, i.e. a single identifier octet with bits 7-8 encoding the class and
-// bits 1-6 encoding the tag number.
-type Tag uint8
-
-// Contructed returns t with the context-specific class bit set.
-func (t Tag) ContextSpecific() Tag { return t | 0x80 }
-
-// Contructed returns t with the constructed class bit set.
-func (t Tag) Constructed() Tag { return t | 0x20 }
-
 // Builder
 
 // AddASN1Int64 appends a DER-encoded ASN.1 INTEGER.
 func (b *Builder) AddASN1Int64(v int64) {
-	b.addASN1Signed(asn1.TagInteger, v)
+	b.addASN1Signed(asn1.INTEGER, v)
+}
+
+// AddASN1Int64WithTag appends a DER-encoded ASN.1 INTEGER with the
+// given tag.
+func (b *Builder) AddASN1Int64WithTag(v int64, tag asn1.Tag) {
+	b.addASN1Signed(tag, v)
 }
 
 // AddASN1Enum appends a DER-encoded ASN.1 ENUMERATION.
 func (b *Builder) AddASN1Enum(v int64) {
-	b.addASN1Signed(asn1.TagEnum, v)
+	b.addASN1Signed(asn1.ENUM, v)
 }
 
-func (b *Builder) addASN1Signed(tag Tag, v int64) {
+func (b *Builder) addASN1Signed(tag asn1.Tag, v int64) {
 	b.AddASN1(tag, func(c *Builder) {
 		length := 1
 		for i := v; i >= 0x80 || i < -0x80; i >>= 8 {
@@ -54,7 +50,7 @@ func (b *Builder) addASN1Signed(tag Tag, v int64) {
 
 // AddASN1Uint64 appends a DER-encoded ASN.1 INTEGER.
 func (b *Builder) AddASN1Uint64(v uint64) {
-	b.AddASN1(asn1.TagInteger, func(c *Builder) {
+	b.AddASN1(asn1.INTEGER, func(c *Builder) {
 		length := 1
 		for i := v; i >= 0x80; i >>= 8 {
 			length++
@@ -73,7 +69,7 @@ func (b *Builder) AddASN1BigInt(n *big.Int) {
 		return
 	}
 
-	b.AddASN1(asn1.TagInteger, func(c *Builder) {
+	b.AddASN1(asn1.INTEGER, func(c *Builder) {
 		if n.Sign() < 0 {
 			// A negative number has to be converted to two's-complement form. So we
 			// invert and subtract 1. If the most-significant-bit isn't set then
@@ -103,7 +99,7 @@ func (b *Builder) AddASN1BigInt(n *big.Int) {
 
 // AddASN1OctetString appends a DER-encoded ASN.1 OCTET STRING.
 func (b *Builder) AddASN1OctetString(bytes []byte) {
-	b.AddASN1(asn1.TagOctetString, func(c *Builder) {
+	b.AddASN1(asn1.OCTET_STRING, func(c *Builder) {
 		c.AddBytes(bytes)
 	})
 }
@@ -116,27 +112,97 @@ func (b *Builder) AddASN1GeneralizedTime(t time.Time) {
 		b.err = fmt.Errorf("cryptobyte: cannot represent %v as a GeneralizedTime", t)
 		return
 	}
-	b.AddASN1(asn1.TagGeneralizedTime, func(c *Builder) {
+	b.AddASN1(asn1.GeneralizedTime, func(c *Builder) {
 		c.AddBytes([]byte(t.Format(generalizedTimeFormatStr)))
 	})
 }
 
-// AddASN1BitString appends a DER-encoded ASN.1 BIT STRING.
-func (b *Builder) AddASN1BitString(s asn1.BitString) {
-	// TODO(martinkr): Implement.
-	b.MarshalASN1(s)
+// AddASN1BitString appends a DER-encoded ASN.1 BIT STRING. This does not
+// support BIT STRINGs that are not a whole number of bytes.
+func (b *Builder) AddASN1BitString(data []byte) {
+	b.AddASN1(asn1.BIT_STRING, func(b *Builder) {
+		b.AddUint8(0)
+		b.AddBytes(data)
+	})
+}
+
+func (b *Builder) addBase128Int(n int64) {
+	var length int
+	if n == 0 {
+		length = 1
+	} else {
+		for i := n; i > 0; i >>= 7 {
+			length++
+		}
+	}
+
+	for i := length - 1; i >= 0; i-- {
+		o := byte(n >> uint(i*7))
+		o &= 0x7f
+		if i != 0 {
+			o |= 0x80
+		}
+
+		b.add(o)
+	}
+}
+
+func isValidOID(oid encoding_asn1.ObjectIdentifier) bool {
+	if len(oid) < 2 {
+		return false
+	}
+
+	if oid[0] > 2 || (oid[0] <= 1 && oid[1] >= 40) {
+		return false
+	}
+
+	for _, v := range oid {
+		if v < 0 {
+			return false
+		}
+	}
+
+	return true
+}
+
+func (b *Builder) AddASN1ObjectIdentifier(oid encoding_asn1.ObjectIdentifier) {
+	b.AddASN1(asn1.OBJECT_IDENTIFIER, func(b *Builder) {
+		if !isValidOID(oid) {
+			b.err = fmt.Errorf("cryptobyte: invalid OID: %v", oid)
+			return
+		}
+
+		b.addBase128Int(int64(oid[0])*40 + int64(oid[1]))
+		for _, v := range oid[2:] {
+			b.addBase128Int(int64(v))
+		}
+	})
+}
+
+func (b *Builder) AddASN1Boolean(v bool) {
+	b.AddASN1(asn1.BOOLEAN, func(b *Builder) {
+		if v {
+			b.AddUint8(0xff)
+		} else {
+			b.AddUint8(0)
+		}
+	})
 }
 
-// MarshalASN1 calls asn1.Marshal on its input and appends the result if
+func (b *Builder) AddASN1NULL() {
+	b.add(uint8(asn1.NULL), 0)
+}
+
+// MarshalASN1 calls encoding_asn1.Marshal on its input and appends the result if
 // successful or records an error if one occurred.
 func (b *Builder) MarshalASN1(v interface{}) {
 	// NOTE(martinkr): This is somewhat of a hack to allow propagation of
-	// asn1.Marshal errors into Builder.err. N.B. if you call MarshalASN1 with a
+	// encoding_asn1.Marshal errors into Builder.err. N.B. if you call MarshalASN1 with a
 	// value embedded into a struct, its tag information is lost.
 	if b.err != nil {
 		return
 	}
-	bytes, err := asn1.Marshal(v)
+	bytes, err := encoding_asn1.Marshal(v)
 	if err != nil {
 		b.err = err
 		return
@@ -148,7 +214,7 @@ func (b *Builder) MarshalASN1(v interface{}) {
 // Tags greater than 30 are not supported and result in an error (i.e.
 // low-tag-number form only). The child builder passed to the
 // BuilderContinuation can be used to build the content of the ASN.1 object.
-func (b *Builder) AddASN1(tag Tag, f BuilderContinuation) {
+func (b *Builder) AddASN1(tag asn1.Tag, f BuilderContinuation) {
 	if b.err != nil {
 		return
 	}
@@ -164,11 +230,32 @@ func (b *Builder) AddASN1(tag Tag, f BuilderContinuation) {
 
 // String
 
+// ReadASN1Boolean decodes an ASN.1 INTEGER and converts it to a boolean
+// representation into out and advances. It reports whether the read
+// was successful.
+func (s *String) ReadASN1Boolean(out *bool) bool {
+	var bytes String
+	if !s.ReadASN1(&bytes, asn1.INTEGER) || len(bytes) != 1 {
+		return false
+	}
+
+	switch bytes[0] {
+	case 0:
+		*out = false
+	case 0xff:
+		*out = true
+	default:
+		return false
+	}
+
+	return true
+}
+
 var bigIntType = reflect.TypeOf((*big.Int)(nil)).Elem()
 
 // ReadASN1Integer decodes an ASN.1 INTEGER into out and advances. If out does
-// not point to an integer or to a big.Int, it panics. It returns true on
-// success and false on error.
+// not point to an integer or to a big.Int, it panics. It reports whether the
+// read was successful.
 func (s *String) ReadASN1Integer(out interface{}) bool {
 	if reflect.TypeOf(out).Kind() != reflect.Ptr {
 		panic("out is not a pointer")
@@ -215,7 +302,7 @@ var bigOne = big.NewInt(1)
 
 func (s *String) readASN1BigInt(out *big.Int) bool {
 	var bytes String
-	if !s.ReadASN1(&bytes, asn1.TagInteger) || !checkASN1Integer(bytes) {
+	if !s.ReadASN1(&bytes, asn1.INTEGER) || !checkASN1Integer(bytes) {
 		return false
 	}
 	if bytes[0]&0x80 == 0x80 {
@@ -235,7 +322,7 @@ func (s *String) readASN1BigInt(out *big.Int) bool {
 
 func (s *String) readASN1Int64(out *int64) bool {
 	var bytes String
-	if !s.ReadASN1(&bytes, asn1.TagInteger) || !checkASN1Integer(bytes) || !asn1Signed(out, bytes) {
+	if !s.ReadASN1(&bytes, asn1.INTEGER) || !checkASN1Integer(bytes) || !asn1Signed(out, bytes) {
 		return false
 	}
 	return true
@@ -258,7 +345,7 @@ func asn1Signed(out *int64, n []byte) bool {
 
 func (s *String) readASN1Uint64(out *uint64) bool {
 	var bytes String
-	if !s.ReadASN1(&bytes, asn1.TagInteger) || !checkASN1Integer(bytes) || !asn1Unsigned(out, bytes) {
+	if !s.ReadASN1(&bytes, asn1.INTEGER) || !checkASN1Integer(bytes) || !asn1Unsigned(out, bytes) {
 		return false
 	}
 	return true
@@ -281,12 +368,20 @@ func asn1Unsigned(out *uint64, n []byte) bool {
 	return true
 }
 
-// ReadASN1Enum decodes an ASN.1 ENUMERATION into out and advances. It returns
-// true on success and false on error.
+// ReadASN1Int64WithTag decodes an ASN.1 INTEGER with the given tag into out
+// and advances. It reports whether the read was successful and resulted in a
+// value that can be represented in an int64.
+func (s *String) ReadASN1Int64WithTag(out *int64, tag asn1.Tag) bool {
+	var bytes String
+	return s.ReadASN1(&bytes, tag) && checkASN1Integer(bytes) && asn1Signed(out, bytes)
+}
+
+// ReadASN1Enum decodes an ASN.1 ENUMERATION into out and advances. It reports
+// whether the read was successful.
 func (s *String) ReadASN1Enum(out *int) bool {
 	var bytes String
 	var i int64
-	if !s.ReadASN1(&bytes, asn1.TagEnum) || !checkASN1Integer(bytes) || !asn1Signed(&i, bytes) {
+	if !s.ReadASN1(&bytes, asn1.ENUM) || !checkASN1Integer(bytes) || !asn1Signed(&i, bytes) {
 		return false
 	}
 	if int64(int(i)) != i {
@@ -314,10 +409,10 @@ func (s *String) readBase128Int(out *int) bool {
 }
 
 // ReadASN1ObjectIdentifier decodes an ASN.1 OBJECT IDENTIFIER into out and
-// advances. It returns true on success and false on error.
-func (s *String) ReadASN1ObjectIdentifier(out *asn1.ObjectIdentifier) bool {
+// advances. It reports whether the read was successful.
+func (s *String) ReadASN1ObjectIdentifier(out *encoding_asn1.ObjectIdentifier) bool {
 	var bytes String
-	if !s.ReadASN1(&bytes, asn1.TagOID) || len(bytes) == 0 {
+	if !s.ReadASN1(&bytes, asn1.OBJECT_IDENTIFIER) || len(bytes) == 0 {
 		return false
 	}
 
@@ -353,10 +448,10 @@ func (s *String) ReadASN1ObjectIdentifier(out *asn1.ObjectIdentifier) bool {
 }
 
 // ReadASN1GeneralizedTime decodes an ASN.1 GENERALIZEDTIME into out and
-// advances. It returns true on success and false on error.
+// advances. It reports whether the read was successful.
 func (s *String) ReadASN1GeneralizedTime(out *time.Time) bool {
 	var bytes String
-	if !s.ReadASN1(&bytes, asn1.TagGeneralizedTime) {
+	if !s.ReadASN1(&bytes, asn1.GeneralizedTime) {
 		return false
 	}
 	t := string(bytes)
@@ -371,11 +466,11 @@ func (s *String) ReadASN1GeneralizedTime(out *time.Time) bool {
 	return true
 }
 
-// ReadASN1BitString decodes an ASN.1 BIT STRING into out and advances. It
-// returns true on success and false on error.
-func (s *String) ReadASN1BitString(out *asn1.BitString) bool {
+// ReadASN1BitString decodes an ASN.1 BIT STRING into out and advances.
+// It reports whether the read was successful.
+func (s *String) ReadASN1BitString(out *encoding_asn1.BitString) bool {
 	var bytes String
-	if !s.ReadASN1(&bytes, asn1.TagBitString) || len(bytes) == 0 {
+	if !s.ReadASN1(&bytes, asn1.BIT_STRING) || len(bytes) == 0 {
 		return false
 	}
 
@@ -392,20 +487,37 @@ func (s *String) ReadASN1BitString(out *asn1.BitString) bool {
 	return true
 }
 
+// ReadASN1BitString decodes an ASN.1 BIT STRING into out and advances. It is
+// an error if the BIT STRING is not a whole number of bytes. It reports
+// whether the read was successful.
+func (s *String) ReadASN1BitStringAsBytes(out *[]byte) bool {
+	var bytes String
+	if !s.ReadASN1(&bytes, asn1.BIT_STRING) || len(bytes) == 0 {
+		return false
+	}
+
+	paddingBits := uint8(bytes[0])
+	if paddingBits != 0 {
+		return false
+	}
+	*out = bytes[1:]
+	return true
+}
+
 // ReadASN1Bytes reads the contents of a DER-encoded ASN.1 element (not including
 // tag and length bytes) into out, and advances. The element must match the
-// given tag. It returns true on success and false on error.
-func (s *String) ReadASN1Bytes(out *[]byte, tag Tag) bool {
+// given tag. It reports whether the read was successful.
+func (s *String) ReadASN1Bytes(out *[]byte, tag asn1.Tag) bool {
 	return s.ReadASN1((*String)(out), tag)
 }
 
 // ReadASN1 reads the contents of a DER-encoded ASN.1 element (not including
 // tag and length bytes) into out, and advances. The element must match the
-// given tag. It returns true on success and false on error.
+// given tag. It reports whether the read was successful.
 //
 // Tags greater than 30 are not supported (i.e. low-tag-number format only).
-func (s *String) ReadASN1(out *String, tag Tag) bool {
-	var t Tag
+func (s *String) ReadASN1(out *String, tag asn1.Tag) bool {
+	var t asn1.Tag
 	if !s.ReadAnyASN1(out, &t) || t != tag {
 		return false
 	}
@@ -414,11 +526,11 @@ func (s *String) ReadASN1(out *String, tag Tag) bool {
 
 // ReadASN1Element reads the contents of a DER-encoded ASN.1 element (including
 // tag and length bytes) into out, and advances. The element must match the
-// given tag. It returns true on success and false on error.
+// given tag. It reports whether the read was successful.
 //
 // Tags greater than 30 are not supported (i.e. low-tag-number format only).
-func (s *String) ReadASN1Element(out *String, tag Tag) bool {
-	var t Tag
+func (s *String) ReadASN1Element(out *String, tag asn1.Tag) bool {
+	var t asn1.Tag
 	if !s.ReadAnyASN1Element(out, &t) || t != tag {
 		return false
 	}
@@ -426,37 +538,44 @@ func (s *String) ReadASN1Element(out *String, tag Tag) bool {
 }
 
 // ReadAnyASN1 reads the contents of a DER-encoded ASN.1 element (not including
-// tag and length bytes) into out, sets outTag to its tag, and advances. It
-// returns true on success and false on error.
+// tag and length bytes) into out, sets outTag to its tag, and advances.
+// It reports whether the read was successful.
 //
 // Tags greater than 30 are not supported (i.e. low-tag-number format only).
-func (s *String) ReadAnyASN1(out *String, outTag *Tag) bool {
+func (s *String) ReadAnyASN1(out *String, outTag *asn1.Tag) bool {
 	return s.readASN1(out, outTag, true /* skip header */)
 }
 
 // ReadAnyASN1Element reads the contents of a DER-encoded ASN.1 element
 // (including tag and length bytes) into out, sets outTag to is tag, and
-// advances. It returns true on success and false on error.
+// advances. It reports whether the read was successful.
 //
 // Tags greater than 30 are not supported (i.e. low-tag-number format only).
-func (s *String) ReadAnyASN1Element(out *String, outTag *Tag) bool {
+func (s *String) ReadAnyASN1Element(out *String, outTag *asn1.Tag) bool {
 	return s.readASN1(out, outTag, false /* include header */)
 }
 
-// PeekASN1Tag returns true if the next ASN.1 value on the string starts with
+// PeekASN1Tag reports whether the next ASN.1 value on the string starts with
 // the given tag.
-func (s String) PeekASN1Tag(tag Tag) bool {
+func (s String) PeekASN1Tag(tag asn1.Tag) bool {
 	if len(s) == 0 {
 		return false
 	}
-	return Tag(s[0]) == tag
+	return asn1.Tag(s[0]) == tag
+}
+
+// SkipASN1 reads and discards an ASN.1 element with the given tag. It
+// reports whether the operation was successful.
+func (s *String) SkipASN1(tag asn1.Tag) bool {
+	var unused String
+	return s.ReadASN1(&unused, tag)
 }
 
-// ReadOptionalASN1 attempts to read the contents of a DER-encoded ASN.Element
-// (not including tag and length bytes) tagged with the given tag into out. It
-// stores whether an element with the tag was found in outPresent, unless
-// outPresent is nil. It returns true on success and false on error.
-func (s *String) ReadOptionalASN1(out *String, outPresent *bool, tag Tag) bool {
+// ReadOptionalASN1 attempts to read the contents of a DER-encoded ASN.1
+// element (not including tag and length bytes) tagged with the given tag into
+// out. It stores whether an element with the tag was found in outPresent,
+// unless outPresent is nil. It reports whether the read was successful.
+func (s *String) ReadOptionalASN1(out *String, outPresent *bool, tag asn1.Tag) bool {
 	present := s.PeekASN1Tag(tag)
 	if outPresent != nil {
 		*outPresent = present
@@ -467,12 +586,22 @@ func (s *String) ReadOptionalASN1(out *String, outPresent *bool, tag Tag) bool {
 	return true
 }
 
+// SkipOptionalASN1 advances s over an ASN.1 element with the given tag, or
+// else leaves s unchanged. It reports whether the operation was successful.
+func (s *String) SkipOptionalASN1(tag asn1.Tag) bool {
+	if !s.PeekASN1Tag(tag) {
+		return true
+	}
+	var unused String
+	return s.ReadASN1(&unused, tag)
+}
+
 // ReadOptionalASN1Integer attempts to read an optional ASN.1 INTEGER
 // explicitly tagged with tag into out and advances. If no element with a
 // matching tag is present, it writes defaultValue into out instead. If out
-// does not point to an integer or to a big.Int, it panics. It returns true on
-// success and false on error.
-func (s *String) ReadOptionalASN1Integer(out interface{}, tag Tag, defaultValue interface{}) bool {
+// does not point to an integer or to a big.Int, it panics. It reports
+// whether the read was successful.
+func (s *String) ReadOptionalASN1Integer(out interface{}, tag asn1.Tag, defaultValue interface{}) bool {
 	if reflect.TypeOf(out).Kind() != reflect.Ptr {
 		panic("out is not a pointer")
 	}
@@ -508,9 +637,9 @@ func (s *String) ReadOptionalASN1Integer(out interface{}, tag Tag, defaultValue
 
 // ReadOptionalASN1OctetString attempts to read an optional ASN.1 OCTET STRING
 // explicitly tagged with tag into out and advances. If no element with a
-// matching tag is present, it writes defaultValue into out instead. It returns
-// true on success and false on error.
-func (s *String) ReadOptionalASN1OctetString(out *[]byte, outPresent *bool, tag Tag) bool {
+// matching tag is present, it sets "out" to nil instead. It reports
+// whether the read was successful.
+func (s *String) ReadOptionalASN1OctetString(out *[]byte, outPresent *bool, tag asn1.Tag) bool {
 	var present bool
 	var child String
 	if !s.ReadOptionalASN1(&child, &present, tag) {
@@ -521,7 +650,7 @@ func (s *String) ReadOptionalASN1OctetString(out *[]byte, outPresent *bool, tag
 	}
 	if present {
 		var oct String
-		if !child.ReadASN1(&oct, asn1.TagOctetString) || !child.Empty() {
+		if !child.ReadASN1(&oct, asn1.OCTET_STRING) || !child.Empty() {
 			return false
 		}
 		*out = oct
@@ -531,7 +660,25 @@ func (s *String) ReadOptionalASN1OctetString(out *[]byte, outPresent *bool, tag
 	return true
 }
 
-func (s *String) readASN1(out *String, outTag *Tag, skipHeader bool) bool {
+// ReadOptionalASN1Boolean sets *out to the value of the next ASN.1 BOOLEAN or,
+// if the next bytes are not an ASN.1 BOOLEAN, to the value of defaultValue.
+// It reports whether the operation was successful.
+func (s *String) ReadOptionalASN1Boolean(out *bool, defaultValue bool) bool {
+	var present bool
+	var child String
+	if !s.ReadOptionalASN1(&child, &present, asn1.BOOLEAN) {
+		return false
+	}
+
+	if !present {
+		*out = defaultValue
+		return true
+	}
+
+	return s.ReadASN1Boolean(out)
+}
+
+func (s *String) readASN1(out *String, outTag *asn1.Tag, skipHeader bool) bool {
 	if len(*s) < 2 {
 		return false
 	}
@@ -547,7 +694,7 @@ func (s *String) readASN1(out *String, outTag *Tag, skipHeader bool) bool {
 	}
 
 	if outTag != nil {
-		*outTag = Tag(tag)
+		*outTag = asn1.Tag(tag)
 	}
 
 	// ITU-T X.690 section 8.1.3

+ 46 - 0
psiphon/common/crypto/cryptobyte/asn1/asn1.go

@@ -0,0 +1,46 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package asn1 contains supporting types for parsing and building ASN.1
+// messages with the cryptobyte package.
+package asn1 // import "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/cryptobyte/asn1"
+
+// Tag represents an ASN.1 identifier octet, consisting of a tag number
+// (indicating a type) and class (such as context-specific or constructed).
+//
+// Methods in the cryptobyte package only support the low-tag-number form, i.e.
+// a single identifier octet with bits 7-8 encoding the class and bits 1-6
+// encoding the tag number.
+type Tag uint8
+
+const (
+	classConstructed     = 0x20
+	classContextSpecific = 0x80
+)
+
+// Constructed returns t with the constructed class bit set.
+func (t Tag) Constructed() Tag { return t | classConstructed }
+
+// ContextSpecific returns t with the context-specific class bit set.
+func (t Tag) ContextSpecific() Tag { return t | classContextSpecific }
+
+// The following is a list of standard tag and class combinations.
+const (
+	BOOLEAN           = Tag(1)
+	INTEGER           = Tag(2)
+	BIT_STRING        = Tag(3)
+	OCTET_STRING      = Tag(4)
+	NULL              = Tag(5)
+	OBJECT_IDENTIFIER = Tag(6)
+	ENUM              = Tag(10)
+	UTF8String        = Tag(12)
+	SEQUENCE          = Tag(16 | classConstructed)
+	SET               = Tag(17 | classConstructed)
+	PrintableString   = Tag(19)
+	T61String         = Tag(20)
+	IA5String         = Tag(22)
+	UTCTime           = Tag(23)
+	GeneralizedTime   = Tag(24)
+	GeneralString     = Tag(27)
+)

+ 63 - 15
psiphon/common/crypto/cryptobyte/asn1_test.go

@@ -6,17 +6,19 @@ package cryptobyte
 
 import (
 	"bytes"
-	"encoding/asn1"
+	encoding_asn1 "encoding/asn1"
 	"math/big"
 	"reflect"
 	"testing"
 	"time"
+
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/cryptobyte/asn1"
 )
 
 type readASN1Test struct {
 	name string
 	in   []byte
-	tag  Tag
+	tag  asn1.Tag
 	ok   bool
 	out  interface{}
 }
@@ -147,6 +149,39 @@ func TestReadASN1IntegerSigned(t *testing.T) {
 			}
 		}
 	})
+
+	// Repeat with the implicit-tagging functions
+	t.Run("WithTag", func(t *testing.T) {
+		for i, test := range testData64 {
+			tag := asn1.Tag((i * 3) % 32).ContextSpecific()
+
+			testData := make([]byte, len(test.in))
+			copy(testData, test.in)
+
+			// Alter the tag of the test case.
+			testData[0] = uint8(tag)
+
+			in := String(testData)
+			var out int64
+			ok := in.ReadASN1Int64WithTag(&out, tag)
+			if !ok || out != test.out {
+				t.Errorf("#%d: in.ReadASN1Int64WithTag() = %v, want true; out = %d, want %d", i, ok, out, test.out)
+			}
+
+			var b Builder
+			b.AddASN1Int64WithTag(test.out, tag)
+			result, err := b.Bytes()
+
+			if err != nil {
+				t.Errorf("#%d: AddASN1Int64WithTag failed: %s", i, err)
+				continue
+			}
+
+			if !bytes.Equal(result, testData) {
+				t.Errorf("#%d: AddASN1Int64WithTag: got %x, want %x", i, result, testData)
+			}
+		}
+	})
 }
 
 func TestReadASN1IntegerUnsigned(t *testing.T) {
@@ -194,7 +229,7 @@ func TestReadASN1IntegerInvalid(t *testing.T) {
 	}
 }
 
-func TestReadASN1ObjectIdentifier(t *testing.T) {
+func TestASN1ObjectIdentifier(t *testing.T) {
 	testData := []struct {
 		in  []byte
 		ok  bool
@@ -212,10 +247,23 @@ func TestReadASN1ObjectIdentifier(t *testing.T) {
 
 	for i, test := range testData {
 		in := String(test.in)
-		var out asn1.ObjectIdentifier
+		var out encoding_asn1.ObjectIdentifier
 		ok := in.ReadASN1ObjectIdentifier(&out)
 		if ok != test.ok || ok && !out.Equal(test.out) {
 			t.Errorf("#%d: in.ReadASN1ObjectIdentifier() = %v, want %v; out = %v, want %v", i, ok, test.ok, out, test.out)
+			continue
+		}
+
+		var b Builder
+		b.AddASN1ObjectIdentifier(out)
+		result, err := b.Bytes()
+		if builderOk := err == nil; test.ok != builderOk {
+			t.Errorf("#%d: error from Builder.Bytes: %s", i, err)
+			continue
+		}
+		if test.ok && !bytes.Equal(result, test.in) {
+			t.Errorf("#%d: reserialisation didn't match, got %x, want %x", i, result, test.in)
+			continue
 		}
 	}
 }
@@ -250,7 +298,7 @@ func TestReadASN1GeneralizedTime(t *testing.T) {
 		{"201001020304-10Z", false, time.Time{}},
 	}
 	for i, test := range testData {
-		in := String(append([]byte{asn1.TagGeneralizedTime, byte(len(test.in))}, test.in...))
+		in := String(append([]byte{byte(asn1.GeneralizedTime), byte(len(test.in))}, test.in...))
 		var out time.Time
 		ok := in.ReadASN1GeneralizedTime(&out)
 		if ok != test.ok || ok && !reflect.DeepEqual(out, test.out) {
@@ -263,20 +311,20 @@ func TestReadASN1BitString(t *testing.T) {
 	testData := []struct {
 		in  []byte
 		ok  bool
-		out asn1.BitString
+		out encoding_asn1.BitString
 	}{
-		{[]byte{}, false, asn1.BitString{}},
-		{[]byte{0x00}, true, asn1.BitString{}},
-		{[]byte{0x07, 0x00}, true, asn1.BitString{Bytes: []byte{0}, BitLength: 1}},
-		{[]byte{0x07, 0x01}, false, asn1.BitString{}},
-		{[]byte{0x07, 0x40}, false, asn1.BitString{}},
-		{[]byte{0x08, 0x00}, false, asn1.BitString{}},
-		{[]byte{0xff}, false, asn1.BitString{}},
-		{[]byte{0xfe, 0x00}, false, asn1.BitString{}},
+		{[]byte{}, false, encoding_asn1.BitString{}},
+		{[]byte{0x00}, true, encoding_asn1.BitString{}},
+		{[]byte{0x07, 0x00}, true, encoding_asn1.BitString{Bytes: []byte{0}, BitLength: 1}},
+		{[]byte{0x07, 0x01}, false, encoding_asn1.BitString{}},
+		{[]byte{0x07, 0x40}, false, encoding_asn1.BitString{}},
+		{[]byte{0x08, 0x00}, false, encoding_asn1.BitString{}},
+		{[]byte{0xff}, false, encoding_asn1.BitString{}},
+		{[]byte{0xfe, 0x00}, false, encoding_asn1.BitString{}},
 	}
 	for i, test := range testData {
 		in := String(append([]byte{3, byte(len(test.in))}, test.in...))
-		var out asn1.BitString
+		var out encoding_asn1.BitString
 		ok := in.ReadASN1BitString(&out)
 		if ok != test.ok || ok && (!bytes.Equal(out.Bytes, test.out.Bytes) || out.BitLength != test.out.BitLength) {
 			t.Errorf("#%d: in.ReadASN1BitString() = %v, want %v; out = %v, want %v", i, ok, test.ok, out, test.out)

+ 69 - 15
psiphon/common/crypto/cryptobyte/builder.go

@@ -10,15 +10,25 @@ import (
 )
 
 // A Builder builds byte strings from fixed-length and length-prefixed values.
+// Builders either allocate space as needed, or are ‘fixed’, which means that
+// they write into a given buffer and produce an error if it's exhausted.
+//
 // The zero value is a usable Builder that allocates space as needed.
+//
+// Simple values are marshaled and appended to a Builder using methods on the
+// Builder. Length-prefixed values are marshaled by providing a
+// BuilderContinuation, which is a function that writes the inner contents of
+// the value to a given Builder. See the documentation for BuilderContinuation
+// for details.
 type Builder struct {
-	err           error
-	result        []byte
-	fixedSize     bool
-	child         *Builder
-	offset        int
-	pendingLenLen int
-	pendingIsASN1 bool
+	err            error
+	result         []byte
+	fixedSize      bool
+	child          *Builder
+	offset         int
+	pendingLenLen  int
+	pendingIsASN1  bool
+	inContinuation *bool
 }
 
 // NewBuilder creates a Builder that appends its output to the given buffer.
@@ -86,9 +96,9 @@ func (b *Builder) AddBytes(v []byte) {
 
 // BuilderContinuation is continuation-passing interface for building
 // length-prefixed byte sequences. Builder methods for length-prefixed
-// sequences (AddUint8LengthPrefixed etc.) will invoke the BuilderContinuation
+// sequences (AddUint8LengthPrefixed etc) will invoke the BuilderContinuation
 // supplied to them. The child builder passed to the continuation can be used
-// to build the content of the length-prefixed sequence. Example:
+// to build the content of the length-prefixed sequence. For example:
 //
 //   parent := cryptobyte.NewBuilder()
 //   parent.AddUint8LengthPrefixed(func (child *Builder) {
@@ -102,8 +112,19 @@ func (b *Builder) AddBytes(v []byte) {
 // length prefix. After the continuation returns, the child must be considered
 // invalid, i.e. users must not store any copies or references of the child
 // that outlive the continuation.
+//
+// If the continuation panics with a value of type BuildError then the inner
+// error will be returned as the error from Bytes. If the child panics
+// otherwise then Bytes will repanic with the same value.
 type BuilderContinuation func(child *Builder)
 
+// BuildError wraps an error. If a BuilderContinuation panics with this value,
+// the panic will be recovered and the inner error will be returned from
+// Builder.Bytes.
+type BuildError struct {
+	Err error
+}
+
 // AddUint8LengthPrefixed adds a 8-bit length-prefixed byte sequence.
 func (b *Builder) AddUint8LengthPrefixed(f BuilderContinuation) {
 	b.addLengthPrefixed(1, false, f)
@@ -119,6 +140,34 @@ func (b *Builder) AddUint24LengthPrefixed(f BuilderContinuation) {
 	b.addLengthPrefixed(3, false, f)
 }
 
+// AddUint32LengthPrefixed adds a big-endian, 32-bit length-prefixed byte sequence.
+func (b *Builder) AddUint32LengthPrefixed(f BuilderContinuation) {
+	b.addLengthPrefixed(4, false, f)
+}
+
+func (b *Builder) callContinuation(f BuilderContinuation, arg *Builder) {
+	if !*b.inContinuation {
+		*b.inContinuation = true
+
+		defer func() {
+			*b.inContinuation = false
+
+			r := recover()
+			if r == nil {
+				return
+			}
+
+			if buildError, ok := r.(BuildError); ok {
+				b.err = buildError.Err
+			} else {
+				panic(r)
+			}
+		}()
+	}
+
+	f(arg)
+}
+
 func (b *Builder) addLengthPrefixed(lenLen int, isASN1 bool, f BuilderContinuation) {
 	// Subsequent writes can be ignored if the builder has encountered an error.
 	if b.err != nil {
@@ -128,15 +177,20 @@ func (b *Builder) addLengthPrefixed(lenLen int, isASN1 bool, f BuilderContinuati
 	offset := len(b.result)
 	b.add(make([]byte, lenLen)...)
 
+	if b.inContinuation == nil {
+		b.inContinuation = new(bool)
+	}
+
 	b.child = &Builder{
-		result:        b.result,
-		fixedSize:     b.fixedSize,
-		offset:        offset,
-		pendingLenLen: lenLen,
-		pendingIsASN1: isASN1,
+		result:         b.result,
+		fixedSize:      b.fixedSize,
+		offset:         offset,
+		pendingLenLen:  lenLen,
+		pendingIsASN1:  isASN1,
+		inContinuation: b.inContinuation,
 	}
 
-	f(b.child)
+	b.callContinuation(f, b.child)
 	b.flushChild()
 	if b.child != nil {
 		panic("cryptobyte: internal error")

+ 49 - 0
psiphon/common/crypto/cryptobyte/cryptobyte_test.go

@@ -6,6 +6,7 @@ package cryptobyte
 
 import (
 	"bytes"
+	"errors"
 	"fmt"
 	"testing"
 )
@@ -18,6 +19,54 @@ func builderBytesEq(b *Builder, want ...byte) error {
 	return nil
 }
 
+func TestContinuationError(t *testing.T) {
+	const errorStr = "TestContinuationError"
+	var b Builder
+	b.AddUint8LengthPrefixed(func(b *Builder) {
+		b.AddUint8(1)
+		panic(BuildError{Err: errors.New(errorStr)})
+	})
+
+	ret, err := b.Bytes()
+	if ret != nil {
+		t.Error("expected nil result")
+	}
+	if err == nil {
+		t.Fatal("unexpected nil error")
+	}
+	if s := err.Error(); s != errorStr {
+		t.Errorf("expected error %q, got %v", errorStr, s)
+	}
+}
+
+func TestContinuationNonError(t *testing.T) {
+	defer func() {
+		recover()
+	}()
+
+	var b Builder
+	b.AddUint8LengthPrefixed(func(b *Builder) {
+		b.AddUint8(1)
+		panic(1)
+	})
+
+	t.Error("Builder did not panic")
+}
+
+func TestGeneratedPanic(t *testing.T) {
+	defer func() {
+		recover()
+	}()
+
+	var b Builder
+	b.AddUint8LengthPrefixed(func(b *Builder) {
+		var p *byte
+		*p = 0
+	})
+
+	t.Error("Builder did not panic")
+}
+
 func TestBytes(t *testing.T) {
 	var b Builder
 	v := []byte("foobarbaz")

+ 42 - 8
psiphon/common/crypto/cryptobyte/example_test.go

@@ -5,9 +5,11 @@
 package cryptobyte_test
 
 import (
-	"encoding/asn1"
+	"errors"
 	"fmt"
+
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/cryptobyte"
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/cryptobyte/asn1"
 )
 
 func ExampleString_lengthPrefixed() {
@@ -37,7 +39,7 @@ func ExampleString_lengthPrefixed() {
 	fmt.Printf("%#v\n", result)
 }
 
-func ExampleString_asn1() {
+func ExampleString_aSN1() {
 	// This is an example of parsing ASN.1 data that looks like:
 	//    Foo ::= SEQUENCE {
 	//      version [6] INTEGER DEFAULT 0
@@ -51,12 +53,12 @@ func ExampleString_asn1() {
 		data, inner, versionBytes cryptobyte.String
 		haveVersion               bool
 	)
-	if !input.ReadASN1(&inner, cryptobyte.Tag(asn1.TagSequence).Constructed()) ||
+	if !input.ReadASN1(&inner, asn1.SEQUENCE) ||
 		!input.Empty() ||
-		!inner.ReadOptionalASN1(&versionBytes, &haveVersion, cryptobyte.Tag(6).Constructed().ContextSpecific()) ||
+		!inner.ReadOptionalASN1(&versionBytes, &haveVersion, asn1.Tag(6).Constructed().ContextSpecific()) ||
 		(haveVersion && !versionBytes.ReadASN1Integer(&version)) ||
 		(haveVersion && !versionBytes.Empty()) ||
-		!inner.ReadASN1(&data, asn1.TagOctetString) ||
+		!inner.ReadASN1(&data, asn1.OCTET_STRING) ||
 		!inner.Empty() {
 		panic("bad format")
 	}
@@ -65,7 +67,7 @@ func ExampleString_asn1() {
 	fmt.Printf("haveVersion: %t, version: %d, data: %s\n", haveVersion, version, string(data))
 }
 
-func ExampleBuilder_asn1() {
+func ExampleBuilder_aSN1() {
 	// This is an example of building ASN.1 data that looks like:
 	//    Foo ::= SEQUENCE {
 	//      version [6] INTEGER DEFAULT 0
@@ -77,9 +79,9 @@ func ExampleBuilder_asn1() {
 	const defaultVersion = 0
 
 	var b cryptobyte.Builder
-	b.AddASN1(cryptobyte.Tag(asn1.TagSequence).Constructed(), func(b *cryptobyte.Builder) {
+	b.AddASN1(asn1.SEQUENCE, func(b *cryptobyte.Builder) {
 		if version != defaultVersion {
-			b.AddASN1(cryptobyte.Tag(6).Constructed().ContextSpecific(), func(b *cryptobyte.Builder) {
+			b.AddASN1(asn1.Tag(6).Constructed().ContextSpecific(), func(b *cryptobyte.Builder) {
 				b.AddASN1Int64(version)
 			})
 		}
@@ -118,3 +120,35 @@ func ExampleBuilder_lengthPrefixed() {
 	// Output: 000c0568656c6c6f05776f726c64
 	fmt.Printf("%x\n", result)
 }
+
+func ExampleBuilder_lengthPrefixOverflow() {
+	// Writing more data that can be expressed by the length prefix results
+	// in an error from Bytes().
+
+	tooLarge := make([]byte, 256)
+
+	var b cryptobyte.Builder
+	b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+		b.AddBytes(tooLarge)
+	})
+
+	result, err := b.Bytes()
+	fmt.Printf("len=%d err=%s\n", len(result), err)
+
+	// Output: len=0 err=cryptobyte: pending child length 256 exceeds 1-byte length prefix
+}
+
+func ExampleBuilderContinuation_errorHandling() {
+	var b cryptobyte.Builder
+	// Continuations that panic with a BuildError will cause Bytes to
+	// return the inner error.
+	b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+		b.AddUint32(0)
+		panic(cryptobyte.BuildError{Err: errors.New("example error")})
+	})
+
+	result, err := b.Bytes()
+	fmt.Printf("len=%d err=%s\n", len(result), err)
+
+	// Output: len=0 err=example error
+}

+ 27 - 18
psiphon/common/crypto/cryptobyte/string.go

@@ -2,9 +2,19 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Package cryptobyte implements building and parsing of byte strings for
-// DER-encoded ASN.1 and TLS messages. See the examples for the Builder and
-// String types to get started.
+// Package cryptobyte contains types that help with parsing and constructing
+// length-prefixed, binary messages, including ASN.1 DER. (The asn1 subpackage
+// contains useful ASN.1 constants.)
+//
+// The String type is for parsing. It wraps a []byte slice and provides helper
+// functions for consuming structures, value by value.
+//
+// The Builder type is for constructing messages. It providers helper functions
+// for appending values and also for appending length-prefixed submessages –
+// without having to worry about calculating the length prefix ahead of time.
+//
+// See the documentation and examples for the Builder and String types to get
+// started.
 package cryptobyte // import "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/cryptobyte"
 
 // String represents a string of bytes. It provides methods for parsing
@@ -27,8 +37,8 @@ func (s *String) Skip(n int) bool {
 	return s.read(n) != nil
 }
 
-// ReadUint8 decodes an 8-bit value into out and advances over it. It
-// returns true on success and false on error.
+// ReadUint8 decodes an 8-bit value into out and advances over it.
+// It reports whether the read was successful.
 func (s *String) ReadUint8(out *uint8) bool {
 	v := s.read(1)
 	if v == nil {
@@ -39,7 +49,7 @@ func (s *String) ReadUint8(out *uint8) bool {
 }
 
 // ReadUint16 decodes a big-endian, 16-bit value into out and advances over it.
-// It returns true on success and false on error.
+// It reports whether the read was successful.
 func (s *String) ReadUint16(out *uint16) bool {
 	v := s.read(2)
 	if v == nil {
@@ -50,7 +60,7 @@ func (s *String) ReadUint16(out *uint16) bool {
 }
 
 // ReadUint24 decodes a big-endian, 24-bit value into out and advances over it.
-// It returns true on success and false on error.
+// It reports whether the read was successful.
 func (s *String) ReadUint24(out *uint32) bool {
 	v := s.read(3)
 	if v == nil {
@@ -61,7 +71,7 @@ func (s *String) ReadUint24(out *uint32) bool {
 }
 
 // ReadUint32 decodes a big-endian, 32-bit value into out and advances over it.
-// It returns true on success and false on error.
+// It reports whether the read was successful.
 func (s *String) ReadUint32(out *uint32) bool {
 	v := s.read(4)
 	if v == nil {
@@ -109,28 +119,27 @@ func (s *String) readLengthPrefixed(lenLen int, outChild *String) bool {
 }
 
 // ReadUint8LengthPrefixed reads the content of an 8-bit length-prefixed value
-// into out and advances over it. It returns true on success and false on
-// error.
+// into out and advances over it. It reports whether the read was successful.
 func (s *String) ReadUint8LengthPrefixed(out *String) bool {
 	return s.readLengthPrefixed(1, out)
 }
 
 // ReadUint16LengthPrefixed reads the content of a big-endian, 16-bit
-// length-prefixed value into out and advances over it. It returns true on
-// success and false on error.
+// length-prefixed value into out and advances over it. It reports whether the
+// read was successful.
 func (s *String) ReadUint16LengthPrefixed(out *String) bool {
 	return s.readLengthPrefixed(2, out)
 }
 
 // ReadUint24LengthPrefixed reads the content of a big-endian, 24-bit
-// length-prefixed value into out and advances over it. It returns true on
-// success and false on error.
+// length-prefixed value into out and advances over it. It reports whether
+// the read was successful.
 func (s *String) ReadUint24LengthPrefixed(out *String) bool {
 	return s.readLengthPrefixed(3, out)
 }
 
-// ReadBytes reads n bytes into out and advances over them. It returns true on
-// success and false and error.
+// ReadBytes reads n bytes into out and advances over them. It reports
+// whether the read was successful.
 func (s *String) ReadBytes(out *[]byte, n int) bool {
 	v := s.read(n)
 	if v == nil {
@@ -140,8 +149,8 @@ func (s *String) ReadBytes(out *[]byte, n int) bool {
 	return true
 }
 
-// CopyBytes copies len(out) bytes into out and advances over them. It returns
-// true on success and false on error.
+// CopyBytes copies len(out) bytes into out and advances over them. It reports
+// whether the copy operation was successful
 func (s *String) CopyBytes(out []byte) bool {
 	n := len(out)
 	v := s.read(n)

+ 1 - 1
psiphon/common/crypto/curve25519/curve25519.go

@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// We have a implementation in amd64 assembly so this code is only run on
+// We have an implementation in amd64 assembly so this code is only run on
 // non-amd64 platforms. The amd64 assembly does not support gccgo.
 // +build !amd64 gccgo appengine
 

+ 50 - 14
psiphon/common/crypto/ed25519/ed25519.go

@@ -6,17 +6,20 @@
 // https://ed25519.cr.yp.to/.
 //
 // These functions are also compatible with the “Ed25519” function defined in
-// https://tools.ietf.org/html/draft-irtf-cfrg-eddsa-05.
+// RFC 8032. However, unlike RFC 8032's formulation, this package's private key
+// representation includes a public key suffix to make multiple signing
+// operations with the same key more efficient. This package refers to the RFC
+// 8032 private key as the “seed”.
 package ed25519
 
 // This code is a port of the public domain, “ref10” implementation of ed25519
 // from SUPERCOP.
 
 import (
+	"bytes"
 	"crypto"
 	cryptorand "crypto/rand"
 	"crypto/sha512"
-	"crypto/subtle"
 	"errors"
 	"io"
 	"strconv"
@@ -31,6 +34,8 @@ const (
 	PrivateKeySize = 64
 	// SignatureSize is the size, in bytes, of signatures generated and verified by this package.
 	SignatureSize = 64
+	// SeedSize is the size, in bytes, of private key seeds. These are the private key representations used by RFC 8032.
+	SeedSize = 32
 )
 
 // PublicKey is the type of Ed25519 public keys.
@@ -46,6 +51,15 @@ func (priv PrivateKey) Public() crypto.PublicKey {
 	return PublicKey(publicKey)
 }
 
+// Seed returns the private key seed corresponding to priv. It is provided for
+// interoperability with RFC 8032. RFC 8032's private keys correspond to seeds
+// in this package.
+func (priv PrivateKey) Seed() []byte {
+	seed := make([]byte, SeedSize)
+	copy(seed, priv[:32])
+	return seed
+}
+
 // Sign signs the given message with priv.
 // Ed25519 performs two passes over messages to be signed and therefore cannot
 // handle pre-hashed messages. Thus opts.HashFunc() must return zero to
@@ -61,19 +75,33 @@ func (priv PrivateKey) Sign(rand io.Reader, message []byte, opts crypto.SignerOp
 
 // GenerateKey generates a public/private key pair using entropy from rand.
 // If rand is nil, crypto/rand.Reader will be used.
-func GenerateKey(rand io.Reader) (publicKey PublicKey, privateKey PrivateKey, err error) {
+func GenerateKey(rand io.Reader) (PublicKey, PrivateKey, error) {
 	if rand == nil {
 		rand = cryptorand.Reader
 	}
 
-	privateKey = make([]byte, PrivateKeySize)
-	publicKey = make([]byte, PublicKeySize)
-	_, err = io.ReadFull(rand, privateKey[:32])
-	if err != nil {
+	seed := make([]byte, SeedSize)
+	if _, err := io.ReadFull(rand, seed); err != nil {
 		return nil, nil, err
 	}
 
-	digest := sha512.Sum512(privateKey[:32])
+	privateKey := NewKeyFromSeed(seed)
+	publicKey := make([]byte, PublicKeySize)
+	copy(publicKey, privateKey[32:])
+
+	return publicKey, privateKey, nil
+}
+
+// NewKeyFromSeed calculates a private key from a seed. It will panic if
+// len(seed) is not SeedSize. This function is provided for interoperability
+// with RFC 8032. RFC 8032's private keys correspond to seeds in this
+// package.
+func NewKeyFromSeed(seed []byte) PrivateKey {
+	if l := len(seed); l != SeedSize {
+		panic("ed25519: bad seed length: " + strconv.Itoa(l))
+	}
+
+	digest := sha512.Sum512(seed)
 	digest[0] &= 248
 	digest[31] &= 127
 	digest[31] |= 64
@@ -85,10 +113,11 @@ func GenerateKey(rand io.Reader) (publicKey PublicKey, privateKey PrivateKey, er
 	var publicKeyBytes [32]byte
 	A.ToBytes(&publicKeyBytes)
 
+	privateKey := make([]byte, PrivateKeySize)
+	copy(privateKey, seed)
 	copy(privateKey[32:], publicKeyBytes[:])
-	copy(publicKey, publicKeyBytes[:])
 
-	return publicKey, privateKey, nil
+	return privateKey
 }
 
 // Sign signs the message with privateKey and returns a signature. It will
@@ -171,11 +200,18 @@ func Verify(publicKey PublicKey, message, sig []byte) bool {
 	edwards25519.ScReduce(&hReduced, &digest)
 
 	var R edwards25519.ProjectiveGroupElement
-	var b [32]byte
-	copy(b[:], sig[32:])
-	edwards25519.GeDoubleScalarMultVartime(&R, &hReduced, &A, &b)
+	var s [32]byte
+	copy(s[:], sig[32:])
+
+	// https://tools.ietf.org/html/rfc8032#section-5.1.7 requires that s be in
+	// the range [0, order) in order to prevent signature malleability.
+	if !edwards25519.ScMinimal(&s) {
+		return false
+	}
+
+	edwards25519.GeDoubleScalarMultVartime(&R, &hReduced, &A, &s)
 
 	var checkR [32]byte
 	R.ToBytes(&checkR)
-	return subtle.ConstantTimeCompare(sig[:32], checkR[:]) == 1
+	return bytes.Equal(sig[:32], checkR[:])
 }

+ 37 - 0
psiphon/common/crypto/ed25519/ed25519_test.go

@@ -139,6 +139,19 @@ func TestGolden(t *testing.T) {
 		if !Verify(pubKey, msg, sig2) {
 			t.Errorf("signature failed to verify on line %d", lineNo)
 		}
+
+		priv2 := NewKeyFromSeed(priv[:32])
+		if !bytes.Equal(priv[:], priv2) {
+			t.Errorf("recreating key pair gave different private key on line %d: %x vs %x", lineNo, priv[:], priv2)
+		}
+
+		if pubKey2 := priv2.Public().(PublicKey); !bytes.Equal(pubKey, pubKey2) {
+			t.Errorf("recreating key pair gave different public key on line %d: %x vs %x", lineNo, pubKey, pubKey2)
+		}
+
+		if seed := priv2.Seed(); !bytes.Equal(priv[:32], seed) {
+			t.Errorf("recreating key pair gave different seed on line %d: %x vs %x", lineNo, priv[:32], seed)
+		}
 	}
 
 	if err := scanner.Err(); err != nil {
@@ -146,6 +159,30 @@ func TestGolden(t *testing.T) {
 	}
 }
 
+func TestMalleability(t *testing.T) {
+	// https://tools.ietf.org/html/rfc8032#section-5.1.7 adds an additional test
+	// that s be in [0, order). This prevents someone from adding a multiple of
+	// order to s and obtaining a second valid signature for the same message.
+	msg := []byte{0x54, 0x65, 0x73, 0x74}
+	sig := []byte{
+		0x7c, 0x38, 0xe0, 0x26, 0xf2, 0x9e, 0x14, 0xaa, 0xbd, 0x05, 0x9a,
+		0x0f, 0x2d, 0xb8, 0xb0, 0xcd, 0x78, 0x30, 0x40, 0x60, 0x9a, 0x8b,
+		0xe6, 0x84, 0xdb, 0x12, 0xf8, 0x2a, 0x27, 0x77, 0x4a, 0xb0, 0x67,
+		0x65, 0x4b, 0xce, 0x38, 0x32, 0xc2, 0xd7, 0x6f, 0x8f, 0x6f, 0x5d,
+		0xaf, 0xc0, 0x8d, 0x93, 0x39, 0xd4, 0xee, 0xf6, 0x76, 0x57, 0x33,
+		0x36, 0xa5, 0xc5, 0x1e, 0xb6, 0xf9, 0x46, 0xb3, 0x1d,
+	}
+	publicKey := []byte{
+		0x7d, 0x4d, 0x0e, 0x7f, 0x61, 0x53, 0xa6, 0x9b, 0x62, 0x42, 0xb5,
+		0x22, 0xab, 0xbe, 0xe6, 0x85, 0xfd, 0xa4, 0x42, 0x0f, 0x88, 0x34,
+		0xb1, 0x08, 0xc3, 0xbd, 0xae, 0x36, 0x9e, 0xf5, 0x49, 0xfa,
+	}
+
+	if Verify(publicKey, msg, sig) {
+		t.Fatal("non-canonical signature accepted")
+	}
+}
+
 func BenchmarkKeyGeneration(b *testing.B) {
 	var zero zeroReader
 	for i := 0; i < b.N; i++ {

+ 22 - 0
psiphon/common/crypto/ed25519/internal/edwards25519/edwards25519.go

@@ -4,6 +4,8 @@
 
 package edwards25519
 
+import "encoding/binary"
+
 // This code is a port of the public domain, “ref10” implementation of ed25519
 // from SUPERCOP.
 
@@ -1769,3 +1771,23 @@ func ScReduce(out *[32]byte, s *[64]byte) {
 	out[30] = byte(s11 >> 9)
 	out[31] = byte(s11 >> 17)
 }
+
+// order is the order of Curve25519 in little-endian form.
+var order = [4]uint64{0x5812631a5cf5d3ed, 0x14def9dea2f79cd6, 0, 0x1000000000000000}
+
+// ScMinimal returns true if the given scalar is less than the order of the
+// curve.
+func ScMinimal(scalar *[32]byte) bool {
+	for i := 3; ; i-- {
+		v := binary.LittleEndian.Uint64(scalar[i*8:])
+		if v > order[i] {
+			return false
+		} else if v < order[i] {
+			break
+		} else if i == 0 {
+			return false
+		}
+	}
+
+	return true
+}

+ 264 - 0
psiphon/common/crypto/internal/chacha20/chacha_generic.go

@@ -0,0 +1,264 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package ChaCha20 implements the core ChaCha20 function as specified
+// in https://tools.ietf.org/html/rfc7539#section-2.3.
+package chacha20
+
+import (
+	"crypto/cipher"
+	"encoding/binary"
+
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/internal/subtle"
+)
+
+// assert that *Cipher implements cipher.Stream
+var _ cipher.Stream = (*Cipher)(nil)
+
+// Cipher is a stateful instance of ChaCha20 using a particular key
+// and nonce. A *Cipher implements the cipher.Stream interface.
+type Cipher struct {
+	key     [8]uint32
+	counter uint32 // incremented after each block
+	nonce   [3]uint32
+	buf     [bufSize]byte // buffer for unused keystream bytes
+	len     int           // number of unused keystream bytes at end of buf
+}
+
+// New creates a new ChaCha20 stream cipher with the given key and nonce.
+// The initial counter value is set to 0.
+func New(key [8]uint32, nonce [3]uint32) *Cipher {
+	return &Cipher{key: key, nonce: nonce}
+}
+
+// ChaCha20 constants spelling "expand 32-byte k"
+const (
+	j0 uint32 = 0x61707865
+	j1 uint32 = 0x3320646e
+	j2 uint32 = 0x79622d32
+	j3 uint32 = 0x6b206574
+)
+
+func quarterRound(a, b, c, d uint32) (uint32, uint32, uint32, uint32) {
+	a += b
+	d ^= a
+	d = (d << 16) | (d >> 16)
+	c += d
+	b ^= c
+	b = (b << 12) | (b >> 20)
+	a += b
+	d ^= a
+	d = (d << 8) | (d >> 24)
+	c += d
+	b ^= c
+	b = (b << 7) | (b >> 25)
+	return a, b, c, d
+}
+
+// XORKeyStream XORs each byte in the given slice with a byte from the
+// cipher's key stream. Dst and src must overlap entirely or not at all.
+//
+// If len(dst) < len(src), XORKeyStream will panic. It is acceptable
+// to pass a dst bigger than src, and in that case, XORKeyStream will
+// only update dst[:len(src)] and will not touch the rest of dst.
+//
+// Multiple calls to XORKeyStream behave as if the concatenation of
+// the src buffers was passed in a single run. That is, Cipher
+// maintains state and does not reset at each XORKeyStream call.
+func (s *Cipher) XORKeyStream(dst, src []byte) {
+	if len(dst) < len(src) {
+		panic("chacha20: output smaller than input")
+	}
+	if subtle.InexactOverlap(dst[:len(src)], src) {
+		panic("chacha20: invalid buffer overlap")
+	}
+
+	// xor src with buffered keystream first
+	if s.len != 0 {
+		buf := s.buf[len(s.buf)-s.len:]
+		if len(src) < len(buf) {
+			buf = buf[:len(src)]
+		}
+		td, ts := dst[:len(buf)], src[:len(buf)] // BCE hint
+		for i, b := range buf {
+			td[i] = ts[i] ^ b
+		}
+		s.len -= len(buf)
+		if s.len != 0 {
+			return
+		}
+		s.buf = [len(s.buf)]byte{} // zero the empty buffer
+		src = src[len(buf):]
+		dst = dst[len(buf):]
+	}
+
+	if len(src) == 0 {
+		return
+	}
+	if haveAsm {
+		if uint64(len(src))+uint64(s.counter)*64 > (1<<38)-64 {
+			panic("chacha20: counter overflow")
+		}
+		s.xorKeyStreamAsm(dst, src)
+		return
+	}
+
+	// set up a 64-byte buffer to pad out the final block if needed
+	// (hoisted out of the main loop to avoid spills)
+	rem := len(src) % 64  // length of final block
+	fin := len(src) - rem // index of final block
+	if rem > 0 {
+		copy(s.buf[len(s.buf)-64:], src[fin:])
+	}
+
+	// pre-calculate most of the first round
+	s1, s5, s9, s13 := quarterRound(j1, s.key[1], s.key[5], s.nonce[0])
+	s2, s6, s10, s14 := quarterRound(j2, s.key[2], s.key[6], s.nonce[1])
+	s3, s7, s11, s15 := quarterRound(j3, s.key[3], s.key[7], s.nonce[2])
+
+	n := len(src)
+	src, dst = src[:n:n], dst[:n:n] // BCE hint
+	for i := 0; i < n; i += 64 {
+		// calculate the remainder of the first round
+		s0, s4, s8, s12 := quarterRound(j0, s.key[0], s.key[4], s.counter)
+
+		// execute the second round
+		x0, x5, x10, x15 := quarterRound(s0, s5, s10, s15)
+		x1, x6, x11, x12 := quarterRound(s1, s6, s11, s12)
+		x2, x7, x8, x13 := quarterRound(s2, s7, s8, s13)
+		x3, x4, x9, x14 := quarterRound(s3, s4, s9, s14)
+
+		// execute the remaining 18 rounds
+		for i := 0; i < 9; i++ {
+			x0, x4, x8, x12 = quarterRound(x0, x4, x8, x12)
+			x1, x5, x9, x13 = quarterRound(x1, x5, x9, x13)
+			x2, x6, x10, x14 = quarterRound(x2, x6, x10, x14)
+			x3, x7, x11, x15 = quarterRound(x3, x7, x11, x15)
+
+			x0, x5, x10, x15 = quarterRound(x0, x5, x10, x15)
+			x1, x6, x11, x12 = quarterRound(x1, x6, x11, x12)
+			x2, x7, x8, x13 = quarterRound(x2, x7, x8, x13)
+			x3, x4, x9, x14 = quarterRound(x3, x4, x9, x14)
+		}
+
+		x0 += j0
+		x1 += j1
+		x2 += j2
+		x3 += j3
+
+		x4 += s.key[0]
+		x5 += s.key[1]
+		x6 += s.key[2]
+		x7 += s.key[3]
+		x8 += s.key[4]
+		x9 += s.key[5]
+		x10 += s.key[6]
+		x11 += s.key[7]
+
+		x12 += s.counter
+		x13 += s.nonce[0]
+		x14 += s.nonce[1]
+		x15 += s.nonce[2]
+
+		// increment the counter
+		s.counter += 1
+		if s.counter == 0 {
+			panic("chacha20: counter overflow")
+		}
+
+		// pad to 64 bytes if needed
+		in, out := src[i:], dst[i:]
+		if i == fin {
+			// src[fin:] has already been copied into s.buf before
+			// the main loop
+			in, out = s.buf[len(s.buf)-64:], s.buf[len(s.buf)-64:]
+		}
+		in, out = in[:64], out[:64] // BCE hint
+
+		// XOR the key stream with the source and write out the result
+		xor(out[0:], in[0:], x0)
+		xor(out[4:], in[4:], x1)
+		xor(out[8:], in[8:], x2)
+		xor(out[12:], in[12:], x3)
+		xor(out[16:], in[16:], x4)
+		xor(out[20:], in[20:], x5)
+		xor(out[24:], in[24:], x6)
+		xor(out[28:], in[28:], x7)
+		xor(out[32:], in[32:], x8)
+		xor(out[36:], in[36:], x9)
+		xor(out[40:], in[40:], x10)
+		xor(out[44:], in[44:], x11)
+		xor(out[48:], in[48:], x12)
+		xor(out[52:], in[52:], x13)
+		xor(out[56:], in[56:], x14)
+		xor(out[60:], in[60:], x15)
+	}
+	// copy any trailing bytes out of the buffer and into dst
+	if rem != 0 {
+		s.len = 64 - rem
+		copy(dst[fin:], s.buf[len(s.buf)-64:])
+	}
+}
+
+// Advance discards bytes in the key stream until the next 64 byte block
+// boundary is reached and updates the counter accordingly. If the key
+// stream is already at a block boundary no bytes will be discarded and
+// the counter will be unchanged.
+func (s *Cipher) Advance() {
+	s.len -= s.len % 64
+	if s.len == 0 {
+		s.buf = [len(s.buf)]byte{}
+	}
+}
+
+// XORKeyStream crypts bytes from in to out using the given key and counters.
+// In and out must overlap entirely or not at all. Counter contains the raw
+// ChaCha20 counter bytes (i.e. block counter followed by nonce).
+func XORKeyStream(out, in []byte, counter *[16]byte, key *[32]byte) {
+	s := Cipher{
+		key: [8]uint32{
+			binary.LittleEndian.Uint32(key[0:4]),
+			binary.LittleEndian.Uint32(key[4:8]),
+			binary.LittleEndian.Uint32(key[8:12]),
+			binary.LittleEndian.Uint32(key[12:16]),
+			binary.LittleEndian.Uint32(key[16:20]),
+			binary.LittleEndian.Uint32(key[20:24]),
+			binary.LittleEndian.Uint32(key[24:28]),
+			binary.LittleEndian.Uint32(key[28:32]),
+		},
+		nonce: [3]uint32{
+			binary.LittleEndian.Uint32(counter[4:8]),
+			binary.LittleEndian.Uint32(counter[8:12]),
+			binary.LittleEndian.Uint32(counter[12:16]),
+		},
+		counter: binary.LittleEndian.Uint32(counter[0:4]),
+	}
+	s.XORKeyStream(out, in)
+}
+
+// HChaCha20 uses the ChaCha20 core to generate a derived key from a key and a
+// nonce. It should only be used as part of the XChaCha20 construction.
+func HChaCha20(key *[8]uint32, nonce *[4]uint32) [8]uint32 {
+	x0, x1, x2, x3 := j0, j1, j2, j3
+	x4, x5, x6, x7 := key[0], key[1], key[2], key[3]
+	x8, x9, x10, x11 := key[4], key[5], key[6], key[7]
+	x12, x13, x14, x15 := nonce[0], nonce[1], nonce[2], nonce[3]
+
+	for i := 0; i < 10; i++ {
+		x0, x4, x8, x12 = quarterRound(x0, x4, x8, x12)
+		x1, x5, x9, x13 = quarterRound(x1, x5, x9, x13)
+		x2, x6, x10, x14 = quarterRound(x2, x6, x10, x14)
+		x3, x7, x11, x15 = quarterRound(x3, x7, x11, x15)
+
+		x0, x5, x10, x15 = quarterRound(x0, x5, x10, x15)
+		x1, x6, x11, x12 = quarterRound(x1, x6, x11, x12)
+		x2, x7, x8, x13 = quarterRound(x2, x7, x8, x13)
+		x3, x4, x9, x14 = quarterRound(x3, x4, x9, x14)
+	}
+
+	var out [8]uint32
+	out[0], out[1], out[2], out[3] = x0, x1, x2, x3
+	out[4], out[5], out[6], out[7] = x12, x13, x14, x15
+	return out
+}

+ 16 - 0
psiphon/common/crypto/internal/chacha20/chacha_noasm.go

@@ -0,0 +1,16 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !s390x gccgo appengine
+
+package chacha20
+
+const (
+	bufSize = 64
+	haveAsm = false
+)
+
+func (*Cipher) xorKeyStreamAsm(dst, src []byte) {
+	panic("not implemented")
+}

+ 30 - 0
psiphon/common/crypto/internal/chacha20/chacha_s390x.go

@@ -0,0 +1,30 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build s390x,!gccgo,!appengine
+
+package chacha20
+
+var haveAsm = hasVectorFacility()
+
+const bufSize = 256
+
+// hasVectorFacility reports whether the machine supports the vector
+// facility (vx).
+// Implementation in asm_s390x.s.
+func hasVectorFacility() bool
+
+// xorKeyStreamVX is an assembly implementation of XORKeyStream. It must only
+// be called when the vector facility is available.
+// Implementation in asm_s390x.s.
+//go:noescape
+func xorKeyStreamVX(dst, src []byte, key *[8]uint32, nonce *[3]uint32, counter *uint32, buf *[256]byte, len *int)
+
+func (c *Cipher) xorKeyStreamAsm(dst, src []byte) {
+	xorKeyStreamVX(dst, src, &c.key, &c.nonce, &c.counter, &c.buf, &c.len)
+}
+
+// EXRL targets, DO NOT CALL!
+func mvcSrcToBuf()
+func mvcBufToDst()

+ 283 - 0
psiphon/common/crypto/internal/chacha20/chacha_s390x.s

@@ -0,0 +1,283 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build s390x,!gccgo,!appengine
+
+#include "go_asm.h"
+#include "textflag.h"
+
+// This is an implementation of the ChaCha20 encryption algorithm as
+// specified in RFC 7539. It uses vector instructions to compute
+// 4 keystream blocks in parallel (256 bytes) which are then XORed
+// with the bytes in the input slice.
+
+GLOBL ·constants<>(SB), RODATA|NOPTR, $32
+// BSWAP: swap bytes in each 4-byte element
+DATA ·constants<>+0x00(SB)/4, $0x03020100
+DATA ·constants<>+0x04(SB)/4, $0x07060504
+DATA ·constants<>+0x08(SB)/4, $0x0b0a0908
+DATA ·constants<>+0x0c(SB)/4, $0x0f0e0d0c
+// J0: [j0, j1, j2, j3]
+DATA ·constants<>+0x10(SB)/4, $0x61707865
+DATA ·constants<>+0x14(SB)/4, $0x3320646e
+DATA ·constants<>+0x18(SB)/4, $0x79622d32
+DATA ·constants<>+0x1c(SB)/4, $0x6b206574
+
+// EXRL targets:
+TEXT ·mvcSrcToBuf(SB), NOFRAME|NOSPLIT, $0
+	MVC $1, (R1), (R8)
+	RET
+
+TEXT ·mvcBufToDst(SB), NOFRAME|NOSPLIT, $0
+	MVC $1, (R8), (R9)
+	RET
+
+#define BSWAP V5
+#define J0    V6
+#define KEY0  V7
+#define KEY1  V8
+#define NONCE V9
+#define CTR   V10
+#define M0    V11
+#define M1    V12
+#define M2    V13
+#define M3    V14
+#define INC   V15
+#define X0    V16
+#define X1    V17
+#define X2    V18
+#define X3    V19
+#define X4    V20
+#define X5    V21
+#define X6    V22
+#define X7    V23
+#define X8    V24
+#define X9    V25
+#define X10   V26
+#define X11   V27
+#define X12   V28
+#define X13   V29
+#define X14   V30
+#define X15   V31
+
+#define NUM_ROUNDS 20
+
+#define ROUND4(a0, a1, a2, a3, b0, b1, b2, b3, c0, c1, c2, c3, d0, d1, d2, d3) \
+	VAF    a1, a0, a0  \
+	VAF    b1, b0, b0  \
+	VAF    c1, c0, c0  \
+	VAF    d1, d0, d0  \
+	VX     a0, a2, a2  \
+	VX     b0, b2, b2  \
+	VX     c0, c2, c2  \
+	VX     d0, d2, d2  \
+	VERLLF $16, a2, a2 \
+	VERLLF $16, b2, b2 \
+	VERLLF $16, c2, c2 \
+	VERLLF $16, d2, d2 \
+	VAF    a2, a3, a3  \
+	VAF    b2, b3, b3  \
+	VAF    c2, c3, c3  \
+	VAF    d2, d3, d3  \
+	VX     a3, a1, a1  \
+	VX     b3, b1, b1  \
+	VX     c3, c1, c1  \
+	VX     d3, d1, d1  \
+	VERLLF $12, a1, a1 \
+	VERLLF $12, b1, b1 \
+	VERLLF $12, c1, c1 \
+	VERLLF $12, d1, d1 \
+	VAF    a1, a0, a0  \
+	VAF    b1, b0, b0  \
+	VAF    c1, c0, c0  \
+	VAF    d1, d0, d0  \
+	VX     a0, a2, a2  \
+	VX     b0, b2, b2  \
+	VX     c0, c2, c2  \
+	VX     d0, d2, d2  \
+	VERLLF $8, a2, a2  \
+	VERLLF $8, b2, b2  \
+	VERLLF $8, c2, c2  \
+	VERLLF $8, d2, d2  \
+	VAF    a2, a3, a3  \
+	VAF    b2, b3, b3  \
+	VAF    c2, c3, c3  \
+	VAF    d2, d3, d3  \
+	VX     a3, a1, a1  \
+	VX     b3, b1, b1  \
+	VX     c3, c1, c1  \
+	VX     d3, d1, d1  \
+	VERLLF $7, a1, a1  \
+	VERLLF $7, b1, b1  \
+	VERLLF $7, c1, c1  \
+	VERLLF $7, d1, d1
+
+#define PERMUTE(mask, v0, v1, v2, v3) \
+	VPERM v0, v0, mask, v0 \
+	VPERM v1, v1, mask, v1 \
+	VPERM v2, v2, mask, v2 \
+	VPERM v3, v3, mask, v3
+
+#define ADDV(x, v0, v1, v2, v3) \
+	VAF x, v0, v0 \
+	VAF x, v1, v1 \
+	VAF x, v2, v2 \
+	VAF x, v3, v3
+
+#define XORV(off, dst, src, v0, v1, v2, v3) \
+	VLM  off(src), M0, M3          \
+	PERMUTE(BSWAP, v0, v1, v2, v3) \
+	VX   v0, M0, M0                \
+	VX   v1, M1, M1                \
+	VX   v2, M2, M2                \
+	VX   v3, M3, M3                \
+	VSTM M0, M3, off(dst)
+
+#define SHUFFLE(a, b, c, d, t, u, v, w) \
+	VMRHF a, c, t \ // t = {a[0], c[0], a[1], c[1]}
+	VMRHF b, d, u \ // u = {b[0], d[0], b[1], d[1]}
+	VMRLF a, c, v \ // v = {a[2], c[2], a[3], c[3]}
+	VMRLF b, d, w \ // w = {b[2], d[2], b[3], d[3]}
+	VMRHF t, u, a \ // a = {a[0], b[0], c[0], d[0]}
+	VMRLF t, u, b \ // b = {a[1], b[1], c[1], d[1]}
+	VMRHF v, w, c \ // c = {a[2], b[2], c[2], d[2]}
+	VMRLF v, w, d // d = {a[3], b[3], c[3], d[3]}
+
+// func xorKeyStreamVX(dst, src []byte, key *[8]uint32, nonce *[3]uint32, counter *uint32, buf *[256]byte, len *int)
+TEXT ·xorKeyStreamVX(SB), NOSPLIT, $0
+	MOVD $·constants<>(SB), R1
+	MOVD dst+0(FP), R2         // R2=&dst[0]
+	LMG  src+24(FP), R3, R4    // R3=&src[0] R4=len(src)
+	MOVD key+48(FP), R5        // R5=key
+	MOVD nonce+56(FP), R6      // R6=nonce
+	MOVD counter+64(FP), R7    // R7=counter
+	MOVD buf+72(FP), R8        // R8=buf
+	MOVD len+80(FP), R9        // R9=len
+
+	// load BSWAP and J0
+	VLM (R1), BSWAP, J0
+
+	// set up tail buffer
+	ADD     $-1, R4, R12
+	MOVBZ   R12, R12
+	CMPUBEQ R12, $255, aligned
+	MOVD    R4, R1
+	AND     $~255, R1
+	MOVD    $(R3)(R1*1), R1
+	EXRL    $·mvcSrcToBuf(SB), R12
+	MOVD    $255, R0
+	SUB     R12, R0
+	MOVD    R0, (R9)               // update len
+
+aligned:
+	// setup
+	MOVD  $95, R0
+	VLM   (R5), KEY0, KEY1
+	VLL   R0, (R6), NONCE
+	VZERO M0
+	VLEIB $7, $32, M0
+	VSRLB M0, NONCE, NONCE
+
+	// initialize counter values
+	VLREPF (R7), CTR
+	VZERO  INC
+	VLEIF  $1, $1, INC
+	VLEIF  $2, $2, INC
+	VLEIF  $3, $3, INC
+	VAF    INC, CTR, CTR
+	VREPIF $4, INC
+
+chacha:
+	VREPF $0, J0, X0
+	VREPF $1, J0, X1
+	VREPF $2, J0, X2
+	VREPF $3, J0, X3
+	VREPF $0, KEY0, X4
+	VREPF $1, KEY0, X5
+	VREPF $2, KEY0, X6
+	VREPF $3, KEY0, X7
+	VREPF $0, KEY1, X8
+	VREPF $1, KEY1, X9
+	VREPF $2, KEY1, X10
+	VREPF $3, KEY1, X11
+	VLR   CTR, X12
+	VREPF $1, NONCE, X13
+	VREPF $2, NONCE, X14
+	VREPF $3, NONCE, X15
+
+	MOVD $(NUM_ROUNDS/2), R1
+
+loop:
+	ROUND4(X0, X4, X12,  X8, X1, X5, X13,  X9, X2, X6, X14, X10, X3, X7, X15, X11)
+	ROUND4(X0, X5, X15, X10, X1, X6, X12, X11, X2, X7, X13, X8,  X3, X4, X14, X9)
+
+	ADD $-1, R1
+	BNE loop
+
+	// decrement length
+	ADD $-256, R4
+	BLT tail
+
+continue:
+	// rearrange vectors
+	SHUFFLE(X0, X1, X2, X3, M0, M1, M2, M3)
+	ADDV(J0, X0, X1, X2, X3)
+	SHUFFLE(X4, X5, X6, X7, M0, M1, M2, M3)
+	ADDV(KEY0, X4, X5, X6, X7)
+	SHUFFLE(X8, X9, X10, X11, M0, M1, M2, M3)
+	ADDV(KEY1, X8, X9, X10, X11)
+	VAF CTR, X12, X12
+	SHUFFLE(X12, X13, X14, X15, M0, M1, M2, M3)
+	ADDV(NONCE, X12, X13, X14, X15)
+
+	// increment counters
+	VAF INC, CTR, CTR
+
+	// xor keystream with plaintext
+	XORV(0*64, R2, R3, X0, X4,  X8, X12)
+	XORV(1*64, R2, R3, X1, X5,  X9, X13)
+	XORV(2*64, R2, R3, X2, X6, X10, X14)
+	XORV(3*64, R2, R3, X3, X7, X11, X15)
+
+	// increment pointers
+	MOVD $256(R2), R2
+	MOVD $256(R3), R3
+
+	CMPBNE  R4, $0, chacha
+	CMPUBEQ R12, $255, return
+	EXRL    $·mvcBufToDst(SB), R12 // len was updated during setup
+
+return:
+	VSTEF $0, CTR, (R7)
+	RET
+
+tail:
+	MOVD R2, R9
+	MOVD R8, R2
+	MOVD R8, R3
+	MOVD $0, R4
+	JMP  continue
+
+// func hasVectorFacility() bool
+TEXT ·hasVectorFacility(SB), NOSPLIT, $24-1
+	MOVD  $x-24(SP), R1
+	XC    $24, 0(R1), 0(R1) // clear the storage
+	MOVD  $2, R0            // R0 is the number of double words stored -1
+	WORD  $0xB2B01000       // STFLE 0(R1)
+	XOR   R0, R0            // reset the value of R0
+	MOVBZ z-8(SP), R1
+	AND   $0x40, R1
+	BEQ   novector
+
+vectorinstalled:
+	// check if the vector instruction has been enabled
+	VLEIB  $0, $0xF, V16
+	VLGVB  $0, V16, R1
+	CMPBNE R1, $0xF, novector
+	MOVB   $1, ret+0(FP)      // have vx
+	RET
+
+novector:
+	MOVB $0, ret+0(FP) // no vx
+	RET

+ 225 - 0
psiphon/common/crypto/internal/chacha20/chacha_test.go

@@ -0,0 +1,225 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package chacha20
+
+import (
+	"encoding/binary"
+	"encoding/hex"
+	"fmt"
+	"math/rand"
+	"testing"
+)
+
+func TestCore(t *testing.T) {
+	// This is just a smoke test that checks the example from
+	// https://tools.ietf.org/html/rfc7539#section-2.3.2. The
+	// chacha20poly1305 package contains much more extensive tests of this
+	// code.
+	var key [32]byte
+	for i := range key {
+		key[i] = byte(i)
+	}
+
+	var input [16]byte
+	input[0] = 1
+	input[7] = 9
+	input[11] = 0x4a
+
+	var out [64]byte
+	XORKeyStream(out[:], out[:], &input, &key)
+	const expected = "10f1e7e4d13b5915500fdd1fa32071c4c7d1f4c733c068030422aa9ac3d46c4ed2826446079faa0914c2d705d98b02a2b5129cd1de164eb9cbd083e8a2503c4e"
+	if result := hex.EncodeToString(out[:]); result != expected {
+		t.Errorf("wanted %x but got %x", expected, result)
+	}
+}
+
+// Run the test cases with the input and output in different buffers.
+func TestNoOverlap(t *testing.T) {
+	for _, c := range testVectors {
+		s := New(c.key, c.nonce)
+		input, err := hex.DecodeString(c.input)
+		if err != nil {
+			t.Fatalf("cannot decode input %#v: %v", c.input, err)
+		}
+		output := make([]byte, c.length)
+		s.XORKeyStream(output, input)
+		got := hex.EncodeToString(output)
+		if got != c.output {
+			t.Errorf("length=%v: got %#v, want %#v", c.length, got, c.output)
+		}
+	}
+}
+
+// Run the test cases with the input and output overlapping entirely.
+func TestOverlap(t *testing.T) {
+	for _, c := range testVectors {
+		s := New(c.key, c.nonce)
+		data, err := hex.DecodeString(c.input)
+		if err != nil {
+			t.Fatalf("cannot decode input %#v: %v", c.input, err)
+		}
+		s.XORKeyStream(data, data)
+		got := hex.EncodeToString(data)
+		if got != c.output {
+			t.Errorf("length=%v: got %#v, want %#v", c.length, got, c.output)
+		}
+	}
+}
+
+// Run the test cases with various source and destination offsets.
+func TestUnaligned(t *testing.T) {
+	const max = 8 // max offset (+1) to test
+	for _, c := range testVectors {
+		input := make([]byte, c.length+max)
+		output := make([]byte, c.length+max)
+		for i := 0; i < max; i++ { // input offsets
+			for j := 0; j < max; j++ { // output offsets
+				s := New(c.key, c.nonce)
+
+				input := input[i : i+c.length]
+				output := output[j : j+c.length]
+
+				data, err := hex.DecodeString(c.input)
+				if err != nil {
+					t.Fatalf("cannot decode input %#v: %v", c.input, err)
+				}
+				copy(input, data)
+				s.XORKeyStream(output, input)
+				got := hex.EncodeToString(output)
+				if got != c.output {
+					t.Errorf("length=%v: got %#v, want %#v", c.length, got, c.output)
+				}
+			}
+		}
+	}
+}
+
+// Run the test cases by calling XORKeyStream multiple times.
+func TestStep(t *testing.T) {
+	// wide range of step sizes to try and hit edge cases
+	steps := [...]int{1, 3, 4, 7, 8, 17, 24, 30, 64, 256}
+	rnd := rand.New(rand.NewSource(123))
+	for _, c := range testVectors {
+		s := New(c.key, c.nonce)
+		input, err := hex.DecodeString(c.input)
+		if err != nil {
+			t.Fatalf("cannot decode input %#v: %v", c.input, err)
+		}
+		output := make([]byte, c.length)
+
+		// step through the buffers
+		i, step := 0, steps[rnd.Intn(len(steps))]
+		for i+step < c.length {
+			s.XORKeyStream(output[i:i+step], input[i:i+step])
+			if i+step < c.length && output[i+step] != 0 {
+				t.Errorf("length=%v, i=%v, step=%v: output overwritten", c.length, i, step)
+			}
+			i += step
+			step = steps[rnd.Intn(len(steps))]
+		}
+		// finish the encryption
+		s.XORKeyStream(output[i:], input[i:])
+
+		got := hex.EncodeToString(output)
+		if got != c.output {
+			t.Errorf("length=%v: got %#v, want %#v", c.length, got, c.output)
+		}
+	}
+}
+
+// Test that Advance() discards bytes until a block boundary is hit.
+func TestAdvance(t *testing.T) {
+	for _, c := range testVectors {
+		for i := 0; i < 63; i++ {
+			s := New(c.key, c.nonce)
+			z := New(c.key, c.nonce)
+			input, err := hex.DecodeString(c.input)
+			if err != nil {
+				t.Fatalf("cannot decode input %#v: %v", c.input, err)
+			}
+			zeros, discard := make([]byte, 64), make([]byte, 64)
+			so, zo := make([]byte, c.length), make([]byte, c.length)
+			for j := 0; j < c.length; j += 64 {
+				lim := j + i
+				if lim > c.length {
+					lim = c.length
+				}
+				s.XORKeyStream(so[j:lim], input[j:lim])
+				// calling s.Advance() multiple times should have no effect
+				for k := 0; k < i%3+1; k++ {
+					s.Advance()
+				}
+				z.XORKeyStream(zo[j:lim], input[j:lim])
+				if lim < c.length {
+					end := 64 - i
+					if c.length-lim < end {
+						end = c.length - lim
+					}
+					z.XORKeyStream(discard[:], zeros[:end])
+				}
+			}
+
+			got := hex.EncodeToString(so)
+			want := hex.EncodeToString(zo)
+			if got != want {
+				t.Errorf("length=%v: got %#v, want %#v", c.length, got, want)
+			}
+		}
+	}
+}
+
+func BenchmarkChaCha20(b *testing.B) {
+	sizes := []int{32, 63, 64, 256, 1024, 1350, 65536}
+	for _, size := range sizes {
+		s := size
+		b.Run(fmt.Sprint(s), func(b *testing.B) {
+			k := [32]byte{}
+			c := [16]byte{}
+			src := make([]byte, s)
+			dst := make([]byte, s)
+			b.SetBytes(int64(s))
+			b.ResetTimer()
+			for i := 0; i < b.N; i++ {
+				XORKeyStream(dst, src, &c, &k)
+			}
+		})
+	}
+}
+
+func TestHChaCha20(t *testing.T) {
+	// See draft-paragon-paseto-rfc-00 §7.2.1.
+	key := []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+		0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+		0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+		0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f}
+	nonce := []byte{0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x4a,
+		0x00, 0x00, 0x00, 0x00, 0x31, 0x41, 0x59, 0x27}
+	expected := []byte{0x82, 0x41, 0x3b, 0x42, 0x27, 0xb2, 0x7b, 0xfe,
+		0xd3, 0x0e, 0x42, 0x50, 0x8a, 0x87, 0x7d, 0x73,
+		0xa0, 0xf9, 0xe4, 0xd5, 0x8a, 0x74, 0xa8, 0x53,
+		0xc1, 0x2e, 0xc4, 0x13, 0x26, 0xd3, 0xec, 0xdc,
+	}
+	result := HChaCha20(&[8]uint32{
+		binary.LittleEndian.Uint32(key[0:4]),
+		binary.LittleEndian.Uint32(key[4:8]),
+		binary.LittleEndian.Uint32(key[8:12]),
+		binary.LittleEndian.Uint32(key[12:16]),
+		binary.LittleEndian.Uint32(key[16:20]),
+		binary.LittleEndian.Uint32(key[20:24]),
+		binary.LittleEndian.Uint32(key[24:28]),
+		binary.LittleEndian.Uint32(key[28:32]),
+	}, &[4]uint32{
+		binary.LittleEndian.Uint32(nonce[0:4]),
+		binary.LittleEndian.Uint32(nonce[4:8]),
+		binary.LittleEndian.Uint32(nonce[8:12]),
+		binary.LittleEndian.Uint32(nonce[12:16]),
+	})
+	for i := 0; i < 8; i++ {
+		want := binary.LittleEndian.Uint32(expected[i*4 : (i+1)*4])
+		if got := result[i]; got != want {
+			t.Errorf("word %d incorrect: want 0x%x, got 0x%x", i, want, got)
+		}
+	}
+}

+ 578 - 0
psiphon/common/crypto/internal/chacha20/vectors_test.go

@@ -0,0 +1,578 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package chacha20
+
+// Test vectors for ChaCha20 implementations.
+
+type testCase struct {
+	length int
+	nonce  [3]uint32
+	key    [8]uint32
+	input  string
+	output string
+}
+
+var testVectors = [...]testCase{
+	{
+		length: 0,
+		nonce:  [3]uint32{0x94d13317, 0x6b6a2b3, 0x3ffe0036},
+		key:    [8]uint32{0x9da8a3b6, 0x3abf4ae6, 0xa2f19cae, 0x1068c707, 0x72e4801e, 0xce165d92, 0x61e7028f, 0x82ac3d57},
+		input:  "",
+		output: "",
+	},
+	{
+		length: 5,
+		nonce:  [3]uint32{0x469fadd, 0xee3fcc1e, 0x45cf77b0},
+		key:    [8]uint32{0x3477e02b, 0x45bf809f, 0x27f4a1fa, 0xdb901de8, 0xd8a190dc, 0x1d2c21d4, 0x87bdf2ac, 0xdfbf0000},
+		input:  "23dbad0780",
+		output: "415a3e498d",
+	},
+	{
+		length: 9,
+		nonce:  [3]uint32{0x512a6b49, 0x8df9af6d, 0x5336a2a5},
+		key:    [8]uint32{0xe9124c25, 0x4fd1a373, 0x7945f7bb, 0xeed5f064, 0x29c4185d, 0x3c9acf13, 0x4c94a367, 0x7c2c2c53},
+		input:  "f518831fab69c054a6",
+		output: "cfe40f63f81391484b",
+	},
+	{
+		length: 12,
+		nonce:  [3]uint32{0xca697a9e, 0x6b2f6717, 0xb7859220},
+		key:    [8]uint32{0xfc825020, 0x5ca4410b, 0x7d5285d0, 0x160a1c9d, 0x15470b41, 0x3634742a, 0xe64aa7fa, 0xca0be67a},
+		input:  "805fad1d62951537aeed9859",
+		output: "47bd303f93c3ce04bce44710",
+	},
+	{
+		length: 14,
+		nonce:  [3]uint32{0xcded3db3, 0x35770a7f, 0x6aede9b},
+		key:    [8]uint32{0x44632def, 0xa5e420a7, 0xfc12a8f, 0x63b79a15, 0x337de314, 0xb82fbf16, 0x3104bc57, 0x677c9227},
+		input:  "f4e8a7577affb841cf48392cf5df",
+		output: "f445c0fb7e3d5bfdab47090ddee6",
+	},
+	{
+		length: 15,
+		nonce:  [3]uint32{0x348a50b1, 0x4acc9280, 0x8d6014ce},
+		key:    [8]uint32{0x34bd31a8, 0x2808f47e, 0x9d8b19f9, 0x4df59683, 0x31584348, 0x34a74a45, 0xde174a2, 0x29d4c7dc},
+		input:  "1179b71ec4dc34bd812f742b5a0b27",
+		output: "cc7f80f333c647d6e592e4f7ecc834",
+	},
+	{
+		length: 20,
+		nonce:  [3]uint32{0xc8754703, 0x9188c521, 0xac8ce8a6},
+		key:    [8]uint32{0xe93c79ed, 0xce89162b, 0x116a8366, 0xecdc657f, 0x5bc81d98, 0xff5d2f52, 0x171f3ebb, 0x50773f2f},
+		input:  "7bd94943d55392d0311c413ac755ce0347872ba3",
+		output: "c43665de15136af232675d9d5dbbeca77f3c542a",
+	},
+	{
+		length: 21,
+		nonce:  [3]uint32{0x9a8655cb, 0x6e9d6ea5, 0x5dad705e},
+		key:    [8]uint32{0x3542d5b3, 0x1f7bfd8f, 0x1038abf8, 0x7214e8ec, 0xedd05693, 0x60e663bd, 0xe8e5d506, 0xeea923a2},
+		input:  "1505f669acc5ad9aaa0e993ba8c24e744d13655e1f",
+		output: "26cad1ccf4cf4c49b267ab7be10bc2ffa3ba66bc86",
+	},
+	{
+		length: 25,
+		nonce:  [3]uint32{0x3f202ca4, 0x63fc86, 0x7260a10e},
+		key:    [8]uint32{0xe28ab1d6, 0xe83b3d47, 0x671271ca, 0xb977bcff, 0xa2f64476, 0x311d79b4, 0x180d91d0, 0xec1a6e0c},
+		input:  "20070523ddb4ebf0d5f20fd95aacf47fb269ebadda6879638a",
+		output: "5ce972624cb2b7e7c28f5b865ba08c887911b4f5e361830a4b",
+	},
+	{
+		length: 31,
+		nonce:  [3]uint32{0xcf8671ea, 0x8d72df2f, 0x8b5a538a},
+		key:    [8]uint32{0xe46ca2bb, 0xd06ab5ef, 0xb0e2966b, 0x54dd0c2d, 0x8815d89a, 0x426c30a9, 0x15b0f1e, 0x254bae75},
+		input:  "d10f8050c1186f92e26f351db36490d82ea677498562d8d4f487a0a4058adf",
+		output: "f30c11bc553b2baf6870760d735680897c9fee168f976b2a33ef395fdbd4fc",
+	},
+	{
+		length: 34,
+		nonce:  [3]uint32{0xd1be983a, 0xf5aa389, 0xfa26c7e1},
+		key:    [8]uint32{0x795c6da7, 0x8cb1aadc, 0xa042359a, 0x95ea2e27, 0x128253c4, 0xaabc592f, 0x391e810, 0xf641d971},
+		input:  "e88dc380b7d45a4a762c34f310199587867516fac4a2634022b96a9f862e17714d17",
+		output: "aac98ba3821399e55a5eab5862f7f1bfc63637d700125878c2b17151f306c9aec80e",
+	},
+	{
+		length: 34,
+		nonce:  [3]uint32{0x98f5f4b8, 0x3f181d73, 0x5bf4572e},
+		key:    [8]uint32{0xa86f8cf7, 0x8db41a2b, 0xe0e03156, 0x3dad8a59, 0xb3e4d1ba, 0x75f6fb38, 0xdb94709d, 0xc3db34f3},
+		input:  "b0fcf0a731e2902787309697db2384e1cda07b60002c95355a4e261fb601f034b2b3",
+		output: "b6c8c40ddda029a70a21c25f724cc90c43f6edc407055683572a9f5e9690a1d571bb",
+	},
+	{
+		length: 40,
+		nonce:  [3]uint32{0x7289ae18, 0x7ebe7e50, 0x7d819176},
+		key:    [8]uint32{0x336c07a0, 0x4a2ea22b, 0xa8872f46, 0xa47b5e28, 0xbe645e3f, 0x371c6591, 0xd2dc237a, 0x92c59580},
+		input:  "cf9ec6fa3f0a67488adb5598a48ed916729a1e416d206f9675dfa9fd6585793f274f363bbca348b3",
+		output: "bb7ed8a199aa329dcd18736ce705804ffae8c3e2ba341ae907f94f4672d57175df25d28e16962fd6",
+	},
+	{
+		length: 47,
+		nonce:  [3]uint32{0xfd3181de, 0x8b193e26, 0xbebc799},
+		key:    [8]uint32{0x781a4c2e, 0x27ab55e2, 0x814aaf43, 0xa0bab01, 0x9de62ce0, 0x472b03d2, 0xdfee18e8, 0x8b855b93},
+		input:  "be9a8211d68642310724eda3dd02f63fcc03a101d9564b0ecee6f4ecececcb0099bb26aabee46b1a2c0416b4ac269e",
+		output: "3152f317cf3626e26d02cff9392619ea02e22115b6d43d6dd2e1177c6bb3cb71c4a90c3d13b63c43e03605ec98d9a1",
+	},
+	{
+		length: 51,
+		nonce:  [3]uint32{0x27b02ff6, 0xa510613e, 0x218b22d8},
+		key:    [8]uint32{0x62fc7732, 0xcef06cf4, 0xa4f45ed5, 0x2f96654f, 0x9f2b956e, 0x42b572f4, 0x5bb59c86, 0x35e4784f},
+		input:  "495343a257250f8970f791f493b89d10edba89806b88aaaeb3b5aefd078ba7b765746164bce653f5e6c096dd8499fb76d97d77",
+		output: "62c01f426581551b5b16e8b1a3a23c86bcdd189ab695dbea4bf811a14741e6ebbb0261ef8ae47778a6be7e0ef11697b891412c",
+	},
+	{
+		length: 52,
+		nonce:  [3]uint32{0x9db97a63, 0xff50248, 0xf2b6df56},
+		key:    [8]uint32{0x2b657a8f, 0xfe67575d, 0xaa56d261, 0x30179a97, 0xaefcfff1, 0x9b8eb698, 0x1efe3756, 0xb4ea450c},
+		input:  "e37fbbd3fe37ce5a99d18e5dcb0dafe7adf8b596528708f7d310569ab44c251377f7363a390c653965e0cb8dd217464b3d8f79c1",
+		output: "b07d4c56fb83a49e8d9fc992e1230bb5086fecbd828cdbc7353f61b1a3cec0baf9c5bf67c9da06b49469a999ba3b37916ec125be",
+	},
+	{
+		length: 56,
+		nonce:  [3]uint32{0xc1dfec38, 0x7d7503d3, 0x9a3e3c66},
+		key:    [8]uint32{0x8614d8e7, 0xde9b0413, 0x2a48b4fa, 0xcbbde744, 0xad5ddc5e, 0x9144d83e, 0x74d9d617, 0x230bdb45},
+		input:  "9efab614388a7d99102bcc901e3623d31fd9dd9d3c3338d086f69c13e7aa8653f9ce76e722e5a6a8cbbbee067a6cb9c59aa9b4b4c518bbed",
+		output: "829d9fe74b7a4b3aeb04580b41d38a156ffbebba5d49ad55d1b0370f25abcd41221304941ad8e0d5095e15fbd839295bf1e7a509a807c005",
+	},
+	{
+		length: 63,
+		nonce:  [3]uint32{0xc7e2521c, 0x795499b4, 0xc7946cd7},
+		key:    [8]uint32{0x53fce774, 0x9a4b53bf, 0x5f614134, 0xa3c39414, 0xa8a07c72, 0x93242311, 0x43aeec99, 0x216deb5a},
+		input:  "03b5d7ab4bd8c9a4f47ec122cbeb595bd1a0d58de3bb3dcc66c4e288f29622d6863e846fdfb27a90740feb03a4761c6017250bc0f129cc65d19680ab9d6970",
+		output: "83db55d9eb441a909268311da67d432c732ad6bda0a0dae710d1bce040b91269deb558a68ced4aa5760ca0b9c5efc84e725f297bdbdadbc368bea4e20261c5",
+	},
+	{
+		length: 66,
+		nonce:  [3]uint32{0x1d41f0a1, 0x7c3b7778, 0x6991eea5},
+		key:    [8]uint32{0x1f213e39, 0x56261d14, 0x15fc7c2c, 0x21feccc5, 0xa95684c5, 0x26600506, 0xdadcc06b, 0xf2c810b0},
+		input:  "2f4da518578a2a82c8c855155645838ca431cdf35d9f8562f256746150580ca1c74f79b3e9ae78224573da8b47a4b3cc63fbed8d4e831a6b4d796c124d87c78a66e5",
+		output: "6fc086ded3d1d5566577ccd9971e713c1126ec52d3894f09ab701116c7b5abda959cbb207f4468eb7b6a6b7e1b6d2bc6047f337499d63522f256ee751b91f84f70b6",
+	},
+	{
+		length: 72,
+		nonce:  [3]uint32{0x749f022c, 0xa021dab0, 0x648c2252},
+		key:    [8]uint32{0xa1ace7b0, 0x567a0ea1, 0x52af13b9, 0xcba30c08, 0xe07a6d74, 0x5c3bca39, 0x85b2ac07, 0x3b5afc0},
+		input:  "55739a1738b4a4028021b21549e2661b050e3d830ad9a56f57bfcaca3e0f72051b9ca92411840061083e5e45124d8425061ab26c632ac7852118411ac80026da946357c630f27225",
+		output: "8051bf98f8f2617e159ba205a9342ab700973dd045e09321805eed89e419f37f3211c5aa82666b9a097270babc26d3bfe0c990fe245ae982a31f23cfbf6156b5c8cfb77f340e2bf5",
+	},
+	{
+		length: 74,
+		nonce:  [3]uint32{0x23c16ba8, 0x9fd1cd4e, 0xcb224ecb},
+		key:    [8]uint32{0xb694404a, 0x86b5f198, 0x10fd1bff, 0x13a84e54, 0xab21e509, 0x7443d764, 0x931b3f1, 0x686e87f2},
+		input:  "7ffd8d5970fdee613eeae531d1c673fd379d64b0b6bfedd010433b080b561038f7f266fa7e15d7d8e10d23f21b9d7724bb200b0f58b9250483e784f4a6555d09c234e8d1c549ebb76a8e",
+		output: "c173617e36ea20ce04c490803b2098bd4f1ff4b31fdca1c51c6475ade83892c5f12731652d5774631d55ae2938617a5e9462bb6083328a23a4fba52de50ca9075586f2efc22aae56e3a8",
+	},
+	{
+		length: 81,
+		nonce:  [3]uint32{0xd65f6f29, 0xf3f76219, 0x9a033c9e},
+		key:    [8]uint32{0xeba017c4, 0x69e0421a, 0x449e2317, 0x29858a11, 0xd0c8523a, 0xa8b0c9a2, 0xab2ca84, 0xaf011a45},
+		input:  "7a5766097562361cfaeac5b8a6175e1ceeeda30aec5e354df4302e7700ea48c505da9fdc57874da879480ecfea9c6c8904f330cbac5e27f296b33b667fea483348f031bef761d0b8e318a8132caa7a5943",
+		output: "5e9fbf427c4f0fcf44db3180ea47d923f52bee933a985543622eff70e2b3f5c673be8e05cd7acbcadd8593da454c60d5f19131e61730a73b9c0f87e3921ee5a591a086446b2a0fadd8a4bc7b49a8e83764",
+	},
+	{
+		length: 88,
+		nonce:  [3]uint32{0xc70ee56e, 0xe58ec41, 0xafd96f61},
+		key:    [8]uint32{0x172af2bb, 0x9085d27c, 0x8ca2c44d, 0x8aa148da, 0x290c88b0, 0x88187439, 0x18d54781, 0x633f2cce},
+		input:  "0777c02a2900052d9b79f38387d2c234108a2ad066cbf7df6ea6acc5a3f86b3d6156abb5b18ad4ecf79e171383a1897e64a95ecdbba6aa3f1c7c12fe31283629ff547cb113a826cb348a7c10507cc645fa2eb97b5f22e44d",
+		output: "368c90db3464ba488340b1960e9f75d2c3b5b392bdd5622ff70e85e6d00b1e6a996ba3978ce64f8f2b5a9a90576c8f32b908233e15d2f443cccc98af87745c93c8056603407a3fb37ce0c1f8ab6384cc37c69c98bfecf337",
+	},
+	{
+		length: 92,
+		nonce:  [3]uint32{0x3006da79, 0x2748051d, 0x72c17cdc},
+		key:    [8]uint32{0x60cdb7e8, 0xcecbe928, 0xe19b7ab9, 0x30d61537, 0xa0fbc199, 0x897738bf, 0xdd7705a9, 0x3e5c1763},
+		input:  "cf2dccbcfd781c030376f9019d841ca701cb54a1791f50f50bee0c2bf178182603a4712b5916eebd5001595c3f48283f1ba097ce2e7bf94f2b7fa957ce776e14a7a570093be2de386ececbd6525e72c5970c3e7d35974b8f0b831fbc",
+		output: "7c92b8c75e6eb8675229660cedcb10334965a7737cde7336512d9eff846c670d1fa8f8a427ea4f43e66be609466711fd241ccff7d3f049bda3a2394e5aa2108abc80e859611dbd3c7ba2d044a3ececa4980dd65e823dd110fea7a548",
+	},
+	{
+		length: 96,
+		nonce:  [3]uint32{0xfc0fb1ee, 0x414cc60a, 0x4144bd67},
+		key:    [8]uint32{0x103291c6, 0x822b03b6, 0xd29ab548, 0xc88f3efe, 0x6936056a, 0x28aaa61f, 0xa0df7858, 0xdaa23519},
+		input:  "e08a8949a1bfd6a8c1186b431b6ad59b106ae5552821db69b66dc03fbc4a2b970dcf9c7da4f5082572bc978f8ee27c554c8884b5a450b36d70453348cd6cac9b80c9900cf98a4088803f564bb1281d24507b2f61ba737c8145c71b50eb0f6dfc",
+		output: "73d043acf9dcd758c7299bd1fd1f4100d61ff77d339e279bfbe6f9233b0d9afa24992a9c1c7a19545d469fdfb369c201322f6fe8c633fcdcffef31032bfb41b9fb55506e301d049fd447d61f974a713debeaed886f486a98efd3d6c3f25fbb30",
+	},
+	{
+		length: 103,
+		nonce:  [3]uint32{0xc2030c57, 0x1e3b59e1, 0x607ede1a},
+		key:    [8]uint32{0xd1bac2b5, 0x56a94583, 0x628b479b, 0x3056a51e, 0x69bf8f8f, 0x2df1e03d, 0x4b9d48d2, 0x7df5c379},
+		input:  "a0c302120111f00c99cff7d839cdf43207a7e2f73d5dd888daa00d84254db0e621a72493480420c9c61ce1cfc54188ff525bb7a0e6c1cd298f598973a1de9fd2d79a21401588775b0adbe261ba4e4f79a894d1bd5835b5924d09ba32ef03cb4bc0bd6eb4ee4274",
+		output: "bc714bd7d8399beedc238f7ddeb0b99d94ad6bf8bf54548a3e4b90a76aa5673c91db6482591e8ff9126e1412bce56d52a4c2d89f22c29858e24482f177abacef428d0ae1779f0ae0778c44f9f02fe474da93c35c615b5fad29eca697978891f426714441317f2b",
+	},
+	{
+		length: 109,
+		nonce:  [3]uint32{0xf44dc81f, 0xcf6e03e7, 0xf4966796},
+		key:    [8]uint32{0xd7b12f4, 0x683f4789, 0xc7828fb4, 0x820fc6a0, 0xc51231eb, 0xe46716d7, 0x4036ef93, 0x26afb96c},
+		input:  "ebce290c03c7cb65d053918ba2da0256dc700b337b8c124c43d5da4746888ca78387feea1a3a72c5e249d3d93a1907977dd4009699a15be5da2ca89c60e971c8df5d4553b61b710d92d3453dea595a0e45ae1e093f02ea70608b7b32f9c6aadc661a052f9b14c03ea0117a3192",
+		output: "cbb8c4ec827a1123c1141327c594d4a8b0b4a74b0008115bb9ec4275db3a8e5529a4f145551af29c473764cbaa0794b2d1eb1066f32a07fd39f5f3fe51498c46fba5310ae7c3664571d6a851e673ded3badc25e426f9c6038724779aa6d2d8ec3f54865f7df612e25575635ab5",
+	},
+	{
+		length: 115,
+		nonce:  [3]uint32{0x8d3e461b, 0x7e05c360, 0x3bbbafdd},
+		key:    [8]uint32{0xf9b917c9, 0x9af89bf7, 0x7decbbc9, 0xe7e5ea7b, 0x9b4aab55, 0x90eff6be, 0xa19b6d90, 0xb9f69b1a},
+		input:  "275c97de985aa265332065ccce437770b110737a77dea62137a5d6cb62e9cb8b504d34334a58a71aba153d9b86f21377467b2fafaf54829331bf2ce0009acb37842b7a4b5f152aab650a393153f1ed479abc21f7a6fe205b9852ff2f7f3a0e3bfe76ca9770efada4e29e06db0569a99d08648e",
+		output: "b225aa01d5c438d572deaea51ac12c0c694e0f9dc0ed2884a98e5e2943d52bb4692d7d8f12486de12d0559087e8c09e4f2d5b74e350838aa2bd36023032ccbcae56be75c6a17c59583d81a1fd60e305af5053ac89f753c9347f3040e48405232dc8428c49dcb3d9b899145f5b3bc955f34dbbe",
+	},
+	{
+		length: 119,
+		nonce:  [3]uint32{0x871f33f5, 0xe4fee3ba, 0xcb8c1e93},
+		key:    [8]uint32{0x33124903, 0x7e0287e5, 0xe9d6988f, 0x1962405f, 0x5f21c1b5, 0x2ac695e6, 0x46b200c9, 0x9fda98ba},
+		input:  "ceda15cfffd53ccebe31b5886facd863f6166e02ec65f46f54148860a5c2702e34fd204d881af6055952690cd1ffa8ba4d0e297cc165d981b371932adb935398c987baff335108c5e77f2e5dd5e1ca9a017bc376cbdbe3c0f45e079c212e8986b438444e79cd37927c1479f45c9e75b0076cc9f8679011",
+		output: "a3f1c3f885583b999c85cd118e2ababfa5a2de0c8eb28aacc161b1efee89d8de36ddeb584174c0e92011b8d667cb64009049976082072e6262933dbf7b14839805e1face375b7cbb54f9828ba1ed8aa55634ec5d72b6351feff4d77a3a22b34203b02e096f5e5f9ae9ad6a9dd16c57ce6d94dcc8873d18",
+	},
+	{
+		length: 120,
+		nonce:  [3]uint32{0xef553ce8, 0xdfe120ea, 0x9a047e3a},
+		key:    [8]uint32{0xbef479c1, 0x59554f8b, 0xbf97f089, 0x52316f1e, 0x141e428, 0xff26dc04, 0xe10c8f57, 0xa7568a59},
+		input:  "799bb2d634406753416b3a2b67513293a0b3496ef5b2d019758dedaaac2edd72502fc4a375b3f0d4237bc16b0e3d47e7ddc315c6aef3a23fcae2eb3a6083bc7ac4fd1b5bf0025cc1cb266b40234b77db762c747d3a7b27956cf3a4cf72320fb60c0d0713fa60b37a6cb5b21a599e79d0f06a5b7201aeb5d2",
+		output: "e84dfb3dbaac364085497aeabd197db852d3140c0c07f5f10e5c144c1fe26a50a9877649e88c6fe04283f4b7590a8d0d042ef577693f76f706e31c4979437590fe0ab03d89afb089d1be50ae173ea5458810372838eceac53bf4bac792735d8149e548efb432e236da92bf3168bbcf36f644c23efb478a4e",
+	},
+	{
+		length: 123,
+		nonce:  [3]uint32{0xd98124a0, 0x78cd80aa, 0x3dc55cfc},
+		key:    [8]uint32{0x2286e41, 0xf13e38e3, 0xf735476b, 0x33c44bfc, 0xd7978797, 0x4a9c4595, 0x6080413, 0x1299fdd8},
+		input:  "b2d060bd173955f44ee01b8bfcf0a6fad017c3517e4e8c8da728379f6d54471c955615e2b1effe4ce3d0139df225223c361be1cac416ade10a749c5da324563696dae8272577e44e8588cd5306bff0bfbdb32af3ac7cbc78be24b51baf4d5e47cf8f1d6b0a63ed9359da45c3e7297b2314028848f5816feab885e2",
+		output: "ffa4aa66dd5d39694ae64696bfa96f771accef68f195456ad815751e25c47ed4f27b436f1b3e3fcaa3e0d04133b53559c100cd633ced3d4321fc56225c85d2443727bce40434455aa4c1f3e6768c0fe58ad88b3a928313d41a7629f1ce874d2c8bcf822ebdaebfd9d95a31bb62daab5385eb8eefe026e8cbf1ff7a",
+	},
+	{
+		length: 127,
+		nonce:  [3]uint32{0x53106b0f, 0xdf11fd81, 0x69d1b6f3},
+		key:    [8]uint32{0x736b138, 0x55cde194, 0xf8273c1, 0xf7c268e6, 0x61362bd5, 0xbb3cb455, 0x44d3c9fc, 0x7d56d3fd},
+		input:  "4f0171d7309493a349530940feece3c6200693f9cff38924114d53f723d090fffa3c80731b5ca989d3e924d1fa14266632cb9ab879e1a36df22dc9f8d1dadea229db72fded0c42187c38b9fa263c20e5fb5b4aa80eb90e8616e36d9b8c613b371c402343823184ecad3532058a46cf9e7ea5a9ecad043ac3028cbcc3f36d32",
+		output: "88c773ff34b23e691e14018ba1b2bd48a4a6979b377eb0d68336ce6192dcd5177e6b4f1c4bea2df90af56b35fe2a1d6279d253c0194dcbca9bf136f92d69165b216e4c9d1ce6b3fbe40c71e32c3f4088de352732d0e2bad9c16fd0bb9bde3d6c30257ce063432d09f19da79d49aa7641124a6c9e3f09449e911edbae11a053",
+	},
+	{
+		length: 130,
+		nonce:  [3]uint32{0x5e90ffbd, 0xa898f173, 0x269f9a88},
+		key:    [8]uint32{0x5244e05f, 0xf9adbe9b, 0x9e9f54ac, 0x23460046, 0x6782cdea, 0xba982c96, 0xc721506b, 0xed10f7e3},
+		input:  "8f8d9e18d3212bd20b96d75c06d1a63622fd83d13f79d542e45996135368772ea81511302a0d87e246dd346314cfe019bae8a5c97f567f12d82aca98dfea397c6a47dd0c419f1c609d9c52dcfcbe7eee68b2635954206ed592b7081442ce9ce3187d10ccd41cc856fb924b011f817c676c9419f52a2938c7af5f76755a75eb065411",
+		output: "4e130c5df384b9c3c84aa38a744260735e93783da0337ade99f777e692c5ea276ac4cc65880b4ae9c3b96888760fdddb74bc2e2694bedf1ee6f14619c8015f951ba81b274b466e318d09defe80bdbed57bc213ac4631d2eb14c8e348181d60f6295ceee1e9231ae047830ef4778ff66146621b76974773b5d11c8e17a476450f46ef",
+	},
+	{
+		length: 130,
+		nonce:  [3]uint32{0x308e39e8, 0x9aa4f14f, 0xf511db96},
+		key:    [8]uint32{0x833b5219, 0x4b82e588, 0x4b2d652c, 0x7c8f6ed7, 0xfe4be863, 0x9d3a50e5, 0xb888099b, 0x9f8d1968},
+		input:  "30d2379dd3ceae612182576f9acf6de505ab5a9445fe1a86ae75c5c29429e11c50fd9ec657b29b173a3763b1e171b5a7da1803ba5d64fccb2d32cb7788be194dbca00c3c91774c4c4c8ede48c1027d7cc8b387101a4fe5e44a1d9693b2f627626025072806083aadbced91c9711a0171f52ffb8ed5596cf34130022398c8a1da99c7",
+		output: "b1e8da34ad0189038ee24673979b405ef73fdbdd6f376f800031d64005a4ebed51a37f2180571223848decbea6dd22b198ab9560d7edc047c5d69183dc69b5fca346911d25cb2a1a9f830dc6382ad0024e8c3eef3aa2d155abcfe43bff01956a5e20a862fbed5c5e8df8eed0601a120caac634b068314e221f175baa11ae29002bb9",
+	},
+	{
+		length: 135,
+		nonce:  [3]uint32{0xa5feca5a, 0x753ac1b4, 0xc5a46609},
+		key:    [8]uint32{0xabbf4859, 0x828d9bf6, 0xf7f7aa6d, 0x25208ca2, 0xd7a4c0ad, 0x2fdd3282, 0x2bfcb8c2, 0x8389d84b},
+		input:  "d9404ccdcc8ef128a1b1ace4f9f1669d274ec82aa914cac34b83ac00b236478fd6167e96ec658850c6c139eb0f6fc0dd7191ba9a39828032008f7f37eb9a8df9d6cdd54240e600efe7fc49a674000c5030d825b2c5c96d0f19b8ecdbf4eeb86d3e569c5e3131abc7d6359dd4255284ccacf150d42e7a899536d51ee6db329654a4581c5ac6e419",
+		output: "c5534b5fb40b4834300e9577a9d87440c5272263d06e6aee84aa92cdf5d1b033145d336f26e5fe55c09a7e75753af93d0786dfc1cb435e86c67bd3ec8e766d0801b99e68691e2c3c3ffec539cf62e68285ea9027daa2716cd6f97e8eb7b9e266357a25eb2d4839a829508a6b7228f2832b3cd998f77597ae530430e6e4ecb53eb9efe456863a04",
+	},
+	{
+		length: 135,
+		nonce:  [3]uint32{0x12aa5846, 0x88604f6c, 0xc10d9585},
+		key:    [8]uint32{0x1491ccd6, 0x602f559d, 0xd4080c06, 0x202fabd, 0xffd3f4f8, 0xbf144c17, 0x88bf3f3c, 0x8083375},
+		input:  "231765f832927461f338aceb0f4cf51fd8469348c69c549c1dec7333d4aa4968c1ed58b65ab3fe3d0562600a2b076d56fd9ef91f589752e0455dd1d2e614cacfc0d757a11a4a2264bd38f23d3cca108632201b4f6c3b06477467726dde0c2f3aee01d66d788247663f1d0e66b044da9393ede27b9905b44115b067914961bdade85a2eca2844e1",
+		output: "1dd35f3f774f66d88cb7c2b23820ee078a093d0d85f86c4f103d869f93e2dbdd8a7cb8f101084fe1d7281a71754ec9aac5eb4fca8c365b24ed80e695caace1a8781a5a225938b50b8be96d0499752fdabd4f50d0b6ce396c6e2ca45308d1f2cc5a2a2361a8ca7a334e6ee62d466d74a1b0bf5b352f4ef6d8f8c589b733748bd3d7cda593243fab",
+	},
+	{
+		length: 140,
+		nonce:  [3]uint32{0x1c9d70f0, 0xa088a367, 0x4ec24d2b},
+		key:    [8]uint32{0x494e9775, 0xd07a852, 0xaf8af24a, 0xc65b825c, 0xc5e06780, 0x17fbbace, 0x651d71b5, 0xf548d8ef},
+		input:  "e46841f12d98aeb7710b9162d342895a971b0e3a499886bbb6aa74dc744a28d89a54542b628acdc2f693cb7c03f73fc3b74069bc3f2d000a145fb8a806cdc7d6fa971da09a33b92851cc3d1f6f5646d7fa2b1d564876feefeb63b6e66dba1c0b86ca345235bb822e0f93132346840d2a3d6eb1b541178ea51affc7b31f8da02732cc4e5bcb5d8683ae0a91c9",
+		output: "1dcbfd0bb2b905656c52bd7b1bcdad9b4d434ae9ac221a0d3a316115cdd4a463fa9b3444d2612a4e277d0dcd881fa6e80e59e5a54e35e1a14747aed31edf4ac24214f9d9c329ebe2157620b64efaded9976549bc4aa100d5c15be3f85f700f8a21dfe77590dfee2de9a23cc1ed1e44f32ebf68ca289b097bc13b42802dc7c75309c4afc25b5741839f7db3d5",
+	},
+	{
+		length: 144,
+		nonce:  [3]uint32{0x23067b8b, 0x5b276c6d, 0xaeca6c60},
+		key:    [8]uint32{0x29d64488, 0x893a2973, 0x32e3b4ef, 0x2af3d5d4, 0x95ec01b, 0xc805b64c, 0x884e8b7d, 0x798d7062},
+		input:  "e98e4a9550bdd29e4106f0cc8669dcc646a69438408e9a72c7cdb9b9d437b5f7a13fcb197629541c55bca1f8972a80cd1c1f591a0e24f977cdeb84763eab2648e42286e6473ea95e3a6a43b07a32b6a6cd80fe007ba0cf7f5ac7e651431f5e72690ec52a7134f9757daf0d8eff6b831a229db4ab8288f6bbf81e16fedebe621fd1737c8792cfd15fb3040f4f6a4cbc1e",
+		output: "5c69cf522c058790a3bc38979e172b60e71f7896d362d754edc1668d4f388b3fc0acdf40786d2f34886e107a142b1e724b9b9b171cb0e38fd78b35f8ac5269d74296c39c9f8628d848f57af9d8525a33f19021db2b9c64ba113171ebb3882075019ec7e77b51ce80b063ed41d48dad481d9536c030002a75d15c1c10ce0ec3ff17bc483f8416055a99b53035f4b6ea60",
+	},
+	{
+		length: 148,
+		nonce:  [3]uint32{0x2b079658, 0xbdf5da85, 0x8a75450d},
+		key:    [8]uint32{0x49c9eaa3, 0x62048819, 0x9baacfa5, 0x3870addc, 0x5c682e1, 0xf4f9fff3, 0xa3848e4b, 0xac1ebc1},
+		input:  "ce0f0d900dd0d31749d08631ec59f216a1391f66a73bae81d3b0e2919a461bc9a14d6a01b827e3bcb55bbccf27c1ed574157e6becd5cf47181a73c9d3e865ab48a20551027e560e965876b0e1a256bfa5cb5179bf54bd8ec65e5570e374b853b37bf4b3ef1ec612d288ebc19275fa88da9419e012f957f9b6a7e375b3377db0eb3619c731aebfeb0930772b4020d3a3e90723e72",
+		output: "b06981b57fe184091ef9f8ccf522a5bcdb59bf9a68a3ddb817fdd999a6ecf81053a602141cf1b17017bae592b6b6e64756631a2b29a9e1b4f877c8b2ae30f71bc921e4f34b6f9cd8e587c57a30245f80e95005d0f18f5114400785140e6743da352d921fb4a74632a9c40115ad7706263ac9b41a11609fa0c42fc00f8d60931976162598df63ebad9496dd8943d25a03fa47475c",
+	},
+	{
+		length: 148,
+		nonce:  [3]uint32{0x98e8ab8, 0x84d8e77b, 0xbb305841},
+		key:    [8]uint32{0x46b5f93c, 0xc8b2778d, 0x2cc5278f, 0xd2a3904c, 0x6ce5d4f, 0xc4459e8, 0x4a35c30, 0x2feadc02},
+		input:  "eccfd66bdc691478f354b8423d6a3f20932a1f591d8e6cefa734975fb8ee6881b6dc92c0d1d5ed54fd1999efd7f11ac697a1f130587dd895eb498c9a8fc7d1714c385ec156ecae3bdea2a3462834245e724531d0fedda2b77693a53ed7354b758e875b23cfc83219a091fb2076e7a88cd77f779ed96f8d81ffa3fe5059303ac706086494b9f2982f4f88a0c6fadc3748625004db",
+		output: "925529047d4177b72bf50905ba77e47608815522c1829b24046e439d5451901257903a5409fb910373167e8b7f4fdfa543a477608ddfc11bbd1efc138366961463b9915b302a346b795dd593f6fcf4fa73529b6fe83079552aabbe99474a72806f59688d826675fa7f6649b9f5307e5028853c9821b8c4a1a0fc4bfdc7c8c78b25aeaba2b5821d17b36317381a3bd578917d2504",
+	},
+	{
+		length: 152,
+		nonce:  [3]uint32{0x2e2a6e4a, 0x9a6d488a, 0xf9966cb6},
+		key:    [8]uint32{0x58903bff, 0xc2be173f, 0xe26128b5, 0xb6b6af53, 0x92f8eeb, 0x38cf3336, 0x7fdf90fb, 0x7ae24b37},
+		input:  "f0c7139c69413869bca980d7f192b2bc3f57e34ca4f26164e1a54a234e84e1aa285cc02cfbaef3dfba2dbb52a555ec1f6ef0e89d0b2f0bd1846e65b74444b5f003a7308965e67bed558689be2668ca10ca368fac072e0e4535a031af23b3c37c561e185872b86c9bceddb5c1199e43fb5f735384766d33710460b541b52d3f5b6c108c08e76724bcac7ad2d866a8bbeeea92a3d867660d2e",
+		output: "d2c16c7a242b493038203daec65960de384c030eb698ef6a53c36eabb7556cbfa4770eaa8bc0a2b385ad97495eeb1c03ff4e6efcb804aefa81c177dc62700a9eefe6e8dd10cff5d43a2f47463cab5eb1ee260c3826cac9bfa070f1e0435541a89ebd224d13cc43f8fff12f38091c2b3f2102d5c20d8b1c3ae4f129364bbe9f9ce2147dcf0639668ddb90dffe6a50f939f53fa7ba358e913f",
+	},
+	{
+		length: 155,
+		nonce:  [3]uint32{0x243e0198, 0x884448c, 0x9a31e760},
+		key:    [8]uint32{0x37e017bc, 0x9b1e2e90, 0x15679daa, 0xf94a23ee, 0xda86dfe, 0xc3eea84c, 0xdd199799, 0x6eeffb92},
+		input:  "7024974ebf3f66e25631c0699bcc057be0af06bc60d81a7131acaa620a998e15f385c4eaf51ff1e0a81ae5c6a7442d28a3cdc8aeb9701055e75d39ecac35f1e0ac9f9affb6f9197c0066bf39338a2286316e9d1bb7464398e411da1507c470d64f88d11d86d09e6958fa856583ace697f4ee4edc82618662cb3c5380cb4ce7f01c770aab3467d6367c409a83e447c36768a92fc78f9cbe5698c11e",
+		output: "ff56a3a6e3867588c753260b320c301ce80de8c406545fdd69025abc21ce7430cba6b4f4a08ad3d95dc09be50e67beeff20d1983a98b9cb544b91165f9a0a5b803a66c4e21bd3a10b463b7c1f565e66064f7019362290c77238d72b0ea1e264c0939d76799843439b9f09e220982eb1dc075d449412f838709428a6b8975db25163c58f40bf320514abf7a685150d37a98bac8b34ccb5245edb551",
+	},
+	{
+		length: 160,
+		nonce:  [3]uint32{0xd24e866d, 0xc59d25d8, 0xfcf623f1},
+		key:    [8]uint32{0x5f32cca0, 0x4167cac5, 0xc04943ee, 0x507fa1ec, 0xad8fdfc0, 0x6266fa2d, 0x22f05341, 0x8074143e},
+		input:  "8d79329cf647e966fde65a57fc959223c745801c55312046b791671773cca0af4cd48ead1f316eba0da44aa5d18025eced0c9ed97abaabb24570d89b5b00c179dca15dbae89c0b12bb9e67028e3ae4d6065041b76e508706bec36517a135554d8e6ef7cf3b613cbf894bec65d4dc4e8cb5ca8734ad397238e1e5f528fa11181a57dc71cc3d8c29f3aba45f746b1e8c7faace119c9ba23a05fffd9022c6c85260",
+		output: "60aea840869f7be6fcc5584b87f43d7ba91ed2d246a8f0a58e82c5153772a9561bdf08e31a0a974f8a057b04a238feb014403cd5ffe9cf231db292199198271f9793c9202387f0835a1e1dc24f85dd86cb34608923783fd38226244a2dd745071b27d49cbffebea80d9dacad1578c09852406aa15250de58d6d09cf50c3fcfff3313fac92c8dad5cb0a61ccc02c91cecee3f628e30c666698edecf81831e55ec",
+	},
+	{
+		length: 167,
+		nonce:  [3]uint32{0x30b61047, 0x810cf901, 0x4d681524},
+		key:    [8]uint32{0xe51476d0, 0xdf98008d, 0x59dfe69e, 0xdb39166, 0x6c1e4a4a, 0xfb76165e, 0x5180f185, 0x7359fb35},
+		input:  "85484293a843d2d80b72924b7972dfa97cbe5b8c6bcc096f4d5b38956eb3f13f47b02b0f759ea37014ecdecfb55f2707ef6d7e81fd4973c92b0043eac160aaf90a4f32b83067b708a08b48db7c5900d87e4f2f62b932cf0981de72b4feea50a5eb00e39429c374698cbe5b86cf3e1fc313a6156a1559f73c5bac146ceaaaf3ccf81917c3fdd0b639d57cf19ab5bc98295fff3c779242f8be486ba348bd757ba920ca6579be2156",
+		output: "bb1650260ef2e86d96d39170f355411b6561082dcc763df0e018fdea8f10e9dc48489fb7a075f7f84260aecc10abcfadbc6e1cd26924b25dedb1cc887ada49bb4e3e02006bdd39098ef404c1c320fb3b294ded3e82b3920c8798727badfb0d63853138c29cf1ebf1759423a1457b3d2c252acf0d1cde8165f01c0b2266297e688ff03756d1b06cb79a2cc3ba649d161b8d9ef1f8fb792bd823c4eabb7fb799393f4106ab324d98",
+	},
+	{
+		length: 172,
+		nonce:  [3]uint32{0x42020cbe, 0xad62af90, 0x29e53cd},
+		key:    [8]uint32{0xabad2095, 0x601ec477, 0x3bc923a1, 0x1edede1a, 0x33612355, 0x285b4858, 0xd3fd6714, 0xe0f4bcc3},
+		input:  "a2fc6e1b5281a4e0330eecd1ab4c41670570423173255979953142b78733b2910fa5540e8294208df6ae4f18672d5ac65acf851bcd394e1932db13c81b21e6f165e5538aff862e46126c650bbe055e54b31c78f2f0221d2631d66ef6d3f4c5ae25eada043b74d8770e2c29799c0954d8ccbd17766b79e6e94e88f478db3566a20cb890846917591a07738328d5c05f7ed4695a82607660f1239661faa9af0368aeb89726f13c2aaecf0deaf7",
+		output: "d8fe402a641c388522842385de98be60f87d922c318215947d4b7562d4ca1e2dbc7ee86494e65fb0bfddfdebdb2ae6469312f95b32c722b2720d64bb8d7cc3dd82f9055b1d89f05b77984f91f94ba4ac79c5129cd7c91cc751b0defc3f2799518e372d27aa683f1e7bbd4f55414c48fe8a3a37ac1f179a1a329cda775aec0d31d75a5a38addb1de67c06bddbedf4c8d87abc18c9f9dd072d457ea29ad4dfb109ce7e99a4a82fbe330b0afbb5",
+	},
+	{
+		length: 176,
+		nonce:  [3]uint32{0xa8021c8f, 0x667a02c4, 0x7a68b693},
+		key:    [8]uint32{0xece401c8, 0xfa805a47, 0x6d572fca, 0x9c1c780c, 0x647545e5, 0xd7ef4c11, 0x91dc1e46, 0xba2a694e},
+		input:  "480387bc6d2bbc9e4ced2448d9ec39a4f27abe8cfb46752d773552ad7808a794058962b49e005fef4e403e6a391d1d3f59025eeb5fb8fbbe920f5361862c205d430eac613cd66108f2f2f0bd4d95a8f6ca7bd1f917eaeb388be87d8b7084a2eb98c575034578edf1b3dafff051a59313873a7be78908599e7e1c442d883d3fd3d26787eb7467eed3a3fb2d40046a4460d5d14215565606bcf8b6270af8500e3504d6d27dacf45bace32214472d525fdc",
+		output: "ab81a9c28358dfe12e35a21e96f5f4190afb59214f3cf310c092ab273c63cd73a783d080c7d4db2faccd70d1180b954cd700c0a56b086691e2c2cd735c88e765e2266cd9ebe1830d63df4b34e2611a8abeeca9c8c4fac71135dafb1cb3569540ed1362ddeb744ed62f6fd21de87b836ec2980f165c02506e0c316ae3cf3d18a862954d9781f726ecc1723af4a730ccc6d6de82553450a52499acb58fb2008969401c45b2f20e12b58f308db1d199b4ff",
+	},
+	{
+		length: 176,
+		nonce:  [3]uint32{0x414e687c, 0xc6fc69c2, 0xd3ca12d3},
+		key:    [8]uint32{0x1b51cca, 0xbc8455af, 0x3f904842, 0x6042b452, 0xcd4dd164, 0xda83f3f0, 0xff04b972, 0xf972dd0e},
+		input:  "b274e61059f3215173ae226e30a92ee4b4f8a3da95f2e768e3fac2e54ddac92c200c525f190403a6ef9d13c0661c6a7e52ed14c73b821c9680f1f29711f28a6f3163cf762742ed9474dbea51ff94503a5a404adbbdfbf4c6041e57cb14ea90945dc6cb095a52a1c57c69c5f62ac1a91cd8784b925666335bbfee331820b5f7470bc566f8bbb303366aafe75d77c4df5de2649ed55b2e5e514c3cb9f632b567594a0cf02ec6089a950dbe00554ee4dfb9",
+		output: "a0969730d48ee881792a3927b2f5d279aba9f2ed01e6b31b92d0e1fb8ba7f35a236d838e0ce5f8654957167de864f324c870864b4e7450a6050cd4950aa35e5a1a34a595e88dd6f6396300aff285de369691b6e0e894106dc5b31525e4539c1e56df3ceedbbab1e85da8c0914e816270a4bae3af294b04a3ea6e9ef7e2aab4da5f5370df2706b5e3f000d88179ac756deaa652a1cc85e80ad9622f1bf91a2776262eb7289846d44f7f8192e763cb37aa",
+	},
+	{
+		length: 183,
+		nonce:  [3]uint32{0xdd315c1d, 0x2335da98, 0xe0a0da0f},
+		key:    [8]uint32{0x6419c7d6, 0xd340f42, 0x7af2f4b8, 0x3536cf42, 0x2f68c6fb, 0xac9d855f, 0x7c4d490, 0x9711b1b1},
+		input:  "ee849039c6cd972dc943d2a4468844d130c0150276f4e0889047e2300c3ecc6792c4527bfe9437dad877eb986e6b1aa9b867d1798c9d314243f0a87ec9ee5b601c2554876c87cbf50df3334a077c4152f8b8fef4a2d301ddbfa90c887ece757c3eb6c4fc1e0212d6b5a8bb038acaec28cba064c9b34f5364cb7f0fc2ac4ef2c7ddde0f5ba17014459eaa78f08a46a01882ebf7c6e409dadda250bb899dc8b3b70e160bbcb4412a9963b174d0fc6bc16383a46ffaacb6e0",
+		output: "3e272ded9c0a5cebe7cf17ac03f69eb20f62996e047501b6cc3c8691ddb2780ea72c21a81888bfea96e4373a412c55ca95648390de740102d661143043baec3976230e024477d134b8504a223c36a215b34164c9e9e1fa99a49fdc56f2f04ea525a6b82997d9bbc95c4b5baeab4dec50061efb7c1a757887acb8b47b142e0a2e61885a2c14c4642d83d718a0546b90699adc545a48129603862a1c89d8e665cde54b3ba487754db6d6f5acf6a4b95693cc569577a2dc48",
+	},
+	{
+		length: 185,
+		nonce:  [3]uint32{0xebb44f7c, 0xaf14c7dd, 0x4543cd7a},
+		key:    [8]uint32{0xce71977, 0x99790e86, 0x6510d6dc, 0x37968ae7, 0x2917fb9a, 0x19ef25f, 0xd282d085, 0x6128d043},
+		input:  "0992396a6f29b861dd0bc256e1d1b7dce88435733506a6aa20c62e43afa542d1c46e28b2e6d8e2eacb7c08db05e356fe404684b0e3a9849596db82eb788aa09258c28eb19e9838f757425b4edef12deeca56e30cf030272e325d4246d6e083219b2f965124963ca91f066d47bf5a8282a011a78b0155aa70038259a4a59135f241fd2f88c908b9f4eef7b7df0f3a1c16a52c009b522f89dabd52601bbf6e3ce68732e1a6d444469480f06da218786cf6c9666362e7a7f7be12",
+		output: "545c05a84b5a4fffd1dd623c8f2b11443818560bdb0c26dadd3b694d4790d294b99059f4127b7cca122c4000954d745af96094ff4623f60db33e994bb6903263d775f48d7047427b3a498c2ecde65bd37bcb8ee7e240a1e08c884c0079cab518f4e1c38ba5ea547f4da83b7c6036e4259bee91c42e8fae895df07781cc166f1d50e1550a88ee0244bb2950070714dd80a891aa8a9f0580a67a35cb44609b82a5cc7235f16deea2c4f3667f2c2b33e8eeef944e1abdc25e48fa",
+	},
+	{
+		length: 187,
+		nonce:  [3]uint32{0x35cb7190, 0x212e9a86, 0xbc423ce4},
+		key:    [8]uint32{0xfa19cede, 0x576ae8f2, 0x58055dab, 0x91b3355d, 0x69d2501a, 0x736323c2, 0x266c1385, 0x134f4557},
+		input:  "3b9efcbbb607fad5e9f1263dad014cc5c2617d439fcd980408f4f9a93acb1a33d1c3a22f38c037e4603dfbbfb5571bc08c4a1958cbbf510e3e4dd19007fe15fad7808369149a9c4db7ca0496f7a600a6f2454ee1cffd5a68d45c270e4b53ac9b77f33a1ffbb1804244f57d2b05b8036fe2cda9efead3d4eff074ea5c07128e0b354a4a11ffa179163933bc6bd10d200804cc93b64575746e94e975f990bddcc8a4335e99e2459fbe9bc0e004ffcd6cac52f48ef55cc0637a75c1dc",
+		output: "631ba7301e33236da2477506ea98d3b732447389e849b68e1f09bd5fd814f40dc3247a1012fa654f08e3dda0c104ee2dff12ecf5cb018644de50d70dfb6c8cc1f5f552e5f1e50466bbb538ad6b98fd37f33fe615c326efc9cc97899b829b007f91569fa9b28ce0076c53daedf9cc0f838e22cf1125b86a6a2c2eb4a45dadea45ad00fb4f054e7d6b09c13ab1dd5328debfbf4f1b70af2b8a5b1d02df8a87d7661473e0c180ba4c815f14db87c5bdc15f11a29d8e0ce3d747d5dcd4",
+	},
+	{
+		length: 191,
+		nonce:  [3]uint32{0xccc941ac, 0xdba45b02, 0xab0d7ad6},
+		key:    [8]uint32{0x9b750752, 0xa627090a, 0x967c95f0, 0xf8ff2c3f, 0x69beb97e, 0xa30b99c1, 0xadddc83, 0x443f9baf},
+		input:  "f28a71efd95e963e5e0bc0fcf04d8768ce93cb55dc73c32e6496022e214596314b7f843f5c7b136a371c2776a0bfbdd534dccbe7f55e9d3d3b5e938f2d7e74393e4caf6c38fa4b05c948e31dc6a9126817fa3d7892c478f75ab9f6ab85c0e12091bd06e89c7d3ca8d9dcdd4c21fead3d769a253919c2c72dd068474ea322b7e71cafa31684e05a63e179e6432fb70661792cc626a5060cec9e506b35d9286f15dc53cc220b1826314eec337dd8e7af688e5950b2316c30516620569ea65aab",
+		output: "1bcea54b1bf4e6e17f87e0d16388abe49b988b9c785b31f67f49f2ca4011ecd2ad5283d52ef707dd3b803e73a17663b5bfa9027710e045a0da4237f77a725cf92792b178575456de731b2971718937dd0e9ea12558c3fa06e80bbf769e9799f7470db5b91476d6175f1a6d8e974fd505854c1230b252bb892a318e6d0c24dcc9ecb4861769cd746abab58805bc41c6086a6d22b951fba57b00c5b78f6dcb2831715b9d4d788b11c06086f1d6e6279cd130bc752218d7836abc77d255a9e7a1",
+	},
+	{
+		length: 198,
+		nonce:  [3]uint32{0x987e7c58, 0xcc839a94, 0x30952e60},
+		key:    [8]uint32{0xe34a286f, 0x4adcd996, 0x97168712, 0xa82dde8, 0x14249e5, 0x5e82810b, 0xb4a445e8, 0x9579adb0},
+		input:  "c1d1ede73bd89b7c3d4ea43b7d49c065a99f789c57452670d1f92f04f2e26f4f5325c825f545016c854f2db2b3448f3dc00afe37c547d0740223515de57fd7a0861b00acfb39931dc9b1681035d69702183e4b9c6559fb8196acbf80b45e8cc5348b638c6d12cea11f6ef3cc370073c5467d0e077d2fb75e6bf89cea9e93e5cf9612862219ca743ef1696783140d833cd2147d8821a33310e3a49360cb26e393b3fee6dba08fcda38d1b7e2310ec1f715e3d8fa0c6b5e291eea07c25afd5c82759a834a89cc5",
+		output: "11a8493cdc495c179f0d29c2b4672997205a9080f596ee3c80d79b55162b1c875ac18eb94bf2a9e05b08024f524a1e9665912394a330c593d23260e6bdf87620c10a48f678693196fb744c49054182fba667c601e7b7ebf0f068e8d69ba004b804fda616a4a0d5350e1a3bd424b8266462be282308219c578569aefc1ccd09ecdf5da283356c9e524e14e69d25b0e19643dab26f54373a7272b43755c3f1ddaee6c5fb9e8e093110c41697e95f73a68c75454e050239197c9fbd8cec76698bd11894ebf6e2b2",
+	},
+	{
+		length: 204,
+		nonce:  [3]uint32{0x851f025a, 0xe6f3c800, 0x85ae7530},
+		key:    [8]uint32{0x2d0dbe47, 0xda05e465, 0x42f6b3b2, 0x7026e79e, 0x9e446680, 0x691df976, 0xf7b23da2, 0xbb3421fa},
+		input:  "37b2dc4b6a5203d3a753d2aeffcdaed5a7c1741ed04d755dd6325902128f63b6981f93c8cc540f678987f0ddb13aae6965abb975a565f0769528e2bc8c6c19d66b8934f2a39f1234f5a5e16f8f0e47789cd3042ca24d7e1d4ddb9f69d6a96e4fd648673a3a7e988a0730229512382caaded327b6bbbbd00a35df681aca21b186bc7ac3356d50889bbf891839a22bb85db4c00bfa43717b26699c485892eb5e16d1034b08d3afa61f3b5f798af502bba33d7281f2f1942b18fb733ca983244e57963615a43b64184f00a5e220",
+		output: "b68c7a2a1c8d8c8a03fc33495199c432726b9a1500bc5b0f8034ce32c3e3a78c42c1078e087665bd93c72a41df6bfa4e5beb63e3d3226aeeba686128229a584fab0c8c074a65cef417ad06ab1565675a41cf06bb0fb38f51204eccccb75edd724cdd16b1d65a272f939c01508f0385ca55ac68a0e145806317cc12e6848b1124943a6b2d99a8c92083fc5f31ab2e7354db3f8f2d783dbf1cfec9c54f8bfcb93d6f28ef66f18f19b0fab8836458e9b09bee742ba936cb2b747dd9dcf97ca7f6c82bf0af6f1b433592d65143fe",
+	},
+	{
+		length: 210,
+		nonce:  [3]uint32{0xaebfd97f, 0xf583442d, 0x15ab2f1f},
+		key:    [8]uint32{0xd3d1cf9b, 0xe43187e6, 0x5071a757, 0x412a83b4, 0x3f27716f, 0x17fdc488, 0x271f77ed, 0x6c4bb056},
+		input:  "68c2c5612912b5f994172720130dff092ee85a2c1395111efa64d5a281ca864d3db9600e685854d81c6de7e8747b92fb7c4c2efa829d3d4c0c9fc9d689e2e5c84c9eae8ba4ab536fb6c7523124b9e9f2997f0b36e05fb16163d6952eee066dd22fb7585925ffded0204cc76818bcead0d1f8095ca2cf9cd1ddcd0361b9c9451940e14332dac4e870e8b2af57f8c55996447e2a8c9d548255fe3ed6c08aedaf05bb599743ecb0df8655152bbb162a52e3f21bea51cb8bf29f6df8525eb1aa9f2dd73cd3d99f4cca31f90c05316a146aab2b5b",
+		output: "d0ae327fa3c4d6270a2750b1125145bdeef8ab5d0a11662c25372e56f368c82c6f5fc99115a06a5968f22ffe1e4c3034c231614dd6304e6853090c5940b4d1f7905ef4588356d16d903199186167fec57e3d5ce72c900fe1330a389200ed61eec0bdc3672554f1588ec342961bf4be874139b95df66431178d1d10b178e11fcbd26963ff589d5d5faf301b7774a56bbfa836112a6ea9c3026ebdd051085f9131132c2700674bef6e6c2c5b96aace94eb2ba6c0e0aef0eefa88995e742ca51ac50af83683b801b7c2c5af4880e2b344cc5564",
+	},
+	{
+		length: 216,
+		nonce:  [3]uint32{0xf9e973b8, 0x2485a6a7, 0x2ea7dee6},
+		key:    [8]uint32{0x96edef11, 0x8cf57f26, 0xb6e3a83c, 0x9ef434c6, 0x4607ea48, 0xace87e4d, 0xa0d87475, 0x3a9c9458},
+		input:  "fed3d1efa309c8b50cb9da02b95167f3b77c76e0f213490a404f049270a9c105158160357b7922e6be78bc014053360534add61c2052265d9d1985022af6c2327cf2d565e9cef25a13202577948c01edc22337dc4c45defe6adbfb36385b2766e4fa7e9059b23754b1bad52e42fce76c87782918c5911f57a9394a565620d4b2d46716aa6b2ba73e9c4001298c77bfdca6e9f7df8c20807fa71278bd11d6c318ed323584978ad345c9d383b9186db3bd9cec6d128f43ff89998f315dd07fa56e2230c89d803c1c000a1b749107a3159a54398dac37487d9a",
+		output: "6a95fba06be8147a269599bccda0ce8f5c693398a83738512e972808ec2f25bc72402d4bcd1bc808cc7772b6e863b0e49d1d70c58fcf4fcaa442215eeb3a4648ade085177b4e7a0b0e2198f0acf5465c97bd63f93781db3f0b9a0a184c3e06a76c4793a13923f83b2242b62511c2edff00b5304584cbe317c538de23785d2504fae8faabee81c5315298186ce3dcbf63370d1ccd9efec718cbc90b3d2e0b0b6aefb3a9b31e4311f8f518be22fdc2b0f00e79a283701c53f6936dd63734ecb24480d5365d1a81392498faf9a1ddee577007acc5f8c87895be",
+	},
+	{
+		length: 217,
+		nonce:  [3]uint32{0xe3bd4c44, 0xa3b75a31, 0xfe92010f},
+		key:    [8]uint32{0xdd05ab8b, 0x5ac7cd1, 0xb8113720, 0x53524706, 0x8e0ceea1, 0x52eb23e7, 0x1c85730b, 0xb33914d5},
+		input:  "d776bee5625d29a2ebf6fec4df94d2b9ac62e8e7c56704fd38a87ee932b787cbc555621535e76ea30183cb0ee30604f485b541f45feb8c01b9750d37fded5cdffbbc34fb90fdc9c7c7ddf949a1d50b796f1ea5db437238c7fb83c4b22c9e491f75b33d84746f1cd10bfda56851b8514ff0ded0adfd5092a66a85202d06bd967485d06a2c56011110da74bf40b6e59f61b0273164744da02ce2b285d5c3f03aee79eea4d4503e517177692ed3bb035071d77fc1b95c97a4d6cc0d41462ae4a357edf478d457c4805fa586515614697e647e19271091d5734d90",
+		output: "60e9b2dd15da511770162345251edfb15cea929fb79285a42f6c616dfde6befc77f252e653b2d7902a403032fc4ce4934620931a2ec952a8d0f14bf1c0b65cc287b23c2300999ed993446eb416749bf0c9c7dfe60181903e5d78a92d85e7a46b5e1f824c6004d851810b0875ec7b4083e7d861aabdd251b255b3f1fd1ee64619a17d97fde45c5704ab1ef28242d607d9501709a3ac28ee7d91a3aac00cd7f27eb9e7feaf7279962b9d3468bb4367e8e725ecf168a2e1af0b0dc5ca3f5a205b8a7a2aae6534edd224efa2cf1a9cd113b372577decaaf83c1afd",
+	},
+	{
+		length: 218,
+		nonce:  [3]uint32{0xcdabfd50, 0xd10d5b99, 0x9e160a85},
+		key:    [8]uint32{0x8231a4e9, 0x89f33c8b, 0xf96b11b, 0x853cae9d, 0xf6624a33, 0xee9523ee, 0x28bb7853, 0x688ac6f8},
+		input:  "4f57848ff5398e61bcafd4d4609bcd616ef109c0f5aa826c84f0e5055d475c6a3a90f978a38d0bd773df153179465ab6402b2c03a4bf43de1f7516eb8626d057ae1ab455316dd87f7636b15762a9e46a332645648b707b139e609b377165207bb501b8bccfa05f1bf0084631c648279afdf51c26798899777812de520f6a6f0d3c7f3ef866982f5d57f9c8d81c9a4eabb036651e8055a43c23a7f558b893dd66d8534bf8d179d8aa7d9e8987cfdaaa7b5a9381ba9c79d5c1161b1bdbd30defdd304ee07f19b7ba829a0d5b40a04b42edd6407b68399caac69069",
+		output: "e096cc68956ed16d2dea1154a259e01647913eeea488be0b54bd1816c781a35e161772ae1f7a26b82e864ade297a51cc9be518641b2e5f195b557ec6fc183e4e5c1fc01d84fe6ca75e5b073af8339427569b1b8ee7fcff0ffa5e7e6237987c40deec0abf091c06a3b28469c8f955fc72e4f3727557f78e8606123e0639dff782e954d55e236448f4223ff6301accda9f8fa6cd43a8d6381e5dde61851a5aec0f23aeca7262659bc793ce71fa7992f80e44611ae080b7d36066e5c75c30851306f0af514591d4d5034ecdf0d6c704bfdf85473f86141c9eb59377",
+	},
+	{
+		length: 219,
+		nonce:  [3]uint32{0x67de323f, 0xa0442ac9, 0x9d77b1d9},
+		key:    [8]uint32{0xca8d33d4, 0x834349d9, 0x5e68d581, 0x99a7c30e, 0xdc7f6038, 0x697e8b63, 0x284c2ece, 0xee3e3bfa},
+		input:  "046a61c0f09dcbf3e3af52fab8bbcded365092fad817b66ed8ca6603b649780ed812af0150adbc8b988c43a6ada564a70df677661aff7b9f380d62977d8180d2506c63637c0585dcef6fe3f7a2cf3bbb7b3d0df7769f04bf0f2e3af9439ab7615c304b32055aea0fc060890beb34fa9f90084814b6ed7363b400dfc52ee87925c5b4a14a98e3b50c7f65adc48c89ddd6414626c5e0bdefabab85c4a0e012243e682d4931be413af62fd7123ab7e7774fcae7e423bf1d3a31d036195437e9ea8f38aa40182daa9aacf3c9f3d90cc0050977c6065c9a46bcca6ba745",
+		output: "cd5a6a263e3ee50dda0e34c614b94c3ec1b14b99a2f4095a6b5715fdfc3449fcdf8a09d1ae02d4c52e5e638f1ee87a4a629f99f15a23dd06718792f24285f5a415e40f698752c697ee81f2f9248da1506ce04a7f489f8e2b02e6834671a2da79acc1cdfb78ea01822d09a1c4a87ffa44e56c4f85f97507044cf946ccb6a2e06e2917bac013f608d75ee78fa422a5efc9c569226bf7068d4705fde3a9fad2030256db0acf9a1d12666e0acf9f5346ad62e5af4c01a008d67ab1224b3e98278d073116ff966cdc779fb3aff985ec9411a3eefa042d71dd4ae5b15d5e",
+	},
+	{
+		length: 221,
+		nonce:  [3]uint32{0xa36a3d5a, 0x1747a05f, 0x5440eb4},
+		key:    [8]uint32{0x2d701ee6, 0x143d5a1a, 0xbb67b9ab, 0xabc88ccc, 0x20baad8f, 0x6507e48b, 0xdb1e1b39, 0x9e521d80},
+		input:  "af516216f74a6344cbe458cbba820f7e25c0b10aa84b790da2ee6317e059171076d7246c2878be83fc00c200d546c007f849e4c163d52c7b0da31beff4abff481be3266b92e668cf4dd1c84d9d7b3e5191dcd6ddb51d17d337621046e83e9ac035fccfb239648bc3c6fd340fbb50707e5a33b3ef439d292192d0e4bc727690c61450e5a28789e5ea50e746bc66d039493e080fb70e9ae06d89004cb71de8178941c422f1e9862492fc9149a4864ff52b1277b9f5a63c2f16e9adb5263cf65a034a62ebb0f1a385d2681c87a35f1c45670b4edef1c68fe9544fcf411d95",
+		output: "b22ffd8f0e549bd3e0206d7f01ff222f92d39b41cf995a331d5ef0cf5c24bcc3ddb36e64d351b5755400246fe4989b5f912e18daa46cdd33e52dafbd2872f16e94220b56315d72c1dbb1525fd34831d7202970c11711ff36de3fc479407c34fef0aea86e172f9beb0f393194355b9dd59625639f4a6bf72ba571c229f2fb053c1114e82793deb2dfe8232f1a327949689d2fb2820662dcd2a39a2546c7df12b3ff7e87e58c74badf568cddebd3c558f0f7874c834c4b8aa988653f138ec79620f5e3ed737690928a30f981dca9f2920ac7307607063b40f87c204de47c",
+	},
+	{
+		length: 223,
+		nonce:  [3]uint32{0xb92be022, 0x1e1257c7, 0xad7c01e},
+		key:    [8]uint32{0xca1dbb9c, 0xaadb9504, 0x77b8a95c, 0xc50deb5e, 0x2dbc0fb8, 0x9e654bc2, 0x94d8925a, 0xfe9cfb66},
+		input:  "a3d70bdb509f10bb28a8caab96db61652467cf4d8e608ee365699d6148d4e84d5d93bdabe29aa4f0bc8ee155f0b1fb73293c5293929eaacdd070e770c7cccfb2de120b0c3811abeeddaf77b7214a375ca67d618a5d169bb274a477421d71a651cfb9370bcf7e0d38f913754c11002089cf6cd6a8de1c8a937fb216591d57b37efdf3797f280773950f7eddeb9c3385c8315ff5ff581c64610a86ada7ff6a1657e262df94892dff9fdfb6e958d101f4c26296470c138dc4e1ca4bb565b3ff877a7f78b3d11d64b7c24e27ba6f6b06f6e368f5ac218cd5d11b815ab0987678eb",
+		output: "646314264896a6e25601e536f6e783d465b2ead1e0be4422bc9cc8eacabae4a749ad533eb28091be8397328dcfb34c92006bbda930ab070ed7b806095bb1c8f476350e7b08ffbd4d7d6055c8defaa8deff9d54f5215c2d7db27ce09e08f5d87a859145ea3126e2a01882921c3fddef3985bd451bca44063258390aec8ec725b07d064314fe43a9c83e9287b47616dfefbf539b82da209aa08a6d3176b7e3b4be4a17d44e581280a684e4a64414649bfcea82b541729f8178b580e8b972a89f5b8c4f9b68205e9396d8ae5e81873b61da074080fd44c52d50fb0880ee9c35da",
+	},
+	{
+		length: 224,
+		nonce:  [3]uint32{0x5091927, 0x661c75ba, 0xc23dad},
+		key:    [8]uint32{0x2e00499d, 0xafdc63db, 0xc3c62efb, 0xb4157a19, 0x84ce8b13, 0x85326279, 0x2ee71e9d, 0x318721e4},
+		input:  "f48b5ae62f9968baa9ba0754276cd8e9dcfa8a88e4571856d483ee857b1e7bc98b4732e81f1b4421a3bf05ab9020d56c573474b2a2ac4a2daf0a7e0c3a692a097e746d12507ba6c47bec1d91d4c7cfc8993c6700c65a0e5f11b1ccd07a04eac41f59b15b085c1e2a38b7d3be9eb7d08984782753ae23acdafbd01ae0065ab9c6d2a2d157c1fc9c49c2444f2e5f9b0f0bbfb055cc04e29b2658b85d414b448a5b62d32af9a1e115d3d396387d4bb97ba656a9202f868b32353cc05f15ae46cbe983d47b78ba73d2578a94d149e2c64a48d0c1a04fc68baf34c24b641ea0b7a800",
+		output: "b9af1016275eaff9905356292944168c3fe5fdffd9e4494eb33d539b34546680936c664420769204e91ead32c2bb33a8b4868b563174d1a46108b9dfe6d9ac6cc1e975f9662c8473b14950cbc9bc2c08de19d5d0653bb460bea37b4c20a9ab118a9550bfeb1b4892a3ff774e8efe3656adcdf48239f96e844d242525ee9f9559f6a469e920dcb3eaa283a0f31f5dfac3c4fac7befa586ac31bd17f8406f5c4379ba8c3e03a6992a1915afa526d5ed8cc7d5a2605423ece9f4a44f0c41d6dc35a5d2085916ca8cabd85ac257421eb78d73451f69aaedeb4ec57840231436654ce",
+	},
+	{
+		length: 227,
+		nonce:  [3]uint32{0x5d6d997c, 0x9d623987, 0x5742de36},
+		key:    [8]uint32{0x57b2a5ea, 0xc5bdd68b, 0x99c7b0c6, 0x26aea960, 0xba5c75f1, 0xa904cf6b, 0x685031de, 0xa0f0e99},
+		input:  "b39101601efa2ecdf41878b0fd920a3005ce709e4ec2970abb76e32c232ea21069f81b246eda75aace7555ce8ae203455d3723e684bd671389300e353eec0d2f499d10654fafda2e7a69bfca7198eb172249167ca8864b5d5f58d28723090ec86e251a1bac0346d52fd81f06e0c05429e0b2b895588290b7d00878a4da3378eb6c7e61487de2b318fedf68fa7ad7c88ee746827c1f60d98c7716f3f9695c5ffd4670f71a0fa78a1fb554ba482c5de83feaed7c65fc71acc9f541342eb8f7622b12bb2cfa222fa2ddd8b3ed210ce442275afa3132c8a0e17dd504ecbc92525c118952be",
+		output: "50eb5b21c179a03b9a822f0075906a3ce4acc32486139f92635c7d834f69071d5a6dc0e15ed06a5cee37147071d59641d140a82ad5815b954e7f28e080c3dbbeaf13943d7b7c66d49d51ba1132eeadd4cb7a7e7d726d08d95f1578d55519f267f753f3e16ff39504a87b2286d8bfba0fe6bc28887b466bf276453a82cdd0abbbbf08db0e1c26c317d50ad9b8dc09cd621bc566d362024e8404739df6468869d2125c58b25d70e392f5e75924c4341be81c263915bb514ad436fb24c2c67450e84f6d1b72d1a02a3310c07a7814d930264fdbbf5ddca7067e18e8a44faa87169b7f2e35",
+	},
+	{
+		length: 233,
+		nonce:  [3]uint32{0x75bca707, 0x89f6d1f4, 0x2a6f657a},
+		key:    [8]uint32{0x949f42cc, 0x2b5d3c48, 0xfe0be473, 0x17ac92aa, 0xbdc9d9dd, 0x74f9df26, 0x26487508, 0x7c7b41a2},
+		input:  "0a42f63b975ad0e12a1e32615813dfd6f79e53ce011e2a2f0534dd054689f8df73a8326fecfd517ff7fe530d78081af66c3a8c7c189eb9d9efed1e5577b5512d42ef1fe273f670ce380c64bc62e217a7e410a8ed89998344e29301e4e053a3a3cf7e71587fd056a6bd976f16e157476a06997dfaaff32172dd84190570621f2221420c0a0ea607ea756e9792c8c0e7157c95b89c9490e20b750ee85e4c27c9b8f409e848ec90afcad33342010bb9808358afbcb3d9b094127c38c243a204e76899677079758e7cbada9a5c18363449eebc07bab516a16372722403a046df85c7dd2ffc804c54d38aab",
+		output: "87a47bcaa1c1eb8e55151011c4f39af4b9e108a55a7124cdcf66d0dee727306e6971f783b038bd6b215f530cdbb53e17975742ec304fdb3792a88b674504396978c6a5e4a9c87a7c3ca430d61165c1a3f6162eeaf38c93e18b6ccb6a595ad428cdc98efef8f84463eed757a72ffd827b71c0579fcc1f4baa11812be2bc5a2a95df8e41d04b33343df09ce628c367d1f88488f7a2787f013c8e76f0b9257cee777ec4adc6df8c5790e41ea02da85142b777a0d4e7c7157a48118046935f8888b5352d1750bf00b92843027a349cf5685e8a2a2efde16dcf5e1c1ed8c779bb38cabfb42ec4dd87d58273",
+	},
+	{
+		length: 234,
+		nonce:  [3]uint32{0x5003a4f7, 0x40bd8cde, 0xfe35fb25},
+		key:    [8]uint32{0x576e49d9, 0xe84e9df, 0x9f227a3, 0x437c9de0, 0xc46ac8de, 0x1a6a2d2b, 0x42ab7684, 0x4253fbb6},
+		input:  "abeff48fa082dfe78cac33636c421991b0d94c3bc9e5bd6d22763601a55201fa47b09ce60cb959ba107020213c28ae31d54923d1e74ab1d9ddc2762b2d23d8c6961d81068230884a39682fa4b30676ffec19319362c075df0b879a0f083a67b23597bf95c4bb997fae4736479cb8a9c00520ba2f6e5962d54c313c576180d17779ff239ad60f1f1373627770d50a1c49718b2b2e536846299e052f8c1a5d3079e91cb1b8eac4661daac32d73b3b99e2051f8f694a61d1e9d3935f802921a4d979b6ade453cf30d73a4a498a6a2c5395c60fcf271d50b4967ac12b0d7bf818c2679d552e9b3b963f9f789",
+		output: "a0d11e732984ad575570ed51031b8ac2d7b4c536f7e85f6fce9ef5d2b946cefe2ee009227d6747c7d133ba69609f4a1e2253d0eb59d1f930611e0c26a7c0cf2d2ce7ccea6e079eadf2eb1acf0463d90fb4b3269faae3febfc88cb9fb0873d8b74894506199394c8e44a96e6b479bd3e045749cce1c3f57243abdb37e67084eb573cd820c6cee424227019592a027e9da8f7b8997bfb292627a986f83c8fb8d156a91a12a8b52659cf9272924631745ed3a2453a4c2d87a167faa9104e799c715ed597bcb66949ab15dae29a86ba147507e8d8af66e96c09c53caa053ad3b79d9ed3c0c6c00169eaec3a3",
+	},
+	{
+		length: 237,
+		nonce:  [3]uint32{0xc6ae48ce, 0x26f0906f, 0xfd8ab8bf},
+		key:    [8]uint32{0x42b82c50, 0x7f519e0d, 0xcbb95098, 0x6f75e532, 0xe2c9f61b, 0x5a4af942, 0x2679777b, 0x6a8e1c9c},
+		input:  "a77b7a5870335b9145fd2e08ec898ba2f158fda16e8a2661a7a416857b6ba6937b4843ecaa79d3635d28383af80290842de9ca0bb621ee22b7fd6bf379922741e812b1739c33dd6923d0607826fc84d46bbdbd1fe9d1255f56a167779a560a6eed1b9c9579b8f771147df467e67a070d9e9ce8ad92dc0543d1c28216c1dec82614ac5e853ed49b6abac7eb3426ef0c749febce2ca4e589d06ccfc8f9f622ede388282d69ceb2fd5122ba024b7a194da9dffc7acb481eabfcd127e9b854be1da727483452a83d1ca14238a496db89958afd7140dd057773ea9a1eee412875b552d464ba0fab31239c752d7dd3d9",
+		output: "b330c33a511d9809436ab0c4b84253eeda63b095d5e8dc74803de5f070444a0256d21d6c1cf82054a231b43648c3547aa37919b32cfd9893e265b55545be6d7cd11d3f238ef66c3c278fcccb7dd0dc59f57750562cb28da05d86ee30265ff6a3991a466ba7e6208c56fc8862e19ac332e5fb3cbcc84e83a6205dee61a71acd363a3c9de96d54070a69860c152d4ceb9c4b4cc3b878547b6116699885654b11f888dc3c23483a4b24fbe27c52545c06dd80ab7223d4578ab89bff5f9cbf5d55b19611a5251031df5da5060a1f198226c638ab5e8ec5db459e9cd8210f64b2521a2329d79228cc484c5065ef8a1d",
+	},
+	{
+		length: 244,
+		nonce:  [3]uint32{0xea38678b, 0xc41eada, 0x3381147b},
+		key:    [8]uint32{0x268fc2ac, 0x21297e86, 0xdf9ef8cf, 0xd4b45234, 0x2a95c4f2, 0xcec36ce3, 0xd5fa38c9, 0x7dc43790},
+		input:  "322d634bc180458123e10d0509870b54e0f0a3a72a2bd9e9cf44324c7a1ca37dd6adf9db1fcc8dadabd881f91d47d93b58382802b42ee936802fac8612ea4dd9eca5f215935ea9ba6233b9c8bddba3385861de669d95c888c8977851cb305db577a4eb2360f362fa459d61ffc8fcaa1502905b073bd8e9567ac7cff8e5fb1002c55641a3af5fc47ac0131fae372f073e19721ffcce9821e0241d7fa67bfc499c8f100e050d39bd4d7cae4557d208629603ec4a007852762ec1905d0e81b873510fd334dedcd9c288eb8415db505913af06bea94d197ab627d58f6a9944f6c56247595fc54ae3f8604aa37c3466f74561131e11dc",
+		output: "edbfb1090987762f75eba2439d746cdbefe8605b8ebad59e075d28b54edfe48813ccae891f6ed655c5ab5211ba896fff0c8e09bd1554aad987dc53f355d0822e9b0f524a99a79c68a9f3b4e30506cd725b07be135e4540078be88dac64fc545c433837b96a924452f6b844291c4c3fb5f8cc94f06d9f19dad7fc945f093020e82ed19f9eb3ddff68b813629991d1a460e5455e1cb41cf23bb3d96fdb6b96581c3bf9ef72814406329bbbba5b835e7724c728cebe88efcd996dea71d0fd5c53e081c21ce8b3764738d693e390fbf8e0137a716760fc9cd2014cd9bf3fd706bc3464d1f15803606976e96b1077cda0a62921ff7c32",
+	},
+	{
+		length: 250,
+		nonce:  [3]uint32{0x883ac584, 0x8fb8e7d5, 0xdf07de66},
+		key:    [8]uint32{0xc7747e47, 0x853d88c6, 0xbf9aa631, 0x78f16480, 0x7c248080, 0x15ff973b, 0x31528a40, 0x629686e5},
+		input:  "e6b8a9012cdfd2041ab2b65b4e4f1442794fdf1c3685e6622ce70f80b9c2252ba6d9e6384d474a7622053d35df946a3b19408b3e1712da00525070279ce381359b542a9ad7c07750e393e0834593777352c1f7dbc84cc1a2b1eba787377d2cb1d08a7d20e1393d44022107acac5d765be37f9075af02e4bbf8e60ceb262aa34e2b870cc7adcf54329a667249cb4958393bff4f4333338cae45cbca419d59e605aa0cecb1241080339198b9b283e4201afc07360b8ae2a57b0b9b97167c315f03fd7a87a00ae73f91ca560a1505f3cdf04576b9aee5ea775f719916f1e1942ad5311c7f87153f8e62855ace3f34afb08d4d7c7f4fd2bf83e42f76",
+		output: "fc2673c80812d101bca7a2e0e105fa449550e695a016596f5c3cde11fb7dc518b94fdb74058e634546a726c37896110e1d1f9cdeccba1c89958041061ded8e8bc2751ec6dad76a305e70c57f9c81a5a65b5116390af4f7bf7053a03ec13f5d60a58cc5ba61f8c46ef6d2d291de490082dcfdf294aeb3a9414d64e4bd6497d4625acfa591627bfd98f0aec7e7def71515c09942db6911d73b96b4bd2d6df03bb729e945d71549d40e4bc401e1f73baf263a74280537692240638619f92645a5ade1eb8151191c7ff8bd715b3c1cd667e69745b806e16d46d9aa680a7367b8fb45a1598631cf3d44c1f5cfcd95bc8dafdb65a2083905a6937fcf21",
+	},
+	{
+		length: 256,
+		nonce:  [3]uint32{0x79cd7a62, 0xae619be, 0x7d96d236},
+		key:    [8]uint32{0x7dec8e64, 0x9f12b14, 0x6c70df2a, 0xeae0aa0d, 0x27b1ac14, 0x7a00d833, 0xe63c0aca, 0x189438e2},
+		input:  "0cfd93b195e37dd15dfae83132c24ed5bfce7fe6fad4064b213b2c31a39e39ddad2f977e904c9c5b055ed03db46fcdd845bbb6ff0ab5a8c92e89295b6801f36ae63eba61fba24a3858aeb36f2da226b23b24d7b2c7d2670f23a9a1b60db85c0ecee584bef1b00e42d10ca17432a74bbb220d88356d82c850da4c09dd5baf413caf8f9479e02a330065fb865489c0f59605d56146ec8434182345de2d15e2a1dceeeee2fe94871d41913f6788738947ed9849ca0ae985e3e19a97bee82b96feeddceb196c9b6012264661945981c279f43db9599a4ef01116f592478619690daa64387290484d21e8d2444751194e1f361fb37f04014a3c7e4b409e5c828d8990",
+		output: "0502848571d1472ff10bec06c1299fad23a2cb824d88bf91b5447c5139500bd837a2fddc629e4a964e84907c1e6740263f1fef4f5ed41062982c150d9e77a1047b7d86c0e191945e8db00ca3845a39560857fc9e0e4a394eea4ba80a689cb5714c4bab7124ffdbfa8bbb91c3eb3caa1621f49dba1eea3ebf1d547ee337f9085638a12317b86c11aa1525813445107038942fc519eebdc1b98d313ad822bf0b94a054259aa8cf1be4b3a68f974269729941747f9a23fa5d83453071b431dac62274c24f6a32248b0785ff90aad5840fadc89af0aef7553d9352cfb00d3999ffbe28cd9fde7854e95710f4532b8bf5011e518c93361e58d22a2302182e00e8bccd",
+	},
+	{
+		length: 268,
+		nonce:  [3]uint32{0xb7581e00, 0x9a1bba92, 0x64356674},
+		key:    [8]uint32{0xdc2c9fcd, 0x5e50de1a, 0x8546466b, 0xc1b49b21, 0x36a670cd, 0x2887f367, 0x2fbf4300, 0xf90a0374},
+		input:  "0d8d864010ce8df1c0179cf0236dce1c100f9c115eaa5294c24a2e1afa27f9d57ebc18f00482be0218d44262bd4db73002ff53c6388f5e333470aced2a42a73b376686c8d02e05ece27cdd8b1e3f675c715981f8b656d68d0e16227b529cf881d2433e4371fbcd933eaa72346e77e688ac80ee95324512c66a4c16338cf38c941b72c21c3d01e005a07c0eb436014fb1ee61806de7e96842ca3217ab8c7607d609dd2f637f9fda8a85cb0549f262c9e4a955c384319a6ad2b696e2593d7d174f5ddb98e2a8d5d12558c18ab67571e9a0202e91ce26d720cbe41a3a6a4f309296ca4d9d9a59a9043dd2e5a707ed7d5034023d5ea06ab14b39b7852e5c984848d5670c6f2f0b189c2a8a4a4bca",
+		output: "d2a5693c9d503a8821751d085a0837579233e65b691366e4a7464481d22800e786939349f721a815f28b4e47c8889f0814fb95d592d1185e45d6dbcac14ffa4f1d6c79194f2f7eb7323439d9607edf80f01e3a968b483eb93c01d9cb9d3625d21d66927e7aeedc1d9bd589560ed2b61cbed5ad0e0310c8ebe140c64c67d4909c010902d5386efa359ab60a9573493d3e5d8761cfd4023eba23de48372032d4673b5f6ad66cd0dfab02a73aa81f269ae88fcabb3ae9cb09f6bf60fd3575a3046bc6843f444e1e9fb9ff9b991620344fb99da68df09496b40f8b9dfc34e830a87f65710940603ebab554d36e8b4c9228bc9c26c07b828f34cdfdd40b161717236ba325e8c20bd018b324345e09",
+	},
+	{
+		length: 305,
+		nonce:  [3]uint32{0x2c641fcb, 0x5170c7e2, 0x62a23688},
+		key:    [8]uint32{0x5aed5915, 0xc5c4cc18, 0xf0e51574, 0x75d894c6, 0x1b7082d1, 0x5d2ea1db, 0x709fd24, 0xf5f69898},
+		input:  "07c50a69e168e388caf6f91471cf436886a3de58ef2c44795d94fba6538add8d414d84f3ef0ac9377fd5bed6aa6805a695f3a711025550bb6f014893c664e09bd05f4d3b850771991fc02f41c7353cd062156243b67fce9c1f0c21eb73087a5de0db0578923eb49bf87a583351e8441c7b121645bcb64ef5960fdca85af863dca7ebb56662e9707d541513bc91bf9b301431423b552e2c148e66ecfd48045ecb3a940dd65694d7fc8bf511e691b9cfd7547fe7bca6465b72ff9f1748723c4eb14f8bc1efb2fbc6726115c597a3881e0d5019335daf2e5ea8796c2a8b893ca798c4ef2639465505c4bd492bf7e934bb35be9b66c9f35730736c65fa4c1a2485378b9d71912cb924634a8e0db2802b75728818dc00fc28effdf1d8a05e4de4608bb6a78bb19c377d5ec77dca1b5ad38fded7",
+		output: "3dff5fde2ca24bf419e13cb7d12368e70449d41f2aa22e4b567f5cbdbcf3257975e44097deb180f2621ec36acf375dad3b7a19234b9856dc6c7842a7f86be00304b41a8c1662a02e8390346cbd0ff6be7bc1ceb821dbd805ab5c93c9c6ea5093249b5dc52081cbbbe1b326e831ef3c6c42fb791790086d1586f7daf031e70a71b54e9134f942e9ce229fc77980eb80c985ee0c5965eaba375d156f9b423b0615f4ca6fd77de28e28f35aba327e4f1b75725730155b7b4d6c5c264bf3d9dc9a16e7ededcc261add8c666278bac5cf0b3275d6d6678060eae30bbf2ce5f63e6a53a450b65aa0adbd1c90cf045f5ddd9700c2a99c80586c5244cf4c08035b6ff630c82cec3a4fcc83860e987898b42fe746939f8b37c814f8dab65de276e9784fb90f0751d3ba0826889e1e7e4fdbf8a90942",
+	},
+	{
+		length: 430,
+		nonce:  [3]uint32{0x99b172cc, 0x91056d0, 0x48057533},
+		key:    [8]uint32{0xe6cf398e, 0xc3c56066, 0xc5ff194c, 0xf6d2d8c4, 0x6d1d8908, 0x63e62065, 0xcca485cb, 0x1eb03dd6},
+		input:  "3ddcd3c00014747903c95e49f64258615455a0b26c5070a9532382a9bbd18eeb19c9fe1a902f5c6baf544c5938fc256d310a9332223dc3c54a6eb79a4b4091c3b01c798d2800418863f2865c1cd8add760e445588576d4a6c945e1d6d50dc913674daa4737ac94d84eb0ff57cda95df915989c75adc97c4e3c1c837c798a432ba4803a246bb274b032db77e5c1bb554a5342ef2e5d3ff7f102adb5d4e282ad800ccae83f68c4bfd3b6046786a8cfaa2b63c62d64c938189b1039ae1a81ce5c91530772cca0f4a3470ba68e4e0548a221eb4addf91554e603155a4592dc5c338aa0f75a8cc2822b318fbfba4a8f73fa08512132705dae792eed6b809c251d35cca60c476406d964187b63cd59333771e37367671d0ccb393f5b8bde77bebc133485ec5c66bdd631d98cdbee78a3cf435d2f824fa2f9e91e89af28b2e155df4fb04bbe4ce0b6162dcd8e81ee8d5922ebf9c957b26c343a0396d91f6287a4af9e11b7fbb5a5a5c1fcdb186365a20617d4ff5037b0bfa97b6213a6ebcf0b78b81c65737378787b255cba03d715fed4addc2c70c1fb4d3ab16f2bff287186c26a164dae2fe9dbe3c4a2e1617f01cae79f",
+		output: "ecea5fc18dc4aed23359cacb8f79a457512e0a27d9816f353e315519d2b2faf74d14ae8ae5e227b203823998a47a050c363a807f45f610942fed4518b8091b88dff8b2af8fb6552eb654c85d2b6a918bcf56fb898392941d983b1afd867ef840e12313059ed3e4d217498dd511563a939c3c536fbbf8e019deed29262f0a655fc680b15939475e0cee0ce2e8bab5834f7354b93e2e0958a5bc608fab369b6aee3c9d73a6898e402484eac7300150517bbd137bf55762897696a3dc4be74b0c141755ac8f2f6e59f707b1690c451a774c46bbe195d826a6784f8d807b78f8ebc343ecacf37cb9b1b2fdbff6a1237b5098853d783e77515c419894c2628f8b5117042294ee2ed58a33746f9e79b13fdfaa25a75fc95340a89076e786e0ecad7de437a9a3fb3092146d255005b22895310b1252a3e34572cf74665b97f4adc30dd0f34e3216c7757953a4b618a775bbe68f9e0922d75afc80a1379aaf1745f2263afb6f0b37553d9c984f1ef781ea75b1980c559c77565c83f3e0bd7a3cd7cdb594658beb7e5eb940633dbc6ae2f50383beea676cb6c814b17b1d73dd133f544da88ab371415889ead21803c1ffe3f2",
+	},
+	{
+		length: 449,
+		nonce:  [3]uint32{0x2adb4a6d, 0x33d00c1c, 0x10a0193c},
+		key:    [8]uint32{0x8bd707df, 0x70212019, 0xdb685581, 0x9cdbd1a3, 0x7db9ff1a, 0x1af119ee, 0xb1d8c0ff, 0x3c4a22cb},
+		input:  "93ce72a518ae892e00c271a08ead720cc4a32b676016612b5bf2b45d9ae9a27da52e664dbbdf709d9a69ba0506e2c988bb5a587400bca8ae4773bf1f315a8f383826741bfd36afeae5219796f5ce34b229cac71c066988dbcae2cbcfcdbb49efcf335380519669aaf3058e9df7f364bfd66c84703d3faaf8747442bdd35ac98acdc719011d27beba39f62eab8656060df02fab7039223f2a96caac8649bc34da45f6f224f928d69c18b281a9b3065f376858c9fd10f26658ae21f5166a50fe9a0d20739402eec84f5240ee05e61268f34408089e264e7006a59bb63eeaa629ba72603e65718d48e94e244e7b39d21d85848d5f6f417631f3876f51b76b6c264356d7d7b1b27bbac78316c5167b689eff236078cf9e2e4626a4ae8bedeecbcaf6883e2e6e9304969b4fc7a4280dcdc5196267e9bb980e225fcbf7a9b2f7098f7f5c9edd06f50c8791edaf387ff3e85ff7bee1f61e4660fddd4eaf5ab0320508e3ccaa9823ae5a71faa86bd76e16d862d83ed57bf6a13de046a3095a74a10c4da952b3c9b8fbde36048537f76eef631a83d55d3a13096e48f02b96a5a8da74c287a9164ce03ddf2f868e9ca3119ec41f0233792e64086c903eb9247dbae80e923eae",
+		output: "bcf49d62dcd1cff9dc37d7096df0c39031e64ccaeea3830fa485edb71b7fcf2ec709a4b327ef9c7d4ea2b35f113a8485d4c236e06b3baccee30e79c6c08739fe5fbed59db30479b56dfbe584a5d79b169b200430ed27072137e940a34170606b31f22095f2151b4d9b901f6337f991a23e4c8997a1ebf5105361fdade1c889b8dc9565e3b33e0bd608c39d725becbb60da8a797186fe0986736112da3d09906442364d6e253e5b27fd5ad72e877c120ea7a11d42b19948f0df5ddabf9cf661c5ce14b81adc2a95b6b0009ece48922b6a2b6efffdf961be8f8ec1b51ad7cfc5c1bca371f42cdac2389cbddcdc5373b6507cdf3ffc7bfb7e81487a778fcf380b934f7326b131cb568bbaa14c8f427920aa78cc0b323d6ea65260022113e2febfb93dcfce791ab6a18489e9b38de281169f1cd3b35eee0a57ed30533d7411a7e50641a78d2e80db1f872398e4ae49938b8d5aa930c0c0da2182bd176e3df56ab90af3e46cdb862cfc12070bc3bd62d6b0387e4eee66d90c50972427b34acaf2baff9d8a76002a20f43c22ac93686defc68b98b7b707d78d0e7265aabadde32507a67f425cbd16c22a426d56b9892bac3a73dd2d2c03efdb22ecc6483f8d1ca67fc7d5",
+	},
+	{
+		length: 487,
+		nonce:  [3]uint32{0xecf15215, 0x45e31add, 0x56499d31},
+		key:    [8]uint32{0xf5988496, 0x49bcc2df, 0x7b4ba3c3, 0x5d5138be, 0xd6cb466b, 0xe98c82f8, 0x147d3f27, 0xc82389f0},
+		input:  "f72bec13b0f0b6f2317118f14c2a0d8e963b1bd49ae7584e710dbde75bb1e30c79281847cb822a5f3ae4fa56825e511212f17f0d293cfe80f872e6992d304e9283d08ce65ceeacb003b36a862c91282a22536e0b9c19953512a1bf9e20d3e7a8f1a2dff45dec0b9b04c592e88a7814540cf636a024d10008463d0b3aafbc4c9359889149433ef173124866aa6f53526ef3b3f2c630860ecdd08ffd9fc050e95da512cc87f812f9391085cdec5cc87258b8560806a52336d612da7ab05e0f60566b950904aa27c975a48c7d78455728c87f9b53aa4978374ab9592e12c22d9a760e26eb527133534ac5bbf969596b71cde8b4ef3587fa7ffa7116834348c275ad4dce68ab3397521ddc8e54380129cc81b981f9b32db20dddb0ecaa0f1ff7b06495a42b4a800a207b8e9ca38794e2fa9f40546e0e3aef7b5236d7fdadd72b1158714a5ad8d6264df1e75120088e449b9e911eddac59f1f19a795205ab7532783a93159876133b3fe3a518475a545fbe8dd2ac143f33c35d98e3ee13b63606b1e671917ac3ff9412773a3ac47b8c6627b8ba9dde6820f4f16c2ed9cb7d7086cfbb0cf2d7533eff253d14f634ab2aad3fb4289b9a0bb667a6fdd0acd5949185d53f1dd2b96ff060bb44f872a67259100669e6eaf1a7e2b11dd5fc35792db0c44a1127765934a068bf",
+		output: "bb618ae6b7739a4dedde1dbacf864b0892b93dea3007237d2f6f23be0718bdd29321e6b0fcb6a44dacf0f5c53d91e16165997e2302ae7ebc2dbd02c0fd8e8606a4ad13e409a4e807f331cf4174171c5fff23ca232192906b4eefdf2ffb4c65af78be01b0ba7d15b4341dd5a2edd49b17db2812358c8af0a4a9724e0169f50d1d331936bc2400012a60849876c3ead52cc9fe60173c9992f83f3e41ebd24fe3961835109612994c7620280539d483f91ef9a64c16032a35612a119589efe6357fa35b19531274576e304be75bc7e91d58015792095bb00ce4de251a52b946554366ea7ed9ce9317020ec155ae0071e022af36ad10eda5d671e5090c136e381cecdb8bc179474fabc7dab2d8a134772976cf0791b6cebe2333d34b4b8e2b6b2eab2b5dc7c6a08a583d091df64328cbcde36bc1b81095d82c741a1503c55d833d551a855e098166c5efffb8e4146e32e54abcaa85076ca6660abdfca9e82824217b5d3f23f7ff3455872bc76751480c1a8e3e725365c82fc135cd3713cc0f1ea733754142f8c37716a2a4fa8a6b898215c287565325774c2510df6b49e78cb986853ac5ca532c9a7e2bceb7c0157f60433f29fe29009343d6035d7b5892c77f821b644590615dc505604501dd218dcab789e6f0525387919cf25c7c6d62a8979e39d346decbed2657",
+	},
+	{
+		length: 511,
+		nonce:  [3]uint32{0xba68c47, 0xbc020097, 0xbf7d14a7},
+		key:    [8]uint32{0x3bbeedde, 0x6e8f4d6c, 0x6e27cd72, 0x140ff360, 0xc891efa0, 0x4aaa227f, 0x733cfef2, 0x2b51f1f3},
+		input:  "96eb94e1adbcc0646440c8824a2fc0f2c4b17d9cbddbb8ba8d9dbd6482fbf7201c74eb923153e0138b2f6f182f9c3d5656ee40bb7c26a01740b5c7d125261d4e4197614800aa152b402ba581bfbf4288e73c9ef7e7e37491212b921420eaaff880eeb458e3d0aa108b01b53492c97e328e9d10e3220b924351d583c00e76aee9325d6b89b1f162ffa30b386b37b5eaf4dfc25d22987dde4496158818c4d8f19ea300fe140be921d3f1abdaf9ab8946833a57cda5f41f995ff80e98b0f10f7afd736dd33438dfd395547f11563056078ff8f7c202aac262955f0ca5dae2365472de40f069028104ac552ea5a45ff2773335e5d3242f1e62e0e98003333dc51a3c8abbaf368f284536672e55d005b24b7aeba8e4cef23289adc12db2213aa037c797e7e753ae985568199cfe14cf1704fbca443e6036bdd05859e3583897cbefe7a0cf268b75d554b2da6e503ee04b126fbf74eaac0ebca37e84ab9c726973af780fe2bc9869fe67b7d9e4a04062ee535b2c1740d1347224e211b5cd37ee14c3325f40abee930eb6a1634986e756b3a1f86a3d7ee7184d95ea948506d8ab8b23f92ecf3eb0586f7a8b1bc227e08a0e32ca75ca4eeffc5c0a2a623547788bca66f3dc2c48671e462544d52a87d34307a7f111aeacb7da50262deab33d9f29dd6b47c3bb555be598d619cc66be8c4b74b01772725268a43d467f39bc565e5efcd0",
+		output: "590965d18ebdf1a89689662cfae1b8c8a73db8b26941313006b9b9bd6afa6a57149d09a27390b8883069e4fc2dfcf75035def1f8b865e24c21b1a1ed3e9f220d7b48046577b661bc92d9888a912984ad415ea2fc92c9e37da0bef5c7dab11495c612c27b5babe6eee28fd26482272fce69ca7f11bac95251735ad808365ac587830ec04105304f8e440a4da47d30e788718da4282941c9c76f18de4f954b8be750b54cb1145489edf273625a0df9a694a23fe7bfea12579b53c3b2a3de85705568cd7e603f3b8beba9a14cad9979ea283a8a291d3e1105b7f890e2a569804d9b7dd4c7e50bd0dcd11223fd7247af77f04212ece1b98c238d2fa0386a994bc502f83dcdd2e5a0d45b185155e1a395d91726d383c2c198fff1590e983c65ee041638510787c8c59c2e96f31678226a033e027bb40c416b73c3dbef31affc93a659c8ec7ffeca313fd5283a80533b2d63941c8f245d22b160c5fe57c5fa4b759c407b9acd6d9c4f80f244360b9acd11e2b43d4af757e16a6ef9d6756df39ca3a8a235e74351f50b2ebf54df633c8c400fd80b41b07117676d486377095660f2f20f62c034563b4560b473a8f4d6a740306d2a822fd8bd98012a840ba9b1709df9a0d61ecc305f7180fd764e334045d9a8ca23cb8036c05616a8b21fc488429ba4168c59dfa231f0ffa668a3be7b16583df1a55bb9c15d51660ddeca730d66f7a9",
+	},
+	{
+		length: 607,
+		nonce:  [3]uint32{0x9419df54, 0x4593f2a, 0x71c06dd6},
+		key:    [8]uint32{0x7b517740, 0x41e86353, 0xed629408, 0x5fe32cea, 0xb06bc5df, 0xaec9b350, 0xc00c2a6f, 0xb3aaf44f},
+		input:  "be3f309c6e7b89e1ec4a855cf161156d09f8a04d5630534ee19e9e071e3f4603f23f0c59a7b7f8a32c4c203ec8c129a268faba09abde7b61135c6c37fd091e2d695f0e242488098ebed30c7d321f4dcef0bdd23fa85a53569868cf2008bf4d2ee7a12a6673298c7e797321b9f4559748223b590e6fcf17aa72251586b01181cefcd32c6a1a20a0fc27143426f6572b1aab0e7301e390cb857f912d78d5153906c698ee140b36cdc72693cc019cb7add747ca3a07b2b82a2332bfa76c962b186ad94209fcf590ed0f6a73b08a771a58eb9649f2f1da4f7c385da83d50c939231f745514d14b0920deedd9c4dc6d2e547f83643d13541870875e52c610372b14b602e7a47f0b3721cfca60ec68e2eee91f40ceba2d0fdb4ebe19cb1d1ab170726c9e600030454ef355f9a40033672be520e528937f38e7a862a5ae50cd94f667cd015a72ee3f91b1a09031bf4c207e0c516b2e7a4baedf373f1ee71843e560741ed3a3094d2b513e2248caf27ce135716f6887d9f1fe5b11e02c12c989d29054ab183a3f55d9b40d78e12ff56edf936ab966c7c3130bea472b71fd69e70165a76afbf720e2c1587a77943b35acfd81b2ab6f39476623edf3663024fb84da8057ed3a361e9533caf9fc58a5e4897e4bf84f58ed063b5c353bdca3792952eec0a1404149ebeb5b17cd6350ab3e27e44e40fbcb00780d001a48d0365d534ff830553409919608881e665f83bb5cf0736d728c41cc4e985c377f89ee1186303d0d76bc634875ab3ebd87059969f24b0464ae11967bcc47f300a34e3b917b1affceea716c5ad9abf1aa3a1106e2f4d006514dc62cfd2a52426968f2f3991c9f9d8fcd",
+		output: "e4032c01bcece73fde73961ed216820dcb44ce20134678c98afb674bb03afec2f4aacbade7f87a32fff57ae9213eaf0509e9d9db1313b06fd1df53561f85896ba627cccd2d0e2ae4f24f5579bf02f6599f5e63412ba084cf53a5bc9a8061b5c029b755329fcd73f629fadd3bcf6cb4c572fea86466cb5159d19eaaf0f44c3471d0323bc7206bb514ed8117a61c6d98d44faff6a83716657531d965ba3efbcf067c452e0d2807db3423958d9a4421886fe132d7c47e82086db9507616b67f0051dffc1a49ecce3ca8e4d5f5af15684cd8837a471430ddd333ea0b6ee603b7d9e702692f857fab060ccf26f2a8e61dfd3b12923acca78b83a6004e4ff09113becf6bdd0bec3a449a195559dfeafd4e2a79ead5ae3c993a15ad9b1a2ce818e18edb010b7fece9aa437d85ba9841d89026d6aac1a3a6ab6dad932a26d7db6f3664b06d51584cf4d22a75c06e2840db7292798306e4d39379af85a6bc8dcaebb5246e07fadd5e336f122de0ecb99ca24a971701a1f43bd69933beef6e52d299b132e7510caf27b99739e32bd272afc36755ea80cc7ed3957d91325584b338d15b19fe554ee70bee903babe21d0cbecd49235c70a3a4f516ce16761d1cfcd70bb4b9c7c73c359f3fdd0753d6c1ac1a1463142f18266b6a9c84675f247d56563646fb2c8c3b6b81944c2ba2b76b685ba5ea40cf539bcf3850a8af3e0a69c0b38164de520a3bea82b91f67d36bbd87877b5be7f06c2d26b2dc747a26a51f51fe293197db0e91e6ac617c71ddc6edfeb7db8f067ac2012268deb7e5f00a640c1bbec5c4c71f10f921071308cadededad5c90e72d744d0bf790b043fd35729570889ebe5",
+	},
+	{
+		length: 682,
+		nonce:  [3]uint32{0x17cebe90, 0xeffe259b, 0xbdf9d4ca},
+		key:    [8]uint32{0x172d51e8, 0x5b80f5c6, 0xb9c9e438, 0xa56119c0, 0x62212323, 0xf5386589, 0xde7079a3, 0x669e643},
+		input:  "0aa4fbce7e1774f0607e7ea01fc0e6d210bb283964ae75e180a9f6ff3d2c4d50914bfc32bca6d243eb33551521d54d66f377fdc1d31974ece79b157905ff7e7a9b064f349727ce37c83c15ae13df635c3e6b4baf994d9aa0bb90b06c6cda51deefda72c97a2993448e654b746b216d2b949bff1af5238558205cfc3162f1d7a020a919db4d4eb44bcf7b269d4df57e24133d1e540694b9148444cee16e64035ef006a6079dff449949c1b342991f2a27f21c8bd74ccf4bc944284a46e9fd9f9bfd4b95f80c05553950fabbf5e5aed6babb8427832266aa4d175114de9127ff6ee848534d6dd5aa6d2dc361319863cdf32cfb1b074faed17d368964393352df01fe8d86af0e994bc9dac315f7d9efa7bef47a16676cdf17a535ae71d399c4c11a3a3ba0491e8d41f419685258a4ec7d1ae588b3ca341719c0827ce5f5a653959a8671844f2d0293c09bc7d35497ed18c160fc7b6d073a311b621a7a37f7ded1df3d73dcba1821278c9e17a191997fa4dab0802e1ee1b468e91e4272c4569a17dc0b2805b980bde798640aa328a3605abea1865083d7446e960c27f69d32882a2a2295efc9c440dc203872373411925f8839715e9441d31dd9cc14bab09a3e03b4a63e14db3039d58725796326ea6327f189beecd63955f1409467c81f4691ecfe9f0ac5234f23dfb84e3199e415ee7b4f67189e8857ff6cb3f64c2ac1b554bfbd679a6ea8491cfd69d96d08ee2744d9103e0b044212560ff707974b1a9043e1f2c3592828fde8ab5e993652c00e2b3fdb19082611b67866ece6c4a2635f87e04d2136d679f632416b03ece4d7e9406f3437163f4fe0c8cc7b87d487f6de3b3022665bcafa847c2b9199e1ba9af7deb0e29b66ad41688d03a8369416dfbee6d03526adb3ebc4b4f8531d73589499a3010b5309e9d9d2f5a9cf347983a92722dbf6c4f0bae8aba57b37d322",
+		output: "a31f9a532f35f20ba604a9ab9989260e5a4ed04e6ecfa1cb9e0e1d16943906acbbb4e761a2bebc86cad0ce8b3f26d98b455e4b0835eb8b43791cea29fe8fa6e5187b60198142059bbce98917aa2957ae2555bee70e6e9e21ff6197a51ac2ca2952c413efec4d9903a2f6883e88aebe7ca8316831f6a8f2cd0e486319b58dc8db862779adff98b7f35c33faa53d56acd7a81e0feffc286b728f3a11afab7cace4c30b1a45780276b1f0ab89242410d07cb1191c7b9da5d09db7c9a729d91ac3ed82f4350f2871a12d125ba672861d1b0af7219c360a0e023a8b7c23fb9d72631c72e032c097118d90e5db0576586d8224165a8376fe8d04de93516848e7c2653cb4f7d24a971ccf4f16c527ea5b4153fad5fd5bf473b15806671854507bf1a6d9e5fe4a6f6ec977197d21d69a041dd955e199031f895adefd850c8b0ae327ba0c18ca1783560e1ff0feb2f659137e34a91e9e9ff04fe3375b7db6e4326986e6265e5fef00297f6ae627c7563846e531762748fe8d0b6baff17acf1e6c5cfefa35a95ef634ff96f83f16342a6c62311fc653d314f8a6de109356ab7801316e69a48834cb6325816b1f66d5c67d6e9c9cbc8e1a0521fd6e4bf77a7d2609f99c9579e143f530677b99d198a97620d087f058edf35eb7271701ecebb8bfde5671641ed21aeee9e7db06b932e0def91be93cf2955159e9666c770cdffa03886eb6e98dfca8f91ff5cef1927c0f82b9226d65c68d011416cbef802c264e34244ead7a6ebbe28a510a37e1276f4f3cf27a3944a08aaa23bd321092761627dae20dc269b6150545c75e995cfee0a9bcedb1ad8b364beb8839fd5c9f7984fa0a08a1a354aebe18f62acf6d6664978fcfda2ce6fc16eaa2cda5b835339001b3b98d3a407a3e18e0ec2da6ee3d1448c1ece2ed67c3f51f01e76ed59f0e61102b103a3c65aea94275e8d1f0d331538efe",
+	},
+	{
+		length: 768,
+		nonce:  [3]uint32{0xb1c9bd09, 0xdbe6497d, 0x16c73b95},
+		key:    [8]uint32{0xbf9d9e5, 0x2eede668, 0x631dca95, 0x4233e36d, 0xd83fe644, 0x99b11f89, 0xef055717, 0x1ae9695f},
+		input:  "e097b1e8dea40f63714e63ab3ad9bdd518ac3e188926d1086a9850a5580affb592f6e421abc617c103479ba39a3924eea1c0bbbb051614c4b5003bbd5fcbb8093864fc1c130748194d6b560e203b889b98b574a98ec3e0e07cb2d9f271ba7794e5419123b4f2ebc7e0d65cd404104868905ff2c38d30c967fe9d77ebdd4b8fa836c3b0ad15e3e70e9a28236d5593e761e694b047f63bc62c7b0d493c3e2528c8af78f56725172ac9416ec2bdc54de92b92a63f9ccb61e686f9249c7cc337d99b2160400bb5535eb8f8eb1e3cafcbceaa821c1088edbacb3b01b5bed977e702de747ad00268ffe72e3d877dd75816db65b5459607cd1b963fe43bf2405ec223ddc0de514d59cde74f7522dc72285caa3eeb7eae527a7723b33d21ce91c91c8d26bf36eeb1dcdfc1e9e475c1565ed9c7e64ef601874a4f277280a5ceec26717e9385aee8b159379e3feed7952b87240c942970d63351259aa7a286ddb4a2620fa67565c92f592902e49422f1eecea2f44d1c0bbbf54a9e5612b86a9549aa3e6639a924c7bbe2d3c1b5669da73c0e2c6f6f6084f54a912ad2635d0141c2f5ac925414dce0da09ab8f86eae2a7b7e48741253189e5fd554d5c04d9807ac6ffd8a4f8229a3e8ab75ca5c778bd7ec5a5c02085faba9792cbc47f9e9311f3444e6544359769e1b3eb4d42ac8923ec94536e1a44497766b5da523f5763749dbc2738dfa8e13c191dfeac56c7614a96bd3ae23e4e6e5ac00be851ac9831108989b491eaade62113c531385ef3e964ce817c8ed0857adca946467682c2f4387fab2f31ce71b58370853171720268459588d5d216faca58d0bebbd7cd83a78445d9b49e83ec2cdb59b5d760880bf60532178d60372752b47d52562b316c7de5c74af9cd588643002d66bc6260595a540d2f82cf2c07fa64e0cdd1f79877b6a25b0608c735a7d35ca10852da441fcfb31061fd7e482a0989866f9eea8b0b39c3d519715c1c2766c3ad99f041143cdb36557ed647403458155dccbb80c3a365f0a85b1135695648ab67ac76b3d219c7b77e49d735c72ac947b1d7eeb279beb9d2602aba7b36ca",
+		output: "7b6e07e6415660affba56047b988f4548b308e7a642c76791f5c3742cc4cb744cde48fc30e50d458084e06c6dd29a52cb4c306a69a493a17c0838d14b107d07b81c983a2dbad09b80f087ba48465a8beaae5b16e8093e17cfb9e84ea3bdb9af00889268a5c01ddf25af434de56f65882322432aa275fac8519e317ef4d89478f29182143f97350983050f5d37c4b518611da6fa2aed7bb73e614231a194fe17c9073e377fc6ea0aa491e15ca54808e0536c8c3f1bf657283f807ebfc89b55049ac8fb86f89f17974fcf0afc1a2c690c0442842d0f4af9ee29dd960e499d1077bfdad4c0c9189a6e83799bb585acdb853c1e99da7ce9c7eeb9bf431f8d364d0ea80b0a95a7807f196c6ee69fe90e6d1f5d23e5cb256e37e65826d7a111f2272884d6319f968580b3164b2697ea6556816cea3ca316651fe2fd68dfa905d080c28622606f7d24da216289fa2c54c6f42dc244ecb047512ace62f0801f2dfad8f0218f45e2b3bbac97c2176c842398b16dfa1fdfc9a68b7b5a1e785d2a0cc592bc491f5a69c81127b758ee02c66b81674d3135c5882d1dc89dadcffa06f4b0644df5c7fd65c72611d79be7ad637edd6fc38b39946aa2a2c6d08ca9d3ff9a8ffe2e7989546489539b1a623fa937c468e59e0978602526b4367de277526895aa222fbaeae2084f418c5745d8ee844da0baa47f592970c14cf710f49539c12104a62baddb3382f5773dd18c83ecb238ae2e749a51584a38e394ebadd175bf5c3cec787907abb1d94af70ae63d3b7d8d5ff254da90b78ec8fe2ea95dfbc6e3e69ecad856c9e54906df8fe39859f2014b74dc3ca0ee2a957001939d37a6c0b489bd3f1658b835a57b24aa282c23e875c9e67e6eb8b32fe44e7d7d8e285d85da0ce1b53990f9fdb5e2e74728e433ed2c1044df9e89cb9bb316c39fc6fc8bcc74a382093926a288170e857d6b7f47858a4c2d05c74263dc9e8199332d0179687f4a4cdfc80ee6737300cefba75905b22d21e897f887b67aa3051877fff11d98bf96ca5091bb225bddd5eae697f3dfb0efcdb788ebf6694b5b39dbb0d4bf9427382a3a58f0b",
+	},
+	{
+		length: 828,
+		nonce:  [3]uint32{0xc7e503e, 0xf8110ddf, 0x83316c8c},
+		key:    [8]uint32{0xfa2d1cd, 0x4fe7f905, 0x2b9e4c1b, 0x115bc881, 0x2922bcc5, 0x3f60aa25, 0x13c26d31, 0x2096af63},
+		input:  "0a1064714f20d9e47fe53250ecfec759f4137e60afaf65755f4709a483504c3855833b6dcaf7aa0180fd735fa9a73d46697f6c45004adf12452ea4c04a720fd7c20b9783b74b8b3ea0c8b1563d5a85f44af8afd7d91ca6298ca22642a684f66e365edd6f6bdb2dd32dfa13c62dc497fb341b86f65d40655931171416e23e3b2623c0b4a67d448877b6e3d4e0fe284034a10162b2b5e21639047036874f4bcde22b145b5f18aa8ff32dec81e6a5ac68b3c30c24bd8fd3b8e098a1cf202e2ab2a3bb66a9393222b9f7384653cda7707f00bc3c81e9591fd040a07d3629410c2db78781a4c9db3df5f9d648162f1b087974f56a89db07aa21ba827e3864a1618945b2fba06853a13c35da2909f5013feb313bae09870b8eab904024adab0d6ac46c1a1499791b47413139dee59db676949b9e9ab8d3d6abaa954ec2a9fc83953c91b483c3b6bd6700b96484850734e72e3710a1b379c0d0698aeaf68f13a0d317bfd689471e3299288e7a383a58522f0daaff210cc4917fa05f0b8ceefc2afc46148a05a100d30787accfb4da094e61ea6b58f132692aedcabae928e53c2594b01507b8fc2d0a85a1d111d1f4de0b95258281ae01873a72606753b6f878ecd8c4f613fb3477710d260f0bca0d4c06f675ab7113eded395f88755a98a0ad22b4a002cfe9447c4e39eda13738f4eccb9c13367ebc2878257c4647d31b67e5e32b6a77f23e9593658d19c0a40e8a7228767afba1cf23072b013b2d76ee66e42b57bec2797ce3619c695a661004c8129cb5c5d6a2836be22483f3b7e40bf8ac5535bf6cd065c4821a87829948c88163cfe3c0f60cea4e7ff59df4cdbf80064b2d664b39487413039999b5e86f1d467b12682d0cd355e9f7cd980e87d584ddbda89f68632d3b8fd6bc3b80205d7feb97a46842b093f74aa14bb21accda7474247b5e39ac76ef75e9b5b52b6a829a7e2297ab88fb0eb690d54ab1af2d7437149a6202035ce15f1e6c6267458d62677c263d83d3f8119af191b7d766582620e0f08b411c996c25ba6a32c2d73f592e789ed662e94103329bfa5e6573f1116ec04438997f3e4ad91b4123b570743455020d914bde2d8417fb24671e6db261732fb89dda1a36614b095529e4f97374c9bc0e55aa577bfffa663c816ca9fae3472e0a",
+		output: "b00a7caf5359c5bcebe590e6bab9aa03370050c55cbd45a257f4869937e922a15f2d38121b1493d6b5dd4a8a47d7b4e5cb049d396ad84ed421df774b0408b6939f18ebf5cf83f48c540affcc2a885967bf4bd222c42904b8a73c4185bde3f97e874fad25b46714235e60c9ff53ed2975c9c85ebad0752249e4b627ffa41555eb9074f63a5f7d61d207d2ce11b2a9fa23a13a0832eccb91efa2afd8d9acfee94ac78a733fa156bfea5006da1d0127c32aadbb75c015b68c627903e1c85bf3a1a9f99c6cfbdbb5c871f7f9661b78cf5e16d819f53e9930e201d4f58e69bcdce77ec5b9b1d2cf206a71f744342273c26b9abc71303c20df3d51f52222893d803fc8e0e0afcd99ee1c7f95b48680403566f7f9e296d7ccc0ec348b6ad515af58d11fd82c628ea29ee6a5d67aaeabd8823addc01a078b04313af73105d4ce4abef8e6ee8ce649640a19678292d4f1017d121549fd2c19ba6cdc0b613e512bc9551d759c6d38aea7e35c0847a142e273a16bb1495e652f9668b97801ba3f6d9931c0a1efaa4452e15732dca1ca9cb45ed289e0fd08d1cee1cdcc9dfba8d0b2562b0b1a180f4ee69d63573222c8d4789bf0d63d2a201a70c7b27c84e620e33e8a863cf49b784269a51ead3d4ad26f044d5859988d5485a11533ea805f5a8f6313caa6b421071a34f57170fdd8e4663e9a4cdcdcc1ddaa9f6e651fb365cf827667b018ae7d028c7f96295b2b4f9eeb4b361b48af86463a79f50b107ab0935e3cec3f4f203cea801ff95fb870d2c2f0e315dc8a6a547dd3c390a1f5403917315164bd2d40362489b389a54e8dc0ddb83e6a43a26c65923e6f76ee0ee0e3a33b0a9066620a01f0319e20b9f1beb3910ad962a3000e6aacb0ae57f3f6c5e0315be5de93edcf0e45e0e47332f9daf7f33e6e8bf1929910b78b8f88ca12bf5519a3217b7554c8c8350cc314561d580bf67a3878e3979430d070121a5e070a3458742e8549bda972f603222e2b30eb8a49a955805307e6e02f8c60a08188f69340e116422458d4a8841f46a78c833b1a822e3f6c9c97422c918f17c36175ca4b3d1c081ee4b175b4b07bf101c3836eb5b9e3cbd08a89b4a1c50edcb41ea8ea6ceb1532f5b842715d50dc21e2499e08c373d3dedb96bb477c8802ab7aa957e0b5810f38",
+	},
+	{
+		length: 859,
+		nonce:  [3]uint32{0xeb02dac9, 0xa7cba06c, 0xc24764c},
+		key:    [8]uint32{0xe9414a57, 0xd5e29546, 0x1a5e2f4c, 0x806e4c46, 0x48098d1f, 0x4351ca1a, 0x53ed97c, 0xa6a495ca},
+		input:  "00fa3b13b5cfa9b5d65a41cc2d3c420518802c22c4582873f1ad52a22032d2cef7c975078b199787e852fb1f914529f60d1cc854e5d6d547216dce043e0fc94866bb2193343c3a07fde60e668266d1cee3067c6f2ce0f9f63456ad08094b6c7f515f7ca90caa96494e2a6835ba1f3f166012ad1ff6af6b5f8455d5c26e72402966af9066ca70ad027eed23b0eb02c751195064a62283975efeb29bc5993f83360d012a2f5275ac758a9e8fe458fc7cc0673e6b9e338678f0faff60a67fff3784c3054dcbd95d1b00ed4c6156b3831cc42a2ccdeee55541f228b88e6c318e2d797c6fc035ae12868c4a4e3843b5b25a530b1477dec3f5ac27644476b5766e0ee132d833f9a63200eb0980bf72c3666150e567e01e3e1f469cf36beea65946fce714a3f354983e54ca4315b57ea35c5f48bd5eada05f49db1004cbb39888ebab3afad62f6509abad77ca8c4ff28731c7ae545e6876c8f4a80b6cc26928ee05001a9764694b52edd605e182d5a3a5fd192bff58aba90f57e4debe612d02cf6f08af33a78ebf8823bb3eb46d4da25b7dfa15ad436c380633d3db3d0dc4dfec6c2324d105e7090e65342b554854e777b40b5dab8125a58e8b212364ff88459a8466ff5ae661034abc8286a78ad5aa582e2dabbcd7a0b0cedcb9fd5f0bb8c3bef9117f2ca6520a72b94e528c1a4a464398e654995d5f4c77cbabf2b204b96a058cf1b38284b34e41ac37b05a003ed51be9602050f21c6b9326714bc425c1e22833da95a6e77571691d4dcab4ef9056c4c7f85d5b445b902eb375b5164c6bdf629ccfd4127a6c024bb6c4da0b6b08350432e58f8229e04e2e76f704be17d36e0c04fcc7a98f721d4572aa7f66ae8e9664300a189bc3862da47b60c8b33424f6d577cc10f4755f36c2a6decc30ba81bf48f96616ccfcfb74965d6bdcab82728bb224c560d1cfd7a175413ad1c14c734746be3b062b4e7514e9075c688103515e32e3335dbd272a315024d56f4ecd354264da9bc712080657b2b51b06dc7c4c441d9858935a4c3e6b207bde38ea83bba4c6854b2bcf914d758e0a174c0528e0e385c7cff355c38db1c22440369141e91266824c59f1ed23e7d4b99d31b0baa7bed4526e24259dbef5c9ae275e97267b756645f804c274d65ac7ab0f7683435bc2e4f24075cd1b790aa2b53fbf044e8f2092bdf0dbe88a582ff8f8de291e8220",
+		output: "bea32587095caac661c3ac49e65654b282192b2addf5b9a403aea6c8bd0096291a0a66ca4062acf1da91fb5749952096ec63ab652ecf94c29807f0aaac939b6896edcd6f0cd8dd8d208b906ef4d7a8766831fecd6ce98f4ea0c34fa9a5114dbeb23c2cd6d3aa962e39b18cb343c24e65d49fad0a0fb50736f8d2b24b011108932484399f4c4510ac9a5e6bc78ff0b450e67f87b49f253b99d95d6294e15a9934fc8b89a5913c08f75d3516766fb0f60f82e2b2647b4619991c78adbcf548c07c0dda30c629349d84f298313c3e629e03760b1cf860264205a950d6fd86732a6513827f72c0dff5aff96f7203464f60849c1065beb70f282cca1334f6f6c767dfff94f063361f592e85597de5d313eaed17bd533db24818d9ba9aea2afa797721fbd19eea7b8d46bbc4b9dc0164636d2e754f5e9e8c04e2a381096331731c645ea1f613a37bfa9a6fb2c6307e9bacacbeab7f5672163ff9742a8115049bce0269d7d5f6f35787be031dbee1535b0516ec0b46d12f5833cde5f2cc569edcdd20993e9776aacf48ace7bfadf79065f2803fba6b2b27aa622abb7ae023ff2b27b727f509f313f92026392485a5ed4fd53b2e22b2d2dc1538ce158d34921214638be30ae054a0f5f1d4f9c590a2d215ac2a5b23ed33871ab26c8bb6db7fe9d6f51e527c9547248a4e9734c64658b22893f4f6867a35f18e2bbfd7d62142025955cb51af8e40b6fcb91c7e959cea2c92022c87c29dae107a306f41b00e73c7bceef8cb070e8f9e830caeee463170e919cba6eee63092a5a7ee33b74db09cdd022fdafbcd5d524253a29a103ba6f4d668d31d18f867557871c0e0258221c3050d57c18bdae4cc4ff8da0daddb5c08619be127ee76a317b59a9d8e67808603a1bfce6b4e0d070082b283bf9c0e6ef8256208e482f3e2d1a40d30807f60a868e2279dfbc3586d44ee25fdca3505cd39fd469c2cd03bc2f921d22a8346750f346c919e7247301c1c8a4a3ddb8eabc6e80d85cd2459afe1cbb4851ea2c86b8075e0fef3177cb074894410ecf681242fac62b5fa4ed3a10ddaa595427851d376cf69e350207b667f7aa26d003f1ec739a8792532ebd93f3cafb1fea40d227bcadda2fb6da794cea3371240f257f80b1b8a857ea453b46938397c1f4b303a46257750003a60666a11d03bf2afb5c71e059933d617288891733b63784bd9c662234f",
+	},
+	{
+		length: 985,
+		nonce:  [3]uint32{0x3c2b47a4, 0xf614c813, 0xa26f7014},
+		key:    [8]uint32{0x39bd3d18, 0xc9aacd67, 0xcb5485b5, 0x20536a22, 0xbb22ac87, 0x1c9da580, 0x7d996b2e, 0x456fe461},
+		input:  "01847d8a97d56e55e12f89adb13c8c0f9dea5555e8dc61171fbb8e181f6cf846a4dd68b2c75335c0896fa215bf7f9eb7e398e4520aaaf33461ecfb61051f43d43569fb75fabd79d319bf39469f951e4da7932a74624c46d8d26a9499c701c00d3dea57a6f65b4c0f33b568d13989340294d17cd005b26d89cf6fa1c88e7b6ef4d074291fa8c117ae05d7c785459ef4561c45af63a811e9aa1c31b69a5bdac2356d955a0f579791247a757a691b3de447a53619878397cd82a74053f06da3574045bc856500ec01fd2afbc64d8dd283ac876a50e9396f78c424ab157f481316fd9c90cd899f5aca46dad32c68f1d64ea7f1c4bdb994ad847072609bd89adc2fa8382a5d573b680533640b8321b6adf27926274660b2cbaf04fbc9a4fb17ce8957c38c7bab1aafd5bf7263171e47d2e1ae5cf0494815642209d303dba561754479c24ea01a573c9083b68acc49907b1748924d3c6a82feb9417ca932578c123f9db35521c0d992565f7396f0c23e436289c1720e4e7c6e285c04a8159f93e06801334e523b18fe188355cc6a155febe64ba053e6b5d1cc87787fd5ae68fa86d8c51868b9f6a9664cf0d56aa6cb8463362bb671e6b8423bcbefe2a1a0acba3f135496736b5cec5e329494af46aba322bf5d1cc108c98298459558773a316e09b0bb960a26f4b0bfbaa493b5f98a0e522b6203c471b10e662abe9b9e60de2a1517843933add02017fadd62608383ad53796159f3d21b2c8ed7295802ca79ea65d550114ca2bcc7f7c3b4c6709fffc3c2de00da06e83d8f0cf04b8c8edd21c0fc11a0b2aa7f6adad255fef25e5c0a9d59546e97446e1fbf6a51a8ea6cad54cabfdd19cd10d7d33ff0549b710557e3931821dd8809ab0a9d3aaa761a01ae0f2e378906672924d6a1b12fb1cca7bed41f31974b9917a05de60c32796f502e7035a2c01cb49bc8e1734b9fa138b81b4dfe19d37f5942dd1b42f03e1e5a6a046ecd457174150e17dd148e4bfea44b72da35ef42a7251244700e59e702033677d42611168fd246e1b18b9a464b6c20fc7fcf6360cd00466ece059a69d7d54a4f5565d08799f85dd3c849a08ba43415077c1c0e5dbdba52bb3167ee99a11db551f0260493be1dde58d2072e8c02251f4f574b6e115cbb6136dc2c3fbce75fdcefe812d9c07a91a89088985a52cb1fb9f6cef80fa30364706414175e42c75e8e37f6e7cd028c99f59caa88c49db5b46e8d6301bc39034013718a9eeef5506415016fb21d70e46a03b4c5ba72f91dd9321ff5e210e5e5f7b0723a3bc4bb02b5a74c1f4a63aa5a993a31f79a768fe8033c9abfeb4deb536af1054be02d8d1c4a6a0fa75f3eb787d57a03b7ae994fb1b54b2c43b230ce32e6245d944b3cea4fa6",
+		output: "785dbea5d1e50af4743ed5fd2209e441fc7c50bc7f6fd9cc7f24654c619e2606178dcbbd81a1f94c1b3176837024098bd31326145be326b32fd9277a55a6fb38780c8dc8b471a3184379d90da4aa87d80b889b1f4d8d0755c1704a526b99ac829b8ad157ca54b2b05ff8b2917e27b0c147ab54add9a89fdcad7b93ba1fe2d5be9de88b68a5324f1b42943e45ee31c4ef783ec9e2337b3f2834b10cf452b313fafdf0c03719140f64060da0a565e185cb8e544e1c185ca230ff2321739a285abe8be4be0ce76678a7b0902a77a645194de49fef8ff64cc464ea25e1f1d72c775e450f08ddd7680d27a4142879787b198583d93b84cd87fd5b4063d92d13d9c9cb580c01fac0174686a18f64e6fa0b3589624cfae04aad74950559bdf92b2b199c60cb04013aa0ef56d1f9ec5b7e968f6a83756ecc9cee7dd8b433f64649f948df5474a64549e71e46fd8bb16568d21f5fb67f5ed555f2b8aec4709383e8cbc45b9fe47c0434178ad4c6d0d42606d6eef0e21d0370898d1d5d646830a88d5f024094fe9c7a2003ca13d20ab7cd748dc11a22f578ddab416f3500eff3d89fc177b46436108e2e2c7973910cb8454a01c9e9b98f966848325444b2ac205b1ed6919fa76aaf63717574761b7f62b10649357df49f85a845a30b6acd57fa202fe58673930ec59399f537e9682b1f5f6f409988789a8e0c1f803478dded14b40d3b6eb3109758efeb6a7fe21f41c4dcc8027258da27ad74010839dbfdf8fe55050511f85c321e653f76e55f22248f46da529a380c6b1a16a19ce73af9715545c2cae098dc42dd61248dbcf7b295f4dc6b8930b41baeef677156c534869be65e723e1aa0336e8be8a3b138f840c9cd63bab6d9d61f239a47d8cf56258544e6ef65edca27069f7a57f087a7cc021fa1294b75c0c0f1093c025e426e4f041ed5187f358402676d5da5fb6ceba76a178f65c8c3046f258531c165b8808bdd221c59ff56e3e06247576e144aac01ea96a07f1be15d7a2b0b3b6c259a9133f8a50b56154ecf9f61022f470027247e6e70e6eaf7ece5e324ec8f95667ffed10337652b119e7cb8d197e306e81ea251340b9fb2c33aa230c0a16e1ca783f9344b3acbf413acd96616e6d477dba90e39326089934bc5ca6620855cdc442e25bf8b8debf335e16e7e25cceb68659cc81b13a507fbd9f30b347126beeb57016bd348fe3df592d4778011664a218227e70d7360d139480500b7f6f84153e61ca4dea105875e19ce3d11a3dfd0ad0074035ff6a9fac0ece91afd8be74c168da20c8baafcc14632eb0e774db758a3d90709cddf0266c27963788c35a842beea8ba2d916234431efde4bb32fd7e1cef51dcf580f4697206bbc3f991f4046360aea6e88ec",
+	},
+}

+ 43 - 0
psiphon/common/crypto/internal/chacha20/xor.go

@@ -0,0 +1,43 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found src the LICENSE file.
+
+package chacha20
+
+import (
+	"runtime"
+)
+
+// Platforms that have fast unaligned 32-bit little endian accesses.
+const unaligned = runtime.GOARCH == "386" ||
+	runtime.GOARCH == "amd64" ||
+	runtime.GOARCH == "arm64" ||
+	runtime.GOARCH == "ppc64le" ||
+	runtime.GOARCH == "s390x"
+
+// xor reads a little endian uint32 from src, XORs it with u and
+// places the result in little endian byte order in dst.
+func xor(dst, src []byte, u uint32) {
+	_, _ = src[3], dst[3] // eliminate bounds checks
+	if unaligned {
+		// The compiler should optimize this code into
+		// 32-bit unaligned little endian loads and stores.
+		// TODO: delete once the compiler does a reliably
+		// good job with the generic code below.
+		// See issue #25111 for more details.
+		v := uint32(src[0])
+		v |= uint32(src[1]) << 8
+		v |= uint32(src[2]) << 16
+		v |= uint32(src[3]) << 24
+		v ^= u
+		dst[0] = byte(v)
+		dst[1] = byte(v >> 8)
+		dst[2] = byte(v >> 16)
+		dst[3] = byte(v >> 24)
+	} else {
+		dst[0] = src[0] ^ byte(u)
+		dst[1] = src[1] ^ byte(u>>8)
+		dst[2] = src[2] ^ byte(u>>16)
+		dst[3] = src[3] ^ byte(u>>24)
+	}
+}

+ 32 - 0
psiphon/common/crypto/internal/subtle/aliasing.go

@@ -0,0 +1,32 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !appengine
+
+// Package subtle implements functions that are often useful in cryptographic
+// code but require careful thought to use correctly.
+package subtle // import "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/internal/subtle"
+
+import "unsafe"
+
+// AnyOverlap reports whether x and y share memory at any (not necessarily
+// corresponding) index. The memory beyond the slice length is ignored.
+func AnyOverlap(x, y []byte) bool {
+	return len(x) > 0 && len(y) > 0 &&
+		uintptr(unsafe.Pointer(&x[0])) <= uintptr(unsafe.Pointer(&y[len(y)-1])) &&
+		uintptr(unsafe.Pointer(&y[0])) <= uintptr(unsafe.Pointer(&x[len(x)-1]))
+}
+
+// InexactOverlap reports whether x and y share memory at any non-corresponding
+// index. The memory beyond the slice length is ignored. Note that x and y can
+// have different lengths and still not have any inexact overlap.
+//
+// InexactOverlap can be used to implement the requirements of the crypto/cipher
+// AEAD, Block, BlockMode and Stream interfaces.
+func InexactOverlap(x, y []byte) bool {
+	if len(x) == 0 || len(y) == 0 || &x[0] == &y[0] {
+		return false
+	}
+	return AnyOverlap(x, y)
+}

+ 35 - 0
psiphon/common/crypto/internal/subtle/aliasing_appengine.go

@@ -0,0 +1,35 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build appengine
+
+// Package subtle implements functions that are often useful in cryptographic
+// code but require careful thought to use correctly.
+package subtle // import "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/internal/subtle"
+
+// This is the Google App Engine standard variant based on reflect
+// because the unsafe package and cgo are disallowed.
+
+import "reflect"
+
+// AnyOverlap reports whether x and y share memory at any (not necessarily
+// corresponding) index. The memory beyond the slice length is ignored.
+func AnyOverlap(x, y []byte) bool {
+	return len(x) > 0 && len(y) > 0 &&
+		reflect.ValueOf(&x[0]).Pointer() <= reflect.ValueOf(&y[len(y)-1]).Pointer() &&
+		reflect.ValueOf(&y[0]).Pointer() <= reflect.ValueOf(&x[len(x)-1]).Pointer()
+}
+
+// InexactOverlap reports whether x and y share memory at any non-corresponding
+// index. The memory beyond the slice length is ignored. Note that x and y can
+// have different lengths and still not have any inexact overlap.
+//
+// InexactOverlap can be used to implement the requirements of the crypto/cipher
+// AEAD, Block, BlockMode and Stream interfaces.
+func InexactOverlap(x, y []byte) bool {
+	if len(x) == 0 || len(y) == 0 || &x[0] == &y[0] {
+		return false
+	}
+	return AnyOverlap(x, y)
+}

+ 50 - 0
psiphon/common/crypto/internal/subtle/aliasing_test.go

@@ -0,0 +1,50 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package subtle_test
+
+import (
+	"testing"
+
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/internal/subtle"
+)
+
+var a, b [100]byte
+
+var aliasingTests = []struct {
+	x, y                       []byte
+	anyOverlap, inexactOverlap bool
+}{
+	{a[:], b[:], false, false},
+	{a[:], b[:0], false, false},
+	{a[:], b[:50], false, false},
+	{a[40:50], a[50:60], false, false},
+	{a[40:50], a[60:70], false, false},
+	{a[:51], a[50:], true, true},
+	{a[:], a[:], true, false},
+	{a[:50], a[:60], true, false},
+	{a[:], nil, false, false},
+	{nil, nil, false, false},
+	{a[:], a[:0], false, false},
+	{a[:10], a[:10:20], true, false},
+	{a[:10], a[5:10:20], true, true},
+}
+
+func testAliasing(t *testing.T, i int, x, y []byte, anyOverlap, inexactOverlap bool) {
+	any := subtle.AnyOverlap(x, y)
+	if any != anyOverlap {
+		t.Errorf("%d: wrong AnyOverlap result, expected %v, got %v", i, anyOverlap, any)
+	}
+	inexact := subtle.InexactOverlap(x, y)
+	if inexact != inexactOverlap {
+		t.Errorf("%d: wrong InexactOverlap result, expected %v, got %v", i, inexactOverlap, any)
+	}
+}
+
+func TestAliasing(t *testing.T) {
+	for i, tt := range aliasingTests {
+		testAliasing(t, i, tt.x, tt.y, tt.anyOverlap, tt.inexactOverlap)
+		testAliasing(t, i, tt.y, tt.x, tt.anyOverlap, tt.inexactOverlap)
+	}
+}

+ 58 - 0
psiphon/common/crypto/nacl/auth/auth.go

@@ -0,0 +1,58 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+Package auth authenticates a message using a secret key.
+
+The Sum function, viewed as a function of the message for a uniform random
+key, is designed to meet the standard notion of unforgeability. This means
+that an attacker cannot find authenticators for any messages not authenticated
+by the sender, even if the attacker has adaptively influenced the messages
+authenticated by the sender. For a formal definition see, e.g., Section 2.4
+of Bellare, Kilian, and Rogaway, "The security of the cipher block chaining
+message authentication code," Journal of Computer and System Sciences 61 (2000),
+362–399; http://www-cse.ucsd.edu/~mihir/papers/cbc.html.
+
+auth does not make any promises regarding "strong" unforgeability; perhaps
+one valid authenticator can be converted into another valid authenticator for
+the same message. NaCl also does not make any promises regarding "truncated
+unforgeability."
+
+This package is interoperable with NaCl: https://nacl.cr.yp.to/auth.html.
+*/
+package auth
+
+import (
+	"crypto/hmac"
+	"crypto/sha512"
+)
+
+const (
+	// Size is the size, in bytes, of an authenticated digest.
+	Size = 32
+	// KeySize is the size, in bytes, of an authentication key.
+	KeySize = 32
+)
+
+// Sum generates an authenticator for m using a secret key and returns the
+// 32-byte digest.
+func Sum(m []byte, key *[KeySize]byte) *[Size]byte {
+	mac := hmac.New(sha512.New, key[:])
+	mac.Write(m)
+	out := new([KeySize]byte)
+	copy(out[:], mac.Sum(nil)[:Size])
+	return out
+}
+
+// Verify checks that digest is a valid authenticator of message m under the
+// given secret key. Verify does not leak timing information.
+func Verify(digest []byte, m []byte, key *[KeySize]byte) bool {
+	if len(digest) != Size {
+		return false
+	}
+	mac := hmac.New(sha512.New, key[:])
+	mac.Write(m)
+	expectedMAC := mac.Sum(nil) // first 256 bits of 512-bit sum
+	return hmac.Equal(digest, expectedMAC[:Size])
+}

+ 172 - 0
psiphon/common/crypto/nacl/auth/auth_test.go

@@ -0,0 +1,172 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package auth
+
+import (
+	"bytes"
+	rand "crypto/rand"
+	mrand "math/rand"
+	"testing"
+)
+
+// Test cases are from RFC 4231, and match those present in the tests directory
+// of the download here: https://nacl.cr.yp.to/install.html
+var testCases = []struct {
+	key [32]byte
+	msg []byte
+	out [32]byte
+}{
+	{
+		key: [32]byte{
+			0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+			0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+			0x0b, 0x0b, 0x0b, 0x0b,
+		},
+		msg: []byte("Hi There"),
+		out: [32]byte{
+			0x87, 0xaa, 0x7c, 0xde, 0xa5, 0xef, 0x61, 0x9d,
+			0x4f, 0xf0, 0xb4, 0x24, 0x1a, 0x1d, 0x6c, 0xb0,
+			0x23, 0x79, 0xf4, 0xe2, 0xce, 0x4e, 0xc2, 0x78,
+			0x7a, 0xd0, 0xb3, 0x05, 0x45, 0xe1, 0x7c, 0xde,
+		},
+	},
+	{
+		key: [32]byte{'J', 'e', 'f', 'e'},
+		msg: []byte("what do ya want for nothing?"),
+		out: [32]byte{
+			0x16, 0x4b, 0x7a, 0x7b, 0xfc, 0xf8, 0x19, 0xe2,
+			0xe3, 0x95, 0xfb, 0xe7, 0x3b, 0x56, 0xe0, 0xa3,
+			0x87, 0xbd, 0x64, 0x22, 0x2e, 0x83, 0x1f, 0xd6,
+			0x10, 0x27, 0x0c, 0xd7, 0xea, 0x25, 0x05, 0x54,
+		},
+	},
+	{
+		key: [32]byte{
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa,
+		},
+		msg: []byte{ // 50 bytes of 0xdd
+			0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+			0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+			0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+			0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+			0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+			0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+			0xdd, 0xdd,
+		},
+		out: [32]byte{
+			0xfa, 0x73, 0xb0, 0x08, 0x9d, 0x56, 0xa2, 0x84,
+			0xef, 0xb0, 0xf0, 0x75, 0x6c, 0x89, 0x0b, 0xe9,
+			0xb1, 0xb5, 0xdb, 0xdd, 0x8e, 0xe8, 0x1a, 0x36,
+			0x55, 0xf8, 0x3e, 0x33, 0xb2, 0x27, 0x9d, 0x39,
+		},
+	},
+	{
+		key: [32]byte{
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+			0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
+			0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+			0x19,
+		},
+		msg: []byte{
+			0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+			0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+			0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+			0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+			0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+			0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+			0xcd, 0xcd,
+		},
+		out: [32]byte{
+			0xb0, 0xba, 0x46, 0x56, 0x37, 0x45, 0x8c, 0x69,
+			0x90, 0xe5, 0xa8, 0xc5, 0xf6, 0x1d, 0x4a, 0xf7,
+			0xe5, 0x76, 0xd9, 0x7f, 0xf9, 0x4b, 0x87, 0x2d,
+			0xe7, 0x6f, 0x80, 0x50, 0x36, 0x1e, 0xe3, 0xdb,
+		},
+	},
+}
+
+func TestSum(t *testing.T) {
+	for i, test := range testCases {
+		tag := Sum(test.msg, &test.key)
+		if !bytes.Equal(tag[:], test.out[:]) {
+			t.Errorf("#%d: Sum: got\n%x\nwant\n%x", i, tag, test.out)
+		}
+	}
+}
+
+func TestVerify(t *testing.T) {
+	wrongMsg := []byte("unknown msg")
+
+	for i, test := range testCases {
+		if !Verify(test.out[:], test.msg, &test.key) {
+			t.Errorf("#%d: Verify(%x, %q, %x) failed", i, test.out, test.msg, test.key)
+		}
+		if Verify(test.out[:], wrongMsg, &test.key) {
+			t.Errorf("#%d: Verify(%x, %q, %x) unexpectedly passed", i, test.out, wrongMsg, test.key)
+		}
+	}
+}
+
+func TestStress(t *testing.T) {
+	if testing.Short() {
+		t.Skip("exhaustiveness test")
+	}
+
+	var key [32]byte
+	msg := make([]byte, 10000)
+	prng := mrand.New(mrand.NewSource(0))
+
+	// copied from tests/auth5.c in nacl
+	for i := 0; i < 10000; i++ {
+		if _, err := rand.Read(key[:]); err != nil {
+			t.Fatal(err)
+		}
+		if _, err := rand.Read(msg[:i]); err != nil {
+			t.Fatal(err)
+		}
+		tag := Sum(msg[:i], &key)
+		if !Verify(tag[:], msg[:i], &key) {
+			t.Errorf("#%d: unexpected failure from Verify", i)
+		}
+		if i > 0 {
+			msgIndex := prng.Intn(i)
+			oldMsgByte := msg[msgIndex]
+			msg[msgIndex] += byte(1 + prng.Intn(255))
+			if Verify(tag[:], msg[:i], &key) {
+				t.Errorf("#%d: unexpected success from Verify after corrupting message", i)
+			}
+			msg[msgIndex] = oldMsgByte
+
+			tag[prng.Intn(len(tag))] += byte(1 + prng.Intn(255))
+			if Verify(tag[:], msg[:i], &key) {
+				t.Errorf("#%d: unexpected success from Verify after corrupting authenticator", i)
+			}
+		}
+	}
+}
+
+func BenchmarkAuth(b *testing.B) {
+	var key [32]byte
+	if _, err := rand.Read(key[:]); err != nil {
+		b.Fatal(err)
+	}
+	buf := make([]byte, 1024)
+	if _, err := rand.Read(buf[:]); err != nil {
+		b.Fatal(err)
+	}
+
+	b.SetBytes(int64(len(buf)))
+	b.ReportAllocs()
+	b.ResetTimer()
+
+	for i := 0; i < b.N; i++ {
+		tag := Sum(buf, &key)
+		if Verify(tag[:], buf, &key) == false {
+			b.Fatal("unexpected failure from Verify")
+		}
+	}
+}

+ 36 - 0
psiphon/common/crypto/nacl/auth/example_test.go

@@ -0,0 +1,36 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package auth_test
+
+import (
+	"encoding/hex"
+	"fmt"
+
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/nacl/auth"
+)
+
+func Example() {
+	// Load your secret key from a safe place and reuse it across multiple
+	// Sum calls. (Obviously don't use this example key for anything
+	// real.) If you want to convert a passphrase to a key, use a suitable
+	// package like bcrypt or scrypt.
+	secretKeyBytes, err := hex.DecodeString("6368616e676520746869732070617373776f726420746f206120736563726574")
+	if err != nil {
+		panic(err)
+	}
+
+	var secretKey [32]byte
+	copy(secretKey[:], secretKeyBytes)
+
+	mac := auth.Sum([]byte("hello world"), &secretKey)
+	fmt.Printf("%x\n", *mac)
+	result := auth.Verify(mac[:], []byte("hello world"), &secretKey)
+	fmt.Println(result)
+	badResult := auth.Verify(mac[:], []byte("different message"), &secretKey)
+	fmt.Println(badResult)
+	// Output: eca5a521f3d77b63f567fb0cb6f5f2d200641bc8dada42f60c5f881260c30317
+	// true
+	// false
+}

+ 19 - 2
psiphon/common/crypto/nacl/box/box.go

@@ -3,7 +3,7 @@
 // license that can be found in the LICENSE file.
 
 /*
-Package box authenticates and encrypts messages using public-key cryptography.
+Package box authenticates and encrypts small messages using public-key cryptography.
 
 Box uses Curve25519, XSalsa20 and Poly1305 to encrypt and authenticate
 messages. The length of messages is not hidden.
@@ -13,6 +13,23 @@ example, by using nonce 1 for the first message, nonce 2 for the second
 message, etc. Nonces are long enough that randomly generated nonces have
 negligible risk of collision.
 
+Messages should be small because:
+
+1. The whole message needs to be held in memory to be processed.
+
+2. Using large messages pressures implementations on small machines to decrypt
+and process plaintext before authenticating it. This is very dangerous, and
+this API does not allow it, but a protocol that uses excessive message sizes
+might present some implementations with no other choice.
+
+3. Fixed overheads will be sufficiently amortised by messages as small as 8KB.
+
+4. Performance may be improved by working with messages that fit into data caches.
+
+Thus large amounts of data should be chunked so that each message is small.
+(Each message still needs a unique nonce.) If in doubt, 16KB is a reasonable
+chunk size.
+
 This package is interoperable with NaCl: https://nacl.cr.yp.to/box.html.
 */
 package box // import "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/nacl/box"
@@ -56,7 +73,7 @@ func Precompute(sharedKey, peersPublicKey, privateKey *[32]byte) {
 }
 
 // Seal appends an encrypted and authenticated copy of message to out, which
-// will be Overhead bytes longer than the original and must not overlap. The
+// will be Overhead bytes longer than the original and must not overlap it. The
 // nonce must be unique for each distinct message for a given pair of keys.
 func Seal(out, message []byte, nonce *[24]byte, peersPublicKey, privateKey *[32]byte) []byte {
 	var sharedKey [32]byte

+ 25 - 1
psiphon/common/crypto/nacl/secretbox/secretbox.go

@@ -13,11 +13,29 @@ example, by using nonce 1 for the first message, nonce 2 for the second
 message, etc. Nonces are long enough that randomly generated nonces have
 negligible risk of collision.
 
+Messages should be small because:
+
+1. The whole message needs to be held in memory to be processed.
+
+2. Using large messages pressures implementations on small machines to decrypt
+and process plaintext before authenticating it. This is very dangerous, and
+this API does not allow it, but a protocol that uses excessive message sizes
+might present some implementations with no other choice.
+
+3. Fixed overheads will be sufficiently amortised by messages as small as 8KB.
+
+4. Performance may be improved by working with messages that fit into data caches.
+
+Thus large amounts of data should be chunked so that each message is small.
+(Each message still needs a unique nonce.) If in doubt, 16KB is a reasonable
+chunk size.
+
 This package is interoperable with NaCl: https://nacl.cr.yp.to/secretbox.html.
 */
 package secretbox // import "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/nacl/secretbox"
 
 import (
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/internal/subtle"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/poly1305"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/salsa20/salsa"
 )
@@ -70,6 +88,9 @@ func Seal(out, message []byte, nonce *[24]byte, key *[32]byte) []byte {
 	copy(poly1305Key[:], firstBlock[:])
 
 	ret, out := sliceForAppend(out, len(message)+poly1305.TagSize)
+	if subtle.AnyOverlap(out, message) {
+		panic("nacl: invalid buffer overlap")
+	}
 
 	// We XOR up to 32 bytes of message with the keystream generated from
 	// the first block.
@@ -101,7 +122,7 @@ func Seal(out, message []byte, nonce *[24]byte, key *[32]byte) []byte {
 // Open authenticates and decrypts a box produced by Seal and appends the
 // message to out, which must not overlap box. The output will be Overhead
 // bytes smaller than box.
-func Open(out []byte, box []byte, nonce *[24]byte, key *[32]byte) ([]byte, bool) {
+func Open(out, box []byte, nonce *[24]byte, key *[32]byte) ([]byte, bool) {
 	if len(box) < Overhead {
 		return nil, false
 	}
@@ -126,6 +147,9 @@ func Open(out []byte, box []byte, nonce *[24]byte, key *[32]byte) ([]byte, bool)
 	}
 
 	ret, out := sliceForAppend(out, len(box)-Overhead)
+	if subtle.AnyOverlap(out, box) {
+		panic("nacl: invalid buffer overlap")
+	}
 
 	// We XOR up to 32 bytes of box with the keystream generated from
 	// the first block.

+ 90 - 0
psiphon/common/crypto/nacl/sign/sign.go

@@ -0,0 +1,90 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package sign signs small messages using public-key cryptography.
+//
+// Sign uses Ed25519 to sign messages. The length of messages is not hidden.
+// Messages should be small because:
+// 1. The whole message needs to be held in memory to be processed.
+// 2. Using large messages pressures implementations on small machines to process
+// plaintext without verifying the signature. This is very dangerous, and this API
+// discourages it, but a protocol that uses excessive message sizes might present
+// some implementations with no other choice.
+// 3. Performance may be improved by working with messages that fit into data caches.
+// Thus large amounts of data should be chunked so that each message is small.
+//
+// This package is not interoperable with the current release of NaCl
+// (https://nacl.cr.yp.to/sign.html), which does not support Ed25519 yet. However,
+// it is compatible with the NaCl fork libsodium (https://www.libsodium.org), as well
+// as TweetNaCl (https://tweetnacl.cr.yp.to/).
+package sign
+
+import (
+	"io"
+
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/ed25519"
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/internal/subtle"
+)
+
+// Overhead is the number of bytes of overhead when signing a message.
+const Overhead = 64
+
+// GenerateKey generates a new public/private key pair suitable for use with
+// Sign and Open.
+func GenerateKey(rand io.Reader) (publicKey *[32]byte, privateKey *[64]byte, err error) {
+	pub, priv, err := ed25519.GenerateKey(rand)
+	if err != nil {
+		return nil, nil, err
+	}
+	publicKey, privateKey = new([32]byte), new([64]byte)
+	copy((*publicKey)[:], pub)
+	copy((*privateKey)[:], priv)
+	return publicKey, privateKey, nil
+}
+
+// Sign appends a signed copy of message to out, which will be Overhead bytes
+// longer than the original and must not overlap it.
+func Sign(out, message []byte, privateKey *[64]byte) []byte {
+	sig := ed25519.Sign(ed25519.PrivateKey((*privateKey)[:]), message)
+	ret, out := sliceForAppend(out, Overhead+len(message))
+	if subtle.AnyOverlap(out, message) {
+		panic("nacl: invalid buffer overlap")
+	}
+	copy(out, sig)
+	copy(out[Overhead:], message)
+	return ret
+}
+
+// Open verifies a signed message produced by Sign and appends the message to
+// out, which must not overlap the signed message. The output will be Overhead
+// bytes smaller than the signed message.
+func Open(out, signedMessage []byte, publicKey *[32]byte) ([]byte, bool) {
+	if len(signedMessage) < Overhead {
+		return nil, false
+	}
+	if !ed25519.Verify(ed25519.PublicKey((*publicKey)[:]), signedMessage[Overhead:], signedMessage[:Overhead]) {
+		return nil, false
+	}
+	ret, out := sliceForAppend(out, len(signedMessage)-Overhead)
+	if subtle.AnyOverlap(out, signedMessage) {
+		panic("nacl: invalid buffer overlap")
+	}
+	copy(out, signedMessage[Overhead:])
+	return ret, true
+}
+
+// sliceForAppend takes a slice and a requested number of bytes. It returns a
+// slice with the contents of the given slice followed by that many bytes and a
+// second slice that aliases into it and contains only the extra bytes. If the
+// original slice has sufficient capacity then no allocation is performed.
+func sliceForAppend(in []byte, n int) (head, tail []byte) {
+	if total := len(in) + n; cap(in) >= total {
+		head = in[:total]
+	} else {
+		head = make([]byte, total)
+		copy(head, in)
+	}
+	tail = head[len(in):]
+	return
+}

+ 74 - 0
psiphon/common/crypto/nacl/sign/sign_test.go

@@ -0,0 +1,74 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package sign
+
+import (
+	"bytes"
+	"crypto/rand"
+	"encoding/hex"
+	"testing"
+)
+
+var testSignedMessage, _ = hex.DecodeString("26a0a47f733d02ddb74589b6cbd6f64a7dab1947db79395a1a9e00e4c902c0f185b119897b89b248d16bab4ea781b5a3798d25c2984aec833dddab57e0891e0d68656c6c6f20776f726c64")
+var testMessage = testSignedMessage[Overhead:]
+var testPublicKey [32]byte
+var testPrivateKey = [64]byte{
+	0x98, 0x3c, 0x6a, 0xa6, 0x21, 0xcc, 0xbb, 0xb2, 0xa7, 0xe8, 0x97, 0x94, 0xde, 0x5f, 0xf8, 0x11,
+	0x8a, 0xf3, 0x33, 0x1a, 0x03, 0x5c, 0x43, 0x99, 0x03, 0x13, 0x2d, 0xd7, 0xb4, 0xc4, 0x8b, 0xb0,
+	0xf6, 0x33, 0x20, 0xa3, 0x34, 0x8b, 0x7b, 0xe2, 0xfe, 0xb4, 0xe7, 0x3a, 0x54, 0x08, 0x2d, 0xd7,
+	0x0c, 0xb7, 0xc0, 0xe3, 0xbf, 0x62, 0x6c, 0x55, 0xf0, 0x33, 0x28, 0x52, 0xf8, 0x48, 0x7d, 0xfd,
+}
+
+func init() {
+	copy(testPublicKey[:], testPrivateKey[32:])
+}
+
+func TestSign(t *testing.T) {
+	signedMessage := Sign(nil, testMessage, &testPrivateKey)
+	if !bytes.Equal(signedMessage, testSignedMessage) {
+		t.Fatalf("signed message did not match, got\n%x\n, expected\n%x", signedMessage, testSignedMessage)
+	}
+}
+
+func TestOpen(t *testing.T) {
+	message, ok := Open(nil, testSignedMessage, &testPublicKey)
+	if !ok {
+		t.Fatalf("valid signed message not successfully verified")
+	}
+	if !bytes.Equal(message, testMessage) {
+		t.Fatalf("message did not match, got\n%x\n, expected\n%x", message, testMessage)
+	}
+	message, ok = Open(nil, testSignedMessage[1:], &testPublicKey)
+	if ok {
+		t.Fatalf("invalid signed message successfully verified")
+	}
+
+	badMessage := make([]byte, len(testSignedMessage))
+	copy(badMessage, testSignedMessage)
+	badMessage[5] ^= 1
+	if _, ok := Open(nil, badMessage, &testPublicKey); ok {
+		t.Fatalf("Open succeeded with a corrupt message")
+	}
+
+	var badPublicKey [32]byte
+	copy(badPublicKey[:], testPublicKey[:])
+	badPublicKey[5] ^= 1
+	if _, ok := Open(nil, testSignedMessage, &badPublicKey); ok {
+		t.Fatalf("Open succeeded with a corrupt public key")
+	}
+}
+
+func TestGenerateSignOpen(t *testing.T) {
+	publicKey, privateKey, _ := GenerateKey(rand.Reader)
+	signedMessage := Sign(nil, testMessage, privateKey)
+	message, ok := Open(nil, signedMessage, publicKey)
+	if !ok {
+		t.Fatalf("failed to verify signed message")
+	}
+
+	if !bytes.Equal(message, testMessage) {
+		t.Fatalf("verified message does not match signed messge, got\n%x\n, expected\n%x", message, testMessage)
+	}
+}

+ 20 - 17
psiphon/common/crypto/ocsp/ocsp.go

@@ -295,17 +295,17 @@ const (
 
 // The enumerated reasons for revoking a certificate.  See RFC 5280.
 const (
-	Unspecified          = iota
-	KeyCompromise        = iota
-	CACompromise         = iota
-	AffiliationChanged   = iota
-	Superseded           = iota
-	CessationOfOperation = iota
-	CertificateHold      = iota
-	_                    = iota
-	RemoveFromCRL        = iota
-	PrivilegeWithdrawn   = iota
-	AACompromise         = iota
+	Unspecified          = 0
+	KeyCompromise        = 1
+	CACompromise         = 2
+	AffiliationChanged   = 3
+	Superseded           = 4
+	CessationOfOperation = 5
+	CertificateHold      = 6
+
+	RemoveFromCRL      = 8
+	PrivilegeWithdrawn = 9
+	AACompromise       = 10
 )
 
 // Request represents an OCSP request. See RFC 6960.
@@ -488,10 +488,6 @@ func ParseResponseForCert(bytes []byte, cert, issuer *x509.Certificate) (*Respon
 		return nil, err
 	}
 
-	if len(basicResp.Certificates) > 1 {
-		return nil, ParseError("OCSP response contains bad number of certificates")
-	}
-
 	if n := len(basicResp.TBSResponseData.Responses); n == 0 || cert == nil && n > 1 {
 		return nil, ParseError("OCSP response contains bad number of responses")
 	}
@@ -544,6 +540,13 @@ func ParseResponseForCert(bytes []byte, cert, issuer *x509.Certificate) (*Respon
 	}
 
 	if len(basicResp.Certificates) > 0 {
+		// Responders should only send a single certificate (if they
+		// send any) that connects the responder's certificate to the
+		// original issuer. We accept responses with multiple
+		// certificates due to a number responders sending them[1], but
+		// ignore all but the first.
+		//
+		// [1] https://github.com/golang/go/issues/21527
 		ret.Certificate, err = x509.ParseCertificate(basicResp.Certificates[0].FullBytes)
 		if err != nil {
 			return nil, err
@@ -659,7 +662,7 @@ func CreateRequest(cert, issuer *x509.Certificate, opts *RequestOptions) ([]byte
 //
 // The issuer cert is used to puplate the IssuerNameHash and IssuerKeyHash fields.
 //
-// The template is used to populate the SerialNumber, RevocationStatus, RevokedAt,
+// The template is used to populate the SerialNumber, Status, RevokedAt,
 // RevocationReason, ThisUpdate, and NextUpdate fields.
 //
 // If template.IssuerHash is not set, SHA1 will be used.
@@ -760,7 +763,7 @@ func CreateResponse(issuer, responderCert *x509.Certificate, template Response,
 	}
 	if template.Certificate != nil {
 		response.Certificates = []asn1.RawValue{
-			asn1.RawValue{FullBytes: template.Certificate.Raw},
+			{FullBytes: template.Certificate.Raw},
 		}
 	}
 	responseDER, err := asn1.Marshal(response)

+ 6 - 6
psiphon/common/crypto/ocsp/ocsp_test.go

@@ -43,11 +43,11 @@ func TestOCSPDecode(t *testing.T) {
 	}
 
 	if !reflect.DeepEqual(resp.ThisUpdate, expected.ThisUpdate) {
-		t.Errorf("resp.ThisUpdate: got %d, want %d", resp.ThisUpdate, expected.ThisUpdate)
+		t.Errorf("resp.ThisUpdate: got %v, want %v", resp.ThisUpdate, expected.ThisUpdate)
 	}
 
 	if !reflect.DeepEqual(resp.NextUpdate, expected.NextUpdate) {
-		t.Errorf("resp.NextUpdate: got %d, want %d", resp.NextUpdate, expected.NextUpdate)
+		t.Errorf("resp.NextUpdate: got %v, want %v", resp.NextUpdate, expected.NextUpdate)
 	}
 
 	if resp.Status != expected.Status {
@@ -218,7 +218,7 @@ func TestOCSPResponse(t *testing.T) {
 
 	extensionBytes, _ := hex.DecodeString(ocspExtensionValueHex)
 	extensions := []pkix.Extension{
-		pkix.Extension{
+		{
 			Id:       ocspExtensionOID,
 			Critical: false,
 			Value:    extensionBytes,
@@ -268,15 +268,15 @@ func TestOCSPResponse(t *testing.T) {
 			}
 
 			if !reflect.DeepEqual(resp.ThisUpdate, template.ThisUpdate) {
-				t.Errorf("resp.ThisUpdate: got %d, want %d", resp.ThisUpdate, template.ThisUpdate)
+				t.Errorf("resp.ThisUpdate: got %v, want %v", resp.ThisUpdate, template.ThisUpdate)
 			}
 
 			if !reflect.DeepEqual(resp.NextUpdate, template.NextUpdate) {
-				t.Errorf("resp.NextUpdate: got %d, want %d", resp.NextUpdate, template.NextUpdate)
+				t.Errorf("resp.NextUpdate: got %v, want %v", resp.NextUpdate, template.NextUpdate)
 			}
 
 			if !reflect.DeepEqual(resp.RevokedAt, template.RevokedAt) {
-				t.Errorf("resp.RevokedAt: got %d, want %d", resp.RevokedAt, template.RevokedAt)
+				t.Errorf("resp.RevokedAt: got %v, want %v", resp.RevokedAt, template.RevokedAt)
 			}
 
 			if !reflect.DeepEqual(resp.Extensions, template.ExtraExtensions) {

+ 49 - 26
psiphon/common/crypto/openpgp/clearsign/clearsign.go

@@ -13,6 +13,7 @@ import (
 	"bufio"
 	"bytes"
 	"crypto"
+	"fmt"
 	"hash"
 	"io"
 	"net/textproto"
@@ -177,8 +178,9 @@ func Decode(data []byte) (b *Block, rest []byte) {
 // message.
 type dashEscaper struct {
 	buffered *bufio.Writer
-	h        hash.Hash
+	hashers  []hash.Hash // one per key in privateKeys
 	hashType crypto.Hash
+	toHash   io.Writer // writes to all the hashes in hashers
 
 	atBeginningOfLine bool
 	isFirstLine       bool
@@ -186,8 +188,8 @@ type dashEscaper struct {
 	whitespace []byte
 	byteBuf    []byte // a one byte buffer to save allocations
 
-	privateKey *packet.PrivateKey
-	config     *packet.Config
+	privateKeys []*packet.PrivateKey
+	config      *packet.Config
 }
 
 func (d *dashEscaper) Write(data []byte) (n int, err error) {
@@ -198,7 +200,7 @@ func (d *dashEscaper) Write(data []byte) (n int, err error) {
 			// The final CRLF isn't included in the hash so we have to wait
 			// until this point (the start of the next line) before writing it.
 			if !d.isFirstLine {
-				d.h.Write(crlf)
+				d.toHash.Write(crlf)
 			}
 			d.isFirstLine = false
 		}
@@ -219,12 +221,12 @@ func (d *dashEscaper) Write(data []byte) (n int, err error) {
 				if _, err = d.buffered.Write(dashEscape); err != nil {
 					return
 				}
-				d.h.Write(d.byteBuf)
+				d.toHash.Write(d.byteBuf)
 				d.atBeginningOfLine = false
 			} else if b == '\n' {
 				// Nothing to do because we delay writing CRLF to the hash.
 			} else {
-				d.h.Write(d.byteBuf)
+				d.toHash.Write(d.byteBuf)
 				d.atBeginningOfLine = false
 			}
 			if err = d.buffered.WriteByte(b); err != nil {
@@ -245,13 +247,13 @@ func (d *dashEscaper) Write(data []byte) (n int, err error) {
 				// Any buffered whitespace wasn't at the end of the line so
 				// we need to write it out.
 				if len(d.whitespace) > 0 {
-					d.h.Write(d.whitespace)
+					d.toHash.Write(d.whitespace)
 					if _, err = d.buffered.Write(d.whitespace); err != nil {
 						return
 					}
 					d.whitespace = d.whitespace[:0]
 				}
-				d.h.Write(d.byteBuf)
+				d.toHash.Write(d.byteBuf)
 				if err = d.buffered.WriteByte(b); err != nil {
 					return
 				}
@@ -269,25 +271,29 @@ func (d *dashEscaper) Close() (err error) {
 			return
 		}
 	}
-	sig := new(packet.Signature)
-	sig.SigType = packet.SigTypeText
-	sig.PubKeyAlgo = d.privateKey.PubKeyAlgo
-	sig.Hash = d.hashType
-	sig.CreationTime = d.config.Now()
-	sig.IssuerKeyId = &d.privateKey.KeyId
-
-	if err = sig.Sign(d.h, d.privateKey, d.config); err != nil {
-		return
-	}
 
 	out, err := armor.Encode(d.buffered, "PGP SIGNATURE", nil)
 	if err != nil {
 		return
 	}
 
-	if err = sig.Serialize(out); err != nil {
-		return
+	t := d.config.Now()
+	for i, k := range d.privateKeys {
+		sig := new(packet.Signature)
+		sig.SigType = packet.SigTypeText
+		sig.PubKeyAlgo = k.PubKeyAlgo
+		sig.Hash = d.hashType
+		sig.CreationTime = t
+		sig.IssuerKeyId = &k.KeyId
+
+		if err = sig.Sign(d.hashers[i], k, d.config); err != nil {
+			return
+		}
+		if err = sig.Serialize(out); err != nil {
+			return
+		}
 	}
+
 	if err = out.Close(); err != nil {
 		return
 	}
@@ -300,8 +306,17 @@ func (d *dashEscaper) Close() (err error) {
 // Encode returns a WriteCloser which will clear-sign a message with privateKey
 // and write it to w. If config is nil, sensible defaults are used.
 func Encode(w io.Writer, privateKey *packet.PrivateKey, config *packet.Config) (plaintext io.WriteCloser, err error) {
-	if privateKey.Encrypted {
-		return nil, errors.InvalidArgumentError("signing key is encrypted")
+	return EncodeMulti(w, []*packet.PrivateKey{privateKey}, config)
+}
+
+// EncodeMulti returns a WriteCloser which will clear-sign a message with all the
+// private keys indicated and write it to w. If config is nil, sensible defaults
+// are used.
+func EncodeMulti(w io.Writer, privateKeys []*packet.PrivateKey, config *packet.Config) (plaintext io.WriteCloser, err error) {
+	for _, k := range privateKeys {
+		if k.Encrypted {
+			return nil, errors.InvalidArgumentError(fmt.Sprintf("signing key %s is encrypted", k.KeyIdString()))
+		}
 	}
 
 	hashType := config.Hash()
@@ -313,7 +328,14 @@ func Encode(w io.Writer, privateKey *packet.PrivateKey, config *packet.Config) (
 	if !hashType.Available() {
 		return nil, errors.UnsupportedError("unsupported hash type: " + strconv.Itoa(int(hashType)))
 	}
-	h := hashType.New()
+	var hashers []hash.Hash
+	var ws []io.Writer
+	for range privateKeys {
+		h := hashType.New()
+		hashers = append(hashers, h)
+		ws = append(ws, h)
+	}
+	toHash := io.MultiWriter(ws...)
 
 	buffered := bufio.NewWriter(w)
 	// start has a \n at the beginning that we don't want here.
@@ -338,16 +360,17 @@ func Encode(w io.Writer, privateKey *packet.PrivateKey, config *packet.Config) (
 
 	plaintext = &dashEscaper{
 		buffered: buffered,
-		h:        h,
+		hashers:  hashers,
 		hashType: hashType,
+		toHash:   toHash,
 
 		atBeginningOfLine: true,
 		isFirstLine:       true,
 
 		byteBuf: make([]byte, 1),
 
-		privateKey: privateKey,
-		config:     config,
+		privateKeys: privateKeys,
+		config:      config,
 	}
 
 	return

+ 69 - 1
psiphon/common/crypto/openpgp/clearsign/clearsign_test.go

@@ -6,8 +6,11 @@ package clearsign
 
 import (
 	"bytes"
-	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/openpgp"
+	"fmt"
 	"testing"
+
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/openpgp"
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/openpgp/packet"
 )
 
 func testParse(t *testing.T, input []byte, expected, expectedPlaintext string) {
@@ -125,6 +128,71 @@ func TestSigning(t *testing.T) {
 	}
 }
 
+// We use this to make test keys, so that they aren't all the same.
+type quickRand byte
+
+func (qr *quickRand) Read(p []byte) (int, error) {
+	for i := range p {
+		p[i] = byte(*qr)
+	}
+	*qr++
+	return len(p), nil
+}
+
+func TestMultiSign(t *testing.T) {
+	zero := quickRand(0)
+	config := packet.Config{Rand: &zero}
+
+	for nKeys := 0; nKeys < 4; nKeys++ {
+	nextTest:
+		for nExtra := 0; nExtra < 4; nExtra++ {
+			var signKeys []*packet.PrivateKey
+			var verifyKeys openpgp.EntityList
+
+			desc := fmt.Sprintf("%d keys; %d of which will be used to verify", nKeys+nExtra, nKeys)
+			for i := 0; i < nKeys+nExtra; i++ {
+				e, err := openpgp.NewEntity("name", "comment", "email", &config)
+				if err != nil {
+					t.Errorf("cannot create key: %v", err)
+					continue nextTest
+				}
+				if i < nKeys {
+					verifyKeys = append(verifyKeys, e)
+				}
+				signKeys = append(signKeys, e.PrivateKey)
+			}
+
+			input := []byte("this is random text\r\n4 17")
+			var output bytes.Buffer
+			w, err := EncodeMulti(&output, signKeys, nil)
+			if err != nil {
+				t.Errorf("EncodeMulti (%s) failed: %v", desc, err)
+			}
+			if _, err := w.Write(input); err != nil {
+				t.Errorf("Write(%q) to signer (%s) failed: %v", string(input), desc, err)
+			}
+			if err := w.Close(); err != nil {
+				t.Errorf("Close() of signer (%s) failed: %v", desc, err)
+			}
+
+			block, _ := Decode(output.Bytes())
+			if string(block.Bytes) != string(input) {
+				t.Errorf("Inline data didn't match original; got %q want %q", string(block.Bytes), string(input))
+			}
+			_, err = openpgp.CheckDetachedSignature(verifyKeys, bytes.NewReader(block.Bytes), block.ArmoredSignature.Body)
+			if nKeys == 0 {
+				if err == nil {
+					t.Errorf("verifying inline (%s) succeeded; want failure", desc)
+				}
+			} else {
+				if err != nil {
+					t.Errorf("verifying inline (%s) failed (%v); want success", desc, err)
+				}
+			}
+		}
+	}
+}
+
 var clearsignInput = []byte(`
 ;lasjlkfdsa
 

+ 112 - 56
psiphon/common/crypto/openpgp/keys.go

@@ -325,16 +325,14 @@ func ReadEntity(packets *packet.Reader) (*Entity, error) {
 		if e.PrivateKey, ok = p.(*packet.PrivateKey); !ok {
 			packets.Unread(p)
 			return nil, errors.StructuralError("first packet was not a public/private key")
-		} else {
-			e.PrimaryKey = &e.PrivateKey.PublicKey
 		}
+		e.PrimaryKey = &e.PrivateKey.PublicKey
 	}
 
 	if !e.PrimaryKey.PubKeyAlgo.CanSign() {
 		return nil, errors.StructuralError("primary key cannot be used for signatures")
 	}
 
-	var current *Identity
 	var revocations []*packet.Signature
 EachPacket:
 	for {
@@ -347,32 +345,8 @@ EachPacket:
 
 		switch pkt := p.(type) {
 		case *packet.UserId:
-			current = new(Identity)
-			current.Name = pkt.Id
-			current.UserId = pkt
-			e.Identities[pkt.Id] = current
-
-			for {
-				p, err = packets.Next()
-				if err == io.EOF {
-					return nil, io.ErrUnexpectedEOF
-				} else if err != nil {
-					return nil, err
-				}
-
-				sig, ok := p.(*packet.Signature)
-				if !ok {
-					return nil, errors.StructuralError("user ID packet not followed by self-signature")
-				}
-
-				if (sig.SigType == packet.SigTypePositiveCert || sig.SigType == packet.SigTypeGenericCert) && sig.IssuerKeyId != nil && *sig.IssuerKeyId == e.PrimaryKey.KeyId {
-					if err = e.PrimaryKey.VerifyUserIdSignature(pkt.Id, e.PrimaryKey, sig); err != nil {
-						return nil, errors.StructuralError("user ID self-signature invalid: " + err.Error())
-					}
-					current.SelfSignature = sig
-					break
-				}
-				current.Signatures = append(current.Signatures, sig)
+			if err := addUserID(e, packets, pkt); err != nil {
+				return nil, err
 			}
 		case *packet.Signature:
 			if pkt.SigType == packet.SigTypeKeyRevocation {
@@ -381,11 +355,9 @@ EachPacket:
 				// TODO: RFC4880 5.2.1 permits signatures
 				// directly on keys (eg. to bind additional
 				// revocation keys).
-			} else if current == nil {
-				return nil, errors.StructuralError("signature packet found before user id packet")
-			} else {
-				current.Signatures = append(current.Signatures, pkt)
 			}
+			// Else, ignoring the signature as it does not follow anything
+			// we would know to attach it to.
 		case *packet.PrivateKey:
 			if pkt.IsSubkey == false {
 				packets.Unread(p)
@@ -426,33 +398,105 @@ EachPacket:
 	return e, nil
 }
 
+func addUserID(e *Entity, packets *packet.Reader, pkt *packet.UserId) error {
+	// Make a new Identity object, that we might wind up throwing away.
+	// We'll only add it if we get a valid self-signature over this
+	// userID.
+	identity := new(Identity)
+	identity.Name = pkt.Id
+	identity.UserId = pkt
+
+	for {
+		p, err := packets.Next()
+		if err == io.EOF {
+			break
+		} else if err != nil {
+			return err
+		}
+
+		sig, ok := p.(*packet.Signature)
+		if !ok {
+			packets.Unread(p)
+			break
+		}
+
+		if (sig.SigType == packet.SigTypePositiveCert || sig.SigType == packet.SigTypeGenericCert) && sig.IssuerKeyId != nil && *sig.IssuerKeyId == e.PrimaryKey.KeyId {
+			if err = e.PrimaryKey.VerifyUserIdSignature(pkt.Id, e.PrimaryKey, sig); err != nil {
+				return errors.StructuralError("user ID self-signature invalid: " + err.Error())
+			}
+			identity.SelfSignature = sig
+			e.Identities[pkt.Id] = identity
+		} else {
+			identity.Signatures = append(identity.Signatures, sig)
+		}
+	}
+
+	return nil
+}
+
 func addSubkey(e *Entity, packets *packet.Reader, pub *packet.PublicKey, priv *packet.PrivateKey) error {
 	var subKey Subkey
 	subKey.PublicKey = pub
 	subKey.PrivateKey = priv
-	p, err := packets.Next()
-	if err == io.EOF {
-		return io.ErrUnexpectedEOF
-	}
-	if err != nil {
-		return errors.StructuralError("subkey signature invalid: " + err.Error())
+
+	for {
+		p, err := packets.Next()
+		if err == io.EOF {
+			break
+		} else if err != nil {
+			return errors.StructuralError("subkey signature invalid: " + err.Error())
+		}
+
+		sig, ok := p.(*packet.Signature)
+		if !ok {
+			packets.Unread(p)
+			break
+		}
+
+		if sig.SigType != packet.SigTypeSubkeyBinding && sig.SigType != packet.SigTypeSubkeyRevocation {
+			return errors.StructuralError("subkey signature with wrong type")
+		}
+
+		if err := e.PrimaryKey.VerifyKeySignature(subKey.PublicKey, sig); err != nil {
+			return errors.StructuralError("subkey signature invalid: " + err.Error())
+		}
+
+		switch sig.SigType {
+		case packet.SigTypeSubkeyRevocation:
+			subKey.Sig = sig
+		case packet.SigTypeSubkeyBinding:
+
+			if shouldReplaceSubkeySig(subKey.Sig, sig) {
+				subKey.Sig = sig
+			}
+		}
 	}
-	var ok bool
-	subKey.Sig, ok = p.(*packet.Signature)
-	if !ok {
+
+	if subKey.Sig == nil {
 		return errors.StructuralError("subkey packet not followed by signature")
 	}
-	if subKey.Sig.SigType != packet.SigTypeSubkeyBinding && subKey.Sig.SigType != packet.SigTypeSubkeyRevocation {
-		return errors.StructuralError("subkey signature with wrong type")
-	}
-	err = e.PrimaryKey.VerifyKeySignature(subKey.PublicKey, subKey.Sig)
-	if err != nil {
-		return errors.StructuralError("subkey signature invalid: " + err.Error())
-	}
+
 	e.Subkeys = append(e.Subkeys, subKey)
+
 	return nil
 }
 
+func shouldReplaceSubkeySig(existingSig, potentialNewSig *packet.Signature) bool {
+	if potentialNewSig == nil {
+		return false
+	}
+
+	if existingSig == nil {
+		return true
+	}
+
+	if existingSig.SigType == packet.SigTypeSubkeyRevocation {
+		return false // never override a revocation signature
+	}
+
+	return potentialNewSig.CreationTime.After(existingSig.CreationTime)
+}
+
 const defaultRSAKeyBits = 2048
 
 // NewEntity returns an Entity that contains a fresh RSA/RSA keypair with a
@@ -487,7 +531,7 @@ func NewEntity(name, comment, email string, config *packet.Config) (*Entity, err
 	}
 	isPrimaryId := true
 	e.Identities[uid.Id] = &Identity{
-		Name:   uid.Name,
+		Name:   uid.Id,
 		UserId: uid,
 		SelfSignature: &packet.Signature{
 			CreationTime: currentTime,
@@ -501,6 +545,10 @@ func NewEntity(name, comment, email string, config *packet.Config) (*Entity, err
 			IssuerKeyId:  &e.PrimaryKey.KeyId,
 		},
 	}
+	err = e.Identities[uid.Id].SelfSignature.SignUserId(uid.Id, e.PrimaryKey, e.PrivateKey, config)
+	if err != nil {
+		return nil, err
+	}
 
 	// If the user passes in a DefaultHash via packet.Config,
 	// set the PreferredHash for the SelfSignature.
@@ -508,6 +556,11 @@ func NewEntity(name, comment, email string, config *packet.Config) (*Entity, err
 		e.Identities[uid.Id].SelfSignature.PreferredHash = []uint8{hashToHashId(config.DefaultHash)}
 	}
 
+	// Likewise for DefaultCipher.
+	if config != nil && config.DefaultCipher != 0 {
+		e.Identities[uid.Id].SelfSignature.PreferredSymmetric = []uint8{uint8(config.DefaultCipher)}
+	}
+
 	e.Subkeys = make([]Subkey, 1)
 	e.Subkeys[0] = Subkey{
 		PublicKey:  packet.NewRSAPublicKey(currentTime, &encryptingPriv.PublicKey),
@@ -525,13 +578,16 @@ func NewEntity(name, comment, email string, config *packet.Config) (*Entity, err
 	}
 	e.Subkeys[0].PublicKey.IsSubkey = true
 	e.Subkeys[0].PrivateKey.IsSubkey = true
-
+	err = e.Subkeys[0].Sig.SignKey(e.Subkeys[0].PublicKey, e.PrivateKey, config)
+	if err != nil {
+		return nil, err
+	}
 	return e, nil
 }
 
-// SerializePrivate serializes an Entity, including private key material, to
-// the given Writer. For now, it must only be used on an Entity returned from
-// NewEntity.
+// SerializePrivate serializes an Entity, including private key material, but
+// excluding signatures from other entities, to the given Writer.
+// Identities and subkeys are re-signed in case they changed since NewEntry.
 // If config is nil, sensible defaults will be used.
 func (e *Entity) SerializePrivate(w io.Writer, config *packet.Config) (err error) {
 	err = e.PrivateKey.Serialize(w)
@@ -569,8 +625,8 @@ func (e *Entity) SerializePrivate(w io.Writer, config *packet.Config) (err error
 	return nil
 }
 
-// Serialize writes the public part of the given Entity to w. (No private
-// key material will be output).
+// Serialize writes the public part of the given Entity to w, including
+// signatures from other entities. No private key material will be output.
 func (e *Entity) Serialize(w io.Writer) error {
 	err := e.PrimaryKey.Serialize(w)
 	if err != nil {

Разница между файлами не показана из-за своего большого размера
+ 198 - 9
psiphon/common/crypto/openpgp/keys_test.go


+ 8 - 1
psiphon/common/crypto/openpgp/packet/encrypted_key.go

@@ -42,12 +42,18 @@ func (e *EncryptedKey) parse(r io.Reader) (err error) {
 	switch e.Algo {
 	case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly:
 		e.encryptedMPI1.bytes, e.encryptedMPI1.bitLength, err = readMPI(r)
+		if err != nil {
+			return
+		}
 	case PubKeyAlgoElGamal:
 		e.encryptedMPI1.bytes, e.encryptedMPI1.bitLength, err = readMPI(r)
 		if err != nil {
 			return
 		}
 		e.encryptedMPI2.bytes, e.encryptedMPI2.bitLength, err = readMPI(r)
+		if err != nil {
+			return
+		}
 	}
 	_, err = consumeAll(r)
 	return
@@ -72,7 +78,8 @@ func (e *EncryptedKey) Decrypt(priv *PrivateKey, config *Config) error {
 	// padding oracle attacks.
 	switch priv.PubKeyAlgo {
 	case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly:
-		b, err = rsa.DecryptPKCS1v15(config.Random(), priv.PrivateKey.(*rsa.PrivateKey), e.encryptedMPI1.bytes)
+		k := priv.PrivateKey.(*rsa.PrivateKey)
+		b, err = rsa.DecryptPKCS1v15(config.Random(), k, padToKeySize(&k.PublicKey, e.encryptedMPI1.bytes))
 	case PubKeyAlgoElGamal:
 		c1 := new(big.Int).SetBytes(e.encryptedMPI1.bytes)
 		c2 := new(big.Int).SetBytes(e.encryptedMPI2.bytes)

+ 39 - 34
psiphon/common/crypto/openpgp/packet/encrypted_key_test.go

@@ -39,39 +39,44 @@ var encryptedKeyPriv = &PrivateKey{
 }
 
 func TestDecryptingEncryptedKey(t *testing.T) {
-	const encryptedKeyHex = "c18c032a67d68660df41c70104005789d0de26b6a50c985a02a13131ca829c413a35d0e6fa8d6842599252162808ac7439c72151c8c6183e76923fe3299301414d0c25a2f06a2257db3839e7df0ec964773f6e4c4ac7ff3b48c444237166dd46ba8ff443a5410dc670cb486672fdbe7c9dfafb75b4fea83af3a204fe2a7dfa86bd20122b4f3d2646cbeecb8f7be8"
-	const expectedKeyHex = "d930363f7e0308c333b9618617ea728963d8df993665ae7be1092d4926fd864b"
-
-	p, err := Read(readerFromHex(encryptedKeyHex))
-	if err != nil {
-		t.Errorf("error from Read: %s", err)
-		return
-	}
-	ek, ok := p.(*EncryptedKey)
-	if !ok {
-		t.Errorf("didn't parse an EncryptedKey, got %#v", p)
-		return
-	}
-
-	if ek.KeyId != 0x2a67d68660df41c7 || ek.Algo != PubKeyAlgoRSA {
-		t.Errorf("unexpected EncryptedKey contents: %#v", ek)
-		return
-	}
-
-	err = ek.Decrypt(encryptedKeyPriv, nil)
-	if err != nil {
-		t.Errorf("error from Decrypt: %s", err)
-		return
-	}
-
-	if ek.CipherFunc != CipherAES256 {
-		t.Errorf("unexpected EncryptedKey contents: %#v", ek)
-		return
-	}
-
-	keyHex := fmt.Sprintf("%x", ek.Key)
-	if keyHex != expectedKeyHex {
-		t.Errorf("bad key, got %s want %x", keyHex, expectedKeyHex)
+	for i, encryptedKeyHex := range []string{
+		"c18c032a67d68660df41c70104005789d0de26b6a50c985a02a13131ca829c413a35d0e6fa8d6842599252162808ac7439c72151c8c6183e76923fe3299301414d0c25a2f06a2257db3839e7df0ec964773f6e4c4ac7ff3b48c444237166dd46ba8ff443a5410dc670cb486672fdbe7c9dfafb75b4fea83af3a204fe2a7dfa86bd20122b4f3d2646cbeecb8f7be8",
+		// MPI can be shorter than the length of the key.
+		"c18b032a67d68660df41c70103f8e520c52ae9807183c669ce26e772e482dc5d8cf60e6f59316e145be14d2e5221ee69550db1d5618a8cb002a719f1f0b9345bde21536d410ec90ba86cac37748dec7933eb7f9873873b2d61d3321d1cd44535014f6df58f7bc0c7afb5edc38e1a974428997d2f747f9a173bea9ca53079b409517d332df62d805564cffc9be6",
+	} {
+		const expectedKeyHex = "d930363f7e0308c333b9618617ea728963d8df993665ae7be1092d4926fd864b"
+
+		p, err := Read(readerFromHex(encryptedKeyHex))
+		if err != nil {
+			t.Errorf("#%d: error from Read: %s", i, err)
+			return
+		}
+		ek, ok := p.(*EncryptedKey)
+		if !ok {
+			t.Errorf("#%d: didn't parse an EncryptedKey, got %#v", i, p)
+			return
+		}
+
+		if ek.KeyId != 0x2a67d68660df41c7 || ek.Algo != PubKeyAlgoRSA {
+			t.Errorf("#%d: unexpected EncryptedKey contents: %#v", i, ek)
+			return
+		}
+
+		err = ek.Decrypt(encryptedKeyPriv, nil)
+		if err != nil {
+			t.Errorf("#%d: error from Decrypt: %s", i, err)
+			return
+		}
+
+		if ek.CipherFunc != CipherAES256 {
+			t.Errorf("#%d: unexpected EncryptedKey contents: %#v", i, ek)
+			return
+		}
+
+		keyHex := fmt.Sprintf("%x", ek.Key)
+		if keyHex != expectedKeyHex {
+			t.Errorf("#%d: bad key, got %s want %s", i, keyHex, expectedKeyHex)
+		}
 	}
 }
 
@@ -121,7 +126,7 @@ func TestEncryptingEncryptedKey(t *testing.T) {
 
 	keyHex := fmt.Sprintf("%x", ek.Key)
 	if keyHex != expectedKeyHex {
-		t.Errorf("bad key, got %s want %x", keyHex, expectedKeyHex)
+		t.Errorf("bad key, got %s want %s", keyHex, expectedKeyHex)
 	}
 }
 

+ 22 - 10
psiphon/common/crypto/openpgp/packet/packet.go

@@ -11,10 +11,12 @@ import (
 	"crypto/aes"
 	"crypto/cipher"
 	"crypto/des"
-	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/cast5"
-	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/openpgp/errors"
+	"crypto/rsa"
 	"io"
 	"math/big"
+
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/cast5"
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/openpgp/errors"
 )
 
 // readFull is the same as io.ReadFull except that reading zero bytes returns
@@ -500,19 +502,17 @@ func readMPI(r io.Reader) (mpi []byte, bitLength uint16, err error) {
 	numBytes := (int(bitLength) + 7) / 8
 	mpi = make([]byte, numBytes)
 	_, err = readFull(r, mpi)
-	return
-}
-
-// mpiLength returns the length of the given *big.Int when serialized as an
-// MPI.
-func mpiLength(n *big.Int) (mpiLengthInBytes int) {
-	mpiLengthInBytes = 2 /* MPI length */
-	mpiLengthInBytes += (n.BitLen() + 7) / 8
+	// According to RFC 4880 3.2. we should check that the MPI has no leading
+	// zeroes (at least when not an encrypted MPI?), but this implementation
+	// does generate leading zeroes, so we keep accepting them.
 	return
 }
 
 // writeMPI serializes a big integer to w.
 func writeMPI(w io.Writer, bitLength uint16, mpiBytes []byte) (err error) {
+	// Note that we can produce leading zeroes, in violation of RFC 4880 3.2.
+	// Implementations seem to be tolerant of them, and stripping them would
+	// make it complex to guarantee matching re-serialization.
 	_, err = w.Write([]byte{byte(bitLength >> 8), byte(bitLength)})
 	if err == nil {
 		_, err = w.Write(mpiBytes)
@@ -525,6 +525,18 @@ func writeBig(w io.Writer, i *big.Int) error {
 	return writeMPI(w, uint16(i.BitLen()), i.Bytes())
 }
 
+// padToKeySize left-pads a MPI with zeroes to match the length of the
+// specified RSA public.
+func padToKeySize(pub *rsa.PublicKey, b []byte) []byte {
+	k := (pub.N.BitLen() + 7) / 8
+	if len(b) >= k {
+		return b
+	}
+	bb := make([]byte, k)
+	copy(bb[len(bb)-len(b):], b)
+	return bb
+}
+
 // CompressionAlgo Represents the different compression algorithms
 // supported by OpenPGP (except for BZIP2, which is not currently
 // supported). See Section 9.3 of RFC 4880.

+ 7 - 0
psiphon/common/crypto/openpgp/packet/private_key.go

@@ -68,10 +68,17 @@ func NewECDSAPrivateKey(currentTime time.Time, priv *ecdsa.PrivateKey) *PrivateK
 // implements RSA or ECDSA.
 func NewSignerPrivateKey(currentTime time.Time, signer crypto.Signer) *PrivateKey {
 	pk := new(PrivateKey)
+	// In general, the public Keys should be used as pointers. We still
+	// type-switch on the values, for backwards-compatibility.
 	switch pubkey := signer.Public().(type) {
+	case *rsa.PublicKey:
+		pk.PublicKey = *NewRSAPublicKey(currentTime, pubkey)
+		pk.PubKeyAlgo = PubKeyAlgoRSASignOnly
 	case rsa.PublicKey:
 		pk.PublicKey = *NewRSAPublicKey(currentTime, &pubkey)
 		pk.PubKeyAlgo = PubKeyAlgoRSASignOnly
+	case *ecdsa.PublicKey:
+		pk.PublicKey = *NewECDSAPublicKey(currentTime, pubkey)
 	case ecdsa.PublicKey:
 		pk.PublicKey = *NewECDSAPublicKey(currentTime, &pubkey)
 	default:

+ 3 - 20
psiphon/common/crypto/openpgp/packet/private_key_test.go

@@ -14,7 +14,6 @@ import (
 	"crypto/x509"
 	"encoding/hex"
 	"hash"
-	"io"
 	"testing"
 	"time"
 )
@@ -162,15 +161,7 @@ func TestECDSAPrivateKey(t *testing.T) {
 }
 
 type rsaSigner struct {
-	priv *rsa.PrivateKey
-}
-
-func (s *rsaSigner) Public() crypto.PublicKey {
-	return s.priv.PublicKey
-}
-
-func (s *rsaSigner) Sign(rand io.Reader, msg []byte, opts crypto.SignerOpts) ([]byte, error) {
-	return s.priv.Sign(rand, msg, opts)
+	*rsa.PrivateKey
 }
 
 func TestRSASignerPrivateKey(t *testing.T) {
@@ -208,15 +199,7 @@ func TestRSASignerPrivateKey(t *testing.T) {
 }
 
 type ecdsaSigner struct {
-	priv *ecdsa.PrivateKey
-}
-
-func (s *ecdsaSigner) Public() crypto.PublicKey {
-	return s.priv.PublicKey
-}
-
-func (s *ecdsaSigner) Sign(rand io.Reader, msg []byte, opts crypto.SignerOpts) ([]byte, error) {
-	return s.priv.Sign(rand, msg, opts)
+	*ecdsa.PrivateKey
 }
 
 func TestECDSASignerPrivateKey(t *testing.T) {
@@ -228,7 +211,7 @@ func TestECDSASignerPrivateKey(t *testing.T) {
 	priv := NewSignerPrivateKey(time.Now(), &ecdsaSigner{ecdsaPriv})
 
 	if priv.PubKeyAlgo != PubKeyAlgoECDSA {
-		t.Fatal("NewSignerPrivateKey should have made a ECSDA private key")
+		t.Fatal("NewSignerPrivateKey should have made an ECSDA private key")
 	}
 
 	sig := &Signature{

+ 8 - 3
psiphon/common/crypto/openpgp/packet/public_key.go

@@ -244,7 +244,12 @@ func NewECDSAPublicKey(creationTime time.Time, pub *ecdsa.PublicKey) *PublicKey
 	}
 
 	pk.ec.p.bytes = elliptic.Marshal(pub.Curve, pub.X, pub.Y)
-	pk.ec.p.bitLength = uint16(8 * len(pk.ec.p.bytes))
+
+	// The bit length is 3 (for the 0x04 specifying an uncompressed key)
+	// plus two field elements (for x and y), which are rounded up to the
+	// nearest byte. See https://tools.ietf.org/html/rfc6637#section-6
+	fieldBytes := (pub.Curve.Params().BitSize + 7) & ^7
+	pk.ec.p.bitLength = uint16(3 + fieldBytes + fieldBytes)
 
 	pk.setFingerPrintAndKeyId()
 	return pk
@@ -515,7 +520,7 @@ func (pk *PublicKey) VerifySignature(signed hash.Hash, sig *Signature) (err erro
 	switch pk.PubKeyAlgo {
 	case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
 		rsaPublicKey, _ := pk.PublicKey.(*rsa.PublicKey)
-		err = rsa.VerifyPKCS1v15(rsaPublicKey, sig.Hash, hashBytes, sig.RSASignature.bytes)
+		err = rsa.VerifyPKCS1v15(rsaPublicKey, sig.Hash, hashBytes, padToKeySize(rsaPublicKey, sig.RSASignature.bytes))
 		if err != nil {
 			return errors.SignatureError("RSA verification failure")
 		}
@@ -566,7 +571,7 @@ func (pk *PublicKey) VerifySignatureV3(signed hash.Hash, sig *SignatureV3) (err
 	switch pk.PubKeyAlgo {
 	case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
 		rsaPublicKey := pk.PublicKey.(*rsa.PublicKey)
-		if err = rsa.VerifyPKCS1v15(rsaPublicKey, sig.Hash, hashBytes, sig.RSASignature.bytes); err != nil {
+		if err = rsa.VerifyPKCS1v15(rsaPublicKey, sig.Hash, hashBytes, padToKeySize(rsaPublicKey, sig.RSASignature.bytes)); err != nil {
 			return errors.SignatureError("RSA verification failure")
 		}
 		return

+ 26 - 0
psiphon/common/crypto/openpgp/packet/public_key_test.go

@@ -6,7 +6,10 @@ package packet
 
 import (
 	"bytes"
+	"crypto/ecdsa"
+	"crypto/elliptic"
 	"encoding/hex"
+	"math/big"
 	"testing"
 	"time"
 )
@@ -186,6 +189,29 @@ func TestEcc384Serialize(t *testing.T) {
 	}
 }
 
+func TestP256KeyID(t *testing.T) {
+	// Confirm that key IDs are correctly calculated for ECC keys.
+	ecdsaPub := &ecdsa.PublicKey{
+		Curve: elliptic.P256(),
+		X:     fromHex("81fbbc20eea9e8d1c3ceabb0a8185925b113d1ac42cd5c78403bd83da19235c6"),
+		Y:     fromHex("5ed6db13d91db34507d0129bf88981878d29adbf8fcd1720afdb767bb3fcaaff"),
+	}
+	pub := NewECDSAPublicKey(time.Unix(1297309478, 0), ecdsaPub)
+
+	const want = uint64(0xd01055fbcadd268e)
+	if pub.KeyId != want {
+		t.Errorf("want key ID: %x, got %x", want, pub.KeyId)
+	}
+}
+
+func fromHex(hex string) *big.Int {
+	n, ok := new(big.Int).SetString(hex, 16)
+	if !ok {
+		panic("bad hex number: " + hex)
+	}
+	return n
+}
+
 const rsaFingerprintHex = "5fb74b1d03b1e3cb31bc2f8aa34d7e18c20c31bb"
 
 const rsaPkDataHex = "988d044d3c5c10010400b1d13382944bd5aba23a4312968b5095d14f947f600eb478e14a6fcb16b0e0cac764884909c020bc495cfcc39a935387c661507bdb236a0612fb582cac3af9b29cc2c8c70090616c41b662f4da4c1201e195472eb7f4ae1ccbcbf9940fe21d985e379a5563dde5b9a23d35f1cfaa5790da3b79db26f23695107bfaca8e7b5bcd0011010001"

+ 3 - 3
psiphon/common/crypto/openpgp/packet/symmetric_key_encrypted.go

@@ -88,10 +88,10 @@ func (ske *SymmetricKeyEncrypted) Decrypt(passphrase []byte) ([]byte, CipherFunc
 		return nil, ske.CipherFunc, errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(cipherFunc)))
 	}
 	plaintextKey = plaintextKey[1:]
-	if l := len(plaintextKey); l == 0 || l%cipherFunc.blockSize() != 0 {
-		return nil, cipherFunc, errors.StructuralError("length of decrypted key not a multiple of block size")
+	if l, cipherKeySize := len(plaintextKey), cipherFunc.KeySize(); l != cipherFunc.KeySize() {
+		return nil, cipherFunc, errors.StructuralError("length of decrypted key (" + strconv.Itoa(l) + ") " +
+			"not equal to cipher keysize (" + strconv.Itoa(cipherKeySize) + ")")
 	}
-
 	return plaintextKey, cipherFunc, nil
 }
 

+ 48 - 34
psiphon/common/crypto/openpgp/packet/symmetric_key_encrypted_test.go

@@ -61,43 +61,57 @@ func TestSymmetricKeyEncrypted(t *testing.T) {
 const symmetricallyEncryptedHex = "8c0d04030302371a0b38d884f02060c91cf97c9973b8e58e028e9501708ccfe618fb92afef7fa2d80ddadd93cf"
 const symmetricallyEncryptedContentsHex = "cb1062004d14c4df636f6e74656e74732e0a"
 
-func TestSerializeSymmetricKeyEncrypted(t *testing.T) {
-	buf := bytes.NewBuffer(nil)
-	passphrase := []byte("testing")
-	const cipherFunc = CipherAES128
-	config := &Config{
-		DefaultCipher: cipherFunc,
+func TestSerializeSymmetricKeyEncryptedCiphers(t *testing.T) {
+	tests := [...]struct {
+		cipherFunc CipherFunction
+		name       string
+	}{
+		{Cipher3DES, "Cipher3DES"},
+		{CipherCAST5, "CipherCAST5"},
+		{CipherAES128, "CipherAES128"},
+		{CipherAES192, "CipherAES192"},
+		{CipherAES256, "CipherAES256"},
 	}
 
-	key, err := SerializeSymmetricKeyEncrypted(buf, passphrase, config)
-	if err != nil {
-		t.Errorf("failed to serialize: %s", err)
-		return
-	}
+	for _, test := range tests {
+		var buf bytes.Buffer
+		passphrase := []byte("testing")
+		config := &Config{
+			DefaultCipher: test.cipherFunc,
+		}
 
-	p, err := Read(buf)
-	if err != nil {
-		t.Errorf("failed to reparse: %s", err)
-		return
-	}
-	ske, ok := p.(*SymmetricKeyEncrypted)
-	if !ok {
-		t.Errorf("parsed a different packet type: %#v", p)
-		return
-	}
+		key, err := SerializeSymmetricKeyEncrypted(&buf, passphrase, config)
+		if err != nil {
+			t.Errorf("cipher(%s) failed to serialize: %s", test.name, err)
+			continue
+		}
 
-	if ske.CipherFunc != config.DefaultCipher {
-		t.Errorf("SKE cipher function is %d (expected %d)", ske.CipherFunc, config.DefaultCipher)
-	}
-	parsedKey, parsedCipherFunc, err := ske.Decrypt(passphrase)
-	if err != nil {
-		t.Errorf("failed to decrypt reparsed SKE: %s", err)
-		return
-	}
-	if !bytes.Equal(key, parsedKey) {
-		t.Errorf("keys don't match after Decrypt: %x (original) vs %x (parsed)", key, parsedKey)
-	}
-	if parsedCipherFunc != cipherFunc {
-		t.Errorf("cipher function doesn't match after Decrypt: %d (original) vs %d (parsed)", cipherFunc, parsedCipherFunc)
+		p, err := Read(&buf)
+		if err != nil {
+			t.Errorf("cipher(%s) failed to reparse: %s", test.name, err)
+			continue
+		}
+
+		ske, ok := p.(*SymmetricKeyEncrypted)
+		if !ok {
+			t.Errorf("cipher(%s) parsed a different packet type: %#v", test.name, p)
+			continue
+		}
+
+		if ske.CipherFunc != config.DefaultCipher {
+			t.Errorf("cipher(%s) SKE cipher function is %d (expected %d)", test.name, ske.CipherFunc, config.DefaultCipher)
+		}
+		parsedKey, parsedCipherFunc, err := ske.Decrypt(passphrase)
+		if err != nil {
+			t.Errorf("cipher(%s) failed to decrypt reparsed SKE: %s", test.name, err)
+			continue
+		}
+		if !bytes.Equal(key, parsedKey) {
+			t.Errorf("cipher(%s) keys don't match after Decrypt: %x (original) vs %x (parsed)", test.name, key, parsedKey)
+		}
+		if parsedCipherFunc != test.cipherFunc {
+			t.Errorf("cipher(%s) cipher function doesn't match after Decrypt: %d (original) vs %d (parsed)",
+				test.name, test.cipherFunc, parsedCipherFunc)
+		}
 	}
 }

+ 1 - 1
psiphon/common/crypto/openpgp/read_test.go

@@ -124,7 +124,7 @@ func checkSignedMessage(t *testing.T, signedHex, expected string) {
 		return
 	}
 
-	if !md.IsSigned || md.SignedByKeyId != 0xa34d7e18c20c31bb || md.SignedBy == nil || md.IsEncrypted || md.IsSymmetricallyEncrypted || len(md.EncryptedToKeyIds) != 0 || md.IsSymmetricallyEncrypted {
+	if !md.IsSigned || md.SignedByKeyId != 0xa34d7e18c20c31bb || md.SignedBy == nil || md.IsEncrypted || md.IsSymmetricallyEncrypted || len(md.EncryptedToKeyIds) != 0 || md.DecryptedWith != (Key{}) {
 		t.Errorf("bad MessageDetails: %#v", md)
 	}
 

+ 105 - 67
psiphon/common/crypto/openpgp/write.go

@@ -164,12 +164,12 @@ func hashToHashId(h crypto.Hash) uint8 {
 	return v
 }
 
-// Encrypt encrypts a message to a number of recipients and, optionally, signs
-// it. hints contains optional information, that is also encrypted, that aids
-// the recipients in processing the message. The resulting WriteCloser must
-// be closed after the contents of the file have been written.
-// If config is nil, sensible defaults will be used.
-func Encrypt(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHints, config *packet.Config) (plaintext io.WriteCloser, err error) {
+// writeAndSign writes the data as a payload package and, optionally, signs
+// it. hints contains optional information, that is also encrypted,
+// that aids the recipients in processing the message. The resulting
+// WriteCloser must be closed after the contents of the file have been
+// written. If config is nil, sensible defaults will be used.
+func writeAndSign(payload io.WriteCloser, candidateHashes []uint8, signed *Entity, hints *FileHints, config *packet.Config) (plaintext io.WriteCloser, err error) {
 	var signer *packet.PrivateKey
 	if signed != nil {
 		signKey, ok := signed.signingKey(config.Now())
@@ -185,6 +185,83 @@ func Encrypt(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHint
 		}
 	}
 
+	var hash crypto.Hash
+	for _, hashId := range candidateHashes {
+		if h, ok := s2k.HashIdToHash(hashId); ok && h.Available() {
+			hash = h
+			break
+		}
+	}
+
+	// If the hash specified by config is a candidate, we'll use that.
+	if configuredHash := config.Hash(); configuredHash.Available() {
+		for _, hashId := range candidateHashes {
+			if h, ok := s2k.HashIdToHash(hashId); ok && h == configuredHash {
+				hash = h
+				break
+			}
+		}
+	}
+
+	if hash == 0 {
+		hashId := candidateHashes[0]
+		name, ok := s2k.HashIdToString(hashId)
+		if !ok {
+			name = "#" + strconv.Itoa(int(hashId))
+		}
+		return nil, errors.InvalidArgumentError("cannot encrypt because no candidate hash functions are compiled in. (Wanted " + name + " in this case.)")
+	}
+
+	if signer != nil {
+		ops := &packet.OnePassSignature{
+			SigType:    packet.SigTypeBinary,
+			Hash:       hash,
+			PubKeyAlgo: signer.PubKeyAlgo,
+			KeyId:      signer.KeyId,
+			IsLast:     true,
+		}
+		if err := ops.Serialize(payload); err != nil {
+			return nil, err
+		}
+	}
+
+	if hints == nil {
+		hints = &FileHints{}
+	}
+
+	w := payload
+	if signer != nil {
+		// If we need to write a signature packet after the literal
+		// data then we need to stop literalData from closing
+		// encryptedData.
+		w = noOpCloser{w}
+
+	}
+	var epochSeconds uint32
+	if !hints.ModTime.IsZero() {
+		epochSeconds = uint32(hints.ModTime.Unix())
+	}
+	literalData, err := packet.SerializeLiteral(w, hints.IsBinary, hints.FileName, epochSeconds)
+	if err != nil {
+		return nil, err
+	}
+
+	if signer != nil {
+		return signatureWriter{payload, literalData, hash, hash.New(), signer, config}, nil
+	}
+	return literalData, nil
+}
+
+// Encrypt encrypts a message to a number of recipients and, optionally, signs
+// it. hints contains optional information, that is also encrypted, that aids
+// the recipients in processing the message. The resulting WriteCloser must
+// be closed after the contents of the file have been written.
+// If config is nil, sensible defaults will be used.
+func Encrypt(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHints, config *packet.Config) (plaintext io.WriteCloser, err error) {
+	if len(to) == 0 {
+		return nil, errors.InvalidArgumentError("no encryption recipient provided")
+	}
+
 	// These are the possible ciphers that we'll use for the message.
 	candidateCiphers := []uint8{
 		uint8(packet.CipherAES128),
@@ -241,33 +318,6 @@ func Encrypt(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHint
 		}
 	}
 
-	var hash crypto.Hash
-	for _, hashId := range candidateHashes {
-		if h, ok := s2k.HashIdToHash(hashId); ok && h.Available() {
-			hash = h
-			break
-		}
-	}
-
-	// If the hash specified by config is a candidate, we'll use that.
-	if configuredHash := config.Hash(); configuredHash.Available() {
-		for _, hashId := range candidateHashes {
-			if h, ok := s2k.HashIdToHash(hashId); ok && h == configuredHash {
-				hash = h
-				break
-			}
-		}
-	}
-
-	if hash == 0 {
-		hashId := candidateHashes[0]
-		name, ok := s2k.HashIdToString(hashId)
-		if !ok {
-			name = "#" + strconv.Itoa(int(hashId))
-		}
-		return nil, errors.InvalidArgumentError("cannot encrypt because no candidate hash functions are compiled in. (Wanted " + name + " in this case.)")
-	}
-
 	symKey := make([]byte, cipher.KeySize())
 	if _, err := io.ReadFull(config.Random(), symKey); err != nil {
 		return nil, err
@@ -279,49 +329,37 @@ func Encrypt(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHint
 		}
 	}
 
-	encryptedData, err := packet.SerializeSymmetricallyEncrypted(ciphertext, cipher, symKey, config)
+	payload, err := packet.SerializeSymmetricallyEncrypted(ciphertext, cipher, symKey, config)
 	if err != nil {
 		return
 	}
 
-	if signer != nil {
-		ops := &packet.OnePassSignature{
-			SigType:    packet.SigTypeBinary,
-			Hash:       hash,
-			PubKeyAlgo: signer.PubKeyAlgo,
-			KeyId:      signer.KeyId,
-			IsLast:     true,
-		}
-		if err := ops.Serialize(encryptedData); err != nil {
-			return nil, err
-		}
-	}
+	return writeAndSign(payload, candidateHashes, signed, hints, config)
+}
 
-	if hints == nil {
-		hints = &FileHints{}
+// Sign signs a message. The resulting WriteCloser must be closed after the
+// contents of the file have been written.  hints contains optional information
+// that aids the recipients in processing the message.
+// If config is nil, sensible defaults will be used.
+func Sign(output io.Writer, signed *Entity, hints *FileHints, config *packet.Config) (input io.WriteCloser, err error) {
+	if signed == nil {
+		return nil, errors.InvalidArgumentError("no signer provided")
 	}
 
-	w := encryptedData
-	if signer != nil {
-		// If we need to write a signature packet after the literal
-		// data then we need to stop literalData from closing
-		// encryptedData.
-		w = noOpCloser{encryptedData}
-
-	}
-	var epochSeconds uint32
-	if !hints.ModTime.IsZero() {
-		epochSeconds = uint32(hints.ModTime.Unix())
-	}
-	literalData, err := packet.SerializeLiteral(w, hints.IsBinary, hints.FileName, epochSeconds)
-	if err != nil {
-		return nil, err
+	// These are the possible hash functions that we'll use for the signature.
+	candidateHashes := []uint8{
+		hashToHashId(crypto.SHA256),
+		hashToHashId(crypto.SHA512),
+		hashToHashId(crypto.SHA1),
+		hashToHashId(crypto.RIPEMD160),
 	}
-
-	if signer != nil {
-		return signatureWriter{encryptedData, literalData, hash, hash.New(), signer, config}, nil
+	defaultHashes := candidateHashes[len(candidateHashes)-1:]
+	preferredHashes := signed.primaryIdentity().SelfSignature.PreferredHash
+	if len(preferredHashes) == 0 {
+		preferredHashes = defaultHashes
 	}
-	return literalData, nil
+	candidateHashes = intersectPreferences(candidateHashes, preferredHashes)
+	return writeAndSign(noOpCloser{output}, candidateHashes, signed, hints, config)
 }
 
 // signatureWriter hashes the contents of a message while passing it along to

+ 89 - 0
psiphon/common/crypto/openpgp/write_test.go

@@ -271,3 +271,92 @@ func TestEncryption(t *testing.T) {
 		}
 	}
 }
+
+var testSigningTests = []struct {
+	keyRingHex string
+}{
+	{
+		testKeys1And2PrivateHex,
+	},
+	{
+		dsaElGamalTestKeysHex,
+	},
+}
+
+func TestSigning(t *testing.T) {
+	for i, test := range testSigningTests {
+		kring, _ := ReadKeyRing(readerFromHex(test.keyRingHex))
+
+		passphrase := []byte("passphrase")
+		for _, entity := range kring {
+			if entity.PrivateKey != nil && entity.PrivateKey.Encrypted {
+				err := entity.PrivateKey.Decrypt(passphrase)
+				if err != nil {
+					t.Errorf("#%d: failed to decrypt key", i)
+				}
+			}
+			for _, subkey := range entity.Subkeys {
+				if subkey.PrivateKey != nil && subkey.PrivateKey.Encrypted {
+					err := subkey.PrivateKey.Decrypt(passphrase)
+					if err != nil {
+						t.Errorf("#%d: failed to decrypt subkey", i)
+					}
+				}
+			}
+		}
+
+		signed := kring[0]
+
+		buf := new(bytes.Buffer)
+		w, err := Sign(buf, signed, nil /* no hints */, nil)
+		if err != nil {
+			t.Errorf("#%d: error in Sign: %s", i, err)
+			continue
+		}
+
+		const message = "testing"
+		_, err = w.Write([]byte(message))
+		if err != nil {
+			t.Errorf("#%d: error writing plaintext: %s", i, err)
+			continue
+		}
+		err = w.Close()
+		if err != nil {
+			t.Errorf("#%d: error closing WriteCloser: %s", i, err)
+			continue
+		}
+
+		md, err := ReadMessage(buf, kring, nil /* no prompt */, nil)
+		if err != nil {
+			t.Errorf("#%d: error reading message: %s", i, err)
+			continue
+		}
+
+		testTime, _ := time.Parse("2006-01-02", "2013-07-01")
+		signKey, _ := kring[0].signingKey(testTime)
+		expectedKeyId := signKey.PublicKey.KeyId
+		if md.SignedByKeyId != expectedKeyId {
+			t.Errorf("#%d: message signed by wrong key id, got: %v, want: %v", i, *md.SignedBy, expectedKeyId)
+		}
+		if md.SignedBy == nil {
+			t.Errorf("#%d: failed to find the signing Entity", i)
+		}
+
+		plaintext, err := ioutil.ReadAll(md.UnverifiedBody)
+		if err != nil {
+			t.Errorf("#%d: error reading contents: %v", i, err)
+			continue
+		}
+
+		if string(plaintext) != message {
+			t.Errorf("#%d: got: %q, want: %q", i, plaintext, message)
+		}
+
+		if md.SignatureError != nil {
+			t.Errorf("#%d: signature error: %q", i, md.SignatureError)
+		}
+		if md.Signature == nil {
+			t.Error("signature missing")
+		}
+	}
+}

+ 19 - 0
psiphon/common/crypto/pbkdf2/pbkdf2_test.go

@@ -155,3 +155,22 @@ func TestWithHMACSHA1(t *testing.T) {
 func TestWithHMACSHA256(t *testing.T) {
 	testHash(t, sha256.New, "SHA256", sha256TestVectors)
 }
+
+var sink uint8
+
+func benchmark(b *testing.B, h func() hash.Hash) {
+	password := make([]byte, h().Size())
+	salt := make([]byte, 8)
+	for i := 0; i < b.N; i++ {
+		password = Key(password, salt, 4096, len(password), h)
+	}
+	sink += password[0]
+}
+
+func BenchmarkHMACSHA1(b *testing.B) {
+	benchmark(b, sha1.New)
+}
+
+func BenchmarkHMACSHA256(b *testing.B) {
+	benchmark(b, sha256.New)
+}

+ 1 - 1
psiphon/common/crypto/pkcs12/crypto.go

@@ -124,7 +124,7 @@ func pbDecrypt(info decryptable, password []byte) (decrypted []byte, err error)
 	return
 }
 
-// decryptable abstracts a object that contains ciphertext.
+// decryptable abstracts an object that contains ciphertext.
 type decryptable interface {
 	Algorithm() pkix.AlgorithmIdentifier
 	Data() []byte

+ 0 - 3
psiphon/common/crypto/pkcs12/internal/rc2/rc2.go

@@ -122,7 +122,6 @@ func (c *rc2Cipher) Encrypt(dst, src []byte) {
 	r3 = r3 + c.k[r2&63]
 
 	for j <= 40 {
-
 		// mix r0
 		r0 = r0 + c.k[j] + (r3 & r2) + ((^r3) & r1)
 		r0 = rotl16(r0, 1)
@@ -151,7 +150,6 @@ func (c *rc2Cipher) Encrypt(dst, src []byte) {
 	r3 = r3 + c.k[r2&63]
 
 	for j <= 60 {
-
 		// mix r0
 		r0 = r0 + c.k[j] + (r3 & r2) + ((^r3) & r1)
 		r0 = rotl16(r0, 1)
@@ -244,7 +242,6 @@ func (c *rc2Cipher) Decrypt(dst, src []byte) {
 	r0 = r0 - c.k[r3&63]
 
 	for j >= 0 {
-
 		// unmix r3
 		r3 = rotl16(r3, 16-5)
 		r3 = r3 - c.k[j] - (r2 & r1) - ((^r2) & r0)

+ 0 - 1
psiphon/common/crypto/pkcs12/internal/rc2/rc2_test.go

@@ -11,7 +11,6 @@ import (
 )
 
 func TestEncryptDecrypt(t *testing.T) {
-
 	// TODO(dgryski): add the rest of the test vectors from the RFC
 	var tests = []struct {
 		key    string

+ 42 - 69
psiphon/common/crypto/poly1305/poly1305_test.go

@@ -5,7 +5,6 @@
 package poly1305
 
 import (
-	"bytes"
 	"encoding/hex"
 	"flag"
 	"testing"
@@ -14,80 +13,51 @@ import (
 
 var stressFlag = flag.Bool("stress", false, "run slow stress tests")
 
-var testData = []struct {
-	in, k, correct []byte
-}{
-	{
-		[]byte("Hello world!"),
-		[]byte("this is 32-byte key for Poly1305"),
-		[]byte{0xa6, 0xf7, 0x45, 0x00, 0x8f, 0x81, 0xc9, 0x16, 0xa2, 0x0d, 0xcc, 0x74, 0xee, 0xf2, 0xb2, 0xf0},
-	},
-	{
-		make([]byte, 32),
-		[]byte("this is 32-byte key for Poly1305"),
-		[]byte{0x49, 0xec, 0x78, 0x09, 0x0e, 0x48, 0x1e, 0xc6, 0xc2, 0x6b, 0x33, 0xb9, 0x1c, 0xcc, 0x03, 0x07},
-	},
-	{
-		make([]byte, 2007),
-		[]byte("this is 32-byte key for Poly1305"),
-		[]byte{0xda, 0x84, 0xbc, 0xab, 0x02, 0x67, 0x6c, 0x38, 0xcd, 0xb0, 0x15, 0x60, 0x42, 0x74, 0xc2, 0xaa},
-	},
-	{
-		make([]byte, 2007),
-		make([]byte, 32),
-		make([]byte, 16),
-	},
-	{
-		// This test triggers an edge-case. See https://go-review.googlesource.com/#/c/30101/.
-		[]byte{0x81, 0xd8, 0xb2, 0xe4, 0x6a, 0x25, 0x21, 0x3b, 0x58, 0xfe, 0xe4, 0x21, 0x3a, 0x2a, 0x28, 0xe9, 0x21, 0xc1, 0x2a, 0x96, 0x32, 0x51, 0x6d, 0x3b, 0x73, 0x27, 0x27, 0x27, 0xbe, 0xcf, 0x21, 0x29},
-		[]byte{0x3b, 0x3a, 0x29, 0xe9, 0x3b, 0x21, 0x3a, 0x5c, 0x5c, 0x3b, 0x3b, 0x05, 0x3a, 0x3a, 0x8c, 0x0d},
-		[]byte{0x6d, 0xc1, 0x8b, 0x8c, 0x34, 0x4c, 0xd7, 0x99, 0x27, 0x11, 0x8b, 0xbe, 0x84, 0xb7, 0xf3, 0x14},
-	},
-	{
-		// This test generates a result of (2^130-1) % (2^130-5).
-		[]byte{
-			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-		},
-		[]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
-		[]byte{4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
-	},
-	{
-		// This test generates a result of (2^130-6) % (2^130-5).
-		[]byte{
-			0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-		},
-		[]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
-		[]byte{0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
-	},
-	{
-		// This test generates a result of (2^130-5) % (2^130-5).
-		[]byte{
-			0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-		},
-		[]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
-		[]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
-	},
+type test struct {
+	in  string
+	key string
+	tag string
 }
 
-func testSum(t *testing.T, unaligned bool) {
-	var out [16]byte
+func (t *test) Input() []byte {
+	in, err := hex.DecodeString(t.in)
+	if err != nil {
+		panic(err)
+	}
+	return in
+}
+
+func (t *test) Key() [32]byte {
+	buf, err := hex.DecodeString(t.key)
+	if err != nil {
+		panic(err)
+	}
 	var key [32]byte
+	copy(key[:], buf[:32])
+	return key
+}
 
+func (t *test) Tag() [16]byte {
+	buf, err := hex.DecodeString(t.tag)
+	if err != nil {
+		panic(err)
+	}
+	var tag [16]byte
+	copy(tag[:], buf[:16])
+	return tag
+}
+
+func testSum(t *testing.T, unaligned bool, sumImpl func(tag *[TagSize]byte, msg []byte, key *[32]byte)) {
+	var tag [16]byte
 	for i, v := range testData {
-		in := v.in
+		in := v.Input()
 		if unaligned {
 			in = unalignBytes(in)
 		}
-		copy(key[:], v.k)
-		Sum(&out, in, &key)
-		if !bytes.Equal(out[:], v.correct) {
-			t.Errorf("%d: expected %x, got %x", i, v.correct, out[:])
+		key := v.Key()
+		sumImpl(&tag, in, &key)
+		if tag != v.Tag() {
+			t.Errorf("%d: expected %x, got %x", i, v.Tag(), tag[:])
 		}
 	}
 }
@@ -125,8 +95,10 @@ func TestBurnin(t *testing.T) {
 	}
 }
 
-func TestSum(t *testing.T)          { testSum(t, false) }
-func TestSumUnaligned(t *testing.T) { testSum(t, true) }
+func TestSum(t *testing.T)                 { testSum(t, false, Sum) }
+func TestSumUnaligned(t *testing.T)        { testSum(t, true, Sum) }
+func TestSumGeneric(t *testing.T)          { testSum(t, false, sumGeneric) }
+func TestSumGenericUnaligned(t *testing.T) { testSum(t, true, sumGeneric) }
 
 func benchmark(b *testing.B, size int, unaligned bool) {
 	var out [16]byte
@@ -146,6 +118,7 @@ func Benchmark64(b *testing.B)          { benchmark(b, 64, false) }
 func Benchmark1K(b *testing.B)          { benchmark(b, 1024, false) }
 func Benchmark64Unaligned(b *testing.B) { benchmark(b, 64, true) }
 func Benchmark1KUnaligned(b *testing.B) { benchmark(b, 1024, true) }
+func Benchmark2M(b *testing.B)          { benchmark(b, 2097152, true) }
 
 func unalignBytes(in []byte) []byte {
 	out := make([]byte, len(in)+1)

+ 14 - 0
psiphon/common/crypto/poly1305/sum_noasm.go

@@ -0,0 +1,14 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build s390x,!go1.11 !arm,!amd64,!s390x gccgo appengine nacl
+
+package poly1305
+
+// Sum generates an authenticator for msg using a one-time key and puts the
+// 16-byte result into out. Authenticating two different messages with the same
+// key allows an attacker to forge messages at will.
+func Sum(out *[TagSize]byte, msg []byte, key *[32]byte) {
+	sumGeneric(out, msg, key)
+}

Некоторые файлы не были показаны из-за большого количества измененных файлов