├── abstract ├── compare.go ├── errors.go └── scheme.go ├── hash ├── pbkdf2 │ ├── raw │ │ ├── pbkdf2.go │ │ ├── base64.go │ │ └── parse.go │ ├── test.py │ ├── pbkdf2.go │ └── pbkdf2_test.go ├── test.py ├── sha2crypt │ ├── raw │ │ ├── base64.go │ │ ├── parse.go │ │ ├── sha2crypt.go │ │ └── sha2crypt_test.go │ └── sha2crypt.go ├── bcrypt │ └── bcrypt.go ├── scrypt │ ├── raw │ │ └── scrypt.go │ └── scrypt.go ├── bcryptsha256 │ └── bcryptsha256.go └── argon2 │ ├── argon2.go │ └── raw │ └── argon2.go ├── example_test.go ├── .github ├── workflows │ └── go.yml └── README.md ├── COPYING ├── default.go ├── passlib.go └── passlib_test.go /abstract/compare.go: -------------------------------------------------------------------------------- 1 | package abstract 2 | 3 | import "crypto/subtle" 4 | 5 | // Compares two strings (typicaly password hashes) in a secure, constant-time 6 | // fashion. Returns true iff they are equal. 7 | func SecureCompare(a, b string) bool { 8 | ab := []byte(a) 9 | bb := []byte(b) 10 | return subtle.ConstantTimeCompare(ab, bb) == 1 11 | } 12 | -------------------------------------------------------------------------------- /hash/pbkdf2/raw/pbkdf2.go: -------------------------------------------------------------------------------- 1 | package raw 2 | 3 | import ( 4 | "golang.org/x/crypto/pbkdf2" 5 | "hash" 6 | ) 7 | 8 | const ( 9 | MinRounds = 1 10 | MaxRounds = 0x7fffffff // setting at 32-bit signed integer limit for now 11 | ) 12 | 13 | func Hash(password, salt []byte, rounds int, hf func() hash.Hash) (hash string) { 14 | return Base64Encode(pbkdf2.Key(password, salt, rounds, hf().Size(), hf)) 15 | } 16 | -------------------------------------------------------------------------------- /hash/pbkdf2/raw/base64.go: -------------------------------------------------------------------------------- 1 | package raw 2 | 3 | import ( 4 | "encoding/base64" 5 | "strings" 6 | ) 7 | 8 | var b64 = base64.RawStdEncoding 9 | 10 | func Base64Encode(src []byte) (dst string) { 11 | dst = b64.EncodeToString(src) 12 | dst = strings.Replace(dst, "+", ".", -1) 13 | return 14 | } 15 | 16 | func Base64Decode(src string) (dst []byte, err error) { 17 | src = strings.Replace(src, ".", "+", -1) 18 | dst, err = b64.DecodeString(src) 19 | return 20 | } 21 | -------------------------------------------------------------------------------- /abstract/errors.go: -------------------------------------------------------------------------------- 1 | // Package abstract contains the abstract description of the Scheme interface, 2 | // plus supporting error definitions. 3 | package abstract 4 | 5 | import "fmt" 6 | 7 | // Indicates that password verification failed because the provided password 8 | // does not match the provided hash. 9 | var ErrInvalidPassword = fmt.Errorf("invalid password") 10 | 11 | // Indicates that password verification is not possible because the hashing 12 | // scheme used by the hash provided is not supported. 13 | var ErrUnsupportedScheme = fmt.Errorf("unsupported scheme") 14 | 15 | // © 2014 Hugo Landau MIT License 16 | -------------------------------------------------------------------------------- /hash/pbkdf2/test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import passlib.hash 3 | import base64 4 | def f(p): 5 | h = passlib.hash.pbkdf2_sha256.hash(p) 6 | print(' {"%s", "%s"},' % (p,h)) 7 | 8 | f('') 9 | f('a') 10 | f('ab') 11 | f('abc') 12 | f('abcd') 13 | f('abcde') 14 | f('abcdef') 15 | f('abcdefg') 16 | f('abcdefgh') 17 | f('abcdefghi') 18 | f('abcdefghij') 19 | f('abcdefghijk') 20 | f('abcdefghijkl') 21 | f('abcdefghijklm') 22 | f('abcdefghijklmn') 23 | f('abcdefghijklmno') 24 | f('abcdefghijklmnop') 25 | f('qrstuvwxyz012345') 26 | f('67890./') 27 | f('ABCDEFGHIJKLMNOP') 28 | f('QRSTUVWXYZ012345') 29 | for i in range(70): 30 | f(('password'*10)[0:i]) 31 | -------------------------------------------------------------------------------- /hash/test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import passlib.hash 3 | import base64 4 | def f(p,s,r): 5 | h = passlib.hash.sha512_crypt.encrypt(p,salt=s,rounds=r) 6 | print(' {"%s", "%s", %s, "%s"},' % (p,s,r,h)) 7 | 8 | f('','',5000) 9 | f('','a',5000) 10 | f('','ab',5000) 11 | f('','abc',5000) 12 | f('','abcd',5000) 13 | f('','abcde',5000) 14 | f('','abcdef',5000) 15 | f('','abcdefg',5000) 16 | f('','abcdefgh',5000) 17 | f('','abcdefghi',5000) 18 | f('','abcdefghij',5000) 19 | f('','abcdefghijk',5000) 20 | f('','abcdefghijkl',5000) 21 | f('','abcdefghijklm',5000) 22 | f('','abcdefghijklmn',5000) 23 | f('','abcdefghijklmno',5000) 24 | f('','abcdefghijklmnop',5000) 25 | f('','qrstuvwxyz012345',5000) 26 | f('','67890./',5000) 27 | f('','ABCDEFGHIJKLMNOP',5000) 28 | f('','QRSTUVWXYZ012345',5000) 29 | f('','a',1000) 30 | f('','a',1001) 31 | f('','a',1002) 32 | for i in range(70): 33 | f(('password'*10)[0:i],'a',5000) 34 | -------------------------------------------------------------------------------- /hash/sha2crypt/raw/base64.go: -------------------------------------------------------------------------------- 1 | package raw 2 | 3 | const bmap = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" 4 | 5 | // Encodes a byte string using the sha2-crypt base64 variant. 6 | func EncodeBase64(b []byte) string { 7 | o := make([]byte, len(b)/3*4+4) 8 | 9 | for i, j := 0, 0; i < len(b); { 10 | b1 := b[i] 11 | b2 := byte(0) 12 | b3 := byte(0) 13 | 14 | if (i + 1) < len(b) { 15 | b2 = b[i+1] 16 | } 17 | if (i + 2) < len(b) { 18 | b3 = b[i+2] 19 | } 20 | 21 | o[j] = bmap[(b1 & 0x3F)] 22 | o[j+1] = bmap[((b1&0xC0)>>6)|((b2&0x0F)<<2)] 23 | o[j+2] = bmap[((b2&0xF0)>>4)|((b3&0x03)<<4)] 24 | o[j+3] = bmap[(b3&0xFC)>>2] 25 | i += 3 26 | j += 4 27 | } 28 | 29 | s := string(o) 30 | return s[0 : len(b)*4/3-(len(b)%4)+1] 31 | } 32 | 33 | // © 2008-2012 Assurance Technologies LLC. (Python passlib) BSD License 34 | // © 2014 Hugo Landau BSD License 35 | -------------------------------------------------------------------------------- /hash/pbkdf2/raw/parse.go: -------------------------------------------------------------------------------- 1 | package raw 2 | 3 | import ( 4 | "crypto/sha1" 5 | "crypto/sha256" 6 | "crypto/sha512" 7 | "fmt" 8 | "hash" 9 | "strconv" 10 | "strings" 11 | ) 12 | 13 | // Indicates that a password hash or stub is invalid. 14 | var ErrInvalidStub = fmt.Errorf("invalid stub") 15 | 16 | // Indicates that the number of rounds specified is not in the valid range. 17 | var ErrInvalidRounds = fmt.Errorf("invalid number of rounds") 18 | 19 | var hashMap = map[string]func() hash.Hash{ 20 | "pbkdf2": sha1.New, 21 | "pbkdf2-sha256": sha256.New, 22 | "pbkdf2-sha512": sha512.New, 23 | } 24 | 25 | func Parse(stub string) (hashFunc func() hash.Hash, rounds int, salt []byte, hash string, err error) { 26 | // does not start with $pbkdf2 27 | if !strings.HasPrefix(stub, "$pbkdf2") { 28 | err = ErrInvalidStub 29 | return 30 | } 31 | 32 | parts := strings.Split(stub, "$") 33 | if f, ok := hashMap[parts[1]]; ok { 34 | hashFunc = f 35 | } else { 36 | err = ErrInvalidStub 37 | return 38 | } 39 | 40 | roundsStr := parts[2] 41 | var n uint64 42 | n, err = strconv.ParseUint(roundsStr, 10, 31) 43 | if err != nil { 44 | err = ErrInvalidStub 45 | return 46 | } 47 | rounds = int(n) 48 | 49 | if rounds < MinRounds || rounds > MaxRounds { 50 | err = ErrInvalidRounds 51 | return 52 | } 53 | 54 | salt, err = Base64Decode(parts[3]) 55 | if err != nil { 56 | err = fmt.Errorf("could not decode base64 salt") 57 | return 58 | } 59 | hash = parts[4] 60 | 61 | return 62 | } 63 | -------------------------------------------------------------------------------- /abstract/scheme.go: -------------------------------------------------------------------------------- 1 | package abstract 2 | 3 | // The Scheme interface provides an abstract interface to an implementation 4 | // of a particular password hashing scheme. The Scheme generates password 5 | // hashes from passwords, verifies passwords using password hashes, randomly 6 | // generates new stubs and can determines whether it recognises a given 7 | // stub or hash. It may also decide to issue upgrades. 8 | type Scheme interface { 9 | // Hashes a plaintext UTF-8 password using a modular crypt stub. Returns the 10 | // hashed password in modular crypt format. 11 | // 12 | // A modular crypt stub is a prefix of a hash in modular crypt format which 13 | // expresses all necessary configuration information, such as salt and 14 | // iteration count. For example, for sha256-crypt, a valid stub would be: 15 | // 16 | // $5$rounds=6000$salt 17 | // 18 | // A full modular crypt hash may also be passed as the stub, in which case 19 | // the hash is ignored. 20 | Hash(password string) (string, error) 21 | 22 | // Verifies a plaintext UTF-8 password using a modular crypt hash. Returns 23 | // an error if the inputs are malformed or the password does not match. 24 | Verify(password, hash string) (err error) 25 | 26 | // Returns true iff this crypter supports the given stub. 27 | SupportsStub(stub string) bool 28 | 29 | // Returns true iff this stub needs an update. 30 | NeedsUpdate(stub string) bool 31 | 32 | // Make a stub with the configured defaults. The salt is generated randomly. 33 | //MakeStub() (string, error) 34 | } 35 | -------------------------------------------------------------------------------- /example_test.go: -------------------------------------------------------------------------------- 1 | package passlib 2 | 3 | // User signup example. 4 | func ExampleHash_signup() { 5 | // User signup example. 6 | // NOTE: Call UseDefaults at application initialisation time to initialise 7 | // passlib before using Hash() or Verify(). See func UseDefaults. 8 | 9 | // ... signup code ... 10 | 11 | // Get the password the user chose by whatever means. 12 | password := getSignupPassword() 13 | username := getSignupUsername() 14 | 15 | hash, err := Hash(password) 16 | if err != nil { 17 | // couldn't hash password for some reason 18 | return 19 | } 20 | 21 | // hash now contains a hash in modular crypt form. 22 | // Store hash in database, etc. 23 | storeHashInDatabase(username, hash) 24 | } 25 | 26 | // User login example. 27 | func ExampleVerify_login() { 28 | // User login example. 29 | // NOTE: Call UseDefaults at application initialisation time to initialise 30 | // passlib before using Hash() or Verify(). See func UseDefaults. 31 | 32 | // Get the password for the user we have stored in the database. 33 | hash := getUserHashFromDatabase() 34 | 35 | // Get the plaintext password the user tried to login with. 36 | password := getLoginPassword() 37 | 38 | newHash, err := Verify(password, hash) 39 | if err != nil { 40 | // Incorrect password, malformed hash, etc. 41 | return 42 | } 43 | 44 | if newHash != "" { 45 | // passlib thinks we should upgrade to a new stronger hash. 46 | // ... store the new hash in the database ... 47 | } 48 | 49 | // ... log the user in ... 50 | } 51 | 52 | // These are dummy functions for the benefit of the examples. 53 | 54 | func getSignupUsername() string { 55 | return "" 56 | } 57 | 58 | func getSignupPassword() string { 59 | return "password" 60 | } 61 | 62 | func getLoginPassword() string { 63 | return "password" 64 | } 65 | 66 | func storeHashInDatabase(username, hash string) { 67 | } 68 | 69 | func getUserHashFromDatabase() string { 70 | return "" 71 | } 72 | -------------------------------------------------------------------------------- /hash/sha2crypt/raw/parse.go: -------------------------------------------------------------------------------- 1 | package raw 2 | 3 | import "fmt" 4 | import "strings" 5 | import "strconv" 6 | 7 | // Indicates that a password hash or stub is invalid. 8 | var ErrInvalidStub = fmt.Errorf("invalid stub") 9 | 10 | // Indicates that the number of rounds specified is not in the valid range. 11 | var ErrInvalidRounds = fmt.Errorf("invalid number of rounds") 12 | 13 | // Scans a sha256-crypt or sha512-crypt modular crypt stub or modular crypt hash 14 | // to determine configuration parameters. 15 | func Parse(stub string) (isSHA512 bool, salt, hash string, rounds int, err error) { 16 | // $5$ 17 | if len(stub) < 3 || stub[0] != '$' || stub[2] != '$' { 18 | err = ErrInvalidStub 19 | return 20 | } 21 | 22 | if stub[1] == '6' { 23 | isSHA512 = true 24 | } else if stub[1] != '5' { 25 | err = ErrInvalidStub 26 | return 27 | } 28 | 29 | rest := stub[3:] 30 | parts := strings.Split(rest, "$") 31 | roundsStr := "" 32 | 33 | switch len(parts) { 34 | case 1: 35 | // $5$ 36 | // $5$salt 37 | salt = parts[0] 38 | case 2: 39 | // $5$salt$hash 40 | // $5$rounds=1000$salt 41 | if strings.HasPrefix(parts[0], "rounds=") { 42 | roundsStr = parts[0] 43 | salt = parts[1] 44 | } else { 45 | salt = parts[0] 46 | hash = parts[1] 47 | } 48 | case 3: 49 | // $5$rounds=1000$salt$hash 50 | roundsStr = parts[0] 51 | salt = parts[1] 52 | hash = parts[2] 53 | default: 54 | err = ErrInvalidStub 55 | } 56 | 57 | if roundsStr != "" { 58 | if !strings.HasPrefix(roundsStr, "rounds=") { 59 | err = ErrInvalidStub 60 | return 61 | } 62 | 63 | roundsStr = roundsStr[7:] 64 | var n uint64 65 | n, err = strconv.ParseUint(roundsStr, 10, 31) 66 | if err != nil { 67 | err = ErrInvalidStub 68 | return 69 | } 70 | 71 | rounds = int(n) 72 | 73 | if rounds < MinimumRounds || rounds > MaximumRounds { 74 | err = ErrInvalidRounds 75 | return 76 | } 77 | } else { 78 | rounds = DefaultRounds 79 | } 80 | 81 | return 82 | } 83 | -------------------------------------------------------------------------------- /hash/bcrypt/bcrypt.go: -------------------------------------------------------------------------------- 1 | // Package bcrypt implements the bcrypt password hashing mechanism. 2 | // 3 | // Please note that bcrypt truncates passwords to 72 characters in length. Consider using 4 | // a more modern hashing scheme such as scrypt or sha-crypt. If you must use bcrypt, 5 | // consider using bcrypt-sha256 instead. 6 | package bcrypt 7 | 8 | import "golang.org/x/crypto/bcrypt" 9 | import "gopkg.in/hlandau/passlib.v1/abstract" 10 | import "fmt" 11 | 12 | // An implementation of Scheme implementing bcrypt. 13 | // 14 | // Uses RecommendedCost. 15 | var Crypter abstract.Scheme 16 | 17 | // The recommended cost for bcrypt. This may change with subsequent releases. 18 | const RecommendedCost = 12 19 | 20 | // bcrypt.DefaultCost is a bit low (10), so use 12 instead. 21 | 22 | func init() { 23 | Crypter = New(RecommendedCost) 24 | } 25 | 26 | // Create a new scheme implementing bcrypt. The recommended cost is RecommendedCost. 27 | func New(cost int) abstract.Scheme { 28 | return &scheme{ 29 | Cost: cost, 30 | } 31 | } 32 | 33 | type scheme struct { 34 | Cost int 35 | } 36 | 37 | func (s *scheme) SupportsStub(stub string) bool { 38 | return len(stub) >= 3 && stub[0] == '$' && stub[1] == '2' && 39 | (stub[2] == '$' || (len(stub) >= 4 && stub[3] == '$' && 40 | (stub[2] == 'a' || stub[2] == 'b' || stub[2] == 'y'))) 41 | } 42 | 43 | func (s *scheme) Hash(password string) (string, error) { 44 | h, err := bcrypt.GenerateFromPassword([]byte(password), s.Cost) 45 | if err != nil { 46 | return "", err 47 | } 48 | 49 | return string(h), nil 50 | } 51 | 52 | func (s *scheme) Verify(password, hash string) error { 53 | err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) 54 | if err == bcrypt.ErrMismatchedHashAndPassword { 55 | err = abstract.ErrInvalidPassword 56 | } 57 | 58 | return err 59 | } 60 | 61 | func (s *scheme) NeedsUpdate(stub string) bool { 62 | cost, err := bcrypt.Cost([]byte(stub)) 63 | if err != nil { 64 | return false 65 | } 66 | 67 | return cost < s.Cost 68 | } 69 | 70 | func (s *scheme) String() string { 71 | return fmt.Sprintf("bcrypt(%d)", s.Cost) 72 | } 73 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | on: 3 | push: 4 | branches: ["master", "dev"] 5 | pull_request: 6 | branches: ["master"] 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | strategy: 11 | matrix: 12 | go-version: ["1.13", "1.16"] 13 | env: 14 | GO111MODULE: off 15 | GOMODCACHE: /sys/no/permission 16 | GOPRIVATE: '*' 17 | GOPROXY: off 18 | GOSUMDB: off 19 | GOPATH: ${{github.workspace}}/go/ 20 | PKGNAME: gopkg.in/hlandau/passlib.v1 21 | steps: 22 | - name: Modules mitigation 23 | run: | 24 | mkdir -p "$GOPATH/hooks" 25 | cat < "$GOPATH/hooks/post-checkout" 26 | #!/usr/bin/env bash 27 | set -euo pipefail 28 | echo checking for errant files... 29 | find . -regextype posix-extended -iregex '^.*/(go\.mod|go\.sum)$' -printf 'WARNING: obliterating %p\n' -exec rm '{}' ';' 30 | exit 0 31 | END 32 | chmod +x "$GOPATH/hooks/post-checkout" 33 | git config --global core.hooksPath "$GOPATH/hooks" 34 | git config --global init.defaultBranch master 35 | 36 | - uses: actions/checkout@v2 37 | with: 38 | path: ${{env.GOPATH}}/src/${{env.PKGNAME}} 39 | 40 | - name: Go 41 | uses: actions/setup-go@v2 42 | with: 43 | go-version: ${{matrix.go-version}} 44 | 45 | - name: Install non-Go dependencies 46 | run: sudo apt-get install libcap-dev 47 | 48 | - name: Install Go dependencies 49 | run: | 50 | if [[ "${{matrix.go-version}}" == "1.4" ]]; then echo disabling PIE; export CGO_LDFLAGS=-no-pie; fi 51 | go get -v -t "$PKGNAME"/... 52 | 53 | - name: Build 54 | run: go build -v "$PKGNAME"/... 55 | 56 | - name: Test 57 | run: go test -v "$PKGNAME"/... 58 | 59 | - name: Ensure no modules 60 | run: | 61 | find "$GOPATH" -regextype posix-extended -regex '^.*/(go\.mod|go\.sum)$' |& tee "$GOPATH/modlist.txt" 62 | [[ "$(cat "$GOPATH/modlist.txt" | wc -l)" == 0 ]] || { echo HAVE MODULES; cat "$GOPATH/modlist.txt"; exit 1; } 63 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | passlib is a Golang password verification library strongly inspired by and 2 | derived from Python passlib (). The BSD 3 | license is preserved and extended to all new code. 4 | 5 | License for Passlib 6 | =================== 7 | Passlib is (c) `Assurance Technologies `_, 8 | and is released under the `BSD license `_:: 9 | 10 | Passlib 11 | Copyright (c) 2008-2012 Assurance Technologies, LLC. 12 | All rights reserved. 13 | 14 | Redistribution and use in source and binary forms, with or without 15 | modification, are permitted provided that the following conditions are 16 | met: 17 | 18 | * Redistributions of source code must retain the above copyright 19 | notice, this list of conditions and the following disclaimer. 20 | 21 | * Redistributions in binary form must reproduce the above copyright 22 | notice, this list of conditions and the following disclaimer in the 23 | documentation and/or other materials provided with the distribution. 24 | 25 | * Neither the name of Assurance Technologies, nor the names of the 26 | contributors may be used to endorse or promote products derived 27 | from this software without specific prior written permission. 28 | 29 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 30 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 31 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 32 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 33 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 34 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 35 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 36 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 37 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 38 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 39 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 40 | -------------------------------------------------------------------------------- /hash/scrypt/raw/scrypt.go: -------------------------------------------------------------------------------- 1 | // Package raw provides a raw implementation of the modular-crypt-wrapped scrypt primitive. 2 | package raw 3 | 4 | import "golang.org/x/crypto/scrypt" 5 | import "encoding/base64" 6 | import "strings" 7 | import "strconv" 8 | import "fmt" 9 | 10 | // The current recommended N value for interactive logins. 11 | const RecommendedN = 16384 12 | 13 | // The current recommended r value for interactive logins. 14 | const Recommendedr = 8 15 | 16 | // The current recommended p value for interactive logins. 17 | const Recommendedp = 1 18 | 19 | // Wrapper for golang.org/x/crypto/scrypt implementing a sensible 20 | // modular crypt interface. 21 | // 22 | // password should be a UTF-8 plaintext password. 23 | // salt should be a random salt value in binary form. 24 | // 25 | // N, r and p are parameters to scrypt. 26 | // 27 | // Returns a modular crypt hash. 28 | func ScryptSHA256(password string, salt []byte, N, r, p int) string { 29 | passwordb := []byte(password) 30 | 31 | hash, err := scrypt.Key(passwordb, salt, N, r, p, 32) 32 | if err != nil { 33 | panic(err) 34 | } 35 | 36 | hstr := base64.StdEncoding.EncodeToString(hash) 37 | sstr := base64.StdEncoding.EncodeToString(salt) 38 | 39 | return fmt.Sprintf("$s2$%d$%d$%d$%s$%s", N, r, p, sstr, hstr) 40 | } 41 | 42 | // Indicates that a password hash or stub is invalid. 43 | var ErrInvalidStub = fmt.Errorf("invalid scrypt password stub") 44 | 45 | // Parses an scrypt modular hash or stub string. 46 | // 47 | // The format is as follows: 48 | // 49 | // $s2$N$r$p$salt$hash // hash 50 | // $s2$N$r$p$salt // stub 51 | // 52 | func Parse(stub string) (salt, hash []byte, N, r, p int, err error) { 53 | if len(stub) < 10 || !strings.HasPrefix(stub, "$s2$") { 54 | err = ErrInvalidStub 55 | return 56 | } 57 | 58 | // $s2$ N$r$p$salt-base64$hash-base64 59 | parts := strings.Split(stub[4:], "$") 60 | 61 | if len(parts) < 4 { 62 | err = ErrInvalidStub 63 | return 64 | } 65 | 66 | var Ni, ri, pi uint64 67 | 68 | Ni, err = strconv.ParseUint(parts[0], 10, 31) 69 | if err != nil { 70 | return 71 | } 72 | 73 | ri, err = strconv.ParseUint(parts[1], 10, 31) 74 | if err != nil { 75 | return 76 | } 77 | 78 | pi, err = strconv.ParseUint(parts[2], 10, 31) 79 | if err != nil { 80 | return 81 | } 82 | 83 | N, r, p = int(Ni), int(ri), int(pi) 84 | 85 | salt, err = base64.StdEncoding.DecodeString(parts[3]) 86 | if err != nil { 87 | return 88 | } 89 | 90 | if len(parts) >= 5 { 91 | hash, err = base64.StdEncoding.DecodeString(parts[4]) 92 | } 93 | 94 | return 95 | } 96 | -------------------------------------------------------------------------------- /hash/pbkdf2/pbkdf2.go: -------------------------------------------------------------------------------- 1 | // Package pbkdf2 implements a modular crypt format for PBKDF2-SHA1, 2 | // PBKDF2-SHA256 and PBKDF-SHA512. 3 | // 4 | // The format is the same as that used by Python's passlib and is compatible. 5 | package pbkdf2 6 | 7 | import ( 8 | "crypto/rand" 9 | "crypto/sha1" 10 | "crypto/sha256" 11 | "crypto/sha512" 12 | "fmt" 13 | "gopkg.in/hlandau/passlib.v1/abstract" 14 | "gopkg.in/hlandau/passlib.v1/hash/pbkdf2/raw" 15 | "hash" 16 | "strings" 17 | ) 18 | 19 | // An implementation of Scheme implementing a number of PBKDF2 modular crypt 20 | // formats used by Python's passlib ($pbkdf2$, $pbkdf2-sha256$, 21 | // $pbkdf2-sha512$). 22 | // 23 | // Uses RecommendedRounds. 24 | // 25 | // WARNING: SHA1 should not be used for new applications under any 26 | // circumstances. It should be used for legacy compatibility only. 27 | var SHA1Crypter abstract.Scheme 28 | var SHA256Crypter abstract.Scheme 29 | var SHA512Crypter abstract.Scheme 30 | 31 | const ( 32 | RecommendedRoundsSHA1 = 131000 33 | RecommendedRoundsSHA256 = 29000 34 | RecommendedRoundsSHA512 = 25000 35 | ) 36 | 37 | const SaltLength = 16 38 | 39 | func init() { 40 | SHA1Crypter = New("$pbkdf2$", sha1.New, RecommendedRoundsSHA1) 41 | SHA256Crypter = New("$pbkdf2-sha256$", sha256.New, RecommendedRoundsSHA256) 42 | SHA512Crypter = New("$pbkdf2-sha512$", sha512.New, RecommendedRoundsSHA512) 43 | } 44 | 45 | type scheme struct { 46 | Ident string 47 | HashFunc func() hash.Hash 48 | Rounds int 49 | } 50 | 51 | func New(ident string, hf func() hash.Hash, rounds int) abstract.Scheme { 52 | return &scheme{ 53 | Ident: ident, 54 | HashFunc: hf, 55 | Rounds: rounds, 56 | } 57 | } 58 | 59 | func (s *scheme) Hash(password string) (string, error) { 60 | salt := make([]byte, SaltLength) 61 | _, err := rand.Read(salt) 62 | if err != nil { 63 | return "", err 64 | } 65 | 66 | hash := raw.Hash([]byte(password), salt, s.Rounds, s.HashFunc) 67 | 68 | newHash := fmt.Sprintf("%s%d$%s$%s", s.Ident, s.Rounds, raw.Base64Encode(salt), hash) 69 | return newHash, nil 70 | } 71 | 72 | func (s *scheme) Verify(password, stub string) (err error) { 73 | _, rounds, salt, oldHash, err := raw.Parse(stub) 74 | if err != nil { 75 | return 76 | } 77 | 78 | newHash := raw.Hash([]byte(password), salt, rounds, s.HashFunc) 79 | 80 | if len(newHash) == 0 || !abstract.SecureCompare(oldHash, newHash) { 81 | err = abstract.ErrInvalidPassword 82 | } 83 | 84 | return 85 | } 86 | 87 | func (s *scheme) SupportsStub(stub string) bool { 88 | return strings.HasPrefix(stub, s.Ident) 89 | } 90 | 91 | func (s *scheme) NeedsUpdate(stub string) bool { 92 | _, rounds, salt, _, err := raw.Parse(stub) 93 | return err == raw.ErrInvalidRounds || rounds < s.Rounds || len(salt) < SaltLength 94 | } 95 | -------------------------------------------------------------------------------- /hash/bcryptsha256/bcryptsha256.go: -------------------------------------------------------------------------------- 1 | // Package bcryptsha256 implements bcrypt with a SHA256 prehash in a format that is compatible with Python passlib's equivalent bcrypt-sha256 scheme. 2 | // 3 | // This is preferred over bcrypt because the prehash essentially renders bcrypt's password length 4 | // limitation irrelevant; although of course it is less compatible. 5 | package bcryptsha256 6 | 7 | import "gopkg.in/hlandau/passlib.v1/abstract" 8 | import "gopkg.in/hlandau/passlib.v1/hash/bcrypt" 9 | import "encoding/base64" 10 | import "crypto/sha256" 11 | import "strings" 12 | import "fmt" 13 | 14 | type scheme struct { 15 | underlying abstract.Scheme 16 | cost int 17 | } 18 | 19 | // An implementation of Scheme implementing Python passlib's `$bcrypt-sha256$` 20 | // bcrypt variant. This is bcrypt with a SHA256 prehash, which removes bcrypt's 21 | // password length limitation. 22 | var Crypter abstract.Scheme 23 | 24 | // The recommended cost for bcrypt-sha256. This may change with subsequent releases. 25 | const RecommendedCost = bcrypt.RecommendedCost 26 | 27 | func init() { 28 | Crypter = New(bcrypt.RecommendedCost) 29 | } 30 | 31 | // Instantiates a new Scheme implementing bcrypt with the given cost. 32 | // 33 | // The recommended cost is RecommendedCost. 34 | func New(cost int) abstract.Scheme { 35 | return &scheme{ 36 | underlying: bcrypt.New(cost), 37 | cost: cost, 38 | } 39 | } 40 | 41 | func (s *scheme) Hash(password string) (string, error) { 42 | p := s.prehash(password) 43 | h, err := s.underlying.Hash(p) 44 | if err != nil { 45 | return "", err 46 | } 47 | 48 | return mangle(h), nil 49 | } 50 | 51 | func (s *scheme) Verify(password, hash string) error { 52 | p := s.prehash(password) 53 | return s.underlying.Verify(p, demangle(hash)) 54 | } 55 | 56 | func (s *scheme) prehash(password string) string { 57 | h := sha256.New() 58 | h.Write([]byte(password)) 59 | v := base64.StdEncoding.EncodeToString(h.Sum(nil)) 60 | return v 61 | } 62 | 63 | func (s *scheme) SupportsStub(stub string) bool { 64 | return strings.HasPrefix(stub, "$bcrypt-sha256$") && s.underlying.SupportsStub(demangle(stub)) 65 | } 66 | 67 | func (s *scheme) NeedsUpdate(stub string) bool { 68 | return s.underlying.NeedsUpdate(demangle(stub)) 69 | } 70 | 71 | func (s *scheme) String() string { 72 | return fmt.Sprintf("bcrypt-sha256(%d)", s.cost) 73 | } 74 | 75 | func demangle(stub string) string { 76 | if strings.HasPrefix(stub, "$bcrypt-sha256$2") { 77 | parts := strings.Split(stub[15:], "$") 78 | // 0: 2a,12 79 | // 1: salt 80 | // 2: hash 81 | parts0 := strings.Split(parts[0], ",") 82 | return "$" + parts0[0] + "$" + fmt.Sprintf("%02s", parts0[1]) + "$" + parts[1] + parts[2] 83 | } else { 84 | return stub 85 | } 86 | } 87 | 88 | func mangle(hash string) string { 89 | parts := strings.Split(hash[1:], "$") 90 | // 0: 2a 91 | // 1: rounds 92 | // 2: salt + hash 93 | salt := parts[2][0:22] 94 | h := parts[2][22:] 95 | return "$bcrypt-sha256$" + parts[0] + "," + parts[1] + "$" + salt + "$" + h 96 | } 97 | -------------------------------------------------------------------------------- /hash/scrypt/scrypt.go: -------------------------------------------------------------------------------- 1 | // Package scrypt implements the scrypt password hashing mechanism, wrapped in 2 | // the modular crypt format. 3 | package scrypt 4 | 5 | import "fmt" 6 | import "expvar" 7 | import "strings" 8 | import "crypto/rand" 9 | import "encoding/base64" 10 | import "gopkg.in/hlandau/passlib.v1/hash/scrypt/raw" 11 | import "gopkg.in/hlandau/passlib.v1/abstract" 12 | 13 | var cScryptSHA256HashCalls = expvar.NewInt("passlib.scryptsha256.hashCalls") 14 | var cScryptSHA256VerifyCalls = expvar.NewInt("passlib.scryptsha256.verifyCalls") 15 | 16 | // An implementation of Scheme performing scrypt-sha256. 17 | // 18 | // Uses the recommended values for N,r,p defined in raw. 19 | var SHA256Crypter abstract.Scheme 20 | 21 | func init() { 22 | SHA256Crypter = NewSHA256( 23 | raw.RecommendedN, 24 | raw.Recommendedr, 25 | raw.Recommendedp, 26 | ) 27 | } 28 | 29 | // Returns an implementation of Scheme implementing scrypt-sha256 30 | // with the specified parameters. 31 | func NewSHA256(N, r, p int) abstract.Scheme { 32 | return &scryptSHA256Crypter{ 33 | nN: N, 34 | r: r, 35 | p: p, 36 | } 37 | } 38 | 39 | type scryptSHA256Crypter struct { 40 | nN, r, p int 41 | } 42 | 43 | func (c *scryptSHA256Crypter) SetParams(N, r, p int) error { 44 | c.nN = N 45 | c.r = r 46 | c.p = p 47 | return nil 48 | } 49 | 50 | func (c *scryptSHA256Crypter) SupportsStub(stub string) bool { 51 | return strings.HasPrefix(stub, "$s2$") 52 | } 53 | 54 | func (c *scryptSHA256Crypter) Hash(password string) (string, error) { 55 | cScryptSHA256HashCalls.Add(1) 56 | 57 | stub, err := c.makeStub() 58 | if err != nil { 59 | return "", err 60 | } 61 | 62 | _, newHash, _, _, _, _, err := c.hash(password, stub) 63 | return newHash, err 64 | } 65 | 66 | func (c *scryptSHA256Crypter) Verify(password, hash string) (err error) { 67 | cScryptSHA256VerifyCalls.Add(1) 68 | 69 | _, newHash, _, _, _, _, err := c.hash(password, hash) 70 | if err == nil && !abstract.SecureCompare(hash, newHash) { 71 | err = abstract.ErrInvalidPassword 72 | } 73 | 74 | return 75 | } 76 | 77 | func (c *scryptSHA256Crypter) NeedsUpdate(stub string) bool { 78 | salt, _, N, r, p, err := raw.Parse(stub) 79 | if err != nil { 80 | return false // ... 81 | } 82 | 83 | return c.needsUpdate(salt, N, r, p) 84 | } 85 | 86 | func (c *scryptSHA256Crypter) needsUpdate(salt []byte, N, r, p int) bool { 87 | return len(salt) < 18 || N < c.nN || r < c.r || p < c.p 88 | } 89 | 90 | func (c *scryptSHA256Crypter) hash(password, stub string) (oldHashRaw []byte, newHash string, salt []byte, N, r, p int, err error) { 91 | salt, oldHashRaw, N, r, p, err = raw.Parse(stub) 92 | if err != nil { 93 | return 94 | } 95 | 96 | return oldHashRaw, raw.ScryptSHA256(password, salt, N, r, p), salt, N, r, p, nil 97 | } 98 | 99 | func (c *scryptSHA256Crypter) makeStub() (string, error) { 100 | buf := make([]byte, 18) 101 | _, err := rand.Read(buf) 102 | if err != nil { 103 | return "", err 104 | } 105 | 106 | salt := base64.StdEncoding.EncodeToString(buf) 107 | 108 | return fmt.Sprintf("$s2$%d$%d$%d$%s", c.nN, c.r, c.p, salt), nil 109 | } 110 | 111 | func (c *scryptSHA256Crypter) String() string { 112 | return fmt.Sprintf("scrypt-sha256(%d,%d,%d)", c.nN, c.r, c.p) 113 | } 114 | -------------------------------------------------------------------------------- /hash/argon2/argon2.go: -------------------------------------------------------------------------------- 1 | // Package argon2 implements the argon2 password hashing mechanism, wrapped in 2 | // the argon2 encoded format. 3 | package argon2 4 | 5 | import ( 6 | "crypto/rand" 7 | "encoding/base64" 8 | "fmt" 9 | "strings" 10 | 11 | "golang.org/x/crypto/argon2" 12 | "gopkg.in/hlandau/passlib.v1/abstract" 13 | "gopkg.in/hlandau/passlib.v1/hash/argon2/raw" 14 | ) 15 | 16 | // An implementation of Scheme performing argon2 hashing. 17 | // 18 | // Uses the recommended values for time, memory and threads defined in raw. 19 | var Crypter abstract.Scheme 20 | 21 | const saltLength = 16 22 | 23 | func init() { 24 | Crypter = New( 25 | raw.RecommendedTime, 26 | raw.RecommendedMemory, 27 | raw.RecommendedThreads, 28 | ) 29 | } 30 | 31 | // Returns an implementation of Scheme implementing argon2 32 | // with the specified parameters. 33 | func New(time, memory uint32, threads uint8) abstract.Scheme { 34 | return &scheme{ 35 | time: time, 36 | memory: memory, 37 | threads: threads, 38 | } 39 | } 40 | 41 | type scheme struct { 42 | time, memory uint32 43 | threads uint8 44 | } 45 | 46 | func (c *scheme) SetParams(time, memory uint32, threads uint8) error { 47 | c.time = time 48 | c.memory = memory 49 | c.threads = threads 50 | return nil 51 | } 52 | 53 | func (c *scheme) SupportsStub(stub string) bool { 54 | return strings.HasPrefix(stub, "$argon2i$") 55 | } 56 | 57 | func (c *scheme) Hash(password string) (string, error) { 58 | 59 | stub, err := c.makeStub() 60 | if err != nil { 61 | return "", err 62 | } 63 | 64 | _, newHash, _, _, _, _, _, err := c.hash(password, stub) 65 | return newHash, err 66 | } 67 | 68 | func (c *scheme) Verify(password, hash string) (err error) { 69 | 70 | _, newHash, _, _, _, _, _, err := c.hash(password, hash) 71 | if err == nil && !abstract.SecureCompare(hash, newHash) { 72 | err = abstract.ErrInvalidPassword 73 | } 74 | 75 | return 76 | } 77 | 78 | func (c *scheme) NeedsUpdate(stub string) bool { 79 | salt, _, version, time, memory, threads, err := raw.Parse(stub) 80 | if err != nil { 81 | return false // ... 82 | } 83 | 84 | return c.needsUpdate(salt, version, time, memory, threads) 85 | } 86 | 87 | func (c *scheme) needsUpdate(salt []byte, version int, time, memory uint32, threads uint8) bool { 88 | return len(salt) < saltLength || version < argon2.Version || time < c.time || memory < c.memory || threads < c.threads 89 | } 90 | 91 | func (c *scheme) hash(password, stub string) (oldHashRaw []byte, newHash string, salt []byte, version int, memory, time uint32, threads uint8, err error) { 92 | 93 | salt, oldHashRaw, version, time, memory, threads, err = raw.Parse(stub) 94 | if err != nil { 95 | return 96 | } 97 | 98 | return oldHashRaw, raw.Argon2(password, salt, time, memory, threads), salt, version, memory, time, threads, nil 99 | } 100 | 101 | func (c *scheme) makeStub() (string, error) { 102 | buf := make([]byte, saltLength) 103 | _, err := rand.Read(buf) 104 | if err != nil { 105 | return "", err 106 | } 107 | 108 | salt := base64.RawStdEncoding.EncodeToString(buf) 109 | 110 | return fmt.Sprintf("$argon2i$v=%d$m=%d,t=%d,p=%d$%s$", argon2.Version, c.memory, c.time, c.threads, salt), nil 111 | } 112 | 113 | func (c *scheme) String() string { 114 | return fmt.Sprintf("argon2(%d,%d,%d,%d)", argon2.Version, c.memory, c.time, c.threads) 115 | } 116 | -------------------------------------------------------------------------------- /.github/README.md: -------------------------------------------------------------------------------- 1 | passlib for go 2 | ============== 3 | 4 | [![godocs.io](https://godocs.io/gopkg.in/hlandau/passlib.v1?status.svg)](https://godocs.io/gopkg.in/hlandau/passlib.v1) [![Build status](https://github.com/hlandau/passlib/actions/workflows/go.yml/badge.svg)](#) [![No modules](https://www.devever.net/~hl/f/no-modules2.svg) 100% modules-free.](https://www.devever.net/~hl/gomod) 5 | 6 | [Python's passlib](https://pythonhosted.org/passlib/) is quite an amazing 7 | library. I'm not sure there's a password library in existence with more thought 8 | put into it, or with more support for obscure password formats. 9 | 10 | This is a skeleton of a port of passlib to Go. It dogmatically adopts the 11 | modular crypt format, which [passlib has excellent documentation for](https://pythonhosted.org/passlib/modular_crypt_format.html#modular-crypt-format). 12 | 13 | Currently, it supports: 14 | 15 | - Argon2i 16 | - scrypt-sha256 17 | - sha512-crypt 18 | - sha256-crypt 19 | - bcrypt 20 | - passlib's bcrypt-sha256 variant 21 | - pbkdf2-sha512 (in passlib format) 22 | - pbkdf2-sha256 (in passlib format) 23 | - pbkdf2-sha1 (in passlib format) 24 | 25 | By default, it will hash using scrypt-sha256 and verify existing hashes using 26 | any of these schemes. 27 | 28 | Example Usage 29 | ------------- 30 | There's a default context for ease of use. Most people need only concern 31 | themselves with the functions `Hash` and `Verify`: 32 | 33 | ```go 34 | // Hash a plaintext, UTF-8 password. 35 | func Hash(password string) (hash string, err error) 36 | 37 | // Verifies a plaintext, UTF-8 password using a previously derived hash. 38 | // Returns non-nil err if verification fails. 39 | // 40 | // Also returns an upgraded password hash if the hash provided is 41 | // deprecated. 42 | func Verify(password, hash string) (newHash string, err error) 43 | ``` 44 | 45 | Here's a rough skeleton of typical usage. 46 | 47 | ```go 48 | import "gopkg.in/hlandau/passlib.v1" 49 | 50 | func RegisterUser() { 51 | (...) 52 | 53 | password := get a (UTF-8, plaintext) password from somewhere 54 | 55 | hash, err := passlib.Hash(password) 56 | if err != nil { 57 | // couldn't hash password for some reason 58 | return 59 | } 60 | 61 | (store hash in database, etc.) 62 | } 63 | 64 | func CheckPassword() bool { 65 | password := get the password the user entered 66 | hash := the hash you stored from the call to Hash() 67 | 68 | newHash, err := passlib.Verify(password, hash) 69 | if err != nil { 70 | // incorrect password, malformed hash, etc. 71 | // either way, reject 72 | return false 73 | } 74 | 75 | // The context has decided, as per its policy, that 76 | // the hash which was used to validate the password 77 | // should be changed. It has upgraded the hash using 78 | // the verified password. 79 | if newHash != "" { 80 | (store newHash in database, replacing old hash) 81 | } 82 | 83 | return true 84 | } 85 | ``` 86 | 87 | scrypt Modular Crypt Format 88 | --------------------------- 89 | Since scrypt does not have a pre-existing modular crypt format standard, I made one. It's as follows: 90 | 91 | $s2$N$r$p$salt$hash 92 | 93 | ...where `N`, `r` and `p` are the respective difficulty parameters to scrypt as positive decimal integers without leading zeroes, and `salt` and `hash` are base64-encoded binary strings. Note that the RFC 4648 base64 encoding is used (not the one used by sha256-crypt and sha512-crypt). 94 | 95 | Licence 96 | ------- 97 | passlib is partially derived from Python's passlib and so maintains its BSD license. 98 | 99 | © 2008-2012 Assurance Technologies LLC. (Python passlib) BSD License 100 | © 2014 Hugo Landau BSD License 101 | 102 | -------------------------------------------------------------------------------- /hash/sha2crypt/sha2crypt.go: -------------------------------------------------------------------------------- 1 | // Package sha2crypt implements sha256-crypt and sha512-crypt. 2 | package sha2crypt 3 | 4 | import "fmt" 5 | import "expvar" 6 | import "crypto/rand" 7 | import "gopkg.in/hlandau/passlib.v1/hash/sha2crypt/raw" 8 | import "gopkg.in/hlandau/passlib.v1/abstract" 9 | 10 | var cSHA2CryptHashCalls = expvar.NewInt("passlib.sha2crypt.hashCalls") 11 | var cSHA2CryptVerifyCalls = expvar.NewInt("passlib.sha2crypt.verifyCalls") 12 | 13 | // An implementation of Scheme performing sha256-crypt. 14 | // 15 | // The number of rounds is raw.RecommendedRounds. 16 | var Crypter256 abstract.Scheme 17 | 18 | // An implementation of Scheme performing sha512-crypt. 19 | // 20 | // The number of rounds is raw.RecommendedRounds. 21 | var Crypter512 abstract.Scheme 22 | 23 | func init() { 24 | Crypter256 = NewCrypter256(raw.RecommendedRounds) 25 | Crypter512 = NewCrypter512(raw.RecommendedRounds) 26 | } 27 | 28 | // Returns a Scheme implementing sha256-crypt using the number of rounds 29 | // specified. 30 | func NewCrypter256(rounds int) abstract.Scheme { 31 | return &sha2Crypter{false, rounds} 32 | } 33 | 34 | // Returns a Scheme implementing sha512-crypt using the number of rounds 35 | // specified. 36 | func NewCrypter512(rounds int) abstract.Scheme { 37 | return &sha2Crypter{true, rounds} 38 | } 39 | 40 | type sha2Crypter struct { 41 | sha512 bool 42 | rounds int 43 | } 44 | 45 | // Changes the default rounds for the crypter. Be warned that this 46 | // is a global setting. The default default value is RecommendedRounds. 47 | func (c *sha2Crypter) SetRounds(rounds int) error { 48 | if rounds < raw.MinimumRounds || rounds > raw.MaximumRounds { 49 | return raw.ErrInvalidRounds 50 | } 51 | 52 | c.rounds = rounds 53 | return nil 54 | } 55 | 56 | func (c *sha2Crypter) SupportsStub(stub string) bool { 57 | if len(stub) < 3 || stub[0] != '$' || stub[2] != '$' { 58 | return false 59 | } 60 | return (stub[1] == '5' && !c.sha512) || (stub[1] == '6' && c.sha512) 61 | } 62 | 63 | func (c *sha2Crypter) Hash(password string) (string, error) { 64 | cSHA2CryptHashCalls.Add(1) 65 | 66 | stub, err := c.makeStub() 67 | if err != nil { 68 | return "", err 69 | } 70 | 71 | _, newHash, _, _, err := c.hash(password, stub) 72 | return newHash, err 73 | } 74 | 75 | func (c *sha2Crypter) Verify(password, hash string) (err error) { 76 | cSHA2CryptVerifyCalls.Add(1) 77 | 78 | _, newHash, _, _, err := c.hash(password, hash) 79 | if err == nil && !abstract.SecureCompare(hash, newHash) { 80 | err = abstract.ErrInvalidPassword 81 | } 82 | 83 | return 84 | } 85 | 86 | func (c *sha2Crypter) NeedsUpdate(stub string) bool { 87 | _, salt, _, rounds, err := raw.Parse(stub) 88 | if err != nil { 89 | return false // ... 90 | } 91 | 92 | return c.needsUpdate(salt, rounds) 93 | } 94 | 95 | func (c *sha2Crypter) needsUpdate(salt string, rounds int) bool { 96 | return rounds < c.rounds || len(salt) < 16 97 | } 98 | 99 | var errInvalidStub = fmt.Errorf("invalid sha2 password stub") 100 | 101 | func (c *sha2Crypter) hash(password, stub string) (oldHash, newHash, salt string, rounds int, err error) { 102 | isSHA512, salt, oldHash, rounds, err := raw.Parse(stub) 103 | if err != nil { 104 | return "", "", "", 0, err 105 | } 106 | 107 | if isSHA512 != c.sha512 { 108 | return "", "", "", 0, errInvalidStub 109 | } 110 | 111 | if c.sha512 { 112 | return oldHash, raw.Crypt512(password, salt, rounds), salt, rounds, nil 113 | } 114 | 115 | return oldHash, raw.Crypt256(password, salt, rounds), salt, rounds, nil 116 | } 117 | 118 | func (c *sha2Crypter) makeStub() (string, error) { 119 | ch := "5" 120 | if c.sha512 { 121 | ch = "6" 122 | } 123 | 124 | buf := make([]byte, 12) 125 | _, err := rand.Read(buf) 126 | if err != nil { 127 | return "", err 128 | } 129 | 130 | salt := raw.EncodeBase64(buf)[0:16] 131 | 132 | if c.rounds == raw.DefaultRounds { 133 | return fmt.Sprintf("$%s$%s", ch, salt), nil 134 | } 135 | 136 | return fmt.Sprintf("$%s$rounds=%d$%s", ch, c.rounds, salt), nil 137 | } 138 | 139 | func (c *sha2Crypter) String() string { 140 | if c.sha512 { 141 | return fmt.Sprintf("sha512-crypt(%d)", c.rounds) 142 | } else { 143 | return fmt.Sprintf("sha256-crypt(%d)", c.rounds) 144 | } 145 | } 146 | 147 | // © 2014 Hugo Landau BSD License 148 | -------------------------------------------------------------------------------- /hash/argon2/raw/argon2.go: -------------------------------------------------------------------------------- 1 | // Package raw provides a raw implementation of the modular-crypt-wrapped Argon2i primitive. 2 | package raw 3 | 4 | import ( 5 | "encoding/base64" 6 | "fmt" 7 | "golang.org/x/crypto/argon2" 8 | "strconv" 9 | "strings" 10 | ) 11 | 12 | // The current recommended time value for interactive logins. 13 | const RecommendedTime uint32 = 4 14 | 15 | // The current recommended memory for interactive logins. 16 | const RecommendedMemory uint32 = 32 * 1024 17 | 18 | // The current recommended number of threads for interactive logins. 19 | const RecommendedThreads uint8 = 4 20 | 21 | // Wrapper for golang.org/x/crypto/argon2 implementing a sensible 22 | // hashing interface. 23 | // 24 | // password should be a UTF-8 plaintext password. 25 | // salt should be a random salt value in binary form. 26 | // 27 | // Time, memory, and threads are parameters to argon2. 28 | // 29 | // Returns an argon2 encoded hash. 30 | func Argon2(password string, salt []byte, time, memory uint32, threads uint8) string { 31 | passwordb := []byte(password) 32 | 33 | hash := argon2.Key(passwordb, salt, time, memory, threads, 32) 34 | 35 | hstr := base64.RawStdEncoding.EncodeToString(hash) 36 | sstr := base64.RawStdEncoding.EncodeToString(salt) 37 | 38 | return fmt.Sprintf("$argon2i$v=%d$m=%d,t=%d,p=%d$%s$%s", argon2.Version, memory, time, threads, sstr, hstr) 39 | } 40 | 41 | // Indicates that a password hash or stub is invalid. 42 | var ErrInvalidStub = fmt.Errorf("invalid argon2 password stub") 43 | 44 | // Indicates that a key-value pair in the configuration part is malformed. 45 | var ErrInvalidKeyValuePair = fmt.Errorf("invalid argon2 key-value pair") 46 | 47 | // Indicates that the version part had the wrong number of parameters. 48 | var ErrParseVersion = fmt.Errorf("version section has wrong number of parameters") 49 | 50 | // Indicates that the hash config part had the wrong number of parameters. 51 | var ErrParseConfig = fmt.Errorf("hash config section has wrong number of parameters") 52 | 53 | // Indicates that the version parameter ("v") was missing in the version part, 54 | // even though it is required. 55 | var ErrMissingVersion = fmt.Errorf("version parameter (v) is missing") 56 | 57 | // Indicates that the memory parameter ("m") was mossing in the hash config 58 | // part, even though it is required. 59 | var ErrMissingMemory = fmt.Errorf("memory parameter (m) is missing") 60 | 61 | // Indicates that the time parameter ("t") was mossing in the hash config part, 62 | // even though it is required. 63 | var ErrMissingTime = fmt.Errorf("time parameter (t) is missing") 64 | 65 | // Indicates that the parallelism parameter ("p") was mossing in the hash config 66 | // part, even though it is required. 67 | var ErrMissingParallelism = fmt.Errorf("parallelism parameter (p) is missing") 68 | 69 | // Parses an argon2 encoded hash. 70 | // 71 | // The format is as follows: 72 | // 73 | // $argon2i$v=version$m=memory,t=time,p=threads$salt$hash // hash 74 | // $argon2i$v=version$m=memory,t=time,p=threads$salt // stub 75 | // 76 | func Parse(stub string) (salt, hash []byte, version int, time, memory uint32, parallelism uint8, err error) { 77 | if len(stub) < 26 || !strings.HasPrefix(stub, "$argon2i$") { 78 | err = ErrInvalidStub 79 | return 80 | } 81 | 82 | // $argon2i$ v=version$m=memory,t=time,p=threads$salt-base64$hash-base64 83 | parts := strings.Split(stub[9:], "$") 84 | 85 | // version-params$hash-config-params$salt[$hash] 86 | if len(parts) < 3 || len(parts) > 4 { 87 | err = ErrInvalidStub 88 | return 89 | } 90 | 91 | // Parse the first configuration part, the version parameters. 92 | versionParams, err := parseKeyValuePair(parts[0]) 93 | if err != nil { 94 | return 95 | } 96 | 97 | // Must be exactly one parameter in the version part. 98 | if len(versionParams) != 1 { 99 | err = ErrParseVersion 100 | return 101 | } 102 | 103 | // It must be "v". 104 | val, ok := versionParams["v"] 105 | if !ok { 106 | err = ErrMissingVersion 107 | return 108 | } 109 | 110 | version = int(val) 111 | 112 | // Parse the second configuration part, the hash config parameters. 113 | hashParams, err := parseKeyValuePair(parts[1]) 114 | if err != nil { 115 | return 116 | } 117 | 118 | // It must have exactly three parameters. 119 | if len(hashParams) != 3 { 120 | err = ErrParseConfig 121 | return 122 | } 123 | 124 | // Memory parameter. 125 | val, ok = hashParams["m"] 126 | if !ok { 127 | err = ErrMissingMemory 128 | return 129 | } 130 | 131 | memory = uint32(val) 132 | 133 | // Time parameter. 134 | val, ok = hashParams["t"] 135 | if !ok { 136 | err = ErrMissingTime 137 | return 138 | } 139 | 140 | time = uint32(val) 141 | 142 | // Parallelism parameter. 143 | val, ok = hashParams["p"] 144 | if !ok { 145 | err = ErrMissingParallelism 146 | return 147 | } 148 | 149 | parallelism = uint8(val) 150 | 151 | // Decode salt. 152 | salt, err = base64.RawStdEncoding.DecodeString(parts[2]) 153 | if err != nil { 154 | return 155 | } 156 | 157 | // Decode hash if present. 158 | if len(parts) >= 4 { 159 | hash, err = base64.RawStdEncoding.DecodeString(parts[3]) 160 | } 161 | 162 | return 163 | } 164 | 165 | func parseKeyValuePair(pairs string) (result map[string]uint64, err error) { 166 | result = map[string]uint64{} 167 | 168 | parameterParts := strings.Split(pairs, ",") 169 | 170 | for _, parameter := range parameterParts { 171 | parts := strings.SplitN(parameter, "=", 2) 172 | if len(parts) != 2 { 173 | err = ErrInvalidKeyValuePair 174 | return 175 | } 176 | 177 | parsedi, err := strconv.ParseUint(parts[1], 10, 32) 178 | if err != nil { 179 | return result, err 180 | } 181 | 182 | result[parts[0]] = parsedi 183 | } 184 | 185 | return result, nil 186 | } 187 | -------------------------------------------------------------------------------- /default.go: -------------------------------------------------------------------------------- 1 | package passlib 2 | 3 | import ( 4 | "fmt" 5 | "gopkg.in/hlandau/passlib.v1/abstract" 6 | "gopkg.in/hlandau/passlib.v1/hash/argon2" 7 | "gopkg.in/hlandau/passlib.v1/hash/bcrypt" 8 | "gopkg.in/hlandau/passlib.v1/hash/bcryptsha256" 9 | "gopkg.in/hlandau/passlib.v1/hash/pbkdf2" 10 | "gopkg.in/hlandau/passlib.v1/hash/scrypt" 11 | "gopkg.in/hlandau/passlib.v1/hash/sha2crypt" 12 | "time" 13 | ) 14 | 15 | // This is the first and default set of defaults used by passlib. It prefers 16 | // scrypt-sha256. It is now obsolete. 17 | const Defaults20160922 = "20160922" 18 | 19 | // This is the most up-to-date set of defaults preferred by passlib. It prefers 20 | // Argon2i. You must opt into it by calling UseDefaults at startup. 21 | const Defaults20180601 = "20180601" 22 | 23 | // This value, when passed to UseDefaults, causes passlib to always use the 24 | // very latest set of defaults. DO NOT use this unless you are sure that 25 | // opportunistic hash upgrades will not cause breakage for your application 26 | // when future versions of passlib are released. See func UseDefaults. 27 | const DefaultsLatest = "latest" 28 | 29 | // Default schemes as of 2016-09-22. 30 | var defaultSchemes20160922 = []abstract.Scheme{ 31 | scrypt.SHA256Crypter, 32 | argon2.Crypter, 33 | sha2crypt.Crypter512, 34 | sha2crypt.Crypter256, 35 | bcryptsha256.Crypter, 36 | pbkdf2.SHA512Crypter, 37 | pbkdf2.SHA256Crypter, 38 | bcrypt.Crypter, 39 | pbkdf2.SHA1Crypter, 40 | } 41 | 42 | // Default schemes as of 2018-06-01. 43 | var defaultSchemes20180601 = []abstract.Scheme{ 44 | argon2.Crypter, 45 | scrypt.SHA256Crypter, 46 | sha2crypt.Crypter512, 47 | sha2crypt.Crypter256, 48 | bcryptsha256.Crypter, 49 | pbkdf2.SHA512Crypter, 50 | pbkdf2.SHA256Crypter, 51 | bcrypt.Crypter, 52 | pbkdf2.SHA1Crypter, 53 | } 54 | 55 | // The default schemes, most preferred first. The first scheme will be used to 56 | // hash passwords, and any of the schemes may be used to verify existing 57 | // passwords. The contents of this value may change with subsequent releases. 58 | // 59 | // If you want to change this, set DefaultSchemes to a slice to an 60 | // abstract.Scheme array of your own construction, rather than mutating the 61 | // array the slice points to. 62 | // 63 | // To see the default schemes used in the current release of passlib, see 64 | // default.go. See also the UseDefaults function for more information on how 65 | // the list of default schemes is determined. The default value of 66 | // DefaultSchemes (the default defaults) won't change; you need to call 67 | // UseDefaults to allow your application to upgrade to newer hashing schemes 68 | // (or set DefaultSchemes manually, or create a custom context with its own 69 | // schemes set). 70 | var DefaultSchemes []abstract.Scheme 71 | 72 | func init() { 73 | DefaultSchemes = defaultSchemes20160922 74 | } 75 | 76 | // It is strongly recommended that you call this function like this before using passlib: 77 | // 78 | // passlib.UseDefaults("YYYYMMDD") 79 | // 80 | // where YYYYMMDD is a date. This will be used to select the preferred scheme 81 | // to use. If you do not call UseDefaults, the preferred scheme (the first item 82 | // in the default schemes list) current as of 2016-09-22 will always be used, 83 | // meaning that upgrade will not occur even though better schemes are now 84 | // available. 85 | // 86 | // Note that even if you don't call this function, new schemes will still be 87 | // added to DefaultSchemes over time as non-initial values (items not at index 88 | // 0), so servers will always, by default, be able to validate all schemes 89 | // which passlib supports at any given time. 90 | // 91 | // The reason you must call this function is as follows: If passlib is deployed 92 | // as part of a web application in a multi-server deployment, and passlib is 93 | // updated, and the new version of that application with the updated passlib is 94 | // deployed, that upgrade process is unlikely to be instantaneous. Old versions 95 | // of the web application may continue to run on some servers. If merely 96 | // upgrading passlib caused password hashes to be upgraded to the newer scheme 97 | // on login, the older daemons may not be able to validate these passwords and 98 | // users may have issues logging in. Although this can be ameliorated to some 99 | // extent by introducing a new scheme to passlib, waiting some months, and only 100 | // then making this the default, this could still cause issued if passlib is 101 | // only updated very occasionally. 102 | // 103 | // Thus, you should update your call to UseDefaults only when all servers have 104 | // been upgraded, and it is thus guaranteed that they will all be able to 105 | // verify the new scheme. Making this value loadable from a configuration file 106 | // is recommended. 107 | // 108 | // If you are using a single-server configuration, you can use the special 109 | // value "latest" here (or, equivalently, a date far into the future), which 110 | // will always use the most preferred scheme. This is hazardous in a 111 | // multi-server environment. 112 | // 113 | // The constants beginning 'Defaults' in this package document dates 114 | // which are meaningful to this function. The constant values they are equal to 115 | // will never change, so there is no need to use them instead of string 116 | // literals, although you may if you wish; they are intended mainly as 117 | // documentation as to the significance of various dates. 118 | // 119 | // Example for opting in to the latest set of defaults: 120 | // 121 | // passlib.UseDefaults(passlib.Defaults20180601) 122 | // 123 | func UseDefaults(date string) error { 124 | if date == "latest" { 125 | DefaultSchemes = defaultSchemes20180601 126 | return nil 127 | } 128 | 129 | t, err := time.ParseInLocation("20060102", date, time.UTC) 130 | if err != nil { 131 | return fmt.Errorf("invalid time string passed to passlib.UseDefaults: %q", date) 132 | } 133 | 134 | if !t.Before(time.Date(2016, 9, 22, 0, 0, 0, 0, time.UTC)) { 135 | DefaultSchemes = defaultSchemes20180601 136 | return nil 137 | } 138 | 139 | DefaultSchemes = defaultSchemes20160922 140 | return nil 141 | } 142 | -------------------------------------------------------------------------------- /hash/sha2crypt/raw/sha2crypt.go: -------------------------------------------------------------------------------- 1 | // Package raw provides a raw implementation of the sha256-crypt and sha512-crypt primitives. 2 | package raw 3 | 4 | import "io" 5 | import "fmt" 6 | import "hash" 7 | import "crypto/sha256" 8 | import "crypto/sha512" 9 | 10 | // The minimum number of rounds permissible for sha256-crypt and sha512-crypt. 11 | const MinimumRounds = 1000 12 | 13 | // The maximum number of rounds permissible for sha256-crypt and sha512-crypt. 14 | // Don't use this! 15 | const MaximumRounds = 999999999 16 | 17 | // This is the 'default' number of rounds for sha256-crypt and sha512-crypt. If 18 | // this rounds value is used the number of rounds is not explicitly specified 19 | // in the modular crypt format, as it is the default. 20 | const DefaultRounds = 5000 21 | 22 | // This is the recommended number of rounds for sha256-crypt and sha512-crypt. 23 | // This may change with subsequent releases of this package. It is recommended 24 | // that you invoke sha256-crypt or sha512-crypt with this value, or a value 25 | // proportional to it. 26 | const RecommendedRounds = 10000 27 | 28 | // Calculates sha256-crypt. The password must be in plaintext and be a UTF-8 29 | // string. 30 | // 31 | // The salt must be a valid ASCII between 0 and 16 characters in length 32 | // inclusive. 33 | // 34 | // See the constants in this package for suggested values for rounds. 35 | // 36 | // Rounds must be in the range 1000 <= rounds <= 999999999. The function panics 37 | // if this is not the case. 38 | // 39 | // The output is in modular crypt format. 40 | func Crypt256(password, salt string, rounds int) string { 41 | return "$5" + shaCrypt(password, salt, rounds, sha256.New, transpose256) 42 | } 43 | 44 | // Calculates sha256-crypt. The password must be in plaintext and be a UTF-8 45 | // string. 46 | // 47 | // The salt must be a valid ASCII between 0 and 16 characters in length 48 | // inclusive. 49 | // 50 | // See the constants in this package for suggested values for rounds. 51 | // 52 | // Rounds must be in the range 1000 <= rounds <= 999999999. The function panics 53 | // if this is not the case. 54 | // 55 | // The output is in modular crypt format. 56 | func Crypt512(password, salt string, rounds int) string { 57 | return "$6" + shaCrypt(password, salt, rounds, sha512.New, transpose512) 58 | } 59 | 60 | func shaCrypt(password, salt string, rounds int, newHash func() hash.Hash, transpose func(b []byte)) string { 61 | if rounds < MinimumRounds || rounds > MaximumRounds { 62 | panic("sha256-crypt rounds must be in 1000 <= rounds <= 999999999") 63 | } 64 | 65 | passwordb := []byte(password) 66 | saltb := []byte(salt) 67 | if len(saltb) > 16 { 68 | panic("salt must not exceed 16 bytes") 69 | } 70 | 71 | // B 72 | b := newHash() 73 | b.Write(passwordb) 74 | b.Write(saltb) 75 | b.Write(passwordb) 76 | bsum := b.Sum(nil) 77 | 78 | // A 79 | a := newHash() 80 | a.Write(passwordb) 81 | a.Write(saltb) 82 | repeat(a, bsum, len(passwordb)) 83 | 84 | plen := len(passwordb) 85 | for plen != 0 { 86 | if (plen & 1) != 0 { 87 | a.Write(bsum) 88 | } else { 89 | a.Write(passwordb) 90 | } 91 | plen = plen >> 1 92 | } 93 | 94 | asum := a.Sum(nil) 95 | 96 | // DP 97 | dp := newHash() 98 | for i := 0; i < len(passwordb); i++ { 99 | dp.Write(passwordb) 100 | } 101 | 102 | dpsum := dp.Sum(nil) 103 | 104 | // P 105 | p := make([]byte, len(passwordb)) 106 | repeatTo(p, dpsum) 107 | 108 | // DS 109 | ds := newHash() 110 | for i := 0; i < (16 + int(asum[0])); i++ { 111 | ds.Write(saltb) 112 | } 113 | 114 | dssum := ds.Sum(nil)[0:len(saltb)] 115 | 116 | // S 117 | s := make([]byte, len(saltb)) 118 | repeatTo(s, dssum) 119 | 120 | // C 121 | cur := asum[:] 122 | for i := 0; i < rounds; i++ { 123 | c := newHash() 124 | if (i & 1) != 0 { 125 | c.Write(p) 126 | } else { 127 | c.Write(cur) 128 | } 129 | if (i % 3) != 0 { 130 | c.Write(s) 131 | } 132 | if (i % 7) != 0 { 133 | c.Write(p) 134 | } 135 | if (i & 1) == 0 { 136 | c.Write(p) 137 | } else { 138 | c.Write(cur) 139 | } 140 | cur = c.Sum(nil)[:] 141 | } 142 | 143 | // Transposition 144 | transpose(cur) 145 | 146 | // Hash 147 | hstr := EncodeBase64(cur) 148 | 149 | if rounds == DefaultRounds { 150 | return fmt.Sprintf("$%s$%s", salt, hstr) 151 | } 152 | 153 | return fmt.Sprintf("$rounds=%d$%s$%s", rounds, salt, hstr) 154 | } 155 | 156 | func repeat(w io.Writer, b []byte, sz int) { 157 | var i int 158 | for i = 0; (i + len(b)) <= sz; i += len(b) { 159 | w.Write(b) 160 | } 161 | w.Write(b[0 : sz-i]) 162 | } 163 | 164 | func repeatTo(out []byte, b []byte) { 165 | if len(b) == 0 { 166 | return 167 | } 168 | 169 | var i int 170 | for i = 0; (i + len(b)) <= len(out); i += len(b) { 171 | copy(out[i:], b) 172 | } 173 | copy(out[i:], b) 174 | } 175 | 176 | func transpose256(b []byte) { 177 | b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15], b[16], b[17], b[18], b[19], b[20], b[21], b[22], b[23], b[24], b[25], b[26], b[27], b[28], b[29], b[30], b[31] = 178 | b[20], b[10], b[0], b[11], b[1], b[21], b[2], b[22], b[12], b[23], b[13], b[3], b[14], b[4], b[24], b[5], b[25], b[15], b[26], b[16], b[6], b[17], b[7], b[27], b[8], b[28], b[18], b[29], b[19], b[9], b[30], b[31] 179 | } 180 | 181 | func transpose512(b []byte) { 182 | b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15], b[16], b[17], b[18], b[19], b[20], b[21], b[22], b[23], b[24], b[25], b[26], b[27], b[28], b[29], b[30], b[31], b[32], b[33], b[34], b[35], b[36], b[37], b[38], b[39], b[40], b[41], b[42], b[43], b[44], b[45], b[46], b[47], b[48], b[49], b[50], b[51], b[52], b[53], b[54], b[55], b[56], b[57], b[58], b[59], b[60], b[61], b[62], b[63] = 183 | b[42], b[21], b[0], b[1], b[43], b[22], b[23], b[2], b[44], b[45], b[24], b[3], b[4], b[46], b[25], b[26], b[5], b[47], b[48], b[27], b[6], b[7], b[49], b[28], b[29], b[8], b[50], b[51], b[30], b[9], b[10], b[52], b[31], b[32], b[11], b[53], b[54], b[33], b[12], b[13], b[55], b[34], b[35], b[14], b[56], b[57], b[36], b[15], b[16], b[58], b[37], b[38], b[17], b[59], b[60], b[39], b[18], b[19], b[61], b[40], b[41], b[20], b[62], b[63] 184 | } 185 | 186 | // © 2008-2012 Assurance Technologies LLC. (Python passlib) BSD License 187 | // © 2014 Hugo Landau BSD License 188 | -------------------------------------------------------------------------------- /passlib.go: -------------------------------------------------------------------------------- 1 | // Package passlib provides a simple password hashing and verification 2 | // interface abstracting multiple password hashing schemes. 3 | // 4 | // After initialisation, most people need concern themselves only with the 5 | // functions Hash and Verify, which uses the default context and sensible 6 | // defaults. 7 | // 8 | // Library Initialization 9 | // 10 | // You should initialise the library before using it with the following line. 11 | // 12 | // // Call this at application startup. 13 | // passlib.UseDefaults(passlib.Defaults20180601) 14 | // 15 | // See func UseDefaults for details. 16 | package passlib // import "gopkg.in/hlandau/passlib.v1" 17 | 18 | import ( 19 | "gopkg.in/hlandau/easymetric.v1/cexp" 20 | "gopkg.in/hlandau/passlib.v1/abstract" 21 | ) 22 | 23 | var cHashCalls = cexp.NewCounter("passlib.ctx.hashCalls") 24 | var cVerifyCalls = cexp.NewCounter("passlib.ctx.verifyCalls") 25 | var cSuccessfulVerifyCalls = cexp.NewCounter("passlib.ctx.successfulVerifyCalls") 26 | var cFailedVerifyCalls = cexp.NewCounter("passlib.ctx.failedVerifyCalls") 27 | var cSuccessfulVerifyCallsWithUpgrade = cexp.NewCounter("passlib.ctx.successfulVerifyCallsWithUpgrade") 28 | var cSuccessfulVerifyCallsDeferringUpgrade = cexp.NewCounter("passlib.ctx.successfulVerifyCallsDeferringUpgrade") 29 | 30 | // A password hashing context, that uses a given set of schemes to hash and 31 | // verify passwords. 32 | type Context struct { 33 | // Slice of schemes to use, most preferred first. 34 | // 35 | // If left uninitialized, a sensible default set of schemes will be used. 36 | // 37 | // An upgrade hash (see the newHash return value of the Verify method of the 38 | // abstract.Scheme interface) will be issued whenever a password is validated 39 | // using a scheme which is not the first scheme in this slice. 40 | Schemes []abstract.Scheme 41 | } 42 | 43 | func (ctx *Context) schemes() []abstract.Scheme { 44 | if ctx.Schemes == nil { 45 | return DefaultSchemes 46 | } 47 | 48 | return ctx.Schemes 49 | } 50 | 51 | // Hashes a UTF-8 plaintext password using the context and produces a password hash. 52 | // 53 | // If stub is "", one is generated automaticaly for the preferred password hashing 54 | // scheme; you should specify stub as "" in almost all cases. 55 | // 56 | // The provided or randomly generated stub is used to deterministically hash 57 | // the password. The returned hash is in modular crypt format. 58 | // 59 | // If the context has not been specifically configured, a sensible default policy 60 | // is used. See the fields of Context. 61 | func (ctx *Context) Hash(password string) (hash string, err error) { 62 | cHashCalls.Add(1) 63 | 64 | return ctx.schemes()[0].Hash(password) 65 | } 66 | 67 | // Verifies a UTF-8 plaintext password using a previously derived password hash 68 | // and the default context. Returns nil err only if the password is valid. 69 | // 70 | // If the hash is determined to be deprecated based on the context policy, and 71 | // the password is valid, the password is hashed using the preferred password 72 | // hashing scheme and returned in newHash. You should use this to upgrade any 73 | // stored password hash in your database. 74 | // 75 | // newHash is empty if the password was not valid or if no upgrade is required. 76 | // 77 | // You should treat any non-nil err as a password verification error. 78 | func (ctx *Context) Verify(password, hash string) (newHash string, err error) { 79 | return ctx.verify(password, hash, true) 80 | } 81 | 82 | // Like Verify, but does not hash an upgrade password when upgrade is required. 83 | func (ctx *Context) VerifyNoUpgrade(password, hash string) error { 84 | _, err := ctx.verify(password, hash, false) 85 | return err 86 | } 87 | 88 | func (ctx *Context) verify(password, hash string, canUpgrade bool) (newHash string, err error) { 89 | cVerifyCalls.Add(1) 90 | 91 | for i, scheme := range ctx.schemes() { 92 | if !scheme.SupportsStub(hash) { 93 | continue 94 | } 95 | 96 | err = scheme.Verify(password, hash) 97 | if err != nil { 98 | cFailedVerifyCalls.Add(1) 99 | return "", err 100 | } 101 | 102 | cSuccessfulVerifyCalls.Add(1) 103 | if i != 0 || scheme.NeedsUpdate(hash) { 104 | if canUpgrade { 105 | cSuccessfulVerifyCallsWithUpgrade.Add(1) 106 | 107 | // If the scheme is not the first scheme, try and rehash with the 108 | // preferred scheme. 109 | if newHash, err2 := ctx.Hash(password); err2 == nil { 110 | return newHash, nil 111 | } 112 | } else { 113 | cSuccessfulVerifyCallsDeferringUpgrade.Add(1) 114 | } 115 | } 116 | 117 | return "", nil 118 | } 119 | 120 | return "", abstract.ErrUnsupportedScheme 121 | } 122 | 123 | // Determines whether a stub or hash needs updating according to the policy of 124 | // the context. 125 | func (ctx *Context) NeedsUpdate(stub string) bool { 126 | for i, scheme := range ctx.schemes() { 127 | if scheme.SupportsStub(stub) { 128 | return i != 0 || scheme.NeedsUpdate(stub) 129 | } 130 | } 131 | 132 | return false 133 | } 134 | 135 | // The default context, which uses sensible defaults. Most users should not 136 | // reconfigure this. The defaults may change over time, so you may wish 137 | // to reconfigure the context or use a custom context if you want precise 138 | // control over the hashes used. 139 | var DefaultContext Context 140 | 141 | // Hashes a UTF-8 plaintext password using the default context and produces a 142 | // password hash. Chooses the preferred password hashing scheme based on the 143 | // configured policy. The default policy is sensible. 144 | func Hash(password string) (hash string, err error) { 145 | return DefaultContext.Hash(password) 146 | } 147 | 148 | // Verifies a UTF-8 plaintext password using a previously derived password hash 149 | // and the default context. Returns nil err only if the password is valid. 150 | // 151 | // If the hash is determined to be deprecated based on policy, and the password 152 | // is valid, the password is hashed using the preferred password hashing scheme 153 | // and returned in newHash. You should use this to upgrade any stored password 154 | // hash in your database. 155 | // 156 | // newHash is empty if the password was invalid or no upgrade is required. 157 | // 158 | // You should treat any non-nil err as a password verification error. 159 | func Verify(password, hash string) (newHash string, err error) { 160 | return DefaultContext.Verify(password, hash) 161 | } 162 | 163 | // Like Verify, but never upgrades. 164 | func VerifyNoUpgrade(password, hash string) error { 165 | return DefaultContext.VerifyNoUpgrade(password, hash) 166 | } 167 | 168 | // Uses the default context to determine whether a stub or hash needs updating. 169 | func NeedsUpdate(stub string) bool { 170 | return DefaultContext.NeedsUpdate(stub) 171 | } 172 | 173 | // © 2008-2012 Assurance Technologies LLC. (Python passlib) BSD License 174 | // © 2014 Hugo Landau BSD License 175 | -------------------------------------------------------------------------------- /hash/pbkdf2/pbkdf2_test.go: -------------------------------------------------------------------------------- 1 | package pbkdf2 2 | 3 | import "testing" 4 | 5 | type test struct { 6 | password string 7 | hash string 8 | } 9 | 10 | var test_sha1 = []test{ 11 | {"", "$pbkdf2$131000$rpVyDoFwDoHwfi8FAGBMqQ$KzxgTFYx.WC8y3G7T.ZRNC16BDs"}, 12 | {"a", "$pbkdf2$131000$6h0jhHAuRSjF2DsnJOT8Hw$iXVE/hRu6mhyxsfGiTVZgBAiWJg"}, 13 | {"ab", "$pbkdf2$131000$sXbunfP.n3PufQ9BSMkZow$rrEl2XYXDwLnHF/7AwSVCq8WCr4"}, 14 | {"abc", "$pbkdf2$131000$bW0tJaT03huD8F6LcU4pRQ$dtV.m979atKXoe8dNNpMa43Gips"}, 15 | {"abcd", "$pbkdf2$131000$2btXqhWCEKLU.p.TUurd.w$G3eQnQiG/BFRU.F4XqwUBPR8O2E"}, 16 | {"abcde", "$pbkdf2$131000$gzDm3PsfA2BMaW3tHaO0Ng$oHF5I8Id58OCLTTUKElczL2oQJ8"}, 17 | {"abcdef", "$pbkdf2$131000$pVSKkdJaS6nV.v/fe29NCQ$ruqeF1RLQsa5bESBa3Wvm0/7vEU"}, 18 | {"abcdefg", "$pbkdf2$131000$FWIshVCqVSolxHgvhdD6/w$W3dxvb8s8/7srAorKOllpLcYFGA"}, 19 | {"abcdefgh", "$pbkdf2$131000$nLNWqvWeU4qRMobwHkOodQ$bKEjhNS2UVYj9Id1v0y7/vSgtPM"}, 20 | {"abcdefghi", "$pbkdf2$131000$pNS6V.o9B2AsxRjD.D.nFA$/b.TnvXwHn9gvL7oGw77UO6x4B4"}, 21 | {"abcdefghij", "$pbkdf2$131000$EkLIWavVunfOeS/FWCvlfA$c8LtgafQyu2YPJ7KaVYFVFQhXQg"}, 22 | {"abcdefghijk", "$pbkdf2$131000$a835PwdgDAGA0BqDEEIoZQ$Q3Lj40rPoYw6zOr7MXCbKe.SgFA"}, 23 | {"abcdefghijkl", "$pbkdf2$131000$n/NeqxWidO5da.1dixEihA$pwWmr/A.RDaydHcBR9izZL2c20M"}, 24 | {"abcdefghijklm", "$pbkdf2$131000$Z8y5t1ZKaa1VCsFY613LWQ$19yrjsbACj.14/FHILQKngksibo"}, 25 | {"abcdefghijklmn", "$pbkdf2$131000$KIUQQiiFsFZq7T1nDMG49w$MPV8Fx/km0tfKd73m1opskTA4vU"}, 26 | {"abcdefghijklmno", "$pbkdf2$131000$.d8bI6RUaq21ViplzDmHcA$7SZbV7nf5aewrQBGF4g0PRRM/Fg"}, 27 | {"abcdefghijklmnop", "$pbkdf2$131000$ba219v6fMyYEAGAsxdi7dw$eVNXt/0ljC8v.WVhLQB167TiUy4"}, 28 | {"qrstuvwxyz012345", "$pbkdf2$131000$FCIkhDCGsBZiDEGIcW7NOQ$VK6RSOHwVEB0WlzR2HzoZcJp02Y"}, 29 | {"67890./", "$pbkdf2$131000$FAKAMGas9X5vDaF0TkkJ4Q$Z7RUXPH2IdhH2FEfbSHfkBS8ZIU"}, 30 | {"ABCDEFGHIJKLMNOP", "$pbkdf2$131000$k/IeQ0iJkVJKCUEIgXCOEQ$zZDBIa8T7jG//e8ITciUIHjqfdM"}, 31 | {"QRSTUVWXYZ012345", "$pbkdf2$131000$LmXs/R9jbM2Z8967N2ZsDQ$rwr7vtpJWCiz.lAy9rzF.WztLqw"}, 32 | } 33 | 34 | var test_sha256 = []test{ 35 | {"", "$pbkdf2-sha256$29000$FeKc8773HmOMcW7tHUPo/Q$Xc31n0kWSaQd7xXJkR0O5W7vHXVCLfKNdKsgiBW.aYc"}, 36 | {"a", "$pbkdf2-sha256$29000$FoKQshaCUMo5B6AUwpjT.g$dosGQILQixtDqR1Zh2.GowEngJ59lWPivBGptx32960"}, 37 | {"ab", "$pbkdf2-sha256$29000$8p5zrhUCIGQMoVRKqZVyzg$O.z4CN4JlPJltIZMzS6bi6YtRH.WfeDvPFCA7Z24o1M"}, 38 | {"abc", "$pbkdf2-sha256$29000$2dsbYwxhzDlHqBWCMObc2w$GYnQVBLHvbjzDpZdOY8lZtkrE8lqbZ3zURM9rXMZv1A"}, 39 | {"abcd", "$pbkdf2-sha256$29000$PieklNJ6z5lzLqU0hhAC4A$E/M477bliAuTGt.b/9Yf4gIsb4.MKXQsO4k1F26BoHw"}, 40 | {"abcde", "$pbkdf2-sha256$29000$WKv1Puc8R4hx7v3/3xvDWA$U9WfYB1Y1EdIkkmbyxkagOaO5ZHiDJKDeMKpySNkpNA"}, 41 | {"abcdef", "$pbkdf2-sha256$29000$cu59zznn3HsvpRSi1HoPwQ$U28XO37KiGT1rfiN1AtmMq5wc9FsqjK4hEPLgKpaCWM"}, 42 | {"abcdefg", "$pbkdf2-sha256$29000$2Nu71zon5BzjvDfGOMdYiw$U7uoKXZVYcwWQ26osBlr3BRpI71/wF52L3nHUmUI4eU"}, 43 | {"abcdefgh", "$pbkdf2-sha256$29000$BGDMeW9tjVFKiREihPD./w$6L38QVvEr4xAN4iUunumpyfcu64ym4n/cBhDu1Y5a.4"}, 44 | {"abcdefghi", "$pbkdf2-sha256$29000$HeN8b42RsjYGAKDUuhdizA$aMOAAuZyr48CIGEKbaEsnvxPBXDSvNKT2nsz5we2IVg"}, 45 | {"abcdefghij", "$pbkdf2-sha256$29000$5vwfo7RWCmHsvVdKibH23g$IJsvPxtO6HqBhGdwbxE33FvqC0eN6vzHfPJTdvgbf9o"}, 46 | {"abcdefghijk", "$pbkdf2-sha256$29000$KwUgROj9nzMGwHiP8X7vfQ$TS4L1KqPKeX7nFZYC8aZ3zo3R/D2w5uqDIx0Qn4OEdA"}, 47 | {"abcdefghijkl", "$pbkdf2-sha256$29000$IkSI0dq7F0KoVUrpvRfiPA$bHh6iwlRwBlrwgpZgt9z7dmSY1QvVLk95CwxjeiJ8KI"}, 48 | {"abcdefghijklm", "$pbkdf2-sha256$29000$LgWAcM55z1kLIcS4935vzQ$b1lkgvjoooyXbJdSlZnva8SHb5IV/e69ReD1c9jHjP8"}, 49 | {"abcdefghijklmn", "$pbkdf2-sha256$29000$652TMmas1VoLQej9H4NQag$HuexmPWNkzQP6gMsPvFn2y1gMHDJC/f5WsSpCaJeXRk"}, 50 | {"abcdefghijklmno", "$pbkdf2-sha256$29000$dc55LwWgtNb63xtDiNF6rw$ndx1bqOEDP93yUxcf/LqvoxljKpdWuVMQAlkM9qZO4g"}, 51 | {"abcdefghijklmnop", "$pbkdf2-sha256$29000$eK.1NobQWosxphRi7F2LcQ$tI.aL90NRMAyK/8HdDbVzsmBI1uymAUi/5b0xBwNCog"}, 52 | {"qrstuvwxyz012345", "$pbkdf2-sha256$29000$i3EuRQiBMEZIiTEmBEBobQ$EhWgevRpzypjSLsRtxKAXtSBAIjArS8Ckhxh/43j024"}, 53 | {"67890./", "$pbkdf2-sha256$29000$gvAewzintLYWwphTqjWGkA$Y7vNdsWTeZCv/l/qyiRbG6JPRji0bY/lANT15anr1cc"}, 54 | {"ABCDEFGHIJKLMNOP", "$pbkdf2-sha256$29000$41xrDUGI8d5ba81Z673Xmg$ywz5c/jLFSeJ6qjC8AMoJe7wgtnP0FMC7pZ4Ku6ja6U"}, 55 | {"QRSTUVWXYZ012345", "$pbkdf2-sha256$29000$S2kNwXhPqbUWojRGSMn5Hw$hvNURMsr8AFlhaIeRhJRVFXirtbNdWvPjB0z24rTTG8"}, 56 | } 57 | 58 | var test_sha512 = []test{ 59 | {"", "$pbkdf2-sha512$25000$Rug9hxCCEAJAqBXCeO99rw$Z5cLeLLbcEHdv.LQzFi86iEVtMDdkKD8eI1b4JynptuWazoGEi/dkOmbD0211BXiKMlMPDBaDjqbp2xAelpSAQ"}, 60 | {"a", "$pbkdf2-sha512$25000$nPN.D2FMKUUopXSudY4xpg$5wB78x5VWeeXvIscWZNQZOmMiVN9s1UDSC58tLd8z9PF.KFCZYzpPy/kT0DFMhaMEUgzhX0rC8LYSVVl1cuJDw"}, 61 | {"ab", "$pbkdf2-sha512$25000$5XxvDYFQqnUOQWhNKeV8Tw$hhHStXAyVWsa7GFMD0oHk1.hI6iYN7MDRx5zhvfcCUV.Hmh3xuezYAkgHQAN1yBdW9eO6p/6cDSj7fmr0AtIhw"}, 62 | {"abc", "$pbkdf2-sha512$25000$29s7h1BqzZnT.n8vBUDIGQ$80zmUh1Ytb8Gd1T.ik/eaFELNmu9gKUZYZZGlm15xqgHSSYvJTYZteFoy5qmAEdSSroYhFLFxW9IGn7lEqY2Sw"}, 63 | {"abcd", "$pbkdf2-sha512$25000$.997D8F4D.G8dw6BMCbEeA$pWOT1dKN8rrA6WubYdrFHkYyvIsAiAJ1JeqoJ1KxvDqP4LUqflgpYUiLfSa0DNliIIrhmKDUTWncXprO57EnEA"}, 64 | {"abcde", "$pbkdf2-sha512$25000$7r1XSqkV4hzj3BsjBOA8Zw$sq9G8ZfZG1g4OGidpElHT7yqanrO9FsgPYY131hAma.LcXuXJybY1mC3Q8XzkDafvngIynOuGl8NDueT481jWA"}, 65 | {"abcdef", "$pbkdf2-sha512$25000$yZnz3ttbq7XW2ruX0tq7tw$8O2TVXrSHNopS3t7uuh/Wl6Hnhf7p4sF78hiYeNzhV5UFFf4Iah93IkHvmycaC3bipGFCoHUzcTNkRiO/AnAMw"}, 66 | {"abcdefg", "$pbkdf2-sha512$25000$Zsw551yLsbb2ntM6J2SsdQ$DrcS6jDeOsZs0yNXvqjioXoRL9EI4vEbrX.CNsIGtd9p5fkn1AO4vcnkjrRj1ZPXFgn0gTu8vQiQg1fDgfSiAg"}, 67 | {"abcdefgh", "$pbkdf2-sha512$25000$FOK8N6Y0xpjzPgdgLKVUSg$jTdoR/ns0CimsLGUjkImjL7dW41xoMKBVjE/CAuPPokMhk9178E2wnaMJ7JJ9gw9ipCPPrICdLzASzmwccgSXg"}, 68 | {"abcdefghi", "$pbkdf2-sha512$25000$7H2vNeacE0IoZQwBwNjbmw$pcMYQMOjFpnuxwdpEkXCId0jWZpnZkf.UGRSemOvfYUqmxt1aa0eXYmZ8cMB5PkhWmsBkOFCPC8HcpfFTkBgtA"}, 69 | {"abcdefghij", "$pbkdf2-sha512$25000$u5cyppSylvL.PyfEeE/JGQ$r1SuBURIVnuPYklue.VwmDTMSPm.KmE8pxUHjMkCTtBwm24DJyBdCtAt8a2SEwA0vgaYyJGN.z68/BvHItBjUg"}, 70 | {"abcdefghijk", "$pbkdf2-sha512$25000$hXCuFULo/f8f41yrlXKOMQ$sCvKQH9qzEvsRk9snlR5Y8iVJ3j086jQ3a9p9V/QckWt7wVYcQZ03/Pg/j5n66PNMRNyorrtUa..8QHi/Pnu2A"}, 71 | {"abcdefghijkl", "$pbkdf2-sha512$25000$nnNuba0VImQsJaT0/p.T8g$2HKbjVtAKSQVom8tiOzcd7RkqxefcCuxt9Y8zmwe2Lds6AvhytxAiOunHX2XE5u5LVnVsblcDvMjvvOlJ2Louw"}, 72 | {"abcdefghijklm", "$pbkdf2-sha512$25000$A2AsReh9zzmHUOqd894bAw$HwnOfFfIaabQ5WeehDfxq5RLPCnXfK9FrxmLESF.dy7PRNyFiJ1HPOBTFVa9I1rMiEKJCy0kzZuhW8IlOJbwsw"}, 73 | {"abcdefghijklmn", "$pbkdf2-sha512$25000$HsM45zzHGMO4995bizFmDA$DG9b4H10vBG.1kdu0pP6RHEeem3/KMCIPKGpKjHhjyWyricpqxPcXZD2ZtcTjQtnuIHhoauIJFHLQXnEOBwC5Q"}, 74 | {"abcdefghijklmno", "$pbkdf2-sha512$25000$Mab0HsOYM0boXct5r7U2xg$zWyjCu4pIvhPv65xgGRCfU2I3GuF0wWHXCpvvdTDIzmY.stNRWFZ1GncCz4G0NzNsINBgCkoHsZQIkJr6Ca4UQ"}, 75 | {"abcdefghijklmnop", "$pbkdf2-sha512$25000$O4fwPmdMyRmDUIrx/h9jTA$Xlp267ZwEbG4aOpN3Bve/ATo3rFA7WH8iMdS16Xbe9rc6P5welk1yiXEMPy7.BFp0qsncipHumaW1trCWVvq/A"}, 76 | {"qrstuvwxyz012345", "$pbkdf2-sha512$25000$XUvJmdNaK8V4z5mTkhICQA$oPcqbiUfaM/IzPcbEEDl9LUMOegfq65fOzdsYUUxU44ops2k91yCprer94a9xXNLSnRRvsZ65wyodFGqnfZ.Pg"}, 77 | {"67890./", "$pbkdf2-sha512$25000$sPZey1lr7T0nZAwBoFTKWQ$y4Y.OYBwnBO6JGP3C5XjipvwBNanCIQUoa1HL2zaU7b3YsoZ5hKOLdTkDjoup2NVOmJQ1QosgkeLGuycm1vXEw"}, 78 | {"ABCDEFGHIJKLMNOP", "$pbkdf2-sha512$25000$7f0/R4jRGmNMidF6713L2Q$XFlwZrQhqT1xZzZKm9YS5FgRkrGvFeToU5V.Bdxhm9ROXWHOoSgRV2bUrRvAAh.9Ob7TKPvZ1ERxjBLH2Chc4Q"}, 79 | {"QRSTUVWXYZ012345", "$pbkdf2-sha512$25000$k/K.V4px7p3zvneuVcoZ4w$u8s2Co25ybjqZHhtEeio10ksQ/Tvo.wYoLNbTfwjGF4gXq3xY.mULeH6jVOxjP7bZv0qMaO79FQ3maXAoo.Yww"}, 80 | } 81 | 82 | func TestPBKDF2_SHA1(t *testing.T) { 83 | var crypter = SHA1Crypter 84 | var test_hashes = test_sha1 85 | 86 | { 87 | ok := crypter.SupportsStub(test_hashes[0].hash) 88 | if !ok { 89 | t.Errorf("crypter reports not support valid stub") 90 | } 91 | 92 | ok = crypter.SupportsStub("$pbkdf234") // this isn't valid 93 | if ok { 94 | t.Errorf("crypter reports supporting invalid stub") 95 | } 96 | } 97 | 98 | { 99 | // do a simple test of hashing and verifying within Go 100 | hash, err := crypter.Hash("helloworld") 101 | if err != nil { 102 | t.Errorf("recieved error whilst hashing password: %v\n", err) 103 | } 104 | 105 | // should be valid 106 | err = crypter.Verify("helloworld", hash) 107 | if err != nil { 108 | t.Errorf("valid password not accepted: %v\n", err) 109 | } 110 | 111 | // should not be valid 112 | err = crypter.Verify("goodbyeuniverse", hash) 113 | if err == nil { 114 | t.Errorf("invalid password accepted\n") 115 | } 116 | } 117 | 118 | { 119 | // run through some python passlib generated passwords and verify them to ensure cross comparability 120 | for _, test := range test_hashes { 121 | err := crypter.Verify(test.password, test.hash) 122 | if err != nil { 123 | t.Errorf("unable to verify password %s: %v", test.password, err) 124 | } 125 | } 126 | } 127 | } 128 | 129 | func TestPBKDF2_SHA256(t *testing.T) { 130 | var crypter = SHA256Crypter 131 | var test_hashes = test_sha256 132 | 133 | { 134 | ok := crypter.SupportsStub(test_hashes[0].hash) 135 | if !ok { 136 | t.Errorf("crypter reports not support valid stub") 137 | } 138 | 139 | ok = crypter.SupportsStub("$pbkdf234") // this isn't valid 140 | if ok { 141 | t.Errorf("crypter reports supporting invalid stub") 142 | } 143 | } 144 | 145 | { 146 | // do a simple test of hashing and verifying within Go 147 | hash, err := crypter.Hash("helloworld") 148 | if err != nil { 149 | t.Errorf("recieved error whilst hashing password: %v\n", err) 150 | } 151 | 152 | // should be valid 153 | err = crypter.Verify("helloworld", hash) 154 | if err != nil { 155 | t.Errorf("valid password not accepted: %v\n", err) 156 | } 157 | 158 | // should not be valid 159 | err = crypter.Verify("goodbyeuniverse", hash) 160 | if err == nil { 161 | t.Errorf("invalid password accepted\n") 162 | } 163 | } 164 | 165 | { 166 | // run through some python passlib generated passwords and verify them to ensure cross comparability 167 | for _, test := range test_hashes { 168 | err := crypter.Verify(test.password, test.hash) 169 | if err != nil { 170 | t.Errorf("unable to verify password %s: %v", test.password, err) 171 | } 172 | } 173 | } 174 | } 175 | 176 | func TestPBKDF2_SHA512(t *testing.T) { 177 | var crypter = SHA512Crypter 178 | var test_hashes = test_sha512 179 | 180 | { 181 | ok := crypter.SupportsStub(test_hashes[0].hash) 182 | if !ok { 183 | t.Errorf("crypter reports not support valid stub") 184 | } 185 | 186 | ok = crypter.SupportsStub("$pbkdf234") // this isn't valid 187 | if ok { 188 | t.Errorf("crypter reports supporting invalid stub") 189 | } 190 | } 191 | 192 | { 193 | // do a simple test of hashing and verifying within Go 194 | hash, err := crypter.Hash("helloworld") 195 | if err != nil { 196 | t.Errorf("recieved error whilst hashing password: %v\n", err) 197 | } 198 | 199 | // should be valid 200 | err = crypter.Verify("helloworld", hash) 201 | if err != nil { 202 | t.Errorf("valid password not accepted: %v\n", err) 203 | } 204 | 205 | // should not be valid 206 | err = crypter.Verify("goodbyeuniverse", hash) 207 | if err == nil { 208 | t.Errorf("invalid password accepted\n") 209 | } 210 | } 211 | 212 | { 213 | // run through some python passlib generated passwords and verify them to ensure cross comparability 214 | for _, test := range test_hashes { 215 | err := crypter.Verify(test.password, test.hash) 216 | if err != nil { 217 | t.Errorf("unable to verify password %s: %v", test.password, err) 218 | } 219 | } 220 | } 221 | } 222 | 223 | func BenchmarkPBDF2_SHA1_Hash(b *testing.B) { 224 | var crypter = SHA1Crypter 225 | const passwd = "benchmarkMeThis!!" 226 | 227 | for i := 0; i < b.N; i++ { 228 | crypter.Hash(passwd) 229 | } 230 | } 231 | 232 | func BenchmarkPBDF2_SHA1_Verify(b *testing.B) { 233 | var crypter = SHA1Crypter 234 | const passwd = "benchmarkMeThis!!" 235 | var hash, _ = crypter.Hash(passwd) 236 | 237 | for i := 0; i < b.N; i++ { 238 | crypter.Verify(passwd, hash) 239 | } 240 | } 241 | 242 | func BenchmarkPBDF2_SHA256_Hash(b *testing.B) { 243 | var crypter = SHA256Crypter 244 | const passwd = "benchmarkMeThis!!" 245 | 246 | for i := 0; i < b.N; i++ { 247 | crypter.Hash(passwd) 248 | } 249 | } 250 | 251 | func BenchmarkPBDF2_SHA256_Verify(b *testing.B) { 252 | var crypter = SHA256Crypter 253 | const passwd = "benchmarkMeThis!!" 254 | var hash, _ = crypter.Hash(passwd) 255 | 256 | for i := 0; i < b.N; i++ { 257 | crypter.Verify(passwd, hash) 258 | } 259 | } 260 | 261 | func BenchmarkPBDF2_SHA512_Hash(b *testing.B) { 262 | var crypter = SHA512Crypter 263 | const passwd = "benchmarkMeThis!!" 264 | 265 | for i := 0; i < b.N; i++ { 266 | crypter.Hash(passwd) 267 | } 268 | } 269 | 270 | func BenchmarkPBDF2_SHA512_Verify(b *testing.B) { 271 | var crypter = SHA512Crypter 272 | const passwd = "benchmarkMeThis!!" 273 | var hash, _ = crypter.Hash(passwd) 274 | 275 | for i := 0; i < b.N; i++ { 276 | crypter.Verify(passwd, hash) 277 | } 278 | } 279 | -------------------------------------------------------------------------------- /passlib_test.go: -------------------------------------------------------------------------------- 1 | package passlib 2 | 3 | import ( 4 | "testing" 5 | 6 | "gopkg.in/hlandau/passlib.v1/abstract" 7 | "gopkg.in/hlandau/passlib.v1/hash/argon2" 8 | "gopkg.in/hlandau/passlib.v1/hash/bcrypt" 9 | "gopkg.in/hlandau/passlib.v1/hash/bcryptsha256" 10 | "gopkg.in/hlandau/passlib.v1/hash/scrypt" 11 | "gopkg.in/hlandau/passlib.v1/hash/sha2crypt" 12 | ) 13 | 14 | //import "gopkg.in/hlandau/passlib.v1/hash/scrypt" 15 | 16 | func TestPasslib(t *testing.T) { 17 | for _, scheme := range DefaultSchemes { 18 | //t.Logf("scheme: %+v\n", scheme) 19 | c := Context{Schemes: []abstract.Scheme{scheme}} 20 | 21 | h, err := c.Hash("password") 22 | if err != nil { 23 | t.Fatalf("err: %v", err) 24 | } 25 | 26 | newHash, err := c.Verify("password", h) 27 | if err != nil { 28 | t.Fatalf("err verifying: %v (%#v)", err, h) 29 | } 30 | if newHash != "" { 31 | t.Fatalf("non-empty newHash with hash just created") 32 | } 33 | 34 | newHash, err = c.Verify("password2", h) 35 | if err == nil { 36 | t.Fatalf("got nil error with wrong password") 37 | } 38 | if newHash != "" { 39 | t.Fatalf("non-empty newHash with wrong password") 40 | } 41 | } 42 | } 43 | 44 | func TestDefault(t *testing.T) { 45 | h, err := Hash("password") 46 | if err != nil { 47 | t.Fatalf("err: %v", err) 48 | } 49 | 50 | newHash, err := Verify("password", h) 51 | if err != nil { 52 | t.Fatalf("err verifying: %v (%#v)", err, h) 53 | } 54 | 55 | if newHash != "" { 56 | t.Fatalf("unexpected upgrade") 57 | } 58 | newHash, err = Verify("foobar", "$s2$16384$8$1$qa9lVfhmTE8F2Jpwya9m7uoE$Q7dSPqhZQCLWpjniaz7RVm+xorpSAPTvOCP2uoZmoiI=") 59 | //$argon2i$v=19$m=32768,t=4,p=4$c29tZXNhbHRzb21lYWxrdA$HcTlbOnOAzJ2dUrlgHnNwC0yallJ/Gl2NbAWqg4IukA") 60 | if err != nil { 61 | t.Fatalf("err verifying known good: %v", err) 62 | } 63 | 64 | if newHash != "" { 65 | t.Fatalf("unexpected upgrade") 66 | } 67 | 68 | // Now test new defaults. 69 | UseDefaults(Defaults20180601) 70 | 71 | newHash, err = Verify("foobar", "$argon2i$v=19$m=32768,t=4,p=4$c29tZXNhbHRzb21lYWxrdA$HcTlbOnOAzJ2dUrlgHnNwC0yallJ/Gl2NbAWqg4IukA") 72 | if err != nil { 73 | t.Fatalf("err verifying known good: %v", err) 74 | } 75 | 76 | if newHash != "" { 77 | t.Fatalf("unexpected upgrade") 78 | } 79 | 80 | // Switch back. 81 | UseDefaults(Defaults20160922) 82 | } 83 | 84 | func TestUpgrade(t *testing.T) { 85 | c := Context{Schemes: DefaultSchemes[1:]} 86 | 87 | h, err := c.Hash("password") 88 | if err != nil { 89 | t.Fatalf("err: %v", err) 90 | } 91 | 92 | newHash, err := Verify("password", h) 93 | if err != nil { 94 | t.Fatalf("err verifying: %v (%#v)", err, h) 95 | } 96 | if newHash == "" { 97 | t.Fatalf("empty newHash when verifying deprecated hash") 98 | } 99 | 100 | newHash2, err := Verify("password", newHash) 101 | if err != nil { 102 | t.Fatalf("err verifying after upgrade: %v", err) 103 | } 104 | if newHash2 != "" { 105 | t.Fatalf("non-empty newHash after upgrade") 106 | } 107 | } 108 | 109 | func kat(t *testing.T, scheme abstract.Scheme, password, hash string) { 110 | c := Context{Schemes: []abstract.Scheme{scheme}} 111 | 112 | _, err := c.Verify(password, hash) 113 | 114 | if err != nil { 115 | t.Logf("err verifying known good hash: %v %s %s", scheme, password, hash) 116 | t.Fail() 117 | } 118 | 119 | _, err = c.Verify(" "+password, hash) 120 | if err == nil { 121 | t.Logf("invalid verification of known hash: %v", scheme) 122 | t.Fail() 123 | } 124 | } 125 | 126 | func TestKat(t *testing.T) { 127 | for _, v := range []struct{ p, h string }{ 128 | {"foobar", "$5$rounds=110000$J672cUm182wrK1bX$0TzjpY6NV07r82J9YebG50dZuwHoQWrny9Q7y6ceO7/"}, 129 | // From passlib 1.6: from JTR 1.7.9 130 | {"U*U*U*U*", "$5$LKO/Ute40T3FNF95$U0prpBQd4PloSGU0pnpM4z9wKn4vZ1.jsrzQfPqxph9"}, 131 | {"U*U***U", "$5$LKO/Ute40T3FNF95$fdgfoJEBoMajNxCv3Ru9LyQ0xZgv0OBMQoq80LQ/Qd."}, 132 | {"U*U***U*", "$5$LKO/Ute40T3FNF95$8Ry82xGnnPI/6HtFYnvPBTYgOL23sdMXn8C29aO.x/A"}, 133 | {"*U*U*U*U", "$5$9mx1HkCz7G1xho50$O7V7YgleJKLUhcfk9pgzdh3RapEaWqMtEp9UUBAKIPA"}, 134 | {"", "$5$kc7lRD1fpYg0g.IP$d7CMTcEqJyTXyeq8hTdu/jB/I6DGkoo62NXbHIR7S43"}, 135 | // From passlib 1.6: custom tests 136 | {"", "$5$rounds=10428$uy/jIAhCetNCTtb0$YWvUOXbkqlqhyoPMpN8BMe.ZGsGx2aBvxTvDFI613c3"}, 137 | {" ", "$5$rounds=10376$I5lNtXtRmf.OoMd8$Ko3AI1VvTANdyKhBPavaRjJzNpSatKU6QVN9uwS9MH."}, 138 | {"test", "$5$rounds=11858$WH1ABM5sKhxbkgCK$aTQsjPkz0rBsH3lQlJxw9HDTDXPKBxC0LlVeV69P.t1"}, 139 | {"Compl3X AlphaNu3meric", "$5$rounds=10350$o.pwkySLCzwTdmQX$nCMVsnF3TXWcBPOympBUUSQi6LGGloZoOsVJMGJ09UB"}, 140 | {"4lpHa N|_|M3r1K W/ Cur5Es: #$%(*)(*%#", "$5$rounds=11944$9dhlu07dQMRWvTId$LyUI5VWkGFwASlzntk1RLurxX54LUhgAcJZIt0pYGT7"}, 141 | {"with unic\u00D6de", "$5$rounds=1000$IbG0EuGQXw5EkMdP$LQ5AfPf13KufFsKtmazqnzSGZ4pxtUNw3woQ.ELRDF4"}, 142 | // From passlib 1.6: more tests 143 | {"secret", "$5$rounds=1004$nacl$oiWPbm.kQ7.jTCZoOtdv7/tO5mWv/vxw5yTqlBagVR7"}, 144 | {"secret", "$5$rounds=1005$nacl$6Mo/TmGDrXxg.bMK9isRzyWH3a..6HnSVVsJMEX7ud/"}, 145 | {"secret", "$5$rounds=1006$nacl$I46VwuAiUBwmVkfPFakCtjVxYYaOJscsuIeuZLbfKID"}, 146 | {"secret", "$5$rounds=1007$nacl$9fY4j1AV3N/dV/YMUn1enRHKH.7nEL4xf1wWB6wfDD4"}, 147 | {"secret", "$5$rounds=1008$nacl$CiFWCfn8ODmWs0I1xAdXFo09tM8jr075CyP64bu3by9"}, 148 | {"secret", "$5$rounds=1009$nacl$QtpFX.CJHgVQ9oAjVYStxAeiU38OmFILWm684c6FyED"}, 149 | {"secret", "$5$rounds=1010$nacl$ktAwXuT5WbjBW/0ZU1eNMpqIWY1Sm4twfRE1zbZyo.B"}, 150 | {"secret", "$5$rounds=1011$nacl$QJWLBEhO9qQHyMx4IJojSN9sS41P1Yuz9REddxdO721"}, 151 | {"secret", "$5$rounds=1012$nacl$mmf/k2PkbBF4VCtERgky3bEVavmLZKFwAcvxD1p3kV2"}, 152 | } { 153 | kat(t, sha2crypt.Crypter256, v.p, v.h) //"foobar", "$5$rounds=110000$J672cUm182wrK1bX$0TzjpY6NV07r82J9YebG50dZuwHoQWrny9Q7y6ceO7/") 154 | } 155 | 156 | for _, v := range []struct{ p, h string }{ 157 | {"foobar", "$6$rounds=100000$Xp12SciZHbjtt67a$RE2cT9MkPR2GFq0rw2ADNIHvIqmj7EFL3K0d2ASe9bub5ANv8Xa4y6pm78pkAPcXoq0zJmSyIc7pqlioTuCdq/"}, 158 | // From passlib 1.6: from JTR 1.7.9 159 | {"U*U*U*U*", "$6$LKO/Ute40T3FNF95$6S/6T2YuOIHY0N3XpLKABJ3soYcXD9mB7uVbtEZDj/LNscVhZoZ9DEH.sBciDrMsHOWOoASbNLTypH/5X26gN0"}, 160 | {"U*U***U", "$6$LKO/Ute40T3FNF95$wK80cNqkiAUzFuVGxW6eFe8J.fSVI65MD5yEm8EjYMaJuDrhwe5XXpHDJpwF/kY.afsUs1LlgQAaOapVNbggZ1"}, 161 | {"U*U***U*", "$6$LKO/Ute40T3FNF95$YS81pp1uhOHTgKLhSMtQCr2cDiUiN03Ud3gyD4ameviK1Zqz.w3oXsMgO6LrqmIEcG3hiqaUqHi/WEE2zrZqa/"}, 162 | {"*U*U*U*U", "$6$OmBOuxFYBZCYAadG$WCckkSZok9xhp4U1shIZEV7CCVwQUwMVea7L3A77th6SaE9jOPupEMJB.z0vIWCDiN9WLh2m9Oszrj5G.gt330"}, 163 | {"", "$6$ojWH1AiTee9x1peC$QVEnTvRVlPRhcLQCk/HnHaZmlGAAjCfrAN0FtOsOnUk5K5Bn/9eLHHiRzrTzaIKjW9NTLNIBUCtNVOowWS2mN."}, 164 | // From passilb 1.6: custom tests 165 | {"", "$6$rounds=11021$KsvQipYPWpr93wWP$v7xjI4X6vyVptJjB1Y02vZC5SaSijBkGmq1uJhPr3cvqvvkd42Xvo48yLVPFt8dvhCsnlUgpX.//Cxn91H4qy1"}, 166 | {" ", "$6$rounds=11104$ED9SA4qGmd57Fq2m$q/.PqACDM/JpAHKmr86nkPzzuR5.YpYa8ZJJvI8Zd89ZPUYTJExsFEIuTYbM7gAGcQtTkCEhBKmp1S1QZwaXx0"}, 167 | {"test", "$6$rounds=11531$G/gkPn17kHYo0gTF$Kq.uZBHlSBXyzsOJXtxJruOOH4yc0Is13uY7yK0PvAvXxbvc1w8DO1RzREMhKsc82K/Jh8OquV8FZUlreYPJk1"}, 168 | {"Compl3X AlphaNu3meric", "$6$rounds=10787$wakX8nGKEzgJ4Scy$X78uqaX1wYXcSCtS4BVYw2trWkvpa8p7lkAtS9O/6045fK4UB2/Jia0Uy/KzCpODlfVxVNZzCCoV9s2hoLfDs/"}, 169 | {"4lpHa N|_|M3r1K W/ Cur5Es: #$%(*)(*%#", "$6$rounds=11065$5KXQoE1bztkY5IZr$Jf6krQSUKKOlKca4hSW07MSerFFzVIZt/N3rOTsUgKqp7cUdHrwV8MoIVNCk9q9WL3ZRMsdbwNXpVk0gVxKtz1"}, 170 | // From passlib 1.6: ensures UTF-8 used for unicode 171 | {"t\u00e1\u0411\u2113\u0259", "$6$rounds=40000$PEZTJDiyzV28M3.m$GTlnzfzGB44DGd1XqlmC4erAJKCP.rhvLvrYxiT38htrNzVGBnplFOHjejUGVrCfusGWxLQCc3pFO0A/1jYYr0"}, 172 | } { 173 | kat(t, sha2crypt.Crypter512, v.p, v.h) 174 | } 175 | 176 | for _, v := range []struct{ p, h string }{ 177 | {"foobar", "$2a$12$R7THiKSJilzQRPcvtUCSu.WI.N3gT2TY5BxjTp6EDnPy40Sn84m4K"}, 178 | // From passlib 1.6: from JTR 1.7.9 179 | {"U*U*U*U*", "$2a$05$c92SVSfjeiCD6F2nAD6y0uBpJDjdRkt0EgeC4/31Rf2LUZbDRDE.O"}, 180 | {"U*U***U", "$2a$05$WY62Xk2TXZ7EvVDQ5fmjNu7b0GEzSzUXUh2cllxJwhtOeMtWV3Ujq"}, 181 | {"U*U***U*", "$2a$05$Fa0iKV3E2SYVUlMknirWU.CFYGvJ67UwVKI1E2FP6XeLiZGcH3MJi"}, 182 | {"*U*U*U*U", "$2a$05$.WRrXibc1zPgIdRXYfv.4uu6TD1KWf0VnHzq/0imhUhuxSxCyeBs2"}, 183 | {"", "$2a$05$Otz9agnajgrAe0.kFVF9V.tzaStZ2s1s4ZWi/LY4sw2k/MTVFj/IO"}, 184 | // From passlib 1.6: test vectors from http://www.openwall.com/crypt v1.2 185 | // Note that this omits any hashes that depend on crypt_blowfish's various 186 | // CVE-2011-2483 workarounds (hash 2a and \xFF\xFF in password, and any 2x 187 | // hashes); and only contain hashes which are correct under both 188 | // crypt_blowfish 1.2 AND OpenBSD. 189 | {"U*U", "$2a$05$CCCCCCCCCCCCCCCCCCCCC.E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW"}, 190 | {"U*U*", "$2a$05$CCCCCCCCCCCCCCCCCCCCC.VGOzA784oUp/Z0DY336zx7pLYAy0lwK"}, 191 | {"U*U*U", "$2a$05$XXXXXXXXXXXXXXXXXXXXXOAcXxm9kjPGEMsLznoKqmqw7tc8WCx4a"}, 192 | {"", "$2a$05$CCCCCCCCCCCCCCCCCCCCC.7uG0VCzI2bS7j6ymqJi9CdcdxiRTWNy"}, 193 | {"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789chars after 72 are ignored", 194 | "$2a$05$abcdefghijklmnopqrstuu5s2v8.iXieOjg/.AySBTTZIIVFJeBui"}, 195 | {"\xa3", "$2a$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq"}, 196 | {"\xff\xa3345", "$2a$05$/OK.fbVrR/bpIqNJ5ianF.nRht2l/HRhr6zmCp9vYUvvsqynflf9e"}, 197 | {"\xa3ab", "$2a$05$/OK.fbVrR/bpIqNJ5ianF.6IflQkJytoRVc1yuaNtHfiuq.FRlSIS"}, 198 | {"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaachars after 72 are ignored as usual", "$2a$05$/OK.fbVrR/bpIqNJ5ianF.swQOIzjOiJ9GHEPuhEkvqrUyvWhEMx6"}, 199 | {"\xaaU\xaaU\xaaU\xaaU\xaaU\xaaU\xaaU\xaaU\xaaU\xaaU\xaaU\xaaU\xaaU\xaaU\xaaU\xaaU\xaaU\xaaU\xaaU\xaaU\xaaU\xaaU\xaaU\xaaU\xaaU\xaaU\xaaU\xaaU\xaaU\xaaU\xaaU\xaaU\xaaU\xaaU\xaaU\xaaU", "$2a$05$/OK.fbVrR/bpIqNJ5ianF.R9xrDjiycxMbQE2bp.vgqlYpW5wx2yy"}, 200 | {"U\xaa\xffU\xaa\xffU\xaa\xffU\xaa\xffU\xaa\xffU\xaa\xffU\xaa\xffU\xaa\xffU\xaa\xffU\xaa\xffU\xaa\xffU\xaa\xffU\xaa\xffU\xaa\xffU\xaa\xffU\xaa\xffU\xaa\xffU\xaa\xffU\xaa\xffU\xaa\xffU\xaa\xffU\xaa\xffU\xaa\xffU\xaa\xff", "$2a$05$/OK.fbVrR/bpIqNJ5ianF.9tQZzcJfm3uj2NvJ/n5xkhpqLrMpWCe"}, 201 | // From passlib 1.6: Keeping one of their 2y tests, because we are supporting that. 202 | {"\xa3", "$2y$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq"}, 203 | // From passlib 1.6: BSD wraparound bug (fixed in 2b) 204 | {"01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123", "$2a$04$R1lJ2gkNaoPGdafE.H.16.1MKHPvmKwryeulRe225LKProWYwt9Oi"}, 205 | {"012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234", "$2a$04$R1lJ2gkNaoPGdafE.H.16.1MKHPvmKwryeulRe225LKProWYwt9Oi"}, 206 | {"0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345", "$2a$04$R1lJ2gkNaoPGdafE.H.16.1MKHPvmKwryeulRe225LKProWYwt9Oi"}, 207 | {"01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456", "$2a$04$R1lJ2gkNaoPGdafE.H.16.1MKHPvmKwryeulRe225LKProWYwt9Oi"}, 208 | // From passlib 1.6: From py-bcrypt tests 209 | {"", "$2a$06$DCq7YPn5Rq63x1Lad4cll.TV4S6ytwfsfvkgY8jIucDrjc8deX1s."}, 210 | {"a", "$2a$10$k87L/MF28Q673VKh8/cPi.SUl7MU/rWuSiIDDFayrKk/1tBsSQu4u"}, 211 | {"abc", "$2a$10$WvvTPHKwdBJ3uk0Z37EMR.hLA2W6N9AEBhEgrAOljy2Ae5MtaSIUi"}, 212 | {"abcdefghijklmnopqrstuvwxyz", "$2a$10$fVH8e28OQRj9tqiDXs1e1uxpsjN0c7II7YPKXua2NAKYvM6iQk7dq"}, 213 | {"~!@#$%^&*() ~!@#$%^&*()PNBFRD", "$2a$10$LgfYWkbzEvQ4JakH7rOvHe0y8pHKF9OaFgwUZ2q7W2FFZmZzJYlfS"}, 214 | // From passlib 1.6: Ensures UTF-8 used for unicode 215 | {"t\u00E1\u0411\u2113\u0259", "$2a$05$Z17AXnnlpzddNUvnC6cZNOSwMA/8oNiKnHTHTwLlBijfucQQlHjaG"}, 216 | // From passlib 1.6: Ensure 2b support 217 | {"t\u00E1\u0411\u2113\u0259", "$2b$05$Z17AXnnlpzddNUvnC6cZNOSwMA/8oNiKnHTHTwLlBijfucQQlHjaG"}, 218 | } { 219 | kat(t, bcrypt.Crypter, v.p, v.h) 220 | } 221 | 222 | for _, v := range []struct{ p, h string }{ 223 | {"foobar", "$bcrypt-sha256$2a,12$rruXEyrqlhdwQf0tc75cyu$CI2KZzhhCtymN3OvZKF2axF4aJUq4x6"}, 224 | 225 | // From passlib 1.6 226 | {"", "$bcrypt-sha256$2a,5$E/e/2AOhqM5W/KJTFQzLce$F6dYSxOdAEoJZO2eoHUZWZljW/e0TXO"}, 227 | {"password", "$bcrypt-sha256$2a,5$5Hg1DKFqPE8C2aflZ5vVoe$12BjNE0p7axMg55.Y/mHsYiVuFBDQyu"}, 228 | {"t\u00E1\u0411\u2113\u0259", "$bcrypt-sha256$2a,5$.US1fQ4TQS.ZTz/uJ5Kyn.$QNdPDOTKKT5/sovNz1iWg26quOU4Pje"}, 229 | {"abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123", "$bcrypt-sha256$2a,5$X1g1nh3g0v4h6970O68cxe$r/hyEtqJ0teqPEmfTLoZ83ciAI1Q74."}, 230 | {"abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123qwr", "$bcrypt-sha256$2a,5$X1g1nh3g0v4h6970O68cxe$021KLEif6epjot5yoxk0m8I0929ohEa"}, 231 | {"abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123xyz", "$bcrypt-sha256$2a,5$X1g1nh3g0v4h6970O68cxe$7.1kgpHduMGEjvM3fX6e/QCvfn6OKja"}, 232 | } { 233 | kat(t, bcryptsha256.Crypter, v.p, v.h) 234 | } 235 | 236 | for _, v := range []struct{ p, h string }{ 237 | {"", "$s2$16384$8$1$5KHwLMZjMDiuPAhUYK/XcKZW$KZIGWg5XM1Xsh8X/wuBE1+KTeFImkuQn3gZpjUZcqns="}, 238 | {"foobar", "$s2$16384$8$1$qa9lVfhmTE8F2Jpwya9m7uoE$Q7dSPqhZQCLWpjniaz7RVm+xorpSAPTvOCP2uoZmoiI="}, 239 | } { 240 | kat(t, scrypt.SHA256Crypter, v.p, v.h) 241 | } 242 | 243 | for _, v := range []struct{ p, h string }{ 244 | {"", "$argon2i$v=19$m=32768,t=4,p=4$XEfcwb81UQKSzIcxVEIgrw$1lAPOhgJpGJEgGSKxdnd3n3F9S5qPZSf53iKM1/SvTk"}, 245 | {"foobar", "$argon2i$v=19$m=32768,t=4,p=4$uN6vgPBb8/liQld8lgFqew$KlvqGCHX7Cap0ohKY7YAUJsbzcnenCwvSAfhqtIA/Q0"}, 246 | } { 247 | kat(t, argon2.Crypter, v.p, v.h) 248 | } 249 | } 250 | 251 | // © 2008-2012 Assurance Technologies LLC. (Python passlib) BSD License 252 | // © 2014 Hugo Landau BSD License 253 | -------------------------------------------------------------------------------- /hash/sha2crypt/raw/sha2crypt_test.go: -------------------------------------------------------------------------------- 1 | package raw 2 | 3 | import "testing" 4 | import "fmt" 5 | 6 | type test struct { 7 | password string 8 | salt string 9 | rounds int 10 | output string 11 | } 12 | 13 | var tests = []test{ 14 | {"", "", 5000, "$5$$3c2QQ0KjIU1OLtB29cl8Fplc2WN7X89bnoEjaR7tWu."}, 15 | {"", "a", 5000, "$5$a$CZ9Csdk0HaS3TQcxgDHTwM2gwOEDCViPn83i6BpFdH."}, 16 | {"", "ab", 5000, "$5$ab$qRq/oZkUn9lJUSIcgVT3NbpSDsjn.r137TBSoCt2kCA"}, 17 | {"", "abc", 5000, "$5$abc$bBHLwRRW2Li0XKaX13kz/g2fkDil4Jx46aNvd.48MS8"}, 18 | {"", "abcd", 5000, "$5$abcd$QS/LhMXxIihGHtawbvVF6A.fmwyLe5V/ymwcRAd7aR7"}, 19 | {"", "abcde", 5000, "$5$abcde$W5y2trSo8TzQutkR2rkKOZNWkb5.R1HL8Xl1lPpegV1"}, 20 | {"", "abcdef", 5000, "$5$abcdef$Rlu/pHRATvgAqn9Ro3bJf.ZEh1ESHNclTgb9r7gcIjD"}, 21 | {"", "abcdefg", 5000, "$5$abcdefg$2DdlZT6BJat7DGJPj6Y2iUPenow9IrC7DFc9IhiMi23"}, 22 | {"", "abcdefgh", 5000, "$5$abcdefgh$mnv0N8gJGuJiQCFVlADjwwxPyRiUO8rGuljjETTLqw9"}, 23 | {"", "abcdefghi", 5000, "$5$abcdefghi$UM73isk8bmbqvGDwvoVb1d6FCC00r02S6e0ZRW5K51A"}, 24 | {"", "abcdefghij", 5000, "$5$abcdefghij$Hohrbaiwnq1C8IvcLkwiwoIvNNABi8X2tI3emOu/zRD"}, 25 | {"", "abcdefghijk", 5000, "$5$abcdefghijk$f5T280uHjpcYnNtXRAeD4ZkPSzktmneYag4O3nAs3m5"}, 26 | {"", "abcdefghijkl", 5000, "$5$abcdefghijkl$M6i7lHXjEAetOBMIifrw/a8O66lffiMfYu.0RbJJqg5"}, 27 | {"", "abcdefghijklm", 5000, "$5$abcdefghijklm$mdxEtAWpQzbySWfztql/vP3zTyZ4kPWdWJvnIpXMUg/"}, 28 | {"", "abcdefghijklmn", 5000, "$5$abcdefghijklmn$03PjbB.9k1U5CG4E9g1k.8iBJa2rmjDrQGUnIH.n6DB"}, 29 | {"", "abcdefghijklmno", 5000, "$5$abcdefghijklmno$r7JUILjL2Vz5.sNbeZVQ55/uYEHk6AtJUQhZJi92pW2"}, 30 | {"", "abcdefghijklmnop", 5000, "$5$abcdefghijklmnop$p99E2fxZB/BTl9j.a2VRY5z71zEP761isnVBuiGlzV3"}, 31 | {"", "qrstuvwxyz012345", 5000, "$5$qrstuvwxyz012345$l8P4SKmrN7jU8ToRpDYNuNim5zSjkm.aVXwCO6WuW81"}, 32 | {"", "67890./", 5000, "$5$67890./$warGVxms4I96esLrgWuWecxitqMuUz0UUzNzOUaRTHD"}, 33 | {"", "ABCDEFGHIJKLMNOP", 5000, "$5$ABCDEFGHIJKLMNOP$OHECpAsJdteNx4ouLLv3xuJdToxDN1xrgHLUxCQ1Ot1"}, 34 | {"", "QRSTUVWXYZ012345", 5000, "$5$QRSTUVWXYZ012345$tgHMWnYMadERx.6xcQq9sHH3BrFXkCuIs5hkZ5N3OPD"}, 35 | {"", "a", 1000, "$5$rounds=1000$a$h8gEN0ODxYvLokgp44mp9FQeMZy0f9j01cbNDQnD727"}, 36 | {"", "a", 1001, "$5$rounds=1001$a$bCioE1DDKVxgfDe/fK/DEzMMMtZ9xupUa1kTFXSsA6C"}, 37 | {"", "a", 1002, "$5$rounds=1002$a$1jMK4X/VTZWDjTD3hzLqFP.8Dvg8IIxwgZo4IO1w73A"}, 38 | {"password", "a", 5000, "$5$a$x8ssIx34C.IRiXo29UURs5AcnNhdWP3dXGeLtS3KoBB"}, 39 | {"", "a", 5000, "$5$a$CZ9Csdk0HaS3TQcxgDHTwM2gwOEDCViPn83i6BpFdH."}, 40 | {"p", "a", 5000, "$5$a$Rsi04Q4ySMRbZwphTRYWj8iCLdcCCsBsGCIsTzF/nBC"}, 41 | {"pa", "a", 5000, "$5$a$NGVApMsYEnnBZcCI6eLl2Y96f6VVWba1V5KeJRNxIr3"}, 42 | {"pas", "a", 5000, "$5$a$GfZW.QTXt6LKucW2Z1ykqgP8WXeXLEyHkj7DOnQ2Vi2"}, 43 | {"pass", "a", 5000, "$5$a$HdNnpOmUtrzbfREdPzizZmBeIUmiBRBmwGPnsTpAOi."}, 44 | {"passw", "a", 5000, "$5$a$AVfXsZ8Dqyg25Obb..jbk..hJ9ukDqDgfPD.D/fC9LD"}, 45 | {"passwo", "a", 5000, "$5$a$3xKtgp0wS8UOJkV0jmSTevsi2fZJXydQlDUrcXxMb7D"}, 46 | {"passwor", "a", 5000, "$5$a$kdspjRhK1765HvXLxOQV2VRgiDr0tUv/8RafT9BkRBC"}, 47 | {"password", "a", 5000, "$5$a$x8ssIx34C.IRiXo29UURs5AcnNhdWP3dXGeLtS3KoBB"}, 48 | {"passwordp", "a", 5000, "$5$a$DnGBT354PVGNLCZ4VYS9.0qxSRqR5yipZimrzuBGmeC"}, 49 | {"passwordpa", "a", 5000, "$5$a$ifynU6HhW0zcrnRFUzaK1HskOFL1/kERzdPwT9vpHf6"}, 50 | {"passwordpas", "a", 5000, "$5$a$MK/aa8qQ0RoCC.GwvJsu3jBjYye3gfh85bHGDN6cue1"}, 51 | {"passwordpass", "a", 5000, "$5$a$Q9CSYYZGe1pZL.KsvU8rCm2wNUlDL5UKMGwi4KZ/RO4"}, 52 | {"passwordpassw", "a", 5000, "$5$a$7ym.mFfQJBrZMJRRfG.E3u1XdXd/WRFPSoeCfG13dI8"}, 53 | {"passwordpasswo", "a", 5000, "$5$a$2.jM1eIxx0/KCI1eJtxr4rwhfzcUinEIytH/1e94n3D"}, 54 | {"passwordpasswor", "a", 5000, "$5$a$5CBP5/5.fG15iPgiDjM.roGL58TLvPbJwmtyQoVqmi2"}, 55 | {"passwordpassword", "a", 5000, "$5$a$6aXe9PoMbuvHBOSpy1o99s3P0xOw6f3lDZH90R3L1f2"}, 56 | {"passwordpasswordp", "a", 5000, "$5$a$AC3YAq4d/84sa78yTIgpRpS8htNCHvwHAMCjlEP.DtD"}, 57 | {"passwordpasswordpa", "a", 5000, "$5$a$AUSVTHrIAl8mkgcGAYscGp5stE30MVK8U6foIoS5TIB"}, 58 | {"passwordpasswordpas", "a", 5000, "$5$a$MQJWP1q3N5ujR1ImGovShoC4QFrqwI8qdeTsnTW/2d8"}, 59 | {"passwordpasswordpass", "a", 5000, "$5$a$WQLgeB4s/wpflgYEANXSMjgsj/1UPMJ3//vD0bObX0B"}, 60 | {"passwordpasswordpassw", "a", 5000, "$5$a$FbIYhyxJ9dTMiIGOj/qGyFNFjhXuxg3nSOcwrc1NW/7"}, 61 | {"passwordpasswordpasswo", "a", 5000, "$5$a$5HfML8NMx7gjm.tsOvX9xk/ci6gwl1vOuIe8NnCBR38"}, 62 | {"passwordpasswordpasswor", "a", 5000, "$5$a$iGXe0BkVLjPmlA9qXAKqbbHyrSvFNq7KM7uZlI6ATeB"}, 63 | {"passwordpasswordpassword", "a", 5000, "$5$a$ADoN8xGmdy5YZMOHJ4dyh3XS36n/6UorDo8ILX7/ce1"}, 64 | {"passwordpasswordpasswordp", "a", 5000, "$5$a$Fi9ExS1OO5e9A78erBOgAJKbVShB96DTR8WonbZ././"}, 65 | {"passwordpasswordpasswordpa", "a", 5000, "$5$a$ESOgdVLaBOC6wt59ces51MsrjcIpu6oIFeseG4erIA."}, 66 | {"passwordpasswordpasswordpas", "a", 5000, "$5$a$OION6Mvg8cASIz3AxjTs06c.olW6vk9dP54zv9NWsP2"}, 67 | {"passwordpasswordpasswordpass", "a", 5000, "$5$a$lRfPLEWXNjdmysZ38qUZTjvDIUyGRWCfMe1g6ODyKX9"}, 68 | {"passwordpasswordpasswordpassw", "a", 5000, "$5$a$IA0fjfZtkWERkZYwwWeI0mchOv.0vCv/fkWIb4IQHI/"}, 69 | {"passwordpasswordpasswordpasswo", "a", 5000, "$5$a$Dfug2LT6shmy7vuBxzP77cYrzG/OtjtG/31Mh2ncay7"}, 70 | {"passwordpasswordpasswordpasswor", "a", 5000, "$5$a$UoMW82csgeMn2gMANJJ3MFK.sclNpANivbJoT9Jx5c3"}, 71 | {"passwordpasswordpasswordpassword", "a", 5000, "$5$a$DGqEndgJ1BnDS3PeMqkGaz.Wu63bVglCBf1/64x2nQ3"}, 72 | {"passwordpasswordpasswordpasswordp", "a", 5000, "$5$a$YXDTXLu7vw90x298TwlWTCysHU9NFuEXKOLs2zG88Y8"}, 73 | {"passwordpasswordpasswordpasswordpa", "a", 5000, "$5$a$Zjc.oRVCaAcRqCOdOzlzWGbEx../OuczSKufDWVwwyD"}, 74 | {"passwordpasswordpasswordpasswordpas", "a", 5000, "$5$a$5bfoOSohT29XbU8LW3lgDsklZf.rP6k0SvKt5k5drQD"}, 75 | {"passwordpasswordpasswordpasswordpass", "a", 5000, "$5$a$6FI0MfRY054P3Cxc5BKx64iCwQkng0HaXgcHMq.xLmC"}, 76 | {"passwordpasswordpasswordpasswordpassw", "a", 5000, "$5$a$xFgU8VdQxzX/hyaghlowfuIMwbplc8jWt0G2wW4f4R8"}, 77 | {"passwordpasswordpasswordpasswordpasswo", "a", 5000, "$5$a$DJMFVNaO6Ncchn7zlEOTodwBCvtUhGLXmpBVf424Xy8"}, 78 | {"passwordpasswordpasswordpasswordpasswor", "a", 5000, "$5$a$LxmLqMrA3AO.sFuc65kMe/4fnAGAVP2dufN1k45FiO6"}, 79 | {"passwordpasswordpasswordpasswordpassword", "a", 5000, "$5$a$ZDsiDUZL1gUsNGWyNGKCMzAnt2QMVXIECb47WHkRKaB"}, 80 | {"passwordpasswordpasswordpasswordpasswordp", "a", 5000, "$5$a$ibwJwx2ykaHM2GPoikj/ldCWNCIs.WRaZNFw6Azsu11"}, 81 | {"passwordpasswordpasswordpasswordpasswordpa", "a", 5000, "$5$a$vy8UVLoVwB.68TIZpslppp/wqlrqS95Pf5tl4FE41S5"}, 82 | {"passwordpasswordpasswordpasswordpasswordpas", "a", 5000, "$5$a$HOmJIgN.F5oDl4JcntyuMIg7VWUdhqG0ItoctTHxwT0"}, 83 | {"passwordpasswordpasswordpasswordpasswordpass", "a", 5000, "$5$a$1SvKzMJOGkCCodx9c7Hb3zmFJn7D5U6NNcDv2XKn1a6"}, 84 | {"passwordpasswordpasswordpasswordpasswordpassw", "a", 5000, "$5$a$XGUrQNPp3aHzUHjWKWag1WPm.bT.uGXohjJQX87Y9RB"}, 85 | {"passwordpasswordpasswordpasswordpasswordpasswo", "a", 5000, "$5$a$KpfsxhqWavFw3Y.zDiVQskgsgQBO9LMTvbIqMU.LPS3"}, 86 | {"passwordpasswordpasswordpasswordpasswordpasswor", "a", 5000, "$5$a$pYJ/Ih0MLYKjJ8JsF5XrJXDA6C6Ey5aKw5pMSuQyLA."}, 87 | {"passwordpasswordpasswordpasswordpasswordpassword", "a", 5000, "$5$a$FSOEL7KkLAsOrMW8/6qdW95lVE5SzftNw8kvFgvLZW0"}, 88 | {"passwordpasswordpasswordpasswordpasswordpasswordp", "a", 5000, "$5$a$U4CAjSRJEyQKUGUcd6LUIoYOh8PIvzCUmKUhhFc4qX7"}, 89 | {"passwordpasswordpasswordpasswordpasswordpasswordpa", "a", 5000, "$5$a$Bw.mc9gewl8Bv8YVP2OAjr1b4VptK0Z/6ngSeIt00d8"}, 90 | {"passwordpasswordpasswordpasswordpasswordpasswordpas", "a", 5000, "$5$a$as9mpD4oZJ45D8MigUqtcZyNg1PtAw2zfa6xDtm53l."}, 91 | {"passwordpasswordpasswordpasswordpasswordpasswordpass", "a", 5000, "$5$a$dy0bt87jdooWdr1yBx4EylgtRpckkwioxPCczgViZj8"}, 92 | {"passwordpasswordpasswordpasswordpasswordpasswordpassw", "a", 5000, "$5$a$wa7KwS.SI3rX6zy9PMjlSUt7iSQl9FxeHcR6Q4On18."}, 93 | {"passwordpasswordpasswordpasswordpasswordpasswordpasswo", "a", 5000, "$5$a$5xz9kR3L.cRN2HO6818NiwfNfL2UrGUvE2G1XM09Tr7"}, 94 | {"passwordpasswordpasswordpasswordpasswordpasswordpasswor", "a", 5000, "$5$a$7t2FJYlFYcs0EWBjDbOOIecnbeZ9FCxzkjEuLZQAq6D"}, 95 | {"passwordpasswordpasswordpasswordpasswordpasswordpassword", "a", 5000, "$5$a$l5ak/wtvCH.t0nzD76gkqo53xXjQhaDVFXOIOfBWo76"}, 96 | {"passwordpasswordpasswordpasswordpasswordpasswordpasswordp", "a", 5000, "$5$a$TmQ3d2DZDP4F3k0Qg4hzLjUigtWDpR8G4MKyscQmT3A"}, 97 | {"passwordpasswordpasswordpasswordpasswordpasswordpasswordpa", "a", 5000, "$5$a$rs3RBXuiFxN7WtBOK91z/1Dzj3HtwbXfF/cbG1KzBeD"}, 98 | {"passwordpasswordpasswordpasswordpasswordpasswordpasswordpas", "a", 5000, "$5$a$aDXAPeSLOV/frmuMhDDxQaI0uN0XyWmt3JIM.fVsNfD"}, 99 | {"passwordpasswordpasswordpasswordpasswordpasswordpasswordpass", "a", 5000, "$5$a$3YzMxqb.xRiaPJFp3YwWo8f7r0/lIj84PvjVVN8BIY3"}, 100 | {"passwordpasswordpasswordpasswordpasswordpasswordpasswordpassw", "a", 5000, "$5$a$ku/pr.KfD8xEZee/t5rpoCyolEbEJn2BVfRc1Am9xO/"}, 101 | {"passwordpasswordpasswordpasswordpasswordpasswordpasswordpasswo", "a", 5000, "$5$a$S1UjlBKykqg8ORL7c78PFAfLtwOpo63f8jo2JLuJZuC"}, 102 | {"passwordpasswordpasswordpasswordpasswordpasswordpasswordpasswor", "a", 5000, "$5$a$TgCskPPq6Px8if/orkdw.n03K1lkZtdnnGx4cYnKp1/"}, 103 | {"passwordpasswordpasswordpasswordpasswordpasswordpasswordpassword", "a", 5000, "$5$a$BjoYXDpjTvzlqtdEHoykxXVV5v/Xgs8MozCtGLopM.0"}, 104 | {"passwordpasswordpasswordpasswordpasswordpasswordpasswordpasswordp", "a", 5000, "$5$a$i3biI/0rZ8f7/29SKGkLzvv49yU./3/gJIAFrZSpC7B"}, 105 | {"passwordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpa", "a", 5000, "$5$a$9/JmGanXN/3/SLRwsUMMlN09QPX92zyFB.mvhLDxU5B"}, 106 | {"passwordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpas", "a", 5000, "$5$a$kCpgfUwpaioM791mWWUFRkAf.Fs2Hapl/Ab352ptZj."}, 107 | {"passwordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpass", "a", 5000, "$5$a$1X6iw/Ul2DdtYkMtZeymMNWD1va8Lmv3.dCXLlk2sx2"}, 108 | {"passwordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpassw", "a", 5000, "$5$a$ijIZ8IGcQTZMzfavUpa6o43YYLCZhl2APwN3UJzYxl/"}, 109 | } 110 | 111 | var tests512 = []test{ 112 | {"", "", 5000, "$6$$/chiBau24cE26QQVW3IfIe68Xu5.JQ4E8Ie7lcRLwqxO5cxGuBhqF2HmTL.zWJ9zjChg3yJYFXeGBQ2y3Ba1d1"}, 113 | {"", "a", 5000, "$6$a$Z6IPkDduHdwavesVY2NFMpOHkigshxPYdhfLg0D3A7/XFQcso9RV2E8qczyJ2su.dj6RRLaTpvgwN1rZfK0I9."}, 114 | {"", "ab", 5000, "$6$ab$xnh5Qsr2NdbFw1PgdZie7nLON3gv.S.23iQDBqAzkdoXPtDnVSpludXkM5UWybQO3OI7hBj9wHg9Ow6sUWD60/"}, 115 | {"", "abc", 5000, "$6$abc$mJP3a6FyA8uCnzRtlnNypPwjnvpi5TP9qOrInzrfDmwxUQG38PkpCPdqfTb8JQfAngapMxeim4AZ..hSdRRzD."}, 116 | {"", "abcd", 5000, "$6$abcd$NU/C5Gju0G9iuhwYGc8C4T7UNm5gyOGf7Xo/YAAExf5aQb28mUxIL2q/EzSmLYbkVO9uOhSV40MbPwn7aHHHz0"}, 117 | {"", "abcde", 5000, "$6$abcde$Lhdc0UuwkjZ5DQ/53BLdYd5GUrFucLIkkGfkueqGsc4oZk0fSB.lW5HNJpH3Ylz.L0QzyqfrhFBNyw3xPs0Lj."}, 118 | {"", "abcdef", 5000, "$6$abcdef$t8zWbEvsAsHfcR0cwTk7.B4mfyeBTxZLDx1BAyR7l78H89jWjwb7da5RMFOTqy7lod6pRidfXKxEJO8PX.PYO0"}, 119 | {"", "abcdefg", 5000, "$6$abcdefg$11VEP0wJAS.aNv1qWIR.Wqn38LpeUu5GpYeIj9UGfCDjlserGN1BRdkqMT23sfc36KUYON4xKPFsNBtxLdp0V0"}, 120 | {"", "abcdefgh", 5000, "$6$abcdefgh$v7sYNA18/BerGOYQLppYLyjH4yJilp8kqe/ef3KYMK9hOIdzH1yzcmP74Ay.m51y1jP3QqxM7Jl75S4CxDhBq."}, 121 | {"", "abcdefghi", 5000, "$6$abcdefghi$N3CEZsJRV9mbISNV5Fl6FFwL8RYkDymKraZl/wxIwD6ipErfVOTUtTkeOnNk9KwglqBRZBEe5QknOoj4HqBHW1"}, 122 | {"", "abcdefghij", 5000, "$6$abcdefghij$7T7df8XBkLMdUu88CTm53FLArDdGmq/nn55CVn9CMt4.MJaFsXu6CQpbmL9UbX0J.C3AX1MkPKvniLL00lAMz0"}, 123 | {"", "abcdefghijk", 5000, "$6$abcdefghijk$L5InIoA.tWhbxByZZU8.E5/ClRCvPvnD/MpP2Fw6eqviqMrAe5oUp3GV4SOIU2fzZJl40dXJFQlpiVkKUCx/C1"}, 124 | {"", "abcdefghijkl", 5000, "$6$abcdefghijkl$pQPK74I5wapdxPf8YWA7pSPuRWmXlI5TbyoI0EK.AHnXx1IZMdT63YUbcHUfjd9mgdWZKLo27sOGTzziErT9Z1"}, 125 | {"", "abcdefghijklm", 5000, "$6$abcdefghijklm$f.v9oOchLiuGYhky7QA9AOqMAmxKTu9iRQmDtn1XWI5mHPvakQUSKJwISf3DEyqCVAX2gVXg2OxqBFKnBjMcB/"}, 126 | {"", "abcdefghijklmn", 5000, "$6$abcdefghijklmn$58TlzgSuzWsAYj1MlhCVtcaEb46PIqS5LEVNFE3Rj1Cn81w7.GnVz2v6jhS4otit9C1e2nojtE9w04CLEqmkj1"}, 127 | {"", "abcdefghijklmno", 5000, "$6$abcdefghijklmno$IuhBzXABLOkV9Iw8epw2E9tIjYVoB7QY1BQIfjaPA5Dm0j9vVBLatLWHlTF0Dr27XctYrYavSRFZBLece6NdP1"}, 128 | {"", "abcdefghijklmnop", 5000, "$6$abcdefghijklmnop$6.vC8ffobuN7AxcHvesxeeksF2DXFfpYyFt3PFU8pYpEQPhWFSN7hwaUQRfHg/LkfB3jIPEitUcU7ZTqjaQUp1"}, 129 | {"", "qrstuvwxyz012345", 5000, "$6$qrstuvwxyz012345$nijv4ng4T0nplrD5Odbhy/WIWjMxYOOfVeatWJn21RsKHpx1wEwv5lJ6bxcRhAZUkGlrXMIYJWFcw8.7vS0Vb/"}, 130 | {"", "67890./", 5000, "$6$67890./$RBwYm1yXiNS03qj/NJuXVKtMZTswb9X6ooUeD3te48jhm2/G7RLrdshz3ni1hE.aPQ1SUJcTMPo5LKoyq1.FW0"}, 131 | {"", "ABCDEFGHIJKLMNOP", 5000, "$6$ABCDEFGHIJKLMNOP$hv8qZhuzMVc0IFBbNgS7.IcohKodUTGf/LUI79ky5NJZbB4wONSS9spEq/roD.WWO73zAFUF4.bmsJRaED0L/1"}, 132 | {"", "QRSTUVWXYZ012345", 5000, "$6$QRSTUVWXYZ012345$zCTwxSLN/2oO9U17pELyZJYdLPAx0d6VlDFIs5fSllerpTG9UXzUG8yzefGh7Nn3fYmYnL/cRkd5Yrtz9qs2h."}, 133 | {"", "a", 1000, "$6$rounds=1000$a$q/ZSdy9IBSzcpbaIjdRTOu569QiYiP4n7Ab08Oq1Ir6J.GD1xR3BAwRIYHnavtE1Wzodknd9BxknlHhFBP/az1"}, 134 | {"", "a", 1001, "$6$rounds=1001$a$1LgZTwgZLXxTQ4YhBC7mzpSbPbXwOMjOQhAP6D6HlIk5CPMWt9eaLdwj70vgjAd4wtjWJERgdMX27ED5phxhe/"}, 135 | {"", "a", 1002, "$6$rounds=1002$a$2K6qlumdpbtycktydOLKQgsyLxUlybgOdZZEBbLhXghjr3fNSW/4AKvCXDs2e9IBZkCGFJH6sYNNMFG.hcJSr."}, 136 | {"", "a", 5000, "$6$a$Z6IPkDduHdwavesVY2NFMpOHkigshxPYdhfLg0D3A7/XFQcso9RV2E8qczyJ2su.dj6RRLaTpvgwN1rZfK0I9."}, 137 | {"p", "a", 5000, "$6$a$2.GZ8yudlr5SHiCPkX5N4O16VrDiQ2OZrbwWIoAlxZVHQFGoqZP6JY4XB1c.jTYlVXS7wOdfIg7aItV3orkit0"}, 138 | {"pa", "a", 5000, "$6$a$HUuS5tY/wnt3E8eDFp/8JPTIHHJWvX37gpDXtpDIxWYor.jtUu90mpY7x8zCGFDsnRKMuIy0.BJC7K1ibKhxx1"}, 139 | {"pas", "a", 5000, "$6$a$g5Q6aW5iqQYNy3wQFOl5cJ7QhA7wsrK765cZK7IzcYg.UsWSADWdkr3X3cFttQPoHnf6eNWSPeRVm59BzO6Fa0"}, 140 | {"pass", "a", 5000, "$6$a$wQrz9ymkqnEyACDR001yzYLlAL/GtICZdnh4j5dTlGNtqc/9FHlDDIoS3v4z5x0I7EXBt2XdNPO/xIW4LYxtO0"}, 141 | {"passw", "a", 5000, "$6$a$vGvPPKUaIa/1Osg.IRyUTPs.AySaBCmwnHinZdj/ikwgFc3xzhpnfTmi43/ZYxtz5xs.pTQf9Xb/yPmzdVTXt1"}, 142 | {"passwo", "a", 5000, "$6$a$.fL3fsn4RwvmlbedPXIEEg6lmJJm/EfDGNSEYud/rlN62bTIbsLhtVqEXUxqMiuPIzuo9.7O91mguOrF7JMjo/"}, 143 | {"passwor", "a", 5000, "$6$a$SFYSJqIV4xSJbYeBNuHaFz8ZtDjv94BBTE674Lpu5UwC3EZerkqluIeL9tRGE8IQDFTNh.4nugIBNliUiMcy10"}, 144 | {"password", "a", 5000, "$6$a$cDF29/4rxkZzke7MDbxA5QugGKNXjz8kkJgmZT.KmMruKzBbS9DbFa09GnKP6G5OyNipfZwCBHbrAJRPb5aAR1"}, 145 | {"passwordp", "a", 5000, "$6$a$d1UzpJNuUZGOszAQcMZiJhPM2.oatkvFbdwaq4L/MuDujM7ADfjiFcxxOv4XkICpM7OV6AQq0EFcro/hokJ3c."}, 146 | {"passwordpa", "a", 5000, "$6$a$0ZQEqjwKwgSy6yYb3hVK14ZT5xBVOzXzDu9Wsfg7XG0UsZBbqSq/ntsy0jS.m9Q6rQ1C6qOAhZyeokwMhwGkL0"}, 147 | {"passwordpas", "a", 5000, "$6$a$jpX5MXO9Ihh2ud9Crs/RO0v5mMaMVyTdK4fMtTJT7wDN3y1QsVNd6XQPjtjyhqoNf6U.17uWJEuqE2EwQUjy2."}, 148 | {"passwordpass", "a", 5000, "$6$a$u8v6VyZMYcXmWvnM3qwSLFyj27OxwCJX4sWeWuoe2NszlwoRq02cAxGdBqv5QT8qCHdVaAodsZSCJVeOOSaaJ0"}, 149 | {"passwordpassw", "a", 5000, "$6$a$C0Lw3KsTXF3v175TFrBSPKZNQUyGvYK8Sxm5i0gxQq1OHY9zzS2bCNT5/25M5zZYKFBa.5YBqIj/.lLyUyWiH0"}, 150 | {"passwordpasswo", "a", 5000, "$6$a$FEDrJkxmbByetjs2HvUO6DdQHnWXhgS.QVBgn.lk/t4hwk2IsLghfsBOOAizbji8xCE6UuVM2dhD/RJpWey2T0"}, 151 | {"passwordpasswor", "a", 5000, "$6$a$paYanVEXJJLnxrsKQc5VWLz.GVfdi9yLoMyfmZ92OwtS.gubT7AIJYFv6uxIR0/X7vnsUb9X.62N2/KGq7STJ1"}, 152 | {"passwordpassword", "a", 5000, "$6$a$w4jBmtZlVA/16NIE743sU5QyJ8BPYPIMBxSpVy1G6KjttDQt83p1oKmwLBp.U4ttvg03hqk/qCw2VD.hEi6fR/"}, 153 | {"passwordpasswordp", "a", 5000, "$6$a$.nQPXAfCa3wYsL6XNUL13Gx.zBPOjFaggIdy4bkC6VJRHVzUnL4UOFz/L/3OyJSmucTeZASIq1r.9AEioKvrb/"}, 154 | {"passwordpasswordpa", "a", 5000, "$6$a$.mGONN21Bxx/mLRteEFjQUNBHhxea1Zghgz1hi.lgr89i79plEf/yWaEcOmZ/jE1n/NMBqyGxl0NcgqTIp4hs0"}, 155 | {"passwordpasswordpas", "a", 5000, "$6$a$Hc1ZpGLw6IftPnNzjofWdNMWYWhKtAV.UDXbBHSkipb0Uv4NbWFggKGJgfHAEP9ZDa0KmJV0e/6FvKqvXDcnN0"}, 156 | {"passwordpasswordpass", "a", 5000, "$6$a$OoesF5Px43ePkU1oKhcsEvg21BH5JxMQTu4FCK9CwhHkr2ges9Wm7IKYYvNILcvxmMnH11nO1Tab7LlpMXqjB."}, 157 | {"passwordpasswordpassw", "a", 5000, "$6$a$/.bOypOhoO7NkxSwepGmfREqx2F6x181xLjl.nk7zlJj.OpROQVqV2ldZAc5vp8NxkQb5NBeNGE0wcKxEMZYM."}, 158 | {"passwordpasswordpasswo", "a", 5000, "$6$a$l429mIwN0QMyuKv8el1pvIs.E5LXoO8NG1vHt685HeRP32GZfQ7rrO5UQJn/iihog3pThh/XEBUZ5sU8ll6hO1"}, 159 | {"passwordpasswordpasswor", "a", 5000, "$6$a$o.0oPvgzTDLzaevEzmf23fCQ3n2ovgtIUZuu7zZjSTF.xzd61L6xfcfsae/caP0jSI2SYyt4afdnofe2zM62V."}, 160 | {"passwordpasswordpassword", "a", 5000, "$6$a$ht/BgUA3Tr1tI8tnRITvibn.rXQ6xjMAXK7bC0bBnK.axJ1J2gVcVyo8vw4pdcm7dDr.ICAxvKSfYnYEPZubG/"}, 161 | {"passwordpasswordpasswordp", "a", 5000, "$6$a$dmbpWylbazJLRY/5kD9k5tIrXk8ZGWvc77CmvSibybc54wpazLQEpSYotQbGFARPRfD8yVhdVNnsmtnQfW7qh/"}, 162 | {"passwordpasswordpasswordpa", "a", 5000, "$6$a$ztoxMY1332MdieRrRCMFXVjxnbhk7wtTDq/MosYcun2wwQSZ9gLlUSbNDTtQ.C9Ns3ZroCWkoAkAhL3ga7Of1/"}, 163 | {"passwordpasswordpasswordpas", "a", 5000, "$6$a$vOcIXCqao40l.MsjsylPdFE43ibraEui21r8obTvy64t61meMD2kzmrwEJpdnxO1/linnmmynDuFzfX9bdQfL."}, 164 | {"passwordpasswordpasswordpass", "a", 5000, "$6$a$rdlSkCcboGfx8MBmmQvgn.ASjHWgLV7dQhuilN5s5GAjOt4lji4lS3R97nFX8tRrEX6pnjsaRqQYyw9a83p4G1"}, 165 | {"passwordpasswordpasswordpassw", "a", 5000, "$6$a$JqofGDjZIjTEG9sk3et1UGklMiS9cFdv0cNrJxVuc5Co6iHGFjxH3LACATDRq..7LIURoFOxIg84iQ6n8Bbhy/"}, 166 | {"passwordpasswordpasswordpasswo", "a", 5000, "$6$a$li226SSGj.Zl04Qrv5gLazUd8Q7FC79PKZ.gskys.RVxRWhCqbeswwxjdFkeNT8DeB9yMV7ObweIRXLU6/vkI."}, 167 | {"passwordpasswordpasswordpasswor", "a", 5000, "$6$a$bte09FK2Zt4vLXVKepQ2L077NMr4.uheBNn0F8Lq2WlTUthyW1uV4jMfqllxrGemBxEGcSxTC1kwibE3WEc41/"}, 168 | {"passwordpasswordpasswordpassword", "a", 5000, "$6$a$VRT5yzDrtRfQ3wJx64TZlf8SXuJCr2zWYAfUvSLQZQqh/Ubdv71P.LMVNLlCaRNvZf1ay6QjThUQJyvPj29sW/"}, 169 | {"passwordpasswordpasswordpasswordp", "a", 5000, "$6$a$NL.wqM1nYkpaeNgUHqndFARbSiKJTfzOFAUBhiENtHtHWaMMflnk5IThT3KWIVa3rnQLDDkGOOgqbHSH8FDbU1"}, 170 | {"passwordpasswordpasswordpasswordpa", "a", 5000, "$6$a$2Y55eiAzLHJ951TbHmfpuwFQiE9o/4dxsns5xRj.RI4Nz2DOCh0Afv7DV2/ASAXXTUvL90mO1VuC5.GovaQDZ."}, 171 | {"passwordpasswordpasswordpasswordpas", "a", 5000, "$6$a$UUbMzedAQ0OrOWi.iHcmH.x9eQKpQDau35bHv5mLtX07habgHpMChhFwywWFm0QOztEUUrbfDCmNwqCpaYw561"}, 172 | {"passwordpasswordpasswordpasswordpass", "a", 5000, "$6$a$VBxf35WZRhyIodRreS6EOEb0mUge4.s/u1CN2pmq7GkmL9V7CbpmY7PLNtMFEwOADyCHur5Bvs.FGQmogQuf91"}, 173 | {"passwordpasswordpasswordpasswordpassw", "a", 5000, "$6$a$1Y7DqOlzQP8bFRlHagWHsnhE8NVuCuIFDLlSq1hB280wKC6fEK7iItr7/P6LwkMN3mqVHuQMHigeleoB1V/qj/"}, 174 | {"passwordpasswordpasswordpasswordpasswo", "a", 5000, "$6$a$YPO0cEt8GcWu5p7.78dSDnHwLSNAaNrCJytQM0O92UFObNssXodhhHmNRNQHuIIJsamDpZo2fenaRNFqEdXoo/"}, 175 | {"passwordpasswordpasswordpasswordpasswor", "a", 5000, "$6$a$CwhkFGAtkg9.ey9cKXcb4LA9Q.xviUgiwmYD65NTE1zV5Fk35yMhTggSIUjwqMBFjkPOT2CoaD23mY6mTF0n51"}, 176 | {"passwordpasswordpasswordpasswordpassword", "a", 5000, "$6$a$vb2IuTylTVRiKzUCoBAVnR78V./MN47yYDkTaZXGWLKIElFeDUnkrsOAlCegKjg7gHyY9LQywy/gyVYU.D7cI."}, 177 | {"passwordpasswordpasswordpasswordpasswordp", "a", 5000, "$6$a$XIZXatmyFVhAR8tUGa72Frt/G9u4QoVNhBF5NM8ZN7i3JKARdYVujvB7GqbY37Wve5rA9tpdgCGpxwhtNMpaN."}, 178 | {"passwordpasswordpasswordpasswordpasswordpa", "a", 5000, "$6$a$WmOPLnHF2wtm0/mLzNA8BmVxJlRFSmmXyl.Wnz89b5JfFFfyldELGa3sFL0LFcupaHQUvVw5QMXRgrPPEGvMC."}, 179 | {"passwordpasswordpasswordpasswordpasswordpas", "a", 5000, "$6$a$QZgDm7odB/xtbf/x5AmI6x9DpWYqtApEGjIPbkxcEC7G25Ibn9plQdqlcwBnwGROwRpVS8zl95yCL4C3t2buA1"}, 180 | {"passwordpasswordpasswordpasswordpasswordpass", "a", 5000, "$6$a$Mh.pluuvGbDSTZn9QNrUYWq8Q8mUtRX38S7uQeccvbdNVTG.e62NPKrgjM4JzMgH.7mWmY4VOyLNQOtH.Qbra."}, 181 | {"passwordpasswordpasswordpasswordpasswordpassw", "a", 5000, "$6$a$VTfS6BpurvR35bGVaiT5aQebuirXM7u0C4HCHOWmSypujiKNHdFb/VIU.IRFa6luGxIKKhpPRfkn73L9zDLJl/"}, 182 | {"passwordpasswordpasswordpasswordpasswordpasswo", "a", 5000, "$6$a$RrtTJpOtdckHPIIG0utYRVLboamUYAYbfjVBuk8.qQHrZ.I6GuHIIrBWd7pdguSaY1QEHRvUB3BLeO8uT/Bh3/"}, 183 | {"passwordpasswordpasswordpasswordpasswordpasswor", "a", 5000, "$6$a$J9zm3yZNN5x8JchIactr2R5WJ7epPLH/RyajLg0v3jlhJLjcYE3wEpCPIVl6xksaEdLhzzaYom.Elk/A5oUla0"}, 184 | {"passwordpasswordpasswordpasswordpasswordpassword", "a", 5000, "$6$a$vr2buLAslLFlLPQzPVAhQDidRVz4YnyTSSlWUVo9bAznxiK7dcimCKpOt/GRA6h22FekXDvH4vADiXTJIOH8v."}, 185 | {"passwordpasswordpasswordpasswordpasswordpasswordp", "a", 5000, "$6$a$RrNNFkr7wxVo4jeUV/LZCugv/cbvBYJiEh9WDXDLnYikSI/uT3zomN64ZK7yz.DcDhFRIXNFz9vQQgCqk7yp3/"}, 186 | {"passwordpasswordpasswordpasswordpasswordpasswordpa", "a", 5000, "$6$a$IrQjaHvY2nSgmIrVpRSAbKf2KwdR.Dr7wXI5aen5BMcwCNbI91nuMYtbGvT4CAFrTvYtw7HlN04sZud16oK/50"}, 187 | {"passwordpasswordpasswordpasswordpasswordpasswordpas", "a", 5000, "$6$a$QXsxgPhX94A/JY/ysc3/6pWmqkL46dgniKePmBj/NWdo8zfQ9H/vXe6A0DzPKPsJeG.eqavXce5ULcS0tmS57."}, 188 | {"passwordpasswordpasswordpasswordpasswordpasswordpass", "a", 5000, "$6$a$Mi5A3/OlyZCNy/RlsqufrkNrODVdLQ00y4wMid0dofzvA5Xdc5rwl6e/IH0ANT.8M4LQ0EnzTaoiaH07Y73yu/"}, 189 | {"passwordpasswordpasswordpasswordpasswordpasswordpassw", "a", 5000, "$6$a$ShmrcfPrK78J4A6yeBfpCBtPaeWj47YPKDktwVHeKv7A7jU/RQPtGfy3gdScjYfwTA/wh95yLTAPDv1Vk13LU."}, 190 | {"passwordpasswordpasswordpasswordpasswordpasswordpasswo", "a", 5000, "$6$a$stWiOqO6J/bOQVkhe3mm5yS3npGU5fFpSMTghQeZNRr/czzh3Nxvg8HGG6JwjLBZbvU4wigYjOrLOMb5xSjXx/"}, 191 | {"passwordpasswordpasswordpasswordpasswordpasswordpasswor", "a", 5000, "$6$a$6JtfKJTheuSyWkjuIGnm0LlRoI2ZltBaNu6185XWVnhUXb.PRYIM5kViLGK1sYCamXo.UsNzmfDkfda4DgiFQ/"}, 192 | {"passwordpasswordpasswordpasswordpasswordpasswordpassword", "a", 5000, "$6$a$4/GynY7Nj6x0M.kzmzQF72YfnP1nRJTm5eZlXqap7EETHW6r4eSas9SEjoxJOWjozECjaJngvYIvBxInjPlvc."}, 193 | {"passwordpasswordpasswordpasswordpasswordpasswordpasswordp", "a", 5000, "$6$a$KFf/SJFiyGGvFMebABD85umh6UKsDFMCOO1F0CNHwyiyQHWRWe3LiIiCysfCCmQFvx6RVv8TzvOd1NsYkkk8Y/"}, 194 | {"passwordpasswordpasswordpasswordpasswordpasswordpasswordpa", "a", 5000, "$6$a$ZvE9/pfJrvzl2hLAotHm7b7LA.yIHv6wiIvQwOstSr1/1X5jW.xEC1UY7b5zrELNNp7eMANlXDfKFmfCZHU0f1"}, 195 | {"passwordpasswordpasswordpasswordpasswordpasswordpasswordpas", "a", 5000, "$6$a$VI0xoGKgZoMgjXIflCIU/GeU1nbd1tAj97efY9W1bt8uT6sHPDM7SJO0rVZqv2wzAioljIf6mRYvvzkggxyq90"}, 196 | {"passwordpasswordpasswordpasswordpasswordpasswordpasswordpass", "a", 5000, "$6$a$2xOzg2n93SZWeYbqJNKNgMngkSvWUKovpGUofhLr/XLFd45PS1vieHlSO5WH5Ok3Gs5nSblUgvTqRJEGDgnZ./"}, 197 | {"passwordpasswordpasswordpasswordpasswordpasswordpasswordpassw", "a", 5000, "$6$a$hATpzgscOxHuvWtEgPxcEHBruxhGXAzXR/OY/3KymoSP90khvc6YJEA5RuyTwLHSgJUfnppQruLi7l8hQ0lLw0"}, 198 | {"passwordpasswordpasswordpasswordpasswordpasswordpasswordpasswo", "a", 5000, "$6$a$b9xZTbVSCBG.3yO1b5sFiUYwI0FNo8CXRq3md/.17G8whmQhUe33Z3g1Zy460VvwjFNeebFySR44Icl7VF0zn/"}, 199 | {"passwordpasswordpasswordpasswordpasswordpasswordpasswordpasswor", "a", 5000, "$6$a$soVtpRYFRKH0i68EXEcbq/KbrJbuKQkXAzw0zCXjshVoamGb6CueGq9zWxVQbsD9l8it5CLugR5r9vwGq/CMe."}, 200 | {"passwordpasswordpasswordpasswordpasswordpasswordpasswordpassword", "a", 5000, "$6$a$MXusFpDMp1x3gwNM4gP0GQCghEvml1nFMvtXAdYa/i9RZ1Dfbn42sXXcS9/g3nYJHrSgbSVpJQ.G/RCb1ms8I0"}, 201 | {"passwordpasswordpasswordpasswordpasswordpasswordpasswordpasswordp", "a", 5000, "$6$a$92E53ObvYVFDERKnb5wMG/20A6pob7GEp./LoefiFURg8wA0JIXJM60/vTAR0a0PoRV/aiNpVLmMUsDZiDUFF."}, 202 | {"passwordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpa", "a", 5000, "$6$a$iUTCg3PequYS6S1NMA5VPze8knWY.sO.BLy0sYWCV1sOZH0up3QWEKa83V8PB6ZCf4j7DOlkbtF9wBXxYYm/N/"}, 203 | {"passwordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpas", "a", 5000, "$6$a$wGCgfgnBB9vCYV3GK/IMCijtORGJphRaB6ecjo9pHs9kr6rhFhhYyUFOloDer9bhSz5Ar4cLwP6k4q/NGdoql0"}, 204 | {"passwordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpass", "a", 5000, "$6$a$GqCyixlbKI9iUMz4G2oQjqcI4EerTFDvIwvdi7cU.Fmkf5q8qxIbwJ8blyB.BEEbaWlZM/Iq.APTtUeOPxN/01"}, 205 | {"passwordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpassw", "a", 5000, "$6$a$tszvYONTDoPoA4xJLb7O4MsxO2wlmXWYGMsCyQ0bjhMtfbQRhWAkBVBVCFkjAkq3tr8S47P.jvW8SsHxSUvK91"}, 206 | } 207 | 208 | func TestSHA256Crypt(t *testing.T) { 209 | for i, tst := range tests { 210 | fmt.Printf("%d\n", i) 211 | out := Crypt256(tst.password, tst.salt, tst.rounds) 212 | if out != tst.output { 213 | t.Errorf("mismatch:\n got: %#v\n expected: %#v\n password: %#v\n salt: %#v\n rounds: %#v\n", 214 | out, tst.output, tst.password, tst.salt, tst.rounds) 215 | } 216 | } 217 | } 218 | 219 | func TestSHA512Crypt(t *testing.T) { 220 | for i, tst := range tests512 { 221 | fmt.Printf("%d\n", i) 222 | out := Crypt512(tst.password, tst.salt, tst.rounds) 223 | if out != tst.output { 224 | t.Errorf("mismatch:\n got: %#v\n expected: %#v\n password: %#v\n salt: %#v\n rounds: %#v\n", 225 | out, tst.output, tst.password, tst.salt, tst.rounds) 226 | } 227 | } 228 | } 229 | 230 | // © 2014 Hugo Landau BSD License 231 | --------------------------------------------------------------------------------