forked from enceve/crypto
-
Notifications
You must be signed in to change notification settings - Fork 0
/
chacha_amd64.go
121 lines (93 loc) · 3.38 KB
/
chacha_amd64.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
// Use of this source code is governed by a license
// that can be found in the LICENSE file.
// +build amd64,!gccgo,!appengine
package chacha
import (
"unsafe"
"github.com/enceve/crypto"
)
// XORKeyStream crypts bytes from src to dst using the given key, nonce and counter.
// The rounds argument specifies the number of rounds (must be even) performed for
// keystream generation. (Common values are 20, 12 or 8) Src and dst may be the same
// slice but otherwise should not overlap. If len(dst) < len(src) this function panics.
func XORKeyStream(dst, src []byte, nonce *[12]byte, key *[32]byte, counter uint32, rounds int) {
length := len(src)
if len(dst) < length {
panic("chacha20/chacha: dst buffer is to small")
}
if rounds <= 0 || rounds%2 != 0 {
panic("chacha20/chacha: rounds must be a multiple of 2")
}
var state [64]byte
copy(state[:], constants[:])
statePtr := (*[8]uint64)(unsafe.Pointer(&state[0]))
keyPtr := (*[4]uint64)(unsafe.Pointer(&key[0]))
statePtr[2] = keyPtr[0]
statePtr[3] = keyPtr[1]
statePtr[4] = keyPtr[2]
statePtr[5] = keyPtr[3]
statePtr[6] = (*(*uint64)(unsafe.Pointer(&nonce[0])) << 32) | uint64(counter)
statePtr[7] = *(*uint64)(unsafe.Pointer(&nonce[4]))
if length >= 64 {
XORBlocks(dst, src, &state, rounds)
}
if n := length & (^(64 - 1)); length-n > 0 {
var block [64]byte
Core(&block, &state, rounds)
crypto.XOR(dst[n:], src[n:], block[:])
}
}
// NewCipher returns a new *chacha.Cipher implementing the ChaCha/X (X = even number of rounds)
// stream cipher. The nonce must be unique for one key for all time.
func NewCipher(nonce *[12]byte, key *[32]byte, rounds int) *Cipher {
if rounds <= 0 || rounds%2 != 0 {
panic("chacha20/chacha: rounds must be a multiply of 2")
}
c := new(Cipher)
c.rounds = rounds
copy(c.state[:], constants[:])
statePtr := (*[8]uint64)(unsafe.Pointer(&(c.state[0])))
keyPtr := (*[4]uint64)(unsafe.Pointer(&key[0]))
statePtr[2] = keyPtr[0]
statePtr[3] = keyPtr[1]
statePtr[4] = keyPtr[2]
statePtr[5] = keyPtr[3]
statePtr[6] = (*(*uint64)(unsafe.Pointer(&nonce[0])) << 32)
statePtr[7] = *(*uint64)(unsafe.Pointer(&nonce[4]))
return c
}
// 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) {
length := len(src)
if len(dst) < length {
panic("chacha20/chacha: dst buffer is to small")
}
if c.off > 0 {
n := crypto.XOR(dst, src, c.block[c.off:])
if n == length {
c.off += n
return
}
src = src[n:]
dst = dst[n:]
length -= n
c.off = 0
}
if length >= 64 {
XORBlocks(dst, src, &(c.state), c.rounds)
}
if n := length & (^(64 - 1)); length-n > 0 {
Core(&(c.block), &(c.state), c.rounds)
c.off += crypto.XOR(dst[n:], src[n:], c.block[:])
}
}
// XORBlocks crypts full block ( len(src) - (len(src) mod 64) bytes ) from src to
// dst using the state. Src and dst may be the same slice but otherwise should not
// overlap. This function increments the counter of state.
// If len(src) > len(dst), XORBlocks does nothing.
func XORBlocks(dst, src []byte, state *[64]byte, rounds int)
// Core generates 64 byte keystream from the given state performing 'rounds' rounds
// and writes them to dst. This function expects valid values. (no nil ptr etc.)
// Core increments the counter of state.
func Core(dst *[64]byte, state *[64]byte, rounds int)