From 89e82e7c356bd76a779ac6a7186292714999bda0 Mon Sep 17 00:00:00 2001 From: Arnab Ghose Date: Mon, 13 Nov 2023 09:55:06 +0530 Subject: [PATCH 1/4] feat: added support for JSON-LD signing for VC status --- client/docs/swagger-ui/swagger.yaml | 36 +++++- cmd/hid-noded/cmd/debug_extensions.go | 36 +++++- proto/ssi/v1/credential_status.proto | 16 +-- tests/e2e/ssi_tests/generate_doc.py | 12 +- tests/e2e/ssi_tests/utils.py | 3 +- x/ssi/keeper/msg_server.go | 10 +- x/ssi/ld-context/context.go | 160 +++++++++++++++----------- x/ssi/ld-context/normalize.go | 77 ++++++++----- x/ssi/ld-context/types.go | 135 +++++++++++++--------- x/ssi/types/credential_status.pb.go | 146 ++++++++++++++++------- x/ssi/types/ssi_types.go | 9 +- x/ssi/verification/client_spec.go | 54 +++++---- 12 files changed, 439 insertions(+), 255 deletions(-) diff --git a/client/docs/swagger-ui/swagger.yaml b/client/docs/swagger-ui/swagger.yaml index 4a60502..0232e63 100644 --- a/client/docs/swagger-ui/swagger.yaml +++ b/client/docs/swagger-ui/swagger.yaml @@ -28,6 +28,10 @@ paths: credentialStatusDocument: type: object properties: + context: + type: array + items: + type: string id: type: string revoked: @@ -40,7 +44,7 @@ paths: type: string issuanceDate: type: string - merkleRootHash: + credentialMerkleRootHash: type: string credentialStatusProof: type: object @@ -159,6 +163,10 @@ paths: credentialStatusDocument: type: object properties: + context: + type: array + items: + type: string id: type: string revoked: @@ -171,7 +179,7 @@ paths: type: string issuanceDate: type: string - merkleRootHash: + credentialMerkleRootHash: type: string credentialStatusProof: type: object @@ -24666,6 +24674,10 @@ definitions: hypersign.ssi.v1.CredentialStatusDocument: type: object properties: + context: + type: array + items: + type: string id: type: string revoked: @@ -24678,7 +24690,7 @@ definitions: type: string issuanceDate: type: string - merkleRootHash: + credentialMerkleRootHash: type: string hypersign.ssi.v1.CredentialStatusState: type: object @@ -24686,6 +24698,10 @@ definitions: credentialStatusDocument: type: object properties: + context: + type: array + items: + type: string id: type: string revoked: @@ -24698,7 +24714,7 @@ definitions: type: string issuanceDate: type: string - merkleRootHash: + credentialMerkleRootHash: type: string credentialStatusProof: type: object @@ -25023,6 +25039,10 @@ definitions: credentialStatusDocument: type: object properties: + context: + type: array + items: + type: string id: type: string revoked: @@ -25035,7 +25055,7 @@ definitions: type: string issuanceDate: type: string - merkleRootHash: + credentialMerkleRootHash: type: string credentialStatusProof: type: object @@ -25071,6 +25091,10 @@ definitions: credentialStatusDocument: type: object properties: + context: + type: array + items: + type: string id: type: string revoked: @@ -25083,7 +25107,7 @@ definitions: type: string issuanceDate: type: string - merkleRootHash: + credentialMerkleRootHash: type: string credentialStatusProof: type: object diff --git a/cmd/hid-noded/cmd/debug_extensions.go b/cmd/hid-noded/cmd/debug_extensions.go index fa1f487..88f2d2b 100644 --- a/cmd/hid-noded/cmd/debug_extensions.go +++ b/cmd/hid-noded/cmd/debug_extensions.go @@ -475,13 +475,13 @@ func signSchemaDocCmd() *cobra.Command { func signCredStatusDocCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "cred-status-doc [doc] [private-key] [signing-algo]", + Use: "cred-status-doc [doc] [private-key] [proof-object-without-signature]", Short: "Credential Status Document signature", Args: cobra.ExactArgs(3), RunE: func(cmd *cobra.Command, args []string) error { argCredStatusDoc := args[0] argPrivateKey := args[1] - argSigningAlgo := args[2] + argProofObjectWithoutSignature := args[2] clientCtx, err := client.GetClientTxContext(cmd) if err != nil { @@ -494,33 +494,59 @@ func signCredStatusDocCmd() *cobra.Command { if err != nil { return err } - credStatusDocBytes := credStatusDoc.GetSignBytes() + + // Unmarshal Proof Object + var credStatusDocProof types.DocumentProof + err = clientCtx.Codec.UnmarshalJSON([]byte(argProofObjectWithoutSignature), &credStatusDocProof) + if err != nil { + return err + } // Sign Credential Status Document var signature string - switch argSigningAlgo { + switch credStatusDocProof.Type { case types.Ed25519Signature2020: + credStatusDocBytes, err := ldcontext.Ed25519Signature2020Normalize(&credStatusDoc, &credStatusDocProof) + if err != nil { + return err + } + signature, err = hidnodecli.GetEd25519Signature2020(argPrivateKey, credStatusDocBytes) if err != nil { return err } case types.EcdsaSecp256k1Signature2019: + credStatusDocBytes, err := ldcontext.EcdsaSecp256k1Signature2019Normalize(&credStatusDoc, &credStatusDocProof) + if err != nil { + return err + } + signature, err = hidnodecli.GetEcdsaSecp256k1Signature2019(argPrivateKey, credStatusDocBytes) if err != nil { return err } case types.EcdsaSecp256k1RecoverySignature2020: + credStatusDocBytes, err := ldcontext.EcdsaSecp256k1RecoverySignature2020Normalize(&credStatusDoc, &credStatusDocProof) + if err != nil { + return err + } + signature, err = hidnodecli.GetEcdsaSecp256k1RecoverySignature2020(argPrivateKey, credStatusDocBytes) if err != nil { return err } case types.BbsBlsSignature2020: + credStatusDocBytes, err := ldcontext.BbsBlsSignature2020Normalize(&credStatusDoc, &credStatusDocProof) + if err != nil { + return err + } + signature, err = hidnodecli.GetBbsBlsSignature2020(argPrivateKey, credStatusDocBytes) if err != nil { return err } case types.BabyJubJubSignature2023: - signature, err = hidnodecli.GetBabyJubJubSignature2023(argPrivateKey, credStatusDocBytes) + signature, err = hidnodecli.GetBabyJubJubSignature2023(argPrivateKey, credStatusDoc.GetSignBytes()) if err != nil { return err } diff --git a/proto/ssi/v1/credential_status.proto b/proto/ssi/v1/credential_status.proto index 434aba1..139c5fb 100644 --- a/proto/ssi/v1/credential_status.proto +++ b/proto/ssi/v1/credential_status.proto @@ -2,17 +2,19 @@ syntax = "proto3"; package hypersign.ssi.v1; import "ssi/v1/proof.proto"; +import "gogoproto/gogo.proto"; option go_package = "github.com/hypersign-protocol/hid-node/x/ssi/types"; message CredentialStatusDocument { - string id = 1; - bool revoked = 2; - bool suspended = 3; - string remarks = 4; - string issuer = 5; - string issuanceDate = 6; - string credentialMerkleRootHash = 7; + repeated string context = 1 [json_name = "@context", (gogoproto.jsontag) = "@context"]; + string id = 2; + bool revoked = 3; + bool suspended = 4; + string remarks = 5; + string issuer = 6; + string issuanceDate = 7; + string credentialMerkleRootHash = 8; } message CredentialStatusState { diff --git a/tests/e2e/ssi_tests/generate_doc.py b/tests/e2e/ssi_tests/generate_doc.py index 5e88eff..fb67d61 100644 --- a/tests/e2e/ssi_tests/generate_doc.py +++ b/tests/e2e/ssi_tests/generate_doc.py @@ -11,6 +11,7 @@ SECP256K1_RECOVERY_CONTEXT = "https://ns.did.ai/suites/secp256k1-2020/v1" SECP256K1_VER_KEY_2019_CONTEXT = "https://ns.did.ai/suites/secp256k1-2019/v1" BBS_CONTEXT = "https://ns.did.ai/suites/bls12381-2020/v1" +CREDENTIAL_STATUS_CONTEXT = "https://raw.githubusercontent.com/hypersign-protocol/hypersign-contexts/main/CredentialStatus.jsonld" def generate_did_document(key_pair, algo="Ed25519Signature2020", bech32prefix="hid", is_uuid=False): base_document = { @@ -151,6 +152,7 @@ def generate_schema_document(key_pair, schema_author, vm, signature=None, algo=" def generate_cred_status_document(key_pair, cred_author, vm, signature=None, algo="Ed25519Signature2020", updated_credstatus_doc=None): base_cred_status_doc = { + "@context": [CREDENTIAL_STATUS_CONTEXT], "id": "", "issuer": "did:hid:devnet:z3861habXtUFLNuu6J7m5p8VPsoBMduYbYeUxfx9CnWZR", "issuanceDate": "2022-08-16T09:37:12Z", @@ -160,17 +162,21 @@ def generate_cred_status_document(key_pair, cred_author, vm, signature=None, alg proof_type = "" if algo == "Ed25519Signature2020": proof_type = "Ed25519Signature2020" + base_cred_status_doc["@context"].append(ED25519_CONTEXT) elif algo == "EcdsaSecp256k1Signature2019": proof_type = "EcdsaSecp256k1Signature2019" + base_cred_status_doc["@context"].append(SECP256K1_VER_KEY_2019_CONTEXT) elif algo == "EcdsaSecp256k1RecoverySignature2020": proof_type = "EcdsaSecp256k1RecoverySignature2020" + base_cred_status_doc["@context"].append(SECP256K1_RECOVERY_CONTEXT) elif algo == "BbsBlsSignature2020": proof_type = "BbsBlsSignature2020" + base_cred_status_doc["@context"].append(BBS_CONTEXT) elif algo == "BabyJubJubSignature2023": proof_type = "BabyJubJubSignature2023" else: raise Exception("Invalid signing algo: " + algo) - + base_cred_status_proof = { "type": proof_type, "created": "2022-08-16T09:37:12Z", @@ -187,11 +193,11 @@ def generate_cred_status_document(key_pair, cred_author, vm, signature=None, alg # Form Signature if not updated_credstatus_doc: if not signature: - signature = get_document_signature(base_cred_status_doc, "cred-status", key_pair, algo) + signature = get_document_signature(base_cred_status_doc, "cred-status", key_pair, algo, proofObj=base_cred_status_proof) base_cred_status_proof["proofValue"] = signature return base_cred_status_doc, base_cred_status_proof else: if not signature: - signature = get_document_signature(updated_credstatus_doc, "cred-status", key_pair, algo) + signature = get_document_signature(updated_credstatus_doc, "cred-status", key_pair, algo, proofObj=base_cred_status_proof) base_cred_status_proof["proofValue"] = signature return updated_credstatus_doc, base_cred_status_proof \ No newline at end of file diff --git a/tests/e2e/ssi_tests/utils.py b/tests/e2e/ssi_tests/utils.py index 2089502..bf6c590 100644 --- a/tests/e2e/ssi_tests/utils.py +++ b/tests/e2e/ssi_tests/utils.py @@ -123,8 +123,7 @@ def get_document_signature(doc: dict, doc_type: str, key_pair: dict, algo: str = else: raise Exception("Invalid value for doc_type param: " + doc_type) - if doc_type == "did": - print() + if doc_type == "did" or doc_type == "cred-status": cmd_string = f"hid-noded debug sign-ssi-doc {doc_cmd} '{json.dumps(doc)}' {private_key} '{json.dumps(proofObj)}'" else: cmd_string = f"hid-noded debug sign-ssi-doc {doc_cmd} '{json.dumps(doc)}' {private_key} {algo}" diff --git a/x/ssi/keeper/msg_server.go b/x/ssi/keeper/msg_server.go index 26f89bc..fbb022c 100644 --- a/x/ssi/keeper/msg_server.go +++ b/x/ssi/keeper/msg_server.go @@ -185,7 +185,7 @@ 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) error { +func (k msgServer) VerifyDocumentProof(ctx sdk.Context, ssiMsg types.SsiMsg, inputDocProof *types.DocumentProof) error { // Get DID Document from State docProofVmId := inputDocProof.GetVerificationMethod() didId, _ := types.SplitDidUrl(docProofVmId) @@ -225,13 +225,7 @@ func (k msgServer) VerifyDocumentProof(ctx sdk.Context, ssiMsg types.SsiMsg, inp ) } - // Verify signature - documentProof := &types.DocumentProof{ - VerificationMethod: inputDocProof.GetVerificationMethod(), - ProofValue: inputDocProof.GetProofValue(), - ClientSpecType: inputDocProof.GetClientSpecType(), - } - err = verification.VerifyDocumentProofSignature(ssiMsg, docVm, documentProof) + err = verification.VerifyDocumentProofSignature(ssiMsg, docVm, inputDocProof) if err != nil { return err } diff --git a/x/ssi/ld-context/context.go b/x/ssi/ld-context/context.go index 6f2aaea..97eb1cb 100644 --- a/x/ssi/ld-context/context.go +++ b/x/ssi/ld-context/context.go @@ -7,6 +7,7 @@ const Secp256k1Recovery2020Context string = "https://ns.did.ai/suites/secp256k1- const BbsSignature2020Context string = "https://ns.did.ai/suites/bls12381-2020/v1" const Secp256k12019Context string = "https://ns.did.ai/suites/secp256k1-2019/v1" const X25519KeyAgreementKeyEIP5630Context string = "https://raw.githubusercontent.com/hypersign-protocol/hypersign-contexts/main/X25519KeyAgreementKeyEIP5630.jsonld" +const CredentialStatusContext string = "https://raw.githubusercontent.com/hypersign-protocol/hypersign-contexts/main/CredentialStatus.jsonld" // As hid-node is not supposed to perform any GET request, the complete Context body of their // respective Context urls has been maintained below. @@ -158,8 +159,8 @@ var ContextUrlMap map[string]contextObject = map[string]contextObject{ }, }, X25519KeyAgreement2020Context: { - "id": "@id", - "type": "@type", + "id": "@id", + "type": "@type", "@protected": true, "X25519KeyAgreementKey2020": map[string]interface{}{ "@id": "https://w3id.org/security#X25519KeyAgreementKey2020", @@ -183,8 +184,8 @@ var ContextUrlMap map[string]contextObject = map[string]contextObject{ }, }, Secp256k1Recovery2020Context: { - "id": "@id", - "type": "@type", + "id": "@id", + "type": "@type", "@protected": true, "EcdsaSecp256k1VerificationKey2020": map[string]interface{}{ "@id": "https://w3id.org/security#EcdsaSecp256k1VerificationKey2020", @@ -224,35 +225,35 @@ var ContextUrlMap map[string]contextObject = map[string]contextObject{ }, "nonce": "https://w3id.org/security#nonce", "proofPurpose": map[string]interface{}{ - "@id": "https://w3id.org/security#proofPurpose", - "@type": "@vocab", + "@id": "https://w3id.org/security#proofPurpose", + "@type": "@vocab", "@context": map[string]interface{}{ "@protected": true, "id": "@id", "type": "@type", "assertionMethod": map[string]interface{}{ - "@id": "https://w3id.org/security#assertionMethod", - "@type": "@id", + "@id": "https://w3id.org/security#assertionMethod", + "@type": "@id", "@container": "@set", }, "authentication": map[string]interface{}{ - "@id": "https://w3id.org/security#authenticationMethod", - "@type": "@id", + "@id": "https://w3id.org/security#authenticationMethod", + "@type": "@id", "@container": "@set", }, "capabilityInvocation": map[string]interface{}{ - "@id": "https://w3id.org/security#capabilityInvocationMethod", - "@type": "@id", + "@id": "https://w3id.org/security#capabilityInvocationMethod", + "@type": "@id", "@container": "@set", }, "capabilityDelegation": map[string]interface{}{ - "@id": "https://w3id.org/security#capabilityDelegationMethod", - "@type": "@id", + "@id": "https://w3id.org/security#capabilityDelegationMethod", + "@type": "@id", "@container": "@set", }, "keyAgreement": map[string]interface{}{ - "@id": "https://w3id.org/security#keyAgreementMethod", - "@type": "@id", + "@id": "https://w3id.org/security#keyAgreementMethod", + "@type": "@id", "@container": "@set", }, }, @@ -320,35 +321,35 @@ var ContextUrlMap map[string]contextObject = map[string]contextObject{ }, "nonce": "https://w3id.org/security#nonce", "proofPurpose": map[string]interface{}{ - "@id": "https://w3id.org/security#proofPurpose", - "@type": "@vocab", + "@id": "https://w3id.org/security#proofPurpose", + "@type": "@vocab", "@context": map[string]interface{}{ "@protected": true, "id": "@id", "type": "@type", "assertionMethod": map[string]interface{}{ - "@id": "https://w3id.org/security#assertionMethod", - "@type": "@id", + "@id": "https://w3id.org/security#assertionMethod", + "@type": "@id", "@container": "@set", }, "authentication": map[string]interface{}{ - "@id": "https://w3id.org/security#authenticationMethod", - "@type": "@id", + "@id": "https://w3id.org/security#authenticationMethod", + "@type": "@id", "@container": "@set", }, "capabilityInvocation": map[string]interface{}{ - "@id": "https://w3id.org/security#capabilityInvocationMethod", - "@type": "@id", + "@id": "https://w3id.org/security#capabilityInvocationMethod", + "@type": "@id", "@container": "@set", }, "capabilityDelegation": map[string]interface{}{ - "@id": "https://w3id.org/security#capabilityDelegationMethod", - "@type": "@id", + "@id": "https://w3id.org/security#capabilityDelegationMethod", + "@type": "@id", "@container": "@set", }, "keyAgreement": map[string]interface{}{ - "@id": "https://w3id.org/security#keyAgreementMethod", - "@type": "@id", + "@id": "https://w3id.org/security#keyAgreementMethod", + "@type": "@id", "@container": "@set", }, }, @@ -368,8 +369,8 @@ var ContextUrlMap map[string]contextObject = map[string]contextObject{ "id": "@id", "type": "@type", "proof": map[string]interface{}{ - "@id": "https://w3id.org/security#proof", - "@type": "@id", + "@id": "https://w3id.org/security#proof", + "@type": "@id", "@container": "@graph", }, "BbsBlsSignature2020": map[string]interface{}{ @@ -384,25 +385,25 @@ var ContextUrlMap map[string]contextObject = map[string]contextObject{ "@id": "http://purl.org/dc/terms/created", "@type": "http://www.w3.org/2001/XMLSchema#dateTime", }, - "domain": "https://w3id.org/security#domain", + "domain": "https://w3id.org/security#domain", "proofValue": "https://w3id.org/security#proofValue", - "nonce": "https://w3id.org/security#nonce", + "nonce": "https://w3id.org/security#nonce", "proofPurpose": map[string]interface{}{ - "@id": "https://w3id.org/security#proofPurpose", - "@type": "@vocab", + "@id": "https://w3id.org/security#proofPurpose", + "@type": "@vocab", "@context": map[string]interface{}{ "@version": 1.1, "@protected": true, "id": "@id", "type": "@type", "assertionMethod": map[string]interface{}{ - "@id": "https://w3id.org/security#assertionMethod", - "@type": "@id", + "@id": "https://w3id.org/security#assertionMethod", + "@type": "@id", "@container": "@set", }, "authentication": map[string]interface{}{ - "@id": "https://w3id.org/security#authenticationMethod", - "@type": "@id", + "@id": "https://w3id.org/security#authenticationMethod", + "@type": "@id", "@container": "@set", }, }, @@ -428,8 +429,8 @@ var ContextUrlMap map[string]contextObject = map[string]contextObject{ "domain": "https://w3id.org/security#domain", "nonce": "https://w3id.org/security#nonce", "proofPurpose": map[string]interface{}{ - "@id": "https://w3id.org/security#proofPurpose", - "@type": "@vocab", + "@id": "https://w3id.org/security#proofPurpose", + "@type": "@vocab", "@context": map[string]interface{}{ "@version": 1.1, "@protected": true, @@ -437,13 +438,13 @@ var ContextUrlMap map[string]contextObject = map[string]contextObject{ "type": "@type", "sec": "https://w3id.org/security#", "assertionMethod": map[string]interface{}{ - "@id": "https://w3id.org/security#assertionMethod", - "@type": "@id", + "@id": "https://w3id.org/security#assertionMethod", + "@type": "@id", "@container": "@set", }, "authentication": map[string]interface{}{ - "@id": "https://w3id.org/security#authenticationMethod", - "@type": "@id", + "@id": "https://w3id.org/security#authenticationMethod", + "@type": "@id", "@container": "@set", }, }, @@ -511,12 +512,12 @@ var ContextUrlMap map[string]contextObject = map[string]contextObject{ }, }, Secp256k12019Context: { - "id": "@id", - "type": "@type", + "id": "@id", + "type": "@type", "@protected": true, "proof": map[string]interface{}{ - "@id": "https://w3id.org/security#proof", - "@type": "@id", + "@id": "https://w3id.org/security#proof", + "@type": "@id", "@container": "@graph", }, "EcdsaSecp256k1VerificationKey2019": map[string]interface{}{ @@ -567,35 +568,35 @@ var ContextUrlMap map[string]contextObject = map[string]contextObject{ }, "nonce": "https://w3id.org/security#nonce", "proofPurpose": map[string]interface{}{ - "@id": "https://w3id.org/security#proofPurpose", - "@type": "@vocab", + "@id": "https://w3id.org/security#proofPurpose", + "@type": "@vocab", "@context": map[string]interface{}{ "@protected": true, "id": "@id", "type": "@type", "assertionMethod": map[string]interface{}{ - "@id": "https://w3id.org/security#assertionMethod", - "@type": "@id", + "@id": "https://w3id.org/security#assertionMethod", + "@type": "@id", "@container": "@set", }, "authentication": map[string]interface{}{ - "@id": "https://w3id.org/security#authenticationMethod", - "@type": "@id", + "@id": "https://w3id.org/security#authenticationMethod", + "@type": "@id", "@container": "@set", }, "capabilityInvocation": map[string]interface{}{ - "@id": "https://w3id.org/security#capabilityInvocationMethod", - "@type": "@id", + "@id": "https://w3id.org/security#capabilityInvocationMethod", + "@type": "@id", "@container": "@set", }, "capabilityDelegation": map[string]interface{}{ - "@id": "https://w3id.org/security#capabilityDelegationMethod", - "@type": "@id", + "@id": "https://w3id.org/security#capabilityDelegationMethod", + "@type": "@id", "@container": "@set", }, "keyAgreement": map[string]interface{}{ - "@id": "https://w3id.org/security#keyAgreementMethod", - "@type": "@id", + "@id": "https://w3id.org/security#keyAgreementMethod", + "@type": "@id", "@container": "@set", }, }, @@ -611,12 +612,12 @@ var ContextUrlMap map[string]contextObject = map[string]contextObject{ }, }, X25519KeyAgreementKeyEIP5630Context: { - "id": "@id", - "type": "@type", + "id": "@id", + "type": "@type", "@protected": true, "proof": map[string]interface{}{ - "@id": "https://w3id.org/security#proof", - "@type": "@id", + "@id": "https://w3id.org/security#proof", + "@type": "@id", "@container": "@graph", }, "X25519KeyAgreementKeyEIP5630": map[string]interface{}{ @@ -644,4 +645,35 @@ var ContextUrlMap map[string]contextObject = map[string]contextObject{ }, }, }, + CredentialStatusContext: { + "@protected": true, + "@version": 1.1, + "hypersign-vocab": "urn:uuid:13fe9318-bb82-4d95-8bf5-8e7fdf8b2026#", + "xsd": "http://www.w3.org/2001/XMLSchema#", + "id": "@id", + "revoked": map[string]interface{}{ + "@id": "hypersign-vocab:revoked", + "@type": "xsd:boolean", + }, + "suspended": map[string]interface{}{ + "@id": "hypersign-vocab:suspended", + "@type": "xsd:boolean", + }, + "remarks": map[string]interface{}{ + "@id": "hypersign-vocab:remarks", + "@type": "xsd:string", + }, + "issuer": map[string]interface{}{ + "@id": "hypersign-vocab:issuer", + "@type": "xsd:string", + }, + "issuanceDate": map[string]interface{}{ + "@id": "hypersign-vocab:issuanceDate", + "@type": "xsd:dateTime", + }, + "credentialMerkleRootHash": map[string]interface{}{ + "@id": "hypersign-vocab:credentialMerkleRootHash", + "@type": "xsd:string", + }, + }, } diff --git a/x/ssi/ld-context/normalize.go b/x/ssi/ld-context/normalize.go index 636cbc7..e938156 100644 --- a/x/ssi/ld-context/normalize.go +++ b/x/ssi/ld-context/normalize.go @@ -8,49 +8,70 @@ import ( // NormalizeByVerificationMethodType normalizes DID Document based on the input Verification // Method type -func NormalizeByVerificationMethodType(didDoc *types.DidDocument, vmType string, didDocumentProof *types.DocumentProof) ([]byte, error) { +func NormalizeByVerificationMethodType(ssiMsg types.SsiMsg, vmType string, didDocumentProof *types.DocumentProof) ([]byte, error) { switch vmType { case types.Ed25519VerificationKey2020: - didDocBytes, err := Ed25519Signature2020Normalize(didDoc, didDocumentProof) + msgBytes, err := Ed25519Signature2020Normalize(ssiMsg, didDocumentProof) if err != nil { return nil, err } - return didDocBytes, nil + return msgBytes, nil case types.EcdsaSecp256k1RecoveryMethod2020: - didDocBytes, err := EcdsaSecp256k1RecoverySignature2020Normalize(didDoc, didDocumentProof) + msgBytes, err := EcdsaSecp256k1RecoverySignature2020Normalize(ssiMsg, didDocumentProof) if err != nil { return nil, err } - return didDocBytes, nil + return msgBytes, nil case types.Bls12381G2Key2020: - didDocBytes, err := BbsBlsSignature2020Normalize(didDoc, didDocumentProof) + msgBytes, err := BbsBlsSignature2020Normalize(ssiMsg, didDocumentProof) if err != nil { return nil, err } - return didDocBytes, nil + return msgBytes, nil case types.EcdsaSecp256k1VerificationKey2019: - didDocBytes, err := EcdsaSecp256k1Signature2019Normalize(didDoc, didDocumentProof) + msgBytes, err := EcdsaSecp256k1Signature2019Normalize(ssiMsg, didDocumentProof) if err != nil { return nil, err } - return didDocBytes, nil + return msgBytes, nil default: - return didDoc.GetSignBytes(), nil + return ssiMsg.GetSignBytes(), nil } } -// normalizeDocumentWithProof normalizes the DidDocument along with Document Proof +// normalizeDocumentWithProof normalizes the SSI document along with Document Proof // Read more: https://w3c.github.io/vc-di-eddsa/#representation-ed25519signature2020 -func normalizeDocumentWithProof(didDoc *types.DidDocument, didDocProof *types.DocumentProof) ([]byte, error) { - jsonLdDid := NewJsonLdDid(didDoc) - canonizedDidDocument, err := jsonLdDid.NormalizeWithURDNA2015() - if err != nil { - return nil, err +func normalizeDocumentWithProof(msg types.SsiMsg, docProof *types.DocumentProof) ([]byte, error) { + // Normalize Document + var canonizedDocument string + var context []string + + switch doc := msg.(type) { + case *types.DidDocument: + var err error + jsonLdDidDocument := NewJsonLdDidDocument(doc) + canonizedDocument, err = normalizeWithURDNA2015(jsonLdDidDocument) + if err != nil { + return nil, err + } + context = doc.Context + case *types.CredentialStatusDocument: + var err error + jsonLdCredentialStatus := NewJsonLdCredentialStatus(doc) + canonizedDocument, err = normalizeWithURDNA2015(jsonLdCredentialStatus) + if err != nil { + return nil, err + } + context = doc.Context + case *types.CredentialSchemaDocument: + return doc.GetSignBytes(), nil } - canonizedDidDocumentHash := sha256.Sum256([]byte(canonizedDidDocument)) - jsonLdDocumentProof := NewJsonLdDocumentProof(didDocProof, didDoc.Context) - canonizedDocumentProof, err := jsonLdDocumentProof.NormalizeWithURDNA2015() + canonizedDocumentHash := sha256.Sum256([]byte(canonizedDocument)) + + // Normalize Document Proof + jsonLdDocumentProof := NewJsonLdDocumentProof(docProof, context) + canonizedDocumentProof, err := normalizeWithURDNA2015(jsonLdDocumentProof) if err != nil { return nil, err } @@ -59,34 +80,34 @@ func normalizeDocumentWithProof(didDoc *types.DidDocument, didDocProof *types.Do var finalNormalizedHash []byte = []byte{} // NOTE: The order is: ProofHash + DocumentHash finalNormalizedHash = append(finalNormalizedHash, canonizedDocumentProofHash[:]...) - finalNormalizedHash = append(finalNormalizedHash, canonizedDidDocumentHash[:]...) + finalNormalizedHash = append(finalNormalizedHash, canonizedDocumentHash[:]...) return finalNormalizedHash, nil } // Ed25519Signature2020Normalize normalizes DID Document in accordance with // EdDSA Cryptosuite v2020 (https://www.w3.org/community/reports/credentials/CG-FINAL-di-eddsa-2020-20220724/) -func Ed25519Signature2020Normalize(didDoc *types.DidDocument, didDocProof *types.DocumentProof) ([]byte, error) { - return normalizeDocumentWithProof(didDoc, didDocProof) +func Ed25519Signature2020Normalize(ssiMsg types.SsiMsg, didDocProof *types.DocumentProof) ([]byte, error) { + return normalizeDocumentWithProof(ssiMsg, didDocProof) } // EcdsaSecp256k1RecoverySignature2020Normalize normalizes DID Document in accordance with // the Identity Foundation draft on EcdsaSecp256k1RecoverySignature2020 // Read more: https://identity.foundation/EcdsaSecp256k1RecoverySignature2020/ -func EcdsaSecp256k1RecoverySignature2020Normalize(didDoc *types.DidDocument, didDocProof *types.DocumentProof) ([]byte, error) { - return normalizeDocumentWithProof(didDoc, didDocProof) +func EcdsaSecp256k1RecoverySignature2020Normalize(ssiMsg types.SsiMsg, didDocProof *types.DocumentProof) ([]byte, error) { + return normalizeDocumentWithProof(ssiMsg, didDocProof) } // BbsBlsSignature2020Normalize normalizes the DID Document for the // BbsBlsSignature2020 signature type // Read more: https://identity.foundation/bbs-signature/draft-irtf-cfrg-bbs-signatures.html -func BbsBlsSignature2020Normalize(didDoc *types.DidDocument, didDocProof *types.DocumentProof) ([]byte, error) { - return normalizeDocumentWithProof(didDoc, didDocProof) +func BbsBlsSignature2020Normalize(ssiMsg types.SsiMsg, didDocProof *types.DocumentProof) ([]byte, error) { + return normalizeDocumentWithProof(ssiMsg, didDocProof) } // EcdsaSecp256k1Signature2019Normalize normalizes the DID Document for the // EcdsaSecp256k1Signature2019 signature type // Read more: https://w3c-ccg.github.io/lds-ecdsa-secp256k1-2019/ -func EcdsaSecp256k1Signature2019Normalize(didDoc *types.DidDocument, didDocProof *types.DocumentProof) ([]byte, error) { - return normalizeDocumentWithProof(didDoc, didDocProof) +func EcdsaSecp256k1Signature2019Normalize(ssiMsg types.SsiMsg, didDocProof *types.DocumentProof) ([]byte, error) { + return normalizeDocumentWithProof(ssiMsg, didDocProof) } diff --git a/x/ssi/ld-context/types.go b/x/ssi/ld-context/types.go index 4a75944..3dbcac7 100644 --- a/x/ssi/ld-context/types.go +++ b/x/ssi/ld-context/types.go @@ -10,10 +10,14 @@ import ( type contextObject map[string]interface{} +type JsonLdDocument interface { + GetContext() []contextObject +} + // It is a similar to `Did` struct, with the exception that the `context` attribute is of type // `contextObject` instead of `[]string`, which is meant for accomodating Context JSON body // having arbritrary attributes. It should be used for performing Canonization. -type JsonLdDid struct { +type JsonLdDidDocument struct { Context []contextObject `json:"@context,omitempty"` Id string `json:"id,omitempty"` Controller []string `json:"controller,omitempty"` @@ -27,13 +31,17 @@ type JsonLdDid struct { Service []*types.Service `json:"service,omitempty"` } -// NewJsonLdDid returns a new JsonLdDid struct from input Did -func NewJsonLdDid(didDoc *types.DidDocument) *JsonLdDid { +func (doc *JsonLdDidDocument) GetContext() []contextObject { + return doc.Context +} + +// NewJsonLdDidDocument returns a new JsonLdDid struct from input Did +func NewJsonLdDidDocument(didDoc *types.DidDocument) *JsonLdDidDocument { if len(didDoc.Context) == 0 { panic("atleast one context url must be provided for DID Document for Canonization") } - var jsonLdDoc *JsonLdDid = &JsonLdDid{} + var jsonLdDoc *JsonLdDidDocument = &JsonLdDidDocument{} for _, url := range didDoc.Context { contextObj, ok := ContextUrlMap[url] @@ -57,44 +65,52 @@ func NewJsonLdDid(didDoc *types.DidDocument) *JsonLdDid { return jsonLdDoc } -// Convert JsonLdDid to interface -func jsonLdDidToInterface(jsonLd *JsonLdDid) interface{} { - var intf interface{} +// It is a similar to `Did` struct, with the exception that the `context` attribute is of type +// `contextObject` instead of `[]string`, which is meant for accomodating Context JSON body +// having arbritrary attributes. It should be used for performing Canonization. +type JsonLdCredentialStatus struct { + Context []contextObject `json:"@context,omitempty"` + Id string `json:"id,omitempty"` + Revoked bool `json:"revoked,omitempty"` + Suspended bool `json:"suspended,omitempty"` + Remarks string `json:"remarks,omitempty"` + Issuer string `json:"issuer,omitempty"` + IssuanceDate string `json:"issuanceDate,omitempty"` + CredentialMerkleRootHash string `json:"credentialMerkleRootHash,omitempty"` +} - jsonLdBytes, err := json.Marshal(jsonLd) - if err != nil { - panic(err) - } +func (doc *JsonLdCredentialStatus) GetContext() []contextObject { + return doc.Context +} - err = json.Unmarshal(jsonLdBytes, &intf) - if err != nil { - panic(err) +// NewJsonLdCredentialStatus returns a new JsonLdCredentialStatus struct from input Credential Status +func NewJsonLdCredentialStatus(credStatusDoc *types.CredentialStatusDocument) *JsonLdCredentialStatus { + if len(credStatusDoc.Context) == 0 { + panic("atleast one context url must be provided in the Credential Status Document for Canonization") } - return intf -} + var jsonLdCredentialStatus *JsonLdCredentialStatus = &JsonLdCredentialStatus{} -// NormalizeWithURDNA2015 performs RDF Canonization upon JsonLdDid using URDNA2015 -// algorithm and returns the canonized document in string -func (jsonLd *JsonLdDid) NormalizeWithURDNA2015() (string, error) { - proc := ld.NewJsonLdProcessor() - options := ld.NewJsonLdOptions("") - options.Algorithm = ld.AlgorithmURDNA2015 - options.Format = "application/n-quads" - - normalisedJsonLdDid, err := proc.Normalize(jsonLdDidToInterface(jsonLd), options) - if err != nil { - return "", fmt.Errorf("unable to Normalize DID Document: %v", err.Error()) + for _, url := range credStatusDoc.Context { + contextObj, ok := ContextUrlMap[url] + if !ok { + panic(fmt.Sprintf("invalid or unsupported context url: %v", url)) + } + jsonLdCredentialStatus.Context = append(jsonLdCredentialStatus.Context, contextObj) } - canonizedDocString := normalisedJsonLdDid.(string) - if canonizedDocString == "" { - return "", fmt.Errorf("normalization yield empty RDF string for did document: %v", jsonLd.Id) - } - return canonizedDocString, nil + jsonLdCredentialStatus.Id = credStatusDoc.Id + jsonLdCredentialStatus.Revoked = credStatusDoc.Revoked + jsonLdCredentialStatus.Remarks = credStatusDoc.Remarks + jsonLdCredentialStatus.Suspended = credStatusDoc.Suspended + jsonLdCredentialStatus.Issuer = credStatusDoc.Issuer + jsonLdCredentialStatus.IssuanceDate = credStatusDoc.IssuanceDate + jsonLdCredentialStatus.CredentialMerkleRootHash = credStatusDoc.CredentialMerkleRootHash + + return jsonLdCredentialStatus } -// ------------------------- +// Document Proof type JsonLdDocumentProof struct { Context []contextObject `json:"@context,omitempty"` @@ -104,7 +120,11 @@ type JsonLdDocumentProof struct { ProofPurpose string `json:"proofPurpose,omitempty"` } -func NewJsonLdDocumentProof(didDocProof *types.DocumentProof, didContexts []string) *JsonLdDocumentProof { +func (doc *JsonLdDocumentProof) GetContext() []contextObject { + return doc.Context +} + +func NewJsonLdDocumentProof(didDocProof *types.DocumentProof, didContexts []string) *JsonLdDocumentProof { if len(didContexts) == 0 { panic("atleast one context url must be provided for DID Document for Canonization") } @@ -127,8 +147,33 @@ func NewJsonLdDocumentProof(didDocProof *types.DocumentProof, didContexts []stri return jsonLdDoc } +// normalizeWithURDNA2015 performs RDF Canonization upon JsonLdDid using URDNA2015 +// algorithm and returns the canonized document in string +func normalizeWithURDNA2015(jsonLdDocument JsonLdDocument) (string, error) { + return normalize(ld.AlgorithmURDNA2015, jsonLdDocument) +} + +func normalize(algorithm string, jsonLdDocument JsonLdDocument) (string, error) { + proc := ld.NewJsonLdProcessor() + options := ld.NewJsonLdOptions("") + options.Algorithm = algorithm // ld.AlgorithmURDNA2015 + options.Format = "application/n-quads" + + normalisedJsonLd, err := proc.Normalize(jsonLdDocToInterface(jsonLdDocument), options) + if err != nil { + return "", fmt.Errorf("unable to Normalize DID Document: %v", err.Error()) + } + + canonizedDocString := normalisedJsonLd.(string) + if canonizedDocString == "" { + return "", fmt.Errorf("normalization of JSON-LD document yielded empty RDF string") + } + + return canonizedDocString, nil +} + // Convert JsonLdDid to interface -func jsonLdDocumentProofToInterface(jsonLd *JsonLdDocumentProof) interface{} { +func jsonLdDocToInterface(jsonLd any) interface{} { var intf interface{} jsonLdBytes, err := json.Marshal(jsonLd) @@ -143,23 +188,3 @@ func jsonLdDocumentProofToInterface(jsonLd *JsonLdDocumentProof) interface{} { return intf } - -// NormalizeWithURDNA2015 performs RDF Canonization upon JsonLdDid using URDNA2015 -// algorithm and returns the canonized document in string -func (jsonLd *JsonLdDocumentProof) NormalizeWithURDNA2015() (string, error) { - proc := ld.NewJsonLdProcessor() - options := ld.NewJsonLdOptions("") - options.Algorithm = ld.AlgorithmURDNA2015 - options.Format = "application/n-quads" - - normalisedJsonLdDocumentProof, err := proc.Normalize(jsonLdDocumentProofToInterface(jsonLd), options) - if err != nil { - return "", fmt.Errorf("unable to Normalize Document Proof: %v", err.Error()) - } - - canonizedDocString := normalisedJsonLdDocumentProof.(string) - if canonizedDocString == "" { - return "", fmt.Errorf("normalization yield empty RDF string for proof") - } - return canonizedDocString, nil -} diff --git a/x/ssi/types/credential_status.pb.go b/x/ssi/types/credential_status.pb.go index b5fba7d..be8345c 100644 --- a/x/ssi/types/credential_status.pb.go +++ b/x/ssi/types/credential_status.pb.go @@ -5,6 +5,7 @@ package types import ( fmt "fmt" + _ "github.com/gogo/protobuf/gogoproto" proto "github.com/gogo/protobuf/proto" io "io" math "math" @@ -23,13 +24,14 @@ var _ = math.Inf const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type CredentialStatusDocument struct { - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Revoked bool `protobuf:"varint,2,opt,name=revoked,proto3" json:"revoked,omitempty"` - Suspended bool `protobuf:"varint,3,opt,name=suspended,proto3" json:"suspended,omitempty"` - Remarks string `protobuf:"bytes,4,opt,name=remarks,proto3" json:"remarks,omitempty"` - Issuer string `protobuf:"bytes,5,opt,name=issuer,proto3" json:"issuer,omitempty"` - IssuanceDate string `protobuf:"bytes,6,opt,name=issuanceDate,proto3" json:"issuanceDate,omitempty"` - CredentialMerkleRootHash string `protobuf:"bytes,7,opt,name=credentialMerkleRootHash,proto3" json:"credentialMerkleRootHash,omitempty"` + Context []string `protobuf:"bytes,1,rep,name=context,json=@context,proto3" json:"@context"` + Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` + Revoked bool `protobuf:"varint,3,opt,name=revoked,proto3" json:"revoked,omitempty"` + Suspended bool `protobuf:"varint,4,opt,name=suspended,proto3" json:"suspended,omitempty"` + Remarks string `protobuf:"bytes,5,opt,name=remarks,proto3" json:"remarks,omitempty"` + Issuer string `protobuf:"bytes,6,opt,name=issuer,proto3" json:"issuer,omitempty"` + IssuanceDate string `protobuf:"bytes,7,opt,name=issuanceDate,proto3" json:"issuanceDate,omitempty"` + CredentialMerkleRootHash string `protobuf:"bytes,8,opt,name=credentialMerkleRootHash,proto3" json:"credentialMerkleRootHash,omitempty"` } func (m *CredentialStatusDocument) Reset() { *m = CredentialStatusDocument{} } @@ -65,6 +67,13 @@ func (m *CredentialStatusDocument) XXX_DiscardUnknown() { var xxx_messageInfo_CredentialStatusDocument proto.InternalMessageInfo +func (m *CredentialStatusDocument) GetContext() []string { + if m != nil { + return m.Context + } + return nil +} + func (m *CredentialStatusDocument) GetId() string { if m != nil { return m.Id @@ -174,30 +183,32 @@ func init() { func init() { proto.RegisterFile("ssi/v1/credential_status.proto", fileDescriptor_9807097035021c62) } var fileDescriptor_9807097035021c62 = []byte{ - // 353 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x52, 0xcd, 0x4e, 0x3a, 0x31, - 0x10, 0xa7, 0xfc, 0xff, 0x82, 0x14, 0x63, 0x4c, 0x13, 0x4c, 0x63, 0xcc, 0x4a, 0x38, 0x11, 0x13, - 0x76, 0x03, 0xde, 0x3c, 0x2a, 0x07, 0x0f, 0x9a, 0x98, 0x35, 0x5e, 0xbc, 0x98, 0x65, 0x3b, 0xb0, - 0x0d, 0xb0, 0xdd, 0x74, 0xba, 0x44, 0xde, 0xc2, 0xc7, 0xf2, 0xc8, 0xc9, 0x78, 0x34, 0xf0, 0x0a, - 0x3e, 0x80, 0xd9, 0xf2, 0x25, 0x28, 0x97, 0xa6, 0x33, 0xbf, 0x8f, 0x66, 0x7e, 0x1d, 0xea, 0x20, - 0x4a, 0x6f, 0xd4, 0xf4, 0x42, 0x0d, 0x02, 0x62, 0x23, 0x83, 0xc1, 0x33, 0x9a, 0xc0, 0xa4, 0xe8, - 0x26, 0x5a, 0x19, 0xc5, 0x8e, 0xa2, 0x71, 0x02, 0x1a, 0x65, 0x2f, 0x76, 0x11, 0xa5, 0x3b, 0x6a, - 0x9e, 0xb0, 0x85, 0x22, 0xd1, 0x4a, 0x75, 0xe7, 0xac, 0xda, 0x17, 0xa1, 0xfc, 0x7a, 0xe5, 0xf0, - 0x60, 0x0d, 0xda, 0x2a, 0x4c, 0x87, 0x10, 0x1b, 0x76, 0x48, 0xf3, 0x52, 0x70, 0x52, 0x25, 0xf5, - 0x92, 0x9f, 0x97, 0x82, 0x71, 0x5a, 0xd4, 0x30, 0x52, 0x7d, 0x10, 0x3c, 0x5f, 0x25, 0xf5, 0x7d, - 0x7f, 0x59, 0xb2, 0x53, 0x5a, 0xc2, 0x14, 0x13, 0x88, 0x05, 0x08, 0xfe, 0xcf, 0x62, 0xeb, 0xc6, - 0x5c, 0x37, 0x0c, 0x74, 0x1f, 0xf9, 0x7f, 0x6b, 0xb6, 0x2c, 0xd9, 0x31, 0x2d, 0x48, 0xc4, 0x14, - 0x34, 0xdf, 0xb3, 0xc0, 0xa2, 0x62, 0x35, 0x7a, 0x90, 0xdd, 0x82, 0x38, 0x84, 0x76, 0x60, 0x80, - 0x17, 0x2c, 0xba, 0xd1, 0x63, 0x97, 0x94, 0xaf, 0x67, 0xbf, 0x03, 0xdd, 0x1f, 0x80, 0xaf, 0x94, - 0xb9, 0x09, 0x30, 0xe2, 0x45, 0xcb, 0xdf, 0x89, 0xd7, 0xde, 0x09, 0xad, 0x6c, 0x8f, 0x9d, 0x9d, - 0xc0, 0xba, 0x3f, 0x5d, 0x37, 0xf3, 0xb0, 0x49, 0x94, 0x5b, 0xe7, 0xee, 0x76, 0xb2, 0xee, 0xae, - 0x04, 0xfd, 0x9d, 0x5e, 0xec, 0x91, 0x56, 0xb6, 0xb1, 0xfb, 0xec, 0x5f, 0x6c, 0xb2, 0xe5, 0xd6, - 0xd9, 0xef, 0x47, 0x96, 0x52, 0x4b, 0xf3, 0xff, 0x56, 0x5f, 0xdd, 0xbe, 0x4d, 0x1d, 0x32, 0x99, - 0x3a, 0xe4, 0x73, 0xea, 0x90, 0xd7, 0x99, 0x93, 0x9b, 0xcc, 0x9c, 0xdc, 0xc7, 0xcc, 0xc9, 0x3d, - 0xb5, 0x7a, 0xd2, 0x44, 0x69, 0xc7, 0x0d, 0xd5, 0xd0, 0x5b, 0x79, 0x37, 0xec, 0x16, 0x84, 0x6a, - 0xe0, 0x45, 0x52, 0x34, 0x62, 0x25, 0xc0, 0x7b, 0xf1, 0xb2, 0x35, 0x31, 0xe3, 0x04, 0xb0, 0x53, - 0xb0, 0xf0, 0xc5, 0x77, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfd, 0x00, 0x93, 0x35, 0x6c, 0x02, 0x00, - 0x00, + // 396 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x52, 0xcd, 0xaa, 0xda, 0x40, + 0x18, 0x75, 0x62, 0xeb, 0xcf, 0x28, 0xa5, 0x0c, 0xb5, 0x0c, 0x52, 0x62, 0x70, 0xd3, 0x50, 0x30, + 0x41, 0xbb, 0xeb, 0xaa, 0x58, 0x17, 0x5d, 0xb4, 0x50, 0x52, 0xba, 0xe9, 0xa6, 0xc4, 0xcc, 0x67, + 0x32, 0xa8, 0x99, 0x30, 0x33, 0x11, 0x7d, 0x8b, 0xbe, 0x40, 0xdf, 0xa7, 0x4b, 0x57, 0xa5, 0xab, + 0xcb, 0x45, 0x77, 0xf7, 0x29, 0x2e, 0x19, 0xff, 0xae, 0xde, 0xeb, 0x26, 0x9c, 0xef, 0x9c, 0xef, + 0x9c, 0xc0, 0x99, 0x0f, 0xdb, 0x4a, 0x71, 0x7f, 0xd1, 0xf7, 0x23, 0x09, 0x0c, 0x52, 0xcd, 0xc3, + 0xd9, 0x2f, 0xa5, 0x43, 0x9d, 0x2b, 0x2f, 0x93, 0x42, 0x0b, 0xf2, 0x32, 0x59, 0x65, 0x20, 0x15, + 0x8f, 0x53, 0x4f, 0x29, 0xee, 0x2d, 0xfa, 0x6d, 0xb2, 0x77, 0x64, 0x52, 0x88, 0xc9, 0x6e, 0xab, + 0xfd, 0x2a, 0x16, 0xb1, 0x30, 0xd0, 0x2f, 0xd0, 0x8e, 0xed, 0xfe, 0xb1, 0x30, 0xfd, 0x74, 0xcc, + 0xfd, 0x6e, 0x62, 0x47, 0x22, 0xca, 0xe7, 0x90, 0x6a, 0xf2, 0x16, 0x57, 0x23, 0x91, 0x6a, 0x58, + 0x6a, 0x8a, 0x9c, 0xb2, 0x5b, 0x1f, 0x36, 0xef, 0x6e, 0x3a, 0xb5, 0x8f, 0x7b, 0x2e, 0x38, 0x22, + 0xf2, 0x02, 0x5b, 0x9c, 0x51, 0xcb, 0x41, 0x6e, 0x3d, 0xb0, 0x38, 0x23, 0x14, 0x57, 0x25, 0x2c, + 0xc4, 0x14, 0x18, 0x2d, 0x3b, 0xc8, 0xad, 0x05, 0x87, 0x91, 0xbc, 0xc1, 0x75, 0x95, 0xab, 0x0c, + 0x52, 0x06, 0x8c, 0x3e, 0x33, 0xda, 0x89, 0xd8, 0xf9, 0xe6, 0xa1, 0x9c, 0x2a, 0xfa, 0xdc, 0x84, + 0x1d, 0x46, 0xf2, 0x1a, 0x57, 0xb8, 0x52, 0x39, 0x48, 0x5a, 0x31, 0xc2, 0x7e, 0x22, 0x5d, 0xdc, + 0x2c, 0x50, 0x98, 0x46, 0x30, 0x0a, 0x35, 0xd0, 0xaa, 0x51, 0xcf, 0x38, 0xf2, 0x01, 0xd3, 0x53, + 0x75, 0x5f, 0x41, 0x4e, 0x67, 0x10, 0x08, 0xa1, 0x3f, 0x87, 0x2a, 0xa1, 0x35, 0xb3, 0x7f, 0x55, + 0xef, 0xfe, 0x43, 0xb8, 0x75, 0xd9, 0x4f, 0xf1, 0x05, 0x32, 0x79, 0x98, 0x7a, 0x5e, 0x1c, 0x45, + 0x0e, 0x72, 0x1b, 0x83, 0x77, 0xde, 0xe5, 0xc3, 0x78, 0xd7, 0xaa, 0x0e, 0xae, 0x66, 0x91, 0x1f, + 0xb8, 0x75, 0xa9, 0x7d, 0x2b, 0x9e, 0xd5, 0xd4, 0xdd, 0x18, 0x74, 0x1e, 0xff, 0xe4, 0x60, 0x35, + 0x6b, 0xc1, 0xd3, 0xee, 0xe1, 0x97, 0xbf, 0x1b, 0x1b, 0xad, 0x37, 0x36, 0xba, 0xdd, 0xd8, 0xe8, + 0xf7, 0xd6, 0x2e, 0xad, 0xb7, 0x76, 0xe9, 0xff, 0xd6, 0x2e, 0xfd, 0x1c, 0xc4, 0x5c, 0x27, 0xf9, + 0xd8, 0x8b, 0xc4, 0xdc, 0x3f, 0x66, 0xf7, 0xcc, 0xb9, 0x44, 0x62, 0xe6, 0x27, 0x9c, 0xf5, 0x52, + 0xc1, 0xc0, 0x5f, 0xfa, 0xc5, 0x95, 0xe9, 0x55, 0x06, 0x6a, 0x5c, 0x31, 0xf2, 0xfb, 0xfb, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xb5, 0x03, 0xc2, 0x4b, 0xab, 0x02, 0x00, 0x00, } func (m *CredentialStatusDocument) Marshal() (dAtA []byte, err error) { @@ -225,28 +236,28 @@ func (m *CredentialStatusDocument) MarshalToSizedBuffer(dAtA []byte) (int, error copy(dAtA[i:], m.CredentialMerkleRootHash) i = encodeVarintCredentialStatus(dAtA, i, uint64(len(m.CredentialMerkleRootHash))) i-- - dAtA[i] = 0x3a + dAtA[i] = 0x42 } if len(m.IssuanceDate) > 0 { i -= len(m.IssuanceDate) copy(dAtA[i:], m.IssuanceDate) i = encodeVarintCredentialStatus(dAtA, i, uint64(len(m.IssuanceDate))) i-- - dAtA[i] = 0x32 + dAtA[i] = 0x3a } if len(m.Issuer) > 0 { i -= len(m.Issuer) copy(dAtA[i:], m.Issuer) i = encodeVarintCredentialStatus(dAtA, i, uint64(len(m.Issuer))) i-- - dAtA[i] = 0x2a + dAtA[i] = 0x32 } if len(m.Remarks) > 0 { i -= len(m.Remarks) copy(dAtA[i:], m.Remarks) i = encodeVarintCredentialStatus(dAtA, i, uint64(len(m.Remarks))) i-- - dAtA[i] = 0x22 + dAtA[i] = 0x2a } if m.Suspended { i-- @@ -256,7 +267,7 @@ func (m *CredentialStatusDocument) MarshalToSizedBuffer(dAtA []byte) (int, error dAtA[i] = 0 } i-- - dAtA[i] = 0x18 + dAtA[i] = 0x20 } if m.Revoked { i-- @@ -266,14 +277,23 @@ func (m *CredentialStatusDocument) MarshalToSizedBuffer(dAtA []byte) (int, error dAtA[i] = 0 } i-- - dAtA[i] = 0x10 + dAtA[i] = 0x18 } if len(m.Id) > 0 { i -= len(m.Id) copy(dAtA[i:], m.Id) i = encodeVarintCredentialStatus(dAtA, i, uint64(len(m.Id))) i-- - dAtA[i] = 0xa + dAtA[i] = 0x12 + } + if len(m.Context) > 0 { + for iNdEx := len(m.Context) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Context[iNdEx]) + copy(dAtA[i:], m.Context[iNdEx]) + i = encodeVarintCredentialStatus(dAtA, i, uint64(len(m.Context[iNdEx]))) + i-- + dAtA[i] = 0xa + } } return len(dAtA) - i, nil } @@ -342,6 +362,12 @@ func (m *CredentialStatusDocument) Size() (n int) { } var l int _ = l + if len(m.Context) > 0 { + for _, s := range m.Context { + l = len(s) + n += 1 + l + sovCredentialStatus(uint64(l)) + } + } l = len(m.Id) if l > 0 { n += 1 + l + sovCredentialStatus(uint64(l)) @@ -424,6 +450,38 @@ func (m *CredentialStatusDocument) Unmarshal(dAtA []byte) error { } switch fieldNum { case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Context", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCredentialStatus + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthCredentialStatus + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthCredentialStatus + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Context = append(m.Context, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) } @@ -455,7 +513,7 @@ func (m *CredentialStatusDocument) Unmarshal(dAtA []byte) error { } m.Id = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 2: + case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Revoked", wireType) } @@ -475,7 +533,7 @@ func (m *CredentialStatusDocument) Unmarshal(dAtA []byte) error { } } m.Revoked = bool(v != 0) - case 3: + case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Suspended", wireType) } @@ -495,7 +553,7 @@ func (m *CredentialStatusDocument) Unmarshal(dAtA []byte) error { } } m.Suspended = bool(v != 0) - case 4: + case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Remarks", wireType) } @@ -527,7 +585,7 @@ func (m *CredentialStatusDocument) Unmarshal(dAtA []byte) error { } m.Remarks = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 5: + case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Issuer", wireType) } @@ -559,7 +617,7 @@ func (m *CredentialStatusDocument) Unmarshal(dAtA []byte) error { } m.Issuer = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 6: + case 7: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field IssuanceDate", wireType) } @@ -591,7 +649,7 @@ func (m *CredentialStatusDocument) Unmarshal(dAtA []byte) error { } m.IssuanceDate = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 7: + case 8: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field CredentialMerkleRootHash", wireType) } diff --git a/x/ssi/types/ssi_types.go b/x/ssi/types/ssi_types.go index 6ca2a7c..613ed46 100644 --- a/x/ssi/types/ssi_types.go +++ b/x/ssi/types/ssi_types.go @@ -11,6 +11,7 @@ import ( type ( SsiMsg interface { proto.Message + GetId() string GetSignBytes() []byte } @@ -87,14 +88,6 @@ type ( } ) -// Handle Proof Struct of SSI Docs -type SSIProofInterface interface { - GetProofValue() string - GetType() string - GetVerificationMethod() string - GetClientSpecType() ClientSpecType -} - // CAIP-10 Blockchain Account Id type BlockchainId struct { CAIP10Prefix string diff --git a/x/ssi/verification/client_spec.go b/x/ssi/verification/client_spec.go index c565ca2..daa8b47 100644 --- a/x/ssi/verification/client_spec.go +++ b/x/ssi/verification/client_spec.go @@ -56,43 +56,47 @@ func getPersonalSignSpecDocBytes(ssiMsg types.SsiMsg) ([]byte, error) { func getDocBytesByClientSpec(ssiMsg types.SsiMsg, extendedVm *types.ExtendedVerificationMethod) ([]byte, error) { switch extendedVm.Proof.ClientSpecType { case types.CLIENT_SPEC_TYPE_NONE: - if didDoc, ok := ssiMsg.(*types.DidDocument); ok && len(didDoc.Context) > 0 { - return ldcontext.NormalizeByVerificationMethodType(didDoc, extendedVm.Type, extendedVm.Proof) + if didDoc, ok := ssiMsg.(*types.DidDocument); ok && len(didDoc.Context) == 0 { + return ssiMsg.GetSignBytes(), nil } - return ssiMsg.GetSignBytes(), nil + + return ldcontext.NormalizeByVerificationMethodType(ssiMsg, extendedVm.Type, extendedVm.Proof) case types.CLIENT_SPEC_TYPE_COSMOS_ADR036: signerAddress, err := getBlockchainAddress(extendedVm.BlockchainAccountId) if err != nil { return nil, err } - if didDoc, ok := ssiMsg.(*types.DidDocument); ok && len(didDoc.Context) > 0 { - canonizedDidDocHash, err := ldcontext.EcdsaSecp256k1Signature2019Normalize(didDoc, extendedVm.Proof) - if err != nil { - return nil, err - } + if didDoc, ok := ssiMsg.(*types.DidDocument); ok && len(didDoc.Context) == 0 { + return getCosmosADR036SignDocBytes(ssiMsg.GetSignBytes(), signerAddress) + } - return getCosmosADR036SignDocBytes(canonizedDidDocHash, signerAddress) + canonizedDidDocHash, err := ldcontext.EcdsaSecp256k1Signature2019Normalize(ssiMsg, extendedVm.Proof) + if err != nil { + return nil, err } - return getCosmosADR036SignDocBytes(ssiMsg.GetSignBytes(), signerAddress) + + return getCosmosADR036SignDocBytes(canonizedDidDocHash, signerAddress) + case types.CLIENT_SPEC_TYPE_ETH_PERSONAL_SIGN: - if didDoc, ok := ssiMsg.(*types.DidDocument); ok && len(didDoc.Context) > 0 { - canonizedDidDocHash, err := ldcontext.EcdsaSecp256k1RecoverySignature2020Normalize(didDoc, extendedVm.Proof) - if err != nil { - return nil, err - } + if didDoc, ok := ssiMsg.(*types.DidDocument); ok && len(didDoc.Context) == 0 { + return getPersonalSignSpecDocBytes(ssiMsg) + } - // TODO: This is temporary fix eth.personal.sign() client function, since it only signs JSON - // stringified document and hence the following struct was used to sign from the Client end. - return json.Marshal(struct { - DidId string `json:"didId"` - DidDocDigest string `json:"didDocDigest"` - }{ - DidId: didDoc.Id, - DidDocDigest: hex.EncodeToString(canonizedDidDocHash), - }) + canonizedDidDocHash, err := ldcontext.EcdsaSecp256k1RecoverySignature2020Normalize(ssiMsg, extendedVm.Proof) + if err != nil { + return nil, err } - return getPersonalSignSpecDocBytes(ssiMsg) + + // TODO: This is temporary fix eth.personal.sign() client function, since it only signs JSON + // stringified document and hence the following struct was used to sign from the Client end. + return json.Marshal(struct { + DidId string `json:"didId"` + DidDocDigest string `json:"didDocDigest"` + }{ + DidId: ssiMsg.GetId(), + DidDocDigest: hex.EncodeToString(canonizedDidDocHash), + }) default: return nil, fmt.Errorf("unsupported clientSpecType %v", extendedVm.Proof.ClientSpecType) } From 0c4abb15831a22b5d00350d388d659eb93dd3ec1 Mon Sep 17 00:00:00 2001 From: Arnab Ghose Date: Mon, 13 Nov 2023 12:52:43 +0530 Subject: [PATCH 2/4] feat: added JSON-LD signing support for credential schema --- cmd/hid-noded/cmd/debug_extensions.go | 49 ++++++-- proto/ssi/v1/credential_schema.proto | 16 +-- tests/e2e/ssi_tests/generate_doc.py | 10 +- tests/e2e/ssi_tests/utils.py | 5 +- x/ssi/ld-context/context.go | 46 ++++++++ x/ssi/ld-context/normalize.go | 53 ++++++++- x/ssi/ld-context/types.go | 69 ++++++------ x/ssi/types/credential_schema.pb.go | 154 ++++++++++++++++++-------- 8 files changed, 296 insertions(+), 106 deletions(-) diff --git a/cmd/hid-noded/cmd/debug_extensions.go b/cmd/hid-noded/cmd/debug_extensions.go index 88f2d2b..dcd73de 100644 --- a/cmd/hid-noded/cmd/debug_extensions.go +++ b/cmd/hid-noded/cmd/debug_extensions.go @@ -413,13 +413,13 @@ func signDidDocCmd() *cobra.Command { func signSchemaDocCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "schema-doc [doc] [private-key] [signing-algo]", + Use: "schema-doc [doc] [private-key] [proof-object-without-signature]", Short: "Schema Document signature", Args: cobra.ExactArgs(3), RunE: func(cmd *cobra.Command, args []string) error { argSchemaDoc := args[0] argPrivateKey := args[1] - argSigningAlgo := args[2] + argProofObjectWithoutSignature := args[2] clientCtx, err := client.GetClientTxContext(cmd) if err != nil { @@ -432,38 +432,67 @@ func signSchemaDocCmd() *cobra.Command { if err != nil { return err } - schemaDocBytes := schemaDoc.GetSignBytes() + + // Unmarshal Proof Object + var credSchemaDocProof types.DocumentProof + err = clientCtx.Codec.UnmarshalJSON([]byte(argProofObjectWithoutSignature), &credSchemaDocProof) + if err != nil { + return err + } // Sign Schema Document var signature string - switch argSigningAlgo { + switch credSchemaDocProof.Type { case types.Ed25519Signature2020: - signature, err = hidnodecli.GetEd25519Signature2020(argPrivateKey, schemaDocBytes) + credSchemaDocBytes, err := ldcontext.Ed25519Signature2020Normalize(&schemaDoc, &credSchemaDocProof) + if err != nil { + return err + } + + signature, err = hidnodecli.GetEd25519Signature2020(argPrivateKey, credSchemaDocBytes) if err != nil { return err } case types.EcdsaSecp256k1Signature2019: - signature, err = hidnodecli.GetEcdsaSecp256k1Signature2019(argPrivateKey, schemaDocBytes) + credSchemaDocBytes, err := ldcontext.EcdsaSecp256k1Signature2019Normalize(&schemaDoc, &credSchemaDocProof) + if err != nil { + return err + } + + signature, err = hidnodecli.GetEcdsaSecp256k1Signature2019(argPrivateKey, credSchemaDocBytes) if err != nil { return err } case types.EcdsaSecp256k1RecoverySignature2020: - signature, err = hidnodecli.GetEcdsaSecp256k1RecoverySignature2020(argPrivateKey, schemaDocBytes) + credSchemaDocBytes, err := ldcontext.EcdsaSecp256k1RecoverySignature2020Normalize(&schemaDoc, &credSchemaDocProof) + if err != nil { + return err + } + + signature, err = hidnodecli.GetEcdsaSecp256k1RecoverySignature2020(argPrivateKey, credSchemaDocBytes) if err != nil { return err } case types.BbsBlsSignature2020: - signature, err = hidnodecli.GetBbsBlsSignature2020(argPrivateKey, schemaDocBytes) + credSchemaDocBytes, err := ldcontext.BbsBlsSignature2020Normalize(&schemaDoc, &credSchemaDocProof) + if err != nil { + return err + } + + signature, err = hidnodecli.GetBbsBlsSignature2020(argPrivateKey, credSchemaDocBytes) if err != nil { return err } case types.BabyJubJubSignature2023: - signature, err = hidnodecli.GetBabyJubJubSignature2023(argPrivateKey, schemaDocBytes) + signature, err = hidnodecli.GetBabyJubJubSignature2023(argPrivateKey, schemaDoc.GetSignBytes()) if err != nil { return err } default: - panic("recieved unsupported signing-algo. Supported algorithms are: [Ed25519Signature2020, EcdsaSecp256k1Signature2019, EcdsaSecp256k1RecoverySignature2020, BbsBlsSignature2020, BabyJubJubSignature2023]") + panic(fmt.Sprintf( + "recieved unsupported signing-algo '%v'. Supported algorithms are: [Ed25519Signature2020, EcdsaSecp256k1Signature2019, EcdsaSecp256k1RecoverySignature2020, BbsBlsSignature2020, BabyJubJubSignature2023]", + credSchemaDocProof.Type, + )) } _, err = fmt.Fprintln(cmd.OutOrStdout(), signature) diff --git a/proto/ssi/v1/credential_schema.proto b/proto/ssi/v1/credential_schema.proto index a8fa062..fc2125c 100644 --- a/proto/ssi/v1/credential_schema.proto +++ b/proto/ssi/v1/credential_schema.proto @@ -2,17 +2,19 @@ syntax = "proto3"; package hypersign.ssi.v1; import "ssi/v1/proof.proto"; +import "gogoproto/gogo.proto"; option go_package = "github.com/hypersign-protocol/hid-node/x/ssi/types"; message CredentialSchemaDocument { - string type = 1; - string modelVersion = 2; - string id = 3; - string name = 4; - string author = 5; - string authored = 6; - CredentialSchemaProperty schema = 7; + repeated string context = 1 [json_name = "@context", (gogoproto.jsontag) = "@context"]; + string type = 2; + string modelVersion = 3; + string id = 4; + string name = 5; + string author = 6; + string authored = 7; + CredentialSchemaProperty schema = 8; } message CredentialSchemaProperty { diff --git a/tests/e2e/ssi_tests/generate_doc.py b/tests/e2e/ssi_tests/generate_doc.py index fb67d61..c032107 100644 --- a/tests/e2e/ssi_tests/generate_doc.py +++ b/tests/e2e/ssi_tests/generate_doc.py @@ -12,6 +12,7 @@ SECP256K1_VER_KEY_2019_CONTEXT = "https://ns.did.ai/suites/secp256k1-2019/v1" BBS_CONTEXT = "https://ns.did.ai/suites/bls12381-2020/v1" CREDENTIAL_STATUS_CONTEXT = "https://raw.githubusercontent.com/hypersign-protocol/hypersign-contexts/main/CredentialStatus.jsonld" +CREDENTIAL_SCHEMA_CONTEXT = "https://raw.githubusercontent.com/hypersign-protocol/hypersign-contexts/main/CredentialSchema.jsonld" def generate_did_document(key_pair, algo="Ed25519Signature2020", bech32prefix="hid", is_uuid=False): base_document = { @@ -96,6 +97,7 @@ def generate_did_document(key_pair, algo="Ed25519Signature2020", bech32prefix="h def generate_schema_document(key_pair, schema_author, vm, signature=None, algo="Ed25519Signature2020", updated_schema=None): base_schema_doc = { + "@context": [CREDENTIAL_SCHEMA_CONTEXT], "type": "https://schema.org/Person", "modelVersion": "v1.0", "id": "", @@ -114,12 +116,16 @@ def generate_schema_document(key_pair, schema_author, vm, signature=None, algo=" proof_type = "" if algo == "Ed25519Signature2020": proof_type = "Ed25519Signature2020" + base_schema_doc["@context"].append(ED25519_CONTEXT) elif algo == "EcdsaSecp256k1Signature2019": proof_type = "EcdsaSecp256k1Signature2019" + base_schema_doc["@context"].append(SECP256K1_VER_KEY_2019_CONTEXT) elif algo == "EcdsaSecp256k1RecoverySignature2020": proof_type = "EcdsaSecp256k1RecoverySignature2020" + base_schema_doc["@context"].append(SECP256K1_RECOVERY_CONTEXT) elif algo == "BbsBlsSignature2020": proof_type = "BbsBlsSignature2020" + base_schema_doc["@context"].append(BBS_CONTEXT) elif algo == "BabyJubJubSignature2023": proof_type = "BabyJubJubSignature2023" else: @@ -141,12 +147,12 @@ def generate_schema_document(key_pair, schema_author, vm, signature=None, algo=" # Form Signature if not updated_schema: if not signature: - signature = get_document_signature(base_schema_doc, "schema", key_pair, algo) + signature = get_document_signature(base_schema_doc, "schema", key_pair, algo, proofObj=base_schema_proof) base_schema_proof["proofValue"] = signature return base_schema_doc, base_schema_proof else: if not signature: - signature = get_document_signature(updated_schema, "schema", key_pair, algo) + signature = get_document_signature(updated_schema, "schema", key_pair, algo, proofObj=base_schema_proof) base_schema_proof["proofValue"] = signature return updated_schema, base_schema_proof diff --git a/tests/e2e/ssi_tests/utils.py b/tests/e2e/ssi_tests/utils.py index bf6c590..f3c3f70 100644 --- a/tests/e2e/ssi_tests/utils.py +++ b/tests/e2e/ssi_tests/utils.py @@ -123,10 +123,7 @@ def get_document_signature(doc: dict, doc_type: str, key_pair: dict, algo: str = else: raise Exception("Invalid value for doc_type param: " + doc_type) - if doc_type == "did" or doc_type == "cred-status": - cmd_string = f"hid-noded debug sign-ssi-doc {doc_cmd} '{json.dumps(doc)}' {private_key} '{json.dumps(proofObj)}'" - else: - cmd_string = f"hid-noded debug sign-ssi-doc {doc_cmd} '{json.dumps(doc)}' {private_key} {algo}" + cmd_string = f"hid-noded debug sign-ssi-doc {doc_cmd} '{json.dumps(doc)}' {private_key} '{json.dumps(proofObj)}'" signature, _ = run_command(cmd_string) if signature == "": diff --git a/x/ssi/ld-context/context.go b/x/ssi/ld-context/context.go index 97eb1cb..560943a 100644 --- a/x/ssi/ld-context/context.go +++ b/x/ssi/ld-context/context.go @@ -8,6 +8,7 @@ const BbsSignature2020Context string = "https://ns.did.ai/suites/bls12381-2020/v const Secp256k12019Context string = "https://ns.did.ai/suites/secp256k1-2019/v1" const X25519KeyAgreementKeyEIP5630Context string = "https://raw.githubusercontent.com/hypersign-protocol/hypersign-contexts/main/X25519KeyAgreementKeyEIP5630.jsonld" const CredentialStatusContext string = "https://raw.githubusercontent.com/hypersign-protocol/hypersign-contexts/main/CredentialStatus.jsonld" +const CredentialSchemaContext string = "https://raw.githubusercontent.com/hypersign-protocol/hypersign-contexts/main/CredentialSchema.jsonld" // As hid-node is not supposed to perform any GET request, the complete Context body of their // respective Context urls has been maintained below. @@ -676,4 +677,49 @@ var ContextUrlMap map[string]contextObject = map[string]contextObject{ "@type": "xsd:string", }, }, + CredentialSchemaContext: { + "@version": 1.1, + "hypersign-vocab": "urn:uuid:13fe9318-bb82-4d95-8bf5-8e7fdf8b2026#", + "xsd": "http://www.w3.org/2001/XMLSchema#", + "id": "@id", + "type": map[string]interface{}{ + "@id": "hypersign-vocab:type", + }, + "modelVersion": map[string]interface{}{ + "@id": "hypersign-vocab:modelVersion", + "@type": "xsd:string", + }, + "name": map[string]interface{}{ + "@id": "hypersign-vocab:name", + "@type": "xsd:string", + }, + "author": map[string]interface{}{ + "@id": "hypersign-vocab:author", + "@type": "xsd:string", + }, + "authored": map[string]interface{}{ + "@id": "hypersign-vocab:authored", + "@type": "xsd:dateTime", + }, + "schema": map[string]interface{}{ + "@id": "hypersign-vocab:schema", + "@type": "xsd:string", + }, + "additionalProperties": map[string]interface{}{ + "@id": "hypersign-vocab:additionalProperties", + "@type": "xsd:boolean", + }, + "description": map[string]interface{}{ + "@id": "hypersign-vocab:description", + "@type": "xsd:string", + }, + "properties": map[string]interface{}{ + "@id": "hypersign-vocab:properties", + "@type": "xsd:string", + }, + "required": map[string]interface{}{ + "@id": "hypersign-vocab:required", + "@container": "@set", + }, + }, } diff --git a/x/ssi/ld-context/normalize.go b/x/ssi/ld-context/normalize.go index e938156..f6a8df8 100644 --- a/x/ssi/ld-context/normalize.go +++ b/x/ssi/ld-context/normalize.go @@ -2,8 +2,11 @@ package ldcontext import ( "crypto/sha256" + "encoding/json" + "fmt" "github.com/hypersign-protocol/hid-node/x/ssi/types" + "github.com/piprate/json-gold/ld" ) // NormalizeByVerificationMethodType normalizes DID Document based on the input Verification @@ -64,7 +67,13 @@ func normalizeDocumentWithProof(msg types.SsiMsg, docProof *types.DocumentProof) } context = doc.Context case *types.CredentialSchemaDocument: - return doc.GetSignBytes(), nil + var err error + jsonLdCredentialSchema := NewJsonLdCredentialSchema(doc) + canonizedDocument, err = normalizeWithURDNA2015(jsonLdCredentialSchema) + if err != nil { + return nil, err + } + context = doc.Context } canonizedDocumentHash := sha256.Sum256([]byte(canonizedDocument)) @@ -85,6 +94,48 @@ func normalizeDocumentWithProof(msg types.SsiMsg, docProof *types.DocumentProof) return finalNormalizedHash, nil } +// normalizeWithURDNA2015 performs RDF Canonization upon JsonLdDid using URDNA2015 +// algorithm and returns the canonized document in string +func normalizeWithURDNA2015(jsonLdDocument JsonLdDocument) (string, error) { + return normalize(ld.AlgorithmURDNA2015, jsonLdDocument) +} + +func normalize(algorithm string, jsonLdDocument JsonLdDocument) (string, error) { + proc := ld.NewJsonLdProcessor() + options := ld.NewJsonLdOptions("") + options.Algorithm = algorithm // ld.AlgorithmURDNA2015 + options.Format = "application/n-quads" + + normalisedJsonLd, err := proc.Normalize(jsonLdDocToInterface(jsonLdDocument), options) + if err != nil { + return "", fmt.Errorf("unable to Normalize DID Document: %v", err.Error()) + } + + canonizedDocString := normalisedJsonLd.(string) + if canonizedDocString == "" { + return "", fmt.Errorf("normalization of JSON-LD document yielded empty RDF string") + } + + return canonizedDocString, nil +} + +// Convert JsonLdDid to interface +func jsonLdDocToInterface(jsonLd any) interface{} { + var intf interface{} + + jsonLdBytes, err := json.Marshal(jsonLd) + if err != nil { + panic(err) + } + + err = json.Unmarshal(jsonLdBytes, &intf) + if err != nil { + panic(err) + } + + return intf +} + // Ed25519Signature2020Normalize normalizes DID Document in accordance with // EdDSA Cryptosuite v2020 (https://www.w3.org/community/reports/credentials/CG-FINAL-di-eddsa-2020-20220724/) func Ed25519Signature2020Normalize(ssiMsg types.SsiMsg, didDocProof *types.DocumentProof) ([]byte, error) { diff --git a/x/ssi/ld-context/types.go b/x/ssi/ld-context/types.go index 3dbcac7..1eaa4f6 100644 --- a/x/ssi/ld-context/types.go +++ b/x/ssi/ld-context/types.go @@ -1,11 +1,9 @@ package ldcontext import ( - "encoding/json" "fmt" "github.com/hypersign-protocol/hid-node/x/ssi/types" - "github.com/piprate/json-gold/ld" ) type contextObject map[string]interface{} @@ -65,7 +63,7 @@ func NewJsonLdDidDocument(didDoc *types.DidDocument) *JsonLdDidDocument { return jsonLdDoc } -// It is a similar to `Did` struct, with the exception that the `context` attribute is of type +// It is a similar to `CredentialStatusDocument` struct, with the exception that the `context` attribute is of type // `contextObject` instead of `[]string`, which is meant for accomodating Context JSON body // having arbritrary attributes. It should be used for performing Canonization. type JsonLdCredentialStatus struct { @@ -147,44 +145,47 @@ func NewJsonLdDocumentProof(didDocProof *types.DocumentProof, didContexts []stri return jsonLdDoc } -// normalizeWithURDNA2015 performs RDF Canonization upon JsonLdDid using URDNA2015 -// algorithm and returns the canonized document in string -func normalizeWithURDNA2015(jsonLdDocument JsonLdDocument) (string, error) { - return normalize(ld.AlgorithmURDNA2015, jsonLdDocument) +// It is a similar to `CredentialSchemaDocument` struct, with the exception that the `context` attribute is of type +// `contextObject` instead of `[]string`, which is meant for accomodating Context JSON body +// having arbritrary attributes. It should be used for performing Canonization. +type JsonLdCredentialSchema struct { + Context []contextObject `json:"@context,omitempty"` + Type string `json:"type,omitempty"` + ModelVersion string `json:"modelVersion,omitempty"` + Id string `json:"id,omitempty"` + Name string `json:"name,omitempty"` + Author string `json:"author,omitempty"` + Authored string `json:"authored,omitempty"` + Schema *types.CredentialSchemaProperty `json:"schema,omitempty"` } -func normalize(algorithm string, jsonLdDocument JsonLdDocument) (string, error) { - proc := ld.NewJsonLdProcessor() - options := ld.NewJsonLdOptions("") - options.Algorithm = algorithm // ld.AlgorithmURDNA2015 - options.Format = "application/n-quads" - - normalisedJsonLd, err := proc.Normalize(jsonLdDocToInterface(jsonLdDocument), options) - if err != nil { - return "", fmt.Errorf("unable to Normalize DID Document: %v", err.Error()) - } +func (doc *JsonLdCredentialSchema) GetContext() []contextObject { + return doc.Context +} - canonizedDocString := normalisedJsonLd.(string) - if canonizedDocString == "" { - return "", fmt.Errorf("normalization of JSON-LD document yielded empty RDF string") +func NewJsonLdCredentialSchema(credSchema *types.CredentialSchemaDocument) *JsonLdCredentialSchema { + if len(credSchema.Context) == 0 { + panic("atleast one context url must be provided for DID Document for Canonization") } - return canonizedDocString, nil -} - -// Convert JsonLdDid to interface -func jsonLdDocToInterface(jsonLd any) interface{} { - var intf interface{} + var jsonLdDoc *JsonLdCredentialSchema = &JsonLdCredentialSchema{} - jsonLdBytes, err := json.Marshal(jsonLd) - if err != nil { - panic(err) + for _, url := range credSchema.Context { + contextObj, ok := ContextUrlMap[url] + if !ok { + panic(fmt.Sprintf("invalid or unsupported context url: %v", url)) + } + jsonLdDoc.Context = append(jsonLdDoc.Context, contextObj) } - err = json.Unmarshal(jsonLdBytes, &intf) - if err != nil { - panic(err) - } + jsonLdDoc.Type = credSchema.Type + jsonLdDoc.ModelVersion = credSchema.ModelVersion + jsonLdDoc.Id = credSchema.Id + jsonLdDoc.Name = credSchema.Name + jsonLdDoc.Author = credSchema.Author + jsonLdDoc.Authored = credSchema.Authored + jsonLdDoc.Schema = credSchema.Schema - return intf + return jsonLdDoc } + diff --git a/x/ssi/types/credential_schema.pb.go b/x/ssi/types/credential_schema.pb.go index 6d12c59..33ff5bc 100644 --- a/x/ssi/types/credential_schema.pb.go +++ b/x/ssi/types/credential_schema.pb.go @@ -5,6 +5,7 @@ package types import ( fmt "fmt" + _ "github.com/gogo/protobuf/gogoproto" proto "github.com/gogo/protobuf/proto" io "io" math "math" @@ -23,13 +24,14 @@ var _ = math.Inf const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type CredentialSchemaDocument struct { - Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` - ModelVersion string `protobuf:"bytes,2,opt,name=modelVersion,proto3" json:"modelVersion,omitempty"` - Id string `protobuf:"bytes,3,opt,name=id,proto3" json:"id,omitempty"` - Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty"` - Author string `protobuf:"bytes,5,opt,name=author,proto3" json:"author,omitempty"` - Authored string `protobuf:"bytes,6,opt,name=authored,proto3" json:"authored,omitempty"` - Schema *CredentialSchemaProperty `protobuf:"bytes,7,opt,name=schema,proto3" json:"schema,omitempty"` + Context []string `protobuf:"bytes,1,rep,name=context,json=@context,proto3" json:"@context"` + Type string `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"` + ModelVersion string `protobuf:"bytes,3,opt,name=modelVersion,proto3" json:"modelVersion,omitempty"` + Id string `protobuf:"bytes,4,opt,name=id,proto3" json:"id,omitempty"` + Name string `protobuf:"bytes,5,opt,name=name,proto3" json:"name,omitempty"` + Author string `protobuf:"bytes,6,opt,name=author,proto3" json:"author,omitempty"` + Authored string `protobuf:"bytes,7,opt,name=authored,proto3" json:"authored,omitempty"` + Schema *CredentialSchemaProperty `protobuf:"bytes,8,opt,name=schema,proto3" json:"schema,omitempty"` } func (m *CredentialSchemaDocument) Reset() { *m = CredentialSchemaDocument{} } @@ -65,6 +67,13 @@ func (m *CredentialSchemaDocument) XXX_DiscardUnknown() { var xxx_messageInfo_CredentialSchemaDocument proto.InternalMessageInfo +func (m *CredentialSchemaDocument) GetContext() []string { + if m != nil { + return m.Context + } + return nil +} + func (m *CredentialSchemaDocument) GetType() string { if m != nil { return m.Type @@ -259,34 +268,36 @@ func init() { func init() { proto.RegisterFile("ssi/v1/credential_schema.proto", fileDescriptor_9a5852fe559c17be) } var fileDescriptor_9a5852fe559c17be = []byte{ - // 419 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0x3d, 0x8f, 0xd3, 0x30, - 0x1c, 0xc6, 0xeb, 0xf6, 0x2e, 0x1c, 0x2e, 0x42, 0xc8, 0xe2, 0x90, 0x75, 0x83, 0x89, 0x32, 0x9d, - 0x90, 0x2e, 0xd1, 0x85, 0x6f, 0x70, 0x30, 0x32, 0x9c, 0x52, 0xc1, 0xc0, 0x82, 0x52, 0xdb, 0x6d, - 0x2c, 0x25, 0x71, 0xb0, 0x9d, 0x8a, 0x7e, 0x0b, 0x3e, 0x16, 0x63, 0x27, 0xd4, 0x11, 0xb5, 0x3b, - 0x9f, 0x01, 0xd9, 0x79, 0x69, 0xe9, 0x8b, 0x74, 0xdb, 0xff, 0xf5, 0xb1, 0x7f, 0x8f, 0x0d, 0x89, - 0xd6, 0x22, 0x5a, 0xdc, 0x47, 0x54, 0x71, 0xc6, 0x4b, 0x23, 0xd2, 0xfc, 0x9b, 0xa6, 0x19, 0x2f, - 0xd2, 0xb0, 0x52, 0xd2, 0x48, 0xf4, 0x2a, 0x5b, 0x56, 0x5c, 0x69, 0x31, 0x2f, 0x43, 0xad, 0x45, - 0xb8, 0xb8, 0xbf, 0x41, 0xed, 0x46, 0xa5, 0xa4, 0x9c, 0x35, 0x53, 0xc1, 0x5f, 0x00, 0xf1, 0x87, - 0x5e, 0x61, 0xe2, 0x04, 0x3e, 0x4a, 0x5a, 0x17, 0xbc, 0x34, 0x08, 0xc1, 0x0b, 0xb3, 0xac, 0x38, - 0x06, 0x3e, 0xb8, 0x7d, 0x9e, 0xb8, 0x18, 0x05, 0xf0, 0x45, 0x21, 0x19, 0xcf, 0xbf, 0x58, 0x6d, - 0x59, 0xe2, 0xa1, 0xeb, 0xfd, 0x57, 0x43, 0x2f, 0xe1, 0x50, 0x30, 0x3c, 0x72, 0x9d, 0xa1, 0x60, - 0x56, 0xa7, 0x4c, 0x0b, 0x8e, 0x2f, 0x1a, 0x1d, 0x1b, 0xa3, 0x37, 0xd0, 0x4b, 0x6b, 0x93, 0x49, - 0x85, 0x2f, 0x5d, 0xb5, 0xcd, 0xd0, 0x0d, 0xbc, 0x6a, 0x22, 0xce, 0xb0, 0xe7, 0x3a, 0x7d, 0x8e, - 0x1e, 0xa0, 0xd7, 0x20, 0xe2, 0x67, 0x3e, 0xb8, 0x1d, 0xc7, 0xef, 0xc2, 0x43, 0xc6, 0xf0, 0x90, - 0xe5, 0x51, 0xc9, 0x8a, 0x2b, 0xb3, 0x4c, 0xda, 0xcd, 0x60, 0x7d, 0x02, 0xb8, 0x1b, 0xb2, 0x97, - 0x6a, 0x0f, 0x68, 0x90, 0xdb, 0x0c, 0xf9, 0x70, 0xcc, 0xb8, 0xa6, 0x4a, 0x54, 0x66, 0xc7, 0xbc, - 0x5f, 0xea, 0xad, 0x1a, 0xed, 0x59, 0x45, 0x20, 0xac, 0x1a, 0x65, 0xc1, 0x75, 0x0b, 0xbf, 0x57, - 0xb1, 0xa8, 0x8a, 0x7f, 0xaf, 0x85, 0x45, 0xbd, 0xf4, 0x47, 0x16, 0xb5, 0xcb, 0x51, 0x0c, 0x5f, - 0xa7, 0x8c, 0x09, 0xab, 0x9d, 0xe6, 0x8f, 0x3b, 0x15, 0x6b, 0xc9, 0x55, 0x72, 0xb2, 0x17, 0xfc, - 0x06, 0xf0, 0xfa, 0x10, 0x6d, 0x62, 0x52, 0xc3, 0xd1, 0x0c, 0x62, 0x7a, 0xe6, 0x91, 0x1d, 0xe9, - 0x93, 0xac, 0xec, 0x36, 0x92, 0xb3, 0x5a, 0xe8, 0x33, 0xbc, 0xa6, 0xc7, 0xde, 0xca, 0x99, 0x73, - 0x6c, 0x1c, 0xbf, 0x3d, 0x3e, 0xa4, 0x5b, 0x75, 0x63, 0xc9, 0xe9, 0xed, 0x87, 0x4f, 0xbf, 0x36, - 0x04, 0xac, 0x36, 0x04, 0xfc, 0xd9, 0x10, 0xf0, 0x73, 0x4b, 0x06, 0xab, 0x2d, 0x19, 0xac, 0xb7, - 0x64, 0xf0, 0x35, 0x9e, 0x0b, 0x93, 0xd5, 0xd3, 0x90, 0xca, 0x22, 0xea, 0xb5, 0xef, 0xdc, 0xd7, - 0xa6, 0x32, 0x8f, 0x32, 0xc1, 0xee, 0x4a, 0xc9, 0x78, 0xf4, 0x23, 0xb2, 0x7f, 0xdf, 0xbe, 0x8a, - 0x9e, 0x7a, 0xae, 0xfd, 0xfe, 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xae, 0x9f, 0xe4, 0xda, 0x41, - 0x03, 0x00, 0x00, + // 462 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x53, 0xc1, 0x6e, 0x13, 0x31, + 0x10, 0x8d, 0x93, 0x36, 0x4d, 0x9d, 0x0a, 0x21, 0xab, 0x45, 0x56, 0x0e, 0x6e, 0x94, 0x0b, 0x11, + 0x52, 0x77, 0xd5, 0xf0, 0x03, 0x28, 0x70, 0xe4, 0x50, 0x6d, 0x05, 0x07, 0x2e, 0x68, 0x6b, 0x3b, + 0x59, 0x4b, 0xd9, 0x9d, 0xc5, 0xf6, 0x56, 0xcd, 0x5f, 0x70, 0xe3, 0x97, 0x38, 0xf6, 0x84, 0x7a, + 0x42, 0x28, 0xb9, 0xf1, 0x15, 0xc8, 0xf6, 0x66, 0x09, 0x49, 0x2b, 0x71, 0x7b, 0xf3, 0xc6, 0xf3, + 0x76, 0xde, 0xf3, 0x1a, 0x33, 0x63, 0x54, 0x7c, 0x7b, 0x19, 0x73, 0x2d, 0x85, 0x2c, 0xac, 0x4a, + 0x17, 0x9f, 0x0d, 0xcf, 0x64, 0x9e, 0x46, 0xa5, 0x06, 0x0b, 0xe4, 0x79, 0xb6, 0x2c, 0xa5, 0x36, + 0x6a, 0x5e, 0x44, 0xc6, 0xa8, 0xe8, 0xf6, 0x72, 0x40, 0xea, 0x89, 0x52, 0x03, 0xcc, 0xc2, 0xa9, + 0xc1, 0xe9, 0x1c, 0xe6, 0xe0, 0x61, 0xec, 0x50, 0x60, 0x47, 0xdf, 0xda, 0x98, 0xbe, 0x6d, 0x74, + 0xaf, 0xbd, 0xec, 0x3b, 0xe0, 0x55, 0x2e, 0x0b, 0x4b, 0x5e, 0xe2, 0x23, 0x0e, 0x85, 0x95, 0x77, + 0x96, 0xa2, 0x61, 0x67, 0x7c, 0x3c, 0x3d, 0xf9, 0xfd, 0xf3, 0xbc, 0xf7, 0xa6, 0xe6, 0x92, 0x06, + 0x11, 0x82, 0x0f, 0xec, 0xb2, 0x94, 0xb4, 0x3d, 0x44, 0xe3, 0xe3, 0xc4, 0x63, 0x32, 0xc2, 0x27, + 0x39, 0x08, 0xb9, 0xf8, 0xe8, 0x56, 0x83, 0x82, 0x76, 0x7c, 0xef, 0x1f, 0x8e, 0x3c, 0xc3, 0x6d, + 0x25, 0xe8, 0x81, 0xef, 0xb4, 0x95, 0x70, 0x3a, 0x45, 0x9a, 0x4b, 0x7a, 0x18, 0x74, 0x1c, 0x26, + 0x2f, 0x70, 0x37, 0xad, 0x6c, 0x06, 0x9a, 0x76, 0x3d, 0x5b, 0x57, 0x64, 0x80, 0x7b, 0x01, 0x49, + 0x41, 0x8f, 0x7c, 0xa7, 0xa9, 0xc9, 0x14, 0x77, 0x43, 0x42, 0xb4, 0x37, 0x44, 0xe3, 0xfe, 0xe4, + 0x55, 0xb4, 0x1b, 0x51, 0xb4, 0x6b, 0xfa, 0x4a, 0x43, 0x29, 0xb5, 0x5d, 0x26, 0xf5, 0xe4, 0xe8, + 0x01, 0xed, 0x27, 0xb3, 0x39, 0xe4, 0x96, 0xaa, 0x3f, 0x80, 0xc2, 0x52, 0xa1, 0x22, 0x43, 0xdc, + 0x17, 0xd2, 0x70, 0xad, 0x4a, 0xeb, 0x3c, 0x87, 0x3c, 0xb6, 0xa9, 0x26, 0xaa, 0xce, 0x56, 0x54, + 0x0c, 0xe3, 0x32, 0x28, 0x2b, 0x69, 0xea, 0x38, 0xb6, 0x18, 0x67, 0x55, 0xcb, 0x2f, 0x95, 0x72, + 0x56, 0x0f, 0xdd, 0x45, 0x24, 0x4d, 0x4d, 0x26, 0xf8, 0x34, 0x15, 0x42, 0x39, 0xed, 0x74, 0x71, + 0xf5, 0x57, 0xc5, 0x85, 0xd5, 0x4b, 0x1e, 0xed, 0x8d, 0x7e, 0x20, 0x7c, 0xb6, 0x6b, 0xed, 0xda, + 0xa6, 0x56, 0x92, 0x19, 0xa6, 0xfc, 0x89, 0xbf, 0xc1, 0x3b, 0xfd, 0xaf, 0x28, 0x37, 0x13, 0xc9, + 0x93, 0x5a, 0xe4, 0x03, 0x3e, 0xe3, 0xfb, 0xd9, 0xc2, 0xcc, 0x27, 0xd6, 0x9f, 0x9c, 0xef, 0x7f, + 0x64, 0x33, 0xea, 0x8f, 0x25, 0x8f, 0x4f, 0x4f, 0xdf, 0x7f, 0x5f, 0x31, 0x74, 0xbf, 0x62, 0xe8, + 0xd7, 0x8a, 0xa1, 0xaf, 0x6b, 0xd6, 0xba, 0x5f, 0xb3, 0xd6, 0xc3, 0x9a, 0xb5, 0x3e, 0x4d, 0xe6, + 0xca, 0x66, 0xd5, 0x4d, 0xc4, 0x21, 0x8f, 0x1b, 0xed, 0x0b, 0xff, 0x06, 0x38, 0x2c, 0xe2, 0x4c, + 0x89, 0x8b, 0x02, 0x84, 0x8c, 0xef, 0x62, 0xf7, 0x74, 0xdc, 0xad, 0x98, 0x9b, 0xae, 0x6f, 0xbf, + 0xfe, 0x13, 0x00, 0x00, 0xff, 0xff, 0x59, 0x50, 0xb4, 0x08, 0x80, 0x03, 0x00, 0x00, } func (m *CredentialSchemaDocument) Marshal() (dAtA []byte, err error) { @@ -319,49 +330,58 @@ func (m *CredentialSchemaDocument) MarshalToSizedBuffer(dAtA []byte) (int, error i = encodeVarintCredentialSchema(dAtA, i, uint64(size)) } i-- - dAtA[i] = 0x3a + dAtA[i] = 0x42 } if len(m.Authored) > 0 { i -= len(m.Authored) copy(dAtA[i:], m.Authored) i = encodeVarintCredentialSchema(dAtA, i, uint64(len(m.Authored))) i-- - dAtA[i] = 0x32 + dAtA[i] = 0x3a } if len(m.Author) > 0 { i -= len(m.Author) copy(dAtA[i:], m.Author) i = encodeVarintCredentialSchema(dAtA, i, uint64(len(m.Author))) i-- - dAtA[i] = 0x2a + dAtA[i] = 0x32 } if len(m.Name) > 0 { i -= len(m.Name) copy(dAtA[i:], m.Name) i = encodeVarintCredentialSchema(dAtA, i, uint64(len(m.Name))) i-- - dAtA[i] = 0x22 + dAtA[i] = 0x2a } if len(m.Id) > 0 { i -= len(m.Id) copy(dAtA[i:], m.Id) i = encodeVarintCredentialSchema(dAtA, i, uint64(len(m.Id))) i-- - dAtA[i] = 0x1a + dAtA[i] = 0x22 } if len(m.ModelVersion) > 0 { i -= len(m.ModelVersion) copy(dAtA[i:], m.ModelVersion) i = encodeVarintCredentialSchema(dAtA, i, uint64(len(m.ModelVersion))) i-- - dAtA[i] = 0x12 + dAtA[i] = 0x1a } if len(m.Type) > 0 { i -= len(m.Type) copy(dAtA[i:], m.Type) i = encodeVarintCredentialSchema(dAtA, i, uint64(len(m.Type))) i-- - dAtA[i] = 0xa + dAtA[i] = 0x12 + } + if len(m.Context) > 0 { + for iNdEx := len(m.Context) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Context[iNdEx]) + copy(dAtA[i:], m.Context[iNdEx]) + i = encodeVarintCredentialSchema(dAtA, i, uint64(len(m.Context[iNdEx]))) + i-- + dAtA[i] = 0xa + } } return len(dAtA) - i, nil } @@ -500,6 +520,12 @@ func (m *CredentialSchemaDocument) Size() (n int) { } var l int _ = l + if len(m.Context) > 0 { + for _, s := range m.Context { + l = len(s) + n += 1 + l + sovCredentialSchema(uint64(l)) + } + } l = len(m.Type) if l > 0 { n += 1 + l + sovCredentialSchema(uint64(l)) @@ -618,6 +644,38 @@ func (m *CredentialSchemaDocument) Unmarshal(dAtA []byte) error { } switch fieldNum { case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Context", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCredentialSchema + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthCredentialSchema + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthCredentialSchema + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Context = append(m.Context, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) } @@ -649,7 +707,7 @@ func (m *CredentialSchemaDocument) Unmarshal(dAtA []byte) error { } m.Type = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 2: + case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ModelVersion", wireType) } @@ -681,7 +739,7 @@ func (m *CredentialSchemaDocument) Unmarshal(dAtA []byte) error { } m.ModelVersion = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 3: + case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) } @@ -713,7 +771,7 @@ func (m *CredentialSchemaDocument) Unmarshal(dAtA []byte) error { } m.Id = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 4: + case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) } @@ -745,7 +803,7 @@ func (m *CredentialSchemaDocument) Unmarshal(dAtA []byte) error { } m.Name = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 5: + case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Author", wireType) } @@ -777,7 +835,7 @@ func (m *CredentialSchemaDocument) Unmarshal(dAtA []byte) error { } m.Author = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 6: + case 7: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Authored", wireType) } @@ -809,7 +867,7 @@ func (m *CredentialSchemaDocument) Unmarshal(dAtA []byte) error { } m.Authored = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 7: + case 8: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Schema", wireType) } From d10e2aeb18517e98521ffcb30f6a0953f08cc26d Mon Sep 17 00:00:00 2001 From: Arnab Ghose Date: Mon, 13 Nov 2023 17:43:06 +0530 Subject: [PATCH 3/4] modified clientSpec check to do JSON-singing only for BabyJubJubSignature2023 signed document --- x/ssi/verification/client_spec.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/x/ssi/verification/client_spec.go b/x/ssi/verification/client_spec.go index daa8b47..9ebaeab 100644 --- a/x/ssi/verification/client_spec.go +++ b/x/ssi/verification/client_spec.go @@ -56,7 +56,7 @@ func getPersonalSignSpecDocBytes(ssiMsg types.SsiMsg) ([]byte, error) { func getDocBytesByClientSpec(ssiMsg types.SsiMsg, extendedVm *types.ExtendedVerificationMethod) ([]byte, error) { switch extendedVm.Proof.ClientSpecType { case types.CLIENT_SPEC_TYPE_NONE: - if didDoc, ok := ssiMsg.(*types.DidDocument); ok && len(didDoc.Context) == 0 { + if extendedVm.Proof.Type == types.BabyJubJubSignature2023 { return ssiMsg.GetSignBytes(), nil } @@ -67,7 +67,7 @@ func getDocBytesByClientSpec(ssiMsg types.SsiMsg, extendedVm *types.ExtendedVeri return nil, err } - if didDoc, ok := ssiMsg.(*types.DidDocument); ok && len(didDoc.Context) == 0 { + if extendedVm.Proof.Type == types.BabyJubJubSignature2023 { return getCosmosADR036SignDocBytes(ssiMsg.GetSignBytes(), signerAddress) } @@ -79,7 +79,7 @@ func getDocBytesByClientSpec(ssiMsg types.SsiMsg, extendedVm *types.ExtendedVeri return getCosmosADR036SignDocBytes(canonizedDidDocHash, signerAddress) case types.CLIENT_SPEC_TYPE_ETH_PERSONAL_SIGN: - if didDoc, ok := ssiMsg.(*types.DidDocument); ok && len(didDoc.Context) == 0 { + if extendedVm.Proof.Type == types.BabyJubJubSignature2023 { return getPersonalSignSpecDocBytes(ssiMsg) } From f3abe4f99b0d50001ff930e6643bc144dd7b152a Mon Sep 17 00:00:00 2001 From: Arnab Ghose Date: Tue, 14 Nov 2023 10:06:18 +0530 Subject: [PATCH 4/4] updated Swagger for Credential Schema --- client/docs/swagger-ui/swagger.yaml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/client/docs/swagger-ui/swagger.yaml b/client/docs/swagger-ui/swagger.yaml index 0232e63..833295f 100644 --- a/client/docs/swagger-ui/swagger.yaml +++ b/client/docs/swagger-ui/swagger.yaml @@ -537,6 +537,10 @@ paths: credentialSchemaDocument: type: object properties: + context: + type: array + items: + type: string type: type: string modelVersion: @@ -685,6 +689,10 @@ paths: credentialSchemaDocument: type: object properties: + context: + type: array + items: + type: string type: type: string modelVersion: @@ -24570,6 +24578,10 @@ definitions: hypersign.ssi.v1.CredentialSchemaDocument: type: object properties: + context: + type: array + items: + type: string type: type: string modelVersion: @@ -24622,6 +24634,10 @@ definitions: credentialSchemaDocument: type: object properties: + context: + type: array + items: + type: string type: type: string modelVersion: @@ -24918,6 +24934,10 @@ definitions: credentialSchemaDocument: type: object properties: + context: + type: array + items: + type: string type: type: string modelVersion: @@ -24981,6 +25001,10 @@ definitions: credentialSchemaDocument: type: object properties: + context: + type: array + items: + type: string type: type: string modelVersion: