From 62b0ca68d4d09252f8942e9d8496b5cbf7a164b8 Mon Sep 17 00:00:00 2001 From: clach04 Date: Sat, 7 Sep 2024 10:29:00 -0700 Subject: [PATCH] Implement #158 - rot47 support The file handler code also has been tweaked so new Substitution ciphers will auto register. --- README.md | 12 +++++++++ puren_tonbo/__init__.py | 39 ++++++++++++++++-------------- puren_tonbo/tests/data/aesop.rot13 | 7 ++++++ puren_tonbo/tests/data/aesop.rot47 | 7 ++++++ puren_tonbo/tests/testsuite.py | 17 +++++++++++++ 5 files changed, 64 insertions(+), 18 deletions(-) create mode 100644 puren_tonbo/tests/data/aesop.rot13 create mode 100644 puren_tonbo/tests/data/aesop.rot47 diff --git a/README.md b/README.md index 49a309d..a2b8bb7 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ Some countries have also have restrictions on import, export, and usage see http + [ptpyvim](#ptpyvim) + [ptcipher](#ptcipher) - [rot-13](#rot-13) + - [rot-47](#rot-47) - [Tombo Blowfish CHI](#tombo-blowfish-chi) - [ccrypt CPT](#ccrypt-cpt) - [OpenPGP - gpg / pgp](#openpgp---gpg---pgp) @@ -353,6 +354,17 @@ https://en.wikipedia.org/wiki/ROT13 ptcipher -v -p test README.rot13 py -3 -m puren_tonbo.tools.ptcipher puren_tonbo/tests/data/aesop.rot13 -p password_ignored + echo Why did the chicken cross the road?|ptcipher -p ignored_password --encrypt --cipher=rot13 + echo Jul qvq gur puvpxra pebff gur ebnq?|ptcipher -p ignored_password --encrypt --cipher=rot13 + +#### rot-47 + +Symmetric Substitution cipher with no passphrase/password/key support. +Do not use, this is implemented as a demo and for testing code paths when encryption libraries are not available. + +https://en.wikipedia.org/wiki/ROT13#Variants + + py -3 -m puren_tonbo.tools.ptcipher puren_tonbo/tests/data/aesop.rot47 -p password_ignored #### Tombo Blowfish CHI diff --git a/puren_tonbo/__init__.py b/puren_tonbo/__init__.py index 65ea683..8c100d7 100644 --- a/puren_tonbo/__init__.py +++ b/puren_tonbo/__init__.py @@ -2,6 +2,7 @@ import bisect import datetime import errno +import inspect from io import BytesIO as FakeFile import json import locale @@ -256,20 +257,30 @@ class Rot13(SubstitutionCipher): description = 'rot-13 UNSECURE!' extensions = ['.rot13'] - __substitution_table = maketrans(b'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz', b'NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm') + substitution_table = maketrans(b'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz', b'NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm') def read_from(self, file_object): # NOTE no password/key usage! data = file_object.read() try: - return data.translate(self.__substitution_table) + return data.translate(self.substitution_table) except Exception as info: #raise # debug # chain exception... raise PurenTonboException(info) def write_to(self, file_object, byte_data): - file_object.write(byte_data.translate(self.__substitution_table)) + file_object.write(byte_data.translate(self.substitution_table)) + + +class Rot47(Rot13): + description = 'rot-47 UNSECURE!' + extensions = ['.rot47'] + + substitution_table = maketrans( + b'!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~', + b'PQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNO' + ) class VimDecryptArgs(): @@ -647,24 +658,16 @@ class ZipBzip2AES(ZipAES): # note uses file extension - could also sniff file header and use file magic file_type_handlers = {} -""" - '.txt': RawFile, # these are not needed, filename2handler() defaults - '.md': RawFile, -} -""" -for file_extension in RawFile.extensions: - file_type_handlers[file_extension] = RawFile - -for file_extension in Rot13.extensions: - file_type_handlers[file_extension] = Rot13 -""" -# TODO introspect and add to list based on class isinstance check -for enc_class in (RawFile, Rot13): +# Dumb introspect code for RawFile and SubstitutionCipher (rot13 and rot47) +for enc_class_name in dir(): #(RawFile, Rot13): + enc_class = globals()[enc_class_name] + if not inspect.isclass(enc_class): + continue + if not issubclass(enc_class, RawFile) and not issubclass(enc_class, SubstitutionCipher): + continue for file_extension in enc_class.extensions: file_type_handlers[file_extension] = enc_class -""" - if chi_io: for file_extension in TomboBlowfish.extensions: diff --git a/puren_tonbo/tests/data/aesop.rot13 b/puren_tonbo/tests/data/aesop.rot13 new file mode 100644 index 0000000..5da3396 --- /dev/null +++ b/puren_tonbo/tests/data/aesop.rot13 @@ -0,0 +1,7 @@ +nrfbc + +Gur Sebtf Qrfvevat n Xvat + +Gur Sebtf jrer yvivat nf unccl nf pbhyq or va n zneful fjnzc gung whfg fhvgrq gurz; gurl jrag fcynfuvat nobhg pnevat sbe abobql naq abobql gebhoyvat jvgu gurz. Ohg fbzr bs gurz gubhtug gung guvf jnf abg evtug, gung gurl fubhyq unir n xvat naq n cebcre pbafgvghgvba, fb gurl qrgrezvarq gb fraq hc n crgvgvba gb Wbir gb tvir gurz jung gurl jnagrq. "Zvtugl Wbir," gurl pevrq, "fraq hagb hf n xvat gung jvyy ehyr bire hf naq xrrc hf va beqre." Wbir ynhturq ng gurve pebnxvat, naq guerj qbja vagb gur fjnzc n uhtr Ybt, juvpu pnzr qbja fcynfuvat vagb gur fjnzc. Gur Sebtf jrer sevtugrarq bhg bs gurve yvirf ol gur pbzzbgvba znqr va gurve zvqfg, naq nyy ehfurq gb gur onax gb ybbx ng gur ubeevoyr zbafgre; ohg nsgre n gvzr, frrvat gung vg qvq abg zbir, bar be gjb bs gur obyqrfg bs gurz iragherq bhg gbjneqf gur Ybt, naq rira qnerq gb gbhpu vg; fgvyy vg qvq abg zbir. Gura gur terngrfg ureb bs gur Sebtf whzcrq hcba gur Ybt naq pbzzraprq qnapvat hc naq qbja hcba vg, gurerhcba nyy gur Sebtf pnzr naq qvq gur fnzr; naq sbe fbzr gvzr gur Sebtf jrag nobhg gurve ohfvarff rirel qnl jvgubhg gnxvat gur fyvtugrfg abgvpr bs gurve arj Xvat Ybt ylvat va gurve zvqfg. Ohg guvf qvq abg fhvg gurz, fb gurl frag nabgure crgvgvba gb Wbir, naq fnvq gb uvz, "Jr jnag n erny xvat; bar gung jvyy ernyyl ehyr bire hf." Abj guvf znqr Wbir natel, fb ur frag nzbat gurz n ovt Fgbex gung fbba frg gb jbex tbooyvat gurz nyy hc. Gura gur Sebtf ercragrq jura gbb yngr. + +Orggre ab ehyr guna pehry ehyr. diff --git a/puren_tonbo/tests/data/aesop.rot47 b/puren_tonbo/tests/data/aesop.rot47 new file mode 100644 index 0000000..3d3aeec --- /dev/null +++ b/puren_tonbo/tests/data/aesop.rot47 @@ -0,0 +1,7 @@ +26D@A + +%96 uC@8D s6D:C:?8 2 z:?8 + +%96 uC@8D H6C6 =:G:?8 2D 92AAJ 2D 4@F=5 36 :? 2 >2CD9J DH2>A E92E ;FDE DF:E65 E96>j E96J H6?E DA=2D9:?8 23@FE 42C:?8 7@C ?@3@5J 2?5 ?@3@5J EC@F3=:?8 H:E9 E96>] qFE D@>6 @7 E96> E9@F89E E92E E9:D H2D ?@E C:89E[ E92E E96J D9@F=5 92G6 2 <:?8 2?5 2 AC@A6C 4@?DE:EFE:@?[ D@ E96J 56E6C>:?65 E@ D6?5 FA 2 A6E:E:@? E@ y@G6 E@ 8:G6 E96> H92E E96J H2?E65] Q|:89EJ y@G6[Q E96J 4C:65[ QD6?5 F?E@ FD 2 <:?8 E92E H:== CF=6 @G6C FD 2?5 <66A FD :? @C56C]Q y@G6 =2F8965 2E E96:C 4C@2<:?8[ 2?5 E9C6H 5@H? :?E@ E96 DH2>A 2 9F86 {@8[ H9:49 42>6 5@H? DA=2D9:?8 :?E@ E96 DH2>A] %96 uC@8D H6C6 7C:89E6?65 @FE @7 E96:C =:G6D 3J E96 4@>>@E:@? >256 :? E96:C >:5DE[ 2?5 2== CFD965 E@ E96 32?< E@ =@@< 2E E96 9@CC:3=6 >@?DE6Cj 3FE 27E6C 2 E:>6[ D66:?8 E92E :E 5:5 ?@E >@G6[ @?6 @C EH@ @7 E96 3@=56DE @7 E96> G6?EFC65 @FE E@H2C5D E96 {@8[ 2?5 6G6? 52C65 E@ E@F49 :Ej DE:== :E 5:5 ?@E >@G6] %96? E96 8C62E6DE 96C@ @7 E96 uC@8D ;F>A65 FA@? E96 {@8 2?5 4@>>6?465 52?4:?8 FA 2?5 5@H? FA@? :E[ E96C6FA@? 2== E96 uC@8D 42>6 2?5 5:5 E96 D2>6j 2?5 7@C D@>6 E:>6 E96 uC@8D H6?E 23@FE E96:C 3FD:?6DD 6G6CJ 52J H:E9@FE E2<:?8 E96 D=:89E6DE ?@E:46 @7 E96:C ?6H z:?8 {@8 =J:?8 :? E96:C >:5DE] qFE E9:D 5:5 ?@E DF:E E96>[ D@ E96J D6?E 2?@E96C A6E:E:@? E@ y@G6[ 2?5 D2:5 E@ 9:>[ Q(6 H2?E 2 C62= <:?8j @?6 E92E H:== C62==J CF=6 @G6C FD]Q }@H E9:D >256 y@G6 2?8CJ[ D@ 96 D6?E 2>@?8 E96> 2 3:8 $E@C< E92E D@@? D6E E@ H@C< 8@33=:?8 E96> 2== FA] %96? E96 uC@8D C6A6?E65 H96? E@@ =2E6] + +q6EE6C ?@ CF=6 E92? 4CF6= CF=6] diff --git a/puren_tonbo/tests/testsuite.py b/puren_tonbo/tests/testsuite.py index 9348979..5cfd436 100644 --- a/puren_tonbo/tests/testsuite.py +++ b/puren_tonbo/tests/testsuite.py @@ -899,6 +899,23 @@ class TestFileSystemNotesWriteFunctionSaveEncryptedRot13(TestFileSystemNotesWrit handler_class = puren_tonbo.Rot13 # rot-13 +# rot-47 +class TestBaseEncryptedRot47(TestBaseEncryptedFile, TestBaseEncryptedFileCompat, TestBaseEncryptedFileUtil): + test_data_bytes = b"this is just a small piece of text." + test_password_bytes = b'mypassword' + pt_handler_class = puren_tonbo.Rot47 + encrypt_pt_handler_class = decrypt_pt_handler_class = puren_tonbo.Rot47 # TODO review this + + def test_same_input_different_crypted_text(self): + self.skip('rot-47 always has same output') + +class TestFileSystemNotesWriteClassSaveEncryptedRot47(TestFileSystemNotesWriteClassSaveRawPlainText): + handler_class = puren_tonbo.Rot47 # rot-47 + +class TestFileSystemNotesWriteFunctionSaveEncryptedRot47(TestFileSystemNotesWriteFunctionSaveRawPlainText): + handler_class = puren_tonbo.Rot47 # rot-47 + + """ TODO implement TestFileSystemNotesWriteClassSaveRawPlainText and TestFileSystemNotesWriteFunctionSaveRawPlainText for: grep '(EncryptedFile):' puren_tonbo/__init__.py