Explorar el Código

Remove dependency on uniuri. Add random.go.

Captcha ids are now 20 characters long.
Dmitry Chestnykh hace 14 años
padre
commit
c087adeb6a
Se han modificado 8 ficheros con 74 adiciones y 49 borrados
  1. 1 0
      Makefile
  2. 3 2
      README.md
  3. 1 10
      audio.go
  4. 6 21
      captcha.go
  5. 0 10
      image.go
  6. 58 0
      random.go
  7. 3 3
      server.go
  8. 2 3
      store_test.go

+ 1 - 0
Makefile

@@ -3,6 +3,7 @@ include $(GOROOT)/src/Make.inc
 TARG=github.com/dchest/captcha
 GOFILES=\
 	captcha.go\
+	random.go\
 	store.go\
 	font.go\
 	image.go\

+ 3 - 2
README.md

@@ -109,8 +109,9 @@ argument.
 
 	func RandomDigits(length int) []byte
 	
-RandomDigits returns a byte slice of the given length containing random
-digits in range 0-9.
+RandomDigits returns a byte slice of the given length containing pseudorandom
+numbers in range 0-9. The slice can be used as a captcha solution.
+
 
 ### func Reload
 

+ 1 - 10
audio.go

@@ -2,7 +2,6 @@ package captcha
 
 import (
 	"bytes"
-	crand "crypto/rand"
 	"encoding/binary"
 	"math"
 	"os"
@@ -154,11 +153,6 @@ func changeSpeed(a []byte, speed float64) []byte {
 	return b
 }
 
-// rndf returns a random float64 number in range [from, to].
-func rndf(from, to float64) float64 {
-	return (to-from)*rand.Float64() + from
-}
-
 func randomSpeed(a []byte) []byte {
 	pitch := rndf(0.9, 1.2)
 	return changeSpeed(a, pitch)
@@ -173,10 +167,7 @@ func makeSilence(length int) []byte {
 }
 
 func makeWhiteNoise(length int, level uint8) []byte {
-	noise := make([]byte, length)
-	if _, err := io.ReadFull(crand.Reader, noise); err != nil {
-		panic("error reading from random source: " + err.String())
-	}
+	noise := randomBytes(length)
 	adj := 128 - level/2
 	for i, v := range noise {
 		v %= level

+ 6 - 21
captcha.go

@@ -43,8 +43,6 @@ package captcha
 
 import (
 	"bytes"
-	"crypto/rand"
-	"github.com/dchest/uniuri"
 	"io"
 	"os"
 )
@@ -57,13 +55,13 @@ const (
 	CollectNum = 100
 	// Expiration time of captchas used by default store.
 	Expiration = 10 * 60 // 10 minutes
-
 )
 
-var ErrNotFound = os.NewError("captcha with the given id not found")
-
-// globalStore is a shared storage for captchas, generated by New function.
-var globalStore = NewMemoryStore(CollectNum, Expiration)
+var (
+	ErrNotFound = os.NewError("captcha with the given id not found")
+	// globalStore is a shared storage for captchas, generated by New function.
+	globalStore = NewMemoryStore(CollectNum, Expiration)
+)
 
 // SetCustomStore sets custom storage for captchas, replacing the default
 // memory store. This function must be called before generating any captchas.
@@ -71,19 +69,6 @@ func SetCustomStore(s Store) {
 	globalStore = s
 }
 
-// RandomDigits returns a byte slice of the given length containing random
-// digits in range 0-9.
-func RandomDigits(length int) []byte {
-	d := make([]byte, length)
-	if _, err := io.ReadFull(rand.Reader, d); err != nil {
-		panic("error reading random source: " + err.String())
-	}
-	for i := range d {
-		d[i] %= 10
-	}
-	return d
-}
-
 // New creates a new captcha with the standard length, saves it in the internal
 // storage and returns its id.
 func New() string {
@@ -93,7 +78,7 @@ func New() string {
 // NewLen is just like New, but accepts length of a captcha solution as the
 // argument.
 func NewLen(length int) (id string) {
-	id = uniuri.New()
+	id = randomId()
 	globalStore.Set(id, RandomDigits(length))
 	return
 }

+ 0 - 10
image.go

@@ -7,7 +7,6 @@ import (
 	"math"
 	"os"
 	"rand"
-	"time"
 )
 
 const (
@@ -27,10 +26,6 @@ type Image struct {
 	dotSize   int
 }
 
-func init() {
-	rand.Seed(time.Seconds())
-}
-
 func randomPalette() image.PalettedColorModel {
 	p := make([]image.Color, circleCount+1)
 	// Transparent color.
@@ -261,8 +256,3 @@ func max3(x, y, z uint8) (o uint8) {
 	}
 	return
 }
-
-// rnd returns a random number in range [from, to].
-func rnd(from, to int) int {
-	return rand.Intn(to+1-from) + from
-}

+ 58 - 0
random.go

@@ -0,0 +1,58 @@
+package captcha
+
+import (
+	crand "crypto/rand"
+	"io"
+	"rand"
+	"time"
+)
+
+// idLen is a length of captcha id string.
+const idLen = 20
+
+// idChars are characters allowed in captcha id.
+var idChars = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
+
+func init() {
+	rand.Seed(time.Seconds())
+}
+
+// RandomDigits returns a byte slice of the given length containing
+// pseudorandom numbers in range 0-9. The slice can be used as a captcha
+// solution.
+func RandomDigits(length int) (b []byte) {
+	b = randomBytes(length)
+	for i := range b {
+		b[i] %= 10
+	}
+	return
+}
+
+// randomBytes returns a byte slice of the given length read from CSPRNG.
+func randomBytes(length int) (b []byte) {
+	b = make([]byte, length)
+	if _, err := io.ReadFull(crand.Reader, b); err != nil {
+		panic("error reading random source: " + err.String())
+	}
+	return
+}
+
+// randomId returns a new random id string.
+func randomId() string {
+	b := randomBytes(idLen)
+	alen := byte(len(idChars))
+	for i, c := range b {
+		b[i] = idChars[c%alen]
+	}
+	return string(b)
+}
+
+// rnd returns a non-crypto pseudorandom int in range [from, to].
+func rnd(from, to int) int {
+	return rand.Intn(to+1-from) + from
+}
+
+// rndf returns a non-crypto pseudorandom float64 in range [from, to].
+func rndf(from, to float64) float64 {
+	return (to-from)*rand.Float64() + from
+}

+ 3 - 3
server.go

@@ -18,13 +18,13 @@ type captchaHandler struct {
 // path component: file name part must contain a captcha id, file extension —
 // its format (PNG or WAV).
 //
-// For example, for file name "B9QTvDV1RXbVJ3Ac.png" it serves an image captcha
-// with id "B9QTvDV1RXbVJ3Ac", and for "B9QTvDV1RXbVJ3Ac.wav" it serves the
+// For example, for file name "LBm5vMjHDtdUfaWYXiQX.png" it serves an image captcha
+// with id "LBm5vMjHDtdUfaWYXiQX", and for "LBm5vMjHDtdUfaWYXiQX.wav" it serves the
 // same captcha in audio format.
 //
 // To serve a captcha as a downloadable file, the URL must be constructed in
 // such a way as if the file to serve is in the "download" subdirectory:
-// "/download/B9QTvDV1RXbVJ3Ac.wav".
+// "/download/LBm5vMjHDtdUfaWYXiQX.wav".
 //
 // To reload captcha (get a different solution for the same captcha id), append
 // "?reload=x" to URL, where x may be anything (for example, current time or a

+ 2 - 3
store_test.go

@@ -2,7 +2,6 @@ package captcha
 
 import (
 	"bytes"
-	"github.com/dchest/uniuri"
 	"testing"
 )
 
@@ -40,7 +39,7 @@ func TestCollect(t *testing.T) {
 	ids := make([]string, 10)
 	d := RandomDigits(10)
 	for i := range ids {
-		ids[i] = uniuri.New()
+		ids[i] = randomId()
 		s.Set(ids[i], d)
 	}
 	s.(*memoryStore).collect()
@@ -65,7 +64,7 @@ func BenchmarkSetCollect(b *testing.B) {
 	s := NewMemoryStore(9999, -1)
 	ids := make([]string, 1000)
 	for i := range ids {
-		ids[i] = uniuri.New()
+		ids[i] = randomId()
 	}
 	b.StartTimer()
 	for i := 0; i < b.N; i++ {