-
Notifications
You must be signed in to change notification settings - Fork 25
/
gcfb.go
129 lines (103 loc) · 3.29 KB
/
gcfb.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
122
123
124
125
126
127
128
129
package cipher
import (
"crypto/cipher"
"github.com/deatil/go-cryptobin/tool/alias"
)
var gcfbC = []byte{
0x69, 0x00, 0x72, 0x22, 0x64, 0xC9, 0x04, 0x23,
0x8D, 0x3A, 0xDB, 0x96, 0x46, 0xE9, 0x2A, 0xC4,
0x18, 0xFE, 0xAC, 0x94, 0x00, 0xED, 0x07, 0x12,
0xC0, 0x86, 0xDC, 0xC2, 0xEF, 0x4C, 0xA9, 0x2B,
}
type GCFBCipherFunc = func([]byte) (cipher.Block, error)
/**
* An implementation of the GOST CFB mode with CryptoPro key meshing as described in RFC 4357.
*/
type gcfb struct {
b GCFBCipherFunc
cfbEngine cipher.Stream
key []byte
iv []byte
bs int
counter int
decrypt bool
}
func (x *gcfb) XORKeyStream(dst, src []byte) {
if len(dst) < len(src) {
panic("cryptobin/gcfb: output smaller than input")
}
if alias.InexactOverlap(dst[:len(src)], src) {
panic("cryptobin/gcfb: invalid buffer overlap")
}
for len(src) > 0 {
if x.counter > 0 && x.counter % 1024 == 0 {
base, err := x.b(x.key)
if err != nil {
panic(err)
}
nextKey := make([]byte, 32)
base.Encrypt(nextKey[0:8], gcfbC[0:8])
base.Encrypt(nextKey[8:16], gcfbC[8:16])
base.Encrypt(nextKey[16:24], gcfbC[16:24])
base.Encrypt(nextKey[24:32], gcfbC[24:32])
copy(x.key, nextKey)
base, err = x.b(nextKey)
if err != nil {
panic(err)
}
base.Encrypt(x.iv, x.iv)
nb, err := x.b(nextKey)
if err != nil {
panic(err)
}
if x.decrypt {
x.cfbEngine = cipher.NewCFBDecrypter(nb, x.iv)
} else {
x.cfbEngine = cipher.NewCFBEncrypter(nb, x.iv)
}
}
x.cfbEngine.XORKeyStream(dst[:x.bs], src[:x.bs])
dst = dst[x.bs:]
src = src[x.bs:]
x.counter += x.bs
}
}
// NewGCFBEncrypter returns a Stream which encrypts with cipher feedback mode,
// using the given Block. The iv must be the same length as the Block's block
// size.
func NewGCFBEncrypter(block GCFBCipherFunc, key, iv []byte) cipher.Stream {
return newGCFB(block, key, iv, false)
}
// NewGCFBDecrypter returns a Stream which decrypts with cipher feedback mode,
// using the given Block. The iv must be the same length as the Block's block
// size.
func NewGCFBDecrypter(block GCFBCipherFunc, key, iv []byte) cipher.Stream {
return newGCFB(block, key, iv, true)
}
func newGCFB(block GCFBCipherFunc, key, iv []byte, decrypt bool) cipher.Stream {
cip, err := block(key)
if err != nil {
panic("cryptobin/gcfb.newGCFB: invalid block")
}
blockSize := cip.BlockSize()
if len(iv) != blockSize {
// stack trace will indicate whether it was de or encryption
panic("cryptobin/gcfb.newGCFB: IV length must equal block size")
}
x := &gcfb{
b: block,
key: make([]byte, len(key)),
iv: make([]byte, blockSize),
bs: blockSize,
counter: 0,
decrypt: decrypt,
}
if x.decrypt {
x.cfbEngine = cipher.NewCFBDecrypter(cip, iv)
} else {
x.cfbEngine = cipher.NewCFBEncrypter(cip, iv)
}
copy(x.key, key)
copy(x.iv, iv)
return x
}