mirror of
https://github.com/PretendoNetwork/nex-go.git
synced 2025-04-02 11:02:14 -04:00
This requires a lot of refactor but it brings back the option of getting events when a connection disconnects.
202 lines
5.6 KiB
Go
202 lines
5.6 KiB
Go
package nex
|
|
|
|
import (
|
|
"crypto/hmac"
|
|
"crypto/md5"
|
|
"crypto/rand"
|
|
"crypto/rc4"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/PretendoNetwork/nex-go/types"
|
|
)
|
|
|
|
// KerberosEncryption is a struct representing a Kerberos encryption utility
|
|
type KerberosEncryption struct {
|
|
key []byte
|
|
}
|
|
|
|
// Validate checks the integrity of the given buffer by verifying the HMAC checksum
|
|
func (ke *KerberosEncryption) Validate(buffer []byte) bool {
|
|
data := buffer[:len(buffer)-0x10]
|
|
checksum := buffer[len(buffer)-0x10:]
|
|
mac := hmac.New(md5.New, ke.key)
|
|
|
|
mac.Write(data)
|
|
|
|
return hmac.Equal(checksum, mac.Sum(nil))
|
|
}
|
|
|
|
// Decrypt decrypts the provided buffer if it passes the integrity check
|
|
func (ke *KerberosEncryption) Decrypt(buffer []byte) ([]byte, error) {
|
|
if !ke.Validate(buffer) {
|
|
return nil, errors.New("Invalid Kerberos checksum (incorrect password)")
|
|
}
|
|
|
|
cipher, err := rc4.NewCipher(ke.key)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
decrypted := make([]byte, len(buffer)-0x10)
|
|
|
|
cipher.XORKeyStream(decrypted, buffer[:len(buffer)-0x10])
|
|
|
|
return decrypted, nil
|
|
}
|
|
|
|
// Encrypt encrypts the given buffer and appends an HMAC checksum for integrity
|
|
func (ke *KerberosEncryption) Encrypt(buffer []byte) []byte {
|
|
cipher, _ := rc4.NewCipher(ke.key)
|
|
encrypted := make([]byte, len(buffer))
|
|
|
|
cipher.XORKeyStream(encrypted, buffer)
|
|
|
|
mac := hmac.New(md5.New, ke.key)
|
|
|
|
mac.Write(encrypted)
|
|
|
|
checksum := mac.Sum(nil)
|
|
|
|
return append(encrypted, checksum...)
|
|
}
|
|
|
|
// NewKerberosEncryption creates a new KerberosEncryption instance with the given key.
|
|
func NewKerberosEncryption(key []byte) *KerberosEncryption {
|
|
return &KerberosEncryption{key: key}
|
|
}
|
|
|
|
// KerberosTicket represents a ticket granting a user access to a secure server
|
|
type KerberosTicket struct {
|
|
SessionKey []byte
|
|
TargetPID *types.PID
|
|
InternalData *types.Buffer
|
|
}
|
|
|
|
// Encrypt writes the ticket data to the provided stream and returns the encrypted byte slice
|
|
func (kt *KerberosTicket) Encrypt(key []byte, stream *ByteStreamOut) ([]byte, error) {
|
|
encryption := NewKerberosEncryption(key)
|
|
|
|
stream.Grow(int64(len(kt.SessionKey)))
|
|
stream.WriteBytesNext(kt.SessionKey)
|
|
|
|
kt.TargetPID.WriteTo(stream)
|
|
kt.InternalData.WriteTo(stream)
|
|
|
|
return encryption.Encrypt(stream.Bytes()), nil
|
|
}
|
|
|
|
// NewKerberosTicket returns a new Ticket instance
|
|
func NewKerberosTicket() *KerberosTicket {
|
|
return &KerberosTicket{}
|
|
}
|
|
|
|
// KerberosTicketInternalData holds the internal data for a kerberos ticket to be processed by the server
|
|
type KerberosTicketInternalData struct {
|
|
Server *PRUDPServer // TODO - Remove this dependency and make a settings struct
|
|
Issued *types.DateTime
|
|
SourcePID *types.PID
|
|
SessionKey []byte
|
|
}
|
|
|
|
// Encrypt writes the ticket data to the provided stream and returns the encrypted byte slice
|
|
func (ti *KerberosTicketInternalData) Encrypt(key []byte, stream *ByteStreamOut) ([]byte, error) {
|
|
ti.Issued.WriteTo(stream)
|
|
ti.SourcePID.WriteTo(stream)
|
|
|
|
stream.Grow(int64(len(ti.SessionKey)))
|
|
stream.WriteBytesNext(ti.SessionKey)
|
|
|
|
data := stream.Bytes()
|
|
|
|
if ti.Server.KerberosTicketVersion == 1 {
|
|
ticketKey := make([]byte, 16)
|
|
_, err := rand.Read(ticketKey)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Failed to generate ticket key. %s", err.Error())
|
|
}
|
|
|
|
hash := md5.Sum(append(key, ticketKey...))
|
|
finalKey := hash[:]
|
|
|
|
encryption := NewKerberosEncryption(finalKey)
|
|
|
|
encrypted := encryption.Encrypt(data)
|
|
|
|
finalStream := NewByteStreamOut(stream.LibraryVersions, stream.Settings)
|
|
|
|
ticketBuffer := types.NewBuffer(ticketKey)
|
|
encryptedBuffer := types.NewBuffer(encrypted)
|
|
|
|
ticketBuffer.WriteTo(finalStream)
|
|
encryptedBuffer.WriteTo(finalStream)
|
|
|
|
return finalStream.Bytes(), nil
|
|
}
|
|
|
|
encryption := NewKerberosEncryption([]byte(key))
|
|
|
|
return encryption.Encrypt(data), nil
|
|
}
|
|
|
|
// Decrypt decrypts the given data and populates the struct
|
|
func (ti *KerberosTicketInternalData) Decrypt(stream *ByteStreamIn, key []byte) error {
|
|
if ti.Server.KerberosTicketVersion == 1 {
|
|
ticketKey := types.NewBuffer(nil)
|
|
if err := ticketKey.ExtractFrom(stream); err != nil {
|
|
return fmt.Errorf("Failed to read Kerberos ticket internal data key. %s", err.Error())
|
|
}
|
|
|
|
data := types.NewBuffer(nil)
|
|
if err := ticketKey.ExtractFrom(stream); err != nil {
|
|
return fmt.Errorf("Failed to read Kerberos ticket internal data. %s", err.Error())
|
|
}
|
|
|
|
hash := md5.Sum(append(key, ticketKey.Value...))
|
|
key = hash[:]
|
|
|
|
stream = NewByteStreamIn(data.Value, stream.LibraryVersions, stream.Settings)
|
|
}
|
|
|
|
encryption := NewKerberosEncryption(key)
|
|
|
|
decrypted, err := encryption.Decrypt(stream.Bytes())
|
|
if err != nil {
|
|
return fmt.Errorf("Failed to decrypt Kerberos ticket internal data. %s", err.Error())
|
|
}
|
|
|
|
stream = NewByteStreamIn(decrypted, stream.LibraryVersions, stream.Settings)
|
|
|
|
timestamp := types.NewDateTime(0)
|
|
if err := timestamp.ExtractFrom(stream); err != nil {
|
|
return fmt.Errorf("Failed to read Kerberos ticket internal data timestamp %s", err.Error())
|
|
}
|
|
|
|
userPID := types.NewPID(0)
|
|
if err := userPID.ExtractFrom(stream); err != nil {
|
|
return fmt.Errorf("Failed to read Kerberos ticket internal data user PID %s", err.Error())
|
|
}
|
|
|
|
ti.Issued = timestamp
|
|
ti.SourcePID = userPID
|
|
ti.SessionKey = stream.ReadBytesNext(int64(ti.Server.SessionKeyLength))
|
|
|
|
return nil
|
|
}
|
|
|
|
// NewKerberosTicketInternalData returns a new KerberosTicketInternalData instance
|
|
func NewKerberosTicketInternalData(server *PRUDPServer) *KerberosTicketInternalData {
|
|
return &KerberosTicketInternalData{Server: server}
|
|
}
|
|
|
|
// DeriveKerberosKey derives a users kerberos encryption key based on their PID and password
|
|
func DeriveKerberosKey(pid *types.PID, password []byte) []byte {
|
|
key := password
|
|
|
|
for i := 0; i < 65000+int(pid.Value())%1024; i++ {
|
|
hash := md5.Sum(key)
|
|
key = hash[:]
|
|
}
|
|
|
|
return key
|
|
}
|