Browse Source

Merge pull request #493 from rod-hynes/master

Upgrade "ssh"; plus other minor enhancements
Rod Hynes 7 years ago
parent
commit
cbc67fd893
100 changed files with 8261 additions and 1860 deletions
  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"
 	"os/signal"
 	"sort"
 	"sort"
 	"sync"
 	"sync"
+	"syscall"
 
 
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
@@ -290,15 +291,30 @@ func main() {
 	systemStopSignal := make(chan os.Signal, 1)
 	systemStopSignal := make(chan os.Signal, 1)
 	signal.Notify(systemStopSignal, os.Interrupt, os.Kill)
 	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
 	// 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()
 	psiphon.DoGarbageCollection()
 
 
 	// Wrap the provider in a layer that locks a mutex before calling a provider function.
 	// 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
 	// can cause OS threads to be spawned. The mutex prevents many calling goroutines from
 	// causing unbounded numbers of OS threads to be spawned.
 	// causing unbounded numbers of OS threads to be spawned.
 	// TODO: replace the mutex with a semaphore, to allow a larger but still bounded concurrent
 	// 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()
 	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.
 // Helper function to store a list of server entries.
 // if embeddedServerEntryListFilename is not empty, embeddedServerEntryList will be ignored.
 // if embeddedServerEntryListFilename is not empty, embeddedServerEntryList will be ignored.
 func storeServerEntries(
 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!
 It is the work of hundreds of contributors. We appreciate your help!
 
 
-
 ## Filing issues
 ## Filing issues
 
 
 When [filing an issue](https://golang.org/issue/new), make sure to answer these five questions:
 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.
 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.
 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)
 Please read the [Contribution Guidelines](https://golang.org/doc/contribute.html)
 before sending patches.
 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
 Unless otherwise noted, the Go source files are distributed under
 the BSD-style license found in the LICENSE file.
 the BSD-style license found in the LICENSE file.
-

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

@@ -14,7 +14,6 @@
 package acme
 package acme
 
 
 import (
 import (
-	"bytes"
 	"context"
 	"context"
 	"crypto"
 	"crypto"
 	"crypto/ecdsa"
 	"crypto/ecdsa"
@@ -23,6 +22,8 @@ import (
 	"crypto/sha256"
 	"crypto/sha256"
 	"crypto/tls"
 	"crypto/tls"
 	"crypto/x509"
 	"crypto/x509"
+	"crypto/x509/pkix"
+	"encoding/asn1"
 	"encoding/base64"
 	"encoding/base64"
 	"encoding/hex"
 	"encoding/hex"
 	"encoding/json"
 	"encoding/json"
@@ -33,14 +34,26 @@ import (
 	"io/ioutil"
 	"io/ioutil"
 	"math/big"
 	"math/big"
 	"net/http"
 	"net/http"
-	"strconv"
 	"strings"
 	"strings"
 	"sync"
 	"sync"
 	"time"
 	"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 (
 const (
 	maxChainLen = 5       // max depth and breadth of a certificate chain
 	maxChainLen = 5       // max depth and breadth of a certificate chain
@@ -51,38 +64,6 @@ const (
 	maxNonces = 100
 	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.
 // Client is an ACME client.
 // The only required field is Key. An example of creating a client with a new key
 // The only required field is Key. An example of creating a client with a new key
 // is as follows:
 // is as follows:
@@ -108,6 +89,22 @@ type Client struct {
 	// will have no effect.
 	// will have no effect.
 	DirectoryURL string
 	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
 	dirMu sync.Mutex // guards writes to dir
 	dir   *Directory // cached result of Client's Discover method
 	dir   *Directory // cached result of Client's Discover method
 
 
@@ -131,15 +128,12 @@ func (c *Client) Discover(ctx context.Context) (Directory, error) {
 	if dirURL == "" {
 	if dirURL == "" {
 		dirURL = LetsEncryptURL
 		dirURL = LetsEncryptURL
 	}
 	}
-	res, err := c.get(ctx, dirURL)
+	res, err := c.get(ctx, dirURL, wantStatus(http.StatusOK))
 	if err != nil {
 	if err != nil {
 		return Directory{}, err
 		return Directory{}, err
 	}
 	}
 	defer res.Body.Close()
 	defer res.Body.Close()
 	c.addNonce(res.Header)
 	c.addNonce(res.Header)
-	if res.StatusCode != http.StatusOK {
-		return Directory{}, responseError(res)
-	}
 
 
 	var v struct {
 	var v struct {
 		Reg    string `json:"new-reg"`
 		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,
 // 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.
 // 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.
 // 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.
 // 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)
 		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 {
 	if err != nil {
 		return nil, "", err
 		return nil, "", err
 	}
 	}
 	defer res.Body.Close()
 	defer res.Body.Close()
-	if res.StatusCode != http.StatusCreated {
-		return nil, "", responseError(res)
-	}
 
 
 	curl := res.Header.Get("Location") // cert permanent URL
 	curl := res.Header.Get("Location") // cert permanent URL
 	if res.ContentLength == 0 {
 	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
 // Callers are encouraged to parse the returned value to ensure the certificate is valid
 // and has expected features.
 // and has expected features.
 func (c *Client) FetchCert(ctx context.Context, url string, bundle bool) ([][]byte, error) {
 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.
 // 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 {
 	if key == nil {
 		key = c.Key
 		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 {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 	defer res.Body.Close()
 	defer res.Body.Close()
-	if res.StatusCode != http.StatusOK {
-		return responseError(res)
-	}
 	return nil
 	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 }
 func AcceptTOS(tosURL string) bool { return true }
 
 
 // Register creates a new account registration by following the "new-reg" flow.
 // 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).
 // 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),
 // 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",
 		Resource:   "new-authz",
 		Identifier: authzID{Type: "dns", Value: domain},
 		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 {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 	defer res.Body.Close()
 	defer res.Body.Close()
-	if res.StatusCode != http.StatusCreated {
-		return nil, responseError(res)
-	}
 
 
 	var v wireAuthz
 	var v wireAuthz
 	if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
 	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,
 // If a caller needs to poll an authorization until its status is final,
 // see the WaitAuthorization method.
 // see the WaitAuthorization method.
 func (c *Client) GetAuthorization(ctx context.Context, url string) (*Authorization, error) {
 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 {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 	defer res.Body.Close()
 	defer res.Body.Close()
-	if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusAccepted {
-		return nil, responseError(res)
-	}
 	var v wireAuthz
 	var v wireAuthz
 	if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
 	if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
 		return nil, fmt.Errorf("acme: invalid response: %v", err)
 		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",
 		Status:   "deactivated",
 		Delete:   true,
 		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 {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 	defer res.Body.Close()
 	defer res.Body.Close()
-	if res.StatusCode != http.StatusOK {
-		return responseError(res)
-	}
 	return nil
 	return nil
 }
 }
 
 
 // WaitAuthorization polls an authorization at the given URL
 // WaitAuthorization polls an authorization at the given URL
 // until it is in one of the final states, StatusValid or StatusInvalid,
 // 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.
 // It returns a non-nil Authorization only if its Status is StatusValid.
 // In all other cases WaitAuthorization returns an error.
 // In all other cases WaitAuthorization returns an error.
 // If the Status is StatusInvalid, the returned error is of type *AuthorizationError.
 // If the Status is StatusInvalid, the returned error is of type *AuthorizationError.
 func (c *Client) WaitAuthorization(ctx context.Context, url string) (*Authorization, error) {
 func (c *Client) WaitAuthorization(ctx context.Context, url string) (*Authorization, error) {
-	sleep := sleeper(ctx)
 	for {
 	for {
-		res, err := c.get(ctx, url)
+		res, err := c.get(ctx, url, wantStatus(http.StatusOK, http.StatusAccepted))
 		if err != nil {
 		if err != nil {
 			return nil, err
 			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
 		var raw wireAuthz
 		err = json.NewDecoder(res.Body).Decode(&raw)
 		err = json.NewDecoder(res.Body).Decode(&raw)
 		res.Body.Close()
 		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
 			return raw.authorization(url), nil
-		}
-		if raw.Status == StatusInvalid {
+		case raw.Status == StatusInvalid:
 			return nil, raw.error(url)
 			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.
 // A client typically polls a challenge status using this method.
 func (c *Client) GetChallenge(ctx context.Context, url string) (*Challenge, error) {
 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 {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 	defer res.Body.Close()
 	defer res.Body.Close()
-	if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusAccepted {
-		return nil, responseError(res)
-	}
 	v := wireChallenge{URI: url}
 	v := wireChallenge{URI: url}
 	if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
 	if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
 		return nil, fmt.Errorf("acme: invalid response: %v", err)
 		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,
 		Type:     chal.Type,
 		Auth:     auth,
 		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 {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 	defer res.Body.Close()
 	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
 	var v wireChallenge
 	if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
 	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.
 // 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 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) {
 func (c *Client) TLSSNI01ChallengeCert(token string, opt ...CertOption) (cert tls.Certificate, name string, err error) {
 	ka, err := keyAuth(c.Key.Public(), token)
 	ka, err := keyAuth(c.Key.Public(), token)
 	if err != nil {
 	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.
 // 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 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) {
 func (c *Client) TLSSNI02ChallengeCert(token string, opt ...CertOption) (cert tls.Certificate, name string, err error) {
 	b := sha256.Sum256([]byte(token))
 	b := sha256.Sum256([]byte(token))
 	h := hex.EncodeToString(b[:])
 	h := hex.EncodeToString(b[:])
@@ -625,6 +589,52 @@ func (c *Client) TLSSNI02ChallengeCert(token string, opt ...CertOption) (cert tl
 	return cert, sanA, nil
 	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.
 // doReg sends all types of registration requests.
 // The type of request is identified by typ argument, which is a "resource"
 // The type of request is identified by typ argument, which is a "resource"
 // in the ACME spec terms.
 // 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.Contact = acct.Contact
 		req.Agreement = acct.AgreedTerms
 		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 {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 	defer res.Body.Close()
 	defer res.Body.Close()
-	if res.StatusCode < 200 || res.StatusCode > 299 {
-		return nil, responseError(res)
-	}
 
 
 	var v struct {
 	var v struct {
 		Contact        []string
 		Contact        []string
@@ -681,59 +692,6 @@ func (c *Client) doReg(ctx context.Context, url string, typ string, acct *Accoun
 	}, nil
 	}, 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
 // popNonce returns a nonce value previously stored with c.addNonce
 // or fetches a fresh one from the given URL.
 // or fetches a fresh one from the given URL.
 func (c *Client) popNonce(ctx context.Context, url string) (string, error) {
 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{}{}
 	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 {
 	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 {
 	if err != nil {
 		return "", err
 		return "", err
 	}
 	}
@@ -877,24 +789,6 @@ func (c *Client) responseCert(ctx context.Context, res *http.Response, bundle bo
 	return cert, nil
 	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.
 // chainCert fetches CA certificate chain recursively by following "up" links.
 // Each recursive call increments the depth by 1, resulting in an error
 // Each recursive call increments the depth by 1, resulting in an error
 // if the recursion level reaches maxChainLen.
 // 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")
 		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 {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 	defer res.Body.Close()
 	defer res.Body.Close()
-	if res.StatusCode != http.StatusOK {
-		return nil, responseError(res)
-	}
 	b, err := ioutil.ReadAll(io.LimitReader(res.Body, maxCertSize+1))
 	b, err := ioutil.ReadAll(io.LimitReader(res.Body, maxCertSize+1))
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
@@ -957,65 +848,6 @@ func linkHeader(h http.Header, rel string) []string {
 	return links
 	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.
 // keyAuth generates a key authorization string for a given token.
 func keyAuth(pub crypto.PublicKey, token string) (string, error) {
 func keyAuth(pub crypto.PublicKey, token string) (string, error) {
 	th, err := JWKThumbprint(pub)
 	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
 	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
 // tlsChallengeCert creates a temporary certificate for TLS-SNI challenges
 // with the given SANs and auto-generated public/private key pair.
 // 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.
 // To create a cert with a custom key pair, specify WithKey option.
 func tlsChallengeCert(san []string, opt []CertOption) (tls.Certificate, error) {
 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 {
 	for _, o := range opt {
 		switch o := o.(type) {
 		switch o := o.(type) {
 		case *certOptKey:
 		case *certOptKey:
@@ -1041,7 +884,7 @@ func tlsChallengeCert(san []string, opt []CertOption) (tls.Certificate, error) {
 			}
 			}
 			key = o.key
 			key = o.key
 		case *certOptTemplate:
 		case *certOptTemplate:
-			var t = *(*x509.Certificate)(o) // shallow copy is ok
+			t := *(*x509.Certificate)(o) // shallow copy is ok
 			tmpl = &t
 			tmpl = &t
 		default:
 		default:
 			// package's fault, if we let this happen:
 			// 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
 			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
 	tmpl.DNSNames = san
+	if len(san) > 0 {
+		tmpl.Subject.CommonName = san[0]
+	}
 
 
 	der, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, key.Public(), key)
 	der, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, key.Public(), key)
 	if err != nil {
 	if err != nil {

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

@@ -13,9 +13,9 @@ import (
 	"crypto/x509"
 	"crypto/x509"
 	"crypto/x509/pkix"
 	"crypto/x509/pkix"
 	"encoding/base64"
 	"encoding/base64"
+	"encoding/hex"
 	"encoding/json"
 	"encoding/json"
 	"fmt"
 	"fmt"
-	"io/ioutil"
 	"math/big"
 	"math/big"
 	"net/http"
 	"net/http"
 	"net/http/httptest"
 	"net/http/httptest"
@@ -485,95 +485,98 @@ func TestGetAuthorization(t *testing.T) {
 }
 }
 
 
 func TestWaitAuthorization(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 {
 		if err == nil {
 			t.Error("err is 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()
 	defer ts.Close()
-
-	res := make(chan error)
-	defer close(res)
+	type res struct {
+		authz *Authorization
+		err   error
+	}
+	ch := make(chan res, 1)
 	go func() {
 	go func() {
 		var client Client
 		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 {
 	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) {
 func TestRevokeAuthorization(t *testing.T) {
@@ -600,7 +603,7 @@ func TestRevokeAuthorization(t *testing.T) {
 				t.Errorf("req.Delete is false")
 				t.Errorf("req.Delete is false")
 			}
 			}
 		case "/2":
 		case "/2":
-			w.WriteHeader(http.StatusInternalServerError)
+			w.WriteHeader(http.StatusBadRequest)
 		}
 		}
 	}))
 	}))
 	defer ts.Close()
 	defer ts.Close()
@@ -821,7 +824,7 @@ func TestFetchCertRetry(t *testing.T) {
 	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		if count < 1 {
 		if count < 1 {
 			w.Header().Set("Retry-After", "0")
 			w.Header().Set("Retry-After", "0")
-			w.WriteHeader(http.StatusAccepted)
+			w.WriteHeader(http.StatusTooManyRequests)
 			count++
 			count++
 			return
 			return
 		}
 		}
@@ -946,7 +949,7 @@ func TestNonce_add(t *testing.T) {
 	c.addNonce(http.Header{"Replay-Nonce": {}})
 	c.addNonce(http.Header{"Replay-Nonce": {}})
 	c.addNonce(http.Header{"Replay-Nonce": {"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) {
 	if !reflect.DeepEqual(c.nonces, nonces) {
 		t.Errorf("c.nonces = %q; want %q", 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) {
 func TestLinkHeader(t *testing.T) {
 	h := http.Header{"Link": {
 	h := http.Header{"Link": {
 		`<https://example.com/acme/new-authz>;rel="next"`,
 		`<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) {
 func TestTLSSNI01ChallengeCert(t *testing.T) {
 	const (
 	const (
 		token = "evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA"
 		token = "evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA"
@@ -1186,6 +1120,9 @@ func TestTLSSNI01ChallengeCert(t *testing.T) {
 	if cert.DNSNames[0] != name {
 	if cert.DNSNames[0] != name {
 		t.Errorf("cert.DNSNames[0] != name: %q vs %q", 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) {
 func TestTLSSNI02ChallengeCert(t *testing.T) {
@@ -1219,6 +1156,61 @@ func TestTLSSNI02ChallengeCert(t *testing.T) {
 	if i >= len(cert.DNSNames) || cert.DNSNames[i] != name {
 	if i >= len(cert.DNSNames) || cert.DNSNames[i] != name {
 		t.Errorf("%v doesn't have %q", cert.DNSNames, 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) {
 func TestTLSChallengeCertOpt(t *testing.T) {
@@ -1319,28 +1311,3 @@ func TestDNS01ChallengeRecord(t *testing.T) {
 		t.Errorf("val = %q; want %q", val, value)
 		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"
 	"fmt"
 	"io"
 	"io"
 	mathrand "math/rand"
 	mathrand "math/rand"
+	"net"
 	"net/http"
 	"net/http"
-	"strconv"
+	"path"
 	"strings"
 	"strings"
 	"sync"
 	"sync"
 	"time"
 	"time"
@@ -43,7 +44,7 @@ var createCertRetryAfter = time.Minute
 var pseudoRand *lockedMathRand
 var pseudoRand *lockedMathRand
 
 
 func init() {
 func init() {
-	src := mathrand.NewSource(timeNow().UnixNano())
+	src := mathrand.NewSource(time.Now().UnixNano())
 	pseudoRand = &lockedMathRand{rnd: mathrand.New(src)}
 	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.
 // 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.
 // 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 {
 type Manager struct {
 	// Prompt specifies a callback function to conditionally accept a CA's Terms of Service (TOS).
 	// 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.
 	// 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.
 	// To always accept the terms, the callers can use AcceptTOS.
 	Prompt func(tosURL string) bool
 	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
 	Cache Cache
 
 
 	// HostPolicy controls which domains the Manager will attempt
 	// 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
 	// Client is used to perform low-level operations, such as account registration
 	// and requesting new certificates.
 	// and requesting new certificates.
+	//
 	// If Client is nil, a zero-value acme.Client is used with acme.LetsEncryptURL
 	// 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.
 	// Mutating the field after the first call of GetCertificate method will have no effect.
 	Client *acme.Client
 	Client *acme.Client
@@ -136,37 +142,95 @@ type Manager struct {
 	// If the Client's account key is already registered, Email is not used.
 	// If the Client's account key is already registered, Email is not used.
 	Email string
 	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
 	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
 	clientMu sync.Mutex
 	client   *acme.Client // initialized by acmeClient method
 	client   *acme.Client // initialized by acmeClient method
 
 
 	stateMu sync.Mutex
 	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.
 	// renewal tracks the set of domains currently running renewal timers.
-	// It is keyed by domain name.
 	renewalMu sync.Mutex
 	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.
 // GetCertificate implements the tls.Config.GetCertificate hook.
 // It provides a TLS certificate for hello.ServerName host, including answering
 // 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
 // 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.
 // 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.
 // 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.
 // 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) {
 func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
 	if m.Prompt == nil {
 	if m.Prompt == nil {
 		return nil, errors.New("acme/autocert: Manager.Prompt not set")
 		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, "."), ".") {
 	if !strings.Contains(strings.Trim(name, "."), ".") {
 		return nil, errors.New("acme/autocert: server name component count invalid")
 		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")
 		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)
 	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
 	defer cancel()
 	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
 			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
 			return cert, nil
 		}
 		}
 		// TODO: cache error results?
 		// TODO: cache error results?
@@ -201,8 +270,11 @@ func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate,
 	}
 	}
 
 
 	// regular domain
 	// 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 {
 	if err == nil {
 		return cert, 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 {
 	if err := m.hostPolicy()(ctx, name); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
-	cert, err = m.createCert(ctx, name)
+	cert, err = m.createCert(ctx, ck)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
-	m.cachePut(ctx, name, cert)
+	m.cachePut(ctx, ck, cert)
 	return cert, nil
 	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.
 // 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
 // If a certificate is found in cache but not in m.state, the latter will be filled
 // with the cached value.
 // 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()
 	m.stateMu.Lock()
-	if s, ok := m.state[name]; ok {
+	if s, ok := m.state[ck]; ok {
 		m.stateMu.Unlock()
 		m.stateMu.Unlock()
 		s.RLock()
 		s.RLock()
 		defer s.RUnlock()
 		defer s.RUnlock()
 		return s.tlscert()
 		return s.tlscert()
 	}
 	}
 	defer m.stateMu.Unlock()
 	defer m.stateMu.Unlock()
-	cert, err := m.cacheGet(ctx, name)
+	cert, err := m.cacheGet(ctx, ck)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		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")
 		return nil, errors.New("acme/autocert: private key cannot sign")
 	}
 	}
 	if m.state == nil {
 	if m.state == nil {
-		m.state = make(map[string]*certState)
+		m.state = make(map[certKey]*certState)
 	}
 	}
 	s := &certState{
 	s := &certState{
 		key:  signer,
 		key:  signer,
 		cert: cert.Certificate,
 		cert: cert.Certificate,
 		leaf: cert.Leaf,
 		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
 	return cert, nil
 }
 }
 
 
 // cacheGet always returns a valid certificate, or an error otherwise.
 // 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 {
 	if m.Cache == nil {
 		return nil, ErrCacheMiss
 		return nil, ErrCacheMiss
 	}
 	}
-	data, err := m.Cache.Get(ctx, domain)
+	data, err := m.Cache.Get(ctx, ck.String())
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
@@ -292,7 +483,7 @@ func (m *Manager) cacheGet(ctx context.Context, domain string) (*tls.Certificate
 	}
 	}
 
 
 	// verify and create TLS cert
 	// verify and create TLS cert
-	leaf, err := validCert(domain, pubDER, privKey)
+	leaf, err := validCert(ck, pubDER, privKey, m.now())
 	if err != nil {
 	if err != nil {
 		return nil, ErrCacheMiss
 		return nil, ErrCacheMiss
 	}
 	}
@@ -304,7 +495,7 @@ func (m *Manager) cacheGet(ctx context.Context, domain string) (*tls.Certificate
 	return tlscert, nil
 	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 {
 	if m.Cache == nil {
 		return 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 {
 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.
 // 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.
 // 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
 	// TODO: maybe rewrite this whole piece using sync.Once
-	state, err := m.certState(domain)
+	state, err := m.certState(ck)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		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.
 	// We are the first; state is locked.
 	// Unblock the readers when domain ownership is verified
 	// 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()
 	defer state.Unlock()
 	state.locked = false
 	state.locked = false
 
 
-	der, leaf, err := m.authorizedCert(ctx, state.key, domain)
+	der, leaf, err := m.authorizedCert(ctx, state.key, ck)
 	if err != nil {
 	if err != nil {
 		// Remove the failed state after some time,
 		// Remove the failed state after some time,
 		// making the manager call createCert again on the following TLS hello.
 		// making the manager call createCert again on the following TLS hello.
 		time.AfterFunc(createCertRetryAfter, func() {
 		time.AfterFunc(createCertRetryAfter, func() {
-			defer testDidRemoveState(domain)
+			defer testDidRemoveState(ck)
 			m.stateMu.Lock()
 			m.stateMu.Lock()
 			defer m.stateMu.Unlock()
 			defer m.stateMu.Unlock()
 			// Verify the state hasn't changed and it's still invalid
 			// Verify the state hasn't changed and it's still invalid
 			// before deleting.
 			// before deleting.
-			s, ok := m.state[domain]
+			s, ok := m.state[ck]
 			if !ok {
 			if !ok {
 				return
 				return
 			}
 			}
-			if _, err := validCert(domain, s.cert, s.key); err == nil {
+			if _, err := validCert(ck, s.cert, s.key, m.now()); err == nil {
 				return
 				return
 			}
 			}
-			delete(m.state, domain)
+			delete(m.state, ck)
 		})
 		})
 		return nil, err
 		return nil, err
 	}
 	}
 	state.cert = der
 	state.cert = der
 	state.leaf = leaf
 	state.leaf = leaf
-	go m.renew(domain, state.key, state.leaf.NotAfter)
+	go m.renew(ck, state.key, state.leaf.NotAfter)
 	return state.tlscert()
 	return state.tlscert()
 }
 }
 
 
 // certState returns a new or existing certState.
 // certState returns a new or existing certState.
 // If a new certState is returned, state.exist is false and the state is locked.
 // 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.
 // 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()
 	m.stateMu.Lock()
 	defer m.stateMu.Unlock()
 	defer m.stateMu.Unlock()
 	if m.state == nil {
 	if m.state == nil {
-		m.state = make(map[string]*certState)
+		m.state = make(map[certKey]*certState)
 	}
 	}
 	// existing state
 	// existing state
-	if state, ok := m.state[domain]; ok {
+	if state, ok := m.state[ck]; ok {
 		return state, nil
 		return state, nil
 	}
 	}
 
 
@@ -419,7 +610,7 @@ func (m *Manager) certState(domain string) (*certState, error) {
 		err error
 		err error
 		key crypto.Signer
 		key crypto.Signer
 	)
 	)
-	if m.ForceRSA {
+	if ck.isRSA {
 		key, err = rsa.GenerateKey(rand.Reader, 2048)
 		key, err = rsa.GenerateKey(rand.Reader, 2048)
 	} else {
 	} else {
 		key, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
 		key, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
@@ -433,21 +624,22 @@ func (m *Manager) certState(domain string) (*certState, error) {
 		locked: true,
 		locked: true,
 	}
 	}
 	state.Lock() // will be unlocked by m.certState caller
 	state.Lock() // will be unlocked by m.certState caller
-	m.state[domain] = state
+	m.state[ck] = state
 	return state, nil
 	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.
 // 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)
 	client, err := m.acmeClient(ctx)
 	if err != nil {
 	if err != nil {
 		return nil, nil, err
 		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 {
 	if err != nil {
 		return nil, nil, err
 		return nil, nil, err
 	}
 	}
@@ -455,105 +647,227 @@ func (m *Manager) authorizedCert(ctx context.Context, key crypto.Signer, domain
 	if err != nil {
 	if err != nil {
 		return nil, nil, err
 		return nil, nil, err
 	}
 	}
-	leaf, err = validCert(domain, der, key)
+	leaf, err = validCert(ck, der, key, m.now())
 	if err != nil {
 	if err != nil {
 		return nil, nil, err
 		return nil, nil, err
 	}
 	}
 	return der, leaf, nil
 	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)
 	client, err := m.acmeClient(ctx)
 	if err != nil {
 	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 {
 	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":
 	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":
 	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 {
 	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.
 // renew starts a cert renewal timer loop, one per domain.
 //
 //
 // The loop is scheduled in two cases:
 // 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 key argument is a certificate private key.
 // The exp argument is the cert expiration time (NotAfter).
 // 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()
 	m.renewalMu.Lock()
 	defer m.renewalMu.Unlock()
 	defer m.renewalMu.Unlock()
-	if m.renewal[domain] != nil {
+	if m.renewal[ck] != nil {
 		// another goroutine is already on it
 		// another goroutine is already on it
 		return
 		return
 	}
 	}
 	if m.renewal == nil {
 	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)
 	dr.start(exp)
 }
 }
 
 
@@ -589,7 +903,10 @@ func (m *Manager) stopRenew() {
 }
 }
 
 
 func (m *Manager) accountKey(ctx context.Context) (crypto.Signer, error) {
 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) {
 	genKey := func() (*ecdsa.PrivateKey, error) {
 		return ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
 		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)
 	data, err := m.Cache.Get(ctx, keyName)
+	if err == ErrCacheMiss {
+		data, err = m.Cache.Get(ctx, legacyKeyName)
+	}
 	if err == ErrCacheMiss {
 	if err == ErrCacheMiss {
 		key, err := genKey()
 		key, err := genKey()
 		if err != nil {
 		if err != nil {
@@ -671,6 +991,13 @@ func (m *Manager) renewBefore() time.Duration {
 	return 720 * time.Hour // 30 days
 	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.
 // certState is ready when its mutex is unlocked for reading.
 type certState struct {
 type certState struct {
 	sync.RWMutex
 	sync.RWMutex
@@ -696,12 +1023,12 @@ func (s *certState) tlscert() (*tls.Certificate, error) {
 	}, nil
 	}, 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{
 	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)
 	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")
 	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.
 // 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)
 	// parse public part(s)
 	var n int
 	var n int
 	for _, b := range der {
 	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)
 		n += copy(pub[n:], b)
 	}
 	}
 	x509Cert, err := x509.ParseCertificates(pub)
 	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")
 		return nil, errors.New("acme/autocert: no public key found")
 	}
 	}
 	// verify the leaf is not expired and matches the domain name
 	// verify the leaf is not expired and matches the domain name
 	leaf = x509Cert[0]
 	leaf = x509Cert[0]
-	now := timeNow()
 	if now.Before(leaf.NotBefore) {
 	if now.Before(leaf.NotBefore) {
 		return nil, errors.New("acme/autocert: certificate is not valid yet")
 		return nil, errors.New("acme/autocert: certificate is not valid yet")
 	}
 	}
 	if now.After(leaf.NotAfter) {
 	if now.After(leaf.NotAfter) {
 		return nil, errors.New("acme/autocert: expired certificate")
 		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
 		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) {
 	switch pub := leaf.PublicKey.(type) {
 	case *rsa.PublicKey:
 	case *rsa.PublicKey:
 		prv, ok := key.(*rsa.PrivateKey)
 		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 {
 		if pub.N.Cmp(prv.N) != 0 {
 			return nil, errors.New("acme/autocert: private key does not match public key")
 			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:
 	case *ecdsa.PublicKey:
 		prv, ok := key.(*ecdsa.PrivateKey)
 		prv, ok := key.(*ecdsa.PrivateKey)
 		if !ok {
 		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 {
 		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")
 			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:
 	default:
 		return nil, errors.New("acme/autocert: unknown public key algorithm")
 		return nil, errors.New("acme/autocert: unknown public key algorithm")
 	}
 	}
 	return leaf, nil
 	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 {
 type lockedMathRand struct {
 	sync.Mutex
 	sync.Mutex
 	rnd *mathrand.Rand
 	rnd *mathrand.Rand
@@ -812,8 +1134,6 @@ func (r *lockedMathRand) int63n(max int64) int64 {
 
 
 // For easier testing.
 // For easier testing.
 var (
 var (
-	timeNow = time.Now
-
 	// Called when a state is removed.
 	// 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
 package autocert
 
 
 import (
 import (
+	"bytes"
 	"context"
 	"context"
 	"crypto"
 	"crypto"
 	"crypto/ecdsa"
 	"crypto/ecdsa"
@@ -14,20 +15,31 @@ import (
 	"crypto/tls"
 	"crypto/tls"
 	"crypto/x509"
 	"crypto/x509"
 	"crypto/x509/pkix"
 	"crypto/x509/pkix"
+	"encoding/asn1"
 	"encoding/base64"
 	"encoding/base64"
 	"encoding/json"
 	"encoding/json"
 	"fmt"
 	"fmt"
 	"html/template"
 	"html/template"
 	"io"
 	"io"
+	"io/ioutil"
 	"math/big"
 	"math/big"
 	"net/http"
 	"net/http"
 	"net/http/httptest"
 	"net/http/httptest"
 	"reflect"
 	"reflect"
+	"strings"
 	"sync"
 	"sync"
 	"testing"
 	"testing"
 	"time"
 	"time"
 
 
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/acme"
 	"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(`{
 var discoTmpl = template.Must(template.New("disco").Parse(`{
@@ -48,11 +60,22 @@ var authzTmpl = template.Must(template.New("authz").Parse(`{
 			"uri": "{{.}}/challenge/2",
 			"uri": "{{.}}/challenge/2",
 			"type": "tls-sni-02",
 			"type": "tls-sni-02",
 			"token": "token-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 {
 type memCache struct {
+	t       *testing.T
 	mu      sync.Mutex
 	mu      sync.Mutex
 	keyData map[string][]byte
 	keyData map[string][]byte
 }
 }
@@ -68,7 +91,26 @@ func (m *memCache) Get(ctx context.Context, key string) ([]byte, error) {
 	return v, nil
 	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 {
 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()
 	m.mu.Lock()
 	defer m.mu.Unlock()
 	defer m.mu.Unlock()
 
 
@@ -84,12 +126,29 @@ func (m *memCache) Delete(ctx context.Context, key string) error {
 	return nil
 	return nil
 }
 }
 
 
-func newMemCache() *memCache {
+func newMemCache(t *testing.T) *memCache {
 	return &memCache{
 	return &memCache{
+		t:       t,
 		keyData: make(map[string][]byte),
 		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) {
 func dummyCert(pub interface{}, san ...string) ([]byte, error) {
 	return dateDummyCert(pub, time.Now(), time.Now().Add(90*24*time.Hour), san...)
 	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)
 	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) {
 func TestGetCertificate(t *testing.T) {
 	man := &Manager{Prompt: AcceptTOS}
 	man := &Manager{Prompt: AcceptTOS}
 	defer man.stopRenew()
 	defer man.stopRenew()
-	hello := &tls.ClientHelloInfo{ServerName: "example.org"}
+	hello := clientHelloInfo("example.org", true)
 	testGetCertificate(t, man, "example.org", hello)
 	testGetCertificate(t, man, "example.org", hello)
 }
 }
 
 
 func TestGetCertificate_trailingDot(t *testing.T) {
 func TestGetCertificate_trailingDot(t *testing.T) {
 	man := &Manager{Prompt: AcceptTOS}
 	man := &Manager{Prompt: AcceptTOS}
 	defer man.stopRenew()
 	defer man.stopRenew()
-	hello := &tls.ClientHelloInfo{ServerName: "example.org."}
+	hello := clientHelloInfo("example.org.", true)
 	testGetCertificate(t, man, "example.org", hello)
 	testGetCertificate(t, man, "example.org", hello)
 }
 }
 
 
 func TestGetCertificate_ForceRSA(t *testing.T) {
 func TestGetCertificate_ForceRSA(t *testing.T) {
 	man := &Manager{
 	man := &Manager{
 		Prompt:   AcceptTOS,
 		Prompt:   AcceptTOS,
-		Cache:    newMemCache(),
+		Cache:    newMemCache(t),
 		ForceRSA: true,
 		ForceRSA: true,
 	}
 	}
 	defer man.stopRenew()
 	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 {
 	if err != nil {
 		t.Fatalf("man.cacheGet: %v", err)
 		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) {
 func TestGetCertificate_nilPrompt(t *testing.T) {
 	man := &Manager{}
 	man := &Manager{}
 	defer man.stopRenew()
 	defer man.stopRenew()
-	url, finish := startACMEServerStub(t, man, "example.org")
+	url, finish := startACMEServerStub(t, getCertificateFromManager(man, true), "example.org")
 	defer finish()
 	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 {
 	if _, err := man.GetCertificate(hello); err == nil {
 		t.Error("got certificate for example.org; wanted error")
 		t.Error("got certificate for example.org; wanted error")
 	}
 	}
@@ -186,7 +250,7 @@ func TestGetCertificate_expiredCache(t *testing.T) {
 	}
 	}
 	tmpl := &x509.Certificate{
 	tmpl := &x509.Certificate{
 		SerialNumber: big.NewInt(1),
 		SerialNumber: big.NewInt(1),
-		Subject:      pkix.Name{CommonName: "example.org"},
+		Subject:      pkix.Name{CommonName: exampleDomain},
 		NotAfter:     time.Now(),
 		NotAfter:     time.Now(),
 	}
 	}
 	pub, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, &pk.PublicKey, pk)
 	pub, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, &pk.PublicKey, pk)
@@ -198,16 +262,16 @@ func TestGetCertificate_expiredCache(t *testing.T) {
 		PrivateKey:  pk,
 		PrivateKey:  pk,
 	}
 	}
 
 
-	man := &Manager{Prompt: AcceptTOS, Cache: newMemCache()}
+	man := &Manager{Prompt: AcceptTOS, Cache: newMemCache(t)}
 	defer man.stopRenew()
 	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)
 		t.Fatalf("man.cachePut: %v", err)
 	}
 	}
 
 
 	// The expired cached cert should trigger a new cert issuance
 	// The expired cached cert should trigger a new cert issuance
 	// and return without an error.
 	// 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) {
 func TestGetCertificate_failedAttempt(t *testing.T) {
@@ -216,7 +280,6 @@ func TestGetCertificate_failedAttempt(t *testing.T) {
 	}))
 	}))
 	defer ts.Close()
 	defer ts.Close()
 
 
-	const example = "example.org"
 	d := createCertRetryAfter
 	d := createCertRetryAfter
 	f := testDidRemoveState
 	f := testDidRemoveState
 	defer func() {
 	defer func() {
@@ -225,51 +288,168 @@ func TestGetCertificate_failedAttempt(t *testing.T) {
 	}()
 	}()
 	createCertRetryAfter = 0
 	createCertRetryAfter = 0
 	done := make(chan struct{})
 	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)
 		close(done)
 	}
 	}
 
 
-	key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
-	if err != nil {
-		t.Fatal(err)
-	}
 	man := &Manager{
 	man := &Manager{
 		Prompt: AcceptTOS,
 		Prompt: AcceptTOS,
 		Client: &acme.Client{
 		Client: &acme.Client{
-			Key:          key,
 			DirectoryURL: ts.URL,
 			DirectoryURL: ts.URL,
 		},
 		},
 	}
 	}
 	defer man.stopRenew()
 	defer man.stopRenew()
-	hello := &tls.ClientHelloInfo{ServerName: example}
+	hello := clientHelloInfo(exampleDomain, true)
 	if _, err := man.GetCertificate(hello); err == nil {
 	if _, err := man.GetCertificate(hello); err == nil {
 		t.Error("GetCertificate: err is nil")
 		t.Error("GetCertificate: err is nil")
 	}
 	}
 	select {
 	select {
 	case <-time.After(5 * time.Second):
 	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:
 	case <-done:
 		man.stateMu.Lock()
 		man.stateMu.Lock()
 		defer man.stateMu.Unlock()
 		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
 // startACMEServerStub runs an ACME server
 // The domain argument is the expected domain name of a certificate request.
 // 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
 	// echo token-02 | shasum -a 256
 	// then divide result in 2 parts separated by dot
 	// then divide result in 2 parts separated by dot
 	tokenCertName := "4e8eb87631187e9ff2153b56b13a4dec.13a35d002e485d60ff37354b32f665d9.token.acme.invalid"
 	tokenCertName := "4e8eb87631187e9ff2153b56b13a4dec.13a35d002e485d60ff37354b32f665d9.token.acme.invalid"
 	verifyTokenCert := func() {
 	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)
 			t.Errorf("verifyTokenCert: GetCertificate(%q): %v", tokenCertName, err)
 			return
 			return
 		}
 		}
@@ -351,8 +531,7 @@ func startACMEServerStub(t *testing.T, man *Manager, domain string) (url string,
 			tick := time.NewTicker(100 * time.Millisecond)
 			tick := time.NewTicker(100 * time.Millisecond)
 			defer tick.Stop()
 			defer tick.Stop()
 			for {
 			for {
-				hello := &tls.ClientHelloInfo{ServerName: tokenCertName}
-				if _, err := man.GetCertificate(hello); err != nil {
+				if err := getCertificate(tokenCertName); err != nil {
 					return
 					return
 				}
 				}
 				select {
 				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.
 // tests man.GetCertificate flow using the provided hello argument.
 // The domain argument is the expected domain name of a certificate request.
 // The domain argument is the expected domain name of a certificate request.
 func testGetCertificate(t *testing.T, man *Manager, domain string, hello *tls.ClientHelloInfo) {
 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()
 	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
 	// simulate tls.Config.GetCertificate
 	var tlscert *tls.Certificate
 	var tlscert *tls.Certificate
+	var err error
 	done := make(chan struct{})
 	done := make(chan struct{})
 	go func() {
 	go func() {
 		tlscert, err = man.GetCertificate(hello)
 		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) {
 func TestAccountKeyCache(t *testing.T) {
-	m := Manager{Cache: newMemCache()}
+	m := Manager{Cache: newMemCache(t)}
 	ctx := context.Background()
 	ctx := context.Background()
 	k1, err := m.accountKey(ctx)
 	k1, err := m.accountKey(ctx)
 	if err != nil {
 	if err != nil {
@@ -436,36 +852,57 @@ func TestAccountKeyCache(t *testing.T) {
 }
 }
 
 
 func TestCache(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 {
 	if err != nil {
 		t.Fatal(err)
 		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 {
 	if err != nil {
 		t.Fatal(err)
 		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()
 	defer man.stopRenew()
 	ctx := context.Background()
 	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)
 		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 {
 	if err != nil {
 		t.Fatalf("man.cacheGet: %v", err)
 		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 {
 	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 {
 	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 {
 		if err != nil && test.ok {
 			t.Errorf("%d: err = %v", i, err)
 			t.Errorf("%d: err = %v", i, err)
 		}
 		}
@@ -597,10 +1036,155 @@ func TestManagerGetCertificateBogusSNI(t *testing.T) {
 		{"fo.o", "cache.Get of fo.o"},
 		{"fo.o", "cache.Get of fo.o"},
 	}
 	}
 	for _, tt := range tests {
 	for _, tt := range tests {
-		_, err := m.GetCertificate(&tls.ClientHelloInfo{ServerName: tt.name})
+		_, err := m.GetCertificate(clientHelloInfo(tt.name, true))
 		got := fmt.Sprint(err)
 		got := fmt.Sprint(err)
 		if got != tt.wantErr {
 		if got != tt.wantErr {
 			t.Errorf("GetCertificate(SNI = %q) = %q; want %q", tt.name, 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")
 var ErrCacheMiss = errors.New("acme/autocert: certificate cache miss")
 
 
 // Cache is used by Manager to store and retrieve previously obtained certificates
 // 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 {
 type Cache interface {
 	// Get returns a certificate data for the specified key.
 	// Get returns a certificate data for the specified key.
 	// If there's no such key, Get returns ErrCacheMiss.
 	// 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
 package autocert_test
 
 
 import (
 import (
-	"crypto/tls"
 	"fmt"
 	"fmt"
 	"log"
 	"log"
 	"net/http"
 	"net/http"
@@ -22,13 +21,14 @@ func ExampleNewListener() {
 }
 }
 
 
 func ExampleManager() {
 func ExampleManager() {
-	m := autocert.Manager{
+	m := &autocert.Manager{
+		Cache:      autocert.DirCache("secret-dir"),
 		Prompt:     autocert.AcceptTOS,
 		Prompt:     autocert.AcceptTOS,
-		HostPolicy: autocert.HostWhitelist("example.org"),
+		HostPolicy: autocert.HostWhitelist("example.org", "www.example.org"),
 	}
 	}
 	s := &http.Server{
 	s := &http.Server{
 		Addr:      ":https",
 		Addr:      ":https",
-		TLSConfig: &tls.Config{GetCertificate: m.GetCertificate},
+		TLSConfig: m.TLSConfig(),
 	}
 	}
 	s.ListenAndServeTLS("", "")
 	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.
 // the Manager m's Prompt, Cache, HostPolicy, and other desired options.
 func (m *Manager) Listener() net.Listener {
 func (m *Manager) Listener() net.Listener {
 	ln := &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")
 	ln.tcpListener, ln.tcpListenErr = net.Listen("tcp", ":443")
 	return ln
 	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
 // domainRenewal tracks the state used by the periodic timers
 // renewing a single domain's cert.
 // renewing a single domain's cert.
 type domainRenewal struct {
 type domainRenewal struct {
-	m      *Manager
-	domain string
-	key    crypto.Signer
+	m   *Manager
+	ck  certKey
+	key crypto.Signer
 
 
 	timerMu sync.Mutex
 	timerMu sync.Mutex
 	timer   *time.Timer
 	timer   *time.Timer
@@ -71,25 +71,43 @@ func (dr *domainRenewal) renew() {
 	testDidRenewLoop(next, err)
 	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.
 // 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,
 // 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.
 // 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.
 // The returned value is a time interval after which the renewal should occur again.
 func (dr *domainRenewal) do(ctx context.Context) (time.Duration, error) {
 func (dr *domainRenewal) do(ctx context.Context) (time.Duration, error) {
 	// a race is likely unavoidable in a distributed environment
 	// a race is likely unavoidable in a distributed environment
 	// but we try nonetheless
 	// 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)
 		next := dr.next(tlscert.Leaf.NotAfter)
 		if next > dr.m.renewBefore()+renewJitter {
 		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 {
 	if err != nil {
 		return 0, err
 		return 0, err
 	}
 	}
@@ -102,16 +120,15 @@ func (dr *domainRenewal) do(ctx context.Context) (time.Duration, error) {
 	if err != nil {
 	if err != nil {
 		return 0, err
 		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
 	return dr.next(leaf.NotAfter), nil
 }
 }
 
 
 func (dr *domainRenewal) next(expiry time.Time) time.Duration {
 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
 	// add a bit of randomness to renew deadline
 	n := pseudoRand.int63n(int64(renewJitter))
 	n := pseudoRand.int63n(int64(renewJitter))
 	d -= time.Duration(n)
 	d -= time.Duration(n)

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

@@ -23,10 +23,10 @@ import (
 
 
 func TestRenewalNext(t *testing.T) {
 func TestRenewalNext(t *testing.T) {
 	now := time.Now()
 	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()
 	defer man.stopRenew()
 	tt := []struct {
 	tt := []struct {
 		expiry   time.Time
 		expiry   time.Time
@@ -48,8 +48,6 @@ func TestRenewalNext(t *testing.T) {
 }
 }
 
 
 func TestRenewFromCache(t *testing.T) {
 func TestRenewFromCache(t *testing.T) {
-	const domain = "example.org"
-
 	// ACME CA server stub
 	// ACME CA server stub
 	var ca *httptest.Server
 	var ca *httptest.Server
 	ca = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 	ca = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@@ -84,7 +82,7 @@ func TestRenewFromCache(t *testing.T) {
 			if err != nil {
 			if err != nil {
 				t.Fatalf("new-cert: CSR: %v", err)
 				t.Fatalf("new-cert: CSR: %v", err)
 			}
 			}
-			der, err := dummyCert(csr.PublicKey, domain)
+			der, err := dummyCert(csr.PublicKey, exampleDomain)
 			if err != nil {
 			if err != nil {
 				t.Fatalf("new-cert: dummyCert: %v", err)
 				t.Fatalf("new-cert: dummyCert: %v", err)
 			}
 			}
@@ -105,30 +103,28 @@ func TestRenewFromCache(t *testing.T) {
 	}))
 	}))
 	defer ca.Close()
 	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{
 	man := &Manager{
 		Prompt:      AcceptTOS,
 		Prompt:      AcceptTOS,
-		Cache:       newMemCache(),
+		Cache:       newMemCache(t),
 		RenewBefore: 24 * time.Hour,
 		RenewBefore: 24 * time.Hour,
 		Client: &acme.Client{
 		Client: &acme.Client{
-			Key:          key,
 			DirectoryURL: ca.URL,
 			DirectoryURL: ca.URL,
 		},
 		},
 	}
 	}
 	defer man.stopRenew()
 	defer man.stopRenew()
 
 
 	// cache an almost expired cert
 	// cache an almost expired cert
+	key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
+	if err != nil {
+		t.Fatal(err)
+	}
 	now := time.Now()
 	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 {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	tlscert := &tls.Certificate{PrivateKey: key, Certificate: [][]byte{cert}}
 	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)
 		t.Fatal(err)
 	}
 	}
 
 
@@ -152,7 +148,7 @@ func TestRenewFromCache(t *testing.T) {
 
 
 		// ensure the new cert is cached
 		// ensure the new cert is cached
 		after := time.Now().Add(future)
 		after := time.Now().Add(future)
-		tlscert, err := man.cacheGet(context.Background(), domain)
+		tlscert, err := man.cacheGet(context.Background(), exampleCertKey)
 		if err != nil {
 		if err != nil {
 			t.Fatalf("man.cacheGet: %v", err)
 			t.Fatalf("man.cacheGet: %v", err)
 		}
 		}
@@ -163,9 +159,9 @@ func TestRenewFromCache(t *testing.T) {
 		// verify the old cert is also replaced in memory
 		// verify the old cert is also replaced in memory
 		man.stateMu.Lock()
 		man.stateMu.Lock()
 		defer man.stateMu.Unlock()
 		defer man.stateMu.Unlock()
-		s := man.state[domain]
+		s := man.state[exampleCertKey]
 		if s == nil {
 		if s == nil {
-			t.Fatalf("m.state[%q] is nil", domain)
+			t.Fatalf("m.state[%q] is nil", exampleCertKey)
 		}
 		}
 		tlscert, err = s.tlscert()
 		tlscert, err = s.tlscert()
 		if err != nil {
 		if err != nil {
@@ -177,7 +173,7 @@ func TestRenewFromCache(t *testing.T) {
 	}
 	}
 
 
 	// trigger renew
 	// trigger renew
-	hello := &tls.ClientHelloInfo{ServerName: domain}
+	hello := clientHelloInfo(exampleDomain, true)
 	if _, err := man.GetCertificate(hello); err != nil {
 	if _, err := man.GetCertificate(hello); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -189,3 +185,145 @@ func TestRenewFromCache(t *testing.T) {
 	case <-done:
 	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
 package acme
 
 
 import (
 import (
+	"crypto"
+	"crypto/x509"
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
 	"net/http"
 	"net/http"
@@ -102,7 +104,7 @@ func RateLimit(err error) (time.Duration, bool) {
 	if e.Header == nil {
 	if e.Header == nil {
 		return 0, true
 		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.
 // 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,
 		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
 		n = 3
 	}
 	}
 	arr[n] = '$'
 	arr[n] = '$'
-	n += 1
+	n++
 	copy(arr[n:], []byte(fmt.Sprintf("%02d", p.cost)))
 	copy(arr[n:], []byte(fmt.Sprintf("%02d", p.cost)))
 	n += 2
 	n += 2
 	arr[n] = '$'
 	arr[n] = '$'
-	n += 1
+	n++
 	copy(arr[n:], p.salt)
 	copy(arr[n:], p.salt)
 	n += encodedSaltSize
 	n += encodedSaltSize
 	copy(arr[n:], p.hash)
 	copy(arr[n:], p.hash)

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

@@ -39,7 +39,10 @@ var (
 	useSSE4 bool
 	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{
 var iv = [8]uint64{
 	0x6a09e667f3bcc908, 0xbb67ae8584caa73b, 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1,
 	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.
 // 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) }
 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) {
 func newDigest(hashSize int, key []byte) (*digest, error) {
+	if hashSize < 1 || hashSize > Size {
+		return nil, errHashSize
+	}
 	if len(key) > Size {
 	if len(key) > Size {
 		return nil, errKeySize
 		return nil, errKeySize
 	}
 	}
@@ -136,6 +152,50 @@ type digest struct {
 	keyLen int
 	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) BlockSize() int { return BlockSize }
 
 
 func (d *digest) Size() int { return d.size }
 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)
 		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
 package blake2b
 
 
+import "golang.org/x/sys/cpu"
+
 func init() {
 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
 //go:noescape
 func hashBlocksAVX2(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte)
 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 hashBlocksSSE4(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte)
 
 
 func hashBlocks(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)
 		hashBlocksAVX2(h, c, flag, blocks)
-	} else if useAVX {
+	case useAVX:
 		hashBlocksAVX(h, c, flag, blocks)
 		hashBlocksAVX(h, c, flag, blocks)
-	} else if useSSE4 {
+	case useSSE4:
 		hashBlocksSSE4(h, c, flag, blocks)
 		hashBlocksSSE4(h, c, flag, blocks)
-	} else {
+	default:
 		hashBlocksGeneric(h, c, flag, blocks)
 		hashBlocksGeneric(h, c, flag, blocks)
 	}
 	}
 }
 }

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

@@ -748,15 +748,3 @@ noinc:
 
 
 	MOVQ BP, SP
 	MOVQ BP, SP
 	RET
 	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
 package blake2b
 
 
+import "golang.org/x/sys/cpu"
+
 func init() {
 func init() {
-	useSSE4 = supportsSSE4()
+	useSSE4 = cpu.X86.HasSSE41
 }
 }
 
 
-//go:noescape
-func supportsSSE4() bool
-
 //go:noescape
 //go:noescape
 func hashBlocksSSE4(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte)
 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
 	MOVQ BP, SP
 	RET
 	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 (
 import (
 	"bytes"
 	"bytes"
+	"encoding"
 	"encoding/hex"
 	"encoding/hex"
 	"fmt"
 	"fmt"
 	"hash"
 	"hash"
@@ -69,6 +70,54 @@ func TestHashes2X(t *testing.T) {
 	testHashes2X(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) {
 func testHashes(t *testing.T) {
 	key, _ := hex.DecodeString("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f")
 	key, _ := hex.DecodeString("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f")
 
 
@@ -126,7 +175,7 @@ func testHashes2X(t *testing.T) {
 			t.Fatalf("#%d (single write): error from Read: %v", i, err)
 			t.Fatalf("#%d (single write): error from Read: %v", i, err)
 		}
 		}
 		if n, err := h.Read(sum); n != 0 || err != io.EOF {
 		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 {
 		if gotHex := fmt.Sprintf("%x", sum); gotHex != expectedHex {
 			t.Fatalf("#%d (single write): got %s, wanted %s", i, 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
 // 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.
 // 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) }
 func New256(key []byte) (hash.Hash, error) { return newDigest(Size, key) }
 
 
 // New128 returns a new hash.Hash computing the BLAKE2s-128 checksum given a
 // New128 returns a new hash.Hash computing the BLAKE2s-128 checksum given a
@@ -120,6 +122,50 @@ type digest struct {
 	keyLen int
 	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) BlockSize() int { return BlockSize }
 
 
 func (d *digest) Size() int { return d.size }
 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)
 		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
 package blake2s
 
 
+import "golang.org/x/sys/cpu"
+
 var (
 var (
 	useSSE4  = false
 	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
 //go:noescape
 func hashBlocksSSE2(h *[8]uint32, c *[2]uint32, flag uint32, blocks []byte)
 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 hashBlocksSSSE3(h *[8]uint32, c *[2]uint32, flag uint32, blocks []byte)
 
 
 func hashBlocks(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)
 		hashBlocksSSSE3(h, c, flag, blocks)
-	} else if useSSE2 {
+	case useSSE2:
 		hashBlocksSSE2(h, c, flag, blocks)
 		hashBlocksSSE2(h, c, flag, blocks)
-	} else {
+	default:
 		hashBlocksGeneric(h, c, flag, blocks)
 		hashBlocksGeneric(h, c, flag, blocks)
 	}
 	}
 }
 }

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

@@ -433,28 +433,3 @@ loop:
 
 
 	MOVL BP, SP
 	MOVL BP, SP
 	RET
 	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
 package blake2s
 
 
+import "golang.org/x/sys/cpu"
+
 var (
 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
 //go:noescape
 func hashBlocksSSE2(h *[8]uint32, c *[2]uint32, flag uint32, blocks []byte)
 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 hashBlocksSSE4(h *[8]uint32, c *[2]uint32, flag uint32, blocks []byte)
 
 
 func hashBlocks(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)
 		hashBlocksSSE4(h, c, flag, blocks)
-	} else if useSSSE3 {
+	case useSSSE3:
 		hashBlocksSSSE3(h, c, flag, blocks)
 		hashBlocksSSSE3(h, c, flag, blocks)
-	} else if useSSE2 {
+	case useSSE2:
 		hashBlocksSSE2(h, c, flag, blocks)
 		hashBlocksSSE2(h, c, flag, blocks)
-	} else {
+	default:
 		hashBlocksGeneric(h, c, flag, blocks)
 		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
 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)
 	HASH_BLOCKS(h+0(FP), c+8(FP), flag+16(FP), blocks_base+24(FP), blocks_len+32(FP), BLAKE2s_SSE4)
 	RET
 	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
 package blake2s
 
 
 import (
 import (
+	"bytes"
+	"encoding"
 	"encoding/hex"
 	"encoding/hex"
 	"fmt"
 	"fmt"
 	"testing"
 	"testing"
@@ -64,6 +66,52 @@ func TestHashes2X(t *testing.T) {
 	testHashes2X(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) {
 func testHashes(t *testing.T) {
 	key, _ := hex.DecodeString("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f")
 	key, _ := hex.DecodeString("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f")
 
 
@@ -185,7 +233,7 @@ func testHashes2X(t *testing.T) {
 	if n, err := h.Read(result[:]); err != nil {
 	if n, err := h.Read(result[:]); err != nil {
 		t.Fatalf("#unknown length: error from Read: %v", err)
 		t.Fatalf("#unknown length: error from Read: %v", err)
 	} else if n != len(result) {
 	} 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"
 	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
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 // 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
 // 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
 // that have been proposed over the past decade. They consist of a triplet of
@@ -14,6 +14,10 @@
 // Barreto-Naehrig curve as described in
 // Barreto-Naehrig curve as described in
 // http://cryptojedi.org/papers/dclxvi-20100714.pdf. Its output is compatible
 // http://cryptojedi.org/papers/dclxvi-20100714.pdf. Its output is compatible
 // with the implementation described in that paper.
 // 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"
 package bn256 // import "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/bn256"
 
 
 import (
 import (
@@ -49,8 +53,8 @@ func RandomG1(r io.Reader) (*big.Int, *G1, error) {
 	return k, new(G1).ScalarBaseMult(k), nil
 	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
 // 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.
 // 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.
 	// Each value is a 256-bit number.
 	const numBytes = 256 / 8
 	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)
 	ret := make([]byte, numBytes*2)
 	copy(ret[1*numBytes-len(xBytes):], xBytes)
 	copy(ret[1*numBytes-len(xBytes):], xBytes)
 	copy(ret[2*numBytes-len(yBytes):], yBytes)
 	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
 	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
 // 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.
 // Marshal converts n into a byte slice.
 func (n *G2) Marshal() []byte {
 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)
 	n.p.MakeAffine(nil)
 
 
 	xxBytes := new(big.Int).Mod(n.p.x.x, p).Bytes()
 	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()
 	yxBytes := new(big.Int).Mod(n.p.y.x, p).Bytes()
 	yyBytes := new(big.Int).Mod(n.p.y.y, 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)
 	ret := make([]byte, numBytes*4)
 	copy(ret[1*numBytes-len(xxBytes):], xxBytes)
 	copy(ret[1*numBytes-len(xxBytes):], xxBytes)
 	copy(ret[2*numBytes-len(xyBytes):], xyBytes)
 	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
 	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 {
 func (c *curvePoint) MakeAffine(pool *bnPool) *curvePoint {
 	if words := c.z.Bits(); len(words) == 1 && words[0] == 1 {
 	if words := c.z.Bits(); len(words) == 1 && words[0] == 1 {
 		return c
 		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)
 	zInv := pool.Get().ModInverse(c.z, p)
 	t := pool.Get().Mul(c.y, zInv)
 	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
 	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 {
 func (c *twistPoint) MakeAffine(pool *bnPool) *twistPoint {
 	if c.z.IsOne() {
 	if c.z.IsOne() {
 		return c
 		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)
 	zInv := newGFp2(pool).Invert(c.z, pool)
 	t := newGFp2(pool).Mul(c.y, zInv, 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
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 // 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"
 package chacha20poly1305 // import "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/chacha20poly1305"
 
 
 import (
 import (
 	"crypto/cipher"
 	"crypto/cipher"
+	"encoding/binary"
 	"errors"
 	"errors"
 )
 )
 
 
 const (
 const (
 	// KeySize is the size of the key used by this AEAD, in bytes.
 	// KeySize is the size of the key used by this AEAD, in bytes.
 	KeySize = 32
 	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
 	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 {
 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) {
 func New(key []byte) (cipher.AEAD, error) {
 	if len(key) != KeySize {
 	if len(key) != KeySize {
 		return nil, errors.New("chacha20poly1305: bad key length")
 		return nil, errors.New("chacha20poly1305: bad key length")
 	}
 	}
 	ret := new(chacha20poly1305)
 	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
 	return ret, nil
 }
 }
 
 

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

@@ -6,7 +6,12 @@
 
 
 package chacha20poly1305
 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
 //go:noescape
 func chacha20Poly1305Open(dst []byte, key []uint32, src, ad []byte) bool
 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
 //go:noescape
 func chacha20Poly1305Seal(dst []byte, key []uint32, src, ad []byte)
 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 (
 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
 // setupState writes a ChaCha20 input matrix to state. See
 // https://tools.ietf.org/html/rfc7539#section-2.3.
 // 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[0] = 0x61707865
 	state[1] = 0x3320646e
 	state[1] = 0x3320646e
 	state[2] = 0x79622d32
 	state[2] = 0x79622d32
 	state[3] = 0x6b206574
 	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[12] = 0
 	state[13] = binary.LittleEndian.Uint32(nonce[:4])
 	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 {
 func (c *chacha20poly1305) seal(dst, nonce, plaintext, additionalData []byte) []byte {
-	if !useASM {
+	if !cpu.X86.HasSSSE3 {
 		return c.sealGeneric(dst, nonce, plaintext, additionalData)
 		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)
 	setupState(&state, &c.key, nonce)
 
 
 	ret, out := sliceForAppend(dst, len(plaintext)+16)
 	ret, out := sliceForAppend(dst, len(plaintext)+16)
+	if subtle.InexactOverlap(out, plaintext) {
+		panic("chacha20poly1305: invalid buffer overlap")
+	}
 	chacha20Poly1305Seal(out[:], state[:], plaintext, additionalData)
 	chacha20Poly1305Seal(out[:], state[:], plaintext, additionalData)
 	return ret
 	return ret
 }
 }
 
 
 func (c *chacha20poly1305) open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
 func (c *chacha20poly1305) open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
-	if !useASM {
+	if !cpu.X86.HasSSSE3 {
 		return c.openGeneric(dst, nonce, ciphertext, additionalData)
 		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]
 	ciphertext = ciphertext[:len(ciphertext)-16]
 	ret, out := sliceForAppend(dst, len(ciphertext))
 	ret, out := sliceForAppend(dst, len(ciphertext))
+	if subtle.InexactOverlap(out, ciphertext) {
+		panic("chacha20poly1305: invalid buffer overlap")
+	}
 	if !chacha20Poly1305Open(out, state[:], ciphertext, additionalData) {
 	if !chacha20Poly1305Open(out, state[:], ciphertext, additionalData) {
 		for i := range out {
 		for i := range out {
 			out[i] = 0
 			out[i] = 0

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

@@ -2693,22 +2693,3 @@ sealAVX2Tail512LoopB:
 	VPERM2I128 $0x13, tmpStoreAVX2, DD3, DD0
 	VPERM2I128 $0x13, tmpStoreAVX2, DD3, DD0
 
 
 	JMP sealAVX2SealHash
 	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 (
 import (
 	"encoding/binary"
 	"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"
 	"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 {
 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
 	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)
 	polyInput := make([]byte, roundTo16(len(additionalData))+roundTo16(len(plaintext))+8+8)
 	copy(polyInput, additionalData)
 	copy(polyInput, additionalData)
@@ -44,11 +50,14 @@ func (c *chacha20poly1305) openGeneric(dst, nonce, ciphertext, additionalData []
 	copy(tag[:], ciphertext[len(ciphertext)-16:])
 	copy(tag[:], ciphertext[len(ciphertext)-16:])
 	ciphertext = ciphertext[:len(ciphertext)-16]
 	ciphertext = ciphertext[:len(ciphertext)-16]
 
 
-	var counter [16]byte
-	copy(counter[4:], nonce)
-
 	var polyKey [32]byte
 	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)
 	polyInput := make([]byte, roundTo16(len(additionalData))+roundTo16(len(ciphertext))+8+8)
 	copy(polyInput, additionalData)
 	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)))
 	binary.LittleEndian.PutUint64(polyInput[len(polyInput)-8:], uint64(len(ciphertext)))
 
 
 	ret, out := sliceForAppend(dst, len(ciphertext))
 	ret, out := sliceForAppend(dst, len(ciphertext))
+	if subtle.InexactOverlap(out, ciphertext) {
+		panic("chacha20poly1305: invalid buffer overlap")
+	}
 	if !poly1305.Verify(&tag, polyInput, &polyKey) {
 	if !poly1305.Verify(&tag, polyInput, &polyKey) {
 		for i := range out {
 		for i := range out {
 			out[i] = 0
 			out[i] = 0
@@ -64,7 +76,6 @@ func (c *chacha20poly1305) openGeneric(dst, nonce, ciphertext, additionalData []
 		return nil, errOpen
 		return nil, errOpen
 	}
 	}
 
 
-	counter[0] = 1
-	chacha20.XORKeyStream(out, ciphertext, &counter, &c.key)
+	s.XORKeyStream(out, ciphertext)
 	return ret, nil
 	return ret, nil
 }
 }

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

@@ -6,9 +6,13 @@ package chacha20poly1305
 
 
 import (
 import (
 	"bytes"
 	"bytes"
-	cr "crypto/rand"
+	"crypto/cipher"
+	cryptorand "crypto/rand"
 	"encoding/hex"
 	"encoding/hex"
-	mr "math/rand"
+	"fmt"
+	"log"
+	mathrand "math/rand"
+	"strconv"
 	"testing"
 	"testing"
 )
 )
 
 
@@ -19,7 +23,18 @@ func TestVectors(t *testing.T) {
 		ad, _ := hex.DecodeString(test.aad)
 		ad, _ := hex.DecodeString(test.aad)
 		plaintext, _ := hex.DecodeString(test.plaintext)
 		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 {
 		if err != nil {
 			t.Fatal(err)
 			t.Fatal(err)
 		}
 		}
@@ -42,7 +57,7 @@ func TestVectors(t *testing.T) {
 		}
 		}
 
 
 		if len(ad) > 0 {
 		if len(ad) > 0 {
-			alterAdIdx := mr.Intn(len(ad))
+			alterAdIdx := mathrand.Intn(len(ad))
 			ad[alterAdIdx] ^= 0x80
 			ad[alterAdIdx] ^= 0x80
 			if _, err := aead.Open(nil, nonce, ct, ad); err == nil {
 			if _, err := aead.Open(nil, nonce, ct, ad); err == nil {
 				t.Errorf("#%d: Open was successful after altering additional data", i)
 				t.Errorf("#%d: Open was successful after altering additional data", i)
@@ -50,14 +65,14 @@ func TestVectors(t *testing.T) {
 			ad[alterAdIdx] ^= 0x80
 			ad[alterAdIdx] ^= 0x80
 		}
 		}
 
 
-		alterNonceIdx := mr.Intn(aead.NonceSize())
+		alterNonceIdx := mathrand.Intn(aead.NonceSize())
 		nonce[alterNonceIdx] ^= 0x80
 		nonce[alterNonceIdx] ^= 0x80
 		if _, err := aead.Open(nil, nonce, ct, ad); err == nil {
 		if _, err := aead.Open(nil, nonce, ct, ad); err == nil {
 			t.Errorf("#%d: Open was successful after altering nonce", i)
 			t.Errorf("#%d: Open was successful after altering nonce", i)
 		}
 		}
 		nonce[alterNonceIdx] ^= 0x80
 		nonce[alterNonceIdx] ^= 0x80
 
 
-		alterCtIdx := mr.Intn(len(ct))
+		alterCtIdx := mathrand.Intn(len(ct))
 		ct[alterCtIdx] ^= 0x80
 		ct[alterCtIdx] ^= 0x80
 		if _, err := aead.Open(nil, nonce, ct, ad); err == nil {
 		if _, err := aead.Open(nil, nonce, ct, ad); err == nil {
 			t.Errorf("#%d: Open was successful after altering ciphertext", i)
 			t.Errorf("#%d: Open was successful after altering ciphertext", i)
@@ -68,87 +83,117 @@ func TestVectors(t *testing.T) {
 
 
 func TestRandom(t *testing.T) {
 func TestRandom(t *testing.T) {
 	// Some random tests to verify Open(Seal) == Plaintext
 	// 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)))
 	b.SetBytes(int64(len(buf)))
 
 
 	var key [32]byte
 	var key [32]byte
-	var nonce [12]byte
+	var nonce = make([]byte, nonceSize)
 	var ad [13]byte
 	var ad [13]byte
 	var out []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()
 	b.ResetTimer()
 	for i := 0; i < b.N; i++ {
 	for i := 0; i < b.N; i++ {
 		out = aead.Seal(out[:0], nonce[:], buf[:], ad[:])
 		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)))
 	b.SetBytes(int64(len(buf)))
 
 
 	var key [32]byte
 	var key [32]byte
-	var nonce [12]byte
+	var nonce = make([]byte, nonceSize)
 	var ad [13]byte
 	var ad [13]byte
 	var ct []byte
 	var ct []byte
 	var out []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[:])
 	ct = aead.Seal(ct[:0], nonce[:], buf[:], ad[:])
 
 
 	b.ResetTimer()
 	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 {
 var chacha20Poly1305Tests = []struct {
 	plaintext, aad, key, nonce, out string
 	plaintext, aad, key, nonce, out string
 }{
 }{
+	{
+		"",
+		"",
+		"808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f",
+		"070000004041424344454647",
+		"a0784d7a4716f3feb4f64e7f4b39bf04",
+	},
 	{
 	{
 		"4c616469657320616e642047656e746c656d656e206f662074686520636c617373206f66202739393a204966204920636f756c64206f6666657220796f75206f6e6c79206f6e652074697020666f7220746865206675747572652c2073756e73637265656e20776f756c642062652069742e",
 		"4c616469657320616e642047656e746c656d656e206f662074686520636c617373206f66202739393a204966204920636f756c64206f6666657220796f75206f6e6c79206f6e652074697020666f7220746865206675747572652c2073756e73637265656e20776f756c642062652069742e",
 		"50515253c0c1c2c3c4c5c6c7",
 		"50515253c0c1c2c3c4c5c6c7",
@@ -329,4 +336,391 @@ var chacha20Poly1305Tests = []struct {
 		"129039b5572e8a7a8131f76a",
 		"129039b5572e8a7a8131f76a",
 		"2c125232a59879aee36cacc4aca5085a4688c4f776667a8fbd86862b5cfb1d57c976688fdd652eafa2b88b1b8e358aa2110ff6ef13cdc1ceca9c9f087c35c38d89d6fbd8de89538070f17916ecb19ca3ef4a1c834f0bdaa1df62aaabef2e117106787056c909e61ecd208357dd5c363f11c5d6cf24992cc873cf69f59360a820fcf290bd90b2cab24c47286acb4e1033962b6d41e562a206a94796a8ab1c6b8bade804ff9bdf5ba6062d2c1f8fe0f4dfc05720bd9a612b92c26789f9f6a7ce43f5e8e3aee99a9cd7d6c11eaa611983c36935b0dda57d898a60a0ab7c4b54",
 		"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
 package cryptobyte
 
 
 import (
 import (
-	"encoding/asn1"
+	encoding_asn1 "encoding/asn1"
 	"fmt"
 	"fmt"
 	"math/big"
 	"math/big"
 	"reflect"
 	"reflect"
 	"time"
 	"time"
+
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/cryptobyte/asn1"
 )
 )
 
 
 // This file contains ASN.1-related methods for String and Builder.
 // 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
 // Builder
 
 
 // AddASN1Int64 appends a DER-encoded ASN.1 INTEGER.
 // AddASN1Int64 appends a DER-encoded ASN.1 INTEGER.
 func (b *Builder) AddASN1Int64(v int64) {
 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.
 // AddASN1Enum appends a DER-encoded ASN.1 ENUMERATION.
 func (b *Builder) AddASN1Enum(v int64) {
 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) {
 	b.AddASN1(tag, func(c *Builder) {
 		length := 1
 		length := 1
 		for i := v; i >= 0x80 || i < -0x80; i >>= 8 {
 		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.
 // AddASN1Uint64 appends a DER-encoded ASN.1 INTEGER.
 func (b *Builder) AddASN1Uint64(v uint64) {
 func (b *Builder) AddASN1Uint64(v uint64) {
-	b.AddASN1(asn1.TagInteger, func(c *Builder) {
+	b.AddASN1(asn1.INTEGER, func(c *Builder) {
 		length := 1
 		length := 1
 		for i := v; i >= 0x80; i >>= 8 {
 		for i := v; i >= 0x80; i >>= 8 {
 			length++
 			length++
@@ -73,7 +69,7 @@ func (b *Builder) AddASN1BigInt(n *big.Int) {
 		return
 		return
 	}
 	}
 
 
-	b.AddASN1(asn1.TagInteger, func(c *Builder) {
+	b.AddASN1(asn1.INTEGER, func(c *Builder) {
 		if n.Sign() < 0 {
 		if n.Sign() < 0 {
 			// A negative number has to be converted to two's-complement form. So we
 			// 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
 			// 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.
 // AddASN1OctetString appends a DER-encoded ASN.1 OCTET STRING.
 func (b *Builder) AddASN1OctetString(bytes []byte) {
 func (b *Builder) AddASN1OctetString(bytes []byte) {
-	b.AddASN1(asn1.TagOctetString, func(c *Builder) {
+	b.AddASN1(asn1.OCTET_STRING, func(c *Builder) {
 		c.AddBytes(bytes)
 		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)
 		b.err = fmt.Errorf("cryptobyte: cannot represent %v as a GeneralizedTime", t)
 		return
 		return
 	}
 	}
-	b.AddASN1(asn1.TagGeneralizedTime, func(c *Builder) {
+	b.AddASN1(asn1.GeneralizedTime, func(c *Builder) {
 		c.AddBytes([]byte(t.Format(generalizedTimeFormatStr)))
 		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.
 // successful or records an error if one occurred.
 func (b *Builder) MarshalASN1(v interface{}) {
 func (b *Builder) MarshalASN1(v interface{}) {
 	// NOTE(martinkr): This is somewhat of a hack to allow propagation of
 	// 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.
 	// value embedded into a struct, its tag information is lost.
 	if b.err != nil {
 	if b.err != nil {
 		return
 		return
 	}
 	}
-	bytes, err := asn1.Marshal(v)
+	bytes, err := encoding_asn1.Marshal(v)
 	if err != nil {
 	if err != nil {
 		b.err = err
 		b.err = err
 		return
 		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.
 // 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
 // low-tag-number form only). The child builder passed to the
 // BuilderContinuation can be used to build the content of the ASN.1 object.
 // 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 {
 	if b.err != nil {
 		return
 		return
 	}
 	}
@@ -164,11 +230,32 @@ func (b *Builder) AddASN1(tag Tag, f BuilderContinuation) {
 
 
 // String
 // 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()
 var bigIntType = reflect.TypeOf((*big.Int)(nil)).Elem()
 
 
 // ReadASN1Integer decodes an ASN.1 INTEGER into out and advances. If out does
 // 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 {
 func (s *String) ReadASN1Integer(out interface{}) bool {
 	if reflect.TypeOf(out).Kind() != reflect.Ptr {
 	if reflect.TypeOf(out).Kind() != reflect.Ptr {
 		panic("out is not a pointer")
 		panic("out is not a pointer")
@@ -215,7 +302,7 @@ var bigOne = big.NewInt(1)
 
 
 func (s *String) readASN1BigInt(out *big.Int) bool {
 func (s *String) readASN1BigInt(out *big.Int) bool {
 	var bytes String
 	var bytes String
-	if !s.ReadASN1(&bytes, asn1.TagInteger) || !checkASN1Integer(bytes) {
+	if !s.ReadASN1(&bytes, asn1.INTEGER) || !checkASN1Integer(bytes) {
 		return false
 		return false
 	}
 	}
 	if bytes[0]&0x80 == 0x80 {
 	if bytes[0]&0x80 == 0x80 {
@@ -235,7 +322,7 @@ func (s *String) readASN1BigInt(out *big.Int) bool {
 
 
 func (s *String) readASN1Int64(out *int64) bool {
 func (s *String) readASN1Int64(out *int64) bool {
 	var bytes String
 	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 false
 	}
 	}
 	return true
 	return true
@@ -258,7 +345,7 @@ func asn1Signed(out *int64, n []byte) bool {
 
 
 func (s *String) readASN1Uint64(out *uint64) bool {
 func (s *String) readASN1Uint64(out *uint64) bool {
 	var bytes String
 	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 false
 	}
 	}
 	return true
 	return true
@@ -281,12 +368,20 @@ func asn1Unsigned(out *uint64, n []byte) bool {
 	return true
 	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 {
 func (s *String) ReadASN1Enum(out *int) bool {
 	var bytes String
 	var bytes String
 	var i int64
 	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
 		return false
 	}
 	}
 	if int64(int(i)) != i {
 	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
 // 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
 	var bytes String
-	if !s.ReadASN1(&bytes, asn1.TagOID) || len(bytes) == 0 {
+	if !s.ReadASN1(&bytes, asn1.OBJECT_IDENTIFIER) || len(bytes) == 0 {
 		return false
 		return false
 	}
 	}
 
 
@@ -353,10 +448,10 @@ func (s *String) ReadASN1ObjectIdentifier(out *asn1.ObjectIdentifier) bool {
 }
 }
 
 
 // ReadASN1GeneralizedTime decodes an ASN.1 GENERALIZEDTIME into out and
 // 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 {
 func (s *String) ReadASN1GeneralizedTime(out *time.Time) bool {
 	var bytes String
 	var bytes String
-	if !s.ReadASN1(&bytes, asn1.TagGeneralizedTime) {
+	if !s.ReadASN1(&bytes, asn1.GeneralizedTime) {
 		return false
 		return false
 	}
 	}
 	t := string(bytes)
 	t := string(bytes)
@@ -371,11 +466,11 @@ func (s *String) ReadASN1GeneralizedTime(out *time.Time) bool {
 	return true
 	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
 	var bytes String
-	if !s.ReadASN1(&bytes, asn1.TagBitString) || len(bytes) == 0 {
+	if !s.ReadASN1(&bytes, asn1.BIT_STRING) || len(bytes) == 0 {
 		return false
 		return false
 	}
 	}
 
 
@@ -392,20 +487,37 @@ func (s *String) ReadASN1BitString(out *asn1.BitString) bool {
 	return true
 	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
 // 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
 // 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)
 	return s.ReadASN1((*String)(out), tag)
 }
 }
 
 
 // ReadASN1 reads the contents of a DER-encoded ASN.1 element (not including
 // 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
 // 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).
 // 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 {
 	if !s.ReadAnyASN1(out, &t) || t != tag {
 		return false
 		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
 // 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
 // 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).
 // 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 {
 	if !s.ReadAnyASN1Element(out, &t) || t != tag {
 		return false
 		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
 // 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).
 // 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 */)
 	return s.readASN1(out, outTag, true /* skip header */)
 }
 }
 
 
 // ReadAnyASN1Element reads the contents of a DER-encoded ASN.1 element
 // ReadAnyASN1Element reads the contents of a DER-encoded ASN.1 element
 // (including tag and length bytes) into out, sets outTag to is tag, and
 // (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).
 // 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 */)
 	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.
 // the given tag.
-func (s String) PeekASN1Tag(tag Tag) bool {
+func (s String) PeekASN1Tag(tag asn1.Tag) bool {
 	if len(s) == 0 {
 	if len(s) == 0 {
 		return false
 		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)
 	present := s.PeekASN1Tag(tag)
 	if outPresent != nil {
 	if outPresent != nil {
 		*outPresent = present
 		*outPresent = present
@@ -467,12 +586,22 @@ func (s *String) ReadOptionalASN1(out *String, outPresent *bool, tag Tag) bool {
 	return true
 	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
 // ReadOptionalASN1Integer attempts to read an optional ASN.1 INTEGER
 // explicitly tagged with tag into out and advances. If no element with a
 // 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
 // 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 {
 	if reflect.TypeOf(out).Kind() != reflect.Ptr {
 		panic("out is not a pointer")
 		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
 // ReadOptionalASN1OctetString attempts to read an optional ASN.1 OCTET STRING
 // explicitly tagged with tag into out and advances. If no element with a
 // 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 present bool
 	var child String
 	var child String
 	if !s.ReadOptionalASN1(&child, &present, tag) {
 	if !s.ReadOptionalASN1(&child, &present, tag) {
@@ -521,7 +650,7 @@ func (s *String) ReadOptionalASN1OctetString(out *[]byte, outPresent *bool, tag
 	}
 	}
 	if present {
 	if present {
 		var oct String
 		var oct String
-		if !child.ReadASN1(&oct, asn1.TagOctetString) || !child.Empty() {
+		if !child.ReadASN1(&oct, asn1.OCTET_STRING) || !child.Empty() {
 			return false
 			return false
 		}
 		}
 		*out = oct
 		*out = oct
@@ -531,7 +660,25 @@ func (s *String) ReadOptionalASN1OctetString(out *[]byte, outPresent *bool, tag
 	return true
 	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 {
 	if len(*s) < 2 {
 		return false
 		return false
 	}
 	}
@@ -547,7 +694,7 @@ func (s *String) readASN1(out *String, outTag *Tag, skipHeader bool) bool {
 	}
 	}
 
 
 	if outTag != nil {
 	if outTag != nil {
-		*outTag = Tag(tag)
+		*outTag = asn1.Tag(tag)
 	}
 	}
 
 
 	// ITU-T X.690 section 8.1.3
 	// 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 (
 import (
 	"bytes"
 	"bytes"
-	"encoding/asn1"
+	encoding_asn1 "encoding/asn1"
 	"math/big"
 	"math/big"
 	"reflect"
 	"reflect"
 	"testing"
 	"testing"
 	"time"
 	"time"
+
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/cryptobyte/asn1"
 )
 )
 
 
 type readASN1Test struct {
 type readASN1Test struct {
 	name string
 	name string
 	in   []byte
 	in   []byte
-	tag  Tag
+	tag  asn1.Tag
 	ok   bool
 	ok   bool
 	out  interface{}
 	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) {
 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 {
 	testData := []struct {
 		in  []byte
 		in  []byte
 		ok  bool
 		ok  bool
@@ -212,10 +247,23 @@ func TestReadASN1ObjectIdentifier(t *testing.T) {
 
 
 	for i, test := range testData {
 	for i, test := range testData {
 		in := String(test.in)
 		in := String(test.in)
-		var out asn1.ObjectIdentifier
+		var out encoding_asn1.ObjectIdentifier
 		ok := in.ReadASN1ObjectIdentifier(&out)
 		ok := in.ReadASN1ObjectIdentifier(&out)
 		if ok != test.ok || ok && !out.Equal(test.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)
 			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{}},
 		{"201001020304-10Z", false, time.Time{}},
 	}
 	}
 	for i, test := range testData {
 	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
 		var out time.Time
 		ok := in.ReadASN1GeneralizedTime(&out)
 		ok := in.ReadASN1GeneralizedTime(&out)
 		if ok != test.ok || ok && !reflect.DeepEqual(out, test.out) {
 		if ok != test.ok || ok && !reflect.DeepEqual(out, test.out) {
@@ -263,20 +311,20 @@ func TestReadASN1BitString(t *testing.T) {
 	testData := []struct {
 	testData := []struct {
 		in  []byte
 		in  []byte
 		ok  bool
 		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 {
 	for i, test := range testData {
 		in := String(append([]byte{3, byte(len(test.in))}, test.in...))
 		in := String(append([]byte{3, byte(len(test.in))}, test.in...))
-		var out asn1.BitString
+		var out encoding_asn1.BitString
 		ok := in.ReadASN1BitString(&out)
 		ok := in.ReadASN1BitString(&out)
 		if ok != test.ok || ok && (!bytes.Equal(out.Bytes, test.out.Bytes) || out.BitLength != test.out.BitLength) {
 		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)
 			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.
 // 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.
 // 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 {
 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.
 // 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
 // BuilderContinuation is continuation-passing interface for building
 // length-prefixed byte sequences. Builder methods for length-prefixed
 // 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
 // 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 := cryptobyte.NewBuilder()
 //   parent.AddUint8LengthPrefixed(func (child *Builder) {
 //   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
 // 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
 // invalid, i.e. users must not store any copies or references of the child
 // that outlive the continuation.
 // 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)
 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.
 // AddUint8LengthPrefixed adds a 8-bit length-prefixed byte sequence.
 func (b *Builder) AddUint8LengthPrefixed(f BuilderContinuation) {
 func (b *Builder) AddUint8LengthPrefixed(f BuilderContinuation) {
 	b.addLengthPrefixed(1, false, f)
 	b.addLengthPrefixed(1, false, f)
@@ -119,6 +140,34 @@ func (b *Builder) AddUint24LengthPrefixed(f BuilderContinuation) {
 	b.addLengthPrefixed(3, false, f)
 	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) {
 func (b *Builder) addLengthPrefixed(lenLen int, isASN1 bool, f BuilderContinuation) {
 	// Subsequent writes can be ignored if the builder has encountered an error.
 	// Subsequent writes can be ignored if the builder has encountered an error.
 	if b.err != nil {
 	if b.err != nil {
@@ -128,15 +177,20 @@ func (b *Builder) addLengthPrefixed(lenLen int, isASN1 bool, f BuilderContinuati
 	offset := len(b.result)
 	offset := len(b.result)
 	b.add(make([]byte, lenLen)...)
 	b.add(make([]byte, lenLen)...)
 
 
+	if b.inContinuation == nil {
+		b.inContinuation = new(bool)
+	}
+
 	b.child = &Builder{
 	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()
 	b.flushChild()
 	if b.child != nil {
 	if b.child != nil {
 		panic("cryptobyte: internal error")
 		panic("cryptobyte: internal error")

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

@@ -6,6 +6,7 @@ package cryptobyte
 
 
 import (
 import (
 	"bytes"
 	"bytes"
+	"errors"
 	"fmt"
 	"fmt"
 	"testing"
 	"testing"
 )
 )
@@ -18,6 +19,54 @@ func builderBytesEq(b *Builder, want ...byte) error {
 	return nil
 	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) {
 func TestBytes(t *testing.T) {
 	var b Builder
 	var b Builder
 	v := []byte("foobarbaz")
 	v := []byte("foobarbaz")

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

@@ -5,9 +5,11 @@
 package cryptobyte_test
 package cryptobyte_test
 
 
 import (
 import (
-	"encoding/asn1"
+	"errors"
 	"fmt"
 	"fmt"
+
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/cryptobyte"
 	"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() {
 func ExampleString_lengthPrefixed() {
@@ -37,7 +39,7 @@ func ExampleString_lengthPrefixed() {
 	fmt.Printf("%#v\n", result)
 	fmt.Printf("%#v\n", result)
 }
 }
 
 
-func ExampleString_asn1() {
+func ExampleString_aSN1() {
 	// This is an example of parsing ASN.1 data that looks like:
 	// This is an example of parsing ASN.1 data that looks like:
 	//    Foo ::= SEQUENCE {
 	//    Foo ::= SEQUENCE {
 	//      version [6] INTEGER DEFAULT 0
 	//      version [6] INTEGER DEFAULT 0
@@ -51,12 +53,12 @@ func ExampleString_asn1() {
 		data, inner, versionBytes cryptobyte.String
 		data, inner, versionBytes cryptobyte.String
 		haveVersion               bool
 		haveVersion               bool
 	)
 	)
-	if !input.ReadASN1(&inner, cryptobyte.Tag(asn1.TagSequence).Constructed()) ||
+	if !input.ReadASN1(&inner, asn1.SEQUENCE) ||
 		!input.Empty() ||
 		!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.ReadASN1Integer(&version)) ||
 		(haveVersion && !versionBytes.Empty()) ||
 		(haveVersion && !versionBytes.Empty()) ||
-		!inner.ReadASN1(&data, asn1.TagOctetString) ||
+		!inner.ReadASN1(&data, asn1.OCTET_STRING) ||
 		!inner.Empty() {
 		!inner.Empty() {
 		panic("bad format")
 		panic("bad format")
 	}
 	}
@@ -65,7 +67,7 @@ func ExampleString_asn1() {
 	fmt.Printf("haveVersion: %t, version: %d, data: %s\n", haveVersion, version, string(data))
 	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:
 	// This is an example of building ASN.1 data that looks like:
 	//    Foo ::= SEQUENCE {
 	//    Foo ::= SEQUENCE {
 	//      version [6] INTEGER DEFAULT 0
 	//      version [6] INTEGER DEFAULT 0
@@ -77,9 +79,9 @@ func ExampleBuilder_asn1() {
 	const defaultVersion = 0
 	const defaultVersion = 0
 
 
 	var b cryptobyte.Builder
 	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 {
 		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)
 				b.AddASN1Int64(version)
 			})
 			})
 		}
 		}
@@ -118,3 +120,35 @@ func ExampleBuilder_lengthPrefixed() {
 	// Output: 000c0568656c6c6f05776f726c64
 	// Output: 000c0568656c6c6f05776f726c64
 	fmt.Printf("%x\n", result)
 	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
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 // 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"
 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
 // 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
 	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 {
 func (s *String) ReadUint8(out *uint8) bool {
 	v := s.read(1)
 	v := s.read(1)
 	if v == nil {
 	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.
 // 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 {
 func (s *String) ReadUint16(out *uint16) bool {
 	v := s.read(2)
 	v := s.read(2)
 	if v == nil {
 	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.
 // 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 {
 func (s *String) ReadUint24(out *uint32) bool {
 	v := s.read(3)
 	v := s.read(3)
 	if v == nil {
 	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.
 // 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 {
 func (s *String) ReadUint32(out *uint32) bool {
 	v := s.read(4)
 	v := s.read(4)
 	if v == nil {
 	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
 // 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 {
 func (s *String) ReadUint8LengthPrefixed(out *String) bool {
 	return s.readLengthPrefixed(1, out)
 	return s.readLengthPrefixed(1, out)
 }
 }
 
 
 // ReadUint16LengthPrefixed reads the content of a big-endian, 16-bit
 // 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 {
 func (s *String) ReadUint16LengthPrefixed(out *String) bool {
 	return s.readLengthPrefixed(2, out)
 	return s.readLengthPrefixed(2, out)
 }
 }
 
 
 // ReadUint24LengthPrefixed reads the content of a big-endian, 24-bit
 // 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 {
 func (s *String) ReadUint24LengthPrefixed(out *String) bool {
 	return s.readLengthPrefixed(3, out)
 	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 {
 func (s *String) ReadBytes(out *[]byte, n int) bool {
 	v := s.read(n)
 	v := s.read(n)
 	if v == nil {
 	if v == nil {
@@ -140,8 +149,8 @@ func (s *String) ReadBytes(out *[]byte, n int) bool {
 	return true
 	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 {
 func (s *String) CopyBytes(out []byte) bool {
 	n := len(out)
 	n := len(out)
 	v := s.read(n)
 	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
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 // 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.
 // non-amd64 platforms. The amd64 assembly does not support gccgo.
 // +build !amd64 gccgo appengine
 // +build !amd64 gccgo appengine
 
 

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

@@ -6,17 +6,20 @@
 // https://ed25519.cr.yp.to/.
 // https://ed25519.cr.yp.to/.
 //
 //
 // These functions are also compatible with the “Ed25519” function defined in
 // 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
 package ed25519
 
 
 // This code is a port of the public domain, “ref10” implementation of ed25519
 // This code is a port of the public domain, “ref10” implementation of ed25519
 // from SUPERCOP.
 // from SUPERCOP.
 
 
 import (
 import (
+	"bytes"
 	"crypto"
 	"crypto"
 	cryptorand "crypto/rand"
 	cryptorand "crypto/rand"
 	"crypto/sha512"
 	"crypto/sha512"
-	"crypto/subtle"
 	"errors"
 	"errors"
 	"io"
 	"io"
 	"strconv"
 	"strconv"
@@ -31,6 +34,8 @@ const (
 	PrivateKeySize = 64
 	PrivateKeySize = 64
 	// SignatureSize is the size, in bytes, of signatures generated and verified by this package.
 	// SignatureSize is the size, in bytes, of signatures generated and verified by this package.
 	SignatureSize = 64
 	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.
 // PublicKey is the type of Ed25519 public keys.
@@ -46,6 +51,15 @@ func (priv PrivateKey) Public() crypto.PublicKey {
 	return PublicKey(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.
 // Sign signs the given message with priv.
 // Ed25519 performs two passes over messages to be signed and therefore cannot
 // Ed25519 performs two passes over messages to be signed and therefore cannot
 // handle pre-hashed messages. Thus opts.HashFunc() must return zero to
 // 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.
 // GenerateKey generates a public/private key pair using entropy from rand.
 // If rand is nil, crypto/rand.Reader will be used.
 // 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 {
 	if rand == nil {
 		rand = cryptorand.Reader
 		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
 		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[0] &= 248
 	digest[31] &= 127
 	digest[31] &= 127
 	digest[31] |= 64
 	digest[31] |= 64
@@ -85,10 +113,11 @@ func GenerateKey(rand io.Reader) (publicKey PublicKey, privateKey PrivateKey, er
 	var publicKeyBytes [32]byte
 	var publicKeyBytes [32]byte
 	A.ToBytes(&publicKeyBytes)
 	A.ToBytes(&publicKeyBytes)
 
 
+	privateKey := make([]byte, PrivateKeySize)
+	copy(privateKey, seed)
 	copy(privateKey[32:], publicKeyBytes[:])
 	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
 // 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)
 	edwards25519.ScReduce(&hReduced, &digest)
 
 
 	var R edwards25519.ProjectiveGroupElement
 	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
 	var checkR [32]byte
 	R.ToBytes(&checkR)
 	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) {
 		if !Verify(pubKey, msg, sig2) {
 			t.Errorf("signature failed to verify on line %d", lineNo)
 			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 {
 	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) {
 func BenchmarkKeyGeneration(b *testing.B) {
 	var zero zeroReader
 	var zero zeroReader
 	for i := 0; i < b.N; i++ {
 	for i := 0; i < b.N; i++ {

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

@@ -4,6 +4,8 @@
 
 
 package edwards25519
 package edwards25519
 
 
+import "encoding/binary"
+
 // This code is a port of the public domain, “ref10” implementation of ed25519
 // This code is a port of the public domain, “ref10” implementation of ed25519
 // from SUPERCOP.
 // from SUPERCOP.
 
 
@@ -1769,3 +1771,23 @@ func ScReduce(out *[32]byte, s *[64]byte) {
 	out[30] = byte(s11 >> 9)
 	out[30] = byte(s11 >> 9)
 	out[31] = byte(s11 >> 17)
 	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.
 // 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
 Box uses Curve25519, XSalsa20 and Poly1305 to encrypt and authenticate
 messages. The length of messages is not hidden.
 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
 message, etc. Nonces are long enough that randomly generated nonces have
 negligible risk of collision.
 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.
 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"
 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
 // 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.
 // 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 {
 func Seal(out, message []byte, nonce *[24]byte, peersPublicKey, privateKey *[32]byte) []byte {
 	var sharedKey [32]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
 message, etc. Nonces are long enough that randomly generated nonces have
 negligible risk of collision.
 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.
 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"
 package secretbox // import "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/nacl/secretbox"
 
 
 import (
 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/poly1305"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/salsa20/salsa"
 	"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[:])
 	copy(poly1305Key[:], firstBlock[:])
 
 
 	ret, out := sliceForAppend(out, len(message)+poly1305.TagSize)
 	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
 	// We XOR up to 32 bytes of message with the keystream generated from
 	// the first block.
 	// 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
 // 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
 // message to out, which must not overlap box. The output will be Overhead
 // bytes smaller than box.
 // 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 {
 	if len(box) < Overhead {
 		return nil, false
 		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)
 	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
 	// We XOR up to 32 bytes of box with the keystream generated from
 	// the first block.
 	// 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.
 // The enumerated reasons for revoking a certificate.  See RFC 5280.
 const (
 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.
 // Request represents an OCSP request. See RFC 6960.
@@ -488,10 +488,6 @@ func ParseResponseForCert(bytes []byte, cert, issuer *x509.Certificate) (*Respon
 		return nil, err
 		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 {
 	if n := len(basicResp.TBSResponseData.Responses); n == 0 || cert == nil && n > 1 {
 		return nil, ParseError("OCSP response contains bad number of responses")
 		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 {
 	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)
 		ret.Certificate, err = x509.ParseCertificate(basicResp.Certificates[0].FullBytes)
 		if err != nil {
 		if err != nil {
 			return nil, err
 			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 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.
 // RevocationReason, ThisUpdate, and NextUpdate fields.
 //
 //
 // If template.IssuerHash is not set, SHA1 will be used.
 // 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 {
 	if template.Certificate != nil {
 		response.Certificates = []asn1.RawValue{
 		response.Certificates = []asn1.RawValue{
-			asn1.RawValue{FullBytes: template.Certificate.Raw},
+			{FullBytes: template.Certificate.Raw},
 		}
 		}
 	}
 	}
 	responseDER, err := asn1.Marshal(response)
 	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) {
 	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) {
 	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 {
 	if resp.Status != expected.Status {
@@ -218,7 +218,7 @@ func TestOCSPResponse(t *testing.T) {
 
 
 	extensionBytes, _ := hex.DecodeString(ocspExtensionValueHex)
 	extensionBytes, _ := hex.DecodeString(ocspExtensionValueHex)
 	extensions := []pkix.Extension{
 	extensions := []pkix.Extension{
-		pkix.Extension{
+		{
 			Id:       ocspExtensionOID,
 			Id:       ocspExtensionOID,
 			Critical: false,
 			Critical: false,
 			Value:    extensionBytes,
 			Value:    extensionBytes,
@@ -268,15 +268,15 @@ func TestOCSPResponse(t *testing.T) {
 			}
 			}
 
 
 			if !reflect.DeepEqual(resp.ThisUpdate, template.ThisUpdate) {
 			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) {
 			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) {
 			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) {
 			if !reflect.DeepEqual(resp.Extensions, template.ExtraExtensions) {

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

@@ -13,6 +13,7 @@ import (
 	"bufio"
 	"bufio"
 	"bytes"
 	"bytes"
 	"crypto"
 	"crypto"
+	"fmt"
 	"hash"
 	"hash"
 	"io"
 	"io"
 	"net/textproto"
 	"net/textproto"
@@ -177,8 +178,9 @@ func Decode(data []byte) (b *Block, rest []byte) {
 // message.
 // message.
 type dashEscaper struct {
 type dashEscaper struct {
 	buffered *bufio.Writer
 	buffered *bufio.Writer
-	h        hash.Hash
+	hashers  []hash.Hash // one per key in privateKeys
 	hashType crypto.Hash
 	hashType crypto.Hash
+	toHash   io.Writer // writes to all the hashes in hashers
 
 
 	atBeginningOfLine bool
 	atBeginningOfLine bool
 	isFirstLine       bool
 	isFirstLine       bool
@@ -186,8 +188,8 @@ type dashEscaper struct {
 	whitespace []byte
 	whitespace []byte
 	byteBuf    []byte // a one byte buffer to save allocations
 	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) {
 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
 			// 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.
 			// until this point (the start of the next line) before writing it.
 			if !d.isFirstLine {
 			if !d.isFirstLine {
-				d.h.Write(crlf)
+				d.toHash.Write(crlf)
 			}
 			}
 			d.isFirstLine = false
 			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 {
 				if _, err = d.buffered.Write(dashEscape); err != nil {
 					return
 					return
 				}
 				}
-				d.h.Write(d.byteBuf)
+				d.toHash.Write(d.byteBuf)
 				d.atBeginningOfLine = false
 				d.atBeginningOfLine = false
 			} else if b == '\n' {
 			} else if b == '\n' {
 				// Nothing to do because we delay writing CRLF to the hash.
 				// Nothing to do because we delay writing CRLF to the hash.
 			} else {
 			} else {
-				d.h.Write(d.byteBuf)
+				d.toHash.Write(d.byteBuf)
 				d.atBeginningOfLine = false
 				d.atBeginningOfLine = false
 			}
 			}
 			if err = d.buffered.WriteByte(b); err != nil {
 			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
 				// Any buffered whitespace wasn't at the end of the line so
 				// we need to write it out.
 				// we need to write it out.
 				if len(d.whitespace) > 0 {
 				if len(d.whitespace) > 0 {
-					d.h.Write(d.whitespace)
+					d.toHash.Write(d.whitespace)
 					if _, err = d.buffered.Write(d.whitespace); err != nil {
 					if _, err = d.buffered.Write(d.whitespace); err != nil {
 						return
 						return
 					}
 					}
 					d.whitespace = d.whitespace[:0]
 					d.whitespace = d.whitespace[:0]
 				}
 				}
-				d.h.Write(d.byteBuf)
+				d.toHash.Write(d.byteBuf)
 				if err = d.buffered.WriteByte(b); err != nil {
 				if err = d.buffered.WriteByte(b); err != nil {
 					return
 					return
 				}
 				}
@@ -269,25 +271,29 @@ func (d *dashEscaper) Close() (err error) {
 			return
 			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)
 	out, err := armor.Encode(d.buffered, "PGP SIGNATURE", nil)
 	if err != nil {
 	if err != nil {
 		return
 		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 {
 	if err = out.Close(); err != nil {
 		return
 		return
 	}
 	}
@@ -300,8 +306,17 @@ func (d *dashEscaper) Close() (err error) {
 // Encode returns a WriteCloser which will clear-sign a message with privateKey
 // 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.
 // 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) {
 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()
 	hashType := config.Hash()
@@ -313,7 +328,14 @@ func Encode(w io.Writer, privateKey *packet.PrivateKey, config *packet.Config) (
 	if !hashType.Available() {
 	if !hashType.Available() {
 		return nil, errors.UnsupportedError("unsupported hash type: " + strconv.Itoa(int(hashType)))
 		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)
 	buffered := bufio.NewWriter(w)
 	// start has a \n at the beginning that we don't want here.
 	// 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{
 	plaintext = &dashEscaper{
 		buffered: buffered,
 		buffered: buffered,
-		h:        h,
+		hashers:  hashers,
 		hashType: hashType,
 		hashType: hashType,
+		toHash:   toHash,
 
 
 		atBeginningOfLine: true,
 		atBeginningOfLine: true,
 		isFirstLine:       true,
 		isFirstLine:       true,
 
 
 		byteBuf: make([]byte, 1),
 		byteBuf: make([]byte, 1),
 
 
-		privateKey: privateKey,
-		config:     config,
+		privateKeys: privateKeys,
+		config:      config,
 	}
 	}
 
 
 	return
 	return

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

@@ -6,8 +6,11 @@ package clearsign
 
 
 import (
 import (
 	"bytes"
 	"bytes"
-	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/openpgp"
+	"fmt"
 	"testing"
 	"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) {
 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(`
 var clearsignInput = []byte(`
 ;lasjlkfdsa
 ;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 {
 		if e.PrivateKey, ok = p.(*packet.PrivateKey); !ok {
 			packets.Unread(p)
 			packets.Unread(p)
 			return nil, errors.StructuralError("first packet was not a public/private key")
 			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() {
 	if !e.PrimaryKey.PubKeyAlgo.CanSign() {
 		return nil, errors.StructuralError("primary key cannot be used for signatures")
 		return nil, errors.StructuralError("primary key cannot be used for signatures")
 	}
 	}
 
 
-	var current *Identity
 	var revocations []*packet.Signature
 	var revocations []*packet.Signature
 EachPacket:
 EachPacket:
 	for {
 	for {
@@ -347,32 +345,8 @@ EachPacket:
 
 
 		switch pkt := p.(type) {
 		switch pkt := p.(type) {
 		case *packet.UserId:
 		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:
 		case *packet.Signature:
 			if pkt.SigType == packet.SigTypeKeyRevocation {
 			if pkt.SigType == packet.SigTypeKeyRevocation {
@@ -381,11 +355,9 @@ EachPacket:
 				// TODO: RFC4880 5.2.1 permits signatures
 				// TODO: RFC4880 5.2.1 permits signatures
 				// directly on keys (eg. to bind additional
 				// directly on keys (eg. to bind additional
 				// revocation keys).
 				// 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:
 		case *packet.PrivateKey:
 			if pkt.IsSubkey == false {
 			if pkt.IsSubkey == false {
 				packets.Unread(p)
 				packets.Unread(p)
@@ -426,33 +398,105 @@ EachPacket:
 	return e, nil
 	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 {
 func addSubkey(e *Entity, packets *packet.Reader, pub *packet.PublicKey, priv *packet.PrivateKey) error {
 	var subKey Subkey
 	var subKey Subkey
 	subKey.PublicKey = pub
 	subKey.PublicKey = pub
 	subKey.PrivateKey = priv
 	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")
 		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)
 	e.Subkeys = append(e.Subkeys, subKey)
+
 	return nil
 	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
 const defaultRSAKeyBits = 2048
 
 
 // NewEntity returns an Entity that contains a fresh RSA/RSA keypair with a
 // 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
 	isPrimaryId := true
 	e.Identities[uid.Id] = &Identity{
 	e.Identities[uid.Id] = &Identity{
-		Name:   uid.Name,
+		Name:   uid.Id,
 		UserId: uid,
 		UserId: uid,
 		SelfSignature: &packet.Signature{
 		SelfSignature: &packet.Signature{
 			CreationTime: currentTime,
 			CreationTime: currentTime,
@@ -501,6 +545,10 @@ func NewEntity(name, comment, email string, config *packet.Config) (*Entity, err
 			IssuerKeyId:  &e.PrimaryKey.KeyId,
 			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,
 	// If the user passes in a DefaultHash via packet.Config,
 	// set the PreferredHash for the SelfSignature.
 	// 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)}
 		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 = make([]Subkey, 1)
 	e.Subkeys[0] = Subkey{
 	e.Subkeys[0] = Subkey{
 		PublicKey:  packet.NewRSAPublicKey(currentTime, &encryptingPriv.PublicKey),
 		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].PublicKey.IsSubkey = true
 	e.Subkeys[0].PrivateKey.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
 	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.
 // If config is nil, sensible defaults will be used.
 func (e *Entity) SerializePrivate(w io.Writer, config *packet.Config) (err error) {
 func (e *Entity) SerializePrivate(w io.Writer, config *packet.Config) (err error) {
 	err = e.PrivateKey.Serialize(w)
 	err = e.PrivateKey.Serialize(w)
@@ -569,8 +625,8 @@ func (e *Entity) SerializePrivate(w io.Writer, config *packet.Config) (err error
 	return nil
 	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 {
 func (e *Entity) Serialize(w io.Writer) error {
 	err := e.PrimaryKey.Serialize(w)
 	err := e.PrimaryKey.Serialize(w)
 	if err != nil {
 	if err != nil {

File diff suppressed because it is too large
+ 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 {
 	switch e.Algo {
 	case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly:
 	case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly:
 		e.encryptedMPI1.bytes, e.encryptedMPI1.bitLength, err = readMPI(r)
 		e.encryptedMPI1.bytes, e.encryptedMPI1.bitLength, err = readMPI(r)
+		if err != nil {
+			return
+		}
 	case PubKeyAlgoElGamal:
 	case PubKeyAlgoElGamal:
 		e.encryptedMPI1.bytes, e.encryptedMPI1.bitLength, err = readMPI(r)
 		e.encryptedMPI1.bytes, e.encryptedMPI1.bitLength, err = readMPI(r)
 		if err != nil {
 		if err != nil {
 			return
 			return
 		}
 		}
 		e.encryptedMPI2.bytes, e.encryptedMPI2.bitLength, err = readMPI(r)
 		e.encryptedMPI2.bytes, e.encryptedMPI2.bitLength, err = readMPI(r)
+		if err != nil {
+			return
+		}
 	}
 	}
 	_, err = consumeAll(r)
 	_, err = consumeAll(r)
 	return
 	return
@@ -72,7 +78,8 @@ func (e *EncryptedKey) Decrypt(priv *PrivateKey, config *Config) error {
 	// padding oracle attacks.
 	// padding oracle attacks.
 	switch priv.PubKeyAlgo {
 	switch priv.PubKeyAlgo {
 	case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly:
 	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:
 	case PubKeyAlgoElGamal:
 		c1 := new(big.Int).SetBytes(e.encryptedMPI1.bytes)
 		c1 := new(big.Int).SetBytes(e.encryptedMPI1.bytes)
 		c2 := new(big.Int).SetBytes(e.encryptedMPI2.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) {
 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)
 	keyHex := fmt.Sprintf("%x", ek.Key)
 	if keyHex != expectedKeyHex {
 	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/aes"
 	"crypto/cipher"
 	"crypto/cipher"
 	"crypto/des"
 	"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"
 	"io"
 	"math/big"
 	"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
 // 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
 	numBytes := (int(bitLength) + 7) / 8
 	mpi = make([]byte, numBytes)
 	mpi = make([]byte, numBytes)
 	_, err = readFull(r, mpi)
 	_, 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
 	return
 }
 }
 
 
 // writeMPI serializes a big integer to w.
 // writeMPI serializes a big integer to w.
 func writeMPI(w io.Writer, bitLength uint16, mpiBytes []byte) (err error) {
 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)})
 	_, err = w.Write([]byte{byte(bitLength >> 8), byte(bitLength)})
 	if err == nil {
 	if err == nil {
 		_, err = w.Write(mpiBytes)
 		_, 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())
 	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
 // CompressionAlgo Represents the different compression algorithms
 // supported by OpenPGP (except for BZIP2, which is not currently
 // supported by OpenPGP (except for BZIP2, which is not currently
 // supported). See Section 9.3 of RFC 4880.
 // 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.
 // implements RSA or ECDSA.
 func NewSignerPrivateKey(currentTime time.Time, signer crypto.Signer) *PrivateKey {
 func NewSignerPrivateKey(currentTime time.Time, signer crypto.Signer) *PrivateKey {
 	pk := new(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) {
 	switch pubkey := signer.Public().(type) {
+	case *rsa.PublicKey:
+		pk.PublicKey = *NewRSAPublicKey(currentTime, pubkey)
+		pk.PubKeyAlgo = PubKeyAlgoRSASignOnly
 	case rsa.PublicKey:
 	case rsa.PublicKey:
 		pk.PublicKey = *NewRSAPublicKey(currentTime, &pubkey)
 		pk.PublicKey = *NewRSAPublicKey(currentTime, &pubkey)
 		pk.PubKeyAlgo = PubKeyAlgoRSASignOnly
 		pk.PubKeyAlgo = PubKeyAlgoRSASignOnly
+	case *ecdsa.PublicKey:
+		pk.PublicKey = *NewECDSAPublicKey(currentTime, pubkey)
 	case ecdsa.PublicKey:
 	case ecdsa.PublicKey:
 		pk.PublicKey = *NewECDSAPublicKey(currentTime, &pubkey)
 		pk.PublicKey = *NewECDSAPublicKey(currentTime, &pubkey)
 	default:
 	default:

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

@@ -14,7 +14,6 @@ import (
 	"crypto/x509"
 	"crypto/x509"
 	"encoding/hex"
 	"encoding/hex"
 	"hash"
 	"hash"
-	"io"
 	"testing"
 	"testing"
 	"time"
 	"time"
 )
 )
@@ -162,15 +161,7 @@ func TestECDSAPrivateKey(t *testing.T) {
 }
 }
 
 
 type rsaSigner struct {
 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) {
 func TestRSASignerPrivateKey(t *testing.T) {
@@ -208,15 +199,7 @@ func TestRSASignerPrivateKey(t *testing.T) {
 }
 }
 
 
 type ecdsaSigner struct {
 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) {
 func TestECDSASignerPrivateKey(t *testing.T) {
@@ -228,7 +211,7 @@ func TestECDSASignerPrivateKey(t *testing.T) {
 	priv := NewSignerPrivateKey(time.Now(), &ecdsaSigner{ecdsaPriv})
 	priv := NewSignerPrivateKey(time.Now(), &ecdsaSigner{ecdsaPriv})
 
 
 	if priv.PubKeyAlgo != PubKeyAlgoECDSA {
 	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{
 	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.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()
 	pk.setFingerPrintAndKeyId()
 	return pk
 	return pk
@@ -515,7 +520,7 @@ func (pk *PublicKey) VerifySignature(signed hash.Hash, sig *Signature) (err erro
 	switch pk.PubKeyAlgo {
 	switch pk.PubKeyAlgo {
 	case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
 	case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
 		rsaPublicKey, _ := pk.PublicKey.(*rsa.PublicKey)
 		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 {
 		if err != nil {
 			return errors.SignatureError("RSA verification failure")
 			return errors.SignatureError("RSA verification failure")
 		}
 		}
@@ -566,7 +571,7 @@ func (pk *PublicKey) VerifySignatureV3(signed hash.Hash, sig *SignatureV3) (err
 	switch pk.PubKeyAlgo {
 	switch pk.PubKeyAlgo {
 	case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
 	case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
 		rsaPublicKey := pk.PublicKey.(*rsa.PublicKey)
 		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 errors.SignatureError("RSA verification failure")
 		}
 		}
 		return
 		return

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

@@ -6,7 +6,10 @@ package packet
 
 
 import (
 import (
 	"bytes"
 	"bytes"
+	"crypto/ecdsa"
+	"crypto/elliptic"
 	"encoding/hex"
 	"encoding/hex"
+	"math/big"
 	"testing"
 	"testing"
 	"time"
 	"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 rsaFingerprintHex = "5fb74b1d03b1e3cb31bc2f8aa34d7e18c20c31bb"
 
 
 const rsaPkDataHex = "988d044d3c5c10010400b1d13382944bd5aba23a4312968b5095d14f947f600eb478e14a6fcb16b0e0cac764884909c020bc495cfcc39a935387c661507bdb236a0612fb582cac3af9b29cc2c8c70090616c41b662f4da4c1201e195472eb7f4ae1ccbcbf9940fe21d985e379a5563dde5b9a23d35f1cfaa5790da3b79db26f23695107bfaca8e7b5bcd0011010001"
 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)))
 		return nil, ske.CipherFunc, errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(cipherFunc)))
 	}
 	}
 	plaintextKey = plaintextKey[1:]
 	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
 	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 symmetricallyEncryptedHex = "8c0d04030302371a0b38d884f02060c91cf97c9973b8e58e028e9501708ccfe618fb92afef7fa2d80ddadd93cf"
 const symmetricallyEncryptedContentsHex = "cb1062004d14c4df636f6e74656e74732e0a"
 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
 		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)
 		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
 	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
 	var signer *packet.PrivateKey
 	if signed != nil {
 	if signed != nil {
 		signKey, ok := signed.signingKey(config.Now())
 		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.
 	// These are the possible ciphers that we'll use for the message.
 	candidateCiphers := []uint8{
 	candidateCiphers := []uint8{
 		uint8(packet.CipherAES128),
 		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())
 	symKey := make([]byte, cipher.KeySize())
 	if _, err := io.ReadFull(config.Random(), symKey); err != nil {
 	if _, err := io.ReadFull(config.Random(), symKey); err != nil {
 		return nil, err
 		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 {
 	if err != nil {
 		return
 		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
 // 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) {
 func TestWithHMACSHA256(t *testing.T) {
 	testHash(t, sha256.New, "SHA256", sha256TestVectors)
 	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
 	return
 }
 }
 
 
-// decryptable abstracts a object that contains ciphertext.
+// decryptable abstracts an object that contains ciphertext.
 type decryptable interface {
 type decryptable interface {
 	Algorithm() pkix.AlgorithmIdentifier
 	Algorithm() pkix.AlgorithmIdentifier
 	Data() []byte
 	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]
 	r3 = r3 + c.k[r2&63]
 
 
 	for j <= 40 {
 	for j <= 40 {
-
 		// mix r0
 		// mix r0
 		r0 = r0 + c.k[j] + (r3 & r2) + ((^r3) & r1)
 		r0 = r0 + c.k[j] + (r3 & r2) + ((^r3) & r1)
 		r0 = rotl16(r0, 1)
 		r0 = rotl16(r0, 1)
@@ -151,7 +150,6 @@ func (c *rc2Cipher) Encrypt(dst, src []byte) {
 	r3 = r3 + c.k[r2&63]
 	r3 = r3 + c.k[r2&63]
 
 
 	for j <= 60 {
 	for j <= 60 {
-
 		// mix r0
 		// mix r0
 		r0 = r0 + c.k[j] + (r3 & r2) + ((^r3) & r1)
 		r0 = r0 + c.k[j] + (r3 & r2) + ((^r3) & r1)
 		r0 = rotl16(r0, 1)
 		r0 = rotl16(r0, 1)
@@ -244,7 +242,6 @@ func (c *rc2Cipher) Decrypt(dst, src []byte) {
 	r0 = r0 - c.k[r3&63]
 	r0 = r0 - c.k[r3&63]
 
 
 	for j >= 0 {
 	for j >= 0 {
-
 		// unmix r3
 		// unmix r3
 		r3 = rotl16(r3, 16-5)
 		r3 = rotl16(r3, 16-5)
 		r3 = r3 - c.k[j] - (r2 & r1) - ((^r2) & r0)
 		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) {
 func TestEncryptDecrypt(t *testing.T) {
-
 	// TODO(dgryski): add the rest of the test vectors from the RFC
 	// TODO(dgryski): add the rest of the test vectors from the RFC
 	var tests = []struct {
 	var tests = []struct {
 		key    string
 		key    string

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

@@ -5,7 +5,6 @@
 package poly1305
 package poly1305
 
 
 import (
 import (
-	"bytes"
 	"encoding/hex"
 	"encoding/hex"
 	"flag"
 	"flag"
 	"testing"
 	"testing"
@@ -14,80 +13,51 @@ import (
 
 
 var stressFlag = flag.Bool("stress", false, "run slow stress tests")
 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
 	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 {
 	for i, v := range testData {
-		in := v.in
+		in := v.Input()
 		if unaligned {
 		if unaligned {
 			in = unalignBytes(in)
 			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) {
 func benchmark(b *testing.B, size int, unaligned bool) {
 	var out [16]byte
 	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 Benchmark1K(b *testing.B)          { benchmark(b, 1024, false) }
 func Benchmark64Unaligned(b *testing.B) { benchmark(b, 64, true) }
 func Benchmark64Unaligned(b *testing.B) { benchmark(b, 64, true) }
 func Benchmark1KUnaligned(b *testing.B) { benchmark(b, 1024, true) }
 func Benchmark1KUnaligned(b *testing.B) { benchmark(b, 1024, true) }
+func Benchmark2M(b *testing.B)          { benchmark(b, 2097152, true) }
 
 
 func unalignBytes(in []byte) []byte {
 func unalignBytes(in []byte) []byte {
 	out := make([]byte, len(in)+1)
 	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)
+}

Some files were not shown because too many files changed in this diff