Beispiel #1
0
def encrypt_public_session(keypkt, key, symalg):
    """Create a public-key encrypted session key.

    :Parameters:
        - `keypkt`: either an `OpenPGP.packet.PublicKey.PublicKey` (or
          subclass) instance or encryption passphrase string
        - `key`: string encryptrion algorithm used for `symalg`
        - `symalg`: integer symmetric encryption algorithm constant

    :Returns: `OpenPGP.packet.PublicKeyEncryptedSessionKey.PublicKeyEncryptedSessionKey` instance
    """
    from openpgp.sap.pkt.Packet import create_Packet
    from openpgp.sap.pkt.PublicKeyEncryptedSessionKey import create_PublicKeyEncryptedSessionKeyBody

    pubalg = keypkt.body.alg
    rnd_prefix = []

    i = 0 # fixing the "intended length" business to 127
    while i <= 63 - len(key): # seems proper, but probably inefficient
        rnd_byte = gen_random(1)

        if '\x00' != rnd_byte:
            rnd_prefix.append(rnd_byte)
            i += 1

    chksum = STN.int2str(STN.checksum(key))[:2]

    if pubalg in [ASYM_RSA_EOS, ASYM_RSA_E]:
        key_tup = (keypkt.body.RSA_n.value, keypkt.body.RSA_e.value)

    elif pubalg in [ASYM_ELGAMAL_EOS, ASYM_ELGAMAL_E]:
        key_tup = (keypkt.body.ELGAMAL_p.value, keypkt.body.ELGAMAL_g.value, keypkt.body.ELGAMAL_y.value)

    else:
        raise NotImplementedError("Unsupported public key algorithm->(%s)." % pubalg)

    padded_key = ''.join(['\x02', ''.join(rnd_prefix), '\x00',
                          STN.int2str(symalg)[0], key, chksum])

    cipher_tup = encrypt_public(pubalg, padded_key, key_tup)

    sesbody = create_PublicKeyEncryptedSessionKeyBody(
        keyid=keypkt.body.id, alg=pubalg, mpis=cipher_tup)

    return create_Packet(PKT_PUBKEYSESKEY, sesbody._d)
Beispiel #2
0
def decrypt_secret_key(keypkt, passphrase=''):
    """Retrieve decryption key values from a secret key packet.

    :Parameters:
        - `keypkt`: `OpenPGP.packet.SecretKey.SecretKey` instance or
          `OpenPGP.packet.SecretSubkey.SecretSubkey` instance
        - `passphrase`: optional decryption string

    :Returns: tuple of secret key integer values

    :Exceptions:
        - `PGPKeyDecryptionFailure`: secret key values did not encrypt
          properly

    Secret key tuples:
        - RSA keys: (d, p, q, u)
        - ElGamal keys: (x, )
        - DSA keys: (x, )

    :note: As far as key decryption goes, the only real check performed is the
        internal MPI check that the header, size, etc. make sense. Chances are
        slight that a failed decryption will render sensible MPIs. This is why
        the integrity checks are so important.
    """
    if not hasattr(keypkt.body, 's2k_usg'):
        raise PGPCryptoError("Key material does not contain secret key values (missing s2k usage).")

    if 0 == keypkt.body.s2k_usg:
        if keypkt.body.alg in [ASYM_RSA_E, ASYM_RSA_EOS]:
            key_tuple = (keypkt.body.RSA_d.value, keypkt.body.RSA_p.value, keypkt.body.RSA_q.value, keypkt.body.RSA_u.value)
        elif keypkt.body.alg in [ASYM_ELGAMAL_E, ASYM_ELGAMAL_EOS]:
            key_tuple = keypkt.body.ELGAMAL_x.value, # coerce tuple
        elif keypkt.body.alg in [ASYM_DSA]:
            key_tuple = keypkt.body.DSA_x.value, # coerce tuple

    else: # ..'idx' comes into play during the integrity check

        if 3 >= keypkt.body.version:

            if keypkt.body.alg in [ASYM_RSA_E, ASYM_RSA_EOS]:
                alg = keypkt.body.alg_sym

                if keypkt.body.s2k_usg in [254, 255]:
                    k = string2key(keypkt.body.s2k, alg, passphrase)

                else:
                    k = md5.new(passphrase).digest()

                # extra work required since MPIs integers are encrypted w/out
                # their lengths
                # create MPIs w/ encrypted integer octets 
                secRSA_d, idx = MPI.strcalc_mpi(keypkt.body._enc_d, 0)
                secRSA_p, idx = MPI.strcalc_mpi(keypkt.body._enc_d, idx)
                secRSA_q, idx = MPI.strcalc_mpi(keypkt.body._enc_d, idx)
                secRSA_u, idx = MPI.strcalc_mpi(keypkt.body._enc_d, idx)
                # decrypt integer octets
                RSA_d_int_d = decrypt_symmetric(alg, k, secRSA_d._int_d, keypkt.body.iv)
                RSA_p_int_d = decrypt_symmetric(alg, k, secRSA_d._int_d, keypkt.body.iv)
                RSA_q_int_d = decrypt_symmetric(alg, k, secRSA_d._int_d, keypkt.body.iv)
                RSA_u_int_d = decrypt_symmetric(alg, k, secRSA_d._int_d, keypkt.body.iv)
                # translate integer values
                RSA_d_value = STN.str2int(RSA_d_int_d)
                RSA_p_value = STN.str2int(RSA_p_int_d)
                RSA_q_value = STN.str2int(RSA_q_int_d)
                RSA_u_value = STN.str2int(RSA_u_int_d)

                key_tuple = (RSA_d_value, RSA_p_value, RSA_q_value, RSA_u_value)
                sec_d = ''.join([secRSA_d._d[:2], RSA_d_int_d, secRSA_p._d[:2], RSA_p_int_d, secRSA_q._d[:2], RSA_q_int_d, secRSA_q._d[:2], RSA_q_int_d])
            else:
                raise NotImplementedError("Unsupported v3 decryption key algorithm->(%s)." % keypkt.body.alg)

        elif 4 == keypkt.body.version:
            alg = keypkt.body.alg_sym
            k = string2key(keypkt.body.s2k, alg, passphrase)
            sec_d = decrypt_symmetric(alg, k, keypkt.body._enc_d, keypkt.body.iv)

            if keypkt.body.alg in [ASYM_RSA_E, ASYM_RSA_EOS, ASYM_RSA_S]:
                RSA_d, idx = MPI.strcalc_mpi(sec_d, 0)
                RSA_p, idx = MPI.strcalc_mpi(sec_d[idx:], idx)
                RSA_q, idx = MPI.strcalc_mpi(sec_d[idx:], idx)
                RSA_u, idx = MPI.strcalc_mpi(sec_d[idx:], idx)
                key_tuple = (RSA_d.value, RSA_p.value, RSA_q.value, RSA_u.value)

            elif keypkt.body.alg in [ASYM_ELGAMAL_E, ASYM_ELGAMAL_EOS]:
                ELGAMAL_x, idx = MPI.strcalc_mpi(sec_d, 0)
                key_tuple = ELGAMAL_x.value, # coerce tuple

            elif keypkt.body.alg in [ASYM_DSA]:
                DSA_x, idx = MPI.strcalc_mpi(sec_d, 0)
                key_tuple = DSA_x.value, # coerce tuple

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

        else:
            raise NotImplementedError, "Unsupported key version->(%s)." % keypkt.body.version

        # check integrity
        if 254 == keypkt.body.s2k_usg:

            if sec_d[idx:] != sha.new(sec_d[:idx]).digest():
                raise PGPCryptoError("Integrity hash check failed.")

        elif 255 == keypkt.body.s2k_usg:

            if keypkt.body.chksum != STN.checksum(sec_d):
                raise PGPCryptoError("Integrity checksum failed.")

    return key_tuple
Beispiel #3
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