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