From 632a77c7b9ccfb8bc0ef04a23ff62daaecf0fe41 Mon Sep 17 00:00:00 2001 From: vtexier <vit@free.fr> Date: Sat, 23 Mar 2019 12:42:09 +0100 Subject: [PATCH] [fix] #78 Fix errors in cleartext AA messages --- duniterpy/key/ascii_armor.py | 21 +++++----- .../load_cleartext_ascii_armor_message.py | 21 ++++++++++ ... => load_encrypted_ascii_armor_message.py} | 11 +++--- .../save_cleartext_ascii_armor_message.py | 39 +++++++++++++++++++ ... => save_encrypted_ascii_armor_message.py} | 12 ++++-- 5 files changed, 85 insertions(+), 19 deletions(-) create mode 100644 examples/load_cleartext_ascii_armor_message.py rename examples/{load_ascii_armor_encrypted_message.py => load_encrypted_ascii_armor_message.py} (71%) create mode 100644 examples/save_cleartext_ascii_armor_message.py rename examples/{save_ascii_armor_encrypted_message.py => save_encrypted_ascii_armor_message.py} (74%) diff --git a/duniterpy/key/ascii_armor.py b/duniterpy/key/ascii_armor.py index 818532d4..2878c23f 100644 --- a/duniterpy/key/ascii_armor.py +++ b/duniterpy/key/ascii_armor.py @@ -63,6 +63,7 @@ class AsciiArmor: """ Class to handle writing and parsing of ascii armor messages """ + @staticmethod def create(message: str, pubkey: Optional[str] = None, signing_keys: Optional[List[SigningKey]] = None, message_comment: Optional[str] = None, signatures_comment: Optional[str] = None, @@ -87,8 +88,8 @@ class AsciiArmor: if scrypt_params is None: scrypt_params = ScryptParams() - # remove last newline of the message if any - message = message.strip("\n\r") + # keep only one newline at the end of the message + message = message.rstrip("\n\r") + "\n" # create block with headers ascii_armor_block = """ @@ -119,9 +120,9 @@ class AsciiArmor: else: # remove trailing spaces message = AsciiArmor._remove_trailing_spaces(message) - # dash escape the message - message = AsciiArmor._dash_escape_text(message) - ascii_armor_block += message + "\n" + + # add dash escaped message to ascii armor content + ascii_armor_block += AsciiArmor._dash_escape_text(message) # if no signature... if signing_keys is None: @@ -141,7 +142,7 @@ class AsciiArmor: def _remove_trailing_spaces(text: str) -> str: clean_text = str() - for line in text.splitlines(): + for line in text.splitlines(True): # remove trailing spaces (0x20) and tabs (0x09) clean_text += line.rstrip("\x09\x20") @@ -151,9 +152,9 @@ class AsciiArmor: def _dash_escape_text(text: str) -> str: dash_escaped_text = str() - for line in text.splitlines(): + for line in text.splitlines(True): # add dash '-' (0x2D) and space ' ' (0x20) as prefix - dash_escaped_text += DASH_ESCAPE_PREFIX + dash_escaped_text += DASH_ESCAPE_PREFIX + line return dash_escaped_text @@ -283,13 +284,13 @@ class AsciiArmor: { 'fields': {}, 'content': '', - }, + }, 'signatures': [] } # type: Dict[str, Any] cursor_status = 0 message = '' signatures_index = 0 - for line in ascii_armor_block.splitlines(): + for line in ascii_armor_block.splitlines(True): # if begin message header detected... if regex_begin_message.match(line): diff --git a/examples/load_cleartext_ascii_armor_message.py b/examples/load_cleartext_ascii_armor_message.py new file mode 100644 index 00000000..e74abb5d --- /dev/null +++ b/examples/load_cleartext_ascii_armor_message.py @@ -0,0 +1,21 @@ +from duniterpy.key import AsciiArmor + +# CONFIG ####################################### + +CLEARTEXT_AA_MESSAGE_PATH = '/tmp/duniter_cleartext_aa_message.txt' + +################################################ + +if __name__ == '__main__': + # Ask public key of the issuer + pubkeyBase58 = input("Enter public key of the message issuer: ") + + # Load cleartext ascii armor message from a file + with open(CLEARTEXT_AA_MESSAGE_PATH, 'r') as file_handler: + ascii_armor_block = file_handler.read() + + print("Cleartext Ascii Armor Message loaded from file ./{0}".format(CLEARTEXT_AA_MESSAGE_PATH)) + + result = AsciiArmor.parse(ascii_armor_block, None, [pubkeyBase58]) + print('------------- MESSAGE ------------\n' + result['message']['content'] + '----------------------------------') + print(result) diff --git a/examples/load_ascii_armor_encrypted_message.py b/examples/load_encrypted_ascii_armor_message.py similarity index 71% rename from examples/load_ascii_armor_encrypted_message.py rename to examples/load_encrypted_ascii_armor_message.py index e9032e8c..be61fcd9 100644 --- a/examples/load_ascii_armor_encrypted_message.py +++ b/examples/load_encrypted_ascii_armor_message.py @@ -1,11 +1,12 @@ import getpass -from duniterpy import __version__ from duniterpy.key import AsciiArmor, SigningKey -################################################ +# CONFIG ####################################### + +ENCRYPTED_AA_MESSAGE_PATH = '/tmp/duniter_aa_encrypted_message.txt' -AA_ENCRYPTED_MESSAGE_FILENAME = 'duniter_aa_encrypted_message.txt' +################################################ if __name__ == '__main__': # Ask public key of the recipient @@ -21,9 +22,9 @@ if __name__ == '__main__': signing_key = SigningKey.from_credentials(salt, password) # Load ascii armor encrypted message from a file - with open(AA_ENCRYPTED_MESSAGE_FILENAME, 'r') as file_handler: + with open(ENCRYPTED_AA_MESSAGE_PATH, 'r') as file_handler: ascii_armor_block = file_handler.read() - print("Ascii Armor Encrypted message loaded from file ./{0}".format(AA_ENCRYPTED_MESSAGE_FILENAME)) + print("Encrypted Ascii Armor Message loaded from file ./{0}".format(ENCRYPTED_AA_MESSAGE_PATH)) print(AsciiArmor.parse(ascii_armor_block, signing_key, [pubkeyBase58])) diff --git a/examples/save_cleartext_ascii_armor_message.py b/examples/save_cleartext_ascii_armor_message.py new file mode 100644 index 00000000..edbbf617 --- /dev/null +++ b/examples/save_cleartext_ascii_armor_message.py @@ -0,0 +1,39 @@ +import getpass +import sys + +from duniterpy import __version__ + +from duniterpy.key import AsciiArmor, SigningKey + +# CONFIG ####################################### + +CLEARTEXT_AA_MESSAGE_PATH = '/tmp/duniter_cleartext_aa_message.txt' + +################################################ + +if __name__ == '__main__': + # prompt hidden user entry + salt = getpass.getpass("Enter your passphrase (salt): ") + + # prompt hidden user entry + password = getpass.getpass("Enter your password: ") + + # init SigningKey instance + signing_key = SigningKey.from_credentials(salt, password) + + # Enter the multi-line message (stop with Ctrl-D below last line to end) + print("Enter your message (Ctrl-D below last line to end):") + message = sys.stdin.read() + + print("Message signed by puplic key : {pubkey}".format(pubkey=signing_key.pubkey)) + + comment = "generated by Duniterpy {0}".format(__version__) + # Dash escape the message and sign it + aa_cleartext_message = AsciiArmor.create(message, None, [signing_key], None, + signatures_comment=comment) + + # Save cleartext ascii armor message in a file + with open(CLEARTEXT_AA_MESSAGE_PATH, 'w') as file_handler: + file_handler.write(aa_cleartext_message) + + print("Cleartext Ascii Armor Message saved in file ./{0}".format(CLEARTEXT_AA_MESSAGE_PATH)) diff --git a/examples/save_ascii_armor_encrypted_message.py b/examples/save_encrypted_ascii_armor_message.py similarity index 74% rename from examples/save_ascii_armor_encrypted_message.py rename to examples/save_encrypted_ascii_armor_message.py index feb5f77f..8df7c273 100644 --- a/examples/save_ascii_armor_encrypted_message.py +++ b/examples/save_encrypted_ascii_armor_message.py @@ -3,9 +3,11 @@ from duniterpy import __version__ from duniterpy.key import AsciiArmor, SigningKey -################################################ +# CONFIG ####################################### + +ENCRYPTED_AA_MESSAGE_PATH = '/tmp/duniter_aa_encrypted_message.txt' -AA_ENCRYPTED_MESSAGE_FILENAME = 'duniter_aa_encrypted_message.txt' +################################################ if __name__ == '__main__': # Ask public key of the recipient @@ -23,13 +25,15 @@ if __name__ == '__main__': # Enter the message message = input("Enter your message: ") + print("Message signed by puplic key : {pubkey}".format(pubkey=signing_key.pubkey)) + comment = "generated by Duniterpy {0}".format(__version__) # Encrypt the message, only the recipient secret key will be able to decrypt the message encrypted_message = AsciiArmor.create(message, pubkeyBase58, [signing_key], message_comment=comment, signatures_comment=comment) # Save encrypted message in a file - with open(AA_ENCRYPTED_MESSAGE_FILENAME, 'w') as file_handler: + with open(ENCRYPTED_AA_MESSAGE_PATH, 'w') as file_handler: file_handler.write(encrypted_message) - print("Ascii Armor Encrypted message saved in file ./{0}".format(AA_ENCRYPTED_MESSAGE_FILENAME)) + print("Encrypted Ascii Armor Message saved in file ./{0}".format(ENCRYPTED_AA_MESSAGE_PATH)) -- GitLab