all repos — captcha @ c087adeb6aa31902853593b5fda6561e4da9c470

Go package captcha implements generation and verification of image and audio CAPTCHAs.

Remove dependency on uniuri. Add random.go.

Captcha ids are now 20 characters long.
Dmitry Chestnykh dmitry@codingrobots.com
Thu, 28 Apr 2011 23:12:29 +0200
commit

c087adeb6aa31902853593b5fda6561e4da9c470

parent

62d3514ca0a88e51dddd54230c9c4426a43408d3

8 files changed, 74 insertions(+), 49 deletions(-)

jump to
M MakefileMakefile

@@ -3,6 +3,7 @@

TARG=github.com/dchest/captcha GOFILES=\ captcha.go\ + random.go\ store.go\ font.go\ image.go\
M README.mdREADME.md

@@ -109,8 +109,9 @@ ### func RandomDigits

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
M audio.goaudio.go

@@ -2,7 +2,6 @@ package captcha

import ( "bytes" - crand "crypto/rand" "encoding/binary" "math" "os"

@@ -154,11 +153,6 @@ }

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 @@ return b

} 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
M captcha.gocaptcha.go

@@ -43,8 +43,6 @@ package captcha

import ( "bytes" - "crypto/rand" - "github.com/dchest/uniuri" "io" "os" )

@@ -57,13 +55,13 @@ // by default store.

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 @@

// 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 }
M image.goimage.go

@@ -7,7 +7,6 @@ "io"

"math" "os" "rand" - "time" ) const (

@@ -25,10 +24,6 @@ *image.Paletted

numWidth int numHeight int dotSize int -} - -func init() { - rand.Seed(time.Seconds()) } func randomPalette() image.PalettedColorModel {

@@ -261,8 +256,3 @@ o = z

} return } - -// rnd returns a random number in range [from, to]. -func rnd(from, to int) int { - return rand.Intn(to+1-from) + from -}
A 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 +}
M server.goserver.go

@@ -18,13 +18,13 @@ // arguments. The server decides which captcha to serve based on the last URL

// 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
M store_test.gostore_test.go

@@ -2,7 +2,6 @@ package captcha

import ( "bytes" - "github.com/dchest/uniuri" "testing" )

@@ -40,7 +39,7 @@ // create 10 ids

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 @@ d := RandomDigits(10)

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++ {