Esempio n. 1
0
def generate_py(bits, randfunc, progress_func=None):
    """generate(bits:int, randfunc:callable, progress_func:callable)

    Generate a DSA key of length 'bits', using 'randfunc' to get
    random data and 'progress_func', if present, to display
    the progress of the key generation.
    """

    if bits<160:
        raise ValueError('Key length < 160 bits')
    obj=DSAobj()
    # Generate string S and prime q
    if progress_func:
        progress_func('p,q\n')
    while (1):
        S, obj.q = generateQ(randfunc)
        n=divmod(bits-1, 160)[0]
        C, N, V = 0, 2, {}
        b=(obj.q >> 5) & 15
        powb=pow(bignum(2), b)
        powL1=pow(bignum(2), bits-1)
        while C<4096:
            for k in range(0, n+1):
                V[k]=bytes_to_long(SHA.new(S+bstr(N)+bstr(k)).digest())
            W=V[n] % powb
            for k in range(n-1, -1, -1):
                W=(W<<160)+V[k]
            X=W+powL1
            p=X-(X%(2*obj.q)-1)
            if powL1<=p and isPrime(p):
                break
            C, N = C+1, N+n+1
        if C<4096:
            break
        if progress_func:
            progress_func('4096 multiples failed\n')

    obj.p = p
    power=divmod(p-1, obj.q)[0]
    if progress_func:
        progress_func('h,g\n')
    while (1):
        h=bytes_to_long(randfunc(bits)) % (p-1)
        g=pow(h, power, p)
        if 1<h<p-1 and g>1:
            break
    obj.g=g
    if progress_func:
        progress_func('x,y\n')
    while (1):
        x=bytes_to_long(randfunc(20))
        if 0 < x < obj.q:
            break
    obj.x, obj.y = x, pow(g, x, p)
    return obj
Esempio n. 2
0
    def undigest(self, blocks):
        """undigest(blocks : [string]) : string

        Perform the reverse package transformation on a list of message
        blocks.  Note that the ciphermodule used for both transformations
        must be the same.  blocks is a list of strings of bit length
        equal to the ciphermodule's block_size.
        """

        # better have at least 2 blocks, for the padbytes package and the hash
        # block accumulator
        if len(blocks) < 2:
            raise ValueError("List must be at least length 2.")

        # blocks is a list of strings.  We need to deal with them as long
        # integers
        blocks = list(map(bytes_to_long, blocks))

        # Calculate the well-known key, to which the hash blocks are
        # encrypted, and create the hash cipher.
        K0 = self.__K0digit * self.__key_size
        hcipher = self.__newcipher(K0)
        block_size = self.__ciphermodule.block_size

        # Since we have all the blocks (or this method would have been called
        # prematurely), we can calculate all the hash blocks.
        hashes = []
        for i in range(1, len(blocks)):
            mticki = blocks[i - 1] ^ i
            hi = hcipher.encrypt(long_to_bytes(mticki, block_size))
            hashes.append(bytes_to_long(hi))

        # now we can calculate K' (key).  remember the last block contains
        # m's' which we don't include here
        key = blocks[-1] ^ reduce(operator.xor, hashes)

        # and now we can create the cipher object
        mcipher = self.__newcipher(long_to_bytes(key, self.__key_size))

        # And we can now decode the original message blocks
        parts = []
        for i in range(1, len(blocks)):
            cipherblock = mcipher.encrypt(long_to_bytes(i, block_size))
            mi = blocks[i - 1] ^ bytes_to_long(cipherblock)
            parts.append(mi)

        # The last message block contains the number of pad bytes appended to
        # the original text string, such that its length was an even multiple
        # of the cipher's block_size.  This number should be small enough that
        # the conversion from long integer to integer should never overflow
        padbytes = int(parts[-1])
        text = b('').join(map(long_to_bytes, parts[:-1]))
        return text[:-padbytes]
Esempio n. 3
0
    def chaff(self, blocks):
        """chaff( [(serial-number:int, data:string, MAC:string)] )
        : [(int, string, string)]

        Add chaff to message blocks.  blocks is a list of 3-tuples of the
        form (serial-number, data, MAC).

        Chaff is created by choosing a random number of the same
        byte-length as data, and another random number of the same
        byte-length as MAC.  The message block's serial number is
        placed on the chaff block and all the packet's chaff blocks
        are randomly interspersed with the single wheat block.  This
        method then returns a list of 3-tuples of the same form.
        Chaffed blocks will contain multiple instances of 3-tuples
        with the same serial number, but the only way to figure out
        which blocks are wheat and which are chaff is to perform the
        MAC hash and compare values.
        """

        chaffedblocks = []

        # count is the number of blocks to add chaff to.  blocksper is the
        # number of chaff blocks to add per message block that is being
        # chaffed.
        count = len(blocks) * self.__factor
        blocksper = list(range(self.__blocksper))
        for i, wheat in zip(list(range(len(blocks))), blocks):
            # it shouldn't matter which of the n blocks we add chaff to, so for
            # ease of implementation, we'll just add them to the first count
            # blocks
            if i < count:
                serial, data, mac = wheat
                datasize = len(data)
                macsize = len(mac)
                addwheat = 1
                # add chaff to this block
                for j in blocksper:
                    import sys
                    chaffdata = self._randnum(datasize)
                    chaffmac = self._randnum(macsize)
                    chaff = (serial, chaffdata, chaffmac)
                    # mix up the order, if the 5th bit is on then put the
                    # wheat on the list
                    if addwheat and bytes_to_long(self._randnum(16)) & 0x40:
                        chaffedblocks.append(wheat)
                        addwheat = 0
                    chaffedblocks.append(chaff)
                if addwheat:
                    chaffedblocks.append(wheat)
            else:
                # just add the wheat
                chaffedblocks.append(wheat)
        return chaffedblocks
Esempio n. 4
0
    def _decodeLen(self, idx, der):
        """Given a (part of a) DER element, and an index to the first byte of
                a DER length tag (L), return a tuple with the payload size,
                and the index of the first byte of the such payload (V).

                Raises a ValueError exception if the DER length is invalid.
                Raises an IndexError exception if the DER element is too short.
                """
        length = bord(der[idx])
        if length <= 127:
            return (length, idx + 1)
        payloadLength = bytes_to_long(der[idx + 1:idx + 1 + (length & 0x7F)])
        if payloadLength <= 127:
            raise ValueError("Not a DER length tag.")
        return (payloadLength, idx + 1 + (length & 0x7F))
Esempio n. 5
0
def generateQ(randfunc):
    S=randfunc(20)
    hash1=SHA.new(S).digest()
    hash2=SHA.new(long_to_bytes(bytes_to_long(S)+1)).digest()
    q = bignum(0)
    for i in range(0,20):
        c=bord(hash1[i])^bord(hash2[i])
        if i==0:
            c=c | 128
        if i==19:
            c= c | 1
        q=q*256+c
    while (not isPrime(q)):
        q=q+2
    if pow(2,159) < q < pow(2,160):
        return S, q
    raise RuntimeError('Bad q value generated')
Esempio n. 6
0
    def decode(self, derEle, noLeftOvers=0):
        """Decode a complete INTEGER DER element, and re-initializes this
                object with it.

                @param derEle       A complete INTEGER DER element. It must start with a DER
                                    INTEGER tag.
                @param noLeftOvers  Indicate whether it is acceptable to complete the
                                    parsing of the DER element and find that not all
                                    bytes in derEle have been used.
                @return             Index of the first unused byte in the given DER element.

                Raises a ValueError exception if the DER element is not a
                valid non-negative INTEGER.
                Raises an IndexError exception if the DER element is too short.
                """
        tlvLength = DerObject.decode(self, derEle, noLeftOvers)
        if self.typeTag != self.typeTags['INTEGER']:
            raise ValueError("Not a DER INTEGER.")
        if bord(self.payload[0]) > 127:
            raise ValueError("Negative INTEGER.")
        self.value = bytes_to_long(self.payload)
        return tlvLength
Esempio n. 7
0
 def getrandbits(self, k):
     """Return a python long integer with k random bits."""
     if self._randfunc is None:
         self._randfunc = Random.new().read
     mask = (1 << k) - 1
     return mask & bytes_to_long(self._randfunc(ceil_div(k, 8)))
Esempio n. 8
0
    def importKey(self, externKey, passphrase=None):
        """Import an RSA key (public or private half), encoded in standard form.

        :Parameter externKey:
            The RSA key to import, encoded as a string.

            An RSA public key can be in any of the following formats:

            - X.509 `subjectPublicKeyInfo` DER SEQUENCE (binary or PEM encoding)
            - `PKCS#1`_ `RSAPublicKey` DER SEQUENCE (binary or PEM encoding)
            - OpenSSH (textual public key only)

            An RSA private key can be in any of the following formats:

            - PKCS#1 `RSAPrivateKey` DER SEQUENCE (binary or PEM encoding)
            - `PKCS#8`_ `PrivateKeyInfo` DER SEQUENCE (binary or PEM encoding)
            - OpenSSH (textual public key only)

            For details about the PEM encoding, see `RFC1421`_/`RFC1423`_.
            
            In case of PEM encoding, the private key can be encrypted with DES or 3TDES according to a certain ``pass phrase``.
            Only OpenSSL-compatible pass phrases are supported.
        :Type externKey: string

        :Parameter passphrase:
            In case of an encrypted PEM key, this is the pass phrase from which the encryption key is derived.
        :Type passphrase: string
        
        :Return: An RSA key object (`_RSAobj`).

        :Raise ValueError/IndexError/TypeError:
            When the given key cannot be parsed (possibly because the pass phrase is wrong).

        .. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt
        .. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt
        .. _`PKCS#1`: http://www.ietf.org/rfc/rfc3447.txt
        .. _`PKCS#8`: http://www.ietf.org/rfc/rfc5208.txt
        """
        externKey = tobytes(externKey)
        if passphrase is not None:
            passphrase = tobytes(passphrase)

        if externKey.startswith(b('-----')):
            # This is probably a PEM encoded key
            lines = externKey.replace(b(" "), b('')).split()
            keyobj = None

            # The encrypted PEM format
            if lines[1].startswith(b('Proc-Type:4,ENCRYPTED')):
                DEK = lines[2].split(b(':'))
                if len(DEK) != 2 or DEK[0] != b('DEK-Info') or not passphrase:
                    raise ValueError("PEM encryption format not supported.")
                algo, salt = DEK[1].split(b(','))
                salt = binascii.a2b_hex(salt)
                import Crypto.Hash.MD5
                from Crypto.Cipher import DES, DES3
                from Crypto.Protocol.KDF import PBKDF1
                if algo == b("DES-CBC"):
                    # This is EVP_BytesToKey in OpenSSL
                    key = PBKDF1(passphrase, salt, 8, 1, Crypto.Hash.MD5)
                    keyobj = DES.new(key, Crypto.Cipher.DES.MODE_CBC, salt)
                elif algo == b("DES-EDE3-CBC"):
                    # Note that EVP_BytesToKey is note exactly the same as PBKDF1
                    key = PBKDF1(passphrase, salt, 16, 1, Crypto.Hash.MD5)
                    key += PBKDF1(key + passphrase, salt, 8, 1,
                                  Crypto.Hash.MD5)
                    keyobj = DES3.new(key, Crypto.Cipher.DES3.MODE_CBC, salt)
                else:
                    raise ValueError("Unsupport PEM encryption algorithm.")
                lines = lines[2:]

            der = binascii.a2b_base64(b('').join(lines[1:-1]))
            if keyobj:
                der = keyobj.decrypt(der)
                padding = bord(der[-1])
                der = der[:-padding]
            return self._importKeyDER(der)

        if externKey.startswith(b('ssh-rsa ')):
            # This is probably an OpenSSH key
            keystring = binascii.a2b_base64(externKey.split(b(' '))[1])
            keyparts = []
            while len(keystring) > 4:
                l = struct.unpack(">I", keystring[:4])[0]
                keyparts.append(keystring[4:4 + l])
                keystring = keystring[4 + l:]
            e = bytes_to_long(keyparts[1])
            n = bytes_to_long(keyparts[2])
            return self.construct([n, e])
        if bord(externKey[0]) == 0x30:
            # This is probably a DER encoded key
            return self._importKeyDER(externKey)

        raise ValueError("RSA key format is not supported")
Esempio n. 9
0
    def digest(self, text):
        """digest(text:string) : [string]

        Perform the All-or-Nothing package transform on the given
        string.  Output is a list of message blocks describing the
        transformed text, where each block is a string of bit length equal
        to the ciphermodule's block_size.
        """

        # generate a random session key and K0, the key used to encrypt the
        # hash blocks.  Rivest calls this a fixed, publically-known encryption
        # key, but says nothing about the security implications of this key or
        # how to choose it.
        key = self._inventkey(self.__key_size)
        K0 = self.__K0digit * self.__key_size

        # we need two cipher objects here, one that is used to encrypt the
        # message blocks and one that is used to encrypt the hashes.  The
        # former uses the randomly generated key, while the latter uses the
        # well-known key.
        mcipher = self.__newcipher(key)
        hcipher = self.__newcipher(K0)

        # Pad the text so that its length is a multiple of the cipher's
        # block_size.  Pad with trailing spaces, which will be eliminated in
        # the undigest() step.
        block_size = self.__ciphermodule.block_size
        padbytes = block_size - (len(text) % block_size)
        text = text + b(' ') * padbytes

        # Run through the algorithm:
        # s: number of message blocks (size of text / block_size)
        # input sequence: m1, m2, ... ms
        # random key K' (`key' in the code)
        # Compute output sequence: m'1, m'2, ... m's' for s' = s + 1
        # Let m'i = mi ^ E(K', i) for i = 1, 2, 3, ..., s
        # Let m's' = K' ^ h1 ^ h2 ^ ... hs
        # where hi = E(K0, m'i ^ i) for i = 1, 2, ... s
        #
        # The one complication I add is that the last message block is hard
        # coded to the number of padbytes added, so that these can be stripped
        # during the undigest() step
        s = divmod(len(text), block_size)[0]
        blocks = []
        hashes = []
        for i in range(1, s + 1):
            start = (i - 1) * block_size
            end = start + block_size
            mi = text[start:end]
            assert len(mi) == block_size
            cipherblock = mcipher.encrypt(long_to_bytes(i, block_size))
            mticki = bytes_to_long(mi) ^ bytes_to_long(cipherblock)
            blocks.append(mticki)
            # calculate the hash block for this block
            hi = hcipher.encrypt(long_to_bytes(mticki ^ i, block_size))
            hashes.append(bytes_to_long(hi))

        # Add the padbytes length as a message block
        i = i + 1
        cipherblock = mcipher.encrypt(long_to_bytes(i, block_size))
        mticki = padbytes ^ bytes_to_long(cipherblock)
        blocks.append(mticki)

        # calculate this block's hash
        hi = hcipher.encrypt(long_to_bytes(mticki ^ i, block_size))
        hashes.append(bytes_to_long(hi))

        # Now calculate the last message block of the sequence 1..s'.  This
        # will contain the random session key XOR'd with all the hash blocks,
        # so that for undigest(), once all the hash blocks are calculated, the
        # session key can be trivially extracted.  Calculating all the hash
        # blocks requires that all the message blocks be received, thus the
        # All-or-Nothing algorithm succeeds.
        mtick_stick = bytes_to_long(key) ^ reduce(operator.xor, hashes)
        blocks.append(mtick_stick)

        # we convert the blocks to strings since in Python, byte sequences are
        # always represented as strings.  This is more consistent with the
        # model that encryption and hash algorithms always operate on strings.
        return [
            long_to_bytes(i, self.__ciphermodule.block_size) for i in blocks
        ]
Esempio n. 10
0
            usage(0)
        elif opt in ('-c', '--cipher'):
            ciphermodule = arg
        elif opt in ('-l', '--aslong'):
            aslong = 1

    # ugly hack to force __import__ to give us the end-path module
    module = __import__('Crypto.Cipher.' + ciphermodule, None, None, ['new'])

    x = AllOrNothing(module)
    print('Original text:\n==========')
    print(__doc__)
    print('==========')
    msgblocks = x.digest(b(__doc__))
    print('message blocks:')
    for i, blk in zip(list(range(len(msgblocks))), msgblocks):
        # base64 adds a trailing newline
        print('    %3d' % i, end=' ')
        if aslong:
            print(bytes_to_long(blk))
        else:
            print(base64.encodestring(blk)[:-1])
    #
    # get a new undigest-only object so there's no leakage
    y = AllOrNothing(module)
    text = y.undigest(msgblocks)
    if text == b(__doc__):
        print('They match!')
    else:
        print('They differ!')