From 395b8b86e55b1c1925a0baa8904f97068ae4c920 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Wed, 19 Jan 2022 17:34:44 +0000 Subject: [PATCH 1/3] feat: Add cryptorand package for random string and number generation This package is taken from the monorepo, and was renamed from crand for improved clarity. It will be used for API key generation. --- cryptorand/must.go | 11 ++ cryptorand/numbers.go | 194 +++++++++++++++++++++++++++++ cryptorand/numbers_must.go | 78 ++++++++++++ cryptorand/numbers_test.go | 212 ++++++++++++++++++++++++++++++++ cryptorand/strings.go | 84 +++++++++++++ cryptorand/strings_must.go | 34 +++++ cryptorand/strings_test.go | 245 +++++++++++++++++++++++++++++++++++++ go.mod | 4 +- go.sum | 1 + 9 files changed, 861 insertions(+), 2 deletions(-) create mode 100644 cryptorand/must.go create mode 100644 cryptorand/numbers.go create mode 100644 cryptorand/numbers_must.go create mode 100644 cryptorand/numbers_test.go create mode 100644 cryptorand/strings.go create mode 100644 cryptorand/strings_must.go create mode 100644 cryptorand/strings_test.go diff --git a/cryptorand/must.go b/cryptorand/must.go new file mode 100644 index 0000000000000..184c012625995 --- /dev/null +++ b/cryptorand/must.go @@ -0,0 +1,11 @@ +package cryptorand + +import "golang.org/x/xerrors" + +// must is a utility function that panics with the given error if +// err is non-nil. +func must(err error) { + if err != nil { + panic(xerrors.Errorf("crand: %w", err)) + } +} diff --git a/cryptorand/numbers.go b/cryptorand/numbers.go new file mode 100644 index 0000000000000..e685aa41d0d63 --- /dev/null +++ b/cryptorand/numbers.go @@ -0,0 +1,194 @@ +package cryptorand + +import ( + "crypto/rand" + "encoding/binary" + + "golang.org/x/xerrors" +) + +// Most of this code is inspired by math/rand, so shares similar +// functions and implementations, but uses crypto/rand to generate +// random Int63 data. + +// Int64 returns a non-negative random 63-bit integer as a int64. +func Int63() (int64, error) { + var i int64 + err := binary.Read(rand.Reader, binary.BigEndian, &i) + if err != nil { + return 0, xerrors.Errorf("read binary: %w", err) + } + + if i < 0 { + return -i, nil + } + return i, nil +} + +// Uint64 returns a random 64-bit integer as a uint64. +func Uint64() (uint64, error) { + upper, err := Int63() + if err != nil { + return 0, xerrors.Errorf("read upper: %w", err) + } + + lower, err := Int63() + if err != nil { + return 0, xerrors.Errorf("read lower: %w", err) + } + + return uint64(lower)>>31 | uint64(upper)<<32, nil +} + +// Int31 returns a non-negative random 31-bit integer as a int32. +func Int31() (int32, error) { + i, err := Int63() + if err != nil { + return 0, err + } + + return int32(i >> 32), nil +} + +// Uint32 returns a 32-bit value as a uint32. +func Uint32() (uint32, error) { + i, err := Int63() + if err != nil { + return 0, err + } + + return uint32(i >> 31), nil +} + +// Int returns a non-negative random integer as a int. +func Int() (int, error) { + i, err := Int63() + if err != nil { + return 0, err + } + + if i < 0 { + return int(-i), nil + } + return int(i), nil +} + +// Int63n returns a non-negative random integer in [0,n) as a int64. +func Int63n(n int64) (int64, error) { + if n <= 0 { + panic("invalid argument to Int63n") + } + + max := int64((1 << 63) - 1 - (1<<63)%uint64(n)) + i, err := Int63() + if err != nil { + return 0, err + } + + for i > max { + i, err = Int63() + if err != nil { + return 0, err + } + } + + return i % n, nil +} + +// Int31n returns a non-negative integer in [0,n) as a int32. +func Int31n(n int32) (int32, error) { + i, err := Uint32() + if err != nil { + return 0, err + } + + return UnbiasedModulo32(i, n) +} + +// UnbiasedModulo32 uniformly modulos v by n over a sufficiently large data +// set, regenerating v if necessary. n must be > 0. All input bits in v must be +// fully random, you cannot cast a random uint8/uint16 for input into this +// function. +func UnbiasedModulo32(v uint32, n int32) (int32, error) { + prod := uint64(v) * uint64(n) + low := uint32(prod) + if low < uint32(n) { + thresh := uint32(-n) % uint32(n) + for low < thresh { + var err error + v, err = Uint32() + if err != nil { + return 0, err + } + prod = uint64(v) * uint64(n) + low = uint32(prod) + } + } + return int32(prod >> 32), nil +} + +// Intn returns a non-negative integer in [0,n) as a int. +func Intn(n int) (int, error) { + if n <= 0 { + panic("n must be a positive nonzero number") + } + + if n <= 1<<31-1 { + i, err := Int31n(int32(n)) + if err != nil { + return 0, err + } + + return int(i), nil + } + + i, err := Int63n(int64(n)) + if err != nil { + return 0, err + } + + return int(i), nil +} + +// Float64 returns a random number in [0.0,1.0) as a float64. +func Float64() (float64, error) { +again: + i, err := Int63n(1 << 53) + if err != nil { + return 0, err + } + + f := (float64(i) / (1 << 53)) + if f == 1 { + goto again + } + + return f, nil +} + +// Float32 returns a random number in [0.0,1.0) as a float32. +func Float32() (float32, error) { +again: + i, err := Float64() + if err != nil { + return 0, err + } + + f := float32(i) + if f == 1 { + goto again + } + + return f, nil +} + +// Bool returns a random true/false value as a bool. +func Bool() (bool, error) { + i, err := Uint64() + if err != nil { + return false, err + } + + // True if the least significant bit is 1 + return i&1 == 1, nil +} diff --git a/cryptorand/numbers_must.go b/cryptorand/numbers_must.go new file mode 100644 index 0000000000000..36aff2a2a9feb --- /dev/null +++ b/cryptorand/numbers_must.go @@ -0,0 +1,78 @@ +package cryptorand + +// MustInt63 returns a non-negative random 63-bit integer as a int64. +func MustInt63() int64 { + i, err := Int63() + must(err) + return i +} + +// MustUint64 returns a random 64-bit integer as a uint64. +func MustUint64() uint64 { + i, err := Uint64() + must(err) + return i +} + +// MustUint32 returns a 32-bit value as a uint32. +func MustUint32() uint32 { + i, err := Uint32() + must(err) + return i +} + +// MustInt31 returns a non-negative random 31-bit integer as a int32. +func MustInt31() int32 { + i, err := Int31() + must(err) + return i +} + +// MustInt returns a non-negative random integer as a int. +func MustInt() int { + i, err := Int() + must(err) + return i +} + +// MustInt63n returns a non-negative random integer in [0,n) as a int64. +func MustInt63n(n int64) int64 { + i, err := Int63n(n) + must(err) + return i +} + +// MustInt31n returns a non-negative integer in [0,n) as a int32. +func MustInt31n(n int32) int32 { + i, err := Int31n(n) + must(err) + return i +} + +// MustIntn returns a non-negative integer in [0,n) as a int. +func MustIntn(n int) int { + i, err := Intn(n) + must(err) + return i +} + +// MustFloat64 returns a random number in [0.0,1.0) as a float64. +func MustFloat64() float64 { + f, err := Float64() + must(err) + return f +} + +// MustFloat32 returns a random number in [0.0,1.0) as a float32. +func MustFloat32() float32 { + f, err := Float32() + must(err) + return f +} + +// MustBool returns a random true/false value as a bool. +func MustBool() bool { + b, err := Bool() + must(err) + return b +} diff --git a/cryptorand/numbers_test.go b/cryptorand/numbers_test.go new file mode 100644 index 0000000000000..e9a21037ca9c9 --- /dev/null +++ b/cryptorand/numbers_test.go @@ -0,0 +1,212 @@ +package cryptorand_test + +import ( + "crypto/rand" + "encoding/binary" + "testing" + + "github.com/coder/coder/cryptorand" + "github.com/stretchr/testify/require" +) + +func TestInt63(t *testing.T) { + t.Parallel() + + for i := 0; i < 20; i++ { + v, err := cryptorand.Int63() + require.NoError(t, err, "unexpected error from Int63") + t.Logf("value: %v <- random?", v) + require.True(t, v >= 0, "values must be positive") + + v = cryptorand.MustInt63() + t.Logf("value: %v <- random?", v) + require.True(t, v >= 0, "values must be positive") + } +} + +func TestUint64(t *testing.T) { + t.Parallel() + + for i := 0; i < 20; i++ { + v, err := cryptorand.Uint64() + require.NoError(t, err, "unexpected error from Uint64") + t.Logf("value: %v <- random?", v) + + v = cryptorand.MustUint64() + t.Logf("value: %v <- random?", v) + } +} + +func TestInt31(t *testing.T) { + t.Parallel() + + for i := 0; i < 20; i++ { + v, err := cryptorand.Int31() + require.NoError(t, err, "unexpected error from Int31") + t.Logf("value: %v <- random?", v) + require.True(t, v >= 0, "values must be positive") + + v = cryptorand.MustInt31() + t.Logf("value: %v <- random?", v) + require.True(t, v >= 0, "values must be positive") + } +} + +func TestUnbiasedModulo32(t *testing.T) { + t.Parallel() + const mod = 7 + dist := [mod]uint32{} + + for i := 0; i < 1000; i++ { + b := [4]byte{} + _, _ = rand.Read(b[:]) + v, err := cryptorand.UnbiasedModulo32(binary.BigEndian.Uint32(b[:]), mod) + require.NoError(t, err, "unexpected error from UnbiasedModulo32") + dist[v]++ + } + + t.Logf("dist: %+v <- evenly distributed?", dist) +} + +func TestUint32(t *testing.T) { + t.Parallel() + + for i := 0; i < 20; i++ { + v, err := cryptorand.Uint32() + require.NoError(t, err, "unexpected error from Uint32") + t.Logf("value: %v <- random?", v) + + v = cryptorand.MustUint32() + t.Logf("value: %v <- random?", v) + } +} + +func TestInt(t *testing.T) { + t.Parallel() + + for i := 0; i < 20; i++ { + v, err := cryptorand.Int() + require.NoError(t, err, "unexpected error from Int") + t.Logf("value: %v <- random?", v) + require.True(t, v >= 0, "values must be positive") + + v = cryptorand.MustInt() + t.Logf("value: %v <- random?", v) + require.True(t, v >= 0, "values must be positive") + } +} + +func TestInt63n(t *testing.T) { + t.Parallel() + + for i := 0; i < 20; i++ { + v, err := cryptorand.Int63n(1 << 35) + require.NoError(t, err, "unexpected error from Int63n") + t.Logf("value: %v <- random?", v) + require.True(t, v >= 0, "values must be positive") + require.True(t, v < 1<<35, "values must be less than 1<<35") + + v = cryptorand.MustInt63n(1 << 40) + t.Logf("value: %v <- random?", v) + require.True(t, v >= 0, "values must be positive") + require.True(t, v < 1<<40, "values must be less than 1<<40") + } +} + +func TestInt31n(t *testing.T) { + t.Parallel() + + for i := 0; i < 20; i++ { + v, err := cryptorand.Int31n(100) + require.NoError(t, err, "unexpected error from Int31n") + t.Logf("value: %v <- random?", v) + require.True(t, v >= 0, "values must be positive") + require.True(t, v < 100, "values must be less than 100") + + v = cryptorand.MustInt31n(500) + t.Logf("value: %v <- random?", v) + require.True(t, v >= 0, "values must be positive") + require.True(t, v < 500, "values must be less than 500") + } +} + +func TestIntn(t *testing.T) { + t.Parallel() + + for i := 0; i < 20; i++ { + v, err := cryptorand.Intn(100) + require.NoError(t, err, "unexpected error from Intn") + t.Logf("value: %v <- random?", v) + require.True(t, v >= 0, "values must be positive") + require.True(t, v < 100, "values must be less than 100") + + v = cryptorand.MustIntn(500) + t.Logf("value: %v <- random?", v) + require.True(t, v >= 0, "values must be positive") + require.True(t, v < 500, "values must be less than 500") + + v = cryptorand.MustIntn(1 << 40) + t.Logf("value: %v <- random?", v) + require.True(t, v >= 0, "values must be positive") + require.True(t, v < 1<<40, "values must be less than 1<<40") + } +} + +func TestFloat64(t *testing.T) { + t.Parallel() + + for i := 0; i < 20; i++ { + v, err := cryptorand.Float64() + require.NoError(t, err, "unexpected error from Float64") + t.Logf("value: %v <- random?", v) + require.True(t, v >= 0.0, "values must be positive") + require.True(t, v < 1.0, "values must be less than 1.0") + + v = cryptorand.MustFloat64() + t.Logf("value: %v <- random?", v) + require.True(t, v >= 0.0, "values must be positive") + require.True(t, v < 1.0, "values must be less than 1.0") + } +} + +func TestFloat32(t *testing.T) { + t.Parallel() + + for i := 0; i < 20; i++ { + v, err := cryptorand.Float32() + require.NoError(t, err, "unexpected error from Float32") + t.Logf("value: %v <- random?", v) + require.True(t, v >= 0.0, "values must be positive") + require.True(t, v < 1.0, "values must be less than 1.0") + + v = cryptorand.MustFloat32() + t.Logf("value: %v <- random?", v) + require.True(t, v >= 0.0, "values must be positive") + require.True(t, v < 1.0, "values must be less than 1.0") + } +} + +func TestBool(t *testing.T) { + t.Parallel() + + const iterations = 10000 + trueCount := 0 + + for i := 0; i < iterations; i += 2 { + v, err := cryptorand.Bool() + require.NoError(t, err, "unexpected error from Bool") + if v { + trueCount++ + } + + v = cryptorand.MustBool() + if v { + trueCount++ + } + } + + percentage := (float64(trueCount) / iterations) * 100 + t.Logf("number of true values: %d of %d total (%.2f%%)", trueCount, iterations, percentage) + require.True(t, percentage > 48, "expected more than 48 percent of values to be true") + require.True(t, percentage < 52, "expected less than 52 percent of values to be true") +} diff --git a/cryptorand/strings.go b/cryptorand/strings.go new file mode 100644 index 0000000000000..897a91d1058e2 --- /dev/null +++ b/cryptorand/strings.go @@ -0,0 +1,84 @@ +package cryptorand + +import ( + "crypto/rand" + "encoding/binary" + "strings" +) + +// Charsets +const ( + // Numeric includes decimal numbers (0-9) + Numeric = "0123456789" + + // Upper is uppercase characters in the Latin alphabet + Upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + + // Lower is lowercase characters in the Latin alphabet + Lower = "abcdefghijklmnopqrstuvwxyz" + + // Alpha is upper or lowercase alphabetic characters + Alpha = Upper + Lower + + // Default is uppercase, lowercase, or numeric characters + Default = Numeric + Alpha + + // Hex is hexadecimal lowercase characters + Hex = "0123456789abcdef" + + // Human creates strings which are easily distinguishable from + // others created with the same charset. It contains most lowercase + // alphanumeric characters without 0,o,i,1,l. + Human = "23456789abcdefghjkmnpqrstuvwxyz" +) + +// StringCharset generates a random string using the provided charset and size +func StringCharset(charSetStr string, size int) (string, error) { + charSet := []rune(charSetStr) + + if len(charSet) == 0 || size == 0 { + return "", nil + } + + // This buffer facilitates pre-emptively creation of random uint32s + // to reduce syscall overhead. + ibuf := make([]byte, 4*size) + + _, err := rand.Read(ibuf) + if err != nil { + return "", err + } + + var buf strings.Builder + buf.Grow(size) + + for i := 0; i < size; i++ { + c, err := UnbiasedModulo32( + binary.BigEndian.Uint32(ibuf[i*4:(i+1)*4]), + int32(len(charSet)), + ) + if err != nil { + return "", err + } + + _, _ = buf.WriteRune(charSet[c]) + } + + return buf.String(), nil +} + +// String returns a random string using Default. +func String(size int) (string, error) { + return StringCharset(Default, size) +} + +// HexString returns a hexadecimal string of given length. +func HexString(size int) (string, error) { + return StringCharset(Hex, size) +} + +// Sha1String returns a 40-character hexadecimal string, which matches +// the length of a SHA-1 hash (160 bits). +func Sha1String() (string, error) { + return StringCharset(Hex, 40) +} diff --git a/cryptorand/strings_must.go b/cryptorand/strings_must.go new file mode 100644 index 0000000000000..ea334b888040c --- /dev/null +++ b/cryptorand/strings_must.go @@ -0,0 +1,34 @@ +package cryptorand + +// MustStringCharset generates a random string of the given length, +// using the provided charset. It will panic if an error occurs. +func MustStringCharset(charSet string, size int) string { + s, err := StringCharset(charSet, size) + must(err) + return s +} + +// MustString generates a random string of the given length, using +// the Default charset. It will panic if an error occurs. +func MustString(size int) string { + s, err := String(size) + must(err) + return s +} + +// MustHexString generates a random hexadecimal string of the given +// length. It will panic if an error occurs. +func MustHexString(size int) string { + s, err := HexString(size) + must(err) + return s +} + +// MustSha1String returns a 20-character hexadecimal string, which +// matches the length of a SHA-1 hash (160 bits). It will panic if +// an error occurs. +func MustSha1String() string { + s, err := Sha1String() + must(err) + return s +} diff --git a/cryptorand/strings_test.go b/cryptorand/strings_test.go new file mode 100644 index 0000000000000..f3b6c8fef2897 --- /dev/null +++ b/cryptorand/strings_test.go @@ -0,0 +1,245 @@ +package cryptorand_test + +import ( + "crypto/rand" + "encoding/binary" + "math/big" + "strings" + "testing" + "unicode/utf8" + + "github.com/coder/coder/cryptorand" + "github.com/stretchr/testify/require" +) + +func TestString(t *testing.T) { + t.Parallel() + + for i := 0; i < 20; i++ { + rs, err := cryptorand.String(10) + require.NoError(t, err, "unexpected error from String") + t.Logf("value: %v <- random?", rs) + + rs = cryptorand.MustString(10) + t.Logf("value: %v <- random?", rs) + } +} + +func TestStringCharset(t *testing.T) { + t.Parallel() + + tests := []struct { + Name string + Charset string + HelperFunc func(int) (string, error) + MustHelperFunc func(int) string + Length int + }{ + { + Name: "MultiByte-20", + Charset: "πŸ’“πŸ˜˜πŸ’“πŸŒ·", + Length: 20, + }, + { + Name: "MultiByte-7", + Charset: "πŸ˜‡πŸ₯°πŸ˜πŸ€©πŸ˜˜πŸ˜—β˜ΊοΈπŸ˜šπŸ˜™πŸ₯²πŸ˜‹πŸ˜›πŸ˜œπŸ€ͺπŸ˜πŸ€‘", + Length: 7, + }, + { + Name: "MixedBytes", + Charset: "πŸ‹πŸŒπŸπŸ₯­πŸŽπŸπŸπŸ‘πŸ’πŸ“πŸ«πŸ₯πŸ…πŸ«’πŸ₯₯πŸ₯‘πŸ†πŸ₯”abcdefg1234", + Length: 10, + }, + { + Name: "Empty", + Charset: cryptorand.Default, + Length: 0, + HelperFunc: cryptorand.String, + MustHelperFunc: cryptorand.MustString, + }, + { + Name: "Numeric", + Charset: cryptorand.Numeric, + Length: 1, + }, + { + Name: "Upper", + Charset: cryptorand.Upper, + Length: 3, + }, + { + Name: "Lower", + Charset: cryptorand.Lower, + Length: 10, + }, + { + Name: "Alpha", + Charset: cryptorand.Alpha, + Length: 20, + }, + { + Name: "Default", + Charset: cryptorand.Default, + Length: 10, + }, + { + Name: "Hex", + Charset: cryptorand.Hex, + Length: 15, + HelperFunc: cryptorand.HexString, + MustHelperFunc: cryptorand.MustHexString, + }, + { + Name: "Human", + Charset: cryptorand.Human, + Length: 20, + }, + } + + for _, test := range tests { + test := test + t.Run(test.Name, func(t *testing.T) { + t.Parallel() + + for i := 0; i < 5; i++ { + rs, err := cryptorand.StringCharset(test.Charset, test.Length) + require.NoError(t, err, "unexpected error from StringCharset") + require.Equal(t, test.Length, utf8.RuneCountInString(rs), "expected RuneCountInString to match requested") + if i == 0 { + t.Logf("value: %v <- random?", rs) + } + } + }) + + t.Run(test.Name+"Must", func(t *testing.T) { + t.Parallel() + + for i := 0; i < 5; i++ { + rs := cryptorand.MustStringCharset(test.Charset, test.Length) + require.Equal(t, test.Length, utf8.RuneCountInString(rs), "expected RuneCountInString to match requested") + if i == 0 { + t.Logf("value: %v <- random?", rs) + } + } + }) + + if test.HelperFunc != nil { + t.Run(test.Name+"HelperFunc", func(t *testing.T) { + t.Parallel() + + for i := 0; i < 5; i++ { + rs, err := test.HelperFunc(test.Length) + require.NoError(t, err, "unexpected error from HelperFunc") + require.Equal(t, test.Length, utf8.RuneCountInString(rs), "expected RuneCountInString to match requested") + if i == 0 { + t.Logf("value: %v <- random?", rs) + } + } + }) + } + + if test.MustHelperFunc != nil { + t.Run(test.Name+"MustHelperFunc", func(t *testing.T) { + t.Parallel() + + for i := 0; i < 5; i++ { + rs := test.MustHelperFunc(test.Length) + require.Equal(t, test.Length, utf8.RuneCountInString(rs), "expected RuneCountInString to match requested") + if i == 0 { + t.Logf("value: %v <- random?", rs) + } + } + }) + } + } +} + +func TestSha1String(t *testing.T) { + t.Parallel() + + for i := 0; i < 20; i++ { + rs, err := cryptorand.Sha1String() + require.NoError(t, err, "unexpected error from String") + require.Equal(t, 40, utf8.RuneCountInString(rs), "expected RuneCountInString to match requested") + t.Logf("value: %v <- random?", rs) + + rs = cryptorand.MustSha1String() + require.Equal(t, 40, utf8.RuneCountInString(rs), "expected RuneCountInString to match requested") + t.Logf("value: %v <- random?", rs) + } +} + +func BenchmarkString20(b *testing.B) { + b.SetBytes(20) + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _, _ = cryptorand.String(20) + } +} + +func BenchmarkStringUnsafe20(b *testing.B) { + mkstring := func(charSetStr string, size int) (string, error) { + charSet := []rune(charSetStr) + + // This buffer facilitates pre-emptively creation of random uint32s + // to reduce syscall overhead. + ibuf := make([]byte, 4*size) + + _, err := rand.Read(ibuf) + if err != nil { + return "", err + } + + var buf strings.Builder + buf.Grow(size) + + for i := 0; i < size; i++ { + n := binary.BigEndian.Uint32(ibuf[i*4 : (i+1)*4]) + _, _ = buf.WriteRune(charSet[n%uint32(len(charSet))]) + } + + return buf.String(), nil + } + + b.SetBytes(20) + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _, _ = mkstring(cryptorand.Default, 20) + } +} + +func BenchmarkStringBigint20(b *testing.B) { + mkstring := func(charSetStr string, size int) (string, error) { + charSet := []rune(charSetStr) + + var buf strings.Builder + buf.Grow(size) + + bi := big.NewInt(int64(size)) + for i := 0; i < size; i++ { + num, err := rand.Int(rand.Reader, bi) + if err != nil { + return "", err + } + + _, _ = buf.WriteRune(charSet[num.Uint64()%uint64(len(charSet))]) + } + + return buf.String(), nil + } + + b.SetBytes(20) + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _, _ = mkstring(cryptorand.Default, 20) + } +} + +func BenchmarkStringRuneCast(b *testing.B) { + s := strings.Repeat("0", 20) + b.SetBytes(int64(len(s))) + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = []rune(s) + } +} diff --git a/go.mod b/go.mod index 577e7278aa438..1bf9c4108b520 100644 --- a/go.mod +++ b/go.mod @@ -18,6 +18,7 @@ require ( github.com/hashicorp/hc-install v0.3.1 github.com/hashicorp/terraform-config-inspect v0.0.0-20211115214459-90acf1ca460f github.com/hashicorp/terraform-exec v0.15.0 + github.com/justinas/nosurf v1.1.1 github.com/lib/pq v1.10.4 github.com/ory/dockertest/v3 v3.8.1 github.com/pion/datachannel v1.5.2 @@ -26,6 +27,7 @@ require ( github.com/pion/webrtc/v3 v3.1.13 github.com/spf13/cobra v1.3.0 github.com/stretchr/testify v1.7.0 + github.com/unrolled/secure v1.0.9 go.uber.org/atomic v1.7.0 go.uber.org/goleak v1.1.12 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 @@ -65,7 +67,6 @@ require ( github.com/hashicorp/terraform-json v0.13.0 // indirect github.com/imdario/mergo v0.3.12 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect - github.com/justinas/nosurf v1.1.1 // indirect github.com/mattn/go-colorable v0.1.12 // indirect github.com/mattn/go-isatty v0.0.14 // indirect github.com/mitchellh/go-wordwrap v1.0.0 // indirect @@ -91,7 +92,6 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/sirupsen/logrus v1.8.1 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/unrolled/secure v1.0.9 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect diff --git a/go.sum b/go.sum index 009f6283c4c39..65f858c429c8f 100644 --- a/go.sum +++ b/go.sum @@ -1159,6 +1159,7 @@ github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/urfave/negroni v1.0.0 h1:kIimOitoypq34K7TG7DUaJ9kq/N4Ofuwi1sjz0KipXc= github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= From 3a961ceb3cc6702ef763d1acec984a0e4ea1dd5d Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Wed, 19 Jan 2022 17:46:40 +0000 Subject: [PATCH 2/3] Remove "Must" functions There is little precedence of functions leading with Must being idiomatic in Go code. Ignoring errors in favor of a panic is dangerous in highly-reliable code. --- cryptorand/numbers_must.go | 78 -------------------------------------- cryptorand/numbers_test.go | 55 +-------------------------- cryptorand/strings_must.go | 34 ----------------- cryptorand/strings_test.go | 60 ++++++----------------------- 4 files changed, 13 insertions(+), 214 deletions(-) delete mode 100644 cryptorand/numbers_must.go delete mode 100644 cryptorand/strings_must.go diff --git a/cryptorand/numbers_must.go b/cryptorand/numbers_must.go deleted file mode 100644 index 36aff2a2a9feb..0000000000000 --- a/cryptorand/numbers_must.go +++ /dev/null @@ -1,78 +0,0 @@ -package cryptorand - -// MustInt63 returns a non-negative random 63-bit integer as a int64. -func MustInt63() int64 { - i, err := Int63() - must(err) - return i -} - -// MustUint64 returns a random 64-bit integer as a uint64. -func MustUint64() uint64 { - i, err := Uint64() - must(err) - return i -} - -// MustUint32 returns a 32-bit value as a uint32. -func MustUint32() uint32 { - i, err := Uint32() - must(err) - return i -} - -// MustInt31 returns a non-negative random 31-bit integer as a int32. -func MustInt31() int32 { - i, err := Int31() - must(err) - return i -} - -// MustInt returns a non-negative random integer as a int. -func MustInt() int { - i, err := Int() - must(err) - return i -} - -// MustInt63n returns a non-negative random integer in [0,n) as a int64. -func MustInt63n(n int64) int64 { - i, err := Int63n(n) - must(err) - return i -} - -// MustInt31n returns a non-negative integer in [0,n) as a int32. -func MustInt31n(n int32) int32 { - i, err := Int31n(n) - must(err) - return i -} - -// MustIntn returns a non-negative integer in [0,n) as a int. -func MustIntn(n int) int { - i, err := Intn(n) - must(err) - return i -} - -// MustFloat64 returns a random number in [0.0,1.0) as a float64. -func MustFloat64() float64 { - f, err := Float64() - must(err) - return f -} - -// MustFloat32 returns a random number in [0.0,1.0) as a float32. -func MustFloat32() float32 { - f, err := Float32() - must(err) - return f -} - -// MustBool returns a random true/false value as a bool. -func MustBool() bool { - b, err := Bool() - must(err) - return b -} diff --git a/cryptorand/numbers_test.go b/cryptorand/numbers_test.go index e9a21037ca9c9..612ec5b7f03fc 100644 --- a/cryptorand/numbers_test.go +++ b/cryptorand/numbers_test.go @@ -17,10 +17,6 @@ func TestInt63(t *testing.T) { require.NoError(t, err, "unexpected error from Int63") t.Logf("value: %v <- random?", v) require.True(t, v >= 0, "values must be positive") - - v = cryptorand.MustInt63() - t.Logf("value: %v <- random?", v) - require.True(t, v >= 0, "values must be positive") } } @@ -31,9 +27,6 @@ func TestUint64(t *testing.T) { v, err := cryptorand.Uint64() require.NoError(t, err, "unexpected error from Uint64") t.Logf("value: %v <- random?", v) - - v = cryptorand.MustUint64() - t.Logf("value: %v <- random?", v) } } @@ -45,10 +38,6 @@ func TestInt31(t *testing.T) { require.NoError(t, err, "unexpected error from Int31") t.Logf("value: %v <- random?", v) require.True(t, v >= 0, "values must be positive") - - v = cryptorand.MustInt31() - t.Logf("value: %v <- random?", v) - require.True(t, v >= 0, "values must be positive") } } @@ -75,9 +64,6 @@ func TestUint32(t *testing.T) { v, err := cryptorand.Uint32() require.NoError(t, err, "unexpected error from Uint32") t.Logf("value: %v <- random?", v) - - v = cryptorand.MustUint32() - t.Logf("value: %v <- random?", v) } } @@ -89,10 +75,6 @@ func TestInt(t *testing.T) { require.NoError(t, err, "unexpected error from Int") t.Logf("value: %v <- random?", v) require.True(t, v >= 0, "values must be positive") - - v = cryptorand.MustInt() - t.Logf("value: %v <- random?", v) - require.True(t, v >= 0, "values must be positive") } } @@ -105,11 +87,6 @@ func TestInt63n(t *testing.T) { t.Logf("value: %v <- random?", v) require.True(t, v >= 0, "values must be positive") require.True(t, v < 1<<35, "values must be less than 1<<35") - - v = cryptorand.MustInt63n(1 << 40) - t.Logf("value: %v <- random?", v) - require.True(t, v >= 0, "values must be positive") - require.True(t, v < 1<<40, "values must be less than 1<<40") } } @@ -122,11 +99,6 @@ func TestInt31n(t *testing.T) { t.Logf("value: %v <- random?", v) require.True(t, v >= 0, "values must be positive") require.True(t, v < 100, "values must be less than 100") - - v = cryptorand.MustInt31n(500) - t.Logf("value: %v <- random?", v) - require.True(t, v >= 0, "values must be positive") - require.True(t, v < 500, "values must be less than 500") } } @@ -139,16 +111,6 @@ func TestIntn(t *testing.T) { t.Logf("value: %v <- random?", v) require.True(t, v >= 0, "values must be positive") require.True(t, v < 100, "values must be less than 100") - - v = cryptorand.MustIntn(500) - t.Logf("value: %v <- random?", v) - require.True(t, v >= 0, "values must be positive") - require.True(t, v < 500, "values must be less than 500") - - v = cryptorand.MustIntn(1 << 40) - t.Logf("value: %v <- random?", v) - require.True(t, v >= 0, "values must be positive") - require.True(t, v < 1<<40, "values must be less than 1<<40") } } @@ -161,11 +123,6 @@ func TestFloat64(t *testing.T) { t.Logf("value: %v <- random?", v) require.True(t, v >= 0.0, "values must be positive") require.True(t, v < 1.0, "values must be less than 1.0") - - v = cryptorand.MustFloat64() - t.Logf("value: %v <- random?", v) - require.True(t, v >= 0.0, "values must be positive") - require.True(t, v < 1.0, "values must be less than 1.0") } } @@ -178,11 +135,6 @@ func TestFloat32(t *testing.T) { t.Logf("value: %v <- random?", v) require.True(t, v >= 0.0, "values must be positive") require.True(t, v < 1.0, "values must be less than 1.0") - - v = cryptorand.MustFloat32() - t.Logf("value: %v <- random?", v) - require.True(t, v >= 0.0, "values must be positive") - require.True(t, v < 1.0, "values must be less than 1.0") } } @@ -192,17 +144,12 @@ func TestBool(t *testing.T) { const iterations = 10000 trueCount := 0 - for i := 0; i < iterations; i += 2 { + for i := 0; i < iterations; i += 1 { v, err := cryptorand.Bool() require.NoError(t, err, "unexpected error from Bool") if v { trueCount++ } - - v = cryptorand.MustBool() - if v { - trueCount++ - } } percentage := (float64(trueCount) / iterations) * 100 diff --git a/cryptorand/strings_must.go b/cryptorand/strings_must.go deleted file mode 100644 index ea334b888040c..0000000000000 --- a/cryptorand/strings_must.go +++ /dev/null @@ -1,34 +0,0 @@ -package cryptorand - -// MustStringCharset generates a random string of the given length, -// using the provided charset. It will panic if an error occurs. -func MustStringCharset(charSet string, size int) string { - s, err := StringCharset(charSet, size) - must(err) - return s -} - -// MustString generates a random string of the given length, using -// the Default charset. It will panic if an error occurs. -func MustString(size int) string { - s, err := String(size) - must(err) - return s -} - -// MustHexString generates a random hexadecimal string of the given -// length. It will panic if an error occurs. -func MustHexString(size int) string { - s, err := HexString(size) - must(err) - return s -} - -// MustSha1String returns a 20-character hexadecimal string, which -// matches the length of a SHA-1 hash (160 bits). It will panic if -// an error occurs. -func MustSha1String() string { - s, err := Sha1String() - must(err) - return s -} diff --git a/cryptorand/strings_test.go b/cryptorand/strings_test.go index f3b6c8fef2897..df81a85320119 100644 --- a/cryptorand/strings_test.go +++ b/cryptorand/strings_test.go @@ -19,9 +19,6 @@ func TestString(t *testing.T) { rs, err := cryptorand.String(10) require.NoError(t, err, "unexpected error from String") t.Logf("value: %v <- random?", rs) - - rs = cryptorand.MustString(10) - t.Logf("value: %v <- random?", rs) } } @@ -29,11 +26,10 @@ func TestStringCharset(t *testing.T) { t.Parallel() tests := []struct { - Name string - Charset string - HelperFunc func(int) (string, error) - MustHelperFunc func(int) string - Length int + Name string + Charset string + HelperFunc func(int) (string, error) + Length int }{ { Name: "MultiByte-20", @@ -51,11 +47,10 @@ func TestStringCharset(t *testing.T) { Length: 10, }, { - Name: "Empty", - Charset: cryptorand.Default, - Length: 0, - HelperFunc: cryptorand.String, - MustHelperFunc: cryptorand.MustString, + Name: "Empty", + Charset: cryptorand.Default, + Length: 0, + HelperFunc: cryptorand.String, }, { Name: "Numeric", @@ -83,11 +78,10 @@ func TestStringCharset(t *testing.T) { Length: 10, }, { - Name: "Hex", - Charset: cryptorand.Hex, - Length: 15, - HelperFunc: cryptorand.HexString, - MustHelperFunc: cryptorand.MustHexString, + Name: "Hex", + Charset: cryptorand.Hex, + Length: 15, + HelperFunc: cryptorand.HexString, }, { Name: "Human", @@ -111,18 +105,6 @@ func TestStringCharset(t *testing.T) { } }) - t.Run(test.Name+"Must", func(t *testing.T) { - t.Parallel() - - for i := 0; i < 5; i++ { - rs := cryptorand.MustStringCharset(test.Charset, test.Length) - require.Equal(t, test.Length, utf8.RuneCountInString(rs), "expected RuneCountInString to match requested") - if i == 0 { - t.Logf("value: %v <- random?", rs) - } - } - }) - if test.HelperFunc != nil { t.Run(test.Name+"HelperFunc", func(t *testing.T) { t.Parallel() @@ -137,20 +119,6 @@ func TestStringCharset(t *testing.T) { } }) } - - if test.MustHelperFunc != nil { - t.Run(test.Name+"MustHelperFunc", func(t *testing.T) { - t.Parallel() - - for i := 0; i < 5; i++ { - rs := test.MustHelperFunc(test.Length) - require.Equal(t, test.Length, utf8.RuneCountInString(rs), "expected RuneCountInString to match requested") - if i == 0 { - t.Logf("value: %v <- random?", rs) - } - } - }) - } } } @@ -162,10 +130,6 @@ func TestSha1String(t *testing.T) { require.NoError(t, err, "unexpected error from String") require.Equal(t, 40, utf8.RuneCountInString(rs), "expected RuneCountInString to match requested") t.Logf("value: %v <- random?", rs) - - rs = cryptorand.MustSha1String() - require.Equal(t, 40, utf8.RuneCountInString(rs), "expected RuneCountInString to match requested") - t.Logf("value: %v <- random?", rs) } } From 1c4b6e33b3399d112b9ea09337e49c4dbd05f959 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Wed, 19 Jan 2022 17:48:48 +0000 Subject: [PATCH 3/3] Remove unused must.go --- cryptorand/must.go | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 cryptorand/must.go diff --git a/cryptorand/must.go b/cryptorand/must.go deleted file mode 100644 index 184c012625995..0000000000000 --- a/cryptorand/must.go +++ /dev/null @@ -1,11 +0,0 @@ -package cryptorand - -import "golang.org/x/xerrors" - -// must is a utility function that panics with the given error if -// err is non-nil. -func must(err error) { - if err != nil { - panic(xerrors.Errorf("crand: %w", err)) - } -}