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

@@ -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
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):
"""
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
"""

View File

@@ -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):
"""

View File

@@ -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):
"""

View File

@@ -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()

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.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

View File

@@ -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