mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-11-03 20:22:25 -06:00 
			
		
		
		
	* Add Swagger spec test script * Fix Swagger spec errors not related to statuses with polls * Add API tests that post a status with a poll * Fix creating a status with a poll from form params * Fix Swagger spec errors related to statuses with polls (this is the last error) * Fix Swagger spec warnings not related to unused definitions * Suppress a duplicate list update params definition that was somehow causing wrong param names * Add Swagger test to CI - updates Drone config - vendorizes go-swagger - fixes a file extension issue that caused the test script to generate JSON instead of YAML with the vendorized version * Put `Sample: ` on its own line everywhere * Remove unused id param from emojiCategoriesGet * Add 5 more pairs of profile fields to account update API Swagger * Remove Swagger prefix from dummy fields It makes the generated code look weird * Manually annotate params for statusCreate operation * Fix all remaining Swagger spec warnings - Change some models into operation parameters - Ignore models that already correspond to manually documented operation parameters but can't be trivially changed (those with file fields) * Documented that creating a status with scheduled_at isn't implemented yet * sign drone.yml * Fix filter API Swagger errors * fixup! Fix filter API Swagger errors --------- Co-authored-by: tobi <tobi.smethurst@protonmail.com>
		
			
				
	
	
		
			653 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			653 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package sprig
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	"crypto"
 | 
						|
	"crypto/aes"
 | 
						|
	"crypto/cipher"
 | 
						|
	"crypto/dsa"
 | 
						|
	"crypto/ecdsa"
 | 
						|
	"crypto/ed25519"
 | 
						|
	"crypto/elliptic"
 | 
						|
	"crypto/hmac"
 | 
						|
	"crypto/rand"
 | 
						|
	"crypto/rsa"
 | 
						|
	"crypto/sha1"
 | 
						|
	"crypto/sha256"
 | 
						|
	"crypto/x509"
 | 
						|
	"crypto/x509/pkix"
 | 
						|
	"encoding/asn1"
 | 
						|
	"encoding/base64"
 | 
						|
	"encoding/binary"
 | 
						|
	"encoding/hex"
 | 
						|
	"encoding/pem"
 | 
						|
	"errors"
 | 
						|
	"fmt"
 | 
						|
	"hash/adler32"
 | 
						|
	"io"
 | 
						|
	"math/big"
 | 
						|
	"net"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"strings"
 | 
						|
 | 
						|
	"github.com/google/uuid"
 | 
						|
	bcrypt_lib "golang.org/x/crypto/bcrypt"
 | 
						|
	"golang.org/x/crypto/scrypt"
 | 
						|
)
 | 
						|
 | 
						|
func sha256sum(input string) string {
 | 
						|
	hash := sha256.Sum256([]byte(input))
 | 
						|
	return hex.EncodeToString(hash[:])
 | 
						|
}
 | 
						|
 | 
						|
func sha1sum(input string) string {
 | 
						|
	hash := sha1.Sum([]byte(input))
 | 
						|
	return hex.EncodeToString(hash[:])
 | 
						|
}
 | 
						|
 | 
						|
func adler32sum(input string) string {
 | 
						|
	hash := adler32.Checksum([]byte(input))
 | 
						|
	return fmt.Sprintf("%d", hash)
 | 
						|
}
 | 
						|
 | 
						|
func bcrypt(input string) string {
 | 
						|
	hash, err := bcrypt_lib.GenerateFromPassword([]byte(input), bcrypt_lib.DefaultCost)
 | 
						|
	if err != nil {
 | 
						|
		return fmt.Sprintf("failed to encrypt string with bcrypt: %s", err)
 | 
						|
	}
 | 
						|
 | 
						|
	return string(hash)
 | 
						|
}
 | 
						|
 | 
						|
func htpasswd(username string, password string) string {
 | 
						|
	if strings.Contains(username, ":") {
 | 
						|
		return fmt.Sprintf("invalid username: %s", username)
 | 
						|
	}
 | 
						|
	return fmt.Sprintf("%s:%s", username, bcrypt(password))
 | 
						|
}
 | 
						|
 | 
						|
func randBytes(count int) (string, error) {
 | 
						|
	buf := make([]byte, count)
 | 
						|
	if _, err := rand.Read(buf); err != nil {
 | 
						|
		return "", err
 | 
						|
	}
 | 
						|
	return base64.StdEncoding.EncodeToString(buf), nil
 | 
						|
}
 | 
						|
 | 
						|
// uuidv4 provides a safe and secure UUID v4 implementation
 | 
						|
func uuidv4() string {
 | 
						|
	return uuid.New().String()
 | 
						|
}
 | 
						|
 | 
						|
var masterPasswordSeed = "com.lyndir.masterpassword"
 | 
						|
 | 
						|
var passwordTypeTemplates = 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 templateCharacters = map[byte]string{
 | 
						|
	'V': "AEIOU",
 | 
						|
	'C': "BCDFGHJKLMNPQRSTVWXYZ",
 | 
						|
	'v': "aeiou",
 | 
						|
	'c': "bcdfghjklmnpqrstvwxyz",
 | 
						|
	'A': "AEIOUBCDFGHJKLMNPQRSTVWXYZ",
 | 
						|
	'a': "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz",
 | 
						|
	'n': "0123456789",
 | 
						|
	'o': "@&%?,=[]_:-+*$#!'^~;()/.",
 | 
						|
	'x': "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz0123456789!@#$%^&*()",
 | 
						|
}
 | 
						|
 | 
						|
func derivePassword(counter uint32, passwordType, password, user, site string) string {
 | 
						|
	var templates = passwordTypeTemplates[passwordType]
 | 
						|
	if templates == nil {
 | 
						|
		return fmt.Sprintf("cannot find password template %s", passwordType)
 | 
						|
	}
 | 
						|
 | 
						|
	var buffer bytes.Buffer
 | 
						|
	buffer.WriteString(masterPasswordSeed)
 | 
						|
	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(masterPasswordSeed))
 | 
						|
	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 {
 | 
						|
		passChars := templateCharacters[element]
 | 
						|
		passChar := passChars[int(seed[i+1])%len(passChars)]
 | 
						|
		buffer.WriteByte(passChar)
 | 
						|
	}
 | 
						|
 | 
						|
	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)
 | 
						|
	case "ed25519":
 | 
						|
		_, priv, err = ed25519.GenerateKey(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)))
 | 
						|
}
 | 
						|
 | 
						|
// DSAKeyFormat stores the format for DSA keys.
 | 
						|
// Used by pemBlockForKey
 | 
						|
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:
 | 
						|
		// attempt PKCS#8 format for all other keys
 | 
						|
		b, err := x509.MarshalPKCS8PrivateKey(k)
 | 
						|
		if err != nil {
 | 
						|
			return nil
 | 
						|
		}
 | 
						|
		return &pem.Block{Type: "PRIVATE KEY", Bytes: b}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func parsePrivateKeyPEM(pemBlock string) (crypto.PrivateKey, error) {
 | 
						|
	block, _ := pem.Decode([]byte(pemBlock))
 | 
						|
	if block == nil {
 | 
						|
		return nil, errors.New("no PEM data in input")
 | 
						|
	}
 | 
						|
 | 
						|
	if block.Type == "PRIVATE KEY" {
 | 
						|
		priv, err := x509.ParsePKCS8PrivateKey(block.Bytes)
 | 
						|
		if err != nil {
 | 
						|
			return nil, fmt.Errorf("decoding PEM as PKCS#8: %s", err)
 | 
						|
		}
 | 
						|
		return priv, nil
 | 
						|
	} else if !strings.HasSuffix(block.Type, " PRIVATE KEY") {
 | 
						|
		return nil, fmt.Errorf("no private key data in PEM block of type %s", block.Type)
 | 
						|
	}
 | 
						|
 | 
						|
	switch block.Type[:len(block.Type)-12] { // strip " PRIVATE KEY"
 | 
						|
	case "RSA":
 | 
						|
		priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
 | 
						|
		if err != nil {
 | 
						|
			return nil, fmt.Errorf("parsing RSA private key from PEM: %s", err)
 | 
						|
		}
 | 
						|
		return priv, nil
 | 
						|
	case "EC":
 | 
						|
		priv, err := x509.ParseECPrivateKey(block.Bytes)
 | 
						|
		if err != nil {
 | 
						|
			return nil, fmt.Errorf("parsing EC private key from PEM: %s", err)
 | 
						|
		}
 | 
						|
		return priv, nil
 | 
						|
	case "DSA":
 | 
						|
		var k DSAKeyFormat
 | 
						|
		_, err := asn1.Unmarshal(block.Bytes, &k)
 | 
						|
		if err != nil {
 | 
						|
			return nil, fmt.Errorf("parsing DSA private key from PEM: %s", err)
 | 
						|
		}
 | 
						|
		priv := &dsa.PrivateKey{
 | 
						|
			PublicKey: dsa.PublicKey{
 | 
						|
				Parameters: dsa.Parameters{
 | 
						|
					P: k.P, Q: k.Q, G: k.G,
 | 
						|
				},
 | 
						|
				Y: k.Y,
 | 
						|
			},
 | 
						|
			X: k.X,
 | 
						|
		}
 | 
						|
		return priv, nil
 | 
						|
	default:
 | 
						|
		return nil, fmt.Errorf("invalid private key type %s", block.Type)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func getPublicKey(priv crypto.PrivateKey) (crypto.PublicKey, error) {
 | 
						|
	switch k := priv.(type) {
 | 
						|
	case interface{ Public() crypto.PublicKey }:
 | 
						|
		return k.Public(), nil
 | 
						|
	case *dsa.PrivateKey:
 | 
						|
		return &k.PublicKey, nil
 | 
						|
	default:
 | 
						|
		return nil, fmt.Errorf("unable to get public key for type %T", priv)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
type certificate struct {
 | 
						|
	Cert string
 | 
						|
	Key  string
 | 
						|
}
 | 
						|
 | 
						|
func buildCustomCertificate(b64cert string, b64key string) (certificate, error) {
 | 
						|
	crt := certificate{}
 | 
						|
 | 
						|
	cert, err := base64.StdEncoding.DecodeString(b64cert)
 | 
						|
	if err != nil {
 | 
						|
		return crt, errors.New("unable to decode base64 certificate")
 | 
						|
	}
 | 
						|
 | 
						|
	key, err := base64.StdEncoding.DecodeString(b64key)
 | 
						|
	if err != nil {
 | 
						|
		return crt, errors.New("unable to decode base64 private key")
 | 
						|
	}
 | 
						|
 | 
						|
	decodedCert, _ := pem.Decode(cert)
 | 
						|
	if decodedCert == nil {
 | 
						|
		return crt, errors.New("unable to decode certificate")
 | 
						|
	}
 | 
						|
	_, err = x509.ParseCertificate(decodedCert.Bytes)
 | 
						|
	if err != nil {
 | 
						|
		return crt, fmt.Errorf(
 | 
						|
			"error parsing certificate: decodedCert.Bytes: %s",
 | 
						|
			err,
 | 
						|
		)
 | 
						|
	}
 | 
						|
 | 
						|
	_, err = parsePrivateKeyPEM(string(key))
 | 
						|
	if err != nil {
 | 
						|
		return crt, fmt.Errorf(
 | 
						|
			"error parsing private key: %s",
 | 
						|
			err,
 | 
						|
		)
 | 
						|
	}
 | 
						|
 | 
						|
	crt.Cert = string(cert)
 | 
						|
	crt.Key = string(key)
 | 
						|
 | 
						|
	return crt, nil
 | 
						|
}
 | 
						|
 | 
						|
func generateCertificateAuthority(
 | 
						|
	cn string,
 | 
						|
	daysValid int,
 | 
						|
) (certificate, error) {
 | 
						|
	priv, err := rsa.GenerateKey(rand.Reader, 2048)
 | 
						|
	if err != nil {
 | 
						|
		return certificate{}, fmt.Errorf("error generating rsa key: %s", err)
 | 
						|
	}
 | 
						|
 | 
						|
	return generateCertificateAuthorityWithKeyInternal(cn, daysValid, priv)
 | 
						|
}
 | 
						|
 | 
						|
func generateCertificateAuthorityWithPEMKey(
 | 
						|
	cn string,
 | 
						|
	daysValid int,
 | 
						|
	privPEM string,
 | 
						|
) (certificate, error) {
 | 
						|
	priv, err := parsePrivateKeyPEM(privPEM)
 | 
						|
	if err != nil {
 | 
						|
		return certificate{}, fmt.Errorf("parsing private key: %s", err)
 | 
						|
	}
 | 
						|
	return generateCertificateAuthorityWithKeyInternal(cn, daysValid, priv)
 | 
						|
}
 | 
						|
 | 
						|
func generateCertificateAuthorityWithKeyInternal(
 | 
						|
	cn string,
 | 
						|
	daysValid int,
 | 
						|
	priv crypto.PrivateKey,
 | 
						|
) (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
 | 
						|
 | 
						|
	ca.Cert, ca.Key, err = getCertAndKey(template, priv, template, priv)
 | 
						|
 | 
						|
	return ca, err
 | 
						|
}
 | 
						|
 | 
						|
func generateSelfSignedCertificate(
 | 
						|
	cn string,
 | 
						|
	ips []interface{},
 | 
						|
	alternateDNS []interface{},
 | 
						|
	daysValid int,
 | 
						|
) (certificate, error) {
 | 
						|
	priv, err := rsa.GenerateKey(rand.Reader, 2048)
 | 
						|
	if err != nil {
 | 
						|
		return certificate{}, fmt.Errorf("error generating rsa key: %s", err)
 | 
						|
	}
 | 
						|
	return generateSelfSignedCertificateWithKeyInternal(cn, ips, alternateDNS, daysValid, priv)
 | 
						|
}
 | 
						|
 | 
						|
func generateSelfSignedCertificateWithPEMKey(
 | 
						|
	cn string,
 | 
						|
	ips []interface{},
 | 
						|
	alternateDNS []interface{},
 | 
						|
	daysValid int,
 | 
						|
	privPEM string,
 | 
						|
) (certificate, error) {
 | 
						|
	priv, err := parsePrivateKeyPEM(privPEM)
 | 
						|
	if err != nil {
 | 
						|
		return certificate{}, fmt.Errorf("parsing private key: %s", err)
 | 
						|
	}
 | 
						|
	return generateSelfSignedCertificateWithKeyInternal(cn, ips, alternateDNS, daysValid, priv)
 | 
						|
}
 | 
						|
 | 
						|
func generateSelfSignedCertificateWithKeyInternal(
 | 
						|
	cn string,
 | 
						|
	ips []interface{},
 | 
						|
	alternateDNS []interface{},
 | 
						|
	daysValid int,
 | 
						|
	priv crypto.PrivateKey,
 | 
						|
) (certificate, error) {
 | 
						|
	cert := certificate{}
 | 
						|
 | 
						|
	template, err := getBaseCertTemplate(cn, ips, alternateDNS, daysValid)
 | 
						|
	if err != nil {
 | 
						|
		return cert, err
 | 
						|
	}
 | 
						|
 | 
						|
	cert.Cert, cert.Key, err = getCertAndKey(template, priv, template, priv)
 | 
						|
 | 
						|
	return cert, err
 | 
						|
}
 | 
						|
 | 
						|
func generateSignedCertificate(
 | 
						|
	cn string,
 | 
						|
	ips []interface{},
 | 
						|
	alternateDNS []interface{},
 | 
						|
	daysValid int,
 | 
						|
	ca certificate,
 | 
						|
) (certificate, error) {
 | 
						|
	priv, err := rsa.GenerateKey(rand.Reader, 2048)
 | 
						|
	if err != nil {
 | 
						|
		return certificate{}, fmt.Errorf("error generating rsa key: %s", err)
 | 
						|
	}
 | 
						|
	return generateSignedCertificateWithKeyInternal(cn, ips, alternateDNS, daysValid, ca, priv)
 | 
						|
}
 | 
						|
 | 
						|
func generateSignedCertificateWithPEMKey(
 | 
						|
	cn string,
 | 
						|
	ips []interface{},
 | 
						|
	alternateDNS []interface{},
 | 
						|
	daysValid int,
 | 
						|
	ca certificate,
 | 
						|
	privPEM string,
 | 
						|
) (certificate, error) {
 | 
						|
	priv, err := parsePrivateKeyPEM(privPEM)
 | 
						|
	if err != nil {
 | 
						|
		return certificate{}, fmt.Errorf("parsing private key: %s", err)
 | 
						|
	}
 | 
						|
	return generateSignedCertificateWithKeyInternal(cn, ips, alternateDNS, daysValid, ca, priv)
 | 
						|
}
 | 
						|
 | 
						|
func generateSignedCertificateWithKeyInternal(
 | 
						|
	cn string,
 | 
						|
	ips []interface{},
 | 
						|
	alternateDNS []interface{},
 | 
						|
	daysValid int,
 | 
						|
	ca certificate,
 | 
						|
	priv crypto.PrivateKey,
 | 
						|
) (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,
 | 
						|
		)
 | 
						|
	}
 | 
						|
	signerKey, err := parsePrivateKeyPEM(ca.Key)
 | 
						|
	if err != nil {
 | 
						|
		return cert, fmt.Errorf(
 | 
						|
			"error parsing private key: %s",
 | 
						|
			err,
 | 
						|
		)
 | 
						|
	}
 | 
						|
 | 
						|
	template, err := getBaseCertTemplate(cn, ips, alternateDNS, daysValid)
 | 
						|
	if err != nil {
 | 
						|
		return cert, err
 | 
						|
	}
 | 
						|
 | 
						|
	cert.Cert, cert.Key, err = getCertAndKey(
 | 
						|
		template,
 | 
						|
		priv,
 | 
						|
		signerCert,
 | 
						|
		signerKey,
 | 
						|
	)
 | 
						|
 | 
						|
	return cert, err
 | 
						|
}
 | 
						|
 | 
						|
func getCertAndKey(
 | 
						|
	template *x509.Certificate,
 | 
						|
	signeeKey crypto.PrivateKey,
 | 
						|
	parent *x509.Certificate,
 | 
						|
	signingKey crypto.PrivateKey,
 | 
						|
) (string, string, error) {
 | 
						|
	signeePubKey, err := getPublicKey(signeeKey)
 | 
						|
	if err != nil {
 | 
						|
		return "", "", fmt.Errorf("error retrieving public key from signee key: %s", err)
 | 
						|
	}
 | 
						|
	derBytes, err := x509.CreateCertificate(
 | 
						|
		rand.Reader,
 | 
						|
		template,
 | 
						|
		parent,
 | 
						|
		signeePubKey,
 | 
						|
		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,
 | 
						|
		pemBlockForKey(signeeKey),
 | 
						|
	); err != nil {
 | 
						|
		return "", "", fmt.Errorf("error pem-encoding key: %s", err)
 | 
						|
	}
 | 
						|
 | 
						|
	return certBuffer.String(), keyBuffer.String(), 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
 | 
						|
	}
 | 
						|
	serialNumberUpperBound := new(big.Int).Lsh(big.NewInt(1), 128)
 | 
						|
	serialNumber, err := rand.Int(rand.Reader, serialNumberUpperBound)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	return &x509.Certificate{
 | 
						|
		SerialNumber: serialNumber,
 | 
						|
		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
 | 
						|
}
 | 
						|
 | 
						|
func encryptAES(password string, plaintext string) (string, error) {
 | 
						|
	if plaintext == "" {
 | 
						|
		return "", nil
 | 
						|
	}
 | 
						|
 | 
						|
	key := make([]byte, 32)
 | 
						|
	copy(key, []byte(password))
 | 
						|
	block, err := aes.NewCipher(key)
 | 
						|
	if err != nil {
 | 
						|
		return "", err
 | 
						|
	}
 | 
						|
 | 
						|
	content := []byte(plaintext)
 | 
						|
	blockSize := block.BlockSize()
 | 
						|
	padding := blockSize - len(content)%blockSize
 | 
						|
	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
 | 
						|
	content = append(content, padtext...)
 | 
						|
 | 
						|
	ciphertext := make([]byte, aes.BlockSize+len(content))
 | 
						|
 | 
						|
	iv := ciphertext[:aes.BlockSize]
 | 
						|
	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
 | 
						|
		return "", err
 | 
						|
	}
 | 
						|
 | 
						|
	mode := cipher.NewCBCEncrypter(block, iv)
 | 
						|
	mode.CryptBlocks(ciphertext[aes.BlockSize:], content)
 | 
						|
 | 
						|
	return base64.StdEncoding.EncodeToString(ciphertext), nil
 | 
						|
}
 | 
						|
 | 
						|
func decryptAES(password string, crypt64 string) (string, error) {
 | 
						|
	if crypt64 == "" {
 | 
						|
		return "", nil
 | 
						|
	}
 | 
						|
 | 
						|
	key := make([]byte, 32)
 | 
						|
	copy(key, []byte(password))
 | 
						|
 | 
						|
	crypt, err := base64.StdEncoding.DecodeString(crypt64)
 | 
						|
	if err != nil {
 | 
						|
		return "", err
 | 
						|
	}
 | 
						|
 | 
						|
	block, err := aes.NewCipher(key)
 | 
						|
	if err != nil {
 | 
						|
		return "", err
 | 
						|
	}
 | 
						|
 | 
						|
	iv := crypt[:aes.BlockSize]
 | 
						|
	crypt = crypt[aes.BlockSize:]
 | 
						|
	decrypted := make([]byte, len(crypt))
 | 
						|
	mode := cipher.NewCBCDecrypter(block, iv)
 | 
						|
	mode.CryptBlocks(decrypted, crypt)
 | 
						|
 | 
						|
	return string(decrypted[:len(decrypted)-int(decrypted[len(decrypted)-1])]), nil
 | 
						|
}
 |