85 lines
1.8 KiB
Go
85 lines
1.8 KiB
Go
|
package passwd
|
||
|
|
||
|
import (
|
||
|
"encoding/binary"
|
||
|
"encoding/hex"
|
||
|
"unicode/utf16"
|
||
|
"unicode/utf8"
|
||
|
|
||
|
"golang.org/x/crypto/md4"
|
||
|
)
|
||
|
|
||
|
type NTHash struct {
|
||
|
Passwd
|
||
|
}
|
||
|
|
||
|
// Make NTHash password interface.
|
||
|
func NewNTPasswd() PasswdInterface {
|
||
|
m := new(NTHash)
|
||
|
m.Magic = NT_HASH_MAGIC
|
||
|
// NT hashes has no salt, so we disable it.
|
||
|
m.SaltLength = -1
|
||
|
// Set the interface to allow parents to call overriden functions.
|
||
|
m.i = m
|
||
|
return m
|
||
|
}
|
||
|
|
||
|
// Encode UTF-8 bytes to UCS-2LE bytes.
|
||
|
// The NT hash uses UCS-2LE, so we need to convert for compatibility.
|
||
|
func (a *NTHash) UTF8ToUCS2LE(src []byte) []byte {
|
||
|
// If there is no source data, return nil.
|
||
|
if len(src) == 0 {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Convert bytes to UTF-8 runes.
|
||
|
var runes []rune
|
||
|
for len(src) > 0 {
|
||
|
r, size := utf8.DecodeRune(src)
|
||
|
runes = append(runes, r)
|
||
|
src = src[size:]
|
||
|
}
|
||
|
|
||
|
// Re-encode UTF-8 to UTF-16.
|
||
|
u := utf16.Encode(runes)
|
||
|
|
||
|
// Setup new byte array to match length of UCS-2LE.
|
||
|
dst := make([]byte, len(u)*2)
|
||
|
|
||
|
// Index for inserting new bytes.
|
||
|
i := 0
|
||
|
|
||
|
// Convert each UTF-16 byte to UCS-2LE.
|
||
|
for _, r := range u {
|
||
|
binary.LittleEndian.PutUint16(dst[i:], r)
|
||
|
i += 2
|
||
|
}
|
||
|
return dst
|
||
|
}
|
||
|
|
||
|
// Hash an NT compatible hash.
|
||
|
func (a *NTHash) Hash(password []byte) (hash []byte) {
|
||
|
// Convert to UCS-2.
|
||
|
ucsPw := a.UTF8ToUCS2LE(password)
|
||
|
|
||
|
// Encoe MD4 hash with UCS-2LE bytes.
|
||
|
h := md4.New()
|
||
|
h.Write(ucsPw)
|
||
|
buf := h.Sum(nil)
|
||
|
|
||
|
// Hex encode MD4 hash.
|
||
|
dst := make([]byte, hex.EncodedLen(len(buf)))
|
||
|
hex.Encode(dst, buf)
|
||
|
|
||
|
// Make crypt compatible hash from encoded hash.
|
||
|
hash = append([]byte(a.Magic), '$')
|
||
|
hash = append(hash, dst...)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Override the hash with salt function with one that encodes the NT hash, ignoring the salt.
|
||
|
func (a *NTHash) HashPasswordWithSalt(password []byte, salt []byte) (hash []byte, err error) {
|
||
|
hash = a.Hash(password)
|
||
|
return
|
||
|
}
|