add obfs4 tunnel support

This commit is contained in:
rui.zheng 2017-08-05 15:46:22 +08:00
parent cfd1223550
commit 0c050c7d30
47 changed files with 11720 additions and 4 deletions

View File

@ -156,6 +156,11 @@ func initChain() (*gost.Chain, error) {
gost.ChainDialOption(chain),
)
chain = gost.NewChain() // cutoff the chain for multiplex
case "obfs4":
if err := gost.Obfs4Init(node, false); err != nil {
return nil, err
}
tr = gost.Obfs4Transporter()
default:
tr = gost.TCPTransporter()
}
@ -265,6 +270,11 @@ func serve(chain *gost.Chain) error {
ln, err = gost.H2Listener(node.Addr, tlsCfg)
case "h2c":
ln, err = gost.H2CListener(node.Addr)
case "obfs4":
if err = gost.Obfs4Init(node, true); err != nil {
return err
}
ln, err = gost.Obfs4Listener(node.Addr)
case "tcp":
ln, err = gost.TCPListener(node.Addr)
case "rtcp":

View File

@ -1,4 +1,6 @@
# username password
test\admin 123456
$test 123456
test001 123456
test002 12345678

View File

@ -46,7 +46,7 @@ func ParseNode(s string) (node Node, err error) {
}
switch node.Transport {
case "tls", "ws", "wss", "kcp", "ssh", "quic", "ssu", "http2", "h2", "h2c", "redirect":
case "tls", "ws", "wss", "kcp", "ssh", "quic", "ssu", "http2", "h2", "h2c", "redirect", "obfs4":
case "https":
node.Protocol = "http"
node.Transport = "tls"

157
obfs4.go Normal file
View 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
View File

@ -8,7 +8,7 @@ import (
)
type tlsTransporter struct {
*tcpTransporter
tcpTransporter
}
// TLSTransporter creates a Transporter that is used by TLS proxy client.

View 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.

View 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.

View File

@ -0,0 +1,219 @@
package pt
import (
"bytes"
"fmt"
"sort"
"strings"
)
// Keyvalue 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 namevalue 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 transportnamevalue 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 namevalue 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, ",")
}

View 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 namevalue 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
}

View 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 keyvalue 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)

View 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.

View 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

View 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
}

View 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
}

View 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()
}

View 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()
}

View 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)
}

View 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
}

View 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
}

View 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)

View 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
}

View 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
View 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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

340
vendor/github.com/agl/ed25519/extra25519/extra25519.go generated vendored Normal file
View 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
View File

@ -0,0 +1,69 @@
SipHash (Go)
============
[![Build Status](https://travis-ci.org/dchest/siphash.svg)](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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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 noncesfor
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
View 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
View 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
View 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
View 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
View 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
View 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
View File

@ -2,12 +2,84 @@
"comment": "",
"ignore": "test",
"package": [
{
"checksumSHA1": "Ntd/jdOFlzTpccS67XPoN8P+GvA=",
"path": "git.torproject.org/pluggable-transports/goptlib.git",
"revision": "a3ad5df6c9e7dc8117f55958b4ce99bf1e0fe291",
"revisionTime": "2017-06-26T23:50:26Z"
},
{
"checksumSHA1": "9FjAFzCl4t9s2I/2LrLcmTqprJc=",
"path": "git.torproject.org/pluggable-transports/obfs4.git/common/csrand",
"revision": "97a875ec3c0afa629405c78e750d27e4e1f851ca",
"revisionTime": "2016-11-15T19:21:38Z"
},
{
"checksumSHA1": "QcRA+Vo62wjolAdMtjWmtyE+j2g=",
"path": "git.torproject.org/pluggable-transports/obfs4.git/common/drbg",
"revision": "97a875ec3c0afa629405c78e750d27e4e1f851ca",
"revisionTime": "2016-11-15T19:21:38Z"
},
{
"checksumSHA1": "aAkBnDAHUvUpyX4G1PmedxQQpx4=",
"path": "git.torproject.org/pluggable-transports/obfs4.git/common/ntor",
"revision": "97a875ec3c0afa629405c78e750d27e4e1f851ca",
"revisionTime": "2016-11-15T19:21:38Z"
},
{
"checksumSHA1": "MNwh5v3DYMhxJQHgwCubElx5hjk=",
"path": "git.torproject.org/pluggable-transports/obfs4.git/common/probdist",
"revision": "97a875ec3c0afa629405c78e750d27e4e1f851ca",
"revisionTime": "2016-11-15T19:21:38Z"
},
{
"checksumSHA1": "g0e9BbYKC/ToxZ+GVh77L0QA8Eg=",
"path": "git.torproject.org/pluggable-transports/obfs4.git/common/replayfilter",
"revision": "97a875ec3c0afa629405c78e750d27e4e1f851ca",
"revisionTime": "2016-11-15T19:21:38Z"
},
{
"checksumSHA1": "bLggyj50IADuEWoWSoyYdU6MaFo=",
"path": "git.torproject.org/pluggable-transports/obfs4.git/transports/base",
"revision": "97a875ec3c0afa629405c78e750d27e4e1f851ca",
"revisionTime": "2016-11-15T19:21:38Z"
},
{
"checksumSHA1": "0WJ//uEKORueVXb9DDrZ7t1v00s=",
"path": "git.torproject.org/pluggable-transports/obfs4.git/transports/obfs4",
"revision": "97a875ec3c0afa629405c78e750d27e4e1f851ca",
"revisionTime": "2016-11-15T19:21:38Z"
},
{
"checksumSHA1": "EuNvu7iIjtIWStjBSXSvaRNa76M=",
"path": "git.torproject.org/pluggable-transports/obfs4.git/transports/obfs4/framing",
"revision": "97a875ec3c0afa629405c78e750d27e4e1f851ca",
"revisionTime": "2016-11-15T19:21:38Z"
},
{
"checksumSHA1": "IFJyJgPCjumDG37lEb0lyRBBGZE=",
"path": "github.com/Yawning/chacha20",
"revision": "c91e78db502ff629614837aacb7aa4efa61c651a",
"revisionTime": "2016-04-30T09:49:23Z"
},
{
"checksumSHA1": "30PBqj9BW03KCVqASvLg3bR+xYc=",
"path": "github.com/agl/ed25519/edwards25519",
"revision": "5312a61534124124185d41f09206b9fef1d88403",
"revisionTime": "2017-01-16T20:05:12Z"
},
{
"checksumSHA1": "wHBvnCJG9bkZdtxFFefU+8/WDSE=",
"path": "github.com/agl/ed25519/extra25519",
"revision": "5312a61534124124185d41f09206b9fef1d88403",
"revisionTime": "2017-01-16T20:05:12Z"
},
{
"checksumSHA1": "xqVDKHGnakGlcRhmWd1j9JYmfLc=",
"path": "github.com/dchest/siphash",
"revision": "4ebf1de738443ea7f45f02dc394c4df1942a126d",
"revisionTime": "2016-08-31T15:17:26Z"
},
{
"checksumSHA1": "aIhLeVAIrsjs63CwqmU3+GU8yT4=",
"path": "github.com/ginuerzh/gosocks4",
@ -206,12 +278,24 @@
"revision": "558b6879de74bc843225cde5686419267ff707ca",
"revisionTime": "2017-07-28T12:36:07Z"
},
{
"checksumSHA1": "Y/FcWB2/xSfX1rRp7HYhktHNw8s=",
"path": "golang.org/x/crypto/nacl/secretbox",
"revision": "558b6879de74bc843225cde5686419267ff707ca",
"revisionTime": "2017-07-28T12:36:07Z"
},
{
"checksumSHA1": "1MGpGDQqnUoRpv7VEcQrXOBydXE=",
"path": "golang.org/x/crypto/pbkdf2",
"revision": "558b6879de74bc843225cde5686419267ff707ca",
"revisionTime": "2017-07-28T12:36:07Z"
},
{
"checksumSHA1": "kVKE0OX1Xdw5mG7XKT86DLLKE2I=",
"path": "golang.org/x/crypto/poly1305",
"revision": "558b6879de74bc843225cde5686419267ff707ca",
"revisionTime": "2017-07-28T12:36:07Z"
},
{
"checksumSHA1": "qsacnnq6zmaoRpDJeTX2YDpIo6U=",
"path": "golang.org/x/crypto/salsa20",

4
ws.go
View File

@ -97,7 +97,7 @@ func (c *websocketConn) SetWriteDeadline(t time.Time) error {
}
type wsTransporter struct {
*tcpTransporter
tcpTransporter
options *WSOptions
}
@ -122,7 +122,7 @@ func (tr *wsTransporter) Handshake(conn net.Conn, options ...HandshakeOption) (n
}
type wssTransporter struct {
*tcpTransporter
tcpTransporter
options *WSOptions
}