Skip to content

Commit

Permalink
add ssha functions
Browse files Browse the repository at this point in the history
  • Loading branch information
Tommi2Day committed May 24, 2024
1 parent bc274fa commit ab48483
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 1 deletion.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Go Library

## [v1.13.2 - 2024-05-24]
### New
- pwlib: add SSHA functions
### Changed
- update dependencies

## [v1.13.1 - 2024-04-23]
### New
- maillib: add SetAuthMethod function
Expand Down
2 changes: 1 addition & 1 deletion Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ this is a collection of my often used functions
- password generation,
- password storing and handling with RSA, Openssl, GoPass, Amazon KMS and Hashicorp Vault
- totp generation
- scram hashing
- scram(e.g.for postgresql) and ssha(e.g for LDAP userPassword) hashing
- tools: collection of often used tools as build loader
- dblib: db related functions, esp. for oracle and tns handling
- maillib: function to send Mails
Expand Down
64 changes: 64 additions & 0 deletions pwlib/ssha.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package pwlib

// https://ferdinand-neman.medium.com/ssha-password-hash-with-golang-7d79d792bd3d
import (
"bytes"
//nolint gosec
"crypto/sha1"
"encoding/base64"
"fmt"
"strings"
)

type SSHAEncoder struct {
}

// Encode encodes the []byte of raw password
func (enc SSHAEncoder) Encode(rawPassPhrase []byte) ([]byte, error) {
salt, err := makeSSHASalt()
if err != nil {
return []byte{}, err
}
hash := makeSSHAHash(rawPassPhrase, salt)
b64 := base64.StdEncoding.EncodeToString(hash)
return []byte(fmt.Sprintf("{SSHA}%s", b64)), nil
}

// Matches matches the encoded password and the raw password
func (enc SSHAEncoder) Matches(encodedPassPhrase, rawPassPhrase []byte) bool {
// strip the {SSHA}
eppS := string(encodedPassPhrase)
if strings.HasPrefix(string(encodedPassPhrase), "{SSHA}") {
eppS = string(encodedPassPhrase)[6:]
}
hash, err := base64.StdEncoding.DecodeString(eppS)
if err != nil {
return false
}
salt := hash[len(hash)-4:]
//nolint gosec
sha := sha1.New()
_, _ = sha.Write(rawPassPhrase)
_, _ = sha.Write(salt)
sum := sha.Sum(nil)

// compare without the last 4 bytes of the hash with the calculated hash
return bytes.Equal(sum, hash[:len(hash)-4])
}

// makeSSHASalt make 4Byte salt for SSHA hashing
func makeSSHASalt() (salt []byte, err error) {
salt, err = makeSalt(4)
return
}

// makeSSHAHash make hasing using SHA-1 with salt. This is not the final output though. You need to append {SSHA} string with base64 of this hash.
func makeSSHAHash(passphrase, salt []byte) []byte {
//nolint gosec
sha := sha1.New()
_, _ = sha.Write(passphrase)
_, _ = sha.Write(salt)

h := sha.Sum(nil)
return append(h, salt...)
}
42 changes: 42 additions & 0 deletions pwlib/ssha_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package pwlib

import (
"testing"

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

var sshaPlain = []byte("password")

func TestSSHA(t *testing.T) {
t.Run("TestSSHAEncoder_Encode", func(t *testing.T) {
enc := SSHAEncoder{}
encoded, err := enc.Encode(sshaPlain)
require.NoError(t, err)
assert.Contains(t, string(encoded), "{SSHA}")
assert.Greater(t, len(encoded), 6)
t.Log(string(encoded))
})

t.Run("TestSSHAEncoder_Matches", func(t *testing.T) {
enc := SSHAEncoder{}
encoded, err := enc.Encode(sshaPlain)
require.NoError(t, err)
assert.True(t, enc.Matches(encoded, sshaPlain))
})

t.Run("TestMakeSSHAHash", func(t *testing.T) {
salt, err := makeSSHASalt()
require.NoError(t, err)
hash := makeSSHAHash(sshaPlain, salt)
assert.Equal(t, 24, len(hash))
t.Log(hash)
})

t.Run("TestMakeSalt", func(t *testing.T) {
salt, err := makeSSHASalt()
require.NoError(t, err)
assert.Equal(t, 4, len(salt))
})
}

0 comments on commit ab48483

Please sign in to comment.