Commit f9dddc49 authored by Balasankar "Balu" C's avatar Balasankar "Balu" C

New upstream version 0.0~git20160922.7680470

parents
language: go
go:
- 1.6
- tip
The MIT License (MIT)
Copyright (c) 2016 Jeroen Simonetti
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
[![GoDoc](https://godoc.org/github.com/jsimonetti/pwscheme?status.svg)](https://godoc.org/github.com/jsimonetti/pwscheme)
[![Travis](https://api.travis-ci.org/jsimonetti/pwscheme.svg?branch=master)](https://travis-ci.org/jsimonetti/pwscheme)
# pwscheme
Golang package defining password schemes
Supported schemes
- {SSHA} Salted SHA1
- {SSHA256} Salted SHA256
- {SSHA512} Salted SHA512
- {MD5-CRYPT} Crypt with MD5
Docs: https://godoc.org/github.com/jsimonetti/pwscheme
// Package md5crypt provides functions to generate and validate {MD5-CRYPT} styled
// password schemes.
// The method used is compatible with libc crypt used in /etc/shadow
package md5crypt
import (
"crypto/md5"
"crypto/rand"
"errors"
"fmt"
"strings"
)
const itoa64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
var md5CryptSwaps = [16]int{12, 6, 0, 13, 7, 1, 14, 8, 2, 15, 9, 3, 5, 10, 4, 11}
var magic = []byte("$1$")
// ErrNotMd5cryptPassword occurs when Validate receives a non-SSHA hash
var ErrNotMd5cryptPassword = errors.New("string is not a MD5-CRYPT password")
// ErrNotMatching occurs when the given password and hash do not match
var ErrNotMatching = errors.New("hash does not match password")
// ErrSaltLengthInCorrect occurs when the given salt is not of the correct
// length
var ErrSaltLengthInCorrect = errors.New("salt length incorrect")
// Generate encrypts a password with a random salt of definable length and
// returns the {MD5-CRYPT} encoding of the password
func Generate(password string, length uint8) (string, error) {
if length > 8 || length < 1 {
return "", ErrSaltLengthInCorrect
}
salt := make([]byte, length)
_, err := rand.Read(salt)
if err != nil {
return "", err
}
hash := fmt.Sprintf("{MD5-CRYPT}%s", crypt([]byte(password), salt))
return hash, nil
}
// Validate compares a given password with a {SSHA} encoded password
// Returns true is they match or an error otherwise
func Validate(password string, hash string) (bool, error) {
if len(hash) < 15 || string(hash[0:14]) != "{MD5-CRYPT}$1$" {
return false, ErrNotMd5cryptPassword
}
data := strings.Split(hash[14:], "$")
newhash := crypt([]byte(password), []byte(data[0]))
if string(newhash) == hash[11:] {
return true, nil
}
return false, ErrNotMatching
}
func crypt(password, salt []byte) []byte {
d := md5.New()
d.Write(password)
d.Write(magic)
d.Write(salt)
d2 := md5.New()
d2.Write(password)
d2.Write(salt)
d2.Write(password)
for i, mixin := 0, d2.Sum(nil); i < len(password); i++ {
d.Write([]byte{mixin[i%16]})
}
for i := len(password); i != 0; i >>= 1 {
if i&1 == 0 {
d.Write([]byte{password[0]})
} else {
d.Write([]byte{0})
}
}
final := d.Sum(nil)
for i := 0; i < 1000; i++ {
d2 := md5.New()
if i&1 == 0 {
d2.Write(final)
} else {
d2.Write(password)
}
if i%3 != 0 {
d2.Write(salt)
}
if i%7 != 0 {
d2.Write(password)
}
if i&1 == 0 {
d2.Write(password)
} else {
d2.Write(final)
}
final = d2.Sum(nil)
}
result := make([]byte, 0, 22)
v := uint(0)
bits := uint(0)
for _, i := range md5CryptSwaps {
v |= (uint(final[i]) << bits)
for bits = bits + 8; bits > 6; bits -= 6 {
result = append(result, itoa64[v&0x3f])
v >>= 6
}
}
result = append(result, itoa64[v&0x3f])
return append(append(append(magic, salt...), '$'), result...)
}
package md5crypt_test
import (
"testing"
"github.com/jsimonetti/pwscheme/md5crypt"
)
func TestValidPassword(t *testing.T) {
pass := "test123"
hash := "{MD5-CRYPT}$1$UNq3KKXM$hsrKHTk9BaYGZwafpr4K80"
if res, err := md5crypt.Validate(pass, hash); err != nil || res != true {
t.Errorf("Valid password fails validation: %s", err)
}
}
func TestInValidPassword(t *testing.T) {
pass := "test12"
hash := "{MD5-CRYPT}$1$UNq3KKXM$hsrKHTk9BaYGZwafpr4K80"
if res, err := md5crypt.Validate(pass, hash); res != false {
t.Errorf("Invalid password passes validation: %s", err)
}
}
func TestGenerate4(t *testing.T) {
pass := "test123"
var hash string
var err error
var res bool
if hash, err = md5crypt.Generate(pass, 4); err != nil {
t.Errorf("Generate password fails: %s", err)
return
}
if res, err = md5crypt.Validate(pass, hash); err != nil || res != true {
t.Errorf("Generated hash can not be validated: %s", err)
}
}
func TestGenerate8(t *testing.T) {
pass := "test123"
var hash string
var err error
var res bool
if hash, err = md5crypt.Generate(pass, 8); err != nil {
t.Errorf("Generate password fails: %s", err)
return
}
if res, err = md5crypt.Validate(pass, hash); err != nil || res != true {
t.Errorf("Generated hash can not be validated: %s", err)
}
}
func TestGenerate0(t *testing.T) {
if _, err := md5crypt.Generate("", 0); err != md5crypt.ErrSaltLengthInCorrect {
t.Errorf("Generated hash with too short salt did not fail")
}
}
func TestGenerate9(t *testing.T) {
if _, err := md5crypt.Generate("", 9); err != md5crypt.ErrSaltLengthInCorrect {
t.Errorf("Generated hash with too long salt did not fail")
}
}
// Package ssha provides functions to generate and validate {SSHA} styled
// password schemes.
// The method used is defined in RFC 2307 and uses a salted SHA1 secure hashing
// algorithm
package ssha
import (
"crypto/rand"
"crypto/sha1"
"encoding/base64"
"errors"
"fmt"
)
// ErrNotSshaPassword occurs when Validate receives a non-SSHA hash
var ErrNotSshaPassword = errors.New("string is not a SSHA hashed password")
// ErrBase64DecodeFailed occurs when the given hash cannot be decode
var ErrBase64DecodeFailed = errors.New("base64 decode of hash failed")
// ErrNotMatching occurs when the given password and hash do not match
var ErrNotMatching = errors.New("hash does not match password")
// Generate encrypts a password with a random salt of definable length and
// returns the {SSHA} encoding of the password
func Generate(password string, length uint8) (string, error) {
salt := make([]byte, length)
_, err := rand.Read(salt)
if err != nil {
return "", err
}
hash := createHash(password, salt)
ret := fmt.Sprintf("{SSHA}%s", base64.StdEncoding.EncodeToString(hash))
return ret, nil
}
// Validate compares a given password with a {SSHA} encoded password
// Returns true is they match or an error otherwise
func Validate(password string, hash string) (bool, error) {
if len(hash) < 7 || string(hash[0:6]) != "{SSHA}" {
return false, ErrNotSshaPassword
}
data, err := base64.StdEncoding.DecodeString(hash[6:])
if len(data) < 21 || err != nil {
return false, ErrBase64DecodeFailed
}
newhash := createHash(password, data[20:])
hashedpw := base64.StdEncoding.EncodeToString(newhash)
if hashedpw == hash[6:] {
return true, nil
}
return false, ErrNotMatching
}
// createHash appends password and salt together to a byte array
func createHash(password string, salt []byte) []byte {
pass := []byte(password)
str := append(pass[:], salt[:]...)
sum := sha1.Sum(str)
result := append(sum[:], salt[:]...)
return result
}
package ssha_test
import (
"testing"
"github.com/jsimonetti/pwscheme/ssha"
)
func TestValidPassword(t *testing.T) {
pass := "test123"
hash := "{SSHA}JFZFs0oHzxbMwkSJmYVeI8MnTDy/276a"
if res, err := ssha.Validate(pass, hash); err != nil || res != true {
t.Errorf("Valid password fails validation: %s", err)
}
}
func TestInValidPassword(t *testing.T) {
pass := "test12"
hash := "{SSHA}JFZFs0oHzxbMwkSJmYVeI8MnTDy/276a"
if res, err := ssha.Validate(pass, hash); res != false {
t.Errorf("Invalid password passes validation: %s", err)
}
}
func TestGenerate4(t *testing.T) {
pass := "test123"
var hash string
var err error
var res bool
if hash, err = ssha.Generate(pass, 4); err != nil {
t.Errorf("Generate password fails: %s", err)
return
}
if res, err = ssha.Validate(pass, hash); err != nil || res != true {
t.Errorf("Generated hash can not be validated: %s", err)
}
}
func TestGenerate8(t *testing.T) {
pass := "test123"
var hash string
var err error
var res bool
if hash, err = ssha.Generate(pass, 8); err != nil {
t.Errorf("Generate password fails: %s", err)
return
}
if res, err = ssha.Validate(pass, hash); err != nil || res != true {
t.Errorf("Generated hash can not be validated: %s", err)
}
}
// Package ssha256 provides functions to generate and validate {SSHA256} styled
// password schemes.
// The method used is defined in RFC 2307 and uses a salted SHA256 secure hashing
// algorithm
package ssha256
import (
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"errors"
"fmt"
)
// ErrNotSshaPassword occurs when Validate receives a non-SSHA256 hash
var ErrNotSshaPassword = errors.New("string is not a SSHA256 hashed password")
// ErrBase64DecodeFailed occurs when the given hash cannot be decode
var ErrBase64DecodeFailed = errors.New("base64 decode of hash failed")
// ErrNotMatching occurs when the given password and hash do not match
var ErrNotMatching = errors.New("hash does not match password")
// Generate encrypts a password with a random salt of definable length and
// returns the {SSHA256} encoding of the password
func Generate(password string, length uint8) (string, error) {
salt := make([]byte, length)
_, err := rand.Read(salt)
if err != nil {
return "", err
}
hash := createHash(password, salt)
ret := fmt.Sprintf("{SSHA256}%s", base64.StdEncoding.EncodeToString(hash))
return ret, nil
}
// Validate compares a given password with a {SSHA256} encoded password
// Returns true is they match or an error otherwise
func Validate(password string, hash string) (bool, error) {
if len(hash) < 10 || string(hash[0:9]) != "{SSHA256}" {
return false, ErrNotSshaPassword
}
data, err := base64.StdEncoding.DecodeString(hash[9:])
if len(data) < 33 || err != nil {
return false, ErrBase64DecodeFailed
}
newhash := createHash(password, data[32:])
hashedpw := base64.StdEncoding.EncodeToString(newhash)
if hashedpw == hash[9:] {
return true, nil
}
return false, ErrNotMatching
}
// createHash appends password and salt together to a byte array
func createHash(password string, salt []byte) []byte {
pass := []byte(password)
str := append(pass[:], salt[:]...)
sum := sha256.Sum256(str)
result := append(sum[:], salt[:]...)
return result
}
package ssha256_test
import (
"testing"
"github.com/jsimonetti/pwscheme/ssha256"
)
func TestValidPassword(t *testing.T) {
pass := "test123"
hash := "{SSHA256}czO44OTV17PcF1cRxWrLZLy9xHd7CWyVYplr1rOhuMlx/7IK"
if res, err := ssha256.Validate(pass, hash); err != nil || res != true {
t.Errorf("Valid password fails validation: %s", err)
}
}
func TestInValidPassword(t *testing.T) {
pass := "test12"
hash := "{SSHA256}czO44OTV17PcF1cRxWrLZLy9xHd7CWyVYplr1rOhuMlx/7IK"
if res, err := ssha256.Validate(pass, hash); res != false {
t.Errorf("Invalid password passes validation: %s", err)
}
}
func TestGenerate4(t *testing.T) {
pass := "test123"
var hash string
var err error
var res bool
if hash, err = ssha256.Generate(pass, 4); err != nil {
t.Errorf("Generate password fails: %s", err)
return
}
if res, err = ssha256.Validate(pass, hash); err != nil && res != false {
t.Errorf("Generated hash can not be validated: %s", err)
}
}
func TestGenerate8(t *testing.T) {
pass := "test123"
var hash string
var err error
var res bool
if hash, err = ssha256.Generate(pass, 8); err != nil {
t.Errorf("Generate password fails: %s", err)
return
}
if res, err = ssha256.Validate(pass, hash); err != nil && res != false {
t.Errorf("Generated hash can not be validated: %s", err)
}
}
// Package ssha512 provides functions to generate and validate {SSHA512} styled
// password schemes.
// The method used is defined in RFC 2307 and uses a salted SHA512 secure hashing
// algorithm
package ssha512
import (
"crypto/rand"
"crypto/sha512"
"encoding/base64"
"errors"
"fmt"
)
// ErrNotSshaPassword occurs when Validate receives a non-SSHA512 hash
var ErrNotSshaPassword = errors.New("string is not a SSHA512 hashed password")
// ErrBase64DecodeFailed occurs when the given hash cannot be decode
var ErrBase64DecodeFailed = errors.New("base64 decode of hash failed")
// ErrNotMatching occurs when the given password and hash do not match
var ErrNotMatching = errors.New("hash does not match password")
// Generate encrypts a password with a random salt of definable length and
// returns the {SSHA512} encoding of the password
func Generate(password string, length uint8) (string, error) {
salt := make([]byte, length)
_, err := rand.Read(salt)
if err != nil {
return "", err
}
hash := createHash(password, salt)
ret := fmt.Sprintf("{SSHA512}%s", base64.StdEncoding.EncodeToString(hash))
return ret, nil
}
// Validate compares a given password with a {SSHA512} encoded password
// Returns true is they match or an error otherwise
func Validate(password string, hash string) (bool, error) {
if len(hash) < 10 || string(hash[0:9]) != "{SSHA512}" {
return false, ErrNotSshaPassword
}
data, err := base64.StdEncoding.DecodeString(hash[9:])
if len(data) < 65 || err != nil {
return false, ErrBase64DecodeFailed
}
newhash := createHash(password, data[64:])
hashedpw := base64.StdEncoding.EncodeToString(newhash)
if hashedpw == hash[9:] {
return true, nil
}
return false, ErrNotMatching
}
// This function appends password and salt together to a byte array
func createHash(password string, salt []byte) []byte {
pass := []byte(password)
str := append(pass[:], salt[:]...)
sum := sha512.Sum512(str)
result := append(sum[:], salt[:]...)
return result
}
package ssha512_test
import (
"testing"
"github.com/jsimonetti/pwscheme/ssha512"
)
func TestValidPassword(t *testing.T) {
pass := "test123"
hash := "{SSHA512}xPUl/px+1cG55rUH4rzcwxdOIPSB2TingLpiJJumN2xyDWN4Ix1WQG3ihnvHaWUE8MYNkvMi5rf0C9NYixHsE6Yh59M="
if res, err := ssha512.Validate(pass, hash); err != nil || res != true {
t.Errorf("Valid password fails validation: %s", err)
}
}
func TestInValidPassword(t *testing.T) {
pass := "test12"
hash := "{SSHA512}xPUl/px+1cG55rUH4rzcwxdOIPSB2TingLpiJJumN2xyDWN4Ix1WQG3ihnvHaWUE8MYNkvMi5rf0C9NYixHsE6Yh59M="
if res, err := ssha512.Validate(pass, hash); res != false {
t.Errorf("Invalid password passes validation: %s", err)
}
}
func TestGenerate4(t *testing.T) {
pass := "test123"
var hash string
var err error
var res bool
if hash, err = ssha512.Generate(pass, 4); err != nil {
t.Errorf("Generate password fails: %s", err)
return
}
if res, err = ssha512.Validate(pass, hash); err != nil && res != false {
t.Errorf("Generated hash can not be validated: %s", err)
}
}
func TestGenerate8(t *testing.T) {
pass := "test123"
var hash string
var err error
var res bool
if hash, err = ssha512.Generate(pass, 8); err != nil {
t.Errorf("Generate password fails: %s", err)
return
}
if res, err = ssha512.Validate(pass, hash); err != nil && res != false {
t.Errorf("Generated hash can not be validated: %s", err)
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment