Documentation
¶
Overview ¶
Package gostls implements a TLS 1.2 client with a crypto/tls-shaped API.
This implementation is TLS 1.2 only. TLS 1.3, session resumption, renegotiation, ALPN extensibility, and 0-RTT are not supported. There is no server role.
Cipher suite availability: AES-128-CBC, AES-256-CBC, AES-128-GCM, AES-256-GCM, and CHACHA20-POLY1305 (RFC 7905) suites are included by default. GOST suites are registered in every build (clean-room backend) but are not in the default negotiation set; request them explicitly via Config.CipherSuites.
Example ¶
Example dials a TLS 1.2 server, completes the handshake, and exchanges application data. The server here is an in-process crypto/tls echo server using a freshly generated CA, so the example is self-contained.
package main
import (
"context"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"io"
"log"
"math/big"
"net"
"time"
stdtls "crypto/tls"
"github.com/bigbes/gostls"
)
func main() {
srv := newExampleServer()
defer srv.Close()
d := &gostls.Dialer{
Config: &gostls.Config{
// Trust only the example CA. In production, omit RootCAs/RootCAPEMs
// to use the host's system trust store.
RootCAPEMs: [][]byte{srv.CACertPEM},
ServerName: "test.example.com",
},
}
conn, err := d.DialContext(context.Background(), "tcp", srv.Addr)
if err != nil {
log.Fatalf("dial: %v", err)
}
defer func() { _ = conn.Close() }()
if _, err := conn.Write([]byte("ping")); err != nil {
log.Fatalf("write: %v", err)
}
buf := make([]byte, 4)
if _, err := io.ReadFull(conn, buf); err != nil {
log.Fatalf("read: %v", err)
}
fmt.Printf("server echoed: %s\n", buf)
}
type exampleServer struct {
Addr string
CACertPEM []byte
listener net.Listener
}
func (s *exampleServer) Close() { _ = s.listener.Close() }
// newExampleServer generates a CA + leaf certificate for "test.example.com"
// and starts a TLS 1.2 listener that echoes whatever it receives. It panics on
// any error, which is acceptable for an example.
func newExampleServer() *exampleServer {
caKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
panic(err)
}
caTmpl := &x509.Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{CommonName: "Example CA"},
NotBefore: time.Now().Add(-time.Hour),
NotAfter: time.Now().Add(time.Hour),
IsCA: true,
BasicConstraintsValid: true,
KeyUsage: x509.KeyUsageCertSign,
}
caDER, err := x509.CreateCertificate(rand.Reader, caTmpl, caTmpl, &caKey.PublicKey, caKey)
if err != nil {
panic(err)
}
caCert, err := x509.ParseCertificate(caDER)
if err != nil {
panic(err)
}
leafKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
panic(err)
}
leafTmpl := &x509.Certificate{
SerialNumber: big.NewInt(2),
Subject: pkix.Name{CommonName: "test.example.com"},
DNSNames: []string{"test.example.com"},
NotBefore: time.Now().Add(-time.Hour),
NotAfter: time.Now().Add(time.Hour),
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
}
leafDER, err := x509.CreateCertificate(rand.Reader, leafTmpl, caCert, &leafKey.PublicKey, caKey)
if err != nil {
panic(err)
}
cfg := &stdtls.Config{
Certificates: []stdtls.Certificate{{
Certificate: [][]byte{leafDER},
PrivateKey: leafKey,
}},
MinVersion: stdtls.VersionTLS12,
MaxVersion: stdtls.VersionTLS12,
}
ln, err := stdtls.Listen("tcp", "127.0.0.1:0", cfg)
if err != nil {
panic(err)
}
go func() {
for {
c, acceptErr := ln.Accept()
if acceptErr != nil {
return
}
go func(conn net.Conn) {
defer func() { _ = conn.Close() }()
buf := make([]byte, 4096)
for {
n, readErr := conn.Read(buf)
if readErr != nil {
return
}
if _, writeErr := conn.Write(buf[:n]); writeErr != nil {
return
}
}
}(c)
}
}()
return &exampleServer{
Addr: ln.Addr().String(),
CACertPEM: pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: caDER}),
listener: ln,
}
}
Output: server echoed: ping
Index ¶
- func BackendTag() string
- func LookupSuiteByName(name string) (uint16, bool)
- type Certificate
- type Config
- type Conn
- func (c *Conn) Close() error
- func (c *Conn) Handshake() error
- func (c *Conn) LocalAddr() net.Addr
- func (c *Conn) Read(b []byte) (int, error)
- func (c *Conn) RemoteAddr() net.Addr
- func (c *Conn) SetDeadline(t time.Time) error
- func (c *Conn) SetReadDeadline(t time.Time) error
- func (c *Conn) SetWriteDeadline(t time.Time) error
- func (c *Conn) Write(b []byte) (int, error)
- type ConnectionState
- type Dialer
- type SuiteInfo
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func BackendTag ¶
func BackendTag() string
BackendTag returns a string identifying the TLS backend compiled into this binary: "default" for the pure-Go backend, "openssl" for the CGO backend.
Example ¶
ExampleBackendTag reports which TLS backend is compiled in. The pure-Go gostls module always reports "default"; the cgo OpenSSL backend (in the separate dialer module) reports "openssl".
package main
import (
"fmt"
"github.com/bigbes/gostls"
)
func main() {
fmt.Println(gostls.BackendTag())
}
Output: default
func LookupSuiteByName ¶
LookupSuiteByName returns the IANA ID and true for the named suite, or 0 and false if the name is not registered.
Example ¶
ExampleLookupSuiteByName resolves a suite name to its IANA cipher suite ID.
package main
import (
"fmt"
"github.com/bigbes/gostls"
)
func main() {
id, ok := gostls.LookupSuiteByName("ECDHE-RSA-AES128-GCM-SHA256")
fmt.Printf("0x%04X %t\n", id, ok)
_, ok = gostls.LookupSuiteByName("NO-SUCH-SUITE")
fmt.Println(ok)
}
Output: 0xC02F true false
Types ¶
type Certificate ¶
type Certificate struct {
// Certificate is the parsed certificate (optional; used for display only).
Certificate *x509.Certificate
// RawCertificate holds the DER-encoded certificate.
RawCertificate []byte
// PrivateKey is the private key corresponding to the certificate's public
// key. Required when using mutual TLS (client authentication).
PrivateKey crypto.PrivateKey
}
Certificate holds a certificate and its private key. Mirrors crypto/tls.Certificate in structure.
type Config ¶
type Config struct {
// RootCAs defines the set of root certificate authorities used to verify
// the server's certificate. If nil, the host's default root CA set is used.
//
// RootCAs and RootCAPEMs are mutually exclusive. Setting both is an error.
RootCAs *x509.CertPool
// RootCAPEMs holds PEM-encoded trusted root certificates. Each element may
// be a single PEM block or a concatenation of multiple PEM blocks. Only
// CERTIFICATE-typed blocks are used; other block types (keys, etc.) are
// ignored, but each element must contain at least one CERTIFICATE block.
//
// This field is the preferred way to supply an in-memory trust store when
// using the openssl backend, where RootCAs is not supported.
//
// RootCAs and RootCAPEMs are mutually exclusive. Setting both is an error.
RootCAPEMs [][]byte
// GOSTRoots is the trust store for GOST-signed server certificates. The
// stdlib pools (RootCAs/RootCAPEMs) cannot verify GOST signatures, so a
// GOST trust anchor must be supplied here as a parsed x509gost certificate.
// If the server presents a GOST-signed certificate and GOSTRoots is empty,
// verification fails (unless InsecureSkipVerify). It is not consulted for
// non-GOST certificates.
GOSTRoots []*x509gost.Certificate
// GOSTIntermediates optionally bridges a GOST-signed leaf to a root in
// GOSTRoots through intermediate CA certificates. Leave nil for a direct
// leaf-signed-by-root chain.
GOSTIntermediates []*x509gost.Certificate
// Certificates contains client certificates for mutual TLS (mTLS) client
// authentication. When the server sends a CertificateRequest during the
// handshake, the first entry in this slice is offered. If the server's
// requested signature algorithms do not include any algorithm supported by
// the key, an empty Certificate message is sent (which is valid per RFC 5246).
//
// Each Certificate.PrivateKey must be *rsa.PrivateKey or *ecdsa.PrivateKey;
// any other type causes Handshake() to return a hard error. If
// Certificate.Certificate is nil, RawCertificate is parsed on demand; an
// empty RawCertificate is a hard error.
Certificates []Certificate
// ServerName is the server name sent in the SNI extension and used for
// certificate verification. If empty and InsecureSkipVerify is false,
// certificate verification will fail because x509 requires a DNS name.
ServerName string
// CipherSuites specifies the list of cipher suite IDs to offer in the
// ClientHello. If nil, all registered suites whose cipher is AES-128-CBC,
// AES-256-CBC, AES-128-GCM, AES-256-GCM, or CHACHA20-POLY1305 are offered.
CipherSuites []uint16
// InsecureSkipVerify controls whether the client verifies the server's
// certificate chain and host name.
//
// WARNING: Setting this to true disables all certificate authentication.
// The connection is susceptible to man-in-the-middle attacks. Use only
// for debugging or with VerifyPeerCertificate performing custom verification.
InsecureSkipVerify bool
// VerifyPeerCertificate is called after normal certificate verification
// (or instead of it if InsecureSkipVerify is true). The first argument is
// the raw DER bytes of the certificates provided by the server. The second
// is the verified chains (empty when InsecureSkipVerify is true).
//
// A non-nil error aborts the handshake with bad_certificate.
VerifyPeerCertificate func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error
// Rand provides the source of entropy for random values used during the
// handshake: client random, client ephemeral keys, RSA pre-master secret,
// and CBC per-record IVs.
//
// If nil, crypto/rand.Reader is used (the correct default for production).
// Override in tests to produce deterministic handshake bytes.
Rand io.Reader
}
Config is the TLS 1.2 client configuration. Field names and semantics mirror crypto/tls.Config where the feature exists in stdlib.
Only a subset of crypto/tls.Config is implemented.
Example (CipherSuites) ¶
ExampleConfig_cipherSuites shows how to request a specific cipher suite — here a GOST suite, which is registered in every build but is not offered by default. Resolve the IANA ID by name and set it on Config.CipherSuites.
package main
import (
"fmt"
"log"
"github.com/bigbes/gostls"
)
func main() {
id, ok := gostls.LookupSuiteByName("GOST2012-KUZNYECHIK-KUZNYECHIKOMAC")
if !ok {
log.Fatal("GOST suite not registered")
}
cfg := &gostls.Config{
ServerName: "gost.example.com",
CipherSuites: []uint16{id}, // offer only this suite.
}
fmt.Printf("offering 0x%04X (%d suite)\n", cfg.CipherSuites[0], len(cfg.CipherSuites))
}
Output: offering 0xC100 (1 suite)
type Conn ¶
type Conn struct {
// contains filtered or unexported fields
}
Conn is a TLS 1.2 client connection implementing net.Conn.
The zero value is not usable; create one with NewConn or Dialer.DialContext.
func NewConn ¶
NewConn creates a Conn wrapping the given net.Conn with the provided Config. This is the primary way to create a *Conn directly — Dialer.DialContext also creates one internally.
func (*Conn) Close ¶
Close sends a close_notify alert (when the handshake completed) and closes the underlying connection. It never waits on an in-flight handshake: closing the transport unblocks a handshake currently blocked on I/O, which then returns an error. This is why the decision below uses the handshakeOK flag rather than driving handshakeOnce (which would block).
func (*Conn) Handshake ¶
Handshake runs the TLS 1.2 client handshake if it has not been run yet. It is called automatically by Read and Write if needed.
func (*Conn) Read ¶
Read reads decrypted application data from the connection. It triggers the handshake if needed.
func (*Conn) RemoteAddr ¶
RemoteAddr returns the remote network address.
func (*Conn) SetDeadline ¶
SetDeadline sets the read and write deadlines on the underlying connection.
func (*Conn) SetReadDeadline ¶
SetReadDeadline sets the deadline for future Read calls.
func (*Conn) SetWriteDeadline ¶
SetWriteDeadline sets the deadline for future Write calls.
type ConnectionState ¶
type ConnectionState struct {
// Version is the TLS version used; always 0x0303 (TLS 1.2).
Version uint16
// CipherSuite is the IANA cipher suite ID negotiated.
CipherSuite uint16
// Suite is the full suite descriptor, or nil before handshake.
Suite *suites.Suite
}
ConnectionState holds information about a TLS connection.
type Dialer ¶
type Dialer struct {
// NetDialer is the underlying network dialer used to establish the TCP
// (or other) connection before the TLS handshake. If nil, a zero-value
// net.Dialer is used.
NetDialer *net.Dialer
// Config is the TLS configuration applied to each new connection.
// If nil, a zero-value Config is used (which requires InsecureSkipVerify
// or a matching RootCAs + ServerName to succeed).
Config *Config
}
Dialer dials TLS connections. Its API mirrors crypto/tls.Dialer.
func (*Dialer) DialContext ¶
DialContext connects to addr using the network dialer, then performs a TLS handshake and returns the fully negotiated connection.
The returned net.Conn is a *Conn. The handshake is completed before DialContext returns.
ctx controls the lifetime of the dial; it is not propagated to subsequent Read/Write calls on the returned Conn.
gostls is the pure-Go TLS backend (CGO_ENABLED=0 compatible). The OpenSSL backend lives in the separate go-tlsdialer module, which selects between gostls and OpenSSL.
type SuiteInfo ¶
type SuiteInfo struct {
// ID is the IANA TLS cipher suite number.
ID uint16
// Name is the OpenSSL-style name (e.g. "ECDHE-RSA-AES128-GCM-SHA256").
Name string
}
SuiteInfo is a minimal descriptor of a registered TLS cipher suite, exported for use by callers outside this module's internal packages.
func AllSuites ¶
func AllSuites() []SuiteInfo
AllSuites returns info about every registered cipher suite. The order is stable but unspecified (registration order).
Example ¶
ExampleAllSuites iterates the registered cipher suites. The order is stable but unspecified, so this example reports whether a known suite is present rather than printing the whole list.
package main
import (
"fmt"
"github.com/bigbes/gostls"
)
func main() {
want := "GOST2012-MAGMA-MAGMAOMAC"
found := false
for _, s := range gostls.AllSuites() {
if s.Name == want {
found = true
break
}
}
fmt.Printf("%s registered: %t\n", want, found)
}
Output: GOST2012-MAGMA-MAGMAOMAC registered: true
Directories
¶
| Path | Synopsis |
|---|---|
|
internal
|
|
|
handshake
Package handshake provides TLS 1.2 handshake message marshaling/unmarshaling and the multi-hash transcript accumulator (RFC 5246 §7.4).
|
Package handshake provides TLS 1.2 handshake message marshaling/unmarshaling and the multi-hash transcript accumulator (RFC 5246 §7.4). |
|
ke
Package ke implements TLS 1.2 key exchange methods.
|
Package ke implements TLS 1.2 key exchange methods. |
|
suites
Package suites defines the TLS 1.2 cipher suite registry, PRF, and key schedule for github.com/bigbes/gostls.
|
Package suites defines the TLS 1.2 cipher suite registry, PRF, and key schedule for github.com/bigbes/gostls. |