This file is indexed.

/usr/lib/python2.7/dist-packages/otrapps/chatsecure.py is in keysync 0.2.2-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
151
152
153
#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''a module for reading and writing ChatSecure's OTR key data'''

from __future__ import print_function
import hashlib
import os
import sys
import pyjavaproperties
import subprocess
import tempfile

if __name__ == '__main__':
    sys.path.insert(0, "../") # so the main() test suite can find otrapps module
import otrapps.util

class ChatSecureProperties():

    path = '/data/data/info.guardianproject.otr.app.im/files/otr_keystore'
    keyfile = 'otr_keystore'
    encryptedkeyfile = keyfile + '.ofcaes'
    files = (keyfile, encryptedkeyfile)

    password = None

    @staticmethod
    def parse(filename):
        '''parse the given file into the standard keydict'''
        # the parsing and generation is done in separate passes so that
        # multiple properties are combined into a single keydict per account,
        # containing all of the fields
        p = pyjavaproperties.Properties()
        p.load(open(filename))
        parsed = []
        for item in p.items():
            propkey = item[0]
            if propkey.endswith('.publicKey'):
                id = '.'.join(propkey.split('.')[0:-1])
                parsed.append(('public-key', id, item[1]))
            elif propkey.endswith('.publicKey.verified'):
                keylist = propkey.split('.')
                fingerprint = keylist[-3]
                id = '.'.join(keylist[0:-3])
                parsed.append(('verified', id, fingerprint))
            elif propkey.endswith('.privateKey'):
                id = '.'.join(propkey.split('.')[0:-1])
                parsed.append(('private-key', id, item[1]))
        # create blank keys for all IDs
        keydict = dict()
        for keydata in parsed:
            name = keydata[1]
            if not name in keydict:
                keydict[name] = dict()
                keydict[name]['name'] = name
                keydict[name]['protocol'] = 'prpl-jabber'
            if keydata[0] == 'private-key':
                cleaned = keydata[2].replace('\\n', '')
                numdict = otrapps.util.ParsePkcs8(cleaned)
                for num in ('g', 'p', 'q', 'x'):
                    keydict[name][num] = numdict[num]
            elif keydata[0] == 'verified':
                keydict[name]['verification'] = 'verified'
                fingerprint = keydata[2].lower()
                otrapps.util.check_and_set(keydict[name], 'fingerprint', fingerprint)
            elif keydata[0] == 'public-key':
                cleaned = keydata[2].replace('\\n', '')
                numdict = otrapps.util.ParseX509(cleaned)
                for num in ('y', 'g', 'p', 'q'):
                    keydict[name][num] = numdict[num]
                fingerprint = otrapps.util.fingerprint((numdict['y'], numdict['g'], numdict['p'], numdict['q']))
                otrapps.util.check_and_set(keydict[name], 'fingerprint', fingerprint)
        return keydict

    @staticmethod
    def write(keydict, savedir, password=None):
        '''given a keydict, generate a chatsecure file in the savedir'''
        p = pyjavaproperties.Properties()
        for name, key in keydict.items():
            # only include XMPP keys, since ChatSecure only supports XMPP
            # accounts, so we avoid spreading private keys around
            if key['protocol'] == 'prpl-jabber' or key['protocol'] == 'prpl-bonjour':
                if 'y' in key:
                    p.setProperty(key['name'] + '.publicKey', otrapps.util.ExportDsaX509(key))
                if 'x' in key:
                    if not password:
                        h = hashlib.sha256()
                        h.update(os.urandom(16)) # salt
                        h.update(bytes(key['x']))
                        password = h.digest().encode('base64')
                    p.setProperty(key['name'] + '.privateKey', otrapps.util.ExportDsaPkcs8(key))
            if 'fingerprint' in key:
                p.setProperty(key['name'] + '.fingerprint', key['fingerprint'])
            if 'verification' in key and key['verification'] != None:
                p.setProperty(key['name'] + '.' + key['fingerprint'].lower()
                              + '.publicKey.verified', 'true')
        fd, filename = tempfile.mkstemp()
        f = os.fdopen(fd, 'w')
        p.store(f)

        # if there is no password, then one has not been set, or there
        # are not private keys included in the file, so its a lower
        # risk file. Encryption only needs to protect the meta data,
        # not the private keys.  Therefore, its not as bad to generate
        # a "random" password here
        if not password:
            password = os.urandom(32).encode('base64')

        # create passphrase file from the first private key
        cmd = ['openssl', 'aes-256-cbc', '-pass', 'stdin', '-in', filename,
               '-out', os.path.join(savedir, 'otr_keystore.ofcaes')]
        p = subprocess.Popen(cmd, stdin=subprocess.PIPE,
                             stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
        ChatSecureProperties.password = password
        print((p.communicate(password)))

    @staticmethod
    def _decrypt_ofcaes(ofcaes_filename, password):
        ''' Decrypt an encrypted key file (with user-supplied password).'''
        # It might be a bad idea to write out this unencrypted file.

        # get a tmp place to put the decrypted file
        fd, filename = tempfile.mkstemp()
        f = os.fdopen(fd, 'w')
        f.close()

        # same as above, but with the -d flag to decrypt
        cmd = ['openssl', 'aes-256-cbc', '-d', '-pass', 'stdin', '-in', ofcaes_filename,
       '-out', filename]
        p = subprocess.Popen(cmd, stdin=subprocess.PIPE,
                             stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
        p.communicate(password)
        return filename


#------------------------------------------------------------------------------#
# for testing from the command line:
def main(argv):
    import pprint

    print('ChatSecure stores its files in ' + ChatSecureProperties.path)

    if len(sys.argv) == 2:
        settingsfile = sys.argv[1]
    else:
        settingsfile = '../tests/chatsecure/otr_keystore'

    p = ChatSecureProperties.parse(settingsfile)
    print('----------------------------------------')
    pprint.pprint(p)
    print('----------------------------------------')

if __name__ == "__main__":
    main(sys.argv[1:])