Skip to content

Commit

Permalink
Merge pull request #363 from hypersign-protocol/key-agreement-support
Browse files Browse the repository at this point in the history
Support for `X25519KeyAgreementKey2020` and `X25519KeyAgreementKeyEIP5630` based keys
  • Loading branch information
arnabghose997 authored Jul 20, 2023
2 parents 20281bc + 455bd01 commit 5e6e330
Show file tree
Hide file tree
Showing 9 changed files with 232 additions and 25 deletions.
82 changes: 81 additions & 1 deletion tests/e2e/ssi_tests/e2e_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,92 @@
sys.path.insert(1, os.getcwd())
import time

from utils import run_blockchain_command, generate_key_pair, secp256k1_pubkey_to_address
from utils import run_blockchain_command, generate_key_pair, secp256k1_pubkey_to_address, add_keyAgreeemnt_pubKeyMultibase
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

def key_agrement_test():
print("\n--1. FAIL: Ed25519VerificationKey2020 based Verification Method ID being added to keyAgreement attribute--\n")

kp_alice = generate_key_pair("ed25519")
signers = []
did_doc_string = generate_did_document(kp_alice, "ed25519")
did_doc_alice = did_doc_string["id"]
ed25519Vm = did_doc_string["verificationMethod"][0]
did_doc_string["keyAgreement"] = [ed25519Vm["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}", True, True)

print("\n--2. FAIL: X25519KeyAgreementKey2020 based Verification Method ID being added to authentication attribute--\n")

kp_bob = generate_key_pair("ed25519")
signers = []
did_doc_string = generate_did_document(kp_bob, "ed25519")
did_doc_alice = did_doc_string["id"]
x25519Vm = add_keyAgreeemnt_pubKeyMultibase(did_doc_string["verificationMethod"][0], "X25519KeyAgreementKey2020")
did_doc_string["verificationMethod"] = [x25519Vm]
did_doc_string["authentication"] = [x25519Vm["id"]]
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 Alice's DID with Id: {did_doc_alice}", True, True)

print("\n--3. PASS: A DID Document is created with Ed25519VerificationKey2020 and X25519KeyAgreementKey2020 based VMs--\n")
did_doc_string = generate_did_document(kp_alice, "ed25519")
signers = []
x25519Vm["controller"] = ed25519Vm["controller"]
did_doc_string["verificationMethod"] = [ed25519Vm, x25519Vm]
did_doc_string["authentication"] = [ed25519Vm["id"]]
did_doc_string["keyAgreement"] = [x25519Vm["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 Alice's DID with Id: {did_doc_string['id']}")

print("\n--4. FAIL: An attempt is made to update the DID Document by passing the signature of X25519KeyAgreementKey2020 based verification method")
signers = []
did_doc_string["context"] = ["some_context"]
did_doc_string["authentication"] = []
signPair_x25519 = {
"kp": kp_bob,
"verificationMethodId": x25519Vm["id"],
"signing_algo": "ed25519"
}
signers.append(signPair_x25519)
update_tx_cmd = form_did_update_tx_multisig(did_doc_string, signers, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME)
run_blockchain_command(update_tx_cmd, f"DID Document update using X25519KeyAgreementKey2020 based verification method", True)

print("\n--5. PASS: An attempt is made to update the DID Document by passing the signature of Ed25519VerificationKey2020 based verification method")
signers = []
did_doc_string["context"] = ["some_context"]
signers.append(signPair_alice)
signers.append(signPair_x25519)
update_tx_cmd = form_did_update_tx_multisig(did_doc_string, signers, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME)
run_blockchain_command(update_tx_cmd, f"DID Document {did_doc_string['id']} update using Ed25519VerificationKey2020 based verification method")

print("\n--6. FAIL: An attempt is made to deactivate the DID Document by passing the signature of X25519KeyAgreementKey2020 based verification method--\n")
signers = []
signers.append(signPair_x25519)
deactivate_tx_cmd = form_did_deactivate_tx_multisig(did_doc_string["id"], signers, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME)
run_blockchain_command(deactivate_tx_cmd, f"DID Document deactivate using X25519KeyAgreementKey2020 based verification method", True)

print("\n--7. PASS: An attempt is made to deactivate the DID Document by passing the signature of Ed25519VerificationKey2020 based verification method--\n")
signers = []
signers.append(signPair_alice)
deactivate_tx_cmd = form_did_deactivate_tx_multisig(did_doc_string["id"], signers, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME)
run_blockchain_command(deactivate_tx_cmd, f"DID Document deactivate using Ed25519VerificationKey2020 based verification method")

def unique_wallet_address_test():
print("\n---1. FAIL: Alice Creates a DID Doc. Bob attempts to create a DID Document by adding one of Alice's VM.---\n")

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 @@ -34,6 +34,7 @@ def run_all_tests():
vm_type_test()
method_specific_id_test()
unique_wallet_address_test()
key_agrement_test()

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

Expand Down
13 changes: 13 additions & 0 deletions tests/e2e/ssi_tests/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,19 @@ def generate_key_pair(algo="ed25519"):
kp = json.loads(result_str)
return kp

def add_keyAgreeemnt_pubKeyMultibase(verification_method, type):
if verification_method["type"] != "Ed25519VerificationKey2020":
raise Exception("verification method " + verification_method["id"] + " must be of type Ed25519VerificationKey2020")

if type == "X25519KeyAgreementKey2020":
verification_method["type"] = "X25519KeyAgreementKey2020"
elif type == "X25519KeyAgreementKeyEIP5630":
verification_method["type"] = "X25519KeyAgreementKeyEIP5630"
else:
raise Exception("invalid key agreement type " + type)

return verification_method

def generate_document_id(doc_type: str, kp: dict = None, algo: str = "ed25519", is_uuid: bool =False):
id = ""
if not kp:
Expand Down
47 changes: 31 additions & 16 deletions x/ssi/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,12 @@ func (k msgServer) formMustControllerVmListMap(ctx sdk.Context,
}
_, presentInControllerMap := controllerMap[vmState.Controller]
if presentInControllerMap {
vmExtended := types.CreateExtendedVerificationMethod(vmState, sign)
controllerMap[controller] = append(controllerMap[controller], vmExtended)
// Skip X25519KeyAgreementKey2020 or X25519KeyAgreementKey2020 because these
// are not allowed for Authentication and Assertion purposes
if (vmState.Type != types.X25519KeyAgreementKey2020) && (vmState.Type != types.X25519KeyAgreementKeyEIP5630) {
vmExtended := types.CreateExtendedVerificationMethod(vmState, sign)
controllerMap[controller] = append(controllerMap[controller], vmExtended)
}
delete(inputSignMap, vmId)
}
}
Expand Down Expand Up @@ -149,8 +153,12 @@ func (k msgServer) formAnyControllerVmListMap(ctx sdk.Context,
}
_, presentInControllerMap := controllerMap[vmState.Controller]
if presentInControllerMap {
vmExtended := types.CreateExtendedVerificationMethod(vmState, sign)
controllerMap[controller] = append(controllerMap[controller], vmExtended)
// Skip X25519KeyAgreementKey2020 or X25519KeyAgreementKey2020 because these
// are not allowed for Authentication and Assertion purposes
if (vmState.Type != types.X25519KeyAgreementKey2020) && (vmState.Type != types.X25519KeyAgreementKeyEIP5630) {
vmExtended := types.CreateExtendedVerificationMethod(vmState, sign)
controllerMap[controller] = append(controllerMap[controller], vmExtended)
}
}
}
}
Expand Down Expand Up @@ -178,33 +186,40 @@ func (k msgServer) getControllerVmFromState(ctx sdk.Context, verificationMethodI
// VerifyDocumentProof verifies the proof of a SSI Document
func (k msgServer) VerifyDocumentProof(ctx sdk.Context, ssiMsg types.SsiMsg, inputDocProof types.SSIProofInterface, clientSpec *types.ClientSpec) error {
// Get DID Document from State
schemaProofVmId := inputDocProof.GetVerificationMethod()
didId, _ := types.SplitDidUrl(schemaProofVmId)
docProofVmId := inputDocProof.GetVerificationMethod()
didId, _ := types.SplitDidUrl(docProofVmId)
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
var docVm *types.VerificationMethod = nil
for _, vm := range didDoc.VerificationMethod {
if vm.Id == schemaProofVmId {
schemaVm = vm
if vm.Id == docProofVmId {
docVm = vm
break
}
}
if schemaVm == nil {
return fmt.Errorf("verificationMethod %s is not present in DID document %s", schemaProofVmId, didId)
if docVm == nil {
return fmt.Errorf("verificationMethod %s is not present in DID document %s", docProofVmId, didId)
}

// VerificationKeySignatureMap has X25519KeyAgreementKey2020 and X25519KeyAgreementKeyEIP5630 as supported Verification Type.
// However, they are not allowed to be used for Authentication or Assertion purposes. Since, their corresponding values in the map
// are empty string, the following check is in place.
if types.VerificationKeySignatureMap[docVm.Type] == "" {
return fmt.Errorf("proof type must be specified")
}

// Check if the Proof Type is correct
if types.VerificationKeySignatureMap[schemaVm.Type] != inputDocProof.GetType() {
if types.VerificationKeySignatureMap[docVm.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,
types.VerificationKeySignatureMap[docVm.Type],
docVm.Id,
docVm.Type,
inputDocProof.GetType(),
)
}
Expand All @@ -215,7 +230,7 @@ func (k msgServer) VerifyDocumentProof(ctx sdk.Context, ssiMsg types.SsiMsg, inp
Signature: inputDocProof.GetProofValue(),
ClientSpec: clientSpec,
}
err = verification.VerifyDocumentProofSignature(ssiMsg, schemaVm, signInfo)
err = verification.VerifyDocumentProofSignature(ssiMsg, docVm, signInfo)
if err != nil {
return err
}
Expand Down
7 changes: 7 additions & 0 deletions x/ssi/keeper/msg_server_create_did.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,13 @@ func getVerificationMethodsForCreateDID(didDocument *types.Did) ([]*types.Verifi
if vm.Controller == didDocument.Id {
foundAtleastOneSubjectVM = true
}

// Skip X25519KeyAgreementKey2020 or X25519KeyAgreementKey2020 because these
// are not allowed for Authentication and Assertion purposes
if (vm.Type == types.X25519KeyAgreementKey2020) || (vm.Type == types.X25519KeyAgreementKeyEIP5630) {
continue
}

mustHaveVerificaitonMethods = append(mustHaveVerificaitonMethods, vm)
}

Expand Down
7 changes: 6 additions & 1 deletion x/ssi/keeper/msg_server_update_did.go
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,11 @@ func getVerificationMethodsForUpdateDID(existingVMs []*types.VerificationMethod,
// Make map of existing VMs
existingVmMap := map[string]*types.VerificationMethod{}
for _, vm := range existingVMs {
// Skip X25519KeyAgreementKey2020 or X25519KeyAgreementKey2020 because these
// are not allowed for Authentication and Assertion purposes
if ((vm.Type == types.X25519KeyAgreementKey2020) || (vm.Type == types.X25519KeyAgreementKeyEIP5630)) {
continue
}
existingVmMap[vm.Id] = vm
}

Expand All @@ -280,7 +285,7 @@ func getVerificationMethodsForUpdateDID(existingVMs []*types.VerificationMethod,
// 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 {
if _, present := existingVmMap[vm.Id]; !present && ((vm.Type != types.X25519KeyAgreementKey2020) && (vm.Type != types.X25519KeyAgreementKeyEIP5630)) {
updatedVms = append(
updatedVms,
vm,
Expand Down
4 changes: 4 additions & 0 deletions x/ssi/types/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,16 @@ const MSINonBlockchainAccountId = "MSINonBlockchainAccountId"
const Ed25519VerificationKey2020 = "Ed25519VerificationKey2020"
const EcdsaSecp256k1VerificationKey2019 = "EcdsaSecp256k1VerificationKey2019"
const EcdsaSecp256k1RecoveryMethod2020 = "EcdsaSecp256k1RecoveryMethod2020"
const X25519KeyAgreementKey2020 = "X25519KeyAgreementKey2020"
const X25519KeyAgreementKeyEIP5630 = "X25519KeyAgreementKeyEIP5630" // TODO: Temporary spec name for KeyAgreement type from Metamask

// Mapping between Verification Key and its corresponding Signature
var VerificationKeySignatureMap = map[string]string{
Ed25519VerificationKey2020: "Ed25519Signature2020",
EcdsaSecp256k1VerificationKey2019: "EcdsaSecp256k1Signature2019",
EcdsaSecp256k1RecoveryMethod2020: "EcdsaSecp256k1RecoverySignature2020",
X25519KeyAgreementKey2020: "", // Authentication and Assertion are not allowed
X25519KeyAgreementKeyEIP5630: "", // Authentication and Assertion are not allowed
}

var supportedVerificationMethodTypes []string = func() []string {
Expand Down
66 changes: 59 additions & 7 deletions x/ssi/types/diddoc_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,36 @@ func verificationKeyCheck(vm *VerificationMethod) error {
vm.Type,
)
}
case X25519KeyAgreementKey2020:
if vm.GetPublicKeyMultibase() == "" {
return fmt.Errorf(
"publicKeyMultibase cannot be empty for verification method %s as it is of type %s",
vm.Id,
vm.Type,
)
}
if vm.GetBlockchainAccountId() != "" {
return fmt.Errorf(
"blockchainAccountId must be empty for verification method %s as it is of type %s",
vm.Id,
vm.Type,
)
}
case X25519KeyAgreementKeyEIP5630:
if vm.GetPublicKeyMultibase() == "" {
return fmt.Errorf(
"publicKeyMultibase cannot be empty for verification method %s as it is of type %s",
vm.Id,
vm.Type,
)
}
if vm.GetBlockchainAccountId() != "" {
return fmt.Errorf(
"blockchainAccountId must be empty for verification method %s as it is of type %s",
vm.Id,
vm.Type,
)
}
default:
return fmt.Errorf("unsupported verification method type: %v", supportedVerificationMethodTypes)
}
Expand Down Expand Up @@ -268,10 +298,10 @@ func validateVerificationMethods(vms []*VerificationMethod) error {
}

func validateVmRelationships(didDoc *Did) error {
// make verificationMethods map
vmMap := map[string]bool{}
// make verificationMethodType map between VM Id and VM type
vmTypeMap := map[string]string{}
for _, vm := range didDoc.VerificationMethod {
vmMap[vm.Id] = true
vmTypeMap[vm.Id] = vm.Type
}

vmRelationshipList := map[string][]string{
Expand All @@ -284,18 +314,40 @@ func validateVmRelationships(didDoc *Did) error {

for field, vmRelationship := range vmRelationshipList {
// didUrl check and presence in verification methods
for _, element := range vmRelationship {
err := isDidUrl(element)
for _, vmId := range vmRelationship {
err := isDidUrl(vmId)
if err != nil {
return fmt.Errorf("%s: %s", field, err)
}
if _, found := vmMap[element]; !found {

if _, found := vmTypeMap[vmId]; !found {
return fmt.Errorf(
"%s: verification method id %s not found in verificationMethod list",
field,
element,
vmId,
)
}

// keyAgreement field should harbour only those Verification Methods whose type is either X25519KeyAgreementKey2020
// or X25519KeyAgreementKeyEIP5630
if (vmTypeMap[vmId] == X25519KeyAgreementKey2020) || (vmTypeMap[vmId] == X25519KeyAgreementKeyEIP5630) {
if (field != "keyAgreement") {
return fmt.Errorf(
"verification method id %v is of type %v which is not allowed in '%v' attribute",
vmId,
vmTypeMap[vmId],
field,
)
}
} else {
if (field == "keyAgreement") {
return fmt.Errorf(
"verification method id %v provided in '%v' attribute must be of type X25519KeyAgreementKey2020 or X25519KeyAgreementKeyEIP5630",
vmId,
field,
)
}
}
}
}

Expand Down
Loading

0 comments on commit 5e6e330

Please sign in to comment.