package sprig import ( "bytes" "crypto/dsa" "crypto/ecdsa" "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" "math/big" "net" "time" "github.com/google/uuid" "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) } // uuidv4 provides a safe and secure UUID v4 implementation func uuidv4() string { return fmt.Sprintf("%s", uuid.New()) } 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 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, ) } decodedKey, _ := pem.Decode(key) if decodedKey == nil { return crt, errors.New("unable to decode key") } _, err = x509.ParsePKCS1PrivateKey(decodedKey.Bytes) if err != nil { return crt, fmt.Errorf( "error parsing prive key: decodedKey.Bytes: %s", err, ) } crt.Cert = string(cert) crt.Key = string(key) return crt, nil } 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 } 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 }