github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/randutil/rand.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2014-2020 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  // Package randutil initialises properly random value generation and
    21  // exposes a streamlined set of functions for it, including for crypto
    22  // random tokens.
    23  package randutil
    24  
    25  import (
    26  	"crypto/sha256"
    27  	"encoding/binary"
    28  	"math/rand"
    29  	"net"
    30  	"os"
    31  	"sync"
    32  	"time"
    33  )
    34  
    35  func init() {
    36  	// golang does not init Seed() itself
    37  	rand.Seed(time.Now().UnixNano() + int64(os.Getpid()))
    38  }
    39  
    40  var moreMixedSeedOnce sync.Once
    41  
    42  func moreMixedSeed() {
    43  	moreMixedSeedOnce.Do(func() {
    44  		h := sha256.New224()
    45  		// do this instead of asking for time and pid again
    46  		var b [8]byte
    47  		rand.Read(b[:])
    48  		h.Write(b[:])
    49  		// mix in the hostname
    50  		if hostname, err := os.Hostname(); err == nil {
    51  			h.Write([]byte(hostname))
    52  		}
    53  		// mix in net interfaces hw addresses (MACs etc)
    54  		if ifaces, err := net.Interfaces(); err == nil {
    55  			for _, iface := range ifaces {
    56  				h.Write(iface.HardwareAddr)
    57  			}
    58  		}
    59  		hs := h.Sum(nil)
    60  		s := binary.LittleEndian.Uint64(hs[0:])
    61  		rand.Seed(int64(s))
    62  	})
    63  }
    64  
    65  const letters = "BCDFGHJKLMNPQRSTVWXYbcdfghjklmnpqrstvwxy0123456789"
    66  
    67  // RandomString returns a random string of length length.
    68  //
    69  // The vowels are omitted to avoid that words are created by pure
    70  // chance. Numbers are included.
    71  //
    72  // Not cryptographically secure.
    73  func RandomString(length int) string {
    74  	out := ""
    75  	for i := 0; i < length; i++ {
    76  		out += string(letters[rand.Intn(len(letters))])
    77  	}
    78  
    79  	return out
    80  }
    81  
    82  // Re-exported from math/rand for streamlining.
    83  var (
    84  	Intn   = rand.Intn
    85  	Int63n = rand.Int63n
    86  )
    87  
    88  // RandomDuration returns a random duration up to the given length.
    89  func RandomDuration(d time.Duration) time.Duration {
    90  	// try to switch to more mixed seed to avoid subsets of a
    91  	// fleet of machines with similar initial conditions to behave
    92  	// the same
    93  	moreMixedSeed()
    94  	return time.Duration(Int63n(int64(d)))
    95  }