Skip to content

Commit

Permalink
Merge pull request #353 from hypersign-protocol/method-specific-id-ru…
Browse files Browse the repository at this point in the history
…les-impl

Feat: Defined rules for `method-specific-id`
  • Loading branch information
arnabghose997 committed Apr 21, 2023
2 parents b943ffc + 2d8b27d commit 597964b
Show file tree
Hide file tree
Showing 12 changed files with 386 additions and 138 deletions.
132 changes: 130 additions & 2 deletions tests/e2e/ssi_tests/e2e_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -842,6 +842,7 @@ def caip10_cosmos_support_test():
did_doc_string_2 = generate_did_document(kp, kp_algo, "osmo")
did_doc_string_2_vm = did_doc_string_2["verificationMethod"][0]
did_doc_string_2_vm["id"] = did_doc_string_2_vm["id"] + "new"
did_doc_string_2_vm["controller"] = did_doc_string_1["verificationMethod"][0]["controller"]

did_doc_string_1["verificationMethod"] = [
did_doc_string_1["verificationMethod"][0],
Expand Down Expand Up @@ -1028,7 +1029,7 @@ def vm_type_test():
print("4. PASS: Registering DID Document with a verification method of type EcdsaSecp256k1VerificationKey2019. Only publicKeyMultibase is passed.")
kp_algo = "secp256k1"
kp = generate_key_pair(algo=kp_algo)
did_doc_string = generate_did_document(kp, kp_algo)
did_doc_string = generate_did_document(kp, kp_algo, is_uuid=True, bech32prefix="")
did_doc_id = did_doc_string["id"]
did_doc_string["verificationMethod"][0]["blockchainAccountId"] = ""
signers = []
Expand Down Expand Up @@ -1122,4 +1123,131 @@ def vm_type_test():
run_blockchain_command(create_tx_cmd, f"Registering DID with Id: {did_doc_id}")

print("--- Test Completed ---\n")


def method_specific_id_test():
print("\n--- Method Specific ID Tests ---\n")

print("1. PASS: Registering a DID Document where the user provides a blockchain address in MSI that they own")

kp_algo = "secp256k1"
kp_alice = generate_key_pair(algo=kp_algo)
signers = []
did_doc_string = generate_did_document(kp_alice, algo=kp_algo)
did_doc_alice = did_doc_string["id"]
did_doc_alice_vm = did_doc_string["verificationMethod"]
signPair_alice = {
"kp": kp_alice,
"verificationMethodId": did_doc_string["verificationMethod"][0]["id"],
"signing_algo": kp_algo
}
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}")

print("2. FAIL: Registering a DID Document where the user provides a blockchain address in MSI that they don't own")

kp_algo = "secp256k1"
kp_bob = generate_key_pair(algo=kp_algo)
signers = []
did_doc_string = generate_did_document(kp_bob, algo=kp_algo)
did_doc_string["controller"] = [did_doc_alice]
did_doc_string["verificationMethod"] = did_doc_alice_vm

did_doc_bob = did_doc_string["id"]
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 Bob's DID with Id: {did_doc_bob}", True)

print("3. PASS: Registering a DID Document where the user provides a multibase encoded public key in MSI that they own")

kp_algo = "ed25519"
kp_alice = generate_key_pair(algo=kp_algo)
signers = []
did_doc_string = generate_did_document(kp_alice, algo=kp_algo)
did_doc_alice = did_doc_string["id"]
did_doc_alice_vm = did_doc_string["verificationMethod"]
signPair_alice = {
"kp": kp_alice,
"verificationMethodId": did_doc_string["verificationMethod"][0]["id"],
"signing_algo": kp_algo
}
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}")

print("4. PASS: Registering a DID Document where the user provides a multibase encoded public key in MSI that they don't own")

kp_algo = "ed25519"
kp_bob = generate_key_pair(algo=kp_algo)
signers = []
did_doc_string = generate_did_document(kp_bob, algo=kp_algo)
did_doc_string["controller"] = [did_doc_alice]
did_doc_string["verificationMethod"] = did_doc_alice_vm

did_doc_bob = did_doc_string["id"]
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 Bob's DID with Id: {did_doc_bob}")

print("5. FAIL: Attempt to Register Invalid DID Documents with invalid DID Id")

did_id_list = [
"did:hid:1:",
"did:hid:1",
"did:hid:devnet",
"did:hid:devnet:",
"did:hid:devnet:zHiii",
"did:hid:devnet:asa54qf",
"did:hid:devnet:asa54qf|sds",
"did:hid:devnet:-asa54-qfsds",
"did:hid:devnet:.com",
]

for did_id in did_id_list:
did_doc_string["id"] = did_id
create_tx_cmd = form_did_create_tx_multisig(did_doc_string, signers, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME)
run_blockchain_command(create_tx_cmd, f"Registering Invalid DID with Id: {did_id}", True, True)

print("6. PASS: Alice tries to update their DID Document by removing the Verification Method associated with the method specific id (CAIP-10 Blockchain Address)")

kp_algo = "secp256k1"
kp_alice = generate_key_pair(algo=kp_algo)
signers = []
did_doc_string = generate_did_document(kp_alice, algo=kp_algo)
did_doc_alice = did_doc_string["id"]
did_doc_alice_vm = did_doc_string["verificationMethod"]
signPair_alice = {
"kp": kp_alice,
"verificationMethodId": did_doc_string["verificationMethod"][0]["id"],
"signing_algo": kp_algo
}
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}")

did_doc_string["verificationMethod"] = []
update_tx_cmd = form_did_update_tx_multisig(did_doc_string, signers, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME)
run_blockchain_command(update_tx_cmd, f"Removal of Verification Method associated with method specific id")

print("7. PASS: Alice tries to update their DID Document by removing the Verification Method associated with the method specific id (Multibase Encoded PublicKey)")

kp_algo = "ed25519"
kp_alice = generate_key_pair(algo=kp_algo)
signers = []
did_doc_string = generate_did_document(kp_alice, algo=kp_algo)
did_doc_alice = did_doc_string["id"]
did_doc_alice_vm = did_doc_string["verificationMethod"]
signPair_alice = {
"kp": kp_alice,
"verificationMethodId": did_doc_string["verificationMethod"][0]["id"],
"signing_algo": kp_algo
}
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}")

did_doc_string["verificationMethod"] = []
update_tx_cmd = form_did_update_tx_multisig(did_doc_string, signers, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME)
run_blockchain_command(update_tx_cmd, f"Removal of Verification Method associated with method specific id")

print("--- Test Completed ---\n")
19 changes: 13 additions & 6 deletions tests/e2e/ssi_tests/generate_doc.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from utils import run_command, generate_document_id, get_document_signature, \
secp256k1_pubkey_to_address

def generate_did_document(key_pair, algo="ed25519", bech32prefix="hid"):
def generate_did_document(key_pair, algo="ed25519", bech32prefix="hid", is_uuid=False):
base_document = {
"context" : [
"https://www.w3.org/ns/did/v1"
Expand All @@ -17,7 +17,7 @@ def generate_did_document(key_pair, algo="ed25519", bech32prefix="hid"):
"authentication": [],
}

did_id = generate_document_id("did", key_pair, algo)
did_id = generate_document_id("did", key_pair, algo, is_uuid)

# Form the DID Document
vm_type = ""
Expand All @@ -31,29 +31,36 @@ def generate_did_document(key_pair, algo="ed25519", bech32prefix="hid"):
raise Exception("unknown signing algorithm: " + key_pair)

verification_method = {
"id": did_id + "#key-1",
"type": vm_type,
"controller": did_id,
"id": "",
"type": "",
"controller": "",
"blockchainAccountId": "",
"publicKeyMultibase": ""
}

if algo == "recover-eth":
verification_method["blockchainAccountId"] = "eip155:1:" + key_pair["ethereum_address"]
elif algo == "secp256k1":

if bech32prefix == "hid":
verification_method["blockchainAccountId"] = "cosmos:jagrat:" + \
secp256k1_pubkey_to_address(key_pair["pub_key_base_64"], bech32prefix)
did_id = "did:hid:devnet:" + verification_method["blockchainAccountId"]
elif bech32prefix == "osmo":
verification_method["blockchainAccountId"] = "cosmos:osmosis-1:" + \
secp256k1_pubkey_to_address(key_pair["pub_key_base_64"], bech32prefix)
did_id = "did:hid:devnet:" + verification_method["blockchainAccountId"]
else:
raise Exception("unsupported bech32 prefix " + bech32prefix)
verification_method["blockchainAccountId"] = ""

verification_method["publicKeyMultibase"] = key_pair["pub_key_multibase"]
else:
verification_method["publicKeyMultibase"] = key_pair["pub_key_multibase"]

verification_method["controller"] = did_id
verification_method["type"] = vm_type
verification_method["id"] = did_id + "#k1"

base_document["id"] = did_id
base_document["controller"] = [did_id]
base_document["verificationMethod"] = [verification_method]
Expand Down
1 change: 1 addition & 0 deletions tests/e2e/ssi_tests/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ def run_all_tests():
caip10_ethereum_support_test()
caip10_cosmos_support_test()
vm_type_test()
method_specific_id_test()

print("============= 😃️ All test cases completed successfully ============== \n")

Expand Down
14 changes: 9 additions & 5 deletions tests/e2e/ssi_tests/utils.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import subprocess
import json
import uuid

def run_command(cmd_string):
if type(cmd_string) != str:
Expand Down Expand Up @@ -58,16 +59,19 @@ def generate_key_pair(algo="ed25519"):
kp = json.loads(result_str)
return kp

def generate_document_id(doc_type: str, kp: dict = None, algo: str = "ed25519"):
def generate_document_id(doc_type: str, kp: dict = None, algo: str = "ed25519", is_uuid: bool =False):
id = ""
if not kp:
kp = generate_key_pair(algo)

if algo in ["recover-eth"]:
method_specific_id = kp["ethereum_address"]
if is_uuid:
method_specific_id = str(uuid.uuid4())
else:
method_specific_id = kp["pub_key_multibase"]

if algo in ["recover-eth"]:
method_specific_id = kp["ethereum_address"]
else:
method_specific_id = kp["pub_key_multibase"]

if method_specific_id == None:
raise Exception("Public key is empty")

Expand Down
39 changes: 38 additions & 1 deletion x/ssi/keeper/msg_server_create_did.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,13 @@ func (k msgServer) CreateDID(goCtx context.Context, msg *types.MsgCreateDID) (*t
}

// Validate namespace in DID Document
if err := didDocNamespaceValidation(k, ctx, msgDidDocument); err != nil {
chainNamespace := k.GetChainNamespace(&ctx)
if err := types.DidChainNamespaceValidation(msgDidDocument, chainNamespace); err != nil {
return nil, sdkerrors.Wrap(types.ErrInvalidDidDoc, err.Error())
}

// Validate ownership of method specific id
if err := checkMethodSpecificIdOwnership(msgDidDocument.VerificationMethod, msgDidDocument.Id); err != nil {
return nil, sdkerrors.Wrap(types.ErrInvalidDidDoc, err.Error())
}

Expand Down Expand Up @@ -76,6 +82,37 @@ func (k msgServer) CreateDID(goCtx context.Context, msg *types.MsgCreateDID) (*t
return &types.MsgCreateDIDResponse{Id: id}, nil
}

// checkMethodSpecificIdOwnership validates the ownership of blockchain account id passed in the method specific
// identifier of DID Document. This ensures that a DID ID (containing a blockchain address) is being created by someone
// who owns the blockchain address.
func checkMethodSpecificIdOwnership(verificationMethods []*types.VerificationMethod, didId string) error {
inputMSI, inputMSIType, err := types.GetMethodSpecificIdAndType(didId)
if err != nil {
return err
}

if inputMSIType == types.MSIBlockchainAccountId {
foundMSIinAnyVM := false
for _, vm := range verificationMethods {
if vm.BlockchainAccountId == inputMSI {
foundMSIinAnyVM = true
break
}
}

if !foundMSIinAnyVM {
return fmt.Errorf(
"proof of ownership for method-specific-id in %v must be provided",
didId,
)
} else {
return nil
}
} else {
return nil
}
}

// getControllersForCreateDID returns a list of controller DIDs
// from controller and verification method attributes
func getControllersForCreateDID(didDocument *types.Did) []string {
Expand Down
3 changes: 2 additions & 1 deletion x/ssi/keeper/msg_server_update_did.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ func (k msgServer) UpdateDID(goCtx context.Context, msg *types.MsgUpdateDID) (*t
}

// Validate namespace in DID Document
if err := didDocNamespaceValidation(k, ctx, msgDidDocument); err != nil {
chainNamespace := k.GetChainNamespace(&ctx)
if err := types.DidChainNamespaceValidation(msgDidDocument, chainNamespace); err != nil {
return nil, sdkerrors.Wrap(types.ErrInvalidDidDoc, err.Error())
}

Expand Down
79 changes: 0 additions & 79 deletions x/ssi/keeper/namespace.go

This file was deleted.

0 comments on commit 597964b

Please sign in to comment.