Mise à jour de GitLab prévue ce 29 mai 2020 à partir de 9h00 CEST | GitLab upgrade planned the 29th of May 2020 from 9:00 AM CEST

Commit 21914877 authored by Vincent Texier's avatar Vincent Texier

issue #52 Refactor Certification document to respect superclass overload (break BC)

Certification methods respect documents methods arguments
Certification accept Identity or pubkey in constructor
Certification created from inline needs to have self.identity populated later
to use raw/sign/signed_raw methods
parent e5f3c4dd
import base64
import logging
import re
from typing import Optional, TypeVar, Type
from typing import Optional, TypeVar, Type, Union
from .block_uid import BlockUID
from .document import Document, MalformedDocumentError
......@@ -136,8 +136,6 @@ Timestamp: {timestamp}
# required to type hint cls in classmethod
CertificationType = TypeVar('CertificationType', bound='Certification')
# todo: certification document should be created with the certified Identity document in arguments
class Certification(Document):
......@@ -169,7 +167,7 @@ class Certification(Document):
"IdtyTimestamp": re_idty_timestamp
def __init__(self, version: int, currency: str, pubkey_from: str, pubkey_to: str,
def __init__(self, version: int, currency: str, pubkey_from: str, identity: Union[Identity, str],
timestamp: BlockUID, signature: str) -> None:
......@@ -177,13 +175,14 @@ class Certification(Document):
:param version: the UCP version
:param currency: the currency of the blockchain
:param pubkey_from: Pubkey of the certifier
:param pubkey_to: Pubkey of the certified
:param identity: Document instance of the certified identity or identity pubkey string
:param timestamp: the blockuid
:param signature: the signature of the document
super().__init__(version, currency, [signature])
self.pubkey_from = pubkey_from
self.pubkey_to = pubkey_to
self.identity = identity if isinstance(identity, Identity) else None
self.pubkey_to = identity.pubkey if isinstance(identity, Identity) else identity
self.timestamp = timestamp
......@@ -209,16 +208,16 @@ class Certification(Document):
pubkey_from = Certification.parse_field("Issuer", lines[n])
n += 1
pubkey_to = Certification.parse_field("IdtyIssuer", lines[n])
identity_pubkey = Certification.parse_field("IdtyIssuer", lines[n])
n += 1
Certification.parse_field("IdtyUniqueID", lines[n])
identity_uid = Certification.parse_field("IdtyUniqueID", lines[n])
n += 1
BlockUID.from_str(Certification.parse_field("IdtyTimestamp", lines[n]))
identity_timestamp = BlockUID.from_str(Certification.parse_field("IdtyTimestamp", lines[n]))
n += 1
Certification.parse_field("IdtySignature", lines[n])
identity_signature = Certification.parse_field("IdtySignature", lines[n])
n += 1
timestamp = BlockUID.from_str(Certification.parse_field("CertTimestamp", lines[n]))
......@@ -226,7 +225,9 @@ class Certification(Document):
signature = Certification.parse_field("Signature", lines[n])
return cls(version, currency, pubkey_from, pubkey_to, timestamp, signature)
identity = Identity(version, currency, identity_pubkey, identity_uid, identity_timestamp, identity_signature)
return cls(version, currency, pubkey_from, identity, timestamp, signature)
def from_inline(cls: Type[CertificationType], version: int, currency: str, blockhash: Optional[str],
......@@ -234,6 +235,9 @@ class Certification(Document):
Return Certification instance from inline document
Only self.pubkey_to is populated.
You must populate self.identity with an Identity instance to use raw/sign/signed_raw methods
:param version: Version of document
:param currency: Name of the currency
:param blockhash: Hash of the block
......@@ -254,13 +258,13 @@ class Certification(Document):
signature = cert_data.group(4)
return cls(version, currency, pubkey_from, pubkey_to, timestamp, signature)
def raw_for_certified(self, certified: Identity) -> str:
def raw(self) -> str:
Return a raw document of the self-certification of the Identity
:param Identity certified: Identity document instance
Return a raw document of the certification
if not isinstance(self.identity, Identity):
raise MalformedDocumentError("Can not return full certification document created from inline")
return """Version: {version}
Type: Certification
Currency: {currency}
......@@ -273,35 +277,39 @@ CertTimestamp: {timestamp}
def sign_for_certified(self, certified: Identity, keys: list) -> None:
def sign(self, keys: list) -> None:
Sign the current document with the keys for the certified Identity given
Warning : current signatures will be replaced with the new ones.
:param certified: Identity instance certified
:param keys: List of libnacl key instances
if not isinstance(self.identity, Identity):
raise MalformedDocumentError("Can not return full certification document created from inline")
self.signatures = []
for key in keys:
signing = base64.b64encode(key.signature(bytes(self.raw_for_certified(certified), 'ascii')))
signing = base64.b64encode(key.signature(bytes(self.raw(), 'ascii')))
logging.debug("Signature : \n{0}".format(signing.decode("ascii")))
def signed_raw_for_certified(self, certified: Identity) -> str:
def signed_raw(self) -> str:
Return signed raw document of the certification for the certified Identity instance
:param certified: Certified Identity instance
raw = self.raw_for_certified(certified)
if not isinstance(self.identity, Identity):
raise MalformedDocumentError("Can not return full certification document created from inline")
raw = self.raw()
signed = "\n".join(self.signatures)
signed_raw = raw + signed + "\n"
return signed_raw
......@@ -319,6 +327,7 @@ CertTimestamp: {timestamp}
# required to type hint cls in classmethod
RevocationType = TypeVar('RevocationType', bound='Revocation')
# todo: Revocation document should be created with the revoked Identity document in arguments
......@@ -70,17 +70,12 @@ def get_certification_document(current_block: dict, self_cert_document: Identity
:rtype: Certification
# construct Certification Document
certification = Certification(
timestamp=BlockUID(current_block['number'], current_block['hash']),
certification = Certification(version=10, currency=current_block['currency'], pubkey_from=from_pubkey,
timestamp=BlockUID(current_block['number'], current_block['hash']), signature="")
# sign document
key = SigningKey(salt, password)
certification.sign_for_certified(self_cert_document, [key])
return certification
......@@ -118,7 +113,7 @@ async def main():
certification = get_certification_document(current_block, identity, pubkey_from, salt, password)
# Here we request for the path wot/certify
response = await client(bma.wot.certify, certification.signed_raw_for_certified(identity))
response = await client(bma.wot.certify, certification.signed_raw())
if response.status == 200:
print(await response.text())
......@@ -91,11 +91,11 @@ J3G9oM5AKYZNLAB5Wx499w61NuUoS57JVccTShUbGpCMjCqj9yXXqNq7dyZpDWA6BxipsiaMZhujMeBf
pubkey_to = "HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd"
timestamp = BlockUID(36, "1076F10A7397715D2BEE82579861999EA1F274AC")
signature = "SoKwoa8PFfCDJWZ6dNCv7XstezHcc2BbKiJgVDXv82R5zYR83nis9dShLgWJ5w48noVUHimdngzYQneNYSMV3rk"
selfcert = Identity(version, currency, pubkey_to, "lolcat",
identity = Identity(version, currency, pubkey_to, "lolcat",
BlockUID(32, "DB30D958EE5CB75186972286ED3F4686B8A1C2CD"),
certification = Certification(version, currency, pubkey_from, pubkey_to, timestamp, signature)
certification = Certification(version, currency, pubkey_from, identity, timestamp, signature)
result = """Version: 2
Type: Certification
......@@ -108,10 +108,10 @@ IdtySignature: J3G9oM5AKYZNLAB5Wx499w61NuUoS57JVccTShUbGpCMjCqj9yXXqNq7dyZpDWA6B
CertTimestamp: 36-1076F10A7397715D2BEE82579861999EA1F274AC
self.assertEqual(certification.signed_raw_for_certified(selfcert), result)
self.assertEqual(certification.signed_raw(), result)
from_raw = Certification.from_signed_raw(certification.signed_raw_for_certified(selfcert))
self.assertEqual(from_raw.signed_raw_for_certified(selfcert), result)
from_raw = Certification.from_signed_raw(certification.signed_raw())
self.assertEqual(from_raw.signed_raw(), result)
def test_revokation_from_inline(self):
version = 2
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment