diff --git a/README.md b/README.md index 802732a..f9858cf 100644 --- a/README.md +++ b/README.md @@ -21,23 +21,55 @@ virtualenv . source bin/activate pip install -r requirements.txt ``` +# Fork() -## Usage -pgpfs is simple to use. It is able to both `store` and `fetch` documents from -the keyserver. Here are examples of usage: +Patched pgpfs code from https://github.com/aestetix/pgpfs. +Has now: -### `store` -To store the file sample.mp3 and save the Key Allocation Table (kat) in -sample.kat, run: -``` -python pgpfs.py store sample.mp3 sample.kat -``` -This will create a file called sample.kat to be used when you want to retrieve -your file. +- argparse +- compression / decompression -### `fetch` -To fetch the file sample.mp3 from the keyserver, assuming the kat file is called -sample.kat, run: -``` -python pgpfs.py fetch sample.kat sample.mp3 -``` +# TODO + +- add threading +- add encryption + +# TODO for someone with time and skills + +- Building an vfs based on pgpfs, implementation ideas welcome :) + +# USAGE + +./pgpfs.py -h +usage: pgpfs.py [-h] -a ACTION [-k KAT] [-s STORE] [--keyserver KEYSERVER] + [--pgpfspath PGPFSPATH] [--gpgbinary PGPFSBIN] + +https://github.com/aestetix/pgpfs + +optional arguments: + -h, --help show this help message and exit + -a ACTION, --action ACTION + action to do: store or fetch + -k KAT, --katfile KAT + if fetch specify KAT file + -s STORE, --storefile STORE + if store specify file for upload + --keyserver KEYSERVER + specify keyserver + --pgpfspath PGPFSPATH + specify path for pgpfs + --gpgbinary PGPFSBIN specify path for pgpfs binary(patched gpg bin) + +# Upload +./pgpfs.py -a store -s somefile.ext -k somefile.kat + +# Download +./pgpfs.py -a store -s example.gif -k sample.kat + +# NOTE +Please note that you need the gpg patched code from here: +https://github.com/aestetix/gpg +Compile and place somewhere. Adjust --gpgbinary accoringly. + +Also you need to run an entropy source: +rndg -r /dev/urandom diff --git a/pgpfs.py b/pgpfs.py index 14044cd..2179865 100755 --- a/pgpfs.py +++ b/pgpfs.py @@ -1,127 +1,210 @@ -#!bin/python +#!/usr/bin/env python2 +# +# watch out, this is only possible with modified gpg version +# download this version as well from aestetix repo + """ pgpfs is a tool to turn your friendly pgp keyserver into a redundant persistant filesystem.""" +import re import os import sys -import re -import base64 -from hashlib import sha256 import gnupg +import base64 +import pylzma +import argparse +import threading -os.system('rm -rf gpg') -GPG = gnupg.GPG(gnupghome='gpg') -KEYSERVER = 'pgp.mit.edu' +from hashlib import sha256 + +# TODO adjust with pgpfspath +os.system('rm -fr .pgpfs') # trial and error lead to this number SPLIT_LENGTH = 986 -# store file +# pack file +def pack_file_to_disk(source_file): + ''' takes the file and packs it ''' + print 'Compressing file' + # TODO gen random temp here + target_file='tempfile.tmp' + with open(source_file, 'r') as source, open(target_file,'wb') as target: + lz = pylzma.compressfile(source, eos=1) + while True: + b =lz.read(1) + if not b: + break + target.write(b) + target.close() + return target_file +def unpack_file_from_disk(packed_file, out_file): + ''' unpack file ''' + print 'Uncompressing file' + with open(packed_file, 'rb') as packed, open(out_file,'wb') as out: + obj = pylzma.decompressobj() + decompress = '' + while True: + b = packed.read(1) + if not b: + break + dec = obj.decompress(b) + out.write(dec) + return True + +# store file def read_file_into_list(source_file): - """ reads file into list""" - with open(source_file, 'r') as source: - data = base64.b64encode(source.read()) - return [data[i:i+SPLIT_LENGTH] for i in range(0, len(data), SPLIT_LENGTH)] + """ reads file into list""" + with open(source_file, 'r') as source: + data = base64.b64encode(source.read()) + return [data[i:i+SPLIT_LENGTH] for i in range(0, len(data), SPLIT_LENGTH)] def create_comment(data): - """ takes data bit and turns it into a key comment""" - checksum = sha256(data).hexdigest() - comment = checksum + ' ' + data - return comment + """ takes data bit and turns it into a key comment""" + checksum = sha256(data).hexdigest() + comment = checksum + ' ' + data + return comment def create_key(name): - """ creates gpg key out of given data""" - input_data = GPG.gen_key_input( - key_type='RSA', - key_length='1024', - name_real='PGP File System', - name_comment=create_comment(name), - name_email='placeholder@email.address' - ) - return GPG.gen_key(input_data) + """ creates gpg key out of given data""" + input_data = GPG.gen_key_input( + key_type='RSA', + key_length='1024', + name_real='PGP File System', + name_comment=create_comment(name), + name_email='placeholder@email.address' + ) + return GPG.gen_key(input_data) def send_key(key_id): - """ uploads given key to keyserver""" - key_id = str(key_id) - GPG.send_keys(KEYSERVER, key_id) - if key_id == GPG.search_keys(key_id, KEYSERVER)[0]['keyid']: - return key_id - else: - error = 'Error uploading key ', key_id - return error + """ uploads given key to keyserver""" + key_id = str(key_id) + GPG.send_keys(KEYSERVER, key_id) + if key_id == GPG.search_keys(key_id, KEYSERVER)[0]['keyid']: + return key_id + else: + error = 'Error uploading key ', key_id + return error def store_file(filename1, filename2): - """ overall function to upload file to keyserver""" - print 'Splitting ', filename1, ' into encoded comments for keys' - file_list = read_file_into_list(filename1) - output_file = open(filename2, 'w') - counter_length = len(file_list) - counter = 0 - for chunk in file_list: - print 'Creating key ', counter, ' of ', counter_length - counter = counter + 1 - key_id = create_key(chunk) - output_file.write(send_key(key_id)+'\n') - print '--> key has been created and uploaded' - print 'File has been successfully uploaded to ', KEYSERVER + """ overall function to upload file to keyserver""" + print 'Splitting ', filename1, ' into encoded comments for keys' + + # watch out return value is the name of the newly packed file now + filename1 = pack_file_to_disk(filename1) + + file_list = read_file_into_list(filename1) + output_file = open(filename2, 'w') + counter_length = len(file_list) + counter = 0 + for chunk in file_list: + print 'Creating key ', counter, ' of ', counter_length + counter = counter + 1 + key_id = create_key(chunk) + output_file.write(send_key(key_id)+'\n') + output_file.flush() + print '--> key has been created and uploaded' + print 'File has been successfully uploaded to ', KEYSERVER + + # TODO adjust with args + # tempfile is created at packing stage + os.system('tempfile.tmp') # fetch file def get_key_comment(key_id): - """ returns comment section of a given key""" - return GPG.search_keys(key_id, KEYSERVER)[0]['uids'] + """ returns comment section of a given key""" + return GPG.search_keys(key_id, KEYSERVER)[0]['uids'] def parse_key(key_id): - """" parses file bit out of key comment""" - comment = get_key_comment(key_id)[0] - regex = re.compile(".*?\\((.*?)\\)") - comment_bits = re.findall(regex, comment)[0].split(' ') - if comment_bits[0] == sha256(comment_bits[1]).hexdigest(): - return comment_bits[1] + """" parses file bit out of key comment""" + comment = get_key_comment(key_id)[0] + regex = re.compile(".*?\\((.*?)\\)") + comment_bits = re.findall(regex, comment)[0].split(' ') + if comment_bits[0] == sha256(comment_bits[1]).hexdigest(): + return comment_bits[1] def fetch_file(index_file, filename): - """ overall function to fetch component file parts from keyserver""" - with open(index_file, 'r') as index, open(filename, 'w+') as download: - print 'Fetching keys from ', KEYSERVER, ' to create ', filename - fetched_file = '' - index_length = len(index.readlines()) - index.seek(0) # because python is stupid - counter = 0 - for key in index.readlines(): - print 'Fetching key ', counter, ' of ', index_length - counter = counter + 1 - fetched_file = fetched_file + parse_key(key.rstrip('\n')) - print 'All keys have been downloaded' - download.write(base64.b64decode(fetched_file)) - print 'File has been decoded and saved as ', filename + """ overall function to fetch component file parts from keyserver""" + filelzma='%s.lzma' % filename + with open(index_file, 'r') as index, open(filelzma, 'w+') as download: + print 'Fetching keys from ', KEYSERVER, ' to create ', filelzma + fetched_file = '' + index_length = len(index.readlines()) + index.seek(0) # because python is stupid + counter = 0 + for key in index.readlines(): + print 'Fetching key ', counter, ' of ', index_length + counter = counter + 1 + fetched_file = fetched_file + parse_key(key.rstrip('\n')) + print 'All keys have been downloaded' + download.write(base64.b64decode(fetched_file)) + # hook in here and unpack the data + # filename == original outname + # filelzma == packed file here as download + unpack_file_from_disk(filelzma,filename) + print 'File has been decoded and saved as ', filename -# handle command line input + # remove lzma + os.unlink(filelzma) -if len(sys.argv) < 3: - print """Usage: ./pgpfs.py [action] filename1 filename2' - action can be either ''store'' or ''fetch''' +def usage(): + print """Usage: ./pgpfs.py [action] filename1 filename2' + action can be either ''store'' or ''fetch''' - When action is 'store': - filename1 is the file to upload, filename2 is the name of the key allocation table - When action is 'fetch': - filename1 is the key allocation table, filename2 is the output file + When action is 'store': + filename1 is the file to upload, filename2 is the name of the key allocation table + When action is 'fetch': + filename1 is the key allocation table, filename2 is the output file - Example uses: - - To store 'sample.mp3' on the keyserver - ./pgpfs.py store sample.mp3 sample.kat - - To fetch the file contained in 'sample.kat' and store it as 'sample.mp3' - ./pgpfs.py fetch sample.kat sample.mp3 - """ - sys.exit(0) + Example uses: + - To store 'sample.mp3' on the keyserver + ./pgpfs.py store sample.mp3 sample.kat + - To fetch the file contained in 'sample.kat' and store it as 'sample.mp3' + ./pgpfs.py fetch sample.kat sample.mp3 + """ + sys.exit(0) -ACTION = sys.argv[1] +def run(args): + ''' run function, do all the magic ''' -if ACTION not in ['store', 'fetch']: - print 'Please specific either ''store'' or ''fetch''' - sys.exit(0) + global KEYSERVER + global GPG -if ACTION == 'store': - store_file(sys.argv[2], sys.argv[3]) -elif ACTION == 'fetch': - fetch_file(sys.argv[2], sys.argv[3]) + + pgpfspath = args.pgpfspath + pgpfsbin = args.pgpfsbin + KEYSERVER = args.keyserver + ACTION = args.action + kat = args.kat + store = args.store + + GPG = gnupg.GPG(gnupghome=pgpfspath,gpgbinary=pgpfsbin) + + if ACTION not in ['store', 'fetch']: + print 'Please specific either ''store'' or ''fetch''' + sys.exit(0) + + if ACTION == 'store': + store_file(store, kat) + elif ACTION == 'fetch': + fetch_file(kat, store) + pass + +def main(): + prog_name='pgpfs.py' + prog_desc='https://github.com/aestetix/pgpfs' + + parser = argparse.ArgumentParser(prog=prog_name, description=prog_desc) + parser.add_argument('-a','--action',help='action to do: store or fetch',dest='action',required=True) + parser.add_argument('-k','--katfile',help='if fetch specify KAT file',dest='kat',required=False) + parser.add_argument('-s','--storefile',help='if store specify file for upload',dest='store',required=False) + parser.add_argument('--keyserver',help='specify keyserver',dest='keyserver',required=False, default='pgp.mit.edu') + parser.add_argument('--pgpfspath',help='specify path for pgpfs',dest='pgpfspath',required=False, default='.pgpfs') + parser.add_argument('--gpgbinary',help='specify path for pgpfs binary(patched gpg bin)',dest='pgpfsbin',required=False, default='/root/fuzzygpg/bin/gpg2') + args = parser.parse_args() + run(args) + +if __name__ == '__main__': + main() diff --git a/requirements.txt b/requirements.txt index 45ce307..d2f0a5e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,5 @@ +# for crypto python-gnupg + +# for compression +pip2 install pylzma diff --git a/sample.kat b/sample.kat index 3828ee6..0776990 100644 --- a/sample.kat +++ b/sample.kat @@ -1,152 +1,6 @@ -FC7E32931156981F45C8921403FC6E2E71437504 -AEFD299201F29D9A214F12A6ED67FD5C097D395E -77D8C204A22D1B95A94F8F968B7D7D6C067F03D0 -DC1860F1F31B97CF0B4C810CC93F45CBF53899DF -7F2DB84F62FB6FB92DDAEBF2C72CE0BEFF30B39C -6855D9307D0D028EC9331D2035A7073251DA8706 -8D9F2349B9985C9ABAA8CFEB97D7987A2E590F51 -C2A4FA1711BA44AE4FA4BD6D64968C33EBD48804 -1A14F953CF068E31B024B14C9E32560C66B72DE6 -9761655B70B37C151A0F8BF91DC037FE91467EB5 -381ADF37F08AD6EC68C29C2BD5D8C50ECB9D2C92 -6E02EF0894D791B14763C3F9C08DE00FF0FAC72C -83BAB626A749B42BFB0DD9A089C1C980B54BFADD -70172D0D8250348D39A8DCC35BBCAEA9CEAD5095 -5641A421CC6EC4D653A5DF7A7E7E6310223F40BB -B18D5207DB52F67B8B887C2EF29F46D59EB47057 -B61017BB57CD67E799F00030F4DA2ABC40AD954F -553A1FA1BC4B2E134FD8B599EE1DD665ADDBA5F3 -C784F793E7F5227E5DE0990C9A974A93412D38F0 -C21D729AB22E32E8D099805F7CD229CE467C1070 -85327227E8F88651D289585306D03094A219DAFB -15BCDE4302088E55E4BD0F350802FDCDF9EFCBE9 -713E962F520A26E4053E2BC6651D639B47AC77C2 -43D14E04CDD79D91F170AFE4CC1E9F4C98C5ADA5 -54F165ED18B2AE530646EFFF0C3148F8FFBC2BAF -E20BC2E85EF4710D865ACED955BD03C4BC1242CF -DFB4A066437EA239C214B8A1029F1C141CECAB4B -C35F3F42A0EE1CD4D667DFB54265422B6F59D6D2 -EBCF384AE04B4819825DB50274CF2B8B25013663 -DA50EF1B63294B0F0C8F3EB8E764A10D813D6161 -504929CCB20136D2C4F6EBE000AAD0977A175F76 -C3E57059241C861E776D2C67E5C355A967BC95C7 -25F9417ADD87117A6F7142FDFE376CC87EA544AC -C12E9E766A9171CB308D2A13043CBC2ED8DD46FC -C342DE30EC6C4EABB9ECA01921C3F45C9E113BA3 -ABCBCF7C746F88C62599373850D271251650DA5E -3A8915A4E29751BF9749CCDE8994FD78F9BD69D8 -1DE994F81F3BF045D0BAA2F3BA569CF9D577522F -D14A65F9C9BD9B7DB809BA75A014CF92C9099A15 -A66CCEC87D2086A7076DB7A9F727E06B4DD0F1CB -98C2188983492CD882094E13899D5A303B2D4699 -EF736B36CB5BD35676251A25165E198CE0E50B0B -9A0B6A1716BE118373E44A196FF6E04971BF30B5 -4D600F90080B05F3556896A51D50FFE827650A1A -5E04FE85AEB4EBC5B7EBEC1DD6795EEAE7255BEC -9FF2074D485AB4F9EE10261C497EB569D2826268 -A8F4F7279CDBBBC8F8CAE7655EA0C8261B474D63 -77AE3944D5D3F29BB6DEE7417DE5AD276BB812FE -BF510C2FCD094E326016EF3F1D68B879C07A3554 -3FEEB6AD801F0D890BDC1E856AEA8066240F72CC -74EF6FDC61394F11B3D30BAAB2E0D0E03B90FD9A -E8743554CE30989683BA6968183E80E5530955C9 -1D916686C99DA213922A2AA3C92E09DAF83ADFA6 -9C68F90DDD04573E01CC784095E8AD7AFBD3A958 -1D7D8F2E058AF51D259E40889E1F54F298C9CF39 -B62E99A68496D7BC364DD6AA078EA3575D0C9BC1 -A27EBF1A05F69D35D31C7D7D0BD466AD4FD2632B -17F0964EC2CBDC03DB95DAA41C2F125B7F959119 -D2D5A6C6811E010FB704EAA6FF6436C30F7C0A8D -231A76CFF6A01447CC10C402AEF4FB526EEBE04F -F51F6DD8E7C54F38F44BFF28F2EE02A71D7DCA05 -6C7F4EA5A1D5389A285576A5059B7508FF62A143 -3240819055A4C33C63038C07AB074E7B1193A46E -E212CE4C6AF43342869D63069FD534087F60E0DD -840460655FB874FF8F9A80F3AF200143EDE39C01 -7D397CF95C2390FEFFC992A1A27CC1069E6B430F -34760DE6285F5BE3A1CD587BC780D2C719D32EA4 -FFBE5077A12BB8F634C3443D62EC8D0C5B8EB58E -396B6CD12626EF54B60EBF3EB8E741069F505795 -F92215F565A48FF1CE555430DFC12645D735BB4D -9186E87CD5268E242BB741B504C3F6E6A68D8DB5 -C8B2AAE555130CD59DADB4D1ADC481E2CF4F22F9 -3027B0DF6EFC9A65CC0C96B03C529763A9DF0441 -888D93910A811942A08849917630E274C7B25212 -A54FF2D620F7FFB2C8BAE3D1FE5BA6D0E011A85C -3C3682B68B081467D238ABF533FAE9DC18023471 -236373230F09D348F4297B930286CF184DB8D58D -0E8EA256ACB357FAE5EEE7CBF4FCD45553D9B5FD -9382FEB1F294763DAEE1B3A2E0447BC888F372D6 -5C7DC7EE295B4526D931B9960D7BFC942A406849 -B021623CA67F9D9FEC2F185980C61BF5FB794FEF -17570A0A21DF24ECD23798378EA6BFA5435E88FC -2E2DEDB85EF6CBEFE52FAD7FD361BC7B659A993F -0619E510B69066E2D905AA8196C45E01B8B88AFD -46B542E6C1C052971A9600A157587967E6B06949 -5B5B738391ADC1E399E8D4E90E1056C5E5466261 -71B97797C1D4BEC8D5C5F94DA64595EC29ACB387 -0273561D3A2E4EBA7052C4306E18F92BEEB5CADE -74E59E4DFAB3AD26FD7F2A929A574B2C9EC8B307 -7436750D2DCF366EA438B9CAF67D8491E0CCFD83 -2336143F7FFEE099A562C79F940E1FB8B5185CBD -E19149A9A44C0930CADA2B00A4E159A380B82CF4 -69588D2817EDE5A47EB76C95D996B5FC32559BCD -99A36ACEE0AAC24636E9C352206162305D3AF7D5 -025CE5B6CD9ED7C7E155CFA71DCEE7230FF06C92 -923BC863D50ADFB81D59216187818104A4FA6647 -78A648F27596C388342AF41FABC12C3EF8D19419 -0BFC560C358749C97B27455B3725A2B7B2170A44 -0FB6FC2AB8203DC1B766B35B13E223590AAE0E62 -5246CBB8A6BC2A1940FA5C1EB33CE9AD5678E6F1 -B3CDB213917711608B10F67623FF8BC2990FB01A -8E9DCDF64E34576F508B2AC1160DF9EB39753351 -729C7801E2B34E43F8677840C62892C25EAB9C31 -CCAF6D87C4367C6D411A416AE47DCD9100CFCC87 -11CB8E5E9632470811409CA26D76D79EB014E736 -E300526068BC9859B801E044EB04C2FFEE00A325 -5FDD54931FB220529B3E239BCB39E20E6AB49E6D -1620368CA99E94FE680691929A9EC6963AAF981E -BA150F4D664C3FF2D0EBEAD54D6B84D86C546D92 -3CCE2C670F46C85A438CC74C93EC3DCE6B61E397 -63D5A2432B606145AFDC033A335FEC7F906E83B3 -CE93757422A3B579FCDEE93B377210563332F891 -177B9AA184E5E9BA6E242C876011ABF4979F4BAF -FC46D820A1ADC9D377A5AF3D3669B7F0A590D9F8 -8E507A347FEFA1BCC694A8A8834240896566791A -B0A28BD84FD258881AE543FF4F3A00C91FC4A734 -BBF485E9CA9E81AA048BA19B2635E10063ACF01E -CFBCEE242835D4FAB673C5FE1E16657256582989 -583E7794847191DBD38183873F633627D0508A26 -DDD1EC1314122342EB547F8C541157A89945D3E7 -120D681444A7B4F59E39FFFD2DAB5E0B4DC17009 -4FDCBCF0C75B35DF4F570E921892BB17B16D8062 -D736015EB64E9FBE622CCEC7B9EF951E8C2D3E3A -D36DB784C7A565E76C9C40776017D134B4447C6F -343B530F95B2CAFB8620A4FD4B31102D02061543 -90AB64DFE7D280E96EAF2B7DA5E522634012C79E -DAA9CE75C9A1F8AB54C51CA153AC4C95C3C6EE6B -F18D849F47A56A55DD00B21C638B2212C7518CDC -F7C035D8A5C597BB579E591EDE497BA0E1162B09 -CC48F8C613A12DE9AD9CFE6B195320FDBDE38F31 -745730E07AF64A794CDBEA9739726598CB17327B -79118B75E03CED3485F715DA9DA60399AEBEBCFE -90D01EB404B84FCA5BF12E183C2D178E076DB003 -4EF9B48377F764E46E3599FF7917A48396D59870 -2F75D55AED2691AE70A90CE3D85D872AB02EE17D -987F3F0A5746FCC011B565E2D7BDE6A4BA3E1E70 -51172E0EE67884A08DFD14628B334CCE66A5B2A5 -0D161D7D63FA04BB6A74214347677220DBAD0E34 -3554DC686D53C10C4C847A4DED57FEEBBC609A67 -3D66E2C78F486457596F8819FDFBBD8E3409E9D7 -B94447C1FC5172C2E8877699E1EEA744F052220B -A1AB1EAC0E84A116D348E04CA44EEA033F5AC4AD -A3CB4E3827F195B6FDC8ECCF6D036631AF1D1866 -56FEB79F69F10DDB33E778ADE835AA029804292B -E827BF52999953B93214F796760B8C3771490129 -CBD1C8C8B3946A83A5D8233876CA0CFA5D4302C2 -8A65DBFD30294CF8AB556D89E04D7D476AEBF597 -96F4EABCF809FD1802C1BF0A8000A3FAF9C1C6F5 -CDED5632BC11999E545D981CCFFCDD3ACC04BA17 -9B524CCB9647E4FE6ED79D129049AA1FAD18DA23 -4D296FBBB9FB6732EC3CD24C674BAA29B827002C -11D08E00DBC668225E6AA084AF1C68F6519C4C31 +2272C34D08040FE9192224098D48A96E2C86C1DC +68ED1D9AF827F06BA229AC8791C7903A86AC78AF +8DB3A3662748A13102B8A954BF825A6CCD4E4515 +9D3961DA2CDE3DF6736AE4520F2A93A9A5B8D6F7 +A08233ACEB11F2ADBA48075E1F8D6337889E8BCF +5913148227C69CEA0C7B15DC7ABA0182E569A9B9