mirror of https://github.com/ginuerzh/gost
22 changed files with 4107 additions and 153 deletions
@ -0,0 +1,122 @@ |
|||
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,14 @@ |
|||
### chacha20 - ChaCha20 |
|||
#### Yawning Angel (yawning at schwanenlied dot me) |
|||
|
|||
Yet another Go ChaCha20 implementation. Everything else I found was slow, |
|||
didn't support all the variants I need to use, or relied on cgo to go fast. |
|||
|
|||
Features: |
|||
|
|||
* 20 round, 256 bit key only. Everything else is pointless and stupid. |
|||
* IETF 96 bit nonce variant. |
|||
* XChaCha 24 byte nonce variant. |
|||
* SSE2 and AVX2 support on amd64 targets. |
|||
* Incremental encrypt/decrypt support, unlike golang.org/x/crypto/salsa20. |
|||
|
|||
@ -0,0 +1,273 @@ |
|||
// chacha20.go - A ChaCha stream cipher implementation.
|
|||
//
|
|||
// To the extent possible under law, Yawning Angel has waived all copyright
|
|||
// and related or neighboring rights to chacha20, using the Creative
|
|||
// Commons "CC0" public domain dedication. See LICENSE or
|
|||
// <http://creativecommons.org/publicdomain/zero/1.0/> for full details.
|
|||
|
|||
package chacha20 |
|||
|
|||
import ( |
|||
"crypto/cipher" |
|||
"encoding/binary" |
|||
"errors" |
|||
"math" |
|||
"runtime" |
|||
) |
|||
|
|||
const ( |
|||
// KeySize is the ChaCha20 key size in bytes.
|
|||
KeySize = 32 |
|||
|
|||
// NonceSize is the ChaCha20 nonce size in bytes.
|
|||
NonceSize = 8 |
|||
|
|||
// INonceSize is the IETF ChaCha20 nonce size in bytes.
|
|||
INonceSize = 12 |
|||
|
|||
// XNonceSize is the XChaCha20 nonce size in bytes.
|
|||
XNonceSize = 24 |
|||
|
|||
// HNonceSize is the HChaCha20 nonce size in bytes.
|
|||
HNonceSize = 16 |
|||
|
|||
// BlockSize is the ChaCha20 block size in bytes.
|
|||
BlockSize = 64 |
|||
|
|||
stateSize = 16 |
|||
chachaRounds = 20 |
|||
|
|||
// The constant "expand 32-byte k" as little endian uint32s.
|
|||
sigma0 = uint32(0x61707865) |
|||
sigma1 = uint32(0x3320646e) |
|||
sigma2 = uint32(0x79622d32) |
|||
sigma3 = uint32(0x6b206574) |
|||
) |
|||
|
|||
var ( |
|||
// ErrInvalidKey is the error returned when the key is invalid.
|
|||
ErrInvalidKey = errors.New("key length must be KeySize bytes") |
|||
|
|||
// ErrInvalidNonce is the error returned when the nonce is invalid.
|
|||
ErrInvalidNonce = errors.New("nonce length must be NonceSize/INonceSize/XNonceSize bytes") |
|||
|
|||
// ErrInvalidCounter is the error returned when the counter is invalid.
|
|||
ErrInvalidCounter = errors.New("block counter is invalid (out of range)") |
|||
|
|||
useUnsafe = false |
|||
usingVectors = false |
|||
blocksFn = blocksRef |
|||
) |
|||
|
|||
// A Cipher is an instance of ChaCha20/XChaCha20 using a particular key and
|
|||
// nonce.
|
|||
type Cipher struct { |
|||
state [stateSize]uint32 |
|||
|
|||
buf [BlockSize]byte |
|||
off int |
|||
ietf bool |
|||
} |
|||
|
|||
// Reset zeros the key data so that it will no longer appear in the process's
|
|||
// memory.
|
|||
func (c *Cipher) Reset() { |
|||
for i := range c.state { |
|||
c.state[i] = 0 |
|||
} |
|||
for i := range c.buf { |
|||
c.buf[i] = 0 |
|||
} |
|||
} |
|||
|
|||
// XORKeyStream sets dst to the result of XORing src with the key stream. Dst
|
|||
// and src may be the same slice but otherwise should not overlap.
|
|||
func (c *Cipher) XORKeyStream(dst, src []byte) { |
|||
if len(dst) < len(src) { |
|||
src = src[:len(dst)] |
|||
} |
|||
|
|||
for remaining := len(src); remaining > 0; { |
|||
// Process multiple blocks at once.
|
|||
if c.off == BlockSize { |
|||
nrBlocks := remaining / BlockSize |
|||
directBytes := nrBlocks * BlockSize |
|||
if nrBlocks > 0 { |
|||
blocksFn(&c.state, src, dst, nrBlocks, c.ietf) |
|||
remaining -= directBytes |
|||
if remaining == 0 { |
|||
return |
|||
} |
|||
dst = dst[directBytes:] |
|||
src = src[directBytes:] |
|||
} |
|||
|
|||
// If there's a partial block, generate 1 block of keystream into
|
|||
// the internal buffer.
|
|||
blocksFn(&c.state, nil, c.buf[:], 1, c.ietf) |
|||
c.off = 0 |
|||
} |
|||
|
|||
// Process partial blocks from the buffered keystream.
|
|||
toXor := BlockSize - c.off |
|||
if remaining < toXor { |
|||
toXor = remaining |
|||
} |
|||
if toXor > 0 { |
|||
for i, v := range src[:toXor] { |
|||
dst[i] = v ^ c.buf[c.off+i] |
|||
} |
|||
dst = dst[toXor:] |
|||
src = src[toXor:] |
|||
|
|||
remaining -= toXor |
|||
c.off += toXor |
|||
} |
|||
} |
|||
} |
|||
|
|||
// KeyStream sets dst to the raw keystream.
|
|||
func (c *Cipher) KeyStream(dst []byte) { |
|||
for remaining := len(dst); remaining > 0; { |
|||
// Process multiple blocks at once.
|
|||
if c.off == BlockSize { |
|||
nrBlocks := remaining / BlockSize |
|||
directBytes := nrBlocks * BlockSize |
|||
if nrBlocks > 0 { |
|||
blocksFn(&c.state, nil, dst, nrBlocks, c.ietf) |
|||
remaining -= directBytes |
|||
if remaining == 0 { |
|||
return |
|||
} |
|||
dst = dst[directBytes:] |
|||
} |
|||
|
|||
// If there's a partial block, generate 1 block of keystream into
|
|||
// the internal buffer.
|
|||
blocksFn(&c.state, nil, c.buf[:], 1, c.ietf) |
|||
c.off = 0 |
|||
} |
|||
|
|||
// Process partial blocks from the buffered keystream.
|
|||
toCopy := BlockSize - c.off |
|||
if remaining < toCopy { |
|||
toCopy = remaining |
|||
} |
|||
if toCopy > 0 { |
|||
copy(dst[:toCopy], c.buf[c.off:c.off+toCopy]) |
|||
dst = dst[toCopy:] |
|||
remaining -= toCopy |
|||
c.off += toCopy |
|||
} |
|||
} |
|||
} |
|||
|
|||
// ReKey reinitializes the ChaCha20/XChaCha20 instance with the provided key
|
|||
// and nonce.
|
|||
func (c *Cipher) ReKey(key, nonce []byte) error { |
|||
if len(key) != KeySize { |
|||
return ErrInvalidKey |
|||
} |
|||
|
|||
switch len(nonce) { |
|||
case NonceSize: |
|||
case INonceSize: |
|||
case XNonceSize: |
|||
var subkey [KeySize]byte |
|||
var subnonce [HNonceSize]byte |
|||
copy(subnonce[:], nonce[0:16]) |
|||
HChaCha(key, &subnonce, &subkey) |
|||
key = subkey[:] |
|||
nonce = nonce[16:24] |
|||
defer func() { |
|||
for i := range subkey { |
|||
subkey[i] = 0 |
|||
} |
|||
}() |
|||
default: |
|||
return ErrInvalidNonce |
|||
} |
|||
|
|||
c.Reset() |
|||
c.state[0] = sigma0 |
|||
c.state[1] = sigma1 |
|||
c.state[2] = sigma2 |
|||
c.state[3] = sigma3 |
|||
c.state[4] = binary.LittleEndian.Uint32(key[0:4]) |
|||
c.state[5] = binary.LittleEndian.Uint32(key[4:8]) |
|||
c.state[6] = binary.LittleEndian.Uint32(key[8:12]) |
|||
c.state[7] = binary.LittleEndian.Uint32(key[12:16]) |
|||
c.state[8] = binary.LittleEndian.Uint32(key[16:20]) |
|||
c.state[9] = binary.LittleEndian.Uint32(key[20:24]) |
|||
c.state[10] = binary.LittleEndian.Uint32(key[24:28]) |
|||
c.state[11] = binary.LittleEndian.Uint32(key[28:32]) |
|||
c.state[12] = 0 |
|||
if len(nonce) == INonceSize { |
|||
c.state[13] = binary.LittleEndian.Uint32(nonce[0:4]) |
|||
c.state[14] = binary.LittleEndian.Uint32(nonce[4:8]) |
|||
c.state[15] = binary.LittleEndian.Uint32(nonce[8:12]) |
|||
c.ietf = true |
|||
} else { |
|||
c.state[13] = 0 |
|||
c.state[14] = binary.LittleEndian.Uint32(nonce[0:4]) |
|||
c.state[15] = binary.LittleEndian.Uint32(nonce[4:8]) |
|||
c.ietf = false |
|||
} |
|||
c.off = BlockSize |
|||
return nil |
|||
|
|||
} |
|||
|
|||
// Seek sets the block counter to a given offset.
|
|||
func (c *Cipher) Seek(blockCounter uint64) error { |
|||
if c.ietf { |
|||
if blockCounter > math.MaxUint32 { |
|||
return ErrInvalidCounter |
|||
} |
|||
c.state[12] = uint32(blockCounter) |
|||
} else { |
|||
c.state[12] = uint32(blockCounter) |
|||
c.state[13] = uint32(blockCounter >> 32) |
|||
} |
|||
c.off = BlockSize |
|||
return nil |
|||
} |
|||
|
|||
// NewCipher returns a new ChaCha20/XChaCha20 instance.
|
|||
func NewCipher(key, nonce []byte) (*Cipher, error) { |
|||
c := new(Cipher) |
|||
if err := c.ReKey(key, nonce); err != nil { |
|||
return nil, err |
|||
} |
|||
return c, nil |
|||
} |
|||
|
|||
// HChaCha is the HChaCha20 hash function used to make XChaCha.
|
|||
func HChaCha(key []byte, nonce *[HNonceSize]byte, out *[32]byte) { |
|||
var x [stateSize]uint32 // Last 4 slots unused, sigma hardcoded.
|
|||
x[0] = binary.LittleEndian.Uint32(key[0:4]) |
|||
x[1] = binary.LittleEndian.Uint32(key[4:8]) |
|||
x[2] = binary.LittleEndian.Uint32(key[8:12]) |
|||
x[3] = binary.LittleEndian.Uint32(key[12:16]) |
|||
x[4] = binary.LittleEndian.Uint32(key[16:20]) |
|||
x[5] = binary.LittleEndian.Uint32(key[20:24]) |
|||
x[6] = binary.LittleEndian.Uint32(key[24:28]) |
|||
x[7] = binary.LittleEndian.Uint32(key[28:32]) |
|||
x[8] = binary.LittleEndian.Uint32(nonce[0:4]) |
|||
x[9] = binary.LittleEndian.Uint32(nonce[4:8]) |
|||
x[10] = binary.LittleEndian.Uint32(nonce[8:12]) |
|||
x[11] = binary.LittleEndian.Uint32(nonce[12:16]) |
|||
hChaChaRef(&x, out) |
|||
} |
|||
|
|||
func init() { |
|||
switch runtime.GOARCH { |
|||
case "386", "amd64": |
|||
// Abuse unsafe to skip calling binary.LittleEndian.PutUint32
|
|||
// in the critical path. This is a big boost on systems that are
|
|||
// little endian and not overly picky about alignment.
|
|||
useUnsafe = true |
|||
} |
|||
} |
|||
|
|||
var _ cipher.Stream = (*Cipher)(nil) |
|||
@ -0,0 +1,95 @@ |
|||
// chacha20_amd64.go - AMD64 optimized chacha20.
|
|||
//
|
|||
// To the extent possible under law, Yawning Angel has waived all copyright
|
|||
// and related or neighboring rights to chacha20, using the Creative
|
|||
// Commons "CC0" public domain dedication. See LICENSE or
|
|||
// <http://creativecommons.org/publicdomain/zero/1.0/> for full details.
|
|||
|
|||
// +build amd64,!gccgo,!appengine
|
|||
|
|||
package chacha20 |
|||
|
|||
import ( |
|||
"math" |
|||
) |
|||
|
|||
var usingAVX2 = false |
|||
|
|||
func blocksAmd64SSE2(x *uint32, inp, outp *byte, nrBlocks uint) |
|||
|
|||
func blocksAmd64AVX2(x *uint32, inp, outp *byte, nrBlocks uint) |
|||
|
|||
func cpuidAmd64(cpuidParams *uint32) |
|||
|
|||
func xgetbv0Amd64(xcrVec *uint32) |
|||
|
|||
func blocksAmd64(x *[stateSize]uint32, in []byte, out []byte, nrBlocks int, isIetf bool) { |
|||
// Probably unneeded, but stating this explicitly simplifies the assembly.
|
|||
if nrBlocks == 0 { |
|||
return |
|||
} |
|||
|
|||
if isIetf { |
|||
var totalBlocks uint64 |
|||
totalBlocks = uint64(x[8]) + uint64(nrBlocks) |
|||
if totalBlocks > math.MaxUint32 { |
|||
panic("chacha20: Exceeded keystream per nonce limit") |
|||
} |
|||
} |
|||
|
|||
if in == nil { |
|||
for i := range out { |
|||
out[i] = 0 |
|||
} |
|||
in = out |
|||
} |
|||
|
|||
// Pointless to call the AVX2 code for just a single block, since half of
|
|||
// the output gets discarded...
|
|||
if usingAVX2 && nrBlocks > 1 { |
|||
blocksAmd64AVX2(&x[0], &in[0], &out[0], uint(nrBlocks)) |
|||
} else { |
|||
blocksAmd64SSE2(&x[0], &in[0], &out[0], uint(nrBlocks)) |
|||
} |
|||
} |
|||
|
|||
func supportsAVX2() bool { |
|||
// https://software.intel.com/en-us/articles/how-to-detect-new-instruction-support-in-the-4th-generation-intel-core-processor-family
|
|||
const ( |
|||
osXsaveBit = 1 << 27 |
|||
avx2Bit = 1 << 5 |
|||
) |
|||
|
|||
// Check to see if CPUID actually supports the leaf that indicates AVX2.
|
|||
// CPUID.(EAX=0H, ECX=0H) >= 7
|
|||
regs := [4]uint32{0x00} |
|||
cpuidAmd64(®s[0]) |
|||
if regs[0] < 7 { |
|||
return false |
|||
} |
|||
|
|||
// Check to see if the OS knows how to save/restore XMM/YMM state.
|
|||
// CPUID.(EAX=01H, ECX=0H):ECX.OSXSAVE[bit 27]==1
|
|||
regs = [4]uint32{0x01} |
|||
cpuidAmd64(®s[0]) |
|||
if regs[2]&osXsaveBit == 0 { |
|||
return false |
|||
} |
|||
xcrRegs := [2]uint32{} |
|||
xgetbv0Amd64(&xcrRegs[0]) |
|||
if xcrRegs[0]&6 != 6 { |
|||
return false |
|||
} |
|||
|
|||
// Check for AVX2 support.
|
|||
// CPUID.(EAX=07H, ECX=0H):EBX.AVX2[bit 5]==1
|
|||
regs = [4]uint32{0x07} |
|||
cpuidAmd64(®s[0]) |
|||
return regs[1]&avx2Bit != 0 |
|||
} |
|||
|
|||
func init() { |
|||
blocksFn = blocksAmd64 |
|||
usingVectors = true |
|||
usingAVX2 = supportsAVX2() |
|||
} |
|||
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -0,0 +1,392 @@ |
|||
// chacha20_ref.go - Reference ChaCha20.
|
|||
//
|
|||
// To the extent possible under law, Yawning Angel has waived all copyright
|
|||
// and related or neighboring rights to chacha20, using the Creative
|
|||
// Commons "CC0" public domain dedication. See LICENSE or
|
|||
// <http://creativecommons.org/publicdomain/zero/1.0/> for full details.
|
|||
|
|||
package chacha20 |
|||
|
|||
import ( |
|||
"encoding/binary" |
|||
"math" |
|||
"unsafe" |
|||
) |
|||
|
|||
func blocksRef(x *[stateSize]uint32, in []byte, out []byte, nrBlocks int, isIetf bool) { |
|||
if isIetf { |
|||
var totalBlocks uint64 |
|||
totalBlocks = uint64(x[8]) + uint64(nrBlocks) |
|||
if totalBlocks > math.MaxUint32 { |
|||
panic("chacha20: Exceeded keystream per nonce limit") |
|||
} |
|||
} |
|||
|
|||
// This routine ignores x[0]...x[4] in favor the const values since it's
|
|||
// ever so slightly faster.
|
|||
|
|||
for n := 0; n < nrBlocks; n++ { |
|||
x0, x1, x2, x3 := sigma0, sigma1, sigma2, sigma3 |
|||
x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15 := x[4], x[5], x[6], x[7], x[8], x[9], x[10], x[11], x[12], x[13], x[14], x[15] |
|||
|
|||
for i := chachaRounds; i > 0; i -= 2 { |
|||
// quarterround(x, 0, 4, 8, 12)
|
|||
x0 += x4 |
|||
x12 ^= x0 |
|||
x12 = (x12 << 16) | (x12 >> 16) |
|||
x8 += x12 |
|||
x4 ^= x8 |
|||
x4 = (x4 << 12) | (x4 >> 20) |
|||
x0 += x4 |
|||
x12 ^= x0 |
|||
x12 = (x12 << 8) | (x12 >> 24) |
|||
x8 += x12 |
|||
x4 ^= x8 |
|||
x4 = (x4 << 7) | (x4 >> 25) |
|||
|
|||
// quarterround(x, 1, 5, 9, 13)
|
|||
x1 += x5 |
|||
x13 ^= x1 |
|||
x13 = (x13 << 16) | (x13 >> 16) |
|||
x9 += x13 |
|||
x5 ^= x9 |
|||
x5 = (x5 << 12) | (x5 >> 20) |
|||
x1 += x5 |
|||
x13 ^= x1 |
|||
x13 = (x13 << 8) | (x13 >> 24) |
|||
x9 += x13 |
|||
x5 ^= x9 |
|||
x5 = (x5 << 7) | (x5 >> 25) |
|||
|
|||
// quarterround(x, 2, 6, 10, 14)
|
|||
x2 += x6 |
|||
x14 ^= x2 |
|||
x14 = (x14 << 16) | (x14 >> 16) |
|||
x10 += x14 |
|||
x6 ^= x10 |
|||
x6 = (x6 << 12) | (x6 >> 20) |
|||
x2 += x6 |
|||
x14 ^= x2 |
|||
x14 = (x14 << 8) | (x14 >> 24) |
|||
x10 += x14 |
|||
x6 ^= x10 |
|||
x6 = (x6 << 7) | (x6 >> 25) |
|||
|
|||
// quarterround(x, 3, 7, 11, 15)
|
|||
x3 += x7 |
|||
x15 ^= x3 |
|||
x15 = (x15 << 16) | (x15 >> 16) |
|||
x11 += x15 |
|||
x7 ^= x11 |
|||
x7 = (x7 << 12) | (x7 >> 20) |
|||
x3 += x7 |
|||
x15 ^= x3 |
|||
x15 = (x15 << 8) | (x15 >> 24) |
|||
x11 += x15 |
|||
x7 ^= x11 |
|||
x7 = (x7 << 7) | (x7 >> 25) |
|||
|
|||
// quarterround(x, 0, 5, 10, 15)
|
|||
x0 += x5 |
|||
x15 ^= x0 |
|||
x15 = (x15 << 16) | (x15 >> 16) |
|||
x10 += x15 |
|||
x5 ^= x10 |
|||
x5 = (x5 << 12) | (x5 >> 20) |
|||
x0 += x5 |
|||
x15 ^= x0 |
|||
x15 = (x15 << 8) | (x15 >> 24) |
|||
x10 += x15 |
|||
x5 ^= x10 |
|||
x5 = (x5 << 7) | (x5 >> 25) |
|||
|
|||
// quarterround(x, 1, 6, 11, 12)
|
|||
x1 += x6 |
|||
x12 ^= x1 |
|||
x12 = (x12 << 16) | (x12 >> 16) |
|||
x11 += x12 |
|||
x6 ^= x11 |
|||
x6 = (x6 << 12) | (x6 >> 20) |
|||
x1 += x6 |
|||
x12 ^= x1 |
|||
x12 = (x12 << 8) | (x12 >> 24) |
|||
x11 += x12 |
|||
x6 ^= x11 |
|||
x6 = (x6 << 7) | (x6 >> 25) |
|||
|
|||
// quarterround(x, 2, 7, 8, 13)
|
|||
x2 += x7 |
|||
x13 ^= x2 |
|||
x13 = (x13 << 16) | (x13 >> 16) |
|||
x8 += x13 |
|||
x7 ^= x8 |
|||
x7 = (x7 << 12) | (x7 >> 20) |
|||
x2 += x7 |
|||
x13 ^= x2 |
|||
x13 = (x13 << 8) | (x13 >> 24) |
|||
x8 += x13 |
|||
x7 ^= x8 |
|||
x7 = (x7 << 7) | (x7 >> 25) |
|||
|
|||
// quarterround(x, 3, 4, 9, 14)
|
|||
x3 += x4 |
|||
x14 ^= x3 |
|||
x14 = (x14 << 16) | (x14 >> 16) |
|||
x9 += x14 |
|||
x4 ^= x9 |
|||
x4 = (x4 << 12) | (x4 >> 20) |
|||
x3 += x4 |
|||
x14 ^= x3 |
|||
x14 = (x14 << 8) | (x14 >> 24) |
|||
x9 += x14 |
|||
x4 ^= x9 |
|||
x4 = (x4 << 7) | (x4 >> 25) |
|||
} |
|||
|
|||
// On amd64 at least, this is a rather big boost.
|
|||
if useUnsafe { |
|||
if in != nil { |
|||
inArr := (*[16]uint32)(unsafe.Pointer(&in[n*BlockSize])) |
|||
outArr := (*[16]uint32)(unsafe.Pointer(&out[n*BlockSize])) |
|||
outArr[0] = inArr[0] ^ (x0 + sigma0) |
|||
outArr[1] = inArr[1] ^ (x1 + sigma1) |
|||
outArr[2] = inArr[2] ^ (x2 + sigma2) |
|||
outArr[3] = inArr[3] ^ (x3 + sigma3) |
|||
outArr[4] = inArr[4] ^ (x4 + x[4]) |
|||
outArr[5] = inArr[5] ^ (x5 + x[5]) |
|||
outArr[6] = inArr[6] ^ (x6 + x[6]) |
|||
outArr[7] = inArr[7] ^ (x7 + x[7]) |
|||
outArr[8] = inArr[8] ^ (x8 + x[8]) |
|||
outArr[9] = inArr[9] ^ (x9 + x[9]) |
|||
outArr[10] = inArr[10] ^ (x10 + x[10]) |
|||
outArr[11] = inArr[11] ^ (x11 + x[11]) |
|||
outArr[12] = inArr[12] ^ (x12 + x[12]) |
|||
outArr[13] = inArr[13] ^ (x13 + x[13]) |
|||
outArr[14] = inArr[14] ^ (x14 + x[14]) |
|||
outArr[15] = inArr[15] ^ (x15 + x[15]) |
|||
} else { |
|||
outArr := (*[16]uint32)(unsafe.Pointer(&out[n*BlockSize])) |
|||
outArr[0] = x0 + sigma0 |
|||
outArr[1] = x1 + sigma1 |
|||
outArr[2] = x2 + sigma2 |
|||
outArr[3] = x3 + sigma3 |
|||
outArr[4] = x4 + x[4] |
|||
outArr[5] = x5 + x[5] |
|||
outArr[6] = x6 + x[6] |
|||
outArr[7] = x7 + x[7] |
|||
outArr[8] = x8 + x[8] |
|||
outArr[9] = x9 + x[9] |
|||
outArr[10] = x10 + x[10] |
|||
outArr[11] = x11 + x[11] |
|||
outArr[12] = x12 + x[12] |
|||
outArr[13] = x13 + x[13] |
|||
outArr[14] = x14 + x[14] |
|||
outArr[15] = x15 + x[15] |
|||
} |
|||
} else { |
|||
// Slow path, either the architecture cares about alignment, or is not little endian.
|
|||
x0 += sigma0 |
|||
x1 += sigma1 |
|||
x2 += sigma2 |
|||
x3 += sigma3 |
|||
x4 += x[4] |
|||
x5 += x[5] |
|||
x6 += x[6] |
|||
x7 += x[7] |
|||
x8 += x[8] |
|||
x9 += x[9] |
|||
x10 += x[10] |
|||
x11 += x[11] |
|||
x12 += x[12] |
|||
x13 += x[13] |
|||
x14 += x[14] |
|||
x15 += x[15] |
|||
if in != nil { |
|||
binary.LittleEndian.PutUint32(out[0:4], binary.LittleEndian.Uint32(in[0:4])^x0) |
|||
binary.LittleEndian.PutUint32(out[4:8], binary.LittleEndian.Uint32(in[4:8])^x1) |
|||
binary.LittleEndian.PutUint32(out[8:12], binary.LittleEndian.Uint32(in[8:12])^x2) |
|||
binary.LittleEndian.PutUint32(out[12:16], binary.LittleEndian.Uint32(in[12:16])^x3) |
|||
binary.LittleEndian.PutUint32(out[16:20], binary.LittleEndian.Uint32(in[16:20])^x4) |
|||
binary.LittleEndian.PutUint32(out[20:24], binary.LittleEndian.Uint32(in[20:24])^x5) |
|||
binary.LittleEndian.PutUint32(out[24:28], binary.LittleEndian.Uint32(in[24:28])^x6) |
|||
binary.LittleEndian.PutUint32(out[28:32], binary.LittleEndian.Uint32(in[28:32])^x7) |
|||
binary.LittleEndian.PutUint32(out[32:36], binary.LittleEndian.Uint32(in[32:36])^x8) |
|||
binary.LittleEndian.PutUint32(out[36:40], binary.LittleEndian.Uint32(in[36:40])^x9) |
|||
binary.LittleEndian.PutUint32(out[40:44], binary.LittleEndian.Uint32(in[40:44])^x10) |
|||
binary.LittleEndian.PutUint32(out[44:48], binary.LittleEndian.Uint32(in[44:48])^x11) |
|||
binary.LittleEndian.PutUint32(out[48:52], binary.LittleEndian.Uint32(in[48:52])^x12) |
|||
binary.LittleEndian.PutUint32(out[52:56], binary.LittleEndian.Uint32(in[52:56])^x13) |
|||
binary.LittleEndian.PutUint32(out[56:60], binary.LittleEndian.Uint32(in[56:60])^x14) |
|||
binary.LittleEndian.PutUint32(out[60:64], binary.LittleEndian.Uint32(in[60:64])^x15) |
|||
in = in[BlockSize:] |
|||
} else { |
|||
binary.LittleEndian.PutUint32(out[0:4], x0) |
|||
binary.LittleEndian.PutUint32(out[4:8], x1) |
|||
binary.LittleEndian.PutUint32(out[8:12], x2) |
|||
binary.LittleEndian.PutUint32(out[12:16], x3) |
|||
binary.LittleEndian.PutUint32(out[16:20], x4) |
|||
binary.LittleEndian.PutUint32(out[20:24], x5) |
|||
binary.LittleEndian.PutUint32(out[24:28], x6) |
|||
binary.LittleEndian.PutUint32(out[28:32], x7) |
|||
binary.LittleEndian.PutUint32(out[32:36], x8) |
|||
binary.LittleEndian.PutUint32(out[36:40], x9) |
|||
binary.LittleEndian.PutUint32(out[40:44], x10) |
|||
binary.LittleEndian.PutUint32(out[44:48], x11) |
|||
binary.LittleEndian.PutUint32(out[48:52], x12) |
|||
binary.LittleEndian.PutUint32(out[52:56], x13) |
|||
binary.LittleEndian.PutUint32(out[56:60], x14) |
|||
binary.LittleEndian.PutUint32(out[60:64], x15) |
|||
} |
|||
out = out[BlockSize:] |
|||
} |
|||
|
|||
// Stoping at 2^70 bytes per nonce is the user's responsibility.
|
|||
ctr := uint64(x[13])<<32 | uint64(x[12]) |
|||
ctr++ |
|||
x[12] = uint32(ctr) |
|||
x[13] = uint32(ctr >> 32) |
|||
} |
|||
} |
|||
|
|||
func hChaChaRef(x *[stateSize]uint32, out *[32]byte) { |
|||
x0, x1, x2, x3 := sigma0, sigma1, sigma2, sigma3 |
|||
x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15 := x[0], x[1], x[2], x[3], x[4], x[5], x[6], x[7], x[8], x[9], x[10], x[11] |
|||
|
|||
for i := chachaRounds; i > 0; i -= 2 { |
|||
// quarterround(x, 0, 4, 8, 12)
|
|||
x0 += x4 |
|||
x12 ^= x0 |
|||
x12 = (x12 << 16) | (x12 >> 16) |
|||
x8 += x12 |
|||
x4 ^= x8 |
|||
x4 = (x4 << 12) | (x4 >> 20) |
|||
x0 += x4 |
|||
x12 ^= x0 |
|||
x12 = (x12 << 8) | (x12 >> 24) |
|||
x8 += x12 |
|||
x4 ^= x8 |
|||
x4 = (x4 << 7) | (x4 >> 25) |
|||
|
|||
// quarterround(x, 1, 5, 9, 13)
|
|||
x1 += x5 |
|||
x13 ^= x1 |
|||
x13 = (x13 << 16) | (x13 >> 16) |
|||
x9 += x13 |
|||
x5 ^= x9 |
|||
x5 = (x5 << 12) | (x5 >> 20) |
|||
x1 += x5 |
|||
x13 ^= x1 |
|||
x13 = (x13 << 8) | (x13 >> 24) |
|||
x9 += x13 |
|||
x5 ^= x9 |
|||
x5 = (x5 << 7) | (x5 >> 25) |
|||
|
|||
// quarterround(x, 2, 6, 10, 14)
|
|||
x2 += x6 |
|||
x14 ^= x2 |
|||
x14 = (x14 << 16) | (x14 >> 16) |
|||
x10 += x14 |
|||
x6 ^= x10 |
|||
x6 = (x6 << 12) | (x6 >> 20) |
|||
x2 += x6 |
|||
x14 ^= x2 |
|||
x14 = (x14 << 8) | (x14 >> 24) |
|||
x10 += x14 |
|||
x6 ^= x10 |
|||
x6 = (x6 << 7) | (x6 >> 25) |
|||
|
|||
// quarterround(x, 3, 7, 11, 15)
|
|||
x3 += x7 |
|||
x15 ^= x3 |
|||
x15 = (x15 << 16) | (x15 >> 16) |
|||
x11 += x15 |
|||
x7 ^= x11 |
|||
x7 = (x7 << 12) | (x7 >> 20) |
|||
x3 += x7 |
|||
x15 ^= x3 |
|||
x15 = (x15 << 8) | (x15 >> 24) |
|||
x11 += x15 |
|||
x7 ^= x11 |
|||
x7 = (x7 << 7) | (x7 >> 25) |
|||
|
|||
// quarterround(x, 0, 5, 10, 15)
|
|||
x0 += x5 |
|||
x15 ^= x0 |
|||
x15 = (x15 << 16) | (x15 >> 16) |
|||
x10 += x15 |
|||
x5 ^= x10 |
|||
x5 = (x5 << 12) | (x5 >> 20) |
|||
x0 += x5 |
|||
x15 ^= x0 |
|||
x15 = (x15 << 8) | (x15 >> 24) |
|||
x10 += x15 |
|||
x5 ^= x10 |
|||
x5 = (x5 << 7) | (x5 >> 25) |
|||
|
|||
// quarterround(x, 1, 6, 11, 12)
|
|||
x1 += x6 |
|||
x12 ^= x1 |
|||
x12 = (x12 << 16) | (x12 >> 16) |
|||
x11 += x12 |
|||
x6 ^= x11 |
|||
x6 = (x6 << 12) | (x6 >> 20) |
|||
x1 += x6 |
|||
x12 ^= x1 |
|||
x12 = (x12 << 8) | (x12 >> 24) |
|||
x11 += x12 |
|||
x6 ^= x11 |
|||
x6 = (x6 << 7) | (x6 >> 25) |
|||
|
|||
// quarterround(x, 2, 7, 8, 13)
|
|||
x2 += x7 |
|||
x13 ^= x2 |
|||
x13 = (x13 << 16) | (x13 >> 16) |
|||
x8 += x13 |
|||
x7 ^= x8 |
|||
x7 = (x7 << 12) | (x7 >> 20) |
|||
x2 += x7 |
|||
x13 ^= x2 |
|||
x13 = (x13 << 8) | (x13 >> 24) |
|||
x8 += x13 |
|||
x7 ^= x8 |
|||
x7 = (x7 << 7) | (x7 >> 25) |
|||
|
|||
// quarterround(x, 3, 4, 9, 14)
|
|||
x3 += x4 |
|||
x14 ^= x3 |
|||
x14 = (x14 << 16) | (x14 >> 16) |
|||
x9 += x14 |
|||
x4 ^= x9 |
|||
x4 = (x4 << 12) | (x4 >> 20) |
|||
x3 += x4 |
|||
x14 ^= x3 |
|||
x14 = (x14 << 8) | (x14 >> 24) |
|||
x9 += x14 |
|||
x4 ^= x9 |
|||
x4 = (x4 << 7) | (x4 >> 25) |
|||
} |
|||
|
|||
// HChaCha returns x0...x3 | x12...x15, which corresponds to the
|
|||
// indexes of the ChaCha constant and the indexes of the IV.
|
|||
if useUnsafe { |
|||
outArr := (*[16]uint32)(unsafe.Pointer(&out[0])) |
|||
outArr[0] = x0 |
|||
outArr[1] = x1 |
|||
outArr[2] = x2 |
|||
outArr[3] = x3 |
|||
outArr[4] = x12 |
|||
outArr[5] = x13 |
|||
outArr[6] = x14 |
|||
outArr[7] = x15 |
|||
} else { |
|||
binary.LittleEndian.PutUint32(out[0:4], x0) |
|||
binary.LittleEndian.PutUint32(out[4:8], x1) |
|||
binary.LittleEndian.PutUint32(out[8:12], x2) |
|||
binary.LittleEndian.PutUint32(out[12:16], x3) |
|||
binary.LittleEndian.PutUint32(out[16:20], x12) |
|||
binary.LittleEndian.PutUint32(out[20:24], x13) |
|||
binary.LittleEndian.PutUint32(out[24:28], x14) |
|||
binary.LittleEndian.PutUint32(out[28:32], x15) |
|||
} |
|||
return |
|||
} |
|||
@ -0,0 +1,135 @@ |
|||
package shadowsocks |
|||
|
|||
import ( |
|||
"bytes" |
|||
"fmt" |
|||
"net" |
|||
"time" |
|||
) |
|||
|
|||
const ( |
|||
maxPacketSize = 4096 // increase it if error occurs
|
|||
) |
|||
|
|||
var ( |
|||
errPacketTooSmall = fmt.Errorf("[udp]read error: cannot decrypt, received packet is smaller than ivLen") |
|||
errPacketTooLarge = fmt.Errorf("[udp]read error: received packet is latger than maxPacketSize(%d)", maxPacketSize) |
|||
errBufferTooSmall = fmt.Errorf("[udp]read error: given buffer is too small to hold data") |
|||
errPacketOtaFailed = fmt.Errorf("[udp]read error: received packet has invalid ota") |
|||
) |
|||
|
|||
type SecurePacketConn struct { |
|||
net.PacketConn |
|||
*Cipher |
|||
ota bool |
|||
} |
|||
|
|||
func NewSecurePacketConn(c net.PacketConn, cipher *Cipher, ota bool) *SecurePacketConn { |
|||
return &SecurePacketConn{ |
|||
PacketConn: c, |
|||
Cipher: cipher, |
|||
ota: ota, |
|||
} |
|||
} |
|||
|
|||
func (c *SecurePacketConn) Close() error { |
|||
return c.PacketConn.Close() |
|||
} |
|||
|
|||
func (c *SecurePacketConn) ReadFrom(b []byte) (n int, src net.Addr, err error) { |
|||
ota := false |
|||
cipher := c.Copy() |
|||
buf := make([]byte, 4096) |
|||
n, src, err = c.PacketConn.ReadFrom(buf) |
|||
if err != nil { |
|||
return |
|||
} |
|||
|
|||
if n < c.info.ivLen { |
|||
return 0, nil, errPacketTooSmall |
|||
} |
|||
|
|||
if len(b) < n-c.info.ivLen { |
|||
err = errBufferTooSmall // just a warning
|
|||
} |
|||
|
|||
iv := make([]byte, c.info.ivLen) |
|||
copy(iv, buf[:c.info.ivLen]) |
|||
|
|||
if err = cipher.initDecrypt(iv); err != nil { |
|||
return |
|||
} |
|||
|
|||
cipher.decrypt(b[0:], buf[c.info.ivLen:n]) |
|||
n -= c.info.ivLen |
|||
if b[idType]&OneTimeAuthMask > 0 { |
|||
ota = true |
|||
} |
|||
|
|||
if c.ota && !ota { |
|||
return 0, src, errPacketOtaFailed |
|||
} |
|||
|
|||
if ota { |
|||
key := cipher.key |
|||
actualHmacSha1Buf := HmacSha1(append(iv, key...), b[:n-lenHmacSha1]) |
|||
if !bytes.Equal(b[n-lenHmacSha1:n], actualHmacSha1Buf) { |
|||
Debug.Printf("verify one time auth failed, iv=%v key=%v data=%v", iv, key, b) |
|||
return 0, src, errPacketOtaFailed |
|||
} |
|||
n -= lenHmacSha1 |
|||
} |
|||
|
|||
return |
|||
} |
|||
|
|||
func (c *SecurePacketConn) WriteTo(b []byte, dst net.Addr) (n int, err error) { |
|||
cipher := c.Copy() |
|||
iv, err := cipher.initEncrypt() |
|||
if err != nil { |
|||
return |
|||
} |
|||
packetLen := len(b) + len(iv) |
|||
|
|||
if c.ota { |
|||
b[idType] |= OneTimeAuthMask |
|||
packetLen += lenHmacSha1 |
|||
key := cipher.key |
|||
actualHmacSha1Buf := HmacSha1(append(iv, key...), b) |
|||
b = append(b, actualHmacSha1Buf...) |
|||
} |
|||
|
|||
cipherData := make([]byte, packetLen) |
|||
copy(cipherData, iv) |
|||
|
|||
cipher.encrypt(cipherData[len(iv):], b) |
|||
n, err = c.PacketConn.WriteTo(cipherData, dst) |
|||
if c.ota { |
|||
n -= lenHmacSha1 |
|||
} |
|||
return |
|||
} |
|||
|
|||
func (c *SecurePacketConn) LocalAddr() net.Addr { |
|||
return c.PacketConn.LocalAddr() |
|||
} |
|||
|
|||
func (c *SecurePacketConn) SetDeadline(t time.Time) error { |
|||
return c.PacketConn.SetDeadline(t) |
|||
} |
|||
|
|||
func (c *SecurePacketConn) SetReadDeadline(t time.Time) error { |
|||
return c.PacketConn.SetReadDeadline(t) |
|||
} |
|||
|
|||
func (c *SecurePacketConn) SetWriteDeadline(t time.Time) error { |
|||
return c.PacketConn.SetWriteDeadline(t) |
|||
} |
|||
|
|||
func (c *SecurePacketConn) IsOta() bool { |
|||
return c.ota |
|||
} |
|||
|
|||
func (c *SecurePacketConn) ForceOTA() net.PacketConn { |
|||
return NewSecurePacketConn(c.PacketConn, c.Cipher.Copy(), true) |
|||
} |
|||
@ -0,0 +1,265 @@ |
|||
package shadowsocks |
|||
|
|||
import ( |
|||
"encoding/binary" |
|||
"fmt" |
|||
"net" |
|||
"strconv" |
|||
"strings" |
|||
"sync" |
|||
"syscall" |
|||
"time" |
|||
) |
|||
|
|||
const ( |
|||
idType = 0 // address type index
|
|||
idIP0 = 1 // ip addres start index
|
|||
idDmLen = 1 // domain address length index
|
|||
idDm0 = 2 // domain address start index
|
|||
|
|||
typeIPv4 = 1 // type is ipv4 address
|
|||
typeDm = 3 // type is domain address
|
|||
typeIPv6 = 4 // type is ipv6 address
|
|||
|
|||
lenIPv4 = 1 + net.IPv4len + 2 // 1addrType + ipv4 + 2port
|
|||
lenIPv6 = 1 + net.IPv6len + 2 // 1addrType + ipv6 + 2port
|
|||
lenDmBase = 1 + 1 + 2 // 1addrType + 1addrLen + 2port, plus addrLen
|
|||
lenHmacSha1 = 10 |
|||
) |
|||
|
|||
var ( |
|||
reqList = newReqList() |
|||
natlist = newNatTable() |
|||
udpTimeout = 30 * time.Second |
|||
reqListRefreshTime = 5 * time.Minute |
|||
) |
|||
|
|||
type natTable struct { |
|||
sync.Mutex |
|||
conns map[string]net.PacketConn |
|||
} |
|||
|
|||
func newNatTable() *natTable { |
|||
return &natTable{conns: map[string]net.PacketConn{}} |
|||
} |
|||
|
|||
func (table *natTable) Delete(index string) net.PacketConn { |
|||
table.Lock() |
|||
defer table.Unlock() |
|||
c, ok := table.conns[index] |
|||
if ok { |
|||
delete(table.conns, index) |
|||
return c |
|||
} |
|||
return nil |
|||
} |
|||
|
|||
func (table *natTable) Get(index string) (c net.PacketConn, ok bool, err error) { |
|||
table.Lock() |
|||
defer table.Unlock() |
|||
c, ok = table.conns[index] |
|||
if !ok { |
|||
c, err = net.ListenPacket("udp", "") |
|||
if err != nil { |
|||
return nil, false, err |
|||
} |
|||
table.conns[index] = c |
|||
} |
|||
return |
|||
} |
|||
|
|||
type requestHeaderList struct { |
|||
sync.Mutex |
|||
List map[string]([]byte) |
|||
} |
|||
|
|||
func newReqList() *requestHeaderList { |
|||
ret := &requestHeaderList{List: map[string]([]byte){}} |
|||
go func() { |
|||
for { |
|||
time.Sleep(reqListRefreshTime) |
|||
ret.Refresh() |
|||
} |
|||
}() |
|||
return ret |
|||
} |
|||
|
|||
func (r *requestHeaderList) Refresh() { |
|||
r.Lock() |
|||
defer r.Unlock() |
|||
for k, _ := range r.List { |
|||
delete(r.List, k) |
|||
} |
|||
} |
|||
|
|||
func (r *requestHeaderList) Get(dstaddr string) (req []byte, ok bool) { |
|||
r.Lock() |
|||
defer r.Unlock() |
|||
req, ok = r.List[dstaddr] |
|||
return |
|||
} |
|||
|
|||
func (r *requestHeaderList) Put(dstaddr string, req []byte) { |
|||
r.Lock() |
|||
defer r.Unlock() |
|||
r.List[dstaddr] = req |
|||
return |
|||
} |
|||
|
|||
func parseHeaderFromAddr(addr net.Addr) ([]byte, int) { |
|||
// if the request address type is domain, it cannot be reverselookuped
|
|||
ip, port, err := net.SplitHostPort(addr.String()) |
|||
if err != nil { |
|||
return nil, 0 |
|||
} |
|||
buf := make([]byte, 20) |
|||
IP := net.ParseIP(ip) |
|||
b1 := IP.To4() |
|||
iplen := 0 |
|||
if b1 == nil { //ipv6
|
|||
b1 = IP.To16() |
|||
buf[0] = typeIPv6 |
|||
iplen = net.IPv6len |
|||
} else { //ipv4
|
|||
buf[0] = typeIPv4 |
|||
iplen = net.IPv4len |
|||
} |
|||
copy(buf[1:], b1) |
|||
port_i, _ := strconv.Atoi(port) |
|||
binary.BigEndian.PutUint16(buf[1+iplen:], uint16(port_i)) |
|||
return buf[:1+iplen+2], 1 + iplen + 2 |
|||
} |
|||
|
|||
func Pipeloop(write net.PacketConn, writeAddr net.Addr, readClose net.PacketConn) { |
|||
buf := leakyBuf.Get() |
|||
defer leakyBuf.Put(buf) |
|||
defer readClose.Close() |
|||
for { |
|||
readClose.SetDeadline(time.Now().Add(udpTimeout)) |
|||
n, raddr, err := readClose.ReadFrom(buf) |
|||
if err != nil { |
|||
if ne, ok := err.(*net.OpError); ok { |
|||
if ne.Err == syscall.EMFILE || ne.Err == syscall.ENFILE { |
|||
// log too many open file error
|
|||
// EMFILE is process reaches open file limits, ENFILE is system limit
|
|||
Debug.Println("[udp]read error:", err) |
|||
} |
|||
} |
|||
Debug.Printf("[udp]closed pipe %s<-%s\n", writeAddr, readClose.LocalAddr()) |
|||
return |
|||
} |
|||
// need improvement here
|
|||
if req, ok := reqList.Get(raddr.String()); ok { |
|||
write.WriteTo(append(req, buf[:n]...), writeAddr) |
|||
} else { |
|||
header, hlen := parseHeaderFromAddr(raddr) |
|||
write.WriteTo(append(header[:hlen], buf[:n]...), writeAddr) |
|||
} |
|||
} |
|||
} |
|||
|
|||
func handleUDPConnection(handle *SecurePacketConn, n int, src net.Addr, receive []byte) { |
|||
var dstIP net.IP |
|||
var reqLen int |
|||
var ota bool |
|||
addrType := receive[idType] |
|||
defer leakyBuf.Put(receive) |
|||
|
|||
if addrType&OneTimeAuthMask > 0 { |
|||
ota = true |
|||
} |
|||
receive[idType] &= ^OneTimeAuthMask |
|||
compatiblemode := !handle.IsOta() && ota |
|||
|
|||
switch addrType & AddrMask { |
|||
case typeIPv4: |
|||
reqLen = lenIPv4 |
|||
if len(receive) < reqLen { |
|||
Debug.Println("[udp]invalid received message.") |
|||
} |
|||
dstIP = net.IP(receive[idIP0 : idIP0+net.IPv4len]) |
|||
case typeIPv6: |
|||
reqLen = lenIPv6 |
|||
if len(receive) < reqLen { |
|||
Debug.Println("[udp]invalid received message.") |
|||
} |
|||
dstIP = net.IP(receive[idIP0 : idIP0+net.IPv6len]) |
|||
case typeDm: |
|||
reqLen = int(receive[idDmLen]) + lenDmBase |
|||
if len(receive) < reqLen { |
|||
Debug.Println("[udp]invalid received message.") |
|||
} |
|||
name := string(receive[idDm0 : idDm0+int(receive[idDmLen])]) |
|||
// avoid panic: syscall: string with NUL passed to StringToUTF16 on windows.
|
|||
if strings.ContainsRune(name, 0x00) { |
|||
fmt.Println("[udp]invalid domain name.") |
|||
return |
|||
} |
|||
dIP, err := net.ResolveIPAddr("ip", name) // carefully with const type
|
|||
if err != nil { |
|||
Debug.Printf("[udp]failed to resolve domain name: %s\n", string(receive[idDm0:idDm0+receive[idDmLen]])) |
|||
return |
|||
} |
|||
dstIP = dIP.IP |
|||
default: |
|||
Debug.Printf("[udp]addrType %d not supported", addrType) |
|||
return |
|||
} |
|||
dst := &net.UDPAddr{ |
|||
IP: dstIP, |
|||
Port: int(binary.BigEndian.Uint16(receive[reqLen-2 : reqLen])), |
|||
} |
|||
if _, ok := reqList.Get(dst.String()); !ok { |
|||
req := make([]byte, reqLen) |
|||
copy(req, receive) |
|||
reqList.Put(dst.String(), req) |
|||
} |
|||
|
|||
remote, exist, err := natlist.Get(src.String()) |
|||
if err != nil { |
|||
return |
|||
} |
|||
if !exist { |
|||
Debug.Printf("[udp]new client %s->%s via %s ota=%v\n", src, dst, remote.LocalAddr(), ota) |
|||
go func() { |
|||
if compatiblemode { |
|||
Pipeloop(handle.ForceOTA(), src, remote) |
|||
} else { |
|||
Pipeloop(handle, src, remote) |
|||
} |
|||
|
|||
natlist.Delete(src.String()) |
|||
}() |
|||
} else { |
|||
Debug.Printf("[udp]using cached client %s->%s via %s ota=%v\n", src, dst, remote.LocalAddr(), ota) |
|||
} |
|||
if remote == nil { |
|||
fmt.Println("WTF") |
|||
} |
|||
remote.SetDeadline(time.Now().Add(udpTimeout)) |
|||
_, err = remote.WriteTo(receive[reqLen:n], dst) |
|||
if err != nil { |
|||
if ne, ok := err.(*net.OpError); ok && (ne.Err == syscall.EMFILE || ne.Err == syscall.ENFILE) { |
|||
// log too many open file error
|
|||
// EMFILE is process reaches open file limits, ENFILE is system limit
|
|||
Debug.Println("[udp]write error:", err) |
|||
} else { |
|||
Debug.Println("[udp]error connecting to:", dst, err) |
|||
} |
|||
if conn := natlist.Delete(src.String()); conn != nil { |
|||
conn.Close() |
|||
} |
|||
} |
|||
// Pipeloop
|
|||
return |
|||
} |
|||
|
|||
func ReadAndHandleUDPReq(c *SecurePacketConn) error { |
|||
buf := leakyBuf.Get() |
|||
n, src, err := c.ReadFrom(buf[0:]) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
go handleUDPConnection(c, n, src, buf) |
|||
return nil |
|||
} |
|||
Loading…
Reference in new issue