tls package

Signed-off-by: Matthew R. Kasun <mkasun@nusak.ca>
This commit is contained in:
Matthew R. Kasun 2022-04-12 16:02:14 -04:00
parent ab8cf43b3b
commit 98cbec6dde
3 changed files with 258 additions and 0 deletions

1
go.mod
View file

@ -32,6 +32,7 @@ require (
)
require (
filippo.io/edwards25519 v1.0.0-rc.1
github.com/go-ping/ping v0.0.0-20211130115550-779d1e919534
github.com/guumaster/hostctl v1.1.2
github.com/kr/pretty v0.3.0

2
go.sum
View file

@ -1,6 +1,8 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0 h1:eOI3/cP2VTU6uZLDYAoic+eyzzB9YyGmJ7eIjl8rOPg=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
filippo.io/edwards25519 v1.0.0-rc.1 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmGfU=
filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU=
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=

255
tls/tls.go Normal file
View file

@ -0,0 +1,255 @@
package tls
import (
"crypto/ed25519"
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"encoding/base64"
"encoding/pem"
"errors"
"fmt"
"log"
"math/big"
"os"
"time"
"filippo.io/edwards25519"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)
type (
Key struct {
point *edwards25519.Point
}
)
// NewKey generates a new key.
func NewKey() *Key {
seed := make([]byte, 64)
rand.Reader.Read(seed)
s, _ := (&edwards25519.Scalar{}).SetUniformBytes(seed)
return &Key{(&edwards25519.Point{}).ScalarBaseMult(s)}
}
// Ed25519PrivateKey returns the private key in Edwards form used for EdDSA.
func (n *Key) Ed25519PrivateKey() (ed25519.PrivateKey, error) {
if n.point == nil {
return ed25519.PrivateKey{}, errors.New("nil point")
}
if len(n.point.Bytes()) != ed25519.SeedSize {
return ed25519.PrivateKey{}, errors.New("incorrect seed size")
}
return ed25519.NewKeyFromSeed(n.point.Bytes()), nil
}
// Curve25519PrivateKey returns the private key in Montogomery form used for ECDH.
func (n *Key) Curve25519PrivateKey() (wgtypes.Key, error) {
if n.point == nil {
return wgtypes.Key{}, errors.New("nil point")
}
if len(n.point.Bytes()) != ed25519.SeedSize {
return wgtypes.Key{}, errors.New("incorrect seed size")
}
return wgtypes.ParseKey(base64.StdEncoding.EncodeToString(n.point.BytesMontgomery()))
}
// Save : saves the private key to path.
func (n *Key) Save(path string) error {
f, err := os.Create(path)
if err != nil {
return err
}
defer f.Close()
f.Write(n.point.Bytes())
return nil
}
// Reads the private key from path.
func ReadFrom(path string) (*Key, error) {
key, err := os.ReadFile(path)
if err != nil {
return nil, err
}
point, err := (&edwards25519.Point{}).SetBytes(key)
if err != nil {
return nil, err
}
return &Key{point}, nil
}
// creates a new pkix.Name
func NewName(commonName, country, org string) pkix.Name {
res := NewCName(commonName)
res.Country = []string{country}
res.Organization = []string{org}
return res
}
// creates a new pkix.Name with only a common name
func NewCName(commonName string) pkix.Name {
return pkix.Name{
CommonName: commonName,
}
}
// creates a new certificate signing request for a
func NewCSR(key ed25519.PrivateKey, name pkix.Name) (*x509.CertificateRequest, error) {
derCertRequest, err := x509.CreateCertificateRequest(rand.Reader, &x509.CertificateRequest{
Subject: name,
PublicKey: key.Public(),
}, key)
if err != nil {
return nil, err
}
csr, err := x509.ParseCertificateRequest(derCertRequest)
if err != nil {
return nil, err
}
return csr, nil
}
// returns a new self-signed certificate
func SelfSignedCA(key ed25519.PrivateKey, req *x509.CertificateRequest, days int) (*x509.Certificate, error) {
template := &x509.Certificate{
BasicConstraintsValid: true,
IsCA: true,
Version: req.Version,
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
NotAfter: time.Now().Add(duration(days)),
NotBefore: time.Now(),
SerialNumber: serialNumber(),
PublicKey: key.Public(),
Subject: pkix.Name{
CommonName: req.Subject.CommonName,
Organization: req.Subject.Organization,
Country: req.Subject.Country,
},
}
rootCa, err := x509.CreateCertificate(rand.Reader, template, template, req.PublicKey, key)
if err != nil {
return nil, err
}
result, err := x509.ParseCertificate(rootCa)
if err != nil {
return nil, err
}
return result, nil
}
// issues a new certificate from a parent certificate authority
func NewEndEntityCert(key ed25519.PrivateKey, req *x509.CertificateRequest, parent *x509.Certificate, days int) (*x509.Certificate, error) {
template := &x509.Certificate{
Version: req.Version,
NotBefore: time.Now(),
NotAfter: time.Now().Add(duration(days)),
SerialNumber: serialNumber(),
SignatureAlgorithm: req.SignatureAlgorithm,
PublicKeyAlgorithm: req.PublicKeyAlgorithm,
PublicKey: req.PublicKey,
Subject: req.Subject,
SubjectKeyId: req.RawSubject,
Issuer: parent.Subject,
}
rootCa, err := x509.CreateCertificate(rand.Reader, template, parent, req.PublicKey, key)
if err != nil {
return nil, err
}
result, err := x509.ParseCertificate(rootCa)
if err != nil {
return nil, err
}
return result, nil
}
func SaveCert(path, name string, cert *x509.Certificate) error {
//certbytes, err := x509.ParseCertificate(cert)
if err := os.MkdirAll(path, 0644); err != nil {
return fmt.Errorf("failed to create dir %s %w", path, err)
}
certOut, err := os.Create(path + name)
if err != nil {
return fmt.Errorf("failed to open certficate file for writing: %v", err)
}
defer certOut.Close()
if err := pem.Encode(certOut, &pem.Block{
Type: "CERTIFICATE",
Bytes: cert.Raw,
}); err != nil {
return fmt.Errorf("failed to write certificate to file %v", err)
}
return nil
}
func SaveKey(path, name string, key ed25519.PrivateKey) error {
//func SaveKey(name string, key *ecdsa.PrivateKey) error {
if err := os.MkdirAll(path, 0644); err != nil {
return fmt.Errorf("failed to create dir %s %w", path, err)
}
keyOut, err := os.Create(path + name)
if err != nil {
return fmt.Errorf("failed open key file for writing: %v", err)
}
defer keyOut.Close()
privBytes, err := x509.MarshalPKCS8PrivateKey(key)
if err != nil {
return fmt.Errorf("failedto marshal key %v ", err)
}
if err := pem.Encode(keyOut, &pem.Block{
Type: "PRIVATE KEY",
Bytes: privBytes,
}); err != nil {
return fmt.Errorf("failed to write key to file %v", err)
}
return nil
}
func ReadCert(name string) (*x509.Certificate, error) {
contents, err := os.ReadFile(name)
if err != nil {
return nil, fmt.Errorf("unable to read file %w", err)
}
block, _ := pem.Decode(contents)
if block == nil || block.Type != "CERTIFICATE" {
return nil, errors.New("not a cert " + block.Type)
}
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return nil, fmt.Errorf("unable to parse cert %w", err)
}
return cert, nil
}
func ReadKey(name string) (*ed25519.PrivateKey, error) {
bytes, err := os.ReadFile(name)
if err != nil {
return nil, fmt.Errorf("unable to read file %w", err)
}
keyBytes, _ := pem.Decode(bytes)
log.Println(keyBytes.Type)
key, err := x509.ParsePKCS8PrivateKey(keyBytes.Bytes)
if err != nil {
return nil, fmt.Errorf("unable to parse file %w", err)
}
private := key.(ed25519.PrivateKey)
return &private, nil
}
func serialNumber() *big.Int {
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
if err != nil {
return nil
}
return serialNumber
}
func duration(days int) time.Duration {
hours := days * 24
duration, err := time.ParseDuration(fmt.Sprintf("%dh", hours))
if err != nil {
duration = time.Until(time.Now().Add(time.Hour * 24))
}
return duration
}