add obfs4 tunnel support
This commit is contained in:
parent
cfd1223550
commit
0c050c7d30
@ -156,6 +156,11 @@ func initChain() (*gost.Chain, error) {
|
||||
gost.ChainDialOption(chain),
|
||||
)
|
||||
chain = gost.NewChain() // cutoff the chain for multiplex
|
||||
case "obfs4":
|
||||
if err := gost.Obfs4Init(node, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tr = gost.Obfs4Transporter()
|
||||
default:
|
||||
tr = gost.TCPTransporter()
|
||||
}
|
||||
@ -265,6 +270,11 @@ func serve(chain *gost.Chain) error {
|
||||
ln, err = gost.H2Listener(node.Addr, tlsCfg)
|
||||
case "h2c":
|
||||
ln, err = gost.H2CListener(node.Addr)
|
||||
case "obfs4":
|
||||
if err = gost.Obfs4Init(node, true); err != nil {
|
||||
return err
|
||||
}
|
||||
ln, err = gost.Obfs4Listener(node.Addr)
|
||||
case "tcp":
|
||||
ln, err = gost.TCPListener(node.Addr)
|
||||
case "rtcp":
|
||||
|
@ -1,4 +1,6 @@
|
||||
# username password
|
||||
|
||||
test\admin 123456
|
||||
$test 123456
|
||||
test001 123456
|
||||
test002 12345678
|
2
node.go
2
node.go
@ -46,7 +46,7 @@ func ParseNode(s string) (node Node, err error) {
|
||||
}
|
||||
|
||||
switch node.Transport {
|
||||
case "tls", "ws", "wss", "kcp", "ssh", "quic", "ssu", "http2", "h2", "h2c", "redirect":
|
||||
case "tls", "ws", "wss", "kcp", "ssh", "quic", "ssu", "http2", "h2", "h2c", "redirect", "obfs4":
|
||||
case "https":
|
||||
node.Protocol = "http"
|
||||
node.Transport = "tls"
|
||||
|
157
obfs4.go
Normal file
157
obfs4.go
Normal file
@ -0,0 +1,157 @@
|
||||
package gost
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
|
||||
"github.com/go-log/log"
|
||||
|
||||
pt "git.torproject.org/pluggable-transports/goptlib.git"
|
||||
"git.torproject.org/pluggable-transports/obfs4.git/transports/base"
|
||||
"git.torproject.org/pluggable-transports/obfs4.git/transports/obfs4"
|
||||
)
|
||||
|
||||
type obfs4Context struct {
|
||||
cf base.ClientFactory
|
||||
cargs interface{} // type obfs4ClientArgs
|
||||
sf base.ServerFactory
|
||||
sargs *pt.Args
|
||||
}
|
||||
|
||||
var obfs4Map = make(map[string]obfs4Context)
|
||||
|
||||
func Obfs4Init(node Node, isServeNode bool) error {
|
||||
if _, ok := obfs4Map[node.Addr]; ok {
|
||||
return fmt.Errorf("obfs4 context already inited")
|
||||
}
|
||||
|
||||
t := new(obfs4.Transport)
|
||||
|
||||
stateDir := node.Values.Get("state-dir")
|
||||
if stateDir == "" {
|
||||
stateDir = "."
|
||||
}
|
||||
|
||||
ptArgs := pt.Args(node.Values)
|
||||
|
||||
if !isServeNode {
|
||||
cf, err := t.ClientFactory(stateDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cargs, err := cf.ParseArgs(&ptArgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
obfs4Map[node.Addr] = obfs4Context{cf: cf, cargs: cargs}
|
||||
} else {
|
||||
sf, err := t.ServerFactory(stateDir, &ptArgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sargs := sf.Args()
|
||||
|
||||
obfs4Map[node.Addr] = obfs4Context{sf: sf, sargs: sargs}
|
||||
|
||||
log.Log("[obfs4] server inited:", obfs4ServerURL(node))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func obfs4GetContext(addr string) (obfs4Context, error) {
|
||||
ctx, ok := obfs4Map[addr]
|
||||
if !ok {
|
||||
return obfs4Context{}, fmt.Errorf("obfs4 context not inited")
|
||||
}
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
func obfs4ServerURL(node Node) string {
|
||||
ctx, err := obfs4GetContext(node.Addr)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
values := (*url.Values)(ctx.sargs)
|
||||
query := values.Encode()
|
||||
|
||||
return fmt.Sprintf(
|
||||
"%s+%s://%s/?%s", //obfs4-cert=%s&iat-mode=%s",
|
||||
node.Protocol,
|
||||
node.Transport,
|
||||
node.Addr,
|
||||
query,
|
||||
)
|
||||
}
|
||||
|
||||
func obfs4ClientConn(addr string, conn net.Conn) (net.Conn, error) {
|
||||
ctx, err := obfs4GetContext(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pseudoDial := func(a, b string) (net.Conn, error) { return conn, nil }
|
||||
return ctx.cf.Dial("tcp", "", pseudoDial, ctx.cargs)
|
||||
}
|
||||
|
||||
func obfs4ServerConn(addr string, conn net.Conn) (net.Conn, error) {
|
||||
ctx, err := obfs4GetContext(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ctx.sf.WrapConn(conn)
|
||||
}
|
||||
|
||||
type obfs4Transporter struct {
|
||||
tcpTransporter
|
||||
}
|
||||
|
||||
// Obfs4Transporter creates a Transporter that is used by obfs4 client.
|
||||
func Obfs4Transporter() Transporter {
|
||||
return &obfs4Transporter{}
|
||||
}
|
||||
|
||||
func (tr *obfs4Transporter) Handshake(conn net.Conn, options ...HandshakeOption) (net.Conn, error) {
|
||||
opts := &HandshakeOptions{}
|
||||
for _, option := range options {
|
||||
option(opts)
|
||||
}
|
||||
return obfs4ClientConn(opts.Addr, conn)
|
||||
}
|
||||
|
||||
type obfs4Listener struct {
|
||||
addr string
|
||||
net.Listener
|
||||
}
|
||||
|
||||
// Obfs4Listener creates a Listener for obfs4 server.
|
||||
func Obfs4Listener(addr string) (Listener, error) {
|
||||
ln, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l := &obfs4Listener{
|
||||
addr: addr,
|
||||
Listener: ln,
|
||||
}
|
||||
return l, nil
|
||||
}
|
||||
|
||||
func (l *obfs4Listener) Accpet() (net.Conn, error) {
|
||||
conn, err := l.Listener.Accept()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cc, err := obfs4ServerConn(l.addr, conn)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
return cc, nil
|
||||
}
|
2
tls.go
2
tls.go
@ -8,7 +8,7 @@ import (
|
||||
)
|
||||
|
||||
type tlsTransporter struct {
|
||||
*tcpTransporter
|
||||
tcpTransporter
|
||||
}
|
||||
|
||||
// TLSTransporter creates a Transporter that is used by TLS proxy client.
|
||||
|
121
vendor/git.torproject.org/pluggable-transports/goptlib.git/COPYING
generated
vendored
Normal file
121
vendor/git.torproject.org/pluggable-transports/goptlib.git/COPYING
generated
vendored
Normal file
@ -0,0 +1,121 @@
|
||||
Creative Commons Legal Code
|
||||
|
||||
CC0 1.0 Universal
|
||||
|
||||
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
|
||||
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
|
||||
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
|
||||
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
|
||||
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
|
||||
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
|
||||
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
|
||||
HEREUNDER.
|
||||
|
||||
Statement of Purpose
|
||||
|
||||
The laws of most jurisdictions throughout the world automatically confer
|
||||
exclusive Copyright and Related Rights (defined below) upon the creator
|
||||
and subsequent owner(s) (each and all, an "owner") of an original work of
|
||||
authorship and/or a database (each, a "Work").
|
||||
|
||||
Certain owners wish to permanently relinquish those rights to a Work for
|
||||
the purpose of contributing to a commons of creative, cultural and
|
||||
scientific works ("Commons") that the public can reliably and without fear
|
||||
of later claims of infringement build upon, modify, incorporate in other
|
||||
works, reuse and redistribute as freely as possible in any form whatsoever
|
||||
and for any purposes, including without limitation commercial purposes.
|
||||
These owners may contribute to the Commons to promote the ideal of a free
|
||||
culture and the further production of creative, cultural and scientific
|
||||
works, or to gain reputation or greater distribution for their Work in
|
||||
part through the use and efforts of others.
|
||||
|
||||
For these and/or other purposes and motivations, and without any
|
||||
expectation of additional consideration or compensation, the person
|
||||
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
|
||||
is an owner of Copyright and Related Rights in the Work, voluntarily
|
||||
elects to apply CC0 to the Work and publicly distribute the Work under its
|
||||
terms, with knowledge of his or her Copyright and Related Rights in the
|
||||
Work and the meaning and intended legal effect of CC0 on those rights.
|
||||
|
||||
1. Copyright and Related Rights. A Work made available under CC0 may be
|
||||
protected by copyright and related or neighboring rights ("Copyright and
|
||||
Related Rights"). Copyright and Related Rights include, but are not
|
||||
limited to, the following:
|
||||
|
||||
i. the right to reproduce, adapt, distribute, perform, display,
|
||||
communicate, and translate a Work;
|
||||
ii. moral rights retained by the original author(s) and/or performer(s);
|
||||
iii. publicity and privacy rights pertaining to a person's image or
|
||||
likeness depicted in a Work;
|
||||
iv. rights protecting against unfair competition in regards to a Work,
|
||||
subject to the limitations in paragraph 4(a), below;
|
||||
v. rights protecting the extraction, dissemination, use and reuse of data
|
||||
in a Work;
|
||||
vi. database rights (such as those arising under Directive 96/9/EC of the
|
||||
European Parliament and of the Council of 11 March 1996 on the legal
|
||||
protection of databases, and under any national implementation
|
||||
thereof, including any amended or successor version of such
|
||||
directive); and
|
||||
vii. other similar, equivalent or corresponding rights throughout the
|
||||
world based on applicable law or treaty, and any national
|
||||
implementations thereof.
|
||||
|
||||
2. Waiver. To the greatest extent permitted by, but not in contravention
|
||||
of, applicable law, Affirmer hereby overtly, fully, permanently,
|
||||
irrevocably and unconditionally waives, abandons, and surrenders all of
|
||||
Affirmer's Copyright and Related Rights and associated claims and causes
|
||||
of action, whether now known or unknown (including existing as well as
|
||||
future claims and causes of action), in the Work (i) in all territories
|
||||
worldwide, (ii) for the maximum duration provided by applicable law or
|
||||
treaty (including future time extensions), (iii) in any current or future
|
||||
medium and for any number of copies, and (iv) for any purpose whatsoever,
|
||||
including without limitation commercial, advertising or promotional
|
||||
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
|
||||
member of the public at large and to the detriment of Affirmer's heirs and
|
||||
successors, fully intending that such Waiver shall not be subject to
|
||||
revocation, rescission, cancellation, termination, or any other legal or
|
||||
equitable action to disrupt the quiet enjoyment of the Work by the public
|
||||
as contemplated by Affirmer's express Statement of Purpose.
|
||||
|
||||
3. Public License Fallback. Should any part of the Waiver for any reason
|
||||
be judged legally invalid or ineffective under applicable law, then the
|
||||
Waiver shall be preserved to the maximum extent permitted taking into
|
||||
account Affirmer's express Statement of Purpose. In addition, to the
|
||||
extent the Waiver is so judged Affirmer hereby grants to each affected
|
||||
person a royalty-free, non transferable, non sublicensable, non exclusive,
|
||||
irrevocable and unconditional license to exercise Affirmer's Copyright and
|
||||
Related Rights in the Work (i) in all territories worldwide, (ii) for the
|
||||
maximum duration provided by applicable law or treaty (including future
|
||||
time extensions), (iii) in any current or future medium and for any number
|
||||
of copies, and (iv) for any purpose whatsoever, including without
|
||||
limitation commercial, advertising or promotional purposes (the
|
||||
"License"). The License shall be deemed effective as of the date CC0 was
|
||||
applied by Affirmer to the Work. Should any part of the License for any
|
||||
reason be judged legally invalid or ineffective under applicable law, such
|
||||
partial invalidity or ineffectiveness shall not invalidate the remainder
|
||||
of the License, and in such case Affirmer hereby affirms that he or she
|
||||
will not (i) exercise any of his or her remaining Copyright and Related
|
||||
Rights in the Work or (ii) assert any associated claims and causes of
|
||||
action with respect to the Work, in either case contrary to Affirmer's
|
||||
express Statement of Purpose.
|
||||
|
||||
4. Limitations and Disclaimers.
|
||||
|
||||
a. No trademark or patent rights held by Affirmer are waived, abandoned,
|
||||
surrendered, licensed or otherwise affected by this document.
|
||||
b. Affirmer offers the Work as-is and makes no representations or
|
||||
warranties of any kind concerning the Work, express, implied,
|
||||
statutory or otherwise, including without limitation warranties of
|
||||
title, merchantability, fitness for a particular purpose, non
|
||||
infringement, or the absence of latent or other defects, accuracy, or
|
||||
the present or absence of errors, whether or not discoverable, all to
|
||||
the greatest extent permissible under applicable law.
|
||||
c. Affirmer disclaims responsibility for clearing rights of other persons
|
||||
that may apply to the Work or any use thereof, including without
|
||||
limitation any person's Copyright and Related Rights in the Work.
|
||||
Further, Affirmer disclaims responsibility for obtaining any necessary
|
||||
consents, permissions or other rights required for any use of the
|
||||
Work.
|
||||
d. Affirmer understands and acknowledges that Creative Commons is not a
|
||||
party to this document and has no duty or obligation with respect to
|
||||
this CC0 or use of the Work.
|
27
vendor/git.torproject.org/pluggable-transports/goptlib.git/README
generated
vendored
Normal file
27
vendor/git.torproject.org/pluggable-transports/goptlib.git/README
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
goptlib is a library for writing Tor pluggable transports in Go.
|
||||
|
||||
https://spec.torproject.org/pt-spec
|
||||
https://gitweb.torproject.org/torspec.git/tree/proposals/196-transport-control-ports.txt
|
||||
https://gitweb.torproject.org/torspec.git/tree/proposals/217-ext-orport-auth.txt
|
||||
https://gitweb.torproject.org/torspec.git/tree/proposals/232-pluggable-transports-through-proxy.txt
|
||||
|
||||
To download a copy of the library into $GOPATH:
|
||||
go get git.torproject.org/pluggable-transports/goptlib.git
|
||||
|
||||
See the included example programs for examples of how to use the
|
||||
library. To build them, enter their directory and run "go build".
|
||||
examples/dummy-client/dummy-client.go
|
||||
examples/dummy-server/dummy-server.go
|
||||
The recommended way to start writing a new transport plugin is to copy
|
||||
dummy-client or dummy-server and make changes to it.
|
||||
|
||||
There is browseable documentation here:
|
||||
https://godoc.org/git.torproject.org/pluggable-transports/goptlib.git
|
||||
|
||||
Report bugs to the tor-dev@lists.torproject.org mailing list or to the
|
||||
bug tracker at https://trac.torproject.org/projects/tor.
|
||||
|
||||
To the extent possible under law, the authors have dedicated all
|
||||
copyright and related and neighboring rights to this software to the
|
||||
public domain worldwide. This software is distributed without any
|
||||
warranty. See COPYING.
|
219
vendor/git.torproject.org/pluggable-transports/goptlib.git/args.go
generated
vendored
Normal file
219
vendor/git.torproject.org/pluggable-transports/goptlib.git/args.go
generated
vendored
Normal file
@ -0,0 +1,219 @@
|
||||
package pt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Key–value mappings for the representation of client and server options.
|
||||
|
||||
// Args maps a string key to a list of values. It is similar to url.Values.
|
||||
type Args map[string][]string
|
||||
|
||||
// Get the first value associated with the given key. If there are any values
|
||||
// associated with the key, the value return has the value and ok is set to
|
||||
// true. If there are no values for the given key, value is "" and ok is false.
|
||||
// If you need access to multiple values, use the map directly.
|
||||
func (args Args) Get(key string) (value string, ok bool) {
|
||||
if args == nil {
|
||||
return "", false
|
||||
}
|
||||
vals, ok := args[key]
|
||||
if !ok || len(vals) == 0 {
|
||||
return "", false
|
||||
}
|
||||
return vals[0], true
|
||||
}
|
||||
|
||||
// Append value to the list of values for key.
|
||||
func (args Args) Add(key, value string) {
|
||||
args[key] = append(args[key], value)
|
||||
}
|
||||
|
||||
// Return the index of the next unescaped byte in s that is in the term set, or
|
||||
// else the length of the string if no terminators appear. Additionally return
|
||||
// the unescaped string up to the returned index.
|
||||
func indexUnescaped(s string, term []byte) (int, string, error) {
|
||||
var i int
|
||||
unesc := make([]byte, 0)
|
||||
for i = 0; i < len(s); i++ {
|
||||
b := s[i]
|
||||
// A terminator byte?
|
||||
if bytes.IndexByte(term, b) != -1 {
|
||||
break
|
||||
}
|
||||
if b == '\\' {
|
||||
i++
|
||||
if i >= len(s) {
|
||||
return 0, "", fmt.Errorf("nothing following final escape in %q", s)
|
||||
}
|
||||
b = s[i]
|
||||
}
|
||||
unesc = append(unesc, b)
|
||||
}
|
||||
return i, string(unesc), nil
|
||||
}
|
||||
|
||||
// Parse a name–value mapping as from an encoded SOCKS username/password.
|
||||
//
|
||||
// "First the '<Key>=<Value>' formatted arguments MUST be escaped, such that all
|
||||
// backslash, equal sign, and semicolon characters are escaped with a
|
||||
// backslash."
|
||||
func parseClientParameters(s string) (args Args, err error) {
|
||||
args = make(Args)
|
||||
if len(s) == 0 {
|
||||
return
|
||||
}
|
||||
i := 0
|
||||
for {
|
||||
var key, value string
|
||||
var offset, begin int
|
||||
|
||||
begin = i
|
||||
// Read the key.
|
||||
offset, key, err = indexUnescaped(s[i:], []byte{'=', ';'})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
i += offset
|
||||
// End of string or no equals sign?
|
||||
if i >= len(s) || s[i] != '=' {
|
||||
err = fmt.Errorf("no equals sign in %q", s[begin:i])
|
||||
return
|
||||
}
|
||||
// Skip the equals sign.
|
||||
i++
|
||||
// Read the value.
|
||||
offset, value, err = indexUnescaped(s[i:], []byte{';'})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
i += offset
|
||||
if len(key) == 0 {
|
||||
err = fmt.Errorf("empty key in %q", s[begin:i])
|
||||
return
|
||||
}
|
||||
args.Add(key, value)
|
||||
if i >= len(s) {
|
||||
break
|
||||
}
|
||||
// Skip the semicolon.
|
||||
i++
|
||||
}
|
||||
return args, nil
|
||||
}
|
||||
|
||||
// Parse a transport–name–value mapping as from TOR_PT_SERVER_TRANSPORT_OPTIONS.
|
||||
//
|
||||
// "...a semicolon-separated list of <key>:<value> pairs, where <key> is a PT
|
||||
// name and <value> is a k=v string value with options that are to be passed to
|
||||
// the transport. Colons, semicolons, equal signs and backslashes must be
|
||||
// escaped with a backslash."
|
||||
// Example: scramblesuit:key=banana;automata:rule=110;automata:depth=3
|
||||
func parseServerTransportOptions(s string) (opts map[string]Args, err error) {
|
||||
opts = make(map[string]Args)
|
||||
if len(s) == 0 {
|
||||
return
|
||||
}
|
||||
i := 0
|
||||
for {
|
||||
var methodName, key, value string
|
||||
var offset, begin int
|
||||
|
||||
begin = i
|
||||
// Read the method name.
|
||||
offset, methodName, err = indexUnescaped(s[i:], []byte{':', '=', ';'})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
i += offset
|
||||
// End of string or no colon?
|
||||
if i >= len(s) || s[i] != ':' {
|
||||
err = fmt.Errorf("no colon in %q", s[begin:i])
|
||||
return
|
||||
}
|
||||
// Skip the colon.
|
||||
i++
|
||||
// Read the key.
|
||||
offset, key, err = indexUnescaped(s[i:], []byte{'=', ';'})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
i += offset
|
||||
// End of string or no equals sign?
|
||||
if i >= len(s) || s[i] != '=' {
|
||||
err = fmt.Errorf("no equals sign in %q", s[begin:i])
|
||||
return
|
||||
}
|
||||
// Skip the equals sign.
|
||||
i++
|
||||
// Read the value.
|
||||
offset, value, err = indexUnescaped(s[i:], []byte{';'})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
i += offset
|
||||
if len(methodName) == 0 {
|
||||
err = fmt.Errorf("empty method name in %q", s[begin:i])
|
||||
return
|
||||
}
|
||||
if len(key) == 0 {
|
||||
err = fmt.Errorf("empty key in %q", s[begin:i])
|
||||
return
|
||||
}
|
||||
if opts[methodName] == nil {
|
||||
opts[methodName] = make(Args)
|
||||
}
|
||||
opts[methodName].Add(key, value)
|
||||
if i >= len(s) {
|
||||
break
|
||||
}
|
||||
// Skip the semicolon.
|
||||
i++
|
||||
}
|
||||
return opts, nil
|
||||
}
|
||||
|
||||
// Escape backslashes and all the bytes that are in set.
|
||||
func backslashEscape(s string, set []byte) string {
|
||||
var buf bytes.Buffer
|
||||
for _, b := range []byte(s) {
|
||||
if b == '\\' || bytes.IndexByte(set, b) != -1 {
|
||||
buf.WriteByte('\\')
|
||||
}
|
||||
buf.WriteByte(b)
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// Encode a name–value mapping so that it is suitable to go in the ARGS option
|
||||
// of an SMETHOD line. The output is sorted by key. The "ARGS:" prefix is not
|
||||
// added.
|
||||
//
|
||||
// "Equal signs and commas [and backslashes] MUST be escaped with a backslash."
|
||||
func encodeSmethodArgs(args Args) string {
|
||||
if args == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
keys := make([]string, 0, len(args))
|
||||
for key := range args {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
escape := func(s string) string {
|
||||
return backslashEscape(s, []byte{'=', ','})
|
||||
}
|
||||
|
||||
var pairs []string
|
||||
for _, key := range keys {
|
||||
for _, value := range args[key] {
|
||||
pairs = append(pairs, escape(key)+"="+escape(value))
|
||||
}
|
||||
}
|
||||
|
||||
return strings.Join(pairs, ",")
|
||||
}
|
949
vendor/git.torproject.org/pluggable-transports/goptlib.git/pt.go
generated
vendored
Normal file
949
vendor/git.torproject.org/pluggable-transports/goptlib.git/pt.go
generated
vendored
Normal file
@ -0,0 +1,949 @@
|
||||
// Package pt implements the Tor pluggable transports specification.
|
||||
//
|
||||
// Sample client usage:
|
||||
// var ptInfo pt.ClientInfo
|
||||
// ...
|
||||
// func handler(conn *pt.SocksConn) error {
|
||||
// defer conn.Close()
|
||||
// remote, err := net.Dial("tcp", conn.Req.Target)
|
||||
// if err != nil {
|
||||
// conn.Reject()
|
||||
// return err
|
||||
// }
|
||||
// defer remote.Close()
|
||||
// err = conn.Grant(remote.RemoteAddr().(*net.TCPAddr))
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// // do something with conn and remote.
|
||||
// return nil
|
||||
// }
|
||||
// func acceptLoop(ln *pt.SocksListener) error {
|
||||
// defer ln.Close()
|
||||
// for {
|
||||
// conn, err := ln.AcceptSocks()
|
||||
// if err != nil {
|
||||
// if e, ok := err.(net.Error); ok && e.Temporary() {
|
||||
// continue
|
||||
// }
|
||||
// return err
|
||||
// }
|
||||
// go handler(conn)
|
||||
// }
|
||||
// return nil
|
||||
// }
|
||||
// ...
|
||||
// func main() {
|
||||
// var err error
|
||||
// ptInfo, err = pt.ClientSetup(nil)
|
||||
// if err != nil {
|
||||
// os.Exit(1)
|
||||
// }
|
||||
// if ptInfo.ProxyURL != nil {
|
||||
// // you need to interpret the proxy URL yourself
|
||||
// // call pt.ProxyDone instead if it's a type you understand
|
||||
// pt.ProxyError(fmt.Sprintf("proxy %s is not supported", ptInfo.ProxyURL))
|
||||
// os.Exit(1)
|
||||
// }
|
||||
// for _, methodName := range ptInfo.MethodNames {
|
||||
// switch methodName {
|
||||
// case "foo":
|
||||
// ln, err := pt.ListenSocks("tcp", "127.0.0.1:0")
|
||||
// if err != nil {
|
||||
// pt.CmethodError(methodName, err.Error())
|
||||
// break
|
||||
// }
|
||||
// go acceptLoop(ln)
|
||||
// pt.Cmethod(methodName, ln.Version(), ln.Addr())
|
||||
// default:
|
||||
// pt.CmethodError(methodName, "no such method")
|
||||
// }
|
||||
// }
|
||||
// pt.CmethodsDone()
|
||||
// }
|
||||
//
|
||||
// Sample server usage:
|
||||
// var ptInfo pt.ServerInfo
|
||||
// ...
|
||||
// func handler(conn net.Conn) error {
|
||||
// defer conn.Close()
|
||||
// or, err := pt.DialOr(&ptInfo, conn.RemoteAddr().String(), "foo")
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
// defer or.Close()
|
||||
// // do something with or and conn
|
||||
// return nil
|
||||
// }
|
||||
// func acceptLoop(ln net.Listener) error {
|
||||
// defer ln.Close()
|
||||
// for {
|
||||
// conn, err := ln.Accept()
|
||||
// if err != nil {
|
||||
// if e, ok := err.(net.Error); ok && e.Temporary() {
|
||||
// continue
|
||||
// }
|
||||
// return err
|
||||
// }
|
||||
// go handler(conn)
|
||||
// }
|
||||
// return nil
|
||||
// }
|
||||
// ...
|
||||
// func main() {
|
||||
// var err error
|
||||
// ptInfo, err = pt.ServerSetup(nil)
|
||||
// if err != nil {
|
||||
// os.Exit(1)
|
||||
// }
|
||||
// for _, bindaddr := range ptInfo.Bindaddrs {
|
||||
// switch bindaddr.MethodName {
|
||||
// case "foo":
|
||||
// ln, err := net.ListenTCP("tcp", bindaddr.Addr)
|
||||
// if err != nil {
|
||||
// pt.SmethodError(bindaddr.MethodName, err.Error())
|
||||
// break
|
||||
// }
|
||||
// go acceptLoop(ln)
|
||||
// pt.Smethod(bindaddr.MethodName, ln.Addr())
|
||||
// default:
|
||||
// pt.SmethodError(bindaddr.MethodName, "no such method")
|
||||
// }
|
||||
// }
|
||||
// pt.SmethodsDone()
|
||||
// }
|
||||
//
|
||||
// Some additional care is needed to handle signals and shutdown properly. See
|
||||
// the example programs dummy-client and dummy-server.
|
||||
//
|
||||
// Tor pluggable transports specification:
|
||||
// https://spec.torproject.org/pt-spec
|
||||
//
|
||||
// Extended ORPort:
|
||||
// https://gitweb.torproject.org/torspec.git/tree/proposals/196-transport-control-ports.txt
|
||||
//
|
||||
// Extended ORPort Authentication:
|
||||
// https://gitweb.torproject.org/torspec.git/tree/proposals/217-ext-orport-auth.txt
|
||||
//
|
||||
// Pluggable Transport through SOCKS proxy:
|
||||
// https://gitweb.torproject.org/torspec.git/tree/proposals/232-pluggable-transports-through-proxy.txt
|
||||
//
|
||||
// The package implements a SOCKS5 server sufficient for a Tor client transport
|
||||
// plugin.
|
||||
//
|
||||
// https://www.ietf.org/rfc/rfc1928.txt
|
||||
// https://www.ietf.org/rfc/rfc1929.txt
|
||||
package pt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"crypto/subtle"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// This type wraps a Write method and calls Sync after each Write.
|
||||
type syncWriter struct {
|
||||
*os.File
|
||||
}
|
||||
|
||||
// Call File.Write and then Sync. An error is returned if either operation
|
||||
// returns an error.
|
||||
func (w syncWriter) Write(p []byte) (n int, err error) {
|
||||
n, err = w.File.Write(p)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = w.Sync()
|
||||
return
|
||||
}
|
||||
|
||||
// Writer to which pluggable transports negotiation messages are written. It
|
||||
// defaults to a Writer that writes to os.Stdout and calls Sync after each
|
||||
// write.
|
||||
//
|
||||
// You may, for example, log pluggable transports messages by defining a Writer
|
||||
// that logs what is written to it:
|
||||
// type logWriteWrapper struct {
|
||||
// io.Writer
|
||||
// }
|
||||
//
|
||||
// func (w logWriteWrapper) Write(p []byte) (int, error) {
|
||||
// log.Print(string(p))
|
||||
// return w.Writer.Write(p)
|
||||
// }
|
||||
// and then redefining Stdout:
|
||||
// pt.Stdout = logWriteWrapper{pt.Stdout}
|
||||
var Stdout io.Writer = syncWriter{os.Stdout}
|
||||
|
||||
// Represents an error that can happen during negotiation, for example
|
||||
// ENV-ERROR. When an error occurs, we print it to stdout and also pass it up
|
||||
// the return chain.
|
||||
type ptErr struct {
|
||||
Keyword string
|
||||
Args []string
|
||||
}
|
||||
|
||||
// Implements the error interface.
|
||||
func (err *ptErr) Error() string {
|
||||
return formatline(err.Keyword, err.Args...)
|
||||
}
|
||||
|
||||
func getenv(key string) string {
|
||||
return os.Getenv(key)
|
||||
}
|
||||
|
||||
// Returns an ENV-ERROR if the environment variable isn't set.
|
||||
func getenvRequired(key string) (string, error) {
|
||||
value := os.Getenv(key)
|
||||
if value == "" {
|
||||
return "", envError(fmt.Sprintf("no %s environment variable", key))
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
// Returns true iff keyword contains only bytes allowed in a PT→Tor output line
|
||||
// keyword.
|
||||
// <KeywordChar> ::= <any US-ASCII alphanumeric, dash, and underscore>
|
||||
func keywordIsSafe(keyword string) bool {
|
||||
for _, b := range []byte(keyword) {
|
||||
switch {
|
||||
case '0' <= b && b <= '9':
|
||||
continue
|
||||
case 'A' <= b && b <= 'Z':
|
||||
continue
|
||||
case 'a' <= b && b <= 'z':
|
||||
continue
|
||||
case b == '-' || b == '_':
|
||||
continue
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Returns true iff arg contains only bytes allowed in a PT→Tor output line arg.
|
||||
// <ArgChar> ::= <any US-ASCII character but NUL or NL>
|
||||
func argIsSafe(arg string) bool {
|
||||
for _, b := range []byte(arg) {
|
||||
if b >= '\x80' || b == '\x00' || b == '\n' {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func formatline(keyword string, v ...string) string {
|
||||
var buf bytes.Buffer
|
||||
if !keywordIsSafe(keyword) {
|
||||
panic(fmt.Sprintf("keyword %q contains forbidden bytes", keyword))
|
||||
}
|
||||
buf.WriteString(keyword)
|
||||
for _, x := range v {
|
||||
if !argIsSafe(x) {
|
||||
panic(fmt.Sprintf("arg %q contains forbidden bytes", x))
|
||||
}
|
||||
buf.WriteString(" " + x)
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// Print a pluggable transports protocol line to Stdout. The line consists of a
|
||||
// keyword followed by any number of space-separated arg strings. Panics if
|
||||
// there are forbidden bytes in the keyword or the args (pt-spec.txt 2.2.1).
|
||||
func line(keyword string, v ...string) {
|
||||
fmt.Fprintln(Stdout, formatline(keyword, v...))
|
||||
}
|
||||
|
||||
// Emit and return the given error as a ptErr.
|
||||
func doError(keyword string, v ...string) *ptErr {
|
||||
line(keyword, v...)
|
||||
return &ptErr{keyword, v}
|
||||
}
|
||||
|
||||
// Emit an ENV-ERROR line with explanation text. Returns a representation of the
|
||||
// error.
|
||||
func envError(msg string) error {
|
||||
return doError("ENV-ERROR", msg)
|
||||
}
|
||||
|
||||
// Emit a VERSION-ERROR line with explanation text. Returns a representation of
|
||||
// the error.
|
||||
func versionError(msg string) error {
|
||||
return doError("VERSION-ERROR", msg)
|
||||
}
|
||||
|
||||
// Emit a CMETHOD-ERROR line with explanation text. Returns a representation of
|
||||
// the error.
|
||||
func CmethodError(methodName, msg string) error {
|
||||
return doError("CMETHOD-ERROR", methodName, msg)
|
||||
}
|
||||
|
||||
// Emit an SMETHOD-ERROR line with explanation text. Returns a representation of
|
||||
// the error.
|
||||
func SmethodError(methodName, msg string) error {
|
||||
return doError("SMETHOD-ERROR", methodName, msg)
|
||||
}
|
||||
|
||||
// Emit a PROXY-ERROR line with explanation text. Returns a representation of
|
||||
// the error.
|
||||
func ProxyError(msg string) error {
|
||||
return doError("PROXY-ERROR", msg)
|
||||
}
|
||||
|
||||
// Emit a CMETHOD line. socks must be "socks4" or "socks5". Call this once for
|
||||
// each listening client SOCKS port.
|
||||
func Cmethod(name string, socks string, addr net.Addr) {
|
||||
line("CMETHOD", name, socks, addr.String())
|
||||
}
|
||||
|
||||
// Emit a CMETHODS DONE line. Call this after opening all client listeners.
|
||||
func CmethodsDone() {
|
||||
line("CMETHODS", "DONE")
|
||||
}
|
||||
|
||||
// Emit an SMETHOD line. Call this once for each listening server port.
|
||||
func Smethod(name string, addr net.Addr) {
|
||||
line("SMETHOD", name, addr.String())
|
||||
}
|
||||
|
||||
// Emit an SMETHOD line with an ARGS option. args is a name–value mapping that
|
||||
// will be added to the server's extrainfo document.
|
||||
//
|
||||
// This is an example of how to check for a required option:
|
||||
// secret, ok := bindaddr.Options.Get("shared-secret")
|
||||
// if ok {
|
||||
// args := pt.Args{}
|
||||
// args.Add("shared-secret", secret)
|
||||
// pt.SmethodArgs(bindaddr.MethodName, ln.Addr(), args)
|
||||
// } else {
|
||||
// pt.SmethodError(bindaddr.MethodName, "need a shared-secret option")
|
||||
// }
|
||||
// Or, if you just want to echo back the options provided by Tor from the
|
||||
// TransportServerOptions configuration,
|
||||
// pt.SmethodArgs(bindaddr.MethodName, ln.Addr(), bindaddr.Options)
|
||||
func SmethodArgs(name string, addr net.Addr, args Args) {
|
||||
line("SMETHOD", name, addr.String(), "ARGS:"+encodeSmethodArgs(args))
|
||||
}
|
||||
|
||||
// Emit an SMETHODS DONE line. Call this after opening all server listeners.
|
||||
func SmethodsDone() {
|
||||
line("SMETHODS", "DONE")
|
||||
}
|
||||
|
||||
// Emit a PROXY DONE line. Call this after parsing ClientInfo.ProxyURL.
|
||||
func ProxyDone() {
|
||||
fmt.Fprintf(Stdout, "PROXY DONE\n")
|
||||
}
|
||||
|
||||
// Get a pluggable transports version offered by Tor and understood by us, if
|
||||
// any. The only version we understand is "1". This function reads the
|
||||
// environment variable TOR_PT_MANAGED_TRANSPORT_VER.
|
||||
func getManagedTransportVer() (string, error) {
|
||||
const transportVersion = "1"
|
||||
managedTransportVer, err := getenvRequired("TOR_PT_MANAGED_TRANSPORT_VER")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, offered := range strings.Split(managedTransportVer, ",") {
|
||||
if offered == transportVersion {
|
||||
return offered, nil
|
||||
}
|
||||
}
|
||||
return "", versionError("no-version")
|
||||
}
|
||||
|
||||
// Return the directory name in the TOR_PT_STATE_LOCATION environment variable,
|
||||
// creating it if it doesn't exist. Returns non-nil error if
|
||||
// TOR_PT_STATE_LOCATION is not set or if there is an error creating the
|
||||
// directory.
|
||||
func MakeStateDir() (string, error) {
|
||||
dir, err := getenvRequired("TOR_PT_STATE_LOCATION")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
err = os.MkdirAll(dir, 0700)
|
||||
return dir, err
|
||||
}
|
||||
|
||||
// Get the list of method names requested by Tor. This function reads the
|
||||
// environment variable TOR_PT_CLIENT_TRANSPORTS.
|
||||
func getClientTransports() ([]string, error) {
|
||||
clientTransports, err := getenvRequired("TOR_PT_CLIENT_TRANSPORTS")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return strings.Split(clientTransports, ","), nil
|
||||
}
|
||||
|
||||
// Get the upstream proxy URL. Returns nil if no proxy is requested. The
|
||||
// function ensures that the Scheme and Host fields are set; i.e., that the URL
|
||||
// is absolute. It additionally checks that the Host field contains both a host
|
||||
// and a port part. This function reads the environment variable TOR_PT_PROXY.
|
||||
//
|
||||
// This function doesn't check that the scheme is one of Tor's supported proxy
|
||||
// schemes; that is, one of "http", "socks5", or "socks4a". The caller must be
|
||||
// able to handle any returned scheme (which may be by calling ProxyError if
|
||||
// it doesn't know how to handle the scheme).
|
||||
func getProxyURL() (*url.URL, error) {
|
||||
rawurl := os.Getenv("TOR_PT_PROXY")
|
||||
if rawurl == "" {
|
||||
return nil, nil
|
||||
}
|
||||
u, err := url.Parse(rawurl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if u.Scheme == "" {
|
||||
return nil, fmt.Errorf("missing scheme")
|
||||
}
|
||||
if u.Host == "" {
|
||||
return nil, fmt.Errorf("missing authority")
|
||||
}
|
||||
host, port, err := net.SplitHostPort(u.Host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if host == "" {
|
||||
return nil, fmt.Errorf("missing host")
|
||||
}
|
||||
if port == "" {
|
||||
return nil, fmt.Errorf("missing port")
|
||||
}
|
||||
return u, nil
|
||||
}
|
||||
|
||||
// This structure is returned by ClientSetup. It consists of a list of method
|
||||
// names and the upstream proxy URL, if any.
|
||||
type ClientInfo struct {
|
||||
MethodNames []string
|
||||
ProxyURL *url.URL
|
||||
}
|
||||
|
||||
// Check the client pluggable transports environment, emitting an error message
|
||||
// and returning a non-nil error if any error is encountered. Returns a
|
||||
// ClientInfo struct.
|
||||
//
|
||||
// If your program needs to know whether to call ClientSetup or ServerSetup
|
||||
// (i.e., if the same program can be run as either a client or a server), check
|
||||
// whether the TOR_PT_CLIENT_TRANSPORTS environment variable is set:
|
||||
// if os.Getenv("TOR_PT_CLIENT_TRANSPORTS") != "" {
|
||||
// // Client mode; call pt.ClientSetup.
|
||||
// } else {
|
||||
// // Server mode; call pt.ServerSetup.
|
||||
// }
|
||||
//
|
||||
// Always pass nil for the unused single parameter. In the past, the parameter
|
||||
// was a list of transport names to use in case Tor requested "*". That feature
|
||||
// was never implemented and has been removed from the pluggable transports
|
||||
// specification.
|
||||
// https://bugs.torproject.org/15612
|
||||
func ClientSetup(_ []string) (info ClientInfo, err error) {
|
||||
ver, err := getManagedTransportVer()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
line("VERSION", ver)
|
||||
|
||||
info.MethodNames, err = getClientTransports()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
info.ProxyURL, err = getProxyURL()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return info, nil
|
||||
}
|
||||
|
||||
// A combination of a method name and an address, as extracted from
|
||||
// TOR_PT_SERVER_BINDADDR.
|
||||
type Bindaddr struct {
|
||||
MethodName string
|
||||
Addr *net.TCPAddr
|
||||
// Options from TOR_PT_SERVER_TRANSPORT_OPTIONS that pertain to this
|
||||
// transport.
|
||||
Options Args
|
||||
}
|
||||
|
||||
func parsePort(portStr string) (int, error) {
|
||||
port, err := strconv.ParseUint(portStr, 10, 16)
|
||||
return int(port), err
|
||||
}
|
||||
|
||||
// Resolve an address string into a net.TCPAddr. We are a bit more strict than
|
||||
// net.ResolveTCPAddr; we don't allow an empty host or port, and the host part
|
||||
// must be a literal IP address.
|
||||
func resolveAddr(addrStr string) (*net.TCPAddr, error) {
|
||||
ipStr, portStr, err := net.SplitHostPort(addrStr)
|
||||
if err != nil {
|
||||
// Before the fixing of bug #7011, tor doesn't put brackets around IPv6
|
||||
// addresses. Split after the last colon, assuming it is a port
|
||||
// separator, and try adding the brackets.
|
||||
// https://bugs.torproject.org/7011
|
||||
parts := strings.Split(addrStr, ":")
|
||||
if len(parts) <= 2 {
|
||||
return nil, err
|
||||
}
|
||||
addrStr := "[" + strings.Join(parts[:len(parts)-1], ":") + "]:" + parts[len(parts)-1]
|
||||
ipStr, portStr, err = net.SplitHostPort(addrStr)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ipStr == "" {
|
||||
return nil, net.InvalidAddrError(fmt.Sprintf("address string %q lacks a host part", addrStr))
|
||||
}
|
||||
if portStr == "" {
|
||||
return nil, net.InvalidAddrError(fmt.Sprintf("address string %q lacks a port part", addrStr))
|
||||
}
|
||||
ip := net.ParseIP(ipStr)
|
||||
if ip == nil {
|
||||
return nil, net.InvalidAddrError(fmt.Sprintf("not an IP string: %q", ipStr))
|
||||
}
|
||||
port, err := parsePort(portStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &net.TCPAddr{IP: ip, Port: port}, nil
|
||||
}
|
||||
|
||||
// Return a new slice, the members of which are those members of addrs having a
|
||||
// MethodName in methodNames.
|
||||
func filterBindaddrs(addrs []Bindaddr, methodNames []string) []Bindaddr {
|
||||
var result []Bindaddr
|
||||
|
||||
for _, ba := range addrs {
|
||||
for _, methodName := range methodNames {
|
||||
if ba.MethodName == methodName {
|
||||
result = append(result, ba)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Return an array of Bindaddrs, being the contents of TOR_PT_SERVER_BINDADDR
|
||||
// with keys filtered by TOR_PT_SERVER_TRANSPORTS. Transport-specific options
|
||||
// from TOR_PT_SERVER_TRANSPORT_OPTIONS are assigned to the Options member.
|
||||
func getServerBindaddrs() ([]Bindaddr, error) {
|
||||
var result []Bindaddr
|
||||
|
||||
// Parse the list of server transport options.
|
||||
serverTransportOptions := getenv("TOR_PT_SERVER_TRANSPORT_OPTIONS")
|
||||
optionsMap, err := parseServerTransportOptions(serverTransportOptions)
|
||||
if err != nil {
|
||||
return nil, envError(fmt.Sprintf("TOR_PT_SERVER_TRANSPORT_OPTIONS: %q: %s", serverTransportOptions, err.Error()))
|
||||
}
|
||||
|
||||
// Get the list of all requested bindaddrs.
|
||||
serverBindaddr, err := getenvRequired("TOR_PT_SERVER_BINDADDR")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
seenMethods := make(map[string]bool)
|
||||
for _, spec := range strings.Split(serverBindaddr, ",") {
|
||||
var bindaddr Bindaddr
|
||||
|
||||
parts := strings.SplitN(spec, "-", 2)
|
||||
if len(parts) != 2 {
|
||||
return nil, envError(fmt.Sprintf("TOR_PT_SERVER_BINDADDR: %q: doesn't contain \"-\"", spec))
|
||||
}
|
||||
bindaddr.MethodName = parts[0]
|
||||
// Check for duplicate method names: "Applications MUST NOT set
|
||||
// more than one <address>:<port> pair per PT name."
|
||||
if seenMethods[bindaddr.MethodName] {
|
||||
return nil, envError(fmt.Sprintf("TOR_PT_SERVER_BINDADDR: %q: duplicate method name %q", spec, bindaddr.MethodName))
|
||||
}
|
||||
seenMethods[bindaddr.MethodName] = true
|
||||
addr, err := resolveAddr(parts[1])
|
||||
if err != nil {
|
||||
return nil, envError(fmt.Sprintf("TOR_PT_SERVER_BINDADDR: %q: %s", spec, err.Error()))
|
||||
}
|
||||
bindaddr.Addr = addr
|
||||
bindaddr.Options = optionsMap[bindaddr.MethodName]
|
||||
result = append(result, bindaddr)
|
||||
}
|
||||
|
||||
// Filter by TOR_PT_SERVER_TRANSPORTS.
|
||||
serverTransports, err := getenvRequired("TOR_PT_SERVER_TRANSPORTS")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result = filterBindaddrs(result, strings.Split(serverTransports, ","))
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func readAuthCookie(f io.Reader) ([]byte, error) {
|
||||
authCookieHeader := []byte("! Extended ORPort Auth Cookie !\x0a")
|
||||
buf := make([]byte, 64)
|
||||
|
||||
n, err := io.ReadFull(f, buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Check that the file ends here.
|
||||
n, err = f.Read(make([]byte, 1))
|
||||
if n != 0 {
|
||||
return nil, fmt.Errorf("file is longer than 64 bytes")
|
||||
} else if err != io.EOF {
|
||||
return nil, fmt.Errorf("did not find EOF at end of file")
|
||||
}
|
||||
header := buf[0:32]
|
||||
cookie := buf[32:64]
|
||||
if subtle.ConstantTimeCompare(header, authCookieHeader) != 1 {
|
||||
return nil, fmt.Errorf("missing auth cookie header")
|
||||
}
|
||||
|
||||
return cookie, nil
|
||||
}
|
||||
|
||||
// Read and validate the contents of an auth cookie file. Returns the 32-byte
|
||||
// cookie. See section 4.2.1.2 of 217-ext-orport-auth.txt.
|
||||
func readAuthCookieFile(filename string) ([]byte, error) {
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
return readAuthCookie(f)
|
||||
}
|
||||
|
||||
// This structure is returned by ServerSetup. It consists of a list of
|
||||
// Bindaddrs, an address for the ORPort, an address for the extended ORPort (if
|
||||
// any), and an authentication cookie (if any).
|
||||
type ServerInfo struct {
|
||||
Bindaddrs []Bindaddr
|
||||
OrAddr *net.TCPAddr
|
||||
ExtendedOrAddr *net.TCPAddr
|
||||
AuthCookiePath string
|
||||
}
|
||||
|
||||
// Check the server pluggable transports environment, emitting an error message
|
||||
// and returning a non-nil error if any error is encountered. Resolves the
|
||||
// various requested bind addresses, the server ORPort and extended ORPort, and
|
||||
// reads the auth cookie file. Returns a ServerInfo struct.
|
||||
//
|
||||
// If your program needs to know whether to call ClientSetup or ServerSetup
|
||||
// (i.e., if the same program can be run as either a client or a server), check
|
||||
// whether the TOR_PT_CLIENT_TRANSPORTS environment variable is set:
|
||||
// if os.Getenv("TOR_PT_CLIENT_TRANSPORTS") != "" {
|
||||
// // Client mode; call pt.ClientSetup.
|
||||
// } else {
|
||||
// // Server mode; call pt.ServerSetup.
|
||||
// }
|
||||
//
|
||||
// Always pass nil for the unused single parameter. In the past, the parameter
|
||||
// was a list of transport names to use in case Tor requested "*". That feature
|
||||
// was never implemented and has been removed from the pluggable transports
|
||||
// specification.
|
||||
// https://bugs.torproject.org/15612
|
||||
func ServerSetup(_ []string) (info ServerInfo, err error) {
|
||||
ver, err := getManagedTransportVer()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
line("VERSION", ver)
|
||||
|
||||
info.Bindaddrs, err = getServerBindaddrs()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
orPort := getenv("TOR_PT_ORPORT")
|
||||
if orPort != "" {
|
||||
info.OrAddr, err = resolveAddr(orPort)
|
||||
if err != nil {
|
||||
err = envError(fmt.Sprintf("cannot resolve TOR_PT_ORPORT %q: %s", orPort, err.Error()))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
info.AuthCookiePath = getenv("TOR_PT_AUTH_COOKIE_FILE")
|
||||
|
||||
extendedOrPort := getenv("TOR_PT_EXTENDED_SERVER_PORT")
|
||||
if extendedOrPort != "" {
|
||||
if info.AuthCookiePath == "" {
|
||||
err = envError("need TOR_PT_AUTH_COOKIE_FILE environment variable with TOR_PT_EXTENDED_SERVER_PORT")
|
||||
return
|
||||
}
|
||||
info.ExtendedOrAddr, err = resolveAddr(extendedOrPort)
|
||||
if err != nil {
|
||||
err = envError(fmt.Sprintf("cannot resolve TOR_PT_EXTENDED_SERVER_PORT %q: %s", extendedOrPort, err.Error()))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Need either OrAddr or ExtendedOrAddr.
|
||||
if info.OrAddr == nil && info.ExtendedOrAddr == nil {
|
||||
err = envError("need TOR_PT_ORPORT or TOR_PT_EXTENDED_SERVER_PORT environment variable")
|
||||
return
|
||||
}
|
||||
|
||||
return info, nil
|
||||
}
|
||||
|
||||
// See 217-ext-orport-auth.txt section 4.2.1.3.
|
||||
func computeServerHash(authCookie, clientNonce, serverNonce []byte) []byte {
|
||||
h := hmac.New(sha256.New, authCookie)
|
||||
io.WriteString(h, "ExtORPort authentication server-to-client hash")
|
||||
h.Write(clientNonce)
|
||||
h.Write(serverNonce)
|
||||
return h.Sum([]byte{})
|
||||
}
|
||||
|
||||
// See 217-ext-orport-auth.txt section 4.2.1.3.
|
||||
func computeClientHash(authCookie, clientNonce, serverNonce []byte) []byte {
|
||||
h := hmac.New(sha256.New, authCookie)
|
||||
io.WriteString(h, "ExtORPort authentication client-to-server hash")
|
||||
h.Write(clientNonce)
|
||||
h.Write(serverNonce)
|
||||
return h.Sum([]byte{})
|
||||
}
|
||||
|
||||
func extOrPortAuthenticate(s io.ReadWriter, info *ServerInfo) error {
|
||||
// Read auth types. 217-ext-orport-auth.txt section 4.1.
|
||||
var authTypes [256]bool
|
||||
var count int
|
||||
for count = 0; count < 256; count++ {
|
||||
buf := make([]byte, 1)
|
||||
_, err := io.ReadFull(s, buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b := buf[0]
|
||||
if b == 0 {
|
||||
break
|
||||
}
|
||||
authTypes[b] = true
|
||||
}
|
||||
if count >= 256 {
|
||||
return fmt.Errorf("read 256 auth types without seeing \\x00")
|
||||
}
|
||||
|
||||
// We support only type 1, SAFE_COOKIE.
|
||||
if !authTypes[1] {
|
||||
return fmt.Errorf("server didn't offer auth type 1")
|
||||
}
|
||||
_, err := s.Write([]byte{1})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
clientNonce := make([]byte, 32)
|
||||
clientHash := make([]byte, 32)
|
||||
serverNonce := make([]byte, 32)
|
||||
serverHash := make([]byte, 32)
|
||||
|
||||
_, err = io.ReadFull(rand.Reader, clientNonce)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = s.Write(clientNonce)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = io.ReadFull(s, serverHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = io.ReadFull(s, serverNonce)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Work around tor bug #15240 where the auth cookie is generated after
|
||||
// pluggable transports are launched, leading to a stale cookie getting
|
||||
// cached forever if it is only read once as part of ServerSetup.
|
||||
// https://bugs.torproject.org/15240
|
||||
authCookie, err := readAuthCookieFile(info.AuthCookiePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading TOR_PT_AUTH_COOKIE_FILE %q: %s", info.AuthCookiePath, err.Error())
|
||||
}
|
||||
|
||||
expectedServerHash := computeServerHash(authCookie, clientNonce, serverNonce)
|
||||
if subtle.ConstantTimeCompare(serverHash, expectedServerHash) != 1 {
|
||||
return fmt.Errorf("mismatch in server hash")
|
||||
}
|
||||
|
||||
clientHash = computeClientHash(authCookie, clientNonce, serverNonce)
|
||||
_, err = s.Write(clientHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
status := make([]byte, 1)
|
||||
_, err = io.ReadFull(s, status)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if status[0] != 1 {
|
||||
return fmt.Errorf("server rejected authentication")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// See section 3.1.1 of 196-transport-control-ports.txt.
|
||||
const (
|
||||
extOrCmdDone = 0x0000
|
||||
extOrCmdUserAddr = 0x0001
|
||||
extOrCmdTransport = 0x0002
|
||||
extOrCmdOkay = 0x1000
|
||||
extOrCmdDeny = 0x1001
|
||||
)
|
||||
|
||||
func extOrPortSendCommand(s io.Writer, cmd uint16, body []byte) error {
|
||||
var buf bytes.Buffer
|
||||
if len(body) > 65535 {
|
||||
return fmt.Errorf("body length %d exceeds maximum of 65535", len(body))
|
||||
}
|
||||
err := binary.Write(&buf, binary.BigEndian, cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = binary.Write(&buf, binary.BigEndian, uint16(len(body)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = binary.Write(&buf, binary.BigEndian, body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = s.Write(buf.Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Send a USERADDR command on s. See section 3.1.2.1 of
|
||||
// 196-transport-control-ports.txt.
|
||||
func extOrPortSendUserAddr(s io.Writer, addr string) error {
|
||||
return extOrPortSendCommand(s, extOrCmdUserAddr, []byte(addr))
|
||||
}
|
||||
|
||||
// Send a TRANSPORT command on s. See section 3.1.2.2 of
|
||||
// 196-transport-control-ports.txt.
|
||||
func extOrPortSendTransport(s io.Writer, methodName string) error {
|
||||
return extOrPortSendCommand(s, extOrCmdTransport, []byte(methodName))
|
||||
}
|
||||
|
||||
// Send a DONE command on s. See section 3.1 of 196-transport-control-ports.txt.
|
||||
func extOrPortSendDone(s io.Writer) error {
|
||||
return extOrPortSendCommand(s, extOrCmdDone, []byte{})
|
||||
}
|
||||
|
||||
func extOrPortRecvCommand(s io.Reader) (cmd uint16, body []byte, err error) {
|
||||
var bodyLen uint16
|
||||
data := make([]byte, 4)
|
||||
|
||||
_, err = io.ReadFull(s, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
buf := bytes.NewBuffer(data)
|
||||
err = binary.Read(buf, binary.BigEndian, &cmd)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = binary.Read(buf, binary.BigEndian, &bodyLen)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
body = make([]byte, bodyLen)
|
||||
_, err = io.ReadFull(s, body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return cmd, body, err
|
||||
}
|
||||
|
||||
// Send USERADDR and TRANSPORT commands followed by a DONE command. Wait for an
|
||||
// OKAY or DENY response command from the server. If addr or methodName is "",
|
||||
// the corresponding command is not sent. Returns nil if and only if OKAY is
|
||||
// received.
|
||||
func extOrPortSetup(s io.ReadWriter, addr, methodName string) error {
|
||||
var err error
|
||||
|
||||
if addr != "" {
|
||||
err = extOrPortSendUserAddr(s, addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if methodName != "" {
|
||||
err = extOrPortSendTransport(s, methodName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err = extOrPortSendDone(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd, _, err := extOrPortRecvCommand(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if cmd == extOrCmdDeny {
|
||||
return fmt.Errorf("server returned DENY after our USERADDR and DONE")
|
||||
} else if cmd != extOrCmdOkay {
|
||||
return fmt.Errorf("server returned unknown command 0x%04x after our USERADDR and DONE", cmd)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Dial info.ExtendedOrAddr if defined, or else info.OrAddr, and return an open
|
||||
// *net.TCPConn. If connecting to the extended OR port, extended OR port
|
||||
// authentication à la 217-ext-orport-auth.txt is done before returning; an
|
||||
// error is returned if authentication fails.
|
||||
//
|
||||
// The addr and methodName arguments are put in USERADDR and TRANSPORT ExtOrPort
|
||||
// commands, respectively. If either is "", the corresponding command is not
|
||||
// sent.
|
||||
func DialOr(info *ServerInfo, addr, methodName string) (*net.TCPConn, error) {
|
||||
if info.ExtendedOrAddr == nil || info.AuthCookiePath == "" {
|
||||
return net.DialTCP("tcp", nil, info.OrAddr)
|
||||
}
|
||||
|
||||
s, err := net.DialTCP("tcp", nil, info.ExtendedOrAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.SetDeadline(time.Now().Add(5 * time.Second))
|
||||
err = extOrPortAuthenticate(s, info)
|
||||
if err != nil {
|
||||
s.Close()
|
||||
return nil, err
|
||||
}
|
||||
err = extOrPortSetup(s, addr, methodName)
|
||||
if err != nil {
|
||||
s.Close()
|
||||
return nil, err
|
||||
}
|
||||
s.SetDeadline(time.Time{})
|
||||
|
||||
return s, nil
|
||||
}
|
507
vendor/git.torproject.org/pluggable-transports/goptlib.git/socks.go
generated
vendored
Normal file
507
vendor/git.torproject.org/pluggable-transports/goptlib.git/socks.go
generated
vendored
Normal file
@ -0,0 +1,507 @@
|
||||
package pt
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
socksVersion = 0x05
|
||||
|
||||
socksAuthNoneRequired = 0x00
|
||||
socksAuthUsernamePassword = 0x02
|
||||
socksAuthNoAcceptableMethods = 0xff
|
||||
|
||||
socksCmdConnect = 0x01
|
||||
socksRsv = 0x00
|
||||
|
||||
socksAtypeV4 = 0x01
|
||||
socksAtypeDomainName = 0x03
|
||||
socksAtypeV6 = 0x04
|
||||
|
||||
socksAuthRFC1929Ver = 0x01
|
||||
socksAuthRFC1929Success = 0x00
|
||||
socksAuthRFC1929Fail = 0x01
|
||||
|
||||
socksRepSucceeded = 0x00
|
||||
// "general SOCKS server failure"
|
||||
SocksRepGeneralFailure = 0x01
|
||||
// "connection not allowed by ruleset"
|
||||
SocksRepConnectionNotAllowed = 0x02
|
||||
// "Network unreachable"
|
||||
SocksRepNetworkUnreachable = 0x03
|
||||
// "Host unreachable"
|
||||
SocksRepHostUnreachable = 0x04
|
||||
// "Connection refused"
|
||||
SocksRepConnectionRefused = 0x05
|
||||
// "TTL expired"
|
||||
SocksRepTTLExpired = 0x06
|
||||
// "Command not supported"
|
||||
SocksRepCommandNotSupported = 0x07
|
||||
// "Address type not supported"
|
||||
SocksRepAddressNotSupported = 0x08
|
||||
)
|
||||
|
||||
// Put a sanity timeout on how long we wait for a SOCKS request.
|
||||
const socksRequestTimeout = 5 * time.Second
|
||||
|
||||
// SocksRequest describes a SOCKS request.
|
||||
type SocksRequest struct {
|
||||
// The endpoint requested by the client as a "host:port" string.
|
||||
Target string
|
||||
// The userid string sent by the client.
|
||||
Username string
|
||||
// The password string sent by the client.
|
||||
Password string
|
||||
// The parsed contents of Username as a key–value mapping.
|
||||
Args Args
|
||||
}
|
||||
|
||||
// SocksConn encapsulates a net.Conn and information associated with a SOCKS request.
|
||||
type SocksConn struct {
|
||||
net.Conn
|
||||
Req SocksRequest
|
||||
}
|
||||
|
||||
// Send a message to the proxy client that access to the given address is
|
||||
// granted. Addr is ignored, and "0.0.0.0:0" is always sent back for
|
||||
// BND.ADDR/BND.PORT in the SOCKS response.
|
||||
func (conn *SocksConn) Grant(addr *net.TCPAddr) error {
|
||||
return sendSocks5ResponseGranted(conn)
|
||||
}
|
||||
|
||||
// Send a message to the proxy client that access was rejected or failed. This
|
||||
// sends back a "General Failure" error code. RejectReason should be used if
|
||||
// more specific error reporting is desired.
|
||||
func (conn *SocksConn) Reject() error {
|
||||
return conn.RejectReason(SocksRepGeneralFailure)
|
||||
}
|
||||
|
||||
// Send a message to the proxy client that access was rejected, with the
|
||||
// specific error code indicating the reason behind the rejection.
|
||||
func (conn *SocksConn) RejectReason(reason byte) error {
|
||||
return sendSocks5ResponseRejected(conn, reason)
|
||||
}
|
||||
|
||||
// SocksListener wraps a net.Listener in order to read a SOCKS request on Accept.
|
||||
//
|
||||
// func handleConn(conn *pt.SocksConn) error {
|
||||
// defer conn.Close()
|
||||
// remote, err := net.Dial("tcp", conn.Req.Target)
|
||||
// if err != nil {
|
||||
// conn.Reject()
|
||||
// return err
|
||||
// }
|
||||
// defer remote.Close()
|
||||
// err = conn.Grant(remote.RemoteAddr().(*net.TCPAddr))
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// // do something with conn and remote
|
||||
// return nil
|
||||
// }
|
||||
// ...
|
||||
// ln, err := pt.ListenSocks("tcp", "127.0.0.1:0")
|
||||
// if err != nil {
|
||||
// panic(err.Error())
|
||||
// }
|
||||
// for {
|
||||
// conn, err := ln.AcceptSocks()
|
||||
// if err != nil {
|
||||
// log.Printf("accept error: %s", err)
|
||||
// if e, ok := err.(net.Error); ok && e.Temporary() {
|
||||
// continue
|
||||
// }
|
||||
// break
|
||||
// }
|
||||
// go handleConn(conn)
|
||||
// }
|
||||
type SocksListener struct {
|
||||
net.Listener
|
||||
}
|
||||
|
||||
// Open a net.Listener according to network and laddr, and return it as a
|
||||
// SocksListener.
|
||||
func ListenSocks(network, laddr string) (*SocksListener, error) {
|
||||
ln, err := net.Listen(network, laddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewSocksListener(ln), nil
|
||||
}
|
||||
|
||||
// Create a new SocksListener wrapping the given net.Listener.
|
||||
func NewSocksListener(ln net.Listener) *SocksListener {
|
||||
return &SocksListener{ln}
|
||||
}
|
||||
|
||||
// Accept is the same as AcceptSocks, except that it returns a generic net.Conn.
|
||||
// It is present for the sake of satisfying the net.Listener interface.
|
||||
func (ln *SocksListener) Accept() (net.Conn, error) {
|
||||
return ln.AcceptSocks()
|
||||
}
|
||||
|
||||
// Call Accept on the wrapped net.Listener, do SOCKS negotiation, and return a
|
||||
// SocksConn. After accepting, you must call either conn.Grant or conn.Reject
|
||||
// (presumably after trying to connect to conn.Req.Target).
|
||||
//
|
||||
// Errors returned by AcceptSocks may be temporary (for example, EOF while
|
||||
// reading the request, or a badly formatted userid string), or permanent (e.g.,
|
||||
// the underlying socket is closed). You can determine whether an error is
|
||||
// temporary and take appropriate action with a type conversion to net.Error.
|
||||
// For example:
|
||||
//
|
||||
// for {
|
||||
// conn, err := ln.AcceptSocks()
|
||||
// if err != nil {
|
||||
// if e, ok := err.(net.Error); ok && e.Temporary() {
|
||||
// log.Printf("temporary accept error; trying again: %s", err)
|
||||
// continue
|
||||
// }
|
||||
// log.Printf("permanent accept error; giving up: %s", err)
|
||||
// break
|
||||
// }
|
||||
// go handleConn(conn)
|
||||
// }
|
||||
func (ln *SocksListener) AcceptSocks() (*SocksConn, error) {
|
||||
retry:
|
||||
c, err := ln.Listener.Accept()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conn := new(SocksConn)
|
||||
conn.Conn = c
|
||||
err = conn.SetDeadline(time.Now().Add(socksRequestTimeout))
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
goto retry
|
||||
}
|
||||
conn.Req, err = socks5Handshake(conn)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
goto retry
|
||||
}
|
||||
err = conn.SetDeadline(time.Time{})
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
goto retry
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// Returns "socks5", suitable to be included in a call to Cmethod.
|
||||
func (ln *SocksListener) Version() string {
|
||||
return "socks5"
|
||||
}
|
||||
|
||||
// socks5handshake conducts the SOCKS5 handshake up to the point where the
|
||||
// client command is read and the proxy must open the outgoing connection.
|
||||
// Returns a SocksRequest.
|
||||
func socks5Handshake(s io.ReadWriter) (req SocksRequest, err error) {
|
||||
rw := bufio.NewReadWriter(bufio.NewReader(s), bufio.NewWriter(s))
|
||||
|
||||
// Negotiate the authentication method.
|
||||
var method byte
|
||||
if method, err = socksNegotiateAuth(rw); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Authenticate the client.
|
||||
if err = socksAuthenticate(rw, method, &req); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Read the command.
|
||||
err = socksReadCommand(rw, &req)
|
||||
return
|
||||
}
|
||||
|
||||
// socksNegotiateAuth negotiates the authentication method and returns the
|
||||
// selected method as a byte. On negotiation failures an error is returned.
|
||||
func socksNegotiateAuth(rw *bufio.ReadWriter) (method byte, err error) {
|
||||
// Validate the version.
|
||||
if err = socksReadByteVerify(rw, "version", socksVersion); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Read the number of methods.
|
||||
var nmethods byte
|
||||
if nmethods, err = socksReadByte(rw); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Read the methods.
|
||||
var methods []byte
|
||||
if methods, err = socksReadBytes(rw, int(nmethods)); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Pick the most "suitable" method.
|
||||
method = socksAuthNoAcceptableMethods
|
||||
for _, m := range methods {
|
||||
switch m {
|
||||
case socksAuthNoneRequired:
|
||||
// Pick Username/Password over None if the client happens to
|
||||
// send both.
|
||||
if method == socksAuthNoAcceptableMethods {
|
||||
method = m
|
||||
}
|
||||
|
||||
case socksAuthUsernamePassword:
|
||||
method = m
|
||||
}
|
||||
}
|
||||
|
||||
// Send the negotiated method.
|
||||
var msg [2]byte
|
||||
msg[0] = socksVersion
|
||||
msg[1] = method
|
||||
if _, err = rw.Writer.Write(msg[:]); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = socksFlushBuffers(rw); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// socksAuthenticate authenticates the client via the chosen authentication
|
||||
// mechanism.
|
||||
func socksAuthenticate(rw *bufio.ReadWriter, method byte, req *SocksRequest) (err error) {
|
||||
switch method {
|
||||
case socksAuthNoneRequired:
|
||||
// Straight into reading the connect.
|
||||
|
||||
case socksAuthUsernamePassword:
|
||||
if err = socksAuthRFC1929(rw, req); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
case socksAuthNoAcceptableMethods:
|
||||
err = fmt.Errorf("SOCKS method select had no compatible methods")
|
||||
return
|
||||
|
||||
default:
|
||||
err = fmt.Errorf("SOCKS method select picked a unsupported method 0x%02x", method)
|
||||
return
|
||||
}
|
||||
|
||||
if err = socksFlushBuffers(rw); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// socksAuthRFC1929 authenticates the client via RFC 1929 username/password
|
||||
// auth. As a design decision any valid username/password is accepted as this
|
||||
// field is primarily used as an out-of-band argument passing mechanism for
|
||||
// pluggable transports.
|
||||
func socksAuthRFC1929(rw *bufio.ReadWriter, req *SocksRequest) (err error) {
|
||||
sendErrResp := func() {
|
||||
// Swallow the write/flush error here, we are going to close the
|
||||
// connection and the original failure is more useful.
|
||||
resp := []byte{socksAuthRFC1929Ver, socksAuthRFC1929Fail}
|
||||
rw.Write(resp[:])
|
||||
socksFlushBuffers(rw)
|
||||
}
|
||||
|
||||
// Validate the fixed parts of the command message.
|
||||
if err = socksReadByteVerify(rw, "auth version", socksAuthRFC1929Ver); err != nil {
|
||||
sendErrResp()
|
||||
return
|
||||
}
|
||||
|
||||
// Read the username.
|
||||
var ulen byte
|
||||
if ulen, err = socksReadByte(rw); err != nil {
|
||||
return
|
||||
}
|
||||
if ulen < 1 {
|
||||
sendErrResp()
|
||||
err = fmt.Errorf("RFC1929 username with 0 length")
|
||||
return
|
||||
}
|
||||
var uname []byte
|
||||
if uname, err = socksReadBytes(rw, int(ulen)); err != nil {
|
||||
return
|
||||
}
|
||||
req.Username = string(uname)
|
||||
|
||||
// Read the password.
|
||||
var plen byte
|
||||
if plen, err = socksReadByte(rw); err != nil {
|
||||
return
|
||||
}
|
||||
if plen < 1 {
|
||||
sendErrResp()
|
||||
err = fmt.Errorf("RFC1929 password with 0 length")
|
||||
return
|
||||
}
|
||||
var passwd []byte
|
||||
if passwd, err = socksReadBytes(rw, int(plen)); err != nil {
|
||||
return
|
||||
}
|
||||
if !(plen == 1 && passwd[0] == 0x00) {
|
||||
// tor will set the password to 'NUL' if there are no arguments.
|
||||
req.Password = string(passwd)
|
||||
}
|
||||
|
||||
// Mash the username/password together and parse it as a pluggable
|
||||
// transport argument string.
|
||||
if req.Args, err = parseClientParameters(req.Username + req.Password); err != nil {
|
||||
sendErrResp()
|
||||
} else {
|
||||
resp := []byte{socksAuthRFC1929Ver, socksAuthRFC1929Success}
|
||||
_, err = rw.Write(resp[:])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// socksReadCommand reads a SOCKS5 client command and parses out the relevant
|
||||
// fields into a SocksRequest. Only CMD_CONNECT is supported.
|
||||
func socksReadCommand(rw *bufio.ReadWriter, req *SocksRequest) (err error) {
|
||||
sendErrResp := func(reason byte) {
|
||||
// Swallow errors that occur when writing/flushing the response,
|
||||
// connection will be closed anyway.
|
||||
sendSocks5ResponseRejected(rw, reason)
|
||||
socksFlushBuffers(rw)
|
||||
}
|
||||
|
||||
// Validate the fixed parts of the command message.
|
||||
if err = socksReadByteVerify(rw, "version", socksVersion); err != nil {
|
||||
sendErrResp(SocksRepGeneralFailure)
|
||||
return
|
||||
}
|
||||
if err = socksReadByteVerify(rw, "command", socksCmdConnect); err != nil {
|
||||
sendErrResp(SocksRepCommandNotSupported)
|
||||
return
|
||||
}
|
||||
if err = socksReadByteVerify(rw, "reserved", socksRsv); err != nil {
|
||||
sendErrResp(SocksRepGeneralFailure)
|
||||
return
|
||||
}
|
||||
|
||||
// Read the destination address/port.
|
||||
// XXX: This should probably eventually send socks 5 error messages instead
|
||||
// of rudely closing connections on invalid addresses.
|
||||
var atype byte
|
||||
if atype, err = socksReadByte(rw); err != nil {
|
||||
return
|
||||
}
|
||||
var host string
|
||||
switch atype {
|
||||
case socksAtypeV4:
|
||||
var addr []byte
|
||||
if addr, err = socksReadBytes(rw, net.IPv4len); err != nil {
|
||||
return
|
||||
}
|
||||
host = net.IPv4(addr[0], addr[1], addr[2], addr[3]).String()
|
||||
|
||||
case socksAtypeDomainName:
|
||||
var alen byte
|
||||
if alen, err = socksReadByte(rw); err != nil {
|
||||
return
|
||||
}
|
||||
if alen == 0 {
|
||||
err = fmt.Errorf("SOCKS request had domain name with 0 length")
|
||||
return
|
||||
}
|
||||
var addr []byte
|
||||
if addr, err = socksReadBytes(rw, int(alen)); err != nil {
|
||||
return
|
||||
}
|
||||
host = string(addr)
|
||||
|
||||
case socksAtypeV6:
|
||||
var rawAddr []byte
|
||||
if rawAddr, err = socksReadBytes(rw, net.IPv6len); err != nil {
|
||||
return
|
||||
}
|
||||
addr := make(net.IP, net.IPv6len)
|
||||
copy(addr[:], rawAddr[:])
|
||||
host = fmt.Sprintf("[%s]", addr.String())
|
||||
|
||||
default:
|
||||
sendErrResp(SocksRepAddressNotSupported)
|
||||
err = fmt.Errorf("SOCKS request had unsupported address type 0x%02x", atype)
|
||||
return
|
||||
}
|
||||
var rawPort []byte
|
||||
if rawPort, err = socksReadBytes(rw, 2); err != nil {
|
||||
return
|
||||
}
|
||||
port := int(rawPort[0])<<8 | int(rawPort[1])<<0
|
||||
|
||||
if err = socksFlushBuffers(rw); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
req.Target = fmt.Sprintf("%s:%d", host, port)
|
||||
return
|
||||
}
|
||||
|
||||
// Send a SOCKS5 response with the given code. BND.ADDR/BND.PORT is always the
|
||||
// IPv4 address/port "0.0.0.0:0".
|
||||
func sendSocks5Response(w io.Writer, code byte) error {
|
||||
resp := make([]byte, 4+4+2)
|
||||
resp[0] = socksVersion
|
||||
resp[1] = code
|
||||
resp[2] = socksRsv
|
||||
resp[3] = socksAtypeV4
|
||||
|
||||
// BND.ADDR/BND.PORT should be the address and port that the outgoing
|
||||
// connection is bound to on the proxy, but Tor does not use this
|
||||
// information, so all zeroes are sent.
|
||||
|
||||
_, err := w.Write(resp[:])
|
||||
return err
|
||||
}
|
||||
|
||||
// Send a SOCKS5 response code 0x00.
|
||||
func sendSocks5ResponseGranted(w io.Writer) error {
|
||||
return sendSocks5Response(w, socksRepSucceeded)
|
||||
}
|
||||
|
||||
// Send a SOCKS5 response with the provided failure reason.
|
||||
func sendSocks5ResponseRejected(w io.Writer, reason byte) error {
|
||||
return sendSocks5Response(w, reason)
|
||||
}
|
||||
|
||||
func socksFlushBuffers(rw *bufio.ReadWriter) error {
|
||||
if err := rw.Writer.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
if rw.Reader.Buffered() > 0 {
|
||||
return fmt.Errorf("%d bytes left after SOCKS message", rw.Reader.Buffered())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func socksReadByte(rw *bufio.ReadWriter) (byte, error) {
|
||||
return rw.Reader.ReadByte()
|
||||
}
|
||||
|
||||
func socksReadBytes(rw *bufio.ReadWriter, n int) ([]byte, error) {
|
||||
ret := make([]byte, n)
|
||||
if _, err := io.ReadFull(rw.Reader, ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func socksReadByteVerify(rw *bufio.ReadWriter, descr string, expected byte) error {
|
||||
val, err := socksReadByte(rw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if val != expected {
|
||||
return fmt.Errorf("SOCKS message field %s was 0x%02x, not 0x%02x", descr, val, expected)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ net.Listener = (*SocksListener)(nil)
|
55
vendor/git.torproject.org/pluggable-transports/obfs4.git/LICENSE
generated
vendored
Normal file
55
vendor/git.torproject.org/pluggable-transports/obfs4.git/LICENSE
generated
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
Copyright (c) 2014, Yawning Angel <yawning at torproject dot org>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
==============================================================================
|
||||
|
||||
Copyright (c) 2012 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
101
vendor/git.torproject.org/pluggable-transports/obfs4.git/common/csrand/csrand.go
generated
vendored
Normal file
101
vendor/git.torproject.org/pluggable-transports/obfs4.git/common/csrand/csrand.go
generated
vendored
Normal file
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Yawning Angel <yawning at torproject dot org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
// Package csrand implements the math/rand interface over crypto/rand, along
|
||||
// with some utility functions for common random number/byte related tasks.
|
||||
//
|
||||
// Not all of the convinience routines are replicated, only those that are
|
||||
// immediately useful. The Rand variable provides access to the full math/rand
|
||||
// API.
|
||||
package csrand
|
||||
|
||||
import (
|
||||
cryptRand "crypto/rand"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
)
|
||||
|
||||
var (
|
||||
csRandSourceInstance csRandSource
|
||||
|
||||
// Rand is a math/rand instance backed by crypto/rand CSPRNG.
|
||||
Rand = rand.New(csRandSourceInstance)
|
||||
)
|
||||
|
||||
type csRandSource struct {
|
||||
// This does not keep any state as it is backed by crypto/rand.
|
||||
}
|
||||
|
||||
func (r csRandSource) Int63() int64 {
|
||||
var src [8]byte
|
||||
if err := Bytes(src[:]); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
val := binary.BigEndian.Uint64(src[:])
|
||||
val &= (1<<63 - 1)
|
||||
|
||||
return int64(val)
|
||||
}
|
||||
|
||||
func (r csRandSource) Seed(seed int64) {
|
||||
// No-op.
|
||||
}
|
||||
|
||||
// Intn returns, as a int, a pseudo random number in [0, n).
|
||||
func Intn(n int) int {
|
||||
return Rand.Intn(n)
|
||||
}
|
||||
|
||||
// Float64 returns, as a float64, a pesudo random number in [0.0,1.0).
|
||||
func Float64() float64 {
|
||||
return Rand.Float64()
|
||||
}
|
||||
|
||||
// IntRange returns a uniformly distributed int [min, max].
|
||||
func IntRange(min, max int) int {
|
||||
if max < min {
|
||||
panic(fmt.Sprintf("IntRange: min > max (%d, %d)", min, max))
|
||||
}
|
||||
|
||||
r := (max + 1) - min
|
||||
ret := Rand.Intn(r)
|
||||
return ret + min
|
||||
}
|
||||
|
||||
// Bytes fills the slice with random data.
|
||||
func Bytes(buf []byte) error {
|
||||
if _, err := io.ReadFull(cryptRand.Reader, buf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Reader is a alias of rand.Reader.
|
||||
var Reader = cryptRand.Reader
|
149
vendor/git.torproject.org/pluggable-transports/obfs4.git/common/drbg/hash_drbg.go
generated
vendored
Normal file
149
vendor/git.torproject.org/pluggable-transports/obfs4.git/common/drbg/hash_drbg.go
generated
vendored
Normal file
@ -0,0 +1,149 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Yawning Angel <yawning at torproject dot org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
// Package drbg implements a minimalistic DRBG based off SipHash-2-4 in OFB
|
||||
// mode.
|
||||
package drbg
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"hash"
|
||||
|
||||
"github.com/dchest/siphash"
|
||||
|
||||
"git.torproject.org/pluggable-transports/obfs4.git/common/csrand"
|
||||
)
|
||||
|
||||
// Size is the length of the HashDrbg output.
|
||||
const Size = siphash.Size
|
||||
|
||||
// SeedLength is the length of the HashDrbg seed.
|
||||
const SeedLength = 16 + Size
|
||||
|
||||
// Seed is the initial state for a HashDrbg. It consists of a SipHash-2-4
|
||||
// key, and 8 bytes of initial data.
|
||||
type Seed [SeedLength]byte
|
||||
|
||||
// Bytes returns a pointer to the raw HashDrbg seed.
|
||||
func (seed *Seed) Bytes() *[SeedLength]byte {
|
||||
return (*[SeedLength]byte)(seed)
|
||||
}
|
||||
|
||||
// Hex returns the hexdecimal representation of the seed.
|
||||
func (seed *Seed) Hex() string {
|
||||
return hex.EncodeToString(seed.Bytes()[:])
|
||||
}
|
||||
|
||||
// NewSeed returns a Seed initialized with the runtime CSPRNG.
|
||||
func NewSeed() (seed *Seed, err error) {
|
||||
seed = new(Seed)
|
||||
if err = csrand.Bytes(seed.Bytes()[:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// SeedFromBytes creates a Seed from the raw bytes, truncating to SeedLength as
|
||||
// appropriate.
|
||||
func SeedFromBytes(src []byte) (seed *Seed, err error) {
|
||||
if len(src) < SeedLength {
|
||||
return nil, InvalidSeedLengthError(len(src))
|
||||
}
|
||||
|
||||
seed = new(Seed)
|
||||
copy(seed.Bytes()[:], src)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// SeedFromHex creates a Seed from the hexdecimal representation, truncating to
|
||||
// SeedLength as appropriate.
|
||||
func SeedFromHex(encoded string) (seed *Seed, err error) {
|
||||
var raw []byte
|
||||
if raw, err = hex.DecodeString(encoded); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return SeedFromBytes(raw)
|
||||
}
|
||||
|
||||
// InvalidSeedLengthError is the error returned when the seed provided to the
|
||||
// DRBG is an invalid length.
|
||||
type InvalidSeedLengthError int
|
||||
|
||||
func (e InvalidSeedLengthError) Error() string {
|
||||
return fmt.Sprintf("invalid seed length: %d", int(e))
|
||||
}
|
||||
|
||||
// HashDrbg is a CSDRBG based off of SipHash-2-4 in OFB mode.
|
||||
type HashDrbg struct {
|
||||
sip hash.Hash64
|
||||
ofb [Size]byte
|
||||
}
|
||||
|
||||
// NewHashDrbg makes a HashDrbg instance based off an optional seed. The seed
|
||||
// is truncated to SeedLength.
|
||||
func NewHashDrbg(seed *Seed) (*HashDrbg, error) {
|
||||
drbg := new(HashDrbg)
|
||||
if seed == nil {
|
||||
var err error
|
||||
if seed, err = NewSeed(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
drbg.sip = siphash.New(seed.Bytes()[:16])
|
||||
copy(drbg.ofb[:], seed.Bytes()[16:])
|
||||
|
||||
return drbg, nil
|
||||
}
|
||||
|
||||
// Int63 returns a uniformly distributed random integer [0, 1 << 63).
|
||||
func (drbg *HashDrbg) Int63() int64 {
|
||||
block := drbg.NextBlock()
|
||||
ret := binary.BigEndian.Uint64(block)
|
||||
ret &= (1<<63 - 1)
|
||||
|
||||
return int64(ret)
|
||||
}
|
||||
|
||||
// Seed does nothing, call NewHashDrbg if you want to reseed.
|
||||
func (drbg *HashDrbg) Seed(seed int64) {
|
||||
// No-op.
|
||||
}
|
||||
|
||||
// NextBlock returns the next 8 byte DRBG block.
|
||||
func (drbg *HashDrbg) NextBlock() []byte {
|
||||
drbg.sip.Write(drbg.ofb[:])
|
||||
copy(drbg.ofb[:], drbg.sip.Sum(nil))
|
||||
|
||||
ret := make([]byte, Size)
|
||||
copy(ret, drbg.ofb[:])
|
||||
return ret
|
||||
}
|
433
vendor/git.torproject.org/pluggable-transports/obfs4.git/common/ntor/ntor.go
generated
vendored
Normal file
433
vendor/git.torproject.org/pluggable-transports/obfs4.git/common/ntor/ntor.go
generated
vendored
Normal file
@ -0,0 +1,433 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Yawning Angel <yawning at torproject dot org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
// Package ntor implements the Tor Project's ntor handshake as defined in
|
||||
// proposal 216 "Improved circuit-creation key exchange". It also supports
|
||||
// using Elligator to transform the Curve25519 public keys sent over the wire
|
||||
// to a form that is indistinguishable from random strings.
|
||||
//
|
||||
// Before using this package, it is strongly recommended that the specification
|
||||
// is read and understood.
|
||||
package ntor
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"crypto/subtle"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"golang.org/x/crypto/curve25519"
|
||||
"golang.org/x/crypto/hkdf"
|
||||
|
||||
"github.com/agl/ed25519/extra25519"
|
||||
|
||||
"git.torproject.org/pluggable-transports/obfs4.git/common/csrand"
|
||||
)
|
||||
|
||||
const (
|
||||
// PublicKeyLength is the length of a Curve25519 public key.
|
||||
PublicKeyLength = 32
|
||||
|
||||
// RepresentativeLength is the length of an Elligator representative.
|
||||
RepresentativeLength = 32
|
||||
|
||||
// PrivateKeyLength is the length of a Curve25519 private key.
|
||||
PrivateKeyLength = 32
|
||||
|
||||
// SharedSecretLength is the length of a Curve25519 shared secret.
|
||||
SharedSecretLength = 32
|
||||
|
||||
// NodeIDLength is the length of a ntor node identifier.
|
||||
NodeIDLength = 20
|
||||
|
||||
// KeySeedLength is the length of the derived KEY_SEED.
|
||||
KeySeedLength = sha256.Size
|
||||
|
||||
// AuthLength is the lenght of the derived AUTH.
|
||||
AuthLength = sha256.Size
|
||||
)
|
||||
|
||||
var protoID = []byte("ntor-curve25519-sha256-1")
|
||||
var tMac = append(protoID, []byte(":mac")...)
|
||||
var tKey = append(protoID, []byte(":key_extract")...)
|
||||
var tVerify = append(protoID, []byte(":key_verify")...)
|
||||
var mExpand = append(protoID, []byte(":key_expand")...)
|
||||
|
||||
// PublicKeyLengthError is the error returned when the public key being
|
||||
// imported is an invalid length.
|
||||
type PublicKeyLengthError int
|
||||
|
||||
func (e PublicKeyLengthError) Error() string {
|
||||
return fmt.Sprintf("ntor: Invalid Curve25519 public key length: %d",
|
||||
int(e))
|
||||
}
|
||||
|
||||
// PrivateKeyLengthError is the error returned when the private key being
|
||||
// imported is an invalid length.
|
||||
type PrivateKeyLengthError int
|
||||
|
||||
func (e PrivateKeyLengthError) Error() string {
|
||||
return fmt.Sprintf("ntor: Invalid Curve25519 private key length: %d",
|
||||
int(e))
|
||||
}
|
||||
|
||||
// NodeIDLengthError is the error returned when the node ID being imported is
|
||||
// an invalid length.
|
||||
type NodeIDLengthError int
|
||||
|
||||
func (e NodeIDLengthError) Error() string {
|
||||
return fmt.Sprintf("ntor: Invalid NodeID length: %d", int(e))
|
||||
}
|
||||
|
||||
// KeySeed is the key material that results from a handshake (KEY_SEED).
|
||||
type KeySeed [KeySeedLength]byte
|
||||
|
||||
// Bytes returns a pointer to the raw key material.
|
||||
func (key_seed *KeySeed) Bytes() *[KeySeedLength]byte {
|
||||
return (*[KeySeedLength]byte)(key_seed)
|
||||
}
|
||||
|
||||
// Auth is the verifier that results from a handshake (AUTH).
|
||||
type Auth [AuthLength]byte
|
||||
|
||||
// Bytes returns a pointer to the raw auth.
|
||||
func (auth *Auth) Bytes() *[AuthLength]byte {
|
||||
return (*[AuthLength]byte)(auth)
|
||||
}
|
||||
|
||||
// NodeID is a ntor node identifier.
|
||||
type NodeID [NodeIDLength]byte
|
||||
|
||||
// NewNodeID creates a NodeID from the raw bytes.
|
||||
func NewNodeID(raw []byte) (*NodeID, error) {
|
||||
if len(raw) != NodeIDLength {
|
||||
return nil, NodeIDLengthError(len(raw))
|
||||
}
|
||||
|
||||
nodeID := new(NodeID)
|
||||
copy(nodeID[:], raw)
|
||||
|
||||
return nodeID, nil
|
||||
}
|
||||
|
||||
// NodeIDFromHex creates a new NodeID from the hexdecimal representation.
|
||||
func NodeIDFromHex(encoded string) (*NodeID, error) {
|
||||
raw, err := hex.DecodeString(encoded)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewNodeID(raw)
|
||||
}
|
||||
|
||||
// Bytes returns a pointer to the raw NodeID.
|
||||
func (id *NodeID) Bytes() *[NodeIDLength]byte {
|
||||
return (*[NodeIDLength]byte)(id)
|
||||
}
|
||||
|
||||
// Hex returns the hexdecimal representation of the NodeID.
|
||||
func (id *NodeID) Hex() string {
|
||||
return hex.EncodeToString(id[:])
|
||||
}
|
||||
|
||||
// PublicKey is a Curve25519 public key in little-endian byte order.
|
||||
type PublicKey [PublicKeyLength]byte
|
||||
|
||||
// Bytes returns a pointer to the raw Curve25519 public key.
|
||||
func (public *PublicKey) Bytes() *[PublicKeyLength]byte {
|
||||
return (*[PublicKeyLength]byte)(public)
|
||||
}
|
||||
|
||||
// Hex returns the hexdecimal representation of the Curve25519 public key.
|
||||
func (public *PublicKey) Hex() string {
|
||||
return hex.EncodeToString(public.Bytes()[:])
|
||||
}
|
||||
|
||||
// NewPublicKey creates a PublicKey from the raw bytes.
|
||||
func NewPublicKey(raw []byte) (*PublicKey, error) {
|
||||
if len(raw) != PublicKeyLength {
|
||||
return nil, PublicKeyLengthError(len(raw))
|
||||
}
|
||||
|
||||
pubKey := new(PublicKey)
|
||||
copy(pubKey[:], raw)
|
||||
|
||||
return pubKey, nil
|
||||
}
|
||||
|
||||
// PublicKeyFromHex returns a PublicKey from the hexdecimal representation.
|
||||
func PublicKeyFromHex(encoded string) (*PublicKey, error) {
|
||||
raw, err := hex.DecodeString(encoded)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewPublicKey(raw)
|
||||
}
|
||||
|
||||
// Representative is an Elligator representative of a Curve25519 public key
|
||||
// in little-endian byte order.
|
||||
type Representative [RepresentativeLength]byte
|
||||
|
||||
// Bytes returns a pointer to the raw Elligator representative.
|
||||
func (repr *Representative) Bytes() *[RepresentativeLength]byte {
|
||||
return (*[RepresentativeLength]byte)(repr)
|
||||
}
|
||||
|
||||
// ToPublic converts a Elligator representative to a Curve25519 public key.
|
||||
func (repr *Representative) ToPublic() *PublicKey {
|
||||
pub := new(PublicKey)
|
||||
|
||||
extra25519.RepresentativeToPublicKey(pub.Bytes(), repr.Bytes())
|
||||
return pub
|
||||
}
|
||||
|
||||
// PrivateKey is a Curve25519 private key in little-endian byte order.
|
||||
type PrivateKey [PrivateKeyLength]byte
|
||||
|
||||
// Bytes returns a pointer to the raw Curve25519 private key.
|
||||
func (private *PrivateKey) Bytes() *[PrivateKeyLength]byte {
|
||||
return (*[PrivateKeyLength]byte)(private)
|
||||
}
|
||||
|
||||
// Hex returns the hexdecimal representation of the Curve25519 private key.
|
||||
func (private *PrivateKey) Hex() string {
|
||||
return hex.EncodeToString(private.Bytes()[:])
|
||||
}
|
||||
|
||||
// Keypair is a Curve25519 keypair with an optional Elligator representative.
|
||||
// As only certain Curve25519 keys can be obfuscated with Elligator, the
|
||||
// representative must be generated along with the keypair.
|
||||
type Keypair struct {
|
||||
public *PublicKey
|
||||
private *PrivateKey
|
||||
representative *Representative
|
||||
}
|
||||
|
||||
// Public returns the Curve25519 public key belonging to the Keypair.
|
||||
func (keypair *Keypair) Public() *PublicKey {
|
||||
return keypair.public
|
||||
}
|
||||
|
||||
// Private returns the Curve25519 private key belonging to the Keypair.
|
||||
func (keypair *Keypair) Private() *PrivateKey {
|
||||
return keypair.private
|
||||
}
|
||||
|
||||
// Representative returns the Elligator representative of the public key
|
||||
// belonging to the Keypair.
|
||||
func (keypair *Keypair) Representative() *Representative {
|
||||
return keypair.representative
|
||||
}
|
||||
|
||||
// HasElligator returns true if the Keypair has an Elligator representative.
|
||||
func (keypair *Keypair) HasElligator() bool {
|
||||
return nil != keypair.representative
|
||||
}
|
||||
|
||||
// NewKeypair generates a new Curve25519 keypair, and optionally also generates
|
||||
// an Elligator representative of the public key.
|
||||
func NewKeypair(elligator bool) (*Keypair, error) {
|
||||
keypair := new(Keypair)
|
||||
keypair.private = new(PrivateKey)
|
||||
keypair.public = new(PublicKey)
|
||||
if elligator {
|
||||
keypair.representative = new(Representative)
|
||||
}
|
||||
|
||||
for {
|
||||
// Generate a Curve25519 private key. Like everyone who does this,
|
||||
// run the CSPRNG output through SHA256 for extra tinfoil hattery.
|
||||
priv := keypair.private.Bytes()[:]
|
||||
if err := csrand.Bytes(priv); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
digest := sha256.Sum256(priv)
|
||||
digest[0] &= 248
|
||||
digest[31] &= 127
|
||||
digest[31] |= 64
|
||||
copy(priv, digest[:])
|
||||
|
||||
if elligator {
|
||||
// Apply the Elligator transform. This fails ~50% of the time.
|
||||
if !extra25519.ScalarBaseMult(keypair.public.Bytes(),
|
||||
keypair.representative.Bytes(),
|
||||
keypair.private.Bytes()) {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
// Generate the corresponding Curve25519 public key.
|
||||
curve25519.ScalarBaseMult(keypair.public.Bytes(),
|
||||
keypair.private.Bytes())
|
||||
}
|
||||
|
||||
return keypair, nil
|
||||
}
|
||||
}
|
||||
|
||||
// KeypairFromHex returns a Keypair from the hexdecimal representation of the
|
||||
// private key.
|
||||
func KeypairFromHex(encoded string) (*Keypair, error) {
|
||||
raw, err := hex.DecodeString(encoded)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(raw) != PrivateKeyLength {
|
||||
return nil, PrivateKeyLengthError(len(raw))
|
||||
}
|
||||
|
||||
keypair := new(Keypair)
|
||||
keypair.private = new(PrivateKey)
|
||||
keypair.public = new(PublicKey)
|
||||
|
||||
copy(keypair.private[:], raw)
|
||||
curve25519.ScalarBaseMult(keypair.public.Bytes(),
|
||||
keypair.private.Bytes())
|
||||
|
||||
return keypair, nil
|
||||
}
|
||||
|
||||
// ServerHandshake does the server side of a ntor handshake and returns status,
|
||||
// KEY_SEED, and AUTH. If status is not true, the handshake MUST be aborted.
|
||||
func ServerHandshake(clientPublic *PublicKey, serverKeypair *Keypair, idKeypair *Keypair, id *NodeID) (ok bool, keySeed *KeySeed, auth *Auth) {
|
||||
var notOk int
|
||||
var secretInput bytes.Buffer
|
||||
|
||||
// Server side uses EXP(X,y) | EXP(X,b)
|
||||
var exp [SharedSecretLength]byte
|
||||
curve25519.ScalarMult(&exp, serverKeypair.private.Bytes(),
|
||||
clientPublic.Bytes())
|
||||
notOk |= constantTimeIsZero(exp[:])
|
||||
secretInput.Write(exp[:])
|
||||
|
||||
curve25519.ScalarMult(&exp, idKeypair.private.Bytes(),
|
||||
clientPublic.Bytes())
|
||||
notOk |= constantTimeIsZero(exp[:])
|
||||
secretInput.Write(exp[:])
|
||||
|
||||
keySeed, auth = ntorCommon(secretInput, id, idKeypair.public,
|
||||
clientPublic, serverKeypair.public)
|
||||
return notOk == 0, keySeed, auth
|
||||
}
|
||||
|
||||
// ClientHandshake does the client side of a ntor handshake and returnes
|
||||
// status, KEY_SEED, and AUTH. If status is not true or AUTH does not match
|
||||
// the value recieved from the server, the handshake MUST be aborted.
|
||||
func ClientHandshake(clientKeypair *Keypair, serverPublic *PublicKey, idPublic *PublicKey, id *NodeID) (ok bool, keySeed *KeySeed, auth *Auth) {
|
||||
var notOk int
|
||||
var secretInput bytes.Buffer
|
||||
|
||||
// Client side uses EXP(Y,x) | EXP(B,x)
|
||||
var exp [SharedSecretLength]byte
|
||||
curve25519.ScalarMult(&exp, clientKeypair.private.Bytes(),
|
||||
serverPublic.Bytes())
|
||||
notOk |= constantTimeIsZero(exp[:])
|
||||
secretInput.Write(exp[:])
|
||||
|
||||
curve25519.ScalarMult(&exp, clientKeypair.private.Bytes(),
|
||||
idPublic.Bytes())
|
||||
notOk |= constantTimeIsZero(exp[:])
|
||||
secretInput.Write(exp[:])
|
||||
|
||||
keySeed, auth = ntorCommon(secretInput, id, idPublic,
|
||||
clientKeypair.public, serverPublic)
|
||||
return notOk == 0, keySeed, auth
|
||||
}
|
||||
|
||||
// CompareAuth does a constant time compare of a Auth and a byte slice
|
||||
// (presumably received over a network).
|
||||
func CompareAuth(auth1 *Auth, auth2 []byte) bool {
|
||||
auth1Bytes := auth1.Bytes()
|
||||
return hmac.Equal(auth1Bytes[:], auth2)
|
||||
}
|
||||
|
||||
func ntorCommon(secretInput bytes.Buffer, id *NodeID, b *PublicKey, x *PublicKey, y *PublicKey) (*KeySeed, *Auth) {
|
||||
keySeed := new(KeySeed)
|
||||
auth := new(Auth)
|
||||
|
||||
// secret_input/auth_input use this common bit, build it once.
|
||||
suffix := bytes.NewBuffer(b.Bytes()[:])
|
||||
suffix.Write(b.Bytes()[:])
|
||||
suffix.Write(x.Bytes()[:])
|
||||
suffix.Write(y.Bytes()[:])
|
||||
suffix.Write(protoID)
|
||||
suffix.Write(id[:])
|
||||
|
||||
// At this point secret_input has the 2 exponents, concatenated, append the
|
||||
// client/server common suffix.
|
||||
secretInput.Write(suffix.Bytes())
|
||||
|
||||
// KEY_SEED = H(secret_input, t_key)
|
||||
h := hmac.New(sha256.New, tKey)
|
||||
h.Write(secretInput.Bytes())
|
||||
tmp := h.Sum(nil)
|
||||
copy(keySeed[:], tmp)
|
||||
|
||||
// verify = H(secret_input, t_verify)
|
||||
h = hmac.New(sha256.New, tVerify)
|
||||
h.Write(secretInput.Bytes())
|
||||
verify := h.Sum(nil)
|
||||
|
||||
// auth_input = verify | ID | B | Y | X | PROTOID | "Server"
|
||||
authInput := bytes.NewBuffer(verify)
|
||||
authInput.Write(suffix.Bytes())
|
||||
authInput.Write([]byte("Server"))
|
||||
h = hmac.New(sha256.New, tMac)
|
||||
h.Write(authInput.Bytes())
|
||||
tmp = h.Sum(nil)
|
||||
copy(auth[:], tmp)
|
||||
|
||||
return keySeed, auth
|
||||
}
|
||||
|
||||
func constantTimeIsZero(x []byte) int {
|
||||
var ret byte
|
||||
for _, v := range x {
|
||||
ret |= v
|
||||
}
|
||||
|
||||
return subtle.ConstantTimeByteEq(ret, 0)
|
||||
}
|
||||
|
||||
// Kdf extracts and expands KEY_SEED via HKDF-SHA256 and returns `okm_len` bytes
|
||||
// of key material.
|
||||
func Kdf(keySeed []byte, okmLen int) []byte {
|
||||
kdf := hkdf.New(sha256.New, keySeed, tKey, mExpand)
|
||||
okm := make([]byte, okmLen)
|
||||
n, err := io.ReadFull(kdf, okm)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("BUG: Failed HKDF: %s", err.Error()))
|
||||
} else if n != len(okm) {
|
||||
panic(fmt.Sprintf("BUG: Got truncated HKDF output: %d", n))
|
||||
}
|
||||
|
||||
return okm
|
||||
}
|
245
vendor/git.torproject.org/pluggable-transports/obfs4.git/common/probdist/weighted_dist.go
generated
vendored
Normal file
245
vendor/git.torproject.org/pluggable-transports/obfs4.git/common/probdist/weighted_dist.go
generated
vendored
Normal file
@ -0,0 +1,245 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Yawning Angel <yawning at torproject dot org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
// Package probdist implements a weighted probability distribution suitable for
|
||||
// protocol parameterization. To allow for easy reproduction of a given
|
||||
// distribution, the drbg package is used as the random number source.
|
||||
package probdist
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"container/list"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"sync"
|
||||
|
||||
"git.torproject.org/pluggable-transports/obfs4.git/common/csrand"
|
||||
"git.torproject.org/pluggable-transports/obfs4.git/common/drbg"
|
||||
)
|
||||
|
||||
const (
|
||||
minValues = 1
|
||||
maxValues = 100
|
||||
)
|
||||
|
||||
// WeightedDist is a weighted distribution.
|
||||
type WeightedDist struct {
|
||||
sync.Mutex
|
||||
|
||||
minValue int
|
||||
maxValue int
|
||||
biased bool
|
||||
values []int
|
||||
weights []float64
|
||||
|
||||
alias []int
|
||||
prob []float64
|
||||
}
|
||||
|
||||
// New creates a weighted distribution of values ranging from min to max
|
||||
// based on a HashDrbg initialized with seed. Optionally, bias the weight
|
||||
// generation to match the ScrambleSuit non-uniform distribution from
|
||||
// obfsproxy.
|
||||
func New(seed *drbg.Seed, min, max int, biased bool) (w *WeightedDist) {
|
||||
w = &WeightedDist{minValue: min, maxValue: max, biased: biased}
|
||||
|
||||
if max <= min {
|
||||
panic(fmt.Sprintf("wDist.Reset(): min >= max (%d, %d)", min, max))
|
||||
}
|
||||
|
||||
w.Reset(seed)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// genValues creates a slice containing a random number of random values
|
||||
// that when scaled by adding minValue will fall into [min, max].
|
||||
func (w *WeightedDist) genValues(rng *rand.Rand) {
|
||||
nValues := (w.maxValue + 1) - w.minValue
|
||||
values := rng.Perm(nValues)
|
||||
if nValues < minValues {
|
||||
nValues = minValues
|
||||
}
|
||||
if nValues > maxValues {
|
||||
nValues = maxValues
|
||||
}
|
||||
nValues = rng.Intn(nValues) + 1
|
||||
w.values = values[:nValues]
|
||||
}
|
||||
|
||||
// genBiasedWeights generates a non-uniform weight list, similar to the
|
||||
// ScrambleSuit prob_dist module.
|
||||
func (w *WeightedDist) genBiasedWeights(rng *rand.Rand) {
|
||||
w.weights = make([]float64, len(w.values))
|
||||
|
||||
culmProb := 0.0
|
||||
for i := range w.weights {
|
||||
p := (1.0 - culmProb) * rng.Float64()
|
||||
w.weights[i] = p
|
||||
culmProb += p
|
||||
}
|
||||
}
|
||||
|
||||
// genUniformWeights generates a uniform weight list.
|
||||
func (w *WeightedDist) genUniformWeights(rng *rand.Rand) {
|
||||
w.weights = make([]float64, len(w.values))
|
||||
for i := range w.weights {
|
||||
w.weights[i] = rng.Float64()
|
||||
}
|
||||
}
|
||||
|
||||
// genTables calculates the alias and prob tables used for Vose's Alias method.
|
||||
// Algorithm taken from http://www.keithschwarz.com/darts-dice-coins/
|
||||
func (w *WeightedDist) genTables() {
|
||||
n := len(w.weights)
|
||||
var sum float64
|
||||
for _, weight := range w.weights {
|
||||
sum += weight
|
||||
}
|
||||
|
||||
// Create arrays $Alias$ and $Prob$, each of size $n$.
|
||||
alias := make([]int, n)
|
||||
prob := make([]float64, n)
|
||||
|
||||
// Create two worklists, $Small$ and $Large$.
|
||||
small := list.New()
|
||||
large := list.New()
|
||||
|
||||
scaled := make([]float64, n)
|
||||
for i, weight := range w.weights {
|
||||
// Multiply each probability by $n$.
|
||||
p_i := weight * float64(n) / sum
|
||||
scaled[i] = p_i
|
||||
|
||||
// For each scaled probability $p_i$:
|
||||
if scaled[i] < 1.0 {
|
||||
// If $p_i < 1$, add $i$ to $Small$.
|
||||
small.PushBack(i)
|
||||
} else {
|
||||
// Otherwise ($p_i \ge 1$), add $i$ to $Large$.
|
||||
large.PushBack(i)
|
||||
}
|
||||
}
|
||||
|
||||
// While $Small$ and $Large$ are not empty: ($Large$ might be emptied first)
|
||||
for small.Len() > 0 && large.Len() > 0 {
|
||||
// Remove the first element from $Small$; call it $l$.
|
||||
l := small.Remove(small.Front()).(int)
|
||||
// Remove the first element from $Large$; call it $g$.
|
||||
g := large.Remove(large.Front()).(int)
|
||||
|
||||
// Set $Prob[l] = p_l$.
|
||||
prob[l] = scaled[l]
|
||||
// Set $Alias[l] = g$.
|
||||
alias[l] = g
|
||||
|
||||
// Set $p_g := (p_g + p_l) - 1$. (This is a more numerically stable option.)
|
||||
scaled[g] = (scaled[g] + scaled[l]) - 1.0
|
||||
|
||||
if scaled[g] < 1.0 {
|
||||
// If $p_g < 1$, add $g$ to $Small$.
|
||||
small.PushBack(g)
|
||||
} else {
|
||||
// Otherwise ($p_g \ge 1$), add $g$ to $Large$.
|
||||
large.PushBack(g)
|
||||
}
|
||||
}
|
||||
|
||||
// While $Large$ is not empty:
|
||||
for large.Len() > 0 {
|
||||
// Remove the first element from $Large$; call it $g$.
|
||||
g := large.Remove(large.Front()).(int)
|
||||
// Set $Prob[g] = 1$.
|
||||
prob[g] = 1.0
|
||||
}
|
||||
|
||||
// While $Small$ is not empty: This is only possible due to numerical instability.
|
||||
for small.Len() > 0 {
|
||||
// Remove the first element from $Small$; call it $l$.
|
||||
l := small.Remove(small.Front()).(int)
|
||||
// Set $Prob[l] = 1$.
|
||||
prob[l] = 1.0
|
||||
}
|
||||
|
||||
w.prob = prob
|
||||
w.alias = alias
|
||||
}
|
||||
|
||||
// Reset generates a new distribution with the same min/max based on a new
|
||||
// seed.
|
||||
func (w *WeightedDist) Reset(seed *drbg.Seed) {
|
||||
// Initialize the deterministic random number generator.
|
||||
drbg, _ := drbg.NewHashDrbg(seed)
|
||||
rng := rand.New(drbg)
|
||||
|
||||
w.Lock()
|
||||
defer w.Unlock()
|
||||
|
||||
w.genValues(rng)
|
||||
if w.biased {
|
||||
w.genBiasedWeights(rng)
|
||||
} else {
|
||||
w.genUniformWeights(rng)
|
||||
}
|
||||
w.genTables()
|
||||
}
|
||||
|
||||
// Sample generates a random value according to the distribution.
|
||||
func (w *WeightedDist) Sample() int {
|
||||
var idx int
|
||||
|
||||
w.Lock()
|
||||
defer w.Unlock()
|
||||
|
||||
// Generate a fair die roll from an $n$-sided die; call the side $i$.
|
||||
i := csrand.Intn(len(w.values))
|
||||
// Flip a biased coin that comes up heads with probability $Prob[i]$.
|
||||
if csrand.Float64() <= w.prob[i] {
|
||||
// If the coin comes up "heads," return $i$.
|
||||
idx = i
|
||||
} else {
|
||||
// Otherwise, return $Alias[i]$.
|
||||
idx = w.alias[i]
|
||||
}
|
||||
|
||||
return w.minValue + w.values[idx]
|
||||
}
|
||||
|
||||
// String returns a dump of the distribution table.
|
||||
func (w *WeightedDist) String() string {
|
||||
var buf bytes.Buffer
|
||||
|
||||
buf.WriteString("[ ")
|
||||
for i, v := range w.values {
|
||||
p := w.weights[i]
|
||||
if p > 0.01 { // Squelch tiny probabilities.
|
||||
buf.WriteString(fmt.Sprintf("%d: %f ", v, p))
|
||||
}
|
||||
}
|
||||
buf.WriteString("]")
|
||||
return buf.String()
|
||||
}
|
147
vendor/git.torproject.org/pluggable-transports/obfs4.git/common/replayfilter/replay_filter.go
generated
vendored
Normal file
147
vendor/git.torproject.org/pluggable-transports/obfs4.git/common/replayfilter/replay_filter.go
generated
vendored
Normal file
@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Yawning Angel <yawning at torproject dot org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
// Package replayfilter implements a generic replay detection filter with a
|
||||
// caller specifiable time-to-live. It only detects if a given byte sequence
|
||||
// has been seen before based on the SipHash-2-4 digest of the sequence.
|
||||
// Collisions are treated as positive matches, though the probability of this
|
||||
// happening is negligible.
|
||||
package replayfilter
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"encoding/binary"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/dchest/siphash"
|
||||
|
||||
"git.torproject.org/pluggable-transports/obfs4.git/common/csrand"
|
||||
)
|
||||
|
||||
// maxFilterSize is the maximum capacity of a replay filter. This value is
|
||||
// more as a safeguard to prevent runaway filter growth, and is sized to be
|
||||
// serveral orders of magnitude greater than the number of connections a busy
|
||||
// bridge sees in one day, so in practice should never be reached.
|
||||
const maxFilterSize = 100 * 1024
|
||||
|
||||
type entry struct {
|
||||
digest uint64
|
||||
firstSeen time.Time
|
||||
element *list.Element
|
||||
}
|
||||
|
||||
// ReplayFilter is a simple filter designed only to detect if a given byte
|
||||
// sequence has been seen before.
|
||||
type ReplayFilter struct {
|
||||
sync.Mutex
|
||||
|
||||
filter map[uint64]*entry
|
||||
fifo *list.List
|
||||
|
||||
key [2]uint64
|
||||
ttl time.Duration
|
||||
}
|
||||
|
||||
// New creates a new ReplayFilter instance.
|
||||
func New(ttl time.Duration) (filter *ReplayFilter, err error) {
|
||||
// Initialize the SipHash-2-4 instance with a random key.
|
||||
var key [16]byte
|
||||
if err = csrand.Bytes(key[:]); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
filter = new(ReplayFilter)
|
||||
filter.filter = make(map[uint64]*entry)
|
||||
filter.fifo = list.New()
|
||||
filter.key[0] = binary.BigEndian.Uint64(key[0:8])
|
||||
filter.key[1] = binary.BigEndian.Uint64(key[8:16])
|
||||
filter.ttl = ttl
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// TestAndSet queries the filter for a given byte sequence, inserts the
|
||||
// sequence, and returns if it was present before the insertion operation.
|
||||
func (f *ReplayFilter) TestAndSet(now time.Time, buf []byte) bool {
|
||||
digest := siphash.Hash(f.key[0], f.key[1], buf)
|
||||
|
||||
f.Lock()
|
||||
defer f.Unlock()
|
||||
|
||||
f.compactFilter(now)
|
||||
|
||||
if e := f.filter[digest]; e != nil {
|
||||
// Hit. Just return.
|
||||
return true
|
||||
}
|
||||
|
||||
// Miss. Add a new entry.
|
||||
e := new(entry)
|
||||
e.digest = digest
|
||||
e.firstSeen = now
|
||||
e.element = f.fifo.PushBack(e)
|
||||
f.filter[digest] = e
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (f *ReplayFilter) compactFilter(now time.Time) {
|
||||
e := f.fifo.Front()
|
||||
for e != nil {
|
||||
ent, _ := e.Value.(*entry)
|
||||
|
||||
// If the filter is not full, only purge entries that exceed the TTL,
|
||||
// otherwise purge at least one entry, then revert to TTL based
|
||||
// compaction.
|
||||
if f.fifo.Len() < maxFilterSize && f.ttl > 0 {
|
||||
deltaT := now.Sub(ent.firstSeen)
|
||||
if deltaT < 0 {
|
||||
// Aeeeeeee, the system time jumped backwards, potentially by
|
||||
// a lot. This will eventually self-correct, but "eventually"
|
||||
// could be a long time. As much as this sucks, jettison the
|
||||
// entire filter.
|
||||
f.reset()
|
||||
return
|
||||
} else if deltaT < f.ttl {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the eldest entry.
|
||||
eNext := e.Next()
|
||||
delete(f.filter, ent.digest)
|
||||
f.fifo.Remove(ent.element)
|
||||
ent.element = nil
|
||||
e = eNext
|
||||
}
|
||||
}
|
||||
|
||||
func (f *ReplayFilter) reset() {
|
||||
f.filter = make(map[uint64]*entry)
|
||||
f.fifo = list.New()
|
||||
}
|
90
vendor/git.torproject.org/pluggable-transports/obfs4.git/transports/base/base.go
generated
vendored
Normal file
90
vendor/git.torproject.org/pluggable-transports/obfs4.git/transports/base/base.go
generated
vendored
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Yawning Angel <yawning at torproject dot org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
// Package base provides the common interface that each supported transport
|
||||
// protocol must implement.
|
||||
package base
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"git.torproject.org/pluggable-transports/goptlib.git"
|
||||
)
|
||||
|
||||
type DialFunc func(string, string) (net.Conn, error)
|
||||
|
||||
// ClientFactory is the interface that defines the factory for creating
|
||||
// pluggable transport protocol client instances.
|
||||
type ClientFactory interface {
|
||||
// Transport returns the Transport instance that this ClientFactory belongs
|
||||
// to.
|
||||
Transport() Transport
|
||||
|
||||
// ParseArgs parses the supplied arguments into an internal representation
|
||||
// for use with WrapConn. This routine is called before the outgoing
|
||||
// TCP/IP connection is created to allow doing things (like keypair
|
||||
// generation) to be hidden from third parties.
|
||||
ParseArgs(args *pt.Args) (interface{}, error)
|
||||
|
||||
// Dial creates an outbound net.Conn, and does whatever is required
|
||||
// (eg: handshaking) to get the connection to the point where it is
|
||||
// ready to relay data.
|
||||
Dial(network, address string, dialFn DialFunc, args interface{}) (net.Conn, error)
|
||||
}
|
||||
|
||||
// ServerFactory is the interface that defines the factory for creating
|
||||
// plugable transport protocol server instances. As the arguments are the
|
||||
// property of the factory, validation is done at factory creation time.
|
||||
type ServerFactory interface {
|
||||
// Transport returns the Transport instance that this ServerFactory belongs
|
||||
// to.
|
||||
Transport() Transport
|
||||
|
||||
// Args returns the Args required on the client side to handshake with
|
||||
// server connections created by this factory.
|
||||
Args() *pt.Args
|
||||
|
||||
// WrapConn wraps the provided net.Conn with a transport protocol
|
||||
// implementation, and does whatever is required (eg: handshaking) to get
|
||||
// the connection to a point where it is ready to relay data.
|
||||
WrapConn(conn net.Conn) (net.Conn, error)
|
||||
}
|
||||
|
||||
// Transport is an interface that defines a pluggable transport protocol.
|
||||
type Transport interface {
|
||||
// Name returns the name of the transport protocol. It MUST be a valid C
|
||||
// identifier.
|
||||
Name() string
|
||||
|
||||
// ClientFactory returns a ClientFactory instance for this transport
|
||||
// protocol.
|
||||
ClientFactory(stateDir string) (ClientFactory, error)
|
||||
|
||||
// ServerFactory returns a ServerFactory instance for this transport
|
||||
// protocol. This can fail if the provided arguments are invalid.
|
||||
ServerFactory(stateDir string, args *pt.Args) (ServerFactory, error)
|
||||
}
|
306
vendor/git.torproject.org/pluggable-transports/obfs4.git/transports/obfs4/framing/framing.go
generated
vendored
Normal file
306
vendor/git.torproject.org/pluggable-transports/obfs4.git/transports/obfs4/framing/framing.go
generated
vendored
Normal file
@ -0,0 +1,306 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Yawning Angel <yawning at torproject dot org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
//
|
||||
// Package framing implements the obfs4 link framing and cryptography.
|
||||
//
|
||||
// The Encoder/Decoder shared secret format is:
|
||||
// uint8_t[32] NaCl secretbox key
|
||||
// uint8_t[16] NaCl Nonce prefix
|
||||
// uint8_t[16] SipHash-2-4 key (used to obfsucate length)
|
||||
// uint8_t[8] SipHash-2-4 IV
|
||||
//
|
||||
// The frame format is:
|
||||
// uint16_t length (obfsucated, big endian)
|
||||
// NaCl secretbox (Poly1305/XSalsa20) containing:
|
||||
// uint8_t[16] tag (Part of the secretbox construct)
|
||||
// uint8_t[] payload
|
||||
//
|
||||
// The length field is length of the NaCl secretbox XORed with the truncated
|
||||
// SipHash-2-4 digest ran in OFB mode.
|
||||
//
|
||||
// Initialize K, IV[0] with values from the shared secret.
|
||||
// On each packet, IV[n] = H(K, IV[n - 1])
|
||||
// mask[n] = IV[n][0:2]
|
||||
// obfsLen = length ^ mask[n]
|
||||
//
|
||||
// The NaCl secretbox (Poly1305/XSalsa20) nonce format is:
|
||||
// uint8_t[24] prefix (Fixed)
|
||||
// uint64_t counter (Big endian)
|
||||
//
|
||||
// The counter is initialized to 1, and is incremented on each frame. Since
|
||||
// the protocol is designed to be used over a reliable medium, the nonce is not
|
||||
// transmitted over the wire as both sides of the conversation know the prefix
|
||||
// and the initial counter value. It is imperative that the counter does not
|
||||
// wrap, and sessions MUST terminate before 2^64 frames are sent.
|
||||
//
|
||||
package framing
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"golang.org/x/crypto/nacl/secretbox"
|
||||
|
||||
"git.torproject.org/pluggable-transports/obfs4.git/common/csrand"
|
||||
"git.torproject.org/pluggable-transports/obfs4.git/common/drbg"
|
||||
)
|
||||
|
||||
const (
|
||||
// MaximumSegmentLength is the length of the largest possible segment
|
||||
// including overhead.
|
||||
MaximumSegmentLength = 1500 - (40 + 12)
|
||||
|
||||
// FrameOverhead is the length of the framing overhead.
|
||||
FrameOverhead = lengthLength + secretbox.Overhead
|
||||
|
||||
// MaximumFramePayloadLength is the length of the maximum allowed payload
|
||||
// per frame.
|
||||
MaximumFramePayloadLength = MaximumSegmentLength - FrameOverhead
|
||||
|
||||
// KeyLength is the length of the Encoder/Decoder secret key.
|
||||
KeyLength = keyLength + noncePrefixLength + drbg.SeedLength
|
||||
|
||||
maxFrameLength = MaximumSegmentLength - lengthLength
|
||||
minFrameLength = FrameOverhead - lengthLength
|
||||
|
||||
keyLength = 32
|
||||
|
||||
noncePrefixLength = 16
|
||||
nonceCounterLength = 8
|
||||
nonceLength = noncePrefixLength + nonceCounterLength
|
||||
|
||||
lengthLength = 2
|
||||
)
|
||||
|
||||
// Error returned when Decoder.Decode() requires more data to continue.
|
||||
var ErrAgain = errors.New("framing: More data needed to decode")
|
||||
|
||||
// Error returned when Decoder.Decode() failes to authenticate a frame.
|
||||
var ErrTagMismatch = errors.New("framing: Poly1305 tag mismatch")
|
||||
|
||||
// Error returned when the NaCl secretbox nonce's counter wraps (FATAL).
|
||||
var ErrNonceCounterWrapped = errors.New("framing: Nonce counter wrapped")
|
||||
|
||||
// InvalidPayloadLengthError is the error returned when Encoder.Encode()
|
||||
// rejects the payload length.
|
||||
type InvalidPayloadLengthError int
|
||||
|
||||
func (e InvalidPayloadLengthError) Error() string {
|
||||
return fmt.Sprintf("framing: Invalid payload length: %d", int(e))
|
||||
}
|
||||
|
||||
type boxNonce struct {
|
||||
prefix [noncePrefixLength]byte
|
||||
counter uint64
|
||||
}
|
||||
|
||||
func (nonce *boxNonce) init(prefix []byte) {
|
||||
if noncePrefixLength != len(prefix) {
|
||||
panic(fmt.Sprintf("BUG: Nonce prefix length invalid: %d", len(prefix)))
|
||||
}
|
||||
|
||||
copy(nonce.prefix[:], prefix)
|
||||
nonce.counter = 1
|
||||
}
|
||||
|
||||
func (nonce boxNonce) bytes(out *[nonceLength]byte) error {
|
||||
// The security guarantee of Poly1305 is broken if a nonce is ever reused
|
||||
// for a given key. Detect this by checking for counter wraparound since
|
||||
// we start each counter at 1. If it ever happens that more than 2^64 - 1
|
||||
// frames are transmitted over a given connection, support for rekeying
|
||||
// will be neccecary, but that's unlikely to happen.
|
||||
if nonce.counter == 0 {
|
||||
return ErrNonceCounterWrapped
|
||||
}
|
||||
|
||||
copy(out[:], nonce.prefix[:])
|
||||
binary.BigEndian.PutUint64(out[noncePrefixLength:], nonce.counter)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encoder is a frame encoder instance.
|
||||
type Encoder struct {
|
||||
key [keyLength]byte
|
||||
nonce boxNonce
|
||||
drbg *drbg.HashDrbg
|
||||
}
|
||||
|
||||
// NewEncoder creates a new Encoder instance. It must be supplied a slice
|
||||
// containing exactly KeyLength bytes of keying material.
|
||||
func NewEncoder(key []byte) *Encoder {
|
||||
if len(key) != KeyLength {
|
||||
panic(fmt.Sprintf("BUG: Invalid encoder key length: %d", len(key)))
|
||||
}
|
||||
|
||||
encoder := new(Encoder)
|
||||
copy(encoder.key[:], key[0:keyLength])
|
||||
encoder.nonce.init(key[keyLength : keyLength+noncePrefixLength])
|
||||
seed, err := drbg.SeedFromBytes(key[keyLength+noncePrefixLength:])
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("BUG: Failed to initialize DRBG: %s", err))
|
||||
}
|
||||
encoder.drbg, _ = drbg.NewHashDrbg(seed)
|
||||
|
||||
return encoder
|
||||
}
|
||||
|
||||
// Encode encodes a single frame worth of payload and returns the encoded
|
||||
// length. InvalidPayloadLengthError is recoverable, all other errors MUST be
|
||||
// treated as fatal and the session aborted.
|
||||
func (encoder *Encoder) Encode(frame, payload []byte) (n int, err error) {
|
||||
payloadLen := len(payload)
|
||||
if MaximumFramePayloadLength < payloadLen {
|
||||
return 0, InvalidPayloadLengthError(payloadLen)
|
||||
}
|
||||
if len(frame) < payloadLen+FrameOverhead {
|
||||
return 0, io.ErrShortBuffer
|
||||
}
|
||||
|
||||
// Generate a new nonce.
|
||||
var nonce [nonceLength]byte
|
||||
if err = encoder.nonce.bytes(&nonce); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
encoder.nonce.counter++
|
||||
|
||||
// Encrypt and MAC payload.
|
||||
box := secretbox.Seal(frame[:lengthLength], payload, &nonce, &encoder.key)
|
||||
|
||||
// Obfuscate the length.
|
||||
length := uint16(len(box) - lengthLength)
|
||||
lengthMask := encoder.drbg.NextBlock()
|
||||
length ^= binary.BigEndian.Uint16(lengthMask)
|
||||
binary.BigEndian.PutUint16(frame[:2], length)
|
||||
|
||||
// Return the frame.
|
||||
return len(box), nil
|
||||
}
|
||||
|
||||
// Decoder is a frame decoder instance.
|
||||
type Decoder struct {
|
||||
key [keyLength]byte
|
||||
nonce boxNonce
|
||||
drbg *drbg.HashDrbg
|
||||
|
||||
nextNonce [nonceLength]byte
|
||||
nextLength uint16
|
||||
nextLengthInvalid bool
|
||||
}
|
||||
|
||||
// NewDecoder creates a new Decoder instance. It must be supplied a slice
|
||||
// containing exactly KeyLength bytes of keying material.
|
||||
func NewDecoder(key []byte) *Decoder {
|
||||
if len(key) != KeyLength {
|
||||
panic(fmt.Sprintf("BUG: Invalid decoder key length: %d", len(key)))
|
||||
}
|
||||
|
||||
decoder := new(Decoder)
|
||||
copy(decoder.key[:], key[0:keyLength])
|
||||
decoder.nonce.init(key[keyLength : keyLength+noncePrefixLength])
|
||||
seed, err := drbg.SeedFromBytes(key[keyLength+noncePrefixLength:])
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("BUG: Failed to initialize DRBG: %s", err))
|
||||
}
|
||||
decoder.drbg, _ = drbg.NewHashDrbg(seed)
|
||||
|
||||
return decoder
|
||||
}
|
||||
|
||||
// Decode decodes a stream of data and returns the length if any. ErrAgain is
|
||||
// a temporary failure, all other errors MUST be treated as fatal and the
|
||||
// session aborted.
|
||||
func (decoder *Decoder) Decode(data []byte, frames *bytes.Buffer) (int, error) {
|
||||
// A length of 0 indicates that we do not know how big the next frame is
|
||||
// going to be.
|
||||
if decoder.nextLength == 0 {
|
||||
// Attempt to pull out the next frame length.
|
||||
if lengthLength > frames.Len() {
|
||||
return 0, ErrAgain
|
||||
}
|
||||
|
||||
// Remove the length field from the buffer.
|
||||
var obfsLen [lengthLength]byte
|
||||
_, err := io.ReadFull(frames, obfsLen[:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Derive the nonce the peer used.
|
||||
if err = decoder.nonce.bytes(&decoder.nextNonce); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Deobfuscate the length field.
|
||||
length := binary.BigEndian.Uint16(obfsLen[:])
|
||||
lengthMask := decoder.drbg.NextBlock()
|
||||
length ^= binary.BigEndian.Uint16(lengthMask)
|
||||
if maxFrameLength < length || minFrameLength > length {
|
||||
// Per "Plaintext Recovery Attacks Against SSH" by
|
||||
// Martin R. Albrecht, Kenneth G. Paterson and Gaven J. Watson,
|
||||
// there are a class of attacks againt protocols that use similar
|
||||
// sorts of framing schemes.
|
||||
//
|
||||
// While obfs4 should not allow plaintext recovery (CBC mode is
|
||||
// not used), attempt to mitigate out of bound frame length errors
|
||||
// by pretending that the length was a random valid range as per
|
||||
// the countermeasure suggested by Denis Bider in section 6 of the
|
||||
// paper.
|
||||
|
||||
decoder.nextLengthInvalid = true
|
||||
length = uint16(csrand.IntRange(minFrameLength, maxFrameLength))
|
||||
}
|
||||
decoder.nextLength = length
|
||||
}
|
||||
|
||||
if int(decoder.nextLength) > frames.Len() {
|
||||
return 0, ErrAgain
|
||||
}
|
||||
|
||||
// Unseal the frame.
|
||||
var box [maxFrameLength]byte
|
||||
n, err := io.ReadFull(frames, box[:decoder.nextLength])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
out, ok := secretbox.Open(data[:0], box[:n], &decoder.nextNonce, &decoder.key)
|
||||
if !ok || decoder.nextLengthInvalid {
|
||||
// When a random length is used (on length error) the tag should always
|
||||
// mismatch, but be paranoid.
|
||||
return 0, ErrTagMismatch
|
||||
}
|
||||
|
||||
// Clean up and prepare for the next frame.
|
||||
decoder.nextLength = 0
|
||||
decoder.nonce.counter++
|
||||
|
||||
return len(out), nil
|
||||
}
|
424
vendor/git.torproject.org/pluggable-transports/obfs4.git/transports/obfs4/handshake_ntor.go
generated
vendored
Normal file
424
vendor/git.torproject.org/pluggable-transports/obfs4.git/transports/obfs4/handshake_ntor.go
generated
vendored
Normal file
@ -0,0 +1,424 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Yawning Angel <yawning at torproject dot org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package obfs4
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"git.torproject.org/pluggable-transports/obfs4.git/common/csrand"
|
||||
"git.torproject.org/pluggable-transports/obfs4.git/common/ntor"
|
||||
"git.torproject.org/pluggable-transports/obfs4.git/common/replayfilter"
|
||||
"git.torproject.org/pluggable-transports/obfs4.git/transports/obfs4/framing"
|
||||
)
|
||||
|
||||
const (
|
||||
maxHandshakeLength = 8192
|
||||
|
||||
clientMinPadLength = (serverMinHandshakeLength + inlineSeedFrameLength) -
|
||||
clientMinHandshakeLength
|
||||
clientMaxPadLength = maxHandshakeLength - clientMinHandshakeLength
|
||||
clientMinHandshakeLength = ntor.RepresentativeLength + markLength + macLength
|
||||
|
||||
serverMinPadLength = 0
|
||||
serverMaxPadLength = maxHandshakeLength - (serverMinHandshakeLength +
|
||||
inlineSeedFrameLength)
|
||||
serverMinHandshakeLength = ntor.RepresentativeLength + ntor.AuthLength +
|
||||
markLength + macLength
|
||||
|
||||
markLength = sha256.Size / 2
|
||||
macLength = sha256.Size / 2
|
||||
|
||||
inlineSeedFrameLength = framing.FrameOverhead + packetOverhead + seedPacketPayloadLength
|
||||
)
|
||||
|
||||
// ErrMarkNotFoundYet is the error returned when the obfs4 handshake is
|
||||
// incomplete and requires more data to continue. This error is non-fatal and
|
||||
// is the equivalent to EAGAIN/EWOULDBLOCK.
|
||||
var ErrMarkNotFoundYet = errors.New("handshake: M_[C,S] not found yet")
|
||||
|
||||
// ErrInvalidHandshake is the error returned when the obfs4 handshake fails due
|
||||
// to the peer not sending the correct mark. This error is fatal and the
|
||||
// connection MUST be dropped.
|
||||
var ErrInvalidHandshake = errors.New("handshake: Failed to find M_[C,S]")
|
||||
|
||||
// ErrReplayedHandshake is the error returned when the obfs4 handshake fails
|
||||
// due it being replayed. This error is fatal and the connection MUST be
|
||||
// dropped.
|
||||
var ErrReplayedHandshake = errors.New("handshake: Replay detected")
|
||||
|
||||
// ErrNtorFailed is the error returned when the ntor handshake fails. This
|
||||
// error is fatal and the connection MUST be dropped.
|
||||
var ErrNtorFailed = errors.New("handshake: ntor handshake failure")
|
||||
|
||||
// InvalidMacError is the error returned when the handshake MACs do not match.
|
||||
// This error is fatal and the connection MUST be dropped.
|
||||
type InvalidMacError struct {
|
||||
Derived []byte
|
||||
Received []byte
|
||||
}
|
||||
|
||||
func (e *InvalidMacError) Error() string {
|
||||
return fmt.Sprintf("handshake: MAC mismatch: Dervied: %s Received: %s.",
|
||||
hex.EncodeToString(e.Derived), hex.EncodeToString(e.Received))
|
||||
}
|
||||
|
||||
// InvalidAuthError is the error returned when the ntor AUTH tags do not match.
|
||||
// This error is fatal and the connection MUST be dropped.
|
||||
type InvalidAuthError struct {
|
||||
Derived *ntor.Auth
|
||||
Received *ntor.Auth
|
||||
}
|
||||
|
||||
func (e *InvalidAuthError) Error() string {
|
||||
return fmt.Sprintf("handshake: ntor AUTH mismatch: Derived: %s Received:%s.",
|
||||
hex.EncodeToString(e.Derived.Bytes()[:]),
|
||||
hex.EncodeToString(e.Received.Bytes()[:]))
|
||||
}
|
||||
|
||||
type clientHandshake struct {
|
||||
keypair *ntor.Keypair
|
||||
nodeID *ntor.NodeID
|
||||
serverIdentity *ntor.PublicKey
|
||||
epochHour []byte
|
||||
|
||||
padLen int
|
||||
mac hash.Hash
|
||||
|
||||
serverRepresentative *ntor.Representative
|
||||
serverAuth *ntor.Auth
|
||||
serverMark []byte
|
||||
}
|
||||
|
||||
func newClientHandshake(nodeID *ntor.NodeID, serverIdentity *ntor.PublicKey, sessionKey *ntor.Keypair) *clientHandshake {
|
||||
hs := new(clientHandshake)
|
||||
hs.keypair = sessionKey
|
||||
hs.nodeID = nodeID
|
||||
hs.serverIdentity = serverIdentity
|
||||
hs.padLen = csrand.IntRange(clientMinPadLength, clientMaxPadLength)
|
||||
hs.mac = hmac.New(sha256.New, append(hs.serverIdentity.Bytes()[:], hs.nodeID.Bytes()[:]...))
|
||||
|
||||
return hs
|
||||
}
|
||||
|
||||
func (hs *clientHandshake) generateHandshake() ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
|
||||
hs.mac.Reset()
|
||||
hs.mac.Write(hs.keypair.Representative().Bytes()[:])
|
||||
mark := hs.mac.Sum(nil)[:markLength]
|
||||
|
||||
// The client handshake is X | P_C | M_C | MAC(X | P_C | M_C | E) where:
|
||||
// * X is the client's ephemeral Curve25519 public key representative.
|
||||
// * P_C is [clientMinPadLength,clientMaxPadLength] bytes of random padding.
|
||||
// * M_C is HMAC-SHA256-128(serverIdentity | NodeID, X)
|
||||
// * MAC is HMAC-SHA256-128(serverIdentity | NodeID, X .... E)
|
||||
// * E is the string representation of the number of hours since the UNIX
|
||||
// epoch.
|
||||
|
||||
// Generate the padding
|
||||
pad, err := makePad(hs.padLen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Write X, P_C, M_C.
|
||||
buf.Write(hs.keypair.Representative().Bytes()[:])
|
||||
buf.Write(pad)
|
||||
buf.Write(mark)
|
||||
|
||||
// Calculate and write the MAC.
|
||||
hs.mac.Reset()
|
||||
hs.mac.Write(buf.Bytes())
|
||||
hs.epochHour = []byte(strconv.FormatInt(getEpochHour(), 10))
|
||||
hs.mac.Write(hs.epochHour)
|
||||
buf.Write(hs.mac.Sum(nil)[:macLength])
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (hs *clientHandshake) parseServerHandshake(resp []byte) (int, []byte, error) {
|
||||
// No point in examining the data unless the miminum plausible response has
|
||||
// been received.
|
||||
if serverMinHandshakeLength > len(resp) {
|
||||
return 0, nil, ErrMarkNotFoundYet
|
||||
}
|
||||
|
||||
if hs.serverRepresentative == nil || hs.serverAuth == nil {
|
||||
// Pull out the representative/AUTH. (XXX: Add ctors to ntor)
|
||||
hs.serverRepresentative = new(ntor.Representative)
|
||||
copy(hs.serverRepresentative.Bytes()[:], resp[0:ntor.RepresentativeLength])
|
||||
hs.serverAuth = new(ntor.Auth)
|
||||
copy(hs.serverAuth.Bytes()[:], resp[ntor.RepresentativeLength:])
|
||||
|
||||
// Derive the mark.
|
||||
hs.mac.Reset()
|
||||
hs.mac.Write(hs.serverRepresentative.Bytes()[:])
|
||||
hs.serverMark = hs.mac.Sum(nil)[:markLength]
|
||||
}
|
||||
|
||||
// Attempt to find the mark + MAC.
|
||||
pos := findMarkMac(hs.serverMark, resp, ntor.RepresentativeLength+ntor.AuthLength+serverMinPadLength,
|
||||
maxHandshakeLength, false)
|
||||
if pos == -1 {
|
||||
if len(resp) >= maxHandshakeLength {
|
||||
return 0, nil, ErrInvalidHandshake
|
||||
}
|
||||
return 0, nil, ErrMarkNotFoundYet
|
||||
}
|
||||
|
||||
// Validate the MAC.
|
||||
hs.mac.Reset()
|
||||
hs.mac.Write(resp[:pos+markLength])
|
||||
hs.mac.Write(hs.epochHour)
|
||||
macCmp := hs.mac.Sum(nil)[:macLength]
|
||||
macRx := resp[pos+markLength : pos+markLength+macLength]
|
||||
if !hmac.Equal(macCmp, macRx) {
|
||||
return 0, nil, &InvalidMacError{macCmp, macRx}
|
||||
}
|
||||
|
||||
// Complete the handshake.
|
||||
serverPublic := hs.serverRepresentative.ToPublic()
|
||||
ok, seed, auth := ntor.ClientHandshake(hs.keypair, serverPublic,
|
||||
hs.serverIdentity, hs.nodeID)
|
||||
if !ok {
|
||||
return 0, nil, ErrNtorFailed
|
||||
}
|
||||
if !ntor.CompareAuth(auth, hs.serverAuth.Bytes()[:]) {
|
||||
return 0, nil, &InvalidAuthError{auth, hs.serverAuth}
|
||||
}
|
||||
|
||||
return pos + markLength + macLength, seed.Bytes()[:], nil
|
||||
}
|
||||
|
||||
type serverHandshake struct {
|
||||
keypair *ntor.Keypair
|
||||
nodeID *ntor.NodeID
|
||||
serverIdentity *ntor.Keypair
|
||||
epochHour []byte
|
||||
serverAuth *ntor.Auth
|
||||
|
||||
padLen int
|
||||
mac hash.Hash
|
||||
|
||||
clientRepresentative *ntor.Representative
|
||||
clientMark []byte
|
||||
}
|
||||
|
||||
func newServerHandshake(nodeID *ntor.NodeID, serverIdentity *ntor.Keypair, sessionKey *ntor.Keypair) *serverHandshake {
|
||||
hs := new(serverHandshake)
|
||||
hs.keypair = sessionKey
|
||||
hs.nodeID = nodeID
|
||||
hs.serverIdentity = serverIdentity
|
||||
hs.padLen = csrand.IntRange(serverMinPadLength, serverMaxPadLength)
|
||||
hs.mac = hmac.New(sha256.New, append(hs.serverIdentity.Public().Bytes()[:], hs.nodeID.Bytes()[:]...))
|
||||
|
||||
return hs
|
||||
}
|
||||
|
||||
func (hs *serverHandshake) parseClientHandshake(filter *replayfilter.ReplayFilter, resp []byte) ([]byte, error) {
|
||||
// No point in examining the data unless the miminum plausible response has
|
||||
// been received.
|
||||
if clientMinHandshakeLength > len(resp) {
|
||||
return nil, ErrMarkNotFoundYet
|
||||
}
|
||||
|
||||
if hs.clientRepresentative == nil {
|
||||
// Pull out the representative/AUTH. (XXX: Add ctors to ntor)
|
||||
hs.clientRepresentative = new(ntor.Representative)
|
||||
copy(hs.clientRepresentative.Bytes()[:], resp[0:ntor.RepresentativeLength])
|
||||
|
||||
// Derive the mark.
|
||||
hs.mac.Reset()
|
||||
hs.mac.Write(hs.clientRepresentative.Bytes()[:])
|
||||
hs.clientMark = hs.mac.Sum(nil)[:markLength]
|
||||
}
|
||||
|
||||
// Attempt to find the mark + MAC.
|
||||
pos := findMarkMac(hs.clientMark, resp, ntor.RepresentativeLength+clientMinPadLength,
|
||||
maxHandshakeLength, true)
|
||||
if pos == -1 {
|
||||
if len(resp) >= maxHandshakeLength {
|
||||
return nil, ErrInvalidHandshake
|
||||
}
|
||||
return nil, ErrMarkNotFoundYet
|
||||
}
|
||||
|
||||
// Validate the MAC.
|
||||
macFound := false
|
||||
for _, off := range []int64{0, -1, 1} {
|
||||
// Allow epoch to be off by up to a hour in either direction.
|
||||
epochHour := []byte(strconv.FormatInt(getEpochHour()+int64(off), 10))
|
||||
hs.mac.Reset()
|
||||
hs.mac.Write(resp[:pos+markLength])
|
||||
hs.mac.Write(epochHour)
|
||||
macCmp := hs.mac.Sum(nil)[:macLength]
|
||||
macRx := resp[pos+markLength : pos+markLength+macLength]
|
||||
if hmac.Equal(macCmp, macRx) {
|
||||
// Ensure that this handshake has not been seen previously.
|
||||
if filter.TestAndSet(time.Now(), macRx) {
|
||||
// The client either happened to generate exactly the same
|
||||
// session key and padding, or someone is replaying a previous
|
||||
// handshake. In either case, fuck them.
|
||||
return nil, ErrReplayedHandshake
|
||||
}
|
||||
|
||||
macFound = true
|
||||
hs.epochHour = epochHour
|
||||
|
||||
// We could break out here, but in the name of reducing timing
|
||||
// variation, evaluate all 3 MACs.
|
||||
}
|
||||
}
|
||||
if !macFound {
|
||||
// This probably should be an InvalidMacError, but conveying the 3 MACS
|
||||
// that would be accepted is annoying so just return a generic fatal
|
||||
// failure.
|
||||
return nil, ErrInvalidHandshake
|
||||
}
|
||||
|
||||
// Client should never sent trailing garbage.
|
||||
if len(resp) != pos+markLength+macLength {
|
||||
return nil, ErrInvalidHandshake
|
||||
}
|
||||
|
||||
clientPublic := hs.clientRepresentative.ToPublic()
|
||||
ok, seed, auth := ntor.ServerHandshake(clientPublic, hs.keypair,
|
||||
hs.serverIdentity, hs.nodeID)
|
||||
if !ok {
|
||||
return nil, ErrNtorFailed
|
||||
}
|
||||
hs.serverAuth = auth
|
||||
|
||||
return seed.Bytes()[:], nil
|
||||
}
|
||||
|
||||
func (hs *serverHandshake) generateHandshake() ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
|
||||
hs.mac.Reset()
|
||||
hs.mac.Write(hs.keypair.Representative().Bytes()[:])
|
||||
mark := hs.mac.Sum(nil)[:markLength]
|
||||
|
||||
// The server handshake is Y | AUTH | P_S | M_S | MAC(Y | AUTH | P_S | M_S | E) where:
|
||||
// * Y is the server's ephemeral Curve25519 public key representative.
|
||||
// * AUTH is the ntor handshake AUTH value.
|
||||
// * P_S is [serverMinPadLength,serverMaxPadLength] bytes of random padding.
|
||||
// * M_S is HMAC-SHA256-128(serverIdentity | NodeID, Y)
|
||||
// * MAC is HMAC-SHA256-128(serverIdentity | NodeID, Y .... E)
|
||||
// * E is the string representation of the number of hours since the UNIX
|
||||
// epoch.
|
||||
|
||||
// Generate the padding
|
||||
pad, err := makePad(hs.padLen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Write Y, AUTH, P_S, M_S.
|
||||
buf.Write(hs.keypair.Representative().Bytes()[:])
|
||||
buf.Write(hs.serverAuth.Bytes()[:])
|
||||
buf.Write(pad)
|
||||
buf.Write(mark)
|
||||
|
||||
// Calculate and write the MAC.
|
||||
hs.mac.Reset()
|
||||
hs.mac.Write(buf.Bytes())
|
||||
hs.mac.Write(hs.epochHour) // Set in hs.parseClientHandshake()
|
||||
buf.Write(hs.mac.Sum(nil)[:macLength])
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// getEpochHour returns the number of hours since the UNIX epoch.
|
||||
func getEpochHour() int64 {
|
||||
return time.Now().Unix() / 3600
|
||||
}
|
||||
|
||||
func findMarkMac(mark, buf []byte, startPos, maxPos int, fromTail bool) (pos int) {
|
||||
if len(mark) != markLength {
|
||||
panic(fmt.Sprintf("BUG: Invalid mark length: %d", len(mark)))
|
||||
}
|
||||
|
||||
endPos := len(buf)
|
||||
if startPos > len(buf) {
|
||||
return -1
|
||||
}
|
||||
if endPos > maxPos {
|
||||
endPos = maxPos
|
||||
}
|
||||
if endPos-startPos < markLength+macLength {
|
||||
return -1
|
||||
}
|
||||
|
||||
if fromTail {
|
||||
// The server can optimize the search process by only examining the
|
||||
// tail of the buffer. The client can't send valid data past M_C |
|
||||
// MAC_C as it does not have the server's public key yet.
|
||||
pos = endPos - (markLength + macLength)
|
||||
if !hmac.Equal(buf[pos:pos+markLength], mark) {
|
||||
return -1
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// The client has to actually do a substring search since the server can
|
||||
// and will send payload trailing the response.
|
||||
//
|
||||
// XXX: bytes.Index() uses a naive search, which kind of sucks.
|
||||
pos = bytes.Index(buf[startPos:endPos], mark)
|
||||
if pos == -1 {
|
||||
return -1
|
||||
}
|
||||
|
||||
// Ensure that there is enough trailing data for the MAC.
|
||||
if startPos+pos+markLength+macLength > endPos {
|
||||
return -1
|
||||
}
|
||||
|
||||
// Return the index relative to the start of the slice.
|
||||
pos += startPos
|
||||
return
|
||||
}
|
||||
|
||||
func makePad(padLen int) ([]byte, error) {
|
||||
pad := make([]byte, padLen)
|
||||
if err := csrand.Bytes(pad); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return pad, nil
|
||||
}
|
647
vendor/git.torproject.org/pluggable-transports/obfs4.git/transports/obfs4/obfs4.go
generated
vendored
Normal file
647
vendor/git.torproject.org/pluggable-transports/obfs4.git/transports/obfs4/obfs4.go
generated
vendored
Normal file
@ -0,0 +1,647 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Yawning Angel <yawning at torproject dot org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
// Package obfs4 provides an implementation of the Tor Project's obfs4
|
||||
// obfuscation protocol.
|
||||
package obfs4
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"flag"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net"
|
||||
"strconv"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"git.torproject.org/pluggable-transports/goptlib.git"
|
||||
"git.torproject.org/pluggable-transports/obfs4.git/common/drbg"
|
||||
"git.torproject.org/pluggable-transports/obfs4.git/common/ntor"
|
||||
"git.torproject.org/pluggable-transports/obfs4.git/common/probdist"
|
||||
"git.torproject.org/pluggable-transports/obfs4.git/common/replayfilter"
|
||||
"git.torproject.org/pluggable-transports/obfs4.git/transports/base"
|
||||
"git.torproject.org/pluggable-transports/obfs4.git/transports/obfs4/framing"
|
||||
)
|
||||
|
||||
const (
|
||||
transportName = "obfs4"
|
||||
|
||||
nodeIDArg = "node-id"
|
||||
publicKeyArg = "public-key"
|
||||
privateKeyArg = "private-key"
|
||||
seedArg = "drbg-seed"
|
||||
iatArg = "iat-mode"
|
||||
certArg = "cert"
|
||||
|
||||
biasCmdArg = "obfs4-distBias"
|
||||
|
||||
seedLength = drbg.SeedLength
|
||||
headerLength = framing.FrameOverhead + packetOverhead
|
||||
clientHandshakeTimeout = time.Duration(60) * time.Second
|
||||
serverHandshakeTimeout = time.Duration(30) * time.Second
|
||||
replayTTL = time.Duration(3) * time.Hour
|
||||
|
||||
maxIATDelay = 100
|
||||
maxCloseDelayBytes = maxHandshakeLength
|
||||
maxCloseDelay = 60
|
||||
)
|
||||
|
||||
const (
|
||||
iatNone = iota
|
||||
iatEnabled
|
||||
iatParanoid
|
||||
)
|
||||
|
||||
// biasedDist controls if the probability table will be ScrambleSuit style or
|
||||
// uniformly distributed.
|
||||
var biasedDist bool
|
||||
|
||||
type obfs4ClientArgs struct {
|
||||
nodeID *ntor.NodeID
|
||||
publicKey *ntor.PublicKey
|
||||
sessionKey *ntor.Keypair
|
||||
iatMode int
|
||||
}
|
||||
|
||||
// Transport is the obfs4 implementation of the base.Transport interface.
|
||||
type Transport struct{}
|
||||
|
||||
// Name returns the name of the obfs4 transport protocol.
|
||||
func (t *Transport) Name() string {
|
||||
return transportName
|
||||
}
|
||||
|
||||
// ClientFactory returns a new obfs4ClientFactory instance.
|
||||
func (t *Transport) ClientFactory(stateDir string) (base.ClientFactory, error) {
|
||||
cf := &obfs4ClientFactory{transport: t}
|
||||
return cf, nil
|
||||
}
|
||||
|
||||
// ServerFactory returns a new obfs4ServerFactory instance.
|
||||
func (t *Transport) ServerFactory(stateDir string, args *pt.Args) (base.ServerFactory, error) {
|
||||
st, err := serverStateFromArgs(stateDir, args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var iatSeed *drbg.Seed
|
||||
if st.iatMode != iatNone {
|
||||
iatSeedSrc := sha256.Sum256(st.drbgSeed.Bytes()[:])
|
||||
var err error
|
||||
iatSeed, err = drbg.SeedFromBytes(iatSeedSrc[:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Store the arguments that should appear in our descriptor for the clients.
|
||||
ptArgs := pt.Args{}
|
||||
ptArgs.Add(certArg, st.cert.String())
|
||||
ptArgs.Add(iatArg, strconv.Itoa(st.iatMode))
|
||||
|
||||
// Initialize the replay filter.
|
||||
filter, err := replayfilter.New(replayTTL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Initialize the close thresholds for failed connections.
|
||||
drbg, err := drbg.NewHashDrbg(st.drbgSeed)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rng := rand.New(drbg)
|
||||
|
||||
sf := &obfs4ServerFactory{t, &ptArgs, st.nodeID, st.identityKey, st.drbgSeed, iatSeed, st.iatMode, filter, rng.Intn(maxCloseDelayBytes), rng.Intn(maxCloseDelay)}
|
||||
return sf, nil
|
||||
}
|
||||
|
||||
type obfs4ClientFactory struct {
|
||||
transport base.Transport
|
||||
}
|
||||
|
||||
func (cf *obfs4ClientFactory) Transport() base.Transport {
|
||||
return cf.transport
|
||||
}
|
||||
|
||||
func (cf *obfs4ClientFactory) ParseArgs(args *pt.Args) (interface{}, error) {
|
||||
var nodeID *ntor.NodeID
|
||||
var publicKey *ntor.PublicKey
|
||||
|
||||
// The "new" (version >= 0.0.3) bridge lines use a unified "cert" argument
|
||||
// for the Node ID and Public Key.
|
||||
certStr, ok := args.Get(certArg)
|
||||
if ok {
|
||||
cert, err := serverCertFromString(certStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nodeID, publicKey = cert.unpack()
|
||||
} else {
|
||||
// The "old" style (version <= 0.0.2) bridge lines use separate Node ID
|
||||
// and Public Key arguments in Base16 encoding and are a UX disaster.
|
||||
nodeIDStr, ok := args.Get(nodeIDArg)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("missing argument '%s'", nodeIDArg)
|
||||
}
|
||||
var err error
|
||||
if nodeID, err = ntor.NodeIDFromHex(nodeIDStr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
publicKeyStr, ok := args.Get(publicKeyArg)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("missing argument '%s'", publicKeyArg)
|
||||
}
|
||||
if publicKey, err = ntor.PublicKeyFromHex(publicKeyStr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// IAT config is common across the two bridge line formats.
|
||||
iatStr, ok := args.Get(iatArg)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("missing argument '%s'", iatArg)
|
||||
}
|
||||
iatMode, err := strconv.Atoi(iatStr)
|
||||
if err != nil || iatMode < iatNone || iatMode > iatParanoid {
|
||||
return nil, fmt.Errorf("invalid iat-mode '%d'", iatMode)
|
||||
}
|
||||
|
||||
// Generate the session key pair before connectiong to hide the Elligator2
|
||||
// rejection sampling from network observers.
|
||||
sessionKey, err := ntor.NewKeypair(true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &obfs4ClientArgs{nodeID, publicKey, sessionKey, iatMode}, nil
|
||||
}
|
||||
|
||||
func (cf *obfs4ClientFactory) Dial(network, addr string, dialFn base.DialFunc, args interface{}) (net.Conn, error) {
|
||||
// Validate args before bothering to open connection.
|
||||
ca, ok := args.(*obfs4ClientArgs)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid argument type for args")
|
||||
}
|
||||
conn, err := dialFn(network, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dialConn := conn
|
||||
if conn, err = newObfs4ClientConn(conn, ca); err != nil {
|
||||
dialConn.Close()
|
||||
return nil, err
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
type obfs4ServerFactory struct {
|
||||
transport base.Transport
|
||||
args *pt.Args
|
||||
|
||||
nodeID *ntor.NodeID
|
||||
identityKey *ntor.Keypair
|
||||
lenSeed *drbg.Seed
|
||||
iatSeed *drbg.Seed
|
||||
iatMode int
|
||||
replayFilter *replayfilter.ReplayFilter
|
||||
|
||||
closeDelayBytes int
|
||||
closeDelay int
|
||||
}
|
||||
|
||||
func (sf *obfs4ServerFactory) Transport() base.Transport {
|
||||
return sf.transport
|
||||
}
|
||||
|
||||
func (sf *obfs4ServerFactory) Args() *pt.Args {
|
||||
return sf.args
|
||||
}
|
||||
|
||||
func (sf *obfs4ServerFactory) WrapConn(conn net.Conn) (net.Conn, error) {
|
||||
// Not much point in having a separate newObfs4ServerConn routine when
|
||||
// wrapping requires using values from the factory instance.
|
||||
|
||||
// Generate the session keypair *before* consuming data from the peer, to
|
||||
// attempt to mask the rejection sampling due to use of Elligator2. This
|
||||
// might be futile, but the timing differential isn't very large on modern
|
||||
// hardware, and there are far easier statistical attacks that can be
|
||||
// mounted as a distinguisher.
|
||||
sessionKey, err := ntor.NewKeypair(true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
lenDist := probdist.New(sf.lenSeed, 0, framing.MaximumSegmentLength, biasedDist)
|
||||
var iatDist *probdist.WeightedDist
|
||||
if sf.iatSeed != nil {
|
||||
iatDist = probdist.New(sf.iatSeed, 0, maxIATDelay, biasedDist)
|
||||
}
|
||||
|
||||
c := &obfs4Conn{conn, true, lenDist, iatDist, sf.iatMode, bytes.NewBuffer(nil), bytes.NewBuffer(nil), make([]byte, consumeReadSize), nil, nil}
|
||||
|
||||
startTime := time.Now()
|
||||
|
||||
if err = c.serverHandshake(sf, sessionKey); err != nil {
|
||||
c.closeAfterDelay(sf, startTime)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
type obfs4Conn struct {
|
||||
net.Conn
|
||||
|
||||
isServer bool
|
||||
|
||||
lenDist *probdist.WeightedDist
|
||||
iatDist *probdist.WeightedDist
|
||||
iatMode int
|
||||
|
||||
receiveBuffer *bytes.Buffer
|
||||
receiveDecodedBuffer *bytes.Buffer
|
||||
readBuffer []byte
|
||||
|
||||
encoder *framing.Encoder
|
||||
decoder *framing.Decoder
|
||||
}
|
||||
|
||||
func newObfs4ClientConn(conn net.Conn, args *obfs4ClientArgs) (c *obfs4Conn, err error) {
|
||||
// Generate the initial protocol polymorphism distribution(s).
|
||||
var seed *drbg.Seed
|
||||
if seed, err = drbg.NewSeed(); err != nil {
|
||||
return
|
||||
}
|
||||
lenDist := probdist.New(seed, 0, framing.MaximumSegmentLength, biasedDist)
|
||||
var iatDist *probdist.WeightedDist
|
||||
if args.iatMode != iatNone {
|
||||
var iatSeed *drbg.Seed
|
||||
iatSeedSrc := sha256.Sum256(seed.Bytes()[:])
|
||||
if iatSeed, err = drbg.SeedFromBytes(iatSeedSrc[:]); err != nil {
|
||||
return
|
||||
}
|
||||
iatDist = probdist.New(iatSeed, 0, maxIATDelay, biasedDist)
|
||||
}
|
||||
|
||||
// Allocate the client structure.
|
||||
c = &obfs4Conn{conn, false, lenDist, iatDist, args.iatMode, bytes.NewBuffer(nil), bytes.NewBuffer(nil), make([]byte, consumeReadSize), nil, nil}
|
||||
|
||||
// Start the handshake timeout.
|
||||
deadline := time.Now().Add(clientHandshakeTimeout)
|
||||
if err = conn.SetDeadline(deadline); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = c.clientHandshake(args.nodeID, args.publicKey, args.sessionKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Stop the handshake timeout.
|
||||
if err = conn.SetDeadline(time.Time{}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (conn *obfs4Conn) clientHandshake(nodeID *ntor.NodeID, peerIdentityKey *ntor.PublicKey, sessionKey *ntor.Keypair) error {
|
||||
if conn.isServer {
|
||||
return fmt.Errorf("clientHandshake called on server connection")
|
||||
}
|
||||
|
||||
// Generate and send the client handshake.
|
||||
hs := newClientHandshake(nodeID, peerIdentityKey, sessionKey)
|
||||
blob, err := hs.generateHandshake()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = conn.Conn.Write(blob); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Consume the server handshake.
|
||||
var hsBuf [maxHandshakeLength]byte
|
||||
for {
|
||||
n, err := conn.Conn.Read(hsBuf[:])
|
||||
if err != nil {
|
||||
// The Read() could have returned data and an error, but there is
|
||||
// no point in continuing on an EOF or whatever.
|
||||
return err
|
||||
}
|
||||
conn.receiveBuffer.Write(hsBuf[:n])
|
||||
|
||||
n, seed, err := hs.parseServerHandshake(conn.receiveBuffer.Bytes())
|
||||
if err == ErrMarkNotFoundYet {
|
||||
continue
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
_ = conn.receiveBuffer.Next(n)
|
||||
|
||||
// Use the derived key material to intialize the link crypto.
|
||||
okm := ntor.Kdf(seed, framing.KeyLength*2)
|
||||
conn.encoder = framing.NewEncoder(okm[:framing.KeyLength])
|
||||
conn.decoder = framing.NewDecoder(okm[framing.KeyLength:])
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (conn *obfs4Conn) serverHandshake(sf *obfs4ServerFactory, sessionKey *ntor.Keypair) error {
|
||||
if !conn.isServer {
|
||||
return fmt.Errorf("serverHandshake called on client connection")
|
||||
}
|
||||
|
||||
// Generate the server handshake, and arm the base timeout.
|
||||
hs := newServerHandshake(sf.nodeID, sf.identityKey, sessionKey)
|
||||
if err := conn.Conn.SetDeadline(time.Now().Add(serverHandshakeTimeout)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Consume the client handshake.
|
||||
var hsBuf [maxHandshakeLength]byte
|
||||
for {
|
||||
n, err := conn.Conn.Read(hsBuf[:])
|
||||
if err != nil {
|
||||
// The Read() could have returned data and an error, but there is
|
||||
// no point in continuing on an EOF or whatever.
|
||||
return err
|
||||
}
|
||||
conn.receiveBuffer.Write(hsBuf[:n])
|
||||
|
||||
seed, err := hs.parseClientHandshake(sf.replayFilter, conn.receiveBuffer.Bytes())
|
||||
if err == ErrMarkNotFoundYet {
|
||||
continue
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
conn.receiveBuffer.Reset()
|
||||
|
||||
if err := conn.Conn.SetDeadline(time.Time{}); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Use the derived key material to intialize the link crypto.
|
||||
okm := ntor.Kdf(seed, framing.KeyLength*2)
|
||||
conn.encoder = framing.NewEncoder(okm[framing.KeyLength:])
|
||||
conn.decoder = framing.NewDecoder(okm[:framing.KeyLength])
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
// Since the current and only implementation always sends a PRNG seed for
|
||||
// the length obfuscation, this makes the amount of data received from the
|
||||
// server inconsistent with the length sent from the client.
|
||||
//
|
||||
// Rebalance this by tweaking the client mimimum padding/server maximum
|
||||
// padding, and sending the PRNG seed unpadded (As in, treat the PRNG seed
|
||||
// as part of the server response). See inlineSeedFrameLength in
|
||||
// handshake_ntor.go.
|
||||
|
||||
// Generate/send the response.
|
||||
blob, err := hs.generateHandshake()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var frameBuf bytes.Buffer
|
||||
if _, err = frameBuf.Write(blob); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Send the PRNG seed as the first packet.
|
||||
if err := conn.makePacket(&frameBuf, packetTypePrngSeed, sf.lenSeed.Bytes()[:], 0); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = conn.Conn.Write(frameBuf.Bytes()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (conn *obfs4Conn) Read(b []byte) (n int, err error) {
|
||||
// If there is no payload from the previous Read() calls, consume data off
|
||||
// the network. Not all data received is guaranteed to be usable payload,
|
||||
// so do this in a loop till data is present or an error occurs.
|
||||
for conn.receiveDecodedBuffer.Len() == 0 {
|
||||
err = conn.readPackets()
|
||||
if err == framing.ErrAgain {
|
||||
// Don't proagate this back up the call stack if we happen to break
|
||||
// out of the loop.
|
||||
err = nil
|
||||
continue
|
||||
} else if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Even if err is set, attempt to do the read anyway so that all decoded
|
||||
// data gets relayed before the connection is torn down.
|
||||
if conn.receiveDecodedBuffer.Len() > 0 {
|
||||
var berr error
|
||||
n, berr = conn.receiveDecodedBuffer.Read(b)
|
||||
if err == nil {
|
||||
// Only propagate berr if there are not more important (fatal)
|
||||
// errors from the network/crypto/packet processing.
|
||||
err = berr
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (conn *obfs4Conn) Write(b []byte) (n int, err error) {
|
||||
chopBuf := bytes.NewBuffer(b)
|
||||
var payload [maxPacketPayloadLength]byte
|
||||
var frameBuf bytes.Buffer
|
||||
|
||||
// Chop the pending data into payload frames.
|
||||
for chopBuf.Len() > 0 {
|
||||
// Send maximum sized frames.
|
||||
rdLen := 0
|
||||
rdLen, err = chopBuf.Read(payload[:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
} else if rdLen == 0 {
|
||||
panic(fmt.Sprintf("BUG: Write(), chopping length was 0"))
|
||||
}
|
||||
n += rdLen
|
||||
|
||||
err = conn.makePacket(&frameBuf, packetTypePayload, payload[:rdLen], 0)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
if conn.iatMode != iatParanoid {
|
||||
// For non-paranoid IAT, pad once per burst. Paranoid IAT handles
|
||||
// things differently.
|
||||
if err = conn.padBurst(&frameBuf, conn.lenDist.Sample()); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
// Write the pending data onto the network. Partial writes are fatal,
|
||||
// because the frame encoder state is advanced, and the code doesn't keep
|
||||
// frameBuf around. In theory, write timeouts and whatnot could be
|
||||
// supported if this wasn't the case, but that complicates the code.
|
||||
if conn.iatMode != iatNone {
|
||||
var iatFrame [framing.MaximumSegmentLength]byte
|
||||
for frameBuf.Len() > 0 {
|
||||
iatWrLen := 0
|
||||
|
||||
switch conn.iatMode {
|
||||
case iatEnabled:
|
||||
// Standard (ScrambleSuit-style) IAT obfuscation optimizes for
|
||||
// bulk transport and will write ~MTU sized frames when
|
||||
// possible.
|
||||
iatWrLen, err = frameBuf.Read(iatFrame[:])
|
||||
|
||||
case iatParanoid:
|
||||
// Paranoid IAT obfuscation throws performance out of the
|
||||
// window and will sample the length distribution every time a
|
||||
// write is scheduled.
|
||||
targetLen := conn.lenDist.Sample()
|
||||
if frameBuf.Len() < targetLen {
|
||||
// There's not enough data buffered for the target write,
|
||||
// so padding must be inserted.
|
||||
if err = conn.padBurst(&frameBuf, targetLen); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if frameBuf.Len() != targetLen {
|
||||
// Ugh, padding came out to a value that required more
|
||||
// than one frame, this is relatively unlikely so just
|
||||
// resample since there's enough data to ensure that
|
||||
// the next sample will be written.
|
||||
continue
|
||||
}
|
||||
}
|
||||
iatWrLen, err = frameBuf.Read(iatFrame[:targetLen])
|
||||
}
|
||||
if err != nil {
|
||||
return 0, err
|
||||
} else if iatWrLen == 0 {
|
||||
panic(fmt.Sprintf("BUG: Write(), iat length was 0"))
|
||||
}
|
||||
|
||||
// Calculate the delay. The delay resolution is 100 usec, leading
|
||||
// to a maximum delay of 10 msec.
|
||||
iatDelta := time.Duration(conn.iatDist.Sample() * 100)
|
||||
|
||||
// Write then sleep.
|
||||
_, err = conn.Conn.Write(iatFrame[:iatWrLen])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
time.Sleep(iatDelta * time.Microsecond)
|
||||
}
|
||||
} else {
|
||||
_, err = conn.Conn.Write(frameBuf.Bytes())
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (conn *obfs4Conn) SetDeadline(t time.Time) error {
|
||||
return syscall.ENOTSUP
|
||||
}
|
||||
|
||||
func (conn *obfs4Conn) SetWriteDeadline(t time.Time) error {
|
||||
return syscall.ENOTSUP
|
||||
}
|
||||
|
||||
func (conn *obfs4Conn) closeAfterDelay(sf *obfs4ServerFactory, startTime time.Time) {
|
||||
// I-it's not like I w-wanna handshake with you or anything. B-b-baka!
|
||||
defer conn.Conn.Close()
|
||||
|
||||
delay := time.Duration(sf.closeDelay)*time.Second + serverHandshakeTimeout
|
||||
deadline := startTime.Add(delay)
|
||||
if time.Now().After(deadline) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := conn.Conn.SetReadDeadline(deadline); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Consume and discard data on this connection until either the specified
|
||||
// interval passes or a certain size has been reached.
|
||||
discarded := 0
|
||||
var buf [framing.MaximumSegmentLength]byte
|
||||
for discarded < int(sf.closeDelayBytes) {
|
||||
n, err := conn.Conn.Read(buf[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
discarded += n
|
||||
}
|
||||
}
|
||||
|
||||
func (conn *obfs4Conn) padBurst(burst *bytes.Buffer, toPadTo int) (err error) {
|
||||
tailLen := burst.Len() % framing.MaximumSegmentLength
|
||||
|
||||
padLen := 0
|
||||
if toPadTo >= tailLen {
|
||||
padLen = toPadTo - tailLen
|
||||
} else {
|
||||
padLen = (framing.MaximumSegmentLength - tailLen) + toPadTo
|
||||
}
|
||||
|
||||
if padLen > headerLength {
|
||||
err = conn.makePacket(burst, packetTypePayload, []byte{},
|
||||
uint16(padLen-headerLength))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else if padLen > 0 {
|
||||
err = conn.makePacket(burst, packetTypePayload, []byte{},
|
||||
maxPacketPayloadLength)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = conn.makePacket(burst, packetTypePayload, []byte{},
|
||||
uint16(padLen))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func init() {
|
||||
flag.BoolVar(&biasedDist, biasCmdArg, false, "Enable obfs4 using ScrambleSuit style table generation")
|
||||
}
|
||||
|
||||
var _ base.ClientFactory = (*obfs4ClientFactory)(nil)
|
||||
var _ base.ServerFactory = (*obfs4ServerFactory)(nil)
|
||||
var _ base.Transport = (*Transport)(nil)
|
||||
var _ net.Conn = (*obfs4Conn)(nil)
|
175
vendor/git.torproject.org/pluggable-transports/obfs4.git/transports/obfs4/packet.go
generated
vendored
Normal file
175
vendor/git.torproject.org/pluggable-transports/obfs4.git/transports/obfs4/packet.go
generated
vendored
Normal file
@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Yawning Angel <yawning at torproject dot org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package obfs4
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"git.torproject.org/pluggable-transports/obfs4.git/common/drbg"
|
||||
"git.torproject.org/pluggable-transports/obfs4.git/transports/obfs4/framing"
|
||||
)
|
||||
|
||||
const (
|
||||
packetOverhead = 2 + 1
|
||||
maxPacketPayloadLength = framing.MaximumFramePayloadLength - packetOverhead
|
||||
maxPacketPaddingLength = maxPacketPayloadLength
|
||||
seedPacketPayloadLength = seedLength
|
||||
|
||||
consumeReadSize = framing.MaximumSegmentLength * 16
|
||||
)
|
||||
|
||||
const (
|
||||
packetTypePayload = iota
|
||||
packetTypePrngSeed
|
||||
)
|
||||
|
||||
// InvalidPacketLengthError is the error returned when decodePacket detects a
|
||||
// invalid packet length/
|
||||
type InvalidPacketLengthError int
|
||||
|
||||
func (e InvalidPacketLengthError) Error() string {
|
||||
return fmt.Sprintf("packet: Invalid packet length: %d", int(e))
|
||||
}
|
||||
|
||||
// InvalidPayloadLengthError is the error returned when decodePacket rejects the
|
||||
// payload length.
|
||||
type InvalidPayloadLengthError int
|
||||
|
||||
func (e InvalidPayloadLengthError) Error() string {
|
||||
return fmt.Sprintf("packet: Invalid payload length: %d", int(e))
|
||||
}
|
||||
|
||||
var zeroPadBytes [maxPacketPaddingLength]byte
|
||||
|
||||
func (conn *obfs4Conn) makePacket(w io.Writer, pktType uint8, data []byte, padLen uint16) error {
|
||||
var pkt [framing.MaximumFramePayloadLength]byte
|
||||
|
||||
if len(data)+int(padLen) > maxPacketPayloadLength {
|
||||
panic(fmt.Sprintf("BUG: makePacket() len(data) + padLen > maxPacketPayloadLength: %d + %d > %d",
|
||||
len(data), padLen, maxPacketPayloadLength))
|
||||
}
|
||||
|
||||
// Packets are:
|
||||
// uint8_t type packetTypePayload (0x00)
|
||||
// uint16_t length Length of the payload (Big Endian).
|
||||
// uint8_t[] payload Data payload.
|
||||
// uint8_t[] padding Padding.
|
||||
pkt[0] = pktType
|
||||
binary.BigEndian.PutUint16(pkt[1:], uint16(len(data)))
|
||||
if len(data) > 0 {
|
||||
copy(pkt[3:], data[:])
|
||||
}
|
||||
copy(pkt[3+len(data):], zeroPadBytes[:padLen])
|
||||
|
||||
pktLen := packetOverhead + len(data) + int(padLen)
|
||||
|
||||
// Encode the packet in an AEAD frame.
|
||||
var frame [framing.MaximumSegmentLength]byte
|
||||
frameLen, err := conn.encoder.Encode(frame[:], pkt[:pktLen])
|
||||
if err != nil {
|
||||
// All encoder errors are fatal.
|
||||
return err
|
||||
}
|
||||
wrLen, err := w.Write(frame[:frameLen])
|
||||
if err != nil {
|
||||
return err
|
||||
} else if wrLen < frameLen {
|
||||
return io.ErrShortWrite
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (conn *obfs4Conn) readPackets() (err error) {
|
||||
// Attempt to read off the network.
|
||||
rdLen, rdErr := conn.Conn.Read(conn.readBuffer)
|
||||
conn.receiveBuffer.Write(conn.readBuffer[:rdLen])
|
||||
|
||||
var decoded [framing.MaximumFramePayloadLength]byte
|
||||
for conn.receiveBuffer.Len() > 0 {
|
||||
// Decrypt an AEAD frame.
|
||||
decLen := 0
|
||||
decLen, err = conn.decoder.Decode(decoded[:], conn.receiveBuffer)
|
||||
if err == framing.ErrAgain {
|
||||
break
|
||||
} else if err != nil {
|
||||
break
|
||||
} else if decLen < packetOverhead {
|
||||
err = InvalidPacketLengthError(decLen)
|
||||
break
|
||||
}
|
||||
|
||||
// Decode the packet.
|
||||
pkt := decoded[0:decLen]
|
||||
pktType := pkt[0]
|
||||
payloadLen := binary.BigEndian.Uint16(pkt[1:])
|
||||
if int(payloadLen) > len(pkt)-packetOverhead {
|
||||
err = InvalidPayloadLengthError(int(payloadLen))
|
||||
break
|
||||
}
|
||||
payload := pkt[3 : 3+payloadLen]
|
||||
|
||||
switch pktType {
|
||||
case packetTypePayload:
|
||||
if payloadLen > 0 {
|
||||
conn.receiveDecodedBuffer.Write(payload)
|
||||
}
|
||||
case packetTypePrngSeed:
|
||||
// Only regenerate the distribution if we are the client.
|
||||
if len(payload) == seedPacketPayloadLength && !conn.isServer {
|
||||
var seed *drbg.Seed
|
||||
seed, err = drbg.SeedFromBytes(payload)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
conn.lenDist.Reset(seed)
|
||||
if conn.iatDist != nil {
|
||||
iatSeedSrc := sha256.Sum256(seed.Bytes()[:])
|
||||
iatSeed, err := drbg.SeedFromBytes(iatSeedSrc[:])
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
conn.iatDist.Reset(iatSeed)
|
||||
}
|
||||
}
|
||||
default:
|
||||
// Ignore unknown packet types.
|
||||
}
|
||||
}
|
||||
|
||||
// Read errors (all fatal) take priority over various frame processing
|
||||
// errors.
|
||||
if rdErr != nil {
|
||||
return rdErr
|
||||
}
|
||||
|
||||
return
|
||||
}
|
260
vendor/git.torproject.org/pluggable-transports/obfs4.git/transports/obfs4/statefile.go
generated
vendored
Normal file
260
vendor/git.torproject.org/pluggable-transports/obfs4.git/transports/obfs4/statefile.go
generated
vendored
Normal file
@ -0,0 +1,260 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Yawning Angel <yawning at torproject dot org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package obfs4
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"git.torproject.org/pluggable-transports/goptlib.git"
|
||||
"git.torproject.org/pluggable-transports/obfs4.git/common/csrand"
|
||||
"git.torproject.org/pluggable-transports/obfs4.git/common/drbg"
|
||||
"git.torproject.org/pluggable-transports/obfs4.git/common/ntor"
|
||||
)
|
||||
|
||||
const (
|
||||
stateFile = "obfs4_state.json"
|
||||
bridgeFile = "obfs4_bridgeline.txt"
|
||||
|
||||
certSuffix = "=="
|
||||
certLength = ntor.NodeIDLength + ntor.PublicKeyLength
|
||||
)
|
||||
|
||||
type jsonServerState struct {
|
||||
NodeID string `json:"node-id"`
|
||||
PrivateKey string `json:"private-key"`
|
||||
PublicKey string `json:"public-key"`
|
||||
DrbgSeed string `json:"drbg-seed"`
|
||||
IATMode int `json:"iat-mode"`
|
||||
}
|
||||
|
||||
type obfs4ServerCert struct {
|
||||
raw []byte
|
||||
}
|
||||
|
||||
func (cert *obfs4ServerCert) String() string {
|
||||
return strings.TrimSuffix(base64.StdEncoding.EncodeToString(cert.raw), certSuffix)
|
||||
}
|
||||
|
||||
func (cert *obfs4ServerCert) unpack() (*ntor.NodeID, *ntor.PublicKey) {
|
||||
if len(cert.raw) != certLength {
|
||||
panic(fmt.Sprintf("cert length %d is invalid", len(cert.raw)))
|
||||
}
|
||||
|
||||
nodeID, _ := ntor.NewNodeID(cert.raw[:ntor.NodeIDLength])
|
||||
pubKey, _ := ntor.NewPublicKey(cert.raw[ntor.NodeIDLength:])
|
||||
|
||||
return nodeID, pubKey
|
||||
}
|
||||
|
||||
func serverCertFromString(encoded string) (*obfs4ServerCert, error) {
|
||||
decoded, err := base64.StdEncoding.DecodeString(encoded + certSuffix)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode cert: %s", err)
|
||||
}
|
||||
|
||||
if len(decoded) != certLength {
|
||||
return nil, fmt.Errorf("cert length %d is invalid", len(decoded))
|
||||
}
|
||||
|
||||
return &obfs4ServerCert{raw: decoded}, nil
|
||||
}
|
||||
|
||||
func serverCertFromState(st *obfs4ServerState) *obfs4ServerCert {
|
||||
cert := new(obfs4ServerCert)
|
||||
cert.raw = append(st.nodeID.Bytes()[:], st.identityKey.Public().Bytes()[:]...)
|
||||
return cert
|
||||
}
|
||||
|
||||
type obfs4ServerState struct {
|
||||
nodeID *ntor.NodeID
|
||||
identityKey *ntor.Keypair
|
||||
drbgSeed *drbg.Seed
|
||||
iatMode int
|
||||
|
||||
cert *obfs4ServerCert
|
||||
}
|
||||
|
||||
func (st *obfs4ServerState) clientString() string {
|
||||
return fmt.Sprintf("%s=%s %s=%d", certArg, st.cert, iatArg, st.iatMode)
|
||||
}
|
||||
|
||||
func serverStateFromArgs(stateDir string, args *pt.Args) (*obfs4ServerState, error) {
|
||||
var js jsonServerState
|
||||
var nodeIDOk, privKeyOk, seedOk bool
|
||||
|
||||
js.NodeID, nodeIDOk = args.Get(nodeIDArg)
|
||||
js.PrivateKey, privKeyOk = args.Get(privateKeyArg)
|
||||
js.DrbgSeed, seedOk = args.Get(seedArg)
|
||||
iatStr, iatOk := args.Get(iatArg)
|
||||
|
||||
// Either a private key, node id, and seed are ALL specified, or
|
||||
// they should be loaded from the state file.
|
||||
if !privKeyOk && !nodeIDOk && !seedOk {
|
||||
if err := jsonServerStateFromFile(stateDir, &js); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if !privKeyOk {
|
||||
return nil, fmt.Errorf("missing argument '%s'", privateKeyArg)
|
||||
} else if !nodeIDOk {
|
||||
return nil, fmt.Errorf("missing argument '%s'", nodeIDArg)
|
||||
} else if !seedOk {
|
||||
return nil, fmt.Errorf("missing argument '%s'", seedArg)
|
||||
}
|
||||
|
||||
// The IAT mode should be independently configurable.
|
||||
if iatOk {
|
||||
// If the IAT mode is specified, attempt to parse and apply it
|
||||
// as an override.
|
||||
iatMode, err := strconv.Atoi(iatStr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("malformed iat-mode '%s'", iatStr)
|
||||
}
|
||||
js.IATMode = iatMode
|
||||
}
|
||||
|
||||
return serverStateFromJSONServerState(stateDir, &js)
|
||||
}
|
||||
|
||||
func serverStateFromJSONServerState(stateDir string, js *jsonServerState) (*obfs4ServerState, error) {
|
||||
var err error
|
||||
|
||||
st := new(obfs4ServerState)
|
||||
if st.nodeID, err = ntor.NodeIDFromHex(js.NodeID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if st.identityKey, err = ntor.KeypairFromHex(js.PrivateKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if st.drbgSeed, err = drbg.SeedFromHex(js.DrbgSeed); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if js.IATMode < iatNone || js.IATMode > iatParanoid {
|
||||
return nil, fmt.Errorf("invalid iat-mode '%d'", js.IATMode)
|
||||
}
|
||||
st.iatMode = js.IATMode
|
||||
st.cert = serverCertFromState(st)
|
||||
|
||||
// Generate a human readable summary of the configured endpoint.
|
||||
if err = newBridgeFile(stateDir, st); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Write back the possibly updated server state.
|
||||
return st, writeJSONServerState(stateDir, js)
|
||||
}
|
||||
|
||||
func jsonServerStateFromFile(stateDir string, js *jsonServerState) error {
|
||||
fPath := path.Join(stateDir, stateFile)
|
||||
f, err := ioutil.ReadFile(fPath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if err = newJSONServerState(stateDir, js); err == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(f, js); err != nil {
|
||||
return fmt.Errorf("failed to load statefile '%s': %s", fPath, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func newJSONServerState(stateDir string, js *jsonServerState) (err error) {
|
||||
// Generate everything a server needs, using the cryptographic PRNG.
|
||||
var st obfs4ServerState
|
||||
rawID := make([]byte, ntor.NodeIDLength)
|
||||
if err = csrand.Bytes(rawID); err != nil {
|
||||
return
|
||||
}
|
||||
if st.nodeID, err = ntor.NewNodeID(rawID); err != nil {
|
||||
return
|
||||
}
|
||||
if st.identityKey, err = ntor.NewKeypair(false); err != nil {
|
||||
return
|
||||
}
|
||||
if st.drbgSeed, err = drbg.NewSeed(); err != nil {
|
||||
return
|
||||
}
|
||||
st.iatMode = iatNone
|
||||
|
||||
// Encode it into JSON format and write the state file.
|
||||
js.NodeID = st.nodeID.Hex()
|
||||
js.PrivateKey = st.identityKey.Private().Hex()
|
||||
js.PublicKey = st.identityKey.Public().Hex()
|
||||
js.DrbgSeed = st.drbgSeed.Hex()
|
||||
js.IATMode = st.iatMode
|
||||
|
||||
return writeJSONServerState(stateDir, js)
|
||||
}
|
||||
|
||||
func writeJSONServerState(stateDir string, js *jsonServerState) error {
|
||||
var err error
|
||||
var encoded []byte
|
||||
if encoded, err = json.Marshal(js); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = ioutil.WriteFile(path.Join(stateDir, stateFile), encoded, 0600); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func newBridgeFile(stateDir string, st *obfs4ServerState) error {
|
||||
const prefix = "# obfs4 torrc client bridge line\n" +
|
||||
"#\n" +
|
||||
"# This file is an automatically generated bridge line based on\n" +
|
||||
"# the current obfs4proxy configuration. EDITING IT WILL HAVE\n" +
|
||||
"# NO EFFECT.\n" +
|
||||
"#\n" +
|
||||
"# Before distributing this Bridge, edit the placeholder fields\n" +
|
||||
"# to contain the actual values:\n" +
|
||||
"# <IP ADDRESS> - The public IP address of your obfs4 bridge.\n" +
|
||||
"# <PORT> - The TCP/IP port of your obfs4 bridge.\n" +
|
||||
"# <FINGERPRINT> - The bridge's fingerprint.\n\n"
|
||||
|
||||
bridgeLine := fmt.Sprintf("Bridge obfs4 <IP ADDRESS>:<PORT> <FINGERPRINT> %s\n",
|
||||
st.clientString())
|
||||
|
||||
tmp := []byte(prefix + bridgeLine)
|
||||
if err := ioutil.WriteFile(path.Join(stateDir, bridgeFile), tmp, 0600); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
27
vendor/github.com/agl/ed25519/LICENSE
generated
vendored
Normal file
27
vendor/github.com/agl/ed25519/LICENSE
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
Copyright (c) 2012 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
1411
vendor/github.com/agl/ed25519/edwards25519/const.go
generated
vendored
Normal file
1411
vendor/github.com/agl/ed25519/edwards25519/const.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1773
vendor/github.com/agl/ed25519/edwards25519/edwards25519.go
generated
vendored
Normal file
1773
vendor/github.com/agl/ed25519/edwards25519/edwards25519.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
340
vendor/github.com/agl/ed25519/extra25519/extra25519.go
generated
vendored
Normal file
340
vendor/github.com/agl/ed25519/extra25519/extra25519.go
generated
vendored
Normal file
@ -0,0 +1,340 @@
|
||||
// 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 extra25519
|
||||
|
||||
import (
|
||||
"crypto/sha512"
|
||||
|
||||
"github.com/agl/ed25519/edwards25519"
|
||||
)
|
||||
|
||||
// PrivateKeyToCurve25519 converts an ed25519 private key into a corresponding
|
||||
// curve25519 private key such that the resulting curve25519 public key will
|
||||
// equal the result from PublicKeyToCurve25519.
|
||||
func PrivateKeyToCurve25519(curve25519Private *[32]byte, privateKey *[64]byte) {
|
||||
h := sha512.New()
|
||||
h.Write(privateKey[:32])
|
||||
digest := h.Sum(nil)
|
||||
|
||||
digest[0] &= 248
|
||||
digest[31] &= 127
|
||||
digest[31] |= 64
|
||||
|
||||
copy(curve25519Private[:], digest)
|
||||
}
|
||||
|
||||
func edwardsToMontgomeryX(outX, y *edwards25519.FieldElement) {
|
||||
// We only need the x-coordinate of the curve25519 point, which I'll
|
||||
// call u. The isomorphism is u=(y+1)/(1-y), since y=Y/Z, this gives
|
||||
// u=(Y+Z)/(Z-Y). We know that Z=1, thus u=(Y+1)/(1-Y).
|
||||
var oneMinusY edwards25519.FieldElement
|
||||
edwards25519.FeOne(&oneMinusY)
|
||||
edwards25519.FeSub(&oneMinusY, &oneMinusY, y)
|
||||
edwards25519.FeInvert(&oneMinusY, &oneMinusY)
|
||||
|
||||
edwards25519.FeOne(outX)
|
||||
edwards25519.FeAdd(outX, outX, y)
|
||||
|
||||
edwards25519.FeMul(outX, outX, &oneMinusY)
|
||||
}
|
||||
|
||||
// PublicKeyToCurve25519 converts an Ed25519 public key into the curve25519
|
||||
// public key that would be generated from the same private key.
|
||||
func PublicKeyToCurve25519(curve25519Public *[32]byte, publicKey *[32]byte) bool {
|
||||
var A edwards25519.ExtendedGroupElement
|
||||
if !A.FromBytes(publicKey) {
|
||||
return false
|
||||
}
|
||||
|
||||
// A.Z = 1 as a postcondition of FromBytes.
|
||||
var x edwards25519.FieldElement
|
||||
edwardsToMontgomeryX(&x, &A.Y)
|
||||
edwards25519.FeToBytes(curve25519Public, &x)
|
||||
return true
|
||||
}
|
||||
|
||||
// sqrtMinusAPlus2 is sqrt(-(486662+2))
|
||||
var sqrtMinusAPlus2 = edwards25519.FieldElement{
|
||||
-12222970, -8312128, -11511410, 9067497, -15300785, -241793, 25456130, 14121551, -12187136, 3972024,
|
||||
}
|
||||
|
||||
// sqrtMinusHalf is sqrt(-1/2)
|
||||
var sqrtMinusHalf = edwards25519.FieldElement{
|
||||
-17256545, 3971863, 28865457, -1750208, 27359696, -16640980, 12573105, 1002827, -163343, 11073975,
|
||||
}
|
||||
|
||||
// halfQMinus1Bytes is (2^255-20)/2 expressed in little endian form.
|
||||
var halfQMinus1Bytes = [32]byte{
|
||||
0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f,
|
||||
}
|
||||
|
||||
// feBytesLess returns one if a <= b and zero otherwise.
|
||||
func feBytesLE(a, b *[32]byte) int32 {
|
||||
equalSoFar := int32(-1)
|
||||
greater := int32(0)
|
||||
|
||||
for i := uint(31); i < 32; i-- {
|
||||
x := int32(a[i])
|
||||
y := int32(b[i])
|
||||
|
||||
greater = (^equalSoFar & greater) | (equalSoFar & ((x - y) >> 31))
|
||||
equalSoFar = equalSoFar & (((x ^ y) - 1) >> 31)
|
||||
}
|
||||
|
||||
return int32(^equalSoFar & 1 & greater)
|
||||
}
|
||||
|
||||
// ScalarBaseMult computes a curve25519 public key from a private key and also
|
||||
// a uniform representative for that public key. Note that this function will
|
||||
// fail and return false for about half of private keys.
|
||||
// See http://elligator.cr.yp.to/elligator-20130828.pdf.
|
||||
func ScalarBaseMult(publicKey, representative, privateKey *[32]byte) bool {
|
||||
var maskedPrivateKey [32]byte
|
||||
copy(maskedPrivateKey[:], privateKey[:])
|
||||
|
||||
maskedPrivateKey[0] &= 248
|
||||
maskedPrivateKey[31] &= 127
|
||||
maskedPrivateKey[31] |= 64
|
||||
|
||||
var A edwards25519.ExtendedGroupElement
|
||||
edwards25519.GeScalarMultBase(&A, &maskedPrivateKey)
|
||||
|
||||
var inv1 edwards25519.FieldElement
|
||||
edwards25519.FeSub(&inv1, &A.Z, &A.Y)
|
||||
edwards25519.FeMul(&inv1, &inv1, &A.X)
|
||||
edwards25519.FeInvert(&inv1, &inv1)
|
||||
|
||||
var t0, u edwards25519.FieldElement
|
||||
edwards25519.FeMul(&u, &inv1, &A.X)
|
||||
edwards25519.FeAdd(&t0, &A.Y, &A.Z)
|
||||
edwards25519.FeMul(&u, &u, &t0)
|
||||
|
||||
var v edwards25519.FieldElement
|
||||
edwards25519.FeMul(&v, &t0, &inv1)
|
||||
edwards25519.FeMul(&v, &v, &A.Z)
|
||||
edwards25519.FeMul(&v, &v, &sqrtMinusAPlus2)
|
||||
|
||||
var b edwards25519.FieldElement
|
||||
edwards25519.FeAdd(&b, &u, &edwards25519.A)
|
||||
|
||||
var c, b3, b7, b8 edwards25519.FieldElement
|
||||
edwards25519.FeSquare(&b3, &b) // 2
|
||||
edwards25519.FeMul(&b3, &b3, &b) // 3
|
||||
edwards25519.FeSquare(&c, &b3) // 6
|
||||
edwards25519.FeMul(&b7, &c, &b) // 7
|
||||
edwards25519.FeMul(&b8, &b7, &b) // 8
|
||||
edwards25519.FeMul(&c, &b7, &u)
|
||||
q58(&c, &c)
|
||||
|
||||
var chi edwards25519.FieldElement
|
||||
edwards25519.FeSquare(&chi, &c)
|
||||
edwards25519.FeSquare(&chi, &chi)
|
||||
|
||||
edwards25519.FeSquare(&t0, &u)
|
||||
edwards25519.FeMul(&chi, &chi, &t0)
|
||||
|
||||
edwards25519.FeSquare(&t0, &b7) // 14
|
||||
edwards25519.FeMul(&chi, &chi, &t0)
|
||||
edwards25519.FeNeg(&chi, &chi)
|
||||
|
||||
var chiBytes [32]byte
|
||||
edwards25519.FeToBytes(&chiBytes, &chi)
|
||||
// chi[1] is either 0 or 0xff
|
||||
if chiBytes[1] == 0xff {
|
||||
return false
|
||||
}
|
||||
|
||||
// Calculate r1 = sqrt(-u/(2*(u+A)))
|
||||
var r1 edwards25519.FieldElement
|
||||
edwards25519.FeMul(&r1, &c, &u)
|
||||
edwards25519.FeMul(&r1, &r1, &b3)
|
||||
edwards25519.FeMul(&r1, &r1, &sqrtMinusHalf)
|
||||
|
||||
var maybeSqrtM1 edwards25519.FieldElement
|
||||
edwards25519.FeSquare(&t0, &r1)
|
||||
edwards25519.FeMul(&t0, &t0, &b)
|
||||
edwards25519.FeAdd(&t0, &t0, &t0)
|
||||
edwards25519.FeAdd(&t0, &t0, &u)
|
||||
|
||||
edwards25519.FeOne(&maybeSqrtM1)
|
||||
edwards25519.FeCMove(&maybeSqrtM1, &edwards25519.SqrtM1, edwards25519.FeIsNonZero(&t0))
|
||||
edwards25519.FeMul(&r1, &r1, &maybeSqrtM1)
|
||||
|
||||
// Calculate r = sqrt(-(u+A)/(2u))
|
||||
var r edwards25519.FieldElement
|
||||
edwards25519.FeSquare(&t0, &c) // 2
|
||||
edwards25519.FeMul(&t0, &t0, &c) // 3
|
||||
edwards25519.FeSquare(&t0, &t0) // 6
|
||||
edwards25519.FeMul(&r, &t0, &c) // 7
|
||||
|
||||
edwards25519.FeSquare(&t0, &u) // 2
|
||||
edwards25519.FeMul(&t0, &t0, &u) // 3
|
||||
edwards25519.FeMul(&r, &r, &t0)
|
||||
|
||||
edwards25519.FeSquare(&t0, &b8) // 16
|
||||
edwards25519.FeMul(&t0, &t0, &b8) // 24
|
||||
edwards25519.FeMul(&t0, &t0, &b) // 25
|
||||
edwards25519.FeMul(&r, &r, &t0)
|
||||
edwards25519.FeMul(&r, &r, &sqrtMinusHalf)
|
||||
|
||||
edwards25519.FeSquare(&t0, &r)
|
||||
edwards25519.FeMul(&t0, &t0, &u)
|
||||
edwards25519.FeAdd(&t0, &t0, &t0)
|
||||
edwards25519.FeAdd(&t0, &t0, &b)
|
||||
edwards25519.FeOne(&maybeSqrtM1)
|
||||
edwards25519.FeCMove(&maybeSqrtM1, &edwards25519.SqrtM1, edwards25519.FeIsNonZero(&t0))
|
||||
edwards25519.FeMul(&r, &r, &maybeSqrtM1)
|
||||
|
||||
var vBytes [32]byte
|
||||
edwards25519.FeToBytes(&vBytes, &v)
|
||||
vInSquareRootImage := feBytesLE(&vBytes, &halfQMinus1Bytes)
|
||||
edwards25519.FeCMove(&r, &r1, vInSquareRootImage)
|
||||
|
||||
edwards25519.FeToBytes(publicKey, &u)
|
||||
edwards25519.FeToBytes(representative, &r)
|
||||
return true
|
||||
}
|
||||
|
||||
// q58 calculates out = z^((p-5)/8).
|
||||
func q58(out, z *edwards25519.FieldElement) {
|
||||
var t1, t2, t3 edwards25519.FieldElement
|
||||
var i int
|
||||
|
||||
edwards25519.FeSquare(&t1, z) // 2^1
|
||||
edwards25519.FeMul(&t1, &t1, z) // 2^1 + 2^0
|
||||
edwards25519.FeSquare(&t1, &t1) // 2^2 + 2^1
|
||||
edwards25519.FeSquare(&t2, &t1) // 2^3 + 2^2
|
||||
edwards25519.FeSquare(&t2, &t2) // 2^4 + 2^3
|
||||
edwards25519.FeMul(&t2, &t2, &t1) // 4,3,2,1
|
||||
edwards25519.FeMul(&t1, &t2, z) // 4..0
|
||||
edwards25519.FeSquare(&t2, &t1) // 5..1
|
||||
for i = 1; i < 5; i++ { // 9,8,7,6,5
|
||||
edwards25519.FeSquare(&t2, &t2)
|
||||
}
|
||||
edwards25519.FeMul(&t1, &t2, &t1) // 9,8,7,6,5,4,3,2,1,0
|
||||
edwards25519.FeSquare(&t2, &t1) // 10..1
|
||||
for i = 1; i < 10; i++ { // 19..10
|
||||
edwards25519.FeSquare(&t2, &t2)
|
||||
}
|
||||
edwards25519.FeMul(&t2, &t2, &t1) // 19..0
|
||||
edwards25519.FeSquare(&t3, &t2) // 20..1
|
||||
for i = 1; i < 20; i++ { // 39..20
|
||||
edwards25519.FeSquare(&t3, &t3)
|
||||
}
|
||||
edwards25519.FeMul(&t2, &t3, &t2) // 39..0
|
||||
edwards25519.FeSquare(&t2, &t2) // 40..1
|
||||
for i = 1; i < 10; i++ { // 49..10
|
||||
edwards25519.FeSquare(&t2, &t2)
|
||||
}
|
||||
edwards25519.FeMul(&t1, &t2, &t1) // 49..0
|
||||
edwards25519.FeSquare(&t2, &t1) // 50..1
|
||||
for i = 1; i < 50; i++ { // 99..50
|
||||
edwards25519.FeSquare(&t2, &t2)
|
||||
}
|
||||
edwards25519.FeMul(&t2, &t2, &t1) // 99..0
|
||||
edwards25519.FeSquare(&t3, &t2) // 100..1
|
||||
for i = 1; i < 100; i++ { // 199..100
|
||||
edwards25519.FeSquare(&t3, &t3)
|
||||
}
|
||||
edwards25519.FeMul(&t2, &t3, &t2) // 199..0
|
||||
edwards25519.FeSquare(&t2, &t2) // 200..1
|
||||
for i = 1; i < 50; i++ { // 249..50
|
||||
edwards25519.FeSquare(&t2, &t2)
|
||||
}
|
||||
edwards25519.FeMul(&t1, &t2, &t1) // 249..0
|
||||
edwards25519.FeSquare(&t1, &t1) // 250..1
|
||||
edwards25519.FeSquare(&t1, &t1) // 251..2
|
||||
edwards25519.FeMul(out, &t1, z) // 251..2,0
|
||||
}
|
||||
|
||||
// chi calculates out = z^((p-1)/2). The result is either 1, 0, or -1 depending
|
||||
// on whether z is a non-zero square, zero, or a non-square.
|
||||
func chi(out, z *edwards25519.FieldElement) {
|
||||
var t0, t1, t2, t3 edwards25519.FieldElement
|
||||
var i int
|
||||
|
||||
edwards25519.FeSquare(&t0, z) // 2^1
|
||||
edwards25519.FeMul(&t1, &t0, z) // 2^1 + 2^0
|
||||
edwards25519.FeSquare(&t0, &t1) // 2^2 + 2^1
|
||||
edwards25519.FeSquare(&t2, &t0) // 2^3 + 2^2
|
||||
edwards25519.FeSquare(&t2, &t2) // 4,3
|
||||
edwards25519.FeMul(&t2, &t2, &t0) // 4,3,2,1
|
||||
edwards25519.FeMul(&t1, &t2, z) // 4..0
|
||||
edwards25519.FeSquare(&t2, &t1) // 5..1
|
||||
for i = 1; i < 5; i++ { // 9,8,7,6,5
|
||||
edwards25519.FeSquare(&t2, &t2)
|
||||
}
|
||||
edwards25519.FeMul(&t1, &t2, &t1) // 9,8,7,6,5,4,3,2,1,0
|
||||
edwards25519.FeSquare(&t2, &t1) // 10..1
|
||||
for i = 1; i < 10; i++ { // 19..10
|
||||
edwards25519.FeSquare(&t2, &t2)
|
||||
}
|
||||
edwards25519.FeMul(&t2, &t2, &t1) // 19..0
|
||||
edwards25519.FeSquare(&t3, &t2) // 20..1
|
||||
for i = 1; i < 20; i++ { // 39..20
|
||||
edwards25519.FeSquare(&t3, &t3)
|
||||
}
|
||||
edwards25519.FeMul(&t2, &t3, &t2) // 39..0
|
||||
edwards25519.FeSquare(&t2, &t2) // 40..1
|
||||
for i = 1; i < 10; i++ { // 49..10
|
||||
edwards25519.FeSquare(&t2, &t2)
|
||||
}
|
||||
edwards25519.FeMul(&t1, &t2, &t1) // 49..0
|
||||
edwards25519.FeSquare(&t2, &t1) // 50..1
|
||||
for i = 1; i < 50; i++ { // 99..50
|
||||
edwards25519.FeSquare(&t2, &t2)
|
||||
}
|
||||
edwards25519.FeMul(&t2, &t2, &t1) // 99..0
|
||||
edwards25519.FeSquare(&t3, &t2) // 100..1
|
||||
for i = 1; i < 100; i++ { // 199..100
|
||||
edwards25519.FeSquare(&t3, &t3)
|
||||
}
|
||||
edwards25519.FeMul(&t2, &t3, &t2) // 199..0
|
||||
edwards25519.FeSquare(&t2, &t2) // 200..1
|
||||
for i = 1; i < 50; i++ { // 249..50
|
||||
edwards25519.FeSquare(&t2, &t2)
|
||||
}
|
||||
edwards25519.FeMul(&t1, &t2, &t1) // 249..0
|
||||
edwards25519.FeSquare(&t1, &t1) // 250..1
|
||||
for i = 1; i < 4; i++ { // 253..4
|
||||
edwards25519.FeSquare(&t1, &t1)
|
||||
}
|
||||
edwards25519.FeMul(out, &t1, &t0) // 253..4,2,1
|
||||
}
|
||||
|
||||
// RepresentativeToPublicKey converts a uniform representative value for a
|
||||
// curve25519 public key, as produced by ScalarBaseMult, to a curve25519 public
|
||||
// key.
|
||||
func RepresentativeToPublicKey(publicKey, representative *[32]byte) {
|
||||
var rr2, v, e edwards25519.FieldElement
|
||||
edwards25519.FeFromBytes(&rr2, representative)
|
||||
|
||||
edwards25519.FeSquare2(&rr2, &rr2)
|
||||
rr2[0]++
|
||||
edwards25519.FeInvert(&rr2, &rr2)
|
||||
edwards25519.FeMul(&v, &edwards25519.A, &rr2)
|
||||
edwards25519.FeNeg(&v, &v)
|
||||
|
||||
var v2, v3 edwards25519.FieldElement
|
||||
edwards25519.FeSquare(&v2, &v)
|
||||
edwards25519.FeMul(&v3, &v, &v2)
|
||||
edwards25519.FeAdd(&e, &v3, &v)
|
||||
edwards25519.FeMul(&v2, &v2, &edwards25519.A)
|
||||
edwards25519.FeAdd(&e, &v2, &e)
|
||||
chi(&e, &e)
|
||||
var eBytes [32]byte
|
||||
edwards25519.FeToBytes(&eBytes, &e)
|
||||
// eBytes[1] is either 0 (for e = 1) or 0xff (for e = -1)
|
||||
eIsMinus1 := int32(eBytes[1]) & 1
|
||||
var negV edwards25519.FieldElement
|
||||
edwards25519.FeNeg(&negV, &v)
|
||||
edwards25519.FeCMove(&v, &negV, eIsMinus1)
|
||||
|
||||
edwards25519.FeZero(&v2)
|
||||
edwards25519.FeCMove(&v2, &edwards25519.A, eIsMinus1)
|
||||
edwards25519.FeSub(&v, &v, &v2)
|
||||
|
||||
edwards25519.FeToBytes(publicKey, &v)
|
||||
}
|
69
vendor/github.com/dchest/siphash/README.md
generated
vendored
Normal file
69
vendor/github.com/dchest/siphash/README.md
generated
vendored
Normal file
@ -0,0 +1,69 @@
|
||||
SipHash (Go)
|
||||
============
|
||||
|
||||
[](https://travis-ci.org/dchest/siphash)
|
||||
|
||||
Go implementation of SipHash-2-4, a fast short-input PRF created by
|
||||
Jean-Philippe Aumasson and Daniel J. Bernstein (http://131002.net/siphash/).
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
$ go get github.com/dchest/siphash
|
||||
|
||||
## Usage
|
||||
|
||||
import "github.com/dchest/siphash"
|
||||
|
||||
There are two ways to use this package.
|
||||
The slower one is to use the standard hash.Hash64 interface:
|
||||
|
||||
h := siphash.New(key)
|
||||
h.Write([]byte("Hello"))
|
||||
sum := h.Sum(nil) // returns 8-byte []byte
|
||||
|
||||
or
|
||||
|
||||
sum64 := h.Sum64() // returns uint64
|
||||
|
||||
The faster one is to use Hash() function, which takes two uint64 parts of
|
||||
16-byte key and a byte slice, and returns uint64 hash:
|
||||
|
||||
sum64 := siphash.Hash(key0, key1, []byte("Hello"))
|
||||
|
||||
The keys and output are little-endian.
|
||||
|
||||
|
||||
## Functions
|
||||
|
||||
### func Hash(k0, k1 uint64, p []byte) uint64
|
||||
|
||||
Hash returns the 64-bit SipHash-2-4 of the given byte slice with two
|
||||
64-bit parts of 128-bit key: k0 and k1.
|
||||
|
||||
### func Hash128(k0, k1 uint64, p []byte) (uint64, uint64)
|
||||
|
||||
Hash128 returns the 128-bit SipHash-2-4 of the given byte slice with two
|
||||
64-bit parts of 128-bit key: k0 and k1.
|
||||
|
||||
Note that 128-bit SipHash is considered experimental by SipHash authors at this time.
|
||||
|
||||
### func New(key []byte) hash.Hash64
|
||||
|
||||
New returns a new hash.Hash64 computing SipHash-2-4 with 16-byte key.
|
||||
|
||||
### func New128(key []byte) hash.Hash
|
||||
|
||||
New128 returns a new hash.Hash computing SipHash-2-4 with 16-byte key and 16-byte output.
|
||||
|
||||
Note that 16-byte output is considered experimental by SipHash authors at this time.
|
||||
|
||||
|
||||
## Public domain dedication
|
||||
|
||||
Written by Dmitry Chestnykh and Damian Gryski.
|
||||
|
||||
To the extent possible under law, the authors have dedicated all copyright
|
||||
and related and neighboring rights to this software to the public domain
|
||||
worldwide. This software is distributed without any warranty.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
148
vendor/github.com/dchest/siphash/blocks.go
generated
vendored
Normal file
148
vendor/github.com/dchest/siphash/blocks.go
generated
vendored
Normal file
@ -0,0 +1,148 @@
|
||||
// +build !arm,!amd64 appengine gccgo
|
||||
|
||||
package siphash
|
||||
|
||||
func once(d *digest) {
|
||||
blocks(d, d.x[:])
|
||||
}
|
||||
|
||||
func finalize(d *digest) uint64 {
|
||||
d0 := *d
|
||||
once(&d0)
|
||||
|
||||
v0, v1, v2, v3 := d0.v0, d0.v1, d0.v2, d0.v3
|
||||
v2 ^= 0xff
|
||||
|
||||
// Round 1.
|
||||
v0 += v1
|
||||
v1 = v1<<13 | v1>>(64-13)
|
||||
v1 ^= v0
|
||||
v0 = v0<<32 | v0>>(64-32)
|
||||
|
||||
v2 += v3
|
||||
v3 = v3<<16 | v3>>(64-16)
|
||||
v3 ^= v2
|
||||
|
||||
v0 += v3
|
||||
v3 = v3<<21 | v3>>(64-21)
|
||||
v3 ^= v0
|
||||
|
||||
v2 += v1
|
||||
v1 = v1<<17 | v1>>(64-17)
|
||||
v1 ^= v2
|
||||
v2 = v2<<32 | v2>>(64-32)
|
||||
|
||||
// Round 2.
|
||||
v0 += v1
|
||||
v1 = v1<<13 | v1>>(64-13)
|
||||
v1 ^= v0
|
||||
v0 = v0<<32 | v0>>(64-32)
|
||||
|
||||
v2 += v3
|
||||
v3 = v3<<16 | v3>>(64-16)
|
||||
v3 ^= v2
|
||||
|
||||
v0 += v3
|
||||
v3 = v3<<21 | v3>>(64-21)
|
||||
v3 ^= v0
|
||||
|
||||
v2 += v1
|
||||
v1 = v1<<17 | v1>>(64-17)
|
||||
v1 ^= v2
|
||||
v2 = v2<<32 | v2>>(64-32)
|
||||
|
||||
// Round 3.
|
||||
v0 += v1
|
||||
v1 = v1<<13 | v1>>(64-13)
|
||||
v1 ^= v0
|
||||
v0 = v0<<32 | v0>>(64-32)
|
||||
|
||||
v2 += v3
|
||||
v3 = v3<<16 | v3>>(64-16)
|
||||
v3 ^= v2
|
||||
|
||||
v0 += v3
|
||||
v3 = v3<<21 | v3>>(64-21)
|
||||
v3 ^= v0
|
||||
|
||||
v2 += v1
|
||||
v1 = v1<<17 | v1>>(64-17)
|
||||
v1 ^= v2
|
||||
v2 = v2<<32 | v2>>(64-32)
|
||||
|
||||
// Round 4.
|
||||
v0 += v1
|
||||
v1 = v1<<13 | v1>>(64-13)
|
||||
v1 ^= v0
|
||||
v0 = v0<<32 | v0>>(64-32)
|
||||
|
||||
v2 += v3
|
||||
v3 = v3<<16 | v3>>(64-16)
|
||||
v3 ^= v2
|
||||
|
||||
v0 += v3
|
||||
v3 = v3<<21 | v3>>(64-21)
|
||||
v3 ^= v0
|
||||
|
||||
v2 += v1
|
||||
v1 = v1<<17 | v1>>(64-17)
|
||||
v1 ^= v2
|
||||
v2 = v2<<32 | v2>>(64-32)
|
||||
|
||||
return v0 ^ v1 ^ v2 ^ v3
|
||||
}
|
||||
|
||||
func blocks(d *digest, p []uint8) {
|
||||
v0, v1, v2, v3 := d.v0, d.v1, d.v2, d.v3
|
||||
|
||||
for len(p) >= BlockSize {
|
||||
m := uint64(p[0]) | uint64(p[1])<<8 | uint64(p[2])<<16 | uint64(p[3])<<24 |
|
||||
uint64(p[4])<<32 | uint64(p[5])<<40 | uint64(p[6])<<48 | uint64(p[7])<<56
|
||||
|
||||
v3 ^= m
|
||||
|
||||
// Round 1.
|
||||
v0 += v1
|
||||
v1 = v1<<13 | v1>>(64-13)
|
||||
v1 ^= v0
|
||||
v0 = v0<<32 | v0>>(64-32)
|
||||
|
||||
v2 += v3
|
||||
v3 = v3<<16 | v3>>(64-16)
|
||||
v3 ^= v2
|
||||
|
||||
v0 += v3
|
||||
v3 = v3<<21 | v3>>(64-21)
|
||||
v3 ^= v0
|
||||
|
||||
v2 += v1
|
||||
v1 = v1<<17 | v1>>(64-17)
|
||||
v1 ^= v2
|
||||
v2 = v2<<32 | v2>>(64-32)
|
||||
|
||||
// Round 2.
|
||||
v0 += v1
|
||||
v1 = v1<<13 | v1>>(64-13)
|
||||
v1 ^= v0
|
||||
v0 = v0<<32 | v0>>(64-32)
|
||||
|
||||
v2 += v3
|
||||
v3 = v3<<16 | v3>>(64-16)
|
||||
v3 ^= v2
|
||||
|
||||
v0 += v3
|
||||
v3 = v3<<21 | v3>>(64-21)
|
||||
v3 ^= v0
|
||||
|
||||
v2 += v1
|
||||
v1 = v1<<17 | v1>>(64-17)
|
||||
v1 ^= v2
|
||||
v2 = v2<<32 | v2>>(64-32)
|
||||
|
||||
v0 ^= m
|
||||
|
||||
p = p[BlockSize:]
|
||||
}
|
||||
|
||||
d.v0, d.v1, d.v2, d.v3 = v0, v1, v2, v3
|
||||
}
|
86
vendor/github.com/dchest/siphash/blocks_amd64.s
generated
vendored
Normal file
86
vendor/github.com/dchest/siphash/blocks_amd64.s
generated
vendored
Normal file
@ -0,0 +1,86 @@
|
||||
// +build amd64,!appengine,!gccgo
|
||||
|
||||
#define ROUND(v0, v1, v2, v3) \
|
||||
ADDQ v1, v0; \
|
||||
RORQ $51, v1; \
|
||||
ADDQ v3, v2; \
|
||||
XORQ v0, v1; \
|
||||
RORQ $48, v3; \
|
||||
RORQ $32, v0; \
|
||||
XORQ v2, v3; \
|
||||
ADDQ v1, v2; \
|
||||
ADDQ v3, v0; \
|
||||
RORQ $43, v3; \
|
||||
RORQ $47, v1; \
|
||||
XORQ v0, v3; \
|
||||
XORQ v2, v1; \
|
||||
RORQ $32, v2
|
||||
|
||||
// blocks(d *digest, data []uint8)
|
||||
TEXT ·blocks(SB),4,$0-32
|
||||
MOVQ d+0(FP), BX
|
||||
MOVQ 0(BX), R9 // R9 = v0
|
||||
MOVQ 8(BX), R10 // R10 = v1
|
||||
MOVQ 16(BX), R11 // R11 = v2
|
||||
MOVQ 24(BX), R12 // R12 = v3
|
||||
MOVQ p_base+8(FP), DI // DI = *uint64
|
||||
MOVQ p_len+16(FP), SI // SI = nblocks
|
||||
XORL DX, DX // DX = index (0)
|
||||
SHRQ $3, SI // SI /= 8
|
||||
body:
|
||||
CMPQ DX, SI
|
||||
JGE end
|
||||
MOVQ 0(DI)(DX*8), CX // CX = m
|
||||
XORQ CX, R12
|
||||
ROUND(R9, R10, R11, R12)
|
||||
ROUND(R9, R10, R11, R12)
|
||||
XORQ CX, R9
|
||||
ADDQ $1, DX
|
||||
JMP body
|
||||
end:
|
||||
MOVQ R9, 0(BX)
|
||||
MOVQ R10, 8(BX)
|
||||
MOVQ R11, 16(BX)
|
||||
MOVQ R12, 24(BX)
|
||||
RET
|
||||
|
||||
// once(d *digest)
|
||||
TEXT ·once(SB),4,$0-8
|
||||
MOVQ d+0(FP), BX
|
||||
MOVQ 0(BX), R9 // R9 = v0
|
||||
MOVQ 8(BX), R10 // R10 = v1
|
||||
MOVQ 16(BX), R11 // R11 = v2
|
||||
MOVQ 24(BX), R12 // R12 = v3
|
||||
MOVQ 48(BX), CX // CX = d.x[:]
|
||||
XORQ CX, R12
|
||||
ROUND(R9, R10, R11, R12)
|
||||
ROUND(R9, R10, R11, R12)
|
||||
XORQ CX, R9
|
||||
MOVQ R9, 0(BX)
|
||||
MOVQ R10, 8(BX)
|
||||
MOVQ R11, 16(BX)
|
||||
MOVQ R12, 24(BX)
|
||||
RET
|
||||
|
||||
// finalize(d *digest) uint64
|
||||
TEXT ·finalize(SB),4,$0-16
|
||||
MOVQ d+0(FP), BX
|
||||
MOVQ 0(BX), R9 // R9 = v0
|
||||
MOVQ 8(BX), R10 // R10 = v1
|
||||
MOVQ 16(BX), R11 // R11 = v2
|
||||
MOVQ 24(BX), R12 // R12 = v3
|
||||
MOVQ 48(BX), CX // CX = d.x[:]
|
||||
XORQ CX, R12
|
||||
ROUND(R9, R10, R11, R12)
|
||||
ROUND(R9, R10, R11, R12)
|
||||
XORQ CX, R9
|
||||
NOTB R11
|
||||
ROUND(R9, R10, R11, R12)
|
||||
ROUND(R9, R10, R11, R12)
|
||||
ROUND(R9, R10, R11, R12)
|
||||
ROUND(R9, R10, R11, R12)
|
||||
XORQ R12, R11
|
||||
XORQ R10, R9
|
||||
XORQ R11, R9
|
||||
MOVQ R9, ret+8(FP)
|
||||
RET
|
144
vendor/github.com/dchest/siphash/blocks_arm.s
generated
vendored
Normal file
144
vendor/github.com/dchest/siphash/blocks_arm.s
generated
vendored
Normal file
@ -0,0 +1,144 @@
|
||||
#include "textflag.h"
|
||||
#define R10 g
|
||||
#define ROUND()\
|
||||
ADD.S R2,R0,R0;\
|
||||
ADC R3,R1,R1;\
|
||||
EOR R2<<13,R0,R8;\
|
||||
EOR R3>>19,R8,R8;\
|
||||
EOR R2>>19,R1,R11;\
|
||||
EOR R3<<13,R11,R11;\
|
||||
ADD.S R6,R4,R4;\
|
||||
ADC R7,R5,R5;\
|
||||
EOR R6<<16,R4,R2;\
|
||||
EOR R7>>16,R2,R2;\
|
||||
EOR R6>>16,R5,R3;\
|
||||
EOR R7<<16,R3,R3;\
|
||||
ADD.S R2,R1,R1;\
|
||||
ADC R3,R0,R0;\
|
||||
EOR R2<<21,R1,R6;\
|
||||
EOR R3>>11,R6,R6;\
|
||||
EOR R2>>11,R0,R7;\
|
||||
EOR R3<<21,R7,R7;\
|
||||
ADD.S R8,R4,R4;\
|
||||
ADC R11,R5,R5;\
|
||||
EOR R8<<17,R4,R2;\
|
||||
EOR R11>>15,R2,R2;\
|
||||
EOR R8>>15,R5,R3;\
|
||||
EOR R11<<17,R3,R3;\
|
||||
ADD.S R2,R1,R1;\
|
||||
ADC R3,R0,R0;\
|
||||
EOR R2<<13,R1,R8;\
|
||||
EOR R3>>19,R8,R8;\
|
||||
EOR R2>>19,R0,R11;\
|
||||
EOR R3<<13,R11,R11;\
|
||||
ADD.S R6,R5,R5;\
|
||||
ADC R7,R4,R4;\
|
||||
EOR R6<<16,R5,R2;\
|
||||
EOR R7>>16,R2,R2;\
|
||||
EOR R6>>16,R4,R3;\
|
||||
EOR R7<<16,R3,R3;\
|
||||
ADD.S R2,R0,R0;\
|
||||
ADC R3,R1,R1;\
|
||||
EOR R2<<21,R0,R6;\
|
||||
EOR R3>>11,R6,R6;\
|
||||
EOR R2>>11,R1,R7;\
|
||||
EOR R3<<21,R7,R7;\
|
||||
ADD.S R8,R5,R5;\
|
||||
ADC R11,R4,R4;\
|
||||
EOR R8<<17,R5,R2;\
|
||||
EOR R11>>15,R2,R2;\
|
||||
EOR R8>>15,R4,R3;\
|
||||
EOR R11<<17,R3,R3;\
|
||||
|
||||
// once(d *digest)
|
||||
TEXT ·once(SB),NOSPLIT,$4-4
|
||||
MOVW d+0(FP),R8
|
||||
MOVM.IA (R8),[R0,R1,R2,R3,R4,R5,R6,R7]
|
||||
MOVW 48(R8),R12
|
||||
MOVW 52(R8),R14
|
||||
EOR R12,R6,R6
|
||||
EOR R14,R7,R7
|
||||
ROUND()
|
||||
EOR R12,R0,R0
|
||||
EOR R14,R1,R1
|
||||
MOVW d+0(FP),R8
|
||||
MOVM.IA [R0,R1,R2,R3,R4,R5,R6,R7],(R8)
|
||||
RET
|
||||
|
||||
// finalize(d *digest) uint64
|
||||
TEXT ·finalize(SB),NOSPLIT,$4-12
|
||||
MOVW d+0(FP),R8
|
||||
MOVM.IA (R8),[R0,R1,R2,R3,R4,R5,R6,R7]
|
||||
MOVW 48(R8),R12
|
||||
MOVW 52(R8),R14
|
||||
EOR R12,R6,R6
|
||||
EOR R14,R7,R7
|
||||
ROUND()
|
||||
EOR R12,R0,R0
|
||||
EOR R14,R1,R1
|
||||
EOR $255,R4
|
||||
ROUND()
|
||||
ROUND()
|
||||
EOR R2,R0,R0
|
||||
EOR R3,R1,R1
|
||||
EOR R6,R4,R4
|
||||
EOR R7,R5,R5
|
||||
EOR R4,R0,R0
|
||||
EOR R5,R1,R1
|
||||
MOVW R0,ret_lo+4(FP)
|
||||
MOVW R1,ret_hi+8(FP)
|
||||
RET
|
||||
|
||||
// blocks(d *digest, data []uint8)
|
||||
TEXT ·blocks(SB),NOSPLIT,$8-16
|
||||
MOVW R10,sav-8(SP)
|
||||
MOVW d+0(FP),R8
|
||||
MOVM.IA (R8),[R0,R1,R2,R3,R4,R5,R6,R7]
|
||||
MOVW p+4(FP),R10
|
||||
MOVW p_len+8(FP),R11
|
||||
ADD R10,R11,R11
|
||||
MOVW R11,endp-4(SP)
|
||||
AND.S $3,R10,R8
|
||||
BNE blocksunaligned
|
||||
blocksloop:
|
||||
MOVM.IA.W (R10),[R12,R14]
|
||||
EOR R12,R6,R6
|
||||
EOR R14,R7,R7
|
||||
ROUND()
|
||||
EOR R12,R0,R0
|
||||
EOR R14,R1,R1
|
||||
MOVW endp-4(SP),R11
|
||||
CMP R11,R10
|
||||
BLO blocksloop
|
||||
MOVW d+0(FP),R8
|
||||
MOVM.IA [R0,R1,R2,R3,R4,R5,R6,R7],(R8)
|
||||
MOVW sav-8(SP),R10
|
||||
RET
|
||||
blocksunaligned:
|
||||
MOVB (R10),R12
|
||||
MOVB 1(R10),R11
|
||||
ORR R11<<8,R12,R12
|
||||
MOVB 2(R10),R11
|
||||
ORR R11<<16,R12,R12
|
||||
MOVB 3(R10),R11
|
||||
ORR R11<<24,R12,R12
|
||||
MOVB 4(R10),R14
|
||||
MOVB 5(R10),R11
|
||||
ORR R11<<8,R14,R14
|
||||
MOVB 6(R10),R11
|
||||
ORR R11<<16,R14,R14
|
||||
MOVB 7(R10),R11
|
||||
ORR R11<<24,R14,R14
|
||||
ADD $8,R10,R10
|
||||
EOR R12,R6,R6
|
||||
EOR R14,R7,R7
|
||||
ROUND()
|
||||
EOR R12,R0,R0
|
||||
EOR R14,R1,R1
|
||||
MOVW endp-4(SP),R11
|
||||
CMP R11,R10
|
||||
BLO blocksunaligned
|
||||
MOVW d+0(FP),R8
|
||||
MOVM.IA [R0,R1,R2,R3,R4,R5,R6,R7],(R8)
|
||||
MOVW sav-8(SP),R10
|
||||
RET
|
216
vendor/github.com/dchest/siphash/hash.go
generated
vendored
Normal file
216
vendor/github.com/dchest/siphash/hash.go
generated
vendored
Normal file
@ -0,0 +1,216 @@
|
||||
// +build !arm,!amd64 appengine gccgo
|
||||
|
||||
// Written in 2012 by Dmitry Chestnykh.
|
||||
//
|
||||
// To the extent possible under law, the author have dedicated all copyright
|
||||
// and related and neighboring rights to this software to the public domain
|
||||
// worldwide. This software is distributed without any warranty.
|
||||
// http://creativecommons.org/publicdomain/zero/1.0/
|
||||
|
||||
package siphash
|
||||
|
||||
// Hash returns the 64-bit SipHash-2-4 of the given byte slice with two 64-bit
|
||||
// parts of 128-bit key: k0 and k1.
|
||||
func Hash(k0, k1 uint64, p []byte) uint64 {
|
||||
// Initialization.
|
||||
v0 := k0 ^ 0x736f6d6570736575
|
||||
v1 := k1 ^ 0x646f72616e646f6d
|
||||
v2 := k0 ^ 0x6c7967656e657261
|
||||
v3 := k1 ^ 0x7465646279746573
|
||||
t := uint64(len(p)) << 56
|
||||
|
||||
// Compression.
|
||||
for len(p) >= BlockSize {
|
||||
m := uint64(p[0]) | uint64(p[1])<<8 | uint64(p[2])<<16 | uint64(p[3])<<24 |
|
||||
uint64(p[4])<<32 | uint64(p[5])<<40 | uint64(p[6])<<48 | uint64(p[7])<<56
|
||||
v3 ^= m
|
||||
|
||||
// Round 1.
|
||||
v0 += v1
|
||||
v1 = v1<<13 | v1>>(64-13)
|
||||
v1 ^= v0
|
||||
v0 = v0<<32 | v0>>(64-32)
|
||||
|
||||
v2 += v3
|
||||
v3 = v3<<16 | v3>>(64-16)
|
||||
v3 ^= v2
|
||||
|
||||
v0 += v3
|
||||
v3 = v3<<21 | v3>>(64-21)
|
||||
v3 ^= v0
|
||||
|
||||
v2 += v1
|
||||
v1 = v1<<17 | v1>>(64-17)
|
||||
v1 ^= v2
|
||||
v2 = v2<<32 | v2>>(64-32)
|
||||
|
||||
// Round 2.
|
||||
v0 += v1
|
||||
v1 = v1<<13 | v1>>(64-13)
|
||||
v1 ^= v0
|
||||
v0 = v0<<32 | v0>>(64-32)
|
||||
|
||||
v2 += v3
|
||||
v3 = v3<<16 | v3>>(64-16)
|
||||
v3 ^= v2
|
||||
|
||||
v0 += v3
|
||||
v3 = v3<<21 | v3>>(64-21)
|
||||
v3 ^= v0
|
||||
|
||||
v2 += v1
|
||||
v1 = v1<<17 | v1>>(64-17)
|
||||
v1 ^= v2
|
||||
v2 = v2<<32 | v2>>(64-32)
|
||||
|
||||
v0 ^= m
|
||||
p = p[BlockSize:]
|
||||
}
|
||||
|
||||
// Compress last block.
|
||||
switch len(p) {
|
||||
case 7:
|
||||
t |= uint64(p[6]) << 48
|
||||
fallthrough
|
||||
case 6:
|
||||
t |= uint64(p[5]) << 40
|
||||
fallthrough
|
||||
case 5:
|
||||
t |= uint64(p[4]) << 32
|
||||
fallthrough
|
||||
case 4:
|
||||
t |= uint64(p[3]) << 24
|
||||
fallthrough
|
||||
case 3:
|
||||
t |= uint64(p[2]) << 16
|
||||
fallthrough
|
||||
case 2:
|
||||
t |= uint64(p[1]) << 8
|
||||
fallthrough
|
||||
case 1:
|
||||
t |= uint64(p[0])
|
||||
}
|
||||
|
||||
v3 ^= t
|
||||
|
||||
// Round 1.
|
||||
v0 += v1
|
||||
v1 = v1<<13 | v1>>(64-13)
|
||||
v1 ^= v0
|
||||
v0 = v0<<32 | v0>>(64-32)
|
||||
|
||||
v2 += v3
|
||||
v3 = v3<<16 | v3>>(64-16)
|
||||
v3 ^= v2
|
||||
|
||||
v0 += v3
|
||||
v3 = v3<<21 | v3>>(64-21)
|
||||
v3 ^= v0
|
||||
|
||||
v2 += v1
|
||||
v1 = v1<<17 | v1>>(64-17)
|
||||
v1 ^= v2
|
||||
v2 = v2<<32 | v2>>(64-32)
|
||||
|
||||
// Round 2.
|
||||
v0 += v1
|
||||
v1 = v1<<13 | v1>>(64-13)
|
||||
v1 ^= v0
|
||||
v0 = v0<<32 | v0>>(64-32)
|
||||
|
||||
v2 += v3
|
||||
v3 = v3<<16 | v3>>(64-16)
|
||||
v3 ^= v2
|
||||
|
||||
v0 += v3
|
||||
v3 = v3<<21 | v3>>(64-21)
|
||||
v3 ^= v0
|
||||
|
||||
v2 += v1
|
||||
v1 = v1<<17 | v1>>(64-17)
|
||||
v1 ^= v2
|
||||
v2 = v2<<32 | v2>>(64-32)
|
||||
|
||||
v0 ^= t
|
||||
|
||||
// Finalization.
|
||||
v2 ^= 0xff
|
||||
|
||||
// Round 1.
|
||||
v0 += v1
|
||||
v1 = v1<<13 | v1>>(64-13)
|
||||
v1 ^= v0
|
||||
v0 = v0<<32 | v0>>(64-32)
|
||||
|
||||
v2 += v3
|
||||
v3 = v3<<16 | v3>>(64-16)
|
||||
v3 ^= v2
|
||||
|
||||
v0 += v3
|
||||
v3 = v3<<21 | v3>>(64-21)
|
||||
v3 ^= v0
|
||||
|
||||
v2 += v1
|
||||
v1 = v1<<17 | v1>>(64-17)
|
||||
v1 ^= v2
|
||||
v2 = v2<<32 | v2>>(64-32)
|
||||
|
||||
// Round 2.
|
||||
v0 += v1
|
||||
v1 = v1<<13 | v1>>(64-13)
|
||||
v1 ^= v0
|
||||
v0 = v0<<32 | v0>>(64-32)
|
||||
|
||||
v2 += v3
|
||||
v3 = v3<<16 | v3>>(64-16)
|
||||
v3 ^= v2
|
||||
|
||||
v0 += v3
|
||||
v3 = v3<<21 | v3>>(64-21)
|
||||
v3 ^= v0
|
||||
|
||||
v2 += v1
|
||||
v1 = v1<<17 | v1>>(64-17)
|
||||
v1 ^= v2
|
||||
v2 = v2<<32 | v2>>(64-32)
|
||||
|
||||
// Round 3.
|
||||
v0 += v1
|
||||
v1 = v1<<13 | v1>>(64-13)
|
||||
v1 ^= v0
|
||||
v0 = v0<<32 | v0>>(64-32)
|
||||
|
||||
v2 += v3
|
||||
v3 = v3<<16 | v3>>(64-16)
|
||||
v3 ^= v2
|
||||
|
||||
v0 += v3
|
||||
v3 = v3<<21 | v3>>(64-21)
|
||||
v3 ^= v0
|
||||
|
||||
v2 += v1
|
||||
v1 = v1<<17 | v1>>(64-17)
|
||||
v1 ^= v2
|
||||
v2 = v2<<32 | v2>>(64-32)
|
||||
|
||||
// Round 4.
|
||||
v0 += v1
|
||||
v1 = v1<<13 | v1>>(64-13)
|
||||
v1 ^= v0
|
||||
v0 = v0<<32 | v0>>(64-32)
|
||||
|
||||
v2 += v3
|
||||
v3 = v3<<16 | v3>>(64-16)
|
||||
v3 ^= v2
|
||||
|
||||
v0 += v3
|
||||
v3 = v3<<21 | v3>>(64-21)
|
||||
v3 ^= v0
|
||||
|
||||
v2 += v1
|
||||
v1 = v1<<17 | v1>>(64-17)
|
||||
v1 ^= v2
|
||||
v2 = v2<<32 | v2>>(64-32)
|
||||
|
||||
return v0 ^ v1 ^ v2 ^ v3
|
||||
}
|
302
vendor/github.com/dchest/siphash/hash128.go
generated
vendored
Normal file
302
vendor/github.com/dchest/siphash/hash128.go
generated
vendored
Normal file
@ -0,0 +1,302 @@
|
||||
// +build !arm,!amd64 appengine gccgo
|
||||
// Written in 2012 by Dmitry Chestnykh.
|
||||
// Modifications 2014 for 128-bit hash function by Damian Gryski.
|
||||
//
|
||||
// To the extent possible under law, the authors have dedicated all copyright
|
||||
// and related and neighboring rights to this software to the public domain
|
||||
// worldwide. This software is distributed without any warranty.
|
||||
// http://creativecommons.org/publicdomain/zero/1.0/
|
||||
|
||||
package siphash
|
||||
|
||||
// Hash returns the 128-bit SipHash-2-4 of the given byte slice with two 64-bit
|
||||
// parts of 128-bit key: k0 and k1.
|
||||
//
|
||||
// Note that 128-bit SipHash is considered experimental by SipHash authors at this time.
|
||||
func Hash128(k0, k1 uint64, p []byte) (uint64, uint64) {
|
||||
// Initialization.
|
||||
v0 := k0 ^ 0x736f6d6570736575
|
||||
v1 := k1 ^ 0x646f72616e646f6d
|
||||
v2 := k0 ^ 0x6c7967656e657261
|
||||
v3 := k1 ^ 0x7465646279746573
|
||||
t := uint64(len(p)) << 56
|
||||
|
||||
v1 ^= 0xee
|
||||
|
||||
// Compression.
|
||||
for len(p) >= BlockSize {
|
||||
m := uint64(p[0]) | uint64(p[1])<<8 | uint64(p[2])<<16 | uint64(p[3])<<24 |
|
||||
uint64(p[4])<<32 | uint64(p[5])<<40 | uint64(p[6])<<48 | uint64(p[7])<<56
|
||||
v3 ^= m
|
||||
|
||||
// Round 1.
|
||||
v0 += v1
|
||||
v1 = v1<<13 | v1>>(64-13)
|
||||
v1 ^= v0
|
||||
v0 = v0<<32 | v0>>(64-32)
|
||||
|
||||
v2 += v3
|
||||
v3 = v3<<16 | v3>>(64-16)
|
||||
v3 ^= v2
|
||||
|
||||
v0 += v3
|
||||
v3 = v3<<21 | v3>>(64-21)
|
||||
v3 ^= v0
|
||||
|
||||
v2 += v1
|
||||
v1 = v1<<17 | v1>>(64-17)
|
||||
v1 ^= v2
|
||||
v2 = v2<<32 | v2>>(64-32)
|
||||
|
||||
// Round 2.
|
||||
v0 += v1
|
||||
v1 = v1<<13 | v1>>(64-13)
|
||||
v1 ^= v0
|
||||
v0 = v0<<32 | v0>>(64-32)
|
||||
|
||||
v2 += v3
|
||||
v3 = v3<<16 | v3>>(64-16)
|
||||
v3 ^= v2
|
||||
|
||||
v0 += v3
|
||||
v3 = v3<<21 | v3>>(64-21)
|
||||
v3 ^= v0
|
||||
|
||||
v2 += v1
|
||||
v1 = v1<<17 | v1>>(64-17)
|
||||
v1 ^= v2
|
||||
v2 = v2<<32 | v2>>(64-32)
|
||||
|
||||
v0 ^= m
|
||||
p = p[BlockSize:]
|
||||
}
|
||||
|
||||
// Compress last block.
|
||||
switch len(p) {
|
||||
case 7:
|
||||
t |= uint64(p[6]) << 48
|
||||
fallthrough
|
||||
case 6:
|
||||
t |= uint64(p[5]) << 40
|
||||
fallthrough
|
||||
case 5:
|
||||
t |= uint64(p[4]) << 32
|
||||
fallthrough
|
||||
case 4:
|
||||
t |= uint64(p[3]) << 24
|
||||
fallthrough
|
||||
case 3:
|
||||
t |= uint64(p[2]) << 16
|
||||
fallthrough
|
||||
case 2:
|
||||
t |= uint64(p[1]) << 8
|
||||
fallthrough
|
||||
case 1:
|
||||
t |= uint64(p[0])
|
||||
}
|
||||
|
||||
v3 ^= t
|
||||
|
||||
// Round 1.
|
||||
v0 += v1
|
||||
v1 = v1<<13 | v1>>(64-13)
|
||||
v1 ^= v0
|
||||
v0 = v0<<32 | v0>>(64-32)
|
||||
|
||||
v2 += v3
|
||||
v3 = v3<<16 | v3>>(64-16)
|
||||
v3 ^= v2
|
||||
|
||||
v0 += v3
|
||||
v3 = v3<<21 | v3>>(64-21)
|
||||
v3 ^= v0
|
||||
|
||||
v2 += v1
|
||||
v1 = v1<<17 | v1>>(64-17)
|
||||
v1 ^= v2
|
||||
v2 = v2<<32 | v2>>(64-32)
|
||||
|
||||
// Round 2.
|
||||
v0 += v1
|
||||
v1 = v1<<13 | v1>>(64-13)
|
||||
v1 ^= v0
|
||||
v0 = v0<<32 | v0>>(64-32)
|
||||
|
||||
v2 += v3
|
||||
v3 = v3<<16 | v3>>(64-16)
|
||||
v3 ^= v2
|
||||
|
||||
v0 += v3
|
||||
v3 = v3<<21 | v3>>(64-21)
|
||||
v3 ^= v0
|
||||
|
||||
v2 += v1
|
||||
v1 = v1<<17 | v1>>(64-17)
|
||||
v1 ^= v2
|
||||
v2 = v2<<32 | v2>>(64-32)
|
||||
|
||||
v0 ^= t
|
||||
|
||||
// Finalization.
|
||||
v2 ^= 0xee
|
||||
|
||||
// Round 1.
|
||||
v0 += v1
|
||||
v1 = v1<<13 | v1>>(64-13)
|
||||
v1 ^= v0
|
||||
v0 = v0<<32 | v0>>(64-32)
|
||||
|
||||
v2 += v3
|
||||
v3 = v3<<16 | v3>>(64-16)
|
||||
v3 ^= v2
|
||||
|
||||
v0 += v3
|
||||
v3 = v3<<21 | v3>>(64-21)
|
||||
v3 ^= v0
|
||||
|
||||
v2 += v1
|
||||
v1 = v1<<17 | v1>>(64-17)
|
||||
v1 ^= v2
|
||||
v2 = v2<<32 | v2>>(64-32)
|
||||
|
||||
// Round 2.
|
||||
v0 += v1
|
||||
v1 = v1<<13 | v1>>(64-13)
|
||||
v1 ^= v0
|
||||
v0 = v0<<32 | v0>>(64-32)
|
||||
|
||||
v2 += v3
|
||||
v3 = v3<<16 | v3>>(64-16)
|
||||
v3 ^= v2
|
||||
|
||||
v0 += v3
|
||||
v3 = v3<<21 | v3>>(64-21)
|
||||
v3 ^= v0
|
||||
|
||||
v2 += v1
|
||||
v1 = v1<<17 | v1>>(64-17)
|
||||
v1 ^= v2
|
||||
v2 = v2<<32 | v2>>(64-32)
|
||||
|
||||
// Round 3.
|
||||
v0 += v1
|
||||
v1 = v1<<13 | v1>>(64-13)
|
||||
v1 ^= v0
|
||||
v0 = v0<<32 | v0>>(64-32)
|
||||
|
||||
v2 += v3
|
||||
v3 = v3<<16 | v3>>(64-16)
|
||||
v3 ^= v2
|
||||
|
||||
v0 += v3
|
||||
v3 = v3<<21 | v3>>(64-21)
|
||||
v3 ^= v0
|
||||
|
||||
v2 += v1
|
||||
v1 = v1<<17 | v1>>(64-17)
|
||||
v1 ^= v2
|
||||
v2 = v2<<32 | v2>>(64-32)
|
||||
|
||||
// Round 4.
|
||||
v0 += v1
|
||||
v1 = v1<<13 | v1>>(64-13)
|
||||
v1 ^= v0
|
||||
v0 = v0<<32 | v0>>(64-32)
|
||||
|
||||
v2 += v3
|
||||
v3 = v3<<16 | v3>>(64-16)
|
||||
v3 ^= v2
|
||||
|
||||
v0 += v3
|
||||
v3 = v3<<21 | v3>>(64-21)
|
||||
v3 ^= v0
|
||||
|
||||
v2 += v1
|
||||
v1 = v1<<17 | v1>>(64-17)
|
||||
v1 ^= v2
|
||||
v2 = v2<<32 | v2>>(64-32)
|
||||
|
||||
r0 := v0 ^ v1 ^ v2 ^ v3
|
||||
|
||||
v1 ^= 0xdd
|
||||
|
||||
// Round 1.
|
||||
v0 += v1
|
||||
v1 = v1<<13 | v1>>(64-13)
|
||||
v1 ^= v0
|
||||
v0 = v0<<32 | v0>>(64-32)
|
||||
|
||||
v2 += v3
|
||||
v3 = v3<<16 | v3>>(64-16)
|
||||
v3 ^= v2
|
||||
|
||||
v0 += v3
|
||||
v3 = v3<<21 | v3>>(64-21)
|
||||
v3 ^= v0
|
||||
|
||||
v2 += v1
|
||||
v1 = v1<<17 | v1>>(64-17)
|
||||
v1 ^= v2
|
||||
v2 = v2<<32 | v2>>(64-32)
|
||||
|
||||
// Round 2.
|
||||
v0 += v1
|
||||
v1 = v1<<13 | v1>>(64-13)
|
||||
v1 ^= v0
|
||||
v0 = v0<<32 | v0>>(64-32)
|
||||
|
||||
v2 += v3
|
||||
v3 = v3<<16 | v3>>(64-16)
|
||||
v3 ^= v2
|
||||
|
||||
v0 += v3
|
||||
v3 = v3<<21 | v3>>(64-21)
|
||||
v3 ^= v0
|
||||
|
||||
v2 += v1
|
||||
v1 = v1<<17 | v1>>(64-17)
|
||||
v1 ^= v2
|
||||
v2 = v2<<32 | v2>>(64-32)
|
||||
|
||||
// Round 3.
|
||||
v0 += v1
|
||||
v1 = v1<<13 | v1>>(64-13)
|
||||
v1 ^= v0
|
||||
v0 = v0<<32 | v0>>(64-32)
|
||||
|
||||
v2 += v3
|
||||
v3 = v3<<16 | v3>>(64-16)
|
||||
v3 ^= v2
|
||||
|
||||
v0 += v3
|
||||
v3 = v3<<21 | v3>>(64-21)
|
||||
v3 ^= v0
|
||||
|
||||
v2 += v1
|
||||
v1 = v1<<17 | v1>>(64-17)
|
||||
v1 ^= v2
|
||||
v2 = v2<<32 | v2>>(64-32)
|
||||
|
||||
// Round 4.
|
||||
v0 += v1
|
||||
v1 = v1<<13 | v1>>(64-13)
|
||||
v1 ^= v0
|
||||
v0 = v0<<32 | v0>>(64-32)
|
||||
|
||||
v2 += v3
|
||||
v3 = v3<<16 | v3>>(64-16)
|
||||
v3 ^= v2
|
||||
|
||||
v0 += v3
|
||||
v3 = v3<<21 | v3>>(64-21)
|
||||
v3 ^= v0
|
||||
|
||||
v2 += v1
|
||||
v1 = v1<<17 | v1>>(64-17)
|
||||
v1 ^= v2
|
||||
v2 = v2<<32 | v2>>(64-32)
|
||||
|
||||
r1 := v0 ^ v1 ^ v2 ^ v3
|
||||
|
||||
return r0, r1
|
||||
}
|
292
vendor/github.com/dchest/siphash/hash128_amd64.s
generated
vendored
Normal file
292
vendor/github.com/dchest/siphash/hash128_amd64.s
generated
vendored
Normal file
@ -0,0 +1,292 @@
|
||||
// +build amd64,!appengine,!gccgo
|
||||
|
||||
// This is a translation of the gcc output of FloodyBerry's pure-C public
|
||||
// domain siphash implementation at https://github.com/floodyberry/siphash
|
||||
|
||||
// This assembly code has been modified from the 64-bit output to the experiment 128-bit output.
|
||||
|
||||
// SI = v0
|
||||
// AX = v1
|
||||
// CX = v2
|
||||
// DX = v3
|
||||
|
||||
// func Hash128(k0, k1 uint64, b []byte) (r0 uint64, r1 uint64)
|
||||
TEXT ·Hash128(SB),4,$0-56
|
||||
MOVQ k0+0(FP),CX
|
||||
MOVQ $0x736F6D6570736575,R9
|
||||
MOVQ k1+8(FP),DI
|
||||
MOVQ $0x6C7967656E657261,BX
|
||||
MOVQ $0x646F72616E646F6D,AX
|
||||
MOVQ b_len+24(FP),DX
|
||||
XORQ $0xEE,AX
|
||||
MOVQ DX,R11
|
||||
MOVQ DX,R10
|
||||
XORQ CX,R9
|
||||
XORQ CX,BX
|
||||
MOVQ $0x7465646279746573,CX
|
||||
XORQ DI,AX
|
||||
XORQ DI,CX
|
||||
SHLQ $0x38,R11
|
||||
XORQ DI,DI
|
||||
MOVQ b_base+16(FP),SI
|
||||
ANDQ $0xFFFFFFFFFFFFFFF8,R10
|
||||
JE afterLoop
|
||||
XCHGQ AX,AX
|
||||
loopBody:
|
||||
MOVQ 0(SI)(DI*1),R8
|
||||
ADDQ AX,R9
|
||||
RORQ $0x33,AX
|
||||
XORQ R9,AX
|
||||
RORQ $0x20,R9
|
||||
ADDQ $0x8,DI
|
||||
XORQ R8,CX
|
||||
ADDQ CX,BX
|
||||
RORQ $0x30,CX
|
||||
XORQ BX,CX
|
||||
ADDQ AX,BX
|
||||
RORQ $0x2F,AX
|
||||
ADDQ CX,R9
|
||||
RORQ $0x2B,CX
|
||||
XORQ BX,AX
|
||||
XORQ R9,CX
|
||||
RORQ $0x20,BX
|
||||
ADDQ AX,R9
|
||||
ADDQ CX,BX
|
||||
RORQ $0x33,AX
|
||||
RORQ $0x30,CX
|
||||
XORQ R9,AX
|
||||
XORQ BX,CX
|
||||
RORQ $0x20,R9
|
||||
ADDQ AX,BX
|
||||
ADDQ CX,R9
|
||||
RORQ $0x2F,AX
|
||||
RORQ $0x2B,CX
|
||||
XORQ BX,AX
|
||||
RORQ $0x20,BX
|
||||
XORQ R9,CX
|
||||
XORQ R8,R9
|
||||
CMPQ R10,DI
|
||||
JA loopBody
|
||||
afterLoop:
|
||||
SUBQ R10,DX
|
||||
|
||||
CMPQ DX,$0x7
|
||||
JA afterSwitch
|
||||
|
||||
// no support for jump tables
|
||||
|
||||
CMPQ DX,$0x7
|
||||
JE sw7
|
||||
|
||||
CMPQ DX,$0x6
|
||||
JE sw6
|
||||
|
||||
CMPQ DX,$0x5
|
||||
JE sw5
|
||||
|
||||
CMPQ DX,$0x4
|
||||
JE sw4
|
||||
|
||||
CMPQ DX,$0x3
|
||||
JE sw3
|
||||
|
||||
CMPQ DX,$0x2
|
||||
JE sw2
|
||||
|
||||
CMPQ DX,$0x1
|
||||
JE sw1
|
||||
|
||||
JMP afterSwitch
|
||||
|
||||
sw7: MOVBQZX 6(SI)(DI*1),DX
|
||||
SHLQ $0x30,DX
|
||||
ORQ DX,R11
|
||||
sw6: MOVBQZX 0x5(SI)(DI*1),DX
|
||||
SHLQ $0x28,DX
|
||||
ORQ DX,R11
|
||||
sw5: MOVBQZX 0x4(SI)(DI*1),DX
|
||||
SHLQ $0x20,DX
|
||||
ORQ DX,R11
|
||||
sw4: MOVBQZX 0x3(SI)(DI*1),DX
|
||||
SHLQ $0x18,DX
|
||||
ORQ DX,R11
|
||||
sw3: MOVBQZX 0x2(SI)(DI*1),DX
|
||||
SHLQ $0x10,DX
|
||||
ORQ DX,R11
|
||||
sw2: MOVBQZX 0x1(SI)(DI*1),DX
|
||||
SHLQ $0x8,DX
|
||||
ORQ DX,R11
|
||||
sw1: MOVBQZX 0(SI)(DI*1),DX
|
||||
ORQ DX,R11
|
||||
afterSwitch:
|
||||
LEAQ (AX)(R9*1),SI
|
||||
XORQ R11,CX
|
||||
RORQ $0x33,AX
|
||||
ADDQ CX,BX
|
||||
MOVQ CX,DX
|
||||
XORQ SI,AX
|
||||
RORQ $0x30,DX
|
||||
RORQ $0x20,SI
|
||||
LEAQ 0(BX)(AX*1),CX
|
||||
XORQ BX,DX
|
||||
RORQ $0x2F,AX
|
||||
ADDQ DX,SI
|
||||
RORQ $0x2B,DX
|
||||
XORQ CX,AX
|
||||
XORQ SI,DX
|
||||
RORQ $0x20,CX
|
||||
ADDQ AX,SI
|
||||
RORQ $0x33,AX
|
||||
ADDQ DX,CX
|
||||
XORQ SI,AX
|
||||
RORQ $0x30,DX
|
||||
RORQ $0x20,SI
|
||||
XORQ CX,DX
|
||||
ADDQ AX,CX
|
||||
RORQ $0x2F,AX
|
||||
ADDQ DX,SI
|
||||
XORQ CX,AX
|
||||
RORQ $0x2B,DX
|
||||
RORQ $0x20,CX
|
||||
XORQ SI,DX
|
||||
XORQ R11,SI
|
||||
XORB $0xEE,CL
|
||||
ADDQ AX,SI
|
||||
RORQ $0x33,AX
|
||||
ADDQ DX,CX
|
||||
RORQ $0x30,DX
|
||||
XORQ SI,AX
|
||||
XORQ CX,DX
|
||||
RORQ $0x20,SI
|
||||
ADDQ AX,CX
|
||||
ADDQ DX,SI
|
||||
RORQ $0x2F,AX
|
||||
RORQ $0x2B,DX
|
||||
XORQ CX,AX
|
||||
XORQ SI,DX
|
||||
RORQ $0x20,CX
|
||||
ADDQ AX,SI
|
||||
ADDQ DX,CX
|
||||
RORQ $0x33,AX
|
||||
RORQ $0x30,DX
|
||||
XORQ SI,AX
|
||||
RORQ $0x20,SI
|
||||
XORQ CX,DX
|
||||
ADDQ AX,CX
|
||||
RORQ $0x2F,AX
|
||||
ADDQ DX,SI
|
||||
RORQ $0x2B,DX
|
||||
XORQ CX,AX
|
||||
XORQ SI,DX
|
||||
RORQ $0x20,CX
|
||||
ADDQ AX,SI
|
||||
ADDQ DX,CX
|
||||
RORQ $0x33,AX
|
||||
RORQ $0x30,DX
|
||||
XORQ CX,DX
|
||||
XORQ SI,AX
|
||||
RORQ $0x20,SI
|
||||
ADDQ DX,SI
|
||||
ADDQ AX,CX
|
||||
RORQ $0x2F,AX
|
||||
XORQ CX,AX
|
||||
RORQ $0x2B,DX
|
||||
RORQ $0x20,CX
|
||||
XORQ SI,DX
|
||||
|
||||
// gcc optimized the tail end of this function differently. However,
|
||||
// we need to preserve out registers to carry out the second stage of
|
||||
// the finalization. This is a duplicate of an earlier finalization
|
||||
// round.
|
||||
|
||||
ADDQ AX,SI
|
||||
RORQ $0x33,AX
|
||||
ADDQ DX,CX
|
||||
RORQ $0x30,DX
|
||||
XORQ SI,AX
|
||||
XORQ CX,DX
|
||||
RORQ $0x20,SI
|
||||
ADDQ AX,CX
|
||||
ADDQ DX,SI
|
||||
RORQ $0x2F,AX
|
||||
RORQ $0x2B,DX
|
||||
XORQ CX,AX
|
||||
XORQ SI,DX
|
||||
RORQ $0x20,CX
|
||||
|
||||
// Stuff the result into BX instead of AX as gcc had done
|
||||
|
||||
MOVQ SI,BX
|
||||
XORQ AX,BX
|
||||
XORQ DX,BX
|
||||
XORQ CX,BX
|
||||
MOVQ BX,ret+40(FP)
|
||||
|
||||
// Start the second finalization round
|
||||
|
||||
XORB $0xDD,AL
|
||||
ADDQ AX,SI
|
||||
RORQ $0x33,AX
|
||||
ADDQ DX,CX
|
||||
RORQ $0x30,DX
|
||||
XORQ SI,AX
|
||||
XORQ CX,DX
|
||||
RORQ $0x20,SI
|
||||
ADDQ AX,CX
|
||||
ADDQ DX,SI
|
||||
RORQ $0x2F,AX
|
||||
RORQ $0x2B,DX
|
||||
XORQ CX,AX
|
||||
XORQ SI,DX
|
||||
RORQ $0x20,CX
|
||||
ADDQ AX,SI
|
||||
ADDQ DX,CX
|
||||
RORQ $0x33,AX
|
||||
RORQ $0x30,DX
|
||||
XORQ SI,AX
|
||||
RORQ $0x20,SI
|
||||
XORQ CX,DX
|
||||
ADDQ AX,CX
|
||||
RORQ $0x2F,AX
|
||||
ADDQ DX,SI
|
||||
RORQ $0x2B,DX
|
||||
XORQ CX,AX
|
||||
XORQ SI,DX
|
||||
RORQ $0x20,CX
|
||||
ADDQ AX,SI
|
||||
ADDQ DX,CX
|
||||
RORQ $0x33,AX
|
||||
RORQ $0x30,DX
|
||||
XORQ CX,DX
|
||||
XORQ SI,AX
|
||||
RORQ $0x20,SI
|
||||
ADDQ DX,SI
|
||||
ADDQ AX,CX
|
||||
RORQ $0x2F,AX
|
||||
XORQ CX,AX
|
||||
RORQ $0x2B,DX
|
||||
RORQ $0x20,CX
|
||||
XORQ SI,DX
|
||||
|
||||
ADDQ AX,SI
|
||||
RORQ $0x33,AX
|
||||
ADDQ DX,CX
|
||||
RORQ $0x30,DX
|
||||
XORQ SI,AX
|
||||
XORQ CX,DX
|
||||
RORQ $0x20,SI
|
||||
ADDQ AX,CX
|
||||
ADDQ DX,SI
|
||||
RORQ $0x2F,AX
|
||||
RORQ $0x2B,DX
|
||||
XORQ CX,AX
|
||||
XORQ SI,DX
|
||||
RORQ $0x20,CX
|
||||
|
||||
MOVQ SI,BX
|
||||
XORQ AX,BX
|
||||
XORQ DX,BX
|
||||
XORQ CX,BX
|
||||
MOVQ BX,ret1+48(FP)
|
||||
|
||||
RET
|
169
vendor/github.com/dchest/siphash/hash128_arm.s
generated
vendored
Normal file
169
vendor/github.com/dchest/siphash/hash128_arm.s
generated
vendored
Normal file
@ -0,0 +1,169 @@
|
||||
#include "textflag.h"
|
||||
#define R10 g
|
||||
#define ROUND()\
|
||||
ADD.S R2,R0,R0;\
|
||||
ADC R3,R1,R1;\
|
||||
EOR R2<<13,R0,R8;\
|
||||
EOR R3>>19,R8,R8;\
|
||||
EOR R2>>19,R1,R11;\
|
||||
EOR R3<<13,R11,R11;\
|
||||
ADD.S R6,R4,R4;\
|
||||
ADC R7,R5,R5;\
|
||||
EOR R6<<16,R4,R2;\
|
||||
EOR R7>>16,R2,R2;\
|
||||
EOR R6>>16,R5,R3;\
|
||||
EOR R7<<16,R3,R3;\
|
||||
ADD.S R2,R1,R1;\
|
||||
ADC R3,R0,R0;\
|
||||
EOR R2<<21,R1,R6;\
|
||||
EOR R3>>11,R6,R6;\
|
||||
EOR R2>>11,R0,R7;\
|
||||
EOR R3<<21,R7,R7;\
|
||||
ADD.S R8,R4,R4;\
|
||||
ADC R11,R5,R5;\
|
||||
EOR R8<<17,R4,R2;\
|
||||
EOR R11>>15,R2,R2;\
|
||||
EOR R8>>15,R5,R3;\
|
||||
EOR R11<<17,R3,R3;\
|
||||
ADD.S R2,R1,R1;\
|
||||
ADC R3,R0,R0;\
|
||||
EOR R2<<13,R1,R8;\
|
||||
EOR R3>>19,R8,R8;\
|
||||
EOR R2>>19,R0,R11;\
|
||||
EOR R3<<13,R11,R11;\
|
||||
ADD.S R6,R5,R5;\
|
||||
ADC R7,R4,R4;\
|
||||
EOR R6<<16,R5,R2;\
|
||||
EOR R7>>16,R2,R2;\
|
||||
EOR R6>>16,R4,R3;\
|
||||
EOR R7<<16,R3,R3;\
|
||||
ADD.S R2,R0,R0;\
|
||||
ADC R3,R1,R1;\
|
||||
EOR R2<<21,R0,R6;\
|
||||
EOR R3>>11,R6,R6;\
|
||||
EOR R2>>11,R1,R7;\
|
||||
EOR R3<<21,R7,R7;\
|
||||
ADD.S R8,R5,R5;\
|
||||
ADC R11,R4,R4;\
|
||||
EOR R8<<17,R5,R2;\
|
||||
EOR R11>>15,R2,R2;\
|
||||
EOR R8>>15,R4,R3;\
|
||||
EOR R11<<17,R3,R3;\
|
||||
|
||||
// Hash128(k0, k1 uint64, b []byte) (uint64, uint64)
|
||||
TEXT ·Hash128(SB),NOSPLIT,$8-44
|
||||
MOVW R10,sav-8(SP)
|
||||
MOVW k0_lo+0(FP),R12
|
||||
MOVW k0_hi+4(FP),R14
|
||||
MOVW $0x70736575,R0
|
||||
MOVW $0x736f6d65,R1
|
||||
MOVW $0x6e657261,R4
|
||||
MOVW $0x6c796765,R5
|
||||
EOR R12,R0,R0
|
||||
EOR R14,R1,R1
|
||||
EOR R12,R4,R4
|
||||
EOR R14,R5,R5
|
||||
MOVW k1_lo+8(FP),R12
|
||||
MOVW k1_hi+12(FP),R14
|
||||
MOVW $0x6e646f83,R2
|
||||
MOVW $0x646f7261,R3
|
||||
MOVW $0x79746573,R6
|
||||
MOVW $0x74656462,R7
|
||||
EOR R12,R2,R2
|
||||
EOR R14,R3,R3
|
||||
EOR R12,R6,R6
|
||||
EOR R14,R7,R7
|
||||
MOVW b+16(FP),R10
|
||||
MOVW b_len+20(FP),R11
|
||||
ADD R10,R11,R11
|
||||
MOVW R11,endb-4(SP)
|
||||
hashloop128:
|
||||
MOVW endb-4(SP),R11
|
||||
SUB R10,R11,R11
|
||||
SUB.S $8,R11
|
||||
BLO hashend128
|
||||
MOVM.IA.W (R10),[R12,R14]
|
||||
EOR R12,R6,R6
|
||||
EOR R14,R7,R7
|
||||
ROUND()
|
||||
EOR R12,R0,R0
|
||||
EOR R14,R1,R1
|
||||
B hashloop128
|
||||
hashloop128unaligned:
|
||||
MOVW endb-4(SP),R11
|
||||
SUB R10,R11,R11
|
||||
SUB.S $8,R11
|
||||
BLO hashend128
|
||||
MOVB (R10),R12
|
||||
MOVB 1(R10),R11
|
||||
ORR R11<<8,R12,R12
|
||||
MOVB 2(R10),R11
|
||||
ORR R11<<16,R12,R12
|
||||
MOVB 3(R10),R11
|
||||
ORR R11<<24,R12,R12
|
||||
MOVB 4(R10),R14
|
||||
MOVB 5(R10),R11
|
||||
ORR R11<<8,R14,R14
|
||||
MOVB 6(R10),R11
|
||||
ORR R11<<16,R14,R14
|
||||
MOVB 7(R10),R11
|
||||
ORR R11<<24,R14,R14
|
||||
ADD $8,R10,R10
|
||||
EOR R12,R6,R6
|
||||
EOR R14,R7,R7
|
||||
ROUND()
|
||||
EOR R12,R0,R0
|
||||
EOR R14,R1,R1
|
||||
B hashloop128unaligned
|
||||
hashend128:
|
||||
MOVW $0x0,R12
|
||||
MOVW $0x0,R14
|
||||
RSB $0,R11,R11
|
||||
AND.S $7,R11
|
||||
BEQ hashlast128
|
||||
MOVW (R10),R12
|
||||
SLL $3,R11
|
||||
AND $63,R11
|
||||
SUB.S $32,R11,R11
|
||||
BEQ hashlast128
|
||||
BLO hashhi128
|
||||
MOVW R12<<R11,R12
|
||||
MOVW R12>>R11,R12
|
||||
B hashlast128
|
||||
hashhi128:
|
||||
ADD $32,R11
|
||||
MOVW 4(R10),R14
|
||||
MOVW R14<<R11,R14
|
||||
MOVW R14>>R11,R14
|
||||
hashlast128:
|
||||
MOVW b_len+20(FP),R11
|
||||
ORR R11<<24,R14,R14
|
||||
EOR R12,R6,R6
|
||||
EOR R14,R7,R7
|
||||
ROUND()
|
||||
EOR R12,R0,R0
|
||||
EOR R14,R1,R1
|
||||
EOR $238,R4
|
||||
ROUND()
|
||||
ROUND()
|
||||
EOR R0,R2,R12
|
||||
EOR R1,R3,R14
|
||||
EOR R4,R12,R12
|
||||
EOR R5,R14,R14
|
||||
EOR R6,R12,R12
|
||||
EOR R7,R14,R14
|
||||
MOVW R12,ret_lo+28(FP)
|
||||
MOVW R14,ret_hi+32(FP)
|
||||
EOR $221,R2
|
||||
ROUND()
|
||||
ROUND()
|
||||
EOR R0,R2,R12
|
||||
EOR R1,R3,R14
|
||||
EOR R4,R12,R12
|
||||
EOR R5,R14,R14
|
||||
EOR R6,R12,R12
|
||||
EOR R7,R14,R14
|
||||
MOVW R12,unnamed_lo+36(FP)
|
||||
MOVW R14,unnamed_hi+40(FP)
|
||||
MOVW sav-8(SP),R10
|
||||
RET
|
201
vendor/github.com/dchest/siphash/hash_amd64.s
generated
vendored
Normal file
201
vendor/github.com/dchest/siphash/hash_amd64.s
generated
vendored
Normal file
@ -0,0 +1,201 @@
|
||||
// +build amd64,!appengine,!gccgo
|
||||
|
||||
// This is a translation of the gcc output of FloodyBerry's pure-C public
|
||||
// domain siphash implementation at https://github.com/floodyberry/siphash
|
||||
// func Hash(k0, k1 uint64, b []byte) uint64
|
||||
TEXT ·Hash(SB),4,$0-48
|
||||
MOVQ k0+0(FP),CX
|
||||
MOVQ $0x736F6D6570736575,R9
|
||||
MOVQ k1+8(FP),DI
|
||||
MOVQ $0x6C7967656E657261,BX
|
||||
MOVQ $0x646F72616E646F6D,AX
|
||||
MOVQ b_len+24(FP),DX
|
||||
MOVQ DX,R11
|
||||
MOVQ DX,R10
|
||||
XORQ CX,R9
|
||||
XORQ CX,BX
|
||||
MOVQ $0x7465646279746573,CX
|
||||
XORQ DI,AX
|
||||
XORQ DI,CX
|
||||
SHLQ $0x38,R11
|
||||
XORQ DI,DI
|
||||
MOVQ b_base+16(FP),SI
|
||||
ANDQ $0xFFFFFFFFFFFFFFF8,R10
|
||||
JE afterLoop
|
||||
XCHGQ AX,AX
|
||||
loopBody:
|
||||
MOVQ 0(SI)(DI*1),R8
|
||||
ADDQ AX,R9
|
||||
RORQ $0x33,AX
|
||||
XORQ R9,AX
|
||||
RORQ $0x20,R9
|
||||
ADDQ $0x8,DI
|
||||
XORQ R8,CX
|
||||
ADDQ CX,BX
|
||||
RORQ $0x30,CX
|
||||
XORQ BX,CX
|
||||
ADDQ AX,BX
|
||||
RORQ $0x2F,AX
|
||||
ADDQ CX,R9
|
||||
RORQ $0x2B,CX
|
||||
XORQ BX,AX
|
||||
XORQ R9,CX
|
||||
RORQ $0x20,BX
|
||||
ADDQ AX,R9
|
||||
ADDQ CX,BX
|
||||
RORQ $0x33,AX
|
||||
RORQ $0x30,CX
|
||||
XORQ R9,AX
|
||||
XORQ BX,CX
|
||||
RORQ $0x20,R9
|
||||
ADDQ AX,BX
|
||||
ADDQ CX,R9
|
||||
RORQ $0x2F,AX
|
||||
RORQ $0x2B,CX
|
||||
XORQ BX,AX
|
||||
RORQ $0x20,BX
|
||||
XORQ R9,CX
|
||||
XORQ R8,R9
|
||||
CMPQ R10,DI
|
||||
JA loopBody
|
||||
afterLoop:
|
||||
SUBQ R10,DX
|
||||
|
||||
CMPQ DX,$0x7
|
||||
JA afterSwitch
|
||||
|
||||
// no support for jump tables
|
||||
|
||||
CMPQ DX,$0x7
|
||||
JE sw7
|
||||
|
||||
CMPQ DX,$0x6
|
||||
JE sw6
|
||||
|
||||
CMPQ DX,$0x5
|
||||
JE sw5
|
||||
|
||||
CMPQ DX,$0x4
|
||||
JE sw4
|
||||
|
||||
CMPQ DX,$0x3
|
||||
JE sw3
|
||||
|
||||
CMPQ DX,$0x2
|
||||
JE sw2
|
||||
|
||||
CMPQ DX,$0x1
|
||||
JE sw1
|
||||
|
||||
JMP afterSwitch
|
||||
|
||||
sw7: MOVBQZX 6(SI)(DI*1),DX
|
||||
SHLQ $0x30,DX
|
||||
ORQ DX,R11
|
||||
sw6: MOVBQZX 0x5(SI)(DI*1),DX
|
||||
SHLQ $0x28,DX
|
||||
ORQ DX,R11
|
||||
sw5: MOVBQZX 0x4(SI)(DI*1),DX
|
||||
SHLQ $0x20,DX
|
||||
ORQ DX,R11
|
||||
sw4: MOVBQZX 0x3(SI)(DI*1),DX
|
||||
SHLQ $0x18,DX
|
||||
ORQ DX,R11
|
||||
sw3: MOVBQZX 0x2(SI)(DI*1),DX
|
||||
SHLQ $0x10,DX
|
||||
ORQ DX,R11
|
||||
sw2: MOVBQZX 0x1(SI)(DI*1),DX
|
||||
SHLQ $0x8,DX
|
||||
ORQ DX,R11
|
||||
sw1: MOVBQZX 0(SI)(DI*1),DX
|
||||
ORQ DX,R11
|
||||
afterSwitch:
|
||||
LEAQ (AX)(R9*1),SI
|
||||
XORQ R11,CX
|
||||
RORQ $0x33,AX
|
||||
ADDQ CX,BX
|
||||
MOVQ CX,DX
|
||||
XORQ SI,AX
|
||||
RORQ $0x30,DX
|
||||
RORQ $0x20,SI
|
||||
LEAQ 0(BX)(AX*1),CX
|
||||
XORQ BX,DX
|
||||
RORQ $0x2F,AX
|
||||
ADDQ DX,SI
|
||||
RORQ $0x2B,DX
|
||||
XORQ CX,AX
|
||||
XORQ SI,DX
|
||||
RORQ $0x20,CX
|
||||
ADDQ AX,SI
|
||||
RORQ $0x33,AX
|
||||
ADDQ DX,CX
|
||||
XORQ SI,AX
|
||||
RORQ $0x30,DX
|
||||
RORQ $0x20,SI
|
||||
XORQ CX,DX
|
||||
ADDQ AX,CX
|
||||
RORQ $0x2F,AX
|
||||
ADDQ DX,SI
|
||||
XORQ CX,AX
|
||||
RORQ $0x2B,DX
|
||||
RORQ $0x20,CX
|
||||
XORQ SI,DX
|
||||
XORQ R11,SI
|
||||
XORB $0xFF,CL
|
||||
ADDQ AX,SI
|
||||
RORQ $0x33,AX
|
||||
ADDQ DX,CX
|
||||
RORQ $0x30,DX
|
||||
XORQ SI,AX
|
||||
XORQ CX,DX
|
||||
RORQ $0x20,SI
|
||||
ADDQ AX,CX
|
||||
ADDQ DX,SI
|
||||
RORQ $0x2F,AX
|
||||
RORQ $0x2B,DX
|
||||
XORQ CX,AX
|
||||
XORQ SI,DX
|
||||
RORQ $0x20,CX
|
||||
ADDQ AX,SI
|
||||
ADDQ DX,CX
|
||||
RORQ $0x33,AX
|
||||
RORQ $0x30,DX
|
||||
XORQ SI,AX
|
||||
RORQ $0x20,SI
|
||||
XORQ CX,DX
|
||||
ADDQ AX,CX
|
||||
RORQ $0x2F,AX
|
||||
ADDQ DX,SI
|
||||
RORQ $0x2B,DX
|
||||
XORQ CX,AX
|
||||
XORQ SI,DX
|
||||
RORQ $0x20,CX
|
||||
ADDQ AX,SI
|
||||
ADDQ DX,CX
|
||||
RORQ $0x33,AX
|
||||
RORQ $0x30,DX
|
||||
XORQ CX,DX
|
||||
XORQ SI,AX
|
||||
RORQ $0x20,SI
|
||||
ADDQ DX,SI
|
||||
ADDQ AX,CX
|
||||
RORQ $0x2F,AX
|
||||
XORQ CX,AX
|
||||
RORQ $0x2B,DX
|
||||
RORQ $0x20,CX
|
||||
XORQ SI,DX
|
||||
ADDQ AX,SI
|
||||
RORQ $0x33,AX
|
||||
ADDQ DX,CX
|
||||
XORQ SI,AX
|
||||
RORQ $0x30,DX
|
||||
XORQ CX,DX
|
||||
ADDQ AX,CX
|
||||
RORQ $0x2F,AX
|
||||
XORQ CX,AX
|
||||
RORQ $0x2B,DX
|
||||
RORQ $0x20,CX
|
||||
XORQ DX,AX
|
||||
XORQ CX,AX
|
||||
MOVQ AX,ret+40(FP)
|
||||
RET
|
160
vendor/github.com/dchest/siphash/hash_arm.s
generated
vendored
Normal file
160
vendor/github.com/dchest/siphash/hash_arm.s
generated
vendored
Normal file
@ -0,0 +1,160 @@
|
||||
#include "textflag.h"
|
||||
#define R10 g
|
||||
#define ROUND()\
|
||||
ADD.S R2,R0,R0;\
|
||||
ADC R3,R1,R1;\
|
||||
EOR R2<<13,R0,R8;\
|
||||
EOR R3>>19,R8,R8;\
|
||||
EOR R2>>19,R1,R11;\
|
||||
EOR R3<<13,R11,R11;\
|
||||
ADD.S R6,R4,R4;\
|
||||
ADC R7,R5,R5;\
|
||||
EOR R6<<16,R4,R2;\
|
||||
EOR R7>>16,R2,R2;\
|
||||
EOR R6>>16,R5,R3;\
|
||||
EOR R7<<16,R3,R3;\
|
||||
ADD.S R2,R1,R1;\
|
||||
ADC R3,R0,R0;\
|
||||
EOR R2<<21,R1,R6;\
|
||||
EOR R3>>11,R6,R6;\
|
||||
EOR R2>>11,R0,R7;\
|
||||
EOR R3<<21,R7,R7;\
|
||||
ADD.S R8,R4,R4;\
|
||||
ADC R11,R5,R5;\
|
||||
EOR R8<<17,R4,R2;\
|
||||
EOR R11>>15,R2,R2;\
|
||||
EOR R8>>15,R5,R3;\
|
||||
EOR R11<<17,R3,R3;\
|
||||
ADD.S R2,R1,R1;\
|
||||
ADC R3,R0,R0;\
|
||||
EOR R2<<13,R1,R8;\
|
||||
EOR R3>>19,R8,R8;\
|
||||
EOR R2>>19,R0,R11;\
|
||||
EOR R3<<13,R11,R11;\
|
||||
ADD.S R6,R5,R5;\
|
||||
ADC R7,R4,R4;\
|
||||
EOR R6<<16,R5,R2;\
|
||||
EOR R7>>16,R2,R2;\
|
||||
EOR R6>>16,R4,R3;\
|
||||
EOR R7<<16,R3,R3;\
|
||||
ADD.S R2,R0,R0;\
|
||||
ADC R3,R1,R1;\
|
||||
EOR R2<<21,R0,R6;\
|
||||
EOR R3>>11,R6,R6;\
|
||||
EOR R2>>11,R1,R7;\
|
||||
EOR R3<<21,R7,R7;\
|
||||
ADD.S R8,R5,R5;\
|
||||
ADC R11,R4,R4;\
|
||||
EOR R8<<17,R5,R2;\
|
||||
EOR R11>>15,R2,R2;\
|
||||
EOR R8>>15,R4,R3;\
|
||||
EOR R11<<17,R3,R3;\
|
||||
|
||||
// Hash(k0, k1 uint64, b []byte) uint64
|
||||
TEXT ·Hash(SB),NOSPLIT,$8-36
|
||||
MOVW R10,sav-8(SP)
|
||||
MOVW k0_lo+0(FP),R12
|
||||
MOVW k0_hi+4(FP),R14
|
||||
MOVW $0x70736575,R0
|
||||
MOVW $0x736f6d65,R1
|
||||
MOVW $0x6e657261,R4
|
||||
MOVW $0x6c796765,R5
|
||||
EOR R12,R0,R0
|
||||
EOR R14,R1,R1
|
||||
EOR R12,R4,R4
|
||||
EOR R14,R5,R5
|
||||
MOVW k1_lo+8(FP),R12
|
||||
MOVW k1_hi+12(FP),R14
|
||||
MOVW $0x6e646f6d,R2
|
||||
MOVW $0x646f7261,R3
|
||||
MOVW $0x79746573,R6
|
||||
MOVW $0x74656462,R7
|
||||
EOR R12,R2,R2
|
||||
EOR R14,R3,R3
|
||||
EOR R12,R6,R6
|
||||
EOR R14,R7,R7
|
||||
MOVW b+16(FP),R10
|
||||
MOVW b_len+20(FP),R11
|
||||
ADD R10,R11,R11
|
||||
MOVW R11,endb-4(SP)
|
||||
AND.S $3,R10,R8
|
||||
BNE hashloopunaligned
|
||||
hashloop:
|
||||
MOVW endb-4(SP),R11
|
||||
SUB R10,R11,R11
|
||||
SUB.S $8,R11
|
||||
BLO hashend
|
||||
MOVM.IA.W (R10),[R12,R14]
|
||||
EOR R12,R6,R6
|
||||
EOR R14,R7,R7
|
||||
ROUND()
|
||||
EOR R12,R0,R0
|
||||
EOR R14,R1,R1
|
||||
B hashloop
|
||||
hashloopunaligned:
|
||||
MOVW endb-4(SP),R11
|
||||
SUB R10,R11,R11
|
||||
SUB.S $8,R11
|
||||
BLO hashend
|
||||
MOVB (R10),R12
|
||||
MOVB 1(R10),R11
|
||||
ORR R11<<8,R12,R12
|
||||
MOVB 2(R10),R11
|
||||
ORR R11<<16,R12,R12
|
||||
MOVB 3(R10),R11
|
||||
ORR R11<<24,R12,R12
|
||||
MOVB 4(R10),R14
|
||||
MOVB 5(R10),R11
|
||||
ORR R11<<8,R14,R14
|
||||
MOVB 6(R10),R11
|
||||
ORR R11<<16,R14,R14
|
||||
MOVB 7(R10),R11
|
||||
ORR R11<<24,R14,R14
|
||||
ADD $8,R10,R10
|
||||
EOR R12,R6,R6
|
||||
EOR R14,R7,R7
|
||||
ROUND()
|
||||
EOR R12,R0,R0
|
||||
EOR R14,R1,R1
|
||||
B hashloopunaligned
|
||||
hashend:
|
||||
MOVW $0x0,R12
|
||||
MOVW $0x0,R14
|
||||
RSB $0,R11,R11
|
||||
AND.S $7,R11
|
||||
BEQ hashlast
|
||||
MOVW (R10),R12
|
||||
SLL $3,R11
|
||||
AND $63,R11
|
||||
SUB.S $32,R11,R11
|
||||
BEQ hashlast
|
||||
BLO hashhi
|
||||
MOVW R12<<R11,R12
|
||||
MOVW R12>>R11,R12
|
||||
B hashlast
|
||||
hashhi:
|
||||
ADD $32,R11
|
||||
MOVW 4(R10),R14
|
||||
MOVW R14<<R11,R14
|
||||
MOVW R14>>R11,R14
|
||||
hashlast:
|
||||
MOVW b_len+20(FP),R11
|
||||
ORR R11<<24,R14,R14
|
||||
EOR R12,R6,R6
|
||||
EOR R14,R7,R7
|
||||
ROUND()
|
||||
EOR R12,R0,R0
|
||||
EOR R14,R1,R1
|
||||
EOR $255,R4
|
||||
ROUND()
|
||||
ROUND()
|
||||
EOR R2,R0,R0
|
||||
EOR R3,R1,R1
|
||||
EOR R6,R4,R4
|
||||
EOR R7,R5,R5
|
||||
EOR R4,R0,R0
|
||||
EOR R5,R1,R1
|
||||
MOVW sav-8(SP),R10
|
||||
MOVW R0,ret_lo+28(FP)
|
||||
MOVW R1,ret_hi+32(FP)
|
||||
RET
|
33
vendor/github.com/dchest/siphash/hash_asm.go
generated
vendored
Normal file
33
vendor/github.com/dchest/siphash/hash_asm.go
generated
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
// +build arm amd64,!appengine,!gccgo
|
||||
|
||||
// Written in 2012 by Dmitry Chestnykh.
|
||||
//
|
||||
// To the extent possible under law, the author have dedicated all copyright
|
||||
// and related and neighboring rights to this software to the public domain
|
||||
// worldwide. This software is distributed without any warranty.
|
||||
// http://creativecommons.org/publicdomain/zero/1.0/
|
||||
|
||||
// This file contains a function definition for use with assembly implementations of Hash()
|
||||
|
||||
package siphash
|
||||
|
||||
//go:noescape
|
||||
|
||||
// Hash returns the 64-bit SipHash-2-4 of the given byte slice with two 64-bit
|
||||
// parts of 128-bit key: k0 and k1.
|
||||
func Hash(k0, k1 uint64, b []byte) uint64
|
||||
|
||||
//go:noescape
|
||||
|
||||
// Hash128 returns the 128-bit SipHash-2-4 of the given byte slice with two
|
||||
// 64-bit parts of 128-bit key: k0 and k1.
|
||||
func Hash128(k0, k1 uint64, b []byte) (uint64, uint64)
|
||||
|
||||
//go:noescape
|
||||
func blocks(d *digest, p []uint8)
|
||||
|
||||
//go:noescape
|
||||
func finalize(d *digest) uint64
|
||||
|
||||
//go:noescape
|
||||
func once(d *digest)
|
318
vendor/github.com/dchest/siphash/siphash.go
generated
vendored
Normal file
318
vendor/github.com/dchest/siphash/siphash.go
generated
vendored
Normal file
@ -0,0 +1,318 @@
|
||||
// Written in 2012-2014 by Dmitry Chestnykh.
|
||||
//
|
||||
// To the extent possible under law, the author have dedicated all copyright
|
||||
// and related and neighboring rights to this software to the public domain
|
||||
// worldwide. This software is distributed without any warranty.
|
||||
// http://creativecommons.org/publicdomain/zero/1.0/
|
||||
|
||||
// Package siphash implements SipHash-2-4, a fast short-input PRF
|
||||
// created by Jean-Philippe Aumasson and Daniel J. Bernstein.
|
||||
package siphash
|
||||
|
||||
import "hash"
|
||||
|
||||
const (
|
||||
// BlockSize is the block size of hash algorithm in bytes.
|
||||
BlockSize = 8
|
||||
|
||||
// Size is the size of hash output in bytes.
|
||||
Size = 8
|
||||
|
||||
// Size128 is the size of 128-bit hash output in bytes.
|
||||
Size128 = 16
|
||||
)
|
||||
|
||||
type digest struct {
|
||||
v0, v1, v2, v3 uint64 // state
|
||||
k0, k1 uint64 // two parts of key
|
||||
x [8]byte // buffer for unprocessed bytes
|
||||
nx int // number of bytes in buffer x
|
||||
size int // output size in bytes (8 or 16)
|
||||
t uint8 // message bytes counter (mod 256)
|
||||
}
|
||||
|
||||
// newDigest returns a new digest with the given output size in bytes (must be 8 or 16).
|
||||
func newDigest(size int, key []byte) *digest {
|
||||
if size != Size && size != Size128 {
|
||||
panic("size must be 8 or 16")
|
||||
}
|
||||
d := new(digest)
|
||||
d.k0 = uint64(key[0]) | uint64(key[1])<<8 | uint64(key[2])<<16 | uint64(key[3])<<24 |
|
||||
uint64(key[4])<<32 | uint64(key[5])<<40 | uint64(key[6])<<48 | uint64(key[7])<<56
|
||||
d.k1 = uint64(key[8]) | uint64(key[9])<<8 | uint64(key[10])<<16 | uint64(key[11])<<24 |
|
||||
uint64(key[12])<<32 | uint64(key[13])<<40 | uint64(key[14])<<48 | uint64(key[15])<<56
|
||||
d.size = size
|
||||
d.Reset()
|
||||
return d
|
||||
}
|
||||
|
||||
// New returns a new hash.Hash64 computing SipHash-2-4 with 16-byte key and 8-byte output.
|
||||
func New(key []byte) hash.Hash64 {
|
||||
return newDigest(Size, key)
|
||||
}
|
||||
|
||||
// New128 returns a new hash.Hash computing SipHash-2-4 with 16-byte key and 16-byte output.
|
||||
//
|
||||
// Note that 16-byte output is considered experimental by SipHash authors at this time.
|
||||
func New128(key []byte) hash.Hash {
|
||||
return newDigest(Size128, key)
|
||||
}
|
||||
|
||||
func (d *digest) Reset() {
|
||||
d.v0 = d.k0 ^ 0x736f6d6570736575
|
||||
d.v1 = d.k1 ^ 0x646f72616e646f6d
|
||||
d.v2 = d.k0 ^ 0x6c7967656e657261
|
||||
d.v3 = d.k1 ^ 0x7465646279746573
|
||||
d.t = 0
|
||||
d.nx = 0
|
||||
if d.size == Size128 {
|
||||
d.v1 ^= 0xee
|
||||
}
|
||||
}
|
||||
|
||||
func (d *digest) Size() int { return d.size }
|
||||
|
||||
func (d *digest) BlockSize() int { return BlockSize }
|
||||
|
||||
func (d *digest) Write(p []byte) (nn int, err error) {
|
||||
nn = len(p)
|
||||
d.t += uint8(nn)
|
||||
if d.nx > 0 {
|
||||
n := len(p)
|
||||
if n > BlockSize-d.nx {
|
||||
n = BlockSize - d.nx
|
||||
}
|
||||
d.nx += copy(d.x[d.nx:], p)
|
||||
if d.nx == BlockSize {
|
||||
once(d)
|
||||
d.nx = 0
|
||||
}
|
||||
p = p[n:]
|
||||
}
|
||||
if len(p) >= BlockSize {
|
||||
n := len(p) &^ (BlockSize - 1)
|
||||
blocks(d, p[:n])
|
||||
p = p[n:]
|
||||
}
|
||||
if len(p) > 0 {
|
||||
d.nx = copy(d.x[:], p)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (d *digest) Sum64() uint64 {
|
||||
for i := d.nx; i < BlockSize-1; i++ {
|
||||
d.x[i] = 0
|
||||
}
|
||||
d.x[7] = d.t
|
||||
return finalize(d)
|
||||
}
|
||||
|
||||
func (d0 *digest) sum128() (r0, r1 uint64) {
|
||||
// Make a copy of d0 so that caller can keep writing and summing.
|
||||
d := *d0
|
||||
|
||||
for i := d.nx; i < BlockSize-1; i++ {
|
||||
d.x[i] = 0
|
||||
}
|
||||
d.x[7] = d.t
|
||||
blocks(&d, d.x[:])
|
||||
|
||||
v0, v1, v2, v3 := d.v0, d.v1, d.v2, d.v3
|
||||
v2 ^= 0xee
|
||||
|
||||
// Round 1.
|
||||
v0 += v1
|
||||
v1 = v1<<13 | v1>>(64-13)
|
||||
v1 ^= v0
|
||||
v0 = v0<<32 | v0>>(64-32)
|
||||
|
||||
v2 += v3
|
||||
v3 = v3<<16 | v3>>(64-16)
|
||||
v3 ^= v2
|
||||
|
||||
v0 += v3
|
||||
v3 = v3<<21 | v3>>(64-21)
|
||||
v3 ^= v0
|
||||
|
||||
v2 += v1
|
||||
v1 = v1<<17 | v1>>(64-17)
|
||||
v1 ^= v2
|
||||
v2 = v2<<32 | v2>>(64-32)
|
||||
|
||||
// Round 2.
|
||||
v0 += v1
|
||||
v1 = v1<<13 | v1>>(64-13)
|
||||
v1 ^= v0
|
||||
v0 = v0<<32 | v0>>(64-32)
|
||||
|
||||
v2 += v3
|
||||
v3 = v3<<16 | v3>>(64-16)
|
||||
v3 ^= v2
|
||||
|
||||
v0 += v3
|
||||
v3 = v3<<21 | v3>>(64-21)
|
||||
v3 ^= v0
|
||||
|
||||
v2 += v1
|
||||
v1 = v1<<17 | v1>>(64-17)
|
||||
v1 ^= v2
|
||||
v2 = v2<<32 | v2>>(64-32)
|
||||
|
||||
// Round 3.
|
||||
v0 += v1
|
||||
v1 = v1<<13 | v1>>(64-13)
|
||||
v1 ^= v0
|
||||
v0 = v0<<32 | v0>>(64-32)
|
||||
|
||||
v2 += v3
|
||||
v3 = v3<<16 | v3>>(64-16)
|
||||
v3 ^= v2
|
||||
|
||||
v0 += v3
|
||||
v3 = v3<<21 | v3>>(64-21)
|
||||
v3 ^= v0
|
||||
|
||||
v2 += v1
|
||||
v1 = v1<<17 | v1>>(64-17)
|
||||
v1 ^= v2
|
||||
v2 = v2<<32 | v2>>(64-32)
|
||||
|
||||
// Round 4.
|
||||
v0 += v1
|
||||
v1 = v1<<13 | v1>>(64-13)
|
||||
v1 ^= v0
|
||||
v0 = v0<<32 | v0>>(64-32)
|
||||
|
||||
v2 += v3
|
||||
v3 = v3<<16 | v3>>(64-16)
|
||||
v3 ^= v2
|
||||
|
||||
v0 += v3
|
||||
v3 = v3<<21 | v3>>(64-21)
|
||||
v3 ^= v0
|
||||
|
||||
v2 += v1
|
||||
v1 = v1<<17 | v1>>(64-17)
|
||||
v1 ^= v2
|
||||
v2 = v2<<32 | v2>>(64-32)
|
||||
|
||||
r0 = v0 ^ v1 ^ v2 ^ v3
|
||||
|
||||
v1 ^= 0xdd
|
||||
|
||||
// Round 1.
|
||||
v0 += v1
|
||||
v1 = v1<<13 | v1>>(64-13)
|
||||
v1 ^= v0
|
||||
v0 = v0<<32 | v0>>(64-32)
|
||||
|
||||
v2 += v3
|
||||
v3 = v3<<16 | v3>>(64-16)
|
||||
v3 ^= v2
|
||||
|
||||
v0 += v3
|
||||
v3 = v3<<21 | v3>>(64-21)
|
||||
v3 ^= v0
|
||||
|
||||
v2 += v1
|
||||
v1 = v1<<17 | v1>>(64-17)
|
||||
v1 ^= v2
|
||||
v2 = v2<<32 | v2>>(64-32)
|
||||
|
||||
// Round 2.
|
||||
v0 += v1
|
||||
v1 = v1<<13 | v1>>(64-13)
|
||||
v1 ^= v0
|
||||
v0 = v0<<32 | v0>>(64-32)
|
||||
|
||||
v2 += v3
|
||||
v3 = v3<<16 | v3>>(64-16)
|
||||
v3 ^= v2
|
||||
|
||||
v0 += v3
|
||||
v3 = v3<<21 | v3>>(64-21)
|
||||
v3 ^= v0
|
||||
|
||||
v2 += v1
|
||||
v1 = v1<<17 | v1>>(64-17)
|
||||
v1 ^= v2
|
||||
v2 = v2<<32 | v2>>(64-32)
|
||||
|
||||
// Round 3.
|
||||
v0 += v1
|
||||
v1 = v1<<13 | v1>>(64-13)
|
||||
v1 ^= v0
|
||||
v0 = v0<<32 | v0>>(64-32)
|
||||
|
||||
v2 += v3
|
||||
v3 = v3<<16 | v3>>(64-16)
|
||||
v3 ^= v2
|
||||
|
||||
v0 += v3
|
||||
v3 = v3<<21 | v3>>(64-21)
|
||||
v3 ^= v0
|
||||
|
||||
v2 += v1
|
||||
v1 = v1<<17 | v1>>(64-17)
|
||||
v1 ^= v2
|
||||
v2 = v2<<32 | v2>>(64-32)
|
||||
|
||||
// Round 4.
|
||||
v0 += v1
|
||||
v1 = v1<<13 | v1>>(64-13)
|
||||
v1 ^= v0
|
||||
v0 = v0<<32 | v0>>(64-32)
|
||||
|
||||
v2 += v3
|
||||
v3 = v3<<16 | v3>>(64-16)
|
||||
v3 ^= v2
|
||||
|
||||
v0 += v3
|
||||
v3 = v3<<21 | v3>>(64-21)
|
||||
v3 ^= v0
|
||||
|
||||
v2 += v1
|
||||
v1 = v1<<17 | v1>>(64-17)
|
||||
v1 ^= v2
|
||||
v2 = v2<<32 | v2>>(64-32)
|
||||
|
||||
r1 = v0 ^ v1 ^ v2 ^ v3
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (d *digest) Sum(in []byte) []byte {
|
||||
if d.size == Size {
|
||||
r := d.Sum64()
|
||||
in = append(in,
|
||||
byte(r),
|
||||
byte(r>>8),
|
||||
byte(r>>16),
|
||||
byte(r>>24),
|
||||
byte(r>>32),
|
||||
byte(r>>40),
|
||||
byte(r>>48),
|
||||
byte(r>>56))
|
||||
} else {
|
||||
r0, r1 := d.sum128()
|
||||
in = append(in,
|
||||
byte(r0),
|
||||
byte(r0>>8),
|
||||
byte(r0>>16),
|
||||
byte(r0>>24),
|
||||
byte(r0>>32),
|
||||
byte(r0>>40),
|
||||
byte(r0>>48),
|
||||
byte(r0>>56),
|
||||
byte(r1),
|
||||
byte(r1>>8),
|
||||
byte(r1>>16),
|
||||
byte(r1>>24),
|
||||
byte(r1>>32),
|
||||
byte(r1>>40),
|
||||
byte(r1>>48),
|
||||
byte(r1>>56))
|
||||
}
|
||||
return in
|
||||
}
|
149
vendor/golang.org/x/crypto/nacl/secretbox/secretbox.go
generated
vendored
Normal file
149
vendor/golang.org/x/crypto/nacl/secretbox/secretbox.go
generated
vendored
Normal file
@ -0,0 +1,149 @@
|
||||
// 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 secretbox encrypts and authenticates small messages.
|
||||
|
||||
Secretbox uses XSalsa20 and Poly1305 to encrypt and authenticate messages with
|
||||
secret-key cryptography. The length of messages is not hidden.
|
||||
|
||||
It is the caller's responsibility to ensure the uniqueness of nonces—for
|
||||
example, by using nonce 1 for the first message, nonce 2 for the second
|
||||
message, etc. Nonces are long enough that randomly generated nonces have
|
||||
negligible risk of collision.
|
||||
|
||||
This package is interoperable with NaCl: https://nacl.cr.yp.to/secretbox.html.
|
||||
*/
|
||||
package secretbox // import "golang.org/x/crypto/nacl/secretbox"
|
||||
|
||||
import (
|
||||
"golang.org/x/crypto/poly1305"
|
||||
"golang.org/x/crypto/salsa20/salsa"
|
||||
)
|
||||
|
||||
// Overhead is the number of bytes of overhead when boxing a message.
|
||||
const Overhead = poly1305.TagSize
|
||||
|
||||
// setup produces a sub-key and Salsa20 counter given a nonce and key.
|
||||
func setup(subKey *[32]byte, counter *[16]byte, nonce *[24]byte, key *[32]byte) {
|
||||
// We use XSalsa20 for encryption so first we need to generate a
|
||||
// key and nonce with HSalsa20.
|
||||
var hNonce [16]byte
|
||||
copy(hNonce[:], nonce[:])
|
||||
salsa.HSalsa20(subKey, &hNonce, key, &salsa.Sigma)
|
||||
|
||||
// The final 8 bytes of the original nonce form the new nonce.
|
||||
copy(counter[:], nonce[16:])
|
||||
}
|
||||
|
||||
// sliceForAppend takes a slice and a requested number of bytes. It returns a
|
||||
// slice with the contents of the given slice followed by that many bytes and a
|
||||
// second slice that aliases into it and contains only the extra bytes. If the
|
||||
// original slice has sufficient capacity then no allocation is performed.
|
||||
func sliceForAppend(in []byte, n int) (head, tail []byte) {
|
||||
if total := len(in) + n; cap(in) >= total {
|
||||
head = in[:total]
|
||||
} else {
|
||||
head = make([]byte, total)
|
||||
copy(head, in)
|
||||
}
|
||||
tail = head[len(in):]
|
||||
return
|
||||
}
|
||||
|
||||
// Seal appends an encrypted and authenticated copy of message to out, which
|
||||
// must not overlap message. The key and nonce pair must be unique for each
|
||||
// distinct message and the output will be Overhead bytes longer than message.
|
||||
func Seal(out, message []byte, nonce *[24]byte, key *[32]byte) []byte {
|
||||
var subKey [32]byte
|
||||
var counter [16]byte
|
||||
setup(&subKey, &counter, nonce, key)
|
||||
|
||||
// The Poly1305 key is generated by encrypting 32 bytes of zeros. Since
|
||||
// Salsa20 works with 64-byte blocks, we also generate 32 bytes of
|
||||
// keystream as a side effect.
|
||||
var firstBlock [64]byte
|
||||
salsa.XORKeyStream(firstBlock[:], firstBlock[:], &counter, &subKey)
|
||||
|
||||
var poly1305Key [32]byte
|
||||
copy(poly1305Key[:], firstBlock[:])
|
||||
|
||||
ret, out := sliceForAppend(out, len(message)+poly1305.TagSize)
|
||||
|
||||
// We XOR up to 32 bytes of message with the keystream generated from
|
||||
// the first block.
|
||||
firstMessageBlock := message
|
||||
if len(firstMessageBlock) > 32 {
|
||||
firstMessageBlock = firstMessageBlock[:32]
|
||||
}
|
||||
|
||||
tagOut := out
|
||||
out = out[poly1305.TagSize:]
|
||||
for i, x := range firstMessageBlock {
|
||||
out[i] = firstBlock[32+i] ^ x
|
||||
}
|
||||
message = message[len(firstMessageBlock):]
|
||||
ciphertext := out
|
||||
out = out[len(firstMessageBlock):]
|
||||
|
||||
// Now encrypt the rest.
|
||||
counter[8] = 1
|
||||
salsa.XORKeyStream(out, message, &counter, &subKey)
|
||||
|
||||
var tag [poly1305.TagSize]byte
|
||||
poly1305.Sum(&tag, ciphertext, &poly1305Key)
|
||||
copy(tagOut, tag[:])
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// Open authenticates and decrypts a box produced by Seal and appends the
|
||||
// message to out, which must not overlap box. The output will be Overhead
|
||||
// bytes smaller than box.
|
||||
func Open(out []byte, box []byte, nonce *[24]byte, key *[32]byte) ([]byte, bool) {
|
||||
if len(box) < Overhead {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
var subKey [32]byte
|
||||
var counter [16]byte
|
||||
setup(&subKey, &counter, nonce, key)
|
||||
|
||||
// The Poly1305 key is generated by encrypting 32 bytes of zeros. Since
|
||||
// Salsa20 works with 64-byte blocks, we also generate 32 bytes of
|
||||
// keystream as a side effect.
|
||||
var firstBlock [64]byte
|
||||
salsa.XORKeyStream(firstBlock[:], firstBlock[:], &counter, &subKey)
|
||||
|
||||
var poly1305Key [32]byte
|
||||
copy(poly1305Key[:], firstBlock[:])
|
||||
var tag [poly1305.TagSize]byte
|
||||
copy(tag[:], box)
|
||||
|
||||
if !poly1305.Verify(&tag, box[poly1305.TagSize:], &poly1305Key) {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
ret, out := sliceForAppend(out, len(box)-Overhead)
|
||||
|
||||
// We XOR up to 32 bytes of box with the keystream generated from
|
||||
// the first block.
|
||||
box = box[Overhead:]
|
||||
firstMessageBlock := box
|
||||
if len(firstMessageBlock) > 32 {
|
||||
firstMessageBlock = firstMessageBlock[:32]
|
||||
}
|
||||
for i, x := range firstMessageBlock {
|
||||
out[i] = firstBlock[32+i] ^ x
|
||||
}
|
||||
|
||||
box = box[len(firstMessageBlock):]
|
||||
out = out[len(firstMessageBlock):]
|
||||
|
||||
// Now decrypt the rest.
|
||||
counter[8] = 1
|
||||
salsa.XORKeyStream(out, box, &counter, &subKey)
|
||||
|
||||
return ret, true
|
||||
}
|
33
vendor/golang.org/x/crypto/poly1305/poly1305.go
generated
vendored
Normal file
33
vendor/golang.org/x/crypto/poly1305/poly1305.go
generated
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
// 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 "golang.org/x/crypto/poly1305"
|
||||
|
||||
import "crypto/subtle"
|
||||
|
||||
// TagSize is the size, in bytes, of a poly1305 authenticator.
|
||||
const TagSize = 16
|
||||
|
||||
// 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
|
||||
}
|
22
vendor/golang.org/x/crypto/poly1305/sum_amd64.go
generated
vendored
Normal file
22
vendor/golang.org/x/crypto/poly1305/sum_amd64.go
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
// 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.
|
||||
|
||||
// +build amd64,!gccgo,!appengine
|
||||
|
||||
package poly1305
|
||||
|
||||
// This function is implemented in sum_amd64.s
|
||||
//go:noescape
|
||||
func poly1305(out *[16]byte, m *byte, mlen uint64, key *[32]byte)
|
||||
|
||||
// Sum generates an authenticator for m 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) {
|
||||
var mPtr *byte
|
||||
if len(m) > 0 {
|
||||
mPtr = &m[0]
|
||||
}
|
||||
poly1305(out, mPtr, uint64(len(m)), key)
|
||||
}
|
125
vendor/golang.org/x/crypto/poly1305/sum_amd64.s
generated
vendored
Normal file
125
vendor/golang.org/x/crypto/poly1305/sum_amd64.s
generated
vendored
Normal file
@ -0,0 +1,125 @@
|
||||
// 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.
|
||||
|
||||
// +build amd64,!gccgo,!appengine
|
||||
|
||||
#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
|
||||
|
||||
DATA ·poly1305Mask<>+0x00(SB)/8, $0x0FFFFFFC0FFFFFFF
|
||||
DATA ·poly1305Mask<>+0x08(SB)/8, $0x0FFFFFFC0FFFFFFC
|
||||
GLOBL ·poly1305Mask<>(SB), RODATA, $16
|
||||
|
||||
// func poly1305(out *[16]byte, m *byte, mlen uint64, key *[32]key)
|
||||
TEXT ·poly1305(SB), $0-32
|
||||
MOVQ out+0(FP), DI
|
||||
MOVQ m+8(FP), SI
|
||||
MOVQ mlen+16(FP), R15
|
||||
MOVQ key+24(FP), AX
|
||||
|
||||
MOVQ 0(AX), R11
|
||||
MOVQ 8(AX), R12
|
||||
ANDQ ·poly1305Mask<>(SB), R11 // r0
|
||||
ANDQ ·poly1305Mask<>+8(SB), R12 // r1
|
||||
XORQ R8, R8 // h0
|
||||
XORQ R9, R9 // h1
|
||||
XORQ R10, R10 // h2
|
||||
|
||||
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, AX
|
||||
MOVQ R9, BX
|
||||
SUBQ $0xFFFFFFFFFFFFFFFB, AX
|
||||
SBBQ $0xFFFFFFFFFFFFFFFF, BX
|
||||
SBBQ $3, R10
|
||||
CMOVQCS R8, AX
|
||||
CMOVQCS R9, BX
|
||||
MOVQ key+24(FP), R8
|
||||
ADDQ 16(R8), AX
|
||||
ADCQ 24(R8), BX
|
||||
|
||||
MOVQ AX, 0(DI)
|
||||
MOVQ BX, 8(DI)
|
||||
RET
|
22
vendor/golang.org/x/crypto/poly1305/sum_arm.go
generated
vendored
Normal file
22
vendor/golang.org/x/crypto/poly1305/sum_arm.go
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
// 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 arm,!gccgo,!appengine,!nacl
|
||||
|
||||
package poly1305
|
||||
|
||||
// This function is implemented in sum_arm.s
|
||||
//go:noescape
|
||||
func poly1305_auth_armv6(out *[16]byte, m *byte, mlen uint32, key *[32]byte)
|
||||
|
||||
// Sum generates an authenticator for m 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) {
|
||||
var mPtr *byte
|
||||
if len(m) > 0 {
|
||||
mPtr = &m[0]
|
||||
}
|
||||
poly1305_auth_armv6(out, mPtr, uint32(len(m)), key)
|
||||
}
|
427
vendor/golang.org/x/crypto/poly1305/sum_arm.s
generated
vendored
Normal file
427
vendor/golang.org/x/crypto/poly1305/sum_arm.s
generated
vendored
Normal file
@ -0,0 +1,427 @@
|
||||
// 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 arm,!gccgo,!appengine,!nacl
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// This code was translated into a form compatible with 5a from the public
|
||||
// domain source by Andrew Moon: github.com/floodyberry/poly1305-opt/blob/master/app/extensions/poly1305.
|
||||
|
||||
DATA ·poly1305_init_constants_armv6<>+0x00(SB)/4, $0x3ffffff
|
||||
DATA ·poly1305_init_constants_armv6<>+0x04(SB)/4, $0x3ffff03
|
||||
DATA ·poly1305_init_constants_armv6<>+0x08(SB)/4, $0x3ffc0ff
|
||||
DATA ·poly1305_init_constants_armv6<>+0x0c(SB)/4, $0x3f03fff
|
||||
DATA ·poly1305_init_constants_armv6<>+0x10(SB)/4, $0x00fffff
|
||||
GLOBL ·poly1305_init_constants_armv6<>(SB), 8, $20
|
||||
|
||||
// Warning: the linker may use R11 to synthesize certain instructions. Please
|
||||
// take care and verify that no synthetic instructions use it.
|
||||
|
||||
TEXT poly1305_init_ext_armv6<>(SB), NOSPLIT, $0
|
||||
// Needs 16 bytes of stack and 64 bytes of space pointed to by R0. (It
|
||||
// might look like it's only 60 bytes of space but the final four bytes
|
||||
// will be written by another function.) We need to skip over four
|
||||
// bytes of stack because that's saving the value of 'g'.
|
||||
ADD $4, R13, R8
|
||||
MOVM.IB [R4-R7], (R8)
|
||||
MOVM.IA.W (R1), [R2-R5]
|
||||
MOVW $·poly1305_init_constants_armv6<>(SB), R7
|
||||
MOVW R2, R8
|
||||
MOVW R2>>26, R9
|
||||
MOVW R3>>20, g
|
||||
MOVW R4>>14, R11
|
||||
MOVW R5>>8, R12
|
||||
ORR R3<<6, R9, R9
|
||||
ORR R4<<12, g, g
|
||||
ORR R5<<18, R11, R11
|
||||
MOVM.IA (R7), [R2-R6]
|
||||
AND R8, R2, R2
|
||||
AND R9, R3, R3
|
||||
AND g, R4, R4
|
||||
AND R11, R5, R5
|
||||
AND R12, R6, R6
|
||||
MOVM.IA.W [R2-R6], (R0)
|
||||
EOR R2, R2, R2
|
||||
EOR R3, R3, R3
|
||||
EOR R4, R4, R4
|
||||
EOR R5, R5, R5
|
||||
EOR R6, R6, R6
|
||||
MOVM.IA.W [R2-R6], (R0)
|
||||
MOVM.IA.W (R1), [R2-R5]
|
||||
MOVM.IA [R2-R6], (R0)
|
||||
ADD $20, R13, R0
|
||||
MOVM.DA (R0), [R4-R7]
|
||||
RET
|
||||
|
||||
#define MOVW_UNALIGNED(Rsrc, Rdst, Rtmp, offset) \
|
||||
MOVBU (offset+0)(Rsrc), Rtmp; \
|
||||
MOVBU Rtmp, (offset+0)(Rdst); \
|
||||
MOVBU (offset+1)(Rsrc), Rtmp; \
|
||||
MOVBU Rtmp, (offset+1)(Rdst); \
|
||||
MOVBU (offset+2)(Rsrc), Rtmp; \
|
||||
MOVBU Rtmp, (offset+2)(Rdst); \
|
||||
MOVBU (offset+3)(Rsrc), Rtmp; \
|
||||
MOVBU Rtmp, (offset+3)(Rdst)
|
||||
|
||||
TEXT poly1305_blocks_armv6<>(SB), NOSPLIT, $0
|
||||
// Needs 24 bytes of stack for saved registers and then 88 bytes of
|
||||
// scratch space after that. We assume that 24 bytes at (R13) have
|
||||
// already been used: four bytes for the link register saved in the
|
||||
// prelude of poly1305_auth_armv6, four bytes for saving the value of g
|
||||
// in that function and 16 bytes of scratch space used around
|
||||
// poly1305_finish_ext_armv6_skip1.
|
||||
ADD $24, R13, R12
|
||||
MOVM.IB [R4-R8, R14], (R12)
|
||||
MOVW R0, 88(R13)
|
||||
MOVW R1, 92(R13)
|
||||
MOVW R2, 96(R13)
|
||||
MOVW R1, R14
|
||||
MOVW R2, R12
|
||||
MOVW 56(R0), R8
|
||||
WORD $0xe1180008 // TST R8, R8 not working see issue 5921
|
||||
EOR R6, R6, R6
|
||||
MOVW.EQ $(1<<24), R6
|
||||
MOVW R6, 84(R13)
|
||||
ADD $116, R13, g
|
||||
MOVM.IA (R0), [R0-R9]
|
||||
MOVM.IA [R0-R4], (g)
|
||||
CMP $16, R12
|
||||
BLO poly1305_blocks_armv6_done
|
||||
|
||||
poly1305_blocks_armv6_mainloop:
|
||||
WORD $0xe31e0003 // TST R14, #3 not working see issue 5921
|
||||
BEQ poly1305_blocks_armv6_mainloop_aligned
|
||||
ADD $100, R13, g
|
||||
MOVW_UNALIGNED(R14, g, R0, 0)
|
||||
MOVW_UNALIGNED(R14, g, R0, 4)
|
||||
MOVW_UNALIGNED(R14, g, R0, 8)
|
||||
MOVW_UNALIGNED(R14, g, R0, 12)
|
||||
MOVM.IA (g), [R0-R3]
|
||||
ADD $16, R14
|
||||
B poly1305_blocks_armv6_mainloop_loaded
|
||||
|
||||
poly1305_blocks_armv6_mainloop_aligned:
|
||||
MOVM.IA.W (R14), [R0-R3]
|
||||
|
||||
poly1305_blocks_armv6_mainloop_loaded:
|
||||
MOVW R0>>26, g
|
||||
MOVW R1>>20, R11
|
||||
MOVW R2>>14, R12
|
||||
MOVW R14, 92(R13)
|
||||
MOVW R3>>8, R4
|
||||
ORR R1<<6, g, g
|
||||
ORR R2<<12, R11, R11
|
||||
ORR R3<<18, R12, R12
|
||||
BIC $0xfc000000, R0, R0
|
||||
BIC $0xfc000000, g, g
|
||||
MOVW 84(R13), R3
|
||||
BIC $0xfc000000, R11, R11
|
||||
BIC $0xfc000000, R12, R12
|
||||
ADD R0, R5, R5
|
||||
ADD g, R6, R6
|
||||
ORR R3, R4, R4
|
||||
ADD R11, R7, R7
|
||||
ADD $116, R13, R14
|
||||
ADD R12, R8, R8
|
||||
ADD R4, R9, R9
|
||||
MOVM.IA (R14), [R0-R4]
|
||||
MULLU R4, R5, (R11, g)
|
||||
MULLU R3, R5, (R14, R12)
|
||||
MULALU R3, R6, (R11, g)
|
||||
MULALU R2, R6, (R14, R12)
|
||||
MULALU R2, R7, (R11, g)
|
||||
MULALU R1, R7, (R14, R12)
|
||||
ADD R4<<2, R4, R4
|
||||
ADD R3<<2, R3, R3
|
||||
MULALU R1, R8, (R11, g)
|
||||
MULALU R0, R8, (R14, R12)
|
||||
MULALU R0, R9, (R11, g)
|
||||
MULALU R4, R9, (R14, R12)
|
||||
MOVW g, 76(R13)
|
||||
MOVW R11, 80(R13)
|
||||
MOVW R12, 68(R13)
|
||||
MOVW R14, 72(R13)
|
||||
MULLU R2, R5, (R11, g)
|
||||
MULLU R1, R5, (R14, R12)
|
||||
MULALU R1, R6, (R11, g)
|
||||
MULALU R0, R6, (R14, R12)
|
||||
MULALU R0, R7, (R11, g)
|
||||
MULALU R4, R7, (R14, R12)
|
||||
ADD R2<<2, R2, R2
|
||||
ADD R1<<2, R1, R1
|
||||
MULALU R4, R8, (R11, g)
|
||||
MULALU R3, R8, (R14, R12)
|
||||
MULALU R3, R9, (R11, g)
|
||||
MULALU R2, R9, (R14, R12)
|
||||
MOVW g, 60(R13)
|
||||
MOVW R11, 64(R13)
|
||||
MOVW R12, 52(R13)
|
||||
MOVW R14, 56(R13)
|
||||
MULLU R0, R5, (R11, g)
|
||||
MULALU R4, R6, (R11, g)
|
||||
MULALU R3, R7, (R11, g)
|
||||
MULALU R2, R8, (R11, g)
|
||||
MULALU R1, R9, (R11, g)
|
||||
ADD $52, R13, R0
|
||||
MOVM.IA (R0), [R0-R7]
|
||||
MOVW g>>26, R12
|
||||
MOVW R4>>26, R14
|
||||
ORR R11<<6, R12, R12
|
||||
ORR R5<<6, R14, R14
|
||||
BIC $0xfc000000, g, g
|
||||
BIC $0xfc000000, R4, R4
|
||||
ADD.S R12, R0, R0
|
||||
ADC $0, R1, R1
|
||||
ADD.S R14, R6, R6
|
||||
ADC $0, R7, R7
|
||||
MOVW R0>>26, R12
|
||||
MOVW R6>>26, R14
|
||||
ORR R1<<6, R12, R12
|
||||
ORR R7<<6, R14, R14
|
||||
BIC $0xfc000000, R0, R0
|
||||
BIC $0xfc000000, R6, R6
|
||||
ADD R14<<2, R14, R14
|
||||
ADD.S R12, R2, R2
|
||||
ADC $0, R3, R3
|
||||
ADD R14, g, g
|
||||
MOVW R2>>26, R12
|
||||
MOVW g>>26, R14
|
||||
ORR R3<<6, R12, R12
|
||||
BIC $0xfc000000, g, R5
|
||||
BIC $0xfc000000, R2, R7
|
||||
ADD R12, R4, R4
|
||||
ADD R14, R0, R0
|
||||
MOVW R4>>26, R12
|
||||
BIC $0xfc000000, R4, R8
|
||||
ADD R12, R6, R9
|
||||
MOVW 96(R13), R12
|
||||
MOVW 92(R13), R14
|
||||
MOVW R0, R6
|
||||
CMP $32, R12
|
||||
SUB $16, R12, R12
|
||||
MOVW R12, 96(R13)
|
||||
BHS poly1305_blocks_armv6_mainloop
|
||||
|
||||
poly1305_blocks_armv6_done:
|
||||
MOVW 88(R13), R12
|
||||
MOVW R5, 20(R12)
|
||||
MOVW R6, 24(R12)
|
||||
MOVW R7, 28(R12)
|
||||
MOVW R8, 32(R12)
|
||||
MOVW R9, 36(R12)
|
||||
ADD $48, R13, R0
|
||||
MOVM.DA (R0), [R4-R8, R14]
|
||||
RET
|
||||
|
||||
#define MOVHUP_UNALIGNED(Rsrc, Rdst, Rtmp) \
|
||||
MOVBU.P 1(Rsrc), Rtmp; \
|
||||
MOVBU.P Rtmp, 1(Rdst); \
|
||||
MOVBU.P 1(Rsrc), Rtmp; \
|
||||
MOVBU.P Rtmp, 1(Rdst)
|
||||
|
||||
#define MOVWP_UNALIGNED(Rsrc, Rdst, Rtmp) \
|
||||
MOVHUP_UNALIGNED(Rsrc, Rdst, Rtmp); \
|
||||
MOVHUP_UNALIGNED(Rsrc, Rdst, Rtmp)
|
||||
|
||||
// func poly1305_auth_armv6(out *[16]byte, m *byte, mlen uint32, key *[32]key)
|
||||
TEXT ·poly1305_auth_armv6(SB), $196-16
|
||||
// The value 196, just above, is the sum of 64 (the size of the context
|
||||
// structure) and 132 (the amount of stack needed).
|
||||
//
|
||||
// At this point, the stack pointer (R13) has been moved down. It
|
||||
// points to the saved link register and there's 196 bytes of free
|
||||
// space above it.
|
||||
//
|
||||
// The stack for this function looks like:
|
||||
//
|
||||
// +---------------------
|
||||
// |
|
||||
// | 64 bytes of context structure
|
||||
// |
|
||||
// +---------------------
|
||||
// |
|
||||
// | 112 bytes for poly1305_blocks_armv6
|
||||
// |
|
||||
// +---------------------
|
||||
// | 16 bytes of final block, constructed at
|
||||
// | poly1305_finish_ext_armv6_skip8
|
||||
// +---------------------
|
||||
// | four bytes of saved 'g'
|
||||
// +---------------------
|
||||
// | lr, saved by prelude <- R13 points here
|
||||
// +---------------------
|
||||
MOVW g, 4(R13)
|
||||
|
||||
MOVW out+0(FP), R4
|
||||
MOVW m+4(FP), R5
|
||||
MOVW mlen+8(FP), R6
|
||||
MOVW key+12(FP), R7
|
||||
|
||||
ADD $136, R13, R0 // 136 = 4 + 4 + 16 + 112
|
||||
MOVW R7, R1
|
||||
|
||||
// poly1305_init_ext_armv6 will write to the stack from R13+4, but
|
||||
// that's ok because none of the other values have been written yet.
|
||||
BL poly1305_init_ext_armv6<>(SB)
|
||||
BIC.S $15, R6, R2
|
||||
BEQ poly1305_auth_armv6_noblocks
|
||||
ADD $136, R13, R0
|
||||
MOVW R5, R1
|
||||
ADD R2, R5, R5
|
||||
SUB R2, R6, R6
|
||||
BL poly1305_blocks_armv6<>(SB)
|
||||
|
||||
poly1305_auth_armv6_noblocks:
|
||||
ADD $136, R13, R0
|
||||
MOVW R5, R1
|
||||
MOVW R6, R2
|
||||
MOVW R4, R3
|
||||
|
||||
MOVW R0, R5
|
||||
MOVW R1, R6
|
||||
MOVW R2, R7
|
||||
MOVW R3, R8
|
||||
AND.S R2, R2, R2
|
||||
BEQ poly1305_finish_ext_armv6_noremaining
|
||||
EOR R0, R0
|
||||
ADD $8, R13, R9 // 8 = offset to 16 byte scratch space
|
||||
MOVW R0, (R9)
|
||||
MOVW R0, 4(R9)
|
||||
MOVW R0, 8(R9)
|
||||
MOVW R0, 12(R9)
|
||||
WORD $0xe3110003 // TST R1, #3 not working see issue 5921
|
||||
BEQ poly1305_finish_ext_armv6_aligned
|
||||
WORD $0xe3120008 // TST R2, #8 not working see issue 5921
|
||||
BEQ poly1305_finish_ext_armv6_skip8
|
||||
MOVWP_UNALIGNED(R1, R9, g)
|
||||
MOVWP_UNALIGNED(R1, R9, g)
|
||||
|
||||
poly1305_finish_ext_armv6_skip8:
|
||||
WORD $0xe3120004 // TST $4, R2 not working see issue 5921
|
||||
BEQ poly1305_finish_ext_armv6_skip4
|
||||
MOVWP_UNALIGNED(R1, R9, g)
|
||||
|
||||
poly1305_finish_ext_armv6_skip4:
|
||||
WORD $0xe3120002 // TST $2, R2 not working see issue 5921
|
||||
BEQ poly1305_finish_ext_armv6_skip2
|
||||
MOVHUP_UNALIGNED(R1, R9, g)
|
||||
B poly1305_finish_ext_armv6_skip2
|
||||
|
||||
poly1305_finish_ext_armv6_aligned:
|
||||
WORD $0xe3120008 // TST R2, #8 not working see issue 5921
|
||||
BEQ poly1305_finish_ext_armv6_skip8_aligned
|
||||
MOVM.IA.W (R1), [g-R11]
|
||||
MOVM.IA.W [g-R11], (R9)
|
||||
|
||||
poly1305_finish_ext_armv6_skip8_aligned:
|
||||
WORD $0xe3120004 // TST $4, R2 not working see issue 5921
|
||||
BEQ poly1305_finish_ext_armv6_skip4_aligned
|
||||
MOVW.P 4(R1), g
|
||||
MOVW.P g, 4(R9)
|
||||
|
||||
poly1305_finish_ext_armv6_skip4_aligned:
|
||||
WORD $0xe3120002 // TST $2, R2 not working see issue 5921
|
||||
BEQ poly1305_finish_ext_armv6_skip2
|
||||
MOVHU.P 2(R1), g
|
||||
MOVH.P g, 2(R9)
|
||||
|
||||
poly1305_finish_ext_armv6_skip2:
|
||||
WORD $0xe3120001 // TST $1, R2 not working see issue 5921
|
||||
BEQ poly1305_finish_ext_armv6_skip1
|
||||
MOVBU.P 1(R1), g
|
||||
MOVBU.P g, 1(R9)
|
||||
|
||||
poly1305_finish_ext_armv6_skip1:
|
||||
MOVW $1, R11
|
||||
MOVBU R11, 0(R9)
|
||||
MOVW R11, 56(R5)
|
||||
MOVW R5, R0
|
||||
ADD $8, R13, R1
|
||||
MOVW $16, R2
|
||||
BL poly1305_blocks_armv6<>(SB)
|
||||
|
||||
poly1305_finish_ext_armv6_noremaining:
|
||||
MOVW 20(R5), R0
|
||||
MOVW 24(R5), R1
|
||||
MOVW 28(R5), R2
|
||||
MOVW 32(R5), R3
|
||||
MOVW 36(R5), R4
|
||||
MOVW R4>>26, R12
|
||||
BIC $0xfc000000, R4, R4
|
||||
ADD R12<<2, R12, R12
|
||||
ADD R12, R0, R0
|
||||
MOVW R0>>26, R12
|
||||
BIC $0xfc000000, R0, R0
|
||||
ADD R12, R1, R1
|
||||
MOVW R1>>26, R12
|
||||
BIC $0xfc000000, R1, R1
|
||||
ADD R12, R2, R2
|
||||
MOVW R2>>26, R12
|
||||
BIC $0xfc000000, R2, R2
|
||||
ADD R12, R3, R3
|
||||
MOVW R3>>26, R12
|
||||
BIC $0xfc000000, R3, R3
|
||||
ADD R12, R4, R4
|
||||
ADD $5, R0, R6
|
||||
MOVW R6>>26, R12
|
||||
BIC $0xfc000000, R6, R6
|
||||
ADD R12, R1, R7
|
||||
MOVW R7>>26, R12
|
||||
BIC $0xfc000000, R7, R7
|
||||
ADD R12, R2, g
|
||||
MOVW g>>26, R12
|
||||
BIC $0xfc000000, g, g
|
||||
ADD R12, R3, R11
|
||||
MOVW $-(1<<26), R12
|
||||
ADD R11>>26, R12, R12
|
||||
BIC $0xfc000000, R11, R11
|
||||
ADD R12, R4, R9
|
||||
MOVW R9>>31, R12
|
||||
SUB $1, R12
|
||||
AND R12, R6, R6
|
||||
AND R12, R7, R7
|
||||
AND R12, g, g
|
||||
AND R12, R11, R11
|
||||
AND R12, R9, R9
|
||||
MVN R12, R12
|
||||
AND R12, R0, R0
|
||||
AND R12, R1, R1
|
||||
AND R12, R2, R2
|
||||
AND R12, R3, R3
|
||||
AND R12, R4, R4
|
||||
ORR R6, R0, R0
|
||||
ORR R7, R1, R1
|
||||
ORR g, R2, R2
|
||||
ORR R11, R3, R3
|
||||
ORR R9, R4, R4
|
||||
ORR R1<<26, R0, R0
|
||||
MOVW R1>>6, R1
|
||||
ORR R2<<20, R1, R1
|
||||
MOVW R2>>12, R2
|
||||
ORR R3<<14, R2, R2
|
||||
MOVW R3>>18, R3
|
||||
ORR R4<<8, R3, R3
|
||||
MOVW 40(R5), R6
|
||||
MOVW 44(R5), R7
|
||||
MOVW 48(R5), g
|
||||
MOVW 52(R5), R11
|
||||
ADD.S R6, R0, R0
|
||||
ADC.S R7, R1, R1
|
||||
ADC.S g, R2, R2
|
||||
ADC.S R11, R3, R3
|
||||
MOVM.IA [R0-R3], (R8)
|
||||
MOVW R5, R12
|
||||
EOR R0, R0, R0
|
||||
EOR R1, R1, R1
|
||||
EOR R2, R2, R2
|
||||
EOR R3, R3, R3
|
||||
EOR R4, R4, R4
|
||||
EOR R5, R5, R5
|
||||
EOR R6, R6, R6
|
||||
EOR R7, R7, R7
|
||||
MOVM.IA.W [R0-R7], (R12)
|
||||
MOVM.IA [R0-R7], (R12)
|
||||
MOVW 4(R13), g
|
||||
RET
|
141
vendor/golang.org/x/crypto/poly1305/sum_ref.go
generated
vendored
Normal file
141
vendor/golang.org/x/crypto/poly1305/sum_ref.go
generated
vendored
Normal file
@ -0,0 +1,141 @@
|
||||
// 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.
|
||||
|
||||
// +build !amd64,!arm gccgo appengine nacl
|
||||
|
||||
package poly1305
|
||||
|
||||
import "encoding/binary"
|
||||
|
||||
// Sum generates an authenticator for msg using a one-time key and puts the
|
||||
// 16-byte result into out. Authenticating two different messages with the same
|
||||
// key allows an attacker to forge messages at will.
|
||||
func Sum(out *[TagSize]byte, msg []byte, key *[32]byte) {
|
||||
var (
|
||||
h0, h1, h2, h3, h4 uint32 // the hash accumulators
|
||||
r0, r1, r2, r3, r4 uint64 // the r part of the key
|
||||
)
|
||||
|
||||
r0 = uint64(binary.LittleEndian.Uint32(key[0:]) & 0x3ffffff)
|
||||
r1 = uint64((binary.LittleEndian.Uint32(key[3:]) >> 2) & 0x3ffff03)
|
||||
r2 = uint64((binary.LittleEndian.Uint32(key[6:]) >> 4) & 0x3ffc0ff)
|
||||
r3 = uint64((binary.LittleEndian.Uint32(key[9:]) >> 6) & 0x3f03fff)
|
||||
r4 = uint64((binary.LittleEndian.Uint32(key[12:]) >> 8) & 0x00fffff)
|
||||
|
||||
R1, R2, R3, R4 := r1*5, r2*5, r3*5, r4*5
|
||||
|
||||
for len(msg) >= TagSize {
|
||||
// h += msg
|
||||
h0 += binary.LittleEndian.Uint32(msg[0:]) & 0x3ffffff
|
||||
h1 += (binary.LittleEndian.Uint32(msg[3:]) >> 2) & 0x3ffffff
|
||||
h2 += (binary.LittleEndian.Uint32(msg[6:]) >> 4) & 0x3ffffff
|
||||
h3 += (binary.LittleEndian.Uint32(msg[9:]) >> 6) & 0x3ffffff
|
||||
h4 += (binary.LittleEndian.Uint32(msg[12:]) >> 8) | (1 << 24)
|
||||
|
||||
// h *= r
|
||||
d0 := (uint64(h0) * r0) + (uint64(h1) * R4) + (uint64(h2) * R3) + (uint64(h3) * R2) + (uint64(h4) * R1)
|
||||
d1 := (d0 >> 26) + (uint64(h0) * r1) + (uint64(h1) * r0) + (uint64(h2) * R4) + (uint64(h3) * R3) + (uint64(h4) * R2)
|
||||
d2 := (d1 >> 26) + (uint64(h0) * r2) + (uint64(h1) * r1) + (uint64(h2) * r0) + (uint64(h3) * R4) + (uint64(h4) * R3)
|
||||
d3 := (d2 >> 26) + (uint64(h0) * r3) + (uint64(h1) * r2) + (uint64(h2) * r1) + (uint64(h3) * r0) + (uint64(h4) * R4)
|
||||
d4 := (d3 >> 26) + (uint64(h0) * r4) + (uint64(h1) * r3) + (uint64(h2) * r2) + (uint64(h3) * r1) + (uint64(h4) * r0)
|
||||
|
||||
// h %= p
|
||||
h0 = uint32(d0) & 0x3ffffff
|
||||
h1 = uint32(d1) & 0x3ffffff
|
||||
h2 = uint32(d2) & 0x3ffffff
|
||||
h3 = uint32(d3) & 0x3ffffff
|
||||
h4 = uint32(d4) & 0x3ffffff
|
||||
|
||||
h0 += uint32(d4>>26) * 5
|
||||
h1 += h0 >> 26
|
||||
h0 = h0 & 0x3ffffff
|
||||
|
||||
msg = msg[TagSize:]
|
||||
}
|
||||
|
||||
if len(msg) > 0 {
|
||||
var block [TagSize]byte
|
||||
off := copy(block[:], msg)
|
||||
block[off] = 0x01
|
||||
|
||||
// h += msg
|
||||
h0 += binary.LittleEndian.Uint32(block[0:]) & 0x3ffffff
|
||||
h1 += (binary.LittleEndian.Uint32(block[3:]) >> 2) & 0x3ffffff
|
||||
h2 += (binary.LittleEndian.Uint32(block[6:]) >> 4) & 0x3ffffff
|
||||
h3 += (binary.LittleEndian.Uint32(block[9:]) >> 6) & 0x3ffffff
|
||||
h4 += (binary.LittleEndian.Uint32(block[12:]) >> 8)
|
||||
|
||||
// h *= r
|
||||
d0 := (uint64(h0) * r0) + (uint64(h1) * R4) + (uint64(h2) * R3) + (uint64(h3) * R2) + (uint64(h4) * R1)
|
||||
d1 := (d0 >> 26) + (uint64(h0) * r1) + (uint64(h1) * r0) + (uint64(h2) * R4) + (uint64(h3) * R3) + (uint64(h4) * R2)
|
||||
d2 := (d1 >> 26) + (uint64(h0) * r2) + (uint64(h1) * r1) + (uint64(h2) * r0) + (uint64(h3) * R4) + (uint64(h4) * R3)
|
||||
d3 := (d2 >> 26) + (uint64(h0) * r3) + (uint64(h1) * r2) + (uint64(h2) * r1) + (uint64(h3) * r0) + (uint64(h4) * R4)
|
||||
d4 := (d3 >> 26) + (uint64(h0) * r4) + (uint64(h1) * r3) + (uint64(h2) * r2) + (uint64(h3) * r1) + (uint64(h4) * r0)
|
||||
|
||||
// h %= p
|
||||
h0 = uint32(d0) & 0x3ffffff
|
||||
h1 = uint32(d1) & 0x3ffffff
|
||||
h2 = uint32(d2) & 0x3ffffff
|
||||
h3 = uint32(d3) & 0x3ffffff
|
||||
h4 = uint32(d4) & 0x3ffffff
|
||||
|
||||
h0 += uint32(d4>>26) * 5
|
||||
h1 += h0 >> 26
|
||||
h0 = h0 & 0x3ffffff
|
||||
}
|
||||
|
||||
// h %= p reduction
|
||||
h2 += h1 >> 26
|
||||
h1 &= 0x3ffffff
|
||||
h3 += h2 >> 26
|
||||
h2 &= 0x3ffffff
|
||||
h4 += h3 >> 26
|
||||
h3 &= 0x3ffffff
|
||||
h0 += 5 * (h4 >> 26)
|
||||
h4 &= 0x3ffffff
|
||||
h1 += h0 >> 26
|
||||
h0 &= 0x3ffffff
|
||||
|
||||
// h - p
|
||||
t0 := h0 + 5
|
||||
t1 := h1 + (t0 >> 26)
|
||||
t2 := h2 + (t1 >> 26)
|
||||
t3 := h3 + (t2 >> 26)
|
||||
t4 := h4 + (t3 >> 26) - (1 << 26)
|
||||
t0 &= 0x3ffffff
|
||||
t1 &= 0x3ffffff
|
||||
t2 &= 0x3ffffff
|
||||
t3 &= 0x3ffffff
|
||||
|
||||
// select h if h < p else h - p
|
||||
t_mask := (t4 >> 31) - 1
|
||||
h_mask := ^t_mask
|
||||
h0 = (h0 & h_mask) | (t0 & t_mask)
|
||||
h1 = (h1 & h_mask) | (t1 & t_mask)
|
||||
h2 = (h2 & h_mask) | (t2 & t_mask)
|
||||
h3 = (h3 & h_mask) | (t3 & t_mask)
|
||||
h4 = (h4 & h_mask) | (t4 & t_mask)
|
||||
|
||||
// h %= 2^128
|
||||
h0 |= h1 << 26
|
||||
h1 = ((h1 >> 6) | (h2 << 20))
|
||||
h2 = ((h2 >> 12) | (h3 << 14))
|
||||
h3 = ((h3 >> 18) | (h4 << 8))
|
||||
|
||||
// s: the s part of the key
|
||||
// tag = (h + s) % (2^128)
|
||||
t := uint64(h0) + uint64(binary.LittleEndian.Uint32(key[16:]))
|
||||
h0 = uint32(t)
|
||||
t = uint64(h1) + uint64(binary.LittleEndian.Uint32(key[20:])) + (t >> 32)
|
||||
h1 = uint32(t)
|
||||
t = uint64(h2) + uint64(binary.LittleEndian.Uint32(key[24:])) + (t >> 32)
|
||||
h2 = uint32(t)
|
||||
t = uint64(h3) + uint64(binary.LittleEndian.Uint32(key[28:])) + (t >> 32)
|
||||
h3 = uint32(t)
|
||||
|
||||
binary.LittleEndian.PutUint32(out[0:], h0)
|
||||
binary.LittleEndian.PutUint32(out[4:], h1)
|
||||
binary.LittleEndian.PutUint32(out[8:], h2)
|
||||
binary.LittleEndian.PutUint32(out[12:], h3)
|
||||
}
|
84
vendor/vendor.json
vendored
84
vendor/vendor.json
vendored
@ -2,12 +2,84 @@
|
||||
"comment": "",
|
||||
"ignore": "test",
|
||||
"package": [
|
||||
{
|
||||
"checksumSHA1": "Ntd/jdOFlzTpccS67XPoN8P+GvA=",
|
||||
"path": "git.torproject.org/pluggable-transports/goptlib.git",
|
||||
"revision": "a3ad5df6c9e7dc8117f55958b4ce99bf1e0fe291",
|
||||
"revisionTime": "2017-06-26T23:50:26Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "9FjAFzCl4t9s2I/2LrLcmTqprJc=",
|
||||
"path": "git.torproject.org/pluggable-transports/obfs4.git/common/csrand",
|
||||
"revision": "97a875ec3c0afa629405c78e750d27e4e1f851ca",
|
||||
"revisionTime": "2016-11-15T19:21:38Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "QcRA+Vo62wjolAdMtjWmtyE+j2g=",
|
||||
"path": "git.torproject.org/pluggable-transports/obfs4.git/common/drbg",
|
||||
"revision": "97a875ec3c0afa629405c78e750d27e4e1f851ca",
|
||||
"revisionTime": "2016-11-15T19:21:38Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "aAkBnDAHUvUpyX4G1PmedxQQpx4=",
|
||||
"path": "git.torproject.org/pluggable-transports/obfs4.git/common/ntor",
|
||||
"revision": "97a875ec3c0afa629405c78e750d27e4e1f851ca",
|
||||
"revisionTime": "2016-11-15T19:21:38Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "MNwh5v3DYMhxJQHgwCubElx5hjk=",
|
||||
"path": "git.torproject.org/pluggable-transports/obfs4.git/common/probdist",
|
||||
"revision": "97a875ec3c0afa629405c78e750d27e4e1f851ca",
|
||||
"revisionTime": "2016-11-15T19:21:38Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "g0e9BbYKC/ToxZ+GVh77L0QA8Eg=",
|
||||
"path": "git.torproject.org/pluggable-transports/obfs4.git/common/replayfilter",
|
||||
"revision": "97a875ec3c0afa629405c78e750d27e4e1f851ca",
|
||||
"revisionTime": "2016-11-15T19:21:38Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "bLggyj50IADuEWoWSoyYdU6MaFo=",
|
||||
"path": "git.torproject.org/pluggable-transports/obfs4.git/transports/base",
|
||||
"revision": "97a875ec3c0afa629405c78e750d27e4e1f851ca",
|
||||
"revisionTime": "2016-11-15T19:21:38Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "0WJ//uEKORueVXb9DDrZ7t1v00s=",
|
||||
"path": "git.torproject.org/pluggable-transports/obfs4.git/transports/obfs4",
|
||||
"revision": "97a875ec3c0afa629405c78e750d27e4e1f851ca",
|
||||
"revisionTime": "2016-11-15T19:21:38Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "EuNvu7iIjtIWStjBSXSvaRNa76M=",
|
||||
"path": "git.torproject.org/pluggable-transports/obfs4.git/transports/obfs4/framing",
|
||||
"revision": "97a875ec3c0afa629405c78e750d27e4e1f851ca",
|
||||
"revisionTime": "2016-11-15T19:21:38Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "IFJyJgPCjumDG37lEb0lyRBBGZE=",
|
||||
"path": "github.com/Yawning/chacha20",
|
||||
"revision": "c91e78db502ff629614837aacb7aa4efa61c651a",
|
||||
"revisionTime": "2016-04-30T09:49:23Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "30PBqj9BW03KCVqASvLg3bR+xYc=",
|
||||
"path": "github.com/agl/ed25519/edwards25519",
|
||||
"revision": "5312a61534124124185d41f09206b9fef1d88403",
|
||||
"revisionTime": "2017-01-16T20:05:12Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "wHBvnCJG9bkZdtxFFefU+8/WDSE=",
|
||||
"path": "github.com/agl/ed25519/extra25519",
|
||||
"revision": "5312a61534124124185d41f09206b9fef1d88403",
|
||||
"revisionTime": "2017-01-16T20:05:12Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "xqVDKHGnakGlcRhmWd1j9JYmfLc=",
|
||||
"path": "github.com/dchest/siphash",
|
||||
"revision": "4ebf1de738443ea7f45f02dc394c4df1942a126d",
|
||||
"revisionTime": "2016-08-31T15:17:26Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "aIhLeVAIrsjs63CwqmU3+GU8yT4=",
|
||||
"path": "github.com/ginuerzh/gosocks4",
|
||||
@ -206,12 +278,24 @@
|
||||
"revision": "558b6879de74bc843225cde5686419267ff707ca",
|
||||
"revisionTime": "2017-07-28T12:36:07Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "Y/FcWB2/xSfX1rRp7HYhktHNw8s=",
|
||||
"path": "golang.org/x/crypto/nacl/secretbox",
|
||||
"revision": "558b6879de74bc843225cde5686419267ff707ca",
|
||||
"revisionTime": "2017-07-28T12:36:07Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "1MGpGDQqnUoRpv7VEcQrXOBydXE=",
|
||||
"path": "golang.org/x/crypto/pbkdf2",
|
||||
"revision": "558b6879de74bc843225cde5686419267ff707ca",
|
||||
"revisionTime": "2017-07-28T12:36:07Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "kVKE0OX1Xdw5mG7XKT86DLLKE2I=",
|
||||
"path": "golang.org/x/crypto/poly1305",
|
||||
"revision": "558b6879de74bc843225cde5686419267ff707ca",
|
||||
"revisionTime": "2017-07-28T12:36:07Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "qsacnnq6zmaoRpDJeTX2YDpIo6U=",
|
||||
"path": "golang.org/x/crypto/salsa20",
|
||||
|
4
ws.go
4
ws.go
@ -97,7 +97,7 @@ func (c *websocketConn) SetWriteDeadline(t time.Time) error {
|
||||
}
|
||||
|
||||
type wsTransporter struct {
|
||||
*tcpTransporter
|
||||
tcpTransporter
|
||||
options *WSOptions
|
||||
}
|
||||
|
||||
@ -122,7 +122,7 @@ func (tr *wsTransporter) Handshake(conn net.Conn, options ...HandshakeOption) (n
|
||||
}
|
||||
|
||||
type wssTransporter struct {
|
||||
*tcpTransporter
|
||||
tcpTransporter
|
||||
options *WSOptions
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user