18

Here's an example of salting and hashing a given password in python.

import scrypt
import os

# Length of salt
PW_SALT_BYTES = 32
# Length of scrypt hash of passwords
PW_HASH_BYTES = 64
# test password
password = "hello"

salt = os.urandom(PW_SALT_BYTES).encode('hex')
# hash(password, salt, N=1 << 14, r=8, p=1, buflen=64)
hashed_password = scrypt.hash(str(password), salt.decode('hex'), buflen=PW_HASH_BYTES).encode('hex')
print(hashed_password)

Which would give us a hashed and salted string in return:-

4d1da45b401961fccb10e094ecd70ec79510f05483ca293d300bbd0024e35866ca39fe09fbc15f83a359431021a1ed9644f7d2b871b357e37a186300877edb18

How would I implement this in golang?

Calvin Cheng
  • 35,640
  • 39
  • 116
  • 167

4 Answers4

25

Rather than using scrypt, a great library for securely hashing passwords with random salts in Golang is golang.org/x/crypto/bcrypt, as mentioned in the following answer:

Bcrypt password hashing in Golang (compatible with Node.js)?

A couple benefits of using bcrypt instead of scrypt:

  1. The salt is automatically (and randomly) generated upon hashing a password, so that you don't have to worry about salt generation.
  2. When storing hashed passwords in a database, you no longer have to worry about storing the salt for each password hash as well.
  3. The syntax is simplified for hashing and checking passwords.
  4. The hash produced by bcrypt includes the bcrypt version, cost, salt and cipher, not only the cipher.

Here's an example of using bcrypt taken from the above answer:

package main

import (
    "golang.org/x/crypto/bcrypt"
    "fmt"
)

func main() {
    password := []byte("MyDarkSecret")

    // Hashing the password with the default cost of 10
    hashedPassword, err := bcrypt.GenerateFromPassword(password, bcrypt.DefaultCost)
    if err != nil {
        panic(err)
    }
    fmt.Println(string(hashedPassword))

    // Comparing the password with the hash
    err = bcrypt.CompareHashAndPassword(hashedPassword, password)
    fmt.Println(err) // nil means it is a match
}
Iron John Bonney
  • 2,451
  • 1
  • 17
  • 14
21

Go doesn't have scrypt in the standard library but there is an "official" implementation in the go.crypto repo.

import (
    "crypto/rand"
    "fmt"
    "io"
    "log"

    "code.google.com/p/go.crypto/scrypt"
)

const (
    PW_SALT_BYTES = 32
    PW_HASH_BYTES = 64

    password = "hello"
)

func main() {
    salt := make([]byte, PW_SALT_BYTES)
    _, err := io.ReadFull(rand.Reader, salt)
    if err != nil {
        log.Fatal(err)
    }

    hash, err := scrypt.Key([]byte(password), salt, 1<<14, 8, 1, PW_HASH_BYTES)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("%x\n", hash)
}
Stephen Weinberg
  • 51,320
  • 14
  • 134
  • 113
  • 1
    This looks like what I was trying as well but am I misunderstanding something because the resulting output `hash` (from golang) does not match with the `hashed_password` printed out in the python script? I was thinking that they should produce the same output since both the test passwords are "hello". – Calvin Cheng Apr 13 '14 at 06:27
  • @CalvinCheng: were you using the same salt when comparing the Python and Go versions? If not, then it's a good thing that the output is different. – James Henstridge Apr 13 '14 at 06:31
  • Yes. I was also trying out with a fixed salt and they don't match. For the above example, you are right that they should not match because the salt is randomly generated. – Calvin Cheng Apr 13 '14 at 06:36
  • OK, I figured it out. This is because in my fixed salt attempts, I forgot to `.encode('hex')` on the fixed salt. So problem solved, and thanks for your solution above. It's exactly what I was looking for. – Calvin Cheng Apr 13 '14 at 07:27
6

It looks like now Go has scrypt in official library. Its subrepository x/crypto among many other crypto functions has an scrypt.

Here is an example of how you can use it:

package main

import (
    "golang.org/x/crypto/scrypt"
    "fmt"
)

func main(){
    salt := []byte("asdfasdf")
    dk, err := scrypt.Key([]byte("some password"), salt, 16384, 8, 1, 32)

    fmt.Println(dk)
    fmt.Println(err)
}
Salvador Dali
  • 214,103
  • 147
  • 703
  • 753
2

Here's a complete hashing utilities funcs I wrote based on RFC 2898 / PKCS #5 v2.0.

Hash can be used to hash passwords, straight forward something like Hash("hello")

while Verify can be used to raw password against a hash, basically what it does is to hashes the raw string and compares it with the actual hash.

package common

import (
    "crypto/rand"
    "crypto/sha1"
    "encoding/base64"
    "errors"
    "fmt"
    "golang.org/x/crypto/pbkdf2"
    "io"
    "strconv"
    "strings"
)

const (
    SALT_BYTE_SIZE    = 24
    HASH_BYTE_SIZE    = 24
    PBKDF2_ITERATIONS = 1000
)

func Hash(password string) (string, error) {
    salt := make([]byte, SALT_BYTE_SIZE)
    if _, err := io.ReadFull(rand.Reader, salt); err != nil {
        fmt.Print("Err generating random salt")
        return "", errors.New("Err generating random salt")
    }

    //todo: enhance: randomize itrs as well
    hbts := pbkdf2.Key([]byte(password), salt, PBKDF2_ITERATIONS, HASH_BYTE_SIZE, sha1.New)
    //hbtstr := fmt.Sprintf("%x", hbts)

    return fmt.Sprintf("%v:%v:%v",
        PBKDF2_ITERATIONS,
        base64.StdEncoding.EncodeToString(salt),
        base64.StdEncoding.EncodeToString(hbts)), nil
}

func Verify(raw, hash string) (bool, error) {
    hparts := strings.Split(hash, ":")

    itr, err := strconv.Atoi(hparts[0])
    if err != nil {
        fmt.Printf("wrong hash %v", hash)
        return false, errors.New("wrong hash, iteration is invalid")
    }
    salt, err := base64.StdEncoding.DecodeString(hparts[1])
    if err != nil {
        fmt.Print("wrong hash, salt error:", err)
        return false, errors.New("wrong hash, salt error:" + err.Error())
    }

    hsh, err := base64.StdEncoding.DecodeString(hparts[2])
    if err != nil {
        fmt.Print("wrong hash, hash error:", err)
        return false, errors.New("wrong hash, hash error:" + err.Error())
    }

    rhash := pbkdf2.Key([]byte(raw), salt, itr, len(hsh), sha1.New)
    return equal(rhash, hsh), nil
}

//bytes comparisons
func equal(h1, h2 []byte) bool {
    diff := uint32(len(h1)) ^ uint32(len(h2))
    for i := 0; i < len(h1) && i < len(h2); i++ {
        diff |= uint32(h1[i] ^ h2[i])
    }

    return diff == 0
}

Here's unit test that would help you figuring out how to call such funcs

package common

import (

    "github.com/stretchr/testify/assert"
    "testing"
)

func TestHash(t *testing.T) {
    hash, err := Hash("hello")
    assert.Nil(t, err)
    assert.NotEmpty(t, hash)

}

func TestVerify(t *testing.T) {
    hash, err := Hash("hello")
    assert.Nil(t, err)
    assert.NotEmpty(t, hash)

    ok, err := Verify("hello", hash)
    assert.Nil(t, err)
    assert.True(t, ok)
}
Muhammad Soliman
  • 21,644
  • 6
  • 109
  • 75