add support for x509 certificate
This commit is contained in:
@@ -959,6 +959,13 @@ class ArrayType(Type):
|
||||
@param s: Stream
|
||||
"""
|
||||
s.writeType(self._array)
|
||||
|
||||
def __getitem__(self, item):
|
||||
"""
|
||||
@summary: Magic function to be FactoryType as transparent as possible
|
||||
@return: index of _value
|
||||
"""
|
||||
return self._array.__getitem__(item)
|
||||
|
||||
def __sizeof__(self):
|
||||
"""
|
||||
|
||||
159
rdpy/core/x509.py
Normal file
159
rdpy/core/x509.py
Normal file
@@ -0,0 +1,159 @@
|
||||
#
|
||||
# Copyright (c) 2014 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/>.
|
||||
#
|
||||
|
||||
"""
|
||||
@see: https://github.com/filippog/pyasn1/blob/master/examples/x509.py
|
||||
"""
|
||||
|
||||
from pyasn1.type import tag, namedtype, namedval, univ, constraint, char, useful
|
||||
from pyasn1.codec.ber import decoder, encoder
|
||||
from pyasn1 import error
|
||||
from error import InvalidExpectedDataException
|
||||
|
||||
MAX = 64
|
||||
|
||||
class DirectoryString(univ.Choice):
|
||||
componentType = namedtype.NamedTypes(
|
||||
namedtype.NamedType('teletexString', char.TeletexString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, MAX))),
|
||||
namedtype.NamedType('printableString', char.PrintableString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, MAX))),
|
||||
namedtype.NamedType('universalString', char.UniversalString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, MAX))),
|
||||
namedtype.NamedType('utf8String', char.UTF8String().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, MAX))),
|
||||
namedtype.NamedType('bmpString', char.BMPString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, MAX))),
|
||||
namedtype.NamedType('ia5String', char.IA5String().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, MAX))) # hm, this should not be here!? XXX
|
||||
)
|
||||
|
||||
class AttributeValue(DirectoryString): pass
|
||||
|
||||
class AttributeType(univ.ObjectIdentifier): pass
|
||||
|
||||
class AttributeTypeAndValue(univ.Sequence):
|
||||
componentType = namedtype.NamedTypes(
|
||||
namedtype.NamedType('type', AttributeType()),
|
||||
namedtype.NamedType('value', AttributeValue())
|
||||
)
|
||||
|
||||
class RelativeDistinguishedName(univ.SetOf):
|
||||
componentType = AttributeTypeAndValue()
|
||||
|
||||
class RDNSequence(univ.SequenceOf):
|
||||
componentType = RelativeDistinguishedName()
|
||||
|
||||
class Name(univ.Choice):
|
||||
componentType = namedtype.NamedTypes(
|
||||
namedtype.NamedType('', RDNSequence())
|
||||
)
|
||||
|
||||
class AlgorithmIdentifier(univ.Sequence):
|
||||
componentType = namedtype.NamedTypes(
|
||||
namedtype.NamedType('algorithm', univ.ObjectIdentifier()),
|
||||
namedtype.OptionalNamedType('parameters', univ.Null())
|
||||
# XXX syntax screwed?
|
||||
# namedtype.OptionalNamedType('parameters', univ.ObjectIdentifier())
|
||||
)
|
||||
|
||||
class Extension(univ.Sequence):
|
||||
componentType = namedtype.NamedTypes(
|
||||
namedtype.NamedType('extnID', univ.ObjectIdentifier()),
|
||||
namedtype.DefaultedNamedType('critical', univ.Boolean('False')),
|
||||
namedtype.NamedType('extnValue', univ.OctetString())
|
||||
)
|
||||
|
||||
class Extensions(univ.SequenceOf):
|
||||
componentType = Extension()
|
||||
sizeSpec = univ.SequenceOf.sizeSpec + constraint.ValueSizeConstraint(1, MAX)
|
||||
|
||||
class SubjectPublicKeyInfo(univ.Sequence):
|
||||
componentType = namedtype.NamedTypes(
|
||||
namedtype.NamedType('algorithm', AlgorithmIdentifier()),
|
||||
namedtype.NamedType('subjectPublicKey', univ.BitString())
|
||||
)
|
||||
|
||||
class UniqueIdentifier(univ.BitString): pass
|
||||
|
||||
class Time(univ.Choice):
|
||||
componentType = namedtype.NamedTypes(
|
||||
namedtype.NamedType('utcTime', useful.UTCTime()),
|
||||
namedtype.NamedType('generalTime', useful.GeneralizedTime())
|
||||
)
|
||||
|
||||
class Validity(univ.Sequence):
|
||||
componentType = namedtype.NamedTypes(
|
||||
namedtype.NamedType('notBefore', Time()),
|
||||
namedtype.NamedType('notAfter', Time())
|
||||
)
|
||||
|
||||
class CertificateSerialNumber(univ.Integer): pass
|
||||
|
||||
class Version(univ.Integer):
|
||||
namedValues = namedval.NamedValues(
|
||||
('v1', 0), ('v2', 1), ('v3', 2)
|
||||
)
|
||||
|
||||
class TBSCertificate(univ.Sequence):
|
||||
componentType = namedtype.NamedTypes(
|
||||
namedtype.DefaultedNamedType('version', Version('v1', tagSet=Version.tagSet.tagExplicitly(tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0)))),
|
||||
namedtype.NamedType('serialNumber', CertificateSerialNumber()),
|
||||
namedtype.NamedType('signature', AlgorithmIdentifier()),
|
||||
namedtype.NamedType('issuer', Name()),
|
||||
namedtype.NamedType('validity', Validity()),
|
||||
namedtype.NamedType('subject', Name()),
|
||||
namedtype.NamedType('subjectPublicKeyInfo', SubjectPublicKeyInfo()),
|
||||
namedtype.OptionalNamedType('issuerUniqueID', UniqueIdentifier().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))),
|
||||
namedtype.OptionalNamedType('subjectUniqueID', UniqueIdentifier().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))),
|
||||
namedtype.OptionalNamedType('extensions', Extensions().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3)))
|
||||
)
|
||||
|
||||
class X509Certificate(univ.Sequence):
|
||||
componentType = namedtype.NamedTypes(
|
||||
namedtype.NamedType('tbsCertificate', TBSCertificate()),
|
||||
namedtype.NamedType('signatureAlgorithm', AlgorithmIdentifier()),
|
||||
namedtype.NamedType('signatureValue', univ.BitString())
|
||||
)
|
||||
|
||||
class RSAPublicKey(univ.Sequence):
|
||||
"""
|
||||
@summary: asn1 public rsa key
|
||||
@see: https://tools.ietf.org/html/rfc3447
|
||||
"""
|
||||
componentType = namedtype.NamedTypes(
|
||||
namedtype.NamedType('modulus', univ.Integer()),
|
||||
namedtype.NamedType('publicExponent', univ.Integer()),
|
||||
)
|
||||
|
||||
def load(stream):
|
||||
"""
|
||||
@summary: load X509 certificate
|
||||
@param stream: {str}
|
||||
"""
|
||||
return decoder.decode(stream, asn1Spec=X509Certificate())[0]
|
||||
|
||||
def extractRSAKey(certificate):
|
||||
"""
|
||||
@summary: try to extract rsa key
|
||||
@return: (modulus, public exponent)
|
||||
"""
|
||||
#http://www.alvestrand.no/objectid/1.2.840.113549.1.1.1.html
|
||||
#if certificate.getComponentByName('tbsCertificate').getComponentByName('subjectPublicKeyInfo').getComponentByName('algorithm').getComponentByName('algorithm')._value != (1, 2, 840, 113549, 1, 1, 1):
|
||||
# raise InvalidExpectedDataException("Certificate doesn't contain RSA public key")
|
||||
|
||||
rsaKey = decoder.decode(encoder.encode(certificate.getComponentByName('tbsCertificate').getComponentByName('subjectPublicKeyInfo').getComponentByName('subjectPublicKey'))[3:], asn1Spec=RSAPublicKey())[0]
|
||||
return rsaKey.getComponentByName('modulus')._value , rsaKey.getComponentByName('publicExponent')._value
|
||||
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ class Tag(object):
|
||||
|
||||
def berPC(pc):
|
||||
"""
|
||||
Return BER_CONSTRUCT if true
|
||||
@summary: Return BER_CONSTRUCT if true
|
||||
BER_PRIMITIVE if false
|
||||
@param pc: boolean
|
||||
@return: BerPc value
|
||||
@@ -62,7 +62,7 @@ def berPC(pc):
|
||||
|
||||
def readLength(s):
|
||||
"""
|
||||
Read length of BER structure
|
||||
@summary: Read length of BER structure
|
||||
length be on 1 2 or 3 bytes
|
||||
@param s: stream
|
||||
@return: int or Python long
|
||||
@@ -86,7 +86,7 @@ def readLength(s):
|
||||
|
||||
def writeLength(size):
|
||||
"""
|
||||
Return structure length as expected in BER specification
|
||||
@summary: Return structure length as expected in BER specification
|
||||
@param size: int or python long
|
||||
@return: UInt8 or (UInt8(0x82), UInt16Be)
|
||||
"""
|
||||
@@ -97,7 +97,7 @@ def writeLength(size):
|
||||
|
||||
def readUniversalTag(s, tag, pc):
|
||||
"""
|
||||
Read tag of BER packet
|
||||
@summary: Read tag of BER packet
|
||||
@param tag: Tag class attributes
|
||||
@param pc: boolean
|
||||
@return: true if tag is correctly read
|
||||
@@ -108,7 +108,7 @@ def readUniversalTag(s, tag, pc):
|
||||
|
||||
def writeUniversalTag(tag, pc):
|
||||
"""
|
||||
Return universal tag byte
|
||||
@summary: Return universal tag byte
|
||||
@param tag: tag class attributes
|
||||
@param pc: boolean
|
||||
@return: UInt8
|
||||
@@ -117,7 +117,7 @@ def writeUniversalTag(tag, pc):
|
||||
|
||||
def readApplicationTag(s, tag):
|
||||
"""
|
||||
Read application tag
|
||||
@summary: Read application tag
|
||||
@param s: stream
|
||||
@param tag: tag class attributes
|
||||
@return: length of application packet
|
||||
@@ -138,7 +138,7 @@ def readApplicationTag(s, tag):
|
||||
|
||||
def writeApplicationTag(tag, size):
|
||||
"""
|
||||
Return structure that represent BER application tag
|
||||
@summary: Return structure that represent BER application tag
|
||||
@param tag: int python that match an uint8(0xff)
|
||||
@param size: size to rest of packet
|
||||
"""
|
||||
@@ -149,7 +149,7 @@ def writeApplicationTag(tag, size):
|
||||
|
||||
def readBoolean(s):
|
||||
"""
|
||||
Return boolean
|
||||
@summary: Return boolean
|
||||
@param s: stream
|
||||
@return: boolean
|
||||
"""
|
||||
@@ -164,7 +164,7 @@ def readBoolean(s):
|
||||
|
||||
def writeBoolean(b):
|
||||
"""
|
||||
Return structure that represent boolean in BER specification
|
||||
@summary: Return structure that represent boolean in BER specification
|
||||
@param b: boolean
|
||||
@return: BER boolean block
|
||||
"""
|
||||
@@ -175,7 +175,7 @@ def writeBoolean(b):
|
||||
|
||||
def readInteger(s):
|
||||
"""
|
||||
Read integer structure from stream
|
||||
@summary: Read integer structure from stream
|
||||
@param s: stream
|
||||
@return: int or long python
|
||||
"""
|
||||
@@ -207,7 +207,7 @@ def readInteger(s):
|
||||
|
||||
def writeInteger(value):
|
||||
"""
|
||||
Write integer value
|
||||
@summary: Write integer value
|
||||
@param param: INT or Python long
|
||||
@return: BER integer block
|
||||
"""
|
||||
@@ -220,7 +220,7 @@ def writeInteger(value):
|
||||
|
||||
def readOctetString(s):
|
||||
"""
|
||||
Read BER string structure
|
||||
@summary: Read BER string structure
|
||||
@param s: stream
|
||||
@return: string python
|
||||
"""
|
||||
@@ -231,7 +231,7 @@ def readOctetString(s):
|
||||
|
||||
def writeOctetstring(value):
|
||||
"""
|
||||
Write string in BER representation
|
||||
@summary: Write string in BER representation
|
||||
@param value: string
|
||||
@return: BER octet string block
|
||||
"""
|
||||
@@ -239,7 +239,7 @@ def writeOctetstring(value):
|
||||
|
||||
def readEnumerated(s):
|
||||
"""
|
||||
Read enumerated structure
|
||||
@summary: Read enumerated structure
|
||||
@param s: Stream
|
||||
@return: int or long
|
||||
"""
|
||||
@@ -253,7 +253,7 @@ def readEnumerated(s):
|
||||
|
||||
def writeEnumerated(enumerated):
|
||||
"""
|
||||
Write enumerated structure
|
||||
@summary: Write enumerated structure
|
||||
@param s: Stream
|
||||
@return: BER enumerated block
|
||||
"""
|
||||
|
||||
@@ -299,7 +299,7 @@ class ServerCertificate(CompositeType):
|
||||
@summary: Server certificate structure
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240521.aspx
|
||||
"""
|
||||
def __init__(self, readLen, conditional):
|
||||
def __init__(self, readLen = None, conditional = lambda:True):
|
||||
CompositeType.__init__(self, readLen = readLen, conditional = conditional)
|
||||
self.dwVersion = UInt32Le()
|
||||
|
||||
@@ -307,10 +307,10 @@ class ServerCertificate(CompositeType):
|
||||
"""
|
||||
Closure for capability factory
|
||||
"""
|
||||
for c in [ProprietaryServerCertificate]:
|
||||
for c in [ProprietaryServerCertificate, X509CertificateChain]:
|
||||
if self.dwVersion.value & 0x7fffffff == c._TYPE_:
|
||||
return c()
|
||||
raise InvalidExpectedDataException("unknown certificate type : %s (RDPY doesn't support x.509 format please repport a bug)"%hex(self.dwVersion.value))
|
||||
raise InvalidExpectedDataException("unknown certificate type : %s "%hex(self.dwVersion.value))
|
||||
|
||||
self.certData = FactoryType(CertificateFactory)
|
||||
|
||||
@@ -331,6 +331,29 @@ class ProprietaryServerCertificate(CompositeType):
|
||||
self.wSignatureBlobType = UInt16Le(0x0008, constant = True)
|
||||
self.wSignatureBlobLen = UInt16Le(lambda:sizeof(self.SignatureBlob))
|
||||
self.SignatureBlob = String(readLen = self.wSignatureBlobLen)
|
||||
|
||||
class CertBlob(CompositeType):
|
||||
"""
|
||||
@summary: certificate blob, contain x509 data
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc241911.aspx
|
||||
"""
|
||||
def __init__(self):
|
||||
CompositeType.__init__(self)
|
||||
self.cbCert = UInt32Le(lambda:sizeof(self.abCert))
|
||||
self.abCert = String(readLen = self.cbCert)
|
||||
|
||||
class X509CertificateChain(CompositeType):
|
||||
"""
|
||||
@summary: X509 certificate chain
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc241910.aspx
|
||||
"""
|
||||
_TYPE_ = CertificateType.CERT_CHAIN_VERSION_2
|
||||
|
||||
def __init__(self):
|
||||
CompositeType.__init__(self)
|
||||
self.NumCertBlobs = UInt32Le()
|
||||
self.CertBlobArray = ArrayType(CertBlob, readLen = self.NumCertBlobs)
|
||||
self.padding = String(readLen = UInt8(lambda:(8 + 4 * self.NumCertBlobs.value)))
|
||||
|
||||
class RSAPublicKey(CompositeType):
|
||||
"""
|
||||
|
||||
@@ -25,7 +25,8 @@
|
||||
from rdpy.core.type import CompositeType, UInt8, UInt16Le, UInt32Le, String, sizeof, FactoryType, ArrayType, Stream
|
||||
from rdpy.core.error import InvalidExpectedDataException
|
||||
import rdpy.core.log as log
|
||||
import rc4, sec
|
||||
import rc4, sec, gcc
|
||||
from rdpy.core import x509
|
||||
|
||||
class MessageType(object):
|
||||
"""
|
||||
|
||||
@@ -306,7 +306,7 @@ class Client(MCSLayer):
|
||||
|
||||
#static virtual channel
|
||||
if self._nbChannelRequested < self._serverSettings.getBlock(gcc.MessageType.SC_NET).channelCount.value:
|
||||
channelId = self._serverSettings.getBlock(gcc.MessageType.SC_NET).channelIdArray._array[self._nbChannelRequested]
|
||||
channelId = self._serverSettings.getBlock(gcc.MessageType.SC_NET).channelIdArray[self._nbChannelRequested]
|
||||
self._nbChannelRequested += 1
|
||||
self.sendChannelJoinRequest(channelId)
|
||||
return
|
||||
@@ -384,7 +384,7 @@ class Client(MCSLayer):
|
||||
if confirm == 0:
|
||||
serverNet = self._serverSettings.getBlock(gcc.MessageType.SC_NET)
|
||||
for i in range(0, serverNet.channelCount.value):
|
||||
if channelId == serverNet.channelIdArray._array[i].value:
|
||||
if channelId == serverNet.channelIdArray[i].value:
|
||||
self._channels[channelId] = self._virtualChannels[i][1]
|
||||
|
||||
self.connectNextChannel()
|
||||
|
||||
@@ -26,7 +26,7 @@ import gcc, lic, tpkt
|
||||
from rdpy.core.type import CompositeType, 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.core import log, x509
|
||||
|
||||
class SecurityFlag(object):
|
||||
"""
|
||||
@@ -155,6 +155,9 @@ def sessionKeyBlob(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()
|
||||
@@ -179,6 +182,9 @@ def macData(macSaltKey, data):
|
||||
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()
|
||||
@@ -514,9 +520,17 @@ class Client(SecLayer):
|
||||
self._encryptRc4 = rc4.RC4Key(self._currentEncryptKey)
|
||||
|
||||
#send client random encrypted with
|
||||
certificate = self._transport.getGCCServerSettings().SC_SECURITY.serverCertificate.certData
|
||||
certificate = self._transport.getGCCServerSettings().SC_SECURITY.serverCertificate.certData._value
|
||||
if isinstance(certificate, gcc.ProprietaryServerCertificate):
|
||||
modulus = bin2bn(certificate.PublicKeyBlob.modulus.value[::-1])
|
||||
publicExponent = certificate.PublicKeyBlob.pubExp.value
|
||||
elif isinstance(certificate, gcc.X509CertificateChain):
|
||||
modulus, publicExponent = x509.extractRSAKey(x509.load(certificate.CertBlobArray[-1].abCert.value))
|
||||
else:
|
||||
raise InvalidExpectedDataException("unknown certificate type")
|
||||
|
||||
#reverse because bignum in little endian
|
||||
serverPublicKey = rsa.PublicKey(bin2bn(certificate.PublicKeyBlob.modulus.value[::-1]), certificate.PublicKeyBlob.pubExp.value)
|
||||
serverPublicKey = rsa.PublicKey(modulus, publicExponent)
|
||||
|
||||
message = ClientSecurityExchangePDU()
|
||||
#reverse because bignum in little endian
|
||||
|
||||
@@ -132,7 +132,7 @@ class X224Layer(LayerAutomata, IStreamSender):
|
||||
LayerAutomata.__init__(self, presentation)
|
||||
#default selectedProtocol is SSl
|
||||
#client requested selectedProtocol
|
||||
self._requestedProtocol = Protocols.PROTOCOL_RDP
|
||||
self._requestedProtocol = Protocols.PROTOCOL_RDP
|
||||
#server selected selectedProtocol
|
||||
self._selectedProtocol = Protocols.PROTOCOL_SSL
|
||||
|
||||
|
||||
Reference in New Issue
Block a user