mirror of https://github.com/gogits/gogs.git
Unknwon
11 years ago
17 changed files with 46 additions and 1122 deletions
@ -1,201 +0,0 @@ |
|||||||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// Package captcha a middleware that provides captcha service for Macaron.
|
|
||||||
package captcha |
|
||||||
|
|
||||||
import ( |
|
||||||
"fmt" |
|
||||||
"html/template" |
|
||||||
"net/http" |
|
||||||
"path" |
|
||||||
"strings" |
|
||||||
|
|
||||||
"github.com/Unknwon/macaron" |
|
||||||
|
|
||||||
"github.com/gogits/cache" |
|
||||||
|
|
||||||
"github.com/gogits/gogs/modules/base" |
|
||||||
"github.com/gogits/gogs/modules/log" |
|
||||||
) |
|
||||||
|
|
||||||
var ( |
|
||||||
defaultChars = []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} |
|
||||||
) |
|
||||||
|
|
||||||
const ( |
|
||||||
// default captcha attributes
|
|
||||||
challengeNums = 6 |
|
||||||
expiration = 600 |
|
||||||
fieldIdName = "captcha_id" |
|
||||||
fieldCaptchaName = "captcha" |
|
||||||
cachePrefix = "captcha_" |
|
||||||
defaultURLPrefix = "/captcha/" |
|
||||||
) |
|
||||||
|
|
||||||
// Captcha struct
|
|
||||||
type Captcha struct { |
|
||||||
store cache.Cache |
|
||||||
|
|
||||||
// url prefix for captcha image
|
|
||||||
URLPrefix string |
|
||||||
|
|
||||||
// specify captcha id input field name
|
|
||||||
FieldIdName string |
|
||||||
// specify captcha result input field name
|
|
||||||
FieldCaptchaName string |
|
||||||
|
|
||||||
// captcha image width and height
|
|
||||||
StdWidth int |
|
||||||
StdHeight int |
|
||||||
|
|
||||||
// captcha chars nums
|
|
||||||
ChallengeNums int |
|
||||||
|
|
||||||
// captcha expiration seconds
|
|
||||||
Expiration int64 |
|
||||||
|
|
||||||
// cache key prefix
|
|
||||||
CachePrefix string |
|
||||||
} |
|
||||||
|
|
||||||
// generate key string
|
|
||||||
func (c *Captcha) key(id string) string { |
|
||||||
return c.CachePrefix + id |
|
||||||
} |
|
||||||
|
|
||||||
// generate rand chars with default chars
|
|
||||||
func (c *Captcha) genRandChars() []byte { |
|
||||||
return base.RandomCreateBytes(c.ChallengeNums, defaultChars...) |
|
||||||
} |
|
||||||
|
|
||||||
// beego filter handler for serve captcha image
|
|
||||||
func (c *Captcha) Handler(ctx *macaron.Context) { |
|
||||||
var chars []byte |
|
||||||
|
|
||||||
id := path.Base(ctx.Req.RequestURI) |
|
||||||
if i := strings.Index(id, "."); i != -1 { |
|
||||||
id = id[:i] |
|
||||||
} |
|
||||||
|
|
||||||
key := c.key(id) |
|
||||||
|
|
||||||
if v, ok := c.store.Get(key).([]byte); ok { |
|
||||||
chars = v |
|
||||||
} else { |
|
||||||
ctx.Status(404) |
|
||||||
ctx.Write([]byte("captcha not found")) |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
// reload captcha
|
|
||||||
if len(ctx.Query("reload")) > 0 { |
|
||||||
chars = c.genRandChars() |
|
||||||
if err := c.store.Put(key, chars, c.Expiration); err != nil { |
|
||||||
ctx.Status(500) |
|
||||||
ctx.Write([]byte("captcha reload error")) |
|
||||||
log.Error(4, "Reload Create Captcha Error: %v", err) |
|
||||||
return |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
img := NewImage(chars, c.StdWidth, c.StdHeight) |
|
||||||
if _, err := img.WriteTo(ctx.RW()); err != nil { |
|
||||||
log.Error(4, "Write Captcha Image Error: %v", err) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// tempalte func for output html
|
|
||||||
func (c *Captcha) CreateCaptchaHtml() template.HTML { |
|
||||||
value, err := c.CreateCaptcha() |
|
||||||
if err != nil { |
|
||||||
log.Error(4, "Create Captcha Error: %v", err) |
|
||||||
return "" |
|
||||||
} |
|
||||||
|
|
||||||
// create html
|
|
||||||
return template.HTML(fmt.Sprintf(`<input type="hidden" name="%s" value="%s">`+ |
|
||||||
`<a class="captcha" href="javascript:">`+ |
|
||||||
`<img onclick="this.src=('%s%s.png?reload='+(new Date()).getTime())" class="captcha-img" src="%s%s.png">`+ |
|
||||||
`</a>`, c.FieldIdName, value, c.URLPrefix, value, c.URLPrefix, value)) |
|
||||||
} |
|
||||||
|
|
||||||
// create a new captcha id
|
|
||||||
func (c *Captcha) CreateCaptcha() (string, error) { |
|
||||||
// generate captcha id
|
|
||||||
id := string(base.RandomCreateBytes(15)) |
|
||||||
|
|
||||||
// get the captcha chars
|
|
||||||
chars := c.genRandChars() |
|
||||||
|
|
||||||
// save to store
|
|
||||||
if err := c.store.Put(c.key(id), chars, c.Expiration); err != nil { |
|
||||||
return "", err |
|
||||||
} |
|
||||||
|
|
||||||
return id, nil |
|
||||||
} |
|
||||||
|
|
||||||
// verify from a request
|
|
||||||
func (c *Captcha) VerifyReq(req *http.Request) bool { |
|
||||||
req.ParseForm() |
|
||||||
return c.Verify(req.Form.Get(c.FieldIdName), req.Form.Get(c.FieldCaptchaName)) |
|
||||||
} |
|
||||||
|
|
||||||
// direct verify id and challenge string
|
|
||||||
func (c *Captcha) Verify(id string, challenge string) (success bool) { |
|
||||||
if len(challenge) == 0 || len(id) == 0 { |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
var chars []byte |
|
||||||
|
|
||||||
key := c.key(id) |
|
||||||
|
|
||||||
if v, ok := c.store.Get(key).([]byte); ok && len(v) == len(challenge) { |
|
||||||
chars = v |
|
||||||
} else { |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
defer func() { |
|
||||||
// finally remove it
|
|
||||||
c.store.Delete(key) |
|
||||||
}() |
|
||||||
|
|
||||||
// verify challenge
|
|
||||||
for i, c := range chars { |
|
||||||
if c != challenge[i]-48 { |
|
||||||
return |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return true |
|
||||||
} |
|
||||||
|
|
||||||
// create a new captcha.Captcha
|
|
||||||
func NewCaptcha(urlPrefix string, store cache.Cache) *Captcha { |
|
||||||
cpt := &Captcha{} |
|
||||||
cpt.store = store |
|
||||||
cpt.FieldIdName = fieldIdName |
|
||||||
cpt.FieldCaptchaName = fieldCaptchaName |
|
||||||
cpt.ChallengeNums = challengeNums |
|
||||||
cpt.Expiration = expiration |
|
||||||
cpt.CachePrefix = cachePrefix |
|
||||||
cpt.StdWidth = stdWidth |
|
||||||
cpt.StdHeight = stdHeight |
|
||||||
|
|
||||||
if len(urlPrefix) == 0 { |
|
||||||
urlPrefix = defaultURLPrefix |
|
||||||
} |
|
||||||
|
|
||||||
if urlPrefix[len(urlPrefix)-1] != '/' { |
|
||||||
urlPrefix += "/" |
|
||||||
} |
|
||||||
|
|
||||||
cpt.URLPrefix = urlPrefix |
|
||||||
|
|
||||||
base.TemplateFuncs["CreateCaptcha"] = cpt.CreateCaptchaHtml |
|
||||||
return cpt |
|
||||||
} |
|
@ -1,487 +0,0 @@ |
|||||||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package captcha |
|
||||||
|
|
||||||
import ( |
|
||||||
"bytes" |
|
||||||
"image" |
|
||||||
"image/color" |
|
||||||
"image/png" |
|
||||||
"io" |
|
||||||
"math" |
|
||||||
) |
|
||||||
|
|
||||||
const ( |
|
||||||
fontWidth = 11 |
|
||||||
fontHeight = 18 |
|
||||||
blackChar = 1 |
|
||||||
|
|
||||||
// Standard width and height of a captcha image.
|
|
||||||
stdWidth = 240 |
|
||||||
stdHeight = 80 |
|
||||||
// Maximum absolute skew factor of a single digit.
|
|
||||||
maxSkew = 0.7 |
|
||||||
// Number of background circles.
|
|
||||||
circleCount = 20 |
|
||||||
) |
|
||||||
|
|
||||||
var font = [][]byte{ |
|
||||||
{ // 0
|
|
||||||
0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, |
|
||||||
0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, |
|
||||||
0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, |
|
||||||
0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, |
|
||||||
1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, |
|
||||||
1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, |
|
||||||
1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, |
|
||||||
1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, |
|
||||||
1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, |
|
||||||
1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, |
|
||||||
1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, |
|
||||||
1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, |
|
||||||
1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, |
|
||||||
1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, |
|
||||||
0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, |
|
||||||
0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, |
|
||||||
0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, |
|
||||||
0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, |
|
||||||
}, |
|
||||||
{ // 1
|
|
||||||
0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, |
|
||||||
0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, |
|
||||||
0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, |
|
||||||
0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, |
|
||||||
0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, |
|
||||||
0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, |
|
||||||
0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, |
|
||||||
0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, |
|
||||||
0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, |
|
||||||
0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, |
|
||||||
0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, |
|
||||||
0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, |
|
||||||
0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, |
|
||||||
0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, |
|
||||||
0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, |
|
||||||
0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, |
|
||||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
|
||||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
|
||||||
}, |
|
||||||
{ // 2
|
|
||||||
0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, |
|
||||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, |
|
||||||
1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, |
|
||||||
0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, |
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, |
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, |
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, |
|
||||||
0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, |
|
||||||
0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, |
|
||||||
0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, |
|
||||||
0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, |
|
||||||
0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, |
|
||||||
0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, |
|
||||||
0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, |
|
||||||
0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, |
|
||||||
0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, |
|
||||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
|
||||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
|
||||||
}, |
|
||||||
{ // 3
|
|
||||||
0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, |
|
||||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, |
|
||||||
1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, |
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, |
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, |
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, |
|
||||||
0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, |
|
||||||
0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, |
|
||||||
0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, |
|
||||||
0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, |
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, |
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, |
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, |
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, |
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, |
|
||||||
1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, |
|
||||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, |
|
||||||
0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, |
|
||||||
}, |
|
||||||
{ // 4
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, |
|
||||||
0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, |
|
||||||
0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, |
|
||||||
0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, |
|
||||||
0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, |
|
||||||
0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, |
|
||||||
0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, |
|
||||||
0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, |
|
||||||
0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, |
|
||||||
0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, |
|
||||||
1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, |
|
||||||
1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, |
|
||||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
|
||||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
|
||||||
0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, |
|
||||||
0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, |
|
||||||
0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, |
|
||||||
0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, |
|
||||||
}, |
|
||||||
{ // 5
|
|
||||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, |
|
||||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, |
|
||||||
0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, |
|
||||||
0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, |
|
||||||
0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, |
|
||||||
0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, |
|
||||||
0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, |
|
||||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, |
|
||||||
0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, |
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, |
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, |
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, |
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, |
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, |
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, |
|
||||||
1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, |
|
||||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, |
|
||||||
0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, |
|
||||||
}, |
|
||||||
{ // 6
|
|
||||||
0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, |
|
||||||
0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, |
|
||||||
0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, |
|
||||||
0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, |
|
||||||
0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, |
|
||||||
0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
|
||||||
1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, |
|
||||||
1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, |
|
||||||
1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, |
|
||||||
1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, |
|
||||||
1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, |
|
||||||
1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, |
|
||||||
1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, |
|
||||||
1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, |
|
||||||
0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, |
|
||||||
0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, |
|
||||||
0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, |
|
||||||
0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, |
|
||||||
}, |
|
||||||
{ // 7
|
|
||||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
|
||||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
|
||||||
1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, |
|
||||||
1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, |
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, |
|
||||||
0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, |
|
||||||
0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, |
|
||||||
0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, |
|
||||||
0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, |
|
||||||
0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, |
|
||||||
0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, |
|
||||||
0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, |
|
||||||
0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, |
|
||||||
0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, |
|
||||||
0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, |
|
||||||
0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, |
|
||||||
0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, |
|
||||||
0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, |
|
||||||
}, |
|
||||||
{ // 8
|
|
||||||
0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, |
|
||||||
0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, |
|
||||||
0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, |
|
||||||
0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, |
|
||||||
0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, |
|
||||||
0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, |
|
||||||
0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, |
|
||||||
0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, |
|
||||||
0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, |
|
||||||
0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, |
|
||||||
0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, |
|
||||||
1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, |
|
||||||
1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, |
|
||||||
1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, |
|
||||||
1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, |
|
||||||
1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, |
|
||||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, |
|
||||||
0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, |
|
||||||
}, |
|
||||||
{ // 9
|
|
||||||
0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, |
|
||||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, |
|
||||||
0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, |
|
||||||
1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, |
|
||||||
1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, |
|
||||||
1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, |
|
||||||
1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, |
|
||||||
1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, |
|
||||||
0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, |
|
||||||
0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, |
|
||||||
0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, |
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, |
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, |
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, |
|
||||||
0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, |
|
||||||
0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, |
|
||||||
0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, |
|
||||||
0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, |
|
||||||
}, |
|
||||||
} |
|
||||||
|
|
||||||
type Image struct { |
|
||||||
*image.Paletted |
|
||||||
numWidth int |
|
||||||
numHeight int |
|
||||||
dotSize int |
|
||||||
} |
|
||||||
|
|
||||||
var prng = &siprng{} |
|
||||||
|
|
||||||
// randIntn returns a pseudorandom non-negative int in range [0, n).
|
|
||||||
func randIntn(n int) int { |
|
||||||
return prng.Intn(n) |
|
||||||
} |
|
||||||
|
|
||||||
// randInt returns a pseudorandom int in range [from, to].
|
|
||||||
func randInt(from, to int) int { |
|
||||||
return prng.Intn(to+1-from) + from |
|
||||||
} |
|
||||||
|
|
||||||
// randFloat returns a pseudorandom float64 in range [from, to].
|
|
||||||
func randFloat(from, to float64) float64 { |
|
||||||
return (to-from)*prng.Float64() + from |
|
||||||
} |
|
||||||
|
|
||||||
func randomPalette() color.Palette { |
|
||||||
p := make([]color.Color, circleCount+1) |
|
||||||
// Transparent color.
|
|
||||||
p[0] = color.RGBA{0xFF, 0xFF, 0xFF, 0x00} |
|
||||||
// Primary color.
|
|
||||||
prim := color.RGBA{ |
|
||||||
uint8(randIntn(129)), |
|
||||||
uint8(randIntn(129)), |
|
||||||
uint8(randIntn(129)), |
|
||||||
0xFF, |
|
||||||
} |
|
||||||
p[1] = prim |
|
||||||
// Circle colors.
|
|
||||||
for i := 2; i <= circleCount; i++ { |
|
||||||
p[i] = randomBrightness(prim, 255) |
|
||||||
} |
|
||||||
return p |
|
||||||
} |
|
||||||
|
|
||||||
// NewImage returns a new captcha image of the given width and height with the
|
|
||||||
// given digits, where each digit must be in range 0-9.
|
|
||||||
func NewImage(digits []byte, width, height int) *Image { |
|
||||||
m := new(Image) |
|
||||||
m.Paletted = image.NewPaletted(image.Rect(0, 0, width, height), randomPalette()) |
|
||||||
m.calculateSizes(width, height, len(digits)) |
|
||||||
// Randomly position captcha inside the image.
|
|
||||||
maxx := width - (m.numWidth+m.dotSize)*len(digits) - m.dotSize |
|
||||||
maxy := height - m.numHeight - m.dotSize*2 |
|
||||||
var border int |
|
||||||
if width > height { |
|
||||||
border = height / 5 |
|
||||||
} else { |
|
||||||
border = width / 5 |
|
||||||
} |
|
||||||
x := randInt(border, maxx-border) |
|
||||||
y := randInt(border, maxy-border) |
|
||||||
// Draw digits.
|
|
||||||
for _, n := range digits { |
|
||||||
m.drawDigit(font[n], x, y) |
|
||||||
x += m.numWidth + m.dotSize |
|
||||||
} |
|
||||||
// Draw strike-through line.
|
|
||||||
m.strikeThrough() |
|
||||||
// Apply wave distortion.
|
|
||||||
m.distort(randFloat(5, 10), randFloat(100, 200)) |
|
||||||
// Fill image with random circles.
|
|
||||||
m.fillWithCircles(circleCount, m.dotSize) |
|
||||||
return m |
|
||||||
} |
|
||||||
|
|
||||||
// encodedPNG encodes an image to PNG and returns
|
|
||||||
// the result as a byte slice.
|
|
||||||
func (m *Image) encodedPNG() []byte { |
|
||||||
var buf bytes.Buffer |
|
||||||
if err := png.Encode(&buf, m.Paletted); err != nil { |
|
||||||
panic(err.Error()) |
|
||||||
} |
|
||||||
return buf.Bytes() |
|
||||||
} |
|
||||||
|
|
||||||
// WriteTo writes captcha image in PNG format into the given writer.
|
|
||||||
func (m *Image) WriteTo(w io.Writer) (int64, error) { |
|
||||||
n, err := w.Write(m.encodedPNG()) |
|
||||||
return int64(n), err |
|
||||||
} |
|
||||||
|
|
||||||
func (m *Image) calculateSizes(width, height, ncount int) { |
|
||||||
// Goal: fit all digits inside the image.
|
|
||||||
var border int |
|
||||||
if width > height { |
|
||||||
border = height / 4 |
|
||||||
} else { |
|
||||||
border = width / 4 |
|
||||||
} |
|
||||||
// Convert everything to floats for calculations.
|
|
||||||
w := float64(width - border*2) |
|
||||||
h := float64(height - border*2) |
|
||||||
// fw takes into account 1-dot spacing between digits.
|
|
||||||
fw := float64(fontWidth + 1) |
|
||||||
fh := float64(fontHeight) |
|
||||||
nc := float64(ncount) |
|
||||||
// Calculate the width of a single digit taking into account only the
|
|
||||||
// width of the image.
|
|
||||||
nw := w / nc |
|
||||||
// Calculate the height of a digit from this width.
|
|
||||||
nh := nw * fh / fw |
|
||||||
// Digit too high?
|
|
||||||
if nh > h { |
|
||||||
// Fit digits based on height.
|
|
||||||
nh = h |
|
||||||
nw = fw / fh * nh |
|
||||||
} |
|
||||||
// Calculate dot size.
|
|
||||||
m.dotSize = int(nh / fh) |
|
||||||
// Save everything, making the actual width smaller by 1 dot to account
|
|
||||||
// for spacing between digits.
|
|
||||||
m.numWidth = int(nw) - m.dotSize |
|
||||||
m.numHeight = int(nh) |
|
||||||
} |
|
||||||
|
|
||||||
func (m *Image) drawHorizLine(fromX, toX, y int, colorIdx uint8) { |
|
||||||
for x := fromX; x <= toX; x++ { |
|
||||||
m.SetColorIndex(x, y, colorIdx) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func (m *Image) drawCircle(x, y, radius int, colorIdx uint8) { |
|
||||||
f := 1 - radius |
|
||||||
dfx := 1 |
|
||||||
dfy := -2 * radius |
|
||||||
xo := 0 |
|
||||||
yo := radius |
|
||||||
|
|
||||||
m.SetColorIndex(x, y+radius, colorIdx) |
|
||||||
m.SetColorIndex(x, y-radius, colorIdx) |
|
||||||
m.drawHorizLine(x-radius, x+radius, y, colorIdx) |
|
||||||
|
|
||||||
for xo < yo { |
|
||||||
if f >= 0 { |
|
||||||
yo-- |
|
||||||
dfy += 2 |
|
||||||
f += dfy |
|
||||||
} |
|
||||||
xo++ |
|
||||||
dfx += 2 |
|
||||||
f += dfx |
|
||||||
m.drawHorizLine(x-xo, x+xo, y+yo, colorIdx) |
|
||||||
m.drawHorizLine(x-xo, x+xo, y-yo, colorIdx) |
|
||||||
m.drawHorizLine(x-yo, x+yo, y+xo, colorIdx) |
|
||||||
m.drawHorizLine(x-yo, x+yo, y-xo, colorIdx) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func (m *Image) fillWithCircles(n, maxradius int) { |
|
||||||
maxx := m.Bounds().Max.X |
|
||||||
maxy := m.Bounds().Max.Y |
|
||||||
for i := 0; i < n; i++ { |
|
||||||
colorIdx := uint8(randInt(1, circleCount-1)) |
|
||||||
r := randInt(1, maxradius) |
|
||||||
m.drawCircle(randInt(r, maxx-r), randInt(r, maxy-r), r, colorIdx) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func (m *Image) strikeThrough() { |
|
||||||
maxx := m.Bounds().Max.X |
|
||||||
maxy := m.Bounds().Max.Y |
|
||||||
y := randInt(maxy/3, maxy-maxy/3) |
|
||||||
amplitude := randFloat(5, 20) |
|
||||||
period := randFloat(80, 180) |
|
||||||
dx := 2.0 * math.Pi / period |
|
||||||
for x := 0; x < maxx; x++ { |
|
||||||
xo := amplitude * math.Cos(float64(y)*dx) |
|
||||||
yo := amplitude * math.Sin(float64(x)*dx) |
|
||||||
for yn := 0; yn < m.dotSize; yn++ { |
|
||||||
r := randInt(0, m.dotSize) |
|
||||||
m.drawCircle(x+int(xo), y+int(yo)+(yn*m.dotSize), r/2, 1) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func (m *Image) drawDigit(digit []byte, x, y int) { |
|
||||||
skf := randFloat(-maxSkew, maxSkew) |
|
||||||
xs := float64(x) |
|
||||||
r := m.dotSize / 2 |
|
||||||
y += randInt(-r, r) |
|
||||||
for yo := 0; yo < fontHeight; yo++ { |
|
||||||
for xo := 0; xo < fontWidth; xo++ { |
|
||||||
if digit[yo*fontWidth+xo] != blackChar { |
|
||||||
continue |
|
||||||
} |
|
||||||
m.drawCircle(x+xo*m.dotSize, y+yo*m.dotSize, r, 1) |
|
||||||
} |
|
||||||
xs += skf |
|
||||||
x = int(xs) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func (m *Image) distort(amplude float64, period float64) { |
|
||||||
w := m.Bounds().Max.X |
|
||||||
h := m.Bounds().Max.Y |
|
||||||
|
|
||||||
oldm := m.Paletted |
|
||||||
newm := image.NewPaletted(image.Rect(0, 0, w, h), oldm.Palette) |
|
||||||
|
|
||||||
dx := 2.0 * math.Pi / period |
|
||||||
for x := 0; x < w; x++ { |
|
||||||
for y := 0; y < h; y++ { |
|
||||||
xo := amplude * math.Sin(float64(y)*dx) |
|
||||||
yo := amplude * math.Cos(float64(x)*dx) |
|
||||||
newm.SetColorIndex(x, y, oldm.ColorIndexAt(x+int(xo), y+int(yo))) |
|
||||||
} |
|
||||||
} |
|
||||||
m.Paletted = newm |
|
||||||
} |
|
||||||
|
|
||||||
func randomBrightness(c color.RGBA, max uint8) color.RGBA { |
|
||||||
minc := min3(c.R, c.G, c.B) |
|
||||||
maxc := max3(c.R, c.G, c.B) |
|
||||||
if maxc > max { |
|
||||||
return c |
|
||||||
} |
|
||||||
n := randIntn(int(max-maxc)) - int(minc) |
|
||||||
return color.RGBA{ |
|
||||||
uint8(int(c.R) + n), |
|
||||||
uint8(int(c.G) + n), |
|
||||||
uint8(int(c.B) + n), |
|
||||||
uint8(c.A), |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func min3(x, y, z uint8) (m uint8) { |
|
||||||
m = x |
|
||||||
if y < m { |
|
||||||
m = y |
|
||||||
} |
|
||||||
if z < m { |
|
||||||
m = z |
|
||||||
} |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
func max3(x, y, z uint8) (m uint8) { |
|
||||||
m = x |
|
||||||
if y > m { |
|
||||||
m = y |
|
||||||
} |
|
||||||
if z > m { |
|
||||||
m = z |
|
||||||
} |
|
||||||
return |
|
||||||
} |
|
@ -1,42 +0,0 @@ |
|||||||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package captcha |
|
||||||
|
|
||||||
import ( |
|
||||||
"testing" |
|
||||||
|
|
||||||
"github.com/gogits/gogs/modules/base" |
|
||||||
) |
|
||||||
|
|
||||||
type byteCounter struct { |
|
||||||
n int64 |
|
||||||
} |
|
||||||
|
|
||||||
func (bc *byteCounter) Write(b []byte) (int, error) { |
|
||||||
bc.n += int64(len(b)) |
|
||||||
return len(b), nil |
|
||||||
} |
|
||||||
|
|
||||||
func BenchmarkNewImage(b *testing.B) { |
|
||||||
b.StopTimer() |
|
||||||
d := base.RandomCreateBytes(challengeNums, defaultChars...) |
|
||||||
b.StartTimer() |
|
||||||
for i := 0; i < b.N; i++ { |
|
||||||
NewImage(d, stdWidth, stdHeight) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func BenchmarkImageWriteTo(b *testing.B) { |
|
||||||
b.StopTimer() |
|
||||||
d := base.RandomCreateBytes(challengeNums, defaultChars...) |
|
||||||
b.StartTimer() |
|
||||||
counter := &byteCounter{} |
|
||||||
for i := 0; i < b.N; i++ { |
|
||||||
img := NewImage(d, stdWidth, stdHeight) |
|
||||||
img.WriteTo(counter) |
|
||||||
b.SetBytes(counter.n) |
|
||||||
counter.n = 0 |
|
||||||
} |
|
||||||
} |
|
@ -1,267 +0,0 @@ |
|||||||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package captcha |
|
||||||
|
|
||||||
import ( |
|
||||||
"crypto/rand" |
|
||||||
"encoding/binary" |
|
||||||
"io" |
|
||||||
"sync" |
|
||||||
) |
|
||||||
|
|
||||||
// siprng is PRNG based on SipHash-2-4.
|
|
||||||
type siprng struct { |
|
||||||
mu sync.Mutex |
|
||||||
k0, k1, ctr uint64 |
|
||||||
} |
|
||||||
|
|
||||||
// siphash implements SipHash-2-4, accepting a uint64 as a message.
|
|
||||||
func siphash(k0, k1, m uint64) uint64 { |
|
||||||
// Initialization.
|
|
||||||
v0 := k0 ^ 0x736f6d6570736575 |
|
||||||
v1 := k1 ^ 0x646f72616e646f6d |
|
||||||
v2 := k0 ^ 0x6c7967656e657261 |
|
||||||
v3 := k1 ^ 0x7465646279746573 |
|
||||||
t := uint64(8) << 56 |
|
||||||
|
|
||||||
// Compression.
|
|
||||||
v3 ^= m |
|
||||||
|
|
||||||
// Round 1.
|
|
||||||
v0 += v1 |
|
||||||
v1 = v1<<13 | v1>>(64-13) |
|
||||||
v1 ^= v0 |
|
||||||
v0 = v0<<32 | v0>>(64-32) |
|
||||||
|
|
||||||
v2 += v3 |
|
||||||
v3 = v3<<16 | v3>>(64-16) |
|
||||||
v3 ^= v2 |
|
||||||
|
|
||||||
v0 += v3 |
|
||||||
v3 = v3<<21 | v3>>(64-21) |
|
||||||
v3 ^= v0 |
|
||||||
|
|
||||||
v2 += v1 |
|
||||||
v1 = v1<<17 | v1>>(64-17) |
|
||||||
v1 ^= v2 |
|
||||||
v2 = v2<<32 | v2>>(64-32) |
|
||||||
|
|
||||||
// Round 2.
|
|
||||||
v0 += v1 |
|
||||||
v1 = v1<<13 | v1>>(64-13) |
|
||||||
v1 ^= v0 |
|
||||||
v0 = v0<<32 | v0>>(64-32) |
|
||||||
|
|
||||||
v2 += v3 |
|
||||||
v3 = v3<<16 | v3>>(64-16) |
|
||||||
v3 ^= v2 |
|
||||||
|
|
||||||
v0 += v3 |
|
||||||
v3 = v3<<21 | v3>>(64-21) |
|
||||||
v3 ^= v0 |
|
||||||
|
|
||||||
v2 += v1 |
|
||||||
v1 = v1<<17 | v1>>(64-17) |
|
||||||
v1 ^= v2 |
|
||||||
v2 = v2<<32 | v2>>(64-32) |
|
||||||
|
|
||||||
v0 ^= m |
|
||||||
|
|
||||||
// Compress last block.
|
|
||||||
v3 ^= t |
|
||||||
|
|
||||||
// Round 1.
|
|
||||||
v0 += v1 |
|
||||||
v1 = v1<<13 | v1>>(64-13) |
|
||||||
v1 ^= v0 |
|
||||||
v0 = v0<<32 | v0>>(64-32) |
|
||||||
|
|
||||||
v2 += v3 |
|
||||||
v3 = v3<<16 | v3>>(64-16) |
|
||||||
v3 ^= v2 |
|
||||||
|
|
||||||
v0 += v3 |
|
||||||
v3 = v3<<21 | v3>>(64-21) |
|
||||||
v3 ^= v0 |
|
||||||
|
|
||||||
v2 += v1 |
|
||||||
v1 = v1<<17 | v1>>(64-17) |
|
||||||
v1 ^= v2 |
|
||||||
v2 = v2<<32 | v2>>(64-32) |
|
||||||
|
|
||||||
// Round 2.
|
|
||||||
v0 += v1 |
|
||||||
v1 = v1<<13 | v1>>(64-13) |
|
||||||
v1 ^= v0 |
|
||||||
v0 = v0<<32 | v0>>(64-32) |
|
||||||
|
|
||||||
v2 += v3 |
|
||||||
v3 = v3<<16 | v3>>(64-16) |
|
||||||
v3 ^= v2 |
|
||||||
|
|
||||||
v0 += v3 |
|
||||||
v3 = v3<<21 | v3>>(64-21) |
|
||||||
v3 ^= v0 |
|
||||||
|
|
||||||
v2 += v1 |
|
||||||
v1 = v1<<17 | v1>>(64-17) |
|
||||||
v1 ^= v2 |
|
||||||
v2 = v2<<32 | v2>>(64-32) |
|
||||||
|
|
||||||
v0 ^= t |
|
||||||
|
|
||||||
// Finalization.
|
|
||||||
v2 ^= 0xff |
|
||||||
|
|
||||||
// Round 1.
|
|
||||||
v0 += v1 |
|
||||||
v1 = v1<<13 | v1>>(64-13) |
|
||||||
v1 ^= v0 |
|
||||||
v0 = v0<<32 | v0>>(64-32) |
|
||||||
|
|
||||||
v2 += v3 |
|
||||||
v3 = v3<<16 | v3>>(64-16) |
|
||||||
v3 ^= v2 |
|
||||||
|
|
||||||
v0 += v3 |
|
||||||
v3 = v3<<21 | v3>>(64-21) |
|
||||||
v3 ^= v0 |
|
||||||
|
|
||||||
v2 += v1 |
|
||||||
v1 = v1<<17 | v1>>(64-17) |
|
||||||
v1 ^= v2 |
|
||||||
v2 = v2<<32 | v2>>(64-32) |
|
||||||
|
|
||||||
// Round 2.
|
|
||||||
v0 += v1 |
|
||||||
v1 = v1<<13 | v1>>(64-13) |
|
||||||
v1 ^= v0 |
|
||||||
v0 = v0<<32 | v0>>(64-32) |
|
||||||
|
|
||||||
v2 += v3 |
|
||||||
v3 = v3<<16 | v3>>(64-16) |
|
||||||
v3 ^= v2 |
|
||||||
|
|
||||||
v0 += v3 |
|
||||||
v3 = v3<<21 | v3>>(64-21) |
|
||||||
v3 ^= v0 |
|
||||||
|
|
||||||
v2 += v1 |
|
||||||
v1 = v1<<17 | v1>>(64-17) |
|
||||||
v1 ^= v2 |
|
||||||
v2 = v2<<32 | v2>>(64-32) |
|
||||||
|
|
||||||
// Round 3.
|
|
||||||
v0 += v1 |
|
||||||
v1 = v1<<13 | v1>>(64-13) |
|
||||||
v1 ^= v0 |
|
||||||
v0 = v0<<32 | v0>>(64-32) |
|
||||||
|
|
||||||
v2 += v3 |
|
||||||
v3 = v3<<16 | v3>>(64-16) |
|
||||||
v3 ^= v2 |
|
||||||
|
|
||||||
v0 += v3 |
|
||||||
v3 = v3<<21 | v3>>(64-21) |
|
||||||
v3 ^= v0 |
|
||||||
|
|
||||||
v2 += v1 |
|
||||||
v1 = v1<<17 | v1>>(64-17) |
|
||||||
v1 ^= v2 |
|
||||||
v2 = v2<<32 | v2>>(64-32) |
|
||||||
|
|
||||||
// Round 4.
|
|
||||||
v0 += v1 |
|
||||||
v1 = v1<<13 | v1>>(64-13) |
|
||||||
v1 ^= v0 |
|
||||||
v0 = v0<<32 | v0>>(64-32) |
|
||||||
|
|
||||||
v2 += v3 |
|
||||||
v3 = v3<<16 | v3>>(64-16) |
|
||||||
v3 ^= v2 |
|
||||||
|
|
||||||
v0 += v3 |
|
||||||
v3 = v3<<21 | v3>>(64-21) |
|
||||||
v3 ^= v0 |
|
||||||
|
|
||||||
v2 += v1 |
|
||||||
v1 = v1<<17 | v1>>(64-17) |
|
||||||
v1 ^= v2 |
|
||||||
v2 = v2<<32 | v2>>(64-32) |
|
||||||
|
|
||||||
return v0 ^ v1 ^ v2 ^ v3 |
|
||||||
} |
|
||||||
|
|
||||||
// rekey sets a new PRNG key, which is read from crypto/rand.
|
|
||||||
func (p *siprng) rekey() { |
|
||||||
var k [16]byte |
|
||||||
if _, err := io.ReadFull(rand.Reader, k[:]); err != nil { |
|
||||||
panic(err.Error()) |
|
||||||
} |
|
||||||
p.k0 = binary.LittleEndian.Uint64(k[0:8]) |
|
||||||
p.k1 = binary.LittleEndian.Uint64(k[8:16]) |
|
||||||
p.ctr = 1 |
|
||||||
} |
|
||||||
|
|
||||||
// Uint64 returns a new pseudorandom uint64.
|
|
||||||
// It rekeys PRNG on the first call and every 64 MB of generated data.
|
|
||||||
func (p *siprng) Uint64() uint64 { |
|
||||||
p.mu.Lock() |
|
||||||
if p.ctr == 0 || p.ctr > 8*1024*1024 { |
|
||||||
p.rekey() |
|
||||||
} |
|
||||||
v := siphash(p.k0, p.k1, p.ctr) |
|
||||||
p.ctr++ |
|
||||||
p.mu.Unlock() |
|
||||||
return v |
|
||||||
} |
|
||||||
|
|
||||||
func (p *siprng) Int63() int64 { |
|
||||||
return int64(p.Uint64() & 0x7fffffffffffffff) |
|
||||||
} |
|
||||||
|
|
||||||
func (p *siprng) Uint32() uint32 { |
|
||||||
return uint32(p.Uint64()) |
|
||||||
} |
|
||||||
|
|
||||||
func (p *siprng) Int31() int32 { |
|
||||||
return int32(p.Uint32() & 0x7fffffff) |
|
||||||
} |
|
||||||
|
|
||||||
func (p *siprng) Intn(n int) int { |
|
||||||
if n <= 0 { |
|
||||||
panic("invalid argument to Intn") |
|
||||||
} |
|
||||||
if n <= 1<<31-1 { |
|
||||||
return int(p.Int31n(int32(n))) |
|
||||||
} |
|
||||||
return int(p.Int63n(int64(n))) |
|
||||||
} |
|
||||||
|
|
||||||
func (p *siprng) Int63n(n int64) int64 { |
|
||||||
if n <= 0 { |
|
||||||
panic("invalid argument to Int63n") |
|
||||||
} |
|
||||||
max := int64((1 << 63) - 1 - (1<<63)%uint64(n)) |
|
||||||
v := p.Int63() |
|
||||||
for v > max { |
|
||||||
v = p.Int63() |
|
||||||
} |
|
||||||
return v % n |
|
||||||
} |
|
||||||
|
|
||||||
func (p *siprng) Int31n(n int32) int32 { |
|
||||||
if n <= 0 { |
|
||||||
panic("invalid argument to Int31n") |
|
||||||
} |
|
||||||
max := int32((1 << 31) - 1 - (1<<31)%uint32(n)) |
|
||||||
v := p.Int31() |
|
||||||
for v > max { |
|
||||||
v = p.Int31() |
|
||||||
} |
|
||||||
return v % n |
|
||||||
} |
|
||||||
|
|
||||||
func (p *siprng) Float64() float64 { return float64(p.Int63()) / (1 << 63) } |
|
@ -1,23 +0,0 @@ |
|||||||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package captcha |
|
||||||
|
|
||||||
import "testing" |
|
||||||
|
|
||||||
func TestSiphash(t *testing.T) { |
|
||||||
good := uint64(0xe849e8bb6ffe2567) |
|
||||||
cur := siphash(0, 0, 0) |
|
||||||
if cur != good { |
|
||||||
t.Fatalf("siphash: expected %x, got %x", good, cur) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func BenchmarkSiprng(b *testing.B) { |
|
||||||
b.SetBytes(8) |
|
||||||
p := &siprng{} |
|
||||||
for i := 0; i < b.N; i++ { |
|
||||||
p.Uint64() |
|
||||||
} |
|
||||||
} |
|
Loading…
Reference in new issue