Exemple #1
0
def crypt_CFB(instream, outstream, algorithm, key, register, direction):
    """'Crypt a string in cipher-feedback mode.

    :Parameters:
        - `instream`: StringIO/file incoming
        - `outstream`: StringIO/file outgoing
        - `algorithm`: integer symmetric cipher constant
        - `key`: string encryption/decryption key
        - `register`: string initialization vector (IV) to feed register
        - `direction`: string 'encrypt' or 'decrypt' setting CFB mode

    :Returns: string ciphertext or cleartext

    OpenPGP performs CFB shifts on blocks of characters the same size
    as the block used by the symmetric cipher - for example, CAST5
    works on 64-bit blocks, therefore CAST5 CFB shifts use 8 bytes at
    a time (the remaining cleartext bytes that do not completely fill
    an 8-byte block at the end of a message are XOR'ed with the
    "left-most" bytes of the encrypted mask).
    """
    ciphermod = _import_cipher(algorithm)
    cipher = ciphermod.new(key, ciphermod.MODE_ECB)
    encrypt = cipher.encrypt
    shift = ciphermod.block_size # number of bytes to process (normally 8)

    # after tweaking, there's still not much difference in speed (~2% max)
    int2str = STN.int2str
    str2int = STN.str2int
    apply_mask = lambda c,m: int2str(str2int(c) ^ str2int(m))
 
    if register is None:
        register = STN.prepad(shift) # use an IV full of 0x00

    if shift > len(register):
        raise PGPCryptoError, "CFB shift amount->(%s) can't be larger than the feedback register->(%s)." % (shift, len(register))
  
    while True:
        inblock = instream.read(shift) # block size = shift size
        chunk = StringIO()

        if inblock:
            mask = encrypt(register)
            chunk.seek(0)

            for i, c in enumerate(inblock):
                chunk.write(apply_mask(c, mask[i]))

            chunk.truncate()
            chunk.seek(0)
            outblock = chunk.read()

            if 'encrypt' == direction:
                register = outblock
            elif 'decrypt' == direction:
                register = inblock

            outstream.write(outblock)

        else:
            break
Exemple #2
0
def __cat_subpkt_block(subpkts):
    subpkt_d = ''.join([x._d for x in subpkts])
    subpkt_d_len = STN.int2str(len(subpkt_d))

    if 2 >= len(subpkt_d_len):
        return STN.prepad(2, subpkt_d_len) + subpkt_d
    else:
        raise PGPValueError, "Subpacket block length (%s) is unacceptable." % len(subpkt_d_len)
Exemple #3
0
def __cat_subpkt_block(subpkts):
    subpkt_d = ''.join([x._d for x in subpkts])
    subpkt_d_len = STN.int2str(len(subpkt_d))

    if 2 >= len(subpkt_d_len):
        return STN.prepad(2, subpkt_d_len) + subpkt_d
    else:
        raise PGPValueError, "Subpacket block length (%s) is unacceptable." % len(
            subpkt_d_len)
Exemple #4
0
def create_LiteralDataBody(*args, **kwords):
    """Create a LiteralDataBody instance.

    :Parameters:
        - `args`: parameter list, will accept keyword dictionary as
          args[0]
        - `kwords`: keyword parameters

    :Keywords:
        - `data`: string of literal data
        - `modified`: integer timestamp of file modification
        - `format`: character 'b' or 't' indicating binary or text
        - `filename`: optional filename associated with data

    :Returns: `LiteralDataBody` instance

    Use the filename '_CONSOLE' to signal extra-careful handling of
    output.
    """
    try:
        if isinstance(args[0], dict):
            kwords = args[0]
    except IndexError:
        pass

    data = kwords.get('data')

    if not data:
        data = ''

    modified = kwords.setdefault('modified', 0)
    format = kwords.setdefault('format', 'b')
    filename = kwords.setdefault('filename', 'outfile')

    if format not in ['b', 't']:
        raise PGPValueError, "Literal data format must be 'b' or 't'. Received->(%s)" % str(format)

    fnlen_d = STN.int2str(len(filename))

    if 1 < len(fnlen_d):
        raise PGPValueError, "Filename length (%s) exceeded 1 octet capacity." % len(fnlen_d)

    modified_d = STN.prepad(4, STN.int2str(modified))
    d = ''.join([format, fnlen_d, filename, modified_d, data])

    return LiteralDataBody(d)
Exemple #5
0
def decrypt(encpkt, passphrase='', sespkt=None, keypkt=None):
    """Decrypt messages in symmetrically encrypted packets (types 9 & 18).

    :Parameters:
        - `encpkt`: packet containing encrypted data (symmetrically
          encrypted or integrity protected packet, types 9 & 18)
        - `passphrase`: string decryption passphrase (see below)
        - `sespkt`: optional session key packet
        - `keypkt`: optional public key packet

    :Returns: string cleartext

    :Exceptions:
        - `PGPError`: implementation error
        - `PGPDecryptionFailure`: decryption failed
        - `PGPSessionDecryptionFailure`: session decryption failed

    This is the all-in-one handler for "normal" decryption - that is,
    decrypting symmetrically encrypted (type 9) and symmetrically
    encrypted integrity protected (type 18) data. By consolidating 
    everything to one function it will be easier (hopefully) to manage
    secure data handling in the future.

    Because this function focuses on decrypting information in packet
    types 9 and 18, decrypted data is by definition (or "will be", due
    to the mechanics of this function) the data used to build an
    OpenPGP message (not the message instance). It is up to the API
    layer to automate things like "if compressed, decompress" or "if
    signed, verify."
    """
    result = key = algorithm = None # key & algo set to force integrity failure 

    if sespkt:
        ses = sespkt.body

        if PKT_PUBKEYSESKEY == sespkt.tag.type:

            if ses.keyid == keypkt.body.id:

                try:
                    seckeys = decrypt_secret_key(keypkt, passphrase)

                except PGPError: # catch MPI value error due to ..
                    raise PGPCryptoError("Public key encrypted session key checksum failed.")

                if keypkt.body.alg in [ASYM_RSA_E, ASYM_RSA_EOS]:
                    cipher_tuple = (ses.RSA_me_modn.value,)
                    key_tuple = (keypkt.body.RSA_n.value, seckeys[0])

                elif keypkt.body.alg in [ASYM_ELGAMAL_E, ASYM_ELGAMAL_EOS]:
                    cipher_tuple = (ses.ELGAMAL_gk_modp.value, ses.ELGAMAL_myk_modp.value)
                    key_tuple = (keypkt.body.ELGAMAL_p.value, seckeys[0])

                else:
                    raise NotImplementedError("Unsupported public encryption algorithm->(%s)." % keypkt.body.alg)

            else: # shouldn't happen, programmer error
                raise PGPCryptoError("The public encryption key did not match the session key target.")

            # should be ready to decrypt session key with key tuple
            padded_key = decrypt_public(ses.alg_pubkey, key_tuple, cipher_tuple)

            if '\x02' == padded_key[0]: # handle EME-PKCS1-v1_5 encoding
                idx = padded_key.find('\x00') # 0x00 used as padding separator

                if -1 != idx and 8 <= idx:
                    message = padded_key[idx+1:]
                    algorithm = STN.str2int(message[0]) # required for both..
                    chksum = STN.str2int(message[-2:])
                    key = message[1:len(message)-2] # ..symencdata and symencintdata

                    if chksum != STN.checksum(key):
                        raise PGPCryptoError("Public Key encrypted session key checksum failed.")

                else:
                    raise PGPCryptoError("Misplaced \\x00 in session key padding, located at index->(%s)." % idx)

            else:
                raise PGPCryptoError("Session key didn't start with \\x02, received->()." % hex(ord(padded_key[0])))

        elif PKT_SYMKEYSESKEY == sespkt.tag.type: # using symmetric key session key
            algorithm = ses.alg
            key = string2key(ses.s2k, algorithm, passphrase)

            if ses.has_key:
                iv = STN.prepad(_import_cipher(algorithm).block_size)
                padded_key = decrypt_symmetric(algorithm, key, ciphertext, iv)
                algorithm = padded_key[0]
                key = padded_key[1:]

        else:
            raise NotImplementedError("Unrecognized session key type-(%s)." % sespkt.tag.type)

    # 'algorithm' & 'key' should be set, it's time to decrypt the message
    if PKT_SYMENCINTDATA == encpkt.tag.type:
        bs = _import_cipher(algorithm).block_size
        iv = STN.prepad(bs)
        cleartext = decrypt_symmetric(algorithm, key, encpkt.body.data, iv)
        prefix = cleartext[:bs+2]
        clearmsg_d = cleartext[bs+2:-22]
        mdc_d = cleartext[-22:]
        mdc = mdc_d[-20:]

        if mdc != sha.new(''.join([prefix, clearmsg_d, '\xd3\x14'])).digest():
            raise PGPCryptoError("Integrity hash check failed.")

    elif PKT_SYMENCDATA == encpkt.tag.type:

        if None == key == algorithm: # non-integrity allows default key & alg
            key = md5.new(passphrase).digest
            algorithm = SYM_IDEA

        clearmsg_d = decrypt_symmetric_resync(algorithm, key, encpkt.body.data)

    return clearmsg_d
Exemple #6
0
    int2str = STN.int2str
    ctx = StringIO() # signature context to hash
    ctx_write = ctx.write

    if 3 == version:                                     ################### v3
        ctx_write(int2str(sigtype)[0])                   # signature type
        ctx_write(int2str(kwords['created'])[:4])        # creation timestamp

    elif 4 == version:                                   ################### v4
        ctx_write('\x04')                                # version
        ctx_write(int2str(sigtype)[0])                   # signature type
        ctx_write(int2str(keyalg)[0])                    # public key alg
        ctx_write(int2str(hashalg)[0])                   # hash algorithm
        subhash = ''.join([x._d for x in hashed_subpkts])
        ctx_write(STN.prepad(2, int2str(len(subhash))))  # hashed len
        ctx_write(subhash)                               # hashed subpkts
        ctx_len = ctx.tell()
        ctx_write('\x04\xff')                            # start trailer
        ctx_write(STN.int2quadoct(ctx_len)[-4:])         # hashed data length
    
    else:
        raise NotImplementedError("Signature version->(%s) is not supported." % version)

    ctx.seek(0)
    ctx_hash = hash_context(version, hashalg, sigtype, ctx, target, primary)
    ctx.close()

    if keyalg in [ASYM_RSA_S, ASYM_RSA_EOS]:
        ctx_hash = pad_rsa(hashalg, ctx_hash, signer.body.RSA_n.bit_length)
        keytup = signer.body.RSA_n.value, seckey