-
Notifications
You must be signed in to change notification settings - Fork 17
/
Copy pathcshake.go
221 lines (197 loc) · 5.91 KB
/
cshake.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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
//go:build !cmd_go_bootstrap
package openssl
// #include "goopenssl.h"
import "C"
import (
"runtime"
"strconv"
"sync"
"unsafe"
)
// shakeOneShot applies the SHAKE extendable output function to data and
// writes the output to out.
func shakeOneShot(secuirtyBits int, data []byte, out []byte) {
// Can't use EVP_Digest because it doesn't support output lengths
// larger than the block size, while crypto/sha3 supports any length.
alg := loadShake(secuirtyBits)
if alg == nil {
panic("openssl: unsupported SHAKE" + strconv.Itoa(secuirtyBits) + " function")
}
ctx := C.go_openssl_EVP_MD_CTX_new()
if ctx == nil {
panic(newOpenSSLError("EVP_MD_CTX_new"))
}
defer C.go_openssl_EVP_MD_CTX_free(ctx)
if C.go_openssl_EVP_DigestInit_ex(ctx, alg.md, nil) != 1 {
panic(newOpenSSLError("EVP_DigestInit_ex"))
}
if C.go_openssl_EVP_DigestUpdate(ctx, unsafe.Pointer(&*addr(data)), C.size_t(len(data))) != 1 {
panic(newOpenSSLError("EVP_DigestUpdate"))
}
if C.go_openssl_EVP_DigestFinalXOF(ctx, (*C.uchar)(unsafe.Pointer(&*addr(out))), C.size_t(len(out))) != 1 {
panic(newOpenSSLError("EVP_DigestFinalXOF"))
}
}
// SumSHAKE128 applies the SHAKE128 extendable output function to data and
// returns an output of the given length in bytes.
func SumSHAKE128(data []byte, length int) []byte {
out := make([]byte, length)
shakeOneShot(128, data, out[:])
return out
}
// SumSHAKE256 applies the SHAKE256 extendable output function to data and
// returns an output of the given length in bytes.
func SumSHAKE256(data []byte, length int) []byte {
out := make([]byte, length)
shakeOneShot(256, data, out[:])
return out
}
// SupportsSHAKE returns true if the SHAKE extendable output functions
// with the given securityBits are supported.
func SupportsSHAKE(securityBits int) bool {
if vMajor == 1 || (vMajor == 3 && vMinor < 3) {
// SHAKE MD's are supported since OpenSSL 1.1.1,
// but EVP_DigestSqueeze is only supported since 3.3,
// and we need it to implement [sha3.SHAKE].
return false
}
return loadShake(securityBits) != nil
}
// SupportsCSHAKE returns true if the CSHAKE extendable output functions
// with the given securityBits are supported.
func SupportsCSHAKE(securityBits int) bool {
return false
}
// SHAKE is an instance of a SHAKE extendable output function.
type SHAKE struct {
alg *shakeAlgorithm
ctx C.GO_EVP_MD_CTX_PTR
lastXofLen int
}
// NewSHAKE128 creates a new SHAKE128 XOF.
func NewSHAKE128() *SHAKE {
return newSHAKE(128)
}
// NewSHAKE256 creates a new SHAKE256 XOF.
func NewSHAKE256() *SHAKE {
return newSHAKE(256)
}
// NewCSHAKE128 creates a new cSHAKE128 XOF.
//
// N is used to define functions based on cSHAKE, it can be empty when plain
// cSHAKE is desired. S is a customization byte string used for domain
// separation. When N and S are both empty, this is equivalent to NewSHAKE128.
func NewCSHAKE128(N, S []byte) *SHAKE {
if len(N) == 0 && len(S) == 0 {
return NewSHAKE128()
}
return nil
}
// NewCSHAKE256 creates a new cSHAKE256 XOF.
//
// N is used to define functions based on cSHAKE, it can be empty when plain
// cSHAKE is desired. S is a customization byte string used for domain
// separation. When N and S are both empty, this is equivalent to NewSHAKE256.
func NewCSHAKE256(N, S []byte) *SHAKE {
if len(N) == 0 && len(S) == 0 {
return NewSHAKE256()
}
return nil
}
func newSHAKE(securityBits int) *SHAKE {
alg := loadShake(securityBits)
if alg == nil {
panic("openssl: unsupported SHAKE" + strconv.Itoa(securityBits) + " function")
}
ctx := C.go_openssl_EVP_MD_CTX_new()
if ctx == nil {
panic(newOpenSSLError("EVP_MD_CTX_new"))
}
if C.go_openssl_EVP_DigestInit_ex(ctx, alg.md, nil) != 1 {
C.go_openssl_EVP_MD_CTX_free(ctx)
panic(newOpenSSLError("EVP_DigestInit_ex"))
}
s := &SHAKE{alg: alg, ctx: ctx}
runtime.SetFinalizer(s, (*SHAKE).finalize)
return s
}
func (s *SHAKE) finalize() {
C.go_openssl_EVP_MD_CTX_free(s.ctx)
}
// Write absorbs more data into the XOF's state.
//
// It panics if any output has already been read.
func (s *SHAKE) Write(p []byte) (n int, err error) {
defer runtime.KeepAlive(s)
if len(p) == 0 {
return 0, nil
}
if C.go_openssl_EVP_DigestUpdate(s.ctx, unsafe.Pointer(&*addr(p)), C.size_t(len(p))) != 1 {
panic(newOpenSSLError("EVP_DigestUpdate"))
}
return len(p), nil
}
// Read squeezes more output from the XOF.
//
// Any call to Write after a call to Read will panic.
func (s *SHAKE) Read(p []byte) (n int, err error) {
defer runtime.KeepAlive(s)
if len(p) == 0 {
return 0, nil
}
if len(p) != s.lastXofLen {
if C.go_openssl_EVP_MD_CTX_ctrl(s.ctx, C.EVP_MD_CTRL_XOF_LEN, C.int(len(p)), nil) != 1 {
panic(newOpenSSLError("EVP_MD_CTX_ctrl"))
}
s.lastXofLen = len(p)
}
if C.go_openssl_EVP_DigestSqueeze(s.ctx, (*C.uchar)(unsafe.Pointer(&*addr(p))), C.size_t(len(p))) != 1 {
panic(newOpenSSLError("EVP_DigestSqueeze"))
}
return len(p), nil
}
// Reset resets the XOF to its initial state.
func (s *SHAKE) Reset() {
defer runtime.KeepAlive(s)
if C.go_openssl_EVP_DigestInit_ex(s.ctx, nil, nil) != 1 {
panic(newOpenSSLError("EVP_DigestInit_ex"))
}
s.lastXofLen = 0
}
// BlockSize returns the rate of the XOF.
func (s *SHAKE) BlockSize() int {
return s.alg.blockSize
}
// cacheSHAKE is a cache of SHAKE XOF length to GO_EVP_MD_PTR.
var cacheSHAKE sync.Map
type shakeAlgorithm struct {
md C.GO_EVP_MD_PTR
blockSize int
}
// loadShake converts a crypto.Hash to a EVP_MD.
func loadShake(securityBits int) (alg *shakeAlgorithm) {
if v, ok := cacheMD.Load(securityBits); ok {
return v.(*shakeAlgorithm)
}
defer func() {
cacheMD.Store(securityBits, alg)
}()
var name *C.char
switch securityBits {
case 128:
name = C.CString("SHAKE-128")
case 256:
name = C.CString("SHAKE-256")
default:
return nil
}
defer C.free(unsafe.Pointer(name))
md := C.go_openssl_EVP_MD_fetch(nil, name, nil)
if md == nil {
return nil
}
alg = new(shakeAlgorithm)
alg.md = md
alg.blockSize = int(C.go_openssl_EVP_MD_get_block_size(md))
return alg
}