This file is indexed.

/usr/lib/python3/dist-packages/pdfrw/crypt.py is in python3-pdfrw 0.4-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# A part of pdfrw (https://github.com/pmaupin/pdfrw)
# Copyright (C) 2017  Jon Lund Steffensen
# MIT license -- See LICENSE.txt for details

from __future__ import division

import hashlib
import struct

try:
    from Crypto.Cipher import ARC4, AES
    HAS_CRYPTO = True
except ImportError:
    HAS_CRYPTO = False

from .objects import PdfDict, PdfName

_PASSWORD_PAD = (
    '(\xbfN^Nu\x8aAd\x00NV\xff\xfa\x01\x08'
    '..\x00\xb6\xd0h>\x80/\x0c\xa9\xfedSiz')


def streamobjects(mylist, isinstance=isinstance, PdfDict=PdfDict):
    for obj in mylist:
        if isinstance(obj, PdfDict) and obj.stream is not None:
            yield obj


def create_key(password, doc):
    """Create an encryption key (Algorithm 2 in PDF spec)."""
    key_size = int(doc.Encrypt.Length or 40) // 8
    padded_pass = (password + _PASSWORD_PAD)[:32]
    hasher = hashlib.md5()
    hasher.update(padded_pass)
    hasher.update(doc.Encrypt.O.to_bytes())
    hasher.update(struct.pack('<i', int(doc.Encrypt.P)))
    hasher.update(doc.ID[0].to_bytes())
    temp_hash = hasher.digest()

    if int(doc.Encrypt.R or 0) >= 3:
        for _ in range(50):
            temp_hash = hashlib.md5(temp_hash[:key_size]).digest()

    return temp_hash[:key_size]


def create_user_hash(key, doc):
    """Create the user password hash (Algorithm 4/5)."""
    revision = int(doc.Encrypt.R or 0)
    if revision < 3:
        cipher = ARC4.new(key)
        return cipher.encrypt(_PASSWORD_PAD)
    else:
        hasher = hashlib.md5()
        hasher.update(_PASSWORD_PAD)
        hasher.update(doc.ID[0].to_bytes())
        temp_hash = hasher.digest()

        for i in range(20):
            temp_key = ''.join(chr(i ^ ord(x)) for x in key)
            cipher = ARC4.new(temp_key)
            temp_hash = cipher.encrypt(temp_hash)

        return temp_hash


def check_user_password(key, doc):
    """Check that the user password is correct (Algorithm 6)."""
    expect_user_hash = create_user_hash(key, doc)
    revision = int(doc.Encrypt.R or 0)
    if revision < 3:
        return doc.Encrypt.U.to_bytes() == expect_user_hash
    else:
        return doc.Encrypt.U.to_bytes()[:16] == expect_user_hash


class AESCryptFilter(object):
    """Crypt filter corresponding to /AESV2."""
    def __init__(self, key):
        self._key = key

    def decrypt_data(self, num, gen, data):
        """Decrypt data (string/stream) using key (Algorithm 1)."""
        key_extension = struct.pack('<i', num)[:3]
        key_extension += struct.pack('<i', gen)[:2]
        key_extension += 'sAlT'
        temp_key = self._key + key_extension
        temp_key = hashlib.md5(temp_key).digest()

        iv = data[:AES.block_size]
        cipher = AES.new(temp_key, AES.MODE_CBC, iv)
        decrypted = cipher.decrypt(data[AES.block_size:])

        # Remove padding
        pad_size = ord(decrypted[-1])
        assert 1 <= pad_size <= 16
        return decrypted[:-pad_size]


class RC4CryptFilter(object):
    """Crypt filter corresponding to /V2."""
    def __init__(self, key):
        self._key = key

    def decrypt_data(self, num, gen, data):
        """Decrypt data (string/stream) using key (Algorithm 1)."""
        new_key_size = min(len(self._key) + 5, 16)
        key_extension = struct.pack('<i', num)[:3]
        key_extension += struct.pack('<i', gen)[:2]
        temp_key = self._key + key_extension
        temp_key = hashlib.md5(temp_key).digest()[:new_key_size]

        cipher = ARC4.new(temp_key)
        return cipher.decrypt(data)


class IdentityCryptFilter(object):
    """Identity crypt filter (pass through with no encryption)."""
    def decrypt_data(self, num, gen, data):
        return data


def decrypt_objects(objects, default_filter, filters):
    """Decrypt list of stream objects.

    The parameter default_filter specifies the default filter to use. The
    filters parameter is a dictionary of alternate filters to use when the
    object specfies an alternate filter locally.
    """
    for obj in streamobjects(objects):
        if getattr(obj, 'decrypted', False):
            continue

        filter = default_filter

        # Check whether a locally defined crypt filter should override the
        # default filter.
        ftype = obj.Filter
        if ftype is not None:
            if not isinstance(ftype, list):
                ftype = [ftype]
            if len(ftype) >= 1 and ftype[0] == PdfName.Crypt:
                ftype = ftype[1:]
                parms = obj.DecodeParms or obj.DP
                filter = filters[parms.Name]

        num, gen = obj.indirect
        obj.stream = filter.decrypt_data(num, gen, obj.stream)
        obj.private.decrypted = True
        obj.Filter = ftype or None