all repos — captcha @ 3eb958d77bb2098ef113dcdba86035a81048fff5

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

random.go (view raw)

  1// Copyright 2011-2014 Dmitry Chestnykh. All rights reserved.
  2// Use of this source code is governed by a MIT-style
  3// license that can be found in the LICENSE file.
  4
  5package captcha
  6
  7import (
  8	"crypto/hmac"
  9	"crypto/rand"
 10	"crypto/sha256"
 11	"io"
 12)
 13
 14// idLen is a length of captcha id string.
 15// (20 bytes of 62-letter alphabet give ~119 bits.)
 16const idLen = 20
 17
 18// idChars are characters allowed in captcha id.
 19var idChars = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
 20
 21// rngKey is a secret key used to deterministically derive seeds for
 22// PRNGs used in image and audio. Generated once during initialization.
 23var rngKey [32]byte
 24
 25func init() {
 26	if _, err := io.ReadFull(rand.Reader, rngKey[:]); err != nil {
 27		panic("captcha: error reading random source: " + err.Error())
 28	}
 29}
 30
 31// Purposes for seed derivation. The goal is to make deterministic PRNG produce
 32// different outputs for images and audio by using different derived seeds.
 33const (
 34	imageSeedPurpose = 0x01
 35	audioSeedPurpose = 0x02
 36)
 37
 38// deriveSeed returns a 16-byte PRNG seed from rngKey, purpose, id and digits.
 39// Same purpose, id and digits will result in the same derived seed for this
 40// instance of running application.
 41//
 42//   out = HMAC(rngKey, purpose || id || 0x00 || digits)  (cut to 16 bytes)
 43//
 44func deriveSeed(purpose byte, id string, digits []byte) (out [16]byte) {
 45	var buf [sha256.Size]byte
 46	h := hmac.New(sha256.New, rngKey[:])
 47	h.Write([]byte{purpose})
 48	io.WriteString(h, id)
 49	h.Write([]byte{0})
 50	h.Write(digits)
 51	sum := h.Sum(buf[:0])
 52	copy(out[:], sum)
 53	return
 54}
 55
 56// RandomDigits returns a byte slice of the given length containing
 57// pseudorandom numbers in range 0-9. The slice can be used as a captcha
 58// solution.
 59func RandomDigits(length int) []byte {
 60	return randomBytesMod(length, 10)
 61}
 62
 63// randomBytes returns a byte slice of the given length read from CSPRNG.
 64func randomBytes(length int) (b []byte) {
 65	b = make([]byte, length)
 66	if _, err := io.ReadFull(rand.Reader, b); err != nil {
 67		panic("captcha: error reading random source: " + err.Error())
 68	}
 69	return
 70}
 71
 72// randomBytesMod returns a byte slice of the given length, where each byte is
 73// a random number modulo mod.
 74func randomBytesMod(length int, mod byte) (b []byte) {
 75	b = make([]byte, length)
 76	maxrb := byte(256 - (256 % int(mod)))
 77	i := 0
 78	for {
 79		r := randomBytes(length + (length / 4))
 80		for _, c := range r {
 81			if c > maxrb {
 82				// Skip this number to avoid modulo bias.
 83				continue
 84			}
 85			b[i] = c % mod
 86			i++
 87			if i == length {
 88				return
 89			}
 90		}
 91	}
 92}
 93
 94// randomId returns a new random id string.
 95func randomId() string {
 96	b := randomBytesMod(idLen, byte(len(idChars)))
 97	for i, c := range b {
 98		b[i] = idChars[c]
 99	}
100	return string(b)
101}