go-passwd/nt_hash.go
2024-09-07 18:43:05 -05:00

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
}