From 02dd787f97e708ce712878b1e648236c02577340 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Thu, 13 Apr 2023 13:34:34 -0700 Subject: [PATCH] Simplify the fingerprint code. There was a lot of stuff left over from experimentation. --- cert/generate | 2 +- player/.gitignore | 1 + player/src/index.ts | 13 +++++++ player/src/player/index.ts | 7 ++-- player/src/transport/.gitignore | 1 - player/src/transport/index.ts | 27 ++++++-------- server/internal/web/web.go | 64 --------------------------------- server/main.go | 17 +-------- 8 files changed, 29 insertions(+), 103 deletions(-) delete mode 100644 player/src/transport/.gitignore delete mode 100644 server/internal/web/web.go diff --git a/cert/generate b/cert/generate index 720fcfd..d89953d 100755 --- a/cert/generate +++ b/cert/generate @@ -17,4 +17,4 @@ go run filippo.io/mkcert -ecdsa -install go run filippo.io/mkcert -ecdsa -days 10 -cert-file "$CRT" -key-file "$KEY" localhost 127.0.0.1 ::1 # Compute the sha256 fingerprint of the certificate for WebTransport -openssl x509 -in "$CRT" -outform der | openssl dgst -sha256 > ../player/src/transport/fingerprint.hex +openssl x509 -in "$CRT" -outform der | openssl dgst -sha256 > ../player/fingerprint.hex diff --git a/player/.gitignore b/player/.gitignore index 6d34cdf..10ec168 100644 --- a/player/.gitignore +++ b/player/.gitignore @@ -1,3 +1,4 @@ node_modules .parcel-cache dist +fingerprint.hex diff --git a/player/src/index.ts b/player/src/index.ts index dfa93a9..e691ab5 100644 --- a/player/src/index.ts +++ b/player/src/index.ts @@ -1,5 +1,14 @@ import Player from "./player" +// @ts-ignore embed the certificate fingerprint using bundler +import fingerprintHex from 'bundle-text:../fingerprint.hex'; + +// Convert the hex to binary. +let fingerprint = []; +for (let c = 0; c < fingerprintHex.length-1; c += 2) { + fingerprint.push(parseInt(fingerprintHex.substring(c, c+2), 16)); +} + const params = new URLSearchParams(window.location.search) const url = params.get("url") || "https://localhost:4443/watch" @@ -7,6 +16,10 @@ const canvas = document.querySelector("canvas#video")! const player = new Player({ url: url, + fingerprint: { // TODO remove when Chrome accepts the system CA + "algorithm": "sha-256", + "value": new Uint8Array(fingerprint), + }, canvas: canvas, }) diff --git a/player/src/player/index.ts b/player/src/player/index.ts index 00438b2..b18a625 100644 --- a/player/src/player/index.ts +++ b/player/src/player/index.ts @@ -4,6 +4,7 @@ import Video from "../video" export interface PlayerInit { url: string; + fingerprint?: WebTransportHash; // the certificate fingerprint, temporarily needed for local development canvas: HTMLCanvasElement; } @@ -20,6 +21,8 @@ export default class Player { this.transport = new Transport({ url: props.url, + fingerprint: props.fingerprint, + audio: this.audio, video: this.video, }) @@ -29,10 +32,6 @@ export default class Player { this.transport.close() } - async connect(url: string) { - await this.transport.connect(url) - } - play() { this.audio.play({}) //this.video.play() diff --git a/player/src/transport/.gitignore b/player/src/transport/.gitignore deleted file mode 100644 index 628e634..0000000 --- a/player/src/transport/.gitignore +++ /dev/null @@ -1 +0,0 @@ -fingerprint.hex diff --git a/player/src/transport/index.ts b/player/src/transport/index.ts index 138688d..90bb29b 100644 --- a/player/src/transport/index.ts +++ b/player/src/transport/index.ts @@ -5,11 +5,10 @@ import * as MP4 from "../mp4" import Audio from "../audio" import Video from "../video" -// @ts-ignore bundler embeds data -import fingerprint from 'bundle-text:./fingerprint.hex'; - export interface TransportInit { url: string; + fingerprint?: WebTransportHash; // the certificate fingerprint, temporarily needed for local development + audio: Audio; video: Video; } @@ -28,7 +27,7 @@ export default class Transport { this.audio = props.audio; this.video = props.video; - this.quic = this.connect(props.url) + this.quic = this.connect(props) // Create a unidirectional stream for all of our messages this.api = this.quic.then((q) => { @@ -43,22 +42,16 @@ export default class Transport { (await this.quic).close() } - async connect(url: string): Promise { - // Convert the hex to binary. - let hash = []; - for (let c = 0; c < fingerprint.length-1; c += 2) { - hash.push(parseInt(fingerprint.substring(c, c+2), 16)); + // Helper function to make creating a promise easier + private async connect(props: TransportInit): Promise { + + let options: WebTransportOptions = {}; + if (props.fingerprint) { + options.serverCertificateHashes = [ props.fingerprint ] } - const quic = new WebTransport(url, { - "serverCertificateHashes": [{ - "algorithm": "sha-256", - "value": new Uint8Array(hash), - }] - }) - + const quic = new WebTransport(props.url, options) await quic.ready - return quic } diff --git a/server/internal/web/web.go b/server/internal/web/web.go deleted file mode 100644 index dfd766b..0000000 --- a/server/internal/web/web.go +++ /dev/null @@ -1,64 +0,0 @@ -package web - -import ( - "context" - "log" - "net/http" - "time" - - "github.com/kixelated/invoker" -) - -type Server struct { - inner http.Server - config Config -} - -type Config struct { - Addr string - CertFile string - KeyFile string - Fingerprint string // the TLS certificate fingerprint -} - -func New(config Config) (s *Server) { - s = new(Server) - s.config = config - - s.inner = http.Server{ - Addr: config.Addr, - } - - http.HandleFunc("/fingerprint", s.handleFingerprint) - - return s -} - -func (s *Server) Run(ctx context.Context) (err error) { - return invoker.Run(ctx, s.runServe, s.runShutdown) -} - -func (s *Server) runServe(context.Context) (err error) { - // NOTE: Doesn't support context, which is why we need runShutdown - err = s.inner.ListenAndServeTLS(s.config.CertFile, s.config.KeyFile) - log.Println(err) - return err -} - -// Gracefully shut down the server when the context is cancelled -func (s *Server) runShutdown(ctx context.Context) (err error) { - <-ctx.Done() - - timeout, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - _ = s.inner.Shutdown(timeout) - - return ctx.Err() -} - -// Return the sha256 of the certificate as a temporary work-around for local development. -// TODO remove this when WebTransport uses the system CA -func (s *Server) handleFingerprint(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Access-Control-Allow-Origin", "*") - _, _ = w.Write([]byte(s.config.Fingerprint)) -} diff --git a/server/main.go b/server/main.go index a0b5f9b..d427a31 100644 --- a/server/main.go +++ b/server/main.go @@ -2,16 +2,13 @@ package main import ( "context" - "crypto/sha256" "crypto/tls" - "encoding/hex" "flag" "fmt" "log" "github.com/kixelated/invoker" "github.com/kixelated/warp/server/internal/warp" - "github.com/kixelated/warp/server/internal/web" ) func main() { @@ -53,19 +50,7 @@ func run(ctx context.Context) (err error) { return fmt.Errorf("failed to create warp server: %w", err) } - hash := sha256.Sum256(tlsCert.Certificate[0]) - fingerprint := hex.EncodeToString(hash[:]) - - webConfig := web.Config{ - Addr: *addr, - CertFile: *cert, - KeyFile: *key, - Fingerprint: fingerprint, - } - - webServer := web.New(webConfig) - log.Printf("listening on %s", *addr) - return invoker.Run(ctx, invoker.Interrupt, warpServer.Run, webServer.Run) + return invoker.Run(ctx, invoker.Interrupt, warpServer.Run) }