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

Commit

Permalink
Export methods to allow generating custom certificates
Browse files Browse the repository at this point in the history
  • Loading branch information
peterargue committed Nov 23, 2021
1 parent efdc0b4 commit 1764007
Showing 1 changed file with 46 additions and 22 deletions.
68 changes: 46 additions & 22 deletions crypto.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package libp2ptls

import (
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
Expand Down Expand Up @@ -38,7 +39,12 @@ type Identity struct {

// NewIdentity creates a new identity
func NewIdentity(privKey ic.PrivKey) (*Identity, error) {
cert, err := keyToCertificate(privKey)
certTmpl, err := defaultCertTemplate()
if err != nil {
return nil, err
}

cert, err := KeyToCertificate(privKey, certTmpl)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -157,17 +163,15 @@ func PubKeyFromCertChain(chain []*x509.Certificate) (ic.PubKey, error) {
return pubKey, nil
}

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

// GenerateSignedExtension uses the provided private key to sign the public key, and returns the
// signature within a pkix.Extension.
// This extension is included in a certificate to cryptographically tie it to the libp2p private key.
func GenerateSignedExtension(sk ic.PrivKey, pubKey crypto.PublicKey) (*pkix.Extension, error) {
keyBytes, err := ic.MarshalPublicKey(sk.GetPublic())
if err != nil {
return nil, err
}
certKeyPub, err := x509.MarshalPKIXPublicKey(certKey.Public())
certKeyPub, err := x509.MarshalPKIXPublicKey(pubKey)
if err != nil {
return nil, err
}
Expand All @@ -183,34 +187,54 @@ func keyToCertificate(sk ic.PrivKey) (*tls.Certificate, error) {
return nil, err
}

return &pkix.Extension{Id: extensionID, Critical: extensionCritical, Value: value}, nil
}

// KeyToCertificate generates a new ECDSA private key and corresponding x509 certificate.
// The certificate includes an extension that cryptographically ties it to the provided libp2p
// private key to authenticate TLS connections.
func KeyToCertificate(sk ic.PrivKey, certTmpl *x509.Certificate) (*tls.Certificate, error) {
certKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return nil, err
}

// after calling CreateCertificate, these will end up in Certificate.Extensions
extension, err := GenerateSignedExtension(sk, certKey.Public())
if err != nil {
return nil, err
}
certTmpl.ExtraExtensions = append(certTmpl.ExtraExtensions, *extension)

certDER, err := x509.CreateCertificate(rand.Reader, certTmpl, certTmpl, certKey.Public(), certKey)
if err != nil {
return nil, err
}
return &tls.Certificate{
Certificate: [][]byte{certDER},
PrivateKey: certKey,
}, nil
}

func defaultCertTemplate() (*x509.Certificate, error) {
bigNum := big.NewInt(1 << 62)
sn, err := rand.Int(rand.Reader, bigNum)
if err != nil {
return nil, err
}

subjectSN, err := rand.Int(rand.Reader, bigNum)
if err != nil {
return nil, err
}
tmpl := &x509.Certificate{

return &x509.Certificate{
SerialNumber: sn,
NotBefore: time.Now().Add(-time.Hour),
NotAfter: time.Now().Add(certValidityPeriod),
// According to RFC 3280, the issuer field must be set,
// see https://datatracker.ietf.org/doc/html/rfc3280#section-4.1.2.4.
Subject: pkix.Name{SerialNumber: subjectSN.String()},
// after calling CreateCertificate, these will end up in Certificate.Extensions
ExtraExtensions: []pkix.Extension{
{Id: extensionID, Critical: extensionCritical, Value: value},
},
}
certDER, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, certKey.Public(), certKey)
if err != nil {
return nil, err
}
return &tls.Certificate{
Certificate: [][]byte{certDER},
PrivateKey: certKey,
}, nil
}

Expand All @@ -219,7 +243,7 @@ func keyToCertificate(sk ic.PrivKey) (*tls.Certificate, error) {
// x86->x86: AES, ARM->x86: ChaCha, x86->ARM: ChaCha and ARM->ARM: Chacha
// This function returns true if we don't have AES hardware support, and false otherwise.
// Thus, ARM servers will always use their own cipher suite preferences (ChaCha first),
// and x86 servers will aways use the client's cipher suite preferences.
// and x86 servers will always use the client's cipher suite preferences.
func preferServerCipherSuites() bool {
// Copied from the Go TLS implementation.

Expand Down

0 comments on commit 1764007

Please sign in to comment.