# Path: crysp/utils/operators.py
# def rol(x,n):
#     return (x<<n | x>>(x.size-n))
# 
# Path: crysp/padding.py
# class MDpadding(blockiterator):
#     def __init__(self,l,wsize):
#         self.wsize = wsize
#         super(MDpadding,self).__init__(l)
#     # add sha1 padding:
#     def lastblock(self,m,**kargs):
#         bitlen = kargs.get('bitlen',None)
#         if bitlen is None:
#             bitlen = self.bitcnt+len(m)*8
#         assert self.bitcnt<=bitlen
#         needed = bitlen-self.bitcnt
#         mb = Bits(m,size=needed)
#         countersize = self.wsize*2
#         N = self.blocksize-1-countersize-needed
#         if N<0: N += self.blocksize
#         pad = mb//Bits(1,1)//Bits(0,N)
#         self.padflag = True
#         self.bitcnt += needed
#         return pad.bytes()+pack(Bits(bitlen,countersize))
#     # remove sha1 padding:
#     def remove(self,c):
#         clen = self.wsize//4
#         counter,_ = unpack(c[-clen:])
#         c = list(c[:-clen])
#         while Bits(c[-1]).ival==0:
#             c.pop()
#         if len(c)==0: raise PaddingError("failed to remove padding")
#         b = Bits(c.pop())
#         b.size=str(b).rfind('1')
#         return b''.join(c)+b.bytes()
# 
# Path: crysp/poly.py
# class Poly(SubPoly):
# 
#   def __getitem__(self,i):
#       if isinstance(i,int):
#           return Poly(self.ival[i],self.size)
#       elif isinstance(i,slice):
#           return Poly(self.span(i),self.size)
#       else:
#           return Poly([self.ival[j] for j in i],self.size)
# 
# Path: crysp/padding.py
# class Nullpadding(blockiterator):
#     def lastblock(self,m,**kargs):
#         bitlen = kargs.get('bitlen',None)
#         if bitlen is None:
#             bitlen = len(m)*8
#         else:
#             bitlen = bitlen-self.bitcnt
#         q = self.blocksize-bitlen
#         b = (Bits(m,bitlen)//Bits(0,q)).bytes()
#         assert len(b)==self.blocklen
#         self.bitcnt += bitlen
#         self.padflag = True
#         self.padcnt = q
#         return b
#     # remove padding:
#     def remove(self,m):
#         b=Bits(m[-self.blocklen:])
#         b.size -= self.padcnt
#         return m[:-self.blocklen]+b.bytes()
# 
# Path: crysp/utils/operators.py
# def concat(L,bigend=False):
#     if len(L)==1: return L[0]
#     if bigend: L = reversed(L)
#     return reduce(lambda x,y: x//y, L)


from crysp.bits import *
from crysp.utils.operators import rol
from crysp.padding import MDpadding
from crysp.poly import Poly
from crysp.padding import Nullpadding
from crysp.utils.operators import concat

Q = [0x7311c2812425cfa0,
     0x6432286434aac8e7,
     0xb60450e9ef68b7c1,
     0xe8fb23908d9f06f1,
     0xdd2e76cba691e5bf,
     0x0cd0d63b2c30bc41,
     0x1f8ccf6823058f8a,
     0x54e5ed5b88e3775d,
     0x4ad12aae0a6d6031,
     0x3e7f16bb88222e0d,
     0x8af8671d3fb50c2c,
     0x995ad1178bd25c31,
     0xc878c1dd04c4b633,
     0x3b72066c7a1552ac,
     0x0d6f3522631effcb]

rin = [10,  5, 13, 10, 11, 12,  2,  7, 14, 15,  7, 13, 11,  7,  6, 12]
lin = [11, 24,  9, 16, 15,  9, 27, 15,  6,  2, 29,  8, 15,  5, 31,  9]

class MD6(object):
    def __init__(self,d=512,Key=b'',L=0):
        self.size = d
        self.chunksize = 1024
        self.blocksize = 3*self.chunksize
        self.wsize = 64
        r = 40+(d//4)
        if Key: r = max(80,r)
        self.keylen = len(Key)
        Key = Key[:64].ljust(64,b'\0')

# Path: crysp/utils/operators.py
# def rol(x,n):
#     return (x<<n | x>>(x.size-n))
# 
# Path: crysp/padding.py
# class MDpadding(blockiterator):
#     def __init__(self,l,wsize):
#         self.wsize = wsize
#         super(MDpadding,self).__init__(l)
#     # add sha1 padding:
#     def lastblock(self,m,**kargs):
#         bitlen = kargs.get('bitlen',None)
#         if bitlen is None:
#             bitlen = self.bitcnt+len(m)*8
#         assert self.bitcnt<=bitlen
#         needed = bitlen-self.bitcnt
#         mb = Bits(m,size=needed)
#         countersize = self.wsize*2
#         N = self.blocksize-1-countersize-needed
#         if N<0: N += self.blocksize
#         pad = mb//Bits(1,1)//Bits(0,N)
#         self.padflag = True
#         self.bitcnt += needed
#         return pad.bytes()+pack(Bits(bitlen,countersize))
#     # remove sha1 padding:
#     def remove(self,c):
#         clen = self.wsize//4
#         counter,_ = unpack(c[-clen:])
#         c = list(c[:-clen])
#         while Bits(c[-1]).ival==0:
#             c.pop()
#         if len(c)==0: raise PaddingError("failed to remove padding")
#         b = Bits(c.pop())
#         b.size=str(b).rfind('1')
#         return b''.join(c)+b.bytes()
# 
# Path: crysp/poly.py
# class Poly(SubPoly):
# 
#   def __getitem__(self,i):
#       if isinstance(i,int):
#           return Poly(self.ival[i],self.size)
#       elif isinstance(i,slice):
#           return Poly(self.span(i),self.size)
#       else:
#           return Poly([self.ival[j] for j in i],self.size)
# 
# Path: crysp/padding.py
# class Nullpadding(blockiterator):
#     def lastblock(self,m,**kargs):
#         bitlen = kargs.get('bitlen',None)
#         if bitlen is None:
#             bitlen = len(m)*8
#         else:
#             bitlen = bitlen-self.bitcnt
#         q = self.blocksize-bitlen
#         b = (Bits(m,bitlen)//Bits(0,q)).bytes()
#         assert len(b)==self.blocklen
#         self.bitcnt += bitlen
#         self.padflag = True
#         self.padcnt = q
#         return b
#     # remove padding:
#     def remove(self,m):
#         b=Bits(m[-self.blocklen:])
#         b.size -= self.padcnt
#         return m[:-self.blocklen]+b.bytes()
# 
# Path: crysp/utils/operators.py
# def concat(L,bigend=False):
#     if len(L)==1: return L[0]
#     if bigend: L = reversed(L)
#     return reduce(lambda x,y: x//y, L)


# Path: crysp/md.py
from crysp.bits import *
from crysp.utils.operators import rol
from crysp.padding import MDpadding
from crysp.poly import Poly
from crysp.padding import Nullpadding
from crysp.utils.operators import concat


Q = [0x7311c2812425cfa0,
     0x6432286434aac8e7,
     0xb60450e9ef68b7c1,
     0xe8fb23908d9f06f1,
     0xdd2e76cba691e5bf,
     0x0cd0d63b2c30bc41,
     0x1f8ccf6823058f8a,
     0x54e5ed5b88e3775d,
     0x4ad12aae0a6d6031,
     0x3e7f16bb88222e0d,
     0x8af8671d3fb50c2c,
     0x995ad1178bd25c31,
     0xc878c1dd04c4b633,
     0x3b72066c7a1552ac,
     0x0d6f3522631effcb]

rin = [10,  5, 13, 10, 11, 12,  2,  7, 14, 15,  7, 13, 11,  7,  6, 12]
lin = [11, 24,  9, 16, 15,  9, 27, 15,  6,  2, 29,  8, 15,  5, 31,  9]

class MD6(object):
    def __init__(self,d=512,Key=b'',L=0):
        self.size = d
        self.chunksize = 1024
        self.blocksize = 3*self.chunksize
        self.wsize = 64
        r = 40+(d//4)
        if Key: r = max(80,r)
        self.keylen = len(Key)
        Key = Key[:64].ljust(64,b'\0')

        self.K = Poly(struct.unpack('>8Q',Key),self.wsize)