-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathinit.go
190 lines (157 loc) · 5.31 KB
/
init.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
// Copyright (C) 2017. See AUTHORS.
//
// 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.
/*
Package openssl is a light wrapper around OpenSSL for Go.
It strives to provide a near-drop-in replacement for the Go standard library
tls package, while allowing for:
# Performance
OpenSSL is battle-tested and optimized C. While Go's built-in library shows
great promise, it is still young and in some places, inefficient. This simple
OpenSSL wrapper can often do at least 2x with the same cipher and protocol.
On my lappytop, I get the following benchmarking speeds:
BenchmarkSHA1Large_openssl 1000 2611282 ns/op 401.56 MB/s
BenchmarkSHA1Large_stdlib 500 3963983 ns/op 264.53 MB/s
BenchmarkSHA1Small_openssl 1000000 3476 ns/op 0.29 MB/s
BenchmarkSHA1Small_stdlib 5000000 550 ns/op 1.82 MB/s
BenchmarkSHA256Large_openssl 200 8085314 ns/op 129.69 MB/s
BenchmarkSHA256Large_stdlib 100 18948189 ns/op 55.34 MB/s
BenchmarkSHA256Small_openssl 1000000 4262 ns/op 0.23 MB/s
BenchmarkSHA256Small_stdlib 1000000 1444 ns/op 0.69 MB/s
BenchmarkOpenSSLThroughput 100000 21634 ns/op 47.33 MB/s
BenchmarkStdlibThroughput 50000 58974 ns/op 17.36 MB/s
# Interoperability
Many systems support OpenSSL with a variety of plugins and modules for things,
such as hardware acceleration in embedded devices.
# Greater flexibility and configuration
OpenSSL allows for far greater configuration of corner cases and backwards
compatibility (such as support of SSLv2). You shouldn't be using SSLv2 if you
can help but, but sometimes you can't help it.
# Security
Yeah yeah, Heartbleed. But according to the author of the standard library's
TLS implementation, Go's TLS library is vulnerable to timing attacks. And
whether or not OpenSSL received the appropriate amount of scrutiny
pre-Heartbleed, it sure is receiving it now.
# Usage
Starting an HTTP server that uses OpenSSL is very easy. It's as simple as:
log.Fatal(openssl.ListenAndServeTLS(
":8443", "my_server.crt", "my_server.key", myHandler))
Getting a net.Listener that uses OpenSSL is also easy:
ctx, err := openssl.NewCtxFromFiles("my_server.crt", "my_server.key")
if err != nil {
log.Fatal(err)
}
l, err := openssl.Listen("tcp", ":7777", ctx)
Making a client connection is straightforward too:
ctx, err := NewCtx()
if err != nil {
log.Fatal(err)
}
err = ctx.LoadVerifyLocations("/etc/ssl/certs/ca-certificates.crt", "")
if err != nil {
log.Fatal(err)
}
conn, err := openssl.Dial("tcp", "localhost:7777", ctx, 0)
Help wanted: To get this library to work with net/http's client, we
had to fork net/http. It would be nice if an alternate http client library
supported the generality needed to use OpenSSL instead of crypto/tls.
*/
package openssl
// #include "shim.h"
import "C"
import (
"errors"
"fmt"
"reflect"
"strings"
"unsafe"
"github.com/hashicorp/go-multierror"
)
func init() {
if rc := C.X_shim_init(); rc != 0 {
panic(fmt.Errorf("x_shim_init failed with %d", rc))
}
loadDefaultProvider()
}
func getOneErrorFromQueue() error {
var (
file *C.char
data *C.char
function *C.char
line C.int
flags C.int
)
err := C.ERR_get_error_all(&file, &line, &function, &data, &flags)
if err == 0 {
return nil
}
return fmt.Errorf(
"%s::%s (%s) in %s:%d::%s",
C.GoString(C.ERR_lib_error_string(err)),
C.GoString(C.ERR_reason_error_string(err)),
C.GoString(data),
C.GoString(file),
line,
C.GoString(function),
)
}
func errorFormatFunction(es []error) string {
if len(es) == 1 {
return fmt.Sprintf("SSL error:\n\t* %s\n\n", es[0])
}
points := make([]string, len(es))
for i, err := range es {
points[i] = fmt.Sprintf("* %s", err)
}
return fmt.Sprintf("SSL errors:\n\t%s\n\n", strings.Join(points, "\n\t"))
}
func getQueuedErrors() error {
var result *multierror.Error
for {
err := getOneErrorFromQueue()
if err == nil {
break
}
result = multierror.Append(result, err)
}
if result != nil {
result.ErrorFormat = errorFormatFunction
}
return result.ErrorOrNil()
}
// errorFromErrorQueue needs to run in the same OS thread as the operation
// that caused the possible error
func errorFromErrorQueue() error {
if err := getQueuedErrors(); err != nil {
return err
}
return errors.New("ssl error")
}
func nonCopyGoBytes(ptr uintptr, length int) []byte {
var slice []byte
header := (*reflect.SliceHeader)(unsafe.Pointer(&slice))
header.Cap = length
header.Len = length
header.Data = ptr
return slice
}
func nonCopyCString(data *C.char, size C.int) []byte {
return nonCopyGoBytes(uintptr(unsafe.Pointer(data)), int(size))
}
// ensureErrorQueueIsClear will only return an error if the error queue is not clear
func ensureErrorQueueIsClear() error {
if err := getQueuedErrors(); err != nil {
return fmt.Errorf("error queue not clear: %w", err)
}
return nil
}