381 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			381 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package sprig
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"crypto/dsa"
 | |
| 	"crypto/ecdsa"
 | |
| 	"crypto/elliptic"
 | |
| 	"crypto/hmac"
 | |
| 	"crypto/rand"
 | |
| 	"crypto/rsa"
 | |
| 	"crypto/sha256"
 | |
| 	"crypto/x509"
 | |
| 	"crypto/x509/pkix"
 | |
| 	"encoding/asn1"
 | |
| 	"encoding/binary"
 | |
| 	"encoding/hex"
 | |
| 	"encoding/pem"
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 	"math/big"
 | |
| 	"net"
 | |
| 	"time"
 | |
| 
 | |
| 	uuid "github.com/satori/go.uuid"
 | |
| 	"golang.org/x/crypto/scrypt"
 | |
| )
 | |
| 
 | |
| func sha256sum(input string) string {
 | |
| 	hash := sha256.Sum256([]byte(input))
 | |
| 	return hex.EncodeToString(hash[:])
 | |
| }
 | |
| 
 | |
| // uuidv4 provides a safe and secure UUID v4 implementation
 | |
| func uuidv4() string {
 | |
| 	return fmt.Sprintf("%s", uuid.NewV4())
 | |
| }
 | |
| 
 | |
| var master_password_seed = "com.lyndir.masterpassword"
 | |
| 
 | |
| var password_type_templates = map[string][][]byte{
 | |
| 	"maximum": {[]byte("anoxxxxxxxxxxxxxxxxx"), []byte("axxxxxxxxxxxxxxxxxno")},
 | |
| 	"long": {[]byte("CvcvnoCvcvCvcv"), []byte("CvcvCvcvnoCvcv"), []byte("CvcvCvcvCvcvno"), []byte("CvccnoCvcvCvcv"), []byte("CvccCvcvnoCvcv"),
 | |
| 		[]byte("CvccCvcvCvcvno"), []byte("CvcvnoCvccCvcv"), []byte("CvcvCvccnoCvcv"), []byte("CvcvCvccCvcvno"), []byte("CvcvnoCvcvCvcc"),
 | |
| 		[]byte("CvcvCvcvnoCvcc"), []byte("CvcvCvcvCvccno"), []byte("CvccnoCvccCvcv"), []byte("CvccCvccnoCvcv"), []byte("CvccCvccCvcvno"),
 | |
| 		[]byte("CvcvnoCvccCvcc"), []byte("CvcvCvccnoCvcc"), []byte("CvcvCvccCvccno"), []byte("CvccnoCvcvCvcc"), []byte("CvccCvcvnoCvcc"),
 | |
| 		[]byte("CvccCvcvCvccno")},
 | |
| 	"medium": {[]byte("CvcnoCvc"), []byte("CvcCvcno")},
 | |
| 	"short":  {[]byte("Cvcn")},
 | |
| 	"basic":  {[]byte("aaanaaan"), []byte("aannaaan"), []byte("aaannaaa")},
 | |
| 	"pin":    {[]byte("nnnn")},
 | |
| }
 | |
| 
 | |
| var template_characters = map[byte]string{
 | |
| 	'V': "AEIOU",
 | |
| 	'C': "BCDFGHJKLMNPQRSTVWXYZ",
 | |
| 	'v': "aeiou",
 | |
| 	'c': "bcdfghjklmnpqrstvwxyz",
 | |
| 	'A': "AEIOUBCDFGHJKLMNPQRSTVWXYZ",
 | |
| 	'a': "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz",
 | |
| 	'n': "0123456789",
 | |
| 	'o': "@&%?,=[]_:-+*$#!'^~;()/.",
 | |
| 	'x': "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz0123456789!@#$%^&*()",
 | |
| }
 | |
| 
 | |
| func derivePassword(counter uint32, password_type, password, user, site string) string {
 | |
| 	var templates = password_type_templates[password_type]
 | |
| 	if templates == nil {
 | |
| 		return fmt.Sprintf("cannot find password template %s", password_type)
 | |
| 	}
 | |
| 
 | |
| 	var buffer bytes.Buffer
 | |
| 	buffer.WriteString(master_password_seed)
 | |
| 	binary.Write(&buffer, binary.BigEndian, uint32(len(user)))
 | |
| 	buffer.WriteString(user)
 | |
| 
 | |
| 	salt := buffer.Bytes()
 | |
| 	key, err := scrypt.Key([]byte(password), salt, 32768, 8, 2, 64)
 | |
| 	if err != nil {
 | |
| 		return fmt.Sprintf("failed to derive password: %s", err)
 | |
| 	}
 | |
| 
 | |
| 	buffer.Truncate(len(master_password_seed))
 | |
| 	binary.Write(&buffer, binary.BigEndian, uint32(len(site)))
 | |
| 	buffer.WriteString(site)
 | |
| 	binary.Write(&buffer, binary.BigEndian, counter)
 | |
| 
 | |
| 	var hmacv = hmac.New(sha256.New, key)
 | |
| 	hmacv.Write(buffer.Bytes())
 | |
| 	var seed = hmacv.Sum(nil)
 | |
| 	var temp = templates[int(seed[0])%len(templates)]
 | |
| 
 | |
| 	buffer.Truncate(0)
 | |
| 	for i, element := range temp {
 | |
| 		pass_chars := template_characters[element]
 | |
| 		pass_char := pass_chars[int(seed[i+1])%len(pass_chars)]
 | |
| 		buffer.WriteByte(pass_char)
 | |
| 	}
 | |
| 
 | |
| 	return buffer.String()
 | |
| }
 | |
| 
 | |
| func generatePrivateKey(typ string) string {
 | |
| 	var priv interface{}
 | |
| 	var err error
 | |
| 	switch typ {
 | |
| 	case "", "rsa":
 | |
| 		// good enough for government work
 | |
| 		priv, err = rsa.GenerateKey(rand.Reader, 4096)
 | |
| 	case "dsa":
 | |
| 		key := new(dsa.PrivateKey)
 | |
| 		// again, good enough for government work
 | |
| 		if err = dsa.GenerateParameters(&key.Parameters, rand.Reader, dsa.L2048N256); err != nil {
 | |
| 			return fmt.Sprintf("failed to generate dsa params: %s", err)
 | |
| 		}
 | |
| 		err = dsa.GenerateKey(key, rand.Reader)
 | |
| 		priv = key
 | |
| 	case "ecdsa":
 | |
| 		// again, good enough for government work
 | |
| 		priv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
 | |
| 	default:
 | |
| 		return "Unknown type " + typ
 | |
| 	}
 | |
| 	if err != nil {
 | |
| 		return fmt.Sprintf("failed to generate private key: %s", err)
 | |
| 	}
 | |
| 
 | |
| 	return string(pem.EncodeToMemory(pemBlockForKey(priv)))
 | |
| }
 | |
| 
 | |
| type DSAKeyFormat struct {
 | |
| 	Version       int
 | |
| 	P, Q, G, Y, X *big.Int
 | |
| }
 | |
| 
 | |
| func pemBlockForKey(priv interface{}) *pem.Block {
 | |
| 	switch k := priv.(type) {
 | |
| 	case *rsa.PrivateKey:
 | |
| 		return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)}
 | |
| 	case *dsa.PrivateKey:
 | |
| 		val := DSAKeyFormat{
 | |
| 			P: k.P, Q: k.Q, G: k.G,
 | |
| 			Y: k.Y, X: k.X,
 | |
| 		}
 | |
| 		bytes, _ := asn1.Marshal(val)
 | |
| 		return &pem.Block{Type: "DSA PRIVATE KEY", Bytes: bytes}
 | |
| 	case *ecdsa.PrivateKey:
 | |
| 		b, _ := x509.MarshalECPrivateKey(k)
 | |
| 		return &pem.Block{Type: "EC PRIVATE KEY", Bytes: b}
 | |
| 	default:
 | |
| 		return nil
 | |
| 	}
 | |
| }
 | |
| 
 | |
| type certificate struct {
 | |
| 	Cert string
 | |
| 	Key  string
 | |
| }
 | |
| 
 | |
| func generateCertificateAuthority(
 | |
| 	cn string,
 | |
| 	daysValid int,
 | |
| ) (certificate, error) {
 | |
| 	ca := certificate{}
 | |
| 
 | |
| 	template, err := getBaseCertTemplate(cn, nil, nil, daysValid)
 | |
| 	if err != nil {
 | |
| 		return ca, err
 | |
| 	}
 | |
| 	// Override KeyUsage and IsCA
 | |
| 	template.KeyUsage = x509.KeyUsageKeyEncipherment |
 | |
| 		x509.KeyUsageDigitalSignature |
 | |
| 		x509.KeyUsageCertSign
 | |
| 	template.IsCA = true
 | |
| 
 | |
| 	priv, err := rsa.GenerateKey(rand.Reader, 2048)
 | |
| 	if err != nil {
 | |
| 		return ca, fmt.Errorf("error generating rsa key: %s", err)
 | |
| 	}
 | |
| 
 | |
| 	ca.Cert, ca.Key, err = getCertAndKey(template, priv, template, priv)
 | |
| 	if err != nil {
 | |
| 		return ca, err
 | |
| 	}
 | |
| 
 | |
| 	return ca, nil
 | |
| }
 | |
| 
 | |
| func generateSelfSignedCertificate(
 | |
| 	cn string,
 | |
| 	ips []interface{},
 | |
| 	alternateDNS []interface{},
 | |
| 	daysValid int,
 | |
| ) (certificate, error) {
 | |
| 	cert := certificate{}
 | |
| 
 | |
| 	template, err := getBaseCertTemplate(cn, ips, alternateDNS, daysValid)
 | |
| 	if err != nil {
 | |
| 		return cert, err
 | |
| 	}
 | |
| 
 | |
| 	priv, err := rsa.GenerateKey(rand.Reader, 2048)
 | |
| 	if err != nil {
 | |
| 		return cert, fmt.Errorf("error generating rsa key: %s", err)
 | |
| 	}
 | |
| 
 | |
| 	cert.Cert, cert.Key, err = getCertAndKey(template, priv, template, priv)
 | |
| 	if err != nil {
 | |
| 		return cert, err
 | |
| 	}
 | |
| 
 | |
| 	return cert, nil
 | |
| }
 | |
| 
 | |
| func generateSignedCertificate(
 | |
| 	cn string,
 | |
| 	ips []interface{},
 | |
| 	alternateDNS []interface{},
 | |
| 	daysValid int,
 | |
| 	ca certificate,
 | |
| ) (certificate, error) {
 | |
| 	cert := certificate{}
 | |
| 
 | |
| 	decodedSignerCert, _ := pem.Decode([]byte(ca.Cert))
 | |
| 	if decodedSignerCert == nil {
 | |
| 		return cert, errors.New("unable to decode certificate")
 | |
| 	}
 | |
| 	signerCert, err := x509.ParseCertificate(decodedSignerCert.Bytes)
 | |
| 	if err != nil {
 | |
| 		return cert, fmt.Errorf(
 | |
| 			"error parsing certificate: decodedSignerCert.Bytes: %s",
 | |
| 			err,
 | |
| 		)
 | |
| 	}
 | |
| 	decodedSignerKey, _ := pem.Decode([]byte(ca.Key))
 | |
| 	if decodedSignerKey == nil {
 | |
| 		return cert, errors.New("unable to decode key")
 | |
| 	}
 | |
| 	signerKey, err := x509.ParsePKCS1PrivateKey(decodedSignerKey.Bytes)
 | |
| 	if err != nil {
 | |
| 		return cert, fmt.Errorf(
 | |
| 			"error parsing prive key: decodedSignerKey.Bytes: %s",
 | |
| 			err,
 | |
| 		)
 | |
| 	}
 | |
| 
 | |
| 	template, err := getBaseCertTemplate(cn, ips, alternateDNS, daysValid)
 | |
| 	if err != nil {
 | |
| 		return cert, err
 | |
| 	}
 | |
| 
 | |
| 	priv, err := rsa.GenerateKey(rand.Reader, 2048)
 | |
| 	if err != nil {
 | |
| 		return cert, fmt.Errorf("error generating rsa key: %s", err)
 | |
| 	}
 | |
| 
 | |
| 	cert.Cert, cert.Key, err = getCertAndKey(
 | |
| 		template,
 | |
| 		priv,
 | |
| 		signerCert,
 | |
| 		signerKey,
 | |
| 	)
 | |
| 	if err != nil {
 | |
| 		return cert, err
 | |
| 	}
 | |
| 
 | |
| 	return cert, nil
 | |
| }
 | |
| 
 | |
| func getCertAndKey(
 | |
| 	template *x509.Certificate,
 | |
| 	signeeKey *rsa.PrivateKey,
 | |
| 	parent *x509.Certificate,
 | |
| 	signingKey *rsa.PrivateKey,
 | |
| ) (string, string, error) {
 | |
| 	derBytes, err := x509.CreateCertificate(
 | |
| 		rand.Reader,
 | |
| 		template,
 | |
| 		parent,
 | |
| 		&signeeKey.PublicKey,
 | |
| 		signingKey,
 | |
| 	)
 | |
| 	if err != nil {
 | |
| 		return "", "", fmt.Errorf("error creating certificate: %s", err)
 | |
| 	}
 | |
| 
 | |
| 	certBuffer := bytes.Buffer{}
 | |
| 	if err := pem.Encode(
 | |
| 		&certBuffer,
 | |
| 		&pem.Block{Type: "CERTIFICATE", Bytes: derBytes},
 | |
| 	); err != nil {
 | |
| 		return "", "", fmt.Errorf("error pem-encoding certificate: %s", err)
 | |
| 	}
 | |
| 
 | |
| 	keyBuffer := bytes.Buffer{}
 | |
| 	if err := pem.Encode(
 | |
| 		&keyBuffer,
 | |
| 		&pem.Block{
 | |
| 			Type:  "RSA PRIVATE KEY",
 | |
| 			Bytes: x509.MarshalPKCS1PrivateKey(signeeKey),
 | |
| 		},
 | |
| 	); err != nil {
 | |
| 		return "", "", fmt.Errorf("error pem-encoding key: %s", err)
 | |
| 	}
 | |
| 
 | |
| 	return string(certBuffer.Bytes()), string(keyBuffer.Bytes()), nil
 | |
| }
 | |
| 
 | |
| func getBaseCertTemplate(
 | |
| 	cn string,
 | |
| 	ips []interface{},
 | |
| 	alternateDNS []interface{},
 | |
| 	daysValid int,
 | |
| ) (*x509.Certificate, error) {
 | |
| 	ipAddresses, err := getNetIPs(ips)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	dnsNames, err := getAlternateDNSStrs(alternateDNS)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	return &x509.Certificate{
 | |
| 		SerialNumber: big.NewInt(1),
 | |
| 		Subject: pkix.Name{
 | |
| 			CommonName: cn,
 | |
| 		},
 | |
| 		IPAddresses: ipAddresses,
 | |
| 		DNSNames:    dnsNames,
 | |
| 		NotBefore:   time.Now(),
 | |
| 		NotAfter:    time.Now().Add(time.Hour * 24 * time.Duration(daysValid)),
 | |
| 		KeyUsage:    x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
 | |
| 		ExtKeyUsage: []x509.ExtKeyUsage{
 | |
| 			x509.ExtKeyUsageServerAuth,
 | |
| 			x509.ExtKeyUsageClientAuth,
 | |
| 		},
 | |
| 		BasicConstraintsValid: true,
 | |
| 	}, nil
 | |
| }
 | |
| 
 | |
| func getNetIPs(ips []interface{}) ([]net.IP, error) {
 | |
| 	if ips == nil {
 | |
| 		return []net.IP{}, nil
 | |
| 	}
 | |
| 	var ipStr string
 | |
| 	var ok bool
 | |
| 	var netIP net.IP
 | |
| 	netIPs := make([]net.IP, len(ips))
 | |
| 	for i, ip := range ips {
 | |
| 		ipStr, ok = ip.(string)
 | |
| 		if !ok {
 | |
| 			return nil, fmt.Errorf("error parsing ip: %v is not a string", ip)
 | |
| 		}
 | |
| 		netIP = net.ParseIP(ipStr)
 | |
| 		if netIP == nil {
 | |
| 			return nil, fmt.Errorf("error parsing ip: %s", ipStr)
 | |
| 		}
 | |
| 		netIPs[i] = netIP
 | |
| 	}
 | |
| 	return netIPs, nil
 | |
| }
 | |
| 
 | |
| func getAlternateDNSStrs(alternateDNS []interface{}) ([]string, error) {
 | |
| 	if alternateDNS == nil {
 | |
| 		return []string{}, nil
 | |
| 	}
 | |
| 	var dnsStr string
 | |
| 	var ok bool
 | |
| 	alternateDNSStrs := make([]string, len(alternateDNS))
 | |
| 	for i, dns := range alternateDNS {
 | |
| 		dnsStr, ok = dns.(string)
 | |
| 		if !ok {
 | |
| 			return nil, fmt.Errorf(
 | |
| 				"error processing alternate dns name: %v is not a string",
 | |
| 				dns,
 | |
| 			)
 | |
| 		}
 | |
| 		alternateDNSStrs[i] = dnsStr
 | |
| 	}
 | |
| 	return alternateDNSStrs, nil
 | |
| }
 |