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),
|
gost.ChainDialOption(chain),
|
||||||
)
|
)
|
||||||
chain = gost.NewChain() // cutoff the chain for multiplex
|
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:
|
default:
|
||||||
tr = gost.TCPTransporter()
|
tr = gost.TCPTransporter()
|
||||||
}
|
}
|
||||||
@ -265,6 +270,11 @@ func serve(chain *gost.Chain) error {
|
|||||||
ln, err = gost.H2Listener(node.Addr, tlsCfg)
|
ln, err = gost.H2Listener(node.Addr, tlsCfg)
|
||||||
case "h2c":
|
case "h2c":
|
||||||
ln, err = gost.H2CListener(node.Addr)
|
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":
|
case "tcp":
|
||||||
ln, err = gost.TCPListener(node.Addr)
|
ln, err = gost.TCPListener(node.Addr)
|
||||||
case "rtcp":
|
case "rtcp":
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
# username password
|
# username password
|
||||||
|
|
||||||
|
test\admin 123456
|
||||||
|
$test 123456
|
||||||
test001 123456
|
test001 123456
|
||||||
test002 12345678
|
test002 12345678
|
2
node.go
2
node.go
@ -46,7 +46,7 @@ func ParseNode(s string) (node Node, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch node.Transport {
|
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":
|
case "https":
|
||||||
node.Protocol = "http"
|
node.Protocol = "http"
|
||||||
node.Transport = "tls"
|
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 {
|
type tlsTransporter struct {
|
||||||
*tcpTransporter
|
tcpTransporter
|
||||||
}
|
}
|
||||||
|
|
||||||
// TLSTransporter creates a Transporter that is used by TLS proxy client.
|
// 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": "",
|
"comment": "",
|
||||||
"ignore": "test",
|
"ignore": "test",
|
||||||
"package": [
|
"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=",
|
"checksumSHA1": "IFJyJgPCjumDG37lEb0lyRBBGZE=",
|
||||||
"path": "github.com/Yawning/chacha20",
|
"path": "github.com/Yawning/chacha20",
|
||||||
"revision": "c91e78db502ff629614837aacb7aa4efa61c651a",
|
"revision": "c91e78db502ff629614837aacb7aa4efa61c651a",
|
||||||
"revisionTime": "2016-04-30T09:49:23Z"
|
"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=",
|
"checksumSHA1": "aIhLeVAIrsjs63CwqmU3+GU8yT4=",
|
||||||
"path": "github.com/ginuerzh/gosocks4",
|
"path": "github.com/ginuerzh/gosocks4",
|
||||||
@ -206,12 +278,24 @@
|
|||||||
"revision": "558b6879de74bc843225cde5686419267ff707ca",
|
"revision": "558b6879de74bc843225cde5686419267ff707ca",
|
||||||
"revisionTime": "2017-07-28T12:36:07Z"
|
"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=",
|
"checksumSHA1": "1MGpGDQqnUoRpv7VEcQrXOBydXE=",
|
||||||
"path": "golang.org/x/crypto/pbkdf2",
|
"path": "golang.org/x/crypto/pbkdf2",
|
||||||
"revision": "558b6879de74bc843225cde5686419267ff707ca",
|
"revision": "558b6879de74bc843225cde5686419267ff707ca",
|
||||||
"revisionTime": "2017-07-28T12:36:07Z"
|
"revisionTime": "2017-07-28T12:36:07Z"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"checksumSHA1": "kVKE0OX1Xdw5mG7XKT86DLLKE2I=",
|
||||||
|
"path": "golang.org/x/crypto/poly1305",
|
||||||
|
"revision": "558b6879de74bc843225cde5686419267ff707ca",
|
||||||
|
"revisionTime": "2017-07-28T12:36:07Z"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "qsacnnq6zmaoRpDJeTX2YDpIo6U=",
|
"checksumSHA1": "qsacnnq6zmaoRpDJeTX2YDpIo6U=",
|
||||||
"path": "golang.org/x/crypto/salsa20",
|
"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 {
|
type wsTransporter struct {
|
||||||
*tcpTransporter
|
tcpTransporter
|
||||||
options *WSOptions
|
options *WSOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,7 +122,7 @@ func (tr *wsTransporter) Handshake(conn net.Conn, options ...HandshakeOption) (n
|
|||||||
}
|
}
|
||||||
|
|
||||||
type wssTransporter struct {
|
type wssTransporter struct {
|
||||||
*tcpTransporter
|
tcpTransporter
|
||||||
options *WSOptions
|
options *WSOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user