syncthing/lib/tlsutil/tlsutil.go

181 lines
4.6 KiB
Go
Raw Normal View History

2014-11-16 20:13:20 +00:00
// Copyright (C) 2014 The Syncthing Authors.
2014-09-29 19:43:32 +00:00
//
2015-03-07 20:36:35 +00:00
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.
2014-06-01 20:50:14 +00:00
package tlsutil
2014-03-02 22:58:14 +00:00
import (
2014-09-14 22:18:05 +00:00
"bufio"
"crypto/ecdsa"
"crypto/elliptic"
2014-03-02 22:58:14 +00:00
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"io"
2014-03-02 22:58:14 +00:00
"math/big"
2014-05-23 12:43:17 +00:00
mr "math/rand"
"net"
2014-03-02 22:58:14 +00:00
"os"
"time"
)
var (
ErrIdentificationFailed = fmt.Errorf("failed to identify socket type")
)
// NewCertificate generates and returns a new TLS certificate. If tlsRSABits
// is greater than zero we generate an RSA certificate with the specified
// number of bits. Otherwise we create a 384 bit ECDSA certificate.
func NewCertificate(certFile, keyFile, tlsDefaultCommonName string, tlsRSABits int) (tls.Certificate, error) {
var priv interface{}
var err error
if tlsRSABits > 0 {
priv, err = rsa.GenerateKey(rand.Reader, tlsRSABits)
} else {
priv, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
}
2014-09-20 13:42:20 +00:00
if err != nil {
return tls.Certificate{}, fmt.Errorf("generate key: %s", err)
2014-09-20 13:42:20 +00:00
}
2014-03-02 22:58:14 +00:00
notBefore := time.Now()
notAfter := time.Date(2049, 12, 31, 23, 59, 59, 0, time.UTC)
template := x509.Certificate{
2014-05-23 12:43:17 +00:00
SerialNumber: new(big.Int).SetInt64(mr.Int63()),
2014-03-02 22:58:14 +00:00
Subject: pkix.Name{
CommonName: tlsDefaultCommonName,
2014-03-02 22:58:14 +00:00
},
NotBefore: notBefore,
NotAfter: notAfter,
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
BasicConstraintsValid: true,
}
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(priv), priv)
2014-09-20 13:42:20 +00:00
if err != nil {
return tls.Certificate{}, fmt.Errorf("create cert: %s", err)
2014-09-20 13:42:20 +00:00
}
2014-03-02 22:58:14 +00:00
certOut, err := os.Create(certFile)
2014-09-20 13:42:20 +00:00
if err != nil {
return tls.Certificate{}, fmt.Errorf("save cert: %s", err)
2014-09-20 13:42:20 +00:00
}
2014-09-20 13:31:15 +00:00
err = pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
2014-09-20 13:42:20 +00:00
if err != nil {
return tls.Certificate{}, fmt.Errorf("save cert: %s", err)
2014-09-20 13:42:20 +00:00
}
2014-09-20 13:31:15 +00:00
err = certOut.Close()
2014-09-20 13:42:20 +00:00
if err != nil {
return tls.Certificate{}, fmt.Errorf("save cert: %s", err)
2014-09-20 13:42:20 +00:00
}
2014-03-02 22:58:14 +00:00
keyOut, err := os.OpenFile(keyFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
2014-09-20 13:42:20 +00:00
if err != nil {
return tls.Certificate{}, fmt.Errorf("save key: %s", err)
2014-09-20 13:42:20 +00:00
}
block, err := pemBlockForKey(priv)
if err != nil {
return tls.Certificate{}, fmt.Errorf("save key: %s", err)
}
err = pem.Encode(keyOut, block)
2014-09-20 13:42:20 +00:00
if err != nil {
return tls.Certificate{}, fmt.Errorf("save key: %s", err)
2014-09-20 13:42:20 +00:00
}
2014-09-20 13:31:15 +00:00
err = keyOut.Close()
2014-09-20 13:42:20 +00:00
if err != nil {
return tls.Certificate{}, fmt.Errorf("save key: %s", err)
2014-09-20 13:42:20 +00:00
}
return tls.LoadX509KeyPair(certFile, keyFile)
2014-03-02 22:58:14 +00:00
}
type DowngradingListener struct {
net.Listener
TLSConfig *tls.Config
}
2014-09-14 22:18:05 +00:00
func (l *DowngradingListener) Accept() (net.Conn, error) {
2015-09-02 21:00:51 +00:00
conn, isTLS, err := l.AcceptNoWrapTLS()
// We failed to identify the socket type, pretend that everything is fine,
// and pass it to the underlying handler, and let them deal with it.
if err == ErrIdentificationFailed {
return conn, nil
}
if err != nil {
return conn, err
}
if isTLS {
return tls.Server(conn, l.TLSConfig), nil
}
return conn, nil
}
2015-09-02 21:00:51 +00:00
func (l *DowngradingListener) AcceptNoWrapTLS() (net.Conn, bool, error) {
2014-09-14 22:18:05 +00:00
conn, err := l.Listener.Accept()
if err != nil {
return nil, false, err
}
2014-09-14 22:18:05 +00:00
br := bufio.NewReader(conn)
2015-08-06 11:07:34 +00:00
conn.SetReadDeadline(time.Now().Add(1 * time.Second))
2014-09-14 22:18:05 +00:00
bs, err := br.Peek(1)
2015-08-06 11:07:34 +00:00
conn.SetReadDeadline(time.Time{})
if err != nil {
// We hit a read error here, but the Accept() call succeeded so we must not return an error.
// We return the connection as is with a special error which handles this
// special case in Accept().
return conn, false, ErrIdentificationFailed
}
2014-09-14 22:18:05 +00:00
2015-09-02 21:00:51 +00:00
return &UnionedConnection{br, conn}, bs[0] == 0x16, nil
}
2015-09-02 21:00:51 +00:00
type UnionedConnection struct {
io.Reader
net.Conn
}
2015-09-02 21:00:51 +00:00
func (c *UnionedConnection) Read(b []byte) (n int, err error) {
return c.Reader.Read(b)
}
func publicKey(priv interface{}) interface{} {
switch k := priv.(type) {
case *rsa.PrivateKey:
return &k.PublicKey
case *ecdsa.PrivateKey:
return &k.PublicKey
default:
return nil
}
}
func pemBlockForKey(priv interface{}) (*pem.Block, error) {
switch k := priv.(type) {
case *rsa.PrivateKey:
return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)}, nil
case *ecdsa.PrivateKey:
b, err := x509.MarshalECPrivateKey(k)
if err != nil {
return nil, err
}
return &pem.Block{Type: "EC PRIVATE KEY", Bytes: b}, nil
default:
return nil, fmt.Errorf("unknown key type")
}
}