github.com/greenpau/go-authcrunch@v1.1.4/pkg/util/random.go (about)

     1  // Copyright 2022 Paul Greenberg greenpau@outlook.com
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package util
    16  
    17  import (
    18  	"crypto/rand"
    19  	"encoding/base32"
    20  	"io"
    21  	//	"math"
    22  	"math/big"
    23  	mathrand "math/rand"
    24  	"unicode"
    25  )
    26  
    27  const charset = "abcdefghijklmnopqrstuvwxyz" +
    28  	"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
    29  
    30  var charsetTable = &unicode.RangeTable{
    31  	R16: []unicode.Range16{
    32  		{0x0061, 0x007a, 1}, // a-z, where a is hex 61
    33  		{0x0041, 0x005a, 1}, // A-Z, where A is hex 41
    34  		{0x0030, 0x0039, 1}, // 0-9, where 0 is hex 30
    35  	},
    36  	LatinOffset: 1,
    37  }
    38  
    39  func genRandInt(i int) uint32 {
    40  	n, err := rand.Int(rand.Reader, big.NewInt(int64(i)))
    41  	if err != nil {
    42  		return uint32(mathrand.Intn(i))
    43  	}
    44  	//if n.Uint64() > math.MaxUint32+1 {
    45  	//	return uint32(n.Uint64() & uint32(0xFFFFFFFF))
    46  	//}
    47  	return uint32(n.Uint64())
    48  }
    49  
    50  func gen(length uint32, charset string) string {
    51  	charsetLen := byte(len(charset))
    52  	b := make([]byte, length)
    53  	if _, err := io.ReadFull(rand.Reader, b); err != nil {
    54  		// for i uint32 := 0; i < length; i++ {
    55  		for i := uint32(0); i < length; {
    56  			b[i] = charset[mathrand.Intn(len(charset))]
    57  		}
    58  		return string(b)
    59  	}
    60  
    61  	for i, char := range b {
    62  		b[i] = byte(charset[char%charsetLen])
    63  	}
    64  
    65  	return string(b)
    66  }
    67  
    68  // GetRandomString returns X character long random string.
    69  func GetRandomString(i int) string {
    70  	if i < 1 {
    71  		i = 40
    72  	}
    73  	return gen(uint32(i), charset)
    74  }
    75  
    76  // GetRandomStringFromRange generates random string of a random length. The
    77  // random lenght is bounded by a and b.
    78  func GetRandomStringFromRange(a, b int) string {
    79  	var i uint32
    80  	if a > b {
    81  		i = genRandInt(a-b) + uint32(b)
    82  	} else {
    83  		i = genRandInt(b-a) + uint32(a)
    84  	}
    85  	return gen(i, charset)
    86  }
    87  
    88  // GetRandomEncodedStringFromRange return the number returned by
    89  // GetRandomStringFromRange() and encoded with Base32
    90  func GetRandomEncodedStringFromRange(a, b int) string {
    91  	s := GetRandomStringFromRange(a, b)
    92  	return base32.StdEncoding.EncodeToString([]byte(s))
    93  }
    94  
    95  // GetRandomStringFromRangeWithCharset generates random string of a random length. The
    96  // random lenght is bounded by a and b. The charset is provided.
    97  func GetRandomStringFromRangeWithCharset(a, b int, cs string) string {
    98  	var i uint32
    99  	if a > b {
   100  		i = genRandInt(a-b) + uint32(b)
   101  	} else {
   102  		i = genRandInt(b-a) + uint32(a)
   103  	}
   104  	return gen(i, cs)
   105  }
   106  
   107  // IsMalformedRandomString checks whether the provided string was generated by
   108  // the GetRandomStringFromRange() function.
   109  func IsMalformedRandomString(s string, a, b int) bool {
   110  	switch {
   111  	case len(s) == 0:
   112  		return false
   113  	case (len(s) < a) || (len(s) > b):
   114  		return true
   115  	}
   116  	for _, char := range s {
   117  		if !unicode.IsLetter(char) && !unicode.IsNumber(char) {
   118  			return true
   119  		}
   120  		if !unicode.In(char, charsetTable) {
   121  			return false
   122  		}
   123  	}
   124  	return false
   125  }
   126  
   127  // SanitizeSessionID sanitizes provided Session ID.
   128  func SanitizeSessionID(s string) string {
   129  	if IsMalformedRandomString(s, 0, 46) {
   130  		return "MALFORMED"
   131  	}
   132  	return s
   133  }