Skip to content

Commit f3eed9e

Browse files
committed
go-aah/aah#20 - random string generation using crypto and byte mask
1 parent fe24aba commit f3eed9e

File tree

2 files changed

+140
-0
lines changed

2 files changed

+140
-0
lines changed

random_key.go

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
// Copyright (c) Jeevanandam M. (https://github.com/jeevatkm)
2+
// go-aah/essentials source code and usage is governed by a MIT style
3+
// license that can be found in the LICENSE file.
4+
5+
package ess
6+
7+
import (
8+
"crypto/rand"
9+
"encoding/hex"
10+
"io"
11+
mrand "math/rand"
12+
"sync"
13+
"time"
14+
)
15+
16+
const (
17+
letterBytes = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
18+
letterIdxBits = 6 // 6 bits to represent a letter index
19+
letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
20+
letterIdxMax = 63 / letterIdxBits // # of letter indices fitting in 63 bits
21+
)
22+
23+
var (
24+
mRandSrc mrand.Source
25+
mr *sync.Mutex
26+
)
27+
28+
//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
29+
// Random String methods
30+
//___________________________________
31+
32+
// RandomString method generates the random string for given length using
33+
// `crypto/rand`.
34+
func RandomString(length int) string {
35+
return hex.EncodeToString(GenerateRandomKey(length / 2))
36+
}
37+
38+
// RandomStringbm method generates the random string for given length using
39+
// `math/rand.Source` and byte mask.
40+
func RandomStringbm(length int) string {
41+
return string(GenerateRandomKeybm(length))
42+
}
43+
44+
//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
45+
// Random key methods
46+
//___________________________________
47+
48+
// GenerateRandomKey method generates the random bytes for given length using
49+
// `crypto/rand`.
50+
func GenerateRandomKey(length int) []byte {
51+
k := make([]byte, length)
52+
if _, err := io.ReadFull(rand.Reader, k); err != nil {
53+
// fallback to math based random key generater
54+
return GenerateRandomKeybm(length)
55+
}
56+
return k
57+
}
58+
59+
// GenerateRandomKeybm method generates the random bytes for given length using
60+
// `math/rand.Source` and byte mask.
61+
// StackOverflow Ref - http://stackoverflow.com/a/31832326
62+
func GenerateRandomKeybm(length int) []byte {
63+
b := make([]byte, length)
64+
// A randSrc() generates 63 random bits, enough for letterIdxMax characters!
65+
for i, cache, remain := length-1, randSrc(), letterIdxMax; i >= 0; {
66+
if remain == 0 {
67+
cache, remain = randSrc(), letterIdxMax
68+
}
69+
if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
70+
b[i] = letterBytes[idx]
71+
i--
72+
}
73+
cache >>= letterIdxBits
74+
remain--
75+
}
76+
77+
return b
78+
}
79+
80+
//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
81+
// Unexported methods
82+
//___________________________________
83+
84+
func randSrc() int64 {
85+
mr.Lock()
86+
defer mr.Unlock()
87+
return mRandSrc.Int63()
88+
}
89+
90+
//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
91+
// init
92+
//___________________________________
93+
94+
func init() {
95+
mRandSrc = mrand.NewSource(time.Now().UnixNano())
96+
mr = &sync.Mutex{}
97+
}

random_key_test.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright (c) Jeevanandam M. (https://github.com/jeevatkm)
2+
// go-aah/essentials source code and usage is governed by a MIT style
3+
// license that can be found in the LICENSE file.
4+
5+
package ess
6+
7+
import (
8+
"testing"
9+
10+
"aahframework.org/test.v0/assert"
11+
)
12+
13+
func TestEssRandomKey(t *testing.T) {
14+
key1 := GenerateRandomKey(32)
15+
assert.NotNil(t, key1)
16+
assert.True(t, len(key1) == 32)
17+
18+
key2 := GenerateRandomKeybm(64)
19+
assert.NotNil(t, key2)
20+
assert.True(t, len(key2) == 64)
21+
}
22+
23+
func TestEssRandomString(t *testing.T) {
24+
str1 := RandomString(32)
25+
assert.True(t, len(str1) == 32)
26+
assert.NotNil(t, str1)
27+
28+
str2 := RandomStringbm(32)
29+
assert.True(t, len(str2) == 32)
30+
assert.NotNil(t, str2)
31+
}
32+
33+
func BenchmarkGenerateRandomKey(b *testing.B) {
34+
for i := 0; i < b.N; i++ {
35+
GenerateRandomKey(16)
36+
}
37+
}
38+
39+
func BenchmarkGenerateRandomKeybm(b *testing.B) {
40+
for i := 0; i < b.N; i++ {
41+
GenerateRandomKeybm(32)
42+
}
43+
}

0 commit comments

Comments
 (0)