Commit 1592d425 authored by Vincent Texier's avatar Vincent Texier

[fix] #78 Add unit tests and fix errors in AsciiArmor class

parent 632a77c7
......@@ -92,8 +92,7 @@ class AsciiArmor:
message = message.rstrip("\n\r") + "\n"
# create block with headers
ascii_armor_block = """
{begin_message_header}
ascii_armor_block = """{begin_message_header}
""".format(begin_message_header=BEGIN_MESSAGE_HEADER)
# if encrypted message...
......@@ -247,7 +246,7 @@ class AsciiArmor:
# TODO: add parse from credentials to use scrypt field creating SigningKey
@staticmethod
def parse(ascii_armor_block: str, signing_key: Optional[SigningKey] = None,
def parse(ascii_armor_message: str, signing_key: Optional[SigningKey] = None,
sender_pubkeys: Optional[List[str]] = None) -> dict:
"""
Return a dict with parsed content (decrypted message, signature validation)
......@@ -263,7 +262,7 @@ class AsciiArmor:
]
}
:param ascii_armor_block: The Ascii Armor Message Block including BEGIN and END headers
:param ascii_armor_message: The Ascii Armor Message Block including BEGIN and END headers
:param signing_key: Optional Libnacl SigningKey instance to decrypt message
:param sender_pubkeys: Optional sender's public keys list to verify signatures
:exception libnacl.CryptError: Raise an exception if keypair fail to decrypt the message
......@@ -271,6 +270,7 @@ class AsciiArmor:
:return:
"""
# regex patterns
regex_begin_message = compile(BEGIN_MESSAGE_HEADER)
regex_end_message = compile(END_MESSAGE_HEADER)
regex_begin_signature = compile(BEGIN_SIGNATURE_HEADER)
......@@ -278,7 +278,9 @@ class AsciiArmor:
regex_fields = compile("^(Version|Scrypt|Comment): (.+)$")
# trim message to get rid of empty lines
ascii_armor_block.strip(" \t\n\r")
ascii_armor_message = ascii_armor_message.strip(" \t\n\r")
# init vars
parsed_result = {
'message':
{
......@@ -290,7 +292,9 @@ class AsciiArmor:
cursor_status = 0
message = ''
signatures_index = 0
for line in ascii_armor_block.splitlines(True):
# parse each line...
for line in ascii_armor_message.splitlines(True):
# if begin message header detected...
if regex_begin_message.match(line):
......@@ -316,12 +320,13 @@ class AsciiArmor:
# if we are on the message content lines...
if cursor_status == ON_MESSAGE_CONTENT:
# if a header is detected...
# if a header is detected, end of message content...
if line.startswith(HEADER_PREFIX):
# if field Version is present...
# if field Version is present, the message is encrypted...
if 'Version' in parsed_result['message']['fields']:
# If keypair instance not given...
# If keypair instance to decrypt not given...
if signing_key is None:
# SigningKey keypair is mandatory to decrypt the message...
raise PARSER_MISSING_SIGNING_KEY_EXCEPTION
......@@ -346,8 +351,13 @@ class AsciiArmor:
cursor_status = ON_SIGNATURE_FIELDS
continue
else:
# concatenate striped dash escaped line to message content
message += AsciiArmor._parse_dash_escaped_line(line)
# if field Version is present, the message is encrypted...
if 'Version' in parsed_result['message']['fields']:
# concatenate encrypted line to message content
message += line
else:
# concatenate cleartext striped dash escaped line to message content
message += AsciiArmor._parse_dash_escaped_line(line)
# if we are on a signature fields zone...
if cursor_status == ON_SIGNATURE_FIELDS:
......
......@@ -25,6 +25,6 @@ if __name__ == '__main__':
with open(ENCRYPTED_AA_MESSAGE_PATH, 'r') as file_handler:
ascii_armor_block = file_handler.read()
print("Encrypted Ascii Armor Message loaded from file ./{0}".format(ENCRYPTED_AA_MESSAGE_PATH))
print("Encrypted Ascii Armor Message loaded from file {0}".format(ENCRYPTED_AA_MESSAGE_PATH))
print(AsciiArmor.parse(ascii_armor_block, signing_key, [pubkeyBase58]))
......@@ -36,4 +36,4 @@ if __name__ == '__main__':
with open(ENCRYPTED_AA_MESSAGE_PATH, 'w') as file_handler:
file_handler.write(encrypted_message)
print("Encrypted Ascii Armor Message saved in file ./{0}".format(ENCRYPTED_AA_MESSAGE_PATH))
print("Encrypted Ascii Armor Message saved in file {0}".format(ENCRYPTED_AA_MESSAGE_PATH))
from duniterpy.key import AsciiArmor, SigningKey
from duniterpy.key.ascii_armor import BEGIN_MESSAGE_HEADER, BEGIN_SIGNATURE_HEADER, END_SIGNATURE_HEADER, \
END_MESSAGE_HEADER
import unittest
class TestAsciiArmor(unittest.TestCase):
def test_create_encrypted_and_signed(self):
# pubkey of recipient
alice_signing_key = SigningKey.from_credentials("alice", "password")
# signing key of issuer
bob_signing_key = SigningKey.from_credentials("bob", "password")
# message
message = """
Hello world !
Héhé ;-)
This is a utf-8 message...
"""
# create encrypted and signed ascii armor message
encrypted_and_signed_aa_message = AsciiArmor.create(message, alice_signing_key.pubkey, [bob_signing_key])
# split in lines for check up
aa_message_lines = encrypted_and_signed_aa_message.splitlines()
# check before message
self.assertEqual(aa_message_lines[0], BEGIN_MESSAGE_HEADER)
self.assertTrue(aa_message_lines[1].startswith("Version:"))
self.assertTrue(aa_message_lines[2].startswith("Scrypt:"))
self.assertEqual("", aa_message_lines[3].strip())
# check after message
self.assertEqual(aa_message_lines[5], BEGIN_SIGNATURE_HEADER)
self.assertTrue(aa_message_lines[6].startswith("Version:"))
self.assertTrue(aa_message_lines[7].startswith("Scrypt:"))
self.assertEqual("", aa_message_lines[8].strip())
self.assertEqual(aa_message_lines[10], END_SIGNATURE_HEADER)
# parse ascii armor message
result = AsciiArmor.parse(encrypted_and_signed_aa_message, alice_signing_key, [bob_signing_key.pubkey])
# check result
self.assertEqual(message + "\n", result['message']['content'])
self.assertTrue(message + "\n", result['signatures'][0]['valid'])
def test_create_encrypted(self):
# pubkey of recipient
alice_signing_key = SigningKey.from_credentials("alice", "password")
# message
message = """
Hello world !
Héhé ;-)
This is a utf-8 message...
"""
# create encrypted and signed ascii armor message
encrypted_aa_message = AsciiArmor.create(message, alice_signing_key.pubkey)
# split in lines for check up
aa_message_lines = encrypted_aa_message.splitlines()
# check before message
self.assertEqual(aa_message_lines[0], BEGIN_MESSAGE_HEADER)
self.assertTrue(aa_message_lines[1].startswith("Version:"))
self.assertTrue(aa_message_lines[2].startswith("Scrypt:"))
self.assertEqual("", aa_message_lines[3].strip())
# check after message
self.assertEqual(aa_message_lines[5], END_MESSAGE_HEADER)
# parse ascii armor message
result = AsciiArmor.parse(encrypted_aa_message, alice_signing_key)
# check result
self.assertEqual(message + "\n", result['message']['content'])
def test_create_signed_cleartext(self):
# signing key of issuer
bob_signing_key = SigningKey.from_credentials("bob", "password")
# message
message = """
Hello world !
Héhé ;-)
This is a utf-8 message...
"""
# create encrypted and signed ascii armor message
signed_cleartext_aa_message = AsciiArmor.create(message, None, [bob_signing_key])
# split in lines for check up
aa_message_lines = signed_cleartext_aa_message.splitlines()
# check before message
self.assertEqual(aa_message_lines[0], BEGIN_MESSAGE_HEADER)
self.assertEqual("", aa_message_lines[1].strip())
# check after message
self.assertEqual(aa_message_lines[10], BEGIN_SIGNATURE_HEADER)
self.assertTrue(aa_message_lines[11].startswith("Version:"))
self.assertTrue(aa_message_lines[12].startswith("Scrypt:"))
self.assertEqual("", aa_message_lines[13].strip())
self.assertEqual(aa_message_lines[15], END_SIGNATURE_HEADER)
# parse ascii armor message
result = AsciiArmor.parse(signed_cleartext_aa_message, None, [bob_signing_key.pubkey])
# check result
self.assertEqual(message + "\n", result['message']['content'])
self.assertTrue(message + "\n", result['signatures'][0]['valid'])
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment