folder reorganization

This commit is contained in:
cttynul
2019-04-23 14:32:53 +02:00
parent 659751b2f4
commit 8e7ee78a87
1195 changed files with 267003 additions and 2 deletions
+6
View File
@@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
import os
import sys
sys.path.append(os.path.dirname(__file__))
+390
View File
@@ -0,0 +1,390 @@
# -*- coding: utf-8 -*-
import os
import re
from nmb.NetBIOS import NetBIOS
from platformcode import logger
from smb.SMBConnection import SMBConnection
GitHub = 'https://github.com/miketeo/pysmb' #buscar aquí de vez en cuando la última versiónde SMB-pysmb, y actualizar en Alfa
vesion_actual_pysmb = '1.1.25' #actualizada el 25/11/2018
remote = None
def parse_url(url):
# logger.info("Url: %s" % url)
url = url.strip()
patron = "^smb://(?:([^;\n]+);)?(?:([^:@\n]+)[:|@])?(?:([^@\n]+)@)?([^/]+)/([^/\n]+)([/]?.*?)$"
domain, user, password, server_name, share_name, path = re.compile(patron, re.DOTALL).match(url).groups()
server_name, server_ip = get_server_name_ip(server_name)
if not user: user = 'guest'
if not password: password = ""
if not domain: domain = ""
if path.endswith("/"): path = path[:-1]
if not path: path = "/"
# logger.info("Dominio: '%s' |Usuario: '%s' | Password: '%s' | Servidor: '%s' | IP: '%s' | Share Name: '%s' | Path: '%s'" % (domain, user, password, server_name, server_ip, share_name, path))
return server_name, server_ip, share_name, unicode(path, "utf8"), user, password, domain
def get_server_name_ip(server):
if re.compile("^\d+.\d+.\d+.\d+$").findall(server) or re.compile("^([^\.]+\.(?:[^\.]+\.)?(?:\w+)?)$").findall(server):
server_ip = server
server_name = None
else:
server_ip = None
server_name = server.upper()
if not server_ip: server_ip = NetBIOS().queryName(server_name)[0]
if not server_name: server_name = NetBIOS().queryIPForName(server_ip)[0]
return server_name, server_ip
def connect(url):
# logger.info("Url: %s" % url)
global remote
server_name, server_ip, share_name, path, user, password, domain = parse_url(url)
#Da problemas asumir que la sesión está abierta. Si se abrió pero ha caducado, dará error. Mejor conectar siempre
"""
if not remote or not remote.sock or not server_name == remote.remote_name:
remote = SMBConnection(user, password, domain, server_name)
remote.connect(server_ip, 139)
"""
remote = SMBConnection(user, password, domain, server_name)
remote.connect(ip=server_ip, timeout=20)
return remote, share_name, path
def listdir(url):
logger.info("Url: %s" % url)
remote, share_name, path = connect(url)
try:
files = [f.filename for f in remote.listPath(share_name, path) if not f.filename in [".", ".."]]
return files
except Exception, e:
raise type(e)(e.message, "")
def walk(url, topdown=True, onerror=None):
logger.info("Url: %s" % url)
remote, share_name, path = connect(url)
try:
names = remote.listPath(share_name, path)
except Exception, _err:
if onerror is not None:
onerror(_err)
return
dirs, nondirs = [], []
for name in names:
if name.filename in [".", ".."]:
continue
if name.isDirectory:
dirs.append(name.filename)
else:
nondirs.append(name.filename)
if topdown:
yield unicode(url, "utf8"), dirs, nondirs
for name in dirs:
new_path = "/".join(url.split("/") + [name.encode("utf8")])
for x in walk(new_path, topdown, onerror):
yield x
if not topdown:
yield unicode(url, "utf8"), dirs, nondirs
def get_attributes(url):
logger.info("Url: %s" % url)
remote, share_name, path = connect(url)
try:
return remote.getAttributes(share_name, path)
except Exception, e:
raise type(e)(e.message, "")
def mkdir(url):
logger.info("Url: %s" % url)
remote, share_name, path = connect(url)
try:
remote.createDirectory(share_name, path)
except Exception, e:
raise type(e)(e.message, "")
def smb_open(url, mode):
logger.info("Url: %s" % url)
return SMBFile(url, mode)
def isfile(url):
logger.info("Url: %s" % url)
remote, share_name, path = connect(url)
try:
files = [f.filename for f in remote.listPath(share_name, os.path.dirname(path)) if not f.isDirectory]
except Exception, e:
raise type(e)(e.message, "")
return os.path.basename(path) in files
def isdir(url):
logger.info("Url: %s" % url)
remote, share_name, path = connect(url)
try:
folders = [f.filename for f in remote.listPath(share_name, os.path.dirname(path)) if f.isDirectory]
except Exception, e:
raise type(e)(e.message, "")
return os.path.basename(path) in folders or path == "/"
def exists(url):
logger.info("Url: %s" % url)
remote, share_name, path = connect(url)
try:
files = [f.filename for f in remote.listPath(share_name, os.path.dirname(path))]
except Exception, e:
raise type(e)(e.message, "")
return os.path.basename(path) in files or path == "/"
def remove(url):
logger.info("Url: %s" % url)
remote, share_name, path = connect(url)
try:
remote.deleteFiles(share_name, path)
except Exception, e:
raise type(e)(e.message, "")
def rmdir(url):
logger.info("Url: %s" % url)
remote, share_name, path = connect(url)
try:
remote.deleteDirectory(share_name, path)
except Exception, e:
raise type(e)(e.message, "")
def rename(url, new_name):
logger.info("Url: %s" % url)
remote, share_name, path = connect(url)
_, _, _, new_name, _, _, _ = parse_url(new_name)
try:
remote.rename(share_name, path, new_name)
except Exception, e:
raise type(e)(e.message, "")
class SMBFile(object):
def __init__(self, url, mode="r"):
import random
try:
import xbmc
except:
xbmc = None
self.url = url
self.remote, self.share, self.path = path = connect(url)
self.mode = mode
self.binary = False
self.canread = False
self.canwrite = False
self.closed = True
self.size = 0
self.pos = 0
if xbmc:
self.tmp_path = os.path.join(xbmc.translatePath("special://temp/"), "%08x" % (random.getrandbits(32)))
else:
self.tmp_path = os.path.join(os.getenv("TEMP") or os.getenv("TMP") or os.getenv("TMPDIR"),
"%08x" % (random.getrandbits(32)))
self.tmp_file = None
self.__get_mode__()
def __del__(self):
if self.tmp_file:
self.tmp_file.close()
if os.path.isfile(self.tmp_path):
os.remove(self.tmp_path)
def tmpfile(self):
if self.tmp_file:
self.tmp_file.close()
self.tmp_file = open(self.tmp_path, "w+b")
return self.tmp_file
def __get_mode__(self):
if "r+" in self.mode:
try:
attr = self.remote.getAttributes(self.share, self.path)
except Exception, e:
raise type(e)(e.message, "")
self.size = attr.file_size
self.canread = True
self.canwrite = True
self.closed = False
elif "r" in self.mode:
try:
attr = self.remote.getAttributes(self.share, self.path)
except Exception, e:
raise type(e)(e.message, "")
self.size = attr.file_size
self.canread = True
self.closed = False
elif "w+" in self.mode:
try:
self.remote.storeFileFromOffset(self.share, self.path, self.tmpfile(), 0, truncate=True)
except Exception, e:
raise type(e)(e.message, "")
self.canread = True
self.canwrite = True
self.closed = False
elif "w" in self.mode:
try:
self.remote.storeFileFromOffset(self.share, self.path, self.tmpfile(), 0, truncate=True)
except Exception, e:
raise type(e)(e.message, "")
self.canwrite = True
self.closed = False
elif "a+" in self.mode:
try:
self.remote.storeFileFromOffset(self.share, self.path, self.tmpfile(), 0)
attr = self.remote.getAttributes(self.share, self.path)
except Exception, e:
raise type(e)(e.message, "")
self.size = attr.file_size
self.pos = self.size
self.canwrite = True
self.canread = True
self.closed = False
elif "a" in self.mode:
try:
self.remote.storeFileFromOffset(self.share, self.path, self.tmpfile(), 0)
attr = self.remote.getAttributes(self.share, self.path)
except Exception, e:
raise type(e)(e.message, "")
self.size = attr.file_size
self.pos = self.size
self.canwrite = True
self.closed = False
if "b" in self.mode:
self.binary = True
def seek(self, offset, whence=0):
if whence == 0:
self.pos = offset
if whence == 1:
self.pos += offset
if whence == 2:
self.pos = self.size + offset
if self.pos < 0: self.pos = 0
def tell(self):
return self.pos
def write(self, data):
if not self.canwrite:
raise IOError("File not open for writing")
f = self.tmpfile()
f.write(data)
f.seek(0)
self.remote.storeFileFromOffset(self.share, self.path, f, self.pos)
self.pos += len(data)
if self.pos > self.size:
self.size = self.pos
def read(self, size=-1L):
if not self.canread:
raise IOError("File not open for reading")
f = self.tmpfile()
self.remote.retrieveFileFromOffset(self.share, self.path, f, self.pos, size)
f.seek(0)
data = f.read()
self.seek(len(data), 1)
return data
def truncate(self, size=None):
if not self.canwrite:
raise IOError("File not open for writing")
data = self.read(size)
f = self.tmpfile()
self.pos = 0
f.write(data)
f.seek(0)
self.remote.storeFileFromOffset(self.share, self.path, f, self.pos, truncate=True)
def close(self):
self.remote.close()
self.closed = True
self.canwrite = False
self.canread = False
def flush(self):
pass
def writelines(self, sequence):
for line in sequence:
self.write(line)
def readlines(self, sizehint=0):
if not self.canread:
raise IOError("File not open for reading")
f = self.tmpfile()
self.remote.retrieveFileFromOffset(self.share, self.path, f, self.pos)
f.seek(0)
data = f.readlines(sizehint)
self.pos += len(data)
if not self.binary:
data = [l.replace("\r", "") for l in data]
return data
def readline(self, size=-1):
if not self.canread:
raise IOError("File not open for reading")
f = self.tmpfile()
self.remote.retrieveFileFromOffset(self.share, self.path, f, self.pos, size)
f.seek(0)
data = f.readline(size)
self.pos += len(data)
if not self.binary:
data = data.replace("\r", "")
return data
def __iter__(self):
return self.readlines().__iter__()
def xreadlines(self):
return self.__iter__()
def __repr__(self):
return self.__str__()
def __str__(self):
return "<open SMBFile '%s', mode '%s' at %s>" % (self.url, self.mode, hex(id(self)))
@property
def __class__(self):
return "<type 'file'>"
+144
View File
@@ -0,0 +1,144 @@
import os, logging, random, socket, time, select
from base import NBNS, NotConnectedError
from nmb_constants import TYPE_CLIENT, TYPE_SERVER, TYPE_WORKSTATION
class NetBIOS(NBNS):
log = logging.getLogger('NMB.NetBIOS')
def __init__(self, broadcast = True, listen_port = 0):
"""
Instantiate a NetBIOS instance, and creates a IPv4 UDP socket to listen/send NBNS packets.
:param boolean broadcast: A boolean flag to indicate if we should setup the listening UDP port in broadcast mode
:param integer listen_port: Specifies the UDP port number to bind to for listening. If zero, OS will automatically select a free port number.
"""
self.broadcast = broadcast
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
if self.broadcast:
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
if listen_port:
self.sock.bind(( '', listen_port ))
def close(self):
"""
Close the underlying and free resources.
The NetBIOS instance should not be used to perform any operations after this method returns.
:return: None
"""
self.sock.close()
self.sock = None
def write(self, data, ip, port):
assert self.sock, 'Socket is already closed'
self.sock.sendto(data, ( ip, port ))
def queryName(self, name, ip = '', port = 137, timeout = 30):
"""
Send a query on the network and hopes that if machine matching the *name* will reply with its IP address.
:param string ip: If the NBNSProtocol instance was instianted with broadcast=True, then this parameter can be an empty string. We will leave it to the OS to determine an appropriate broadcast address.
If the NBNSProtocol instance was instianted with broadcast=False, then you should provide a target IP to send the query.
:param integer port: The NetBIOS-NS port (IANA standard defines this port to be 137). You should not touch this parameter unless you know what you are doing.
:param integer/float timeout: Number of seconds to wait for a reply, after which the method will return None
:return: A list of IP addresses in dotted notation (aaa.bbb.ccc.ddd). On timeout, returns None.
"""
assert self.sock, 'Socket is already closed'
trn_id = random.randint(1, 0xFFFF)
data = self.prepareNameQuery(trn_id, name)
if self.broadcast and not ip:
ip = '<broadcast>'
elif not ip:
self.log.warning('queryName: ip parameter is empty. OS might not transmit this query to the network')
self.write(data, ip, port)
return self._pollForNetBIOSPacket(trn_id, timeout)
def queryIPForName(self, ip, port = 137, timeout = 30):
"""
Send a query to the machine with *ip* and hopes that the machine will reply back with its name.
The implementation of this function is contributed by Jason Anderson.
:param string ip: If the NBNSProtocol instance was instianted with broadcast=True, then this parameter can be an empty string. We will leave it to the OS to determine an appropriate broadcast address.
If the NBNSProtocol instance was instianted with broadcast=False, then you should provide a target IP to send the query.
:param integer port: The NetBIOS-NS port (IANA standard defines this port to be 137). You should not touch this parameter unless you know what you are doing.
:param integer/float timeout: Number of seconds to wait for a reply, after which the method will return None
:return: A list of string containing the names of the machine at *ip*. On timeout, returns None.
"""
assert self.sock, 'Socket is already closed'
trn_id = random.randint(1, 0xFFFF)
data = self.prepareNetNameQuery(trn_id, False)
self.write(data, ip, port)
ret = self._pollForQueryPacket(trn_id, timeout)
if ret:
return map(lambda s: s[0], filter(lambda s: s[1] == TYPE_SERVER, ret))
else:
return None
#
# Protected Methods
#
def _pollForNetBIOSPacket(self, wait_trn_id, timeout):
end_time = time.time() + timeout
while True:
try:
_timeout = end_time - time.time()
if _timeout <= 0:
return None
ready, _, _ = select.select([ self.sock.fileno() ], [ ], [ ], _timeout)
if not ready:
return None
data, _ = self.sock.recvfrom(0xFFFF)
if len(data) == 0:
raise NotConnectedError
trn_id, ret = self.decodePacket(data)
if trn_id == wait_trn_id:
return ret
except select.error, ex:
if type(ex) is types.TupleType:
if ex[0] != errno.EINTR and ex[0] != errno.EAGAIN:
raise ex
else:
raise ex
#
# Contributed by Jason Anderson
#
def _pollForQueryPacket(self, wait_trn_id, timeout):
end_time = time.time() + timeout
while True:
try:
_timeout = end_time - time.time()
if _timeout <= 0:
return None
ready, _, _ = select.select([ self.sock.fileno() ], [ ], [ ], _timeout)
if not ready:
return None
data, _ = self.sock.recvfrom(0xFFFF)
if len(data) == 0:
raise NotConnectedError
trn_id, ret = self.decodeIPQueryPacket(data)
if trn_id == wait_trn_id:
return ret
except select.error, ex:
if type(ex) is types.TupleType:
if ex[0] != errno.EINTR and ex[0] != errno.EAGAIN:
raise ex
else:
raise ex
+136
View File
@@ -0,0 +1,136 @@
import os, logging, random, socket, time
from twisted.internet import reactor, defer
from twisted.internet.protocol import DatagramProtocol
from nmb_constants import TYPE_SERVER
from base import NBNS
IP_QUERY, NAME_QUERY = range(2)
class NetBIOSTimeout(Exception):
"""Raised in NBNSProtocol via Deferred.errback method when queryName method has timeout waiting for reply"""
pass
class NBNSProtocol(DatagramProtocol, NBNS):
log = logging.getLogger('NMB.NBNSProtocol')
def __init__(self, broadcast = True, listen_port = 0):
"""
Instantiate a NBNSProtocol instance.
This automatically calls reactor.listenUDP method to start listening for incoming packets, so you **must not** call the listenUDP method again.
:param boolean broadcast: A boolean flag to indicate if we should setup the listening UDP port in broadcast mode
:param integer listen_port: Specifies the UDP port number to bind to for listening. If zero, OS will automatically select a free port number.
"""
self.broadcast = broadcast
self.pending_trns = { } # TRN ID -> ( expiry_time, name, Deferred instance )
self.transport = reactor.listenUDP(listen_port, self)
if self.broadcast:
self.transport.getHandle().setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
reactor.callLater(1, self.cleanupPendingTrns)
def datagramReceived(self, data, from_info):
host, port = from_info
trn_id, ret = self.decodePacket(data)
# pending transaction exists for trn_id - handle it and remove from queue
if trn_id in self.pending_trns:
_, ip, d = self.pending_trns.pop(trn_id)
if ip is NAME_QUERY:
# decode as query packet
trn_id, ret = self.decodeIPQueryPacket(data)
d.callback(ret)
def write(self, data, ip, port):
# We don't use the transport.write method directly as it keeps raising DeprecationWarning for ip='<broadcast>'
self.transport.getHandle().sendto(data, ( ip, port ))
def queryName(self, name, ip = '', port = 137, timeout = 30):
"""
Send a query on the network and hopes that if machine matching the *name* will reply with its IP address.
:param string ip: If the NBNSProtocol instance was instianted with broadcast=True, then this parameter can be an empty string. We will leave it to the OS to determine an appropriate broadcast address.
If the NBNSProtocol instance was instianted with broadcast=False, then you should provide a target IP to send the query.
:param integer port: The NetBIOS-NS port (IANA standard defines this port to be 137). You should not touch this parameter unless you know what you are doing.
:param integer/float timeout: Number of seconds to wait for a reply, after which the returned Deferred instance will be called with a NetBIOSTimeout exception.
:return: A *twisted.internet.defer.Deferred* instance. The callback function will be called with a list of IP addresses in dotted notation (aaa.bbb.ccc.ddd).
On timeout, the errback function will be called with a Failure instance wrapping around a NetBIOSTimeout exception
"""
trn_id = random.randint(1, 0xFFFF)
while True:
if not self.pending_trns.has_key(trn_id):
break
else:
trn_id = (trn_id + 1) & 0xFFFF
data = self.prepareNameQuery(trn_id, name)
if self.broadcast and not ip:
ip = '<broadcast>'
elif not ip:
self.log.warning('queryName: ip parameter is empty. OS might not transmit this query to the network')
self.write(data, ip, port)
d = defer.Deferred()
self.pending_trns[trn_id] = ( time.time()+timeout, name, d )
return d
def queryIPForName(self, ip, port = 137, timeout = 30):
"""
Send a query to the machine with *ip* and hopes that the machine will reply back with its name.
The implementation of this function is contributed by Jason Anderson.
:param string ip: If the NBNSProtocol instance was instianted with broadcast=True, then this parameter can be an empty string. We will leave it to the OS to determine an appropriate broadcast address.
If the NBNSProtocol instance was instianted with broadcast=False, then you should provide a target IP to send the query.
:param integer port: The NetBIOS-NS port (IANA standard defines this port to be 137). You should not touch this parameter unless you know what you are doing.
:param integer/float timeout: Number of seconds to wait for a reply, after which the method will return None
:return: A *twisted.internet.defer.Deferred* instance. The callback function will be called with a list of names of the machine at *ip*.
On timeout, the errback function will be called with a Failure instance wrapping around a NetBIOSTimeout exception
"""
trn_id = random.randint(1, 0xFFFF)
while True:
if not self.pending_trns.has_key(trn_id):
break
else:
trn_id = (trn_id + 1) & 0xFFFF
data = self.prepareNetNameQuery(trn_id)
self.write(data, ip, port)
d = defer.Deferred()
d2 = defer.Deferred()
d2.addErrback(d.errback)
def stripCode(ret):
if ret is not None: # got valid response. Somehow the callback is also called when there is an error.
d.callback(map(lambda s: s[0], filter(lambda s: s[1] == TYPE_SERVER, ret)))
d2.addCallback(stripCode)
self.pending_trns[trn_id] = ( time.time()+timeout, NAME_QUERY, d2 )
return d
def stopProtocol(self):
DatagramProtocol.stopProtocol(self)
def cleanupPendingTrns(self):
now = time.time()
# reply should have been received in the past
expired = filter(lambda (trn_id, (expiry_time, name, d)): expiry_time < now, self.pending_trns.iteritems())
# remove expired items from dict + call errback
def expire_item(item):
trn_id, (expiry_time, name, d) = item
del self.pending_trns[trn_id]
try:
d.errback(NetBIOSTimeout(name))
except: pass
map(expire_item, expired)
if self.transport:
reactor.callLater(1, self.cleanupPendingTrns)
View File
+181
View File
@@ -0,0 +1,181 @@
import struct, logging, random
from nmb_constants import *
from nmb_structs import *
from utils import encode_name
class NMBSession:
log = logging.getLogger('NMB.NMBSession')
def __init__(self, my_name, remote_name, host_type = TYPE_SERVER, is_direct_tcp = False):
self.my_name = my_name.upper()
self.remote_name = remote_name.upper()
self.host_type = host_type
self.data_buf = ''
if is_direct_tcp:
self.data_nmb = DirectTCPSessionMessage()
self.sendNMBPacket = self._sendNMBPacket_DirectTCP
else:
self.data_nmb = NMBSessionMessage()
self.sendNMBPacket = self._sendNMBPacket_NetBIOS
#
# Overridden Methods
#
def write(self, data):
raise NotImplementedError
def onNMBSessionMessage(self, flags, data):
pass
def onNMBSessionOK(self):
pass
def onNMBSessionFailed(self):
pass
#
# Public Methods
#
def feedData(self, data):
self.data_buf = self.data_buf + data
offset = 0
while True:
length = self.data_nmb.decode(self.data_buf, offset)
if length == 0:
break
elif length > 0:
offset += length
self._processNMBSessionPacket(self.data_nmb)
else:
raise NMBError
if offset > 0:
self.data_buf = self.data_buf[offset:]
def sendNMBMessage(self, data):
self.sendNMBPacket(SESSION_MESSAGE, data)
def requestNMBSession(self):
my_name_encoded = encode_name(self.my_name, TYPE_WORKSTATION)
remote_name_encoded = encode_name(self.remote_name, self.host_type)
self.sendNMBPacket(SESSION_REQUEST, remote_name_encoded + my_name_encoded)
#
# Protected Methods
#
def _processNMBSessionPacket(self, packet):
if packet.type == SESSION_MESSAGE:
self.onNMBSessionMessage(packet.flags, packet.data)
elif packet.type == POSITIVE_SESSION_RESPONSE:
self.onNMBSessionOK()
elif packet.type == NEGATIVE_SESSION_RESPONSE:
self.onNMBSessionFailed()
elif packet.type == SESSION_KEEPALIVE:
# Discard keepalive packets - [RFC1002]: 5.2.2.1
pass
else:
self.log.warning('Unrecognized NMB session type: 0x%02x', packet.type)
def _sendNMBPacket_NetBIOS(self, packet_type, data):
length = len(data)
assert length <= 0x01FFFF
flags = 0
if length > 0xFFFF:
flags |= 0x01
length &= 0xFFFF
self.write(struct.pack('>BBH', packet_type, flags, length) + data)
def _sendNMBPacket_DirectTCP(self, packet_type, data):
length = len(data)
assert length <= 0x00FFFFFF
self.write(struct.pack('>I', length) + data)
class NBNS:
log = logging.getLogger('NMB.NBNS')
HEADER_STRUCT_FORMAT = '>HHHHHH'
HEADER_STRUCT_SIZE = struct.calcsize(HEADER_STRUCT_FORMAT)
def write(self, data, ip, port):
raise NotImplementedError
def decodePacket(self, data):
if len(data) < self.HEADER_STRUCT_SIZE:
raise Exception
trn_id, code, question_count, answer_count, authority_count, additional_count = struct.unpack(self.HEADER_STRUCT_FORMAT, data[:self.HEADER_STRUCT_SIZE])
is_response = bool((code >> 15) & 0x01)
opcode = (code >> 11) & 0x0F
flags = (code >> 4) & 0x7F
rcode = code & 0x0F
if opcode == 0x0000 and is_response:
name_len = ord(data[self.HEADER_STRUCT_SIZE])
offset = self.HEADER_STRUCT_SIZE+2+name_len+8 # constant 2 for the padding bytes before/after the Name and constant 8 for the Type, Class and TTL fields in the Answer section after the Name
record_count = (struct.unpack('>H', data[offset:offset+2])[0]) / 6
offset += 4 # Constant 4 for the Data Length and Flags field
ret = [ ]
for i in range(0, record_count):
ret.append('%d.%d.%d.%d' % struct.unpack('4B', (data[offset:offset + 4])))
offset += 6
return trn_id, ret
else:
return trn_id, None
def prepareNameQuery(self, trn_id, name, is_broadcast = True):
header = struct.pack(self.HEADER_STRUCT_FORMAT,
trn_id, (is_broadcast and 0x0110) or 0x0100, 1, 0, 0, 0)
payload = encode_name(name, 0x20) + '\x00\x20\x00\x01'
return header + payload
#
# Contributed by Jason Anderson
#
def decodeIPQueryPacket(self, data):
if len(data) < self.HEADER_STRUCT_SIZE:
raise Exception
trn_id, code, question_count, answer_count, authority_count, additional_count = struct.unpack(self.HEADER_STRUCT_FORMAT, data[:self.HEADER_STRUCT_SIZE])
is_response = bool((code >> 15) & 0x01)
opcode = (code >> 11) & 0x0F
flags = (code >> 4) & 0x7F
rcode = code & 0x0F
numnames = struct.unpack('B', data[self.HEADER_STRUCT_SIZE + 44])[0]
if numnames > 0:
ret = [ ]
offset = self.HEADER_STRUCT_SIZE + 45
for i in range(0, numnames):
mynme = data[offset:offset + 15]
mynme = mynme.strip()
ret.append(( mynme, ord(data[offset+15]) ))
offset += 18
return trn_id, ret
else:
return trn_id, None
#
# Contributed by Jason Anderson
#
def prepareNetNameQuery(self, trn_id, is_broadcast = True):
header = struct.pack(self.HEADER_STRUCT_FORMAT,
trn_id, (is_broadcast and 0x0010) or 0x0000, 1, 0, 0, 0)
payload = encode_name('*', 0) + '\x00\x21\x00\x01'
return header + payload
+38
View File
@@ -0,0 +1,38 @@
# Default port for NetBIOS name service
NETBIOS_NS_PORT = 137
# Default port for NetBIOS session service
NETBIOS_SESSION_PORT = 139
# Owner Node Type Constants
NODE_B = 0x00
NODE_P = 0x01
NODE_M = 0x10
NODE_RESERVED = 0x11
# Name Type Constants
TYPE_UNKNOWN = 0x01
TYPE_WORKSTATION = 0x00
TYPE_CLIENT = 0x03
TYPE_SERVER = 0x20
TYPE_DOMAIN_MASTER = 0x1B
TYPE_MASTER_BROWSER = 0x1D
TYPE_BROWSER = 0x1E
TYPE_NAMES = { TYPE_UNKNOWN: 'Unknown',
TYPE_WORKSTATION: 'Workstation',
TYPE_CLIENT: 'Client',
TYPE_SERVER: 'Server',
TYPE_MASTER_BROWSER: 'Master Browser',
TYPE_BROWSER: 'Browser Server',
TYPE_DOMAIN_MASTER: 'Domain Master'
}
# Values for Session Packet Type field in Session Packets
SESSION_MESSAGE = 0x00
SESSION_REQUEST = 0x81
POSITIVE_SESSION_RESPONSE = 0x82
NEGATIVE_SESSION_RESPONSE = 0x83
REGTARGET_SESSION_RESPONSE = 0x84
SESSION_KEEPALIVE = 0x85
+69
View File
@@ -0,0 +1,69 @@
import struct
class NMBError(Exception): pass
class NotConnectedError(NMBError):
"""
Raisd when the underlying NMB connection has been disconnected or not connected yet
"""
pass
class NMBSessionMessage:
HEADER_STRUCT_FORMAT = '>BBH'
HEADER_STRUCT_SIZE = struct.calcsize(HEADER_STRUCT_FORMAT)
def __init__(self):
self.reset()
def reset(self):
self.type = 0
self.flags = 0
self.data = ''
def decode(self, data, offset):
data_len = len(data)
if data_len < offset + self.HEADER_STRUCT_SIZE:
# Not enough data for decoding
return 0
self.reset()
self.type, self.flags, length = struct.unpack(self.HEADER_STRUCT_FORMAT, data[offset:offset+self.HEADER_STRUCT_SIZE])
if self.flags & 0x01:
length |= 0x010000
if data_len < offset + self.HEADER_STRUCT_SIZE + length:
return 0
self.data = data[offset+self.HEADER_STRUCT_SIZE:offset+self.HEADER_STRUCT_SIZE+length]
return self.HEADER_STRUCT_SIZE + length
class DirectTCPSessionMessage(NMBSessionMessage):
HEADER_STRUCT_FORMAT = '>I'
HEADER_STRUCT_SIZE = struct.calcsize(HEADER_STRUCT_FORMAT)
def decode(self, data, offset):
data_len = len(data)
if data_len < offset + self.HEADER_STRUCT_SIZE:
# Not enough data for decoding
return 0
self.reset()
length = struct.unpack(self.HEADER_STRUCT_FORMAT, data[offset:offset+self.HEADER_STRUCT_SIZE])[0]
if length >> 24 != 0:
raise NMBError("Invalid protocol header for Direct TCP session message")
if data_len < offset + self.HEADER_STRUCT_SIZE + length:
return 0
self.data = data[offset+self.HEADER_STRUCT_SIZE:offset+self.HEADER_STRUCT_SIZE+length]
return self.HEADER_STRUCT_SIZE + length
+50
View File
@@ -0,0 +1,50 @@
import string, re
def encode_name(name, type, scope = None):
"""
Perform first and second level encoding of name as specified in RFC 1001 (Section 4)
"""
if name == '*':
name = name + '\0' * 15
elif len(name) > 15:
name = name[:15] + chr(type)
else:
name = string.ljust(name, 15) + chr(type)
def _do_first_level_encoding(m):
s = ord(m.group(0))
return string.uppercase[s >> 4] + string.uppercase[s & 0x0f]
encoded_name = chr(len(name) * 2) + re.sub('.', _do_first_level_encoding, name)
if scope:
encoded_scope = ''
for s in string.split(scope, '.'):
encoded_scope = encoded_scope + chr(len(s)) + s
return encoded_name + encoded_scope + '\0'
else:
return encoded_name + '\0'
def decode_name(name):
name_length = ord(name[0])
assert name_length == 32
def _do_first_level_decoding(m):
s = m.group(0)
return chr(((ord(s[0]) - ord('A')) << 4) | (ord(s[1]) - ord('A')))
decoded_name = re.sub('..', _do_first_level_decoding, name[1:33])
if name[33] == '\0':
return 34, decoded_name, ''
else:
decoded_domain = ''
offset = 34
while 1:
domain_length = ord(name[offset])
if domain_length == 0:
break
decoded_domain = '.' + name[offset:offset + domain_length]
offset = offset + domain_length
return offset + 1, decoded_name, decoded_domain
+8
View File
@@ -0,0 +1,8 @@
import sys
# http://www.python.org/dev/peps/pep-0396/
__version__ = '0.1.9'
if sys.version_info[:2] < (2, 4):
raise RuntimeError('PyASN1 requires Python 2.4 or later')
+1
View File
@@ -0,0 +1 @@
# This file is necessary to make this directory a package.
@@ -0,0 +1 @@
# This file is necessary to make this directory a package.
+842
View File
@@ -0,0 +1,842 @@
# BER decoder
from pyasn1 import debug, error
from pyasn1.codec.ber import eoo
from pyasn1.compat.octets import oct2int, isOctetsType
from pyasn1.type import tag, univ, char, useful, tagmap
class AbstractDecoder:
protoComponent = None
def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
length, state, decodeFun, substrateFun):
raise error.PyAsn1Error('Decoder not implemented for %s' % (tagSet,))
def indefLenValueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
length, state, decodeFun, substrateFun):
raise error.PyAsn1Error('Indefinite length mode decoder not implemented for %s' % (tagSet,))
class AbstractSimpleDecoder(AbstractDecoder):
tagFormats = (tag.tagFormatSimple,)
def _createComponent(self, asn1Spec, tagSet, value=None):
if tagSet[0][1] not in self.tagFormats:
raise error.PyAsn1Error('Invalid tag format %s for %s' % (tagSet[0], self.protoComponent.prettyPrintType()))
if asn1Spec is None:
return self.protoComponent.clone(value, tagSet)
elif value is None:
return asn1Spec
else:
return asn1Spec.clone(value)
class AbstractConstructedDecoder(AbstractDecoder):
tagFormats = (tag.tagFormatConstructed,)
def _createComponent(self, asn1Spec, tagSet, value=None):
if tagSet[0][1] not in self.tagFormats:
raise error.PyAsn1Error('Invalid tag format %s for %s' % (tagSet[0], self.protoComponent.prettyPrintType()))
if asn1Spec is None:
return self.protoComponent.clone(tagSet)
else:
return asn1Spec.clone()
class ExplicitTagDecoder(AbstractSimpleDecoder):
protoComponent = univ.Any('')
tagFormats = (tag.tagFormatConstructed,)
def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
length, state, decodeFun, substrateFun):
if substrateFun:
return substrateFun(
self._createComponent(asn1Spec, tagSet, ''),
substrate, length
)
head, tail = substrate[:length], substrate[length:]
value, _ = decodeFun(head, asn1Spec, tagSet, length)
return value, tail
def indefLenValueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
length, state, decodeFun, substrateFun):
if substrateFun:
return substrateFun(
self._createComponent(asn1Spec, tagSet, ''),
substrate, length
)
value, substrate = decodeFun(substrate, asn1Spec, tagSet, length)
terminator, substrate = decodeFun(substrate, allowEoo=True)
if eoo.endOfOctets.isSameTypeWith(terminator) and \
terminator == eoo.endOfOctets:
return value, substrate
else:
raise error.PyAsn1Error('Missing end-of-octets terminator')
explicitTagDecoder = ExplicitTagDecoder()
class IntegerDecoder(AbstractSimpleDecoder):
protoComponent = univ.Integer(0)
precomputedValues = {
'\x00': 0,
'\x01': 1,
'\x02': 2,
'\x03': 3,
'\x04': 4,
'\x05': 5,
'\x06': 6,
'\x07': 7,
'\x08': 8,
'\x09': 9,
'\xff': -1,
'\xfe': -2,
'\xfd': -3,
'\xfc': -4,
'\xfb': -5
}
def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet, length,
state, decodeFun, substrateFun):
head, tail = substrate[:length], substrate[length:]
if not head:
return self._createComponent(asn1Spec, tagSet, 0), tail
if head in self.precomputedValues:
value = self.precomputedValues[head]
else:
firstOctet = oct2int(head[0])
if firstOctet & 0x80:
value = -1
else:
value = 0
for octet in head:
value = value << 8 | oct2int(octet)
return self._createComponent(asn1Spec, tagSet, value), tail
class BooleanDecoder(IntegerDecoder):
protoComponent = univ.Boolean(0)
def _createComponent(self, asn1Spec, tagSet, value=None):
return IntegerDecoder._createComponent(self, asn1Spec, tagSet, value and 1 or 0)
class BitStringDecoder(AbstractSimpleDecoder):
protoComponent = univ.BitString(())
tagFormats = (tag.tagFormatSimple, tag.tagFormatConstructed)
def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet, length,
state, decodeFun, substrateFun):
head, tail = substrate[:length], substrate[length:]
if tagSet[0][1] == tag.tagFormatSimple: # XXX what tag to check?
if not head:
raise error.PyAsn1Error('Empty substrate')
trailingBits = oct2int(head[0])
if trailingBits > 7:
raise error.PyAsn1Error(
'Trailing bits overflow %s' % trailingBits
)
head = head[1:]
lsb = p = 0; l = len(head)-1; b = []
while p <= l:
if p == l:
lsb = trailingBits
j = 7
o = oct2int(head[p])
while j >= lsb:
b.append((o>>j)&0x01)
j = j - 1
p = p + 1
return self._createComponent(asn1Spec, tagSet, b), tail
r = self._createComponent(asn1Spec, tagSet, ())
if substrateFun:
return substrateFun(r, substrate, length)
while head:
component, head = decodeFun(head, self.protoComponent)
r = r + component
return r, tail
def indefLenValueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
length, state, decodeFun, substrateFun):
r = self._createComponent(asn1Spec, tagSet, '')
if substrateFun:
return substrateFun(r, substrate, length)
while substrate:
component, substrate = decodeFun(substrate, self.protoComponent,
allowEoo=True)
if eoo.endOfOctets.isSameTypeWith(component) and \
component == eoo.endOfOctets:
break
r = r + component
else:
raise error.SubstrateUnderrunError(
'No EOO seen before substrate ends'
)
return r, substrate
class OctetStringDecoder(AbstractSimpleDecoder):
protoComponent = univ.OctetString('')
tagFormats = (tag.tagFormatSimple, tag.tagFormatConstructed)
def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet, length,
state, decodeFun, substrateFun):
head, tail = substrate[:length], substrate[length:]
if tagSet[0][1] == tag.tagFormatSimple: # XXX what tag to check?
return self._createComponent(asn1Spec, tagSet, head), tail
r = self._createComponent(asn1Spec, tagSet, '')
if substrateFun:
return substrateFun(r, substrate, length)
while head:
component, head = decodeFun(head, self.protoComponent)
r = r + component
return r, tail
def indefLenValueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
length, state, decodeFun, substrateFun):
r = self._createComponent(asn1Spec, tagSet, '')
if substrateFun:
return substrateFun(r, substrate, length)
while substrate:
component, substrate = decodeFun(substrate, self.protoComponent,
allowEoo=True)
if eoo.endOfOctets.isSameTypeWith(component) and \
component == eoo.endOfOctets:
break
r = r + component
else:
raise error.SubstrateUnderrunError(
'No EOO seen before substrate ends'
)
return r, substrate
class NullDecoder(AbstractSimpleDecoder):
protoComponent = univ.Null('')
def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
length, state, decodeFun, substrateFun):
head, tail = substrate[:length], substrate[length:]
r = self._createComponent(asn1Spec, tagSet)
if head:
raise error.PyAsn1Error('Unexpected %d-octet substrate for Null' % length)
return r, tail
class ObjectIdentifierDecoder(AbstractSimpleDecoder):
protoComponent = univ.ObjectIdentifier(())
def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet, length,
state, decodeFun, substrateFun):
head, tail = substrate[:length], substrate[length:]
if not head:
raise error.PyAsn1Error('Empty substrate')
oid = ()
index = 0
substrateLen = len(head)
while index < substrateLen:
subId = oct2int(head[index])
index += 1
if subId < 128:
oid = oid + (subId,)
elif subId > 128:
# Construct subid from a number of octets
nextSubId = subId
subId = 0
while nextSubId >= 128:
subId = (subId << 7) + (nextSubId & 0x7F)
if index >= substrateLen:
raise error.SubstrateUnderrunError(
'Short substrate for sub-OID past %s' % (oid,)
)
nextSubId = oct2int(head[index])
index += 1
oid = oid + ((subId << 7) + nextSubId,)
elif subId == 128:
# ASN.1 spec forbids leading zeros (0x80) in OID
# encoding, tolerating it opens a vulnerability. See
# http://www.cosic.esat.kuleuven.be/publications/article-1432.pdf
# page 7
raise error.PyAsn1Error('Invalid octet 0x80 in OID encoding')
# Decode two leading arcs
if 0 <= oid[0] <= 39:
oid = (0,) + oid
elif 40 <= oid[0] <= 79:
oid = (1, oid[0]-40) + oid[1:]
elif oid[0] >= 80:
oid = (2, oid[0]-80) + oid[1:]
else:
raise error.PyAsn1Error('Malformed first OID octet: %s' % head[0])
return self._createComponent(asn1Spec, tagSet, oid), tail
class RealDecoder(AbstractSimpleDecoder):
protoComponent = univ.Real()
def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
length, state, decodeFun, substrateFun):
head, tail = substrate[:length], substrate[length:]
if not head:
return self._createComponent(asn1Spec, tagSet, 0.0), tail
fo = oct2int(head[0]); head = head[1:]
if fo & 0x80: # binary encoding
if not head:
raise error.PyAsn1Error("Incomplete floating-point value")
n = (fo & 0x03) + 1
if n == 4:
n = oct2int(head[0])
head = head[1:]
eo, head = head[:n], head[n:]
if not eo or not head:
raise error.PyAsn1Error('Real exponent screwed')
e = oct2int(eo[0]) & 0x80 and -1 or 0
while eo: # exponent
e <<= 8
e |= oct2int(eo[0])
eo = eo[1:]
b = fo >> 4 & 0x03 # base bits
if b > 2:
raise error.PyAsn1Error('Illegal Real base')
if b == 1: # encbase = 8
e *= 3
elif b == 2: # encbase = 16
e *= 4
p = 0
while head: # value
p <<= 8
p |= oct2int(head[0])
head = head[1:]
if fo & 0x40: # sign bit
p = -p
sf = fo >> 2 & 0x03 # scale bits
p *= 2**sf
value = (p, 2, e)
elif fo & 0x40: # infinite value
value = fo & 0x01 and '-inf' or 'inf'
elif fo & 0xc0 == 0: # character encoding
if not head:
raise error.PyAsn1Error("Incomplete floating-point value")
try:
if fo & 0x3 == 0x1: # NR1
value = (int(head), 10, 0)
elif fo & 0x3 == 0x2: # NR2
value = float(head)
elif fo & 0x3 == 0x3: # NR3
value = float(head)
else:
raise error.SubstrateUnderrunError(
'Unknown NR (tag %s)' % fo
)
except ValueError:
raise error.SubstrateUnderrunError(
'Bad character Real syntax'
)
else:
raise error.SubstrateUnderrunError(
'Unknown encoding (tag %s)' % fo
)
return self._createComponent(asn1Spec, tagSet, value), tail
class SequenceDecoder(AbstractConstructedDecoder):
protoComponent = univ.Sequence()
def _getComponentTagMap(self, r, idx):
try:
return r.getComponentTagMapNearPosition(idx)
except error.PyAsn1Error:
return
def _getComponentPositionByType(self, r, t, idx):
return r.getComponentPositionNearType(t, idx)
def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
length, state, decodeFun, substrateFun):
head, tail = substrate[:length], substrate[length:]
r = self._createComponent(asn1Spec, tagSet)
idx = 0
if substrateFun:
return substrateFun(r, substrate, length)
while head:
asn1Spec = self._getComponentTagMap(r, idx)
component, head = decodeFun(head, asn1Spec)
idx = self._getComponentPositionByType(
r, component.getEffectiveTagSet(), idx
)
r.setComponentByPosition(idx, component, asn1Spec is None)
idx = idx + 1
r.setDefaultComponents()
r.verifySizeSpec()
return r, tail
def indefLenValueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
length, state, decodeFun, substrateFun):
r = self._createComponent(asn1Spec, tagSet)
if substrateFun:
return substrateFun(r, substrate, length)
idx = 0
while substrate:
asn1Spec = self._getComponentTagMap(r, idx)
component, substrate = decodeFun(substrate, asn1Spec, allowEoo=True)
if eoo.endOfOctets.isSameTypeWith(component) and \
component == eoo.endOfOctets:
break
idx = self._getComponentPositionByType(
r, component.getEffectiveTagSet(), idx
)
r.setComponentByPosition(idx, component, asn1Spec is None)
idx = idx + 1
else:
raise error.SubstrateUnderrunError(
'No EOO seen before substrate ends'
)
r.setDefaultComponents()
r.verifySizeSpec()
return r, substrate
class SequenceOfDecoder(AbstractConstructedDecoder):
protoComponent = univ.SequenceOf()
def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
length, state, decodeFun, substrateFun):
head, tail = substrate[:length], substrate[length:]
r = self._createComponent(asn1Spec, tagSet)
if substrateFun:
return substrateFun(r, substrate, length)
asn1Spec = r.getComponentType()
idx = 0
while head:
component, head = decodeFun(head, asn1Spec)
r.setComponentByPosition(idx, component, asn1Spec is None)
idx = idx + 1
r.verifySizeSpec()
return r, tail
def indefLenValueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
length, state, decodeFun, substrateFun):
r = self._createComponent(asn1Spec, tagSet)
if substrateFun:
return substrateFun(r, substrate, length)
asn1Spec = r.getComponentType()
idx = 0
while substrate:
component, substrate = decodeFun(substrate, asn1Spec, allowEoo=True)
if eoo.endOfOctets.isSameTypeWith(component) and \
component == eoo.endOfOctets:
break
r.setComponentByPosition(idx, component, asn1Spec is None)
idx = idx + 1
else:
raise error.SubstrateUnderrunError(
'No EOO seen before substrate ends'
)
r.verifySizeSpec()
return r, substrate
class SetDecoder(SequenceDecoder):
protoComponent = univ.Set()
def _getComponentTagMap(self, r, idx):
return r.getComponentTagMap()
def _getComponentPositionByType(self, r, t, idx):
nextIdx = r.getComponentPositionByType(t)
if nextIdx is None:
return idx
else:
return nextIdx
class SetOfDecoder(SequenceOfDecoder):
protoComponent = univ.SetOf()
class ChoiceDecoder(AbstractConstructedDecoder):
protoComponent = univ.Choice()
tagFormats = (tag.tagFormatSimple, tag.tagFormatConstructed)
def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
length, state, decodeFun, substrateFun):
head, tail = substrate[:length], substrate[length:]
r = self._createComponent(asn1Spec, tagSet)
if substrateFun:
return substrateFun(r, substrate, length)
if r.getTagSet() == tagSet: # explicitly tagged Choice
component, head = decodeFun(
head, r.getComponentTagMap()
)
else:
component, head = decodeFun(
head, r.getComponentTagMap(), tagSet, length, state
)
if isinstance(component, univ.Choice):
effectiveTagSet = component.getEffectiveTagSet()
else:
effectiveTagSet = component.getTagSet()
r.setComponentByType(effectiveTagSet, component, 0, asn1Spec is None)
return r, tail
def indefLenValueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
length, state, decodeFun, substrateFun):
r = self._createComponent(asn1Spec, tagSet)
if substrateFun:
return substrateFun(r, substrate, length)
if r.getTagSet() == tagSet: # explicitly tagged Choice
component, substrate = decodeFun(substrate, r.getComponentTagMap())
# eat up EOO marker
eooMarker, substrate = decodeFun(substrate, allowEoo=True)
if not eoo.endOfOctets.isSameTypeWith(eooMarker) or \
eooMarker != eoo.endOfOctets:
raise error.PyAsn1Error('No EOO seen before substrate ends')
else:
component, substrate= decodeFun(
substrate, r.getComponentTagMap(), tagSet, length, state
)
if isinstance(component, univ.Choice):
effectiveTagSet = component.getEffectiveTagSet()
else:
effectiveTagSet = component.getTagSet()
r.setComponentByType(effectiveTagSet, component, 0, asn1Spec is None)
return r, substrate
class AnyDecoder(AbstractSimpleDecoder):
protoComponent = univ.Any()
tagFormats = (tag.tagFormatSimple, tag.tagFormatConstructed)
def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
length, state, decodeFun, substrateFun):
if asn1Spec is None or \
asn1Spec is not None and tagSet != asn1Spec.getTagSet():
# untagged Any container, recover inner header substrate
length = length + len(fullSubstrate) - len(substrate)
substrate = fullSubstrate
if substrateFun:
return substrateFun(self._createComponent(asn1Spec, tagSet),
substrate, length)
head, tail = substrate[:length], substrate[length:]
return self._createComponent(asn1Spec, tagSet, value=head), tail
def indefLenValueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
length, state, decodeFun, substrateFun):
if asn1Spec is not None and tagSet == asn1Spec.getTagSet():
# tagged Any type -- consume header substrate
header = ''
else:
# untagged Any, recover header substrate
header = fullSubstrate[:-len(substrate)]
r = self._createComponent(asn1Spec, tagSet, header)
# Any components do not inherit initial tag
asn1Spec = self.protoComponent
if substrateFun:
return substrateFun(r, substrate, length)
while substrate:
component, substrate = decodeFun(substrate, asn1Spec, allowEoo=True)
if eoo.endOfOctets.isSameTypeWith(component) and \
component == eoo.endOfOctets:
break
r = r + component
else:
raise error.SubstrateUnderrunError(
'No EOO seen before substrate ends'
)
return r, substrate
# character string types
class UTF8StringDecoder(OctetStringDecoder):
protoComponent = char.UTF8String()
class NumericStringDecoder(OctetStringDecoder):
protoComponent = char.NumericString()
class PrintableStringDecoder(OctetStringDecoder):
protoComponent = char.PrintableString()
class TeletexStringDecoder(OctetStringDecoder):
protoComponent = char.TeletexString()
class VideotexStringDecoder(OctetStringDecoder):
protoComponent = char.VideotexString()
class IA5StringDecoder(OctetStringDecoder):
protoComponent = char.IA5String()
class GraphicStringDecoder(OctetStringDecoder):
protoComponent = char.GraphicString()
class VisibleStringDecoder(OctetStringDecoder):
protoComponent = char.VisibleString()
class GeneralStringDecoder(OctetStringDecoder):
protoComponent = char.GeneralString()
class UniversalStringDecoder(OctetStringDecoder):
protoComponent = char.UniversalString()
class BMPStringDecoder(OctetStringDecoder):
protoComponent = char.BMPString()
# "useful" types
class ObjectDescriptorDecoder(OctetStringDecoder):
protoComponent = useful.ObjectDescriptor()
class GeneralizedTimeDecoder(OctetStringDecoder):
protoComponent = useful.GeneralizedTime()
class UTCTimeDecoder(OctetStringDecoder):
protoComponent = useful.UTCTime()
tagMap = {
univ.Integer.tagSet: IntegerDecoder(),
univ.Boolean.tagSet: BooleanDecoder(),
univ.BitString.tagSet: BitStringDecoder(),
univ.OctetString.tagSet: OctetStringDecoder(),
univ.Null.tagSet: NullDecoder(),
univ.ObjectIdentifier.tagSet: ObjectIdentifierDecoder(),
univ.Enumerated.tagSet: IntegerDecoder(),
univ.Real.tagSet: RealDecoder(),
univ.Sequence.tagSet: SequenceDecoder(), # conflicts with SequenceOf
univ.Set.tagSet: SetDecoder(), # conflicts with SetOf
univ.Choice.tagSet: ChoiceDecoder(), # conflicts with Any
# character string types
char.UTF8String.tagSet: UTF8StringDecoder(),
char.NumericString.tagSet: NumericStringDecoder(),
char.PrintableString.tagSet: PrintableStringDecoder(),
char.TeletexString.tagSet: TeletexStringDecoder(),
char.VideotexString.tagSet: VideotexStringDecoder(),
char.IA5String.tagSet: IA5StringDecoder(),
char.GraphicString.tagSet: GraphicStringDecoder(),
char.VisibleString.tagSet: VisibleStringDecoder(),
char.GeneralString.tagSet: GeneralStringDecoder(),
char.UniversalString.tagSet: UniversalStringDecoder(),
char.BMPString.tagSet: BMPStringDecoder(),
# useful types
useful.ObjectDescriptor.tagSet: ObjectDescriptorDecoder(),
useful.GeneralizedTime.tagSet: GeneralizedTimeDecoder(),
useful.UTCTime.tagSet: UTCTimeDecoder()
}
# Type-to-codec map for ambiguous ASN.1 types
typeMap = {
univ.Set.typeId: SetDecoder(),
univ.SetOf.typeId: SetOfDecoder(),
univ.Sequence.typeId: SequenceDecoder(),
univ.SequenceOf.typeId: SequenceOfDecoder(),
univ.Choice.typeId: ChoiceDecoder(),
univ.Any.typeId: AnyDecoder()
}
( stDecodeTag, stDecodeLength, stGetValueDecoder, stGetValueDecoderByAsn1Spec,
stGetValueDecoderByTag, stTryAsExplicitTag, stDecodeValue,
stDumpRawValue, stErrorCondition, stStop ) = [x for x in range(10)]
class Decoder:
defaultErrorState = stErrorCondition
# defaultErrorState = stDumpRawValue
defaultRawDecoder = AnyDecoder()
supportIndefLength = True
def __init__(self, tagMap, typeMap={}):
self.__tagMap = tagMap
self.__typeMap = typeMap
# Tag & TagSet objects caches
self.__tagCache = {}
self.__tagSetCache = {}
def __call__(self, substrate, asn1Spec=None, tagSet=None,
length=None, state=stDecodeTag, recursiveFlag=1,
substrateFun=None, allowEoo=False):
if debug.logger & debug.flagDecoder:
debug.logger('decoder called at scope %s with state %d, working with up to %d octets of substrate: %s' % (debug.scope, state, len(substrate), debug.hexdump(substrate)))
fullSubstrate = substrate
while state != stStop:
if state == stDecodeTag:
if not substrate:
raise error.SubstrateUnderrunError(
'Short octet stream on tag decoding'
)
if not isOctetsType(substrate) and \
not isinstance(substrate, univ.OctetString):
raise error.PyAsn1Error('Bad octet stream type')
# Decode tag
firstOctet = substrate[0]
substrate = substrate[1:]
if firstOctet in self.__tagCache:
lastTag = self.__tagCache[firstOctet]
else:
t = oct2int(firstOctet)
# Look for end-of-octets sentinel
if t == 0:
if substrate and oct2int(substrate[0]) == 0:
if allowEoo and self.supportIndefLength:
debug.logger and debug.logger & debug.flagDecoder and debug.logger('end-of-octets sentinel found')
value, substrate = eoo.endOfOctets, substrate[1:]
state = stStop
continue
else:
raise error.PyAsn1Error('Unexpected end-of-contents sentinel')
else:
raise error.PyAsn1Error('Zero tag encountered')
tagClass = t&0xC0
tagFormat = t&0x20
tagId = t&0x1F
if tagId == 0x1F:
tagId = 0
while 1:
if not substrate:
raise error.SubstrateUnderrunError(
'Short octet stream on long tag decoding'
)
t = oct2int(substrate[0])
tagId = tagId << 7 | (t&0x7F)
substrate = substrate[1:]
if not t&0x80:
break
lastTag = tag.Tag(
tagClass=tagClass, tagFormat=tagFormat, tagId=tagId
)
if tagId < 31:
# cache short tags
self.__tagCache[firstOctet] = lastTag
if tagSet is None:
if firstOctet in self.__tagSetCache:
tagSet = self.__tagSetCache[firstOctet]
else:
# base tag not recovered
tagSet = tag.TagSet((), lastTag)
if firstOctet in self.__tagCache:
self.__tagSetCache[firstOctet] = tagSet
else:
tagSet = lastTag + tagSet
state = stDecodeLength
debug.logger and debug.logger & debug.flagDecoder and debug.logger('tag decoded into %s, decoding length' % tagSet)
if state == stDecodeLength:
# Decode length
if not substrate:
raise error.SubstrateUnderrunError(
'Short octet stream on length decoding'
)
firstOctet = oct2int(substrate[0])
if firstOctet == 128:
size = 1
length = -1
elif firstOctet < 128:
length, size = firstOctet, 1
else:
size = firstOctet & 0x7F
# encoded in size bytes
length = 0
lengthString = substrate[1:size+1]
# missing check on maximum size, which shouldn't be a
# problem, we can handle more than is possible
if len(lengthString) != size:
raise error.SubstrateUnderrunError(
'%s<%s at %s' %
(size, len(lengthString), tagSet)
)
for char in lengthString:
length = (length << 8) | oct2int(char)
size = size + 1
substrate = substrate[size:]
if length != -1 and len(substrate) < length:
raise error.SubstrateUnderrunError(
'%d-octet short' % (length - len(substrate))
)
if length == -1 and not self.supportIndefLength:
error.PyAsn1Error('Indefinite length encoding not supported by this codec')
state = stGetValueDecoder
debug.logger and debug.logger & debug.flagDecoder and debug.logger('value length decoded into %d, payload substrate is: %s' % (length, debug.hexdump(length == -1 and substrate or substrate[:length])))
if state == stGetValueDecoder:
if asn1Spec is None:
state = stGetValueDecoderByTag
else:
state = stGetValueDecoderByAsn1Spec
#
# There're two ways of creating subtypes in ASN.1 what influences
# decoder operation. These methods are:
# 1) Either base types used in or no IMPLICIT tagging has been
# applied on subtyping.
# 2) Subtype syntax drops base type information (by means of
# IMPLICIT tagging.
# The first case allows for complete tag recovery from substrate
# while the second one requires original ASN.1 type spec for
# decoding.
#
# In either case a set of tags (tagSet) is coming from substrate
# in an incremental, tag-by-tag fashion (this is the case of
# EXPLICIT tag which is most basic). Outermost tag comes first
# from the wire.
#
if state == stGetValueDecoderByTag:
if tagSet in self.__tagMap:
concreteDecoder = self.__tagMap[tagSet]
else:
concreteDecoder = None
if concreteDecoder:
state = stDecodeValue
else:
_k = tagSet[:1]
if _k in self.__tagMap:
concreteDecoder = self.__tagMap[_k]
else:
concreteDecoder = None
if concreteDecoder:
state = stDecodeValue
else:
state = stTryAsExplicitTag
if debug.logger and debug.logger & debug.flagDecoder:
debug.logger('codec %s chosen by a built-in type, decoding %s' % (concreteDecoder and concreteDecoder.__class__.__name__ or "<none>", state == stDecodeValue and 'value' or 'as explicit tag'))
debug.scope.push(concreteDecoder is None and '?' or concreteDecoder.protoComponent.__class__.__name__)
if state == stGetValueDecoderByAsn1Spec:
if isinstance(asn1Spec, (dict, tagmap.TagMap)):
if tagSet in asn1Spec:
__chosenSpec = asn1Spec[tagSet]
else:
__chosenSpec = None
if debug.logger and debug.logger & debug.flagDecoder:
debug.logger('candidate ASN.1 spec is a map of:')
for t, v in asn1Spec.getPosMap().items():
debug.logger(' %s -> %s' % (t, v.__class__.__name__))
if asn1Spec.getNegMap():
debug.logger('but neither of: ')
for t, v in asn1Spec.getNegMap().items():
debug.logger(' %s -> %s' % (t, v.__class__.__name__))
debug.logger('new candidate ASN.1 spec is %s, chosen by %s' % (__chosenSpec is None and '<none>' or __chosenSpec.prettyPrintType(), tagSet))
else:
__chosenSpec = asn1Spec
debug.logger and debug.logger & debug.flagDecoder and debug.logger('candidate ASN.1 spec is %s' % asn1Spec.__class__.__name__)
if __chosenSpec is not None and (
tagSet == __chosenSpec.getTagSet() or \
tagSet in __chosenSpec.getTagMap()
):
# use base type for codec lookup to recover untagged types
baseTagSet = __chosenSpec.baseTagSet
if __chosenSpec.typeId is not None and \
__chosenSpec.typeId in self.__typeMap:
# ambiguous type
concreteDecoder = self.__typeMap[__chosenSpec.typeId]
debug.logger and debug.logger & debug.flagDecoder and debug.logger('value decoder chosen for an ambiguous type by type ID %s' % (__chosenSpec.typeId,))
elif baseTagSet in self.__tagMap:
# base type or tagged subtype
concreteDecoder = self.__tagMap[baseTagSet]
debug.logger and debug.logger & debug.flagDecoder and debug.logger('value decoder chosen by base %s' % (baseTagSet,))
else:
concreteDecoder = None
if concreteDecoder:
asn1Spec = __chosenSpec
state = stDecodeValue
else:
state = stTryAsExplicitTag
else:
concreteDecoder = None
state = stTryAsExplicitTag
if debug.logger and debug.logger & debug.flagDecoder:
debug.logger('codec %s chosen by ASN.1 spec, decoding %s' % (state == stDecodeValue and concreteDecoder.__class__.__name__ or "<none>", state == stDecodeValue and 'value' or 'as explicit tag'))
debug.scope.push(__chosenSpec is None and '?' or __chosenSpec.__class__.__name__)
if state == stTryAsExplicitTag:
if tagSet and \
tagSet[0][1] == tag.tagFormatConstructed and \
tagSet[0][0] != tag.tagClassUniversal:
# Assume explicit tagging
concreteDecoder = explicitTagDecoder
state = stDecodeValue
else:
concreteDecoder = None
state = self.defaultErrorState
debug.logger and debug.logger & debug.flagDecoder and debug.logger('codec %s chosen, decoding %s' % (concreteDecoder and concreteDecoder.__class__.__name__ or "<none>", state == stDecodeValue and 'value' or 'as failure'))
if state == stDumpRawValue:
concreteDecoder = self.defaultRawDecoder
debug.logger and debug.logger & debug.flagDecoder and debug.logger('codec %s chosen, decoding value' % concreteDecoder.__class__.__name__)
state = stDecodeValue
if state == stDecodeValue:
if recursiveFlag == 0 and not substrateFun: # legacy
substrateFun = lambda a,b,c: (a,b[:c])
if length == -1: # indef length
value, substrate = concreteDecoder.indefLenValueDecoder(
fullSubstrate, substrate, asn1Spec, tagSet, length,
stGetValueDecoder, self, substrateFun
)
else:
value, substrate = concreteDecoder.valueDecoder(
fullSubstrate, substrate, asn1Spec, tagSet, length,
stGetValueDecoder, self, substrateFun
)
state = stStop
debug.logger and debug.logger & debug.flagDecoder and debug.logger('codec %s yields type %s, value:\n%s\n...remaining substrate is: %s' % (concreteDecoder.__class__.__name__, value.__class__.__name__, value.prettyPrint(), substrate and debug.hexdump(substrate) or '<none>'))
if state == stErrorCondition:
raise error.PyAsn1Error(
'%s not in asn1Spec: %s' % (tagSet, asn1Spec)
)
if debug.logger and debug.logger & debug.flagDecoder:
debug.scope.pop()
debug.logger('decoder left scope %s, call completed' % debug.scope)
return value, substrate
decode = Decoder(tagMap, typeMap)
# XXX
# non-recursive decoding; return position rather than substrate
+434
View File
@@ -0,0 +1,434 @@
# BER encoder
from pyasn1 import debug, error
from pyasn1.codec.ber import eoo
from pyasn1.compat.octets import int2oct, oct2int, ints2octs, null, str2octs
from pyasn1.type import base, tag, univ, char, useful
class Error(Exception): pass
class AbstractItemEncoder:
supportIndefLenMode = 1
def encodeTag(self, t, isConstructed):
tagClass, tagFormat, tagId = t.asTuple() # this is a hotspot
v = tagClass | tagFormat
if isConstructed:
v = v|tag.tagFormatConstructed
if tagId < 31:
return int2oct(v|tagId)
else:
s = int2oct(tagId&0x7f)
tagId = tagId >> 7
while tagId:
s = int2oct(0x80|(tagId&0x7f)) + s
tagId = tagId >> 7
return int2oct(v|0x1F) + s
def encodeLength(self, length, defMode):
if not defMode and self.supportIndefLenMode:
return int2oct(0x80)
if length < 0x80:
return int2oct(length)
else:
substrate = null
while length:
substrate = int2oct(length&0xff) + substrate
length = length >> 8
substrateLen = len(substrate)
if substrateLen > 126:
raise Error('Length octets overflow (%d)' % substrateLen)
return int2oct(0x80 | substrateLen) + substrate
def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
raise Error('Not implemented')
def _encodeEndOfOctets(self, encodeFun, defMode):
if defMode or not self.supportIndefLenMode:
return null
else:
return encodeFun(eoo.endOfOctets, defMode)
def encode(self, encodeFun, value, defMode, maxChunkSize):
substrate, isConstructed = self.encodeValue(
encodeFun, value, defMode, maxChunkSize
)
tagSet = value.getTagSet()
if tagSet:
if not isConstructed: # primitive form implies definite mode
defMode = 1
return self.encodeTag(
tagSet[-1], isConstructed
) + self.encodeLength(
len(substrate), defMode
) + substrate + self._encodeEndOfOctets(encodeFun, defMode)
else:
return substrate # untagged value
class EndOfOctetsEncoder(AbstractItemEncoder):
def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
return null, 0
class ExplicitlyTaggedItemEncoder(AbstractItemEncoder):
def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
if isinstance(value, base.AbstractConstructedAsn1Item):
value = value.clone(tagSet=value.getTagSet()[:-1],
cloneValueFlag=1)
else:
value = value.clone(tagSet=value.getTagSet()[:-1])
return encodeFun(value, defMode, maxChunkSize), 1
explicitlyTaggedItemEncoder = ExplicitlyTaggedItemEncoder()
class BooleanEncoder(AbstractItemEncoder):
supportIndefLenMode = 0
_true = ints2octs((1,))
_false = ints2octs((0,))
def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
return value and self._true or self._false, 0
class IntegerEncoder(AbstractItemEncoder):
supportIndefLenMode = 0
supportCompactZero = False
def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
if value == 0: # shortcut for zero value
if self.supportCompactZero:
# this seems to be a correct way for encoding zeros
return null, 0
else:
# this seems to be a widespread way for encoding zeros
return ints2octs((0,)), 0
octets = []
value = int(value) # to save on ops on asn1 type
while 1:
octets.insert(0, value & 0xff)
if value == 0 or value == -1:
break
value = value >> 8
if value == 0 and octets[0] & 0x80:
octets.insert(0, 0)
while len(octets) > 1 and \
(octets[0] == 0 and octets[1] & 0x80 == 0 or \
octets[0] == 0xff and octets[1] & 0x80 != 0):
del octets[0]
return ints2octs(octets), 0
class BitStringEncoder(AbstractItemEncoder):
def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
if not maxChunkSize or len(value) <= maxChunkSize*8:
out_len = (len(value) + 7) // 8
out_list = out_len * [0]
j = 7
i = -1
for val in value:
j += 1
if j == 8:
i += 1
j = 0
out_list[i] = out_list[i] | val << (7-j)
return int2oct(7-j) + ints2octs(out_list), 0
else:
pos = 0; substrate = null
while 1:
# count in octets
v = value.clone(value[pos*8:pos*8+maxChunkSize*8])
if not v:
break
substrate = substrate + encodeFun(v, defMode, maxChunkSize)
pos = pos + maxChunkSize
return substrate, 1
class OctetStringEncoder(AbstractItemEncoder):
def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
if not maxChunkSize or len(value) <= maxChunkSize:
return value.asOctets(), 0
else:
pos = 0; substrate = null
while 1:
v = value.clone(value[pos:pos+maxChunkSize])
if not v:
break
substrate = substrate + encodeFun(v, defMode, maxChunkSize)
pos = pos + maxChunkSize
return substrate, 1
class NullEncoder(AbstractItemEncoder):
supportIndefLenMode = 0
def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
return null, 0
class ObjectIdentifierEncoder(AbstractItemEncoder):
supportIndefLenMode = 0
precomputedValues = {
(1, 3, 6, 1, 2): (43, 6, 1, 2),
(1, 3, 6, 1, 4): (43, 6, 1, 4)
}
def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
oid = value.asTuple()
if oid[:5] in self.precomputedValues:
octets = self.precomputedValues[oid[:5]]
oid = oid[5:]
else:
if len(oid) < 2:
raise error.PyAsn1Error('Short OID %s' % (value,))
octets = ()
# Build the first twos
if oid[0] == 0 and 0 <= oid[1] <= 39:
oid = (oid[1],) + oid[2:]
elif oid[0] == 1 and 0 <= oid[1] <= 39:
oid = (oid[1] + 40,) + oid[2:]
elif oid[0] == 2:
oid = (oid[1] + 80,) + oid[2:]
else:
raise error.PyAsn1Error(
'Impossible initial arcs %s at %s' % (oid[:2], value)
)
# Cycle through subIds
for subId in oid:
if subId > -1 and subId < 128:
# Optimize for the common case
octets = octets + (subId & 0x7f,)
elif subId < 0:
raise error.PyAsn1Error(
'Negative OID arc %s at %s' % (subId, value)
)
else:
# Pack large Sub-Object IDs
res = (subId & 0x7f,)
subId = subId >> 7
while subId > 0:
res = (0x80 | (subId & 0x7f),) + res
subId = subId >> 7
# Add packed Sub-Object ID to resulted Object ID
octets += res
return ints2octs(octets), 0
class RealEncoder(AbstractItemEncoder):
supportIndefLenMode = 0
binEncBase = 2 # set to None to choose encoding base automatically
def _dropFloatingPoint(self, m, encbase, e):
ms, es = 1, 1
if m < 0:
ms = -1 # mantissa sign
if e < 0:
es = -1 # exponenta sign
m *= ms
if encbase == 8:
m = m*2**(abs(e) % 3 * es)
e = abs(e) // 3 * es
elif encbase == 16:
m = m*2**(abs(e) % 4 * es)
e = abs(e) // 4 * es
while 1:
if int(m) != m:
m *= encbase
e -= 1
continue
break
return ms, int(m), encbase, e
def _chooseEncBase(self, value):
m, b, e = value
base = [2, 8, 16]
if value.binEncBase in base:
return self._dropFloatingPoint(m, value.binEncBase, e)
elif self.binEncBase in base:
return self._dropFloatingPoint(m, self.binEncBase, e)
# auto choosing base 2/8/16
mantissa = [m, m, m]
exponenta = [e, e, e]
encbase = 2
e = float('inf')
for i in range(3):
sign, mantissa[i], base[i], exponenta[i] = \
self._dropFloatingPoint(mantissa[i], base[i], exponenta[i])
if abs(exponenta[i]) < abs(e) or \
(abs(exponenta[i]) == abs(e) and mantissa[i] < m):
e = exponenta[i]
m = int(mantissa[i])
encbase = base[i]
return sign, m, encbase, e
def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
if value.isPlusInfinity():
return int2oct(0x40), 0
if value.isMinusInfinity():
return int2oct(0x41), 0
m, b, e = value
if not m:
return null, 0
if b == 10:
return str2octs('\x03%dE%s%d' % (m, e == 0 and '+' or '', e)), 0
elif b == 2:
fo = 0x80 # binary encoding
ms, m, encbase, e = self._chooseEncBase(value)
if ms < 0: # mantissa sign
fo = fo | 0x40 # sign bit
# exponenta & mantissa normalization
if encbase == 2:
while m & 0x1 == 0:
m >>= 1
e += 1
elif encbase == 8:
while m & 0x7 == 0:
m >>= 3
e += 1
fo |= 0x10
else: # encbase = 16
while m & 0xf == 0:
m >>= 4
e += 1
fo |= 0x20
sf = 0 # scale factor
while m & 0x1 == 0:
m >>= 1
sf += 1
if sf > 3:
raise error.PyAsn1Error('Scale factor overflow') # bug if raised
fo |= sf << 2
eo = null
if e == 0 or e == -1:
eo = int2oct(e&0xff)
else:
while e not in (0, -1):
eo = int2oct(e&0xff) + eo
e >>= 8
if e == 0 and eo and oct2int(eo[0]) & 0x80:
eo = int2oct(0) + eo
if e == -1 and eo and not (oct2int(eo[0]) & 0x80):
eo = int2oct(0xff) + eo
n = len(eo)
if n > 0xff:
raise error.PyAsn1Error('Real exponent overflow')
if n == 1:
pass
elif n == 2:
fo |= 1
elif n == 3:
fo |= 2
else:
fo |= 3
eo = int2oct(n&0xff) + eo
po = null
while m:
po = int2oct(m&0xff) + po
m >>= 8
substrate = int2oct(fo) + eo + po
return substrate, 0
else:
raise error.PyAsn1Error('Prohibited Real base %s' % b)
class SequenceEncoder(AbstractItemEncoder):
def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
value.setDefaultComponents()
value.verifySizeSpec()
substrate = null; idx = len(value)
while idx > 0:
idx = idx - 1
if value[idx] is None: # Optional component
continue
component = value.getDefaultComponentByPosition(idx)
if component is not None and component == value[idx]:
continue
substrate = encodeFun(
value[idx], defMode, maxChunkSize
) + substrate
return substrate, 1
class SequenceOfEncoder(AbstractItemEncoder):
def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
value.verifySizeSpec()
substrate = null; idx = len(value)
while idx > 0:
idx = idx - 1
substrate = encodeFun(
value[idx], defMode, maxChunkSize
) + substrate
return substrate, 1
class ChoiceEncoder(AbstractItemEncoder):
def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
return encodeFun(value.getComponent(), defMode, maxChunkSize), 1
class AnyEncoder(OctetStringEncoder):
def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
return value.asOctets(), defMode == 0
tagMap = {
eoo.endOfOctets.tagSet: EndOfOctetsEncoder(),
univ.Boolean.tagSet: BooleanEncoder(),
univ.Integer.tagSet: IntegerEncoder(),
univ.BitString.tagSet: BitStringEncoder(),
univ.OctetString.tagSet: OctetStringEncoder(),
univ.Null.tagSet: NullEncoder(),
univ.ObjectIdentifier.tagSet: ObjectIdentifierEncoder(),
univ.Enumerated.tagSet: IntegerEncoder(),
univ.Real.tagSet: RealEncoder(),
# Sequence & Set have same tags as SequenceOf & SetOf
univ.SequenceOf.tagSet: SequenceOfEncoder(),
univ.SetOf.tagSet: SequenceOfEncoder(),
univ.Choice.tagSet: ChoiceEncoder(),
# character string types
char.UTF8String.tagSet: OctetStringEncoder(),
char.NumericString.tagSet: OctetStringEncoder(),
char.PrintableString.tagSet: OctetStringEncoder(),
char.TeletexString.tagSet: OctetStringEncoder(),
char.VideotexString.tagSet: OctetStringEncoder(),
char.IA5String.tagSet: OctetStringEncoder(),
char.GraphicString.tagSet: OctetStringEncoder(),
char.VisibleString.tagSet: OctetStringEncoder(),
char.GeneralString.tagSet: OctetStringEncoder(),
char.UniversalString.tagSet: OctetStringEncoder(),
char.BMPString.tagSet: OctetStringEncoder(),
# useful types
useful.ObjectDescriptor.tagSet: OctetStringEncoder(),
useful.GeneralizedTime.tagSet: OctetStringEncoder(),
useful.UTCTime.tagSet: OctetStringEncoder()
}
# Type-to-codec map for ambiguous ASN.1 types
typeMap = {
univ.Set.typeId: SequenceEncoder(),
univ.SetOf.typeId: SequenceOfEncoder(),
univ.Sequence.typeId: SequenceEncoder(),
univ.SequenceOf.typeId: SequenceOfEncoder(),
univ.Choice.typeId: ChoiceEncoder(),
univ.Any.typeId: AnyEncoder()
}
class Encoder:
supportIndefLength = True
def __init__(self, tagMap, typeMap={}):
self.__tagMap = tagMap
self.__typeMap = typeMap
def __call__(self, value, defMode=True, maxChunkSize=0):
if not defMode and not self.supportIndefLength:
raise error.PyAsn1Error('Indefinite length encoding not supported by this codec')
debug.logger & debug.flagEncoder and debug.logger('encoder called in %sdef mode, chunk size %s for type %s, value:\n%s' % (not defMode and 'in' or '', maxChunkSize, value.prettyPrintType(), value.prettyPrint()))
tagSet = value.getTagSet()
if len(tagSet) > 1:
concreteEncoder = explicitlyTaggedItemEncoder
else:
if value.typeId is not None and value.typeId in self.__typeMap:
concreteEncoder = self.__typeMap[value.typeId]
elif tagSet in self.__tagMap:
concreteEncoder = self.__tagMap[tagSet]
else:
tagSet = value.baseTagSet
if tagSet in self.__tagMap:
concreteEncoder = self.__tagMap[tagSet]
else:
raise Error('No encoder for %s' % (value,))
debug.logger & debug.flagEncoder and debug.logger('using value codec %s chosen by %s' % (concreteEncoder.__class__.__name__, tagSet))
substrate = concreteEncoder.encode(
self, value, defMode, maxChunkSize
)
debug.logger & debug.flagEncoder and debug.logger('built %s octets of substrate: %s\nencoder completed' % (len(substrate), debug.hexdump(substrate)))
return substrate
encode = Encoder(tagMap, typeMap)
+8
View File
@@ -0,0 +1,8 @@
from pyasn1.type import base, tag
class EndOfOctets(base.AbstractSimpleAsn1Item):
defaultValue = 0
tagSet = tag.initTagSet(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x00)
)
endOfOctets = EndOfOctets()
@@ -0,0 +1 @@
# This file is necessary to make this directory a package.
@@ -0,0 +1,36 @@
# CER decoder
from pyasn1 import error
from pyasn1.codec.ber import decoder
from pyasn1.compat.octets import oct2int
from pyasn1.type import univ
class BooleanDecoder(decoder.AbstractSimpleDecoder):
protoComponent = univ.Boolean(0)
def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet, length,
state, decodeFun, substrateFun):
head, tail = substrate[:length], substrate[length:]
if not head or length != 1:
raise error.PyAsn1Error('Not single-octet Boolean payload')
byte = oct2int(head[0])
# CER/DER specifies encoding of TRUE as 0xFF and FALSE as 0x0, while
# BER allows any non-zero value as TRUE; cf. sections 8.2.2. and 11.1
# in http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf
if byte == 0xff:
value = 1
elif byte == 0x00:
value = 0
else:
raise error.PyAsn1Error('Unexpected Boolean payload: %s' % byte)
return self._createComponent(asn1Spec, tagSet, value), tail
tagMap = decoder.tagMap.copy()
tagMap.update({
univ.Boolean.tagSet: BooleanDecoder()
})
typeMap = decoder.typeMap
class Decoder(decoder.Decoder): pass
decode = Decoder(tagMap, decoder.typeMap)
+131
View File
@@ -0,0 +1,131 @@
# CER encoder
from pyasn1 import error
from pyasn1.codec.ber import encoder
from pyasn1.compat.octets import int2oct, str2octs, null
from pyasn1.type import univ
from pyasn1.type import useful
class BooleanEncoder(encoder.IntegerEncoder):
def encodeValue(self, encodeFun, client, defMode, maxChunkSize):
if client == 0:
substrate = int2oct(0)
else:
substrate = int2oct(255)
return substrate, 0
class BitStringEncoder(encoder.BitStringEncoder):
def encodeValue(self, encodeFun, client, defMode, maxChunkSize):
return encoder.BitStringEncoder.encodeValue(
self, encodeFun, client, defMode, 1000
)
class OctetStringEncoder(encoder.OctetStringEncoder):
def encodeValue(self, encodeFun, client, defMode, maxChunkSize):
return encoder.OctetStringEncoder.encodeValue(
self, encodeFun, client, defMode, 1000
)
class RealEncoder(encoder.RealEncoder):
def _chooseEncBase(self, value):
m, b, e = value
return self._dropFloatingPoint(m, b, e)
# specialized GeneralStringEncoder here
class GeneralizedTimeEncoder(OctetStringEncoder):
zchar = str2octs('Z')
pluschar = str2octs('+')
minuschar = str2octs('-')
zero = str2octs('0')
def encodeValue(self, encodeFun, client, defMode, maxChunkSize):
octets = client.asOctets()
# This breaks too many existing data items
# if '.' not in octets:
# raise error.PyAsn1Error('Format must include fraction of second: %r' % octets)
if len(octets) < 15:
raise error.PyAsn1Error('Bad UTC time length: %r' % octets)
if self.pluschar in octets or self.minuschar in octets:
raise error.PyAsn1Error('Must be UTC time: %r' % octets)
if octets[-1] != self.zchar[0]:
raise error.PyAsn1Error('Missing timezone specifier: %r' % octets)
return encoder.OctetStringEncoder.encodeValue(
self, encodeFun, client, defMode, 1000
)
class UTCTimeEncoder(encoder.OctetStringEncoder):
zchar = str2octs('Z')
pluschar = str2octs('+')
minuschar = str2octs('-')
def encodeValue(self, encodeFun, client, defMode, maxChunkSize):
octets = client.asOctets()
if self.pluschar in octets or self.minuschar in octets:
raise error.PyAsn1Error('Must be UTC time: %r' % octets)
if octets and octets[-1] != self.zchar[0]:
client = client.clone(octets + self.zchar)
if len(client) != 13:
raise error.PyAsn1Error('Bad UTC time length: %r' % client)
return encoder.OctetStringEncoder.encodeValue(
self, encodeFun, client, defMode, 1000
)
class SetOfEncoder(encoder.SequenceOfEncoder):
def encodeValue(self, encodeFun, client, defMode, maxChunkSize):
if isinstance(client, univ.SequenceAndSetBase):
client.setDefaultComponents()
client.verifySizeSpec()
substrate = null; idx = len(client)
# This is certainly a hack but how else do I distinguish SetOf
# from Set if they have the same tags&constraints?
if isinstance(client, univ.SequenceAndSetBase):
# Set
comps = []
while idx > 0:
idx = idx - 1
if client[idx] is None: # Optional component
continue
if client.getDefaultComponentByPosition(idx) == client[idx]:
continue
comps.append(client[idx])
comps.sort(key=lambda x: isinstance(x, univ.Choice) and \
x.getMinTagSet() or x.getTagSet())
for c in comps:
substrate += encodeFun(c, defMode, maxChunkSize)
else:
# SetOf
compSubs = []
while idx > 0:
idx = idx - 1
compSubs.append(
encodeFun(client[idx], defMode, maxChunkSize)
)
compSubs.sort() # perhaps padding's not needed
substrate = null
for compSub in compSubs:
substrate += compSub
return substrate, 1
tagMap = encoder.tagMap.copy()
tagMap.update({
univ.Boolean.tagSet: BooleanEncoder(),
univ.BitString.tagSet: BitStringEncoder(),
univ.OctetString.tagSet: OctetStringEncoder(),
univ.Real.tagSet: RealEncoder(),
useful.GeneralizedTime.tagSet: GeneralizedTimeEncoder(),
useful.UTCTime.tagSet: UTCTimeEncoder(),
univ.SetOf().tagSet: SetOfEncoder() # conflcts with Set
})
typeMap = encoder.typeMap.copy()
typeMap.update({
univ.Set.typeId: SetOfEncoder(),
univ.SetOf.typeId: SetOfEncoder()
})
class Encoder(encoder.Encoder):
def __call__(self, client, defMode=False, maxChunkSize=0):
return encoder.Encoder.__call__(self, client, defMode, maxChunkSize)
encode = Encoder(tagMap, typeMap)
# EncoderFactory queries class instance and builds a map of tags -> encoders
@@ -0,0 +1 @@
# This file is necessary to make this directory a package.
@@ -0,0 +1,9 @@
# DER decoder
from pyasn1.codec.cer import decoder
tagMap = decoder.tagMap
typeMap = decoder.typeMap
class Decoder(decoder.Decoder):
supportIndefLength = False
decode = Decoder(tagMap, typeMap)
@@ -0,0 +1,33 @@
# DER encoder
from pyasn1 import error
from pyasn1.codec.cer import encoder
from pyasn1.type import univ
class SetOfEncoder(encoder.SetOfEncoder):
def _cmpSetComponents(self, c1, c2):
tagSet1 = isinstance(c1, univ.Choice) and \
c1.getEffectiveTagSet() or c1.getTagSet()
tagSet2 = isinstance(c2, univ.Choice) and \
c2.getEffectiveTagSet() or c2.getTagSet()
return cmp(tagSet1, tagSet2)
tagMap = encoder.tagMap.copy()
tagMap.update({
# Overload CER encoders with BER ones (a bit hackerish XXX)
univ.BitString.tagSet: encoder.encoder.BitStringEncoder(),
univ.OctetString.tagSet: encoder.encoder.OctetStringEncoder(),
# Set & SetOf have same tags
univ.SetOf().tagSet: SetOfEncoder()
})
typeMap = encoder.typeMap
class Encoder(encoder.Encoder):
supportIndefLength = False
def __call__(self, client, defMode=True, maxChunkSize=0):
if not defMode:
raise error.PyAsn1Error('DER forbids indefinite length mode')
return encoder.Encoder.__call__(self, client, defMode, maxChunkSize)
encode = Encoder(tagMap, typeMap)
+1
View File
@@ -0,0 +1 @@
# This file is necessary to make this directory a package.
+10
View File
@@ -0,0 +1,10 @@
from sys import version_info
if version_info[0:2] < (2, 6):
def bin(x):
if x <= 1:
return '0b'+str(x)
else:
return bin(x>>1) + str(x&1)
else:
bin = bin
+22
View File
@@ -0,0 +1,22 @@
from sys import version_info
if version_info[0] <= 2:
int2oct = chr
ints2octs = lambda s: ''.join([ int2oct(x) for x in s ])
null = ''
oct2int = ord
octs2ints = lambda s: [ oct2int(x) for x in s ]
str2octs = lambda x: x
octs2str = lambda x: x
isOctetsType = lambda s: isinstance(s, str)
isStringType = lambda s: isinstance(s, (str, unicode))
else:
ints2octs = bytes
int2oct = lambda x: ints2octs((x,))
null = ints2octs()
oct2int = lambda x: x
octs2ints = lambda s: [ x for x in s ]
str2octs = lambda x: x.encode()
octs2str = lambda x: x.decode()
isOctetsType = lambda s: isinstance(s, bytes)
isStringType = lambda s: isinstance(s, str)
+110
View File
@@ -0,0 +1,110 @@
import logging
from pyasn1 import __version__
from pyasn1 import error
from pyasn1.compat.octets import octs2ints
flagNone = 0x0000
flagEncoder = 0x0001
flagDecoder = 0x0002
flagAll = 0xffff
flagMap = {
'encoder': flagEncoder,
'decoder': flagDecoder,
'all': flagAll
}
class Printer:
def __init__(self, logger=None, handler=None, formatter=None):
if logger is None:
logger = logging.getLogger('pyasn1')
logger.setLevel(logging.DEBUG)
if handler is None:
handler = logging.StreamHandler()
if formatter is None:
formatter = logging.Formatter('%(asctime)s %(name)s: %(message)s')
handler.setFormatter(formatter)
handler.setLevel(logging.DEBUG)
logger.addHandler(handler)
self.__logger = logger
def __call__(self, msg): self.__logger.debug(msg)
def __str__(self): return '<python built-in logging>'
if hasattr(logging, 'NullHandler'):
NullHandler = logging.NullHandler
else:
# Python 2.6 and older
class NullHandler(logging.Handler):
def emit(self, record):
pass
class Debug:
defaultPrinter = None
def __init__(self, *flags, **options):
self._flags = flagNone
if options.get('printer') is not None:
self._printer = options.get('printer')
elif self.defaultPrinter is not None:
self._printer = self.defaultPrinter
if 'loggerName' in options:
# route our logs to parent logger
self._printer = Printer(
logger=logging.getLogger(options['loggerName']),
handler=NullHandler()
)
else:
self._printer = Printer()
self('running pyasn1 version %s' % __version__)
for f in flags:
inverse = f and f[0] in ('!', '~')
if inverse:
f = f[1:]
try:
if inverse:
self._flags &= ~flagMap[f]
else:
self._flags |= flagMap[f]
except KeyError:
raise error.PyAsn1Error('bad debug flag %s' % f)
self('debug category \'%s\' %s' % (f, inverse and 'disabled' or 'enabled'))
def __str__(self):
return 'logger %s, flags %x' % (self._printer, self._flags)
def __call__(self, msg):
self._printer(msg)
def __and__(self, flag):
return self._flags & flag
def __rand__(self, flag):
return flag & self._flags
logger = 0
def setLogger(l):
global logger
logger = l
def hexdump(octets):
return ' '.join(
[ '%s%.2X' % (n%16 == 0 and ('\n%.5d: ' % n) or '', x)
for n,x in zip(range(len(octets)), octs2ints(octets)) ]
)
class Scope:
def __init__(self):
self._list = []
def __str__(self): return '.'.join(self._list)
def push(self, token):
self._list.append(token)
def pop(self):
return self._list.pop()
scope = Scope()
+3
View File
@@ -0,0 +1,3 @@
class PyAsn1Error(Exception): pass
class ValueConstraintError(PyAsn1Error): pass
class SubstrateUnderrunError(PyAsn1Error): pass
+1
View File
@@ -0,0 +1 @@
# This file is necessary to make this directory a package.
+280
View File
@@ -0,0 +1,280 @@
# Base classes for ASN.1 types
import sys
from pyasn1 import error
from pyasn1.type import constraint, tagmap, tag
class Asn1Item: pass
class Asn1ItemBase(Asn1Item):
# Set of tags for this ASN.1 type
tagSet = tag.TagSet()
# A list of constraint.Constraint instances for checking values
subtypeSpec = constraint.ConstraintsIntersection()
# Used for ambiguous ASN.1 types identification
typeId = None
def __init__(self, tagSet=None, subtypeSpec=None):
if tagSet is None:
self._tagSet = self.tagSet
else:
self._tagSet = tagSet
if subtypeSpec is None:
self._subtypeSpec = self.subtypeSpec
else:
self._subtypeSpec = subtypeSpec
def _verifySubtypeSpec(self, value, idx=None):
try:
self._subtypeSpec(value, idx)
except error.PyAsn1Error:
c, i, t = sys.exc_info()
raise c('%s at %s' % (i, self.__class__.__name__))
def getSubtypeSpec(self): return self._subtypeSpec
def getTagSet(self): return self._tagSet
def getEffectiveTagSet(self): return self._tagSet # used by untagged types
def getTagMap(self): return tagmap.TagMap({self._tagSet: self})
def isSameTypeWith(self, other, matchTags=True, matchConstraints=True):
return self is other or \
(not matchTags or \
self._tagSet == other.getTagSet()) and \
(not matchConstraints or \
self._subtypeSpec==other.getSubtypeSpec())
def isSuperTypeOf(self, other, matchTags=True, matchConstraints=True):
"""Returns true if argument is a ASN1 subtype of ourselves"""
return (not matchTags or \
self._tagSet.isSuperTagSetOf(other.getTagSet())) and \
(not matchConstraints or \
(self._subtypeSpec.isSuperTypeOf(other.getSubtypeSpec())))
class NoValue:
def __getattr__(self, attr):
raise error.PyAsn1Error('No value for %s()' % attr)
def __getitem__(self, i):
raise error.PyAsn1Error('No value')
def __repr__(self): return '%s()' % self.__class__.__name__
noValue = NoValue()
# Base class for "simple" ASN.1 objects. These are immutable.
class AbstractSimpleAsn1Item(Asn1ItemBase):
defaultValue = noValue
def __init__(self, value=None, tagSet=None, subtypeSpec=None):
Asn1ItemBase.__init__(self, tagSet, subtypeSpec)
if value is None or value is noValue:
value = self.defaultValue
if value is None or value is noValue:
self.__hashedValue = value = noValue
else:
value = self.prettyIn(value)
self._verifySubtypeSpec(value)
self.__hashedValue = hash(value)
self._value = value
self._len = None
def __repr__(self):
r = []
if self._value is not self.defaultValue:
r.append(self.prettyOut(self._value))
if self._tagSet is not self.tagSet:
r.append('tagSet=%r' % (self._tagSet,))
if self._subtypeSpec is not self.subtypeSpec:
r.append('subtypeSpec=%r' % (self._subtypeSpec,))
return '%s(%s)' % (self.__class__.__name__, ', '.join(r))
def __str__(self): return str(self._value)
def __eq__(self, other):
return self is other and True or self._value == other
def __ne__(self, other): return self._value != other
def __lt__(self, other): return self._value < other
def __le__(self, other): return self._value <= other
def __gt__(self, other): return self._value > other
def __ge__(self, other): return self._value >= other
if sys.version_info[0] <= 2:
def __nonzero__(self): return bool(self._value)
else:
def __bool__(self): return bool(self._value)
def __hash__(self):
return self.__hashedValue is noValue and hash(noValue) or self.__hashedValue
def hasValue(self):
return not isinstance(self._value, NoValue)
def clone(self, value=None, tagSet=None, subtypeSpec=None):
if value is None and tagSet is None and subtypeSpec is None:
return self
if value is None:
value = self._value
if tagSet is None:
tagSet = self._tagSet
if subtypeSpec is None:
subtypeSpec = self._subtypeSpec
return self.__class__(value, tagSet, subtypeSpec)
def subtype(self, value=None, implicitTag=None, explicitTag=None,
subtypeSpec=None):
if value is None:
value = self._value
if implicitTag is not None:
tagSet = self._tagSet.tagImplicitly(implicitTag)
elif explicitTag is not None:
tagSet = self._tagSet.tagExplicitly(explicitTag)
else:
tagSet = self._tagSet
if subtypeSpec is None:
subtypeSpec = self._subtypeSpec
else:
subtypeSpec = subtypeSpec + self._subtypeSpec
return self.__class__(value, tagSet, subtypeSpec)
def prettyIn(self, value): return value
def prettyOut(self, value): return str(value)
def prettyPrint(self, scope=0):
if self.hasValue():
return self.prettyOut(self._value)
else:
return '<no value>'
# XXX Compatibility stub
def prettyPrinter(self, scope=0): return self.prettyPrint(scope)
def prettyPrintType(self, scope=0):
return '%s -> %s' % (self.getTagSet(), self.__class__.__name__)
#
# Constructed types:
# * There are five of them: Sequence, SequenceOf/SetOf, Set and Choice
# * ASN1 types and values are represened by Python class instances
# * Value initialization is made for defaulted components only
# * Primary method of component addressing is by-position. Data model for base
# type is Python sequence. Additional type-specific addressing methods
# may be implemented for particular types.
# * SequenceOf and SetOf types do not implement any additional methods
# * Sequence, Set and Choice types also implement by-identifier addressing
# * Sequence, Set and Choice types also implement by-asn1-type (tag) addressing
# * Sequence and Set types may include optional and defaulted
# components
# * Constructed types hold a reference to component types used for value
# verification and ordering.
# * Component type is a scalar type for SequenceOf/SetOf types and a list
# of types for Sequence/Set/Choice.
#
class AbstractConstructedAsn1Item(Asn1ItemBase):
componentType = None
sizeSpec = constraint.ConstraintsIntersection()
def __init__(self, componentType=None, tagSet=None,
subtypeSpec=None, sizeSpec=None):
Asn1ItemBase.__init__(self, tagSet, subtypeSpec)
if componentType is None:
self._componentType = self.componentType
else:
self._componentType = componentType
if sizeSpec is None:
self._sizeSpec = self.sizeSpec
else:
self._sizeSpec = sizeSpec
self._componentValues = []
self._componentValuesSet = 0
def __repr__(self):
r = []
if self._componentType is not self.componentType:
r.append('componentType=%r' % (self._componentType,))
if self._tagSet is not self.tagSet:
r.append('tagSet=%r' % (self._tagSet,))
if self._subtypeSpec is not self.subtypeSpec:
r.append('subtypeSpec=%r' % (self._subtypeSpec,))
r = '%s(%s)' % (self.__class__.__name__, ', '.join(r))
if self._componentValues:
r += '.setComponents(%s)' % ', '.join([repr(x) for x in self._componentValues])
return r
def __eq__(self, other):
return self is other and True or self._componentValues == other
def __ne__(self, other): return self._componentValues != other
def __lt__(self, other): return self._componentValues < other
def __le__(self, other): return self._componentValues <= other
def __gt__(self, other): return self._componentValues > other
def __ge__(self, other): return self._componentValues >= other
if sys.version_info[0] <= 2:
def __nonzero__(self): return bool(self._componentValues)
else:
def __bool__(self): return bool(self._componentValues)
def getComponentTagMap(self):
raise error.PyAsn1Error('Method not implemented')
def _cloneComponentValues(self, myClone, cloneValueFlag): pass
def clone(self, tagSet=None, subtypeSpec=None, sizeSpec=None,
cloneValueFlag=None):
if tagSet is None:
tagSet = self._tagSet
if subtypeSpec is None:
subtypeSpec = self._subtypeSpec
if sizeSpec is None:
sizeSpec = self._sizeSpec
r = self.__class__(self._componentType, tagSet, subtypeSpec, sizeSpec)
if cloneValueFlag:
self._cloneComponentValues(r, cloneValueFlag)
return r
def subtype(self, implicitTag=None, explicitTag=None, subtypeSpec=None,
sizeSpec=None, cloneValueFlag=None):
if implicitTag is not None:
tagSet = self._tagSet.tagImplicitly(implicitTag)
elif explicitTag is not None:
tagSet = self._tagSet.tagExplicitly(explicitTag)
else:
tagSet = self._tagSet
if subtypeSpec is None:
subtypeSpec = self._subtypeSpec
else:
subtypeSpec = subtypeSpec + self._subtypeSpec
if sizeSpec is None:
sizeSpec = self._sizeSpec
else:
sizeSpec = sizeSpec + self._sizeSpec
r = self.__class__(self._componentType, tagSet, subtypeSpec, sizeSpec)
if cloneValueFlag:
self._cloneComponentValues(r, cloneValueFlag)
return r
def _verifyComponent(self, idx, value): pass
def verifySizeSpec(self): self._sizeSpec(self)
def getComponentByPosition(self, idx):
raise error.PyAsn1Error('Method not implemented')
def setComponentByPosition(self, idx, value, verifyConstraints=True):
raise error.PyAsn1Error('Method not implemented')
def setComponents(self, *args, **kwargs):
for idx in range(len(args)):
self[idx] = args[idx]
for k in kwargs:
self[k] = kwargs[k]
return self
def getComponentType(self): return self._componentType
def setDefaultComponents(self): pass
def __getitem__(self, idx): return self.getComponentByPosition(idx)
def __setitem__(self, idx, value): self.setComponentByPosition(idx, value)
def __len__(self): return len(self._componentValues)
def clear(self):
self._componentValues = []
self._componentValuesSet = 0
+64
View File
@@ -0,0 +1,64 @@
# ASN.1 "character string" types
from pyasn1.type import univ, tag
class NumericString(univ.OctetString):
tagSet = univ.OctetString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 18)
)
class PrintableString(univ.OctetString):
tagSet = univ.OctetString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 19)
)
class TeletexString(univ.OctetString):
tagSet = univ.OctetString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 20)
)
class T61String(TeletexString): pass
class VideotexString(univ.OctetString):
tagSet = univ.OctetString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 21)
)
class IA5String(univ.OctetString):
tagSet = univ.OctetString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 22)
)
class GraphicString(univ.OctetString):
tagSet = univ.OctetString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 25)
)
class VisibleString(univ.OctetString):
tagSet = univ.OctetString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 26)
)
class ISO646String(VisibleString): pass
class GeneralString(univ.OctetString):
tagSet = univ.OctetString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 27)
)
class UniversalString(univ.OctetString):
tagSet = univ.OctetString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 28)
)
encoding = "utf-32-be"
class BMPString(univ.OctetString):
tagSet = univ.OctetString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 30)
)
encoding = "utf-16-be"
class UTF8String(univ.OctetString):
tagSet = univ.OctetString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12)
)
encoding = "utf-8"
+202
View File
@@ -0,0 +1,202 @@
#
# ASN.1 subtype constraints classes.
#
# Constraints are relatively rare, but every ASN1 object
# is doing checks all the time for whether they have any
# constraints and whether they are applicable to the object.
#
# What we're going to do is define objects/functions that
# can be called unconditionally if they are present, and that
# are simply not present if there are no constraints.
#
# Original concept and code by Mike C. Fletcher.
#
import sys
from pyasn1.type import error
class AbstractConstraint:
"""Abstract base-class for constraint objects
Constraints should be stored in a simple sequence in the
namespace of their client Asn1Item sub-classes.
"""
def __init__(self, *values):
self._valueMap = {}
self._setValues(values)
self.__hashedValues = None
def __call__(self, value, idx=None):
try:
self._testValue(value, idx)
except error.ValueConstraintError:
raise error.ValueConstraintError(
'%s failed at: \"%s\"' % (self, sys.exc_info()[1])
)
def __repr__(self):
return '%s(%s)' % (
self.__class__.__name__,
', '.join([repr(x) for x in self._values])
)
def __eq__(self, other):
return self is other and True or self._values == other
def __ne__(self, other): return self._values != other
def __lt__(self, other): return self._values < other
def __le__(self, other): return self._values <= other
def __gt__(self, other): return self._values > other
def __ge__(self, other): return self._values >= other
if sys.version_info[0] <= 2:
def __nonzero__(self): return bool(self._values)
else:
def __bool__(self): return bool(self._values)
def __hash__(self):
if self.__hashedValues is None:
self.__hashedValues = hash((self.__class__.__name__, self._values))
return self.__hashedValues
def _setValues(self, values): self._values = values
def _testValue(self, value, idx):
raise error.ValueConstraintError(value)
# Constraints derivation logic
def getValueMap(self): return self._valueMap
def isSuperTypeOf(self, otherConstraint):
return self in otherConstraint.getValueMap() or \
otherConstraint is self or otherConstraint == self
def isSubTypeOf(self, otherConstraint):
return otherConstraint in self._valueMap or \
otherConstraint is self or otherConstraint == self
class SingleValueConstraint(AbstractConstraint):
"""Value must be part of defined values constraint"""
def _testValue(self, value, idx):
# XXX index vals for performance?
if value not in self._values:
raise error.ValueConstraintError(value)
class ContainedSubtypeConstraint(AbstractConstraint):
"""Value must satisfy all of defined set of constraints"""
def _testValue(self, value, idx):
for c in self._values:
c(value, idx)
class ValueRangeConstraint(AbstractConstraint):
"""Value must be within start and stop values (inclusive)"""
def _testValue(self, value, idx):
if value < self.start or value > self.stop:
raise error.ValueConstraintError(value)
def _setValues(self, values):
if len(values) != 2:
raise error.PyAsn1Error(
'%s: bad constraint values' % (self.__class__.__name__,)
)
self.start, self.stop = values
if self.start > self.stop:
raise error.PyAsn1Error(
'%s: screwed constraint values (start > stop): %s > %s' % (
self.__class__.__name__,
self.start, self.stop
)
)
AbstractConstraint._setValues(self, values)
class ValueSizeConstraint(ValueRangeConstraint):
"""len(value) must be within start and stop values (inclusive)"""
def _testValue(self, value, idx):
l = len(value)
if l < self.start or l > self.stop:
raise error.ValueConstraintError(value)
class PermittedAlphabetConstraint(SingleValueConstraint):
def _setValues(self, values):
self._values = ()
for v in values:
self._values = self._values + tuple(v)
def _testValue(self, value, idx):
for v in value:
if v not in self._values:
raise error.ValueConstraintError(value)
# This is a bit kludgy, meaning two op modes within a single constraing
class InnerTypeConstraint(AbstractConstraint):
"""Value must satisfy type and presense constraints"""
def _testValue(self, value, idx):
if self.__singleTypeConstraint:
self.__singleTypeConstraint(value)
elif self.__multipleTypeConstraint:
if idx not in self.__multipleTypeConstraint:
raise error.ValueConstraintError(value)
constraint, status = self.__multipleTypeConstraint[idx]
if status == 'ABSENT': # XXX presense is not checked!
raise error.ValueConstraintError(value)
constraint(value)
def _setValues(self, values):
self.__multipleTypeConstraint = {}
self.__singleTypeConstraint = None
for v in values:
if isinstance(v, tuple):
self.__multipleTypeConstraint[v[0]] = v[1], v[2]
else:
self.__singleTypeConstraint = v
AbstractConstraint._setValues(self, values)
# Boolean ops on constraints
class ConstraintsExclusion(AbstractConstraint):
"""Value must not fit the single constraint"""
def _testValue(self, value, idx):
try:
self._values[0](value, idx)
except error.ValueConstraintError:
return
else:
raise error.ValueConstraintError(value)
def _setValues(self, values):
if len(values) != 1:
raise error.PyAsn1Error('Single constraint expected')
AbstractConstraint._setValues(self, values)
class AbstractConstraintSet(AbstractConstraint):
"""Value must not satisfy the single constraint"""
def __getitem__(self, idx): return self._values[idx]
def __add__(self, value): return self.__class__(self, value)
def __radd__(self, value): return self.__class__(self, value)
def __len__(self): return len(self._values)
# Constraints inclusion in sets
def _setValues(self, values):
self._values = values
for v in values:
self._valueMap[v] = 1
self._valueMap.update(v.getValueMap())
class ConstraintsIntersection(AbstractConstraintSet):
"""Value must satisfy all constraints"""
def _testValue(self, value, idx):
for v in self._values:
v(value, idx)
class ConstraintsUnion(AbstractConstraintSet):
"""Value must satisfy at least one constraint"""
def _testValue(self, value, idx):
for v in self._values:
try:
v(value, idx)
except error.ValueConstraintError:
pass
else:
return
raise error.ValueConstraintError(
'all of %s failed for \"%s\"' % (self._values, value)
)
# XXX
# add tests for type check
+3
View File
@@ -0,0 +1,3 @@
from pyasn1.error import PyAsn1Error
class ValueConstraintError(PyAsn1Error): pass
+151
View File
@@ -0,0 +1,151 @@
# NamedType specification for constructed types
import sys
from pyasn1 import error
from pyasn1.type import tagmap
class NamedType:
isOptional = 0
isDefaulted = 0
def __init__(self, name, t):
self.__name = name; self.__type = t
def __repr__(self): return '%s(%r, %r)' % (
self.__class__.__name__, self.__name, self.__type
)
def __eq__(self, other): return tuple(self) == tuple(other)
def __ne__(self, other): return tuple(self) != tuple(other)
def __lt__(self, other): return tuple(self) < tuple(other)
def __le__(self, other): return tuple(self) <= tuple(other)
def __gt__(self, other): return tuple(self) > tuple(other)
def __ge__(self, other): return tuple(self) >= tuple(other)
def __hash__(self): return hash(tuple(self))
def getType(self): return self.__type
def getName(self): return self.__name
def __getitem__(self, idx):
if idx == 0: return self.__name
if idx == 1: return self.__type
raise IndexError()
class OptionalNamedType(NamedType):
isOptional = 1
class DefaultedNamedType(NamedType):
isDefaulted = 1
class NamedTypes:
def __init__(self, *namedTypes):
self.__namedTypes = namedTypes
self.__namedTypesLen = len(self.__namedTypes)
self.__minTagSet = None
self.__tagToPosIdx = {}; self.__nameToPosIdx = {}
self.__tagMap = { False: None, True: None }
self.__ambigiousTypes = {}
def __repr__(self):
return '%s(%s)' % (
self.__class__.__name__,
', '.join([ repr(x) for x in self.__namedTypes ])
)
def __eq__(self, other): return tuple(self) == tuple(other)
def __ne__(self, other): return tuple(self) != tuple(other)
def __lt__(self, other): return tuple(self) < tuple(other)
def __le__(self, other): return tuple(self) <= tuple(other)
def __gt__(self, other): return tuple(self) > tuple(other)
def __ge__(self, other): return tuple(self) >= tuple(other)
def __hash__(self): return hash(tuple(self))
def __getitem__(self, idx): return self.__namedTypes[idx]
if sys.version_info[0] <= 2:
def __nonzero__(self): return bool(self.__namedTypesLen)
else:
def __bool__(self): return bool(self.__namedTypesLen)
def __len__(self): return self.__namedTypesLen
def clone(self): return self.__class__(*self.__namedTypes)
def getTypeByPosition(self, idx):
if idx < 0 or idx >= self.__namedTypesLen:
raise error.PyAsn1Error('Type position out of range')
else:
return self.__namedTypes[idx].getType()
def getPositionByType(self, tagSet):
if not self.__tagToPosIdx:
idx = self.__namedTypesLen
while idx > 0:
idx = idx - 1
tagMap = self.__namedTypes[idx].getType().getTagMap()
for t in tagMap.getPosMap():
if t in self.__tagToPosIdx:
raise error.PyAsn1Error('Duplicate type %s' % (t,))
self.__tagToPosIdx[t] = idx
try:
return self.__tagToPosIdx[tagSet]
except KeyError:
raise error.PyAsn1Error('Type %s not found' % (tagSet,))
def getNameByPosition(self, idx):
try:
return self.__namedTypes[idx].getName()
except IndexError:
raise error.PyAsn1Error('Type position out of range')
def getPositionByName(self, name):
if not self.__nameToPosIdx:
idx = self.__namedTypesLen
while idx > 0:
idx = idx - 1
n = self.__namedTypes[idx].getName()
if n in self.__nameToPosIdx:
raise error.PyAsn1Error('Duplicate name %s' % (n,))
self.__nameToPosIdx[n] = idx
try:
return self.__nameToPosIdx[name]
except KeyError:
raise error.PyAsn1Error('Name %s not found' % (name,))
def __buildAmbigiousTagMap(self):
ambigiousTypes = ()
idx = self.__namedTypesLen
while idx > 0:
idx = idx - 1
t = self.__namedTypes[idx]
if t.isOptional or t.isDefaulted:
ambigiousTypes = (t, ) + ambigiousTypes
else:
ambigiousTypes = (t, )
self.__ambigiousTypes[idx] = NamedTypes(*ambigiousTypes)
def getTagMapNearPosition(self, idx):
if not self.__ambigiousTypes: self.__buildAmbigiousTagMap()
try:
return self.__ambigiousTypes[idx].getTagMap()
except KeyError:
raise error.PyAsn1Error('Type position out of range')
def getPositionNearType(self, tagSet, idx):
if not self.__ambigiousTypes: self.__buildAmbigiousTagMap()
try:
return idx+self.__ambigiousTypes[idx].getPositionByType(tagSet)
except KeyError:
raise error.PyAsn1Error('Type position out of range')
def genMinTagSet(self):
if self.__minTagSet is None:
for t in self.__namedTypes:
__type = t.getType()
tagSet = getattr(__type,'getMinTagSet',__type.getTagSet)()
if self.__minTagSet is None or tagSet < self.__minTagSet:
self.__minTagSet = tagSet
return self.__minTagSet
def getTagMap(self, uniq=False):
if self.__tagMap[uniq] is None:
tagMap = tagmap.TagMap()
for nt in self.__namedTypes:
tagMap = tagMap.clone(
nt.getType(), nt.getType().getTagMap(), uniq
)
self.__tagMap[uniq] = tagMap
return self.__tagMap[uniq]
+58
View File
@@ -0,0 +1,58 @@
# ASN.1 named integers
from pyasn1 import error
__all__ = [ 'NamedValues' ]
class NamedValues:
def __init__(self, *namedValues):
self.nameToValIdx = {}; self.valToNameIdx = {}
self.namedValues = ()
automaticVal = 1
for namedValue in namedValues:
if isinstance(namedValue, tuple):
name, val = namedValue
else:
name = namedValue
val = automaticVal
if name in self.nameToValIdx:
raise error.PyAsn1Error('Duplicate name %s' % (name,))
self.nameToValIdx[name] = val
if val in self.valToNameIdx:
raise error.PyAsn1Error('Duplicate value %s=%s' % (name, val))
self.valToNameIdx[val] = name
self.namedValues = self.namedValues + ((name, val),)
automaticVal = automaticVal + 1
def __repr__(self):
return '%s(%s)' % (self.__class__.__name__, ', '.join([repr(x) for x in self.namedValues]))
def __str__(self): return str(self.namedValues)
def __eq__(self, other): return tuple(self) == tuple(other)
def __ne__(self, other): return tuple(self) != tuple(other)
def __lt__(self, other): return tuple(self) < tuple(other)
def __le__(self, other): return tuple(self) <= tuple(other)
def __gt__(self, other): return tuple(self) > tuple(other)
def __ge__(self, other): return tuple(self) >= tuple(other)
def __hash__(self): return hash(tuple(self))
def getName(self, value):
if value in self.valToNameIdx:
return self.valToNameIdx[value]
def getValue(self, name):
if name in self.nameToValIdx:
return self.nameToValIdx[name]
def __getitem__(self, i): return self.namedValues[i]
def __len__(self): return len(self.namedValues)
def __add__(self, namedValues):
return self.__class__(*self.namedValues + namedValues)
def __radd__(self, namedValues):
return self.__class__(*namedValues + tuple(self))
def clone(self, *namedValues):
return self.__class__(*tuple(self) + namedValues)
# XXX clone/subtype?
+129
View File
@@ -0,0 +1,129 @@
# ASN.1 types tags
from operator import getitem
from pyasn1 import error
tagClassUniversal = 0x00
tagClassApplication = 0x40
tagClassContext = 0x80
tagClassPrivate = 0xC0
tagFormatSimple = 0x00
tagFormatConstructed = 0x20
tagCategoryImplicit = 0x01
tagCategoryExplicit = 0x02
tagCategoryUntagged = 0x04
class Tag:
def __init__(self, tagClass, tagFormat, tagId):
if tagId < 0:
raise error.PyAsn1Error(
'Negative tag ID (%s) not allowed' % (tagId,)
)
self.__tag = (tagClass, tagFormat, tagId)
self.uniq = (tagClass, tagId)
self.__hashedUniqTag = hash(self.uniq)
def __str__(self):
return '[%s:%s:%s]' % self.__tag
def __repr__(self):
return '%s(tagClass=%s, tagFormat=%s, tagId=%s)' % (
(self.__class__.__name__,) + self.__tag
)
# These is really a hotspot -- expose public "uniq" attribute to save on
# function calls
def __eq__(self, other): return self.uniq == other.uniq
def __ne__(self, other): return self.uniq != other.uniq
def __lt__(self, other): return self.uniq < other.uniq
def __le__(self, other): return self.uniq <= other.uniq
def __gt__(self, other): return self.uniq > other.uniq
def __ge__(self, other): return self.uniq >= other.uniq
def __hash__(self): return self.__hashedUniqTag
def __getitem__(self, idx): return self.__tag[idx]
def __and__(self, otherTag):
(tagClass, tagFormat, tagId) = otherTag
return self.__class__(
self.__tag&tagClass, self.__tag&tagFormat, self.__tag&tagId
)
def __or__(self, otherTag):
(tagClass, tagFormat, tagId) = otherTag
return self.__class__(
self.__tag[0]|tagClass,
self.__tag[1]|tagFormat,
self.__tag[2]|tagId
)
def asTuple(self): return self.__tag # __getitem__() is slow
class TagSet:
def __init__(self, baseTag=(), *superTags):
self.__baseTag = baseTag
self.__superTags = superTags
self.__hashedSuperTags = hash(superTags)
_uniq = ()
for t in superTags:
_uniq = _uniq + t.uniq
self.uniq = _uniq
self.__lenOfSuperTags = len(superTags)
def __str__(self):
return self.__superTags and '+'.join([str(x) for x in self.__superTags]) or '[untagged]'
def __repr__(self):
return '%s(%s)' % (
self.__class__.__name__,
'(), ' + ', '.join([repr(x) for x in self.__superTags])
)
def __add__(self, superTag):
return self.__class__(
self.__baseTag, *self.__superTags + (superTag,)
)
def __radd__(self, superTag):
return self.__class__(
self.__baseTag, *(superTag,) + self.__superTags
)
def tagExplicitly(self, superTag):
tagClass, tagFormat, tagId = superTag
if tagClass == tagClassUniversal:
raise error.PyAsn1Error(
'Can\'t tag with UNIVERSAL-class tag'
)
if tagFormat != tagFormatConstructed:
superTag = Tag(tagClass, tagFormatConstructed, tagId)
return self + superTag
def tagImplicitly(self, superTag):
tagClass, tagFormat, tagId = superTag
if self.__superTags:
superTag = Tag(tagClass, self.__superTags[-1][1], tagId)
return self[:-1] + superTag
def getBaseTag(self): return self.__baseTag
def __getitem__(self, idx):
if isinstance(idx, slice):
return self.__class__(
self.__baseTag, *getitem(self.__superTags, idx)
)
return self.__superTags[idx]
def __eq__(self, other): return self.uniq == other.uniq
def __ne__(self, other): return self.uniq != other.uniq
def __lt__(self, other): return self.uniq < other.uniq
def __le__(self, other): return self.uniq <= other.uniq
def __gt__(self, other): return self.uniq > other.uniq
def __ge__(self, other): return self.uniq >= other.uniq
def __hash__(self): return self.__hashedSuperTags
def __len__(self): return self.__lenOfSuperTags
def isSuperTagSetOf(self, tagSet):
if len(tagSet) < self.__lenOfSuperTags:
return
idx = self.__lenOfSuperTags - 1
while idx >= 0:
if self.__superTags[idx] != tagSet[idx]:
return
idx = idx - 1
return 1
def initTagSet(tag): return TagSet(tag, tag)
+66
View File
@@ -0,0 +1,66 @@
from pyasn1 import error
class TagMap:
def __init__(self, posMap={}, negMap={}, defType=None):
self.__posMap = posMap.copy()
self.__negMap = negMap.copy()
self.__defType = defType
def __contains__(self, tagSet):
return tagSet in self.__posMap or \
self.__defType is not None and tagSet not in self.__negMap
def __getitem__(self, tagSet):
if tagSet in self.__posMap:
return self.__posMap[tagSet]
elif tagSet in self.__negMap:
raise error.PyAsn1Error('Key in negative map')
elif self.__defType is not None:
return self.__defType
else:
raise KeyError()
def __repr__(self):
s = self.__class__.__name__ + '('
if self.__posMap:
s = s + 'posMap=%r, ' % (self.__posMap,)
if self.__negMap:
s = s + 'negMap=%r, ' % (self.__negMap,)
if self.__defType is not None:
s = s + 'defType=%r' % (self.__defType,)
return s + ')'
def __str__(self):
s = self.__class__.__name__ + ':\n'
if self.__posMap:
s = s + 'posMap:\n%s, ' % ',\n '.join([ x.prettyPrintType() for x in self.__posMap.values()])
if self.__negMap:
s = s + 'negMap:\n%s, ' % ',\n '.join([ x.prettyPrintType() for x in self.__negMap.values()])
if self.__defType is not None:
s = s + 'defType:\n%s, ' % self.__defType.prettyPrintType()
return s
def clone(self, parentType, tagMap, uniq=False):
if self.__defType is not None and tagMap.getDef() is not None:
raise error.PyAsn1Error('Duplicate default value at %s' % (self,))
if tagMap.getDef() is not None:
defType = tagMap.getDef()
else:
defType = self.__defType
posMap = self.__posMap.copy()
for k in tagMap.getPosMap():
if uniq and k in posMap:
raise error.PyAsn1Error('Duplicate positive key %s' % (k,))
posMap[k] = parentType
negMap = self.__negMap.copy()
negMap.update(tagMap.getNegMap())
return self.__class__(
posMap, negMap, defType,
)
def getPosMap(self): return self.__posMap.copy()
def getNegMap(self): return self.__negMap.copy()
def getDef(self): return self.__defType
File diff suppressed because it is too large Load Diff
+17
View File
@@ -0,0 +1,17 @@
# ASN.1 "useful" types
from pyasn1.type import char, tag
class ObjectDescriptor(char.GraphicString):
tagSet = char.GraphicString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 7)
)
class GeneralizedTime(char.VisibleString):
tagSet = char.VisibleString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 24)
)
class UTCTime(char.VisibleString):
tagSet = char.VisibleString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 23)
)
+630
View File
@@ -0,0 +1,630 @@
import os, logging, select, socket, struct, errno
from smb_constants import *
from smb_structs import *
from base import SMB, NotConnectedError, NotReadyError, SMBTimeout
class SMBConnection(SMB):
log = logging.getLogger('SMB.SMBConnection')
#: SMB messages will never be signed regardless of remote server's configurations; access errors will occur if the remote server requires signing.
SIGN_NEVER = 0
#: SMB messages will be signed when remote server supports signing but not requires signing.
SIGN_WHEN_SUPPORTED = 1
#: SMB messages will only be signed when remote server requires signing.
SIGN_WHEN_REQUIRED = 2
def __init__(self, username, password, my_name, remote_name, domain = '', use_ntlm_v2 = True, sign_options = SIGN_WHEN_REQUIRED, is_direct_tcp = False):
"""
Create a new SMBConnection instance.
*username* and *password* are the user credentials required to authenticate the underlying SMB connection with the remote server.
File operations can only be proceeded after the connection has been authenticated successfully.
Note that you need to call *connect* method to actually establish the SMB connection to the remote server and perform authentication.
The default TCP port for most SMB/CIFS servers using NetBIOS over TCP/IP is 139.
Some newer server installations might also support Direct hosting of SMB over TCP/IP; for these servers, the default TCP port is 445.
:param string my_name: The local NetBIOS machine name that will identify where this connection is originating from.
You can freely choose a name as long as it contains a maximum of 15 alphanumeric characters and does not contain spaces and any of ``\/:*?";|+``
:param string remote_name: The NetBIOS machine name of the remote server.
On windows, you can find out the machine name by right-clicking on the "My Computer" and selecting "Properties".
This parameter must be the same as what has been configured on the remote server, or else the connection will be rejected.
:param string domain: The network domain. On windows, it is known as the workgroup. Usually, it is safe to leave this parameter as an empty string.
:param boolean use_ntlm_v2: Indicates whether pysmb should be NTLMv1 or NTLMv2 authentication algorithm for authentication.
The choice of NTLMv1 and NTLMv2 is configured on the remote server, and there is no mechanism to auto-detect which algorithm has been configured.
Hence, we can only "guess" or try both algorithms.
On Sambda, Windows Vista and Windows 7, NTLMv2 is enabled by default. On Windows XP, we can use NTLMv1 before NTLMv2.
:param int sign_options: Determines whether SMB messages will be signed. Default is *SIGN_WHEN_REQUIRED*.
If *SIGN_WHEN_REQUIRED* (value=2), SMB messages will only be signed when remote server requires signing.
If *SIGN_WHEN_SUPPORTED* (value=1), SMB messages will be signed when remote server supports signing but not requires signing.
If *SIGN_NEVER* (value=0), SMB messages will never be signed regardless of remote server's configurations; access errors will occur if the remote server requires signing.
:param boolean is_direct_tcp: Controls whether the NetBIOS over TCP/IP (is_direct_tcp=False) or the newer Direct hosting of SMB over TCP/IP (is_direct_tcp=True) will be used for the communication.
The default parameter is False which will use NetBIOS over TCP/IP for wider compatibility (TCP port: 139).
"""
SMB.__init__(self, username, password, my_name, remote_name, domain, use_ntlm_v2, sign_options, is_direct_tcp)
self.sock = None
self.auth_result = None
self.is_busy = False
self.is_direct_tcp = is_direct_tcp
#
# SMB (and its superclass) Methods
#
def onAuthOK(self):
self.auth_result = True
def onAuthFailed(self):
self.auth_result = False
def write(self, data):
assert self.sock
data_len = len(data)
total_sent = 0
while total_sent < data_len:
sent = self.sock.send(data[total_sent:])
if sent == 0:
raise NotConnectedError('Server disconnected')
total_sent = total_sent + sent
#
# Misc Properties
#
@property
def isUsingSMB2(self):
"""A convenient property to return True if the underlying SMB connection is using SMB2 protocol."""
return self.is_using_smb2
#
# Public Methods
#
def connect(self, ip, port = 139, sock_family = socket.AF_INET, timeout = 60):
"""
Establish the SMB connection to the remote SMB/CIFS server.
You must call this method before attempting any of the file operations with the remote server.
This method will block until the SMB connection has attempted at least one authentication.
:return: A boolean value indicating the result of the authentication atttempt: True if authentication is successful; False, if otherwise.
"""
if self.sock:
self.sock.close()
self.auth_result = None
self.sock = socket.socket(sock_family)
self.sock.settimeout(timeout)
self.sock.connect(( ip, port ))
self.is_busy = True
try:
if not self.is_direct_tcp:
self.requestNMBSession()
else:
self.onNMBSessionOK()
while self.auth_result is None:
self._pollForNetBIOSPacket(timeout)
finally:
self.is_busy = False
return self.auth_result
def close(self):
"""
Terminate the SMB connection (if it has been started) and release any sources held by the underlying socket.
"""
if self.sock:
self.sock.close()
self.sock = None
def listShares(self, timeout = 30):
"""
Retrieve a list of shared resources on remote server.
:return: A list of :doc:`smb.base.SharedDevice<smb_SharedDevice>` instances describing the shared resource
"""
if not self.sock:
raise NotConnectedError('Not connected to server')
results = [ ]
def cb(entries):
self.is_busy = False
results.extend(entries)
def eb(failure):
self.is_busy = False
raise failure
self.is_busy = True
try:
self._listShares(cb, eb, timeout)
while self.is_busy:
self._pollForNetBIOSPacket(timeout)
finally:
self.is_busy = False
return results
def listPath(self, service_name, path,
search = SMB_FILE_ATTRIBUTE_READONLY | SMB_FILE_ATTRIBUTE_HIDDEN | SMB_FILE_ATTRIBUTE_SYSTEM | SMB_FILE_ATTRIBUTE_DIRECTORY | SMB_FILE_ATTRIBUTE_ARCHIVE | SMB_FILE_ATTRIBUTE_INCL_NORMAL,
pattern = '*', timeout = 30):
"""
Retrieve a directory listing of files/folders at *path*
For simplicity, pysmb defines a "normal" file as a file entry that is not read-only, not hidden, not system, not archive and not a directory.
It ignores other attributes like compression, indexed, sparse, temporary and encryption.
Note that the default search parameter will query for all read-only (SMB_FILE_ATTRIBUTE_READONLY), hidden (SMB_FILE_ATTRIBUTE_HIDDEN),
system (SMB_FILE_ATTRIBUTE_SYSTEM), archive (SMB_FILE_ATTRIBUTE_ARCHIVE), normal (SMB_FILE_ATTRIBUTE_INCL_NORMAL) files
and directories (SMB_FILE_ATTRIBUTE_DIRECTORY).
If you do not need to include "normal" files in the result, define your own search parameter without the SMB_FILE_ATTRIBUTE_INCL_NORMAL constant.
SMB_FILE_ATTRIBUTE_NORMAL should be used by itself and not be used with other bit constants.
:param string/unicode service_name: the name of the shared folder for the *path*
:param string/unicode path: path relative to the *service_name* where we are interested to learn about its files/sub-folders.
:param integer search: integer value made up from a bitwise-OR of *SMB_FILE_ATTRIBUTE_xxx* bits (see smb_constants.py).
:param string/unicode pattern: the filter to apply to the results before returning to the client.
:return: A list of :doc:`smb.base.SharedFile<smb_SharedFile>` instances.
"""
if not self.sock:
raise NotConnectedError('Not connected to server')
results = [ ]
def cb(entries):
self.is_busy = False
results.extend(entries)
def eb(failure):
self.is_busy = False
raise failure
self.is_busy = True
try:
self._listPath(service_name, path, cb, eb, search = search, pattern = pattern, timeout = timeout)
while self.is_busy:
self._pollForNetBIOSPacket(timeout)
finally:
self.is_busy = False
return results
def listSnapshots(self, service_name, path, timeout = 30):
"""
Retrieve a list of available snapshots (shadow copies) for *path*.
Note that snapshot features are only supported on Windows Vista Business, Enterprise and Ultimate, and on all Windows 7 editions.
:param string/unicode service_name: the name of the shared folder for the *path*
:param string/unicode path: path relative to the *service_name* where we are interested in the list of available snapshots
:return: A list of python *datetime.DateTime* instances in GMT/UTC time zone
"""
if not self.sock:
raise NotConnectedError('Not connected to server')
results = [ ]
def cb(entries):
self.is_busy = False
results.extend(entries)
def eb(failure):
self.is_busy = False
raise failure
self.is_busy = True
try:
self._listSnapshots(service_name, path, cb, eb, timeout = timeout)
while self.is_busy:
self._pollForNetBIOSPacket(timeout)
finally:
self.is_busy = False
return results
def getAttributes(self, service_name, path, timeout = 30):
"""
Retrieve information about the file at *path* on the *service_name*.
:param string/unicode service_name: the name of the shared folder for the *path*
:param string/unicode path: Path of the file on the remote server. If the file cannot be opened for reading, an :doc:`OperationFailure<smb_exceptions>` will be raised.
:return: A :doc:`smb.base.SharedFile<smb_SharedFile>` instance containing the attributes of the file.
"""
if not self.sock:
raise NotConnectedError('Not connected to server')
results = [ ]
def cb(info):
self.is_busy = False
results.append(info)
def eb(failure):
self.is_busy = False
raise failure
self.is_busy = True
try:
self._getAttributes(service_name, path, cb, eb, timeout)
while self.is_busy:
self._pollForNetBIOSPacket(timeout)
finally:
self.is_busy = False
return results[0]
def getSecurity(self, service_name, path, timeout = 30):
"""
Retrieve the security descriptor of the file at *path* on the *service_name*.
:param string/unicode service_name: the name of the shared folder for the *path*
:param string/unicode path: Path of the file on the remote server. If the file cannot be opened for reading, an :doc:`OperationFailure<smb_exceptions>` will be raised.
:return: A :class:`smb.security_descriptors.SecurityDescriptor` instance containing the security information of the file.
"""
if not self.sock:
raise NotConnectedError('Not connected to server')
results = [ ]
def cb(info):
self.is_busy = False
results.append(info)
def eb(failure):
self.is_busy = False
raise failure
self.is_busy = True
try:
self._getSecurity(service_name, path, cb, eb, timeout)
while self.is_busy:
self._pollForNetBIOSPacket(timeout)
finally:
self.is_busy = False
return results[0]
def retrieveFile(self, service_name, path, file_obj, timeout = 30):
"""
Retrieve the contents of the file at *path* on the *service_name* and write these contents to the provided *file_obj*.
Use *retrieveFileFromOffset()* method if you wish to specify the offset to read from the remote *path* and/or the number of bytes to write to the *file_obj*.
:param string/unicode service_name: the name of the shared folder for the *path*
:param string/unicode path: Path of the file on the remote server. If the file cannot be opened for reading, an :doc:`OperationFailure<smb_exceptions>` will be raised.
:param file_obj: A file-like object that has a *write* method. Data will be written continuously to *file_obj* until EOF is received from the remote service.
:return: A 2-element tuple of ( file attributes of the file on server, number of bytes written to *file_obj* ).
The file attributes is an integer value made up from a bitwise-OR of *SMB_FILE_ATTRIBUTE_xxx* bits (see smb_constants.py)
"""
return self.retrieveFileFromOffset(service_name, path, file_obj, 0L, -1L, timeout)
def retrieveFileFromOffset(self, service_name, path, file_obj, offset = 0L, max_length = -1L, timeout = 30):
"""
Retrieve the contents of the file at *path* on the *service_name* and write these contents to the provided *file_obj*.
:param string/unicode service_name: the name of the shared folder for the *path*
:param string/unicode path: Path of the file on the remote server. If the file cannot be opened for reading, an :doc:`OperationFailure<smb_exceptions>` will be raised.
:param file_obj: A file-like object that has a *write* method. Data will be written continuously to *file_obj* up to *max_length* number of bytes.
:param integer/long offset: the offset in the remote *path* where the first byte will be read and written to *file_obj*. Must be either zero or a positive integer/long value.
:param integer/long max_length: maximum number of bytes to read from the remote *path* and write to the *file_obj*. Specify a negative value to read from *offset* to the EOF.
If zero, the method returns immediately after the file is opened successfully for reading.
:return: A 2-element tuple of ( file attributes of the file on server, number of bytes written to *file_obj* ).
The file attributes is an integer value made up from a bitwise-OR of *SMB_FILE_ATTRIBUTE_xxx* bits (see smb_constants.py)
"""
if not self.sock:
raise NotConnectedError('Not connected to server')
results = [ ]
def cb(r):
self.is_busy = False
results.append(r[1:])
def eb(failure):
self.is_busy = False
raise failure
self.is_busy = True
try:
self._retrieveFileFromOffset(service_name, path, file_obj, cb, eb, offset, max_length, timeout = timeout)
while self.is_busy:
self._pollForNetBIOSPacket(timeout)
finally:
self.is_busy = False
return results[0]
def storeFile(self, service_name, path, file_obj, timeout = 30):
"""
Store the contents of the *file_obj* at *path* on the *service_name*.
If the file already exists on the remote server, it will be truncated and overwritten.
:param string/unicode service_name: the name of the shared folder for the *path*
:param string/unicode path: Path of the file on the remote server. If the file at *path* does not exist, it will be created. Otherwise, it will be overwritten.
If the *path* refers to a folder or the file cannot be opened for writing, an :doc:`OperationFailure<smb_exceptions>` will be raised.
:param file_obj: A file-like object that has a *read* method. Data will read continuously from *file_obj* until EOF.
:return: Number of bytes uploaded
"""
return self.storeFileFromOffset(service_name, path, file_obj, 0L, True, timeout)
def storeFileFromOffset(self, service_name, path, file_obj, offset = 0L, truncate = False, timeout = 30):
"""
Store the contents of the *file_obj* at *path* on the *service_name*.
:param string/unicode service_name: the name of the shared folder for the *path*
:param string/unicode path: Path of the file on the remote server. If the file at *path* does not exist, it will be created.
If the *path* refers to a folder or the file cannot be opened for writing, an :doc:`OperationFailure<smb_exceptions>` will be raised.
:param file_obj: A file-like object that has a *read* method. Data will read continuously from *file_obj* until EOF.
:param offset: Long integer value which specifies the offset in the remote server to start writing. First byte of the file is 0.
:param truncate: Boolean value. If True and the file exists on the remote server, it will be truncated first before writing. Default is False.
:return: the file position where the next byte will be written.
"""
if not self.sock:
raise NotConnectedError('Not connected to server')
results = [ ]
def cb(r):
self.is_busy = False
results.append(r[1])
def eb(failure):
self.is_busy = False
raise failure
self.is_busy = True
try:
self._storeFileFromOffset(service_name, path, file_obj, cb, eb, offset, truncate = truncate, timeout = timeout)
while self.is_busy:
self._pollForNetBIOSPacket(timeout)
finally:
self.is_busy = False
return results[0]
def deleteFiles(self, service_name, path_file_pattern, timeout = 30):
"""
Delete one or more regular files. It supports the use of wildcards in file names, allowing for deletion of multiple files in a single request.
:param string/unicode service_name: Contains the name of the shared folder.
:param string/unicode path_file_pattern: The pathname of the file(s) to be deleted, relative to the service_name.
Wildcards may be used in th filename component of the path.
If your path/filename contains non-English characters, you must pass in an unicode string.
:return: None
"""
if not self.sock:
raise NotConnectedError('Not connected to server')
def cb(r):
self.is_busy = False
def eb(failure):
self.is_busy = False
raise failure
self.is_busy = True
try:
self._deleteFiles(service_name, path_file_pattern, cb, eb, timeout = timeout)
while self.is_busy:
self._pollForNetBIOSPacket(timeout)
finally:
self.is_busy = False
def resetFileAttributes(self, service_name, path_file_pattern, timeout = 30):
"""
Reset file attributes of one or more regular files or folders.
It supports the use of wildcards in file names, allowing for unlocking of multiple files/folders in a single request.
This function is very helpful when deleting files/folders that are read-only.
Note: this function is currently only implemented for SMB2! Technically, it sets the FILE_ATTRIBUTE_NORMAL flag, therefore clearing all other flags. (See https://msdn.microsoft.com/en-us/library/cc232110.aspx for further information)
:param string/unicode service_name: Contains the name of the shared folder.
:param string/unicode path_file_pattern: The pathname of the file(s) to be deleted, relative to the service_name.
Wildcards may be used in the filename component of the path.
If your path/filename contains non-English characters, you must pass in an unicode string.
:return: None
"""
if not self.sock:
raise NotConnectedError('Not connected to server')
def cb(r):
self.is_busy = False
def eb(failure):
self.is_busy = False
raise failure
self.is_busy = True
try:
self._resetFileAttributes(service_name, path_file_pattern, cb, eb, timeout = timeout)
while self.is_busy:
self._pollForNetBIOSPacket(timeout)
finally:
self.is_busy = False
def createDirectory(self, service_name, path, timeout = 30):
"""
Creates a new directory *path* on the *service_name*.
:param string/unicode service_name: Contains the name of the shared folder.
:param string/unicode path: The path of the new folder (relative to) the shared folder.
If the path contains non-English characters, an unicode string must be used to pass in the path.
:return: None
"""
if not self.sock:
raise NotConnectedError('Not connected to server')
def cb(r):
self.is_busy = False
def eb(failure):
self.is_busy = False
raise failure
self.is_busy = True
try:
self._createDirectory(service_name, path, cb, eb, timeout = timeout)
while self.is_busy:
self._pollForNetBIOSPacket(timeout)
finally:
self.is_busy = False
def deleteDirectory(self, service_name, path, timeout = 30):
"""
Delete the empty folder at *path* on *service_name*
:param string/unicode service_name: Contains the name of the shared folder.
:param string/unicode path: The path of the to-be-deleted folder (relative to) the shared folder.
If the path contains non-English characters, an unicode string must be used to pass in the path.
:return: None
"""
if not self.sock:
raise NotConnectedError('Not connected to server')
def cb(r):
self.is_busy = False
def eb(failure):
self.is_busy = False
raise failure
self.is_busy = True
try:
self._deleteDirectory(service_name, path, cb, eb, timeout = timeout)
while self.is_busy:
self._pollForNetBIOSPacket(timeout)
finally:
self.is_busy = False
def rename(self, service_name, old_path, new_path, timeout = 30):
"""
Rename a file or folder at *old_path* to *new_path* shared at *service_name*. Note that this method cannot be used to rename file/folder across different shared folders
*old_path* and *new_path* are string/unicode referring to the old and new path of the renamed resources (relative to) the shared folder.
If the path contains non-English characters, an unicode string must be used to pass in the path.
:param string/unicode service_name: Contains the name of the shared folder.
:return: None
"""
if not self.sock:
raise NotConnectedError('Not connected to server')
def cb(r):
self.is_busy = False
def eb(failure):
self.is_busy = False
raise failure
self.is_busy = True
try:
self._rename(service_name, old_path, new_path, cb, eb)
while self.is_busy:
self._pollForNetBIOSPacket(timeout)
finally:
self.is_busy = False
def echo(self, data, timeout = 10):
"""
Send an echo command containing *data* to the remote SMB/CIFS server. The remote SMB/CIFS will reply with the same *data*.
:param bytes data: Data to send to the remote server. Must be a bytes object.
:return: The *data* parameter
"""
if not self.sock:
raise NotConnectedError('Not connected to server')
results = [ ]
def cb(r):
self.is_busy = False
results.append(r)
def eb(failure):
self.is_busy = False
raise failure
self.is_busy = True
try:
self._echo(data, cb, eb)
while self.is_busy:
self._pollForNetBIOSPacket(timeout)
finally:
self.is_busy = False
return results[0]
#
# Protected Methods
#
def _pollForNetBIOSPacket(self, timeout):
expiry_time = time.time() + timeout
read_len = 4
data = ''
while read_len > 0:
try:
if expiry_time < time.time():
raise SMBTimeout
ready, _, _ = select.select([ self.sock.fileno() ], [ ], [ ], timeout)
if not ready:
raise SMBTimeout
d = self.sock.recv(read_len)
if len(d) == 0:
raise NotConnectedError
data = data + d
read_len -= len(d)
except select.error, ex:
if isinstance(ex, types.TupleType):
if ex[0] != errno.EINTR and ex[0] != errno.EAGAIN:
raise ex
else:
raise ex
type_, flags, length = struct.unpack('>BBH', data)
if type_ == 0x0:
# This is a Direct TCP packet
# The length is specified in the header from byte 8. (0-indexed)
# we read a structure assuming NBT, so to get the real length
# combine the length and flag fields together
length = length + (flags << 16)
else:
# This is a NetBIOS over TCP (NBT) packet
# The length is specified in the header from byte 16. (0-indexed)
if flags & 0x01:
length = length | 0x10000
read_len = length
while read_len > 0:
try:
if expiry_time < time.time():
raise SMBTimeout
ready, _, _ = select.select([ self.sock.fileno() ], [ ], [ ], timeout)
if not ready:
raise SMBTimeout
d = self.sock.recv(read_len)
if len(d) == 0:
raise NotConnectedError
data = data + d
read_len -= len(d)
except select.error, ex:
if isinstance(ex, types.TupleType):
if ex[0] != errno.EINTR and ex[0] != errno.EAGAIN:
raise ex
else:
raise ex
self.feedData(data)
+97
View File
@@ -0,0 +1,97 @@
import os, sys, socket, urllib2, mimetypes, mimetools, tempfile
from urllib import (unwrap, unquote, splittype, splithost, quote,
addinfourl, splitport, splittag,
splitattr, ftpwrapper, splituser, splitpasswd, splitvalue)
from nmb.NetBIOS import NetBIOS
from smb.SMBConnection import SMBConnection
try:
from cStringIO import StringIO
except ImportError:
from StringIO import StringIO
USE_NTLM = True
MACHINE_NAME = None
class SMBHandler(urllib2.BaseHandler):
def smb_open(self, req):
global USE_NTLM, MACHINE_NAME
host = req.get_host()
if not host:
raise urllib2.URLError('SMB error: no host given')
host, port = splitport(host)
if port is None:
port = 139
else:
port = int(port)
# username/password handling
user, host = splituser(host)
if user:
user, passwd = splitpasswd(user)
else:
passwd = None
host = unquote(host)
user = user or ''
domain = ''
if ';' in user:
domain, user = user.split(';', 1)
passwd = passwd or ''
myname = MACHINE_NAME or self.generateClientMachineName()
n = NetBIOS()
names = n.queryIPForName(host)
if names:
server_name = names[0]
else:
raise urllib2.URLError('SMB error: Hostname does not reply back with its machine name')
path, attrs = splitattr(req.get_selector())
if path.startswith('/'):
path = path[1:]
dirs = path.split('/')
dirs = map(unquote, dirs)
service, path = dirs[0], '/'.join(dirs[1:])
try:
conn = SMBConnection(user, passwd, myname, server_name, domain=domain, use_ntlm_v2 = USE_NTLM)
conn.connect(host, port)
if req.has_data():
data_fp = req.get_data()
filelen = conn.storeFile(service, path, data_fp)
headers = "Content-length: 0\n"
fp = StringIO("")
else:
fp = self.createTempFile()
file_attrs, retrlen = conn.retrieveFile(service, path, fp)
fp.seek(0)
headers = ""
mtype = mimetypes.guess_type(req.get_full_url())[0]
if mtype:
headers += "Content-type: %s\n" % mtype
if retrlen is not None and retrlen >= 0:
headers += "Content-length: %d\n" % retrlen
sf = StringIO(headers)
headers = mimetools.Message(sf)
return addinfourl(fp, headers, req.get_full_url())
except Exception, ex:
raise urllib2.URLError, ('smb error: %s' % ex), sys.exc_info()[2]
def createTempFile(self):
return tempfile.TemporaryFile()
def generateClientMachineName(self):
hostname = socket.gethostname()
if hostname:
return hostname.split('.')[0]
return 'SMB%d' % os.getpid()
+409
View File
@@ -0,0 +1,409 @@
import os, logging, time
from twisted.internet import reactor, defer
from twisted.internet.protocol import ClientFactory, Protocol
from smb_constants import *
from smb_structs import *
from base import SMB, NotConnectedError, NotReadyError, SMBTimeout
__all__ = [ 'SMBProtocolFactory', 'NotConnectedError', 'NotReadyError' ]
class SMBProtocol(Protocol, SMB):
log = logging.getLogger('SMB.SMBProtocol')
#
# Protocol Methods
#
def connectionMade(self):
self.factory.instance = self
if not self.is_direct_tcp:
self.requestNMBSession()
else:
self.onNMBSessionOK()
def connectionLost(self, reason):
if self.factory.instance == self:
self.instance = None
def dataReceived(self, data):
self.feedData(data)
#
# SMB (and its superclass) Methods
#
def write(self, data):
self.transport.write(data)
def onAuthOK(self):
if self.factory.instance == self:
self.factory.onAuthOK()
reactor.callLater(1, self._cleanupPendingRequests)
def onAuthFailed(self):
if self.factory.instance == self:
self.factory.onAuthFailed()
def onNMBSessionFailed(self):
self.log.error('Cannot establish NetBIOS session. You might have provided a wrong remote_name')
#
# Protected Methods
#
def _cleanupPendingRequests(self):
if self.factory.instance == self:
now = time.time()
to_remove = []
for mid, r in self.pending_requests.iteritems():
if r.expiry_time < now:
try:
r.errback(SMBTimeout())
except Exception: pass
to_remove.append(mid)
for mid in to_remove:
del self.pending_requests[mid]
reactor.callLater(1, self._cleanupPendingRequests)
class SMBProtocolFactory(ClientFactory):
protocol = SMBProtocol
log = logging.getLogger('SMB.SMBFactory')
#: SMB messages will never be signed regardless of remote server's configurations; access errors will occur if the remote server requires signing.
SIGN_NEVER = 0
#: SMB messages will be signed when remote server supports signing but not requires signing.
SIGN_WHEN_SUPPORTED = 1
#: SMB messages will only be signed when remote server requires signing.
SIGN_WHEN_REQUIRED = 2
def __init__(self, username, password, my_name, remote_name, domain = '', use_ntlm_v2 = True, sign_options = SIGN_WHEN_REQUIRED, is_direct_tcp = False):
"""
Create a new SMBProtocolFactory instance. You will pass this instance to *reactor.connectTCP()* which will then instantiate the TCP connection to the remote SMB/CIFS server.
Note that the default TCP port for most SMB/CIFS servers using NetBIOS over TCP/IP is 139.
Some newer server installations might also support Direct hosting of SMB over TCP/IP; for these servers, the default TCP port is 445.
*username* and *password* are the user credentials required to authenticate the underlying SMB connection with the remote server.
File operations can only be proceeded after the connection has been authenticated successfully.
:param string my_name: The local NetBIOS machine name that will identify where this connection is originating from.
You can freely choose a name as long as it contains a maximum of 15 alphanumeric characters and does not contain spaces and any of ``\/:*?";|+``.
:param string remote_name: The NetBIOS machine name of the remote server.
On windows, you can find out the machine name by right-clicking on the "My Computer" and selecting "Properties".
This parameter must be the same as what has been configured on the remote server, or else the connection will be rejected.
:param string domain: The network domain. On windows, it is known as the workgroup. Usually, it is safe to leave this parameter as an empty string.
:param boolean use_ntlm_v2: Indicates whether pysmb should be NTLMv1 or NTLMv2 authentication algorithm for authentication.
The choice of NTLMv1 and NTLMv2 is configured on the remote server, and there is no mechanism to auto-detect which algorithm has been configured.
Hence, we can only "guess" or try both algorithms.
On Sambda, Windows Vista and Windows 7, NTLMv2 is enabled by default. On Windows XP, we can use NTLMv1 before NTLMv2.
:param int sign_options: Determines whether SMB messages will be signed. Default is *SIGN_WHEN_REQUIRED*.
If *SIGN_WHEN_REQUIRED* (value=2), SMB messages will only be signed when remote server requires signing.
If *SIGN_WHEN_SUPPORTED* (value=1), SMB messages will be signed when remote server supports signing but not requires signing.
If *SIGN_NEVER* (value=0), SMB messages will never be signed regardless of remote server's configurations; access errors will occur if the remote server requires signing.
:param boolean is_direct_tcp: Controls whether the NetBIOS over TCP/IP (is_direct_tcp=False) or the newer Direct hosting of SMB over TCP/IP (is_direct_tcp=True) will be used for the communication.
The default parameter is False which will use NetBIOS over TCP/IP for wider compatibility (TCP port: 139).
"""
self.username = username
self.password = password
self.my_name = my_name
self.remote_name = remote_name
self.domain = domain
self.use_ntlm_v2 = use_ntlm_v2
self.sign_options = sign_options
self.is_direct_tcp = is_direct_tcp
self.instance = None #: The single SMBProtocol instance for each SMBProtocolFactory instance. Usually, you should not need to touch this attribute directly.
#
# Public Property
#
@property
def isReady(self):
"""A convenient property to return True if the underlying SMB connection has connected to remote server, has successfully authenticated itself and is ready for file operations."""
return bool(self.instance and self.instance.has_authenticated)
@property
def isUsingSMB2(self):
"""A convenient property to return True if the underlying SMB connection is using SMB2 protocol."""
return self.instance and self.instance.is_using_smb2
#
# Public Methods for Callbacks
#
def onAuthOK(self):
"""
Override this method in your *SMBProtocolFactory* subclass to add in post-authentication handling.
This method will be called when the server has replied that the SMB connection has been successfully authenticated.
File operations can proceed when this method has been called.
"""
pass
def onAuthFailed(self):
"""
Override this method in your *SMBProtocolFactory* subclass to add in post-authentication handling.
This method will be called when the server has replied that the SMB connection has been successfully authenticated.
If you want to retry authenticating from this method,
1. Disconnect the underlying SMB connection (call ``self.instance.transport.loseConnection()``)
2. Create a new SMBProtocolFactory subclass instance with different user credientials or different NTLM algorithm flag.
3. Call ``reactor.connectTCP`` with the new instance to re-establish the SMB connection
"""
pass
#
# Public Methods
#
def listShares(self, timeout = 30):
"""
Retrieve a list of shared resources on remote server.
:param integer/float timeout: Number of seconds that pysmb will wait before raising *SMBTimeout* via the returned *Deferred* instance's *errback* method.
:return: A *twisted.internet.defer.Deferred* instance. The callback function will be called with a list of :doc:`smb.base.SharedDevice<smb_SharedDevice>` instances.
"""
if not self.instance:
raise NotConnectedError('Not connected to server')
d = defer.Deferred()
self.instance._listShares(d.callback, d.errback, timeout)
return d
def listPath(self, service_name, path,
search = SMB_FILE_ATTRIBUTE_READONLY | SMB_FILE_ATTRIBUTE_HIDDEN | SMB_FILE_ATTRIBUTE_SYSTEM | SMB_FILE_ATTRIBUTE_DIRECTORY | SMB_FILE_ATTRIBUTE_ARCHIVE | SMB_FILE_ATTRIBUTE_INCL_NORMAL,
pattern = '*', timeout = 30):
"""
Retrieve a directory listing of files/folders at *path*
For simplicity, pysmb defines a "normal" file as a file entry that is not read-only, not hidden, not system, not archive and not a directory.
It ignores other attributes like compression, indexed, sparse, temporary and encryption.
Note that the default search parameter will query for all read-only (SMB_FILE_ATTRIBUTE_READONLY), hidden (SMB_FILE_ATTRIBUTE_HIDDEN),
system (SMB_FILE_ATTRIBUTE_SYSTEM), archive (SMB_FILE_ATTRIBUTE_ARCHIVE), normal (SMB_FILE_ATTRIBUTE_INCL_NORMAL) files
and directories (SMB_FILE_ATTRIBUTE_DIRECTORY).
If you do not need to include "normal" files in the result, define your own search parameter without the SMB_FILE_ATTRIBUTE_INCL_NORMAL constant.
SMB_FILE_ATTRIBUTE_NORMAL should be used by itself and not be used with other bit constants.
:param string/unicode service_name: the name of the shared folder for the *path*
:param string/unicode path: path relative to the *service_name* where we are interested to learn about its files/sub-folders.
:param integer search: integer value made up from a bitwise-OR of *SMB_FILE_ATTRIBUTE_xxx* bits (see smb_constants.py).
:param string/unicode pattern: the filter to apply to the results before returning to the client.
:param integer/float timeout: Number of seconds that pysmb will wait before raising *SMBTimeout* via the returned *Deferred* instance's *errback* method.
:return: A *twisted.internet.defer.Deferred* instance. The callback function will be called with a list of :doc:`smb.base.SharedFile<smb_SharedFile>` instances.
"""
if not self.instance:
raise NotConnectedError('Not connected to server')
d = defer.Deferred()
self.instance._listPath(service_name, path, d.callback, d.errback, search = search, pattern = pattern, timeout = timeout)
return d
def listSnapshots(self, service_name, path, timeout = 30):
"""
Retrieve a list of available snapshots (a.k.a. shadow copies) for *path*.
Note that snapshot features are only supported on Windows Vista Business, Enterprise and Ultimate, and on all Windows 7 editions.
:param string/unicode service_name: the name of the shared folder for the *path*
:param string/unicode path: path relative to the *service_name* where we are interested in the list of available snapshots
:return: A *twisted.internet.defer.Deferred* instance. The callback function will be called with a list of python *datetime.DateTime*
instances in GMT/UTC time zone
"""
if not self.instance:
raise NotConnectedError('Not connected to server')
d = defer.Deferred()
self.instance._listSnapshots(service_name, path, d.callback, d.errback, timeout = timeout)
return d
def getAttributes(self, service_name, path, timeout = 30):
"""
Retrieve information about the file at *path* on the *service_name*.
:param string/unicode service_name: the name of the shared folder for the *path*
:param string/unicode path: Path of the file on the remote server. If the file cannot be opened for reading, an :doc:`OperationFailure<smb_exceptions>` will be raised.
:return: A *twisted.internet.defer.Deferred* instance. The callback function will be called with a :doc:`smb.base.SharedFile<smb_SharedFile>` instance containing the attributes of the file.
"""
if not self.instance:
raise NotConnectedError('Not connected to server')
d = defer.Deferred()
self.instance._getAttributes(service_name, path, d.callback, d.errback, timeout = timeout)
return d
def retrieveFile(self, service_name, path, file_obj, timeout = 30):
"""
Retrieve the contents of the file at *path* on the *service_name* and write these contents to the provided *file_obj*.
Use *retrieveFileFromOffset()* method if you need to specify the offset to read from the remote *path* and/or the maximum number of bytes to write to the *file_obj*.
The meaning of the *timeout* parameter will be different from other file operation methods. As the downloaded file usually exceeeds the maximum size
of each SMB/CIFS data message, it will be packetized into a series of request messages (each message will request about about 60kBytes).
The *timeout* parameter is an integer/float value that specifies the timeout interval for these individual SMB/CIFS message to be transmitted and downloaded from the remote SMB/CIFS server.
:param string/unicode service_name: the name of the shared folder for the *path*
:param string/unicode path: Path of the file on the remote server. If the file cannot be opened for reading, an :doc:`OperationFailure<smb_exceptions>` will be called in the returned *Deferred* errback.
:param file_obj: A file-like object that has a *write* method. Data will be written continuously to *file_obj* until EOF is received from the remote service.
:return: A *twisted.internet.defer.Deferred* instance. The callback function will be called with a 3-element tuple of ( *file_obj*, file attributes of the file on server, number of bytes written to *file_obj* ).
The file attributes is an integer value made up from a bitwise-OR of *SMB_FILE_ATTRIBUTE_xxx* bits (see smb_constants.py)
"""
return self.retrieveFileFromOffset(service_name, path, file_obj, 0L, -1L, timeout)
def retrieveFileFromOffset(self, service_name, path, file_obj, offset = 0L, max_length = -1L, timeout = 30):
"""
Retrieve the contents of the file at *path* on the *service_name* and write these contents to the provided *file_obj*.
The meaning of the *timeout* parameter will be different from other file operation methods. As the downloaded file usually exceeeds the maximum size
of each SMB/CIFS data message, it will be packetized into a series of request messages (each message will request about about 60kBytes).
The *timeout* parameter is an integer/float value that specifies the timeout interval for these individual SMB/CIFS message to be transmitted and downloaded from the remote SMB/CIFS server.
:param string/unicode service_name: the name of the shared folder for the *path*
:param string/unicode path: Path of the file on the remote server. If the file cannot be opened for reading, an :doc:`OperationFailure<smb_exceptions>` will be called in the returned *Deferred* errback.
:param file_obj: A file-like object that has a *write* method. Data will be written continuously to *file_obj* until EOF is received from the remote service.
:param integer/long offset: the offset in the remote *path* where the first byte will be read and written to *file_obj*. Must be either zero or a positive integer/long value.
:param integer/long max_length: maximum number of bytes to read from the remote *path* and write to the *file_obj*. Specify a negative value to read from *offset* to the EOF.
If zero, the *Deferred* callback is invoked immediately after the file is opened successfully for reading.
:return: A *twisted.internet.defer.Deferred* instance. The callback function will be called with a 3-element tuple of ( *file_obj*, file attributes of the file on server, number of bytes written to *file_obj* ).
The file attributes is an integer value made up from a bitwise-OR of *SMB_FILE_ATTRIBUTE_xxx* bits (see smb_constants.py)
"""
if not self.instance:
raise NotConnectedError('Not connected to server')
d = defer.Deferred()
self.instance._retrieveFileFromOffset(service_name, path, file_obj, d.callback, d.errback, offset, max_length, timeout = timeout)
return d
def storeFile(self, service_name, path, file_obj, timeout = 30):
"""
Store the contents of the *file_obj* at *path* on the *service_name*.
The meaning of the *timeout* parameter will be different from other file operation methods. As the uploaded file usually exceeeds the maximum size
of each SMB/CIFS data message, it will be packetized into a series of messages (usually about 60kBytes).
The *timeout* parameter is an integer/float value that specifies the timeout interval for these individual SMB/CIFS message to be transmitted and acknowledged
by the remote SMB/CIFS server.
:param string/unicode service_name: the name of the shared folder for the *path*
:param string/unicode path: Path of the file on the remote server. If the file at *path* does not exist, it will be created. Otherwise, it will be overwritten.
If the *path* refers to a folder or the file cannot be opened for writing, an :doc:`OperationFailure<smb_exceptions>` will be called in the returned *Deferred* errback.
:param file_obj: A file-like object that has a *read* method. Data will read continuously from *file_obj* until EOF.
:return: A *twisted.internet.defer.Deferred* instance. The callback function will be called with a 2-element tuple of ( *file_obj*, number of bytes uploaded ).
"""
if not self.instance:
raise NotConnectedError('Not connected to server')
d = defer.Deferred()
self.instance._storeFile(service_name, path, file_obj, d.callback, d.errback, timeout = timeout)
return d
def deleteFiles(self, service_name, path_file_pattern, timeout = 30):
"""
Delete one or more regular files. It supports the use of wildcards in file names, allowing for deletion of multiple files in a single request.
:param string/unicode service_name: Contains the name of the shared folder.
:param string/unicode path_file_pattern: The pathname of the file(s) to be deleted, relative to the service_name.
Wildcards may be used in th filename component of the path.
If your path/filename contains non-English characters, you must pass in an unicode string.
:param integer/float timeout: Number of seconds that pysmb will wait before raising *SMBTimeout* via the returned *Deferred* instance's *errback* method.
:return: A *twisted.internet.defer.Deferred* instance. The callback function will be called with the *path_file_pattern* parameter.
"""
if not self.instance:
raise NotConnectedError('Not connected to server')
d = defer.Deferred()
self.instance._deleteFiles(service_name, path_file_pattern, d.callback, d.errback, timeout = timeout)
return d
def createDirectory(self, service_name, path):
"""
Creates a new directory *path* on the *service_name*.
:param string/unicode service_name: Contains the name of the shared folder.
:param string/unicode path: The path of the new folder (relative to) the shared folder.
If the path contains non-English characters, an unicode string must be used to pass in the path.
:param integer/float timeout: Number of seconds that pysmb will wait before raising *SMBTimeout* via the returned *Deferred* instance's *errback* method.
:return: A *twisted.internet.defer.Deferred* instance. The callback function will be called with the *path* parameter.
"""
if not self.instance:
raise NotConnectedError('Not connected to server')
d = defer.Deferred()
self.instance._createDirectory(service_name, path, d.callback, d.errback)
return d
def deleteDirectory(self, service_name, path):
"""
Delete the empty folder at *path* on *service_name*
:param string/unicode service_name: Contains the name of the shared folder.
:param string/unicode path: The path of the to-be-deleted folder (relative to) the shared folder.
If the path contains non-English characters, an unicode string must be used to pass in the path.
:param integer/float timeout: Number of seconds that pysmb will wait before raising *SMBTimeout* via the returned *Deferred* instance's *errback* method.
:return: A *twisted.internet.defer.Deferred* instance. The callback function will be called with the *path* parameter.
"""
if not self.instance:
raise NotConnectedError('Not connected to server')
d = defer.Deferred()
self.instance._deleteDirectory(service_name, path, d.callback, d.errback)
return d
def rename(self, service_name, old_path, new_path):
"""
Rename a file or folder at *old_path* to *new_path* shared at *service_name*. Note that this method cannot be used to rename file/folder across different shared folders
*old_path* and *new_path* are string/unicode referring to the old and new path of the renamed resources (relative to) the shared folder.
If the path contains non-English characters, an unicode string must be used to pass in the path.
:param string/unicode service_name: Contains the name of the shared folder.
:param integer/float timeout: Number of seconds that pysmb will wait before raising *SMBTimeout* via the returned *Deferred* instance's *errback* method.
:return: A *twisted.internet.defer.Deferred* instance. The callback function will be called with a 2-element tuple of ( *old_path*, *new_path* ).
"""
if not self.instance:
raise NotConnectedError('Not connected to server')
d = defer.Deferred()
self.instance._rename(service_name, old_path, new_path, d.callback, d.errback)
return d
def echo(self, data, timeout = 10):
"""
Send an echo command containing *data* to the remote SMB/CIFS server. The remote SMB/CIFS will reply with the same *data*.
:param bytes data: Data to send to the remote server. Must be a bytes object.
:param integer/float timeout: Number of seconds that pysmb will wait before raising *SMBTimeout* via the returned *Deferred* instance's *errback* method.
:return: A *twisted.internet.defer.Deferred* instance. The callback function will be called with the *data* parameter.
"""
if not self.instance:
raise NotConnectedError('Not connected to server')
d = defer.Deferred()
self.instance._echo(data, d.callback, d.errback, timeout)
return d
def closeConnection(self):
"""
Disconnect from the remote SMB/CIFS server. The TCP connection will be closed at the earliest opportunity after this method returns.
:return: None
"""
if not self.instance:
raise NotConnectedError('Not connected to server')
self.instance.transport.loseConnection()
#
# ClientFactory methods
# (Do not touch these unless you know what you are doing)
#
def buildProtocol(self, addr):
p = self.protocol(self.username, self.password, self.my_name, self.remote_name, self.domain, self.use_ntlm_v2, self.sign_options, self.is_direct_tcp)
p.factory = self
return p
+1
View File
@@ -0,0 +1 @@
File diff suppressed because it is too large Load Diff
+248
View File
@@ -0,0 +1,248 @@
import types, hmac, binascii, struct, random
from utils.pyDes import des
try:
import hashlib
hashlib.new('md4')
def MD4(): return hashlib.new('md4')
except ( ImportError, ValueError ):
from utils.md4 import MD4
try:
import hashlib
def MD5(s): return hashlib.md5(s)
except ImportError:
import md5
def MD5(s): return md5.new(s)
################
# NTLMv2 Methods
################
# The following constants are defined in accordance to [MS-NLMP]: 2.2.2.5
NTLM_NegotiateUnicode = 0x00000001
NTLM_NegotiateOEM = 0x00000002
NTLM_RequestTarget = 0x00000004
NTLM_Unknown9 = 0x00000008
NTLM_NegotiateSign = 0x00000010
NTLM_NegotiateSeal = 0x00000020
NTLM_NegotiateDatagram = 0x00000040
NTLM_NegotiateLanManagerKey = 0x00000080
NTLM_Unknown8 = 0x00000100
NTLM_NegotiateNTLM = 0x00000200
NTLM_NegotiateNTOnly = 0x00000400
NTLM_Anonymous = 0x00000800
NTLM_NegotiateOemDomainSupplied = 0x00001000
NTLM_NegotiateOemWorkstationSupplied = 0x00002000
NTLM_Unknown6 = 0x00004000
NTLM_NegotiateAlwaysSign = 0x00008000
NTLM_TargetTypeDomain = 0x00010000
NTLM_TargetTypeServer = 0x00020000
NTLM_TargetTypeShare = 0x00040000
NTLM_NegotiateExtendedSecurity = 0x00080000
NTLM_NegotiateIdentify = 0x00100000
NTLM_Unknown5 = 0x00200000
NTLM_RequestNonNTSessionKey = 0x00400000
NTLM_NegotiateTargetInfo = 0x00800000
NTLM_Unknown4 = 0x01000000
NTLM_NegotiateVersion = 0x02000000
NTLM_Unknown3 = 0x04000000
NTLM_Unknown2 = 0x08000000
NTLM_Unknown1 = 0x10000000
NTLM_Negotiate128 = 0x20000000
NTLM_NegotiateKeyExchange = 0x40000000
NTLM_Negotiate56 = 0x80000000
NTLM_FLAGS = NTLM_NegotiateUnicode | \
NTLM_RequestTarget | \
NTLM_NegotiateNTLM | \
NTLM_NegotiateAlwaysSign | \
NTLM_NegotiateExtendedSecurity | \
NTLM_NegotiateTargetInfo | \
NTLM_NegotiateVersion | \
NTLM_Negotiate128 | \
NTLM_NegotiateKeyExchange | \
NTLM_Negotiate56
def generateNegotiateMessage():
"""
References:
===========
- [MS-NLMP]: 2.2.1.1
"""
s = struct.pack('<8sII8s8s8s',
'NTLMSSP\0', 0x01, NTLM_FLAGS,
'\0' * 8, # Domain
'\0' * 8, # Workstation
'\x06\x00\x72\x17\x00\x00\x00\x0F') # Version [MS-NLMP]: 2.2.2.10
return s
def generateAuthenticateMessage(challenge_flags, nt_response, lm_response, session_key, user, domain = 'WORKGROUP', workstation = 'LOCALHOST'):
"""
References:
===========
- [MS-NLMP]: 2.2.1.3
"""
FORMAT = '<8sIHHIHHIHHIHHIHHIHHII'
FORMAT_SIZE = struct.calcsize(FORMAT)
lm_response_length = len(lm_response)
lm_response_offset = FORMAT_SIZE
nt_response_length = len(nt_response)
nt_response_offset = lm_response_offset + lm_response_length
domain_unicode = domain.encode('UTF-16LE')
domain_length = len(domain_unicode)
domain_offset = nt_response_offset + nt_response_length
padding = ''
if domain_offset % 2 != 0:
padding = '\0'
domain_offset += 1
user_unicode = user.encode('UTF-16LE')
user_length = len(user_unicode)
user_offset = domain_offset + domain_length
workstation_unicode = workstation.encode('UTF-16LE')
workstation_length = len(workstation_unicode)
workstation_offset = user_offset + user_length
session_key_length = len(session_key)
session_key_offset = workstation_offset + workstation_length
auth_flags = challenge_flags
auth_flags &= ~NTLM_NegotiateVersion
s = struct.pack(FORMAT,
'NTLMSSP\0', 0x03,
lm_response_length, lm_response_length, lm_response_offset,
nt_response_length, nt_response_length, nt_response_offset,
domain_length, domain_length, domain_offset,
user_length, user_length, user_offset,
workstation_length, workstation_length, workstation_offset,
session_key_length, session_key_length, session_key_offset,
auth_flags)
return s + lm_response + nt_response + padding + domain_unicode + user_unicode + workstation_unicode + session_key
def decodeChallengeMessage(ntlm_data):
"""
References:
===========
- [MS-NLMP]: 2.2.1.2
- [MS-NLMP]: 2.2.2.1 (AV_PAIR)
"""
FORMAT = '<8sIHHII8s8sHHI'
FORMAT_SIZE = struct.calcsize(FORMAT)
signature, message_type, \
targetname_len, targetname_maxlen, targetname_offset, \
flags, challenge, _, \
targetinfo_len, targetinfo_maxlen, targetinfo_offset, \
= struct.unpack(FORMAT, ntlm_data[:FORMAT_SIZE])
assert signature == 'NTLMSSP\0'
assert message_type == 0x02
return challenge, flags, ntlm_data[targetinfo_offset:targetinfo_offset+targetinfo_len]
def generateChallengeResponseV2(password, user, server_challenge, server_info, domain = '', client_challenge = None):
client_timestamp = '\0' * 8
if not client_challenge:
client_challenge = ''
for i in range(0, 8):
client_challenge += chr(random.getrandbits(8))
assert len(client_challenge) == 8
d = MD4()
d.update(password.encode('UTF-16LE'))
ntlm_hash = d.digest() # The NT password hash
response_key = hmac.new(ntlm_hash, (user.upper() + domain).encode('UTF-16LE')).digest() # The NTLMv2 password hash. In [MS-NLMP], this is the result of NTOWFv2 and LMOWFv2 functions
temp = '\x01\x01' + '\0'*6 + client_timestamp + client_challenge + '\0'*4 + server_info
ntproofstr = hmac.new(response_key, server_challenge + temp).digest()
nt_challenge_response = ntproofstr + temp
lm_challenge_response = hmac.new(response_key, server_challenge + client_challenge).digest() + client_challenge
session_key = hmac.new(response_key, ntproofstr).digest()
return nt_challenge_response, lm_challenge_response, session_key
################
# NTLMv1 Methods
################
def expandDesKey(key):
"""Expand the key from a 7-byte password key into a 8-byte DES key"""
s = chr(((ord(key[0]) >> 1) & 0x7f) << 1)
s = s + chr(((ord(key[0]) & 0x01) << 6 | ((ord(key[1]) >> 2) & 0x3f)) << 1)
s = s + chr(((ord(key[1]) & 0x03) << 5 | ((ord(key[2]) >> 3) & 0x1f)) << 1)
s = s + chr(((ord(key[2]) & 0x07) << 4 | ((ord(key[3]) >> 4) & 0x0f)) << 1)
s = s + chr(((ord(key[3]) & 0x0f) << 3 | ((ord(key[4]) >> 5) & 0x07)) << 1)
s = s + chr(((ord(key[4]) & 0x1f) << 2 | ((ord(key[5]) >> 6) & 0x03)) << 1)
s = s + chr(((ord(key[5]) & 0x3f) << 1 | ((ord(key[6]) >> 7) & 0x01)) << 1)
s = s + chr((ord(key[6]) & 0x7f) << 1)
return s
def DESL(K, D):
"""
References:
===========
- http://ubiqx.org/cifs/SMB.html (2.8.3.4)
- [MS-NLMP]: Section 6
"""
d1 = des(expandDesKey(K[0:7]))
d2 = des(expandDesKey(K[7:14]))
d3 = des(expandDesKey(K[14:16] + '\0' * 5))
return d1.encrypt(D) + d2.encrypt(D) + d3.encrypt(D)
def generateChallengeResponseV1(password, server_challenge, has_extended_security = False, client_challenge = None):
"""
Generate a NTLMv1 response
@param password: User password string
@param server_challange: A 8-byte challenge string sent from the server
@param has_extended_security: A boolean value indicating whether NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY flag is enabled in the NTLM negFlag
@param client_challenge: A 8-byte string representing client challenge. If None, it will be generated randomly if needed by the response generation
@return: a tuple of ( NT challenge response string, LM challenge response string )
References:
===========
- http://ubiqx.org/cifs/SMB.html (2.8.3.3 and 2.8.3.4)
- [MS-NLMP]: 3.3.1
"""
_password = (password.upper() + '\0' * 14)[:14]
d1 = des(expandDesKey(_password[:7]))
d2 = des(expandDesKey(_password[7:]))
lm_response_key = d1.encrypt("KGS!@#$%") + d2.encrypt("KGS!@#$%") # LM password hash. In [MS-NLMP], this is the result of LMOWFv1 function
d = MD4()
d.update(password.encode('UTF-16LE'))
nt_response_key = d.digest() # In [MS-NLMP], this is the result of NTOWFv1 function
if has_extended_security:
if not client_challenge:
client_challenge = ''
for i in range(0, 8):
client_challenge += chr(random.getrandbits(8))
assert len(client_challenge) == 8
lm_challenge_response = client_challenge + '\0'*16
nt_challenge_response = DESL(nt_response_key, MD5(server_challenge + client_challenge).digest()[0:8])
else:
nt_challenge_response = DESL(nt_response_key, server_challenge) # The result after DESL is the NT response
lm_challenge_response = DESL(lm_response_key, server_challenge) # The result after DESL is the LM response
d = MD4()
d.update(nt_response_key)
session_key = d.digest()
return nt_challenge_response, lm_challenge_response, session_key
+367
View File
@@ -0,0 +1,367 @@
"""
This module implements security descriptors, and the partial structures
used in them, as specified in [MS-DTYP].
"""
import struct
# Security descriptor control flags
# [MS-DTYP]: 2.4.6
SECURITY_DESCRIPTOR_OWNER_DEFAULTED = 0x0001
SECURITY_DESCRIPTOR_GROUP_DEFAULTED = 0x0002
SECURITY_DESCRIPTOR_DACL_PRESENT = 0x0004
SECURITY_DESCRIPTOR_DACL_DEFAULTED = 0x0008
SECURITY_DESCRIPTOR_SACL_PRESENT = 0x0010
SECURITY_DESCRIPTOR_SACL_DEFAULTED = 0x0020
SECURITY_DESCRIPTOR_SERVER_SECURITY = 0x0040
SECURITY_DESCRIPTOR_DACL_TRUSTED = 0x0080
SECURITY_DESCRIPTOR_DACL_COMPUTED_INHERITANCE_REQUIRED = 0x0100
SECURITY_DESCRIPTOR_SACL_COMPUTED_INHERITANCE_REQUIRED = 0x0200
SECURITY_DESCRIPTOR_DACL_AUTO_INHERITED = 0x0400
SECURITY_DESCRIPTOR_SACL_AUTO_INHERITED = 0x0800
SECURITY_DESCRIPTOR_DACL_PROTECTED = 0x1000
SECURITY_DESCRIPTOR_SACL_PROTECTED = 0x2000
SECURITY_DESCRIPTOR_RM_CONTROL_VALID = 0x4000
SECURITY_DESCRIPTOR_SELF_RELATIVE = 0x8000
# ACE types
# [MS-DTYP]: 2.4.4.1
ACE_TYPE_ACCESS_ALLOWED = 0x00
ACE_TYPE_ACCESS_DENIED = 0x01
ACE_TYPE_SYSTEM_AUDIT = 0x02
ACE_TYPE_SYSTEM_ALARM = 0x03
ACE_TYPE_ACCESS_ALLOWED_COMPOUND = 0x04
ACE_TYPE_ACCESS_ALLOWED_OBJECT = 0x05
ACE_TYPE_ACCESS_DENIED_OBJECT = 0x06
ACE_TYPE_SYSTEM_AUDIT_OBJECT = 0x07
ACE_TYPE_SYSTEM_ALARM_OBJECT = 0x08
ACE_TYPE_ACCESS_ALLOWED_CALLBACK = 0x09
ACE_TYPE_ACCESS_DENIED_CALLBACK = 0x0A
ACE_TYPE_ACCESS_ALLOWED_CALLBACK_OBJECT = 0x0B
ACE_TYPE_ACCESS_DENIED_CALLBACK_OBJECT = 0x0C
ACE_TYPE_SYSTEM_AUDIT_CALLBACK = 0x0D
ACE_TYPE_SYSTEM_ALARM_CALLBACK = 0x0E
ACE_TYPE_SYSTEM_AUDIT_CALLBACK_OBJECT = 0x0F
ACE_TYPE_SYSTEM_ALARM_CALLBACK_OBJECT = 0x10
ACE_TYPE_SYSTEM_MANDATORY_LABEL = 0x11
ACE_TYPE_SYSTEM_RESOURCE_ATTRIBUTE = 0x12
ACE_TYPE_SYSTEM_SCOPED_POLICY_ID = 0x13
# ACE flags
# [MS-DTYP]: 2.4.4.1
ACE_FLAG_OBJECT_INHERIT = 0x01
ACE_FLAG_CONTAINER_INHERIT = 0x02
ACE_FLAG_NO_PROPAGATE_INHERIT = 0x04
ACE_FLAG_INHERIT_ONLY = 0x08
ACE_FLAG_INHERITED = 0x10
ACE_FLAG_SUCCESSFUL_ACCESS = 0x40
ACE_FLAG_FAILED_ACCESS = 0x80
# Pre-defined well-known SIDs
# [MS-DTYP]: 2.4.2.4
SID_NULL = "S-1-0-0"
SID_EVERYONE = "S-1-1-0"
SID_LOCAL = "S-1-2-0"
SID_CONSOLE_LOGON = "S-1-2-1"
SID_CREATOR_OWNER = "S-1-3-0"
SID_CREATOR_GROUP = "S-1-3-1"
SID_OWNER_SERVER = "S-1-3-2"
SID_GROUP_SERVER = "S-1-3-3"
SID_OWNER_RIGHTS = "S-1-3-4"
SID_NT_AUTHORITY = "S-1-5"
SID_DIALUP = "S-1-5-1"
SID_NETWORK = "S-1-5-2"
SID_BATCH = "S-1-5-3"
SID_INTERACTIVE = "S-1-5-4"
SID_SERVICE = "S-1-5-6"
SID_ANONYMOUS = "S-1-5-7"
SID_PROXY = "S-1-5-8"
SID_ENTERPRISE_DOMAIN_CONTROLLERS = "S-1-5-9"
SID_PRINCIPAL_SELF = "S-1-5-10"
SID_AUTHENTICATED_USERS = "S-1-5-11"
SID_RESTRICTED_CODE = "S-1-5-12"
SID_TERMINAL_SERVER_USER = "S-1-5-13"
SID_REMOTE_INTERACTIVE_LOGON = "S-1-5-14"
SID_THIS_ORGANIZATION = "S-1-5-15"
SID_IUSR = "S-1-5-17"
SID_LOCAL_SYSTEM = "S-1-5-18"
SID_LOCAL_SERVICE = "S-1-5-19"
SID_NETWORK_SERVICE = "S-1-5-20"
SID_COMPOUNDED_AUTHENTICATION = "S-1-5-21-0-0-0-496"
SID_CLAIMS_VALID = "S-1-5-21-0-0-0-497"
SID_BUILTIN_ADMINISTRATORS = "S-1-5-32-544"
SID_BUILTIN_USERS = "S-1-5-32-545"
SID_BUILTIN_GUESTS = "S-1-5-32-546"
SID_POWER_USERS = "S-1-5-32-547"
SID_ACCOUNT_OPERATORS = "S-1-5-32-548"
SID_SERVER_OPERATORS = "S-1-5-32-549"
SID_PRINTER_OPERATORS = "S-1-5-32-550"
SID_BACKUP_OPERATORS = "S-1-5-32-551"
SID_REPLICATOR = "S-1-5-32-552"
SID_ALIAS_PREW2KCOMPACC = "S-1-5-32-554"
SID_REMOTE_DESKTOP = "S-1-5-32-555"
SID_NETWORK_CONFIGURATION_OPS = "S-1-5-32-556"
SID_INCOMING_FOREST_TRUST_BUILDERS = "S-1-5-32-557"
SID_PERFMON_USERS = "S-1-5-32-558"
SID_PERFLOG_USERS = "S-1-5-32-559"
SID_WINDOWS_AUTHORIZATION_ACCESS_GROUP = "S-1-5-32-560"
SID_TERMINAL_SERVER_LICENSE_SERVERS = "S-1-5-32-561"
SID_DISTRIBUTED_COM_USERS = "S-1-5-32-562"
SID_IIS_IUSRS = "S-1-5-32-568"
SID_CRYPTOGRAPHIC_OPERATORS = "S-1-5-32-569"
SID_EVENT_LOG_READERS = "S-1-5-32-573"
SID_CERTIFICATE_SERVICE_DCOM_ACCESS = "S-1-5-32-574"
SID_RDS_REMOTE_ACCESS_SERVERS = "S-1-5-32-575"
SID_RDS_ENDPOINT_SERVERS = "S-1-5-32-576"
SID_RDS_MANAGEMENT_SERVERS = "S-1-5-32-577"
SID_HYPER_V_ADMINS = "S-1-5-32-578"
SID_ACCESS_CONTROL_ASSISTANCE_OPS = "S-1-5-32-579"
SID_REMOTE_MANAGEMENT_USERS = "S-1-5-32-580"
SID_WRITE_RESTRICTED_CODE = "S-1-5-33"
SID_NTLM_AUTHENTICATION = "S-1-5-64-10"
SID_SCHANNEL_AUTHENTICATION = "S-1-5-64-14"
SID_DIGEST_AUTHENTICATION = "S-1-5-64-21"
SID_THIS_ORGANIZATION_CERTIFICATE = "S-1-5-65-1"
SID_NT_SERVICE = "S-1-5-80"
SID_USER_MODE_DRIVERS = "S-1-5-84-0-0-0-0-0"
SID_LOCAL_ACCOUNT = "S-1-5-113"
SID_LOCAL_ACCOUNT_AND_MEMBER_OF_ADMINISTRATORS_GROUP = "S-1-5-114"
SID_OTHER_ORGANIZATION = "S-1-5-1000"
SID_ALL_APP_PACKAGES = "S-1-15-2-1"
SID_ML_UNTRUSTED = "S-1-16-0"
SID_ML_LOW = "S-1-16-4096"
SID_ML_MEDIUM = "S-1-16-8192"
SID_ML_MEDIUM_PLUS = "S-1-16-8448"
SID_ML_HIGH = "S-1-16-12288"
SID_ML_SYSTEM = "S-1-16-16384"
SID_ML_PROTECTED_PROCESS = "S-1-16-20480"
SID_AUTHENTICATION_AUTHORITY_ASSERTED_IDENTITY = "S-1-18-1"
SID_SERVICE_ASSERTED_IDENTITY = "S-1-18-2"
SID_FRESH_PUBLIC_KEY_IDENTITY = "S-1-18-3"
SID_KEY_TRUST_IDENTITY = "S-1-18-4"
SID_KEY_PROPERTY_MFA = "S-1-18-5"
SID_KEY_PROPERTY_ATTESTATION = "S-1-18-6"
class SID(object):
"""
A Windows security identifier. Represents a single principal, such a
user or a group, as a sequence of numbers consisting of the revision,
identifier authority, and a variable-length list of subauthorities.
See [MS-DTYP]: 2.4.2
"""
def __init__(self, revision, identifier_authority, subauthorities):
#: Revision, should always be 1.
self.revision = revision
#: An integer representing the identifier authority.
self.identifier_authority = identifier_authority
#: A list of integers representing all subauthorities.
self.subauthorities = subauthorities
def __str__(self):
"""
String representation, as specified in [MS-DTYP]: 2.4.2.1
"""
if self.identifier_authority >= 2**32:
id_auth = '%#x' % (self.identifier_authority,)
else:
id_auth = self.identifier_authority
auths = [self.revision, id_auth] + self.subauthorities
return 'S-' + '-'.join(str(subauth) for subauth in auths)
def __repr__(self):
return 'SID(%r)' % (str(self),)
@classmethod
def from_bytes(cls, data, return_tail=False):
revision, subauth_count = struct.unpack('<BB', data[:2])
identifier_authority = struct.unpack('>Q', '\x00\x00' + data[2:8])[0]
subauth_data = data[8:]
subauthorities = [struct.unpack('<L', subauth_data[4 * i : 4 * (i+1)])[0]
for i in range(subauth_count)]
sid = cls(revision, identifier_authority, subauthorities)
if return_tail:
return sid, subauth_data[4 * subauth_count :]
return sid
class ACE(object):
"""
Represents a single access control entry.
See [MS-DTYP]: 2.4.4
"""
HEADER_FORMAT = '<BBH'
def __init__(self, type_, flags, mask, sid, additional_data):
#: An integer representing the type of the ACE. One of the
#: ``ACE_TYPE_*`` constants. Corresponds to the ``AceType`` field
#: from [MS-DTYP] 2.4.4.1.
self.type = type_
#: An integer bitmask with ACE flags, corresponds to the
#: ``AceFlags`` field.
self.flags = flags
#: An integer representing the ``ACCESS_MASK`` as specified in
#: [MS-DTYP] 2.4.3.
self.mask = mask
#: The :class:`SID` of a trustee.
self.sid = sid
#: A dictionary of additional fields present in the ACE, depending
#: on the type. The following fields can be present:
#:
#: * ``flags``
#: * ``object_type``
#: * ``inherited_object_type``
#: * ``application_data``
#: * ``attribute_data``
self.additional_data = additional_data
def __repr__(self):
return "ACE(type=%#04x, flags=%#04x, mask=%#010x, sid=%s)" % (
self.type, self.flags, self.mask, self.sid,
)
@property
def isInheritOnly(self):
"""Convenience property which indicates if this ACE is inherit
only, meaning that it doesn't apply to the object itself."""
return bool(self.flags & ACE_FLAG_INHERIT_ONLY)
@classmethod
def from_bytes(cls, data):
header_size = struct.calcsize(cls.HEADER_FORMAT)
header = data[:header_size]
type_, flags, size = struct.unpack(cls.HEADER_FORMAT, header)
assert len(data) >= size
body = data[header_size:size]
additional_data = {}
# In all ACE types, the mask immediately follows the header.
mask = struct.unpack('<I', body[:4])[0]
body = body[4:]
# All OBJECT-type ACEs contain additional flags, and two GUIDs as
# the following fields.
if type_ in (ACE_TYPE_ACCESS_ALLOWED_OBJECT,
ACE_TYPE_ACCESS_DENIED_OBJECT,
ACE_TYPE_ACCESS_ALLOWED_CALLBACK_OBJECT,
ACE_TYPE_ACCESS_DENIED_CALLBACK_OBJECT,
ACE_TYPE_SYSTEM_AUDIT_OBJECT,
ACE_TYPE_SYSTEM_AUDIT_CALLBACK_OBJECT):
additional_data['flags'] = struct.unpack('<I', body[:4])[0]
additional_data['object_type'] = body[4:20]
additional_data['inherited_object_type'] = body[20:36]
body = body[36:]
# Then the SID in all types.
sid, body = SID.from_bytes(body, return_tail=True)
# CALLBACK-type ACEs (and for some obscure reason,
# SYSTEM_AUDIT_OBJECT) have a final tail of application data.
if type_ in (ACE_TYPE_ACCESS_ALLOWED_CALLBACK,
ACE_TYPE_ACCESS_DENIED_CALLBACK,
ACE_TYPE_ACCESS_ALLOWED_CALLBACK_OBJECT,
ACE_TYPE_ACCESS_DENIED_CALLBACK_OBJECT,
ACE_TYPE_SYSTEM_AUDIT_OBJECT,
ACE_TYPE_SYSTEM_AUDIT_CALLBACK,
ACE_TYPE_SYSTEM_AUDIT_CALLBACK_OBJECT):
additional_data['application_data'] = body
# SYSTEM_RESOURCE_ATTRIBUTE ACEs have a tail of attribute data.
if type_ == ACE_TYPE_SYSTEM_RESOURCE_ATTRIBUTE:
additional_data['attribute_data'] = body
return cls(type_, flags, mask, sid, additional_data)
class ACL(object):
"""
Access control list, encapsulating a sequence of access control
entries.
See [MS-DTYP]: 2.4.5
"""
HEADER_FORMAT = '<BBHHH'
def __init__(self, revision, aces):
#: Integer value of the revision.
self.revision = revision
#: List of :class:`ACE` instances.
self.aces = aces
def __repr__(self):
return "ACL(%r)" % (self.aces,)
@classmethod
def from_bytes(cls, data):
revision = None
aces = []
header_size = struct.calcsize(cls.HEADER_FORMAT)
header, remaining = data[:header_size], data[header_size:]
revision, sbz1, size, count, sbz2 = struct.unpack(cls.HEADER_FORMAT, header)
assert len(data) >= size
for i in range(count):
ace_size = struct.unpack('<H', remaining[2:4])[0]
ace_data, remaining = remaining[:ace_size], remaining[ace_size:]
aces.append(ACE.from_bytes(ace_data))
return cls(revision, aces)
class SecurityDescriptor(object):
"""
Represents a security descriptor.
See [MS-DTYP]: 2.4.6
"""
HEADER_FORMAT = '<BBHIIII'
def __init__(self, flags, owner, group, dacl, sacl):
#: Integer bitmask of control flags. Corresponds to the
#: ``Control`` field in [MS-DTYP] 2.4.6.
self.flags = flags
#: Instance of :class:`SID` representing the owner user.
self.owner = owner
#: Instance of :class:`SID` representing the owner group.
self.group = group
#: Instance of :class:`ACL` representing the discretionary access
#: control list, which specifies access restrictions of an object.
self.dacl = dacl
#: Instance of :class:`ACL` representing the system access control
#: list, which specifies audit logging of an object.
self.sacl = sacl
@classmethod
def from_bytes(cls, data):
owner = None
group = None
dacl = None
sacl = None
header = data[:struct.calcsize(cls.HEADER_FORMAT)]
(revision, sbz1, flags, owner_offset, group_offset, sacl_offset,
dacl_offset) = struct.unpack(cls.HEADER_FORMAT, header)
assert revision == 1
assert flags & SECURITY_DESCRIPTOR_SELF_RELATIVE
for offset in (owner_offset, group_offset, sacl_offset, dacl_offset):
assert 0 <= offset < len(data)
if owner_offset:
owner = SID.from_bytes(data[owner_offset:])
if group_offset:
group = SID.from_bytes(data[group_offset:])
if dacl_offset:
dacl = ACL.from_bytes(data[dacl_offset:])
if sacl_offset:
sacl = ACL.from_bytes(data[sacl_offset:])
return cls(flags, owner, group, dacl, sacl)
+136
View File
@@ -0,0 +1,136 @@
from pyasn1.type import tag, univ, namedtype, namedval, constraint
from pyasn1.codec.der import encoder, decoder
__all__ = [ 'generateNegotiateSecurityBlob', 'generateAuthSecurityBlob', 'decodeChallengeSecurityBlob', 'decodeAuthResponseSecurityBlob' ]
class UnsupportedSecurityProvider(Exception): pass
class BadSecurityBlobError(Exception): pass
def generateNegotiateSecurityBlob(ntlm_data):
mech_token = univ.OctetString(ntlm_data).subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))
mech_types = MechTypeList().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))
mech_types.setComponentByPosition(0, univ.ObjectIdentifier('1.3.6.1.4.1.311.2.2.10'))
n = NegTokenInit().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))
n.setComponentByName('mechTypes', mech_types)
n.setComponentByName('mechToken', mech_token)
nt = NegotiationToken()
nt.setComponentByName('negTokenInit', n)
ct = ContextToken()
ct.setComponentByName('thisMech', univ.ObjectIdentifier('1.3.6.1.5.5.2'))
ct.setComponentByName('innerContextToken', nt)
return encoder.encode(ct)
def generateAuthSecurityBlob(ntlm_data):
response_token = univ.OctetString(ntlm_data).subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))
n = NegTokenTarg().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1))
n.setComponentByName('responseToken', response_token)
nt = NegotiationToken()
nt.setComponentByName('negTokenTarg', n)
return encoder.encode(nt)
def decodeChallengeSecurityBlob(data):
try:
d, _ = decoder.decode(data, asn1Spec = NegotiationToken())
nt = d.getComponentByName('negTokenTarg')
token = nt.getComponentByName('responseToken')
if not token:
raise BadSecurityBlobError('NTLMSSP_CHALLENGE security blob does not contain responseToken field')
provider_oid = nt.getComponentByName('supportedMech')
if provider_oid and str(provider_oid) != '1.3.6.1.4.1.311.2.2.10': # This OID is defined in [MS-NLMP]: 1.9
raise UnsupportedSecurityProvider('Security provider "%s" is not supported by pysmb' % str(provider_oid))
result = nt.getComponentByName('negResult')
return int(result), str(token)
except Exception, ex:
raise BadSecurityBlobError(str(ex))
def decodeAuthResponseSecurityBlob(data):
try:
d, _ = decoder.decode(data, asn1Spec = NegotiationToken())
nt = d.getComponentByName('negTokenTarg')
result = nt.getComponentByName('negResult')
return int(result)
except Exception, ex:
raise BadSecurityBlobError(str(ex))
#
# GSS-API ASN.1 (RFC2478 section 3.2.1)
#
RESULT_ACCEPT_COMPLETED = 0
RESULT_ACCEPT_INCOMPLETE = 1
RESULT_REJECT = 2
class NegResultEnumerated(univ.Enumerated):
namedValues = namedval.NamedValues(
( 'accept_completed', 0 ),
( 'accept_incomplete', 1 ),
( 'reject', 2 )
)
subtypeSpec = univ.Enumerated.subtypeSpec + constraint.SingleValueConstraint(0, 1, 2)
class MechTypeList(univ.SequenceOf):
componentType = univ.ObjectIdentifier()
class ContextFlags(univ.BitString):
namedValues = namedval.NamedValues(
( 'delegFlag', 0 ),
( 'mutualFlag', 1 ),
( 'replayFlag', 2 ),
( 'sequenceFlag', 3 ),
( 'anonFlag', 4 ),
( 'confFlag', 5 ),
( 'integFlag', 6 )
)
class NegTokenInit(univ.Sequence):
componentType = namedtype.NamedTypes(
namedtype.OptionalNamedType('mechTypes', MechTypeList().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))),
namedtype.OptionalNamedType('reqFlags', ContextFlags().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1))),
namedtype.OptionalNamedType('mechToken', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 2))),
namedtype.OptionalNamedType('mechListMIC', univ.OctetString().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 3)))
)
class NegTokenTarg(univ.Sequence):
componentType = namedtype.NamedTypes(
namedtype.OptionalNamedType('negResult', NegResultEnumerated().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))),
namedtype.OptionalNamedType('supportedMech', univ.ObjectIdentifier().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1))),
namedtype.OptionalNamedType('responseToken', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 2))),
namedtype.OptionalNamedType('mechListMIC', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 3)))
)
class NegotiationToken(univ.Choice):
componentType = namedtype.NamedTypes(
namedtype.NamedType('negTokenInit', NegTokenInit().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))),
namedtype.NamedType('negTokenTarg', NegTokenTarg().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1)))
)
class ContextToken(univ.Sequence):
tagSet = univ.Sequence.tagSet.tagImplicitly(tag.Tag(tag.tagClassApplication, tag.tagFormatConstructed, 0))
componentType = namedtype.NamedTypes(
namedtype.NamedType('thisMech', univ.ObjectIdentifier()),
namedtype.NamedType('innerContextToken', NegotiationToken())
)
+115
View File
@@ -0,0 +1,115 @@
# Bitmask for Flags field in SMB2 message header
SMB2_FLAGS_SERVER_TO_REDIR = 0x01
SMB2_FLAGS_ASYNC_COMMAND = 0x02
SMB2_FLAGS_RELATED_OPERATIONS = 0x04
SMB2_FLAGS_SIGNED = 0x08
SMB2_FLAGS_DFS_OPERATIONS = 0x10000000
# Values for Command field in SMB2 message header
SMB2_COM_NEGOTIATE = 0x0000
SMB2_COM_SESSION_SETUP = 0x0001
SMB2_COM_LOGOFF = 0x0002
SMB2_COM_TREE_CONNECT = 0x0003
SMB2_COM_TREE_DISCONNECT = 0x0004
SMB2_COM_CREATE = 0x0005
SMB2_COM_CLOSE = 0x0006
SMB2_COM_FLUSH = 0x0007
SMB2_COM_READ = 0x0008
SMB2_COM_WRITE = 0x0009
SMB2_COM_LOCK = 0x000A
SMB2_COM_IOCTL = 0x000B
SMB2_COM_CANCEL = 0x000C
SMB2_COM_ECHO = 0x000D
SMB2_COM_QUERY_DIRECTORY = 0x000E
SMB2_COM_CHANGE_NOTIFY = 0x000F
SMB2_COM_QUERY_INFO = 0x0010
SMB2_COM_SET_INFO = 0x0011
SMB2_COM_OPLOCK_BREAK = 0x0012
SMB2_COMMAND_NAMES = {
0x0000: 'SMB2_COM_NEGOTIATE',
0x0001: 'SMB2_COM_SESSION_SETUP',
0x0002: 'SMB2_COM_LOGOFF',
0x0003: 'SMB2_COM_TREE_CONNECT',
0x0004: 'SMB2_COM_TREE_DISCONNECT',
0x0005: 'SMB2_COM_CREATE',
0x0006: 'SMB2_COM_CLOSE',
0x0007: 'SMB2_COM_FLUSH',
0x0008: 'SMB2_COM_READ',
0x0009: 'SMB2_COM_WRITE',
0x000A: 'SMB2_COM_LOCK',
0x000B: 'SMB2_COM_IOCTL',
0x000C: 'SMB2_COM_CANCEL',
0x000D: 'SMB2_COM_ECHO',
0x000E: 'SMB2_COM_QUERY_DIRECTORY',
0x000F: 'SMB2_COM_CHANGE_NOTIFY',
0x0010: 'SMB2_COM_QUERY_INFO',
0x0011: 'SMB2_COM_SET_INFO',
0x0012: 'SMB2_COM_OPLOCK_BREAK',
}
# Values for dialect_revision field in SMB2NegotiateResponse class
SMB2_DIALECT_2 = 0x0202 # 2.0.2 - First SMB2 version
SMB2_DIALECT_21 = 0x0210 # 2.1 - Windows 7
SMB2_DIALET_30 = 0x0300 # 3.0 - Windows 8
SMB2_DIALECT_302 = 0x0302 # 3.0.2 - Windows 8.1
SMB2_DIALECT_311 = 0x0311 # 3.1.1 - Windows 10
SMB2_DIALECT_2ALL = 0x02FF # Wildcard (for negotiation only)
# Bit mask for SecurityMode field in SMB2NegotiateResponse class
SMB2_NEGOTIATE_SIGNING_ENABLED = 0x0001
SMB2_NEGOTIATE_SIGNING_REQUIRED = 0x0002
# Values for ShareType field in SMB2TreeConnectResponse class
SMB2_SHARE_TYPE_DISK = 0x01
SMB2_SHARE_TYPE_PIPE = 0x02
SMB2_SHARE_TYPE_PRINTER = 0x03
# Bitmask for Capabilities in SMB2TreeConnectResponse class
SMB2_SHARE_CAP_DFS = 0x0008
# SMB 2.1 / 3 Capabilities flags
SMB2_GLOBAL_CAP_DFS = 0x01
SMB2_GLOBAL_CAP_LEASING = 0x02
SMB2_GLOBAL_CAP_LARGE_MTU = 0x04
SMB2_GLOBAL_CAP_MULTI_CHANNEL = 0x08
SMB2_GLOBAL_CAP_PERSISTENT_HANDLES = 0x10
SMB2_GLOBAL_CAP_DIRECTORY_LEASING = 0x20
SMB2_GLOBAL_CAP_ENCRYPTION = 0x40
# Values for OpLockLevel field in SMB2CreateRequest class
SMB2_OPLOCK_LEVEL_NONE = 0x00
SMB2_OPLOCK_LEVEL_II = 0x01
SMB2_OPLOCK_LEVEL_EXCLUSIVE = 0x08
SMB2_OPLOCK_LEVEL_BATCH = 0x09
SMB2_OPLOCK_LEVEL_LEASE = 0xFF
# Values for FileAttributes field in SMB2CreateRequest class
# The values are defined in [MS-FSCC] 2.6
SMB2_FILE_ATTRIBUTE_ARCHIVE = 0x0020
SMB2_FILE_ATTRIBUTE_COMPRESSED = 0x0800
SMB2_FILE_ATTRIBUTE_DIRECTORY = 0x0010
SMB2_FILE_ATTRIBUTE_ENCRYPTED = 0x4000
SMB2_FILE_ATTRIBUTE_HIDDEN = 0x0002
SMB2_FILE_ATTRIBUTE_NORMAL = 0x0080
SMB2_FILE_ATTRIBUTE_NOTINDEXED = 0x2000
SMB2_FILE_ATTRIBUTE_OFFLINE = 0x1000
SMB2_FILE_ATTRIBUTE_READONLY = 0x0001
SMB2_FILE_ATTRIBUTE_SPARSE = 0x0200
SMB2_FILE_ATTRIBUTE_SYSTEM = 0x0004
SMB2_FILE_ATTRIBUTE_TEMPORARY = 0x0100
# Values for CreateAction field in SMB2CreateResponse class
SMB2_FILE_SUPERCEDED = 0x00
SMB2_FILE_OPENED = 0x01
SMB2_FILE_CREATED = 0x02
SMB2_FILE_OVERWRITTEN = 0x03
# Values for InfoType field in SMB2QueryInfoRequest class
SMB2_INFO_FILE = 0x01
SMB2_INFO_FILESYSTEM = 0x02
SMB2_INFO_SECURITY = 0x03
SMB2_INFO_QUOTA = 0x04
File diff suppressed because it is too large Load Diff
+257
View File
@@ -0,0 +1,257 @@
# Values for Command field in SMB message header
SMB_COM_CREATE_DIRECTORY = 0x00
SMB_COM_DELETE_DIRECTORY = 0x01
SMB_COM_CLOSE = 0x04
SMB_COM_DELETE = 0x06
SMB_COM_RENAME = 0x07
SMB_COM_TRANSACTION = 0x25
SMB_COM_ECHO = 0x2B
SMB_COM_OPEN_ANDX = 0x2D
SMB_COM_READ_ANDX = 0x2E
SMB_COM_WRITE_ANDX = 0x2F
SMB_COM_TRANSACTION2 = 0x32
SMB_COM_NEGOTIATE = 0x72
SMB_COM_SESSION_SETUP_ANDX = 0x73
SMB_COM_TREE_CONNECT_ANDX = 0x75
SMB_COM_NT_TRANSACT = 0xA0
SMB_COM_NT_CREATE_ANDX = 0xA2
SMB_COMMAND_NAMES = {
0x00: 'SMB_COM_CREATE_DIRECTORY',
0x01: 'SMB_COM_DELETE_DIRECTORY',
0x04: 'SMB_COM_CLOSE',
0x06: 'SMB_COM_DELETE',
0x25: 'SMB_COM_TRANSACTION',
0x2B: 'SMB_COM_ECHO',
0x2D: 'SMB_COM_OPEN_ANDX',
0x2E: 'SMB_COM_READ_ANDX',
0x2F: 'SMB_COM_WRITE_ANDX',
0x32: 'SMB_COM_TRANSACTION2',
0x72: 'SMB_COM_NEGOTIATE',
0x73: 'SMB_COM_SESSION_SETUP_ANDX',
0x75: 'SMB_COM_TREE_CONNECT_ANDX',
0xA0: 'SMB_COM_NT_TRANSACT',
0xA2: 'SMB_COM_NT_CREATE_ANDX',
}
# Bitmask for Flags field in SMB message header
SMB_FLAGS_LOCK_AND_READ_OK = 0x01 # LANMAN1.0
SMB_FLAGS_BUF_AVAIL = 0x02 # LANMAN1.0, Obsolete
SMB_FLAGS_CASE_INSENSITIVE = 0x08 # LANMAN1.0, Obsolete
SMB_FLAGS_CANONICALIZED_PATHS = 0x10 # LANMAN1.0, Obsolete
SMB_FLAGS_OPLOCK = 0x20 # LANMAN1.0, Obsolete
SMB_FLAGS_OPBATCH = 0x40 # LANMAN1.0, Obsolete
SMB_FLAGS_REPLY = 0x80 # LANMAN1.0
# Bitmask for Flags2 field in SMB message header
SMB_FLAGS2_LONG_NAMES = 0x0001 # LANMAN2.0
SMB_FLAGS2_EAS = 0x0002 # LANMAN1.2
SMB_FLAGS2_SMB_SECURITY_SIGNATURE = 0x0004 # NT LANMAN
SMB_FLAGS2_IS_LONG_NAME = 0x0040 # NT LANMAN
SMB_FLAGS2_DFS = 0x1000 # NT LANMAN
SMB_FLAGS2_REPARSE_PATH = 0x0400 #
SMB_FLAGS2_EXTENDED_SECURITY = 0x0800 #
SMB_FLAGS2_PAGING_IO = 0x2000 # NT LANMAN
SMB_FLAGS2_NT_STATUS = 0x4000 # NT LANMAN
SMB_FLAGS2_UNICODE = 0x8000 # NT LANMAN
# Bitmask for Capabilities field in SMB_COM_SESSION_SETUP_ANDX response
# [MS-SMB]: 2.2.4.5.2.1 (Capabilities field)
CAP_RAW_MODE = 0x01
CAP_MPX_MODE = 0x02
CAP_UNICODE = 0x04
CAP_LARGE_FILES = 0x08
CAP_NT_SMBS = 0x10
CAP_RPC_REMOTE_APIS = 0x20
CAP_STATUS32 = 0x40
CAP_LEVEL_II_OPLOCKS = 0x80
CAP_LOCK_AND_READ = 0x0100
CAP_NT_FIND = 0x0200
CAP_DFS = 0x1000
CAP_INFOLEVEL_PASSTHRU = 0x2000
CAP_LARGE_READX = 0x4000
CAP_LARGE_WRITEX = 0x8000
CAP_LWIO = 0x010000
CAP_UNIX = 0x800000
CAP_COMPRESSED = 0x02000000
CAP_DYNAMIC_REAUTH = 0x20000000
CAP_PERSISTENT_HANDLES = 0x40000000
CAP_EXTENDED_SECURITY = 0x80000000
# Value for Action field in SMB_COM_SESSION_SETUP_ANDX response
SMB_SETUP_GUEST = 0x0001
SMB_SETUP_USE_LANMAN_KEY = 0X0002
# Bitmask for SecurityMode field in SMB_COM_NEGOTIATE response
NEGOTIATE_USER_SECURITY = 0x01
NEGOTIATE_ENCRYPT_PASSWORDS = 0x02
NEGOTIATE_SECURITY_SIGNATURES_ENABLE = 0x04
NEGOTIATE_SECURITY_SIGNATURES_REQUIRE = 0x08
# Available constants for Service field in SMB_COM_TREE_CONNECT_ANDX request
# [MS-CIFS]: 2.2.4.55.1 (Service field)
SERVICE_PRINTER = 'LPT1:'
SERVICE_NAMED_PIPE = 'IPC'
SERVICE_COMM = 'COMM'
SERVICE_ANY = '?????'
# Bitmask for Flags field in SMB_COM_NT_CREATE_ANDX request
# [MS-CIFS]: 2.2.4.64.1
# [MS-SMB]: 2.2.4.9.1
NT_CREATE_REQUEST_OPLOCK = 0x02
NT_CREATE_REQUEST_OPBATCH = 0x04
NT_CREATE_OPEN_TARGET_DIR = 0x08
NT_CREATE_REQUEST_EXTENDED_RESPONSE = 0x10 # Defined in [MS-SMB]: 2.2.4.9.1
# Bitmask for DesiredAccess field in SMB_COM_NT_CREATE_ANDX request
# and SMB2CreateRequest class
# Also used for MaximalAccess field in SMB2TreeConnectResponse class
# [MS-CIFS]: 2.2.4.64.1
# [MS-SMB2]: 2.2.13.1.1
FILE_READ_DATA = 0x01
FILE_WRITE_DATA = 0X02
FILE_APPEND_DATA = 0x04
FILE_READ_EA = 0x08
FILE_WRITE_EA = 0x10
FILE_EXECUTE = 0x20
FILE_DELETE_CHILD = 0x40
FILE_READ_ATTRIBUTES = 0x80
FILE_WRITE_ATTRIBUTES = 0x0100
DELETE = 0x010000
READ_CONTROL = 0x020000
WRITE_DAC = 0x040000
WRITE_OWNER = 0x080000
SYNCHRONIZE = 0x100000
ACCESS_SYSTEM_SECURITY = 0x01000000
MAXIMUM_ALLOWED = 0x02000000
GENERIC_ALL = 0x10000000
GENERIC_EXECUTE = 0x20000000
GENERIC_WRITE = 0x40000000
GENERIC_READ = 0x80000000L
# SMB_EXT_FILE_ATTR bitmask ([MS-CIFS]: 2.2.1.2.3)
# Includes extensions defined in [MS-SMB] 2.2.1.2.1
# Bitmask for FileAttributes field in SMB_COM_NT_CREATE_ANDX request ([MS-CIFS]: 2.2.4.64.1)
# Also used for FileAttributes field in SMB2CreateRequest class ([MS-SMB2]: 2.2.13)
ATTR_READONLY = 0x01
ATTR_HIDDEN = 0x02
ATTR_SYSTEM = 0x04
ATTR_DIRECTORY = 0x10
ATTR_ARCHIVE = 0x20
ATTR_NORMAL = 0x80
ATTR_TEMPORARY = 0x0100
ATTR_SPARSE = 0x0200
ATTR_REPARSE_POINT = 0x0400
ATTR_COMPRESSED = 0x0800
ATTR_OFFLINE = 0x1000
ATTR_NOT_CONTENT_INDEXED = 0x2000
ATTR_ENCRYPTED = 0x4000
POSIX_SEMANTICS = 0x01000000
BACKUP_SEMANTICS = 0x02000000
DELETE_ON_CLOSE = 0x04000000
SEQUENTIAL_SCAN = 0x08000000
RANDOM_ACCESS = 0x10000000
NO_BUFFERING = 0x20000000
WRITE_THROUGH = 0x80000000
# Bitmask for ShareAccess field in SMB_COM_NT_CREATE_ANDX request
# and SMB2CreateRequest class
# [MS-CIFS]: 2.2.4.64.1
# [MS-SMB2]: 2.2.13
FILE_SHARE_NONE = 0x00
FILE_SHARE_READ = 0x01
FILE_SHARE_WRITE = 0x02
FILE_SHARE_DELETE = 0x04
# Values for CreateDisposition field in SMB_COM_NT_CREATE_ANDX request
# and SMB2CreateRequest class
# [MS-CIFS]: 2.2.4.64.1
# [MS-SMB2]: 2.2.13
FILE_SUPERSEDE = 0x00
FILE_OPEN = 0x01
FILE_CREATE = 0x02
FILE_OPEN_IF = 0x03
FILE_OVERWRITE = 0x04
FILE_OVERWRITE_IF = 0x05
# Bitmask for CreateOptions field in SMB_COM_NT_CREATE_ANDX request
# and SMB2CreateRequest class
# [MS-CIFS]: 2.2.4.64.1
# [MS-SMB2]: 2.2.13
FILE_DIRECTORY_FILE = 0x01
FILE_WRITE_THROUGH = 0x02
FILE_SEQUENTIAL_ONLY = 0x04
FILE_NO_INTERMEDIATE_BUFFERING = 0x08
FILE_SYNCHRONOUS_IO_ALERT = 0x10
FILE_SYNCHRONOUS_IO_NONALERT = 0x20
FILE_NON_DIRECTORY_FILE = 0x40
FILE_CREATE_TREE_CONNECTION = 0x80
FILE_COMPLETE_IF_OPLOCKED = 0x0100
FILE_NO_EA_KNOWLEDGE = 0x0200
FILE_OPEN_FOR_RECOVERY = 0x0400
FILE_RANDOM_ACCESS = 0x0800
FILE_DELETE_ON_CLOSE = 0x1000
FILE_OPEN_BY_FILE_ID = 0x2000
FILE_OPEN_FOR_BACKUP_INTENT = 0x4000
FILE_NO_COMPRESSION = 0x8000
FILE_RESERVE_OPFILTER = 0x100000
FILE_OPEN_NO_RECALL = 0x400000
FILE_OPEN_FOR_FREE_SPACE_QUERY = 0x800000
# Values for ImpersonationLevel field in SMB_COM_NT_CREATE_ANDX request
# and SMB2CreateRequest class
# For interpretations about these values, refer to [MS-WSO] and [MSDN-IMPERS]
# [MS-CIFS]: 2.2.4.64.1
# [MS-SMB]: 2.2.4.9.1
# [MS-SMB2]: 2.2.13
SEC_ANONYMOUS = 0x00
SEC_IDENTIFY = 0x01
SEC_IMPERSONATE = 0x02
SEC_DELEGATION = 0x03 # Defined in [MS-SMB]: 2.2.4.9.1
# Values for SecurityFlags field in SMB_COM_NT_CREATE_ANDX request
# [MS-CIFS]: 2.2.4.64.1
SMB_SECURITY_CONTEXT_TRACKING = 0x01
SMB_SECURITY_EFFECTIVE_ONLY = 0x02
# Bitmask for Flags field in SMB_COM_TRANSACTION2 request
# [MS-CIFS]: 2.2.4.46.1
DISCONNECT_TID = 0x01
NO_RESPONSE = 0x02
# Bitmask for basic file attributes
# [MS-CIFS]: 2.2.1.2.4
SMB_FILE_ATTRIBUTE_NORMAL = 0x00
SMB_FILE_ATTRIBUTE_READONLY = 0x01
SMB_FILE_ATTRIBUTE_HIDDEN = 0x02
SMB_FILE_ATTRIBUTE_SYSTEM = 0x04
SMB_FILE_ATTRIBUTE_VOLUME = 0x08 # Unsupported for listPath() operations
SMB_FILE_ATTRIBUTE_DIRECTORY = 0x10
SMB_FILE_ATTRIBUTE_ARCHIVE = 0x20
# SMB_FILE_ATTRIBUTE_INCL_NORMAL is a special placeholder to include normal files for
# with other search attributes for listPath() operations. It is not defined in the MS-CIFS specs.
SMB_FILE_ATTRIBUTE_INCL_NORMAL = 0x10000
# Do not use the following values for listPath() operations as they are not supported for SMB2
SMB_SEARCH_ATTRIBUTE_READONLY = 0x0100
SMB_SEARCH_ATTRIBUTE_HIDDEN = 0x0200
SMB_SEARCH_ATTRIBUTE_SYSTEM = 0x0400
SMB_SEARCH_ATTRIBUTE_DIRECTORY = 0x1000
SMB_SEARCH_ATTRIBUTE_ARCHIVE = 0x2000
# Bitmask for OptionalSupport field in SMB_COM_TREE_CONNECT_ANDX response
SMB_TREE_CONNECTX_SUPPORT_SEARCH = 0x0001
SMB_TREE_CONNECTX_SUPPORT_DFS = 0x0002
# Bitmask for security information fields, specified as
# AdditionalInformation in SMB2
# [MS-SMB]: 2.2.7.4
# [MS-SMB2]: 2.2.37
OWNER_SECURITY_INFORMATION = 0x00000001
GROUP_SECURITY_INFORMATION = 0x00000002
DACL_SECURITY_INFORMATION = 0x00000004
SACL_SECURITY_INFORMATION = 0x00000008
LABEL_SECURITY_INFORMATION = 0x00000010
ATTRIBUTE_SECURITY_INFORMATION = 0x00000020
SCOPE_SECURITY_INFORMATION = 0x00000040
BACKUP_SECURITY_INFORMATION = 0x00010000
File diff suppressed because it is too large Load Diff
+12
View File
@@ -0,0 +1,12 @@
md4.py and U32.py
Both modules downloaded from http://www.oocities.org/rozmanov/python/md4.html.
Licensed under LGPL
pyDes.py 2.0.0
Downloaded from http://twhiteman.netfirms.com/des.html
Licensed under public domain
sha256.py
Downloaded from http://xbmc-addons.googlecode.com/svn-history/r1686/trunk/scripts/OpenSubtitles_OSD/resources/lib/sha256.py
Licensed under MIT
+148
View File
@@ -0,0 +1,148 @@
# U32.py implements 32-bit unsigned int class for Python
# Version 1.0
# Copyright (C) 2001-2002 Dmitry Rozmanov
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# e-mail: dima@xenon.spb.ru
#
#====================================================================
C = 0x1000000000L
#--------------------------------------------------------------------
def norm(n):
return n & 0xFFFFFFFFL
#====================================================================
class U32:
v = 0L
#--------------------------------------------------------------------
def __init__(self, value = 0):
self.v = C + norm(abs(long(value)))
#--------------------------------------------------------------------
def set(self, value = 0):
self.v = C + norm(abs(long(value)))
#--------------------------------------------------------------------
def __repr__(self):
return hex(norm(self.v))
#--------------------------------------------------------------------
def __long__(self): return long(norm(self.v))
#--------------------------------------------------------------------
def __int__(self): return int(norm(self.v))
#--------------------------------------------------------------------
def __chr__(self): return chr(norm(self.v))
#--------------------------------------------------------------------
def __add__(self, b):
r = U32()
r.v = C + norm(self.v + b.v)
return r
#--------------------------------------------------------------------
def __sub__(self, b):
r = U32()
if self.v < b.v:
r.v = C + norm(0x100000000L - (b.v - self.v))
else: r.v = C + norm(self.v - b.v)
return r
#--------------------------------------------------------------------
def __mul__(self, b):
r = U32()
r.v = C + norm(self.v * b.v)
return r
#--------------------------------------------------------------------
def __div__(self, b):
r = U32()
r.v = C + (norm(self.v) / norm(b.v))
return r
#--------------------------------------------------------------------
def __mod__(self, b):
r = U32()
r.v = C + (norm(self.v) % norm(b.v))
return r
#--------------------------------------------------------------------
def __neg__(self): return U32(self.v)
#--------------------------------------------------------------------
def __pos__(self): return U32(self.v)
#--------------------------------------------------------------------
def __abs__(self): return U32(self.v)
#--------------------------------------------------------------------
def __invert__(self):
r = U32()
r.v = C + norm(~self.v)
return r
#--------------------------------------------------------------------
def __lshift__(self, b):
r = U32()
r.v = C + norm(self.v << b)
return r
#--------------------------------------------------------------------
def __rshift__(self, b):
r = U32()
r.v = C + (norm(self.v) >> b)
return r
#--------------------------------------------------------------------
def __and__(self, b):
r = U32()
r.v = C + norm(self.v & b.v)
return r
#--------------------------------------------------------------------
def __or__(self, b):
r = U32()
r.v = C + norm(self.v | b.v)
return r
#--------------------------------------------------------------------
def __xor__(self, b):
r = U32()
r.v = C + norm(self.v ^ b.v)
return r
#--------------------------------------------------------------------
def __not__(self):
return U32(not norm(self.v))
#--------------------------------------------------------------------
def truth(self):
return norm(self.v)
#--------------------------------------------------------------------
def __cmp__(self, b):
if norm(self.v) > norm(b.v): return 1
elif norm(self.v) < norm(b.v): return -1
else: return 0
#--------------------------------------------------------------------
def __nonzero__(self):
return norm(self.v)
+3
View File
@@ -0,0 +1,3 @@
def convertFILETIMEtoEpoch(t):
return (t - 116444736000000000L) / 10000000.0;
+254
View File
@@ -0,0 +1,254 @@
# md4.py implements md4 hash class for Python
# Version 1.0
# Copyright (C) 2001-2002 Dmitry Rozmanov
#
# based on md4.c from "the Python Cryptography Toolkit, version 1.0.0
# Copyright (C) 1995, A.M. Kuchling"
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# e-mail: dima@xenon.spb.ru
#
#====================================================================
# MD4 validation data
md4_test= [
('', 0x31d6cfe0d16ae931b73c59d7e0c089c0L),
("a", 0xbde52cb31de33e46245e05fbdbd6fb24L),
("abc", 0xa448017aaf21d8525fc10ae87aa6729dL),
("message digest", 0xd9130a8164549fe818874806e1c7014bL),
("abcdefghijklmnopqrstuvwxyz", 0xd79e1c308aa5bbcdeea8ed63df412da9L),
("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
0x043f8582f241db351ce627e153e7f0e4L),
("12345678901234567890123456789012345678901234567890123456789012345678901234567890",
0xe33b4ddc9c38f2199c3e7b164fcc0536L),
]
#====================================================================
from U32 import U32
#--------------------------------------------------------------------
class MD4:
A = None
B = None
C = None
D = None
count, len1, len2 = None, None, None
buf = []
#-----------------------------------------------------
def __init__(self):
self.A = U32(0x67452301L)
self.B = U32(0xefcdab89L)
self.C = U32(0x98badcfeL)
self.D = U32(0x10325476L)
self.count, self.len1, self.len2 = U32(0L), U32(0L), U32(0L)
self.buf = [0x00] * 64
#-----------------------------------------------------
def __repr__(self):
r = 'A = %s, \nB = %s, \nC = %s, \nD = %s.\n' % (self.A.__repr__(), self.B.__repr__(), self.C.__repr__(), self.D.__repr__())
r = r + 'count = %s, \nlen1 = %s, \nlen2 = %s.\n' % (self.count.__repr__(), self.len1.__repr__(), self.len2.__repr__())
for i in range(4):
for j in range(16):
r = r + '%4s ' % hex(self.buf[i+j])
r = r + '\n'
return r
#-----------------------------------------------------
def make_copy(self):
dest = new()
dest.len1 = self.len1
dest.len2 = self.len2
dest.A = self.A
dest.B = self.B
dest.C = self.C
dest.D = self.D
dest.count = self.count
for i in range(self.count):
dest.buf[i] = self.buf[i]
return dest
#-----------------------------------------------------
def update(self, str):
buf = []
for i in str: buf.append(ord(i))
ilen = U32(len(buf))
# check if the first length is out of range
# as the length is measured in bits then multiplay it by 8
if (long(self.len1 + (ilen << 3)) < long(self.len1)):
self.len2 = self.len2 + U32(1)
self.len1 = self.len1 + (ilen << 3)
self.len2 = self.len2 + (ilen >> 29)
L = U32(0)
bufpos = 0
while (long(ilen) > 0):
if (64 - long(self.count)) < long(ilen): L = U32(64 - long(self.count))
else: L = ilen
for i in range(int(L)): self.buf[i + int(self.count)] = buf[i + bufpos]
self.count = self.count + L
ilen = ilen - L
bufpos = bufpos + int(L)
if (long(self.count) == 64L):
self.count = U32(0L)
X = []
i = 0
for j in range(16):
X.append(U32(self.buf[i]) + (U32(self.buf[i+1]) << 8) + \
(U32(self.buf[i+2]) << 16) + (U32(self.buf[i+3]) << 24))
i = i + 4
A = self.A
B = self.B
C = self.C
D = self.D
A = f1(A,B,C,D, 0, 3, X)
D = f1(D,A,B,C, 1, 7, X)
C = f1(C,D,A,B, 2,11, X)
B = f1(B,C,D,A, 3,19, X)
A = f1(A,B,C,D, 4, 3, X)
D = f1(D,A,B,C, 5, 7, X)
C = f1(C,D,A,B, 6,11, X)
B = f1(B,C,D,A, 7,19, X)
A = f1(A,B,C,D, 8, 3, X)
D = f1(D,A,B,C, 9, 7, X)
C = f1(C,D,A,B,10,11, X)
B = f1(B,C,D,A,11,19, X)
A = f1(A,B,C,D,12, 3, X)
D = f1(D,A,B,C,13, 7, X)
C = f1(C,D,A,B,14,11, X)
B = f1(B,C,D,A,15,19, X)
A = f2(A,B,C,D, 0, 3, X)
D = f2(D,A,B,C, 4, 5, X)
C = f2(C,D,A,B, 8, 9, X)
B = f2(B,C,D,A,12,13, X)
A = f2(A,B,C,D, 1, 3, X)
D = f2(D,A,B,C, 5, 5, X)
C = f2(C,D,A,B, 9, 9, X)
B = f2(B,C,D,A,13,13, X)
A = f2(A,B,C,D, 2, 3, X)
D = f2(D,A,B,C, 6, 5, X)
C = f2(C,D,A,B,10, 9, X)
B = f2(B,C,D,A,14,13, X)
A = f2(A,B,C,D, 3, 3, X)
D = f2(D,A,B,C, 7, 5, X)
C = f2(C,D,A,B,11, 9, X)
B = f2(B,C,D,A,15,13, X)
A = f3(A,B,C,D, 0, 3, X)
D = f3(D,A,B,C, 8, 9, X)
C = f3(C,D,A,B, 4,11, X)
B = f3(B,C,D,A,12,15, X)
A = f3(A,B,C,D, 2, 3, X)
D = f3(D,A,B,C,10, 9, X)
C = f3(C,D,A,B, 6,11, X)
B = f3(B,C,D,A,14,15, X)
A = f3(A,B,C,D, 1, 3, X)
D = f3(D,A,B,C, 9, 9, X)
C = f3(C,D,A,B, 5,11, X)
B = f3(B,C,D,A,13,15, X)
A = f3(A,B,C,D, 3, 3, X)
D = f3(D,A,B,C,11, 9, X)
C = f3(C,D,A,B, 7,11, X)
B = f3(B,C,D,A,15,15, X)
self.A = self.A + A
self.B = self.B + B
self.C = self.C + C
self.D = self.D + D
#-----------------------------------------------------
def digest(self):
res = [0x00] * 16
s = [0x00] * 8
padding = [0x00] * 64
padding[0] = 0x80
padlen, oldlen1, oldlen2 = U32(0), U32(0), U32(0)
temp = self.make_copy()
oldlen1 = temp.len1
oldlen2 = temp.len2
if (56 <= long(self.count)): padlen = U32(56 - long(self.count) + 64)
else: padlen = U32(56 - long(self.count))
temp.update(int_array2str(padding[:int(padlen)]))
s[0]= (oldlen1) & U32(0xFF)
s[1]=((oldlen1) >> 8) & U32(0xFF)
s[2]=((oldlen1) >> 16) & U32(0xFF)
s[3]=((oldlen1) >> 24) & U32(0xFF)
s[4]= (oldlen2) & U32(0xFF)
s[5]=((oldlen2) >> 8) & U32(0xFF)
s[6]=((oldlen2) >> 16) & U32(0xFF)
s[7]=((oldlen2) >> 24) & U32(0xFF)
temp.update(int_array2str(s))
res[ 0]= temp.A & U32(0xFF)
res[ 1]=(temp.A >> 8) & U32(0xFF)
res[ 2]=(temp.A >> 16) & U32(0xFF)
res[ 3]=(temp.A >> 24) & U32(0xFF)
res[ 4]= temp.B & U32(0xFF)
res[ 5]=(temp.B >> 8) & U32(0xFF)
res[ 6]=(temp.B >> 16) & U32(0xFF)
res[ 7]=(temp.B >> 24) & U32(0xFF)
res[ 8]= temp.C & U32(0xFF)
res[ 9]=(temp.C >> 8) & U32(0xFF)
res[10]=(temp.C >> 16) & U32(0xFF)
res[11]=(temp.C >> 24) & U32(0xFF)
res[12]= temp.D & U32(0xFF)
res[13]=(temp.D >> 8) & U32(0xFF)
res[14]=(temp.D >> 16) & U32(0xFF)
res[15]=(temp.D >> 24) & U32(0xFF)
return int_array2str(res)
#====================================================================
# helpers
def F(x, y, z): return (((x) & (y)) | ((~x) & (z)))
def G(x, y, z): return (((x) & (y)) | ((x) & (z)) | ((y) & (z)))
def H(x, y, z): return ((x) ^ (y) ^ (z))
def ROL(x, n): return (((x) << n) | ((x) >> (32-n)))
def f1(a, b, c, d, k, s, X): return ROL(a + F(b, c, d) + X[k], s)
def f2(a, b, c, d, k, s, X): return ROL(a + G(b, c, d) + X[k] + U32(0x5a827999L), s)
def f3(a, b, c, d, k, s, X): return ROL(a + H(b, c, d) + X[k] + U32(0x6ed9eba1L), s)
#--------------------------------------------------------------------
# helper function
def int_array2str(array):
str = ''
for i in array:
str = str + chr(i)
return str
#--------------------------------------------------------------------
# To be able to use md4.new() instead of md4.MD4()
new = MD4
+852
View File
@@ -0,0 +1,852 @@
#############################################################################
# Documentation #
#############################################################################
# Author: Todd Whiteman
# Date: 16th March, 2009
# Verion: 2.0.0
# License: Public Domain - free to do as you wish
# Homepage: http://twhiteman.netfirms.com/des.html
#
# This is a pure python implementation of the DES encryption algorithm.
# It's pure python to avoid portability issues, since most DES
# implementations are programmed in C (for performance reasons).
#
# Triple DES class is also implemented, utilising the DES base. Triple DES
# is either DES-EDE3 with a 24 byte key, or DES-EDE2 with a 16 byte key.
#
# See the README.txt that should come with this python module for the
# implementation methods used.
#
# Thanks to:
# * David Broadwell for ideas, comments and suggestions.
# * Mario Wolff for pointing out and debugging some triple des CBC errors.
# * Santiago Palladino for providing the PKCS5 padding technique.
# * Shaya for correcting the PAD_PKCS5 triple des CBC errors.
#
"""A pure python implementation of the DES and TRIPLE DES encryption algorithms.
Class initialization
--------------------
pyDes.des(key, [mode], [IV], [pad], [padmode])
pyDes.triple_des(key, [mode], [IV], [pad], [padmode])
key -> Bytes containing the encryption key. 8 bytes for DES, 16 or 24 bytes
for Triple DES
mode -> Optional argument for encryption type, can be either
pyDes.ECB (Electronic Code Book) or pyDes.CBC (Cypher Block Chaining)
IV -> Optional Initial Value bytes, must be supplied if using CBC mode.
Length must be 8 bytes.
pad -> Optional argument, set the pad character (PAD_NORMAL) to use during
all encrypt/decrpt operations done with this instance.
padmode -> Optional argument, set the padding mode (PAD_NORMAL or PAD_PKCS5)
to use during all encrypt/decrpt operations done with this instance.
I recommend to use PAD_PKCS5 padding, as then you never need to worry about any
padding issues, as the padding can be removed unambiguously upon decrypting
data that was encrypted using PAD_PKCS5 padmode.
Common methods
--------------
encrypt(data, [pad], [padmode])
decrypt(data, [pad], [padmode])
data -> Bytes to be encrypted/decrypted
pad -> Optional argument. Only when using padmode of PAD_NORMAL. For
encryption, adds this characters to the end of the data block when
data is not a multiple of 8 bytes. For decryption, will remove the
trailing characters that match this pad character from the last 8
bytes of the unencrypted data block.
padmode -> Optional argument, set the padding mode, must be one of PAD_NORMAL
or PAD_PKCS5). Defaults to PAD_NORMAL.
Example
-------
from pyDes import *
data = "Please encrypt my data"
k = des("DESCRYPT", CBC, "\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5)
# For Python3, you'll need to use bytes, i.e.:
# data = b"Please encrypt my data"
# k = des(b"DESCRYPT", CBC, b"\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5)
d = k.encrypt(data)
print "Encrypted: %r" % d
print "Decrypted: %r" % k.decrypt(d)
assert k.decrypt(d, padmode=PAD_PKCS5) == data
See the module source (pyDes.py) for more examples of use.
You can also run the pyDes.py file without and arguments to see a simple test.
Note: This code was not written for high-end systems needing a fast
implementation, but rather a handy portable solution with small usage.
"""
import sys
# _pythonMajorVersion is used to handle Python2 and Python3 differences.
_pythonMajorVersion = sys.version_info[0]
# Modes of crypting / cyphering
ECB = 0
CBC = 1
# Modes of padding
PAD_NORMAL = 1
PAD_PKCS5 = 2
# PAD_PKCS5: is a method that will unambiguously remove all padding
# characters after decryption, when originally encrypted with
# this padding mode.
# For a good description of the PKCS5 padding technique, see:
# http://www.faqs.org/rfcs/rfc1423.html
# The base class shared by des and triple des.
class _baseDes(object):
def __init__(self, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL):
if IV:
IV = self._guardAgainstUnicode(IV)
if pad:
pad = self._guardAgainstUnicode(pad)
self.block_size = 8
# Sanity checking of arguments.
if pad and padmode == PAD_PKCS5:
raise ValueError("Cannot use a pad character with PAD_PKCS5")
if IV and len(IV) != self.block_size:
raise ValueError("Invalid Initial Value (IV), must be a multiple of " + str(self.block_size) + " bytes")
# Set the passed in variables
self._mode = mode
self._iv = IV
self._padding = pad
self._padmode = padmode
def getKey(self):
"""getKey() -> bytes"""
return self.__key
def setKey(self, key):
"""Will set the crypting key for this object."""
key = self._guardAgainstUnicode(key)
self.__key = key
def getMode(self):
"""getMode() -> pyDes.ECB or pyDes.CBC"""
return self._mode
def setMode(self, mode):
"""Sets the type of crypting mode, pyDes.ECB or pyDes.CBC"""
self._mode = mode
def getPadding(self):
"""getPadding() -> bytes of length 1. Padding character."""
return self._padding
def setPadding(self, pad):
"""setPadding() -> bytes of length 1. Padding character."""
if pad is not None:
pad = self._guardAgainstUnicode(pad)
self._padding = pad
def getPadMode(self):
"""getPadMode() -> pyDes.PAD_NORMAL or pyDes.PAD_PKCS5"""
return self._padmode
def setPadMode(self, mode):
"""Sets the type of padding mode, pyDes.PAD_NORMAL or pyDes.PAD_PKCS5"""
self._padmode = mode
def getIV(self):
"""getIV() -> bytes"""
return self._iv
def setIV(self, IV):
"""Will set the Initial Value, used in conjunction with CBC mode"""
if not IV or len(IV) != self.block_size:
raise ValueError("Invalid Initial Value (IV), must be a multiple of " + str(self.block_size) + " bytes")
IV = self._guardAgainstUnicode(IV)
self._iv = IV
def _padData(self, data, pad, padmode):
# Pad data depending on the mode
if padmode is None:
# Get the default padding mode.
padmode = self.getPadMode()
if pad and padmode == PAD_PKCS5:
raise ValueError("Cannot use a pad character with PAD_PKCS5")
if padmode == PAD_NORMAL:
if len(data) % self.block_size == 0:
# No padding required.
return data
if not pad:
# Get the default padding.
pad = self.getPadding()
if not pad:
raise ValueError("Data must be a multiple of " + str(self.block_size) + " bytes in length. Use padmode=PAD_PKCS5 or set the pad character.")
data += (self.block_size - (len(data) % self.block_size)) * pad
elif padmode == PAD_PKCS5:
pad_len = 8 - (len(data) % self.block_size)
if _pythonMajorVersion < 3:
data += pad_len * chr(pad_len)
else:
data += bytes([pad_len] * pad_len)
return data
def _unpadData(self, data, pad, padmode):
# Unpad data depending on the mode.
if not data:
return data
if pad and padmode == PAD_PKCS5:
raise ValueError("Cannot use a pad character with PAD_PKCS5")
if padmode is None:
# Get the default padding mode.
padmode = self.getPadMode()
if padmode == PAD_NORMAL:
if not pad:
# Get the default padding.
pad = self.getPadding()
if pad:
data = data[:-self.block_size] + \
data[-self.block_size:].rstrip(pad)
elif padmode == PAD_PKCS5:
if _pythonMajorVersion < 3:
pad_len = ord(data[-1])
else:
pad_len = data[-1]
data = data[:-pad_len]
return data
def _guardAgainstUnicode(self, data):
# Only accept byte strings or ascii unicode values, otherwise
# there is no way to correctly decode the data into bytes.
if _pythonMajorVersion < 3:
if isinstance(data, unicode):
raise ValueError("pyDes can only work with bytes, not Unicode strings.")
else:
if isinstance(data, str):
# Only accept ascii unicode values.
try:
return data.encode('ascii')
except UnicodeEncodeError:
pass
raise ValueError("pyDes can only work with encoded strings, not Unicode.")
return data
#############################################################################
# DES #
#############################################################################
class des(_baseDes):
"""DES encryption/decrytpion class
Supports ECB (Electronic Code Book) and CBC (Cypher Block Chaining) modes.
pyDes.des(key,[mode], [IV])
key -> Bytes containing the encryption key, must be exactly 8 bytes
mode -> Optional argument for encryption type, can be either pyDes.ECB
(Electronic Code Book), pyDes.CBC (Cypher Block Chaining)
IV -> Optional Initial Value bytes, must be supplied if using CBC mode.
Must be 8 bytes in length.
pad -> Optional argument, set the pad character (PAD_NORMAL) to use
during all encrypt/decrpt operations done with this instance.
padmode -> Optional argument, set the padding mode (PAD_NORMAL or
PAD_PKCS5) to use during all encrypt/decrpt operations done
with this instance.
"""
# Permutation and translation tables for DES
__pc1 = [56, 48, 40, 32, 24, 16, 8,
0, 57, 49, 41, 33, 25, 17,
9, 1, 58, 50, 42, 34, 26,
18, 10, 2, 59, 51, 43, 35,
62, 54, 46, 38, 30, 22, 14,
6, 61, 53, 45, 37, 29, 21,
13, 5, 60, 52, 44, 36, 28,
20, 12, 4, 27, 19, 11, 3
]
# number left rotations of pc1
__left_rotations = [
1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
]
# permuted choice key (table 2)
__pc2 = [
13, 16, 10, 23, 0, 4,
2, 27, 14, 5, 20, 9,
22, 18, 11, 3, 25, 7,
15, 6, 26, 19, 12, 1,
40, 51, 30, 36, 46, 54,
29, 39, 50, 44, 32, 47,
43, 48, 38, 55, 33, 52,
45, 41, 49, 35, 28, 31
]
# initial permutation IP
__ip = [57, 49, 41, 33, 25, 17, 9, 1,
59, 51, 43, 35, 27, 19, 11, 3,
61, 53, 45, 37, 29, 21, 13, 5,
63, 55, 47, 39, 31, 23, 15, 7,
56, 48, 40, 32, 24, 16, 8, 0,
58, 50, 42, 34, 26, 18, 10, 2,
60, 52, 44, 36, 28, 20, 12, 4,
62, 54, 46, 38, 30, 22, 14, 6
]
# Expansion table for turning 32 bit blocks into 48 bits
__expansion_table = [
31, 0, 1, 2, 3, 4,
3, 4, 5, 6, 7, 8,
7, 8, 9, 10, 11, 12,
11, 12, 13, 14, 15, 16,
15, 16, 17, 18, 19, 20,
19, 20, 21, 22, 23, 24,
23, 24, 25, 26, 27, 28,
27, 28, 29, 30, 31, 0
]
# The (in)famous S-boxes
__sbox = [
# S1
[14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7,
0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8,
4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0,
15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13],
# S2
[15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10,
3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5,
0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15,
13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9],
# S3
[10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8,
13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1,
13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7,
1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12],
# S4
[7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15,
13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9,
10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4,
3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14],
# S5
[2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9,
14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6,
4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14,
11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3],
# S6
[12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11,
10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8,
9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6,
4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13],
# S7
[4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1,
13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6,
1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2,
6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12],
# S8
[13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7,
1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2,
7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8,
2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11],
]
# 32-bit permutation function P used on the output of the S-boxes
__p = [
15, 6, 19, 20, 28, 11,
27, 16, 0, 14, 22, 25,
4, 17, 30, 9, 1, 7,
23,13, 31, 26, 2, 8,
18, 12, 29, 5, 21, 10,
3, 24
]
# final permutation IP^-1
__fp = [
39, 7, 47, 15, 55, 23, 63, 31,
38, 6, 46, 14, 54, 22, 62, 30,
37, 5, 45, 13, 53, 21, 61, 29,
36, 4, 44, 12, 52, 20, 60, 28,
35, 3, 43, 11, 51, 19, 59, 27,
34, 2, 42, 10, 50, 18, 58, 26,
33, 1, 41, 9, 49, 17, 57, 25,
32, 0, 40, 8, 48, 16, 56, 24
]
# Type of crypting being done
ENCRYPT = 0x00
DECRYPT = 0x01
# Initialisation
def __init__(self, key, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL):
# Sanity checking of arguments.
if len(key) != 8:
raise ValueError("Invalid DES key size. Key must be exactly 8 bytes long.")
_baseDes.__init__(self, mode, IV, pad, padmode)
self.key_size = 8
self.L = []
self.R = []
self.Kn = [ [0] * 48 ] * 16 # 16 48-bit keys (K1 - K16)
self.final = []
self.setKey(key)
def setKey(self, key):
"""Will set the crypting key for this object. Must be 8 bytes."""
_baseDes.setKey(self, key)
self.__create_sub_keys()
def __String_to_BitList(self, data):
"""Turn the string data, into a list of bits (1, 0)'s"""
if _pythonMajorVersion < 3:
# Turn the strings into integers. Python 3 uses a bytes
# class, which already has this behaviour.
data = [ord(c) for c in data]
l = len(data) * 8
result = [0] * l
pos = 0
for ch in data:
i = 7
while i >= 0:
if ch & (1 << i) != 0:
result[pos] = 1
else:
result[pos] = 0
pos += 1
i -= 1
return result
def __BitList_to_String(self, data):
"""Turn the list of bits -> data, into a string"""
result = []
pos = 0
c = 0
while pos < len(data):
c += data[pos] << (7 - (pos % 8))
if (pos % 8) == 7:
result.append(c)
c = 0
pos += 1
if _pythonMajorVersion < 3:
return ''.join([ chr(c) for c in result ])
else:
return bytes(result)
def __permutate(self, table, block):
"""Permutate this block with the specified table"""
return list(map(lambda x: block[x], table))
# Transform the secret key, so that it is ready for data processing
# Create the 16 subkeys, K[1] - K[16]
def __create_sub_keys(self):
"""Create the 16 subkeys K[1] to K[16] from the given key"""
key = self.__permutate(des.__pc1, self.__String_to_BitList(self.getKey()))
i = 0
# Split into Left and Right sections
self.L = key[:28]
self.R = key[28:]
while i < 16:
j = 0
# Perform circular left shifts
while j < des.__left_rotations[i]:
self.L.append(self.L[0])
del self.L[0]
self.R.append(self.R[0])
del self.R[0]
j += 1
# Create one of the 16 subkeys through pc2 permutation
self.Kn[i] = self.__permutate(des.__pc2, self.L + self.R)
i += 1
# Main part of the encryption algorithm, the number cruncher :)
def __des_crypt(self, block, crypt_type):
"""Crypt the block of data through DES bit-manipulation"""
block = self.__permutate(des.__ip, block)
self.L = block[:32]
self.R = block[32:]
# Encryption starts from Kn[1] through to Kn[16]
if crypt_type == des.ENCRYPT:
iteration = 0
iteration_adjustment = 1
# Decryption starts from Kn[16] down to Kn[1]
else:
iteration = 15
iteration_adjustment = -1
i = 0
while i < 16:
# Make a copy of R[i-1], this will later become L[i]
tempR = self.R[:]
# Permutate R[i - 1] to start creating R[i]
self.R = self.__permutate(des.__expansion_table, self.R)
# Exclusive or R[i - 1] with K[i], create B[1] to B[8] whilst here
self.R = list(map(lambda x, y: x ^ y, self.R, self.Kn[iteration]))
B = [self.R[:6], self.R[6:12], self.R[12:18], self.R[18:24], self.R[24:30], self.R[30:36], self.R[36:42], self.R[42:]]
# Optimization: Replaced below commented code with above
#j = 0
#B = []
#while j < len(self.R):
# self.R[j] = self.R[j] ^ self.Kn[iteration][j]
# j += 1
# if j % 6 == 0:
# B.append(self.R[j-6:j])
# Permutate B[1] to B[8] using the S-Boxes
j = 0
Bn = [0] * 32
pos = 0
while j < 8:
# Work out the offsets
m = (B[j][0] << 1) + B[j][5]
n = (B[j][1] << 3) + (B[j][2] << 2) + (B[j][3] << 1) + B[j][4]
# Find the permutation value
v = des.__sbox[j][(m << 4) + n]
# Turn value into bits, add it to result: Bn
Bn[pos] = (v & 8) >> 3
Bn[pos + 1] = (v & 4) >> 2
Bn[pos + 2] = (v & 2) >> 1
Bn[pos + 3] = v & 1
pos += 4
j += 1
# Permutate the concatination of B[1] to B[8] (Bn)
self.R = self.__permutate(des.__p, Bn)
# Xor with L[i - 1]
self.R = list(map(lambda x, y: x ^ y, self.R, self.L))
# Optimization: This now replaces the below commented code
#j = 0
#while j < len(self.R):
# self.R[j] = self.R[j] ^ self.L[j]
# j += 1
# L[i] becomes R[i - 1]
self.L = tempR
i += 1
iteration += iteration_adjustment
# Final permutation of R[16]L[16]
self.final = self.__permutate(des.__fp, self.R + self.L)
return self.final
# Data to be encrypted/decrypted
def crypt(self, data, crypt_type):
"""Crypt the data in blocks, running it through des_crypt()"""
# Error check the data
if not data:
return ''
if len(data) % self.block_size != 0:
if crypt_type == des.DECRYPT: # Decryption must work on 8 byte blocks
raise ValueError("Invalid data length, data must be a multiple of " + str(self.block_size) + " bytes\n.")
if not self.getPadding():
raise ValueError("Invalid data length, data must be a multiple of " + str(self.block_size) + " bytes\n. Try setting the optional padding character")
else:
data += (self.block_size - (len(data) % self.block_size)) * self.getPadding()
# print "Len of data: %f" % (len(data) / self.block_size)
if self.getMode() == CBC:
if self.getIV():
iv = self.__String_to_BitList(self.getIV())
else:
raise ValueError("For CBC mode, you must supply the Initial Value (IV) for ciphering")
# Split the data into blocks, crypting each one seperately
i = 0
dict = {}
result = []
#cached = 0
#lines = 0
while i < len(data):
# Test code for caching encryption results
#lines += 1
#if dict.has_key(data[i:i+8]):
#print "Cached result for: %s" % data[i:i+8]
# cached += 1
# result.append(dict[data[i:i+8]])
# i += 8
# continue
block = self.__String_to_BitList(data[i:i+8])
# Xor with IV if using CBC mode
if self.getMode() == CBC:
if crypt_type == des.ENCRYPT:
block = list(map(lambda x, y: x ^ y, block, iv))
#j = 0
#while j < len(block):
# block[j] = block[j] ^ iv[j]
# j += 1
processed_block = self.__des_crypt(block, crypt_type)
if crypt_type == des.DECRYPT:
processed_block = list(map(lambda x, y: x ^ y, processed_block, iv))
#j = 0
#while j < len(processed_block):
# processed_block[j] = processed_block[j] ^ iv[j]
# j += 1
iv = block
else:
iv = processed_block
else:
processed_block = self.__des_crypt(block, crypt_type)
# Add the resulting crypted block to our list
#d = self.__BitList_to_String(processed_block)
#result.append(d)
result.append(self.__BitList_to_String(processed_block))
#dict[data[i:i+8]] = d
i += 8
# print "Lines: %d, cached: %d" % (lines, cached)
# Return the full crypted string
if _pythonMajorVersion < 3:
return ''.join(result)
else:
return bytes.fromhex('').join(result)
def encrypt(self, data, pad=None, padmode=None):
"""encrypt(data, [pad], [padmode]) -> bytes
data : Bytes to be encrypted
pad : Optional argument for encryption padding. Must only be one byte
padmode : Optional argument for overriding the padding mode.
The data must be a multiple of 8 bytes and will be encrypted
with the already specified key. Data does not have to be a
multiple of 8 bytes if the padding character is supplied, or
the padmode is set to PAD_PKCS5, as bytes will then added to
ensure the be padded data is a multiple of 8 bytes.
"""
data = self._guardAgainstUnicode(data)
if pad is not None:
pad = self._guardAgainstUnicode(pad)
data = self._padData(data, pad, padmode)
return self.crypt(data, des.ENCRYPT)
def decrypt(self, data, pad=None, padmode=None):
"""decrypt(data, [pad], [padmode]) -> bytes
data : Bytes to be encrypted
pad : Optional argument for decryption padding. Must only be one byte
padmode : Optional argument for overriding the padding mode.
The data must be a multiple of 8 bytes and will be decrypted
with the already specified key. In PAD_NORMAL mode, if the
optional padding character is supplied, then the un-encrypted
data will have the padding characters removed from the end of
the bytes. This pad removal only occurs on the last 8 bytes of
the data (last data block). In PAD_PKCS5 mode, the special
padding end markers will be removed from the data after decrypting.
"""
data = self._guardAgainstUnicode(data)
if pad is not None:
pad = self._guardAgainstUnicode(pad)
data = self.crypt(data, des.DECRYPT)
return self._unpadData(data, pad, padmode)
#############################################################################
# Triple DES #
#############################################################################
class triple_des(_baseDes):
"""Triple DES encryption/decrytpion class
This algorithm uses the DES-EDE3 (when a 24 byte key is supplied) or
the DES-EDE2 (when a 16 byte key is supplied) encryption methods.
Supports ECB (Electronic Code Book) and CBC (Cypher Block Chaining) modes.
pyDes.des(key, [mode], [IV])
key -> Bytes containing the encryption key, must be either 16 or
24 bytes long
mode -> Optional argument for encryption type, can be either pyDes.ECB
(Electronic Code Book), pyDes.CBC (Cypher Block Chaining)
IV -> Optional Initial Value bytes, must be supplied if using CBC mode.
Must be 8 bytes in length.
pad -> Optional argument, set the pad character (PAD_NORMAL) to use
during all encrypt/decrpt operations done with this instance.
padmode -> Optional argument, set the padding mode (PAD_NORMAL or
PAD_PKCS5) to use during all encrypt/decrpt operations done
with this instance.
"""
def __init__(self, key, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL):
_baseDes.__init__(self, mode, IV, pad, padmode)
self.setKey(key)
def setKey(self, key):
"""Will set the crypting key for this object. Either 16 or 24 bytes long."""
self.key_size = 24 # Use DES-EDE3 mode
if len(key) != self.key_size:
if len(key) == 16: # Use DES-EDE2 mode
self.key_size = 16
else:
raise ValueError("Invalid triple DES key size. Key must be either 16 or 24 bytes long")
if self.getMode() == CBC:
if not self.getIV():
# Use the first 8 bytes of the key
self._iv = key[:self.block_size]
if len(self.getIV()) != self.block_size:
raise ValueError("Invalid IV, must be 8 bytes in length")
self.__key1 = des(key[:8], self._mode, self._iv,
self._padding, self._padmode)
self.__key2 = des(key[8:16], self._mode, self._iv,
self._padding, self._padmode)
if self.key_size == 16:
self.__key3 = self.__key1
else:
self.__key3 = des(key[16:], self._mode, self._iv,
self._padding, self._padmode)
_baseDes.setKey(self, key)
# Override setter methods to work on all 3 keys.
def setMode(self, mode):
"""Sets the type of crypting mode, pyDes.ECB or pyDes.CBC"""
_baseDes.setMode(self, mode)
for key in (self.__key1, self.__key2, self.__key3):
key.setMode(mode)
def setPadding(self, pad):
"""setPadding() -> bytes of length 1. Padding character."""
_baseDes.setPadding(self, pad)
for key in (self.__key1, self.__key2, self.__key3):
key.setPadding(pad)
def setPadMode(self, mode):
"""Sets the type of padding mode, pyDes.PAD_NORMAL or pyDes.PAD_PKCS5"""
_baseDes.setPadMode(self, mode)
for key in (self.__key1, self.__key2, self.__key3):
key.setPadMode(mode)
def setIV(self, IV):
"""Will set the Initial Value, used in conjunction with CBC mode"""
_baseDes.setIV(self, IV)
for key in (self.__key1, self.__key2, self.__key3):
key.setIV(IV)
def encrypt(self, data, pad=None, padmode=None):
"""encrypt(data, [pad], [padmode]) -> bytes
data : bytes to be encrypted
pad : Optional argument for encryption padding. Must only be one byte
padmode : Optional argument for overriding the padding mode.
The data must be a multiple of 8 bytes and will be encrypted
with the already specified key. Data does not have to be a
multiple of 8 bytes if the padding character is supplied, or
the padmode is set to PAD_PKCS5, as bytes will then added to
ensure the be padded data is a multiple of 8 bytes.
"""
ENCRYPT = des.ENCRYPT
DECRYPT = des.DECRYPT
data = self._guardAgainstUnicode(data)
if pad is not None:
pad = self._guardAgainstUnicode(pad)
# Pad the data accordingly.
data = self._padData(data, pad, padmode)
if self.getMode() == CBC:
self.__key1.setIV(self.getIV())
self.__key2.setIV(self.getIV())
self.__key3.setIV(self.getIV())
i = 0
result = []
while i < len(data):
block = self.__key1.crypt(data[i:i+8], ENCRYPT)
block = self.__key2.crypt(block, DECRYPT)
block = self.__key3.crypt(block, ENCRYPT)
self.__key1.setIV(block)
self.__key2.setIV(block)
self.__key3.setIV(block)
result.append(block)
i += 8
if _pythonMajorVersion < 3:
return ''.join(result)
else:
return bytes.fromhex('').join(result)
else:
data = self.__key1.crypt(data, ENCRYPT)
data = self.__key2.crypt(data, DECRYPT)
return self.__key3.crypt(data, ENCRYPT)
def decrypt(self, data, pad=None, padmode=None):
"""decrypt(data, [pad], [padmode]) -> bytes
data : bytes to be encrypted
pad : Optional argument for decryption padding. Must only be one byte
padmode : Optional argument for overriding the padding mode.
The data must be a multiple of 8 bytes and will be decrypted
with the already specified key. In PAD_NORMAL mode, if the
optional padding character is supplied, then the un-encrypted
data will have the padding characters removed from the end of
the bytes. This pad removal only occurs on the last 8 bytes of
the data (last data block). In PAD_PKCS5 mode, the special
padding end markers will be removed from the data after
decrypting, no pad character is required for PAD_PKCS5.
"""
ENCRYPT = des.ENCRYPT
DECRYPT = des.DECRYPT
data = self._guardAgainstUnicode(data)
if pad is not None:
pad = self._guardAgainstUnicode(pad)
if self.getMode() == CBC:
self.__key1.setIV(self.getIV())
self.__key2.setIV(self.getIV())
self.__key3.setIV(self.getIV())
i = 0
result = []
while i < len(data):
iv = data[i:i+8]
block = self.__key3.crypt(iv, DECRYPT)
block = self.__key2.crypt(block, ENCRYPT)
block = self.__key1.crypt(block, DECRYPT)
self.__key1.setIV(iv)
self.__key2.setIV(iv)
self.__key3.setIV(iv)
result.append(block)
i += 8
if _pythonMajorVersion < 3:
data = ''.join(result)
else:
data = bytes.fromhex('').join(result)
else:
data = self.__key3.crypt(data, DECRYPT)
data = self.__key2.crypt(data, ENCRYPT)
data = self.__key1.crypt(data, DECRYPT)
return self._unpadData(data, pad, padmode)
+110
View File
@@ -0,0 +1,110 @@
#!/usr/bin/python
__author__ = 'Thomas Dixon'
__license__ = 'MIT'
import copy, struct, sys
digest_size = 32
blocksize = 1
def new(m=None):
return sha256(m)
class sha256(object):
_k = (0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2)
_h = (0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19)
_output_size = 8
blocksize = 1
block_size = 64
digest_size = 32
def __init__(self, m=None):
self._buffer = ''
self._counter = 0
if m is not None:
if type(m) is not str:
raise TypeError, '%s() argument 1 must be string, not %s' % (self.__class__.__name__, type(m).__name__)
self.update(m)
def _rotr(self, x, y):
return ((x >> y) | (x << (32-y))) & 0xFFFFFFFF
def _sha256_process(self, c):
w = [0]*64
w[0:15] = struct.unpack('!16L', c)
for i in range(16, 64):
s0 = self._rotr(w[i-15], 7) ^ self._rotr(w[i-15], 18) ^ (w[i-15] >> 3)
s1 = self._rotr(w[i-2], 17) ^ self._rotr(w[i-2], 19) ^ (w[i-2] >> 10)
w[i] = (w[i-16] + s0 + w[i-7] + s1) & 0xFFFFFFFF
a,b,c,d,e,f,g,h = self._h
for i in range(64):
s0 = self._rotr(a, 2) ^ self._rotr(a, 13) ^ self._rotr(a, 22)
maj = (a & b) ^ (a & c) ^ (b & c)
t2 = s0 + maj
s1 = self._rotr(e, 6) ^ self._rotr(e, 11) ^ self._rotr(e, 25)
ch = (e & f) ^ ((~e) & g)
t1 = h + s1 + ch + self._k[i] + w[i]
h = g
g = f
f = e
e = (d + t1) & 0xFFFFFFFF
d = c
c = b
b = a
a = (t1 + t2) & 0xFFFFFFFF
self._h = [(x+y) & 0xFFFFFFFF for x,y in zip(self._h, [a,b,c,d,e,f,g,h])]
def update(self, m):
if not m:
return
if type(m) is not str:
raise TypeError, '%s() argument 1 must be string, not %s' % (sys._getframe().f_code.co_name, type(m).__name__)
self._buffer += m
self._counter += len(m)
while len(self._buffer) >= 64:
self._sha256_process(self._buffer[:64])
self._buffer = self._buffer[64:]
def digest(self):
mdi = self._counter & 0x3F
length = struct.pack('!Q', self._counter<<3)
if mdi < 56:
padlen = 55-mdi
else:
padlen = 119-mdi
r = self.copy()
r.update('\x80'+('\x00'*padlen)+length)
return ''.join([struct.pack('!L', i) for i in r._h[:self._output_size]])
def hexdigest(self):
return self.digest().encode('hex')
def copy(self):
return copy.deepcopy(self)