// Service > TlsCerts
// This package provides a generator and utility functions for generating and manipulating self-signed TLS certificates.

/*
  Mind that certificates generated by this won't actually be valid for any use unless explicitly approved. There are a couple reasons for this. First of all, they're self signed, with them acting as their own CA.

  Second, the identifier field in the certificate will be its own thing. It cannot be a domain: most Aether nodes won't be serving from domains. It cannot be an IP address, IP addresses change often. Therefore what it is is that it will be just a generic name, like "Aether."

  The final thing is that these certificates are actually not verified before use.

  # What does that mean?
  The certificate is still used to achieve the encryption, but the certificate itself won't be checked for whether it's a trusted certificate or not.

  # Why?
  Because Aether nodes do not trust any remotes, thus checking whether we trust a certificate is unnecessary. We don't trust any. We do not assume a certificate being the same as the node being the same, we do not assume any sort of security privileges because of the certificate's existence and/or validity, We assume nothing. All data that is sent over this TLS wire is public.

  TLS effectively does two things: it verifies the remote, and it encrypts the connection. We do not need the former, but we use the latter. We do not need the former because we don't care which remote is on the other end of the pipe, all the remotes are the same to the local node.

  # So why is TLS even being used?
  Because it prevents passive MiTM attacks by your ISP and the likes. Even when the data traversing between nodes is public, there is tremendous benefit in encrypting the pipe itself, so that the ISP's visibility to the network is zero.

  When all connections are non-TLS:
  ISP sees all connections and all data that passes through them. It can act as a panopticon, a godlike observer who can see the whole system and all the pipes within it at the same time.

  When all connections are TLS:
  ISP sees garble moving between computers. It no longer has god-mode view of all pipes. The only way an ISP can see what goes over the pipes is to join the network itself and run a node, and even then, it only sees the connections coming to that node, from remotes it can convince to connect to it.

  In summary, we do not need verify-remote abilities of TLS, but we make good use of encryption provided by it.

*/
package tlscerts

import (
	"aether-core/aether/services/globals"
	"aether-core/aether/services/logging"
	"aether-core/aether/services/toolbox"
	"bytes"
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"fmt"
	"io/ioutil"
	"log"
	"math/big"
	"os"
	"path/filepath"
	"time"
)

const (
	host      = "Aether"
	validFrom = "Dec 16 12:00:00 1689"
	validFor  = time.Duration(0)
)

func generateTLSKeyPair() (string, string) {
	priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	if err != nil {
		log.Fatalf("Failed to generate private key: %s", err)
	}
	notBefore, err2 := time.Parse("Jan 2 15:04:05 2006", validFrom)
	if err2 != nil {
		fmt.Fprintf(os.Stderr, "Failed to parse creation date: %s\n", err2)
		os.Exit(1)
	}
	notAfter := notBefore.Add(validFor)
	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
	serialNumber, err3 := rand.Int(rand.Reader, serialNumberLimit)
	if err3 != nil {
		log.Fatalf("Failed to generate serial number: %s", err3)
	}
	template := x509.Certificate{
		SerialNumber: serialNumber,
		Subject: pkix.Name{
			Organization: []string{host},
		},
		NotBefore: notBefore,
		NotAfter:  notAfter,

		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
		BasicConstraintsValid: true,
		IsCA: true,
	}
	derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
	if err != nil {
		log.Fatalf("Failed to create certificate: %s", err)
	}
	bufCert := new(bytes.Buffer)
	pem.Encode(bufCert, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
	pubCert := bufCert.String()
	bufKey := new(bytes.Buffer)
	pem.Encode(bufKey, pemBlockForKey(priv))
	privKey := bufKey.String()
	return pubCert, privKey
}

func pemBlockForKey(priv *ecdsa.PrivateKey) *pem.Block {
	b, err := x509.MarshalECPrivateKey(priv)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Unable to marshal ECDSA private key: %v", err)
		os.Exit(2)
	}
	return &pem.Block{Type: "EC PRIVATE KEY", Bytes: b}
}

// Generate generates a key pair if needed.
func Generate() {
	baseTLSPath := filepath.Join(globals.BackendConfig.GetUserDirectory(), "backend", "tls")
	toolbox.CreatePath(baseTLSPath)
	certLoc := filepath.Join(baseTLSPath, "cert.pem")
	keyLoc := filepath.Join(baseTLSPath, "key.pub")
	if !toolbox.FileExists(certLoc) || !toolbox.FileExists(keyLoc) {
		toolbox.DeleteFromDisk(certLoc)
		toolbox.DeleteFromDisk(keyLoc)
		cert, key := generateTLSKeyPair()
		err := ioutil.WriteFile(certLoc, []byte(cert), 0755)
		if err != nil {
			logging.Logf(1, "TLS key writing to file failed. Error: %v", err)
		}
		err2 := ioutil.WriteFile(keyLoc, []byte(key), 0755)
		if err2 != nil {
			logging.Logf(1, "TLS key writing to file failed. Error: %v", err2)
		}
	}
}
