add support for x509 certificate

This commit is contained in:
speyrefitte
2014-12-12 18:14:12 +01:00
parent f36f8222df
commit f9f92b8215
8 changed files with 229 additions and 25 deletions

View File

@@ -960,6 +960,13 @@ class ArrayType(Type):
""" """
s.writeType(self._array) 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): def __sizeof__(self):
""" """
@summary: Size in bytes of all inner type @summary: Size in bytes of all inner type

159
rdpy/core/x509.py Normal file
View 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

View File

@@ -50,7 +50,7 @@ class Tag(object):
def berPC(pc): def berPC(pc):
""" """
Return BER_CONSTRUCT if true @summary: Return BER_CONSTRUCT if true
BER_PRIMITIVE if false BER_PRIMITIVE if false
@param pc: boolean @param pc: boolean
@return: BerPc value @return: BerPc value
@@ -62,7 +62,7 @@ def berPC(pc):
def readLength(s): def readLength(s):
""" """
Read length of BER structure @summary: Read length of BER structure
length be on 1 2 or 3 bytes length be on 1 2 or 3 bytes
@param s: stream @param s: stream
@return: int or Python long @return: int or Python long
@@ -86,7 +86,7 @@ def readLength(s):
def writeLength(size): 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 @param size: int or python long
@return: UInt8 or (UInt8(0x82), UInt16Be) @return: UInt8 or (UInt8(0x82), UInt16Be)
""" """
@@ -97,7 +97,7 @@ def writeLength(size):
def readUniversalTag(s, tag, pc): def readUniversalTag(s, tag, pc):
""" """
Read tag of BER packet @summary: Read tag of BER packet
@param tag: Tag class attributes @param tag: Tag class attributes
@param pc: boolean @param pc: boolean
@return: true if tag is correctly read @return: true if tag is correctly read
@@ -108,7 +108,7 @@ def readUniversalTag(s, tag, pc):
def writeUniversalTag(tag, pc): def writeUniversalTag(tag, pc):
""" """
Return universal tag byte @summary: Return universal tag byte
@param tag: tag class attributes @param tag: tag class attributes
@param pc: boolean @param pc: boolean
@return: UInt8 @return: UInt8
@@ -117,7 +117,7 @@ def writeUniversalTag(tag, pc):
def readApplicationTag(s, tag): def readApplicationTag(s, tag):
""" """
Read application tag @summary: Read application tag
@param s: stream @param s: stream
@param tag: tag class attributes @param tag: tag class attributes
@return: length of application packet @return: length of application packet
@@ -138,7 +138,7 @@ def readApplicationTag(s, tag):
def writeApplicationTag(tag, size): 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 tag: int python that match an uint8(0xff)
@param size: size to rest of packet @param size: size to rest of packet
""" """
@@ -149,7 +149,7 @@ def writeApplicationTag(tag, size):
def readBoolean(s): def readBoolean(s):
""" """
Return boolean @summary: Return boolean
@param s: stream @param s: stream
@return: boolean @return: boolean
""" """
@@ -164,7 +164,7 @@ def readBoolean(s):
def writeBoolean(b): def writeBoolean(b):
""" """
Return structure that represent boolean in BER specification @summary: Return structure that represent boolean in BER specification
@param b: boolean @param b: boolean
@return: BER boolean block @return: BER boolean block
""" """
@@ -175,7 +175,7 @@ def writeBoolean(b):
def readInteger(s): def readInteger(s):
""" """
Read integer structure from stream @summary: Read integer structure from stream
@param s: stream @param s: stream
@return: int or long python @return: int or long python
""" """
@@ -207,7 +207,7 @@ def readInteger(s):
def writeInteger(value): def writeInteger(value):
""" """
Write integer value @summary: Write integer value
@param param: INT or Python long @param param: INT or Python long
@return: BER integer block @return: BER integer block
""" """
@@ -220,7 +220,7 @@ def writeInteger(value):
def readOctetString(s): def readOctetString(s):
""" """
Read BER string structure @summary: Read BER string structure
@param s: stream @param s: stream
@return: string python @return: string python
""" """
@@ -231,7 +231,7 @@ def readOctetString(s):
def writeOctetstring(value): def writeOctetstring(value):
""" """
Write string in BER representation @summary: Write string in BER representation
@param value: string @param value: string
@return: BER octet string block @return: BER octet string block
""" """
@@ -239,7 +239,7 @@ def writeOctetstring(value):
def readEnumerated(s): def readEnumerated(s):
""" """
Read enumerated structure @summary: Read enumerated structure
@param s: Stream @param s: Stream
@return: int or long @return: int or long
""" """
@@ -253,7 +253,7 @@ def readEnumerated(s):
def writeEnumerated(enumerated): def writeEnumerated(enumerated):
""" """
Write enumerated structure @summary: Write enumerated structure
@param s: Stream @param s: Stream
@return: BER enumerated block @return: BER enumerated block
""" """

View File

@@ -299,7 +299,7 @@ class ServerCertificate(CompositeType):
@summary: Server certificate structure @summary: Server certificate structure
@see: http://msdn.microsoft.com/en-us/library/cc240521.aspx @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) CompositeType.__init__(self, readLen = readLen, conditional = conditional)
self.dwVersion = UInt32Le() self.dwVersion = UInt32Le()
@@ -307,10 +307,10 @@ class ServerCertificate(CompositeType):
""" """
Closure for capability factory Closure for capability factory
""" """
for c in [ProprietaryServerCertificate]: for c in [ProprietaryServerCertificate, X509CertificateChain]:
if self.dwVersion.value & 0x7fffffff == c._TYPE_: if self.dwVersion.value & 0x7fffffff == c._TYPE_:
return c() 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) self.certData = FactoryType(CertificateFactory)
@@ -332,6 +332,29 @@ class ProprietaryServerCertificate(CompositeType):
self.wSignatureBlobLen = UInt16Le(lambda:sizeof(self.SignatureBlob)) self.wSignatureBlobLen = UInt16Le(lambda:sizeof(self.SignatureBlob))
self.SignatureBlob = String(readLen = self.wSignatureBlobLen) 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): class RSAPublicKey(CompositeType):
""" """
@see: http://msdn.microsoft.com/en-us/library/cc240520.aspx @see: http://msdn.microsoft.com/en-us/library/cc240520.aspx

View File

@@ -25,7 +25,8 @@
from rdpy.core.type import CompositeType, UInt8, UInt16Le, UInt32Le, String, sizeof, FactoryType, ArrayType, Stream from rdpy.core.type import CompositeType, UInt8, UInt16Le, UInt32Le, String, sizeof, FactoryType, ArrayType, Stream
from rdpy.core.error import InvalidExpectedDataException from rdpy.core.error import InvalidExpectedDataException
import rdpy.core.log as log import rdpy.core.log as log
import rc4, sec import rc4, sec, gcc
from rdpy.core import x509
class MessageType(object): class MessageType(object):
""" """

View File

@@ -306,7 +306,7 @@ class Client(MCSLayer):
#static virtual channel #static virtual channel
if self._nbChannelRequested < self._serverSettings.getBlock(gcc.MessageType.SC_NET).channelCount.value: 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._nbChannelRequested += 1
self.sendChannelJoinRequest(channelId) self.sendChannelJoinRequest(channelId)
return return
@@ -384,7 +384,7 @@ class Client(MCSLayer):
if confirm == 0: if confirm == 0:
serverNet = self._serverSettings.getBlock(gcc.MessageType.SC_NET) serverNet = self._serverSettings.getBlock(gcc.MessageType.SC_NET)
for i in range(0, serverNet.channelCount.value): 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._channels[channelId] = self._virtualChannels[i][1]
self.connectNextChannel() self.connectNextChannel()

View File

@@ -26,7 +26,7 @@ import gcc, lic, tpkt
from rdpy.core.type import CompositeType, Stream, UInt32Le, UInt16Le, String, sizeof, UInt8 from rdpy.core.type import CompositeType, Stream, UInt32Le, UInt16Le, String, sizeof, UInt8
from rdpy.core.layer import LayerAutomata, IStreamSender from rdpy.core.layer import LayerAutomata, IStreamSender
from rdpy.core.error import InvalidExpectedDataException from rdpy.core.error import InvalidExpectedDataException
from rdpy.core import log from rdpy.core import log, x509
class SecurityFlag(object): class SecurityFlag(object):
""" """
@@ -155,6 +155,9 @@ def sessionKeyBlob(secret, random1, random2):
def macData(macSaltKey, data): def macData(macSaltKey, data):
""" """
@see: http://msdn.microsoft.com/en-us/library/cc241995.aspx @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() sha1Digest = sha.new()
md5Digest = md5.new() md5Digest = md5.new()
@@ -179,6 +182,9 @@ def macData(macSaltKey, data):
def tempKey(initialKey, currentKey): def tempKey(initialKey, currentKey):
""" """
@see: http://msdn.microsoft.com/en-us/library/cc240792.aspx @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() sha1Digest = sha.new()
md5Digest = md5.new() md5Digest = md5.new()
@@ -514,9 +520,17 @@ class Client(SecLayer):
self._encryptRc4 = rc4.RC4Key(self._currentEncryptKey) self._encryptRc4 = rc4.RC4Key(self._currentEncryptKey)
#send client random encrypted with #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 #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() message = ClientSecurityExchangePDU()
#reverse because bignum in little endian #reverse because bignum in little endian