1// Copyright 2018 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5package osp
6
7// TODO(pthatcher):
8// - avoid NetworkIdleTimeout
9// - make a client object that can send and receive more than one stream
10// - make a server object that can send and receive more than one stream
11
12import (
13	"context"
14	"crypto/rand"
15	"crypto/rsa"
16	"crypto/tls"
17	"crypto/x509"
18	"encoding/pem"
19	"fmt"
20	"io"
21	"log"
22	"math/big"
23
24	quic "github.com/lucas-clemente/quic-go"
25)
26
27func GenerateTlsCert() (*tls.Certificate, error) {
28	key, err := rsa.GenerateKey(rand.Reader, 1024)
29	if err != nil {
30		return nil, err
31	}
32	template := x509.Certificate{SerialNumber: big.NewInt(1)}
33	certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &key.PublicKey, key)
34	if err != nil {
35		return nil, err
36	}
37	keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)})
38	certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER})
39
40	tlsCert, err := tls.X509KeyPair(certPEM, keyPEM)
41	if err != nil {
42		return nil, err
43	}
44	return &tlsCert, nil
45}
46
47func readAllStreams(ctx context.Context, session quic.Session, streams chan<- io.ReadWriteCloser) {
48	for !done(ctx) {
49		stream, err := session.AcceptStream()
50		if err != nil && !done(ctx) {
51			log.Println("Failed to accept stream with QUIC", err.Error())
52		}
53		streams <- stream
54	}
55}
56
57// Returns a quic.Session object with a .OpenStreamSync method to send streams
58func DialAsQuicClient(ctx context.Context, hostname string, port int) (quic.Session, error) {
59	// TODO(pthatcher): Change InsecureSkipVerify
60	tlsConfig := &tls.Config{InsecureSkipVerify: true}
61	addr := fmt.Sprintf("%s:%d", hostname, port)
62	session, err := quic.DialAddrContext(ctx, addr, tlsConfig, nil)
63	return session, err
64}
65
66// Reads in streams
67func RunQuicServer(ctx context.Context, port int, cert tls.Certificate, streams chan<- io.ReadWriteCloser) error {
68	addr := fmt.Sprintf(":%d", port)
69	tlsConfig := &tls.Config{Certificates: []tls.Certificate{cert}}
70	listener, err := quic.ListenAddr(addr, tlsConfig, nil)
71	if err != nil {
72		return err
73	}
74	go func() {
75		waitUntilDone(ctx)
76		listener.Close()
77	}()
78	for {
79		session, err := listener.Accept()
80		if err != nil && !done(ctx) {
81			log.Println("Failed to accept session with QUIC:", err.Error())
82		}
83		go readAllStreams(ctx, session, streams)
84	}
85}
86