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 }