#from PyKyleUtils import *
from Cryptic import *
import threading

DefInRecvTmOut = 30
EnvInfo = dict()
EnvInfo["IsTest"] = False
EnvInfo["DbgLog"] = None
EnvInfo["IsDbg"] = False
def InitDbg(fNameLog):
    EnvInfo["IsDbg"] = True
    EnvInfo["DbgLog"] = open(fNameLog, "wb")
BlkCipher = Cipheros
EncFunc = CmpxCBCCrypt
DefBlkLen = 4096
class AnyFile:
    def __init__(self, Obj):
        self.IsIter = True
        try:
            Tmp = Obj[0:len(Obj):-1]
        except:
            self.IsIter = False
        self.Obj = Obj
        self.Pos = 0
    def read(self, Num):
        if self.IsIter:
            if self.Pos >= len(self.Obj):
                return ""
            Orig = self.Pos
            self.Pos += Num
            if self.Pos > len(self.Obj):
                self.Pos = len(self.Obj)
            return self.Obj[Orig:self.Pos]
        return self.Obj.read(Num)
    def write(self, Data, KeepLast = False):
        if self.IsIter:
            if KeepLast:
                self.Obj = self.Obj[0:self.Pos] + Data + self.Obj[self.Pos:]
            else:
                self.Obj = self.Obj[0:self.Pos] + Data
            self.Pos += len(Data)
            return len(Data)
        return self.Obj.write(Data)
    def GetLen(self):
        if self.IsIter:
            return len(self.Obj)
        self.Pos = self.Obj.tell()
        self.Obj.seek(0, os.SEEK_END)
        Rtn = self.Obj.tell()
        self.Obj.seek(self.Pos)
        return Rtn
def DbgLogStr(Data, InfId):
    if EnvInfo["IsDbg"]:
        EnvInfo["DbgLog"].write(chr(0))
        EnvInfo["DbgLog"].write(chr(InfId))
        EnvInfo["DbgLog"].write(AlignStrBytesLong(long(time.time() * 1000), 8))
        EnvInfo["DbgLog"].write(AlignStrBytesLong(len(Data), 4))
        EnvInfo["DbgLog"].write(Data)
        EnvInfo["DbgLog"].flush()
def DbgLogNum(Data, InfId):
    if EnvInfo["IsDbg"]:
        EnvInfo["DbgLog"].write(chr(1))
        EnvInfo["DbgLog"].write(chr(InfId))
        EnvInfo["DbgLog"].write(AlignStrBytesLong(long(time.time() * 1000), 8))
        EnvInfo["DbgLog"].write(AlignStrBytesLong(Data, 8))
        EnvInfo["DbgLog"].flush()
DBGINF_RECV = 0
DBGINF_SEND = 1
DBGINF_EXCRECV = 2
DBGINF_EXCSEND = 3
DBGINF_OKRECV = 4
DBGINF_OKSEND = 5
DBGINF_KYRECV = 6
DBGINF_KYSEND = 7
DBGINF_EXCKYRECV = 8
DBGINF_EXCKYSEND = 9
DBGINF_OKKYRECV = 10
DBGINF_OKKYSEND = 11
DBGINF_DHXCHG = 12
MAX_ZERO = 12
class MngdSock:
    FuncNames = ["settimeout", "gettimeout", "recv", "send", "close", "IsOpen", "NumAccess", "Sock", "Timeout", "ReqClose", "PartTmOut", "Debug"]
    def __init__(self, Sock, IntervalTmOut = 4):
        self.PartTmOut = IntervalTmOut
        self.Sock = Sock
        self.IsOpen = True
        self.ReqClose = False
        self.Timeout = self.Sock.gettimeout()
        self.Sock.settimeout(self.PartTmOut)
        self.NumAccess = 0
        self.Debug = False
    def settimeout(self, TmOut):
        self.Timeout = TmOut
    def gettimeout(self):
        return self.Timeout
    def recv(self, Num):
        if not self.IsOpen or self.ReqClose:
            raise Exception("connection reset from within")
        self.NumAccess += 1
        if self.Debug: DbgLogNum(Num, DBGINF_RECV)
        self.Sock.settimeout(self.PartTmOut)
        Rtn = ""
        ZeroCount = 0
        if self.Timeout == None:
            while Num > 0 and not self.ReqClose:
                try:
                    Str = self.Sock.recv(Num)
                    Num -= len(Str)
                    if len(Str) == 0:
                        ZeroCount += 1
                    if ZeroCount > MAX_ZERO:
                        raise Exception("socket disconnected")
                    if self.Debug: DbgLogStr(Str, DBGINF_RECV)
                    Rtn += Str
                except socket.timeout as Msg:
                    if self.ReqClose:
                        self.NumAccess -= 1
                        if self.NumAccess == 0:
                            self.Sock.close()
                            self.IsOpen = False
                        if self.Debug: DbgLogStr("connection reset from within", DBGINF_EXCRECV)
                        raise Exception("connection reset from within")
                except Exception as Msg:
                    if self.Debug: DbgLogStr(str(Msg), DBGINF_EXCRECV)
                    self.NumAccess -= 1
                    raise Msg
            self.NumAccess -= 1
            if self.ReqClose:
                if self.NumAccess == 0:
                    self.Sock.close()
                    self.IsOpen = False
                if self.Debug: DbgLogStr("connection reset from within", DBGINF_EXCRECV)
                raise Exception("connection reset from within")
            if self.Debug: DbgLogStr("recv completed successfully", DBGINF_OKRECV)
            return Rtn
        TmLeft = self.Timeout
        while Num > 0 and not self.ReqClose:
            try:
                Str = self.Sock.recv(Num)
                Num -= len(Str)
                if len(Str) == 0:
                    ZeroCount += 1
                if ZeroCount > MAX_ZERO:
                    raise Exception("socket disconnected")
                if self.Debug: DbgLogStr(Str, DBGINF_RECV)
                Rtn += Str
                TmLeft = self.Timeout
            except socket.timeout as Msg:
                if self.ReqClose:
                    self.NumAccess -= 1
                    if self.NumAccess == 0:
                        self.Sock.close()
                        self.IsOpen = False
                    if self.Debug: DbgLogStr("connection reset from within", DBGINF_EXCRECV)
                    raise Exception("connection reset from within")
                TmLeft -= self.PartTmOut
                if TmLeft <= 0:
                    self.NumAccess -= 1
                    if self.Debug: DbgLogStr("socket timed out", DBGINF_EXCRECV)
                    raise Msg
            except Exception as Msg:
                self.NumAccess -= 1
                raise Msg
        self.NumAccess -= 1
        if self.ReqClose:
            if self.NumAccess == 0:
                self.Sock.close()
                self.IsOpen = False
            if self.Debug: DbgLogStr("connection reset from within", DBGINF_EXCRECV)
            raise Exception("connection reset from within")
        if self.Debug: DbgLogStr("recv completed successfully", DBGINF_OKRECV)
        return Rtn
    def send(self, Data):
        if not self.IsOpen or self.ReqClose:
            raise Exception("connection reset from within")
        self.NumAccess += 1
        self.Sock.settimeout(self.PartTmOut)
        TotSent = 0
        if self.Debug: DbgLogStr(Data, DBGINF_SEND)
        if self.Timeout == None:
            while len(Data) > 0:
                try:
                    Num = self.Sock.send(Data)
                    Data = Data[Num: ]
                    TotSent += Num
                    if self.Debug: DbgLogNum(Num, DBGINF_SEND)
                except socket.timeout as Msg:
                    if self.ReqClose:
                        self.NumAccess -= 1
                        if self.NumAccess == 0:
                            self.Sock.close()
                            self.IsOpen = False
                        if self.Debug: DbgLogStr("connection reset from within", DBGINF_EXCSEND)
                        raise Exception("connection reset from within")
                except Exception as Msg:
                    if self.Debug: DbgLogStr(str(Msg), DBGINF_EXCSEND)
                    self.NumAccess -= 1
                    raise Msg
            self.NumAccess -= 1
            if self.ReqClose:
                if self.NumAccess == 0:
                    self.Sock.close()
                    self.IsOpen = False
                if self.Debug: DbgLogStr("connection reset from within", DBGINF_EXCSEND)
                raise Exception("connection reset from within")
            if self.Debug: DbgLogStr("sent: " + str(TotSent) + " Bytes", DBGINF_OKSEND)
            return TotSent
        TmLeft = self.Timeout
        while len(Data) > 0:
            try:
                Num = self.Sock.send(Data)
                Data = Data[Num: ]
                TmLeft = self.Timeout
                TotSent += Num
            except socket.timeout as Msg:
                if self.ReqClose:
                    self.NumAccess -= 1
                    if self.NumAccess == 0:
                        self.Sock.close()
                        self.IsOpen = False
                    if self.Debug: DbgLogStr("connection reset from within", DBGINF_EXCSEND)
                    raise Exception("connection reset from within")
                TmLeft -= self.PartTmOut
                if TmLeft <= 0:
                    if self.Debug: DbgLogStr("socket timed out", DBGINF_EXCRECV)
                    self.NumAccess -= 1
                    raise Msg
            except Exception as Msg:
                if self.Debug: DbgLogStr(str(Msg), DBGINF_EXCSEND)
                self.NumAccess -= 1
                raise Msg
        self.NumAccess -= 1
        if self.ReqClose:
            if self.NumAccess == 0:
                self.Sock.close()
                self.IsOpen = False
            if self.Debug: DbgLogStr("connection reset from within", DBGINF_EXCSEND)
            raise Exception("connection reset from within")
        if self.Debug: DbgLogStr("sent: " + str(TotSent) + " Bytes", DBGINF_OKSEND)
        return TotSent
    def __getattr__(self, Attr):
        if Attr in MngdSock.FuncNames:
            return self.__getattribute__(Attr)
        return self.Sock.__getattribute__(Attr)
    def close(self):
        if self.IsOpen and self.NumAccess > 0: self.ReqClose = True
        else:
            self.IsOpen = False
            self.Sock.close()
MngdSock.FuncNames.sort()
LenProgBars = 64
def EncSendOrig(Sock, Enc, NoEncDat, EncDat, ProgBar = False, Timeout = DefInRecvTmOut, BlockLen = DefBlkLen):
    EncDat = AnyFile(EncDat)
    NoEncDat = AnyFile(NoEncDat)
    NoEncHead = GetStrBytesLong(NoEncDat.GetLen())
    EncHead = GetStrBytesLong(EncDat.GetLen())
    TotLen = NoEncDat.GetLen() + EncDat.GetLen()
    StrSend = chr((len(NoEncHead) - 1) + (len(EncHead) - 1) * 16) + NoEncHead + EncHead
    Sock.send(StrSend[0])
    Sock.send(StrSend[1:])
    Data = NoEncDat.read(BlockLen)
    TmRtn = Sock.gettimeout()
    Sock.settimeout(Timeout)
    if ProgBar:
        if IsInIdle:
            print " " * (LenProgBars + 15) + "|"
            sys.stdout.write("upload progress:")
        else:
            sys.stdout.write("UploadProgress[" + " " * LenProgBars + "]")
            sys.stdout.write("\b" * (LenProgBars + 1))
    PrevBar = 0
    NumSent1 = 0
    try:
        while len(Data) == BlockLen:
            while len(Data) > 0:
                NumSent = Sock.send(Data)
                Data = Data[NumSent:]
            NumSent1 += BlockLen
            if ProgBar:
                if PrevBar < NumSent1 * LenProgBars / TotLen:
                    Off = NumSent1 * LenProgBars / TotLen - PrevBar
                    sys.stdout.write("=" * Off)
                    PrevBar += Off
            Data = NoEncDat.read(BlockLen)
        NumSent1 += len(Data)
        while len(Data) > 0:
            NumSent = Sock.send(Data)
            Data = Data[NumSent:]
        if ProgBar:
            if PrevBar < NumSent1 * LenProgBars / TotLen:
                Off = NumSent1 * LenProgBars / TotLen - PrevBar
                sys.stdout.write("=" * Off)
                PrevBar += Off
        Data = EncDat.read(BlockLen)
        while len(Data) == BlockLen:
            Data = Enc.Crypt(Data)
            while len(Data) > 0:
                NumSent = Sock.send(Data)
                Data = Data[NumSent:]
            NumSent1 += BlockLen
            if ProgBar:
                if PrevBar < NumSent1 * LenProgBars / TotLen:
                    Off = NumSent1 * LenProgBars / TotLen - PrevBar
                    sys.stdout.write("=" * Off)
                    PrevBar += Off
            Data = EncDat.read(BlockLen)
        NumSent1 += len(Data)
        if len(Data) > 0: Data = Enc.Crypt(Data)
        while len(Data) > 0:
            NumSent = Sock.send(Data)
            Data = Data[NumSent:]
        if ProgBar:
            if PrevBar < NumSent1 * LenProgBars / TotLen:
                Off = NumSent1 * LenProgBars / TotLen - PrevBar
                sys.stdout.write("=" * Off)
                PrevBar += Off
    except socket.timeout:
        Sock.settimeout(TmRtn)
        if ProgBar: sys.stdout.write("\n")
        return -1
    except Exception as Msg:
        Sock.settimeout(TmRtn)
        if ProgBar: sys.stdout.write("\n")
        raise Msg
    Sock.settimeout(TmRtn)
    if ProgBar: sys.stdout.write("\n")
    return 0

def EncRecvOrig(Sock, Enc, NoEncDat, EncDat, ProgBar = False, Timeout = DefInRecvTmOut, BlockLen = DefBlkLen):
    NumLen = ord(Sock.recv(1))
    NoEncNumLen = NumLen % 16 + 1
    EncNumLen = NumLen / 16 + 1
    StrBytes = Sock.recv(EncNumLen + NoEncNumLen)
    NoEncLen = GetLongStrBytes(StrBytes[0:NoEncNumLen])
    EncLen = GetLongStrBytes(StrBytes[NoEncNumLen:])
    TmRtn = Sock.gettimeout()
    Sock.settimeout(Timeout)
    NumRecv = 0
    TotLen = NoEncLen + EncLen
    if ProgBar:
        if IsInIdle:
            print " " * (LenProgBars + 15) + "|"
            sys.stdout.write("download amount:")
        else:
            sys.stdout.write("DownloadAmount[" + " " * LenProgBars + "]")
            sys.stdout.write("\b" * (LenProgBars + 15))
    PrevBar = 0
    try:
        c = 0
        PartLen = NoEncLen / BlockLen
        while c < PartLen:
            Data = ""
            while len(Data) < BlockLen:
                Data += Sock.recv(BlockLen - len(Data))
            NoEncDat.write(Data)
            c += 1
            NumRecv += BlockLen
            if ProgBar:
                if PrevBar < NumRecv * LenProgBars / TotLen:
                    Off = NumRecv * LenProgBars / TotLen - PrevBar
                    sys.stdout.write("=" * Off)
                    PrevBar += Off
        Data = ""
        PartLen = NoEncLen % BlockLen
        NumRecv += PartLen
        while len(Data) < PartLen:
            Data += Sock.recv(PartLen - len(Data))
        if PartLen > 0:
            NoEncDat.write(Data)
        if ProgBar:
            if PrevBar < NumRecv * LenProgBars / TotLen:
                Off = NumRecv * LenProgBars / TotLen - PrevBar
                sys.stdout.write("=" * Off)
                PrevBar += Off
        c = 0
        PartLen = EncLen / BlockLen
        while c < PartLen:
            Data = ""
            while len(Data) < BlockLen:
                Data += Sock.recv(BlockLen - len(Data))
            EncDat.write(Enc.Crypt(Data))
            c += 1
            NumRecv += BlockLen
            if ProgBar:
                if PrevBar < NumRecv * LenProgBars / TotLen:
                    Off = NumRecv * LenProgBars / TotLen - PrevBar
                    sys.stdout.write("=" * Off)
                    PrevBar += Off
        Data = ""
        PartLen = EncLen % BlockLen
        NumRecv += PartLen
        NumGet = 0
        if Enc != None: NumGet = ((PartLen + len(Enc.Key) - 1) / len(Enc.Key)) * len(Enc.Key)
        while len(Data) < NumGet:
            Data += Sock.recv(NumGet - len(Data))
        if PartLen > 0:
            EncDat.write(Enc.Crypt(Data)[0:PartLen])
        if ProgBar:
            if PrevBar < NumRecv * LenProgBars / TotLen:
                Off = NumRecv * LenProgBars / TotLen - PrevBar
                sys.stdout.write("=" * Off)
                PrevBar += Off
    except socket.timeout:
        Sock.settimeout(TmRtn)
        if ProgBar: sys.stdout.write("\n")
        return -1
    except Exception as Msg:
        Sock.settimeout(TmRtn)
        if ProgBar: sys.stdout.write("\n")
        raise Msg
    Sock.settimeout(TmRtn)
    if ProgBar: sys.stdout.write("\n")
    return 0
def EncSendOpt(Sock, Enc, NoEncDat, EncDat, ProgBar = False, Timeout = DefInRecvTmOut, BlockLen = DefBlkLen):
    EncDat = AnyFile(EncDat)
    NoEncDat = AnyFile(NoEncDat)
    NoEncHead = GetStrBytesLong(NoEncDat.GetLen())
    EncHead = GetStrBytesLong(EncDat.GetLen())
    TotLen = NoEncDat.GetLen() + EncDat.GetLen()
    StrSend = chr((len(NoEncHead) - 1) + (len(EncHead) - 1) * 16) + NoEncHead + EncHead
    Sock.send(StrSend[0])
    Sock.send(StrSend[1:])
    Data = NoEncDat.read(BlockLen)
    TmRtn = Sock.gettimeout()
    Sock.settimeout(Timeout)
    NumSent1 = 0
    if ProgBar:
        if IsInIdle:
            print " " * (LenProgBars + 15) + "|"
            sys.stdout.write("upload progress:")
        else:
            sys.stdout.write("UploadProgress[" + " " * LenProgBars + "]")
            sys.stdout.write("\b" * (LenProgBars + 1))
    PrevBar = 0
    try:
        while len(Data) == BlockLen:
            NumSent1 += BlockLen
            Sock.send(Data)
            if ProgBar:
                if PrevBar < NumSent1 * LenProgBars / TotLen:
                    Off = NumSent1 * LenProgBars / TotLen - PrevBar
                    sys.stdout.write("=" * Off)
                    PrevBar += Off
            Data = NoEncDat.read(BlockLen)
        NumSent1 += len(Data)
        if len(Data) > 0:
            Sock.send(Data)
        if ProgBar:
            if PrevBar < NumSent1 * LenProgBars / TotLen:
                Off = NumSent1 * LenProgBars / TotLen - PrevBar
                sys.stdout.write("=" * Off)
                PrevBar += Off
        Data = EncDat.read(BlockLen)
        while len(Data) == BlockLen:
            NumSent1 += BlockLen
            Sock.send(Enc.Crypt(Data))
            if ProgBar:
                if PrevBar < NumSent1 * LenProgBars / TotLen:
                    Off = NumSent1 * LenProgBars / TotLen - PrevBar
                    sys.stdout.write("=" * Off)
                    PrevBar += Off
            Data = EncDat.read(BlockLen)
        NumSent1 += len(Data)
        if len(Data) > 0:
            Sock.send(Enc.Crypt(Data))
        if ProgBar:
            if PrevBar < NumSent1 * LenProgBars / TotLen:
                Off = NumSent1 * LenProgBars / TotLen - PrevBar
                sys.stdout.write("=" * Off)
                PrevBar += Off
    except socket.timeout:
        Sock.settimeout(TmRtn)
        if ProgBar: sys.stdout.write("\n")
        return -1
    except Exception as Msg:
        Sock.settimeout(TmRtn)
        if ProgBar: sys.stdout.write("\n")
        raise Msg
    Sock.settimeout(TmRtn)
    if ProgBar: sys.stdout.write("\n")
    return 0

def EncRecvOpt(Sock, Enc, NoEncDat, EncDat, ProgBar = False, Timeout = DefInRecvTmOut, BlockLen = DefBlkLen):
    NumLen = ord(Sock.recv(1))
    NoEncNumLen = NumLen % 16 + 1
    EncNumLen = NumLen / 16 + 1
    StrBytes = Sock.recv(EncNumLen + NoEncNumLen)
    NoEncLen = GetLongStrBytes(StrBytes[0:NoEncNumLen])
    EncLen = GetLongStrBytes(StrBytes[NoEncNumLen:])
    TmRtn = Sock.gettimeout()
    Sock.settimeout(Timeout)
    NumRecv = 0
    TotLen = NoEncLen + EncLen
    if ProgBar:
        if IsInIdle:
            print " " * (LenProgBars + 15) + "|"
            sys.stdout.write("download amount:")
        else:
            sys.stdout.write("DownloadAmount[" + " " * LenProgBars + "]")
            sys.stdout.write("\b" * (LenProgBars + 1))
    PrevBar = 0
    try:
        c = 0
        PartLen = NoEncLen / BlockLen
        while c < PartLen:
            NoEncDat.write(Sock.recv(BlockLen))
            c += 1
            NumRecv += BlockLen
            if ProgBar:
                if PrevBar < NumRecv * LenProgBars / TotLen:
                    Off = NumRecv * LenProgBars / TotLen - PrevBar
                    sys.stdout.write("=" * Off)
                    PrevBar += Off
        Data = ""
        PartLen = NoEncLen % BlockLen
        NumRecv += PartLen
        if PartLen > 0:
            NoEncDat.write(Sock.recv(PartLen))
        if ProgBar:
            if PrevBar < NumRecv * LenProgBars / TotLen:
                Off = NumRecv * LenProgBars / TotLen - PrevBar
                sys.stdout.write("=" * Off)
                PrevBar += Off
        c = 0
        PartLen = EncLen / BlockLen
        while c < PartLen:
            EncDat.write(Enc.Crypt(Sock.recv(BlockLen)))
            c += 1
            NumRecv += BlockLen
            if ProgBar:
                if PrevBar < NumRecv * LenProgBars / TotLen:
                    Off = NumRecv * LenProgBars / TotLen - PrevBar
                    sys.stdout.write("=" * Off)
                    PrevBar += Off
        PartLen = EncLen % BlockLen
        NumRecv += PartLen
        if PartLen > 0 and Enc != None:
            NumChunks = (PartLen + len(Enc.Key) - 1) / len(Enc.Key)
            StrTmp = Enc.Crypt(Sock.recv(len(Enc.Key) * NumChunks))
            EncDat.write(StrTmp[0:PartLen])
        if ProgBar:
            if PrevBar < NumRecv * LenProgBars / TotLen:
                Off = NumRecv * LenProgBars / TotLen - PrevBar
                sys.stdout.write("=" * Off)
                PrevBar += Off
    except socket.timeout:
        Sock.settimeout(TmRtn)
        if ProgBar: sys.stdout.write("\n")
        return -1
    except Exception as Msg:
        Sock.settimeout(TmRtn)
        if ProgBar: sys.stdout.write("\n")
        raise Msg
    Sock.settimeout(TmRtn)
    if ProgBar: sys.stdout.write("\n")
    return 0


class CltProt:
    def __init__(self, Serv, Enc):
        self.Sock = Serv
        self.Enc = Enc
        if self.Enc == None:
            self.Enc = [None, None]
        self.ProgBar = False
        self.TimeoutExcept = False
    def Send(self, NoEncDat, EncDat):
        if not self.TimeoutExcept: return EncSend(self.Sock, self.Enc[0], NoEncDat, EncDat, self.ProgBar)
        elif EncSend(self.Sock, self.Enc[0], NoEncDat, EncDat, self.ProgBar) == -1:
            raise Exception(-1, "socket timeout")
    def Recv(self, EncFile = None, NoEncFile = None): #returns RecvRtn, NoEnc, Enc
        EncAny = None
        if EncFile != None:
            EncAny = AnyFile(EncFile)
        else:
            EncAny = AnyFile("")
        NoEncAny = None
        if NoEncFile != None:
            NoEncAny = AnyFile(EncFile)
        else:
            NoEncAny = AnyFile("")
        Rtn = [EncRecv(self.Sock, self.Enc[1], NoEncAny, EncAny, self.ProgBar)]
        if Rtn[0] == -1 and self.TimeoutExcept:
            raise Exception(-1, "socket timeout")
        if NoEncFile == None:
            Rtn.append(NoEncAny.Obj)
        if EncFile == None:
            Rtn.append(EncAny.Obj)
        if len(Rtn) == 1: return Rtn[0]
        return Rtn
class ServProt:
    def __init__(self, Clt, Enc):
        self.Sock = Clt
        self.Enc = Enc
        if self.Enc == None:
            self.Enc = [None, None]
        self.TimeoutExcept = False
    def Send(self, NoEncDat, EncDat):
        if not self.TimeoutExcept: return EncSend(self.Sock, self.Enc[0], NoEncDat, EncDat)
        elif EncSend(self.Sock, self.Enc[0], NoEncDat, EncDat) == -1:
            raise Exception(-1, "socket timeout")
    def Recv(self, EncFile = None, NoEncFile = None): #returns RecvRtn, NoEnc, Enc
        EncAny = None
        if EncFile != None:
            EncAny = AnyFile(EncFile)
        else:
            EncAny = AnyFile("")
        NoEncAny = None
        if NoEncFile != None:
            NoEncAny = AnyFile(EncFile)
        else:
            NoEncAny = AnyFile("")
        Rtn = [EncRecv(self.Sock, self.Enc[1], NoEncAny, EncAny)]
        if Rtn[0] == -1 and self.TimeoutExcept:
            raise Exception(-1, "socket timeout")
        if NoEncFile == None:
            Rtn.append(NoEncAny.Obj)
        if EncFile == None:
            Rtn.append(EncAny.Obj)
        if len(Rtn) == 1: return Rtn[0]
        else: return Rtn
def GetAlignRSA(Key):
    return (Key[0].bit_length() - 1) / 8
def CryptVerify(Key, FuncNum, DataIn, IsUndo, RandDat):
    BtAlign = CertData.GetBtAligns[FuncNum](Key)
    if IsUndo:
        Rtn = GetAlignMsg(CertData.LstFuncs[FuncNum](DataIn, Key, True), BtAlign)
        IsMatch, Rtn = UnmixAlignStrs(Rtn, RandDat, BtAlign)
        if not IsMatch:
            raise Exception("Certification Invalid Warining", "Certificate was checked and failed. In other words the person at the other end may not be who you think that person is.", Rtn)
        else: return Rtn
    else:
        Rtn = MixAlignStrs(DataIn, RandDat, CertData.GetBtAligns[FuncNum](Key))
        return CertData.LstFuncs[FuncNum](MkAlignMsg(Rtn, BtAlign), Key, False)
#Verified Protocol
class VeriProt:
    def __init__(self, Prot, Key, FuncNum, RandLen = None):
        self.Prot = Prot
        self.Key = Key
        self.FuncNum = FuncNum
        if RandLen != None:
            self.RandDat = GetRandBytes(RandLen)
            self.Prot.Send(self.RandDat, "")
        else:
            ErrCode, self.RandDat, OtherDat = self.Prot.Recv()
            if ErrCode != 0:
                raise Exception("ConnectionError: Error code: " + str(ErrCode))
        self.BtAlignLen = CertData.GetBtAligns[FuncNum](Key) / 256 + 1
        self.MaxBtLen = CertData.GetBtAligns[FuncNum](Key) + 1
    def SendRaw(self, Data):
        self.Prot.Send(CryptVerify(self.Key, self.FuncNum, Data, False, self.RandDat), "")
    def SendLenRaw(self, Data):
        Data = AlignStrBytesLong((len(Data) + self.BtAlignLen) % self.MaxBtLen, self.BtAlignLen)
        self.Prot.Send(CryptVerify(self.Key, self.FuncNum, Data, False, self.RandDat), "")
    def RecvLenRaw(self):
        ErrCode, Data, EncDat = self.Prot.Recv()
        if ErrCode != 0:
            raise Exception("ConnectionError: Error code: " + str(ErrCode))
        Data = CryptVerify(self.Key, self.FuncNum, Data, True, self.RandDat)
        DataLen = len(Data) - GetLongStrBytes(Data[0:self.BtAlignLen])
        return Data[2:DataLen]
    def RecvRaw(self):
        ErrCode, Data, EncDat = self.Prot.Recv()
        if ErrCode != 0:
            raise Exception("ConnectionError: Error code: " + str(ErrCode))
        return CryptVerify(self.Key, self.FuncNum, Data, True, self.RandDat)
def FmtInetAddr(Bytes):
    Rtn = ""
    for Ch in Bytes[0:4]:
        Rtn += str(ord(Ch)) + "."
    return Rtn[:-1]
def CmprInetAddr(Str):
    Rtn = ""
    for Num in Str.split('.'):
        Rtn += chr(int(Num))
    if len(Rtn) != 4: raise ValueError("IPv4 address must have 4 numbers")
    return Rtn
def CmprInet6Addr(Str):
    Rtn = ""
    Toks = Str.split(':')
    if Toks[0:2] == ['', '']: Toks.pop(0)
    LstShorts = [0] * 8
    Off = 0
    for c in xrange(len(Toks)):
        if Toks[c] == "":
            if Off > 0: raise ValueError("IPv6 address cannot have more than one ::")
            Off = 8 - len(Toks)
        else: LstShorts[c + Off] = StrToInt(Toks[c], 16)
    for Short in LstShorts:
        Rtn += AlignStrBytesLong(Short, 2)
    return Rtn
def FmtInet6Addr(Bytes):
    LstShort = [0] * 8
    for c in xrange(0, 16, 2):
        LstShort[c / 2] = GetLongStrBytes(Bytes[c: c + 2])
    CurZeroLen = 0
    MaxZeroLen = 0
    CurZeroPos = 0
    MaxZeroPos = 0
    for c in xrange(len(LstShort)):
        if CurZeroLen > 0 and LstShort[c] != 0:
            CurZeroLen = 0
            continue
        elif CurZeroLen == 0 and LstShort[c] == 0:
            CurZeroLen = 1
            CurZeroPos = c
        elif CurZeroLen > 0 and LstShort[c] == 0:
            CurZeroLen += 1
        if CurZeroLen > MaxZeroLen:
            MaxZeroPos = CurZeroPos
            MaxZeroLen = CurZeroLen
    Toks = list()
    for c in xrange(0, MaxZeroPos):
        Toks.append(":")
        Toks.append(IntToStr(LstShort[c], 16))
    Toks.append("::")
    for c in xrange(MaxZeroPos + MaxZeroLen, len(LstShort)):
        Toks.append(IntToStr(LstShort[c], 16))
        Toks.append(":")
    if MaxZeroPos > 0: Toks.pop(0)
    Toks.pop()
    return "".join(Toks)
SkTypes = {}
SkProtos = {}
SkAfs = {}
for Str in dir(socket):
    if Str[0:5] == "SOCK_": SkTypes[getattr(socket, Str)] = Str
    elif Str[0:3] == "AF_": SkAfs[getattr(socket, Str)] = Str
    elif Str[0:8] == "IPPROTO_": SkProtos[getattr(socket, Str)] = Str
SkTypes[0] = "SOCK_DEFAULT"
SkAfInd = ['AF_UNSPEC', 'AF_APPLETALK', 'AF_INET', 'AF_ASH',
           'AF_ATMPVC', 'AF_ATMSVC', 'AF_X25', 'AF_AX25',
           'AF_BLUETOOTH', 'AF_BRIDGE', 'AF_INET6', 'AF_DECnet',
           'AF_ECONET', 'AF_IPX', 'AF_IRDA', 'AF_KEY', 'AF_LLC',
           'AF_NETBEUI', 'AF_NETLINK', 'AF_NETROM', 'AF_PACKET',
           'AF_PPPOX', 'AF_ROSE', 'AF_ROUTE', 'AF_SECURITY',
           'AF_SNA', 'AF_TIPC', 'AF_UNIX', 'AF_WANPIPE']
SkAfntoh = {}
SkAfhton = {}
for c in xrange(len(SkAfInd)):
    k = SkAfInd[c]
    v = getattr(socket, k, None)
    SkAfntoh[c] = v
    if v != None: SkAfhton[v] = c
class AddrInfo:
    LenAfDat = {2:(FmtInetAddr, CmprInetAddr, (4, 2)), 10:(FmtInet6Addr, CmprInet6Addr, (16, 2, 4, 4))}
    def __init__(self, Fl, Af, Type = None, Proto = None, Addr = None):
        if Fl != None:
            self.Af = SkAfntoh[Af]
            self.NetAf = Af
            self.Type = GetLongStrBytes(Fl.read(4))
            self.Proto = GetLongStrBytes(Fl.read(4))
            Fmt, Cmpr, LenDat = AddrInfo.LenAfDat[self.NetAf]
            self.Addr = list()
            for Len in LenDat:
                self.Addr.append(GetLongStrBytes(Fl.read(Len)))
            self.Addr[0] = Fmt(AlignStrBytesLong(self.Addr[0], LenDat[0]))
            self.Addr = tuple(self.Addr)
        else:
            self.NetAf = Af
            self.Af = SkAfhton[Af]
            self.Type = Type
            self.Proto = Proto
            self.Addr = Addr
    def Save(self, Fl):
        Fl.write(AlignStrBytesLong(self.NetAf, 4))
        Fl.write(AlignStrBytesLong(self.Type, 4))
        Fl.write(AlignStrBytesLong(self.Proto, 4))
        Fmt, Cmpr, LenDat = AddrInfo.LenAfDat[self.NetAf]
        TmpAddr = list(self.Addr)
        TmpAddr[0] = Cmpr(TmpAddr[0])
        Fl.write(TmpAddr[0])
        for c in xrange(1, len(TmpAddr)):
            Fl.write(AlignStrBytesLong(TmpAddr[c], LenDat[c]))
    def __str__(self):
        Rtn = "AF: " + SkAfs[self.Af]
        Rtn += ", TYPE: " + SkTypes[self.Type]
        Rtn += ", PROTO: " + SkProtos[self.Proto]
        Rtn += ", ADDR: (\"" + self.Addr[0] + "\""
        for Elem in self.Addr[1:]:
            Rtn += ", " + str(Elem)
        return Rtn + ")"
def KeyToStr(Key):
    Rtn = IntToStr(Key[0], 16)
    for c in xrange(1, len(Key)):
        Rtn += "\n  " + IntToStr(Key[c], 16)
    return Rtn
class CertData:
    LstFuncs = [TotCryptifyRSA]
    GetBtAligns = [GetAlignRSA]
    OPT_CHNG_NAME = 1
    OPT_CHNG_FUNC = 2
    def __init__(self, Fl, PublKey = None, Name = None, FuncNum = None):
        if Fl == None:
            self.Key = PublKey
            self.Name = Name
            self.FuncNum = FuncNum
            self.LstAddrInf = list()
        else:
            self.Key = UnpackListStrFl(Fl, 1, 2)
            for c in xrange(len(self.Key)):
                self.Key[c] = GetLongStrBytes(self.Key[c])
            self.FuncNum = ord(Fl.read(1))
            LenName = GetLongStrBytes(Fl.read(2))
            self.Name = Fl.read(LenName)
            self.LstAddrInf = list()
            Af = GetLongStrBytes(Fl.read(4))
            while Af != 0:
                self.LstAddrInf.append(AddrInfo(Fl, Af))
                Af = GetLongStrBytes(Fl.read(4))
        self.Serial = None
    def Update(self, ChkDat, DataIn): #ChkDat is sent to the client for the client to prove that it has possesion of the current valid private key
        NewKeyData = None
        try:
            NewKeyData = CryptVerify(self.Key, self.FuncNum, DataIn, True, ChkDat)
        except Exception as Msg:
            return "\x03failed due to invalid key"
        try:
            NewKey, OtherData = UnpackListStr(NewKeyData, 1, 2, True)
            Opts = ord(OtherData)
            NewName = None
            NewFunc = None
            if Opts & CertData.OPT_CHNG_NAME > 0:
                LenName = GetLongStrBytes(OtherData[0:2])
                NewName = OtherData[2: 2 + LenName]
                OtherData = OtherData[2 + LenName: ]
            if Opts & CertData.OPT_CHNG_FUNC > 0:
                NewFunc = ord(OtherData[0])
            if NewFunc != None and NewFunc >= len(CertData.LstFuncs):
                return "\x02Invalid Function"
            if NewName != None:
                CertNamed = FindNamedCert(NewName)
                if CertNamed != None and CertNamed != self:
                    return "\x01That name is already taken"
            self.Key = NewKey
            if NewName != None: self.Name = NewName
            if NewFunc != None: self.FuncNum = NewFunc
            self.Serial = None
            return "\0succeded"
        except:
            return "\x04Invalid format for updating information"
    def Serialize(self):
        if self.Serial != None:
            return self.Serial
        self.Serial = ""
        Keys = [None] * self.Key
        for c in xrange(len(Keys)):
            Keys[c] = GetStrBytesLong(self.Key[c])
        self.Serial += PackListStr(Keys, 1, 2)
        self.Serial += AlignStrBytesLong(len(self.Name), 2)
        self.Serial += self.Name
        self.Serial += chr(self.FuncNum)
        return self.Serial
    def SetAddrInf(self, ChkDat, AddrData):
        try:
            AddrData = CryptVerify(self.Key, self.FuncNum, AddrData, True, ChkDat)
        except Exception as Msg:
            return "\x03failed due to invalid key"
        Backup = self.LstAddrInf
        self.LstAddrInf = list()
        Fl = AnyFile(AddrData)
        try:
            Af = GetLongStrBytes(Fl.read(4))
            while Af != 0:
                self.LstAddrInf.append(AddrInfo(Fl, Af))
                Af = GetLongStrBytes(Fl.read(4))
            return "\0succeded"
        except Exception as Msg:
            self.LstAddrInf = Backup
            return "\x04Invalid format for updating information"
    def Save(self, Fl):
        if self.Serial != None:
            Fl.write(self.Serial)
            return
        Keys = [None] * len(self.Key)
        for c in xrange(len(Keys)):
            Keys[c] = GetStrBytesLong(self.Key[c])
        PackListStrFl(Fl, Keys, 1, 2)
        Fl.write(chr(self.FuncNum))
        Fl.write(AlignStrBytesLong(len(self.Name), 2))
        Fl.write(self.Name)
        for Addr in self.LstAddrInf:
            Addr.Save(Fl)
        Fl.write("\0\0\0\0")
    def GetAddrInfo(self, Port, Af, Type, Proto):
        Rtn = list()
        for AddrInf in self.LstAddrInf:
            if Af != 0 and AddrInf.Af != Af: continue
            elif Type != 0 and AddrInf.Type != Type: continue
            elif Proto != 0 and AddrInf.Proto != Proto: continue
            elif Port != 0 and AddrInf.Addr[1] != Port: continue
            Rtn.append(AddrInf)
        return Rtn
    def __str__(self):
        Rtn = "Name: " + self.Name
        Rtn += "\n  Key: " + KeyToStr(self.Key).replace("\n", "\n  ")
        Rtn += "\n  FuncNum: " + str(self.FuncNum)
        Rtn += "\n  Number of Address Info: " + str(len(self.LstAddrInf))
        for AddrInf in self.LstAddrInf:
            Rtn += "\n    " + str(AddrInf)
        return Rtn
#Root Certificate Servers (ie servers that are verified with root certificates and exist as a trusted source of other named certificates)
#Codes:
#0x00: GetInfo -> Send:[CertNum, RandDat], Recv(PublDCrpt):[Latest Certificate in use, ... oldest unexpired certificate]
#0x01: GetCert -> Send:[CertNum, CertName, RandDat], Recv(PublDCrpt):[PublKey, FuncNum]
#0x02: UpdateCert -> Send:[CertNum, *[UpdateData(PublEnCrptNoMix)]], Recv(PublDCrptNoMix):[ResultString]
#0x03: MakeCert

class RootCert:
    def __init__(self, PublKey, Name, FuncNum, ExpireDate, CertNum):
        self.Expires = ExpireDate
        self.Key = PublKey
        self.Name = Name
        self.FuncNum = FuncNum
        self.CertNum = CertNum
    def UpdateRoot(self, PercRand = .25):
        Sock = GetClientSock(self.Name, 15024)
        Prot = CltProt(MngdSock(Sock), None)
        Prot.TimeoutExcept = True
        RandDat = self.GetRandDat(PercRand)
        Prot.Send("\0" + AlignStrBytesLong(self.CertNum, 2) + AlignStrBytesLong(len(RandDat), 2) + RandDat, "")
        Rtn, NoEnc, EncDat = Prot.Recv()
        if NoEnc[0] != '\0':
            raise Exception("Invalid Root Certificate Number", NoEnc[1:])
        NewCert = UnpackListStr(CryptVerify(self.Key, self.FuncNum, NoEnc, True, RandDat), 2, 2)
        if NewCert[0].Expires > NewCert[-1].Expires:
            NewCert = NewCert[0]
        else:
            NewCert = NewCert[-1]
        self.Key, NewCert = UnpackListStr(NewCert, 1, 2, True)
        for c in xrange(len(self.Key)):
            self.Key[c] = GetLongStrBytes(self.Key[c])
        self.FuncNum = ord(NewCert[0])
        self.Expires = GetLongStrBytes(NewCert[1:5])
        self.CertNum = GetLongStrBytes(NewCert[5:7])
    def GetServCert(self, ServName, PercRand = .25):#Problem section
        Sock = GetClientSock(self.Name, 15024)
        if Sock == None:
            print "Sock None\n    " + str(self.Name)
        Prot = CltProt(Sock, None)
        RandDat = self.GetRandDat(PercRand)
        Prot.Send("\x01" + AlignStrBytesLong(self.CertNum, 2) + PackStrLen(ServName, 2) + RandDat, "")
        Rtn, NoEnc, Enc = Prot.Recv()
        Prot.Sock.close()
        Rtn = None
        if NoEnc[0] == '\0': Rtn, NoEnc = NoEnc[0], CryptVerify(self.Key, self.FuncNum, NoEnc[1:], True, RandDat)
        return Rtn == '\0', NoEnc
    def SetServCert(self, CertStr, PercRand = .25):
        RandDat = self.GetRandDat(PercRand)
        Sock = GetClientSock(self.Name, 15024)
        Prot = CltProt(Sock, None)
        Prot.Send("\x03" + AlignStrBytesLong(self.CertNum, 2) + RandDat, "")
        Rtn, NoEnc, EncDat = Prot.Recv()
        if NoEnc[0] != '\0':
            raise Exception(NoEnc[1:])
        print "SetServCert:1" + str(len(NoEnc[1:])) + ":" + repr(NoEnc[1:])
        Prot.Send(CryptVerify(self.Key, self.FuncNum, CertStr, False, NoEnc[1:]), "")
        print "SetServCert:2"
        Rtn, NoEnc, EncDat = Prot.Recv()
        print "SetServCert:3"
        NoEnc = CryptVerify(self.Key, self.FuncNum, NoEnc, True, RandDat)
        print "SetServCert:4"
        if NoEnc[0] != '\0':
            raise Exception(NoEnc[1:])
        return 0
    def GetAddrInfo(self, Host, Port, Af, Type, Proto, PercRand = .25):
        RandDat = self.GetRandDat(PercRand)
        Sock = GetClientSock(self.Name, 15024)
        Prot = CltProt(Sock, None)
        SendMe = "\x04" + AlignStrBytesLong(self.CertNum, 2)
        SendMe += PackStrLen(Host, 2)
        SendMe += AlignStrBytesLong(Port, 2)
        SendMe += AlignStrBytesLong(Af, 4)
        SendMe += AlignStrBytesLong(Type, 4)
        SendMe += AlignStrBytesLong(Proto, 4)
        SendMe += RandDat
        Prot.Send(SendMe, "")
        Rtn, NoEnc, EncDat = Prot.Recv()
        if NoEnc[0] != '\0': raise Exception(NoEnc[1:])
        NoEnc = CryptVerify(self.Key, self.FuncNum, NoEnc[1:], True, RandDat)
        Fl = AnyFile(NoEnc)
        Rtn = list()
        while Fl.Pos < Fl.GetLen():
            Rtn.append(AddrInfo(Fl, GetLongStrBytes(Fl.read(4))))
        return Rtn
    def GetRandDat(self, PercRand = .25):
        RandByteLen = int(CertData.GetBtAligns[self.FuncNum](self.Key) * PercRand)
        return GetRandBytes(RandByteLen)
class HkGetAddrInfo:
    def __init__(self, TheRoot):
        self.TheRoot = TheRoot
    def __call__(self, Host, Port, Af, Type, Proto, Flags):
        if Port == None: Port = 0
        LstAddrInf = self.TheRoot.GetAddrInfo(Host, Port, Af, Type, Proto)
        Rtn = list()
        for AddrInf in LstAddrInf:
            Rtn.append((AddrInf.Af, AddrInf.Type, AddrInf.Proto, "", AddrInf.Addr))
        return Rtn
def LoadRootFromFile(Fl):
    Key = UnpackListStrFl(Fl, 1, 2)
    for c in xrange(len(Key)):
        Key[c] = GetLongStrBytes(Key[c])
    FuncNum = ord(Fl.read(1))
    Expires = GetLongStrBytes(Fl.read(4))
    CertNum = GetLongStrBytes(Fl.read(2))
    NameLen = GetLongStrBytes(Fl.read(2))
    Name = Fl.read(NameLen)
    if EnvInfo["IsTest"]:
        Name = socket.gethostname()
    return RootCert(Key, Name, FuncNum, Expires, CertNum)
def SaveRootToFile(Fl, Root):
    Key = list(Root.Key)
    for c in xrange(len(Key)):
        Key[c] = GetStrBytesLong(Key[c])
    PackListStrFl(Fl, Key, 1, 2)
    Fl.write(chr(Root.FuncNum))
    Fl.write(AlignStrBytesLong(Root.Expires, 4))
    Fl.write(AlignStrBytesLong(Root.CertNum, 2))
    Fl.write(AlignStrBytesLong(len(Root.Name), 2))
    Fl.write(Root.Name)
class ServCert:
    def __init__(self, Fl, PrivKey = None, Name = None, FuncNum = None):
        if Fl == None:
            self.Key = PrivKey
            self.FuncNum = FuncNum
            self.Name = Name
        else:
            self.Key = UnpackListStrFl(Fl, 1, 2)
            for c in xrange(len(self.Key)):
                self.Key[c] = GetLongStrBytes(self.Key[c])
            self.FuncNum = ord(Fl.read(1))
            NameLen = GetLongStrBytes(Fl.read(2))
            self.Name = Fl.read(NameLen)
        self.RandByteLen = CertData.GetBtAligns[self.FuncNum](self.Key)
    def SendData(self, Prot, Data):
        Rtn, RandDat, EncDat = Prot.Recv()
        Prot.Send(CryptVerify(self.Key, self.FuncNum, Data, False, RandDat), "")
    def RecvData(self, Prot, PercRand = .25):
        RandDat = GetRandBytes(int(self.RandByteLen * PercRand))
        Prot.Send(RandDat, "")
        Rtn, NoEnc, Enc = Prot.Recv()
        return CryptVerify(self.Key, self.FuncNum, NoEnc, True, RandDat)
    def Update(self, TheRoot, NewPrivKey, NewPublKey):
        Sock = GetClientSock(TheRoot.Name, 15024)
        Sock = MngdSock(Sock)
        Prot = CltProt(Sock, None)
        Prot.TimeoutExcept = True
        RandDatUsr = TheRoot.GetRandDat()
        Prot.Send("\x02" + AlignStrBytesLong(TheRoot.CertNum, 2) + RandDatUsr, "")
        Rtn, NoEnc, EncDat = Prot.Recv()
        RandDat = NoEnc[1:]
        if NoEnc[0] != '\0':
            raise Exception("Error: " + RandDat)
        Prot.Send(PackStrLen(self.Name, 2) + CryptVerify(self.Key, self.FuncNum, PackListStr(NewPublKey) + chr(0), False, RandDat), "")
        Rtn = Prot.Recv()[1]
        if Rtn[0] == '\0':
            Rtn = CryptVerify(TheRoot.Key, TheRoot.FuncNum, Rtn[1:], True, RandDatUsr)
            if Rtn[0] == '\0':
                self.Key = NewPrivKey
                Rtn = None
        return Rtn
    def SetAddrInfo(self, TheRoot, LstAddrInf):
        Sock = GetClientSock(TheRoot.Name, 15024)
        Sock = MngdSock(Sock)
        Prot = CltProt(Sock, None)
        Prot.TimeoutExcept = True
        RandDatUsr = TheRoot.GetRandDat()
        Prot.Send("\x05" + AlignStrBytesLong(TheRoot.CertNum, 2) + RandDatUsr, "")
        Rtn, NoEnc, EncDat = Prot.Recv()
        RandDat = NoEnc[1:]
        if NoEnc[0] != '\0':
            raise Exception("Error: " + RandDat)
        Fl = AnyFile("")
        for AddrInf in LstAddrInf:
            AddrInf.Save(Fl)
        Prot.Send(PackStrLen(self.Name, 2) + CryptVerify(self.Key, self.FuncNum, Fl.Obj, False, RandDat), "")
        Rtn = Prot.Recv()[1]
        if Rtn[0] == '\0':
            Rtn = CryptVerify(TheRoot.Key, TheRoot.FuncNum, Rtn[1:], True, RandDatUsr)
            if Rtn[0] == '\0': Rtn = None
        return Rtn
    def Save(self, Fl):
        Tmp = list(self.Key)
        for c in xrange(len(Tmp)):
            Tmp[c] = GetStrBytesLong(Tmp[c])
        PackListStrFl(Fl, Tmp, 1, 2)
        Fl.write(chr(self.FuncNum))
        Fl.write(AlignStrBytesLong(len(self.Name), 2) + self.Name)
class CltCert:
    def __init__(self, Name, Root, PercRand = .25):
        Rtn, StrCert = Root.GetServCert(Name, PercRand)
        if not Rtn:
            raise Exception("This client failed to get the certificate", StrCert)
        self.Key, StrCert = UnpackListStr(StrCert, 1, 2, True)
        for c in xrange(len(self.Key)):
            self.Key[c] = GetLongStrBytes(self.Key[c])
        self.FuncNum = ord(StrCert[0])
        self.RandByteLen = CertData.GetBtAligns[self.FuncNum](self.Key)
    def RecvData(self, Prot, PercRand = .25):
        RandDat = GetRandBytes(int(self.RandByteLen * PercRand))
        Prot.Send(RandDat, "")
        Rtn, NoEnc, Enc = Prot.Recv()
        return CryptVerify(self.Key, self.FuncNum, NoEnc, True, RandDat)
    def SendData(self, Prot, Data):
        Rtn, RandDat, EncDat = Prot.Recv()
        Prot.Send(CryptVerify(self.Key, self.FuncNum, Data, False, RandDat), "")
def CmpPrimesLst(x, y):
    ValX = int(x != None)
    ValY = int(y != None)
    if not(ValX and ValY):
        return ValX - ValY
    return cmp(x[0], y[0])
class DHPrimeLst(object):
    LstBasePrimes = [2, 3, 5, 7, 11, 13, 17, 19]
    def GenThrd(self):
        while self.Alive:
            self.GenCond.acquire()
            for c in xrange(self.NumPrimes):
                if self.LstPrimes[c] != None: continue
                self.LstPrimes[c] = [self.PrimeLifetime, BitGetPrime(self.BitLen)]
            self.GenCond.wait()
        self.GenCond.release()
    def __init__(self, Fl, NumPrimes = 12, PrimeLifetime = 0, BitLen = 2048):
        if Fl != None:
            self.NumPrimes = GetLongStrBytes(Fl.read(1))
            self.PrimeLifetime = GetLongStrBytes(Fl.read(2))
            self.BitLen = GetLongStrBytes(Fl.read(2))
            NumCurPrimes = GetLongStrBytes(Fl.read(1))
            self.LstPrimes = [None] * self.NumPrimes
            for c in xrange(NumCurPrimes):
                self.LstPrimes[c] = [
                    GetLongStrBytes(Fl.read(2)),
                    GetLongStrBytes(UnpackStrLenFl(Fl, 2))]
            self.LstPrimes.sort(CmpPrimesLst)
        else:
            self.NumPrimes = NumPrimes
            self.PrimeLifetime = PrimeLifetime
            self.BitLen = BitLen
            self.LstPrimes = [None] * self.NumPrimes
        self.Thrd = threading.Thread(target = DHPrimeLst.GenThrd, args = (self,))
        self.Alive = True
        self.GenCond = threading.Condition()
        self.Thrd.start()
    #GetPublicInfo
    def __call__(self):
        PickFrom = [c for c in xrange(len(self.LstPrimes)) if self.LstPrimes[c] != None]
        if len(PickFrom) == 0:
            if self.GenCond.acquire(False):
                self.GenCond.notify()
                self.GenCond.release()
            return [SafeRand.choice(DHPrimeLst.LstBasePrimes), BitGetPrime(self.BitLen)]
        c = SafeRand.choice(PickFrom)
        RtnMod = self.LstPrimes[c][1]
        if self.LstPrimes[c][0] == 1:
            self.LstPrimes[c] = None
            if self.GenCond.acquire(False):
                self.GenCond.notify()
                self.GenCond.release()
        elif self.LstPrimes[c][0] > 1:
            self.LstPrimes[c][0] -= 1
        BasePrime = SafeRand.choice(DHPrimeLst.LstBasePrimes)
        return [BasePrime, RtnMod]
    def Save(self, Fl):
        Fl.write(AlignStrBytesLong(self.NumPrimes, 1))
        Fl.write(AlignStrBytesLong(self.PrimeLifetime, 2))
        Fl.write(AlignStrBytesLong(self.BitLen, 2))
        StrWrite = ""
        NumPrimes = 0
        for c in xrange(self.NumPrimes):
            if self.LstPrimes[c] == None: continue
            StrWrite += AlignStrBytesLong(self.LstPrimes[c][0], 2)
            StrWrite += PackStrLen(GetStrBytesLong(self.LstPrimes[c][1]), 2)
            NumPrimes += 1
        Fl.write(AlignStrBytesLong(NumPrimes, 1))
        Fl.write(StrWrite)
    def __del__(self):
        self.Alive = False
        self.GenCond.acquire()
        self.GenCond.notify()
        self.GenCond.release()
        self.Thrd.join()
def MkRSACertStr(Name, KeySize = RSACrypto.PrimeBitSize * 2):
    CryptInst = RSACrypto()
    CryptInst.PrimeBitLen = KeySize / 2
    PublKey = list(CryptInst.GenKey())
    PrivKey = list(CryptInst.GetPrivKey())
    for c in xrange(len(PublKey)):
        PublKey[c] = GetStrBytesLong(PublKey[c])
        PrivKey[c] = GetStrBytesLong(PrivKey[c])
    PrivKeyStr = PackListStr(PrivKey, 1, 2)
    PublKeyStr = PackListStr(PublKey, 1, 2)
    ComStr = chr(0) + AlignStrBytesLong(len(Name), 2) + Name
    return PrivKeyStr + ComStr, PublKeyStr + ComStr
def MkRSACertPrivStr(Name, KeySize = RSACrypto.PrimeBitSize * 2):
    CryptInst = RSACrypto()
    CryptInst.PrimeBitLen = KeySize / 2
    PublKey = list(CryptInst.GenKey())
    PrivKey = list(CryptInst.GetPrivKey())
    for c in xrange(len(PrivKey)):
        PrivKey[c] = GetStrBytesLong(PrivKey[c])
    PrivKeyStr = PackListStr(PrivKey, 1, 2)
    ComStr = chr(0) + AlignStrBytesLong(len(Name), 2) + Name
    return PrivKeyStr + ComStr, CertData(None, PublKey, Name, 0)
DHCfg = ValuePack({"CheckData": True, "FuncGetDHData": None})
def SetupModExpDH(Sock, Data, IsServ):
    Cur = DHKeyExchange()
    Cur.CheckData = DHCfg.CheckData
    if IsServ:
        Prot = ServProt(Sock, None)
        print "ModExpDH Send 1"
        Prot.Send(GetStrBytesLong(Cur.KeySize), "")
        if DHCfg.FuncGetDHData == None:
            Cur.GenPublicInfo()
        else:
            Cur.SetPublicInfo(DHCfg.FuncGetDHData())
        PubInf = Cur.GetPublicInfo()
        print "ModExpDH Send 2"
        Prot.Send(chr(PubInf[0]) + GetStrBytesLong(PubInf[1]), "")
        print "ModExpDH Send 3"
        Prot.Send(GetStrBytesLong(Cur.GenPublicPrivateMix()), "")
        print "ModExpDH Recv 1"
        Rtn, NoEnc, EncDat = Prot.Recv()
        Cur.GenSharedSecret(GetLongStrBytes(NoEnc))
        return ServProt(Sock, Cur.GenMidEncSt(EncFunc, BlkCipher))
    else:
        Prot = CltProt(Sock, None)
        print "ModExpDH Recv 1"
        Rtn, NoEnc, EndDat = Prot.Recv()
        if Rtn == -1:
            raise Exception("Socket timed out")
        Cur.KeySize = GetLongStrBytes(NoEnc)
        print "ModExpDH Recv 2"
        Rtn, NoEnc, EndDat = Prot.Recv()
        Cur.SetPublicInfo([ord(NoEnc[0]), GetLongStrBytes(NoEnc[1:])])
        SendMix = Cur.GenPublicPrivateMix()
        print "ModExpDH Recv 3"
        Rtn, NoEnc, EncDat = Prot.Recv()
        Cur.GenSharedSecret(GetLongStrBytes(NoEnc))
        print "ModExpDH Send 1"
        Prot.Send(GetStrBytesLong(SendMix), "")
        return CltProt(Sock, Cur.GenMidEncSt(EncFunc, BlkCipher))
def DHVerifyRSA(Sock, Data, IsServ):
    Cur = DHKeyExchange()
    Cur.CheckData = DHCfg.CheckData
    Prot = None
    if IsServ:
        Prot = ServProt(Sock, None)
        ThisCert = Data[0]
        #print "DHVerifyRSA Send 1"
        ThisCert.SendData(Prot, GetStrBytesLong(Cur.KeySize))
        if DHCfg.FuncGetDHData == None:
            Cur.GenPublicInfo()
        else:
            Cur.SetPublicInfo(DHCfg.FuncGetDHData())
        PubInf = Cur.GetPublicInfo()
        #print "DHVerifyRSA Send 2"
        ThisCert.SendData(Prot, chr(PubInf[0]) + GetStrBytesLong(PubInf[1]))
        #print "DHVerifyRSA Send 3"
        ThisCert.SendData(Prot, GetStrBytesLong(Cur.GenPublicPrivateMix()))
        #print "DHVerifyRSA Recv 1"
        Cur.GenSharedSecret(GetLongStrBytes(ThisCert.RecvData(Prot)))
    else:
        Prot = CltProt(Sock, None)
        ThisCert = CltCert(Data[0], Data[1])
        print "DHVerifyRSA Recv 1"
        Cur.KeySize = GetLongStrBytes(ThisCert.RecvData(Prot))
        print "DHVerifyRSA Recv 2"
        PublInfo = ThisCert.RecvData(Prot)
        Cur.SetPublicInfo([ord(PublInfo[0]), GetLongStrBytes(PublInfo[1:])])
        SendMix = GetStrBytesLong(Cur.GenPublicPrivateMix())
        print "DHVerifyRSA Recv 3"
        Cur.GenSharedSecret(GetLongStrBytes(ThisCert.RecvData(Prot)))
        print "DHVerifyRSA Send 1"
        ThisCert.SendData(Prot, SendMix)
    Prot.Enc = Cur.GenMidEncSt(EncFunc, BlkCipher)
    EnvInfo["NormInf"] = [Prot.Enc[0], Prot.Enc[1], Cur]
    return Prot
class KyleSock:
    def __init__(self, TheSock):
        self.Sock = TheSock
        self.Debug = True
    def send(self, Data, Flags = 0):
        try:
            if self.Debug: DbgLogStr(Data, DBGINF_KYSEND)
            Rtn = int(self.Sock.send(ByteArray(Data, len(Data)), Flags))
            if self.Debug: DbgLogNum(Rtn, DBGINF_OKKYSEND)
            return Rtn
        except Exception as Msg:
            if self.Debug: DbgLogStr(str(Msg), DBGINF_EXCKYSEND)
            if Msg.args[0] == 10060: raise socket.timeout()
            else: raise Msg
    def recv(self, Num, Flags = 0):
        RtnTmp = None
        try:
            if self.Debug: DbgLogNum(Num, DBGINF_KYRECV)
            RtnTmp = KyleObj(ByteArray, self.Sock.recv(Num, Flags))
        except Exception as Msg:
            if self.Debug: DbgLogStr(str(Msg), DBGINF_EXCKYRECV)
            if Msg.args[0] == 10060: raise socket.timeout()
            else: raise Msg
        Rtn = ctypes.string_at(RtnTmp.DataPtr(), RtnTmp.Len())
        if self.Debug:
            DbgLogStr(Rtn, DBGINF_KYRECV)
            DbgLogStr("Recv " + str(RtnTmp.Len()) + " Bytes", DBGINF_OKKYRECV)
        del RtnTmp
        return Rtn
    def settimeout(self, Num):
        if Num == None: Num = -1
        self.Sock.settimeout(float(Num))
    def gettimeout(self):
        return self.Sock.gettimeout()
class KyleProt:
    def __init__(self, TheProt, TheSock):
        self.Prot = TheProt
        self.TimeoutExcept = True
        self.Sock = TheSock
    def Send(self, NoEncDat, EncDat):
        if not isinstance(NoEncDat, KyleObj):
            NoEncDat = ByteArray(NoEncDat, len(NoEncDat))
        if not isinstance(EncDat, KyleObj):
            EncDat = ByteArray(EncDat, len(EncDat))
        try:
            self.Prot.Send(NoEncDat, EncDat)
        except Exception as Msg:
            if len(Msg.args) == 2 and Msg.args[0] == 10060:
                if self.TimeoutExcept: raise socket.timeout()
                else: return -1
            raise Msg
        return 0
    def Recv(self, EncDat = None, NoEncDat = None):
        RtnEnc = False
        RtnNoEnc = False
        if EncDat == None:
            RtnEnc = True
            EncDat = ByteArray()
        if NoEncDat == None:
            RtnNoEnc = True
            NoEncDat = ByteArray()
        if not isinstance(EncDat, KyleObj):
            raise Exception("Unsupported Type")
        if not isinstance(NoEncDat, KyleObj):
            raise Exception("Unsupported Type")
        try:
            self.Prot.Recv(NoEncDat, EncDat)
        except Exception as Msg:
            if len(Msg.args) == 2 and Msg.args[0] == 10060:
                if self.TimeoutExcept: raise socket.timeout()
                else: return -1
            raise Msg
        LstRtn = [0]
        if RtnNoEnc:
            TheLen = NoEncDat.Len()
            if TheLen == 0: RtnNoEnc = b""
            else: RtnNoEnc = ctypes.string_at(NoEncDat.DataPtr(), TheLen)
            LstRtn.append(RtnNoEnc)
        if RtnEnc:
            TheLen = EncDat.Len()
            if TheLen == 0: RtnEnc = b""
            else:
                Ptr = EncDat.DataPtr()
                print TheLen, Ptr, type(Ptr)
                RtnEnc = ctypes.string_at(EncDat.DataPtr(), TheLen)
            LstRtn.append(RtnEnc)
        if len(LstRtn) == 1: return LstRtn[0]
        else: return tuple(LstRtn)
def DHVeriRSAKyle(Prot, Data, IsServ):
    Cur = DHKeyExchange()
    Cur.CheckData = DHCfg.CheckData
    if IsServ:
        ThisCert = Data[0]
        #print "DHVerifyRSA Send 1"
        ThisCert.SendData(Prot, GetStrBytesLong(Cur.KeySize))
        if DHCfg.FuncGetDHData == None:
            Cur.GenPublicInfo()
        else:
            Cur.SetPublicInfo(DHCfg.FuncGetDHData())
        PubInf = Cur.GetPublicInfo()
        #print "DHVerifyRSA Send 2"
        ThisCert.SendData(Prot, chr(PubInf[0]) + GetStrBytesLong(PubInf[1]))
        #print "DHVerifyRSA Send 3"
        ThisCert.SendData(Prot, GetStrBytesLong(Cur.GenPublicPrivateMix()))
        #print "DHVerifyRSA Recv 1"
        Cur.GenSharedSecret(GetLongStrBytes(ThisCert.RecvData(Prot)))
    else:
        ThisCert = CltCert(Data[0], Data[1])
        print "DHVerifyRSA Recv 1"
        Cur.KeySize = GetLongStrBytes(ThisCert.RecvData(Prot))
        print "DHVerifyRSA Recv 2"
        PublInfo = ThisCert.RecvData(Prot)
        Cur.SetPublicInfo([ord(PublInfo[0]), GetLongStrBytes(PublInfo[1:])])
        SendMix = GetStrBytesLong(Cur.GenPublicPrivateMix())
        print "DHVerifyRSA Recv 3"
        Cur.GenSharedSecret(GetLongStrBytes(ThisCert.RecvData(Prot)))
        print "DHVerifyRSA Send 1"
        ThisCert.SendData(Prot, SendMix)
    Key = AlignStrBytesLong(DefCmprFunc(Cur.SharedKey, 128), 16)
    InitVec = AlignStrBytesLong(MyHash_11(Key, len(Key) * 8), len(Key))
    EnvInfo["KyleInf"] = [Key, InitVec, Cur]
    Prot.Prot.Init(ByteArray(Key, len(Key)), ByteArray(InitVec, len(InitVec)), 0, 0)
    return Prot
def LoadRootFromfName(fName):
    Fl = open(fName, "rb")
    Rtn = LoadRootFromFile(Fl)
    Fl.close()
    return Rtn
LstSecFunc = [SetupModExpDH, DHVerifyRSA]
LstKyleSecFunc = [None, DHVeriRSAKyle]
CltSecurity = 1
def SetUpDH(Sock, DefSec = CltSecurity, Data = list(), IsServ = False):
    print "Begginning to setup dh exchange"
    IsDbg = hasattr(Sock, "Debug")
    if IsDbg: IsDbg = Sock.Debug
    if IsDbg: DbgLogStr("Begin Setup dh exchange", DBGINF_DHXCHG)
    global LstSecFunc
    Rtn = None
    if IsServ:
        StrSend = chr(len(LstSecFunc) - 1)
        StrSend += chr(DefSec)
        Rtn = EncSend(Sock, None, StrSend, "")
        RecvDat = [AnyFile(""), AnyFile("")]
        Rtn = EncRecv(Sock, None, RecvDat[0], RecvDat[1])
        StrRecv, Stuff = RecvDat[0].Obj, RecvDat[1].Obj
        SecSel = None
        if ord(StrRecv[0]) >= len(LstSecFunc):
            SecSel = DefSec
        else:
            SecSel = ord(StrRecv[0])
        Rtn = LstSecFunc[SecSel](Sock, Data, True)
    else:
        RecvDat = [AnyFile(""), AnyFile("")]
        Rtn = EncRecv(Sock, None, RecvDat[1], RecvDat[0])
        StrRecv, Stuff = RecvDat[0].Obj, RecvDat[1].Obj
        Opt = DefSec
        if Opt >= len(LstSecFunc):
            Opt = CltSecurity
        elif Opt < 0:
            Opt = ord(StrRecv[0])
        EncSend(Sock, None, chr(Opt), "")
        Rtn = LstSecFunc[Opt](Sock, Data, False)
    print "End Setup dh exchange"
    if IsDbg: DbgLogStr("End Setup dh exchange", DBGINF_DHXCHG)
    return Rtn
def KyleSetUpDH(Sock, DefSec = CltSecurity, Data = list(), IsServ = False):
    print "Begginning to setup dh exchange"
    DbgLogStr("Begin Setup dh exchange", DBGINF_DHXCHG)
    global LstKyleSecFunc
    Rtn = None
    Prot = KyleProt(EncProt(Sock, 0, 0, 0, 0), Sock)
    Sock.NoGC()
    if IsServ:
        StrSend = chr(len(LstSecFunc) - 1)
        StrSend += chr(DefSec)
        Rtn = Prot.Send(StrSend, "")
        Rtn, StrRecv, Stuff = Prot.Recv()
        SecSel = None
        if ord(StrRecv[0]) >= len(LstSecFunc):
            SecSel = DefSec
        else:
            SecSel = ord(StrRecv[0])
        Rtn = LstKyleSecFunc[SecSel](Prot, Data, True)
    else:
        Rtn, StrRecv, Stuff = Prot.Recv()
        Opt = DefSec
        if Opt >= len(LstSecFunc):
            Opt = CltSecurity
        elif Opt < 0:
            Opt = ord(StrRecv[0])
        Prot.Send(chr(Opt), "")
        Rtn = LstKyleSecFunc[Opt](Prot, Data, False)
    print "End Setup dh exchange"
    DbgLogStr("End Setup dh exchange", DBGINF_DHXCHG)
    return Prot
def ProtInit(IsServ = False):
    global EncSend
    global EncRecv
    if IsServ: EncSend, EncRecv = EncSendOpt, EncRecvOpt
    else: EncSend, EncRecv = EncSendOrig, EncRecvOrig
if __name__ == "__main__": ProtInit()
EnvInfo["IsTest"] = False
