From cdb9463ec8bed94ffcbcc49826014ed62d4f4a77 Mon Sep 17 00:00:00 2001 From: Winni Neessen Date: Mon, 7 Oct 2024 15:02:49 +0200 Subject: [PATCH 1/3] Delete redundant random number generation methods Removed `randNum` functions and associated tests from `random_119.go`, `random_121.go`, and `random_122.go`. These functions are no longer necessary. Additionally, replaced `TestRandomNum` and `TestRandomNumZero` with benchmarking for `randomStringSecure`. --- random_119.go | 32 -------------------------------- random_121.go | 30 ------------------------------ random_122.go | 30 ------------------------------ random_test.go | 36 +++++++----------------------------- 4 files changed, 7 insertions(+), 121 deletions(-) delete mode 100644 random_119.go delete mode 100644 random_121.go delete mode 100644 random_122.go diff --git a/random_119.go b/random_119.go deleted file mode 100644 index 98d761c..0000000 --- a/random_119.go +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-FileCopyrightText: 2022-2023 The go-mail Authors -// -// SPDX-License-Identifier: MIT - -//go:build !go1.20 -// +build !go1.20 - -package mail - -import ( - "math/rand" - "time" -) - -// randNum returns a random number with a maximum value of maxval. -// -// This function generates a random integer between 0 and maxval (exclusive). It seeds the -// random number generator with the current time in nanoseconds to ensure different results -// each time the function is called. -// -// Parameters: -// - maxval: The upper bound for the random number generation (exclusive). -// -// Returns: -// - A random integer between 0 and maxval. If maxval is less than or equal to 0, it returns 0. -func randNum(maxval int) int { - if maxval <= 0 { - return 0 - } - rand.Seed(time.Now().UnixNano()) - return rand.Intn(maxval) -} diff --git a/random_121.go b/random_121.go deleted file mode 100644 index ea1e399..0000000 --- a/random_121.go +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-FileCopyrightText: 2022-2023 The go-mail Authors -// -// SPDX-License-Identifier: MIT - -//go:build go1.20 && !go1.22 -// +build go1.20,!go1.22 - -package mail - -import ( - "math/rand" -) - -// randNum returns a random number with a maximum value of maxval. -// -// This function generates a random integer between 0 and maxval (exclusive). If maxval is less -// than or equal to 0, it returns 0. The random number generator uses the default seed provided -// by the rand package. -// -// Parameters: -// - maxval: The upper bound for the random number generation (exclusive). -// -// Returns: -// - A random integer between 0 and maxval. If maxval is less than or equal to 0, it returns 0. -func randNum(maxval int) int { - if maxval <= 0 { - return 0 - } - return rand.Intn(maxval) -} diff --git a/random_122.go b/random_122.go deleted file mode 100644 index 27ded3d..0000000 --- a/random_122.go +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-FileCopyrightText: 2022-2023 The go-mail Authors -// -// SPDX-License-Identifier: MIT - -//go:build go1.22 -// +build go1.22 - -package mail - -import ( - "math/rand/v2" -) - -// randNum returns a random number with a maximum value of maxval. -// -// This function generates a random integer between 0 and maxval (exclusive). It utilizes -// the math/rand/v2 interface for Go 1.22+ and will default to math/rand for older Go versions. -// If maxval is less than or equal to 0, it returns 0. -// -// Parameters: -// - maxval: The upper bound for the random number generation (exclusive). -// -// Returns: -// - A random integer between 0 and maxval. If maxval is less than or equal to 0, it returns 0. -func randNum(maxval int) int { - if maxval <= 0 { - return 0 - } - return rand.IntN(maxval) -} diff --git a/random_test.go b/random_test.go index e69b4e7..a608c2a 100644 --- a/random_test.go +++ b/random_test.go @@ -38,34 +38,12 @@ func TestRandomStringSecure(t *testing.T) { } } -// TestRandomNum tests the randomNum method -func TestRandomNum(t *testing.T) { - tt := []struct { - testName string - max int - }{ - {"Max: 1", 1}, - {"Max: 20", 20}, - {"Max: 50", 50}, - {"Max: 100", 100}, - {"Max: 1000", 1000}, - {"Max: 10000", 10000}, - {"Max: 100000000", 100000000}, - } - - for _, tc := range tt { - t.Run(tc.testName, func(t *testing.T) { - rn := randNum(tc.max) - if rn > tc.max { - t.Errorf("random number generation failed: %d is bigger than given value %d", rn, tc.max) - } - }) - } -} - -func TestRandomNumZero(t *testing.T) { - rn := randNum(0) - if rn != 0 { - t.Errorf("random number generation failed: %d is not zero", rn) +func BenchmarkGenerator_RandomStringSecure(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _, err := randomStringSecure(22) + if err != nil { + b.Errorf("RandomStringFromCharRange() failed: %s", err) + } } } From 5c8b2fc371113a859409fd1e0ecf51e1c213473e Mon Sep 17 00:00:00 2001 From: Winni Neessen Date: Mon, 7 Oct 2024 15:03:07 +0200 Subject: [PATCH 2/3] Update character set and bit size for secure string generation Revised the character set to include a larger variety of symbols and adjusted the bit size calculations to correspond with the new set size. This ensures more efficient and secure random string generation by effectively utilizing the bitmask. --- random.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/random.go b/random.go index 2478c75..e987f00 100644 --- a/random.go +++ b/random.go @@ -11,16 +11,17 @@ import ( ) // Range of characters for the secure string generation -const cr = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890" +const cr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-" // Bitmask sizes for the string generators (based on 93 chars total) // // These constants define bitmask-related values used for efficient random string generation. -// The bitmask operates over 93 possible characters, and the constants help determine the +// The bitmask operates over 66 possible characters, and the constants help determine the // number of bits and indices used in the process. const ( - // letterIdxBits: Number of bits (7) needed to represent a letter index. - letterIdxBits = 7 + // letterIdxBits: Number of bits needed to represent a letter index. We have 64 possible characters + // which fit into 6 bits. + letterIdxBits = 6 // letterIdxMask: Bitmask to extract letter indices (all 1-bits for letterIdxBits). letterIdxMask = 1< Date: Mon, 7 Oct 2024 15:04:04 +0200 Subject: [PATCH 3/3] Simplify Message-ID generation Updated the SetMessageID method to generate a "Message-ID" using a single randomly generated string combined with the hostname, replacing the prior complex format that included process ID and multiple random numbers. This change simplifies the code of the generated IDs. --- msg.go | 18 +++++++----------- msg_test.go | 2 +- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/msg.go b/msg.go index fc0bb57..61feda1 100644 --- a/msg.go +++ b/msg.go @@ -972,12 +972,12 @@ func (m *Msg) Subject(subj string) { // SetMessageID generates and sets a unique "Message-ID" header for the Msg. // -// This method creates a "Message-ID" string using the current process ID, random numbers, and the hostname -// of the machine. The generated ID helps uniquely identify the message in email systems, facilitating tracking -// and preventing duplication. If the hostname cannot be retrieved, it defaults to "localhost.localdomain". +// This method creates a "Message-ID" string using a randomly generated string and the hostname of the machine. +// The generated ID helps uniquely identify the message in email systems, facilitating tracking and preventing +// duplication. If the hostname cannot be retrieved, it defaults to "localhost.localdomain". // // The generated Message-ID follows the format -// "". +// "". // // References: // - https://datatracker.ietf.org/doc/html/rfc5322#section-3.6.4 @@ -986,13 +986,9 @@ func (m *Msg) SetMessageID() { if err != nil { hostname = "localhost.localdomain" } - randNumPrimary := randNum(100000000) - randNumSecondary := randNum(10000) - randString, _ := randomStringSecure(17) - procID := os.Getpid() * randNumSecondary - messageID := fmt.Sprintf("%d.%d%d.%s@%s", procID, randNumPrimary, randNumSecondary, - randString, hostname) - m.SetMessageIDWithValue(messageID) + // We have 64 possible characters, which for a 22 character string, provides approx. 132 bits of entropy. + randString, _ := randomStringSecure(22) + m.SetMessageIDWithValue(fmt.Sprintf("%s@%s", randString, hostname)) } // GetMessageID retrieves the "Message-ID" header from the Msg. diff --git a/msg_test.go b/msg_test.go index f570b02..7fcbd99 100644 --- a/msg_test.go +++ b/msg_test.go @@ -786,8 +786,8 @@ func TestMsg_SetMessageIDWithValue(t *testing.T) { // TestMsg_SetMessageIDRandomness tests the randomness of Msg.SetMessageID methods func TestMsg_SetMessageIDRandomness(t *testing.T) { var mids []string + m := NewMsg() for i := 0; i < 50_000; i++ { - m := NewMsg() m.SetMessageID() mid := m.GetMessageID() mids = append(mids, mid)