mirror of https://github.com/ginuerzh/gost
47 changed files with 11720 additions and 4 deletions
@ -1,4 +1,6 @@ |
|||||
# username password |
# username password |
||||
|
|
||||
|
test\admin 123456 |
||||
|
$test 123456 |
||||
test001 123456 |
test001 123456 |
||||
test002 12345678 |
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