def __init__( self, currency: str, pubkey: str, challenge: Optional[str] = None, signature: Optional[str] = None, ) -> None: """ Init Connect message document :param currency: Name of the currency :param pubkey: Public key of the node :param challenge: [Optional, default=None] Big random string, typically an uuid :param signature: [Optional, default=None] Base64 encoded signature of raw formated document """ if signature is not None: signatures = [signature] else: signatures = [] super().__init__(self.version, currency, signatures) self.pubkey = pubkey if challenge is None: # create challenge self.challenge = get_ws2p_challenge() else: self.challenge = challenge if signature is not None: # verify signature verifying_key = VerifyingKey(self.pubkey) verifying_key.verify_document(self)
def test_ws2p_headv0(self): headv0, _ = HeadV0.from_inline("WS2P:HEAD:3dnbnYY9i2bHMQUGyFp5GVvJ2wBkVpus31cDJA5cfRpj:" "54813-00000A24802B33B71A91B6E990038C145A4815A45C71E57B2F2EF393183C7E2C", "a1vAAM666kPsMCFTbkgkcCsqHf8nmXR+Lh3D3u+BaXzmArj7kwlItbdGUs4fc9QUG5Lp4TwPS7nhOM5t1Kt6CA==") verifying_key = VerifyingKey(headv0.pubkey) self.assertTrue(verifying_key.verify_ws2p_head(headv0))
def test_block_document(self): block_document = """Version: 10 Type: Block Currency: g1 Number: 15145 PoWMin: 80 Time: 1493684276 MedianTime: 1493681245 UnitBase: 0 Issuer: 6fFt4zdvtNyVcfJn7Y41mKLmMDizyK3nVeNW3qdDXzpc IssuersFrame: 106 IssuersFrameVar: 0 DifferentIssuersCount: 21 PreviousHash: 00000A0CE0AE54F3F6B63383F386067160C477B5338FB93AF3AF0776A959AA32 PreviousIssuer: D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx MembersCount: 98 Identities: Joiners: Actives: Leavers: Revoked: Excluded: Certifications: Transactions: InnerHash: AA01ABD5C6D3F99A189C0CF0E37768DA0F876526AF93FE150E92B135D4AD0D85 Nonce: 10300000099432 """ block_signature = "Uxa3L+/m/dWLex2xSh7Jv1beAn4f99BmoYAs7iX3Lr+t1l5jzJpd9m4iI1cHppIizCgbg6ztaiZedQ+Mp6KuDg==" block = Block.from_signed_raw(block_document + block_signature + "\n") verifying_key = VerifyingKey(block.issuer) self.assertTrue(verifying_key.verify_document(block))
def test_ws2p_headv2(self): headv2, _ = HeadV2.from_inline("WS2POCA:HEAD:2:D3krfq6J9AmfpKnS3gQVYoy7NzGCc61vokteTS8LJ4YH:" "99393-0000017256006BFA979565F1280488D5831DD66054069E46A3EDEB1AECDBBF13:cb36b021:" "duniter:1.6.21:1:20:19", "CgD1vaImPWZUCDFt5HDHUdjCTFcIwW5ndiCx6kXioFLZoz1a4WhCFYXvjI2N8+jEwQdWtf5+yNoHonqBSqirAQ==") verifying_key = VerifyingKey(headv2.pubkey) self.assertTrue(verifying_key.verify_ws2p_head(headv2))
def test_ws2p_headv1(self): headv1, _ = HeadV1.from_inline("WS2POCAIC:HEAD:1:HbTqJ1Ts3RhJ8Rx4XkNyh1oSKmoZL1kY5U7t9mKTSjAB:" "102131-0000066028B991BDFE3FF6DBA84EF519F76B62EA3787BC29D9A05557675B1F16:1152e46e:" "duniter:1.6.21:1", "ZGpT8HG4uX5Hc96gqhzIkkELVjGQKDp2/L+7BTFG5ODxGYWd2VX/H+hdZRqf0iUWRNuhxlequ68kkwMaE6ymBw==") verifying_key = VerifyingKey(headv1.pubkey) self.assertTrue(verifying_key.verify_ws2p_head(headv1))
def handle_new_node(self, peer): key = VerifyingKey(peer.pubkey) if key.verify_document(peer): if len(self._discovery_stack) < 1000 \ and peer.signatures[0] not in [p.signatures[0] for p in self._discovery_stack]: self._logger.debug("Stacking new peer document : {0}".format(peer.pubkey)) self._discovery_stack.append(peer) else: self._logger.debug("Wrong document received : {0}".format(peer.signed_raw()))
def test_get_verified_data(self): sign_key = SigningKey.from_credentials("alice", "password", ScryptParams()) message = "Hello world with utf-8 chars like éàè !" # Sign the message, the signed string is the message itself plus the # signature signed_message = sign_key.sign(bytes(message, 'utf-8')) # type: bytes # Verify the message! verifier = VerifyingKey(sign_key.pubkey) verified_message = verifier.get_verified_data(signed_message) self.assertEqual(message, verified_message.decode('utf-8'))
def test_peer_signature(self): signed_raw = """Version: 2 Type: Peer Currency: test_net PublicKey: 8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU Block: 2086-00005D6FC6E22FB308D8815A565A01C66FFB7DC761D616DE0698F6322565F1D6 Endpoints: BASIC_MERKLED_API testnet.duniter.inso.ovh 80 4aQ/sfqFAFUeYkkLdC2OfgXqTBjCIcMptpR/GIlGqbe4aFVJcy9NEVAFx7sHiLuAb+VNnec3XHHC+xOk3MLzDA== """ "" peer = Peer.from_signed_raw(signed_raw) pubkey = "8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU" verifying_key = VerifyingKey(pubkey) self.assertTrue(verifying_key.verify_document(peer))
def test_transaction_document(self): transaction_document = """TX:10:1:6:6:2:1:0 278644-000004546FCB16F2851A8B6D1066219B0EBB3580C882850411618E35241719EA 8rYgYd64F2Y3Gfxwohjrc7K3zSNpDz79yNxRJorUwmse 1011:0:D:8rYgYd64F2Y3Gfxwohjrc7K3zSNpDz79yNxRJorUwmse:278052 1011:0:D:8rYgYd64F2Y3Gfxwohjrc7K3zSNpDz79yNxRJorUwmse:278333 1011:0:D:8rYgYd64F2Y3Gfxwohjrc7K3zSNpDz79yNxRJorUwmse:278609 1011:0:T:4116D06975AE613C96183390FC5A2BE2561F36C86F5CFE69EB23E3B517AA6F17:1 20330:0:T:56D8A0ACE3BC7E1173FF8BFB8A97A2F3353B6F3AEBCF4923C8BE2E81FDCC0685:1 11121:0:T:7CC29A8707D72936ED0EB9C618CEB3278DFAB4647B6639AA09620FA31EAD95D8:1 0:SIG(0) 1:SIG(0) 2:SIG(0) 3:SIG(0) 4:SIG(0) 5:SIG(0) 30000:0:SIG(2mKmto464oWCVsRgcYM6vpwsLsGk6MhMtrBKf7DTAU34) 5495:0:SIG(8rYgYd64F2Y3Gfxwohjrc7K3zSNpDz79yNxRJorUwmse) Solde huile Millepertuis rgjOmzFH5h+hkDbJLk1b88X7Z83HMgTa5rBckeMSdF/yZtItN3zMn09MphcXjffdrKcK+MebwoisLJqV+jXrDg== """ tx = Transaction.from_compact("g1", transaction_document) verifying_key = VerifyingKey(tx.issuers[0]) self.assertTrue(verifying_key.verify_document(tx))
def test_from_sign_to_verify(self): sign_key = SigningKey("saltsalt", "passwordpassword") verify_key = VerifyingKey(sign_key.pubkey) self.assertEqual(verify_key.vk, sign_key.vk)
from duniterpy.key import VerifyingKey if __name__ == '__main__': if len(sys.argv) < 2: print(""" Usage: python verify_signed_message.py SIGNED_MESSAGE_FILEPATH """) # capture signed message filepath argument signed_message_path = sys.argv[1] # ask public key of the signer pubkeyBase58 = input("Enter public key of the message issuer: ") # open signed message file with open(signed_message_path, 'rb') as file_handler: signed_message = file_handler.read() # Verify the message! verifier = VerifyingKey(pubkeyBase58) try: message = verifier.get_verified_data(signed_message).decode('utf-8') print("Signature valid for this message:") except ValueError as error: message = str(error) print(message)
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) :: { 'message': { 'fields': {}, 'content': str, }, 'signatures': [ {'pubkey': str, 'valid': bool, fields: {}} ] } :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 :exception MissingSigningKeyException: Raise an exception if no keypair given for encrypted message :return: """ # regex patterns regex_begin_message = compile(BEGIN_MESSAGE_HEADER) regex_end_message = compile(END_MESSAGE_HEADER) regex_begin_signature = compile(BEGIN_SIGNATURE_HEADER) regex_end_signature = compile(END_SIGNATURE_HEADER) regex_fields = compile("^(Version|Comment): (.+)$") # trim message to get rid of empty lines ascii_armor_message = ascii_armor_message.strip(" \t\n\r") # init vars parsed_result = { 'message': { 'fields': {}, 'content': '', }, 'signatures': [] } # type: Dict[str, Any] cursor_status = 0 message = '' signatures_index = 0 # parse each line... for line in ascii_armor_message.splitlines(True): # if begin message header detected... if regex_begin_message.match(line): cursor_status = ON_MESSAGE_FIELDS continue # if we are on the fields lines... if cursor_status == ON_MESSAGE_FIELDS: # parse field m = regex_fields.match(line.strip()) if m: # capture field msg_field_name = m.groups()[0] msg_field_value = m.groups()[1] parsed_result['message']['fields'][msg_field_name] = msg_field_value continue # if blank line... if line.strip("\n\t\r ") == '': cursor_status = ON_MESSAGE_CONTENT continue # if we are on the message content lines... if cursor_status == ON_MESSAGE_CONTENT: # if a header is detected, end of message content... if line.startswith(HEADER_PREFIX): # if field Version is present, the message is encrypted... if 'Version' in parsed_result['message']['fields']: # 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 # decrypt message with secret key from keypair message = AsciiArmor._decrypt(message, signing_key) # save message content in result parsed_result['message']['content'] = message # if message end header... if regex_end_message.match(line): # stop parsing break # if signature begin header... if regex_begin_signature.match(line): # add signature dict in list parsed_result['signatures'].append({ 'fields': {} }) cursor_status = ON_SIGNATURE_FIELDS continue else: # 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: # parse field m = regex_fields.match(line.strip()) if m: # capture field sig_field_name = m.groups()[0] sig_field_value = m.groups()[1] parsed_result['signatures'][signatures_index]['fields'][sig_field_name] = sig_field_value continue # if blank line... if line.strip("\n\t\r ") == '': cursor_status = ON_SIGNATURE_CONTENT continue # if we are on the signature content... if cursor_status == ON_SIGNATURE_CONTENT: # if no public keys provided... if sender_pubkeys is None: # raise exception raise PARSER_MISSING_PUBLIC_KEYS_EXCEPTION # if end signature header detected... if regex_end_signature.match(line): # end of parsing break # if begin signature header detected... if regex_begin_signature.match(line): signatures_index += 1 cursor_status = ON_SIGNATURE_FIELDS continue for pubkey in sender_pubkeys: verifier = VerifyingKey(pubkey) signature = base64.b64decode(line) parsed_result['signatures'][signatures_index]['pubkey'] = pubkey try: libnacl.crypto_sign_verify_detached(signature, message, verifier.vk) parsed_result['signatures'][signatures_index]['valid'] = True except ValueError: parsed_result['signatures'][signatures_index]['valid'] = False return parsed_result
def test_from_credentials(self): sign_key = SigningKey.from_credentials("alice", "password", ScryptParams()) verify_key = VerifyingKey(sign_key.pubkey) self.assertEqual(verify_key.vk, sign_key.vk)
def test_from_sign_to_verify(self): sign_key = SigningKey("saltsalt", "passwordpassword", ScryptParams(4096, 16, 1)) verify_key = VerifyingKey(sign_key.pubkey) self.assertEqual(verify_key.vk, sign_key.vk)