Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
ThisGuyCodes committed Jul 5, 2013
0 parents commit fec39e5
Show file tree
Hide file tree
Showing 5 changed files with 341 additions and 0 deletions.
22 changes: 22 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so

# Folders
_obj
_test

# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe

# Cloud 9
.c9revisions/
19 changes: 19 additions & 0 deletions Mysql.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package passwords

import (
"crypto/sha1"
"encoding/hex"
)

// Passwords for MySql
func Mysql(pass string) string {
// Create our own hasher, for thread safety
hasher := sha1.New()
// This is actually really simple, it's just a sha1 of a sha1 displayed as hex
// (with a leading * to indicate the 'new' password hash of MySql)
hasher.Write([]byte(pass))
interm := hasher.Sum(nil)
hasher.Reset()
hasher.Write(interm)
return "*" + hex.EncodeToString(hasher.Sum(nil))
}
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Passwords
=========

As far as I know, no one has bothered to port the crypt() function to other platforms, or even really looked at it in a long while.

I found myself in need of being able to make unix password hashes in my Go programs, so I went hunting to re-create this functionality. I wasn't able to find the source of crypt() itself (at least not abstracted to the point that I'd have to dig through several libraries of source). But I did find a pure bash recreation of it, at least for sha512. While abhorently slow, this bash script did acurately produce the same output as crypt() (read: same output as mkpasswd). So I used this as a template for recreating it in Go.

Long story short, I did it, and learned that crypt() is absurdly (and unessecarily) complicated (security through obscurity is not security, expecially when the source for your process is very public).

I decided to make this into a library that'll provide the ability to programatically create various proprietary password hashes. So far this is just sha512 from crypt() and MySql's PASSWORD(). If you have/know of others please let me know! (or just make a pull request ;D)
141 changes: 141 additions & 0 deletions ShaCrypt.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package passwords

import (
"crypto/sha512"
"errors"
"math/big"
)

const (
b64String = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
sigBytes = 86
)

var (
// we have to re-arrange the hash towards the end, this is the new order (space efficient BECAUSE I CAN)
newOrder = [64]uint8{
63, 62, 20, 41, 40, 61, 19, 18, 39, 60, 59, 17, 38, 37, 58, 16, 15, 36, 57, 56, 14, 35,
34, 55, 13, 12, 33, 54, 53, 11, 32, 31, 52, 10, 9, 30, 51, 50, 8, 29, 28, 49, 7,
6, 27, 48, 47, 5, 26, 25, 46, 4, 3, 24, 45, 44, 2, 23, 22, 43, 1, 0, 21, 42}
big63 = big.NewInt(63)
)

// Creates a unix password string using sha512
func ShaCrypt(pass, salt string) (string, error) {
// Get the length (we need this later)
passLen := len(pass)
saltLen := len(salt)

// Salt must be the right length
if saltLen < 8 || saltLen > 16 {
return "", errors.New("arg 2 must be between 8 and 16 bytes long")
}

// Create the hasher (unique per instance to be thread safe :D)
hasher := sha512.New()
hashLen := hasher.Size()

// Change to bytes, more naitive
passByte := []byte(pass)
saltByte := []byte(salt)

// Create the 'alternate'
hasher.Write(passByte)
hasher.Write(saltByte)
hasher.Write(passByte)
alternate := hasher.Sum(nil)
hasher.Reset()

// Write the initial password and salt
hasher.Write(passByte)
hasher.Write(saltByte)

// Write one byte of 'alternate' for every byte of the password
for i := passLen; i != 0; {
var n int
if i > hashLen {
n, _ = hasher.Write(alternate)
} else {
n, _ = hasher.Write(alternate[:i])
}
i -= n
}

// Alternate between the password and the alternate for the length of the password
for i := passLen; i != 0; i >>= 1 {
if i&1 == 1 {
hasher.Write(alternate)
} else {
hasher.Write(passByte)
}
}

// Now we have our intermediate :D (re-using variables tho {this is for you gc :3})
alternate = hasher.Sum(nil)
hasher.Reset()

// Get value of the first byte
firstByte := int(alternate[0])

// Get first passLen bytes of hash of password repeated passLen times
for i := 0; i < passLen; i++ {
hasher.Write(passByte)
}
pHash := hasher.Sum(nil)[:passLen]
hasher.Reset()

// Get the first saltLen bytes of hash of salt repeated (first byte of pass) + 16 times
for i := 0; i < firstByte+16; i++ {
hasher.Write(saltByte)
}
sHash := hasher.Sum(nil)[:saltLen]
// We reset at the start on the next one (to avoid an extra call to reset), so we can skip this one
//hasher.Reset()

// Loop 5k times, on some do some things, others do others
for i := 0; i < 5000; i++ {
hasher.Reset()
if i&1 == 1 {
hasher.Write(pHash)
} else {
hasher.Write(alternate)
}
if i%3 != 0 {
hasher.Write(sHash)
}
if i%7 != 0 {
hasher.Write(pHash)
}
if i&1 == 1 {
hasher.Write(alternate)
} else {
hasher.Write(pHash)
}
alternate = hasher.Sum(nil)
}
// Not using this again, so no need to reset
//hasher.Reset()

// Now re-arrange the intermediate
intermediate := make([]byte, hashLen)
for i, new := range newOrder {
intermediate[i] = alternate[new]
}

// I THINK it's a normal b64 encoding (with the altered order of bytes used, see b64String)
// but with the order the 6-bits are read reversed. That is if my understanding of b64 encoding
// is correct. Bellow does what is needed, and very efficiently. I'll look into reversing the
// order and using the base64 package later.
asInt := big.NewInt(0)
asInt.SetBytes(intermediate)
rem := big.NewInt(0)
realPass := make([]byte, sigBytes)
for i := 0; i < sigBytes; i++ {
rem.And(asInt, big63)
realPass[i] = b64String[rem.Int64()]
asInt.Rsh(asInt, 6)
}

// Done!
return "$6$" + salt + "$" + string(realPass), nil
}
149 changes: 149 additions & 0 deletions reference.bash
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
#!/bin/bash
# sha512-crypt for GNU and Bash
# By Vidar 'koala_man' Holen

## This is the script that I used as reference to reverse engineer crypt(), at least for sha512

b64="./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"

stringToNumber() {
expression=0
for((i=0; i<${#1}; i++))
do
expression=$(printf '(%s)*256+%d' "$expression" "'${1:$i:1}")
done
bc <<< "$expression"
}

# Turn some string into a \xd4\x1d hex string
stringToHex() {
for((i=0; i<${#1}; i++))
do
printf '\\x%x' "'${1:i:1}"
done
}

# Turn stdin into a \xd4\x1d style sha512 hash
sha512hex() {
sum=$(sha512sum)
read sum rest <<< "$sum" # remove trailing dash
hex=$(sed 's/../\\x&/g' <<< "$sum")
echo "$hex"
}

# Turn an integer into a crypt base64 string with n characters
### Take an number ($1) (representing a binary value), and return the first n ($2) b64 bytes of it
intToBase64() {
number=$1
n=$2
for((j=0; j<n; j++))
do
digit=$(bc <<< "$number % 64")
number=$(bc <<< "$number / 64")
echo -n "${b64:digit:1}"
done
}

# From hex string $1, get the bytes indexed by $2, $3 ..
### This does the re-arraging of the hash
getBytes() {
num=$1
shift
### This is a bashism, for i loops over $1 with shift
for i
do
echo -n "${num:$((i*4)):4}"
done
}


### converts a hex string to a number, the tr capitalizes it, sed removes the \x before the hex
hexToInt() {
{
echo 'ibase=16;'
tr a-f A-F <<< "$1" | sed -e 's/\\x//g'
} | bc
}

base64EncodeBytes() {
n=$1
shift
bytes=$(getBytes "$@")
int=$(hexToInt "$bytes")
intToBase64 "$int" "$n"
}

doHash() {
password="$1"
passwordLength=$(printf "$password" | wc -c)
salt="$2"
saltLength=$(printf "$salt" | wc -c)
magic="$3"
[[ -z $magic ]] && magic='$6$'

salt=${salt#$magic}
salt=${salt:0:64} # 16 first bytes

intermediate=$(
{
# Start intermediate result
printf "$password$salt"
# compute a separate sha512 sum
alternate=$(printf "$password$salt$password" | sha512hex)
# Add one byte from alternate for each character in the password. Wtf?
while true; do printf "$alternate"; done | head -c "$passwordLength"
# For every 1 bit in the key length, add the alternate sum
# Otherwise add the entire key (unlike MD5-crypt)
for ((i=$passwordLength; i != 0; i>>=1))
do
if (( i & 1 ))
then
printf "$alternate"
else
printf "$password"
fi
done
} | sha512hex
)
firstByte=$(hexToInt $(getBytes "$intermediate" 0))

p_bytes=$(for((i=0; i<$passwordLength; i++)); do printf "$password"; done | sha512hex | head -c $((passwordLength*4)) )
s_bytes=$(for((i=0; i<16+${firstByte}; i++)); do printf "$salt"; done | sha512hex | head -c $((saltLength*4)) )


for((i=0; i<5000; i++))
do
intermediate=$({
(( i & 1 )) && printf "$p_bytes" || printf "$intermediate"
(( i % 3 )) && printf "$s_bytes"
(( i % 7 )) && printf "$p_bytes"
(( i & 1 )) && printf "$intermediate" || printf "$p_bytes"
} | sha512hex)
done

# Rearrange the bytes and crypt-base64 encode them
hex=$(base64EncodeBytes 86 "$intermediate" \
63 62 20 41 40 61 19 18 39 60 59 17 38 37 58 16 15 36 57 56 14 35 \
34 55 13 12 33 54 53 11 32 31 52 10 9 30 51 50 8 29 28 49 7 \
6 27 48 47 5 26 25 46 4 3 24 45 44 2 23 22 43 1 0 21 42)

printf "%s$salt\$%s\n" "$magic" "$hex"

}


if [[ $# < 1 ]]
then
echo "Usage: $0 password [salt]" >&2
exit 1
fi

password=$(stringToHex "$1")
salt=$(stringToHex "$2")
[[ -z $salt ]] && salt=$(tr -cd 'a-zA-Z0-9' < /dev/urandom | head -c 16)

doHash "$password" "$salt" '$6$'

0 comments on commit fec39e5

Please sign in to comment.