Skip to content

Commit

Permalink
Fix client and add server
Browse files Browse the repository at this point in the history
 - Upgraded the project to latest go version and go modules.
 - Added github action to launch tests.
 - Fixed client using retrial workaround.
 - Added server side code, still not working though.
  • Loading branch information
igolaizola committed Jul 11, 2024
1 parent c5b10c4 commit d1d1515
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 20 deletions.
26 changes: 26 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: ci

on:
push:
branches:
- master
pull_request:
branches:
- master

jobs:
ci:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup go
uses: actions/setup-go@v4
with:
go-version-file: 'go.mod'
- name: Build
run: go build -v ./...
- name: Lint
uses: golangci/golangci-lint-action@v3
- name: Test
run: go test -v ./...
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module github.com/igolaizola/resume-tls

go 1.22.2
57 changes: 40 additions & 17 deletions resumetls.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,12 @@ import (
"crypto/rand"
"crypto/tls"
"io"
"io/ioutil"
"net"
"reflect"

intio "github.com/igarciaolaizola/resume-tls/internal/io"
intnet "github.com/igarciaolaizola/resume-tls/internal/net"
intref "github.com/igarciaolaizola/resume-tls/internal/reflect"
intio "github.com/igolaizola/resume-tls/internal/io"
intnet "github.com/igolaizola/resume-tls/internal/net"
intref "github.com/igolaizola/resume-tls/internal/reflect"
)

// State is buffered handshake data
Expand All @@ -32,16 +31,40 @@ type Conn struct {
*tls.Conn
}

// Client returs a resumable tls conn
// Client returns a resumable tls client conn
func Client(conn net.Conn, cfg *tls.Config, state *State) (*Conn, error) {
return newConn(tls.Client, conn, cfg, state)
}

// Server returns a resumable tls server conn
func Server(conn net.Conn, cfg *tls.Config, state *State) (*Conn, error) {
return newConn(tls.Server, conn, cfg, state)
}

// newConn returns a resumable tls conn
func newConn(tlsConn func(net.Conn, *tls.Config) *tls.Conn, conn net.Conn, cfg *tls.Config, state *State) (*Conn, error) {
if state != nil {
return clientResume(conn, cfg, state)
var err error
// Client hello message sometimes consumes more random bytes than the
// ones provided by the state. Probably due to how elliptic curves keys
// are generated.
// We have tested empirically that retrying 10 times is enough to get a
// successful handshake. Here, we set the limit to 20 to be on the safe
// side.
for i := 0; i < 20; i++ {
var c *Conn
c, err = resume(tlsConn, conn, cfg, state)
if err == nil {
return c, err
}
}
return nil, err
}
return clientInitialize(conn, cfg), nil
return initialize(tlsConn, conn, cfg), nil
}

// client initializes a resumable TLS client conn
func clientInitialize(conn net.Conn, cfg *tls.Config) *Conn {
// initializes a resumable TLS client conn
func initialize(tlsConn func(net.Conn, *tls.Config) *tls.Conn, conn net.Conn, cfg *tls.Config) *Conn {
connBuf := &bytes.Buffer{}
randBuf := &bytes.Buffer{}

Expand All @@ -64,12 +87,12 @@ func clientInitialize(conn net.Conn, cfg *tls.Config) *Conn {
overrideRand: ovRand,
connBuffer: connBuf,
randBuffer: randBuf,
Conn: tls.Client(ovConn, cfg),
Conn: tlsConn(ovConn, cfg),
}
}

// clientResume resumes a resumable TLS client conn
func clientResume(conn net.Conn, cfg *tls.Config, state *State) (*Conn, error) {
// resume resumes a resumable TLS client conn
func resume(tlsConn func(net.Conn, *tls.Config) *tls.Conn, conn net.Conn, cfg *tls.Config, state *State) (*Conn, error) {
rnd := cfg.Rand
if rnd == nil {
rnd = rand.Reader
Expand All @@ -81,24 +104,24 @@ func clientResume(conn net.Conn, cfg *tls.Config, state *State) (*Conn, error) {
ovConn := &intnet.OverrideConn{
Conn: conn,
OverrideReader: io.MultiReader(bytes.NewBuffer(state.conn), conn),
OverrideWriter: ioutil.Discard,
OverrideWriter: io.Discard,
}
cfg.Rand = ovRand

cli := tls.Client(ovConn, cfg)
if err := cli.Handshake(); err != nil {
c := tlsConn(ovConn, cfg)
if err := c.Handshake(); err != nil {
return nil, err
}
ovRand.OverrideReader = nil
ovConn.OverrideReader = nil
ovConn.OverrideWriter = nil
setSeq(cli, state.inSeq, state.outSeq)
setSeq(c, state.inSeq, state.outSeq)

return &Conn{
handshaked: true,
connBuffer: bytes.NewBuffer(state.conn),
randBuffer: bytes.NewBuffer(state.rand),
Conn: cli,
Conn: c,
}, nil
}

Expand Down
106 changes: 103 additions & 3 deletions resumetls_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ fi06KUiLh/4rJtf2wph2wN8SPAY4yQkopFlDYTJNmhhYsKTGIhrpww==
-----END RSA PRIVATE KEY-----`

func TestClient(t *testing.T) {
for i := 0; i < 100; i++ {
testClient(t)
}
}

func testClient(t *testing.T) {
sConn, cConn := net.Pipe()

pair, err := tls.X509KeyPair([]byte(cert), []byte(key))
Expand All @@ -77,18 +83,18 @@ func TestClient(t *testing.T) {
// Launch server in another goroutine
go func() {
if err := srv.Handshake(); err != nil {
t.Fatal(err)
panic(err)
}
// Loop of read write
for i := 0; i < 2; i++ {
recv := make([]byte, 1024)
n, err := srv.Read(recv)
if err != nil {
t.Fatal(err)
panic(err)
}

if _, err := srv.Write(recv[:n]); err != nil {
t.Fatal(err)
panic(err)
}
}
}()
Expand Down Expand Up @@ -140,3 +146,97 @@ func TestClient(t *testing.T) {
t.Errorf("messages missmatch: %s != %s", message, recv[:n])
}
}

func TestServer(t *testing.T) {
for i := 0; i < 100; i++ {
testServer(t)
}
}

func testServer(t *testing.T) {
sConn, cConn := net.Pipe()

pair, err := tls.X509KeyPair([]byte(cert), []byte(key))
if err != nil {
t.Fatal(err)
}

cli := tls.Client(sConn, &tls.Config{
InsecureSkipVerify: true,
})

srv, err := Server(cConn, &tls.Config{
InsecureSkipVerify: true,
Certificates: []tls.Certificate{pair},
}, nil)
if err != nil {
t.Fatal(err)
}

// Launch client in another goroutine
go func() {
if err := cli.Handshake(); err != nil {
panic(err)
}
// Loop of read write
for i := 0; i < 2; i++ {
recv := make([]byte, 1024)
n, err := cli.Read(recv)
if err != nil {
panic(err)
}

if _, err := cli.Write(recv[:n]); err != nil {
panic(err)
}
}
}()

// Initial handshake
if err := srv.Handshake(); err != nil {
t.Fatal(err)
}

// Test write and read
message := []byte("Hello")
if _, err := srv.Write(message); err != nil {
t.Fatal(err)
}

recv := make([]byte, 1024)
n, err := srv.Read(recv)
if err != nil {
t.Fatal(err)
}

if !bytes.Equal(message, recv[:n]) {
t.Errorf("messages missmatch: %s != %s", message, recv[:n])
}

// Extract TLS state
state := srv.State()

// Resume server
srv2, err := Server(cConn, &tls.Config{
InsecureSkipVerify: true,
Certificates: []tls.Certificate{pair},
}, state)
if err != nil {
t.Fatal(err)
}

// Test write and read on resumed client
if _, err := srv2.Write(message); err != nil {
t.Fatal(err)
}

recv = make([]byte, 1024)
n, err = srv.Read(recv)
if err != nil {
t.Fatal(err)
}

if !bytes.Equal(message, recv[:n]) {
t.Errorf("messages missmatch: %s != %s", message, recv[:n])
}
}

0 comments on commit d1d1515

Please sign in to comment.