pass
2014-11-11
Child:22ce15152c43
0:b394d1d58b85 Browse Files
First commit. First untested, uncommented iteration of the library, based on feedback from Jan Lehnardt, who really _is_ the perfect person. We at least have a README, though. TODO: golint, go vet, tests, comments
.hgignore LICENSE README.md pass.go
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/.hgignore Tue Nov 11 19:26:25 2014 -0500 1.3 @@ -0,0 +1,1 @@ 1.4 +cover.out
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 2.2 +++ b/LICENSE Tue Nov 11 19:26:25 2014 -0500 2.3 @@ -0,0 +1,21 @@ 2.4 +The MIT License (MIT) 2.5 + 2.6 +Copyright (c) 2014 Second Bit, LLC 2.7 + 2.8 +Permission is hereby granted, free of charge, to any person obtaining a copy 2.9 +of this software and associated documentation files (the "Software"), to deal 2.10 +in the Software without restriction, including without limitation the rights 2.11 +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 2.12 +copies of the Software, and to permit persons to whom the Software is 2.13 +furnished to do so, subject to the following conditions: 2.14 + 2.15 +The above copyright notice and this permission notice shall be included in 2.16 +all copies or substantial portions of the Software. 2.17 + 2.18 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 2.19 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 2.20 +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 2.21 +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 2.22 +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 2.23 +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 2.24 +THE SOFTWARE.
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 3.2 +++ b/README.md Tue Nov 11 19:26:25 2014 -0500 3.3 @@ -0,0 +1,25 @@ 3.4 +# Pass 3.5 + 3.6 +Pass is a library of convenience functions for securely handling passphrases. 3.7 + 3.8 +## On Startup 3.9 + 3.10 +When your server first starts up, you should run the `CalculateIterations` function, passing the `hash.Hash` you intend to use in hashing passphrases. I recommend SHA-256. This iteration count will be used later, so hold on to it. 3.11 + 3.12 +## To Hash a Passphrase 3.13 + 3.14 +When a user first signs up, you need to hash their passphrase and store it in your database. _Never store their unencrypted passphrase **anywhere**_. 3.15 + 3.16 +To do this, run the `Create` function, passing the `hash.Hash` you intend to use in hashing their passphrase (I recommend SHA-256), the number of iterations to use in hashing their passphrase (this should come from `CalculateIterations` and the hash being used to create the passphrase should be the hash used to calculate the number of iterations), and the passphrase the user entered as a slice of bytes. 3.17 + 3.18 +The resulting hash and a salt will be returned. You should store the salt, the hash, and the number of iterations in your database--this will allow you to set those variables on a per-passphrase basis. 3.19 + 3.20 +## To Check a Passphrase 3.21 + 3.22 +Retrieve the passphrase you wish to check against (probably keyed by ID or username) from your database. You'll need the salt, the hash, the hashing algorithm, and the number of iterations to run. 3.23 + 3.24 +Run `Check`, passing it the hashing algorithm--which should match the hashing algorithm run against the passphrase originally--, the number of iterations, the passphrase the user just entered you want to compare to the stored passphrase, and the salt. The output will be a candidate hash. 3.25 + 3.26 +Finally, run `Compare`, passing it the candidate hash and the hash from your database. This is a constant time function, and will compare the two based on the length of the candidate. 3.27 + 3.28 +If `Compare` returns true, the passphrases match. 3.29 \ No newline at end of file
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 4.2 +++ b/pass.go Tue Nov 11 19:26:25 2014 -0500 4.3 @@ -0,0 +1,51 @@ 4.4 +package pass 4.5 + 4.6 +import ( 4.7 + "crypto/rand" 4.8 + "crypto/subtle" 4.9 + "hash" 4.10 + "time" 4.11 + 4.12 + "code.google.com/p/go.crypto/pbkdf2" 4.13 +) 4.14 + 4.15 +func Create(h func() hash.Hash, iters int, passphrase []byte) (result, salt []byte, err error) { 4.16 + salt = make([]byte, 32) 4.17 + _, err = rand.Read(salt) 4.18 + if err == nil { 4.19 + return []byte{}, []byte{}, err 4.20 + } 4.21 + result = Check(h, iters, passphrase, salt) 4.22 + return result, salt, err 4.23 +} 4.24 + 4.25 +func CalculateIterations(h func() hash.Hash) (int, error) { 4.26 + hashInstance := h() 4.27 + salt := make([]byte, 32) 4.28 + _, err := rand.Read(salt) 4.29 + if err != nil { 4.30 + return 0, err 4.31 + } 4.32 + iter := 2048 4.33 + var duration time.Duration 4.34 + for duration < time.Second { 4.35 + iter = iter * 2 4.36 + timeStart := time.Now() 4.37 + pbkdf2.Key([]byte("password1"), salt, iter, hashInstance.Size(), h) 4.38 + duration = time.Since(timeStart) 4.39 + } 4.40 + return iter, nil 4.41 +} 4.42 + 4.43 +func Check(h func() hash.Hash, iters int, passphrase, salt []byte) []byte { 4.44 + hashInstance := h() 4.45 + return pbkdf2.Key(passphrase, salt, iters, hashInstance.Size(), h) 4.46 +} 4.47 + 4.48 +func Compare(candidate, expectation []byte) bool { 4.49 + candidateConsistent := make([]byte, len(candidate)) 4.50 + expectationConsistent := make([]byte, len(candidate)) 4.51 + subtle.ConstantTimeCopy(1, candidateConsistent, candidate) 4.52 + subtle.ConstantTimeCopy(1, expectationConsistent, expectation) 4.53 + return subtle.ConstantTimeCompare(candidateConsistent, expectationConsistent) == 1 4.54 +}