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