From 146cfe024fdfaed71d0a08ecc06fabd4c4382b5e Mon Sep 17 00:00:00 2001 From: arnabghose997 Date: Tue, 28 Feb 2023 11:13:41 +0530 Subject: [PATCH 1/3] refactor: createDID, deactivateDID, updateDID RPCs --- go.mod | 11 +- go.sum | 25 +- tests/e2e/ssi_tests/e2e_tests.py | 1053 ++++++++--------- tests/e2e/ssi_tests/generate_doc.py | 6 +- tests/e2e/ssi_tests/run.py | 18 +- tests/e2e/ssi_tests/transactions.py | 56 + x/ssi/client/cli/tx_utils.go | 2 +- x/ssi/keeper/client_spec.go | 11 +- x/ssi/keeper/controller.go | 136 --- x/ssi/keeper/did.go | 5 + x/ssi/keeper/msg_server.go | 212 ++++ x/ssi/keeper/msg_server_create_did.go | 129 ++ x/ssi/keeper/msg_server_credential.go | 74 +- x/ssi/keeper/msg_server_deactivate_did.go | 115 ++ x/ssi/keeper/msg_server_did.go | 269 ----- x/ssi/keeper/msg_server_schema.go | 27 +- x/ssi/keeper/msg_server_update_did.go | 220 ++++ x/ssi/keeper/namespace.go | 79 ++ x/ssi/tests/common.go | 15 +- x/ssi/tests/tx_create_did_test.go | 4 - x/ssi/tests/tx_create_schema_test.go | 96 -- .../tx_register_credential_status_test.go | 99 -- .../{common/constants.go => types/common.go} | 11 +- x/ssi/types/diddoc_validation.go | 324 +++++ x/ssi/types/message_did.go | 46 +- x/ssi/types/ssi_types.go | 32 +- x/ssi/types/stateless_verification.go | 58 - x/ssi/types/utils.go | 48 + x/ssi/verification/caip10.go | 6 +- x/ssi/verification/common_document_checks.go | 9 - x/ssi/verification/crypto.go | 95 +- x/ssi/verification/did_verification.go | 112 -- x/ssi/verification/signature_verification.go | 117 +- x/ssi/verification/utils.go | 91 -- 34 files changed, 1917 insertions(+), 1694 deletions(-) delete mode 100644 x/ssi/keeper/controller.go create mode 100644 x/ssi/keeper/msg_server_create_did.go create mode 100644 x/ssi/keeper/msg_server_deactivate_did.go delete mode 100644 x/ssi/keeper/msg_server_did.go create mode 100644 x/ssi/keeper/msg_server_update_did.go create mode 100644 x/ssi/keeper/namespace.go rename x/ssi/{common/constants.go => types/common.go} (82%) create mode 100644 x/ssi/types/diddoc_validation.go delete mode 100644 x/ssi/types/stateless_verification.go create mode 100644 x/ssi/types/utils.go delete mode 100644 x/ssi/verification/did_verification.go delete mode 100644 x/ssi/verification/utils.go diff --git a/go.mod b/go.mod index 6693eeb..d5eabd9 100644 --- a/go.mod +++ b/go.mod @@ -89,6 +89,7 @@ require ( github.com/mtibben/percent v0.2.1 // indirect github.com/multiformats/go-base32 v0.0.3 // indirect github.com/multiformats/go-base36 v0.1.0 // indirect + github.com/onsi/gomega v1.25.0 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml/v2 v2.0.5 // indirect github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect @@ -116,9 +117,11 @@ require ( go.etcd.io/bbolt v1.3.6 // indirect golang.org/x/crypto v0.1.0 // indirect golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect - golang.org/x/sys v0.1.0 // indirect - golang.org/x/term v0.1.0 // indirect - golang.org/x/text v0.4.0 // indirect + golang.org/x/mod v0.8.0 // indirect + golang.org/x/sys v0.5.0 // indirect + golang.org/x/term v0.5.0 // indirect + golang.org/x/text v0.7.0 // indirect + golang.org/x/tools v0.4.0 // indirect google.golang.org/protobuf v1.28.2-0.20220831092852-f930b1dc76e8 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect @@ -130,7 +133,7 @@ require ( github.com/golang/protobuf v1.5.2 github.com/multiformats/go-multibase v0.0.3 github.com/spf13/viper v1.13.0 - golang.org/x/net v0.1.0 // indirect + golang.org/x/net v0.6.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index 1b39601..536422a 100644 --- a/go.sum +++ b/go.sum @@ -926,8 +926,9 @@ github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5 github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.13.0 h1:7lLHu94wT9Ij0o6EWWclhu0aOh32VxhkwEJvzuWPeak= github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY= +github.com/onsi/gomega v1.25.0 h1:Vw7br2PCDYijJHSfBOWhov+8cAnUf8MfMaIOV323l6Y= +github.com/onsi/gomega v1.25.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= @@ -1378,7 +1379,8 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I= +golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1440,8 +1442,8 @@ golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20210903162142-ad29c8ab022f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0= -golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1564,13 +1566,13 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw= -golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1580,8 +1582,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1690,7 +1692,8 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE= +golang.org/x/tools v0.4.0 h1:7mTAgkunk3fr4GAloyyCasadO6h9zSsQZbwvcaIciV4= +golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/tests/e2e/ssi_tests/e2e_tests.py b/tests/e2e/ssi_tests/e2e_tests.py index 8480ef3..4bf4710 100644 --- a/tests/e2e/ssi_tests/e2e_tests.py +++ b/tests/e2e/ssi_tests/e2e_tests.py @@ -1,331 +1,469 @@ import os import sys sys.path.insert(1, os.getcwd()) - -import json +import time from utils import run_blockchain_command, generate_key_pair -from generate_doc import generate_did_document, \ - generate_schema_document, generate_cred_status_document -from transactions import form_create_schema_tx, form_did_create_tx, \ - form_create_cred_status_tx, form_did_deactivate_tx, form_did_update_tx, query_did +from generate_doc import generate_did_document, generate_schema_document, generate_cred_status_document +from transactions import form_did_create_tx_multisig, form_did_update_tx_multisig, \ + query_did, form_create_schema_tx, form_did_deactivate_tx_multisig, form_create_cred_status_tx from constants import DEFAULT_BLOCKCHAIN_ACCOUNT_NAME -# TC-8: Deactivated DID attempts to create Schema and Credential Status Documents. -def deactivated_did_should_not_create_ssi_elements(): - print("\n---- Test Started: TC-8: Deactivated DID attempts to create Schema and Credential Status Documents. ----\n") - print("In this workflow, a deactivated DID attempts to register Schema Doc and Credential Status Doc") - print("It is expected to fail\n") - - print("Registering a DID Document") - kp_did = generate_key_pair() - did_doc = generate_did_document(kp_did) - create_did_tx_cmd = form_did_create_tx(did_doc, kp_did, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME) - did_doc_id = did_doc["id"] - run_blockchain_command(create_did_tx_cmd, f"Registering DID with Id: {did_doc_id}") - - print("Deactivating the Registered DID Document") - deactivate_did_tx_cmd = form_did_deactivate_tx( - did_doc_id, - kp_did, - DEFAULT_BLOCKCHAIN_ACCOUNT_NAME - ) - run_blockchain_command(deactivate_did_tx_cmd, f"Deactivating DID with Id: {did_doc_id}") - - print("Deactivated DID attempts to register Schema Document") - schema_doc, schema_proof = generate_schema_document( - kp_did, - did_doc_id, - did_doc["authentication"][0] - ) - create_schema_cmd = form_create_schema_tx( - schema_doc, - schema_proof, - DEFAULT_BLOCKCHAIN_ACCOUNT_NAME - ) - schema_doc_id = schema_doc["id"] - run_blockchain_command(create_schema_cmd, f"Registering Schema with Id: {schema_doc_id} with {did_doc_id} being the author", True) - - print("Deactivated DID attempts to register Credential Status Document") - cred_doc, cred_proof = generate_cred_status_document( - kp_did, - did_doc_id, - did_doc["authentication"][0] - ) - register_cred_status_cmd = form_create_cred_status_tx( - cred_doc, - cred_proof, - DEFAULT_BLOCKCHAIN_ACCOUNT_NAME - ) - cred_id = cred_doc["claim"]["id"] - run_blockchain_command(register_cred_status_cmd, f"Registering Credential status with Id: {cred_id} with {did_doc_id} being the issuer", True) - print("\n--- Test Completed: TC-8: Deactivated DID attempts to create Schema and Credential Status Documents. ---\n") - -# TC-7: Parent DID adds multiple DIDs in its controller group, and removes itself. One of the controllers and the Parent DID attemps to change the DID Document. -def multiple_controllers_with_one_signer(): - print("\n--- Test Started: TC-7: Parent DID adds multiple DIDs in its controller group, and removes itself. One of the controllers and the Parent DID attemps to change the DID Document. ---\n") - print("In this worflow, two DIDs are added to another DID's Controller Group. One of them attempts to make change in the parent DID Document") - - print("Registering Parent DID Document") - kp_parent_did = generate_key_pair() - parent_did_doc = generate_did_document(kp_parent_did) - create_did_tx_cmd = form_did_create_tx(parent_did_doc, kp_parent_did, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME) - parent_did_doc_id = parent_did_doc["id"] - run_blockchain_command(create_did_tx_cmd, f"Registering DID with Id: {parent_did_doc_id}") - - print("Registering 1st Controller DID Document") - kp_controller_1_did = generate_key_pair() - controller_1_did_doc = generate_did_document(kp_controller_1_did) - create_did_tx_cmd = form_did_create_tx(controller_1_did_doc, kp_controller_1_did, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME) - controller_1_did_doc_id = controller_1_did_doc["id"] - run_blockchain_command(create_did_tx_cmd, f"Registering DID with Id: {controller_1_did_doc_id}") - - print("Registering 2nd Controller DID Document") - kp_controller_2_did = generate_key_pair(algo="secp256k1") - controller_2_did_doc = generate_did_document(kp_controller_2_did, algo="secp256k1") - create_did_tx_cmd = form_did_create_tx(controller_2_did_doc, kp_controller_2_did, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME, signing_algo="secp256k1") - controller_2_did_doc_id = controller_2_did_doc["id"] - run_blockchain_command(create_did_tx_cmd, f"Registering DID with Id: {controller_2_did_doc_id}") - - print("Adding 1st and 2nd DID Controllers to Parent DID's Controller group") - parent_did_doc = query_did(parent_did_doc_id)["didDocument"] - parent_did_doc["controller"] = [controller_1_did_doc_id, controller_2_did_doc_id] - update_did_tx_cmd = form_did_update_tx( - parent_did_doc, - kp_parent_did, - DEFAULT_BLOCKCHAIN_ACCOUNT_NAME - ) - run_blockchain_command(update_did_tx_cmd, f"Adding 1st and 2nd Controller DIDs to Parent DID's Control Group") - - print("2nd Controller trying to make a change in Parent DID") - parent_did_doc = query_did(parent_did_doc_id)["didDocument"] - parent_did_doc["context"] = ["websitebycontroller2.com"] - update_did_tx_cmd = form_did_update_tx( - parent_did_doc, - kp_controller_2_did, - DEFAULT_BLOCKCHAIN_ACCOUNT_NAME, - controller_2_did_doc["authentication"][0], - "secp256k1" - ) - run_blockchain_command(update_did_tx_cmd, f"2nd Controller trying to update Parent DID") - - print("Parent DID trying to update it's DID") - parent_did_doc = query_did(parent_did_doc_id)["didDocument"] - parent_did_doc["context"] = ["websitebyparent.com"] - update_did_tx_cmd = form_did_update_tx( - parent_did_doc, - kp_parent_did, - DEFAULT_BLOCKCHAIN_ACCOUNT_NAME - ) - run_blockchain_command(update_did_tx_cmd, f"Parent DID trying to update it's own DID Document", True) - print("\n--- Test Completed: TC-7: Parent DID adds multiple DIDs in its controller group, and removes itself. One of the controllers and the Parent DID attemps to change the DID Document. ---\n") - -# TC-6: Controller DID attempts to update the Parent DID Document -def controller_did_trying_to_update_diddoc(): - print("\n--- Test Started: TC-6: Controller DID attempts to update the Parent DID Document ---\n") - print("In this workflow, Controller DID attempts to update its Parent DID Document") - - print("Registering the canon DID Document") - kp_canon_did = generate_key_pair() - canon_did_doc = generate_did_document(kp_canon_did) - canon_did_doc["controller"] = [canon_did_doc["id"]] - create_did_tx_cmd = form_did_create_tx(canon_did_doc, kp_canon_did, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME) - canon_did_doc_id = canon_did_doc["id"] - run_blockchain_command(create_did_tx_cmd, f"Registering DID with Id: {canon_did_doc_id}") - - print("Registering the controller DID Document") - kp_controller_did = generate_key_pair() - controller_did_doc = generate_did_document(kp_controller_did) - create_did_tx_cmd = form_did_create_tx( - controller_did_doc, - kp_controller_did, - DEFAULT_BLOCKCHAIN_ACCOUNT_NAME - ) - controller_did_doc_id = controller_did_doc["id"] - run_blockchain_command(create_did_tx_cmd, f"Registering DID with Id: {controller_did_doc_id}") - - print("Adding the registered controller DID to Canon DID Document's controller group") - canon_did_doc = query_did(canon_did_doc_id)["didDocument"] - canon_did_doc["controller"] = [canon_did_doc_id, controller_did_doc_id] - update_did_tx_cmd = form_did_update_tx( - canon_did_doc, - kp_canon_did, - DEFAULT_BLOCKCHAIN_ACCOUNT_NAME - ) - run_blockchain_command(update_did_tx_cmd, f"Adding Controller DID to Canon DID's Control Group") - - print("Controller DID trying to update canon DID Document") - canon_did_doc = query_did(canon_did_doc_id)["didDocument"] - canon_did_doc["context"] = ["somenewwebsite.com"] - update_did_tx_cmd = form_did_update_tx( - canon_did_doc, - kp_controller_did, - DEFAULT_BLOCKCHAIN_ACCOUNT_NAME, - controller_did_doc["authentication"][0] - ) - run_blockchain_command(update_did_tx_cmd, f"Attempt by controller to make changes in Canon DID") - print("\n--- Test Completed: TC-6: Controller DID attempts to update the Parent DID Document ---\n") - -# TC-5: Non-Controller DID attempts to update a DID Document (Invalid Case) -def non_controller_did_trying_to_update_diddoc(): - print("\n--- Test Started: TC-5: Non-Controller DID attempts to update a DID Document (Invalid Case) ---\n") - print("In this workflow, Non-Controller DID attempts to update a DID Document") - print("This is expected to fail\n") - - print("Registering the canon DID Document") - kp_canon_did = generate_key_pair() - canon_did_doc = generate_did_document(kp_canon_did) - create_did_tx_cmd = form_did_create_tx(canon_did_doc, kp_canon_did, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME) - canon_did_doc_id = canon_did_doc["id"] - run_blockchain_command(create_did_tx_cmd, f"Registering DID with Id: {canon_did_doc_id}") - - print("Registering the non controller DID Document") - kp_non_controller_did = generate_key_pair() - non_controller_did_doc = generate_did_document(kp_non_controller_did) - create_did_tx_cmd = form_did_create_tx( - non_controller_did_doc, - kp_non_controller_did, - DEFAULT_BLOCKCHAIN_ACCOUNT_NAME - ) - non_controller_did_doc_id = non_controller_did_doc["id"] - run_blockchain_command(create_did_tx_cmd, f"Registering DID with Id: {non_controller_did_doc_id}") +# TC - I : Create DID scenarios +def create_did_test(): + print("\n--- Create DID Test ---\n") + + print("1. FAIL: Alice has a registered DID Document where Alice is the controller. Bob tries to register their DID Document by keeping both Alice and Bob as controllers, and by sending only his signature.\n") + kp_alice = generate_key_pair() + signers = [] + did_doc_string = generate_did_document(kp_alice) + did_doc_alice = did_doc_string["id"] + signPair_alice = { + "kp": kp_alice, + "verificationMethodId": did_doc_string["verificationMethod"][0]["id"], + "signing_algo": "ed25519" + } + signers.append(signPair_alice) + create_tx_cmd = form_did_create_tx_multisig(did_doc_string, signers, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME) + run_blockchain_command(create_tx_cmd, f"Registering Alice's DID with Id: {did_doc_alice}") + + kp_bob = generate_key_pair() + signers = [] + did_doc_string = generate_did_document(kp_bob) + did_doc_bob = did_doc_string["id"] + did_doc_string["controller"] = [did_doc_alice, did_doc_bob] + signPair_bob = { + "kp": kp_bob, + "verificationMethodId": did_doc_string["verificationMethod"][0]["id"], + "signing_algo": "ed25519" + } + signers.append(signPair_bob) + create_tx_cmd = form_did_create_tx_multisig(did_doc_string, signers, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME) + run_blockchain_command(create_tx_cmd, f"Registering Bob's DID with Id: {did_doc_bob}", True) + + print("2. PASS: Alice has a registered DID Document where Alice is the controller. Bob tries to register their DID Document by keeping both Alice and Bob as controllers, and by sending only both Alice's and Bob's signatures.\n") + signers = [] + signers.append(signPair_bob) + signers.append(signPair_alice) + create_tx_cmd = form_did_create_tx_multisig(did_doc_string, signers, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME) + run_blockchain_command(create_tx_cmd, f"Registering DID with Id: {did_doc_bob}") + + + print("3. PASS: Alice has a registered DID Document where Alice is the controller. She tries to create an organization DID, in which Alice is the only controller and it's verification method field is empty.\n") + kp_org = generate_key_pair() + signers = [] + did_doc_string = generate_did_document(kp_org) + did_doc_org = did_doc_string["id"] + did_doc_string["controller"] = [did_doc_alice] + did_doc_string["verificationMethod"] = [] + signers.append(signPair_alice) + create_tx_cmd = form_did_create_tx_multisig(did_doc_string, signers, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME) + run_blockchain_command(create_tx_cmd, f"Registering DID with Id: {did_doc_org}") + + #Alice creates a DID where the controller only has Alice's DID and the verfication method has two ETH wallets added. Signature of all hot wallets are passed + kp_hot_wallet_1 = generate_key_pair("recover-eth") + kp_hot_wallet_2 = generate_key_pair("recover-eth") + kp_org = generate_key_pair() + + did_doc_string = generate_did_document(kp_org) + did_doc_canon = did_doc_string["id"] + did_doc_string["controller"] = [did_doc_alice] + did_doc_string["verificationMethod"] = [ + { + "id": "did:hid:devnet:" + kp_hot_wallet_1["ethereum_address"] + "#k1", + "controller": did_doc_alice, + "type": "EcdsaSecp256k1RecoveryMethod2020", + "blockchainAccountId": "eip155:1:" + kp_hot_wallet_1["ethereum_address"], + }, + { + "id": "did:hid:devnet:" + kp_hot_wallet_2["ethereum_address"] + "#k2", + "controller": did_doc_alice, + "type": "EcdsaSecp256k1RecoveryMethod2020", + "blockchainAccountId": "eip155:1:" + kp_hot_wallet_2["ethereum_address"], + }, + ] - print("Non Controller DID trying to update canon DID Document") - canon_did_doc["context"] = canon_did_doc["context"].append("newwebsite.com") - update_did_tx_cmd = form_did_update_tx( - canon_did_doc, - kp_non_controller_did, - DEFAULT_BLOCKCHAIN_ACCOUNT_NAME, - non_controller_did_doc["authentication"][0] - ) - run_blockchain_command(update_did_tx_cmd, f"Attempt by non-controller to make changes in Canon DID", True) - print("\n--- Test Completed: TC-5: Non-Controller DID attempts to update a DID Document (Invalid Case) ---\n") - -# TC-1: A simple SSI flow where three elements of SSI (DID Document, Schema Document and Credential Status Document) -def simple_ssi_flow(): - print("\n--- Test Started: TC-1: A simple SSI flow where three elements of SSI (DID Document, Schema Document and Credential Status Document) ---\n") - print("In this workflow, a DID document is registered, following which a credential schema and a credential status document is registered\n") + signPair_hotWallet1 = { + "kp": kp_hot_wallet_1, + "verificationMethodId": did_doc_string["verificationMethod"][0]["id"], + "signing_algo": "recover-eth" + } + signPair_hotWallet2 = { + "kp": kp_hot_wallet_2, + "verificationMethodId": did_doc_string["verificationMethod"][1]["id"], + "signing_algo": "recover-eth" + } + + print("4. FAIL: Alice has a registered DID Document where Alice is the controller. Alice tries to register an Org DID Document where Alice is the sole controller, and there are two verification Methods, of type EcdsaSecp256k1RecoveryMethod2020, and Alice is the controller for each one of them. Signature is provided by only one of the VMs.\n") + signers = [] + signers.append(signPair_hotWallet2) + create_tx_cmd = form_did_create_tx_multisig(did_doc_string, signers, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME) + run_blockchain_command(create_tx_cmd, f"Registering DID with Id: {did_doc_canon}", True) + + print("5. PASS: Alice has a registered DID Document where Alice is the controller. Alice tries to register an Org DID Document where Alice is the sole controller, and there are two verification Methods, of type EcdsaSecp256k1RecoveryMethod2020, and Alice is the controller for each one of them. Signature is provided by both VMs.\n") + signers = [] + signers.append(signPair_hotWallet1) + signers.append(signPair_hotWallet2) + create_tx_cmd = form_did_create_tx_multisig(did_doc_string, signers, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME) + run_blockchain_command(create_tx_cmd, f"Registering DID with Id: {did_doc_canon}") - kp = generate_key_pair() + # Alice creates a DID where they keep the VM of their friend Eve in the verificationMethod list of the document + print("6. FAIL: Alice creates an Org DID where Alice is the controller, and she adds a verification method of her friend Eve. Only Alice sends the singature.\n") + kp_eve = generate_key_pair("secp256k1") + did_doc_string = generate_did_document(kp_eve, algo="secp256k1") + did_doc_eve = did_doc_string["id"] + did_doc_eve_vms = did_doc_string["verificationMethod"] + signers = [] + signPair_eve = { + "kp": kp_eve, + "verificationMethodId": did_doc_string["verificationMethod"][0]["id"], + "signing_algo": "secp256k1" + } + signers.append(signPair_eve) + create_tx_cmd = form_did_create_tx_multisig(did_doc_string, signers, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME) + run_blockchain_command(create_tx_cmd, f"Registering Eve's DID with Id: {did_doc_eve}") + + kp_random = generate_key_pair() + did_doc_string = generate_did_document(kp_random) + did_doc_string["controller"] = [did_doc_alice] + did_doc_string["verificationMethod"] = [ + did_doc_eve_vms[0] + ] + signers = [] + signers.append(signPair_alice) + create_tx_cmd = form_did_create_tx_multisig(did_doc_string, signers, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME) + run_blockchain_command(create_tx_cmd, f"Registering DID by passing only Alice's Signature", True) - print("Registering a DID Document") - did_doc_string = generate_did_document(kp) - did_doc_id = did_doc_string["id"] - create_tx_cmd = form_did_create_tx(did_doc_string, kp, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME) - run_blockchain_command(create_tx_cmd, f"Registering DID with Id: {did_doc_id}") + print("7. PASS: Alice creates an Org DID where Alice is the controller, and she adds a verification method of her friend Eve. Both Alice and Eve send their singatures.\n") + signers = [] + signers.append(signPair_alice) + signers.append(signPair_eve) + create_tx_cmd = form_did_create_tx_multisig(did_doc_string, signers, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME) + run_blockchain_command(create_tx_cmd, f"Registering DID by passing both Alice's and Eve's Signature") - print("Registering a Schema Document") - schema_doc, schema_proof = generate_schema_document( - kp, - did_doc_id, - did_doc_string["authentication"][0] - ) - create_schema_cmd = form_create_schema_tx( - schema_doc, - schema_proof, - DEFAULT_BLOCKCHAIN_ACCOUNT_NAME - ) - schema_doc_id = schema_doc["id"] - run_blockchain_command(create_schema_cmd, f"Registering Schema with Id: {schema_doc_id}") + print("\n--- Test Completed ---\n") - print("Registering a Credential Status Document") - cred_doc, cred_proof = generate_cred_status_document( - kp, - did_doc_id, - did_doc_string["authentication"][0] - ) - register_cred_status_cmd = form_create_cred_status_tx( - cred_doc, - cred_proof, - DEFAULT_BLOCKCHAIN_ACCOUNT_NAME - ) - cred_id = cred_doc["claim"]["id"] - run_blockchain_command(register_cred_status_cmd, f"Registering Credential status with Id: {cred_id}") - print("\n--- Test Completed: TC-1: A simple SSI flow where three elements of SSI (DID Document, Schema Document and Credential Status Document) ---\n") +# TC - II : Update DID scenarios +def update_did_test(): + print("\n--- Update DID Test ---\n") -# TC-2: Controller DID attempts to create Credential Schema and Credential Status Documents on behalf of Parent DID -def controller_creates_schema_cred_status(): - print("--- Test Started: TC-2: Controller DID attempts to create Credential Schema and Credential Status Documents on behalf of Parent DID ---\n") - print("In this workflow, a DID document registered with another DID Id in its controller group. The controller is expected to register schema and credential status") + print("1. FAIL: Alice creates an Org DID where alice is the controller, and Bob's VM is added to its VM List only. Bob attempts to update Org DID by sending his signature.\n") - print("Registering DID for an Employee") - employee_kp = generate_key_pair() - employee_did = generate_did_document(employee_kp) - employee_did_id = employee_did["id"] - create_employee_did_tx = form_did_create_tx(employee_did, employee_kp, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME) - run_blockchain_command(create_employee_did_tx, f"Registering Employee DID Document with ID {employee_did_id}") - - print("Registering DID for an Organization") - org_kp = generate_key_pair() - org_did = generate_did_document(org_kp) - org_did["controller"] = [employee_did_id] - org_did_id = org_did["id"] - create_org_did_tx = form_did_create_tx(org_did, employee_kp, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME, employee_did["authentication"][0]) - run_blockchain_command(create_org_did_tx, f"Registering Organisation DID Document with ID {org_did_id}") - - print("Employee registering a Schema on behalf of Organization's DID") - schema_doc, schema_proof = generate_schema_document( - employee_kp, - org_did_id, - employee_did["authentication"][0] - ) - create_schema_cmd = form_create_schema_tx( - schema_doc, - schema_proof, - DEFAULT_BLOCKCHAIN_ACCOUNT_NAME - ) - schema_doc_id = schema_doc["id"] - schema_author = schema_doc["author"] - run_blockchain_command(create_schema_cmd, f"Registering Schema with Id: {schema_doc_id} with {schema_author} being the author") - - print("Employee registering a Credential Status Document on behalf of Organization's DID") - cred_doc, cred_proof = generate_cred_status_document( - employee_kp, - org_did_id, - employee_did["authentication"][0] - ) - register_cred_status_cmd = form_create_cred_status_tx( - cred_doc, - cred_proof, - DEFAULT_BLOCKCHAIN_ACCOUNT_NAME - ) - cred_id = cred_doc["claim"]["id"] - cred_author = cred_doc["issuer"] - run_blockchain_command(register_cred_status_cmd, f"Registering credential status with Id: {cred_id} and {cred_author} being the author") - print("\n--- Test Completed: TC-2: Controller DID attempts to create Credential Schema and Credential Status Documents on behalf of Parent DID ---\n") - -# TC-3: Multiple Controller DID attempt to create Credential Schema and Credential Status Documents on behalf of Parent DID -def controllers_create_schema_cred_status(): - print("\n--- Test Started: TC-3: Multiple Controller DID attempt to create Credential Schema and Credential Status Documents on behalf of Parent DID ---\n") - print("In this workflow, a DID document registered with mutiple DIDs in its controller group. The controllers are expected to register schema and credential status") + # Register Alice's DID + kp_alice = generate_key_pair() + signers = [] + did_doc_string = generate_did_document(kp_alice) + did_doc_alice = did_doc_string["id"] + did_doc_alice_vm = did_doc_string["verificationMethod"][0] + signPair_alice = { + "kp": kp_alice, + "verificationMethodId": did_doc_alice_vm["id"], + "signing_algo": "ed25519" + } + signers.append(signPair_alice) + create_tx_cmd = form_did_create_tx_multisig(did_doc_string, signers, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME) + run_blockchain_command(create_tx_cmd, f"Registering of Alice's DID with Id: {did_doc_alice}") + + # Register Bob's DID + kp_bob = generate_key_pair() + signers = [] + did_doc_string = generate_did_document(kp_bob) + did_doc_bob = did_doc_string["id"] + did_doc_bob_vm = did_doc_string["verificationMethod"][0] + signPair_bob = { + "kp": kp_bob, + "verificationMethodId": did_doc_bob_vm["id"], + "signing_algo": "ed25519" + } + signers.append(signPair_bob) + create_tx_cmd = form_did_create_tx_multisig(did_doc_string, signers, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME) + run_blockchain_command(create_tx_cmd, f"Registering of Bob's DID with Id: {did_doc_bob}") + + # Alice creates Organization DID with itself being the only controller and Bob's VM being added to VM List + kp_org = generate_key_pair() + signers = [] + did_doc_string = generate_did_document(kp_org) + did_doc_org = did_doc_string["id"] + did_doc_string["controller"] = [did_doc_alice] + did_doc_string["verificationMethod"] = [did_doc_bob_vm] + signers.append(signPair_alice) + signers.append(signPair_bob) + create_tx_cmd = form_did_create_tx_multisig(did_doc_string, signers, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME) + run_blockchain_command(create_tx_cmd, f"Registering of Org DID with Id: {did_doc_org}") + + # Bob (who is not the controller) attempts to make changes in Org DID + signers = [] + did_doc_string["context"] = ["gmm", "gmm2"] + signers.append(signPair_bob) + update_tx_cmd = form_did_update_tx_multisig(did_doc_string, signers, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME) + run_blockchain_command(update_tx_cmd, f"Bob (non-controller) attempts to update Org DID with Id: {did_doc_org}", True) + + # Alice (who is the controller) attempts to make changes in Org DID + print("2. PASS: Alice creates an Org DID where alice is the controller, and Bob's VM is added to its VM List only. Alice attempts to update Org DID by sending her signature.\n") + signers = [] + did_doc_string["context"] = ["gmm", "gmm2"] + signers.append(signPair_alice) + update_tx_cmd = form_did_update_tx_multisig(did_doc_string, signers, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME) + run_blockchain_command(update_tx_cmd, f"Alice (controller) attempts to update Org DID with Id: {did_doc_org}") + + did_doc_string_org = did_doc_string + + print("3. FAIL: Alice attempts to add George as controller of Org ID. Only George's Signature is sent.\n") + signers = [] + kp_george = generate_key_pair() + did_doc_string = generate_did_document(kp_george) + did_doc_george = did_doc_string["id"] + did_doc_george_vm = did_doc_string["verificationMethod"][0] + signPair_george = { + "kp": kp_george, + "verificationMethodId": did_doc_george_vm["id"], + "signing_algo": "ed25519" + } + signers.append(signPair_george) + update_tx_cmd = form_did_create_tx_multisig(did_doc_string, signers, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME) + run_blockchain_command(update_tx_cmd, f"Registering George's DID with Id: {did_doc_george}") + + # Addition of George's DID to controller by only Alice's siganature + signers = [] + did_doc_string_org["controller"] = [did_doc_alice, did_doc_george] + signers.append(signPair_alice) + update_tx_cmd = form_did_update_tx_multisig(did_doc_string_org, signers, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME) + run_blockchain_command(update_tx_cmd, f"Adding George's DID as controller with Alice's signature only", True) + + # Addition of George's DID to controller by only George's siganature + print("4. FAIL: Alice attempts to add George as controller of Org ID. Only Alice's Signature is sent.\n") + signers = [] + did_doc_string_org["controller"] = [did_doc_alice, did_doc_george] + signers.append(signPair_george) + update_tx_cmd = form_did_update_tx_multisig(did_doc_string_org, signers, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME) + run_blockchain_command(update_tx_cmd, f"Adding George's DID as controller with George's signature only", True) + + # Addition of George's DID to controller by George's and Alice's siganature + print("5. PASS: Alice attempts to add George as controller of Org ID. Both Alice's and George's Signatures are sent.\n") + signers = [] + did_doc_string_org["controller"] = [did_doc_alice, did_doc_george] + signers.append(signPair_alice) + signers.append(signPair_george) + update_tx_cmd = form_did_update_tx_multisig(did_doc_string_org, signers, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME) + run_blockchain_command(update_tx_cmd, f"Adding George's DID as controller with George's and Alice's signature") + + # Removal of George's controller by Alice's signature + print("6. PASS: Alice attempts to remove George as controller of Org ID. Only Alice's Signature is sent.\n") + signers = [] + did_doc_string_org["controller"] = [did_doc_alice] + signers.append(signPair_alice) + update_tx_cmd = form_did_update_tx_multisig(did_doc_string_org, signers, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME) + run_blockchain_command(update_tx_cmd, f"Removal of George's controller by Alice's signature") + + + # Addition of George's controller and removal of Alice's controller at same time + print("7. PASS: Addition of George as a controller and simultaneous removal of Alice as a controller. Both alice's and george's signature are passed.\n") + signers = [] + did_doc_string_org["controller"] = [did_doc_george] + signers.append(signPair_alice) + signers.append(signPair_george) + update_tx_cmd = form_did_update_tx_multisig(did_doc_string_org, signers, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME) + run_blockchain_command(update_tx_cmd, f"Removal of Alice's controller and addition of Bob's controller") + + #Alice creates a DID where the controller only has Alice's DID and the verfication method has two ETH wallets added. Signature of all hot wallets are passed + print("8. FAIL: Alice has already created two didDocs, each representing a wallet. Now, she creates a DID where she is the controller, and the VMs from two DIDDocs are just added in the VM List. Each of these VMs have different controllers. One of the VMs attempts to update DIDDoc.\n") + kp_hot_wallet_1 = generate_key_pair("recover-eth") + signers = [] + did_doc_string = generate_did_document(kp_hot_wallet_1, algo="recover-eth") + did_doc_hw1 = did_doc_string["id"] + did_doc_hw1_vm = did_doc_string["verificationMethod"][0] + signPair_hw1 = { + "kp": kp_hot_wallet_1, + "verificationMethodId": did_doc_hw1_vm["id"], + "signing_algo": "recover-eth" + } + signers.append(signPair_hw1) + create_tx_cmd = form_did_create_tx_multisig(did_doc_string, signers, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME) + run_blockchain_command(create_tx_cmd, f"Registering Hot Wallet 1 with Id: {did_doc_hw1}") + + kp_hot_wallet_2 = generate_key_pair("recover-eth") + signers = [] + did_doc_string = generate_did_document(kp_hot_wallet_2, algo="recover-eth") + did_doc_hw2 = did_doc_string["id"] + did_doc_hw2_vm = did_doc_string["verificationMethod"][0] + signPair_hw2 = { + "kp": kp_hot_wallet_2, + "verificationMethodId": did_doc_hw2_vm["id"], + "signing_algo": "recover-eth" + } + signers.append(signPair_hw2) + create_tx_cmd = form_did_create_tx_multisig(did_doc_string, signers, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME) + run_blockchain_command(create_tx_cmd, f"Registering Hot Wallet 2 with Id: {did_doc_hw2}") + + kp_org = generate_key_pair() + signers = [] + did_doc_string = generate_did_document(kp_org) + did_doc_org = did_doc_string["id"] + did_doc_string["controller"] = [did_doc_alice] + did_doc_string["verificationMethod"] = [ + did_doc_hw1_vm, + did_doc_hw2_vm + ] + signers.append(signPair_alice) + signers.append(signPair_hw1) + signers.append(signPair_hw2) + create_tx_cmd = form_did_create_tx_multisig(did_doc_string, signers, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME) + run_blockchain_command(create_tx_cmd, f"Registering of Org DID with Id: {did_doc_org}") + + print("Hot-Wallet 1 attemps to update Org DID. It is expected to fail") + did_doc_string["context"] = ["exempler.org"] + signers = [] + signers.append(signPair_hw1) + update_tx_cmd = form_did_update_tx_multisig(did_doc_string, signers, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME) + run_blockchain_command(update_tx_cmd, f"Hot Wallet 1 attempts to update Tx", True) + + print("9. PASS: Alice has already created two didDocs, each representing a wallet. Now, she creates a DID where she is the controller, and the VMs from two DIDDocs are just added in the VM List. Each of these VMs have different controllers. Alice attempts to update DIDDoc.\n") + signers = [] + signers.append(signPair_alice) + update_tx_cmd = form_did_update_tx_multisig(did_doc_string, signers, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME) + run_blockchain_command(update_tx_cmd, f"Alice (controller) attempts to update Tx") + + print("10. FAIL: Alice tries to update her DID Document without changing anything\n") + did_doc_string = query_did(did_doc_alice)["didDocument"] + signers = [] + signers.append(signPair_alice) + update_tx_cmd = form_did_update_tx_multisig(did_doc_string, signers, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME) + run_blockchain_command(update_tx_cmd, f"Alice attempts update without any change Tx", True) + + # Register Alice's DID + print("11. PASS: Jenny creates herself a DID with empty Controller list. She then attempts to update the DIDDoc by changing the context field and passes her signature only.\n") + kp_jenny = generate_key_pair() + signers = [] + did_doc_string = generate_did_document(kp_jenny) + did_doc_string["controller"] = [] + did_doc_jenny = did_doc_string["id"] + did_doc_jenny_vm = did_doc_string["verificationMethod"][0] + signPair_jenny = { + "kp": kp_jenny, + "verificationMethodId": did_doc_jenny_vm["id"], + "signing_algo": "ed25519" + } + signers.append(signPair_jenny) + create_tx_cmd = form_did_create_tx_multisig(did_doc_string, signers, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME) + run_blockchain_command(create_tx_cmd, f"Registering of Jenny's DID with Id: {did_doc_jenny}") + + signers = [] + did_doc_string["context"] = ["yo"] + signers.append(signPair_jenny) + update_tx_cmd = form_did_update_tx_multisig(did_doc_string, signers, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME) + run_blockchain_command(update_tx_cmd, f"Jenny (controller) attempts to update Tx") + + print("\n--- Test Completed ---\n") + +def deactivate_did(): + print("\n--- Deactivate DID Test ---\n") + + print("1. PASS: Alice creates an Org DID with herself and Bob being the Controller. Alice attempts to deactivate it \n") + + # Register Alice's DID + kp_alice = generate_key_pair() + signers = [] + did_doc_string = generate_did_document(kp_alice) + did_doc_alice = did_doc_string["id"] + did_doc_alice_vm = did_doc_string["verificationMethod"][0] + signPair_alice = { + "kp": kp_alice, + "verificationMethodId": did_doc_alice_vm["id"], + "signing_algo": "ed25519" + } + signers.append(signPair_alice) + create_tx_cmd = form_did_create_tx_multisig(did_doc_string, signers, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME) + run_blockchain_command(create_tx_cmd, f"Registering of Alice's DID with Id: {did_doc_alice}") + + # Register Bob's DID + kp_bob = generate_key_pair() + signers = [] + did_doc_string = generate_did_document(kp_bob) + did_doc_bob = did_doc_string["id"] + did_doc_bob_vm = did_doc_string["verificationMethod"][0] + signPair_bob = { + "kp": kp_bob, + "verificationMethodId": did_doc_bob_vm["id"], + "signing_algo": "ed25519" + } + signers.append(signPair_bob) + create_tx_cmd = form_did_create_tx_multisig(did_doc_string, signers, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME) + run_blockchain_command(create_tx_cmd, f"Registering of Bob's DID with Id: {did_doc_bob}") + + # Alice creates Organization DID with itself being the only controller and Bob's VM being added to VM List + kp_org = generate_key_pair() + signers = [] + did_doc_string = generate_did_document(kp_org) + did_doc_org = did_doc_string["id"] + did_doc_string["controller"] = [did_doc_alice, did_doc_bob] + did_doc_string["verificationMethod"] = [] + signers.append(signPair_alice) + signers.append(signPair_bob) + create_tx_cmd = form_did_create_tx_multisig(did_doc_string, signers, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME) + run_blockchain_command(create_tx_cmd, f"Registering of Org DID with Id: {did_doc_org}") + + # Deactivate DID + signers = [] + signers.append(signPair_alice) + deactivate_tx_cmd = form_did_deactivate_tx_multisig(did_doc_org, signers, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME) + run_blockchain_command(deactivate_tx_cmd, f"Deactivation of Org's DID with Id: {did_doc_org}") + + print("2. PASS: Mike creates a DID for himself, but the controller list is empty. Mike attempts to deactivate it \n") - print("Registering DID for an Employee 1") - employee_kp_1 = generate_key_pair() - employee_did_1 = generate_did_document(employee_kp_1) - employee_did_1_id = employee_did_1["id"] - create_employee_did_tx_1 = form_did_create_tx(employee_did_1, employee_kp_1, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME) - run_blockchain_command(create_employee_did_tx_1, f"Registering Employee DID Document with ID {employee_did_1_id}") - - print("Registering DID for an Employee 2") - employee_kp_2 = generate_key_pair() - employee_did_2 = generate_did_document(employee_kp_2) - employee_did_2_id = employee_did_2["id"] - create_employee_did_tx_2 = form_did_create_tx(employee_did_2, employee_kp_2, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME) - run_blockchain_command(create_employee_did_tx_2, f"Registering Employee DID Document with ID {employee_did_2_id}") - - print("Registering DID for an Organization") - org_kp = generate_key_pair() - org_did = generate_did_document(org_kp) - org_did["controller"] = [employee_did_1_id, employee_did_2_id] - org_did_id = org_did["id"] - create_org_did_tx = form_did_create_tx(org_did, employee_kp_2, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME, employee_did_2["authentication"][0]) - run_blockchain_command(create_org_did_tx, f"Registering Organisation DID Document with ID {org_did_id}") - - print("Employee registering a Schema on behalf of Organization's DID") + # Register Alice's DID + kp_mike = generate_key_pair() + signers = [] + did_doc_string = generate_did_document(kp_mike) + did_doc_string["controller"] = [] + did_doc_mike = did_doc_string["id"] + did_doc_mike_vm = did_doc_string["verificationMethod"][0] + signPair_mike = { + "kp": kp_mike, + "verificationMethodId": did_doc_mike_vm["id"], + "signing_algo": "ed25519" + } + signers.append(signPair_mike) + create_tx_cmd = form_did_create_tx_multisig(did_doc_string, signers, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME) + run_blockchain_command(create_tx_cmd, f"Registering of Mike's DID with Id: {did_doc_mike}") + + + # Deactivate DID + signers = [] + signers.append(signPair_mike) + deactivate_tx_cmd = form_did_deactivate_tx_multisig(did_doc_mike, signers, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME) + run_blockchain_command(deactivate_tx_cmd, f"Deactivation of Mike's DID with Id: {did_doc_mike}") + +def schema_test(): + print("\n--- Schema Test ---\n") + + print("1. FAIL: Alice creates a DID with herself being the controller, and then deactivates it. She attempts to registers a schema using one of her VMs\n") + + # Register Alice's DID + kp_alice = generate_key_pair() + signers = [] + did_doc_string = generate_did_document(kp_alice) + did_doc_alice = did_doc_string["id"] + did_doc_alice_vm = did_doc_string["verificationMethod"][0] + signPair_alice = { + "kp": kp_alice, + "verificationMethodId": did_doc_alice_vm["id"], + "signing_algo": "ed25519" + } + signers.append(signPair_alice) + create_tx_cmd = form_did_create_tx_multisig(did_doc_string, signers, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME) + run_blockchain_command(create_tx_cmd, f"Registering of Alice's DID with Id: {did_doc_alice}") + + # Deactiving Alice's DID + signers = [] + signers.append(signPair_alice) + deactivate_tx_cmd = form_did_deactivate_tx_multisig(did_doc_alice, signers, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME) + run_blockchain_command(deactivate_tx_cmd, f"Deactivation of Alice's DID with Id: {did_doc_alice}") + + # Register Schema from one of alice's VM Id schema_doc, schema_proof = generate_schema_document( - employee_kp_2, - org_did_id, - employee_did_2["authentication"][0] + kp_alice, + did_doc_alice, + did_doc_alice_vm["id"] ) create_schema_cmd = form_create_schema_tx( schema_doc, @@ -333,51 +471,31 @@ def controllers_create_schema_cred_status(): DEFAULT_BLOCKCHAIN_ACCOUNT_NAME ) schema_doc_id = schema_doc["id"] - schema_author = schema_doc["author"] - run_blockchain_command(create_schema_cmd, f"Registering Schema with Id: {schema_doc_id} with {schema_author} being the author") + run_blockchain_command(create_schema_cmd, f"Registering Schema with Id: {schema_doc_id}", True) - print("Employee registering a Credential Status Document on behalf of Organization's DID") - cred_doc, cred_proof = generate_cred_status_document( - employee_kp_1, - org_did_id, - employee_did_1["authentication"][0] - ) - register_cred_status_cmd = form_create_cred_status_tx( - cred_doc, - cred_proof, - DEFAULT_BLOCKCHAIN_ACCOUNT_NAME - ) - cred_id = cred_doc["claim"]["id"] - cred_author = cred_doc["issuer"] - run_blockchain_command(register_cred_status_cmd, f"Registering credential status with Id: {cred_id} and {cred_author} being the author") - print("\n--- Test Completed: TC-3: Multiple Controller DID attempt to create Credential Schema and Credential Status Documents on behalf of Parent DID ---\n") - -# TC-4: Non-Controller DID attempts to create Credential Schema and Credential Status Documents on behalf of Parent DID (Invalid Case) -def invalid_case_controller_creates_schema_cred_status(): - print("\n--- Test Started: TC-4: Non-Controller DID attempts to create Credential Schema and Credential Status Documents on behalf of Parent DID (Invalid Case) ---\n") - print("In this workflow, a DID document registered with another DID Id in its controller group. In this case, if the canon DID tries to create Schema or Credential Document, it should fail.\n") + print("2. PASS: Bob creates a DID with herself being the controller. He attempts to registers a schema using one of her VMs\n") - print("Registering DID for an Employee") - employee_kp = generate_key_pair() - employee_did = generate_did_document(employee_kp) - employee_did_id = employee_did["id"] - create_employee_did_tx = form_did_create_tx(employee_did, employee_kp, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME) - run_blockchain_command(create_employee_did_tx, f"Registering Employee DID Document with ID {employee_did_id}") - - print("Registering DID for an Organization") - org_kp = generate_key_pair() - org_did = generate_did_document(org_kp) - org_did["controller"] = [employee_did_id] - org_did_id = org_did["id"] - create_org_did_tx = form_did_create_tx(org_did, employee_kp, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME, employee_did["authentication"][0]) - run_blockchain_command(create_org_did_tx, f"Registering Organisation DID Document with ID {org_did_id}") - - print("\nAttempting to register Schema using Organization's cryptographic material") - print("This is expected to fail") + # Register Alice's DID + kp_bob = generate_key_pair() + signers = [] + did_doc_string = generate_did_document(kp_bob) + did_doc_bob = did_doc_string["id"] + did_doc_bob_vm = did_doc_string["verificationMethod"][0] + signPair_bob = { + "kp": kp_bob, + "verificationMethodId": did_doc_bob_vm["id"], + "signing_algo": "ed25519" + } + signers.append(signPair_bob) + create_tx_cmd = form_did_create_tx_multisig(did_doc_string, signers, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME) + run_blockchain_command(create_tx_cmd, f"Registering of Bob's DID with Id: {did_doc_bob}") + + + # Register Schema from one of alice's VM Id schema_doc, schema_proof = generate_schema_document( - org_kp, - org_did_id, - org_did["authentication"][0] + kp_bob, + did_doc_bob, + did_doc_bob_vm["id"] ) create_schema_cmd = form_create_schema_tx( schema_doc, @@ -385,61 +503,41 @@ def invalid_case_controller_creates_schema_cred_status(): DEFAULT_BLOCKCHAIN_ACCOUNT_NAME ) schema_doc_id = schema_doc["id"] - schema_author = schema_doc["author"] - run_blockchain_command(create_schema_cmd, f"Registering Schema with Id: {schema_doc_id} with {schema_author} being the author\n", True) - - print("\nAttempting to register Credential Status using Organization's cryptographic material") - print("This is expected to fail") - cred_doc, cred_proof = generate_cred_status_document( - org_kp, - org_did_id, - org_did["authentication"][0] - ) - register_cred_status_cmd = form_create_cred_status_tx( - cred_doc, - cred_proof, - DEFAULT_BLOCKCHAIN_ACCOUNT_NAME - ) - cred_id = cred_doc["claim"]["id"] - cred_author = cred_doc["issuer"] - run_blockchain_command(register_cred_status_cmd, f"Registering credential status with Id: {cred_id} with {cred_author} being the issuer\n", True) - - print("\n--- Test Completed: TC-4: Non-Controller DID attempts to create Credential Schema and Credential Status Documents on behalf of Parent DID (Invalid Case) ---\n") - -# TC-9: `x/ssi` module related transactions, using `secp256k1` keypair -def ssi_operations_using_secp256k1(): - print("\n--- Test Started: TC-9: `x/ssi` module related transactions, using `secp256k1` keypair ---\n") - - kp_algo = "secp256k1" - kp = generate_key_pair(algo=kp_algo) + run_blockchain_command(create_schema_cmd, f"Registering Schema with Id: {schema_doc_id}") - print("Registering a DID Document") - did_doc_string = generate_did_document(kp, kp_algo) - did_doc_id = did_doc_string["id"] - create_tx_cmd = form_did_create_tx(did_doc_string, kp, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME, signing_algo=kp_algo) - run_blockchain_command(create_tx_cmd, f"Registering DID with Id: {did_doc_id}") + print("\n--- Test Completed ---\n") - print("Registering a Schema Document") - schema_doc, schema_proof = generate_schema_document( - kp, - did_doc_id, - did_doc_string["authentication"][0], - algo = kp_algo - ) - create_schema_cmd = form_create_schema_tx( - schema_doc, - schema_proof, - DEFAULT_BLOCKCHAIN_ACCOUNT_NAME - ) - schema_doc_id = schema_doc["id"] - run_blockchain_command(create_schema_cmd, f"Registering Schema with Id: {schema_doc_id}") +def credential_status_test(): + print("\n--- Credential Status Test ---\n") - print("Registering a Credential Status Document") + print("1. FAIL: Alice creates a DID with herself being the controller, and then deactivates it. She attempts to registers a credential status using one of her VMs\n") + + # Register Alice's DID + kp_alice = generate_key_pair() + signers = [] + did_doc_string = generate_did_document(kp_alice) + did_doc_alice = did_doc_string["id"] + did_doc_alice_vm = did_doc_string["verificationMethod"][0] + signPair_alice = { + "kp": kp_alice, + "verificationMethodId": did_doc_alice_vm["id"], + "signing_algo": "ed25519" + } + signers.append(signPair_alice) + create_tx_cmd = form_did_create_tx_multisig(did_doc_string, signers, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME) + run_blockchain_command(create_tx_cmd, f"Registering of Alice's DID with Id: {did_doc_alice}") + + # Deactiving Alice's DID + signers = [] + signers.append(signPair_alice) + deactivate_tx_cmd = form_did_deactivate_tx_multisig(did_doc_alice, signers, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME) + run_blockchain_command(deactivate_tx_cmd, f"Deactivation of Alice's DID with Id: {did_doc_alice}") + + # Register Credential Status from one of alice's VM Id cred_doc, cred_proof = generate_cred_status_document( - kp, - did_doc_id, - did_doc_string["authentication"][0], - algo = kp_algo + kp_alice, + did_doc_alice, + did_doc_alice_vm["id"] ) register_cred_status_cmd = form_create_cred_status_tx( cred_doc, @@ -447,110 +545,31 @@ def ssi_operations_using_secp256k1(): DEFAULT_BLOCKCHAIN_ACCOUNT_NAME ) cred_id = cred_doc["claim"]["id"] - run_blockchain_command(register_cred_status_cmd, f"Registering Credential status with Id: {cred_id}") - - print("Updating the Registered DID Document") - registered_did_doc = query_did(did_doc_id)["didDocument"] - registered_did_doc["context"] = registered_did_doc["context"].append("newwebsite.com") - update_did_tx_cmd = form_did_update_tx( - registered_did_doc, - kp, - DEFAULT_BLOCKCHAIN_ACCOUNT_NAME, - signing_algo=kp_algo - ) - run_blockchain_command(update_did_tx_cmd, f"Updating DID Document with Id: {did_doc_id}") - - print("Deactivating the Registered DID Document") - deactivate_did_tx_cmd = form_did_deactivate_tx( - did_doc_id, - kp, - DEFAULT_BLOCKCHAIN_ACCOUNT_NAME, - signing_algo=kp_algo - ) - run_blockchain_command(deactivate_did_tx_cmd, f"Deactivating DID Document with Id: {did_doc_id}") - - print("\n--- Test Completed: TC-9: `x/ssi` module related transactions, using `secp256k1` keypair ---\n") - -# TC-10: Test scenarios for `blockchainAccountId`. -def caip10_support_test(): - print("\n--- Test Started: TC-10: Test scenarios for `blockchainAccountId` ---\n") - kp_algo = "recover-eth" - - # Invalid blockchain Account Ids - invalid_blockchain_account_ids = [ - "abc345566", - "eip:1:0x1234", - "eip155", - "eip155:1:", - "eip155::", - "eip155:1", - "eip155:::::23", - "eip155::0x1234567" - ] + run_blockchain_command(register_cred_status_cmd, f"Registering Credential status with Id: {cred_id}", True) - for invalid_blockchain_id in invalid_blockchain_account_ids: - print("Registering a DID Document with an invalid blockchainAccountId:", invalid_blockchain_id) - kp = generate_key_pair(algo=kp_algo) - did_doc_string = generate_did_document(kp, kp_algo) - did_doc_string["verificationMethod"][0]["blockchainAccountId"] = invalid_blockchain_id - create_tx_cmd = form_did_create_tx(did_doc_string, kp, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME, signing_algo=kp_algo) - run_blockchain_command(create_tx_cmd, f"Registering a DID Document with an invalid blockchainAccountId: {invalid_blockchain_id}", True) + print("2. PASS: Bob creates a DID with herself being the controller. He attempts to registers a credential status using one of his VMs\n") - print("Registering a DID with a VM of type EcdsaSecp256k1SignatureRecovery2020 having publicKeyMultibase attribute populated") - kp = generate_key_pair(algo=kp_algo) - did_doc_string = generate_did_document(kp, kp_algo) - did_doc_id = did_doc_string["id"] - did_doc_string["verificationMethod"][0]["publicKeyMultibase"] = "zrxxgf1f9xPYTraixqi9tipLta61hp4VJWQUUW5pmwcVz" - create_tx_cmd = form_did_create_tx(did_doc_string, kp, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME, signing_algo=kp_algo) - run_blockchain_command(create_tx_cmd, "Registering a DID with a VM of type EcdsaSecp256k1SignatureRecovery2020 having publicKeyMultibase attribute populated", True, True) - - # Test for valid blockchainAccountId - kp = generate_key_pair(algo=kp_algo) - did_doc_string = generate_did_document(kp, kp_algo) - did_doc_id = did_doc_string["id"] - print( - "Registering a DID Document with a valid blockchainAccountId:", - did_doc_string["verificationMethod"][0]["blockchainAccountId"] - ) - create_tx_cmd = form_did_create_tx(did_doc_string, kp, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME, signing_algo=kp_algo) - run_blockchain_command(create_tx_cmd, f"Registering DID with Id: {did_doc_id}") - - print("\n--- Test Completed: TC-10: Test scenarios for `blockchainAccountId` ---\n") - -# TC-11: `x/ssi` module related transactions, using ethereum based `secp256k1` keypair -def ssi_operation_using_ethereum_keypair_test(): - print("\n--- Test Started: TC-11: `x/ssi` module related transactions, using ethereum based `secp256k1` keypair ---\n") - - kp_algo = "recover-eth" - kp = generate_key_pair(algo=kp_algo) - - print("Registering a DID Document") - did_doc_string = generate_did_document(kp, kp_algo) - did_doc_id = did_doc_string["id"] - create_tx_cmd = form_did_create_tx(did_doc_string, kp, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME, signing_algo=kp_algo) - run_blockchain_command(create_tx_cmd, f"Registering DID with Id: {did_doc_id}") - - print("Registering a Schema Document") - schema_doc, schema_proof = generate_schema_document( - kp, - did_doc_id, - did_doc_string["authentication"][0], - algo = kp_algo - ) - create_schema_cmd = form_create_schema_tx( - schema_doc, - schema_proof, - DEFAULT_BLOCKCHAIN_ACCOUNT_NAME - ) - schema_doc_id = schema_doc["id"] - run_blockchain_command(create_schema_cmd, f"Registering Schema with Id: {schema_doc_id}") - - print("Registering a Credential Status Document") + # Register Alice's DID + kp_bob = generate_key_pair() + signers = [] + did_doc_string = generate_did_document(kp_bob) + did_doc_bob = did_doc_string["id"] + did_doc_bob_vm = did_doc_string["verificationMethod"][0] + signPair_bob = { + "kp": kp_bob, + "verificationMethodId": did_doc_bob_vm["id"], + "signing_algo": "ed25519" + } + signers.append(signPair_bob) + create_tx_cmd = form_did_create_tx_multisig(did_doc_string, signers, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME) + run_blockchain_command(create_tx_cmd, f"Registering of Bob's DID with Id: {did_doc_bob}") + + + # Register Credential Status from one of alice's VM Id cred_doc, cred_proof = generate_cred_status_document( - kp, - did_doc_id, - did_doc_string["authentication"][0], - algo = kp_algo + kp_bob, + did_doc_bob, + did_doc_bob_vm["id"] ) register_cred_status_cmd = form_create_cred_status_tx( cred_doc, @@ -560,24 +579,4 @@ def ssi_operation_using_ethereum_keypair_test(): cred_id = cred_doc["claim"]["id"] run_blockchain_command(register_cred_status_cmd, f"Registering Credential status with Id: {cred_id}") - print("Updating the Registered DID Document") - registered_did_doc = query_did(did_doc_id)["didDocument"] - registered_did_doc["context"] = registered_did_doc["context"].append("newwebsite.com") - update_did_tx_cmd = form_did_update_tx( - registered_did_doc, - kp, - DEFAULT_BLOCKCHAIN_ACCOUNT_NAME, - signing_algo=kp_algo - ) - run_blockchain_command(update_did_tx_cmd, f"Updating DID Document with Id: {did_doc_id}") - - print("Deactivating the Registered DID Document") - deactivate_did_tx_cmd = form_did_deactivate_tx( - did_doc_id, - kp, - DEFAULT_BLOCKCHAIN_ACCOUNT_NAME, - signing_algo=kp_algo - ) - run_blockchain_command(deactivate_did_tx_cmd, f"Deactivating DID Document with Id: {did_doc_id}") - - print("\n--- Test Completed: TC-11: `x/ssi` module related transactions, using ethereum based `secp256k1` keypair ---\n") + print("\n--- Test Completed ---\n") \ No newline at end of file diff --git a/tests/e2e/ssi_tests/generate_doc.py b/tests/e2e/ssi_tests/generate_doc.py index 3581774..34c1905 100644 --- a/tests/e2e/ssi_tests/generate_doc.py +++ b/tests/e2e/ssi_tests/generate_doc.py @@ -40,14 +40,12 @@ def generate_did_document(key_pair, algo="ed25519"): verification_method["blockchainAccountId"] = "eip155:1:" + key_pair["ethereum_address"] else: verification_method["publicKeyMultibase"] = key_pair["pub_key_multibase"] - - authentication_verification_method_id = verification_method["id"] base_document["id"] = did_id base_document["controller"] = [did_id] base_document["verificationMethod"] = [verification_method] - base_document["authentication"] = [authentication_verification_method_id] - base_document["assertionMethod"] = [authentication_verification_method_id] + base_document["authentication"] = [] + base_document["assertionMethod"] = [] return base_document def generate_schema_document(key_pair, schema_author, vm, signature=None, algo="ed25519"): diff --git a/tests/e2e/ssi_tests/run.py b/tests/e2e/ssi_tests/run.py index 5831082..ac12599 100644 --- a/tests/e2e/ssi_tests/run.py +++ b/tests/e2e/ssi_tests/run.py @@ -24,17 +24,11 @@ def generate_report(func): def run_all_tests(): print("============= 🔧️ Running all x/ssi e2e tests ============== \n") - simple_ssi_flow() - controller_creates_schema_cred_status() - controllers_create_schema_cred_status() - invalid_case_controller_creates_schema_cred_status() - non_controller_did_trying_to_update_diddoc() - controller_did_trying_to_update_diddoc() - multiple_controllers_with_one_signer() - deactivated_did_should_not_create_ssi_elements() - ssi_operations_using_secp256k1() - caip10_support_test() - ssi_operation_using_ethereum_keypair_test() + create_did_test() + update_did_test() + schema_test() + deactivate_did() + credential_status_test() print("============= 😃️ All test cases completed successfully ============== \n") @@ -47,6 +41,6 @@ def run_all_tests(): generate_report(run_all_tests) else: run_all_tests() - + except Exception as e: raise(e) diff --git a/tests/e2e/ssi_tests/transactions.py b/tests/e2e/ssi_tests/transactions.py index 8b8623f..3c4eb88 100644 --- a/tests/e2e/ssi_tests/transactions.py +++ b/tests/e2e/ssi_tests/transactions.py @@ -9,6 +9,62 @@ COMMON_TX_COMMAND_FLAGS = "--chain-id hidnode --output json --broadcast-mode block --keyring-backend test --yes" +def form_did_create_tx_multisig(diddoc, signPairs, blockchain_account): + signPairStr = "" + + for signPair in signPairs: + private_key = "" + vmId = signPair["verificationMethodId"] + signAlgo = signPair["signing_algo"] + + if signAlgo == "recover-eth": + private_key = signPair["kp"]["priv_key_hex"] + else: + private_key = signPair["kp"]["priv_key_base_64"] + + signPairStr += f"{vmId} {private_key} {signAlgo} " + + cmd_string = f"hid-noded tx ssi create-did '{json.dumps(diddoc)}' {signPairStr} --from {blockchain_account} " + COMMON_TX_COMMAND_FLAGS + return cmd_string + +def form_did_update_tx_multisig(diddoc, signPairs, blockchain_account): + signPairStr = "" + + for signPair in signPairs: + private_key = "" + vmId = signPair["verificationMethodId"] + signAlgo = signPair["signing_algo"] + + if signAlgo == "recover-eth": + private_key = signPair["kp"]["priv_key_hex"] + else: + private_key = signPair["kp"]["priv_key_base_64"] + + signPairStr += f"{vmId} {private_key} {signAlgo} " + + version_id = query_did(diddoc["id"])["didDocumentMetadata"]["versionId"] + cmd_string = f"hid-noded tx ssi update-did '{json.dumps(diddoc)}' '{version_id}' {signPairStr} --from {blockchain_account} " + COMMON_TX_COMMAND_FLAGS + return cmd_string + +def form_did_deactivate_tx_multisig(didId, signPairs, blockchain_account): + signPairStr = "" + + for signPair in signPairs: + private_key = "" + vmId = signPair["verificationMethodId"] + signAlgo = signPair["signing_algo"] + + if signAlgo == "recover-eth": + private_key = signPair["kp"]["priv_key_hex"] + else: + private_key = signPair["kp"]["priv_key_base_64"] + + signPairStr += f"{vmId} {private_key} {signAlgo} " + + version_id = query_did(didId)["didDocumentMetadata"]["versionId"] + cmd_string = f"hid-noded tx ssi deactivate-did '{didId}' '{version_id}' {signPairStr} --from {blockchain_account} " + COMMON_TX_COMMAND_FLAGS + return cmd_string + def form_did_create_tx(did_doc, kp, blockchain_account, verificationMethodId=None, signing_algo="ed25519"): if signing_algo == "recover-eth": private_key = kp["priv_key_hex"] diff --git a/x/ssi/client/cli/tx_utils.go b/x/ssi/client/cli/tx_utils.go index 857d19d..311f4d1 100644 --- a/x/ssi/client/cli/tx_utils.go +++ b/x/ssi/client/cli/tx_utils.go @@ -62,7 +62,7 @@ func GetEthRecoverySignature(privateKey string, message []byte) (string, error) if err != nil { panic(err) } - + return etherhexutil.Encode(sigBytes), nil } diff --git a/x/ssi/keeper/client_spec.go b/x/ssi/keeper/client_spec.go index 1c51276..f17096a 100644 --- a/x/ssi/keeper/client_spec.go +++ b/x/ssi/keeper/client_spec.go @@ -7,7 +7,6 @@ import ( "strings" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/hypersign-protocol/hid-node/x/ssi/common" "github.com/hypersign-protocol/hid-node/x/ssi/types" ) @@ -54,11 +53,11 @@ func getPersonalSignSpecDocBytes(clientSpecOpts types.ClientSpecOpts) ([]byte, e } // Get the updated marshaled SSI document for the respective ClientSpec -func getClientSpecDocBytes(clientSpecType string, clientSpecOpts types.ClientSpecOpts) ([]byte, error) { - switch clientSpecType { - case common.ADR036Spec: +func getClientSpecDocBytes(clientSpecOpts types.ClientSpecOpts) ([]byte, error) { + switch clientSpecOpts.ClientSpecType { + case types.ADR036Spec: return getCosmosADR036SignDocBytes(clientSpecOpts) - case common.PersonalSignSpec: + case types.PersonalSignSpec: return getPersonalSignSpecDocBytes(clientSpecOpts) // Non-ClientSpec RPC Request // Return marshaled SSI document as-is @@ -69,7 +68,7 @@ func getClientSpecDocBytes(clientSpecType string, clientSpecOpts types.ClientSpe types.ErrInvalidClientSpecType, fmt.Sprintf( "supported client specs are : [%s]", - strings.Join(common.SupportedClientSpecs, ", "), + strings.Join(types.SupportedClientSpecs, ", "), ), ) } diff --git a/x/ssi/keeper/controller.go b/x/ssi/keeper/controller.go deleted file mode 100644 index 877247a..0000000 --- a/x/ssi/keeper/controller.go +++ /dev/null @@ -1,136 +0,0 @@ -package keeper - -import ( - "fmt" - "reflect" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/hypersign-protocol/hid-node/x/ssi/types" - "github.com/hypersign-protocol/hid-node/x/ssi/verification" -) - -func (k msgServer) ValidateDidControllers(ctx *sdk.Context, id string, controllers []string, verMethods []*types.VerificationMethod) error { - for _, verificationMethod := range verMethods { - if err := k.validateController( - ctx, - id, - verificationMethod.GetController(), - ); err != nil { - return err - } - } - - for _, didController := range controllers { - if err := k.validateController(ctx, id, didController); err != nil { - return err - } - } - return nil -} - -func (k *Keeper) validateController(ctx *sdk.Context, id string, controller string) error { - if id == controller { - return nil - } - didDoc, err := k.GetDidDocumentState(ctx, controller) - if err != nil { - return types.ErrDidDocNotFound.Wrap(controller) - } - if len(didDoc.DidDocument.Authentication) == 0 { - return types.ErrBadRequestInvalidVerMethod.Wrap( - fmt.Sprintf( - "verificatition method controller %s doesn't have an authentication keys", - controller, - ), - ) - } - return nil -} - -// Get Verification Method and Verification Relationship fields for Signers, if they don't have any -func (k *Keeper) GetVMForSigners(ctx *sdk.Context, signers []types.Signer) ([]types.Signer, error) { - for i := 0; i < len(signers); i++ { - if signers[i].VerificationMethod == nil { - fetchedDidDoc, err := k.GetDidDocumentState(ctx, signers[i].Signer) - if err != nil { - return nil, types.ErrDidDocNotFound.Wrap(signers[i].Signer) - } - - signers[i].Authentication = fetchedDidDoc.GetDidDocument().GetAuthentication() - signers[i].AssertionMethod = fetchedDidDoc.GetDidDocument().GetAssertionMethod() - signers[i].KeyAgreement = fetchedDidDoc.GetDidDocument().GetKeyAgreement() - signers[i].CapabilityInvocation = fetchedDidDoc.GetDidDocument().GetCapabilityInvocation() - signers[i].CapabilityDelegation = fetchedDidDoc.GetDidDocument().GetCapabilityDelegation() - signers[i].VerificationMethod = fetchedDidDoc.GetDidDocument().GetVerificationMethod() - } - } - - return signers, nil -} - -// Get the updated signers from the new DID Document -func GetUpdatedSigners(ctx *sdk.Context, oldDidDocument *types.Did, updatedDidDocument *types.Did, signatures []*types.SignInfo) []types.Signer { - var signers []types.Signer - - oldController := oldDidDocument.Controller - if len(oldController) == 0 { - oldController = []string{oldDidDocument.Id} - } - - for _, controller := range oldController { - signers = append(signers, types.Signer{Signer: controller}) - } - - oldVerificationMethods := oldDidDocument.GetVerificationMethod() - for _, oldVM := range oldVerificationMethods { - newVM := verification.FindVerificationMethod( - updatedDidDocument.GetVerificationMethod(), - oldVM.GetId(), - ) - - // Verification Method has been deleted - if newVM == nil { - signers = appendSignerIfNeed( - signers, - oldVM.GetController(), - updatedDidDocument) - } - - // Verification Method has been changed - if !reflect.DeepEqual(oldVM, newVM) { - signers = appendSignerIfNeed( - signers, - newVM.GetController(), - updatedDidDocument) - } - - // Verification Method Controller has been changed, need to add old controller - if newVM.Controller != oldVM.Controller { - signers = appendSignerIfNeed( - signers, - oldVM.GetController(), - updatedDidDocument) - } - } - - return signers -} - -func appendSignerIfNeed(signers []types.Signer, controller string, msg *types.Did) []types.Signer { - for _, signer := range signers { - if signer.Signer == controller { - return signers - } - } - - signer := types.Signer{ - Signer: controller, - } - - if controller == msg.GetId() { - signer.VerificationMethod = msg.GetVerificationMethod() - signer.Authentication = msg.GetAuthentication() - } - - return append(signers, signer) -} diff --git a/x/ssi/keeper/did.go b/x/ssi/keeper/did.go index 8077520..fbde6ad 100644 --- a/x/ssi/keeper/did.go +++ b/x/ssi/keeper/did.go @@ -48,6 +48,11 @@ func (k Keeper) GetDidDocumentState(ctx *sdk.Context, id string) (*types.DidDocu var didDocState types.DidDocumentState var bytes = store.Get([]byte(id)) + + if len(bytes) == 0 { + return nil, sdkerrors.Wrap(types.ErrDidDocNotFound, id) + } + if err := k.cdc.Unmarshal(bytes, &didDocState); err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidType, err.Error()) } diff --git a/x/ssi/keeper/msg_server.go b/x/ssi/keeper/msg_server.go index bc428c9..e28b7fd 100644 --- a/x/ssi/keeper/msg_server.go +++ b/x/ssi/keeper/msg_server.go @@ -1,7 +1,12 @@ package keeper import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/hypersign-protocol/hid-node/x/ssi/types" + "github.com/hypersign-protocol/hid-node/x/ssi/verification" ) type msgServer struct { @@ -15,3 +20,210 @@ func NewMsgServerImpl(keeper Keeper) types.MsgServer { } var _ types.MsgServer = msgServer{} + +// checkControllerPresenceInState checks if VM of subject is present +func (k msgServer) checkControllerPresenceInState( + ctx sdk.Context, controllers []string, + didSubject string, +) error { + for _, controller := range controllers { + // Skip if controller is the DID subject + if controller == didSubject { + continue + } + + didDoc, err := k.GetDidDocumentState(&ctx, controller) + if err != nil { + return err + } + + // Check if they are deactivated + if didDoc.DidDocumentMetadata.Deactivated { + return sdkerrors.Wrapf(types.ErrDidDocDeactivated, "%s", didDoc.DidDocument.Id) + } + } + return nil +} + +// formMustControllerVmListMap returns a map between controller and a list of Extended Verification Methods, where +// every verification method of every controller needs to be valid +func (k msgServer) formMustControllerVmListMap(ctx sdk.Context, + controllers []string, verificationMethods []*types.VerificationMethod, + inputSignMap map[string]string, +) (map[string][]*types.ExtendedVerificationMethod, error) { + var controllerMap map[string][]*types.ExtendedVerificationMethod = map[string][]*types.ExtendedVerificationMethod{} + var vmMap map[string]*types.VerificationMethod = map[string]*types.VerificationMethod{} + + // Make controller map + for _, controller := range controllers { + controllerMap[controller] = []*types.ExtendedVerificationMethod{} + } + + // Make Verification method map + for _, vm := range verificationMethods { + vmMap[vm.Id] = vm + } + + for _, vm := range verificationMethods { + // Check if the required verification method is present in SignMap + if _, present := inputSignMap[vm.Id]; !present { + return nil, fmt.Errorf("signature required for verification method %s", vm.Id) + } else { + if _, presentInControllerMap := controllerMap[vm.Controller]; presentInControllerMap { + vmExtended := types.CreateExtendedVerificationMethod(vm, inputSignMap[vm.Id]) + controllerMap[vm.Controller] = append(controllerMap[vm.Controller], vmExtended) + } + } + } + + // Append only those signatures whose controller are present in the controllerMap + for vmId, sign := range inputSignMap { + controller, _ := types.SplitDidUrl(vmId) + + if _, present := controllerMap[controller]; present { + // Check if the VM is present in Subject DID + _, presentInSubjectDidDoc := vmMap[vmId] + + if presentInSubjectDidDoc { + _, presentInControllerMap := controllerMap[vmMap[vmId].Controller] + if presentInControllerMap { + vmExtended := types.CreateExtendedVerificationMethod(vmMap[vmId], sign) + controllerMap[controller] = append(controllerMap[controller], vmExtended) + } + // Check for VM from the respective controller's DID Doc + } else { + vmState, err := k.getControllerVmFromState(ctx, vmId) + if err != nil { + return nil, err + } + _, presentInControllerMap := controllerMap[vmState.Controller] + if presentInControllerMap { + vmExtended := types.CreateExtendedVerificationMethod(vmState, sign) + controllerMap[controller] = append(controllerMap[controller], vmExtended) + } + } + } + } + return controllerMap, nil +} + +// formAnyControllerVmListMap returns a map between controller and a list of Extended Verification Methods, where +// atleast one verification method of any controller needs to be valid +func (k msgServer) formAnyControllerVmListMap(ctx sdk.Context, + controllers []string, verificationMethods []*types.VerificationMethod, + inputSignMap map[string]string, +) (map[string][]*types.ExtendedVerificationMethod, error) { + var controllerMap map[string][]*types.ExtendedVerificationMethod = map[string][]*types.ExtendedVerificationMethod{} + var vmMap map[string]*types.VerificationMethod = map[string]*types.VerificationMethod{} + + // Make controller map + for _, controller := range controllers { + controllerMap[controller] = []*types.ExtendedVerificationMethod{} + } + + // Make Verification method map + for _, vm := range verificationMethods { + vmMap[vm.Id] = vm + } + + // Append only those signatures whose controller are present in the controllerMap + for vmId, sign := range inputSignMap { + controller, _ := types.SplitDidUrl(vmId) + + if _, present := controllerMap[controller]; present { + // Check if the VM is present in Subject DID + _, presentInSubjectDidDoc := vmMap[vmId] + + if presentInSubjectDidDoc { + _, presentInControllerMap := controllerMap[vmMap[vmId].Controller] + if presentInControllerMap { + vmExtended := types.CreateExtendedVerificationMethod(vmMap[vmId], sign) + controllerMap[controller] = append(controllerMap[controller], vmExtended) + } + // Check for VM from the respective controller's DID Doc + } else { + vmState, err := k.getControllerVmFromState(ctx, vmId) + if err != nil { + return nil, err + } + _, presentInControllerMap := controllerMap[vmState.Controller] + if presentInControllerMap { + vmExtended := types.CreateExtendedVerificationMethod(vmState, sign) + controllerMap[controller] = append(controllerMap[controller], vmExtended) + } + } + } + } + return controllerMap, nil +} + +func (k msgServer) getControllerVmFromState(ctx sdk.Context, verificationMethodId string) (*types.VerificationMethod, error) { + didId, _ := types.SplitDidUrl(verificationMethodId) + + didDocumentState, err := k.GetDidDocumentState(&ctx, didId) + if err != nil { + return nil, err + } + + for _, vm := range didDocumentState.DidDocument.VerificationMethod { + if vm.Id == verificationMethodId { + return vm, nil + } + } + + return nil, fmt.Errorf("verification method %v not found in controller %v", verificationMethodId, didId) +} + +// VerifyDocumentProof verifies the proof of a SSI Document +func (k msgServer) VerifyDocumentProof(ctx sdk.Context, docBytes []byte, inputDocProof types.SSIProofInterface) error { + // Get DID Document from State + schemaProofVmId := inputDocProof.GetVerificationMethod() + didId, _ := types.SplitDidUrl(schemaProofVmId) + didDocumentState, err := k.GetDidDocumentState(&ctx, didId) + if err != nil { + return err + } + didDoc := didDocumentState.DidDocument + + // Search for Verification Method in DID Document + var schemaVm *types.VerificationMethod = nil + for _, vm := range didDoc.VerificationMethod { + if vm.Id == schemaProofVmId { + schemaVm = vm + break + } + } + if schemaVm == nil { + return fmt.Errorf("verificationMethod %s is not present in DID document %s", schemaProofVmId, didId) + } + + // Check if the Proof Type is correct + if types.VerificationKeySignatureMap[schemaVm.Type] != inputDocProof.GetType() { + return fmt.Errorf( + "expected proof type to be %v as the verificationMethod type of %v is %v, recieved %v", + types.VerificationKeySignatureMap[schemaVm.Type], + schemaVm.Id, + schemaVm.Type, + inputDocProof.GetType(), + ) + } + + // Verify signature + err = verification.VerifyDocumentProofSignature(docBytes, schemaVm, inputDocProof.GetProofValue()) + if err != nil { + return err + } + + return nil +} + +// makeSignatureMap converts []SignInfo to map +func makeSignatureMap(inputSignatures []*types.SignInfo) map[string]string { + var signMap map[string]string = map[string]string{} + + for _, sign := range inputSignatures { + signMap[sign.VerificationMethodId] = sign.Signature + } + + return signMap +} diff --git a/x/ssi/keeper/msg_server_create_did.go b/x/ssi/keeper/msg_server_create_did.go new file mode 100644 index 0000000..29f8b44 --- /dev/null +++ b/x/ssi/keeper/msg_server_create_did.go @@ -0,0 +1,129 @@ +package keeper + +import ( + "context" + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/hypersign-protocol/hid-node/x/ssi/types" + "github.com/hypersign-protocol/hid-node/x/ssi/verification" +) + +// CreateDID is a RPC method for registration of a DID Document +func (k msgServer) CreateDID(goCtx context.Context, msg *types.MsgCreateDID) (*types.MsgCreateDIDResponse, error) { + // Unwrap Go Context to Cosmos SDK Context + ctx := sdk.UnwrapSDKContext(goCtx) + + // Get the RPC inputs + msgDidDocument := msg.DidDocString + msgSignatures := msg.Signatures + + // Validate DID Document + if err := msgDidDocument.ValidateDidDocument(); err != nil { + return nil, err + } + + // Validate namespace in DID Document + if err := didNamespaceValidation(k, ctx, msgDidDocument); err != nil { + return nil, err + } + + // Checks if the Did Document is already registered + if k.HasDid(ctx, msgDidDocument.Id) { + return nil, sdkerrors.Wrap(types.ErrDidDocExists, msgDidDocument.Id) + } + + // Get the list of controllers and check the non-subject controller's existance in the state + controllerList := getControllersForCreateDID(msgDidDocument) + + if err := k.checkControllerPresenceInState(ctx, controllerList, msgDidDocument.Id); err != nil { + return nil, err + } + + // Collect necessary Verification Methods which are needed to be valid + requiredVMs, err := getVerificationMethodsForCreateDID(msgDidDocument) + if err != nil { + return nil, err + } + + // Associate Signatures + signMap := makeSignatureMap(msgSignatures) + + requiredVmMap, err := k.formMustControllerVmListMap(ctx, controllerList, requiredVMs, signMap) + if err != nil { + return nil, err + } + + // ClientSpec check + clientSpecOpts := types.ClientSpecOpts{ + ClientSpecType: msg.ClientSpec, + SSIDoc: msgDidDocument, + SignerAddress: msg.Creator, + } + var didDocBytes []byte + didDocBytes, err = getClientSpecDocBytes(clientSpecOpts) + if err != nil { + return nil, err + } + + // Verify Signatures + err = verification.VerifySignatureOfEveryController(didDocBytes, requiredVmMap) + if err != nil { + return nil, err + } + + // Formt DID Document Metadata + metadata := types.CreateNewMetadata(ctx) + + // Form the Completet DID Document + didDocumentState := types.DidDocumentState{ + DidDocument: msgDidDocument, + DidDocumentMetadata: &metadata, + } + + // Register DID Document in Store once all validation checks are passed + id := k.RegisterDidDocumentInStore(ctx, &didDocumentState) + + return &types.MsgCreateDIDResponse{Id: id}, nil +} + +// getControllersForCreateDID returns a list of controller DIDs +// from controller and verification method attributes +func getControllersForCreateDID(didDocument *types.Did) []string { + var controllerList []string + + // DID Subject is assumed to be the DID Controller if the controller list is empty + if len(didDocument.Controller) == 0 { + controllerList = append(controllerList, didDocument.Id) + } + + controllerList = append(controllerList, didDocument.Controller...) + + for _, vm := range didDocument.VerificationMethod { + controllerList = append(controllerList, vm.Controller) + } + + return types.GetUniqueElements(controllerList) +} + +// getVerificationMethodsForCreateDID fetches all the Verification Methods needed to be verified +func getVerificationMethodsForCreateDID(didDocument *types.Did) ([]*types.VerificationMethod, error) { + var mustHaveVerificaitonMethods []*types.VerificationMethod = []*types.VerificationMethod{} + var foundAtleastOneSubjectVM bool = false + + for _, vm := range didDocument.VerificationMethod { + // set foundAtleastOneSubjectVM to true if atleast one Subject DID's VM is found + if vm.Controller == didDocument.Id { + foundAtleastOneSubjectVM = true + } + mustHaveVerificaitonMethods = append(mustHaveVerificaitonMethods, vm) + } + + if !foundAtleastOneSubjectVM && types.FindInSlice(didDocument.Controller, didDocument.Id) { + return nil, fmt.Errorf( + "there should be atleast one verification method from subject DID controller %v", didDocument.Id) + } + + return mustHaveVerificaitonMethods, nil +} diff --git a/x/ssi/keeper/msg_server_credential.go b/x/ssi/keeper/msg_server_credential.go index 52407a8..b8da5a4 100644 --- a/x/ssi/keeper/msg_server_credential.go +++ b/x/ssi/keeper/msg_server_credential.go @@ -86,47 +86,20 @@ func (k msgServer) RegisterCredentialStatus(goCtx context.Context, msg *types.Ms return nil, sdkerrors.Wrapf(types.ErrInvalidCredentialHash, "supported hashing algorithms: sha256") } - // Verify the Signature - didDocumentState, err := k.GetDidDocumentState(&ctx, issuerId) - if err != nil { - return nil, err - } - - didDocument := didDocumentState.GetDidDocument() - - signature := &types.SignInfo{ - VerificationMethodId: msgCredProof.GetVerificationMethod(), - Signature: msgCredProof.GetProofValue(), - } - signatures := []*types.SignInfo{signature} - signers := didDocument.GetSigners() - signersWithVM, err := k.GetVMForSigners(&ctx, signers) - if err != nil { - return nil, err - } - // ClientSpec check - clientSpecType := msg.GetClientSpec() - singerAddress := msg.GetCreator() - clientSpecOpts := types.ClientSpecOpts{ - SSIDoc: msgCredStatus, - SignerAddress: singerAddress, - } - - credDocBytes, err := getClientSpecDocBytes(clientSpecType, clientSpecOpts) - if err != nil { - return nil, err + ClientSpecType: msg.ClientSpec, + SSIDoc: msgCredStatus, + SignerAddress: msg.Creator, } - // Proof Type Check - err = verification.DocumentProofTypeCheck(msgCredProof.Type, signersWithVM, msgCredProof.VerificationMethod) + credDocBytes, err := getClientSpecDocBytes(clientSpecOpts) if err != nil { return nil, err } // Verify Signature - err = verification.VerifyDocumentSignature(&ctx, credDocBytes, signersWithVM, signatures) + err = k.VerifyDocumentProof(ctx, credDocBytes, msgCredProof) if err != nil { return nil, err } @@ -304,47 +277,20 @@ func (k msgServer) updateCredentialStatus(ctx sdk.Context, msg *types.MsgRegiste } } - // Verify the Signature - didDocumentState, err := k.GetDidDocumentState(&ctx, issuerId) - if err != nil { - return nil, err - } - - didDocument := didDocumentState.GetDidDocument() - - signature := &types.SignInfo{ - VerificationMethodId: msgNewCredProof.GetVerificationMethod(), - Signature: msgNewCredProof.GetProofValue(), - } - signatures := []*types.SignInfo{signature} - signers := didDocument.GetSigners() - signersWithVM, err := k.GetVMForSigners(&ctx, signers) - if err != nil { - return nil, err - } - // ClientSpec check - clientSpecType := msg.GetClientSpec() - signerAddress := msg.GetCreator() - clientSpecOpts := types.ClientSpecOpts{ - SSIDoc: msgNewCredStatus, - SignerAddress: signerAddress, - } - - credDocBytes, err := getClientSpecDocBytes(clientSpecType, clientSpecOpts) - if err != nil { - return nil, err + ClientSpecType: msg.ClientSpec, + SSIDoc: msgNewCredStatus, + SignerAddress: msg.Creator, } - // Proof Type Check - err = verification.DocumentProofTypeCheck(msgNewCredProof.Type, signersWithVM, msgNewCredProof.VerificationMethod) + credDocBytes, err := getClientSpecDocBytes(clientSpecOpts) if err != nil { return nil, err } // Verify Signature - err = verification.VerifyDocumentSignature(&ctx, credDocBytes, signersWithVM, signatures) + err = k.VerifyDocumentProof(ctx, credDocBytes, msgNewCredProof) if err != nil { return nil, err } diff --git a/x/ssi/keeper/msg_server_deactivate_did.go b/x/ssi/keeper/msg_server_deactivate_did.go new file mode 100644 index 0000000..9a433bd --- /dev/null +++ b/x/ssi/keeper/msg_server_deactivate_did.go @@ -0,0 +1,115 @@ +package keeper + +import ( + "context" + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + + "github.com/hypersign-protocol/hid-node/x/ssi/types" + "github.com/hypersign-protocol/hid-node/x/ssi/verification" +) + +// RPC controller for deactivating an existing DID document registered on hid-node +func (k msgServer) DeactivateDID(goCtx context.Context, msg *types.MsgDeactivateDID) (*types.MsgDeactivateDIDResponse, error) { + // Unwrap Go Context to Cosmos SDK Context + ctx := sdk.UnwrapSDKContext(goCtx) + + // Get the RPC inputs + msgDidId := msg.DidId + msgSignatures := msg.Signatures + + // Checks if the Did Document is already registered + if !k.HasDid(ctx, msgDidId) { + return nil, sdkerrors.Wrap(types.ErrDidDocNotFound, msgDidId) + } + + // Get the DID Document from state + didDocumentState, err := k.GetDidDocumentState(&ctx, msgDidId) + if err != nil { + return nil, err + } + didDocument := didDocumentState.DidDocument + didDocumentMetadata := didDocumentState.DidDocumentMetadata + + // Check if Did Document is deactivated + if didDocumentMetadata.Deactivated { + return nil, sdkerrors.Wrapf(types.ErrDidDocDeactivated, "DID Document %s is already deactivated", msgDidId) + } + + // Check if the version id of existing Did Document matches with the current one + existingDidDocVersionId := didDocumentMetadata.VersionId + incomingDidDocVersionId := msg.VersionId + if existingDidDocVersionId != incomingDidDocVersionId { + errMsg := fmt.Sprintf( + "Expected %s with version %s. Got version %s", + didDocument.Id, existingDidDocVersionId, incomingDidDocVersionId) + return nil, sdkerrors.Wrap(types.ErrUnexpectedDidVersion, errMsg) + } + + // Gather controllers + controllers := getControllersForDeactivateDID(didDocument) + if err := k.checkControllerPresenceInState(ctx, controllers, didDocument.Id); err != nil { + return nil, err + } + + signMap := makeSignatureMap(msgSignatures) + + // Get controller VM map + controllerMap, err := k.formAnyControllerVmListMap(ctx, controllers, + didDocument.VerificationMethod, signMap) + if err != nil { + return nil, err + } + + // Get Client Spec + clientSpecOpts := types.ClientSpecOpts{ + ClientSpecType: msg.ClientSpec, + SSIDoc: didDocument, + SignerAddress: msg.Creator, + } + var didDocBytes []byte + didDocBytes, err = getClientSpecDocBytes(clientSpecOpts) + if err != nil { + return nil, err + } + + // Signature Verification + err = verification.VerifySignatureOfAnyController(didDocBytes, controllerMap) + if err != nil { + return nil, err + } + + // Create updated metadata + updatedMetadata := types.CreateNewMetadata(ctx) + updatedMetadata.Created = didDocumentState.GetDidDocumentMetadata().GetCreated() + updatedMetadata.Deactivated = true + + // Form the updated DID Document + updatedDidDocumentState := types.DidDocumentState{ + DidDocument: didDocument, + DidDocumentMetadata: &updatedMetadata, + } + + // Update the DID Document in Store + if err := k.UpdateDidDocumentInStore(ctx, updatedDidDocumentState); err != nil { + return nil, err + } + + return &types.MsgDeactivateDIDResponse{Id: 1}, nil +} + +// getControllersForDeactivateDID returns a list of controllers required for Deactivate DID Operation +func getControllersForDeactivateDID(didDocument *types.Did) []string { + var controllers []string = []string{} + + // If the controller list is empty, DID Subject is assumed to be the sole controller of DID Document + if len(didDocument.Controller) == 0 { + controllers = append(controllers, didDocument.Id) + } else { + controllers = didDocument.Controller + } + + return controllers +} diff --git a/x/ssi/keeper/msg_server_did.go b/x/ssi/keeper/msg_server_did.go deleted file mode 100644 index fb3dfcb..0000000 --- a/x/ssi/keeper/msg_server_did.go +++ /dev/null @@ -1,269 +0,0 @@ -package keeper - -import ( - "context" - "fmt" - - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/hypersign-protocol/hid-node/x/ssi/types" - "github.com/hypersign-protocol/hid-node/x/ssi/verification" -) - -// RPC controller for registering DID document on hid-node -func (k msgServer) CreateDID(goCtx context.Context, msg *types.MsgCreateDID) (*types.MsgCreateDIDResponse, error) { - ctx := sdk.UnwrapSDKContext(goCtx) - - chainNamespace := k.GetChainNamespace(&ctx) - - msgDidDocument := msg.GetDidDocString() - didId := msgDidDocument.GetId() - didSigners := msgDidDocument.GetSigners() - // Get the Verification Method for controller DIDs - didSignersWithVM, err := k.GetVMForSigners(&ctx, didSigners) - if err != nil { - return nil, err - } - - msgSignatures := msg.GetSignatures() - signerAddress := msg.GetCreator() - - // Checks if the Did Document is valid - err = verification.ValidateDidDocument(msgDidDocument, chainNamespace) - if err != nil { - return nil, err - } - - // Checks if the Did Document is already registered - if k.HasDid(ctx, didId) { - return nil, sdkerrors.Wrap(types.ErrDidDocExists, fmt.Sprintf("DID already exists %s", didId)) - } - - // Checks if the Controllers are valid - didControllers := msgDidDocument.GetController() - didVerificationMethod := msgDidDocument.GetVerificationMethod() - if k.ValidateDidControllers(&ctx, didId, didControllers, didVerificationMethod) != nil { - return nil, sdkerrors.Wrap(types.ErrInvalidDidDoc, "DID controller is not valid") - } - - // Verification of Did Document Signature - - // ClientSpec check - var didDocBytes []byte - - msgClientSpecType := msg.GetClientSpec() - clientSpecOpts := types.ClientSpecOpts{ - SSIDoc: msgDidDocument, - SignerAddress: signerAddress, - } - - didDocBytes, err = getClientSpecDocBytes(msgClientSpecType, clientSpecOpts) - if err != nil { - return nil, err - } - - if err := verification.VerifyDidSignature(&ctx, didDocBytes, didSignersWithVM, msgSignatures); err != nil { - return nil, err - } - - // Create the Metadata - metadata := types.CreateNewMetadata(ctx) - - // Form the Completet DID Document - didDocumentState := types.DidDocumentState{ - DidDocument: msgDidDocument, - DidDocumentMetadata: &metadata, - } - - // Register DID Document in Store once all validation checks are passed - id := k.RegisterDidDocumentInStore(ctx, &didDocumentState) - - return &types.MsgCreateDIDResponse{Id: id}, nil -} - -// RPC controller for updating an existing DID document registered on hid-node -func (k msgServer) UpdateDID(goCtx context.Context, msg *types.MsgUpdateDID) (*types.MsgUpdateDIDResponse, error) { - ctx := sdk.UnwrapSDKContext(goCtx) - chainNamespace := k.GetChainNamespace(&ctx) - - msgDidDocument := msg.GetDidDocString() - didId := msgDidDocument.GetId() - - msgVersionId := msg.GetVersionId() - - // Check if the input DID Document is valid - err := verification.ValidateDidDocument(msgDidDocument, chainNamespace) - if err != nil { - return nil, err - } - - // Checks whether the DID Document exists in the store - if !k.HasDid(ctx, didId) { - return nil, sdkerrors.Wrap(sdkerrors.ErrKeyNotFound, fmt.Sprintf("DID doesnt exists %s", didId)) - } - - // Get the registered Did Document from store - oldDidDocumentState, err := k.GetDidDocumentState(&ctx, didId) - if err != nil { - return nil, err - } - oldDidDocument := oldDidDocumentState.GetDidDocument() - oldMetaData := oldDidDocumentState.GetDidDocumentMetadata() - - // Check if the status of DID Document is deactivated - if err := verification.VerifyDidDeactivate(oldMetaData, didId); err != nil { - return nil, err - } - - // Check if the version id of existing Did Document matches with the current one - if oldMetaData.VersionId != msgVersionId { - errMsg := fmt.Sprintf("Expected %s with version %s. Got version %s", msgDidDocument.Id, oldMetaData.VersionId, msgVersionId) - return nil, sdkerrors.Wrap(types.ErrUnexpectedDidVersion, errMsg) - } - - // Check if the controllers are valid - didControllers := msgDidDocument.GetController() - didVerificationMethod := msgDidDocument.GetVerificationMethod() - if k.ValidateDidControllers(&ctx, didId, didControllers, didVerificationMethod) != nil { - return nil, sdkerrors.Wrap(types.ErrInvalidDidDoc, "DID controller is not valid") - } - - // Validate Signatures - signatures := msg.GetSignatures() - signers := GetUpdatedSigners(&ctx, oldDidDocument, msgDidDocument, signatures) - signersWithVm, err := k.GetVMForSigners(&ctx, signers) - if err != nil { - return nil, err - } - - // ClientSpec check - var didDocBytes []byte - - clientSpecType := msg.GetClientSpec() - signerAddress := msg.GetCreator() - clientSpecOpts := types.ClientSpecOpts{ - SSIDoc: msgDidDocument, - SignerAddress: signerAddress, - } - - didDocBytes, err = getClientSpecDocBytes(clientSpecType, clientSpecOpts) - if err != nil { - return nil, err - } - - if err := verification.VerifyDidSignature(&ctx, didDocBytes, signersWithVm, msg.Signatures); err != nil { - return nil, err - } - - // Create the Metadata and assign `created` and `deactivated` to previous DIDDoc's metadata values - metadata := types.CreateNewMetadata(ctx) - metadata.Created = oldDidDocumentState.GetDidDocumentMetadata().GetCreated() - metadata.Deactivated = oldDidDocumentState.GetDidDocumentMetadata().GetDeactivated() - - // Form the DID Document - didDocumentState := types.DidDocumentState{ - DidDocument: msgDidDocument, - DidDocumentMetadata: &metadata, - } - - // Update the DID Document in store - if err := k.UpdateDidDocumentInStore(ctx, didDocumentState); err != nil { - return nil, err - } - - return &types.MsgUpdateDIDResponse{UpdateId: didId}, nil -} - -// RPC controller for deactivating an existing DID document registered on hid-node -func (k msgServer) DeactivateDID(goCtx context.Context, msg *types.MsgDeactivateDID) (*types.MsgDeactivateDIDResponse, error) { - ctx := sdk.UnwrapSDKContext(goCtx) - - chainNamespace := k.GetChainNamespace(&ctx) - - msgDidId := msg.GetDidId() - msgVersionId := msg.GetVersionId() - - // Check if the Did id format is valid - err := verification.IsValidID(msgDidId, chainNamespace, "didDocument") - if err != nil { - return nil, err - } - - // Checks whether the DID Document exists in the store - if !k.HasDid(ctx, msgDidId) { - return nil, sdkerrors.Wrap(sdkerrors.ErrKeyNotFound, fmt.Sprintf("DID doesnt exists %s", msgDidId)) - } - - // Retrieve the DID Document from store - didDocumentState, err := k.GetDidDocumentState(&ctx, msgDidId) - if err != nil { - return nil, fmt.Errorf("failed to fetch DID Document with Did Id %s from store", msgDidId) - } - didDoc := didDocumentState.GetDidDocument() - didDocMetadata := didDocumentState.GetDidDocumentMetadata() - oldVersionId := didDocMetadata.GetVersionId() - - // Check if the DID is already deactivated - if err := verification.VerifyDidDeactivate(didDocMetadata, msgDidId); err != nil { - return nil, err - } - - // Check if the input version id is similar to registered DID Document's version id - if oldVersionId != msgVersionId { - errMsg := fmt.Sprintf("Expected %s with version %s. Got version %s", msgDidId, oldVersionId, msgVersionId) - return nil, sdkerrors.Wrap(types.ErrUnexpectedDidVersion, errMsg) - } - - // Check the validity of DID Controllers - didControllers := didDoc.GetController() - didVerificationMethod := didDoc.GetVerificationMethod() - if k.ValidateDidControllers(&ctx, msgDidId, didControllers, didVerificationMethod) != nil { - return nil, sdkerrors.Wrap(types.ErrInvalidDidDoc, "DID controller is not valid") - } - - // Signature Verification - signers := didDoc.GetSigners() - // Get the Verification Method for controller DIDs - signersWithVM, err := k.GetVMForSigners(&ctx, signers) - if err != nil { - return nil, err - } - signatures := msg.GetSignatures() - - // ClientSpec check - var didDocBytes []byte - msgClientSpecType := msg.GetClientSpec() - msgSignerAddress := msg.GetCreator() - - clientSpecOpts := types.ClientSpecOpts{ - SSIDoc: didDoc, - SignerAddress: msgSignerAddress, - } - - didDocBytes, err = getClientSpecDocBytes(msgClientSpecType, clientSpecOpts) - if err != nil { - return nil, err - } - - if err := verification.VerifyDidSignature(&ctx, didDocBytes, signersWithVM, signatures); err != nil { - return nil, err - } - - // Create updated metadata - updatedMetadata := types.CreateNewMetadata(ctx) - updatedMetadata.Created = didDocumentState.GetDidDocumentMetadata().GetCreated() - updatedMetadata.Deactivated = true - - // Form the updated DID Document - updatedDidDocumentState := types.DidDocumentState{ - DidDocument: didDoc, - DidDocumentMetadata: &updatedMetadata, - } - - // Update the DID Document in Store - if err := k.UpdateDidDocumentInStore(ctx, updatedDidDocumentState); err != nil { - return nil, err - } - - return &types.MsgDeactivateDIDResponse{Id: 1}, nil -} diff --git a/x/ssi/keeper/msg_server_schema.go b/x/ssi/keeper/msg_server_schema.go index ec2a809..7907b63 100644 --- a/x/ssi/keeper/msg_server_schema.go +++ b/x/ssi/keeper/msg_server_schema.go @@ -62,37 +62,20 @@ func (k msgServer) CreateSchema(goCtx context.Context, msg *types.MsgCreateSchem return nil, sdkerrors.Wrapf(types.ErrInvalidDate, "created date provided shouldn't be greater than the current block time") } - signature := &types.SignInfo{ - VerificationMethodId: schemaProof.VerificationMethod, - Signature: schemaProof.ProofValue, - } - signatures := []*types.SignInfo{signature} - signers := authorDidDocument.DidDocument.GetSigners() - signersWithVM, err := k.GetVMForSigners(&ctx, signers) - if err != nil { - return nil, err - } - // ClientSpec check - clientSpecType := msg.ClientSpec clientSpecOpts := types.ClientSpecOpts{ - SSIDoc: schemaDoc, - SignerAddress: msg.Creator, - } - - schemaDocBytes, err := getClientSpecDocBytes(clientSpecType, clientSpecOpts) - if err != nil { - return nil, err + ClientSpecType: msg.ClientSpec, + SSIDoc: schemaDoc, + SignerAddress: msg.Creator, } - // Proof Type Check - err = verification.DocumentProofTypeCheck(schemaProof.Type, signersWithVM, schemaProof.VerificationMethod) + schemaDocBytes, err := getClientSpecDocBytes(clientSpecOpts) if err != nil { return nil, err } // Signature check - if err := verification.VerifyDocumentSignature(&ctx, schemaDocBytes, signersWithVM, signatures); err != nil { + if err := k.VerifyDocumentProof(ctx, schemaDocBytes, schemaProof); err != nil { return nil, err } diff --git a/x/ssi/keeper/msg_server_update_did.go b/x/ssi/keeper/msg_server_update_did.go new file mode 100644 index 0000000..a626f81 --- /dev/null +++ b/x/ssi/keeper/msg_server_update_did.go @@ -0,0 +1,220 @@ +package keeper + +import ( + "context" + "fmt" + "reflect" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/hypersign-protocol/hid-node/x/ssi/types" + "github.com/hypersign-protocol/hid-node/x/ssi/verification" +) + +// RPC controller for updating an existing DID document registered on hid-node +func (k msgServer) UpdateDID(goCtx context.Context, msg *types.MsgUpdateDID) (*types.MsgUpdateDIDResponse, error) { + // Unwrap Go Context to Cosmos SDK Context + ctx := sdk.UnwrapSDKContext(goCtx) + + // Get the RPC inputs + msgDidDocument := msg.DidDocString + msgSignatures := msg.Signatures + + // Validate DID Document + if err := msgDidDocument.ValidateDidDocument(); err != nil { + return nil, err + } + + // Validate namespace in DID Document + if err := didNamespaceValidation(k, ctx, msgDidDocument); err != nil { + return nil, err + } + + // Checks if the Did Document is already registered + if !k.HasDid(ctx, msgDidDocument.Id) { + return nil, sdkerrors.Wrap(types.ErrDidDocNotFound, msgDidDocument.Id) + } + + // Fetch registered Did Document from state + existingDidDocumentState, err := k.GetDidDocumentState(&ctx, msgDidDocument.Id) + if err != nil { + return nil, err + } + existingDidDocument := existingDidDocumentState.DidDocument + + // Check if the incoming DID Document has any changes. If not, throw an error. + if reflect.DeepEqual(existingDidDocument, msgDidDocument) { + return nil, sdkerrors.Wrap(types.ErrInvalidDidDoc, "incoming DID Document does not have any changes") + } + + // Check if the version id of existing Did Document matches with the current one + existingDidDocVersionId := existingDidDocumentState.DidDocumentMetadata.VersionId + incomingDidDocVersionId := msg.VersionId + if existingDidDocVersionId != incomingDidDocVersionId { + errMsg := fmt.Sprintf( + "Expected %s with version %s. Got version %s", + msgDidDocument.Id, existingDidDocVersionId, incomingDidDocVersionId) + return nil, sdkerrors.Wrap(types.ErrUnexpectedDidVersion, errMsg) + } + + // Look for any change in Controller array + mandatoryControllers, anyControllers := getControllersForUpdateDID(existingDidDocument, msgDidDocument) + if err := k.checkControllerPresenceInState(ctx, mandatoryControllers, msgDidDocument.Id); err != nil { + return nil, err + } + if err := k.checkControllerPresenceInState(ctx, anyControllers, msgDidDocument.Id); err != nil { + return nil, err + } + + // Gather Verification Methods + updatedVms := getVerificationMethodsForUpdateDID(existingDidDocument.VerificationMethod, msgDidDocument.VerificationMethod) + + signMap := makeSignatureMap(msgSignatures) + + requiredVmMap, err := k.formMustControllerVmListMap(ctx, mandatoryControllers, updatedVms, signMap) + if err != nil { + return nil, err + } + + optionalVmMap, err := k.formAnyControllerVmListMap(ctx, + anyControllers, existingDidDocument.VerificationMethod, signMap) + if err != nil { + return nil, err + } + + // ClientSpec Opts + clientSpecOpts := types.ClientSpecOpts{ + ClientSpecType: msg.ClientSpec, + SSIDoc: msgDidDocument, + SignerAddress: msg.Creator, + } + var didDocBytes []byte + didDocBytes, err = getClientSpecDocBytes(clientSpecOpts) + if err != nil { + return nil, err + } + + // Signature Verification + if err := verification.VerifySignatureOfEveryController(didDocBytes, requiredVmMap); err != nil { + return nil, err + } + + if err := verification.VerifySignatureOfAnyController(didDocBytes, optionalVmMap); err != nil { + return nil, err + } + + // Create the Metadata and assign `created` and `deactivated` to previous DIDDoc's metadata values + metadata := types.CreateNewMetadata(ctx) + metadata.Created = existingDidDocumentState.GetDidDocumentMetadata().GetCreated() + metadata.Deactivated = existingDidDocumentState.GetDidDocumentMetadata().GetDeactivated() + + // Form the DID Document + didDocumentState := types.DidDocumentState{ + DidDocument: msgDidDocument, + DidDocumentMetadata: &metadata, + } + + // Update the DID Document in store + if err := k.UpdateDidDocumentInStore(ctx, didDocumentState); err != nil { + return nil, err + } + + return &types.MsgUpdateDIDResponse{UpdateId: msgDidDocument.Id}, nil +} + +// getControllersForUpdateDID returns two lists of controllers. The first parameter is a list of controllers, +// where every controller needs to be verified. The second parameter is a list of controllers, where atleast one +// of the controllers require verification. +func getControllersForUpdateDID(existingDidDoc *types.Did, incomingDidDoc *types.Did) ([]string, []string) { + var mandatoryControllers []string = []string{} + var anyControllers []string = []string{} + + // Make map of existing controllers + existingControllersMap := map[string]bool{} + if len(existingDidDoc.Controller) == 0 { + // If the controller list is empty, DID Subject is assumed to be the sole controller of DID Document + existingControllersMap[existingDidDoc.Id] = true + } else { + for _, controller := range existingDidDoc.Controller { + existingControllersMap[controller] = true + } + } + + // Make map of incoming controllers + incomingControllersMap := map[string]bool{} + // If the controller list is empty, DID Subject is assumed to be the sole controller of DID Document + if len(incomingDidDoc.Controller) == 0 { + incomingControllersMap[incomingDidDoc.Id] = true + if _, present := existingControllersMap[incomingDidDoc.Id]; !present { + mandatoryControllers = append( + mandatoryControllers, + incomingDidDoc.Id, + ) + } else { + anyControllers = append( + anyControllers, + incomingDidDoc.Id, + ) + } + } else { + for _, controller := range incomingDidDoc.Controller { + incomingControllersMap[controller] = true + // Check if controller present in existing controller map. + // If it's not present, the controller is being added to existing Did Document. + // Add the controller to "required" group + if _, present := existingControllersMap[controller]; !present { + mandatoryControllers = append( + mandatoryControllers, + controller, + ) + } else { + anyControllers = append( + anyControllers, + controller, + ) + } + } + } + + // Check if any controllers are deleted + // If so, add them to the "optional" group + for controller := range existingControllersMap { + if _, present := incomingControllersMap[controller]; !present { + anyControllers = append( + anyControllers, + controller, + ) + } + } + + return mandatoryControllers, anyControllers +} + +// getVerificationMethodsForUpdateDID returns a map highlighting inclusion of new Verification methods +// and/or removal of any existing Verification method. +func getVerificationMethodsForUpdateDID(existingVMs []*types.VerificationMethod, incomingVMs []*types.VerificationMethod) []*types.VerificationMethod { + updatedVms := []*types.VerificationMethod{} + + // Make map of existing VMs + existingVmMap := map[string]*types.VerificationMethod{} + for _, vm := range existingVMs { + existingVmMap[vm.Id] = vm + } + + // Make map of incoming VMs + incomingVmMap := map[string]*types.VerificationMethod{} + for _, vm := range incomingVMs { + incomingVmMap[vm.Id] = vm + // Check if VM is present in existing VM map. + // If it's not present, the VM is being added to existing Did Document. + // Add the VM to "required" group + if _, present := existingVmMap[vm.Id]; !present { + updatedVms = append( + updatedVms, + vm, + ) + } + } + + return updatedVms +} diff --git a/x/ssi/keeper/namespace.go b/x/ssi/keeper/namespace.go new file mode 100644 index 0000000..df9d226 --- /dev/null +++ b/x/ssi/keeper/namespace.go @@ -0,0 +1,79 @@ +package keeper + +import ( + "fmt" + "strings" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/hypersign-protocol/hid-node/x/ssi/types" +) + +// namespaceValidation validates the namespace in document id +func namespaceValidation(k msgServer, ctx sdk.Context, docId string) error { + genesisNamespace := k.GetChainNamespace(&ctx) + + docIdElements := strings.Split(docId, ":") + docNamespaceIndex := 2 + + if genesisNamespace == "" { + if len(docIdElements) != 3 { + return fmt.Errorf( + "expected number of did id elements for mainnet to be 3, got %s for id: %s", + fmt.Sprint(len(docIdElements)), + docId, + ) + } + } else { + docNamespace := docIdElements[docNamespaceIndex] + if genesisNamespace != docNamespace { + return fmt.Errorf( + "expected namespace for id %s to be %s, got %s", + docId, genesisNamespace, docNamespace) + } + } + + return nil +} + +// didNamespaceValidation validates the namespace in Did Document Id +func didNamespaceValidation(k msgServer, ctx sdk.Context, didDoc *types.Did) error { + // Subject ID check + if err := namespaceValidation(k, ctx, didDoc.Id); err != nil { + return err + } + + // Controllers check + for _, controller := range didDoc.Controller { + if err := namespaceValidation(k, ctx, controller); err != nil { + return err + } + } + + // Verification Method ID checks + for _, vm := range didDoc.VerificationMethod { + didId, _ := types.GetElementsFromDidUrl(vm.Id) + if err := namespaceValidation(k, ctx, didId); err != nil { + return err + } + } + + // Verification Relationships check + vmRelationshipList := [][]string{ + didDoc.Authentication, + didDoc.AssertionMethod, + didDoc.KeyAgreement, + didDoc.CapabilityDelegation, + didDoc.CapabilityInvocation, + } + + for _, vmRelationship := range vmRelationshipList { + // didUrl check and presence in verification methods + for _, id := range vmRelationship { + if err := namespaceValidation(k, ctx, id); err != nil { + return err + } + } + } + + return nil +} diff --git a/x/ssi/tests/common.go b/x/ssi/tests/common.go index 361c171..309f1c3 100644 --- a/x/ssi/tests/common.go +++ b/x/ssi/tests/common.go @@ -10,7 +10,6 @@ import ( "strings" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/hypersign-protocol/hid-node/x/ssi/common" "github.com/hypersign-protocol/hid-node/x/ssi/keeper" "github.com/hypersign-protocol/hid-node/x/ssi/types" "github.com/multiformats/go-multibase" @@ -79,9 +78,9 @@ func GenerateDidDocumentRPCElements(keyPair GenericKeyPair, signingElements []Di var vmType string switch keyPair.(type) { case ed25519KeyPair: - vmType = common.Ed25519VerificationKey2020 + vmType = types.Ed25519VerificationKey2020 case secp256k1KeyPair: - vmType = common.EcdsaSecp256k1VerificationKey2019 + vmType = types.EcdsaSecp256k1VerificationKey2019 } var vm = &types.VerificationMethod{ @@ -106,7 +105,7 @@ func GenerateDidDocumentRPCElements(keyPair GenericKeyPair, signingElements []Di } } else { signingElements = []DidSigningElements{ - DidSigningElements{ + { keyPair: keyPair, vmId: vm.Id, }, @@ -165,9 +164,9 @@ func GenerateSchemaDocumentRPCElements(keyPair GenericKeyPair, authorId string, var proofType string switch keyPair.(type) { case ed25519KeyPair: - proofType = common.VerificationKeySignatureMap["Ed25519VerificationKey2020"] + proofType = types.VerificationKeySignatureMap["Ed25519VerificationKey2020"] case secp256k1KeyPair: - proofType = common.VerificationKeySignatureMap["EcdsaSecp256k1VerificationKey2019"] + proofType = types.VerificationKeySignatureMap["EcdsaSecp256k1VerificationKey2019"] } var schemaProof *types.SchemaProof = &types.SchemaProof{ @@ -207,9 +206,9 @@ func GenerateCredStatusRPCElements(keyPair GenericKeyPair, issuerId string, verf var proofType string switch keyPair.(type) { case ed25519KeyPair: - proofType = common.VerificationKeySignatureMap["Ed25519VerificationKey2020"] + proofType = types.VerificationKeySignatureMap["Ed25519VerificationKey2020"] case secp256k1KeyPair: - proofType = common.VerificationKeySignatureMap["EcdsaSecp256k1VerificationKey2019"] + proofType = types.VerificationKeySignatureMap["EcdsaSecp256k1VerificationKey2019"] } var credentialProof *types.CredentialProof = &types.CredentialProof{ diff --git a/x/ssi/tests/tx_create_did_test.go b/x/ssi/tests/tx_create_did_test.go index 1877260..d5f68d6 100644 --- a/x/ssi/tests/tx_create_did_test.go +++ b/x/ssi/tests/tx_create_did_test.go @@ -1,13 +1,11 @@ package tests import ( - "fmt" "testing" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/hypersign-protocol/hid-node/x/ssi/keeper" "github.com/hypersign-protocol/hid-node/x/ssi/types" - "github.com/stretchr/testify/assert" ) func TestCreateDIDUsingEd25519KeyPair(t *testing.T) { @@ -102,8 +100,6 @@ func TestInvalidServiceType(t *testing.T) { t.FailNow() } - assert.Contains(t, err.Error(), fmt.Sprintf("Service Type %s is Invalid: Invalid Service", invalidServiceType)) - t.Log("Test Completed") } diff --git a/x/ssi/tests/tx_create_schema_test.go b/x/ssi/tests/tx_create_schema_test.go index ba676b3..b9d666b 100644 --- a/x/ssi/tests/tx_create_schema_test.go +++ b/x/ssi/tests/tx_create_schema_test.go @@ -57,99 +57,3 @@ func TestCreateSchema(t *testing.T) { t.Log("Create Schema Tx Test Completed") } - -func TestCreateSchemaWithMultiControllerDid(t *testing.T) { - t.Log("Running test for Valid Create Schmea Tx") - k, ctx := TestKeeper(t) - msgServer := keeper.NewMsgServerImpl(*k) - goCtx := sdk.WrapSDKContext(ctx) - - k.SetChainNamespace(&ctx, "devnet") - - keyPair1 := GenerateSecp256k1KeyPair() - rpcElements1 := GenerateDidDocumentRPCElements(keyPair1, []DidSigningElements{}) - t.Logf("Registering Employee 1 with DID Id: %s", rpcElements1.DidDocument.GetId()) - - msgCreateDID := &types.MsgCreateDID{ - DidDocString: rpcElements1.DidDocument, - Signatures: rpcElements1.Signatures, - Creator: rpcElements1.Creator, - } - - _, err := msgServer.CreateDID(goCtx, msgCreateDID) - if err != nil { - t.Error("DID Registeration Failed") - t.Error(err) - t.FailNow() - } - t.Log("Employee 1 DID Registered Successfully") - - keyPair2 := GenerateSecp256k1KeyPair() - rpcElements2 := GenerateDidDocumentRPCElements(keyPair2, []DidSigningElements{}) - t.Logf("Registering Employee 2 with DID Id: %s", rpcElements2.DidDocument.GetId()) - - msgCreateDID = &types.MsgCreateDID{ - DidDocString: rpcElements2.DidDocument, - Signatures: rpcElements2.Signatures, - Creator: rpcElements2.Creator, - } - - _, err = msgServer.CreateDID(goCtx, msgCreateDID) - if err != nil { - t.Error("DID Registeration Failed") - t.Error(err) - t.FailNow() - } - t.Log("Employee 2 DID Registered Successfully") - - keyPairOrg := GenerateSecp256k1KeyPair() - singingElements := []DidSigningElements{ - DidSigningElements{ - keyPair: keyPair1, - vmId: rpcElements1.DidDocument.VerificationMethod[0].Id, - }, - DidSigningElements{ - keyPair: keyPair2, - vmId: rpcElements2.DidDocument.VerificationMethod[0].Id, - }, - } - rpcElementsOrg := GenerateDidDocumentRPCElements(keyPairOrg, singingElements) - t.Logf("Registering Org with DID Id: %s", rpcElementsOrg.DidDocument.GetId()) - - msgCreateDID = &types.MsgCreateDID{ - DidDocString: rpcElementsOrg.DidDocument, - Signatures: rpcElementsOrg.Signatures, - Creator: rpcElementsOrg.Creator, - } - - _, err = msgServer.CreateDID(goCtx, msgCreateDID) - if err != nil { - t.Error("DID Registeration Failed") - t.Error(err) - t.FailNow() - } - t.Log("Org DID Registered Successfully") - - t.Log("Registering Schema") - schemaRpcElements := GenerateSchemaDocumentRPCElements( - keyPair1, - rpcElementsOrg.DidDocument.Id, - rpcElements1.DidDocument.AssertionMethod[0], - ) - - msgCreateSchema := &types.MsgCreateSchema{ - SchemaDoc: schemaRpcElements.SchemaDocument, - SchemaProof: schemaRpcElements.SchemaProof, - Creator: schemaRpcElements.Creator, - } - - _, errCreateSchema := msgServer.CreateSchema(goCtx, msgCreateSchema) - if errCreateSchema != nil { - t.Error("Schema Registeration Failed") - t.Error(errCreateSchema) - t.FailNow() - } - t.Log("Schema Registered Successfully") - - t.Log("Create Schema Tx Test Completed") -} diff --git a/x/ssi/tests/tx_register_credential_status_test.go b/x/ssi/tests/tx_register_credential_status_test.go index 05180d0..e21774e 100644 --- a/x/ssi/tests/tx_register_credential_status_test.go +++ b/x/ssi/tests/tx_register_credential_status_test.go @@ -62,105 +62,6 @@ func TestRegisterCredentialStatus(t *testing.T) { t.Log("Valid Register Cred Tx Test Completed") } -func TestCreateCredentialStatusWithMultiControllerDid(t *testing.T) { - t.Log("Running test for Valid Create Schmea Tx") - k, ctx := TestKeeper(t) - msgServer := keeper.NewMsgServerImpl(*k) - goCtx := sdk.WrapSDKContext(ctx) - - k.SetChainNamespace(&ctx, "devnet") - - keyPair1 := GenerateSecp256k1KeyPair() - rpcElements1 := GenerateDidDocumentRPCElements(keyPair1, []DidSigningElements{}) - t.Logf("Registering Employee 1 with DID Id: %s", rpcElements1.DidDocument.GetId()) - - msgCreateDID := &types.MsgCreateDID{ - DidDocString: rpcElements1.DidDocument, - Signatures: rpcElements1.Signatures, - Creator: rpcElements1.Creator, - } - - _, err := msgServer.CreateDID(goCtx, msgCreateDID) - if err != nil { - t.Error("DID Registeration Failed") - t.Error(err) - t.FailNow() - } - t.Log("Employee 1 DID Registered Successfully") - - keyPair2 := GenerateSecp256k1KeyPair() - rpcElements2 := GenerateDidDocumentRPCElements(keyPair2, []DidSigningElements{}) - t.Logf("Registering Employee 2 with DID Id: %s", rpcElements2.DidDocument.GetId()) - - msgCreateDID = &types.MsgCreateDID{ - DidDocString: rpcElements2.DidDocument, - Signatures: rpcElements2.Signatures, - Creator: rpcElements2.Creator, - } - - _, err = msgServer.CreateDID(goCtx, msgCreateDID) - if err != nil { - t.Error("DID Registeration Failed") - t.Error(err) - t.FailNow() - } - t.Log("Employee 2 DID Registered Successfully") - - keyPairOrg := GenerateSecp256k1KeyPair() - singingElements := []DidSigningElements{ - DidSigningElements{ - keyPair: keyPair1, - vmId: rpcElements1.DidDocument.VerificationMethod[0].Id, - }, - DidSigningElements{ - keyPair: keyPair2, - vmId: rpcElements2.DidDocument.VerificationMethod[0].Id, - }, - } - rpcElementsOrg := GenerateDidDocumentRPCElements(keyPairOrg, singingElements) - t.Logf("Registering Org with DID Id: %s", rpcElementsOrg.DidDocument.GetId()) - - msgCreateDID = &types.MsgCreateDID{ - DidDocString: rpcElementsOrg.DidDocument, - Signatures: rpcElementsOrg.Signatures, - Creator: rpcElementsOrg.Creator, - } - - _, err = msgServer.CreateDID(goCtx, msgCreateDID) - if err != nil { - t.Error("DID Registeration Failed") - t.Error(err) - t.FailNow() - } - t.Log("Org DID Registered Successfully") - - t.Log("Registering Schema") - credRpcElements := GenerateCredStatusRPCElements( - keyPair2, - rpcElementsOrg.DidDocument.Id, - rpcElements2.DidDocument.VerificationMethod[0], - ) - - msgRegisterCredentialStatus := &types.MsgRegisterCredentialStatus{ - CredentialStatus: credRpcElements.Status, - Proof: credRpcElements.Proof, - Creator: Creator, - } - t.Logf("Registering Credential Status with Id: %s", credRpcElements.Status.GetClaim().GetId()) - - t.Logf("Block Time: %s", ctx.BlockTime().Format(time.RFC3339)) - - _, errCredStatus := msgServer.RegisterCredentialStatus(goCtx, msgRegisterCredentialStatus) - if errCredStatus != nil { - t.Error("Credential Status Registeration Failed") - t.Error(errCredStatus) - t.FailNow() - } - t.Log("Credential Status Registeration Successful") - - t.Log("Valid Register Cred Tx Test Completed") -} - func TestUpdateCredentialStatus(t *testing.T) { t.Log("Running test for updating status of registered credential status") k, ctx := TestKeeper(t) diff --git a/x/ssi/common/constants.go b/x/ssi/types/common.go similarity index 82% rename from x/ssi/common/constants.go rename to x/ssi/types/common.go index 8bc5517..f5f53e0 100644 --- a/x/ssi/common/constants.go +++ b/x/ssi/types/common.go @@ -1,4 +1,4 @@ -package common +package types // Supported Verification Method Types const Ed25519VerificationKey2020 = "Ed25519VerificationKey2020" @@ -12,6 +12,15 @@ var VerificationKeySignatureMap = map[string]string{ EcdsaSecp256k1RecoveryMethod2020: "EcdsaSecp256k1RecoverySignature2020", } +// Supported Service Types +var SupportedServiceTypes = []string{ + "LinkedDomains", +} + +// Did Document ID +const DocumentIdentifierDid = "did" +const DidMethod = "hid" + // Supported CAIP-10 prefixes const EIP155 string = "eip155" diff --git a/x/ssi/types/diddoc_validation.go b/x/ssi/types/diddoc_validation.go new file mode 100644 index 0000000..144e33e --- /dev/null +++ b/x/ssi/types/diddoc_validation.go @@ -0,0 +1,324 @@ +package types + +import ( + "fmt" + "regexp" + "strings" + + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// isValidDidDoc checks if the DID Id is valid +func isValidDidDocId(id string) error { + // check the number of elements in DID Document + idElements := strings.Split(id, ":") + if !(len(idElements) == 3 || len(idElements) == 4) { + return sdkerrors.Wrapf( + ErrInvalidDidDoc, + "number of elements in DID Id %s should be either 3 or 4", + id, + ) + } + + // check if the first element is valid document identifier + if idElements[0] != DocumentIdentifierDid { + return sdkerrors.Wrapf( + ErrInvalidDidDoc, + "document identifier should be %s", + DocumentIdentifierDid, + ) + } + + // check if the second element is the correct DID method + if idElements[1] != DidMethod { + return sdkerrors.Wrapf( + ErrInvalidDidDoc, + "DID method should be %s", + DidMethod, + ) + } + + // check proper method specific id + // TODO: need to define a specification for method-specific-id + methodSpecificId := idElements[len(idElements)-1] + isProperMethodSpecificId, err := regexp.MatchString( + "^[a-zA-Z0-9]{32,}$", + methodSpecificId, + ) + if err != nil { + return fmt.Errorf("error in parsing regular expression for method-specific-id: %s", err.Error()) + } + if !isProperMethodSpecificId { + return sdkerrors.Wrap( + ErrInvalidMethodSpecificId, + fmt.Sprintf( + "method-specific-id should be an alphanumeric string with minimum of 32 characters, received: %s", + methodSpecificId, + ), + ) + } + + return nil +} + +// isDidUrl checks if the input is a DID Url or not. +// More on DID Url syntax: https://www.w3.org/TR/did-core/#did-url-syntax +// TODO: add check for path and query +func isDidUrl(id string) error { + didId, fragment := GetElementsFromDidUrl(id) + + // check for fragment + if didId == "" || fragment == "" { + return sdkerrors.Wrapf( + ErrInvalidDidDoc, + "invalid didUrl %s", + id, + ) + } + + // check for DID Id + err := isValidDidDocId(didId) + if err != nil { + return err + } + return nil +} + +func isSupportedVmType(typ string) error { + supportedList := func() string { + var resultStr string = "" + for vKeyType := range VerificationKeySignatureMap { + resultStr += fmt.Sprintf("%s, ", vKeyType) + } + return resultStr + } + + if _, supported := VerificationKeySignatureMap[typ]; !supported { + return sdkerrors.Wrapf( + ErrInvalidDidDoc, + "%s verification method type not supported, supported verification method types: %s ", + typ, + supportedList(), + ) + } + return nil +} + +// verificationKeyCheck validates of publicKeyMultibase and blockchainAccountId. +// If the verfication method type is EcdsaSecp256k1RecoveryMethod2020, only blockchainAccountId +// field must be populated, else only publicKeyMultibase must be populated. +func verificationKeyCheck(vm *VerificationMethod) error { + // Verification Method of Type EcdsaSecp256k1RecoveryMethod2020 should only have + // `blockchainAccountId` field populated + switch vm.Type { + case EcdsaSecp256k1RecoveryMethod2020: + if vm.GetBlockchainAccountId() == "" { + return sdkerrors.Wrapf( + ErrBadRequestInvalidVerMethod, + "blockchainAccountId cannot be empty for verification method %s as it is of type %s", + vm.Id, + vm.Type, + ) + } + if vm.GetPublicKeyMultibase() != "" { + return sdkerrors.Wrapf( + ErrBadRequestInvalidVerMethod, + "publicKeyMultibase should be empty for verification method %s as it is type %s", + vm.Id, + vm.Type, + ) + } + + default: + if vm.GetBlockchainAccountId() != "" { + return sdkerrors.Wrapf( + ErrBadRequestInvalidVerMethod, + "blockchainAccountId should be empty for verification method %s as it is of type %s", + vm.Id, + vm.Type, + ) + } + if vm.GetPublicKeyMultibase() == "" { + return sdkerrors.Wrapf( + ErrBadRequestInvalidVerMethod, + "publicKeyMultibase cannot be empty for verification method %s as it is type %s", + vm.Id, + vm.Type, + ) + } + } + + return nil +} + +// checkDuplicateId return a duplicate Id from the list, if found +func checkDuplicateId(list []string) string { + presentMap := map[string]bool{} + for idx := range list { + if _, present := presentMap[list[idx]]; !present { + presentMap[list[idx]] = true + } else { + return list[idx] + } + } + return "" +} + +// validateServices validates the Service attribute of DID Document +func validateServices(services []*Service) error { + for _, service := range services { + var err error + + // validate service Id + if err = isDidUrl(service.Id); err != nil { + return ErrInvalidService.Wrapf("service ID %s is Invalid", service.Id) + } + + // validate service Type + foundType := false + for _, sType := range SupportedServiceTypes { + if service.Type == sType { + foundType = true + } + } + if !foundType { + return ErrInvalidService.Wrapf("service Type %s is Invalid", service.Type) + } + } + + // check if any duplicate service id exists + serviceIdList := []string{} + for _, service := range services { + serviceIdList = append(serviceIdList, service.Id) + } + if duplicateId := checkDuplicateId(serviceIdList); duplicateId != "" { + return ErrInvalidService.Wrapf("duplicate service found with Id: %s ", duplicateId) + } + + return nil +} + +// validateVerificationMethods validates all the verification methods present in DID Document +func validateVerificationMethods(vms []*VerificationMethod) error { + for _, vm := range vms { + var err error + + // Vm Id check + err = isDidUrl(vm.Id) + if err != nil { + return err + } + + // Vm Type check + err = isSupportedVmType(vm.Type) + if err != nil { + return err + } + + // Controller check + err = isValidDidDocId(vm.Controller) + if err != nil { + return err + } + + // blockchainAccountId and publicKeyMultibase check + err = verificationKeyCheck(vm) + if err != nil { + return err + } + } + + // check duplicate Vm Ids, publicKeyMultibase and blockchainAccountId + vmIdList := []string{} + publicKeyMultibaseList := []string{} + blockchainAccountIdList := []string{} + + for _, vm := range vms { + vmIdList = append(vmIdList, vm.Id) + publicKeyMultibaseList = append(publicKeyMultibaseList, vm.PublicKeyMultibase) + blockchainAccountIdList = append(blockchainAccountIdList, vm.BlockchainAccountId) + } + if duplicateId := checkDuplicateId(vmIdList); duplicateId != "" { + return ErrInvalidDidDoc.Wrapf("duplicate verification method Id found: %s ", duplicateId) + } + if duplicateKey := checkDuplicateId(publicKeyMultibaseList); duplicateKey != "" { + return ErrInvalidDidDoc.Wrapf("duplicate publicKeyMultibase found: %s ", duplicateKey) + } + if duplicateKey := checkDuplicateId(blockchainAccountIdList); duplicateKey != "" { + return ErrInvalidDidDoc.Wrapf("duplicate blockchainAccountId found: %s ", duplicateKey) + } + + return nil +} + +func validateVmRelationships(didDoc *Did) error { + // make verificationMethods map + vmMap := map[string]bool{} + for _, vm := range didDoc.VerificationMethod { + vmMap[vm.Id] = true + } + + vmRelationshipList := map[string][]string{ + "authentication": didDoc.Authentication, + "assertionMethod": didDoc.AssertionMethod, + "keyAgreement": didDoc.KeyAgreement, + "capabilityDelegation": didDoc.CapabilityDelegation, + "capabilityInvocation": didDoc.CapabilityInvocation, + } + + for field, vmRelationship := range vmRelationshipList { + // didUrl check and presence in verification methods + for _, element := range vmRelationship { + err := isDidUrl(element) + if err != nil { + return fmt.Errorf("%s: %s", field, err) + } + if _, found := vmMap[element]; !found { + return fmt.Errorf( + "%s: verification method id %s not found in verificationMethod list", + field, + element, + ) + } + } + } + + return nil +} + +// ValidateDidDocument validates the DID Document +func (didDoc *Did) ValidateDidDocument() error { + // Id check + err := isValidDidDocId(didDoc.Id) + if err != nil { + return err + } + + // Controller check + for _, controller := range didDoc.Controller { + err := isValidDidDocId(controller) + if err != nil { + return err + } + } + + // VerificationMethod check + err = validateVerificationMethods(didDoc.VerificationMethod) + if err != nil { + return err + } + + // Services check + err = validateServices(didDoc.Service) + if err != nil { + return err + } + + // Verification Method Relationships check + err = validateVmRelationships(didDoc) + if err != nil { + return err + } + + return nil +} diff --git a/x/ssi/types/message_did.go b/x/ssi/types/message_did.go index dd136ac..9b67323 100644 --- a/x/ssi/types/message_did.go +++ b/x/ssi/types/message_did.go @@ -5,36 +5,6 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) -// Returns a list of controllers present in the Did document along with -// their verification methods. -func (msg *Did) GetSigners() []Signer { - nControllers := len(msg.Controller) - - if nControllers > 0 { - signers := make([]Signer, nControllers) - for i, controller := range msg.Controller { - if controller == msg.Id { - signers[i] = Signer{ - Signer: controller, - Authentication: msg.Authentication, - AssertionMethod: msg.AssertionMethod, - KeyAgreement: msg.KeyAgreement, - CapabilityInvocation: msg.CapabilityInvocation, - CapabilityDelegation: msg.CapabilityDelegation, - VerificationMethod: msg.VerificationMethod, - } - } else { - signers[i] = Signer{ - Signer: controller, - } - } - } - return signers - } - - return []Signer{} -} - func (msg *Did) GetSignBytes() []byte { return ModuleCdc.MustMarshal(msg) } @@ -72,9 +42,11 @@ func (msg *MsgCreateDID) GetSignBytes() []byte { } func (msg *MsgCreateDID) ValidateBasic() error { - msgDidDocument := msg.GetDidDocString() - err := didDocumentStatelessVerification(msgDidDocument) - return err + didDoc := msg.DidDocString + if err := didDoc.ValidateDidDocument(); err != nil { + return err + } + return nil } // MsgUpdateDID Type Methods @@ -113,9 +85,11 @@ func (msg *MsgUpdateDID) GetSignBytes() []byte { } func (msg *MsgUpdateDID) ValidateBasic() error { - msgDidDocument := msg.GetDidDocString() - err := didDocumentStatelessVerification(msgDidDocument) - return err + didDoc := msg.DidDocString + if err := didDoc.ValidateDidDocument(); err != nil { + return err + } + return nil } // MsgDeactivateDID Type Methods diff --git a/x/ssi/types/ssi_types.go b/x/ssi/types/ssi_types.go index 90c2135..3ec9695 100644 --- a/x/ssi/types/ssi_types.go +++ b/x/ssi/types/ssi_types.go @@ -24,12 +24,33 @@ type ( DidId string IsValid bool } + + ExtendedVerificationMethod struct { + Id string + Type string + Controller string + PublicKeyMultibase string + BlockchainAccountId string + Signature string + } ) +func CreateExtendedVerificationMethod(vm *VerificationMethod, signature string) *ExtendedVerificationMethod { + return &ExtendedVerificationMethod{ + Id: vm.Id, + Type: vm.Type, + Controller: vm.Controller, + PublicKeyMultibase: vm.PublicKeyMultibase, + BlockchainAccountId: vm.BlockchainAccountId, + Signature: signature, + } +} + // Struct catering to supported Client Spec's required inputs type ClientSpecOpts struct { - SSIDoc SsiMsg - SignerAddress string + ClientSpecType string + SSIDoc SsiMsg + SignerAddress string } // Cosmos ADR SignDoc Struct Definitions @@ -58,3 +79,10 @@ type ( Sequence string `json:"sequence"` } ) + +// Handle Proof Struct of SSI Docs +type SSIProofInterface interface { + GetProofValue() string + GetType() string + GetVerificationMethod() string +} diff --git a/x/ssi/types/stateless_verification.go b/x/ssi/types/stateless_verification.go deleted file mode 100644 index 1d7df6b..0000000 --- a/x/ssi/types/stateless_verification.go +++ /dev/null @@ -1,58 +0,0 @@ -package types - -import ( - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/hypersign-protocol/hid-node/x/ssi/common" -) - -func didDocumentStatelessVerification(didDoc *Did) error { - didId := didDoc.GetId() - if didId == "" { - return ErrBadRequestIsRequired.Wrap("didDoc Id is required") - } - - // Verification Method of Type EcdsaSecp256k1RecoveryMethod2020 should only have - // `blockchainAccountId` field populated - verificationMethods := didDoc.GetVerificationMethod() - for i := 0; i < len(verificationMethods); i++ { - switch verificationMethods[i].Type { - case common.EcdsaSecp256k1RecoveryMethod2020: - if verificationMethods[i].GetBlockchainAccountId() == "" { - return sdkerrors.Wrapf( - ErrBadRequestInvalidVerMethod, - "blockchainAccountId cannot be empty for verification method %s as it is of type %s", - verificationMethods[i].Id, - verificationMethods[i].Type, - ) - } - if verificationMethods[i].GetPublicKeyMultibase() != "" { - return sdkerrors.Wrapf( - ErrBadRequestInvalidVerMethod, - "publicKeyMultibase should be empty for verification method %s as it is type %s", - verificationMethods[i].Id, - verificationMethods[i].Type, - ) - } - - default: - if verificationMethods[i].GetBlockchainAccountId() != "" { - return sdkerrors.Wrapf( - ErrBadRequestInvalidVerMethod, - "blockchainAccountId should be empty for verification method %s as it is of type %s", - verificationMethods[i].Id, - verificationMethods[i].Type, - ) - } - if verificationMethods[i].GetPublicKeyMultibase() == "" { - return sdkerrors.Wrapf( - ErrBadRequestInvalidVerMethod, - "publicKeyMultibase cannot be empty for verification method %s as it is type %s", - verificationMethods[i].Id, - verificationMethods[i].Type, - ) - } - } - } - - return nil -} diff --git a/x/ssi/types/utils.go b/x/ssi/types/utils.go new file mode 100644 index 0000000..4bddd18 --- /dev/null +++ b/x/ssi/types/utils.go @@ -0,0 +1,48 @@ +package types + +import ( + "strings" +) + +// GetDidFromDidUrl returns didId from didURL. +// TODO: need to handle query and path +func GetElementsFromDidUrl(didUrl string) (string, string) { + didId, fragment, _ := strings.Cut(didUrl, "#") + return didId, fragment +} + +// splitDidUrl returns the elements of a DID URL. It returns the element in order: +// didId, fragment +func SplitDidUrl(didUrl string) (string, string) { + didUrlElements := strings.Split(didUrl, "#") + + didId := didUrlElements[0] + fragment := didUrlElements[1] + + return didId, fragment +} + +// GetUniqueElements returns a list with unique elements +func GetUniqueElements(list []string) []string { + var uniqueList []string + var elementMap map[string]bool = make(map[string]bool) + + for _, element := range list { + if !elementMap[element] { + elementMap[element] = true + uniqueList = append(uniqueList, element) + } + } + + return uniqueList +} + +// FindInSlice checks if an element is present in the list +func FindInSlice(list []string, element string) bool { + for _, x := range list { + if x == element { + return true + } + } + return false +} diff --git a/x/ssi/verification/caip10.go b/x/ssi/verification/caip10.go index 97a4686..7cfc193 100644 --- a/x/ssi/verification/caip10.go +++ b/x/ssi/verification/caip10.go @@ -3,7 +3,7 @@ package verification import ( "strings" - "github.com/hypersign-protocol/hid-node/x/ssi/common" + "github.com/hypersign-protocol/hid-node/x/ssi/types" ) // Extracts the blockchain address from blockchainAccountId @@ -19,8 +19,8 @@ func getCAIP10Chain(blockchainAccountId string) string { userPrefix := strings.Join(segments[0:len(segments)-1], ":") // Ethereum based chain (EIP-155) check - if strings.HasPrefix(userPrefix, common.EIP155) { - return common.EIP155 + if strings.HasPrefix(userPrefix, types.EIP155) { + return types.EIP155 } else { return "" } diff --git a/x/ssi/verification/common_document_checks.go b/x/ssi/verification/common_document_checks.go index f971f69..9f7435e 100644 --- a/x/ssi/verification/common_document_checks.go +++ b/x/ssi/verification/common_document_checks.go @@ -115,12 +115,3 @@ func IsValidID(Id string, namespace string, docType string) error { return nil } - -func HasAtleastOneTrueSigner(s []types.ValidDid) types.ValidDid { - for _, v := range s { - if v.IsValid { - return v - } - } - return types.ValidDid{} -} diff --git a/x/ssi/verification/crypto.go b/x/ssi/verification/crypto.go index c31ee60..bd0a3a2 100644 --- a/x/ssi/verification/crypto.go +++ b/x/ssi/verification/crypto.go @@ -5,7 +5,6 @@ import ( "encoding/base64" "fmt" - "github.com/hypersign-protocol/hid-node/x/ssi/common" "github.com/hypersign-protocol/hid-node/x/ssi/types" "github.com/multiformats/go-multibase" @@ -17,32 +16,67 @@ import ( ethercrypto "github.com/ethereum/go-ethereum/crypto" ) -func verify(verificationMethodType string, verificationKey string, signature string, data []byte) (bool, error) { - switch verificationMethodType { - case common.Ed25519VerificationKey2020: +func verifyAll(extendedVmList []*types.ExtendedVerificationMethod, data []byte) error { + for _, extendedVm := range extendedVmList { + err := verify(extendedVm, data) + if err != nil { + return err + } + } + return nil +} + +func verifyAny(extendedVmList []*types.ExtendedVerificationMethod, data []byte) bool { + found := false + + for _, extendedVm := range extendedVmList { + err := verify(extendedVm, data) + if err == nil { + found = true + break + } + } + + return found +} + +func verify(extendedVm *types.ExtendedVerificationMethod, data []byte) error { + var verificationKey string + var signature string = extendedVm.Signature + + if extendedVm.PublicKeyMultibase != "" { + verificationKey = extendedVm.PublicKeyMultibase + } else if extendedVm.BlockchainAccountId != "" { + verificationKey = extendedVm.BlockchainAccountId + } else { + return fmt.Errorf("either publicKeyMultibase or BlockchainAccountId must be present") + } + + switch extendedVm.Type { + case types.Ed25519VerificationKey2020: return verifyEd25519(verificationKey, signature, data) - case common.EcdsaSecp256k1VerificationKey2019: + case types.EcdsaSecp256k1VerificationKey2019: return verifySecp256k1(verificationKey, signature, data) - case common.EcdsaSecp256k1RecoveryMethod2020: + case types.EcdsaSecp256k1RecoveryMethod2020: chain := getCAIP10Chain(verificationKey) // Check for supported chains switch chain { // Ethereum based chains - case common.EIP155: + case types.EIP155: return recoverEthPublicKey(verificationKey, signature, data) default: - return false, fmt.Errorf("unsupported blockchain address: %s", verificationKey) + return fmt.Errorf("unsupported blockchain address: %s", verificationKey) } default: - return false, fmt.Errorf("unsupported verification method: %s", verificationMethodType) + return fmt.Errorf("unsupported verification method: %s", extendedVm.Type) } } -func verifyEd25519(publicKey string, signature string, documentBytes []byte) (bool, error) { +func verifyEd25519(publicKey string, signature string, documentBytes []byte) error { // Decode Public Key _, publicKeyBytes, err := multibase.Decode(publicKey) if err != nil { - return false, types.ErrInvalidPublicKey.Wrapf( + return types.ErrInvalidPublicKey.Wrapf( "Cannot decode Ed25519 public key %s", publicKey, ) @@ -51,35 +85,39 @@ func verifyEd25519(publicKey string, signature string, documentBytes []byte) (bo // Decode Signatures signatureBytes, err := base64.StdEncoding.DecodeString(signature) if err != nil { - return false, err + return err } - isValidSignature := ed25519.Verify(publicKeyBytes, documentBytes, signatureBytes) - return isValidSignature, nil + if !ed25519.Verify(publicKeyBytes, documentBytes, signatureBytes) { + return fmt.Errorf("signature verification failed") + } else { + return nil + } } -func verifySecp256k1(publicKey string, signature string, documentBytes []byte) (bool, error) { +func verifySecp256k1(publicKey string, signature string, documentBytes []byte) error { // Decode Public Key _, publicKeyBytes, err := multibase.Decode(publicKey) if err != nil { - return false, err + return err } var pubKeyObj secp256k1.PubKey = publicKeyBytes // Decode and Parse Signature signatureBytes, err := base64.StdEncoding.DecodeString(signature) if err != nil { - return false, err + return err } - isValidSignature := pubKeyObj.VerifySignature(documentBytes, signatureBytes) - return isValidSignature, nil + if !pubKeyObj.VerifySignature(documentBytes, signatureBytes) { + return fmt.Errorf("signature verification failed") + } else { + return nil + } } // Support for EIP155 based blockchain address -func recoverEthPublicKey(blockchainAccountId string, signature string, documentBytes []byte) (bool, error) { - var isValidSignature bool - +func recoverEthPublicKey(blockchainAccountId string, signature string, documentBytes []byte) error { // Extract blockchain address from blockchain account id blockchainAddress := getBlockchainAddress(blockchainAccountId) @@ -90,24 +128,27 @@ func recoverEthPublicKey(blockchainAccountId string, signature string, documentB // Decode hex-encoded signature string to bytes signatureBytes, err := etherhexutil.Decode(signature) if err != nil { - return false, err + return err } // Handle the signature recieved from web3-js client package by subtracting 27 from the recovery byte if signatureBytes[ethercrypto.RecoveryIDOffset] == 27 || signatureBytes[ethercrypto.RecoveryIDOffset] == 28 { signatureBytes[ethercrypto.RecoveryIDOffset] -= 27 - } + } // Recover public key from signature recoveredPublicKey, err := ethercrypto.SigToPub(msgHash, signatureBytes) if err != nil { - return false, err + return err } // Convert public key to hex-encoded address recoveredBlockchainAddress := ethercrypto.PubkeyToAddress(*recoveredPublicKey).Hex() // Match the recovered address against user provided address - isValidSignature = recoveredBlockchainAddress == blockchainAddress - return isValidSignature, nil + if recoveredBlockchainAddress != blockchainAddress { + return fmt.Errorf("signature verification failed") + } else { + return nil + } } diff --git a/x/ssi/verification/did_verification.go b/x/ssi/verification/did_verification.go deleted file mode 100644 index a1f0d63..0000000 --- a/x/ssi/verification/did_verification.go +++ /dev/null @@ -1,112 +0,0 @@ -package verification - -import ( - "fmt" - "strings" - - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/hypersign-protocol/hid-node/x/ssi/types" -) - -// Cheks whether the Service is valid -func ValidateServices(services []*types.Service, method string, namespace string) error { - for idx, service := range services { - if !IsValidDidFragment(service.Id, method, namespace) { - return types.ErrInvalidService.Wrapf("Service ID %s is Invalid", service.Id) - } - - if !IsValidDidServiceType(service.Type) { - return types.ErrInvalidService.Wrapf("Service Type %s is Invalid", service.Type) - } - - if DuplicateServiceExists(service.Id, services[idx+1:]) { - return types.ErrInvalidService.Wrapf("Service with Id: %s is duplicate", service.Id) - } - } - return nil -} - -// Check for valid DID fragment -func IsValidDidFragment(didUrl string, method string, namespace string) bool { - if !strings.Contains(didUrl, "#") { - return false - } - - did, _ := splitDidUrlIntoDid(didUrl) - err := IsValidID(did, namespace, "didDocument") - return err == nil -} - -// Check Valid DID service type -func IsValidDidServiceType(sType string) bool { - for _, val := range ServiceTypes { - if val == sType { - return true - } - } - return false -} - -func DuplicateServiceExists(serviceId string, services []*types.Service) bool { - _, fragment := splitDidUrlIntoDid(serviceId) - for _, s := range services { - _, sFragment := splitDidUrlIntoDid(s.Id) - if fragment == sFragment { - return true - } - } - return false -} - -// Checks whether the DidDoc string is valid -func ValidateDidDocument(didDoc *types.Did, genesisNamespace string) error { - didArrayMap := map[string][]string{ - "authentication": didDoc.GetAuthentication(), - "assertionMethod": didDoc.GetAssertionMethod(), - "keyAgreement": didDoc.GetKeyAgreement(), - "capabilityInvocation": didDoc.GetCapabilityInvocation(), - "capabilityDelegation": didDoc.GetCapabilityDelegation(), - } - - nonEmptyFields := map[string]string{ - "id": didDoc.GetId(), - } - - // Format Check for Did Id - err := IsValidID(didDoc.GetId(), genesisNamespace, "didDocument") - if err != nil { - return err - } - - // Verification Method Relationships Check - for field, didArray := range didArrayMap { - for _, elem := range didArray { - if !IsValidDidFragment(elem, DidMethod, genesisNamespace) { - return sdkerrors.Wrap(types.ErrInvalidDidDoc, fmt.Sprintf("The field %s is an invalid DID Array", field)) - } - } - } - - // Empty Field check - for field, value := range nonEmptyFields { - if value == "" { - return sdkerrors.Wrap(types.ErrInvalidDidDoc, fmt.Sprintf("The field %s must have a value", field)) - } - } - - // Valid Services Check - err = ValidateServices(didDoc.GetService(), DidMethod, genesisNamespace) - if err != nil { - return err - } - - return nil -} - -// Check the Deactivate status of DID -func VerifyDidDeactivate(metadata *types.Metadata, id string) error { - if metadata.GetDeactivated() { - return sdkerrors.Wrap(types.ErrDidDocDeactivated, fmt.Sprintf("DidDoc ID: %s", id)) - } - return nil -} diff --git a/x/ssi/verification/signature_verification.go b/x/ssi/verification/signature_verification.go index 6842b64..5d43a49 100644 --- a/x/ssi/verification/signature_verification.go +++ b/x/ssi/verification/signature_verification.go @@ -3,117 +3,50 @@ package verification import ( "fmt" - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/hypersign-protocol/hid-node/x/ssi/common" "github.com/hypersign-protocol/hid-node/x/ssi/types" ) -// Verify signatures against signer's public keys -// If atleast one of the signatures is valid, return true -func VerifyIdentitySignature(signer types.Signer, signatures []*types.SignInfo, signingInput []byte) (bool, error) { - result := false - - for _, signature := range signatures { - did, _ := splitDidUrlIntoDid(signature.VerificationMethodId) - if did == signer.Signer { - vmElements, err := findVerificationMethodElements(signer, signature.VerificationMethodId) - if err != nil { - return false, err - } - - var verificationKey string - if vmElements.PublicKey != "" { - verificationKey = vmElements.PublicKey - } - if vmElements.BlockchainAccountId != "" { - verificationKey = vmElements.BlockchainAccountId - } - - vmType := vmElements.VerificationMethodType - - result, err = verify(vmType, verificationKey, signature.Signature, signingInput) - if err != nil { - return false, err - } +// VerifySignatureOfEveryController verifies every required verification method of every controller +func VerifySignatureOfEveryController( + didDoc []byte, VmMap map[string][]*types.ExtendedVerificationMethod, +) error { + for controller, vmList := range VmMap { + if len(vmList) == 0 { + return fmt.Errorf("require atleast one valid signature for controller %s", controller) } - } - - return result, nil -} - -func VerifyDidSignature(ctx *sdk.Context, didDocBytes []byte, signers []types.Signer, signatures []*types.SignInfo) error { - var validArr []types.ValidDid - - if len(signers) == 0 { - return types.ErrInvalidSignature.Wrap("At least one signer should be present") - } - - if len(signatures) == 0 { - return types.ErrInvalidSignature.Wrap("At least one signature should be present") - } - - for _, signer := range signers { - valid, err := VerifyIdentitySignature(signer, signatures, didDocBytes) + err := verifyAll(vmList, didDoc) if err != nil { - return sdkerrors.Wrap(types.ErrInvalidSignature, err.Error()) + return fmt.Errorf("need every signature for controller %s to be valid", controller) } - validArr = append(validArr, types.ValidDid{DidId: signer.Signer, IsValid: valid}) } - - validDid := HasAtleastOneTrueSigner(validArr) - - if validDid == (types.ValidDid{}) { - return sdkerrors.Wrap(types.ErrInvalidSignature, validDid.DidId) - } - return nil } -func DocumentProofTypeCheck(inputProofType string, signers []types.Signer, vmId string) error { - var vmType string - var expectedProofType string - - for i := 0; i < len(signers); i++ { - if signers[i].VerificationMethod[0].Id == vmId { - vmType = signers[i].VerificationMethod[0].Type +// VerifySignatureOfEveryController verifies any required0 verification8 +func VerifySignatureOfAnyController( + didDoc []byte, VmMap map[string][]*types.ExtendedVerificationMethod, +) error { + found := false + for _, vmList := range VmMap { + found = verifyAny(vmList, didDoc) + if found { break } } - if vmType == "" { - return types.ErrVerificationMethodNotFound.Wrap(vmId) - } - - expectedProofType = common.VerificationKeySignatureMap[vmType] - if inputProofType != expectedProofType { + if !found { return fmt.Errorf( - "expected document proof type for verification method type %s to be '%s', recieved '%s'", - vmType, - expectedProofType, - inputProofType, - ) + "need atleast one valid signature from any of the existing controllers in the registered didDoc") } + return nil } -// Verify Signature for Credential Schema and Credential Status Documents -func VerifyDocumentSignature(ctx *sdk.Context, docBytes []byte, signers []types.Signer, signatures []*types.SignInfo) error { - var validArr []types.ValidDid - - for _, signer := range signers { - valid, err := VerifyIdentitySignature(signer, signatures, docBytes) - if err != nil { - return sdkerrors.Wrap(types.ErrInvalidSignature, err.Error()) - } - validArr = append(validArr, types.ValidDid{DidId: signer.Signer, IsValid: valid}) - } - - validDid := HasAtleastOneTrueSigner(validArr) - - if validDid == (types.ValidDid{}) { - return sdkerrors.Wrap(types.ErrInvalidSignature, validDid.DidId) +// VerifyDocumentProofSignature verfies the proof of the SSI Document such as Credential Schema and Credential Status +func VerifyDocumentProofSignature(singedData []byte, vm *types.VerificationMethod, singature string) error { + vmExtended := types.CreateExtendedVerificationMethod(vm, singature) + if err := verify(vmExtended, singedData); err != nil { + return err } - return nil } diff --git a/x/ssi/verification/utils.go b/x/ssi/verification/utils.go deleted file mode 100644 index bf1336e..0000000 --- a/x/ssi/verification/utils.go +++ /dev/null @@ -1,91 +0,0 @@ -package verification - -import ( - "strings" - - "github.com/hypersign-protocol/hid-node/x/ssi/types" - "github.com/multiformats/go-multibase" -) - -type verificationMethodElements struct { - PublicKey string - VerificationMethodType string - BlockchainAccountId string -} - -func FindVerificationMethod(vms []*types.VerificationMethod, id string) *types.VerificationMethod { - for _, vm := range vms { - if vm.Id == id { - return vm - } - } - return nil -} - -func findVerificationMethodElements(signer types.Signer, vmId string) (verificationMethodElements, error) { - if signer.Authentication != nil { - for _, authentication := range signer.Authentication { - if authentication == vmId { - vm := FindVerificationMethod(signer.VerificationMethod, vmId) - if vm == nil { - return verificationMethodElements{}, types.ErrVerificationMethodNotFound.Wrap(vmId) - } - return getVerificationMethodElements(vm) - } - } - } - - if signer.AssertionMethod != nil { - for _, assertionMethod := range signer.AssertionMethod { - if assertionMethod == vmId { - vm := FindVerificationMethod(signer.VerificationMethod, vmId) - if vm == nil { - return verificationMethodElements{}, types.ErrVerificationMethodNotFound.Wrap(vmId) - } - return getVerificationMethodElements(vm) - } - } - } - return verificationMethodElements{}, types.ErrVerificationMethodNotFound.Wrap(vmId) -} - -func splitDidUrlIntoDid(didUrl string) (string, string) { - segments := strings.Split(didUrl, "#") - return segments[0], segments[1] -} - -func getVerificationMethodElements(v *types.VerificationMethod) (verificationMethodElements, error) { - var publicKey string = v.PublicKeyMultibase - var verificationMethodType string = v.Type - var blockchainAccountId string = v.BlockchainAccountId - - var vmElements verificationMethodElements - vmElements.VerificationMethodType = verificationMethodType - - // Check if the public key is decoded successfully - if publicKey != "" { - if publicKey[0] != 'z' { - return verificationMethodElements{}, types.ErrInvalidPublicKey.Wrapf( - "public key is expected to be in multibase base58btc encoding, recieved public key: %s", - publicKey, - ) - } - - _, _, err := multibase.Decode(publicKey) - if err != nil { - return verificationMethodElements{}, types.ErrInvalidPublicKey.Wrapf( - "multibase decoding failed, recieved public key: %s", - publicKey, - ) - } - - vmElements.PublicKey = publicKey - } - - // Check if the blockchainAccountId is of supported type - if blockchainAccountId != "" { - vmElements.BlockchainAccountId = blockchainAccountId - } - - return vmElements, nil -} From be2da81892e69e7bd8799d41cb75402affe4424a Mon Sep 17 00:00:00 2001 From: arnabghose997 Date: Thu, 2 Mar 2023 10:51:49 +0530 Subject: [PATCH 2/3] refactor: every error messages thrown from RPC will carry a status code --- x/ssi/keeper/client_spec.go | 10 ++-- x/ssi/keeper/credential.go | 20 ++++--- x/ssi/keeper/did.go | 6 +-- x/ssi/keeper/grpc_query_credential.go | 4 +- x/ssi/keeper/msg_server.go | 3 +- x/ssi/keeper/msg_server_create_did.go | 18 +++---- x/ssi/keeper/msg_server_credential.go | 30 +++++------ x/ssi/keeper/msg_server_deactivate_did.go | 10 ++-- x/ssi/keeper/msg_server_schema.go | 4 +- x/ssi/keeper/msg_server_update_did.go | 22 ++++---- x/ssi/keeper/namespace.go | 16 +++--- x/ssi/types/diddoc_validation.go | 50 +++++++----------- x/ssi/types/errors.go | 41 +++++---------- x/ssi/verification/common_document_checks.go | 12 ++--- x/ssi/verification/credential_verification.go | 52 +++++++------------ x/ssi/verification/crypto.go | 10 ++-- 16 files changed, 129 insertions(+), 179 deletions(-) diff --git a/x/ssi/keeper/client_spec.go b/x/ssi/keeper/client_spec.go index f17096a..b78233c 100644 --- a/x/ssi/keeper/client_spec.go +++ b/x/ssi/keeper/client_spec.go @@ -6,7 +6,6 @@ import ( "fmt" "strings" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/hypersign-protocol/hid-node/x/ssi/types" ) @@ -64,12 +63,9 @@ func getClientSpecDocBytes(clientSpecOpts types.ClientSpecOpts) ([]byte, error) case "": return clientSpecOpts.SSIDoc.GetSignBytes(), nil default: - return nil, sdkerrors.Wrap( - types.ErrInvalidClientSpecType, - fmt.Sprintf( - "supported client specs are : [%s]", - strings.Join(types.SupportedClientSpecs, ", "), - ), + return nil, fmt.Errorf( + "supported client specs are : [%s]", + strings.Join(types.SupportedClientSpecs, ", "), ) } } diff --git a/x/ssi/keeper/credential.go b/x/ssi/keeper/credential.go index db10966..bdda5fe 100644 --- a/x/ssi/keeper/credential.go +++ b/x/ssi/keeper/credential.go @@ -2,33 +2,37 @@ package keeper import ( "encoding/binary" + "fmt" "time" "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/hypersign-protocol/hid-node/x/ssi/types" ) -func (k Keeper) RegisterCred(ctx sdk.Context, cred *types.Credential) uint64 { - count := k.GetCredentialCount(ctx) +func (k Keeper) RegisterCredentialStatusInState(ctx sdk.Context, cred *types.Credential) uint64 { + count := k.GetCredentialStatusCount(ctx) store := prefix.NewStore(ctx.KVStore(k.storeKey), []byte(types.CredKey)) id := cred.GetClaim().GetId() credBytes := k.cdc.MustMarshal(cred) store.Set([]byte(id), credBytes) - k.SetCredentialCount(ctx, count+1) + k.SetCredentialStatusCount(ctx, count+1) return count } -func (k Keeper) GetCredential(ctx *sdk.Context, id string) (*types.Credential, error) { +func (k Keeper) GetCredentialStatusFromState(ctx *sdk.Context, id string) (*types.Credential, error) { store := prefix.NewStore(ctx.KVStore(k.storeKey), []byte(types.CredKey)) var cred types.Credential var bytes = store.Get([]byte(id)) + if len(bytes) == 0 { + return nil, fmt.Errorf("credential status document %s not found", id) + } + if err := k.cdc.Unmarshal(bytes, &cred); err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidType, err.Error()) + return nil, fmt.Errorf("internal: unable to unmarshal credentialStatus id %s from state", id) } return &cred, nil @@ -40,7 +44,7 @@ func (k Keeper) HasCredential(ctx sdk.Context, id string) bool { return store.Has([]byte(id)) } -func (k Keeper) GetCredentialCount(ctx sdk.Context) uint64 { +func (k Keeper) GetCredentialStatusCount(ctx sdk.Context) uint64 { store := prefix.NewStore(ctx.KVStore(k.storeKey), []byte(types.CredCountKey)) byteKey := []byte(types.CredCountKey) bz := store.Get(byteKey) @@ -50,7 +54,7 @@ func (k Keeper) GetCredentialCount(ctx sdk.Context) uint64 { return binary.BigEndian.Uint64(bz) } -func (k Keeper) SetCredentialCount(ctx sdk.Context, count uint64) { +func (k Keeper) SetCredentialStatusCount(ctx sdk.Context, count uint64) { store := prefix.NewStore(ctx.KVStore(k.storeKey), []byte(types.CredCountKey)) byteKey := []byte(types.CredCountKey) bz := make([]byte, 8) diff --git a/x/ssi/keeper/did.go b/x/ssi/keeper/did.go index fbde6ad..b47ccb3 100644 --- a/x/ssi/keeper/did.go +++ b/x/ssi/keeper/did.go @@ -2,10 +2,10 @@ package keeper import ( "encoding/binary" + "fmt" "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/hypersign-protocol/hid-node/utils" "github.com/hypersign-protocol/hid-node/x/ssi/types" ) @@ -50,11 +50,11 @@ func (k Keeper) GetDidDocumentState(ctx *sdk.Context, id string) (*types.DidDocu var bytes = store.Get([]byte(id)) if len(bytes) == 0 { - return nil, sdkerrors.Wrap(types.ErrDidDocNotFound, id) + return nil, fmt.Errorf("DID Document %s not found", id) } if err := k.cdc.Unmarshal(bytes, &didDocState); err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidType, err.Error()) + return nil, fmt.Errorf("internal: unable to unmarshal didDocBytes of id %s", id) } return &didDocState, nil diff --git a/x/ssi/keeper/grpc_query_credential.go b/x/ssi/keeper/grpc_query_credential.go index d07831c..3fac0e5 100644 --- a/x/ssi/keeper/grpc_query_credential.go +++ b/x/ssi/keeper/grpc_query_credential.go @@ -17,7 +17,7 @@ func (k Keeper) QueryCredential(goCtx context.Context, req *types.QueryCredentia } ctx := sdk.UnwrapSDKContext(goCtx) - cred, err := k.GetCredential(&ctx, req.CredId) + cred, err := k.GetCredentialStatusFromState(&ctx, req.CredId) if err != nil { return nil, err } @@ -51,5 +51,5 @@ func (k Keeper) QueryCredentials(goCtx context.Context, req *types.QueryCredenti if err != nil { return nil, status.Error(codes.Internal, err.Error()) } - return &types.QueryCredentialsResponse{Credentials: credentials, TotalCount: k.GetCredentialCount(ctx)}, nil + return &types.QueryCredentialsResponse{Credentials: credentials, TotalCount: k.GetCredentialStatusCount(ctx)}, nil } diff --git a/x/ssi/keeper/msg_server.go b/x/ssi/keeper/msg_server.go index e28b7fd..cdb228c 100644 --- a/x/ssi/keeper/msg_server.go +++ b/x/ssi/keeper/msg_server.go @@ -4,7 +4,6 @@ import ( "fmt" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/hypersign-protocol/hid-node/x/ssi/types" "github.com/hypersign-protocol/hid-node/x/ssi/verification" ) @@ -39,7 +38,7 @@ func (k msgServer) checkControllerPresenceInState( // Check if they are deactivated if didDoc.DidDocumentMetadata.Deactivated { - return sdkerrors.Wrapf(types.ErrDidDocDeactivated, "%s", didDoc.DidDocument.Id) + return fmt.Errorf("DID document %s is deactivated", didDoc.DidDocument.Id) } } return nil diff --git a/x/ssi/keeper/msg_server_create_did.go b/x/ssi/keeper/msg_server_create_did.go index 29f8b44..3bb1373 100644 --- a/x/ssi/keeper/msg_server_create_did.go +++ b/x/ssi/keeper/msg_server_create_did.go @@ -21,12 +21,12 @@ func (k msgServer) CreateDID(goCtx context.Context, msg *types.MsgCreateDID) (*t // Validate DID Document if err := msgDidDocument.ValidateDidDocument(); err != nil { - return nil, err + return nil, sdkerrors.Wrap(types.ErrInvalidDidDoc, err.Error()) } // Validate namespace in DID Document - if err := didNamespaceValidation(k, ctx, msgDidDocument); err != nil { - return nil, err + if err := didDocNamespaceValidation(k, ctx, msgDidDocument); err != nil { + return nil, sdkerrors.Wrap(types.ErrInvalidDidDoc, err.Error()) } // Checks if the Did Document is already registered @@ -38,13 +38,13 @@ func (k msgServer) CreateDID(goCtx context.Context, msg *types.MsgCreateDID) (*t controllerList := getControllersForCreateDID(msgDidDocument) if err := k.checkControllerPresenceInState(ctx, controllerList, msgDidDocument.Id); err != nil { - return nil, err + return nil, sdkerrors.Wrap(types.ErrInvalidDidDoc, err.Error()) } // Collect necessary Verification Methods which are needed to be valid requiredVMs, err := getVerificationMethodsForCreateDID(msgDidDocument) if err != nil { - return nil, err + return nil, sdkerrors.Wrap(types.ErrVerificationMethodNotFound, err.Error()) } // Associate Signatures @@ -52,7 +52,7 @@ func (k msgServer) CreateDID(goCtx context.Context, msg *types.MsgCreateDID) (*t requiredVmMap, err := k.formMustControllerVmListMap(ctx, controllerList, requiredVMs, signMap) if err != nil { - return nil, err + return nil, sdkerrors.Wrap(types.ErrInvalidDidDoc, err.Error()) } // ClientSpec check @@ -64,13 +64,13 @@ func (k msgServer) CreateDID(goCtx context.Context, msg *types.MsgCreateDID) (*t var didDocBytes []byte didDocBytes, err = getClientSpecDocBytes(clientSpecOpts) if err != nil { - return nil, err + return nil, sdkerrors.Wrapf(types.ErrInvalidClientSpecType, err.Error()) } // Verify Signatures err = verification.VerifySignatureOfEveryController(didDocBytes, requiredVmMap) if err != nil { - return nil, err + return nil, sdkerrors.Wrapf(types.ErrInvalidSignature, err.Error()) } // Formt DID Document Metadata @@ -122,7 +122,7 @@ func getVerificationMethodsForCreateDID(didDocument *types.Did) ([]*types.Verifi if !foundAtleastOneSubjectVM && types.FindInSlice(didDocument.Controller, didDocument.Id) { return nil, fmt.Errorf( - "there should be atleast one verification method from subject DID controller %v", didDocument.Id) + "there should be atleast one verification method of DID Subject %v", didDocument.Id) } return mustHaveVerificaitonMethods, nil diff --git a/x/ssi/keeper/msg_server_credential.go b/x/ssi/keeper/msg_server_credential.go index b8da5a4..6819f7b 100644 --- a/x/ssi/keeper/msg_server_credential.go +++ b/x/ssi/keeper/msg_server_credential.go @@ -26,7 +26,7 @@ func (k msgServer) RegisterCredentialStatus(goCtx context.Context, msg *types.Ms // Check the format of Credential ID err := verification.IsValidID(credId, chainNamespace, "credDocument") if err != nil { - return nil, err + return nil, sdkerrors.Wrap(types.ErrInvalidSchemaID, err.Error()) } // Check if the credential already exist in the store @@ -46,7 +46,7 @@ func (k msgServer) RegisterCredentialStatus(goCtx context.Context, msg *types.Ms // Check if issuer's DID is deactivated issuerDidDocument, err := k.GetDidDocumentState(&ctx, issuerId) if err != nil { - return nil, err + return nil, sdkerrors.Wrap(types.ErrInvalidDidDoc, err.Error()) } if issuerDidDocument.DidDocumentMetadata.Deactivated { return nil, sdkerrors.Wrap(types.ErrDidDocDeactivated, fmt.Sprintf("%s is deactivated and cannot used be used to register credential status", issuerDidDocument.DidDocument.Id)) @@ -66,12 +66,12 @@ func (k msgServer) RegisterCredentialStatus(goCtx context.Context, msg *types.Ms } if err := verification.VerifyCredentialStatusDates(issuanceDateParsed, expirationDateParsed); err != nil { - return nil, err + return nil, sdkerrors.Wrapf(types.ErrInvalidCredentialField, err.Error()) } // Check if updated date is similar to created date if err := verification.VerifyCredentialProofDates(msgCredProof, true); err != nil { - return nil, err + return nil, sdkerrors.Wrapf(types.ErrInvalidCredentialField, err.Error()) } // Check if the created date lies between issuance and expiration @@ -95,13 +95,13 @@ func (k msgServer) RegisterCredentialStatus(goCtx context.Context, msg *types.Ms credDocBytes, err := getClientSpecDocBytes(clientSpecOpts) if err != nil { - return nil, err + return nil, sdkerrors.Wrapf(types.ErrInvalidClientSpecType, err.Error()) } // Verify Signature err = k.VerifyDocumentProof(ctx, credDocBytes, msgCredProof) if err != nil { - return nil, err + return nil, sdkerrors.Wrapf(types.ErrInvalidSignature, err.Error()) } cred := &types.Credential{ @@ -113,14 +113,14 @@ func (k msgServer) RegisterCredentialStatus(goCtx context.Context, msg *types.Ms Proof: msgCredProof, } - id = k.RegisterCred(ctx, cred) + id = k.RegisterCredentialStatusInState(ctx, cred) } else { cred, err := k.updateCredentialStatus(ctx, msg) if err != nil { return nil, err } - id = k.RegisterCred(ctx, cred) + id = k.RegisterCredentialStatusInState(ctx, cred) } return &types.MsgRegisterCredentialStatusResponse{Id: id}, nil @@ -133,7 +133,7 @@ func (k msgServer) updateCredentialStatus(ctx sdk.Context, msg *types.MsgRegiste credId := msgNewCredStatus.GetClaim().GetId() // Get Credential from store - oldCredStatus, err := k.GetCredential(&ctx, credId) + oldCredStatus, err := k.GetCredentialStatusFromState(&ctx, credId) if err != nil { return nil, err } @@ -147,7 +147,7 @@ func (k msgServer) updateCredentialStatus(ctx sdk.Context, msg *types.MsgRegiste // Check if issuer's DID is deactivated issuerDidDocument, err := k.GetDidDocumentState(&ctx, issuerId) if err != nil { - return nil, err + return nil, sdkerrors.Wrap(types.ErrDidDocNotFound, err.Error()) } if issuerDidDocument.DidDocumentMetadata.Deactivated { return nil, sdkerrors.Wrap(types.ErrDidDocDeactivated, fmt.Sprintf("%s is deactivated and cannot used be used to register credential status", issuerDidDocument.DidDocument.Id)) @@ -156,7 +156,7 @@ func (k msgServer) updateCredentialStatus(ctx sdk.Context, msg *types.MsgRegiste // Check if the provided isser Id is the one who issued the VC if issuerId != oldCredStatus.GetIssuer() { return nil, sdkerrors.Wrapf(types.ErrInvalidCredentialField, - fmt.Sprintf("Issuer ID %s is not issuer of verifiable credential id %s", issuerId, credId)) + fmt.Sprintf("issuer id %s is not issuer of verifiable credential id %s", issuerId, credId)) } // Check if the new expiration date and issuance date are same as old one. @@ -192,12 +192,12 @@ func (k msgServer) updateCredentialStatus(ctx sdk.Context, msg *types.MsgRegiste // Check if new expiration date isn't less than new issuance date if err := verification.VerifyCredentialStatusDates(newIssuanceDateParsed, newExpirationDateParsed); err != nil { - return nil, err + return nil, sdkerrors.Wrap(types.ErrInvalidCredentialField, err.Error()) } // Check if updated date iss imilar to created date if err := verification.VerifyCredentialProofDates(msgNewCredProof, false); err != nil { - return nil, err + return nil, sdkerrors.Wrap(types.ErrInvalidCredentialField, err.Error()) } // Check if the created date lies between issuance and expiration @@ -286,13 +286,13 @@ func (k msgServer) updateCredentialStatus(ctx sdk.Context, msg *types.MsgRegiste credDocBytes, err := getClientSpecDocBytes(clientSpecOpts) if err != nil { - return nil, err + return nil, sdkerrors.Wrap(types.ErrInvalidClientSpecType, err.Error()) } // Verify Signature err = k.VerifyDocumentProof(ctx, credDocBytes, msgNewCredProof) if err != nil { - return nil, err + return nil, sdkerrors.Wrap(types.ErrInvalidSignature, err.Error()) } cred := types.Credential{ diff --git a/x/ssi/keeper/msg_server_deactivate_did.go b/x/ssi/keeper/msg_server_deactivate_did.go index 9a433bd..9e7cc17 100644 --- a/x/ssi/keeper/msg_server_deactivate_did.go +++ b/x/ssi/keeper/msg_server_deactivate_did.go @@ -28,7 +28,7 @@ func (k msgServer) DeactivateDID(goCtx context.Context, msg *types.MsgDeactivate // Get the DID Document from state didDocumentState, err := k.GetDidDocumentState(&ctx, msgDidId) if err != nil { - return nil, err + return nil, sdkerrors.Wrap(types.ErrDidDocNotFound, err.Error()) } didDocument := didDocumentState.DidDocument didDocumentMetadata := didDocumentState.DidDocumentMetadata @@ -51,7 +51,7 @@ func (k msgServer) DeactivateDID(goCtx context.Context, msg *types.MsgDeactivate // Gather controllers controllers := getControllersForDeactivateDID(didDocument) if err := k.checkControllerPresenceInState(ctx, controllers, didDocument.Id); err != nil { - return nil, err + return nil, sdkerrors.Wrap(types.ErrInvalidDidDoc, err.Error()) } signMap := makeSignatureMap(msgSignatures) @@ -60,7 +60,7 @@ func (k msgServer) DeactivateDID(goCtx context.Context, msg *types.MsgDeactivate controllerMap, err := k.formAnyControllerVmListMap(ctx, controllers, didDocument.VerificationMethod, signMap) if err != nil { - return nil, err + return nil, sdkerrors.Wrap(types.ErrInvalidDidDoc, err.Error()) } // Get Client Spec @@ -72,13 +72,13 @@ func (k msgServer) DeactivateDID(goCtx context.Context, msg *types.MsgDeactivate var didDocBytes []byte didDocBytes, err = getClientSpecDocBytes(clientSpecOpts) if err != nil { - return nil, err + return nil, sdkerrors.Wrapf(types.ErrInvalidClientSpecType, err.Error()) } // Signature Verification err = verification.VerifySignatureOfAnyController(didDocBytes, controllerMap) if err != nil { - return nil, err + return nil, sdkerrors.Wrapf(types.ErrInvalidSignature, err.Error()) } // Create updated metadata diff --git a/x/ssi/keeper/msg_server_schema.go b/x/ssi/keeper/msg_server_schema.go index 7907b63..6ed7fe3 100644 --- a/x/ssi/keeper/msg_server_schema.go +++ b/x/ssi/keeper/msg_server_schema.go @@ -71,12 +71,12 @@ func (k msgServer) CreateSchema(goCtx context.Context, msg *types.MsgCreateSchem schemaDocBytes, err := getClientSpecDocBytes(clientSpecOpts) if err != nil { - return nil, err + return nil, sdkerrors.Wrap(types.ErrInvalidClientSpecType, err.Error()) } // Signature check if err := k.VerifyDocumentProof(ctx, schemaDocBytes, schemaProof); err != nil { - return nil, err + return nil, sdkerrors.Wrap(types.ErrInvalidClientSpecType, err.Error()) } var schema = types.Schema{ diff --git a/x/ssi/keeper/msg_server_update_did.go b/x/ssi/keeper/msg_server_update_did.go index a626f81..9328198 100644 --- a/x/ssi/keeper/msg_server_update_did.go +++ b/x/ssi/keeper/msg_server_update_did.go @@ -22,12 +22,12 @@ func (k msgServer) UpdateDID(goCtx context.Context, msg *types.MsgUpdateDID) (*t // Validate DID Document if err := msgDidDocument.ValidateDidDocument(); err != nil { - return nil, err + return nil, sdkerrors.Wrap(types.ErrInvalidDidDoc, err.Error()) } // Validate namespace in DID Document - if err := didNamespaceValidation(k, ctx, msgDidDocument); err != nil { - return nil, err + if err := didDocNamespaceValidation(k, ctx, msgDidDocument); err != nil { + return nil, sdkerrors.Wrap(types.ErrInvalidDidDoc, err.Error()) } // Checks if the Did Document is already registered @@ -38,7 +38,7 @@ func (k msgServer) UpdateDID(goCtx context.Context, msg *types.MsgUpdateDID) (*t // Fetch registered Did Document from state existingDidDocumentState, err := k.GetDidDocumentState(&ctx, msgDidDocument.Id) if err != nil { - return nil, err + return nil, sdkerrors.Wrap(types.ErrDidDocNotFound, err.Error()) } existingDidDocument := existingDidDocumentState.DidDocument @@ -60,10 +60,10 @@ func (k msgServer) UpdateDID(goCtx context.Context, msg *types.MsgUpdateDID) (*t // Look for any change in Controller array mandatoryControllers, anyControllers := getControllersForUpdateDID(existingDidDocument, msgDidDocument) if err := k.checkControllerPresenceInState(ctx, mandatoryControllers, msgDidDocument.Id); err != nil { - return nil, err + return nil, sdkerrors.Wrap(types.ErrInvalidDidDoc, err.Error()) } if err := k.checkControllerPresenceInState(ctx, anyControllers, msgDidDocument.Id); err != nil { - return nil, err + return nil, sdkerrors.Wrap(types.ErrInvalidDidDoc, err.Error()) } // Gather Verification Methods @@ -73,13 +73,13 @@ func (k msgServer) UpdateDID(goCtx context.Context, msg *types.MsgUpdateDID) (*t requiredVmMap, err := k.formMustControllerVmListMap(ctx, mandatoryControllers, updatedVms, signMap) if err != nil { - return nil, err + return nil, sdkerrors.Wrap(types.ErrInvalidDidDoc, err.Error()) } optionalVmMap, err := k.formAnyControllerVmListMap(ctx, anyControllers, existingDidDocument.VerificationMethod, signMap) if err != nil { - return nil, err + return nil, sdkerrors.Wrap(types.ErrInvalidDidDoc, err.Error()) } // ClientSpec Opts @@ -91,16 +91,16 @@ func (k msgServer) UpdateDID(goCtx context.Context, msg *types.MsgUpdateDID) (*t var didDocBytes []byte didDocBytes, err = getClientSpecDocBytes(clientSpecOpts) if err != nil { - return nil, err + return nil, sdkerrors.Wrapf(types.ErrInvalidClientSpecType, err.Error()) } // Signature Verification if err := verification.VerifySignatureOfEveryController(didDocBytes, requiredVmMap); err != nil { - return nil, err + return nil, sdkerrors.Wrapf(types.ErrInvalidSignature, err.Error()) } if err := verification.VerifySignatureOfAnyController(didDocBytes, optionalVmMap); err != nil { - return nil, err + return nil, sdkerrors.Wrapf(types.ErrInvalidSignature, err.Error()) } // Create the Metadata and assign `created` and `deactivated` to previous DIDDoc's metadata values diff --git a/x/ssi/keeper/namespace.go b/x/ssi/keeper/namespace.go index df9d226..ad6e8e2 100644 --- a/x/ssi/keeper/namespace.go +++ b/x/ssi/keeper/namespace.go @@ -8,8 +8,8 @@ import ( "github.com/hypersign-protocol/hid-node/x/ssi/types" ) -// namespaceValidation validates the namespace in document id -func namespaceValidation(k msgServer, ctx sdk.Context, docId string) error { +// didNamespaceValidation validates the namespace in DID Document Id +func didNamespaceValidation(k msgServer, ctx sdk.Context, docId string) error { genesisNamespace := k.GetChainNamespace(&ctx) docIdElements := strings.Split(docId, ":") @@ -35,16 +35,16 @@ func namespaceValidation(k msgServer, ctx sdk.Context, docId string) error { return nil } -// didNamespaceValidation validates the namespace in Did Document Id -func didNamespaceValidation(k msgServer, ctx sdk.Context, didDoc *types.Did) error { +// didDocNamespaceValidation validates the namespace in Did Document +func didDocNamespaceValidation(k msgServer, ctx sdk.Context, didDoc *types.Did) error { // Subject ID check - if err := namespaceValidation(k, ctx, didDoc.Id); err != nil { + if err := didNamespaceValidation(k, ctx, didDoc.Id); err != nil { return err } // Controllers check for _, controller := range didDoc.Controller { - if err := namespaceValidation(k, ctx, controller); err != nil { + if err := didNamespaceValidation(k, ctx, controller); err != nil { return err } } @@ -52,7 +52,7 @@ func didNamespaceValidation(k msgServer, ctx sdk.Context, didDoc *types.Did) err // Verification Method ID checks for _, vm := range didDoc.VerificationMethod { didId, _ := types.GetElementsFromDidUrl(vm.Id) - if err := namespaceValidation(k, ctx, didId); err != nil { + if err := didNamespaceValidation(k, ctx, didId); err != nil { return err } } @@ -69,7 +69,7 @@ func didNamespaceValidation(k msgServer, ctx sdk.Context, didDoc *types.Did) err for _, vmRelationship := range vmRelationshipList { // didUrl check and presence in verification methods for _, id := range vmRelationship { - if err := namespaceValidation(k, ctx, id); err != nil { + if err := didNamespaceValidation(k, ctx, id); err != nil { return err } } diff --git a/x/ssi/types/diddoc_validation.go b/x/ssi/types/diddoc_validation.go index 144e33e..a6dba62 100644 --- a/x/ssi/types/diddoc_validation.go +++ b/x/ssi/types/diddoc_validation.go @@ -4,8 +4,6 @@ import ( "fmt" "regexp" "strings" - - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) // isValidDidDoc checks if the DID Id is valid @@ -13,8 +11,7 @@ func isValidDidDocId(id string) error { // check the number of elements in DID Document idElements := strings.Split(id, ":") if !(len(idElements) == 3 || len(idElements) == 4) { - return sdkerrors.Wrapf( - ErrInvalidDidDoc, + return fmt.Errorf( "number of elements in DID Id %s should be either 3 or 4", id, ) @@ -22,8 +19,7 @@ func isValidDidDocId(id string) error { // check if the first element is valid document identifier if idElements[0] != DocumentIdentifierDid { - return sdkerrors.Wrapf( - ErrInvalidDidDoc, + return fmt.Errorf( "document identifier should be %s", DocumentIdentifierDid, ) @@ -31,8 +27,7 @@ func isValidDidDocId(id string) error { // check if the second element is the correct DID method if idElements[1] != DidMethod { - return sdkerrors.Wrapf( - ErrInvalidDidDoc, + return fmt.Errorf( "DID method should be %s", DidMethod, ) @@ -49,12 +44,9 @@ func isValidDidDocId(id string) error { return fmt.Errorf("error in parsing regular expression for method-specific-id: %s", err.Error()) } if !isProperMethodSpecificId { - return sdkerrors.Wrap( - ErrInvalidMethodSpecificId, - fmt.Sprintf( - "method-specific-id should be an alphanumeric string with minimum of 32 characters, received: %s", - methodSpecificId, - ), + return fmt.Errorf( + "method-specific-id should be an alphanumeric string with minimum of 32 characters, received: %s", + methodSpecificId, ) } @@ -69,8 +61,7 @@ func isDidUrl(id string) error { // check for fragment if didId == "" || fragment == "" { - return sdkerrors.Wrapf( - ErrInvalidDidDoc, + return fmt.Errorf( "invalid didUrl %s", id, ) @@ -94,8 +85,7 @@ func isSupportedVmType(typ string) error { } if _, supported := VerificationKeySignatureMap[typ]; !supported { - return sdkerrors.Wrapf( - ErrInvalidDidDoc, + return fmt.Errorf( "%s verification method type not supported, supported verification method types: %s ", typ, supportedList(), @@ -113,16 +103,14 @@ func verificationKeyCheck(vm *VerificationMethod) error { switch vm.Type { case EcdsaSecp256k1RecoveryMethod2020: if vm.GetBlockchainAccountId() == "" { - return sdkerrors.Wrapf( - ErrBadRequestInvalidVerMethod, + return fmt.Errorf( "blockchainAccountId cannot be empty for verification method %s as it is of type %s", vm.Id, vm.Type, ) } if vm.GetPublicKeyMultibase() != "" { - return sdkerrors.Wrapf( - ErrBadRequestInvalidVerMethod, + return fmt.Errorf( "publicKeyMultibase should be empty for verification method %s as it is type %s", vm.Id, vm.Type, @@ -131,16 +119,14 @@ func verificationKeyCheck(vm *VerificationMethod) error { default: if vm.GetBlockchainAccountId() != "" { - return sdkerrors.Wrapf( - ErrBadRequestInvalidVerMethod, + return fmt.Errorf( "blockchainAccountId should be empty for verification method %s as it is of type %s", vm.Id, vm.Type, ) } if vm.GetPublicKeyMultibase() == "" { - return sdkerrors.Wrapf( - ErrBadRequestInvalidVerMethod, + return fmt.Errorf( "publicKeyMultibase cannot be empty for verification method %s as it is type %s", vm.Id, vm.Type, @@ -171,7 +157,7 @@ func validateServices(services []*Service) error { // validate service Id if err = isDidUrl(service.Id); err != nil { - return ErrInvalidService.Wrapf("service ID %s is Invalid", service.Id) + return fmt.Errorf("service ID %s is Invalid", service.Id) } // validate service Type @@ -182,7 +168,7 @@ func validateServices(services []*Service) error { } } if !foundType { - return ErrInvalidService.Wrapf("service Type %s is Invalid", service.Type) + return fmt.Errorf("service Type %s is Invalid", service.Type) } } @@ -192,7 +178,7 @@ func validateServices(services []*Service) error { serviceIdList = append(serviceIdList, service.Id) } if duplicateId := checkDuplicateId(serviceIdList); duplicateId != "" { - return ErrInvalidService.Wrapf("duplicate service found with Id: %s ", duplicateId) + return fmt.Errorf("duplicate service found with Id: %s ", duplicateId) } return nil @@ -239,13 +225,13 @@ func validateVerificationMethods(vms []*VerificationMethod) error { blockchainAccountIdList = append(blockchainAccountIdList, vm.BlockchainAccountId) } if duplicateId := checkDuplicateId(vmIdList); duplicateId != "" { - return ErrInvalidDidDoc.Wrapf("duplicate verification method Id found: %s ", duplicateId) + return fmt.Errorf("duplicate verification method Id found: %s ", duplicateId) } if duplicateKey := checkDuplicateId(publicKeyMultibaseList); duplicateKey != "" { - return ErrInvalidDidDoc.Wrapf("duplicate publicKeyMultibase found: %s ", duplicateKey) + return fmt.Errorf("duplicate publicKeyMultibase found: %s ", duplicateKey) } if duplicateKey := checkDuplicateId(blockchainAccountIdList); duplicateKey != "" { - return ErrInvalidDidDoc.Wrapf("duplicate blockchainAccountId found: %s ", duplicateKey) + return fmt.Errorf("duplicate blockchainAccountId found: %s ", duplicateKey) } return nil diff --git a/x/ssi/types/errors.go b/x/ssi/types/errors.go index b358d1e..e3a8595 100644 --- a/x/ssi/types/errors.go +++ b/x/ssi/types/errors.go @@ -8,31 +8,18 @@ import ( // x/ssi module sentinel errors var ( - ErrBadRequestIsRequired = sdkerrors.Register(ModuleName, 101, "is required") - ErrBadRequestIsNotDid = sdkerrors.Register(ModuleName, 102, "is an invalid Hypersign DID format") - ErrDidDocExists = sdkerrors.Register(ModuleName, 103, "DidDoc already exists") - ErrInvalidDidDoc = sdkerrors.Register(ModuleName, 104, "DidDoc is invalid") - ErrVerificationMethodNotFound = sdkerrors.Register(ModuleName, 105, "verification method not found") - ErrInvalidPublicKey = sdkerrors.Register(ModuleName, 106, "invalid public key") - ErrInvalidSignature = sdkerrors.Register(ModuleName, 107, "invalid signature detected") - ErrDidDocNotFound = sdkerrors.Register(ModuleName, 108, "DID Doc not found") - ErrSchemaExists = sdkerrors.Register(ModuleName, 109, "Schema already exists") - ErrInvalidSchemaID = sdkerrors.Register(ModuleName, 110, "Invalid schema Id") - ErrBadRequestInvalidVerMethod = sdkerrors.Register(ModuleName, 111, "Invalid verification method") - ErrInvalidService = sdkerrors.Register(ModuleName, 112, "Invalid Service") - ErrUnexpectedDidVersion = sdkerrors.Register(ModuleName, 113, "Unexpected DID version") - ErrDidDocDeactivated = sdkerrors.Register(ModuleName, 114, "DID Document is deactivated") - ErrInvalidDidElements = sdkerrors.Register(ModuleName, 115, "Invalid DID elements") - ErrInvalidDidMethod = sdkerrors.Register(ModuleName, 116, "Invalid DID method") - ErrInvalidMethodSpecificId = sdkerrors.Register(ModuleName, 117, "Invalid DID method specific Id") - ErrCredentialExists = sdkerrors.Register(ModuleName, 118, "Credential Already Exists") - ErrCredentialNotFound = sdkerrors.Register(ModuleName, 119, "Credential Not Found") - ErrInvalidCredentialStatus = sdkerrors.Register(ModuleName, 120, "Invalid Credential Status") - ErrInvalidDate = sdkerrors.Register(ModuleName, 121, "Invalid Date") - ErrInvalidCredentialField = sdkerrors.Register(ModuleName, 122, "Invalid Credential Field") - ErrInvalidCredentialHash = sdkerrors.Register(ModuleName, 123, "Invalid Credential Hash") - ErrInvalidSchemaModelVersion = sdkerrors.Register(ModuleName, 124, "Invalid Schema Model Version") - ErrNullSchemaField = sdkerrors.Register(ModuleName, 125, "Null Schema Field") - ErrInvalidClientSpecType = sdkerrors.Register(ModuleName, 126, "Invalid Client Spec Type") - ErrInvalidBlockchainAccountId = sdkerrors.Register(ModuleName, 127, "invalid blockchain account id") + ErrDidDocExists = sdkerrors.Register(ModuleName, 101, "didDoc already exists") + ErrInvalidDidDoc = sdkerrors.Register(ModuleName, 102, "didDoc is invalid") + ErrVerificationMethodNotFound = sdkerrors.Register(ModuleName, 103, "verification method not found") + ErrInvalidSignature = sdkerrors.Register(ModuleName, 104, "invalid signature detected") + ErrDidDocNotFound = sdkerrors.Register(ModuleName, 105, "didDoc not found") + ErrSchemaExists = sdkerrors.Register(ModuleName, 106, "schema already exists") + ErrInvalidSchemaID = sdkerrors.Register(ModuleName, 107, "invalid schema Id") + ErrUnexpectedDidVersion = sdkerrors.Register(ModuleName, 108, "unexpected DID version") + ErrDidDocDeactivated = sdkerrors.Register(ModuleName, 109, "didDoc is deactivated") + ErrInvalidCredentialStatus = sdkerrors.Register(ModuleName, 110, "invalid Credential Status") + ErrInvalidDate = sdkerrors.Register(ModuleName, 111, "invalid Date") + ErrInvalidCredentialField = sdkerrors.Register(ModuleName, 112, "invalid Credential Field") + ErrInvalidCredentialHash = sdkerrors.Register(ModuleName, 113, "invalid Credential Hash") + ErrInvalidClientSpecType = sdkerrors.Register(ModuleName, 114, "invalid Client Spec Type") ) diff --git a/x/ssi/verification/common_document_checks.go b/x/ssi/verification/common_document_checks.go index 9f7435e..5fe173a 100644 --- a/x/ssi/verification/common_document_checks.go +++ b/x/ssi/verification/common_document_checks.go @@ -4,9 +4,6 @@ import ( "fmt" "regexp" "strings" - - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/hypersign-protocol/hid-node/x/ssi/types" ) func documentIdentifier(docType string) string { @@ -96,12 +93,9 @@ func IsValidID(Id string, namespace string, docType string) error { return fmt.Errorf("error in parsing regular expression for method-specific-id: %s", err.Error()) } if !isProperMethodSpecificId { - return sdkerrors.Wrap( - types.ErrInvalidMethodSpecificId, - fmt.Sprintf( - "method-specific-id should be an alphanumeric string with minimum 32 characters, recieved: %s", - docElements[docMethodSpecificId], - ), + return fmt.Errorf( + "method-specific-id should be an alphanumeric string with minimum 32 characters, recieved: %s", + docElements[docMethodSpecificId], ) } diff --git a/x/ssi/verification/credential_verification.go b/x/ssi/verification/credential_verification.go index a51bd7c..33e6cee 100644 --- a/x/ssi/verification/credential_verification.go +++ b/x/ssi/verification/credential_verification.go @@ -5,7 +5,6 @@ import ( "regexp" "time" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/hypersign-protocol/hid-node/x/ssi/types" ) @@ -29,13 +28,10 @@ func VerifyCredentialStatusDates(issuanceDate time.Time, expirationDate time.Tim var dateDiff int64 = int64(expirationDate.Sub(issuanceDate)) / 1e9 // converting nanoseconds to seconds // Check if the expiration date predates the issuance date. if dateDiff < 0 { - return sdkerrors.Wrapf( - types.ErrInvalidDate, - fmt.Sprintf( - "expiration date %s cannot be less than issuance date %s", - expirationDate, - issuanceDate, - ), + return fmt.Errorf( + "expiration date %s cannot be less than issuance date %s", + expirationDate, + issuanceDate, ) } @@ -48,24 +44,18 @@ func VerifyCredentialProofDates(credProof *types.CredentialProof, credRegistrati proofCreatedDate := credProof.GetCreated() proofCreatedDateParsed, err := time.Parse(time.RFC3339, proofCreatedDate) if err != nil { - return sdkerrors.Wrapf( - types.ErrInvalidDate, - fmt.Sprintf( - "invalid created date format: %s", - proofCreatedDate, - ), + return fmt.Errorf( + "invalid created date format: %s", + proofCreatedDate, ) } proofUpdatedDate := credProof.GetUpdated() proofUpdatedDateParsed, err := time.Parse(time.RFC3339, proofUpdatedDate) if err != nil { - return sdkerrors.Wrapf( - types.ErrInvalidDate, - fmt.Sprintf( - "invalid created date format: %s", - proofUpdatedDate, - ), + return fmt.Errorf( + "invalid created date format: %s", + proofUpdatedDate, ) } @@ -73,25 +63,19 @@ func VerifyCredentialProofDates(credProof *types.CredentialProof, credRegistrati // else, check for updated date being greater than created date will proceeed if credRegistration { if !proofUpdatedDateParsed.Equal(proofCreatedDateParsed) { - return sdkerrors.Wrapf( - types.ErrInvalidDate, - fmt.Sprintf( - "updated date %s should be similar to created date %s", - proofUpdatedDate, - proofCreatedDate, - ), + return fmt.Errorf( + "updated date %s should be similar to created date %s", + proofUpdatedDate, + proofCreatedDate, ) } } else { dateDiff = int64(proofUpdatedDateParsed.Sub(proofCreatedDateParsed)) / 1e9 // converting nanoseconds to seconds if dateDiff <= 0 { - return sdkerrors.Wrapf( - types.ErrInvalidDate, - fmt.Sprintf( - "update date %s cannot be less than or equal to created date %s in case of credential status update", - proofUpdatedDate, - proofCreatedDate, - ), + return fmt.Errorf( + "update date %s cannot be less than or equal to created date %s in case of credential status update", + proofUpdatedDate, + proofCreatedDate, ) } } diff --git a/x/ssi/verification/crypto.go b/x/ssi/verification/crypto.go index bd0a3a2..385d770 100644 --- a/x/ssi/verification/crypto.go +++ b/x/ssi/verification/crypto.go @@ -76,8 +76,8 @@ func verifyEd25519(publicKey string, signature string, documentBytes []byte) err // Decode Public Key _, publicKeyBytes, err := multibase.Decode(publicKey) if err != nil { - return types.ErrInvalidPublicKey.Wrapf( - "Cannot decode Ed25519 public key %s", + return fmt.Errorf( + "cannot decode Ed25519 public key %s", publicKey, ) } @@ -89,7 +89,7 @@ func verifyEd25519(publicKey string, signature string, documentBytes []byte) err } if !ed25519.Verify(publicKeyBytes, documentBytes, signatureBytes) { - return fmt.Errorf("signature verification failed") + return fmt.Errorf("ed25519: signature could not be verified") } else { return nil } @@ -110,7 +110,7 @@ func verifySecp256k1(publicKey string, signature string, documentBytes []byte) e } if !pubKeyObj.VerifySignature(documentBytes, signatureBytes) { - return fmt.Errorf("signature verification failed") + return fmt.Errorf("secp256k1: signature could not be verified") } else { return nil } @@ -147,7 +147,7 @@ func recoverEthPublicKey(blockchainAccountId string, signature string, documentB // Match the recovered address against user provided address if recoveredBlockchainAddress != blockchainAddress { - return fmt.Errorf("signature verification failed") + return fmt.Errorf("eth-recovery-method-secp256k1: signature could not be verified") } else { return nil } From 45b9fa71fcc77e351c206122fb7abaaa25af3c64 Mon Sep 17 00:00:00 2001 From: arnabghose997 Date: Thu, 2 Mar 2023 11:45:32 +0530 Subject: [PATCH 3/3] refactor: didDoc and credentialStatus doc will throw proper error status codes --- x/ssi/keeper/grpc_query_credential.go | 3 ++- x/ssi/keeper/grpc_query_did.go | 3 ++- x/ssi/keeper/msg_server_credential.go | 2 +- x/ssi/types/errors.go | 1 + 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/x/ssi/keeper/grpc_query_credential.go b/x/ssi/keeper/grpc_query_credential.go index 3fac0e5..51fc8d7 100644 --- a/x/ssi/keeper/grpc_query_credential.go +++ b/x/ssi/keeper/grpc_query_credential.go @@ -5,6 +5,7 @@ import ( "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/types/query" "github.com/hypersign-protocol/hid-node/x/ssi/types" "google.golang.org/grpc/codes" @@ -19,7 +20,7 @@ func (k Keeper) QueryCredential(goCtx context.Context, req *types.QueryCredentia cred, err := k.GetCredentialStatusFromState(&ctx, req.CredId) if err != nil { - return nil, err + return nil, sdkerrors.Wrap(types.ErrCredentialStatusNotFound, err.Error()) } return &types.QueryCredentialResponse{CredStatus: cred}, nil diff --git a/x/ssi/keeper/grpc_query_did.go b/x/ssi/keeper/grpc_query_did.go index 71b2a99..483edcf 100644 --- a/x/ssi/keeper/grpc_query_did.go +++ b/x/ssi/keeper/grpc_query_did.go @@ -5,6 +5,7 @@ import ( "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/types/query" "github.com/hypersign-protocol/hid-node/x/ssi/types" @@ -60,7 +61,7 @@ func (k Keeper) QueryDidDocument(goCtx context.Context, req *types.QueryDidDocum // Check if DID Document exists didDoc, err := k.GetDidDocumentState(&ctx, req.DidId) if err != nil { - return nil, err + return nil, sdkerrors.Wrap(types.ErrDidDocNotFound, err.Error()) } return &types.QueryDidDocumentResponse{ diff --git a/x/ssi/keeper/msg_server_credential.go b/x/ssi/keeper/msg_server_credential.go index 6819f7b..a5a83a9 100644 --- a/x/ssi/keeper/msg_server_credential.go +++ b/x/ssi/keeper/msg_server_credential.go @@ -135,7 +135,7 @@ func (k msgServer) updateCredentialStatus(ctx sdk.Context, msg *types.MsgRegiste // Get Credential from store oldCredStatus, err := k.GetCredentialStatusFromState(&ctx, credId) if err != nil { - return nil, err + return nil, sdkerrors.Wrap(types.ErrCredentialStatusNotFound, err.Error()) } // Check if the DID of the issuer exists diff --git a/x/ssi/types/errors.go b/x/ssi/types/errors.go index e3a8595..bda79f4 100644 --- a/x/ssi/types/errors.go +++ b/x/ssi/types/errors.go @@ -22,4 +22,5 @@ var ( ErrInvalidCredentialField = sdkerrors.Register(ModuleName, 112, "invalid Credential Field") ErrInvalidCredentialHash = sdkerrors.Register(ModuleName, 113, "invalid Credential Hash") ErrInvalidClientSpecType = sdkerrors.Register(ModuleName, 114, "invalid Client Spec Type") + ErrCredentialStatusNotFound = sdkerrors.Register(ModuleName, 115, "credentialStatus document not found") )