github.com/verrazzano/verrazzano@v1.7.1/pkg/security/password/password.go (about)

     1  // Copyright (c) 2021, 2022, Oracle and/or its affiliates.
     2  // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
     3  
     4  package password
     5  
     6  import (
     7  	"crypto/rand"
     8  	b64 "encoding/base64"
     9  	"fmt"
    10  	"regexp"
    11  	"strings"
    12  )
    13  
    14  const mask = "******"
    15  
    16  const (
    17  	modeAlphaNum   = 0
    18  	modeAlphaLower = 1
    19  )
    20  
    21  // GeneratePassword will generate a password of length
    22  func GeneratePassword(length int) (string, error) {
    23  	return GeneratePasswordUsingMode(length, modeAlphaNum)
    24  }
    25  
    26  // GenerateRandomAlphaLower will generate a lower-case alpha string of length
    27  // Should be used only for generating semi-unique, non-cryptographic, non-secret strings -- NOT passwords!
    28  func GenerateRandomAlphaLower(length int) (string, error) {
    29  	return GeneratePasswordUsingMode(length, modeAlphaLower)
    30  }
    31  
    32  // GeneratePasswordUsingMode will generate a password of length with mode
    33  func GeneratePasswordUsingMode(length int, mode int) (string, error) {
    34  	if length < 1 {
    35  		return "", fmt.Errorf("cannot create password of length %d", length)
    36  	}
    37  	// Enlarge buffer so plenty of room is left when special characters are stripped out
    38  	b := make([]byte, length*4)
    39  	_, err := rand.Read(b)
    40  	if err != nil {
    41  		return "", err
    42  	}
    43  	pw := b64.StdEncoding.EncodeToString(b)
    44  	if mode == modeAlphaNum {
    45  		pw, err = makeAlphaNumeric(pw)
    46  	} else {
    47  		pw, err = makeAlphaLower(pw)
    48  	}
    49  	if err != nil {
    50  		return "", err
    51  	}
    52  	return pw[:length], nil
    53  }
    54  
    55  // makeAlphaNumeric removes all special characters from a password string
    56  func makeAlphaNumeric(input string) (string, error) {
    57  	// Make a Regex to say we only want letters and numbers
    58  	reg, err := regexp.Compile("[^a-zA-Z0-9]+")
    59  	if err != nil {
    60  		return "", err
    61  	}
    62  	return reg.ReplaceAllString(input, ""), nil
    63  }
    64  
    65  // makeAlphaLower removes all special characters and numbers from a string, and lowercases the result
    66  func makeAlphaLower(input string) (string, error) {
    67  	// Make a Regex to say we only want letters
    68  	reg, err := regexp.Compile("[^a-zA-Z]+")
    69  	if err != nil {
    70  		return "", err
    71  	}
    72  	return strings.ToLower(reg.ReplaceAllString(input, "")), nil
    73  }
    74  
    75  // MaskFunction creates a function intended to mask passwords which are substrings in other strings
    76  // f := MaskFunction("pw=") creates a function that masks strings like so:
    77  // f("pw=xyz") = "pw=******"
    78  func MaskFunction(prefix string) func(string) string {
    79  	re := regexp.MustCompile(fmt.Sprintf(`%s.*?(?:\s|$)`, prefix))
    80  	return func(s string) string {
    81  		replace := fmt.Sprintf("%s%s", prefix, mask)
    82  		matches := re.FindAllString(s, -1)
    83  		for _, match := range matches {
    84  			ch := match[len(match)-1]
    85  			var m string
    86  			if ch == ' ' || ch == '\n' || ch == '\t' || ch == '\r' {
    87  				m = match[:len(match)-1] // preserve last non-masked character if there is one
    88  			} else {
    89  				m = match
    90  			}
    91  			s = strings.ReplaceAll(s, m, replace)
    92  		}
    93  		return s
    94  	}
    95  }