mirror of https://github.com/ginuerzh/gost
47 changed files with 11720 additions and 4 deletions
@ -1,4 +1,6 @@ |
|||
# username password |
|||
|
|||
test\admin 123456 |
|||
$test 123456 |
|||
test001 123456 |
|||
test002 12345678 |
|||
@ -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 |
|||
} |
|||
@ -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. |
|||
@ -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 [email protected] 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. |
|||
@ -0,0 +1,219 @@ |
|||
package pt |
|||
|
|||
import ( |
|||
"bytes" |
|||
"fmt" |
|||
"sort" |
|||
"strings" |
|||
) |
|||
|
|||
// Key–value mappings for the representation of client and server options.
|
|||
|
|||
// Args maps a string key to a list of values. It is similar to url.Values.
|
|||
type Args map[string][]string |
|||
|
|||
// Get the first value associated with the given key. If there are any values
|
|||
// associated with the key, the value return has the value and ok is set to
|
|||
// true. If there are no values for the given key, value is "" and ok is false.
|
|||
// If you need access to multiple values, use the map directly.
|
|||
func (args Args) Get(key string) (value string, ok bool) { |
|||
if args == nil { |
|||
return "", false |
|||
} |
|||
vals, ok := args[key] |
|||
if !ok || len(vals) == 0 { |
|||
return "", false |
|||
} |
|||
return vals[0], true |
|||
} |
|||
|
|||
// Append value to the list of values for key.
|
|||
func (args Args) Add(key, value string) { |
|||
args[key] = append(args[key], value) |
|||
} |
|||
|
|||
// Return the index of the next unescaped byte in s that is in the term set, or
|
|||
// else the length of the string if no terminators appear. Additionally return
|
|||
// the unescaped string up to the returned index.
|
|||
func indexUnescaped(s string, term []byte) (int, string, error) { |
|||
var i int |
|||
unesc := make([]byte, 0) |
|||
for i = 0; i < len(s); i++ { |
|||
b := s[i] |
|||
// A terminator byte?
|
|||
if bytes.IndexByte(term, b) != -1 { |
|||
break |
|||
} |
|||
if b == '\\' { |
|||
i++ |
|||
if i >= len(s) { |
|||
return 0, "", fmt.Errorf("nothing following final escape in %q", s) |
|||
} |
|||
b = s[i] |
|||
} |
|||
unesc = append(unesc, b) |
|||
} |
|||
return i, string(unesc), nil |
|||
} |
|||
|
|||
// Parse a name–value mapping as from an encoded SOCKS username/password.
|
|||
//
|
|||
// "First the '<Key>=<Value>' formatted arguments MUST be escaped, such that all
|
|||
// backslash, equal sign, and semicolon characters are escaped with a
|
|||
// backslash."
|
|||
func parseClientParameters(s string) (args Args, err error) { |
|||
args = make(Args) |
|||
if len(s) == 0 { |
|||
return |
|||
} |
|||
i := 0 |
|||
for { |
|||
var key, value string |
|||
var offset, begin int |
|||
|
|||
begin = i |
|||
// Read the key.
|
|||
offset, key, err = indexUnescaped(s[i:], []byte{'=', ';'}) |
|||
if err != nil { |
|||
return |
|||
} |
|||
i += offset |
|||
// End of string or no equals sign?
|
|||
if i >= len(s) || s[i] != '=' { |
|||
err = fmt.Errorf("no equals sign in %q", s[begin:i]) |
|||
return |
|||
} |
|||
// Skip the equals sign.
|
|||
i++ |
|||
// Read the value.
|
|||
offset, value, err = indexUnescaped(s[i:], []byte{';'}) |
|||
if err != nil { |
|||
return |
|||
} |
|||
i += offset |
|||
if len(key) == 0 { |
|||
err = fmt.Errorf("empty key in %q", s[begin:i]) |
|||
return |
|||
} |
|||
args.Add(key, value) |
|||
if i >= len(s) { |
|||
break |
|||
} |
|||
// Skip the semicolon.
|
|||
i++ |
|||
} |
|||
return args, nil |
|||
} |
|||
|
|||
// Parse a transport–name–value mapping as from TOR_PT_SERVER_TRANSPORT_OPTIONS.
|
|||
//
|
|||
// "...a semicolon-separated list of <key>:<value> pairs, where <key> is a PT
|
|||
// name and <value> is a k=v string value with options that are to be passed to
|
|||
// the transport. Colons, semicolons, equal signs and backslashes must be
|
|||
// escaped with a backslash."
|
|||
// Example: scramblesuit:key=banana;automata:rule=110;automata:depth=3
|
|||
func parseServerTransportOptions(s string) (opts map[string]Args, err error) { |
|||
opts = make(map[string]Args) |
|||
if len(s) == 0 { |
|||
return |
|||
} |
|||
i := 0 |
|||
for { |
|||
var methodName, key, value string |
|||
var offset, begin int |
|||
|
|||
begin = i |
|||
// Read the method name.
|
|||
offset, methodName, err = indexUnescaped(s[i:], []byte{':', '=', ';'}) |
|||
if err != nil { |
|||
return |
|||
} |
|||
i += offset |
|||
// End of string or no colon?
|
|||
if i >= len(s) || s[i] != ':' { |
|||
err = fmt.Errorf("no colon in %q", s[begin:i]) |
|||
return |
|||
} |
|||
// Skip the colon.
|
|||
i++ |
|||
// Read the key.
|
|||
offset, key, err = indexUnescaped(s[i:], []byte{'=', ';'}) |
|||
if err != nil { |
|||
return |
|||
} |
|||
i += offset |
|||
// End of string or no equals sign?
|
|||
if i >= len(s) || s[i] != '=' { |
|||
err = fmt.Errorf("no equals sign in %q", s[begin:i]) |
|||
return |
|||
} |
|||
// Skip the equals sign.
|
|||
i++ |
|||
// Read the value.
|
|||
offset, value, err = indexUnescaped(s[i:], []byte{';'}) |
|||
if err != nil { |
|||
return |
|||
} |
|||
i += offset |
|||
if len(methodName) == 0 { |
|||
err = fmt.Errorf("empty method name in %q", s[begin:i]) |
|||
return |
|||
} |
|||
if len(key) == 0 { |
|||
err = fmt.Errorf("empty key in %q", s[begin:i]) |
|||
return |
|||
} |
|||
if opts[methodName] == nil { |
|||
opts[methodName] = make(Args) |
|||
} |
|||
opts[methodName].Add(key, value) |
|||
if i >= len(s) { |
|||
break |
|||
} |
|||
// Skip the semicolon.
|
|||
i++ |
|||
} |
|||
return opts, nil |
|||
} |
|||
|
|||
// Escape backslashes and all the bytes that are in set.
|
|||
func backslashEscape(s string, set []byte) string { |
|||
var buf bytes.Buffer |
|||
for _, b := range []byte(s) { |
|||
if b == '\\' || bytes.IndexByte(set, b) != -1 { |
|||
buf.WriteByte('\\') |
|||
} |
|||
buf.WriteByte(b) |
|||
} |
|||
return buf.String() |
|||
} |
|||
|
|||
// Encode a name–value mapping so that it is suitable to go in the ARGS option
|
|||
// of an SMETHOD line. The output is sorted by key. The "ARGS:" prefix is not
|
|||
// added.
|
|||
//
|
|||
// "Equal signs and commas [and backslashes] MUST be escaped with a backslash."
|
|||
func encodeSmethodArgs(args Args) string { |
|||
if args == nil { |
|||
return "" |
|||
} |
|||
|
|||
keys := make([]string, 0, len(args)) |
|||
for key := range args { |
|||
keys = append(keys, key) |
|||
} |
|||
sort.Strings(keys) |
|||
|
|||
escape := func(s string) string { |
|||
return backslashEscape(s, []byte{'=', ','}) |
|||
} |
|||
|
|||
var pairs []string |
|||
for _, key := range keys { |
|||
for _, value := range args[key] { |
|||
pairs = append(pairs, escape(key)+"="+escape(value)) |
|||
} |
|||
} |
|||
|
|||
return strings.Join(pairs, ",") |
|||
} |
|||
@ -0,0 +1,949 @@ |
|||
// Package pt implements the Tor pluggable transports specification.
|
|||
//
|
|||
// Sample client usage:
|
|||
// var ptInfo pt.ClientInfo
|
|||
// ...
|
|||
// func handler(conn *pt.SocksConn) error {
|
|||
// defer conn.Close()
|
|||
// remote, err := net.Dial("tcp", conn.Req.Target)
|
|||
// if err != nil {
|
|||
// conn.Reject()
|
|||
// return err
|
|||
// }
|
|||
// defer remote.Close()
|
|||
// err = conn.Grant(remote.RemoteAddr().(*net.TCPAddr))
|
|||
// if err != nil {
|
|||
// return err
|
|||
// }
|
|||
// // do something with conn and remote.
|
|||
// return nil
|
|||
// }
|
|||
// func acceptLoop(ln *pt.SocksListener) error {
|
|||
// defer ln.Close()
|
|||
// for {
|
|||
// conn, err := ln.AcceptSocks()
|
|||
// if err != nil {
|
|||
// if e, ok := err.(net.Error); ok && e.Temporary() {
|
|||
// continue
|
|||
// }
|
|||
// return err
|
|||
// }
|
|||
// go handler(conn)
|
|||
// }
|
|||
// return nil
|
|||
// }
|
|||
// ...
|
|||
// func main() {
|
|||
// var err error
|
|||
// ptInfo, err = pt.ClientSetup(nil)
|
|||
// if err != nil {
|
|||
// os.Exit(1)
|
|||
// }
|
|||
// if ptInfo.ProxyURL != nil {
|
|||
// // you need to interpret the proxy URL yourself
|
|||
// // call pt.ProxyDone instead if it's a type you understand
|
|||
// pt.ProxyError(fmt.Sprintf("proxy %s is not supported", ptInfo.ProxyURL))
|
|||
// os.Exit(1)
|
|||
// }
|
|||
// for _, methodName := range ptInfo.MethodNames {
|
|||
// switch methodName {
|
|||
// case "foo":
|
|||
// ln, err := pt.ListenSocks("tcp", "127.0.0.1:0")
|
|||
// if err != nil {
|
|||
// pt.CmethodError(methodName, err.Error())
|
|||
// break
|
|||
// }
|
|||
// go acceptLoop(ln)
|
|||
// pt.Cmethod(methodName, ln.Version(), ln.Addr())
|
|||
// default:
|
|||
// pt.CmethodError(methodName, "no such method")
|
|||
// }
|
|||
// }
|
|||
// pt.CmethodsDone()
|
|||
// }
|
|||
//
|
|||
// Sample server usage:
|
|||
// var ptInfo pt.ServerInfo
|
|||
// ...
|
|||
// func handler(conn net.Conn) error {
|
|||
// defer conn.Close()
|
|||
// or, err := pt.DialOr(&ptInfo, conn.RemoteAddr().String(), "foo")
|
|||
// if err != nil {
|
|||
// return
|
|||
// }
|
|||
// defer or.Close()
|
|||
// // do something with or and conn
|
|||
// return nil
|
|||
// }
|
|||
// func acceptLoop(ln net.Listener) error {
|
|||
// defer ln.Close()
|
|||
// for {
|
|||
// conn, err := ln.Accept()
|
|||
// if err != nil {
|
|||
// if e, ok := err.(net.Error); ok && e.Temporary() {
|
|||
// continue
|
|||
// }
|
|||
// return err
|
|||
// }
|
|||
// go handler(conn)
|
|||
// }
|
|||
// return nil
|
|||
// }
|
|||
// ...
|
|||
// func main() {
|
|||
// var err error
|
|||
// ptInfo, err = pt.ServerSetup(nil)
|
|||
// if err != nil {
|
|||
// os.Exit(1)
|
|||
// }
|
|||
// for _, bindaddr := range ptInfo.Bindaddrs {
|
|||
// switch bindaddr.MethodName {
|
|||
// case "foo":
|
|||
// ln, err := net.ListenTCP("tcp", bindaddr.Addr)
|
|||
// if err != nil {
|
|||
// pt.SmethodError(bindaddr.MethodName, err.Error())
|
|||
// break
|
|||
// }
|
|||
// go acceptLoop(ln)
|
|||
// pt.Smethod(bindaddr.MethodName, ln.Addr())
|
|||
// default:
|
|||
// pt.SmethodError(bindaddr.MethodName, "no such method")
|
|||
// }
|
|||
// }
|
|||
// pt.SmethodsDone()
|
|||
// }
|
|||
//
|
|||
// Some additional care is needed to handle signals and shutdown properly. See
|
|||
// the example programs dummy-client and dummy-server.
|
|||
//
|
|||
// Tor pluggable transports specification:
|
|||
// https://spec.torproject.org/pt-spec
|
|||
//
|
|||
// Extended ORPort:
|
|||
// https://gitweb.torproject.org/torspec.git/tree/proposals/196-transport-control-ports.txt
|
|||
//
|
|||
// Extended ORPort Authentication:
|
|||
// https://gitweb.torproject.org/torspec.git/tree/proposals/217-ext-orport-auth.txt
|
|||
//
|
|||
// Pluggable Transport through SOCKS proxy:
|
|||
// https://gitweb.torproject.org/torspec.git/tree/proposals/232-pluggable-transports-through-proxy.txt
|
|||
//
|
|||
// The package implements a SOCKS5 server sufficient for a Tor client transport
|
|||
// plugin.
|
|||
//
|
|||
// https://www.ietf.org/rfc/rfc1928.txt
|
|||
// https://www.ietf.org/rfc/rfc1929.txt
|
|||
package pt |
|||
|
|||
import ( |
|||
"bytes" |
|||
"crypto/hmac" |
|||
"crypto/rand" |
|||
"crypto/sha256" |
|||
"crypto/subtle" |
|||
"encoding/binary" |
|||
"fmt" |
|||
"io" |
|||
"net" |
|||
"net/url" |
|||
"os" |
|||
"strconv" |
|||
"strings" |
|||
"time" |
|||
) |
|||
|
|||
// This type wraps a Write method and calls Sync after each Write.
|
|||
type syncWriter struct { |
|||
*os.File |
|||
} |
|||
|
|||
// Call File.Write and then Sync. An error is returned if either operation
|
|||
// returns an error.
|
|||
func (w syncWriter) Write(p []byte) (n int, err error) { |
|||
n, err = w.File.Write(p) |
|||
if err != nil { |
|||
return |
|||
} |
|||
err = w.Sync() |
|||
return |
|||
} |
|||
|
|||
// Writer to which pluggable transports negotiation messages are written. It
|
|||
// defaults to a Writer that writes to os.Stdout and calls Sync after each
|
|||
// write.
|
|||
//
|
|||
// You may, for example, log pluggable transports messages by defining a Writer
|
|||
// that logs what is written to it:
|
|||
// type logWriteWrapper struct {
|
|||
// io.Writer
|
|||
// }
|
|||
//
|
|||
// func (w logWriteWrapper) Write(p []byte) (int, error) {
|
|||
// log.Print(string(p))
|
|||
// return w.Writer.Write(p)
|
|||
// }
|
|||
// and then redefining Stdout:
|
|||
// pt.Stdout = logWriteWrapper{pt.Stdout}
|
|||
var Stdout io.Writer = syncWriter{os.Stdout} |
|||
|
|||
// Represents an error that can happen during negotiation, for example
|
|||
// ENV-ERROR. When an error occurs, we print it to stdout and also pass it up
|
|||
// the return chain.
|
|||
type ptErr struct { |
|||
Keyword string |
|||
Args []string |
|||
} |
|||
|
|||
// Implements the error interface.
|
|||
func (err *ptErr) Error() string { |
|||
return formatline(err.Keyword, err.Args...) |
|||
} |
|||
|
|||
func getenv(key string) string { |
|||
return os.Getenv(key) |
|||
} |
|||
|
|||
// Returns an ENV-ERROR if the environment variable isn't set.
|
|||
func getenvRequired(key string) (string, error) { |
|||
value := os.Getenv(key) |
|||
if value == "" { |
|||
return "", envError(fmt.Sprintf("no %s environment variable", key)) |
|||
} |
|||
return value, nil |
|||
} |
|||
|
|||
// Returns true iff keyword contains only bytes allowed in a PT→Tor output line
|
|||
// keyword.
|
|||
// <KeywordChar> ::= <any US-ASCII alphanumeric, dash, and underscore>
|
|||
func keywordIsSafe(keyword string) bool { |
|||
for _, b := range []byte(keyword) { |
|||
switch { |
|||
case '0' <= b && b <= '9': |
|||
continue |
|||
case 'A' <= b && b <= 'Z': |
|||
continue |
|||
case 'a' <= b && b <= 'z': |
|||
continue |
|||
case b == '-' || b == '_': |
|||
continue |
|||
default: |
|||
return false |
|||
} |
|||
} |
|||
return true |
|||
} |
|||
|
|||
// Returns true iff arg contains only bytes allowed in a PT→Tor output line arg.
|
|||
// <ArgChar> ::= <any US-ASCII character but NUL or NL>
|
|||
func argIsSafe(arg string) bool { |
|||
for _, b := range []byte(arg) { |
|||
if b >= '\x80' || b == '\x00' || b == '\n' { |
|||
return false |
|||
} |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func formatline(keyword string, v ...string) string { |
|||
var buf bytes.Buffer |
|||
if !keywordIsSafe(keyword) { |
|||
panic(fmt.Sprintf("keyword %q contains forbidden bytes", keyword)) |
|||
} |
|||
buf.WriteString(keyword) |
|||
for _, x := range v { |
|||
if !argIsSafe(x) { |
|||
panic(fmt.Sprintf("arg %q contains forbidden bytes", x)) |
|||
} |
|||
buf.WriteString(" " + x) |
|||
} |
|||
return buf.String() |
|||
} |
|||
|
|||
// Print a pluggable transports protocol line to Stdout. The line consists of a
|
|||
// keyword followed by any number of space-separated arg strings. Panics if
|
|||
// there are forbidden bytes in the keyword or the args (pt-spec.txt 2.2.1).
|
|||
func line(keyword string, v ...string) { |
|||
fmt.Fprintln(Stdout, formatline(keyword, v...)) |
|||
} |
|||
|
|||
// Emit and return the given error as a ptErr.
|
|||
func doError(keyword string, v ...string) *ptErr { |
|||
line(keyword, v...) |
|||
return &ptErr{keyword, v} |
|||
} |
|||
|
|||
// Emit an ENV-ERROR line with explanation text. Returns a representation of the
|
|||
// error.
|
|||
func envError(msg string) error { |
|||
return doError("ENV-ERROR", msg) |
|||
} |
|||
|
|||
// Emit a VERSION-ERROR line with explanation text. Returns a representation of
|
|||
// the error.
|
|||
func versionError(msg string) error { |
|||
return doError("VERSION-ERROR", msg) |
|||
} |
|||
|
|||
// Emit a CMETHOD-ERROR line with explanation text. Returns a representation of
|
|||
// the error.
|
|||
func CmethodError(methodName, msg string) error { |
|||
return doError("CMETHOD-ERROR", methodName, msg) |
|||
} |
|||
|
|||
// Emit an SMETHOD-ERROR line with explanation text. Returns a representation of
|
|||
// the error.
|
|||
func SmethodError(methodName, msg string) error { |
|||
return doError("SMETHOD-ERROR", methodName, msg) |
|||
} |
|||
|
|||
// Emit a PROXY-ERROR line with explanation text. Returns a representation of
|
|||
// the error.
|
|||
func ProxyError(msg string) error { |
|||
return doError("PROXY-ERROR", msg) |
|||
} |
|||
|
|||
// Emit a CMETHOD line. socks must be "socks4" or "socks5". Call this once for
|
|||
// each listening client SOCKS port.
|
|||
func Cmethod(name string, socks string, addr net.Addr) { |
|||
line("CMETHOD", name, socks, addr.String()) |
|||
} |
|||
|
|||
// Emit a CMETHODS DONE line. Call this after opening all client listeners.
|
|||
func CmethodsDone() { |
|||
line("CMETHODS", "DONE") |
|||
} |
|||
|
|||
// Emit an SMETHOD line. Call this once for each listening server port.
|
|||
func Smethod(name string, addr net.Addr) { |
|||
line("SMETHOD", name, addr.String()) |
|||
} |
|||
|
|||
// Emit an SMETHOD line with an ARGS option. args is a name–value mapping that
|
|||
// will be added to the server's extrainfo document.
|
|||
//
|
|||
// This is an example of how to check for a required option:
|
|||
// secret, ok := bindaddr.Options.Get("shared-secret")
|
|||
// if ok {
|
|||
// args := pt.Args{}
|
|||
// args.Add("shared-secret", secret)
|
|||
// pt.SmethodArgs(bindaddr.MethodName, ln.Addr(), args)
|
|||
// } else {
|
|||
// pt.SmethodError(bindaddr.MethodName, "need a shared-secret option")
|
|||
// }
|
|||
// Or, if you just want to echo back the options provided by Tor from the
|
|||
// TransportServerOptions configuration,
|
|||
// pt.SmethodArgs(bindaddr.MethodName, ln.Addr(), bindaddr.Options)
|
|||
func SmethodArgs(name string, addr net.Addr, args Args) { |
|||
line("SMETHOD", name, addr.String(), "ARGS:"+encodeSmethodArgs(args)) |
|||
} |
|||
|
|||
// Emit an SMETHODS DONE line. Call this after opening all server listeners.
|
|||
func SmethodsDone() { |
|||
line("SMETHODS", "DONE") |
|||
} |
|||
|
|||
// Emit a PROXY DONE line. Call this after parsing ClientInfo.ProxyURL.
|
|||
func ProxyDone() { |
|||
fmt.Fprintf(Stdout, "PROXY DONE\n") |
|||
} |
|||
|
|||
// Get a pluggable transports version offered by Tor and understood by us, if
|
|||
// any. The only version we understand is "1". This function reads the
|
|||
// environment variable TOR_PT_MANAGED_TRANSPORT_VER.
|
|||
func getManagedTransportVer() (string, error) { |
|||
const transportVersion = "1" |
|||
managedTransportVer, err := getenvRequired("TOR_PT_MANAGED_TRANSPORT_VER") |
|||
if err != nil { |
|||
return "", err |
|||
} |
|||
for _, offered := range strings.Split(managedTransportVer, ",") { |
|||
if offered == transportVersion { |
|||
return offered, nil |
|||
} |
|||
} |
|||
return "", versionError("no-version") |
|||
} |
|||
|
|||
// Return the directory name in the TOR_PT_STATE_LOCATION environment variable,
|
|||
// creating it if it doesn't exist. Returns non-nil error if
|
|||
// TOR_PT_STATE_LOCATION is not set or if there is an error creating the
|
|||
// directory.
|
|||
func MakeStateDir() (string, error) { |
|||
dir, err := getenvRequired("TOR_PT_STATE_LOCATION") |
|||
if err != nil { |
|||
return "", err |
|||
} |
|||
err = os.MkdirAll(dir, 0700) |
|||
return dir, err |
|||
} |
|||
|
|||
// Get the list of method names requested by Tor. This function reads the
|
|||
// environment variable TOR_PT_CLIENT_TRANSPORTS.
|
|||
func getClientTransports() ([]string, error) { |
|||
clientTransports, err := getenvRequired("TOR_PT_CLIENT_TRANSPORTS") |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
return strings.Split(clientTransports, ","), nil |
|||
} |
|||
|
|||
// Get the upstream proxy URL. Returns nil if no proxy is requested. The
|
|||
// function ensures that the Scheme and Host fields are set; i.e., that the URL
|
|||
// is absolute. It additionally checks that the Host field contains both a host
|
|||
// and a port part. This function reads the environment variable TOR_PT_PROXY.
|
|||
//
|
|||
// This function doesn't check that the scheme is one of Tor's supported proxy
|
|||
// schemes; that is, one of "http", "socks5", or "socks4a". The caller must be
|
|||
// able to handle any returned scheme (which may be by calling ProxyError if
|
|||
// it doesn't know how to handle the scheme).
|
|||
func getProxyURL() (*url.URL, error) { |
|||
rawurl := os.Getenv("TOR_PT_PROXY") |
|||
if rawurl == "" { |
|||
return nil, nil |
|||
} |
|||
u, err := url.Parse(rawurl) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
if u.Scheme == "" { |
|||
return nil, fmt.Errorf("missing scheme") |
|||
} |
|||
if u.Host == "" { |
|||
return nil, fmt.Errorf("missing authority") |
|||
} |
|||
host, port, err := net.SplitHostPort(u.Host) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
if host == "" { |
|||
return nil, fmt.Errorf("missing host") |
|||
} |
|||
if port == "" { |
|||
return nil, fmt.Errorf("missing port") |
|||
} |
|||
return u, nil |
|||
} |
|||
|
|||
// This structure is returned by ClientSetup. It consists of a list of method
|
|||
// names and the upstream proxy URL, if any.
|
|||
type ClientInfo struct { |
|||
MethodNames []string |
|||
ProxyURL *url.URL |
|||
} |
|||
|
|||
// Check the client pluggable transports environment, emitting an error message
|
|||
// and returning a non-nil error if any error is encountered. Returns a
|
|||
// ClientInfo struct.
|
|||
//
|
|||
// If your program needs to know whether to call ClientSetup or ServerSetup
|
|||
// (i.e., if the same program can be run as either a client or a server), check
|
|||
// whether the TOR_PT_CLIENT_TRANSPORTS environment variable is set:
|
|||
// if os.Getenv("TOR_PT_CLIENT_TRANSPORTS") != "" {
|
|||
// // Client mode; call pt.ClientSetup.
|
|||
// } else {
|
|||
// // Server mode; call pt.ServerSetup.
|
|||
// }
|
|||
//
|
|||
// Always pass nil for the unused single parameter. In the past, the parameter
|
|||
// was a list of transport names to use in case Tor requested "*". That feature
|
|||
// was never implemented and has been removed from the pluggable transports
|
|||
// specification.
|
|||
// https://bugs.torproject.org/15612
|
|||
func ClientSetup(_ []string) (info ClientInfo, err error) { |
|||
ver, err := getManagedTransportVer() |
|||
if err != nil { |
|||
return |
|||
} |
|||
line("VERSION", ver) |
|||
|
|||
info.MethodNames, err = getClientTransports() |
|||
if err != nil { |
|||
return |
|||
} |
|||
|
|||
info.ProxyURL, err = getProxyURL() |
|||
if err != nil { |
|||
return |
|||
} |
|||
|
|||
return info, nil |
|||
} |
|||
|
|||
// A combination of a method name and an address, as extracted from
|
|||
// TOR_PT_SERVER_BINDADDR.
|
|||
type Bindaddr struct { |
|||
MethodName string |
|||
Addr *net.TCPAddr |
|||
// Options from TOR_PT_SERVER_TRANSPORT_OPTIONS that pertain to this
|
|||
// transport.
|
|||
Options Args |
|||
} |
|||
|
|||
func parsePort(portStr string) (int, error) { |
|||
port, err := strconv.ParseUint(portStr, 10, 16) |
|||
return int(port), err |
|||
} |
|||
|
|||
// Resolve an address string into a net.TCPAddr. We are a bit more strict than
|
|||
// net.ResolveTCPAddr; we don't allow an empty host or port, and the host part
|
|||
// must be a literal IP address.
|
|||
func resolveAddr(addrStr string) (*net.TCPAddr, error) { |
|||
ipStr, portStr, err := net.SplitHostPort(addrStr) |
|||
if err != nil { |
|||
// Before the fixing of bug #7011, tor doesn't put brackets around IPv6
|
|||
// addresses. Split after the last colon, assuming it is a port
|
|||
// separator, and try adding the brackets.
|
|||
// https://bugs.torproject.org/7011
|
|||
parts := strings.Split(addrStr, ":") |
|||
if len(parts) <= 2 { |
|||
return nil, err |
|||
} |
|||
addrStr := "[" + strings.Join(parts[:len(parts)-1], ":") + "]:" + parts[len(parts)-1] |
|||
ipStr, portStr, err = net.SplitHostPort(addrStr) |
|||
} |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
if ipStr == "" { |
|||
return nil, net.InvalidAddrError(fmt.Sprintf("address string %q lacks a host part", addrStr)) |
|||
} |
|||
if portStr == "" { |
|||
return nil, net.InvalidAddrError(fmt.Sprintf("address string %q lacks a port part", addrStr)) |
|||
} |
|||
ip := net.ParseIP(ipStr) |
|||
if ip == nil { |
|||
return nil, net.InvalidAddrError(fmt.Sprintf("not an IP string: %q", ipStr)) |
|||
} |
|||
port, err := parsePort(portStr) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
return &net.TCPAddr{IP: ip, Port: port}, nil |
|||
} |
|||
|
|||
// Return a new slice, the members of which are those members of addrs having a
|
|||
// MethodName in methodNames.
|
|||
func filterBindaddrs(addrs []Bindaddr, methodNames []string) []Bindaddr { |
|||
var result []Bindaddr |
|||
|
|||
for _, ba := range addrs { |
|||
for _, methodName := range methodNames { |
|||
if ba.MethodName == methodName { |
|||
result = append(result, ba) |
|||
break |
|||
} |
|||
} |
|||
} |
|||
|
|||
return result |
|||
} |
|||
|
|||
// Return an array of Bindaddrs, being the contents of TOR_PT_SERVER_BINDADDR
|
|||
// with keys filtered by TOR_PT_SERVER_TRANSPORTS. Transport-specific options
|
|||
// from TOR_PT_SERVER_TRANSPORT_OPTIONS are assigned to the Options member.
|
|||
func getServerBindaddrs() ([]Bindaddr, error) { |
|||
var result []Bindaddr |
|||
|
|||
// Parse the list of server transport options.
|
|||
serverTransportOptions := getenv("TOR_PT_SERVER_TRANSPORT_OPTIONS") |
|||
optionsMap, err := parseServerTransportOptions(serverTransportOptions) |
|||
if err != nil { |
|||
return nil, envError(fmt.Sprintf("TOR_PT_SERVER_TRANSPORT_OPTIONS: %q: %s", serverTransportOptions, err.Error())) |
|||
} |
|||
|
|||
// Get the list of all requested bindaddrs.
|
|||
serverBindaddr, err := getenvRequired("TOR_PT_SERVER_BINDADDR") |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
seenMethods := make(map[string]bool) |
|||
for _, spec := range strings.Split(serverBindaddr, ",") { |
|||
var bindaddr Bindaddr |
|||
|
|||
parts := strings.SplitN(spec, "-", 2) |
|||
if len(parts) != 2 { |
|||
return nil, envError(fmt.Sprintf("TOR_PT_SERVER_BINDADDR: %q: doesn't contain \"-\"", spec)) |
|||
} |
|||
bindaddr.MethodName = parts[0] |
|||
// Check for duplicate method names: "Applications MUST NOT set
|
|||
// more than one <address>:<port> pair per PT name."
|
|||
if seenMethods[bindaddr.MethodName] { |
|||
return nil, envError(fmt.Sprintf("TOR_PT_SERVER_BINDADDR: %q: duplicate method name %q", spec, bindaddr.MethodName)) |
|||
} |
|||
seenMethods[bindaddr.MethodName] = true |
|||
addr, err := resolveAddr(parts[1]) |
|||
if err != nil { |
|||
return nil, envError(fmt.Sprintf("TOR_PT_SERVER_BINDADDR: %q: %s", spec, err.Error())) |
|||
} |
|||
bindaddr.Addr = addr |
|||
bindaddr.Options = optionsMap[bindaddr.MethodName] |
|||
result = append(result, bindaddr) |
|||
} |
|||
|
|||
// Filter by TOR_PT_SERVER_TRANSPORTS.
|
|||
serverTransports, err := getenvRequired("TOR_PT_SERVER_TRANSPORTS") |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
result = filterBindaddrs(result, strings.Split(serverTransports, ",")) |
|||
|
|||
return result, nil |
|||
} |
|||
|
|||
func readAuthCookie(f io.Reader) ([]byte, error) { |
|||
authCookieHeader := []byte("! Extended ORPort Auth Cookie !\x0a") |
|||
buf := make([]byte, 64) |
|||
|
|||
n, err := io.ReadFull(f, buf) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
// Check that the file ends here.
|
|||
n, err = f.Read(make([]byte, 1)) |
|||
if n != 0 { |
|||
return nil, fmt.Errorf("file is longer than 64 bytes") |
|||
} else if err != io.EOF { |
|||
return nil, fmt.Errorf("did not find EOF at end of file") |
|||
} |
|||
header := buf[0:32] |
|||
cookie := buf[32:64] |
|||
if subtle.ConstantTimeCompare(header, authCookieHeader) != 1 { |
|||
return nil, fmt.Errorf("missing auth cookie header") |
|||
} |
|||
|
|||
return cookie, nil |
|||
} |
|||
|
|||
// Read and validate the contents of an auth cookie file. Returns the 32-byte
|
|||
// cookie. See section 4.2.1.2 of 217-ext-orport-auth.txt.
|
|||
func readAuthCookieFile(filename string) ([]byte, error) { |
|||
f, err := os.Open(filename) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
defer f.Close() |
|||
|
|||
return readAuthCookie(f) |
|||
} |
|||
|
|||
// This structure is returned by ServerSetup. It consists of a list of
|
|||
// Bindaddrs, an address for the ORPort, an address for the extended ORPort (if
|
|||
// any), and an authentication cookie (if any).
|
|||
type ServerInfo struct { |
|||
Bindaddrs []Bindaddr |
|||
OrAddr *net.TCPAddr |
|||
ExtendedOrAddr *net.TCPAddr |
|||
AuthCookiePath string |
|||
} |
|||
|
|||
// Check the server pluggable transports environment, emitting an error message
|
|||
// and returning a non-nil error if any error is encountered. Resolves the
|
|||
// various requested bind addresses, the server ORPort and extended ORPort, and
|
|||
// reads the auth cookie file. Returns a ServerInfo struct.
|
|||
//
|
|||
// If your program needs to know whether to call ClientSetup or ServerSetup
|
|||
// (i.e., if the same program can be run as either a client or a server), check
|
|||
// whether the TOR_PT_CLIENT_TRANSPORTS environment variable is set:
|
|||
// if os.Getenv("TOR_PT_CLIENT_TRANSPORTS") != "" {
|
|||
// // Client mode; call pt.ClientSetup.
|
|||
// } else {
|
|||
// // Server mode; call pt.ServerSetup.
|
|||
// }
|
|||
//
|
|||
// Always pass nil for the unused single parameter. In the past, the parameter
|
|||
// was a list of transport names to use in case Tor requested "*". That feature
|
|||
// was never implemented and has been removed from the pluggable transports
|
|||
// specification.
|
|||
// https://bugs.torproject.org/15612
|
|||
func ServerSetup(_ []string) (info ServerInfo, err error) { |
|||
ver, err := getManagedTransportVer() |
|||
if err != nil { |
|||
return |
|||
} |
|||
line("VERSION", ver) |
|||
|
|||
info.Bindaddrs, err = getServerBindaddrs() |
|||
if err != nil { |
|||
return |
|||
} |
|||
|
|||
orPort := getenv("TOR_PT_ORPORT") |
|||
if orPort != "" { |
|||
info.OrAddr, err = resolveAddr(orPort) |
|||
if err != nil { |
|||
err = envError(fmt.Sprintf("cannot resolve TOR_PT_ORPORT %q: %s", orPort, err.Error())) |
|||
return |
|||
} |
|||
} |
|||
|
|||
info.AuthCookiePath = getenv("TOR_PT_AUTH_COOKIE_FILE") |
|||
|
|||
extendedOrPort := getenv("TOR_PT_EXTENDED_SERVER_PORT") |
|||
if extendedOrPort != "" { |
|||
if info.AuthCookiePath == "" { |
|||
err = envError("need TOR_PT_AUTH_COOKIE_FILE environment variable with TOR_PT_EXTENDED_SERVER_PORT") |
|||
return |
|||
} |
|||
info.ExtendedOrAddr, err = resolveAddr(extendedOrPort) |
|||
if err != nil { |
|||
err = envError(fmt.Sprintf("cannot resolve TOR_PT_EXTENDED_SERVER_PORT %q: %s", extendedOrPort, err.Error())) |
|||
return |
|||
} |
|||
} |
|||
|
|||
// Need either OrAddr or ExtendedOrAddr.
|
|||
if info.OrAddr == nil && info.ExtendedOrAddr == nil { |
|||
err = envError("need TOR_PT_ORPORT or TOR_PT_EXTENDED_SERVER_PORT environment variable") |
|||
return |
|||
} |
|||
|
|||
return info, nil |
|||
} |
|||
|
|||
// See 217-ext-orport-auth.txt section 4.2.1.3.
|
|||
func computeServerHash(authCookie, clientNonce, serverNonce []byte) []byte { |
|||
h := hmac.New(sha256.New, authCookie) |
|||
io.WriteString(h, "ExtORPort authentication server-to-client hash") |
|||
h.Write(clientNonce) |
|||
h.Write(serverNonce) |
|||
return h.Sum([]byte{}) |
|||
} |
|||
|
|||
// See 217-ext-orport-auth.txt section 4.2.1.3.
|
|||
func computeClientHash(authCookie, clientNonce, serverNonce []byte) []byte { |
|||
h := hmac.New(sha256.New, authCookie) |
|||
io.WriteString(h, "ExtORPort authentication client-to-server hash") |
|||
h.Write(clientNonce) |
|||
h.Write(serverNonce) |
|||
return h.Sum([]byte{}) |
|||
} |
|||
|
|||
func extOrPortAuthenticate(s io.ReadWriter, info *ServerInfo) error { |
|||
// Read auth types. 217-ext-orport-auth.txt section 4.1.
|
|||
var authTypes [256]bool |
|||
var count int |
|||
for count = 0; count < 256; count++ { |
|||
buf := make([]byte, 1) |
|||
_, err := io.ReadFull(s, buf) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
b := buf[0] |
|||
if b == 0 { |
|||
break |
|||
} |
|||
authTypes[b] = true |
|||
} |
|||
if count >= 256 { |
|||
return fmt.Errorf("read 256 auth types without seeing \\x00") |
|||
} |
|||
|
|||
// We support only type 1, SAFE_COOKIE.
|
|||
if !authTypes[1] { |
|||
return fmt.Errorf("server didn't offer auth type 1") |
|||
} |
|||
_, err := s.Write([]byte{1}) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
|
|||
clientNonce := make([]byte, 32) |
|||
clientHash := make([]byte, 32) |
|||
serverNonce := make([]byte, 32) |
|||
serverHash := make([]byte, 32) |
|||
|
|||
_, err = io.ReadFull(rand.Reader, clientNonce) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
_, err = s.Write(clientNonce) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
|
|||
_, err = io.ReadFull(s, serverHash) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
_, err = io.ReadFull(s, serverNonce) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
|
|||
// Work around tor bug #15240 where the auth cookie is generated after
|
|||
// pluggable transports are launched, leading to a stale cookie getting
|
|||
// cached forever if it is only read once as part of ServerSetup.
|
|||
// https://bugs.torproject.org/15240
|
|||
authCookie, err := readAuthCookieFile(info.AuthCookiePath) |
|||
if err != nil { |
|||
return fmt.Errorf("error reading TOR_PT_AUTH_COOKIE_FILE %q: %s", info.AuthCookiePath, err.Error()) |
|||
} |
|||
|
|||
expectedServerHash := computeServerHash(authCookie, clientNonce, serverNonce) |
|||
if subtle.ConstantTimeCompare(serverHash, expectedServerHash) != 1 { |
|||
return fmt.Errorf("mismatch in server hash") |
|||
} |
|||
|
|||
clientHash = computeClientHash(authCookie, clientNonce, serverNonce) |
|||
_, err = s.Write(clientHash) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
|
|||
status := make([]byte, 1) |
|||
_, err = io.ReadFull(s, status) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
if status[0] != 1 { |
|||
return fmt.Errorf("server rejected authentication") |
|||
} |
|||
|
|||
return nil |
|||
} |
|||
|
|||
// See section 3.1.1 of 196-transport-control-ports.txt.
|
|||
const ( |
|||
extOrCmdDone = 0x0000 |
|||
extOrCmdUserAddr = 0x0001 |
|||
extOrCmdTransport = 0x0002 |
|||
extOrCmdOkay = 0x1000 |
|||
extOrCmdDeny = 0x1001 |
|||
) |
|||
|
|||
func extOrPortSendCommand(s io.Writer, cmd uint16, body []byte) error { |
|||
var buf bytes.Buffer |
|||
if len(body) > 65535 { |
|||
return fmt.Errorf("body length %d exceeds maximum of 65535", len(body)) |
|||
} |
|||
err := binary.Write(&buf, binary.BigEndian, cmd) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
err = binary.Write(&buf, binary.BigEndian, uint16(len(body))) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
err = binary.Write(&buf, binary.BigEndian, body) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
_, err = s.Write(buf.Bytes()) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
|
|||
return nil |
|||
} |
|||
|
|||
// Send a USERADDR command on s. See section 3.1.2.1 of
|
|||
// 196-transport-control-ports.txt.
|
|||
func extOrPortSendUserAddr(s io.Writer, addr string) error { |
|||
return extOrPortSendCommand(s, extOrCmdUserAddr, []byte(addr)) |
|||
} |
|||
|
|||
// Send a TRANSPORT command on s. See section 3.1.2.2 of
|
|||
// 196-transport-control-ports.txt.
|
|||
func extOrPortSendTransport(s io.Writer, methodName string) error { |
|||
return extOrPortSendCommand(s, extOrCmdTransport, []byte(methodName)) |
|||
} |
|||
|
|||
// Send a DONE command on s. See section 3.1 of 196-transport-control-ports.txt.
|
|||
func extOrPortSendDone(s io.Writer) error { |
|||
return extOrPortSendCommand(s, extOrCmdDone, []byte{}) |
|||
} |
|||
|
|||
func extOrPortRecvCommand(s io.Reader) (cmd uint16, body []byte, err error) { |
|||
var bodyLen uint16 |
|||
data := make([]byte, 4) |
|||
|
|||
_, err = io.ReadFull(s, data) |
|||
if err != nil { |
|||
return |
|||
} |
|||
buf := bytes.NewBuffer(data) |
|||
err = binary.Read(buf, binary.BigEndian, &cmd) |
|||
if err != nil { |
|||
return |
|||
} |
|||
err = binary.Read(buf, binary.BigEndian, &bodyLen) |
|||
if err != nil { |
|||
return |
|||
} |
|||
body = make([]byte, bodyLen) |
|||
_, err = io.ReadFull(s, body) |
|||
if err != nil { |
|||
return |
|||
} |
|||
|
|||
return cmd, body, err |
|||
} |
|||
|
|||
// Send USERADDR and TRANSPORT commands followed by a DONE command. Wait for an
|
|||
// OKAY or DENY response command from the server. If addr or methodName is "",
|
|||
// the corresponding command is not sent. Returns nil if and only if OKAY is
|
|||
// received.
|
|||
func extOrPortSetup(s io.ReadWriter, addr, methodName string) error { |
|||
var err error |
|||
|
|||
if addr != "" { |
|||
err = extOrPortSendUserAddr(s, addr) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
} |
|||
if methodName != "" { |
|||
err = extOrPortSendTransport(s, methodName) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
} |
|||
err = extOrPortSendDone(s) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
cmd, _, err := extOrPortRecvCommand(s) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
if cmd == extOrCmdDeny { |
|||
return fmt.Errorf("server returned DENY after our USERADDR and DONE") |
|||
} else if cmd != extOrCmdOkay { |
|||
return fmt.Errorf("server returned unknown command 0x%04x after our USERADDR and DONE", cmd) |
|||
} |
|||
|
|||
return nil |
|||
} |
|||
|
|||
// Dial info.ExtendedOrAddr if defined, or else info.OrAddr, and return an open
|
|||
// *net.TCPConn. If connecting to the extended OR port, extended OR port
|
|||
// authentication à la 217-ext-orport-auth.txt is done before returning; an
|
|||
// error is returned if authentication fails.
|
|||
//
|
|||
// The addr and methodName arguments are put in USERADDR and TRANSPORT ExtOrPort
|
|||
// commands, respectively. If either is "", the corresponding command is not
|
|||
// sent.
|
|||
func DialOr(info *ServerInfo, addr, methodName string) (*net.TCPConn, error) { |
|||
if info.ExtendedOrAddr == nil || info.AuthCookiePath == "" { |
|||
return net.DialTCP("tcp", nil, info.OrAddr) |
|||
} |
|||
|
|||
s, err := net.DialTCP("tcp", nil, info.ExtendedOrAddr) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
s.SetDeadline(time.Now().Add(5 * time.Second)) |
|||
err = extOrPortAuthenticate(s, info) |
|||
if err != nil { |
|||
s.Close() |
|||
return nil, err |
|||
} |
|||
err = extOrPortSetup(s, addr, methodName) |
|||
if err != nil { |
|||
s.Close() |
|||
return nil, err |
|||
} |
|||
s.SetDeadline(time.Time{}) |
|||
|
|||
return s, nil |
|||
} |
|||
@ -0,0 +1,507 @@ |
|||
package pt |
|||
|
|||
import ( |
|||
"bufio" |
|||
"fmt" |
|||
"io" |
|||
"net" |
|||
"time" |
|||
) |
|||
|
|||
const ( |
|||
socksVersion = 0x05 |
|||
|
|||
socksAuthNoneRequired = 0x00 |
|||
socksAuthUsernamePassword = 0x02 |
|||
socksAuthNoAcceptableMethods = 0xff |
|||
|
|||
socksCmdConnect = 0x01 |
|||
socksRsv = 0x00 |
|||
|
|||
socksAtypeV4 = 0x01 |
|||
socksAtypeDomainName = 0x03 |
|||
socksAtypeV6 = 0x04 |
|||
|
|||
socksAuthRFC1929Ver = 0x01 |
|||
socksAuthRFC1929Success = 0x00 |
|||
socksAuthRFC1929Fail = 0x01 |
|||
|
|||
socksRepSucceeded = 0x00 |
|||
// "general SOCKS server failure"
|
|||
SocksRepGeneralFailure = 0x01 |
|||
// "connection not allowed by ruleset"
|
|||
SocksRepConnectionNotAllowed = 0x02 |
|||
// "Network unreachable"
|
|||
SocksRepNetworkUnreachable = 0x03 |
|||
// "Host unreachable"
|
|||
SocksRepHostUnreachable = 0x04 |
|||
// "Connection refused"
|
|||
SocksRepConnectionRefused = 0x05 |
|||
// "TTL expired"
|
|||
SocksRepTTLExpired = 0x06 |
|||
// "Command not supported"
|
|||
SocksRepCommandNotSupported = 0x07 |
|||
// "Address type not supported"
|
|||
SocksRepAddressNotSupported = 0x08 |
|||
) |
|||
|
|||
// Put a sanity timeout on how long we wait for a SOCKS request.
|
|||
const socksRequestTimeout = 5 * time.Second |
|||
|
|||
// SocksRequest describes a SOCKS request.
|
|||
type SocksRequest struct { |
|||
// The endpoint requested by the client as a "host:port" string.
|
|||
Target string |
|||
// The userid string sent by the client.
|
|||
Username string |
|||
// The password string sent by the client.
|
|||
Password string |
|||
// The parsed contents of Username as a key–value mapping.
|
|||
Args Args |
|||
} |
|||
|
|||
// SocksConn encapsulates a net.Conn and information associated with a SOCKS request.
|
|||
type SocksConn struct { |
|||
net.Conn |
|||
Req SocksRequest |
|||
} |
|||
|
|||
// Send a message to the proxy client that access to the given address is
|
|||
// granted. Addr is ignored, and "0.0.0.0:0" is always sent back for
|
|||
// BND.ADDR/BND.PORT in the SOCKS response.
|
|||
func (conn *SocksConn) Grant(addr *net.TCPAddr) error { |
|||
return sendSocks5ResponseGranted(conn) |
|||
} |
|||
|
|||
// Send a message to the proxy client that access was rejected or failed. This
|
|||
// sends back a "General Failure" error code. RejectReason should be used if
|
|||
// more specific error reporting is desired.
|
|||
func (conn *SocksConn) Reject() error { |
|||
return conn.RejectReason(SocksRepGeneralFailure) |
|||
} |
|||
|
|||
// Send a message to the proxy client that access was rejected, with the
|
|||
// specific error code indicating the reason behind the rejection.
|
|||
func (conn *SocksConn) RejectReason(reason byte) error { |
|||
return sendSocks5ResponseRejected(conn, reason) |
|||
} |
|||
|
|||
// SocksListener wraps a net.Listener in order to read a SOCKS request on Accept.
|
|||
//
|
|||
// func handleConn(conn *pt.SocksConn) error {
|
|||
// defer conn.Close()
|
|||
// remote, err := net.Dial("tcp", conn.Req.Target)
|
|||
// if err != nil {
|
|||
// conn.Reject()
|
|||
// return err
|
|||
// }
|
|||
// defer remote.Close()
|
|||
// err = conn.Grant(remote.RemoteAddr().(*net.TCPAddr))
|
|||
// if err != nil {
|
|||
// return err
|
|||
// }
|
|||
// // do something with conn and remote
|
|||
// return nil
|
|||
// }
|
|||
// ...
|
|||
// ln, err := pt.ListenSocks("tcp", "127.0.0.1:0")
|
|||
// if err != nil {
|
|||
// panic(err.Error())
|
|||
// }
|
|||
// for {
|
|||
// conn, err := ln.AcceptSocks()
|
|||
// if err != nil {
|
|||
// log.Printf("accept error: %s", err)
|
|||
// if e, ok := err.(net.Error); ok && e.Temporary() {
|
|||
// continue
|
|||
// }
|
|||
// break
|
|||
// }
|
|||
// go handleConn(conn)
|
|||
// }
|
|||
type SocksListener struct { |
|||
net.Listener |
|||
} |
|||
|
|||
// Open a net.Listener according to network and laddr, and return it as a
|
|||
// SocksListener.
|
|||
func ListenSocks(network, laddr string) (*SocksListener, error) { |
|||
ln, err := net.Listen(network, laddr) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
return NewSocksListener(ln), nil |
|||
} |
|||
|
|||
// Create a new SocksListener wrapping the given net.Listener.
|
|||
func NewSocksListener(ln net.Listener) *SocksListener { |
|||
return &SocksListener{ln} |
|||
} |
|||
|
|||
// Accept is the same as AcceptSocks, except that it returns a generic net.Conn.
|
|||
// It is present for the sake of satisfying the net.Listener interface.
|
|||
func (ln *SocksListener) Accept() (net.Conn, error) { |
|||
return ln.AcceptSocks() |
|||
} |
|||
|
|||
// Call Accept on the wrapped net.Listener, do SOCKS negotiation, and return a
|
|||
// SocksConn. After accepting, you must call either conn.Grant or conn.Reject
|
|||
// (presumably after trying to connect to conn.Req.Target).
|
|||
//
|
|||
// Errors returned by AcceptSocks may be temporary (for example, EOF while
|
|||
// reading the request, or a badly formatted userid string), or permanent (e.g.,
|
|||
// the underlying socket is closed). You can determine whether an error is
|
|||
// temporary and take appropriate action with a type conversion to net.Error.
|
|||
// For example:
|
|||
//
|
|||
// for {
|
|||
// conn, err := ln.AcceptSocks()
|
|||
// if err != nil {
|
|||
// if e, ok := err.(net.Error); ok && e.Temporary() {
|
|||
// log.Printf("temporary accept error; trying again: %s", err)
|
|||
// continue
|
|||
// }
|
|||
// log.Printf("permanent accept error; giving up: %s", err)
|
|||
// break
|
|||
// }
|
|||
// go handleConn(conn)
|
|||
// }
|
|||
func (ln *SocksListener) AcceptSocks() (*SocksConn, error) { |
|||
retry: |
|||
c, err := ln.Listener.Accept() |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
conn := new(SocksConn) |
|||
conn.Conn = c |
|||
err = conn.SetDeadline(time.Now().Add(socksRequestTimeout)) |
|||
if err != nil { |
|||
conn.Close() |
|||
goto retry |
|||
} |
|||
conn.Req, err = socks5Handshake(conn) |
|||
if err != nil { |
|||
conn.Close() |
|||
goto retry |
|||
} |
|||
err = conn.SetDeadline(time.Time{}) |
|||
if err != nil { |
|||
conn.Close() |
|||
goto retry |
|||
} |
|||
return conn, nil |
|||
} |
|||
|
|||
// Returns "socks5", suitable to be included in a call to Cmethod.
|
|||
func (ln *SocksListener) Version() string { |
|||
return "socks5" |
|||
} |
|||
|
|||
// socks5handshake conducts the SOCKS5 handshake up to the point where the
|
|||
// client command is read and the proxy must open the outgoing connection.
|
|||
// Returns a SocksRequest.
|
|||
func socks5Handshake(s io.ReadWriter) (req SocksRequest, err error) { |
|||
rw := bufio.NewReadWriter(bufio.NewReader(s), bufio.NewWriter(s)) |
|||
|
|||
// Negotiate the authentication method.
|
|||
var method byte |
|||
if method, err = socksNegotiateAuth(rw); err != nil { |
|||
return |
|||
} |
|||
|
|||
// Authenticate the client.
|
|||
if err = socksAuthenticate(rw, method, &req); err != nil { |
|||
return |
|||
} |
|||
|
|||
// Read the command.
|
|||
err = socksReadCommand(rw, &req) |
|||
return |
|||
} |
|||
|
|||
// socksNegotiateAuth negotiates the authentication method and returns the
|
|||
// selected method as a byte. On negotiation failures an error is returned.
|
|||
func socksNegotiateAuth(rw *bufio.ReadWriter) (method byte, err error) { |
|||
// Validate the version.
|
|||
if err = socksReadByteVerify(rw, "version", socksVersion); err != nil { |
|||
return |
|||
} |
|||
|
|||
// Read the number of methods.
|
|||
var nmethods byte |
|||
if nmethods, err = socksReadByte(rw); err != nil { |
|||
return |
|||
} |
|||
|
|||
// Read the methods.
|
|||
var methods []byte |
|||
if methods, err = socksReadBytes(rw, int(nmethods)); err != nil { |
|||
return |
|||
} |
|||
|
|||
// Pick the most "suitable" method.
|
|||
method = socksAuthNoAcceptableMethods |
|||
for _, m := range methods { |
|||
switch m { |
|||
case socksAuthNoneRequired: |
|||
// Pick Username/Password over None if the client happens to
|
|||
// send both.
|
|||
if method == socksAuthNoAcceptableMethods { |
|||
method = m |
|||
} |
|||
|
|||
case socksAuthUsernamePassword: |
|||
method = m |
|||
} |
|||
} |
|||
|
|||
// Send the negotiated method.
|
|||
var msg [2]byte |
|||
msg[0] = socksVersion |
|||
msg[1] = method |
|||
if _, err = rw.Writer.Write(msg[:]); err != nil { |
|||
return |
|||
} |
|||
|
|||
if err = socksFlushBuffers(rw); err != nil { |
|||
return |
|||
} |
|||
return |
|||
} |
|||
|
|||
// socksAuthenticate authenticates the client via the chosen authentication
|
|||
// mechanism.
|
|||
func socksAuthenticate(rw *bufio.ReadWriter, method byte, req *SocksRequest) (err error) { |
|||
switch method { |
|||
case socksAuthNoneRequired: |
|||
// Straight into reading the connect.
|
|||
|
|||
case socksAuthUsernamePassword: |
|||
if err = socksAuthRFC1929(rw, req); err != nil { |
|||
return |
|||
} |
|||
|
|||
case socksAuthNoAcceptableMethods: |
|||
err = fmt.Errorf("SOCKS method select had no compatible methods") |
|||
return |
|||
|
|||
default: |
|||
err = fmt.Errorf("SOCKS method select picked a unsupported method 0x%02x", method) |
|||
return |
|||
} |
|||
|
|||
if err = socksFlushBuffers(rw); err != nil { |
|||
return |
|||
} |
|||
return |
|||
} |
|||
|
|||
// socksAuthRFC1929 authenticates the client via RFC 1929 username/password
|
|||
// auth. As a design decision any valid username/password is accepted as this
|
|||
// field is primarily used as an out-of-band argument passing mechanism for
|
|||
// pluggable transports.
|
|||
func socksAuthRFC1929(rw *bufio.ReadWriter, req *SocksRequest) (err error) { |
|||
sendErrResp := func() { |
|||
// Swallow the write/flush error here, we are going to close the
|
|||
// connection and the original failure is more useful.
|
|||
resp := []byte{socksAuthRFC1929Ver, socksAuthRFC1929Fail} |
|||
rw.Write(resp[:]) |
|||
socksFlushBuffers(rw) |
|||
} |
|||
|
|||
// Validate the fixed parts of the command message.
|
|||
if err = socksReadByteVerify(rw, "auth version", socksAuthRFC1929Ver); err != nil { |
|||
sendErrResp() |
|||
return |
|||
} |
|||
|
|||
// Read the username.
|
|||
var ulen byte |
|||
if ulen, err = socksReadByte(rw); err != nil { |
|||
return |
|||
} |
|||
if ulen < 1 { |
|||
sendErrResp() |
|||
err = fmt.Errorf("RFC1929 username with 0 length") |
|||
return |
|||
} |
|||
var uname []byte |
|||
if uname, err = socksReadBytes(rw, int(ulen)); err != nil { |
|||
return |
|||
} |
|||
req.Username = string(uname) |
|||
|
|||
// Read the password.
|
|||
var plen byte |
|||
if plen, err = socksReadByte(rw); err != nil { |
|||
return |
|||
} |
|||
if plen < 1 { |
|||
sendErrResp() |
|||
err = fmt.Errorf("RFC1929 password with 0 length") |
|||
return |
|||
} |
|||
var passwd []byte |
|||
if passwd, err = socksReadBytes(rw, int(plen)); err != nil { |
|||
return |
|||
} |
|||
if !(plen == 1 && passwd[0] == 0x00) { |
|||
// tor will set the password to 'NUL' if there are no arguments.
|
|||
req.Password = string(passwd) |
|||
} |
|||
|
|||
// Mash the username/password together and parse it as a pluggable
|
|||
// transport argument string.
|
|||
if req.Args, err = parseClientParameters(req.Username + req.Password); err != nil { |
|||
sendErrResp() |
|||
} else { |
|||
resp := []byte{socksAuthRFC1929Ver, socksAuthRFC1929Success} |
|||
_, err = rw.Write(resp[:]) |
|||
} |
|||
return |
|||
} |
|||
|
|||
// socksReadCommand reads a SOCKS5 client command and parses out the relevant
|
|||
// fields into a SocksRequest. Only CMD_CONNECT is supported.
|
|||
func socksReadCommand(rw *bufio.ReadWriter, req *SocksRequest) (err error) { |
|||
sendErrResp := func(reason byte) { |
|||
// Swallow errors that occur when writing/flushing the response,
|
|||
// connection will be closed anyway.
|
|||
sendSocks5ResponseRejected(rw, reason) |
|||
socksFlushBuffers(rw) |
|||
} |
|||
|
|||
// Validate the fixed parts of the command message.
|
|||
if err = socksReadByteVerify(rw, "version", socksVersion); err != nil { |
|||
sendErrResp(SocksRepGeneralFailure) |
|||
return |
|||
} |
|||
if err = socksReadByteVerify(rw, "command", socksCmdConnect); err != nil { |
|||
sendErrResp(SocksRepCommandNotSupported) |
|||
return |
|||
} |
|||
if err = socksReadByteVerify(rw, "reserved", socksRsv); err != nil { |
|||
sendErrResp(SocksRepGeneralFailure) |
|||
return |
|||
} |
|||
|
|||
// Read the destination address/port.
|
|||
// XXX: This should probably eventually send socks 5 error messages instead
|
|||
// of rudely closing connections on invalid addresses.
|
|||
var atype byte |
|||
if atype, err = socksReadByte(rw); err != nil { |
|||
return |
|||
} |
|||
var host string |
|||
switch atype { |
|||
case socksAtypeV4: |
|||
var addr []byte |
|||
if addr, err = socksReadBytes(rw, net.IPv4len); err != nil { |
|||
return |
|||
} |
|||
host = net.IPv4(addr[0], addr[1], addr[2], addr[3]).String() |
|||
|
|||
case socksAtypeDomainName: |
|||
var alen byte |
|||
if alen, err = socksReadByte(rw); err != nil { |
|||
return |
|||
} |
|||
if alen == 0 { |
|||
err = fmt.Errorf("SOCKS request had domain name with 0 length") |
|||
return |
|||
} |
|||
var addr []byte |
|||
if addr, err = socksReadBytes(rw, int(alen)); err != nil { |
|||
return |
|||
} |
|||
host = string(addr) |
|||
|
|||
case socksAtypeV6: |
|||
var rawAddr []byte |
|||
if rawAddr, err = socksReadBytes(rw, net.IPv6len); err != nil { |
|||
return |
|||
} |
|||
addr := make(net.IP, net.IPv6len) |
|||
copy(addr[:], rawAddr[:]) |
|||
host = fmt.Sprintf("[%s]", addr.String()) |
|||
|
|||
default: |
|||
sendErrResp(SocksRepAddressNotSupported) |
|||
err = fmt.Errorf("SOCKS request had unsupported address type 0x%02x", atype) |
|||
return |
|||
} |
|||
var rawPort []byte |
|||
if rawPort, err = socksReadBytes(rw, 2); err != nil { |
|||
return |
|||
} |
|||
port := int(rawPort[0])<<8 | int(rawPort[1])<<0 |
|||
|
|||
if err = socksFlushBuffers(rw); err != nil { |
|||
return |
|||
} |
|||
|
|||
req.Target = fmt.Sprintf("%s:%d", host, port) |
|||
return |
|||
} |
|||
|
|||
// Send a SOCKS5 response with the given code. BND.ADDR/BND.PORT is always the
|
|||
// IPv4 address/port "0.0.0.0:0".
|
|||
func sendSocks5Response(w io.Writer, code byte) error { |
|||
resp := make([]byte, 4+4+2) |
|||
resp[0] = socksVersion |
|||
resp[1] = code |
|||
resp[2] = socksRsv |
|||
resp[3] = socksAtypeV4 |
|||
|
|||
// BND.ADDR/BND.PORT should be the address and port that the outgoing
|
|||
// connection is bound to on the proxy, but Tor does not use this
|
|||
// information, so all zeroes are sent.
|
|||
|
|||
_, err := w.Write(resp[:]) |
|||
return err |
|||
} |
|||
|
|||
// Send a SOCKS5 response code 0x00.
|
|||
func sendSocks5ResponseGranted(w io.Writer) error { |
|||
return sendSocks5Response(w, socksRepSucceeded) |
|||
} |
|||
|
|||
// Send a SOCKS5 response with the provided failure reason.
|
|||
func sendSocks5ResponseRejected(w io.Writer, reason byte) error { |
|||
return sendSocks5Response(w, reason) |
|||
} |
|||
|
|||
func socksFlushBuffers(rw *bufio.ReadWriter) error { |
|||
if err := rw.Writer.Flush(); err != nil { |
|||
return err |
|||
} |
|||
if rw.Reader.Buffered() > 0 { |
|||
return fmt.Errorf("%d bytes left after SOCKS message", rw.Reader.Buffered()) |
|||
} |
|||
return nil |
|||
} |
|||
|
|||
func socksReadByte(rw *bufio.ReadWriter) (byte, error) { |
|||
return rw.Reader.ReadByte() |
|||
} |
|||
|
|||
func socksReadBytes(rw *bufio.ReadWriter, n int) ([]byte, error) { |
|||
ret := make([]byte, n) |
|||
if _, err := io.ReadFull(rw.Reader, ret); err != nil { |
|||
return nil, err |
|||
} |
|||
return ret, nil |
|||
} |
|||
|
|||
func socksReadByteVerify(rw *bufio.ReadWriter, descr string, expected byte) error { |
|||
val, err := socksReadByte(rw) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
if val != expected { |
|||
return fmt.Errorf("SOCKS message field %s was 0x%02x, not 0x%02x", descr, val, expected) |
|||
} |
|||
return nil |
|||
} |
|||
|
|||
var _ net.Listener = (*SocksListener)(nil) |
|||
@ -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. |
|||
|
|||
@ -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 |
|||
@ -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 |
|||
} |
|||
@ -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 |
|||
} |
|||
@ -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() |
|||
} |
|||
@ -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() |
|||
} |
|||
@ -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) |
|||
} |
|||
@ -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 |
|||
} |
|||
@ -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 |
|||
} |
|||
@ -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) |
|||
@ -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 |
|||
} |
|||
@ -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 |
|||
} |
|||
@ -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. |
|||
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -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) |
|||
} |
|||
@ -0,0 +1,69 @@ |
|||
SipHash (Go) |
|||
============ |
|||
|
|||
[](https://travis-ci.org/dchest/siphash) |
|||
|
|||
Go implementation of SipHash-2-4, a fast short-input PRF created by |
|||
Jean-Philippe Aumasson and Daniel J. Bernstein (http://131002.net/siphash/). |
|||
|
|||
|
|||
## Installation |
|||
|
|||
$ go get github.com/dchest/siphash |
|||
|
|||
## Usage |
|||
|
|||
import "github.com/dchest/siphash" |
|||
|
|||
There are two ways to use this package. |
|||
The slower one is to use the standard hash.Hash64 interface: |
|||
|
|||
h := siphash.New(key) |
|||
h.Write([]byte("Hello")) |
|||
sum := h.Sum(nil) // returns 8-byte []byte |
|||
|
|||
or |
|||
|
|||
sum64 := h.Sum64() // returns uint64 |
|||
|
|||
The faster one is to use Hash() function, which takes two uint64 parts of |
|||
16-byte key and a byte slice, and returns uint64 hash: |
|||
|
|||
sum64 := siphash.Hash(key0, key1, []byte("Hello")) |
|||
|
|||
The keys and output are little-endian. |
|||
|
|||
|
|||
## Functions |
|||
|
|||
### func Hash(k0, k1 uint64, p []byte) uint64 |
|||
|
|||
Hash returns the 64-bit SipHash-2-4 of the given byte slice with two |
|||
64-bit parts of 128-bit key: k0 and k1. |
|||
|
|||
### func Hash128(k0, k1 uint64, p []byte) (uint64, uint64) |
|||
|
|||
Hash128 returns the 128-bit SipHash-2-4 of the given byte slice with two |
|||
64-bit parts of 128-bit key: k0 and k1. |
|||
|
|||
Note that 128-bit SipHash is considered experimental by SipHash authors at this time. |
|||
|
|||
### func New(key []byte) hash.Hash64 |
|||
|
|||
New returns a new hash.Hash64 computing SipHash-2-4 with 16-byte key. |
|||
|
|||
### func New128(key []byte) hash.Hash |
|||
|
|||
New128 returns a new hash.Hash computing SipHash-2-4 with 16-byte key and 16-byte output. |
|||
|
|||
Note that 16-byte output is considered experimental by SipHash authors at this time. |
|||
|
|||
|
|||
## Public domain dedication |
|||
|
|||
Written by Dmitry Chestnykh and Damian Gryski. |
|||
|
|||
To the extent possible under law, the authors have dedicated all copyright |
|||
and related and neighboring rights to this software to the public domain |
|||
worldwide. This software is distributed without any warranty. |
|||
http://creativecommons.org/publicdomain/zero/1.0/ |
|||
@ -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 |
|||
} |
|||
@ -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 |
|||
@ -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 |
|||
@ -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 |
|||
} |
|||
@ -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 |
|||
} |
|||
@ -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 |
|||
@ -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 |
|||
@ -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 |
|||
@ -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 |
|||
@ -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) |
|||
@ -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 |
|||
} |
|||
@ -0,0 +1,149 @@ |
|||
// Copyright 2012 The Go Authors. All rights reserved.
|
|||
// Use of this source code is governed by a BSD-style
|
|||
// license that can be found in the LICENSE file.
|
|||
|
|||
/* |
|||
Package secretbox encrypts and authenticates small messages. |
|||
|
|||
Secretbox uses XSalsa20 and Poly1305 to encrypt and authenticate messages with |
|||
secret-key cryptography. The length of messages is not hidden. |
|||
|
|||
It is the caller's responsibility to ensure the uniqueness of nonces—for |
|||
example, by using nonce 1 for the first message, nonce 2 for the second |
|||
message, etc. Nonces are long enough that randomly generated nonces have |
|||
negligible risk of collision. |
|||
|
|||
This package is interoperable with NaCl: https://nacl.cr.yp.to/secretbox.html.
|
|||
*/ |
|||
package secretbox // import "golang.org/x/crypto/nacl/secretbox"
|
|||
|
|||
import ( |
|||
"golang.org/x/crypto/poly1305" |
|||
"golang.org/x/crypto/salsa20/salsa" |
|||
) |
|||
|
|||
// Overhead is the number of bytes of overhead when boxing a message.
|
|||
const Overhead = poly1305.TagSize |
|||
|
|||
// setup produces a sub-key and Salsa20 counter given a nonce and key.
|
|||
func setup(subKey *[32]byte, counter *[16]byte, nonce *[24]byte, key *[32]byte) { |
|||
// We use XSalsa20 for encryption so first we need to generate a
|
|||
// key and nonce with HSalsa20.
|
|||
var hNonce [16]byte |
|||
copy(hNonce[:], nonce[:]) |
|||
salsa.HSalsa20(subKey, &hNonce, key, &salsa.Sigma) |
|||
|
|||
// The final 8 bytes of the original nonce form the new nonce.
|
|||
copy(counter[:], nonce[16:]) |
|||
} |
|||
|
|||
// sliceForAppend takes a slice and a requested number of bytes. It returns a
|
|||
// slice with the contents of the given slice followed by that many bytes and a
|
|||
// second slice that aliases into it and contains only the extra bytes. If the
|
|||
// original slice has sufficient capacity then no allocation is performed.
|
|||
func sliceForAppend(in []byte, n int) (head, tail []byte) { |
|||
if total := len(in) + n; cap(in) >= total { |
|||
head = in[:total] |
|||
} else { |
|||
head = make([]byte, total) |
|||
copy(head, in) |
|||
} |
|||
tail = head[len(in):] |
|||
return |
|||
} |
|||
|
|||
// Seal appends an encrypted and authenticated copy of message to out, which
|
|||
// must not overlap message. The key and nonce pair must be unique for each
|
|||
// distinct message and the output will be Overhead bytes longer than message.
|
|||
func Seal(out, message []byte, nonce *[24]byte, key *[32]byte) []byte { |
|||
var subKey [32]byte |
|||
var counter [16]byte |
|||
setup(&subKey, &counter, nonce, key) |
|||
|
|||
// The Poly1305 key is generated by encrypting 32 bytes of zeros. Since
|
|||
// Salsa20 works with 64-byte blocks, we also generate 32 bytes of
|
|||
// keystream as a side effect.
|
|||
var firstBlock [64]byte |
|||
salsa.XORKeyStream(firstBlock[:], firstBlock[:], &counter, &subKey) |
|||
|
|||
var poly1305Key [32]byte |
|||
copy(poly1305Key[:], firstBlock[:]) |
|||
|
|||
ret, out := sliceForAppend(out, len(message)+poly1305.TagSize) |
|||
|
|||
// We XOR up to 32 bytes of message with the keystream generated from
|
|||
// the first block.
|
|||
firstMessageBlock := message |
|||
if len(firstMessageBlock) > 32 { |
|||
firstMessageBlock = firstMessageBlock[:32] |
|||
} |
|||
|
|||
tagOut := out |
|||
out = out[poly1305.TagSize:] |
|||
for i, x := range firstMessageBlock { |
|||
out[i] = firstBlock[32+i] ^ x |
|||
} |
|||
message = message[len(firstMessageBlock):] |
|||
ciphertext := out |
|||
out = out[len(firstMessageBlock):] |
|||
|
|||
// Now encrypt the rest.
|
|||
counter[8] = 1 |
|||
salsa.XORKeyStream(out, message, &counter, &subKey) |
|||
|
|||
var tag [poly1305.TagSize]byte |
|||
poly1305.Sum(&tag, ciphertext, &poly1305Key) |
|||
copy(tagOut, tag[:]) |
|||
|
|||
return ret |
|||
} |
|||
|
|||
// Open authenticates and decrypts a box produced by Seal and appends the
|
|||
// message to out, which must not overlap box. The output will be Overhead
|
|||
// bytes smaller than box.
|
|||
func Open(out []byte, box []byte, nonce *[24]byte, key *[32]byte) ([]byte, bool) { |
|||
if len(box) < Overhead { |
|||
return nil, false |
|||
} |
|||
|
|||
var subKey [32]byte |
|||
var counter [16]byte |
|||
setup(&subKey, &counter, nonce, key) |
|||
|
|||
// The Poly1305 key is generated by encrypting 32 bytes of zeros. Since
|
|||
// Salsa20 works with 64-byte blocks, we also generate 32 bytes of
|
|||
// keystream as a side effect.
|
|||
var firstBlock [64]byte |
|||
salsa.XORKeyStream(firstBlock[:], firstBlock[:], &counter, &subKey) |
|||
|
|||
var poly1305Key [32]byte |
|||
copy(poly1305Key[:], firstBlock[:]) |
|||
var tag [poly1305.TagSize]byte |
|||
copy(tag[:], box) |
|||
|
|||
if !poly1305.Verify(&tag, box[poly1305.TagSize:], &poly1305Key) { |
|||
return nil, false |
|||
} |
|||
|
|||
ret, out := sliceForAppend(out, len(box)-Overhead) |
|||
|
|||
// We XOR up to 32 bytes of box with the keystream generated from
|
|||
// the first block.
|
|||
box = box[Overhead:] |
|||
firstMessageBlock := box |
|||
if len(firstMessageBlock) > 32 { |
|||
firstMessageBlock = firstMessageBlock[:32] |
|||
} |
|||
for i, x := range firstMessageBlock { |
|||
out[i] = firstBlock[32+i] ^ x |
|||
} |
|||
|
|||
box = box[len(firstMessageBlock):] |
|||
out = out[len(firstMessageBlock):] |
|||
|
|||
// Now decrypt the rest.
|
|||
counter[8] = 1 |
|||
salsa.XORKeyStream(out, box, &counter, &subKey) |
|||
|
|||
return ret, true |
|||
} |
|||
@ -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 |
|||
} |
|||
@ -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) |
|||
} |
|||
@ -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 |
|||
@ -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) |
|||
} |
|||
@ -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 |
|||
@ -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) |
|||
} |
|||
Loading…
Reference in new issue