Skip to content

Commit

Permalink
Merge pull request #256 from blockchain-certificates/feat/multiple-si…
Browse files Browse the repository at this point in the history
…gnatures-non-chained

Support non chained signatures
  • Loading branch information
lemoustachiste authored Jun 7, 2023
2 parents 45dd96c + 1ecd92f commit b4d939e
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 9 deletions.
4 changes: 2 additions & 2 deletions cert_issuer/blockchain_handlers/bitcoin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,12 @@ def instantiate_blockchain_handlers(app_config, file_mode=True):

if file_mode:
certificate_batch_handler = CertificateBatchHandler(secret_manager=secret_manager,
certificate_handler=CertificateV3Handler(),
certificate_handler=CertificateV3Handler(app_config),
merkle_tree=MerkleTreeGenerator(),
config=app_config)
else:
certificate_batch_handler = CertificateBatchWebHandler(secret_manager=secret_manager,
certificate_handler=CertificateWebV3Handler(),
certificate_handler=CertificateWebV3Handler(app_config),
merkle_tree=MerkleTreeGenerator(),
config=app_config)
if chain.is_mock_type():
Expand Down
10 changes: 8 additions & 2 deletions cert_issuer/certificate_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@


class CertificateV3Handler(CertificateHandler):
def __init__(self, app_config):
self.app_config = app_config

def get_byte_array_to_issue(self, certificate_metadata):
certificate_json = self._get_certificate_to_issue(certificate_metadata)
return JSONLDHandler.normalize_to_utf8(certificate_json)
Expand All @@ -22,7 +25,7 @@ def add_proof(self, certificate_metadata, merkle_proof):
:return:
"""
certificate_json = self._get_certificate_to_issue(certificate_metadata)
certificate_json = ProofHandler().add_proof(certificate_json, merkle_proof)
certificate_json = ProofHandler().add_proof(certificate_json, merkle_proof, self.app_config)

with open(certificate_metadata.blockchain_cert_file_name, 'w') as out_file:
out_file.write(json.dumps(certificate_json))
Expand All @@ -33,11 +36,14 @@ def _get_certificate_to_issue(self, certificate_metadata):
return certificate_json

class CertificateWebV3Handler(CertificateHandler):
def __init__(self, app_config):
self.app_config = app_config

def get_byte_array_to_issue(self, certificate_json):
return JSONLDHandler.normalize_to_utf8(certificate_json)

def add_proof(self, certificate_json, merkle_proof):
certificate_json = ProofHandler().add_proof(certificate_json, merkle_proof)
certificate_json = ProofHandler().add_proof(certificate_json, merkle_proof, self.app_config)
return certificate_json

class CertificateBatchWebHandler(BatchHandler):
Expand Down
12 changes: 12 additions & 0 deletions cert_issuer/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,18 @@ def add_arguments(p):
env_var='CONTEXT_FILE_PATHS',
nargs='+'
)
p.add_argument('--multiple_proofs',
default='chained',
type=str,
choices=['chained', 'concurrent'],
help='How to handle a document that was previously signed by another party. \n' +
'If the document has not been signed yet, a single proof will be added. \n' +
'"chained": Chained proof also sign the previous proof(s) of the document, making them ' +
'untemperable with in the final document, ie: a notary signs over the signatures of the buyer ' +
'and the seller in a real estate transaction. \n' +
'"concurrent": Concurrent proofs mean the parties independently sign the document without ' +
'the other parties\' signatures. Defaults to chained proofs.'
)


def get_config():
Expand Down
19 changes: 15 additions & 4 deletions cert_issuer/proof_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,31 @@ class ProofHandler:
def __init__(self):
self.contextUrls = ContextUrls()

def add_proof(self, certificate_json, merkle_proof):
def add_proof(self, certificate_json, merkle_proof, app_config=None):
if 'proof' in certificate_json:
if not isinstance(certificate_json['proof'], list):
# convert proof to list
initial_proof = certificate_json['proof']
certificate_json['proof'] = [initial_proof]
previous_proof = certificate_json['proof'][-1]
certificate_json['proof'].append(ChainedProof2021(previous_proof, merkle_proof).to_json_object())
self.update_context_for_chained_proof(certificate_json)
if self.is_multiple_proof_config_chained(app_config):
self.add_chained_proof(certificate_json, merkle_proof)
else:
certificate_json['proof'].append(merkle_proof)
else:
certificate_json['proof'] = merkle_proof
self.update_context_for_single_proof(certificate_json)
return certificate_json

def is_multiple_proof_config_chained(self, app_config):
if app_config is None:
return True
return app_config.multiple_proofs == 'chained'

def add_chained_proof(self, certificate_json, merkle_proof):
previous_proof = certificate_json['proof'][-1]
certificate_json['proof'].append(ChainedProof2021(previous_proof, merkle_proof).to_json_object())
self.update_context_for_chained_proof(certificate_json)

def update_context_for_chained_proof(self, certificate_json):
context = certificate_json['@context']
if self.contextUrls.merkle_proof_2019() not in context:
Expand Down
2 changes: 1 addition & 1 deletion tests/test_certificate_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ def xtest_add_proof(self, mock_open):
assert file_call in call_strings

def test_web_add_proof(self):
handler = CertificateWebV3Handler()
handler = CertificateWebV3Handler(None)
proof = {'a': 'merkel'}
chain = mock.Mock()
certificate_json = {
Expand Down
32 changes: 32 additions & 0 deletions tests/test_proof_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,38 @@ def test_single_signature_3_1_update_context_merkle_proof_2019(self):
output = self.handler.add_proof(fixture_certificate_json, fixture_proof)
self.assertIn(self.contextUrls.merkle_proof_2019(), output['@context'])

def test_multiple_non_chained_signature(self):
fixture_initial_proof = {
'type': 'Ed25519Signature2020',
'created': '2022-05-02T16:36:22.933Z',
'verificationMethod': 'did:key:z6MkjHnntGvtLjwfAMHWTAXXGJHhVL3DPtaT9BHmyTjWpjqs#z6MkjHnntGvtLjwfAMHWTAXXGJHhVL3DPtaT9BHmyTjWpjqs',
'proofPurpose': 'assertionMethod',
'proofValue': 'zAvFt59599JweBZ4zPP6Ge8LhKgECtBvDRmjG5VQbgEkPCiyMcM9QAPanJgSCs6RRGcKu96qNpfmpe9eTygpFZP6'
}
fixture_certificate_json = {
'@context': [
'https://www.w3.org/2018/credentials/v1',
'https://w3id.org/blockcerts/v3'
],
'kek': 'kek',
'proof': fixture_initial_proof
}
fixture_proof = {
'type': 'MerkleProof2019',
'created': '2022-05-05T08:05:14.912828',
'proofValue': 'zMcm4LfQFUZkWZyLJp1bqtXF8vkZZwp79x7Nvt5BmN2XV4usLLtDoeqiq3et923mcWfXde4a3m4f57yUZcATCbBXV1byb5AXbV8EzT6E8B9JKf3scvxxZCBVePtV4SrhYysAiLNJ9N2R8LgnpJ47wnQHkaTB1AMxrcLEHUTxm4zJTtQqf9orDLf3L4VoLzmST7ZzsDjuX9cw2hZ3Aazhhjy7swG44xfF1PC73SyCv77pDnJ6BSHm3azmbVG6BXv1EPtwF4J1YRqwojBEWk9nDgduACR7b9qNhQ46ND4B5vL8p3LkqTh',
'proofPurpose': 'assertionMethod',
'verificationMethod': 'did:ion:EiA_Z6LQILbB2zj_eVrqfQ2xDm4HNqeJUw5Kj2Z7bFOOeQ#key-1'
}
class MockConfig:
multiple_proofs = 'concurrent'

output = self.handler.add_proof(fixture_certificate_json, fixture_proof, MockConfig())
self.assertEqual(output['proof'], [
fixture_initial_proof,
fixture_proof
])

def test_multiple_two_chained_signature(self):
fixture_initial_proof = {
'type': 'Ed25519Signature2020',
Expand Down

0 comments on commit b4d939e

Please sign in to comment.