crypto.go 3.28 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package libp2pquic

import (
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/tls"
	"crypto/x509"
	"errors"
	"math/big"
	"time"

	"github.com/gogo/protobuf/proto"
	ic "github.com/libp2p/go-libp2p-crypto"
	pb "github.com/libp2p/go-libp2p-crypto/pb"
)

// mint certificate selection is broken.
const hostname = "quic.ipfs"

21
22
const certValidityPeriod = 180 * 24 * time.Hour

23
func generateConfig(privKey ic.PrivKey) (*tls.Config, error) {
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
	key, hostCert, err := keyToCertificate(privKey)
	if err != nil {
		return nil, err
	}
	// The ephemeral key used just for a couple of connections (or a limited time).
	ephemeralKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	if err != nil {
		return nil, err
	}
	// Sign the ephemeral key using the host key.
	// This is the only time that the host's private key of the peer is needed.
	// Note that this step could be done asynchronously, such that a running node doesn't need access its private key at all.
	certTemplate := &x509.Certificate{
		DNSNames:     []string{hostname},
		SerialNumber: big.NewInt(1),
		NotBefore:    time.Now().Add(-24 * time.Hour),
40
		NotAfter:     time.Now().Add(certValidityPeriod),
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
	}
	certDER, err := x509.CreateCertificate(rand.Reader, certTemplate, hostCert, ephemeralKey.Public(), key)
	if err != nil {
		return nil, err
	}
	cert, err := x509.ParseCertificate(certDER)
	if err != nil {
		return nil, err
	}
	return &tls.Config{
		ServerName:         hostname,
		InsecureSkipVerify: true, // This is not insecure here. We will verify the cert chain ourselves.
		ClientAuth:         tls.RequireAnyClientCert,
		Certificates: []tls.Certificate{{
			Certificate: [][]byte{cert.Raw, hostCert.Raw},
			PrivateKey:  ephemeralKey,
		}},
	}, nil
}

61
62
func getRemotePubKey(chain []*x509.Certificate) (ic.PubKey, error) {
	if len(chain) != 2 {
63
64
65
		return nil, errors.New("expected 2 certificates in the chain")
	}
	pool := x509.NewCertPool()
66
67
	pool.AddCert(chain[1])
	if _, err := chain[0].Verify(x509.VerifyOptions{Roots: pool}); err != nil {
68
69
		return nil, err
	}
70
	remotePubKey, err := x509.MarshalPKIXPublicKey(chain[1].PublicKey)
71
72
73
74
75
76
77
78
79
80
81
82
83
84
	if err != nil {
		return nil, err
	}
	return ic.UnmarshalRsaPublicKey(remotePubKey)
}

func keyToCertificate(sk ic.PrivKey) (interface{}, *x509.Certificate, error) {
	sn, err := rand.Int(rand.Reader, big.NewInt(1<<62))
	if err != nil {
		return nil, nil, err
	}
	tmpl := &x509.Certificate{
		SerialNumber: sn,
		NotBefore:    time.Now().Add(-24 * time.Hour),
85
		NotAfter:     time.Now().Add(certValidityPeriod),
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
		IsCA:         true,
		BasicConstraintsValid: true,
	}

	var publicKey, privateKey interface{}
	keyBytes, err := sk.Bytes()
	if err != nil {
		return nil, nil, err
	}
	pbmes := new(pb.PrivateKey)
	if err := proto.Unmarshal(keyBytes, pbmes); err != nil {
		return nil, nil, err
	}
	switch pbmes.GetType() {
	case pb.KeyType_RSA:
		k, err := x509.ParsePKCS1PrivateKey(pbmes.GetData())
		if err != nil {
			return nil, nil, err
		}
		publicKey = &k.PublicKey
		privateKey = k
	// TODO: add support for ECDSA
	default:
		return nil, nil, errors.New("unsupported key type for TLS")
	}
	certDER, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, publicKey, privateKey)
	if err != nil {
		return nil, nil, err
	}
	cert, err := x509.ParseCertificate(certDER)
	if err != nil {
		return nil, nil, err
	}
	return privateKey, cert, nil
}