mirror of https://github.com/ginuerzh/gost
218 changed files with 39179 additions and 8830 deletions
@ -0,0 +1,25 @@ |
|||
# Compiled Object files, Static and Dynamic libs (Shared Objects) |
|||
*.o |
|||
*.a |
|||
*.so |
|||
|
|||
# Folders |
|||
_obj |
|||
_test |
|||
.vscode |
|||
|
|||
# Architecture specific extensions/prefixes |
|||
*.[568vq] |
|||
[568vq].out |
|||
|
|||
*.cgo1.go |
|||
*.cgo2.c |
|||
_cgo_defun.c |
|||
_cgo_gotypes.go |
|||
_cgo_export.* |
|||
|
|||
_testmain.go |
|||
|
|||
*.exe |
|||
*.test |
|||
*.prof |
|||
@ -0,0 +1,25 @@ |
|||
language: go |
|||
|
|||
go: |
|||
- "1.8.x" |
|||
- "1.9.x" |
|||
- "1.10.x" |
|||
|
|||
env: |
|||
- TRAVIS_GOARCH=amd64 |
|||
- TRAVIS_GOARCH=386 |
|||
|
|||
before_install: |
|||
- export GOARCH=$TRAVIS_GOARCH |
|||
|
|||
branches: |
|||
only: |
|||
- master |
|||
|
|||
before_script: |
|||
- go get -u github.com/klauspost/asmfmt/cmd/asmfmt |
|||
|
|||
script: |
|||
- diff -au <(gofmt -d .) <(printf "") |
|||
- diff -au <(asmfmt -d .) <(printf "") |
|||
- go test -v ./... |
|||
@ -0,0 +1,21 @@ |
|||
The MIT License (MIT) |
|||
|
|||
Copyright (c) 2016 Andreas Auernhammer |
|||
|
|||
Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
of this software and associated documentation files (the "Software"), to deal |
|||
in the Software without restriction, including without limitation the rights |
|||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
copies of the Software, and to permit persons to whom the Software is |
|||
furnished to do so, subject to the following conditions: |
|||
|
|||
The above copyright notice and this permission notice shall be included in all |
|||
copies or substantial portions of the Software. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|||
SOFTWARE. |
|||
@ -0,0 +1,82 @@ |
|||
[](https://godoc.org/github.com/aead/chacha20) |
|||
[](https://travis-ci.org/aead/chacha20) |
|||
[](https://goreportcard.com/report/aead/chacha20) |
|||
|
|||
## The ChaCha20 stream cipher |
|||
|
|||
ChaCha is a stream cipher family created by Daniel J. Bernstein. |
|||
The most common ChaCha variant is ChaCha20 (20 rounds). ChaCha20 is |
|||
standardized in [RFC 7539](https://tools.ietf.org/html/rfc7539 "RFC 7539"). |
|||
|
|||
This package provides implementations of three ChaCha versions: |
|||
- ChaCha20 with a 64 bit nonce (can en/decrypt up to 2^64 * 64 bytes for one key-nonce combination) |
|||
- ChaCha20 with a 96 bit nonce (can en/decrypt up to 2^32 * 64 bytes ~ 256 GB for one key-nonce combination) |
|||
- XChaCha20 with a 192 bit nonce (can en/decrypt up to 2^64 * 64 bytes for one key-nonce combination) |
|||
|
|||
Furthermore the chacha sub package implements ChaCha20/12 and ChaCha20/8. |
|||
These versions use 12 or 8 rounds instead of 20. |
|||
But it's recommended to use ChaCha20 (with 20 rounds) - it will be fast enough for almost all purposes. |
|||
|
|||
### Installation |
|||
Install in your GOPATH: `go get -u github.com/aead/chacha20` |
|||
|
|||
### Requirements |
|||
All go versions >= 1.8.7 are supported. |
|||
The code may also work on Go 1.7 but this is not tested. |
|||
|
|||
### Performance |
|||
|
|||
#### AMD64 |
|||
Hardware: Intel i7-6500U 2.50GHz x 2 |
|||
System: Linux Ubuntu 16.04 - kernel: 4.4.0-62-generic |
|||
Go version: 1.8.0 |
|||
``` |
|||
AVX2 |
|||
name speed cpb |
|||
ChaCha20_64-4 573MB/s ± 0% 4.16 |
|||
ChaCha20_1K-4 2.19GB/s ± 0% 1.06 |
|||
XChaCha20_64-4 261MB/s ± 0% 9.13 |
|||
XChaCha20_1K-4 1.69GB/s ± 4% 1.37 |
|||
XORKeyStream64-4 474MB/s ± 2% 5.02 |
|||
XORKeyStream1K-4 2.09GB/s ± 1% 1.11 |
|||
XChaCha20_XORKeyStream64-4 262MB/s ± 0% 9.09 |
|||
XChaCha20_XORKeyStream1K-4 1.71GB/s ± 1% 1.36 |
|||
|
|||
SSSE3 |
|||
name speed cpb |
|||
ChaCha20_64-4 583MB/s ± 0% 4.08 |
|||
ChaCha20_1K-4 1.15GB/s ± 1% 2.02 |
|||
XChaCha20_64-4 267MB/s ± 0% 8.92 |
|||
XChaCha20_1K-4 984MB/s ± 5% 2.42 |
|||
XORKeyStream64-4 492MB/s ± 1% 4.84 |
|||
XORKeyStream1K-4 1.10GB/s ± 5% 2.11 |
|||
XChaCha20_XORKeyStream64-4 266MB/s ± 0% 8.96 |
|||
XChaCha20_XORKeyStream1K-4 1.00GB/s ± 2% 2.32 |
|||
``` |
|||
#### 386 |
|||
Hardware: Intel i7-6500U 2.50GHz x 2 |
|||
System: Linux Ubuntu 16.04 - kernel: 4.4.0-62-generic |
|||
Go version: 1.8.0 |
|||
``` |
|||
SSSE3 |
|||
name speed cpb |
|||
ChaCha20_64-4 570MB/s ± 0% 4.18 |
|||
ChaCha20_1K-4 650MB/s ± 0% 3.66 |
|||
XChaCha20_64-4 223MB/s ± 0% 10.69 |
|||
XChaCha20_1K-4 584MB/s ± 1% 4.08 |
|||
XORKeyStream64-4 392MB/s ± 1% 6.08 |
|||
XORKeyStream1K-4 629MB/s ± 1% 3.79 |
|||
XChaCha20_XORKeyStream64-4 222MB/s ± 0% 10.73 |
|||
XChaCha20_XORKeyStream1K-4 585MB/s ± 0% 4.07 |
|||
|
|||
SSE2 |
|||
name speed cpb |
|||
ChaCha20_64-4 509MB/s ± 0% 4.68 |
|||
ChaCha20_1K-4 553MB/s ± 2% 4.31 |
|||
XChaCha20_64-4 201MB/s ± 0% 11.86 |
|||
XChaCha20_1K-4 498MB/s ± 4% 4.78 |
|||
XORKeyStream64-4 359MB/s ± 1% 6.64 |
|||
XORKeyStream1K-4 545MB/s ± 0% 4.37 |
|||
XChaCha20_XORKeyStream64-4 201MB/s ± 1% 11.86 |
|||
XChaCha20_XORKeyStream1K-4 507MB/s ± 0% 4.70 |
|||
``` |
|||
@ -0,0 +1,197 @@ |
|||
// Copyright (c) 2016 Andreas Auernhammer. All rights reserved.
|
|||
// Use of this source code is governed by a license that can be
|
|||
// found in the LICENSE file.
|
|||
|
|||
// Package chacha implements some low-level functions of the
|
|||
// ChaCha cipher family.
|
|||
package chacha // import "github.com/aead/chacha20/chacha"
|
|||
|
|||
import ( |
|||
"encoding/binary" |
|||
"errors" |
|||
"math" |
|||
) |
|||
|
|||
const ( |
|||
// NonceSize is the size of the ChaCha20 nonce in bytes.
|
|||
NonceSize = 8 |
|||
|
|||
// INonceSize is the size of the IETF-ChaCha20 nonce in bytes.
|
|||
INonceSize = 12 |
|||
|
|||
// XNonceSize is the size of the XChaCha20 nonce in bytes.
|
|||
XNonceSize = 24 |
|||
|
|||
// KeySize is the size of the key in bytes.
|
|||
KeySize = 32 |
|||
) |
|||
|
|||
var ( |
|||
useSSE2 bool |
|||
useSSSE3 bool |
|||
useAVX bool |
|||
useAVX2 bool |
|||
) |
|||
|
|||
var ( |
|||
errKeySize = errors.New("chacha20/chacha: bad key length") |
|||
errInvalidNonce = errors.New("chacha20/chacha: bad nonce length") |
|||
) |
|||
|
|||
func setup(state *[64]byte, nonce, key []byte) (err error) { |
|||
if len(key) != KeySize { |
|||
err = errKeySize |
|||
return |
|||
} |
|||
var Nonce [16]byte |
|||
switch len(nonce) { |
|||
case NonceSize: |
|||
copy(Nonce[8:], nonce) |
|||
initialize(state, key, &Nonce) |
|||
case INonceSize: |
|||
copy(Nonce[4:], nonce) |
|||
initialize(state, key, &Nonce) |
|||
case XNonceSize: |
|||
var tmpKey [32]byte |
|||
var hNonce [16]byte |
|||
|
|||
copy(hNonce[:], nonce[:16]) |
|||
copy(tmpKey[:], key) |
|||
HChaCha20(&tmpKey, &hNonce, &tmpKey) |
|||
copy(Nonce[8:], nonce[16:]) |
|||
initialize(state, tmpKey[:], &Nonce) |
|||
|
|||
// BUG(aead): A "good" compiler will remove this (optimizations)
|
|||
// But using the provided key instead of tmpKey,
|
|||
// will change the key (-> probably confuses users)
|
|||
for i := range tmpKey { |
|||
tmpKey[i] = 0 |
|||
} |
|||
default: |
|||
err = errInvalidNonce |
|||
} |
|||
return |
|||
} |
|||
|
|||
// XORKeyStream crypts bytes from src to dst using the given nonce and key.
|
|||
// The length of the nonce determinds the version of ChaCha20:
|
|||
// - NonceSize: ChaCha20/r with a 64 bit nonce and a 2^64 * 64 byte period.
|
|||
// - INonceSize: ChaCha20/r as defined in RFC 7539 and a 2^32 * 64 byte period.
|
|||
// - XNonceSize: XChaCha20/r with a 192 bit nonce and a 2^64 * 64 byte period.
|
|||
// The rounds argument specifies the number of rounds performed for keystream
|
|||
// generation - valid values are 8, 12 or 20. The src and dst may be the same slice
|
|||
// but otherwise should not overlap. If len(dst) < len(src) this function panics.
|
|||
// If the nonce is neither 64, 96 nor 192 bits long, this function panics.
|
|||
func XORKeyStream(dst, src, nonce, key []byte, rounds int) { |
|||
if rounds != 20 && rounds != 12 && rounds != 8 { |
|||
panic("chacha20/chacha: bad number of rounds") |
|||
} |
|||
if len(dst) < len(src) { |
|||
panic("chacha20/chacha: dst buffer is to small") |
|||
} |
|||
if len(nonce) == INonceSize && uint64(len(src)) > (1<<38) { |
|||
panic("chacha20/chacha: src is too large") |
|||
} |
|||
|
|||
var block, state [64]byte |
|||
if err := setup(&state, nonce, key); err != nil { |
|||
panic(err) |
|||
} |
|||
xorKeyStream(dst, src, &block, &state, rounds) |
|||
} |
|||
|
|||
// Cipher implements ChaCha20/r (XChaCha20/r) for a given number of rounds r.
|
|||
type Cipher struct { |
|||
state, block [64]byte |
|||
off int |
|||
rounds int // 20 for ChaCha20
|
|||
noncesize int |
|||
} |
|||
|
|||
// NewCipher returns a new *chacha.Cipher implementing the ChaCha20/r or XChaCha20/r
|
|||
// (r = 8, 12 or 20) stream cipher. The nonce must be unique for one key for all time.
|
|||
// The length of the nonce determinds the version of ChaCha20:
|
|||
// - NonceSize: ChaCha20/r with a 64 bit nonce and a 2^64 * 64 byte period.
|
|||
// - INonceSize: ChaCha20/r as defined in RFC 7539 and a 2^32 * 64 byte period.
|
|||
// - XNonceSize: XChaCha20/r with a 192 bit nonce and a 2^64 * 64 byte period.
|
|||
// If the nonce is neither 64, 96 nor 192 bits long, a non-nil error is returned.
|
|||
func NewCipher(nonce, key []byte, rounds int) (*Cipher, error) { |
|||
if rounds != 20 && rounds != 12 && rounds != 8 { |
|||
panic("chacha20/chacha: bad number of rounds") |
|||
} |
|||
|
|||
c := new(Cipher) |
|||
if err := setup(&(c.state), nonce, key); err != nil { |
|||
return nil, err |
|||
} |
|||
c.rounds = rounds |
|||
|
|||
if len(nonce) == INonceSize { |
|||
c.noncesize = INonceSize |
|||
} else { |
|||
c.noncesize = NonceSize |
|||
} |
|||
|
|||
return c, nil |
|||
} |
|||
|
|||
// XORKeyStream crypts bytes from src to dst. Src and dst may be the same slice
|
|||
// but otherwise should not overlap. If len(dst) < len(src) the function panics.
|
|||
func (c *Cipher) XORKeyStream(dst, src []byte) { |
|||
if len(dst) < len(src) { |
|||
panic("chacha20/chacha: dst buffer is to small") |
|||
} |
|||
|
|||
if c.off > 0 { |
|||
n := len(c.block[c.off:]) |
|||
if len(src) <= n { |
|||
for i, v := range src { |
|||
dst[i] = v ^ c.block[c.off] |
|||
c.off++ |
|||
} |
|||
if c.off == 64 { |
|||
c.off = 0 |
|||
} |
|||
return |
|||
} |
|||
|
|||
for i, v := range c.block[c.off:] { |
|||
dst[i] = src[i] ^ v |
|||
} |
|||
src = src[n:] |
|||
dst = dst[n:] |
|||
c.off = 0 |
|||
} |
|||
|
|||
// check for counter overflow
|
|||
blocksToXOR := len(src) / 64 |
|||
if len(src)%64 != 0 { |
|||
blocksToXOR++ |
|||
} |
|||
var overflow bool |
|||
if c.noncesize == INonceSize { |
|||
overflow = binary.LittleEndian.Uint32(c.state[48:]) > math.MaxUint32-uint32(blocksToXOR) |
|||
} else { |
|||
overflow = binary.LittleEndian.Uint64(c.state[48:]) > math.MaxUint64-uint64(blocksToXOR) |
|||
} |
|||
if overflow { |
|||
panic("chacha20/chacha: counter overflow") |
|||
} |
|||
|
|||
c.off += xorKeyStream(dst, src, &(c.block), &(c.state), c.rounds) |
|||
} |
|||
|
|||
// SetCounter skips ctr * 64 byte blocks. SetCounter(0) resets the cipher.
|
|||
// This function always skips the unused keystream of the current 64 byte block.
|
|||
func (c *Cipher) SetCounter(ctr uint64) { |
|||
if c.noncesize == INonceSize { |
|||
binary.LittleEndian.PutUint32(c.state[48:], uint32(ctr)) |
|||
} else { |
|||
binary.LittleEndian.PutUint64(c.state[48:], ctr) |
|||
} |
|||
c.off = 0 |
|||
} |
|||
|
|||
// HChaCha20 generates 32 pseudo-random bytes from a 128 bit nonce and a 256 bit secret key.
|
|||
// It can be used as a key-derivation-function (KDF).
|
|||
func HChaCha20(out *[32]byte, nonce *[16]byte, key *[32]byte) { hChaCha20(out, nonce, key) } |
|||
@ -0,0 +1,406 @@ |
|||
// Copyright (c) 2016 Andreas Auernhammer. All rights reserved. |
|||
// Use of this source code is governed by a license that can be |
|||
// found in the LICENSE file. |
|||
|
|||
// +build amd64,!gccgo,!appengine,!nacl |
|||
|
|||
#include "const.s" |
|||
#include "macro.s" |
|||
|
|||
#define TWO 0(SP) |
|||
#define C16 32(SP) |
|||
#define C8 64(SP) |
|||
#define STATE_0 96(SP) |
|||
#define STATE_1 128(SP) |
|||
#define STATE_2 160(SP) |
|||
#define STATE_3 192(SP) |
|||
#define TMP_0 224(SP) |
|||
#define TMP_1 256(SP) |
|||
|
|||
// func xorKeyStreamAVX(dst, src []byte, block, state *[64]byte, rounds int) int |
|||
TEXT ·xorKeyStreamAVX2(SB), 4, $320-80 |
|||
MOVQ dst_base+0(FP), DI |
|||
MOVQ src_base+24(FP), SI |
|||
MOVQ block+48(FP), BX |
|||
MOVQ state+56(FP), AX |
|||
MOVQ rounds+64(FP), DX |
|||
MOVQ src_len+32(FP), CX |
|||
|
|||
MOVQ SP, R8 |
|||
ADDQ $32, SP |
|||
ANDQ $-32, SP |
|||
|
|||
VMOVDQU 0(AX), Y2 |
|||
VMOVDQU 32(AX), Y3 |
|||
VPERM2I128 $0x22, Y2, Y0, Y0 |
|||
VPERM2I128 $0x33, Y2, Y1, Y1 |
|||
VPERM2I128 $0x22, Y3, Y2, Y2 |
|||
VPERM2I128 $0x33, Y3, Y3, Y3 |
|||
|
|||
TESTQ CX, CX |
|||
JZ done |
|||
|
|||
VMOVDQU ·one_AVX2<>(SB), Y4 |
|||
VPADDD Y4, Y3, Y3 |
|||
|
|||
VMOVDQA Y0, STATE_0 |
|||
VMOVDQA Y1, STATE_1 |
|||
VMOVDQA Y2, STATE_2 |
|||
VMOVDQA Y3, STATE_3 |
|||
|
|||
VMOVDQU ·rol16_AVX2<>(SB), Y4 |
|||
VMOVDQU ·rol8_AVX2<>(SB), Y5 |
|||
VMOVDQU ·two_AVX2<>(SB), Y6 |
|||
VMOVDQA Y4, Y14 |
|||
VMOVDQA Y5, Y15 |
|||
VMOVDQA Y4, C16 |
|||
VMOVDQA Y5, C8 |
|||
VMOVDQA Y6, TWO |
|||
|
|||
CMPQ CX, $64 |
|||
JBE between_0_and_64 |
|||
CMPQ CX, $192 |
|||
JBE between_64_and_192 |
|||
CMPQ CX, $320 |
|||
JBE between_192_and_320 |
|||
CMPQ CX, $448 |
|||
JBE between_320_and_448 |
|||
|
|||
at_least_512: |
|||
VMOVDQA Y0, Y4 |
|||
VMOVDQA Y1, Y5 |
|||
VMOVDQA Y2, Y6 |
|||
VPADDQ TWO, Y3, Y7 |
|||
VMOVDQA Y0, Y8 |
|||
VMOVDQA Y1, Y9 |
|||
VMOVDQA Y2, Y10 |
|||
VPADDQ TWO, Y7, Y11 |
|||
VMOVDQA Y0, Y12 |
|||
VMOVDQA Y1, Y13 |
|||
VMOVDQA Y2, Y14 |
|||
VPADDQ TWO, Y11, Y15 |
|||
|
|||
MOVQ DX, R9 |
|||
|
|||
chacha_loop_512: |
|||
VMOVDQA Y8, TMP_0 |
|||
CHACHA_QROUND_AVX(Y0, Y1, Y2, Y3, Y8, C16, C8) |
|||
CHACHA_QROUND_AVX(Y4, Y5, Y6, Y7, Y8, C16, C8) |
|||
VMOVDQA TMP_0, Y8 |
|||
VMOVDQA Y0, TMP_0 |
|||
CHACHA_QROUND_AVX(Y8, Y9, Y10, Y11, Y0, C16, C8) |
|||
CHACHA_QROUND_AVX(Y12, Y13, Y14, Y15, Y0, C16, C8) |
|||
CHACHA_SHUFFLE_AVX(Y1, Y2, Y3) |
|||
CHACHA_SHUFFLE_AVX(Y5, Y6, Y7) |
|||
CHACHA_SHUFFLE_AVX(Y9, Y10, Y11) |
|||
CHACHA_SHUFFLE_AVX(Y13, Y14, Y15) |
|||
|
|||
CHACHA_QROUND_AVX(Y12, Y13, Y14, Y15, Y0, C16, C8) |
|||
CHACHA_QROUND_AVX(Y8, Y9, Y10, Y11, Y0, C16, C8) |
|||
VMOVDQA TMP_0, Y0 |
|||
VMOVDQA Y8, TMP_0 |
|||
CHACHA_QROUND_AVX(Y4, Y5, Y6, Y7, Y8, C16, C8) |
|||
CHACHA_QROUND_AVX(Y0, Y1, Y2, Y3, Y8, C16, C8) |
|||
VMOVDQA TMP_0, Y8 |
|||
CHACHA_SHUFFLE_AVX(Y3, Y2, Y1) |
|||
CHACHA_SHUFFLE_AVX(Y7, Y6, Y5) |
|||
CHACHA_SHUFFLE_AVX(Y11, Y10, Y9) |
|||
CHACHA_SHUFFLE_AVX(Y15, Y14, Y13) |
|||
SUBQ $2, R9 |
|||
JA chacha_loop_512 |
|||
|
|||
VMOVDQA Y12, TMP_0 |
|||
VMOVDQA Y13, TMP_1 |
|||
VPADDD STATE_0, Y0, Y0 |
|||
VPADDD STATE_1, Y1, Y1 |
|||
VPADDD STATE_2, Y2, Y2 |
|||
VPADDD STATE_3, Y3, Y3 |
|||
XOR_AVX2(DI, SI, 0, Y0, Y1, Y2, Y3, Y12, Y13) |
|||
VMOVDQA STATE_0, Y0 |
|||
VMOVDQA STATE_1, Y1 |
|||
VMOVDQA STATE_2, Y2 |
|||
VMOVDQA STATE_3, Y3 |
|||
VPADDQ TWO, Y3, Y3 |
|||
|
|||
VPADDD Y0, Y4, Y4 |
|||
VPADDD Y1, Y5, Y5 |
|||
VPADDD Y2, Y6, Y6 |
|||
VPADDD Y3, Y7, Y7 |
|||
XOR_AVX2(DI, SI, 128, Y4, Y5, Y6, Y7, Y12, Y13) |
|||
VPADDQ TWO, Y3, Y3 |
|||
|
|||
VPADDD Y0, Y8, Y8 |
|||
VPADDD Y1, Y9, Y9 |
|||
VPADDD Y2, Y10, Y10 |
|||
VPADDD Y3, Y11, Y11 |
|||
XOR_AVX2(DI, SI, 256, Y8, Y9, Y10, Y11, Y12, Y13) |
|||
VPADDQ TWO, Y3, Y3 |
|||
|
|||
VPADDD TMP_0, Y0, Y12 |
|||
VPADDD TMP_1, Y1, Y13 |
|||
VPADDD Y2, Y14, Y14 |
|||
VPADDD Y3, Y15, Y15 |
|||
VPADDQ TWO, Y3, Y3 |
|||
|
|||
CMPQ CX, $512 |
|||
JB less_than_512 |
|||
|
|||
XOR_AVX2(DI, SI, 384, Y12, Y13, Y14, Y15, Y4, Y5) |
|||
VMOVDQA Y3, STATE_3 |
|||
ADDQ $512, SI |
|||
ADDQ $512, DI |
|||
SUBQ $512, CX |
|||
CMPQ CX, $448 |
|||
JA at_least_512 |
|||
|
|||
TESTQ CX, CX |
|||
JZ done |
|||
|
|||
VMOVDQA C16, Y14 |
|||
VMOVDQA C8, Y15 |
|||
|
|||
CMPQ CX, $64 |
|||
JBE between_0_and_64 |
|||
CMPQ CX, $192 |
|||
JBE between_64_and_192 |
|||
CMPQ CX, $320 |
|||
JBE between_192_and_320 |
|||
JMP between_320_and_448 |
|||
|
|||
less_than_512: |
|||
XOR_UPPER_AVX2(DI, SI, 384, Y12, Y13, Y14, Y15, Y4, Y5) |
|||
EXTRACT_LOWER(BX, Y12, Y13, Y14, Y15, Y4) |
|||
ADDQ $448, SI |
|||
ADDQ $448, DI |
|||
SUBQ $448, CX |
|||
JMP finalize |
|||
|
|||
between_320_and_448: |
|||
VMOVDQA Y0, Y4 |
|||
VMOVDQA Y1, Y5 |
|||
VMOVDQA Y2, Y6 |
|||
VPADDQ TWO, Y3, Y7 |
|||
VMOVDQA Y0, Y8 |
|||
VMOVDQA Y1, Y9 |
|||
VMOVDQA Y2, Y10 |
|||
VPADDQ TWO, Y7, Y11 |
|||
|
|||
MOVQ DX, R9 |
|||
|
|||
chacha_loop_384: |
|||
CHACHA_QROUND_AVX(Y0, Y1, Y2, Y3, Y13, Y14, Y15) |
|||
CHACHA_QROUND_AVX(Y4, Y5, Y6, Y7, Y13, Y14, Y15) |
|||
CHACHA_QROUND_AVX(Y8, Y9, Y10, Y11, Y13, Y14, Y15) |
|||
CHACHA_SHUFFLE_AVX(Y1, Y2, Y3) |
|||
CHACHA_SHUFFLE_AVX(Y5, Y6, Y7) |
|||
CHACHA_SHUFFLE_AVX(Y9, Y10, Y11) |
|||
CHACHA_QROUND_AVX(Y0, Y1, Y2, Y3, Y13, Y14, Y15) |
|||
CHACHA_QROUND_AVX(Y4, Y5, Y6, Y7, Y13, Y14, Y15) |
|||
CHACHA_QROUND_AVX(Y8, Y9, Y10, Y11, Y13, Y14, Y15) |
|||
CHACHA_SHUFFLE_AVX(Y3, Y2, Y1) |
|||
CHACHA_SHUFFLE_AVX(Y7, Y6, Y5) |
|||
CHACHA_SHUFFLE_AVX(Y11, Y10, Y9) |
|||
SUBQ $2, R9 |
|||
JA chacha_loop_384 |
|||
|
|||
VPADDD STATE_0, Y0, Y0 |
|||
VPADDD STATE_1, Y1, Y1 |
|||
VPADDD STATE_2, Y2, Y2 |
|||
VPADDD STATE_3, Y3, Y3 |
|||
XOR_AVX2(DI, SI, 0, Y0, Y1, Y2, Y3, Y12, Y13) |
|||
VMOVDQA STATE_0, Y0 |
|||
VMOVDQA STATE_1, Y1 |
|||
VMOVDQA STATE_2, Y2 |
|||
VMOVDQA STATE_3, Y3 |
|||
VPADDQ TWO, Y3, Y3 |
|||
|
|||
VPADDD Y0, Y4, Y4 |
|||
VPADDD Y1, Y5, Y5 |
|||
VPADDD Y2, Y6, Y6 |
|||
VPADDD Y3, Y7, Y7 |
|||
XOR_AVX2(DI, SI, 128, Y4, Y5, Y6, Y7, Y12, Y13) |
|||
VPADDQ TWO, Y3, Y3 |
|||
|
|||
VPADDD Y0, Y8, Y8 |
|||
VPADDD Y1, Y9, Y9 |
|||
VPADDD Y2, Y10, Y10 |
|||
VPADDD Y3, Y11, Y11 |
|||
VPADDQ TWO, Y3, Y3 |
|||
|
|||
CMPQ CX, $384 |
|||
JB less_than_384 |
|||
|
|||
XOR_AVX2(DI, SI, 256, Y8, Y9, Y10, Y11, Y12, Y13) |
|||
SUBQ $384, CX |
|||
TESTQ CX, CX |
|||
JE done |
|||
|
|||
ADDQ $384, SI |
|||
ADDQ $384, DI |
|||
JMP between_0_and_64 |
|||
|
|||
less_than_384: |
|||
XOR_UPPER_AVX2(DI, SI, 256, Y8, Y9, Y10, Y11, Y12, Y13) |
|||
EXTRACT_LOWER(BX, Y8, Y9, Y10, Y11, Y12) |
|||
ADDQ $320, SI |
|||
ADDQ $320, DI |
|||
SUBQ $320, CX |
|||
JMP finalize |
|||
|
|||
between_192_and_320: |
|||
VMOVDQA Y0, Y4 |
|||
VMOVDQA Y1, Y5 |
|||
VMOVDQA Y2, Y6 |
|||
VMOVDQA Y3, Y7 |
|||
VMOVDQA Y0, Y8 |
|||
VMOVDQA Y1, Y9 |
|||
VMOVDQA Y2, Y10 |
|||
VPADDQ TWO, Y3, Y11 |
|||
|
|||
MOVQ DX, R9 |
|||
|
|||
chacha_loop_256: |
|||
CHACHA_QROUND_AVX(Y4, Y5, Y6, Y7, Y13, Y14, Y15) |
|||
CHACHA_QROUND_AVX(Y8, Y9, Y10, Y11, Y13, Y14, Y15) |
|||
CHACHA_SHUFFLE_AVX(Y5, Y6, Y7) |
|||
CHACHA_SHUFFLE_AVX(Y9, Y10, Y11) |
|||
CHACHA_QROUND_AVX(Y4, Y5, Y6, Y7, Y13, Y14, Y15) |
|||
CHACHA_QROUND_AVX(Y8, Y9, Y10, Y11, Y13, Y14, Y15) |
|||
CHACHA_SHUFFLE_AVX(Y7, Y6, Y5) |
|||
CHACHA_SHUFFLE_AVX(Y11, Y10, Y9) |
|||
SUBQ $2, R9 |
|||
JA chacha_loop_256 |
|||
|
|||
VPADDD Y0, Y4, Y4 |
|||
VPADDD Y1, Y5, Y5 |
|||
VPADDD Y2, Y6, Y6 |
|||
VPADDD Y3, Y7, Y7 |
|||
VPADDQ TWO, Y3, Y3 |
|||
XOR_AVX2(DI, SI, 0, Y4, Y5, Y6, Y7, Y12, Y13) |
|||
VPADDD Y0, Y8, Y8 |
|||
VPADDD Y1, Y9, Y9 |
|||
VPADDD Y2, Y10, Y10 |
|||
VPADDD Y3, Y11, Y11 |
|||
VPADDQ TWO, Y3, Y3 |
|||
|
|||
CMPQ CX, $256 |
|||
JB less_than_256 |
|||
|
|||
XOR_AVX2(DI, SI, 128, Y8, Y9, Y10, Y11, Y12, Y13) |
|||
SUBQ $256, CX |
|||
TESTQ CX, CX |
|||
JE done |
|||
|
|||
ADDQ $256, SI |
|||
ADDQ $256, DI |
|||
JMP between_0_and_64 |
|||
|
|||
less_than_256: |
|||
XOR_UPPER_AVX2(DI, SI, 128, Y8, Y9, Y10, Y11, Y12, Y13) |
|||
EXTRACT_LOWER(BX, Y8, Y9, Y10, Y11, Y12) |
|||
ADDQ $192, SI |
|||
ADDQ $192, DI |
|||
SUBQ $192, CX |
|||
JMP finalize |
|||
|
|||
between_64_and_192: |
|||
VMOVDQA Y0, Y4 |
|||
VMOVDQA Y1, Y5 |
|||
VMOVDQA Y2, Y6 |
|||
VMOVDQA Y3, Y7 |
|||
|
|||
MOVQ DX, R9 |
|||
|
|||
chacha_loop_128: |
|||
CHACHA_QROUND_AVX(Y4, Y5, Y6, Y7, Y13, Y14, Y15) |
|||
CHACHA_SHUFFLE_AVX(Y5, Y6, Y7) |
|||
CHACHA_QROUND_AVX(Y4, Y5, Y6, Y7, Y13, Y14, Y15) |
|||
CHACHA_SHUFFLE_AVX(Y7, Y6, Y5) |
|||
SUBQ $2, R9 |
|||
JA chacha_loop_128 |
|||
|
|||
VPADDD Y0, Y4, Y4 |
|||
VPADDD Y1, Y5, Y5 |
|||
VPADDD Y2, Y6, Y6 |
|||
VPADDD Y3, Y7, Y7 |
|||
VPADDQ TWO, Y3, Y3 |
|||
|
|||
CMPQ CX, $128 |
|||
JB less_than_128 |
|||
|
|||
XOR_AVX2(DI, SI, 0, Y4, Y5, Y6, Y7, Y12, Y13) |
|||
SUBQ $128, CX |
|||
TESTQ CX, CX |
|||
JE done |
|||
|
|||
ADDQ $128, SI |
|||
ADDQ $128, DI |
|||
JMP between_0_and_64 |
|||
|
|||
less_than_128: |
|||
XOR_UPPER_AVX2(DI, SI, 0, Y4, Y5, Y6, Y7, Y12, Y13) |
|||
EXTRACT_LOWER(BX, Y4, Y5, Y6, Y7, Y13) |
|||
ADDQ $64, SI |
|||
ADDQ $64, DI |
|||
SUBQ $64, CX |
|||
JMP finalize |
|||
|
|||
between_0_and_64: |
|||
VMOVDQA X0, X4 |
|||
VMOVDQA X1, X5 |
|||
VMOVDQA X2, X6 |
|||
VMOVDQA X3, X7 |
|||
|
|||
MOVQ DX, R9 |
|||
|
|||
chacha_loop_64: |
|||
CHACHA_QROUND_AVX(X4, X5, X6, X7, X13, X14, X15) |
|||
CHACHA_SHUFFLE_AVX(X5, X6, X7) |
|||
CHACHA_QROUND_AVX(X4, X5, X6, X7, X13, X14, X15) |
|||
CHACHA_SHUFFLE_AVX(X7, X6, X5) |
|||
SUBQ $2, R9 |
|||
JA chacha_loop_64 |
|||
|
|||
VPADDD X0, X4, X4 |
|||
VPADDD X1, X5, X5 |
|||
VPADDD X2, X6, X6 |
|||
VPADDD X3, X7, X7 |
|||
VMOVDQU ·one<>(SB), X0 |
|||
VPADDQ X0, X3, X3 |
|||
|
|||
CMPQ CX, $64 |
|||
JB less_than_64 |
|||
|
|||
XOR_AVX(DI, SI, 0, X4, X5, X6, X7, X13) |
|||
SUBQ $64, CX |
|||
JMP done |
|||
|
|||
less_than_64: |
|||
VMOVDQU X4, 0(BX) |
|||
VMOVDQU X5, 16(BX) |
|||
VMOVDQU X6, 32(BX) |
|||
VMOVDQU X7, 48(BX) |
|||
|
|||
finalize: |
|||
XORQ R11, R11 |
|||
XORQ R12, R12 |
|||
MOVQ CX, BP |
|||
|
|||
xor_loop: |
|||
MOVB 0(SI), R11 |
|||
MOVB 0(BX), R12 |
|||
XORQ R11, R12 |
|||
MOVB R12, 0(DI) |
|||
INCQ SI |
|||
INCQ BX |
|||
INCQ DI |
|||
DECQ BP |
|||
JA xor_loop |
|||
|
|||
done: |
|||
VMOVDQU X3, 48(AX) |
|||
VZEROUPPER |
|||
MOVQ R8, SP |
|||
MOVQ CX, ret+72(FP) |
|||
RET |
|||
|
|||
@ -0,0 +1,60 @@ |
|||
// Copyright (c) 2016 Andreas Auernhammer. All rights reserved.
|
|||
// Use of this source code is governed by a license that can be
|
|||
// found in the LICENSE file.
|
|||
|
|||
// +build 386,!gccgo,!appengine,!nacl
|
|||
|
|||
package chacha |
|||
|
|||
import ( |
|||
"encoding/binary" |
|||
|
|||
"golang.org/x/sys/cpu" |
|||
) |
|||
|
|||
func init() { |
|||
useSSE2 = cpu.X86.HasSSE2 |
|||
useSSSE3 = cpu.X86.HasSSSE3 |
|||
useAVX = false |
|||
useAVX2 = false |
|||
} |
|||
|
|||
func initialize(state *[64]byte, key []byte, nonce *[16]byte) { |
|||
binary.LittleEndian.PutUint32(state[0:], sigma[0]) |
|||
binary.LittleEndian.PutUint32(state[4:], sigma[1]) |
|||
binary.LittleEndian.PutUint32(state[8:], sigma[2]) |
|||
binary.LittleEndian.PutUint32(state[12:], sigma[3]) |
|||
copy(state[16:], key[:]) |
|||
copy(state[48:], nonce[:]) |
|||
} |
|||
|
|||
// This function is implemented in chacha_386.s
|
|||
//go:noescape
|
|||
func hChaCha20SSE2(out *[32]byte, nonce *[16]byte, key *[32]byte) |
|||
|
|||
// This function is implemented in chacha_386.s
|
|||
//go:noescape
|
|||
func hChaCha20SSSE3(out *[32]byte, nonce *[16]byte, key *[32]byte) |
|||
|
|||
// This function is implemented in chacha_386.s
|
|||
//go:noescape
|
|||
func xorKeyStreamSSE2(dst, src []byte, block, state *[64]byte, rounds int) int |
|||
|
|||
func hChaCha20(out *[32]byte, nonce *[16]byte, key *[32]byte) { |
|||
switch { |
|||
case useSSSE3: |
|||
hChaCha20SSSE3(out, nonce, key) |
|||
case useSSE2: |
|||
hChaCha20SSE2(out, nonce, key) |
|||
default: |
|||
hChaCha20Generic(out, nonce, key) |
|||
} |
|||
} |
|||
|
|||
func xorKeyStream(dst, src []byte, block, state *[64]byte, rounds int) int { |
|||
if useSSE2 { |
|||
return xorKeyStreamSSE2(dst, src, block, state, rounds) |
|||
} else { |
|||
return xorKeyStreamGeneric(dst, src, block, state, rounds) |
|||
} |
|||
} |
|||
@ -0,0 +1,163 @@ |
|||
// Copyright (c) 2016 Andreas Auernhammer. All rights reserved. |
|||
// Use of this source code is governed by a license that can be |
|||
// found in the LICENSE file. |
|||
|
|||
// +build 386,!gccgo,!appengine,!nacl |
|||
|
|||
#include "const.s" |
|||
#include "macro.s" |
|||
|
|||
// FINALIZE xors len bytes from src and block using |
|||
// the temp. registers t0 and t1 and writes the result |
|||
// to dst. |
|||
#define FINALIZE(dst, src, block, len, t0, t1) \ |
|||
XORL t0, t0; \ |
|||
XORL t1, t1; \ |
|||
FINALIZE_LOOP:; \ |
|||
MOVB 0(src), t0; \ |
|||
MOVB 0(block), t1; \ |
|||
XORL t0, t1; \ |
|||
MOVB t1, 0(dst); \ |
|||
INCL src; \ |
|||
INCL block; \ |
|||
INCL dst; \ |
|||
DECL len; \ |
|||
JG FINALIZE_LOOP \ |
|||
|
|||
#define Dst DI |
|||
#define Nonce AX |
|||
#define Key BX |
|||
#define Rounds DX |
|||
|
|||
// func hChaCha20SSE2(out *[32]byte, nonce *[16]byte, key *[32]byte) |
|||
TEXT ·hChaCha20SSE2(SB), 4, $0-12 |
|||
MOVL out+0(FP), Dst |
|||
MOVL nonce+4(FP), Nonce |
|||
MOVL key+8(FP), Key |
|||
|
|||
MOVOU ·sigma<>(SB), X0 |
|||
MOVOU 0*16(Key), X1 |
|||
MOVOU 1*16(Key), X2 |
|||
MOVOU 0*16(Nonce), X3 |
|||
MOVL $20, Rounds |
|||
|
|||
chacha_loop: |
|||
CHACHA_QROUND_SSE2(X0, X1, X2, X3, X4) |
|||
CHACHA_SHUFFLE_SSE(X1, X2, X3) |
|||
CHACHA_QROUND_SSE2(X0, X1, X2, X3, X4) |
|||
CHACHA_SHUFFLE_SSE(X3, X2, X1) |
|||
SUBL $2, Rounds |
|||
JNZ chacha_loop |
|||
|
|||
MOVOU X0, 0*16(Dst) |
|||
MOVOU X3, 1*16(Dst) |
|||
RET |
|||
|
|||
// func hChaCha20SSSE3(out *[32]byte, nonce *[16]byte, key *[32]byte) |
|||
TEXT ·hChaCha20SSSE3(SB), 4, $0-12 |
|||
MOVL out+0(FP), Dst |
|||
MOVL nonce+4(FP), Nonce |
|||
MOVL key+8(FP), Key |
|||
|
|||
MOVOU ·sigma<>(SB), X0 |
|||
MOVOU 0*16(Key), X1 |
|||
MOVOU 1*16(Key), X2 |
|||
MOVOU 0*16(Nonce), X3 |
|||
MOVL $20, Rounds |
|||
|
|||
MOVOU ·rol16<>(SB), X5 |
|||
MOVOU ·rol8<>(SB), X6 |
|||
|
|||
chacha_loop: |
|||
CHACHA_QROUND_SSSE3(X0, X1, X2, X3, X4, X5, X6) |
|||
CHACHA_SHUFFLE_SSE(X1, X2, X3) |
|||
CHACHA_QROUND_SSSE3(X0, X1, X2, X3, X4, X5, X6) |
|||
CHACHA_SHUFFLE_SSE(X3, X2, X1) |
|||
SUBL $2, Rounds |
|||
JNZ chacha_loop |
|||
|
|||
MOVOU X0, 0*16(Dst) |
|||
MOVOU X3, 1*16(Dst) |
|||
RET |
|||
|
|||
#undef Dst |
|||
#undef Nonce |
|||
#undef Key |
|||
#undef Rounds |
|||
|
|||
#define State AX |
|||
#define Dst DI |
|||
#define Src SI |
|||
#define Len DX |
|||
#define Tmp0 BX |
|||
#define Tmp1 BP |
|||
|
|||
// func xorKeyStreamSSE2(dst, src []byte, block, state *[64]byte, rounds int) int |
|||
TEXT ·xorKeyStreamSSE2(SB), 4, $0-40 |
|||
MOVL dst_base+0(FP), Dst |
|||
MOVL src_base+12(FP), Src |
|||
MOVL state+28(FP), State |
|||
MOVL src_len+16(FP), Len |
|||
MOVL $0, ret+36(FP) // Number of bytes written to the keystream buffer - 0 iff len mod 64 == 0 |
|||
|
|||
MOVOU 0*16(State), X0 |
|||
MOVOU 1*16(State), X1 |
|||
MOVOU 2*16(State), X2 |
|||
MOVOU 3*16(State), X3 |
|||
TESTL Len, Len |
|||
JZ DONE |
|||
|
|||
GENERATE_KEYSTREAM: |
|||
MOVO X0, X4 |
|||
MOVO X1, X5 |
|||
MOVO X2, X6 |
|||
MOVO X3, X7 |
|||
MOVL rounds+32(FP), Tmp0 |
|||
|
|||
CHACHA_LOOP: |
|||
CHACHA_QROUND_SSE2(X4, X5, X6, X7, X0) |
|||
CHACHA_SHUFFLE_SSE(X5, X6, X7) |
|||
CHACHA_QROUND_SSE2(X4, X5, X6, X7, X0) |
|||
CHACHA_SHUFFLE_SSE(X7, X6, X5) |
|||
SUBL $2, Tmp0 |
|||
JA CHACHA_LOOP |
|||
|
|||
MOVOU 0*16(State), X0 // Restore X0 from state |
|||
PADDL X0, X4 |
|||
PADDL X1, X5 |
|||
PADDL X2, X6 |
|||
PADDL X3, X7 |
|||
MOVOU ·one<>(SB), X0 |
|||
PADDQ X0, X3 |
|||
|
|||
CMPL Len, $64 |
|||
JL BUFFER_KEYSTREAM |
|||
|
|||
XOR_SSE(Dst, Src, 0, X4, X5, X6, X7, X0) |
|||
MOVOU 0*16(State), X0 // Restore X0 from state |
|||
ADDL $64, Src |
|||
ADDL $64, Dst |
|||
SUBL $64, Len |
|||
JZ DONE |
|||
JMP GENERATE_KEYSTREAM // There is at least one more plaintext byte |
|||
|
|||
BUFFER_KEYSTREAM: |
|||
MOVL block+24(FP), State |
|||
MOVOU X4, 0(State) |
|||
MOVOU X5, 16(State) |
|||
MOVOU X6, 32(State) |
|||
MOVOU X7, 48(State) |
|||
MOVL Len, ret+36(FP) // Number of bytes written to the keystream buffer - 0 < Len < 64 |
|||
FINALIZE(Dst, Src, State, Len, Tmp0, Tmp1) |
|||
|
|||
DONE: |
|||
MOVL state+28(FP), State |
|||
MOVOU X3, 3*16(State) |
|||
RET |
|||
|
|||
#undef State |
|||
#undef Dst |
|||
#undef Src |
|||
#undef Len |
|||
#undef Tmp0 |
|||
#undef Tmp1 |
|||
@ -0,0 +1,76 @@ |
|||
// Copyright (c) 2017 Andreas Auernhammer. All rights reserved.
|
|||
// Use of this source code is governed by a license that can be
|
|||
// found in the LICENSE file.
|
|||
|
|||
// +build go1.7,amd64,!gccgo,!appengine,!nacl
|
|||
|
|||
package chacha |
|||
|
|||
import "golang.org/x/sys/cpu" |
|||
|
|||
func init() { |
|||
useSSE2 = cpu.X86.HasSSE2 |
|||
useSSSE3 = cpu.X86.HasSSSE3 |
|||
useAVX = cpu.X86.HasAVX |
|||
useAVX2 = cpu.X86.HasAVX2 |
|||
} |
|||
|
|||
// This function is implemented in chacha_amd64.s
|
|||
//go:noescape
|
|||
func initialize(state *[64]byte, key []byte, nonce *[16]byte) |
|||
|
|||
// This function is implemented in chacha_amd64.s
|
|||
//go:noescape
|
|||
func hChaCha20SSE2(out *[32]byte, nonce *[16]byte, key *[32]byte) |
|||
|
|||
// This function is implemented in chacha_amd64.s
|
|||
//go:noescape
|
|||
func hChaCha20SSSE3(out *[32]byte, nonce *[16]byte, key *[32]byte) |
|||
|
|||
// This function is implemented in chachaAVX2_amd64.s
|
|||
//go:noescape
|
|||
func hChaCha20AVX(out *[32]byte, nonce *[16]byte, key *[32]byte) |
|||
|
|||
// This function is implemented in chacha_amd64.s
|
|||
//go:noescape
|
|||
func xorKeyStreamSSE2(dst, src []byte, block, state *[64]byte, rounds int) int |
|||
|
|||
// This function is implemented in chacha_amd64.s
|
|||
//go:noescape
|
|||
func xorKeyStreamSSSE3(dst, src []byte, block, state *[64]byte, rounds int) int |
|||
|
|||
// This function is implemented in chacha_amd64.s
|
|||
//go:noescape
|
|||
func xorKeyStreamAVX(dst, src []byte, block, state *[64]byte, rounds int) int |
|||
|
|||
// This function is implemented in chachaAVX2_amd64.s
|
|||
//go:noescape
|
|||
func xorKeyStreamAVX2(dst, src []byte, block, state *[64]byte, rounds int) int |
|||
|
|||
func hChaCha20(out *[32]byte, nonce *[16]byte, key *[32]byte) { |
|||
switch { |
|||
case useAVX: |
|||
hChaCha20AVX(out, nonce, key) |
|||
case useSSSE3: |
|||
hChaCha20SSSE3(out, nonce, key) |
|||
case useSSE2: |
|||
hChaCha20SSE2(out, nonce, key) |
|||
default: |
|||
hChaCha20Generic(out, nonce, key) |
|||
} |
|||
} |
|||
|
|||
func xorKeyStream(dst, src []byte, block, state *[64]byte, rounds int) int { |
|||
switch { |
|||
case useAVX2: |
|||
return xorKeyStreamAVX2(dst, src, block, state, rounds) |
|||
case useAVX: |
|||
return xorKeyStreamAVX(dst, src, block, state, rounds) |
|||
case useSSSE3: |
|||
return xorKeyStreamSSSE3(dst, src, block, state, rounds) |
|||
case useSSE2: |
|||
return xorKeyStreamSSE2(dst, src, block, state, rounds) |
|||
default: |
|||
return xorKeyStreamGeneric(dst, src, block, state, rounds) |
|||
} |
|||
} |
|||
File diff suppressed because it is too large
@ -0,0 +1,319 @@ |
|||
// Copyright (c) 2016 Andreas Auernhammer. All rights reserved.
|
|||
// Use of this source code is governed by a license that can be
|
|||
// found in the LICENSE file.
|
|||
|
|||
package chacha |
|||
|
|||
import "encoding/binary" |
|||
|
|||
var sigma = [4]uint32{0x61707865, 0x3320646e, 0x79622d32, 0x6b206574} |
|||
|
|||
func xorKeyStreamGeneric(dst, src []byte, block, state *[64]byte, rounds int) int { |
|||
for len(src) >= 64 { |
|||
chachaGeneric(block, state, rounds) |
|||
|
|||
for i, v := range block { |
|||
dst[i] = src[i] ^ v |
|||
} |
|||
src = src[64:] |
|||
dst = dst[64:] |
|||
} |
|||
|
|||
n := len(src) |
|||
if n > 0 { |
|||
chachaGeneric(block, state, rounds) |
|||
for i, v := range src { |
|||
dst[i] = v ^ block[i] |
|||
} |
|||
} |
|||
return n |
|||
} |
|||
|
|||
func chachaGeneric(dst *[64]byte, state *[64]byte, rounds int) { |
|||
v00 := binary.LittleEndian.Uint32(state[0:]) |
|||
v01 := binary.LittleEndian.Uint32(state[4:]) |
|||
v02 := binary.LittleEndian.Uint32(state[8:]) |
|||
v03 := binary.LittleEndian.Uint32(state[12:]) |
|||
v04 := binary.LittleEndian.Uint32(state[16:]) |
|||
v05 := binary.LittleEndian.Uint32(state[20:]) |
|||
v06 := binary.LittleEndian.Uint32(state[24:]) |
|||
v07 := binary.LittleEndian.Uint32(state[28:]) |
|||
v08 := binary.LittleEndian.Uint32(state[32:]) |
|||
v09 := binary.LittleEndian.Uint32(state[36:]) |
|||
v10 := binary.LittleEndian.Uint32(state[40:]) |
|||
v11 := binary.LittleEndian.Uint32(state[44:]) |
|||
v12 := binary.LittleEndian.Uint32(state[48:]) |
|||
v13 := binary.LittleEndian.Uint32(state[52:]) |
|||
v14 := binary.LittleEndian.Uint32(state[56:]) |
|||
v15 := binary.LittleEndian.Uint32(state[60:]) |
|||
|
|||
s00, s01, s02, s03, s04, s05, s06, s07 := v00, v01, v02, v03, v04, v05, v06, v07 |
|||
s08, s09, s10, s11, s12, s13, s14, s15 := v08, v09, v10, v11, v12, v13, v14, v15 |
|||
|
|||
for i := 0; i < rounds; i += 2 { |
|||
v00 += v04 |
|||
v12 ^= v00 |
|||
v12 = (v12 << 16) | (v12 >> 16) |
|||
v08 += v12 |
|||
v04 ^= v08 |
|||
v04 = (v04 << 12) | (v04 >> 20) |
|||
v00 += v04 |
|||
v12 ^= v00 |
|||
v12 = (v12 << 8) | (v12 >> 24) |
|||
v08 += v12 |
|||
v04 ^= v08 |
|||
v04 = (v04 << 7) | (v04 >> 25) |
|||
v01 += v05 |
|||
v13 ^= v01 |
|||
v13 = (v13 << 16) | (v13 >> 16) |
|||
v09 += v13 |
|||
v05 ^= v09 |
|||
v05 = (v05 << 12) | (v05 >> 20) |
|||
v01 += v05 |
|||
v13 ^= v01 |
|||
v13 = (v13 << 8) | (v13 >> 24) |
|||
v09 += v13 |
|||
v05 ^= v09 |
|||
v05 = (v05 << 7) | (v05 >> 25) |
|||
v02 += v06 |
|||
v14 ^= v02 |
|||
v14 = (v14 << 16) | (v14 >> 16) |
|||
v10 += v14 |
|||
v06 ^= v10 |
|||
v06 = (v06 << 12) | (v06 >> 20) |
|||
v02 += v06 |
|||
v14 ^= v02 |
|||
v14 = (v14 << 8) | (v14 >> 24) |
|||
v10 += v14 |
|||
v06 ^= v10 |
|||
v06 = (v06 << 7) | (v06 >> 25) |
|||
v03 += v07 |
|||
v15 ^= v03 |
|||
v15 = (v15 << 16) | (v15 >> 16) |
|||
v11 += v15 |
|||
v07 ^= v11 |
|||
v07 = (v07 << 12) | (v07 >> 20) |
|||
v03 += v07 |
|||
v15 ^= v03 |
|||
v15 = (v15 << 8) | (v15 >> 24) |
|||
v11 += v15 |
|||
v07 ^= v11 |
|||
v07 = (v07 << 7) | (v07 >> 25) |
|||
v00 += v05 |
|||
v15 ^= v00 |
|||
v15 = (v15 << 16) | (v15 >> 16) |
|||
v10 += v15 |
|||
v05 ^= v10 |
|||
v05 = (v05 << 12) | (v05 >> 20) |
|||
v00 += v05 |
|||
v15 ^= v00 |
|||
v15 = (v15 << 8) | (v15 >> 24) |
|||
v10 += v15 |
|||
v05 ^= v10 |
|||
v05 = (v05 << 7) | (v05 >> 25) |
|||
v01 += v06 |
|||
v12 ^= v01 |
|||
v12 = (v12 << 16) | (v12 >> 16) |
|||
v11 += v12 |
|||
v06 ^= v11 |
|||
v06 = (v06 << 12) | (v06 >> 20) |
|||
v01 += v06 |
|||
v12 ^= v01 |
|||
v12 = (v12 << 8) | (v12 >> 24) |
|||
v11 += v12 |
|||
v06 ^= v11 |
|||
v06 = (v06 << 7) | (v06 >> 25) |
|||
v02 += v07 |
|||
v13 ^= v02 |
|||
v13 = (v13 << 16) | (v13 >> 16) |
|||
v08 += v13 |
|||
v07 ^= v08 |
|||
v07 = (v07 << 12) | (v07 >> 20) |
|||
v02 += v07 |
|||
v13 ^= v02 |
|||
v13 = (v13 << 8) | (v13 >> 24) |
|||
v08 += v13 |
|||
v07 ^= v08 |
|||
v07 = (v07 << 7) | (v07 >> 25) |
|||
v03 += v04 |
|||
v14 ^= v03 |
|||
v14 = (v14 << 16) | (v14 >> 16) |
|||
v09 += v14 |
|||
v04 ^= v09 |
|||
v04 = (v04 << 12) | (v04 >> 20) |
|||
v03 += v04 |
|||
v14 ^= v03 |
|||
v14 = (v14 << 8) | (v14 >> 24) |
|||
v09 += v14 |
|||
v04 ^= v09 |
|||
v04 = (v04 << 7) | (v04 >> 25) |
|||
} |
|||
|
|||
v00 += s00 |
|||
v01 += s01 |
|||
v02 += s02 |
|||
v03 += s03 |
|||
v04 += s04 |
|||
v05 += s05 |
|||
v06 += s06 |
|||
v07 += s07 |
|||
v08 += s08 |
|||
v09 += s09 |
|||
v10 += s10 |
|||
v11 += s11 |
|||
v12 += s12 |
|||
v13 += s13 |
|||
v14 += s14 |
|||
v15 += s15 |
|||
|
|||
s12++ |
|||
binary.LittleEndian.PutUint32(state[48:], s12) |
|||
if s12 == 0 { // indicates overflow
|
|||
s13++ |
|||
binary.LittleEndian.PutUint32(state[52:], s13) |
|||
} |
|||
|
|||
binary.LittleEndian.PutUint32(dst[0:], v00) |
|||
binary.LittleEndian.PutUint32(dst[4:], v01) |
|||
binary.LittleEndian.PutUint32(dst[8:], v02) |
|||
binary.LittleEndian.PutUint32(dst[12:], v03) |
|||
binary.LittleEndian.PutUint32(dst[16:], v04) |
|||
binary.LittleEndian.PutUint32(dst[20:], v05) |
|||
binary.LittleEndian.PutUint32(dst[24:], v06) |
|||
binary.LittleEndian.PutUint32(dst[28:], v07) |
|||
binary.LittleEndian.PutUint32(dst[32:], v08) |
|||
binary.LittleEndian.PutUint32(dst[36:], v09) |
|||
binary.LittleEndian.PutUint32(dst[40:], v10) |
|||
binary.LittleEndian.PutUint32(dst[44:], v11) |
|||
binary.LittleEndian.PutUint32(dst[48:], v12) |
|||
binary.LittleEndian.PutUint32(dst[52:], v13) |
|||
binary.LittleEndian.PutUint32(dst[56:], v14) |
|||
binary.LittleEndian.PutUint32(dst[60:], v15) |
|||
} |
|||
|
|||
func hChaCha20Generic(out *[32]byte, nonce *[16]byte, key *[32]byte) { |
|||
v00 := sigma[0] |
|||
v01 := sigma[1] |
|||
v02 := sigma[2] |
|||
v03 := sigma[3] |
|||
v04 := binary.LittleEndian.Uint32(key[0:]) |
|||
v05 := binary.LittleEndian.Uint32(key[4:]) |
|||
v06 := binary.LittleEndian.Uint32(key[8:]) |
|||
v07 := binary.LittleEndian.Uint32(key[12:]) |
|||
v08 := binary.LittleEndian.Uint32(key[16:]) |
|||
v09 := binary.LittleEndian.Uint32(key[20:]) |
|||
v10 := binary.LittleEndian.Uint32(key[24:]) |
|||
v11 := binary.LittleEndian.Uint32(key[28:]) |
|||
v12 := binary.LittleEndian.Uint32(nonce[0:]) |
|||
v13 := binary.LittleEndian.Uint32(nonce[4:]) |
|||
v14 := binary.LittleEndian.Uint32(nonce[8:]) |
|||
v15 := binary.LittleEndian.Uint32(nonce[12:]) |
|||
|
|||
for i := 0; i < 20; i += 2 { |
|||
v00 += v04 |
|||
v12 ^= v00 |
|||
v12 = (v12 << 16) | (v12 >> 16) |
|||
v08 += v12 |
|||
v04 ^= v08 |
|||
v04 = (v04 << 12) | (v04 >> 20) |
|||
v00 += v04 |
|||
v12 ^= v00 |
|||
v12 = (v12 << 8) | (v12 >> 24) |
|||
v08 += v12 |
|||
v04 ^= v08 |
|||
v04 = (v04 << 7) | (v04 >> 25) |
|||
v01 += v05 |
|||
v13 ^= v01 |
|||
v13 = (v13 << 16) | (v13 >> 16) |
|||
v09 += v13 |
|||
v05 ^= v09 |
|||
v05 = (v05 << 12) | (v05 >> 20) |
|||
v01 += v05 |
|||
v13 ^= v01 |
|||
v13 = (v13 << 8) | (v13 >> 24) |
|||
v09 += v13 |
|||
v05 ^= v09 |
|||
v05 = (v05 << 7) | (v05 >> 25) |
|||
v02 += v06 |
|||
v14 ^= v02 |
|||
v14 = (v14 << 16) | (v14 >> 16) |
|||
v10 += v14 |
|||
v06 ^= v10 |
|||
v06 = (v06 << 12) | (v06 >> 20) |
|||
v02 += v06 |
|||
v14 ^= v02 |
|||
v14 = (v14 << 8) | (v14 >> 24) |
|||
v10 += v14 |
|||
v06 ^= v10 |
|||
v06 = (v06 << 7) | (v06 >> 25) |
|||
v03 += v07 |
|||
v15 ^= v03 |
|||
v15 = (v15 << 16) | (v15 >> 16) |
|||
v11 += v15 |
|||
v07 ^= v11 |
|||
v07 = (v07 << 12) | (v07 >> 20) |
|||
v03 += v07 |
|||
v15 ^= v03 |
|||
v15 = (v15 << 8) | (v15 >> 24) |
|||
v11 += v15 |
|||
v07 ^= v11 |
|||
v07 = (v07 << 7) | (v07 >> 25) |
|||
v00 += v05 |
|||
v15 ^= v00 |
|||
v15 = (v15 << 16) | (v15 >> 16) |
|||
v10 += v15 |
|||
v05 ^= v10 |
|||
v05 = (v05 << 12) | (v05 >> 20) |
|||
v00 += v05 |
|||
v15 ^= v00 |
|||
v15 = (v15 << 8) | (v15 >> 24) |
|||
v10 += v15 |
|||
v05 ^= v10 |
|||
v05 = (v05 << 7) | (v05 >> 25) |
|||
v01 += v06 |
|||
v12 ^= v01 |
|||
v12 = (v12 << 16) | (v12 >> 16) |
|||
v11 += v12 |
|||
v06 ^= v11 |
|||
v06 = (v06 << 12) | (v06 >> 20) |
|||
v01 += v06 |
|||
v12 ^= v01 |
|||
v12 = (v12 << 8) | (v12 >> 24) |
|||
v11 += v12 |
|||
v06 ^= v11 |
|||
v06 = (v06 << 7) | (v06 >> 25) |
|||
v02 += v07 |
|||
v13 ^= v02 |
|||
v13 = (v13 << 16) | (v13 >> 16) |
|||
v08 += v13 |
|||
v07 ^= v08 |
|||
v07 = (v07 << 12) | (v07 >> 20) |
|||
v02 += v07 |
|||
v13 ^= v02 |
|||
v13 = (v13 << 8) | (v13 >> 24) |
|||
v08 += v13 |
|||
v07 ^= v08 |
|||
v07 = (v07 << 7) | (v07 >> 25) |
|||
v03 += v04 |
|||
v14 ^= v03 |
|||
v14 = (v14 << 16) | (v14 >> 16) |
|||
v09 += v14 |
|||
v04 ^= v09 |
|||
v04 = (v04 << 12) | (v04 >> 20) |
|||
v03 += v04 |
|||
v14 ^= v03 |
|||
v14 = (v14 << 8) | (v14 >> 24) |
|||
v09 += v14 |
|||
v04 ^= v09 |
|||
v04 = (v04 << 7) | (v04 >> 25) |
|||
} |
|||
|
|||
binary.LittleEndian.PutUint32(out[0:], v00) |
|||
binary.LittleEndian.PutUint32(out[4:], v01) |
|||
binary.LittleEndian.PutUint32(out[8:], v02) |
|||
binary.LittleEndian.PutUint32(out[12:], v03) |
|||
binary.LittleEndian.PutUint32(out[16:], v12) |
|||
binary.LittleEndian.PutUint32(out[20:], v13) |
|||
binary.LittleEndian.PutUint32(out[24:], v14) |
|||
binary.LittleEndian.PutUint32(out[28:], v15) |
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
// Copyright (c) 2016 Andreas Auernhammer. All rights reserved.
|
|||
// Use of this source code is governed by a license that can be
|
|||
// found in the LICENSE file.
|
|||
|
|||
// +build !amd64,!386 gccgo appengine nacl
|
|||
|
|||
package chacha |
|||
|
|||
import "encoding/binary" |
|||
|
|||
func init() { |
|||
useSSE2 = false |
|||
useSSSE3 = false |
|||
useAVX = false |
|||
useAVX2 = false |
|||
} |
|||
|
|||
func initialize(state *[64]byte, key []byte, nonce *[16]byte) { |
|||
binary.LittleEndian.PutUint32(state[0:], sigma[0]) |
|||
binary.LittleEndian.PutUint32(state[4:], sigma[1]) |
|||
binary.LittleEndian.PutUint32(state[8:], sigma[2]) |
|||
binary.LittleEndian.PutUint32(state[12:], sigma[3]) |
|||
copy(state[16:], key[:]) |
|||
copy(state[48:], nonce[:]) |
|||
} |
|||
|
|||
func xorKeyStream(dst, src []byte, block, state *[64]byte, rounds int) int { |
|||
return xorKeyStreamGeneric(dst, src, block, state, rounds) |
|||
} |
|||
|
|||
func hChaCha20(out *[32]byte, nonce *[16]byte, key *[32]byte) { |
|||
hChaCha20Generic(out, nonce, key) |
|||
} |
|||
@ -0,0 +1,53 @@ |
|||
// Copyright (c) 2018 Andreas Auernhammer. All rights reserved. |
|||
// Use of this source code is governed by a license that can be |
|||
// found in the LICENSE file. |
|||
|
|||
// +build 386,!gccgo,!appengine,!nacl amd64,!gccgo,!appengine,!nacl |
|||
|
|||
#include "textflag.h" |
|||
|
|||
DATA ·sigma<>+0x00(SB)/4, $0x61707865 |
|||
DATA ·sigma<>+0x04(SB)/4, $0x3320646e |
|||
DATA ·sigma<>+0x08(SB)/4, $0x79622d32 |
|||
DATA ·sigma<>+0x0C(SB)/4, $0x6b206574 |
|||
GLOBL ·sigma<>(SB), (NOPTR+RODATA), $16 // The 4 ChaCha initialization constants |
|||
|
|||
// SSE2/SSE3/AVX constants |
|||
|
|||
DATA ·one<>+0x00(SB)/8, $1 |
|||
DATA ·one<>+0x08(SB)/8, $0 |
|||
GLOBL ·one<>(SB), (NOPTR+RODATA), $16 // The constant 1 as 128 bit value |
|||
|
|||
DATA ·rol16<>+0x00(SB)/8, $0x0504070601000302 |
|||
DATA ·rol16<>+0x08(SB)/8, $0x0D0C0F0E09080B0A |
|||
GLOBL ·rol16<>(SB), (NOPTR+RODATA), $16 // The PSHUFB 16 bit left rotate constant |
|||
|
|||
DATA ·rol8<>+0x00(SB)/8, $0x0605040702010003 |
|||
DATA ·rol8<>+0x08(SB)/8, $0x0E0D0C0F0A09080B |
|||
GLOBL ·rol8<>(SB), (NOPTR+RODATA), $16 // The PSHUFB 8 bit left rotate constant |
|||
|
|||
// AVX2 constants |
|||
|
|||
DATA ·one_AVX2<>+0x00(SB)/8, $0 |
|||
DATA ·one_AVX2<>+0x08(SB)/8, $0 |
|||
DATA ·one_AVX2<>+0x10(SB)/8, $1 |
|||
DATA ·one_AVX2<>+0x18(SB)/8, $0 |
|||
GLOBL ·one_AVX2<>(SB), (NOPTR+RODATA), $32 // The constant 1 as 256 bit value |
|||
|
|||
DATA ·two_AVX2<>+0x00(SB)/8, $2 |
|||
DATA ·two_AVX2<>+0x08(SB)/8, $0 |
|||
DATA ·two_AVX2<>+0x10(SB)/8, $2 |
|||
DATA ·two_AVX2<>+0x18(SB)/8, $0 |
|||
GLOBL ·two_AVX2<>(SB), (NOPTR+RODATA), $32 |
|||
|
|||
DATA ·rol16_AVX2<>+0x00(SB)/8, $0x0504070601000302 |
|||
DATA ·rol16_AVX2<>+0x08(SB)/8, $0x0D0C0F0E09080B0A |
|||
DATA ·rol16_AVX2<>+0x10(SB)/8, $0x0504070601000302 |
|||
DATA ·rol16_AVX2<>+0x18(SB)/8, $0x0D0C0F0E09080B0A |
|||
GLOBL ·rol16_AVX2<>(SB), (NOPTR+RODATA), $32 // The VPSHUFB 16 bit left rotate constant |
|||
|
|||
DATA ·rol8_AVX2<>+0x00(SB)/8, $0x0605040702010003 |
|||
DATA ·rol8_AVX2<>+0x08(SB)/8, $0x0E0D0C0F0A09080B |
|||
DATA ·rol8_AVX2<>+0x10(SB)/8, $0x0605040702010003 |
|||
DATA ·rol8_AVX2<>+0x18(SB)/8, $0x0E0D0C0F0A09080B |
|||
GLOBL ·rol8_AVX2<>(SB), (NOPTR+RODATA), $32 // The VPSHUFB 8 bit left rotate constant |
|||
@ -0,0 +1,163 @@ |
|||
// Copyright (c) 2018 Andreas Auernhammer. All rights reserved. |
|||
// Use of this source code is governed by a license that can be |
|||
// found in the LICENSE file. |
|||
|
|||
// +build 386,!gccgo,!appengine,!nacl amd64,!gccgo,!appengine,!nacl |
|||
|
|||
// ROTL_SSE rotates all 4 32 bit values of the XMM register v |
|||
// left by n bits using SSE2 instructions (0 <= n <= 32). |
|||
// The XMM register t is used as a temp. register. |
|||
#define ROTL_SSE(n, t, v) \ |
|||
MOVO v, t; \ |
|||
PSLLL $n, t; \ |
|||
PSRLL $(32-n), v; \ |
|||
PXOR t, v |
|||
|
|||
// ROTL_AVX rotates all 4/8 32 bit values of the AVX/AVX2 register v |
|||
// left by n bits using AVX/AVX2 instructions (0 <= n <= 32). |
|||
// The AVX/AVX2 register t is used as a temp. register. |
|||
#define ROTL_AVX(n, t, v) \ |
|||
VPSLLD $n, v, t; \ |
|||
VPSRLD $(32-n), v, v; \ |
|||
VPXOR v, t, v |
|||
|
|||
// CHACHA_QROUND_SSE2 performs a ChaCha quarter-round using the |
|||
// 4 XMM registers v0, v1, v2 and v3. It uses only ROTL_SSE2 for |
|||
// rotations. The XMM register t is used as a temp. register. |
|||
#define CHACHA_QROUND_SSE2(v0, v1, v2, v3, t) \ |
|||
PADDL v1, v0; \ |
|||
PXOR v0, v3; \ |
|||
ROTL_SSE(16, t, v3); \ |
|||
PADDL v3, v2; \ |
|||
PXOR v2, v1; \ |
|||
ROTL_SSE(12, t, v1); \ |
|||
PADDL v1, v0; \ |
|||
PXOR v0, v3; \ |
|||
ROTL_SSE(8, t, v3); \ |
|||
PADDL v3, v2; \ |
|||
PXOR v2, v1; \ |
|||
ROTL_SSE(7, t, v1) |
|||
|
|||
// CHACHA_QROUND_SSSE3 performs a ChaCha quarter-round using the |
|||
// 4 XMM registers v0, v1, v2 and v3. It uses PSHUFB for 8/16 bit |
|||
// rotations. The XMM register t is used as a temp. register. |
|||
// |
|||
// r16 holds the PSHUFB constant for a 16 bit left rotate. |
|||
// r8 holds the PSHUFB constant for a 8 bit left rotate. |
|||
#define CHACHA_QROUND_SSSE3(v0, v1, v2, v3, t, r16, r8) \ |
|||
PADDL v1, v0; \ |
|||
PXOR v0, v3; \ |
|||
PSHUFB r16, v3; \ |
|||
PADDL v3, v2; \ |
|||
PXOR v2, v1; \ |
|||
ROTL_SSE(12, t, v1); \ |
|||
PADDL v1, v0; \ |
|||
PXOR v0, v3; \ |
|||
PSHUFB r8, v3; \ |
|||
PADDL v3, v2; \ |
|||
PXOR v2, v1; \ |
|||
ROTL_SSE(7, t, v1) |
|||
|
|||
// CHACHA_QROUND_AVX performs a ChaCha quarter-round using the |
|||
// 4 AVX/AVX2 registers v0, v1, v2 and v3. It uses VPSHUFB for 8/16 bit |
|||
// rotations. The AVX/AVX2 register t is used as a temp. register. |
|||
// |
|||
// r16 holds the VPSHUFB constant for a 16 bit left rotate. |
|||
// r8 holds the VPSHUFB constant for a 8 bit left rotate. |
|||
#define CHACHA_QROUND_AVX(v0, v1, v2, v3, t, r16, r8) \ |
|||
VPADDD v0, v1, v0; \ |
|||
VPXOR v3, v0, v3; \ |
|||
VPSHUFB r16, v3, v3; \ |
|||
VPADDD v2, v3, v2; \ |
|||
VPXOR v1, v2, v1; \ |
|||
ROTL_AVX(12, t, v1); \ |
|||
VPADDD v0, v1, v0; \ |
|||
VPXOR v3, v0, v3; \ |
|||
VPSHUFB r8, v3, v3; \ |
|||
VPADDD v2, v3, v2; \ |
|||
VPXOR v1, v2, v1; \ |
|||
ROTL_AVX(7, t, v1) |
|||
|
|||
// CHACHA_SHUFFLE_SSE performs a ChaCha shuffle using the |
|||
// 3 XMM registers v1, v2 and v3. The inverse shuffle is |
|||
// performed by switching v1 and v3: CHACHA_SHUFFLE_SSE(v3, v2, v1). |
|||
#define CHACHA_SHUFFLE_SSE(v1, v2, v3) \ |
|||
PSHUFL $0x39, v1, v1; \ |
|||
PSHUFL $0x4E, v2, v2; \ |
|||
PSHUFL $0x93, v3, v3 |
|||
|
|||
// CHACHA_SHUFFLE_AVX performs a ChaCha shuffle using the |
|||
// 3 AVX/AVX2 registers v1, v2 and v3. The inverse shuffle is |
|||
// performed by switching v1 and v3: CHACHA_SHUFFLE_AVX(v3, v2, v1). |
|||
#define CHACHA_SHUFFLE_AVX(v1, v2, v3) \ |
|||
VPSHUFD $0x39, v1, v1; \ |
|||
VPSHUFD $0x4E, v2, v2; \ |
|||
VPSHUFD $0x93, v3, v3 |
|||
|
|||
// XOR_SSE extracts 4x16 byte vectors from src at |
|||
// off, xors all vectors with the corresponding XMM |
|||
// register (v0 - v3) and writes the result to dst |
|||
// at off. |
|||
// The XMM register t is used as a temp. register. |
|||
#define XOR_SSE(dst, src, off, v0, v1, v2, v3, t) \ |
|||
MOVOU 0+off(src), t; \ |
|||
PXOR v0, t; \ |
|||
MOVOU t, 0+off(dst); \ |
|||
MOVOU 16+off(src), t; \ |
|||
PXOR v1, t; \ |
|||
MOVOU t, 16+off(dst); \ |
|||
MOVOU 32+off(src), t; \ |
|||
PXOR v2, t; \ |
|||
MOVOU t, 32+off(dst); \ |
|||
MOVOU 48+off(src), t; \ |
|||
PXOR v3, t; \ |
|||
MOVOU t, 48+off(dst) |
|||
|
|||
// XOR_AVX extracts 4x16 byte vectors from src at |
|||
// off, xors all vectors with the corresponding AVX |
|||
// register (v0 - v3) and writes the result to dst |
|||
// at off. |
|||
// The XMM register t is used as a temp. register. |
|||
#define XOR_AVX(dst, src, off, v0, v1, v2, v3, t) \ |
|||
VPXOR 0+off(src), v0, t; \ |
|||
VMOVDQU t, 0+off(dst); \ |
|||
VPXOR 16+off(src), v1, t; \ |
|||
VMOVDQU t, 16+off(dst); \ |
|||
VPXOR 32+off(src), v2, t; \ |
|||
VMOVDQU t, 32+off(dst); \ |
|||
VPXOR 48+off(src), v3, t; \ |
|||
VMOVDQU t, 48+off(dst) |
|||
|
|||
#define XOR_AVX2(dst, src, off, v0, v1, v2, v3, t0, t1) \ |
|||
VMOVDQU (0+off)(src), t0; \ |
|||
VPERM2I128 $32, v1, v0, t1; \ |
|||
VPXOR t0, t1, t0; \ |
|||
VMOVDQU t0, (0+off)(dst); \ |
|||
VMOVDQU (32+off)(src), t0; \ |
|||
VPERM2I128 $32, v3, v2, t1; \ |
|||
VPXOR t0, t1, t0; \ |
|||
VMOVDQU t0, (32+off)(dst); \ |
|||
VMOVDQU (64+off)(src), t0; \ |
|||
VPERM2I128 $49, v1, v0, t1; \ |
|||
VPXOR t0, t1, t0; \ |
|||
VMOVDQU t0, (64+off)(dst); \ |
|||
VMOVDQU (96+off)(src), t0; \ |
|||
VPERM2I128 $49, v3, v2, t1; \ |
|||
VPXOR t0, t1, t0; \ |
|||
VMOVDQU t0, (96+off)(dst) |
|||
|
|||
#define XOR_UPPER_AVX2(dst, src, off, v0, v1, v2, v3, t0, t1) \ |
|||
VMOVDQU (0+off)(src), t0; \ |
|||
VPERM2I128 $32, v1, v0, t1; \ |
|||
VPXOR t0, t1, t0; \ |
|||
VMOVDQU t0, (0+off)(dst); \ |
|||
VMOVDQU (32+off)(src), t0; \ |
|||
VPERM2I128 $32, v3, v2, t1; \ |
|||
VPXOR t0, t1, t0; \ |
|||
VMOVDQU t0, (32+off)(dst); \ |
|||
|
|||
#define EXTRACT_LOWER(dst, v0, v1, v2, v3, t0) \ |
|||
VPERM2I128 $49, v1, v0, t0; \ |
|||
VMOVDQU t0, 0(dst); \ |
|||
VPERM2I128 $49, v3, v2, t0; \ |
|||
VMOVDQU t0, 32(dst) |
|||
@ -0,0 +1,41 @@ |
|||
// Copyright (c) 2016 Andreas Auernhammer. All rights reserved.
|
|||
// Use of this source code is governed by a license that can be
|
|||
// found in the LICENSE file.
|
|||
|
|||
// Package chacha20 implements the ChaCha20 / XChaCha20 stream chipher.
|
|||
// Notice that one specific key-nonce combination must be unique for all time.
|
|||
//
|
|||
// There are three versions of ChaCha20:
|
|||
// - ChaCha20 with a 64 bit nonce (en/decrypt up to 2^64 * 64 bytes for one key-nonce combination)
|
|||
// - ChaCha20 with a 96 bit nonce (en/decrypt up to 2^32 * 64 bytes (~256 GB) for one key-nonce combination)
|
|||
// - XChaCha20 with a 192 bit nonce (en/decrypt up to 2^64 * 64 bytes for one key-nonce combination)
|
|||
package chacha20 // import "github.com/aead/chacha20"
|
|||
|
|||
import ( |
|||
"crypto/cipher" |
|||
|
|||
"github.com/aead/chacha20/chacha" |
|||
) |
|||
|
|||
// XORKeyStream crypts bytes from src to dst using the given nonce and key.
|
|||
// The length of the nonce determinds the version of ChaCha20:
|
|||
// - 8 bytes: ChaCha20 with a 64 bit nonce and a 2^64 * 64 byte period.
|
|||
// - 12 bytes: ChaCha20 as defined in RFC 7539 and a 2^32 * 64 byte period.
|
|||
// - 24 bytes: XChaCha20 with a 192 bit nonce and a 2^64 * 64 byte period.
|
|||
// Src and dst may be the same slice but otherwise should not overlap.
|
|||
// If len(dst) < len(src) this function panics.
|
|||
// If the nonce is neither 64, 96 nor 192 bits long, this function panics.
|
|||
func XORKeyStream(dst, src, nonce, key []byte) { |
|||
chacha.XORKeyStream(dst, src, nonce, key, 20) |
|||
} |
|||
|
|||
// NewCipher returns a new cipher.Stream implementing a ChaCha20 version.
|
|||
// The nonce must be unique for one key for all time.
|
|||
// The length of the nonce determinds the version of ChaCha20:
|
|||
// - 8 bytes: ChaCha20 with a 64 bit nonce and a 2^64 * 64 byte period.
|
|||
// - 12 bytes: ChaCha20 as defined in RFC 7539 and a 2^32 * 64 byte period.
|
|||
// - 24 bytes: XChaCha20 with a 192 bit nonce and a 2^64 * 64 byte period.
|
|||
// If the nonce is neither 64, 96 nor 192 bits long, a non-nil error is returned.
|
|||
func NewCipher(nonce, key []byte) (cipher.Stream, error) { |
|||
return chacha.NewCipher(nonce, key, 20) |
|||
} |
|||
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -0,0 +1,202 @@ |
|||
|
|||
Apache License |
|||
Version 2.0, January 2004 |
|||
http://www.apache.org/licenses/ |
|||
|
|||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION |
|||
|
|||
1. Definitions. |
|||
|
|||
"License" shall mean the terms and conditions for use, reproduction, |
|||
and distribution as defined by Sections 1 through 9 of this document. |
|||
|
|||
"Licensor" shall mean the copyright owner or entity authorized by |
|||
the copyright owner that is granting the License. |
|||
|
|||
"Legal Entity" shall mean the union of the acting entity and all |
|||
other entities that control, are controlled by, or are under common |
|||
control with that entity. For the purposes of this definition, |
|||
"control" means (i) the power, direct or indirect, to cause the |
|||
direction or management of such entity, whether by contract or |
|||
otherwise, or (ii) ownership of fifty percent (50%) or more of the |
|||
outstanding shares, or (iii) beneficial ownership of such entity. |
|||
|
|||
"You" (or "Your") shall mean an individual or Legal Entity |
|||
exercising permissions granted by this License. |
|||
|
|||
"Source" form shall mean the preferred form for making modifications, |
|||
including but not limited to software source code, documentation |
|||
source, and configuration files. |
|||
|
|||
"Object" form shall mean any form resulting from mechanical |
|||
transformation or translation of a Source form, including but |
|||
not limited to compiled object code, generated documentation, |
|||
and conversions to other media types. |
|||
|
|||
"Work" shall mean the work of authorship, whether in Source or |
|||
Object form, made available under the License, as indicated by a |
|||
copyright notice that is included in or attached to the work |
|||
(an example is provided in the Appendix below). |
|||
|
|||
"Derivative Works" shall mean any work, whether in Source or Object |
|||
form, that is based on (or derived from) the Work and for which the |
|||
editorial revisions, annotations, elaborations, or other modifications |
|||
represent, as a whole, an original work of authorship. For the purposes |
|||
of this License, Derivative Works shall not include works that remain |
|||
separable from, or merely link (or bind by name) to the interfaces of, |
|||
the Work and Derivative Works thereof. |
|||
|
|||
"Contribution" shall mean any work of authorship, including |
|||
the original version of the Work and any modifications or additions |
|||
to that Work or Derivative Works thereof, that is intentionally |
|||
submitted to Licensor for inclusion in the Work by the copyright owner |
|||
or by an individual or Legal Entity authorized to submit on behalf of |
|||
the copyright owner. For the purposes of this definition, "submitted" |
|||
means any form of electronic, verbal, or written communication sent |
|||
to the Licensor or its representatives, including but not limited to |
|||
communication on electronic mailing lists, source code control systems, |
|||
and issue tracking systems that are managed by, or on behalf of, the |
|||
Licensor for the purpose of discussing and improving the Work, but |
|||
excluding communication that is conspicuously marked or otherwise |
|||
designated in writing by the copyright owner as "Not a Contribution." |
|||
|
|||
"Contributor" shall mean Licensor and any individual or Legal Entity |
|||
on behalf of whom a Contribution has been received by Licensor and |
|||
subsequently incorporated within the Work. |
|||
|
|||
2. Grant of Copyright License. Subject to the terms and conditions of |
|||
this License, each Contributor hereby grants to You a perpetual, |
|||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable |
|||
copyright license to reproduce, prepare Derivative Works of, |
|||
publicly display, publicly perform, sublicense, and distribute the |
|||
Work and such Derivative Works in Source or Object form. |
|||
|
|||
3. Grant of Patent License. Subject to the terms and conditions of |
|||
this License, each Contributor hereby grants to You a perpetual, |
|||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable |
|||
(except as stated in this section) patent license to make, have made, |
|||
use, offer to sell, sell, import, and otherwise transfer the Work, |
|||
where such license applies only to those patent claims licensable |
|||
by such Contributor that are necessarily infringed by their |
|||
Contribution(s) alone or by combination of their Contribution(s) |
|||
with the Work to which such Contribution(s) was submitted. If You |
|||
institute patent litigation against any entity (including a |
|||
cross-claim or counterclaim in a lawsuit) alleging that the Work |
|||
or a Contribution incorporated within the Work constitutes direct |
|||
or contributory patent infringement, then any patent licenses |
|||
granted to You under this License for that Work shall terminate |
|||
as of the date such litigation is filed. |
|||
|
|||
4. Redistribution. You may reproduce and distribute copies of the |
|||
Work or Derivative Works thereof in any medium, with or without |
|||
modifications, and in Source or Object form, provided that You |
|||
meet the following conditions: |
|||
|
|||
(a) You must give any other recipients of the Work or |
|||
Derivative Works a copy of this License; and |
|||
|
|||
(b) You must cause any modified files to carry prominent notices |
|||
stating that You changed the files; and |
|||
|
|||
(c) You must retain, in the Source form of any Derivative Works |
|||
that You distribute, all copyright, patent, trademark, and |
|||
attribution notices from the Source form of the Work, |
|||
excluding those notices that do not pertain to any part of |
|||
the Derivative Works; and |
|||
|
|||
(d) If the Work includes a "NOTICE" text file as part of its |
|||
distribution, then any Derivative Works that You distribute must |
|||
include a readable copy of the attribution notices contained |
|||
within such NOTICE file, excluding those notices that do not |
|||
pertain to any part of the Derivative Works, in at least one |
|||
of the following places: within a NOTICE text file distributed |
|||
as part of the Derivative Works; within the Source form or |
|||
documentation, if provided along with the Derivative Works; or, |
|||
within a display generated by the Derivative Works, if and |
|||
wherever such third-party notices normally appear. The contents |
|||
of the NOTICE file are for informational purposes only and |
|||
do not modify the License. You may add Your own attribution |
|||
notices within Derivative Works that You distribute, alongside |
|||
or as an addendum to the NOTICE text from the Work, provided |
|||
that such additional attribution notices cannot be construed |
|||
as modifying the License. |
|||
|
|||
You may add Your own copyright statement to Your modifications and |
|||
may provide additional or different license terms and conditions |
|||
for use, reproduction, or distribution of Your modifications, or |
|||
for any such Derivative Works as a whole, provided Your use, |
|||
reproduction, and distribution of the Work otherwise complies with |
|||
the conditions stated in this License. |
|||
|
|||
5. Submission of Contributions. Unless You explicitly state otherwise, |
|||
any Contribution intentionally submitted for inclusion in the Work |
|||
by You to the Licensor shall be under the terms and conditions of |
|||
this License, without any additional terms or conditions. |
|||
Notwithstanding the above, nothing herein shall supersede or modify |
|||
the terms of any separate license agreement you may have executed |
|||
with Licensor regarding such Contributions. |
|||
|
|||
6. Trademarks. This License does not grant permission to use the trade |
|||
names, trademarks, service marks, or product names of the Licensor, |
|||
except as required for reasonable and customary use in describing the |
|||
origin of the Work and reproducing the content of the NOTICE file. |
|||
|
|||
7. Disclaimer of Warranty. Unless required by applicable law or |
|||
agreed to in writing, Licensor provides the Work (and each |
|||
Contributor provides its Contributions) on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
|||
implied, including, without limitation, any warranties or conditions |
|||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A |
|||
PARTICULAR PURPOSE. You are solely responsible for determining the |
|||
appropriateness of using or redistributing the Work and assume any |
|||
risks associated with Your exercise of permissions under this License. |
|||
|
|||
8. Limitation of Liability. In no event and under no legal theory, |
|||
whether in tort (including negligence), contract, or otherwise, |
|||
unless required by applicable law (such as deliberate and grossly |
|||
negligent acts) or agreed to in writing, shall any Contributor be |
|||
liable to You for damages, including any direct, indirect, special, |
|||
incidental, or consequential damages of any character arising as a |
|||
result of this License or out of the use or inability to use the |
|||
Work (including but not limited to damages for loss of goodwill, |
|||
work stoppage, computer failure or malfunction, or any and all |
|||
other commercial damages or losses), even if such Contributor |
|||
has been advised of the possibility of such damages. |
|||
|
|||
9. Accepting Warranty or Additional Liability. While redistributing |
|||
the Work or Derivative Works thereof, You may choose to offer, |
|||
and charge a fee for, acceptance of support, warranty, indemnity, |
|||
or other liability obligations and/or rights consistent with this |
|||
License. However, in accepting such obligations, You may act only |
|||
on Your own behalf and on Your sole responsibility, not on behalf |
|||
of any other Contributor, and only if You agree to indemnify, |
|||
defend, and hold each Contributor harmless for any liability |
|||
incurred by, or claims asserted against, such Contributor by reason |
|||
of your accepting any such warranty or additional liability. |
|||
|
|||
END OF TERMS AND CONDITIONS |
|||
|
|||
APPENDIX: How to apply the Apache License to your work. |
|||
|
|||
To apply the Apache License to your work, attach the following |
|||
boilerplate notice, with the fields enclosed by brackets "[]" |
|||
replaced with your own identifying information. (Don't include |
|||
the brackets!) The text should be enclosed in the appropriate |
|||
comment syntax for the file format. We also recommend that a |
|||
file or class name and description of purpose be included on the |
|||
same "printed page" as the copyright notice for easier |
|||
identification within third-party archives. |
|||
|
|||
Copyright [yyyy] [name of copyright owner] |
|||
|
|||
Licensed under the Apache License, Version 2.0 (the "License"); |
|||
you may not use this file except in compliance with the License. |
|||
You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
@ -0,0 +1,144 @@ |
|||
package core |
|||
|
|||
import ( |
|||
"crypto/md5" |
|||
"errors" |
|||
"net" |
|||
"sort" |
|||
"strings" |
|||
|
|||
"github.com/shadowsocks/go-shadowsocks2/shadowaead" |
|||
"github.com/shadowsocks/go-shadowsocks2/shadowstream" |
|||
) |
|||
|
|||
type Cipher interface { |
|||
StreamConnCipher |
|||
PacketConnCipher |
|||
} |
|||
|
|||
type StreamConnCipher interface { |
|||
StreamConn(net.Conn) net.Conn |
|||
} |
|||
|
|||
type PacketConnCipher interface { |
|||
PacketConn(net.PacketConn) net.PacketConn |
|||
} |
|||
|
|||
// ErrCipherNotSupported occurs when a cipher is not supported (likely because of security concerns).
|
|||
var ErrCipherNotSupported = errors.New("cipher not supported") |
|||
|
|||
// List of AEAD ciphers: key size in bytes and constructor
|
|||
var aeadList = map[string]struct { |
|||
KeySize int |
|||
New func([]byte) (shadowaead.Cipher, error) |
|||
}{ |
|||
"AEAD_AES_128_GCM": {16, shadowaead.AESGCM}, |
|||
"AEAD_AES_192_GCM": {24, shadowaead.AESGCM}, |
|||
"AEAD_AES_256_GCM": {32, shadowaead.AESGCM}, |
|||
"AEAD_CHACHA20_POLY1305": {32, shadowaead.Chacha20Poly1305}, |
|||
} |
|||
|
|||
// List of stream ciphers: key size in bytes and constructor
|
|||
var streamList = map[string]struct { |
|||
KeySize int |
|||
New func(key []byte) (shadowstream.Cipher, error) |
|||
}{ |
|||
"AES-128-CTR": {16, shadowstream.AESCTR}, |
|||
"AES-192-CTR": {24, shadowstream.AESCTR}, |
|||
"AES-256-CTR": {32, shadowstream.AESCTR}, |
|||
"AES-128-CFB": {16, shadowstream.AESCFB}, |
|||
"AES-192-CFB": {24, shadowstream.AESCFB}, |
|||
"AES-256-CFB": {32, shadowstream.AESCFB}, |
|||
"CHACHA20-IETF": {32, shadowstream.Chacha20IETF}, |
|||
"XCHACHA20": {32, shadowstream.Xchacha20}, |
|||
} |
|||
|
|||
// ListCipher returns a list of available cipher names sorted alphabetically.
|
|||
func ListCipher() []string { |
|||
var l []string |
|||
for k := range aeadList { |
|||
l = append(l, k) |
|||
} |
|||
for k := range streamList { |
|||
l = append(l, k) |
|||
} |
|||
sort.Strings(l) |
|||
return l |
|||
} |
|||
|
|||
// PickCipher returns a Cipher of the given name. Derive key from password if given key is empty.
|
|||
func PickCipher(name string, key []byte, password string) (Cipher, error) { |
|||
name = strings.ToUpper(name) |
|||
|
|||
switch name { |
|||
case "DUMMY": |
|||
return &dummy{}, nil |
|||
case "CHACHA20-IETF-POLY1305": |
|||
name = "AEAD_CHACHA20_POLY1305" |
|||
case "AES-128-GCM": |
|||
name = "AEAD_AES_128_GCM" |
|||
case "AES-196-GCM": |
|||
name = "AEAD_AES_196_GCM" |
|||
case "AES-256-GCM": |
|||
name = "AEAD_AES_256_GCM" |
|||
} |
|||
|
|||
if choice, ok := aeadList[name]; ok { |
|||
if len(key) == 0 { |
|||
key = kdf(password, choice.KeySize) |
|||
} |
|||
if len(key) != choice.KeySize { |
|||
return nil, shadowaead.KeySizeError(choice.KeySize) |
|||
} |
|||
aead, err := choice.New(key) |
|||
return &aeadCipher{aead}, err |
|||
} |
|||
|
|||
if choice, ok := streamList[name]; ok { |
|||
if len(key) == 0 { |
|||
key = kdf(password, choice.KeySize) |
|||
} |
|||
if len(key) != choice.KeySize { |
|||
return nil, shadowstream.KeySizeError(choice.KeySize) |
|||
} |
|||
ciph, err := choice.New(key) |
|||
return &streamCipher{ciph}, err |
|||
} |
|||
|
|||
return nil, ErrCipherNotSupported |
|||
} |
|||
|
|||
type aeadCipher struct{ shadowaead.Cipher } |
|||
|
|||
func (aead *aeadCipher) StreamConn(c net.Conn) net.Conn { return shadowaead.NewConn(c, aead) } |
|||
func (aead *aeadCipher) PacketConn(c net.PacketConn) net.PacketConn { |
|||
return shadowaead.NewPacketConn(c, aead) |
|||
} |
|||
|
|||
type streamCipher struct{ shadowstream.Cipher } |
|||
|
|||
func (ciph *streamCipher) StreamConn(c net.Conn) net.Conn { return shadowstream.NewConn(c, ciph) } |
|||
func (ciph *streamCipher) PacketConn(c net.PacketConn) net.PacketConn { |
|||
return shadowstream.NewPacketConn(c, ciph) |
|||
} |
|||
|
|||
// dummy cipher does not encrypt
|
|||
|
|||
type dummy struct{} |
|||
|
|||
func (dummy) StreamConn(c net.Conn) net.Conn { return c } |
|||
func (dummy) PacketConn(c net.PacketConn) net.PacketConn { return c } |
|||
|
|||
// key-derivation function from original Shadowsocks
|
|||
func kdf(password string, keyLen int) []byte { |
|||
var b, prev []byte |
|||
h := md5.New() |
|||
for len(b) < keyLen { |
|||
h.Write(prev) |
|||
h.Write([]byte(password)) |
|||
b = h.Sum(b) |
|||
prev = b[len(b)-h.Size():] |
|||
h.Reset() |
|||
} |
|||
return b[:keyLen] |
|||
} |
|||
@ -0,0 +1,2 @@ |
|||
// Package core implements essential parts of Shadowsocks
|
|||
package core |
|||
@ -0,0 +1,8 @@ |
|||
package core |
|||
|
|||
import "net" |
|||
|
|||
func ListenPacket(network, address string, ciph PacketConnCipher) (net.PacketConn, error) { |
|||
c, err := net.ListenPacket(network, address) |
|||
return ciph.PacketConn(c), err |
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
package core |
|||
|
|||
import "net" |
|||
|
|||
type listener struct { |
|||
net.Listener |
|||
StreamConnCipher |
|||
} |
|||
|
|||
func Listen(network, address string, ciph StreamConnCipher) (net.Listener, error) { |
|||
l, err := net.Listen(network, address) |
|||
return &listener{l, ciph}, err |
|||
} |
|||
|
|||
func (l *listener) Accept() (net.Conn, error) { |
|||
c, err := l.Listener.Accept() |
|||
return l.StreamConn(c), err |
|||
} |
|||
|
|||
func Dial(network, address string, ciph StreamConnCipher) (net.Conn, error) { |
|||
c, err := net.Dial(network, address) |
|||
return ciph.StreamConn(c), err |
|||
} |
|||
@ -0,0 +1,83 @@ |
|||
package shadowaead |
|||
|
|||
import ( |
|||
"crypto/aes" |
|||
"crypto/cipher" |
|||
"crypto/sha1" |
|||
"io" |
|||
"strconv" |
|||
|
|||
"golang.org/x/crypto/chacha20poly1305" |
|||
"golang.org/x/crypto/hkdf" |
|||
) |
|||
|
|||
type Cipher interface { |
|||
KeySize() int |
|||
SaltSize() int |
|||
Encrypter(salt []byte) (cipher.AEAD, error) |
|||
Decrypter(salt []byte) (cipher.AEAD, error) |
|||
} |
|||
|
|||
type KeySizeError int |
|||
|
|||
func (e KeySizeError) Error() string { |
|||
return "key size error: need " + strconv.Itoa(int(e)) + " bytes" |
|||
} |
|||
|
|||
func hkdfSHA1(secret, salt, info, outkey []byte) { |
|||
r := hkdf.New(sha1.New, secret, salt, info) |
|||
if _, err := io.ReadFull(r, outkey); err != nil { |
|||
panic(err) // should never happen
|
|||
} |
|||
} |
|||
|
|||
type metaCipher struct { |
|||
psk []byte |
|||
makeAEAD func(key []byte) (cipher.AEAD, error) |
|||
} |
|||
|
|||
func (a *metaCipher) KeySize() int { return len(a.psk) } |
|||
func (a *metaCipher) SaltSize() int { |
|||
if ks := a.KeySize(); ks > 16 { |
|||
return ks |
|||
} |
|||
return 16 |
|||
} |
|||
func (a *metaCipher) Encrypter(salt []byte) (cipher.AEAD, error) { |
|||
subkey := make([]byte, a.KeySize()) |
|||
hkdfSHA1(a.psk, salt, []byte("ss-subkey"), subkey) |
|||
return a.makeAEAD(subkey) |
|||
} |
|||
func (a *metaCipher) Decrypter(salt []byte) (cipher.AEAD, error) { |
|||
subkey := make([]byte, a.KeySize()) |
|||
hkdfSHA1(a.psk, salt, []byte("ss-subkey"), subkey) |
|||
return a.makeAEAD(subkey) |
|||
} |
|||
|
|||
func aesGCM(key []byte) (cipher.AEAD, error) { |
|||
blk, err := aes.NewCipher(key) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
return cipher.NewGCM(blk) |
|||
} |
|||
|
|||
// AESGCM creates a new Cipher with a pre-shared key. len(psk) must be
|
|||
// one of 16, 24, or 32 to select AES-128/196/256-GCM.
|
|||
func AESGCM(psk []byte) (Cipher, error) { |
|||
switch l := len(psk); l { |
|||
case 16, 24, 32: // AES 128/196/256
|
|||
default: |
|||
return nil, aes.KeySizeError(l) |
|||
} |
|||
return &metaCipher{psk: psk, makeAEAD: aesGCM}, nil |
|||
} |
|||
|
|||
// Chacha20Poly1305 creates a new Cipher with a pre-shared key. len(psk)
|
|||
// must be 32.
|
|||
func Chacha20Poly1305(psk []byte) (Cipher, error) { |
|||
if len(psk) != chacha20poly1305.KeySize { |
|||
return nil, KeySizeError(chacha20poly1305.KeySize) |
|||
} |
|||
return &metaCipher{psk: psk, makeAEAD: chacha20poly1305.New}, nil |
|||
} |
|||
@ -0,0 +1,35 @@ |
|||
/* |
|||
Package shadowaead implements a simple AEAD-protected secure protocol. |
|||
|
|||
In general, there are two types of connections: stream-oriented and packet-oriented. |
|||
Stream-oriented connections (e.g. TCP) assume reliable and orderly delivery of bytes. |
|||
Packet-oriented connections (e.g. UDP) assume unreliable and out-of-order delivery of packets, |
|||
where each packet is either delivered intact or lost. |
|||
|
|||
An encrypted stream starts with a random salt to derive a session key, followed by any number of |
|||
encrypted records. Each encrypted record has the following structure: |
|||
|
|||
[encrypted payload length] |
|||
[payload length tag] |
|||
[encrypted payload] |
|||
[payload tag] |
|||
|
|||
Payload length is 2-byte unsigned big-endian integer capped at 0x3FFF (16383). |
|||
The higher 2 bits are reserved and must be set to zero. The first AEAD encrypt/decrypt |
|||
operation uses a counting nonce starting from 0. After each encrypt/decrypt operation, |
|||
the nonce is incremented by one as if it were an unsigned little-endian integer. |
|||
|
|||
|
|||
Each encrypted packet transmitted on a packet-oriented connection has the following structure: |
|||
|
|||
[random salt] |
|||
[encrypted payload] |
|||
[payload tag] |
|||
|
|||
The salt is used to derive a subkey to initiate an AEAD. Packets are encrypted/decrypted independently |
|||
using zero nonce. |
|||
|
|||
In both stream-oriented and packet-oriented connections, length of nonce and tag varies |
|||
depending on which AEAD is used. Salt should be at least 16-byte long. |
|||
*/ |
|||
package shadowaead |
|||
@ -0,0 +1,97 @@ |
|||
package shadowaead |
|||
|
|||
import ( |
|||
"crypto/rand" |
|||
"errors" |
|||
"io" |
|||
"net" |
|||
"sync" |
|||
) |
|||
|
|||
// ErrShortPacket means that the packet is too short for a valid encrypted packet.
|
|||
var ErrShortPacket = errors.New("short packet") |
|||
|
|||
var _zerononce [128]byte // read-only. 128 bytes is more than enough.
|
|||
|
|||
// Pack encrypts plaintext using Cipher with a randomly generated salt and
|
|||
// returns a slice of dst containing the encrypted packet and any error occurred.
|
|||
// Ensure len(dst) >= ciph.SaltSize() + len(plaintext) + aead.Overhead().
|
|||
func Pack(dst, plaintext []byte, ciph Cipher) ([]byte, error) { |
|||
saltSize := ciph.SaltSize() |
|||
salt := dst[:saltSize] |
|||
if _, err := io.ReadFull(rand.Reader, salt); err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
aead, err := ciph.Encrypter(salt) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
if len(dst) < saltSize+len(plaintext)+aead.Overhead() { |
|||
return nil, io.ErrShortBuffer |
|||
} |
|||
b := aead.Seal(dst[saltSize:saltSize], _zerononce[:aead.NonceSize()], plaintext, nil) |
|||
return dst[:saltSize+len(b)], nil |
|||
} |
|||
|
|||
// Unpack decrypts pkt using Cipher and returns a slice of dst containing the decrypted payload and any error occurred.
|
|||
// Ensure len(dst) >= len(pkt) - aead.SaltSize() - aead.Overhead().
|
|||
func Unpack(dst, pkt []byte, ciph Cipher) ([]byte, error) { |
|||
saltSize := ciph.SaltSize() |
|||
if len(pkt) < saltSize { |
|||
return nil, ErrShortPacket |
|||
} |
|||
salt := pkt[:saltSize] |
|||
aead, err := ciph.Decrypter(salt) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
if len(pkt) < saltSize+aead.Overhead() { |
|||
return nil, ErrShortPacket |
|||
} |
|||
if saltSize+len(dst)+aead.Overhead() < len(pkt) { |
|||
return nil, io.ErrShortBuffer |
|||
} |
|||
b, err := aead.Open(dst[:0], _zerononce[:aead.NonceSize()], pkt[saltSize:], nil) |
|||
return b, err |
|||
} |
|||
|
|||
type packetConn struct { |
|||
net.PacketConn |
|||
Cipher |
|||
sync.Mutex |
|||
buf []byte // write lock
|
|||
} |
|||
|
|||
// NewPacketConn wraps a net.PacketConn with cipher
|
|||
func NewPacketConn(c net.PacketConn, ciph Cipher) net.PacketConn { |
|||
const maxPacketSize = 64 * 1024 |
|||
return &packetConn{PacketConn: c, Cipher: ciph, buf: make([]byte, maxPacketSize)} |
|||
} |
|||
|
|||
// WriteTo encrypts b and write to addr using the embedded PacketConn.
|
|||
func (c *packetConn) WriteTo(b []byte, addr net.Addr) (int, error) { |
|||
c.Lock() |
|||
defer c.Unlock() |
|||
buf, err := Pack(c.buf, b, c) |
|||
if err != nil { |
|||
return 0, err |
|||
} |
|||
_, err = c.PacketConn.WriteTo(buf, addr) |
|||
return len(b), err |
|||
} |
|||
|
|||
// ReadFrom reads from the embedded PacketConn and decrypts into b.
|
|||
func (c *packetConn) ReadFrom(b []byte) (int, net.Addr, error) { |
|||
n, addr, err := c.PacketConn.ReadFrom(b) |
|||
if err != nil { |
|||
return n, addr, err |
|||
} |
|||
bb, err := Unpack(b[c.Cipher.SaltSize():], b[:n], c) |
|||
if err != nil { |
|||
return n, addr, err |
|||
} |
|||
copy(b, bb) |
|||
return len(bb), addr, err |
|||
} |
|||
@ -0,0 +1,270 @@ |
|||
package shadowaead |
|||
|
|||
import ( |
|||
"bytes" |
|||
"crypto/cipher" |
|||
"crypto/rand" |
|||
"io" |
|||
"net" |
|||
) |
|||
|
|||
// payloadSizeMask is the maximum size of payload in bytes.
|
|||
const payloadSizeMask = 0x3FFF // 16*1024 - 1
|
|||
|
|||
type writer struct { |
|||
io.Writer |
|||
cipher.AEAD |
|||
nonce []byte |
|||
buf []byte |
|||
} |
|||
|
|||
// NewWriter wraps an io.Writer with AEAD encryption.
|
|||
func NewWriter(w io.Writer, aead cipher.AEAD) io.Writer { return newWriter(w, aead) } |
|||
|
|||
func newWriter(w io.Writer, aead cipher.AEAD) *writer { |
|||
return &writer{ |
|||
Writer: w, |
|||
AEAD: aead, |
|||
buf: make([]byte, 2+aead.Overhead()+payloadSizeMask+aead.Overhead()), |
|||
nonce: make([]byte, aead.NonceSize()), |
|||
} |
|||
} |
|||
|
|||
// Write encrypts b and writes to the embedded io.Writer.
|
|||
func (w *writer) Write(b []byte) (int, error) { |
|||
n, err := w.ReadFrom(bytes.NewBuffer(b)) |
|||
return int(n), err |
|||
} |
|||
|
|||
// ReadFrom reads from the given io.Reader until EOF or error, encrypts and
|
|||
// writes to the embedded io.Writer. Returns number of bytes read from r and
|
|||
// any error encountered.
|
|||
func (w *writer) ReadFrom(r io.Reader) (n int64, err error) { |
|||
for { |
|||
buf := w.buf |
|||
payloadBuf := buf[2+w.Overhead() : 2+w.Overhead()+payloadSizeMask] |
|||
nr, er := r.Read(payloadBuf) |
|||
|
|||
if nr > 0 { |
|||
n += int64(nr) |
|||
buf = buf[:2+w.Overhead()+nr+w.Overhead()] |
|||
payloadBuf = payloadBuf[:nr] |
|||
buf[0], buf[1] = byte(nr>>8), byte(nr) // big-endian payload size
|
|||
w.Seal(buf[:0], w.nonce, buf[:2], nil) |
|||
increment(w.nonce) |
|||
|
|||
w.Seal(payloadBuf[:0], w.nonce, payloadBuf, nil) |
|||
increment(w.nonce) |
|||
|
|||
_, ew := w.Writer.Write(buf) |
|||
if ew != nil { |
|||
err = ew |
|||
break |
|||
} |
|||
} |
|||
|
|||
if er != nil { |
|||
if er != io.EOF { // ignore EOF as per io.ReaderFrom contract
|
|||
err = er |
|||
} |
|||
break |
|||
} |
|||
} |
|||
|
|||
return n, err |
|||
} |
|||
|
|||
type reader struct { |
|||
io.Reader |
|||
cipher.AEAD |
|||
nonce []byte |
|||
buf []byte |
|||
leftover []byte |
|||
} |
|||
|
|||
// NewReader wraps an io.Reader with AEAD decryption.
|
|||
func NewReader(r io.Reader, aead cipher.AEAD) io.Reader { return newReader(r, aead) } |
|||
|
|||
func newReader(r io.Reader, aead cipher.AEAD) *reader { |
|||
return &reader{ |
|||
Reader: r, |
|||
AEAD: aead, |
|||
buf: make([]byte, payloadSizeMask+aead.Overhead()), |
|||
nonce: make([]byte, aead.NonceSize()), |
|||
} |
|||
} |
|||
|
|||
// read and decrypt a record into the internal buffer. Return decrypted payload length and any error encountered.
|
|||
func (r *reader) read() (int, error) { |
|||
// decrypt payload size
|
|||
buf := r.buf[:2+r.Overhead()] |
|||
_, err := io.ReadFull(r.Reader, buf) |
|||
if err != nil { |
|||
return 0, err |
|||
} |
|||
|
|||
_, err = r.Open(buf[:0], r.nonce, buf, nil) |
|||
increment(r.nonce) |
|||
if err != nil { |
|||
return 0, err |
|||
} |
|||
|
|||
size := (int(buf[0])<<8 + int(buf[1])) & payloadSizeMask |
|||
|
|||
// decrypt payload
|
|||
buf = r.buf[:size+r.Overhead()] |
|||
_, err = io.ReadFull(r.Reader, buf) |
|||
if err != nil { |
|||
return 0, err |
|||
} |
|||
|
|||
_, err = r.Open(buf[:0], r.nonce, buf, nil) |
|||
increment(r.nonce) |
|||
if err != nil { |
|||
return 0, err |
|||
} |
|||
|
|||
return size, nil |
|||
} |
|||
|
|||
// Read reads from the embedded io.Reader, decrypts and writes to b.
|
|||
func (r *reader) Read(b []byte) (int, error) { |
|||
// copy decrypted bytes (if any) from previous record first
|
|||
if len(r.leftover) > 0 { |
|||
n := copy(b, r.leftover) |
|||
r.leftover = r.leftover[n:] |
|||
return n, nil |
|||
} |
|||
|
|||
n, err := r.read() |
|||
m := copy(b, r.buf[:n]) |
|||
if m < n { // insufficient len(b), keep leftover for next read
|
|||
r.leftover = r.buf[m:n] |
|||
} |
|||
return m, err |
|||
} |
|||
|
|||
// WriteTo reads from the embedded io.Reader, decrypts and writes to w until
|
|||
// there's no more data to write or when an error occurs. Return number of
|
|||
// bytes written to w and any error encountered.
|
|||
func (r *reader) WriteTo(w io.Writer) (n int64, err error) { |
|||
// write decrypted bytes left over from previous record
|
|||
for len(r.leftover) > 0 { |
|||
nw, ew := w.Write(r.leftover) |
|||
r.leftover = r.leftover[nw:] |
|||
n += int64(nw) |
|||
if ew != nil { |
|||
return n, ew |
|||
} |
|||
} |
|||
|
|||
for { |
|||
nr, er := r.read() |
|||
if nr > 0 { |
|||
nw, ew := w.Write(r.buf[:nr]) |
|||
n += int64(nw) |
|||
|
|||
if ew != nil { |
|||
err = ew |
|||
break |
|||
} |
|||
} |
|||
|
|||
if er != nil { |
|||
if er != io.EOF { // ignore EOF as per io.Copy contract (using src.WriteTo shortcut)
|
|||
err = er |
|||
} |
|||
break |
|||
} |
|||
} |
|||
|
|||
return n, err |
|||
} |
|||
|
|||
// increment little-endian encoded unsigned integer b. Wrap around on overflow.
|
|||
func increment(b []byte) { |
|||
for i := range b { |
|||
b[i]++ |
|||
if b[i] != 0 { |
|||
return |
|||
} |
|||
} |
|||
} |
|||
|
|||
type streamConn struct { |
|||
net.Conn |
|||
Cipher |
|||
r *reader |
|||
w *writer |
|||
} |
|||
|
|||
func (c *streamConn) initReader() error { |
|||
salt := make([]byte, c.SaltSize()) |
|||
if _, err := io.ReadFull(c.Conn, salt); err != nil { |
|||
return err |
|||
} |
|||
|
|||
aead, err := c.Decrypter(salt) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
|
|||
c.r = newReader(c.Conn, aead) |
|||
return nil |
|||
} |
|||
|
|||
func (c *streamConn) Read(b []byte) (int, error) { |
|||
if c.r == nil { |
|||
if err := c.initReader(); err != nil { |
|||
return 0, err |
|||
} |
|||
} |
|||
return c.r.Read(b) |
|||
} |
|||
|
|||
func (c *streamConn) WriteTo(w io.Writer) (int64, error) { |
|||
if c.r == nil { |
|||
if err := c.initReader(); err != nil { |
|||
return 0, err |
|||
} |
|||
} |
|||
return c.r.WriteTo(w) |
|||
} |
|||
|
|||
func (c *streamConn) initWriter() error { |
|||
salt := make([]byte, c.SaltSize()) |
|||
if _, err := io.ReadFull(rand.Reader, salt); err != nil { |
|||
return err |
|||
} |
|||
aead, err := c.Encrypter(salt) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
_, err = c.Conn.Write(salt) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
c.w = newWriter(c.Conn, aead) |
|||
return nil |
|||
} |
|||
|
|||
func (c *streamConn) Write(b []byte) (int, error) { |
|||
if c.w == nil { |
|||
if err := c.initWriter(); err != nil { |
|||
return 0, err |
|||
} |
|||
} |
|||
return c.w.Write(b) |
|||
} |
|||
|
|||
func (c *streamConn) ReadFrom(r io.Reader) (int64, error) { |
|||
if c.w == nil { |
|||
if err := c.initWriter(); err != nil { |
|||
return 0, err |
|||
} |
|||
} |
|||
return c.w.ReadFrom(r) |
|||
} |
|||
|
|||
// NewConn wraps a stream-oriented net.Conn with cipher.
|
|||
func NewConn(c net.Conn, ciph Cipher) net.Conn { return &streamConn{Conn: c, Cipher: ciph} } |
|||
@ -0,0 +1,92 @@ |
|||
package shadowstream |
|||
|
|||
import ( |
|||
"crypto/aes" |
|||
"crypto/cipher" |
|||
"strconv" |
|||
|
|||
"github.com/aead/chacha20" |
|||
"github.com/aead/chacha20/chacha" |
|||
) |
|||
|
|||
// Cipher generates a pair of stream ciphers for encryption and decryption.
|
|||
type Cipher interface { |
|||
IVSize() int |
|||
Encrypter(iv []byte) cipher.Stream |
|||
Decrypter(iv []byte) cipher.Stream |
|||
} |
|||
|
|||
type KeySizeError int |
|||
|
|||
func (e KeySizeError) Error() string { |
|||
return "key size error: need " + strconv.Itoa(int(e)) + " bytes" |
|||
} |
|||
|
|||
// CTR mode
|
|||
type ctrStream struct{ cipher.Block } |
|||
|
|||
func (b *ctrStream) IVSize() int { return b.BlockSize() } |
|||
func (b *ctrStream) Decrypter(iv []byte) cipher.Stream { return b.Encrypter(iv) } |
|||
func (b *ctrStream) Encrypter(iv []byte) cipher.Stream { return cipher.NewCTR(b, iv) } |
|||
|
|||
func AESCTR(key []byte) (Cipher, error) { |
|||
blk, err := aes.NewCipher(key) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
return &ctrStream{blk}, nil |
|||
} |
|||
|
|||
// CFB mode
|
|||
type cfbStream struct{ cipher.Block } |
|||
|
|||
func (b *cfbStream) IVSize() int { return b.BlockSize() } |
|||
func (b *cfbStream) Decrypter(iv []byte) cipher.Stream { return cipher.NewCFBDecrypter(b, iv) } |
|||
func (b *cfbStream) Encrypter(iv []byte) cipher.Stream { return cipher.NewCFBEncrypter(b, iv) } |
|||
|
|||
func AESCFB(key []byte) (Cipher, error) { |
|||
blk, err := aes.NewCipher(key) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
return &cfbStream{blk}, nil |
|||
} |
|||
|
|||
// IETF-variant of chacha20
|
|||
type chacha20ietfkey []byte |
|||
|
|||
func (k chacha20ietfkey) IVSize() int { return chacha.INonceSize } |
|||
func (k chacha20ietfkey) Decrypter(iv []byte) cipher.Stream { return k.Encrypter(iv) } |
|||
func (k chacha20ietfkey) Encrypter(iv []byte) cipher.Stream { |
|||
ciph, err := chacha20.NewCipher(iv, k) |
|||
if err != nil { |
|||
panic(err) // should never happen
|
|||
} |
|||
return ciph |
|||
} |
|||
|
|||
func Chacha20IETF(key []byte) (Cipher, error) { |
|||
if len(key) != chacha.KeySize { |
|||
return nil, KeySizeError(chacha.KeySize) |
|||
} |
|||
return chacha20ietfkey(key), nil |
|||
} |
|||
|
|||
type xchacha20key []byte |
|||
|
|||
func (k xchacha20key) IVSize() int { return chacha.XNonceSize } |
|||
func (k xchacha20key) Decrypter(iv []byte) cipher.Stream { return k.Encrypter(iv) } |
|||
func (k xchacha20key) Encrypter(iv []byte) cipher.Stream { |
|||
ciph, err := chacha20.NewCipher(iv, k) |
|||
if err != nil { |
|||
panic(err) // should never happen
|
|||
} |
|||
return ciph |
|||
} |
|||
|
|||
func Xchacha20(key []byte) (Cipher, error) { |
|||
if len(key) != chacha.KeySize { |
|||
return nil, KeySizeError(chacha.KeySize) |
|||
} |
|||
return xchacha20key(key), nil |
|||
} |
|||
@ -0,0 +1,2 @@ |
|||
// Package shadowstream implements the original Shadowsocks protocol protected by stream cipher.
|
|||
package shadowstream |
|||
@ -0,0 +1,80 @@ |
|||
package shadowstream |
|||
|
|||
import ( |
|||
"crypto/rand" |
|||
"errors" |
|||
"io" |
|||
"net" |
|||
"sync" |
|||
) |
|||
|
|||
// ErrShortPacket means the packet is too short to be a valid encrypted packet.
|
|||
var ErrShortPacket = errors.New("short packet") |
|||
|
|||
// Pack encrypts plaintext using stream cipher s and a random IV.
|
|||
// Returns a slice of dst containing random IV and ciphertext.
|
|||
// Ensure len(dst) >= s.IVSize() + len(plaintext).
|
|||
func Pack(dst, plaintext []byte, s Cipher) ([]byte, error) { |
|||
if len(dst) < s.IVSize()+len(plaintext) { |
|||
return nil, io.ErrShortBuffer |
|||
} |
|||
iv := dst[:s.IVSize()] |
|||
_, err := io.ReadFull(rand.Reader, iv) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
s.Encrypter(iv).XORKeyStream(dst[len(iv):], plaintext) |
|||
return dst[:len(iv)+len(plaintext)], nil |
|||
} |
|||
|
|||
// Unpack decrypts pkt using stream cipher s.
|
|||
// Returns a slice of dst containing decrypted plaintext.
|
|||
func Unpack(dst, pkt []byte, s Cipher) ([]byte, error) { |
|||
if len(pkt) < s.IVSize() { |
|||
return nil, ErrShortPacket |
|||
} |
|||
|
|||
if len(dst) < len(pkt)-s.IVSize() { |
|||
return nil, io.ErrShortBuffer |
|||
} |
|||
iv := pkt[:s.IVSize()] |
|||
s.Decrypter(iv).XORKeyStream(dst, pkt[len(iv):]) |
|||
return dst[:len(pkt)-len(iv)], nil |
|||
} |
|||
|
|||
type packetConn struct { |
|||
net.PacketConn |
|||
Cipher |
|||
buf []byte |
|||
sync.Mutex // write lock
|
|||
} |
|||
|
|||
// NewPacketConn wraps a net.PacketConn with stream cipher encryption/decryption.
|
|||
func NewPacketConn(c net.PacketConn, ciph Cipher) net.PacketConn { |
|||
return &packetConn{PacketConn: c, Cipher: ciph, buf: make([]byte, 64*1024)} |
|||
} |
|||
|
|||
func (c *packetConn) WriteTo(b []byte, addr net.Addr) (int, error) { |
|||
c.Lock() |
|||
defer c.Unlock() |
|||
buf, err := Pack(c.buf, b, c.Cipher) |
|||
if err != nil { |
|||
return 0, err |
|||
} |
|||
_, err = c.PacketConn.WriteTo(buf, addr) |
|||
return len(b), err |
|||
} |
|||
|
|||
func (c *packetConn) ReadFrom(b []byte) (int, net.Addr, error) { |
|||
n, addr, err := c.PacketConn.ReadFrom(b) |
|||
if err != nil { |
|||
return n, addr, err |
|||
} |
|||
bb, err := Unpack(b[c.IVSize():], b[:n], c.Cipher) |
|||
if err != nil { |
|||
return n, addr, err |
|||
} |
|||
copy(b, bb) |
|||
return len(bb), addr, err |
|||
} |
|||
@ -0,0 +1,171 @@ |
|||
package shadowstream |
|||
|
|||
import ( |
|||
"bytes" |
|||
"crypto/cipher" |
|||
"crypto/rand" |
|||
"io" |
|||
"net" |
|||
) |
|||
|
|||
const bufSize = 32 * 1024 |
|||
|
|||
type writer struct { |
|||
io.Writer |
|||
cipher.Stream |
|||
buf []byte |
|||
} |
|||
|
|||
// NewWriter wraps an io.Writer with stream cipher encryption.
|
|||
func NewWriter(w io.Writer, s cipher.Stream) io.Writer { |
|||
return &writer{Writer: w, Stream: s, buf: make([]byte, bufSize)} |
|||
} |
|||
|
|||
func (w *writer) ReadFrom(r io.Reader) (n int64, err error) { |
|||
for { |
|||
buf := w.buf |
|||
nr, er := r.Read(buf) |
|||
if nr > 0 { |
|||
n += int64(nr) |
|||
buf = buf[:nr] |
|||
w.XORKeyStream(buf, buf) |
|||
_, ew := w.Writer.Write(buf) |
|||
if ew != nil { |
|||
err = ew |
|||
return |
|||
} |
|||
} |
|||
|
|||
if er != nil { |
|||
if er != io.EOF { // ignore EOF as per io.ReaderFrom contract
|
|||
err = er |
|||
} |
|||
return |
|||
} |
|||
} |
|||
} |
|||
|
|||
func (w *writer) Write(b []byte) (int, error) { |
|||
n, err := w.ReadFrom(bytes.NewBuffer(b)) |
|||
return int(n), err |
|||
} |
|||
|
|||
type reader struct { |
|||
io.Reader |
|||
cipher.Stream |
|||
buf []byte |
|||
} |
|||
|
|||
// NewReader wraps an io.Reader with stream cipher decryption.
|
|||
func NewReader(r io.Reader, s cipher.Stream) io.Reader { |
|||
return &reader{Reader: r, Stream: s, buf: make([]byte, bufSize)} |
|||
} |
|||
|
|||
func (r *reader) Read(b []byte) (int, error) { |
|||
|
|||
n, err := r.Reader.Read(b) |
|||
if err != nil { |
|||
return 0, err |
|||
} |
|||
b = b[:n] |
|||
r.XORKeyStream(b, b) |
|||
return n, nil |
|||
} |
|||
|
|||
func (r *reader) WriteTo(w io.Writer) (n int64, err error) { |
|||
for { |
|||
buf := r.buf |
|||
nr, er := r.Read(buf) |
|||
if nr > 0 { |
|||
nw, ew := w.Write(buf[:nr]) |
|||
n += int64(nw) |
|||
|
|||
if ew != nil { |
|||
err = ew |
|||
return |
|||
} |
|||
} |
|||
|
|||
if er != nil { |
|||
if er != io.EOF { // ignore EOF as per io.Copy contract (using src.WriteTo shortcut)
|
|||
err = er |
|||
} |
|||
return |
|||
} |
|||
} |
|||
} |
|||
|
|||
type conn struct { |
|||
net.Conn |
|||
Cipher |
|||
r *reader |
|||
w *writer |
|||
} |
|||
|
|||
// NewConn wraps a stream-oriented net.Conn with stream cipher encryption/decryption.
|
|||
func NewConn(c net.Conn, ciph Cipher) net.Conn { |
|||
return &conn{Conn: c, Cipher: ciph} |
|||
} |
|||
|
|||
func (c *conn) initReader() error { |
|||
if c.r == nil { |
|||
buf := make([]byte, bufSize) |
|||
iv := buf[:c.IVSize()] |
|||
if _, err := io.ReadFull(c.Conn, iv); err != nil { |
|||
return err |
|||
} |
|||
c.r = &reader{Reader: c.Conn, Stream: c.Decrypter(iv), buf: buf} |
|||
} |
|||
return nil |
|||
} |
|||
|
|||
func (c *conn) Read(b []byte) (int, error) { |
|||
if c.r == nil { |
|||
if err := c.initReader(); err != nil { |
|||
return 0, err |
|||
} |
|||
} |
|||
return c.r.Read(b) |
|||
} |
|||
|
|||
func (c *conn) WriteTo(w io.Writer) (int64, error) { |
|||
if c.r == nil { |
|||
if err := c.initReader(); err != nil { |
|||
return 0, err |
|||
} |
|||
} |
|||
return c.r.WriteTo(w) |
|||
} |
|||
|
|||
func (c *conn) initWriter() error { |
|||
if c.w == nil { |
|||
buf := make([]byte, bufSize) |
|||
iv := buf[:c.IVSize()] |
|||
if _, err := io.ReadFull(rand.Reader, iv); err != nil { |
|||
return err |
|||
} |
|||
if _, err := c.Conn.Write(iv); err != nil { |
|||
return err |
|||
} |
|||
c.w = &writer{Writer: c.Conn, Stream: c.Encrypter(iv), buf: buf} |
|||
} |
|||
return nil |
|||
} |
|||
|
|||
func (c *conn) Write(b []byte) (int, error) { |
|||
if c.w == nil { |
|||
if err := c.initWriter(); err != nil { |
|||
return 0, err |
|||
} |
|||
} |
|||
return c.w.Write(b) |
|||
} |
|||
|
|||
func (c *conn) ReadFrom(r io.Reader) (int64, error) { |
|||
if c.w == nil { |
|||
if err := c.initWriter(); err != nil { |
|||
return 0, err |
|||
} |
|||
} |
|||
return c.w.ReadFrom(r) |
|||
} |
|||
@ -0,0 +1,101 @@ |
|||
// Copyright 2016 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 chacha20poly1305 implements the ChaCha20-Poly1305 AEAD as specified in RFC 7539,
|
|||
// and its extended nonce variant XChaCha20-Poly1305.
|
|||
package chacha20poly1305 // import "golang.org/x/crypto/chacha20poly1305"
|
|||
|
|||
import ( |
|||
"crypto/cipher" |
|||
"encoding/binary" |
|||
"errors" |
|||
) |
|||
|
|||
const ( |
|||
// KeySize is the size of the key used by this AEAD, in bytes.
|
|||
KeySize = 32 |
|||
|
|||
// NonceSize is the size of the nonce used with the standard variant of this
|
|||
// AEAD, in bytes.
|
|||
//
|
|||
// Note that this is too short to be safely generated at random if the same
|
|||
// key is reused more than 2³² times.
|
|||
NonceSize = 12 |
|||
|
|||
// NonceSizeX is the size of the nonce used with the XChaCha20-Poly1305
|
|||
// variant of this AEAD, in bytes.
|
|||
NonceSizeX = 24 |
|||
) |
|||
|
|||
type chacha20poly1305 struct { |
|||
key [8]uint32 |
|||
} |
|||
|
|||
// New returns a ChaCha20-Poly1305 AEAD that uses the given 256-bit key.
|
|||
func New(key []byte) (cipher.AEAD, error) { |
|||
if len(key) != KeySize { |
|||
return nil, errors.New("chacha20poly1305: bad key length") |
|||
} |
|||
ret := new(chacha20poly1305) |
|||
ret.key[0] = binary.LittleEndian.Uint32(key[0:4]) |
|||
ret.key[1] = binary.LittleEndian.Uint32(key[4:8]) |
|||
ret.key[2] = binary.LittleEndian.Uint32(key[8:12]) |
|||
ret.key[3] = binary.LittleEndian.Uint32(key[12:16]) |
|||
ret.key[4] = binary.LittleEndian.Uint32(key[16:20]) |
|||
ret.key[5] = binary.LittleEndian.Uint32(key[20:24]) |
|||
ret.key[6] = binary.LittleEndian.Uint32(key[24:28]) |
|||
ret.key[7] = binary.LittleEndian.Uint32(key[28:32]) |
|||
return ret, nil |
|||
} |
|||
|
|||
func (c *chacha20poly1305) NonceSize() int { |
|||
return NonceSize |
|||
} |
|||
|
|||
func (c *chacha20poly1305) Overhead() int { |
|||
return 16 |
|||
} |
|||
|
|||
func (c *chacha20poly1305) Seal(dst, nonce, plaintext, additionalData []byte) []byte { |
|||
if len(nonce) != NonceSize { |
|||
panic("chacha20poly1305: bad nonce length passed to Seal") |
|||
} |
|||
|
|||
if uint64(len(plaintext)) > (1<<38)-64 { |
|||
panic("chacha20poly1305: plaintext too large") |
|||
} |
|||
|
|||
return c.seal(dst, nonce, plaintext, additionalData) |
|||
} |
|||
|
|||
var errOpen = errors.New("chacha20poly1305: message authentication failed") |
|||
|
|||
func (c *chacha20poly1305) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) { |
|||
if len(nonce) != NonceSize { |
|||
panic("chacha20poly1305: bad nonce length passed to Open") |
|||
} |
|||
if len(ciphertext) < 16 { |
|||
return nil, errOpen |
|||
} |
|||
if uint64(len(ciphertext)) > (1<<38)-48 { |
|||
panic("chacha20poly1305: ciphertext too large") |
|||
} |
|||
|
|||
return c.open(dst, nonce, ciphertext, additionalData) |
|||
} |
|||
|
|||
// 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 |
|||
} |
|||
@ -0,0 +1,86 @@ |
|||
// Copyright 2016 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 go1.7,amd64,!gccgo,!appengine
|
|||
|
|||
package chacha20poly1305 |
|||
|
|||
import ( |
|||
"encoding/binary" |
|||
|
|||
"golang.org/x/crypto/internal/subtle" |
|||
"golang.org/x/sys/cpu" |
|||
) |
|||
|
|||
//go:noescape
|
|||
func chacha20Poly1305Open(dst []byte, key []uint32, src, ad []byte) bool |
|||
|
|||
//go:noescape
|
|||
func chacha20Poly1305Seal(dst []byte, key []uint32, src, ad []byte) |
|||
|
|||
var ( |
|||
useAVX2 = cpu.X86.HasAVX2 && cpu.X86.HasBMI2 |
|||
) |
|||
|
|||
// setupState writes a ChaCha20 input matrix to state. See
|
|||
// https://tools.ietf.org/html/rfc7539#section-2.3.
|
|||
func setupState(state *[16]uint32, key *[8]uint32, nonce []byte) { |
|||
state[0] = 0x61707865 |
|||
state[1] = 0x3320646e |
|||
state[2] = 0x79622d32 |
|||
state[3] = 0x6b206574 |
|||
|
|||
state[4] = key[0] |
|||
state[5] = key[1] |
|||
state[6] = key[2] |
|||
state[7] = key[3] |
|||
state[8] = key[4] |
|||
state[9] = key[5] |
|||
state[10] = key[6] |
|||
state[11] = key[7] |
|||
|
|||
state[12] = 0 |
|||
state[13] = binary.LittleEndian.Uint32(nonce[:4]) |
|||
state[14] = binary.LittleEndian.Uint32(nonce[4:8]) |
|||
state[15] = binary.LittleEndian.Uint32(nonce[8:12]) |
|||
} |
|||
|
|||
func (c *chacha20poly1305) seal(dst, nonce, plaintext, additionalData []byte) []byte { |
|||
if !cpu.X86.HasSSSE3 { |
|||
return c.sealGeneric(dst, nonce, plaintext, additionalData) |
|||
} |
|||
|
|||
var state [16]uint32 |
|||
setupState(&state, &c.key, nonce) |
|||
|
|||
ret, out := sliceForAppend(dst, len(plaintext)+16) |
|||
if subtle.InexactOverlap(out, plaintext) { |
|||
panic("chacha20poly1305: invalid buffer overlap") |
|||
} |
|||
chacha20Poly1305Seal(out[:], state[:], plaintext, additionalData) |
|||
return ret |
|||
} |
|||
|
|||
func (c *chacha20poly1305) open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) { |
|||
if !cpu.X86.HasSSSE3 { |
|||
return c.openGeneric(dst, nonce, ciphertext, additionalData) |
|||
} |
|||
|
|||
var state [16]uint32 |
|||
setupState(&state, &c.key, nonce) |
|||
|
|||
ciphertext = ciphertext[:len(ciphertext)-16] |
|||
ret, out := sliceForAppend(dst, len(ciphertext)) |
|||
if subtle.InexactOverlap(out, ciphertext) { |
|||
panic("chacha20poly1305: invalid buffer overlap") |
|||
} |
|||
if !chacha20Poly1305Open(out, state[:], ciphertext, additionalData) { |
|||
for i := range out { |
|||
out[i] = 0 |
|||
} |
|||
return nil, errOpen |
|||
} |
|||
|
|||
return ret, nil |
|||
} |
|||
File diff suppressed because it is too large
@ -0,0 +1,81 @@ |
|||
// Copyright 2016 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 chacha20poly1305 |
|||
|
|||
import ( |
|||
"encoding/binary" |
|||
|
|||
"golang.org/x/crypto/internal/chacha20" |
|||
"golang.org/x/crypto/internal/subtle" |
|||
"golang.org/x/crypto/poly1305" |
|||
) |
|||
|
|||
func roundTo16(n int) int { |
|||
return 16 * ((n + 15) / 16) |
|||
} |
|||
|
|||
func (c *chacha20poly1305) sealGeneric(dst, nonce, plaintext, additionalData []byte) []byte { |
|||
ret, out := sliceForAppend(dst, len(plaintext)+poly1305.TagSize) |
|||
if subtle.InexactOverlap(out, plaintext) { |
|||
panic("chacha20poly1305: invalid buffer overlap") |
|||
} |
|||
|
|||
var polyKey [32]byte |
|||
s := chacha20.New(c.key, [3]uint32{ |
|||
binary.LittleEndian.Uint32(nonce[0:4]), |
|||
binary.LittleEndian.Uint32(nonce[4:8]), |
|||
binary.LittleEndian.Uint32(nonce[8:12]), |
|||
}) |
|||
s.XORKeyStream(polyKey[:], polyKey[:]) |
|||
s.Advance() // skip the next 32 bytes
|
|||
s.XORKeyStream(out, plaintext) |
|||
|
|||
polyInput := make([]byte, roundTo16(len(additionalData))+roundTo16(len(plaintext))+8+8) |
|||
copy(polyInput, additionalData) |
|||
copy(polyInput[roundTo16(len(additionalData)):], out[:len(plaintext)]) |
|||
binary.LittleEndian.PutUint64(polyInput[len(polyInput)-16:], uint64(len(additionalData))) |
|||
binary.LittleEndian.PutUint64(polyInput[len(polyInput)-8:], uint64(len(plaintext))) |
|||
|
|||
var tag [poly1305.TagSize]byte |
|||
poly1305.Sum(&tag, polyInput, &polyKey) |
|||
copy(out[len(plaintext):], tag[:]) |
|||
|
|||
return ret |
|||
} |
|||
|
|||
func (c *chacha20poly1305) openGeneric(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) { |
|||
var tag [poly1305.TagSize]byte |
|||
copy(tag[:], ciphertext[len(ciphertext)-16:]) |
|||
ciphertext = ciphertext[:len(ciphertext)-16] |
|||
|
|||
var polyKey [32]byte |
|||
s := chacha20.New(c.key, [3]uint32{ |
|||
binary.LittleEndian.Uint32(nonce[0:4]), |
|||
binary.LittleEndian.Uint32(nonce[4:8]), |
|||
binary.LittleEndian.Uint32(nonce[8:12]), |
|||
}) |
|||
s.XORKeyStream(polyKey[:], polyKey[:]) |
|||
s.Advance() // skip the next 32 bytes
|
|||
|
|||
polyInput := make([]byte, roundTo16(len(additionalData))+roundTo16(len(ciphertext))+8+8) |
|||
copy(polyInput, additionalData) |
|||
copy(polyInput[roundTo16(len(additionalData)):], ciphertext) |
|||
binary.LittleEndian.PutUint64(polyInput[len(polyInput)-16:], uint64(len(additionalData))) |
|||
binary.LittleEndian.PutUint64(polyInput[len(polyInput)-8:], uint64(len(ciphertext))) |
|||
|
|||
ret, out := sliceForAppend(dst, len(ciphertext)) |
|||
if subtle.InexactOverlap(out, ciphertext) { |
|||
panic("chacha20poly1305: invalid buffer overlap") |
|||
} |
|||
if !poly1305.Verify(&tag, polyInput, &polyKey) { |
|||
for i := range out { |
|||
out[i] = 0 |
|||
} |
|||
return nil, errOpen |
|||
} |
|||
|
|||
s.XORKeyStream(out, ciphertext) |
|||
return ret, nil |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
// Copyright 2016 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 !go1.7 gccgo appengine
|
|||
|
|||
package chacha20poly1305 |
|||
|
|||
func (c *chacha20poly1305) seal(dst, nonce, plaintext, additionalData []byte) []byte { |
|||
return c.sealGeneric(dst, nonce, plaintext, additionalData) |
|||
} |
|||
|
|||
func (c *chacha20poly1305) open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) { |
|||
return c.openGeneric(dst, nonce, ciphertext, additionalData) |
|||
} |
|||
@ -0,0 +1,104 @@ |
|||
// Copyright 2018 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 chacha20poly1305 |
|||
|
|||
import ( |
|||
"crypto/cipher" |
|||
"encoding/binary" |
|||
"errors" |
|||
|
|||
"golang.org/x/crypto/internal/chacha20" |
|||
) |
|||
|
|||
type xchacha20poly1305 struct { |
|||
key [8]uint32 |
|||
} |
|||
|
|||
// NewX returns a XChaCha20-Poly1305 AEAD that uses the given 256-bit key.
|
|||
//
|
|||
// XChaCha20-Poly1305 is a ChaCha20-Poly1305 variant that takes a longer nonce,
|
|||
// suitable to be generated randomly without risk of collisions. It should be
|
|||
// preferred when nonce uniqueness cannot be trivially ensured, or whenever
|
|||
// nonces are randomly generated.
|
|||
func NewX(key []byte) (cipher.AEAD, error) { |
|||
if len(key) != KeySize { |
|||
return nil, errors.New("chacha20poly1305: bad key length") |
|||
} |
|||
ret := new(xchacha20poly1305) |
|||
ret.key[0] = binary.LittleEndian.Uint32(key[0:4]) |
|||
ret.key[1] = binary.LittleEndian.Uint32(key[4:8]) |
|||
ret.key[2] = binary.LittleEndian.Uint32(key[8:12]) |
|||
ret.key[3] = binary.LittleEndian.Uint32(key[12:16]) |
|||
ret.key[4] = binary.LittleEndian.Uint32(key[16:20]) |
|||
ret.key[5] = binary.LittleEndian.Uint32(key[20:24]) |
|||
ret.key[6] = binary.LittleEndian.Uint32(key[24:28]) |
|||
ret.key[7] = binary.LittleEndian.Uint32(key[28:32]) |
|||
return ret, nil |
|||
} |
|||
|
|||
func (*xchacha20poly1305) NonceSize() int { |
|||
return NonceSizeX |
|||
} |
|||
|
|||
func (*xchacha20poly1305) Overhead() int { |
|||
return 16 |
|||
} |
|||
|
|||
func (x *xchacha20poly1305) Seal(dst, nonce, plaintext, additionalData []byte) []byte { |
|||
if len(nonce) != NonceSizeX { |
|||
panic("chacha20poly1305: bad nonce length passed to Seal") |
|||
} |
|||
|
|||
// XChaCha20-Poly1305 technically supports a 64-bit counter, so there is no
|
|||
// size limit. However, since we reuse the ChaCha20-Poly1305 implementation,
|
|||
// the second half of the counter is not available. This is unlikely to be
|
|||
// an issue because the cipher.AEAD API requires the entire message to be in
|
|||
// memory, and the counter overflows at 256 GB.
|
|||
if uint64(len(plaintext)) > (1<<38)-64 { |
|||
panic("chacha20poly1305: plaintext too large") |
|||
} |
|||
|
|||
hNonce := [4]uint32{ |
|||
binary.LittleEndian.Uint32(nonce[0:4]), |
|||
binary.LittleEndian.Uint32(nonce[4:8]), |
|||
binary.LittleEndian.Uint32(nonce[8:12]), |
|||
binary.LittleEndian.Uint32(nonce[12:16]), |
|||
} |
|||
c := &chacha20poly1305{ |
|||
key: chacha20.HChaCha20(&x.key, &hNonce), |
|||
} |
|||
// The first 4 bytes of the final nonce are unused counter space.
|
|||
cNonce := make([]byte, NonceSize) |
|||
copy(cNonce[4:12], nonce[16:24]) |
|||
|
|||
return c.seal(dst, cNonce[:], plaintext, additionalData) |
|||
} |
|||
|
|||
func (x *xchacha20poly1305) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) { |
|||
if len(nonce) != NonceSizeX { |
|||
panic("chacha20poly1305: bad nonce length passed to Open") |
|||
} |
|||
if len(ciphertext) < 16 { |
|||
return nil, errOpen |
|||
} |
|||
if uint64(len(ciphertext)) > (1<<38)-48 { |
|||
panic("chacha20poly1305: ciphertext too large") |
|||
} |
|||
|
|||
hNonce := [4]uint32{ |
|||
binary.LittleEndian.Uint32(nonce[0:4]), |
|||
binary.LittleEndian.Uint32(nonce[4:8]), |
|||
binary.LittleEndian.Uint32(nonce[8:12]), |
|||
binary.LittleEndian.Uint32(nonce[12:16]), |
|||
} |
|||
c := &chacha20poly1305{ |
|||
key: chacha20.HChaCha20(&x.key, &hNonce), |
|||
} |
|||
// The first 4 bytes of the final nonce are unused counter space.
|
|||
cNonce := make([]byte, NonceSize) |
|||
copy(cNonce[4:12], nonce[16:24]) |
|||
|
|||
return c.open(dst, cNonce[:], ciphertext, additionalData) |
|||
} |
|||
@ -0,0 +1,87 @@ |
|||
// Copyright 2018 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 cpu implements processor feature detection for
|
|||
// various CPU architectures.
|
|||
package cpu |
|||
|
|||
// CacheLinePad is used to pad structs to avoid false sharing.
|
|||
type CacheLinePad struct{ _ [cacheLineSize]byte } |
|||
|
|||
// X86 contains the supported CPU features of the
|
|||
// current X86/AMD64 platform. If the current platform
|
|||
// is not X86/AMD64 then all feature flags are false.
|
|||
//
|
|||
// X86 is padded to avoid false sharing. Further the HasAVX
|
|||
// and HasAVX2 are only set if the OS supports XMM and YMM
|
|||
// registers in addition to the CPUID feature bit being set.
|
|||
var X86 struct { |
|||
_ CacheLinePad |
|||
HasAES bool // AES hardware implementation (AES NI)
|
|||
HasADX bool // Multi-precision add-carry instruction extensions
|
|||
HasAVX bool // Advanced vector extension
|
|||
HasAVX2 bool // Advanced vector extension 2
|
|||
HasBMI1 bool // Bit manipulation instruction set 1
|
|||
HasBMI2 bool // Bit manipulation instruction set 2
|
|||
HasERMS bool // Enhanced REP for MOVSB and STOSB
|
|||
HasFMA bool // Fused-multiply-add instructions
|
|||
HasOSXSAVE bool // OS supports XSAVE/XRESTOR for saving/restoring XMM registers.
|
|||
HasPCLMULQDQ bool // PCLMULQDQ instruction - most often used for AES-GCM
|
|||
HasPOPCNT bool // Hamming weight instruction POPCNT.
|
|||
HasSSE2 bool // Streaming SIMD extension 2 (always available on amd64)
|
|||
HasSSE3 bool // Streaming SIMD extension 3
|
|||
HasSSSE3 bool // Supplemental streaming SIMD extension 3
|
|||
HasSSE41 bool // Streaming SIMD extension 4 and 4.1
|
|||
HasSSE42 bool // Streaming SIMD extension 4 and 4.2
|
|||
_ CacheLinePad |
|||
} |
|||
|
|||
// ARM64 contains the supported CPU features of the
|
|||
// current ARMv8(aarch64) platform. If the current platform
|
|||
// is not arm64 then all feature flags are false.
|
|||
var ARM64 struct { |
|||
_ CacheLinePad |
|||
HasFP bool // Floating-point instruction set (always available)
|
|||
HasASIMD bool // Advanced SIMD (always available)
|
|||
HasEVTSTRM bool // Event stream support
|
|||
HasAES bool // AES hardware implementation
|
|||
HasPMULL bool // Polynomial multiplication instruction set
|
|||
HasSHA1 bool // SHA1 hardware implementation
|
|||
HasSHA2 bool // SHA2 hardware implementation
|
|||
HasCRC32 bool // CRC32 hardware implementation
|
|||
HasATOMICS bool // Atomic memory operation instruction set
|
|||
HasFPHP bool // Half precision floating-point instruction set
|
|||
HasASIMDHP bool // Advanced SIMD half precision instruction set
|
|||
HasCPUID bool // CPUID identification scheme registers
|
|||
HasASIMDRDM bool // Rounding double multiply add/subtract instruction set
|
|||
HasJSCVT bool // Javascript conversion from floating-point to integer
|
|||
HasFCMA bool // Floating-point multiplication and addition of complex numbers
|
|||
HasLRCPC bool // Release Consistent processor consistent support
|
|||
HasDCPOP bool // Persistent memory support
|
|||
HasSHA3 bool // SHA3 hardware implementation
|
|||
HasSM3 bool // SM3 hardware implementation
|
|||
HasSM4 bool // SM4 hardware implementation
|
|||
HasASIMDDP bool // Advanced SIMD double precision instruction set
|
|||
HasSHA512 bool // SHA512 hardware implementation
|
|||
HasSVE bool // Scalable Vector Extensions
|
|||
HasASIMDFHM bool // Advanced SIMD multiplication FP16 to FP32
|
|||
_ CacheLinePad |
|||
} |
|||
|
|||
// PPC64 contains the supported CPU features of the current ppc64/ppc64le platforms.
|
|||
// If the current platform is not ppc64/ppc64le then all feature flags are false.
|
|||
//
|
|||
// For ppc64/ppc64le, it is safe to check only for ISA level starting on ISA v3.00,
|
|||
// since there are no optional categories. There are some exceptions that also
|
|||
// require kernel support to work (DARN, SCV), so there are feature bits for
|
|||
// those as well. The minimum processor requirement is POWER8 (ISA 2.07).
|
|||
// The struct is padded to avoid false sharing.
|
|||
var PPC64 struct { |
|||
_ CacheLinePad |
|||
HasDARN bool // Hardware random number generator (requires kernel enablement)
|
|||
HasSCV bool // Syscall vectored (requires kernel enablement)
|
|||
IsPOWER8 bool // ISA v2.07 (POWER8)
|
|||
IsPOWER9 bool // ISA v3.00 (POWER9)
|
|||
_ CacheLinePad |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
// Copyright 2018 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 cpu |
|||
|
|||
const cacheLineSize = 32 |
|||
|
|||
func doinit() {} |
|||
@ -0,0 +1,67 @@ |
|||
// Copyright 2018 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 cpu |
|||
|
|||
const cacheLineSize = 64 |
|||
|
|||
// HWCAP/HWCAP2 bits. These are exposed by Linux.
|
|||
const ( |
|||
hwcap_FP = 1 << 0 |
|||
hwcap_ASIMD = 1 << 1 |
|||
hwcap_EVTSTRM = 1 << 2 |
|||
hwcap_AES = 1 << 3 |
|||
hwcap_PMULL = 1 << 4 |
|||
hwcap_SHA1 = 1 << 5 |
|||
hwcap_SHA2 = 1 << 6 |
|||
hwcap_CRC32 = 1 << 7 |
|||
hwcap_ATOMICS = 1 << 8 |
|||
hwcap_FPHP = 1 << 9 |
|||
hwcap_ASIMDHP = 1 << 10 |
|||
hwcap_CPUID = 1 << 11 |
|||
hwcap_ASIMDRDM = 1 << 12 |
|||
hwcap_JSCVT = 1 << 13 |
|||
hwcap_FCMA = 1 << 14 |
|||
hwcap_LRCPC = 1 << 15 |
|||
hwcap_DCPOP = 1 << 16 |
|||
hwcap_SHA3 = 1 << 17 |
|||
hwcap_SM3 = 1 << 18 |
|||
hwcap_SM4 = 1 << 19 |
|||
hwcap_ASIMDDP = 1 << 20 |
|||
hwcap_SHA512 = 1 << 21 |
|||
hwcap_SVE = 1 << 22 |
|||
hwcap_ASIMDFHM = 1 << 23 |
|||
) |
|||
|
|||
func doinit() { |
|||
// HWCAP feature bits
|
|||
ARM64.HasFP = isSet(HWCap, hwcap_FP) |
|||
ARM64.HasASIMD = isSet(HWCap, hwcap_ASIMD) |
|||
ARM64.HasEVTSTRM = isSet(HWCap, hwcap_EVTSTRM) |
|||
ARM64.HasAES = isSet(HWCap, hwcap_AES) |
|||
ARM64.HasPMULL = isSet(HWCap, hwcap_PMULL) |
|||
ARM64.HasSHA1 = isSet(HWCap, hwcap_SHA1) |
|||
ARM64.HasSHA2 = isSet(HWCap, hwcap_SHA2) |
|||
ARM64.HasCRC32 = isSet(HWCap, hwcap_CRC32) |
|||
ARM64.HasATOMICS = isSet(HWCap, hwcap_ATOMICS) |
|||
ARM64.HasFPHP = isSet(HWCap, hwcap_FPHP) |
|||
ARM64.HasASIMDHP = isSet(HWCap, hwcap_ASIMDHP) |
|||
ARM64.HasCPUID = isSet(HWCap, hwcap_CPUID) |
|||
ARM64.HasASIMDRDM = isSet(HWCap, hwcap_ASIMDRDM) |
|||
ARM64.HasJSCVT = isSet(HWCap, hwcap_JSCVT) |
|||
ARM64.HasFCMA = isSet(HWCap, hwcap_FCMA) |
|||
ARM64.HasLRCPC = isSet(HWCap, hwcap_LRCPC) |
|||
ARM64.HasDCPOP = isSet(HWCap, hwcap_DCPOP) |
|||
ARM64.HasSHA3 = isSet(HWCap, hwcap_SHA3) |
|||
ARM64.HasSM3 = isSet(HWCap, hwcap_SM3) |
|||
ARM64.HasSM4 = isSet(HWCap, hwcap_SM4) |
|||
ARM64.HasASIMDDP = isSet(HWCap, hwcap_ASIMDDP) |
|||
ARM64.HasSHA512 = isSet(HWCap, hwcap_SHA512) |
|||
ARM64.HasSVE = isSet(HWCap, hwcap_SVE) |
|||
ARM64.HasASIMDFHM = isSet(HWCap, hwcap_ASIMDFHM) |
|||
} |
|||
|
|||
func isSet(hwc uint, value uint) bool { |
|||
return hwc&value != 0 |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
// Copyright 2018 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 386 amd64 amd64p32
|
|||
// +build !gccgo
|
|||
|
|||
package cpu |
|||
|
|||
// cpuid is implemented in cpu_x86.s for gc compiler
|
|||
// and in cpu_gccgo.c for gccgo.
|
|||
func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32) |
|||
|
|||
// xgetbv with ecx = 0 is implemented in cpu_x86.s for gc compiler
|
|||
// and in cpu_gccgo.c for gccgo.
|
|||
func xgetbv() (eax, edx uint32) |
|||
@ -0,0 +1,43 @@ |
|||
// Copyright 2018 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 386 amd64 amd64p32
|
|||
// +build gccgo
|
|||
|
|||
#include <cpuid.h> |
|||
#include <stdint.h> |
|||
|
|||
// Need to wrap __get_cpuid_count because it's declared as static.
|
|||
int |
|||
gccgoGetCpuidCount(uint32_t leaf, uint32_t subleaf, |
|||
uint32_t *eax, uint32_t *ebx, |
|||
uint32_t *ecx, uint32_t *edx) |
|||
{ |
|||
return __get_cpuid_count(leaf, subleaf, eax, ebx, ecx, edx); |
|||
} |
|||
|
|||
// xgetbv reads the contents of an XCR (Extended Control Register)
|
|||
// specified in the ECX register into registers EDX:EAX.
|
|||
// Currently, the only supported value for XCR is 0.
|
|||
//
|
|||
// TODO: Replace with a better alternative:
|
|||
//
|
|||
// #include <xsaveintrin.h>
|
|||
//
|
|||
// #pragma GCC target("xsave")
|
|||
//
|
|||
// void gccgoXgetbv(uint32_t *eax, uint32_t *edx) {
|
|||
// unsigned long long x = _xgetbv(0);
|
|||
// *eax = x & 0xffffffff;
|
|||
// *edx = (x >> 32) & 0xffffffff;
|
|||
// }
|
|||
//
|
|||
// Note that _xgetbv is defined starting with GCC 8.
|
|||
void |
|||
gccgoXgetbv(uint32_t *eax, uint32_t *edx) |
|||
{ |
|||
__asm(" xorl %%ecx, %%ecx\n" |
|||
" xgetbv" |
|||
: "=a"(*eax), "=d"(*edx)); |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
// Copyright 2018 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 386 amd64 amd64p32
|
|||
// +build gccgo
|
|||
|
|||
package cpu |
|||
|
|||
//extern gccgoGetCpuidCount
|
|||
func gccgoGetCpuidCount(eaxArg, ecxArg uint32, eax, ebx, ecx, edx *uint32) |
|||
|
|||
func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32) { |
|||
var a, b, c, d uint32 |
|||
gccgoGetCpuidCount(eaxArg, ecxArg, &a, &b, &c, &d) |
|||
return a, b, c, d |
|||
} |
|||
|
|||
//extern gccgoXgetbv
|
|||
func gccgoXgetbv(eax, edx *uint32) |
|||
|
|||
func xgetbv() (eax, edx uint32) { |
|||
var a, d uint32 |
|||
gccgoXgetbv(&a, &d) |
|||
return a, d |
|||
} |
|||
@ -0,0 +1,61 @@ |
|||
// Copyright 2018 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,!amd64p32,!386
|
|||
|
|||
package cpu |
|||
|
|||
import ( |
|||
"encoding/binary" |
|||
"io/ioutil" |
|||
"runtime" |
|||
) |
|||
|
|||
const ( |
|||
_AT_HWCAP = 16 |
|||
_AT_HWCAP2 = 26 |
|||
|
|||
procAuxv = "/proc/self/auxv" |
|||
|
|||
uintSize uint = 32 << (^uint(0) >> 63) |
|||
) |
|||
|
|||
// For those platforms don't have a 'cpuid' equivalent we use HWCAP/HWCAP2
|
|||
// These are initialized in cpu_$GOARCH.go
|
|||
// and should not be changed after they are initialized.
|
|||
var HWCap uint |
|||
var HWCap2 uint |
|||
|
|||
func init() { |
|||
buf, err := ioutil.ReadFile(procAuxv) |
|||
if err != nil { |
|||
panic("read proc auxv failed: " + err.Error()) |
|||
} |
|||
|
|||
pb := int(uintSize / 8) |
|||
|
|||
for i := 0; i < len(buf)-pb*2; i += pb * 2 { |
|||
var tag, val uint |
|||
switch uintSize { |
|||
case 32: |
|||
tag = uint(binary.LittleEndian.Uint32(buf[i:])) |
|||
val = uint(binary.LittleEndian.Uint32(buf[i+pb:])) |
|||
case 64: |
|||
if runtime.GOARCH == "ppc64" { |
|||
tag = uint(binary.BigEndian.Uint64(buf[i:])) |
|||
val = uint(binary.BigEndian.Uint64(buf[i+pb:])) |
|||
} else { |
|||
tag = uint(binary.LittleEndian.Uint64(buf[i:])) |
|||
val = uint(binary.LittleEndian.Uint64(buf[i+pb:])) |
|||
} |
|||
} |
|||
switch tag { |
|||
case _AT_HWCAP: |
|||
HWCap = val |
|||
case _AT_HWCAP2: |
|||
HWCap2 = val |
|||
} |
|||
} |
|||
doinit() |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
// Copyright 2018 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 mips64 mips64le
|
|||
|
|||
package cpu |
|||
|
|||
const cacheLineSize = 32 |
|||
|
|||
func doinit() {} |
|||
@ -0,0 +1,11 @@ |
|||
// Copyright 2018 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 mips mipsle
|
|||
|
|||
package cpu |
|||
|
|||
const cacheLineSize = 32 |
|||
|
|||
func doinit() {} |
|||
@ -0,0 +1,32 @@ |
|||
// Copyright 2018 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 ppc64 ppc64le
|
|||
|
|||
package cpu |
|||
|
|||
const cacheLineSize = 128 |
|||
|
|||
// HWCAP/HWCAP2 bits. These are exposed by the kernel.
|
|||
const ( |
|||
// ISA Level
|
|||
_PPC_FEATURE2_ARCH_2_07 = 0x80000000 |
|||
_PPC_FEATURE2_ARCH_3_00 = 0x00800000 |
|||
|
|||
// CPU features
|
|||
_PPC_FEATURE2_DARN = 0x00200000 |
|||
_PPC_FEATURE2_SCV = 0x00100000 |
|||
) |
|||
|
|||
func doinit() { |
|||
// HWCAP2 feature bits
|
|||
PPC64.IsPOWER8 = isSet(HWCap2, _PPC_FEATURE2_ARCH_2_07) |
|||
PPC64.IsPOWER9 = isSet(HWCap2, _PPC_FEATURE2_ARCH_3_00) |
|||
PPC64.HasDARN = isSet(HWCap2, _PPC_FEATURE2_DARN) |
|||
PPC64.HasSCV = isSet(HWCap2, _PPC_FEATURE2_SCV) |
|||
} |
|||
|
|||
func isSet(hwc uint, value uint) bool { |
|||
return hwc&value != 0 |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
// Copyright 2018 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 cpu |
|||
|
|||
const cacheLineSize = 256 |
|||
|
|||
func doinit() {} |
|||
@ -0,0 +1,55 @@ |
|||
// Copyright 2018 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 386 amd64 amd64p32
|
|||
|
|||
package cpu |
|||
|
|||
const cacheLineSize = 64 |
|||
|
|||
func init() { |
|||
maxID, _, _, _ := cpuid(0, 0) |
|||
|
|||
if maxID < 1 { |
|||
return |
|||
} |
|||
|
|||
_, _, ecx1, edx1 := cpuid(1, 0) |
|||
X86.HasSSE2 = isSet(26, edx1) |
|||
|
|||
X86.HasSSE3 = isSet(0, ecx1) |
|||
X86.HasPCLMULQDQ = isSet(1, ecx1) |
|||
X86.HasSSSE3 = isSet(9, ecx1) |
|||
X86.HasFMA = isSet(12, ecx1) |
|||
X86.HasSSE41 = isSet(19, ecx1) |
|||
X86.HasSSE42 = isSet(20, ecx1) |
|||
X86.HasPOPCNT = isSet(23, ecx1) |
|||
X86.HasAES = isSet(25, ecx1) |
|||
X86.HasOSXSAVE = isSet(27, ecx1) |
|||
|
|||
osSupportsAVX := false |
|||
// For XGETBV, OSXSAVE bit is required and sufficient.
|
|||
if X86.HasOSXSAVE { |
|||
eax, _ := xgetbv() |
|||
// Check if XMM and YMM registers have OS support.
|
|||
osSupportsAVX = isSet(1, eax) && isSet(2, eax) |
|||
} |
|||
|
|||
X86.HasAVX = isSet(28, ecx1) && osSupportsAVX |
|||
|
|||
if maxID < 7 { |
|||
return |
|||
} |
|||
|
|||
_, ebx7, _, _ := cpuid(7, 0) |
|||
X86.HasBMI1 = isSet(3, ebx7) |
|||
X86.HasAVX2 = isSet(5, ebx7) && osSupportsAVX |
|||
X86.HasBMI2 = isSet(8, ebx7) |
|||
X86.HasERMS = isSet(9, ebx7) |
|||
X86.HasADX = isSet(19, ebx7) |
|||
} |
|||
|
|||
func isSet(bitpos uint, value uint32) bool { |
|||
return value&(1<<bitpos) != 0 |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
// Copyright 2018 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 386 amd64 amd64p32 |
|||
// +build !gccgo |
|||
|
|||
#include "textflag.h" |
|||
|
|||
// func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32) |
|||
TEXT ·cpuid(SB), NOSPLIT, $0-24 |
|||
MOVL eaxArg+0(FP), AX |
|||
MOVL ecxArg+4(FP), CX |
|||
CPUID |
|||
MOVL AX, eax+8(FP) |
|||
MOVL BX, ebx+12(FP) |
|||
MOVL CX, ecx+16(FP) |
|||
MOVL DX, edx+20(FP) |
|||
RET |
|||
|
|||
// func xgetbv() (eax, edx uint32) |
|||
TEXT ·xgetbv(SB),NOSPLIT,$0-8 |
|||
MOVL $0, CX |
|||
XGETBV |
|||
MOVL AX, eax+0(FP) |
|||
MOVL DX, edx+4(FP) |
|||
RET |
|||
@ -0,0 +1,29 @@ |
|||
// Copyright 2018 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 !gccgo |
|||
|
|||
#include "textflag.h" |
|||
|
|||
// |
|||
// System call support for ARM64, FreeBSD |
|||
// |
|||
|
|||
// Just jump to package syscall's implementation for all these functions. |
|||
// The runtime may know about them. |
|||
|
|||
TEXT ·Syscall(SB),NOSPLIT,$0-56 |
|||
JMP syscall·Syscall(SB) |
|||
|
|||
TEXT ·Syscall6(SB),NOSPLIT,$0-80 |
|||
JMP syscall·Syscall6(SB) |
|||
|
|||
TEXT ·Syscall9(SB),NOSPLIT,$0-104 |
|||
JMP syscall·Syscall9(SB) |
|||
|
|||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56 |
|||
JMP syscall·RawSyscall(SB) |
|||
|
|||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80 |
|||
JMP syscall·RawSyscall6(SB) |
|||
@ -0,0 +1,29 @@ |
|||
// Copyright 2019 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 !gccgo |
|||
|
|||
#include "textflag.h" |
|||
|
|||
// |
|||
// System call support for ARM64, NetBSD |
|||
// |
|||
|
|||
// Just jump to package syscall's implementation for all these functions. |
|||
// The runtime may know about them. |
|||
|
|||
TEXT ·Syscall(SB),NOSPLIT,$0-56 |
|||
B syscall·Syscall(SB) |
|||
|
|||
TEXT ·Syscall6(SB),NOSPLIT,$0-80 |
|||
B syscall·Syscall6(SB) |
|||
|
|||
TEXT ·Syscall9(SB),NOSPLIT,$0-104 |
|||
B syscall·Syscall9(SB) |
|||
|
|||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56 |
|||
B syscall·RawSyscall(SB) |
|||
|
|||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80 |
|||
B syscall·RawSyscall6(SB) |
|||
@ -0,0 +1,18 @@ |
|||
// Copyright 2019 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 unix |
|||
|
|||
import "unsafe" |
|||
|
|||
// FcntlInt performs a fcntl syscall on fd with the provided command and argument.
|
|||
func FcntlInt(fd uintptr, cmd, arg int) (int, error) { |
|||
return fcntl(int(fd), cmd, arg) |
|||
} |
|||
|
|||
// FcntlFlock performs a fcntl syscall for the F_GETLK, F_SETLK or F_SETLKW command.
|
|||
func FcntlFlock(fd uintptr, cmd int, lk *Flock_t) error { |
|||
_, err := fcntl(int(fd), cmd, int(uintptr(unsafe.Pointer(lk)))) |
|||
return err |
|||
} |
|||
@ -0,0 +1,61 @@ |
|||
// Copyright 2018 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 ignore
|
|||
|
|||
// mkasm_darwin.go generates assembly trampolines to call libSystem routines from Go.
|
|||
//This program must be run after mksyscall.go.
|
|||
package main |
|||
|
|||
import ( |
|||
"bytes" |
|||
"fmt" |
|||
"io/ioutil" |
|||
"log" |
|||
"os" |
|||
"strings" |
|||
) |
|||
|
|||
func main() { |
|||
in1, err := ioutil.ReadFile("syscall_darwin.go") |
|||
if err != nil { |
|||
log.Fatalf("can't open syscall_darwin.go: %s", err) |
|||
} |
|||
arch := os.Args[1] |
|||
in2, err := ioutil.ReadFile(fmt.Sprintf("syscall_darwin_%s.go", arch)) |
|||
if err != nil { |
|||
log.Fatalf("can't open syscall_darwin_%s.go: %s", arch, err) |
|||
} |
|||
in3, err := ioutil.ReadFile(fmt.Sprintf("zsyscall_darwin_%s.go", arch)) |
|||
if err != nil { |
|||
log.Fatalf("can't open zsyscall_darwin_%s.go: %s", arch, err) |
|||
} |
|||
in := string(in1) + string(in2) + string(in3) |
|||
|
|||
trampolines := map[string]bool{} |
|||
|
|||
var out bytes.Buffer |
|||
|
|||
fmt.Fprintf(&out, "// go run mkasm_darwin.go %s\n", strings.Join(os.Args[1:], " ")) |
|||
fmt.Fprintf(&out, "// Code generated by the command above; DO NOT EDIT.\n") |
|||
fmt.Fprintf(&out, "\n") |
|||
fmt.Fprintf(&out, "// +build go1.12\n") |
|||
fmt.Fprintf(&out, "\n") |
|||
fmt.Fprintf(&out, "#include \"textflag.h\"\n") |
|||
for _, line := range strings.Split(in, "\n") { |
|||
if !strings.HasPrefix(line, "func ") || !strings.HasSuffix(line, "_trampoline()") { |
|||
continue |
|||
} |
|||
fn := line[5 : len(line)-13] |
|||
if !trampolines[fn] { |
|||
trampolines[fn] = true |
|||
fmt.Fprintf(&out, "TEXT ·%s_trampoline(SB),NOSPLIT,$0-0\n", fn) |
|||
fmt.Fprintf(&out, "\tJMP\t%s(SB)\n", fn) |
|||
} |
|||
} |
|||
err = ioutil.WriteFile(fmt.Sprintf("zsyscall_darwin_%s.s", arch), out.Bytes(), 0644) |
|||
if err != nil { |
|||
log.Fatalf("can't write zsyscall_darwin_%s.s: %s", arch, err) |
|||
} |
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue