732 lines
27 KiB
Python
732 lines
27 KiB
Python
#
|
|
# Copyright (c) 2014-2015 Sylvain Peyrefitte
|
|
#
|
|
# This file is part of rdpy.
|
|
#
|
|
# rdpy is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
#
|
|
|
|
"""
|
|
RDP Standard security layer
|
|
"""
|
|
|
|
import sha, md5
|
|
import lic, tpkt
|
|
from t125 import gcc, mcs
|
|
from rdpy.core.type import CompositeType, CallableValue, Stream, UInt32Le, UInt16Le, String, sizeof, UInt8
|
|
from rdpy.core.layer import LayerAutomata, IStreamSender
|
|
from rdpy.core.error import InvalidExpectedDataException
|
|
from rdpy.core import log
|
|
from rdpy.security import rc4
|
|
import rdpy.security.rsa_wrapper as rsa
|
|
|
|
class SecurityFlag(object):
|
|
"""
|
|
@summary: Microsoft security flags
|
|
@see: http://msdn.microsoft.com/en-us/library/cc240579.aspx
|
|
"""
|
|
SEC_EXCHANGE_PKT = 0x0001
|
|
SEC_TRANSPORT_REQ = 0x0002
|
|
RDP_SEC_TRANSPORT_RSP = 0x0004
|
|
SEC_ENCRYPT = 0x0008
|
|
SEC_RESET_SEQNO = 0x0010
|
|
SEC_IGNORE_SEQNO = 0x0020
|
|
SEC_INFO_PKT = 0x0040
|
|
SEC_LICENSE_PKT = 0x0080
|
|
SEC_LICENSE_ENCRYPT_CS = 0x0200
|
|
SEC_LICENSE_ENCRYPT_SC = 0x0200
|
|
SEC_REDIRECTION_PKT = 0x0400
|
|
SEC_SECURE_CHECKSUM = 0x0800
|
|
SEC_AUTODETECT_REQ = 0x1000
|
|
SEC_AUTODETECT_RSP = 0x2000
|
|
SEC_HEARTBEAT = 0x4000
|
|
SEC_FLAGSHI_VALID = 0x8000
|
|
|
|
class InfoFlag(object):
|
|
"""
|
|
Client capabilities informations
|
|
"""
|
|
INFO_MOUSE = 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
|
|
|
|
class PerfFlag(object):
|
|
"""
|
|
Network performances flag
|
|
"""
|
|
PERF_DISABLE_WALLPAPER = 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
|
|
|
|
class AfInet(object):
|
|
"""
|
|
IPv4 or IPv6 address style
|
|
"""
|
|
AF_INET = 0x00002
|
|
AF_INET6 = 0x0017
|
|
|
|
def saltedHash(inputData, salt, salt1, salt2):
|
|
"""
|
|
@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))
|
|
"""
|
|
sha1Digest = sha.new()
|
|
md5Digest = md5.new()
|
|
|
|
sha1Digest.update(inputData)
|
|
sha1Digest.update(salt[:48])
|
|
sha1Digest.update(salt1)
|
|
sha1Digest.update(salt2)
|
|
sha1Sig = sha1Digest.digest()
|
|
|
|
md5Digest.update(salt[:48])
|
|
md5Digest.update(sha1Sig)
|
|
|
|
return md5Digest.digest()
|
|
|
|
def finalHash(key, random1, random2):
|
|
"""
|
|
@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])
|
|
"""
|
|
md5Digest = md5.new()
|
|
md5Digest.update(key)
|
|
md5Digest.update(random1)
|
|
md5Digest.update(random2)
|
|
return md5Digest.digest()
|
|
|
|
def masterSecret(secret, random1, random2):
|
|
"""
|
|
@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
|
|
"""
|
|
return saltedHash("A", secret, random1, random2) + saltedHash("BB", secret, random1, random2) + saltedHash("CCC", secret, random1, random2)
|
|
|
|
def sessionKeyBlob(secret, random1, random2):
|
|
"""
|
|
@summary: Generate master secret
|
|
@param secret: secret
|
|
@param clientRandom : client random
|
|
@param serverRandom : server random
|
|
"""
|
|
return saltedHash("X", secret, random1, random2) + saltedHash("YY", secret, random1, random2) + saltedHash("ZZZ", secret, random1, random2)
|
|
|
|
def macData(macSaltKey, data):
|
|
"""
|
|
@see: http://msdn.microsoft.com/en-us/library/cc241995.aspx
|
|
@param macSaltKey: {str} mac key
|
|
@param data: {str} data to sign
|
|
@return: {str} signature
|
|
"""
|
|
sha1Digest = sha.new()
|
|
md5Digest = md5.new()
|
|
|
|
#encode length
|
|
dataLength = Stream()
|
|
dataLength.writeType(UInt32Le(len(data)))
|
|
|
|
sha1Digest.update(macSaltKey)
|
|
sha1Digest.update("\x36" * 40)
|
|
sha1Digest.update(dataLength.getvalue())
|
|
sha1Digest.update(data)
|
|
|
|
sha1Sig = sha1Digest.digest()
|
|
|
|
md5Digest.update(macSaltKey)
|
|
md5Digest.update("\x5c" * 48)
|
|
md5Digest.update(sha1Sig)
|
|
|
|
return md5Digest.digest()
|
|
|
|
def macSaltedData(macSaltKey, data, encryptionCount):
|
|
"""
|
|
@see: https://msdn.microsoft.com/en-us/library/cc240789.aspx
|
|
@param macSaltKey: {str} mac key
|
|
@param data: {str} data to sign
|
|
@param encryptionCount: nb encrypted packet
|
|
@return: {str} signature
|
|
"""
|
|
sha1Digest = sha.new()
|
|
md5Digest = md5.new()
|
|
|
|
#encode length
|
|
dataLengthS = Stream()
|
|
dataLengthS.writeType(UInt32Le(len(data)))
|
|
|
|
encryptionCountS = Stream()
|
|
encryptionCountS.writeType(UInt32Le(encryptionCount))
|
|
|
|
sha1Digest.update(macSaltKey)
|
|
sha1Digest.update("\x36" * 40)
|
|
sha1Digest.update(dataLengthS.getvalue())
|
|
sha1Digest.update(data)
|
|
sha1Digest.update(encryptionCountS.getvalue())
|
|
|
|
sha1Sig = sha1Digest.digest()
|
|
|
|
md5Digest.update(macSaltKey)
|
|
md5Digest.update("\x5c" * 48)
|
|
md5Digest.update(sha1Sig)
|
|
|
|
return md5Digest.digest()
|
|
|
|
def tempKey(initialKey, currentKey):
|
|
"""
|
|
@see: http://msdn.microsoft.com/en-us/library/cc240792.aspx
|
|
@param initialKey: {str} key computed first time
|
|
@param currentKey: {str} key actually used
|
|
@return: {str} temp key
|
|
"""
|
|
sha1Digest = sha.new()
|
|
md5Digest = md5.new()
|
|
|
|
sha1Digest.update(initialKey)
|
|
sha1Digest.update("\x36" * 40)
|
|
sha1Digest.update(currentKey)
|
|
|
|
sha1Sig = sha1Digest.digest()
|
|
|
|
md5Digest.update(initialKey)
|
|
md5Digest.update("\x5c" * 48)
|
|
md5Digest.update(sha1Sig)
|
|
|
|
return md5Digest.digest()
|
|
|
|
def gen40bits(data):
|
|
"""
|
|
@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
|
|
"""
|
|
return "\xd1\x26\x9e" + data[:8][-5:]
|
|
|
|
def gen56bits(data):
|
|
"""
|
|
@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
|
|
"""
|
|
return "\xd1" + data[:8][-7:]
|
|
|
|
def generateKeys(clientRandom, serverRandom, method):
|
|
"""
|
|
@param method: {gcc.Encryption}
|
|
@param clientRandom: {str[32]} client random
|
|
@param serverRandom: {str[32]} server random
|
|
@see: http://msdn.microsoft.com/en-us/library/cc240785.aspx
|
|
@return: MACKey, initialFirstKey128(ClientdecryptKey, serverEncryptKey), initialSecondKey128(ServerDecryptKey, ClientEncryptKey)
|
|
"""
|
|
preMasterHash = clientRandom[:24] + serverRandom[:24]
|
|
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.EncryptionMethod.ENCRYPTION_FLAG_40BIT:
|
|
return gen40bits(macKey128), gen40bits(initialFirstKey128), gen40bits(initialSecondKey128)
|
|
|
|
elif method == gcc.EncryptionMethod.ENCRYPTION_FLAG_56BIT:
|
|
return gen56bits(macKey128), gen56bits(initialFirstKey128), gen56bits(initialSecondKey128)
|
|
|
|
elif method == gcc.EncryptionMethod.ENCRYPTION_FLAG_128BIT:
|
|
return macKey128, initialFirstKey128, initialSecondKey128
|
|
|
|
raise InvalidExpectedDataException("Bad encryption method")
|
|
|
|
def updateKey(initialKey, currentKey, method):
|
|
"""
|
|
@summary: update session key
|
|
@param initialKey: {str} Initial key
|
|
@param currentKey: {str} Current key
|
|
@return newKey: {str} key to use
|
|
@see: http://msdn.microsoft.com/en-us/library/cc240792.aspx
|
|
"""
|
|
#generate valid key
|
|
if method == gcc.EncryptionMethod.ENCRYPTION_FLAG_40BIT:
|
|
tempKey128 = tempKey(initialKey[:8], currentKey[:8])
|
|
return gen40bits(rc4.crypt(rc4.RC4Key(tempKey128[:8]), tempKey128[:8]))
|
|
|
|
elif method == gcc.EncryptionMethod.ENCRYPTION_FLAG_56BIT:
|
|
tempKey128 = tempKey(initialKey[:8], currentKey[:8])
|
|
return gen56bits(rc4.crypt(rc4.RC4Key(tempKey128[:8]), tempKey128[:8]))
|
|
|
|
elif method == gcc.EncryptionMethod.ENCRYPTION_FLAG_128BIT:
|
|
tempKey128 = tempKey(initialKey, currentKey)
|
|
return rc4.crypt(rc4.RC4Key(tempKey128), tempKey128)
|
|
|
|
class ClientSecurityExchangePDU(CompositeType):
|
|
"""
|
|
@summary: contain client random for basic security
|
|
@see: http://msdn.microsoft.com/en-us/library/cc240472.aspx
|
|
"""
|
|
def __init__(self):
|
|
CompositeType.__init__(self)
|
|
self.length = UInt32Le(lambda:(sizeof(self) - 4))
|
|
self.encryptedClientRandom = String(readLen = CallableValue(lambda:(self.length.value - 8)))
|
|
self.padding = String("\x00" * 8, readLen = CallableValue(8))
|
|
|
|
class RDPInfo(CompositeType):
|
|
"""
|
|
@summary: Client informations
|
|
Contains credentials (very important packet)
|
|
@see: http://msdn.microsoft.com/en-us/library/cc240475.aspx
|
|
"""
|
|
def __init__(self, extendedInfoConditional):
|
|
CompositeType.__init__(self)
|
|
#code page
|
|
self.codePage = UInt32Le()
|
|
#support flag
|
|
self.flag = UInt32Le(InfoFlag.INFO_MOUSE | InfoFlag.INFO_UNICODE | InfoFlag.INFO_LOGONNOTIFY | InfoFlag.INFO_LOGONERRORS | InfoFlag.INFO_DISABLECTRLALTDEL | InfoFlag.INFO_ENABLEWINDOWSKEY)
|
|
self.cbDomain = UInt16Le(lambda:sizeof(self.domain) - 2)
|
|
self.cbUserName = UInt16Le(lambda:sizeof(self.userName) - 2)
|
|
self.cbPassword = UInt16Le(lambda:sizeof(self.password) - 2)
|
|
self.cbAlternateShell = UInt16Le(lambda:sizeof(self.alternateShell) - 2)
|
|
self.cbWorkingDir = UInt16Le(lambda:sizeof(self.workingDir) - 2)
|
|
#microsoft domain
|
|
self.domain = String(readLen = CallableValue(lambda:self.cbDomain.value + 2), unicode = True)
|
|
self.userName = String(readLen = CallableValue(lambda:self.cbUserName.value + 2), unicode = True)
|
|
self.password = String(readLen = CallableValue(lambda:self.cbPassword.value + 2), unicode = True)
|
|
#shell execute at start of session
|
|
self.alternateShell = String(readLen = CallableValue(lambda:self.cbAlternateShell.value + 2), unicode = True)
|
|
#working directory for session
|
|
self.workingDir = String(readLen = CallableValue(lambda:self.cbWorkingDir.value + 2), unicode = True)
|
|
self.extendedInfo = RDPExtendedInfo(conditional = extendedInfoConditional)
|
|
|
|
class RDPExtendedInfo(CompositeType):
|
|
"""
|
|
@summary: Add more client informations
|
|
"""
|
|
def __init__(self, conditional):
|
|
CompositeType.__init__(self, conditional = conditional)
|
|
self.clientAddressFamily = UInt16Le(AfInet.AF_INET)
|
|
self.cbClientAddress = UInt16Le(lambda:sizeof(self.clientAddress))
|
|
self.clientAddress = String(readLen = self.cbClientAddress, unicode = True)
|
|
self.cbClientDir = UInt16Le(lambda:sizeof(self.clientDir))
|
|
self.clientDir = String(readLen = self.cbClientDir, unicode = True)
|
|
#TODO make tiomezone
|
|
self.clientTimeZone = String("\x00" * 172)
|
|
self.clientSessionId = UInt32Le()
|
|
self.performanceFlags = UInt32Le()
|
|
|
|
class SecLayer(LayerAutomata, IStreamSender, tpkt.IFastPathListener, tpkt.IFastPathSender, mcs.IGCCConfig):
|
|
"""
|
|
@summary: Standard RDP security layer
|
|
This layer is Transparent as possible for upper layer
|
|
"""
|
|
def __init__(self, presentation):
|
|
"""
|
|
@param presentation: Layer (generally pdu layer)
|
|
"""
|
|
LayerAutomata.__init__(self, presentation)
|
|
#thios layer is like a fastpath proxy
|
|
self._fastPathTransport = None
|
|
self._fastPathPresentation = None
|
|
|
|
#credentials
|
|
self._info = RDPInfo(extendedInfoConditional = lambda:(self.getGCCServerSettings().SC_CORE.rdpVersion.value == gcc.Version.RDP_VERSION_5_PLUS))
|
|
|
|
#True if classic encryption is enable
|
|
self._enableEncryption = False
|
|
|
|
#Enable Secure Mac generation
|
|
self._enableSecureCheckSum = False
|
|
|
|
#initialise decrypt and encrypt keys
|
|
self._macKey = None
|
|
self._initialDecrytKey = None
|
|
self._initialEncryptKey = None
|
|
self._currentDecrytKey = None
|
|
self._currentEncryptKey = None
|
|
|
|
#counter before update
|
|
self._nbEncryptedPacket = 0
|
|
self._nbDecryptedPacket = 0
|
|
|
|
#current rc4 tab
|
|
self._decryptRc4 = None
|
|
self._encryptRc4 = None
|
|
|
|
|
|
def readEncryptedPayload(self, s, saltedMacGeneration):
|
|
"""
|
|
@summary: decrypt basic RDP security payload
|
|
@param s: {Stream} encrypted stream
|
|
@param saltedMacGeneration: {bool} use salted mac generation
|
|
@return: {Stream} decrypted
|
|
"""
|
|
#if update is needed
|
|
if self._nbDecryptedPacket == 4096:
|
|
log.debug("update decrypt key")
|
|
self._currentDecrytKey = updateKey( self._initialDecrytKey, self._currentDecrytKey,
|
|
self.getGCCServerSettings().SC_SECURITY.encryptionMethod.value)
|
|
self._decryptRc4 = rc4.RC4Key(self._currentDecrytKey)
|
|
self._nbDecryptedPacket = 0
|
|
|
|
signature = String(readLen = CallableValue(8))
|
|
encryptedPayload = String()
|
|
s.readType((signature, encryptedPayload))
|
|
decrypted = rc4.crypt(self._decryptRc4, encryptedPayload.value)
|
|
|
|
#ckeck signature
|
|
if not saltedMacGeneration and macData(self._macKey, decrypted)[:8] != signature.value:
|
|
raise InvalidExpectedDataException("bad signature")
|
|
|
|
if saltedMacGeneration and macSaltedData(self._macKey, decrypted, self._nbDecryptedPacket)[:8] != signature.value:
|
|
raise InvalidExpectedDataException("bad signature")
|
|
|
|
#count
|
|
self._nbDecryptedPacket += 1
|
|
|
|
return Stream(decrypted)
|
|
|
|
def writeEncryptedPayload(self, data, saltedMacGeneration):
|
|
"""
|
|
@summary: sign and crypt data
|
|
@param data: {Type} raw stream
|
|
@param saltedMacGeneration: {bool} use salted mac generation
|
|
@return: {Tuple} (signature, encryptedData)
|
|
"""
|
|
if self._nbEncryptedPacket == 4096:
|
|
log.debug("update encrypt key")
|
|
self._currentEncryptKey = updateKey( self._initialEncryptKey, self._currentEncryptKey,
|
|
self.getGCCServerSettings().SC_SECURITY.encryptionMethod.value)
|
|
self._encryptRc4 = rc4.RC4Key(self._currentEncryptKey)
|
|
self._nbEncryptedPacket = 0
|
|
|
|
self._nbEncryptedPacket += 1
|
|
|
|
s = Stream()
|
|
s.writeType(data)
|
|
|
|
if saltedMacGeneration:
|
|
return (String(macSaltedData(self._macKey, s.getvalue(), self._nbEncryptedPacket - 1)[:8]), String(rc4.crypt(self._encryptRc4, s.getvalue())))
|
|
else:
|
|
return (String(macData(self._macKey, s.getvalue())[:8]), String(rc4.crypt(self._encryptRc4, s.getvalue())))
|
|
|
|
def recv(self, data):
|
|
"""
|
|
@summary: if basic RDP security layer is activate decrypt
|
|
else pass to upper layer
|
|
@param data : {Stream} input Stream
|
|
"""
|
|
if not self._enableEncryption:
|
|
self._presentation.recv(data)
|
|
return
|
|
|
|
securityFlag = UInt16Le()
|
|
securityFlagHi = UInt16Le()
|
|
data.readType((securityFlag, securityFlagHi))
|
|
|
|
if securityFlag.value & SecurityFlag.SEC_ENCRYPT:
|
|
data = self.readEncryptedPayload(data, securityFlag.value & SecurityFlag.SEC_SECURE_CHECKSUM)
|
|
|
|
self._presentation.recv(data)
|
|
|
|
def send(self, data):
|
|
"""
|
|
@summary: if basic RDP security layer is activate encrypt
|
|
else pass to upper layer
|
|
@param data: {Type | Tuple}
|
|
"""
|
|
if not self._enableEncryption:
|
|
self._transport.send(data)
|
|
return
|
|
|
|
flag = SecurityFlag.SEC_ENCRYPT
|
|
|
|
if self._enableSecureCheckSum:
|
|
flag |= SecurityFlag.SEC_SECURE_CHECKSUM
|
|
|
|
self.sendFlagged(flag, data)
|
|
|
|
def sendFlagged(self, flag, data):
|
|
"""
|
|
@summary: explicit send flag method for particular packet
|
|
(info packet or license packet)
|
|
If encryption is enable apply it
|
|
@param flag: {integer} security flag
|
|
@param data: {Type | Tuple}
|
|
"""
|
|
if flag & SecurityFlag.SEC_ENCRYPT:
|
|
data = self.writeEncryptedPayload(data, flag & SecurityFlag.SEC_SECURE_CHECKSUM)
|
|
self._transport.send((UInt16Le(flag), UInt16Le(), data))
|
|
|
|
def recvFastPath(self, secFlag, fastPathS):
|
|
"""
|
|
@summary: Call when fast path packet is received
|
|
@param secFlag: {SecFlags}
|
|
@param fastPathS: {Stream}
|
|
"""
|
|
if self._enableEncryption and secFlag & tpkt.SecFlags.FASTPATH_OUTPUT_ENCRYPTED:
|
|
fastPathS = self.readEncryptedPayload(fastPathS, secFlag & tpkt.SecFlags.FASTPATH_OUTPUT_SECURE_CHECKSUM)
|
|
|
|
self._fastPathPresentation.recvFastPath(secFlag, fastPathS)
|
|
|
|
def setFastPathListener(self, fastPathListener):
|
|
"""
|
|
@param fastPathListener : {IFastPathListener}
|
|
"""
|
|
self._fastPathPresentation = fastPathListener
|
|
|
|
def sendFastPath(self, secFlag, fastPathS):
|
|
"""
|
|
@summary: Send fastPathS Type as fast path packet
|
|
@param secFlag: {SecFlags}
|
|
@param fastPathS: {Stream} type transform to stream and send as fastpath
|
|
"""
|
|
if self._enableEncryption:
|
|
secFlag |= tpkt.SecFlags.FASTPATH_OUTPUT_ENCRYPTED
|
|
|
|
if self._enableSecureCheckSum:
|
|
secFlag |= tpkt.SecFlags.FASTPATH_OUTPUT_SECURE_CHECKSUM
|
|
|
|
fastPathS = self.writeEncryptedPayload(fastPathS, self._enableSecureCheckSum)
|
|
|
|
self._fastPathTransport.sendFastPath(secFlag, fastPathS)
|
|
|
|
def setFastPathSender(self, fastPathSender):
|
|
"""
|
|
@param fastPathSender: {tpkt.FastPathSender}
|
|
"""
|
|
self._fastPathTransport = fastPathSender
|
|
|
|
def getUserId(self):
|
|
"""
|
|
@return: {integer} mcs user id
|
|
@see: mcs.IGCCConfig
|
|
"""
|
|
return self._transport.getUserId()
|
|
|
|
def getChannelId(self):
|
|
"""
|
|
@return: {integer} return channel id of proxy
|
|
@see: mcs.IGCCConfig
|
|
"""
|
|
return self._transport.getChannelId()
|
|
|
|
def getGCCClientSettings(self):
|
|
"""
|
|
@return: {gcc.Settings} mcs layer gcc client settings
|
|
@see: mcs.IGCCConfig
|
|
"""
|
|
return self._transport.getGCCClientSettings()
|
|
|
|
def getGCCServerSettings(self):
|
|
"""
|
|
@return: {gcc.Settings} mcs layer gcc server settings
|
|
@see: mcs.IGCCConfig
|
|
"""
|
|
return self._transport.getGCCServerSettings()
|
|
|
|
class Client(SecLayer):
|
|
"""
|
|
@summary: Client side of security layer
|
|
"""
|
|
def __init__(self, presentation):
|
|
SecLayer.__init__(self, presentation)
|
|
self._licenceManager = lic.LicenseManager(self)
|
|
|
|
def connect(self):
|
|
"""
|
|
@summary: send client random if needed and send info packet
|
|
"""
|
|
self._enableEncryption = self.getGCCClientSettings().CS_CORE.serverSelectedProtocol == 0
|
|
|
|
if self._enableEncryption:
|
|
self.sendClientRandom()
|
|
|
|
self.sendInfoPkt()
|
|
|
|
def sendInfoPkt(self):
|
|
"""
|
|
@summary: send information packet (with credentials)
|
|
next state -> recvLicenceInfo
|
|
"""
|
|
secFlag = SecurityFlag.SEC_INFO_PKT
|
|
if self._enableEncryption:
|
|
secFlag |= SecurityFlag.SEC_ENCRYPT
|
|
self.sendFlagged(secFlag, self._info)
|
|
|
|
self.setNextState(self.recvLicenceInfo)
|
|
|
|
def sendClientRandom(self):
|
|
"""
|
|
@summary: generate and send client random and init session keys
|
|
"""
|
|
#generate client random
|
|
clientRandom = rsa.random(256)
|
|
self._macKey, self._initialDecrytKey, self._initialEncryptKey = generateKeys( clientRandom,
|
|
self.getGCCServerSettings().SC_SECURITY.serverRandom.value,
|
|
self.getGCCServerSettings().SC_SECURITY.encryptionMethod.value)
|
|
#initialize keys
|
|
self._currentDecrytKey = self._initialDecrytKey
|
|
self._currentEncryptKey = self._initialEncryptKey
|
|
self._decryptRc4 = rc4.RC4Key(self._currentDecrytKey)
|
|
self._encryptRc4 = rc4.RC4Key(self._currentEncryptKey)
|
|
|
|
#verify certificate
|
|
if not self.getGCCServerSettings().SC_SECURITY.serverCertificate.certData.verify():
|
|
log.warning("cannot verify server identity")
|
|
#send client random encrypted with
|
|
serverPublicKey = self.getGCCServerSettings().SC_SECURITY.serverCertificate.certData.getPublicKey()
|
|
message = ClientSecurityExchangePDU()
|
|
#reverse because bignum in little endian
|
|
message.encryptedClientRandom.value = rsa.encrypt(clientRandom[::-1], serverPublicKey)[::-1]
|
|
self.sendFlagged(SecurityFlag.SEC_EXCHANGE_PKT, message)
|
|
|
|
def recvLicenceInfo(self, s):
|
|
"""
|
|
@summary: Read license info packet and check if is a valid client info
|
|
Wait Demand Active PDU
|
|
@param s: Stream
|
|
"""
|
|
#packet preambule
|
|
securityFlag = UInt16Le()
|
|
securityFlagHi = UInt16Le()
|
|
s.readType((securityFlag, securityFlagHi))
|
|
|
|
if not (securityFlag.value & SecurityFlag.SEC_LICENSE_PKT):
|
|
raise InvalidExpectedDataException("waiting license packet")
|
|
|
|
if self._licenceManager.recv(s):
|
|
self.setNextState()
|
|
#end of connection step of
|
|
self._presentation.connect()
|
|
|
|
class Server(SecLayer):
|
|
"""
|
|
@summary: Client side of security layer
|
|
"""
|
|
def __init__(self, presentation):
|
|
"""
|
|
@param presentation: {Layer}
|
|
"""
|
|
SecLayer.__init__(self, presentation)
|
|
self._rsaPublicKey, self._rsaPrivateKey = rsa.newkeys(512)
|
|
|
|
def connect(self):
|
|
"""
|
|
@summary: init automata to wait info packet
|
|
"""
|
|
self._enableEncryption = self.getGCCClientSettings().CS_CORE.serverSelectedProtocol == 0
|
|
if self._enableEncryption:
|
|
self.setNextState(self.recvClientRandom)
|
|
else:
|
|
self.setNextState(self.recvInfoPkt)
|
|
|
|
def getCertificate(self):
|
|
"""
|
|
@summary: generate proprietary certificate from rsa public key
|
|
"""
|
|
certificate = gcc.ProprietaryServerCertificate()
|
|
certificate.PublicKeyBlob.modulus.value = rsa.int2bytes(self._rsaPublicKey.n)[::-1]
|
|
certificate.PublicKeyBlob.pubExp.value = self._rsaPublicKey.e
|
|
certificate.sign()
|
|
return gcc.ServerCertificate(certificate)
|
|
|
|
def recvClientRandom(self, s):
|
|
"""
|
|
@summary: receive client random and generate session keys
|
|
@param s: {Stream}
|
|
"""
|
|
#packet preambule
|
|
securityFlag = UInt16Le()
|
|
securityFlagHi = UInt16Le()
|
|
s.readType((securityFlag, securityFlagHi))
|
|
|
|
if not (securityFlag.value & SecurityFlag.SEC_EXCHANGE_PKT):
|
|
raise InvalidExpectedDataException("waiting client random")
|
|
|
|
message = ClientSecurityExchangePDU()
|
|
s.readType(message)
|
|
clientRandom = rsa.decrypt(message.encryptedClientRandom.value[::-1], self._rsaPrivateKey)[::-1]
|
|
|
|
self._macKey, self._initialEncryptKey, self._initialDecrytKey = generateKeys( clientRandom,
|
|
self.getGCCServerSettings().SC_SECURITY.serverRandom.value,
|
|
self.getGCCServerSettings().SC_SECURITY.encryptionMethod.value)
|
|
#initialize keys
|
|
self._currentDecrytKey = self._initialDecrytKey
|
|
self._currentEncryptKey = self._initialEncryptKey
|
|
self._decryptRc4 = rc4.RC4Key(self._currentDecrytKey)
|
|
self._encryptRc4 = rc4.RC4Key(self._currentEncryptKey)
|
|
|
|
self.setNextState(self.recvInfoPkt)
|
|
|
|
def recvInfoPkt(self, s):
|
|
"""
|
|
@summary: receive info packet from client
|
|
Client credentials
|
|
Send License valid error message
|
|
Send Demand Active PDU
|
|
Wait Confirm Active PDU
|
|
@param s: {Stream}
|
|
"""
|
|
securityFlag = UInt16Le()
|
|
securityFlagHi = UInt16Le()
|
|
s.readType((securityFlag, securityFlagHi))
|
|
|
|
if not (securityFlag.value & SecurityFlag.SEC_INFO_PKT):
|
|
raise InvalidExpectedDataException("Waiting info packet")
|
|
|
|
if securityFlag.value & SecurityFlag.SEC_ENCRYPT:
|
|
s = self.readEncryptedPayload(s, securityFlag.value & SecurityFlag.SEC_SECURE_CHECKSUM)
|
|
|
|
s.readType(self._info)
|
|
#next state send error license
|
|
self.sendLicensingErrorMessage()
|
|
#reinit state
|
|
self.setNextState()
|
|
self._presentation.connect()
|
|
|
|
def sendLicensingErrorMessage(self):
|
|
"""
|
|
@summary: Send a licensing error data
|
|
"""
|
|
self.sendFlagged(SecurityFlag.SEC_LICENSE_PKT, lic.createValidClientLicensingErrorMessage()) |