From e7c8a3cb8d26da68b09f799585c03970cd243be1 Mon Sep 17 00:00:00 2001 From: Unknown Date: Sun, 6 Apr 2014 16:10:57 -0400 Subject: [PATCH] Add salt for every single user --- .gopmfile | 1 - README.md | 4 ++-- README_ZH.md | 2 +- gogs.go | 2 +- models/user.go | 31 ++++++++++++++++--------------- modules/base/tool.go | 40 ++++++++++++++++++++++++++++++++++++++++ routers/user/setting.go | 7 ++----- routers/user/user.go | 7 ++----- 8 files changed, 64 insertions(+), 30 deletions(-) diff --git a/.gopmfile b/.gopmfile index 9bdca49fc8..c9fad8a036 100644 --- a/.gopmfile +++ b/.gopmfile @@ -7,7 +7,6 @@ github.com/go-martini/martini = github.com/Unknwon/com = github.com/Unknwon/cae = github.com/Unknwon/goconfig = -github.com/dchest/scrypt = github.com/nfnt/resize = github.com/lunny/xorm = github.com/go-sql-driver/mysql = diff --git a/README.md b/README.md index ede1894ad0..fe15328b1b 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,9 @@ Gogs(Go Git Service) is a Self Hosted Git Service in the Go Programming Language ![Demo](http://gowalker.org/public/gogs_demo.gif) -##### Current version: 0.2.1 Alpha +##### Current version: 0.2.2 Alpha -#### Due to testing purpose, data of [try.gogits.org](http://try.gogits.org) has been reset in March 29, 2014 and will reset multiple times after. Please do NOT put your important data on the site. +#### Due to testing purpose, data of [try.gogits.org](http://try.gogits.org) has been reset in April 6, 2014 and will reset multiple times after. Please do NOT put your important data on the site. #### Other language version diff --git a/README_ZH.md b/README_ZH.md index 9b5e46413e..015ee0af99 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -5,7 +5,7 @@ Gogs(Go Git Service) 是一个由 Go 语言编写的自助 Git 托管服务。 ![Demo](http://gowalker.org/public/gogs_demo.gif) -##### 当前版本:0.2.1 Alpha +##### 当前版本:0.2.2 Alpha ## 开发目的 diff --git a/gogs.go b/gogs.go index 0e48ff7b11..e719748200 100644 --- a/gogs.go +++ b/gogs.go @@ -19,7 +19,7 @@ import ( // Test that go1.2 tag above is included in builds. main.go refers to this definition. const go12tag = true -const APP_VER = "0.2.1.0406 Alpha" +const APP_VER = "0.2.2.0406 Alpha" func init() { base.AppVer = APP_VER diff --git a/models/user.go b/models/user.go index 2196eae84f..a5a6de097f 100644 --- a/models/user.go +++ b/models/user.go @@ -5,6 +5,7 @@ package models import ( + "crypto/sha256" "encoding/hex" "errors" "fmt" @@ -13,8 +14,6 @@ import ( "strings" "time" - "github.com/dchest/scrypt" - "github.com/gogits/git" "github.com/gogits/gogs/modules/base" @@ -62,6 +61,7 @@ type User struct { IsActive bool IsAdmin bool Rands string `xorm:"VARCHAR(10)"` + Salt string `xorm:"VARCHAR(10)"` Created time.Time `xorm:"created"` Updated time.Time `xorm:"updated"` } @@ -89,10 +89,9 @@ func (user *User) NewGitSig() *git.Signature { } // EncodePasswd encodes password to safe format. -func (user *User) EncodePasswd() error { - newPasswd, err := scrypt.Key([]byte(user.Passwd), []byte(base.SecretKey), 16384, 8, 1, 64) +func (user *User) EncodePasswd() { + newPasswd := base.PBKDF2([]byte(user.Passwd), []byte(user.Salt), 10000, 50, sha256.New) user.Passwd = fmt.Sprintf("%x", newPasswd) - return err } // Member represents user is member of organization. @@ -148,9 +147,9 @@ func RegisterUser(user *User) (*User, error) { user.Avatar = base.EncodeMd5(user.Email) user.AvatarEmail = user.Email user.Rands = GetUserSalt() - if err = user.EncodePasswd(); err != nil { - return nil, err - } else if _, err = orm.Insert(user); err != nil { + user.Salt = GetUserSalt() + user.EncodePasswd() + if _, err = orm.Insert(user); err != nil { return nil, err } else if err = os.MkdirAll(UserPath(user.Name), os.ModePerm); err != nil { if _, err := orm.Id(user.Id).Delete(&User{}); err != nil { @@ -384,18 +383,20 @@ func GetUserByEmail(email string) (*User, error) { // LoginUserPlain validates user by raw user name and password. func LoginUserPlain(name, passwd string) (*User, error) { - user := User{LowerName: strings.ToLower(name), Passwd: passwd} - if err := user.EncodePasswd(); err != nil { - return nil, err - } - + user := User{LowerName: strings.ToLower(name)} has, err := orm.Get(&user) if err != nil { return nil, err } else if !has { - err = ErrUserNotExist + return nil, ErrUserNotExist } - return &user, err + + newUser := &User{Passwd: passwd, Salt: user.Salt} + newUser.EncodePasswd() + if user.Passwd != newUser.Passwd { + return nil, ErrUserNotExist + } + return &user, nil } // Follow is connection request for receiving user notifycation. diff --git a/modules/base/tool.go b/modules/base/tool.go index 3946c4b56b..f7d1bc2c55 100644 --- a/modules/base/tool.go +++ b/modules/base/tool.go @@ -6,12 +6,14 @@ package base import ( "bytes" + "crypto/hmac" "crypto/md5" "crypto/rand" "crypto/sha1" "encoding/hex" "encoding/json" "fmt" + "hash" "math" "strconv" "strings" @@ -40,6 +42,44 @@ func GetRandomString(n int, alphabets ...byte) string { return string(bytes) } +// http://code.google.com/p/go/source/browse/pbkdf2/pbkdf2.go?repo=crypto +func PBKDF2(password, salt []byte, iter, keyLen int, h func() hash.Hash) []byte { + prf := hmac.New(h, password) + hashLen := prf.Size() + numBlocks := (keyLen + hashLen - 1) / hashLen + + var buf [4]byte + dk := make([]byte, 0, numBlocks*hashLen) + U := make([]byte, hashLen) + for block := 1; block <= numBlocks; block++ { + // N.B.: || means concatenation, ^ means XOR + // for each block T_i = U_1 ^ U_2 ^ ... ^ U_iter + // U_1 = PRF(password, salt || uint(i)) + prf.Reset() + prf.Write(salt) + buf[0] = byte(block >> 24) + buf[1] = byte(block >> 16) + buf[2] = byte(block >> 8) + buf[3] = byte(block) + prf.Write(buf[:4]) + dk = prf.Sum(dk) + T := dk[len(dk)-hashLen:] + copy(U, T) + + // U_n = PRF(password, U_(n-1)) + for n := 2; n <= iter; n++ { + prf.Reset() + prf.Write(U) + U = U[:0] + U = prf.Sum(U) + for x := range U { + T[x] ^= U[x] + } + } + } + return dk[:keyLen] +} + // verify time limit code func VerifyTimeLimitCode(data string, minutes int, code string) bool { if len(code) <= 18 { diff --git a/routers/user/setting.go b/routers/user/setting.go index 4b6d88a362..ea779e8549 100644 --- a/routers/user/setting.go +++ b/routers/user/setting.go @@ -73,11 +73,7 @@ func SettingPassword(ctx *middleware.Context, form auth.UpdatePasswdForm) { user := ctx.User newUser := &models.User{Passwd: form.NewPasswd} - if err := newUser.EncodePasswd(); err != nil { - ctx.Handle(200, "setting.SettingPassword", err) - return - } - + newUser.EncodePasswd() if user.Passwd != newUser.Passwd { ctx.Data["HasError"] = true ctx.Data["ErrorMsg"] = "Old password is not correct" @@ -85,6 +81,7 @@ func SettingPassword(ctx *middleware.Context, form auth.UpdatePasswdForm) { ctx.Data["HasError"] = true ctx.Data["ErrorMsg"] = "New password and re-type password are not same" } else { + newUser.Salt = models.GetUserSalt() user.Passwd = newUser.Passwd if err := models.UpdateUser(user); err != nil { ctx.Handle(200, "setting.SettingPassword", err) diff --git a/routers/user/user.go b/routers/user/user.go index 872ed0d600..12f2bd8c51 100644 --- a/routers/user/user.go +++ b/routers/user/user.go @@ -477,12 +477,9 @@ func ResetPasswd(ctx *middleware.Context) { } u.Passwd = passwd - if err := u.EncodePasswd(); err != nil { - ctx.Handle(404, "user.ResetPasswd(EncodePasswd)", err) - return - } - u.Rands = models.GetUserSalt() + u.Salt = models.GetUserSalt() + u.EncodePasswd() if err := models.UpdateUser(u); err != nil { ctx.Handle(404, "user.ResetPasswd(UpdateUser)", err) return