Files
rdplib/protocol/sec/sec.go
T

995 lines
29 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package sec
import (
"bytes"
"crypto/md5"
"crypto/rand"
"crypto/rc4"
"crypto/rsa"
"crypto/sha1"
"encoding/binary"
"errors"
"fmt"
"github.com/lunixbochs/struc"
"io"
"log/slog"
"unicode/utf16"
"git.zeroonesoft.cn/golib/rdplib/core"
"git.zeroonesoft.cn/golib/rdplib/emission"
"git.zeroonesoft.cn/golib/rdplib/protocol/lic"
"git.zeroonesoft.cn/golib/rdplib/protocol/nla"
"git.zeroonesoft.cn/golib/rdplib/protocol/t125"
"git.zeroonesoft.cn/golib/rdplib/protocol/t125/gcc"
)
// Pre-computed padding bytes used in MAC generation (avoids per-call allocations).
var (
macPad36 [40]byte
macPad5C [48]byte
)
func init() {
for i := range macPad36 {
macPad36[i] = 0x36
}
for i := range macPad5C {
macPad5C[i] = 0x5c
}
}
/**
* SecurityFlag
* @see http://msdn.microsoft.com/en-us/library/cc240579.aspx
*/
const (
EXCHANGE_PKT uint16 = 0x0001
TRANSPORT_REQ = 0x0002
TRANSPORT_RSP = 0x0004
ENCRYPT = 0x0008
RESET_SEQNO = 0x0010
IGNORE_SEQNO = 0x0020
INFO_PKT = 0x0040
LICENSE_PKT = 0x0080
LICENSE_ENCRYPT_CS = 0x0200
LICENSE_ENCRYPT_SC = 0x0200
REDIRECTION_PKT = 0x0400
SECURE_CHECKSUM = 0x0800
AUTODETECT_REQ = 0x1000
AUTODETECT_RSP = 0x2000
HEARTBEAT = 0x4000
FLAGSHI_VALID = 0x8000
)
const (
INFO_MOUSE uint32 = 0x00000001
INFO_DISABLECTRLALTDEL = 0x00000002
INFO_AUTOLOGON = 0x00000008
INFO_UNICODE = 0x00000010
INFO_MAXIMIZESHELL = 0x00000020
INFO_LOGONNOTIFY = 0x00000040
INFO_COMPRESSION = 0x00000080
INFO_ENABLEWINDOWSKEY = 0x00000100
INFO_REMOTECONSOLEAUDIO = 0x00002000
INFO_FORCE_ENCRYPTED_CS_PDU = 0x00004000
INFO_RAIL = 0x00008000
INFO_LOGONERRORS = 0x00010000
INFO_MOUSE_HAS_WHEEL = 0x00020000
INFO_PASSWORD_IS_SC_PIN = 0x00040000
INFO_NOAUDIOPLAYBACK = 0x00080000
INFO_USING_SAVED_CREDS = 0x00100000
INFO_AUDIOCAPTURE = 0x00200000
INFO_VIDEO_DISABLE = 0x00400000
INFO_CompressionTypeMask = 0x00001E00
// INFO_CompressionTypeMask 取值(MS-RDPBCGR 2.2.1.11.1.1,
// PACKET_COMPR_TYPE_*:4 位枚举,表示客户端支持的最高压缩包):
// 0x0=RDP4(8K MPPC) 0x1=RDP5(64K MPPC) 0x2=RDP6.0 0x3=RDP6.1
// (后两者是 MS-RDPEGDI 的独立压缩包,core 包未实现,不可声明)
INFO_CompressionTypeRDP5 = 0x00000200
)
const (
AF_INET uint16 = 0x00002
AF_INET6 = 0x0017
)
const (
PERF_DISABLE_WALLPAPER uint32 = 0x00000001
PERF_DISABLE_FULLWINDOWDRAG = 0x00000002
PERF_DISABLE_MENUANIMATIONS = 0x00000004
PERF_DISABLE_THEMING = 0x00000008
PERF_DISABLE_CURSOR_SHADOW = 0x00000020
PERF_DISABLE_CURSORSETTINGS = 0x00000040
PERF_ENABLE_FONT_SMOOTHING = 0x00000080
PERF_ENABLE_DESKTOP_COMPOSITION = 0x00000100
)
const (
FASTPATH_OUTPUT_SECURE_CHECKSUM = 0x1
FASTPATH_OUTPUT_ENCRYPTED = 0x2
)
type ClientAutoReconnect struct {
CbAutoReconnectLen uint16
CbLen uint32
Version uint32
LogonId uint32
SecVerifier []byte
}
func NewClientAutoReconnect(id uint32, random []byte) *ClientAutoReconnect {
return &ClientAutoReconnect{
CbAutoReconnectLen: 28,
CbLen: 28,
Version: 1,
LogonId: id,
SecVerifier: nla.HMAC_MD5(random, random),
}
}
type RDPExtendedInfo struct {
ClientAddressFamily uint16 `struc:"little"`
CbClientAddress uint16 `struc:"little,sizeof=ClientAddress"`
ClientAddress []byte `struc:"[]byte"`
CbClientDir uint16 `struc:"little,sizeof=ClientDir"`
ClientDir []byte `struc:"[]byte"`
ClientTimeZone []byte `struc:"[172]byte"`
ClientSessionId uint32 `struc:"litttle"`
PerformanceFlags uint32 `struc:"little"`
AutoReconnect *ClientAutoReconnect
// [MS-RDPBCGR] 2.2.1.11.1.1.1 中 cbAutoReconnectCookie 之后的 optional 字段链。
// Windows 8/2012+ 服务器按 mstsc 的完整布局解析,缺失时报
// ERRINFO_TIMEZONE_KEY_NAME_LENGTH_TOO_SHORT (0x112F) 并断开连接。
DynDSTTimeZoneKeyName []byte // UTF-16LE,不含结尾空字符
DynamicDaylightTimeDisabled uint16
}
func NewExtendedInfo(auto *ClientAutoReconnect) *RDPExtendedInfo {
return &RDPExtendedInfo{
ClientAddressFamily: AF_INET,
ClientAddress: []byte{0, 0},
ClientDir: []byte{0, 0},
ClientTimeZone: make([]byte, 172),
ClientSessionId: 0,
// 视觉全开:启用壁纸/整窗拖动/菜单动画(对齐 mstsc 默认体验档),
// 仅保留字体平滑与桌面合成增强
PerformanceFlags: (PERF_ENABLE_FONT_SMOOTHING |
PERF_ENABLE_DESKTOP_COMPOSITION),
AutoReconnect: auto,
}
}
func (o *RDPExtendedInfo) Serialize() []byte {
buff := &bytes.Buffer{}
core.WriteUInt16LE(o.ClientAddressFamily, buff)
core.WriteUInt16LE(uint16(len(o.ClientAddress)), buff)
core.WriteBytes(o.ClientAddress, buff)
core.WriteUInt16LE(uint16(len(o.ClientDir)), buff)
core.WriteBytes(o.ClientDir, buff)
core.WriteBytes(o.ClientTimeZone, buff)
core.WriteUInt32LE(o.ClientSessionId, buff)
core.WriteUInt32LE(o.PerformanceFlags, buff)
// cbAutoReconnectCookie 恒存在(无 cookie 时写 0),与 mstsc/FreeRDP 一致
if o.AutoReconnect != nil {
core.WriteUInt16LE(o.AutoReconnect.CbAutoReconnectLen, buff)
core.WriteUInt32LE(o.AutoReconnect.CbLen, buff)
core.WriteUInt32LE(o.AutoReconnect.Version, buff)
core.WriteUInt32LE(o.AutoReconnect.LogonId, buff)
core.WriteBytes(o.AutoReconnect.SecVerifier, buff)
} else {
core.WriteUInt16LE(0, buff)
}
// reserved1、reserved2、dynamic DST 字段链(FreeRDP info.c 同布局)
core.WriteUInt16LE(0, buff)
core.WriteUInt16LE(0, buff)
core.WriteUInt16LE(uint16(len(o.DynDSTTimeZoneKeyName)), buff)
core.WriteBytes(o.DynDSTTimeZoneKeyName, buff)
core.WriteUInt16LE(o.DynamicDaylightTimeDisabled, buff)
return buff.Bytes()
}
type RDPInfo struct {
CodePage uint32
Flag uint32
CbDomain uint16
CbUserName uint16
CbPassword uint16
CbAlternateShell uint16
CbWorkingDir uint16
Domain []byte
UserName []byte
Password []byte
AlternateShell []byte
WorkingDir []byte
ExtendedInfo *RDPExtendedInfo
}
func NewRDPInfo() *RDPInfo {
info := &RDPInfo{
Flag: INFO_MOUSE | INFO_UNICODE | INFO_MAXIMIZESHELL |
INFO_ENABLEWINDOWSKEY | INFO_DISABLECTRLALTDEL | INFO_MOUSE_HAS_WHEEL |
INFO_FORCE_ENCRYPTED_CS_PDU | INFO_AUTOLOGON,
Domain: []byte{0, 0},
UserName: []byte{0, 0},
Password: []byte{0, 0},
AlternateShell: []byte{0, 0},
WorkingDir: []byte{0, 0},
ExtendedInfo: NewExtendedInfo(nil),
}
// 批量压缩:声明 K64(64K 历史 MPPC)——core/mppc.go 解码器支持的变体。
// 传统位图管线(16/24/32bpp 位图模式)不做此协商时服务器发原始位图,
// 带宽会高一个数量级。
info.Flag |= INFO_COMPRESSION | INFO_CompressionTypeRDP5
return info
}
func (o *RDPInfo) SetClientAutoReconnect(auto *ClientAutoReconnect) {
o.ExtendedInfo.AutoReconnect = auto
}
func (o *RDPInfo) SetClientInfo() {
o.Flag |= INFO_LOGONNOTIFY | INFO_LOGONERRORS
}
func (o *RDPInfo) Serialize(hasExtended bool) []byte {
buff := &bytes.Buffer{}
core.WriteUInt32LE(o.CodePage, buff) // 0000000
core.WriteUInt32LE(o.Flag, buff) // 0530101
core.WriteUInt16LE(uint16(len(o.Domain)-2), buff) // 001c
core.WriteUInt16LE(uint16(len(o.UserName)-2), buff) // 0008
core.WriteUInt16LE(uint16(len(o.Password)-2), buff) //000c
core.WriteUInt16LE(uint16(len(o.AlternateShell)-2), buff) //0000
core.WriteUInt16LE(uint16(len(o.WorkingDir)-2), buff) //0000
core.WriteBytes(o.Domain, buff)
core.WriteBytes(o.UserName, buff)
core.WriteBytes(o.Password, buff)
core.WriteBytes(o.AlternateShell, buff)
core.WriteBytes(o.WorkingDir, buff)
if hasExtended {
core.WriteBytes(o.ExtendedInfo.Serialize(), buff)
}
return buff.Bytes()
}
type SecurityHeader struct {
securityFlag uint16
securityFlagHi uint16
}
func readSecurityHeader(r io.Reader) *SecurityHeader {
s := &SecurityHeader{}
s.securityFlag, _ = core.ReadUint16LE(r)
s.securityFlagHi, _ = core.ReadUint16LE(r)
return s
}
type SEC struct {
emission.Emitter
transport core.Transport
info *RDPInfo
machineName string
clientData []any
serverData []any
enableEncryption bool
//Enable Secure Mac generation
enableSecureCheckSum bool
//counter before update
nbEncryptedPacket int
nbDecryptedPacket int
currentDecrytKey []byte
currentEncryptKey []byte
//current rc4 tab
decryptRc4 *rc4.Cipher
encryptRc4 *rc4.Cipher
macKey []byte
// fastPathSender is the underlying transport (typically TPKT) that knows
// how to wrap a payload in a fast-path frame. Set via SetFastPathSender
// to enable Fast-Path Client Input PDUs (MS-RDPBCGR §2.2.8.1.2).
fastPathSender core.FastPathSender
}
func NewSEC(t core.Transport) *SEC {
sec := &SEC{
*emission.NewEmitter(),
t,
NewRDPInfo(),
"",
nil,
nil,
false,
false,
0,
0,
nil,
nil,
nil,
nil,
nil,
nil,
}
t.On("close", func() {
sec.Emit("close")
}).On("error", func(err error) {
sec.Emit("error", err)
})
return sec
}
func (s *SEC) Read(data []byte) (n int, err error) {
return s.transport.Read(data)
}
func (s *SEC) Write(b []byte) (n int, err error) {
if !s.enableEncryption {
return s.transport.Write(b)
}
data := s.encrytData(b)
return s.transport.Write(data)
}
func (s *SEC) Close() error {
return s.transport.Close()
}
// SetFastPathSender wires the underlying transport that can frame fast-path
// PDUs. When set, SendFastPath is usable.
func (s *SEC) SetFastPathSender(f core.FastPathSender) {
s.fastPathSender = f
}
// SendFastPath wraps the given payload in a fast-path frame using the
// underlying transport. Returns an error when legacy RDP encryption is
// enabled (this layer does not yet sign fast-path output), allowing the
// caller to fall back to a slow-path send.
func (s *SEC) SendFastPath(secFlag byte, b []byte) (int, error) {
if s.fastPathSender == nil {
return 0, fmt.Errorf("sec: fastPathSender not set")
}
if s.enableEncryption {
return 0, fmt.Errorf("sec: fast-path output not supported with legacy encryption")
}
return s.fastPathSender.SendFastPath(secFlag, b)
}
// LegacyEncryptionEnabled reports whether the per-PDU RDP encryption layer
// (not TLS/CredSSP) is in use. Callers use this to disable optimisations
// like fast-path input that this layer does not yet implement signing for.
func (s *SEC) LegacyEncryptionEnabled() bool {
return s.enableEncryption
}
func (s *SEC) sendFlagged(flag uint16, data []byte) (n int, err error) {
slog.Debug("sendFlagged", "flag", flag, "data", core.Hex(data))
b := s.encryt(flag, data)
return s.transport.Write(b)
}
/*
@see: http://msdn.microsoft.com/en-us/library/cc241995.aspx
@param macSaltKey: {str} mac key
@param data: {str} data to sign
@return: {str} signature
*/
func macData(macSaltKey, data []byte) []byte {
sha1Digest := sha1.New()
md5Digest := md5.New()
var lenBuf [4]byte
binary.LittleEndian.PutUint32(lenBuf[:], uint32(len(data)))
sha1Digest.Write(macSaltKey)
sha1Digest.Write(macPad36[:])
sha1Digest.Write(lenBuf[:])
sha1Digest.Write(data)
sha1Sig := sha1Digest.Sum(nil)
md5Digest.Write(macSaltKey)
md5Digest.Write(macPad5C[:])
md5Digest.Write(sha1Sig)
return md5Digest.Sum(nil)
}
func (s *SEC) readEncryptedPayload(data []byte, checkSum bool) []byte {
sign := data[:8]
slog.Debug("readEncryptedPayload", "sign", sign)
encryptedPayload := data[8:]
if s.decryptRc4 == nil {
s.decryptRc4, _ = rc4.NewCipher(s.currentDecrytKey)
}
s.nbDecryptedPacket++
plaintext := make([]byte, len(encryptedPayload))
s.decryptRc4.XORKeyStream(plaintext, encryptedPayload)
return plaintext
}
func (s *SEC) writeEncryptedPayload(data []byte, checkSum bool) []byte {
if checkSum {
return []byte{}
}
s.nbEncryptedPacket++
slog.Debug("writeEncryptedPayload", "nbEncryptedPacket", s.nbEncryptedPacket)
sign := macData(s.macKey, data)[:8]
if s.encryptRc4 == nil {
s.encryptRc4, _ = rc4.NewCipher(s.currentEncryptKey)
}
result := make([]byte, 8+len(data))
copy(result[:8], sign)
s.encryptRc4.XORKeyStream(result[8:], data)
slog.Debug("writeEncryptedPayload", "sign", core.Hex(sign), "plaintext", core.Hex(result[8:]))
return result
}
func (s *SEC) encryt(flag uint16, b []byte) []byte {
data := b
if flag&ENCRYPT != 0 {
data = s.writeEncryptedPayload(b, flag&SECURE_CHECKSUM != 0)
}
result := make([]byte, 4+len(data))
binary.LittleEndian.PutUint16(result[0:], flag)
binary.LittleEndian.PutUint16(result[2:], 0)
copy(result[4:], data)
return result
}
func (s *SEC) encrytData(b []byte) []byte {
if !s.enableEncryption {
return b
}
var flag uint16 = ENCRYPT
if s.enableSecureCheckSum {
flag |= SECURE_CHECKSUM
}
return s.encryt(flag, b)
}
func (s *SEC) decrytData(b []byte) []byte {
if !s.enableEncryption {
return b
}
if len(b) < 4 {
return b
}
securityFlag := binary.LittleEndian.Uint16(b[0:])
// securityFlagHi = b[2:4] (ignored)
data := b[4:]
if securityFlag&ENCRYPT != 0 {
data = s.readEncryptedPayload(data, securityFlag&SECURE_CHECKSUM != 0)
}
return data
}
type Client struct {
*SEC
userId uint16
channelId uint16
//initialise decrypt and encrypt keys
initialDecrytKey []byte
initialEncryptKey []byte
fastPathListener core.FastPathListener
channelSender core.ChannelSender
}
func NewClient(t core.Transport) *Client {
c := &Client{
SEC: NewSEC(t),
}
t.On("connect", c.connect)
return c
}
func (c *Client) SetClientAutoReconnect(id uint32, random []byte) {
auto := NewClientAutoReconnect(id, random)
c.info.SetClientAutoReconnect(auto)
}
// SetPerformanceFlags 覆盖 Client Info PDU 的 performanceFlags
// (MS-RDPBCGR 2.2.1.11.1.1.1):禁用类位置位 = 关闭对应桌面元素,
// PERF_ENABLE_FONT_SMOOTHING / PERF_ENABLE_DESKTOP_COMPOSITION 置位 =
// 开启对应增强。未调用时保持 NewExtendedInfo 的默认(视觉全开)。
func (c *Client) SetPerformanceFlags(flags uint32) {
c.info.ExtendedInfo.PerformanceFlags = flags
}
// SetNoAudioPlayback 声明客户端不播放音频(mstsc「不播放」)。
func (c *Client) SetNoAudioPlayback() {
c.info.Flag |= INFO_NOAUDIOPLAYBACK
}
// SetRemoteConsoleAudio 声明音频在服务器本机播放(mstsc「在远程计算机播放」)。
func (c *Client) SetRemoteConsoleAudio() {
c.info.Flag |= INFO_REMOTECONSOLEAUDIO
}
func (c *Client) SetAlternateShell(shell string) {
buff := &bytes.Buffer{}
for _, ch := range utf16.Encode([]rune(shell)) {
core.WriteUInt16LE(ch, buff)
}
core.WriteUInt16LE(0, buff)
c.info.AlternateShell = buff.Bytes()
c.info.Flag |= INFO_RAIL
}
func (c *Client) SetUser(user string) {
buff := &bytes.Buffer{}
for _, ch := range utf16.Encode([]rune(user)) {
core.WriteUInt16LE(ch, buff)
}
core.WriteUInt16LE(0, buff)
c.info.UserName = buff.Bytes()
}
func (c *Client) SetPwd(pwd string) {
buff := &bytes.Buffer{}
for _, ch := range utf16.Encode([]rune(pwd)) {
core.WriteUInt16LE(ch, buff)
}
core.WriteUInt16LE(0, buff)
c.info.Password = buff.Bytes()
}
func (c *Client) SetDomain(domain string) {
buff := &bytes.Buffer{}
for _, ch := range utf16.Encode([]rune(domain)) {
core.WriteUInt16LE(ch, buff)
}
core.WriteUInt16LE(0, buff)
c.info.Domain = buff.Bytes()
}
// SetClientTimezone 按 [MS-RDPBCGR] 2.2.1.11.1.1.1.1 填充 Client Info PDU 时区。
// name 为 Windows 时区注册表键名(如 "UTC"、"China Standard Time");
// biasMinutes 为 UTC 与本地时间之差(东八区为 -480)。
// dynamic DST 键名缺失或为空会使现代 Windows 服务器以
// ERRINFO_TIMEZONE_KEY_NAME_LENGTH_TOO_SHORT (0x112F) 断开连接。
func (c *Client) SetClientTimezone(name string, biasMinutes int) {
tz := make([]byte, 172)
binary.LittleEndian.PutUint32(tz[0:4], uint32(int32(biasMinutes)))
// 布局:bias(4) standardName(64) standardDate(16) standardBias(4)
// daylightName(64) daylightDate(16) daylightBias(4)
writeName := func(off int, s string) {
w := utf16.Encode([]rune(s))
for i := 0; i*2+1 < 64; i++ {
v := uint16(0)
if i < len(w) {
v = w[i]
}
binary.LittleEndian.PutUint16(tz[off+i*2:], v)
}
}
writeName(4, name) // standardName
writeName(88, name) // daylightName
c.info.ExtendedInfo.ClientTimeZone = tz
dyn := &bytes.Buffer{}
for _, ch := range utf16.Encode([]rune(name)) {
core.WriteUInt16LE(ch, dyn)
}
c.info.ExtendedInfo.DynDSTTimeZoneKeyName = dyn.Bytes()
}
func (c *Client) connect(clientData []any, serverData []any, userId uint16, channels []t125.MCSChannelInfo) {
slog.Debug("connected!", "clientData", clientData, "serverData", serverData, "userId", userId, "channels", channels)
c.clientData = clientData
c.serverData = serverData
c.userId = userId
for _, channel := range channels {
if channel.Name == t125.GLOBAL_CHANNEL_NAME {
c.channelId = channel.ID
//break
}
}
c.enableEncryption = c.ClientCoreData().ServerSelectedProtocol == 0
if c.enableEncryption {
c.sendClientRandom()
}
c.sendInfoPkt()
c.transport.Once("sec", c.recvLicenceInfo)
}
func (c *Client) ClientCoreData() *gcc.ClientCoreData {
return c.clientData[0].(*gcc.ClientCoreData)
}
func (c *Client) ClientSecurityData() *gcc.ClientSecurityData {
return c.clientData[1].(*gcc.ClientSecurityData)
}
func (c *Client) ClientNetworkData() *gcc.ClientNetworkData {
return c.clientData[2].(*gcc.ClientNetworkData)
}
func (c *Client) serverCoreData() *gcc.ServerCoreData {
return c.serverData[0].(*gcc.ServerCoreData)
}
func (c *Client) ServerSecurityData() *gcc.ServerSecurityData {
return c.serverData[1].(*gcc.ServerSecurityData)
}
/*
@summary: generate 40 bits data from 128 bits data
@param data: {str} 128 bits data
@return: {str} 40 bits data
@see: http://msdn.microsoft.com/en-us/library/cc240785.aspx
*/
func gen40bits(data []byte) []byte {
return append([]byte("\xd1\x26\x9e"), data[3:8]...)
}
/*
@summary: generate 56 bits data from 128 bits data
@param data: {str} 128 bits data
@return: {str} 56 bits data
@see: http://msdn.microsoft.com/en-us/library/cc240785.aspx
*/
func gen56bits(data []byte) []byte {
return append([]byte("\xd1"), data[1:8]...)
}
/*
@summary: Generate particular signature from combination of sha1 and md5
@see: http://msdn.microsoft.com/en-us/library/cc241992.aspx
@param inputData: strange input (see doc)
@param salt: salt for context call
@param salt1: another salt (ex : client random)
@param salt2: another another salt (ex: server random)
@return : MD5(Salt + SHA1(Input + Salt + Salt1 + Salt2))
*/
func saltedHash(inputData, salt, salt1, salt2 []byte) []byte {
sha1Digest := sha1.New()
md5Digest := md5.New()
sha1Digest.Write(inputData)
sha1Digest.Write(salt[:48])
sha1Digest.Write(salt1)
sha1Digest.Write(salt2)
sha1Sig := sha1Digest.Sum(nil)
md5Digest.Write(salt[:48])
md5Digest.Write(sha1Sig)
return md5Digest.Sum(nil)[:16]
}
/*
@summary: MD5(in0[:16] + in1[:32] + in2[:32])
@param key: in 16
@param random1: in 32
@param random2: in 32
@return MD5(in0[:16] + in1[:32] + in2[:32])
*/
func finalHash(key, random1, random2 []byte) []byte {
md5Digest := md5.New()
md5Digest.Write(key)
md5Digest.Write(random1)
md5Digest.Write(random2)
return md5Digest.Sum(nil)
}
/*
@summary: Generate master secret
@param secret: {str} secret
@param clientRandom : {str} client random
@param serverRandom : {str} server random
@see: http://msdn.microsoft.com/en-us/library/cc241992.aspx
*/
func masterSecret(secret, random1, random2 []byte) []byte {
sh1 := saltedHash([]byte("A"), secret, random1, random2)
sh2 := saltedHash([]byte("BB"), secret, random1, random2)
sh3 := saltedHash([]byte("CCC"), secret, random1, random2)
ms := bytes.NewBuffer(nil)
ms.Write(sh1)
ms.Write(sh2)
ms.Write(sh3)
return ms.Bytes()
}
/*
@summary: Generate master secret
@param secret: secret
@param clientRandom : client random
@param serverRandom : server random
*/
func sessionKeyBlob(secret, random1, random2 []byte) []byte {
sh1 := saltedHash([]byte("X"), secret, random1, random2)
sh2 := saltedHash([]byte("YY"), secret, random1, random2)
sh3 := saltedHash([]byte("ZZZ"), secret, random1, random2)
ms := bytes.NewBuffer(nil)
ms.Write(sh1)
ms.Write(sh2)
ms.Write(sh3)
return ms.Bytes()
}
func generateKeys(clientRandom, serverRandom []byte, method uint32) ([]byte, []byte, []byte) {
b := &bytes.Buffer{}
b.Write(clientRandom[:24])
b.Write(serverRandom[:24])
preMasterHash := b.Bytes()
slog.Debug("getnerateKeys", "method", method)
masterHash := masterSecret(preMasterHash, clientRandom, serverRandom)
sessionKey := sessionKeyBlob(masterHash, clientRandom, serverRandom)
macKey128 := sessionKey[:16]
initialFirstKey128 := finalHash(sessionKey[16:32], clientRandom, serverRandom)
initialSecondKey128 := finalHash(sessionKey[32:48], clientRandom, serverRandom)
//generate valid key
if method == gcc.ENCRYPTION_FLAG_40BIT {
return gen40bits(macKey128), gen40bits(initialFirstKey128), gen40bits(initialSecondKey128)
} else if method == gcc.ENCRYPTION_FLAG_56BIT {
return gen56bits(macKey128), gen56bits(initialFirstKey128), gen56bits(initialSecondKey128)
}
// method == gcc.ENCRYPTION_FLAG_128BIT
return macKey128, initialFirstKey128, initialSecondKey128
}
type ClientSecurityExchangePDU struct {
Length uint32 `struc:"little"`
EncryptedClientRandom []byte `struc:"little"`
Padding []byte `struc:"[8]byte"`
}
func (e *ClientSecurityExchangePDU) serialize() []byte {
buff := &bytes.Buffer{}
core.WriteUInt32LE(e.Length, buff)
core.WriteBytes(e.EncryptedClientRandom, buff)
core.WriteBytes(e.Padding, buff)
return buff.Bytes()
}
func (c *Client) sendClientRandom() {
clientRandom := core.Random(32)
slog.Debug("sendClientRandom", "clientRandom", core.Hex(clientRandom))
serverRandom := c.ServerSecurityData().ServerRandom
slog.Debug("sendlientRandom", "ServerRandom", core.Hex(serverRandom))
c.macKey, c.initialDecrytKey, c.initialEncryptKey = generateKeys(clientRandom,
serverRandom, c.ServerSecurityData().EncryptionMethod)
//initialize keys
c.currentDecrytKey = c.initialDecrytKey
c.currentEncryptKey = c.initialEncryptKey
//verify certificate
if !c.ServerSecurityData().ServerCertificate.CertData.Verify() {
slog.Warn("Cannot verify server identity")
}
serverPubKey, _ := c.ServerSecurityData().ServerCertificate.CertData.GetPublicKey()
ret, err := rsa.EncryptPKCS1v15(rand.Reader, serverPubKey, core.Reverse(clientRandom))
if err != nil {
slog.Error("sendlientRandom", "err", err)
}
message := ClientSecurityExchangePDU{}
message.EncryptedClientRandom = core.Reverse(ret)
message.Length = uint32(len(message.EncryptedClientRandom) + 8)
message.Padding = make([]byte, 8)
slog.Debug("sendlientRandom", "message", message)
c.sendFlagged(EXCHANGE_PKT, message.serialize())
}
func (c *Client) sendInfoPkt() {
var secFlag uint16 = INFO_PKT
if c.enableEncryption {
secFlag |= ENCRYPT
}
slog.Debug("sendInfoPkt", "secFlag", secFlag, "hasExtended", c.ClientCoreData().RdpVersion >= gcc.RDP_VERSION_5_PLUS,
"infoFlag", fmt.Sprintf("0x%08X", c.info.Flag))
infoBytes := c.info.Serialize(c.ClientCoreData().RdpVersion >= gcc.RDP_VERSION_5_PLUS)
slog.Debug("sendInfoPkt bytes", "len", len(infoBytes), "hex", core.Hex(infoBytes))
c.sendFlagged(secFlag, infoBytes)
}
func (c *Client) recvLicenceInfo(channel string, s []byte) {
slog.Debug("recvLicenceInfo", "s", core.Hex(s))
r := bytes.NewReader(s)
h := readSecurityHeader(r)
if (h.securityFlag & LICENSE_PKT) == 0 {
c.Emit("error", errors.New("NODE_RDP_PROTOCOL_PDU_SEC_BAD_LICENSE_HEADER"))
return
}
p := lic.ReadLicensePacket(r)
switch p.BMsgtype {
case lic.NEW_LICENSE:
slog.Debug("sec NEW_LICENSE")
c.Emit("success")
goto connect
case lic.ERROR_ALERT:
message := p.LicensingMessage.(*lic.ErrorMessage)
slog.Debug("recvLicenceInfo ERROR_ALERT", "ErrorCode", message.DwErrorCode)
if message.DwErrorCode == lic.STATUS_VALID_CLIENT && message.DwStateTransaction == lic.ST_NO_TRANSITION {
goto connect
}
goto retry
case lic.LICENSE_REQUEST:
slog.Debug("recvLicenceInfo LICENSE_REQUEST")
c.sendClientNewLicenseRequest(p.LicensingMessage.([]byte))
goto retry
case lic.PLATFORM_CHALLENGE:
slog.Debug("recvLicenceInfo PLATFORM_CHALLENGE")
c.sendClientChallengeResponse(p.LicensingMessage.([]byte))
goto retry
default:
slog.Error("Not a valid license packet")
c.Emit("error", errors.New("Not a valid license packet"))
return
}
connect:
c.transport.On("sec", c.recvData)
c.Emit("connect", c.clientData[0].(*gcc.ClientCoreData), c.userId, c.channelId)
return
retry:
c.transport.Once("sec", c.recvLicenceInfo)
return
}
func (c *Client) sendClientNewLicenseRequest(data []byte) {
var req lic.ServerLicenseRequest
struc.Unpack(bytes.NewReader(data), &req)
var sc gcc.ServerCertificate
if c.ServerSecurityData().ServerCertificate.DwVersion != 0 {
sc = c.ServerSecurityData().ServerCertificate
} else {
rd := bytes.NewReader(req.ServerCertificate.BlobData)
err := sc.Unpack(rd)
if err != nil {
slog.Error("sendClientNewLicenseRequest", "err", err)
return
}
}
serverRandom := req.ServerRandom
clientRandom := core.Random(32)
preMasterSecret := core.Random(48)
masSecret := masterSecret(preMasterSecret, clientRandom, serverRandom)
sessionKeyBlob := masterSecret(masSecret, serverRandom, clientRandom)
c.macKey = sessionKeyBlob[:16]
c.initialDecrytKey = finalHash(sessionKeyBlob[16:32], clientRandom, serverRandom)
//format message
message := &lic.ClientNewLicenseRequest{}
message.PreferredKeyExchangeAlg = 0x00000001
message.PlatformId = 0x04000000 | 0x00010000
message.ClientRandom = clientRandom
buff := &bytes.Buffer{}
serverPubKey, _ := sc.CertData.GetPublicKey()
ret, err := rsa.EncryptPKCS1v15(rand.Reader, serverPubKey, core.Reverse(preMasterSecret))
if err != nil {
slog.Error("sendClientNewLicenseRequest", "err", err)
}
buff.Write(core.Reverse(ret))
buff.Write([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
message.EncryptedPreMasterSecret.BlobData = buff.Bytes()
message.EncryptedPreMasterSecret.WBlobLen = uint16(buff.Len())
message.EncryptedPreMasterSecret.WBlobType = lic.BB_RANDOM_BLOB
buff.Reset()
buff.Write(c.info.UserName)
buff.Write([]byte{0x00})
message.ClientUserName.BlobData = buff.Bytes()
message.ClientUserName.WBlobLen = uint16(buff.Len())
message.ClientUserName.WBlobType = lic.BB_CLIENT_USER_NAME_BLOB
buff.Reset()
buff.Write(c.ClientCoreData().ClientName[:])
buff.Write([]byte{0x00})
message.ClientMachineName.BlobData = buff.Bytes()
message.ClientMachineName.WBlobLen = uint16(buff.Len())
message.ClientMachineName.WBlobType = lic.BB_CLIENT_MACHINE_NAME_BLOB
buff.Reset()
err = struc.Pack(buff, message)
if err != nil {
slog.Error("sendClientNewLicenseRequest", "err", err)
}
c.sendFlagged(LICENSE_PKT, buff.Bytes())
}
func (c *Client) sendClientChallengeResponse(data []byte) {
var pc lic.ServerPlatformChallenge
struc.Unpack(bytes.NewReader(data), &pc)
serverEncryptedChallenge := pc.EncryptedPlatformChallenge.BlobData
//decrypt server challenge
//it should be TEST word in unicode format
rc, _ := rc4.NewCipher(c.initialDecrytKey)
serverChallenge := make([]byte, 20)
rc.XORKeyStream(serverChallenge, serverEncryptedChallenge)
//if serverChallenge != "T\x00E\x00S\x00T\x00\x00\x00":
//raise InvalidExpectedDataException("bad license server challenge")
//generate hwid
b := &bytes.Buffer{}
b.Write(c.ClientCoreData().ClientName[:])
b.Write(c.info.UserName)
for range 2 {
b.Write([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
}
hwid := b.Bytes()[:20]
encryptedHWID := make([]byte, 20)
rc.XORKeyStream(encryptedHWID, hwid)
b.Reset()
b.Write(serverChallenge)
b.Write(hwid)
message := &lic.ClientPLatformChallengeResponse{}
message.EncryptedPlatformChallengeResponse.BlobData = serverEncryptedChallenge
message.EncryptedHWID.BlobData = encryptedHWID
message.MACData = macData(c.macKey, b.Bytes())[:16]
b.Reset()
struc.Pack(b, message)
c.sendFlagged(LICENSE_PKT, b.Bytes())
}
func (c *Client) recvData(channel string, s []byte) {
data := c.decrytData(s)
if channel != t125.GLOBAL_CHANNEL_NAME {
c.Emit("channel", channel, data)
return
}
c.Emit("data", data)
}
func (c *Client) SetFastPathListener(f core.FastPathListener) {
c.fastPathListener = f
}
func (c *Client) RecvFastPath(secFlag byte, s []byte) {
data := s
if c.enableEncryption && secFlag&FASTPATH_OUTPUT_ENCRYPTED != 0 {
data = c.readEncryptedPayload(s, secFlag&FASTPATH_OUTPUT_SECURE_CHECKSUM != 0)
}
c.fastPathListener.RecvFastPath(secFlag, data)
}
func (c *Client) SetChannelSender(f core.ChannelSender) {
c.channelSender = f
}
func (c *Client) SendToChannel(channel string, b []byte) (int, error) {
if !c.enableEncryption {
return c.channelSender.SendToChannel(channel, b)
}
var flag uint16 = ENCRYPT
if c.enableSecureCheckSum {
flag |= SECURE_CHECKSUM
}
data := c.writeEncryptedPayload(b, c.enableSecureCheckSum)
buff := &bytes.Buffer{}
core.WriteUInt16LE(flag, buff)
core.WriteUInt16LE(0, buff)
core.WriteBytes(data, buff)
return c.channelSender.SendToChannel(channel, buff.Bytes())
}