github.com/status-im/status-go@v1.1.0/server/pairing/certs.go (about)

     1  package pairing
     2  
     3  import (
     4  	"crypto/ecdsa"
     5  	"crypto/sha256"
     6  	"crypto/tls"
     7  	"crypto/x509"
     8  	"encoding/asn1"
     9  	"encoding/pem"
    10  	"fmt"
    11  	"math/big"
    12  	"net"
    13  	"net/url"
    14  	"time"
    15  
    16  	"go.uber.org/zap"
    17  
    18  	"github.com/status-im/status-go/logutils"
    19  	"github.com/status-im/status-go/server"
    20  )
    21  
    22  const CertificateMaxClockDrift = time.Minute
    23  
    24  func makeSerialNumberFromKey(pk *ecdsa.PrivateKey) *big.Int {
    25  	h := sha256.New()
    26  	h.Write(append(pk.D.Bytes(), append(pk.Y.Bytes(), pk.X.Bytes()...)...))
    27  
    28  	return new(big.Int).SetBytes(h.Sum(nil))
    29  }
    30  
    31  func GenerateCertFromKey(pk *ecdsa.PrivateKey, from time.Time, IPAddresses []net.IP, DNSNames []string) (tls.Certificate, []byte, error) {
    32  	cert := server.GenerateX509Cert(makeSerialNumberFromKey(pk), from.Add(-CertificateMaxClockDrift), from.Add(time.Hour), IPAddresses, DNSNames)
    33  	certPem, keyPem, err := server.GenerateX509PEMs(cert, pk)
    34  	if err != nil {
    35  		return tls.Certificate{}, nil, err
    36  	}
    37  
    38  	tlsCert, err := tls.X509KeyPair(certPem, keyPem)
    39  	if err != nil {
    40  		return tls.Certificate{}, nil, err
    41  	}
    42  
    43  	block, _ := pem.Decode(certPem)
    44  	if block == nil {
    45  		return tls.Certificate{}, nil, fmt.Errorf("failed to decode certPem")
    46  	}
    47  	leaf, err := x509.ParseCertificate(block.Bytes)
    48  	if err != nil {
    49  		return tls.Certificate{}, nil, err
    50  	}
    51  	tlsCert.Leaf = leaf
    52  
    53  	return tlsCert, certPem, nil
    54  }
    55  
    56  // verifyCertPublicKey checks that the ecdsa.PublicKey using in a x509.Certificate matches a known ecdsa.PublicKey
    57  func verifyCertPublicKey(cert *x509.Certificate, publicKey *ecdsa.PublicKey) error {
    58  	certKey, ok := cert.PublicKey.(*ecdsa.PublicKey)
    59  	if !ok {
    60  		return fmt.Errorf("unexpected public key type, expected ecdsa.PublicKey")
    61  	}
    62  
    63  	if !certKey.Equal(publicKey) {
    64  		return fmt.Errorf("server certificate MUST match the given public key")
    65  	}
    66  	return nil
    67  }
    68  
    69  // verifyCertSig checks that a x509.Certificate's Signature verifies against x509.Certificate's PublicKey
    70  // If the x509.Certificate's PublicKey is not an ecdsa.PublicKey an error will be thrown
    71  func verifyCertSig(cert *x509.Certificate) (bool, error) {
    72  	var esig struct {
    73  		R, S *big.Int
    74  	}
    75  	if _, err := asn1.Unmarshal(cert.Signature, &esig); err != nil {
    76  		return false, err
    77  	}
    78  
    79  	hash := sha256.New()
    80  	hash.Write(cert.RawTBSCertificate)
    81  
    82  	ecKey, ok := cert.PublicKey.(*ecdsa.PublicKey)
    83  	if !ok {
    84  		return false, fmt.Errorf("certificate public is not an ecdsa.PublicKey")
    85  	}
    86  
    87  	verified := ecdsa.Verify(ecKey, hash.Sum(nil), esig.R, esig.S)
    88  	return verified, nil
    89  }
    90  
    91  // verifyCert verifies an x509.Certificate against a known ecdsa.PublicKey
    92  // combining the checks of verifyCertPublicKey and verifyCertSig.
    93  // If an x509.Certificate fails to verify an error is also thrown
    94  func verifyCert(cert *x509.Certificate, publicKey *ecdsa.PublicKey) error {
    95  	err := verifyCertPublicKey(cert, publicKey)
    96  	if err != nil {
    97  		return err
    98  	}
    99  
   100  	verified, err := verifyCertSig(cert)
   101  	if err != nil {
   102  		return err
   103  	}
   104  	if !verified {
   105  		return fmt.Errorf("server certificate signature MUST verify")
   106  	}
   107  	return nil
   108  }
   109  
   110  // getServerCert pings a given tls host, extracts and returns its x509.Certificate
   111  // the function expects there to be only 1 certificate
   112  func getServerCert(URL *url.URL) (*x509.Certificate, error) {
   113  	conf := &tls.Config{
   114  		InsecureSkipVerify: true, // nolint: gosec // Only skip verify to get the server's TLS cert. DO NOT skip for any other reason.
   115  	}
   116  
   117  	// one second should be enough to get the server's TLS cert in LAN?
   118  	conn, err := tls.DialWithDialer(&net.Dialer{Timeout: time.Second}, "tcp", URL.Host, conf)
   119  	if err != nil {
   120  		return nil, err
   121  	}
   122  	defer func(conn *tls.Conn) {
   123  		if e := conn.Close(); e != nil {
   124  			logutils.ZapLogger().Warn("failed to close temporary TLS connection:", zap.Error(e))
   125  		}
   126  	}(conn)
   127  
   128  	certs := conn.ConnectionState().PeerCertificates
   129  	if len(certs) != 1 {
   130  		return nil, fmt.Errorf("expected 1 TLS certificate, received '%d'", len(certs))
   131  	}
   132  
   133  	return certs[0], nil
   134  }