Skip to content
This repository has been archived by the owner on Aug 19, 2022. It is now read-only.

WIP: Add Openssl with TLS 1.3 #71

Draft
wants to merge 8 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ script:
# some tests are randomized. Run them a few times.
- for i in `seq 1 10`; do
ginkgo -r -v --cover --randomizeAllSpecs --randomizeSuites --trace --progress;
ginkgo -tags openssl -r -v --cover --randomizeAllSpecs --randomizeSuites --trace --progress;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should use the Travis build matrix feature.

done

after_success:
Expand Down
9 changes: 7 additions & 2 deletions conn.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
package libp2ptls

import (
"crypto/tls"
"net"

ci "github.com/libp2p/go-libp2p-core/crypto"
"github.com/libp2p/go-libp2p-core/peer"
"github.com/libp2p/go-libp2p-core/sec"
)

type handshakeConn interface {
net.Conn
Handshake() error
}

type conn struct {
*tls.Conn
net.Conn

localPeer peer.ID
privKey ci.PrivKey
Expand Down
98 changes: 31 additions & 67 deletions crypto.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
// +build !openssl

package libp2ptls

import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"errors"
"fmt"
"math/big"
"net"
"time"

"golang.org/x/sys/cpu"
Expand All @@ -19,20 +18,10 @@ import (
"github.com/libp2p/go-libp2p-core/peer"
)

const certValidityPeriod = 100 * 365 * 24 * time.Hour // ~100 years
const certificatePrefix = "libp2p-tls-handshake:"
const alpn string = "libp2p"

var extensionID = getPrefixedExtensionID([]int{1, 1})

type signedKey struct {
PubKey []byte
Signature []byte
}

// Identity is used to secure connections
type Identity struct {
config tls.Config
config tls.Config
privateKey ic.PrivKey
}

// NewIdentity creates a new identity
Expand All @@ -57,6 +46,21 @@ func NewIdentity(privKey ic.PrivKey) (*Identity, error) {
}, nil
}

// CreateServerConn creates server connection to do the tls handshake.
func (i *Identity) CreateServerConn(insecure net.Conn) (handshakeConn, <-chan ic.PubKey, error) {
config, keyCh := i.ConfigForAny()
conn := tls.Server(insecure, config)
return conn, keyCh, nil
}

// CreateClientConn creates client connection to do the tls handshake.
func (i *Identity) CreateClientConn(insecure net.Conn, remote peer.ID) (handshakeConn,
<-chan ic.PubKey, error) {
config, keyCh := i.ConfigForPeer(remote)
conn := tls.Client(insecure, config)
return conn, keyCh, nil
}

// ConfigForAny is a short-hand for ConfigForPeer("").
func (i *Identity) ConfigForAny() (*tls.Config, <-chan ic.PubKey) {
return i.ConfigForPeer("")
Expand Down Expand Up @@ -92,12 +96,9 @@ func (i *Identity) ConfigForPeer(remote peer.ID) (*tls.Config, <-chan ic.PubKey)
if err != nil {
return err
}
if remote != "" && !remote.MatchesPublicKey(pubKey) {
peerID, err := peer.IDFromPublicKey(pubKey)
if err != nil {
peerID = peer.ID(fmt.Sprintf("(not determined: %s)", err.Error()))
}
return fmt.Errorf("peer IDs don't match: expected %s, got %s", remote, peerID)
err = validateRemote(pubKey, remote)
if err != nil {
return err
}
keyCh <- pubKey
return nil
Expand Down Expand Up @@ -132,74 +133,37 @@ func PubKeyFromCertChain(chain []*x509.Certificate) (ic.PubKey, error) {
if !found {
return nil, errors.New("expected certificate to contain the key extension")
}
var sk signedKey
if _, err := asn1.Unmarshal(keyExt.Value, &sk); err != nil {
return nil, fmt.Errorf("unmarshalling signed certificate failed: %s", err)
}
pubKey, err := ic.UnmarshalPublicKey(sk.PubKey)
if err != nil {
return nil, fmt.Errorf("unmarshalling public key failed: %s", err)
}

certKeyPub, err := x509.MarshalPKIXPublicKey(cert.PublicKey)
if err != nil {
return nil, err
}
valid, err := pubKey.Verify(append([]byte(certificatePrefix), certKeyPub...), sk.Signature)
if err != nil {
return nil, fmt.Errorf("signature verification failed: %s", err)
}
if !valid {
return nil, errors.New("signature invalid")
}
return pubKey, nil
return unmarshalExtensionPublicKey(keyExt.Value, certKeyPub)
}

func keyToCertificate(sk ic.PrivKey) (*tls.Certificate, error) {
certKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return nil, err
}

keyBytes, err := ic.MarshalPublicKey(sk.GetPublic())
if err != nil {
return nil, err
}
certKeyPub, err := x509.MarshalPKIXPublicKey(certKey.Public())
if err != nil {
return nil, err
}
signature, err := sk.Sign(append([]byte(certificatePrefix), certKeyPub...))
if err != nil {
return nil, err
}
value, err := asn1.Marshal(signedKey{
PubKey: keyBytes,
Signature: signature,
})
config, err := newCertificateConfig(sk)
if err != nil {
return nil, err
}

sn, err := rand.Int(rand.Reader, big.NewInt(1<<62))
if err != nil {
return nil, err
}
tmpl := &x509.Certificate{
SerialNumber: sn,
SerialNumber: config.serialNumber,
NotBefore: time.Time{},
NotAfter: time.Now().Add(certValidityPeriod),
// after calling CreateCertificate, these will end up in Certificate.Extensions
ExtraExtensions: []pkix.Extension{
{Id: extensionID, Value: value},
{Id: extensionID, Value: config.extensionValue},
},
}
certDER, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, certKey.Public(), certKey)
certDER, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, config.certKey.Public(),
config.certKey)
if err != nil {
return nil, err
}
return &tls.Certificate{
Certificate: [][]byte{certDER},
PrivateKey: certKey,
PrivateKey: config.certKey,
}, nil
}

Expand Down
Loading