xref: /openbsd/regress/lib/libtls/gotls/tls.go (revision 7f9db3f5)
1// Package tls provides a Go interface to the libtls library.
2package tls
3
4/*
5#cgo LDFLAGS: -ltls -lssl -lcrypto
6
7#include <stdlib.h>
8
9#include <tls.h>
10
11typedef void *tls;
12*/
13import "C"
14
15import (
16	"errors"
17	"fmt"
18	"time"
19	"unsafe"
20)
21
22var (
23	errWantPollIn  = errors.New("want poll in")
24	errWantPollOut = errors.New("want poll out")
25)
26
27// ProtocolVersion represents a TLS protocol version.
28type ProtocolVersion uint32
29
30// String returns the string representation of a protocol version.
31func (pv ProtocolVersion) String() string {
32	name, ok := protocolNames[pv]
33	if !ok {
34		return fmt.Sprintf("unknown protocol version %x", uint32(pv))
35	}
36	return name
37}
38
39const (
40	ProtocolTLSv10 ProtocolVersion = C.TLS_PROTOCOL_TLSv1_0
41	ProtocolTLSv11 ProtocolVersion = C.TLS_PROTOCOL_TLSv1_1
42	ProtocolTLSv12 ProtocolVersion = C.TLS_PROTOCOL_TLSv1_2
43	ProtocolTLSv13 ProtocolVersion = C.TLS_PROTOCOL_TLSv1_3
44	ProtocolsAll   ProtocolVersion = C.TLS_PROTOCOLS_ALL
45)
46
47var protocolNames = map[ProtocolVersion]string{
48	ProtocolTLSv10: "TLSv1",
49	ProtocolTLSv11: "TLSv1.1",
50	ProtocolTLSv12: "TLSv1.2",
51	ProtocolTLSv13: "TLSv1.3",
52	ProtocolsAll:   "all",
53}
54
55// ProtocolVersionFromString returns the protocol version with the given name.
56func ProtocolVersionFromString(version string) (ProtocolVersion, error) {
57	for proto, name := range protocolNames {
58		if version == name {
59			return proto, nil
60		}
61	}
62	return 0, fmt.Errorf("unknown protocol version %q", version)
63}
64
65// TLSConfig provides configuration options for a TLS context.
66type TLSConfig struct {
67	tlsCfg *C.struct_tls_config
68}
69
70// TLS encapsulates the TLS context.
71type TLS struct {
72	cfg *TLSConfig
73	ctx *C.struct_tls
74}
75
76// Init initialises the TLS library.
77func Init() error {
78	if C.tls_init() != 0 {
79		return errors.New("initialisation failed")
80	}
81	return nil
82}
83
84// NewConfig returns a new TLS configuration.
85func NewConfig() (*TLSConfig, error) {
86	cfg := C.tls_config_new()
87	if cfg == nil {
88		return nil, errors.New("failed to allocate config")
89	}
90	return &TLSConfig{
91		tlsCfg: cfg,
92	}, nil
93}
94
95// Error returns the error message from the TLS configuration.
96func (c *TLSConfig) Error() error {
97	if msg := C.tls_config_error(c.tlsCfg); msg != nil {
98		return errors.New(C.GoString(msg))
99	}
100	return errors.New("unknown error")
101}
102
103// SetCAFile sets the CA file to be used for connections.
104func (c *TLSConfig) SetCAFile(filename string) error {
105	caFile := C.CString(filename)
106	defer C.free(unsafe.Pointer(caFile))
107	if C.tls_config_set_ca_file(c.tlsCfg, caFile) != 0 {
108		return c.Error()
109	}
110	return nil
111}
112
113// SetCiphers sets the cipher suites enabled for the connection.
114func (c *TLSConfig) SetCiphers(ciphers string) error {
115	cipherStr := C.CString(ciphers)
116	defer C.free(unsafe.Pointer(cipherStr))
117	if C.tls_config_set_ciphers(c.tlsCfg, cipherStr) != 0 {
118		return c.Error()
119	}
120	return nil
121}
122
123// SetProtocols sets the protocol versions enabled for the connection.
124func (c *TLSConfig) SetProtocols(proto ProtocolVersion) error {
125	if C.tls_config_set_protocols(c.tlsCfg, C.uint32_t(proto)) != 0 {
126		return c.Error()
127	}
128	return nil
129}
130
131// InsecureNoVerifyCert disables certificate verification for the connection.
132func (c *TLSConfig) InsecureNoVerifyCert() {
133	C.tls_config_insecure_noverifycert(c.tlsCfg)
134}
135
136// InsecureNoVerifyName disables server name verification for the connection.
137func (c *TLSConfig) InsecureNoVerifyName() {
138	C.tls_config_insecure_noverifyname(c.tlsCfg)
139}
140
141// SetSecure enables verification for the connection.
142func (c *TLSConfig) SetVerify() {
143	C.tls_config_verify(c.tlsCfg)
144}
145
146// Free frees resources associated with the TLS configuration.
147func (c *TLSConfig) Free() {
148	if c.tlsCfg == nil {
149		return
150	}
151	C.tls_config_free(c.tlsCfg)
152	c.tlsCfg = nil
153}
154
155// NewClient returns a new TLS client context, using the optional configuration.
156// If no configuration is specified the default configuration will be used.
157func NewClient(config *TLSConfig) (*TLS, error) {
158	var sslCfg *C.struct_tls_config
159	if config != nil {
160		sslCfg = config.tlsCfg
161	}
162	ctx := C.tls_client()
163	if ctx == nil {
164		return nil, errors.New("tls client failed")
165	}
166	if C.tls_configure(ctx, sslCfg) != 0 {
167		return nil, errors.New("tls configure failed")
168	}
169	return &TLS{
170		cfg: config,
171		ctx: ctx,
172	}, nil
173}
174
175// Error returns the error message from the TLS context.
176func (t *TLS) Error() error {
177	if msg := C.tls_error(t.ctx); msg != nil {
178		return errors.New(C.GoString(msg))
179	}
180	return errors.New("unknown error")
181}
182
183// PeerCertProvided returns whether the peer provided a certificate.
184func (t *TLS) PeerCertProvided() bool {
185	return C.tls_peer_cert_provided(t.ctx) == 1
186}
187
188// PeerCertContainsName checks whether the peer certificate contains
189// the specified name.
190func (t *TLS) PeerCertContainsName(name string) bool {
191	n := C.CString(name)
192	defer C.free(unsafe.Pointer(n))
193	return C.tls_peer_cert_contains_name(t.ctx, n) == 1
194}
195
196// PeerCertIssuer returns the issuer of the peer certificate.
197func (t *TLS) PeerCertIssuer() (string, error) {
198	issuer := C.tls_peer_cert_issuer(t.ctx)
199	if issuer == nil {
200		return "", errors.New("no issuer returned")
201	}
202	return C.GoString(issuer), nil
203}
204
205// PeerCertCommonName returns the common name of the peer certificate.
206func (t *TLS) PeerCertCommonName() (string, error) {
207	commonName := C.tls_peer_cert_common_name(t.ctx)
208	if commonName == nil {
209		return "", errors.New("no peer cert common name returned")
210	}
211	return C.GoString(commonName), nil
212}
213
214// PeerCertSubject returns the subject of the peer certificate.
215func (t *TLS) PeerCertSubject() (string, error) {
216	subject := C.tls_peer_cert_subject(t.ctx)
217	if subject == nil {
218		return "", errors.New("no subject returned")
219	}
220	return C.GoString(subject), nil
221}
222
223// PeerCertHash returns a hash of the peer certificate.
224func (t *TLS) PeerCertHash() (string, error) {
225	hash := C.tls_peer_cert_hash(t.ctx)
226	if hash == nil {
227		return "", errors.New("no hash returned")
228	}
229	return C.GoString(hash), nil
230}
231
232// PeerCertNotBefore returns the notBefore time from the peer
233// certificate.
234func (t *TLS) PeerCertNotBefore() (time.Time, error) {
235	notBefore := C.tls_peer_cert_notbefore(t.ctx)
236	if notBefore == -1 {
237		return time.Time{}, errors.New("no notBefore time returned")
238	}
239	return time.Unix(int64(notBefore), 0), nil
240}
241
242// PeerCertNotAfter returns the notAfter time from the peer
243// certificate.
244func (t *TLS) PeerCertNotAfter() (time.Time, error) {
245	notAfter := C.tls_peer_cert_notafter(t.ctx)
246	if notAfter == -1 {
247		return time.Time{}, errors.New("no notAfter time")
248	}
249	return time.Unix(int64(notAfter), 0), nil
250}
251
252// ConnVersion returns the protocol version of the connection.
253func (t *TLS) ConnVersion() (ProtocolVersion, error) {
254	ver := C.tls_conn_version(t.ctx)
255	if ver == nil {
256		return 0, errors.New("no connection version")
257	}
258	return ProtocolVersionFromString(C.GoString(ver))
259}
260
261// ConnCipher returns the cipher suite used for the connection.
262func (t *TLS) ConnCipher() (string, error) {
263	cipher := C.tls_conn_cipher(t.ctx)
264	if cipher == nil {
265		return "", errors.New("no connection cipher")
266	}
267	return C.GoString(cipher), nil
268}
269
270// ConnCipherStrength returns the strength in bits for the symmetric
271// cipher that is used for the connection.
272func (t *TLS) ConnCipherStrength() (int, error) {
273	strength := C.tls_conn_cipher_strength(t.ctx)
274	if strength == 0 {
275		return 0, errors.New("no connection cipher strength")
276	}
277	return int(strength), nil
278}
279
280// Connect attempts to establish an TLS connection to the specified host on
281// the given port. The host may optionally contain a colon separated port
282// value if the port string is specified as an empty string.
283func (t *TLS) Connect(host, port string) error {
284	h := C.CString(host)
285	var p *C.char
286	if port != "" {
287		p = C.CString(port)
288	}
289	defer C.free(unsafe.Pointer(h))
290	defer C.free(unsafe.Pointer(p))
291	if C.tls_connect(t.ctx, h, p) != 0 {
292		return t.Error()
293	}
294	return nil
295}
296
297// Handshake attempts to complete the TLS handshake.
298func (t *TLS) Handshake() error {
299	ret := C.tls_handshake(t.ctx)
300	switch {
301	case ret == C.TLS_WANT_POLLIN:
302		return errWantPollIn
303	case ret == C.TLS_WANT_POLLOUT:
304		return errWantPollOut
305	case ret != 0:
306		return t.Error()
307	}
308	return nil
309}
310
311// Read reads data the TLS connection into the given buffer.
312func (t *TLS) Read(buf []byte) (int, error) {
313	ret := C.tls_read(t.ctx, unsafe.Pointer(&buf[0]), C.size_t(len(buf)))
314	switch {
315	case ret == C.TLS_WANT_POLLIN:
316		return -1, errWantPollIn
317	case ret == C.TLS_WANT_POLLOUT:
318		return -1, errWantPollOut
319	case ret < 0:
320		return -1, t.Error()
321	}
322	return int(ret), nil
323}
324
325// Write writes the given data to the TLS connection.
326func (t *TLS) Write(buf []byte) (int, error) {
327	p := C.CString(string(buf))
328	defer C.free(unsafe.Pointer(p))
329	ret := C.tls_write(t.ctx, unsafe.Pointer(p), C.size_t(len(buf)))
330	switch {
331	case ret == C.TLS_WANT_POLLIN:
332		return -1, errWantPollIn
333	case ret == C.TLS_WANT_POLLOUT:
334		return -1, errWantPollOut
335	case ret < 0:
336		return -1, t.Error()
337	}
338	return int(ret), nil
339}
340
341// Close closes the TLS connection.
342func (t *TLS) Close() error {
343	ret := C.tls_close(t.ctx)
344	switch {
345	case ret == C.TLS_WANT_POLLIN:
346		return errWantPollIn
347	case ret == C.TLS_WANT_POLLOUT:
348		return errWantPollOut
349	case ret != 0:
350		return t.Error()
351	}
352	return nil
353}
354
355// Free frees resources associated with the TLS context.
356func (t *TLS) Free() {
357	if t.ctx == nil {
358		return
359	}
360	C.tls_free(t.ctx)
361	t.ctx = nil
362}
363