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