Skip to content

Commit

Permalink
Add TinyGo support for {DI,TO0,TO1}Server
Browse files Browse the repository at this point in the history
Signed-off-by: Ben Krieger <ben.krieger@intel.com>
  • Loading branch information
ben-krieger committed Oct 9, 2024
1 parent 2a8cf36 commit 580d17d
Show file tree
Hide file tree
Showing 14 changed files with 434 additions and 82 deletions.
2 changes: 1 addition & 1 deletion cbor/cbor.go
Original file line number Diff line number Diff line change
Expand Up @@ -918,7 +918,7 @@ func (d *Decoder) decodeMap(rv reflect.Value, additional []byte) error {

// Clear map
rmap := rv
rmap.Clear()
mapClear(rmap)

// Get key-value types
keyType := rmap.Type().Key()
Expand Down
10 changes: 10 additions & 0 deletions cbor/map.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// SPDX-FileCopyrightText: (C) 2024 Intel Corporation
// SPDX-License-Identifier: Apache 2.0

//go:build !tinygo

package cbor

import "reflect"

func mapClear(rv reflect.Value) { rv.Clear() }
15 changes: 15 additions & 0 deletions cbor/map_tinygo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// SPDX-FileCopyrightText: (C) 2024 Intel Corporation
// SPDX-License-Identifier: Apache 2.0

//go:build tinygo

package cbor

import "reflect"

func mapClear(rv reflect.Value) {
iter := rv.MapRange()
for iter.Next() {
rv.SetMapIndex(iter.Key(), reflect.Value{})
}
}
1 change: 1 addition & 0 deletions examples/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ require (
github.com/fido-device-onboard/go-fdo/tpm v0.0.0-00010101000000-000000000000
github.com/google/go-tpm v0.9.2-0.20240920144513-364d5f2f78b9
github.com/google/go-tpm-tools v0.3.13-0.20230620182252-4639ecce2aba
github.com/syumai/workers v0.26.3
hermannm.dev/devlog v0.4.1
)

Expand Down
2 changes: 2 additions & 0 deletions examples/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/syumai/workers v0.26.3 h1:AF+IBaRccbR4JIj2kNJLJblruPFMD/pAbzkopejGcP8=
github.com/syumai/workers v0.26.3/go.mod h1:ZnqmdiHNBrbxOLrZ/HJ5jzHy6af9cmiNZk10R9NrIEA=
github.com/tetratelabs/wazero v1.8.0 h1:iEKu0d4c2Pd+QSRieYbnQC9yiFlMS9D+Jr0LsRmcF4g=
github.com/tetratelabs/wazero v1.8.0/go.mod h1:yAI0XTsMBhREkM/YDAK/zNou3GoiAce1P6+rp/wQhjs=
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
Expand Down
35 changes: 35 additions & 0 deletions examples/wasm/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// SPDX-FileCopyrightText: (C) 2024 Intel Corporation
// SPDX-License-Identifier: Apache 2.0

// Package main implements a WASM AIO server example (that would panic with
// real use).
package main

import (
"log"
"net/http"

"github.com/syumai/workers"

"github.com/fido-device-onboard/go-fdo"
"github.com/fido-device-onboard/go-fdo/custom"
fdo_http "github.com/fido-device-onboard/go-fdo/http"
"github.com/fido-device-onboard/go-fdo/sqlite"
)

// Build with tinygo build -target wasm -no-debug -o rv.wasm ./main.go

func main() {
db, err := sqlite.Init(nil)
if err != nil {
log.Fatal(err)
}
workers.Serve(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
(&fdo_http.Handler{
DIResponder: &fdo.DIServer[custom.DeviceMfgInfo]{Session: db},
TO0Responder: &fdo.TO0Server{Session: db},
TO1Responder: &fdo.TO1Server{Session: db},
// TO2Server uses goroutines and cannot be compiled with TinyGo
}).ServeHTTP(w, r)
}))
}
16 changes: 16 additions & 0 deletions http/debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"encoding/hex"
"log/slog"

"github.com/fido-device-onboard/go-fdo/cbor"
"github.com/fido-device-onboard/go-fdo/cbor/cdn"
)

Expand All @@ -22,3 +23,18 @@ func tryDebugNotation(b []byte) string {
}
return d
}

func debugUnencryptedMessage(msgType uint8, msg any) {
if debugEnabled() {
return
}
body, _ := cbor.Marshal(msg)
slog.Debug("unencrypted request", "msg", msgType, "body", tryDebugNotation(body))
}

func debugDecryptedMessage(msgType uint8, decrypted []byte) {
if debugEnabled() {
return
}
slog.Debug("decrypted response", "msg", msgType, "body", tryDebugNotation(decrypted))
}
39 changes: 5 additions & 34 deletions http/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ import (
"io"
"log/slog"
"net/http"
"net/http/httptest"
"net/http/httputil"
"strconv"
"strings"
"time"
Expand Down Expand Up @@ -42,12 +40,10 @@ type Handler struct {

func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Parse message type from request URL
typ, err := strconv.ParseUint(r.PathValue("msg"), 10, 8)
if err != nil {
writeErr(w, 0, fmt.Errorf("invalid message type"))
msgType, ok := msgTypeFromPath(w, r)
if !ok {
return
}
msgType := uint8(typ)
proto := protocol.Of(msgType)

// Parse request headers
Expand Down Expand Up @@ -108,39 +104,14 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}

if debugEnabled() {
h.debugRequest(ctx, w, r, msgType, resp)
debugRequest(w, r, func(w http.ResponseWriter, r *http.Request) {
h.handleRequest(ctx, w, r, msgType, resp)
})
return
}
h.handleRequest(ctx, w, r, msgType, resp)
}

func (h Handler) debugRequest(ctx context.Context, w http.ResponseWriter, r *http.Request, msgType uint8, resp protocol.Responder) {
// Dump request
debugReq, _ := httputil.DumpRequest(r, false)
var saveBody bytes.Buffer
if _, err := saveBody.ReadFrom(r.Body); err == nil {
r.Body = io.NopCloser(&saveBody)
}
slog.Debug("request", "dump", string(bytes.TrimSpace(debugReq)),
"body", tryDebugNotation(saveBody.Bytes()))

// Dump response
rr := httptest.NewRecorder()
h.handleRequest(ctx, rr, r, msgType, resp)
debugResp, _ := httputil.DumpResponse(rr.Result(), false)
slog.Debug("response", "dump", string(bytes.TrimSpace(debugResp)),
"body", tryDebugNotation(rr.Body.Bytes()))

// Copy recorded response into response writer
for key, values := range rr.Header() {
for _, value := range values {
w.Header().Add(key, value)
}
}
w.WriteHeader(rr.Code)
_, _ = w.Write(rr.Body.Bytes())
}

func (h Handler) handleRequest(ctx context.Context, w http.ResponseWriter, r *http.Request, msgType uint8, resp protocol.Responder) {
// Validate content length
maxSize := h.MaxContentLength
Expand Down
30 changes: 4 additions & 26 deletions http/transport.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ import (
"errors"
"fmt"
"io"
"log/slog"
"net/http"
"net/http/httputil"
"net/url"
"path"
"strconv"
Expand Down Expand Up @@ -51,8 +49,6 @@ type Transport struct {
}

// Send sends a single message and receives a single response message.
//
//nolint:gocyclo
func (t *Transport) Send(ctx context.Context, msgType uint8, msg any, sess kex.Session) (respType uint8, _ io.ReadCloser, _ error) {
// Initialize default values
if t.Client == nil {
Expand All @@ -64,10 +60,7 @@ func (t *Transport) Send(ctx context.Context, msgType uint8, msg any, sess kex.S

// Encrypt if a key exchange session is provided
if sess != nil {
if debugEnabled() {
body, _ := cbor.Marshal(msg)
slog.Debug("unencrypted request", "msg", msgType, "body", tryDebugNotation(body))
}
debugUnencryptedMessage(msgType, msg)
var err error
msg, err = sess.Encrypt(rand.Reader, msg)
if err != nil {
Expand Down Expand Up @@ -105,24 +98,12 @@ func (t *Transport) Send(ctx context.Context, msgType uint8, msg any, sess kex.S
}

// Perform HTTP request
if debugEnabled() {
debugReq, _ := httputil.DumpRequestOut(req, false)
slog.Debug("request", "dump", string(bytes.TrimSpace(debugReq)),
"body", tryDebugNotation(body.Bytes()))
}
debugRequestOut(req, body)
resp, err := t.Client.Do(req)
if err != nil {
return 0, nil, fmt.Errorf("error making HTTP request for message %d: %w", msgType, err)
}
if debugEnabled() {
debugResp, _ := httputil.DumpResponse(resp, false)
var saveBody bytes.Buffer
if _, err := saveBody.ReadFrom(resp.Body); err == nil {
resp.Body = io.NopCloser(&saveBody)
}
slog.Debug("response", "dump", string(bytes.TrimSpace(debugResp)),
"body", tryDebugNotation(saveBody.Bytes()))
}
debugResponse(resp)

return t.handleResponse(resp, sess)
}
Expand Down Expand Up @@ -189,10 +170,7 @@ func (t *Transport) handleResponse(resp *http.Response, sess kex.Session) (msgTy
if err != nil {
return 0, nil, fmt.Errorf("error decrypting message %d: %w", msgType, err)
}

if debugEnabled() {
slog.Debug("decrypted response", "msg", msgType, "body", tryDebugNotation(decrypted))
}
debugDecryptedMessage(msgType, decrypted)

content = io.NopCloser(bytes.NewBuffer(decrypted))
}
Expand Down
75 changes: 75 additions & 0 deletions http/util.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// SPDX-FileCopyrightText: (C) 2024 Intel Corporation
// SPDX-License-Identifier: Apache 2.0

//go:build !tinygo

package http

import (
"bytes"
"fmt"
"io"
"log/slog"
"net/http"
"net/http/httptest"
"net/http/httputil"
"strconv"
)

func msgTypeFromPath(w http.ResponseWriter, r *http.Request) (uint8, bool) {
typ, err := strconv.ParseUint(r.PathValue("msg"), 10, 8)
if err != nil {
writeErr(w, 0, fmt.Errorf("invalid message type"))
return 0, false
}
return uint8(typ), true
}

func debugRequest(w http.ResponseWriter, r *http.Request, handler http.HandlerFunc) {
// Dump request
debugReq, _ := httputil.DumpRequest(r, false)
var saveBody bytes.Buffer
if _, err := saveBody.ReadFrom(r.Body); err == nil {
r.Body = io.NopCloser(&saveBody)
}
slog.Debug("request", "dump", string(bytes.TrimSpace(debugReq)),
"body", tryDebugNotation(saveBody.Bytes()))

// Dump response
rr := httptest.NewRecorder()
handler(rr, r)
debugResp, _ := httputil.DumpResponse(rr.Result(), false)
slog.Debug("response", "dump", string(bytes.TrimSpace(debugResp)),
"body", tryDebugNotation(rr.Body.Bytes()))

// Copy recorded response into response writer
for key, values := range rr.Header() {
for _, value := range values {
w.Header().Add(key, value)
}
}
w.WriteHeader(rr.Code)
_, _ = w.Write(rr.Body.Bytes())
}

func debugRequestOut(req *http.Request, body *bytes.Buffer) {
if !debugEnabled() {
return
}
debugReq, _ := httputil.DumpRequestOut(req, false)
slog.Debug("request", "dump", string(bytes.TrimSpace(debugReq)),
"body", tryDebugNotation(body.Bytes()))
}

func debugResponse(resp *http.Response) {
if !debugEnabled() {
return
}
debugResp, _ := httputil.DumpResponse(resp, false)
var saveBody bytes.Buffer
if _, err := saveBody.ReadFrom(resp.Body); err == nil {
resp.Body = io.NopCloser(&saveBody)
}
slog.Debug("response", "dump", string(bytes.TrimSpace(debugResp)),
"body", tryDebugNotation(saveBody.Bytes()))
}
Loading

0 comments on commit 580d17d

Please sign in to comment.