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

Merge commit '206c631317ddbca60a26bf039aceb685da27e9bb'

- Squash update x/crypto/ssh fork subtree to 089bfa5675191fd96a44247682f76ebca03d7916
Rod Hynes 4 лет назад
Родитель
Сommit
b38e1eec45
77 измененных файлов с 11961 добавлено и 2027 удалено
  1. 0 2
      psiphon/common/crypto/.gitignore
  2. 40 0
      psiphon/common/crypto/internal/poly1305/bits_compat.go
  3. 22 0
      psiphon/common/crypto/internal/poly1305/bits_go1.13.go
  4. 10 0
      psiphon/common/crypto/internal/poly1305/mac_noasm.go
  5. 99 0
      psiphon/common/crypto/internal/poly1305/poly1305.go
  6. 276 0
      psiphon/common/crypto/internal/poly1305/poly1305_test.go
  7. 48 0
      psiphon/common/crypto/internal/poly1305/sum_amd64.go
  8. 109 0
      psiphon/common/crypto/internal/poly1305/sum_amd64.s
  9. 310 0
      psiphon/common/crypto/internal/poly1305/sum_generic.go
  10. 48 0
      psiphon/common/crypto/internal/poly1305/sum_ppc64le.go
  11. 182 0
      psiphon/common/crypto/internal/poly1305/sum_ppc64le.s
  12. 76 0
      psiphon/common/crypto/internal/poly1305/sum_s390x.go
  13. 504 0
      psiphon/common/crypto/internal/poly1305/sum_s390x.s
  14. 3000 0
      psiphon/common/crypto/internal/poly1305/vectors_test.go
  15. 2 1
      psiphon/common/crypto/internal/subtle/aliasing.go
  16. 36 0
      psiphon/common/crypto/internal/subtle/aliasing_purego.go
  17. 1 1
      psiphon/common/crypto/nacl/secretbox/secretbox.go
  18. 1 1
      psiphon/common/crypto/nacl/secretbox/secretbox_reader.go
  19. 140 10
      psiphon/common/crypto/ssh/agent/client.go
  20. 189 16
      psiphon/common/crypto/ssh/agent/client_test.go
  21. 9 9
      psiphon/common/crypto/ssh/agent/example_test.go
  22. 27 1
      psiphon/common/crypto/ssh/agent/keyring.go
  23. 48 1
      psiphon/common/crypto/ssh/agent/server.go
  24. 3 0
      psiphon/common/crypto/ssh/benchmark_test.go
  25. 40 15
      psiphon/common/crypto/ssh/certs.go
  26. 14 14
      psiphon/common/crypto/ssh/cipher.go
  27. 8 8
      psiphon/common/crypto/ssh/cipher_test.go
  28. 2 2
      psiphon/common/crypto/ssh/client.go
  29. 127 11
      psiphon/common/crypto/ssh/client_auth.go
  30. 277 7
      psiphon/common/crypto/ssh/client_auth_test.go
  31. 49 0
      psiphon/common/crypto/ssh/client_test.go
  32. 32 8
      psiphon/common/crypto/ssh/common.go
  33. 176 0
      psiphon/common/crypto/ssh/common_test.go
  34. 2 1
      psiphon/common/crypto/ssh/example_test.go
  35. 3 3
      psiphon/common/crypto/ssh/handshake.go
  36. 3 0
      psiphon/common/crypto/ssh/handshake_test.go
  37. 93 0
      psiphon/common/crypto/ssh/internal/bcrypt_pbkdf/bcrypt_pbkdf.go
  38. 97 0
      psiphon/common/crypto/ssh/internal/bcrypt_pbkdf/bcrypt_pbkdf_test.go
  39. 243 1
      psiphon/common/crypto/ssh/kex.go
  40. 40 25
      psiphon/common/crypto/ssh/kex_test.go
  41. 507 67
      psiphon/common/crypto/ssh/keys.go
  42. 122 35
      psiphon/common/crypto/ssh/keys_test.go
  43. 2 2
      psiphon/common/crypto/ssh/knownhosts/knownhosts.go
  44. 100 0
      psiphon/common/crypto/ssh/messages.go
  45. 22 1
      psiphon/common/crypto/ssh/mux.go
  46. 215 1
      psiphon/common/crypto/ssh/mux_test.go
  47. 5 5
      psiphon/common/crypto/ssh/randomized_kex_test.go
  48. 131 4
      psiphon/common/crypto/ssh/server.go
  49. 7 5
      psiphon/common/crypto/ssh/session_test.go
  50. 139 0
      psiphon/common/crypto/ssh/ssh_gss.go
  51. 109 0
      psiphon/common/crypto/ssh/ssh_gss_test.go
  52. 38 913
      psiphon/common/crypto/ssh/terminal/terminal.go
  53. 0 358
      psiphon/common/crypto/ssh/terminal/terminal_test.go
  54. 0 114
      psiphon/common/crypto/ssh/terminal/util.go
  55. 0 12
      psiphon/common/crypto/ssh/terminal/util_bsd.go
  56. 0 10
      psiphon/common/crypto/ssh/terminal/util_linux.go
  57. 0 58
      psiphon/common/crypto/ssh/terminal/util_plan9.go
  58. 0 124
      psiphon/common/crypto/ssh/terminal/util_solaris.go
  59. 0 103
      psiphon/common/crypto/ssh/terminal/util_windows.go
  60. 2 1
      psiphon/common/crypto/ssh/test/agent_unix_test.go
  61. 2 1
      psiphon/common/crypto/ssh/test/banner_test.go
  62. 2 1
      psiphon/common/crypto/ssh/test/cert_test.go
  63. 4 3
      psiphon/common/crypto/ssh/test/dial_unix_test.go
  64. 16 4
      psiphon/common/crypto/ssh/test/forward_unix_test.go
  65. 1 0
      psiphon/common/crypto/ssh/test/multi_auth_test.go
  66. 62 56
      psiphon/common/crypto/ssh/test/session_test.go
  67. 9 2
      psiphon/common/crypto/ssh/test/test_unix_test.go
  68. 87 4
      psiphon/common/crypto/ssh/testdata/keys.go
  69. 6 6
      psiphon/common/crypto/ssh/transport.go
  70. 159 0
      vendor/golang.org/x/crypto/blowfish/block.go
  71. 99 0
      vendor/golang.org/x/crypto/blowfish/cipher.go
  72. 199 0
      vendor/golang.org/x/crypto/blowfish/const.go
  73. 223 0
      vendor/golang.org/x/crypto/ed25519/ed25519.go
  74. 74 0
      vendor/golang.org/x/crypto/ed25519/ed25519_go113.go
  75. 1422 0
      vendor/golang.org/x/crypto/ed25519/internal/edwards25519/const.go
  76. 1793 0
      vendor/golang.org/x/crypto/ed25519/internal/edwards25519/edwards25519.go
  77. 18 0
      vendor/vendor.json

+ 0 - 2
psiphon/common/crypto/.gitignore

@@ -1,2 +0,0 @@
-# Add no patterns to .hgignore except for files generated by the build.
-last-change

+ 40 - 0
psiphon/common/crypto/internal/poly1305/bits_compat.go

@@ -0,0 +1,40 @@
+// Copyright 2019 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.
+
+//go:build !go1.13
+// +build !go1.13
+
+package poly1305
+
+// Generic fallbacks for the math/bits intrinsics, copied from
+// src/math/bits/bits.go. They were added in Go 1.12, but Add64 and Sum64 had
+// variable time fallbacks until Go 1.13.
+
+func bitsAdd64(x, y, carry uint64) (sum, carryOut uint64) {
+	sum = x + y + carry
+	carryOut = ((x & y) | ((x | y) &^ sum)) >> 63
+	return
+}
+
+func bitsSub64(x, y, borrow uint64) (diff, borrowOut uint64) {
+	diff = x - y - borrow
+	borrowOut = ((^x & y) | (^(x ^ y) & diff)) >> 63
+	return
+}
+
+func bitsMul64(x, y uint64) (hi, lo uint64) {
+	const mask32 = 1<<32 - 1
+	x0 := x & mask32
+	x1 := x >> 32
+	y0 := y & mask32
+	y1 := y >> 32
+	w0 := x0 * y0
+	t := x1*y0 + w0>>32
+	w1 := t & mask32
+	w2 := t >> 32
+	w1 += x0 * y1
+	hi = x1*y1 + w2 + w1>>32
+	lo = x * y
+	return
+}

+ 22 - 0
psiphon/common/crypto/internal/poly1305/bits_go1.13.go

@@ -0,0 +1,22 @@
+// Copyright 2019 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.
+
+//go:build go1.13
+// +build go1.13
+
+package poly1305
+
+import "math/bits"
+
+func bitsAdd64(x, y, carry uint64) (sum, carryOut uint64) {
+	return bits.Add64(x, y, carry)
+}
+
+func bitsSub64(x, y, borrow uint64) (diff, borrowOut uint64) {
+	return bits.Sub64(x, y, borrow)
+}
+
+func bitsMul64(x, y uint64) (hi, lo uint64) {
+	return bits.Mul64(x, y)
+}

+ 10 - 0
psiphon/common/crypto/internal/poly1305/mac_noasm.go

@@ -0,0 +1,10 @@
+// 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.
+
+//go:build (!amd64 && !ppc64le && !s390x) || !gc || purego
+// +build !amd64,!ppc64le,!s390x !gc purego
+
+package poly1305
+
+type mac struct{ macGeneric }

+ 99 - 0
psiphon/common/crypto/internal/poly1305/poly1305.go

@@ -0,0 +1,99 @@
+// Copyright 2012 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 poly1305 implements Poly1305 one-time message authentication code as
+// specified in https://cr.yp.to/mac/poly1305-20050329.pdf.
+//
+// Poly1305 is a fast, one-time authentication function. It is infeasible for an
+// attacker to generate an authenticator for a message without the key. However, a
+// key must only be used for a single message. Authenticating two different
+// messages with the same key allows an attacker to forge authenticators for other
+// messages with the same key.
+//
+// Poly1305 was originally coupled with AES in order to make Poly1305-AES. AES was
+// used with a fixed key in order to generate one-time keys from an nonce.
+// However, in this package AES isn't used and the one-time key is specified
+// directly.
+package poly1305
+
+import "crypto/subtle"
+
+// TagSize is the size, in bytes, of a poly1305 authenticator.
+const TagSize = 16
+
+// 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 *[16]byte, m []byte, key *[32]byte) {
+	h := New(key)
+	h.Write(m)
+	h.Sum(out[:0])
+}
+
+// Verify returns true if mac is a valid authenticator for m with the given key.
+func Verify(mac *[16]byte, m []byte, key *[32]byte) bool {
+	var tmp [16]byte
+	Sum(&tmp, m, key)
+	return subtle.ConstantTimeCompare(tmp[:], mac[:]) == 1
+}
+
+// New returns a new MAC computing an authentication
+// tag of all data written to it with the given key.
+// This allows writing the message progressively instead
+// of passing it as a single slice. Common users should use
+// the Sum function instead.
+//
+// The key must be unique for each message, as authenticating
+// two different messages with the same key allows an attacker
+// to forge messages at will.
+func New(key *[32]byte) *MAC {
+	m := &MAC{}
+	initialize(key, &m.macState)
+	return m
+}
+
+// MAC is an io.Writer computing an authentication tag
+// of the data written to it.
+//
+// MAC cannot be used like common hash.Hash implementations,
+// because using a poly1305 key twice breaks its security.
+// Therefore writing data to a running MAC after calling
+// Sum or Verify causes it to panic.
+type MAC struct {
+	mac // platform-dependent implementation
+
+	finalized bool
+}
+
+// Size returns the number of bytes Sum will return.
+func (h *MAC) Size() int { return TagSize }
+
+// Write adds more data to the running message authentication code.
+// It never returns an error.
+//
+// It must not be called after the first call of Sum or Verify.
+func (h *MAC) Write(p []byte) (n int, err error) {
+	if h.finalized {
+		panic("poly1305: write to MAC after Sum or Verify")
+	}
+	return h.mac.Write(p)
+}
+
+// Sum computes the authenticator of all data written to the
+// message authentication code.
+func (h *MAC) Sum(b []byte) []byte {
+	var mac [TagSize]byte
+	h.mac.Sum(&mac)
+	h.finalized = true
+	return append(b, mac[:]...)
+}
+
+// Verify returns whether the authenticator of all data written to
+// the message authentication code matches the expected value.
+func (h *MAC) Verify(expected []byte) bool {
+	var mac [TagSize]byte
+	h.mac.Sum(&mac)
+	h.finalized = true
+	return subtle.ConstantTimeCompare(expected, mac[:]) == 1
+}

+ 276 - 0
psiphon/common/crypto/internal/poly1305/poly1305_test.go

@@ -0,0 +1,276 @@
+// Copyright 2012 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 poly1305
+
+import (
+	"crypto/rand"
+	"encoding/binary"
+	"encoding/hex"
+	"flag"
+	"testing"
+	"unsafe"
+)
+
+var stressFlag = flag.Bool("stress", false, "run slow stress tests")
+
+type test struct {
+	in    string
+	key   string
+	tag   string
+	state string
+}
+
+func (t *test) Input() []byte {
+	in, err := hex.DecodeString(t.in)
+	if err != nil {
+		panic(err)
+	}
+	return in
+}
+
+func (t *test) Key() [32]byte {
+	buf, err := hex.DecodeString(t.key)
+	if err != nil {
+		panic(err)
+	}
+	var key [32]byte
+	copy(key[:], buf[:32])
+	return key
+}
+
+func (t *test) Tag() [16]byte {
+	buf, err := hex.DecodeString(t.tag)
+	if err != nil {
+		panic(err)
+	}
+	var tag [16]byte
+	copy(tag[:], buf[:16])
+	return tag
+}
+
+func (t *test) InitialState() [3]uint64 {
+	// state is hex encoded in big-endian byte order
+	if t.state == "" {
+		return [3]uint64{0, 0, 0}
+	}
+	buf, err := hex.DecodeString(t.state)
+	if err != nil {
+		panic(err)
+	}
+	if len(buf) != 3*8 {
+		panic("incorrect state length")
+	}
+	return [3]uint64{
+		binary.BigEndian.Uint64(buf[16:24]),
+		binary.BigEndian.Uint64(buf[8:16]),
+		binary.BigEndian.Uint64(buf[0:8]),
+	}
+}
+
+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 {
+		// cannot set initial state before calling sum, so skip those tests
+		if v.InitialState() != [3]uint64{0, 0, 0} {
+			continue
+		}
+
+		in := v.Input()
+		if unaligned {
+			in = unalignBytes(in)
+		}
+		key := v.Key()
+		sumImpl(&tag, in, &key)
+		if tag != v.Tag() {
+			t.Errorf("%d: expected %x, got %x", i, v.Tag(), tag[:])
+		}
+		if !Verify(&tag, in, &key) {
+			t.Errorf("%d: tag didn't verify", i)
+		}
+		// If the key is zero, the tag will always be zero, independent of the input.
+		if len(in) > 0 && key != [32]byte{} {
+			in[0] ^= 0xff
+			if Verify(&tag, in, &key) {
+				t.Errorf("%d: tag verified after altering the input", i)
+			}
+			in[0] ^= 0xff
+		}
+		// If the input is empty, the tag only depends on the second half of the key.
+		if len(in) > 0 {
+			key[0] ^= 0xff
+			if Verify(&tag, in, &key) {
+				t.Errorf("%d: tag verified after altering the key", i)
+			}
+			key[0] ^= 0xff
+		}
+		tag[0] ^= 0xff
+		if Verify(&tag, in, &key) {
+			t.Errorf("%d: tag verified after altering the tag", i)
+		}
+		tag[0] ^= 0xff
+	}
+}
+
+func TestBurnin(t *testing.T) {
+	// This test can be used to sanity-check significant changes. It can
+	// take about many minutes to run, even on fast machines. It's disabled
+	// by default.
+	if !*stressFlag {
+		t.Skip("skipping without -stress")
+	}
+
+	var key [32]byte
+	var input [25]byte
+	var output [16]byte
+
+	for i := range key {
+		key[i] = 1
+	}
+	for i := range input {
+		input[i] = 2
+	}
+
+	for i := uint64(0); i < 1e10; i++ {
+		Sum(&output, input[:], &key)
+		copy(key[0:], output[:])
+		copy(key[16:], output[:])
+		copy(input[:], output[:])
+		copy(input[16:], output[:])
+	}
+
+	const expected = "5e3b866aea0b636d240c83c428f84bfa"
+	if got := hex.EncodeToString(output[:]); got != expected {
+		t.Errorf("expected %s, got %s", expected, got)
+	}
+}
+
+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 TestWriteGeneric(t *testing.T)          { testWriteGeneric(t, false) }
+func TestWriteGenericUnaligned(t *testing.T) { testWriteGeneric(t, true) }
+func TestWrite(t *testing.T)                 { testWrite(t, false) }
+func TestWriteUnaligned(t *testing.T)        { testWrite(t, true) }
+
+func testWriteGeneric(t *testing.T, unaligned bool) {
+	for i, v := range testData {
+		key := v.Key()
+		input := v.Input()
+		var out [16]byte
+
+		if unaligned {
+			input = unalignBytes(input)
+		}
+		h := newMACGeneric(&key)
+		if s := v.InitialState(); s != [3]uint64{0, 0, 0} {
+			h.macState.h = s
+		}
+		n, err := h.Write(input[:len(input)/3])
+		if err != nil || n != len(input[:len(input)/3]) {
+			t.Errorf("#%d: unexpected Write results: n = %d, err = %v", i, n, err)
+		}
+		n, err = h.Write(input[len(input)/3:])
+		if err != nil || n != len(input[len(input)/3:]) {
+			t.Errorf("#%d: unexpected Write results: n = %d, err = %v", i, n, err)
+		}
+		h.Sum(&out)
+		if tag := v.Tag(); out != tag {
+			t.Errorf("%d: expected %x, got %x", i, tag[:], out[:])
+		}
+	}
+}
+
+func testWrite(t *testing.T, unaligned bool) {
+	for i, v := range testData {
+		key := v.Key()
+		input := v.Input()
+		var out [16]byte
+
+		if unaligned {
+			input = unalignBytes(input)
+		}
+		h := New(&key)
+		if s := v.InitialState(); s != [3]uint64{0, 0, 0} {
+			h.macState.h = s
+		}
+		n, err := h.Write(input[:len(input)/3])
+		if err != nil || n != len(input[:len(input)/3]) {
+			t.Errorf("#%d: unexpected Write results: n = %d, err = %v", i, n, err)
+		}
+		n, err = h.Write(input[len(input)/3:])
+		if err != nil || n != len(input[len(input)/3:]) {
+			t.Errorf("#%d: unexpected Write results: n = %d, err = %v", i, n, err)
+		}
+		h.Sum(out[:0])
+		tag := v.Tag()
+		if out != tag {
+			t.Errorf("%d: expected %x, got %x", i, tag[:], out[:])
+		}
+		if !h.Verify(tag[:]) {
+			t.Errorf("%d: Verify failed", i)
+		}
+		tag[0] ^= 0xff
+		if h.Verify(tag[:]) {
+			t.Errorf("%d: Verify succeeded after modifying the tag", i)
+		}
+	}
+}
+
+func benchmarkSum(b *testing.B, size int, unaligned bool) {
+	var out [16]byte
+	var key [32]byte
+	in := make([]byte, size)
+	if unaligned {
+		in = unalignBytes(in)
+	}
+	rand.Read(in)
+	b.SetBytes(int64(len(in)))
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		Sum(&out, in, &key)
+	}
+}
+
+func benchmarkWrite(b *testing.B, size int, unaligned bool) {
+	var key [32]byte
+	h := New(&key)
+	in := make([]byte, size)
+	if unaligned {
+		in = unalignBytes(in)
+	}
+	rand.Read(in)
+	b.SetBytes(int64(len(in)))
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		h.Write(in)
+	}
+}
+
+func Benchmark64(b *testing.B)          { benchmarkSum(b, 64, false) }
+func Benchmark1K(b *testing.B)          { benchmarkSum(b, 1024, false) }
+func Benchmark2M(b *testing.B)          { benchmarkSum(b, 2*1024*1024, false) }
+func Benchmark64Unaligned(b *testing.B) { benchmarkSum(b, 64, true) }
+func Benchmark1KUnaligned(b *testing.B) { benchmarkSum(b, 1024, true) }
+func Benchmark2MUnaligned(b *testing.B) { benchmarkSum(b, 2*1024*1024, true) }
+
+func BenchmarkWrite64(b *testing.B)          { benchmarkWrite(b, 64, false) }
+func BenchmarkWrite1K(b *testing.B)          { benchmarkWrite(b, 1024, false) }
+func BenchmarkWrite2M(b *testing.B)          { benchmarkWrite(b, 2*1024*1024, false) }
+func BenchmarkWrite64Unaligned(b *testing.B) { benchmarkWrite(b, 64, true) }
+func BenchmarkWrite1KUnaligned(b *testing.B) { benchmarkWrite(b, 1024, true) }
+func BenchmarkWrite2MUnaligned(b *testing.B) { benchmarkWrite(b, 2*1024*1024, true) }
+
+func unalignBytes(in []byte) []byte {
+	out := make([]byte, len(in)+1)
+	if uintptr(unsafe.Pointer(&out[0]))&(unsafe.Alignof(uint32(0))-1) == 0 {
+		out = out[1:]
+	} else {
+		out = out[:len(in)]
+	}
+	copy(out, in)
+	return out
+}

+ 48 - 0
psiphon/common/crypto/internal/poly1305/sum_amd64.go

@@ -0,0 +1,48 @@
+// Copyright 2012 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.
+
+//go:build gc && !purego
+// +build gc,!purego
+
+package poly1305
+
+//go:noescape
+func update(state *macState, msg []byte)
+
+// mac is a wrapper for macGeneric that redirects calls that would have gone to
+// updateGeneric to update.
+//
+// Its Write and Sum methods are otherwise identical to the macGeneric ones, but
+// using function pointers would carry a major performance cost.
+type mac struct{ macGeneric }
+
+func (h *mac) Write(p []byte) (int, error) {
+	nn := len(p)
+	if h.offset > 0 {
+		n := copy(h.buffer[h.offset:], p)
+		if h.offset+n < TagSize {
+			h.offset += n
+			return nn, nil
+		}
+		p = p[n:]
+		h.offset = 0
+		update(&h.macState, h.buffer[:])
+	}
+	if n := len(p) - (len(p) % TagSize); n > 0 {
+		update(&h.macState, p[:n])
+		p = p[n:]
+	}
+	if len(p) > 0 {
+		h.offset += copy(h.buffer[h.offset:], p)
+	}
+	return nn, nil
+}
+
+func (h *mac) Sum(out *[16]byte) {
+	state := h.macState
+	if h.offset > 0 {
+		update(&state, h.buffer[:h.offset])
+	}
+	finalize(out, &state.h, &state.s)
+}

+ 109 - 0
psiphon/common/crypto/internal/poly1305/sum_amd64.s

@@ -0,0 +1,109 @@
+// Copyright 2012 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.
+
+//go:build gc && !purego
+// +build gc,!purego
+
+#include "textflag.h"
+
+#define POLY1305_ADD(msg, h0, h1, h2) \
+	ADDQ 0(msg), h0;  \
+	ADCQ 8(msg), h1;  \
+	ADCQ $1, h2;      \
+	LEAQ 16(msg), msg
+
+#define POLY1305_MUL(h0, h1, h2, r0, r1, t0, t1, t2, t3) \
+	MOVQ  r0, AX;                  \
+	MULQ  h0;                      \
+	MOVQ  AX, t0;                  \
+	MOVQ  DX, t1;                  \
+	MOVQ  r0, AX;                  \
+	MULQ  h1;                      \
+	ADDQ  AX, t1;                  \
+	ADCQ  $0, DX;                  \
+	MOVQ  r0, t2;                  \
+	IMULQ h2, t2;                  \
+	ADDQ  DX, t2;                  \
+	                               \
+	MOVQ  r1, AX;                  \
+	MULQ  h0;                      \
+	ADDQ  AX, t1;                  \
+	ADCQ  $0, DX;                  \
+	MOVQ  DX, h0;                  \
+	MOVQ  r1, t3;                  \
+	IMULQ h2, t3;                  \
+	MOVQ  r1, AX;                  \
+	MULQ  h1;                      \
+	ADDQ  AX, t2;                  \
+	ADCQ  DX, t3;                  \
+	ADDQ  h0, t2;                  \
+	ADCQ  $0, t3;                  \
+	                               \
+	MOVQ  t0, h0;                  \
+	MOVQ  t1, h1;                  \
+	MOVQ  t2, h2;                  \
+	ANDQ  $3, h2;                  \
+	MOVQ  t2, t0;                  \
+	ANDQ  $0xFFFFFFFFFFFFFFFC, t0; \
+	ADDQ  t0, h0;                  \
+	ADCQ  t3, h1;                  \
+	ADCQ  $0, h2;                  \
+	SHRQ  $2, t3, t2;              \
+	SHRQ  $2, t3;                  \
+	ADDQ  t2, h0;                  \
+	ADCQ  t3, h1;                  \
+	ADCQ  $0, h2
+
+// func update(state *[7]uint64, msg []byte)
+TEXT ·update(SB), $0-32
+	MOVQ state+0(FP), DI
+	MOVQ msg_base+8(FP), SI
+	MOVQ msg_len+16(FP), R15
+
+	MOVQ 0(DI), R8   // h0
+	MOVQ 8(DI), R9   // h1
+	MOVQ 16(DI), R10 // h2
+	MOVQ 24(DI), R11 // r0
+	MOVQ 32(DI), R12 // r1
+
+	CMPQ R15, $16
+	JB   bytes_between_0_and_15
+
+loop:
+	POLY1305_ADD(SI, R8, R9, R10)
+
+multiply:
+	POLY1305_MUL(R8, R9, R10, R11, R12, BX, CX, R13, R14)
+	SUBQ $16, R15
+	CMPQ R15, $16
+	JAE  loop
+
+bytes_between_0_and_15:
+	TESTQ R15, R15
+	JZ    done
+	MOVQ  $1, BX
+	XORQ  CX, CX
+	XORQ  R13, R13
+	ADDQ  R15, SI
+
+flush_buffer:
+	SHLQ $8, BX, CX
+	SHLQ $8, BX
+	MOVB -1(SI), R13
+	XORQ R13, BX
+	DECQ SI
+	DECQ R15
+	JNZ  flush_buffer
+
+	ADDQ BX, R8
+	ADCQ CX, R9
+	ADCQ $0, R10
+	MOVQ $16, R15
+	JMP  multiply
+
+done:
+	MOVQ R8, 0(DI)
+	MOVQ R9, 8(DI)
+	MOVQ R10, 16(DI)
+	RET

+ 310 - 0
psiphon/common/crypto/internal/poly1305/sum_generic.go

@@ -0,0 +1,310 @@
+// 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.
+
+// This file provides the generic implementation of Sum and MAC. Other files
+// might provide optimized assembly implementations of some of this code.
+
+package poly1305
+
+import "encoding/binary"
+
+// Poly1305 [RFC 7539] is a relatively simple algorithm: the authentication tag
+// for a 64 bytes message is approximately
+//
+//     s + m[0:16] * r⁴ + m[16:32] * r³ + m[32:48] * r² + m[48:64] * r  mod  2¹³⁰ - 5
+//
+// for some secret r and s. It can be computed sequentially like
+//
+//     for len(msg) > 0:
+//         h += read(msg, 16)
+//         h *= r
+//         h %= 2¹³⁰ - 5
+//     return h + s
+//
+// All the complexity is about doing performant constant-time math on numbers
+// larger than any available numeric type.
+
+func sumGeneric(out *[TagSize]byte, msg []byte, key *[32]byte) {
+	h := newMACGeneric(key)
+	h.Write(msg)
+	h.Sum(out)
+}
+
+func newMACGeneric(key *[32]byte) macGeneric {
+	m := macGeneric{}
+	initialize(key, &m.macState)
+	return m
+}
+
+// macState holds numbers in saturated 64-bit little-endian limbs. That is,
+// the value of [x0, x1, x2] is x[0] + x[1] * 2⁶⁴ + x[2] * 2¹²⁸.
+type macState struct {
+	// h is the main accumulator. It is to be interpreted modulo 2¹³⁰ - 5, but
+	// can grow larger during and after rounds. It must, however, remain below
+	// 2 * (2¹³⁰ - 5).
+	h [3]uint64
+	// r and s are the private key components.
+	r [2]uint64
+	s [2]uint64
+}
+
+type macGeneric struct {
+	macState
+
+	buffer [TagSize]byte
+	offset int
+}
+
+// Write splits the incoming message into TagSize chunks, and passes them to
+// update. It buffers incomplete chunks.
+func (h *macGeneric) Write(p []byte) (int, error) {
+	nn := len(p)
+	if h.offset > 0 {
+		n := copy(h.buffer[h.offset:], p)
+		if h.offset+n < TagSize {
+			h.offset += n
+			return nn, nil
+		}
+		p = p[n:]
+		h.offset = 0
+		updateGeneric(&h.macState, h.buffer[:])
+	}
+	if n := len(p) - (len(p) % TagSize); n > 0 {
+		updateGeneric(&h.macState, p[:n])
+		p = p[n:]
+	}
+	if len(p) > 0 {
+		h.offset += copy(h.buffer[h.offset:], p)
+	}
+	return nn, nil
+}
+
+// Sum flushes the last incomplete chunk from the buffer, if any, and generates
+// the MAC output. It does not modify its state, in order to allow for multiple
+// calls to Sum, even if no Write is allowed after Sum.
+func (h *macGeneric) Sum(out *[TagSize]byte) {
+	state := h.macState
+	if h.offset > 0 {
+		updateGeneric(&state, h.buffer[:h.offset])
+	}
+	finalize(out, &state.h, &state.s)
+}
+
+// [rMask0, rMask1] is the specified Poly1305 clamping mask in little-endian. It
+// clears some bits of the secret coefficient to make it possible to implement
+// multiplication more efficiently.
+const (
+	rMask0 = 0x0FFFFFFC0FFFFFFF
+	rMask1 = 0x0FFFFFFC0FFFFFFC
+)
+
+// initialize loads the 256-bit key into the two 128-bit secret values r and s.
+func initialize(key *[32]byte, m *macState) {
+	m.r[0] = binary.LittleEndian.Uint64(key[0:8]) & rMask0
+	m.r[1] = binary.LittleEndian.Uint64(key[8:16]) & rMask1
+	m.s[0] = binary.LittleEndian.Uint64(key[16:24])
+	m.s[1] = binary.LittleEndian.Uint64(key[24:32])
+}
+
+// uint128 holds a 128-bit number as two 64-bit limbs, for use with the
+// bits.Mul64 and bits.Add64 intrinsics.
+type uint128 struct {
+	lo, hi uint64
+}
+
+func mul64(a, b uint64) uint128 {
+	hi, lo := bitsMul64(a, b)
+	return uint128{lo, hi}
+}
+
+func add128(a, b uint128) uint128 {
+	lo, c := bitsAdd64(a.lo, b.lo, 0)
+	hi, c := bitsAdd64(a.hi, b.hi, c)
+	if c != 0 {
+		panic("poly1305: unexpected overflow")
+	}
+	return uint128{lo, hi}
+}
+
+func shiftRightBy2(a uint128) uint128 {
+	a.lo = a.lo>>2 | (a.hi&3)<<62
+	a.hi = a.hi >> 2
+	return a
+}
+
+// updateGeneric absorbs msg into the state.h accumulator. For each chunk m of
+// 128 bits of message, it computes
+//
+//     h₊ = (h + m) * r  mod  2¹³⁰ - 5
+//
+// If the msg length is not a multiple of TagSize, it assumes the last
+// incomplete chunk is the final one.
+func updateGeneric(state *macState, msg []byte) {
+	h0, h1, h2 := state.h[0], state.h[1], state.h[2]
+	r0, r1 := state.r[0], state.r[1]
+
+	for len(msg) > 0 {
+		var c uint64
+
+		// For the first step, h + m, we use a chain of bits.Add64 intrinsics.
+		// The resulting value of h might exceed 2¹³⁰ - 5, but will be partially
+		// reduced at the end of the multiplication below.
+		//
+		// The spec requires us to set a bit just above the message size, not to
+		// hide leading zeroes. For full chunks, that's 1 << 128, so we can just
+		// add 1 to the most significant (2¹²⁸) limb, h2.
+		if len(msg) >= TagSize {
+			h0, c = bitsAdd64(h0, binary.LittleEndian.Uint64(msg[0:8]), 0)
+			h1, c = bitsAdd64(h1, binary.LittleEndian.Uint64(msg[8:16]), c)
+			h2 += c + 1
+
+			msg = msg[TagSize:]
+		} else {
+			var buf [TagSize]byte
+			copy(buf[:], msg)
+			buf[len(msg)] = 1
+
+			h0, c = bitsAdd64(h0, binary.LittleEndian.Uint64(buf[0:8]), 0)
+			h1, c = bitsAdd64(h1, binary.LittleEndian.Uint64(buf[8:16]), c)
+			h2 += c
+
+			msg = nil
+		}
+
+		// Multiplication of big number limbs is similar to elementary school
+		// columnar multiplication. Instead of digits, there are 64-bit limbs.
+		//
+		// We are multiplying a 3 limbs number, h, by a 2 limbs number, r.
+		//
+		//                        h2    h1    h0  x
+		//                              r1    r0  =
+		//                       ----------------
+		//                      h2r0  h1r0  h0r0     <-- individual 128-bit products
+		//            +   h2r1  h1r1  h0r1
+		//               ------------------------
+		//                 m3    m2    m1    m0      <-- result in 128-bit overlapping limbs
+		//               ------------------------
+		//         m3.hi m2.hi m1.hi m0.hi           <-- carry propagation
+		//     +         m3.lo m2.lo m1.lo m0.lo
+		//        -------------------------------
+		//           t4    t3    t2    t1    t0      <-- final result in 64-bit limbs
+		//
+		// The main difference from pen-and-paper multiplication is that we do
+		// carry propagation in a separate step, as if we wrote two digit sums
+		// at first (the 128-bit limbs), and then carried the tens all at once.
+
+		h0r0 := mul64(h0, r0)
+		h1r0 := mul64(h1, r0)
+		h2r0 := mul64(h2, r0)
+		h0r1 := mul64(h0, r1)
+		h1r1 := mul64(h1, r1)
+		h2r1 := mul64(h2, r1)
+
+		// Since h2 is known to be at most 7 (5 + 1 + 1), and r0 and r1 have their
+		// top 4 bits cleared by rMask{0,1}, we know that their product is not going
+		// to overflow 64 bits, so we can ignore the high part of the products.
+		//
+		// This also means that the product doesn't have a fifth limb (t4).
+		if h2r0.hi != 0 {
+			panic("poly1305: unexpected overflow")
+		}
+		if h2r1.hi != 0 {
+			panic("poly1305: unexpected overflow")
+		}
+
+		m0 := h0r0
+		m1 := add128(h1r0, h0r1) // These two additions don't overflow thanks again
+		m2 := add128(h2r0, h1r1) // to the 4 masked bits at the top of r0 and r1.
+		m3 := h2r1
+
+		t0 := m0.lo
+		t1, c := bitsAdd64(m1.lo, m0.hi, 0)
+		t2, c := bitsAdd64(m2.lo, m1.hi, c)
+		t3, _ := bitsAdd64(m3.lo, m2.hi, c)
+
+		// Now we have the result as 4 64-bit limbs, and we need to reduce it
+		// modulo 2¹³⁰ - 5. The special shape of this Crandall prime lets us do
+		// a cheap partial reduction according to the reduction identity
+		//
+		//     c * 2¹³⁰ + n  =  c * 5 + n  mod  2¹³⁰ - 5
+		//
+		// because 2¹³⁰ = 5 mod 2¹³⁰ - 5. Partial reduction since the result is
+		// likely to be larger than 2¹³⁰ - 5, but still small enough to fit the
+		// assumptions we make about h in the rest of the code.
+		//
+		// See also https://speakerdeck.com/gtank/engineering-prime-numbers?slide=23
+
+		// We split the final result at the 2¹³⁰ mark into h and cc, the carry.
+		// Note that the carry bits are effectively shifted left by 2, in other
+		// words, cc = c * 4 for the c in the reduction identity.
+		h0, h1, h2 = t0, t1, t2&maskLow2Bits
+		cc := uint128{t2 & maskNotLow2Bits, t3}
+
+		// To add c * 5 to h, we first add cc = c * 4, and then add (cc >> 2) = c.
+
+		h0, c = bitsAdd64(h0, cc.lo, 0)
+		h1, c = bitsAdd64(h1, cc.hi, c)
+		h2 += c
+
+		cc = shiftRightBy2(cc)
+
+		h0, c = bitsAdd64(h0, cc.lo, 0)
+		h1, c = bitsAdd64(h1, cc.hi, c)
+		h2 += c
+
+		// h2 is at most 3 + 1 + 1 = 5, making the whole of h at most
+		//
+		//     5 * 2¹²⁸ + (2¹²⁸ - 1) = 6 * 2¹²⁸ - 1
+	}
+
+	state.h[0], state.h[1], state.h[2] = h0, h1, h2
+}
+
+const (
+	maskLow2Bits    uint64 = 0x0000000000000003
+	maskNotLow2Bits uint64 = ^maskLow2Bits
+)
+
+// select64 returns x if v == 1 and y if v == 0, in constant time.
+func select64(v, x, y uint64) uint64 { return ^(v-1)&x | (v-1)&y }
+
+// [p0, p1, p2] is 2¹³⁰ - 5 in little endian order.
+const (
+	p0 = 0xFFFFFFFFFFFFFFFB
+	p1 = 0xFFFFFFFFFFFFFFFF
+	p2 = 0x0000000000000003
+)
+
+// finalize completes the modular reduction of h and computes
+//
+//     out = h + s  mod  2¹²⁸
+//
+func finalize(out *[TagSize]byte, h *[3]uint64, s *[2]uint64) {
+	h0, h1, h2 := h[0], h[1], h[2]
+
+	// After the partial reduction in updateGeneric, h might be more than
+	// 2¹³⁰ - 5, but will be less than 2 * (2¹³⁰ - 5). To complete the reduction
+	// in constant time, we compute t = h - (2¹³⁰ - 5), and select h as the
+	// result if the subtraction underflows, and t otherwise.
+
+	hMinusP0, b := bitsSub64(h0, p0, 0)
+	hMinusP1, b := bitsSub64(h1, p1, b)
+	_, b = bitsSub64(h2, p2, b)
+
+	// h = h if h < p else h - p
+	h0 = select64(b, h0, hMinusP0)
+	h1 = select64(b, h1, hMinusP1)
+
+	// Finally, we compute the last Poly1305 step
+	//
+	//     tag = h + s  mod  2¹²⁸
+	//
+	// by just doing a wide addition with the 128 low bits of h and discarding
+	// the overflow.
+	h0, c := bitsAdd64(h0, s[0], 0)
+	h1, _ = bitsAdd64(h1, s[1], c)
+
+	binary.LittleEndian.PutUint64(out[0:8], h0)
+	binary.LittleEndian.PutUint64(out[8:16], h1)
+}

+ 48 - 0
psiphon/common/crypto/internal/poly1305/sum_ppc64le.go

@@ -0,0 +1,48 @@
+// Copyright 2019 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.
+
+//go:build gc && !purego
+// +build gc,!purego
+
+package poly1305
+
+//go:noescape
+func update(state *macState, msg []byte)
+
+// mac is a wrapper for macGeneric that redirects calls that would have gone to
+// updateGeneric to update.
+//
+// Its Write and Sum methods are otherwise identical to the macGeneric ones, but
+// using function pointers would carry a major performance cost.
+type mac struct{ macGeneric }
+
+func (h *mac) Write(p []byte) (int, error) {
+	nn := len(p)
+	if h.offset > 0 {
+		n := copy(h.buffer[h.offset:], p)
+		if h.offset+n < TagSize {
+			h.offset += n
+			return nn, nil
+		}
+		p = p[n:]
+		h.offset = 0
+		update(&h.macState, h.buffer[:])
+	}
+	if n := len(p) - (len(p) % TagSize); n > 0 {
+		update(&h.macState, p[:n])
+		p = p[n:]
+	}
+	if len(p) > 0 {
+		h.offset += copy(h.buffer[h.offset:], p)
+	}
+	return nn, nil
+}
+
+func (h *mac) Sum(out *[16]byte) {
+	state := h.macState
+	if h.offset > 0 {
+		update(&state, h.buffer[:h.offset])
+	}
+	finalize(out, &state.h, &state.s)
+}

+ 182 - 0
psiphon/common/crypto/internal/poly1305/sum_ppc64le.s

@@ -0,0 +1,182 @@
+// Copyright 2019 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.
+
+//go:build gc && !purego
+// +build gc,!purego
+
+#include "textflag.h"
+
+// This was ported from the amd64 implementation.
+
+#define POLY1305_ADD(msg, h0, h1, h2, t0, t1, t2) \
+	MOVD (msg), t0;  \
+	MOVD 8(msg), t1; \
+	MOVD $1, t2;     \
+	ADDC t0, h0, h0; \
+	ADDE t1, h1, h1; \
+	ADDE t2, h2;     \
+	ADD  $16, msg
+
+#define POLY1305_MUL(h0, h1, h2, r0, r1, t0, t1, t2, t3, t4, t5) \
+	MULLD  r0, h0, t0;  \
+	MULLD  r0, h1, t4;  \
+	MULHDU r0, h0, t1;  \
+	MULHDU r0, h1, t5;  \
+	ADDC   t4, t1, t1;  \
+	MULLD  r0, h2, t2;  \
+	ADDZE  t5;          \
+	MULHDU r1, h0, t4;  \
+	MULLD  r1, h0, h0;  \
+	ADD    t5, t2, t2;  \
+	ADDC   h0, t1, t1;  \
+	MULLD  h2, r1, t3;  \
+	ADDZE  t4, h0;      \
+	MULHDU r1, h1, t5;  \
+	MULLD  r1, h1, t4;  \
+	ADDC   t4, t2, t2;  \
+	ADDE   t5, t3, t3;  \
+	ADDC   h0, t2, t2;  \
+	MOVD   $-4, t4;     \
+	MOVD   t0, h0;      \
+	MOVD   t1, h1;      \
+	ADDZE  t3;          \
+	ANDCC  $3, t2, h2;  \
+	AND    t2, t4, t0;  \
+	ADDC   t0, h0, h0;  \
+	ADDE   t3, h1, h1;  \
+	SLD    $62, t3, t4; \
+	SRD    $2, t2;      \
+	ADDZE  h2;          \
+	OR     t4, t2, t2;  \
+	SRD    $2, t3;      \
+	ADDC   t2, h0, h0;  \
+	ADDE   t3, h1, h1;  \
+	ADDZE  h2
+
+DATA ·poly1305Mask<>+0x00(SB)/8, $0x0FFFFFFC0FFFFFFF
+DATA ·poly1305Mask<>+0x08(SB)/8, $0x0FFFFFFC0FFFFFFC
+GLOBL ·poly1305Mask<>(SB), RODATA, $16
+
+// func update(state *[7]uint64, msg []byte)
+TEXT ·update(SB), $0-32
+	MOVD state+0(FP), R3
+	MOVD msg_base+8(FP), R4
+	MOVD msg_len+16(FP), R5
+
+	MOVD 0(R3), R8   // h0
+	MOVD 8(R3), R9   // h1
+	MOVD 16(R3), R10 // h2
+	MOVD 24(R3), R11 // r0
+	MOVD 32(R3), R12 // r1
+
+	CMP R5, $16
+	BLT bytes_between_0_and_15
+
+loop:
+	POLY1305_ADD(R4, R8, R9, R10, R20, R21, R22)
+
+multiply:
+	POLY1305_MUL(R8, R9, R10, R11, R12, R16, R17, R18, R14, R20, R21)
+	ADD $-16, R5
+	CMP R5, $16
+	BGE loop
+
+bytes_between_0_and_15:
+	CMP  R5, $0
+	BEQ  done
+	MOVD $0, R16 // h0
+	MOVD $0, R17 // h1
+
+flush_buffer:
+	CMP R5, $8
+	BLE just1
+
+	MOVD $8, R21
+	SUB  R21, R5, R21
+
+	// Greater than 8 -- load the rightmost remaining bytes in msg
+	// and put into R17 (h1)
+	MOVD (R4)(R21), R17
+	MOVD $16, R22
+
+	// Find the offset to those bytes
+	SUB R5, R22, R22
+	SLD $3, R22
+
+	// Shift to get only the bytes in msg
+	SRD R22, R17, R17
+
+	// Put 1 at high end
+	MOVD $1, R23
+	SLD  $3, R21
+	SLD  R21, R23, R23
+	OR   R23, R17, R17
+
+	// Remainder is 8
+	MOVD $8, R5
+
+just1:
+	CMP R5, $8
+	BLT less8
+
+	// Exactly 8
+	MOVD (R4), R16
+
+	CMP R17, $0
+
+	// Check if we've already set R17; if not
+	// set 1 to indicate end of msg.
+	BNE  carry
+	MOVD $1, R17
+	BR   carry
+
+less8:
+	MOVD  $0, R16   // h0
+	MOVD  $0, R22   // shift count
+	CMP   R5, $4
+	BLT   less4
+	MOVWZ (R4), R16
+	ADD   $4, R4
+	ADD   $-4, R5
+	MOVD  $32, R22
+
+less4:
+	CMP   R5, $2
+	BLT   less2
+	MOVHZ (R4), R21
+	SLD   R22, R21, R21
+	OR    R16, R21, R16
+	ADD   $16, R22
+	ADD   $-2, R5
+	ADD   $2, R4
+
+less2:
+	CMP   R5, $0
+	BEQ   insert1
+	MOVBZ (R4), R21
+	SLD   R22, R21, R21
+	OR    R16, R21, R16
+	ADD   $8, R22
+
+insert1:
+	// Insert 1 at end of msg
+	MOVD $1, R21
+	SLD  R22, R21, R21
+	OR   R16, R21, R16
+
+carry:
+	// Add new values to h0, h1, h2
+	ADDC  R16, R8
+	ADDE  R17, R9
+	ADDZE R10, R10
+	MOVD  $16, R5
+	ADD   R5, R4
+	BR    multiply
+
+done:
+	// Save h0, h1, h2 in state
+	MOVD R8, 0(R3)
+	MOVD R9, 8(R3)
+	MOVD R10, 16(R3)
+	RET

+ 76 - 0
psiphon/common/crypto/internal/poly1305/sum_s390x.go

@@ -0,0 +1,76 @@
+// 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.
+
+//go:build gc && !purego
+// +build gc,!purego
+
+package poly1305
+
+import (
+	"golang.org/x/sys/cpu"
+)
+
+// updateVX is an assembly implementation of Poly1305 that uses vector
+// instructions. It must only be called if the vector facility (vx) is
+// available.
+//go:noescape
+func updateVX(state *macState, msg []byte)
+
+// mac is a replacement for macGeneric that uses a larger buffer and redirects
+// calls that would have gone to updateGeneric to updateVX if the vector
+// facility is installed.
+//
+// A larger buffer is required for good performance because the vector
+// implementation has a higher fixed cost per call than the generic
+// implementation.
+type mac struct {
+	macState
+
+	buffer [16 * TagSize]byte // size must be a multiple of block size (16)
+	offset int
+}
+
+func (h *mac) Write(p []byte) (int, error) {
+	nn := len(p)
+	if h.offset > 0 {
+		n := copy(h.buffer[h.offset:], p)
+		if h.offset+n < len(h.buffer) {
+			h.offset += n
+			return nn, nil
+		}
+		p = p[n:]
+		h.offset = 0
+		if cpu.S390X.HasVX {
+			updateVX(&h.macState, h.buffer[:])
+		} else {
+			updateGeneric(&h.macState, h.buffer[:])
+		}
+	}
+
+	tail := len(p) % len(h.buffer) // number of bytes to copy into buffer
+	body := len(p) - tail          // number of bytes to process now
+	if body > 0 {
+		if cpu.S390X.HasVX {
+			updateVX(&h.macState, p[:body])
+		} else {
+			updateGeneric(&h.macState, p[:body])
+		}
+	}
+	h.offset = copy(h.buffer[:], p[body:]) // copy tail bytes - can be 0
+	return nn, nil
+}
+
+func (h *mac) Sum(out *[TagSize]byte) {
+	state := h.macState
+	remainder := h.buffer[:h.offset]
+
+	// Use the generic implementation if we have 2 or fewer blocks left
+	// to sum. The vector implementation has a higher startup time.
+	if cpu.S390X.HasVX && len(remainder) > 2*TagSize {
+		updateVX(&state, remainder)
+	} else if len(remainder) > 0 {
+		updateGeneric(&state, remainder)
+	}
+	finalize(out, &state.h, &state.s)
+}

+ 504 - 0
psiphon/common/crypto/internal/poly1305/sum_s390x.s

@@ -0,0 +1,504 @@
+// 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.
+
+//go:build gc && !purego
+// +build gc,!purego
+
+#include "textflag.h"
+
+// This implementation of Poly1305 uses the vector facility (vx)
+// to process up to 2 blocks (32 bytes) per iteration using an
+// algorithm based on the one described in:
+//
+// NEON crypto, Daniel J. Bernstein & Peter Schwabe
+// https://cryptojedi.org/papers/neoncrypto-20120320.pdf
+//
+// This algorithm uses 5 26-bit limbs to represent a 130-bit
+// value. These limbs are, for the most part, zero extended and
+// placed into 64-bit vector register elements. Each vector
+// register is 128-bits wide and so holds 2 of these elements.
+// Using 26-bit limbs allows us plenty of headroom to accommodate
+// accumulations before and after multiplication without
+// overflowing either 32-bits (before multiplication) or 64-bits
+// (after multiplication).
+//
+// In order to parallelise the operations required to calculate
+// the sum we use two separate accumulators and then sum those
+// in an extra final step. For compatibility with the generic
+// implementation we perform this summation at the end of every
+// updateVX call.
+//
+// To use two accumulators we must multiply the message blocks
+// by r² rather than r. Only the final message block should be
+// multiplied by r.
+//
+// Example:
+//
+// We want to calculate the sum (h) for a 64 byte message (m):
+//
+//   h = m[0:16]r⁴ + m[16:32]r³ + m[32:48]r² + m[48:64]r
+//
+// To do this we split the calculation into the even indices
+// and odd indices of the message. These form our SIMD 'lanes':
+//
+//   h = m[ 0:16]r⁴ + m[32:48]r² +   <- lane 0
+//       m[16:32]r³ + m[48:64]r      <- lane 1
+//
+// To calculate this iteratively we refactor so that both lanes
+// are written in terms of r² and r:
+//
+//   h = (m[ 0:16]r² + m[32:48])r² + <- lane 0
+//       (m[16:32]r² + m[48:64])r    <- lane 1
+//                ^             ^
+//                |             coefficients for second iteration
+//                coefficients for first iteration
+//
+// So in this case we would have two iterations. In the first
+// both lanes are multiplied by r². In the second only the
+// first lane is multiplied by r² and the second lane is
+// instead multiplied by r. This gives use the odd and even
+// powers of r that we need from the original equation.
+//
+// Notation:
+//
+//   h - accumulator
+//   r - key
+//   m - message
+//
+//   [a, b]       - SIMD register holding two 64-bit values
+//   [a, b, c, d] - SIMD register holding four 32-bit values
+//   xᵢ[n]        - limb n of variable x with bit width i
+//
+// Limbs are expressed in little endian order, so for 26-bit
+// limbs x₂₆[4] will be the most significant limb and x₂₆[0]
+// will be the least significant limb.
+
+// masking constants
+#define MOD24 V0 // [0x0000000000ffffff, 0x0000000000ffffff] - mask low 24-bits
+#define MOD26 V1 // [0x0000000003ffffff, 0x0000000003ffffff] - mask low 26-bits
+
+// expansion constants (see EXPAND macro)
+#define EX0 V2
+#define EX1 V3
+#define EX2 V4
+
+// key (r², r or 1 depending on context)
+#define R_0 V5
+#define R_1 V6
+#define R_2 V7
+#define R_3 V8
+#define R_4 V9
+
+// precalculated coefficients (5r², 5r or 0 depending on context)
+#define R5_1 V10
+#define R5_2 V11
+#define R5_3 V12
+#define R5_4 V13
+
+// message block (m)
+#define M_0 V14
+#define M_1 V15
+#define M_2 V16
+#define M_3 V17
+#define M_4 V18
+
+// accumulator (h)
+#define H_0 V19
+#define H_1 V20
+#define H_2 V21
+#define H_3 V22
+#define H_4 V23
+
+// temporary registers (for short-lived values)
+#define T_0 V24
+#define T_1 V25
+#define T_2 V26
+#define T_3 V27
+#define T_4 V28
+
+GLOBL ·constants<>(SB), RODATA, $0x30
+// EX0
+DATA ·constants<>+0x00(SB)/8, $0x0006050403020100
+DATA ·constants<>+0x08(SB)/8, $0x1016151413121110
+// EX1
+DATA ·constants<>+0x10(SB)/8, $0x060c0b0a09080706
+DATA ·constants<>+0x18(SB)/8, $0x161c1b1a19181716
+// EX2
+DATA ·constants<>+0x20(SB)/8, $0x0d0d0d0d0d0f0e0d
+DATA ·constants<>+0x28(SB)/8, $0x1d1d1d1d1d1f1e1d
+
+// MULTIPLY multiplies each lane of f and g, partially reduced
+// modulo 2¹³⁰ - 5. The result, h, consists of partial products
+// in each lane that need to be reduced further to produce the
+// final result.
+//
+//   h₁₃₀ = (f₁₃₀g₁₃₀) % 2¹³⁰ + (5f₁₃₀g₁₃₀) / 2¹³⁰
+//
+// Note that the multiplication by 5 of the high bits is
+// achieved by precalculating the multiplication of four of the
+// g coefficients by 5. These are g51-g54.
+#define MULTIPLY(f0, f1, f2, f3, f4, g0, g1, g2, g3, g4, g51, g52, g53, g54, h0, h1, h2, h3, h4) \
+	VMLOF  f0, g0, h0        \
+	VMLOF  f0, g3, h3        \
+	VMLOF  f0, g1, h1        \
+	VMLOF  f0, g4, h4        \
+	VMLOF  f0, g2, h2        \
+	VMLOF  f1, g54, T_0      \
+	VMLOF  f1, g2, T_3       \
+	VMLOF  f1, g0, T_1       \
+	VMLOF  f1, g3, T_4       \
+	VMLOF  f1, g1, T_2       \
+	VMALOF f2, g53, h0, h0   \
+	VMALOF f2, g1, h3, h3    \
+	VMALOF f2, g54, h1, h1   \
+	VMALOF f2, g2, h4, h4    \
+	VMALOF f2, g0, h2, h2    \
+	VMALOF f3, g52, T_0, T_0 \
+	VMALOF f3, g0, T_3, T_3  \
+	VMALOF f3, g53, T_1, T_1 \
+	VMALOF f3, g1, T_4, T_4  \
+	VMALOF f3, g54, T_2, T_2 \
+	VMALOF f4, g51, h0, h0   \
+	VMALOF f4, g54, h3, h3   \
+	VMALOF f4, g52, h1, h1   \
+	VMALOF f4, g0, h4, h4    \
+	VMALOF f4, g53, h2, h2   \
+	VAG    T_0, h0, h0       \
+	VAG    T_3, h3, h3       \
+	VAG    T_1, h1, h1       \
+	VAG    T_4, h4, h4       \
+	VAG    T_2, h2, h2
+
+// REDUCE performs the following carry operations in four
+// stages, as specified in Bernstein & Schwabe:
+//
+//   1: h₂₆[0]->h₂₆[1] h₂₆[3]->h₂₆[4]
+//   2: h₂₆[1]->h₂₆[2] h₂₆[4]->h₂₆[0]
+//   3: h₂₆[0]->h₂₆[1] h₂₆[2]->h₂₆[3]
+//   4: h₂₆[3]->h₂₆[4]
+//
+// The result is that all of the limbs are limited to 26-bits
+// except for h₂₆[1] and h₂₆[4] which are limited to 27-bits.
+//
+// Note that although each limb is aligned at 26-bit intervals
+// they may contain values that exceed 2²⁶ - 1, hence the need
+// to carry the excess bits in each limb.
+#define REDUCE(h0, h1, h2, h3, h4) \
+	VESRLG $26, h0, T_0  \
+	VESRLG $26, h3, T_1  \
+	VN     MOD26, h0, h0 \
+	VN     MOD26, h3, h3 \
+	VAG    T_0, h1, h1   \
+	VAG    T_1, h4, h4   \
+	VESRLG $26, h1, T_2  \
+	VESRLG $26, h4, T_3  \
+	VN     MOD26, h1, h1 \
+	VN     MOD26, h4, h4 \
+	VESLG  $2, T_3, T_4  \
+	VAG    T_3, T_4, T_4 \
+	VAG    T_2, h2, h2   \
+	VAG    T_4, h0, h0   \
+	VESRLG $26, h2, T_0  \
+	VESRLG $26, h0, T_1  \
+	VN     MOD26, h2, h2 \
+	VN     MOD26, h0, h0 \
+	VAG    T_0, h3, h3   \
+	VAG    T_1, h1, h1   \
+	VESRLG $26, h3, T_2  \
+	VN     MOD26, h3, h3 \
+	VAG    T_2, h4, h4
+
+// EXPAND splits the 128-bit little-endian values in0 and in1
+// into 26-bit big-endian limbs and places the results into
+// the first and second lane of d₂₆[0:4] respectively.
+//
+// The EX0, EX1 and EX2 constants are arrays of byte indices
+// for permutation. The permutation both reverses the bytes
+// in the input and ensures the bytes are copied into the
+// destination limb ready to be shifted into their final
+// position.
+#define EXPAND(in0, in1, d0, d1, d2, d3, d4) \
+	VPERM  in0, in1, EX0, d0 \
+	VPERM  in0, in1, EX1, d2 \
+	VPERM  in0, in1, EX2, d4 \
+	VESRLG $26, d0, d1       \
+	VESRLG $30, d2, d3       \
+	VESRLG $4, d2, d2        \
+	VN     MOD26, d0, d0     \ // [in0₂₆[0], in1₂₆[0]]
+	VN     MOD26, d3, d3     \ // [in0₂₆[3], in1₂₆[3]]
+	VN     MOD26, d1, d1     \ // [in0₂₆[1], in1₂₆[1]]
+	VN     MOD24, d4, d4     \ // [in0₂₆[4], in1₂₆[4]]
+	VN     MOD26, d2, d2     // [in0₂₆[2], in1₂₆[2]]
+
+// func updateVX(state *macState, msg []byte)
+TEXT ·updateVX(SB), NOSPLIT, $0
+	MOVD state+0(FP), R1
+	LMG  msg+8(FP), R2, R3 // R2=msg_base, R3=msg_len
+
+	// load EX0, EX1 and EX2
+	MOVD $·constants<>(SB), R5
+	VLM  (R5), EX0, EX2
+
+	// generate masks
+	VGMG $(64-24), $63, MOD24 // [0x00ffffff, 0x00ffffff]
+	VGMG $(64-26), $63, MOD26 // [0x03ffffff, 0x03ffffff]
+
+	// load h (accumulator) and r (key) from state
+	VZERO T_1               // [0, 0]
+	VL    0(R1), T_0        // [h₆₄[0], h₆₄[1]]
+	VLEG  $0, 16(R1), T_1   // [h₆₄[2], 0]
+	VL    24(R1), T_2       // [r₆₄[0], r₆₄[1]]
+	VPDI  $0, T_0, T_2, T_3 // [h₆₄[0], r₆₄[0]]
+	VPDI  $5, T_0, T_2, T_4 // [h₆₄[1], r₆₄[1]]
+
+	// unpack h and r into 26-bit limbs
+	// note: h₆₄[2] may have the low 3 bits set, so h₂₆[4] is a 27-bit value
+	VN     MOD26, T_3, H_0            // [h₂₆[0], r₂₆[0]]
+	VZERO  H_1                        // [0, 0]
+	VZERO  H_3                        // [0, 0]
+	VGMG   $(64-12-14), $(63-12), T_0 // [0x03fff000, 0x03fff000] - 26-bit mask with low 12 bits masked out
+	VESLG  $24, T_1, T_1              // [h₆₄[2]<<24, 0]
+	VERIMG $-26&63, T_3, MOD26, H_1   // [h₂₆[1], r₂₆[1]]
+	VESRLG $+52&63, T_3, H_2          // [h₂₆[2], r₂₆[2]] - low 12 bits only
+	VERIMG $-14&63, T_4, MOD26, H_3   // [h₂₆[1], r₂₆[1]]
+	VESRLG $40, T_4, H_4              // [h₂₆[4], r₂₆[4]] - low 24 bits only
+	VERIMG $+12&63, T_4, T_0, H_2     // [h₂₆[2], r₂₆[2]] - complete
+	VO     T_1, H_4, H_4              // [h₂₆[4], r₂₆[4]] - complete
+
+	// replicate r across all 4 vector elements
+	VREPF $3, H_0, R_0 // [r₂₆[0], r₂₆[0], r₂₆[0], r₂₆[0]]
+	VREPF $3, H_1, R_1 // [r₂₆[1], r₂₆[1], r₂₆[1], r₂₆[1]]
+	VREPF $3, H_2, R_2 // [r₂₆[2], r₂₆[2], r₂₆[2], r₂₆[2]]
+	VREPF $3, H_3, R_3 // [r₂₆[3], r₂₆[3], r₂₆[3], r₂₆[3]]
+	VREPF $3, H_4, R_4 // [r₂₆[4], r₂₆[4], r₂₆[4], r₂₆[4]]
+
+	// zero out lane 1 of h
+	VLEIG $1, $0, H_0 // [h₂₆[0], 0]
+	VLEIG $1, $0, H_1 // [h₂₆[1], 0]
+	VLEIG $1, $0, H_2 // [h₂₆[2], 0]
+	VLEIG $1, $0, H_3 // [h₂₆[3], 0]
+	VLEIG $1, $0, H_4 // [h₂₆[4], 0]
+
+	// calculate 5r (ignore least significant limb)
+	VREPIF $5, T_0
+	VMLF   T_0, R_1, R5_1 // [5r₂₆[1], 5r₂₆[1], 5r₂₆[1], 5r₂₆[1]]
+	VMLF   T_0, R_2, R5_2 // [5r₂₆[2], 5r₂₆[2], 5r₂₆[2], 5r₂₆[2]]
+	VMLF   T_0, R_3, R5_3 // [5r₂₆[3], 5r₂₆[3], 5r₂₆[3], 5r₂₆[3]]
+	VMLF   T_0, R_4, R5_4 // [5r₂₆[4], 5r₂₆[4], 5r₂₆[4], 5r₂₆[4]]
+
+	// skip r² calculation if we are only calculating one block
+	CMPBLE R3, $16, skip
+
+	// calculate r²
+	MULTIPLY(R_0, R_1, R_2, R_3, R_4, R_0, R_1, R_2, R_3, R_4, R5_1, R5_2, R5_3, R5_4, M_0, M_1, M_2, M_3, M_4)
+	REDUCE(M_0, M_1, M_2, M_3, M_4)
+	VGBM   $0x0f0f, T_0
+	VERIMG $0, M_0, T_0, R_0 // [r₂₆[0], r²₂₆[0], r₂₆[0], r²₂₆[0]]
+	VERIMG $0, M_1, T_0, R_1 // [r₂₆[1], r²₂₆[1], r₂₆[1], r²₂₆[1]]
+	VERIMG $0, M_2, T_0, R_2 // [r₂₆[2], r²₂₆[2], r₂₆[2], r²₂₆[2]]
+	VERIMG $0, M_3, T_0, R_3 // [r₂₆[3], r²₂₆[3], r₂₆[3], r²₂₆[3]]
+	VERIMG $0, M_4, T_0, R_4 // [r₂₆[4], r²₂₆[4], r₂₆[4], r²₂₆[4]]
+
+	// calculate 5r² (ignore least significant limb)
+	VREPIF $5, T_0
+	VMLF   T_0, R_1, R5_1 // [5r₂₆[1], 5r²₂₆[1], 5r₂₆[1], 5r²₂₆[1]]
+	VMLF   T_0, R_2, R5_2 // [5r₂₆[2], 5r²₂₆[2], 5r₂₆[2], 5r²₂₆[2]]
+	VMLF   T_0, R_3, R5_3 // [5r₂₆[3], 5r²₂₆[3], 5r₂₆[3], 5r²₂₆[3]]
+	VMLF   T_0, R_4, R5_4 // [5r₂₆[4], 5r²₂₆[4], 5r₂₆[4], 5r²₂₆[4]]
+
+loop:
+	CMPBLE R3, $32, b2 // 2 or fewer blocks remaining, need to change key coefficients
+
+	// load next 2 blocks from message
+	VLM (R2), T_0, T_1
+
+	// update message slice
+	SUB  $32, R3
+	MOVD $32(R2), R2
+
+	// unpack message blocks into 26-bit big-endian limbs
+	EXPAND(T_0, T_1, M_0, M_1, M_2, M_3, M_4)
+
+	// add 2¹²⁸ to each message block value
+	VLEIB $4, $1, M_4
+	VLEIB $12, $1, M_4
+
+multiply:
+	// accumulate the incoming message
+	VAG H_0, M_0, M_0
+	VAG H_3, M_3, M_3
+	VAG H_1, M_1, M_1
+	VAG H_4, M_4, M_4
+	VAG H_2, M_2, M_2
+
+	// multiply the accumulator by the key coefficient
+	MULTIPLY(M_0, M_1, M_2, M_3, M_4, R_0, R_1, R_2, R_3, R_4, R5_1, R5_2, R5_3, R5_4, H_0, H_1, H_2, H_3, H_4)
+
+	// carry and partially reduce the partial products
+	REDUCE(H_0, H_1, H_2, H_3, H_4)
+
+	CMPBNE R3, $0, loop
+
+finish:
+	// sum lane 0 and lane 1 and put the result in lane 1
+	VZERO  T_0
+	VSUMQG H_0, T_0, H_0
+	VSUMQG H_3, T_0, H_3
+	VSUMQG H_1, T_0, H_1
+	VSUMQG H_4, T_0, H_4
+	VSUMQG H_2, T_0, H_2
+
+	// reduce again after summation
+	// TODO(mundaym): there might be a more efficient way to do this
+	// now that we only have 1 active lane. For example, we could
+	// simultaneously pack the values as we reduce them.
+	REDUCE(H_0, H_1, H_2, H_3, H_4)
+
+	// carry h[1] through to h[4] so that only h[4] can exceed 2²⁶ - 1
+	// TODO(mundaym): in testing this final carry was unnecessary.
+	// Needs a proof before it can be removed though.
+	VESRLG $26, H_1, T_1
+	VN     MOD26, H_1, H_1
+	VAQ    T_1, H_2, H_2
+	VESRLG $26, H_2, T_2
+	VN     MOD26, H_2, H_2
+	VAQ    T_2, H_3, H_3
+	VESRLG $26, H_3, T_3
+	VN     MOD26, H_3, H_3
+	VAQ    T_3, H_4, H_4
+
+	// h is now < 2(2¹³⁰ - 5)
+	// Pack each lane in h₂₆[0:4] into h₁₂₈[0:1].
+	VESLG $26, H_1, H_1
+	VESLG $26, H_3, H_3
+	VO    H_0, H_1, H_0
+	VO    H_2, H_3, H_2
+	VESLG $4, H_2, H_2
+	VLEIB $7, $48, H_1
+	VSLB  H_1, H_2, H_2
+	VO    H_0, H_2, H_0
+	VLEIB $7, $104, H_1
+	VSLB  H_1, H_4, H_3
+	VO    H_3, H_0, H_0
+	VLEIB $7, $24, H_1
+	VSRLB H_1, H_4, H_1
+
+	// update state
+	VSTEG $1, H_0, 0(R1)
+	VSTEG $0, H_0, 8(R1)
+	VSTEG $1, H_1, 16(R1)
+	RET
+
+b2:  // 2 or fewer blocks remaining
+	CMPBLE R3, $16, b1
+
+	// Load the 2 remaining blocks (17-32 bytes remaining).
+	MOVD $-17(R3), R0    // index of final byte to load modulo 16
+	VL   (R2), T_0       // load full 16 byte block
+	VLL  R0, 16(R2), T_1 // load final (possibly partial) block and pad with zeros to 16 bytes
+
+	// The Poly1305 algorithm requires that a 1 bit be appended to
+	// each message block. If the final block is less than 16 bytes
+	// long then it is easiest to insert the 1 before the message
+	// block is split into 26-bit limbs. If, on the other hand, the
+	// final message block is 16 bytes long then we append the 1 bit
+	// after expansion as normal.
+	MOVBZ  $1, R0
+	MOVD   $-16(R3), R3   // index of byte in last block to insert 1 at (could be 16)
+	CMPBEQ R3, $16, 2(PC) // skip the insertion if the final block is 16 bytes long
+	VLVGB  R3, R0, T_1    // insert 1 into the byte at index R3
+
+	// Split both blocks into 26-bit limbs in the appropriate lanes.
+	EXPAND(T_0, T_1, M_0, M_1, M_2, M_3, M_4)
+
+	// Append a 1 byte to the end of the second to last block.
+	VLEIB $4, $1, M_4
+
+	// Append a 1 byte to the end of the last block only if it is a
+	// full 16 byte block.
+	CMPBNE R3, $16, 2(PC)
+	VLEIB  $12, $1, M_4
+
+	// Finally, set up the coefficients for the final multiplication.
+	// We have previously saved r and 5r in the 32-bit even indexes
+	// of the R_[0-4] and R5_[1-4] coefficient registers.
+	//
+	// We want lane 0 to be multiplied by r² so that can be kept the
+	// same. We want lane 1 to be multiplied by r so we need to move
+	// the saved r value into the 32-bit odd index in lane 1 by
+	// rotating the 64-bit lane by 32.
+	VGBM   $0x00ff, T_0         // [0, 0xffffffffffffffff] - mask lane 1 only
+	VERIMG $32, R_0, T_0, R_0   // [_,  r²₂₆[0], _,  r₂₆[0]]
+	VERIMG $32, R_1, T_0, R_1   // [_,  r²₂₆[1], _,  r₂₆[1]]
+	VERIMG $32, R_2, T_0, R_2   // [_,  r²₂₆[2], _,  r₂₆[2]]
+	VERIMG $32, R_3, T_0, R_3   // [_,  r²₂₆[3], _,  r₂₆[3]]
+	VERIMG $32, R_4, T_0, R_4   // [_,  r²₂₆[4], _,  r₂₆[4]]
+	VERIMG $32, R5_1, T_0, R5_1 // [_, 5r²₂₆[1], _, 5r₂₆[1]]
+	VERIMG $32, R5_2, T_0, R5_2 // [_, 5r²₂₆[2], _, 5r₂₆[2]]
+	VERIMG $32, R5_3, T_0, R5_3 // [_, 5r²₂₆[3], _, 5r₂₆[3]]
+	VERIMG $32, R5_4, T_0, R5_4 // [_, 5r²₂₆[4], _, 5r₂₆[4]]
+
+	MOVD $0, R3
+	BR   multiply
+
+skip:
+	CMPBEQ R3, $0, finish
+
+b1:  // 1 block remaining
+
+	// Load the final block (1-16 bytes). This will be placed into
+	// lane 0.
+	MOVD $-1(R3), R0
+	VLL  R0, (R2), T_0 // pad to 16 bytes with zeros
+
+	// The Poly1305 algorithm requires that a 1 bit be appended to
+	// each message block. If the final block is less than 16 bytes
+	// long then it is easiest to insert the 1 before the message
+	// block is split into 26-bit limbs. If, on the other hand, the
+	// final message block is 16 bytes long then we append the 1 bit
+	// after expansion as normal.
+	MOVBZ  $1, R0
+	CMPBEQ R3, $16, 2(PC)
+	VLVGB  R3, R0, T_0
+
+	// Set the message block in lane 1 to the value 0 so that it
+	// can be accumulated without affecting the final result.
+	VZERO T_1
+
+	// Split the final message block into 26-bit limbs in lane 0.
+	// Lane 1 will be contain 0.
+	EXPAND(T_0, T_1, M_0, M_1, M_2, M_3, M_4)
+
+	// Append a 1 byte to the end of the last block only if it is a
+	// full 16 byte block.
+	CMPBNE R3, $16, 2(PC)
+	VLEIB  $4, $1, M_4
+
+	// We have previously saved r and 5r in the 32-bit even indexes
+	// of the R_[0-4] and R5_[1-4] coefficient registers.
+	//
+	// We want lane 0 to be multiplied by r so we need to move the
+	// saved r value into the 32-bit odd index in lane 0. We want
+	// lane 1 to be set to the value 1. This makes multiplication
+	// a no-op. We do this by setting lane 1 in every register to 0
+	// and then just setting the 32-bit index 3 in R_0 to 1.
+	VZERO T_0
+	MOVD  $0, R0
+	MOVD  $0x10111213, R12
+	VLVGP R12, R0, T_1         // [_, 0x10111213, _, 0x00000000]
+	VPERM T_0, R_0, T_1, R_0   // [_,  r₂₆[0], _, 0]
+	VPERM T_0, R_1, T_1, R_1   // [_,  r₂₆[1], _, 0]
+	VPERM T_0, R_2, T_1, R_2   // [_,  r₂₆[2], _, 0]
+	VPERM T_0, R_3, T_1, R_3   // [_,  r₂₆[3], _, 0]
+	VPERM T_0, R_4, T_1, R_4   // [_,  r₂₆[4], _, 0]
+	VPERM T_0, R5_1, T_1, R5_1 // [_, 5r₂₆[1], _, 0]
+	VPERM T_0, R5_2, T_1, R5_2 // [_, 5r₂₆[2], _, 0]
+	VPERM T_0, R5_3, T_1, R5_3 // [_, 5r₂₆[3], _, 0]
+	VPERM T_0, R5_4, T_1, R5_4 // [_, 5r₂₆[4], _, 0]
+
+	// Set the value of lane 1 to be 1.
+	VLEIF $3, $1, R_0 // [_,  r₂₆[0], _, 1]
+
+	MOVD $0, R3
+	BR   multiply

+ 3000 - 0
psiphon/common/crypto/internal/poly1305/vectors_test.go

@@ -0,0 +1,3000 @@
+// 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 poly1305
+
+var testData = [...]test{
+	// edge cases
+	{
+		// see https://go-review.googlesource.com/#/c/30101/
+		key: "3b3a29e93b213a5c5c3b3b053a3a8c0d00000000000000000000000000000000",
+		tag: "6dc18b8c344cd79927118bbe84b7f314",
+		in:  "81d8b2e46a25213b58fee4213a2a28e921c12a9632516d3b73272727becf2129",
+	},
+	{
+		key: "0100000000000000000000000000000000000000000000000000000000000000",
+		tag: "04000000000000000000000000000000", // (2¹³⁰-1) % (2¹³⁰-5)
+		in: "ffffffffffffffffffffffffffffffff" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000",
+	},
+	{
+		key: "0100000000000000000000000000000000000000000000000000000000000000",
+		tag: "faffffffffffffffffffffffffffffff", // (2¹³⁰-6) % (2¹³⁰-5)
+		in: "faffffffffffffffffffffffffffffff" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000",
+	},
+	{
+		key: "0100000000000000000000000000000000000000000000000000000000000000",
+		tag: "00000000000000000000000000000000", // (2¹³⁰-5) % (2¹³⁰-5)
+		in: "fbffffffffffffffffffffffffffffff" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000",
+	},
+	{
+		key: "0100000000000000000000000000000000000000000000000000000000000000",
+		tag: "f9ffffffffffffffffffffffffffffff", // (2*(2¹³⁰-6)) % (2¹³⁰-5)
+		in: "faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000",
+	},
+	{
+		key: "0100000000000000000000000000000000000000000000000000000000000000",
+		tag: "00000000000000000000000000000000", // (2*(2¹³⁰-5)) % (2¹³⁰-5)
+		in: "fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000",
+	},
+	{
+		key: "0100000000000000000000000000000000000000000000000000000000000000",
+		tag: "f8ffffffffffffffffffffffffffffff", // (3*(2¹³⁰-6)) % (2¹³⁰-5)
+		in: "faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000",
+	},
+	{
+		key: "0100000000000000000000000000000000000000000000000000000000000000",
+		tag: "00000000000000000000000000000000", // (3*(2¹³⁰-5)) % (2¹³⁰-5)
+		in: "fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000",
+	},
+	{
+		key: "0100000000000000000000000000000000000000000000000000000000000000",
+		tag: "f7ffffffffffffffffffffffffffffff", // (4*(2¹³⁰-6)) % (2¹³⁰-5)
+		in: "faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000",
+	},
+	{
+		key: "0100000000000000000000000000000000000000000000000000000000000000",
+		tag: "00000000000000000000000000000000", // (4*(2¹³⁰-5)) % (2¹³⁰-5)
+		in: "fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000",
+	},
+	{
+		key: "0100000000000000000000000000000000000000000000000000000000000000",
+		tag: "f3ffffffffffffffffffffffffffffff", // (8*(2¹³⁰-6)) % (2¹³⁰-5)
+		in: "faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000",
+	},
+	{
+		key: "0100000000000000000000000000000000000000000000000000000000000000",
+		tag: "00000000000000000000000000000000", // (8*(2¹³⁰-5)) % (2¹³⁰-5)
+		in: "fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000",
+	},
+	{
+		key: "0100000000000000000000000000000000000000000000000000000000000000",
+		tag: "ebffffffffffffffffffffffffffffff", // (16*(2¹³⁰-6)) % (2¹³⁰-5)
+		in: "faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000",
+	},
+	{
+		key: "0100000000000000000000000000000000000000000000000000000000000000",
+		tag: "00000000000000000000000000000000", // (16*(2¹³⁰-5)) % (2¹³⁰-5)
+		in: "fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000",
+	},
+	// original smoke tests
+	{
+		key: "746869732069732033322d62797465206b657920666f7220506f6c7931333035",
+		tag: "a6f745008f81c916a20dcc74eef2b2f0",
+		in:  "48656c6c6f20776f726c6421",
+	},
+	{
+		key: "746869732069732033322d62797465206b657920666f7220506f6c7931333035",
+		tag: "49ec78090e481ec6c26b33b91ccc0307",
+		in:  "0000000000000000000000000000000000000000000000000000000000000000",
+	},
+	{
+		key: "746869732069732033322d62797465206b657920666f7220506f6c7931333035",
+		tag: "da84bcab02676c38cdb015604274c2aa",
+		in: "000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000",
+	},
+	{
+		key: "0000000000000000000000000000000000000000000000000000000000000000",
+		tag: "00000000000000000000000000000000",
+		in: "000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000",
+	},
+	// randomly generated
+	{
+		key: "52fdfc072182654f163f5f0f9a621d729566c74d10037c4d7bbb0407d1e2c649",
+		tag: "9566c74d10037c4d7bbb0407d1e2c649",
+		in:  "",
+	},
+	{
+		key: "81855ad8681d0d86d1e91e00167939cb6694d2c422acd208a0072939487f6999",
+		tag: "eaa270caaa12faa39b797374a4b8a420",
+		in:  "eb",
+	},
+	{
+		key: "9d18a44784045d87f3c67cf22746e995af5a25367951baa2ff6cd471c483f15f",
+		tag: "dbea66e1da48a8f822887c6162c2acf1",
+		in:  "b90b",
+	},
+	{
+		key: "adb37c5821b6d95526a41a9504680b4e7c8b763a1b1d49d4955c848621632525",
+		tag: "6ac09aaa88c32ee95a7198376f16abdb",
+		in:  "3fec73",
+	},
+	{
+		key: "8dd7a9e28bf921119c160f0702448615bbda08313f6a8eb668d20bf505987592",
+		tag: "b1443487f97fe340b04a74719ed4de68",
+		in:  "1e668a5b",
+	},
+	{
+		key: "df2c7fc4844592d2572bcd0668d2d6c52f5054e2d0836bf84c7174cb7476364c",
+		tag: "7463be0f9d99a5348039e4afcbf4019c",
+		in:  "c3dbd968b0",
+	},
+	{
+		key: "f7172ed85794bb358b0c3b525da1786f9fff094279db1944ebd7a19d0f7bbacb",
+		tag: "2edaee3bcf303fd05609e131716f8157",
+		in:  "e0255aa5b7d4",
+	},
+	{
+		key: "4bec40f84c892b9bffd43629b0223beea5f4f74391f445d15afd4294040374f6",
+		tag: "965f18767420c1d94a4ef657e8d15e1e",
+		in:  "924b98cbf8713f",
+	},
+	{
+		key: "8d962d7c8d019192c24224e2cafccae3a61fb586b14323a6bc8f9e7df1d92933",
+		tag: "2bf4a33287dd6d87e1ed4282f7342b6a",
+		in:  "3ff993933bea6f5b",
+	},
+	{
+		key: "3af6de0374366c4719e43a1b067d89bc7f01f1f573981659a44ff17a4c7215a3",
+		tag: "c5e987b60373a48893c5af30acf2471f",
+		in:  "b539eb1e5849c6077d",
+	},
+	{
+		key: "bb5722f5717a289a266f97647981998ebea89c0b4b373970115e82ed6f4125c8",
+		tag: "19f0f640b309d168ea1b480e6a4faee5",
+		in:  "fa7311e4d7defa922daa",
+	},
+	{
+		key: "e7786667f7e936cd4f24abf7df866baa56038367ad6145de1ee8f4a8b0993ebd",
+		tag: "de75e5565d97834b9fa84ad568d31359",
+		in:  "f8883a0ad8be9c3978b048",
+	},
+	{
+		key: "83e56a156a8de563afa467d49dec6a40e9a1d007f033c2823061bdd0eaa59f8e",
+		tag: "de184a5a9b826aa203c5c017986d6690",
+		in:  "4da6430105220d0b29688b73",
+	},
+	{
+		key: "4b8ea0f3ca9936e8461f10d77c96ea80a7a665f606f6a63b7f3dfd2567c18979",
+		tag: "7478f18d9684905aa5d1a34ee67e4c84",
+		in:  "e4d60f26686d9bf2fb26c901ff",
+	},
+	{
+		key: "354cde1607ee294b39f32b7c7822ba64f84ab43ca0c6e6b91c1fd3be89904341",
+		tag: "3b2008a9c52b5308f5538b789ab5506f",
+		in:  "79d3af4491a369012db92d184fc3",
+	},
+	{
+		key: "9d1734ff5716428953bb6865fcf92b0c3a17c9028be9914eb7649c6c93478009",
+		tag: "71c8e76a67a505b7370b562ba15ba032",
+		in:  "79d1830356f2a54c3deab2a4b4475d",
+	},
+	{
+		key: "63afbe8fb56987c77f5818526f1814be823350eab13935f31d84484517e924ae",
+		tag: "1dc895f74f866bdb3edf6c4430829c1c",
+		in:  "f78ae151c00755925836b7075885650c",
+	},
+	{
+		key: "30ec29a3703934bf50a28da102975deda77e758579ea3dfe4136abf752b3b827",
+		tag: "afca2b3ba7b0e1a928001966883e9b16",
+		in: "1d03e944b3c9db366b75045f8efd69d22ae5411947cb553d7694267aef4e" +
+			"bcea406b32d6108bd68584f57e37caac6e33feaa3263a399437024ba9c9b" +
+			"14678a274f01a910ae295f6efbfe5f5abf44ccde263b5606633e2bf0006f" +
+			"28295d7d39069f01a239c4365854c3af7f6b41d631f92b9a8d12f4125732" +
+			"5fff332f7576b0620556304a3e3eae14c28d0cea39d2901a52720da85ca1" +
+			"e4b38eaf3f",
+	},
+	{
+		key: "44c6c6ef8362f2f54fc00e09d6fc25640854c15dfcacaa8a2cecce5a3aba53ab",
+		tag: "6f2a09aa76c9b76774e31ec02dcf7991",
+		in: "705b18db94b4d338a5143e63408d8724b0cf3fae17a3f79be1072fb63c35" +
+			"d6042c4160f38ee9e2a9f3fb4ffb0019b454d522b5ffa17604193fb89667" +
+			"10a7960732ca52cf53c3f520c889b79bf504cfb57c7601232d589baccea9" +
+			"d6e263e25c27741d3f6c62cbbb15d9afbcbf7f7da41ab0408e3969c2e2cd" +
+			"cf233438bf1774ace7709a4f091e9a83fdeae0ec55eb233a9b5394cb3c78" +
+			"56b546d313c8a3b4c1c0e05447f4ba370eb36dbcfdec90b302dcdc3b9ef5" +
+			"22e2a6f1ed0afec1f8e20faabedf6b162e717d3a748a58677a0c56348f89" +
+			"21a266b11d0f334c62fe52ba53af19779cb2948b6570ffa0b773963c130a" +
+			"d797ddea",
+	},
+	{
+		key: "fe4e3ad29b5125210f0ef1c314090f07c79a6f571c246f3e9ac0b7413ef110bd",
+		tag: "27381e3fc2a356103fb796f107d826e7",
+		in: "58b00ce73bff706f7ff4b6f44090a32711f3208e4e4b89cb5165ce64002c" +
+			"bd9c2887aa113df2468928d5a23b9ca740f80c9382d9c6034ad2960c7965" +
+			"03e1ce221725f50caf1fbfe831b10b7bf5b15c47a53dbf8e7dcafc9e1386" +
+			"47a4b44ed4bce964ed47f74aa594468ced323cb76f0d3fac476c9fb03fc9" +
+			"228fbae88fd580663a0454b68312207f0a3b584c62316492b49753b5d502" +
+			"7ce15a4f0a58250d8fb50e77f2bf4f0152e5d49435807f9d4b97be6fb779" +
+			"70466a5626fe33408cf9e88e2c797408a32d29416baf206a329cfffd4a75" +
+			"e498320982c85aad70384859c05a4b13a1d5b2f5bfef5a6ed92da482caa9" +
+			"568e5b6fe9d8a9ddd9eb09277b92cef9046efa18500944cbe800a0b1527e" +
+			"a6",
+	},
+	{
+		key: "4729a861d2f6497a3235c37f4192779ec1d96b3b1c5424fce0b727b03072e641",
+		tag: "0173965669fb9de88d38a827a0271271",
+		in: "5a761f03abaa40abc9448fddeb2191d945c04767af847afd0edb5d8857b7" +
+			"99acb18e4affabe3037ffe7fa68aa8af5e39cc416e734d373c5ebebc9cdc" +
+			"c595bcce3c7bd3d8df93fab7e125ddebafe65a31bd5d41e2d2ce9c2b1789" +
+			"2f0fea1931a290220777a93143dfdcbfa68406e877073ff08834e197a403" +
+			"4aa48afa3f85b8a62708caebbac880b5b89b93da53810164402104e648b6" +
+			"226a1b78021851f5d9ac0f313a89ddfc454c5f8f72ac89b38b19f53784c1" +
+			"9e9beac03c875a27db029de37ae37a42318813487685929359ca8c5eb94e" +
+			"152dc1af42ea3d1676c1bdd19ab8e2925c6daee4de5ef9f9dcf08dfcbd02" +
+			"b80809398585928a0f7de50be1a6dc1d5768e8537988fddce562e9b948c9" +
+			"18bba3e933e5c400cde5e60c5ead6fc7ae77ba1d259b188a4b21c86fbc23" +
+			"d728b45347eada650af24c56d0800a8691332088a805bd55c446e25eb075" +
+			"90bafcccbec6177536401d9a2b7f512b54bfc9d00532adf5aaa7c3a96bc5" +
+			"9b489f77d9042c5bce26b163defde5ee6a0fbb3e9346cef81f0ae9515ef3" +
+			"0fa47a364e75aea9e111d596e685a591121966e031650d510354aa845580" +
+			"ff560760fd36514ca197c875f1d02d9216eba7627e2398322eb5cf43d72b" +
+			"d2e5b887d4630fb8d4747ead6eb82acd1c5b078143ee26a586ad23139d50" +
+			"41723470bf24a865837c",
+	},
+	{
+		key: "9123461c41f5ff99aa99ce24eb4d788576e3336e65491622558fdf297b9fa007",
+		tag: "1eb0cdad9237905250d30a24fe172a34",
+		in: "864bafd7cd4ca1b2fb5766ab431a032b72b9a7e937ed648d0801f29055d3" +
+			"090d2463718254f9442483c7b98b938045da519843854b0ed3f7ba951a49" +
+			"3f321f0966603022c1dfc579b99ed9d20d573ad53171c8fef7f1f4e4613b" +
+			"b365b2ebb44f0ffb6907136385cdc838f0bdd4c812f042577410aca008c2" +
+			"afbc4c79c62572e20f8ed94ee62b4de7aa1cc84c887e1f7c31e927dfe52a" +
+			"5f8f46627eb5d3a4fe16fafce23623e196c9dfff7fbaff4ffe94f4589733" +
+			"e563e19d3045aad3e226488ac02cca4291aed169dce5039d6ab00e40f67a" +
+			"ab29332de1448b35507c7c8a09c4db07105dc31003620405da3b2169f5a9" +
+			"10c9d0096e5e3ef1b570680746acd0cc7760331b663138d6d342b051b5df" +
+			"410637cf7aee9b0c8c10a8f9980630f34ce001c0ab7ac65e502d39b216cb" +
+			"c50e73a32eaf936401e2506bd8b82c30d346bc4b2fa319f245a8657ec122" +
+			"eaf4ad5425c249ee160e17b95541c2aee5df820ac85de3f8e784870fd87a" +
+			"36cc0d163833df636613a9cc947437b6592835b9f6f4f8c0e70dbeebae7b" +
+			"14cdb9bc41033aa5baf40d45e24d72eac4a28e3ca030c9937ab8409a7cbf" +
+			"05ae21f97425254543d94d115900b90ae703b97d9856d2441d14ba49a677" +
+			"de8b18cb454b99ddd9daa7ccbb7500dae4e2e5df8cf3859ebddada6745fb" +
+			"a6a04c5c37c7ca35036f11732ce8bc27b48868611fc73c82a491bfabd7a1" +
+			"9df50fdc78a55dbbc2fd37f9296566557fab885b039f30e706f0cd5961e1" +
+			"9b642221db44a69497b8ad99408fe1e037c68bf7c5e5de1d2c68192348ec" +
+			"1189fb2e36973cef09ff14be23922801f6eaee41409158b45f2dec82d17c" +
+			"aaba160cd6",
+	},
+	{
+		key: "40ff73495fe4a05ce1202ca7287ed3235b95e69f571fa5e656aaa51fae1ebdd7",
+		tag: "2e619d8ea81b77484e4fddeb29844e4b",
+		in: "aa6269c2ec7f4057b33593bc84888c970fd528d4a99a1eab9d2420134537" +
+			"cd6d02282e0981e140232a4a87383a21d1845c408ad757043813032a0bd5" +
+			"a30dcca6e3aa2df04715d879279a96879a4f3690ac2025a60c7db15e0501" +
+			"ebc34b734355fe4a059bd3899d920e95f1c46d432f9b08e64d7f9b38965d" +
+			"5a77a7ac183c3833e1a3425ead69d4f975012fd1a49ed832f69e6e9c63b4" +
+			"53ec049c9e7a5cf944232d10353f64434abae060f6506ad3fdb1f4415b0a" +
+			"f9ce8c208bc20ee526741539fa3203c77ecba410fd6718f227e0b430f9bc" +
+			"b049a3d38540dc222969120ce80f2007cd42a708a721aa29987b45d4e428" +
+			"811984ecad349cc35dd93515cefe0b002cee5e71c47935e281ebfc4b8b65" +
+			"2b69ccb092e55a20f1b9f97d046296124621928739a86671cc180152b953" +
+			"e3bf9d19f825c3dd54ae1688e49efb5efe65dcdad34bc860010e7c8c997c" +
+			"d5f9e320ca7d39d4ba801a175b1c76f057832f3f36d7d893e216e4c7bbdb" +
+			"548d0ba48449330027368b34f9c69776b4591532da1c5be68ef4eebe8cb8" +
+			"fa7dc5483fb70c2c896334cb1f9cb5dfe044fa086197ff5dfd02f2ba3884" +
+			"c53dd718c8560da743a8e9d4aeae20ccef002d82ca352592b8d8f2a8df3b" +
+			"0c35f15b9b370dca80d4ca8e9a133eb52094f2dd5c08731f52315d828846" +
+			"e37df68fd10658b480f2ac84233633957e688e924ffe3713b52c76fd8a56" +
+			"da8bb07daa8eb4eb8f7334f99256e2766a4109150eed424f0f743543cdea" +
+			"66e5baaa03edc918e8305bb19fc0c6b4ddb4aa3886cb5090940fc6d4cabe" +
+			"2153809e4ed60a0e2af07f1b2a6bb5a6017a578a27cbdc20a1759f76b088" +
+			"9a83ce25ce3ca91a4eb5c2f8580819da04d02c41770c01746de44f3db6e3" +
+			"402e7873db7635516e87b33e4b412ba3df68544920f5ea27ec097710954f" +
+			"42158bdba66d4814c064b4112538676095467c89ba98e6a543758d7093a4" +
+			"94df",
+	},
+	{
+		key: "5cc36d09c7a6472a41f29c380a987b1ecdcf84765f4e5d3ceefc1c02181f570f",
+		tag: "0d57b8cbea8090df0541354673dcb4e0",
+		in: "44fcd629f08dc1ef53c9ae0d8869fe67fdc7a2c67b425f13c5be8d9f630c" +
+			"1d063c02fd75cf64c1aec9d2e2ef6e6431d5f5ad0489078dc61f46494dcc" +
+			"f403dad7f094170d2c3e29c198b0f341e284c4be8fa60c1a478d6bd55dd2" +
+			"c04dad86d2053d5d25b014e3d8b64322cdcb5004faa46cfa2d6ad2ff933b" +
+			"c3bd9a5a74660af3d048a9a43634c0250427d9a6219197a3f3633f841753" +
+			"ba7c27f3619f387b6b1a6cb9c1dc227674aa020724d137da2cb87b1615d5" +
+			"12974fa4747dd1e17d02c9462a44fec150ca3a8f99cc1e4953365e429956" +
+			"5e108535b1f62e1d4ba18e17a52164418bfd1a933f7fb3a126c860830a87" +
+			"293d9271da736e4398c1e37fb75c4bf02786e1faf4b610cd1377fbb9ae18" +
+			"0655a0abefbad700c09473469f1eca5a66d53fa3dc7cd3e7c3b0411d7e14" +
+			"5f96eb9654ab94913dda503a50f9e773842f4d2a5faa60869bf365830511" +
+			"f2ededd03e0a73000edb60c9a29a5f5e194cf3b5667a694690384599d116" +
+			"f8d2fd93b2aed55b7d44b5b054f3f38e788e4fdf36e591568c41d1052cad" +
+			"0fcb68ca4c4bf5090d57df9db6f0d91dd8b11b804f331adb7efb087a5604" +
+			"e9e22b4d54db40bcbc6e272ff5eaddfc1471459e59f0554c58251342134a" +
+			"8daaef1498069ba581ef1da2510be92843487a4eb8111c79a6f0195fc38a" +
+			"d6aee93c1df2b5897eaa38ad8f47ab2fe0e3aa3e6accbfd4c16d46843318" +
+			"5fc61c861b96ca65e34d31f24d6f56ee85092314a4d7656205c15322f1c9" +
+			"7613c079eae292ba966e10d1e700164e518b243f424c46f9ea63db1c2c34" +
+			"b512c403c128ee19030a6226517b805a072512a5e4cd274b7fd1fa23f830" +
+			"058208ff1a063b41039c74036b5b3da8b1a0b93135a710352da0f6c31203" +
+			"a09d1f2329651bb3ab3984ab591f2247e71cd44835e7a1a1b66d8595f7ae" +
+			"f9bf39d1417d2d31ea3599d405ff4b5999a86f52f3259b452909b57937d8" +
+			"5364d6c23deb4f14e0d9fcee9184df5994fdc11f045c025c8d561adb0e7d" +
+			"fd4748fd4b20f84e53322471a410cdb3fd88e48b2e7eb7ae5dae994cb5ea" +
+			"e3eaf21cf9005db560d6d22e4d9b97d7e9e488751afcd72aa176c0fcde93" +
+			"16f676fd527d9c42105b851639f09ea70533d26fc60cbeb4b76ed554fc99" +
+			"177620b28ca6f56a716f8cb384",
+	},
+	{
+		key: "811c3e356e7c793acf114c624dc86ace38e67bff2a60e5b2a6c20723c1b9f003",
+		tag: "c6e59044cefc43ee681c3eed872d02b3",
+		in: "e115b304c023792448794546a2474f04294d7a616215e5dd6c40a65bb6ed" +
+			"b508c3680b14c176c327fdfb1ee21962c0006b7deb4e5de87db21989d13c" +
+			"3ab0462d5d2a52ef4ca0d366ae06a314f50e3a21d9247f814037798cc5e1" +
+			"0a63de027477decdeb8a8e0c279299272490106ddf8683126f60d35772c6" +
+			"dfc744b0adbfd5dcf118c4f2b06cfaf077881d733a5e643b7c46976647d1" +
+			"c1d3f8f6237c6218fa86fb47080b1f7966137667bd6661660c43b75b6339" +
+			"0b514bbe491aa46b524bde1c5b7456255fb214c3f74907b7ce1cba94210b" +
+			"78b5e68f049fcb002b96a5d38d59df6e977d587abb42d0972d5f3ffc898b" +
+			"3cbec26f104255761aee1b8a232d703585dd276ee1f43c8cd7e92a993eb1" +
+			"5107d02f59ba75f8dd1442ee37786ddb902deb88dd0ebdbf229fb25a9dca" +
+			"86d0ce46a278a45f5517bff2c049cc959a227dcdd3aca677e96ce84390e9" +
+			"b9a28e0988777331847a59f1225b027a66c1421422683dd6081af95e16f2" +
+			"48ab03da494112449ce7bdace6c988292f95699bb5e4d9c8d250aa28a6df" +
+			"44c0c265156deb27e9476a0a4af44f34bdf631b4af1146afe34ea988fc95" +
+			"3e71fc21ce60b3962313000fe46d757109281f6e55bc950200d0834ceb5c" +
+			"41553afd12576f3fbb9a8e05883ccc51c9a1269b6d8e9d27123dce5d0bd6" +
+			"db649c6fea06b4e4e9dea8d2d17709dc50ae8aa38231fd409e9580e255fe" +
+			"2bf59e6e1b6e310610ea4881206262be76120d6c97db969e003947f08bad" +
+			"8fa731f149397c47d2c964e84f090e77e19046277e18cd8917c48a776c9d" +
+			"e627b6656203b522c60e97cc61914621c564243913ae643f1c9c9e0ad00a" +
+			"14f66eaa45844229ecc35abb2637317ae5d5e338c68691bea8fa1fd469b7" +
+			"b54d0fccd730c1284ec7e6fccdec800b8fa67e6e55ac574f1e53a65ab976" +
+			"4c218a404184793cc9892308e296b334c85f7097edc16927c2451c4cd7e5" +
+			"3f239aa4f4c83241bde178f692898b1ece2dbcb19a97e64c4710326528f2" +
+			"4b099d0b674bd614fad307d9b9440adab32117f0f15b1450277b00eb366e" +
+			"0260fca84c1d27e50a1116d2ce16c8f5eb212c77c1a84425744ea3195edb" +
+			"b54c970b77e090b644942d43fe8c4546a158bad7620217a40e34b9bb84d1" +
+			"89eff32b20ef3f015714dbb1f150015d6eeb84cbccbd3fffa63bde89",
+	},
+	{
+		key: "f33691f5db2dea41e1e608af3ff39f3a6988dba204ce1b09214475ae0ea864b8",
+		tag: "6e50e70411201378c8d67857d7b631d2",
+		in: "439bc9ea10db4d2b08c7fcf2e8bd89fa9844f8061d462e28f174489e7514" +
+			"0f84e842040141cc59ce38f9551850cfbdfac2d75337d155090d70d0d930" +
+			"04340bdfe60062f17c53f3c9005b9995a0feb49f6bef8eaff80f4feb7ef3" +
+			"f2181733a4b43b6ac43a5130a73a9b3c2cbc93bd296cd5f48c9df022b6c8" +
+			"2bb752bc21e3d8379be31328aa32edc11efc8a4b4b3f370ee8c870cd281d" +
+			"614e6bc2c0a5ca303bc48696a3bd574ee34738de4c4c29910f8feb7557bf" +
+			"ffcfe7428b4703144bd6d7fe5b3f5de748918553df5453b3c6001696f3de" +
+			"0137e454aadf30cedfb6be36b0b908a38409f1a2dc202fc285610765e4c8" +
+			"6414692bf4bde20ed899e97727b7ea1d95d7c621717c560f1d260ab3624e" +
+			"d6168d77c483dd5ce0d234049017795f2e5a7569d7ad323c50a5b1170337" +
+			"4174a9977026c20cd52c10b72f14e0569a684a3dcf2ccbc148fd3db506e2" +
+			"8d24f6c55544cb3980a36e86747adc89ebad78d1630618d113fa445f8625" +
+			"b583cd7be33913c30c419d047cf3baf40fd05219a1fcec717b87a65fa022" +
+			"1a3aa8143062d77588168019454240ae3d37640996f2967810459bc658df" +
+			"e556de4d07263dc3d9158ec242008226d1c6aea7f0846e12ce2d316e80da" +
+			"522343264ec9451ec23aaaa367d640faad4af3d44d6d86544ade34c93518" +
+			"2843f6b4d1c934996778affa9ee962e7dfef5e70d933d4309f0f343e9606" +
+			"1b91b11ac380a9675e17a96099fe411bedc28a298cd78d5496e28fbbd4f5" +
+			"b0a27735d1144348e22be5b75724d8f125e99c4cb4e9c3a1f0b4e9da5146" +
+			"e6afaa33d02fda74bf58a8badee2b634b989c01755afa6ab20ee494c6ae4" +
+			"c2c6f17af6b53b61d2947d83a18eb3b8a1612aad5d3ea7e8e35f325c9168" +
+			"ac490f22cb713ddb61fbd96011c5849ac8e2fcd42db820349bdf9157dcc0" +
+			"0d9f9ed9c099b10c7194d48b623b0df43759734b2a2e5f8a35e7192bf9a0" +
+			"03dcb9d16a54bd84d922f85b6021b28aacc5264fe9e83deb48f18f864cbd" +
+			"367eb163d39c45b0eb907311a2a4b09fb26109088df782ce031b02f3caff" +
+			"d2dbe25b1cbde9f35ba7c47292a4fd49e7def7a28824f3dfda259a86c3de" +
+			"59257c255c712686ee47d128a55c7b9e8c546035eab7e2da420f32ed5c94" +
+			"bc12a34dc68eb99257a7ea03b69d6c760b0681fa24e4ca97b7c377182ab5" +
+			"fee30a278b08c44c988a8f925af2997883111c750d176b432735868208f4" +
+			"0de7137331b544f2d28040a3581d195e82811c945c3f9fde68fc21b36a44" +
+			"e1cfa2d8eb625f3102461539b3f13c660936a5ddb29a0ae791fbf52c2f69" +
+			"7bd334653f3605b362d91cd78569b41dbd09b2a5892440b5097fa08d0b4b" +
+			"291fc5b934585dd8d5adc80d573fdd194b2eae26dfc49f5e51c1f1607d7e" +
+			"87740702f244bf39ca1d52423e0ae84891dfdf4f43ef984c7a5f293a2007" +
+			"a1e00e39c757f064518953f55621f955986f63",
+	},
+	{
+		key: "d115b6ac998a65b48b3dae5977abaf985258d3d1cfe1616cec3d6a77f7a75785",
+		tag: "b431c9318ec2769fc8ee8f5fc3c079c3",
+		in: "7e7eb43839a6d7616b8a7b1fb7144817904342a9bd34167051162941a6b1" +
+			"b85db5e587f76e4a53211755d5ab29c11822d7711a97b3f1ff5b21f2485d" +
+			"9c86241fb56cdd6796245d3112df11ad9a7344db44d09934c4efb280ed65" +
+			"80cfcafb5c97a32993cbbf4917183e0b7bb38f2ce2479c28e1d39f673962" +
+			"17a7010448dfd39a4e7f406c8bd2d804f993bb410fffa4eb57518a531ecf" +
+			"259a8af068230acb826d9ffc20ee0fc43885221a321e3928971bb28615f0" +
+			"d9f099f5b68a80503a910fdba0bc643c60b64837900be38770b6b30c362c" +
+			"4580722b5dbb1b9c8cd02a18fd7b5661d2c4d28aa941c50af6655c826690" +
+			"37312fbf9f1cf4adb0b9400532755011b40e8252bd0e3c7a22efb0ef9122" +
+			"1e04b4aa8316d4a4ffeaa11909d38cc264650e7ca416835ded0953f39e29" +
+			"b01d3a33bba454760fb0a96d9fe50b3e42c95271e57840380d1fd39a375b" +
+			"3e5513a31a4b80a2dad8731d4fd1ced5ff61e1fbe8ff3ff90a277e6b5631" +
+			"f99f046c4c3c66158554f61af2ede73aede97e94b1d1f129aaadf9b53548" +
+			"553cc2304103e245b77701f134d94d2a3658f2b41108c5a519c2c8f450db" +
+			"027824f1c0ab94010589a4139ff521938b4f0c7bf0986585f535b6e292e5" +
+			"b3ded23bf81cec17c8420fe67a449e508864e4cbb7eaf335975668f013e9" +
+			"da70b33bd52a72094a8f03762ea7440ce9fcd10e251837cfc9ccc1a8cc47" +
+			"0c67379f6a32f16cf70ea8c19d1a67779a9b2d2b379665e0e908a88b26e7" +
+			"8c9f94f17acefa6d5feb70a7095e0297c53e091cf98df132a23a5ce5aa72" +
+			"59f1154b92e079f0b6f95d2a38aa5d62a2fd97c12ee7b085e57cc4652863" +
+			"8defacc1e70c3aceab82a9fa04e6aa70f5fbfd19de075bee4e3aac4a87d0" +
+			"ad0226a463a554816f1ebac08f30f4c3a93fa85d79b92f0da06348b4f008" +
+			"880fac2df0f768d8f9d082f5a747afb0f62eb29c89d926de9fc491921474" +
+			"1d8647c67d57ac55f94751389ee466bbd44dbe186f2f38abbc61a0425613" +
+			"e9b6a64e6bcb45a2e2bb783b9103483643d5610a7e2dcdb10b5d78423285" +
+			"506b42a99b00a4fb7b619b4526bb4ec78299dd01ad894fde2f053e18c55b" +
+			"6047f86333f2690c2cb8e87d9834ab8a5e339aa346e4d9952ed62dc083e3" +
+			"b11a823a67f23fec099a033f127ebe8626a89fa1a5a6b3520aa0d215a8e7" +
+			"dea3af37907686c16521739a95d6c532cc259c497bf397fceaea49cd46b9" +
+			"ad5c1b39a36fdd2f0d2225fef1b6ca2bb73fe604646c10ba4c572ab13a26" +
+			"559ededc98f5a34c874cc25621e65ba4852529b5a4e9c1b2bf8e1a8f8ff0" +
+			"5a31095b84696c6381eb9ad37ac0db184fe5fccf3554e514946a33cabe6f" +
+			"4d617b549d28ad1cc4642dac96e0215ee1596481600d3619e8f45e2c9ae1" +
+			"da834d44aca216bba0efef6254503ca90339f2d7ca508b2722d50c08def8" +
+			"a736590fa44855cd9eb9979c743783aa26e633696739f2ae25ff7b72ceb2" +
+			"4dff4455b85bbd675c8cb71ad18386dc58c371bdf37b4b3875b98a9423ff" +
+			"3becfc0d0ba2aacab3ee7683cb3b345095fefcaca5751ca793da63c89428",
+	},
+	{
+		key: "f3717306b9729be998cdb2c9d856306c5ae3d89da2cdcef12f86f6110c98d873",
+		tag: "907dba0f4849c7cf4570b5128b5f31d5",
+		in: "079572187d4559f24d8e48dc366441acf226a4db79e214ec3ee288acc349" +
+			"887e2e377419bcafa377d0151497b52e4d9cf2a02b0fc91ad9516482bdf6" +
+			"eccd1497954b53241bfb0bc5c04cc45045c6251f23a510060fee32721872" +
+			"bbc95cd8d400dff00bcac2ecce6229c7d73d8f85ed5a87afdccf6dedd299" +
+			"2d5c7b5b8090c47c737ded036ff0e9aedf02a2242fd9820be618b9601e73" +
+			"d3ba5d8f1ae9805cfd2306251704bc74e3546997f109f1dfae20c03ff31f" +
+			"17564769aa49f01233c9c4b79f90fa3d1433d18cdc497914046ad77d2792" +
+			"2588a7d0e61d4258d7d80cdab8503e3111ddca22cf7f39c1f80f1e16a68d" +
+			"9e21db8b53dd316dfa4233cb453a39a90101c60efc08514a3057db007e96" +
+			"507745bd4a0764ed8717a250bffb5fd1ea58474bdfb5b869681939693926" +
+			"40d832a3387ed4ac9cdab0d2af8fcb51b86e4d927097f1e79b5af96574ec" +
+			"d59d0dd150a0208978c41de28ad6cadf72a49279cffd6dc281c640f2e294" +
+			"4cde49a13ed390da1dd92e3011ce0f4a0863375a9db3f67fca1e3b8288a0" +
+			"78611161d7cb668ecdb932e1ff3733982c8c460eeeff2bca46c96e8a02cf" +
+			"b55d770940de556373a4dd676e3a0dd66f1280c8cb77a85136b3f003fab4" +
+			"887dad548de7bfe6488ae55e7a71da4097db03900d4b94e776a939530328" +
+			"83492da900b2a6c3e73d7a6f12ee30c9dd06cc34e5a3893976eb1de5864d" +
+			"32e792ac02e68d052d9d0cfc7cfb40b77728422f6c26cf68987c6b40fcfe" +
+			"9d660abc657360eb129de11bd70af5eb8fe350af2c27a6ece2cdf81b94c8" +
+			"0e68e8c51106497cfa5171236efe2d71d76b5dff3352af9b407dc5aab60f" +
+			"46b5683646f5b28732b7c750d351a08a507243d8e437cc4bef13a3edaa20" +
+			"5fc4e9968b4e563fa0dc965ba20b8e48bc188a321b16d3213bed69647512" +
+			"7a20afc1a3680ef261df6d37b017dee05cfc3a42e4130216e5540cf715c4" +
+			"e638d7d615c50bef576eeb19b3b15b2c2b454dfcef2b18161a143ddf52fc" +
+			"8e88fa71cbe34c92cd4b5a0adc81e5c33e11d2721bc1b95a9e693ac3cabc" +
+			"490889a8a42bf7e22375b679e8598c8faef22a006ed2da8ab1c08aaed2f5" +
+			"6d6f26649036335c0881bfec1e3a5346335c3b3707ee92173f1a7a3305c2" +
+			"933f78e995da8f1df64daf12b81ce23c8813c27fd4551103dc33561c2e80" +
+			"45b6b6770fa03498fd359a104884699d628020173edbcc4398b977e456e4" +
+			"885964840466176a490e7c513ba5d66090277c1ab1632a995a54f555a452" +
+			"1170a000507865b6650730aa6d6050a55959102836fff3d37e4773340e59" +
+			"2e56951ff9652519de4421d9c5b63edbeb30a3852a1ea110a9a29721aee3" +
+			"23d5a306de1624cecc87badc47aa87f489635d2fb60bff62ba67f5257999" +
+			"6af0a1f1a6fbcd8704e119196fcc289a6db6a4170a2cae31a1d30744b702" +
+			"2536d1526d41659c2dcc8b39c26aecfc0f8a707136d81b2827a158fd7386" +
+			"a537514471c213a8c859016748e0264cf3fbde10f40c620840ec4df99432" +
+			"e2b9e1e368e33f126ec40c572e841c2618d49d4eb098b9533b1f4ae00b46" +
+			"8d15de8c8ab6d0b650e599576f2bd90a124c9c6a0f911fd1bd8253bac272" +
+			"942cbdf8864f3747ff7f09d8a5a9d8599be7ee1744e5f1faf3e526cd2a06" +
+			"b157527272af9d38565957c9ce663c295766c0e0e464971c6282b70d4c0c" +
+			"1fb3b69856b34c089ad2b2c745f5a033cee1429c5b855581ee285278893c" +
+			"43a5968d9c28384b7abe8d072ba69089c938685cb1eab461f05314ad6d06" +
+			"eaa58512f8738bde35b7b15ef359dd2e8753cb1ed6",
+	},
+	{
+		key: "9772c1a4b74cbf53586e5df04369b35f1fdca390565872251bc6844bc81bda88",
+		tag: "68eb7fc459ecc3be819485001ab438dc",
+		in: "e115cc2f33e367cb85c01a914b3a512404ad6a98b5b0c3a211d4bffd5802" +
+			"ee43b3fb07451c74524ec8b4eddbb41ca33dd6e49791875d716a44bec97b" +
+			"7c2d4546616939ffa3b1ab9b8ba1d1a637e7c985cc922606caa0453085e3" +
+			"5f2fe0bd2de129d1d1856ade975a3281a62965927d8bb695e54514e69558" +
+			"89361a2a00a1b24e62bda78d0b71a0d40147016fcdaf1a702331dda8e678" +
+			"d8f476dcc91698da1688c610ec0cb1d9b8fbcd45dfde6d1503ba60a01337" +
+			"ae5b2f5c854a82c3087779babd2e522dd92f4718cd9f8c649ac226745ca2" +
+			"fa1696442764758f67cd926369578ae87612790dc56ed9cda935281a490e" +
+			"5c984950ec7a4e930520d273a69da4ed3a330e532508e26f942961fed0e3" +
+			"efeed52a7b96250d723155aa39a8ae85131c255c32bf406b647de1a37fba" +
+			"dc61e302bb5b70adec4505ee66b3a1d1b7bfe9c58b11e53ad556d56e5807" +
+			"017bb30b71be94e8f86aaf1496e8b8d6db75ec0afbe1cd336c23963c745d" +
+			"7b4ba1787ceb30728f1762b46f6eaad5064c8029d29b86266b87f93142a2" +
+			"74f519f3281d8c1cb43c23eb184ae41f3f625cf624b05a48d73cd7783fdf" +
+			"14954a03ec1a930e9a954424eff030e3f15357de4c19983f484619a0e9e2" +
+			"b67221cf965e9aa8d8926595c793adfe0181050df8b845ce648a66df532f" +
+			"78b10c83ecc86374a4f8abf8edcc303654bafd3dcc7de9c77a0a9d1d98fb" +
+			"121534b47d16f75b55fdc2a5e2e6799f8a2f8000d4292282e56863ae422a" +
+			"5779900ad6881b78946e750d7777f33f2f013a75c19615632c0e40b98338" +
+			"1e9b8d35a26abe30242c45662eebb157e6d7a8a5519de60268ac289b8295" +
+			"5d4feb47b9eef6da65031c6f52c2c4f5baa36fce3618b6a331f1e8bdd621" +
+			"48954fcf0846afeeb0a6cadb495c909a7fe671b021d5b0b4669961052187" +
+			"d01b67d44218471bfb04c1a3d82bf7b776208013fc8adabaefb11719f7a7" +
+			"e6cb0b92d4cc39b403ceb56bd806cbdcc9ee75362ab4aaeb760e170fdc6a" +
+			"23c038d45f465d8ec8519af8b0aad2eb5fae2972c603ed35ff8e46644803" +
+			"fc042ff8044540280766e35d8aaddcaa81e7c0c7eba28674f710492924c6" +
+			"1743da4d241e12b0c519910d4e31de332c2672ea77c9a3d5c60cd78a35d7" +
+			"924fda105b6f0a7cc11523157982418405be0bacf554b6398aeb9a1a3b12" +
+			"fe411c09e9bfb66416a47dd51cbd29abf8fbbd264dd57ba21a388c7e19e8" +
+			"12e66768b2584ad8471bef36245881fc04a22d9900a246668592ca35cfc3" +
+			"a8faf77da494df65f7d5c3daa129b7c98cef57e0826dee394eb927b3d6b3" +
+			"a3c42fa2576dcc6efd1259b6819da9544c82728276b324a36121a519aee5" +
+			"ae850738a44349cdec1220a6a933808aee44ba48ce46ec8fb7d897bd9e6b" +
+			"c4c325a27d1b457eb6be5c1806cd301c5d874d2e863fb0a01cbd3e1f5b0f" +
+			"8e0c771fca0c0b14042a7b0f3ae6264294a82212119b73821dcfbbfd85bb" +
+			"625b6f75e4dc0ee0292ab4f17daf1d507e6c97364260480d406bd43b7d8e" +
+			"8c2f26672a916321b482d5fa7166e282bfeed9b3598c8f8c19d2f8c8b98d" +
+			"f24c2500c8ad41cd6ed3f2835737916d846f1a6406cda1125ed7740fe301" +
+			"d1144559b7c95fa407599ae40a795226513153f86c9b8abe7d8aa6963c99" +
+			"5646ec586cbf20a03a698cc0681b7bd333402d00fa8e15cb32300b5a24ea" +
+			"316c5e1df67de78891846cb9183a4b112c3bcc17bcaa5fecd6c1dbbf6ef8" +
+			"272d9269e7f0ba9f17050a6aa5f11cb28874360396ab647941f2c9a85cb0" +
+			"6a969919b16997b0827af8f909c614545f1ad638ebb23109f6bab6b49b22" +
+			"b2285cabbb998b3e1bf42771b4d4e52330b224e5a1d63169ec85fe1c7dd2" +
+			"46dbafa6138448420f463d547a41c2b26026d4621b854bc7786ab3a0a93a" +
+			"e5390dd840f2454028b7c3bb87680f04f084089bbc8786ee42cf06904d01" +
+			"7e405144d2fae141599e2babe71abfbe7644fb25ec8a8a44a8928ff77a59" +
+			"a3e235de6bd7c7b803cf3cf60435e473e3315f02d7292b1c3f5a19c93646" +
+			"3cc4ccd6b24961083756f86ffa107322c5c7dd8d2e4ca0466f6725e8a35b" +
+			"574f0439f34ca52a393b2f017d2503ba2018fb4a0991fddc1949832d370a" +
+			"27c42e",
+	},
+	{
+		key: "d18a328b63a1d0f34e987682fe6ca3d48b4834b4312a17e99b3d88827b8d2238",
+		tag: "938b43b80cb3935e39b21dd8ba133cf8",
+		in: "bc2b0baf92580ee6c5efe640f2a029a791a3c77bec459be74cbc30931508" +
+			"d9f312c3a0944212831cbe4fc92e8f107f2f750c91bcc09f7624fa9a09b4" +
+			"9b7712cf5d619ea9da100fc23068ae2f4e353047e3956b215884bdb12235" +
+			"3f06b8ee98f36c3212493d61ae9ce151cd0453f3075b18a12d7d73da3de7" +
+			"dc2d98376cfb420069ca8148c511ca6bbae57572394a3c615a6fefb30c5f" +
+			"d727f964b4065ac9ee252bdd2bcae3e70162fe0e8069974e073f0a093d45" +
+			"be52d7de16a8f5f65c548aa6525822ffb00dc642530fedf355f7188ef017" +
+			"56384760c80afb61ad903d10119a7d615ec4fbdc79c490160bdeaf200915" +
+			"e405f2a921a2380c0ab9d2ac1e4fdc8ec4b907368c004458598efac13dc7" +
+			"2751e7faded538e3dc8b16590cac9b7ec294da0ad53e22cb9c05d8ef494f" +
+			"a04f6ab7c843c867fbe3cf1b4eb146d65339b0b03392259f12627a8e98e8" +
+			"0f4896c30b8ecd210acb2365539a872541921dcd8e1e54caf4936dfc7e1f" +
+			"68f3bbce61d325b447a8cce7f0fcad28494f2e47dae46b136594b5dfca7a" +
+			"bdafd6856f91496c05b21079aa55aa8c41628220a2cf0cdd755893375b7b" +
+			"b13d914c9a1d1db4a18f8fa36c55e52d0342352052032fb62d32fcd51cb1" +
+			"ac46f44b06e682db5d96d583cda03b966c650c03ae53542e8da1066b6884" +
+			"4a7e2280c664415e413f270b1fdcfbb40b9daa6131d071ee7eb1553dc5b1" +
+			"a50677971223dc316d2d326d57cbd529c88698facdca425e2d5c6b10d7ae" +
+			"cae28b8890aa44ede9b9193dbe8d1d8aa1fa580ca384b57eadcbefc96dd8" +
+			"bfccbe3b855a96f1fd4913035f817b75954ef1827c7718aab24d353e41cb" +
+			"a73748e14e0c2750d5b6a9752125708cc7ee7a498c7fbadf4186e7f8fa93" +
+			"bfdf281a49400f877621651b8ba87edda5231e80b758564e75139b61b1a9" +
+			"9fb9ec694f928ab1f47c6c4287bd4182d1b2be053380616e98da06f3ef57" +
+			"b570ade17c51da1d602b6ebc5a638ebde30d99bf4f91d0e01557c7dcd8f7" +
+			"9e5120143c935fc699eb5616ccd3cac56b5f8a53ed9e6c47ba896bfefe71" +
+			"2004ad908c12cf6d954b83bec8fb0e641cc261ff8f542b86e62d90e227f2" +
+			"a5bd59c9d390c0dd857f6da2b7624787a0bb31908bae84896890b283da61" +
+			"d8ec4f56eea38b22b438d6374b42243f9c1d94288874e53ab90c554cc1f1" +
+			"d736acde67aff55007fd4b3becc4d0f3ddd96f10dc75255cb0327aa47076" +
+			"2b3a3a656e33c87b02a682658b6cd2a75d9c0462803c9bbffa51441501a0" +
+			"3a2fbb2344aa13d27ffb9e98704ea6720b6a9992e53449688cd74d0648fa" +
+			"e8e776b0ea6bf048b2ec05341e5948cab0af015328b284ae7bd89a5f763c" +
+			"eaf5ca3e647a9f5bff7197e4d357e4359fa5fe30709545453149be510e3b" +
+			"ff86beeba5110c79c0215fbe9ac9339a8ac7d41f7488588ab14ac657aaf7" +
+			"d5c03a353932bbb2b261f0e83f3526c5e8e0c2348a10ab4eed6ecdcf9014" +
+			"7550abcb0a722f257e01d38bad47cdd5a64eef43ef4e741bf50da275720a" +
+			"0aee47adfc5cd2534b911dc269197c3c396820b303f6941e3fd85b5ed21d" +
+			"6d8136745c3eeb9f36b1f226434e334dc94be8a5606079cb7643136aacd2" +
+			"da9c38b2eb7e2b898bd8632003767bf0c87d00a3c2fcee48bbbcdd949af3" +
+			"3455128216709df25879b0ce894ac4f121dfca6b8c7865002b828696641d" +
+			"14ffc59924fbda50866fded0afaea545c8008c564a3a0b023f519a9980ea" +
+			"d541d91d1c07a739fd02286ea5660e473f80494236a68e84ea31aad71348" +
+			"e45055ded69c39941e31d51df257a4d0b0d8f025dbedee093f2b91795bc1" +
+			"533dc472020769a157a187abd6d8d52e1693e2ef56b2212759d0c0120e54" +
+			"c425d0084fdb3925e296dd6cdd8e677043a90674904057d88ebdea5998aa" +
+			"03562a790adecc4399352df43e5179cf8c584d95ef8e4b37295946b1d37f" +
+			"faf4b3b7b98869184e42ea8b304fe1059f180ff83d14a0861ca7c0682c34" +
+			"b48a70df8653bd8d9a26f9489e1271fa44e41b392e648d0e619ecdad2c53" +
+			"952094802eeb70ade4ffe096e3049867de93a824217e31364b18204e9681" +
+			"dd8e84ae2678aad155b238f59dd9bf9ce07e97183a690b2a46a8f3624843" +
+			"5b2f713e7d8dcda4dea1e3c4cf9692dda082322c51f7bb1f63d92aa987ec" +
+			"cf1355a043e21a7b8d60a2b97f18487f6fff4c77df92dbfdc9837540c518" +
+			"9fd9585731bc6e726a34ca21154b0499522c9d1016953dd0fa2eb6a92b6d" +
+			"14d6e3da5c12fabe92bd639e253983fc91041091791643",
+	},
+	{
+		key: "46e8eb27acfdc8f4be622d8741c7bc414464c149e21da97ab4afbf3e07b98b0e",
+		tag: "56b5f49be824c7a19b19faabf0787a87",
+		in: "ced52b76c057872a60107194b432cf04b7be05e65209045d2952ea0284d8" +
+			"3e2ed5a15cfdc58071204573c18ab03765b4d5e63a601419e039c42075b2" +
+			"7ebb2827de9c6233d6632e6d3db9140bdb4a9291d53f33734c2dc8e24df9" +
+			"0764dc10e0d321d20fdf659bfa2a81bc9e04fd0f83448143276647c08bfa" +
+			"dcfe3bc23898eda655c9353693ed7b022f43eefa23c21db7660c5029ca64" +
+			"a6085d93029ea6c43197356f56b7624d4819f5008d053357d981ffbe7f40" +
+			"96d6c55d8417002d36189b04bbb2c637339d90f4910a400833a8d422d88d" +
+			"c816c1636e8d9f7f926c244a28d9e0a956cec11e81d0fd81d4b2b5d4904a" +
+			"d1a5f55b5ec078dcb5c2bc1112bbfd5efc8c2577fe6d9872a985ee129e5b" +
+			"953e9cebf28cf23c6f9c6a5e09cb09ab586c6a50e4389cd3110777591d7f" +
+			"0608a3fd95b99f6ba03984fb0e13c6bbbde3668c59f2f2b69d7caadffa94" +
+			"6f67e725d56280e59e66dca025a18d4616e81abd9801835bd94485bb2025" +
+			"dee81fba440005b181ee81dc1d7796cbec92e4ec1c9016c8e8073cf281ce" +
+			"f749993f09a618a4671d58b476feffa454600f82955c591882715148a826" +
+			"586f68bb50059914dce1c1c85e5e3951647c9964ec9316005209a58baeb5" +
+			"2c6d01e6b4c275c0050a7e2bdc52133e433b050a700b556d4314e5c041d1" +
+			"93ee47f47adc971aed1b63259dd5cd4f95854a71a947eae3d3d12d0d7b52" +
+			"c6cd2fef2d2e892607a9681d73ac3236fad21ee30a4f857010bc95c00d5f" +
+			"6f0c6b3fe50cd6452be6eec4f5f01542dc2cb5e2db1f52224f11348fe2a0" +
+			"5d1e5885f1317f2d06ce2813dc4c723008e836a2ee95d0aac66855fe4c3b" +
+			"1b2e02ba0700be759b1ef1c2a3123ee4ccf9200d8d4de5e0d503f04c2053" +
+			"66393d1e91b648392ca28389d976aa618b4796acbfe8aa356ecdce1f7786" +
+			"bf09af226bb9402317b6fa319bbb9248d8ce00b1f49f066c69d4df93266b" +
+			"938342cd7fd4b07c320c2409ef72d8a57c21d0c6d6d493f7ca94d01b9852" +
+			"e4fca6a9291e9060154bc38af6c86932645f53914709fc90e11db56ec471" +
+			"6d600ee6452041248ea8244f79534f793bfc1f2020855d817cb4ca3c48ea" +
+			"7f6441ce9af9bda61936c226d810086c04a35e8654fdc30d4b35701adccc" +
+			"016d5895b2121ba4066e44d694f6371d97911786edb73dc3020ba186a01f" +
+			"ee3dd6036c0e205a8d05979bad228fd12c0fd2fded6c7f1e4c11354d266e" +
+			"d9c2f706269c43cd90504997d93a17b39b10dab0ff083ab3bd06540ce612" +
+			"d08f46ce75a16ef330525737410a0d98fb3d484968f9c12edcaf50103fdc" +
+			"c14128ea4ad6c30b56247eab28197fe617e5f88afa5cbe003c63d423647a" +
+			"d3042626fafd2084a0582ff1b1efdb5baa162662048019546234e2f6b6a1" +
+			"d8bb971114aae41df7795b4f3598f2af9e8921a9aadc7fab6c780aaa32a3" +
+			"84865a4ccb02351dbc55ec92a3152d1e66ec9d478be5dca17b4a131b4a0d" +
+			"3d4420fc6123fef80fd56ca266407d58a7880d6b7e5ce2b6bdc9a3721071" +
+			"7feec573d83c83a2e3f7d4023f2f68e785cde728fdbf5054060e4c89faa6" +
+			"1c9dd10524a08811d15c627b3b4ada549a3fa1d8dd77c005daaf2addeb10" +
+			"0abf694da8dd692f113965cd6366a5a7b0c17e1f2a320243e2c90b01418e" +
+			"22426d0401a2c8fd02cb3129a14fdfa6cbcaa1f1c2f17706e9ac374a3458" +
+			"777761e986ee4c358d26f8e420d33230d198fd86704e77298dd4c40c5205" +
+			"7566ac0cd92993b21937c3a3b4a8b89110a97cf38c781ad758bdc28f3565" +
+			"60cf3acbedfa8e05b396d226ef619746e8e4fa84c8e00a7f0e6d652808c8" +
+			"9c9b123d9bd802624cfa949eb68af85ca459b9aa85b81dbc0b630856cb9d" +
+			"7e18cdc96b3c069a006dd5b716e218a5ed1f580be3e3ccf0083017607902" +
+			"a7967a02d0a439e7c54b3b7ca4cc9d94a7754efba0bb5e192e8d1a6e7c79" +
+			"4aa59e410869b21009d9443204213f7bceb880ccf1f61edb6a67c395a361" +
+			"ff14144262b4d90c0e715dbefce92339ff704cc4065d56118624a7e429e4" +
+			"cadf0b9d2e7ffc4eb31c6078474a5265beba0774209c79bf81a930b302bd" +
+			"0f142534a6ae402da6d355a010d8c82dc379ea16d49b9d859a7de4db6e62" +
+			"40f6976ae0f47bc583b327df7ec88f5bd68f713b5d53796e72e28c29e843" +
+			"6c64cd411d335623ff4f5d167f3c7b8cba411e82f03714662425c8e1bc1e" +
+			"fbf435d28df541a914a55317de0ded8c744a1c3a6e047590244b207bcdcb" +
+			"f4bd1f9f81210deddd629192c58e6fd73e83812f084ef52f21c67bea98ee" +
+			"17554437d9642e2e",
+	},
+	{
+		key: "b41210e5ef845bd5a8128455c4e67b533e3e2b19dffc1fb754caa528c234d6a0",
+		tag: "72c9534aec8c1d883eef899f04e1c65e",
+		in: "7eeca180bb20d99635e36b9208221b2b8ef073fbf5a57f5190e19cb86c49" +
+			"89b0e8150d22ec3aaf56f6ed9cb6720284d13a4b0a34cd3d7f7fc7089326" +
+			"6d1893fa4185269fb806677ff490aec8f889896fca50d6c80d295875b1d5" +
+			"4a779b6d49305360b31011b48537157d0f323ff4e865d46fba6bd23a06c1" +
+			"46878cf9404360d325432312ff08ce495edca63a3c93c44d79c050e3f1de" +
+			"4b6ca5fedbbd43dbdef9ceb26d440a59c7e0be3a8e461c4f15b6b1e1dc36" +
+			"a71fc723ad593fb903e83d0804ce497fc49bfc6b6a602b9dc6e9891010b1" +
+			"4ca066cb1c68044c1ad837c638076dd3708078509cba49fdc54922cdf5d7" +
+			"715fb43e9b5a5942cb8950eade143577bc9dcedde58d51deddc70075e452" +
+			"bbceab1e95b5d003eb96bea69687faa6d50d9c605769cb4287b5d9924dd6" +
+			"8881c699abaa6f93e41dac7639cdbbbd0259099a3ed096f482a1fa322b15" +
+			"ffc379812c74e09e95f1bd3706347eac421fe56895e738a47fcd3e118773" +
+			"c3a7e7e264cc7ff5a53a80e436df058265dab9756fdf6913786a47e98bbc" +
+			"411052d58ffec9ee948e28cbaadaae471c5d828eaf3b3c87d3bfd495477b" +
+			"403da54f1418a15ace0d4d0df68f6a8f2b0457b127d5eae1f45ae055afa1" +
+			"8f058d5dd7eea559de3ae9378ca53f7d6dc9a9465ea1f945295f16ee0404" +
+			"7fc9dd3deda8ee32631d7af70c20edc1e12c5f8abd2e78f43dbd4cd6407f" +
+			"038efab144a24ea8a090a7ba3e6499345a60106220c2959a388e1a73d070" +
+			"1d854bfaaa86165a5aee934b615ac7f45da7c43a1e8f74613917ed10dcd2" +
+			"27e4b070414412e77851db5bc053e5f502bb4e2b2645bca074c18643e814" +
+			"4caeccb58be49ea9a552913c0616382c899635eea79a166988c206b9aaa0" +
+			"977c7ced89c4c7aaeaa8fb89b38030c44530a97187fda592b088198b63a5" +
+			"2dfad59a0a4c1aadf812bdf1881924e8b51b8fd4dbca8e73b2986b3ab484" +
+			"171e9d0cbb08be40ae60de8818bd7f400191b42c7b3200c27643f06720a7" +
+			"e0a17441f34131629388ac43955b78c31ea6602a70dd665f872e7669e865" +
+			"f6f40e634e8772d747608cd3a570e1726eb1ddca64f08582b022bb026eda" +
+			"6a913dc83f174ce3c18b9fc0503d3ac74e2fe45691d6dfb4af8c86d752a1" +
+			"6d6664fab4de08afe8858392fcc35cb9ea82fc42c42d48c0c0556267ea0d" +
+			"cc19b10f05e0318c4488ffe704b5036908f5cb938eebd3163503acaa874f" +
+			"592d945448fbeb93a877a26a72306a36e181745ba300afdc30cb7986919f" +
+			"3dbdc5c47ef1fa052a9e4aeeda3955f61ce2f30a0593a81dbaffebac5a49" +
+			"e5a8d1308352701d1ca9e620a67a89abdf5f0f8b1a0acfde5819981d4b77" +
+			"58799c0fe41030b86754837712af821c315301aa8dd50d1387b9fb92ee63" +
+			"10777e08229edd54e5e86b086ac281bd321082ef46ce298a6211aaa3aa4f" +
+			"6e55b5a4641220ec94cca73087760da1b1ac3e0da3f438214e691aa184b0" +
+			"535950b715a64d11485940dcaa3f72e0aa521002b1443f5e7880e2a85b83" +
+			"40d32db0fc4c4702e10f0fa24a35da9307850e945f608ad34d6cfdf6f2b9" +
+			"ff4f6b8e9eb5a883546578e2ff3cc5787322e4384640f42dc5bd05f432d9" +
+			"610dcf7c06cdf34762dd2a5e805e24aee8cebb3b4db9e4d1471da995bba9" +
+			"a72cf59ea8a040671b1d8ce24a3dce4fc86d2df85c8ab5e1eb2b0567c186" +
+			"4fb464f48c3ca72c7df2749542ed4d4be51b63769012ce3d06356856b2a4" +
+			"24995a2429a156ad93bc79c705e7b163149ce53a42c34a19680dfe4fd0f7" +
+			"fce38c30dffe9da9bc941d131f435c1398f8284a230e9d6e3992710074c3" +
+			"881d03aa309a9edd0fde7a39c33f6455dfcc5ae3fa20ea0e0d6549a43536" +
+			"b4cd8a2991a135b7d7a4265fb840318813091274414108f13fe191db7774" +
+			"6a5f4270f6d51a29ff523954f84cb76131d4abee79161dcbd97dc1ef24cf" +
+			"db1fade057dddee00a1e0de0db1afaeed1b535f7bb402afa3b297551fd14" +
+			"8c8f3e05f1351d3a8ee2948daaf14e7fc448c4670c906ae076eac5a7c656" +
+			"fd5f9cd937b91e26c9e5adb43c138f8d65e447b0022a524e059f879c6e27" +
+			"4ff7e671f75717233aae70853d5bd7bbb41b43c47bb08d6dc2f54f9ec606" +
+			"9487d1267add72403d01552a3d138abab9ca8a0d2dc32439759aa5695f70" +
+			"1a17d28dfb85850fdb55fddadcdde4d220e4b05821e5736d346e7dc9c945" +
+			"72743366488b1de8975184771361894b6520e3407c5c2e38473430969e35" +
+			"b106024da8618665d58c9d084824a28991a33658d6ec702139e01b65b7d0" +
+			"cc537a644caeee880657803d95f5f67816948d5ab362922f8ffbd531473e" +
+			"b0ff8fde2afc37a4abfa28dbed0be1b3d4ed48a1d02358e8403905d33b12" +
+			"3066e7a9fe2491ee9eb24fc9de7dbd322c8ddbc5ebcd0d92cd102ebac96b" +
+			"90e2fd784fd6d4b699304df23b17d963080a013794322690456be525c071" +
+			"b78fcd2d1148026e44ff14c4d0f942cd44d2b3263f4a93b79ec7a618b4b0" +
+			"d77ae7a1f6e6c7c7e2f498b825bf1954df348bae45ae1d7c87b6787f1212" +
+			"60c9a724429a4a2491ef989f65acfdc72fa717486dcf1984905218e11cc3" +
+			"970a09d71061e6df751f100abfbf",
+	},
+	{
+		key: "d9b0dc303188756312c12d08488c29f43a72e78714560fe476703c1d9d3e20c1",
+		tag: "6b9782f2a09b59653aa448348a49291b",
+		in: "dbde1820035997dc8a8ff3015b4e0674e7ce7bf0c2d994b7977f2d91b49b" +
+			"f200995040daeb1218a0f4307b6b8211913992b070d321bdb947b4ba5017" +
+			"a0885e7e5502710a75cbbcb56d49e1bdc2bc2afa5a0e83851162dec41340" +
+			"bafc41c5e11fcbf4ea2ac45bc57def4742281bbf734777f83c9ae1ea3d5e" +
+			"d42380230570f59c40d5dd9a2d89b75fa3c92664f12a274d965ed8de79a8" +
+			"b37f3763939ad21d1703ad794f617c8b32b20cc4dd7c1b7f969a65e1bafa" +
+			"f6c43f30c9eba256f10201910e2cc31a9b13a46ad29257024ef8f2ee29b2" +
+			"ee63cc5b6230ab9f87cd5cb534f4b0bb08a790466e0d57b849fffa1ed21b" +
+			"fb0b27804e3ff9df7bebf14e100cf91691a493e53870abfad6321f6711c5" +
+			"0fbcf1f0b2c1e5231d6c0a08e710525176355f6f82bedc1f787f0d3cb41f" +
+			"a11e91ebf9f4cbae46035a371232d63ef0d8bda0355af8cd0a2f7d1327d8" +
+			"0ab769ea0f1da0f76ec99cc737b5ce84675fa8a9ac0c98342bb82b5848bf" +
+			"656d35327ea01a1b09d84ab974c307511af68a30cd6978b529a8f58c68a5" +
+			"9d476062ace8897ec0d1a90d5d167e29ebaa6f46d93d697760c8771417ce" +
+			"94c0f3698985a98702833d1b68641b811840ca3d935386dbd4600fbc81c8" +
+			"728c4fd0e4588be739a048f03bd4ac651ceecd7e2fb120fe7190011f957f" +
+			"cbbfdc025f1ca0b356208db8cad87fcd53c5d3a30a7c2a48140ccd4cdb49" +
+			"f3961cef742caedd1e848bf3cacafb0da030416bf3177877aa0bc5f9d1cc" +
+			"41fafcb829d5e3ace9394028683d712552579e024084a6b855830ad9f567" +
+			"ff58f05d3ec263eddd6f56adec378f167e8dabbeaf7d0a9e65c71660314d" +
+			"6c8d54beeca2711113fbc32a2ff8c0daa8373278d10085d2a0660ad53f4e" +
+			"1ade74a483be180180acf9e9ad3ea5bdd9162ccd69599163a451c6837d5e" +
+			"a5e115bd9a560f395128ea002ee739009a44fa46078b18959933fb6e866f" +
+			"eb4612a56ce93b1affcb95fccaa18d71a148582ba1412a5daa07404fcb39" +
+			"c3cb4a2519cc506c1172c6c326016ae2e5410f6a438569f35a50d45cbf3c" +
+			"c46188651aa22c257858f60649cee8c05c75953ce49358dfe5980445fce9" +
+			"614ccd16d333ad236e29d204691ca0bf46f29da954bcaae52e41016556d2" +
+			"f4cae1d37565bcbe84de1b49f344d0200478a38187da29c155cc98184d9d" +
+			"33dca088d70054e0fce321f7a90c48a14963d0ace2b4e7a24b21c14a5e67" +
+			"1994fe1f7d22d1135d4df9268dd18d323fde3603288735626a5449582d35" +
+			"30e2c2225414e05a8c7b987c873a82e272a5d83e59b90f3d7264631d6ad0" +
+			"4a0cf3b5e96596a66ed5bfbc24ab6e4870aeec0acbad2cc5affaee06de32" +
+			"dca06f175bf763cf8e7fdf95941a177e934f0078be7dbaa4c9b6f5c16b4a" +
+			"5607bab5d56144a6ba3c7d9a084b8d1f4b24b6f9754ed207b230d3a2cc26" +
+			"259ccc725e1f8a44c4df8143e13edb5ebf073e2c9d2da5f1562df4feece2" +
+			"f6480987f093f642eb7afa3aa92dce2a8b60bb925cd2d11cf6c2ae7d2153" +
+			"1a9c8f068d71d0e682023932fe64e956a49347aed22b21084c4a84480491" +
+			"244ac6b337b6d12d5551ad5684766c68bacca62bdcafab6603c81bdbd8e6" +
+			"80d9d8b3825eaea4df023142e840f98ee251466a0422d810a54726a9f03a" +
+			"7e0afeb0043e60e2ba4908f951d2e87fcbc372096f2a9f4f2a95ad5faede" +
+			"3796b11ecf4401c3ee3d268bd8c46476c61e0ffc5c43c0f3c58c79e20f75" +
+			"520c102aa3c260972a870fc50f8841fa0553a9e30bf37ad282fb51b34adc" +
+			"7a933ca1691a8a706605ce0b906fdccbe954f8e5f2f63c42599a483c4be7" +
+			"3a041ef90ad930fe60e7e6d44bab29eebde5abb111e433447825c8a46ef7" +
+			"070d1f65862b30418efd93bfea9c2b601a994354a2ff1fc11c383e7bc555" +
+			"9e7546b8bf8d44358b1ce8cb63978dd194260e00a88a8fd17df06373aa80" +
+			"04a89172a6051bd5b8cea41bdaf3f23fc0612197f5573f3f72bce39c9f89" +
+			"faf3fb48d8ca918586d4feaea7e0f2a0d7a6afca096a081af462ea5318cc" +
+			"898a9cc09e8258a837559570cbd5eb901e8c0e04ee88ba31c81a76b000b8" +
+			"0e544feba576b3eb5272b53e46e96a0b35b9c759caadcec61444f8ec47c3" +
+			"45a1d2304e2708eeddfbfa75a98eab3493889047d690e84431d445407fdd" +
+			"99560c0bdd287e0944116f8ac62ab992ed3f1e2b415aea784b03c6904795" +
+			"f4326ff60bc839615f2894570dc9c27cf928ef192047528a1a19ec990978" +
+			"3b0d1a13dd4baf4a19e49bf798975abe2ad167dd574b32b3d0c22aa4d9b5" +
+			"2761e8f56cf2100fe5a39fceae3d865f3724d4f299d07ff899fed6baf7fc" +
+			"eb7189357bf56cf94a6493e61301b43e3ed158cb9c7a0e615fd9888c2db0" +
+			"7f7689762f62ef6b3ad4125e06b07a422f5040c3aa8b8f205d68356c9225" +
+			"56fc4c976165fed9599daeb297498ecf744bf6c7dc5e30604c461ad99402" +
+			"2eea0fb6fe33f82a97b5c272fd24162a94b761ec7e52173e7bb42e88b343" +
+			"64f5fa2c141ed04a86b8d00fd9c25bf77a8dc3e63f5543331405be6bf421" +
+			"6a891089b316aa4f887cb4aff0dfb4e80c2ccd65ddd9daa74b17b4411c0f" +
+			"c849dc748d9b138279dcd9ebfc6e6759a53f5c28a41bb82107d71cc161fa" +
+			"81291a8290",
+	},
+	{
+		key: "fb70ae7ec12264ff9f51124da188e5b11dbf53cae2671363f6054b575b1ddcc1",
+		tag: "d9ab81fab28b3be96fa3331714e78c9a",
+		in: "c62edf20b1d53962b42386eb570b10378f9764421ecbd7c4802853332747" +
+			"19ff4c89c06005050fa9ba6579a844060eb7ece6c43bab520e683e0f36ba" +
+			"49cba259edc6ae35d41e0d7812a7d5edbe4d90cd5e0504d16f4c3f70d01f" +
+			"5a0313de55934b661ce1ec317968c2c4de60f45c66cded8c10565a1ca6d2" +
+			"3a84bf182df2fcb05956ed4d46b49fc0fe3bd23961d9466fde070341ce41" +
+			"bc6e148449360a31634fe10e91082d82def90d9da2c250ea72c58add2058" +
+			"d046b4392b78bc3af5b3936ed568733e8ad5672dabbfa3130a6a535ec73b" +
+			"da8e7223535f49f96cd35d56ed4792c5cb7076720d5461d96a2692b2ada5" +
+			"2be08fb7bad15d15a0108143790024f0f15f5adc275e783aa56b70844061" +
+			"e30952a040e4cb9650f2a010417812790105d8f58bd25d99b0db3cb16229" +
+			"3f6322e86cd5b0bb1505a7b998fb0f81d1e1915faca3c2c8ddea39115507" +
+			"80339430a7955521839deff5b301f3fad54edd5ebd2ac4ec9b1795cb4dc0" +
+			"e2eb62ebca8e886c3f1e507d10a0228c3027b472a7104b815f5ec8dae55e" +
+			"0783ff7ae9a3e6b99e381ad788206b135520cb870ba0cdbe876feea843b8" +
+			"5a82adc95a6d71c555f798da92b82daf0abfcdbc82ec30b1f12d78490b06" +
+			"7315735017a94ac150b44dfaace151896f873923310ffcd41e91bac04de6" +
+			"d70ea71565948c907ab21c4a23703fbbd2a8de6d3095f3d8f901538968e3" +
+			"60e7bfddb9d22036b1c23f4f5f1b2ee22623426a2d5de68c1e1a38e38e08" +
+			"e2b5670aac1edff69e9c73c2ca56cb69c709009ef1d541aff1fdb2b40c92" +
+			"9b87f162f394b76cdbba1f5605993e4dd9c312321d59b0aa5c6e33be1b10" +
+			"bfd00b92d4c02db064d0e4a98f2913c89051b0f0ead163deb5087b6466d9" +
+			"84f57553b0fa53850eaa142e072fd91802eb9f0d2eb7318dd620555e6ce1" +
+			"86706b866d41cf6ba81f100342faa14d801dc6f3d522db38fab17a879fcb" +
+			"b6acfe922163505bd23a6842f6ef6397ae5fb6e6016421998bd43b0142b0" +
+			"3ca3b16d6ccb7a47891c75c687d791a930b26aaa2e3412e7aa16e2cf1501" +
+			"7bf6df6d2e1c289af0d7ce03954a60c1dfcee5e4b3da51eb43ddd14faf59" +
+			"082005d0c8b104561f66c002ff426be60be769282fc5685cfd1968df1941" +
+			"73667e48e9ad681d35757f1199f1d93377bbad093c8cc3efa2bcb6ecb703" +
+			"694422772d15aaa58cab9e9ab277ed510f684114cc4a44ccadb3eb1c9a76" +
+			"d8619a9b7743106df6fb6f927ac49b22ae5bb9a9a4d231e340a2cd0e3282" +
+			"53f6d75df694826f60e4b3e758398793eaf73ef5d4b56cd1471e16400f40" +
+			"4a947e9737f4f874fe09a29ad799f4525156e3abbf0585c3c3c0a3744c86" +
+			"5d56db3d2ecba6bcbb1adcc8bf5f3b2a2d46d3eba18cda55201598a8112f" +
+			"d8f14e205f0e615f081b8ff6c5aa6669da776bfc7c34d5af4d0b26d0d819" +
+			"f6aacc53cf3c6653138b9a962acee9d6ea01d280c35bb1f05d1509238ccf" +
+			"004c5013167f804d1780d9f4ef9d45742fccac346b0472bde24ff5db9ae0" +
+			"16455a3c02256358fcd8e6a9aae94f8a37a1a3da58a889bbe3d295e16544" +
+			"2e580f59bdd31c92ffcab40c49c1cdbb4db1dd4882b66edc10fcb1704203" +
+			"c518c1d8d4c268588ce13fc38e0210aeb47d11d2603d4b3de5c6ff5e969b" +
+			"9d5904abb282b699bd04a6e9f1cb323679e30400d725aab128a032745dc0" +
+			"be05a46b02b34b93bff02523cd8498c021fc35a488f164a70ef1ceb873d9" +
+			"14a681d3a3a34cc76bfd5a547e2630d7741a284511bae5897d9f7a197fc2" +
+			"456af5c6cd7e1a93d3388c7a990b5feacd7749cf39fdecdc20adfdd540c6" +
+			"9d330195db7cc0d4555ea5f5356a3647e2265399f153c34ed1e217c5dafd" +
+			"c2c5dd3d566c332c7ddacb0d76ecd3a0ad505a4165443aa81b0f43cabfb4" +
+			"62942fe74a77c22b8f68a8b1a6d712d1e9b86e6a750005a3796ba1545396" +
+			"13170906d228dabf572ab969c762f8b296054f23d5d4a37bff64bf9cc46f" +
+			"43b491b41101256018376d487fe8097f1653a7a9e99e1ef2492600598fb0" +
+			"bbb7df8270be8b9106126d6f491f8b342a96ab95df6133e883d3db4c6a99" +
+			"402aeb58d371263a32dcf76d33c8904395b9cf0016fdfc15608eb43e20b0" +
+			"99cbe7455f7a76f69bba058ef96f83ae752587485657f89c7f26fde7fbeb" +
+			"a82ede581ee92821dc13b8202930aa58bd4f1c86f68926baca0d06fee642" +
+			"ea8c652d226af91a9638a0244f1a03c7ce56969b87cd5c1f86110d192e0b" +
+			"98dd979d74acca6c1956b1127d9a1f456053d17974081ed8ced0faa4293a" +
+			"319e5b25ba285c1151214f52c283e39c35af51c4572c8e395b7856697bfe" +
+			"dfc4145ab4ed0bdbe43ba509c06a196ae6bf30d7582550cb546c63b51833" +
+			"cb0dfff7196d83f6a1c6d6d712cce2ec1989fd9ff5a0a22ac5022b49d566" +
+			"58f196703e4809e7624fe7cfa6c13b378f5aac7e66e657ed7eaa942d1a00" +
+			"544a947199f24d736b8976ec2cfb563433c49ba131bd08b63636854219d4" +
+			"c45100c98e3092773ef492dd9210bfd8f54cfe2cddafcf5c05468d90e620" +
+			"0c2ef99d17fa6992cc45eff3072b7cfd51cabb07ea3019582c245b3ff758" +
+			"0302e88edc2c13fc43646ba34de37338568baa66ecff3accfebad88d143a" +
+			"fd1c3b09ae39c501e3f116af33b0b720d6c2baf5acd7f31220788b2f9017" +
+			"3ed7a51f400054e174d3b692273fcab263eb87bc38b1f486e707d399fe8d" +
+			"5a3f0a7ed4f5e443d477d1ab30bc0b312b7d85754cb886e9",
+	},
+	{
+		key: "f7e7affceb80a0127d9ce2f27693f447be80efc695d2e3ee9ca37c3f1b4120f4",
+		tag: "41c32ced08a16bb35ac8c23868f58ac9",
+		in: "5a3607fb98eaea52e4d642e98aa35719bfce5b7d7902950995f4a87c3dc6" +
+			"ad6238aadc71b7884318c2b93cd24139eed13d68773f901307a90189e272" +
+			"6471e4bf9e786b2e4cf144764f33c3ac3e66521f845f6f0688f09eaa227f" +
+			"e71033b0f74295f6ddb91fe741323f2b54f420cb9b774d4291b06219f1fb" +
+			"4410b55900425c5e6fcabec76a5c2424d637a1641db6f0f6cad564a36a91" +
+			"0f49894bfd598e91f38ceea65e8253c1284f210cf7b50a96e664e562f3cc" +
+			"01c4fc490fa6d4679fd63fbb3ed8995a8a05166b573e92d22ef4370c6aac" +
+			"74ae94c94177e5f71143c6f340efceefda679ae76f6ed7f26eaa4848a8de" +
+			"8c40894316efbb06400f9695b18ba279e8947c032a84a40ca647d9ace457" +
+			"6dd0082494d6bd7be4e7928e749c78110af8774a5d43e9c9479964e2fddc" +
+			"ee51146460eac734311225d08c60706e40f298a7cb97f369ef599be097ac" +
+			"3bf1c275497bbd68968a235fdf8a61bc7cfeef0fe451bb04e662ca39f34e" +
+			"a8e3acdd0befe9762f9eeb275c0cdd43c80fc91131d1e0e790020975ab65" +
+			"afbea81f303ebd86760821efb4cad7cc01fd6d6fd194ac5ffe7703d890d0" +
+			"169e21b444cdbaf691fc741a5d99bd47357c37785755fa72582ca4754a03" +
+			"b4def86ded39aa6d9eb3f38801077e6d17e3cee3fb57ae83f30c79c3cf29" +
+			"0e2739c6b7323612cec3a561ebeadb4faa642f150323aaa9d270658c907c" +
+			"4c1610a5e1834730c08be3379cf1abc50c30e2bf01ce903927c27d85e135" +
+			"3db9e216dda8860c45925e2bb791abe5c8281ee6d16607bdca87f60662dc" +
+			"bd6e20224e7f009a86db66fadd8e37e0a59559328385090c6953cd20bb61" +
+			"f28a734fb056714f5159977f18e5c5f11de75f7a00ba807e47a29e4da32d" +
+			"5c67ec76ce4d7b669b5e6ee17e1df7c673dd8a7c87fce665cda8adb9547d" +
+			"1dccbdbe7be44846b4b121b0bfa65e4ed530789510d79bc4477e50178060" +
+			"f2668ac8956f39ef422ecb0e4cf90b8ce508552eedeeefa6c7d1bccc077e" +
+			"8088bd7e0e6aaf0bda9f11c412c270ee2ad6912f9808f9344a4bb137bdac" +
+			"b5b9372b00b0de026a8f5d1fb13972e1290b5005689f7636c43aee2fd443" +
+			"93d390371ae573f0e064b2d7df552b9adf04bf173d71c621795b9fb503dc" +
+			"5e918536c6ad25ce4a76f70e6b752b6d44be321187269a19bcf33ec899ca" +
+			"40e88b4eb23217095a85057bf95d8a54812cae4a7d32e0c2966a21376110" +
+			"74c6c8c3dd45a553c43c675d23308709f91be0b235d0222aa5e1e1ce08f9" +
+			"c6b45ceb5b47bcd7d7b2d4380bcdbd6eced452d93e6d8cbe18123277889c" +
+			"7f86b15fb991364a501fbf5d8244f2e3332ea0ab49e833c6f765017a4006" +
+			"cc7cd1a0365945a8d8873cb21832b210c83e451c01ac949de2fb0f7a420e" +
+			"405bf64eb251c6f022181595d68174b91e503187d3b3f49b60c23e44ea40" +
+			"ca20311305b413047bb22e89672758b74d6bd1a06decf09e9556421087a4" +
+			"0c1d2c44c5fb13d4d9625581ac4ccef1a1b5eeb5689aac5c0291aebda276" +
+			"50daf9d4396a64d02c6d58bcbd609d9a0017880ae0cbaf02ad0f1fc8d1b3" +
+			"ec987ffe13102d77352690c9b761bf13ea0b3a8ebad4a0823817fcaab4d0" +
+			"9b0bf03486620761dc77a6ba007ba07153b17425c4026597473e78863cbf" +
+			"430c0e5e9b04a83ad11506b61b8d9be3aeb06b5114e0d53d4724863eba12" +
+			"4f3b974bdb0d02743520409910621cd730c97ca984fe2921c38055f83ee8" +
+			"c4611db92e52d8ea51d89203e89df7586c574df15f3a96ed5a10bf04cb27" +
+			"f9656b5b11cf35fd21360b029ab26e9a741c6b3e6357aa1a41de2cac6e85" +
+			"f9a49e3441e60a60e74f434e1b8cd4454b11962e5507ebf904e9d6c52a7d" +
+			"9722300517c434758fbd6191f4550108b143eb16c0b60094fdc29327492c" +
+			"18a3f36737e506fda2ae48cd48691533f525acfffb619d356bf8347a8bbb" +
+			"4babdc2ac866e497f192e65a694d620687cfb4f631fbd6ae5d20ac2e3a12" +
+			"4d85f9391a240b616d829ac2adceedf8f3451ee77e4835639b13c622ef8c" +
+			"48a181fc7598eacb419fa438d4046aa971942c86b36eb8e16eab67105783" +
+			"d27fc56f5b66f35451b2a407d4648a87ae70807e45bccf14983b3abcb198" +
+			"d661d562dfcb00ffc569ca967171746e4e36f839946bc7d2ea9a0eda85b5" +
+			"a5594f6a9c1b179f7230eaa7797a6aaf8628d67fd538050cf47aa654778c" +
+			"11dbdc149458c1ec2233c7ca5cb172356424eb79479b6a3eed1deb9f3278" +
+			"5282a1034ba165032b0d30733912e7cd775cdb7e0f2616b05d521dc407a2" +
+			"ae7dfcf46fbae30547b56f14dbb0ead11b3666666c45d345cd5dbfa200ae" +
+			"24d5d0b747cdc29dfe7d9029a3e8c94d205c0b78b56d5e18613b3169bd44" +
+			"1b3c31513528fe102f9bac588c400f29c515d59bbcb0725a62c2e5bfb32b" +
+			"5cf291d737e67f923080f52d8a79f2324e45a3bd051bd51bac2816c501af" +
+			"873b27f253ef9b92ba4d7a422e2fb26a35c1e99eca605acc10d2a60369d0" +
+			"1f52bca5850299a522b3aa126f470675fa2ec84793a31e9ac0d11beab08e" +
+			"2c66d989a1e1b89db8d11439ad0d0e79617eafe0160e88384f936c15eb15" +
+			"ece4ff00e1ba80b0f9fb7a7d6138bdf0bf48d5d2ad494deae0ccf448c4bd" +
+			"60f0788d3f2b76de8ad1456f7572bd0ffd27bc2836d704d95e9c0df34571" +
+			"9dab267dd805577fafda03b834dd225ad9714d2bd182b4103faa5975180f" +
+			"90d5d6cac1825a19b9d4c87cc825512ae9dbeb33d2759c990905050f960c" +
+			"db3eb364c15b593524c882902b2a1d7fe40ea3f54fb0202fd8821463c7e3" +
+			"4b02a1209ba0048a9805f0468a13e03d18009318ecd92042959be263a51a" +
+			"407f1e660632c4247419659a4e073a8e9cd4a226763a7daea464d5427270" +
+			"7efd053cb4efc0504602c4f63e7d247b55db2ce1c07138f585d16cec97a3" +
+			"0731d5aec2166cb4de41695feb76280cbae1af8a2e67c2d5a3ac5487ffe8" +
+			"640f308ace6137e83576b79d586b663122221c20aba7a6bf60f73958f436" +
+			"59f087f850ba6e2d7fd862249c5fa6b20e3e43d4f2aa10d4c9cebfcbdf02" +
+			"6b8d103e4f89b93dd8af172f421001c8b162bd6d0b847a58ac108b6d6cc4" +
+			"9c7a9ba069deee",
+	},
+	{
+		key: "e3d21f9674f72ae65661aebe726a8a6496dd3cc4b3319f797e75ccbc98125caa",
+		tag: "3c95668130de728d24f7bca0c91588bc",
+		in: "baaea2b4b4cbe9dbc4fa193c376271f40a9e216836dc35ac8012476e9abd" +
+			"43dac6b9ce67dc6815904e6c84a5730cea0f9b4c6900a04ae2f7344fd846" +
+			"58a99513ffb268c6899dfe98d605c11e7dc77de77b0d30986f3051754503" +
+			"7c26be7b719aa9ca1140cfdf4c586b7fe726a8bc403249396a11cfee0a6a" +
+			"f6c5e72259785cfd13c2897384fe527100170001ea19106aed38f7d5d9a7" +
+			"ad43f0b41451e19989192a46b4f9734a774b6304cb74feb7d83822044a24" +
+			"2e51d55c0b8318e0439493bd1a57cc13f6079166cabc46877d003dcd39b2" +
+			"c0b90f6b32fc77acf04a6c125e11b35d91e2b18401cd53df4aff804e3c67" +
+			"a8bb3894b27c6e9b0070b53a85aafab0c0a253f9cfd4d3cd3be52428385b" +
+			"24a3f9f71660ca2c38474d14a0309e2f400e2c21af6e379099283ff241d7" +
+			"51da5a96a8dcbfdc43b913b29cc8cf8020eebb4a67f5bed31f2e383f8656" +
+			"8c815ff172382b425e95902e80f5fc219eccb51b656d37b56660f749e5b1" +
+			"4976a23648680a472d02ba71476e0afb29a0e084984f4eac3befbf8dd802" +
+			"2b7dca4dadd18bbe58e49c49ce48a06a71557a9a620c51e2623f818e4d62" +
+			"c2564c7ba04595cc109685869b183faeff2ac7a65049fc57cb10fb01951e" +
+			"a525332782d691f9759ec2ecd68bebb9c7aece5d522a08ce7830be520db4" +
+			"c9d60a2e490eaa0c91e37b256a97f84b39fe3c77953748c3b86fd84e9547" +
+			"a298c049cb28b8c85d59548b8dce635d59487c9de615802d16a8adc4c0e7" +
+			"80f35b9f10588a431b39b499dca929ab9d225f26e5721820627fe62427fe" +
+			"06d5773a50878b6effe840dc55bd3ea0c35168f6b6a972d57e8f88c5993d" +
+			"1ae33e0b7e9459c123753b518c184de7aaf429df078c9a18a29af77c727b" +
+			"796f5c1a501fa8105ee873c4e78c907142eb19690638a182fddb413adb06" +
+			"d66db19c7f6f46dac582bd72a6347b4427a576eb769d233febaf7be8f768" +
+			"337273c12253924f15653f9f3602b783703a81454a1dd7a8772a9ab1eeb8" +
+			"51be33e0c6c0708f3cc2012cabe8e2f0c38e35372abe27bc148fc4e1054d" +
+			"9d151f80aec0232a3a92dd77928a3678ebd7d09ba7b4e1d83227257292c0" +
+			"b8bc4a76de36bff6c9deb383029afaf4f37d5b935dc080a18665545e4acc" +
+			"195da0b9545d8902408886204b64f8548b32d012e0cdc520c17d9fb3be97" +
+			"800c2e2b945cb09a75a0a49e5d4d81c4194d91e839333b2b9b9e34d588e4" +
+			"e20cc1e911ca0a1429fa70ff063f0090fd842f89dfc5cc44affcce4e1e1b" +
+			"8b11c612f66b074c03ac2a055fd8f51ac9ed4f2e624589ff5730721d077a" +
+			"fb4c19e43abf8cf3ffa698362be8be51e92c2c91a4a56be64d9ac6d3fbaf" +
+			"5536a24c7fd0adaf74ca84c508e5e8c8bf7d4254e0c44158bd26acdf3f64" +
+			"e78438b3aaff89ac9986cef1e3a88d5bf2016340367a1cacd01ec167ec6d" +
+			"185d93a2a220d718b43ce1d429d2cb598605660b030e51e8d75fdbdd5b8f" +
+			"8677675e196a40a88285b18b24c5d2d594bab3d457e6f9e503e38cd470a6" +
+			"9ff8037c9a0a0f110a434335d954fa856a3721e0edcfb14287c3dd9639ba" +
+			"4db32b7da0670dd0a872e468e3819741d0d4ecf0a4f7a011bbae1493c01e" +
+			"642757491189f8664be3ec6437c4f3c76abfb0276e44a4d28871d3487c2c" +
+			"ce2f230452cb06184bb8620919659a7ba0a3d5c12ec25678b03403715ee4" +
+			"acb6a53d281036d8f3a085143cf5ecc3a0c6c92129caa7ac1f645c7bb95e" +
+			"4f63da38dc319e2ccff4a9006f9b9b1a38c4c39f6dc686bb82d43fb9fce4" +
+			"0c767d3ff22f52c5f9900130c65bb6a9cc7408a777d49b70946665f4a733" +
+			"5099376b276a43dc9a6382bb2d40425f6481b1846148434c672b84dd7a20" +
+			"33deb5140d43ba39e04ffe83659b6deb48629e1abf51e68748deffb756a3" +
+			"ed9e0807506b248a024cd509f539f4161366547c62c72933584e851599b6" +
+			"82ec16f1d79e9c6a01cff6f51ba7f46b67cdca09f3ab8496322b990a6116" +
+			"8d7574854a1cb1cb8f30a303dbd13a095df56dbb940dd16ce79879cd2d73" +
+			"80a419842fa1b34da668286de4c1ff5917b7aaa64713c349dc8f855d04ae" +
+			"de9a3a4d0739dfc36510b1e7bb1695418164285c44631b4b1a7c5798ecb2" +
+			"d976c1a3679a827bf0e8c662567e402bcc1354222036ad5959a6f0b8508c" +
+			"6a8c7d4a63e7dde154d778fc80a011592771d55801c7e1297b00b77f80d6" +
+			"314ebd1f5b3057398d1943599897cfabb65e7568d8fbdfcbecfd4b8a83ca" +
+			"0a7bed08ab9a656424831e0d7718c15727af7c83b2ef5eb5684aa044eca2" +
+			"ba896811246766248b20a325094a4b4159f9cde1ee349be6dc3c9a190453" +
+			"0349212a9537f65ae333c288753cd2bef6c5beb2f4164168d965a2c0fb9c" +
+			"c8c73d9e776e23d53ddcfb83bb7dfe2a1b8c781280f449d6f310faf8b53e" +
+			"89e6a611d6d3f42f2aaed5259730d149b3e7dabdc9f865bc1555374738c8" +
+			"456abe112e9628fb31efc2ecdc972da05987aafce728ccaed246cfcdf518" +
+			"3fe5dae528bbfb99d33194167e0f84d462d3d0da83e92227cf57922c7956" +
+			"4fe44648d87c69ad708e797972c44c4a5183fd5d1150a1182e3d39c3cd16" +
+			"3920f1d7ed83992bc4116d9351ae1c6c4827d1374242e374310409f32d5f" +
+			"0f38c78b6489c568b791c70394d29ea2516dcb10e51bdad862ce3339d5e6" +
+			"14fe14f150961809c36e0a2c8eb872e9f7a1c0956fbc9194cb63ff9993e5" +
+			"d0dcf62c0f49e81dbe99f3656c4dea57b766ae9a11254f9970618f1b33c8" +
+			"f339f440de240170f7a21f03ff2da42102b323ce2b9b7d0de5aae324d1ba" +
+			"c87b1e4c5279a566bf659778f8b03882aded57377a0f1b063af2897060e4" +
+			"23be7cefd4aa9a28479c16773944d254fc21d3e1acdf508b7972372b5991" +
+			"3b8b088e93471a7d54c6ae4c52ba465ef07f19f269677fc2f64d3fb3d7f1" +
+			"9069d6c7001d4b002ed6683c59bd5651a450503b68a4a00820b8c17e3263" +
+			"18f32c21dfbcb2a02a104edaeff67ec09533aaf3d1a7fb41aa5d506ccdbb" +
+			"e6e35fa0a263c0aad3acc91182addf8c5bdfbd0626702694b8d652a63c65" +
+			"8d6b2b7c75d015630de508195e1fca9573b61bc549ca017c4bd888194d44" +
+			"3e031f36170215a301f922736a819f3ffda69117170d1933300366c5f2ae" +
+			"1052446ef7c3b82c5868be158a881597132f51c91c80c24ebf621393dc45" +
+			"05fe057364a76ae67494a8a5f67acb551cfe89f447df272ed9c1509fc330" +
+			"2c3e16541452d4d68438f26858724012ad3b72c094b9f166c6bedb8336a3" +
+			"41e032988f39cf53535789b320b5424d07b6bf5f8792e3aceb0e868765b8" +
+			"611d7905089949e0c273e2410c72a146cd63981f420405bd883e5390e985" +
+			"8214a8db714e8400a21d0636d7e5d9671a3582ab9ff032170b8dd6b9d5a2" +
+			"144d065228fa54aea9a22654df67f3f62c5fc59d68914d8b219829b536cd" +
+			"2ae937ecccdb6031d94cb3",
+	},
+	{
+		key: "84373472e362a356bd5c9b50f55c588d067b939009944f02564f136c62dac36b",
+		tag: "12dd5297cfcec53deae1dd5f9325d894",
+		in: "860d9b2954c3daf18fd67eb8bd9e6e3de2e4988ad9b04b1987219204dee2" +
+			"388db1c59a935de27bce29e7cd3ebdf038785efb35eabd4c3785a62b1d9c" +
+			"3ffa25e2273cfe5eb10b4ec6152cd8f21dea415421b452efc7cc4ea6bf1a" +
+			"b85fa6614e7f6d650125424865386ff8ab53247a63ff023b2d0753a9e5bd" +
+			"458d6ab0156fd3cf2d5002f902f927a847e8c4a8426b0a5191f5e237d590" +
+			"2659ce9be9024750d1d618a6b8dd57efb6c2bbac2930858f1132639391aa" +
+			"9e8a620a2a7d64bb7e943c77753401b5b619d95ef857df25a52b4eb97372" +
+			"a05416706b2644e2687bf1d42c0cf06e5eef8a1fc7e178440bfebb85c44a" +
+			"4837f69e43a1789728a999c5e04291576e757510f22bca11583a4e93688b" +
+			"442f2b2dab8d5ea9441ff09b8287862ca538ad979297cc75510a3d9ef36a" +
+			"662b4b7c373f184202befa5bf3f315642e6210763d033b7e2c59731cb356" +
+			"045e9470bf2f83cd62f11b3e904b0c0b1be99bcb805150ba7ef12b8df3ca" +
+			"bfc5055640687d710ab88e0fa8034b26112ebfd044a4b290b1c6f6d18c31" +
+			"ba9880b1cf2d81b5d02f00d6d351da5dbf47b6a5cb7b53eaf6de52c8a68d" +
+			"053602ccffa37ccb44a7683ab4f8a58c4bbc9e140e4e6f3cc10a5c07ebd6" +
+			"070818db983f9f415168606011efab6b8d7b4e61e8eadd8bfd8d028b89bf" +
+			"b0a16996252d7b4ee4f9ab50fc9d6e482ecf99beeabc38d70efbb9a0d4b7" +
+			"9a1c5d2835adf8e25111352eabd24d562644efc97637f695e4792f2049c6" +
+			"00f4d889ceb951cfe289adf159865d013046985d7fe2598014bf2dbbc528" +
+			"b4166fc2180e724ded8e7ea1c8d66338ec50d955d5594a0a7b4655338b70" +
+			"e8978485a722df814fdc6fd2436dbc060121fcb575672b2a5e454c1209bc" +
+			"2bb21a99d39dcb3c697306dbc2104d60fd8051c43ea2fce268987d0ec249" +
+			"a5c02f91d3b0dfee181b3cf8ef1ba9665daf7ea1f1d3b216e378943b78b6" +
+			"bb41e5dba095748bc776f8df6383033a1f5504955da3f42153b1c7ea83e2" +
+			"f90b990ea0c5bd3906b5c4060b19f447ec7762916b8766e5a23bc4d39cdf" +
+			"8e27752df8129b60ccee1731e47383b589d4fcad865eed4041a186df206e" +
+			"9fb69ab6ea092e36f186a6fea8d77bd7f3ab0fa0e29404d617317c75c832" +
+			"854427848237cfc18486c95f7213b9d53f324da036e8d298133b5003984a" +
+			"b9d71836f9f1b059db90005a9067c261bd85aaeed4d623df2220eb52b73d" +
+			"d683abcdee5cebd411996f853752f638bd28df6d78bec2ed3e00d7beea06" +
+			"2b81c19682ffb2f6abe3a3623a2e0570650c1384f1818d76fbefe3a7ef3f" +
+			"46138160ef897f9934e00e066e215230e719c23905dc60d7fa4d666fa52f" +
+			"e7737db15126d3262c3a4c385cdb23ff3b56c131e43b241f4a6062a1a248" +
+			"de9f13eb82c11f7b6a22c28904a1eb6513cdb11179067b13c7b5f83a58c1" +
+			"4f2753f19fdb356f124f52923249d6e4a2c8dadc8bb0fc91e360155a14c5" +
+			"c194334b9f0a566d51fad98592b59c1cc4b40eeddb34e64f337f83874884" +
+			"0583f853398c343dabc29b9444be1e316309fb8d81304d654b3d4bc4cff3" +
+			"55fc31278fe22e649324ef10acd247c0b72397edf96a1c16bbbef0640296" +
+			"4d219575fd23c36efc1fb8f8a34b510ba9bdfb3b478e236777ef7c6c47f5" +
+			"5a2bd0383d8eed3759456ffcffb15e61985b08c022658a5ffc875821bdf8" +
+			"83f69f096dcc72a96888c3af76db57a54be701759670bf05cc9015f5bf1a" +
+			"745cf755a25b1403a870875701427f820c4b29eccc260f30113629ba03e2" +
+			"785014bdcbf34d0c67aa6aca20d2dece811788686d5a45820d2980bf7d69" +
+			"d5c820a09bad7bd95166f63dcfbe8652565c285e60e2704955d69b3037d8" +
+			"7f5e6567d95b8891276d5cf7c59047d10a02ae4a28794405e2524ec2d595" +
+			"1b36ad1b9d5265fa098a033b88aa66cd9eaf01eea49c7dc4cc51c486f624" +
+			"507a2be23f152f43709b2cfecee44945ca506950e90e70164b77e12e1c13" +
+			"0b4d1021c2afa20038f190096276cd22e89b6e7dd10fd58fa033c9d42536" +
+			"98de3f4908203be8dbf259112f840c76726d982b4a837cae7139e27182b6" +
+			"1b4dfbcc50e42d5ab8532edfbd30f668879824e9ebc34b63ff1526cda81a" +
+			"e38352a774d79f73219500e57f0159a32326195d8895d965071834876a45" +
+			"c1a3c0bc4b1638535f7d40011cd5b23343fc27fa318c1aa3f9d8c43351c6" +
+			"6148dc2175e0e620813266da3000954dfa22048f305244629d512e852376" +
+			"6248a897a3ec3e2983aaa8a0f025f18feea57a5153a59b02604ebfcc7a9f" +
+			"b03e62443df88ead9dee955e23bcf6528c278a353f254c9484a67a7b263d" +
+			"a301923a4efb6866aeaaafd428e6da48781365bc49e90cd16b2388220d08" +
+			"bb9f79d14012b5a8299a651917b6a829488753b6ca449a14e8dd8c5fd5ef" +
+			"657d627b8e7773475b802655dc033694f24376e3b01e519d1aa8365d0e55" +
+			"92d0a4adbf555639b6d75d7ee59a7d12c6c11317b7927f11bbe75ed90508" +
+			"b0698420e231206704d22dd1f1740edbdcaf19a47d66ace4eecbcefb77b0" +
+			"85cfcfaced4d2d6048ce76434eb79990f0898adb4af2c377b581ebab3f3a" +
+			"150f40dcae002d4caa60050591c0de4ba83bfd59a08670beaa4641aa9829" +
+			"bdbb720d6eb8b2f3e864a98676a67271a82cffdca2b3590a0b5f97efa5d4" +
+			"ba062b4798707159782bedc75e5363d5f5d55ec2bef70db22955adf401fa" +
+			"c3b7af937816eb25d54d9f2a92e5a2a04bd8b8d7568204fd289f5ed2e033" +
+			"a76209d288e11e8a4dbb06b9029e90cb186446746853f02d738e06bba538" +
+			"894e03e2658ab3d7f9ac861d2cffdf12396004d1cd15f18812d3803ab9e0" +
+			"6f41c9b374d6a0678bb82ce06d9e3b9dbc8d2e90b8f64d0d040f3fa8a3fa" +
+			"8be71d2b3183cceae1bcbfa2353689d842f7d7052e5699dcc70ab2b58761" +
+			"7041e5aa1e2f41911d525505f061d3ca45152f5a7a1fab50c674e4597a52" +
+			"b46aafb4ba57413879cad1308321843abb7c39696fc2f2e225878bb1191e" +
+			"e151cc76f1a1b8d491c1672fecbf710db82dcd32554361967fc839c8e5d4" +
+			"e488856e1b9382eb3fc3bdc3b6886a3cd79761b02bafa080a745ef6afa26" +
+			"822f1d10d5e8eefb842837d82c9986e78fc3390caa142b7643de8f613e5a" +
+			"890a57f5883409549537f8139534f4ca1b60f33e42be25433f1d82add530" +
+			"6a4cfce258c0d4f1f3c9148ffb5c4b626d51f78ac20bff0393b7fdb4b9cd" +
+			"70fee7f69892c8a9ee089c6c5c7bee0a1b825e5b9517f2c82d6c149735fe" +
+			"45a8839812c2deb2a355b6230697053092eca450b7b0d3242b2689efe364" +
+			"09e820d91fa4932034d96495d9dd3baa4b385da815a7cb69438ff648b326" +
+			"e7efe8d688e88570ba59df7c439faf72c95317a10c984c5ec0043407e9fc" +
+			"9b46487810eac19d2bb40e0a654935f76e7d8861480c5f48419eb33084d4" +
+			"0e1070e5ad542c94f58b49e67dd05b6637a2c67d41451b7e00ba30eff221" +
+			"755d6d427ec634a2b95980d274a89579feccf1c7df3787a9435e588f2496" +
+			"06a93b7ac41c8aaa84b91c95cad9463d4881de7353d95b13bbde4c9da90b" +
+			"f1fe96257309a416407c64368b5564f022c4a493f2a39df1696f45801e42" +
+			"a5",
+	},
+	{
+		key: "2d0035a30d19b9cbc7a27561f3ab474c01115c4499b4adec660ea06ebaa1a14c",
+		tag: "a2c77b55cb0c076d8ea83cfe0e64f293",
+		in: "4e667580ba4f38f64e5cb5566bffb486dcae10cd17acb3754251e837767f" +
+			"16429bba2b832f29ba538f97f3556548d163be25e69f88fff0743150623b" +
+			"e0a1d82af9384ca335927a0e9cacc3dadbdf1e24fa5c81f2602d109e1400" +
+			"33929e409b9a0fa4f2653944edcb8b3ef963ba7f8806196c73bff0ded670" +
+			"c6def5d240c5f3daa121f8d5bec9b2a0b0f1d62d54b013dc742d6bd46325" +
+			"460f692b76d4991f0796820ddebf150c7d33829795784dd2759b334d2706" +
+			"70a7264941be5d99d460d078a9eedc3660cb3176ad302f9365f0bd698e46" +
+			"9f3e63511abc81109995dba17be1abe8bcd28407c7fc8d02c14794bb033e" +
+			"178a94f6dc73719d5bc235f980a16eccb4121ca83b13c4e165931ae4f192" +
+			"4292f8cfdf1c3ed40feb71e13d919b48fa296dddb4d23114a3d86ec10f16" +
+			"f314de4cef813ed24b49f4c7bc44cb8424df1f70e8d77366161c7cdd709e" +
+			"97610aca3a24fb2202ffe15eaaa25d711cb5179212a2c6497a13e5d7c365" +
+			"7bc502b3d2ebde2e57b714dd9bc21e73795f3d35d620613918c4c9aa0e89" +
+			"031481c97a5a4c15ec6abe42d40498c33d71c823bf1d5bb5fee457e2fff0" +
+			"bf777c80c6e3336ab3ce793440e74b336a8f7034f6ea2e4ff5ea4ea7c350" +
+			"65cf2ccd2da1d6df29bde10f4cc0202b5e4cf7ed097da49b970a6db41e5e" +
+			"98f3845b42f46663b1d1ff01da71389a8737ba8f51eac1ef357ba5ac9a80" +
+			"dd2c7f9476111dcd651fc33f4c86dc8658656f3f02a8878bc38ff0d0a1af" +
+			"2e31fb92eaef08c50195490818661feaf90e8b6f5daa1ebedb2cdbc8d5dc" +
+			"16db3505f9611ac46bc37931e02c1fd6aad6e4b7e187d5e6f990fddc9563" +
+			"2b33f55bf68b0db3890b11113ecc839a4fa4de25160e574289aabe4d8fb7" +
+			"9cecf9d2fa75ac8d0195beefbdfe0815f8d7d9751c1280a29b547149ec7c" +
+			"2295f5afa53cfb516158086bf203357eec2a5db71143f996c81555a47f92" +
+			"209719a71570a5553f1ff9b4b41827dd74657b463f36623565f0c9f4d2ee" +
+			"8735d6af56ceb3b3d0ec516b22f0ddafbc24647481f61ab169e2616c91c0" +
+			"e1f6a35436598ed801670e1dba76226cbd0544959ebe70f836c8a7df575c" +
+			"b907d780ed5aa0d6e4e8e0d2f457efe89a777374aa49d4961db96dbb787f" +
+			"021d99231001360d532a70ee1fb94bd6f26524dd4b7556c6d40e08723d7f" +
+			"9905aca66c4743f2bf8b34493bdabcfca617809a867bfe0a4f94c756a6a3" +
+			"dcd04ffc0a3ac671a0afefe0d5d447efcec48c6368998760db6a572676d4" +
+			"29b6d3d6e0c815650447748c4b27541c5447acfb8f7261b6378f3fc0fdd7" +
+			"375eb9d458648c7fe9cd96344f11aca912cc5098e9ee39e0b6794cc1dc2d" +
+			"f1b10f927102705efa20e667b63a91f935c17764650b287f5289d5790766" +
+			"555f31985c5aad94c652ba41fa9c0195d15405f1fcce9e23054a42c8a252" +
+			"da83bf6268782ba44edec5d8f94a20b1830cd1c5894cc6b9b52ad0b12a5e" +
+			"cf3195a32a0b02483ae3b954ac6f3af1e0f334221279d03a72138f3a2cb2" +
+			"1e706427c4d604674dab88d429f28a67be7a996126e077a1dcf8989d90d0" +
+			"8b08f4abb9a546b3c64ecaa287bf3468c59add86365b885f52afe13ed8d2" +
+			"69ea61832a7ecbb96ff3336f58a1eeaa6dde3611f3ff7c2cc8c9b745b0e8" +
+			"b5919914245a49ac192cd77d10deb9a249623f696065a532c20eef9e9b0f" +
+			"e706579566a9eeb14d4e8251a7750e29eaa60f034c1a7a1d51aa03a45fff" +
+			"89acf41080deec5506128b06f003fa46bc4021a82fad6a8052a49744ed69" +
+			"45bd9331b5ae80d873cd042bff079b2b9d8af8065a22c449c32a56dbbe7a" +
+			"80d0f3e30b9167532506915883dce0aa9cb749e4368c595c5bd33b57e36d" +
+			"98cc9bf91cbfa47331d69b5cbe9c92bc66c0fc9ca8717bfc108e1f710333" +
+			"14dba02a28b9aa05890cb01ae9175806c3c4215bd446f6cc96ec5d08982b" +
+			"4f83cd1646160e1d306b3cdec02d251f0901b03e8c3c35464eaa5082586b" +
+			"b55482db97599d513ed8d7a82e32fae302684b7ede058474c1fac7893444" +
+			"16fec93fb982accd162dd956ba2f31a894e9366eca00e6e997fbbf9a2980" +
+			"8b83a139f6432147a717381bb8baa2205715f735c1e0db273cdda6897c9f" +
+			"39bf0d7eb7caf93f657ef4d3fecea28baf69cf36d3cf347081df3114455e" +
+			"b4fe3e49ad3c3f14435e0b39b6c0d16db0fbcfd7ba8da8760d5952c03667" +
+			"251e7a4c3008cfb0904225e55c23b884bb09d26631650460c4240bd5a165" +
+			"b531ee76ba5749b3bc60adad35de519321c1672b47bc35fb59f7792a3495" +
+			"11b2bb3504ba4a28717823a27a1f99ce6970290b26efcf1e7a0399b10eb1" +
+			"0c1299c09b80f4520d00e7908d004d5b6a72a411759cfa9523f6b2912234" +
+			"481b1d8fe4c2365961c0528bd593d42bebb398b5836ae6ca013fe440adbb" +
+			"0090e8ea274f4d8bcae483e3663051a328f7c12870b40e4973a9797a2336" +
+			"3d3c53e1b0d1a9159bfb26158f44734b3c34b571be641bba2db937d4ae1e" +
+			"edc807b95b1c2a7d44804885536316ad38aedf0d83b1519661f2bb5283cb" +
+			"9c50dd61c3753433e988189f26962d1f4befd444257d0b6d5b819d5fd572" +
+			"22c9fdff032e07a4d8686d451e71de4748965309c0a2d7c422ab7cf3d96a" +
+			"8c0a1b0afb229debd1c9421cb828b9f2be96bb9d6b5be7ef8134bd9ccf81" +
+			"51620937d720d83dbdddbfaba8ecd2eab6f1974090efde0ca963e9fdd691" +
+			"ed0cc5e074c5780779222552fa46ddcd951763a32aa3a044ff4a73cbab41" +
+			"dabb3c2c03fcda68303477f0dc26f35bdb5c9bde721fba1a2db732a89629" +
+			"a8de3cfebc3918df1a9d5053d09da5b7316e3285bf62156ca28cb64d343e" +
+			"72445fd66757bf4ab374fe7932a65f3d7fb6e42cb12e5b67ddf8530383a4" +
+			"6c1ee7ec8883e454a467df1aa7e468a6e7035515f473901efca5d46ff358" +
+			"70e0cc2575bbd7f8866c8e73cb157903a1694ff3051424f28de826984dcd" +
+			"065dc3658df144ae3a6d37b88c367e3cf7c58169dfdedda4a2821ce22188" +
+			"40472ff72f0dd1a6b0100555ff188b80f835259a634405e3dad61fc299f9" +
+			"307e27503b2cb7714bf3b636cc64b61d2e374119c8ef8adb21f1516c7fe2" +
+			"38c807818065bf312003c12e02525d69d9629a99e4ac66ad2e792f302cd2" +
+			"a6f5f702dd28040738a084a7052f2c3ed0924c33b7a5d357b7c9a29cebd8" +
+			"621a4bfb7bb34676ff210d59f7f9d4eafb7c5c490c9ea48402af5bb072c4" +
+			"731bdebcbed4e8e08a67931b6d7342d4ef7bc4a75ca1dfbd32ed6027d8fc" +
+			"b71e3f55565c02e06daa8c579b69774889181291c470576a99e11f2c5acf" +
+			"77e091ef65ed243d4287176f7f6ac7aba6908c9ff1fa43b894a499b642ad" +
+			"c01b2fa1c4b58801411941bb448f1f7a04794d2cfe5db1be61f7b86d6eca" +
+			"c547ee51d4c9050f9e9f318dae958c150acc21c878f0c7df6065294eb1d9" +
+			"a278c920838a0db752b080a32e67ac312fa76b589a385f31847196076ed8" +
+			"1021fcc375bfcc8e1361878e2693860eb21ff0595e4eaaf7897f2b79367f" +
+			"7c4f711279bf0c93a97dcb1cd8d87e444ad5f4cb5c1de44e37868c6743f1" +
+			"cd72cec376726f26c8bd4836f9a9f9c68042f95ca6f9d7cde493e531c553" +
+			"8bf7ace6dd768db69ac7b41ce93e8ca27ff20a83ff2148ec5b89e05d8b8f" +
+			"5d78d0fe16b96f6eb8d3b20126a186085c6825df81aa16b3dbf57eabc360" +
+			"71299ccdda60e250c652408d9cd1da94d73c728440ae08fddb901aec0fac" +
+			"1050a778b10f94f84883bee158bc53b1c001807c43a3151fbf581b18dda2" +
+			"527430872834e5c380575c54b7aa50f817cf3249fb943d46933cad32092e" +
+			"bfc575bd31cc744b7405580a5f2eabe27a02eec31e0d7306750adbbb9f08" +
+			"c78cb2d4c738b2274c7310cbf8dd0e59138b6a91b8253ae9512fe3d7367e" +
+			"a965ac44d54a7ed664e5e5c3c6c2d942eac388cd32beffb38f",
+	},
+	{
+		key: "2f29d71d73f7af98f96b34e939e1a21e2789ec6271b878bbebd14d7942d30080",
+		tag: "ec02f4953a9a63ab6f2bfc3501e4fab8",
+		in: "0e0950987f3508239063e26a13727fefcdfd2cea6a903615c64bf12d9ed3" +
+			"887f9b2cf7ccaa196ccc7756b09471475b9daefd4261e69abd23b9faf9c5" +
+			"1fd5d5788bb39d3c068fa6807d30f6201d3f6dfd31715d08b1733440cde1" +
+			"049608d23c4e45c5ed61f863350232f85827e7c292dc5f1eced1cbc912e3" +
+			"f5c420bd945911d3881ede5153d3b2cc85371fff98d2caf97cad6ef59001" +
+			"4017f9690cab08989851c2647e77e81401714a93ed9f938b79f8f54e3133" +
+			"fc2cdef259df2ba0d48f37bf9e43792e3a777214cf4aab6dde6deeb543a8" +
+			"813b71b5974136c1220d6218a252881f0f5677ff5b6aba127f19a5f3c5aa" +
+			"c988543d7839a90a3f947c4e4d5c6ae1ab48dbd40456d1aa65339a4c15eb" +
+			"520e8ff9f965ac4c37735937cf09942e7958f8a6cddee41707423f715903" +
+			"ffe0d15af8c3140d3a736d23be7485fceb9f07c6509f2c506eda4ec9d30c" +
+			"cc133708f48d8828e332808c84a745d337296d871b9794de1c5d06534aaf" +
+			"65587526a84e2521f8b332645e0e72564bb308ecf99b7bc69608474389d1" +
+			"686ffab8c49b7f04dadc28d2ecdd0f508dad2135843304e378b3bc7a4f25" +
+			"7fa4316be956e0a021edb8045f39fa9f002087f067199bd6001acaadd261" +
+			"4bf6aefd3f098f92a959685f24bb2206c347359d9c6adc6847117bb434ac" +
+			"6c40ec618f6ae8b75a5e2e4d44c332b7b06c8b4d521493b9b0bde8894209" +
+			"717a24b320214297b62dec741cea018ea681c9b56702068528b3726953e8" +
+			"c5e4ccd5029e4183e772d9834a56a88d45bf87603dfda40e03f7e894766a" +
+			"7623ab4dcc0dfc3086d17566945069173935916f772e2a5f8e1547348f28" +
+			"782400fc069ac0e2b94242e9e0f1ba2d0e76898f9b986540e61ea64d7f69" +
+			"1006b86ce61565da75eb16a8b4c5865ca4eebdde2190e354734bda94fe7e" +
+			"12ff47dcb5d5e6ad93cfadcc491cb350b09ffe391a157e14b65e3a211b5d" +
+			"4e447c3ff95571dbab33a83126d68dfddf9383b4359d4103ca64af1e6963" +
+			"d09e17eb944aa71e76711dca33168586bfc44ebe9fdc55497d83f238c66d" +
+			"bcb16063bc85635f0f1a6280563bca49ef971db96a41b6ac5e0642643262" +
+			"61eb4662f3d6ad4cac826db895de22c9b8aa35e6464a7f44e1ae7238e355" +
+			"068d68754ffcca76c50b7ce7ef9bfebac9eeab32c87d059cc7ef2adb5d57" +
+			"c7419adb394eef48441952253e8391e555730e29789d6293c3696f441449" +
+			"0aebe2bbe541e191a6652ffbec1192f0f9395b7ea370aefc1f1cc8438035" +
+			"d7681f12f1e11d6e334da188b10c302fc0f4bcf1de448090510a8f1d5683" +
+			"0c943a3c388b33a038c26741a4cf3487313f755fe7a28e25e44b5383c5f4" +
+			"cd6ef34d7dd73462226281899dc3f2e69809a0150f694673f31addc89888" +
+			"072a7d4ecd63d6b90540f9522ec05829a7f17d48728345ad808fb0203883" +
+			"3cbd018d612992a88df944b8e34a70920b3f26cda2e8bb16c3aa38b12b33" +
+			"b395c9ba5e809f60ff05f087112151af1b5987403cff8bb2dce79093f431" +
+			"2c744f911a6f3091e4f9ef9375c4dce4c241d2f6024a1797321851ca316c" +
+			"4e460fc060e7839deaff8ab5e8bf682c0f21ab6952eb793cffe690db911f" +
+			"50b11f56ea352942c43bfff51d4360882754faeb7cf28b6b32bf7fc9ca71" +
+			"fbfe1d72be05b8bac9ba513d731e2c9d13d6f2f10eb926edaaf0e3996656" +
+			"da8718a8e103c59326529e91ebac6ed52657c9690ccbf81028cd9fb189ec" +
+			"4de94fc0771e53302c8d9082835a68780cccd772660a110a1b40c57bef3a" +
+			"c1d69428aea549ed17663a96895a66a3bb5ff6ff61dc64908df49b760caf" +
+			"a5aff05e2766a418dbaa1e7d189a9edd55a04fee8c9d6e506d299abc36a9" +
+			"d67be035fea5d220f41d081af67615fe627c4dd04bd8659c7fa4f57f35d0" +
+			"db40d9684aa178d7483ed5d86f04eaea412e0ea05a4698377dbff4fc3a39" +
+			"1f6ce0cb833d3118d6c69319b511cce65fdc74928e270da0c537f8201eff" +
+			"77416155d4a39c7ad38c22cdbf7d2b7ff7d85383c178a835ec604c3f9ee3" +
+			"7399f7dd826e34f1a35ab75da44ba56f86097ddc0f3658ef5bd65a24f4de" +
+			"4255d0b03411a9d7f0ddc29e33cb865da23393471aa94e6c9e72e789206d" +
+			"3ba118aecd39727068f528f01b25fae2280d70033e4ee46b41b864bb922e" +
+			"001d8bf46d6fbaa5a594e926f45eb3a4d2f074506d7834b606f43c89699a" +
+			"6db00b374658d9333700894d440a712a1f25f5538f9e7c8ee57ae7e612df" +
+			"13292c8ba9dbede4fb77cc6c8944aaef59ea6ad3b36db398f4bb0f82d40b" +
+			"44879835f224d6e05992b1b8a68dd58c3dbda2fd73786492ee48c7a25f87" +
+			"264b766930fe9427487504fad17f8d230934f044e49ba219f26ead728856" +
+			"cb30eecc33a3946d3b1b781061f2458c7c46f6d96f3e06f369f97be91835" +
+			"f23b38347d1e381ad5be4419275772c2abd549522a0203c1ee9c96faefe1" +
+			"df413c4b7b2624417890e0716854b7092b3b3b368cb674035d3e6bab2357" +
+			"e7c262b606f7141b6dad2f6145ebc1deb7597814719784f3c17848a90ffb" +
+			"cb0289e2f3cc7da12442b837c4e47f468bca3eb4e944a31c48562c2f144e" +
+			"9e920ab5e4cf90a14ccadbae29af13db38cda911e3c8f6f525e6722809b5" +
+			"31a4de1926ab12f643d25af87eb8610df59eded6ec278242247dc69a4213" +
+			"13f7c2b26ae7a917c1bdaf66c56876e9104d40b59e6ca1431ddb77fc89f3" +
+			"14b46a154cf127688564a4f9e120d7b5816cd24a6e095dc8ab8b43bc3639" +
+			"329719f0e0f723e2f5136d82638e2249e648ebca67cf0306741e9e8d45cb" +
+			"903bca85485c4007397c88a1ce07266f4f611b96b7e0ace3074247a7dfb1" +
+			"cdbbdd66e25e172fd2bda74abde7f3b4cb5cc7ee7859f053b2f04f9de03b" +
+			"a8e96264117f502087c3ddbee8d850bf3618b4de90f7b3e562dfa57e4426" +
+			"5357236e35e71d1669226d63bca50b1b944ac07a1f794e73e80985689b25" +
+			"f18fc709367d63b8639d71865cee667536040be827145c08cf3e57a66678" +
+			"4c81115706a146eccadc7aa1a9f074b47e95bcba7db8108a13279077bef2" +
+			"64699fb87e5abf5b05ff3879d7c7c5169c7cae817c13f0859d4e9c05db0f" +
+			"74c045ecc30a51e515feea627da387ff780719395b5b9ad93179b16fad10" +
+			"5856049169dcebd43a7f39c549762405f807378e854b1654a1179d895ef0" +
+			"85aafc72c7fe1e0e1cd3abf8e20935e331145bbcece4f17ad24ebb6c64ea" +
+			"73bd98a7494c134859206c9422f7c4a057db0ae0770c4bcb08c1a6b9ca4b" +
+			"7dd8c1cdb3e4977c7ce6c1e79b9d6ad98e27d2759b53cee73ec037a8b686" +
+			"f1ff78eb8421f41c74ce9c62a90d38b75159ec925f232e0db71362f31e29" +
+			"4336f5580a34b26c5a01ee3454cba227c7f400f6889a319d7121dcea27b9" +
+			"584f33ac796d48a9a24cc5b6799ee12f10725fbc10d7cf83e4b87d9c444b" +
+			"f43e2f5ee49d8f3b531ebb58fed4234cb8bcab1b8b18bf50956506baae8b" +
+			"c1b7492250f3adf64294310387f1d4bcac12652895d4f2dce26f380733ce" +
+			"0b5820e9fcd8512a1585a49940a32fc8875ac3c9542a4270602e5e97e720" +
+			"90ed71b51badb775340429fdbe45b887fb9ee61cf9e091c06092cf0a2129" +
+			"b26572574c46910cb458bca7c63eddd29d89753d57e568323e380065794d" +
+			"3fa1ffb874543f5b0ddc702b087e91e22604d9600d37fa0dd90d7acb2458" +
+			"4cd408a4e66bb781dde5f39efda6a8fc26be0d08ffdf851e422ab1500c28" +
+			"bf6b4c85bdfa94e8aef5cda22870c39ad49c3c6acdbb3b0d58cd05424c65" +
+			"20740b5c2bce4336545eda12716317df58e6fb764fcb3004f5248c5ccd84" +
+			"f63abdc0dd2a64e447c0de4da4a1082a729d8ebe14810d396933085cde18" +
+			"318278481fdb9a748b637cacb491f5234bfe16b53a35da6677336baeedb7" +
+			"4a28c19a412e7812dace251446d40ec07afd63854c3dffbd5c0f6a9a3cac" +
+			"ee3bab07fba94800fd1fa0fe44f5f2ecb2b4a188cd02b8a2df0728347c50" +
+			"7d0cc58fcd5d54dffdbda11dd1bcc59758396ed8db77498fbe13238d3d8a" +
+			"0040194dfe66811542ddaa658094a9580d4e4b4e29",
+	},
+	{
+		key: "1285f117bd90b70ef078ae62f37d2218419e894b7d334759ddb2d88833b287b5",
+		tag: "429b2b39195a10357043c9601590a277",
+		in: "00ef065a1adb4ce7108b497813ccc748933fa8442689a7cb8dc7c1ffdbf6" +
+			"c09adfe05ca2cc5ec3acb7493f3497ee8f9cd9bb8a4b332c18e33f78114a" +
+			"c8f9a72ddb9f13494e934ad711818909831013ba195b53f5e9e5b4689399" +
+			"6d0b669f3860958a32b85a21009d47fddbc8697b7c9b92dc75d5060eb4fb" +
+			"40aed7a1dbe69dbbeb6296f5467ea2426cd17d323671fa408855bc53e5c2" +
+			"d111203ae38cecac7719c0bd7f21f6bd6a1588187b3b513983627b80ac0b" +
+			"300b7fa038af1cc8512403ac2cea6e406595202ec3e74014d94cf8780ed0" +
+			"33c570e887ca7fb35ee4768202aa52427d02c24e63f7f2cede95ca9909e9" +
+			"dfa86246a27db757750667c198c9aff4ce348f7ac51864b36ef5695df713" +
+			"d17b8f561a972d0136bd9ee9aa16079c2ab5d29ac9ab472255ade05dc49c" +
+			"b966e0c1c04258ef9ec59ded01f402d9fdcd9a2020a2038a8c78892ca218" +
+			"30136069485527069132959dab2b81c73ca590fde2a7ecff761d95a54d63" +
+			"a2664aa5a6deec163e46b5225bc98976a4f363063b0f42e29f792d138af8" +
+			"eae68d3854b5c1985d5cd1c9f49f529b0b4d2c936887b5b92cdebacef992" +
+			"c35e0b7bbd52114aff8c6b261852e28e451b02099814f809b0289cba0586" +
+			"04a363e3f969aad3d982f645ec4c549f943fb360fb8fa0d5a597bf89842f" +
+			"8ced6014a5b2590ef71524a7ad50fe0ef0e2f81b6e26b99f9ebbc8036549" +
+			"f7eacbf6ab884710c6406ff59788e03ede35c30d4781ad5af171e0623e8f" +
+			"cf5344d71165f0475e256e9159040f702b359a2963116ed135dd6c1d111d" +
+			"2a1e33e15c178ca4f02c5fb15593c50cf9a8a492f01e04778dbb81d26c99" +
+			"0c58cf50a9bcf4fe38fbfc0fc0685d8bd422a773c7bce649f7a86c59118e" +
+			"f5f857b2c72508cd1ef05e1a0c0b7ab4687fdd57437092eb49bf41a9ae8b" +
+			"bd98272ea2f8ee2515ff267fa6ae892c266a7effe61ed54984924aefc461" +
+			"6cf483dec024ad666bc797beaa429a742d1b8806f67d451b6d3a85b4d474" +
+			"003cfe9e9dd906df47da5559c41f15afabecc3e6af279cca0f2a200eb2e8" +
+			"31437e034d457fc880f60f5ae635690bce82bf6d1ad6b4f5344ec042bf25" +
+			"7d010273c861e3ac516e9ee2bab3a255f570baa32298467bf704bf6d9076" +
+			"a4c0b08a528a05cd1fcbdf51f3885fbaba7891a144fc058919903b269b4a" +
+			"29f43926eda32c38853b814a7d528156c223748d674d8f7f5448350f011b" +
+			"bfab1511001b8014e20fee37ccd4a0456f638c197c86dc116b34f955c0b7" +
+			"dee10bac5ea0c2fec8a780ac05098b51b902ca6afff4db3c6fb4f761df79" +
+			"b2039dc5f16d9402442a6fcf6c4297769e6c36824d908beba8e584ea0b3a" +
+			"91b9017baeefac651d0307bd89f517789236c0693c65a5a20f244d39684c" +
+			"eb810cd2ffd3c78fe9285d2eb9f55d133b86113efb8dffcbc6d258e84c38" +
+			"2dd8f4d7d63b65672516d9bfcc3310a79ce244b60d380128d529487f99b7" +
+			"d532d5f5c28fad8b9a071fd2fab8fd98f6d7ed9dadbd2fc4396476eba6e2" +
+			"1a1b1cc594a31fbd3418d98e4aa736cab285a2786fbbd4650e49f9b080ed" +
+			"3fda34941c28d25545395e1408fc3e60730d0696061f821a4d24123cadf2" +
+			"3af3d37ba7ce1ba3cde1368d468f136df82c02f9be9210022192aa02117a" +
+			"ef5ff70bcfeffd47bc37b920826a4d3db001f956939abc0df520f3ec1613" +
+			"ba1c4b3385cad97e42bfd15a3150711fe86ba4562f17780cee1cdf198615" +
+			"ca06270db84986f33e1d53d552b0da82397c496a23c7a78ca7641a908e71" +
+			"89249cc657c0431f1e09ae0213f28a27e6267e9d17b5bba0ea4f3c21f266" +
+			"fe538e215ec62f85517ae6bd87799ac5ce68453f09cbbc50d6e2a168f0cf" +
+			"7166ad50cb65b6c76406c326573c00e04a3186251c6181933828c58f4198" +
+			"f8208c4484805639b0d428fd05b57e4356239638f458a84000c7a7a8de62" +
+			"ec25b54d1e39d2579ec9c512fec475f243576f35efc02a1cd6b0478e2dc8" +
+			"be5f17aa4e3849cd42e76fbffe6e7d6f912d6edf80f718f94a7e48e1fc10" +
+			"6cac29627d9d4b82f05a30cd7c739f7f3ef7ea368d22612f189da450e274" +
+			"de7b61c6361521e684d639be5af4cb11fefa5fce6f8a5065c90873e504c1" +
+			"2c940571ea7bd7e9221129b83039d2edb069e8b5bb68567d8fcae34c6ee0" +
+			"cb94474d8b056cc3c7403873f2fe6db3b567a44e702e4f4813b2a264231b" +
+			"0a998207b41916715ef94e5eec281589d0a711f8e74be32bc60f43d693de" +
+			"77f21d5f7eef892abe87725f3d2b01d9ddb6dee15f40735a8fb67766dbcd" +
+			"020a93b8eef4361dc3a891d521551f65dbe6e3f68c60819b0a540b0991c6" +
+			"4449d207cf5b1c198c17ad6caf3adc628d09fa0baae7a696d84e1879577c" +
+			"ffe9b3f62669d4ea5ebab6364f08c66d170ee4a94d61fb77d60b33dd6b60" +
+			"650f034c5c9879243d5c16f853dd7a89885a9047a341b076912d47872b3b" +
+			"3de49edf7451b435698ac4e182d16c339be83e18531a34aebad36c5c7c93" +
+			"aaf121cf99ff92d3844d40740fe001eeca9ee71300d826bc3cfc87a29d39" +
+			"ea108a3cf259657ec4b967fbb534e7513ef3a96bffb35abc5ce0e890696e" +
+			"54fab515af3d2c0be6e003747504e486c0ec6e30fa4ca79d6596ae0425f3" +
+			"396e40fd37432e52c74f812250dad603b3502f97ada48a26e39fd4d44584" +
+			"6591bfa5ffb3770d95d3dbd49e9c3a38c6305796b8f7d79bd0845170925d" +
+			"575774445299bdf9d3f8ad3dc2dc5cfd3ef0293b84d6e11370851af05ebf" +
+			"b3510a22edd930797dcb76b759a9b5a77ed8dd5130e79ff5ac44b01901bb" +
+			"79603cecf674202bc5d84076ff41b3c806454ce80cb9e5fa9db77294d20e" +
+			"6d3008ae3017aba712862ecd4b32daafef1b8cc8b19ee8f8bc3835e2372b" +
+			"5cec66222ad5ea9df753c033508ec43c8b5995e88c36c13ea3465c8bc462" +
+			"ae0a659d9767db34499e9d01fb1588410257d6f588b3fdb766a66bce28b5" +
+			"e0880f8cf988a2e5eb5bf80cd7d83192b7392fbb2e3a07d51aea2b6bfac0" +
+			"d74d304f56d5af3598a0712cb09c04c5dc14194eca8e1b9b29f88344c0ea" +
+			"55638c0f8ebb70b6242b797fe2525fa1bde76293dbc0a66ab4715e6f9b11" +
+			"f7ecd8f35a20ee4ff3552caf01bb307e257ec0576023d624d6094d43d25a" +
+			"aadfce939a6808f8baacb2109c3de50a1cfada9e384cdba3e97d2c9025a3" +
+			"2377bb195fce68c5569d2d1267e1bc68fcd925ddb4acf567fb29ea80517a" +
+			"7e4056fb014cdee597333ac2408157ff60cfa1afdc363a11fd4883308cab" +
+			"d9a8fe56c2b41c95eaef854f20bf5941ed23156d86de3bd413465a3bc74d" +
+			"5acffcd15722879849c261c1bbe987f89a1f00b3069453841b7da667d566" +
+			"e41fd894d94de44c23fed08d9bdffb723aa8449bf236261240d865efd7b1" +
+			"74a4460e5004ff77f4196d1d421227dff7c78f1726df7b5eebddb4bb5f57" +
+			"5ade25296dda2e71ab87ea2b44ef2ce8742a7ad5c1e7a40e097eb336561e" +
+			"865515f7ee0efbe01d5a928f208f7c9f2f58974d1c11af0e737c673dc446" +
+			"1795da9757010cefc6e7f2784658717938735ed8cbcbd7981a1bb8f31cab" +
+			"b901c87a3218dd1195c59f64d0bc3ce8b72580fe38e6dbf1181e0090e5c6" +
+			"d162df9f31cc52fa6a8ac61897e9b4b3cb0ca2bfb38a38d9b78e46d775d5" +
+			"7645d2d6da16bda8edd8675e2ba121f7f85400cf7cacb9ffcdfae583fb93" +
+			"753d07985a00afc3a4e26c9939a5116d9b61196502f5d774ab4c7fb6cfa6" +
+			"01bcfddcfabfcd28055e858d7d3c19feb6bd7c02565add3a3af61bfba8b6" +
+			"f4b52c072a8613e878368318383143059a98a85ba521f781a8983c2486ba" +
+			"b83f5b91fce02acee0be8d0dda7489975f0506c8f363b5adc48ba971adeb" +
+			"4e1c830b5f264ed42da36d2b5ce2fdab1e63333b1061ec5a44ec1b6e99da" +
+			"0f25e7f7250e788fe3f1b8e64467d3d709aeb7360720f854afe38e190cc0" +
+			"925c6cbd77fbfccc07d8beeb0ce68e47442fadaf13b53c30a03ce317cf79" +
+			"dc9155ddf96814583695f15c970fd0b6cea0b04b1825eb26e65ea9351bf2" +
+			"f7a841ddaa8c9f8e885b7c30b9985bac23d3ce777b",
+	},
+	{
+		key: "491ebd0dddefc9f0117176772f9bab61b92a1f1de13796176091c56d1e53dfbe",
+		tag: "fbd3f884a3dc2a8be06ce03883282e1e",
+		in: "953b9a40789b206fb507ec2c5e9c88ca1baf25ad24c11a62f664db1da8bf" +
+			"dbe9b54f8e93b0bfb4adb12f8873096b8960fd91eb92a8ddb53232ac9141" +
+			"57caced33424cff943a8db129049af7e7b733afbec6637d8ee4f39d063e2" +
+			"be241cca6a339e48d72372efabceac57220692c40856532d95529adfae87" +
+			"a71c72f30244126d01a875375ad8836ef8db929bc81027935042a05c346f" +
+			"bc94dcc057db015e55c56064d2b11154596b813ee64b73bcac05d2688bf6" +
+			"f1fbb0cf3f8307b3df44c3e2dd1d226a4d0e9dc5f7482bada9611970f887" +
+			"f656dcb19ce1f8c5c86f4cbd1e4f49b18f170ecfd184028e769e79d7424f" +
+			"d01cb315897c21111f53f4d41c3b71402eea695272cb5b4e5f33abb9df50" +
+			"cbdaa55ed629d3ed7d93b43e550295502db1f2ed884afc320518e88be4c6" +
+			"b62a13f8d3636ba091d07dbc6c20c7e7fda016c05b2fadcfc9ea32f4ee2c" +
+			"4893de78ad8a1771aacf6efdbd8fb1f6ee9b0572ced3edc6313185b5d398" +
+			"88ce77950aa4c5201a256e3ae3e74f05b70faada14124b35b105a70e7769" +
+			"7184576b69708eaabd36e0ba885fc6bafd5738a67307a1181792333cddfd" +
+			"a4ef19c88497c82fccff05a8f9f732fc7505f0467a14e135288ee018aef3" +
+			"d0412f6b0760573d8ee4ab455d2789b4d22a42eebdf60616fe403627cfca" +
+			"fea672bd0a49e8e7b80e7b7b8feebce3381f2fc16819a8996a99ea230c3a" +
+			"84b510cf2e0d914610d646a2f45a14268ec1d6fca03d0aea5c9ae1c8d519" +
+			"b0e8b0f6fb8ad176b5d6aa620b253cc492b5e5645353fbd9b6c02bea48f0" +
+			"286e2c669782b5ffefa4d8f3f1037151026d9cca78e7808dfbe61df29e82" +
+			"951d7154f3c97606cd1e99300012578ea6a776dcef0811338b56606b51a6" +
+			"9893fe68f762af6c9c26066b1d503e64877d8cd988b443af66a36af8bdfa" +
+			"41b4dfb3721d1d81895884755b9c52527030afdfaecd66d4638fab1d1786" +
+			"3d5517ef7ee7d081b5555d24991810f1edde30930fd392f817cfe632b4ca" +
+			"6fb0460c36bde4a5620b9c369bf51c7d870c43998b8171a553d2f643fe8a" +
+			"58aabfce8cf7363ea978ff4d53f58284db822ca95b80306ec02a64d26a29" +
+			"c98520f1924c70d161682c54d08a2c48f54bb72980a8cf5babd0aaf0fd72" +
+			"7d5b1b9d9b731dc49bad228fe83f7347750e277a4fbd526983c206e075d6" +
+			"a03d68957b3e925a71bc1ea7304c77660d112a5d19fd21a785d4a8d7f2eb" +
+			"dc4183376d8125341eb28b2df5be0b4e04bbf95c47d2fe2aed939619cb97" +
+			"79548b752f57b723cf8295dfce69c9b7486b75a4e900f91926636f3fc78f" +
+			"7b7720a5151abdf5868fecf1e1a1d830cd6a4c5e3cd739da4432cf1fe2af" +
+			"a1090d6a1eeb32e7236ecfddb9d07b97220ab8e23edcc93d91abc11b0c30" +
+			"460d2027869d1c2487070cf60b85ad0b8bc5df566f6fdb0e58fd044da530" +
+			"6d277e564ca6cbfa820ca73fb6201b240a5a94c4ecd11d466cdc44046a66" +
+			"32478221bfa69b3a2cebd16baa302a573c90895d7f4cab453b11e3a4d8bb" +
+			"b5a9bf264781ce5b9796e3c47d0fa57f46b923889af4d073270a360dae8d" +
+			"51d85ea916f14787c6500d2d906ccaaa92d20d93edd09139f79bfeb5fcd9" +
+			"8c1cdbcbe9f2587e9c9094e3c4a32ab9ba56f400b929e80c0551f953896b" +
+			"e8eda6ecf22e6d4a541957dec21d6a9cf388ff0ba58169ab934902892a58" +
+			"86e1126b16118e965a271495ffa339c49466209ed3875b568a4290b7b949" +
+			"69d0465744a3c2a75c599c3a04ab1a3fd09125fe8f45724b2f48c7822b9f" +
+			"ef95af4b758ae66a8b6646df7a0a1aabe2a24c052fd6d30561cae0389263" +
+			"e3388c4c1effe431a04356c334aac64f36593544885c4b7295b57dc39638" +
+			"b665b22dcbf7dd6da867615de38c6a575cc66391135d47f8e1f0c73c6129" +
+			"17ada4099723933a758d83311b384364263cad5fe14bdd7c825d9601c400" +
+			"3537a5aca7f9da4710c132ce8b0f1464cee625633ef57f507739a0ab1cd2" +
+			"21ae634d4d0b3ff07e9ecb1baaef0a82a97279d46543a0464855cd62c07d" +
+			"5e890265612906a9eac88bec07b1dea5f67054c31ae40f8c673296cc5df7" +
+			"f0dd8cc9e643b44fd90dc2d1e870ad8acdbe165237642fd04c00965837cf" +
+			"bd2344ae830887a5719a3c16dc8ec08bd9131d055bfb959b64ff4cb638a1" +
+			"002a4fe02e369871cc4e3ffda17dd85343e679fab43e11970e60198b424b" +
+			"676ab17fb0dee10cc9c2e92b32b68d5b05b7a559176f822850c0557ed98b" +
+			"7454916e32af549a0027db95f02b88cfc5e7e05f28f53757dd97cc0f0594" +
+			"212f8801e58043cb17b040413c226dfce2104a172d218caa4353890de17d" +
+			"be1f53af6ceda24b8781801516cc51de9ca459e469b3c322be13d8c9541f" +
+			"755c518ca41a0ed42e44b9f87faa2a968b0292216e9f3d3e8987282103e5" +
+			"016fe9f7681496e1e8d663eb2d8bc30b41d735465527f19e336a98d2dc54" +
+			"d7c020bfab30fe6c62cbae7d09f84af69bc2c51a1839ffba15015d381ba0" +
+			"a44a3758771c4f18d13827f518f30bb74f4bff29a87d4b9e949f1063f63f" +
+			"662721cfd64ffe1dab3761852387f78fa83fb48ae2c75fc567475b673da6" +
+			"fa8f53770b6e5a3c9fad951ec099c6bc1e72d1c489e1ae620e7f12ddc29f" +
+			"ed65f29c65cef75014b999d739e2e6e015f928a30f2fee3f2e59bf65b54d" +
+			"89948bf2bfde98b076e5460643952befd02fc1b0f472a8b75195c53ea296" +
+			"6403b9028db529cd04b97231bac3068855fa211f4d976a88bc27a0088f04" +
+			"576e2487ac0467992066ef7667ca8429faee92db38003728e5c219c751f6" +
+			"6f011b5d679fdd957f4575a0cfb6b54693a9624f2c7e66c578f5f0367005" +
+			"c66addd1e3ab7ea1ac404e357cbdab9438b9b4f80b3a6761b864b006f1df" +
+			"689ae4c0434b06b686d5353d3e421b57381ea24fdcf6199195ccdb3d5cf4" +
+			"623a6bb1f9eba9b22fa15395f65f8093b5f90455061c1cbf8128b44a31e3" +
+			"910862a59e187aa7f4d22e0317ae6c177cef24eebc44171f70c25efac73b" +
+			"38ada0cba0b74f72d1c171277a734819c1111ebe46d5db20a6ff20e2c1a9" +
+			"a57edae95a3c1f80ddf2b12c86d3df0078a7bf68695b16ccf92053c727a4" +
+			"80586b8d87d0d1772e456fde0c20a7927f351a641bff5f22f9ee2217b6a2" +
+			"d0983c8102d7d5356dea60a19e105ce366b9d000987c8c33396569f97c56" +
+			"2d0fc0bc5859779aa10efd1f8df0909c307a9110083cc6d9748456c9bddf" +
+			"16dccee52b7974867cec718bb0b76b3353379a621257094277a30148ac38" +
+			"e5cf67ed7cc9c1bae12dbdeb99d7d880ce98e17f0dc93c5330d1824a3c9e" +
+			"ffd86f89e15b59a4bee5a48d4f674766896e187abaa39917b83f8d2f3265" +
+			"bbe7aac44c9f8d92f775fe6493e85ab44e6e28f79f28eff156c21e1abdae" +
+			"d10a291b88c4020b1ae8be001080870847a852d073e82bfc751028ac62d5" +
+			"6aeac1b18f2cff1c0c7d336bf08f8cd5099d9d3b28f9e16077e9caabab49" +
+			"f2d234616a7522a6bde1a3b3c608df4cc74a6c633d4c8068138abda8d26b" +
+			"4ca70f95d152888fb32bdee5dfad8ff4a5b002a0a327c873656db8d6fdd8" +
+			"ed882e47ce8e47c729e1292db9122ce2e9fa275f9bb986eb7e0a1dccb7cf" +
+			"abd0449c92fd35e2aedc4aa89caf53bcd28170cae85e93f93988e723a896" +
+			"10cefb4edb6fa545835fba3107e21dceb272c5a32da26fa77df070f41d7c" +
+			"ad1d68b836199ff0f1221e36b9b976b5e69bed54b5bfec67fe9cbb383484" +
+			"696265204797634594bc335150daea92dbc1004f613b4c27bf5c699debf9" +
+			"4365041b5a894701da68a93bcb61f4e546c553fe61f14ab0322b45915da6" +
+			"ecacaa093b0071f2516ca8c3fef2f1e3c403993d734403c47bfe5f4379e9" +
+			"cb5b613fde3c0d880cecef4101aad8b8b1c60a92ac5185f6c243fdf1711b" +
+			"0b56f0fd8e5ed6cc0f99da888e4f156455a0f0eb365b8964347eedd15d80" +
+			"2f297977af667ed1376dfcc610f5152421b97afaaf16f9db57a435328595" +
+			"b9aa00b5ed9ff106c66970fafef379f4d2f98f2c5984ea05aad64651fbf7" +
+			"7968c8cbc4e959859b85302a88a3c2faed37765f3f6ced59d8feb6c72e71" +
+			"f9d4497d98bccf95fcb650f29131e1df1bf06a5443f8af844aa1a7b5a68e" +
+			"bb250c7de3a65ae9b1086cf83f832050e55030d0f67c6a54ea2a1dbe18e2" +
+			"8a96c9e0dea2966997bfc5c5afd4244e3c8477c4f5e8bee8fc8ca9a5cde4" +
+			"d9c5a2c7f3d2e811b1de7ce4279229319e432674c609b4c8b70dc6172e9e" +
+			"653fe1969bbc2cb3685e64fd81d96d33",
+	},
+	{
+		key: "b41db44465a0f0d70093f0303bbd7776017bca8461c92116595ae89f1da1e95f",
+		tag: "d8a111a09db22b841fa28367ce35438b",
+		in: "b074b0984fb83749586881e8ec2c5ce9e086cfb2aad17b42b2429d4cf43a" +
+			"0400fd15352d182e6c51e9338da892f886f460d40bd178d81c52e9ab9c1c" +
+			"bdd812594e6fe7a9bb7fb729c11328d3288604097600a0c151fa3d9e4268" +
+			"de75866558e9f47d8dd331994bf69f826fd4a6cb475ae5e18365f59a477a" +
+			"dde7fbcf7e40b4e3dee020a115830b86f0faae561751e9b596c07491c42d" +
+			"e02fc979e69071113953729d7b99f1867116d058a90f1b8c0f9ba12c6322" +
+			"4ebd1b563a87734f5d6e2d4e6715d5f0213e33316500cc4b23784f78a9bf" +
+			"13fdf99bfe149cf47aeaaeb9df1cee140c3c1264fe89bcde8acda6bde16c" +
+			"e3d770ba51950b67ad2c5232ae0cff048ddfda8540cf18e673582dc96987" +
+			"4b127f655e7d4e08859f2c6b95403cd5b4e2c21f72bb872e49e592306286" +
+			"48ba1b16fc9637709636b198f9a297aec364d4c3bc869dcad32b1830e434" +
+			"b556b429136f0012a0a0b6fb3797bc8668014b010ea51674ef8865348dcc" +
+			"197672047fcf72e6b6910a0e32a4f110d85e28db0e338d9cfdec715a8800" +
+			"b4f007a7951d09e41620815848c89f8768344c50bd522c46f64ac6c98e53" +
+			"92176651961c7a70b62f3d1819bfda674e2ecd3167415edc4b97419e8ae4" +
+			"9974b56cd8d52e1d05b82610b59606a750b34844ca33bfc9b21fb970738d" +
+			"b66f48928df79cf67730a30b0b612f8c15c22892120548ab460a6b9bb3ac" +
+			"e30554c86c9681c797821a1b1ce91d0e87fe90ad4097c974cfbdfd5c4c24" +
+			"a5f808f388e1b1473e858f48a387614501c8c39d6973ded69b1764663cd5" +
+			"166be02b596a49e392d637e3d8afc91323f7450318b79d5488c040e346cf" +
+			"0cee512044514b570aa66bb98d639a9ee23a7cebe28474592623d082873b" +
+			"73efb3eaa4721fc4761e15a390497cb13cce181107e8b1a0186b9e47a5a4" +
+			"b67a5be3cd88a43d341ef63f10af6970aaf56035db938655020809033a92" +
+			"8d4fe6d2f5424fbde2fe82adfd991d388edf293cb4e3eb68d876f225a5f1" +
+			"58208bcb1aaefcbc28d6763d267406aa8d6ecb413d18cff7a318ba031ba6" +
+			"0ac4560748c248de64eec56dd4540124b38581604f502d94a2004f9eb1d6" +
+			"edb009e16af6c6d3ccbea79b10743da98aee7ace407a90c6cfdde694f36b" +
+			"e0271e722618a457be68619b980754795f4ac95ebf4f1820b85ca8e3fbff" +
+			"a2430f8e01ab422d7140751f7741f2c921400dac404b04e049736738a87b" +
+			"6f49bd54b1b447b922c473831a65f224ab84fc96e4551a0333bc6187e15c" +
+			"c0f0ad628068bcd7c043bd1e3036ec01e7fdc3d157476149917baafaced0" +
+			"15d09fafb92181a0ec65b00c9c13631e65de184377416e04d3d93b847e0e" +
+			"286c1d88245d4d550d30d4fbfcb416ff26a39a94275631c2deafc7cb6780" +
+			"f149e4d0e9c4515b708fcd62be5252485407a6ceeb9247de34e0266ef384" +
+			"976f6d31284c97468b3b03e951d87a5a00836ea303a266147a79ff3431b4" +
+			"b382e86c74d92661e0f65e266b7d569c03994b667a8137f3080eda2ff542" +
+			"0f0b52b427558dc26932a22a615c9e6b1834a251c6b68fdfc0bbe0e8781e" +
+			"36adf669f2d78bd23509ef7e086634e526258e8d11a1e0be0a678ac09c7b" +
+			"b4e3c5758504011e701dc85997fe2a3e40c7af83f032bdbe7adc10ef1e4a" +
+			"666946c2bf31dd8e3a383211c9684d5302f89dafcf77976d5a02c14e2462" +
+			"09d2d99918e82402cb0eacaa12032ad8316315af1b3d3bd5058f7c935d35" +
+			"ef0d4e71373958fd5e4140a9a586d89c53e4144c00148a4706a524896eb0" +
+			"5b1479a0de5d3f57be46b3f5fa4e49bffe027c81a33e37abc01a4cafe08b" +
+			"8e21fa86b42be52d75d6407e6cdf399de7aedb9b61a6917b2677b211c979" +
+			"33536664c637a57ce2234e3319fe8b4a77d7285ae6347464dfd0aab3e6f1" +
+			"178e0029686770d3b0dd541490b097f001e95f27efe8eb16e4747937d643" +
+			"cdefd49e586ecad541270cedc3064bdb7c79f086bf1fa8c666304d977a15" +
+			"54ae268881e17d8bc3fe51fa9969f7e560e3d3e050424febec0998b35f2a" +
+			"7378b2c3e384cbfc80c4987734d76c78224cb81cc5376f88f0ceda28aa50" +
+			"44e956537c3ee209071d84a66173384e0aa466d989759fb1f2f17fe627a0" +
+			"ffeaae7c5a3884b237f5151278a07117c2e833f1815c7e0e0b1611f25058" +
+			"ca338d21deb1a571faf1d0486667cb7c58e2814c3722d24fb77ce1b7e018" +
+			"2ae5746442b5ad00208b17c0a68bab4df8a8f36edead4fbe79b4c9220dd6" +
+			"acea6d23c7caaf6ce7cabeeca677a1c764d610ea6c7e994d6a9c88f57fda" +
+			"ef160b251e7595578ea2cc1441d480c14b8b6945e76a001891b1f214979b" +
+			"c52ec15e9480d706a40cb6e3b259ee99a9e84e63a738f1b52cf71c8ecb04" +
+			"fc833c2c680bfed587aa1541e5ffe8bbd7b21302bbf745011e559f94f952" +
+			"8b7fad8a37f6d855306a5be22725859cc950bcc334179d49564af3b9c78c" +
+			"e1de59a9cb45086a33856ba7195c17cef573950155bea73ed16645768bf0" +
+			"a5cefce78ba3ff98a54a8e8afc5dfcb0d422bd811ba9b7770a663b081dbb" +
+			"40aefffbeabca955a9638830f0c5d70663cbf5b26067cd061c4a3f5cf8fa" +
+			"4b6678d82d9a2aa33f8538b7499a3466f6b0ae2a1daf280ab91a6c220684" +
+			"12705245f353b4b83db50bedd3bf99d42bde6363fd6212cb745467acb007" +
+			"b678128f6580629a06171f7f3af272f8900b801af3bf47439167871e7b0c" +
+			"33f198333992a6c52c32be46071738cfbf245937d48f816ebb88ff0e726a" +
+			"dc41de4c771ff0bd320a4c0b1fcccd9fd6c42ec9c5185943c70e9a4b7c26" +
+			"a980afe104bb1f99576671a254704c7d4233eaf9915e1d56c103ba9f6e8a" +
+			"46aff466933bf58c9842796ae9cd21f7ac6aa96ef42ca54e390203bac354" +
+			"b7c1de7d1887c48255201335f819020e2782a2ee8af92ceb206b651ae92b" +
+			"3f4fdefed05e08974aee0a353d104b1be9a5e75c7f958f1981271b0a6928" +
+			"05a7a2f28a0448d86102b4fadf9ab4ec2f98e31e64fcfdf2b524780b3342" +
+			"7a2a3100c2032fc93199f3ea7a9e8063fe73282dcb1fafaa9496c7da868f" +
+			"dcf33bbb761df0bfc6fef30fadd2b6efef4fd3216a8aee48a2ef28102491" +
+			"cf7278b567c272d1064a277eb193b3f6f01df641ddb729f72454943cbd3b" +
+			"671ec077f9e3548f5f57d063c653ebee4f228a78f8a128d26f7f4b44160a" +
+			"07e942bab87b2d043c77ecdf10c1a419e0a1c4162a99c21d4abae0558b8f" +
+			"4dc0b7f1ca3892a6babf71f2f70aaca26bb813ac884ee5d71abd273ff1c4" +
+			"add230a771b678afbb12a1ca7fbcb2c0f5589c9ce67fe8f78a8db87825b3" +
+			"09ca34f48ac35aa7ac69c2fb2423807650fcf47ee5529e9d79dd2628718e" +
+			"230ffe5b83f9d5bdfd9c5d211282e71cbcacf972995bf1b13d21419f7fa2" +
+			"8829ed1dcc459da35883b9269a474f7fceff01d44ab78caf1ef7d8117f50" +
+			"cc83eb624062b149a6ed06ddd1cd1feafccdee7122353e7b3eb82978ca69" +
+			"247fde52d2d6cfe7324f04af5259e1b5c2460889da4541b431ba342a1c25" +
+			"3a1b1b65fce7120829e5466e7ad2fe4e0f773c7c13954a9c92d906c91aa1" +
+			"de211f40916596bfa8245344e257e5907a2c49ebcc864cfbe28663e700d8" +
+			"472c50355313d5cf088e9e8a19cdd85bcfc483520498c6386050e53a3ff8" +
+			"1e2b77b55b116a853d71f60d621265166cd7e95ff5cb4466226d7cef68ff" +
+			"d0a35b61e76a43cdcfa8da7fff9558e2f89b981ec6be632b126303ca1fe8" +
+			"53d5c628d967d39317b60ac904d6a882beb0746f6925a86693aff4deaac2" +
+			"e5b64b611de86767d55a6e11221605508b1c5cc828251539b1b6f65c2c04" +
+			"8e65be5422c1b11194eb687d906c559068c0a810713b23b30d8b17f10df7" +
+			"0962c5e7e782aff7bb95adfe4cba9d90b0ebc975fa56822025100b5cb8b3" +
+			"8bdc8928c1a2a8034dd66e2a763696d7ce6cef4dd586b83f7d01749d37fc" +
+			"4fe8d7abd324d4ff1efdbdbfeb0a2fbb8b266fc2bce8e5e5b95d0089e7c5" +
+			"d7de4db837d1822ac8db8198889d6bfe778d0b19e842f12b5afd740aaecd" +
+			"e36e2cefc2cf0b082aa0c4f75684d024b8d828d8f2911fe1aae270251f62" +
+			"4f49584e40bb193577c9d8e04eb16c094653cdf9a15fe9210f724c7a7c73" +
+			"74cfd1a74abb5ceae88ea54f7e7569f8eb674529cbec965ed05bb62f1968" +
+			"8fdaa97297268bfeefd06eb21f700cc56f9bf7f6cecbbbe7278ada8399fb" +
+			"960371a2d5cdb852b11c9fa17650e614c5297bf46cb7889d52bcf49d2560" +
+			"720852822b75bb16524d88273cb366b84b88282da91875562e5a1fe73973" +
+			"afe90e5cdd3f5381612d3ba7bfa058d023a9326e403ec474d8938313fb32" +
+			"bdb5bf899b900c3818c43c8a0af6a061bd26e847ed75983402ee8a9cf4ef" +
+			"85bba5545a0d329ba81495157eda0286f1917de512fe448251697dea406d" +
+			"a510adcb05",
+	},
+	{
+		key: "b78d5b3019688e6ef5980c17d28d7f543ca5b8f9f360f805ee459717ca0d85a1",
+		tag: "f01babc4901e957d0c2032a7279321e1",
+		in: "ba7d35b2ef8af1118bce1e78018c9314b0c8c320591e103d23f715acb05e" +
+			"dc98fbc618de06627661df5842dbba9f604c2d20d664e5db06e949b11d49" +
+			"665088dbafdb0d39d20beaca7d723f8dcdc57e9c5583d303b6cdfdbecf95" +
+			"7d8daf2f1c72b2a6fa27e3d18841f4841abafd334c110cd2b74efb6191db" +
+			"ab9b8fc8427ee17664082f31db98d30bf15dda967e20730a9ef525abe9f3" +
+			"f620e559ed22bf74d347c9869f0311f33da7f1a3dc858b3a8aa73a35989d" +
+			"b055a4a2c269c95e352259c57de8b94d8de48984ecde426d3ef60ec1c7b4" +
+			"41cc950f7764f55bd0cf52d069b9ad446d1f765f35d02ec104ffcc00bf1e" +
+			"dc1b951ef953acd19984ff1b41041bea0e9f5326a7c9ed97e6aab42174ee" +
+			"971ea1dbe2fd1c1f67f977ab215962b0195417170f6b7748fd57262424d6" +
+			"cf7c235b34425f4047191232722932213b3eb73904cadd6a2e9c7571d7c6" +
+			"6c2f705b5039ff75e5e71c5aa738bf4177653e6eb0b49303a4bc0e641e91" +
+			"2691f217296a3325431d578d615afddf47784e4618a2ca40ccecb05d621d" +
+			"a52f272b8cf84f7fd8177c83af1580d25a764cc06436d67171cb5d1e3b39" +
+			"367b46d9a59d849d87ab6bfcf3fb9bac2b1ebfcd1cef4459e74b0e1b7080" +
+			"dabd2dea79f75581a55de63c4b23ff67d986ad060102933fc6cce8d614c9" +
+			"c86dc84068828dd9e21ffc5665c809d83b09432fd315dfce5d7a4ebd8143" +
+			"181953e3f8716e47b0b30cc1f753e31a7d509f2dbd4177b6da310cf3cd02" +
+			"5db270adf98e96259a5ae1b81f5be4d5c76f502a612ca73c76b91e0ca695" +
+			"aa921f9489948619482c2956205ae71fffc3aba4476ff754e4878e36c763" +
+			"2c935c076857c5b90cd63ea4764efbcee53e2ddc9bdce54b1cbbcf0e7544" +
+			"d023e7c2b79419ad92221a1f76abe31a8236e370d38e2493cc9ca2aaa811" +
+			"30fc713d11f500fd071d6eba6861e8b0859b372e62fe60b627a96c377f66" +
+			"236aedf307e1d148a61bdad072b93d7d2a73367c595b1e048f7023e72729" +
+			"1ec508326f5424a5bbf4e010d0240b71fa9137e6642ab40c5e4fff79877d" +
+			"b3253c663a221b49b3e77ea307c7b9f3f72a0f3a54d0112c45c64a0c0034" +
+			"baf2b55ae36ea6f811bbb480cee663136474dacac174c73b1e8be817916c" +
+			"fd4eb1876582bb3a36cfbabad91776aa676305ddf568a86e3a5eb687fa81" +
+			"67771fca7b5ca00e974b3cc3e322b4bd9bcee2a87d0ae7976da5e04fa18c" +
+			"219fa988d4f6fce62f194b05c26ed3ae1b066cd9751a2d916d53426a454d" +
+			"58f9c3b2fb49374e5791b412fdee1b6029144f1ca787f56fece4f64f4fac" +
+			"bfe4cfd8ba7c807a83cf44008fe5126a283ab2631a87acd8e2a3bd10979c" +
+			"4b07a84a49b0687a45a4798ded0b5e9b2acce30e714d78395bfa8f33ca91" +
+			"e68b2138bd67d8a694cd87c88dcefcd101a3b408d7a9095cc6a4b38898ec" +
+			"c8b375f5a67deaaf73eb7e99b10314ca6bba824658bee85dd731d9a1475f" +
+			"976b7c0aed4b67b088f0db5ca5091273217f724969dff6cf184181377c45" +
+			"5722beb23fd9d097a82ea2d8d527ba6284acc20cb30f2e52af28800c61fd" +
+			"1faf9f4f619550e0162a1a63758e202533889b27420fe7d0eac9a47a6e11" +
+			"1d80054412340e0426cdddbb3c7b9b823b8db3ef58230fad7a3ac21a7805" +
+			"d30878d4ea78dda95c951b7a5dc552e9434c35e03e1dd88652d3714f8fbe" +
+			"a39936cc0717c2e0335371f2a751204f5d9386baaec853f019325edfd1b0" +
+			"719d1fdac3fbd774a64bf957fc54039501f66df94b5b9b82c2076c597065" +
+			"dfcfe58b2e215a3734066aeb685ef97759c704b5f32dd672ba59b74806cf" +
+			"ad5daeeb98d16f7332ff0ca713d541c84e4aef0750bab7477ea707e2e497" +
+			"e12882dbc0765106070ec6a722d08fe5c84a677817b28fa3a41a6117f2f5" +
+			"465c2a2f0eb2b8be4f36e676b4115008bade3573c86cfb1370c03b6b0dc4" +
+			"bbbb0ada4dedac10a593655068a26febc2bf10d869cac84e046c9c846ce7" +
+			"927431f606f07b92abdfd81260199ae05ed01dfa07088c56a6a8de9c6d51" +
+			"d61d6a6d3f9904c216ea8329467a006a3d2495a768a39ef99a21827d2def" +
+			"909bb743fed7209f7fe59ff1c1e710095b05f166c6173deef5c6ec4105c5" +
+			"fc3b87c8269c786bebd999af4acbf12d20453b125f338aee87e9509ee405" +
+			"9c9e568e336304d7be9ffe81d1700555b0800242d9b7450d7256f2b17f6e" +
+			"d46a39f67bb2980572ce73169e352070dbafd4c7fa5a6be78cf9b72981c0" +
+			"a01f1e1e30ee3736c59828b791d2373799854497a28a44bbe0e074925723" +
+			"4986696fbb06ef9ea83fbd49c45a583ce12ff10258ba06127c67b0f66dd1" +
+			"09f1366d8036853973d8884f93de54fb2a12949eefc020717eff47898cef" +
+			"306b5de068411f1e113ffdfe2556e0faedc3e27d95a45b8afc15ba0eeeff" +
+			"eb86da7b4324e20af80c62bf0ceb4aee1515f5912f71c6bf2febf20123e3" +
+			"dd3a82dc1e58a108f1039942dcdacdeb1f0ad0b2ef34488d98d6a52311ae" +
+			"acbd03c12f6e775e375d5979c7c295bb049f2cfd3580e3da3841ddd8e6af" +
+			"4de5e6512ca79cebcab9280554524881da37984d340e8f0163fe10a02ed0" +
+			"88682560bc6d3c4dbcf1a542ffb3dcc2ed16a2eb96896e8269697ffeb50b" +
+			"73f2cc354092e782a0072fc12e1eaff117c2cc8a5a1ad8b47802ac9e23fb" +
+			"91a0cef9e4027595e0885464e61563093ee2b1dc5f22dfd04af7de6a70d5" +
+			"977d3751a4b3cc0c71a71c59c0534cb1f8c0eeddcf1c0e1b3e5ad0d083b6" +
+			"6e8b998ddf9ae9d3b365c851d42e995b9afdf8d66b2ac40bf514ce32e456" +
+			"0880afd38c42c08926067eb243c4b1184e667ba756c14ace5f525eb48df7" +
+			"ebb429d0a23d159664f8021d27dc7167081de331c7114c9c6456e1ffdb42" +
+			"2172a81c06d8deca995e158c48df27261a83f83e0127f5e056a139be9b76" +
+			"e25dadf534d3d1ed6ebc0b5d77d51e5b90ff86f30d4023066115bc11b33c" +
+			"c827b1103098826d0bf8777176b2da6f1e5b580e407ccf7e614fdf4f5b53" +
+			"3ef6d30b20c1bee61eab90e983b1a97173a62720ffd27abb8976a948d532" +
+			"d06596c23b0ef31c79831bead8f8e99ad209af3658cac0cb3c3f9c88379b" +
+			"9bc871d8e84171d53400902da1243f664afeaff60bd96ba2639a7644676c" +
+			"a79f43130af12ba2c877d67f7ec030a4217a72f5368af7c9f24e643db6ac" +
+			"97a04adaf57dbc53762d8dfa1afd49667c4041adcb5ec303e191b786273b" +
+			"bb065cd9f16a3a4a399c6a7aab9c1a6604998264e8b3dbd13d8f2228b13b" +
+			"2c2b9fec5055d8e9f2df1d9a25e4bfe2029776389877bbef7e2c7621f06b" +
+			"c0b7fc0786e2b2d042483ccd4a59d2872a6c5ac73e217123e5c8401580a8" +
+			"d967e0895aaa28f4d25ce68c90b4394d8113bc423e9fae46ac47bc2ac191" +
+			"fb97b80b5a85feb2bb54f84c493235c1408662fe253c6786fcf6fdb8be87" +
+			"dc66a72cc847f94dfb5214af5905b7039a7363a1b23a07853daa26862783" +
+			"ba08a80846fbb93ce98700a4f9961115128dd67bd7d19e0c588fdf6196c1" +
+			"1cb0154002ae862f11421f5dc3a57b6c0870b452272be556a1d14eab1af0" +
+			"a91ff5b89de6bbeed6e03bc64f5efddf9e54da71c594bc5ef78e0192cfde" +
+			"da36e4ad1a6b0b51110c1b24d20dea1f19e18cb1184d80189f842d4f07ac" +
+			"834744dd009aa3771b1e5502fe4b65a403a4bb319e1880ff6ba852e90a8f" +
+			"4fcb52cf374c88408428cdb1255291b04ed58c992310955198d61fa1fd9d" +
+			"762d48f2f65a287773efc67d549981c291b427889d3e3dfc0cc6cd68415c" +
+			"dbed81b516786dacf431472a7dfc99688d15bb6c1b85b1a2015a106e5de8" +
+			"cb9eec4c80b17d00fdcf4a9c64de4643a95dade8fa9f1bc5c839037d86c1" +
+			"3800a244188e3b18561a74912ed72f99f2365f0126732d037dd54a3ab77f" +
+			"9a9f6a1c1469ea92eb707482066bd4990dec4d7614ccb4ea6dd4deb8bee2" +
+			"2c4dc0b9b4d4cc70a500d2c8a5ac3ef88a38439b7dc254a6d920cfd317a8" +
+			"4d7747148c65b6730709e43369d4c995b03c58b9df444f77f216944e70f6" +
+			"6446554d8d513b8f7f28ef0a2d7ad5ca2f6110304196953247a7ac184f68" +
+			"61fba896c2d5a59007ec2b2c8e263957e54cdc1f3b4a145228823fdf0960" +
+			"c33a28f59b03ee4be21001d2f56fd49ed14db33b2c4eec2c3f41b250a624" +
+			"99a9b6602c1e838526a54cdcd058af1c252d56009d4c7769deace53bdb66" +
+			"543f5a081cdde775e61efa70956fe2a7a6019a164c6e413ded314bc928b4" +
+			"aebccb946ffdf3eb33e187bf421febe26112b3262a526de65678cd1fa03b" +
+			"83513705108fe0bb87aa99aceb28af3641c46a2c4427cc1063de01aedaea" +
+			"fba68155d4de494a27ff6b7fcc8f5c5c3f7d3a115c397a1a295bc55aec8f" +
+			"7f150cbce2a8aa4706d54ec863877bb966ad441c57e612a1b5d438b98d9e" +
+			"fcdfe6d4f66e885f96407e038015cf974ae5a3540692b054d2ddfde59b28" +
+			"ede7e2f581eeb56c5b88e2779aea60c1d8ca6107b0cdda1ac93e6c7520da" +
+			"edc66afeed12f980e20e1e1c327d15ade4bb90de30b011a9cb33855ca3ca" +
+			"e2",
+	},
+	{
+		key: "2b0b0fd3347e73c2fa3a9234e2787e690a11aec97a1c6d555ff7b4047b36f372",
+		tag: "81b1a6633f849ab0aa7baafa58a5d9b8",
+		in: "427f3a7a5f1142ffa68e83df5f917e07b2bc454f3adce068a8ae9e0908e1" +
+			"3e0099aaa9074697593c6d8c2528fedddeca05e3888be1a0a201c389a72d" +
+			"20cb661017544d95a431e70e7c6580d8fb46ea4495bc59db6ae2cd69510a" +
+			"02426c50de1b6110120f759960605aca718d4d0a497e003e1ea2b8ae9a53" +
+			"df3c1eb4f704eb32f8f05eb08cecba0fd4a94f0daa3b0984c30a38f94b7a" +
+			"10cde723182d30588bc40f1f9d38a3bab4800fdd5148e34e396144763696" +
+			"c9b3e9b8adfdb337123d54237c7413f98bb2056152b256e37a27bb947c67" +
+			"240fa3ce8da62ab367db540bcdd9eb873d6c71c75a08fe99b5c11ec8e6af" +
+			"f926d2adfcf073479de394d4aac5fdc6241824d944b8773db604c59afc01" +
+			"495ee755905e5616f256c8a64321d743a1c9368d46418826d99b762e2f6b" +
+			"f998d37a995969cdc1de85f0ce3987c6550459f5e5bfd9173bfcb9e0112a" +
+			"d91f092de446beba14fb3b8ce3fb2f9c941815b2cb5a3b406e2d887b7912" +
+			"bba07c8dc7caab9836827da93ca71fa5ada810da1e5e9b09738524564d8c" +
+			"923746d19c78dc9107b9f20f653e05d7f2eb6bd90cf5eb30fdd7b587eb46" +
+			"74a1064c70ef0af2e75373044d32b78d96eb1db3112342d38dca0e47b96e" +
+			"9307fcdd711b1c66355186369a28481cb47ef6bf6651c2ff7ee4665247cb" +
+			"12b573933d3b626d1c6264c88bd77873c2e73e73ee649216bf0b6d6615ab" +
+			"245c43569d0b8096596f25ceca8667661de1cd60dd575697370ebd63f7e9" +
+			"5333e8a2cdb829b75ea83d72cd246d50358f7c094c8a515805fda03165d5" +
+			"21391617c9f9a2ea562b419632df611a67912d2b369e5e505dbd5c719253" +
+			"16d66cd608cc4a9583a8eaa4661b7279870345fac3031631c1a220551527" +
+			"5be7d8d89b71960e687aace3a0e8f206e475053d6fbf97717b154c75406f" +
+			"2caa97d1ab66048f1c99281c188a2f37b8bfc736c25840a9130ef2031c05" +
+			"6acd9dc10592eddf94f5bac85319b10ae46cc136a0738aa803837287ed7e" +
+			"dafe08d1fcf31d5e63763e39a5e1f4d7d0edab368d44e63fdb33c28905ff" +
+			"d6be406a024c017081b4f2d70860776e9d2556cd008fa5017b58733da13c" +
+			"634938407a118827a80baa28d4e605db59430f65862b90cd8356baa287b8" +
+			"4e6d9199fd80abb9fa697e2c2c4c760128e4ec0438388cf407e2a2fe0f57" +
+			"908187ed8efd4c5cb83cc91dbe6a11444eede85099149ca82921bc28bdd6" +
+			"b9999594a41d97307f8854b1bf77b697e8cdd4daead2aa49fbc571aa44c0" +
+			"bc84a57cb5fd85f06847ad897ceaf449eec45bddd4e4eb1e1e119d15d5e7" +
+			"90957e686acbdda1bbe47ea935ebc4b8c2e3cf9b7157cc6dc03bcb19508d" +
+			"a9e19cb76d166da55559ec7e0995d9b50c6c45932d5b46eee400c56d9dee" +
+			"618977dcf6f76e3e86bc5207493afbc2aae9f569ec9277f33d9f61c03d59" +
+			"dd6d8250ee8cb3e54e5e941afb74f0735c41d52ef967610c9f55b2b52868" +
+			"4b549a99ae3392a7237bb52ff5f8d97327e2837268e767bed0bea51f76bf" +
+			"88bf0286bf22b881f93f1d54fab5cd4e3c148c96c39e7aeef375de249df0" +
+			"4d89d1bd97a7afb2be0cbfd3380cb861d31e4ad1ea8627721e4518b9db3c" +
+			"cda20273ec23549c4adc3c027e3ac9558de2010a0263c1225a77dac8be60" +
+			"d498b913f91391d8b2656ffddb06e748cb454dc2b7226745f11030a6b9ae" +
+			"09ac8ac428d9c6500801fb540650c94610ab70465b1210c6db2064dc84dd" +
+			"7f52573f8f40c281470e85176c85ec6de3c718663d30ad6b3dfc1a3a9606" +
+			"1936744357ca62fb8bb066aa1fcac6d7a2adf0a635cd546bef39fbd3ee0a" +
+			"8802ab0466ec9b049b5892a9befa4377cd199a887c34569b6f90852139a7" +
+			"86babc0049ee2b527aa96b988237a52eae8b4b49d2ee15ee5294118cee62" +
+			"3c3e11cecb836b21af88555f10be2eff8379beb615b7b3d6c01d545cacf6" +
+			"61be8ebbf7a3c58ac5e0e7b17997659a2bf15f2b2e3d680d142fd29d23a7" +
+			"aea9890f3ff7c337fce49ecedaf38573edfae07810ba9806723e576d687e" +
+			"a11700b8ccb96a6559259c367cef4e3999a05a373ab00a5672ce8b3d1dec" +
+			"a414187f383e449d10021b73c1f7e39ce01516b7af96193f9993036049fc" +
+			"72ac059ef36b2bcfbe13acf140d41592880fb8294ebffb98eb428ce9e65e" +
+			"1094521bcf8ecd71b84c7064539a7a1aac1ad2a8a22558fb3febe8a44b87" +
+			"72fc00c735773d4ce2868a0b478ee574b4f2e2ceb189221d36780b66212c" +
+			"dd8fd3627cf2faaa23a3d0b3cd7779b4d2b7f5b01eb8f1d78f5b6549c32a" +
+			"cc27945b5209f2dc82979324aebb5a80ab8a3b02129d358a7a98003e701c" +
+			"788a64de89726da470010eda8fdcf3da58b020fadc8970fafb08a29bef20" +
+			"2bd0707e994015258b08958fc2af4c86c3a570443fe6e1d786d7617b0c66" +
+			"29a6d9a97740c487622b5b8186c529d7f8af04d9f0a9f883043f08103ca4" +
+			"d70057ee76639f3b1046d86928d54cd79fb5bb7b46defdf15d2f8578568f" +
+			"1d7b73e475e798ec6812586700e038ed4791b23ac9439d679a1a4bc04cea" +
+			"e328330c24b065c9cdcdcedfbaf58e5299779e6f48783d29ec3b1643bc8f" +
+			"1095c724dea75770583b15797fc666f787510d91e65a8e2090cc1ed2013f" +
+			"e63ab17bc7640ee817487f4eac8326e9c4698cb4df05d01bae8c0d00fc00" +
+			"08919484d5e386c8f60b8ac097c93c025d74faa56e8cb688d1f0c554fc95" +
+			"aae30873e09aae39b2b53b1fd330b8546e82d9e09bbb80132d794c46263f" +
+			"4fd7b45fda61f86576dec52c49f2373e4dca31f276d033e155bbcdda82af" +
+			"8f823948498f4949bf23a08f4c8ca5fcc8598b89c7691a13e5aba3299ee0" +
+			"0b479b031463a11b97a9d0ed3189d60a6b6c2390fa5c27ce27e28384e4fb" +
+			"04291b476f01689292ace4db14abcb22a1a37556675c3497ac08098dfd94" +
+			"d682401cabec239377dff592c91aca7eb86634e9d5a2848161dc9f8c0c3a" +
+			"f7b6a728371fac9be057107b32634478476a34cbc8b95f83e5b7c08d28f6" +
+			"fb793e557513ca4c5342b124ad7808c7de9ecd2ac22d35d6d3c9ce2f8418" +
+			"7f16103879ed1f4827d1537f7a92b5bbd7cd12d1ecc13b91b2257ad073b7" +
+			"a9b1ea8f56b781bea1bddf19b3d7b5973f1065fb72105bb4aeecca5b7513" +
+			"ffd44d62bf41751e58490f171eb9e9eb6d57ffebedd4f77dd32f4016b769" +
+			"fed08dd96929e8efb39774d3c694b0d30c58610541dcfab3c1cd34970195" +
+			"7bf50204acd498da7e83947815e40f42338204392563a7b9039c8583a4dc" +
+			"faba5eaf2d0c27ada3b357b4fccd1595b9de09c607ebf20c537eb5b214b8" +
+			"e358cd97992fa5487bc1572c8459c583116a71e87c45c0ba2ca801931a47" +
+			"a18ef0785ebbe420790a30278d2d0d42a0225d211900618438d1a0b2d5be" +
+			"d14f8b4be850dc8cb08d775a011683a69ee1970bb114d8d5017de492f672" +
+			"09062d9ba3616e256d24078536f30489e4dacd6429ed37aab9b73c53fdd8" +
+			"a8a7aff1b914b9d82d75a46d0ccf85f48d3ce9a8d3f959b596ae9994ac3e" +
+			"3b4af137d0c8e07ece1b21fd8aa05522ba98f85a7ab24ed8c1e265fadf4e" +
+			"9a18c5ab5684d8ba8d3382ad53b415c73ebfaba35abeebaf973b6f18e0d8" +
+			"7f019420eb34e09bbb12afc5b149f1e9e9b6ae36ebde429d437ada1a2d52" +
+			"b998f7c75ef731132aafc3bb106a2ad3ae11223a355804d4869ebaa47166" +
+			"2df261d95d48ac6eb17c1781e81c0027ccf8f05c39e1eda7793cb16622be" +
+			"ce7a1ad5d2f72f8bf4bdb2f4f4dcadac3db3bf727f0d447adddad4500360" +
+			"09ee011bf4155e5e46c74b00d72e8e6a88de9a81a5a4685651b90e874dfe" +
+			"eba41698c98370fd9e99619ce59ebb8342417d03fc724f9c910ae36ac5e5" +
+			"b46c424141073199aaac34232a8e17ebbfdd80eb75e82290de92968f3893" +
+			"0ab53dc83ac433833576e86fbabfb9d7cd792c7e062811f4cb017710f841" +
+			"1e0fb65ea4b3cd68b0af132cb08330aa13579196ec632091476f268b44ba" +
+			"8f2e64b482427dfc535d40d3f58b4dee99053b35a3fed1cb245c711fa16f" +
+			"c141974c8db04f4c525205dad6ca23ccaebde585cd3bc91f5874452ed473" +
+			"08de95cb6164102744f90b3007e511e091653c97d364fe0cbd7f4cd3249c" +
+			"1f5c452becd722ccc8c6b4e371e2631337dff78efd903a8fc195a90ca5a2" +
+			"aa4513bc63cd43794ff06c5337329055c43d4fb547e63d6e4d14fbe37b52" +
+			"1411caf2f1b0df51a68f677db59aa227c725cf494ccb7f8cacc5a06ac5bd" +
+			"f135a2603175a5fd5e5af615fd2e7cea61934e6d938b9e672290aaccd99a" +
+			"7e26dc55efe928e56ae6354168264e61668a61f842a581cd0c4b39e0e429" +
+			"04631c01320857b4d7e260a39c7fbed0593875b495a76aa782b51fee4f88" +
+			"84ca8ddb8dda560b695323cdde78f82dd85757cadea12ef7cf205138c7ba" +
+			"db6a7361a8d7868c7aefa7aaf15f212f5f5ab090fd40113e5e3ad1ab04f9" +
+			"b7f68a12ad0c6db642d4efb3d9f54070cc80d05842272991bcdae54cd484" +
+			"9a017d2879fd2f6d6ebce27469dda28ad5c345c7f3c9738038667cc9a5bf" +
+			"97f8f3bc",
+	},
+	{
+		key: "aa3a83a6843cec16ab9a02db3725654cb177e55ec9c0c4abd03ada0fbafca99a",
+		tag: "719dbe5a028d634398ce98e6702a164b",
+		in: "643883153c215352a4ff2bb2d6c857bafa6444f910653cacd2bbdb50ffdb" +
+			"cae23cc297a66e3afefbd85ab885e8ccf8d8f4930e403662fb4db5121aca" +
+			"82dfcc3069bd5f90be4f5bfd3c10f8038272021f155e5de0a381d1716abe" +
+			"0b64b6d0f73c30baf6ddfe0e6a700483cad0fa14f637afb2f72361e84915" +
+			"78ba117e1c03f01fd61aa8f31da6464f3d0c529524d12dc53b68f4d4b326" +
+			"db7fc45c63f75244002b8f9a185556f8aab85948647818f1486d32c73614" +
+			"b8c4763e2645bdb457721ff3901327588da01622a37ccbbd0374fec6fd1b" +
+			"cce62157e64c4cde22c3a5f14c54cd6db63db0bd77e14579989f1dd46461" +
+			"4c8691ef26406984b3f794bb7b612e8b160374be11586ec91e3dbb3d2ccc" +
+			"dbfd9c4b52f0069df27f04853e7cc8b2e382323345b82ce19473c30296cc" +
+			"453f479af9a09ec759597337221e37e395b5ef958d91767eeb2df37069a4" +
+			"f3a530399961b6bf01a88ce9dfcc21c573e899b7951723d76d3993666b7e" +
+			"24dc2570afe738cbe215272ccedb9d752e1a2da00d76adb4bc0bd05b52c3" +
+			"fa08445671c7c99981a1b535582e9b3228ce61662a1d90a9c79afbdcfcd4" +
+			"74def2b7880cac6533ba0a73fa0ba595e81fd9a72ec26965acc0f4159ba5" +
+			"08cd42553c23540bc582e6e9ac996a95a63309f3fa012eac14128818a377" +
+			"4d39936338827bbaafad7316e500a89ed0df7af81be99e2f6aae6bb62568" +
+			"1dfa7e100ebca5c8d70f67be3c1e534f25446738d990ee821c195c98d19c" +
+			"fd901e7722b4e388da90b95ac0b5b5dc5d052ad6b54f6ea34a824bcf0cd8" +
+			"7f1fc9a07e8f5b8aa0793e3c9c1022109a7c7ae97ee2a2867fd0cf0f8971" +
+			"34b3d150d3b24fcf8323de929b73cca01244df02510393f0b3905caa0268" +
+			"7fe35f64391e7d4b30be1cc98319716528ca4f35bb75d7e55cf7749968c5" +
+			"37136eddb149a9f91c456fde51937c0f35e7e524647311077e6fbe7f3c12" +
+			"37b9584fcf3b0f78744c7b2d3b452823aca06d144e4463eb5b01014201cc" +
+			"bfed1adf3414427072135d48e705b1b36ab602cae69428e7c19d39cbb4e0" +
+			"ca26a871d607ed4daa158b5c58a0a9f4aa935c18a66bdeff42f3dc44166b" +
+			"a299d71a2141877f23213b11c52d068b5afadc1fad76387cf1e76571e334" +
+			"0b066ade8da02fe3b0bdc575b1d9ec5d5f5a5f78599f14b62db0bef7ccc6" +
+			"1711482dfa4787957d42a58fdc2f99525c32962b06492229399980601bd2" +
+			"ee252306b1464914424de9aa414a0a6e5dadf8ffbf789e6d18a761035d3e" +
+			"f2ff0753becbd2dd19fc1c28f9acebec86f934f20b608a9ef735ac91f6b7" +
+			"83d9327cce7f4870d39bbbfb0100838dee83e6baf2b40cfc98415dd174ed" +
+			"72e393ad0459e8035dce7eb18eb3af2f39d2712846b9e1852cd61d06dfc3" +
+			"5e34fb761b67e2a711ceb4a82557371ed32ca8db2e4cd7fea0b6bd026177" +
+			"4057b9abc45dae6869cab1097459473a389a80a4523e5de696554f8b0bec" +
+			"0ca605e6acfaa00386fb5a48e0f5893860a29f35e680be979cf3bf81ee7e" +
+			"ed88262dc80af042b8cfe6359cf8b475560bb704728034e2bd67e590bd76" +
+			"1632e516e3292b564c7265d7a6dc15c75ba6f6a447b1c98c25315ac7de59" +
+			"9edc4993e4dc7d1dbfcea7e50ebd0b226e096500216c42de3abe352e5b09" +
+			"a3c9754aa35d00883906599c90a80284d172a90abbeaf7e156fe2166ada1" +
+			"794420fe55b1a166d752d0eb7f04e822d021c615e84777101e7c9f9dd12e" +
+			"565b7d093fe978f85e6142c1ca26798b45f4b8d23ecff6be836e810e314f" +
+			"ebd2ea66f2ac95bad84b39b7a6bac41448f237b45e9ec579235ba2bf5fa1" +
+			"f00286379ec107c743f06ae0d11b57a2f5b32e3bc5f1697aae812d7ca303" +
+			"b196a8a43259257f7697bae67adc7f121be561b2d0725982532ffc06cb22" +
+			"839d9066dce0e4d683d9348899089f6732de62751ca77f1c439e43054468" +
+			"2c531b9c61977bc221b66030f7571dfb3ddfb91d9838529dbc99612f650a" +
+			"d72bb78de061192068941a81d6ac341101aeb745b61bd7a87a35a2714d50" +
+			"c3eb2c3ea148fb9ebed948307f8b491aec277ac01903ba36e6ad54f89fe4" +
+			"280a17f8e7ae639e75aec16d56576f03c2a1efe4af995eb825ccaa6efe0f" +
+			"d6d878299a351591d791c286cac5cb049834580d47a9bb7720d0603e3141" +
+			"ad7c1ec2dd23d3002e15d73c1828a7f08062848b1b6fcf816bd954743547" +
+			"6f0d6f882125bd03095eb1b1a846d535730e258fc279f7095de7c2d3fcca" +
+			"a4640a2e2d5ce0974c1e073c60bb78171c1c88ae62c7213a95d36ea9ab17" +
+			"59093813b85d17ff106e69100bd739ede9656388bf47cc52730766a8a186" +
+			"9dcc623e09e43cfba1f83ae1d9f16789064ec73504c29686760ea02c6634" +
+			"a929ca10c6d334b1751494c6d143671ce8e1e7dcc9bcda25af895a193032" +
+			"ce27c1016ccc4d85507fd2265ebf280d3419f54f66ba2a161c068491578f" +
+			"be056f02f97be745db443e25ed2647c5348f278f4ad8bf5b2a2c2d56e795" +
+			"532e25585984a3a94f435ef2742a0413abed7230ff2e9724187c91f73a7a" +
+			"726ebf36bc8d0d959418dd586452664990889358c56720c1001c004ff768" +
+			"54b9850890ce1b31735fd9f4a3640622ef0b25c659e8a937daa0df7a21f1" +
+			"77be13dfdb8f729da1f48e39a05f592d8c98da416b022fd8edab8e6132eb" +
+			"a80c00501f5cc1e0243b6b096c8dbe7f8c6ffa2f8bcc7f309fb80b489b92" +
+			"c4878fabad42d91876e10ee64ccd415124461cdc7d86c7bb6bcd9133f3c0" +
+			"dfa8f629ddb43ab914c0ac5ecddf4398052229876fd838b9ae72523946cb" +
+			"bba0906a6b3ef26672c78cb24cbf691a5ec869d9fc912009d840772b7da0" +
+			"c7f47856037c7608705cd533918c207a744f75fdfac618a6981778e09332" +
+			"5c7d22170da85bdc61044b4c397919d601a30746cefefa798c58f02cb827" +
+			"0d130c813cbeb67b77fe67da37a1b04bf3f1e9ee95b104939220fb8a0394" +
+			"86ab8954b2a1468016f546406d1946d531966eadce8af3e02a1f59043ff6" +
+			"e1efc237dbf4dfd482c876531d131c9b120af8b8fd9662cef1a47a32da40" +
+			"da96c57dc4efad707a4e86d0b84262d850b451bda48e630c482ef7ede5bd" +
+			"c55147f69e2ff8d49262d9fe66368d1e38ecdb5c1d4e4042effff0670e69" +
+			"04e47d7d3047a971d65372126ff5d0426d82b12b253bb4b55005e7a22de5" +
+			"6fa54f1dfcce30b1e4b4f12b1e3c0de27cea30ce79b08c8c1aceb1ffa285" +
+			"c317d203a9f2e01d542874fc8035b7670f3648eec79561d6ff2fc20d114f" +
+			"ba4fbed462f1cd975ee78763c41663849b44cb2827ee875e500b445193e1" +
+			"4556bcccfaba833bb4ea331d24a6a3bd8ec09906c7b75598b44ce1820a49" +
+			"fca4a0c1501e6c67515d4fa7f88f6aa3cd7fbc6802131a7b14b219e154db" +
+			"9ed241133e10ace40e4d963f904dd9f3bdaaade99f19de1ddfe8af2b3cc4" +
+			"0a48374dd8eb559782bea5410f8f9a1cd128523c0157b6baad9ea331c273" +
+			"311492fa65c032d0d3b513d23b13b86201840d51759021e4133f873f2781" +
+			"8f54f34ba73b4f33107d49c8de1533856ec37bb440f3c67d42148765610c" +
+			"3296bce932c839fd866bec3762a38406ac2b39d0d93730d0c88cb8f765dc" +
+			"d8ee71263fc96068b538da06fc49e25dbeaa10a5111a9af8e8f8d78e6ed1" +
+			"3752ad021d9f2c6b5ff18a859fee9651d23a7237bd5a5c29029db3882c47" +
+			"0470de59fd19fb3bfbd25d116f2f13ef5c534bf3a84284ae03e3cf9cf01d" +
+			"9e984af9a2e63de54e030857b1a071267cc33d22843b28b64b66e4e02803" +
+			"c6ab5635291aefa69cfeb3958c09d0b37176842b902da26caae3f0d305e7" +
+			"c6ab550414e862e1d13d9bb9dc6122cb90ddb1a7bc6d31c55f146659baa9" +
+			"6cca4ea283e5e1639967889543ecb6849e355b6c0227572097221dd46c1d" +
+			"f8600b230e9644ba611ba45cd83fa4ac7df647b3be57387b6db12682018a" +
+			"de9be50a8ea7d5f7c743bf0c6382964bb385b3c207c0cdd63279c16130b3" +
+			"73ba974125291673344b35c8ef9a33be5a8a394e28dc1448f54d46af675a" +
+			"edc88ce85a11ad7e50058df4f3f2364abd243683d58a2b13fcb0dc0eed21" +
+			"380b666eb87f4be75e7f2842bae916c15af3e9658c55408537b2301faa6e" +
+			"42af4d94e3eda6a41d6d302be281e2a9299e9d0fb1f20cf4ca978e66bdd7" +
+			"4c8bea0f15c84d6513cdea787dacbd4bb529ed03528284cb12f6ecd841d3" +
+			"c58c3a57c6bc19b65d6d10692f4e1ad63b091137c8acacc6bc1496953f81" +
+			"2972bf6362cf883bb75a2d10614029596bf9f35e92addbb50315b30161b7" +
+			"de8867a1393d9583887a292cadceb54078c9c846ec30882e6ff987494060" +
+			"721d3c761940b91a126e8d1e0118617bdae01a7f9c1aa96bdd6c78ca06f2" +
+			"6c8d85664a8705334f4997c724ef98fe265985593d5a9c30798714e6de1e" +
+			"bd04b648be47a6b5d986a3103e738a5cd114b19b7ba99d2e2eec6181bf3d" +
+			"ff0fec8c54ae6118be8702c3e775d493a6fafb509712a43ee66c3f4b75b0" +
+			"194c88937cffa5fa17b284d2556f2b0eebf876e05f92c065515198bd5e83" +
+			"00d0db432cb256a4a0f9963a05694ffce3ecbd182209e0b7bb50120f6be4" +
+			"eeb9d268b17790ee14a2c887dc5753e0086630b3123734053aa37595aa8f" +
+			"31968ddae4991af4ab970c1e3cfa1146a2efd9dc42abd6af14777b8a0455" +
+			"3865691cbac4b4417b3fa13c154d581b498f3b8cb77adf0e42dc2f2fb521" +
+			"732447de97271e542c6cf8cad3ba0148cc3ba1f2983ead836a25a2c022d0" +
+			"43ba18fcd009d518d07b53344a5bc4d626b3b38405a114471f75dc70e015" +
+			"d11e8f6f57d087fa72909785573008b1",
+	},
+	{
+		key: "1793bfda9c8666f0839b4b983776735a927bdaa3da99b13c9f3d1cc57d4d6b03",
+		tag: "bc89cfec34ab2f4f2d5308b8c1a5e70a",
+		in: "a09f661aa125471417d88912f0a4a14115df9a3a19c1de184878291acb0e" +
+			"89ee1f9d8213f62df442f8969a9a5a7c402fea09bdbe236fb832544e1f93" +
+			"9cdd4873802b2bb8fc35ba06b7ff96da6dc7efddfeeda84116bc525a7fc5" +
+			"2d84d2e63cbac00b122dc64f2d15b36595259d81a1d2a09f204c54072751" +
+			"dd812259df1104bb2d2ee58baee917c5d0aa2649c8a1503114501e6ed6fe" +
+			"239847d3d88dccd63d5f842426b600079c6bf06e80a2813b2208181163b8" +
+			"61dca07fa4d88254e84dac1c78c38397a016b5ad55a6b58878f99036db56" +
+			"89871ab3c321f6ed5895f218f8fd976c348b3f1269fcdf4d38c9492b4721" +
+			"6c45f499f5705830b33114d721f9731acf6c69fca681b74c2d82c92e145b" +
+			"7bab77110821d3a12cc818d7595a5c60c4b5e5219376c38a4dd52d435d41" +
+			"562802ff65ba2bba5c331c333d5adf194d29b2cd9ebb55927bb4ec17681a" +
+			"3f5574ad34fb4e964f2c756f6dbbb7a6876a21579a515263444de7a30a33" +
+			"15005458bc137ccfdff18a3892fc9f58f1de10d4de20bbcf860f5f036d8e" +
+			"8a188f18e5cf7ea3cd260710e7491befcb131d49a28dfb1ef688fd021a1e" +
+			"e4420d32fbfb03b47f5e85c37d91e49a1b0db85d966eb5434c4197433eb4" +
+			"9d56f2ff999c9a72230447032dc949202468261b48b6ac212e3f651d6c63" +
+			"03a06c90bb2d3a755ed91ba73bcdc28e1c5b0936e51e0a9f69c3ebabd3db" +
+			"add7abab6d8f6a44daeb3126429a01815f57444fb7022a4a510f8b564ae2" +
+			"dd9779b3a273fef15859a33e233724846c30d89fb78a595b6ff6c834812c" +
+			"00a991e405806aafd0c26a788895ad00a5e43c5426197aa8247207077548" +
+			"ee67db4cd6f878431a2e36e952d84b5fb89d681f553198e2c066310ea6ac" +
+			"3a31f5b1792620616f6c41d486fb844eeacc7fd36971abf416e8d6d50985" +
+			"c83cc92ea46ac37da8f0026aba30c945d8bb15080d2d95e4081bad626199" +
+			"3f95f57ed3252822a7caa035ae22a36c35e280cbbc82d729346cacdb1794" +
+			"ae9a9bb2793fd1d5c47121b135c2836063367339c5151b4e35278e97f62a" +
+			"fdd2f231d4b47812d083a829ebb9c374ff2ae8479cc4b76d55f9cef3ec6c" +
+			"4894f53e8caaeb0d8cd072960cedaf758e48e3640590d4f728626e0a08ee" +
+			"ebf719c96bf8ed4d0c283be09c0ae67b609e22d3b9aa6b03642854909de0" +
+			"5ed52b39673867bf586a632ab8072de15c637cc212cba8387515c9c9c433" +
+			"abd7ba6b02abd09da06a34694ad34f88515b65c0c9c247fdf9819fb05a1a" +
+			"ea4728c1182f8a08a64b7581cd0fb2131265edcb3d4874b009aede0e87ed" +
+			"463a2e4392aefd55e008eb7ba931788262f56e53193122a3555d4c08133b" +
+			"66020154b15643fa7f4f5e9f17621d350ede3dc70be02c59e40fea74dbbd" +
+			"7919d1a8d4e22ef07c916fa65e7d4b89fb11a7c24ddc4ca5f43344c753b6" +
+			"1331c3fa4558738ba7832b5b2a275bc9b7989b6e6888865793329806cd3b" +
+			"f0ba57c941d4428623e062f4ac05e7cd79ad5446f8838f2b247b66bddadf" +
+			"540845a1bb304a04b7edbbff579c8d37e2f6718f8690abd5231822c7e565" +
+			"69365ce532449a41ae963ec23a2a75e88307dc6b59cbb3fab913e43ed74d" +
+			"841ca9f6e4ef96dfd9f04e29e89361aece439c0b2e1943b30410a63d495c" +
+			"522ac3ec1b04ec4cb345f7f86969957ad750e5bd7dbf1d6a22eed02f70b8" +
+			"1cb5b2b020c0694d7f63044f9de0c3de1ede52009c858992d01ebb92ff19" +
+			"a9e0fbea18942fbafb77746c8e9e687dd58ccc569e767528bde43b62c7c1" +
+			"270a5721f1212de2b29a7aae2d6ba6cd173d7fbc78aec4356ce2e8ba9164" +
+			"d97dec061dd0c3a0e3c520a7611ac99739049dd5825537c70b7ef660046c" +
+			"1785546cd99aa400da848eb7c3c91247415c8e245d0f14c30d482c5849ae" +
+			"aaeab2568288229b08267818dae8f76fc674c684c99eb5faf88a0783813d" +
+			"f7298e0b50cb233f78471e5ca9cc3b04927c26a3871cf253798cc49aa717" +
+			"d8f18a1ddcbdc26497d188f15f86ec494dcf8f942c3e07e572385c6fa0ef" +
+			"40c0b625f1737543074a747a369482a0b342a08b3eccac9f9209be31aefe" +
+			"5a7794974f71ac0bc9a58026397ea3dd4f5e40511d58d2a3b45925c194ef" +
+			"13987037d736dd48b509d003a86471d5f161e0e5dd168b4f1ce32f703b89" +
+			"15004d8dfc708a5bb02b2e6fb67424b2cbcb31ddaa0114c4016b0917382d" +
+			"aad11815ff5b6e37d5af48daa5ef67cee3439283712bc51b5adf2356cb2a" +
+			"5181b8941fd78945c7c9d61497683e44fee456ad345e12b4258f15945d45" +
+			"b6ca4369ee792d849112d583fdb39cd4d333ee057355f0abc8d1eea4640c" +
+			"128cc1617982db0394233dbd416102eec1874081247d2982bbf9fed1b1b3" +
+			"8f4da923d68c8975c698f189a4d7840fd7aca9dceb7d91c076f85e1c546f" +
+			"4d5de4f60c91348455aaea30cac134c844dad93d583c139dd52b3be6346c" +
+			"4d2e6864125c5a2d0aed8f67930e1ebf8700ca88aacc914ea76ff17148f0" +
+			"777738cc126e75a2c81110faf02fefc47c91edbab7814599000ce55fe20e" +
+			"f313566e9b62457acf2f22e1141e220bd9d4747417d03e703d4e39282803" +
+			"386327fc65dd597f723ee28185c78d9195fc70a75706c36287ab9c6e00e8" +
+			"5cecbbd6043c6af8d30df6cdd8777be0686853b7c8a55a5b1e03e4431d39" +
+			"1725ff99875a85cae6926998723b36d13ad458220712209bfc5e8d2ca5d4" +
+			"4ea044d5ba846b4035e7ac7e9885f55d3f85c0c1b3d09fe929a74450f5d2" +
+			"9c9672e42d3f59be4ca9d864a4322cc454c2578493bd498a51bbe960e657" +
+			"3e5dd02c4a3a386d4f29e4578a39e9184024cd28d0e86ecac893b8e271bf" +
+			"ce3f944d130817378c74d471bd20a4086f2429ed66c5c99969fd8da358ff" +
+			"5c3be72bf356ae49a385aa0a631b588ddb63628fd162673e915cfc4de56e" +
+			"ae6ff7101df3b33125c9bab95928f6e61c60039b6cc07a66f9c733251447" +
+			"ef9c1ffefa2158a8ddf89dc08686a4cf9b86ea09914e79842d72a3236afc" +
+			"98a3afa0a1cac5590ab6a923e35a2ab8db6410a9d33cb84d1c48a054377e" +
+			"549774b25f50fbb343ecd5db095155cce9fb0c77d09752f62d4bbf16a770" +
+			"30452a75f6bdf73f7807d8f3a6bae16ad06b22175fee60549c22548de9c1" +
+			"3df35ef4e7bf7b66491a62b93c2c3fb0c5edc51f60f5704b56af30f1079d" +
+			"7c385b99f958ef8209e030e381d1ee8d67d3cb84f32e030e8ea2c1d0c77f" +
+			"d6b242a9f48707557c8682a08e1127f51221a55c733ab1edd00a9c2912cb" +
+			"36dde85f73b524e1a4f4da6414c5e4c18d9537722b2becc8a91bcc63f2b0" +
+			"9f32409c53c2beee0de6726dabcd6bf33118a5c23fb9c5c1810476efe658" +
+			"4bb6109c516b45e16b2f79f96755680374d82b91f2c519639a1815fd485b" +
+			"a3c00b46fbefeafcf25554ec5a6a5ae2da07c85b8a0f9fcde50263d9ed85" +
+			"038b2f7aadb9de765655bd201235218bfc74bcad6a9ddf4506167a649afa" +
+			"df400b85752d68a92b7a97f26b334dd77fce824862046b286a7c8e0adc36" +
+			"f713a252a673d4d995b268badf4bec8b8eefe85c25b823b6728582d35c4a" +
+			"60041114dab72b0623b99e2758f6a1e97365279bfba0eb1fc8952ca4f2c6" +
+			"fbffd9f5fd7dcad1125b18a796981b5ead0b6431141315898ace96f0d38f" +
+			"865698df8822ca7b65644b6b1f0a0f0d2e5850d4c93ec48ca3eba1b919e2" +
+			"4413a46d595ffa427715e499db3b7b9ab53c64abec7302bc737a5bd124bc" +
+			"da756abbca132f7f67e6989e09bfb23b497da31bf156bb9c69ae54588df1" +
+			"7420e8fe989f0472c8893b2bfe57cdae265a8cc7aeb39624167a567a6fbe" +
+			"bb1aa30c3dcfd14f2808a070994085e6e1fa79021e77c399f90ab1f995a7" +
+			"baff672cb693bd39b798b4c890b7d0a57978d6b9bcdc5bf3f4d205f8f24b" +
+			"2b43d3ae300a96971c9182be297618b9adceebedba1ab0f324b01d23d7e6" +
+			"35f009db3dbbc643c2d787567594bc639bfd78c4f3e6d948caf06f013423" +
+			"eb3c764666b58f886d5d28137c053c2a28535efcea400147e92ac6753574" +
+			"3b47f9cb48852abed1d057647d5b1c6f334eab1a813401fccd3dae332738" +
+			"776bb223e359f3c459b5c573ba64fa945bdd66c5ac0fcbd53b67032a7b80" +
+			"25f551e8d1fd2a4291bdb7941cbabe3a09765dc263e2bbb6db7077cc8fe6" +
+			"790d4bed5e36bd976d1e37dfdba36aafcdaa10c5f3ed51ba973379bcb8fd" +
+			"203d8b7282abbd271ecf947e54486e8653b7712c9df996a8ad035f41f29c" +
+			"ab81509f922c67dacb03f25f8f120cb1365ab3c1c286849c2722448ba9bc" +
+			"ff42a6b8a7a52f2c79b2bfcbdd22ef8a5651c18879a9575dac35f57d8107" +
+			"d6bece37b15d7dfff480c01f4461ef11f22228792accda4f7936d29d4c56" +
+			"cbba103b6d3e6db86e39e5f1bb9e9fd955df65b8a6e44a148620f02b5b90" +
+			"b2be9e5bb526d0ec75b1e723e94da933a356d7ca42d0ce8349699f730b8e" +
+			"59bac24a6b633759c88041d29399ce60a2ca2261c7eec1acb9a56e0e65bd" +
+			"e37653ce2cf7eb83a4d019c755bdc5d685b6394ecddb9006823182dd8138" +
+			"a1bf79a32d07a8e5e8ab221995c714e571b40bb255b79e328ab883542c16" +
+			"4899fffa16eb3296f310e302512352a864fd809beaab4169113027c6ccca" +
+			"99a92c6ce35c30f9449a3add70f10db1ed08078e8e6cbaafef630aab7e9f" +
+			"c8adb09c18e33fe1af3620d1e4d069ac11325e23cc18e5519a1ed249caf8" +
+			"ddba871c701f1287cc160019766988f63e089bd9bf1af7e6f5b9002e3b6c" +
+			"264d69a8bac16914ab55c418d3a8e974677cdcbea36c912e90386a839a37" +
+			"77b878e680c07c7cc99f42a7dd71924babf7fb0627d1f2cc60d9d390d1e1" +
+			"50d47386be6eefec9ddbb83b28fa7e2fd28cc3867cbe42d13b00545af8a0" +
+			"48cc07016ec79808b180e0b258c564739185da754f2e",
+	},
+	{
+		key: "0d41cb4ac25217feb20e86fc2490e8d2ea2e8225c051252a9395cc4f56e1ae5a",
+		tag: "42df9f9a59d6dc05c98fd9e9577f7176",
+		in: "01caba7a19cdb09dc0ec6c522c61c628eacf17ef15485aa5710fed723875" +
+			"2e4e8e93dd4bbc414e4c5620bab596876dfbea33987e568ddabf7814b318" +
+			"8210a5f8d70041351e4d8410840642a29cc8d901c25fa67cc8f9664ea5e1" +
+			"9e433eaff7c722d0258ae112b7aca47120aa8af4420d4412a10732551db2" +
+			"cd3e0af6e5855d5eea61035af15a4d0d898d04033809e995706eba750a7c" +
+			"ac07aaa0dc71477d3020f778d0347f1a8e37c18540deb9ae967e734c0264" +
+			"df0e1f52b0b5334805579ea744c8784c3ae0c3ff8217cd3f53cb747f6996" +
+			"f3d2147699799e649061b205f97f7992e147fb20f21ff862c6c512e95534" +
+			"f03075e8e52f162e0d70d7a259e3618474427f400f44f75198edebae6e40" +
+			"a2173257d114e1bb5a13cf419c821eb124d90e89a938d91f4d2e70dfd1ab" +
+			"60446f1b602614930a329e98a0c30f107d342281db25b8f8259933e14d20" +
+			"8bbd991e42969e8b0600272f9bd408483cddfc4cb8dfe7bc19be1989c7fa" +
+			"129d38e1078d094b82e0a845040ddd69f220dc4aa2b236c44101d7da7779" +
+			"9827a7b037561b51e50fa033a045571c7267af93b96192df3bf6180c9a30" +
+			"7e8c8f2b1d6b9391767369625015da02730ad6070df4595eb8099bd8e484" +
+			"59214310cb62c3a91a4fa8ac3b3d7b2017d4254fb465f0a248e1bf45819b" +
+			"4f0360f37c9a79d405e2bb72e5c25a1b4df192cfd524d61e1e8b274f2fe0" +
+			"634c73f0653c7c9e9062c9d081f22a8b0327897eed7c6e870f2815bbac8f" +
+			"585c1bd868759a98dcb5c3db2f6c53244b9cc494a56f28a9ba673167cea8" +
+			"b799f37049ee7b0772972b3a6603f0b80eddb58ef03f916106814d72f000" +
+			"250b3573c97c5c105910d79b2f85ad9d56002a76a1f43d9d1c244ef56d3e" +
+			"032a9bab95fe3bd5dd830ad7d7e341f28b58c0440658f7fc2ca98f157708" +
+			"1c647e91432cb0739d9acdbf973ceb9b0047634d695279e8837b04dc5357" +
+			"f013fde3c55c9c53bf1d817ec59a1b18ed0ac0081ed9bbb3bcd1a5d3634f" +
+			"50f7506f79dc6a4ebfa640bf65682fe9aeca68088e276937669250064de1" +
+			"c19ad6d5c697f862114d0f81d2cc52be831ed20d3aab1e41fe6f476b5392" +
+			"af4799392464c51394c2d1a8325ee2e84f1635d295ee663490e538eb338c" +
+			"7126a8e731ad5c0becf144c7a9cae5c6493350b589385de29e1a0ad6716c" +
+			"346ec4f0a31ca5ea35c59ab6b099f65d7f0b3d00925a1da1b5777c029aea" +
+			"9679e895d7100645dc83f81d82a6174beab2357f7888ea640900cf3ee67a" +
+			"e0724a123919d78e70e05288f67e5e69ffa6f345be8a96e58bbe260184b5" +
+			"ec5c0c1354cfd516ebdb8d420029137d41b029641959cc07fa7b4e16b39d" +
+			"17f36b2367057410a42e0550e9ec1dcd2df4604d52d4f9dd1140d57af08d" +
+			"50e1527dad793b6d649324de799754f755818bf10e6d1ab614958dbb24ac" +
+			"8e2c01270a90ec3df4379c3f509b5ef721b0fd4f91a1bdb8127ae4dc74d0" +
+			"75f6cd8bb28319d6f8e8d8ff64fb4a42d646e9365156c6bc72cc46e9cd1c" +
+			"f9e735549e3df9a8e6b5fe541948b126190117db71fd1d61ad84be0f725f" +
+			"20b99eb141b240326d399976c4f2ce5823d94649a9580e1e8820bf49184d" +
+			"fc34378a60bea89b12aca69cb996c17847b7fb517cf2d51f16d78e3875ce" +
+			"aa33be15f6a154004f0e1134c6652c815c705efc34bcf35bd7743d28f0a2" +
+			"77d82dea4709dab41fbfb4e0cbc118c17aa00808872f0edc6437c357cd31" +
+			"74a02aee61890464e03e9458853189431bf5df6a0ad5d69951e24be7f266" +
+			"5bb3c904aa03f799fe7edc7bc6779d621cab7e520b5994f81505d0f01e55" +
+			"96e14b4c1efdf3e8aadee866c5337c1e50066b3acc039c84567b29b7d957" +
+			"683cadfb04fb35402acaba631e46ca83dbdd8adf28e377ec147e4d555a21" +
+			"e6d779d7c5a3078ab72702234d36ca65f68bd01221c9411f68f32e16ef04" +
+			"99a20c2d945fa31b79d9965853d38ada9d48eead9084d868c6bad974b0f4" +
+			"0956aa0fcbce6dac905858e46c4b62c0ee576b8db7d484a524e951f4c179" +
+			"decfc7d6f619e86dee808f246dd71c7e0b51d28bc958110d122fa2717148" +
+			"77823242711632f6e1c7c15248655ced8e451a107707cec8c84929beece4" +
+			"efe5503d3c1763d0ab7f139f043e26027d5e52a00d5414dd98a324a8fc2a" +
+			"06a1345cbde747f41099c3377b86bbdc5a17c8f6e5b773a761f78573832e" +
+			"4359b143810361dedc79142fffc49ddc0b32f225d50d360ceec3920fb0ba" +
+			"0693b644ee07fbd1ce829e223a02794b197614061c4bfa46112d105c2b7b" +
+			"4efea448501d146dece44f6640d674d5749db498b32969de6e165e705a18" +
+			"2aa1f3d8e16892b0120337640d52c9bee35e5b4b17f03eaeb31205c8ecbe" +
+			"1ae1b110023016e40ee87370a65c5c20bfb00f100d3c6c1de6e4a1c90162" +
+			"f25bddbf300ed637330206788a4ff96903f971c9618493ad074412af625c" +
+			"ff9e0f8f183bbd5e96c1f28307e6cae8b50cc0eb1a3a8154e44e9de947af" +
+			"002e4d1098d6b0ee3f2e71a10d03eb444729c42461283f37be8af2ce81ba" +
+			"bac246a05c2c94efacc43f0cf9ff3df38ab6fc1648c796ae7026ea95752e" +
+			"b70873a6da59da10d8b5316126431c4a17289466e95dc739c061d7a4b13a" +
+			"450809479eef421bddcdade77a6df133410328c754af8999a09b1a5c056b" +
+			"ecbb6fc2c339586ab92100f46d2fa1fa689994b36aa70703d76bf7738adc" +
+			"f0589fdfa6bd215339ad69ed983f62efce0add5a63fe7dfe4bfa006ff16e" +
+			"0cc06d39199ad60adcae12b75ca98d764502a783373da3a41281e03c2037" +
+			"e1b3ca7f7eb60e2b67427e97ec72d36670db7662c6daa505701fd279f116" +
+			"ac0ef569471f204e1531c25a4ac3ce19b6f68a8994b6f89b5abf034a6507" +
+			"32c7fad4206eb4eaa7cd9a710d866bf3c3f13c16faa268ae0cf4f69be909" +
+			"bb9b79aab80dd25101d4cc813a48d3f38d870f10ac0b6768005aa0e69e87" +
+			"dfc0424deef06414c9ba6f498c93c41c692a7a6221fb5595b390a32c70e0" +
+			"2cd64471c797ee8a143725849c1e054ee2043dcfc0b4cb1c00be21a14be9" +
+			"2d9a07f1b4e975d4c86b8a5c1387e6c42bf393e078fe86d24612d497e14b" +
+			"874485a3cc922b5b6d91295d7b79ab8bfa1c7f64b51e761d19bb9da82a5a" +
+			"a34aa469699036b6b2c55e2b84f84942f10585027ab07e2e0e562e0fc3dd" +
+			"36047850ded84be4416e22aa41c7a2f7d4a4d8e3dd420d746a1d8d56d87e" +
+			"5133a1b4380bd9a89500fd6d7e68a1ec02eb9e79e4a13edfdde1273466e4" +
+			"6b0e6a75f59ff6175716629da52463ad21de27f40fa2e25a566eec4b2696" +
+			"4af3a717dfb0170a73144c0bd9b00bed67ad8c0a146eb5a055812d071209" +
+			"c9d530cd4f50a41488c2238898dea8bb36b0f1496d3ea8c4ff8e263b367f" +
+			"64977679e697d88e5295bd97ac16a0420850d1ead9621e25a3f58925c266" +
+			"ef5246488b1c15a8fe0d8ec4291864faa5a67b2388b7786f47b6d27e8fe8" +
+			"46f85f85163e54155ef95cea4901e712a44404a4d3f27f28dd961ce36b84" +
+			"f3856770f07f20a2ebd34d77405beab04ddfc09770167d7d6340f494dc6b" +
+			"7e4c3df896bd974730193b1e862b58d4a5938e6e4ae8897dba8812924379" +
+			"e54f51a71364d39f76e24fdf2c6c704479ce85b456558ca6947b8fd76f03" +
+			"78273f0a7bcd1d860ef1defe4eea8fdb81c73eda028d82fdcb2248582ac4" +
+			"59eb7698a811e6c5823be886410f6b8577ff2e8252343b6ea890016ae846" +
+			"01c5894cfb988121059fd9c8fbc1596da470a149404fc67baa15383d38cb" +
+			"d17ac107b4ff3c1ca4c76b7930de02b240e7547d39f4978e0cc1fa37f8c1" +
+			"012b677f07bb4df4486196e9b0beb823a3827585475b878e3f6f0a2d3836" +
+			"2c7d34f9f3c91ed46c39cec95c2a0b6f0279a03a00ed5035b0725c393849" +
+			"cdb1ed3c0ecbcf3c2ce108017f468e1c3d469c03e8231d4195344ced70cf" +
+			"daa667252cc1554dce8d0c54eb4cf4da62367d77d7dcc02f81e788ce9f8d" +
+			"d306ba1b48192359cfe92bdbea9980f87ea0677d7d2082205a436cf514e6" +
+			"fde5eadd21b13dc836ce33b5dfb6118bcac79ae00fbb16d61f00a923b145" +
+			"f9caa9f3a2c7f0104f8b052e390987e57c8dc80cd5f0358afb0111af1fc4" +
+			"e31f92bd832ad35fd2e0bdf768272de52ce0b152f74d43a8973ad516b3ea" +
+			"f5937ec8a236ebc86adeba610de0cf7168453111f3c983b64df07678cae0" +
+			"a75466ae15adfb127328e716448cdbd2c1b73424cc29d93df11a765441e0" +
+			"0eeed72228e1099bd20569d9d0e9e5a0b3c11d0002e2896631186483db61" +
+			"c1a0cb407951f9b1ea6d3ebc79b37afb5a7037e957985e4955979b91fb85" +
+			"61ca7d5e8b9cdd5b7ce0130a880d9241027b011fea7696b0c695d4949ca2" +
+			"d0cf22d44b9fee073ecaef66d4981e172e03ea71a6edc7144393bfea5071" +
+			"2afac137f091bae2f5700bfb073a6d57fddcba674a899d7349044a10aadb" +
+			"2e7f547887dd2f765f394de5dc9ef5dbf1eab4d869be8cb68aad8e2614ac" +
+			"37bbf21ccd5a832ee09fdd07ce50a580a2af36256b1046e646fe3dff6d20" +
+			"0c5110f1ad1311bc39b8114cd11ecdb87f94df43d4f6468932fc0ed892d0" +
+			"3d8f3db3f8323ebb29776ab7d260493a36700bcda668abd62126a8189e91" +
+			"df2d2970ef688d4e8172fc942e69ba63941a36b79ac546fff38f5f7d1176" +
+			"57612a662ea38134e1090c3e903c9adacdeefd3ac2a0467e9f5125058c19" +
+			"7b2260d2afad2b0e627a9ae52cd579ee27168065658089e1b83a2d8cdb47" +
+			"e08966e4ec0018e78c4d267f9575b8fea2a42de5c2d25356fe4b8c9cb1ac" +
+			"daf0d1af4bf58b9704cd4bc08471e3b9a0e45a5693433ede2eb1374bce44" +
+			"1f1811cdc7612d7bb61f4f34aea0a44757bbcc12a55c1ba41a7901eb004e" +
+			"689587a38e5b4df4574ddcc7b2eda97f6e480d7d39f45247ea3b03c90a93" +
+			"0dd168b65d52a59ce9c2cb4e860cc6aaa0ee02a58d0c8ba990194bce80fe" +
+			"8c34ba5693fb0943ec2cbfc919e534cc47c04f502b6c217c2f860d1d482a" +
+			"a016aa02adfc2bea3171fc4e27e2a262fd37b824099aa227fccca508f778" +
+			"b8c6ec7aaff1d15f6497753f439daa9e52060fd6e9e056e6843d770fb057" +
+			"6d9e2e782db4843c0c2c7f408a17376719a3c5cf9fa08f04f8a779885a16" +
+			"5cf93ce404be",
+	},
+	{
+		key: "ddbd5d6c5ebd61fa72b453dd849dc302c98a0f3e300f4768bf1dc698a3827dd2",
+		tag: "af608b71a353e63c64911558baa122f3",
+		in: "c67e2524b0de16483158a0232078fadcf611e4fbdb9e642e397b21222423" +
+			"cc2ed42ed34ffcb178448919ee337eff9d7d691f622e70fd3317cfd271df" +
+			"fe6a9d9b7e07db0d20813e2331164a654386db2ab06ae2983bf2460eaaa6" +
+			"3aa0171fb87afb82e85b40d95c8993b2039d32e9d38473dd13f41fb1ff1e" +
+			"261752ab004b221a4472b9b1a0e139f0c999f826a26a7e7df362b0611aac" +
+			"fa83c55cca2f7c0138d2c30313c2f6eb357278328ea6ebd6a5077947e18a" +
+			"a97c34b9dde3b6f2de4b83778ffcebc8c9cb58756691d5e2a3d15a759a2e" +
+			"5050b6da937a6f5551aec069a08027d60dd870d175d2a5b5f0b4f3143904" +
+			"7445c368a5c866370e9426abbc1a1c5a272b96731c4128aedeee93e8e00b" +
+			"b450601a6d31ea279b9450e738b4a47c0dc22d2d8ed5d44257f6318e0c59" +
+			"b951fb6b57746062ab95cd73c23ef0a5c000a7d14c18bfff172e59b6f6de" +
+			"aa61b81009e803eb05e24fb0b706870e18889a9180ac16a042d12dfff9d9" +
+			"1b88130f045d2342fd5ddc5f443681c31090459f262d1a65654c55251fc7" +
+			"d5a67bd2e62940ccd606f3e50700e4d1e992a3fdf0388b9ce3df9de6dda1" +
+			"5c1cd6b70622ac062dcb7ed7058872c00ff3df94032853927126cf6fa4cd" +
+			"c468d91c9b52dcbc272fd7ba920dcd3ea1e048af9c3286dba74d988ce9ce" +
+			"77174e25a87935352721dc23b60a9549322fadbe6a00dd1197dfa25b33fd" +
+			"9e5713afcfd0fae6dbcf27147fa58d995580d7e0a903c895752fe9819f5b" +
+			"b002ed752719552d0f3575312f2e618173a8ae7c147ca64a709053e5d2e1" +
+			"2f4d1ea337afa9ac4f9ba62760046ec1e48f4ed8f6df66786c9fd9f5bc7f" +
+			"9ca2526e1327b042f4657c405757690e190c91f260dee2dd3d2e6616b721" +
+			"e489c7c3cb828478a3d953b88f09904e7927cdf6dbd6a5419eeeb83c0be2" +
+			"51934a80dfe61e09442f0761aa2d013e10aeec3a32df204571ce8984a430" +
+			"9bbe30ccc91977790bf0305d2651ee450b749c3e7761534e45970e70a0a8" +
+			"473cadbc88f096970c275f188c9d2644e237fd50c2e24c1eabbf7578e80e" +
+			"6500762ac513fcd68cf6f8bb7a9d9eedadca059d9ecec07fe6fe7792b468" +
+			"9311861728dd482f087c28374cf9c5ea20b2c8630029e8485fa6fe518c74" +
+			"ef77d44eb7526ca764e50b5f34ed0f253a91fb2af6e59338e2af6e041e01" +
+			"084e1efade1aebb7d1b698ccdb8b4248ac89cd40d9517d840960c08f5e86" +
+			"88d8ba2b54889c1870d315498b70e0e9720f2c8c53a3377a8c0bd2d6a1c6" +
+			"f17c6ff847eb14def6855dc3886b99039e528b421ccbf6064e39263f8f3d" +
+			"340d5d20b1b14c264ac2310b5f3a0c6f0c1006d0d4f1a69af68d28ab447f" +
+			"cd17387e1fc98f164982a6d05dd32d6b4f0f1b04e40c6c6e0fb4467dd6b1" +
+			"0c5a9c92cc8c2bc97ef669b6d55cdd0aa8a15c46af954359165949012713" +
+			"4ea9f74181d54a300d3172c9f01db73288ef6a709c763a4891666d0baf88" +
+			"8531dcc77f0911412d096aef9033fa36b5c1ed283b8b5c109e45b5cde911" +
+			"6f3da2533fa0ab81929bd5783271d5501a9e4fce2aff9eb5a70a4215b253" +
+			"46885d7e4225fe34bb55b309a114a312693d60ccc61267359a8c2dd28141" +
+			"226e7cfd99f0f12c69df57d75dd790dbabfe3145f7fd1a24fa58e03bc2e2" +
+			"6ea19288af4929e5acc517d8f52a074745ff4644d94179eae6ba7d267292" +
+			"bbd2053167a0da9be5e4b6cd0a4200fcac5182d9957dffbefa857e662b82" +
+			"fc3a7cc32506e78030ed5c5d448d7f1b4fd854a735a0c50016bb85e6e716" +
+			"0f87527bca0de235f4b7dacb75be84919c15a5b8cf6bec035795cb67061b" +
+			"7855c2134c1b1bfa6affe04b7db239f73af6ea9c02bc9f7972b7f6400b6b" +
+			"838f4653aefc42179c21765e3ca7a5e96b4402ff544d4bc2332756a23500" +
+			"11241dc42ec6848afe127c00b9c333e69bb5a54ea5c7193e59ea22bd6d32" +
+			"af4f56b1bd2d5982ef7d9c1b02d7668525e4e81b68a400f7afc2653f0f41" +
+			"a03e11c7a02bd094830093481afbab96397245b9f37a568ea1c4ae248cdf" +
+			"afc87f88b1fb5dc300d8e9039af4e6e701b458ed3f32d693f2e869b76bb5" +
+			"1358cbbe5b5089013bf452734388a176cccfc1ae9b7cff603631ca48e129" +
+			"b5c9573d4e379547272cce8aeeeb407d3fc57f782a0eb5fcbd41e6fb13be" +
+			"7e4f1067cd407b42a6121b2969c384916ba2b32563e659f52aae09c8ce2e" +
+			"3c500fbb7e58be74cc1592dcfacd9f0d4cea1a90a18658147c81cccf6fb3" +
+			"078ed27f369e7646f551386a74e1b07074d93e0c1f298c761af46cdaae9f" +
+			"f4be86808b66d0e228016d27a3a77c843365cb847fddccb0bbcfb3b9008a" +
+			"1bacac59ffb0aa759a0568c72c556caf0ac1091431b574687c5fc7bd486e" +
+			"963e0fc3bdc828d988734a21070747c955cf8dba2df1c3a0ba8146cd58b5" +
+			"91b6d54712db67a9851b1607c8445bc97406eeb7488f5f85e547850d619c" +
+			"407f97632ca1801f52c09c2b314b4ab0f8e7fb5851fd60852f4666913ca6" +
+			"bc840c1ec8f8f06caefdbfbf02ce00f20b87b14ba9e651c80f40a31d0306" +
+			"403f541776075fbf23733a6b19e3b44d04b455b29ef8effa70cce0c59331" +
+			"7119abc07aa8c8d0246a760b0b36a3d87b244e83bae8a745b8277a531298" +
+			"f5d0283498a509c89898ddf0f7a7455be1f8a6889c46d323f1dd18c3babe" +
+			"1751a05f871f0639f50967afa46c19cb93d9c2a79c81e2436a7a62f225bc" +
+			"37c90698640f5b43673e1dc276de05ff1e29acdb4ace5121659db5f23c49" +
+			"57aae22f53e6f2cc935824fbd07c2ac87672eeeab895c3f06e09e178560e" +
+			"2fcfa7097f10201dfb8b1ebac08ca806c1b3ba3aff9284846a1a3beada53" +
+			"e9f7ade12eb89b5591f462b2543bb4090e081fee9fb53bbf821dc92d6b16" +
+			"fe820ab2ee4b1f6c0b6a6f19edb0bf6479e257fc73bcd60dc2261d0a4752" +
+			"e23a0be18abf355f3065177d8c3c14e21edc178d0abd1b39f703e6335131" +
+			"ec90cba3d9846cee7354a06c320a3f61b8a269abc7138831614f57ca6c19" +
+			"a4a621142889cd924bf4ffb82b57f871b854f3157e8874c22d43a5726900" +
+			"bafbb8f2260a1eba3a462e23d4def2ccf68ebaae8e52739a1ce67c039eaf" +
+			"9a6c3232fbb5a91d1e59a8dcd3798ba71345fbf83d09b83b41cc49d5ff5f" +
+			"2e809d2b1d5fbc1e7001ea76b9b2d8f896eb6609e2e1c5c562d2a6e74960" +
+			"2d67a0f6b43a201d5087509b8dc7b0440144e308c18ff8b96b607de2f20c" +
+			"6ee99bb05367a8b25947011889f724965a2b5c52c9db1e0622df9343c548" +
+			"d054699badeb15fc41055af0d79a2bfc1a5b4574634fa0dd9dd10a6213ed" +
+			"b6991187dc560facdc27440456a0a209fd7f5ee4fb350ae71f869723e5eb" +
+			"5338e3d1448bc993afca6957f4cc7b047a2c7c9593b7234725e66cc0eb23" +
+			"3824eb4cb905701cc522ec210950b871397c6c0bb3d0b839f2eb1a120f70" +
+			"36107246df4dfb2c24891bef0bd1dc131f2c9d7c295ee967e3184d963037" +
+			"fcc9e0b8c7011c8e04b4e70038150d34caab4f8c0230418cd2d8a91146e4" +
+			"4e11cf6707452ddc03d9b4e6380658135dfb48f62c0690ebad75167f4dd1" +
+			"c0df3ed555b5081a7b82616d9e501757c83c2193d0f640236d59f9c97a4a" +
+			"5c8bf532aea2cf5964ed2dbd8a70c01ca5c7677224cf2a37f3b24d8fe4ba" +
+			"91cd3b5033715de227de51deed15afb8eda9d2b9615d197b8f98322d7096" +
+			"79c5131eed48050fbe0145a9284e236605c25a4876e2adba42f4e35a8949" +
+			"3d59bbf44b3338d9d2e65a7d7ec6c863cd47cae9e23181b07298078a5e9b" +
+			"06a5c7e1059f474eb1a4247e8f02cdd4efdca67d22035b12abecf9b15982" +
+			"de4932a28e797bc4de38442cff2cba263eeddba0ab14fc706dbca04eaca1" +
+			"b4cc13000a10e35b32461424809b299798e4d8e66c92aa3181c5df16ab65" +
+			"9611cb625e895a8021af8c60960227d6f2ebeacb17b13536a5ff139734ef" +
+			"37cb67018ef9a410b856e6f6eddbe3f59b088d538c50a8f3f0912d06e47b" +
+			"88d773069aa759cc614e1f53cf6e572c127123d1ab56b79ee753a921cb22" +
+			"a60e4e6cae768c9966de4e2625484f2e990154da7fca84b6e6c0b59201e7" +
+			"fb8a729cb20b4c774381e84f1bd6e304543d952dc76ef741b72f3a4ca7a6" +
+			"ea7958b8b6337994ed82dcf988eb70f509610b9a279ab4d0f28cc2b2dd99" +
+			"3b8637a6be0cb4b5f67c79654c6b15e1b61120374ba9b974a628c547f11e" +
+			"52d72d39f8f9c5dbfc23a89f22d38984dd8d5c3ca72cd54e6adfe2b3d163" +
+			"86afdb50967846a4c311351a51e5fd322757bdb061d44c8796a61fa4db36" +
+			"793bc11984eac83bbcefb40d0bc7bab0ca81e7df3a7f58c6fe800396716d" +
+			"832acaddff6d72c8e19dc9ea838294ead800deadb6bc18d3e399fa76c46c" +
+			"5d88ee72a86a87399423b0578eb6e27d78156ea2abf6f08b5cbf747f2f74" +
+			"5301b694bfba84bfe3c5527acd50660eea5105a2644c1aa92f954a604fb6" +
+			"a1b3b2d0331497deafc3aaadc7040b9188a36cf607ee85a0655ae963fd32" +
+			"91dd58f8bb50b4e46dcf7c2957639bffa6b12d895660dc0323b7a092f999" +
+			"813380b820e1873c60d3e3038129c66d507862100a5d5842150869e7873d" +
+			"6bb6ad022350ffa3813aca26c80ccae72692bed9c77c9d4da23178c57153" +
+			"90b5f4505240a796ec9d10a7f280bd60a570b1b693453807707651fc0464" +
+			"03e4768965a6f42f112152942134f0a38c84137c7a6e086ef1ab9ad20d24" +
+			"3b93356b305c0996ab7d02c02c44cbaf8f7e60b8c0b8c9fece3f189b099d" +
+			"dbd126b7357c1c4ea1c8bc1ad93db91ea9bf043a4320acb60b502bec37b8" +
+			"6b2a5004b8225e549e613c6f83b97b7e4aeda1b013e0a442d7ce2f14e78e" +
+			"a94bab700c9ac0abba945e28f39fdadff223c4498cb204f01ddfcb450a41" +
+			"f32ae47f99a49114c6646a5cb103e9cd75f9d81dba417e48c4053e3b0295" +
+			"2267cd30589b0f5d993a5485a6ead1ffab9f2f4294c5853ba76383a326a6" +
+			"a42fb8b78948aa49f0f1f614bd0a3fbd2a58a3197daf2094605bd838285a" +
+			"1260f1265dca74aadd95652632335fd17cafcb73b202c3f0e5da836c2dcf" +
+			"2934f005935dca80154af43fa34c8ba440d1581b74ff17dfaca369dc9aa6" +
+			"734c03916d78e1b952691cef918fe033d33f7f4323cf724ffb8cd6c219bd" +
+			"046e9f268eb0601098e93daa59dde370e46269dd7c54891f71bee2829a53" +
+			"df86a2c7fb1046cd7c98fa21cd83597be554997a70acebe0b6e60f1f7098" +
+			"6f65adcae24385cb7102bdd3e01300ffd15d00f9764b3a5c51e35e5c9cdd" +
+			"da84f4b656fe514ec4ff8dcd774373f8a9103cf36abefe875f7084b9bbd9" +
+			"42e0c997ec2d860a4b622ff1a39a628582fd81f237d3d8f6843d26ac77cf" +
+			"bd48003e8e8c591ff813a9a897e3149ff0297ff476299d717e54d885cdd4" +
+			"4c3ba6ebf54bc7a1",
+	},
+	{
+		key: "b15578da1020f662ada0ad4f33a180d9f8ad4991b3720bc42a22b52625c7414a",
+		tag: "b0e4ad4a010afd6dd41ed82868cda555",
+		in: "6d2afb7a9154064341bdbb533f11990d4987e7c90fbfc0167c1e58d6efff" +
+			"6010f7ed569dac62ad37183b0d384519ebed0bf9c6e05a070b4858e6b846" +
+			"547ab5e45619c866f83cce83dcdab6a8a6c36b115ac832de1c6d433b94fa" +
+			"35803fa1a36f1ee114f8632402a027a74ac110394f32ec4006beb0057f09" +
+			"a94dada8bd0d1ca9a14b1f2efb8f526d79d6438bbbaac0ca1a43935627e5" +
+			"d129d52c06bf6413af07513bc579447eccc3a9406645c94dae59dab98d6a" +
+			"f92fa90fd4efaaa4bec466806ed401d2083cda587139ad7e9ee2adbb1dfe" +
+			"a88b59dd788b954a0f52c3854a3fffecb4bea83debbb2f5f8883e6415d3b" +
+			"ac1b872df1afe185468adc59364c173082f1dd6da9d348f5f5ba2d216243" +
+			"23de1f623eeec875bf31d12acec40dc0c1b9562826f3105cdad4c43cf45d" +
+			"829aa8b14012c47847aef7a2a6e3935fd972235f5d3a7ce4ad3582785393" +
+			"602e2e27329914021eff38ed2926c88acec1551f17a1b818fc1c3ed4b3b6" +
+			"6825d55bea269d710123b52e12ca9520a069d9c6a21df3a0253b3a4a6a8c" +
+			"dc226d667541548834da6bdbbdc165f39e40047d4b647c507d981be17b3a" +
+			"836063436241a8bb46b11a2867b621413c42d838e4578b72cc1982e34bde" +
+			"c303b5575ef4b8dd9fea8ed5bf69539413909d03461d3853b5fbf714a61c" +
+			"769569f42b38fac4b849104e2f2ac1dad0e388646278789f83e0b0511571" +
+			"019d3bfc5b03ca4cb5564e4e75e103ea1b6000be6588e27105d7cdc2d2f1" +
+			"f680ad34ef823ac4bd4068146e9997834665aec7dcc7a82ff28d85d52dd6" +
+			"9c18dd35f326bcf709f74df5981bb90ca8e765fef9f0698a19e12220b287" +
+			"24a6d9e4f4c7ce93f8ca9a126689ad1df820072557ce3db246cdf41599dd" +
+			"44ca841bece6c7869358005536e1189aa86b764e890ef90970d6e3831def" +
+			"fa890bf8692381123924e7d9df804fd770a0a30ee97d5dcdca302833efe8" +
+			"1d4b2505b17382f0b3429b38c41269ac95e36e9f5a1dbc6e6c8963741917" +
+			"02a23198decb4efe6809fcbeb5d0c9098a4c300155dc841610e55c8a6e27" +
+			"2a38a39de3d8ebf38a750af25836ffb1bb7822bb98886280f0cab6838c01" +
+			"cec57961bdc2e1bf158248309ff9294adcb962252b1c24646d132a3be2c9" +
+			"1ff82e8e101facbdb807826cc9d1840a90874ba08692e808c336c9d280ee" +
+			"f36a43a75c746fb864f85711e802546ab5cc3f8f117904ba1a85d6e4b729" +
+			"85122c5041891e16d55b93d6fc1b7fcfdc80ed3d72d55d64b8895bbf2f8e" +
+			"d188684e7e89afdc1e6a7ab9bd1d3da95d68698df2cdcbb2e1a4ae70e2fd" +
+			"dd4760f9e5cf4255eeb1e9e8009ab507395bacb8b2177e7c5757ad02baa9" +
+			"a96db967d20a150d2dd7f3081d90675fe0c82f94aa3cfdf6ac5585583901" +
+			"7a8e122170cc817f327a3c8ef44acd6e4fa81b73bcd0bcb5792eed470481" +
+			"152e87f7a20c3f7c69d5a8199bf9bb7c7269b450dc37a9b22102acaa8438" +
+			"134d6d733d231cee9522f7d02fbb37b5818ad3ca72df4752230ee11392ef" +
+			"8f8219be55202bc3d476f5a9078b32fb63d42bed4cda5ef90cc62467bf5e" +
+			"418ecd9d5d0cf1a33eb9a930e652ce96057fef40b65588aac67621d651a0" +
+			"9003dbc3925912e385296cd3b2b386a44113308ddf2af52ca390487eb20c" +
+			"716b76d78ad45129e7c285d918de7107ea8c3b0cfd9e73933b87c0b2b505" +
+			"cb4c95794f2ee6d6d43e2e76026923a0bbfbc3bb22df9ad729452283ce62" +
+			"dc9b26684fd45e07650581afd73713a708869a069c58b599ab478974f206" +
+			"dbd3e4e563e346ff1881723c5fd440bdf9f70f761c6f746113397d7c04b6" +
+			"b341d7e44de7de0aae79badaaef5ed372ef629dffd52926110683ab2d4da" +
+			"a4be83eb86c8700703a660edd5a5029f66f1581da96fe1feefc970ab4086" +
+			"a83ae02e959821967bd27b3b629652f5bc3db2b7f1af674f9f3fb3a788f7" +
+			"88e6dc1722382971831a7ed72502f85b25888c1534d81c0a4f7351ecc40f" +
+			"4e0412e05718403fae5746d313a78c80ac297f1391ad389070410e1330a1" +
+			"b07d683d1c795bda74bde947f2cf0dc9638b5d0851cda27df030403816dd" +
+			"3b70f042888c9c192656cc4b9fea10b81b5347900d9199c8f0f47d42f2ee" +
+			"482b68acfa5ff47d9950c950a926a497d94c6a796e0b715416520bd6c59f" +
+			"30217718d5f1d7bf7c24039f6467214ac8783cf011b25c37c67dfddde426" +
+			"40afe97f94879f4586954737b86701b32d560f08caec3fc45184bc719c7c" +
+			"5bf699074fde814acae32c189158c737665a8f94637068322f0c23ff8860" +
+			"f1b1c1bd766440afee290aa6f7150c7adefa6d72a738cd2268da7c94788e" +
+			"bb39002e9a328a51f3a92dc5c7cd9e4faed5702d3592ad16217c4978f84e" +
+			"af0fd2c9e4c6f4dcdd9112c781eb41a9aacb0f7935bb5c92d41e67cfff6b" +
+			"991ccefbd667ffeded1de325da50c33e28e2eef2f636c9726dc5bfe753ee" +
+			"c7bb6e1f080c89451f81bc8c29dc9067ce83deed02769714fa9bb477aca5" +
+			"c09089934674a0cc8e4b2c3136b2e4af8040cc601b90a4dec898dc922ca4" +
+			"976ab5ae4ac5af93fa5b1854a76ac3bcc2090bdeaa49ec4f319cf7c7b674" +
+			"6d8e617abb3361b28b27983dd1b139ec4f5af7e116439d7ecb16534817bf" +
+			"264dbd8f59e80b443be12c17fa013c7f4d029504c9bb62b296c2326f4f49" +
+			"cc3201b70ac3f62abb683c630179594a6d4cf30fd55b163bf8d01986bb6b" +
+			"cb7050fd527f095c45661920268e56f760fee80a29c9d37b7fc23f608710" +
+			"1e723038e64ee1b91c4849d69bd95fc9bc24fc4a234f4855f2a203e3f699" +
+			"c32698585c83781677739f2c48697c93b3388dcc64aa61f01118495ded33" +
+			"21ef9a1c949481f96005f8d5b277a7d6a0d906ec304cf4292df172e72d20" +
+			"29ecdeb65f06267a605f376804bf7bc5b82d5c8facfe7e41dc10806d27e0" +
+			"bcc5a341d80b3c1532407f75088716d732632cd88b0037f0d829bf385fec" +
+			"b52a202956489f61f16b0f4781bf59068b33d7330571d0b4a6ed91830258" +
+			"e1220b308784fa155be9bc821f5c0009a33802fa66dd66d1dde997dddd97" +
+			"873ddf65927dc1be979af2b5f110eee627dc1e210326ac20544a757ac168" +
+			"1823f3dd04b1ddc4bf96677a0a87633994e7af2ec99b7d5dfe44c6192be6" +
+			"a6e69d17b074256da3947808fbf68c7506a7e2c99e6b64d1ffadbd6285d8" +
+			"e7e032e24d42dde0594bf03fd550be05e5d66c91a660cd1ab7cb1f43fa9d" +
+			"69885203a7aee35a28f117427d7ac02b742f53d13b818f8631081b1730d1" +
+			"5b4e1e283cc8e5c4fc3b4652fce05fd8db821f99fcf93e6842816a549791" +
+			"7f6c49cc53d733788b2fe3c687de58bfe6153c70d99380df1fd566a7c758" +
+			"8052c62e73340d6a9eccd2ed26b763d518f3a0c4d6362212fbecebb4ffb7" +
+			"dc94d29944fcc4ab37725b105aa7571f364146782356d8ef056a0be93a55" +
+			"0c890df8fecc178776fe40703ad1bd2443d92c420be4306d99686592c030" +
+			"fd3e2230c0b48d8db79002e8a832ef27edb53a45532955f1171203d38414" +
+			"b4692e901e9f40f918528fc494430f86cf967452f456b01846ac6a383fc0" +
+			"de2243c7d804e8643aabcb78e2653b145f400a999670217c8da43bbb9c11" +
+			"e074176424be0c116c304a420120138e901eca4b12ce68fec460b23bc0c7" +
+			"765a74fc66cbda0e503e7b1baf5883744e468c97c5f1c4b0acc4b87de9f1" +
+			"4b537405dfb28195439d1ff848d9cd28a8d375038ebb540a9075b7b5074b" +
+			"ebc18418a370f1d3ac5d68f5d239513002ad11bfc2b7ff53e2e41ccffc4b" +
+			"0503acc4967c93ae8590a43439b5e7987d10cb8d1957bd9ef717ee3d12df" +
+			"5d6736c1d8bd8da102337a94b7d14f830f6c403cbaf7925a8a2a7af1311c" +
+			"57224967a38f6ca374013a9819c55fd2e2a5fac4f2490be5b059f4cd9c60" +
+			"2d62f80789eb8d9ab893c7f44a4945e41886af218179dfa754bbb59aab68" +
+			"13b71d2202eb8fc8a425625d21176a28a620e21bb0dad820c0b7051ce8d1" +
+			"3a33f3af0958bb6cd89f9d6414ab00ddd1d2f9fdece9183d0c05fcdfd117" +
+			"10d250e4b2029e6992a88293d0457e73e5b1b6a1aae182c69b9cb664992f" +
+			"073595ef68117026ad7ea579a4043cda318931eee7b2946a34cdc7c9755f" +
+			"80cc79a2bfe3ed9c79dc52faa5126b824868c965eeb37e9e4e6a49600f3a" +
+			"cce93c0853b546edb310dcd16a5755f15b1098b2f59dbd2d90e2ea8360ba" +
+			"f12108236e854465456598ae2f7bc380f008f2e3cd7c98c87643cafd7c36" +
+			"d40e2597236428d46aa5b260f84b4212d5e26804086adcf00363ce4becb4" +
+			"9b57eb2847b2f18ec82c99714ad4ddfe4ff3bcac1d0fcaa32660a1dccc68" +
+			"5bed83254c8e2ea0ae3632a70cfbcbeadef922d78a006d43ac7ab1f8a609" +
+			"c6e0ebc3ca6bb8430f1a562f41010db74b9febf931ca794fa08d1bc17780" +
+			"532ae76f25c4ee679d788835dfa4e70ca154c9e2865c3750ffe7b837eed1" +
+			"972be058fdf2bdb3eb301867bb132306c7aa237f6771d60bbc56cf31cb30" +
+			"32a87204d454542de747418470025ab84935d3eaaca01dbbdae9ef6b5d3a" +
+			"ca62ce9f871a3e1272b2b671582c096a349c00f32d742ddb17993994d8ae" +
+			"fc178cbcf9abc03114ff2bf7db8f757c63d6898faccd822f5c2e9a7570fb" +
+			"9cfff148570888be24ae42644c1a5bebb6f6287147a4bcc01c7675be9e4a" +
+			"897519dd3132a7cc2e778f8c90d23dc8073f6fa108d7ef82d561794bd9d5" +
+			"f1faa306334f338ac3ba99c853f79c24f7048fa906fde87d1ed28a7b11c0" +
+			"66a3bb98f8d21055aaafdf7e069b77b60b3d5cbe7c5e4379c7651af955cd" +
+			"82a19a09caf36becb6cd3fe9e12f40379941542709991066df21b7b12dfb" +
+			"2416d83fcdc33bb583e3b42f24f53edf8dc7c579ad3be831c99f72bf9fb7" +
+			"a35b6562e824e039e6bf1adc8f5ca53846de7bae11c4317e696d887df33c" +
+			"525f0a9c01fc29f2c26c90b85fe82ed8bd50954cd4e9ac7c85c7f3efec75" +
+			"da1da4ed173cb695cee295190527edb3cb06c5dbdabe0228cc60b6455153" +
+			"76244f27aa56da2db10f2659090137ffb82c57233c833e0bbf22d6f647fb" +
+			"97b3652d2888b3ab08010b8e8a6967d560b747757806736dc98b78226634" +
+			"f1eecaa4a2e23ba36591acb5737d735c5bc7a2e36f1a46946927e061fdf7" +
+			"7a3b68ef582c26b01f5aa9a438ecc26c6941221d1590c838072f9e471fe7" +
+			"fd59dacb0d092d40d76ea2f7c6e954a132a015bd4cb31147f3ebe4518322" +
+			"916438a62836ac85a4cf4492190a85bcc8edb37e38b99ea552d749c30f74" +
+			"ca20c298165e8ed02d4671e0b41cac3a32a345b9349ad22c2a4bb2c16a4c" +
+			"e0613ca0f0518759f7d2b33cfad2fae764f410d4d9ff8a76ae02a8107e7e" +
+			"01d9cd0552676b85ba002f19c01ad5f416d1d08bb84fec7c3555b098dbce" +
+			"48e1a5d847895e54db9c5b80cc22d5b87cd41a1a94be102bdd45a3cda5d1" +
+			"181e10446d213d6b3fdc350d486d2011d705c5f16ccf7519065c47bad7d6" +
+			"89c71e5fdf9d04bfb91eb1f07fa0f001009c1d4b1f6a116a570823a8580b",
+	},
+	{
+		key: "392468efccff36dade31fc1c62eb38bb61394fe448def9d9d9beec2413ddb418",
+		tag: "e1122e7c8e6965b90addbd46d8a548d6",
+		in: "6a13d37f0ec933194c227351f4a19b507d93465b1f3e88dcb5f1ed1262fa" +
+			"58ea99ff31e6fc85c39c04129fa69195b71b2060122fe618dd9430a63f97" +
+			"54b52a80b3cd099f248f91a468bae211a27bdb47ba005d29881ea5143a82" +
+			"967c4c30c9a4f0dba1a4975e6407fe296d40023a00efa06be763f2d73d46" +
+			"a2901ae28b3d8ce18009a462e223b71476d7b954c138e177d15a390847de" +
+			"96a7f7fd0598748e86b0f08e64d915e67c7e3cf936f3dcd60edebd36e2a1" +
+			"d65b6ac29530c48ab3bd52d45b4f938a19b9b31e2911105a8561600d5377" +
+			"905a67112ec28025aa680350ff85b808c5b4c98b7b9567d03f5ed3911ec9" +
+			"365a8de4b15ca62adaa69e5ba710eb1756a346016c67a297d8624f9f1ab5" +
+			"b3fbce98b141049f0ce26c85d2f8a9cc6ca8ab6c6e148be968931430dcc6" +
+			"2bf58ea9698ef52a5d271cf48e6748ac9e04bc7ae7da205a1a7535478322" +
+			"d820eca146cedf4b2f9aa9fcfd77ab56a7276977401dcc1f96baa1b607e0" +
+			"256bd04ec324ec67a4313e2d5a53d3a3fb5332927929b20c63bde805f637" +
+			"eb1050fee2a152a0405634f55c48a59fe370d54b2ab1671dae2c7fd92243" +
+			"10627808e553127c74f724362b4a6ee49b697daae7df3ddc5d2ed9d6befd" +
+			"77fb9f68fe3041f6ef13f46f34ab682ab8563e8996344f82b2ef006a8d54" +
+			"3dd9c1db4979d7da97bda45e722065f8a238f0873217b783a9a629a12b3a" +
+			"4de437445039997bd243efbf5e3b6059b9459d395290efb9081c632fb694" +
+			"81000dc74c395cb507422df181aba20f776ce3fd8765ac485021992c98b1" +
+			"67c68805662cb4356a0ee7ba6bdae51ac10cd06bb5b2f3a72841c714c8ed" +
+			"bc56998fe2fefb9bf69e172fdf54b2ab138ae59372c52a67e93882a3000f" +
+			"d966992aa2250c6ff93e9cac89645d70625d79332ade5dab7eb1adbe7dce" +
+			"5a013fb65ad32fe22ed16fb9bb35eca1f37a0433c320e8752f8fc4b7618c" +
+			"5e4df2efece832e259ad98b895c474e47d0e3fc488bea8f717a17de0dcf7" +
+			"597fb8fe12e62246296f9a887dcc3a700820c190a55a4931a7d44bd3bb2e" +
+			"ab6c8a8126f1be93790cebabc1d69e01796e6cc80e7c16bbc82fb333fb21" +
+			"c774ab7db843242838e82d8e1cb8ccab385e67a4271fe7031d74b6e8edcc" +
+			"8ed585d1c05a365c7665899c1dbc561151d3b44bceace77c4f53c0e0f6f7" +
+			"74d42f9ad3e56f1c2a8d53879d695f895690afb4698472a3d52d67159313" +
+			"133c87823fe0500eb68fe286f8b9a2f59f12785d026dc97bdbf793c7d1eb" +
+			"155f1f136aae66c256583e987f718afbe733e0a5ce30d021493fb84e2242" +
+			"5b18754d126235ef80335004fa84f88361a584753df409360cd8bd45bace" +
+			"8f48156bec66577bf2c685089f5ac7e7ec76c0df068fbaa47661f8517f92" +
+			"e14723b3b278f151816537a7212c96bd340a00c15c9c9bc9a2a5d163655d" +
+			"84b38073e2be9217cad97d362d89d4baf3ce0a8d8562f19a8c97a9aaf5e7" +
+			"77d60456360ffb77b30f177d2809052020d141697ecf9cb65f42b9190caf" +
+			"6540b2c82f6e5a8482934a6a1a5711a8c24546cd8ba432068404eae5a827" +
+			"2e09efc3c6037af4feaac0a46329229b010ecac6b9f077a9b076bb6d9ce1" +
+			"38401eb38d124baa11507a994185295020bf9b754fcf78430db9253f5929" +
+			"87c46c0f8589c4e463b15a3840b1cea795e24cf6b20f29a630136e0589b3" +
+			"8dd7fbe5ea21da72c88bd8e56473586822aa3765660a45a988df9b8eb8e8" +
+			"141939d3e4cc637c5d788064d40a9f7c734e43fdf8d7189a5d76700d9743" +
+			"fe0122944663afdb88c5201318ca782f6848b742ddebe7463fd4a32280ac" +
+			"1cf8311e9137d319de05ce9cd85abab24c5364041c14d3b4ce650400498e" +
+			"122166eccc12784b7ac3b262ac0b198ffc26eeed9a5da5374f7a2a53c87a" +
+			"78c217ea1fbf8d38f62511657b73109f31691aef14d82ce6e1010eae9e6f" +
+			"a419e5c1c16c0cc70651eb3374c03549a1bc7d3ed42d60f886102c798dbc" +
+			"ba56f0a2b3b9b412530c35f5f7ed06311ee14571f9c26ed9c81ef38ff000" +
+			"2f5ef3aab7351e32049a6ef8f48a43da1d84402d229df513dfaf1b2e4043" +
+			"6ce68c70ebeddd7477c9164f0dce45a6fc5de050f52ec269659d5854bcae" +
+			"f7762ed7400713c27a4d523eaf8c136c4a1ca00b9e9e55902daf6cdf8528" +
+			"c22ca1f2fa7ce87902d75a6850e1a5a4592497be1bb401878f18b189b0e2" +
+			"c59d10705bfabde3cd2da01eb452006b294108d5d42e88e9e15424d8cd0b" +
+			"8ab43a6c546b3dbf52e47b59cde6a3e417b0395220b6d63736d429da3458" +
+			"9a2524f1629320206fa7f1d8a041e17222c4a5814561937e1030e6375c77" +
+			"9dc988bb928bbdbe2c2eb20111639725d82b5d7192cd3e4acc27581f0ba7" +
+			"286cff41f97aa5a52ea0083de5057fd2ba985aa738e4d03fcf11ebab1d97" +
+			"e2ac77d1c2beb8799150a421a07b3777d0b850f24194b8309135b13da6c7" +
+			"e38653a711e407a1811290fbb7bc15d8b12efc6916e97ead41e042a44721" +
+			"e9cde3388073d921595bcddcac758dc675173f38242e65e4a284aaa7e8fa" +
+			"6adddaf00bc46428ab2d8601205b8895bcedfc80ca0aa4619ed6bb082ddf" +
+			"33ec04fa5d417f33fcdd238c6b11320c5a08f800e0f350b75d81e3bcbd15" +
+			"58a1eab87a3c8c2ffd7ba1d7e754e607cf98ba22a3fc766c45bd6f2569b4" +
+			"84639e6611714119d188a24a5e963089a16ed34e20b9f154cad8ac6031dd" +
+			"7a3a885afc2ae5e003ae8d4e4aabdb3e51dfc423b8cf4ed9ae2010072cbb" +
+			"b1108c7da1ff075e54ed827a0963ac5523ecdf3fc5eee7b4d1a6773764ec" +
+			"5c30f41690523fd70d895edb7ca6a1806d54240c4c7b43410da73503a323" +
+			"90d9070ed30da3a2fb5eccd40d083be7cf8bf40b4279f819cf795b6f075b" +
+			"5a67a10a06a6076d0d83c72efea05f244901c4b5fd9eb380432519311baf" +
+			"8c81f6325df4d37ff4d30d318f904ebb837ec76b341dd00a8f247cf0bbe9" +
+			"6f3784dc8f5feb344958fdf1a9ececb105f8770826db1f17a5281e997951" +
+			"d3c60cc28fc3e66ffeb5dbac315f98f6d240208043f28dee963d843e68ab" +
+			"57d847f76ae2f96ce6e37f377ef5dfef2176ecd7440ce4dadcec2231b606" +
+			"e4a80420fb3ed135640e1f05d6bd58b8dce062dd7d36b885d424f6318e5e" +
+			"a0753efbb33bbc7360d2b5dfab3ae0d5e000b8d31f2ba0f5fd8b34f96b55" +
+			"28fff35e769461d0f03cf3bfdf0b801dcbbf2838180cb9b108e06c353e3f" +
+			"0b9ef61678cfed1ea37ae76bccb5ef5957ac2c8e8f4794c8145a15f1cc88" +
+			"bfb0881080326c481b373c3bc9b07a9b60a0c8bd5fa4f6f90145590a5227" +
+			"6fcc0ccc2375d0ccb571d414d1b0c38b4e02c39db4d701c5e25e90785ef4" +
+			"d26f35edd8c4b96455bdca7245cfefd9cfbd2f319615e5fdf07bb9564fa0" +
+			"44bb35a58391d02e3927780b4076bc0893dfcb4b63a32cd7a541a4a8c253" +
+			"0349c6e96e378dbeb66dedf87d813d0b744452c1c4088507dca722193827" +
+			"9e2dfa24e4a409de494acf654f44262db9206a7717fa434ac4fdc6a6eb5b" +
+			"1fd5a193b6043bc4327c8c09fd6822eaa9df37bbcac1077754a295621601" +
+			"267b68733b62dadc2563f1700af180141f29899e2689dbbe9745ba8477f4" +
+			"352921900b403a01c9dd042a8c1b0e0489959fb0b0a8431c97b41e202204" +
+			"212ebfa00c593399dbd14d7aec07b8292d2e40b48f05fcd54a15da4a24d7" +
+			"2759e409f4c7b5b98fce4abac6c30e4872d92efa1f96479ec30f21699825" +
+			"50fa60584f5a09051a00f8e7dbb3853e66ca3f05fbfe43bef9b120a25a01" +
+			"eb436ba8ecda715201eda72e517d628f883386c1503aa8b8e75610f7155e" +
+			"9f916335ab6d6f0f9589b6220cd2b81c2c937dc065d3d14a7df8cc916cd0" +
+			"0ce1bb53fd9c8974298d3bd316f3658aa8cc6904f073a1472149e4b08c64" +
+			"5e11abe0428ccb6174df2103edd735965d6454b543d3f01410f77053f65e" +
+			"c1d1aee56fdd3af23bcd4e1a7fcc4e600c4831007c33fe5f0c8300f686eb" +
+			"9b4d1e4f08fe4ddc8a90be14dc3a5a88ff96716509341d5db24c0d016863" +
+			"998b1859c5021df815a6f1ca9845f1a8e99dbad132b406227c5897a1bdf3" +
+			"e698962f799133ff4429decbef6ce036296facf38e4812fec102b76c6d30" +
+			"beba1b70722254fafbc471096153478c971db7d96263660209265cb10f13" +
+			"b34b5fd55c4abe818a5f9715d8a85094e2946b7a001b47f629e26c636d86" +
+			"4968ad2ab616dfe28840bd60b4b9855c8dbe1cb873fcbc4577b5fefeb8bb" +
+			"4832039867dc35db9c036c83bc204396e3474ddfe806c77c65c936f488b6" +
+			"7c1028739562d7bb055d21441af29ae2921290e548dccf8a56021385422b" +
+			"15da6b232b24151309a75a00296d11aa1952a1513110b0faa93d1d8cd9ae" +
+			"fa9f1c59377ec9165b2c9e07cbde40db7b81bca6d58fc28bae8f473cd0e9" +
+			"a2420e0b943a83d284108626c24ac570b1d6c1ab971e71f43fbd6c00e171" +
+			"238141a6dc987a60385c3a04dd147a2f8e80dfe727b104c0fdd80b326f59" +
+			"0b9f86fd7b2fd1122a390979889eabd803ab57159c8509a1443eb6789382" +
+			"090a770ae4eba03306f96e50e19a7d44c584ccc230d104548946efca4520" +
+			"d61de5f473e2f4eada6c8ce9c7ee975eb4f63c0483cb775ed7d3cf690a61" +
+			"7d6656d683a8512707d81ca5ba176a42bcffcfa692129f292607d2a47536" +
+			"ccaeb464c9272d6f3816074b712af602470088b253deba18771e5f67734b" +
+			"587707cdd06f35264b2262fd253c25b5d38ee7db287610e5398062b7a34e" +
+			"6e4cf7447d00873b930ad148fd96f0ab18771bc468b874bb109924101c84" +
+			"c4e239ecc7687d875e4d94a1a973620ca61e35a872c2e2e61a502169f1bb" +
+			"4e5ff5fa2bff657be6195b3e2c7151a52fc0096d98e7f08f5a98f570aee1" +
+			"7b4275f1356e87e080ce0e1b9bbabe7dea48b5903bc390ce23472ad64a89" +
+			"41c3247bfd23ea90b2dee09085571bad85568040105e098f993bb37e43c3" +
+			"e6d511171c77cfc450570dfb9fc6a3930ef43c03f8213f6203d545d791c7" +
+			"d3fa42d5dde1655038d35c5dfacc12e9dee24fe833977549eda68ae8b508" +
+			"be277e743921b584f9dfa0eefbd8bf3c23f51efdef7f7487001d29e8097b" +
+			"ba63289cfca743023d1668555a46fe6d5b7421377414df1e9ef135480622" +
+			"22e2e9a7baa618d88f407517f6317b6a0ba3384ace16d68631d59ea169d5" +
+			"092d20afc1a481b82be5e734bb092953a0a94702bae1a0f48d2a22b9a05f" +
+			"f64493b7b2e984f27582b1eb937fddf8512c49830435d146dcc291a4118d" +
+			"5dc638b99cdcbcc5860de7a92c5b13cbd1e01e051f01af40afe124346320" +
+			"d3626bf9d8f7850744e032a993c276fd388718237740c6caf260fca60b8d" +
+			"d846102e3262b6e05ceca00c6affe938fac1847350865fc858d3ddd1d130" +
+			"71d1221ce7c5d575587fcba580e544b74d877ed5ca92763ef0ca0d7bfa08" +
+			"d57a0216b2a01a2b9ec74b8430051e0074862b7be25b6766ab520f2eb75d" +
+			"eeb979c28f03795f6f1e4b8410beab19a20febc91985b8a7c298534a6598" +
+			"f2c5b0dc5de9f5e55a97791507bc6373db26",
+	},
+
+	// Override initial state to ensure large h (subject to h < 2(2¹³⁰ - 5)) is
+	// deserialized from the state correctly.
+	{
+		key:   "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+		state: "0000000000000007fffffffffffffffffffffffffffffff5", // 2(2¹³⁰ - 5) - 1
+		in:    "",
+		tag:   "f9ffffffffffffffffffffffffffffff",
+	},
+	{
+		key:   "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+		state: "000000000000000700000000000000000000000000000000", // 2¹³⁰
+		in:    "",
+		tag:   "04000000000000000000000000000000",
+	},
+	{
+		key:   "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+		state: "0000000000000007fffffffffffffffffffffffffffffff5", // 2(2¹³⁰ - 5) - 1
+		in: "ffffffffffffffffffffffffffffffff" +
+			"ffffffffffffffffffffffffffffffff" +
+			"ffffffffffffffffffffffffffffffff" +
+			"ffffffffffffffffffffffffffffffff" +
+			"ffffffffffffffffffffffffffffffff" +
+			"ffffffffffffffffffffffffffffffff" +
+			"ffffffffffffffffffffffffffffffff" +
+			"ffffffffffffffffffffffffffffffff" +
+			"ffffffffffffffffffffffffffffffff" +
+			"ffffffffffffffffffffffffffffffff" +
+			"ffffffffffffffffffffffffffffffff" +
+			"ffffffffffffffffffffffffffffffff" +
+			"ffffffffffffffffffffffffffffffff" +
+			"ffffffffffffffffffffffffffffffff" +
+			"ffffffffffffffffffffffffffffffff" +
+			"ffffffffffffffffffffffffffffffff",
+		tag: "1b000e5e5dfe8f5c4da11dd17b7654e7",
+	},
+	{
+		key:   "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+		state: "000000000000000700000000000000000000000000000001", // 2¹³⁰
+		in: "ffffffffffffffffffffffffffffffff" +
+			"ffffffffffffffffffffffffffffffff" +
+			"ffffffffffffffffffffffffffffffff" +
+			"ffffffffffffffffffffffffffffffff" +
+			"ffffffffffffffffffffffffffffffff" +
+			"ffffffffffffffffffffffffffffffff" +
+			"ffffffffffffffffffffffffffffffff" +
+			"ffffffffffffffffffffffffffffffff" +
+			"ffffffffffffffffffffffffffffffff" +
+			"ffffffffffffffffffffffffffffffff" +
+			"ffffffffffffffffffffffffffffffff" +
+			"ffffffffffffffffffffffffffffffff" +
+			"ffffffffffffffffffffffffffffffff" +
+			"ffffffffffffffffffffffffffffffff" +
+			"ffffffffffffffffffffffffffffffff" +
+			"ffffffffffffffffffffffffffffffff",
+		tag: "380859a4a5860b0e0967edfd711d37de",
+	},
+}

+ 2 - 1
psiphon/common/crypto/internal/subtle/aliasing.go

@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build !appengine
+//go:build !purego
+// +build !purego
 
 // Package subtle implements functions that are often useful in cryptographic
 // code but require careful thought to use correctly.

+ 36 - 0
psiphon/common/crypto/internal/subtle/aliasing_purego.go

@@ -0,0 +1,36 @@
+// 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.
+
+//go:build purego
+// +build purego
+
+// Package subtle implements functions that are often useful in cryptographic
+// code but require careful thought to use correctly.
+package subtle // import "golang.org/x/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)
+}

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

@@ -35,8 +35,8 @@ This package is interoperable with NaCl: https://nacl.cr.yp.to/secretbox.html.
 package secretbox // import "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/nacl/secretbox"
 
 import (
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/internal/poly1305"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/internal/subtle"
-	"golang.org/x/crypto/poly1305"
 	"golang.org/x/crypto/salsa20/salsa"
 )
 

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

@@ -29,7 +29,7 @@ import (
 	"fmt"
 	"io"
 
-	"golang.org/x/crypto/poly1305"
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/internal/poly1305"
 	"golang.org/x/crypto/salsa20/salsa"
 )
 

+ 140 - 10
psiphon/common/crypto/ssh/agent/client.go

@@ -15,7 +15,6 @@ import (
 	"bytes"
 	"crypto/dsa"
 	"crypto/ecdsa"
-	"crypto/ed25519"
 	"crypto/elliptic"
 	"crypto/rsa"
 	"encoding/base64"
@@ -26,7 +25,20 @@ import (
 	"math/big"
 	"sync"
 
+	"crypto"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/ssh"
+	"golang.org/x/crypto/ed25519"
+)
+
+// SignatureFlags represent additional flags that can be passed to the signature
+// requests an defined in [PROTOCOL.agent] section 4.5.1.
+type SignatureFlags uint32
+
+// SignatureFlag values as defined in [PROTOCOL.agent] section 5.3.
+const (
+	SignatureFlagReserved SignatureFlags = 1 << iota
+	SignatureFlagRsaSha256
+	SignatureFlagRsaSha512
 )
 
 // Agent represents the capabilities of an ssh-agent.
@@ -57,6 +69,26 @@ type Agent interface {
 	Signers() ([]ssh.Signer, error)
 }
 
+type ExtendedAgent interface {
+	Agent
+
+	// SignWithFlags signs like Sign, but allows for additional flags to be sent/received
+	SignWithFlags(key ssh.PublicKey, data []byte, flags SignatureFlags) (*ssh.Signature, error)
+
+	// Extension processes a custom extension request. Standard-compliant agents are not
+	// required to support any extensions, but this method allows agents to implement
+	// vendor-specific methods or add experimental features. See [PROTOCOL.agent] section 4.7.
+	// If agent extensions are unsupported entirely this method MUST return an
+	// ErrExtensionUnsupported error. Similarly, if just the specific extensionType in
+	// the request is unsupported by the agent then ErrExtensionUnsupported MUST be
+	// returned.
+	//
+	// In the case of success, since [PROTOCOL.agent] section 4.7 specifies that the contents
+	// of the response are unspecified (including the type of the message), the complete
+	// response will be returned as a []byte slice, including the "type" byte of the message.
+	Extension(extensionType string, contents []byte) ([]byte, error)
+}
+
 // ConstraintExtension describes an optional constraint defined by users.
 type ConstraintExtension struct {
 	// ExtensionName consist of a UTF-8 string suffixed by the
@@ -70,8 +102,9 @@ type ConstraintExtension struct {
 
 // AddedKey describes an SSH key to be added to an Agent.
 type AddedKey struct {
-	// PrivateKey must be a *rsa.PrivateKey, *dsa.PrivateKey or
-	// *ecdsa.PrivateKey, which will be inserted into the agent.
+	// PrivateKey must be a *rsa.PrivateKey, *dsa.PrivateKey,
+	// ed25519.PrivateKey or *ecdsa.PrivateKey, which will be inserted into the
+	// agent.
 	PrivateKey interface{}
 	// Certificate, if not nil, is communicated to the agent and will be
 	// stored with the key.
@@ -179,6 +212,23 @@ type constrainExtensionAgentMsg struct {
 	Rest []byte `ssh:"rest"`
 }
 
+// See [PROTOCOL.agent], section 4.7
+const agentExtension = 27
+const agentExtensionFailure = 28
+
+// ErrExtensionUnsupported indicates that an extension defined in
+// [PROTOCOL.agent] section 4.7 is unsupported by the agent. Specifically this
+// error indicates that the agent returned a standard SSH_AGENT_FAILURE message
+// as the result of a SSH_AGENTC_EXTENSION request. Note that the protocol
+// specification (and therefore this error) does not distinguish between a
+// specific extension being unsupported and extensions being unsupported entirely.
+var ErrExtensionUnsupported = errors.New("agent: extension unsupported")
+
+type extensionAgentMsg struct {
+	ExtensionType string `sshtype:"27"`
+	Contents      []byte
+}
+
 // Key represents a protocol 2 public key as defined in
 // [PROTOCOL.agent], section 2.5.2.
 type Key struct {
@@ -260,7 +310,7 @@ type client struct {
 
 // NewClient returns an Agent that talks to an ssh-agent process over
 // the given connection.
-func NewClient(rw io.ReadWriter) Agent {
+func NewClient(rw io.ReadWriter) ExtendedAgent {
 	return &client{conn: rw}
 }
 
@@ -268,6 +318,21 @@ func NewClient(rw io.ReadWriter) Agent {
 // unmarshaled into reply and replyType is set to the first byte of
 // the reply, which contains the type of the message.
 func (c *client) call(req []byte) (reply interface{}, err error) {
+	buf, err := c.callRaw(req)
+	if err != nil {
+		return nil, err
+	}
+	reply, err = unmarshal(buf)
+	if err != nil {
+		return nil, clientErr(err)
+	}
+	return reply, nil
+}
+
+// callRaw sends an RPC to the agent. On success, the raw
+// bytes of the response are returned; no unmarshalling is
+// performed on the response.
+func (c *client) callRaw(req []byte) (reply []byte, err error) {
 	c.mu.Lock()
 	defer c.mu.Unlock()
 
@@ -284,18 +349,14 @@ func (c *client) call(req []byte) (reply interface{}, err error) {
 	}
 	respSize := binary.BigEndian.Uint32(respSizeBuf[:])
 	if respSize > maxAgentResponseBytes {
-		return nil, clientErr(err)
+		return nil, clientErr(errors.New("response too large"))
 	}
 
 	buf := make([]byte, respSize)
 	if _, err = io.ReadFull(c.conn, buf); err != nil {
 		return nil, clientErr(err)
 	}
-	reply, err = unmarshal(buf)
-	if err != nil {
-		return nil, clientErr(err)
-	}
-	return reply, err
+	return buf, nil
 }
 
 func (c *client) simpleCall(req []byte) error {
@@ -369,9 +430,14 @@ func (c *client) List() ([]*Key, error) {
 // Sign has the agent sign the data using a protocol 2 key as defined
 // in [PROTOCOL.agent] section 2.6.2.
 func (c *client) Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error) {
+	return c.SignWithFlags(key, data, 0)
+}
+
+func (c *client) SignWithFlags(key ssh.PublicKey, data []byte, flags SignatureFlags) (*ssh.Signature, error) {
 	req := ssh.Marshal(signRequestAgentMsg{
 		KeyBlob: key.Marshal(),
 		Data:    data,
+		Flags:   uint32(flags),
 	})
 
 	msg, err := c.call(req)
@@ -501,6 +567,17 @@ func (c *client) insertKey(s interface{}, comment string, constraints []byte) er
 			Comments:    comment,
 			Constraints: constraints,
 		})
+	case ed25519.PrivateKey:
+		req = ssh.Marshal(ed25519KeyMsg{
+			Type:        ssh.KeyAlgoED25519,
+			Pub:         []byte(k)[32:],
+			Priv:        []byte(k),
+			Comments:    comment,
+			Constraints: constraints,
+		})
+	// This function originally supported only *ed25519.PrivateKey, however the
+	// general idiom is to pass ed25519.PrivateKey by value, not by pointer.
+	// We still support the pointer variant for backwards compatibility.
 	case *ed25519.PrivateKey:
 		req = ssh.Marshal(ed25519KeyMsg{
 			Type:        ssh.KeyAlgoED25519,
@@ -618,6 +695,18 @@ func (c *client) insertCert(s interface{}, cert *ssh.Certificate, comment string
 			Comments:    comment,
 			Constraints: constraints,
 		})
+	case ed25519.PrivateKey:
+		req = ssh.Marshal(ed25519CertMsg{
+			Type:        cert.Type(),
+			CertBytes:   cert.Marshal(),
+			Pub:         []byte(k)[32:],
+			Priv:        []byte(k),
+			Comments:    comment,
+			Constraints: constraints,
+		})
+	// This function originally supported only *ed25519.PrivateKey, however the
+	// general idiom is to pass ed25519.PrivateKey by value, not by pointer.
+	// We still support the pointer variant for backwards compatibility.
 	case *ed25519.PrivateKey:
 		req = ssh.Marshal(ed25519CertMsg{
 			Type:        cert.Type(),
@@ -681,3 +770,44 @@ func (s *agentKeyringSigner) Sign(rand io.Reader, data []byte) (*ssh.Signature,
 	// The agent has its own entropy source, so the rand argument is ignored.
 	return s.agent.Sign(s.pub, data)
 }
+
+func (s *agentKeyringSigner) SignWithOpts(rand io.Reader, data []byte, opts crypto.SignerOpts) (*ssh.Signature, error) {
+	var flags SignatureFlags
+	if opts != nil {
+		switch opts.HashFunc() {
+		case crypto.SHA256:
+			flags = SignatureFlagRsaSha256
+		case crypto.SHA512:
+			flags = SignatureFlagRsaSha512
+		}
+	}
+	return s.agent.SignWithFlags(s.pub, data, flags)
+}
+
+// Calls an extension method. It is up to the agent implementation as to whether or not
+// any particular extension is supported and may always return an error. Because the
+// type of the response is up to the implementation, this returns the bytes of the
+// response and does not attempt any type of unmarshalling.
+func (c *client) Extension(extensionType string, contents []byte) ([]byte, error) {
+	req := ssh.Marshal(extensionAgentMsg{
+		ExtensionType: extensionType,
+		Contents:      contents,
+	})
+	buf, err := c.callRaw(req)
+	if err != nil {
+		return nil, err
+	}
+	if len(buf) == 0 {
+		return nil, errors.New("agent: failure; empty response")
+	}
+	// [PROTOCOL.agent] section 4.7 indicates that an SSH_AGENT_FAILURE message
+	// represents an agent that does not support the extension
+	if buf[0] == agentFailure {
+		return nil, ErrExtensionUnsupported
+	}
+	if buf[0] == agentExtensionFailure {
+		return nil, errors.New("agent: generic extension failure")
+	}
+
+	return buf, nil
+}

+ 189 - 16
psiphon/common/crypto/ssh/agent/client_test.go

@@ -8,11 +8,15 @@ import (
 	"bytes"
 	"crypto/rand"
 	"errors"
+	"io"
 	"net"
 	"os"
 	"os/exec"
 	"path/filepath"
+	"runtime"
 	"strconv"
+	"strings"
+	"sync"
 	"testing"
 	"time"
 
@@ -20,7 +24,7 @@ import (
 )
 
 // startOpenSSHAgent executes ssh-agent, and returns an Agent interface to it.
-func startOpenSSHAgent(t *testing.T) (client Agent, socket string, cleanup func()) {
+func startOpenSSHAgent(t *testing.T) (client ExtendedAgent, socket string, cleanup func()) {
 	if testing.Short() {
 		// ssh-agent is not always available, and the key
 		// types supported vary by platform.
@@ -33,17 +37,19 @@ func startOpenSSHAgent(t *testing.T) (client Agent, socket string, cleanup func(
 	}
 
 	cmd := exec.Command(bin, "-s")
+	cmd.Env = []string{} // Do not let the user's environment influence ssh-agent behavior.
+	cmd.Stderr = new(bytes.Buffer)
 	out, err := cmd.Output()
 	if err != nil {
-		t.Fatalf("cmd.Output: %v", err)
+		t.Fatalf("%s failed: %v\n%s", strings.Join(cmd.Args, " "), err, cmd.Stderr)
 	}
 
-	/* Output looks like:
+	// Output looks like:
+	//
+	//	SSH_AUTH_SOCK=/tmp/ssh-P65gpcqArqvH/agent.15541; export SSH_AUTH_SOCK;
+	//	SSH_AGENT_PID=15542; export SSH_AGENT_PID;
+	//	echo Agent pid 15542;
 
-		   SSH_AUTH_SOCK=/tmp/ssh-P65gpcqArqvH/agent.15541; export SSH_AUTH_SOCK;
-	           SSH_AGENT_PID=15542; export SSH_AGENT_PID;
-	           echo Agent pid 15542;
-	*/
 	fields := bytes.Split(out, []byte(";"))
 	line := bytes.SplitN(fields[0], []byte("="), 2)
 	line[0] = bytes.TrimLeft(line[0], "\n")
@@ -79,13 +85,12 @@ func startOpenSSHAgent(t *testing.T) (client Agent, socket string, cleanup func(
 	}
 }
 
-// startKeyringAgent uses Keyring to simulate a ssh-agent Server and returns a client.
-func startKeyringAgent(t *testing.T) (client Agent, cleanup func()) {
+func startAgent(t *testing.T, agent Agent) (client ExtendedAgent, cleanup func()) {
 	c1, c2, err := netPipe()
 	if err != nil {
 		t.Fatalf("netPipe: %v", err)
 	}
-	go ServeAgent(NewKeyring(), c2)
+	go ServeAgent(agent, c2)
 
 	return NewClient(c1), func() {
 		c1.Close()
@@ -93,6 +98,11 @@ func startKeyringAgent(t *testing.T) (client Agent, cleanup func()) {
 	}
 }
 
+// startKeyringAgent uses Keyring to simulate a ssh-agent Server and returns a client.
+func startKeyringAgent(t *testing.T) (client ExtendedAgent, cleanup func()) {
+	return startAgent(t, NewKeyring())
+}
+
 func testOpenSSHAgent(t *testing.T, key interface{}, cert *ssh.Certificate, lifetimeSecs uint32) {
 	agent, _, cleanup := startOpenSSHAgent(t)
 	defer cleanup()
@@ -107,7 +117,7 @@ func testKeyringAgent(t *testing.T, key interface{}, cert *ssh.Certificate, life
 	testAgentInterface(t, agent, key, cert, lifetimeSecs)
 }
 
-func testAgentInterface(t *testing.T, agent Agent, key interface{}, cert *ssh.Certificate, lifetimeSecs uint32) {
+func testAgentInterface(t *testing.T, agent ExtendedAgent, key interface{}, cert *ssh.Certificate, lifetimeSecs uint32) {
 	signer, err := ssh.NewSignerFromKey(key)
 	if err != nil {
 		t.Fatalf("NewSignerFromKey(%T): %v", key, err)
@@ -159,6 +169,25 @@ func testAgentInterface(t *testing.T, agent Agent, key interface{}, cert *ssh.Ce
 		t.Fatalf("Verify(%s): %v", pubKey.Type(), err)
 	}
 
+	// For tests on RSA keys, try signing with SHA-256 and SHA-512 flags
+	if pubKey.Type() == "ssh-rsa" {
+		sshFlagTest := func(flag SignatureFlags, expectedSigFormat string) {
+			sig, err = agent.SignWithFlags(pubKey, data, flag)
+			if err != nil {
+				t.Fatalf("SignWithFlags(%s): %v", pubKey.Type(), err)
+			}
+			if sig.Format != expectedSigFormat {
+				t.Fatalf("Signature format didn't match expected value: %s != %s", sig.Format, expectedSigFormat)
+			}
+			if err := pubKey.Verify(data, sig); err != nil {
+				t.Fatalf("Verify(%s): %v", pubKey.Type(), err)
+			}
+		}
+		sshFlagTest(0, ssh.SigAlgoRSA)
+		sshFlagTest(SignatureFlagRsaSha256, ssh.SigAlgoRSASHA2256)
+		sshFlagTest(SignatureFlagRsaSha512, ssh.SigAlgoRSASHA2512)
+	}
+
 	// If the key has a lifetime, is it removed when it should be?
 	if lifetimeSecs > 0 {
 		time.Sleep(time.Second*time.Duration(lifetimeSecs) + 100*time.Millisecond)
@@ -173,6 +202,63 @@ func testAgentInterface(t *testing.T, agent Agent, key interface{}, cert *ssh.Ce
 
 }
 
+func TestMalformedRequests(t *testing.T) {
+	keyringAgent := NewKeyring()
+	listener, err := netListener()
+	if err != nil {
+		t.Fatalf("netListener: %v", err)
+	}
+	defer listener.Close()
+
+	testCase := func(t *testing.T, requestBytes []byte, wantServerErr bool) {
+		var wg sync.WaitGroup
+		wg.Add(1)
+		go func() {
+			defer wg.Done()
+			c, err := listener.Accept()
+			if err != nil {
+				t.Errorf("listener.Accept: %v", err)
+				return
+			}
+			defer c.Close()
+
+			err = ServeAgent(keyringAgent, c)
+			if err == nil {
+				t.Error("ServeAgent should have returned an error to malformed input")
+			} else {
+				if (err != io.EOF) != wantServerErr {
+					t.Errorf("ServeAgent returned expected error: %v", err)
+				}
+			}
+		}()
+
+		c, err := net.Dial("tcp", listener.Addr().String())
+		if err != nil {
+			t.Fatalf("net.Dial: %v", err)
+		}
+		_, err = c.Write(requestBytes)
+		if err != nil {
+			t.Errorf("Unexpected error writing raw bytes on connection: %v", err)
+		}
+		c.Close()
+		wg.Wait()
+	}
+
+	var testCases = []struct {
+		name          string
+		requestBytes  []byte
+		wantServerErr bool
+	}{
+		{"Empty request", []byte{}, false},
+		{"Short header", []byte{0x00}, true},
+		{"Empty body", []byte{0x00, 0x00, 0x00, 0x00}, true},
+		{"Short body", []byte{0x00, 0x00, 0x00, 0x01}, false},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) { testCase(t, tc.requestBytes, tc.wantServerErr) })
+	}
+}
+
 func TestAgent(t *testing.T) {
 	for _, keyType := range []string{"rsa", "dsa", "ecdsa", "ed25519"} {
 		testOpenSSHAgent(t, testPrivateKeys[keyType], nil, 0)
@@ -192,17 +278,26 @@ func TestCert(t *testing.T) {
 	testKeyringAgent(t, testPrivateKeys["rsa"], cert, 0)
 }
 
-// netPipe is analogous to net.Pipe, but it uses a real net.Conn, and
-// therefore is buffered (net.Pipe deadlocks if both sides start with
-// a write.)
-func netPipe() (net.Conn, net.Conn, error) {
+// netListener creates a localhost network listener.
+func netListener() (net.Listener, error) {
 	listener, err := net.Listen("tcp", "127.0.0.1:0")
 	if err != nil {
 		listener, err = net.Listen("tcp", "[::1]:0")
 		if err != nil {
-			return nil, nil, err
+			return nil, err
 		}
 	}
+	return listener, nil
+}
+
+// netPipe is analogous to net.Pipe, but it uses a real net.Conn, and
+// therefore is buffered (net.Pipe deadlocks if both sides start with
+// a write.)
+func netPipe() (net.Conn, net.Conn, error) {
+	listener, err := netListener()
+	if err != nil {
+		return nil, nil, err
+	}
 	defer listener.Close()
 	c1, err := net.Dial("tcp", listener.Addr().String())
 	if err != nil {
@@ -218,6 +313,49 @@ func netPipe() (net.Conn, net.Conn, error) {
 	return c1, c2, nil
 }
 
+func TestServerResponseTooLarge(t *testing.T) {
+	a, b, err := netPipe()
+	if err != nil {
+		t.Fatalf("netPipe: %v", err)
+	}
+	done := make(chan struct{})
+	defer func() { <-done }()
+
+	defer a.Close()
+	defer b.Close()
+
+	var response identitiesAnswerAgentMsg
+	response.NumKeys = 1
+	response.Keys = make([]byte, maxAgentResponseBytes+1)
+
+	agent := NewClient(a)
+	go func() {
+		defer close(done)
+		n, err := b.Write(ssh.Marshal(response))
+		if n < 4 {
+			if runtime.GOOS == "plan9" {
+				if e1, ok := err.(*net.OpError); ok {
+					if e2, ok := e1.Err.(*os.PathError); ok {
+						switch e2.Err.Error() {
+						case "Hangup", "i/o on hungup channel":
+							// syscall.Pwrite returns -1 in this case even when some data did get written.
+							return
+						}
+					}
+				}
+			}
+			t.Errorf("At least 4 bytes (the response size) should have been successfully written: %d < 4: %v", n, err)
+		}
+	}()
+	_, err = agent.List()
+	if err == nil {
+		t.Fatal("Did not get error result")
+	}
+	if err.Error() != "agent: client error: response too large" {
+		t.Fatal("Did not get expected error result")
+	}
+}
+
 func TestAuth(t *testing.T) {
 	agent, _, cleanup := startOpenSSHAgent(t)
 	defer cleanup()
@@ -377,3 +515,38 @@ func testAgentLifetime(t *testing.T, agent Agent) {
 		t.Errorf("Want 0 keys, got %v", len(keys))
 	}
 }
+
+type keyringExtended struct {
+	*keyring
+}
+
+func (r *keyringExtended) Extension(extensionType string, contents []byte) ([]byte, error) {
+	if extensionType != "my-extension@example.com" {
+		return []byte{agentExtensionFailure}, nil
+	}
+	return append([]byte{agentSuccess}, contents...), nil
+}
+
+func TestAgentExtensions(t *testing.T) {
+	agent, _, cleanup := startOpenSSHAgent(t)
+	defer cleanup()
+	_, err := agent.Extension("my-extension@example.com", []byte{0x00, 0x01, 0x02})
+	if err == nil {
+		t.Fatal("should have gotten agent extension failure")
+	}
+
+	agent, cleanup = startAgent(t, &keyringExtended{})
+	defer cleanup()
+	result, err := agent.Extension("my-extension@example.com", []byte{0x00, 0x01, 0x02})
+	if err != nil {
+		t.Fatalf("agent extension failure: %v", err)
+	}
+	if len(result) != 4 || !bytes.Equal(result, []byte{agentSuccess, 0x00, 0x01, 0x02}) {
+		t.Fatalf("agent extension result invalid: %v", result)
+	}
+
+	_, err = agent.Extension("bad-extension@example.com", []byte{0x00, 0x01, 0x02})
+	if err == nil {
+		t.Fatal("should have gotten agent extension failure")
+	}
+}

+ 9 - 9
psiphon/common/crypto/ssh/agent/example_test.go

@@ -13,20 +13,20 @@ import (
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/ssh/agent"
 )
 
-func ExampleClientAgent() {
-	// ssh-agent has a UNIX socket under $SSH_AUTH_SOCK
+func ExampleNewClient() {
+	// ssh-agent(1) provides a UNIX socket at $SSH_AUTH_SOCK.
 	socket := os.Getenv("SSH_AUTH_SOCK")
 	conn, err := net.Dial("unix", socket)
 	if err != nil {
-		log.Fatalf("net.Dial: %v", err)
+		log.Fatalf("Failed to open SSH_AUTH_SOCK: %v", err)
 	}
+
 	agentClient := agent.NewClient(conn)
 	config := &ssh.ClientConfig{
-		User: "username",
+		User: "gopher",
 		Auth: []ssh.AuthMethod{
-			// Use a callback rather than PublicKeys
-			// so we only consult the agent once the remote server
-			// wants it.
+			// Use a callback rather than PublicKeys so we only consult the
+			// agent once the remote server wants it.
 			ssh.PublicKeysCallback(agentClient.Signers),
 		},
 		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
@@ -34,8 +34,8 @@ func ExampleClientAgent() {
 
 	sshc, err := ssh.Dial("tcp", "localhost:22", config)
 	if err != nil {
-		log.Fatalf("Dial: %v", err)
+		log.Fatal(err)
 	}
-	// .. use sshc
+	// Use sshc...
 	sshc.Close()
 }

+ 27 - 1
psiphon/common/crypto/ssh/agent/keyring.go

@@ -182,6 +182,10 @@ func (r *keyring) Add(key AddedKey) error {
 
 // Sign returns a signature for the data.
 func (r *keyring) Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error) {
+	return r.SignWithFlags(key, data, 0)
+}
+
+func (r *keyring) SignWithFlags(key ssh.PublicKey, data []byte, flags SignatureFlags) (*ssh.Signature, error) {
 	r.mu.Lock()
 	defer r.mu.Unlock()
 	if r.locked {
@@ -192,7 +196,24 @@ func (r *keyring) Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error) {
 	wanted := key.Marshal()
 	for _, k := range r.keys {
 		if bytes.Equal(k.signer.PublicKey().Marshal(), wanted) {
-			return k.signer.Sign(rand.Reader, data)
+			if flags == 0 {
+				return k.signer.Sign(rand.Reader, data)
+			} else {
+				if algorithmSigner, ok := k.signer.(ssh.AlgorithmSigner); !ok {
+					return nil, fmt.Errorf("agent: signature does not support non-default signature algorithm: %T", k.signer)
+				} else {
+					var algorithm string
+					switch flags {
+					case SignatureFlagRsaSha256:
+						algorithm = ssh.SigAlgoRSASHA2256
+					case SignatureFlagRsaSha512:
+						algorithm = ssh.SigAlgoRSASHA2512
+					default:
+						return nil, fmt.Errorf("agent: unsupported signature flags: %d", flags)
+					}
+					return algorithmSigner.SignWithAlgorithm(rand.Reader, data, algorithm)
+				}
+			}
 		}
 	}
 	return nil, errors.New("not found")
@@ -213,3 +234,8 @@ func (r *keyring) Signers() ([]ssh.Signer, error) {
 	}
 	return s, nil
 }
+
+// The keyring does not support any extensions
+func (r *keyring) Extension(extensionType string, contents []byte) ([]byte, error) {
+	return nil, ErrExtensionUnsupported
+}

+ 48 - 1
psiphon/common/crypto/ssh/agent/server.go

@@ -128,7 +128,14 @@ func (s *server) processRequest(data []byte) (interface{}, error) {
 			Blob:   req.KeyBlob,
 		}
 
-		sig, err := s.agent.Sign(k, req.Data) //  TODO(hanwen): flags.
+		var sig *ssh.Signature
+		var err error
+		if extendedAgent, ok := s.agent.(ExtendedAgent); ok {
+			sig, err = extendedAgent.SignWithFlags(k, req.Data, SignatureFlags(req.Flags))
+		} else {
+			sig, err = s.agent.Sign(k, req.Data)
+		}
+
 		if err != nil {
 			return nil, err
 		}
@@ -150,6 +157,43 @@ func (s *server) processRequest(data []byte) (interface{}, error) {
 
 	case agentAddIDConstrained, agentAddIdentity:
 		return nil, s.insertIdentity(data)
+
+	case agentExtension:
+		// Return a stub object where the whole contents of the response gets marshaled.
+		var responseStub struct {
+			Rest []byte `ssh:"rest"`
+		}
+
+		if extendedAgent, ok := s.agent.(ExtendedAgent); !ok {
+			// If this agent doesn't implement extensions, [PROTOCOL.agent] section 4.7
+			// requires that we return a standard SSH_AGENT_FAILURE message.
+			responseStub.Rest = []byte{agentFailure}
+		} else {
+			var req extensionAgentMsg
+			if err := ssh.Unmarshal(data, &req); err != nil {
+				return nil, err
+			}
+			res, err := extendedAgent.Extension(req.ExtensionType, req.Contents)
+			if err != nil {
+				// If agent extensions are unsupported, return a standard SSH_AGENT_FAILURE
+				// message as required by [PROTOCOL.agent] section 4.7.
+				if err == ErrExtensionUnsupported {
+					responseStub.Rest = []byte{agentFailure}
+				} else {
+					// As the result of any other error processing an extension request,
+					// [PROTOCOL.agent] section 4.7 requires that we return a
+					// SSH_AGENT_EXTENSION_FAILURE code.
+					responseStub.Rest = []byte{agentExtensionFailure}
+				}
+			} else {
+				if len(res) == 0 {
+					return nil, nil
+				}
+				responseStub.Rest = res
+			}
+		}
+
+		return responseStub, nil
 	}
 
 	return nil, fmt.Errorf("unknown opcode %d", data[0])
@@ -497,6 +541,9 @@ func ServeAgent(agent Agent, c io.ReadWriter) error {
 			return err
 		}
 		l := binary.BigEndian.Uint32(length[:])
+		if l == 0 {
+			return fmt.Errorf("agent: request size is 0")
+		}
 		if l > maxAgentResponseBytes {
 			// We also cap requests.
 			return fmt.Errorf("agent: request too large: %d", l)

+ 3 - 0
psiphon/common/crypto/ssh/benchmark_test.go

@@ -93,6 +93,9 @@ func BenchmarkEndToEnd(b *testing.B) {
 			b.Fatalf("Client: %v", err)
 		}
 		ch, incoming, err := newCh.Accept()
+		if err != nil {
+			b.Fatalf("Accept: %v", err)
+		}
 		go DiscardRequests(incoming)
 		for i := 0; i < b.N; i++ {
 			if _, err := io.ReadFull(ch, output); err != nil {

+ 40 - 15
psiphon/common/crypto/ssh/certs.go

@@ -17,12 +17,14 @@ import (
 // These constants from [PROTOCOL.certkeys] represent the algorithm names
 // for certificate types supported by this package.
 const (
-	CertAlgoRSAv01      = "ssh-rsa-cert-v01@openssh.com"
-	CertAlgoDSAv01      = "ssh-dss-cert-v01@openssh.com"
-	CertAlgoECDSA256v01 = "ecdsa-sha2-nistp256-cert-v01@openssh.com"
-	CertAlgoECDSA384v01 = "ecdsa-sha2-nistp384-cert-v01@openssh.com"
-	CertAlgoECDSA521v01 = "ecdsa-sha2-nistp521-cert-v01@openssh.com"
-	CertAlgoED25519v01  = "ssh-ed25519-cert-v01@openssh.com"
+	CertAlgoRSAv01        = "ssh-rsa-cert-v01@openssh.com"
+	CertAlgoDSAv01        = "ssh-dss-cert-v01@openssh.com"
+	CertAlgoECDSA256v01   = "ecdsa-sha2-nistp256-cert-v01@openssh.com"
+	CertAlgoECDSA384v01   = "ecdsa-sha2-nistp384-cert-v01@openssh.com"
+	CertAlgoECDSA521v01   = "ecdsa-sha2-nistp521-cert-v01@openssh.com"
+	CertAlgoSKECDSA256v01 = "sk-ecdsa-sha2-nistp256-cert-v01@openssh.com"
+	CertAlgoED25519v01    = "ssh-ed25519-cert-v01@openssh.com"
+	CertAlgoSKED25519v01  = "sk-ssh-ed25519-cert-v01@openssh.com"
 )
 
 // Certificate types distinguish between host and user
@@ -37,6 +39,7 @@ const (
 type Signature struct {
 	Format string
 	Blob   []byte
+	Rest   []byte `ssh:"rest"`
 }
 
 // CertTimeInfinity can be used for OpenSSHCertV01.ValidBefore to indicate that
@@ -222,6 +225,11 @@ type openSSHCertSigner struct {
 	signer Signer
 }
 
+type algorithmOpenSSHCertSigner struct {
+	*openSSHCertSigner
+	algorithmSigner AlgorithmSigner
+}
+
 // NewCertSigner returns a Signer that signs with the given Certificate, whose
 // private key is held by signer. It returns an error if the public key in cert
 // doesn't match the key used by signer.
@@ -230,7 +238,12 @@ func NewCertSigner(cert *Certificate, signer Signer) (Signer, error) {
 		return nil, errors.New("ssh: signer and cert have different public key")
 	}
 
-	return &openSSHCertSigner{cert, signer}, nil
+	if algorithmSigner, ok := signer.(AlgorithmSigner); ok {
+		return &algorithmOpenSSHCertSigner{
+			&openSSHCertSigner{cert, signer}, algorithmSigner}, nil
+	} else {
+		return &openSSHCertSigner{cert, signer}, nil
+	}
 }
 
 func (s *openSSHCertSigner) Sign(rand io.Reader, data []byte) (*Signature, error) {
@@ -241,6 +254,10 @@ func (s *openSSHCertSigner) PublicKey() PublicKey {
 	return s.pub
 }
 
+func (s *algorithmOpenSSHCertSigner) SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*Signature, error) {
+	return s.algorithmSigner.SignWithAlgorithm(rand, data, algorithm)
+}
+
 const sourceAddressCriticalOption = "source-address"
 
 // CertChecker does the work of verifying a certificate. Its methods
@@ -397,8 +414,8 @@ func (c *CertChecker) CheckCert(principal string, cert *Certificate) error {
 	return nil
 }
 
-// SignCert sets c.SignatureKey to the authority's public key and stores a
-// Signature, by authority, in the certificate.
+// SignCert signs the certificate with an authority, setting the Nonce,
+// SignatureKey, and Signature fields.
 func (c *Certificate) SignCert(rand io.Reader, authority Signer) error {
 	c.Nonce = make([]byte, 32)
 	if _, err := io.ReadFull(rand, c.Nonce); err != nil {
@@ -415,12 +432,14 @@ func (c *Certificate) SignCert(rand io.Reader, authority Signer) error {
 }
 
 var certAlgoNames = map[string]string{
-	KeyAlgoRSA:      CertAlgoRSAv01,
-	KeyAlgoDSA:      CertAlgoDSAv01,
-	KeyAlgoECDSA256: CertAlgoECDSA256v01,
-	KeyAlgoECDSA384: CertAlgoECDSA384v01,
-	KeyAlgoECDSA521: CertAlgoECDSA521v01,
-	KeyAlgoED25519:  CertAlgoED25519v01,
+	KeyAlgoRSA:        CertAlgoRSAv01,
+	KeyAlgoDSA:        CertAlgoDSAv01,
+	KeyAlgoECDSA256:   CertAlgoECDSA256v01,
+	KeyAlgoECDSA384:   CertAlgoECDSA384v01,
+	KeyAlgoECDSA521:   CertAlgoECDSA521v01,
+	KeyAlgoSKECDSA256: CertAlgoSKECDSA256v01,
+	KeyAlgoED25519:    CertAlgoED25519v01,
+	KeyAlgoSKED25519:  CertAlgoSKED25519v01,
 }
 
 // certToPrivAlgo returns the underlying algorithm for a certificate algorithm.
@@ -504,6 +523,12 @@ func parseSignatureBody(in []byte) (out *Signature, rest []byte, ok bool) {
 		return
 	}
 
+	switch out.Format {
+	case KeyAlgoSKECDSA256, CertAlgoSKECDSA256v01, KeyAlgoSKED25519, CertAlgoSKED25519v01:
+		out.Rest = in
+		return out, nil, ok
+	}
+
 	return out, in, ok
 }
 

+ 14 - 14
psiphon/common/crypto/ssh/cipher.go

@@ -17,8 +17,8 @@ import (
 	"io"
 	"io/ioutil"
 
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/internal/poly1305"
 	"golang.org/x/crypto/chacha20"
-	"golang.org/x/crypto/poly1305"
 )
 
 const (
@@ -119,7 +119,7 @@ var cipherModes = map[string]*cipherMode{
 	chacha20Poly1305ID: {64, 0, newChaCha20Cipher},
 
 	// CBC mode is insecure and so is not included in the default config.
-	// (See http://www.isg.rhul.ac.uk/~kp/SandPfinal.pdf). If absolutely
+	// (See https://www.ieee-security.org/TC/SP2013/papers/4977a526.pdf). If absolutely
 	// needed, it's possible to specify a custom Config to enable it.
 	// You should expect that an active attacker can recover plaintext if
 	// you do.
@@ -148,8 +148,8 @@ type streamPacketCipher struct {
 	macResult   []byte
 }
 
-// readPacket reads and decrypt a single packet from the reader argument.
-func (s *streamPacketCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) {
+// readCipherPacket reads and decrypt a single packet from the reader argument.
+func (s *streamPacketCipher) readCipherPacket(seqNum uint32, r io.Reader) ([]byte, error) {
 	if _, err := io.ReadFull(r, s.prefix[:]); err != nil {
 		return nil, err
 	}
@@ -220,8 +220,8 @@ func (s *streamPacketCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, err
 	return s.packetData[:length-paddingLength-1], nil
 }
 
-// writePacket encrypts and sends a packet of data to the writer argument
-func (s *streamPacketCipher) writePacket(seqNum uint32, w io.Writer, rand io.Reader, packet []byte) error {
+// writeCipherPacket encrypts and sends a packet of data to the writer argument
+func (s *streamPacketCipher) writeCipherPacket(seqNum uint32, w io.Writer, rand io.Reader, packet []byte) error {
 	if len(packet) > maxPacket {
 		return errors.New("ssh: packet too large")
 	}
@@ -326,7 +326,7 @@ func newGCMCipher(key, iv, unusedMacKey []byte, unusedAlgs directionAlgorithms)
 
 const gcmTagSize = 16
 
-func (c *gcmCipher) writePacket(seqNum uint32, w io.Writer, rand io.Reader, packet []byte) error {
+func (c *gcmCipher) writeCipherPacket(seqNum uint32, w io.Writer, rand io.Reader, packet []byte) error {
 	// Pad out to multiple of 16 bytes. This is different from the
 	// stream cipher because that encrypts the length too.
 	padding := byte(packetSizeMultiple - (1+len(packet))%packetSizeMultiple)
@@ -369,7 +369,7 @@ func (c *gcmCipher) incIV() {
 	}
 }
 
-func (c *gcmCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) {
+func (c *gcmCipher) readCipherPacket(seqNum uint32, r io.Reader) ([]byte, error) {
 	if _, err := io.ReadFull(r, c.prefix[:]); err != nil {
 		return nil, err
 	}
@@ -485,8 +485,8 @@ type cbcError string
 
 func (e cbcError) Error() string { return string(e) }
 
-func (c *cbcCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) {
-	p, err := c.readPacketLeaky(seqNum, r)
+func (c *cbcCipher) readCipherPacket(seqNum uint32, r io.Reader) ([]byte, error) {
+	p, err := c.readCipherPacketLeaky(seqNum, r)
 	if err != nil {
 		if _, ok := err.(cbcError); ok {
 			// Verification error: read a fixed amount of
@@ -499,7 +499,7 @@ func (c *cbcCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) {
 	return p, err
 }
 
-func (c *cbcCipher) readPacketLeaky(seqNum uint32, r io.Reader) ([]byte, error) {
+func (c *cbcCipher) readCipherPacketLeaky(seqNum uint32, r io.Reader) ([]byte, error) {
 	blockSize := c.decrypter.BlockSize()
 
 	// Read the header, which will include some of the subsequent data in the
@@ -575,7 +575,7 @@ func (c *cbcCipher) readPacketLeaky(seqNum uint32, r io.Reader) ([]byte, error)
 	return c.packetData[prefixLen:paddingStart], nil
 }
 
-func (c *cbcCipher) writePacket(seqNum uint32, w io.Writer, rand io.Reader, packet []byte) error {
+func (c *cbcCipher) writeCipherPacket(seqNum uint32, w io.Writer, rand io.Reader, packet []byte) error {
 	effectiveBlockSize := maxUInt32(cbcMinPacketSizeMultiple, c.encrypter.BlockSize())
 
 	// Length of encrypted portion of the packet (header, payload, padding).
@@ -660,7 +660,7 @@ func newChaCha20Cipher(key, unusedIV, unusedMACKey []byte, unusedAlgs directionA
 	return c, nil
 }
 
-func (c *chacha20Poly1305Cipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) {
+func (c *chacha20Poly1305Cipher) readCipherPacket(seqNum uint32, r io.Reader) ([]byte, error) {
 	nonce := make([]byte, 12)
 	binary.BigEndian.PutUint32(nonce[8:], seqNum)
 	s, err := chacha20.NewUnauthenticatedCipher(c.contentKey[:], nonce)
@@ -726,7 +726,7 @@ func (c *chacha20Poly1305Cipher) readPacket(seqNum uint32, r io.Reader) ([]byte,
 	return plain, nil
 }
 
-func (c *chacha20Poly1305Cipher) writePacket(seqNum uint32, w io.Writer, rand io.Reader, payload []byte) error {
+func (c *chacha20Poly1305Cipher) writeCipherPacket(seqNum uint32, w io.Writer, rand io.Reader, payload []byte) error {
 	nonce := make([]byte, 12)
 	binary.BigEndian.PutUint32(nonce[8:], seqNum)
 	s, err := chacha20.NewUnauthenticatedCipher(c.contentKey[:], nonce)

+ 8 - 8
psiphon/common/crypto/ssh/cipher_test.go

@@ -56,13 +56,13 @@ func testPacketCipher(t *testing.T, cipher, mac string) {
 	want := "bla bla"
 	input := []byte(want)
 	buf := &bytes.Buffer{}
-	if err := client.writePacket(0, buf, rand.Reader, input); err != nil {
-		t.Fatalf("writePacket(%q, %q): %v", cipher, mac, err)
+	if err := client.writeCipherPacket(0, buf, rand.Reader, input); err != nil {
+		t.Fatalf("writeCipherPacket(%q, %q): %v", cipher, mac, err)
 	}
 
-	packet, err := server.readPacket(0, buf)
+	packet, err := server.readCipherPacket(0, buf)
 	if err != nil {
-		t.Fatalf("readPacket(%q, %q): %v", cipher, mac, err)
+		t.Fatalf("readCipherPacket(%q, %q): %v", cipher, mac, err)
 	}
 
 	if string(packet) != want {
@@ -85,8 +85,8 @@ func TestCBCOracleCounterMeasure(t *testing.T) {
 	want := "bla bla"
 	input := []byte(want)
 	buf := &bytes.Buffer{}
-	if err := client.writePacket(0, buf, rand.Reader, input); err != nil {
-		t.Errorf("writePacket: %v", err)
+	if err := client.writeCipherPacket(0, buf, rand.Reader, input); err != nil {
+		t.Errorf("writeCipherPacket: %v", err)
 	}
 
 	packetSize := buf.Len()
@@ -106,9 +106,9 @@ func TestCBCOracleCounterMeasure(t *testing.T) {
 		fresh.Bytes()[i] ^= 0x01
 
 		before := fresh.Len()
-		_, err = server.readPacket(0, fresh)
+		_, err = server.readCipherPacket(0, fresh)
 		if err == nil {
-			t.Errorf("corrupt byte %d: readPacket succeeded ", i)
+			t.Errorf("corrupt byte %d: readCipherPacket succeeded ", i)
 			continue
 		}
 		if _, ok := err.(cbcError); !ok {

+ 2 - 2
psiphon/common/crypto/ssh/client.go

@@ -77,7 +77,7 @@ func NewClientConn(c net.Conn, addr string, config *ClientConfig) (Conn, <-chan
 	}
 
 	conn := &connection{
-		sshConn: sshConn{conn: c},
+		sshConn: sshConn{conn: c, user: fullConf.User},
 	}
 
 	if err := conn.clientHandshake(addr, &fullConf); err != nil {
@@ -185,7 +185,7 @@ func Dial(network, addr string, config *ClientConfig) (*Client, error) {
 // keys.  A HostKeyCallback must return nil if the host key is OK, or
 // an error to reject it. It receives the hostname as passed to Dial
 // or NewClientConn. The remote address is the RemoteAddr of the
-// net.Conn underlying the the SSH connection.
+// net.Conn underlying the SSH connection.
 type HostKeyCallback func(hostname string, remote net.Addr, key PublicKey) error
 
 // BannerCallback is the function type used for treat the banner sent by

+ 127 - 11
psiphon/common/crypto/ssh/client_auth.go

@@ -36,7 +36,7 @@ func (c *connection) clientAuthenticate(config *ClientConfig) error {
 
 	// during the authentication phase the client first attempts the "none" method
 	// then any untried methods suggested by the server.
-	tried := make(map[string]bool)
+	var tried []string
 	var lastMethods []string
 
 	sessionID := c.transport.getSessionID()
@@ -49,7 +49,9 @@ func (c *connection) clientAuthenticate(config *ClientConfig) error {
 			// success
 			return nil
 		} else if ok == authFailure {
-			tried[auth.method()] = true
+			if m := auth.method(); !contains(tried, m) {
+				tried = append(tried, m)
+			}
 		}
 		if methods == nil {
 			methods = lastMethods
@@ -61,7 +63,7 @@ func (c *connection) clientAuthenticate(config *ClientConfig) error {
 	findNext:
 		for _, a := range config.Auth {
 			candidateMethod := a.method()
-			if tried[candidateMethod] {
+			if contains(tried, candidateMethod) {
 				continue
 			}
 			for _, meth := range methods {
@@ -72,16 +74,16 @@ func (c *connection) clientAuthenticate(config *ClientConfig) error {
 			}
 		}
 	}
-	return fmt.Errorf("ssh: unable to authenticate, attempted methods %v, no supported methods remain", keys(tried))
+	return fmt.Errorf("ssh: unable to authenticate, attempted methods %v, no supported methods remain", tried)
 }
 
-func keys(m map[string]bool) []string {
-	s := make([]string, 0, len(m))
-
-	for key := range m {
-		s = append(s, key)
+func contains(list []string, e string) bool {
+	for _, s := range list {
+		if s == e {
+			return true
+		}
 	}
-	return s
+	return false
 }
 
 // An AuthMethod represents an instance of an RFC 4252 authentication method.
@@ -469,7 +471,7 @@ func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packe
 		}
 
 		if len(answers) != len(prompts) {
-			return authFailure, nil, errors.New("ssh: not enough answers from keyboard-interactive callback")
+			return authFailure, nil, fmt.Errorf("ssh: incorrect number of answers from keyboard-interactive callback %d (expected %d)", len(answers), len(prompts))
 		}
 		responseLength := 1 + 4
 		for _, a := range answers {
@@ -523,3 +525,117 @@ func (r *retryableAuthMethod) method() string {
 func RetryableAuthMethod(auth AuthMethod, maxTries int) AuthMethod {
 	return &retryableAuthMethod{authMethod: auth, maxTries: maxTries}
 }
+
+// GSSAPIWithMICAuthMethod is an AuthMethod with "gssapi-with-mic" authentication.
+// See RFC 4462 section 3
+// gssAPIClient is implementation of the GSSAPIClient interface, see the definition of the interface for details.
+// target is the server host you want to log in to.
+func GSSAPIWithMICAuthMethod(gssAPIClient GSSAPIClient, target string) AuthMethod {
+	if gssAPIClient == nil {
+		panic("gss-api client must be not nil with enable gssapi-with-mic")
+	}
+	return &gssAPIWithMICCallback{gssAPIClient: gssAPIClient, target: target}
+}
+
+type gssAPIWithMICCallback struct {
+	gssAPIClient GSSAPIClient
+	target       string
+}
+
+func (g *gssAPIWithMICCallback) auth(session []byte, user string, c packetConn, rand io.Reader) (authResult, []string, error) {
+	m := &userAuthRequestMsg{
+		User:    user,
+		Service: serviceSSH,
+		Method:  g.method(),
+	}
+	// The GSS-API authentication method is initiated when the client sends an SSH_MSG_USERAUTH_REQUEST.
+	// See RFC 4462 section 3.2.
+	m.Payload = appendU32(m.Payload, 1)
+	m.Payload = appendString(m.Payload, string(krb5OID))
+	if err := c.writePacket(Marshal(m)); err != nil {
+		return authFailure, nil, err
+	}
+	// The server responds to the SSH_MSG_USERAUTH_REQUEST with either an
+	// SSH_MSG_USERAUTH_FAILURE if none of the mechanisms are supported or
+	// with an SSH_MSG_USERAUTH_GSSAPI_RESPONSE.
+	// See RFC 4462 section 3.3.
+	// OpenSSH supports Kerberos V5 mechanism only for GSS-API authentication,so I don't want to check
+	// selected mech if it is valid.
+	packet, err := c.readPacket()
+	if err != nil {
+		return authFailure, nil, err
+	}
+	userAuthGSSAPIResp := &userAuthGSSAPIResponse{}
+	if err := Unmarshal(packet, userAuthGSSAPIResp); err != nil {
+		return authFailure, nil, err
+	}
+	// Start the loop into the exchange token.
+	// See RFC 4462 section 3.4.
+	var token []byte
+	defer g.gssAPIClient.DeleteSecContext()
+	for {
+		// Initiates the establishment of a security context between the application and a remote peer.
+		nextToken, needContinue, err := g.gssAPIClient.InitSecContext("host@"+g.target, token, false)
+		if err != nil {
+			return authFailure, nil, err
+		}
+		if len(nextToken) > 0 {
+			if err := c.writePacket(Marshal(&userAuthGSSAPIToken{
+				Token: nextToken,
+			})); err != nil {
+				return authFailure, nil, err
+			}
+		}
+		if !needContinue {
+			break
+		}
+		packet, err = c.readPacket()
+		if err != nil {
+			return authFailure, nil, err
+		}
+		switch packet[0] {
+		case msgUserAuthFailure:
+			var msg userAuthFailureMsg
+			if err := Unmarshal(packet, &msg); err != nil {
+				return authFailure, nil, err
+			}
+			if msg.PartialSuccess {
+				return authPartialSuccess, msg.Methods, nil
+			}
+			return authFailure, msg.Methods, nil
+		case msgUserAuthGSSAPIError:
+			userAuthGSSAPIErrorResp := &userAuthGSSAPIError{}
+			if err := Unmarshal(packet, userAuthGSSAPIErrorResp); err != nil {
+				return authFailure, nil, err
+			}
+			return authFailure, nil, fmt.Errorf("GSS-API Error:\n"+
+				"Major Status: %d\n"+
+				"Minor Status: %d\n"+
+				"Error Message: %s\n", userAuthGSSAPIErrorResp.MajorStatus, userAuthGSSAPIErrorResp.MinorStatus,
+				userAuthGSSAPIErrorResp.Message)
+		case msgUserAuthGSSAPIToken:
+			userAuthGSSAPITokenReq := &userAuthGSSAPIToken{}
+			if err := Unmarshal(packet, userAuthGSSAPITokenReq); err != nil {
+				return authFailure, nil, err
+			}
+			token = userAuthGSSAPITokenReq.Token
+		}
+	}
+	// Binding Encryption Keys.
+	// See RFC 4462 section 3.5.
+	micField := buildMIC(string(session), user, "ssh-connection", "gssapi-with-mic")
+	micToken, err := g.gssAPIClient.GetMIC(micField)
+	if err != nil {
+		return authFailure, nil, err
+	}
+	if err := c.writePacket(Marshal(&userAuthGSSAPIMIC{
+		MIC: micToken,
+	})); err != nil {
+		return authFailure, nil, err
+	}
+	return handleAuthResponse(c)
+}
+
+func (g *gssAPIWithMICCallback) method() string {
+	return "gssapi-with-mic"
+}

+ 277 - 7
psiphon/common/crypto/ssh/client_auth_test.go

@@ -9,6 +9,9 @@ import (
 	"crypto/rand"
 	"errors"
 	"fmt"
+	"io"
+	"log"
+	"net"
 	"os"
 	"strings"
 	"testing"
@@ -28,8 +31,21 @@ func (cr keyboardInteractive) Challenge(user string, instruction string, questio
 var clientPassword = "tiger"
 
 // tryAuth runs a handshake with a given config against an SSH server
-// with config serverConfig
+// with config serverConfig. Returns both client and server side errors.
 func tryAuth(t *testing.T, config *ClientConfig) error {
+	err, _ := tryAuthBothSides(t, config, nil)
+	return err
+}
+
+// tryAuth runs a handshake with a given config against an SSH server
+// with a given GSSAPIWithMICConfig and config serverConfig. Returns both client and server side errors.
+func tryAuthWithGSSAPIWithMICConfig(t *testing.T, clientConfig *ClientConfig, gssAPIWithMICConfig *GSSAPIWithMICConfig) error {
+	err, _ := tryAuthBothSides(t, clientConfig, gssAPIWithMICConfig)
+	return err
+}
+
+// tryAuthBothSides runs the handshake and returns the resulting errors from both sides of the connection.
+func tryAuthBothSides(t *testing.T, config *ClientConfig, gssAPIWithMICConfig *GSSAPIWithMICConfig) (clientError error, serverAuthErrors []error) {
 	c1, c2, err := netPipe()
 	if err != nil {
 		t.Fatalf("netPipe: %v", err)
@@ -52,7 +68,6 @@ func tryAuth(t *testing.T, config *ClientConfig) error {
 			return c.Serial == 666
 		},
 	}
-
 	serverConfig := &ServerConfig{
 		PasswordCallback: func(conn ConnMetadata, pass []byte) (*Permissions, error) {
 			if conn.User() == "testuser" && string(pass) == clientPassword {
@@ -76,12 +91,17 @@ func tryAuth(t *testing.T, config *ClientConfig) error {
 			}
 			return nil, errors.New("keyboard-interactive failed")
 		},
+		GSSAPIWithMICConfig: gssAPIWithMICConfig,
 	}
 	serverConfig.AddHostKey(testSigners["rsa"])
 
+	serverConfig.AuthLogCallback = func(conn ConnMetadata, method string, err error) {
+		serverAuthErrors = append(serverAuthErrors, err)
+	}
+
 	go newServer(c1, serverConfig)
 	_, _, _, err = NewClientConn(c2, "", config)
-	return err
+	return err, serverAuthErrors
 }
 
 func TestClientAuthPublicKey(t *testing.T) {
@@ -213,6 +233,45 @@ func TestAuthMethodRSAandDSA(t *testing.T) {
 	}
 }
 
+type invalidAlgSigner struct {
+	Signer
+}
+
+func (s *invalidAlgSigner) Sign(rand io.Reader, data []byte) (*Signature, error) {
+	sig, err := s.Signer.Sign(rand, data)
+	if sig != nil {
+		sig.Format = "invalid"
+	}
+	return sig, err
+}
+
+func TestMethodInvalidAlgorithm(t *testing.T) {
+	config := &ClientConfig{
+		User: "testuser",
+		Auth: []AuthMethod{
+			PublicKeys(&invalidAlgSigner{testSigners["rsa"]}),
+		},
+		HostKeyCallback: InsecureIgnoreHostKey(),
+	}
+
+	err, serverErrors := tryAuthBothSides(t, config, nil)
+	if err == nil {
+		t.Fatalf("login succeeded")
+	}
+
+	found := false
+	want := "algorithm \"invalid\""
+
+	var errStrings []string
+	for _, err := range serverErrors {
+		found = found || (err != nil && strings.Contains(err.Error(), want))
+		errStrings = append(errStrings, err.Error())
+	}
+	if !found {
+		t.Errorf("server got error %q, want substring %q", errStrings, want)
+	}
+}
+
 func TestClientHMAC(t *testing.T) {
 	for _, mac := range supportedMACs {
 		config := &ClientConfig{
@@ -257,7 +316,7 @@ func TestClientUnsupportedKex(t *testing.T) {
 			PublicKeys(),
 		},
 		Config: Config{
-			KeyExchanges: []string{"diffie-hellman-group-exchange-sha256"}, // not currently supported
+			KeyExchanges: []string{"non-existent-kex"},
 		},
 		HostKeyCallback: InsecureIgnoreHostKey(),
 	}
@@ -427,7 +486,7 @@ func TestRetryableAuth(t *testing.T) {
 	}
 }
 
-func ExampleRetryableAuthMethod(t *testing.T) {
+func ExampleRetryableAuthMethod() {
 	user := "testuser"
 	NumberOfPrompts := 3
 
@@ -445,9 +504,17 @@ func ExampleRetryableAuthMethod(t *testing.T) {
 		},
 	}
 
-	if err := tryAuth(t, config); err != nil {
-		t.Fatalf("unable to dial remote side: %s", err)
+	host := "mysshserver"
+	netConn, err := net.Dial("tcp", host)
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	sshConn, _, _, err := NewClientConn(netConn, host, config)
+	if err != nil {
+		log.Fatal(err)
 	}
+	_ = sshConn
 }
 
 // Test if username is received on server side when NoClientAuth is used
@@ -626,3 +693,206 @@ func TestClientAuthErrorList(t *testing.T) {
 		}
 	}
 }
+
+func TestAuthMethodGSSAPIWithMIC(t *testing.T) {
+	type testcase struct {
+		config        *ClientConfig
+		gssConfig     *GSSAPIWithMICConfig
+		clientWantErr string
+		serverWantErr string
+	}
+	testcases := []*testcase{
+		{
+			config: &ClientConfig{
+				User: "testuser",
+				Auth: []AuthMethod{
+					GSSAPIWithMICAuthMethod(
+						&FakeClient{
+							exchanges: []*exchange{
+								{
+									outToken: "client-valid-token-1",
+								},
+								{
+									expectedToken: "server-valid-token-1",
+								},
+							},
+							mic:      []byte("valid-mic"),
+							maxRound: 2,
+						}, "testtarget",
+					),
+				},
+				HostKeyCallback: InsecureIgnoreHostKey(),
+			},
+			gssConfig: &GSSAPIWithMICConfig{
+				AllowLogin: func(conn ConnMetadata, srcName string) (*Permissions, error) {
+					if srcName != conn.User()+"@DOMAIN" {
+						return nil, fmt.Errorf("srcName is %s, conn user is %s", srcName, conn.User())
+					}
+					return nil, nil
+				},
+				Server: &FakeServer{
+					exchanges: []*exchange{
+						{
+							outToken:      "server-valid-token-1",
+							expectedToken: "client-valid-token-1",
+						},
+					},
+					maxRound:    1,
+					expectedMIC: []byte("valid-mic"),
+					srcName:     "testuser@DOMAIN",
+				},
+			},
+		},
+		{
+			config: &ClientConfig{
+				User: "testuser",
+				Auth: []AuthMethod{
+					GSSAPIWithMICAuthMethod(
+						&FakeClient{
+							exchanges: []*exchange{
+								{
+									outToken: "client-valid-token-1",
+								},
+								{
+									expectedToken: "server-valid-token-1",
+								},
+							},
+							mic:      []byte("valid-mic"),
+							maxRound: 2,
+						}, "testtarget",
+					),
+				},
+				HostKeyCallback: InsecureIgnoreHostKey(),
+			},
+			gssConfig: &GSSAPIWithMICConfig{
+				AllowLogin: func(conn ConnMetadata, srcName string) (*Permissions, error) {
+					return nil, fmt.Errorf("user is not allowed to login")
+				},
+				Server: &FakeServer{
+					exchanges: []*exchange{
+						{
+							outToken:      "server-valid-token-1",
+							expectedToken: "client-valid-token-1",
+						},
+					},
+					maxRound:    1,
+					expectedMIC: []byte("valid-mic"),
+					srcName:     "testuser@DOMAIN",
+				},
+			},
+			serverWantErr: "user is not allowed to login",
+			clientWantErr: "ssh: handshake failed: ssh: unable to authenticate",
+		},
+		{
+			config: &ClientConfig{
+				User: "testuser",
+				Auth: []AuthMethod{
+					GSSAPIWithMICAuthMethod(
+						&FakeClient{
+							exchanges: []*exchange{
+								{
+									outToken: "client-valid-token-1",
+								},
+								{
+									expectedToken: "server-valid-token-1",
+								},
+							},
+							mic:      []byte("valid-mic"),
+							maxRound: 2,
+						}, "testtarget",
+					),
+				},
+				HostKeyCallback: InsecureIgnoreHostKey(),
+			},
+			gssConfig: &GSSAPIWithMICConfig{
+				AllowLogin: func(conn ConnMetadata, srcName string) (*Permissions, error) {
+					if srcName != conn.User() {
+						return nil, fmt.Errorf("srcName is %s, conn user is %s", srcName, conn.User())
+					}
+					return nil, nil
+				},
+				Server: &FakeServer{
+					exchanges: []*exchange{
+						{
+							outToken:      "server-invalid-token-1",
+							expectedToken: "client-valid-token-1",
+						},
+					},
+					maxRound:    1,
+					expectedMIC: []byte("valid-mic"),
+					srcName:     "testuser@DOMAIN",
+				},
+			},
+			clientWantErr: "ssh: handshake failed: got \"server-invalid-token-1\", want token \"server-valid-token-1\"",
+		},
+		{
+			config: &ClientConfig{
+				User: "testuser",
+				Auth: []AuthMethod{
+					GSSAPIWithMICAuthMethod(
+						&FakeClient{
+							exchanges: []*exchange{
+								{
+									outToken: "client-valid-token-1",
+								},
+								{
+									expectedToken: "server-valid-token-1",
+								},
+							},
+							mic:      []byte("invalid-mic"),
+							maxRound: 2,
+						}, "testtarget",
+					),
+				},
+				HostKeyCallback: InsecureIgnoreHostKey(),
+			},
+			gssConfig: &GSSAPIWithMICConfig{
+				AllowLogin: func(conn ConnMetadata, srcName string) (*Permissions, error) {
+					if srcName != conn.User() {
+						return nil, fmt.Errorf("srcName is %s, conn user is %s", srcName, conn.User())
+					}
+					return nil, nil
+				},
+				Server: &FakeServer{
+					exchanges: []*exchange{
+						{
+							outToken:      "server-valid-token-1",
+							expectedToken: "client-valid-token-1",
+						},
+					},
+					maxRound:    1,
+					expectedMIC: []byte("valid-mic"),
+					srcName:     "testuser@DOMAIN",
+				},
+			},
+			serverWantErr: "got MICToken \"invalid-mic\", want \"valid-mic\"",
+			clientWantErr: "ssh: handshake failed: ssh: unable to authenticate",
+		},
+	}
+
+	for i, c := range testcases {
+		clientErr, serverErrs := tryAuthBothSides(t, c.config, c.gssConfig)
+		if (c.clientWantErr == "") != (clientErr == nil) {
+			t.Fatalf("client got %v, want %s, case %d", clientErr, c.clientWantErr, i)
+		}
+		if (c.serverWantErr == "") != (len(serverErrs) == 2 && serverErrs[1] == nil || len(serverErrs) == 1) {
+			t.Fatalf("server got err %v, want %s", serverErrs, c.serverWantErr)
+		}
+		if c.clientWantErr != "" {
+			if clientErr != nil && !strings.Contains(clientErr.Error(), c.clientWantErr) {
+				t.Fatalf("client  got %v, want %s, case %d", clientErr, c.clientWantErr, i)
+			}
+		}
+		found := false
+		var errStrings []string
+		if c.serverWantErr != "" {
+			for _, err := range serverErrs {
+				found = found || (err != nil && strings.Contains(err.Error(), c.serverWantErr))
+				errStrings = append(errStrings, err.Error())
+			}
+			if !found {
+				t.Errorf("server got error %q, want substring %q, case %d", errStrings, c.serverWantErr, i)
+			}
+		}
+	}
+}

+ 49 - 0
psiphon/common/crypto/ssh/client_test.go

@@ -164,3 +164,52 @@ func TestBannerCallback(t *testing.T) {
 		t.Fatalf("got %s; want %s", receivedBanner, expected)
 	}
 }
+
+func TestNewClientConn(t *testing.T) {
+	for _, tt := range []struct {
+		name string
+		user string
+	}{
+		{
+			name: "good user field for ConnMetadata",
+			user: "testuser",
+		},
+		{
+			name: "empty user field for ConnMetadata",
+			user: "",
+		},
+	} {
+		t.Run(tt.name, func(t *testing.T) {
+			c1, c2, err := netPipe()
+			if err != nil {
+				t.Fatalf("netPipe: %v", err)
+			}
+			defer c1.Close()
+			defer c2.Close()
+
+			serverConf := &ServerConfig{
+				PasswordCallback: func(conn ConnMetadata, password []byte) (*Permissions, error) {
+					return &Permissions{}, nil
+				},
+			}
+			serverConf.AddHostKey(testSigners["rsa"])
+			go NewServerConn(c1, serverConf)
+
+			clientConf := &ClientConfig{
+				User: tt.user,
+				Auth: []AuthMethod{
+					Password("testpw"),
+				},
+				HostKeyCallback: InsecureIgnoreHostKey(),
+			}
+			clientConn, _, _, err := NewClientConn(c2, "", clientConf)
+			if err != nil {
+				t.Fatal(err)
+			}
+
+			if userGot := clientConn.User(); userGot != tt.user {
+				t.Errorf("got user %q; want user %q", userGot, tt.user)
+			}
+		})
+	}
+}

+ 32 - 8
psiphon/common/crypto/ssh/common.go

@@ -57,6 +57,24 @@ var supportedKexAlgos = []string{
 	kexAlgoDH14SHA256, kexAlgoDH14SHA1,
 }
 
+// serverForbiddenKexAlgos contains key exchange algorithms, that are forbidden
+// for the server half.
+var serverForbiddenKexAlgos = map[string]struct{}{
+	kexAlgoDHGEXSHA1:   {}, // server half implementation is only minimal to satisfy the automated tests
+	kexAlgoDHGEXSHA256: {}, // server half implementation is only minimal to satisfy the automated tests
+}
+
+// preferredKexAlgos specifies the default preference for key-exchange algorithms
+// in preference order.
+var preferredKexAlgos = []string{
+	kexAlgoCurve25519SHA256,
+	kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521,
+
+	// [Psiphon]
+	// Add kexAlgoDH14SHA256
+	kexAlgoDH14SHA256, kexAlgoDH14SHA1,
+}
+
 // supportedHostKeyAlgos specifies the supported host-key algorithms (i.e. methods
 // of authenticating servers) in preference order.
 var supportedHostKeyAlgos = []string{
@@ -115,6 +133,7 @@ func findCommon(what string, client []string, server []string) (common string, e
 	return "", fmt.Errorf("ssh: no common algorithm for %s; client offered: %v, server offered: %v", what, client, server)
 }
 
+// directionAlgorithms records algorithm choices in one direction (either read or write)
 type directionAlgorithms struct {
 	Cipher      string
 	MAC         string
@@ -143,7 +162,7 @@ type algorithms struct {
 	r       directionAlgorithms
 }
 
-func findAgreedAlgorithms(clientKexInit, serverKexInit *kexInitMsg) (algs *algorithms, err error) {
+func findAgreedAlgorithms(isClient bool, clientKexInit, serverKexInit *kexInitMsg) (algs *algorithms, err error) {
 	result := &algorithms{}
 
 	result.kex, err = findCommon("key exchange", clientKexInit.KexAlgos, serverKexInit.KexAlgos)
@@ -156,32 +175,37 @@ func findAgreedAlgorithms(clientKexInit, serverKexInit *kexInitMsg) (algs *algor
 		return
 	}
 
-	result.w.Cipher, err = findCommon("client to server cipher", clientKexInit.CiphersClientServer, serverKexInit.CiphersClientServer)
+	stoc, ctos := &result.w, &result.r
+	if isClient {
+		ctos, stoc = stoc, ctos
+	}
+
+	ctos.Cipher, err = findCommon("client to server cipher", clientKexInit.CiphersClientServer, serverKexInit.CiphersClientServer)
 	if err != nil {
 		return
 	}
 
-	result.r.Cipher, err = findCommon("server to client cipher", clientKexInit.CiphersServerClient, serverKexInit.CiphersServerClient)
+	stoc.Cipher, err = findCommon("server to client cipher", clientKexInit.CiphersServerClient, serverKexInit.CiphersServerClient)
 	if err != nil {
 		return
 	}
 
-	result.w.MAC, err = findCommon("client to server MAC", clientKexInit.MACsClientServer, serverKexInit.MACsClientServer)
+	ctos.MAC, err = findCommon("client to server MAC", clientKexInit.MACsClientServer, serverKexInit.MACsClientServer)
 	if err != nil {
 		return
 	}
 
-	result.r.MAC, err = findCommon("server to client MAC", clientKexInit.MACsServerClient, serverKexInit.MACsServerClient)
+	stoc.MAC, err = findCommon("server to client MAC", clientKexInit.MACsServerClient, serverKexInit.MACsServerClient)
 	if err != nil {
 		return
 	}
 
-	result.w.Compression, err = findCommon("client to server compression", clientKexInit.CompressionClientServer, serverKexInit.CompressionClientServer)
+	ctos.Compression, err = findCommon("client to server compression", clientKexInit.CompressionClientServer, serverKexInit.CompressionClientServer)
 	if err != nil {
 		return
 	}
 
-	result.r.Compression, err = findCommon("server to client compression", clientKexInit.CompressionServerClient, serverKexInit.CompressionServerClient)
+	stoc.Compression, err = findCommon("server to client compression", clientKexInit.CompressionServerClient, serverKexInit.CompressionServerClient)
 	if err != nil {
 		return
 	}
@@ -252,7 +276,7 @@ func (c *Config) SetDefaults() {
 	c.Ciphers = ciphers
 
 	if c.KeyExchanges == nil {
-		c.KeyExchanges = supportedKexAlgos
+		c.KeyExchanges = preferredKexAlgos
 	}
 
 	if c.MACs == nil {

+ 176 - 0
psiphon/common/crypto/ssh/common_test.go

@@ -0,0 +1,176 @@
+// Copyright 2019 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 ssh
+
+import (
+	"reflect"
+	"testing"
+)
+
+func TestFindAgreedAlgorithms(t *testing.T) {
+	initKex := func(k *kexInitMsg) {
+		if k.KexAlgos == nil {
+			k.KexAlgos = []string{"kex1"}
+		}
+		if k.ServerHostKeyAlgos == nil {
+			k.ServerHostKeyAlgos = []string{"hostkey1"}
+		}
+		if k.CiphersClientServer == nil {
+			k.CiphersClientServer = []string{"cipher1"}
+
+		}
+		if k.CiphersServerClient == nil {
+			k.CiphersServerClient = []string{"cipher1"}
+
+		}
+		if k.MACsClientServer == nil {
+			k.MACsClientServer = []string{"mac1"}
+
+		}
+		if k.MACsServerClient == nil {
+			k.MACsServerClient = []string{"mac1"}
+
+		}
+		if k.CompressionClientServer == nil {
+			k.CompressionClientServer = []string{"compression1"}
+
+		}
+		if k.CompressionServerClient == nil {
+			k.CompressionServerClient = []string{"compression1"}
+
+		}
+		if k.LanguagesClientServer == nil {
+			k.LanguagesClientServer = []string{"language1"}
+
+		}
+		if k.LanguagesServerClient == nil {
+			k.LanguagesServerClient = []string{"language1"}
+
+		}
+	}
+
+	initDirAlgs := func(a *directionAlgorithms) {
+		if a.Cipher == "" {
+			a.Cipher = "cipher1"
+		}
+		if a.MAC == "" {
+			a.MAC = "mac1"
+		}
+		if a.Compression == "" {
+			a.Compression = "compression1"
+		}
+	}
+
+	initAlgs := func(a *algorithms) {
+		if a.kex == "" {
+			a.kex = "kex1"
+		}
+		if a.hostKey == "" {
+			a.hostKey = "hostkey1"
+		}
+		initDirAlgs(&a.r)
+		initDirAlgs(&a.w)
+	}
+
+	type testcase struct {
+		name                   string
+		clientIn, serverIn     kexInitMsg
+		wantClient, wantServer algorithms
+		wantErr                bool
+	}
+
+	cases := []testcase{
+		testcase{
+			name: "standard",
+		},
+
+		testcase{
+			name: "no common hostkey",
+			serverIn: kexInitMsg{
+				ServerHostKeyAlgos: []string{"hostkey2"},
+			},
+			wantErr: true,
+		},
+
+		testcase{
+			name: "no common kex",
+			serverIn: kexInitMsg{
+				KexAlgos: []string{"kex2"},
+			},
+			wantErr: true,
+		},
+
+		testcase{
+			name: "no common cipher",
+			serverIn: kexInitMsg{
+				CiphersClientServer: []string{"cipher2"},
+			},
+			wantErr: true,
+		},
+
+		testcase{
+			name: "client decides cipher",
+			serverIn: kexInitMsg{
+				CiphersClientServer: []string{"cipher1", "cipher2"},
+				CiphersServerClient: []string{"cipher2", "cipher3"},
+			},
+			clientIn: kexInitMsg{
+				CiphersClientServer: []string{"cipher2", "cipher1"},
+				CiphersServerClient: []string{"cipher3", "cipher2"},
+			},
+			wantClient: algorithms{
+				r: directionAlgorithms{
+					Cipher: "cipher3",
+				},
+				w: directionAlgorithms{
+					Cipher: "cipher2",
+				},
+			},
+			wantServer: algorithms{
+				w: directionAlgorithms{
+					Cipher: "cipher3",
+				},
+				r: directionAlgorithms{
+					Cipher: "cipher2",
+				},
+			},
+		},
+
+		// TODO(hanwen): fix and add tests for AEAD ignoring
+		// the MACs field
+	}
+
+	for i := range cases {
+		initKex(&cases[i].clientIn)
+		initKex(&cases[i].serverIn)
+		initAlgs(&cases[i].wantClient)
+		initAlgs(&cases[i].wantServer)
+	}
+
+	for _, c := range cases {
+		t.Run(c.name, func(t *testing.T) {
+			serverAlgs, serverErr := findAgreedAlgorithms(false, &c.clientIn, &c.serverIn)
+			clientAlgs, clientErr := findAgreedAlgorithms(true, &c.clientIn, &c.serverIn)
+
+			serverHasErr := serverErr != nil
+			clientHasErr := clientErr != nil
+			if c.wantErr != serverHasErr || c.wantErr != clientHasErr {
+				t.Fatalf("got client/server error (%v, %v), want hasError %v",
+					clientErr, serverErr, c.wantErr)
+
+			}
+			if c.wantErr {
+				return
+			}
+
+			if !reflect.DeepEqual(serverAlgs, &c.wantServer) {
+				t.Errorf("server: got algs %#v, want %#v", serverAlgs, &c.wantServer)
+			}
+			if !reflect.DeepEqual(clientAlgs, &c.wantClient) {
+				t.Errorf("server: got algs %#v, want %#v", clientAlgs, &c.wantClient)
+			}
+		})
+	}
+}

+ 2 - 1
psiphon/common/crypto/ssh/example_test.go

@@ -140,7 +140,7 @@ func ExampleNewServerConn() {
 	}
 }
 
-func ExampleHostKeyCheck() {
+func ExampleClientConfig_HostKeyCallback() {
 	// Every client must provide a host key check.  Here is a
 	// simple-minded parse of OpenSSH's known_hosts file
 	host := "hostname"
@@ -198,6 +198,7 @@ func ExampleDial() {
 	if err != nil {
 		log.Fatal("Failed to dial: ", err)
 	}
+	defer client.Close()
 
 	// Each ClientConn can support multiple interactive sessions,
 	// represented by a Session.

+ 3 - 3
psiphon/common/crypto/ssh/handshake.go

@@ -497,7 +497,6 @@ func (t *handshakeTransport) sendKexInit() error {
 		(!equal(t.config.KeyExchanges, supportedKexAlgos) ||
 			!equal(t.config.Ciphers, preferredCiphers) ||
 			!equal(t.config.MACs, supportedMACs)) {
-
 		return errors.New("ssh: custom algorithm preferences not supported")
 	}
 
@@ -700,7 +699,8 @@ func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error {
 
 	clientInit := otherInit
 	serverInit := t.sentInitMsg
-	if len(t.hostKeys) == 0 {
+	isClient := len(t.hostKeys) == 0
+	if isClient {
 		clientInit, serverInit = serverInit, clientInit
 
 		magics.clientKexInit = t.sentInitPacket
@@ -708,7 +708,7 @@ func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error {
 	}
 
 	var err error
-	t.algorithms, err = findAgreedAlgorithms(clientInit, serverInit)
+	t.algorithms, err = findAgreedAlgorithms(isClient, clientInit, serverInit)
 	if err != nil {
 		return err
 	}

+ 3 - 0
psiphon/common/crypto/ssh/handshake_test.go

@@ -421,6 +421,9 @@ func TestHandshakeErrorHandlingWriteCoupled(t *testing.T) {
 // handshakeTransport deadlocks, the go runtime will detect it and
 // panic.
 func testHandshakeErrorHandlingN(t *testing.T, readLimit, writeLimit int, coupled bool) {
+	if runtime.GOOS == "js" && runtime.GOARCH == "wasm" {
+		t.Skip("skipping on js/wasm; see golang.org/issue/32840")
+	}
 	msg := Marshal(&serviceRequestMsg{strings.Repeat("x", int(minRekeyThreshold)/4)})
 
 	a, b := memPipe()

+ 93 - 0
psiphon/common/crypto/ssh/internal/bcrypt_pbkdf/bcrypt_pbkdf.go

@@ -0,0 +1,93 @@
+// Copyright 2014 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 bcrypt_pbkdf implements bcrypt_pbkdf(3) from OpenBSD.
+//
+// See https://flak.tedunangst.com/post/bcrypt-pbkdf and
+// https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/lib/libutil/bcrypt_pbkdf.c.
+package bcrypt_pbkdf
+
+import (
+	"crypto/sha512"
+	"errors"
+	"golang.org/x/crypto/blowfish"
+)
+
+const blockSize = 32
+
+// Key derives a key from the password, salt and rounds count, returning a
+// []byte of length keyLen that can be used as cryptographic key.
+func Key(password, salt []byte, rounds, keyLen int) ([]byte, error) {
+	if rounds < 1 {
+		return nil, errors.New("bcrypt_pbkdf: number of rounds is too small")
+	}
+	if len(password) == 0 {
+		return nil, errors.New("bcrypt_pbkdf: empty password")
+	}
+	if len(salt) == 0 || len(salt) > 1<<20 {
+		return nil, errors.New("bcrypt_pbkdf: bad salt length")
+	}
+	if keyLen > 1024 {
+		return nil, errors.New("bcrypt_pbkdf: keyLen is too large")
+	}
+
+	numBlocks := (keyLen + blockSize - 1) / blockSize
+	key := make([]byte, numBlocks*blockSize)
+
+	h := sha512.New()
+	h.Write(password)
+	shapass := h.Sum(nil)
+
+	shasalt := make([]byte, 0, sha512.Size)
+	cnt, tmp := make([]byte, 4), make([]byte, blockSize)
+	for block := 1; block <= numBlocks; block++ {
+		h.Reset()
+		h.Write(salt)
+		cnt[0] = byte(block >> 24)
+		cnt[1] = byte(block >> 16)
+		cnt[2] = byte(block >> 8)
+		cnt[3] = byte(block)
+		h.Write(cnt)
+		bcryptHash(tmp, shapass, h.Sum(shasalt))
+
+		out := make([]byte, blockSize)
+		copy(out, tmp)
+		for i := 2; i <= rounds; i++ {
+			h.Reset()
+			h.Write(tmp)
+			bcryptHash(tmp, shapass, h.Sum(shasalt))
+			for j := 0; j < len(out); j++ {
+				out[j] ^= tmp[j]
+			}
+		}
+
+		for i, v := range out {
+			key[i*numBlocks+(block-1)] = v
+		}
+	}
+	return key[:keyLen], nil
+}
+
+var magic = []byte("OxychromaticBlowfishSwatDynamite")
+
+func bcryptHash(out, shapass, shasalt []byte) {
+	c, err := blowfish.NewSaltedCipher(shapass, shasalt)
+	if err != nil {
+		panic(err)
+	}
+	for i := 0; i < 64; i++ {
+		blowfish.ExpandKey(shasalt, c)
+		blowfish.ExpandKey(shapass, c)
+	}
+	copy(out, magic)
+	for i := 0; i < 32; i += 8 {
+		for j := 0; j < 64; j++ {
+			c.Encrypt(out[i:i+8], out[i:i+8])
+		}
+	}
+	// Swap bytes due to different endianness.
+	for i := 0; i < 32; i += 4 {
+		out[i+3], out[i+2], out[i+1], out[i] = out[i], out[i+1], out[i+2], out[i+3]
+	}
+}

+ 97 - 0
psiphon/common/crypto/ssh/internal/bcrypt_pbkdf/bcrypt_pbkdf_test.go

@@ -0,0 +1,97 @@
+// Copyright 2014 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 bcrypt_pbkdf
+
+import (
+	"bytes"
+	"testing"
+)
+
+// Test vectors generated by the reference implementation from OpenBSD.
+var golden = []struct {
+	rounds                 int
+	password, salt, result []byte
+}{
+	{
+		12,
+		[]byte("password"),
+		[]byte("salt"),
+		[]byte{
+			0x1a, 0xe4, 0x2c, 0x05, 0xd4, 0x87, 0xbc, 0x02, 0xf6,
+			0x49, 0x21, 0xa4, 0xeb, 0xe4, 0xea, 0x93, 0xbc, 0xac,
+			0xfe, 0x13, 0x5f, 0xda, 0x99, 0x97, 0x4c, 0x06, 0xb7,
+			0xb0, 0x1f, 0xae, 0x14, 0x9a,
+		},
+	},
+	{
+		3,
+		[]byte("passwordy\x00PASSWORD\x00"),
+		[]byte("salty\x00SALT\x00"),
+		[]byte{
+			0x7f, 0x31, 0x0b, 0xd3, 0xe7, 0x8c, 0x32, 0x80, 0xc5,
+			0x9c, 0xe4, 0x59, 0x52, 0x11, 0xa2, 0x92, 0x8e, 0x8d,
+			0x4e, 0xc7, 0x44, 0xc1, 0xed, 0x2e, 0xfc, 0x9f, 0x76,
+			0x4e, 0x33, 0x88, 0xe0, 0xad,
+		},
+	},
+	{
+		// See http://thread.gmane.org/gmane.os.openbsd.bugs/20542
+		8,
+		[]byte("секретное слово"),
+		[]byte("посолить немножко"),
+		[]byte{
+			0x8d, 0xf4, 0x3f, 0xc6, 0xfe, 0x13, 0x1f, 0xc4, 0x7f,
+			0x0c, 0x9e, 0x39, 0x22, 0x4b, 0xd9, 0x4c, 0x70, 0xb6,
+			0xfc, 0xc8, 0xee, 0x81, 0x35, 0xfa, 0xdd, 0xf6, 0x11,
+			0x56, 0xe6, 0xcb, 0x27, 0x33, 0xea, 0x76, 0x5f, 0x31,
+			0x5a, 0x3e, 0x1e, 0x4a, 0xfc, 0x35, 0xbf, 0x86, 0x87,
+			0xd1, 0x89, 0x25, 0x4c, 0x1e, 0x05, 0xa6, 0xfe, 0x80,
+			0xc0, 0x61, 0x7f, 0x91, 0x83, 0xd6, 0x72, 0x60, 0xd6,
+			0xa1, 0x15, 0xc6, 0xc9, 0x4e, 0x36, 0x03, 0xe2, 0x30,
+			0x3f, 0xbb, 0x43, 0xa7, 0x6a, 0x64, 0x52, 0x3f, 0xfd,
+			0xa6, 0x86, 0xb1, 0xd4, 0x51, 0x85, 0x43,
+		},
+	},
+}
+
+func TestKey(t *testing.T) {
+	for i, v := range golden {
+		k, err := Key(v.password, v.salt, v.rounds, len(v.result))
+		if err != nil {
+			t.Errorf("%d: %s", i, err)
+			continue
+		}
+		if !bytes.Equal(k, v.result) {
+			t.Errorf("%d: expected\n%x\n, got\n%x\n", i, v.result, k)
+		}
+	}
+}
+
+func TestBcryptHash(t *testing.T) {
+	good := []byte{
+		0x87, 0x90, 0x48, 0x70, 0xee, 0xf9, 0xde, 0xdd, 0xf8, 0xe7,
+		0x61, 0x1a, 0x14, 0x01, 0x06, 0xe6, 0xaa, 0xf1, 0xa3, 0x63,
+		0xd9, 0xa2, 0xc5, 0x04, 0xdb, 0x35, 0x64, 0x43, 0x72, 0x1e,
+		0xb5, 0x55,
+	}
+	var pass, salt [64]byte
+	var result [32]byte
+	for i := 0; i < 64; i++ {
+		pass[i] = byte(i)
+		salt[i] = byte(i + 64)
+	}
+	bcryptHash(result[:], pass[:], salt[:])
+	if !bytes.Equal(result[:], good) {
+		t.Errorf("expected %x, got %x", good, result)
+	}
+}
+
+func BenchmarkKey(b *testing.B) {
+	pass := []byte("password")
+	salt := []byte("salt")
+	for i := 0; i < b.N; i++ {
+		Key(pass, salt, 10, 32)
+	}
+}

+ 243 - 1
psiphon/common/crypto/ssh/kex.go

@@ -10,7 +10,9 @@ import (
 	"crypto/elliptic"
 	"crypto/rand"
 	"crypto/subtle"
+	"encoding/binary"
 	"errors"
+	"fmt"
 	"io"
 	"math/big"
 
@@ -25,6 +27,12 @@ const (
 	kexAlgoECDH384          = "ecdh-sha2-nistp384"
 	kexAlgoECDH521          = "ecdh-sha2-nistp521"
 	kexAlgoCurve25519SHA256 = "curve25519-sha256@libssh.org"
+
+	// For the following kex only the client half contains a production
+	// ready implementation. The server half only consists of a minimal
+	// implementation to satisfy the automated tests.
+	kexAlgoDHGEXSHA1   = "diffie-hellman-group-exchange-sha1"
+	kexAlgoDHGEXSHA256 = "diffie-hellman-group-exchange-sha256"
 )
 
 // kexResult captures the outcome of a key exchange.
@@ -213,7 +221,7 @@ func (group *dhGroup) Server(c packetConn, randSource io.Reader, magics *handsha
 		HostKey:   hostKeyBytes,
 		Signature: sig,
 		Hash:      hashFunc,
-	}, nil
+	}, err
 }
 
 // ecdh performs Elliptic Curve Diffie-Hellman key exchange as
@@ -429,6 +437,8 @@ func init() {
 	kexAlgoMap[kexAlgoECDH384] = &ecdh{elliptic.P384()}
 	kexAlgoMap[kexAlgoECDH256] = &ecdh{elliptic.P256()}
 	kexAlgoMap[kexAlgoCurve25519SHA256] = &curve25519sha256{}
+	kexAlgoMap[kexAlgoDHGEXSHA1] = &dhGEXSHA{hashFunc: crypto.SHA1}
+	kexAlgoMap[kexAlgoDHGEXSHA256] = &dhGEXSHA{hashFunc: crypto.SHA256}
 }
 
 // curve25519sha256 implements the curve25519-sha256@libssh.org key
@@ -565,3 +575,235 @@ func (kex *curve25519sha256) Server(c packetConn, rand io.Reader, magics *handsh
 		Hash:      crypto.SHA256,
 	}, nil
 }
+
+// dhGEXSHA implements the diffie-hellman-group-exchange-sha1 and
+// diffie-hellman-group-exchange-sha256 key agreement protocols,
+// as described in RFC 4419
+type dhGEXSHA struct {
+	g, p     *big.Int
+	hashFunc crypto.Hash
+}
+
+const (
+	dhGroupExchangeMinimumBits   = 2048
+	dhGroupExchangePreferredBits = 2048
+	dhGroupExchangeMaximumBits   = 8192
+)
+
+func (gex *dhGEXSHA) diffieHellman(theirPublic, myPrivate *big.Int) (*big.Int, error) {
+	if theirPublic.Sign() <= 0 || theirPublic.Cmp(gex.p) >= 0 {
+		return nil, fmt.Errorf("ssh: DH parameter out of bounds")
+	}
+	return new(big.Int).Exp(theirPublic, myPrivate, gex.p), nil
+}
+
+func (gex dhGEXSHA) Client(c packetConn, randSource io.Reader, magics *handshakeMagics) (*kexResult, error) {
+	// Send GexRequest
+	kexDHGexRequest := kexDHGexRequestMsg{
+		MinBits:      dhGroupExchangeMinimumBits,
+		PreferedBits: dhGroupExchangePreferredBits,
+		MaxBits:      dhGroupExchangeMaximumBits,
+	}
+	if err := c.writePacket(Marshal(&kexDHGexRequest)); err != nil {
+		return nil, err
+	}
+
+	// Receive GexGroup
+	packet, err := c.readPacket()
+	if err != nil {
+		return nil, err
+	}
+
+	var kexDHGexGroup kexDHGexGroupMsg
+	if err = Unmarshal(packet, &kexDHGexGroup); err != nil {
+		return nil, err
+	}
+
+	// reject if p's bit length < dhGroupExchangeMinimumBits or > dhGroupExchangeMaximumBits
+	if kexDHGexGroup.P.BitLen() < dhGroupExchangeMinimumBits || kexDHGexGroup.P.BitLen() > dhGroupExchangeMaximumBits {
+		return nil, fmt.Errorf("ssh: server-generated gex p is out of range (%d bits)", kexDHGexGroup.P.BitLen())
+	}
+
+	gex.p = kexDHGexGroup.P
+	gex.g = kexDHGexGroup.G
+
+	// Check if g is safe by verifing that g > 1 and g < p - 1
+	one := big.NewInt(1)
+	var pMinusOne = &big.Int{}
+	pMinusOne.Sub(gex.p, one)
+	if gex.g.Cmp(one) != 1 && gex.g.Cmp(pMinusOne) != -1 {
+		return nil, fmt.Errorf("ssh: server provided gex g is not safe")
+	}
+
+	// Send GexInit
+	var pHalf = &big.Int{}
+	pHalf.Rsh(gex.p, 1)
+	x, err := rand.Int(randSource, pHalf)
+	if err != nil {
+		return nil, err
+	}
+	X := new(big.Int).Exp(gex.g, x, gex.p)
+	kexDHGexInit := kexDHGexInitMsg{
+		X: X,
+	}
+	if err := c.writePacket(Marshal(&kexDHGexInit)); err != nil {
+		return nil, err
+	}
+
+	// Receive GexReply
+	packet, err = c.readPacket()
+	if err != nil {
+		return nil, err
+	}
+
+	var kexDHGexReply kexDHGexReplyMsg
+	if err = Unmarshal(packet, &kexDHGexReply); err != nil {
+		return nil, err
+	}
+
+	kInt, err := gex.diffieHellman(kexDHGexReply.Y, x)
+	if err != nil {
+		return nil, err
+	}
+
+	// Check if k is safe by verifing that k > 1 and k < p - 1
+	if kInt.Cmp(one) != 1 && kInt.Cmp(pMinusOne) != -1 {
+		return nil, fmt.Errorf("ssh: derived k is not safe")
+	}
+
+	h := gex.hashFunc.New()
+	magics.write(h)
+	writeString(h, kexDHGexReply.HostKey)
+	binary.Write(h, binary.BigEndian, uint32(dhGroupExchangeMinimumBits))
+	binary.Write(h, binary.BigEndian, uint32(dhGroupExchangePreferredBits))
+	binary.Write(h, binary.BigEndian, uint32(dhGroupExchangeMaximumBits))
+	writeInt(h, gex.p)
+	writeInt(h, gex.g)
+	writeInt(h, X)
+	writeInt(h, kexDHGexReply.Y)
+	K := make([]byte, intLength(kInt))
+	marshalInt(K, kInt)
+	h.Write(K)
+
+	return &kexResult{
+		H:         h.Sum(nil),
+		K:         K,
+		HostKey:   kexDHGexReply.HostKey,
+		Signature: kexDHGexReply.Signature,
+		Hash:      gex.hashFunc,
+	}, nil
+}
+
+// Server half implementation of the Diffie Hellman Key Exchange with SHA1 and SHA256.
+//
+// This is a minimal implementation to satisfy the automated tests.
+func (gex dhGEXSHA) Server(c packetConn, randSource io.Reader, magics *handshakeMagics, priv Signer) (result *kexResult, err error) {
+	// Receive GexRequest
+	packet, err := c.readPacket()
+	if err != nil {
+		return
+	}
+	var kexDHGexRequest kexDHGexRequestMsg
+	if err = Unmarshal(packet, &kexDHGexRequest); err != nil {
+		return
+	}
+
+	// smoosh the user's preferred size into our own limits
+	if kexDHGexRequest.PreferedBits > dhGroupExchangeMaximumBits {
+		kexDHGexRequest.PreferedBits = dhGroupExchangeMaximumBits
+	}
+	if kexDHGexRequest.PreferedBits < dhGroupExchangeMinimumBits {
+		kexDHGexRequest.PreferedBits = dhGroupExchangeMinimumBits
+	}
+	// fix min/max if they're inconsistent.  technically, we could just pout
+	// and hang up, but there's no harm in giving them the benefit of the
+	// doubt and just picking a bitsize for them.
+	if kexDHGexRequest.MinBits > kexDHGexRequest.PreferedBits {
+		kexDHGexRequest.MinBits = kexDHGexRequest.PreferedBits
+	}
+	if kexDHGexRequest.MaxBits < kexDHGexRequest.PreferedBits {
+		kexDHGexRequest.MaxBits = kexDHGexRequest.PreferedBits
+	}
+
+	// Send GexGroup
+	// This is the group called diffie-hellman-group14-sha1 in RFC
+	// 4253 and Oakley Group 14 in RFC 3526.
+	p, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", 16)
+	gex.p = p
+	gex.g = big.NewInt(2)
+
+	kexDHGexGroup := kexDHGexGroupMsg{
+		P: gex.p,
+		G: gex.g,
+	}
+	if err := c.writePacket(Marshal(&kexDHGexGroup)); err != nil {
+		return nil, err
+	}
+
+	// Receive GexInit
+	packet, err = c.readPacket()
+	if err != nil {
+		return
+	}
+	var kexDHGexInit kexDHGexInitMsg
+	if err = Unmarshal(packet, &kexDHGexInit); err != nil {
+		return
+	}
+
+	var pHalf = &big.Int{}
+	pHalf.Rsh(gex.p, 1)
+
+	y, err := rand.Int(randSource, pHalf)
+	if err != nil {
+		return
+	}
+
+	Y := new(big.Int).Exp(gex.g, y, gex.p)
+	kInt, err := gex.diffieHellman(kexDHGexInit.X, y)
+	if err != nil {
+		return nil, err
+	}
+
+	hostKeyBytes := priv.PublicKey().Marshal()
+
+	h := gex.hashFunc.New()
+	magics.write(h)
+	writeString(h, hostKeyBytes)
+	binary.Write(h, binary.BigEndian, uint32(dhGroupExchangeMinimumBits))
+	binary.Write(h, binary.BigEndian, uint32(dhGroupExchangePreferredBits))
+	binary.Write(h, binary.BigEndian, uint32(dhGroupExchangeMaximumBits))
+	writeInt(h, gex.p)
+	writeInt(h, gex.g)
+	writeInt(h, kexDHGexInit.X)
+	writeInt(h, Y)
+
+	K := make([]byte, intLength(kInt))
+	marshalInt(K, kInt)
+	h.Write(K)
+
+	H := h.Sum(nil)
+
+	// H is already a hash, but the hostkey signing will apply its
+	// own key-specific hash algorithm.
+	sig, err := signAndMarshal(priv, randSource, H)
+	if err != nil {
+		return nil, err
+	}
+
+	kexDHGexReply := kexDHGexReplyMsg{
+		HostKey:   hostKeyBytes,
+		Y:         Y,
+		Signature: sig,
+	}
+	packet = Marshal(&kexDHGexReply)
+
+	err = c.writePacket(packet)
+
+	return &kexResult{
+		H:         H,
+		K:         K,
+		HostKey:   hostKeyBytes,
+		Signature: sig,
+		Hash:      gex.hashFunc,
+	}, err
+}

+ 40 - 25
psiphon/common/crypto/ssh/kex_test.go

@@ -9,9 +9,14 @@ package ssh
 import (
 	"crypto/rand"
 	"reflect"
+	"sync"
 	"testing"
 )
 
+// Runs multiple key exchanges concurrent to detect potential data races with
+// kex obtained from the global kexAlgoMap.
+// This test needs to be executed using the race detector in order to detect
+// race conditions.
 func TestKexes(t *testing.T) {
 	type kexResultErr struct {
 		result *kexResult
@@ -19,32 +24,42 @@ func TestKexes(t *testing.T) {
 	}
 
 	for name, kex := range kexAlgoMap {
-		a, b := memPipe()
+		t.Run(name, func(t *testing.T) {
+			wg := sync.WaitGroup{}
+			for i := 0; i < 3; i++ {
+				wg.Add(1)
+				go func() {
+					defer wg.Done()
+					a, b := memPipe()
 
-		s := make(chan kexResultErr, 1)
-		c := make(chan kexResultErr, 1)
-		var magics handshakeMagics
-		go func() {
-			r, e := kex.Client(a, rand.Reader, &magics)
-			a.Close()
-			c <- kexResultErr{r, e}
-		}()
-		go func() {
-			r, e := kex.Server(b, rand.Reader, &magics, testSigners["ecdsa"])
-			b.Close()
-			s <- kexResultErr{r, e}
-		}()
+					s := make(chan kexResultErr, 1)
+					c := make(chan kexResultErr, 1)
+					var magics handshakeMagics
+					go func() {
+						r, e := kex.Client(a, rand.Reader, &magics)
+						a.Close()
+						c <- kexResultErr{r, e}
+					}()
+					go func() {
+						r, e := kex.Server(b, rand.Reader, &magics, testSigners["ecdsa"])
+						b.Close()
+						s <- kexResultErr{r, e}
+					}()
 
-		clientRes := <-c
-		serverRes := <-s
-		if clientRes.err != nil {
-			t.Errorf("client: %v", clientRes.err)
-		}
-		if serverRes.err != nil {
-			t.Errorf("server: %v", serverRes.err)
-		}
-		if !reflect.DeepEqual(clientRes.result, serverRes.result) {
-			t.Errorf("kex %q: mismatch %#v, %#v", name, clientRes.result, serverRes.result)
-		}
+					clientRes := <-c
+					serverRes := <-s
+					if clientRes.err != nil {
+						t.Errorf("client: %v", clientRes.err)
+					}
+					if serverRes.err != nil {
+						t.Errorf("server: %v", serverRes.err)
+					}
+					if !reflect.DeepEqual(clientRes.result, serverRes.result) {
+						t.Errorf("kex %q: mismatch %#v, %#v", name, clientRes.result, serverRes.result)
+					}
+				}()
+			}
+			wg.Wait()
+		})
 	}
 }

+ 507 - 67
psiphon/common/crypto/ssh/keys.go

@@ -7,9 +7,10 @@ package ssh
 import (
 	"bytes"
 	"crypto"
+	"crypto/aes"
+	"crypto/cipher"
 	"crypto/dsa"
 	"crypto/ecdsa"
-	"crypto/ed25519"
 	"crypto/elliptic"
 	"crypto/md5"
 	"crypto/rsa"
@@ -24,17 +25,32 @@ import (
 	"io"
 	"math/big"
 	"strings"
+
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/ssh/internal/bcrypt_pbkdf"
+	"golang.org/x/crypto/ed25519"
 )
 
 // These constants represent the algorithm names for key types supported by this
 // package.
 const (
-	KeyAlgoRSA      = "ssh-rsa"
-	KeyAlgoDSA      = "ssh-dss"
-	KeyAlgoECDSA256 = "ecdsa-sha2-nistp256"
-	KeyAlgoECDSA384 = "ecdsa-sha2-nistp384"
-	KeyAlgoECDSA521 = "ecdsa-sha2-nistp521"
-	KeyAlgoED25519  = "ssh-ed25519"
+	KeyAlgoRSA        = "ssh-rsa"
+	KeyAlgoDSA        = "ssh-dss"
+	KeyAlgoECDSA256   = "ecdsa-sha2-nistp256"
+	KeyAlgoSKECDSA256 = "sk-ecdsa-sha2-nistp256@openssh.com"
+	KeyAlgoECDSA384   = "ecdsa-sha2-nistp384"
+	KeyAlgoECDSA521   = "ecdsa-sha2-nistp521"
+	KeyAlgoED25519    = "ssh-ed25519"
+	KeyAlgoSKED25519  = "sk-ssh-ed25519@openssh.com"
+)
+
+// These constants represent non-default signature algorithms that are supported
+// as algorithm parameters to AlgorithmSigner.SignWithAlgorithm methods. See
+// [PROTOCOL.agent] section 4.5.1 and
+// https://tools.ietf.org/html/draft-ietf-curdle-rsa-sha2-10
+const (
+	SigAlgoRSA        = "ssh-rsa"
+	SigAlgoRSASHA2256 = "rsa-sha2-256"
+	SigAlgoRSASHA2512 = "rsa-sha2-512"
 )
 
 // parsePubKey parses a public key of the given algorithm.
@@ -47,9 +63,13 @@ func parsePubKey(in []byte, algo string) (pubKey PublicKey, rest []byte, err err
 		return parseDSA(in)
 	case KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521:
 		return parseECDSA(in)
+	case KeyAlgoSKECDSA256:
+		return parseSKECDSA(in)
 	case KeyAlgoED25519:
 		return parseED25519(in)
-	case CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoED25519v01:
+	case KeyAlgoSKED25519:
+		return parseSKEd25519(in)
+	case CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoSKECDSA256v01, CertAlgoED25519v01, CertAlgoSKED25519v01:
 		cert, err := parseCert(in, certToPrivAlgo(algo))
 		if err != nil {
 			return nil, nil, err
@@ -300,6 +320,19 @@ type Signer interface {
 	Sign(rand io.Reader, data []byte) (*Signature, error)
 }
 
+// A AlgorithmSigner is a Signer that also supports specifying a specific
+// algorithm to use for signing.
+type AlgorithmSigner interface {
+	Signer
+
+	// SignWithAlgorithm is like Signer.Sign, but allows specification of a
+	// non-default signing algorithm. See the SigAlgo* constants in this
+	// package for signature algorithms supported by this package. Callers may
+	// pass an empty string for the algorithm in which case the AlgorithmSigner
+	// will use its default algorithm.
+	SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*Signature, error)
+}
+
 type rsaPublicKey rsa.PublicKey
 
 func (r *rsaPublicKey) Type() string {
@@ -348,13 +381,21 @@ func (r *rsaPublicKey) Marshal() []byte {
 }
 
 func (r *rsaPublicKey) Verify(data []byte, sig *Signature) error {
-	if sig.Format != r.Type() {
+	var hash crypto.Hash
+	switch sig.Format {
+	case SigAlgoRSA:
+		hash = crypto.SHA1
+	case SigAlgoRSASHA2256:
+		hash = crypto.SHA256
+	case SigAlgoRSASHA2512:
+		hash = crypto.SHA512
+	default:
 		return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, r.Type())
 	}
-	h := crypto.SHA1.New()
+	h := hash.New()
 	h.Write(data)
 	digest := h.Sum(nil)
-	return rsa.VerifyPKCS1v15((*rsa.PublicKey)(r), crypto.SHA1, digest, sig.Blob)
+	return rsa.VerifyPKCS1v15((*rsa.PublicKey)(r), hash, digest, sig.Blob)
 }
 
 func (r *rsaPublicKey) CryptoPublicKey() crypto.PublicKey {
@@ -458,6 +499,14 @@ func (k *dsaPrivateKey) PublicKey() PublicKey {
 }
 
 func (k *dsaPrivateKey) Sign(rand io.Reader, data []byte) (*Signature, error) {
+	return k.SignWithAlgorithm(rand, data, "")
+}
+
+func (k *dsaPrivateKey) SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*Signature, error) {
+	if algorithm != "" && algorithm != k.PublicKey().Type() {
+		return nil, fmt.Errorf("ssh: unsupported signature algorithm %s", algorithm)
+	}
+
 	h := crypto.SHA1.New()
 	h.Write(data)
 	digest := h.Sum(nil)
@@ -513,9 +562,11 @@ func parseED25519(in []byte) (out PublicKey, rest []byte, err error) {
 		return nil, nil, err
 	}
 
-	key := ed25519.PublicKey(w.KeyBytes)
+	if l := len(w.KeyBytes); l != ed25519.PublicKeySize {
+		return nil, nil, fmt.Errorf("invalid size %d for Ed25519 public key", l)
+	}
 
-	return (ed25519PublicKey)(key), w.Rest, nil
+	return ed25519PublicKey(w.KeyBytes), w.Rest, nil
 }
 
 func (k ed25519PublicKey) Marshal() []byte {
@@ -533,9 +584,11 @@ func (k ed25519PublicKey) Verify(b []byte, sig *Signature) error {
 	if sig.Format != k.Type() {
 		return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, k.Type())
 	}
+	if l := len(k); l != ed25519.PublicKeySize {
+		return fmt.Errorf("ssh: invalid size %d for Ed25519 public key", l)
+	}
 
-	edKey := (ed25519.PublicKey)(k)
-	if ok := ed25519.Verify(edKey, b, sig.Blob); !ok {
+	if ok := ed25519.Verify(ed25519.PublicKey(k), b, sig.Blob); !ok {
 		return errors.New("ssh: signature did not verify")
 	}
 
@@ -645,6 +698,224 @@ func (k *ecdsaPublicKey) CryptoPublicKey() crypto.PublicKey {
 	return (*ecdsa.PublicKey)(k)
 }
 
+// skFields holds the additional fields present in U2F/FIDO2 signatures.
+// See openssh/PROTOCOL.u2f 'SSH U2F Signatures' for details.
+type skFields struct {
+	// Flags contains U2F/FIDO2 flags such as 'user present'
+	Flags byte
+	// Counter is a monotonic signature counter which can be
+	// used to detect concurrent use of a private key, should
+	// it be extracted from hardware.
+	Counter uint32
+}
+
+type skECDSAPublicKey struct {
+	// application is a URL-like string, typically "ssh:" for SSH.
+	// see openssh/PROTOCOL.u2f for details.
+	application string
+	ecdsa.PublicKey
+}
+
+func (k *skECDSAPublicKey) Type() string {
+	return KeyAlgoSKECDSA256
+}
+
+func (k *skECDSAPublicKey) nistID() string {
+	return "nistp256"
+}
+
+func parseSKECDSA(in []byte) (out PublicKey, rest []byte, err error) {
+	var w struct {
+		Curve       string
+		KeyBytes    []byte
+		Application string
+		Rest        []byte `ssh:"rest"`
+	}
+
+	if err := Unmarshal(in, &w); err != nil {
+		return nil, nil, err
+	}
+
+	key := new(skECDSAPublicKey)
+	key.application = w.Application
+
+	if w.Curve != "nistp256" {
+		return nil, nil, errors.New("ssh: unsupported curve")
+	}
+	key.Curve = elliptic.P256()
+
+	key.X, key.Y = elliptic.Unmarshal(key.Curve, w.KeyBytes)
+	if key.X == nil || key.Y == nil {
+		return nil, nil, errors.New("ssh: invalid curve point")
+	}
+
+	return key, w.Rest, nil
+}
+
+func (k *skECDSAPublicKey) Marshal() []byte {
+	// See RFC 5656, section 3.1.
+	keyBytes := elliptic.Marshal(k.Curve, k.X, k.Y)
+	w := struct {
+		Name        string
+		ID          string
+		Key         []byte
+		Application string
+	}{
+		k.Type(),
+		k.nistID(),
+		keyBytes,
+		k.application,
+	}
+
+	return Marshal(&w)
+}
+
+func (k *skECDSAPublicKey) Verify(data []byte, sig *Signature) error {
+	if sig.Format != k.Type() {
+		return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, k.Type())
+	}
+
+	h := ecHash(k.Curve).New()
+	h.Write([]byte(k.application))
+	appDigest := h.Sum(nil)
+
+	h.Reset()
+	h.Write(data)
+	dataDigest := h.Sum(nil)
+
+	var ecSig struct {
+		R *big.Int
+		S *big.Int
+	}
+	if err := Unmarshal(sig.Blob, &ecSig); err != nil {
+		return err
+	}
+
+	var skf skFields
+	if err := Unmarshal(sig.Rest, &skf); err != nil {
+		return err
+	}
+
+	blob := struct {
+		ApplicationDigest []byte `ssh:"rest"`
+		Flags             byte
+		Counter           uint32
+		MessageDigest     []byte `ssh:"rest"`
+	}{
+		appDigest,
+		skf.Flags,
+		skf.Counter,
+		dataDigest,
+	}
+
+	original := Marshal(blob)
+
+	h.Reset()
+	h.Write(original)
+	digest := h.Sum(nil)
+
+	if ecdsa.Verify((*ecdsa.PublicKey)(&k.PublicKey), digest, ecSig.R, ecSig.S) {
+		return nil
+	}
+	return errors.New("ssh: signature did not verify")
+}
+
+type skEd25519PublicKey struct {
+	// application is a URL-like string, typically "ssh:" for SSH.
+	// see openssh/PROTOCOL.u2f for details.
+	application string
+	ed25519.PublicKey
+}
+
+func (k *skEd25519PublicKey) Type() string {
+	return KeyAlgoSKED25519
+}
+
+func parseSKEd25519(in []byte) (out PublicKey, rest []byte, err error) {
+	var w struct {
+		KeyBytes    []byte
+		Application string
+		Rest        []byte `ssh:"rest"`
+	}
+
+	if err := Unmarshal(in, &w); err != nil {
+		return nil, nil, err
+	}
+
+	if l := len(w.KeyBytes); l != ed25519.PublicKeySize {
+		return nil, nil, fmt.Errorf("invalid size %d for Ed25519 public key", l)
+	}
+
+	key := new(skEd25519PublicKey)
+	key.application = w.Application
+	key.PublicKey = ed25519.PublicKey(w.KeyBytes)
+
+	return key, w.Rest, nil
+}
+
+func (k *skEd25519PublicKey) Marshal() []byte {
+	w := struct {
+		Name        string
+		KeyBytes    []byte
+		Application string
+	}{
+		KeyAlgoSKED25519,
+		[]byte(k.PublicKey),
+		k.application,
+	}
+	return Marshal(&w)
+}
+
+func (k *skEd25519PublicKey) Verify(data []byte, sig *Signature) error {
+	if sig.Format != k.Type() {
+		return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, k.Type())
+	}
+	if l := len(k.PublicKey); l != ed25519.PublicKeySize {
+		return fmt.Errorf("invalid size %d for Ed25519 public key", l)
+	}
+
+	h := sha256.New()
+	h.Write([]byte(k.application))
+	appDigest := h.Sum(nil)
+
+	h.Reset()
+	h.Write(data)
+	dataDigest := h.Sum(nil)
+
+	var edSig struct {
+		Signature []byte `ssh:"rest"`
+	}
+
+	if err := Unmarshal(sig.Blob, &edSig); err != nil {
+		return err
+	}
+
+	var skf skFields
+	if err := Unmarshal(sig.Rest, &skf); err != nil {
+		return err
+	}
+
+	blob := struct {
+		ApplicationDigest []byte `ssh:"rest"`
+		Flags             byte
+		Counter           uint32
+		MessageDigest     []byte `ssh:"rest"`
+	}{
+		appDigest,
+		skf.Flags,
+		skf.Counter,
+		dataDigest,
+	}
+
+	original := Marshal(blob)
+
+	if ok := ed25519.Verify(k.PublicKey, original, edSig.Signature); !ok {
+		return errors.New("ssh: signature did not verify")
+	}
+
+	return nil
+}
+
 // NewSignerFromKey takes an *rsa.PrivateKey, *dsa.PrivateKey,
 // *ecdsa.PrivateKey or any other crypto.Signer and returns a
 // corresponding Signer instance. ECDSA keys must use P-256, P-384 or
@@ -690,16 +961,42 @@ func (s *wrappedSigner) PublicKey() PublicKey {
 }
 
 func (s *wrappedSigner) Sign(rand io.Reader, data []byte) (*Signature, error) {
+	return s.SignWithAlgorithm(rand, data, "")
+}
+
+func (s *wrappedSigner) SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*Signature, error) {
 	var hashFunc crypto.Hash
 
-	switch key := s.pubKey.(type) {
-	case *rsaPublicKey, *dsaPublicKey:
-		hashFunc = crypto.SHA1
-	case *ecdsaPublicKey:
-		hashFunc = ecHash(key.Curve)
-	case ed25519PublicKey:
-	default:
-		return nil, fmt.Errorf("ssh: unsupported key type %T", key)
+	if _, ok := s.pubKey.(*rsaPublicKey); ok {
+		// RSA keys support a few hash functions determined by the requested signature algorithm
+		switch algorithm {
+		case "", SigAlgoRSA:
+			algorithm = SigAlgoRSA
+			hashFunc = crypto.SHA1
+		case SigAlgoRSASHA2256:
+			hashFunc = crypto.SHA256
+		case SigAlgoRSASHA2512:
+			hashFunc = crypto.SHA512
+		default:
+			return nil, fmt.Errorf("ssh: unsupported signature algorithm %s", algorithm)
+		}
+	} else {
+		// The only supported algorithm for all other key types is the same as the type of the key
+		if algorithm == "" {
+			algorithm = s.pubKey.Type()
+		} else if algorithm != s.pubKey.Type() {
+			return nil, fmt.Errorf("ssh: unsupported signature algorithm %s", algorithm)
+		}
+
+		switch key := s.pubKey.(type) {
+		case *dsaPublicKey:
+			hashFunc = crypto.SHA1
+		case *ecdsaPublicKey:
+			hashFunc = ecHash(key.Curve)
+		case ed25519PublicKey:
+		default:
+			return nil, fmt.Errorf("ssh: unsupported key type %T", key)
+		}
 	}
 
 	var digest []byte
@@ -744,7 +1041,7 @@ func (s *wrappedSigner) Sign(rand io.Reader, data []byte) (*Signature, error) {
 	}
 
 	return &Signature{
-		Format: s.pubKey.Type(),
+		Format: algorithm,
 		Blob:   signature,
 	}, nil
 }
@@ -764,14 +1061,18 @@ func NewPublicKey(key interface{}) (PublicKey, error) {
 	case *dsa.PublicKey:
 		return (*dsaPublicKey)(key), nil
 	case ed25519.PublicKey:
-		return (ed25519PublicKey)(key), nil
+		if l := len(key); l != ed25519.PublicKeySize {
+			return nil, fmt.Errorf("ssh: invalid size %d for Ed25519 public key", l)
+		}
+		return ed25519PublicKey(key), nil
 	default:
 		return nil, fmt.Errorf("ssh: unsupported key type %T", key)
 	}
 }
 
 // ParsePrivateKey returns a Signer from a PEM encoded private key. It supports
-// the same keys as ParseRawPrivateKey.
+// the same keys as ParseRawPrivateKey. If the private key is encrypted, it
+// will return a PassphraseMissingError.
 func ParsePrivateKey(pemBytes []byte) (Signer, error) {
 	key, err := ParseRawPrivateKey(pemBytes)
 	if err != nil {
@@ -784,8 +1085,8 @@ func ParsePrivateKey(pemBytes []byte) (Signer, error) {
 // ParsePrivateKeyWithPassphrase returns a Signer from a PEM encoded private
 // key and passphrase. It supports the same keys as
 // ParseRawPrivateKeyWithPassphrase.
-func ParsePrivateKeyWithPassphrase(pemBytes, passPhrase []byte) (Signer, error) {
-	key, err := ParseRawPrivateKeyWithPassphrase(pemBytes, passPhrase)
+func ParsePrivateKeyWithPassphrase(pemBytes, passphrase []byte) (Signer, error) {
+	key, err := ParseRawPrivateKeyWithPassphrase(pemBytes, passphrase)
 	if err != nil {
 		return nil, err
 	}
@@ -801,8 +1102,21 @@ func encryptedBlock(block *pem.Block) bool {
 	return strings.Contains(block.Headers["Proc-Type"], "ENCRYPTED")
 }
 
+// A PassphraseMissingError indicates that parsing this private key requires a
+// passphrase. Use ParsePrivateKeyWithPassphrase.
+type PassphraseMissingError struct {
+	// PublicKey will be set if the private key format includes an unencrypted
+	// public key along with the encrypted private key.
+	PublicKey PublicKey
+}
+
+func (*PassphraseMissingError) Error() string {
+	return "ssh: this private key is passphrase protected"
+}
+
 // ParseRawPrivateKey returns a private key from a PEM encoded private key. It
-// supports RSA (PKCS#1), PKCS#8, DSA (OpenSSL), and ECDSA private keys.
+// supports RSA (PKCS#1), PKCS#8, DSA (OpenSSL), and ECDSA private keys. If the
+// private key is encrypted, it will return a PassphraseMissingError.
 func ParseRawPrivateKey(pemBytes []byte) (interface{}, error) {
 	block, _ := pem.Decode(pemBytes)
 	if block == nil {
@@ -810,7 +1124,7 @@ func ParseRawPrivateKey(pemBytes []byte) (interface{}, error) {
 	}
 
 	if encryptedBlock(block) {
-		return nil, errors.New("ssh: cannot decode encrypted private keys")
+		return nil, &PassphraseMissingError{}
 	}
 
 	switch block.Type {
@@ -824,33 +1138,35 @@ func ParseRawPrivateKey(pemBytes []byte) (interface{}, error) {
 	case "DSA PRIVATE KEY":
 		return ParseDSAPrivateKey(block.Bytes)
 	case "OPENSSH PRIVATE KEY":
-		return parseOpenSSHPrivateKey(block.Bytes)
+		return parseOpenSSHPrivateKey(block.Bytes, unencryptedOpenSSHKey)
 	default:
 		return nil, fmt.Errorf("ssh: unsupported key type %q", block.Type)
 	}
 }
 
 // ParseRawPrivateKeyWithPassphrase returns a private key decrypted with
-// passphrase from a PEM encoded private key. If wrong passphrase, return
-// x509.IncorrectPasswordError.
-func ParseRawPrivateKeyWithPassphrase(pemBytes, passPhrase []byte) (interface{}, error) {
+// passphrase from a PEM encoded private key. If the passphrase is wrong, it
+// will return x509.IncorrectPasswordError.
+func ParseRawPrivateKeyWithPassphrase(pemBytes, passphrase []byte) (interface{}, error) {
 	block, _ := pem.Decode(pemBytes)
 	if block == nil {
 		return nil, errors.New("ssh: no key found")
 	}
-	buf := block.Bytes
 
-	if encryptedBlock(block) {
-		if x509.IsEncryptedPEMBlock(block) {
-			var err error
-			buf, err = x509.DecryptPEMBlock(block, passPhrase)
-			if err != nil {
-				if err == x509.IncorrectPasswordError {
-					return nil, err
-				}
-				return nil, fmt.Errorf("ssh: cannot decode encrypted private keys: %v", err)
-			}
+	if block.Type == "OPENSSH PRIVATE KEY" {
+		return parseOpenSSHPrivateKey(block.Bytes, passphraseProtectedOpenSSHKey(passphrase))
+	}
+
+	if !encryptedBlock(block) || !x509.IsEncryptedPEMBlock(block) {
+		return nil, errors.New("ssh: not an encrypted key")
+	}
+
+	buf, err := x509.DecryptPEMBlock(block, passphrase)
+	if err != nil {
+		if err == x509.IncorrectPasswordError {
+			return nil, err
 		}
+		return nil, fmt.Errorf("ssh: cannot decode encrypted private keys: %v", err)
 	}
 
 	switch block.Type {
@@ -860,8 +1176,6 @@ func ParseRawPrivateKeyWithPassphrase(pemBytes, passPhrase []byte) (interface{},
 		return x509.ParseECPrivateKey(buf)
 	case "DSA PRIVATE KEY":
 		return ParseDSAPrivateKey(buf)
-	case "OPENSSH PRIVATE KEY":
-		return parseOpenSSHPrivateKey(buf)
 	default:
 		return nil, fmt.Errorf("ssh: unsupported key type %q", block.Type)
 	}
@@ -899,9 +1213,68 @@ func ParseDSAPrivateKey(der []byte) (*dsa.PrivateKey, error) {
 	}, nil
 }
 
-// Implemented based on the documentation at
-// https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key
-func parseOpenSSHPrivateKey(key []byte) (crypto.PrivateKey, error) {
+func unencryptedOpenSSHKey(cipherName, kdfName, kdfOpts string, privKeyBlock []byte) ([]byte, error) {
+	if kdfName != "none" || cipherName != "none" {
+		return nil, &PassphraseMissingError{}
+	}
+	if kdfOpts != "" {
+		return nil, errors.New("ssh: invalid openssh private key")
+	}
+	return privKeyBlock, nil
+}
+
+func passphraseProtectedOpenSSHKey(passphrase []byte) openSSHDecryptFunc {
+	return func(cipherName, kdfName, kdfOpts string, privKeyBlock []byte) ([]byte, error) {
+		if kdfName == "none" || cipherName == "none" {
+			return nil, errors.New("ssh: key is not password protected")
+		}
+		if kdfName != "bcrypt" {
+			return nil, fmt.Errorf("ssh: unknown KDF %q, only supports %q", kdfName, "bcrypt")
+		}
+
+		var opts struct {
+			Salt   string
+			Rounds uint32
+		}
+		if err := Unmarshal([]byte(kdfOpts), &opts); err != nil {
+			return nil, err
+		}
+
+		k, err := bcrypt_pbkdf.Key(passphrase, []byte(opts.Salt), int(opts.Rounds), 32+16)
+		if err != nil {
+			return nil, err
+		}
+		key, iv := k[:32], k[32:]
+
+		c, err := aes.NewCipher(key)
+		if err != nil {
+			return nil, err
+		}
+		switch cipherName {
+		case "aes256-ctr":
+			ctr := cipher.NewCTR(c, iv)
+			ctr.XORKeyStream(privKeyBlock, privKeyBlock)
+		case "aes256-cbc":
+			if len(privKeyBlock)%c.BlockSize() != 0 {
+				return nil, fmt.Errorf("ssh: invalid encrypted private key length, not a multiple of the block size")
+			}
+			cbc := cipher.NewCBCDecrypter(c, iv)
+			cbc.CryptBlocks(privKeyBlock, privKeyBlock)
+		default:
+			return nil, fmt.Errorf("ssh: unknown cipher %q, only supports %q or %q", cipherName, "aes256-ctr", "aes256-cbc")
+		}
+
+		return privKeyBlock, nil
+	}
+}
+
+type openSSHDecryptFunc func(CipherName, KdfName, KdfOpts string, PrivKeyBlock []byte) ([]byte, error)
+
+// parseOpenSSHPrivateKey parses an OpenSSH private key, using the decrypt
+// function to unwrap the encrypted portion. unencryptedOpenSSHKey can be used
+// as the decrypt function to parse an unencrypted private key. See
+// https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key.
+func parseOpenSSHPrivateKey(key []byte, decrypt openSSHDecryptFunc) (crypto.PrivateKey, error) {
 	const magic = "openssh-key-v1\x00"
 	if len(key) < len(magic) || string(key[:len(magic)]) != magic {
 		return nil, errors.New("ssh: invalid openssh private key format")
@@ -920,9 +1293,22 @@ func parseOpenSSHPrivateKey(key []byte) (crypto.PrivateKey, error) {
 	if err := Unmarshal(remaining, &w); err != nil {
 		return nil, err
 	}
+	if w.NumKeys != 1 {
+		// We only support single key files, and so does OpenSSH.
+		// https://github.com/openssh/openssh-portable/blob/4103a3ec7/sshkey.c#L4171
+		return nil, errors.New("ssh: multi-key files are not supported")
+	}
 
-	if w.KdfName != "none" || w.CipherName != "none" {
-		return nil, errors.New("ssh: cannot decode encrypted private keys")
+	privKeyBlock, err := decrypt(w.CipherName, w.KdfName, w.KdfOpts, w.PrivKeyBlock)
+	if err != nil {
+		if err, ok := err.(*PassphraseMissingError); ok {
+			pub, errPub := ParsePublicKey(w.PubKey)
+			if errPub != nil {
+				return nil, fmt.Errorf("ssh: failed to parse embedded public key: %v", errPub)
+			}
+			err.PublicKey = pub
+		}
+		return nil, err
 	}
 
 	pk1 := struct {
@@ -932,15 +1318,13 @@ func parseOpenSSHPrivateKey(key []byte) (crypto.PrivateKey, error) {
 		Rest    []byte `ssh:"rest"`
 	}{}
 
-	if err := Unmarshal(w.PrivKeyBlock, &pk1); err != nil {
-		return nil, err
-	}
-
-	if pk1.Check1 != pk1.Check2 {
-		return nil, errors.New("ssh: checkint mismatch")
+	if err := Unmarshal(privKeyBlock, &pk1); err != nil || pk1.Check1 != pk1.Check2 {
+		if w.CipherName != "none" {
+			return nil, x509.IncorrectPasswordError
+		}
+		return nil, errors.New("ssh: malformed OpenSSH key")
 	}
 
-	// we only handle ed25519 and rsa keys currently
 	switch pk1.Keytype {
 	case KeyAlgoRSA:
 		// https://github.com/openssh/openssh-portable/blob/master/sshkey.c#L2760-L2773
@@ -959,10 +1343,8 @@ func parseOpenSSHPrivateKey(key []byte) (crypto.PrivateKey, error) {
 			return nil, err
 		}
 
-		for i, b := range key.Pad {
-			if int(b) != i+1 {
-				return nil, errors.New("ssh: padding not as expected")
-			}
+		if err := checkOpenSSHKeyPadding(key.Pad); err != nil {
+			return nil, err
 		}
 
 		pk := &rsa.PrivateKey{
@@ -997,20 +1379,78 @@ func parseOpenSSHPrivateKey(key []byte) (crypto.PrivateKey, error) {
 			return nil, errors.New("ssh: private key unexpected length")
 		}
 
-		for i, b := range key.Pad {
-			if int(b) != i+1 {
-				return nil, errors.New("ssh: padding not as expected")
-			}
+		if err := checkOpenSSHKeyPadding(key.Pad); err != nil {
+			return nil, err
 		}
 
 		pk := ed25519.PrivateKey(make([]byte, ed25519.PrivateKeySize))
 		copy(pk, key.Priv)
 		return &pk, nil
+	case KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521:
+		key := struct {
+			Curve   string
+			Pub     []byte
+			D       *big.Int
+			Comment string
+			Pad     []byte `ssh:"rest"`
+		}{}
+
+		if err := Unmarshal(pk1.Rest, &key); err != nil {
+			return nil, err
+		}
+
+		if err := checkOpenSSHKeyPadding(key.Pad); err != nil {
+			return nil, err
+		}
+
+		var curve elliptic.Curve
+		switch key.Curve {
+		case "nistp256":
+			curve = elliptic.P256()
+		case "nistp384":
+			curve = elliptic.P384()
+		case "nistp521":
+			curve = elliptic.P521()
+		default:
+			return nil, errors.New("ssh: unhandled elliptic curve: " + key.Curve)
+		}
+
+		X, Y := elliptic.Unmarshal(curve, key.Pub)
+		if X == nil || Y == nil {
+			return nil, errors.New("ssh: failed to unmarshal public key")
+		}
+
+		if key.D.Cmp(curve.Params().N) >= 0 {
+			return nil, errors.New("ssh: scalar is out of range")
+		}
+
+		x, y := curve.ScalarBaseMult(key.D.Bytes())
+		if x.Cmp(X) != 0 || y.Cmp(Y) != 0 {
+			return nil, errors.New("ssh: public key does not match private key")
+		}
+
+		return &ecdsa.PrivateKey{
+			PublicKey: ecdsa.PublicKey{
+				Curve: curve,
+				X:     X,
+				Y:     Y,
+			},
+			D: key.D,
+		}, nil
 	default:
 		return nil, errors.New("ssh: unhandled key type")
 	}
 }
 
+func checkOpenSSHKeyPadding(pad []byte) error {
+	for i, b := range pad {
+		if int(b) != i+1 {
+			return errors.New("ssh: padding not as expected")
+		}
+	}
+	return nil
+}
+
 // FingerprintLegacyMD5 returns the user presentation of the key's
 // fingerprint as described by RFC 4716 section 4.
 func FingerprintLegacyMD5(pubKey PublicKey) string {

+ 122 - 35
psiphon/common/crypto/ssh/keys_test.go

@@ -8,12 +8,12 @@ import (
 	"bytes"
 	"crypto/dsa"
 	"crypto/ecdsa"
-	"crypto/ed25519"
 	"crypto/elliptic"
 	"crypto/rand"
 	"crypto/rsa"
 	"crypto/x509"
 	"encoding/base64"
+	"encoding/hex"
 	"encoding/pem"
 	"fmt"
 	"io"
@@ -22,6 +22,7 @@ import (
 	"testing"
 
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/ssh/testdata"
+	"golang.org/x/crypto/ed25519"
 )
 
 func rawKey(pub PublicKey) interface{} {
@@ -109,6 +110,49 @@ func TestKeySignVerify(t *testing.T) {
 	}
 }
 
+func TestKeySignWithAlgorithmVerify(t *testing.T) {
+	for _, priv := range testSigners {
+		if algorithmSigner, ok := priv.(AlgorithmSigner); !ok {
+			t.Errorf("Signers constructed by ssh package should always implement the AlgorithmSigner interface: %T", priv)
+		} else {
+			pub := priv.PublicKey()
+			data := []byte("sign me")
+
+			signWithAlgTestCase := func(algorithm string, expectedAlg string) {
+				sig, err := algorithmSigner.SignWithAlgorithm(rand.Reader, data, algorithm)
+				if err != nil {
+					t.Fatalf("Sign(%T): %v", priv, err)
+				}
+				if sig.Format != expectedAlg {
+					t.Errorf("signature format did not match requested signature algorithm: %s != %s", sig.Format, expectedAlg)
+				}
+
+				if err := pub.Verify(data, sig); err != nil {
+					t.Errorf("publicKey.Verify(%T): %v", priv, err)
+				}
+				sig.Blob[5]++
+				if err := pub.Verify(data, sig); err == nil {
+					t.Errorf("publicKey.Verify on broken sig did not fail")
+				}
+			}
+
+			// Using the empty string as the algorithm name should result in the same signature format as the algorithm-free Sign method.
+			defaultSig, err := priv.Sign(rand.Reader, data)
+			if err != nil {
+				t.Fatalf("Sign(%T): %v", priv, err)
+			}
+			signWithAlgTestCase("", defaultSig.Format)
+
+			// RSA keys are the only ones which currently support more than one signing algorithm
+			if pub.Type() == KeyAlgoRSA {
+				for _, algorithm := range []string{SigAlgoRSA, SigAlgoRSASHA2256, SigAlgoRSASHA2512} {
+					signWithAlgTestCase(algorithm, algorithm)
+				}
+			}
+		}
+	}
+}
+
 func TestParseRSAPrivateKey(t *testing.T) {
 	key := testPrivateKeys["rsa"]
 
@@ -135,44 +179,45 @@ func TestParseECPrivateKey(t *testing.T) {
 	}
 }
 
-// See Issue https://github.com/golang/go/issues/6650.
-func TestParseEncryptedPrivateKeysFails(t *testing.T) {
-	const wantSubstring = "encrypted"
-	for i, tt := range testdata.PEMEncryptedKeys {
-		_, err := ParsePrivateKey(tt.PEMBytes)
-		if err == nil {
-			t.Errorf("#%d key %s: ParsePrivateKey successfully parsed, expected an error", i, tt.Name)
-			continue
-		}
-
-		if !strings.Contains(err.Error(), wantSubstring) {
-			t.Errorf("#%d key %s: got error %q, want substring %q", i, tt.Name, err, wantSubstring)
-		}
-	}
-}
-
-// Parse encrypted private keys with passphrase
 func TestParseEncryptedPrivateKeysWithPassphrase(t *testing.T) {
 	data := []byte("sign me")
 	for _, tt := range testdata.PEMEncryptedKeys {
-		s, err := ParsePrivateKeyWithPassphrase(tt.PEMBytes, []byte(tt.EncryptionKey))
-		if err != nil {
-			t.Fatalf("ParsePrivateKeyWithPassphrase returned error: %s", err)
-			continue
-		}
-		sig, err := s.Sign(rand.Reader, data)
-		if err != nil {
-			t.Fatalf("dsa.Sign: %v", err)
-		}
-		if err := s.PublicKey().Verify(data, sig); err != nil {
-			t.Errorf("Verify failed: %v", err)
-		}
-	}
+		t.Run(tt.Name, func(t *testing.T) {
+			_, err := ParsePrivateKeyWithPassphrase(tt.PEMBytes, []byte("incorrect"))
+			if err != x509.IncorrectPasswordError {
+				t.Errorf("got %v want IncorrectPasswordError", err)
+			}
 
-	tt := testdata.PEMEncryptedKeys[0]
-	_, err := ParsePrivateKeyWithPassphrase(tt.PEMBytes, []byte("incorrect"))
-	if err != x509.IncorrectPasswordError {
-		t.Fatalf("got %v want IncorrectPasswordError", err)
+			s, err := ParsePrivateKeyWithPassphrase(tt.PEMBytes, []byte(tt.EncryptionKey))
+			if err != nil {
+				t.Fatalf("ParsePrivateKeyWithPassphrase returned error: %s", err)
+			}
+
+			sig, err := s.Sign(rand.Reader, data)
+			if err != nil {
+				t.Fatalf("Signer.Sign: %v", err)
+			}
+			if err := s.PublicKey().Verify(data, sig); err != nil {
+				t.Errorf("Verify failed: %v", err)
+			}
+
+			_, err = ParsePrivateKey(tt.PEMBytes)
+			if err == nil {
+				t.Fatalf("ParsePrivateKey succeeded, expected an error")
+			}
+
+			if err, ok := err.(*PassphraseMissingError); !ok {
+				t.Errorf("got error %q, want PassphraseMissingError", err)
+			} else if tt.IncludesPublicKey {
+				if err.PublicKey == nil {
+					t.Fatalf("expected PassphraseMissingError.PublicKey not to be nil")
+				}
+				got, want := err.PublicKey.Marshal(), s.PublicKey().Marshal()
+				if !bytes.Equal(got, want) {
+					t.Errorf("error field %q doesn't match signer public key %q", got, want)
+				}
+			}
+		})
 	}
 }
 
@@ -529,3 +574,45 @@ func TestInvalidKeys(t *testing.T) {
 		}
 	}
 }
+
+func TestSKKeys(t *testing.T) {
+	for _, d := range testdata.SKData {
+		pk, _, _, _, err := ParseAuthorizedKey(d.PubKey)
+		if err != nil {
+			t.Fatalf("parseAuthorizedKey returned error: %v", err)
+		}
+
+		sigBuf := make([]byte, hex.DecodedLen(len(d.HexSignature)))
+		if _, err := hex.Decode(sigBuf, d.HexSignature); err != nil {
+			t.Fatalf("hex.Decode() failed: %v", err)
+		}
+
+		dataBuf := make([]byte, hex.DecodedLen(len(d.HexData)))
+		if _, err := hex.Decode(dataBuf, d.HexData); err != nil {
+			t.Fatalf("hex.Decode() failed: %v", err)
+		}
+
+		sig, _, ok := parseSignature(sigBuf)
+		if !ok {
+			t.Fatalf("parseSignature(%v) failed", sigBuf)
+		}
+
+		// Test that good data and signature pass verification
+		if err := pk.Verify(dataBuf, sig); err != nil {
+			t.Errorf("%s: PublicKey.Verify(%v, %v) failed: %v", d.Name, dataBuf, sig, err)
+		}
+
+		// Invalid data being passed in
+		invalidData := []byte("INVALID DATA")
+		if err := pk.Verify(invalidData, sig); err == nil {
+			t.Errorf("%s with invalid data: PublicKey.Verify(%v, %v) passed unexpectedly", d.Name, invalidData, sig)
+		}
+
+		// Change byte in blob to corrup signature
+		sig.Blob[5] = byte('A')
+		// Corrupted data being passed in
+		if err := pk.Verify(dataBuf, sig); err == nil {
+			t.Errorf("%s with corrupted signature: PublicKey.Verify(%v, %v) passed unexpectedly", d.Name, dataBuf, sig)
+		}
+	}
+}

+ 2 - 2
psiphon/common/crypto/ssh/knownhosts/knownhosts.go

@@ -350,8 +350,8 @@ func (db *hostKeyDB) check(address string, remote net.Addr, remoteKey ssh.Public
 	return db.checkAddr(hostToCheck, remoteKey)
 }
 
-// checkAddrs checks if we can find the given public key for any of
-// the given addresses.  If we only find an entry for the IP address,
+// checkAddr checks if we can find the given public key for the
+// given address.  If we only find an entry for the IP address,
 // or only the hostname, then this still succeeds.
 func (db *hostKeyDB) checkAddr(a addr, remoteKey ssh.PublicKey) error {
 	// TODO(hanwen): are these the right semantics? What if there

+ 100 - 0
psiphon/common/crypto/ssh/messages.go

@@ -97,6 +97,36 @@ type kexDHReplyMsg struct {
 	Signature []byte
 }
 
+// See RFC 4419, section 5.
+const msgKexDHGexGroup = 31
+
+type kexDHGexGroupMsg struct {
+	P *big.Int `sshtype:"31"`
+	G *big.Int
+}
+
+const msgKexDHGexInit = 32
+
+type kexDHGexInitMsg struct {
+	X *big.Int `sshtype:"32"`
+}
+
+const msgKexDHGexReply = 33
+
+type kexDHGexReplyMsg struct {
+	HostKey   []byte `sshtype:"33"`
+	Y         *big.Int
+	Signature []byte
+}
+
+const msgKexDHGexRequest = 34
+
+type kexDHGexRequestMsg struct {
+	MinBits      uint32 `sshtype:"34"`
+	PreferedBits uint32
+	MaxBits      uint32
+}
+
 // See RFC 4253, section 10.
 const msgServiceRequest = 5
 
@@ -275,6 +305,42 @@ type userAuthPubKeyOkMsg struct {
 	PubKey []byte
 }
 
+// See RFC 4462, section 3
+const msgUserAuthGSSAPIResponse = 60
+
+type userAuthGSSAPIResponse struct {
+	SupportMech []byte `sshtype:"60"`
+}
+
+const msgUserAuthGSSAPIToken = 61
+
+type userAuthGSSAPIToken struct {
+	Token []byte `sshtype:"61"`
+}
+
+const msgUserAuthGSSAPIMIC = 66
+
+type userAuthGSSAPIMIC struct {
+	MIC []byte `sshtype:"66"`
+}
+
+// See RFC 4462, section 3.9
+const msgUserAuthGSSAPIErrTok = 64
+
+type userAuthGSSAPIErrTok struct {
+	ErrorToken []byte `sshtype:"64"`
+}
+
+// See RFC 4462, section 3.8
+const msgUserAuthGSSAPIError = 65
+
+type userAuthGSSAPIError struct {
+	MajorStatus uint32 `sshtype:"65"`
+	MinorStatus uint32
+	Message     string
+	LanguageTag string
+}
+
 // typeTags returns the possible type bytes for the given reflect.Type, which
 // should be a struct. The possible values are separated by a '|' character.
 func typeTags(structType reflect.Type) (tags []byte) {
@@ -756,6 +822,14 @@ func decode(packet []byte) (interface{}, error) {
 		msg = new(channelRequestSuccessMsg)
 	case msgChannelFailure:
 		msg = new(channelRequestFailureMsg)
+	case msgUserAuthGSSAPIToken:
+		msg = new(userAuthGSSAPIToken)
+	case msgUserAuthGSSAPIMIC:
+		msg = new(userAuthGSSAPIMIC)
+	case msgUserAuthGSSAPIErrTok:
+		msg = new(userAuthGSSAPIErrTok)
+	case msgUserAuthGSSAPIError:
+		msg = new(userAuthGSSAPIError)
 	default:
 		return nil, unexpectedMessageError(0, packet[0])
 	}
@@ -764,3 +838,29 @@ func decode(packet []byte) (interface{}, error) {
 	}
 	return msg, nil
 }
+
+var packetTypeNames = map[byte]string{
+	msgDisconnect:          "disconnectMsg",
+	msgServiceRequest:      "serviceRequestMsg",
+	msgServiceAccept:       "serviceAcceptMsg",
+	msgKexInit:             "kexInitMsg",
+	msgKexDHInit:           "kexDHInitMsg",
+	msgKexDHReply:          "kexDHReplyMsg",
+	msgUserAuthRequest:     "userAuthRequestMsg",
+	msgUserAuthSuccess:     "userAuthSuccessMsg",
+	msgUserAuthFailure:     "userAuthFailureMsg",
+	msgUserAuthPubKeyOk:    "userAuthPubKeyOkMsg",
+	msgGlobalRequest:       "globalRequestMsg",
+	msgRequestSuccess:      "globalRequestSuccessMsg",
+	msgRequestFailure:      "globalRequestFailureMsg",
+	msgChannelOpen:         "channelOpenMsg",
+	msgChannelData:         "channelDataMsg",
+	msgChannelOpenConfirm:  "channelOpenConfirmMsg",
+	msgChannelOpenFailure:  "channelOpenFailureMsg",
+	msgChannelWindowAdjust: "windowAdjustMsg",
+	msgChannelEOF:          "channelEOFMsg",
+	msgChannelClose:        "channelCloseMsg",
+	msgChannelRequest:      "channelRequestMsg",
+	msgChannelSuccess:      "channelRequestSuccessMsg",
+	msgChannelFailure:      "channelRequestFailureMsg",
+}

+ 22 - 1
psiphon/common/crypto/ssh/mux.go

@@ -240,7 +240,7 @@ func (m *mux) onePacket() error {
 	id := binary.BigEndian.Uint32(packet[1:])
 	ch := m.chanList.getChan(id)
 	if ch == nil {
-		return fmt.Errorf("ssh: invalid channel %d", id)
+		return m.handleUnknownChannelPacket(id, packet)
 	}
 
 	return ch.handlePacket(packet)
@@ -328,3 +328,24 @@ func (m *mux) openChannel(chanType string, extra []byte) (*channel, error) {
 		return nil, fmt.Errorf("ssh: unexpected packet in response to channel open: %T", msg)
 	}
 }
+
+func (m *mux) handleUnknownChannelPacket(id uint32, packet []byte) error {
+	msg, err := decode(packet)
+	if err != nil {
+		return err
+	}
+
+	switch msg := msg.(type) {
+	// RFC 4254 section 5.4 says unrecognized channel requests should
+	// receive a failure response.
+	case *channelRequestMsg:
+		if msg.WantReply {
+			return m.sendMessage(channelRequestFailureMsg{
+				PeersID: msg.PeersID,
+			})
+		}
+		return nil
+	default:
+		return fmt.Errorf("ssh: invalid channel %d", id)
+	}
+}

+ 215 - 1
psiphon/common/crypto/ssh/mux_test.go

@@ -9,6 +9,7 @@ import (
 	"io/ioutil"
 	"sync"
 	"testing"
+	"time"
 )
 
 // PSIPHON
@@ -25,7 +26,7 @@ func muxPair() (*mux, *mux) {
 	return s, c
 }
 
-// Returns both ends of a channel, and the mux for the the 2nd
+// Returns both ends of a channel, and the mux for the 2nd
 // channel.
 func channelPair(t *testing.T) (*channel, *channel, *mux) {
 	c, s := muxPair()
@@ -293,6 +294,219 @@ func TestMuxChannelRequest(t *testing.T) {
 	}
 }
 
+func TestMuxUnknownChannelRequests(t *testing.T) {
+	clientPipe, serverPipe := memPipe()
+	client := newMux(clientPipe)
+	defer serverPipe.Close()
+	defer client.Close()
+
+	kDone := make(chan struct{})
+	go func() {
+		// Ignore unknown channel messages that don't want a reply.
+		err := serverPipe.writePacket(Marshal(channelRequestMsg{
+			PeersID:             1,
+			Request:             "keepalive@openssh.com",
+			WantReply:           false,
+			RequestSpecificData: []byte{},
+		}))
+		if err != nil {
+			t.Fatalf("send: %v", err)
+		}
+
+		// Send a keepalive, which should get a channel failure message
+		// in response.
+		err = serverPipe.writePacket(Marshal(channelRequestMsg{
+			PeersID:             2,
+			Request:             "keepalive@openssh.com",
+			WantReply:           true,
+			RequestSpecificData: []byte{},
+		}))
+		if err != nil {
+			t.Fatalf("send: %v", err)
+		}
+
+		packet, err := serverPipe.readPacket()
+		if err != nil {
+			t.Fatalf("read packet: %v", err)
+		}
+		decoded, err := decode(packet)
+		if err != nil {
+			t.Fatalf("decode failed: %v", err)
+		}
+
+		switch msg := decoded.(type) {
+		case *channelRequestFailureMsg:
+			if msg.PeersID != 2 {
+				t.Fatalf("received response to wrong message: %v", msg)
+			}
+		default:
+			t.Fatalf("unexpected channel message: %v", msg)
+		}
+
+		kDone <- struct{}{}
+
+		// Receive and respond to the keepalive to confirm the mux is
+		// still processing requests.
+		packet, err = serverPipe.readPacket()
+		if err != nil {
+			t.Fatalf("read packet: %v", err)
+		}
+		if packet[0] != msgGlobalRequest {
+			t.Fatalf("expected global request")
+		}
+
+		err = serverPipe.writePacket(Marshal(globalRequestFailureMsg{
+			Data: []byte{},
+		}))
+		if err != nil {
+			t.Fatalf("failed to send failure msg: %v", err)
+		}
+
+		close(kDone)
+	}()
+
+	// Wait for the server to send the keepalive message and receive back a
+	// response.
+	select {
+	case <-kDone:
+	case <-time.After(10 * time.Second):
+		t.Fatalf("server never received ack")
+	}
+
+	// Confirm client hasn't closed.
+	if _, _, err := client.SendRequest("keepalive@golang.org", true, nil); err != nil {
+		t.Fatalf("failed to send keepalive: %v", err)
+	}
+
+	select {
+	case <-kDone:
+	case <-time.After(10 * time.Second):
+		t.Fatalf("server never shut down")
+	}
+}
+
+func TestMuxClosedChannel(t *testing.T) {
+	clientPipe, serverPipe := memPipe()
+	client := newMux(clientPipe)
+	defer serverPipe.Close()
+	defer client.Close()
+
+	kDone := make(chan struct{})
+	go func() {
+		// Open the channel.
+		packet, err := serverPipe.readPacket()
+		if err != nil {
+			t.Fatalf("read packet: %v", err)
+		}
+		if packet[0] != msgChannelOpen {
+			t.Fatalf("expected chan open")
+		}
+
+		var openMsg channelOpenMsg
+		if err := Unmarshal(packet, &openMsg); err != nil {
+			t.Fatalf("unmarshal: %v", err)
+		}
+
+		// Send back the opened channel confirmation.
+		err = serverPipe.writePacket(Marshal(channelOpenConfirmMsg{
+			PeersID:       openMsg.PeersID,
+			MyID:          0,
+			MyWindow:      0,
+			MaxPacketSize: channelMaxPacket,
+		}))
+		if err != nil {
+			t.Fatalf("send: %v", err)
+		}
+
+		// Close the channel.
+		err = serverPipe.writePacket(Marshal(channelCloseMsg{
+			PeersID: openMsg.PeersID,
+		}))
+		if err != nil {
+			t.Fatalf("send: %v", err)
+		}
+
+		// Send a keepalive message on the channel we just closed.
+		err = serverPipe.writePacket(Marshal(channelRequestMsg{
+			PeersID:             openMsg.PeersID,
+			Request:             "keepalive@openssh.com",
+			WantReply:           true,
+			RequestSpecificData: []byte{},
+		}))
+		if err != nil {
+			t.Fatalf("send: %v", err)
+		}
+
+		// Receive the channel closed response.
+		packet, err = serverPipe.readPacket()
+		if err != nil {
+			t.Fatalf("read packet: %v", err)
+		}
+		if packet[0] != msgChannelClose {
+			t.Fatalf("expected channel close")
+		}
+
+		// Receive the keepalive response failure.
+		packet, err = serverPipe.readPacket()
+		if err != nil {
+			t.Fatalf("read packet: %v", err)
+		}
+		if packet[0] != msgChannelFailure {
+			t.Fatalf("expected channel close")
+		}
+		kDone <- struct{}{}
+
+		// Receive and respond to the keepalive to confirm the mux is
+		// still processing requests.
+		packet, err = serverPipe.readPacket()
+		if err != nil {
+			t.Fatalf("read packet: %v", err)
+		}
+		if packet[0] != msgGlobalRequest {
+			t.Fatalf("expected global request")
+		}
+
+		err = serverPipe.writePacket(Marshal(globalRequestFailureMsg{
+			Data: []byte{},
+		}))
+		if err != nil {
+			t.Fatalf("failed to send failure msg: %v", err)
+		}
+
+		close(kDone)
+	}()
+
+	// Open a channel.
+	ch, err := client.openChannel("chan", nil)
+	if err != nil {
+		t.Fatalf("OpenChannel: %v", err)
+	}
+	defer ch.Close()
+
+	// Wait for the server to close the channel and send the keepalive.
+	select {
+	case <-kDone:
+	case <-time.After(10 * time.Second):
+		t.Fatalf("server never received ack")
+	}
+
+	// Make sure the channel closed.
+	if _, ok := <-ch.incomingRequests; ok {
+		t.Fatalf("channel not closed")
+	}
+
+	// Confirm client hasn't closed
+	if _, _, err := client.SendRequest("keepalive@golang.org", true, nil); err != nil {
+		t.Fatalf("failed to send keepalive: %v", err)
+	}
+
+	select {
+	case <-kDone:
+	case <-time.After(10 * time.Second):
+		t.Fatalf("server never shut down")
+	}
+}
+
 func TestMuxGlobalRequest(t *testing.T) {
 	clientMux, serverMux := muxPair()
 	defer serverMux.Close()

+ 5 - 5
psiphon/common/crypto/ssh/randomized_kex_test.go

@@ -24,11 +24,11 @@ import (
 	"context"
 	"crypto/rand"
 	"crypto/rsa"
-	"errors"
 	"net"
 	"testing"
 
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/prng"
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
 	"golang.org/x/sync/errgroup"
 )
 
@@ -79,7 +79,7 @@ func TestRandomizedSSHKEXes(t *testing.T) {
 				certChecker := &CertChecker{
 					HostKeyFallback: func(addr string, remote net.Addr, key PublicKey) error {
 						if !bytes.Equal(publicKey.Marshal(), key.Marshal()) {
-							return errors.New("unexpected host public key")
+							return errors.TraceNew("unexpected host public key")
 						}
 						return nil
 					},
@@ -99,7 +99,7 @@ func TestRandomizedSSHKEXes(t *testing.T) {
 
 				clientSSHConn, _, _, err := NewClientConn(clientConn, "", clientConfig)
 				if err != nil {
-					return err
+					return errors.Trace(err)
 				}
 
 				clientSSHConn.Close()
@@ -115,7 +115,7 @@ func TestRandomizedSSHKEXes(t *testing.T) {
 					if c.User() == username && string(pass) == password {
 						return nil, nil
 					}
-					return nil, errors.New("authentication failed")
+					return nil, errors.TraceNew("authentication failed")
 				}
 
 				serverConfig := &ServerConfig{
@@ -127,7 +127,7 @@ func TestRandomizedSSHKEXes(t *testing.T) {
 
 				serverSSHConn, _, _, err := NewServerConn(serverConn, serverConfig)
 				if err != nil {
-					return err
+					return errors.Trace(err)
 				}
 
 				serverSSHConn.Close()

+ 131 - 4
psiphon/common/crypto/ssh/server.go

@@ -45,6 +45,20 @@ type Permissions struct {
 	Extensions map[string]string
 }
 
+type GSSAPIWithMICConfig struct {
+	// AllowLogin, must be set, is called when gssapi-with-mic
+	// authentication is selected (RFC 4462 section 3). The srcName is from the
+	// results of the GSS-API authentication. The format is username@DOMAIN.
+	// GSSAPI just guarantees to the server who the user is, but not if they can log in, and with what permissions.
+	// This callback is called after the user identity is established with GSSAPI to decide if the user can login with
+	// which permissions. If the user is allowed to login, it should return a nil error.
+	AllowLogin func(conn ConnMetadata, srcName string) (*Permissions, error)
+
+	// Server must be set. It's the implementation
+	// of the GSSAPIServer interface. See GSSAPIServer interface for details.
+	Server GSSAPIServer
+}
+
 // ServerConfig holds server specific configuration data.
 type ServerConfig struct {
 	// Config contains configuration shared between client and server.
@@ -99,6 +113,10 @@ type ServerConfig struct {
 	// BannerCallback, if present, is called and the return string is sent to
 	// the client after key exchange completed but before authentication.
 	BannerCallback func(conn ConnMetadata) string
+
+	// GSSAPIWithMICConfig includes gssapi server and callback, which if both non-nil, is used
+	// when gssapi-with-mic authentication is selected (RFC 4462 section 3).
+	GSSAPIWithMICConfig *GSSAPIWithMICConfig
 }
 
 // AddHostKey adds a private key as a host key. If an existing host
@@ -175,6 +193,12 @@ func NewServerConn(c net.Conn, config *ServerConfig) (*ServerConn, <-chan NewCha
 	if fullConf.MaxAuthTries == 0 {
 		fullConf.MaxAuthTries = 6
 	}
+	// Check if the config contains any unsupported key exchanges
+	for _, kex := range fullConf.KeyExchanges {
+		if _, ok := serverForbiddenKexAlgos[kex]; ok {
+			return nil, nil, nil, fmt.Errorf("ssh: unsupported key exchange %s for server", kex)
+		}
+	}
 
 	s := &connection{
 		sshConn: sshConn{conn: c},
@@ -204,7 +228,9 @@ func (s *connection) serverHandshake(config *ServerConfig) (*Permissions, error)
 		return nil, errors.New("ssh: server has no host keys")
 	}
 
-	if !config.NoClientAuth && config.PasswordCallback == nil && config.PublicKeyCallback == nil && config.KeyboardInteractiveCallback == nil {
+	if !config.NoClientAuth && config.PasswordCallback == nil && config.PublicKeyCallback == nil &&
+		config.KeyboardInteractiveCallback == nil && (config.GSSAPIWithMICConfig == nil ||
+		config.GSSAPIWithMICConfig.AllowLogin == nil || config.GSSAPIWithMICConfig.Server == nil) {
 		return nil, errors.New("ssh: no authentication methods configured but NoClientAuth is also false")
 	}
 
@@ -258,8 +284,8 @@ func (s *connection) serverHandshake(config *ServerConfig) (*Permissions, error)
 
 func isAcceptableAlgo(algo string) bool {
 	switch algo {
-	case KeyAlgoRSA, KeyAlgoDSA, KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521, KeyAlgoED25519,
-		CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoED25519v01:
+	case KeyAlgoRSA, KeyAlgoDSA, KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521, KeyAlgoSKECDSA256, KeyAlgoED25519, KeyAlgoSKED25519,
+		CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoSKECDSA256v01, CertAlgoED25519v01, CertAlgoSKED25519v01:
 		return true
 	}
 	return false
@@ -295,6 +321,55 @@ func checkSourceAddress(addr net.Addr, sourceAddrs string) error {
 	return fmt.Errorf("ssh: remote address %v is not allowed because of source-address restriction", addr)
 }
 
+func gssExchangeToken(gssapiConfig *GSSAPIWithMICConfig, firstToken []byte, s *connection,
+	sessionID []byte, userAuthReq userAuthRequestMsg) (authErr error, perms *Permissions, err error) {
+	gssAPIServer := gssapiConfig.Server
+	defer gssAPIServer.DeleteSecContext()
+	var srcName string
+	for {
+		var (
+			outToken     []byte
+			needContinue bool
+		)
+		outToken, srcName, needContinue, err = gssAPIServer.AcceptSecContext(firstToken)
+		if err != nil {
+			return err, nil, nil
+		}
+		if len(outToken) != 0 {
+			if err := s.transport.writePacket(Marshal(&userAuthGSSAPIToken{
+				Token: outToken,
+			})); err != nil {
+				return nil, nil, err
+			}
+		}
+		if !needContinue {
+			break
+		}
+		packet, err := s.transport.readPacket()
+		if err != nil {
+			return nil, nil, err
+		}
+		userAuthGSSAPITokenReq := &userAuthGSSAPIToken{}
+		if err := Unmarshal(packet, userAuthGSSAPITokenReq); err != nil {
+			return nil, nil, err
+		}
+	}
+	packet, err := s.transport.readPacket()
+	if err != nil {
+		return nil, nil, err
+	}
+	userAuthGSSAPIMICReq := &userAuthGSSAPIMIC{}
+	if err := Unmarshal(packet, userAuthGSSAPIMICReq); err != nil {
+		return nil, nil, err
+	}
+	mic := buildMIC(string(sessionID), userAuthReq.User, userAuthReq.Service, userAuthReq.Method)
+	if err := gssAPIServer.VerifyMIC(mic, userAuthGSSAPIMICReq.MIC); err != nil {
+		return err, nil, nil
+	}
+	perms, authErr = gssapiConfig.AllowLogin(s, srcName)
+	return authErr, perms, nil
+}
+
 // ServerAuthError represents server authentication errors and is
 // sometimes returned by NewServerConn. It appends any authentication
 // errors that may occur, and is returned if all of the authentication
@@ -404,7 +479,7 @@ userAuthLoop:
 			perms, authErr = config.PasswordCallback(s, password)
 		case "keyboard-interactive":
 			if config.KeyboardInteractiveCallback == nil {
-				authErr = errors.New("ssh: keyboard-interactive auth not configubred")
+				authErr = errors.New("ssh: keyboard-interactive auth not configured")
 				break
 			}
 
@@ -484,6 +559,7 @@ userAuthLoop:
 				// sig.Format.  This is usually the same, but
 				// for certs, the names differ.
 				if !isAcceptableAlgo(sig.Format) {
+					authErr = fmt.Errorf("ssh: algorithm %q not accepted", sig.Format)
 					break
 				}
 				signedData := buildDataSignedForAuth(sessionID, userAuthReq, algoBytes, pubKeyData)
@@ -495,6 +571,53 @@ userAuthLoop:
 				authErr = candidate.result
 				perms = candidate.perms
 			}
+		case "gssapi-with-mic":
+			if config.GSSAPIWithMICConfig == nil {
+				authErr = errors.New("ssh: gssapi-with-mic auth not configured")
+				break
+			}
+			gssapiConfig := config.GSSAPIWithMICConfig
+			userAuthRequestGSSAPI, err := parseGSSAPIPayload(userAuthReq.Payload)
+			if err != nil {
+				return nil, parseError(msgUserAuthRequest)
+			}
+			// OpenSSH supports Kerberos V5 mechanism only for GSS-API authentication.
+			if userAuthRequestGSSAPI.N == 0 {
+				authErr = fmt.Errorf("ssh: Mechanism negotiation is not supported")
+				break
+			}
+			var i uint32
+			present := false
+			for i = 0; i < userAuthRequestGSSAPI.N; i++ {
+				if userAuthRequestGSSAPI.OIDS[i].Equal(krb5Mesh) {
+					present = true
+					break
+				}
+			}
+			if !present {
+				authErr = fmt.Errorf("ssh: GSSAPI authentication must use the Kerberos V5 mechanism")
+				break
+			}
+			// Initial server response, see RFC 4462 section 3.3.
+			if err := s.transport.writePacket(Marshal(&userAuthGSSAPIResponse{
+				SupportMech: krb5OID,
+			})); err != nil {
+				return nil, err
+			}
+			// Exchange token, see RFC 4462 section 3.4.
+			packet, err := s.transport.readPacket()
+			if err != nil {
+				return nil, err
+			}
+			userAuthGSSAPITokenReq := &userAuthGSSAPIToken{}
+			if err := Unmarshal(packet, userAuthGSSAPITokenReq); err != nil {
+				return nil, err
+			}
+			authErr, perms, err = gssExchangeToken(gssapiConfig, userAuthGSSAPITokenReq.Token, s, sessionID,
+				userAuthReq)
+			if err != nil {
+				return nil, err
+			}
 		default:
 			authErr = fmt.Errorf("ssh: unknown method %q", userAuthReq.Method)
 		}
@@ -521,6 +644,10 @@ userAuthLoop:
 		if config.KeyboardInteractiveCallback != nil {
 			failureMsg.Methods = append(failureMsg.Methods, "keyboard-interactive")
 		}
+		if config.GSSAPIWithMICConfig != nil && config.GSSAPIWithMICConfig.Server != nil &&
+			config.GSSAPIWithMICConfig.AllowLogin != nil {
+			failureMsg.Methods = append(failureMsg.Methods, "gssapi-with-mic")
+		}
 
 		if len(failureMsg.Methods) == 0 {
 			return nil, errors.New("ssh: no authentication methods configured but NoClientAuth is also false")

+ 7 - 5
psiphon/common/crypto/ssh/session_test.go

@@ -35,7 +35,7 @@ func dial(handler serverType, t *testing.T) *Client {
 		}
 		conf.AddHostKey(testSigners["rsa"])
 
-		_, chans, reqs, err := NewServerConn(c1, &conf)
+		conn, chans, reqs, err := NewServerConn(c1, &conf)
 		if err != nil {
 			t.Fatalf("Unable to handshake: %v", err)
 		}
@@ -56,6 +56,9 @@ func dial(handler serverType, t *testing.T) *Client {
 				handler(ch, inReqs, t)
 			}()
 		}
+		if err := conn.Wait(); err != io.EOF {
+			t.Logf("server exit reason: %v", err)
+		}
 	}()
 
 	config := &ClientConfig{
@@ -358,10 +361,9 @@ func TestServerWindow(t *testing.T) {
 	}
 	written, err := copyNRandomly("stdin", serverStdin, origBuf, windowTestBytes)
 	if err != nil {
-		t.Fatalf("failed to copy origBuf to serverStdin: %v", err)
-	}
-	if written != windowTestBytes {
-		t.Fatalf("Wrote only %d of %d bytes to server", written, windowTestBytes)
+		t.Errorf("failed to copy origBuf to serverStdin: %v", err)
+	} else if written != windowTestBytes {
+		t.Errorf("Wrote only %d of %d bytes to server", written, windowTestBytes)
 	}
 
 	echoedBytes := <-result

+ 139 - 0
psiphon/common/crypto/ssh/ssh_gss.go

@@ -0,0 +1,139 @@
+// Copyright 2011 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 ssh
+
+import (
+	"encoding/asn1"
+	"errors"
+)
+
+var krb5OID []byte
+
+func init() {
+	krb5OID, _ = asn1.Marshal(krb5Mesh)
+}
+
+// GSSAPIClient provides the API to plug-in GSSAPI authentication for client logins.
+type GSSAPIClient interface {
+	// InitSecContext initiates the establishment of a security context for GSS-API between the
+	// ssh client and ssh server. Initially the token parameter should be specified as nil.
+	// The routine may return a outputToken which should be transferred to
+	// the ssh server, where the ssh server will present it to
+	// AcceptSecContext. If no token need be sent, InitSecContext will indicate this by setting
+	// needContinue to false. To complete the context
+	// establishment, one or more reply tokens may be required from the ssh
+	// server;if so, InitSecContext will return a needContinue which is true.
+	// In this case, InitSecContext should be called again when the
+	// reply token is received from the ssh server, passing the reply
+	// token to InitSecContext via the token parameters.
+	// See RFC 2743 section 2.2.1 and RFC 4462 section 3.4.
+	InitSecContext(target string, token []byte, isGSSDelegCreds bool) (outputToken []byte, needContinue bool, err error)
+	// GetMIC generates a cryptographic MIC for the SSH2 message, and places
+	// the MIC in a token for transfer to the ssh server.
+	// The contents of the MIC field are obtained by calling GSS_GetMIC()
+	// over the following, using the GSS-API context that was just
+	// established:
+	//  string    session identifier
+	//  byte      SSH_MSG_USERAUTH_REQUEST
+	//  string    user name
+	//  string    service
+	//  string    "gssapi-with-mic"
+	// See RFC 2743 section 2.3.1 and RFC 4462 3.5.
+	GetMIC(micFiled []byte) ([]byte, error)
+	// Whenever possible, it should be possible for
+	// DeleteSecContext() calls to be successfully processed even
+	// if other calls cannot succeed, thereby enabling context-related
+	// resources to be released.
+	// In addition to deleting established security contexts,
+	// gss_delete_sec_context must also be able to delete "half-built"
+	// security contexts resulting from an incomplete sequence of
+	// InitSecContext()/AcceptSecContext() calls.
+	// See RFC 2743 section 2.2.3.
+	DeleteSecContext() error
+}
+
+// GSSAPIServer provides the API to plug in GSSAPI authentication for server logins.
+type GSSAPIServer interface {
+	// AcceptSecContext allows a remotely initiated security context between the application
+	// and a remote peer to be established by the ssh client. The routine may return a
+	// outputToken which should be transferred to the ssh client,
+	// where the ssh client will present it to InitSecContext.
+	// If no token need be sent, AcceptSecContext will indicate this
+	// by setting the needContinue to false. To
+	// complete the context establishment, one or more reply tokens may be
+	// required from the ssh client. if so, AcceptSecContext
+	// will return a needContinue which is true, in which case it
+	// should be called again when the reply token is received from the ssh
+	// client, passing the token to AcceptSecContext via the
+	// token parameters.
+	// The srcName return value is the authenticated username.
+	// See RFC 2743 section 2.2.2 and RFC 4462 section 3.4.
+	AcceptSecContext(token []byte) (outputToken []byte, srcName string, needContinue bool, err error)
+	// VerifyMIC verifies that a cryptographic MIC, contained in the token parameter,
+	// fits the supplied message is received from the ssh client.
+	// See RFC 2743 section 2.3.2.
+	VerifyMIC(micField []byte, micToken []byte) error
+	// Whenever possible, it should be possible for
+	// DeleteSecContext() calls to be successfully processed even
+	// if other calls cannot succeed, thereby enabling context-related
+	// resources to be released.
+	// In addition to deleting established security contexts,
+	// gss_delete_sec_context must also be able to delete "half-built"
+	// security contexts resulting from an incomplete sequence of
+	// InitSecContext()/AcceptSecContext() calls.
+	// See RFC 2743 section 2.2.3.
+	DeleteSecContext() error
+}
+
+var (
+	// OpenSSH supports Kerberos V5 mechanism only for GSS-API authentication,
+	// so we also support the krb5 mechanism only.
+	// See RFC 1964 section 1.
+	krb5Mesh = asn1.ObjectIdentifier{1, 2, 840, 113554, 1, 2, 2}
+)
+
+// The GSS-API authentication method is initiated when the client sends an SSH_MSG_USERAUTH_REQUEST
+// See RFC 4462 section 3.2.
+type userAuthRequestGSSAPI struct {
+	N    uint32
+	OIDS []asn1.ObjectIdentifier
+}
+
+func parseGSSAPIPayload(payload []byte) (*userAuthRequestGSSAPI, error) {
+	n, rest, ok := parseUint32(payload)
+	if !ok {
+		return nil, errors.New("parse uint32 failed")
+	}
+	s := &userAuthRequestGSSAPI{
+		N:    n,
+		OIDS: make([]asn1.ObjectIdentifier, n),
+	}
+	for i := 0; i < int(n); i++ {
+		var (
+			desiredMech []byte
+			err         error
+		)
+		desiredMech, rest, ok = parseString(rest)
+		if !ok {
+			return nil, errors.New("parse string failed")
+		}
+		if rest, err = asn1.Unmarshal(desiredMech, &s.OIDS[i]); err != nil {
+			return nil, err
+		}
+
+	}
+	return s, nil
+}
+
+// See RFC 4462 section 3.6.
+func buildMIC(sessionID string, username string, service string, authMethod string) []byte {
+	out := make([]byte, 0, 0)
+	out = appendString(out, sessionID)
+	out = append(out, msgUserAuthRequest)
+	out = appendString(out, username)
+	out = appendString(out, service)
+	out = appendString(out, authMethod)
+	return out
+}

+ 109 - 0
psiphon/common/crypto/ssh/ssh_gss_test.go

@@ -0,0 +1,109 @@
+package ssh
+
+import (
+	"fmt"
+	"testing"
+)
+
+func TestParseGSSAPIPayload(t *testing.T) {
+	payload := []byte{0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0b, 0x06, 0x09,
+		0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x01, 0x02, 0x02}
+	res, err := parseGSSAPIPayload(payload)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if ok := res.OIDS[0].Equal(krb5Mesh); !ok {
+		t.Fatalf("got %v, want %v", res, krb5Mesh)
+	}
+}
+
+func TestBuildMIC(t *testing.T) {
+	sessionID := []byte{134, 180, 134, 194, 62, 145, 171, 82, 119, 149, 254, 196, 125, 173, 177, 145, 187, 85, 53,
+		183, 44, 150, 219, 129, 166, 195, 19, 33, 209, 246, 175, 121}
+	username := "testuser"
+	service := "ssh-connection"
+	authMethod := "gssapi-with-mic"
+	expected := []byte{0, 0, 0, 32, 134, 180, 134, 194, 62, 145, 171, 82, 119, 149, 254, 196, 125, 173, 177, 145, 187, 85, 53, 183, 44, 150, 219, 129, 166, 195, 19, 33, 209, 246, 175, 121, 50, 0, 0, 0, 8, 116, 101, 115, 116, 117, 115, 101, 114, 0, 0, 0, 14, 115, 115, 104, 45, 99, 111, 110, 110, 101, 99, 116, 105, 111, 110, 0, 0, 0, 15, 103, 115, 115, 97, 112, 105, 45, 119, 105, 116, 104, 45, 109, 105, 99}
+	result := buildMIC(string(sessionID), username, service, authMethod)
+	if string(result) != string(expected) {
+		t.Fatalf("buildMic: got %v, want %v", result, expected)
+	}
+}
+
+type exchange struct {
+	outToken      string
+	expectedToken string
+}
+
+type FakeClient struct {
+	exchanges []*exchange
+	round     int
+	mic       []byte
+	maxRound  int
+}
+
+func (f *FakeClient) InitSecContext(target string, token []byte, isGSSDelegCreds bool) (outputToken []byte, needContinue bool, err error) {
+	if token == nil {
+		if f.exchanges[f.round].expectedToken != "" {
+			err = fmt.Errorf("got empty token, want %q", f.exchanges[f.round].expectedToken)
+		} else {
+			outputToken = []byte(f.exchanges[f.round].outToken)
+		}
+	} else {
+		if string(token) != string(f.exchanges[f.round].expectedToken) {
+			err = fmt.Errorf("got %q, want token %q", token, f.exchanges[f.round].expectedToken)
+		} else {
+			outputToken = []byte(f.exchanges[f.round].outToken)
+		}
+	}
+	f.round++
+	needContinue = f.round < f.maxRound
+	return
+}
+
+func (f *FakeClient) GetMIC(micField []byte) ([]byte, error) {
+	return f.mic, nil
+}
+
+func (f *FakeClient) DeleteSecContext() error {
+	return nil
+}
+
+type FakeServer struct {
+	exchanges   []*exchange
+	round       int
+	expectedMIC []byte
+	srcName     string
+	maxRound    int
+}
+
+func (f *FakeServer) AcceptSecContext(token []byte) (outputToken []byte, srcName string, needContinue bool, err error) {
+	if token == nil {
+		if f.exchanges[f.round].expectedToken != "" {
+			err = fmt.Errorf("got empty token, want %q", f.exchanges[f.round].expectedToken)
+		} else {
+			outputToken = []byte(f.exchanges[f.round].outToken)
+		}
+	} else {
+		if string(token) != string(f.exchanges[f.round].expectedToken) {
+			err = fmt.Errorf("got %q, want token %q", token, f.exchanges[f.round].expectedToken)
+		} else {
+			outputToken = []byte(f.exchanges[f.round].outToken)
+		}
+	}
+	f.round++
+	needContinue = f.round < f.maxRound
+	srcName = f.srcName
+	return
+}
+
+func (f *FakeServer) VerifyMIC(micField []byte, micToken []byte) error {
+	if string(micToken) != string(f.expectedMIC) {
+		return fmt.Errorf("got MICToken %q, want %q", micToken, f.expectedMIC)
+	}
+	return nil
+}
+
+func (f *FakeServer) DeleteSecContext() error {
+	return nil
+}

+ 38 - 913
psiphon/common/crypto/ssh/terminal/terminal.go

@@ -2,950 +2,75 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// Package terminal provides support functions for dealing with terminals, as
+// commonly found on UNIX systems.
+//
+// Deprecated: this package moved to golang.org/x/term.
 package terminal
 
 import (
-	"bytes"
 	"io"
-	"sync"
-	"unicode/utf8"
+
+	"golang.org/x/term"
 )
 
 // EscapeCodes contains escape sequences that can be written to the terminal in
 // order to achieve different styles of text.
-type EscapeCodes struct {
-	// Foreground colors
-	Black, Red, Green, Yellow, Blue, Magenta, Cyan, White []byte
-
-	// Reset all attributes
-	Reset []byte
-}
-
-var vt100EscapeCodes = EscapeCodes{
-	Black:   []byte{keyEscape, '[', '3', '0', 'm'},
-	Red:     []byte{keyEscape, '[', '3', '1', 'm'},
-	Green:   []byte{keyEscape, '[', '3', '2', 'm'},
-	Yellow:  []byte{keyEscape, '[', '3', '3', 'm'},
-	Blue:    []byte{keyEscape, '[', '3', '4', 'm'},
-	Magenta: []byte{keyEscape, '[', '3', '5', 'm'},
-	Cyan:    []byte{keyEscape, '[', '3', '6', 'm'},
-	White:   []byte{keyEscape, '[', '3', '7', 'm'},
-
-	Reset: []byte{keyEscape, '[', '0', 'm'},
-}
+type EscapeCodes = term.EscapeCodes
 
 // Terminal contains the state for running a VT100 terminal that is capable of
 // reading lines of input.
-type Terminal struct {
-	// AutoCompleteCallback, if non-null, is called for each keypress with
-	// the full input line and the current position of the cursor (in
-	// bytes, as an index into |line|). If it returns ok=false, the key
-	// press is processed normally. Otherwise it returns a replacement line
-	// and the new cursor position.
-	AutoCompleteCallback func(line string, pos int, key rune) (newLine string, newPos int, ok bool)
-
-	// Escape contains a pointer to the escape codes for this terminal.
-	// It's always a valid pointer, although the escape codes themselves
-	// may be empty if the terminal doesn't support them.
-	Escape *EscapeCodes
-
-	// lock protects the terminal and the state in this object from
-	// concurrent processing of a key press and a Write() call.
-	lock sync.Mutex
-
-	c      io.ReadWriter
-	prompt []rune
-
-	// line is the current line being entered.
-	line []rune
-	// pos is the logical position of the cursor in line
-	pos int
-	// echo is true if local echo is enabled
-	echo bool
-	// pasteActive is true iff there is a bracketed paste operation in
-	// progress.
-	pasteActive bool
-
-	// cursorX contains the current X value of the cursor where the left
-	// edge is 0. cursorY contains the row number where the first row of
-	// the current line is 0.
-	cursorX, cursorY int
-	// maxLine is the greatest value of cursorY so far.
-	maxLine int
-
-	termWidth, termHeight int
-
-	// outBuf contains the terminal data to be sent.
-	outBuf []byte
-	// remainder contains the remainder of any partial key sequences after
-	// a read. It aliases into inBuf.
-	remainder []byte
-	inBuf     [256]byte
-
-	// history contains previously entered commands so that they can be
-	// accessed with the up and down keys.
-	history stRingBuffer
-	// historyIndex stores the currently accessed history entry, where zero
-	// means the immediately previous entry.
-	historyIndex int
-	// When navigating up and down the history it's possible to return to
-	// the incomplete, initial line. That value is stored in
-	// historyPending.
-	historyPending string
-}
+type Terminal = term.Terminal
 
 // NewTerminal runs a VT100 terminal on the given ReadWriter. If the ReadWriter is
 // a local terminal, that terminal must first have been put into raw mode.
 // prompt is a string that is written at the start of each input line (i.e.
 // "> ").
 func NewTerminal(c io.ReadWriter, prompt string) *Terminal {
-	return &Terminal{
-		Escape:       &vt100EscapeCodes,
-		c:            c,
-		prompt:       []rune(prompt),
-		termWidth:    80,
-		termHeight:   24,
-		echo:         true,
-		historyIndex: -1,
-	}
-}
-
-const (
-	keyCtrlD     = 4
-	keyCtrlU     = 21
-	keyEnter     = '\r'
-	keyEscape    = 27
-	keyBackspace = 127
-	keyUnknown   = 0xd800 /* UTF-16 surrogate area */ + iota
-	keyUp
-	keyDown
-	keyLeft
-	keyRight
-	keyAltLeft
-	keyAltRight
-	keyHome
-	keyEnd
-	keyDeleteWord
-	keyDeleteLine
-	keyClearScreen
-	keyPasteStart
-	keyPasteEnd
-)
-
-var (
-	crlf       = []byte{'\r', '\n'}
-	pasteStart = []byte{keyEscape, '[', '2', '0', '0', '~'}
-	pasteEnd   = []byte{keyEscape, '[', '2', '0', '1', '~'}
-)
-
-// bytesToKey tries to parse a key sequence from b. If successful, it returns
-// the key and the remainder of the input. Otherwise it returns utf8.RuneError.
-func bytesToKey(b []byte, pasteActive bool) (rune, []byte) {
-	if len(b) == 0 {
-		return utf8.RuneError, nil
-	}
-
-	if !pasteActive {
-		switch b[0] {
-		case 1: // ^A
-			return keyHome, b[1:]
-		case 5: // ^E
-			return keyEnd, b[1:]
-		case 8: // ^H
-			return keyBackspace, b[1:]
-		case 11: // ^K
-			return keyDeleteLine, b[1:]
-		case 12: // ^L
-			return keyClearScreen, b[1:]
-		case 23: // ^W
-			return keyDeleteWord, b[1:]
-		}
-	}
-
-	if b[0] != keyEscape {
-		if !utf8.FullRune(b) {
-			return utf8.RuneError, b
-		}
-		r, l := utf8.DecodeRune(b)
-		return r, b[l:]
-	}
-
-	if !pasteActive && len(b) >= 3 && b[0] == keyEscape && b[1] == '[' {
-		switch b[2] {
-		case 'A':
-			return keyUp, b[3:]
-		case 'B':
-			return keyDown, b[3:]
-		case 'C':
-			return keyRight, b[3:]
-		case 'D':
-			return keyLeft, b[3:]
-		case 'H':
-			return keyHome, b[3:]
-		case 'F':
-			return keyEnd, b[3:]
-		}
-	}
-
-	if !pasteActive && len(b) >= 6 && b[0] == keyEscape && b[1] == '[' && b[2] == '1' && b[3] == ';' && b[4] == '3' {
-		switch b[5] {
-		case 'C':
-			return keyAltRight, b[6:]
-		case 'D':
-			return keyAltLeft, b[6:]
-		}
-	}
-
-	if !pasteActive && len(b) >= 6 && bytes.Equal(b[:6], pasteStart) {
-		return keyPasteStart, b[6:]
-	}
-
-	if pasteActive && len(b) >= 6 && bytes.Equal(b[:6], pasteEnd) {
-		return keyPasteEnd, b[6:]
-	}
-
-	// If we get here then we have a key that we don't recognise, or a
-	// partial sequence. It's not clear how one should find the end of a
-	// sequence without knowing them all, but it seems that [a-zA-Z~] only
-	// appears at the end of a sequence.
-	for i, c := range b[0:] {
-		if c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '~' {
-			return keyUnknown, b[i+1:]
-		}
-	}
-
-	return utf8.RuneError, b
-}
-
-// queue appends data to the end of t.outBuf
-func (t *Terminal) queue(data []rune) {
-	t.outBuf = append(t.outBuf, []byte(string(data))...)
-}
-
-var eraseUnderCursor = []rune{' ', keyEscape, '[', 'D'}
-var space = []rune{' '}
-
-func isPrintable(key rune) bool {
-	isInSurrogateArea := key >= 0xd800 && key <= 0xdbff
-	return key >= 32 && !isInSurrogateArea
-}
-
-// moveCursorToPos appends data to t.outBuf which will move the cursor to the
-// given, logical position in the text.
-func (t *Terminal) moveCursorToPos(pos int) {
-	if !t.echo {
-		return
-	}
-
-	x := visualLength(t.prompt) + pos
-	y := x / t.termWidth
-	x = x % t.termWidth
-
-	up := 0
-	if y < t.cursorY {
-		up = t.cursorY - y
-	}
-
-	down := 0
-	if y > t.cursorY {
-		down = y - t.cursorY
-	}
-
-	left := 0
-	if x < t.cursorX {
-		left = t.cursorX - x
-	}
-
-	right := 0
-	if x > t.cursorX {
-		right = x - t.cursorX
-	}
-
-	t.cursorX = x
-	t.cursorY = y
-	t.move(up, down, left, right)
-}
-
-func (t *Terminal) move(up, down, left, right int) {
-	movement := make([]rune, 3*(up+down+left+right))
-	m := movement
-	for i := 0; i < up; i++ {
-		m[0] = keyEscape
-		m[1] = '['
-		m[2] = 'A'
-		m = m[3:]
-	}
-	for i := 0; i < down; i++ {
-		m[0] = keyEscape
-		m[1] = '['
-		m[2] = 'B'
-		m = m[3:]
-	}
-	for i := 0; i < left; i++ {
-		m[0] = keyEscape
-		m[1] = '['
-		m[2] = 'D'
-		m = m[3:]
-	}
-	for i := 0; i < right; i++ {
-		m[0] = keyEscape
-		m[1] = '['
-		m[2] = 'C'
-		m = m[3:]
-	}
-
-	t.queue(movement)
-}
-
-func (t *Terminal) clearLineToRight() {
-	op := []rune{keyEscape, '[', 'K'}
-	t.queue(op)
-}
-
-const maxLineLength = 4096
-
-func (t *Terminal) setLine(newLine []rune, newPos int) {
-	if t.echo {
-		t.moveCursorToPos(0)
-		t.writeLine(newLine)
-		for i := len(newLine); i < len(t.line); i++ {
-			t.writeLine(space)
-		}
-		t.moveCursorToPos(newPos)
-	}
-	t.line = newLine
-	t.pos = newPos
-}
-
-func (t *Terminal) advanceCursor(places int) {
-	t.cursorX += places
-	t.cursorY += t.cursorX / t.termWidth
-	if t.cursorY > t.maxLine {
-		t.maxLine = t.cursorY
-	}
-	t.cursorX = t.cursorX % t.termWidth
-
-	if places > 0 && t.cursorX == 0 {
-		// Normally terminals will advance the current position
-		// when writing a character. But that doesn't happen
-		// for the last character in a line. However, when
-		// writing a character (except a new line) that causes
-		// a line wrap, the position will be advanced two
-		// places.
-		//
-		// So, if we are stopping at the end of a line, we
-		// need to write a newline so that our cursor can be
-		// advanced to the next line.
-		t.outBuf = append(t.outBuf, '\r', '\n')
-	}
-}
-
-func (t *Terminal) eraseNPreviousChars(n int) {
-	if n == 0 {
-		return
-	}
-
-	if t.pos < n {
-		n = t.pos
-	}
-	t.pos -= n
-	t.moveCursorToPos(t.pos)
-
-	copy(t.line[t.pos:], t.line[n+t.pos:])
-	t.line = t.line[:len(t.line)-n]
-	if t.echo {
-		t.writeLine(t.line[t.pos:])
-		for i := 0; i < n; i++ {
-			t.queue(space)
-		}
-		t.advanceCursor(n)
-		t.moveCursorToPos(t.pos)
-	}
-}
-
-// countToLeftWord returns then number of characters from the cursor to the
-// start of the previous word.
-func (t *Terminal) countToLeftWord() int {
-	if t.pos == 0 {
-		return 0
-	}
-
-	pos := t.pos - 1
-	for pos > 0 {
-		if t.line[pos] != ' ' {
-			break
-		}
-		pos--
-	}
-	for pos > 0 {
-		if t.line[pos] == ' ' {
-			pos++
-			break
-		}
-		pos--
-	}
-
-	return t.pos - pos
-}
-
-// countToRightWord returns then number of characters from the cursor to the
-// start of the next word.
-func (t *Terminal) countToRightWord() int {
-	pos := t.pos
-	for pos < len(t.line) {
-		if t.line[pos] == ' ' {
-			break
-		}
-		pos++
-	}
-	for pos < len(t.line) {
-		if t.line[pos] != ' ' {
-			break
-		}
-		pos++
-	}
-	return pos - t.pos
-}
-
-// visualLength returns the number of visible glyphs in s.
-func visualLength(runes []rune) int {
-	inEscapeSeq := false
-	length := 0
-
-	for _, r := range runes {
-		switch {
-		case inEscapeSeq:
-			if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') {
-				inEscapeSeq = false
-			}
-		case r == '\x1b':
-			inEscapeSeq = true
-		default:
-			length++
-		}
-	}
-
-	return length
-}
-
-// handleKey processes the given key and, optionally, returns a line of text
-// that the user has entered.
-func (t *Terminal) handleKey(key rune) (line string, ok bool) {
-	if t.pasteActive && key != keyEnter {
-		t.addKeyToLine(key)
-		return
-	}
-
-	switch key {
-	case keyBackspace:
-		if t.pos == 0 {
-			return
-		}
-		t.eraseNPreviousChars(1)
-	case keyAltLeft:
-		// move left by a word.
-		t.pos -= t.countToLeftWord()
-		t.moveCursorToPos(t.pos)
-	case keyAltRight:
-		// move right by a word.
-		t.pos += t.countToRightWord()
-		t.moveCursorToPos(t.pos)
-	case keyLeft:
-		if t.pos == 0 {
-			return
-		}
-		t.pos--
-		t.moveCursorToPos(t.pos)
-	case keyRight:
-		if t.pos == len(t.line) {
-			return
-		}
-		t.pos++
-		t.moveCursorToPos(t.pos)
-	case keyHome:
-		if t.pos == 0 {
-			return
-		}
-		t.pos = 0
-		t.moveCursorToPos(t.pos)
-	case keyEnd:
-		if t.pos == len(t.line) {
-			return
-		}
-		t.pos = len(t.line)
-		t.moveCursorToPos(t.pos)
-	case keyUp:
-		entry, ok := t.history.NthPreviousEntry(t.historyIndex + 1)
-		if !ok {
-			return "", false
-		}
-		if t.historyIndex == -1 {
-			t.historyPending = string(t.line)
-		}
-		t.historyIndex++
-		runes := []rune(entry)
-		t.setLine(runes, len(runes))
-	case keyDown:
-		switch t.historyIndex {
-		case -1:
-			return
-		case 0:
-			runes := []rune(t.historyPending)
-			t.setLine(runes, len(runes))
-			t.historyIndex--
-		default:
-			entry, ok := t.history.NthPreviousEntry(t.historyIndex - 1)
-			if ok {
-				t.historyIndex--
-				runes := []rune(entry)
-				t.setLine(runes, len(runes))
-			}
-		}
-	case keyEnter:
-		t.moveCursorToPos(len(t.line))
-		t.queue([]rune("\r\n"))
-		line = string(t.line)
-		ok = true
-		t.line = t.line[:0]
-		t.pos = 0
-		t.cursorX = 0
-		t.cursorY = 0
-		t.maxLine = 0
-	case keyDeleteWord:
-		// Delete zero or more spaces and then one or more characters.
-		t.eraseNPreviousChars(t.countToLeftWord())
-	case keyDeleteLine:
-		// Delete everything from the current cursor position to the
-		// end of line.
-		for i := t.pos; i < len(t.line); i++ {
-			t.queue(space)
-			t.advanceCursor(1)
-		}
-		t.line = t.line[:t.pos]
-		t.moveCursorToPos(t.pos)
-	case keyCtrlD:
-		// Erase the character under the current position.
-		// The EOF case when the line is empty is handled in
-		// readLine().
-		if t.pos < len(t.line) {
-			t.pos++
-			t.eraseNPreviousChars(1)
-		}
-	case keyCtrlU:
-		t.eraseNPreviousChars(t.pos)
-	case keyClearScreen:
-		// Erases the screen and moves the cursor to the home position.
-		t.queue([]rune("\x1b[2J\x1b[H"))
-		t.queue(t.prompt)
-		t.cursorX, t.cursorY = 0, 0
-		t.advanceCursor(visualLength(t.prompt))
-		t.setLine(t.line, t.pos)
-	default:
-		if t.AutoCompleteCallback != nil {
-			prefix := string(t.line[:t.pos])
-			suffix := string(t.line[t.pos:])
-
-			t.lock.Unlock()
-			newLine, newPos, completeOk := t.AutoCompleteCallback(prefix+suffix, len(prefix), key)
-			t.lock.Lock()
-
-			if completeOk {
-				t.setLine([]rune(newLine), utf8.RuneCount([]byte(newLine)[:newPos]))
-				return
-			}
-		}
-		if !isPrintable(key) {
-			return
-		}
-		if len(t.line) == maxLineLength {
-			return
-		}
-		t.addKeyToLine(key)
-	}
-	return
-}
-
-// addKeyToLine inserts the given key at the current position in the current
-// line.
-func (t *Terminal) addKeyToLine(key rune) {
-	if len(t.line) == cap(t.line) {
-		newLine := make([]rune, len(t.line), 2*(1+len(t.line)))
-		copy(newLine, t.line)
-		t.line = newLine
-	}
-	t.line = t.line[:len(t.line)+1]
-	copy(t.line[t.pos+1:], t.line[t.pos:])
-	t.line[t.pos] = key
-	if t.echo {
-		t.writeLine(t.line[t.pos:])
-	}
-	t.pos++
-	t.moveCursorToPos(t.pos)
-}
-
-func (t *Terminal) writeLine(line []rune) {
-	for len(line) != 0 {
-		remainingOnLine := t.termWidth - t.cursorX
-		todo := len(line)
-		if todo > remainingOnLine {
-			todo = remainingOnLine
-		}
-		t.queue(line[:todo])
-		t.advanceCursor(visualLength(line[:todo]))
-		line = line[todo:]
-	}
-}
-
-// writeWithCRLF writes buf to w but replaces all occurrences of \n with \r\n.
-func writeWithCRLF(w io.Writer, buf []byte) (n int, err error) {
-	for len(buf) > 0 {
-		i := bytes.IndexByte(buf, '\n')
-		todo := len(buf)
-		if i >= 0 {
-			todo = i
-		}
-
-		var nn int
-		nn, err = w.Write(buf[:todo])
-		n += nn
-		if err != nil {
-			return n, err
-		}
-		buf = buf[todo:]
-
-		if i >= 0 {
-			if _, err = w.Write(crlf); err != nil {
-				return n, err
-			}
-			n++
-			buf = buf[1:]
-		}
-	}
-
-	return n, nil
-}
-
-func (t *Terminal) Write(buf []byte) (n int, err error) {
-	t.lock.Lock()
-	defer t.lock.Unlock()
-
-	if t.cursorX == 0 && t.cursorY == 0 {
-		// This is the easy case: there's nothing on the screen that we
-		// have to move out of the way.
-		return writeWithCRLF(t.c, buf)
-	}
-
-	// We have a prompt and possibly user input on the screen. We
-	// have to clear it first.
-	t.move(0 /* up */, 0 /* down */, t.cursorX /* left */, 0 /* right */)
-	t.cursorX = 0
-	t.clearLineToRight()
-
-	for t.cursorY > 0 {
-		t.move(1 /* up */, 0, 0, 0)
-		t.cursorY--
-		t.clearLineToRight()
-	}
-
-	if _, err = t.c.Write(t.outBuf); err != nil {
-		return
-	}
-	t.outBuf = t.outBuf[:0]
-
-	if n, err = writeWithCRLF(t.c, buf); err != nil {
-		return
-	}
-
-	t.writeLine(t.prompt)
-	if t.echo {
-		t.writeLine(t.line)
-	}
-
-	t.moveCursorToPos(t.pos)
-
-	if _, err = t.c.Write(t.outBuf); err != nil {
-		return
-	}
-	t.outBuf = t.outBuf[:0]
-	return
-}
-
-// ReadPassword temporarily changes the prompt and reads a password, without
-// echo, from the terminal.
-func (t *Terminal) ReadPassword(prompt string) (line string, err error) {
-	t.lock.Lock()
-	defer t.lock.Unlock()
-
-	oldPrompt := t.prompt
-	t.prompt = []rune(prompt)
-	t.echo = false
-
-	line, err = t.readLine()
-
-	t.prompt = oldPrompt
-	t.echo = true
-
-	return
-}
-
-// ReadLine returns a line of input from the terminal.
-func (t *Terminal) ReadLine() (line string, err error) {
-	t.lock.Lock()
-	defer t.lock.Unlock()
-
-	return t.readLine()
-}
-
-func (t *Terminal) readLine() (line string, err error) {
-	// t.lock must be held at this point
-
-	if t.cursorX == 0 && t.cursorY == 0 {
-		t.writeLine(t.prompt)
-		t.c.Write(t.outBuf)
-		t.outBuf = t.outBuf[:0]
-	}
-
-	lineIsPasted := t.pasteActive
-
-	for {
-		rest := t.remainder
-		lineOk := false
-		for !lineOk {
-			var key rune
-			key, rest = bytesToKey(rest, t.pasteActive)
-			if key == utf8.RuneError {
-				break
-			}
-			if !t.pasteActive {
-				if key == keyCtrlD {
-					if len(t.line) == 0 {
-						return "", io.EOF
-					}
-				}
-				if key == keyPasteStart {
-					t.pasteActive = true
-					if len(t.line) == 0 {
-						lineIsPasted = true
-					}
-					continue
-				}
-			} else if key == keyPasteEnd {
-				t.pasteActive = false
-				continue
-			}
-			if !t.pasteActive {
-				lineIsPasted = false
-			}
-			line, lineOk = t.handleKey(key)
-		}
-		if len(rest) > 0 {
-			n := copy(t.inBuf[:], rest)
-			t.remainder = t.inBuf[:n]
-		} else {
-			t.remainder = nil
-		}
-		t.c.Write(t.outBuf)
-		t.outBuf = t.outBuf[:0]
-		if lineOk {
-			if t.echo {
-				t.historyIndex = -1
-				t.history.Add(line)
-			}
-			if lineIsPasted {
-				err = ErrPasteIndicator
-			}
-			return
-		}
-
-		// t.remainder is a slice at the beginning of t.inBuf
-		// containing a partial key sequence
-		readBuf := t.inBuf[len(t.remainder):]
-		var n int
-
-		t.lock.Unlock()
-		n, err = t.c.Read(readBuf)
-		t.lock.Lock()
-
-		if err != nil {
-			return
-		}
-
-		t.remainder = t.inBuf[:n+len(t.remainder)]
-	}
-}
-
-// SetPrompt sets the prompt to be used when reading subsequent lines.
-func (t *Terminal) SetPrompt(prompt string) {
-	t.lock.Lock()
-	defer t.lock.Unlock()
-
-	t.prompt = []rune(prompt)
-}
-
-func (t *Terminal) clearAndRepaintLinePlusNPrevious(numPrevLines int) {
-	// Move cursor to column zero at the start of the line.
-	t.move(t.cursorY, 0, t.cursorX, 0)
-	t.cursorX, t.cursorY = 0, 0
-	t.clearLineToRight()
-	for t.cursorY < numPrevLines {
-		// Move down a line
-		t.move(0, 1, 0, 0)
-		t.cursorY++
-		t.clearLineToRight()
-	}
-	// Move back to beginning.
-	t.move(t.cursorY, 0, 0, 0)
-	t.cursorX, t.cursorY = 0, 0
-
-	t.queue(t.prompt)
-	t.advanceCursor(visualLength(t.prompt))
-	t.writeLine(t.line)
-	t.moveCursorToPos(t.pos)
-}
-
-func (t *Terminal) SetSize(width, height int) error {
-	t.lock.Lock()
-	defer t.lock.Unlock()
-
-	if width == 0 {
-		width = 1
-	}
-
-	oldWidth := t.termWidth
-	t.termWidth, t.termHeight = width, height
-
-	switch {
-	case width == oldWidth:
-		// If the width didn't change then nothing else needs to be
-		// done.
-		return nil
-	case len(t.line) == 0 && t.cursorX == 0 && t.cursorY == 0:
-		// If there is nothing on current line and no prompt printed,
-		// just do nothing
-		return nil
-	case width < oldWidth:
-		// Some terminals (e.g. xterm) will truncate lines that were
-		// too long when shinking. Others, (e.g. gnome-terminal) will
-		// attempt to wrap them. For the former, repainting t.maxLine
-		// works great, but that behaviour goes badly wrong in the case
-		// of the latter because they have doubled every full line.
-
-		// We assume that we are working on a terminal that wraps lines
-		// and adjust the cursor position based on every previous line
-		// wrapping and turning into two. This causes the prompt on
-		// xterms to move upwards, which isn't great, but it avoids a
-		// huge mess with gnome-terminal.
-		if t.cursorX >= t.termWidth {
-			t.cursorX = t.termWidth - 1
-		}
-		t.cursorY *= 2
-		t.clearAndRepaintLinePlusNPrevious(t.maxLine * 2)
-	case width > oldWidth:
-		// If the terminal expands then our position calculations will
-		// be wrong in the future because we think the cursor is
-		// |t.pos| chars into the string, but there will be a gap at
-		// the end of any wrapped line.
-		//
-		// But the position will actually be correct until we move, so
-		// we can move back to the beginning and repaint everything.
-		t.clearAndRepaintLinePlusNPrevious(t.maxLine)
-	}
-
-	_, err := t.c.Write(t.outBuf)
-	t.outBuf = t.outBuf[:0]
-	return err
-}
-
-type pasteIndicatorError struct{}
-
-func (pasteIndicatorError) Error() string {
-	return "terminal: ErrPasteIndicator not correctly handled"
+	return term.NewTerminal(c, prompt)
 }
 
 // ErrPasteIndicator may be returned from ReadLine as the error, in addition
 // to valid line data. It indicates that bracketed paste mode is enabled and
 // that the returned line consists only of pasted data. Programs may wish to
 // interpret pasted data more literally than typed data.
-var ErrPasteIndicator = pasteIndicatorError{}
+var ErrPasteIndicator = term.ErrPasteIndicator
 
-// SetBracketedPasteMode requests that the terminal bracket paste operations
-// with markers. Not all terminals support this but, if it is supported, then
-// enabling this mode will stop any autocomplete callback from running due to
-// pastes. Additionally, any lines that are completely pasted will be returned
-// from ReadLine with the error set to ErrPasteIndicator.
-func (t *Terminal) SetBracketedPasteMode(on bool) {
-	if on {
-		io.WriteString(t.c, "\x1b[?2004h")
-	} else {
-		io.WriteString(t.c, "\x1b[?2004l")
-	}
-}
+// State contains the state of a terminal.
+type State = term.State
 
-// stRingBuffer is a ring buffer of strings.
-type stRingBuffer struct {
-	// entries contains max elements.
-	entries []string
-	max     int
-	// head contains the index of the element most recently added to the ring.
-	head int
-	// size contains the number of elements in the ring.
-	size int
+// IsTerminal returns whether the given file descriptor is a terminal.
+func IsTerminal(fd int) bool {
+	return term.IsTerminal(fd)
 }
 
-func (s *stRingBuffer) Add(a string) {
-	if s.entries == nil {
-		const defaultNumEntries = 100
-		s.entries = make([]string, defaultNumEntries)
-		s.max = defaultNumEntries
-	}
+// ReadPassword reads a line of input from a terminal without local echo.  This
+// is commonly used for inputting passwords and other sensitive data. The slice
+// returned does not include the \n.
+func ReadPassword(fd int) ([]byte, error) {
+	return term.ReadPassword(fd)
+}
 
-	s.head = (s.head + 1) % s.max
-	s.entries[s.head] = a
-	if s.size < s.max {
-		s.size++
-	}
+// MakeRaw puts the terminal connected to the given file descriptor into raw
+// mode and returns the previous state of the terminal so that it can be
+// restored.
+func MakeRaw(fd int) (*State, error) {
+	return term.MakeRaw(fd)
 }
 
-// NthPreviousEntry returns the value passed to the nth previous call to Add.
-// If n is zero then the immediately prior value is returned, if one, then the
-// next most recent, and so on. If such an element doesn't exist then ok is
-// false.
-func (s *stRingBuffer) NthPreviousEntry(n int) (value string, ok bool) {
-	if n >= s.size {
-		return "", false
-	}
-	index := s.head - n
-	if index < 0 {
-		index += s.max
-	}
-	return s.entries[index], true
+// Restore restores the terminal connected to the given file descriptor to a
+// previous state.
+func Restore(fd int, oldState *State) error {
+	return term.Restore(fd, oldState)
 }
 
-// readPasswordLine reads from reader until it finds \n or io.EOF.
-// The slice returned does not include the \n.
-// readPasswordLine also ignores any \r it finds.
-func readPasswordLine(reader io.Reader) ([]byte, error) {
-	var buf [1]byte
-	var ret []byte
+// GetState returns the current state of a terminal which may be useful to
+// restore the terminal after a signal.
+func GetState(fd int) (*State, error) {
+	return term.GetState(fd)
+}
 
-	for {
-		n, err := reader.Read(buf[:])
-		if n > 0 {
-			switch buf[0] {
-			case '\n':
-				return ret, nil
-			case '\r':
-				// remove \r from passwords on Windows
-			default:
-				ret = append(ret, buf[0])
-			}
-			continue
-		}
-		if err != nil {
-			if err == io.EOF && len(ret) > 0 {
-				return ret, nil
-			}
-			return ret, err
-		}
-	}
+// GetSize returns the dimensions of the given terminal.
+func GetSize(fd int) (width, height int, err error) {
+	return term.GetSize(fd)
 }

+ 0 - 358
psiphon/common/crypto/ssh/terminal/terminal_test.go

@@ -1,358 +0,0 @@
-// Copyright 2011 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 darwin dragonfly freebsd linux,!appengine netbsd openbsd windows plan9 solaris
-
-package terminal
-
-import (
-	"bytes"
-	"io"
-	"os"
-	"runtime"
-	"testing"
-)
-
-type MockTerminal struct {
-	toSend       []byte
-	bytesPerRead int
-	received     []byte
-}
-
-func (c *MockTerminal) Read(data []byte) (n int, err error) {
-	n = len(data)
-	if n == 0 {
-		return
-	}
-	if n > len(c.toSend) {
-		n = len(c.toSend)
-	}
-	if n == 0 {
-		return 0, io.EOF
-	}
-	if c.bytesPerRead > 0 && n > c.bytesPerRead {
-		n = c.bytesPerRead
-	}
-	copy(data, c.toSend[:n])
-	c.toSend = c.toSend[n:]
-	return
-}
-
-func (c *MockTerminal) Write(data []byte) (n int, err error) {
-	c.received = append(c.received, data...)
-	return len(data), nil
-}
-
-func TestClose(t *testing.T) {
-	c := &MockTerminal{}
-	ss := NewTerminal(c, "> ")
-	line, err := ss.ReadLine()
-	if line != "" {
-		t.Errorf("Expected empty line but got: %s", line)
-	}
-	if err != io.EOF {
-		t.Errorf("Error should have been EOF but got: %s", err)
-	}
-}
-
-var keyPressTests = []struct {
-	in             string
-	line           string
-	err            error
-	throwAwayLines int
-}{
-	{
-		err: io.EOF,
-	},
-	{
-		in:   "\r",
-		line: "",
-	},
-	{
-		in:   "foo\r",
-		line: "foo",
-	},
-	{
-		in:   "a\x1b[Cb\r", // right
-		line: "ab",
-	},
-	{
-		in:   "a\x1b[Db\r", // left
-		line: "ba",
-	},
-	{
-		in:   "a\177b\r", // backspace
-		line: "b",
-	},
-	{
-		in: "\x1b[A\r", // up
-	},
-	{
-		in: "\x1b[B\r", // down
-	},
-	{
-		in:   "line\x1b[A\x1b[B\r", // up then down
-		line: "line",
-	},
-	{
-		in:             "line1\rline2\x1b[A\r", // recall previous line.
-		line:           "line1",
-		throwAwayLines: 1,
-	},
-	{
-		// recall two previous lines and append.
-		in:             "line1\rline2\rline3\x1b[A\x1b[Axxx\r",
-		line:           "line1xxx",
-		throwAwayLines: 2,
-	},
-	{
-		// Ctrl-A to move to beginning of line followed by ^K to kill
-		// line.
-		in:   "a b \001\013\r",
-		line: "",
-	},
-	{
-		// Ctrl-A to move to beginning of line, Ctrl-E to move to end,
-		// finally ^K to kill nothing.
-		in:   "a b \001\005\013\r",
-		line: "a b ",
-	},
-	{
-		in:   "\027\r",
-		line: "",
-	},
-	{
-		in:   "a\027\r",
-		line: "",
-	},
-	{
-		in:   "a \027\r",
-		line: "",
-	},
-	{
-		in:   "a b\027\r",
-		line: "a ",
-	},
-	{
-		in:   "a b \027\r",
-		line: "a ",
-	},
-	{
-		in:   "one two thr\x1b[D\027\r",
-		line: "one two r",
-	},
-	{
-		in:   "\013\r",
-		line: "",
-	},
-	{
-		in:   "a\013\r",
-		line: "a",
-	},
-	{
-		in:   "ab\x1b[D\013\r",
-		line: "a",
-	},
-	{
-		in:   "Ξεσκεπάζω\r",
-		line: "Ξεσκεπάζω",
-	},
-	{
-		in:             "£\r\x1b[A\177\r", // non-ASCII char, enter, up, backspace.
-		line:           "",
-		throwAwayLines: 1,
-	},
-	{
-		in:             "£\r££\x1b[A\x1b[B\177\r", // non-ASCII char, enter, 2x non-ASCII, up, down, backspace, enter.
-		line:           "£",
-		throwAwayLines: 1,
-	},
-	{
-		// Ctrl-D at the end of the line should be ignored.
-		in:   "a\004\r",
-		line: "a",
-	},
-	{
-		// a, b, left, Ctrl-D should erase the b.
-		in:   "ab\x1b[D\004\r",
-		line: "a",
-	},
-	{
-		// a, b, c, d, left, left, ^U should erase to the beginning of
-		// the line.
-		in:   "abcd\x1b[D\x1b[D\025\r",
-		line: "cd",
-	},
-	{
-		// Bracketed paste mode: control sequences should be returned
-		// verbatim in paste mode.
-		in:   "abc\x1b[200~de\177f\x1b[201~\177\r",
-		line: "abcde\177",
-	},
-	{
-		// Enter in bracketed paste mode should still work.
-		in:             "abc\x1b[200~d\refg\x1b[201~h\r",
-		line:           "efgh",
-		throwAwayLines: 1,
-	},
-	{
-		// Lines consisting entirely of pasted data should be indicated as such.
-		in:   "\x1b[200~a\r",
-		line: "a",
-		err:  ErrPasteIndicator,
-	},
-}
-
-func TestKeyPresses(t *testing.T) {
-	for i, test := range keyPressTests {
-		for j := 1; j < len(test.in); j++ {
-			c := &MockTerminal{
-				toSend:       []byte(test.in),
-				bytesPerRead: j,
-			}
-			ss := NewTerminal(c, "> ")
-			for k := 0; k < test.throwAwayLines; k++ {
-				_, err := ss.ReadLine()
-				if err != nil {
-					t.Errorf("Throwaway line %d from test %d resulted in error: %s", k, i, err)
-				}
-			}
-			line, err := ss.ReadLine()
-			if line != test.line {
-				t.Errorf("Line resulting from test %d (%d bytes per read) was '%s', expected '%s'", i, j, line, test.line)
-				break
-			}
-			if err != test.err {
-				t.Errorf("Error resulting from test %d (%d bytes per read) was '%v', expected '%v'", i, j, err, test.err)
-				break
-			}
-		}
-	}
-}
-
-func TestPasswordNotSaved(t *testing.T) {
-	c := &MockTerminal{
-		toSend:       []byte("password\r\x1b[A\r"),
-		bytesPerRead: 1,
-	}
-	ss := NewTerminal(c, "> ")
-	pw, _ := ss.ReadPassword("> ")
-	if pw != "password" {
-		t.Fatalf("failed to read password, got %s", pw)
-	}
-	line, _ := ss.ReadLine()
-	if len(line) > 0 {
-		t.Fatalf("password was saved in history")
-	}
-}
-
-var setSizeTests = []struct {
-	width, height int
-}{
-	{40, 13},
-	{80, 24},
-	{132, 43},
-}
-
-func TestTerminalSetSize(t *testing.T) {
-	for _, setSize := range setSizeTests {
-		c := &MockTerminal{
-			toSend:       []byte("password\r\x1b[A\r"),
-			bytesPerRead: 1,
-		}
-		ss := NewTerminal(c, "> ")
-		ss.SetSize(setSize.width, setSize.height)
-		pw, _ := ss.ReadPassword("Password: ")
-		if pw != "password" {
-			t.Fatalf("failed to read password, got %s", pw)
-		}
-		if string(c.received) != "Password: \r\n" {
-			t.Errorf("failed to set the temporary prompt expected %q, got %q", "Password: ", c.received)
-		}
-	}
-}
-
-func TestReadPasswordLineEnd(t *testing.T) {
-	var tests = []struct {
-		input string
-		want  string
-	}{
-		{"\n", ""},
-		{"\r\n", ""},
-		{"test\r\n", "test"},
-		{"testtesttesttes\n", "testtesttesttes"},
-		{"testtesttesttes\r\n", "testtesttesttes"},
-		{"testtesttesttesttest\n", "testtesttesttesttest"},
-		{"testtesttesttesttest\r\n", "testtesttesttesttest"},
-	}
-	for _, test := range tests {
-		buf := new(bytes.Buffer)
-		if _, err := buf.WriteString(test.input); err != nil {
-			t.Fatal(err)
-		}
-
-		have, err := readPasswordLine(buf)
-		if err != nil {
-			t.Errorf("readPasswordLine(%q) failed: %v", test.input, err)
-			continue
-		}
-		if string(have) != test.want {
-			t.Errorf("readPasswordLine(%q) returns %q, but %q is expected", test.input, string(have), test.want)
-			continue
-		}
-
-		if _, err = buf.WriteString(test.input); err != nil {
-			t.Fatal(err)
-		}
-		have, err = readPasswordLine(buf)
-		if err != nil {
-			t.Errorf("readPasswordLine(%q) failed: %v", test.input, err)
-			continue
-		}
-		if string(have) != test.want {
-			t.Errorf("readPasswordLine(%q) returns %q, but %q is expected", test.input, string(have), test.want)
-			continue
-		}
-	}
-}
-
-func TestMakeRawState(t *testing.T) {
-	fd := int(os.Stdout.Fd())
-	if !IsTerminal(fd) {
-		t.Skip("stdout is not a terminal; skipping test")
-	}
-
-	st, err := GetState(fd)
-	if err != nil {
-		t.Fatalf("failed to get terminal state from GetState: %s", err)
-	}
-
-	if runtime.GOOS == "darwin" && (runtime.GOARCH == "arm" || runtime.GOARCH == "arm64") {
-		t.Skip("MakeRaw not allowed on iOS; skipping test")
-	}
-
-	defer Restore(fd, st)
-	raw, err := MakeRaw(fd)
-	if err != nil {
-		t.Fatalf("failed to get terminal state from MakeRaw: %s", err)
-	}
-
-	if *st != *raw {
-		t.Errorf("states do not match; was %v, expected %v", raw, st)
-	}
-}
-
-func TestOutputNewlines(t *testing.T) {
-	// \n should be changed to \r\n in terminal output.
-	buf := new(bytes.Buffer)
-	term := NewTerminal(buf, ">")
-
-	term.Write([]byte("1\n2\n"))
-	output := string(buf.Bytes())
-	const expected = "1\r\n2\r\n"
-
-	if output != expected {
-		t.Errorf("incorrect output: was %q, expected %q", output, expected)
-	}
-}

+ 0 - 114
psiphon/common/crypto/ssh/terminal/util.go

@@ -1,114 +0,0 @@
-// Copyright 2011 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 darwin dragonfly freebsd linux,!appengine netbsd openbsd
-
-// Package terminal provides support functions for dealing with terminals, as
-// commonly found on UNIX systems.
-//
-// Putting a terminal into raw mode is the most common requirement:
-//
-// 	oldState, err := terminal.MakeRaw(0)
-// 	if err != nil {
-// 	        panic(err)
-// 	}
-// 	defer terminal.Restore(0, oldState)
-package terminal // import "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/ssh/terminal"
-
-import (
-	"golang.org/x/sys/unix"
-)
-
-// State contains the state of a terminal.
-type State struct {
-	termios unix.Termios
-}
-
-// IsTerminal returns true if the given file descriptor is a terminal.
-func IsTerminal(fd int) bool {
-	_, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
-	return err == nil
-}
-
-// MakeRaw put the terminal connected to the given file descriptor into raw
-// mode and returns the previous state of the terminal so that it can be
-// restored.
-func MakeRaw(fd int) (*State, error) {
-	termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
-	if err != nil {
-		return nil, err
-	}
-
-	oldState := State{termios: *termios}
-
-	// This attempts to replicate the behaviour documented for cfmakeraw in
-	// the termios(3) manpage.
-	termios.Iflag &^= unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON
-	termios.Oflag &^= unix.OPOST
-	termios.Lflag &^= unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN
-	termios.Cflag &^= unix.CSIZE | unix.PARENB
-	termios.Cflag |= unix.CS8
-	termios.Cc[unix.VMIN] = 1
-	termios.Cc[unix.VTIME] = 0
-	if err := unix.IoctlSetTermios(fd, ioctlWriteTermios, termios); err != nil {
-		return nil, err
-	}
-
-	return &oldState, nil
-}
-
-// GetState returns the current state of a terminal which may be useful to
-// restore the terminal after a signal.
-func GetState(fd int) (*State, error) {
-	termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
-	if err != nil {
-		return nil, err
-	}
-
-	return &State{termios: *termios}, nil
-}
-
-// Restore restores the terminal connected to the given file descriptor to a
-// previous state.
-func Restore(fd int, state *State) error {
-	return unix.IoctlSetTermios(fd, ioctlWriteTermios, &state.termios)
-}
-
-// GetSize returns the dimensions of the given terminal.
-func GetSize(fd int) (width, height int, err error) {
-	ws, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ)
-	if err != nil {
-		return -1, -1, err
-	}
-	return int(ws.Col), int(ws.Row), nil
-}
-
-// passwordReader is an io.Reader that reads from a specific file descriptor.
-type passwordReader int
-
-func (r passwordReader) Read(buf []byte) (int, error) {
-	return unix.Read(int(r), buf)
-}
-
-// ReadPassword reads a line of input from a terminal without local echo.  This
-// is commonly used for inputting passwords and other sensitive data. The slice
-// returned does not include the \n.
-func ReadPassword(fd int) ([]byte, error) {
-	termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
-	if err != nil {
-		return nil, err
-	}
-
-	newState := *termios
-	newState.Lflag &^= unix.ECHO
-	newState.Lflag |= unix.ICANON | unix.ISIG
-	newState.Iflag |= unix.ICRNL
-	if err := unix.IoctlSetTermios(fd, ioctlWriteTermios, &newState); err != nil {
-		return nil, err
-	}
-
-	defer unix.IoctlSetTermios(fd, ioctlWriteTermios, termios)
-
-	return readPasswordLine(passwordReader(fd))
-}

+ 0 - 12
psiphon/common/crypto/ssh/terminal/util_bsd.go

@@ -1,12 +0,0 @@
-// Copyright 2013 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 darwin dragonfly freebsd netbsd openbsd
-
-package terminal
-
-import "golang.org/x/sys/unix"
-
-const ioctlReadTermios = unix.TIOCGETA
-const ioctlWriteTermios = unix.TIOCSETA

+ 0 - 10
psiphon/common/crypto/ssh/terminal/util_linux.go

@@ -1,10 +0,0 @@
-// Copyright 2013 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 terminal
-
-import "golang.org/x/sys/unix"
-
-const ioctlReadTermios = unix.TCGETS
-const ioctlWriteTermios = unix.TCSETS

+ 0 - 58
psiphon/common/crypto/ssh/terminal/util_plan9.go

@@ -1,58 +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 terminal provides support functions for dealing with terminals, as
-// commonly found on UNIX systems.
-//
-// Putting a terminal into raw mode is the most common requirement:
-//
-// 	oldState, err := terminal.MakeRaw(0)
-// 	if err != nil {
-// 	        panic(err)
-// 	}
-// 	defer terminal.Restore(0, oldState)
-package terminal
-
-import (
-	"fmt"
-	"runtime"
-)
-
-type State struct{}
-
-// IsTerminal returns true if the given file descriptor is a terminal.
-func IsTerminal(fd int) bool {
-	return false
-}
-
-// MakeRaw put the terminal connected to the given file descriptor into raw
-// mode and returns the previous state of the terminal so that it can be
-// restored.
-func MakeRaw(fd int) (*State, error) {
-	return nil, fmt.Errorf("terminal: MakeRaw not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
-}
-
-// GetState returns the current state of a terminal which may be useful to
-// restore the terminal after a signal.
-func GetState(fd int) (*State, error) {
-	return nil, fmt.Errorf("terminal: GetState not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
-}
-
-// Restore restores the terminal connected to the given file descriptor to a
-// previous state.
-func Restore(fd int, state *State) error {
-	return fmt.Errorf("terminal: Restore not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
-}
-
-// GetSize returns the dimensions of the given terminal.
-func GetSize(fd int) (width, height int, err error) {
-	return 0, 0, fmt.Errorf("terminal: GetSize not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
-}
-
-// ReadPassword reads a line of input from a terminal without local echo.  This
-// is commonly used for inputting passwords and other sensitive data. The slice
-// returned does not include the \n.
-func ReadPassword(fd int) ([]byte, error) {
-	return nil, fmt.Errorf("terminal: ReadPassword not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
-}

+ 0 - 124
psiphon/common/crypto/ssh/terminal/util_solaris.go

@@ -1,124 +0,0 @@
-// Copyright 2015 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 solaris
-
-package terminal // import "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/ssh/terminal"
-
-import (
-	"golang.org/x/sys/unix"
-	"io"
-	"syscall"
-)
-
-// State contains the state of a terminal.
-type State struct {
-	termios unix.Termios
-}
-
-// IsTerminal returns true if the given file descriptor is a terminal.
-func IsTerminal(fd int) bool {
-	_, err := unix.IoctlGetTermio(fd, unix.TCGETA)
-	return err == nil
-}
-
-// ReadPassword reads a line of input from a terminal without local echo.  This
-// is commonly used for inputting passwords and other sensitive data. The slice
-// returned does not include the \n.
-func ReadPassword(fd int) ([]byte, error) {
-	// see also: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libast/common/uwin/getpass.c
-	val, err := unix.IoctlGetTermios(fd, unix.TCGETS)
-	if err != nil {
-		return nil, err
-	}
-	oldState := *val
-
-	newState := oldState
-	newState.Lflag &^= syscall.ECHO
-	newState.Lflag |= syscall.ICANON | syscall.ISIG
-	newState.Iflag |= syscall.ICRNL
-	err = unix.IoctlSetTermios(fd, unix.TCSETS, &newState)
-	if err != nil {
-		return nil, err
-	}
-
-	defer unix.IoctlSetTermios(fd, unix.TCSETS, &oldState)
-
-	var buf [16]byte
-	var ret []byte
-	for {
-		n, err := syscall.Read(fd, buf[:])
-		if err != nil {
-			return nil, err
-		}
-		if n == 0 {
-			if len(ret) == 0 {
-				return nil, io.EOF
-			}
-			break
-		}
-		if buf[n-1] == '\n' {
-			n--
-		}
-		ret = append(ret, buf[:n]...)
-		if n < len(buf) {
-			break
-		}
-	}
-
-	return ret, nil
-}
-
-// MakeRaw puts the terminal connected to the given file descriptor into raw
-// mode and returns the previous state of the terminal so that it can be
-// restored.
-// see http://cr.illumos.org/~webrev/andy_js/1060/
-func MakeRaw(fd int) (*State, error) {
-	termios, err := unix.IoctlGetTermios(fd, unix.TCGETS)
-	if err != nil {
-		return nil, err
-	}
-
-	oldState := State{termios: *termios}
-
-	termios.Iflag &^= unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON
-	termios.Oflag &^= unix.OPOST
-	termios.Lflag &^= unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN
-	termios.Cflag &^= unix.CSIZE | unix.PARENB
-	termios.Cflag |= unix.CS8
-	termios.Cc[unix.VMIN] = 1
-	termios.Cc[unix.VTIME] = 0
-
-	if err := unix.IoctlSetTermios(fd, unix.TCSETS, termios); err != nil {
-		return nil, err
-	}
-
-	return &oldState, nil
-}
-
-// Restore restores the terminal connected to the given file descriptor to a
-// previous state.
-func Restore(fd int, oldState *State) error {
-	return unix.IoctlSetTermios(fd, unix.TCSETS, &oldState.termios)
-}
-
-// GetState returns the current state of a terminal which may be useful to
-// restore the terminal after a signal.
-func GetState(fd int) (*State, error) {
-	termios, err := unix.IoctlGetTermios(fd, unix.TCGETS)
-	if err != nil {
-		return nil, err
-	}
-
-	return &State{termios: *termios}, nil
-}
-
-// GetSize returns the dimensions of the given terminal.
-func GetSize(fd int) (width, height int, err error) {
-	ws, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ)
-	if err != nil {
-		return 0, 0, err
-	}
-	return int(ws.Col), int(ws.Row), nil
-}

+ 0 - 103
psiphon/common/crypto/ssh/terminal/util_windows.go

@@ -1,103 +0,0 @@
-// Copyright 2011 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 windows
-
-// Package terminal provides support functions for dealing with terminals, as
-// commonly found on UNIX systems.
-//
-// Putting a terminal into raw mode is the most common requirement:
-//
-// 	oldState, err := terminal.MakeRaw(0)
-// 	if err != nil {
-// 	        panic(err)
-// 	}
-// 	defer terminal.Restore(0, oldState)
-package terminal
-
-import (
-	"os"
-
-	"golang.org/x/sys/windows"
-)
-
-type State struct {
-	mode uint32
-}
-
-// IsTerminal returns true if the given file descriptor is a terminal.
-func IsTerminal(fd int) bool {
-	var st uint32
-	err := windows.GetConsoleMode(windows.Handle(fd), &st)
-	return err == nil
-}
-
-// MakeRaw put the terminal connected to the given file descriptor into raw
-// mode and returns the previous state of the terminal so that it can be
-// restored.
-func MakeRaw(fd int) (*State, error) {
-	var st uint32
-	if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
-		return nil, err
-	}
-	raw := st &^ (windows.ENABLE_ECHO_INPUT | windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT | windows.ENABLE_PROCESSED_OUTPUT)
-	if err := windows.SetConsoleMode(windows.Handle(fd), raw); err != nil {
-		return nil, err
-	}
-	return &State{st}, nil
-}
-
-// GetState returns the current state of a terminal which may be useful to
-// restore the terminal after a signal.
-func GetState(fd int) (*State, error) {
-	var st uint32
-	if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
-		return nil, err
-	}
-	return &State{st}, nil
-}
-
-// Restore restores the terminal connected to the given file descriptor to a
-// previous state.
-func Restore(fd int, state *State) error {
-	return windows.SetConsoleMode(windows.Handle(fd), state.mode)
-}
-
-// GetSize returns the dimensions of the given terminal.
-func GetSize(fd int) (width, height int, err error) {
-	var info windows.ConsoleScreenBufferInfo
-	if err := windows.GetConsoleScreenBufferInfo(windows.Handle(fd), &info); err != nil {
-		return 0, 0, err
-	}
-	return int(info.Size.X), int(info.Size.Y), nil
-}
-
-// ReadPassword reads a line of input from a terminal without local echo.  This
-// is commonly used for inputting passwords and other sensitive data. The slice
-// returned does not include the \n.
-func ReadPassword(fd int) ([]byte, error) {
-	var st uint32
-	if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
-		return nil, err
-	}
-	old := st
-
-	st &^= (windows.ENABLE_ECHO_INPUT)
-	st |= (windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT | windows.ENABLE_PROCESSED_OUTPUT)
-	if err := windows.SetConsoleMode(windows.Handle(fd), st); err != nil {
-		return nil, err
-	}
-
-	defer windows.SetConsoleMode(windows.Handle(fd), old)
-
-	var h windows.Handle
-	p, _ := windows.GetCurrentProcess()
-	if err := windows.DuplicateHandle(p, windows.Handle(fd), p, &h, 0, false, windows.DUPLICATE_SAME_ACCESS); err != nil {
-		return nil, err
-	}
-
-	f := os.NewFile(uintptr(h), "stdin")
-	defer f.Close()
-	return readPasswordLine(f)
-}

+ 2 - 1
psiphon/common/crypto/ssh/test/agent_unix_test.go

@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build darwin dragonfly freebsd linux netbsd openbsd
+//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd
+// +build aix darwin dragonfly freebsd linux netbsd openbsd
 
 package test
 

+ 2 - 1
psiphon/common/crypto/ssh/test/banner_test.go

@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build darwin dragonfly freebsd linux netbsd openbsd
+//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd
+// +build aix darwin dragonfly freebsd linux netbsd openbsd
 
 package test
 

+ 2 - 1
psiphon/common/crypto/ssh/test/cert_test.go

@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build darwin dragonfly freebsd linux netbsd openbsd
+//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd
+// +build aix darwin dragonfly freebsd linux netbsd openbsd
 
 package test
 

+ 4 - 3
psiphon/common/crypto/ssh/test/dial_unix_test.go

@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build !windows
+//go:build !windows && !solaris && !js
+// +build !windows,!solaris,!js
 
 package test
 
@@ -104,8 +105,8 @@ func (x *unixDialTester) TestServerConn(t *testing.T, c net.Conn) {
 	if c.LocalAddr().String() != x.listenAddr {
 		t.Fatalf("expected %q, got %q", x.listenAddr, c.LocalAddr().String())
 	}
-	if c.RemoteAddr().String() != "@" {
-		t.Fatalf("expected \"@\", got %q", c.RemoteAddr().String())
+	if c.RemoteAddr().String() != "@" && c.RemoteAddr().String() != "" {
+		t.Fatalf("expected \"@\" or \"\", got %q", c.RemoteAddr().String())
 	}
 }
 

+ 16 - 4
psiphon/common/crypto/ssh/test/forward_unix_test.go

@@ -2,12 +2,14 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build darwin dragonfly freebsd linux netbsd openbsd
+//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd
+// +build aix darwin dragonfly freebsd linux netbsd openbsd
 
 package test
 
 import (
 	"bytes"
+	"fmt"
 	"io"
 	"io/ioutil"
 	"math/rand"
@@ -31,17 +33,21 @@ func testPortForward(t *testing.T, n, listenAddr string) {
 		t.Fatal(err)
 	}
 
+	errCh := make(chan error, 1)
+
 	go func() {
+		defer close(errCh)
 		sshConn, err := sshListener.Accept()
 		if err != nil {
-			t.Fatalf("listen.Accept failed: %v", err)
+			errCh <- fmt.Errorf("listen.Accept failed: %v", err)
+			return
 		}
+		defer sshConn.Close()
 
 		_, err = io.Copy(sshConn, sshConn)
 		if err != nil && err != io.EOF {
-			t.Fatalf("ssh client copy: %v", err)
+			errCh <- fmt.Errorf("ssh client copy: %v", err)
 		}
-		sshConn.Close()
 	}()
 
 	forwardedAddr := sshListener.Addr().String()
@@ -76,6 +82,12 @@ func testPortForward(t *testing.T, n, listenAddr string) {
 		t.Errorf("netConn.CloseWrite: %v", err)
 	}
 
+	// Check for errors on server goroutine
+	err = <-errCh
+	if err != nil {
+		t.Fatalf("server: %v", err)
+	}
+
 	read := <-readChan
 
 	if len(sent) != len(read) {

+ 1 - 0
psiphon/common/crypto/ssh/test/multi_auth_test.go

@@ -14,6 +14,7 @@
 // not exist these tests will be skipped. See compile instructions
 // (for linux) in file ./sshd_test_pw.c.
 
+//go:build linux
 // +build linux
 
 package test

+ 62 - 56
psiphon/common/crypto/ssh/test/session_test.go

@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build !windows
+//go:build !windows && !solaris && !js
+// +build !windows,!solaris,!js
 
 package test
 
@@ -13,6 +14,7 @@ import (
 	"errors"
 	"fmt"
 	"io"
+	"runtime"
 	"strings"
 	"testing"
 
@@ -216,24 +218,12 @@ func TestKeyChange(t *testing.T) {
 	}
 }
 
-func TestInvalidTerminalMode(t *testing.T) {
-	server := newServer(t)
-	defer server.Shutdown()
-	conn := server.Dial(clientConfig())
-	defer conn.Close()
-
-	session, err := conn.NewSession()
-	if err != nil {
-		t.Fatalf("session failed: %v", err)
-	}
-	defer session.Close()
-
-	if err = session.RequestPty("vt100", 80, 40, ssh.TerminalModes{255: 1984}); err == nil {
-		t.Fatalf("req-pty failed: successful request with invalid mode")
-	}
-}
-
 func TestValidTerminalMode(t *testing.T) {
+	if runtime.GOOS == "aix" {
+		// On AIX, sshd cannot acquire /dev/pts/* if launched as
+		// a non-root user.
+		t.Skipf("skipping on %s", runtime.GOOS)
+	}
 	server := newServer(t)
 	defer server.Shutdown()
 	conn := server.Dial(clientConfig())
@@ -278,6 +268,11 @@ func TestValidTerminalMode(t *testing.T) {
 }
 
 func TestWindowChange(t *testing.T) {
+	if runtime.GOOS == "aix" {
+		// On AIX, sshd cannot acquire /dev/pts/* if launched as
+		// a non-root user.
+		t.Skipf("skipping on %s", runtime.GOOS)
+	}
 	server := newServer(t)
 	defer server.Shutdown()
 	conn := server.Dial(clientConfig())
@@ -351,7 +346,7 @@ func testOneCipher(t *testing.T, cipher string, cipherOrder []string) {
 		t.Fatalf("NewSession: %v", err)
 	}
 
-	out, err := session.Output(fmt.Sprintf("dd if=/dev/zero of=/dev/stdout bs=%d count=1", numBytes))
+	out, err := session.Output(fmt.Sprintf("dd if=/dev/zero bs=%d count=1", numBytes))
 	if err != nil {
 		t.Fatalf("Output: %v", err)
 	}
@@ -384,17 +379,19 @@ func TestMACs(t *testing.T) {
 	macOrder := config.MACs
 
 	for _, mac := range macOrder {
-		server := newServer(t)
-		defer server.Shutdown()
-		conf := clientConfig()
-		conf.MACs = []string{mac}
-		// Don't fail if sshd doesn't have the MAC.
-		conf.MACs = append(conf.MACs, macOrder...)
-		if conn, err := server.TryDial(conf); err == nil {
-			conn.Close()
-		} else {
-			t.Fatalf("failed for MAC %q", mac)
-		}
+		t.Run(mac, func(t *testing.T) {
+			server := newServer(t)
+			defer server.Shutdown()
+			conf := clientConfig()
+			conf.MACs = []string{mac}
+			// Don't fail if sshd doesn't have the MAC.
+			conf.MACs = append(conf.MACs, macOrder...)
+			if conn, err := server.TryDial(conf); err == nil {
+				conn.Close()
+			} else {
+				t.Fatalf("failed for MAC %q", mac)
+			}
+		})
 	}
 }
 
@@ -402,18 +399,25 @@ func TestKeyExchanges(t *testing.T) {
 	var config ssh.Config
 	config.SetDefaults()
 	kexOrder := config.KeyExchanges
+	// Based on the discussion in #17230, the key exchange algorithms
+	// diffie-hellman-group-exchange-sha1 and diffie-hellman-group-exchange-sha256
+	// are not included in the default list of supported kex so we have to add them
+	// here manually.
+	kexOrder = append(kexOrder, "diffie-hellman-group-exchange-sha1", "diffie-hellman-group-exchange-sha256")
 	for _, kex := range kexOrder {
-		server := newServer(t)
-		defer server.Shutdown()
-		conf := clientConfig()
-		// Don't fail if sshd doesn't have the kex.
-		conf.KeyExchanges = append([]string{kex}, kexOrder...)
-		conn, err := server.TryDial(conf)
-		if err == nil {
-			conn.Close()
-		} else {
-			t.Errorf("failed for kex %q", kex)
-		}
+		t.Run(kex, func(t *testing.T) {
+			server := newServer(t)
+			defer server.Shutdown()
+			conf := clientConfig()
+			// Don't fail if sshd doesn't have the kex.
+			conf.KeyExchanges = append([]string{kex}, kexOrder...)
+			conn, err := server.TryDial(conf)
+			if err == nil {
+				conn.Close()
+			} else {
+				t.Errorf("failed for kex %q", kex)
+			}
+		})
 	}
 }
 
@@ -424,20 +428,22 @@ func TestClientAuthAlgorithms(t *testing.T) {
 		"ecdsa",
 		"ed25519",
 	} {
-		server := newServer(t)
-		conf := clientConfig()
-		conf.SetDefaults()
-		conf.Auth = []ssh.AuthMethod{
-			ssh.PublicKeys(testSigners[key]),
-		}
-
-		conn, err := server.TryDial(conf)
-		if err == nil {
-			conn.Close()
-		} else {
-			t.Errorf("failed for key %q", key)
-		}
-
-		server.Shutdown()
+		t.Run(key, func(t *testing.T) {
+			server := newServer(t)
+			conf := clientConfig()
+			conf.SetDefaults()
+			conf.Auth = []ssh.AuthMethod{
+				ssh.PublicKeys(testSigners[key]),
+			}
+
+			conn, err := server.TryDial(conf)
+			if err == nil {
+				conn.Close()
+			} else {
+				t.Errorf("failed for key %q", key)
+			}
+
+			server.Shutdown()
+		})
 	}
 }

+ 9 - 2
psiphon/common/crypto/ssh/test/test_unix_test.go

@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build darwin dragonfly freebsd linux netbsd openbsd plan9
+//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || plan9
+// +build aix darwin dragonfly freebsd linux netbsd openbsd plan9
 
 package test
 
@@ -306,7 +307,13 @@ func newServerForConfig(t *testing.T, config string, configVars map[string]strin
 	if err != nil {
 		t.Fatalf("user.Current: %v", err)
 	}
-	if u.Name == "root" {
+	uname := u.Name
+	if uname == "" {
+		// Check the value of u.Username as u.Name
+		// can be "" on some OSes like AIX.
+		uname = u.Username
+	}
+	if uname == "root" {
 		t.Skip("skipping test because current user is root")
 	}
 	dir, err := ioutil.TempDir("", "sshtest")

+ 87 - 4
psiphon/common/crypto/ssh/testdata/keys.go

@@ -113,6 +113,37 @@ pY2QA+P3QlrKxT/VWUMjHUbNNdYfJm48xu0SGNMRdKMAAABBAORh2NP/06JUV3J9W/2Hju
 X1ViJuqqcQnJPVzpgSL826EC2xwOECTqoY8uvFpUdD7CtpksIxNVqRIhuNOlz0lqEAAABB
 ANkaHTTaPojClO0dKJ/Zjs7pWOCGliebBYprQ/Y4r9QLBkC/XaWMS26gFIrjgC7D2Rv+rZ
 wSD0v0RcmkITP1ZR0AAAAYcHF1ZXJuYUBMdWNreUh5ZHJvLmxvY2FsAQID
+-----END OPENSSH PRIVATE KEY-----`),
+	"p256-openssh-format": []byte(`-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS
+1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQSN5Ld/DFy8LJK0yrWg+Ryhq4/ifHry
+QyCQeT4UXSB+UGdRct7kWA0hARbTaSCh+8U/Gs5O+IkDNoTKVsgxKUMQAAAAsO3C7nPtwu
+5zAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBI3kt38MXLwskrTK
+taD5HKGrj+J8evJDIJB5PhRdIH5QZ1Fy3uRYDSEBFtNpIKH7xT8azk74iQM2hMpWyDEpQx
+AAAAAhAIHB48R+goZaiXndfYTrwk4BT1+MeLPC2/dwe0J5d1QDAAAAE21hcmlhbm9AZW5k
+b3IubG9jYWwBAgME
+-----END OPENSSH PRIVATE KEY-----`),
+	"p384-openssh-format": []byte(`-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAiAAAABNlY2RzYS
+1zaGEyLW5pc3RwMzg0AAAACG5pc3RwMzg0AAAAYQTZb2VzEPs2NN/i1qHddKTVfwoIq3Tf
+PeQ/kcWBvuCVJfIygvpm9MeusawEPuLSEXwiNDew+YHZ9xHIvFjCmZsLuEOzuh9t9KotwM
+57H+7N+RDFzhM2j8hAaOuT5XDLKfUAAADgn/Sny5/0p8sAAAATZWNkc2Etc2hhMi1uaXN0
+cDM4NAAAAAhuaXN0cDM4NAAAAGEE2W9lcxD7NjTf4tah3XSk1X8KCKt03z3kP5HFgb7glS
+XyMoL6ZvTHrrGsBD7i0hF8IjQ3sPmB2fcRyLxYwpmbC7hDs7ofbfSqLcDOex/uzfkQxc4T
+No/IQGjrk+Vwyyn1AAAAMQDg0hwGKB/9Eq+e2FeTspi8QHW5xTD6prqsHDFx4cKk0ccgFV
+61dhFhD/8SEbYlHzEAAAATbWFyaWFub0BlbmRvci5sb2NhbAECAwQ=
+-----END OPENSSH PRIVATE KEY-----`),
+	"p521-openssh-format": []byte(`-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAArAAAABNlY2RzYS
+1zaGEyLW5pc3RwNTIxAAAACG5pc3RwNTIxAAAAhQQBKzI3QSp1a2e1zMulZl1uFF1Y2Dnv
+LSIwEu837hOV1epYEgNveAhGNm57TuBqYtnZeVfd2pzaz7CKX6N4B33N1XABQ5Ngji7lF2
+dUbmhNqJoMh43ioIsQNBaBenhmRpYP6f5k8P/7JZMIsLhkJk2hykb8maSZ+B3PYwPMNBdS
+vP+0sHQAAAEYIsr2CCLK9ggAAAATZWNkc2Etc2hhMi1uaXN0cDUyMQAAAAhuaXN0cDUyMQ
+AAAIUEASsyN0EqdWtntczLpWZdbhRdWNg57y0iMBLvN+4TldXqWBIDb3gIRjZue07gamLZ
+2XlX3dqc2s+wil+jeAd9zdVwAUOTYI4u5RdnVG5oTaiaDIeN4qCLEDQWgXp4ZkaWD+n+ZP
+D/+yWTCLC4ZCZNocpG/Jmkmfgdz2MDzDQXUrz/tLB0AAAAQgEdeH+im6iRcP/juTAoeSHo
+ExLtWhgL4JYqRwcOnzCKuLOPjEY/HSOuc+HRrbN9rbjsq+PcPHYe1NnkzXk0IW8hxQAAAB
+NtYXJpYW5vQGVuZG9yLmxvY2FsAQIDBAUGBw==
 -----END OPENSSH PRIVATE KEY-----`),
 	"user": []byte(`-----BEGIN EC PRIVATE KEY-----
 MHcCAQEEILYCAeq8f7V4vSSypRw7pxy8yz3V5W4qg8kSC3zJhqpQoAoGCCqGSM49
@@ -155,7 +186,7 @@ var SSHCertificates = map[string][]byte{
 	// Generated by the following commands:
 	//
 	// 1. Assumes "rsa" key above in file named "rsa", write out the public key to "rsa.pub":
-	//    ssh-keygen -y -f rsa > rsa.pu
+	//    ssh-keygen -y -f rsa > rsa.pub
 	//
 	// 2. Assumes "ca" key above in file named "ca", sign a cert for "rsa.pub":
 	//    ssh-keygen -s ca -h -n host.example.com -V +500w -I host.example.com-key rsa.pub
@@ -164,9 +195,10 @@ var SSHCertificates = map[string][]byte{
 }
 
 var PEMEncryptedKeys = []struct {
-	Name          string
-	EncryptionKey string
-	PEMBytes      []byte
+	Name              string
+	EncryptionKey     string
+	IncludesPublicKey bool
+	PEMBytes          []byte
 }{
 	0: {
 		Name:          "rsa-encrypted",
@@ -224,4 +256,55 @@ IiHM7GBn+0nJoKTXsOGMIBe3ulKlKVxLjEuk9yivh/8=
 -----END DSA PRIVATE KEY-----
 `),
 	},
+
+	2: {
+		Name:              "ed25519-encrypted",
+		EncryptionKey:     "password",
+		IncludesPublicKey: true,
+		PEMBytes: []byte(`-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABDKj29BlC
+ocEWuVhQ94/RjoAAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIIw1gSurPTDwZidA
+2AIjQZgoQi3IFn9jBtFdP10/Jj7DAAAAoFGkQbB2teSU7ikUsnc7ct2aH3pitM359lNVUh
+7DQbJWMjbQFbrBYyDJP+ALj1/RZmP2yoIf7/wr99q53/pm28Xp1gGP5V2RGRJYCA6kgFIH
+xdB6KEw1Ce7Bz8JaDIeagAGd3xtQTH3cuuleVxCZZnk9NspsPxigADKCls/RUiK7F+z3Qf
+Lvs9+PH8nIuhFMYZgo3liqZbVS5z4Fqhyzyq4=
+-----END OPENSSH PRIVATE KEY-----
+`),
+	},
+
+	3: {
+		Name:              "ed25519-encrypted-cbc",
+		EncryptionKey:     "password",
+		IncludesPublicKey: true,
+		PEMBytes: []byte(`-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jYmMAAAAGYmNyeXB0AAAAGAAAABDzGKF3uX
+G1gXALZKFd6Ir4AAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIDne4/teO42zTDdj
+NwxUMNpbfmp/dxgU4ZNkC3ydgcugAAAAoJ3J/oA7+iqVOz0CIUUk9ufdP1VP4jDf2um+0s
+Sgs7x6Gpyjq67Ps7wLRdSmxr/G5b+Z8dRGFYS/wUCQEe3whwuImvLyPwWjXLzkAyMzc01f
+ywBGSrHnvP82ppenc2HuTI+E05Xc02i6JVyI1ShiekQL5twoqtR6pEBZnD17UonIx7cRzZ
+gbDGyT3bXMQtagvCwoW+/oMTKXiZP5jCJpEO8=
+-----END OPENSSH PRIVATE KEY-----
+`),
+	},
+}
+
+// SKData contains a list of PubKeys backed by U2F/FIDO2 Security Keys and their test data.
+var SKData = []struct {
+	Name         string
+	PubKey       []byte
+	HexData      []byte
+	HexSignature []byte
+}{
+	{
+		Name:         "sk-ecdsa-sha2-nistp256@openssh.com",
+		PubKey:       []byte("sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBGRNqlFgED/pf4zXz8IzqA6CALNwYcwgd4MQDmIS1GOtn1SySFObiuyJaOlpqkV5FeEifhxfIC2ejKKtNyO4CysAAAAEc3NoOg== user@host"),
+		HexData:      []byte("00000020A4DE1F50DE0EF3F66DCD156C78F5C93B07EEE89D5B5A6531656E835FA1C87B323200000006736B696E6E650000000E7373682D636F6E6E656374696F6E000000097075626C69636B65790100000022736B2D65636473612D736861322D6E69737470323536406F70656E7373682E636F6D0000007F00000022736B2D65636473612D736861322D6E69737470323536406F70656E7373682E636F6D000000086E697374703235360000004104644DAA5160103FE97F8CD7CFC233A80E8200B37061CC207783100E6212D463AD9F54B248539B8AEC8968E969AA457915E1227E1C5F202D9E8CA2AD3723B80B2B000000047373683A"),
+		HexSignature: []byte("0000007800000022736B2D65636473612D736861322D6E69737470323536406F70656E7373682E636F6D000000490000002016CC1A3070E180621CB206C2C6313D1CC5F094DB844A61D06001E243C608875F0000002100E4BD45D6B9DAA11489AEA8D76C222AA3FD6D50FBFFDA8049526D5D61F63B2C5601000000F9"),
+	},
+	{
+		Name:         "sk-ssh-ed25519@openssh.com",
+		PubKey:       []byte("sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIJjzc2a20RjCvN/0ibH6UpGuN9F9hDvD7x182bOesNhHAAAABHNzaDo= user@host"),
+		HexData:      []byte("000000204CFE6EA65CCB99B69348339165C7F38E359D95807A377EEE8E603C71DC3316FA3200000006736B696E6E650000000E7373682D636F6E6E656374696F6E000000097075626C69636B6579010000001A736B2D7373682D65643235353139406F70656E7373682E636F6D0000004A0000001A736B2D7373682D65643235353139406F70656E7373682E636F6D0000002098F37366B6D118C2BCDFF489B1FA5291AE37D17D843BC3EF1D7CD9B39EB0D847000000047373683A"),
+		HexSignature: []byte("000000670000001A736B2D7373682D65643235353139406F70656E7373682E636F6D000000404BF5CA0CAA553099306518732317B3FE4BA6C75365BC0CB02019FBE65A1647016CBD7A682C26928DF234C378ADDBC5077B47F72381144840BF00FB2DA2FB6A0A010000009E"),
+	},
 }

+ 6 - 6
psiphon/common/crypto/ssh/transport.go

@@ -53,14 +53,14 @@ type transport struct {
 // packetCipher represents a combination of SSH encryption/MAC
 // protocol.  A single instance should be used for one direction only.
 type packetCipher interface {
-	// writePacket encrypts the packet and writes it to w. The
+	// writeCipherPacket encrypts the packet and writes it to w. The
 	// contents of the packet are generally scrambled.
-	writePacket(seqnum uint32, w io.Writer, rand io.Reader, packet []byte) error
+	writeCipherPacket(seqnum uint32, w io.Writer, rand io.Reader, packet []byte) error
 
-	// readPacket reads and decrypts a packet of data. The
+	// readCipherPacket reads and decrypts a packet of data. The
 	// returned packet may be overwritten by future calls of
 	// readPacket.
-	readPacket(seqnum uint32, r io.Reader) ([]byte, error)
+	readCipherPacket(seqnum uint32, r io.Reader) ([]byte, error)
 }
 
 // connectionState represents one side (read or write) of the
@@ -127,7 +127,7 @@ func (t *transport) readPacket() (p []byte, err error) {
 }
 
 func (s *connectionState) readPacket(r *bufio.Reader) ([]byte, error) {
-	packet, err := s.packetCipher.readPacket(s.seqNum, r)
+	packet, err := s.packetCipher.readCipherPacket(s.seqNum, r)
 	s.seqNum++
 	if err == nil && len(packet) == 0 {
 		err = errors.New("ssh: zero length packet")
@@ -175,7 +175,7 @@ func (t *transport) writePacket(packet []byte) error {
 func (s *connectionState) writePacket(w *bufio.Writer, rand io.Reader, packet []byte) error {
 	changeKeys := len(packet) > 0 && packet[0] == msgNewKeys
 
-	err := s.packetCipher.writePacket(s.seqNum, w, rand, packet)
+	err := s.packetCipher.writeCipherPacket(s.seqNum, w, rand, packet)
 	if err != nil {
 		return err
 	}

+ 159 - 0
vendor/golang.org/x/crypto/blowfish/block.go

@@ -0,0 +1,159 @@
+// Copyright 2010 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 blowfish
+
+// getNextWord returns the next big-endian uint32 value from the byte slice
+// at the given position in a circular manner, updating the position.
+func getNextWord(b []byte, pos *int) uint32 {
+	var w uint32
+	j := *pos
+	for i := 0; i < 4; i++ {
+		w = w<<8 | uint32(b[j])
+		j++
+		if j >= len(b) {
+			j = 0
+		}
+	}
+	*pos = j
+	return w
+}
+
+// ExpandKey performs a key expansion on the given *Cipher. Specifically, it
+// performs the Blowfish algorithm's key schedule which sets up the *Cipher's
+// pi and substitution tables for calls to Encrypt. This is used, primarily,
+// by the bcrypt package to reuse the Blowfish key schedule during its
+// set up. It's unlikely that you need to use this directly.
+func ExpandKey(key []byte, c *Cipher) {
+	j := 0
+	for i := 0; i < 18; i++ {
+		// Using inlined getNextWord for performance.
+		var d uint32
+		for k := 0; k < 4; k++ {
+			d = d<<8 | uint32(key[j])
+			j++
+			if j >= len(key) {
+				j = 0
+			}
+		}
+		c.p[i] ^= d
+	}
+
+	var l, r uint32
+	for i := 0; i < 18; i += 2 {
+		l, r = encryptBlock(l, r, c)
+		c.p[i], c.p[i+1] = l, r
+	}
+
+	for i := 0; i < 256; i += 2 {
+		l, r = encryptBlock(l, r, c)
+		c.s0[i], c.s0[i+1] = l, r
+	}
+	for i := 0; i < 256; i += 2 {
+		l, r = encryptBlock(l, r, c)
+		c.s1[i], c.s1[i+1] = l, r
+	}
+	for i := 0; i < 256; i += 2 {
+		l, r = encryptBlock(l, r, c)
+		c.s2[i], c.s2[i+1] = l, r
+	}
+	for i := 0; i < 256; i += 2 {
+		l, r = encryptBlock(l, r, c)
+		c.s3[i], c.s3[i+1] = l, r
+	}
+}
+
+// This is similar to ExpandKey, but folds the salt during the key
+// schedule. While ExpandKey is essentially expandKeyWithSalt with an all-zero
+// salt passed in, reusing ExpandKey turns out to be a place of inefficiency
+// and specializing it here is useful.
+func expandKeyWithSalt(key []byte, salt []byte, c *Cipher) {
+	j := 0
+	for i := 0; i < 18; i++ {
+		c.p[i] ^= getNextWord(key, &j)
+	}
+
+	j = 0
+	var l, r uint32
+	for i := 0; i < 18; i += 2 {
+		l ^= getNextWord(salt, &j)
+		r ^= getNextWord(salt, &j)
+		l, r = encryptBlock(l, r, c)
+		c.p[i], c.p[i+1] = l, r
+	}
+
+	for i := 0; i < 256; i += 2 {
+		l ^= getNextWord(salt, &j)
+		r ^= getNextWord(salt, &j)
+		l, r = encryptBlock(l, r, c)
+		c.s0[i], c.s0[i+1] = l, r
+	}
+
+	for i := 0; i < 256; i += 2 {
+		l ^= getNextWord(salt, &j)
+		r ^= getNextWord(salt, &j)
+		l, r = encryptBlock(l, r, c)
+		c.s1[i], c.s1[i+1] = l, r
+	}
+
+	for i := 0; i < 256; i += 2 {
+		l ^= getNextWord(salt, &j)
+		r ^= getNextWord(salt, &j)
+		l, r = encryptBlock(l, r, c)
+		c.s2[i], c.s2[i+1] = l, r
+	}
+
+	for i := 0; i < 256; i += 2 {
+		l ^= getNextWord(salt, &j)
+		r ^= getNextWord(salt, &j)
+		l, r = encryptBlock(l, r, c)
+		c.s3[i], c.s3[i+1] = l, r
+	}
+}
+
+func encryptBlock(l, r uint32, c *Cipher) (uint32, uint32) {
+	xl, xr := l, r
+	xl ^= c.p[0]
+	xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[1]
+	xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[2]
+	xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[3]
+	xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[4]
+	xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[5]
+	xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[6]
+	xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[7]
+	xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[8]
+	xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[9]
+	xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[10]
+	xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[11]
+	xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[12]
+	xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[13]
+	xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[14]
+	xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[15]
+	xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[16]
+	xr ^= c.p[17]
+	return xr, xl
+}
+
+func decryptBlock(l, r uint32, c *Cipher) (uint32, uint32) {
+	xl, xr := l, r
+	xl ^= c.p[17]
+	xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[16]
+	xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[15]
+	xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[14]
+	xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[13]
+	xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[12]
+	xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[11]
+	xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[10]
+	xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[9]
+	xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[8]
+	xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[7]
+	xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[6]
+	xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[5]
+	xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[4]
+	xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[3]
+	xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[2]
+	xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[1]
+	xr ^= c.p[0]
+	return xr, xl
+}

+ 99 - 0
vendor/golang.org/x/crypto/blowfish/cipher.go

@@ -0,0 +1,99 @@
+// Copyright 2010 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 blowfish implements Bruce Schneier's Blowfish encryption algorithm.
+//
+// Blowfish is a legacy cipher and its short block size makes it vulnerable to
+// birthday bound attacks (see https://sweet32.info). It should only be used
+// where compatibility with legacy systems, not security, is the goal.
+//
+// Deprecated: any new system should use AES (from crypto/aes, if necessary in
+// an AEAD mode like crypto/cipher.NewGCM) or XChaCha20-Poly1305 (from
+// golang.org/x/crypto/chacha20poly1305).
+package blowfish // import "golang.org/x/crypto/blowfish"
+
+// The code is a port of Bruce Schneier's C implementation.
+// See https://www.schneier.com/blowfish.html.
+
+import "strconv"
+
+// The Blowfish block size in bytes.
+const BlockSize = 8
+
+// A Cipher is an instance of Blowfish encryption using a particular key.
+type Cipher struct {
+	p              [18]uint32
+	s0, s1, s2, s3 [256]uint32
+}
+
+type KeySizeError int
+
+func (k KeySizeError) Error() string {
+	return "crypto/blowfish: invalid key size " + strconv.Itoa(int(k))
+}
+
+// NewCipher creates and returns a Cipher.
+// The key argument should be the Blowfish key, from 1 to 56 bytes.
+func NewCipher(key []byte) (*Cipher, error) {
+	var result Cipher
+	if k := len(key); k < 1 || k > 56 {
+		return nil, KeySizeError(k)
+	}
+	initCipher(&result)
+	ExpandKey(key, &result)
+	return &result, nil
+}
+
+// NewSaltedCipher creates a returns a Cipher that folds a salt into its key
+// schedule. For most purposes, NewCipher, instead of NewSaltedCipher, is
+// sufficient and desirable. For bcrypt compatibility, the key can be over 56
+// bytes.
+func NewSaltedCipher(key, salt []byte) (*Cipher, error) {
+	if len(salt) == 0 {
+		return NewCipher(key)
+	}
+	var result Cipher
+	if k := len(key); k < 1 {
+		return nil, KeySizeError(k)
+	}
+	initCipher(&result)
+	expandKeyWithSalt(key, salt, &result)
+	return &result, nil
+}
+
+// BlockSize returns the Blowfish block size, 8 bytes.
+// It is necessary to satisfy the Block interface in the
+// package "crypto/cipher".
+func (c *Cipher) BlockSize() int { return BlockSize }
+
+// Encrypt encrypts the 8-byte buffer src using the key k
+// and stores the result in dst.
+// Note that for amounts of data larger than a block,
+// it is not safe to just call Encrypt on successive blocks;
+// instead, use an encryption mode like CBC (see crypto/cipher/cbc.go).
+func (c *Cipher) Encrypt(dst, src []byte) {
+	l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3])
+	r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7])
+	l, r = encryptBlock(l, r, c)
+	dst[0], dst[1], dst[2], dst[3] = byte(l>>24), byte(l>>16), byte(l>>8), byte(l)
+	dst[4], dst[5], dst[6], dst[7] = byte(r>>24), byte(r>>16), byte(r>>8), byte(r)
+}
+
+// Decrypt decrypts the 8-byte buffer src using the key k
+// and stores the result in dst.
+func (c *Cipher) Decrypt(dst, src []byte) {
+	l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3])
+	r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7])
+	l, r = decryptBlock(l, r, c)
+	dst[0], dst[1], dst[2], dst[3] = byte(l>>24), byte(l>>16), byte(l>>8), byte(l)
+	dst[4], dst[5], dst[6], dst[7] = byte(r>>24), byte(r>>16), byte(r>>8), byte(r)
+}
+
+func initCipher(c *Cipher) {
+	copy(c.p[0:], p[0:])
+	copy(c.s0[0:], s0[0:])
+	copy(c.s1[0:], s1[0:])
+	copy(c.s2[0:], s2[0:])
+	copy(c.s3[0:], s3[0:])
+}

+ 199 - 0
vendor/golang.org/x/crypto/blowfish/const.go

@@ -0,0 +1,199 @@
+// Copyright 2010 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.
+
+// The startup permutation array and substitution boxes.
+// They are the hexadecimal digits of PI; see:
+// https://www.schneier.com/code/constants.txt.
+
+package blowfish
+
+var s0 = [256]uint32{
+	0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96,
+	0xba7c9045, 0xf12c7f99, 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16,
+	0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, 0x0d95748f, 0x728eb658,
+	0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
+	0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e,
+	0x6c9e0e8b, 0xb01e8a3e, 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60,
+	0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, 0x55ca396a, 0x2aab10b6,
+	0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,
+	0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c,
+	0x7a325381, 0x28958677, 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193,
+	0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, 0xef845d5d, 0xe98575b1,
+	0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,
+	0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a,
+	0x670c9c61, 0xabd388f0, 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3,
+	0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, 0xa1f1651d, 0x39af0176,
+	0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
+	0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706,
+	0x1bfedf72, 0x429b023d, 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b,
+	0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, 0xe3fe501a, 0xb6794c3b,
+	0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,
+	0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f, 0x9b30952c,
+	0xcc814544, 0xaf5ebd09, 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3,
+	0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, 0x5579c0bd, 0x1a60320a,
+	0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,
+	0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760,
+	0x53317b48, 0x3e00df82, 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db,
+	0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, 0x695b27b0, 0xbbca58c8,
+	0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
+	0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33,
+	0x62fb1341, 0xcee4c6e8, 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4,
+	0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, 0xd08ed1d0, 0xafc725e0,
+	0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,
+	0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777,
+	0xea752dfe, 0x8b021fa1, 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299,
+	0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, 0x165fa266, 0x80957705,
+	0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
+	0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d, 0x2071b35e,
+	0x226800bb, 0x57b8e0af, 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa,
+	0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, 0x83260376, 0x6295cfa9,
+	0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
+	0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 0x571be91f,
+	0xf296ec6b, 0x2a0dd915, 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664,
+	0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a,
+}
+
+var s1 = [256]uint32{
+	0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d,
+	0x9cee60b8, 0x8fedb266, 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1,
+	0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, 0x3f54989a, 0x5b429d65,
+	0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
+	0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f, 0x3ebaefc9,
+	0x3c971814, 0x6b6a70a1, 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737,
+	0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, 0xb03ada37, 0xf0500c0d,
+	0xf01c1f04, 0x0200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,
+	0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc,
+	0xc8b57634, 0x9af3dda7, 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41,
+	0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, 0x4e548b38, 0x4f6db908,
+	0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
+	0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124,
+	0x501adde6, 0x9f84cd87, 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c,
+	0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, 0xef1c1847, 0x3215d908,
+	0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,
+	0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x043556f1, 0xd7a3c76b,
+	0x3c11183b, 0x5924a509, 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e,
+	0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, 0x771fe71c, 0x4e3d06fa,
+	0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,
+	0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d,
+	0x1939260f, 0x19c27960, 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66,
+	0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, 0xc332ddef, 0xbe6c5aa5,
+	0x65582185, 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
+	0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96,
+	0x0334fe1e, 0xaa0363cf, 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14,
+	0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, 0x648b1eaf, 0x19bdf0ca,
+	0xa02369b9, 0x655abb50, 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
+	0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77,
+	0x11ed935f, 0x16681281, 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99,
+	0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, 0xcdb30aeb, 0x532e3054,
+	0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
+	0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea,
+	0xdb6c4f15, 0xfacb4fd0, 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105,
+	0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, 0xcf62a1f2, 0x5b8d2646,
+	0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
+	0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea,
+	0x1dadf43e, 0x233f7061, 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb,
+	0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, 0xa6078084, 0x19f8509e,
+	0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
+	0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3, 0x105588cd,
+	0x675fda79, 0xe3674340, 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20,
+	0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7,
+}
+
+var s2 = [256]uint32{
+	0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7,
+	0xbcf46b2e, 0xd4a20068, 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af,
+	0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, 0x4d95fc1d, 0x96b591af,
+	0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,
+	0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4,
+	0x0a2c86da, 0xe9b66dfb, 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee,
+	0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, 0xaace1e7c, 0xd3375fec,
+	0xce78a399, 0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
+	0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74, 0xdd5b4332,
+	0x6841e7f7, 0xca7820fb, 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527,
+	0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, 0x55a867bc, 0xa1159a58,
+	0xcca92963, 0x99e1db33, 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,
+	0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22,
+	0x48c1133f, 0xc70f86dc, 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17,
+	0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, 0x257b7834, 0x602a9c60,
+	0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,
+	0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99,
+	0xde720c8c, 0x2da2f728, 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0,
+	0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, 0x0a476341, 0x992eff74,
+	0x3a6f6eab, 0xf4f8fd37, 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
+	0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, 0xf1290dc7, 0xcc00ffa3,
+	0xb5390f92, 0x690fed0b, 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3,
+	0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, 0x37392eb3, 0xcc115979,
+	0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,
+	0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018, 0x11caedfa,
+	0x3d25bdd8, 0xe2e1c3c9, 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a,
+	0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, 0x9dbc8057, 0xf0f7c086,
+	0x60787bf8, 0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,
+	0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24,
+	0x55464299, 0xbf582e61, 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2,
+	0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, 0x7aeb2661, 0x8b1ddf84,
+	0x846a0e79, 0x915f95e2, 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,
+	0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09,
+	0x662d09a1, 0xc4324633, 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10,
+	0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, 0xdcb7da83, 0x573906fe,
+	0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,
+	0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0,
+	0x006058aa, 0x30dc7d62, 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634,
+	0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, 0x6f05e409, 0x4b7c0188,
+	0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
+	0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8,
+	0xa28514d9, 0x6c51133c, 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837,
+	0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0,
+}
+
+var s3 = [256]uint32{
+	0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742,
+	0xd3822740, 0x99bc9bbe, 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b,
+	0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, 0x5748ab2f, 0xbc946e79,
+	0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
+	0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 0xa1fad5f0, 0x6a2d519a,
+	0x63ef8ce2, 0x9a86ee22, 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4,
+	0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, 0x2826a2f9, 0xa73a3ae1,
+	0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,
+	0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797,
+	0x2cf0b7d9, 0x022b8b51, 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28,
+	0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, 0xe029ac71, 0xe019a5e6,
+	0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,
+	0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4, 0x88f46dba,
+	0x03a16125, 0x0564f0bd, 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a,
+	0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, 0x7533d928, 0xb155fdf5,
+	0x03563482, 0x8aba3cbb, 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,
+	0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce,
+	0x5121ce64, 0x774fbe32, 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680,
+	0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, 0xb39a460a, 0x6445c0dd,
+	0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
+	0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb,
+	0x8d6612ae, 0xbf3c6f47, 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370,
+	0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, 0x4040cb08, 0x4eb4e2cc,
+	0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,
+	0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, 0x611560b1, 0xe7933fdc,
+	0xbb3a792b, 0x344525bd, 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9,
+	0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, 0x1a908749, 0xd44fbd9a,
+	0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,
+	0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c, 0x15e6fc2a,
+	0x0f91fc71, 0x9b941525, 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1,
+	0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, 0xe0ec6e0e, 0x1698db3b,
+	0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,
+	0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d, 0x9b992f2e,
+	0xe60b6f47, 0x0fe3f11d, 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f,
+	0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, 0xf523f357, 0xa6327623,
+	0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,
+	0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a,
+	0x45e1d006, 0xc3f27b9a, 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6,
+	0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, 0x53113ec0, 0x1640e3d3,
+	0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,
+	0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c,
+	0x01c36ae4, 0xd6ebe1f9, 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f,
+	0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6,
+}
+
+var p = [18]uint32{
+	0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0,
+	0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
+	0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 0x9216d5d9, 0x8979fb1b,
+}

+ 223 - 0
vendor/golang.org/x/crypto/ed25519/ed25519.go

@@ -0,0 +1,223 @@
+// 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.
+
+// In Go 1.13, the ed25519 package was promoted to the standard library as
+// crypto/ed25519, and this package became a wrapper for the standard library one.
+//
+//go:build !go1.13
+// +build !go1.13
+
+// Package ed25519 implements the Ed25519 signature algorithm. See
+// https://ed25519.cr.yp.to/.
+//
+// These functions are also compatible with the “Ed25519” function defined in
+// RFC 8032. However, unlike RFC 8032's formulation, this package's private key
+// representation includes a public key suffix to make multiple signing
+// operations with the same key more efficient. This package refers to the RFC
+// 8032 private key as the “seed”.
+package ed25519
+
+// This code is a port of the public domain, “ref10” implementation of ed25519
+// from SUPERCOP.
+
+import (
+	"bytes"
+	"crypto"
+	cryptorand "crypto/rand"
+	"crypto/sha512"
+	"errors"
+	"io"
+	"strconv"
+
+	"golang.org/x/crypto/ed25519/internal/edwards25519"
+)
+
+const (
+	// PublicKeySize is the size, in bytes, of public keys as used in this package.
+	PublicKeySize = 32
+	// PrivateKeySize is the size, in bytes, of private keys as used in this package.
+	PrivateKeySize = 64
+	// SignatureSize is the size, in bytes, of signatures generated and verified by this package.
+	SignatureSize = 64
+	// SeedSize is the size, in bytes, of private key seeds. These are the private key representations used by RFC 8032.
+	SeedSize = 32
+)
+
+// PublicKey is the type of Ed25519 public keys.
+type PublicKey []byte
+
+// PrivateKey is the type of Ed25519 private keys. It implements crypto.Signer.
+type PrivateKey []byte
+
+// Public returns the PublicKey corresponding to priv.
+func (priv PrivateKey) Public() crypto.PublicKey {
+	publicKey := make([]byte, PublicKeySize)
+	copy(publicKey, priv[32:])
+	return PublicKey(publicKey)
+}
+
+// Seed returns the private key seed corresponding to priv. It is provided for
+// interoperability with RFC 8032. RFC 8032's private keys correspond to seeds
+// in this package.
+func (priv PrivateKey) Seed() []byte {
+	seed := make([]byte, SeedSize)
+	copy(seed, priv[:32])
+	return seed
+}
+
+// Sign signs the given message with priv.
+// Ed25519 performs two passes over messages to be signed and therefore cannot
+// handle pre-hashed messages. Thus opts.HashFunc() must return zero to
+// indicate the message hasn't been hashed. This can be achieved by passing
+// crypto.Hash(0) as the value for opts.
+func (priv PrivateKey) Sign(rand io.Reader, message []byte, opts crypto.SignerOpts) (signature []byte, err error) {
+	if opts.HashFunc() != crypto.Hash(0) {
+		return nil, errors.New("ed25519: cannot sign hashed message")
+	}
+
+	return Sign(priv, message), nil
+}
+
+// GenerateKey generates a public/private key pair using entropy from rand.
+// If rand is nil, crypto/rand.Reader will be used.
+func GenerateKey(rand io.Reader) (PublicKey, PrivateKey, error) {
+	if rand == nil {
+		rand = cryptorand.Reader
+	}
+
+	seed := make([]byte, SeedSize)
+	if _, err := io.ReadFull(rand, seed); err != nil {
+		return nil, nil, err
+	}
+
+	privateKey := NewKeyFromSeed(seed)
+	publicKey := make([]byte, PublicKeySize)
+	copy(publicKey, privateKey[32:])
+
+	return publicKey, privateKey, nil
+}
+
+// NewKeyFromSeed calculates a private key from a seed. It will panic if
+// len(seed) is not SeedSize. This function is provided for interoperability
+// with RFC 8032. RFC 8032's private keys correspond to seeds in this
+// package.
+func NewKeyFromSeed(seed []byte) PrivateKey {
+	if l := len(seed); l != SeedSize {
+		panic("ed25519: bad seed length: " + strconv.Itoa(l))
+	}
+
+	digest := sha512.Sum512(seed)
+	digest[0] &= 248
+	digest[31] &= 127
+	digest[31] |= 64
+
+	var A edwards25519.ExtendedGroupElement
+	var hBytes [32]byte
+	copy(hBytes[:], digest[:])
+	edwards25519.GeScalarMultBase(&A, &hBytes)
+	var publicKeyBytes [32]byte
+	A.ToBytes(&publicKeyBytes)
+
+	privateKey := make([]byte, PrivateKeySize)
+	copy(privateKey, seed)
+	copy(privateKey[32:], publicKeyBytes[:])
+
+	return privateKey
+}
+
+// Sign signs the message with privateKey and returns a signature. It will
+// panic if len(privateKey) is not PrivateKeySize.
+func Sign(privateKey PrivateKey, message []byte) []byte {
+	if l := len(privateKey); l != PrivateKeySize {
+		panic("ed25519: bad private key length: " + strconv.Itoa(l))
+	}
+
+	h := sha512.New()
+	h.Write(privateKey[:32])
+
+	var digest1, messageDigest, hramDigest [64]byte
+	var expandedSecretKey [32]byte
+	h.Sum(digest1[:0])
+	copy(expandedSecretKey[:], digest1[:])
+	expandedSecretKey[0] &= 248
+	expandedSecretKey[31] &= 63
+	expandedSecretKey[31] |= 64
+
+	h.Reset()
+	h.Write(digest1[32:])
+	h.Write(message)
+	h.Sum(messageDigest[:0])
+
+	var messageDigestReduced [32]byte
+	edwards25519.ScReduce(&messageDigestReduced, &messageDigest)
+	var R edwards25519.ExtendedGroupElement
+	edwards25519.GeScalarMultBase(&R, &messageDigestReduced)
+
+	var encodedR [32]byte
+	R.ToBytes(&encodedR)
+
+	h.Reset()
+	h.Write(encodedR[:])
+	h.Write(privateKey[32:])
+	h.Write(message)
+	h.Sum(hramDigest[:0])
+	var hramDigestReduced [32]byte
+	edwards25519.ScReduce(&hramDigestReduced, &hramDigest)
+
+	var s [32]byte
+	edwards25519.ScMulAdd(&s, &hramDigestReduced, &expandedSecretKey, &messageDigestReduced)
+
+	signature := make([]byte, SignatureSize)
+	copy(signature[:], encodedR[:])
+	copy(signature[32:], s[:])
+
+	return signature
+}
+
+// Verify reports whether sig is a valid signature of message by publicKey. It
+// will panic if len(publicKey) is not PublicKeySize.
+func Verify(publicKey PublicKey, message, sig []byte) bool {
+	if l := len(publicKey); l != PublicKeySize {
+		panic("ed25519: bad public key length: " + strconv.Itoa(l))
+	}
+
+	if len(sig) != SignatureSize || sig[63]&224 != 0 {
+		return false
+	}
+
+	var A edwards25519.ExtendedGroupElement
+	var publicKeyBytes [32]byte
+	copy(publicKeyBytes[:], publicKey)
+	if !A.FromBytes(&publicKeyBytes) {
+		return false
+	}
+	edwards25519.FeNeg(&A.X, &A.X)
+	edwards25519.FeNeg(&A.T, &A.T)
+
+	h := sha512.New()
+	h.Write(sig[:32])
+	h.Write(publicKey[:])
+	h.Write(message)
+	var digest [64]byte
+	h.Sum(digest[:0])
+
+	var hReduced [32]byte
+	edwards25519.ScReduce(&hReduced, &digest)
+
+	var R edwards25519.ProjectiveGroupElement
+	var s [32]byte
+	copy(s[:], sig[32:])
+
+	// https://tools.ietf.org/html/rfc8032#section-5.1.7 requires that s be in
+	// the range [0, order) in order to prevent signature malleability.
+	if !edwards25519.ScMinimal(&s) {
+		return false
+	}
+
+	edwards25519.GeDoubleScalarMultVartime(&R, &hReduced, &A, &s)
+
+	var checkR [32]byte
+	R.ToBytes(&checkR)
+	return bytes.Equal(sig[:32], checkR[:])
+}

+ 74 - 0
vendor/golang.org/x/crypto/ed25519/ed25519_go113.go

@@ -0,0 +1,74 @@
+// Copyright 2019 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.
+
+//go:build go1.13
+// +build go1.13
+
+// Package ed25519 implements the Ed25519 signature algorithm. See
+// https://ed25519.cr.yp.to/.
+//
+// These functions are also compatible with the “Ed25519” function defined in
+// 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”.
+//
+// Beginning with Go 1.13, the functionality of this package was moved to the
+// standard library as crypto/ed25519. This package only acts as a compatibility
+// wrapper.
+package ed25519
+
+import (
+	"crypto/ed25519"
+	"io"
+)
+
+const (
+	// PublicKeySize is the size, in bytes, of public keys as used in this package.
+	PublicKeySize = 32
+	// PrivateKeySize is the size, in bytes, of private keys as used in this package.
+	PrivateKeySize = 64
+	// SignatureSize is the size, in bytes, of signatures generated and verified by this package.
+	SignatureSize = 64
+	// SeedSize is the size, in bytes, of private key seeds. These are the private key representations used by RFC 8032.
+	SeedSize = 32
+)
+
+// PublicKey is the type of Ed25519 public keys.
+//
+// This type is an alias for crypto/ed25519's PublicKey type.
+// See the crypto/ed25519 package for the methods on this type.
+type PublicKey = ed25519.PublicKey
+
+// PrivateKey is the type of Ed25519 private keys. It implements crypto.Signer.
+//
+// This type is an alias for crypto/ed25519's PrivateKey type.
+// See the crypto/ed25519 package for the methods on this type.
+type PrivateKey = ed25519.PrivateKey
+
+// GenerateKey generates a public/private key pair using entropy from rand.
+// If rand is nil, crypto/rand.Reader will be used.
+func GenerateKey(rand io.Reader) (PublicKey, PrivateKey, error) {
+	return ed25519.GenerateKey(rand)
+}
+
+// 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 {
+	return ed25519.NewKeyFromSeed(seed)
+}
+
+// Sign signs the message with privateKey and returns a signature. It will
+// panic if len(privateKey) is not PrivateKeySize.
+func Sign(privateKey PrivateKey, message []byte) []byte {
+	return ed25519.Sign(privateKey, message)
+}
+
+// Verify reports whether sig is a valid signature of message by publicKey. It
+// will panic if len(publicKey) is not PublicKeySize.
+func Verify(publicKey PublicKey, message, sig []byte) bool {
+	return ed25519.Verify(publicKey, message, sig)
+}

+ 1422 - 0
vendor/golang.org/x/crypto/ed25519/internal/edwards25519/const.go

@@ -0,0 +1,1422 @@
+// 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 edwards25519
+
+// These values are from the public domain, “ref10” implementation of ed25519
+// from SUPERCOP.
+
+// d is a constant in the Edwards curve equation.
+var d = FieldElement{
+	-10913610, 13857413, -15372611, 6949391, 114729, -8787816, -6275908, -3247719, -18696448, -12055116,
+}
+
+// d2 is 2*d.
+var d2 = FieldElement{
+	-21827239, -5839606, -30745221, 13898782, 229458, 15978800, -12551817, -6495438, 29715968, 9444199,
+}
+
+// SqrtM1 is the square-root of -1 in the field.
+var SqrtM1 = FieldElement{
+	-32595792, -7943725, 9377950, 3500415, 12389472, -272473, -25146209, -2005654, 326686, 11406482,
+}
+
+// A is a constant in the Montgomery-form of curve25519.
+var A = FieldElement{
+	486662, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+}
+
+// bi contains precomputed multiples of the base-point. See the Ed25519 paper
+// for a discussion about how these values are used.
+var bi = [8]PreComputedGroupElement{
+	{
+		FieldElement{25967493, -14356035, 29566456, 3660896, -12694345, 4014787, 27544626, -11754271, -6079156, 2047605},
+		FieldElement{-12545711, 934262, -2722910, 3049990, -727428, 9406986, 12720692, 5043384, 19500929, -15469378},
+		FieldElement{-8738181, 4489570, 9688441, -14785194, 10184609, -12363380, 29287919, 11864899, -24514362, -4438546},
+	},
+	{
+		FieldElement{15636291, -9688557, 24204773, -7912398, 616977, -16685262, 27787600, -14772189, 28944400, -1550024},
+		FieldElement{16568933, 4717097, -11556148, -1102322, 15682896, -11807043, 16354577, -11775962, 7689662, 11199574},
+		FieldElement{30464156, -5976125, -11779434, -15670865, 23220365, 15915852, 7512774, 10017326, -17749093, -9920357},
+	},
+	{
+		FieldElement{10861363, 11473154, 27284546, 1981175, -30064349, 12577861, 32867885, 14515107, -15438304, 10819380},
+		FieldElement{4708026, 6336745, 20377586, 9066809, -11272109, 6594696, -25653668, 12483688, -12668491, 5581306},
+		FieldElement{19563160, 16186464, -29386857, 4097519, 10237984, -4348115, 28542350, 13850243, -23678021, -15815942},
+	},
+	{
+		FieldElement{5153746, 9909285, 1723747, -2777874, 30523605, 5516873, 19480852, 5230134, -23952439, -15175766},
+		FieldElement{-30269007, -3463509, 7665486, 10083793, 28475525, 1649722, 20654025, 16520125, 30598449, 7715701},
+		FieldElement{28881845, 14381568, 9657904, 3680757, -20181635, 7843316, -31400660, 1370708, 29794553, -1409300},
+	},
+	{
+		FieldElement{-22518993, -6692182, 14201702, -8745502, -23510406, 8844726, 18474211, -1361450, -13062696, 13821877},
+		FieldElement{-6455177, -7839871, 3374702, -4740862, -27098617, -10571707, 31655028, -7212327, 18853322, -14220951},
+		FieldElement{4566830, -12963868, -28974889, -12240689, -7602672, -2830569, -8514358, -10431137, 2207753, -3209784},
+	},
+	{
+		FieldElement{-25154831, -4185821, 29681144, 7868801, -6854661, -9423865, -12437364, -663000, -31111463, -16132436},
+		FieldElement{25576264, -2703214, 7349804, -11814844, 16472782, 9300885, 3844789, 15725684, 171356, 6466918},
+		FieldElement{23103977, 13316479, 9739013, -16149481, 817875, -15038942, 8965339, -14088058, -30714912, 16193877},
+	},
+	{
+		FieldElement{-33521811, 3180713, -2394130, 14003687, -16903474, -16270840, 17238398, 4729455, -18074513, 9256800},
+		FieldElement{-25182317, -4174131, 32336398, 5036987, -21236817, 11360617, 22616405, 9761698, -19827198, 630305},
+		FieldElement{-13720693, 2639453, -24237460, -7406481, 9494427, -5774029, -6554551, -15960994, -2449256, -14291300},
+	},
+	{
+		FieldElement{-3151181, -5046075, 9282714, 6866145, -31907062, -863023, -18940575, 15033784, 25105118, -7894876},
+		FieldElement{-24326370, 15950226, -31801215, -14592823, -11662737, -5090925, 1573892, -2625887, 2198790, -15804619},
+		FieldElement{-3099351, 10324967, -2241613, 7453183, -5446979, -2735503, -13812022, -16236442, -32461234, -12290683},
+	},
+}
+
+// base contains precomputed multiples of the base-point. See the Ed25519 paper
+// for a discussion about how these values are used.
+var base = [32][8]PreComputedGroupElement{
+	{
+		{
+			FieldElement{25967493, -14356035, 29566456, 3660896, -12694345, 4014787, 27544626, -11754271, -6079156, 2047605},
+			FieldElement{-12545711, 934262, -2722910, 3049990, -727428, 9406986, 12720692, 5043384, 19500929, -15469378},
+			FieldElement{-8738181, 4489570, 9688441, -14785194, 10184609, -12363380, 29287919, 11864899, -24514362, -4438546},
+		},
+		{
+			FieldElement{-12815894, -12976347, -21581243, 11784320, -25355658, -2750717, -11717903, -3814571, -358445, -10211303},
+			FieldElement{-21703237, 6903825, 27185491, 6451973, -29577724, -9554005, -15616551, 11189268, -26829678, -5319081},
+			FieldElement{26966642, 11152617, 32442495, 15396054, 14353839, -12752335, -3128826, -9541118, -15472047, -4166697},
+		},
+		{
+			FieldElement{15636291, -9688557, 24204773, -7912398, 616977, -16685262, 27787600, -14772189, 28944400, -1550024},
+			FieldElement{16568933, 4717097, -11556148, -1102322, 15682896, -11807043, 16354577, -11775962, 7689662, 11199574},
+			FieldElement{30464156, -5976125, -11779434, -15670865, 23220365, 15915852, 7512774, 10017326, -17749093, -9920357},
+		},
+		{
+			FieldElement{-17036878, 13921892, 10945806, -6033431, 27105052, -16084379, -28926210, 15006023, 3284568, -6276540},
+			FieldElement{23599295, -8306047, -11193664, -7687416, 13236774, 10506355, 7464579, 9656445, 13059162, 10374397},
+			FieldElement{7798556, 16710257, 3033922, 2874086, 28997861, 2835604, 32406664, -3839045, -641708, -101325},
+		},
+		{
+			FieldElement{10861363, 11473154, 27284546, 1981175, -30064349, 12577861, 32867885, 14515107, -15438304, 10819380},
+			FieldElement{4708026, 6336745, 20377586, 9066809, -11272109, 6594696, -25653668, 12483688, -12668491, 5581306},
+			FieldElement{19563160, 16186464, -29386857, 4097519, 10237984, -4348115, 28542350, 13850243, -23678021, -15815942},
+		},
+		{
+			FieldElement{-15371964, -12862754, 32573250, 4720197, -26436522, 5875511, -19188627, -15224819, -9818940, -12085777},
+			FieldElement{-8549212, 109983, 15149363, 2178705, 22900618, 4543417, 3044240, -15689887, 1762328, 14866737},
+			FieldElement{-18199695, -15951423, -10473290, 1707278, -17185920, 3916101, -28236412, 3959421, 27914454, 4383652},
+		},
+		{
+			FieldElement{5153746, 9909285, 1723747, -2777874, 30523605, 5516873, 19480852, 5230134, -23952439, -15175766},
+			FieldElement{-30269007, -3463509, 7665486, 10083793, 28475525, 1649722, 20654025, 16520125, 30598449, 7715701},
+			FieldElement{28881845, 14381568, 9657904, 3680757, -20181635, 7843316, -31400660, 1370708, 29794553, -1409300},
+		},
+		{
+			FieldElement{14499471, -2729599, -33191113, -4254652, 28494862, 14271267, 30290735, 10876454, -33154098, 2381726},
+			FieldElement{-7195431, -2655363, -14730155, 462251, -27724326, 3941372, -6236617, 3696005, -32300832, 15351955},
+			FieldElement{27431194, 8222322, 16448760, -3907995, -18707002, 11938355, -32961401, -2970515, 29551813, 10109425},
+		},
+	},
+	{
+		{
+			FieldElement{-13657040, -13155431, -31283750, 11777098, 21447386, 6519384, -2378284, -1627556, 10092783, -4764171},
+			FieldElement{27939166, 14210322, 4677035, 16277044, -22964462, -12398139, -32508754, 12005538, -17810127, 12803510},
+			FieldElement{17228999, -15661624, -1233527, 300140, -1224870, -11714777, 30364213, -9038194, 18016357, 4397660},
+		},
+		{
+			FieldElement{-10958843, -7690207, 4776341, -14954238, 27850028, -15602212, -26619106, 14544525, -17477504, 982639},
+			FieldElement{29253598, 15796703, -2863982, -9908884, 10057023, 3163536, 7332899, -4120128, -21047696, 9934963},
+			FieldElement{5793303, 16271923, -24131614, -10116404, 29188560, 1206517, -14747930, 4559895, -30123922, -10897950},
+		},
+		{
+			FieldElement{-27643952, -11493006, 16282657, -11036493, 28414021, -15012264, 24191034, 4541697, -13338309, 5500568},
+			FieldElement{12650548, -1497113, 9052871, 11355358, -17680037, -8400164, -17430592, 12264343, 10874051, 13524335},
+			FieldElement{25556948, -3045990, 714651, 2510400, 23394682, -10415330, 33119038, 5080568, -22528059, 5376628},
+		},
+		{
+			FieldElement{-26088264, -4011052, -17013699, -3537628, -6726793, 1920897, -22321305, -9447443, 4535768, 1569007},
+			FieldElement{-2255422, 14606630, -21692440, -8039818, 28430649, 8775819, -30494562, 3044290, 31848280, 12543772},
+			FieldElement{-22028579, 2943893, -31857513, 6777306, 13784462, -4292203, -27377195, -2062731, 7718482, 14474653},
+		},
+		{
+			FieldElement{2385315, 2454213, -22631320, 46603, -4437935, -15680415, 656965, -7236665, 24316168, -5253567},
+			FieldElement{13741529, 10911568, -33233417, -8603737, -20177830, -1033297, 33040651, -13424532, -20729456, 8321686},
+			FieldElement{21060490, -2212744, 15712757, -4336099, 1639040, 10656336, 23845965, -11874838, -9984458, 608372},
+		},
+		{
+			FieldElement{-13672732, -15087586, -10889693, -7557059, -6036909, 11305547, 1123968, -6780577, 27229399, 23887},
+			FieldElement{-23244140, -294205, -11744728, 14712571, -29465699, -2029617, 12797024, -6440308, -1633405, 16678954},
+			FieldElement{-29500620, 4770662, -16054387, 14001338, 7830047, 9564805, -1508144, -4795045, -17169265, 4904953},
+		},
+		{
+			FieldElement{24059557, 14617003, 19037157, -15039908, 19766093, -14906429, 5169211, 16191880, 2128236, -4326833},
+			FieldElement{-16981152, 4124966, -8540610, -10653797, 30336522, -14105247, -29806336, 916033, -6882542, -2986532},
+			FieldElement{-22630907, 12419372, -7134229, -7473371, -16478904, 16739175, 285431, 2763829, 15736322, 4143876},
+		},
+		{
+			FieldElement{2379352, 11839345, -4110402, -5988665, 11274298, 794957, 212801, -14594663, 23527084, -16458268},
+			FieldElement{33431127, -11130478, -17838966, -15626900, 8909499, 8376530, -32625340, 4087881, -15188911, -14416214},
+			FieldElement{1767683, 7197987, -13205226, -2022635, -13091350, 448826, 5799055, 4357868, -4774191, -16323038},
+		},
+	},
+	{
+		{
+			FieldElement{6721966, 13833823, -23523388, -1551314, 26354293, -11863321, 23365147, -3949732, 7390890, 2759800},
+			FieldElement{4409041, 2052381, 23373853, 10530217, 7676779, -12885954, 21302353, -4264057, 1244380, -12919645},
+			FieldElement{-4421239, 7169619, 4982368, -2957590, 30256825, -2777540, 14086413, 9208236, 15886429, 16489664},
+		},
+		{
+			FieldElement{1996075, 10375649, 14346367, 13311202, -6874135, -16438411, -13693198, 398369, -30606455, -712933},
+			FieldElement{-25307465, 9795880, -2777414, 14878809, -33531835, 14780363, 13348553, 12076947, -30836462, 5113182},
+			FieldElement{-17770784, 11797796, 31950843, 13929123, -25888302, 12288344, -30341101, -7336386, 13847711, 5387222},
+		},
+		{
+			FieldElement{-18582163, -3416217, 17824843, -2340966, 22744343, -10442611, 8763061, 3617786, -19600662, 10370991},
+			FieldElement{20246567, -14369378, 22358229, -543712, 18507283, -10413996, 14554437, -8746092, 32232924, 16763880},
+			FieldElement{9648505, 10094563, 26416693, 14745928, -30374318, -6472621, 11094161, 15689506, 3140038, -16510092},
+		},
+		{
+			FieldElement{-16160072, 5472695, 31895588, 4744994, 8823515, 10365685, -27224800, 9448613, -28774454, 366295},
+			FieldElement{19153450, 11523972, -11096490, -6503142, -24647631, 5420647, 28344573, 8041113, 719605, 11671788},
+			FieldElement{8678025, 2694440, -6808014, 2517372, 4964326, 11152271, -15432916, -15266516, 27000813, -10195553},
+		},
+		{
+			FieldElement{-15157904, 7134312, 8639287, -2814877, -7235688, 10421742, 564065, 5336097, 6750977, -14521026},
+			FieldElement{11836410, -3979488, 26297894, 16080799, 23455045, 15735944, 1695823, -8819122, 8169720, 16220347},
+			FieldElement{-18115838, 8653647, 17578566, -6092619, -8025777, -16012763, -11144307, -2627664, -5990708, -14166033},
+		},
+		{
+			FieldElement{-23308498, -10968312, 15213228, -10081214, -30853605, -11050004, 27884329, 2847284, 2655861, 1738395},
+			FieldElement{-27537433, -14253021, -25336301, -8002780, -9370762, 8129821, 21651608, -3239336, -19087449, -11005278},
+			FieldElement{1533110, 3437855, 23735889, 459276, 29970501, 11335377, 26030092, 5821408, 10478196, 8544890},
+		},
+		{
+			FieldElement{32173121, -16129311, 24896207, 3921497, 22579056, -3410854, 19270449, 12217473, 17789017, -3395995},
+			FieldElement{-30552961, -2228401, -15578829, -10147201, 13243889, 517024, 15479401, -3853233, 30460520, 1052596},
+			FieldElement{-11614875, 13323618, 32618793, 8175907, -15230173, 12596687, 27491595, -4612359, 3179268, -9478891},
+		},
+		{
+			FieldElement{31947069, -14366651, -4640583, -15339921, -15125977, -6039709, -14756777, -16411740, 19072640, -9511060},
+			FieldElement{11685058, 11822410, 3158003, -13952594, 33402194, -4165066, 5977896, -5215017, 473099, 5040608},
+			FieldElement{-20290863, 8198642, -27410132, 11602123, 1290375, -2799760, 28326862, 1721092, -19558642, -3131606},
+		},
+	},
+	{
+		{
+			FieldElement{7881532, 10687937, 7578723, 7738378, -18951012, -2553952, 21820786, 8076149, -27868496, 11538389},
+			FieldElement{-19935666, 3899861, 18283497, -6801568, -15728660, -11249211, 8754525, 7446702, -5676054, 5797016},
+			FieldElement{-11295600, -3793569, -15782110, -7964573, 12708869, -8456199, 2014099, -9050574, -2369172, -5877341},
+		},
+		{
+			FieldElement{-22472376, -11568741, -27682020, 1146375, 18956691, 16640559, 1192730, -3714199, 15123619, 10811505},
+			FieldElement{14352098, -3419715, -18942044, 10822655, 32750596, 4699007, -70363, 15776356, -28886779, -11974553},
+			FieldElement{-28241164, -8072475, -4978962, -5315317, 29416931, 1847569, -20654173, -16484855, 4714547, -9600655},
+		},
+		{
+			FieldElement{15200332, 8368572, 19679101, 15970074, -31872674, 1959451, 24611599, -4543832, -11745876, 12340220},
+			FieldElement{12876937, -10480056, 33134381, 6590940, -6307776, 14872440, 9613953, 8241152, 15370987, 9608631},
+			FieldElement{-4143277, -12014408, 8446281, -391603, 4407738, 13629032, -7724868, 15866074, -28210621, -8814099},
+		},
+		{
+			FieldElement{26660628, -15677655, 8393734, 358047, -7401291, 992988, -23904233, 858697, 20571223, 8420556},
+			FieldElement{14620715, 13067227, -15447274, 8264467, 14106269, 15080814, 33531827, 12516406, -21574435, -12476749},
+			FieldElement{236881, 10476226, 57258, -14677024, 6472998, 2466984, 17258519, 7256740, 8791136, 15069930},
+		},
+		{
+			FieldElement{1276410, -9371918, 22949635, -16322807, -23493039, -5702186, 14711875, 4874229, -30663140, -2331391},
+			FieldElement{5855666, 4990204, -13711848, 7294284, -7804282, 1924647, -1423175, -7912378, -33069337, 9234253},
+			FieldElement{20590503, -9018988, 31529744, -7352666, -2706834, 10650548, 31559055, -11609587, 18979186, 13396066},
+		},
+		{
+			FieldElement{24474287, 4968103, 22267082, 4407354, 24063882, -8325180, -18816887, 13594782, 33514650, 7021958},
+			FieldElement{-11566906, -6565505, -21365085, 15928892, -26158305, 4315421, -25948728, -3916677, -21480480, 12868082},
+			FieldElement{-28635013, 13504661, 19988037, -2132761, 21078225, 6443208, -21446107, 2244500, -12455797, -8089383},
+		},
+		{
+			FieldElement{-30595528, 13793479, -5852820, 319136, -25723172, -6263899, 33086546, 8957937, -15233648, 5540521},
+			FieldElement{-11630176, -11503902, -8119500, -7643073, 2620056, 1022908, -23710744, -1568984, -16128528, -14962807},
+			FieldElement{23152971, 775386, 27395463, 14006635, -9701118, 4649512, 1689819, 892185, -11513277, -15205948},
+		},
+		{
+			FieldElement{9770129, 9586738, 26496094, 4324120, 1556511, -3550024, 27453819, 4763127, -19179614, 5867134},
+			FieldElement{-32765025, 1927590, 31726409, -4753295, 23962434, -16019500, 27846559, 5931263, -29749703, -16108455},
+			FieldElement{27461885, -2977536, 22380810, 1815854, -23033753, -3031938, 7283490, -15148073, -19526700, 7734629},
+		},
+	},
+	{
+		{
+			FieldElement{-8010264, -9590817, -11120403, 6196038, 29344158, -13430885, 7585295, -3176626, 18549497, 15302069},
+			FieldElement{-32658337, -6171222, -7672793, -11051681, 6258878, 13504381, 10458790, -6418461, -8872242, 8424746},
+			FieldElement{24687205, 8613276, -30667046, -3233545, 1863892, -1830544, 19206234, 7134917, -11284482, -828919},
+		},
+		{
+			FieldElement{11334899, -9218022, 8025293, 12707519, 17523892, -10476071, 10243738, -14685461, -5066034, 16498837},
+			FieldElement{8911542, 6887158, -9584260, -6958590, 11145641, -9543680, 17303925, -14124238, 6536641, 10543906},
+			FieldElement{-28946384, 15479763, -17466835, 568876, -1497683, 11223454, -2669190, -16625574, -27235709, 8876771},
+		},
+		{
+			FieldElement{-25742899, -12566864, -15649966, -846607, -33026686, -796288, -33481822, 15824474, -604426, -9039817},
+			FieldElement{10330056, 70051, 7957388, -9002667, 9764902, 15609756, 27698697, -4890037, 1657394, 3084098},
+			FieldElement{10477963, -7470260, 12119566, -13250805, 29016247, -5365589, 31280319, 14396151, -30233575, 15272409},
+		},
+		{
+			FieldElement{-12288309, 3169463, 28813183, 16658753, 25116432, -5630466, -25173957, -12636138, -25014757, 1950504},
+			FieldElement{-26180358, 9489187, 11053416, -14746161, -31053720, 5825630, -8384306, -8767532, 15341279, 8373727},
+			FieldElement{28685821, 7759505, -14378516, -12002860, -31971820, 4079242, 298136, -10232602, -2878207, 15190420},
+		},
+		{
+			FieldElement{-32932876, 13806336, -14337485, -15794431, -24004620, 10940928, 8669718, 2742393, -26033313, -6875003},
+			FieldElement{-1580388, -11729417, -25979658, -11445023, -17411874, -10912854, 9291594, -16247779, -12154742, 6048605},
+			FieldElement{-30305315, 14843444, 1539301, 11864366, 20201677, 1900163, 13934231, 5128323, 11213262, 9168384},
+		},
+		{
+			FieldElement{-26280513, 11007847, 19408960, -940758, -18592965, -4328580, -5088060, -11105150, 20470157, -16398701},
+			FieldElement{-23136053, 9282192, 14855179, -15390078, -7362815, -14408560, -22783952, 14461608, 14042978, 5230683},
+			FieldElement{29969567, -2741594, -16711867, -8552442, 9175486, -2468974, 21556951, 3506042, -5933891, -12449708},
+		},
+		{
+			FieldElement{-3144746, 8744661, 19704003, 4581278, -20430686, 6830683, -21284170, 8971513, -28539189, 15326563},
+			FieldElement{-19464629, 10110288, -17262528, -3503892, -23500387, 1355669, -15523050, 15300988, -20514118, 9168260},
+			FieldElement{-5353335, 4488613, -23803248, 16314347, 7780487, -15638939, -28948358, 9601605, 33087103, -9011387},
+		},
+		{
+			FieldElement{-19443170, -15512900, -20797467, -12445323, -29824447, 10229461, -27444329, -15000531, -5996870, 15664672},
+			FieldElement{23294591, -16632613, -22650781, -8470978, 27844204, 11461195, 13099750, -2460356, 18151676, 13417686},
+			FieldElement{-24722913, -4176517, -31150679, 5988919, -26858785, 6685065, 1661597, -12551441, 15271676, -15452665},
+		},
+	},
+	{
+		{
+			FieldElement{11433042, -13228665, 8239631, -5279517, -1985436, -725718, -18698764, 2167544, -6921301, -13440182},
+			FieldElement{-31436171, 15575146, 30436815, 12192228, -22463353, 9395379, -9917708, -8638997, 12215110, 12028277},
+			FieldElement{14098400, 6555944, 23007258, 5757252, -15427832, -12950502, 30123440, 4617780, -16900089, -655628},
+		},
+		{
+			FieldElement{-4026201, -15240835, 11893168, 13718664, -14809462, 1847385, -15819999, 10154009, 23973261, -12684474},
+			FieldElement{-26531820, -3695990, -1908898, 2534301, -31870557, -16550355, 18341390, -11419951, 32013174, -10103539},
+			FieldElement{-25479301, 10876443, -11771086, -14625140, -12369567, 1838104, 21911214, 6354752, 4425632, -837822},
+		},
+		{
+			FieldElement{-10433389, -14612966, 22229858, -3091047, -13191166, 776729, -17415375, -12020462, 4725005, 14044970},
+			FieldElement{19268650, -7304421, 1555349, 8692754, -21474059, -9910664, 6347390, -1411784, -19522291, -16109756},
+			FieldElement{-24864089, 12986008, -10898878, -5558584, -11312371, -148526, 19541418, 8180106, 9282262, 10282508},
+		},
+		{
+			FieldElement{-26205082, 4428547, -8661196, -13194263, 4098402, -14165257, 15522535, 8372215, 5542595, -10702683},
+			FieldElement{-10562541, 14895633, 26814552, -16673850, -17480754, -2489360, -2781891, 6993761, -18093885, 10114655},
+			FieldElement{-20107055, -929418, 31422704, 10427861, -7110749, 6150669, -29091755, -11529146, 25953725, -106158},
+		},
+		{
+			FieldElement{-4234397, -8039292, -9119125, 3046000, 2101609, -12607294, 19390020, 6094296, -3315279, 12831125},
+			FieldElement{-15998678, 7578152, 5310217, 14408357, -33548620, -224739, 31575954, 6326196, 7381791, -2421839},
+			FieldElement{-20902779, 3296811, 24736065, -16328389, 18374254, 7318640, 6295303, 8082724, -15362489, 12339664},
+		},
+		{
+			FieldElement{27724736, 2291157, 6088201, -14184798, 1792727, 5857634, 13848414, 15768922, 25091167, 14856294},
+			FieldElement{-18866652, 8331043, 24373479, 8541013, -701998, -9269457, 12927300, -12695493, -22182473, -9012899},
+			FieldElement{-11423429, -5421590, 11632845, 3405020, 30536730, -11674039, -27260765, 13866390, 30146206, 9142070},
+		},
+		{
+			FieldElement{3924129, -15307516, -13817122, -10054960, 12291820, -668366, -27702774, 9326384, -8237858, 4171294},
+			FieldElement{-15921940, 16037937, 6713787, 16606682, -21612135, 2790944, 26396185, 3731949, 345228, -5462949},
+			FieldElement{-21327538, 13448259, 25284571, 1143661, 20614966, -8849387, 2031539, -12391231, -16253183, -13582083},
+		},
+		{
+			FieldElement{31016211, -16722429, 26371392, -14451233, -5027349, 14854137, 17477601, 3842657, 28012650, -16405420},
+			FieldElement{-5075835, 9368966, -8562079, -4600902, -15249953, 6970560, -9189873, 16292057, -8867157, 3507940},
+			FieldElement{29439664, 3537914, 23333589, 6997794, -17555561, -11018068, -15209202, -15051267, -9164929, 6580396},
+		},
+	},
+	{
+		{
+			FieldElement{-12185861, -7679788, 16438269, 10826160, -8696817, -6235611, 17860444, -9273846, -2095802, 9304567},
+			FieldElement{20714564, -4336911, 29088195, 7406487, 11426967, -5095705, 14792667, -14608617, 5289421, -477127},
+			FieldElement{-16665533, -10650790, -6160345, -13305760, 9192020, -1802462, 17271490, 12349094, 26939669, -3752294},
+		},
+		{
+			FieldElement{-12889898, 9373458, 31595848, 16374215, 21471720, 13221525, -27283495, -12348559, -3698806, 117887},
+			FieldElement{22263325, -6560050, 3984570, -11174646, -15114008, -566785, 28311253, 5358056, -23319780, 541964},
+			FieldElement{16259219, 3261970, 2309254, -15534474, -16885711, -4581916, 24134070, -16705829, -13337066, -13552195},
+		},
+		{
+			FieldElement{9378160, -13140186, -22845982, -12745264, 28198281, -7244098, -2399684, -717351, 690426, 14876244},
+			FieldElement{24977353, -314384, -8223969, -13465086, 28432343, -1176353, -13068804, -12297348, -22380984, 6618999},
+			FieldElement{-1538174, 11685646, 12944378, 13682314, -24389511, -14413193, 8044829, -13817328, 32239829, -5652762},
+		},
+		{
+			FieldElement{-18603066, 4762990, -926250, 8885304, -28412480, -3187315, 9781647, -10350059, 32779359, 5095274},
+			FieldElement{-33008130, -5214506, -32264887, -3685216, 9460461, -9327423, -24601656, 14506724, 21639561, -2630236},
+			FieldElement{-16400943, -13112215, 25239338, 15531969, 3987758, -4499318, -1289502, -6863535, 17874574, 558605},
+		},
+		{
+			FieldElement{-13600129, 10240081, 9171883, 16131053, -20869254, 9599700, 33499487, 5080151, 2085892, 5119761},
+			FieldElement{-22205145, -2519528, -16381601, 414691, -25019550, 2170430, 30634760, -8363614, -31999993, -5759884},
+			FieldElement{-6845704, 15791202, 8550074, -1312654, 29928809, -12092256, 27534430, -7192145, -22351378, 12961482},
+		},
+		{
+			FieldElement{-24492060, -9570771, 10368194, 11582341, -23397293, -2245287, 16533930, 8206996, -30194652, -5159638},
+			FieldElement{-11121496, -3382234, 2307366, 6362031, -135455, 8868177, -16835630, 7031275, 7589640, 8945490},
+			FieldElement{-32152748, 8917967, 6661220, -11677616, -1192060, -15793393, 7251489, -11182180, 24099109, -14456170},
+		},
+		{
+			FieldElement{5019558, -7907470, 4244127, -14714356, -26933272, 6453165, -19118182, -13289025, -6231896, -10280736},
+			FieldElement{10853594, 10721687, 26480089, 5861829, -22995819, 1972175, -1866647, -10557898, -3363451, -6441124},
+			FieldElement{-17002408, 5906790, 221599, -6563147, 7828208, -13248918, 24362661, -2008168, -13866408, 7421392},
+		},
+		{
+			FieldElement{8139927, -6546497, 32257646, -5890546, 30375719, 1886181, -21175108, 15441252, 28826358, -4123029},
+			FieldElement{6267086, 9695052, 7709135, -16603597, -32869068, -1886135, 14795160, -7840124, 13746021, -1742048},
+			FieldElement{28584902, 7787108, -6732942, -15050729, 22846041, -7571236, -3181936, -363524, 4771362, -8419958},
+		},
+	},
+	{
+		{
+			FieldElement{24949256, 6376279, -27466481, -8174608, -18646154, -9930606, 33543569, -12141695, 3569627, 11342593},
+			FieldElement{26514989, 4740088, 27912651, 3697550, 19331575, -11472339, 6809886, 4608608, 7325975, -14801071},
+			FieldElement{-11618399, -14554430, -24321212, 7655128, -1369274, 5214312, -27400540, 10258390, -17646694, -8186692},
+		},
+		{
+			FieldElement{11431204, 15823007, 26570245, 14329124, 18029990, 4796082, -31446179, 15580664, 9280358, -3973687},
+			FieldElement{-160783, -10326257, -22855316, -4304997, -20861367, -13621002, -32810901, -11181622, -15545091, 4387441},
+			FieldElement{-20799378, 12194512, 3937617, -5805892, -27154820, 9340370, -24513992, 8548137, 20617071, -7482001},
+		},
+		{
+			FieldElement{-938825, -3930586, -8714311, 16124718, 24603125, -6225393, -13775352, -11875822, 24345683, 10325460},
+			FieldElement{-19855277, -1568885, -22202708, 8714034, 14007766, 6928528, 16318175, -1010689, 4766743, 3552007},
+			FieldElement{-21751364, -16730916, 1351763, -803421, -4009670, 3950935, 3217514, 14481909, 10988822, -3994762},
+		},
+		{
+			FieldElement{15564307, -14311570, 3101243, 5684148, 30446780, -8051356, 12677127, -6505343, -8295852, 13296005},
+			FieldElement{-9442290, 6624296, -30298964, -11913677, -4670981, -2057379, 31521204, 9614054, -30000824, 12074674},
+			FieldElement{4771191, -135239, 14290749, -13089852, 27992298, 14998318, -1413936, -1556716, 29832613, -16391035},
+		},
+		{
+			FieldElement{7064884, -7541174, -19161962, -5067537, -18891269, -2912736, 25825242, 5293297, -27122660, 13101590},
+			FieldElement{-2298563, 2439670, -7466610, 1719965, -27267541, -16328445, 32512469, -5317593, -30356070, -4190957},
+			FieldElement{-30006540, 10162316, -33180176, 3981723, -16482138, -13070044, 14413974, 9515896, 19568978, 9628812},
+		},
+		{
+			FieldElement{33053803, 199357, 15894591, 1583059, 27380243, -4580435, -17838894, -6106839, -6291786, 3437740},
+			FieldElement{-18978877, 3884493, 19469877, 12726490, 15913552, 13614290, -22961733, 70104, 7463304, 4176122},
+			FieldElement{-27124001, 10659917, 11482427, -16070381, 12771467, -6635117, -32719404, -5322751, 24216882, 5944158},
+		},
+		{
+			FieldElement{8894125, 7450974, -2664149, -9765752, -28080517, -12389115, 19345746, 14680796, 11632993, 5847885},
+			FieldElement{26942781, -2315317, 9129564, -4906607, 26024105, 11769399, -11518837, 6367194, -9727230, 4782140},
+			FieldElement{19916461, -4828410, -22910704, -11414391, 25606324, -5972441, 33253853, 8220911, 6358847, -1873857},
+		},
+		{
+			FieldElement{801428, -2081702, 16569428, 11065167, 29875704, 96627, 7908388, -4480480, -13538503, 1387155},
+			FieldElement{19646058, 5720633, -11416706, 12814209, 11607948, 12749789, 14147075, 15156355, -21866831, 11835260},
+			FieldElement{19299512, 1155910, 28703737, 14890794, 2925026, 7269399, 26121523, 15467869, -26560550, 5052483},
+		},
+	},
+	{
+		{
+			FieldElement{-3017432, 10058206, 1980837, 3964243, 22160966, 12322533, -6431123, -12618185, 12228557, -7003677},
+			FieldElement{32944382, 14922211, -22844894, 5188528, 21913450, -8719943, 4001465, 13238564, -6114803, 8653815},
+			FieldElement{22865569, -4652735, 27603668, -12545395, 14348958, 8234005, 24808405, 5719875, 28483275, 2841751},
+		},
+		{
+			FieldElement{-16420968, -1113305, -327719, -12107856, 21886282, -15552774, -1887966, -315658, 19932058, -12739203},
+			FieldElement{-11656086, 10087521, -8864888, -5536143, -19278573, -3055912, 3999228, 13239134, -4777469, -13910208},
+			FieldElement{1382174, -11694719, 17266790, 9194690, -13324356, 9720081, 20403944, 11284705, -14013818, 3093230},
+		},
+		{
+			FieldElement{16650921, -11037932, -1064178, 1570629, -8329746, 7352753, -302424, 16271225, -24049421, -6691850},
+			FieldElement{-21911077, -5927941, -4611316, -5560156, -31744103, -10785293, 24123614, 15193618, -21652117, -16739389},
+			FieldElement{-9935934, -4289447, -25279823, 4372842, 2087473, 10399484, 31870908, 14690798, 17361620, 11864968},
+		},
+		{
+			FieldElement{-11307610, 6210372, 13206574, 5806320, -29017692, -13967200, -12331205, -7486601, -25578460, -16240689},
+			FieldElement{14668462, -12270235, 26039039, 15305210, 25515617, 4542480, 10453892, 6577524, 9145645, -6443880},
+			FieldElement{5974874, 3053895, -9433049, -10385191, -31865124, 3225009, -7972642, 3936128, -5652273, -3050304},
+		},
+		{
+			FieldElement{30625386, -4729400, -25555961, -12792866, -20484575, 7695099, 17097188, -16303496, -27999779, 1803632},
+			FieldElement{-3553091, 9865099, -5228566, 4272701, -5673832, -16689700, 14911344, 12196514, -21405489, 7047412},
+			FieldElement{20093277, 9920966, -11138194, -5343857, 13161587, 12044805, -32856851, 4124601, -32343828, -10257566},
+		},
+		{
+			FieldElement{-20788824, 14084654, -13531713, 7842147, 19119038, -13822605, 4752377, -8714640, -21679658, 2288038},
+			FieldElement{-26819236, -3283715, 29965059, 3039786, -14473765, 2540457, 29457502, 14625692, -24819617, 12570232},
+			FieldElement{-1063558, -11551823, 16920318, 12494842, 1278292, -5869109, -21159943, -3498680, -11974704, 4724943},
+		},
+		{
+			FieldElement{17960970, -11775534, -4140968, -9702530, -8876562, -1410617, -12907383, -8659932, -29576300, 1903856},
+			FieldElement{23134274, -14279132, -10681997, -1611936, 20684485, 15770816, -12989750, 3190296, 26955097, 14109738},
+			FieldElement{15308788, 5320727, -30113809, -14318877, 22902008, 7767164, 29425325, -11277562, 31960942, 11934971},
+		},
+		{
+			FieldElement{-27395711, 8435796, 4109644, 12222639, -24627868, 14818669, 20638173, 4875028, 10491392, 1379718},
+			FieldElement{-13159415, 9197841, 3875503, -8936108, -1383712, -5879801, 33518459, 16176658, 21432314, 12180697},
+			FieldElement{-11787308, 11500838, 13787581, -13832590, -22430679, 10140205, 1465425, 12689540, -10301319, -13872883},
+		},
+	},
+	{
+		{
+			FieldElement{5414091, -15386041, -21007664, 9643570, 12834970, 1186149, -2622916, -1342231, 26128231, 6032912},
+			FieldElement{-26337395, -13766162, 32496025, -13653919, 17847801, -12669156, 3604025, 8316894, -25875034, -10437358},
+			FieldElement{3296484, 6223048, 24680646, -12246460, -23052020, 5903205, -8862297, -4639164, 12376617, 3188849},
+		},
+		{
+			FieldElement{29190488, -14659046, 27549113, -1183516, 3520066, -10697301, 32049515, -7309113, -16109234, -9852307},
+			FieldElement{-14744486, -9309156, 735818, -598978, -20407687, -5057904, 25246078, -15795669, 18640741, -960977},
+			FieldElement{-6928835, -16430795, 10361374, 5642961, 4910474, 12345252, -31638386, -494430, 10530747, 1053335},
+		},
+		{
+			FieldElement{-29265967, -14186805, -13538216, -12117373, -19457059, -10655384, -31462369, -2948985, 24018831, 15026644},
+			FieldElement{-22592535, -3145277, -2289276, 5953843, -13440189, 9425631, 25310643, 13003497, -2314791, -15145616},
+			FieldElement{-27419985, -603321, -8043984, -1669117, -26092265, 13987819, -27297622, 187899, -23166419, -2531735},
+		},
+		{
+			FieldElement{-21744398, -13810475, 1844840, 5021428, -10434399, -15911473, 9716667, 16266922, -5070217, 726099},
+			FieldElement{29370922, -6053998, 7334071, -15342259, 9385287, 2247707, -13661962, -4839461, 30007388, -15823341},
+			FieldElement{-936379, 16086691, 23751945, -543318, -1167538, -5189036, 9137109, 730663, 9835848, 4555336},
+		},
+		{
+			FieldElement{-23376435, 1410446, -22253753, -12899614, 30867635, 15826977, 17693930, 544696, -11985298, 12422646},
+			FieldElement{31117226, -12215734, -13502838, 6561947, -9876867, -12757670, -5118685, -4096706, 29120153, 13924425},
+			FieldElement{-17400879, -14233209, 19675799, -2734756, -11006962, -5858820, -9383939, -11317700, 7240931, -237388},
+		},
+		{
+			FieldElement{-31361739, -11346780, -15007447, -5856218, -22453340, -12152771, 1222336, 4389483, 3293637, -15551743},
+			FieldElement{-16684801, -14444245, 11038544, 11054958, -13801175, -3338533, -24319580, 7733547, 12796905, -6335822},
+			FieldElement{-8759414, -10817836, -25418864, 10783769, -30615557, -9746811, -28253339, 3647836, 3222231, -11160462},
+		},
+		{
+			FieldElement{18606113, 1693100, -25448386, -15170272, 4112353, 10045021, 23603893, -2048234, -7550776, 2484985},
+			FieldElement{9255317, -3131197, -12156162, -1004256, 13098013, -9214866, 16377220, -2102812, -19802075, -3034702},
+			FieldElement{-22729289, 7496160, -5742199, 11329249, 19991973, -3347502, -31718148, 9936966, -30097688, -10618797},
+		},
+		{
+			FieldElement{21878590, -5001297, 4338336, 13643897, -3036865, 13160960, 19708896, 5415497, -7360503, -4109293},
+			FieldElement{27736861, 10103576, 12500508, 8502413, -3413016, -9633558, 10436918, -1550276, -23659143, -8132100},
+			FieldElement{19492550, -12104365, -29681976, -852630, -3208171, 12403437, 30066266, 8367329, 13243957, 8709688},
+		},
+	},
+	{
+		{
+			FieldElement{12015105, 2801261, 28198131, 10151021, 24818120, -4743133, -11194191, -5645734, 5150968, 7274186},
+			FieldElement{2831366, -12492146, 1478975, 6122054, 23825128, -12733586, 31097299, 6083058, 31021603, -9793610},
+			FieldElement{-2529932, -2229646, 445613, 10720828, -13849527, -11505937, -23507731, 16354465, 15067285, -14147707},
+		},
+		{
+			FieldElement{7840942, 14037873, -33364863, 15934016, -728213, -3642706, 21403988, 1057586, -19379462, -12403220},
+			FieldElement{915865, -16469274, 15608285, -8789130, -24357026, 6060030, -17371319, 8410997, -7220461, 16527025},
+			FieldElement{32922597, -556987, 20336074, -16184568, 10903705, -5384487, 16957574, 52992, 23834301, 6588044},
+		},
+		{
+			FieldElement{32752030, 11232950, 3381995, -8714866, 22652988, -10744103, 17159699, 16689107, -20314580, -1305992},
+			FieldElement{-4689649, 9166776, -25710296, -10847306, 11576752, 12733943, 7924251, -2752281, 1976123, -7249027},
+			FieldElement{21251222, 16309901, -2983015, -6783122, 30810597, 12967303, 156041, -3371252, 12331345, -8237197},
+		},
+		{
+			FieldElement{8651614, -4477032, -16085636, -4996994, 13002507, 2950805, 29054427, -5106970, 10008136, -4667901},
+			FieldElement{31486080, 15114593, -14261250, 12951354, 14369431, -7387845, 16347321, -13662089, 8684155, -10532952},
+			FieldElement{19443825, 11385320, 24468943, -9659068, -23919258, 2187569, -26263207, -6086921, 31316348, 14219878},
+		},
+		{
+			FieldElement{-28594490, 1193785, 32245219, 11392485, 31092169, 15722801, 27146014, 6992409, 29126555, 9207390},
+			FieldElement{32382935, 1110093, 18477781, 11028262, -27411763, -7548111, -4980517, 10843782, -7957600, -14435730},
+			FieldElement{2814918, 7836403, 27519878, -7868156, -20894015, -11553689, -21494559, 8550130, 28346258, 1994730},
+		},
+		{
+			FieldElement{-19578299, 8085545, -14000519, -3948622, 2785838, -16231307, -19516951, 7174894, 22628102, 8115180},
+			FieldElement{-30405132, 955511, -11133838, -15078069, -32447087, -13278079, -25651578, 3317160, -9943017, 930272},
+			FieldElement{-15303681, -6833769, 28856490, 1357446, 23421993, 1057177, 24091212, -1388970, -22765376, -10650715},
+		},
+		{
+			FieldElement{-22751231, -5303997, -12907607, -12768866, -15811511, -7797053, -14839018, -16554220, -1867018, 8398970},
+			FieldElement{-31969310, 2106403, -4736360, 1362501, 12813763, 16200670, 22981545, -6291273, 18009408, -15772772},
+			FieldElement{-17220923, -9545221, -27784654, 14166835, 29815394, 7444469, 29551787, -3727419, 19288549, 1325865},
+		},
+		{
+			FieldElement{15100157, -15835752, -23923978, -1005098, -26450192, 15509408, 12376730, -3479146, 33166107, -8042750},
+			FieldElement{20909231, 13023121, -9209752, 16251778, -5778415, -8094914, 12412151, 10018715, 2213263, -13878373},
+			FieldElement{32529814, -11074689, 30361439, -16689753, -9135940, 1513226, 22922121, 6382134, -5766928, 8371348},
+		},
+	},
+	{
+		{
+			FieldElement{9923462, 11271500, 12616794, 3544722, -29998368, -1721626, 12891687, -8193132, -26442943, 10486144},
+			FieldElement{-22597207, -7012665, 8587003, -8257861, 4084309, -12970062, 361726, 2610596, -23921530, -11455195},
+			FieldElement{5408411, -1136691, -4969122, 10561668, 24145918, 14240566, 31319731, -4235541, 19985175, -3436086},
+		},
+		{
+			FieldElement{-13994457, 16616821, 14549246, 3341099, 32155958, 13648976, -17577068, 8849297, 65030, 8370684},
+			FieldElement{-8320926, -12049626, 31204563, 5839400, -20627288, -1057277, -19442942, 6922164, 12743482, -9800518},
+			FieldElement{-2361371, 12678785, 28815050, 4759974, -23893047, 4884717, 23783145, 11038569, 18800704, 255233},
+		},
+		{
+			FieldElement{-5269658, -1773886, 13957886, 7990715, 23132995, 728773, 13393847, 9066957, 19258688, -14753793},
+			FieldElement{-2936654, -10827535, -10432089, 14516793, -3640786, 4372541, -31934921, 2209390, -1524053, 2055794},
+			FieldElement{580882, 16705327, 5468415, -2683018, -30926419, -14696000, -7203346, -8994389, -30021019, 7394435},
+		},
+		{
+			FieldElement{23838809, 1822728, -15738443, 15242727, 8318092, -3733104, -21672180, -3492205, -4821741, 14799921},
+			FieldElement{13345610, 9759151, 3371034, -16137791, 16353039, 8577942, 31129804, 13496856, -9056018, 7402518},
+			FieldElement{2286874, -4435931, -20042458, -2008336, -13696227, 5038122, 11006906, -15760352, 8205061, 1607563},
+		},
+		{
+			FieldElement{14414086, -8002132, 3331830, -3208217, 22249151, -5594188, 18364661, -2906958, 30019587, -9029278},
+			FieldElement{-27688051, 1585953, -10775053, 931069, -29120221, -11002319, -14410829, 12029093, 9944378, 8024},
+			FieldElement{4368715, -3709630, 29874200, -15022983, -20230386, -11410704, -16114594, -999085, -8142388, 5640030},
+		},
+		{
+			FieldElement{10299610, 13746483, 11661824, 16234854, 7630238, 5998374, 9809887, -16694564, 15219798, -14327783},
+			FieldElement{27425505, -5719081, 3055006, 10660664, 23458024, 595578, -15398605, -1173195, -18342183, 9742717},
+			FieldElement{6744077, 2427284, 26042789, 2720740, -847906, 1118974, 32324614, 7406442, 12420155, 1994844},
+		},
+		{
+			FieldElement{14012521, -5024720, -18384453, -9578469, -26485342, -3936439, -13033478, -10909803, 24319929, -6446333},
+			FieldElement{16412690, -4507367, 10772641, 15929391, -17068788, -4658621, 10555945, -10484049, -30102368, -4739048},
+			FieldElement{22397382, -7767684, -9293161, -12792868, 17166287, -9755136, -27333065, 6199366, 21880021, -12250760},
+		},
+		{
+			FieldElement{-4283307, 5368523, -31117018, 8163389, -30323063, 3209128, 16557151, 8890729, 8840445, 4957760},
+			FieldElement{-15447727, 709327, -6919446, -10870178, -29777922, 6522332, -21720181, 12130072, -14796503, 5005757},
+			FieldElement{-2114751, -14308128, 23019042, 15765735, -25269683, 6002752, 10183197, -13239326, -16395286, -2176112},
+		},
+	},
+	{
+		{
+			FieldElement{-19025756, 1632005, 13466291, -7995100, -23640451, 16573537, -32013908, -3057104, 22208662, 2000468},
+			FieldElement{3065073, -1412761, -25598674, -361432, -17683065, -5703415, -8164212, 11248527, -3691214, -7414184},
+			FieldElement{10379208, -6045554, 8877319, 1473647, -29291284, -12507580, 16690915, 2553332, -3132688, 16400289},
+		},
+		{
+			FieldElement{15716668, 1254266, -18472690, 7446274, -8448918, 6344164, -22097271, -7285580, 26894937, 9132066},
+			FieldElement{24158887, 12938817, 11085297, -8177598, -28063478, -4457083, -30576463, 64452, -6817084, -2692882},
+			FieldElement{13488534, 7794716, 22236231, 5989356, 25426474, -12578208, 2350710, -3418511, -4688006, 2364226},
+		},
+		{
+			FieldElement{16335052, 9132434, 25640582, 6678888, 1725628, 8517937, -11807024, -11697457, 15445875, -7798101},
+			FieldElement{29004207, -7867081, 28661402, -640412, -12794003, -7943086, 31863255, -4135540, -278050, -15759279},
+			FieldElement{-6122061, -14866665, -28614905, 14569919, -10857999, -3591829, 10343412, -6976290, -29828287, -10815811},
+		},
+		{
+			FieldElement{27081650, 3463984, 14099042, -4517604, 1616303, -6205604, 29542636, 15372179, 17293797, 960709},
+			FieldElement{20263915, 11434237, -5765435, 11236810, 13505955, -10857102, -16111345, 6493122, -19384511, 7639714},
+			FieldElement{-2830798, -14839232, 25403038, -8215196, -8317012, -16173699, 18006287, -16043750, 29994677, -15808121},
+		},
+		{
+			FieldElement{9769828, 5202651, -24157398, -13631392, -28051003, -11561624, -24613141, -13860782, -31184575, 709464},
+			FieldElement{12286395, 13076066, -21775189, -1176622, -25003198, 4057652, -32018128, -8890874, 16102007, 13205847},
+			FieldElement{13733362, 5599946, 10557076, 3195751, -5557991, 8536970, -25540170, 8525972, 10151379, 10394400},
+		},
+		{
+			FieldElement{4024660, -16137551, 22436262, 12276534, -9099015, -2686099, 19698229, 11743039, -33302334, 8934414},
+			FieldElement{-15879800, -4525240, -8580747, -2934061, 14634845, -698278, -9449077, 3137094, -11536886, 11721158},
+			FieldElement{17555939, -5013938, 8268606, 2331751, -22738815, 9761013, 9319229, 8835153, -9205489, -1280045},
+		},
+		{
+			FieldElement{-461409, -7830014, 20614118, 16688288, -7514766, -4807119, 22300304, 505429, 6108462, -6183415},
+			FieldElement{-5070281, 12367917, -30663534, 3234473, 32617080, -8422642, 29880583, -13483331, -26898490, -7867459},
+			FieldElement{-31975283, 5726539, 26934134, 10237677, -3173717, -605053, 24199304, 3795095, 7592688, -14992079},
+		},
+		{
+			FieldElement{21594432, -14964228, 17466408, -4077222, 32537084, 2739898, 6407723, 12018833, -28256052, 4298412},
+			FieldElement{-20650503, -11961496, -27236275, 570498, 3767144, -1717540, 13891942, -1569194, 13717174, 10805743},
+			FieldElement{-14676630, -15644296, 15287174, 11927123, 24177847, -8175568, -796431, 14860609, -26938930, -5863836},
+		},
+	},
+	{
+		{
+			FieldElement{12962541, 5311799, -10060768, 11658280, 18855286, -7954201, 13286263, -12808704, -4381056, 9882022},
+			FieldElement{18512079, 11319350, -20123124, 15090309, 18818594, 5271736, -22727904, 3666879, -23967430, -3299429},
+			FieldElement{-6789020, -3146043, 16192429, 13241070, 15898607, -14206114, -10084880, -6661110, -2403099, 5276065},
+		},
+		{
+			FieldElement{30169808, -5317648, 26306206, -11750859, 27814964, 7069267, 7152851, 3684982, 1449224, 13082861},
+			FieldElement{10342826, 3098505, 2119311, 193222, 25702612, 12233820, 23697382, 15056736, -21016438, -8202000},
+			FieldElement{-33150110, 3261608, 22745853, 7948688, 19370557, -15177665, -26171976, 6482814, -10300080, -11060101},
+		},
+		{
+			FieldElement{32869458, -5408545, 25609743, 15678670, -10687769, -15471071, 26112421, 2521008, -22664288, 6904815},
+			FieldElement{29506923, 4457497, 3377935, -9796444, -30510046, 12935080, 1561737, 3841096, -29003639, -6657642},
+			FieldElement{10340844, -6630377, -18656632, -2278430, 12621151, -13339055, 30878497, -11824370, -25584551, 5181966},
+		},
+		{
+			FieldElement{25940115, -12658025, 17324188, -10307374, -8671468, 15029094, 24396252, -16450922, -2322852, -12388574},
+			FieldElement{-21765684, 9916823, -1300409, 4079498, -1028346, 11909559, 1782390, 12641087, 20603771, -6561742},
+			FieldElement{-18882287, -11673380, 24849422, 11501709, 13161720, -4768874, 1925523, 11914390, 4662781, 7820689},
+		},
+		{
+			FieldElement{12241050, -425982, 8132691, 9393934, 32846760, -1599620, 29749456, 12172924, 16136752, 15264020},
+			FieldElement{-10349955, -14680563, -8211979, 2330220, -17662549, -14545780, 10658213, 6671822, 19012087, 3772772},
+			FieldElement{3753511, -3421066, 10617074, 2028709, 14841030, -6721664, 28718732, -15762884, 20527771, 12988982},
+		},
+		{
+			FieldElement{-14822485, -5797269, -3707987, 12689773, -898983, -10914866, -24183046, -10564943, 3299665, -12424953},
+			FieldElement{-16777703, -15253301, -9642417, 4978983, 3308785, 8755439, 6943197, 6461331, -25583147, 8991218},
+			FieldElement{-17226263, 1816362, -1673288, -6086439, 31783888, -8175991, -32948145, 7417950, -30242287, 1507265},
+		},
+		{
+			FieldElement{29692663, 6829891, -10498800, 4334896, 20945975, -11906496, -28887608, 8209391, 14606362, -10647073},
+			FieldElement{-3481570, 8707081, 32188102, 5672294, 22096700, 1711240, -33020695, 9761487, 4170404, -2085325},
+			FieldElement{-11587470, 14855945, -4127778, -1531857, -26649089, 15084046, 22186522, 16002000, -14276837, -8400798},
+		},
+		{
+			FieldElement{-4811456, 13761029, -31703877, -2483919, -3312471, 7869047, -7113572, -9620092, 13240845, 10965870},
+			FieldElement{-7742563, -8256762, -14768334, -13656260, -23232383, 12387166, 4498947, 14147411, 29514390, 4302863},
+			FieldElement{-13413405, -12407859, 20757302, -13801832, 14785143, 8976368, -5061276, -2144373, 17846988, -13971927},
+		},
+	},
+	{
+		{
+			FieldElement{-2244452, -754728, -4597030, -1066309, -6247172, 1455299, -21647728, -9214789, -5222701, 12650267},
+			FieldElement{-9906797, -16070310, 21134160, 12198166, -27064575, 708126, 387813, 13770293, -19134326, 10958663},
+			FieldElement{22470984, 12369526, 23446014, -5441109, -21520802, -9698723, -11772496, -11574455, -25083830, 4271862},
+		},
+		{
+			FieldElement{-25169565, -10053642, -19909332, 15361595, -5984358, 2159192, 75375, -4278529, -32526221, 8469673},
+			FieldElement{15854970, 4148314, -8893890, 7259002, 11666551, 13824734, -30531198, 2697372, 24154791, -9460943},
+			FieldElement{15446137, -15806644, 29759747, 14019369, 30811221, -9610191, -31582008, 12840104, 24913809, 9815020},
+		},
+		{
+			FieldElement{-4709286, -5614269, -31841498, -12288893, -14443537, 10799414, -9103676, 13438769, 18735128, 9466238},
+			FieldElement{11933045, 9281483, 5081055, -5183824, -2628162, -4905629, -7727821, -10896103, -22728655, 16199064},
+			FieldElement{14576810, 379472, -26786533, -8317236, -29426508, -10812974, -102766, 1876699, 30801119, 2164795},
+		},
+		{
+			FieldElement{15995086, 3199873, 13672555, 13712240, -19378835, -4647646, -13081610, -15496269, -13492807, 1268052},
+			FieldElement{-10290614, -3659039, -3286592, 10948818, 23037027, 3794475, -3470338, -12600221, -17055369, 3565904},
+			FieldElement{29210088, -9419337, -5919792, -4952785, 10834811, -13327726, -16512102, -10820713, -27162222, -14030531},
+		},
+		{
+			FieldElement{-13161890, 15508588, 16663704, -8156150, -28349942, 9019123, -29183421, -3769423, 2244111, -14001979},
+			FieldElement{-5152875, -3800936, -9306475, -6071583, 16243069, 14684434, -25673088, -16180800, 13491506, 4641841},
+			FieldElement{10813417, 643330, -19188515, -728916, 30292062, -16600078, 27548447, -7721242, 14476989, -12767431},
+		},
+		{
+			FieldElement{10292079, 9984945, 6481436, 8279905, -7251514, 7032743, 27282937, -1644259, -27912810, 12651324},
+			FieldElement{-31185513, -813383, 22271204, 11835308, 10201545, 15351028, 17099662, 3988035, 21721536, -3148940},
+			FieldElement{10202177, -6545839, -31373232, -9574638, -32150642, -8119683, -12906320, 3852694, 13216206, 14842320},
+		},
+		{
+			FieldElement{-15815640, -10601066, -6538952, -7258995, -6984659, -6581778, -31500847, 13765824, -27434397, 9900184},
+			FieldElement{14465505, -13833331, -32133984, -14738873, -27443187, 12990492, 33046193, 15796406, -7051866, -8040114},
+			FieldElement{30924417, -8279620, 6359016, -12816335, 16508377, 9071735, -25488601, 15413635, 9524356, -7018878},
+		},
+		{
+			FieldElement{12274201, -13175547, 32627641, -1785326, 6736625, 13267305, 5237659, -5109483, 15663516, 4035784},
+			FieldElement{-2951309, 8903985, 17349946, 601635, -16432815, -4612556, -13732739, -15889334, -22258478, 4659091},
+			FieldElement{-16916263, -4952973, -30393711, -15158821, 20774812, 15897498, 5736189, 15026997, -2178256, -13455585},
+		},
+	},
+	{
+		{
+			FieldElement{-8858980, -2219056, 28571666, -10155518, -474467, -10105698, -3801496, 278095, 23440562, -290208},
+			FieldElement{10226241, -5928702, 15139956, 120818, -14867693, 5218603, 32937275, 11551483, -16571960, -7442864},
+			FieldElement{17932739, -12437276, -24039557, 10749060, 11316803, 7535897, 22503767, 5561594, -3646624, 3898661},
+		},
+		{
+			FieldElement{7749907, -969567, -16339731, -16464, -25018111, 15122143, -1573531, 7152530, 21831162, 1245233},
+			FieldElement{26958459, -14658026, 4314586, 8346991, -5677764, 11960072, -32589295, -620035, -30402091, -16716212},
+			FieldElement{-12165896, 9166947, 33491384, 13673479, 29787085, 13096535, 6280834, 14587357, -22338025, 13987525},
+		},
+		{
+			FieldElement{-24349909, 7778775, 21116000, 15572597, -4833266, -5357778, -4300898, -5124639, -7469781, -2858068},
+			FieldElement{9681908, -6737123, -31951644, 13591838, -6883821, 386950, 31622781, 6439245, -14581012, 4091397},
+			FieldElement{-8426427, 1470727, -28109679, -1596990, 3978627, -5123623, -19622683, 12092163, 29077877, -14741988},
+		},
+		{
+			FieldElement{5269168, -6859726, -13230211, -8020715, 25932563, 1763552, -5606110, -5505881, -20017847, 2357889},
+			FieldElement{32264008, -15407652, -5387735, -1160093, -2091322, -3946900, 23104804, -12869908, 5727338, 189038},
+			FieldElement{14609123, -8954470, -6000566, -16622781, -14577387, -7743898, -26745169, 10942115, -25888931, -14884697},
+		},
+		{
+			FieldElement{20513500, 5557931, -15604613, 7829531, 26413943, -2019404, -21378968, 7471781, 13913677, -5137875},
+			FieldElement{-25574376, 11967826, 29233242, 12948236, -6754465, 4713227, -8940970, 14059180, 12878652, 8511905},
+			FieldElement{-25656801, 3393631, -2955415, -7075526, -2250709, 9366908, -30223418, 6812974, 5568676, -3127656},
+		},
+		{
+			FieldElement{11630004, 12144454, 2116339, 13606037, 27378885, 15676917, -17408753, -13504373, -14395196, 8070818},
+			FieldElement{27117696, -10007378, -31282771, -5570088, 1127282, 12772488, -29845906, 10483306, -11552749, -1028714},
+			FieldElement{10637467, -5688064, 5674781, 1072708, -26343588, -6982302, -1683975, 9177853, -27493162, 15431203},
+		},
+		{
+			FieldElement{20525145, 10892566, -12742472, 12779443, -29493034, 16150075, -28240519, 14943142, -15056790, -7935931},
+			FieldElement{-30024462, 5626926, -551567, -9981087, 753598, 11981191, 25244767, -3239766, -3356550, 9594024},
+			FieldElement{-23752644, 2636870, -5163910, -10103818, 585134, 7877383, 11345683, -6492290, 13352335, -10977084},
+		},
+		{
+			FieldElement{-1931799, -5407458, 3304649, -12884869, 17015806, -4877091, -29783850, -7752482, -13215537, -319204},
+			FieldElement{20239939, 6607058, 6203985, 3483793, -18386976, -779229, -20723742, 15077870, -22750759, 14523817},
+			FieldElement{27406042, -6041657, 27423596, -4497394, 4996214, 10002360, -28842031, -4545494, -30172742, -4805667},
+		},
+	},
+	{
+		{
+			FieldElement{11374242, 12660715, 17861383, -12540833, 10935568, 1099227, -13886076, -9091740, -27727044, 11358504},
+			FieldElement{-12730809, 10311867, 1510375, 10778093, -2119455, -9145702, 32676003, 11149336, -26123651, 4985768},
+			FieldElement{-19096303, 341147, -6197485, -239033, 15756973, -8796662, -983043, 13794114, -19414307, -15621255},
+		},
+		{
+			FieldElement{6490081, 11940286, 25495923, -7726360, 8668373, -8751316, 3367603, 6970005, -1691065, -9004790},
+			FieldElement{1656497, 13457317, 15370807, 6364910, 13605745, 8362338, -19174622, -5475723, -16796596, -5031438},
+			FieldElement{-22273315, -13524424, -64685, -4334223, -18605636, -10921968, -20571065, -7007978, -99853, -10237333},
+		},
+		{
+			FieldElement{17747465, 10039260, 19368299, -4050591, -20630635, -16041286, 31992683, -15857976, -29260363, -5511971},
+			FieldElement{31932027, -4986141, -19612382, 16366580, 22023614, 88450, 11371999, -3744247, 4882242, -10626905},
+			FieldElement{29796507, 37186, 19818052, 10115756, -11829032, 3352736, 18551198, 3272828, -5190932, -4162409},
+		},
+		{
+			FieldElement{12501286, 4044383, -8612957, -13392385, -32430052, 5136599, -19230378, -3529697, 330070, -3659409},
+			FieldElement{6384877, 2899513, 17807477, 7663917, -2358888, 12363165, 25366522, -8573892, -271295, 12071499},
+			FieldElement{-8365515, -4042521, 25133448, -4517355, -6211027, 2265927, -32769618, 1936675, -5159697, 3829363},
+		},
+		{
+			FieldElement{28425966, -5835433, -577090, -4697198, -14217555, 6870930, 7921550, -6567787, 26333140, 14267664},
+			FieldElement{-11067219, 11871231, 27385719, -10559544, -4585914, -11189312, 10004786, -8709488, -21761224, 8930324},
+			FieldElement{-21197785, -16396035, 25654216, -1725397, 12282012, 11008919, 1541940, 4757911, -26491501, -16408940},
+		},
+		{
+			FieldElement{13537262, -7759490, -20604840, 10961927, -5922820, -13218065, -13156584, 6217254, -15943699, 13814990},
+			FieldElement{-17422573, 15157790, 18705543, 29619, 24409717, -260476, 27361681, 9257833, -1956526, -1776914},
+			FieldElement{-25045300, -10191966, 15366585, 15166509, -13105086, 8423556, -29171540, 12361135, -18685978, 4578290},
+		},
+		{
+			FieldElement{24579768, 3711570, 1342322, -11180126, -27005135, 14124956, -22544529, 14074919, 21964432, 8235257},
+			FieldElement{-6528613, -2411497, 9442966, -5925588, 12025640, -1487420, -2981514, -1669206, 13006806, 2355433},
+			FieldElement{-16304899, -13605259, -6632427, -5142349, 16974359, -10911083, 27202044, 1719366, 1141648, -12796236},
+		},
+		{
+			FieldElement{-12863944, -13219986, -8318266, -11018091, -6810145, -4843894, 13475066, -3133972, 32674895, 13715045},
+			FieldElement{11423335, -5468059, 32344216, 8962751, 24989809, 9241752, -13265253, 16086212, -28740881, -15642093},
+			FieldElement{-1409668, 12530728, -6368726, 10847387, 19531186, -14132160, -11709148, 7791794, -27245943, 4383347},
+		},
+	},
+	{
+		{
+			FieldElement{-28970898, 5271447, -1266009, -9736989, -12455236, 16732599, -4862407, -4906449, 27193557, 6245191},
+			FieldElement{-15193956, 5362278, -1783893, 2695834, 4960227, 12840725, 23061898, 3260492, 22510453, 8577507},
+			FieldElement{-12632451, 11257346, -32692994, 13548177, -721004, 10879011, 31168030, 13952092, -29571492, -3635906},
+		},
+		{
+			FieldElement{3877321, -9572739, 32416692, 5405324, -11004407, -13656635, 3759769, 11935320, 5611860, 8164018},
+			FieldElement{-16275802, 14667797, 15906460, 12155291, -22111149, -9039718, 32003002, -8832289, 5773085, -8422109},
+			FieldElement{-23788118, -8254300, 1950875, 8937633, 18686727, 16459170, -905725, 12376320, 31632953, 190926},
+		},
+		{
+			FieldElement{-24593607, -16138885, -8423991, 13378746, 14162407, 6901328, -8288749, 4508564, -25341555, -3627528},
+			FieldElement{8884438, -5884009, 6023974, 10104341, -6881569, -4941533, 18722941, -14786005, -1672488, 827625},
+			FieldElement{-32720583, -16289296, -32503547, 7101210, 13354605, 2659080, -1800575, -14108036, -24878478, 1541286},
+		},
+		{
+			FieldElement{2901347, -1117687, 3880376, -10059388, -17620940, -3612781, -21802117, -3567481, 20456845, -1885033},
+			FieldElement{27019610, 12299467, -13658288, -1603234, -12861660, -4861471, -19540150, -5016058, 29439641, 15138866},
+			FieldElement{21536104, -6626420, -32447818, -10690208, -22408077, 5175814, -5420040, -16361163, 7779328, 109896},
+		},
+		{
+			FieldElement{30279744, 14648750, -8044871, 6425558, 13639621, -743509, 28698390, 12180118, 23177719, -554075},
+			FieldElement{26572847, 3405927, -31701700, 12890905, -19265668, 5335866, -6493768, 2378492, 4439158, -13279347},
+			FieldElement{-22716706, 3489070, -9225266, -332753, 18875722, -1140095, 14819434, -12731527, -17717757, -5461437},
+		},
+		{
+			FieldElement{-5056483, 16566551, 15953661, 3767752, -10436499, 15627060, -820954, 2177225, 8550082, -15114165},
+			FieldElement{-18473302, 16596775, -381660, 15663611, 22860960, 15585581, -27844109, -3582739, -23260460, -8428588},
+			FieldElement{-32480551, 15707275, -8205912, -5652081, 29464558, 2713815, -22725137, 15860482, -21902570, 1494193},
+		},
+		{
+			FieldElement{-19562091, -14087393, -25583872, -9299552, 13127842, 759709, 21923482, 16529112, 8742704, 12967017},
+			FieldElement{-28464899, 1553205, 32536856, -10473729, -24691605, -406174, -8914625, -2933896, -29903758, 15553883},
+			FieldElement{21877909, 3230008, 9881174, 10539357, -4797115, 2841332, 11543572, 14513274, 19375923, -12647961},
+		},
+		{
+			FieldElement{8832269, -14495485, 13253511, 5137575, 5037871, 4078777, 24880818, -6222716, 2862653, 9455043},
+			FieldElement{29306751, 5123106, 20245049, -14149889, 9592566, 8447059, -2077124, -2990080, 15511449, 4789663},
+			FieldElement{-20679756, 7004547, 8824831, -9434977, -4045704, -3750736, -5754762, 108893, 23513200, 16652362},
+		},
+	},
+	{
+		{
+			FieldElement{-33256173, 4144782, -4476029, -6579123, 10770039, -7155542, -6650416, -12936300, -18319198, 10212860},
+			FieldElement{2756081, 8598110, 7383731, -6859892, 22312759, -1105012, 21179801, 2600940, -9988298, -12506466},
+			FieldElement{-24645692, 13317462, -30449259, -15653928, 21365574, -10869657, 11344424, 864440, -2499677, -16710063},
+		},
+		{
+			FieldElement{-26432803, 6148329, -17184412, -14474154, 18782929, -275997, -22561534, 211300, 2719757, 4940997},
+			FieldElement{-1323882, 3911313, -6948744, 14759765, -30027150, 7851207, 21690126, 8518463, 26699843, 5276295},
+			FieldElement{-13149873, -6429067, 9396249, 365013, 24703301, -10488939, 1321586, 149635, -15452774, 7159369},
+		},
+		{
+			FieldElement{9987780, -3404759, 17507962, 9505530, 9731535, -2165514, 22356009, 8312176, 22477218, -8403385},
+			FieldElement{18155857, -16504990, 19744716, 9006923, 15154154, -10538976, 24256460, -4864995, -22548173, 9334109},
+			FieldElement{2986088, -4911893, 10776628, -3473844, 10620590, -7083203, -21413845, 14253545, -22587149, 536906},
+		},
+		{
+			FieldElement{4377756, 8115836, 24567078, 15495314, 11625074, 13064599, 7390551, 10589625, 10838060, -15420424},
+			FieldElement{-19342404, 867880, 9277171, -3218459, -14431572, -1986443, 19295826, -15796950, 6378260, 699185},
+			FieldElement{7895026, 4057113, -7081772, -13077756, -17886831, -323126, -716039, 15693155, -5045064, -13373962},
+		},
+		{
+			FieldElement{-7737563, -5869402, -14566319, -7406919, 11385654, 13201616, 31730678, -10962840, -3918636, -9669325},
+			FieldElement{10188286, -15770834, -7336361, 13427543, 22223443, 14896287, 30743455, 7116568, -21786507, 5427593},
+			FieldElement{696102, 13206899, 27047647, -10632082, 15285305, -9853179, 10798490, -4578720, 19236243, 12477404},
+		},
+		{
+			FieldElement{-11229439, 11243796, -17054270, -8040865, -788228, -8167967, -3897669, 11180504, -23169516, 7733644},
+			FieldElement{17800790, -14036179, -27000429, -11766671, 23887827, 3149671, 23466177, -10538171, 10322027, 15313801},
+			FieldElement{26246234, 11968874, 32263343, -5468728, 6830755, -13323031, -15794704, -101982, -24449242, 10890804},
+		},
+		{
+			FieldElement{-31365647, 10271363, -12660625, -6267268, 16690207, -13062544, -14982212, 16484931, 25180797, -5334884},
+			FieldElement{-586574, 10376444, -32586414, -11286356, 19801893, 10997610, 2276632, 9482883, 316878, 13820577},
+			FieldElement{-9882808, -4510367, -2115506, 16457136, -11100081, 11674996, 30756178, -7515054, 30696930, -3712849},
+		},
+		{
+			FieldElement{32988917, -9603412, 12499366, 7910787, -10617257, -11931514, -7342816, -9985397, -32349517, 7392473},
+			FieldElement{-8855661, 15927861, 9866406, -3649411, -2396914, -16655781, -30409476, -9134995, 25112947, -2926644},
+			FieldElement{-2504044, -436966, 25621774, -5678772, 15085042, -5479877, -24884878, -13526194, 5537438, -13914319},
+		},
+	},
+	{
+		{
+			FieldElement{-11225584, 2320285, -9584280, 10149187, -33444663, 5808648, -14876251, -1729667, 31234590, 6090599},
+			FieldElement{-9633316, 116426, 26083934, 2897444, -6364437, -2688086, 609721, 15878753, -6970405, -9034768},
+			FieldElement{-27757857, 247744, -15194774, -9002551, 23288161, -10011936, -23869595, 6503646, 20650474, 1804084},
+		},
+		{
+			FieldElement{-27589786, 15456424, 8972517, 8469608, 15640622, 4439847, 3121995, -10329713, 27842616, -202328},
+			FieldElement{-15306973, 2839644, 22530074, 10026331, 4602058, 5048462, 28248656, 5031932, -11375082, 12714369},
+			FieldElement{20807691, -7270825, 29286141, 11421711, -27876523, -13868230, -21227475, 1035546, -19733229, 12796920},
+		},
+		{
+			FieldElement{12076899, -14301286, -8785001, -11848922, -25012791, 16400684, -17591495, -12899438, 3480665, -15182815},
+			FieldElement{-32361549, 5457597, 28548107, 7833186, 7303070, -11953545, -24363064, -15921875, -33374054, 2771025},
+			FieldElement{-21389266, 421932, 26597266, 6860826, 22486084, -6737172, -17137485, -4210226, -24552282, 15673397},
+		},
+		{
+			FieldElement{-20184622, 2338216, 19788685, -9620956, -4001265, -8740893, -20271184, 4733254, 3727144, -12934448},
+			FieldElement{6120119, 814863, -11794402, -622716, 6812205, -15747771, 2019594, 7975683, 31123697, -10958981},
+			FieldElement{30069250, -11435332, 30434654, 2958439, 18399564, -976289, 12296869, 9204260, -16432438, 9648165},
+		},
+		{
+			FieldElement{32705432, -1550977, 30705658, 7451065, -11805606, 9631813, 3305266, 5248604, -26008332, -11377501},
+			FieldElement{17219865, 2375039, -31570947, -5575615, -19459679, 9219903, 294711, 15298639, 2662509, -16297073},
+			FieldElement{-1172927, -7558695, -4366770, -4287744, -21346413, -8434326, 32087529, -1222777, 32247248, -14389861},
+		},
+		{
+			FieldElement{14312628, 1221556, 17395390, -8700143, -4945741, -8684635, -28197744, -9637817, -16027623, -13378845},
+			FieldElement{-1428825, -9678990, -9235681, 6549687, -7383069, -468664, 23046502, 9803137, 17597934, 2346211},
+			FieldElement{18510800, 15337574, 26171504, 981392, -22241552, 7827556, -23491134, -11323352, 3059833, -11782870},
+		},
+		{
+			FieldElement{10141598, 6082907, 17829293, -1947643, 9830092, 13613136, -25556636, -5544586, -33502212, 3592096},
+			FieldElement{33114168, -15889352, -26525686, -13343397, 33076705, 8716171, 1151462, 1521897, -982665, -6837803},
+			FieldElement{-32939165, -4255815, 23947181, -324178, -33072974, -12305637, -16637686, 3891704, 26353178, 693168},
+		},
+		{
+			FieldElement{30374239, 1595580, -16884039, 13186931, 4600344, 406904, 9585294, -400668, 31375464, 14369965},
+			FieldElement{-14370654, -7772529, 1510301, 6434173, -18784789, -6262728, 32732230, -13108839, 17901441, 16011505},
+			FieldElement{18171223, -11934626, -12500402, 15197122, -11038147, -15230035, -19172240, -16046376, 8764035, 12309598},
+		},
+	},
+	{
+		{
+			FieldElement{5975908, -5243188, -19459362, -9681747, -11541277, 14015782, -23665757, 1228319, 17544096, -10593782},
+			FieldElement{5811932, -1715293, 3442887, -2269310, -18367348, -8359541, -18044043, -15410127, -5565381, 12348900},
+			FieldElement{-31399660, 11407555, 25755363, 6891399, -3256938, 14872274, -24849353, 8141295, -10632534, -585479},
+		},
+		{
+			FieldElement{-12675304, 694026, -5076145, 13300344, 14015258, -14451394, -9698672, -11329050, 30944593, 1130208},
+			FieldElement{8247766, -6710942, -26562381, -7709309, -14401939, -14648910, 4652152, 2488540, 23550156, -271232},
+			FieldElement{17294316, -3788438, 7026748, 15626851, 22990044, 113481, 2267737, -5908146, -408818, -137719},
+		},
+		{
+			FieldElement{16091085, -16253926, 18599252, 7340678, 2137637, -1221657, -3364161, 14550936, 3260525, -7166271},
+			FieldElement{-4910104, -13332887, 18550887, 10864893, -16459325, -7291596, -23028869, -13204905, -12748722, 2701326},
+			FieldElement{-8574695, 16099415, 4629974, -16340524, -20786213, -6005432, -10018363, 9276971, 11329923, 1862132},
+		},
+		{
+			FieldElement{14763076, -15903608, -30918270, 3689867, 3511892, 10313526, -21951088, 12219231, -9037963, -940300},
+			FieldElement{8894987, -3446094, 6150753, 3013931, 301220, 15693451, -31981216, -2909717, -15438168, 11595570},
+			FieldElement{15214962, 3537601, -26238722, -14058872, 4418657, -15230761, 13947276, 10730794, -13489462, -4363670},
+		},
+		{
+			FieldElement{-2538306, 7682793, 32759013, 263109, -29984731, -7955452, -22332124, -10188635, 977108, 699994},
+			FieldElement{-12466472, 4195084, -9211532, 550904, -15565337, 12917920, 19118110, -439841, -30534533, -14337913},
+			FieldElement{31788461, -14507657, 4799989, 7372237, 8808585, -14747943, 9408237, -10051775, 12493932, -5409317},
+		},
+		{
+			FieldElement{-25680606, 5260744, -19235809, -6284470, -3695942, 16566087, 27218280, 2607121, 29375955, 6024730},
+			FieldElement{842132, -2794693, -4763381, -8722815, 26332018, -12405641, 11831880, 6985184, -9940361, 2854096},
+			FieldElement{-4847262, -7969331, 2516242, -5847713, 9695691, -7221186, 16512645, 960770, 12121869, 16648078},
+		},
+		{
+			FieldElement{-15218652, 14667096, -13336229, 2013717, 30598287, -464137, -31504922, -7882064, 20237806, 2838411},
+			FieldElement{-19288047, 4453152, 15298546, -16178388, 22115043, -15972604, 12544294, -13470457, 1068881, -12499905},
+			FieldElement{-9558883, -16518835, 33238498, 13506958, 30505848, -1114596, -8486907, -2630053, 12521378, 4845654},
+		},
+		{
+			FieldElement{-28198521, 10744108, -2958380, 10199664, 7759311, -13088600, 3409348, -873400, -6482306, -12885870},
+			FieldElement{-23561822, 6230156, -20382013, 10655314, -24040585, -11621172, 10477734, -1240216, -3113227, 13974498},
+			FieldElement{12966261, 15550616, -32038948, -1615346, 21025980, -629444, 5642325, 7188737, 18895762, 12629579},
+		},
+	},
+	{
+		{
+			FieldElement{14741879, -14946887, 22177208, -11721237, 1279741, 8058600, 11758140, 789443, 32195181, 3895677},
+			FieldElement{10758205, 15755439, -4509950, 9243698, -4879422, 6879879, -2204575, -3566119, -8982069, 4429647},
+			FieldElement{-2453894, 15725973, -20436342, -10410672, -5803908, -11040220, -7135870, -11642895, 18047436, -15281743},
+		},
+		{
+			FieldElement{-25173001, -11307165, 29759956, 11776784, -22262383, -15820455, 10993114, -12850837, -17620701, -9408468},
+			FieldElement{21987233, 700364, -24505048, 14972008, -7774265, -5718395, 32155026, 2581431, -29958985, 8773375},
+			FieldElement{-25568350, 454463, -13211935, 16126715, 25240068, 8594567, 20656846, 12017935, -7874389, -13920155},
+		},
+		{
+			FieldElement{6028182, 6263078, -31011806, -11301710, -818919, 2461772, -31841174, -5468042, -1721788, -2776725},
+			FieldElement{-12278994, 16624277, 987579, -5922598, 32908203, 1248608, 7719845, -4166698, 28408820, 6816612},
+			FieldElement{-10358094, -8237829, 19549651, -12169222, 22082623, 16147817, 20613181, 13982702, -10339570, 5067943},
+		},
+		{
+			FieldElement{-30505967, -3821767, 12074681, 13582412, -19877972, 2443951, -19719286, 12746132, 5331210, -10105944},
+			FieldElement{30528811, 3601899, -1957090, 4619785, -27361822, -15436388, 24180793, -12570394, 27679908, -1648928},
+			FieldElement{9402404, -13957065, 32834043, 10838634, -26580150, -13237195, 26653274, -8685565, 22611444, -12715406},
+		},
+		{
+			FieldElement{22190590, 1118029, 22736441, 15130463, -30460692, -5991321, 19189625, -4648942, 4854859, 6622139},
+			FieldElement{-8310738, -2953450, -8262579, -3388049, -10401731, -271929, 13424426, -3567227, 26404409, 13001963},
+			FieldElement{-31241838, -15415700, -2994250, 8939346, 11562230, -12840670, -26064365, -11621720, -15405155, 11020693},
+		},
+		{
+			FieldElement{1866042, -7949489, -7898649, -10301010, 12483315, 13477547, 3175636, -12424163, 28761762, 1406734},
+			FieldElement{-448555, -1777666, 13018551, 3194501, -9580420, -11161737, 24760585, -4347088, 25577411, -13378680},
+			FieldElement{-24290378, 4759345, -690653, -1852816, 2066747, 10693769, -29595790, 9884936, -9368926, 4745410},
+		},
+		{
+			FieldElement{-9141284, 6049714, -19531061, -4341411, -31260798, 9944276, -15462008, -11311852, 10931924, -11931931},
+			FieldElement{-16561513, 14112680, -8012645, 4817318, -8040464, -11414606, -22853429, 10856641, -20470770, 13434654},
+			FieldElement{22759489, -10073434, -16766264, -1871422, 13637442, -10168091, 1765144, -12654326, 28445307, -5364710},
+		},
+		{
+			FieldElement{29875063, 12493613, 2795536, -3786330, 1710620, 15181182, -10195717, -8788675, 9074234, 1167180},
+			FieldElement{-26205683, 11014233, -9842651, -2635485, -26908120, 7532294, -18716888, -9535498, 3843903, 9367684},
+			FieldElement{-10969595, -6403711, 9591134, 9582310, 11349256, 108879, 16235123, 8601684, -139197, 4242895},
+		},
+	},
+	{
+		{
+			FieldElement{22092954, -13191123, -2042793, -11968512, 32186753, -11517388, -6574341, 2470660, -27417366, 16625501},
+			FieldElement{-11057722, 3042016, 13770083, -9257922, 584236, -544855, -7770857, 2602725, -27351616, 14247413},
+			FieldElement{6314175, -10264892, -32772502, 15957557, -10157730, 168750, -8618807, 14290061, 27108877, -1180880},
+		},
+		{
+			FieldElement{-8586597, -7170966, 13241782, 10960156, -32991015, -13794596, 33547976, -11058889, -27148451, 981874},
+			FieldElement{22833440, 9293594, -32649448, -13618667, -9136966, 14756819, -22928859, -13970780, -10479804, -16197962},
+			FieldElement{-7768587, 3326786, -28111797, 10783824, 19178761, 14905060, 22680049, 13906969, -15933690, 3797899},
+		},
+		{
+			FieldElement{21721356, -4212746, -12206123, 9310182, -3882239, -13653110, 23740224, -2709232, 20491983, -8042152},
+			FieldElement{9209270, -15135055, -13256557, -6167798, -731016, 15289673, 25947805, 15286587, 30997318, -6703063},
+			FieldElement{7392032, 16618386, 23946583, -8039892, -13265164, -1533858, -14197445, -2321576, 17649998, -250080},
+		},
+		{
+			FieldElement{-9301088, -14193827, 30609526, -3049543, -25175069, -1283752, -15241566, -9525724, -2233253, 7662146},
+			FieldElement{-17558673, 1763594, -33114336, 15908610, -30040870, -12174295, 7335080, -8472199, -3174674, 3440183},
+			FieldElement{-19889700, -5977008, -24111293, -9688870, 10799743, -16571957, 40450, -4431835, 4862400, 1133},
+		},
+		{
+			FieldElement{-32856209, -7873957, -5422389, 14860950, -16319031, 7956142, 7258061, 311861, -30594991, -7379421},
+			FieldElement{-3773428, -1565936, 28985340, 7499440, 24445838, 9325937, 29727763, 16527196, 18278453, 15405622},
+			FieldElement{-4381906, 8508652, -19898366, -3674424, -5984453, 15149970, -13313598, 843523, -21875062, 13626197},
+		},
+		{
+			FieldElement{2281448, -13487055, -10915418, -2609910, 1879358, 16164207, -10783882, 3953792, 13340839, 15928663},
+			FieldElement{31727126, -7179855, -18437503, -8283652, 2875793, -16390330, -25269894, -7014826, -23452306, 5964753},
+			FieldElement{4100420, -5959452, -17179337, 6017714, -18705837, 12227141, -26684835, 11344144, 2538215, -7570755},
+		},
+		{
+			FieldElement{-9433605, 6123113, 11159803, -2156608, 30016280, 14966241, -20474983, 1485421, -629256, -15958862},
+			FieldElement{-26804558, 4260919, 11851389, 9658551, -32017107, 16367492, -20205425, -13191288, 11659922, -11115118},
+			FieldElement{26180396, 10015009, -30844224, -8581293, 5418197, 9480663, 2231568, -10170080, 33100372, -1306171},
+		},
+		{
+			FieldElement{15121113, -5201871, -10389905, 15427821, -27509937, -15992507, 21670947, 4486675, -5931810, -14466380},
+			FieldElement{16166486, -9483733, -11104130, 6023908, -31926798, -1364923, 2340060, -16254968, -10735770, -10039824},
+			FieldElement{28042865, -3557089, -12126526, 12259706, -3717498, -6945899, 6766453, -8689599, 18036436, 5803270},
+		},
+	},
+	{
+		{
+			FieldElement{-817581, 6763912, 11803561, 1585585, 10958447, -2671165, 23855391, 4598332, -6159431, -14117438},
+			FieldElement{-31031306, -14256194, 17332029, -2383520, 31312682, -5967183, 696309, 50292, -20095739, 11763584},
+			FieldElement{-594563, -2514283, -32234153, 12643980, 12650761, 14811489, 665117, -12613632, -19773211, -10713562},
+		},
+		{
+			FieldElement{30464590, -11262872, -4127476, -12734478, 19835327, -7105613, -24396175, 2075773, -17020157, 992471},
+			FieldElement{18357185, -6994433, 7766382, 16342475, -29324918, 411174, 14578841, 8080033, -11574335, -10601610},
+			FieldElement{19598397, 10334610, 12555054, 2555664, 18821899, -10339780, 21873263, 16014234, 26224780, 16452269},
+		},
+		{
+			FieldElement{-30223925, 5145196, 5944548, 16385966, 3976735, 2009897, -11377804, -7618186, -20533829, 3698650},
+			FieldElement{14187449, 3448569, -10636236, -10810935, -22663880, -3433596, 7268410, -10890444, 27394301, 12015369},
+			FieldElement{19695761, 16087646, 28032085, 12999827, 6817792, 11427614, 20244189, -1312777, -13259127, -3402461},
+		},
+		{
+			FieldElement{30860103, 12735208, -1888245, -4699734, -16974906, 2256940, -8166013, 12298312, -8550524, -10393462},
+			FieldElement{-5719826, -11245325, -1910649, 15569035, 26642876, -7587760, -5789354, -15118654, -4976164, 12651793},
+			FieldElement{-2848395, 9953421, 11531313, -5282879, 26895123, -12697089, -13118820, -16517902, 9768698, -2533218},
+		},
+		{
+			FieldElement{-24719459, 1894651, -287698, -4704085, 15348719, -8156530, 32767513, 12765450, 4940095, 10678226},
+			FieldElement{18860224, 15980149, -18987240, -1562570, -26233012, -11071856, -7843882, 13944024, -24372348, 16582019},
+			FieldElement{-15504260, 4970268, -29893044, 4175593, -20993212, -2199756, -11704054, 15444560, -11003761, 7989037},
+		},
+		{
+			FieldElement{31490452, 5568061, -2412803, 2182383, -32336847, 4531686, -32078269, 6200206, -19686113, -14800171},
+			FieldElement{-17308668, -15879940, -31522777, -2831, -32887382, 16375549, 8680158, -16371713, 28550068, -6857132},
+			FieldElement{-28126887, -5688091, 16837845, -1820458, -6850681, 12700016, -30039981, 4364038, 1155602, 5988841},
+		},
+		{
+			FieldElement{21890435, -13272907, -12624011, 12154349, -7831873, 15300496, 23148983, -4470481, 24618407, 8283181},
+			FieldElement{-33136107, -10512751, 9975416, 6841041, -31559793, 16356536, 3070187, -7025928, 1466169, 10740210},
+			FieldElement{-1509399, -15488185, -13503385, -10655916, 32799044, 909394, -13938903, -5779719, -32164649, -15327040},
+		},
+		{
+			FieldElement{3960823, -14267803, -28026090, -15918051, -19404858, 13146868, 15567327, 951507, -3260321, -573935},
+			FieldElement{24740841, 5052253, -30094131, 8961361, 25877428, 6165135, -24368180, 14397372, -7380369, -6144105},
+			FieldElement{-28888365, 3510803, -28103278, -1158478, -11238128, -10631454, -15441463, -14453128, -1625486, -6494814},
+		},
+	},
+	{
+		{
+			FieldElement{793299, -9230478, 8836302, -6235707, -27360908, -2369593, 33152843, -4885251, -9906200, -621852},
+			FieldElement{5666233, 525582, 20782575, -8038419, -24538499, 14657740, 16099374, 1468826, -6171428, -15186581},
+			FieldElement{-4859255, -3779343, -2917758, -6748019, 7778750, 11688288, -30404353, -9871238, -1558923, -9863646},
+		},
+		{
+			FieldElement{10896332, -7719704, 824275, 472601, -19460308, 3009587, 25248958, 14783338, -30581476, -15757844},
+			FieldElement{10566929, 12612572, -31944212, 11118703, -12633376, 12362879, 21752402, 8822496, 24003793, 14264025},
+			FieldElement{27713862, -7355973, -11008240, 9227530, 27050101, 2504721, 23886875, -13117525, 13958495, -5732453},
+		},
+		{
+			FieldElement{-23481610, 4867226, -27247128, 3900521, 29838369, -8212291, -31889399, -10041781, 7340521, -15410068},
+			FieldElement{4646514, -8011124, -22766023, -11532654, 23184553, 8566613, 31366726, -1381061, -15066784, -10375192},
+			FieldElement{-17270517, 12723032, -16993061, 14878794, 21619651, -6197576, 27584817, 3093888, -8843694, 3849921},
+		},
+		{
+			FieldElement{-9064912, 2103172, 25561640, -15125738, -5239824, 9582958, 32477045, -9017955, 5002294, -15550259},
+			FieldElement{-12057553, -11177906, 21115585, -13365155, 8808712, -12030708, 16489530, 13378448, -25845716, 12741426},
+			FieldElement{-5946367, 10645103, -30911586, 15390284, -3286982, -7118677, 24306472, 15852464, 28834118, -7646072},
+		},
+		{
+			FieldElement{-17335748, -9107057, -24531279, 9434953, -8472084, -583362, -13090771, 455841, 20461858, 5491305},
+			FieldElement{13669248, -16095482, -12481974, -10203039, -14569770, -11893198, -24995986, 11293807, -28588204, -9421832},
+			FieldElement{28497928, 6272777, -33022994, 14470570, 8906179, -1225630, 18504674, -14165166, 29867745, -8795943},
+		},
+		{
+			FieldElement{-16207023, 13517196, -27799630, -13697798, 24009064, -6373891, -6367600, -13175392, 22853429, -4012011},
+			FieldElement{24191378, 16712145, -13931797, 15217831, 14542237, 1646131, 18603514, -11037887, 12876623, -2112447},
+			FieldElement{17902668, 4518229, -411702, -2829247, 26878217, 5258055, -12860753, 608397, 16031844, 3723494},
+		},
+		{
+			FieldElement{-28632773, 12763728, -20446446, 7577504, 33001348, -13017745, 17558842, -7872890, 23896954, -4314245},
+			FieldElement{-20005381, -12011952, 31520464, 605201, 2543521, 5991821, -2945064, 7229064, -9919646, -8826859},
+			FieldElement{28816045, 298879, -28165016, -15920938, 19000928, -1665890, -12680833, -2949325, -18051778, -2082915},
+		},
+		{
+			FieldElement{16000882, -344896, 3493092, -11447198, -29504595, -13159789, 12577740, 16041268, -19715240, 7847707},
+			FieldElement{10151868, 10572098, 27312476, 7922682, 14825339, 4723128, -32855931, -6519018, -10020567, 3852848},
+			FieldElement{-11430470, 15697596, -21121557, -4420647, 5386314, 15063598, 16514493, -15932110, 29330899, -15076224},
+		},
+	},
+	{
+		{
+			FieldElement{-25499735, -4378794, -15222908, -6901211, 16615731, 2051784, 3303702, 15490, -27548796, 12314391},
+			FieldElement{15683520, -6003043, 18109120, -9980648, 15337968, -5997823, -16717435, 15921866, 16103996, -3731215},
+			FieldElement{-23169824, -10781249, 13588192, -1628807, -3798557, -1074929, -19273607, 5402699, -29815713, -9841101},
+		},
+		{
+			FieldElement{23190676, 2384583, -32714340, 3462154, -29903655, -1529132, -11266856, 8911517, -25205859, 2739713},
+			FieldElement{21374101, -3554250, -33524649, 9874411, 15377179, 11831242, -33529904, 6134907, 4931255, 11987849},
+			FieldElement{-7732, -2978858, -16223486, 7277597, 105524, -322051, -31480539, 13861388, -30076310, 10117930},
+		},
+		{
+			FieldElement{-29501170, -10744872, -26163768, 13051539, -25625564, 5089643, -6325503, 6704079, 12890019, 15728940},
+			FieldElement{-21972360, -11771379, -951059, -4418840, 14704840, 2695116, 903376, -10428139, 12885167, 8311031},
+			FieldElement{-17516482, 5352194, 10384213, -13811658, 7506451, 13453191, 26423267, 4384730, 1888765, -5435404},
+		},
+		{
+			FieldElement{-25817338, -3107312, -13494599, -3182506, 30896459, -13921729, -32251644, -12707869, -19464434, -3340243},
+			FieldElement{-23607977, -2665774, -526091, 4651136, 5765089, 4618330, 6092245, 14845197, 17151279, -9854116},
+			FieldElement{-24830458, -12733720, -15165978, 10367250, -29530908, -265356, 22825805, -7087279, -16866484, 16176525},
+		},
+		{
+			FieldElement{-23583256, 6564961, 20063689, 3798228, -4740178, 7359225, 2006182, -10363426, -28746253, -10197509},
+			FieldElement{-10626600, -4486402, -13320562, -5125317, 3432136, -6393229, 23632037, -1940610, 32808310, 1099883},
+			FieldElement{15030977, 5768825, -27451236, -2887299, -6427378, -15361371, -15277896, -6809350, 2051441, -15225865},
+		},
+		{
+			FieldElement{-3362323, -7239372, 7517890, 9824992, 23555850, 295369, 5148398, -14154188, -22686354, 16633660},
+			FieldElement{4577086, -16752288, 13249841, -15304328, 19958763, -14537274, 18559670, -10759549, 8402478, -9864273},
+			FieldElement{-28406330, -1051581, -26790155, -907698, -17212414, -11030789, 9453451, -14980072, 17983010, 9967138},
+		},
+		{
+			FieldElement{-25762494, 6524722, 26585488, 9969270, 24709298, 1220360, -1677990, 7806337, 17507396, 3651560},
+			FieldElement{-10420457, -4118111, 14584639, 15971087, -15768321, 8861010, 26556809, -5574557, -18553322, -11357135},
+			FieldElement{2839101, 14284142, 4029895, 3472686, 14402957, 12689363, -26642121, 8459447, -5605463, -7621941},
+		},
+		{
+			FieldElement{-4839289, -3535444, 9744961, 2871048, 25113978, 3187018, -25110813, -849066, 17258084, -7977739},
+			FieldElement{18164541, -10595176, -17154882, -1542417, 19237078, -9745295, 23357533, -15217008, 26908270, 12150756},
+			FieldElement{-30264870, -7647865, 5112249, -7036672, -1499807, -6974257, 43168, -5537701, -32302074, 16215819},
+		},
+	},
+	{
+		{
+			FieldElement{-6898905, 9824394, -12304779, -4401089, -31397141, -6276835, 32574489, 12532905, -7503072, -8675347},
+			FieldElement{-27343522, -16515468, -27151524, -10722951, 946346, 16291093, 254968, 7168080, 21676107, -1943028},
+			FieldElement{21260961, -8424752, -16831886, -11920822, -23677961, 3968121, -3651949, -6215466, -3556191, -7913075},
+		},
+		{
+			FieldElement{16544754, 13250366, -16804428, 15546242, -4583003, 12757258, -2462308, -8680336, -18907032, -9662799},
+			FieldElement{-2415239, -15577728, 18312303, 4964443, -15272530, -12653564, 26820651, 16690659, 25459437, -4564609},
+			FieldElement{-25144690, 11425020, 28423002, -11020557, -6144921, -15826224, 9142795, -2391602, -6432418, -1644817},
+		},
+		{
+			FieldElement{-23104652, 6253476, 16964147, -3768872, -25113972, -12296437, -27457225, -16344658, 6335692, 7249989},
+			FieldElement{-30333227, 13979675, 7503222, -12368314, -11956721, -4621693, -30272269, 2682242, 25993170, -12478523},
+			FieldElement{4364628, 5930691, 32304656, -10044554, -8054781, 15091131, 22857016, -10598955, 31820368, 15075278},
+		},
+		{
+			FieldElement{31879134, -8918693, 17258761, 90626, -8041836, -4917709, 24162788, -9650886, -17970238, 12833045},
+			FieldElement{19073683, 14851414, -24403169, -11860168, 7625278, 11091125, -19619190, 2074449, -9413939, 14905377},
+			FieldElement{24483667, -11935567, -2518866, -11547418, -1553130, 15355506, -25282080, 9253129, 27628530, -7555480},
+		},
+		{
+			FieldElement{17597607, 8340603, 19355617, 552187, 26198470, -3176583, 4593324, -9157582, -14110875, 15297016},
+			FieldElement{510886, 14337390, -31785257, 16638632, 6328095, 2713355, -20217417, -11864220, 8683221, 2921426},
+			FieldElement{18606791, 11874196, 27155355, -5281482, -24031742, 6265446, -25178240, -1278924, 4674690, 13890525},
+		},
+		{
+			FieldElement{13609624, 13069022, -27372361, -13055908, 24360586, 9592974, 14977157, 9835105, 4389687, 288396},
+			FieldElement{9922506, -519394, 13613107, 5883594, -18758345, -434263, -12304062, 8317628, 23388070, 16052080},
+			FieldElement{12720016, 11937594, -31970060, -5028689, 26900120, 8561328, -20155687, -11632979, -14754271, -10812892},
+		},
+		{
+			FieldElement{15961858, 14150409, 26716931, -665832, -22794328, 13603569, 11829573, 7467844, -28822128, 929275},
+			FieldElement{11038231, -11582396, -27310482, -7316562, -10498527, -16307831, -23479533, -9371869, -21393143, 2465074},
+			FieldElement{20017163, -4323226, 27915242, 1529148, 12396362, 15675764, 13817261, -9658066, 2463391, -4622140},
+		},
+		{
+			FieldElement{-16358878, -12663911, -12065183, 4996454, -1256422, 1073572, 9583558, 12851107, 4003896, 12673717},
+			FieldElement{-1731589, -15155870, -3262930, 16143082, 19294135, 13385325, 14741514, -9103726, 7903886, 2348101},
+			FieldElement{24536016, -16515207, 12715592, -3862155, 1511293, 10047386, -3842346, -7129159, -28377538, 10048127},
+		},
+	},
+	{
+		{
+			FieldElement{-12622226, -6204820, 30718825, 2591312, -10617028, 12192840, 18873298, -7297090, -32297756, 15221632},
+			FieldElement{-26478122, -11103864, 11546244, -1852483, 9180880, 7656409, -21343950, 2095755, 29769758, 6593415},
+			FieldElement{-31994208, -2907461, 4176912, 3264766, 12538965, -868111, 26312345, -6118678, 30958054, 8292160},
+		},
+		{
+			FieldElement{31429822, -13959116, 29173532, 15632448, 12174511, -2760094, 32808831, 3977186, 26143136, -3148876},
+			FieldElement{22648901, 1402143, -22799984, 13746059, 7936347, 365344, -8668633, -1674433, -3758243, -2304625},
+			FieldElement{-15491917, 8012313, -2514730, -12702462, -23965846, -10254029, -1612713, -1535569, -16664475, 8194478},
+		},
+		{
+			FieldElement{27338066, -7507420, -7414224, 10140405, -19026427, -6589889, 27277191, 8855376, 28572286, 3005164},
+			FieldElement{26287124, 4821776, 25476601, -4145903, -3764513, -15788984, -18008582, 1182479, -26094821, -13079595},
+			FieldElement{-7171154, 3178080, 23970071, 6201893, -17195577, -4489192, -21876275, -13982627, 32208683, -1198248},
+		},
+		{
+			FieldElement{-16657702, 2817643, -10286362, 14811298, 6024667, 13349505, -27315504, -10497842, -27672585, -11539858},
+			FieldElement{15941029, -9405932, -21367050, 8062055, 31876073, -238629, -15278393, -1444429, 15397331, -4130193},
+			FieldElement{8934485, -13485467, -23286397, -13423241, -32446090, 14047986, 31170398, -1441021, -27505566, 15087184},
+		},
+		{
+			FieldElement{-18357243, -2156491, 24524913, -16677868, 15520427, -6360776, -15502406, 11461896, 16788528, -5868942},
+			FieldElement{-1947386, 16013773, 21750665, 3714552, -17401782, -16055433, -3770287, -10323320, 31322514, -11615635},
+			FieldElement{21426655, -5650218, -13648287, -5347537, -28812189, -4920970, -18275391, -14621414, 13040862, -12112948},
+		},
+		{
+			FieldElement{11293895, 12478086, -27136401, 15083750, -29307421, 14748872, 14555558, -13417103, 1613711, 4896935},
+			FieldElement{-25894883, 15323294, -8489791, -8057900, 25967126, -13425460, 2825960, -4897045, -23971776, -11267415},
+			FieldElement{-15924766, -5229880, -17443532, 6410664, 3622847, 10243618, 20615400, 12405433, -23753030, -8436416},
+		},
+		{
+			FieldElement{-7091295, 12556208, -20191352, 9025187, -17072479, 4333801, 4378436, 2432030, 23097949, -566018},
+			FieldElement{4565804, -16025654, 20084412, -7842817, 1724999, 189254, 24767264, 10103221, -18512313, 2424778},
+			FieldElement{366633, -11976806, 8173090, -6890119, 30788634, 5745705, -7168678, 1344109, -3642553, 12412659},
+		},
+		{
+			FieldElement{-24001791, 7690286, 14929416, -168257, -32210835, -13412986, 24162697, -15326504, -3141501, 11179385},
+			FieldElement{18289522, -14724954, 8056945, 16430056, -21729724, 7842514, -6001441, -1486897, -18684645, -11443503},
+			FieldElement{476239, 6601091, -6152790, -9723375, 17503545, -4863900, 27672959, 13403813, 11052904, 5219329},
+		},
+	},
+	{
+		{
+			FieldElement{20678546, -8375738, -32671898, 8849123, -5009758, 14574752, 31186971, -3973730, 9014762, -8579056},
+			FieldElement{-13644050, -10350239, -15962508, 5075808, -1514661, -11534600, -33102500, 9160280, 8473550, -3256838},
+			FieldElement{24900749, 14435722, 17209120, -15292541, -22592275, 9878983, -7689309, -16335821, -24568481, 11788948},
+		},
+		{
+			FieldElement{-3118155, -11395194, -13802089, 14797441, 9652448, -6845904, -20037437, 10410733, -24568470, -1458691},
+			FieldElement{-15659161, 16736706, -22467150, 10215878, -9097177, 7563911, 11871841, -12505194, -18513325, 8464118},
+			FieldElement{-23400612, 8348507, -14585951, -861714, -3950205, -6373419, 14325289, 8628612, 33313881, -8370517},
+		},
+		{
+			FieldElement{-20186973, -4967935, 22367356, 5271547, -1097117, -4788838, -24805667, -10236854, -8940735, -5818269},
+			FieldElement{-6948785, -1795212, -32625683, -16021179, 32635414, -7374245, 15989197, -12838188, 28358192, -4253904},
+			FieldElement{-23561781, -2799059, -32351682, -1661963, -9147719, 10429267, -16637684, 4072016, -5351664, 5596589},
+		},
+		{
+			FieldElement{-28236598, -3390048, 12312896, 6213178, 3117142, 16078565, 29266239, 2557221, 1768301, 15373193},
+			FieldElement{-7243358, -3246960, -4593467, -7553353, -127927, -912245, -1090902, -4504991, -24660491, 3442910},
+			FieldElement{-30210571, 5124043, 14181784, 8197961, 18964734, -11939093, 22597931, 7176455, -18585478, 13365930},
+		},
+		{
+			FieldElement{-7877390, -1499958, 8324673, 4690079, 6261860, 890446, 24538107, -8570186, -9689599, -3031667},
+			FieldElement{25008904, -10771599, -4305031, -9638010, 16265036, 15721635, 683793, -11823784, 15723479, -15163481},
+			FieldElement{-9660625, 12374379, -27006999, -7026148, -7724114, -12314514, 11879682, 5400171, 519526, -1235876},
+		},
+		{
+			FieldElement{22258397, -16332233, -7869817, 14613016, -22520255, -2950923, -20353881, 7315967, 16648397, 7605640},
+			FieldElement{-8081308, -8464597, -8223311, 9719710, 19259459, -15348212, 23994942, -5281555, -9468848, 4763278},
+			FieldElement{-21699244, 9220969, -15730624, 1084137, -25476107, -2852390, 31088447, -7764523, -11356529, 728112},
+		},
+		{
+			FieldElement{26047220, -11751471, -6900323, -16521798, 24092068, 9158119, -4273545, -12555558, -29365436, -5498272},
+			FieldElement{17510331, -322857, 5854289, 8403524, 17133918, -3112612, -28111007, 12327945, 10750447, 10014012},
+			FieldElement{-10312768, 3936952, 9156313, -8897683, 16498692, -994647, -27481051, -666732, 3424691, 7540221},
+		},
+		{
+			FieldElement{30322361, -6964110, 11361005, -4143317, 7433304, 4989748, -7071422, -16317219, -9244265, 15258046},
+			FieldElement{13054562, -2779497, 19155474, 469045, -12482797, 4566042, 5631406, 2711395, 1062915, -5136345},
+			FieldElement{-19240248, -11254599, -29509029, -7499965, -5835763, 13005411, -6066489, 12194497, 32960380, 1459310},
+		},
+	},
+	{
+		{
+			FieldElement{19852034, 7027924, 23669353, 10020366, 8586503, -6657907, 394197, -6101885, 18638003, -11174937},
+			FieldElement{31395534, 15098109, 26581030, 8030562, -16527914, -5007134, 9012486, -7584354, -6643087, -5442636},
+			FieldElement{-9192165, -2347377, -1997099, 4529534, 25766844, 607986, -13222, 9677543, -32294889, -6456008},
+		},
+		{
+			FieldElement{-2444496, -149937, 29348902, 8186665, 1873760, 12489863, -30934579, -7839692, -7852844, -8138429},
+			FieldElement{-15236356, -15433509, 7766470, 746860, 26346930, -10221762, -27333451, 10754588, -9431476, 5203576},
+			FieldElement{31834314, 14135496, -770007, 5159118, 20917671, -16768096, -7467973, -7337524, 31809243, 7347066},
+		},
+		{
+			FieldElement{-9606723, -11874240, 20414459, 13033986, 13716524, -11691881, 19797970, -12211255, 15192876, -2087490},
+			FieldElement{-12663563, -2181719, 1168162, -3804809, 26747877, -14138091, 10609330, 12694420, 33473243, -13382104},
+			FieldElement{33184999, 11180355, 15832085, -11385430, -1633671, 225884, 15089336, -11023903, -6135662, 14480053},
+		},
+		{
+			FieldElement{31308717, -5619998, 31030840, -1897099, 15674547, -6582883, 5496208, 13685227, 27595050, 8737275},
+			FieldElement{-20318852, -15150239, 10933843, -16178022, 8335352, -7546022, -31008351, -12610604, 26498114, 66511},
+			FieldElement{22644454, -8761729, -16671776, 4884562, -3105614, -13559366, 30540766, -4286747, -13327787, -7515095},
+		},
+		{
+			FieldElement{-28017847, 9834845, 18617207, -2681312, -3401956, -13307506, 8205540, 13585437, -17127465, 15115439},
+			FieldElement{23711543, -672915, 31206561, -8362711, 6164647, -9709987, -33535882, -1426096, 8236921, 16492939},
+			FieldElement{-23910559, -13515526, -26299483, -4503841, 25005590, -7687270, 19574902, 10071562, 6708380, -6222424},
+		},
+		{
+			FieldElement{2101391, -4930054, 19702731, 2367575, -15427167, 1047675, 5301017, 9328700, 29955601, -11678310},
+			FieldElement{3096359, 9271816, -21620864, -15521844, -14847996, -7592937, -25892142, -12635595, -9917575, 6216608},
+			FieldElement{-32615849, 338663, -25195611, 2510422, -29213566, -13820213, 24822830, -6146567, -26767480, 7525079},
+		},
+		{
+			FieldElement{-23066649, -13985623, 16133487, -7896178, -3389565, 778788, -910336, -2782495, -19386633, 11994101},
+			FieldElement{21691500, -13624626, -641331, -14367021, 3285881, -3483596, -25064666, 9718258, -7477437, 13381418},
+			FieldElement{18445390, -4202236, 14979846, 11622458, -1727110, -3582980, 23111648, -6375247, 28535282, 15779576},
+		},
+		{
+			FieldElement{30098053, 3089662, -9234387, 16662135, -21306940, 11308411, -14068454, 12021730, 9955285, -16303356},
+			FieldElement{9734894, -14576830, -7473633, -9138735, 2060392, 11313496, -18426029, 9924399, 20194861, 13380996},
+			FieldElement{-26378102, -7965207, -22167821, 15789297, -18055342, -6168792, -1984914, 15707771, 26342023, 10146099},
+		},
+	},
+	{
+		{
+			FieldElement{-26016874, -219943, 21339191, -41388, 19745256, -2878700, -29637280, 2227040, 21612326, -545728},
+			FieldElement{-13077387, 1184228, 23562814, -5970442, -20351244, -6348714, 25764461, 12243797, -20856566, 11649658},
+			FieldElement{-10031494, 11262626, 27384172, 2271902, 26947504, -15997771, 39944, 6114064, 33514190, 2333242},
+		},
+		{
+			FieldElement{-21433588, -12421821, 8119782, 7219913, -21830522, -9016134, -6679750, -12670638, 24350578, -13450001},
+			FieldElement{-4116307, -11271533, -23886186, 4843615, -30088339, 690623, -31536088, -10406836, 8317860, 12352766},
+			FieldElement{18200138, -14475911, -33087759, -2696619, -23702521, -9102511, -23552096, -2287550, 20712163, 6719373},
+		},
+		{
+			FieldElement{26656208, 6075253, -7858556, 1886072, -28344043, 4262326, 11117530, -3763210, 26224235, -3297458},
+			FieldElement{-17168938, -14854097, -3395676, -16369877, -19954045, 14050420, 21728352, 9493610, 18620611, -16428628},
+			FieldElement{-13323321, 13325349, 11432106, 5964811, 18609221, 6062965, -5269471, -9725556, -30701573, -16479657},
+		},
+		{
+			FieldElement{-23860538, -11233159, 26961357, 1640861, -32413112, -16737940, 12248509, -5240639, 13735342, 1934062},
+			FieldElement{25089769, 6742589, 17081145, -13406266, 21909293, -16067981, -15136294, -3765346, -21277997, 5473616},
+			FieldElement{31883677, -7961101, 1083432, -11572403, 22828471, 13290673, -7125085, 12469656, 29111212, -5451014},
+		},
+		{
+			FieldElement{24244947, -15050407, -26262976, 2791540, -14997599, 16666678, 24367466, 6388839, -10295587, 452383},
+			FieldElement{-25640782, -3417841, 5217916, 16224624, 19987036, -4082269, -24236251, -5915248, 15766062, 8407814},
+			FieldElement{-20406999, 13990231, 15495425, 16395525, 5377168, 15166495, -8917023, -4388953, -8067909, 2276718},
+		},
+		{
+			FieldElement{30157918, 12924066, -17712050, 9245753, 19895028, 3368142, -23827587, 5096219, 22740376, -7303417},
+			FieldElement{2041139, -14256350, 7783687, 13876377, -25946985, -13352459, 24051124, 13742383, -15637599, 13295222},
+			FieldElement{33338237, -8505733, 12532113, 7977527, 9106186, -1715251, -17720195, -4612972, -4451357, -14669444},
+		},
+		{
+			FieldElement{-20045281, 5454097, -14346548, 6447146, 28862071, 1883651, -2469266, -4141880, 7770569, 9620597},
+			FieldElement{23208068, 7979712, 33071466, 8149229, 1758231, -10834995, 30945528, -1694323, -33502340, -14767970},
+			FieldElement{1439958, -16270480, -1079989, -793782, 4625402, 10647766, -5043801, 1220118, 30494170, -11440799},
+		},
+		{
+			FieldElement{-5037580, -13028295, -2970559, -3061767, 15640974, -6701666, -26739026, 926050, -1684339, -13333647},
+			FieldElement{13908495, -3549272, 30919928, -6273825, -21521863, 7989039, 9021034, 9078865, 3353509, 4033511},
+			FieldElement{-29663431, -15113610, 32259991, -344482, 24295849, -12912123, 23161163, 8839127, 27485041, 7356032},
+		},
+	},
+	{
+		{
+			FieldElement{9661027, 705443, 11980065, -5370154, -1628543, 14661173, -6346142, 2625015, 28431036, -16771834},
+			FieldElement{-23839233, -8311415, -25945511, 7480958, -17681669, -8354183, -22545972, 14150565, 15970762, 4099461},
+			FieldElement{29262576, 16756590, 26350592, -8793563, 8529671, -11208050, 13617293, -9937143, 11465739, 8317062},
+		},
+		{
+			FieldElement{-25493081, -6962928, 32500200, -9419051, -23038724, -2302222, 14898637, 3848455, 20969334, -5157516},
+			FieldElement{-20384450, -14347713, -18336405, 13884722, -33039454, 2842114, -21610826, -3649888, 11177095, 14989547},
+			FieldElement{-24496721, -11716016, 16959896, 2278463, 12066309, 10137771, 13515641, 2581286, -28487508, 9930240},
+		},
+		{
+			FieldElement{-17751622, -2097826, 16544300, -13009300, -15914807, -14949081, 18345767, -13403753, 16291481, -5314038},
+			FieldElement{-33229194, 2553288, 32678213, 9875984, 8534129, 6889387, -9676774, 6957617, 4368891, 9788741},
+			FieldElement{16660756, 7281060, -10830758, 12911820, 20108584, -8101676, -21722536, -8613148, 16250552, -11111103},
+		},
+		{
+			FieldElement{-19765507, 2390526, -16551031, 14161980, 1905286, 6414907, 4689584, 10604807, -30190403, 4782747},
+			FieldElement{-1354539, 14736941, -7367442, -13292886, 7710542, -14155590, -9981571, 4383045, 22546403, 437323},
+			FieldElement{31665577, -12180464, -16186830, 1491339, -18368625, 3294682, 27343084, 2786261, -30633590, -14097016},
+		},
+		{
+			FieldElement{-14467279, -683715, -33374107, 7448552, 19294360, 14334329, -19690631, 2355319, -19284671, -6114373},
+			FieldElement{15121312, -15796162, 6377020, -6031361, -10798111, -12957845, 18952177, 15496498, -29380133, 11754228},
+			FieldElement{-2637277, -13483075, 8488727, -14303896, 12728761, -1622493, 7141596, 11724556, 22761615, -10134141},
+		},
+		{
+			FieldElement{16918416, 11729663, -18083579, 3022987, -31015732, -13339659, -28741185, -12227393, 32851222, 11717399},
+			FieldElement{11166634, 7338049, -6722523, 4531520, -29468672, -7302055, 31474879, 3483633, -1193175, -4030831},
+			FieldElement{-185635, 9921305, 31456609, -13536438, -12013818, 13348923, 33142652, 6546660, -19985279, -3948376},
+		},
+		{
+			FieldElement{-32460596, 11266712, -11197107, -7899103, 31703694, 3855903, -8537131, -12833048, -30772034, -15486313},
+			FieldElement{-18006477, 12709068, 3991746, -6479188, -21491523, -10550425, -31135347, -16049879, 10928917, 3011958},
+			FieldElement{-6957757, -15594337, 31696059, 334240, 29576716, 14796075, -30831056, -12805180, 18008031, 10258577},
+		},
+		{
+			FieldElement{-22448644, 15655569, 7018479, -4410003, -30314266, -1201591, -1853465, 1367120, 25127874, 6671743},
+			FieldElement{29701166, -14373934, -10878120, 9279288, -17568, 13127210, 21382910, 11042292, 25838796, 4642684},
+			FieldElement{-20430234, 14955537, -24126347, 8124619, -5369288, -5990470, 30468147, -13900640, 18423289, 4177476},
+		},
+	},
+}

+ 1793 - 0
vendor/golang.org/x/crypto/ed25519/internal/edwards25519/edwards25519.go

@@ -0,0 +1,1793 @@
+// 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 edwards25519
+
+import "encoding/binary"
+
+// This code is a port of the public domain, “ref10” implementation of ed25519
+// from SUPERCOP.
+
+// FieldElement represents an element of the field GF(2^255 - 19).  An element
+// t, entries t[0]...t[9], represents the integer t[0]+2^26 t[1]+2^51 t[2]+2^77
+// t[3]+2^102 t[4]+...+2^230 t[9].  Bounds on each t[i] vary depending on
+// context.
+type FieldElement [10]int32
+
+var zero FieldElement
+
+func FeZero(fe *FieldElement) {
+	copy(fe[:], zero[:])
+}
+
+func FeOne(fe *FieldElement) {
+	FeZero(fe)
+	fe[0] = 1
+}
+
+func FeAdd(dst, a, b *FieldElement) {
+	dst[0] = a[0] + b[0]
+	dst[1] = a[1] + b[1]
+	dst[2] = a[2] + b[2]
+	dst[3] = a[3] + b[3]
+	dst[4] = a[4] + b[4]
+	dst[5] = a[5] + b[5]
+	dst[6] = a[6] + b[6]
+	dst[7] = a[7] + b[7]
+	dst[8] = a[8] + b[8]
+	dst[9] = a[9] + b[9]
+}
+
+func FeSub(dst, a, b *FieldElement) {
+	dst[0] = a[0] - b[0]
+	dst[1] = a[1] - b[1]
+	dst[2] = a[2] - b[2]
+	dst[3] = a[3] - b[3]
+	dst[4] = a[4] - b[4]
+	dst[5] = a[5] - b[5]
+	dst[6] = a[6] - b[6]
+	dst[7] = a[7] - b[7]
+	dst[8] = a[8] - b[8]
+	dst[9] = a[9] - b[9]
+}
+
+func FeCopy(dst, src *FieldElement) {
+	copy(dst[:], src[:])
+}
+
+// Replace (f,g) with (g,g) if b == 1;
+// replace (f,g) with (f,g) if b == 0.
+//
+// Preconditions: b in {0,1}.
+func FeCMove(f, g *FieldElement, b int32) {
+	b = -b
+	f[0] ^= b & (f[0] ^ g[0])
+	f[1] ^= b & (f[1] ^ g[1])
+	f[2] ^= b & (f[2] ^ g[2])
+	f[3] ^= b & (f[3] ^ g[3])
+	f[4] ^= b & (f[4] ^ g[4])
+	f[5] ^= b & (f[5] ^ g[5])
+	f[6] ^= b & (f[6] ^ g[6])
+	f[7] ^= b & (f[7] ^ g[7])
+	f[8] ^= b & (f[8] ^ g[8])
+	f[9] ^= b & (f[9] ^ g[9])
+}
+
+func load3(in []byte) int64 {
+	var r int64
+	r = int64(in[0])
+	r |= int64(in[1]) << 8
+	r |= int64(in[2]) << 16
+	return r
+}
+
+func load4(in []byte) int64 {
+	var r int64
+	r = int64(in[0])
+	r |= int64(in[1]) << 8
+	r |= int64(in[2]) << 16
+	r |= int64(in[3]) << 24
+	return r
+}
+
+func FeFromBytes(dst *FieldElement, src *[32]byte) {
+	h0 := load4(src[:])
+	h1 := load3(src[4:]) << 6
+	h2 := load3(src[7:]) << 5
+	h3 := load3(src[10:]) << 3
+	h4 := load3(src[13:]) << 2
+	h5 := load4(src[16:])
+	h6 := load3(src[20:]) << 7
+	h7 := load3(src[23:]) << 5
+	h8 := load3(src[26:]) << 4
+	h9 := (load3(src[29:]) & 8388607) << 2
+
+	FeCombine(dst, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9)
+}
+
+// FeToBytes marshals h to s.
+// Preconditions:
+//   |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
+//
+// Write p=2^255-19; q=floor(h/p).
+// Basic claim: q = floor(2^(-255)(h + 19 2^(-25)h9 + 2^(-1))).
+//
+// Proof:
+//   Have |h|<=p so |q|<=1 so |19^2 2^(-255) q|<1/4.
+//   Also have |h-2^230 h9|<2^230 so |19 2^(-255)(h-2^230 h9)|<1/4.
+//
+//   Write y=2^(-1)-19^2 2^(-255)q-19 2^(-255)(h-2^230 h9).
+//   Then 0<y<1.
+//
+//   Write r=h-pq.
+//   Have 0<=r<=p-1=2^255-20.
+//   Thus 0<=r+19(2^-255)r<r+19(2^-255)2^255<=2^255-1.
+//
+//   Write x=r+19(2^-255)r+y.
+//   Then 0<x<2^255 so floor(2^(-255)x) = 0 so floor(q+2^(-255)x) = q.
+//
+//   Have q+2^(-255)x = 2^(-255)(h + 19 2^(-25) h9 + 2^(-1))
+//   so floor(2^(-255)(h + 19 2^(-25) h9 + 2^(-1))) = q.
+func FeToBytes(s *[32]byte, h *FieldElement) {
+	var carry [10]int32
+
+	q := (19*h[9] + (1 << 24)) >> 25
+	q = (h[0] + q) >> 26
+	q = (h[1] + q) >> 25
+	q = (h[2] + q) >> 26
+	q = (h[3] + q) >> 25
+	q = (h[4] + q) >> 26
+	q = (h[5] + q) >> 25
+	q = (h[6] + q) >> 26
+	q = (h[7] + q) >> 25
+	q = (h[8] + q) >> 26
+	q = (h[9] + q) >> 25
+
+	// Goal: Output h-(2^255-19)q, which is between 0 and 2^255-20.
+	h[0] += 19 * q
+	// Goal: Output h-2^255 q, which is between 0 and 2^255-20.
+
+	carry[0] = h[0] >> 26
+	h[1] += carry[0]
+	h[0] -= carry[0] << 26
+	carry[1] = h[1] >> 25
+	h[2] += carry[1]
+	h[1] -= carry[1] << 25
+	carry[2] = h[2] >> 26
+	h[3] += carry[2]
+	h[2] -= carry[2] << 26
+	carry[3] = h[3] >> 25
+	h[4] += carry[3]
+	h[3] -= carry[3] << 25
+	carry[4] = h[4] >> 26
+	h[5] += carry[4]
+	h[4] -= carry[4] << 26
+	carry[5] = h[5] >> 25
+	h[6] += carry[5]
+	h[5] -= carry[5] << 25
+	carry[6] = h[6] >> 26
+	h[7] += carry[6]
+	h[6] -= carry[6] << 26
+	carry[7] = h[7] >> 25
+	h[8] += carry[7]
+	h[7] -= carry[7] << 25
+	carry[8] = h[8] >> 26
+	h[9] += carry[8]
+	h[8] -= carry[8] << 26
+	carry[9] = h[9] >> 25
+	h[9] -= carry[9] << 25
+	// h10 = carry9
+
+	// Goal: Output h[0]+...+2^255 h10-2^255 q, which is between 0 and 2^255-20.
+	// Have h[0]+...+2^230 h[9] between 0 and 2^255-1;
+	// evidently 2^255 h10-2^255 q = 0.
+	// Goal: Output h[0]+...+2^230 h[9].
+
+	s[0] = byte(h[0] >> 0)
+	s[1] = byte(h[0] >> 8)
+	s[2] = byte(h[0] >> 16)
+	s[3] = byte((h[0] >> 24) | (h[1] << 2))
+	s[4] = byte(h[1] >> 6)
+	s[5] = byte(h[1] >> 14)
+	s[6] = byte((h[1] >> 22) | (h[2] << 3))
+	s[7] = byte(h[2] >> 5)
+	s[8] = byte(h[2] >> 13)
+	s[9] = byte((h[2] >> 21) | (h[3] << 5))
+	s[10] = byte(h[3] >> 3)
+	s[11] = byte(h[3] >> 11)
+	s[12] = byte((h[3] >> 19) | (h[4] << 6))
+	s[13] = byte(h[4] >> 2)
+	s[14] = byte(h[4] >> 10)
+	s[15] = byte(h[4] >> 18)
+	s[16] = byte(h[5] >> 0)
+	s[17] = byte(h[5] >> 8)
+	s[18] = byte(h[5] >> 16)
+	s[19] = byte((h[5] >> 24) | (h[6] << 1))
+	s[20] = byte(h[6] >> 7)
+	s[21] = byte(h[6] >> 15)
+	s[22] = byte((h[6] >> 23) | (h[7] << 3))
+	s[23] = byte(h[7] >> 5)
+	s[24] = byte(h[7] >> 13)
+	s[25] = byte((h[7] >> 21) | (h[8] << 4))
+	s[26] = byte(h[8] >> 4)
+	s[27] = byte(h[8] >> 12)
+	s[28] = byte((h[8] >> 20) | (h[9] << 6))
+	s[29] = byte(h[9] >> 2)
+	s[30] = byte(h[9] >> 10)
+	s[31] = byte(h[9] >> 18)
+}
+
+func FeIsNegative(f *FieldElement) byte {
+	var s [32]byte
+	FeToBytes(&s, f)
+	return s[0] & 1
+}
+
+func FeIsNonZero(f *FieldElement) int32 {
+	var s [32]byte
+	FeToBytes(&s, f)
+	var x uint8
+	for _, b := range s {
+		x |= b
+	}
+	x |= x >> 4
+	x |= x >> 2
+	x |= x >> 1
+	return int32(x & 1)
+}
+
+// FeNeg sets h = -f
+//
+// Preconditions:
+//    |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
+//
+// Postconditions:
+//    |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
+func FeNeg(h, f *FieldElement) {
+	h[0] = -f[0]
+	h[1] = -f[1]
+	h[2] = -f[2]
+	h[3] = -f[3]
+	h[4] = -f[4]
+	h[5] = -f[5]
+	h[6] = -f[6]
+	h[7] = -f[7]
+	h[8] = -f[8]
+	h[9] = -f[9]
+}
+
+func FeCombine(h *FieldElement, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9 int64) {
+	var c0, c1, c2, c3, c4, c5, c6, c7, c8, c9 int64
+
+	/*
+	  |h0| <= (1.1*1.1*2^52*(1+19+19+19+19)+1.1*1.1*2^50*(38+38+38+38+38))
+	    i.e. |h0| <= 1.2*2^59; narrower ranges for h2, h4, h6, h8
+	  |h1| <= (1.1*1.1*2^51*(1+1+19+19+19+19+19+19+19+19))
+	    i.e. |h1| <= 1.5*2^58; narrower ranges for h3, h5, h7, h9
+	*/
+
+	c0 = (h0 + (1 << 25)) >> 26
+	h1 += c0
+	h0 -= c0 << 26
+	c4 = (h4 + (1 << 25)) >> 26
+	h5 += c4
+	h4 -= c4 << 26
+	/* |h0| <= 2^25 */
+	/* |h4| <= 2^25 */
+	/* |h1| <= 1.51*2^58 */
+	/* |h5| <= 1.51*2^58 */
+
+	c1 = (h1 + (1 << 24)) >> 25
+	h2 += c1
+	h1 -= c1 << 25
+	c5 = (h5 + (1 << 24)) >> 25
+	h6 += c5
+	h5 -= c5 << 25
+	/* |h1| <= 2^24; from now on fits into int32 */
+	/* |h5| <= 2^24; from now on fits into int32 */
+	/* |h2| <= 1.21*2^59 */
+	/* |h6| <= 1.21*2^59 */
+
+	c2 = (h2 + (1 << 25)) >> 26
+	h3 += c2
+	h2 -= c2 << 26
+	c6 = (h6 + (1 << 25)) >> 26
+	h7 += c6
+	h6 -= c6 << 26
+	/* |h2| <= 2^25; from now on fits into int32 unchanged */
+	/* |h6| <= 2^25; from now on fits into int32 unchanged */
+	/* |h3| <= 1.51*2^58 */
+	/* |h7| <= 1.51*2^58 */
+
+	c3 = (h3 + (1 << 24)) >> 25
+	h4 += c3
+	h3 -= c3 << 25
+	c7 = (h7 + (1 << 24)) >> 25
+	h8 += c7
+	h7 -= c7 << 25
+	/* |h3| <= 2^24; from now on fits into int32 unchanged */
+	/* |h7| <= 2^24; from now on fits into int32 unchanged */
+	/* |h4| <= 1.52*2^33 */
+	/* |h8| <= 1.52*2^33 */
+
+	c4 = (h4 + (1 << 25)) >> 26
+	h5 += c4
+	h4 -= c4 << 26
+	c8 = (h8 + (1 << 25)) >> 26
+	h9 += c8
+	h8 -= c8 << 26
+	/* |h4| <= 2^25; from now on fits into int32 unchanged */
+	/* |h8| <= 2^25; from now on fits into int32 unchanged */
+	/* |h5| <= 1.01*2^24 */
+	/* |h9| <= 1.51*2^58 */
+
+	c9 = (h9 + (1 << 24)) >> 25
+	h0 += c9 * 19
+	h9 -= c9 << 25
+	/* |h9| <= 2^24; from now on fits into int32 unchanged */
+	/* |h0| <= 1.8*2^37 */
+
+	c0 = (h0 + (1 << 25)) >> 26
+	h1 += c0
+	h0 -= c0 << 26
+	/* |h0| <= 2^25; from now on fits into int32 unchanged */
+	/* |h1| <= 1.01*2^24 */
+
+	h[0] = int32(h0)
+	h[1] = int32(h1)
+	h[2] = int32(h2)
+	h[3] = int32(h3)
+	h[4] = int32(h4)
+	h[5] = int32(h5)
+	h[6] = int32(h6)
+	h[7] = int32(h7)
+	h[8] = int32(h8)
+	h[9] = int32(h9)
+}
+
+// FeMul calculates h = f * g
+// Can overlap h with f or g.
+//
+// Preconditions:
+//    |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
+//    |g| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
+//
+// Postconditions:
+//    |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
+//
+// Notes on implementation strategy:
+//
+// Using schoolbook multiplication.
+// Karatsuba would save a little in some cost models.
+//
+// Most multiplications by 2 and 19 are 32-bit precomputations;
+// cheaper than 64-bit postcomputations.
+//
+// There is one remaining multiplication by 19 in the carry chain;
+// one *19 precomputation can be merged into this,
+// but the resulting data flow is considerably less clean.
+//
+// There are 12 carries below.
+// 10 of them are 2-way parallelizable and vectorizable.
+// Can get away with 11 carries, but then data flow is much deeper.
+//
+// With tighter constraints on inputs, can squeeze carries into int32.
+func FeMul(h, f, g *FieldElement) {
+	f0 := int64(f[0])
+	f1 := int64(f[1])
+	f2 := int64(f[2])
+	f3 := int64(f[3])
+	f4 := int64(f[4])
+	f5 := int64(f[5])
+	f6 := int64(f[6])
+	f7 := int64(f[7])
+	f8 := int64(f[8])
+	f9 := int64(f[9])
+
+	f1_2 := int64(2 * f[1])
+	f3_2 := int64(2 * f[3])
+	f5_2 := int64(2 * f[5])
+	f7_2 := int64(2 * f[7])
+	f9_2 := int64(2 * f[9])
+
+	g0 := int64(g[0])
+	g1 := int64(g[1])
+	g2 := int64(g[2])
+	g3 := int64(g[3])
+	g4 := int64(g[4])
+	g5 := int64(g[5])
+	g6 := int64(g[6])
+	g7 := int64(g[7])
+	g8 := int64(g[8])
+	g9 := int64(g[9])
+
+	g1_19 := int64(19 * g[1]) /* 1.4*2^29 */
+	g2_19 := int64(19 * g[2]) /* 1.4*2^30; still ok */
+	g3_19 := int64(19 * g[3])
+	g4_19 := int64(19 * g[4])
+	g5_19 := int64(19 * g[5])
+	g6_19 := int64(19 * g[6])
+	g7_19 := int64(19 * g[7])
+	g8_19 := int64(19 * g[8])
+	g9_19 := int64(19 * g[9])
+
+	h0 := f0*g0 + f1_2*g9_19 + f2*g8_19 + f3_2*g7_19 + f4*g6_19 + f5_2*g5_19 + f6*g4_19 + f7_2*g3_19 + f8*g2_19 + f9_2*g1_19
+	h1 := f0*g1 + f1*g0 + f2*g9_19 + f3*g8_19 + f4*g7_19 + f5*g6_19 + f6*g5_19 + f7*g4_19 + f8*g3_19 + f9*g2_19
+	h2 := f0*g2 + f1_2*g1 + f2*g0 + f3_2*g9_19 + f4*g8_19 + f5_2*g7_19 + f6*g6_19 + f7_2*g5_19 + f8*g4_19 + f9_2*g3_19
+	h3 := f0*g3 + f1*g2 + f2*g1 + f3*g0 + f4*g9_19 + f5*g8_19 + f6*g7_19 + f7*g6_19 + f8*g5_19 + f9*g4_19
+	h4 := f0*g4 + f1_2*g3 + f2*g2 + f3_2*g1 + f4*g0 + f5_2*g9_19 + f6*g8_19 + f7_2*g7_19 + f8*g6_19 + f9_2*g5_19
+	h5 := f0*g5 + f1*g4 + f2*g3 + f3*g2 + f4*g1 + f5*g0 + f6*g9_19 + f7*g8_19 + f8*g7_19 + f9*g6_19
+	h6 := f0*g6 + f1_2*g5 + f2*g4 + f3_2*g3 + f4*g2 + f5_2*g1 + f6*g0 + f7_2*g9_19 + f8*g8_19 + f9_2*g7_19
+	h7 := f0*g7 + f1*g6 + f2*g5 + f3*g4 + f4*g3 + f5*g2 + f6*g1 + f7*g0 + f8*g9_19 + f9*g8_19
+	h8 := f0*g8 + f1_2*g7 + f2*g6 + f3_2*g5 + f4*g4 + f5_2*g3 + f6*g2 + f7_2*g1 + f8*g0 + f9_2*g9_19
+	h9 := f0*g9 + f1*g8 + f2*g7 + f3*g6 + f4*g5 + f5*g4 + f6*g3 + f7*g2 + f8*g1 + f9*g0
+
+	FeCombine(h, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9)
+}
+
+func feSquare(f *FieldElement) (h0, h1, h2, h3, h4, h5, h6, h7, h8, h9 int64) {
+	f0 := int64(f[0])
+	f1 := int64(f[1])
+	f2 := int64(f[2])
+	f3 := int64(f[3])
+	f4 := int64(f[4])
+	f5 := int64(f[5])
+	f6 := int64(f[6])
+	f7 := int64(f[7])
+	f8 := int64(f[8])
+	f9 := int64(f[9])
+	f0_2 := int64(2 * f[0])
+	f1_2 := int64(2 * f[1])
+	f2_2 := int64(2 * f[2])
+	f3_2 := int64(2 * f[3])
+	f4_2 := int64(2 * f[4])
+	f5_2 := int64(2 * f[5])
+	f6_2 := int64(2 * f[6])
+	f7_2 := int64(2 * f[7])
+	f5_38 := 38 * f5 // 1.31*2^30
+	f6_19 := 19 * f6 // 1.31*2^30
+	f7_38 := 38 * f7 // 1.31*2^30
+	f8_19 := 19 * f8 // 1.31*2^30
+	f9_38 := 38 * f9 // 1.31*2^30
+
+	h0 = f0*f0 + f1_2*f9_38 + f2_2*f8_19 + f3_2*f7_38 + f4_2*f6_19 + f5*f5_38
+	h1 = f0_2*f1 + f2*f9_38 + f3_2*f8_19 + f4*f7_38 + f5_2*f6_19
+	h2 = f0_2*f2 + f1_2*f1 + f3_2*f9_38 + f4_2*f8_19 + f5_2*f7_38 + f6*f6_19
+	h3 = f0_2*f3 + f1_2*f2 + f4*f9_38 + f5_2*f8_19 + f6*f7_38
+	h4 = f0_2*f4 + f1_2*f3_2 + f2*f2 + f5_2*f9_38 + f6_2*f8_19 + f7*f7_38
+	h5 = f0_2*f5 + f1_2*f4 + f2_2*f3 + f6*f9_38 + f7_2*f8_19
+	h6 = f0_2*f6 + f1_2*f5_2 + f2_2*f4 + f3_2*f3 + f7_2*f9_38 + f8*f8_19
+	h7 = f0_2*f7 + f1_2*f6 + f2_2*f5 + f3_2*f4 + f8*f9_38
+	h8 = f0_2*f8 + f1_2*f7_2 + f2_2*f6 + f3_2*f5_2 + f4*f4 + f9*f9_38
+	h9 = f0_2*f9 + f1_2*f8 + f2_2*f7 + f3_2*f6 + f4_2*f5
+
+	return
+}
+
+// FeSquare calculates h = f*f. Can overlap h with f.
+//
+// Preconditions:
+//    |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
+//
+// Postconditions:
+//    |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
+func FeSquare(h, f *FieldElement) {
+	h0, h1, h2, h3, h4, h5, h6, h7, h8, h9 := feSquare(f)
+	FeCombine(h, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9)
+}
+
+// FeSquare2 sets h = 2 * f * f
+//
+// Can overlap h with f.
+//
+// Preconditions:
+//    |f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc.
+//
+// Postconditions:
+//    |h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc.
+// See fe_mul.c for discussion of implementation strategy.
+func FeSquare2(h, f *FieldElement) {
+	h0, h1, h2, h3, h4, h5, h6, h7, h8, h9 := feSquare(f)
+
+	h0 += h0
+	h1 += h1
+	h2 += h2
+	h3 += h3
+	h4 += h4
+	h5 += h5
+	h6 += h6
+	h7 += h7
+	h8 += h8
+	h9 += h9
+
+	FeCombine(h, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9)
+}
+
+func FeInvert(out, z *FieldElement) {
+	var t0, t1, t2, t3 FieldElement
+	var i int
+
+	FeSquare(&t0, z)        // 2^1
+	FeSquare(&t1, &t0)      // 2^2
+	for i = 1; i < 2; i++ { // 2^3
+		FeSquare(&t1, &t1)
+	}
+	FeMul(&t1, z, &t1)      // 2^3 + 2^0
+	FeMul(&t0, &t0, &t1)    // 2^3 + 2^1 + 2^0
+	FeSquare(&t2, &t0)      // 2^4 + 2^2 + 2^1
+	FeMul(&t1, &t1, &t2)    // 2^4 + 2^3 + 2^2 + 2^1 + 2^0
+	FeSquare(&t2, &t1)      // 5,4,3,2,1
+	for i = 1; i < 5; i++ { // 9,8,7,6,5
+		FeSquare(&t2, &t2)
+	}
+	FeMul(&t1, &t2, &t1)     // 9,8,7,6,5,4,3,2,1,0
+	FeSquare(&t2, &t1)       // 10..1
+	for i = 1; i < 10; i++ { // 19..10
+		FeSquare(&t2, &t2)
+	}
+	FeMul(&t2, &t2, &t1)     // 19..0
+	FeSquare(&t3, &t2)       // 20..1
+	for i = 1; i < 20; i++ { // 39..20
+		FeSquare(&t3, &t3)
+	}
+	FeMul(&t2, &t3, &t2)     // 39..0
+	FeSquare(&t2, &t2)       // 40..1
+	for i = 1; i < 10; i++ { // 49..10
+		FeSquare(&t2, &t2)
+	}
+	FeMul(&t1, &t2, &t1)     // 49..0
+	FeSquare(&t2, &t1)       // 50..1
+	for i = 1; i < 50; i++ { // 99..50
+		FeSquare(&t2, &t2)
+	}
+	FeMul(&t2, &t2, &t1)      // 99..0
+	FeSquare(&t3, &t2)        // 100..1
+	for i = 1; i < 100; i++ { // 199..100
+		FeSquare(&t3, &t3)
+	}
+	FeMul(&t2, &t3, &t2)     // 199..0
+	FeSquare(&t2, &t2)       // 200..1
+	for i = 1; i < 50; i++ { // 249..50
+		FeSquare(&t2, &t2)
+	}
+	FeMul(&t1, &t2, &t1)    // 249..0
+	FeSquare(&t1, &t1)      // 250..1
+	for i = 1; i < 5; i++ { // 254..5
+		FeSquare(&t1, &t1)
+	}
+	FeMul(out, &t1, &t0) // 254..5,3,1,0
+}
+
+func fePow22523(out, z *FieldElement) {
+	var t0, t1, t2 FieldElement
+	var i int
+
+	FeSquare(&t0, z)
+	for i = 1; i < 1; i++ {
+		FeSquare(&t0, &t0)
+	}
+	FeSquare(&t1, &t0)
+	for i = 1; i < 2; i++ {
+		FeSquare(&t1, &t1)
+	}
+	FeMul(&t1, z, &t1)
+	FeMul(&t0, &t0, &t1)
+	FeSquare(&t0, &t0)
+	for i = 1; i < 1; i++ {
+		FeSquare(&t0, &t0)
+	}
+	FeMul(&t0, &t1, &t0)
+	FeSquare(&t1, &t0)
+	for i = 1; i < 5; i++ {
+		FeSquare(&t1, &t1)
+	}
+	FeMul(&t0, &t1, &t0)
+	FeSquare(&t1, &t0)
+	for i = 1; i < 10; i++ {
+		FeSquare(&t1, &t1)
+	}
+	FeMul(&t1, &t1, &t0)
+	FeSquare(&t2, &t1)
+	for i = 1; i < 20; i++ {
+		FeSquare(&t2, &t2)
+	}
+	FeMul(&t1, &t2, &t1)
+	FeSquare(&t1, &t1)
+	for i = 1; i < 10; i++ {
+		FeSquare(&t1, &t1)
+	}
+	FeMul(&t0, &t1, &t0)
+	FeSquare(&t1, &t0)
+	for i = 1; i < 50; i++ {
+		FeSquare(&t1, &t1)
+	}
+	FeMul(&t1, &t1, &t0)
+	FeSquare(&t2, &t1)
+	for i = 1; i < 100; i++ {
+		FeSquare(&t2, &t2)
+	}
+	FeMul(&t1, &t2, &t1)
+	FeSquare(&t1, &t1)
+	for i = 1; i < 50; i++ {
+		FeSquare(&t1, &t1)
+	}
+	FeMul(&t0, &t1, &t0)
+	FeSquare(&t0, &t0)
+	for i = 1; i < 2; i++ {
+		FeSquare(&t0, &t0)
+	}
+	FeMul(out, &t0, z)
+}
+
+// Group elements are members of the elliptic curve -x^2 + y^2 = 1 + d * x^2 *
+// y^2 where d = -121665/121666.
+//
+// Several representations are used:
+//   ProjectiveGroupElement: (X:Y:Z) satisfying x=X/Z, y=Y/Z
+//   ExtendedGroupElement: (X:Y:Z:T) satisfying x=X/Z, y=Y/Z, XY=ZT
+//   CompletedGroupElement: ((X:Z),(Y:T)) satisfying x=X/Z, y=Y/T
+//   PreComputedGroupElement: (y+x,y-x,2dxy)
+
+type ProjectiveGroupElement struct {
+	X, Y, Z FieldElement
+}
+
+type ExtendedGroupElement struct {
+	X, Y, Z, T FieldElement
+}
+
+type CompletedGroupElement struct {
+	X, Y, Z, T FieldElement
+}
+
+type PreComputedGroupElement struct {
+	yPlusX, yMinusX, xy2d FieldElement
+}
+
+type CachedGroupElement struct {
+	yPlusX, yMinusX, Z, T2d FieldElement
+}
+
+func (p *ProjectiveGroupElement) Zero() {
+	FeZero(&p.X)
+	FeOne(&p.Y)
+	FeOne(&p.Z)
+}
+
+func (p *ProjectiveGroupElement) Double(r *CompletedGroupElement) {
+	var t0 FieldElement
+
+	FeSquare(&r.X, &p.X)
+	FeSquare(&r.Z, &p.Y)
+	FeSquare2(&r.T, &p.Z)
+	FeAdd(&r.Y, &p.X, &p.Y)
+	FeSquare(&t0, &r.Y)
+	FeAdd(&r.Y, &r.Z, &r.X)
+	FeSub(&r.Z, &r.Z, &r.X)
+	FeSub(&r.X, &t0, &r.Y)
+	FeSub(&r.T, &r.T, &r.Z)
+}
+
+func (p *ProjectiveGroupElement) ToBytes(s *[32]byte) {
+	var recip, x, y FieldElement
+
+	FeInvert(&recip, &p.Z)
+	FeMul(&x, &p.X, &recip)
+	FeMul(&y, &p.Y, &recip)
+	FeToBytes(s, &y)
+	s[31] ^= FeIsNegative(&x) << 7
+}
+
+func (p *ExtendedGroupElement) Zero() {
+	FeZero(&p.X)
+	FeOne(&p.Y)
+	FeOne(&p.Z)
+	FeZero(&p.T)
+}
+
+func (p *ExtendedGroupElement) Double(r *CompletedGroupElement) {
+	var q ProjectiveGroupElement
+	p.ToProjective(&q)
+	q.Double(r)
+}
+
+func (p *ExtendedGroupElement) ToCached(r *CachedGroupElement) {
+	FeAdd(&r.yPlusX, &p.Y, &p.X)
+	FeSub(&r.yMinusX, &p.Y, &p.X)
+	FeCopy(&r.Z, &p.Z)
+	FeMul(&r.T2d, &p.T, &d2)
+}
+
+func (p *ExtendedGroupElement) ToProjective(r *ProjectiveGroupElement) {
+	FeCopy(&r.X, &p.X)
+	FeCopy(&r.Y, &p.Y)
+	FeCopy(&r.Z, &p.Z)
+}
+
+func (p *ExtendedGroupElement) ToBytes(s *[32]byte) {
+	var recip, x, y FieldElement
+
+	FeInvert(&recip, &p.Z)
+	FeMul(&x, &p.X, &recip)
+	FeMul(&y, &p.Y, &recip)
+	FeToBytes(s, &y)
+	s[31] ^= FeIsNegative(&x) << 7
+}
+
+func (p *ExtendedGroupElement) FromBytes(s *[32]byte) bool {
+	var u, v, v3, vxx, check FieldElement
+
+	FeFromBytes(&p.Y, s)
+	FeOne(&p.Z)
+	FeSquare(&u, &p.Y)
+	FeMul(&v, &u, &d)
+	FeSub(&u, &u, &p.Z) // y = y^2-1
+	FeAdd(&v, &v, &p.Z) // v = dy^2+1
+
+	FeSquare(&v3, &v)
+	FeMul(&v3, &v3, &v) // v3 = v^3
+	FeSquare(&p.X, &v3)
+	FeMul(&p.X, &p.X, &v)
+	FeMul(&p.X, &p.X, &u) // x = uv^7
+
+	fePow22523(&p.X, &p.X) // x = (uv^7)^((q-5)/8)
+	FeMul(&p.X, &p.X, &v3)
+	FeMul(&p.X, &p.X, &u) // x = uv^3(uv^7)^((q-5)/8)
+
+	var tmpX, tmp2 [32]byte
+
+	FeSquare(&vxx, &p.X)
+	FeMul(&vxx, &vxx, &v)
+	FeSub(&check, &vxx, &u) // vx^2-u
+	if FeIsNonZero(&check) == 1 {
+		FeAdd(&check, &vxx, &u) // vx^2+u
+		if FeIsNonZero(&check) == 1 {
+			return false
+		}
+		FeMul(&p.X, &p.X, &SqrtM1)
+
+		FeToBytes(&tmpX, &p.X)
+		for i, v := range tmpX {
+			tmp2[31-i] = v
+		}
+	}
+
+	if FeIsNegative(&p.X) != (s[31] >> 7) {
+		FeNeg(&p.X, &p.X)
+	}
+
+	FeMul(&p.T, &p.X, &p.Y)
+	return true
+}
+
+func (p *CompletedGroupElement) ToProjective(r *ProjectiveGroupElement) {
+	FeMul(&r.X, &p.X, &p.T)
+	FeMul(&r.Y, &p.Y, &p.Z)
+	FeMul(&r.Z, &p.Z, &p.T)
+}
+
+func (p *CompletedGroupElement) ToExtended(r *ExtendedGroupElement) {
+	FeMul(&r.X, &p.X, &p.T)
+	FeMul(&r.Y, &p.Y, &p.Z)
+	FeMul(&r.Z, &p.Z, &p.T)
+	FeMul(&r.T, &p.X, &p.Y)
+}
+
+func (p *PreComputedGroupElement) Zero() {
+	FeOne(&p.yPlusX)
+	FeOne(&p.yMinusX)
+	FeZero(&p.xy2d)
+}
+
+func geAdd(r *CompletedGroupElement, p *ExtendedGroupElement, q *CachedGroupElement) {
+	var t0 FieldElement
+
+	FeAdd(&r.X, &p.Y, &p.X)
+	FeSub(&r.Y, &p.Y, &p.X)
+	FeMul(&r.Z, &r.X, &q.yPlusX)
+	FeMul(&r.Y, &r.Y, &q.yMinusX)
+	FeMul(&r.T, &q.T2d, &p.T)
+	FeMul(&r.X, &p.Z, &q.Z)
+	FeAdd(&t0, &r.X, &r.X)
+	FeSub(&r.X, &r.Z, &r.Y)
+	FeAdd(&r.Y, &r.Z, &r.Y)
+	FeAdd(&r.Z, &t0, &r.T)
+	FeSub(&r.T, &t0, &r.T)
+}
+
+func geSub(r *CompletedGroupElement, p *ExtendedGroupElement, q *CachedGroupElement) {
+	var t0 FieldElement
+
+	FeAdd(&r.X, &p.Y, &p.X)
+	FeSub(&r.Y, &p.Y, &p.X)
+	FeMul(&r.Z, &r.X, &q.yMinusX)
+	FeMul(&r.Y, &r.Y, &q.yPlusX)
+	FeMul(&r.T, &q.T2d, &p.T)
+	FeMul(&r.X, &p.Z, &q.Z)
+	FeAdd(&t0, &r.X, &r.X)
+	FeSub(&r.X, &r.Z, &r.Y)
+	FeAdd(&r.Y, &r.Z, &r.Y)
+	FeSub(&r.Z, &t0, &r.T)
+	FeAdd(&r.T, &t0, &r.T)
+}
+
+func geMixedAdd(r *CompletedGroupElement, p *ExtendedGroupElement, q *PreComputedGroupElement) {
+	var t0 FieldElement
+
+	FeAdd(&r.X, &p.Y, &p.X)
+	FeSub(&r.Y, &p.Y, &p.X)
+	FeMul(&r.Z, &r.X, &q.yPlusX)
+	FeMul(&r.Y, &r.Y, &q.yMinusX)
+	FeMul(&r.T, &q.xy2d, &p.T)
+	FeAdd(&t0, &p.Z, &p.Z)
+	FeSub(&r.X, &r.Z, &r.Y)
+	FeAdd(&r.Y, &r.Z, &r.Y)
+	FeAdd(&r.Z, &t0, &r.T)
+	FeSub(&r.T, &t0, &r.T)
+}
+
+func geMixedSub(r *CompletedGroupElement, p *ExtendedGroupElement, q *PreComputedGroupElement) {
+	var t0 FieldElement
+
+	FeAdd(&r.X, &p.Y, &p.X)
+	FeSub(&r.Y, &p.Y, &p.X)
+	FeMul(&r.Z, &r.X, &q.yMinusX)
+	FeMul(&r.Y, &r.Y, &q.yPlusX)
+	FeMul(&r.T, &q.xy2d, &p.T)
+	FeAdd(&t0, &p.Z, &p.Z)
+	FeSub(&r.X, &r.Z, &r.Y)
+	FeAdd(&r.Y, &r.Z, &r.Y)
+	FeSub(&r.Z, &t0, &r.T)
+	FeAdd(&r.T, &t0, &r.T)
+}
+
+func slide(r *[256]int8, a *[32]byte) {
+	for i := range r {
+		r[i] = int8(1 & (a[i>>3] >> uint(i&7)))
+	}
+
+	for i := range r {
+		if r[i] != 0 {
+			for b := 1; b <= 6 && i+b < 256; b++ {
+				if r[i+b] != 0 {
+					if r[i]+(r[i+b]<<uint(b)) <= 15 {
+						r[i] += r[i+b] << uint(b)
+						r[i+b] = 0
+					} else if r[i]-(r[i+b]<<uint(b)) >= -15 {
+						r[i] -= r[i+b] << uint(b)
+						for k := i + b; k < 256; k++ {
+							if r[k] == 0 {
+								r[k] = 1
+								break
+							}
+							r[k] = 0
+						}
+					} else {
+						break
+					}
+				}
+			}
+		}
+	}
+}
+
+// GeDoubleScalarMultVartime sets r = a*A + b*B
+// where a = a[0]+256*a[1]+...+256^31 a[31].
+// and b = b[0]+256*b[1]+...+256^31 b[31].
+// B is the Ed25519 base point (x,4/5) with x positive.
+func GeDoubleScalarMultVartime(r *ProjectiveGroupElement, a *[32]byte, A *ExtendedGroupElement, b *[32]byte) {
+	var aSlide, bSlide [256]int8
+	var Ai [8]CachedGroupElement // A,3A,5A,7A,9A,11A,13A,15A
+	var t CompletedGroupElement
+	var u, A2 ExtendedGroupElement
+	var i int
+
+	slide(&aSlide, a)
+	slide(&bSlide, b)
+
+	A.ToCached(&Ai[0])
+	A.Double(&t)
+	t.ToExtended(&A2)
+
+	for i := 0; i < 7; i++ {
+		geAdd(&t, &A2, &Ai[i])
+		t.ToExtended(&u)
+		u.ToCached(&Ai[i+1])
+	}
+
+	r.Zero()
+
+	for i = 255; i >= 0; i-- {
+		if aSlide[i] != 0 || bSlide[i] != 0 {
+			break
+		}
+	}
+
+	for ; i >= 0; i-- {
+		r.Double(&t)
+
+		if aSlide[i] > 0 {
+			t.ToExtended(&u)
+			geAdd(&t, &u, &Ai[aSlide[i]/2])
+		} else if aSlide[i] < 0 {
+			t.ToExtended(&u)
+			geSub(&t, &u, &Ai[(-aSlide[i])/2])
+		}
+
+		if bSlide[i] > 0 {
+			t.ToExtended(&u)
+			geMixedAdd(&t, &u, &bi[bSlide[i]/2])
+		} else if bSlide[i] < 0 {
+			t.ToExtended(&u)
+			geMixedSub(&t, &u, &bi[(-bSlide[i])/2])
+		}
+
+		t.ToProjective(r)
+	}
+}
+
+// equal returns 1 if b == c and 0 otherwise, assuming that b and c are
+// non-negative.
+func equal(b, c int32) int32 {
+	x := uint32(b ^ c)
+	x--
+	return int32(x >> 31)
+}
+
+// negative returns 1 if b < 0 and 0 otherwise.
+func negative(b int32) int32 {
+	return (b >> 31) & 1
+}
+
+func PreComputedGroupElementCMove(t, u *PreComputedGroupElement, b int32) {
+	FeCMove(&t.yPlusX, &u.yPlusX, b)
+	FeCMove(&t.yMinusX, &u.yMinusX, b)
+	FeCMove(&t.xy2d, &u.xy2d, b)
+}
+
+func selectPoint(t *PreComputedGroupElement, pos int32, b int32) {
+	var minusT PreComputedGroupElement
+	bNegative := negative(b)
+	bAbs := b - (((-bNegative) & b) << 1)
+
+	t.Zero()
+	for i := int32(0); i < 8; i++ {
+		PreComputedGroupElementCMove(t, &base[pos][i], equal(bAbs, i+1))
+	}
+	FeCopy(&minusT.yPlusX, &t.yMinusX)
+	FeCopy(&minusT.yMinusX, &t.yPlusX)
+	FeNeg(&minusT.xy2d, &t.xy2d)
+	PreComputedGroupElementCMove(t, &minusT, bNegative)
+}
+
+// GeScalarMultBase computes h = a*B, where
+//   a = a[0]+256*a[1]+...+256^31 a[31]
+//   B is the Ed25519 base point (x,4/5) with x positive.
+//
+// Preconditions:
+//   a[31] <= 127
+func GeScalarMultBase(h *ExtendedGroupElement, a *[32]byte) {
+	var e [64]int8
+
+	for i, v := range a {
+		e[2*i] = int8(v & 15)
+		e[2*i+1] = int8((v >> 4) & 15)
+	}
+
+	// each e[i] is between 0 and 15 and e[63] is between 0 and 7.
+
+	carry := int8(0)
+	for i := 0; i < 63; i++ {
+		e[i] += carry
+		carry = (e[i] + 8) >> 4
+		e[i] -= carry << 4
+	}
+	e[63] += carry
+	// each e[i] is between -8 and 8.
+
+	h.Zero()
+	var t PreComputedGroupElement
+	var r CompletedGroupElement
+	for i := int32(1); i < 64; i += 2 {
+		selectPoint(&t, i/2, int32(e[i]))
+		geMixedAdd(&r, h, &t)
+		r.ToExtended(h)
+	}
+
+	var s ProjectiveGroupElement
+
+	h.Double(&r)
+	r.ToProjective(&s)
+	s.Double(&r)
+	r.ToProjective(&s)
+	s.Double(&r)
+	r.ToProjective(&s)
+	s.Double(&r)
+	r.ToExtended(h)
+
+	for i := int32(0); i < 64; i += 2 {
+		selectPoint(&t, i/2, int32(e[i]))
+		geMixedAdd(&r, h, &t)
+		r.ToExtended(h)
+	}
+}
+
+// The scalars are GF(2^252 + 27742317777372353535851937790883648493).
+
+// Input:
+//   a[0]+256*a[1]+...+256^31*a[31] = a
+//   b[0]+256*b[1]+...+256^31*b[31] = b
+//   c[0]+256*c[1]+...+256^31*c[31] = c
+//
+// Output:
+//   s[0]+256*s[1]+...+256^31*s[31] = (ab+c) mod l
+//   where l = 2^252 + 27742317777372353535851937790883648493.
+func ScMulAdd(s, a, b, c *[32]byte) {
+	a0 := 2097151 & load3(a[:])
+	a1 := 2097151 & (load4(a[2:]) >> 5)
+	a2 := 2097151 & (load3(a[5:]) >> 2)
+	a3 := 2097151 & (load4(a[7:]) >> 7)
+	a4 := 2097151 & (load4(a[10:]) >> 4)
+	a5 := 2097151 & (load3(a[13:]) >> 1)
+	a6 := 2097151 & (load4(a[15:]) >> 6)
+	a7 := 2097151 & (load3(a[18:]) >> 3)
+	a8 := 2097151 & load3(a[21:])
+	a9 := 2097151 & (load4(a[23:]) >> 5)
+	a10 := 2097151 & (load3(a[26:]) >> 2)
+	a11 := (load4(a[28:]) >> 7)
+	b0 := 2097151 & load3(b[:])
+	b1 := 2097151 & (load4(b[2:]) >> 5)
+	b2 := 2097151 & (load3(b[5:]) >> 2)
+	b3 := 2097151 & (load4(b[7:]) >> 7)
+	b4 := 2097151 & (load4(b[10:]) >> 4)
+	b5 := 2097151 & (load3(b[13:]) >> 1)
+	b6 := 2097151 & (load4(b[15:]) >> 6)
+	b7 := 2097151 & (load3(b[18:]) >> 3)
+	b8 := 2097151 & load3(b[21:])
+	b9 := 2097151 & (load4(b[23:]) >> 5)
+	b10 := 2097151 & (load3(b[26:]) >> 2)
+	b11 := (load4(b[28:]) >> 7)
+	c0 := 2097151 & load3(c[:])
+	c1 := 2097151 & (load4(c[2:]) >> 5)
+	c2 := 2097151 & (load3(c[5:]) >> 2)
+	c3 := 2097151 & (load4(c[7:]) >> 7)
+	c4 := 2097151 & (load4(c[10:]) >> 4)
+	c5 := 2097151 & (load3(c[13:]) >> 1)
+	c6 := 2097151 & (load4(c[15:]) >> 6)
+	c7 := 2097151 & (load3(c[18:]) >> 3)
+	c8 := 2097151 & load3(c[21:])
+	c9 := 2097151 & (load4(c[23:]) >> 5)
+	c10 := 2097151 & (load3(c[26:]) >> 2)
+	c11 := (load4(c[28:]) >> 7)
+	var carry [23]int64
+
+	s0 := c0 + a0*b0
+	s1 := c1 + a0*b1 + a1*b0
+	s2 := c2 + a0*b2 + a1*b1 + a2*b0
+	s3 := c3 + a0*b3 + a1*b2 + a2*b1 + a3*b0
+	s4 := c4 + a0*b4 + a1*b3 + a2*b2 + a3*b1 + a4*b0
+	s5 := c5 + a0*b5 + a1*b4 + a2*b3 + a3*b2 + a4*b1 + a5*b0
+	s6 := c6 + a0*b6 + a1*b5 + a2*b4 + a3*b3 + a4*b2 + a5*b1 + a6*b0
+	s7 := c7 + a0*b7 + a1*b6 + a2*b5 + a3*b4 + a4*b3 + a5*b2 + a6*b1 + a7*b0
+	s8 := c8 + a0*b8 + a1*b7 + a2*b6 + a3*b5 + a4*b4 + a5*b3 + a6*b2 + a7*b1 + a8*b0
+	s9 := c9 + a0*b9 + a1*b8 + a2*b7 + a3*b6 + a4*b5 + a5*b4 + a6*b3 + a7*b2 + a8*b1 + a9*b0
+	s10 := c10 + a0*b10 + a1*b9 + a2*b8 + a3*b7 + a4*b6 + a5*b5 + a6*b4 + a7*b3 + a8*b2 + a9*b1 + a10*b0
+	s11 := c11 + a0*b11 + a1*b10 + a2*b9 + a3*b8 + a4*b7 + a5*b6 + a6*b5 + a7*b4 + a8*b3 + a9*b2 + a10*b1 + a11*b0
+	s12 := a1*b11 + a2*b10 + a3*b9 + a4*b8 + a5*b7 + a6*b6 + a7*b5 + a8*b4 + a9*b3 + a10*b2 + a11*b1
+	s13 := a2*b11 + a3*b10 + a4*b9 + a5*b8 + a6*b7 + a7*b6 + a8*b5 + a9*b4 + a10*b3 + a11*b2
+	s14 := a3*b11 + a4*b10 + a5*b9 + a6*b8 + a7*b7 + a8*b6 + a9*b5 + a10*b4 + a11*b3
+	s15 := a4*b11 + a5*b10 + a6*b9 + a7*b8 + a8*b7 + a9*b6 + a10*b5 + a11*b4
+	s16 := a5*b11 + a6*b10 + a7*b9 + a8*b8 + a9*b7 + a10*b6 + a11*b5
+	s17 := a6*b11 + a7*b10 + a8*b9 + a9*b8 + a10*b7 + a11*b6
+	s18 := a7*b11 + a8*b10 + a9*b9 + a10*b8 + a11*b7
+	s19 := a8*b11 + a9*b10 + a10*b9 + a11*b8
+	s20 := a9*b11 + a10*b10 + a11*b9
+	s21 := a10*b11 + a11*b10
+	s22 := a11 * b11
+	s23 := int64(0)
+
+	carry[0] = (s0 + (1 << 20)) >> 21
+	s1 += carry[0]
+	s0 -= carry[0] << 21
+	carry[2] = (s2 + (1 << 20)) >> 21
+	s3 += carry[2]
+	s2 -= carry[2] << 21
+	carry[4] = (s4 + (1 << 20)) >> 21
+	s5 += carry[4]
+	s4 -= carry[4] << 21
+	carry[6] = (s6 + (1 << 20)) >> 21
+	s7 += carry[6]
+	s6 -= carry[6] << 21
+	carry[8] = (s8 + (1 << 20)) >> 21
+	s9 += carry[8]
+	s8 -= carry[8] << 21
+	carry[10] = (s10 + (1 << 20)) >> 21
+	s11 += carry[10]
+	s10 -= carry[10] << 21
+	carry[12] = (s12 + (1 << 20)) >> 21
+	s13 += carry[12]
+	s12 -= carry[12] << 21
+	carry[14] = (s14 + (1 << 20)) >> 21
+	s15 += carry[14]
+	s14 -= carry[14] << 21
+	carry[16] = (s16 + (1 << 20)) >> 21
+	s17 += carry[16]
+	s16 -= carry[16] << 21
+	carry[18] = (s18 + (1 << 20)) >> 21
+	s19 += carry[18]
+	s18 -= carry[18] << 21
+	carry[20] = (s20 + (1 << 20)) >> 21
+	s21 += carry[20]
+	s20 -= carry[20] << 21
+	carry[22] = (s22 + (1 << 20)) >> 21
+	s23 += carry[22]
+	s22 -= carry[22] << 21
+
+	carry[1] = (s1 + (1 << 20)) >> 21
+	s2 += carry[1]
+	s1 -= carry[1] << 21
+	carry[3] = (s3 + (1 << 20)) >> 21
+	s4 += carry[3]
+	s3 -= carry[3] << 21
+	carry[5] = (s5 + (1 << 20)) >> 21
+	s6 += carry[5]
+	s5 -= carry[5] << 21
+	carry[7] = (s7 + (1 << 20)) >> 21
+	s8 += carry[7]
+	s7 -= carry[7] << 21
+	carry[9] = (s9 + (1 << 20)) >> 21
+	s10 += carry[9]
+	s9 -= carry[9] << 21
+	carry[11] = (s11 + (1 << 20)) >> 21
+	s12 += carry[11]
+	s11 -= carry[11] << 21
+	carry[13] = (s13 + (1 << 20)) >> 21
+	s14 += carry[13]
+	s13 -= carry[13] << 21
+	carry[15] = (s15 + (1 << 20)) >> 21
+	s16 += carry[15]
+	s15 -= carry[15] << 21
+	carry[17] = (s17 + (1 << 20)) >> 21
+	s18 += carry[17]
+	s17 -= carry[17] << 21
+	carry[19] = (s19 + (1 << 20)) >> 21
+	s20 += carry[19]
+	s19 -= carry[19] << 21
+	carry[21] = (s21 + (1 << 20)) >> 21
+	s22 += carry[21]
+	s21 -= carry[21] << 21
+
+	s11 += s23 * 666643
+	s12 += s23 * 470296
+	s13 += s23 * 654183
+	s14 -= s23 * 997805
+	s15 += s23 * 136657
+	s16 -= s23 * 683901
+	s23 = 0
+
+	s10 += s22 * 666643
+	s11 += s22 * 470296
+	s12 += s22 * 654183
+	s13 -= s22 * 997805
+	s14 += s22 * 136657
+	s15 -= s22 * 683901
+	s22 = 0
+
+	s9 += s21 * 666643
+	s10 += s21 * 470296
+	s11 += s21 * 654183
+	s12 -= s21 * 997805
+	s13 += s21 * 136657
+	s14 -= s21 * 683901
+	s21 = 0
+
+	s8 += s20 * 666643
+	s9 += s20 * 470296
+	s10 += s20 * 654183
+	s11 -= s20 * 997805
+	s12 += s20 * 136657
+	s13 -= s20 * 683901
+	s20 = 0
+
+	s7 += s19 * 666643
+	s8 += s19 * 470296
+	s9 += s19 * 654183
+	s10 -= s19 * 997805
+	s11 += s19 * 136657
+	s12 -= s19 * 683901
+	s19 = 0
+
+	s6 += s18 * 666643
+	s7 += s18 * 470296
+	s8 += s18 * 654183
+	s9 -= s18 * 997805
+	s10 += s18 * 136657
+	s11 -= s18 * 683901
+	s18 = 0
+
+	carry[6] = (s6 + (1 << 20)) >> 21
+	s7 += carry[6]
+	s6 -= carry[6] << 21
+	carry[8] = (s8 + (1 << 20)) >> 21
+	s9 += carry[8]
+	s8 -= carry[8] << 21
+	carry[10] = (s10 + (1 << 20)) >> 21
+	s11 += carry[10]
+	s10 -= carry[10] << 21
+	carry[12] = (s12 + (1 << 20)) >> 21
+	s13 += carry[12]
+	s12 -= carry[12] << 21
+	carry[14] = (s14 + (1 << 20)) >> 21
+	s15 += carry[14]
+	s14 -= carry[14] << 21
+	carry[16] = (s16 + (1 << 20)) >> 21
+	s17 += carry[16]
+	s16 -= carry[16] << 21
+
+	carry[7] = (s7 + (1 << 20)) >> 21
+	s8 += carry[7]
+	s7 -= carry[7] << 21
+	carry[9] = (s9 + (1 << 20)) >> 21
+	s10 += carry[9]
+	s9 -= carry[9] << 21
+	carry[11] = (s11 + (1 << 20)) >> 21
+	s12 += carry[11]
+	s11 -= carry[11] << 21
+	carry[13] = (s13 + (1 << 20)) >> 21
+	s14 += carry[13]
+	s13 -= carry[13] << 21
+	carry[15] = (s15 + (1 << 20)) >> 21
+	s16 += carry[15]
+	s15 -= carry[15] << 21
+
+	s5 += s17 * 666643
+	s6 += s17 * 470296
+	s7 += s17 * 654183
+	s8 -= s17 * 997805
+	s9 += s17 * 136657
+	s10 -= s17 * 683901
+	s17 = 0
+
+	s4 += s16 * 666643
+	s5 += s16 * 470296
+	s6 += s16 * 654183
+	s7 -= s16 * 997805
+	s8 += s16 * 136657
+	s9 -= s16 * 683901
+	s16 = 0
+
+	s3 += s15 * 666643
+	s4 += s15 * 470296
+	s5 += s15 * 654183
+	s6 -= s15 * 997805
+	s7 += s15 * 136657
+	s8 -= s15 * 683901
+	s15 = 0
+
+	s2 += s14 * 666643
+	s3 += s14 * 470296
+	s4 += s14 * 654183
+	s5 -= s14 * 997805
+	s6 += s14 * 136657
+	s7 -= s14 * 683901
+	s14 = 0
+
+	s1 += s13 * 666643
+	s2 += s13 * 470296
+	s3 += s13 * 654183
+	s4 -= s13 * 997805
+	s5 += s13 * 136657
+	s6 -= s13 * 683901
+	s13 = 0
+
+	s0 += s12 * 666643
+	s1 += s12 * 470296
+	s2 += s12 * 654183
+	s3 -= s12 * 997805
+	s4 += s12 * 136657
+	s5 -= s12 * 683901
+	s12 = 0
+
+	carry[0] = (s0 + (1 << 20)) >> 21
+	s1 += carry[0]
+	s0 -= carry[0] << 21
+	carry[2] = (s2 + (1 << 20)) >> 21
+	s3 += carry[2]
+	s2 -= carry[2] << 21
+	carry[4] = (s4 + (1 << 20)) >> 21
+	s5 += carry[4]
+	s4 -= carry[4] << 21
+	carry[6] = (s6 + (1 << 20)) >> 21
+	s7 += carry[6]
+	s6 -= carry[6] << 21
+	carry[8] = (s8 + (1 << 20)) >> 21
+	s9 += carry[8]
+	s8 -= carry[8] << 21
+	carry[10] = (s10 + (1 << 20)) >> 21
+	s11 += carry[10]
+	s10 -= carry[10] << 21
+
+	carry[1] = (s1 + (1 << 20)) >> 21
+	s2 += carry[1]
+	s1 -= carry[1] << 21
+	carry[3] = (s3 + (1 << 20)) >> 21
+	s4 += carry[3]
+	s3 -= carry[3] << 21
+	carry[5] = (s5 + (1 << 20)) >> 21
+	s6 += carry[5]
+	s5 -= carry[5] << 21
+	carry[7] = (s7 + (1 << 20)) >> 21
+	s8 += carry[7]
+	s7 -= carry[7] << 21
+	carry[9] = (s9 + (1 << 20)) >> 21
+	s10 += carry[9]
+	s9 -= carry[9] << 21
+	carry[11] = (s11 + (1 << 20)) >> 21
+	s12 += carry[11]
+	s11 -= carry[11] << 21
+
+	s0 += s12 * 666643
+	s1 += s12 * 470296
+	s2 += s12 * 654183
+	s3 -= s12 * 997805
+	s4 += s12 * 136657
+	s5 -= s12 * 683901
+	s12 = 0
+
+	carry[0] = s0 >> 21
+	s1 += carry[0]
+	s0 -= carry[0] << 21
+	carry[1] = s1 >> 21
+	s2 += carry[1]
+	s1 -= carry[1] << 21
+	carry[2] = s2 >> 21
+	s3 += carry[2]
+	s2 -= carry[2] << 21
+	carry[3] = s3 >> 21
+	s4 += carry[3]
+	s3 -= carry[3] << 21
+	carry[4] = s4 >> 21
+	s5 += carry[4]
+	s4 -= carry[4] << 21
+	carry[5] = s5 >> 21
+	s6 += carry[5]
+	s5 -= carry[5] << 21
+	carry[6] = s6 >> 21
+	s7 += carry[6]
+	s6 -= carry[6] << 21
+	carry[7] = s7 >> 21
+	s8 += carry[7]
+	s7 -= carry[7] << 21
+	carry[8] = s8 >> 21
+	s9 += carry[8]
+	s8 -= carry[8] << 21
+	carry[9] = s9 >> 21
+	s10 += carry[9]
+	s9 -= carry[9] << 21
+	carry[10] = s10 >> 21
+	s11 += carry[10]
+	s10 -= carry[10] << 21
+	carry[11] = s11 >> 21
+	s12 += carry[11]
+	s11 -= carry[11] << 21
+
+	s0 += s12 * 666643
+	s1 += s12 * 470296
+	s2 += s12 * 654183
+	s3 -= s12 * 997805
+	s4 += s12 * 136657
+	s5 -= s12 * 683901
+	s12 = 0
+
+	carry[0] = s0 >> 21
+	s1 += carry[0]
+	s0 -= carry[0] << 21
+	carry[1] = s1 >> 21
+	s2 += carry[1]
+	s1 -= carry[1] << 21
+	carry[2] = s2 >> 21
+	s3 += carry[2]
+	s2 -= carry[2] << 21
+	carry[3] = s3 >> 21
+	s4 += carry[3]
+	s3 -= carry[3] << 21
+	carry[4] = s4 >> 21
+	s5 += carry[4]
+	s4 -= carry[4] << 21
+	carry[5] = s5 >> 21
+	s6 += carry[5]
+	s5 -= carry[5] << 21
+	carry[6] = s6 >> 21
+	s7 += carry[6]
+	s6 -= carry[6] << 21
+	carry[7] = s7 >> 21
+	s8 += carry[7]
+	s7 -= carry[7] << 21
+	carry[8] = s8 >> 21
+	s9 += carry[8]
+	s8 -= carry[8] << 21
+	carry[9] = s9 >> 21
+	s10 += carry[9]
+	s9 -= carry[9] << 21
+	carry[10] = s10 >> 21
+	s11 += carry[10]
+	s10 -= carry[10] << 21
+
+	s[0] = byte(s0 >> 0)
+	s[1] = byte(s0 >> 8)
+	s[2] = byte((s0 >> 16) | (s1 << 5))
+	s[3] = byte(s1 >> 3)
+	s[4] = byte(s1 >> 11)
+	s[5] = byte((s1 >> 19) | (s2 << 2))
+	s[6] = byte(s2 >> 6)
+	s[7] = byte((s2 >> 14) | (s3 << 7))
+	s[8] = byte(s3 >> 1)
+	s[9] = byte(s3 >> 9)
+	s[10] = byte((s3 >> 17) | (s4 << 4))
+	s[11] = byte(s4 >> 4)
+	s[12] = byte(s4 >> 12)
+	s[13] = byte((s4 >> 20) | (s5 << 1))
+	s[14] = byte(s5 >> 7)
+	s[15] = byte((s5 >> 15) | (s6 << 6))
+	s[16] = byte(s6 >> 2)
+	s[17] = byte(s6 >> 10)
+	s[18] = byte((s6 >> 18) | (s7 << 3))
+	s[19] = byte(s7 >> 5)
+	s[20] = byte(s7 >> 13)
+	s[21] = byte(s8 >> 0)
+	s[22] = byte(s8 >> 8)
+	s[23] = byte((s8 >> 16) | (s9 << 5))
+	s[24] = byte(s9 >> 3)
+	s[25] = byte(s9 >> 11)
+	s[26] = byte((s9 >> 19) | (s10 << 2))
+	s[27] = byte(s10 >> 6)
+	s[28] = byte((s10 >> 14) | (s11 << 7))
+	s[29] = byte(s11 >> 1)
+	s[30] = byte(s11 >> 9)
+	s[31] = byte(s11 >> 17)
+}
+
+// Input:
+//   s[0]+256*s[1]+...+256^63*s[63] = s
+//
+// Output:
+//   s[0]+256*s[1]+...+256^31*s[31] = s mod l
+//   where l = 2^252 + 27742317777372353535851937790883648493.
+func ScReduce(out *[32]byte, s *[64]byte) {
+	s0 := 2097151 & load3(s[:])
+	s1 := 2097151 & (load4(s[2:]) >> 5)
+	s2 := 2097151 & (load3(s[5:]) >> 2)
+	s3 := 2097151 & (load4(s[7:]) >> 7)
+	s4 := 2097151 & (load4(s[10:]) >> 4)
+	s5 := 2097151 & (load3(s[13:]) >> 1)
+	s6 := 2097151 & (load4(s[15:]) >> 6)
+	s7 := 2097151 & (load3(s[18:]) >> 3)
+	s8 := 2097151 & load3(s[21:])
+	s9 := 2097151 & (load4(s[23:]) >> 5)
+	s10 := 2097151 & (load3(s[26:]) >> 2)
+	s11 := 2097151 & (load4(s[28:]) >> 7)
+	s12 := 2097151 & (load4(s[31:]) >> 4)
+	s13 := 2097151 & (load3(s[34:]) >> 1)
+	s14 := 2097151 & (load4(s[36:]) >> 6)
+	s15 := 2097151 & (load3(s[39:]) >> 3)
+	s16 := 2097151 & load3(s[42:])
+	s17 := 2097151 & (load4(s[44:]) >> 5)
+	s18 := 2097151 & (load3(s[47:]) >> 2)
+	s19 := 2097151 & (load4(s[49:]) >> 7)
+	s20 := 2097151 & (load4(s[52:]) >> 4)
+	s21 := 2097151 & (load3(s[55:]) >> 1)
+	s22 := 2097151 & (load4(s[57:]) >> 6)
+	s23 := (load4(s[60:]) >> 3)
+
+	s11 += s23 * 666643
+	s12 += s23 * 470296
+	s13 += s23 * 654183
+	s14 -= s23 * 997805
+	s15 += s23 * 136657
+	s16 -= s23 * 683901
+	s23 = 0
+
+	s10 += s22 * 666643
+	s11 += s22 * 470296
+	s12 += s22 * 654183
+	s13 -= s22 * 997805
+	s14 += s22 * 136657
+	s15 -= s22 * 683901
+	s22 = 0
+
+	s9 += s21 * 666643
+	s10 += s21 * 470296
+	s11 += s21 * 654183
+	s12 -= s21 * 997805
+	s13 += s21 * 136657
+	s14 -= s21 * 683901
+	s21 = 0
+
+	s8 += s20 * 666643
+	s9 += s20 * 470296
+	s10 += s20 * 654183
+	s11 -= s20 * 997805
+	s12 += s20 * 136657
+	s13 -= s20 * 683901
+	s20 = 0
+
+	s7 += s19 * 666643
+	s8 += s19 * 470296
+	s9 += s19 * 654183
+	s10 -= s19 * 997805
+	s11 += s19 * 136657
+	s12 -= s19 * 683901
+	s19 = 0
+
+	s6 += s18 * 666643
+	s7 += s18 * 470296
+	s8 += s18 * 654183
+	s9 -= s18 * 997805
+	s10 += s18 * 136657
+	s11 -= s18 * 683901
+	s18 = 0
+
+	var carry [17]int64
+
+	carry[6] = (s6 + (1 << 20)) >> 21
+	s7 += carry[6]
+	s6 -= carry[6] << 21
+	carry[8] = (s8 + (1 << 20)) >> 21
+	s9 += carry[8]
+	s8 -= carry[8] << 21
+	carry[10] = (s10 + (1 << 20)) >> 21
+	s11 += carry[10]
+	s10 -= carry[10] << 21
+	carry[12] = (s12 + (1 << 20)) >> 21
+	s13 += carry[12]
+	s12 -= carry[12] << 21
+	carry[14] = (s14 + (1 << 20)) >> 21
+	s15 += carry[14]
+	s14 -= carry[14] << 21
+	carry[16] = (s16 + (1 << 20)) >> 21
+	s17 += carry[16]
+	s16 -= carry[16] << 21
+
+	carry[7] = (s7 + (1 << 20)) >> 21
+	s8 += carry[7]
+	s7 -= carry[7] << 21
+	carry[9] = (s9 + (1 << 20)) >> 21
+	s10 += carry[9]
+	s9 -= carry[9] << 21
+	carry[11] = (s11 + (1 << 20)) >> 21
+	s12 += carry[11]
+	s11 -= carry[11] << 21
+	carry[13] = (s13 + (1 << 20)) >> 21
+	s14 += carry[13]
+	s13 -= carry[13] << 21
+	carry[15] = (s15 + (1 << 20)) >> 21
+	s16 += carry[15]
+	s15 -= carry[15] << 21
+
+	s5 += s17 * 666643
+	s6 += s17 * 470296
+	s7 += s17 * 654183
+	s8 -= s17 * 997805
+	s9 += s17 * 136657
+	s10 -= s17 * 683901
+	s17 = 0
+
+	s4 += s16 * 666643
+	s5 += s16 * 470296
+	s6 += s16 * 654183
+	s7 -= s16 * 997805
+	s8 += s16 * 136657
+	s9 -= s16 * 683901
+	s16 = 0
+
+	s3 += s15 * 666643
+	s4 += s15 * 470296
+	s5 += s15 * 654183
+	s6 -= s15 * 997805
+	s7 += s15 * 136657
+	s8 -= s15 * 683901
+	s15 = 0
+
+	s2 += s14 * 666643
+	s3 += s14 * 470296
+	s4 += s14 * 654183
+	s5 -= s14 * 997805
+	s6 += s14 * 136657
+	s7 -= s14 * 683901
+	s14 = 0
+
+	s1 += s13 * 666643
+	s2 += s13 * 470296
+	s3 += s13 * 654183
+	s4 -= s13 * 997805
+	s5 += s13 * 136657
+	s6 -= s13 * 683901
+	s13 = 0
+
+	s0 += s12 * 666643
+	s1 += s12 * 470296
+	s2 += s12 * 654183
+	s3 -= s12 * 997805
+	s4 += s12 * 136657
+	s5 -= s12 * 683901
+	s12 = 0
+
+	carry[0] = (s0 + (1 << 20)) >> 21
+	s1 += carry[0]
+	s0 -= carry[0] << 21
+	carry[2] = (s2 + (1 << 20)) >> 21
+	s3 += carry[2]
+	s2 -= carry[2] << 21
+	carry[4] = (s4 + (1 << 20)) >> 21
+	s5 += carry[4]
+	s4 -= carry[4] << 21
+	carry[6] = (s6 + (1 << 20)) >> 21
+	s7 += carry[6]
+	s6 -= carry[6] << 21
+	carry[8] = (s8 + (1 << 20)) >> 21
+	s9 += carry[8]
+	s8 -= carry[8] << 21
+	carry[10] = (s10 + (1 << 20)) >> 21
+	s11 += carry[10]
+	s10 -= carry[10] << 21
+
+	carry[1] = (s1 + (1 << 20)) >> 21
+	s2 += carry[1]
+	s1 -= carry[1] << 21
+	carry[3] = (s3 + (1 << 20)) >> 21
+	s4 += carry[3]
+	s3 -= carry[3] << 21
+	carry[5] = (s5 + (1 << 20)) >> 21
+	s6 += carry[5]
+	s5 -= carry[5] << 21
+	carry[7] = (s7 + (1 << 20)) >> 21
+	s8 += carry[7]
+	s7 -= carry[7] << 21
+	carry[9] = (s9 + (1 << 20)) >> 21
+	s10 += carry[9]
+	s9 -= carry[9] << 21
+	carry[11] = (s11 + (1 << 20)) >> 21
+	s12 += carry[11]
+	s11 -= carry[11] << 21
+
+	s0 += s12 * 666643
+	s1 += s12 * 470296
+	s2 += s12 * 654183
+	s3 -= s12 * 997805
+	s4 += s12 * 136657
+	s5 -= s12 * 683901
+	s12 = 0
+
+	carry[0] = s0 >> 21
+	s1 += carry[0]
+	s0 -= carry[0] << 21
+	carry[1] = s1 >> 21
+	s2 += carry[1]
+	s1 -= carry[1] << 21
+	carry[2] = s2 >> 21
+	s3 += carry[2]
+	s2 -= carry[2] << 21
+	carry[3] = s3 >> 21
+	s4 += carry[3]
+	s3 -= carry[3] << 21
+	carry[4] = s4 >> 21
+	s5 += carry[4]
+	s4 -= carry[4] << 21
+	carry[5] = s5 >> 21
+	s6 += carry[5]
+	s5 -= carry[5] << 21
+	carry[6] = s6 >> 21
+	s7 += carry[6]
+	s6 -= carry[6] << 21
+	carry[7] = s7 >> 21
+	s8 += carry[7]
+	s7 -= carry[7] << 21
+	carry[8] = s8 >> 21
+	s9 += carry[8]
+	s8 -= carry[8] << 21
+	carry[9] = s9 >> 21
+	s10 += carry[9]
+	s9 -= carry[9] << 21
+	carry[10] = s10 >> 21
+	s11 += carry[10]
+	s10 -= carry[10] << 21
+	carry[11] = s11 >> 21
+	s12 += carry[11]
+	s11 -= carry[11] << 21
+
+	s0 += s12 * 666643
+	s1 += s12 * 470296
+	s2 += s12 * 654183
+	s3 -= s12 * 997805
+	s4 += s12 * 136657
+	s5 -= s12 * 683901
+	s12 = 0
+
+	carry[0] = s0 >> 21
+	s1 += carry[0]
+	s0 -= carry[0] << 21
+	carry[1] = s1 >> 21
+	s2 += carry[1]
+	s1 -= carry[1] << 21
+	carry[2] = s2 >> 21
+	s3 += carry[2]
+	s2 -= carry[2] << 21
+	carry[3] = s3 >> 21
+	s4 += carry[3]
+	s3 -= carry[3] << 21
+	carry[4] = s4 >> 21
+	s5 += carry[4]
+	s4 -= carry[4] << 21
+	carry[5] = s5 >> 21
+	s6 += carry[5]
+	s5 -= carry[5] << 21
+	carry[6] = s6 >> 21
+	s7 += carry[6]
+	s6 -= carry[6] << 21
+	carry[7] = s7 >> 21
+	s8 += carry[7]
+	s7 -= carry[7] << 21
+	carry[8] = s8 >> 21
+	s9 += carry[8]
+	s8 -= carry[8] << 21
+	carry[9] = s9 >> 21
+	s10 += carry[9]
+	s9 -= carry[9] << 21
+	carry[10] = s10 >> 21
+	s11 += carry[10]
+	s10 -= carry[10] << 21
+
+	out[0] = byte(s0 >> 0)
+	out[1] = byte(s0 >> 8)
+	out[2] = byte((s0 >> 16) | (s1 << 5))
+	out[3] = byte(s1 >> 3)
+	out[4] = byte(s1 >> 11)
+	out[5] = byte((s1 >> 19) | (s2 << 2))
+	out[6] = byte(s2 >> 6)
+	out[7] = byte((s2 >> 14) | (s3 << 7))
+	out[8] = byte(s3 >> 1)
+	out[9] = byte(s3 >> 9)
+	out[10] = byte((s3 >> 17) | (s4 << 4))
+	out[11] = byte(s4 >> 4)
+	out[12] = byte(s4 >> 12)
+	out[13] = byte((s4 >> 20) | (s5 << 1))
+	out[14] = byte(s5 >> 7)
+	out[15] = byte((s5 >> 15) | (s6 << 6))
+	out[16] = byte(s6 >> 2)
+	out[17] = byte(s6 >> 10)
+	out[18] = byte((s6 >> 18) | (s7 << 3))
+	out[19] = byte(s7 >> 5)
+	out[20] = byte(s7 >> 13)
+	out[21] = byte(s8 >> 0)
+	out[22] = byte(s8 >> 8)
+	out[23] = byte((s8 >> 16) | (s9 << 5))
+	out[24] = byte(s9 >> 3)
+	out[25] = byte(s9 >> 11)
+	out[26] = byte((s9 >> 19) | (s10 << 2))
+	out[27] = byte(s10 >> 6)
+	out[28] = byte((s10 >> 14) | (s11 << 7))
+	out[29] = byte(s11 >> 1)
+	out[30] = byte(s11 >> 9)
+	out[31] = byte(s11 >> 17)
+}
+
+// order is the order of Curve25519 in little-endian form.
+var order = [4]uint64{0x5812631a5cf5d3ed, 0x14def9dea2f79cd6, 0, 0x1000000000000000}
+
+// ScMinimal returns true if the given scalar is less than the order of the
+// curve.
+func ScMinimal(scalar *[32]byte) bool {
+	for i := 3; ; i-- {
+		v := binary.LittleEndian.Uint64(scalar[i*8:])
+		if v > order[i] {
+			return false
+		} else if v < order[i] {
+			break
+		} else if i == 0 {
+			return false
+		}
+	}
+
+	return true
+}

+ 18 - 0
vendor/vendor.json

@@ -698,6 +698,12 @@
 			"revision": "c8d3bf9c5392d5f66747f112cd55055d7a530b19",
 			"revisionTime": "2020-11-22T21:41:03Z"
 		},
+		{
+			"checksumSHA1": "q+XI9g44wd9mYvf3S5Wo8YZjAus=",
+			"path": "golang.org/x/crypto/blowfish",
+			"revision": "089bfa5675191fd96a44247682f76ebca03d7916",
+			"revisionTime": "2020-12-12T03:52:14Z"
+		},
 		{
 			"checksumSHA1": "iEhSrgDaUwe8T6wxMMwLX0zx2DU=",
 			"path": "golang.org/x/crypto/chacha20",
@@ -728,6 +734,18 @@
 			"revision": "c8d3bf9c5392d5f66747f112cd55055d7a530b19",
 			"revisionTime": "2020-11-22T21:41:03Z"
 		},
+		{
+			"checksumSHA1": "ZCYqRggHbwiEZNnAJh3KKDd2FNY=",
+			"path": "golang.org/x/crypto/ed25519",
+			"revision": "089bfa5675191fd96a44247682f76ebca03d7916",
+			"revisionTime": "2020-12-12T03:52:14Z"
+		},
+		{
+			"checksumSHA1": "0JTAFXPkankmWcZGQJGScLDiaN8=",
+			"path": "golang.org/x/crypto/ed25519/internal/edwards25519",
+			"revision": "089bfa5675191fd96a44247682f76ebca03d7916",
+			"revisionTime": "2020-12-12T03:52:14Z"
+		},
 		{
 			"checksumSHA1": "ELSEW2KG0p3oua5lIxl1xW2oFBo=",
 			"path": "golang.org/x/crypto/hkdf",