def decrypt(content, salt=None, key=None,
            private_key=None, dh=None, auth_secret=None,
            keyid=None, keylabel="P-256",
            rs=4096, version="aes128gcm"):
    """
    Decrypt a data block

    :param content: Data to be decrypted
    :type content: str
    :param salt: Encryption salt
    :type salt: str
    :param key: local public key
    :type key: str
    :param private_key: DH private key
    :type key: object
    :param keyid: Internal key identifier for private key info
    :type keyid: str
    :param dh: Remote Diffie Hellman sequence (omit for aes128gcm)
    :type dh: str
    :param rs: Record size
    :type rs: int
    :param auth_secret: Authorization secret
    :type auth_secret: str
    :param version: ECE Method version
    :type version: enumerate('aes128gcm', 'aesgcm', 'aesgcm128')
    :return: Decrypted message content
    :rtype str

    """
    def parse_content_header(content):
        """Parse an aes128gcm content body and extract the header values.

        :param content: The encrypted body of the message
        :type content: str

        """
        id_len = struct.unpack("!B", content[20:21])[0]
        return {
            "salt": content[:16],
            "rs": struct.unpack("!L", content[16:20])[0],
            "keyid": content[21:21 + id_len],
            "content": content[21 + id_len:],
        }

    def decrypt_record(key, nonce, counter, content):
        decryptor = Cipher(
            algorithms.AES(key),
            modes.GCM(iv(nonce, counter), tag=content[-TAG_LENGTH:]),
            backend=default_backend()
        ).decryptor()
        return decryptor.update(content[:-TAG_LENGTH]) + decryptor.finalize()

    def unpad_legacy(data):
        pad_size = versions[version]['pad']
        pad = functools.reduce(
            lambda x, y: x << 8 | y, struct.unpack(
                "!" + ("B" * pad_size), data[0:pad_size])
        )
        if pad_size + pad > len(data) or \
           data[pad_size:pad_size+pad] != (b"\x00" * pad):
            raise ECEException(u"Bad padding")
        return data[pad_size + pad:]

    def unpad(data, last):
        i = len(data) - 1
        for i in range(len(data) - 1, -1, -1):
            v = struct.unpack('B', data[i:i+1])[0]
            if v != 0:
                if not last and v != 1:
                    raise ECEException(u'record delimiter != 1')
                if last and v != 2:
                    raise ECEException(u'last record delimiter != 2')
                return data[0:i]
        raise ECEException(u'all zero record plaintext')

    if version not in versions:
        raise ECEException(u"Invalid version")

    overhead = versions[version]['pad']
    if version == "aes128gcm":
        try:
            content_header = parse_content_header(content)
        except Exception:
            raise ECEException("Could not parse the content header")
        salt = content_header['salt']
        rs = content_header['rs']
        keyid = content_header['keyid']
        if private_key is not None and not dh:
            dh = keyid
        else:
            keyid = keyid.decode('utf-8')
        content = content_header['content']
        overhead += 16

    (key_, nonce_) = derive_key("decrypt", version=version,
                                salt=salt, key=key,
                                private_key=private_key, dh=dh,
                                auth_secret=auth_secret,
                                keyid=keyid, keylabel=keylabel)
    if rs <= overhead:
        raise ECEException(u"Record size too small")
    chunk = rs
    if version != "aes128gcm":
        chunk += 16  # account for tags in old versions
        if len(content) % chunk == 0:
            raise ECEException(u"Message truncated")

    result = b''
    counter = 0
    try:
        for i in list(range(0, len(content), chunk)):
            data = decrypt_record(key_, nonce_, counter, content[i:i + chunk])
            if version == 'aes128gcm':
                last = (i + chunk) >= len(content)
                result += unpad(data, last)
            else:
                result += unpad_legacy(data)
            counter += 1
    except InvalidTag as ex:
        raise ECEException("Decryption error: {}".format(repr(ex)))
    return result
def decrypt ( content , salt = None , key = None , private_key = None , dh = None , auth_secret = None , keyid = None , keylabel = "P-256" , rs = 4096 , version = "aes128gcm" ) : def parse_content_header ( content ) : id_len = struct . unpack ( "!B" , content [ 20 : 21 ] ) [ 0 ] return { "salt" : content [ : 16 ] , "rs" : struct . unpack ( "!L" , content [ 16 : 20 ] ) [ 0 ] , "keyid" : content [ 21 : 21 + id_len ] , "content" : content [ 21 + id_len : ] , } def decrypt_record ( key , nonce , counter , content ) : decryptor = Cipher ( algorithms . AES ( key ) , modes . GCM ( iv ( nonce , counter ) , tag = content [ - TAG_LENGTH : ] ) , backend = default_backend ( ) ) . decryptor ( ) return decryptor . update ( content [ : - TAG_LENGTH ] ) + decryptor . finalize ( ) def unpad_legacy ( data ) : pad_size = versions [ version ] [ 'pad' ] pad = functools . reduce ( lambda x , y : x << 8 | y , struct . unpack ( "!" + ( "B" * pad_size ) , data [ 0 : pad_size ] ) ) if pad_size + pad > len ( data ) or data [ pad_size : pad_size + pad ] != ( b"\x00" * pad ) : raise ECEException ( u"Bad padding" ) return data [ pad_size + pad : ] def unpad ( data , last ) : i = len ( data ) - 1 for i in range ( len ( data ) - 1 , - 1 , - 1 ) : v = struct . unpack ( 'B' , data [ i : i + 1 ] ) [ 0 ] if v != 0 : if not last and v != 1 : raise ECEException ( u'record delimiter != 1' ) if last and v != 2 : raise ECEException ( u'last record delimiter != 2' ) return data [ 0 : i ] raise ECEException ( u'all zero record plaintext' ) if version not in versions : raise ECEException ( u"Invalid version" ) overhead = versions [ version ] [ 'pad' ] if version == "aes128gcm" : try : content_header = parse_content_header ( content ) except Exception : raise ECEException ( "Could not parse the content header" ) salt = content_header [ 'salt' ] rs = content_header [ 'rs' ] keyid = content_header [ 'keyid' ] if private_key is not None and not dh : dh = keyid else : keyid = keyid . decode ( 'utf-8' ) content = content_header [ 'content' ] overhead += 16 ( key_ , nonce_ ) = derive_key ( "decrypt" , version = version , salt = salt , key = key , private_key = private_key , dh = dh , auth_secret = auth_secret , keyid = keyid , keylabel = keylabel ) if rs <= overhead : raise ECEException ( u"Record size too small" ) chunk = rs if version != "aes128gcm" : chunk += 16 if len ( content ) % chunk == 0 : raise ECEException ( u"Message truncated" ) result = b'' counter = 0 try : for i in list ( range ( 0 , len ( content ) , chunk ) ) : data = decrypt_record ( key_ , nonce_ , counter , content [ i : i + chunk ] ) if version == 'aes128gcm' : last = ( i + chunk ) >= len ( content ) result += unpad ( data , last ) else : result += unpad_legacy ( data ) counter += 1 except InvalidTag as ex : raise ECEException ( "Decryption error: {}" . format ( repr ( ex ) ) ) return result
Decrypt a data block

    :param content: Data to be decrypted
    :type content: str
    :param salt: Encryption salt
    :type salt: str
    :param key: local public key
    :type key: str
    :param private_key: DH private key
    :type key: object
    :param keyid: Internal key identifier for private key info
    :type keyid: str
    :param dh: Remote Diffie Hellman sequence (omit for aes128gcm)
    :type dh: str
    :param rs: Record size
    :type rs: int
    :param auth_secret: Authorization secret
    :type auth_secret: str
    :param version: ECE Method version
    :type version: enumerate('aes128gcm', 'aesgcm', 'aesgcm128')
    :return: Decrypted message content
    :rtype str