├── verifiable ├── testdata │ ├── related_resources_2.json │ ├── context │ │ ├── context1.jsonld │ │ ├── context2.jsonld │ │ ├── presentation_submission_v1.jsonld │ │ └── lds_jws2020_v1.jsonld │ ├── credential_with_resource.json │ ├── v2_credential_without_issuer.jsonld │ ├── valid_doc.jsonld │ ├── v2_valid_credential.jsonld │ ├── example_presentation_4.json │ ├── example_presentation_1_ed25519.jsonld │ ├── example_presentation_5.json │ ├── v2_valid_credential_multi_status.jsonld │ ├── example-vcs.json │ ├── v1_credential_without_issuancedate.jsonld │ └── v1_valid_credential.jsonld ├── interfaces.go ├── credential_jwt_unsecured.go ├── cache_js_wasm.go ├── cache.go ├── presentation_ldp.go ├── credential_ldp.go ├── jwt_unsecured.go ├── credential_jws.go ├── presentation_jwt_unsecured.go ├── cwt │ ├── cwt.go │ └── cbor.go ├── presentation_jws.go ├── jws.go ├── credential_testsuite.go ├── jwt_unsecured_test.go ├── credential_bbs.go ├── did_data_integrity.go ├── cose.go ├── presentation_cwt_proof_test.go ├── test-suite │ ├── contexts │ │ └── credentials-examples_v1.jsonld │ └── config.json ├── presentation_jwt_test.go ├── credential_cwt.go ├── presentation_cwt.go ├── did_ldp_test.go ├── credential_jwt_unsecured_test.go ├── example_support_test.go ├── presentation_jwt_unsecured_test.go ├── jsonld.go ├── embedded_proof.go ├── presentation_ldp_test.go └── credential_testsuite_test.go ├── jwt ├── testdata │ ├── interop_key_ed25519.jwk │ ├── interop_key_secp256k1.jwk │ ├── interop_key_secp256r1.jwk │ ├── interop_key_secp384r1.jwk │ ├── interop_credential_4_secp256k1.jwt │ ├── interop_credential_7_secp256r1.jwt │ └── interop_credential_10_secp384r1.jwt ├── verifier.go ├── signer.go ├── jose_wrappers_test.go └── jose_wrappers.go ├── vermethod ├── method.go └── vdrkeyresolver.go ├── sdjwt ├── common │ ├── types.go │ └── testdata │ │ ├── array_element_and_one_missing_v5.json │ │ └── full_disclosures_v5.json ├── sdjwt-doc.go ├── verifier │ ├── keybidning.go │ ├── holderbidning.go │ └── example_test.go └── issuer │ └── example_test.go ├── cwt ├── signer.go ├── interfaces.go ├── wrappers.go ├── wrappers_test.go └── cwt.go ├── proof ├── checker │ └── types.go ├── creator │ └── creator_test.go ├── descriptor.go ├── jwtproofs │ ├── ps256 │ │ └── proof.go │ ├── rs256 │ │ └── proof.go │ ├── es521 │ │ └── proof.go │ ├── es256 │ │ └── proof.go │ ├── es384 │ │ └── proof.go │ ├── eddsa │ │ └── proof.go │ └── es256k │ │ └── proof.go ├── defaults │ └── all.go └── ldproofs │ ├── ed25519signature2018 │ └── proof.go │ ├── bbsblssignatureproof2020 │ └── proof.go │ ├── bbsblssignature2020 │ └── proof.go │ ├── ed25519signature2020 │ └── proof.go │ └── ecdsasecp256k1signature2019 │ └── proof.go ├── dataintegrity ├── suite │ ├── ecdsa2019 │ │ └── testdata │ │ │ ├── invalid_jsonld.jsonld │ │ │ └── valid_credential.jsonld │ ├── eddsa2022 │ │ └── testdata │ │ │ ├── invalid_jsonld.jsonld │ │ │ └── valid_credential.jsonld │ └── suite.go ├── dataintegrity.go └── models │ └── models.go ├── util ├── maphelpers │ ├── map.go │ └── mapstruct.go └── time │ └── time_test.go ├── crypto-ext ├── pubkey │ └── pubkey.go ├── verifiers │ └── ed25519 │ │ ├── verifier.go │ │ └── verifier_test.go └── testutil │ └── sign_helper.go ├── presexch ├── testdata │ ├── contexts │ │ ├── mdl-broken.jsonld │ │ └── mdl-v1.jsonld │ ├── sample_1.json │ ├── university_degree.jwt │ ├── presentation_definition.json │ ├── submission_requirements_pd.json │ ├── sample_2.json │ ├── permanent_resident_card.jwt │ ├── nested_submission_requirements_pd.json │ └── verified_employee.jwt ├── internal │ └── requirementlogic │ │ └── stringset.go ├── README.md └── package_test.go ├── scripts ├── check_lint.sh ├── check_unit.sh └── check_license.sh ├── .gitignore ├── status ├── validator │ └── validator.go ├── api │ └── api.go └── internal │ └── bitstring │ └── bitstring.go ├── Makefile ├── .github └── workflows │ └── build.yml ├── README.md ├── internal └── testutil │ └── kmscryptoutil │ └── kmscryptoutil.go ├── go.mod └── didconfig └── client └── didconfig.go /verifiable/testdata/related_resources_2.json: -------------------------------------------------------------------------------- 1 | { 2 | "@context": { 3 | "@vocab": "https://www.w3.org/ns/credentials/examples#" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /jwt/testdata/interop_key_ed25519.jwk: -------------------------------------------------------------------------------- 1 | { 2 | "kty": "OKP", 3 | "crv": "Ed25519", 4 | "x": "JYCAGl6C7gcDeKbNqtXBfpGzH0f5elifj7L6zYNj_Is" 5 | } 6 | -------------------------------------------------------------------------------- /verifiable/testdata/context/context1.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": { 3 | "c1": "https://example.com/vocab#c1", 4 | "s1": "https://example.com/vocab#s1" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /verifiable/testdata/context/context2.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": { 3 | "c2": "https://example.com/vocab#c2", 4 | "s2": "https://example.com/vocab#s2" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /jwt/testdata/interop_key_secp256k1.jwk: -------------------------------------------------------------------------------- 1 | { 2 | "kty": "EC", 3 | "crv": "secp256k1", 4 | "x": "BuzCc8bUShI3GfVz-WLISZDGq7wnVB4h_nvNQcWn7Jw", 5 | "y": "dLrbspB9PZvZF0dr42nCmF38192dQKglvB52IZBM-vs" 6 | } -------------------------------------------------------------------------------- /jwt/testdata/interop_key_secp256r1.jwk: -------------------------------------------------------------------------------- 1 | { 2 | "kty": "EC", 3 | "crv": "P-256", 4 | "x": "40TexHWb6XTyuShaqhiazvmfxyK5zibbtOBXsQFKJg8", 5 | "y": "SGIGDSRHOOYJntO1lIapw_vR1FP7SPBlmA_2aM9HoFU" 6 | } -------------------------------------------------------------------------------- /jwt/testdata/interop_key_secp384r1.jwk: -------------------------------------------------------------------------------- 1 | { 2 | "kty": "EC", 3 | "crv": "P-384", 4 | "x": "BiU_mGfa3uWMKrC4Q6EFvM5D5Qiz2orm7ABlIaC1iJWOuapQC9U_fbqrKwRFRepf", 5 | "y": "_23qoYv94V-PSWzqQMnUqq1nu_MdE4fEIaAhCCYplAwlp4c3LKZDtkgQaPdF4kIT" 6 | } -------------------------------------------------------------------------------- /vermethod/method.go: -------------------------------------------------------------------------------- 1 | package vermethod 2 | 3 | import "github.com/trustbloc/kms-go/doc/jose/jwk" 4 | 5 | // VerificationMethod is defined either as raw public key bytes (Value field) or as JSON Web Key. 6 | type VerificationMethod struct { 7 | Type string 8 | Value []byte 9 | JWK *jwk.JWK 10 | } 11 | -------------------------------------------------------------------------------- /sdjwt/common/types.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Avast Software. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package common 8 | 9 | type recursiveData struct { 10 | disclosures map[string]*DisclosureClaim 11 | nestedSD []string 12 | cleanupDigestsClaims bool 13 | } 14 | -------------------------------------------------------------------------------- /cwt/signer.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Gen Digital Inc. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package cwt 8 | 9 | // ProofCreator defines signer interface which is used to sign VC JWT. 10 | type ProofCreator interface { 11 | SignCWT(params SignParameters, cborData []byte) ([]byte, error) 12 | } 13 | -------------------------------------------------------------------------------- /verifiable/interfaces.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright SecureKey Technologies Inc. All Rights Reserved. 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package verifiable 7 | 8 | //go:generate mockgen -destination interfaces_mocks_test.go -package verifiable_test -source=interfaces.go 9 | 10 | import "net/http" 11 | 12 | // nolint 13 | type httpClient interface { 14 | Do(req *http.Request) (*http.Response, error) 15 | } 16 | -------------------------------------------------------------------------------- /proof/checker/types.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Gen Digital Inc. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package checker 8 | 9 | import "github.com/veraison/go-cose" 10 | 11 | // CheckCWTProofRequest is the request for checking a CWT proof. 12 | type CheckCWTProofRequest struct { 13 | KeyID string 14 | KeyMaterial string // hex encoded key material 15 | Algo cose.Algorithm 16 | } 17 | -------------------------------------------------------------------------------- /jwt/verifier.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Gen Digital Inc. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package jwt 8 | 9 | import "github.com/trustbloc/kms-go/doc/jose" 10 | 11 | // ProofChecker used to check proof of jwt vc. 12 | type ProofChecker interface { 13 | // CheckJWTProof check jwt proof. 14 | CheckJWTProof(headers jose.Headers, expectedProofIssuer string, msg, signature []byte) error 15 | } 16 | -------------------------------------------------------------------------------- /verifiable/credential_jwt_unsecured.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright SecureKey Technologies Inc. All Rights Reserved. 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package verifiable 7 | 8 | // MarshalUnsecuredJWT serialized JWT into unsecured JWT. 9 | func (jcc *JWTCredClaims) MarshalUnsecuredJWT() (string, error) { 10 | return marshalUnsecuredJWT(jcc) 11 | } 12 | 13 | func decodeCredJWTUnsecured(rawJwt string) ([]byte, error) { 14 | _, vcBytes, err := decodeCredJWT(rawJwt) 15 | 16 | return vcBytes, err 17 | } 18 | -------------------------------------------------------------------------------- /verifiable/testdata/credential_with_resource.json: -------------------------------------------------------------------------------- 1 | { 2 | "@context": [ 3 | "https://www.w3.org/ns/credentials/v2" 4 | ], 5 | "type": [ 6 | "VerifiableCredential" 7 | ], 8 | "issuer": "did:example:issuer", 9 | "credentialSubject": { 10 | "id": "did:example:subject" 11 | }, 12 | "relatedResource": [ 13 | { 14 | "id": "https://w3c.github.io/vc-data-model/related-resource.json", 15 | "digestSRI": "sha256-ca3d163bab055381827226140568f3bef634ac187cebd76878e0b63e9e442356" 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /dataintegrity/suite/ecdsa2019/testdata/invalid_jsonld.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": 3.1, 3 | "id": "http://example.edu/credentials/1872", 4 | "type": "VerifiableCredential", 5 | "credentialSubject": { 6 | "id": "did:example:ebfeb1f712ebc6f1c276e12ec21" 7 | }, 8 | "issuer": { 9 | "id": "did:example:76e12ec712ebc6f1c221ebfeb1f", 10 | "name": "Example University", 11 | "image": "data:image/png;base64,iVBOR" 12 | }, 13 | "issuanceDate": "2010-01-01T19:23:24Z", 14 | "expirationDate": "2020-01-01T19:23:24Z" 15 | } 16 | -------------------------------------------------------------------------------- /dataintegrity/suite/eddsa2022/testdata/invalid_jsonld.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": 3.1, 3 | "id": "http://example.edu/credentials/1872", 4 | "type": "VerifiableCredential", 5 | "credentialSubject": { 6 | "id": "did:example:ebfeb1f712ebc6f1c276e12ec21" 7 | }, 8 | "issuer": { 9 | "id": "did:example:76e12ec712ebc6f1c221ebfeb1f", 10 | "name": "Example University", 11 | "image": "data:image/png;base64,iVBOR" 12 | }, 13 | "issuanceDate": "2010-01-01T19:23:24Z", 14 | "expirationDate": "2020-01-01T19:23:24Z" 15 | } 16 | -------------------------------------------------------------------------------- /util/maphelpers/map.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright SecureKey Technologies Inc. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package maphelpers 8 | 9 | // CopyMap performs shallow copy of map and nested maps. 10 | func CopyMap(m map[string]interface{}) map[string]interface{} { 11 | cm := make(map[string]interface{}) 12 | 13 | for k, v := range m { 14 | vm, ok := v.(map[string]interface{}) 15 | if ok { 16 | cm[k] = CopyMap(vm) 17 | } else { 18 | cm[k] = v 19 | } 20 | } 21 | 22 | return cm 23 | } 24 | -------------------------------------------------------------------------------- /crypto-ext/pubkey/pubkey.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Gen Digital Inc. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package pubkey 8 | 9 | import ( 10 | "github.com/trustbloc/kms-go/doc/jose/jwk" 11 | "github.com/trustbloc/kms-go/spi/kms" 12 | ) 13 | 14 | // BytesKey contains bytes of public key. 15 | type BytesKey struct { 16 | Bytes []byte 17 | } 18 | 19 | // PublicKey contains a result of public key resolution. 20 | type PublicKey struct { 21 | Type kms.KeyType 22 | 23 | BytesKey *BytesKey 24 | JWK *jwk.JWK 25 | } 26 | -------------------------------------------------------------------------------- /jwt/testdata/interop_credential_4_secp256k1.jwt: -------------------------------------------------------------------------------- 1 | { 2 | "jwt": "eyJhbGciOiJFUzI1NksiLCJraWQiOiJkaWQ6ZXhhbXBsZToxMjMja2V5LTEifQ.eyJpc3MiOiJkaWQ6ZXhhbXBsZToxMjMiLCJzdWIiOnt9LCJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vdzNpZC5vcmcvc2VjdXJpdHkvc3VpdGVzL2p3cy0yMDIwL3YxIl0sInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiXSwiaXNzdWVyIjoiZGlkOmV4YW1wbGU6MTIzIiwiaXNzdWFuY2VEYXRlIjoiMjAyMS0wMS0wMVQxOToyMzoyNFoiLCJjcmVkZW50aWFsU3ViamVjdCI6e319LCJuYmYiOjE2MDk1MjkwMDR9.YVL3RiZBtVgB8A1Et6GkPVrNH1SEe9hSoOho-wxPavwKTs7WxFliYQk9GNladKX_Gt6Js88mlwNMHWBStE7aug" 3 | } -------------------------------------------------------------------------------- /jwt/testdata/interop_credential_7_secp256r1.jwt: -------------------------------------------------------------------------------- 1 | { 2 | "jwt": "eyJhbGciOiJFUzI1NiIsImtpZCI6ImRpZDpleGFtcGxlOjEyMyNrZXktMiJ9.eyJpc3MiOiJkaWQ6ZXhhbXBsZToxMjMiLCJzdWIiOnt9LCJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vdzNpZC5vcmcvc2VjdXJpdHkvc3VpdGVzL2p3cy0yMDIwL3YxIl0sInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiXSwiaXNzdWVyIjoiZGlkOmV4YW1wbGU6MTIzIiwiaXNzdWFuY2VEYXRlIjoiMjAyMS0wMS0wMVQxOToyMzoyNFoiLCJjcmVkZW50aWFsU3ViamVjdCI6e319LCJuYmYiOjE2MDk1MjkwMDR9.cXODOOr-lXftWixTpRL3SQcpfZi8Rluk7e_z1MUC9dnMVuILoqGAoEyG2ub5kCSU9xGxhGMP6Vf10kE-OhuhTQ" 3 | } -------------------------------------------------------------------------------- /cwt/interfaces.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Gen Digital Inc. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package cwt 8 | 9 | //go:generate mockgen -destination interfaces_mocks_test.go -package cwt_test -source=interfaces.go 10 | import ( 11 | "github.com/trustbloc/vc-go/proof/checker" 12 | ) 13 | 14 | // ProofChecker used to check proof of jwt vc. 15 | type ProofChecker interface { 16 | CheckCWTProof( 17 | checkCWTRequest checker.CheckCWTProofRequest, 18 | expectedProofIssuer string, 19 | msg []byte, 20 | signature []byte, 21 | ) error 22 | } 23 | -------------------------------------------------------------------------------- /verifiable/cache_js_wasm.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright SecureKey Technologies Inc. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package verifiable 8 | 9 | import "time" 10 | 11 | // NewExpirableSchemaCache creates new instance of ExpirableSchemaCache. 12 | func NewExpirableSchemaCache(size int, expiration time.Duration) *ExpirableSchemaCache { 13 | // TODO Add cache implementation for VC wasm https://github.com/hyperledger/aries-framework-go/issues/1009 14 | return &ExpirableSchemaCache{ 15 | cache: nil, 16 | expiration: expiration, 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /presexch/testdata/contexts/mdl-broken.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context":{ 3 | "@version":1.1, 4 | "id":"@id", 5 | "type":"@type", 6 | "name":"http://schema.org/name", 7 | "description":"http://schema.org/description", 8 | 9 | "mDL":{ 10 | "@id":"ex:mDL", 11 | "@context":{ 12 | "@version":1.1, 13 | "@protected":true, 14 | "id":"@id", 15 | "type":"@type", 16 | "xsd":"http://www.w3.org/2001/XMLSchema#", 17 | "ex":"https://example.org/examples#", 18 | "mDL":"duplicate-definition" 19 | } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /verifiable/cache.go: -------------------------------------------------------------------------------- 1 | //go:build !js && !wasm 2 | // +build !js,!wasm 3 | 4 | /* 5 | Copyright SecureKey Technologies Inc. All Rights Reserved. 6 | 7 | SPDX-License-Identifier: Apache-2.0 8 | */ 9 | 10 | package verifiable 11 | 12 | import ( 13 | "time" 14 | 15 | "github.com/VictoriaMetrics/fastcache" 16 | ) 17 | 18 | // NewExpirableSchemaCache creates new instance of ExpirableSchemaCache. 19 | func NewExpirableSchemaCache(size int, expiration time.Duration) *ExpirableSchemaCache { 20 | return &ExpirableSchemaCache{ 21 | cache: fastcache.New(size), 22 | expiration: expiration, 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /jwt/testdata/interop_credential_10_secp384r1.jwt: -------------------------------------------------------------------------------- 1 | { 2 | "jwt": "eyJhbGciOiJFUzM4NCIsImtpZCI6ImRpZDpleGFtcGxlOjEyMyNrZXktMyJ9.eyJpc3MiOiJkaWQ6ZXhhbXBsZToxMjMiLCJzdWIiOnt9LCJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vdzNpZC5vcmcvc2VjdXJpdHkvc3VpdGVzL2p3cy0yMDIwL3YxIl0sInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiXSwiaXNzdWVyIjoiZGlkOmV4YW1wbGU6MTIzIiwiaXNzdWFuY2VEYXRlIjoiMjAyMS0wMS0wMVQxOToyMzoyNFoiLCJjcmVkZW50aWFsU3ViamVjdCI6e319LCJuYmYiOjE2MDk1MjkwMDR9.05C5qLfgoT_qIuePQw7wCbBneKYVLpNdTUzXXBAhVudFX57OAkyEM02FGs1vdQo2qWELgJ8eLa1s6mNNbdyCHJqIIpQIEkeX8H1-yQNohkuWTHgO55XF4GaoghmRaSeK" 3 | } -------------------------------------------------------------------------------- /jwt/signer.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Gen Digital Inc. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package jwt 8 | 9 | import ( 10 | "github.com/trustbloc/kms-go/doc/jose" 11 | ) 12 | 13 | // SignParameters contains parameters of signing for jwt vc. 14 | type SignParameters struct { 15 | KeyID string 16 | JWTAlg string 17 | AdditionalHeaders jose.Headers 18 | } 19 | 20 | // ProofCreator defines signer interface which is used to sign VC JWT. 21 | type ProofCreator interface { 22 | SignJWT(params SignParameters, data []byte) ([]byte, error) 23 | CreateJWTHeaders(params SignParameters) (jose.Headers, error) 24 | } 25 | -------------------------------------------------------------------------------- /dataintegrity/suite/ecdsa2019/testdata/valid_credential.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": [ 3 | "https://www.w3.org/2018/credentials/v1", 4 | "https://w3id.org/security/jws/v1", 5 | "https://w3id.org/security/suites/ed25519-2020/v1" 6 | ], 7 | "id": "http://example.edu/credentials/1872", 8 | "type": "VerifiableCredential", 9 | "credentialSubject": { 10 | "id": "did:example:ebfeb1f712ebc6f1c276e12ec21" 11 | }, 12 | "issuer": { 13 | "id": "did:example:76e12ec712ebc6f1c221ebfeb1f", 14 | "name": "Example University", 15 | "image": "data:image/png;base64,iVBOR" 16 | }, 17 | "issuanceDate": "2010-01-01T19:23:24Z", 18 | "expirationDate": "2020-01-01T19:23:24Z" 19 | } 20 | -------------------------------------------------------------------------------- /dataintegrity/suite/eddsa2022/testdata/valid_credential.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": [ 3 | "https://www.w3.org/2018/credentials/v1", 4 | "https://w3id.org/security/jws/v1", 5 | "https://w3id.org/security/suites/ed25519-2020/v1" 6 | ], 7 | "id": "http://example.edu/credentials/1872", 8 | "type": "VerifiableCredential", 9 | "credentialSubject": { 10 | "id": "did:example:ebfeb1f712ebc6f1c276e12ec21" 11 | }, 12 | "issuer": { 13 | "id": "did:example:76e12ec712ebc6f1c221ebfeb1f", 14 | "name": "Example University", 15 | "image": "data:image/png;base64,iVBOR" 16 | }, 17 | "issuanceDate": "2010-01-01T19:23:24Z", 18 | "expirationDate": "2020-01-01T19:23:24Z" 19 | } 20 | -------------------------------------------------------------------------------- /proof/creator/creator_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Gen Digital Inc. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package creator_test 8 | 9 | import ( 10 | "testing" 11 | 12 | "github.com/trustbloc/vc-go/proof/testsupport/commontest" 13 | ) 14 | 15 | func TestProofCreator_Common(t *testing.T) { 16 | t.Run("Test With all LD proofs", func(t *testing.T) { 17 | commontest.TestAllLDSignersVerifiers(t) 18 | }) 19 | 20 | t.Run("Test With all jwt proofs", func(t *testing.T) { 21 | commontest.TestAllJWTSignersVerifiers(t) 22 | }) 23 | 24 | t.Run("Test With all cwt proofs", func(t *testing.T) { 25 | commontest.TestAllCWTSignersVerifiers(t) 26 | }) 27 | } 28 | -------------------------------------------------------------------------------- /scripts/check_lint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright SecureKey Technologies Inc. All Rights Reserved. 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | # 7 | 8 | set -e 9 | 10 | echo "Running $0" 11 | 12 | DOCKER_CMD=${DOCKER_CMD:-docker} 13 | GOLANGCI_LINT_IMAGE="golangci/golangci-lint:v2.5.0" 14 | SHARED_OPTS="--rm --security-opt seccomp=unconfined -e GOPROXY=${GOPROXY} -v $(pwd):/opt/workspace" 15 | 16 | if [ ! $(command -v ${DOCKER_CMD}) ]; then 17 | exit 0 18 | fi 19 | 20 | echo "linting root directory.." 21 | ${DOCKER_CMD} run ${SHARED_OPTS} -w /opt/workspace ${GOLANGCI_LINT_IMAGE} golangci-lint run 22 | echo "done linting root directory" 23 | 24 | echo "Done Running $0" 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright Gen Digital Inc. All Rights Reserved. 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | 7 | # Binaries for programs and plugins 8 | *.exe 9 | *.exe~ 10 | *.dll 11 | *.so 12 | *.dylib 13 | *.wasm 14 | *.wasm.gz 15 | 16 | # Editor and go temporary files & folders 17 | .swp 18 | vendor 19 | 20 | # Test binary, build with `go test -c` 21 | *.test 22 | 23 | # Output of the go coverage tool, specifically when used with LiteIDE 24 | *.out 25 | .idea 26 | .DS_Store 27 | 28 | coverage.txt 29 | 30 | # Exclude build directory 31 | .build 32 | *.log 33 | 34 | node_modules 35 | dist 36 | build 37 | umd 38 | 39 | # Auto-generated mock files 40 | *_mocks_test.go 41 | gomocks_test.go -------------------------------------------------------------------------------- /scripts/check_unit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright Avast Software. All Rights Reserved. 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | # 7 | set -e 8 | 9 | echo "Running $0" 10 | 11 | pwd=`pwd` 12 | touch "$pwd"/coverage.out 13 | 14 | amend_coverage_file () { 15 | if [ -f profile.out ]; then 16 | cat profile.out | grep -v ".gen.go" >> "$pwd"/coverage.out 17 | rm profile.out 18 | fi 19 | } 20 | 21 | # Running wallet-sdk unit tests 22 | PKGS=`go list github.com/trustbloc/vc-go/... 2> /dev/null | \ 23 | grep -v /mocks` 24 | go test $PKGS -count=1 -race -coverprofile=profile.out -covermode=atomic -timeout=10m 25 | amend_coverage_file 26 | 27 | 28 | cd "$pwd" -------------------------------------------------------------------------------- /verifiable/presentation_ldp.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright SecureKey Technologies Inc. All Rights Reserved. 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package verifiable 7 | 8 | import ( 9 | "fmt" 10 | 11 | ldprocessor "github.com/trustbloc/did-go/doc/ld/processor" 12 | ) 13 | 14 | // AddLinkedDataProof appends proof to the Verifiable Presentation. 15 | func (vp *Presentation) AddLinkedDataProof(context *LinkedDataProofContext, jsonldOpts ...ldprocessor.Opts) error { 16 | raw, err := vp.raw() 17 | if err != nil { 18 | return fmt.Errorf("add linked data proof to VP: %w", err) 19 | } 20 | 21 | proofs, err := addLinkedDataProof(context, raw, jsonldOpts...) 22 | if err != nil { 23 | return err 24 | } 25 | 26 | vp.Proofs = proofs 27 | 28 | return nil 29 | } 30 | -------------------------------------------------------------------------------- /verifiable/testdata/context/presentation_submission_v1.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": { 3 | "@version": 1.1, 4 | "id": "@id", 5 | "type": "@type", 6 | 7 | "PresentationSubmission": { 8 | "@id": "ex:PresentationSubmission", 9 | "@context": { 10 | "@version": 1.1, 11 | "@protected": true, 12 | 13 | "id": "@id", 14 | "type": "@type" 15 | } 16 | }, 17 | "ex": "https://example.org/examples#", 18 | "presentation_submission": {"@id": "ex:presentation_submission", "@type": "@id"}, 19 | "descriptor_map": {"@id": "ex:descriptor_map", "@type": "@id"}, 20 | "path": {"@id": "ex:path", "@type": "@id"} 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /verifiable/credential_ldp.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright SecureKey Technologies Inc. All Rights Reserved. 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package verifiable 7 | 8 | import ( 9 | "github.com/trustbloc/did-go/doc/ld/processor" 10 | ) 11 | 12 | // AddLinkedDataProof appends proof to the Verifiable Credential. 13 | func (vc *Credential) AddLinkedDataProof(context *LinkedDataProofContext, jsonldOpts ...processor.Opts) error { 14 | proofs, err := addLinkedDataProof(context, vc.credentialJSON, jsonldOpts...) 15 | if err != nil { 16 | return err 17 | } 18 | 19 | vc.ldProofs = proofs 20 | 21 | return nil 22 | } 23 | 24 | // ResetProofs sets new proofs for vc. 25 | func (vc *Credential) ResetProofs(newProofs []Proof) { 26 | vc.credentialJSON[jsonFldLDProof] = proofsToRaw(newProofs) 27 | vc.ldProofs = newProofs 28 | } 29 | -------------------------------------------------------------------------------- /verifiable/jwt_unsecured.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright SecureKey Technologies Inc. All Rights Reserved. 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package verifiable 7 | 8 | import ( 9 | "fmt" 10 | 11 | "github.com/trustbloc/kms-go/doc/jose" 12 | 13 | "github.com/trustbloc/vc-go/jwt" 14 | ) 15 | 16 | func marshalUnsecuredJWT(claims interface{}) (string, error) { 17 | token, err := jwt.NewUnsecured(claims) 18 | if err != nil { 19 | return "", fmt.Errorf("marshal unsecured JWT: %w", err) 20 | } 21 | 22 | return token.Serialize(false) 23 | } 24 | 25 | func unmarshalUnsecuredJWT(rawJWT string, claims interface{}) (jose.Headers, error) { 26 | token, _, err := jwt.Parse(rawJWT) 27 | if err != nil { 28 | return nil, fmt.Errorf("unmarshal unsecured JWT: %w", err) 29 | } 30 | 31 | return token.Headers, token.DecodeClaims(claims) 32 | } 33 | -------------------------------------------------------------------------------- /verifiable/credential_jws.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright SecureKey Technologies Inc. All Rights Reserved. 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package verifiable 7 | 8 | import ( 9 | "github.com/trustbloc/kms-go/doc/jose" 10 | 11 | "github.com/trustbloc/vc-go/jwt" 12 | ) 13 | 14 | // MarshalJWS serializes JWT into signed form (JWS). 15 | func (jcc *JWTCredClaims) MarshalJWS( 16 | signatureAlg JWSAlgorithm, 17 | signer jwt.ProofCreator, 18 | keyID string, 19 | ) (string, jose.Headers, error) { 20 | return marshalJWS(jcc, signatureAlg, signer, keyID) 21 | } 22 | 23 | // MarshalJWSString serializes JWT into signed form (JWS). 24 | func (jcc *JWTCredClaims) MarshalJWSString( 25 | signatureAlg JWSAlgorithm, 26 | signer jwt.ProofCreator, 27 | keyID string, 28 | ) (string, error) { 29 | strJWT, _, err := marshalJWS(jcc, signatureAlg, signer, keyID) 30 | return strJWT, err 31 | } 32 | -------------------------------------------------------------------------------- /verifiable/presentation_jwt_unsecured.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright SecureKey Technologies Inc. All Rights Reserved. 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package verifiable 7 | 8 | import ( 9 | "fmt" 10 | ) 11 | 12 | // MarshalUnsecuredJWT serializes JWT presentation claims into unsecured JWT. 13 | func (jpc *JWTPresClaims) MarshalUnsecuredJWT() (string, error) { 14 | return marshalUnsecuredJWT(jpc) 15 | } 16 | 17 | func unmarshalUnsecuredJWTPresClaims(vpJWT string) (*JWTPresClaims, error) { 18 | var claims JWTPresClaims 19 | 20 | _, err := unmarshalUnsecuredJWT(vpJWT, &claims) 21 | if err != nil { 22 | return nil, fmt.Errorf("parse VP in JWT Unsecured form: %w", err) 23 | } 24 | 25 | return &claims, nil 26 | } 27 | 28 | func decodeVPFromUnsecuredJWT(vpJWT string) ([]byte, rawPresentation, error) { 29 | return decodePresJWT(vpJWT, unmarshalUnsecuredJWTPresClaims) 30 | } 31 | -------------------------------------------------------------------------------- /status/validator/validator.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Avast Software. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | // Package validator holds validation handlers for status fields 8 | // for different formats of verifiable credential status list. 9 | package validator 10 | 11 | import ( 12 | "fmt" 13 | 14 | "github.com/trustbloc/vc-go/status/api" 15 | "github.com/trustbloc/vc-go/status/validator/bitstringstatus" 16 | "github.com/trustbloc/vc-go/status/validator/statuslist2021" 17 | ) 18 | 19 | // GetValidator returns the VC status list validator for the given status type. 20 | func GetValidator(statusType string) (api.Validator, error) { //nolint:ireturn 21 | switch statusType { 22 | case statuslist2021.StatusList2021Type: 23 | return &statuslist2021.Validator{}, nil 24 | case bitstringstatus.BitstringStatusListType: 25 | return &bitstringstatus.Validator{}, nil 26 | default: 27 | return nil, fmt.Errorf("unsupported VCStatusListType %s", statusType) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /verifiable/cwt/cwt.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Gen Digital Inc. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package cwt 8 | 9 | import ( 10 | "github.com/fxamacker/cbor/v2" 11 | "github.com/veraison/go-cose" 12 | ) 13 | 14 | // GetProofValue returns the proof value for the given COSE_Sign1 message. 15 | func GetProofValue(message *cose.Sign1Message) ([]byte, error) { 16 | var protected cbor.RawMessage 17 | protected, err := message.Headers.MarshalProtected() 18 | 19 | if err != nil { 20 | return nil, err 21 | } 22 | 23 | cborProtectedData, err := deterministicBinaryString(protected) 24 | if err != nil { 25 | return nil, err 26 | } 27 | 28 | sigStructure := []interface{}{ 29 | "Signature1", // context 30 | cborProtectedData, // body_protected 31 | []byte{}, // external_aad 32 | message.Payload, // payload 33 | } 34 | 35 | cborData, err := cbor.Marshal(sigStructure) 36 | if err != nil { 37 | return nil, err 38 | } 39 | 40 | return cborData, nil 41 | } 42 | -------------------------------------------------------------------------------- /cwt/wrappers.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Gen Digital Inc. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package cwt 8 | 9 | import ( 10 | "strings" 11 | 12 | "github.com/veraison/go-cose" 13 | 14 | "github.com/trustbloc/vc-go/proof/checker" 15 | ) 16 | 17 | // Verifier verifies CWT proof. 18 | type Verifier struct { 19 | ProofChecker ProofChecker 20 | expectedProofIssuer *string 21 | } 22 | 23 | // Verify verifies CWT proof. 24 | func (v *Verifier) Verify( 25 | keyMaterial string, 26 | keyID string, 27 | algo cose.Algorithm, 28 | msg []byte, 29 | sign []byte, 30 | ) error { 31 | var expectedProofIssuer string 32 | if v.expectedProofIssuer != nil { 33 | expectedProofIssuer = *v.expectedProofIssuer 34 | } 35 | 36 | if expectedProofIssuer == "" && keyID != "" { 37 | expectedProofIssuer = strings.Split(keyID, "#")[0] 38 | } 39 | 40 | return v.ProofChecker.CheckCWTProof(checker.CheckCWTProofRequest{ 41 | KeyMaterial: keyMaterial, 42 | KeyID: keyID, 43 | Algo: algo, 44 | }, expectedProofIssuer, msg, sign) 45 | } 46 | -------------------------------------------------------------------------------- /status/api/api.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Avast Software. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | // Package api contains dependency-injection interfaces for Credential Status validation clients. 8 | package api 9 | 10 | import ( 11 | "github.com/trustbloc/vc-go/verifiable" 12 | ) 13 | 14 | // Validator holds handlers for validating a particular format of Status(Revocation) List VC. 15 | type Validator interface { 16 | ValidateStatus(vcStatus *verifiable.TypedID) error 17 | GetStatusVCURI(vcStatus *verifiable.TypedID) (string, error) 18 | GetStatusListIndex(vcStatus *verifiable.TypedID) (int, error) 19 | GetStatusPurpose(vcStatus *verifiable.TypedID) (string, error) 20 | MultiBaseEncoding() bool 21 | } 22 | 23 | // ValidatorGetter provides the matching Validator for a given credential status type. 24 | type ValidatorGetter func(statusType string) (Validator, error) 25 | 26 | // StatusListVCURIResolver resolves a VC StatusList Credential. 27 | type StatusListVCURIResolver interface { 28 | Resolve(statusListVCURL string) (*verifiable.Credential, error) 29 | } 30 | -------------------------------------------------------------------------------- /util/maphelpers/mapstruct.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright SecureKey Technologies Inc. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package maphelpers 8 | 9 | import ( 10 | "fmt" 11 | "reflect" 12 | "strconv" 13 | "strings" 14 | "time" 15 | 16 | "github.com/go-jose/go-jose/v3/jwt" 17 | "github.com/mitchellh/mapstructure" 18 | ) 19 | 20 | // JSONNumberToJwtNumericDate hook for mapstructure library to decode json.Number to jwt.NumericDate. 21 | func JSONNumberToJwtNumericDate() mapstructure.DecodeHookFuncType { 22 | return func( 23 | f reflect.Type, 24 | t reflect.Type, 25 | data interface{}, 26 | ) (interface{}, error) { 27 | if f.String() != "json.Number" || !strings.Contains("jwt.NumericDate", t.String()) { 28 | return data, nil 29 | } 30 | 31 | parsedFloat, err := strconv.ParseFloat(fmt.Sprint(data), 64) 32 | if err != nil { 33 | return nil, err 34 | } 35 | 36 | date := jwt.NewNumericDate(time.Unix(int64(parsedFloat), 0)) 37 | 38 | if t.String() == "jwt.NumericDate" { 39 | return date, nil 40 | } 41 | 42 | return &date, nil 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /cwt/wrappers_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Gen Digital Inc. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package cwt_test 8 | 9 | import ( 10 | "testing" 11 | 12 | "github.com/golang/mock/gomock" 13 | "github.com/stretchr/testify/assert" 14 | "github.com/veraison/go-cose" 15 | 16 | "github.com/trustbloc/vc-go/cwt" 17 | "github.com/trustbloc/vc-go/proof/checker" 18 | ) 19 | 20 | func TestWrapper(t *testing.T) { 21 | t.Run("extract issuer", func(t *testing.T) { 22 | mockVerifier := NewMockProofChecker(gomock.NewController(t)) 23 | verifier := cwt.Verifier{ 24 | ProofChecker: mockVerifier, 25 | } 26 | 27 | mockVerifier.EXPECT().CheckCWTProof(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). 28 | DoAndReturn(func(request checker.CheckCWTProofRequest, expectedProofIssuer string, 29 | bytes []byte, bytes2 []byte) error { 30 | assert.Equal(t, "coap://as.example.com", expectedProofIssuer) 31 | 32 | return nil 33 | }) 34 | 35 | assert.NoError(t, verifier.Verify("", "coap://as.example.com#AsymmetricECDSA256#321232131", 36 | cose.AlgorithmEdDSA, nil, nil)) 37 | }) 38 | } 39 | -------------------------------------------------------------------------------- /verifiable/testdata/v2_credential_without_issuer.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": [ 3 | "https://www.w3.org/ns/credentials/v2", 4 | "https://www.w3.org/ns/credentials/examples/v2" 5 | ], 6 | "id": "http://example.edu/credentials/1872", 7 | "type": ["VerifiableCredential", "AlumniCredential"], 8 | "credentialSubject": { 9 | "id": "did:example:ebfeb1f712ebc6f1c276e12ec21", 10 | "alumniOf": { 11 | "id": "did:example:c276e12ec21ebfeb1f712ebc6f1", 12 | "name": [{ 13 | "value": "Example University", 14 | "lang": "en" 15 | }, { 16 | "value": "Exemple d'Université", 17 | "lang": "fr" 18 | }] 19 | } 20 | }, 21 | "proof": { 22 | "type": "RsaSignature2018", 23 | "created": "2017-06-18T21:19:10Z", 24 | "proofPurpose": "assertionMethod", 25 | "verificationMethod": "https://example.edu/issuers/565049#key-1", 26 | "jws": "eyJhbGciOiJSUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..TCYt5XsITJX1CxPCT8yAV-TVkIEq_PbChOMqsLfRoPsnsgw5WEuts01mq-pQy7UJiN5mgRxD-WUcX16dUEMGlv50aqzpqh4Qktb3rk-BuQy72IFLOqV0G_zS245-kronKb78cPN25DGlcTwLtjPAYuNzVBAh4vGHSrQyHUdBBPM" 27 | } 28 | } -------------------------------------------------------------------------------- /verifiable/testdata/valid_doc.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": ["https://w3id.org/did/v1", "https://w3id.org/security/v2"], 3 | "id": "did:example:21tDAKCERh95uGgKbJNHYp", 4 | "verificationMethod": [ 5 | { 6 | "id": "did:example:123456789abcdefghi#keys-1", 7 | "type": "EcdsaSecp256k1VerificationKey2019", 8 | "controller": "did:example:123456789abcdefghi", 9 | "publicKeyBase58": "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV" 10 | }, 11 | { 12 | "id": "did:example:123456789abcdefghw#key2", 13 | "type": "RsaVerificationKey2018", 14 | "controller": "did:example:123456789abcdefghw", 15 | "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAryQICCl6NZ5gDKrnSztO\n3Hy8PEUcuyvg/ikC+VcIo2SFFSf18a3IMYldIugqqqZCs4/4uVW3sbdLs/6PfgdX\n7O9D22ZiFWHPYA2k2N744MNiCD1UE+tJyllUhSblK48bn+v1oZHCM0nYQ2NqUkvS\nj+hwUU3RiWl7x3D2s9wSdNt7XUtW05a/FXehsPSiJfKvHJJnGOX0BgTvkLnkAOTd\nOrUZ/wK69Dzu4IvrN4vs9Nes8vbwPa/ddZEzGR0cQMt0JBkhk9kU/qwqUseP1QRJ\n5I1jR4g8aYPL/ke9K35PxZWuDp3U0UPAZ3PjFAh+5T+fc7gzCs9dPzSHloruU+gl\nFQIDAQAB\n-----END PUBLIC KEY-----" 16 | } 17 | ], 18 | "created": "2002-10-10T17:00:00Z" 19 | } -------------------------------------------------------------------------------- /sdjwt/common/testdata/array_element_and_one_missing_v5.json: -------------------------------------------------------------------------------- 1 | [ 2 | "WyI2NDYwRkU1STJvN0l0bktGX2s4YWZ3IiwiYWRkcmVzcyIseyJfc2QiOlsiQjJBQVFzTVk4V1B1bWZoOWtXY3J1RXV4TUVaT2J5bW5MNGU2OVB5U0psNCIsIkV3TGVJbVFVdWIyS1F6eURoNmxnU1c1TnZsMExqQWFlaXFCZHJzeDY1T28iLCI4TG56SUJ5QlNDZ243SG1zcURUbW1GeXZSVTRRdHRuaVNlZE4xanN2LXhvIiwibk54dThkU3laV0x3QVAteTJtV0k5aXRpMmRHejQ5RUM0eDY2RGxDZ0QwZyJdLCJleHRyYSI6eyJfc2QiOlsib2RqUjRraHQxcGVNZFRUMzVFMUotWXF5Q0gyM0dfSGhrQXRLZks0cUpIVSJdfSwiZXh0cmFBcnJJbmNsdWRlIjpbeyIuLi4iOiIxRVN5RGlLTE9KbEF2VnYtQnJaN1JQTU4zRXVOdU1Jc014aXVMeGhZWjg0In0sIlBMIl0sInJlZ2lvbiI6IlNhY2hzZW4tQW5oYWx0In1d", 3 | "WyJZbEFCTmZkaXhKSVF4Z3hNZ0RTcUpRIiwiY291bnRyeSIsIkRFIl0", 4 | "WyJJdGZ2ellZdXlMSTF3TGZLU213M0dRIiwiZXh0cmFBcnIiLFt7Ii4uLiI6IkRsVHpXT0tWbzJNbzNPNUZER0hpWGhuSnd4c2hBTkQyMUVibmQyWFRiT0kifSx7Ii4uLiI6IkMwbUl4SXdEQm4xZ1IxamZBTDFYZVJNdGlYLVdDMVBjc3FUVkphdnJMQTQifV1d", 5 | "WyJkWW10LWNjcWMyeUJYd1ZGTFZkeFdnIiwic3RyZWV0X2FkZHJlc3MiLCJTY2h1bHN0ci4gMTIiXQ", 6 | "WyIxRTlRZnRDS3YtbTFjN0VFOXlXMmh3IiwiRXh0cmExIl0", 7 | "WyI0Mjl4ejFGeTlEdU9SQ0R2cHd3bzFBIiwiRXh0cmEyIl0", 8 | "WyJvVy1oMDZYVUNsTU45YTBVV3VHMGhBIiwicmVjdXJzaXZlIix7Il9zZCI6WyJoX2h1bVhsYjhVekM5T0tGOHc3SEd6ZmYzSGgzMmh3SGR6Vms5WS1oOGR3Il19XQ", 9 | "WyItdHBPTUlPS3dnOUNtNlBRVTlDTktBIiwia2V5MSIsInZhbHVlMSJd" 10 | ] -------------------------------------------------------------------------------- /verifiable/presentation_jws.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright SecureKey Technologies Inc. All Rights Reserved. 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package verifiable 7 | 8 | import ( 9 | "fmt" 10 | 11 | "github.com/trustbloc/vc-go/jwt" 12 | ) 13 | 14 | // MarshalJWS serializes JWT presentation claims into signed form (JWS). 15 | func (jpc *JWTPresClaims) MarshalJWS(signatureAlg JWSAlgorithm, signer jwt.ProofCreator, keyID string) (string, error) { 16 | strJWT, _, err := marshalJWS(jpc, signatureAlg, signer, keyID) 17 | return strJWT, err 18 | } 19 | 20 | func unmarshalPresJWSClaims(vpJWT string, verifier jwt.ProofChecker) (*JWTPresClaims, error) { 21 | var claims JWTPresClaims 22 | 23 | _, err := unmarshalJWT(vpJWT, &claims) 24 | if err != nil { 25 | return nil, err 26 | } 27 | 28 | if verifier != nil { 29 | err = jwt.CheckProof(vpJWT, verifier, nil, nil) 30 | if err != nil { 31 | return nil, fmt.Errorf("jwt proof check: %w", err) 32 | } 33 | } 34 | 35 | return &claims, err 36 | } 37 | 38 | func decodeVPFromJWS(vpJWT string, verifier jwt.ProofChecker) ([]byte, rawPresentation, error) { 39 | return decodePresJWT(vpJWT, func(vpJWT string) (*JWTPresClaims, error) { 40 | return unmarshalPresJWSClaims(vpJWT, verifier) 41 | }) 42 | } 43 | -------------------------------------------------------------------------------- /verifiable/jws.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright SecureKey Technologies Inc. All Rights Reserved. 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package verifiable 7 | 8 | import ( 9 | "fmt" 10 | 11 | "github.com/trustbloc/kms-go/doc/jose" 12 | 13 | "github.com/trustbloc/vc-go/jwt" 14 | ) 15 | 16 | // MarshalJWS serializes JWT presentation claims into signed form (JWS). 17 | func marshalJWS(jwtClaims interface{}, signatureAlg JWSAlgorithm, 18 | signer jwt.ProofCreator, keyID string) (string, jose.Headers, error) { 19 | algName, err := signatureAlg.Name() 20 | if err != nil { 21 | return "", nil, err 22 | } 23 | 24 | signParameters := jwt.SignParameters{ 25 | KeyID: keyID, 26 | JWTAlg: algName, 27 | } 28 | 29 | token, err := jwt.NewSigned(jwtClaims, signParameters, signer) 30 | if err != nil { 31 | return "", nil, err 32 | } 33 | 34 | jwtStr, err := token.Serialize(false) 35 | if err != nil { 36 | return "", nil, err 37 | } 38 | 39 | return jwtStr, token.Headers, nil 40 | } 41 | 42 | func unmarshalJWT(rawJwt string, claims interface{}) (jose.Headers, error) { 43 | jsonWebToken, _, err := jwt.Parse(rawJwt, 44 | jwt.DecodeClaimsTo(claims), 45 | jwt.WithIgnoreClaimsMapDecoding(true), 46 | ) 47 | if err != nil { 48 | return nil, fmt.Errorf("parse JWT: %w", err) 49 | } 50 | 51 | return jsonWebToken.Headers, nil 52 | } 53 | -------------------------------------------------------------------------------- /verifiable/credential_testsuite.go: -------------------------------------------------------------------------------- 1 | //go:build testsuite 2 | // +build testsuite 3 | 4 | /* 5 | Copyright SecureKey Technologies Inc. All Rights Reserved. 6 | SPDX-License-Identifier: Apache-2.0 7 | 8 | This is not actually a test but rather a stand-alone generator application 9 | that is used by VC Test Suite (https://github.com/w3c/vc-test-suite). 10 | To run VC Test Suite, execute `make vc-test-suite`. 11 | */ 12 | 13 | package verifiable 14 | 15 | // WithNoProofCheck disables checking of Verifiable Credential's proofs. 16 | func WithNoProofCheck() CredentialOpt { 17 | return func(opts *credentialOpts) { 18 | opts.disabledProofCheck = true 19 | } 20 | } 21 | 22 | // WithPresNoProofCheck tells to skip checking of Verifiable Presentation's proofs. 23 | func WithPresNoProofCheck() PresentationOpt { 24 | return func(opts *presentationOpts) { 25 | opts.disabledProofCheck = true 26 | } 27 | } 28 | 29 | // WithPresRequireVC option enables check for at least one verifiableCredential in the VP. 30 | func WithPresRequireVC() PresentationOpt { 31 | return func(opts *presentationOpts) { 32 | opts.requireVC = true 33 | } 34 | } 35 | 36 | // WithPresRequireProof option enables check for at least one proof in the VP. 37 | func WithPresRequireProof() PresentationOpt { 38 | return func(opts *presentationOpts) { 39 | opts.requireProof = true 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /sdjwt/common/testdata/full_disclosures_v5.json: -------------------------------------------------------------------------------- 1 | [ 2 | "WyI2NDYwRkU1STJvN0l0bktGX2s4YWZ3IiwiYWRkcmVzcyIseyJfc2QiOlsiQjJBQVFzTVk4V1B1bWZoOWtXY3J1RXV4TUVaT2J5bW5MNGU2OVB5U0psNCIsIkV3TGVJbVFVdWIyS1F6eURoNmxnU1c1TnZsMExqQWFlaXFCZHJzeDY1T28iLCI4TG56SUJ5QlNDZ243SG1zcURUbW1GeXZSVTRRdHRuaVNlZE4xanN2LXhvIiwibk54dThkU3laV0x3QVAteTJtV0k5aXRpMmRHejQ5RUM0eDY2RGxDZ0QwZyJdLCJleHRyYSI6eyJfc2QiOlsib2RqUjRraHQxcGVNZFRUMzVFMUotWXF5Q0gyM0dfSGhrQXRLZks0cUpIVSJdfSwiZXh0cmFBcnJJbmNsdWRlIjpbeyIuLi4iOiIxRVN5RGlLTE9KbEF2VnYtQnJaN1JQTU4zRXVOdU1Jc014aXVMeGhZWjg0In0sIlBMIl0sInJlZ2lvbiI6IlNhY2hzZW4tQW5oYWx0In1d", 3 | "WyJZbEFCTmZkaXhKSVF4Z3hNZ0RTcUpRIiwiY291bnRyeSIsIkRFIl0", 4 | "WyJJdGZ2ellZdXlMSTF3TGZLU213M0dRIiwiZXh0cmFBcnIiLFt7Ii4uLiI6IkRsVHpXT0tWbzJNbzNPNUZER0hpWGhuSnd4c2hBTkQyMUVibmQyWFRiT0kifSx7Ii4uLiI6IkMwbUl4SXdEQm4xZ1IxamZBTDFYZVJNdGlYLVdDMVBjc3FUVkphdnJMQTQifV1d", 5 | "WyJkWW10LWNjcWMyeUJYd1ZGTFZkeFdnIiwic3RyZWV0X2FkZHJlc3MiLCJTY2h1bHN0ci4gMTIiXQ", 6 | "WyJURWtwSjJkYWxraGltUUVLd25Cblp3IiwiVUEiXQ", 7 | "WyIxRTlRZnRDS3YtbTFjN0VFOXlXMmh3IiwiRXh0cmExIl0", 8 | "WyI0Mjl4ejFGeTlEdU9SQ0R2cHd3bzFBIiwiRXh0cmEyIl0", 9 | "WyJvVy1oMDZYVUNsTU45YTBVV3VHMGhBIiwicmVjdXJzaXZlIix7Il9zZCI6WyJoX2h1bVhsYjhVekM5T0tGOHc3SEd6ZmYzSGgzMmh3SGR6Vms5WS1oOGR3Il19XQ", 10 | "WyItdHBPTUlPS3dnOUNtNlBRVTlDTktBIiwia2V5MSIsInZhbHVlMSJd", 11 | "WyJ5WElBaTZSb1Y1eDV2X3lsVm1wXzhBIiwibG9jYWxpdHkiLCJTY2h1bHBmb3J0YSJd" 12 | ] -------------------------------------------------------------------------------- /dataintegrity/dataintegrity.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Gen Digital Inc. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package dataintegrity 8 | 9 | import ( 10 | "errors" 11 | 12 | "github.com/trustbloc/did-go/doc/did" 13 | vdrapi "github.com/trustbloc/did-go/vdr/api" 14 | ) 15 | 16 | var ( 17 | // ErrUnsupportedSuite is returned when a Signer or Verifier is required to use 18 | // a cryptographic suite for which it doesn't have a suite.Signer or 19 | // suite.Verifier (respectively) initialized. 20 | ErrUnsupportedSuite = errors.New("data integrity proof requires unsupported cryptographic suite") 21 | // ErrNoResolver is returned when a Signer or Verifier needs to resolve a 22 | // verification method but has no DID resolver. 23 | ErrNoResolver = errors.New("either did resolver or both verification method and verification relationship must be provided") //nolint:lll 24 | // ErrVMResolution is returned when a Signer or Verifier needs to resolve a 25 | // verification method but this fails. 26 | ErrVMResolution = errors.New("failed to resolve verification method") 27 | ) 28 | 29 | type didResolver interface { 30 | Resolve(did string, opts ...vdrapi.DIDMethodOption) (*did.DocResolution, error) 31 | } 32 | 33 | // Options contains initialization parameters for Data Integrity Signer and Verifier. 34 | type Options struct { 35 | DIDResolver didResolver 36 | } 37 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Copyright SecureKey Technologies Inc. 2 | # 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | export GOTOOLCHAIN=go1.25.0+auto 6 | # Tool commands (overridable) 7 | DOCKER_CMD ?= docker 8 | 9 | GOBIN_PATH=$(abspath .)/build/bin 10 | MOCKGEN=$(GOBIN_PATH)/mockgen 11 | GOMOCKS=pkg/internal/gomocks 12 | MOCK_VERSION ?=v1.7.0-rc.1 13 | 14 | OS := $(shell uname) 15 | ifeq ($(OS),$(filter $(OS),Darwin Linux)) 16 | PATH:=$(PATH):$(GOBIN_PATH) 17 | else 18 | PATH:=$(PATH);$(subst /,\\,$(GOBIN_PATH)) 19 | endif 20 | 21 | .PHONY: all 22 | all: clean checks unit-test 23 | 24 | .PHONY: checks 25 | checks: generate license #lint 26 | 27 | .PHONY: lint 28 | lint: generate 29 | @scripts/check_lint.sh 30 | 31 | .PHONY: license 32 | license: 33 | @scripts/check_license.sh 34 | 35 | .PHONY: unit-test 36 | unit-test: generate 37 | @scripts/check_unit.sh 38 | 39 | .PHONY: clean 40 | clean: 41 | @rm -rf ./.build 42 | @rm -rf coverage*.out 43 | 44 | .PHONY: generate 45 | generate: 46 | @GOBIN=$(GOBIN_PATH) go install github.com/golang/mock/mockgen@$(MOCK_VERSION) 47 | @go generate ./... 48 | 49 | .PHONY: tidy-modules 50 | tidy-modules: 51 | @find . -type d \( -name build -prune \) -o -name go.mod -print | while read -r gomod_path; do \ 52 | dir_path=$$(dirname "$$gomod_path"); \ 53 | echo "Executing 'go mod tidy' in directory: $$dir_path"; \ 54 | (cd "$$dir_path" && GOPROXY=$(GOPROXY) go mod tidy) || exit 1; \ 55 | done -------------------------------------------------------------------------------- /verifiable/jwt_unsecured_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright SecureKey Technologies Inc. All Rights Reserved. 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package verifiable 7 | 8 | import ( 9 | "testing" 10 | 11 | "github.com/stretchr/testify/require" 12 | 13 | "github.com/trustbloc/kms-go/doc/jose" 14 | ) 15 | 16 | func TestUnsecuredJWT(t *testing.T) { 17 | headers := jose.Headers{"alg": "none"} 18 | claims := map[string]interface{}{"sub": "user123", "productIds": []interface{}{1., 2.}} 19 | 20 | serializedJWT, err := marshalUnsecuredJWT(claims) 21 | require.NoError(t, err) 22 | require.NotEmpty(t, serializedJWT) 23 | 24 | var claimsParsed map[string]interface{} 25 | joseHeaders, err := unmarshalUnsecuredJWT(serializedJWT, &claimsParsed) 26 | require.NoError(t, err) 27 | 28 | require.Equal(t, claims, claimsParsed) 29 | require.Equal(t, joseHeaders, headers) 30 | 31 | // marshal with invalid claims 32 | invalidClaims := map[string]interface{}{"error": map[chan int]interface{}{make(chan int): 6}} 33 | serializedJWT, err = marshalUnsecuredJWT(invalidClaims) 34 | require.Error(t, err) 35 | require.Contains(t, err.Error(), "marshal unsecured JWT") 36 | require.Empty(t, serializedJWT) 37 | 38 | // unmarshal invalid JWT 39 | joseHeaders, err = unmarshalUnsecuredJWT("not a valid compact serialized JWT", &claimsParsed) 40 | require.Error(t, err) 41 | require.Contains(t, err.Error(), "marshal unsecured JWT") 42 | require.Empty(t, serializedJWT) 43 | require.Empty(t, joseHeaders) 44 | } 45 | -------------------------------------------------------------------------------- /verifiable/testdata/v2_valid_credential.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": [ 3 | "https://www.w3.org/ns/credentials/v2", 4 | "https://www.w3.org/ns/credentials/examples/v2" 5 | ], 6 | "id": "http://example.edu/credentials/1872", 7 | "type": ["VerifiableCredential", "AlumniCredential"], 8 | "issuer": { 9 | "id": "did:example:76e12ec712ebc6f1c221ebfeb1f", 10 | "name": "Example University", 11 | "image": "data:image/png;base64,iVBOR" 12 | }, 13 | "validFrom": "2010-01-01T19:23:24Z", 14 | "credentialStatus": { 15 | "id": "urn:uuid:44118c22-5b7b-4d1f-8818-01ea041648e2", 16 | "statusListCredential": "http://vc-rest-echo.trustbloc.local:8075/issuer/groups/f9af5dcf-23d8-490f-b8cb-d8b83afcb91a/credentials/status/5a8f8941-aeb9-4200-ae03-76556745ec41", 17 | "statusListIndex": "5341", 18 | "statusPurpose": "revocation", 19 | "type": "BitstringStatusListEntry" 20 | }, 21 | "credentialSubject": { 22 | "id": "did:example:ebfeb1f712ebc6f1c276e12ec21", 23 | "alumniOf": { 24 | "id": "did:example:c276e12ec21ebfeb1f712ebc6f1", 25 | "name": [{ 26 | "value": "Example University", 27 | "lang": "en" 28 | }, { 29 | "value": "Exemple d'Université", 30 | "lang": "fr" 31 | }] 32 | } 33 | }, 34 | "termsOfUse": { 35 | "type": "TrustFrameworkPolicy", 36 | "trustFramework": "Employment&Life", 37 | "policyId": "https://policy.example/policies/125", 38 | "legalBasis": "professional qualifications directive" 39 | } 40 | } -------------------------------------------------------------------------------- /verifiable/credential_bbs.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright SecureKey Technologies Inc. All Rights Reserved. 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package verifiable 7 | 8 | import ( 9 | "encoding/json" 10 | "errors" 11 | "fmt" 12 | 13 | jsonutil "github.com/trustbloc/vc-go/util/json" 14 | ) 15 | 16 | // GenerateBBSSelectiveDisclosure generate BBS+ selective disclosure from one BBS+ signature. 17 | func (vc *Credential) GenerateBBSSelectiveDisclosure(revealDoc map[string]interface{}, 18 | nonce []byte, bbsProofCreator *BBSProofCreator, opts ...CredentialOpt) (*Credential, error) { 19 | if len(vc.ldProofs) == 0 { 20 | return nil, errors.New("expected at least one proof present") 21 | } 22 | 23 | vcOpts := getCredentialOpts(opts) 24 | jsonldProcessorOpts := mapJSONLDProcessorOpts(&vcOpts.jsonldCredentialOpts) 25 | 26 | if bbsProofCreator == nil { 27 | return nil, errors.New("bbs proof creator not defined") 28 | } 29 | 30 | vcDoc, err := jsonutil.ToMap(vc) 31 | if err != nil { 32 | return nil, err 33 | } 34 | 35 | vcWithSelectiveDisclosureDoc, err := BBSSelectiveDisclosure(vcDoc, revealDoc, nonce, 36 | bbsProofCreator, jsonldProcessorOpts...) 37 | if err != nil { 38 | return nil, fmt.Errorf("create VC selective disclosure: %w", err) 39 | } 40 | 41 | vcWithSelectiveDisclosureBytes, err := json.Marshal(vcWithSelectiveDisclosureDoc) 42 | if err != nil { 43 | return nil, err 44 | } 45 | 46 | opts = append(opts, WithDisabledProofCheck()) 47 | 48 | return ParseCredential(vcWithSelectiveDisclosureBytes, opts...) 49 | } 50 | -------------------------------------------------------------------------------- /verifiable/did_data_integrity.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright SecureKey Technologies Inc. All Rights Reserved. 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package verifiable 7 | 8 | import ( 9 | "encoding/json" 10 | "fmt" 11 | 12 | "github.com/trustbloc/did-go/doc/did" 13 | 14 | "github.com/trustbloc/vc-go/dataintegrity" 15 | ) 16 | 17 | // AddDIDDataIntegrityProof adds a Data Integrity Proof to the did.Doc. 18 | func AddDIDDataIntegrityProof( 19 | didDoc *did.Doc, 20 | context *DataIntegrityProofContext, 21 | diSigner *dataintegrity.Signer, 22 | ) (*did.Doc, error) { 23 | didBytes, err := didDoc.JSONBytes() 24 | if err != nil { 25 | return nil, fmt.Errorf("decode did doc: %w", err) 26 | } 27 | 28 | var jsonLdObject map[string]interface{} 29 | 30 | err = json.Unmarshal(didBytes, &jsonLdObject) 31 | if err != nil { 32 | return nil, fmt.Errorf("unmarshal did doc: %w", err) 33 | } 34 | 35 | // TODO: rewrite to use json object instead bytes presentation 36 | diProof, err := addDataIntegrityProof(context, didBytes, diSigner) 37 | if err != nil { 38 | return nil, fmt.Errorf("create data integrity proof: %w", err) 39 | } 40 | 41 | jsonLdObject[jsonFldLDProof] = proofsToRaw(diProof) 42 | 43 | signedDoc, err := json.Marshal(jsonLdObject) 44 | if err != nil { 45 | return nil, fmt.Errorf("encode signed did doc: %w", err) 46 | } 47 | 48 | didDocSignedLDP, err := did.ParseDocument(signedDoc) 49 | if err != nil { 50 | return nil, fmt.Errorf("parse signed did doc: %w", err) 51 | } 52 | 53 | return didDocSignedLDP, nil 54 | } 55 | -------------------------------------------------------------------------------- /verifiable/cose.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright SecureKey Technologies Inc. All Rights Reserved. 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package verifiable 7 | 8 | import ( 9 | "github.com/fxamacker/cbor/v2" 10 | "github.com/veraison/go-cose" 11 | 12 | "github.com/trustbloc/vc-go/cwt" 13 | cwt2 "github.com/trustbloc/vc-go/verifiable/cwt" 14 | ) 15 | 16 | const ( 17 | HeaderLabelTyp = 16 18 | ) 19 | 20 | // MarshalJWS serializes JWT presentation claims into signed form (JWS). 21 | func marshalCOSE( 22 | claims interface{}, 23 | signatureAlg cose.Algorithm, 24 | signer cwt.ProofCreator, 25 | keyID string, 26 | ) ([]byte, *cose.Sign1Message, error) { 27 | payload, err := cbor.Marshal(claims) 28 | if err != nil { 29 | return nil, nil, err 30 | } 31 | 32 | msg := &cose.Sign1Message{ 33 | Headers: cose.Headers{ 34 | Protected: cose.ProtectedHeader{ 35 | cose.HeaderLabelAlgorithm: signatureAlg, 36 | cose.HeaderLabelKeyID: []byte(keyID), 37 | }, 38 | Unprotected: cose.UnprotectedHeader{ 39 | HeaderLabelTyp: "application/vc+ld+json+cose", 40 | }, 41 | }, 42 | Payload: payload, 43 | } 44 | 45 | signData, err := cwt2.GetProofValue(msg) 46 | if err != nil { 47 | return nil, nil, err 48 | } 49 | 50 | signed, err := signer.SignCWT(cwt.SignParameters{ 51 | KeyID: keyID, 52 | CWTAlg: signatureAlg, 53 | }, signData) 54 | if err != nil { 55 | return nil, nil, err 56 | } 57 | 58 | msg.Signature = signed 59 | 60 | final, err := cbor.Marshal(msg) 61 | if err != nil { 62 | return nil, nil, err 63 | } 64 | 65 | return final, msg, nil 66 | } 67 | -------------------------------------------------------------------------------- /verifiable/testdata/example_presentation_4.json: -------------------------------------------------------------------------------- 1 | { 2 | "@context": [ 3 | "https://www.w3.org/ns/credentials/v2" 4 | ], 5 | "type": [ 6 | "VerifiablePresentation" 7 | ], 8 | "verifiableCredential": [ 9 | { 10 | "@context": [ 11 | "https://www.w3.org/ns/credentials/v2" 12 | ], 13 | "type": [ 14 | "VerifiableCredential" 15 | ], 16 | "issuer": "did:key:z6MkpJySvETLnxhQG9DzEdmKJtysBDjuuTeDfUj1uNNCUqcj", 17 | "credentialSubject": { 18 | "id": "did:example:subject" 19 | }, 20 | "proof": { 21 | "type": "DataIntegrityProof", 22 | "created": "2024-11-25T13:41:35Z", 23 | "verificationMethod": "did:key:z6MkpJySvETLnxhQG9DzEdmKJtysBDjuuTeDfUj1uNNCUqcj#z6MkpJySvETLnxhQG9DzEdmKJtysBDjuuTeDfUj1uNNCUqcj", 24 | "cryptosuite": "eddsa-rdfc-2022", 25 | "proofPurpose": "assertionMethod", 26 | "proofValue": "z46ES7jhCbsh9gYDXDpnPtacr43eTxLLvW3xVQGD8Jx45Ep1quheAfR9A9nhc5H1nMfk3VtsBC25CxYkhUs6sg4dT" 27 | } 28 | } 29 | ], 30 | "proof": { 31 | "type": "DataIntegrityProof", 32 | "created": "2024-11-25T13:41:35Z", 33 | "verificationMethod": "did:key:z6MkpJySvETLnxhQG9DzEdmKJtysBDjuuTeDfUj1uNNCUqcj#z6MkpJySvETLnxhQG9DzEdmKJtysBDjuuTeDfUj1uNNCUqcj", 34 | "cryptosuite": "eddsa-rdfc-2022", 35 | "proofPurpose": "authentication", 36 | "challenge": "ugBYp7yLdKSpAW1yakgot3g", 37 | "domain": "github.com/w3c/vc-data-model-2.0-test-suite", 38 | "proofValue": "zcZYLNg1DRJFSQWc993f12JnLNvw4MMCxNcJyymRkJoqKp66JvsyiyS3p7oiHHfLoAiS7xxwyS8Rd7UDfVmLBn7Q" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /presexch/testdata/sample_1.json: -------------------------------------------------------------------------------- 1 | { 2 | "id":"32f54163-7166-48f1-93d8-ff217bdb0653", 3 | "input_descriptors":[ 4 | { 5 | "id":"banking_input", 6 | "name":"Bank Account Information", 7 | "purpose":"We need your bank and account information.", 8 | "schema":[ 9 | { 10 | "uri":"https://bank-standards.com/customer.json" 11 | } 12 | ], 13 | "constraints":{ 14 | "limit_disclosure":"required", 15 | "fields":[ 16 | { 17 | "path":[ 18 | "$.issuer", 19 | "$.vc.issuer", 20 | "$.iss" 21 | ], 22 | "purpose":"The claim must be from one of the specified issuers", 23 | "filter":{ 24 | "type":"string", 25 | "pattern":"did:example:123|did:example:456" 26 | } 27 | } 28 | ] 29 | } 30 | }, 31 | { 32 | "id":"citizenship_input", 33 | "name":"US Passport", 34 | "schema":[ 35 | { 36 | "uri":"hub://did:foo:123/Collections/schema.us.gov/passport.json" 37 | } 38 | ], 39 | "constraints":{ 40 | "fields":[ 41 | { 42 | "path":[ 43 | "$.credentialSubject.birth_date", 44 | "$.vc.credentialSubject.birth_date", 45 | "$.birth_date" 46 | ], 47 | "filter":{ 48 | "type":"string", 49 | "format":"date", 50 | "minimum":"1999-5-16" 51 | } 52 | } 53 | ] 54 | } 55 | } 56 | ] 57 | } -------------------------------------------------------------------------------- /verifiable/testdata/example_presentation_1_ed25519.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": [ 3 | "https://www.w3.org/ns/credentials/v2" 4 | ], 5 | "type": [ 6 | "VerifiablePresentation" 7 | ], 8 | "verifiableCredential": [ 9 | { 10 | "@context": [ 11 | "https://www.w3.org/ns/credentials/v2" 12 | ], 13 | "type": [ 14 | "VerifiableCredential" 15 | ], 16 | "issuer": "did:key:z6MkpJySvETLnxhQG9DzEdmKJtysBDjuuTeDfUj1uNNCUqcj", 17 | "credentialSubject": { 18 | "id": "did:example:subject" 19 | }, 20 | "proof": { 21 | "type": "DataIntegrityProof", 22 | "created": "2024-10-21T19:27:16Z", 23 | "verificationMethod": "did:key:z6MkpJySvETLnxhQG9DzEdmKJtysBDjuuTeDfUj1uNNCUqcj#z6MkpJySvETLnxhQG9DzEdmKJtysBDjuuTeDfUj1uNNCUqcj", 24 | "cryptosuite": "eddsa-rdfc-2022", 25 | "proofPurpose": "assertionMethod", 26 | "proofValue": "z2kb5qAGHsTYw9zeGqEScYFFHmWj8qoYrN5JeK6rp3PJ88Ma5sPAgvoZpvPbbfRFwAZZhmDG1w3L5A2jdzSKX4TPJ" 27 | } 28 | } 29 | ], 30 | "proof": { 31 | "type": "DataIntegrityProof", 32 | "created": "2024-10-21T19:27:40Z", 33 | "verificationMethod": "did:key:z6MkpJySvETLnxhQG9DzEdmKJtysBDjuuTeDfUj1uNNCUqcj#z6MkpJySvETLnxhQG9DzEdmKJtysBDjuuTeDfUj1uNNCUqcj", 34 | "cryptosuite": "eddsa-rdfc-2022", 35 | "proofPurpose": "authentication", 36 | "challenge": "ubXbWYV5hUDu1VCy2b75qKg", 37 | "domain": "github.com/w3c/vc-data-model-2.0-test-suite", 38 | "proofValue": "z2dmNLqEfBenLXXJnd79ucZr1UJR2qkNdWkNuohmTfXfKMwc7U2MKuMVSje1mJKmSWpNtfVmZRDKeSeLsNrWPRk9f" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /verifiable/testdata/example_presentation_5.json: -------------------------------------------------------------------------------- 1 | { 2 | "@context": [ 3 | "https://www.w3.org/ns/credentials/v2" 4 | ], 5 | "type": [ 6 | "VerifiablePresentation" 7 | ], 8 | "holder" : "someone", 9 | "verifiableCredential": [ 10 | { 11 | "@context": [ 12 | "https://www.w3.org/ns/credentials/v2" 13 | ], 14 | "type": [ 15 | "VerifiableCredential" 16 | ], 17 | "issuer": "did:key:z6MkpJySvETLnxhQG9DzEdmKJtysBDjuuTeDfUj1uNNCUqcj", 18 | "credentialSubject": { 19 | "id": "did:example:subject" 20 | }, 21 | "proof": { 22 | "type": "DataIntegrityProof", 23 | "created": "2024-11-25T13:41:35Z", 24 | "verificationMethod": "did:key:z6MkpJySvETLnxhQG9DzEdmKJtysBDjuuTeDfUj1uNNCUqcj#z6MkpJySvETLnxhQG9DzEdmKJtysBDjuuTeDfUj1uNNCUqcj", 25 | "cryptosuite": "eddsa-rdfc-2022", 26 | "proofPurpose": "assertionMethod", 27 | "proofValue": "z46ES7jhCbsh9gYDXDpnPtacr43eTxLLvW3xVQGD8Jx45Ep1quheAfR9A9nhc5H1nMfk3VtsBC25CxYkhUs6sg4dT" 28 | } 29 | } 30 | ], 31 | "proof": { 32 | "type": "DataIntegrityProof", 33 | "created": "2024-11-25T13:41:35Z", 34 | "verificationMethod": "did:key:z6MkpJySvETLnxhQG9DzEdmKJtysBDjuuTeDfUj1uNNCUqcj#z6MkpJySvETLnxhQG9DzEdmKJtysBDjuuTeDfUj1uNNCUqcj", 35 | "cryptosuite": "eddsa-rdfc-2022", 36 | "proofPurpose": "authentication", 37 | "challenge": "ugBYp7yLdKSpAW1yakgot3g", 38 | "domain": "github.com/w3c/vc-data-model-2.0-test-suite", 39 | "proofValue": "zcZYLNg1DRJFSQWc993f12JnLNvw4MMCxNcJyymRkJoqKp66JvsyiyS3p7oiHHfLoAiS7xxwyS8Rd7UDfVmLBn7Q" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright Gen Digital Inc. All Rights Reserved. 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | 7 | name: "vc-go ci" 8 | 9 | on: 10 | push: 11 | branches: [ main ] 12 | pull_request: 13 | branches: [ main ] 14 | 15 | jobs: 16 | SemanticPullRequest: 17 | name: Semantic Pull Request Check 18 | if: github.event_name == 'pull_request' 19 | runs-on: ubuntu-latest 20 | steps: 21 | - uses: amannn/action-semantic-pull-request@v4 22 | env: 23 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 24 | 25 | Checks: 26 | runs-on: ubuntu-22.04 27 | timeout-minutes: 10 28 | steps: 29 | - uses: actions/checkout@v3 30 | - name: Setup Go 1.23 31 | uses: actions/setup-go@v4 32 | with: 33 | go-version: '1.23' 34 | - name: Run checks 35 | run: | 36 | echo $PATH 37 | go env 38 | echo ${{ github.workspace }} 39 | make checks 40 | env: 41 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 42 | 43 | UnitTest: 44 | runs-on: ubuntu-22.04 45 | timeout-minutes: 10 46 | steps: 47 | 48 | - name: Setup Go 1.23 49 | uses: actions/setup-go@v4 50 | with: 51 | go-version: '1.23' 52 | id: go 53 | 54 | - uses: actions/checkout@v3 55 | 56 | - name: Run unit test 57 | timeout-minutes: 15 58 | run: make unit-test 59 | 60 | - name: Upload coverage to Codecov 61 | timeout-minutes: 10 62 | uses: codecov/codecov-action@v3.1.6 63 | with: 64 | file: ./coverage.out 65 | -------------------------------------------------------------------------------- /crypto-ext/verifiers/ed25519/verifier.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Gen Digital Inc. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package ed25519 8 | 9 | import ( 10 | "crypto/ed25519" 11 | "errors" 12 | "fmt" 13 | 14 | "github.com/trustbloc/kms-go/spi/kms" 15 | 16 | "github.com/trustbloc/vc-go/crypto-ext/pubkey" 17 | ) 18 | 19 | // Verifier verifies a Ed25519 signature taking Ed25519 public key bytes as input. 20 | type Verifier struct { 21 | } 22 | 23 | // New creates a new ed25519 Verifier. 24 | func New() *Verifier { 25 | return &Verifier{} 26 | } 27 | 28 | // SupportedKeyType checks if verifier supports given key. 29 | func (sv *Verifier) SupportedKeyType(keyType kms.KeyType) bool { 30 | return keyType == kms.ED25519Type 31 | } 32 | 33 | // Verify verifies the signature. 34 | func (sv *Verifier) Verify(signature, msg []byte, pubKey *pubkey.PublicKey) error { 35 | if !sv.SupportedKeyType(pubKey.Type) { 36 | return fmt.Errorf("unsupported key type %s", pubKey.Type) 37 | } 38 | 39 | var value []byte 40 | if pubKey.BytesKey != nil { 41 | value = pubKey.BytesKey.Bytes 42 | } 43 | 44 | if pubKey.JWK != nil { 45 | var ok bool 46 | value, ok = pubKey.JWK.Public().Key.(ed25519.PublicKey) 47 | 48 | if !ok { 49 | return errors.New("public key not ed25519.VerificationMethod") 50 | } 51 | } 52 | // ed25519 panics if key size is wrong 53 | if len(value) != ed25519.PublicKeySize { 54 | return errors.New("ed25519: invalid key") 55 | } 56 | 57 | verified := ed25519.Verify(value, msg, signature) 58 | if !verified { 59 | return errors.New("ed25519: invalid signature") 60 | } 61 | 62 | return nil 63 | } 64 | -------------------------------------------------------------------------------- /proof/descriptor.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Gen Digital Inc. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package proof 8 | 9 | import ( 10 | "github.com/trustbloc/did-go/doc/ld/processor" 11 | "github.com/trustbloc/kms-go/spi/kms" 12 | "github.com/veraison/go-cose" 13 | ) 14 | 15 | const ( 16 | // CWTProofType is the proof type for CWT. 17 | CWTProofType = "application/openid4vci-proof+cwt" 18 | 19 | // COSEKeyHeader is the header for COSE key. 20 | COSEKeyHeader = "COSE_Key" 21 | ) 22 | 23 | // SupportedVerificationMethod describes verification methods that supported by proof checker. 24 | type SupportedVerificationMethod struct { 25 | VerificationMethodType string // verification method type from did. E.g. Ed25519VerificationKey2020, JsonWebKey2020. 26 | KMSKeyType kms.KeyType 27 | JWKKeyType string 28 | JWKCurve string 29 | RequireJWK bool 30 | } 31 | 32 | // LDProofDescriptor describes ld proof. 33 | type LDProofDescriptor interface { 34 | // GetCanonicalDocument will return normalized/canonical version of the document 35 | GetCanonicalDocument(doc map[string]interface{}, opts ...processor.Opts) ([]byte, error) 36 | 37 | // GetDigest returns document digest. 38 | GetDigest(doc []byte) []byte 39 | 40 | // ProofType return proof type. 41 | ProofType() string 42 | 43 | SupportedVerificationMethods() []SupportedVerificationMethod 44 | } 45 | 46 | // JWTProofDescriptor describes jwt proof. 47 | type JWTProofDescriptor interface { 48 | JWTAlgorithm() string 49 | CWTAlgorithm() cose.Algorithm 50 | 51 | SupportedVerificationMethods() []SupportedVerificationMethod 52 | } 53 | -------------------------------------------------------------------------------- /presexch/internal/requirementlogic/stringset.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Gen Digital Inc. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package requirementlogic 8 | 9 | import ( 10 | "sort" 11 | "strings" 12 | ) 13 | 14 | // StringSet implements a set of strings. 15 | type StringSet map[string]struct{} 16 | 17 | // InitFromSlice creates a new StringSet containing all the given values. 18 | func InitFromSlice(values []string) StringSet { 19 | s := make(StringSet, len(values)) 20 | 21 | for _, value := range values { 22 | s.Add(value) 23 | } 24 | 25 | return s 26 | } 27 | 28 | // MergeAll returns the union of the given StringSet parameters as a new StringSet. 29 | func MergeAll(sets ...StringSet) StringSet { 30 | out := StringSet{} 31 | 32 | for _, set := range sets { 33 | for k := range set { 34 | out.Add(k) 35 | } 36 | } 37 | 38 | return out 39 | } 40 | 41 | // Has returns whether s contains value. 42 | func (s StringSet) Has(value string) bool { 43 | _, ok := s[value] 44 | return ok 45 | } 46 | 47 | // Add adds value to s. 48 | func (s StringSet) Add(value string) { 49 | s[value] = struct{}{} 50 | } 51 | 52 | // Len returns the size of s. 53 | func (s StringSet) Len() int { 54 | return len(s) 55 | } 56 | 57 | // ToString creates a string representation of s, guaranteed to be identical for 58 | // two StringSets containing the same strings. 59 | func (s StringSet) ToString() string { 60 | escaped := make([]string, s.Len()) 61 | 62 | for d := range s { 63 | escaped = append(escaped, strings.ReplaceAll(d, "\x00", "\\x00")) 64 | } 65 | 66 | sort.Strings(escaped) 67 | 68 | return strings.Join(escaped, "\x00") 69 | } 70 | -------------------------------------------------------------------------------- /proof/jwtproofs/ps256/proof.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Gen Digital Inc. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package ps256 8 | 9 | import ( 10 | "github.com/trustbloc/kms-go/spi/kms" 11 | "github.com/veraison/go-cose" 12 | 13 | "github.com/trustbloc/vc-go/proof" 14 | ) 15 | 16 | // Proof describes ed25519 proof type. 17 | type Proof struct { 18 | supportedVMs []proof.SupportedVerificationMethod 19 | } 20 | 21 | const ( 22 | //JWKKeyType for ps256. 23 | JWKKeyType = "RSA" 24 | //JWTAlg for ps256. 25 | JWTAlg = "PS256" 26 | ) 27 | 28 | // New an instance of ed25519 proof type descriptor. 29 | func New() *Proof { 30 | p := &Proof{} 31 | p.supportedVMs = []proof.SupportedVerificationMethod{ 32 | { 33 | VerificationMethodType: "RsaVerificationKey2018", 34 | KMSKeyType: kms.RSAPS256Type, 35 | JWKKeyType: JWKKeyType, 36 | }, 37 | { 38 | VerificationMethodType: "JsonWebKey2020", 39 | KMSKeyType: kms.RSAPS256Type, 40 | JWKKeyType: JWKKeyType, 41 | RequireJWK: true, 42 | }, 43 | } 44 | 45 | return p 46 | } 47 | 48 | // SupportedVerificationMethods returns list of verification methods supported by this proof type. 49 | func (s *Proof) SupportedVerificationMethods() []proof.SupportedVerificationMethod { 50 | return s.supportedVMs 51 | } 52 | 53 | // JWTAlgorithm return jwt alg that corresponds to VerificationMethod. 54 | func (s *Proof) JWTAlgorithm() string { 55 | return JWTAlg 56 | } 57 | 58 | // CWTAlgorithm return cwt algorithm that corresponds to VerificationMethod. 59 | func (s *Proof) CWTAlgorithm() cose.Algorithm { 60 | return cose.AlgorithmPS256 61 | } 62 | -------------------------------------------------------------------------------- /proof/jwtproofs/rs256/proof.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Gen Digital Inc. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package rs256 8 | 9 | import ( 10 | "github.com/trustbloc/kms-go/spi/kms" 11 | "github.com/veraison/go-cose" 12 | 13 | "github.com/trustbloc/vc-go/proof" 14 | ) 15 | 16 | // Proof describes ed25519 proof type. 17 | type Proof struct { 18 | supportedVMs []proof.SupportedVerificationMethod 19 | } 20 | 21 | const ( 22 | //JWKKeyType for rsa proof. 23 | JWKKeyType = "RSA" 24 | //JWTAlg for rsa proof. 25 | JWTAlg = "RS256" 26 | ) 27 | 28 | // New an instance of ed25519 proof type descriptor. 29 | func New() *Proof { 30 | p := &Proof{} 31 | p.supportedVMs = []proof.SupportedVerificationMethod{ 32 | { 33 | VerificationMethodType: "RsaVerificationKey2018", 34 | KMSKeyType: kms.RSARS256Type, 35 | JWKKeyType: JWKKeyType, 36 | }, 37 | { 38 | VerificationMethodType: "JsonWebKey2020", 39 | KMSKeyType: kms.RSARS256Type, 40 | JWKKeyType: JWKKeyType, 41 | RequireJWK: true, 42 | }, 43 | } 44 | 45 | return p 46 | } 47 | 48 | // SupportedVerificationMethods returns list of verification methods supported by this proof type. 49 | func (s *Proof) SupportedVerificationMethods() []proof.SupportedVerificationMethod { 50 | return s.supportedVMs 51 | } 52 | 53 | // JWTAlgorithm return jwt alg that corresponds to VerificationMethod. 54 | func (s *Proof) JWTAlgorithm() string { 55 | return JWTAlg 56 | } 57 | 58 | // CWTAlgorithm return cwt algorithm that corresponds to VerificationMethod. 59 | func (s *Proof) CWTAlgorithm() cose.Algorithm { 60 | return cose.AlgorithmRS256 61 | } 62 | -------------------------------------------------------------------------------- /jwt/jose_wrappers_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Gen Digital Inc. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package jwt 8 | 9 | import ( 10 | "testing" 11 | 12 | "github.com/stretchr/testify/require" 13 | "github.com/trustbloc/kms-go/doc/jose" 14 | ) 15 | 16 | func TestJoseVerifier(t *testing.T) { 17 | testIssuer := "did:test:testIssuer" 18 | 19 | t.Run("expectedProofIssuer is defined", func(t *testing.T) { 20 | mock := &mockProofChecker{} 21 | verifier := &joseVerifier{proofChecker: mock, expectedProofIssuer: &testIssuer} 22 | 23 | err := verifier.Verify(jose.Headers{"kid": "did:test:keyIssuer#key-1"}, nil, nil, nil) 24 | require.NoError(t, err) 25 | require.Equal(t, testIssuer, mock.resultedExpectedProofIssuer) 26 | }) 27 | 28 | t.Run("expectedProofIssuer should be derived from key id", func(t *testing.T) { 29 | mock := &mockProofChecker{} 30 | verifier := &joseVerifier{proofChecker: mock} 31 | 32 | err := verifier.Verify(jose.Headers{"kid": "did:test:keyIssuer#key-1"}, nil, nil, nil) 33 | require.NoError(t, err) 34 | require.Equal(t, "did:test:keyIssuer", mock.resultedExpectedProofIssuer) 35 | }) 36 | 37 | t.Run("test missed key id", func(t *testing.T) { 38 | mock := &mockProofChecker{} 39 | verifier := &joseVerifier{proofChecker: mock} 40 | 41 | err := verifier.Verify(jose.Headers{}, nil, nil, nil) 42 | require.ErrorContains(t, err, "missed kid in jwt header") 43 | }) 44 | } 45 | 46 | type mockProofChecker struct { 47 | resultedExpectedProofIssuer string 48 | } 49 | 50 | func (m *mockProofChecker) CheckJWTProof(headers jose.Headers, 51 | expectedProofIssuer string, msg, signature []byte) error { 52 | m.resultedExpectedProofIssuer = expectedProofIssuer 53 | return nil 54 | } 55 | -------------------------------------------------------------------------------- /presexch/testdata/university_degree.jwt: -------------------------------------------------------------------------------- 1 | eyJhbGciOiJFUzI1NiIsImtpZCI6ImRpZDpvcmI6dUFBQTpFaURwenMwaHkwcTBJZjRaZkpBMWt4QlFkOWVkNkZvQkZoaHFEV1NpQmVLYUlnI2QzY2ZkMzZiLTRmNzUtNDA0MS1iNDE2LWYwYTdhM2M2YjlmNiJ9.eyJpYXQiOjE1ODQzOTgyNDYsImlzcyI6ImRpZDpvcmI6dUFBQTpFaURwenMwaHkwcTBJZjRaZkpBMWt4QlFkOWVkNkZvQkZoaHFEV1NpQmVLYUlnIiwianRpIjoidXJuOnV1aWQ6YmRhODYyMzYtZDk5ZS00NTMwLWI2YmYtZTc3OWQ1YWVmNTRjIiwibmJmIjoxNTg0Mzk4MjQ2LCJzdWIiOiJkaWQ6b3JiOnVBQUE6RWlBUlR2dkNzV0ZUU0NjMzU0NDdZcEkyTUpwRkFhSlp0RmxjZVZ6OWxjTVlWdyIsInZjIjp7IkBjb250ZXh0IjpbImh0dHBzOi8vd3d3LnczLm9yZy8yMDE4L2NyZWRlbnRpYWxzL3YxIiwiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvZXhhbXBsZXMvdjEiLCJodHRwczovL3czaWQub3JnL3ZjL3N0YXR1cy1saXN0LzIwMjEvdjEiXSwiY3JlZGVudGlhbFN0YXR1cyI6eyJpZCI6InVybjp1dWlkOjhhMzgxYzkwLTQzOTItNDc4Yi1iMGQ4LTAxNWExMTllODc3ZCIsInN0YXR1c0xpc3RDcmVkZW50aWFsIjoiaHR0cDovL3ZjLXJlc3QtZWNoby50cnVzdGJsb2MubG9jYWw6ODA3NS9pc3N1ZXIvcHJvZmlsZXMvaV9teXByb2ZpbGVfdWRfZXMyNTZfand0L2NyZWRlbnRpYWxzL3N0YXR1cy8xIiwic3RhdHVzTGlzdEluZGV4IjoiMyIsInN0YXR1c1B1cnBvc2UiOiJyZXZvY2F0aW9uIiwidHlwZSI6IlN0YXR1c0xpc3QyMDIxRW50cnkifSwiY3JlZGVudGlhbFN1YmplY3QiOnsiZGVncmVlIjp7ImRlZ3JlZSI6Ik1JVCIsInR5cGUiOiJCYWNoZWxvckRlZ3JlZSJ9LCJpZCI6ImRpZDpvcmI6dUFBQTpFaUFSVHZ2Q3NXRlRTQ2MzNTQ0N1lwSTJNSnBGQWFKWnRGbGNlVno5bGNNWVZ3IiwibmFtZSI6IkpheWRlbiBEb2UiLCJzcG91c2UiOiJkaWQ6ZXhhbXBsZTpjMjc2ZTEyZWMyMWViZmViMWY3MTJlYmM2ZjEifSwiaWQiOiJ1cm46dXVpZDpiZGE4NjIzNi1kOTllLTQ1MzAtYjZiZi1lNzc5ZDVhZWY1NGMiLCJpc3N1YW5jZURhdGUiOiIyMDIwLTAzLTE2VDIyOjM3OjI2WiIsImlzc3VlciI6eyJpZCI6ImRpZDpvcmI6dUFBQTpFaURwenMwaHkwcTBJZjRaZkpBMWt4QlFkOWVkNkZvQkZoaHFEV1NpQmVLYUlnIiwibmFtZSI6ImlfbXlwcm9maWxlX3VkX2VzMjU2X2p3dCJ9LCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIiwiVW5pdmVyc2l0eURlZ3JlZUNyZWRlbnRpYWwiXX19.MEUCIFTezGNnefbdoMvUagSjTOQpngkeivbfge_z52jdlsdcAiEA6MTRsH_z-1V3h2q9Oc5A4Bu2GLPjIt2h_9Cw__ZSMSs -------------------------------------------------------------------------------- /presexch/testdata/presentation_definition.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "32f54163-7166-48f1-93d8-ff217bdb0653", 3 | "input_descriptors": [ 4 | { 5 | "id": "VerifiedEmployee", 6 | "name": "Verified Employee", 7 | "constraints": { 8 | "fields": [ 9 | { 10 | "path": [ 11 | "$.type", 12 | "$.vc.type" 13 | ], 14 | "filter": { 15 | "type": "array", 16 | "contains": { 17 | "type": "string", 18 | "const": "VerifiedEmployee" 19 | } 20 | } 21 | } 22 | ] 23 | } 24 | }, 25 | { 26 | "id": "DriversLicense", 27 | "name": "Driver's License", 28 | "constraints": { 29 | "fields": [ 30 | { 31 | "path": [ 32 | "$.type", 33 | "$.vc.type" 34 | ], 35 | "filter": { 36 | "type": "array", 37 | "contains": { 38 | "type": "string", 39 | "const": "DriversLicense" 40 | } 41 | } 42 | } 43 | ] 44 | } 45 | }, 46 | { 47 | "id": "degree", 48 | "name": "degree", 49 | "purpose": "We can only hire with bachelor's degree.", 50 | "constraints": { 51 | "fields": [ 52 | { 53 | "path": [ 54 | "$.credentialSubject.degree.type", 55 | "$.vc.credentialSubject.degree.type" 56 | ], 57 | "purpose": "We can only hire with bachelor's degree.", 58 | "filter": { 59 | "type": "string", 60 | "const": "BachelorDegree" 61 | } 62 | } 63 | ] 64 | } 65 | } 66 | ] 67 | } -------------------------------------------------------------------------------- /sdjwt/sdjwt-doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright SecureKey Technologies Inc. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | // Package sdjwt implements creating JSON Web Token (JWT) documents that support selective disclosure of JWT claims. 8 | // 9 | // In an SD-JWT, claims can be hidden, but cryptographically protected against undetected modification. 10 | // 11 | // When issuing the SD-JWT to the Holder, the Issuer also sends the cleartext counterparts of all hidden claims, 12 | // the so-called Disclosures, separate from the SD-JWT itself. 13 | // 14 | // The Holder decides which claims to disclose to a Verifier and forwards the respective Disclosures 15 | // together with the SD-JWT to the Verifier. 16 | // 17 | // The Verifier has to verify that all disclosed claim values were part of the original, Issuer-signed SD-JWT. 18 | // The Verifier will not, however, learn any claim values not disclosed in the Disclosures. 19 | // 20 | // This implementation supports: 21 | // 22 | // - selectively disclosable claims in flat data structures as well as more complex, nested data structures 23 | // 24 | // - combining selectively disclosable claims with clear-text claims that are always disclosed 25 | // 26 | // - options for specifying registered claim names that will be included in plaintext (e.g. iss, exp, or nbf) 27 | // 28 | // - option for configuring clear-text claims 29 | // 30 | // For selectively disclosable claims, claim names are always blinded. 31 | // 32 | // This implementation also supports an optional mechanism for Holder Binding, 33 | // the concept of binding an SD-JWT to key material controlled by the Holder. 34 | // The strength of the Holder Binding is conditional upon the trust in the protection 35 | // of the private key of the key pair an SD-JWT is bound to. 36 | package sdjwt 37 | -------------------------------------------------------------------------------- /proof/jwtproofs/es521/proof.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Gen Digital Inc. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package es521 8 | 9 | import ( 10 | "github.com/trustbloc/kms-go/spi/kms" 11 | "github.com/veraison/go-cose" 12 | 13 | "github.com/trustbloc/vc-go/proof" 14 | ) 15 | 16 | // Proof describes es521 proof type. 17 | type Proof struct { 18 | supportedVMs []proof.SupportedVerificationMethod 19 | } 20 | 21 | const ( 22 | // JWKKeyType for es521. 23 | JWKKeyType = "EC" 24 | // JWKCurve for es521. 25 | JWKCurve = "P-521" 26 | // JWTAlg for es521. 27 | JWTAlg = "ES521" 28 | ) 29 | 30 | // New an instance of ed25519 proof type descriptor. 31 | func New() *Proof { 32 | p := &Proof{} 33 | p.supportedVMs = []proof.SupportedVerificationMethod{ 34 | { 35 | VerificationMethodType: "JsonWebKey2020", 36 | KMSKeyType: kms.ECDSAP521TypeIEEEP1363, 37 | JWKKeyType: JWKKeyType, 38 | JWKCurve: JWKCurve, 39 | RequireJWK: true, 40 | }, 41 | { 42 | VerificationMethodType: "JsonWebKey2020", 43 | KMSKeyType: kms.ECDSAP521TypeDER, 44 | JWKKeyType: JWKKeyType, 45 | JWKCurve: JWKCurve, 46 | RequireJWK: true, 47 | }, 48 | } 49 | 50 | return p 51 | } 52 | 53 | // SupportedVerificationMethods returns list of verification methods supported by this proof type. 54 | func (s *Proof) SupportedVerificationMethods() []proof.SupportedVerificationMethod { 55 | return s.supportedVMs 56 | } 57 | 58 | // JWTAlgorithm return jwt alg that corresponds to VerificationMethod. 59 | func (s *Proof) JWTAlgorithm() string { 60 | return JWTAlg 61 | } 62 | 63 | // CWTAlgorithm return cwt algorithm that corresponds to VerificationMethod. 64 | func (s *Proof) CWTAlgorithm() cose.Algorithm { 65 | return 0 66 | } 67 | -------------------------------------------------------------------------------- /verifiable/presentation_cwt_proof_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright SecureKey Technologies Inc. All Rights Reserved. 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package verifiable 7 | 8 | import ( 9 | "encoding/hex" 10 | "fmt" 11 | "testing" 12 | 13 | "github.com/stretchr/testify/assert" 14 | "github.com/stretchr/testify/require" 15 | "github.com/trustbloc/kms-go/spi/kms" 16 | "github.com/veraison/go-cose" 17 | 18 | "github.com/trustbloc/vc-go/proof/testsupport" 19 | ) 20 | 21 | func TestParsePresentationFromCWS_EdDSA(t *testing.T) { 22 | vpBytes := []byte(validPresentation) 23 | 24 | vp, err := newTestPresentation(t, vpBytes, WithPresDisabledProofCheck()) 25 | require.NoError(t, err) 26 | 27 | holderKeyID := vp.Holder + "#keys-" + keyID 28 | 29 | proofCreator, proofChecher := testsupport.NewKMSSigVerPair(t, kms.ED25519Type, 30 | holderKeyID) 31 | 32 | // marshal presentation into JWS using EdDSA (Ed25519 signature algorithm). 33 | cwtClaims, err := vp.CWTClaims([]string{}, false) 34 | require.NoError(t, err) 35 | 36 | cwtBytes, msg, err := cwtClaims.MarshalCWT(cose.AlgorithmEdDSA, proofCreator, holderKeyID) 37 | require.NoError(t, err) 38 | assert.NotNil(t, msg) 39 | 40 | hexStr := hex.EncodeToString(cwtBytes) 41 | fmt.Println(hexStr) 42 | // unmarshal presentation from JWS 43 | vpFromJWS, err := newTestPresentation(t, 44 | cwtBytes, 45 | WithPresProofChecker(proofChecher)) 46 | require.NoError(t, err) 47 | 48 | require.Equal(t, cwtBytes, vpFromJWS.CWT.Raw) 49 | 50 | marshaled, err := vpFromJWS.MarshalCBOR() 51 | require.NoError(t, err) 52 | 53 | vpFromJWS2, err2 := newTestPresentation(t, 54 | marshaled, 55 | WithPresProofChecker(proofChecher)) 56 | require.NoError(t, err2) 57 | 58 | // unmarshalled presentation must be the same as original one 59 | require.Equal(t, vpFromJWS, vpFromJWS2) 60 | } 61 | -------------------------------------------------------------------------------- /jwt/jose_wrappers.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Gen Digital Inc. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package jwt 8 | 9 | import ( 10 | "errors" 11 | "strings" 12 | 13 | "github.com/trustbloc/kms-go/doc/jose" 14 | ) 15 | 16 | // NewJOSESigner wraps ProofCreator into jose signer. 17 | func NewJOSESigner(params SignParameters, signer ProofCreator) (*JoseSigner, error) { 18 | headers, err := signer.CreateJWTHeaders(params) 19 | if err != nil { 20 | return nil, err 21 | } 22 | 23 | return &JoseSigner{ 24 | signer: signer, 25 | signParams: params, 26 | headers: headers, 27 | }, nil 28 | } 29 | 30 | // JoseSigner implement jose.proofCreator interface. 31 | type JoseSigner struct { 32 | signer ProofCreator 33 | signParams SignParameters 34 | headers jose.Headers 35 | } 36 | 37 | // Sign returns signature. 38 | func (s JoseSigner) Sign(data []byte) ([]byte, error) { 39 | return s.signer.SignJWT(s.signParams, data) 40 | } 41 | 42 | // Headers returns headers. 43 | func (s JoseSigner) Headers() jose.Headers { 44 | return s.headers 45 | } 46 | 47 | type joseVerifier struct { 48 | proofChecker ProofChecker 49 | expectedProofIssuer *string 50 | } 51 | 52 | func (v *joseVerifier) Verify(joseHeaders jose.Headers, _, signingInput, signature []byte) error { 53 | var expectedProofIssuer string 54 | 55 | if v.expectedProofIssuer != nil { 56 | expectedProofIssuer = *v.expectedProofIssuer 57 | } else { 58 | // if expectedProofIssuer not set, we get issuer DID from first part of key id. 59 | keyID, ok := joseHeaders.KeyID() 60 | if !ok { 61 | return errors.New("missed kid in jwt header") 62 | } 63 | 64 | expectedProofIssuer = strings.Split(keyID, "#")[0] 65 | } 66 | 67 | return v.proofChecker.CheckJWTProof(joseHeaders, expectedProofIssuer, signingInput, signature) 68 | } 69 | -------------------------------------------------------------------------------- /proof/jwtproofs/es256/proof.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Gen Digital Inc. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package es256 8 | 9 | import ( 10 | "github.com/trustbloc/kms-go/spi/kms" 11 | "github.com/veraison/go-cose" 12 | 13 | "github.com/trustbloc/vc-go/proof" 14 | ) 15 | 16 | // Proof describes ed25519 proof type. 17 | type Proof struct { 18 | supportedVMs []proof.SupportedVerificationMethod 19 | } 20 | 21 | const ( 22 | // JWKKeyType for es256. 23 | JWKKeyType = "EC" 24 | // JWKCurve for es256. 25 | JWKCurve = "P-256" 26 | // JWTAlg for es256. 27 | JWTAlg = "ES256" 28 | ) 29 | 30 | // New an instance of ed25519 proof type descriptor. 31 | func New() *Proof { 32 | p := &Proof{} 33 | p.supportedVMs = []proof.SupportedVerificationMethod{ 34 | { 35 | VerificationMethodType: "JsonWebKey2020", 36 | KMSKeyType: kms.ECDSAP256TypeIEEEP1363, 37 | JWKKeyType: JWKKeyType, 38 | JWKCurve: JWKCurve, 39 | RequireJWK: true, 40 | }, 41 | { 42 | VerificationMethodType: "JsonWebKey2020", 43 | KMSKeyType: kms.ECDSAP256TypeDER, 44 | JWKKeyType: JWKKeyType, 45 | JWKCurve: JWKCurve, 46 | RequireJWK: true, 47 | }, 48 | } 49 | 50 | return p 51 | } 52 | 53 | // SupportedVerificationMethods returns list of verification methods supported by this proof type. 54 | func (s *Proof) SupportedVerificationMethods() []proof.SupportedVerificationMethod { 55 | return s.supportedVMs 56 | } 57 | 58 | // JWTAlgorithm return jwt alg that corresponds to VerificationMethod. 59 | func (s *Proof) JWTAlgorithm() string { 60 | return JWTAlg 61 | } 62 | 63 | // CWTAlgorithm return cwt algorithm that corresponds to VerificationMethod. 64 | func (s *Proof) CWTAlgorithm() cose.Algorithm { 65 | return cose.AlgorithmES256 66 | } 67 | -------------------------------------------------------------------------------- /proof/jwtproofs/es384/proof.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Gen Digital Inc. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package es384 8 | 9 | import ( 10 | "github.com/trustbloc/kms-go/spi/kms" 11 | "github.com/veraison/go-cose" 12 | 13 | "github.com/trustbloc/vc-go/proof" 14 | ) 15 | 16 | // Proof describes es384 proof type. 17 | type Proof struct { 18 | supportedVMs []proof.SupportedVerificationMethod 19 | } 20 | 21 | const ( 22 | // JWKKeyType for es384. 23 | JWKKeyType = "EC" 24 | // JWKCurve for es384. 25 | JWKCurve = "P-384" 26 | // JWTAlg for es384. 27 | JWTAlg = "ES384" 28 | ) 29 | 30 | // New an instance of ed25519 proof type descriptor. 31 | func New() *Proof { 32 | p := &Proof{} 33 | p.supportedVMs = []proof.SupportedVerificationMethod{ 34 | { 35 | VerificationMethodType: "JsonWebKey2020", 36 | KMSKeyType: kms.ECDSAP384TypeIEEEP1363, 37 | JWKKeyType: JWKKeyType, 38 | JWKCurve: JWKCurve, 39 | RequireJWK: true, 40 | }, 41 | { 42 | VerificationMethodType: "JsonWebKey2020", 43 | KMSKeyType: kms.ECDSAP384TypeDER, 44 | JWKKeyType: JWKKeyType, 45 | JWKCurve: JWKCurve, 46 | RequireJWK: true, 47 | }, 48 | } 49 | 50 | return p 51 | } 52 | 53 | // SupportedVerificationMethods returns list of verification methods supported by this proof type. 54 | func (s *Proof) SupportedVerificationMethods() []proof.SupportedVerificationMethod { 55 | return s.supportedVMs 56 | } 57 | 58 | // JWTAlgorithm return jwt alg that corresponds to VerificationMethod. 59 | func (s *Proof) JWTAlgorithm() string { 60 | return JWTAlg 61 | } 62 | 63 | // CWTAlgorithm return cwt algorithm that corresponds to VerificationMethod. 64 | func (s *Proof) CWTAlgorithm() cose.Algorithm { 65 | return cose.AlgorithmES384 66 | } 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://raw.githubusercontent.com/trustbloc/vc-go/main/LICENSE) 2 | [![Release](https://img.shields.io/github/release/trustbloc/vc-go.svg?style=flat-square)](https://github.com/trustbloc/vc-go/releases/latest) 3 | [![Godocs](https://img.shields.io/badge/godoc-reference-blue.svg)](https://godoc.org/github.com/trustbloc/vc-go) 4 | 5 | [![Build Status](https://github.com/trustbloc/vc-go/actions/workflows/build.yml/badge.svg?branch=main)](https://github.com/trustbloc/vc-go/actions/workflows/build.yml) 6 | [![Go Report Card](https://goreportcard.com/badge/github.com/trustbloc/vc-go)](https://goreportcard.com/report/github.com/trustbloc/vc-go) 7 | 8 | 9 | # TrustBloc Verifiable Credential (VC) Go Library 10 | 11 | The TrustBloc VC Go repo contains [W3C Verifiable Credential(VC)](https://www.w3.org/TR/did-core/) related shared code. 12 | 13 | The library has the following implementations. 14 | - [W3C Verifiable Credential(VC)](https://www.w3.org/TR/vc-data-model/) Data model 15 | - JSON-LD Signature Suites 16 | - BbsBlsSignature2020 17 | - EcdsaSecp256k1Signature2019 18 | - Ed25519Signature2018 19 | - Ed25519Signature2020 20 | - JsonWebSignature2020 21 | - [Data Integrity](https://www.w3.org/TR/vc-data-integrity/) 22 | - JWT Signature Suites 23 | - [JWT](https://www.w3.org/TR/vc-data-model/#json-web-token) 24 | - [SD-JWT](https://www.ietf.org/archive/id/draft-ietf-oauth-selective-disclosure-jwt-05.html) 25 | - [DIF Presentation Exchange](https://identity.foundation/presentation-exchange/) 26 | - Verifiable Credential(VC) Status 27 | - [StatusList2021Entry](https://www.w3.org/TR/vc-status-list/) 28 | - [DIF Well Known DID Configuration](https://identity.foundation/.well-known/resources/did-configuration/) 29 | 30 | 31 | ## License 32 | Apache License, Version 2.0 (Apache-2.0). See the [LICENSE](LICENSE) file. 33 | -------------------------------------------------------------------------------- /proof/jwtproofs/eddsa/proof.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Gen Digital Inc. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package eddsa 8 | 9 | import ( 10 | "github.com/trustbloc/kms-go/spi/kms" 11 | "github.com/veraison/go-cose" 12 | 13 | "github.com/trustbloc/vc-go/proof" 14 | ) 15 | 16 | // Proof describes ed25519 proof type. 17 | type Proof struct { 18 | supportedVMs []proof.SupportedVerificationMethod 19 | } 20 | 21 | const ( 22 | // VerificationMethodType of eddsa. 23 | VerificationMethodType = "Ed25519VerificationKey2018" 24 | // JWKKeyType of eddsa. 25 | JWKKeyType = "OKP" 26 | // JWKCurve of eddsa. 27 | JWKCurve = "Ed25519" 28 | // JWTAlg of eddsa. 29 | JWTAlg = "EdDSA" 30 | ) 31 | 32 | // New an instance of ed25519 proof type descriptor. 33 | func New() *Proof { 34 | p := &Proof{} 35 | p.supportedVMs = []proof.SupportedVerificationMethod{ 36 | { 37 | VerificationMethodType: VerificationMethodType, 38 | KMSKeyType: kms.ED25519Type, 39 | JWKKeyType: JWKKeyType, 40 | JWKCurve: JWKCurve, 41 | }, 42 | { 43 | VerificationMethodType: "JsonWebKey2020", 44 | KMSKeyType: kms.ED25519Type, 45 | JWKKeyType: JWKKeyType, 46 | JWKCurve: JWKCurve, 47 | RequireJWK: true, 48 | }, 49 | } 50 | 51 | return p 52 | } 53 | 54 | // SupportedVerificationMethods returns list of verification methods supported by this proof type. 55 | func (s *Proof) SupportedVerificationMethods() []proof.SupportedVerificationMethod { 56 | return s.supportedVMs 57 | } 58 | 59 | // JWTAlgorithm return jwt alg that corresponds to VerificationMethod. 60 | func (s *Proof) JWTAlgorithm() string { 61 | return JWTAlg 62 | } 63 | 64 | // CWTAlgorithm return cwt algorithm that corresponds to VerificationMethod. 65 | func (s *Proof) CWTAlgorithm() cose.Algorithm { 66 | return cose.AlgorithmEd25519 67 | } 68 | -------------------------------------------------------------------------------- /verifiable/testdata/v2_valid_credential_multi_status.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": [ 3 | "https://www.w3.org/ns/credentials/v2", 4 | "https://www.w3.org/ns/credentials/examples/v2" 5 | ], 6 | "id": "http://example.edu/credentials/1872", 7 | "type": ["VerifiableCredential", "AlumniCredential"], 8 | "issuer": { 9 | "id": "did:example:76e12ec712ebc6f1c221ebfeb1f", 10 | "name": "Example University", 11 | "image": "data:image/png;base64,iVBOR" 12 | }, 13 | "validFrom": "2010-01-01T19:23:24Z", 14 | "credentialStatus": [ 15 | { 16 | "id": "urn:uuid:44118c22-5b7b-4d1f-8818-01ea041648e2", 17 | "statusListCredential": "http://vc-rest-echo.trustbloc.local:8075/issuer/groups/f9af5dcf-23d8-490f-b8cb-d8b83afcb91a/credentials/status/5a8f8941-aeb9-4200-ae03-76556745ec41", 18 | "statusListIndex": "5341", 19 | "statusPurpose": "revocation", 20 | "type": "BitstringStatusListEntry" 21 | }, 22 | { 23 | "id": "urn:uuid:44118c22-5b7b-4d1f-8818-11ea021648d2", 24 | "statusListCredential": "http://vc-rest-echo.trustbloc.local:8075/issuer/groups/f9af5dcf-23d8-490f-b8cb-d8b83afcb91a/credentials/status/5a8f8941-aeb9-4200-ae03-76556745ec43", 25 | "statusListIndex": "1337", 26 | "statusPurpose": "suspension", 27 | "type": "BitstringStatusListEntry" 28 | } 29 | ], 30 | "credentialSubject": { 31 | "id": "did:example:ebfeb1f712ebc6f1c276e12ec21", 32 | "alumniOf": { 33 | "id": "did:example:c276e12ec21ebfeb1f712ebc6f1", 34 | "name": [{ 35 | "value": "Example University", 36 | "lang": "en" 37 | }, { 38 | "value": "Exemple d'Université", 39 | "lang": "fr" 40 | }] 41 | } 42 | }, 43 | "termsOfUse": { 44 | "type": "TrustFrameworkPolicy", 45 | "trustFramework": "Employment&Life", 46 | "policyId": "https://policy.example/policies/125", 47 | "legalBasis": "professional qualifications directive" 48 | } 49 | } -------------------------------------------------------------------------------- /vermethod/vdrkeyresolver.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Gen Digital Inc. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package vermethod 8 | 9 | import ( 10 | "fmt" 11 | 12 | "github.com/trustbloc/did-go/doc/did" 13 | vdrapi "github.com/trustbloc/did-go/vdr/api" 14 | ) 15 | 16 | type didResolver interface { 17 | Resolve(did string, opts ...vdrapi.DIDMethodOption) (*did.DocResolution, error) 18 | } 19 | 20 | // VDRResolver resolves DID in order to find public keys for VC verification using vdr.Registry. 21 | // A source of DID could be issuer of VC or holder of VP. It can be also obtained from 22 | // JWS "issuer" claim or "verificationMethod" of Linked Data Proof. 23 | type VDRResolver struct { 24 | vdr didResolver 25 | } 26 | 27 | // NewVDRResolver creates VDRResolver. 28 | func NewVDRResolver(vdr didResolver) *VDRResolver { 29 | return &VDRResolver{vdr: vdr} 30 | } 31 | 32 | // ResolveVerificationMethod resolves verification method by key id. 33 | func (r *VDRResolver) ResolveVerificationMethod( 34 | verificationMethod string, 35 | expectedKeyController string, 36 | ) (*VerificationMethod, error) { 37 | docResolution, err := r.vdr.Resolve(expectedKeyController) 38 | if err != nil { 39 | return nil, fmt.Errorf("resolve DID %s: %w", expectedKeyController, err) 40 | } 41 | 42 | for _, verifications := range docResolution.DIDDocument.VerificationMethods() { 43 | for _, verification := range verifications { 44 | if verification.VerificationMethod.ID == verificationMethod && 45 | verification.Relationship != did.KeyAgreement { 46 | return &VerificationMethod{ 47 | Type: verification.VerificationMethod.Type, 48 | Value: verification.VerificationMethod.Value, 49 | JWK: verification.VerificationMethod.JSONWebKey(), 50 | }, nil 51 | } 52 | } 53 | } 54 | 55 | return nil, fmt.Errorf("public key with KID %s is not found for DID %s", verificationMethod, expectedKeyController) 56 | } 57 | -------------------------------------------------------------------------------- /verifiable/test-suite/contexts/credentials-examples_v1.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": [{ 3 | "@version": 1.1 4 | },"https://www.w3.org/ns/odrl.jsonld", { 5 | "ex": "https://example.org/examples#", 6 | "schema": "http://schema.org/", 7 | "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#", 8 | 9 | "3rdPartyCorrelation": "ex:3rdPartyCorrelation", 10 | "AllVerifiers": "ex:AllVerifiers", 11 | "Archival": "ex:Archival", 12 | "BachelorDegree": "ex:BachelorDegree", 13 | "Child": "ex:Child", 14 | "CLCredentialDefinition2019": "ex:CLCredentialDefinition2019", 15 | "CLSignature2019": "ex:CLSignature2019", 16 | "IssuerPolicy": "ex:IssuerPolicy", 17 | "HolderPolicy": "ex:HolderPolicy", 18 | "Mother": "ex:Mother", 19 | "RelationshipCredential": "ex:RelationshipCredential", 20 | "UniversityDegreeCredential": "ex:UniversityDegreeCredential", 21 | "ZkpExampleSchema2018": "ex:ZkpExampleSchema2018", 22 | 23 | "issuerData": "ex:issuerData", 24 | "attributes": "ex:attributes", 25 | "signature": "ex:signature", 26 | "signatureCorrectnessProof": "ex:signatureCorrectnessProof", 27 | "primaryProof": "ex:primaryProof", 28 | "nonRevocationProof": "ex:nonRevocationProof", 29 | 30 | "alumniOf": {"@id": "schema:alumniOf", "@type": "rdf:HTML"}, 31 | "child": {"@id": "ex:child", "@type": "@id"}, 32 | "degree": "ex:degree", 33 | "degreeType": "ex:degreeType", 34 | "degreeSchool": "ex:degreeSchool", 35 | "college": "ex:college", 36 | "name": {"@id": "schema:name", "@type": "rdf:HTML"}, 37 | "givenName": "schema:givenName", 38 | "familyName": "schema:familyName", 39 | "parent": {"@id": "ex:parent", "@type": "@id"}, 40 | "referenceId": "ex:referenceId", 41 | "documentPresence": "ex:documentPresence", 42 | "evidenceDocument": "ex:evidenceDocument", 43 | "spouse": "schema:spouse", 44 | "subjectPresence": "ex:subjectPresence", 45 | "verifier": {"@id": "ex:verifier", "@type": "@id"} 46 | }] 47 | } -------------------------------------------------------------------------------- /verifiable/presentation_jwt_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright SecureKey Technologies Inc. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package verifiable 8 | 9 | import ( 10 | "testing" 11 | 12 | "github.com/stretchr/testify/require" 13 | ) 14 | 15 | func TestNewJWTPresClaims(t *testing.T) { 16 | vp, err := newTestPresentation(t, []byte(validPresentation), WithPresDisabledProofCheck()) 17 | require.NoError(t, err) 18 | 19 | audience := []string{"did:example:4a57546973436f6f6c4a4a57573"} 20 | 21 | t.Run("new JWT claims of VP with minimization", func(t *testing.T) { 22 | claims, err := newJWTPresClaims(vp, audience, true) 23 | require.NoError(t, err) 24 | require.NotNil(t, claims) 25 | 26 | // issuer, ID and audience are filled in JWT claims 27 | require.Equal(t, vp.Holder, claims.Issuer) 28 | require.Equal(t, vp.ID, claims.ID) 29 | require.Equal(t, audience[0], claims.Audience[0]) 30 | 31 | require.NotNil(t, claims.Presentation) 32 | 33 | // ID and Holder are cleared (minimized) in "vp" claim 34 | require.Empty(t, claims.Presentation[vpFldID]) 35 | require.Empty(t, claims.Presentation[vpFldHolder]) 36 | 37 | // minimization does not affect original VP 38 | require.NotEqual(t, vp.ID, claims.Presentation[vpFldID]) 39 | require.NotEqual(t, vp.Holder, claims.Presentation[vpFldHolder]) 40 | }) 41 | 42 | t.Run("new JWT claims of VP without minimization", func(t *testing.T) { 43 | claims, err := newJWTPresClaims(vp, audience, false) 44 | require.NoError(t, err) 45 | require.NotNil(t, claims) 46 | 47 | // issuer, ID and audience are filled in JWT claims 48 | require.Equal(t, vp.Holder, claims.Issuer) 49 | require.Equal(t, vp.ID, claims.ID) 50 | require.Equal(t, audience[0], claims.Audience[0]) 51 | 52 | require.NotNil(t, claims.Presentation) 53 | 54 | // ID and Holder are cleared (minimized) in "vp" claim 55 | require.Equal(t, vp.ID, claims.Presentation[vpFldID]) 56 | require.Equal(t, vp.Holder, claims.Presentation[vpFldHolder]) 57 | }) 58 | } 59 | -------------------------------------------------------------------------------- /presexch/testdata/submission_requirements_pd.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "32f54163-7166-48f1-93d8-ff217bdb0653", 3 | "submission_requirements": [{ 4 | "name": "Citizenship Information", 5 | "rule": "pick", 6 | "count": 1, 7 | "from": "A" 8 | }], 9 | "input_descriptors": [ 10 | { 11 | "id": "VerifiedEmployee", 12 | "name": "Verified Employee", 13 | "group": ["A"], 14 | "constraints": { 15 | "fields": [ 16 | { 17 | "path": [ 18 | "$.type", 19 | "$.vc.type" 20 | ], 21 | "filter": { 22 | "type": "array", 23 | "contains": { 24 | "type": "string", 25 | "const": "VerifiedEmployee" 26 | } 27 | } 28 | } 29 | ] 30 | } 31 | }, 32 | { 33 | "id": "DriversLicense", 34 | "name": "Driver's License", 35 | "group": ["A"], 36 | "constraints": { 37 | "fields": [ 38 | { 39 | "path": [ 40 | "$.type", 41 | "$.vc.type" 42 | ], 43 | "filter": { 44 | "type": "array", 45 | "contains": { 46 | "type": "string", 47 | "const": "DriversLicense" 48 | } 49 | } 50 | } 51 | ] 52 | } 53 | }, 54 | { 55 | "id": "degree", 56 | "name": "degree", 57 | "group": ["A"], 58 | "purpose": "We can only hire with bachelor's degree.", 59 | "constraints": { 60 | "fields": [ 61 | { 62 | "path": [ 63 | "$.credentialSubject.degree.type", 64 | "$.vc.credentialSubject.degree.type" 65 | ], 66 | "purpose": "We can only hire with bachelor's degree.", 67 | "filter": { 68 | "type": "string", 69 | "const": "BachelorDegree" 70 | } 71 | } 72 | ] 73 | } 74 | } 75 | ] 76 | } -------------------------------------------------------------------------------- /verifiable/testdata/example-vcs.json: -------------------------------------------------------------------------------- 1 | { 2 | "@context": [ 3 | "https://www.w3.org/ns/credentials/v2", 4 | { 5 | "@protected": true, 6 | "DriverLicense": { 7 | "@context": { 8 | "@protected": true, 9 | "dateOfBirth": "urn:example:dateOfBirth", 10 | "documentIdentifier": "urn:example:documentIdentifier", 11 | "expirationDate": "urn:example:expiration", 12 | "id": "@id", 13 | "issuingAuthority": "urn:example:issuingAuthority", 14 | "type": "@type" 15 | }, 16 | "@id": "urn:example:DriverLicense" 17 | }, 18 | "DriverLicenseCredential": "urn:example:DriverLicenseCredential", 19 | "driverLicense": { 20 | "@id": "urn:example:driverLicense", 21 | "@type": "@id" 22 | } 23 | } 24 | ], 25 | "credentialSubject": { 26 | "driverLicense": { 27 | "dateOfBirth": "01-01-1990", 28 | "documentIdentifier": "T21387yc328c7y32h23f23", 29 | "expirationDate": "01-01-2030", 30 | "issuingAuthority": "VA", 31 | "type": "DriverLicense" 32 | }, 33 | "id": "urn:uuid:1a0e4ef5-091f-4060-842e-18e519ab9440" 34 | }, 35 | "id": "urn:uuid:8fff3a44-df43-49cf-88d1-613d7e01cf84", 36 | "issuer": { 37 | "id": "did:key:z82Lm1Lzm8tFZvUaA3egHh3MAbiPiD6LSGSi8X6yzQ9yH7zaxNt7JfDcVKLhVnn5pRPAkh4", 38 | "name": "ecdsa-test-suite-issuer" 39 | }, 40 | "proof": { 41 | "created": "2024-11-25T12:06:55Z", 42 | "cryptosuite": "ecdsa-rdfc-2019", 43 | "proofPurpose": "assertionMethod", 44 | "proofValue": "z3nj9tns6ZptdGiAC99azefeaKmbxLMHcF6S868922a6ghNF9dfDX1boevtEetH4cUsLU6wcfZ8nc3tmFnkmv6Pk5Bq3SGsaZfysKPXukgtE7cTeHDc3XZfWsAeNwZwmHYLkX2qAGucZDU", 45 | "type": "DataIntegrityProof", 46 | "verificationMethod": "did:key:z82Lm1Lzm8tFZvUaA3egHh3MAbiPiD6LSGSi8X6yzQ9yH7zaxNt7JfDcVKLhVnn5pRPAkh4#z82Lm1Lzm8tFZvUaA3egHh3MAbiPiD6LSGSi8X6yzQ9yH7zaxNt7JfDcVKLhVnn5pRPAkh4" 47 | }, 48 | "type": [ 49 | "VerifiableCredential", 50 | "DriverLicenseCredential" 51 | ] 52 | } 53 | -------------------------------------------------------------------------------- /verifiable/credential_cwt.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright SecureKey Technologies Inc. All Rights Reserved. 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package verifiable 7 | 8 | import ( 9 | "fmt" 10 | 11 | josejwt "github.com/go-jose/go-jose/v3/jwt" 12 | "github.com/veraison/go-cose" 13 | 14 | "github.com/trustbloc/vc-go/cwt" 15 | jsonutil "github.com/trustbloc/vc-go/util/json" 16 | ) 17 | 18 | // CWTClaims converts Verifiable Credential into CWT Credential claims, which can be than serialized 19 | // e.g. into JWS. 20 | func (vc *Credential) CWTClaims() (*CWTCredClaims, error) { 21 | return newCWTCredClaims(vc) 22 | } 23 | 24 | // newJWTCredClaims creates JWT Claims of VC with an option to minimize certain fields of VC 25 | // which is put into "vc" claim. 26 | func newCWTCredClaims(vc *Credential) (*CWTCredClaims, error) { 27 | vcc := &vc.credentialContents 28 | 29 | subjectID, err := SubjectID(vcc.Subject) 30 | if err != nil { 31 | return nil, fmt.Errorf("get VC subject id: %w", err) 32 | } 33 | 34 | // currently jwt encoding supports only single subject (by the spec) 35 | claims := &CWTClaims{ 36 | Issuer: vcc.Issuer.ID, // iss 37 | NotBefore: josejwt.NewNumericDate(vcc.Issued.Time), // nbf 38 | ID: vcc.ID, // jti 39 | Subject: subjectID, // sub 40 | } 41 | 42 | if vcc.Expired != nil { 43 | claims.Expiry = josejwt.NewNumericDate(vcc.Expired.Time) // exp 44 | } 45 | 46 | if vcc.Issued != nil { 47 | claims.IssuedAt = josejwt.NewNumericDate(vcc.Issued.Time) 48 | } 49 | 50 | credentialJSONCopy := jsonutil.ShallowCopyObj(vc.credentialJSON) 51 | 52 | credClaims := &CWTCredClaims{ 53 | CWTClaims: claims, 54 | VC: credentialJSONCopy, 55 | } 56 | 57 | return credClaims, nil 58 | } 59 | 60 | // MarshaCOSE serializes into signed form (COSE). 61 | func (jcc *CWTCredClaims) MarshaCOSE( 62 | signatureAlg cose.Algorithm, 63 | signer cwt.ProofCreator, 64 | keyID string, 65 | ) ([]byte, *cose.Sign1Message, error) { 66 | return marshalCOSE(jcc, signatureAlg, signer, keyID) 67 | } 68 | -------------------------------------------------------------------------------- /presexch/testdata/sample_2.json: -------------------------------------------------------------------------------- 1 | { 2 | "id":"32f54163-7166-48f1-93d8-ff217bdb0653", 3 | "submission_requirements":[ 4 | { 5 | "name":"Citizenship Information", 6 | "rule":"pick", 7 | "count":1, 8 | "from":"A" 9 | } 10 | ], 11 | "input_descriptors":[ 12 | { 13 | "id":"citizenship_input_1", 14 | "name":"EU Driver's License", 15 | "group":[ 16 | "A" 17 | ], 18 | "schema":[ 19 | { 20 | "uri":"https://eu.com/claims/DriversLicense.json" 21 | } 22 | ], 23 | "constraints":{ 24 | "fields":[ 25 | { 26 | "path":[ 27 | "$.issuer", 28 | "$.vc.issuer", 29 | "$.iss" 30 | ], 31 | "purpose":"The claim must be from one of the specified issuers", 32 | "filter":{ 33 | "type":"string", 34 | "pattern":"did:example:gov1|did:example:gov2" 35 | } 36 | }, 37 | { 38 | "path":[ 39 | "$.credentialSubject.dob", 40 | "$.vc.credentialSubject.dob", 41 | "$.dob" 42 | ], 43 | "filter":{ 44 | "type":"string", 45 | "format":"date", 46 | "maximum":"1999-6-15" 47 | } 48 | } 49 | ] 50 | } 51 | }, 52 | { 53 | "id":"citizenship_input_2", 54 | "name":"US Passport", 55 | "group":[ 56 | "A" 57 | ], 58 | "schema":[ 59 | { 60 | "uri":"hub://did:foo:123/Collections/schema.us.gov/passport.json" 61 | } 62 | ], 63 | "constraints":{ 64 | "fields":[ 65 | { 66 | "path":[ 67 | "$.credentialSubject.birth_date", 68 | "$.vc.credentialSubject.birth_date", 69 | "$.birth_date" 70 | ], 71 | "filter":{ 72 | "type":"string", 73 | "format":"date", 74 | "maximum":"1999-5-16" 75 | } 76 | } 77 | ] 78 | } 79 | } 80 | ] 81 | } -------------------------------------------------------------------------------- /presexch/testdata/permanent_resident_card.jwt: -------------------------------------------------------------------------------- 1 | eyJhbGciOiJFUzI1NiIsImtpZCI6ImRpZDpvcmI6dUFBQTpFaUQwTXJEV2I5VTVkcWJxWGxPQXRGakhzREVpTmJDX3ZySDh5d0xOc1E4NkhBIzIxMzNmODg5LWRjMGMtNGI0Mi1hMDgwLTliZDk1OGRkYmZhMSJ9.eyJleHAiOjE4OTA5OTQ3OTIsImlhdCI6MTU3NTM3NTU5MiwiaXNzIjoiZGlkOm9yYjp1QUFBOkVpRDBNckRXYjlVNWRxYnFYbE9BdEZqSHNERWlOYkNfdnJIOHl3TE5zUTg2SEEiLCJqdGkiOiJ1cm46dXVpZDoyMjI5Y2QzNC05MmFjLTQyYmYtODNiMC03NDMwYWRkNDBiYTgiLCJuYmYiOjE1NzUzNzU1OTIsInN1YiI6ImRpZDpvcmI6dUFBQTpFaUNMZXMyejVVaktHSVN1UGQtdjJwTDhnX1E1Wm5XUG9yaE5FOWpDZzhkYlJnIiwidmMiOnsiQGNvbnRleHQiOlsiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvdjEiLCJodHRwczovL3czaWQub3JnL2NpdGl6ZW5zaGlwL3YxIiwiaHR0cHM6Ly93M2lkLm9yZy92Yy9zdGF0dXMtbGlzdC8yMDIxL3YxIl0sImNyZWRlbnRpYWxTdGF0dXMiOnsiaWQiOiJ1cm46dXVpZDplNTJlOGY4Ni1jMjUwLTQ2ZTEtOTcxOS1jNmU5YmQ2OTVhYTEiLCJzdGF0dXNMaXN0Q3JlZGVudGlhbCI6Imh0dHA6Ly92Yy1yZXN0LWVjaG8udHJ1c3RibG9jLmxvY2FsOjgwNzUvaXNzdWVyL3Byb2ZpbGVzL2lfbXlwcm9maWxlX3VkX2VzMjU2X2p3dC9jcmVkZW50aWFscy9zdGF0dXMvMSIsInN0YXR1c0xpc3RJbmRleCI6IjIiLCJzdGF0dXNQdXJwb3NlIjoicmV2b2NhdGlvbiIsInR5cGUiOiJTdGF0dXNMaXN0MjAyMUVudHJ5In0sImNyZWRlbnRpYWxTdWJqZWN0Ijp7ImJpcnRoQ291bnRyeSI6IkJhaGFtYXMiLCJiaXJ0aERhdGUiOiIxOTU4LTA3LTE3IiwiY29tbXV0ZXJDbGFzc2lmaWNhdGlvbiI6IkMxIiwiZmFtaWx5TmFtZSI6IlNNSVRIIiwiZ2VuZGVyIjoiTWFsZSIsImdpdmVuTmFtZSI6IkpPSE4iLCJpZCI6ImRpZDpvcmI6dUFBQTpFaUNMZXMyejVVaktHSVN1UGQtdjJwTDhnX1E1Wm5XUG9yaE5FOWpDZzhkYlJnIiwiaW1hZ2UiOiJkYXRhOmltYWdlL3BuZztiYXNlNjQsaVZCT1J3MEtHZ28uLi5rSmdnZz09IiwibHByQ2F0ZWdvcnkiOiJDMDkiLCJscHJOdW1iZXIiOiI5OTktOTk5LTk5OSIsInJlc2lkZW50U2luY2UiOiIyMDE1LTAxLTAxIiwidHlwZSI6WyJQZXJtYW5lbnRSZXNpZGVudCIsIlBlcnNvbiJdfSwiZGVzY3JpcHRpb24iOiJQZXJtYW5lbnQgUmVzaWRlbnQgQ2FyZCIsImV4cGlyYXRpb25EYXRlIjoiMjAyOS0xMi0wM1QxMjoxOTo1MloiLCJpZCI6InVybjp1dWlkOjIyMjljZDM0LTkyYWMtNDJiZi04M2IwLTc0MzBhZGQ0MGJhOCIsImlzc3VhbmNlRGF0ZSI6IjIwMTktMTItMDNUMTI6MTk6NTJaIiwiaXNzdWVyIjp7ImlkIjoiZGlkOm9yYjp1QUFBOkVpRDBNckRXYjlVNWRxYnFYbE9BdEZqSHNERWlOYkNfdnJIOHl3TE5zUTg2SEEiLCJuYW1lIjoiaV9teXByb2ZpbGVfdWRfZXMyNTZfand0In0sIm5hbWUiOiJQZXJtYW5lbnQgUmVzaWRlbnQgQ2FyZCIsInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiLCJQZXJtYW5lbnRSZXNpZGVudENhcmQiXX19.MEYCIQCOwohd_4ihHC30aS0QGHiFHcWChDQ08HRMWdpimokHdgIhAKaCfr3yGJ7xayjkUl2C2VDI0YSfCRKq9UcQWBUvV752 -------------------------------------------------------------------------------- /internal/testutil/kmscryptoutil/kmscryptoutil.go: -------------------------------------------------------------------------------- 1 | // Package kmscryptoutil contains test utilities for tests using the kmscrypto wrappers. 2 | package kmscryptoutil 3 | 4 | import ( 5 | "crypto" 6 | "encoding/base64" 7 | "testing" 8 | 9 | "github.com/stretchr/testify/require" 10 | "github.com/trustbloc/kms-go/doc/jose/jwk" 11 | "github.com/trustbloc/kms-go/doc/jose/jwk/jwksupport" 12 | "github.com/trustbloc/kms-go/kms" 13 | "github.com/trustbloc/kms-go/secretlock/noop" 14 | kmsapi "github.com/trustbloc/kms-go/spi/kms" 15 | wrapperapi "github.com/trustbloc/kms-go/wrapper/api" 16 | "github.com/trustbloc/kms-go/wrapper/localsuite" 17 | "github.com/trustbloc/vc-go/legacy/mock/storage" 18 | ) 19 | 20 | // LocalKMSCrypto creates a kmscrypto.KMSCrypto instance that uses localkms and tinkcrypto. 21 | func LocalKMSCrypto(t *testing.T) wrapperapi.KMSCrypto { 22 | kc, err := LocalKMSCryptoErr() 23 | require.NoError(t, err) 24 | 25 | return kc 26 | } 27 | 28 | // LocalKMSCryptoErr creates a kmscrypto.KMSCrypto instance that uses localkms and tinkcrypto. 29 | // 30 | // This API returns an error instead of needing a testing parameter. 31 | func LocalKMSCryptoErr() (wrapperapi.KMSCrypto, error) { 32 | suite, err := LocalKMSCryptoSuite() 33 | if err != nil { 34 | return nil, err 35 | } 36 | 37 | return suite.KMSCrypto() 38 | } 39 | 40 | // LocalKMSCryptoSuite creates a kms+crypto wrapper suite that uses localkms and tinkcrypto. 41 | func LocalKMSCryptoSuite() (wrapperapi.Suite, error) { 42 | p, err := kms.NewAriesProviderWrapper(storage.NewMockStoreProvider()) 43 | if err != nil { 44 | return nil, err 45 | } 46 | 47 | return localsuite.NewLocalCryptoSuite("local-lock://custom/master/key/", p, &noop.NoLock{}) 48 | } 49 | 50 | // PubKeyBytesToJWK converts the given public key to a JWK. 51 | func PubKeyBytesToJWK(t *testing.T, pubKeyBytes []byte, keyType kmsapi.KeyType) *jwk.JWK { 52 | pubJWK, err := jwksupport.PubKeyBytesToJWK(pubKeyBytes, keyType) 53 | require.NoError(t, err) 54 | 55 | tp, err := pubJWK.Thumbprint(crypto.SHA256) 56 | require.NoError(t, err) 57 | 58 | pubJWK.KeyID = base64.RawURLEncoding.EncodeToString(tp) 59 | 60 | return pubJWK 61 | } 62 | -------------------------------------------------------------------------------- /verifiable/presentation_cwt.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright SecureKey Technologies Inc. All Rights Reserved. 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package verifiable 7 | 8 | import ( 9 | "fmt" 10 | 11 | "github.com/fxamacker/cbor/v2" 12 | "github.com/veraison/go-cose" 13 | 14 | "github.com/trustbloc/vc-go/cwt" 15 | "github.com/trustbloc/vc-go/jwt" 16 | ) 17 | 18 | func newCWTPresClaims(vp *Presentation, audience []string, minimizeVP bool) (*CWTPresClaims, error) { 19 | jwtClaims, err := newJWTPresClaims(vp, audience, minimizeVP) 20 | if err != nil { 21 | return nil, err 22 | } 23 | 24 | return &CWTPresClaims{ 25 | Claims: jwtClaims.Claims, 26 | Presentation: jwtClaims.Presentation, 27 | }, nil 28 | } 29 | 30 | type CWTPresClaims struct { 31 | *jwt.Claims 32 | Presentation rawPresentation `json:"vp,omitempty"` 33 | } 34 | 35 | func (c *CWTPresClaims) MarshalCWT( 36 | signatureAlg cose.Algorithm, 37 | signer cwt.ProofCreator, 38 | keyID string, 39 | ) ([]byte, *cose.Sign1Message, error) { 40 | return marshalCOSE(c, signatureAlg, signer, keyID) 41 | } 42 | 43 | // CreateCWTVP creates a CWT presentation from the given presentation. 44 | func (vp *Presentation) CreateCWTVP( 45 | aud []string, 46 | signatureAlg cose.Algorithm, 47 | signer cwt.ProofCreator, 48 | keyID string, 49 | minimizeVP bool, 50 | ) (*Presentation, error) { 51 | cwtClaims, err := vp.CWTClaims(aud, minimizeVP) 52 | if err != nil { 53 | return nil, fmt.Errorf("failed to create CWT claims: %w", err) 54 | } 55 | 56 | cwtBytes, msg, err := cwtClaims.MarshalCWT(signatureAlg, signer, keyID) 57 | if err != nil { 58 | return nil, fmt.Errorf("failed to marshal CWT: %w", err) 59 | } 60 | 61 | var vpMap map[interface{}]interface{} 62 | 63 | err = cbor.Unmarshal(msg.Payload, &vpMap) 64 | if err != nil { 65 | return nil, fmt.Errorf("failed to unmarshal VP map: %w", err) 66 | } 67 | 68 | vp2 := vp.Clone() 69 | 70 | vp2.CWT = &VpCWT{ 71 | Raw: cwtBytes, 72 | Message: msg, 73 | VPMap: convertToStringMap(vpMap), 74 | } 75 | 76 | vp2.JWT = "" 77 | 78 | return vp2, nil 79 | } 80 | 81 | // IsCWT returns true is the presentation is CWT. 82 | func (vp *Presentation) IsCWT() bool { 83 | return vp.CWT != nil && len(vp.CWT.Raw) > 0 84 | } 85 | -------------------------------------------------------------------------------- /sdjwt/verifier/keybidning.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright SecureKey Technologies Inc. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | /* 7 | Package verifier enables the Verifier: An entity that requests, checks and 8 | extracts the claims from an SD-JWT and respective Disclosures. 9 | */ 10 | 11 | package verifier 12 | 13 | import ( 14 | "fmt" 15 | 16 | "github.com/go-jose/go-jose/v3/jwt" 17 | "github.com/mitchellh/mapstructure" 18 | 19 | afgjwt "github.com/trustbloc/vc-go/jwt" 20 | utils "github.com/trustbloc/vc-go/util/maphelpers" 21 | ) 22 | 23 | // verifyKeyBindingJWT verifies key binding JWT. 24 | // Section: https://www.ietf.org/archive/id/draft-ietf-oauth-selective-disclosure-jwt-02.html#section-6.2-4.6.1 25 | func verifyKeyBindingJWT(holderJWT *afgjwt.JSONWebToken, pOpts *parseOpts) error { 26 | var bindingPayload keyBindingPayload 27 | 28 | d, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ 29 | Result: &bindingPayload, 30 | TagName: "json", 31 | Squash: true, 32 | WeaklyTypedInput: true, 33 | DecodeHook: utils.JSONNumberToJwtNumericDate(), 34 | }) 35 | if err != nil { 36 | return fmt.Errorf("mapstruct verifyHodlder. error: %w", err) 37 | } 38 | 39 | if err = d.Decode(holderJWT.Payload); err != nil { 40 | return fmt.Errorf("mapstruct verifyHodlder decode. error: %w", err) 41 | } 42 | 43 | if pOpts.expectedNonceForHolderVerification != "" && pOpts.expectedNonceForHolderVerification != bindingPayload.Nonce { 44 | return fmt.Errorf("nonce value '%s' does not match expected nonce value '%s'", 45 | bindingPayload.Nonce, pOpts.expectedNonceForHolderVerification) 46 | } 47 | 48 | if pOpts.expectedAudienceForHolderVerification != "" && 49 | pOpts.expectedAudienceForHolderVerification != bindingPayload.Audience { 50 | return fmt.Errorf("audience value '%s' does not match expected audience value '%s'", 51 | bindingPayload.Audience, pOpts.expectedAudienceForHolderVerification) 52 | } 53 | 54 | return nil 55 | } 56 | 57 | // keyBindingPayload represents expected key binding payload. 58 | type keyBindingPayload struct { 59 | Nonce string `json:"nonce,omitempty"` 60 | Audience string `json:"aud,omitempty"` 61 | IssuedAt *jwt.NumericDate `json:"iat,omitempty"` 62 | } 63 | -------------------------------------------------------------------------------- /verifiable/did_ldp_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright SecureKey Technologies Inc. All Rights Reserved. 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package verifiable 7 | 8 | import ( 9 | _ "embed" 10 | "testing" 11 | 12 | "github.com/stretchr/testify/require" 13 | "github.com/trustbloc/did-go/doc/did" 14 | jsonldsig "github.com/trustbloc/did-go/doc/ld/processor" 15 | "github.com/trustbloc/kms-go/spi/kms" 16 | 17 | "github.com/trustbloc/vc-go/proof/testsupport" 18 | ) 19 | 20 | //go:embed testdata/valid_doc.jsonld 21 | var validDoc []byte //nolint:gochecknoglobals 22 | 23 | func Test_AddDIDLinkedDataProof_VerifyDIDProof(t *testing.T) { 24 | r := require.New(t) 25 | 26 | didDoc, err := did.ParseDocument(validDoc) 27 | r.NoError(err) 28 | 29 | proofCreator, proofChecker := testsupport.NewKMSSigVerPair(t, kms.ED25519Type, 30 | "did:example:76e12ec712ebc6f1c221ebfeb1f#key1") 31 | 32 | ldpContext := &LinkedDataProofContext{ 33 | SignatureType: "Ed25519Signature2018", 34 | KeyType: kms.ED25519Type, 35 | SignatureRepresentation: SignatureProofValue, 36 | ProofCreator: proofCreator, 37 | VerificationMethod: "did:example:76e12ec712ebc6f1c221ebfeb1f#key1", 38 | } 39 | 40 | dl := createTestDocumentLoader(t) 41 | 42 | t.Run("Success LDP", func(t *testing.T) { 43 | signedDoc, err := AddDIDLinkedDataProof( 44 | didDoc, ldpContext, jsonldsig.WithDocumentLoader(dl)) 45 | r.NoError(err) 46 | 47 | err = VerifyDIDProof( 48 | signedDoc, 49 | WithDIDProofChecker(proofChecker), 50 | WithDIDJSONLDDocumentLoader(dl), 51 | ) 52 | r.NoError(err) 53 | 54 | r.Len(signedDoc.Proof, 1) 55 | r.Empty(signedDoc.Proof[0].JWS) 56 | r.NotEmpty(signedDoc.Proof[0].ProofValue) 57 | }) 58 | 59 | t.Run("Success JWK", func(t *testing.T) { 60 | ldpContext.SignatureRepresentation = SignatureJWS 61 | 62 | signedDoc, err := AddDIDLinkedDataProof( 63 | didDoc, ldpContext, jsonldsig.WithDocumentLoader(dl)) 64 | r.NoError(err) 65 | 66 | err = VerifyDIDProof( 67 | signedDoc, 68 | WithDIDProofChecker(proofChecker), 69 | WithDIDJSONLDDocumentLoader(dl), 70 | ) 71 | r.NoError(err) 72 | 73 | r.Len(signedDoc.Proof, 1) 74 | r.NotEmpty(signedDoc.Proof[0].JWS) 75 | r.Empty(signedDoc.Proof[0].ProofValue) 76 | }) 77 | } 78 | -------------------------------------------------------------------------------- /verifiable/testdata/v1_credential_without_issuancedate.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": [ 3 | "https://www.w3.org/2018/credentials/v1", 4 | "https://www.w3.org/2018/credentials/examples/v1", 5 | "https://w3id.org/security/jws/v1", 6 | "https://trustbloc.github.io/context/vc/examples-v1.jsonld", 7 | "https://w3id.org/security/suites/ed25519-2020/v1" 8 | ], 9 | "id": "http://example.edu/credentials/1872", 10 | "type": "VerifiableCredential", 11 | "credentialSubject": { 12 | "id": "did:example:ebfeb1f712ebc6f1c276e12ec21" 13 | }, 14 | "issuer": { 15 | "id": "did:example:76e12ec712ebc6f1c221ebfeb1f", 16 | "name": "Example University", 17 | "image": "data:image/png;base64,iVBOR" 18 | }, 19 | "expirationDate": "2020-01-01T19:23:24Z", 20 | "credentialStatus": { 21 | "id": "https://example.edu/status/24", 22 | "type": "CredentialStatusList2017" 23 | }, 24 | "evidence": [ 25 | { 26 | "id": "https://example.edu/evidence/f2aeec97-fc0d-42bf-8ca7-0548192d4231", 27 | "type": [ 28 | "DocumentVerification" 29 | ], 30 | "verifier": "https://example.edu/issuers/14", 31 | "evidenceDocument": "DriversLicense", 32 | "subjectPresence": "Physical", 33 | "documentPresence": "Physical" 34 | }, 35 | { 36 | "id": "https://example.edu/evidence/f2aeec97-fc0d-42bf-8ca7-0548192dxyzab", 37 | "type": [ 38 | "SupportingActivity" 39 | ], 40 | "verifier": "https://example.edu/issuers/14", 41 | "evidenceDocument": "Fluid Dynamics Focus", 42 | "subjectPresence": "Digital", 43 | "documentPresence": "Digital" 44 | } 45 | ], 46 | "termsOfUse": [ 47 | { 48 | "type": "IssuerPolicy", 49 | "id": "http://example.com/policies/credential/4", 50 | "profile": "http://example.com/profiles/credential", 51 | "prohibition": [ 52 | { 53 | "assigner": "https://example.edu/issuers/14", 54 | "assignee": "AllVerifiers", 55 | "target": "http://example.edu/credentials/3732", 56 | "action": [ 57 | "Archival" 58 | ] 59 | } 60 | ] 61 | } 62 | ], 63 | "refreshService": { 64 | "id": "https://example.edu/refresh/3732", 65 | "type": "ManualRefreshService2018" 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /sdjwt/verifier/holderbidning.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright SecureKey Technologies Inc. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | /* 7 | Package verifier enables the Verifier: An entity that requests, checks and 8 | extracts the claims from an SD-JWT and respective Disclosures. 9 | */ 10 | 11 | package verifier 12 | 13 | import ( 14 | "fmt" 15 | 16 | "github.com/go-jose/go-jose/v3/jwt" 17 | "github.com/mitchellh/mapstructure" 18 | 19 | afgjwt "github.com/trustbloc/vc-go/jwt" 20 | utils "github.com/trustbloc/vc-go/util/maphelpers" 21 | ) 22 | 23 | // verifyHolderBindingJWT verifies holder binding JWT. 24 | // Section: https://www.ietf.org/archive/id/draft-ietf-oauth-selective-disclosure-jwt-05.html#section-6.3-4.3.1 25 | func verifyHolderBindingJWT(holderJWT *afgjwt.JSONWebToken, pOpts *parseOpts) error { 26 | var bindingPayload holderBindingPayload 27 | 28 | d, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ 29 | Result: &bindingPayload, 30 | TagName: "json", 31 | Squash: true, 32 | WeaklyTypedInput: true, 33 | DecodeHook: utils.JSONNumberToJwtNumericDate(), 34 | }) 35 | if err != nil { 36 | return fmt.Errorf("mapstruct verifyHodlder. error: %w", err) 37 | } 38 | 39 | if err = d.Decode(holderJWT.Payload); err != nil { 40 | return fmt.Errorf("mapstruct verifyHodlder decode. error: %w", err) 41 | } 42 | 43 | if pOpts.expectedNonceForHolderVerification != "" && 44 | pOpts.expectedNonceForHolderVerification != bindingPayload.Nonce { 45 | return fmt.Errorf("nonce value '%s' does not match expected nonce value '%s'", 46 | bindingPayload.Nonce, pOpts.expectedNonceForHolderVerification) 47 | } 48 | 49 | if pOpts.expectedAudienceForHolderVerification != "" && 50 | pOpts.expectedAudienceForHolderVerification != bindingPayload.Audience { 51 | return fmt.Errorf("audience value '%s' does not match expected audience value '%s'", 52 | bindingPayload.Audience, pOpts.expectedAudienceForHolderVerification) 53 | } 54 | 55 | return nil 56 | } 57 | 58 | // holderBindingPayload represents expected holder binding payload. 59 | type holderBindingPayload struct { 60 | Nonce string `json:"nonce,omitempty"` 61 | Audience string `json:"aud,omitempty"` 62 | IssuedAt *jwt.NumericDate `json:"iat,omitempty"` 63 | } 64 | -------------------------------------------------------------------------------- /verifiable/testdata/v1_valid_credential.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": [ 3 | "https://www.w3.org/2018/credentials/v1", 4 | "https://www.w3.org/2018/credentials/examples/v1", 5 | "https://w3id.org/security/jws/v1", 6 | "https://trustbloc.github.io/context/vc/examples-v1.jsonld", 7 | "https://w3id.org/security/suites/ed25519-2020/v1" 8 | ], 9 | "id": "http://example.edu/credentials/1872", 10 | "type": "VerifiableCredential", 11 | "credentialSubject": { 12 | "id": "did:example:ebfeb1f712ebc6f1c276e12ec21" 13 | }, 14 | "issuer": { 15 | "id": "did:example:76e12ec712ebc6f1c221ebfeb1f", 16 | "name": "Example University", 17 | "image": "data:image/png;base64,iVBOR" 18 | }, 19 | "issuanceDate": "2010-01-01T19:23:24Z", 20 | "expirationDate": "2020-01-01T19:23:24Z", 21 | "credentialStatus": { 22 | "id": "https://example.edu/status/24", 23 | "type": "CredentialStatusList2017" 24 | }, 25 | "evidence": [ 26 | { 27 | "id": "https://example.edu/evidence/f2aeec97-fc0d-42bf-8ca7-0548192d4231", 28 | "type": [ 29 | "DocumentVerification" 30 | ], 31 | "verifier": "https://example.edu/issuers/14", 32 | "evidenceDocument": "DriversLicense", 33 | "subjectPresence": "Physical", 34 | "documentPresence": "Physical" 35 | }, 36 | { 37 | "id": "https://example.edu/evidence/f2aeec97-fc0d-42bf-8ca7-0548192dxyzab", 38 | "type": [ 39 | "SupportingActivity" 40 | ], 41 | "verifier": "https://example.edu/issuers/14", 42 | "evidenceDocument": "Fluid Dynamics Focus", 43 | "subjectPresence": "Digital", 44 | "documentPresence": "Digital" 45 | } 46 | ], 47 | "termsOfUse": [ 48 | { 49 | "type": "IssuerPolicy", 50 | "id": "http://example.com/policies/credential/4", 51 | "profile": "http://example.com/profiles/credential", 52 | "prohibition": [ 53 | { 54 | "assigner": "https://example.edu/issuers/14", 55 | "assignee": "AllVerifiers", 56 | "target": "http://example.edu/credentials/3732", 57 | "action": [ 58 | "Archival" 59 | ] 60 | } 61 | ] 62 | } 63 | ], 64 | "refreshService": { 65 | "id": "https://example.edu/refresh/3732", 66 | "type": "ManualRefreshService2018" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /crypto-ext/verifiers/ed25519/verifier_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Gen Digital Inc. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package ed25519_test 8 | 9 | import ( 10 | "testing" 11 | 12 | "github.com/stretchr/testify/require" 13 | "github.com/trustbloc/kms-go/doc/jose/jwk" 14 | kmsapi "github.com/trustbloc/kms-go/spi/kms" 15 | 16 | gojose "github.com/go-jose/go-jose/v3" 17 | 18 | "github.com/trustbloc/vc-go/crypto-ext/pubkey" 19 | "github.com/trustbloc/vc-go/crypto-ext/testutil" 20 | "github.com/trustbloc/vc-go/crypto-ext/verifiers/ed25519" 21 | ) 22 | 23 | func TestNewEd25519SignatureVerifier(t *testing.T) { 24 | v := ed25519.New() 25 | require.NotNil(t, v) 26 | 27 | signer, pubKey, err := testutil.CreateKMSSigner(kmsapi.ED25519Type, true) 28 | require.NoError(t, err) 29 | 30 | msg := []byte("test message") 31 | msgSig, err := signer.Sign(msg) 32 | require.NoError(t, err) 33 | 34 | err = v.Verify(msgSig, msg, pubKey) 35 | require.NoError(t, err) 36 | 37 | // invalid public key type 38 | err = v.Verify(msgSig, msg, &pubkey.PublicKey{ 39 | Type: kmsapi.ED25519, 40 | BytesKey: &pubkey.BytesKey{Bytes: []byte("invalid-key")}, 41 | }) 42 | require.Error(t, err) 43 | require.EqualError(t, err, "ed25519: invalid key") 44 | 45 | // invalid public key type2 46 | err = v.Verify(msgSig, msg, &pubkey.PublicKey{ 47 | Type: kmsapi.ED25519, 48 | }) 49 | require.Error(t, err) 50 | require.EqualError(t, err, "ed25519: invalid key") 51 | 52 | // unsupported key type 53 | err = v.Verify(msgSig, msg, &pubkey.PublicKey{ 54 | Type: kmsapi.RSAPS256Type, 55 | BytesKey: &pubkey.BytesKey{Bytes: []byte("invalid-key")}, 56 | }) 57 | require.Error(t, err) 58 | require.EqualError(t, err, "unsupported key type RSAPS256") 59 | 60 | // invalid JWK value 61 | err = v.Verify(msgSig, msg, &pubkey.PublicKey{ 62 | Type: kmsapi.ED25519, 63 | JWK: &jwk.JWK{ 64 | JSONWebKey: gojose.JSONWebKey{ 65 | Key: "foo", 66 | }, 67 | Kty: "OKP", 68 | Crv: "Ed25519", 69 | }, 70 | }) 71 | require.Error(t, err) 72 | require.EqualError(t, err, "public key not ed25519.VerificationMethod") 73 | 74 | // invalid signature 75 | err = v.Verify([]byte("invalid signature"), msg, pubKey) 76 | require.Error(t, err) 77 | require.EqualError(t, err, "ed25519: invalid signature") 78 | } 79 | -------------------------------------------------------------------------------- /verifiable/credential_jwt_unsecured_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright SecureKey Technologies Inc. All Rights Reserved. 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package verifiable 7 | 8 | import ( 9 | "encoding/json" 10 | "testing" 11 | 12 | "github.com/go-jose/go-jose/v3/jwt" 13 | "github.com/stretchr/testify/require" 14 | ) 15 | 16 | func TestCredentialJWTClaimsMarshallingToUnsecuredJWT(t *testing.T) { 17 | vc, err := parseTestCredential(t, []byte(v1ValidCredential), WithDisabledProofCheck()) 18 | require.NoError(t, err) 19 | 20 | jwtClaims, err := vc.JWTClaims(true) 21 | require.NoError(t, err) 22 | 23 | sJWT, err := jwtClaims.MarshalUnsecuredJWT() 24 | require.NoError(t, err) 25 | require.NotNil(t, sJWT) 26 | 27 | vcBytes, err := decodeCredJWTUnsecured(sJWT) 28 | require.NoError(t, err) 29 | 30 | var vcRaw JSONObject 31 | err = json.Unmarshal(vcBytes, &vcRaw) 32 | require.NoError(t, err) 33 | 34 | require.NoError(t, err) 35 | require.Equal(t, vc.stringJSON(t), jsonObjectToString(t, vcRaw)) 36 | } 37 | 38 | func TestCredUnsecuredJWTDecoderParseJWTClaims(t *testing.T) { 39 | t.Run("Successful unsecured JWT decoding", func(t *testing.T) { 40 | vc, err := parseTestCredential(t, []byte(v1ValidCredential), WithDisabledProofCheck()) 41 | require.NoError(t, err) 42 | 43 | jwtClaims, err := vc.JWTClaims(true) 44 | require.NoError(t, err) 45 | 46 | sJWT, err := jwtClaims.MarshalUnsecuredJWT() 47 | require.NoError(t, err) 48 | 49 | decodedCred, err := decodeCredJWTUnsecured(sJWT) 50 | require.NoError(t, err) 51 | require.NotNil(t, decodedCred) 52 | }) 53 | 54 | t.Run("Invalid serialized unsecured JWT", func(t *testing.T) { 55 | vcBytes, err := decodeCredJWTUnsecured("parse JWT") 56 | require.Error(t, err) 57 | require.Contains(t, err.Error(), "parse JWT") 58 | require.Nil(t, vcBytes) 59 | }) 60 | 61 | t.Run("Invalid format of \"vc\" claim", func(t *testing.T) { 62 | claims := &invalidCredClaims{ 63 | Claims: &jwt.Claims{}, 64 | Credential: 55, // "vc" claim of invalid format 65 | } 66 | 67 | rawJWT, err := marshalUnsecuredJWT(claims) 68 | require.NoError(t, err) 69 | 70 | vcBytes, err := decodeCredJWTUnsecured(rawJWT) 71 | require.Error(t, err) 72 | require.Contains(t, err.Error(), "decode JWT claims from payload") 73 | require.Nil(t, vcBytes) 74 | }) 75 | } 76 | -------------------------------------------------------------------------------- /verifiable/example_support_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright SecureKey Technologies Inc. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package verifiable_test 8 | 9 | import ( 10 | "encoding/base64" 11 | 12 | "github.com/trustbloc/bbs-signature-go/bbs12381g2pub" 13 | lddocloader "github.com/trustbloc/did-go/doc/ld/documentloader" 14 | ldtestutil "github.com/trustbloc/did-go/doc/ld/testutil" 15 | 16 | "github.com/trustbloc/vc-go/verifiable" 17 | ) 18 | 19 | type UniversityDegree struct { 20 | Type string `json:"type,omitempty"` 21 | University string `json:"university,omitempty"` 22 | } 23 | 24 | type UniversityDegreeSubject struct { 25 | ID string `json:"id,omitempty"` 26 | Name string `json:"name,omitempty"` 27 | Spouse string `json:"spouse,omitempty"` 28 | Degree UniversityDegree `json:"degree,omitempty"` 29 | } 30 | 31 | type UniversityDegreeCredential struct { 32 | *verifiable.Credential 33 | 34 | ReferenceNumber int `json:"referenceNumber,omitempty"` 35 | } 36 | 37 | func (udc *UniversityDegreeCredential) MarshalJSON() ([]byte, error) { 38 | raw := udc.Credential.ToRawJSON() 39 | raw["referenceNumber"] = udc.ReferenceNumber 40 | 41 | vc, err := verifiable.ParseCredentialJSON(raw, 42 | verifiable.WithCredDisableValidation(), 43 | verifiable.WithDisabledProofCheck()) 44 | if err != nil { 45 | panic(err) 46 | } 47 | 48 | return vc.MarshalJSON() 49 | } 50 | 51 | func getJSONLDDocumentLoader() *lddocloader.DocumentLoader { 52 | loader, err := ldtestutil.DocumentLoader() 53 | if err != nil { 54 | panic(err) 55 | } 56 | 57 | return loader 58 | } 59 | 60 | func loadBBSKeyPair(pubKeyB64, privKeyB64 string) (*bbs12381g2pub.PublicKey, *bbs12381g2pub.PrivateKey, error) { 61 | pubKeyBytes, err := base64.RawStdEncoding.DecodeString(pubKeyB64) 62 | if err != nil { 63 | return nil, nil, err 64 | } 65 | 66 | pubKey, err := bbs12381g2pub.UnmarshalPublicKey(pubKeyBytes) 67 | if err != nil { 68 | return nil, nil, err 69 | } 70 | 71 | privKeyBytes, err := base64.RawStdEncoding.DecodeString(privKeyB64) 72 | if err != nil { 73 | return nil, nil, err 74 | } 75 | 76 | privKey, err := bbs12381g2pub.UnmarshalPrivateKey(privKeyBytes) 77 | if err != nil { 78 | return nil, nil, err 79 | } 80 | 81 | return pubKey, privKey, nil 82 | } 83 | -------------------------------------------------------------------------------- /presexch/testdata/contexts/mdl-v1.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context":{ 3 | "@version":1.1, 4 | "id":"@id", 5 | "type":"@type", 6 | "name":"http://schema.org/name", 7 | "description":"http://schema.org/description", 8 | 9 | "mDL":{ 10 | "@id":"ex:mDL", 11 | "@context":{ 12 | "@version":1.1, 13 | "@protected":true, 14 | "id":"@id", 15 | "type":"@type", 16 | "xsd":"http://www.w3.org/2001/XMLSchema#", 17 | "ex":"https://example.org/examples#", 18 | 19 | "family_name":{ "@id":"ex:family_name" }, 20 | "given_name":{ "@id":"ex:given_name" }, 21 | "birthdate":{ "@id":"ex:birthdate" }, 22 | "issue_date":{ "@id":"ex:issue_date" }, 23 | "expiry_date":{ "@id":"ex:expiry_date" }, 24 | "issuing_country":{ "@id":"ex:issuing_country" }, 25 | "issuing_authority":{ "@id":"ex:issuing_authority" }, 26 | "document_number":{ "@id":"ex:document_number" }, 27 | "driving_privileges":{ "@id":"ex:driving_privileges" }, 28 | "portrait":{ "@id":"ex:portrait" }, 29 | "administrative_number":{ "@id":"ex:administrative_number" }, 30 | "gender":{ "@id":"ex:gender" }, 31 | "height":{ "@id":"ex:height" }, 32 | "weight":{ "@id":"ex:weight" }, 33 | "eye_color":{ "@id":"ex:eye_color" }, 34 | "hair_color":{ "@id":"ex:hair_color" }, 35 | "birthplace":{ "@id":"ex:birthplace" }, 36 | "resident_address":{ "@id":"ex:resident_address" }, 37 | "portrait_capture_date":{ "@id":"ex:portrait_capture_date" }, 38 | "age_in_years":{ "@id":"ex:age_in_years" }, 39 | "age_birth_year":{ "@id":"ex:age_birth_year" }, 40 | "issuing_jurisdiction":{ "@id":"ex:issuing_jurisdiction" }, 41 | "nationality":{ "@id":"ex:nationality" }, 42 | "resident_city":{ "@id":"ex:resident_city" }, 43 | "resident_state":{ "@id":"ex:resident_state" }, 44 | "resident_postal_code":{ "@id":"ex:resident_postal_code" }, 45 | "biometric_template_xx":{ "@id":"ex:biometric_template_xx" }, 46 | "name_nat_char":{ "@id":"ex:name_nat_char" }, 47 | "mgmt_lastupdate":{ "@id":"ex:mgmt_lastupdate" }, 48 | "mgmt_validity":{ "@id":"ex:mgmt_validity" }, 49 | "mgmt_nextupdate":{ "@id":"ex:mgmt_nextupdate" } 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /proof/jwtproofs/es256k/proof.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Gen Digital Inc. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package es256k 8 | 9 | import ( 10 | "github.com/trustbloc/kms-go/spi/kms" 11 | "github.com/veraison/go-cose" 12 | 13 | "github.com/trustbloc/vc-go/proof" 14 | ) 15 | 16 | // Proof describes ed25519 proof type. 17 | type Proof struct { 18 | supportedVMs []proof.SupportedVerificationMethod 19 | } 20 | 21 | // Constant describing es256k alg. 22 | const ( 23 | VerificationMethodType = "EcdsaSecp256k1VerificationKey2019" 24 | JWKKeyType = "EC" 25 | JWKCurve = "secp256k1" 26 | JWTAlg = "ES256K" 27 | ) 28 | 29 | // New an instance of ed25519 proof type descriptor. 30 | func New() *Proof { 31 | p := &Proof{} 32 | p.supportedVMs = []proof.SupportedVerificationMethod{ 33 | { 34 | VerificationMethodType: VerificationMethodType, 35 | KMSKeyType: kms.ECDSASecp256k1TypeIEEEP1363, 36 | JWKKeyType: JWKKeyType, 37 | JWKCurve: JWKCurve, 38 | }, 39 | { 40 | VerificationMethodType: "JsonWebKey2020", 41 | KMSKeyType: kms.ECDSASecp256k1TypeIEEEP1363, 42 | JWKKeyType: JWKKeyType, 43 | JWKCurve: JWKCurve, 44 | RequireJWK: true, 45 | }, 46 | 47 | { 48 | VerificationMethodType: VerificationMethodType, 49 | KMSKeyType: kms.ECDSASecp256k1TypeDER, 50 | JWKKeyType: JWKKeyType, 51 | JWKCurve: JWKCurve, 52 | }, 53 | { 54 | VerificationMethodType: "JsonWebKey2020", 55 | KMSKeyType: kms.ECDSASecp256k1TypeDER, 56 | JWKKeyType: JWKKeyType, 57 | JWKCurve: JWKCurve, 58 | RequireJWK: true, 59 | }, 60 | } 61 | 62 | return p 63 | } 64 | 65 | // SupportedVerificationMethods returns list of verification methods supported by this proof type. 66 | func (s *Proof) SupportedVerificationMethods() []proof.SupportedVerificationMethod { 67 | return s.supportedVMs 68 | } 69 | 70 | // JWTAlgorithm return jwt alg that corresponds to VerificationMethod. 71 | func (s *Proof) JWTAlgorithm() string { 72 | return JWTAlg 73 | } 74 | 75 | // CWTAlgorithm return cwt algorithm that corresponds to VerificationMethod. 76 | func (s *Proof) CWTAlgorithm() cose.Algorithm { 77 | return 0 78 | } 79 | -------------------------------------------------------------------------------- /sdjwt/issuer/example_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright SecureKey Technologies Inc. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package issuer 8 | 9 | import ( 10 | "crypto/ed25519" 11 | "crypto/rand" 12 | "encoding/json" 13 | "fmt" 14 | 15 | "github.com/trustbloc/vc-go/crypto-ext/testutil" 16 | ) 17 | 18 | func ExampleNew() { 19 | signer, err := setUp() 20 | if err != nil { 21 | fmt.Println("failed to set-up test: %w", err.Error()) 22 | } 23 | 24 | claims := map[string]interface{}{ 25 | "last_name": "Smith", 26 | "address": map[string]interface{}{ 27 | "street_address": "123 Main St", 28 | "country": "US", 29 | }, 30 | } 31 | 32 | // Issuer will issue SD-JWT for specified claims. Salt function is only provided to keep example outcome the same. 33 | token, err := New("https://example.com/issuer", claims, nil, signer, 34 | WithStructuredClaims(true), 35 | WithNonSelectivelyDisclosableClaims([]string{"address.country"}), 36 | WithSaltFnc(func() (string, error) { 37 | return sampleSalt, nil 38 | })) 39 | if err != nil { 40 | fmt.Println("failed to issue SD-JWT: %w", err.Error()) 41 | } 42 | 43 | var decoded map[string]interface{} 44 | 45 | err = token.DecodeClaims(&decoded) 46 | if err != nil { 47 | fmt.Println("failed to decode SD-JWT claims: %w", err.Error()) 48 | } 49 | 50 | issuerClaimsJSON, err := marshalObj(decoded) 51 | if err != nil { 52 | fmt.Println("verifier failed to marshal verified claims: %w", err.Error()) 53 | } 54 | 55 | fmt.Println(issuerClaimsJSON) 56 | 57 | // Output: { 58 | // "_sd": [ 59 | // "V9-Eiizd3iJpdlxojQuwps44Zba7z6R08S7rPCDg_wU" 60 | // ], 61 | // "_sd_alg": "sha-256", 62 | // "address": { 63 | // "_sd": [ 64 | // "tD1XVFffEo0KTGuvHn9UlXCBgt3vot5xAanqXMdvVMg" 65 | // ], 66 | // "country": "US" 67 | // }, 68 | // "iss": "https://example.com/issuer" 69 | // } 70 | } 71 | 72 | func setUp() (*testutil.Ed25519Signer, error) { 73 | _, issuerPrivateKey, err := ed25519.GenerateKey(rand.Reader) 74 | if err != nil { 75 | return nil, err 76 | } 77 | 78 | signer := testutil.NewEd25519Signer(issuerPrivateKey) 79 | 80 | return signer, nil 81 | } 82 | 83 | func marshalObj(obj interface{}) (string, error) { 84 | objBytes, err := json.Marshal(obj) 85 | if err != nil { 86 | fmt.Println("failed to marshal object: %w", err.Error()) 87 | } 88 | 89 | return prettyPrint(objBytes) 90 | } 91 | -------------------------------------------------------------------------------- /verifiable/presentation_jwt_unsecured_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright SecureKey Technologies Inc. All Rights Reserved. 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package verifiable 7 | 8 | import ( 9 | "testing" 10 | 11 | "github.com/go-jose/go-jose/v3/jwt" 12 | "github.com/stretchr/testify/require" 13 | ) 14 | 15 | func TestJWTPresClaims_MarshalUnsecuredJWT(t *testing.T) { 16 | vp, err := newTestPresentation(t, []byte(validPresentation), WithPresDisabledProofCheck()) 17 | require.NoError(t, err) 18 | 19 | jws := createCredUnsecuredJWT(t, vp) 20 | 21 | _, rawVC, err := decodeVPFromUnsecuredJWT(jws) 22 | 23 | require.NoError(t, err) 24 | require.Equal(t, vp.stringJSON(t), jsonObjectToString(t, rawVC)) 25 | } 26 | 27 | func TestDecodeVPFromUnsecuredJWT(t *testing.T) { 28 | t.Run("Successful unsecured JWT decoding", func(t *testing.T) { 29 | vp, err := newTestPresentation(t, []byte(validPresentation), WithPresDisabledProofCheck()) 30 | require.NoError(t, err) 31 | 32 | jws := createCredUnsecuredJWT(t, vp) 33 | 34 | vpDecodedBytes, vpRaw, err := decodeVPFromUnsecuredJWT(jws) 35 | require.NoError(t, err) 36 | require.NotNil(t, vpDecodedBytes) 37 | require.Equal(t, vp.stringJSON(t), jsonObjectToString(t, vpRaw)) 38 | }) 39 | 40 | t.Run("Invalid serialized unsecured JWT", func(t *testing.T) { 41 | vpBytes, vpRaw, err := decodeVPFromUnsecuredJWT("invalid JWS") 42 | require.Error(t, err) 43 | require.Contains(t, err.Error(), "decode Verifiable Presentation JWT claims") 44 | require.Nil(t, vpBytes) 45 | require.Nil(t, vpRaw) 46 | }) 47 | 48 | t.Run("Invalid format of \"vp\" claim", func(t *testing.T) { 49 | claims := &invalidPresClaims{ 50 | Claims: &jwt.Claims{}, 51 | Presentation: 55, // "vp" claim of invalid format 52 | } 53 | 54 | rawJWT, err := marshalUnsecuredJWT(claims) 55 | require.NoError(t, err) 56 | 57 | vpBytes, vpRaw, err := decodeVPFromUnsecuredJWT(rawJWT) 58 | require.Error(t, err) 59 | require.Contains(t, err.Error(), "decode Verifiable Presentation JWT claims") 60 | require.Nil(t, vpBytes) 61 | require.Nil(t, vpRaw) 62 | }) 63 | } 64 | 65 | func createCredUnsecuredJWT(t *testing.T, vp *Presentation) string { 66 | claims, err := newJWTPresClaims(vp, []string{}, false) 67 | require.NoError(t, err) 68 | require.NotNil(t, claims) 69 | 70 | unsecuredJWT, err := claims.MarshalUnsecuredJWT() 71 | require.NoError(t, err) 72 | 73 | return unsecuredJWT 74 | } 75 | -------------------------------------------------------------------------------- /presexch/testdata/nested_submission_requirements_pd.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "32f54163-7166-48f1-93d8-ff217bdb0653", 3 | "submission_requirements": [ 4 | { 5 | "name": "Nested requirements", 6 | "rule": "all", 7 | "from_nested": [ 8 | { 9 | "name": "VerifiedEmployee or Degree", 10 | "rule": "pick", 11 | "count": 1, 12 | "from": "A" 13 | }, 14 | { 15 | "name": "DriversLicense", 16 | "rule": "pick", 17 | "count": 1, 18 | "from": "B" 19 | } 20 | ] 21 | } 22 | ], 23 | "input_descriptors": [ 24 | { 25 | "id": "VerifiedEmployee", 26 | "name": "Verified Employee", 27 | "group": [ 28 | "A" 29 | ], 30 | "constraints": { 31 | "fields": [ 32 | { 33 | "path": [ 34 | "$.type", 35 | "$.vc.type" 36 | ], 37 | "filter": { 38 | "type": "array", 39 | "contains": { 40 | "type": "string", 41 | "const": "VerifiedEmployee" 42 | } 43 | } 44 | } 45 | ] 46 | } 47 | }, 48 | { 49 | "id": "DriversLicense", 50 | "name": "Driver's License", 51 | "group": [ 52 | "B" 53 | ], 54 | "constraints": { 55 | "fields": [ 56 | { 57 | "path": [ 58 | "$.type", 59 | "$.vc.type" 60 | ], 61 | "filter": { 62 | "type": "array", 63 | "contains": { 64 | "type": "string", 65 | "const": "DriversLicense" 66 | } 67 | } 68 | } 69 | ] 70 | } 71 | }, 72 | { 73 | "id": "degree", 74 | "name": "degree", 75 | "group": [ 76 | "A" 77 | ], 78 | "purpose": "We can only hire with bachelor's degree.", 79 | "constraints": { 80 | "fields": [ 81 | { 82 | "path": [ 83 | "$.credentialSubject.degree.type", 84 | "$.vc.credentialSubject.degree.type" 85 | ], 86 | "purpose": "We can only hire with bachelor's degree.", 87 | "filter": { 88 | "type": "string", 89 | "const": "BachelorDegree" 90 | } 91 | } 92 | ] 93 | } 94 | } 95 | ] 96 | } -------------------------------------------------------------------------------- /verifiable/test-suite/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "generator": "aries-framework-go-gen", 3 | "presentationGenerator": "aries-framework-go-gen --presentation", 4 | "generatorOptions": "", 5 | "sectionsNotSupported": ["zkp"], 6 | "jwt": { 7 | "es256kPrivateKeyJwk": { 8 | "kty": "EC", 9 | "kid": "did:example:0xab#verikey-1", 10 | "crv": "P-256K", 11 | "x": "7KEKZa5xJPh7WVqHJyUpb2MgEe3nA8Rk7eUlXsmBl-M", 12 | "y": "3zIgl_ml4RhapyEm5J7lvU-4f5jiBvZr4KgxUjEhl9o", 13 | "key_ops": [ 14 | "sign", 15 | "verify" 16 | ], 17 | "d": "IQkxsrZICFvhYEe6ft3wk-LISUUkcFj5uScVQAUGizo" 18 | }, 19 | "rs256PrivateKeyJwk": { 20 | "kty": "RSA", 21 | "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", 22 | "e": "AQAB", 23 | "d": "X4cTteJY_gn4FYPsXB8rdXix5vwsg1FLN5E3EaG6RJoVH-HLLKD9M7dx5oo7GURknchnrRweUkC7hT5fJLM0WbFAKNLWY2vv7B6NqXSzUvxT0_YSfqijwp3RTzlBaCxWp4doFk5N2o8Gy_nHNKroADIkJ46pRUohsXywbReAdYaMwFs9tv8d_cPVY3i07a3t8MN6TNwm0dSawm9v47UiCl3Sk5ZiG7xojPLu4sbg1U2jx4IBTNBznbJSzFHK66jT8bgkuqsk0GjskDJk19Z4qwjwbsnn4j2WBii3RL-Us2lGVkY8fkFzme1z0HbIkfz0Y6mqnOYtqc0X4jfcKoAC8Q", 24 | "p": "83i-7IvMGXoMXCskv73TKr8637FiO7Z27zv8oj6pbWUQyLPQBQxtPVnwD20R-60eTDmD2ujnMt5PoqMrm8RfmNhVWDtjjMmCMjOpSXicFHj7XOuVIYQyqVWlWEh6dN36GVZYk93N8Bc9vY41xy8B9RzzOGVQzXvNEvn7O0nVbfs", 25 | "q": "3dfOR9cuYq-0S-mkFLzgItgMEfFzB2q3hWehMuG0oCuqnb3vobLyumqjVZQO1dIrdwgTnCdpYzBcOfW5r370AFXjiWft_NGEiovonizhKpo9VVS78TzFgxkIdrecRezsZ-1kYd_s1qDbxtkDEgfAITAG9LUnADun4vIcb6yelxk", 26 | "dp": "G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oimYwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_NmtuYZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0", 27 | "dq": "s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUUvMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk", 28 | "qi": "GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzgUIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rxyR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU", 29 | "alg": "RS256", 30 | "kid": "did:example:0xab#verikey-1" 31 | }, 32 | "aud": "did:example:0xcafe" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /dataintegrity/models/models.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Gen Digital Inc. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package models 8 | 9 | import ( 10 | "time" 11 | 12 | "github.com/trustbloc/did-go/doc/did" 13 | ) 14 | 15 | const ( 16 | // DataIntegrityProof is the type property on proofs created using data 17 | // integrity cryptographic suites. 18 | DataIntegrityProof = "DataIntegrityProof" 19 | ) 20 | 21 | // KeyManager manages keys and their storage for the aries framework. 22 | type KeyManager interface { // TODO note: only used by deprecated function 23 | // Get key handle for the given keyID 24 | // Returns: 25 | // - handle instance (to private key) 26 | // - error if failure 27 | Get(keyID string) (interface{}, error) 28 | } 29 | 30 | // VerificationMethod implements the data integrity verification method model: 31 | // https://www.w3.org/TR/vc-data-integrity/#verification-methods 32 | type VerificationMethod = did.VerificationMethod 33 | 34 | // Proof implements the data integrity proof model: 35 | // https://www.w3.org/TR/vc-data-integrity/#proofs 36 | type Proof struct { 37 | ID string `json:"id,omitempty"` 38 | Type string `json:"type"` 39 | CryptoSuite string `json:"cryptosuite,omitempty"` 40 | ProofPurpose string `json:"proofPurpose"` 41 | VerificationMethod string `json:"verificationMethod"` 42 | Created string `json:"created,omitempty"` 43 | Expires string `json:"expires,omitempty"` 44 | Domain string `json:"domain,omitempty"` 45 | Challenge string `json:"challenge,omitempty"` 46 | ProofValue string `json:"proofValue"` 47 | PreviousProof string `json:"previousProof,omitempty"` 48 | } 49 | 50 | // ProofOptions provides options for signing or verifying a data integrity proof. 51 | type ProofOptions struct { 52 | Purpose string 53 | VerificationMethodID string 54 | VerificationMethod *VerificationMethod 55 | ProofType string 56 | SuiteType string 57 | Domain string 58 | Challenge string 59 | Created time.Time 60 | Expires time.Time // During verification process the value must be taken from Proof.Expires. 61 | CustomFields map[string]interface{} 62 | } 63 | 64 | // DateTimeFormat is the date-time format used by the data integrity 65 | // specification, which matches RFC3339. 66 | // https://www.w3.org/TR/xmlschema11-2/#dateTime 67 | const DateTimeFormat = time.RFC3339 68 | -------------------------------------------------------------------------------- /verifiable/jsonld.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright SecureKey Technologies Inc. All Rights Reserved. 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package verifiable 7 | 8 | import ( 9 | "errors" 10 | "fmt" 11 | ) 12 | 13 | const ( 14 | // V1ContextURI is the required JSON-LD context for VCs and VPs. 15 | V1ContextURI = "https://www.w3.org/2018/credentials/v1" 16 | // V1ContextID is the non-fragment part of the JSON-LD schema ID for VCs and VPs. 17 | V1ContextID = "https://www.w3.org/2018/credentials" 18 | 19 | V2ContextURI = "https://www.w3.org/ns/credentials/v2" 20 | V2ContextID = "https://www.w3.org/ns/credentials" 21 | ) 22 | 23 | // GetBaseContext gets the base context from the contexts. 24 | // The base context is the first element in the array and must be one of: 25 | // - https://www.w3.org/2018/credentials/v1 26 | // - https://www.w3.org/ns/credentials/v2 27 | func GetBaseContext(contexts []string) (string, error) { 28 | if len(contexts) == 0 { 29 | return "", errors.New("@context is required") 30 | } 31 | 32 | ctx := contexts[0] 33 | 34 | if ctx == V1ContextURI || ctx == V2ContextURI { 35 | return ctx, nil 36 | } 37 | 38 | return "", fmt.Errorf("unsupported @context: %s", ctx) 39 | } 40 | 41 | // GetBaseContextFromRawDocument gets the base context from the raw document. 42 | // The @context is either a string or array of strings. If it's given as an array 43 | // then the base context is the first element in the @context array and must be one of: 44 | // 45 | // - https://www.w3.org/2018/credentials/v1 46 | // - https://www.w3.org/ns/credentials/v2 47 | func GetBaseContextFromRawDocument(doc map[string]interface{}) (string, error) { 48 | ctx, ok := doc[jsonFldContext] 49 | if !ok { 50 | return "", fmt.Errorf("%s is required", jsonFldContext) 51 | } 52 | 53 | baseContext, _, err := decodeContext(ctx) 54 | if err != nil { 55 | return "", err 56 | } 57 | 58 | return GetBaseContext(baseContext) 59 | } 60 | 61 | // IsBaseContext returns true if the given context is the base context. 62 | func IsBaseContext(contexts []string, ctx string) bool { 63 | if len(contexts) == 0 { 64 | return false 65 | } 66 | 67 | return contexts[0] == ctx 68 | } 69 | 70 | // HasBaseContext returns true if the given document has the given base context. 71 | func HasBaseContext(doc map[string]interface{}, ctx string) bool { 72 | rawContext, ok := doc[jsonFldContext] 73 | if !ok { 74 | return false 75 | } 76 | 77 | contexts, _, err := decodeContext(rawContext) 78 | if err != nil { 79 | return false 80 | } 81 | 82 | return IsBaseContext(contexts, ctx) 83 | } 84 | -------------------------------------------------------------------------------- /proof/defaults/all.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Gen Digital Inc. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package defaults 8 | 9 | import ( 10 | "github.com/trustbloc/vc-go/crypto-ext/verifiers/bbs" 11 | "github.com/trustbloc/vc-go/crypto-ext/verifiers/ecdsa" 12 | "github.com/trustbloc/vc-go/crypto-ext/verifiers/ed25519" 13 | "github.com/trustbloc/vc-go/crypto-ext/verifiers/rsa" 14 | proofdesc "github.com/trustbloc/vc-go/proof" 15 | "github.com/trustbloc/vc-go/proof/checker" 16 | "github.com/trustbloc/vc-go/proof/jwtproofs/eddsa" 17 | "github.com/trustbloc/vc-go/proof/jwtproofs/es256" 18 | "github.com/trustbloc/vc-go/proof/jwtproofs/es256k" 19 | "github.com/trustbloc/vc-go/proof/jwtproofs/es384" 20 | "github.com/trustbloc/vc-go/proof/jwtproofs/es521" 21 | "github.com/trustbloc/vc-go/proof/jwtproofs/ps256" 22 | "github.com/trustbloc/vc-go/proof/jwtproofs/rs256" 23 | "github.com/trustbloc/vc-go/proof/ldproofs/bbsblssignature2020" 24 | "github.com/trustbloc/vc-go/proof/ldproofs/bbsblssignatureproof2020" 25 | "github.com/trustbloc/vc-go/proof/ldproofs/ecdsasecp256k1signature2019" 26 | "github.com/trustbloc/vc-go/proof/ldproofs/ed25519signature2018" 27 | "github.com/trustbloc/vc-go/proof/ldproofs/ed25519signature2020" 28 | "github.com/trustbloc/vc-go/proof/ldproofs/jsonwebsignature2020" 29 | "github.com/trustbloc/vc-go/vermethod" 30 | ) 31 | 32 | type verificationMethodResolver interface { 33 | ResolveVerificationMethod(verificationMethod string, expectedProofIssuer string) (*vermethod.VerificationMethod, error) 34 | } 35 | 36 | // NewDefaultProofChecker creates proof checker with all available validation algorithms. 37 | func NewDefaultProofChecker(verificationMethodResolver verificationMethodResolver) *checker.ProofChecker { 38 | jwtCheckers := []proofdesc.JWTProofDescriptor{ 39 | eddsa.New(), es256.New(), es256k.New(), es384.New(), es521.New(), rs256.New(), ps256.New(), 40 | } 41 | 42 | return checker.New(verificationMethodResolver, 43 | checker.WithSignatureVerifiers(ed25519.New(), bbs.NewBBSG2SignatureVerifier(), 44 | rsa.NewPS256(), rsa.NewRS256(), 45 | ecdsa.NewSecp256k1(), ecdsa.NewES256(), ecdsa.NewES384(), ecdsa.NewES521()), 46 | checker.WithLDProofTypes( 47 | bbsblssignature2020.New(), 48 | ecdsasecp256k1signature2019.New(), 49 | ed25519signature2018.New(), 50 | ed25519signature2020.New(), 51 | jsonwebsignature2020.New(), 52 | ), 53 | checker.WithLDProofTypeEx(bbsblssignatureproof2020.New(), bbs.NewBBSG2SignatureProofVerifier()), 54 | checker.WithJWTAlg(jwtCheckers...), 55 | checker.WithCWTAlg(jwtCheckers...), 56 | ) 57 | } 58 | -------------------------------------------------------------------------------- /verifiable/cwt/cbor.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Gen Digital Inc. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package cwt 8 | 9 | import ( 10 | "errors" 11 | "io" 12 | 13 | "github.com/fxamacker/cbor/v2" 14 | ) 15 | 16 | // Pre-configured modes for CBOR encoding and decoding. 17 | var ( 18 | encMode cbor.EncMode //nolint:gochecknoglobals 19 | decModeWithTagsForbidden cbor.DecMode //nolint:gochecknoglobals 20 | ) 21 | 22 | func init() { // nolint:gochecknoinits 23 | var err error 24 | 25 | // init encode mode 26 | encOpts := cbor.EncOptions{ 27 | Sort: cbor.SortCoreDeterministic, // sort map keys 28 | IndefLength: cbor.IndefLengthForbidden, // no streaming 29 | } 30 | 31 | encMode, err = encOpts.EncMode() 32 | if err != nil { 33 | panic(err) 34 | } 35 | 36 | // init decode mode 37 | decOpts := cbor.DecOptions{ 38 | DupMapKey: cbor.DupMapKeyEnforcedAPF, // duplicated key not allowed 39 | IndefLength: cbor.IndefLengthForbidden, // no streaming 40 | IntDec: cbor.IntDecConvertSigned, // decode CBOR uint/int to Go int64 41 | } 42 | decOpts.TagsMd = cbor.TagsForbidden 43 | decModeWithTagsForbidden, err = decOpts.DecMode() 44 | 45 | if err != nil { 46 | panic(err) 47 | } 48 | } 49 | 50 | // deterministicBinaryString converts a bstr into the deterministic encoding. 51 | // 52 | // Reference: https://www.rfc-editor.org/rfc/rfc9052.html#section-9 53 | func deterministicBinaryString(data cbor.RawMessage) (cbor.RawMessage, error) { //nolint:gocyclo 54 | if len(data) == 0 { 55 | return nil, io.EOF 56 | } 57 | 58 | if data[0]>>5 != 2 { // major type 2: bstr 59 | return nil, errors.New("cbor: require bstr type") 60 | } 61 | 62 | // fast path: return immediately if bstr is already deterministic 63 | if err := decModeWithTagsForbidden.Valid(data); err != nil { 64 | return nil, err 65 | } 66 | 67 | ai := data[0] & 0x1f 68 | if ai < 24 { 69 | return data, nil 70 | } 71 | 72 | switch ai { 73 | case 24: 74 | if data[1] >= 24 { 75 | return data, nil 76 | } 77 | case 25: 78 | if data[1] != 0 { 79 | return data, nil 80 | } 81 | case 26: 82 | if data[1] != 0 || data[2] != 0 { 83 | return data, nil 84 | } 85 | case 27: 86 | if data[1] != 0 || data[2] != 0 || data[3] != 0 || data[4] != 0 { 87 | return data, nil 88 | } 89 | } 90 | 91 | // slow path: convert by re-encoding 92 | // error checking is not required since `data` has been validataed 93 | var s []byte 94 | 95 | err := decModeWithTagsForbidden.Unmarshal(data, &s) 96 | if err != nil { 97 | return nil, err 98 | } 99 | 100 | return encMode.Marshal(s) 101 | } 102 | -------------------------------------------------------------------------------- /status/internal/bitstring/bitstring.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Avast Software. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | // Package bitstring provides functions for operating on byte slices as if they are 0-indexed arrays of bits, 8 | // packed 8 bits to a byte, LSB-first. 9 | package bitstring 10 | 11 | import ( 12 | "bytes" 13 | "compress/gzip" 14 | "encoding/base64" 15 | "errors" 16 | "fmt" 17 | 18 | "github.com/multiformats/go-multibase" 19 | ) 20 | 21 | const ( 22 | bitsPerByte = 8 23 | one = 0x1 24 | ) 25 | 26 | // Decode decodes a compressed bitstring from a base64URL-encoded string. 27 | func Decode(src string, opts ...Opt) ([]byte, error) { 28 | options := &options{} 29 | 30 | for _, opt := range opts { 31 | opt(options) 32 | } 33 | 34 | var decodedBits []byte 35 | 36 | var err error 37 | 38 | if options.multiBaseEncoding { 39 | _, decodedBits, err = multibase.Decode(src) 40 | if err != nil { 41 | return nil, fmt.Errorf("decode: %w", err) 42 | } 43 | } else { 44 | decodedBits, err = base64.RawURLEncoding.DecodeString(src) 45 | if err != nil { 46 | return nil, err 47 | } 48 | } 49 | 50 | b := bytes.NewReader(decodedBits) 51 | 52 | zipReader, err := gzip.NewReader(b) 53 | if err != nil { 54 | return nil, err 55 | } 56 | 57 | buf := new(bytes.Buffer) 58 | if _, err := buf.ReadFrom(zipReader); err != nil { 59 | return nil, err 60 | } 61 | 62 | return buf.Bytes(), nil 63 | } 64 | 65 | // BitAt returns the bit in the idx'th position (zero-indexed) in the given bitstring. 66 | func BitAt(bitString []byte, idx int) (bool, error) { 67 | nByte := idx / bitsPerByte 68 | nBit := idx % bitsPerByte 69 | 70 | if idx < 0 || nByte >= len(bitString) { 71 | return false, errors.New("position is invalid") 72 | } 73 | 74 | bitValue := (bitString[nByte] & (one << nBit)) != 0 75 | 76 | return bitValue, nil 77 | } 78 | 79 | // Encode gzips a bitstring and encodes it as a raw urlsafe base-64 string. 80 | func Encode(bitString []byte) (string, error) { 81 | var buf bytes.Buffer 82 | 83 | w := gzip.NewWriter(&buf) 84 | if _, err := w.Write(bitString); err != nil { 85 | return "", err 86 | } 87 | 88 | if err := w.Close(); err != nil { 89 | return "", err 90 | } 91 | 92 | return base64.RawURLEncoding.EncodeToString(buf.Bytes()), nil 93 | } 94 | 95 | type Opt func(*options) 96 | 97 | type options struct { 98 | multiBaseEncoding bool 99 | } 100 | 101 | // WithMultiBaseEncoding sets support of multiBase encoding. 102 | func WithMultiBaseEncoding(multiBaseEncoding bool) Opt { 103 | return func(options *options) { 104 | options.multiBaseEncoding = multiBaseEncoding 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /scripts/check_license.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright IBM Corp, SecureKey Technologies Inc. All Rights Reserved. 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | # 7 | 8 | echo "Running $0" 9 | 10 | function filterExcludedFiles { 11 | CHECK=`echo "$CHECK" | grep -v .png$ | grep -v .rst$ | grep -v ^.git/ \ 12 | | grep -v .pem$ | grep -v .block$ | grep -v .tx$ | grep -v ^LICENSE$ | grep -v _sk$ \ 13 | | grep -v .key$ | grep -v .crt$ | grep -v \\.gen.go$ | grep -v \\.json$ | grep -v Gopkg.lock$ \ 14 | | grep -v .md$ | grep -v ^vendor/ | grep -v ^build/ | grep -v .pb.go$ | grep -v ci.properties$ \ 15 | | grep -v go.sum$ | grep -v gomocks | grep -v \\.jsonld$ | grep -v testdata/ | grep -v third_party/ | sort -u` 16 | } 17 | 18 | CHECK=$(git diff --name-only --diff-filter=ACMRTUXB HEAD) 19 | REMOTE_REF=$(git log -1 --pretty=format:"%d" | grep '[(].*\/' | wc -l) 20 | 21 | # If CHECK is empty then there is no working directory changes: fallback to last two commits. 22 | # Else if REMOTE_REF=0 then working copy commits are even with remote: only use the working copy changes. 23 | # Otherwise assume that the change is amending the previous commit: use both last two commit and working copy changes. 24 | if [[ -z "${CHECK}" ]] || [[ "${REMOTE_REF}" -eq 0 ]]; then 25 | if [[ ! -z "${CHECK}" ]]; then 26 | echo "Examining last commit and working directory changes" 27 | CHECK+=$'\n' 28 | else 29 | echo "Examining last commit changes" 30 | fi 31 | 32 | LAST_COMMITS=($(git log -2 --pretty=format:"%h")) 33 | CHECK+=$(git diff-tree --no-commit-id --name-only --diff-filter=ACMRTUXB -r ${LAST_COMMITS[1]} ${LAST_COMMITS[0]}) 34 | else 35 | echo "Examining working directory changes" 36 | fi 37 | 38 | filterExcludedFiles 39 | 40 | if [[ -z "$CHECK" ]]; then 41 | echo "All files are excluded from having license headers" 42 | exit 0 43 | fi 44 | 45 | missing=`echo "$CHECK" | xargs ls -d 2>/dev/null | xargs grep -L "SPDX-License-Identifier"` 46 | if [[ -z "$missing" ]]; then 47 | echo "All files have SPDX-License-Identifier headers" 48 | exit 0 49 | fi 50 | echo "The following files are missing SPDX-License-Identifier headers:" 51 | echo "$missing" 52 | echo 53 | echo "Please replace the Apache license header comment text with:" 54 | echo "SPDX-License-Identifier: Apache-2.0" 55 | 56 | echo 57 | echo "Checking committed files for traditional Apache License headers ..." 58 | missing=`echo "$missing" | xargs ls -d 2>/dev/null | xargs grep -L "http://www.apache.org/licenses/LICENSE-2.0"` 59 | if [[ -z "$missing" ]]; then 60 | echo "All remaining files have Apache 2.0 headers" 61 | exit 0 62 | fi 63 | echo "The following files are missing traditional Apache 2.0 headers:" 64 | echo "$missing" 65 | echo "Fatal Error - All files must have a license header" 66 | exit 1 -------------------------------------------------------------------------------- /dataintegrity/suite/suite.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Gen Digital Inc. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package suite 8 | 9 | import ( 10 | "errors" 11 | 12 | "github.com/trustbloc/vc-go/dataintegrity/models" 13 | ) 14 | 15 | // RequiresCreated specifies that a data integrity suite implementation, must 16 | // provide a method that tells the caller whether this suite requires the 17 | // proof.Created field to exist. 18 | type RequiresCreated interface { 19 | RequiresCreated() bool 20 | } 21 | 22 | // Signer is an implementation of a data integrity cryptographic suite that 23 | // provides the transform, hash, and proof generation steps of the data 24 | // integrity Add Proof algorithm. 25 | type Signer interface { 26 | // CreateProof performs data integrity proof creation steps: transform, hash, 27 | // and proof generation, using this implementation's cryptographic suite. 28 | CreateProof(doc []byte, opts *models.ProofOptions) (*models.Proof, error) 29 | RequiresCreated 30 | } 31 | 32 | // Verifier is an implementation of a data integrity cryptographic suite that 33 | // provides the transform, hash, and proof verification steps of the data 34 | // integrity verify Proof algorithm. 35 | type Verifier interface { 36 | // VerifyProof performs data integrity proof verification steps: transform, 37 | // hash, and proof verification, using this implementation's cryptographic 38 | // suite. 39 | VerifyProof(doc []byte, proof *models.Proof, opts *models.ProofOptions) error 40 | RequiresCreated 41 | } 42 | 43 | // Suite implements a data integrity cryptographic suite for both proof creation 44 | // and proof verification. 45 | type Suite interface { 46 | Signer 47 | Verifier 48 | } 49 | 50 | // Type provides a method that returns the cryptographic suite type of the 51 | // corresponding suite. Each suite has a type constant that's defined in its 52 | // associated specification. 53 | type Type interface { 54 | Type() []string 55 | } 56 | 57 | // SignerInitializer initializes a Signer, using initialization options that 58 | // were passed into the SignerInitializer's creation. 59 | type SignerInitializer interface { 60 | Signer() (Signer, error) 61 | Type 62 | } 63 | 64 | // VerifierInitializer initializes a Verifier, using initialization options that 65 | // were passed into the VerifierInitializer's creation. 66 | type VerifierInitializer interface { 67 | Verifier() (Verifier, error) 68 | Type 69 | } 70 | 71 | var ( 72 | // ErrInvalidProof is returned by Verifier.VerifyProof when the given proof is 73 | // invalid. 74 | ErrInvalidProof = errors.New("data integrity proof invalid") 75 | // ErrProofTransformation is returned by Signer.CreateProof and 76 | // Verifier.VerifyProof when proof transformation fails. 77 | ErrProofTransformation = errors.New("error in data integrity proof transformation") 78 | ) 79 | -------------------------------------------------------------------------------- /proof/ldproofs/ed25519signature2018/proof.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright SecureKey Technologies Inc. All Rights Reserved. 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | // Package ed25519signature2018 implements the Ed25519Signature2018 signature suite 7 | // for the Linked Data Signatures [LD-SIGNATURES] specification. 8 | // It uses the RDF Dataset Normalization Algorithm [RDF-DATASET-NORMALIZATION] 9 | // to transform the input document into its canonical form. 10 | // It uses SHA-256 [RFC6234] as the message digest algorithm and 11 | // Ed25519 [ED25519] as the signature algorithm. 12 | package ed25519signature2018 13 | 14 | import ( 15 | "crypto/sha256" 16 | 17 | "github.com/trustbloc/did-go/doc/ld/processor" 18 | "github.com/trustbloc/kms-go/spi/kms" 19 | 20 | "github.com/trustbloc/vc-go/proof" 21 | ) 22 | 23 | // Proof describes ed25519 proof type. 24 | type Proof struct { 25 | jsonldProcessor *processor.Processor 26 | supportedVMs []proof.SupportedVerificationMethod 27 | } 28 | 29 | const ( 30 | // ProofType or ed25519signature2018. 31 | ProofType = "Ed25519Signature2018" 32 | // VerificationMethodType or ed25519signature2018. 33 | VerificationMethodType = "Ed25519VerificationKey2018" 34 | // JWKKeyType or ed25519signature2018. 35 | JWKKeyType = "OKP" 36 | // JWKCurve or ed25519signature2018. 37 | JWKCurve = "Ed25519" 38 | rdfDataSetAlg = "URDNA2015" 39 | ) 40 | 41 | // New an instance of ed25519 proof type descriptor. 42 | func New() *Proof { 43 | p := &Proof{jsonldProcessor: processor.NewProcessor(rdfDataSetAlg)} 44 | p.supportedVMs = []proof.SupportedVerificationMethod{ 45 | { 46 | VerificationMethodType: VerificationMethodType, 47 | KMSKeyType: kms.ED25519Type, 48 | JWKKeyType: JWKKeyType, 49 | JWKCurve: JWKCurve, 50 | }, 51 | { 52 | VerificationMethodType: "JsonWebKey2020", 53 | KMSKeyType: kms.ED25519Type, 54 | JWKKeyType: JWKKeyType, 55 | JWKCurve: JWKCurve, 56 | RequireJWK: true, 57 | }, 58 | } 59 | 60 | return p 61 | } 62 | 63 | // ProofType return proof type name. 64 | func (s *Proof) ProofType() string { 65 | return ProofType 66 | } 67 | 68 | // SupportedVerificationMethods returns list of verification methods supported by this proof type. 69 | func (s *Proof) SupportedVerificationMethods() []proof.SupportedVerificationMethod { 70 | return s.supportedVMs 71 | } 72 | 73 | // GetCanonicalDocument will return normalized/canonical version of the document 74 | // Ed25519Signature2018 signature SignatureSuite uses RDF Dataset Normalization as canonicalization algorithm. 75 | func (s *Proof) GetCanonicalDocument(doc map[string]interface{}, opts ...processor.Opts) ([]byte, error) { 76 | return s.jsonldProcessor.GetCanonicalDocument(doc, opts...) 77 | } 78 | 79 | // GetDigest returns document digest. 80 | func (s *Proof) GetDigest(doc []byte) []byte { 81 | digest := sha256.Sum256(doc) 82 | return digest[:] 83 | } 84 | -------------------------------------------------------------------------------- /sdjwt/verifier/example_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright SecureKey Technologies Inc. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package verifier 8 | 9 | import ( 10 | "crypto/ed25519" 11 | "crypto/rand" 12 | "encoding/json" 13 | "fmt" 14 | 15 | "github.com/trustbloc/vc-go/crypto-ext/testutil" 16 | "github.com/trustbloc/vc-go/proof/checker" 17 | "github.com/trustbloc/vc-go/proof/testsupport" 18 | "github.com/trustbloc/vc-go/sdjwt/common" 19 | "github.com/trustbloc/vc-go/sdjwt/holder" 20 | "github.com/trustbloc/vc-go/sdjwt/issuer" 21 | ) 22 | 23 | func ExampleParse() { 24 | signer, signatureVerifier, err := setUp() 25 | if err != nil { 26 | fmt.Println("failed to set-up test: %w", err.Error()) 27 | } 28 | 29 | claims := map[string]interface{}{ 30 | "given_name": "Albert", 31 | "last_name": "Smith", 32 | } 33 | 34 | // Issuer will issue SD-JWT for specified claims. 35 | token, err := issuer.New(testIssuer, claims, nil, signer) 36 | if err != nil { 37 | fmt.Println("failed to issue SD-JWT: %w", err.Error()) 38 | } 39 | 40 | combinedFormatForIssuance, err := token.Serialize(false) 41 | if err != nil { 42 | fmt.Println("failed to issue SD-JWT: %w", err.Error()) 43 | } 44 | 45 | // Holder will parse combined format for issuance for verification purposes. 46 | _, err = holder.Parse(combinedFormatForIssuance, holder.WithSignatureVerifier(signatureVerifier)) 47 | if err != nil { 48 | fmt.Println("holder failed to parse SD-JWT: %w", err.Error()) 49 | } 50 | 51 | // The Holder will disclose all claims. 52 | combinedFormatForPresentation := combinedFormatForIssuance + common.CombinedFormatSeparator 53 | 54 | // Verifier will validate combined format for presentation and create verified claims. 55 | verifiedClaims, err := Parse(combinedFormatForPresentation, 56 | WithSignatureVerifier(signatureVerifier)) 57 | if err != nil { 58 | fmt.Println("verifier failed to parse holder presentation: %w", err.Error()) 59 | } 60 | 61 | verifiedClaimsJSON, err := marshalObj(verifiedClaims) 62 | if err != nil { 63 | fmt.Println("verifier failed to marshal verified claims: %w", err.Error()) 64 | } 65 | 66 | fmt.Println(verifiedClaimsJSON) 67 | 68 | // Output: { 69 | // "given_name": "Albert", 70 | // "iss": "https://example.com/issuer", 71 | // "last_name": "Smith" 72 | // } 73 | } 74 | 75 | func setUp() (*testutil.Ed25519Signer, *checker.EmbeddedVMProofChecker, error) { 76 | issuerPublicKey, issuerPrivateKey, err := ed25519.GenerateKey(rand.Reader) 77 | if err != nil { 78 | return nil, nil, err 79 | } 80 | 81 | signer := testutil.NewEd25519Signer(issuerPrivateKey) 82 | 83 | signatureVerifier := testsupport.NewEd25519Verifier(issuerPublicKey) 84 | 85 | return signer, signatureVerifier, nil 86 | } 87 | 88 | func marshalObj(obj interface{}) (string, error) { 89 | objBytes, err := json.Marshal(obj) 90 | if err != nil { 91 | fmt.Println("failed to marshal object: %w", err.Error()) 92 | } 93 | 94 | return prettyPrint(objBytes) 95 | } 96 | -------------------------------------------------------------------------------- /proof/ldproofs/bbsblssignatureproof2020/proof.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright SecureKey Technologies Inc. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package bbsblssignatureproof2020 8 | 9 | // Package bbsblssignatureproof2020 implements the BBS+ Signature Proof Suite 2020 signature suite 10 | // (https://w3c-ccg.github.io/ldp-bbs2020) in conjunction with the signing and verification algorithms of the 11 | // Linked Data Proofs. 12 | // It uses the RDF Dataset Normalization Algorithm to transform the input document into its canonical form. 13 | // It uses SHA-256 [RFC6234] as the statement digest algorithm. 14 | // It uses BBS+ signature algorithm (https://mattrglobal.github.io/bbs-signatures-spec/). 15 | // It uses BLS12-381 pairing-friendly curve (https://tools.ietf.org/html/draft-irtf-cfrg-pairing-friendly-curves-03). 16 | 17 | import ( 18 | "strings" 19 | 20 | "github.com/trustbloc/did-go/doc/ld/processor" 21 | "github.com/trustbloc/kms-go/spi/kms" 22 | 23 | "github.com/trustbloc/vc-go/proof" 24 | ) 25 | 26 | // Proof implements BbsBlsSignatureProof2020 signature suite. 27 | type Proof struct { 28 | jsonldProcessor *processor.Processor 29 | supportedVMs []proof.SupportedVerificationMethod 30 | } 31 | 32 | const ( 33 | // ProofType for bbsblssignatureproof2020. 34 | ProofType = "BbsBlsSignatureProof2020" 35 | // VerificationMethodType for bbsblssignatureproof2020. 36 | VerificationMethodType = "Bls12381G2Key2020" 37 | rdfDataSetAlg = "URDNA2015" 38 | ) 39 | 40 | // New an instance of Linked Data Signatures for the suite. 41 | func New() *Proof { 42 | p := &Proof{jsonldProcessor: processor.NewProcessor(rdfDataSetAlg)} 43 | p.supportedVMs = []proof.SupportedVerificationMethod{ 44 | { 45 | VerificationMethodType: VerificationMethodType, 46 | KMSKeyType: kms.BLS12381G2Type, 47 | }, 48 | } 49 | 50 | return p 51 | } 52 | 53 | // ProofType return proof type name. 54 | func (s *Proof) ProofType() string { 55 | return ProofType 56 | } 57 | 58 | // SupportedVerificationMethods returns list of verification methods supported by this proof type. 59 | func (s *Proof) SupportedVerificationMethods() []proof.SupportedVerificationMethod { 60 | return s.supportedVMs 61 | } 62 | 63 | // GetCanonicalDocument will return normalized/canonical version of the document. 64 | // BbsBlsSignatureProof2020 signature suite uses RDF Dataset Normalization as canonicalization algorithm. 65 | func (s *Proof) GetCanonicalDocument(doc map[string]interface{}, opts ...processor.Opts) ([]byte, error) { 66 | if v, ok := doc["type"]; ok { 67 | docType, ok := v.(string) 68 | 69 | if ok && strings.HasSuffix(docType, ProofType) { 70 | docType = strings.Replace(docType, ProofType, "BbsBlsSignature2020", 1) 71 | doc["type"] = docType 72 | } 73 | } 74 | 75 | return s.jsonldProcessor.GetCanonicalDocument(doc, opts...) 76 | } 77 | 78 | // GetDigest returns the doc itself as we would process N-Quads statements as messages to be signed/verified. 79 | func (s *Proof) GetDigest(doc []byte) []byte { 80 | return doc 81 | } 82 | -------------------------------------------------------------------------------- /proof/ldproofs/bbsblssignature2020/proof.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright SecureKey Technologies Inc. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package bbsblssignature2020 8 | 9 | // Package bbsblssignature2020 implements the BBS+ Signature Suite 2020 signature suite 10 | // (https://w3c-ccg.github.io/ldp-bbs2020) in conjunction with the signing and verification algorithms of the 11 | // Linked Data Proofs. 12 | // It uses the RDF Dataset Normalization Algorithm to transform the input document into its canonical form. 13 | // It uses SHA-256 [RFC6234] as the statement digest algorithm. 14 | // It uses BBS+ signature algorithm (https://mattrglobal.github.io/bbs-signatures-spec/). 15 | // It uses BLS12-381 pairing-friendly curve (https://tools.ietf.org/html/draft-irtf-cfrg-pairing-friendly-curves-03). 16 | 17 | import ( 18 | "github.com/trustbloc/did-go/doc/ld/processor" 19 | "github.com/trustbloc/kms-go/spi/kms" 20 | 21 | "github.com/trustbloc/vc-go/proof" 22 | ) 23 | 24 | // Proof implements BbsBlsSignature2020 signature suite. 25 | type Proof struct { 26 | jsonldProcessor *processor.Processor 27 | supportedVMs []proof.SupportedVerificationMethod 28 | } 29 | 30 | const ( 31 | // ProofType is the BbsBlsSignature2020 type string. 32 | ProofType = "BbsBlsSignature2020" 33 | // VerificationMethodType for bbsblssignature2020. 34 | VerificationMethodType = "Bls12381G2Key2020" 35 | // JWKKeyType for bbsblssignature2020. 36 | JWKKeyType = "EC" 37 | // JWKCurve for bbsblssignature2020. 38 | JWKCurve = "BLS12381_G2" 39 | rdfDataSetAlg = "URDNA2015" 40 | ) 41 | 42 | // New an instance of BbsBlsSignature2020 proof descriptor. 43 | func New() *Proof { 44 | p := &Proof{jsonldProcessor: processor.NewProcessor(rdfDataSetAlg)} 45 | p.supportedVMs = []proof.SupportedVerificationMethod{ 46 | { 47 | VerificationMethodType: VerificationMethodType, 48 | KMSKeyType: kms.BLS12381G2Type, 49 | JWKKeyType: JWKKeyType, 50 | JWKCurve: JWKCurve, 51 | }, 52 | { 53 | VerificationMethodType: "JsonWebKey2020", 54 | KMSKeyType: kms.BLS12381G2Type, 55 | JWKKeyType: JWKKeyType, 56 | JWKCurve: JWKCurve, 57 | RequireJWK: true, 58 | }, 59 | } 60 | 61 | return p 62 | } 63 | 64 | // ProofType return proof type name. 65 | func (s *Proof) ProofType() string { 66 | return ProofType 67 | } 68 | 69 | // SupportedVerificationMethods returns list of verification methods supported by this proof type. 70 | func (s *Proof) SupportedVerificationMethods() []proof.SupportedVerificationMethod { 71 | return s.supportedVMs 72 | } 73 | 74 | // GetCanonicalDocument will return normalized/canonical version of the document. 75 | // BbsBlsSignature2020 signature suite uses RDF Dataset Normalization as canonicalization algorithm. 76 | func (s *Proof) GetCanonicalDocument(doc map[string]interface{}, opts ...processor.Opts) ([]byte, error) { 77 | return s.jsonldProcessor.GetCanonicalDocument(doc, opts...) 78 | } 79 | 80 | // GetDigest returns the doc itself as we would process N-Quads statements as messages to be signed/verified. 81 | func (s *Proof) GetDigest(doc []byte) []byte { 82 | return doc 83 | } 84 | -------------------------------------------------------------------------------- /verifiable/embedded_proof.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright SecureKey Technologies Inc. All Rights Reserved. 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package verifiable 7 | 8 | import ( 9 | "encoding/json" 10 | "errors" 11 | "fmt" 12 | 13 | jsonld "github.com/trustbloc/did-go/doc/ld/processor" 14 | 15 | "github.com/trustbloc/vc-go/dataintegrity/models" 16 | "github.com/trustbloc/vc-go/verifiable/lddocument" 17 | ) 18 | 19 | type embeddedProofCheckOpts struct { 20 | proofChecker lddocument.ProofChecker 21 | disabledProofCheck bool 22 | 23 | dataIntegrityOpts *verifyDataIntegrityOpts 24 | 25 | jsonldCredentialOpts 26 | } 27 | 28 | // nolint:gocyclo 29 | func checkEmbeddedProofBytes(docBytes []byte, expectedProofIssuer *string, opts *embeddedProofCheckOpts) error { 30 | if opts.disabledProofCheck { 31 | return nil 32 | } 33 | 34 | var jsonldDoc map[string]interface{} 35 | 36 | if err := json.Unmarshal(docBytes, &jsonldDoc); err != nil { 37 | return fmt.Errorf("embedded proof is not JSON: %w", err) 38 | } 39 | 40 | delete(jsonldDoc, "jwt") 41 | 42 | return checkEmbeddedProof(jsonldDoc, expectedProofIssuer, opts) 43 | } 44 | 45 | // nolint:gocyclo 46 | func checkEmbeddedProof(jsonldDoc map[string]interface{}, expectedProofIssuer *string, 47 | opts *embeddedProofCheckOpts) error { 48 | proofElement, ok := jsonldDoc["proof"] 49 | if !ok || proofElement == nil { 50 | return errors.New("proof not found") 51 | } 52 | 53 | proofs, err := getProofs(proofElement) 54 | if err != nil { 55 | return fmt.Errorf("check embedded proof: %w", err) 56 | } 57 | 58 | if len(opts.externalContext) > 0 { 59 | // Use external contexts for check of the linked data proofs to enrich JSON-LD context vocabulary. 60 | jsonldDoc["@context"] = jsonld.AppendExternalContexts(jsonldDoc["@context"], opts.externalContext...) 61 | } 62 | 63 | if len(proofs) > 0 { 64 | typeStr, ok := proofs[0]["type"] 65 | if ok && typeStr == models.DataIntegrityProof { 66 | var docBytes []byte 67 | 68 | docBytes, err = json.Marshal(jsonldDoc) 69 | if err != nil { 70 | return err 71 | } 72 | 73 | return checkDataIntegrityProof(docBytes, opts.dataIntegrityOpts) 74 | } 75 | } 76 | 77 | if opts.proofChecker == nil { 78 | return errors.New("proofChecker is not defined") 79 | } 80 | 81 | err = checkLinkedDataProof(jsonldDoc, opts.proofChecker, expectedProofIssuer, 82 | &opts.jsonldCredentialOpts) 83 | if err != nil { 84 | return fmt.Errorf("check embedded proof: %w", err) 85 | } 86 | 87 | return nil 88 | } 89 | 90 | func getProofs(proofElement interface{}) ([]map[string]interface{}, error) { 91 | switch p := proofElement.(type) { 92 | case map[string]interface{}: 93 | return []map[string]interface{}{p}, nil 94 | 95 | case []interface{}: 96 | proofs := make([]map[string]interface{}, len(p)) 97 | 98 | for i := range p { 99 | proofMap, ok := p[i].(map[string]interface{}) 100 | if !ok { 101 | return nil, errors.New("invalid proof type") 102 | } 103 | 104 | proofs[i] = proofMap 105 | } 106 | 107 | return proofs, nil 108 | } 109 | 110 | return nil, errors.New("invalid proof type") 111 | } 112 | -------------------------------------------------------------------------------- /verifiable/presentation_ldp_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright SecureKey Technologies Inc. All Rights Reserved. 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package verifiable 7 | 8 | import ( 9 | "encoding/json" 10 | "testing" 11 | 12 | "github.com/stretchr/testify/require" 13 | ldprocessor "github.com/trustbloc/did-go/doc/ld/processor" 14 | "github.com/trustbloc/kms-go/spi/kms" 15 | 16 | "github.com/trustbloc/vc-go/proof/testsupport" 17 | jsonutil "github.com/trustbloc/vc-go/util/json" 18 | ) 19 | 20 | func TestParsePresentationFromLinkedDataProof(t *testing.T) { 21 | r := require.New(t) 22 | 23 | proofCreator, proofChecker := testsupport.NewKMSSigVerPair(t, kms.ED25519Type, "did:example:123456#key1") 24 | 25 | ldpContext := &LinkedDataProofContext{ 26 | SignatureType: "Ed25519Signature2018", 27 | KeyType: kms.ED25519Type, 28 | SignatureRepresentation: SignatureJWS, 29 | ProofCreator: proofCreator, 30 | VerificationMethod: "did:example:123456#key1", 31 | } 32 | 33 | vc, err := newTestPresentation(t, []byte(validPresentation), WithPresDisabledProofCheck()) 34 | r.NoError(err) 35 | 36 | err = vc.AddLinkedDataProof(ldpContext, ldprocessor.WithDocumentLoader(createTestDocumentLoader(t))) 37 | r.NoError(err) 38 | 39 | vcBytes, err := json.Marshal(vc) 40 | r.NoError(err) 41 | 42 | vcWithLdp, err := newTestPresentation(t, vcBytes, WithPresProofChecker(proofChecker)) 43 | r.NoError(err) 44 | 45 | r.NoError(err) 46 | r.Equal(vc, vcWithLdp) 47 | 48 | // signature suite is not passed, cannot make a proof check 49 | vcWithLdp, err = newTestPresentation(t, vcBytes) 50 | r.Error(err) 51 | require.Nil(t, vcWithLdp) 52 | } 53 | 54 | func TestPresentation_AddLinkedDataProof(t *testing.T) { 55 | r := require.New(t) 56 | 57 | proofCreator, _ := testsupport.NewKMSSigVerPair(t, kms.ED25519Type, "not used") 58 | 59 | ldpContext := &LinkedDataProofContext{ 60 | SignatureType: "Ed25519Signature2018", 61 | KeyType: kms.ED25519Type, 62 | SignatureRepresentation: SignatureProofValue, 63 | ProofCreator: proofCreator, 64 | } 65 | 66 | t.Run("Add a valid Linked Data proof to VC", func(t *testing.T) { 67 | vp, err := newTestPresentation(t, []byte(validPresentation), WithPresDisabledProofCheck()) 68 | r.NoError(err) 69 | 70 | err = vp.AddLinkedDataProof(ldpContext, ldprocessor.WithDocumentLoader(createTestDocumentLoader(t))) 71 | r.NoError(err) 72 | 73 | err = vp.AddLinkedDataProof(ldpContext, ldprocessor.WithDocumentLoader(createTestDocumentLoader(t))) 74 | r.NoError(err) 75 | 76 | err = vp.AddLinkedDataProof(ldpContext, ldprocessor.WithDocumentLoader(createTestDocumentLoader(t))) 77 | r.NoError(err) 78 | 79 | vpJSON, err := vp.MarshalJSON() 80 | r.NoError(err) 81 | 82 | vpMap, err := jsonutil.ToMap(vpJSON) 83 | r.NoError(err) 84 | 85 | r.Contains(vpMap, "proof") 86 | vpProof := vpMap["proof"] 87 | vpProofs, ok := vpProof.([]interface{}) 88 | r.True(ok) 89 | r.Len(vpProofs, 3) 90 | newVPProof, ok := vpProofs[1].(map[string]interface{}) 91 | r.True(ok) 92 | r.Contains(newVPProof, "created") 93 | r.Contains(newVPProof, "proofValue") 94 | r.Equal("Ed25519Signature2018", newVPProof["type"]) 95 | }) 96 | } 97 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | // Copyright Gen Digital Inc. All Rights Reserved. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | module github.com/trustbloc/vc-go 6 | 7 | go 1.25.3 8 | 9 | require ( 10 | github.com/PaesslerAG/gval v1.2.4 11 | github.com/PaesslerAG/jsonpath v0.1.2-0.20240726212847-3a740cf7976f 12 | github.com/VictoriaMetrics/fastcache v1.13.0 13 | github.com/btcsuite/btcd/btcec/v2 v2.3.5 14 | github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce 15 | github.com/fxamacker/cbor/v2 v2.9.0 16 | github.com/go-jose/go-jose/v3 v3.0.4 17 | github.com/golang/mock v1.6.0 18 | github.com/google/uuid v1.6.0 19 | github.com/hashicorp/golang-lru/v2 v2.0.7 20 | github.com/kawamuray/jsonpath v0.0.0-20210127151053-2ab0d7f0a6ad 21 | github.com/mitchellh/mapstructure v1.5.0 22 | github.com/multiformats/go-multibase v0.2.0 23 | github.com/piprate/json-gold v0.5.1-0.20230111113000-6ddbe6e6f19f 24 | github.com/samber/lo v1.52.0 25 | github.com/stretchr/testify v1.11.1 26 | github.com/theory/jsonpath v0.10.2 27 | github.com/tidwall/gjson v1.18.0 28 | github.com/tidwall/sjson v1.1.4 29 | github.com/trustbloc/bbs-signature-go v1.0.3 30 | github.com/trustbloc/did-go v1.3.5 31 | github.com/trustbloc/kms-go v1.2.3 32 | github.com/veraison/go-cose v1.3.0 33 | github.com/xeipuuv/gojsonschema v1.2.0 34 | golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 35 | ) 36 | 37 | require ( 38 | github.com/IBM/mathlib v0.0.3-0.20250709075152-a138079496c3 // indirect 39 | github.com/bits-and-blooms/bitset v1.24.2 // indirect 40 | github.com/cenkalti/backoff/v4 v4.3.0 // indirect 41 | github.com/cespare/xxhash/v2 v2.3.0 // indirect 42 | github.com/consensys/gnark-crypto v0.19.2 // indirect 43 | github.com/davecgh/go-spew v1.1.1 // indirect 44 | github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect 45 | github.com/golang/protobuf v1.5.4 // indirect 46 | github.com/golang/snappy v1.0.0 // indirect 47 | github.com/google/tink/go v1.7.0 // indirect 48 | github.com/hyperledger/fabric-amcl v0.0.0-20230602173724-9e02669dceb2 // indirect 49 | github.com/kilic/bls12-381 v0.1.1-0.20220929213557-ca162e8a70f4 // indirect 50 | github.com/mr-tron/base58 v1.2.0 // indirect 51 | github.com/multiformats/go-base32 v0.1.0 // indirect 52 | github.com/multiformats/go-base36 v0.2.0 // indirect 53 | github.com/pkg/errors v0.9.1 // indirect 54 | github.com/pmezard/go-difflib v1.0.0 // indirect 55 | github.com/pquerna/cachecontrol v0.2.0 // indirect 56 | github.com/shopspring/decimal v1.4.0 // indirect 57 | github.com/teserakt-io/golang-ed25519 v0.0.0-20210104091850-3888c087a4c8 // indirect 58 | github.com/tidwall/match v1.2.0 // indirect 59 | github.com/tidwall/pretty v1.2.1 // indirect 60 | github.com/x448/float16 v0.8.4 // indirect 61 | github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect 62 | github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect 63 | golang.org/x/crypto v0.43.0 // indirect 64 | golang.org/x/sys v0.37.0 // indirect 65 | golang.org/x/text v0.30.0 // indirect 66 | google.golang.org/protobuf v1.36.10 // indirect 67 | gopkg.in/yaml.v3 v3.0.1 // indirect 68 | ) 69 | 70 | replace github.com/piprate/json-gold v0.5.1-0.20230111113000-6ddbe6e6f19f => github.com/trustbloc/json-gold v0.5.2-0.20241206130328-d2135d9f36a8 71 | -------------------------------------------------------------------------------- /presexch/README.md: -------------------------------------------------------------------------------- 1 | # Presentation Exchange 2 | 3 | This document shows how to use [Presentation Exchange](https://identity.foundation/presentation-exchange) by examples. 4 | Code examples can be found [here](example_test.go). 5 | 6 | 1. This example demonstrates `predicate` and `limit_disclosure` usage. 7 | The `presentation definition` below requires field `age` to be greater or equal to 18. 8 | Also, we have `limit_disclosure=true` which requires that output data is limited to the entries specified in the `fields` property. 9 | The `predicate` means that the result should be expressed as a boolean value. 10 | ```json 11 | { 12 | "id": "31e2f0f1-6b70-411d-b239-56aed5321884", 13 | "purpose": "To sell you a drink we need to know that you are an adult.", 14 | "input_descriptors": [ 15 | { 16 | "id": "867bfe7a-5b91-46b2-9ba4-70028b8d9cc8", 17 | "purpose": "Your age should be greater or equal to 18.", 18 | "schema": [ 19 | { 20 | "uri": "https://www.w3.org/TR/vc-data-model/#types" 21 | } 22 | ], 23 | "constraints": { 24 | "limit_disclosure": true, 25 | "fields": [ 26 | { 27 | "path": [ 28 | "$.age" 29 | ], 30 | "filter": { 31 | "type": "integer", 32 | "minimum": 18 33 | }, 34 | "predicate": "required" 35 | } 36 | ] 37 | } 38 | } 39 | ] 40 | } 41 | ``` 42 | Let's say we have such a credential in our database. 43 | ```json 44 | { 45 | "@context": [ 46 | "https://www.w3.org/2018/credentials/v1" 47 | ], 48 | "age": 21, 49 | "credentialSchema": [ 50 | { 51 | "id": "https://www.w3.org/TR/vc-data-model/#types" 52 | } 53 | ], 54 | "first_name": "Jesse", 55 | "id": "2dc74354-e965-4883-be5e-bfec48bf60c7", 56 | "issuer": "", 57 | "last_name": "Pinkman", 58 | "type": "VerifiableCredential" 59 | } 60 | ``` 61 | As a result, we will have the following `verifiable presentation`: 62 | ```json 63 | { 64 | "@context": [ 65 | "https://www.w3.org/2018/credentials/v1", 66 | "https://identity.foundation/presentation-exchange/submission/v1" 67 | ], 68 | "presentation_submission": { 69 | "id": "accd5adf-1dbf-4ed9-9ba2-d687476126cb", 70 | "definition_id": "31e2f0f1-6b70-411d-b239-56aed5321884", 71 | "descriptor_map": [ 72 | { 73 | "id": "867bfe7a-5b91-46b2-9ba4-70028b8d9cc8", 74 | "format": "ldp_vp", 75 | "path": "$.verifiableCredential[0]" 76 | } 77 | ] 78 | }, 79 | "type": [ 80 | "VerifiablePresentation", 81 | "PresentationSubmission" 82 | ], 83 | "verifiableCredential": [ 84 | { 85 | "@context": [ 86 | "https://www.w3.org/2018/credentials/v1" 87 | ], 88 | "age": true, 89 | "credentialSchema": [ 90 | { 91 | "id": "https://www.w3.org/TR/vc-data-model/#types" 92 | } 93 | ], 94 | "credentialSubject": null, 95 | "id": "2dc74354-e965-4883-be5e-bfec48bf60c7", 96 | "issuer": "", 97 | "type": "VerifiableCredential" 98 | } 99 | ] 100 | } 101 | ``` 102 | 103 | As you can see the VP has a credential without `first_name` and `last_name` (because of `limit_disclosure`). 104 | Also, instead of `age`, we have a boolean value (because of `predicate`). -------------------------------------------------------------------------------- /verifiable/credential_testsuite_test.go: -------------------------------------------------------------------------------- 1 | //go:build testsuite 2 | // +build testsuite 3 | 4 | /* 5 | Copyright SecureKey Technologies Inc. All Rights Reserved. 6 | SPDX-License-Identifier: Apache-2.0 7 | 8 | This is not actually a test but rather a stand-alone generator application 9 | that is used by VC Test Suite (https://github.com/w3c/vc-test-suite). 10 | To run VC Test Suite, execute `make vc-test-suite`. 11 | */ 12 | 13 | package verifiable 14 | 15 | import ( 16 | "encoding/json" 17 | "testing" 18 | 19 | "github.com/stretchr/testify/require" 20 | ) 21 | 22 | // nolint:lll 23 | const validEmptyPresentation = ` 24 | { 25 | "@context": [ 26 | "https://www.w3.org/2018/credentials/v1", 27 | "https://www.w3.org/2018/credentials/examples/v1", 28 | "https://trustbloc.github.io/context/vc/examples-v1.jsonld" 29 | ], 30 | "id": "urn:uuid:3978344f-8596-4c3a-a978-8fcaba3903c5", 31 | "type": "VerifiablePresentation", 32 | "holder": "did:example:ebfeb1f712ebc6f1c276e12ec21" 33 | } 34 | ` 35 | 36 | func TestWithNoProofCheck(t *testing.T) { 37 | credentialOpt := WithNoProofCheck() 38 | require.NotNil(t, credentialOpt) 39 | 40 | opts := &credentialOpts{} 41 | credentialOpt(opts) 42 | require.True(t, opts.disabledProofCheck) 43 | } 44 | 45 | func TestWithPresSkippedEmbeddedProofCheck(t *testing.T) { 46 | vpOpt := WithPresNoProofCheck() 47 | require.NotNil(t, vpOpt) 48 | 49 | opts := &presentationOpts{} 50 | vpOpt(opts) 51 | require.True(t, opts.disabledProofCheck) 52 | } 53 | 54 | func TestWithPresRequireVC(t *testing.T) { 55 | vpOpt := WithPresRequireVC() 56 | require.NotNil(t, vpOpt) 57 | 58 | opts := &presentationOpts{} 59 | vpOpt(opts) 60 | require.True(t, opts.requireVC) 61 | } 62 | 63 | func TestWithPresRequireProof(t *testing.T) { 64 | vpOpt := WithPresRequireProof() 65 | require.NotNil(t, vpOpt) 66 | 67 | opts := &presentationOpts{} 68 | vpOpt(opts) 69 | require.True(t, opts.requireProof) 70 | 71 | var raw rawPresentation 72 | require.NoError(t, json.Unmarshal([]byte(validPresentation), &raw)) 73 | raw.Proof = nil 74 | bytes, err := json.Marshal(raw) 75 | require.NoError(t, err) 76 | vp, err := newTestPresentation(bytes, WithPresRequireProof()) 77 | require.Error(t, err) 78 | require.Contains(t, err.Error(), "embedded proof is missing") 79 | require.Nil(t, vp) 80 | } 81 | 82 | func TestNewPresentationWithEmptyFields(t *testing.T) { 83 | t.Run("creates a new Verifiable Presentation from JSON with valid empty VC structure", func(t *testing.T) { 84 | vp, err := newTestPresentation([]byte(validEmptyPresentation)) 85 | require.NoError(t, err) 86 | require.NotNil(t, vp) 87 | }) 88 | 89 | t.Run("creates a new Verifiable Presentation from JSON with invalid empty VC structure", func(t *testing.T) { 90 | vp, err := newTestPresentation([]byte(validEmptyPresentation), WithPresRequireVC()) 91 | require.Error(t, err) 92 | require.Contains(t, err.Error(), "verifiableCredential is required") 93 | require.Nil(t, vp) 94 | }) 95 | 96 | t.Run("creates a new Verifiable Presentation from JSON with invalid empty proof structure", func(t *testing.T) { 97 | vp, err := newTestPresentation([]byte(validEmptyPresentation), WithPresRequireProof()) 98 | require.Error(t, err) 99 | require.Contains(t, err.Error(), "embedded proof is missing") 100 | require.Nil(t, vp) 101 | }) 102 | } 103 | -------------------------------------------------------------------------------- /util/time/time_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright SecureKey Technologies Inc. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package time 8 | 9 | import ( 10 | "encoding/json" 11 | "testing" 12 | "time" 13 | 14 | "github.com/stretchr/testify/require" 15 | ) 16 | 17 | func TestTimeWithTrailingZeroMsec(t *testing.T) { 18 | timeTests := []struct { 19 | in string 20 | out string 21 | }{ 22 | {"2018-03-15T00:00:00Z", "2018-03-15T00:00:00Z"}, 23 | {"2018-03-15T00:00:00.9724Z", "2018-03-15T00:00:00.9724Z"}, 24 | {"2018-03-15T00:00:00.000Z", "2018-03-15T00:00:00.000Z"}, 25 | {"2018-03-15T00:00:00.00000Z", "2018-03-15T00:00:00.00000Z"}, 26 | {"2018-03-15T00:00:00.0000100Z", "2018-03-15T00:00:00.0000100Z"}, 27 | {"2018-03-15T00:00:00.0000100+07:00", "2018-03-15T00:00:00.0000100+07:00"}, 28 | {"2018-03-15T00:00:00.0000100-07:00", "2018-03-15T00:00:00.0000100-07:00"}, 29 | {"2018-03-15T00:00:00", "2018-03-15T00:00:00"}, 30 | {"2018-03-15T00:00:00.9724", "2018-03-15T00:00:00.9724"}, 31 | {"2018-03-15T00:00:00.000", "2018-03-15T00:00:00.000"}, 32 | {"2018-03-15T00:00:00.00000", "2018-03-15T00:00:00.00000"}, 33 | {"2018-03-15T00:00:00.0000100", "2018-03-15T00:00:00.0000100"}, 34 | {"2021-08-24T22:18:06.333020", "2021-08-24T22:18:06.333020"}, // acapy interop corner case 35 | } 36 | 37 | for _, tt := range timeTests { 38 | t.Run(tt.in, func(t *testing.T) { 39 | var timeMsec TimeWrapper 40 | err := json.Unmarshal([]byte(quote(tt.in)), &timeMsec) 41 | require.NoError(t, err) 42 | 43 | timeMsecBytes, err := json.Marshal(timeMsec) 44 | require.NoError(t, err) 45 | require.Equal(t, quote(tt.out), string(timeMsecBytes)) 46 | }) 47 | } 48 | 49 | // Marshal corner cases. 50 | timeMsec := &TimeWrapper{Time: time.Date(10001, time.January, 1, 0, 0, 0, 0, time.UTC)} 51 | bytes, err := timeMsec.MarshalJSON() 52 | require.Error(t, err) 53 | require.Nil(t, bytes) 54 | 55 | timeMsec = NewTime(time.Date(2021, time.January, 1, 0, 0, 0, 0, time.UTC)) 56 | bytes, err = timeMsec.MarshalJSON() 57 | require.NoError(t, err) 58 | require.NotNil(t, bytes) 59 | 60 | timeMsec = NewTimeWithTrailingZeroMsec(time.Date(2021, time.January, 1, 0, 0, 0, 0, time.UTC), 0) 61 | bytes, err = timeMsec.MarshalJSON() 62 | require.NoError(t, err) 63 | require.NotNil(t, bytes) 64 | 65 | // Unmarshal corner cases. 66 | newTimeMsec := &TimeWrapper{} 67 | err = newTimeMsec.UnmarshalJSON([]byte(quote("not_date"))) 68 | require.Error(t, err) 69 | 70 | err = newTimeMsec.UnmarshalJSON([]byte("null")) 71 | require.NoError(t, err) 72 | 73 | err = newTimeMsec.UnmarshalJSON([]byte("not string")) 74 | require.Error(t, err) 75 | } 76 | 77 | func TestParse(t *testing.T) { 78 | timeMsec, err := ParseTimeWrapper("2018-03-15T00:00:00.000Z") 79 | require.NoError(t, err) 80 | require.Equal(t, "2018-03-15T00:00:00.000Z", timeMsec.FormatToString()) 81 | 82 | timeMsec, err = ParseTimeWrapper("2018-03-15T00:00:00.123Z") 83 | require.NoError(t, err) 84 | require.Equal(t, "2018-03-15T00:00:00.123Z", timeMsec.FormatToString()) 85 | 86 | timeMsec, err = ParseTimeWithTrailingZeroMsec("2018-03-15T00:00:00.123Z") 87 | require.NoError(t, err) 88 | require.Equal(t, "2018-03-15T00:00:00.123Z", timeMsec.FormatToString()) 89 | 90 | // error case 91 | timeMsec, err = ParseTimeWrapper("invalid") 92 | require.Error(t, err) 93 | require.Nil(t, timeMsec) 94 | } 95 | 96 | func quote(str string) string { 97 | return `"` + str + `"` 98 | } 99 | -------------------------------------------------------------------------------- /presexch/package_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright SecureKey Technologies Inc. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package presexch 8 | 9 | import ( 10 | _ "embed" 11 | "fmt" 12 | "testing" 13 | 14 | "github.com/piprate/json-gold/ld" 15 | "github.com/stretchr/testify/require" 16 | ldcontext "github.com/trustbloc/did-go/doc/ld/context" 17 | ldtestutil "github.com/trustbloc/did-go/doc/ld/testutil" 18 | 19 | "github.com/trustbloc/vc-go/verifiable" 20 | ) 21 | 22 | type mockLoader struct { 23 | ctxDoc ld.RemoteDocument 24 | err error 25 | } 26 | 27 | // nolint:gochecknoglobals // required for go:embed 28 | var ( 29 | //go:embed testdata/contexts/mdl-v1.jsonld 30 | mDLv1JSONLD []byte 31 | //go:embed testdata/contexts/mdl-broken.jsonld 32 | mDLBroken []byte 33 | ) 34 | 35 | func (ml *mockLoader) LoadDocument(url string) (*ld.RemoteDocument, error) { 36 | return &ml.ctxDoc, ml.err 37 | } 38 | 39 | func TestGetContext(t *testing.T) { 40 | t.Run("fail to load ctx", func(t *testing.T) { 41 | errLoader := mockLoader{err: fmt.Errorf("context load error")} 42 | 43 | ctxOut, err := getContext("foo", &errLoader) 44 | require.Error(t, err) 45 | require.Nil(t, ctxOut) 46 | require.Contains(t, err.Error(), "context load error") 47 | }) 48 | 49 | t.Run("data wrong format", func(t *testing.T) { 50 | badTypeLoader := mockLoader{ctxDoc: ld.RemoteDocument{Document: "foo"}} 51 | 52 | ctxOut, err := getContext("foo", &badTypeLoader) 53 | require.Error(t, err) 54 | require.Nil(t, ctxOut) 55 | require.Contains(t, err.Error(), "expects jsonld document") 56 | }) 57 | 58 | t.Run("missing @context field", func(t *testing.T) { 59 | missingCtxLoader := mockLoader{ctxDoc: ld.RemoteDocument{Document: map[string]interface{}{ 60 | "foo": "bar", 61 | }}} 62 | 63 | ctxOut, err := getContext("foo", &missingCtxLoader) 64 | require.Error(t, err) 65 | require.Nil(t, ctxOut) 66 | require.Contains(t, err.Error(), "@context field not found") 67 | }) 68 | } 69 | 70 | func Test_mDLNestedCtx(t *testing.T) { 71 | schemas := []*Schema{{ 72 | URI: "https://example.org/examples#mDL", 73 | }} 74 | 75 | cred, err := verifiable.CreateCredential(verifiable.CredentialContents{ 76 | Context: []string{ 77 | verifiable.V1ContextURI, 78 | "https://trustbloc.github.io/context/vc/examples/mdl-v1.jsonld", 79 | }, 80 | Types: []string{verifiable.VCType, "mDL"}, 81 | }, nil) 82 | require.NoError(t, err) 83 | 84 | creds := []*verifiable.Credential{cred} 85 | 86 | t.Run("success", func(t *testing.T) { 87 | docLoader, err := ldtestutil.DocumentLoader(ldcontext.Document{ 88 | URL: "https://trustbloc.github.io/context/vc/examples/mdl-v1.jsonld", 89 | Content: mDLv1JSONLD, 90 | }) 91 | 92 | require.NoError(t, err) 93 | 94 | matched, err := filterSchema(schemas, creds, docLoader) 95 | require.NoError(t, err) 96 | require.Len(t, matched, 1) 97 | }) 98 | 99 | t.Run("failure: fail to parse child ctx", func(t *testing.T) { 100 | docLoader, err := ldtestutil.DocumentLoader(ldcontext.Document{ 101 | URL: "https://trustbloc.github.io/context/vc/examples/mdl-v1.jsonld", 102 | Content: mDLBroken, 103 | }) 104 | require.NoError(t, err) 105 | 106 | matched, err := filterSchema(schemas, creds, docLoader) 107 | require.NoError(t, err) 108 | require.Len(t, matched, 0) 109 | }) 110 | } 111 | -------------------------------------------------------------------------------- /proof/ldproofs/ed25519signature2020/proof.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright SecureKey Technologies Inc. All Rights Reserved. 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | // Package ed25519signature2020 implements the Ed25519Signature2020 signature suite 7 | // for the Linked Data Signatures [LD-SIGNATURES] specification. 8 | // It uses the RDF Dataset Normalization Algorithm [RDF-DATASET-NORMALIZATION] 9 | // to transform the input document into its canonical form. 10 | // It uses SHA-256 [RFC6234] as the message digest algorithm and 11 | // Ed25519 [ED25519] as the signature algorithm. 12 | package ed25519signature2020 13 | 14 | import ( 15 | "crypto/sha256" 16 | 17 | "github.com/trustbloc/did-go/doc/ld/processor" 18 | "github.com/trustbloc/kms-go/spi/kms" 19 | 20 | "github.com/trustbloc/vc-go/proof" 21 | ) 22 | 23 | // Proof describe Ed25519Signature2020 proof. 24 | type Proof struct { 25 | jsonldProcessor *processor.Processor 26 | supportedVMs []proof.SupportedVerificationMethod 27 | } 28 | 29 | const ( 30 | // ProofType for Ed25519Signature2020. 31 | ProofType = "Ed25519Signature2020" 32 | // VerificationMethodType for Ed25519Signature2020. 33 | VerificationMethodType = "Ed25519VerificationKey2020" 34 | // JWKKeyType for Ed25519Signature2020. 35 | JWKKeyType = "OKP" 36 | // JWKCurve for Ed25519Signature2020. 37 | JWKCurve = "Ed25519" 38 | // CorrespondedJWTAlg for Ed25519Signature2020. 39 | CorrespondedJWTAlg = "EdDSA" 40 | rdfDataSetAlg = "URDNA2015" 41 | ) 42 | 43 | // New an instance of ed25519 proof descriptor. 44 | func New() *Proof { 45 | p := &Proof{jsonldProcessor: processor.NewProcessor(rdfDataSetAlg)} 46 | p.supportedVMs = []proof.SupportedVerificationMethod{ 47 | { 48 | VerificationMethodType: VerificationMethodType, 49 | KMSKeyType: kms.ED25519Type, 50 | JWKKeyType: JWKKeyType, 51 | JWKCurve: JWKCurve, 52 | }, 53 | { 54 | VerificationMethodType: "Ed25519VerificationKey2018", 55 | KMSKeyType: kms.ED25519Type, 56 | JWKKeyType: JWKKeyType, 57 | JWKCurve: JWKCurve, 58 | }, 59 | { 60 | VerificationMethodType: "JsonWebKey2020", 61 | KMSKeyType: kms.ED25519Type, 62 | JWKKeyType: JWKKeyType, 63 | JWKCurve: JWKCurve, 64 | RequireJWK: true, 65 | }, 66 | } 67 | 68 | return p 69 | } 70 | 71 | // ProofType return proof type name. 72 | func (s *Proof) ProofType() string { 73 | return ProofType 74 | } 75 | 76 | // SupportedVerificationMethods returns list of verification methods supported by this proof type. 77 | func (s *Proof) SupportedVerificationMethods() []proof.SupportedVerificationMethod { 78 | return s.supportedVMs 79 | } 80 | 81 | // IsJWTAlgSupported return jwt alg that corresponds to VerificationMethod. 82 | func (s *Proof) IsJWTAlgSupported(alg string) bool { 83 | return alg == CorrespondedJWTAlg 84 | } 85 | 86 | // GetCanonicalDocument will return normalized/canonical version of the document 87 | // Ed25519Signature2020 signature SignatureSuite uses RDF Dataset Normalization as canonicalization algorithm. 88 | func (s *Proof) GetCanonicalDocument(doc map[string]interface{}, opts ...processor.Opts) ([]byte, error) { 89 | return s.jsonldProcessor.GetCanonicalDocument(doc, opts...) 90 | } 91 | 92 | // GetDigest returns document digest. 93 | func (s *Proof) GetDigest(doc []byte) []byte { 94 | digest := sha256.Sum256(doc) 95 | return digest[:] 96 | } 97 | -------------------------------------------------------------------------------- /cwt/cwt.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright SecureKey Technologies Inc. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package cwt 8 | 9 | import ( 10 | "errors" 11 | 12 | "github.com/fxamacker/cbor/v2" 13 | "github.com/veraison/go-cose" 14 | 15 | "github.com/trustbloc/vc-go/proof" 16 | "github.com/trustbloc/vc-go/verifiable/cwt" 17 | ) 18 | 19 | const ( 20 | issuerPayloadIndex = 1 21 | ) 22 | 23 | // SignParameters contains parameters of signing for cwt vc. 24 | type SignParameters struct { 25 | KeyID string 26 | CWTAlg cose.Algorithm 27 | } 28 | 29 | // ParseAndCheckProof parses input JWT in serialized form into JSON Web Token and check signature proof. 30 | // if checkIssuer set to true, will check if issuer set by "iss" own key set by "kid" header. 31 | func ParseAndCheckProof( 32 | cwtSerialized []byte, 33 | proofChecker ProofChecker, 34 | checkIssuer bool, 35 | ) (*cose.Sign1Message, []byte, error) { 36 | cwtParsed, err := Parse(cwtSerialized) 37 | if err != nil { 38 | return nil, nil, err 39 | } 40 | 41 | var expectedProofIssuer *string 42 | 43 | if checkIssuer { 44 | payload := map[int]interface{}{} 45 | if err = cbor.Unmarshal(cwtParsed.Payload, &payload); err != nil { 46 | return nil, nil, err 47 | } 48 | 49 | iss, ok := payload[issuerPayloadIndex] 50 | if !ok { 51 | return nil, nil, errors.New("check cwt failure: iss claim is required") 52 | } 53 | 54 | issStr, ok := iss.(string) 55 | if !ok { 56 | return nil, nil, errors.New("check cwt failure: iss claim is not a string") 57 | } 58 | 59 | expectedProofIssuer = &issStr 60 | } 61 | 62 | _, ok := cwtParsed.Headers.Protected[proof.COSEKeyHeader].(string) 63 | if !ok { 64 | return nil, nil, errors.New("check cwt failure: COSE_Key header is required") 65 | } 66 | 67 | proofValue, err := cwt.GetProofValue(cwtParsed) 68 | if err != nil { 69 | return nil, nil, err 70 | } 71 | 72 | err = CheckProof(cwtParsed, proofChecker, expectedProofIssuer, proofValue, cwtParsed.Signature) 73 | if err != nil { 74 | return nil, nil, err 75 | } 76 | 77 | return cwtParsed, cwtParsed.Payload, nil 78 | } 79 | 80 | // Parse parses input CWT in serialized form into JSON Web Token. 81 | func Parse(cwtSerialized []byte) (*cose.Sign1Message, error) { 82 | var message cose.Sign1Message 83 | if err := message.UnmarshalCBOR(cwtSerialized); err != nil { 84 | return nil, err 85 | } 86 | 87 | return &message, nil 88 | } 89 | 90 | // CheckProof checks that jwt have correct signature. 91 | func CheckProof( 92 | message *cose.Sign1Message, 93 | proofChecker ProofChecker, 94 | expectedProofIssuer *string, 95 | msg []byte, 96 | signature []byte, 97 | ) error { 98 | var err error 99 | 100 | alg, err := message.Headers.Protected.Algorithm() 101 | if err != nil { 102 | return err 103 | } 104 | 105 | // currently supported only COSE_Key, x5chain is not supported by go opensource implementation yet 106 | keyMaterial, _ := message.Headers.Protected[proof.COSEKeyHeader].(string) // nolint 107 | keyIDBinary, _ := message.Headers.Protected[cose.HeaderLabelKeyID].([]byte) // nolint 108 | 109 | var rawKeyID string 110 | if len(keyIDBinary) > 0 { 111 | rawKeyID = string(keyIDBinary) 112 | } 113 | 114 | checker := Verifier{ 115 | ProofChecker: proofChecker, 116 | expectedProofIssuer: expectedProofIssuer, 117 | } 118 | 119 | return checker.Verify(keyMaterial, rawKeyID, alg, msg, signature) 120 | } 121 | -------------------------------------------------------------------------------- /verifiable/testdata/context/lds_jws2020_v1.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": { 3 | "privateKeyJwk": { 4 | "@id": "https://w3id.org/security#privateKeyJwk", 5 | "@type": "@json" 6 | }, 7 | "JsonWebKey2020": { 8 | "@id": "https://w3id.org/security#JsonWebKey2020", 9 | "@context": { 10 | "@protected": true, 11 | "id": "@id", 12 | "type": "@type", 13 | "publicKeyJwk": { 14 | "@id": "https://w3id.org/security#publicKeyJwk", 15 | "@type": "@json" 16 | } 17 | } 18 | }, 19 | "JsonWebSignature2020": { 20 | "@id": "https://w3id.org/security#JsonWebSignature2020", 21 | "@context": { 22 | "@protected": true, 23 | "id": "@id", 24 | "type": "@type", 25 | "challenge": "https://w3id.org/security#challenge", 26 | "created": { 27 | "@id": "http://purl.org/dc/terms/created", 28 | "@type": "http://www.w3.org/2001/XMLSchema#dateTime" 29 | }, 30 | "domain": "https://w3id.org/security#domain", 31 | "expires": { 32 | "@id": "https://w3id.org/security#expiration", 33 | "@type": "http://www.w3.org/2001/XMLSchema#dateTime" 34 | }, 35 | "jws": "https://w3id.org/security#jws", 36 | "nonce": "https://w3id.org/security#nonce", 37 | "proofPurpose": { 38 | "@id": "https://w3id.org/security#proofPurpose", 39 | "@type": "@vocab", 40 | "@context": { 41 | "@protected": true, 42 | "id": "@id", 43 | "type": "@type", 44 | "assertionMethod": { 45 | "@id": "https://w3id.org/security#assertionMethod", 46 | "@type": "@id", 47 | "@container": "@set" 48 | }, 49 | "authentication": { 50 | "@id": "https://w3id.org/security#authenticationMethod", 51 | "@type": "@id", 52 | "@container": "@set" 53 | }, 54 | "capabilityInvocation": { 55 | "@id": "https://w3id.org/security#capabilityInvocationMethod", 56 | "@type": "@id", 57 | "@container": "@set" 58 | }, 59 | "capabilityDelegation": { 60 | "@id": "https://w3id.org/security#capabilityDelegationMethod", 61 | "@type": "@id", 62 | "@container": "@set" 63 | }, 64 | "keyAgreement": { 65 | "@id": "https://w3id.org/security#keyAgreementMethod", 66 | "@type": "@id", 67 | "@container": "@set" 68 | } 69 | } 70 | }, 71 | "verificationMethod": { 72 | "@id": "https://w3id.org/security#verificationMethod", 73 | "@type": "@id" 74 | } 75 | } 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /crypto-ext/testutil/sign_helper.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Gen Digital Inc. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package testutil 8 | 9 | import ( 10 | "crypto/ecdsa" 11 | "crypto/elliptic" 12 | "crypto/rand" 13 | "crypto/rsa" 14 | "crypto/x509" 15 | 16 | "github.com/btcsuite/btcd/btcec/v2" 17 | "github.com/trustbloc/kms-go/doc/jose/jwk/jwksupport" 18 | "github.com/trustbloc/kms-go/spi/kms" 19 | wrapperapi "github.com/trustbloc/kms-go/wrapper/api" 20 | 21 | "github.com/trustbloc/vc-go/crypto-ext/pubkey" 22 | "github.com/trustbloc/vc-go/internal/testutil/kmscryptoutil" 23 | ) 24 | 25 | // CreateRSARS256 created signer and corresponding public key. 26 | func CreateRSARS256() (*RS256Signer, *pubkey.PublicKey, error) { 27 | privKey, err := rsa.GenerateKey(rand.Reader, 2048) 28 | if err != nil { 29 | return nil, nil, err 30 | } 31 | 32 | pubKey := &privKey.PublicKey 33 | signer := NewRS256Signer(privKey) 34 | 35 | pub := &pubkey.PublicKey{ 36 | Type: kms.RSARS256Type, 37 | BytesKey: &pubkey.BytesKey{Bytes: x509.MarshalPKCS1PublicKey(pubKey)}, 38 | } 39 | 40 | return signer, pub, nil 41 | } 42 | 43 | // CreateRSAPS256 created signer and corresponding public key. 44 | func CreateRSAPS256() (*PS256Signer, *pubkey.PublicKey, error) { 45 | privKey, err := rsa.GenerateKey(rand.Reader, 2048) 46 | if err != nil { 47 | return nil, nil, err 48 | } 49 | 50 | pubKey := &privKey.PublicKey 51 | signer := NewPS256Signer(privKey) 52 | 53 | pub := &pubkey.PublicKey{ 54 | Type: kms.RSAPS256Type, 55 | BytesKey: &pubkey.BytesKey{Bytes: x509.MarshalPKCS1PublicKey(pubKey)}, 56 | } 57 | 58 | return signer, pub, nil 59 | } 60 | 61 | // CreateEDSASecp256k1 created signer and corresponding public key. 62 | func CreateEDSASecp256k1(jwkVM bool) (*ECDSASigner, *pubkey.PublicKey, error) { 63 | privKey, err := ecdsa.GenerateKey(btcec.S256(), rand.Reader) 64 | if err != nil { 65 | return nil, nil, err 66 | } 67 | 68 | pubKey := &privKey.PublicKey 69 | signer := NewECDSASecp256k1Signer(privKey) 70 | 71 | pub := &pubkey.PublicKey{ 72 | Type: kms.ECDSASecp256k1TypeIEEEP1363, 73 | BytesKey: &pubkey.BytesKey{Bytes: elliptic.Marshal(pubKey.Curve, pubKey.X, pubKey.Y)}, 74 | } 75 | 76 | if jwkVM { 77 | pubJWK, err := jwksupport.JWKFromKey(pubKey) 78 | if err != nil { 79 | return nil, nil, err 80 | } 81 | 82 | pub = &pubkey.PublicKey{ 83 | Type: kms.ECDSASecp256k1TypeIEEEP1363, 84 | JWK: pubJWK, 85 | } 86 | } 87 | 88 | return signer, pub, nil 89 | } 90 | 91 | // CreateKMSSigner created signer and corresponding public key. 92 | func CreateKMSSigner(keyType kms.KeyType, jwkVM bool) (wrapperapi.FixedKeyCrypto, *pubkey.PublicKey, error) { 93 | kmsCrypto, err := kmscryptoutil.LocalKMSCryptoErr() 94 | if err != nil { 95 | return nil, nil, err 96 | } 97 | 98 | pubJWK, err := kmsCrypto.Create(keyType) 99 | if err != nil { 100 | return nil, nil, err 101 | } 102 | 103 | fkc, err := kmsCrypto.FixedKeyCrypto(pubJWK) 104 | if err != nil { 105 | return nil, nil, err 106 | } 107 | 108 | vm := &pubkey.PublicKey{ 109 | Type: keyType, 110 | JWK: pubJWK, 111 | } 112 | 113 | if !jwkVM { 114 | pubKeyBytes, err := pubJWK.PublicKeyBytes() 115 | vm = &pubkey.PublicKey{ 116 | Type: keyType, 117 | BytesKey: &pubkey.BytesKey{Bytes: pubKeyBytes}, 118 | } 119 | 120 | if err != nil { 121 | return nil, nil, err 122 | } 123 | } 124 | 125 | return fkc, vm, nil 126 | } 127 | -------------------------------------------------------------------------------- /didconfig/client/didconfig.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright SecureKey Technologies Inc. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package client 8 | 9 | import ( 10 | "context" 11 | "fmt" 12 | "io" 13 | "io/ioutil" 14 | "log" 15 | "net/http" 16 | "os" 17 | "time" 18 | 19 | jsonld "github.com/piprate/json-gold/ld" 20 | "github.com/trustbloc/did-go/doc/did" 21 | vdrapi "github.com/trustbloc/did-go/vdr/api" 22 | 23 | "github.com/trustbloc/vc-go/didconfig/verifier" 24 | ) 25 | 26 | var errLogger = log.New(os.Stderr, " [did-go/did-config/client] ", log.Ldate|log.Ltime|log.LUTC) 27 | 28 | const defaultTimeout = time.Minute 29 | 30 | // Client is a JSON-LD SDK client. 31 | type Client struct { 32 | httpClient httpClient 33 | didConfigOpts []verifier.DIDConfigurationOpt 34 | } 35 | 36 | // New creates new did configuration client. 37 | func New(opts ...Option) *Client { 38 | client := &Client{ 39 | httpClient: &http.Client{Timeout: defaultTimeout}, 40 | } 41 | 42 | for _, opt := range opts { 43 | opt(client) 44 | } 45 | 46 | return client 47 | } 48 | 49 | // httpClient represents an HTTP client. 50 | type httpClient interface { 51 | Do(req *http.Request) (*http.Response, error) 52 | } 53 | 54 | // Option configures the did configuration client. 55 | type Option func(opts *Client) 56 | 57 | // WithHTTPClient option is for custom http client. 58 | func WithHTTPClient(httpClient httpClient) Option { 59 | return func(opts *Client) { 60 | opts.httpClient = httpClient 61 | } 62 | } 63 | 64 | // WithJSONLDDocumentLoader defines a JSON-LD document loader. 65 | func WithJSONLDDocumentLoader(documentLoader jsonld.DocumentLoader) Option { 66 | return func(opts *Client) { 67 | opts.didConfigOpts = append(opts.didConfigOpts, verifier.WithJSONLDDocumentLoader(documentLoader)) 68 | } 69 | } 70 | 71 | type didResolver interface { 72 | Resolve(did string, opts ...vdrapi.DIDMethodOption) (*did.DocResolution, error) 73 | } 74 | 75 | // WithVDRegistry defines a vdr service. 76 | func WithVDRegistry(didResolver didResolver) Option { 77 | return func(opts *Client) { 78 | opts.didConfigOpts = append(opts.didConfigOpts, verifier.WithVDRegistry(didResolver)) 79 | } 80 | } 81 | 82 | // VerifyDIDAndDomain will verify that there is valid domain linkage credential in did configuration 83 | // for specified did and domain. 84 | func (c *Client) VerifyDIDAndDomain(did, domain string) error { 85 | endpoint := domain + "/.well-known/did-configuration.json" 86 | 87 | req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, endpoint, nil) 88 | if err != nil { 89 | return fmt.Errorf("new HTTP request: %w", err) 90 | } 91 | 92 | resp, err := c.httpClient.Do(req) 93 | if err != nil { 94 | return fmt.Errorf("httpClient.Do: %w", err) 95 | } 96 | 97 | defer closeResponseBody(resp.Body) 98 | 99 | responseBytes, err := ioutil.ReadAll(resp.Body) 100 | if err != nil { 101 | return fmt.Errorf("failed to read response: %w", err) 102 | } 103 | 104 | if resp.StatusCode != http.StatusOK { 105 | return fmt.Errorf("endpoint %s returned status '%d' and message '%s'", 106 | endpoint, resp.StatusCode, responseBytes) 107 | } 108 | 109 | return verifier.VerifyDIDAndDomain(responseBytes, did, domain, c.didConfigOpts...) 110 | } 111 | 112 | func closeResponseBody(respBody io.Closer) { 113 | e := respBody.Close() 114 | if e != nil { 115 | errLogger.Printf("failed to close response body: %v", e) 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /proof/ldproofs/ecdsasecp256k1signature2019/proof.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright SecureKey Technologies Inc. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | // Package ecdsasecp256k1signature2019 implements the EcdsaSecp256k1Signature2019 signature suite 8 | // for the Linked Data Signatures specification (https://w3c-dvcg.github.io/lds-ecdsa-secp256k1-2019/). 9 | // It uses the RDF Dataset Normalization Algorithm to transform the input document into its canonical form. 10 | // It uses SHA-256 [RFC6234] as the message digest algorithm. 11 | // Supported signature algorithms depend on the signer/verifier provided as options to the New(). 12 | package ecdsasecp256k1signature2019 13 | 14 | import ( 15 | "crypto/sha256" 16 | 17 | "github.com/trustbloc/did-go/doc/ld/processor" 18 | "github.com/trustbloc/kms-go/spi/kms" 19 | 20 | "github.com/trustbloc/vc-go/proof" 21 | ) 22 | 23 | // Proof implements EcdsaSecp256k1Signature2019 proof type. 24 | type Proof struct { 25 | jsonldProcessor *processor.Processor 26 | supportedVMs []proof.SupportedVerificationMethod 27 | } 28 | 29 | const ( 30 | // ProofType for ecdsasecp256k1signature2019. 31 | ProofType = "EcdsaSecp256k1Signature2019" 32 | // VerificationMethodType for ecdsasecp256k1signature2019. 33 | VerificationMethodType = "EcdsaSecp256k1VerificationKey2019" 34 | // JWKKeyType for ecdsasecp256k1signature2019. 35 | JWKKeyType = "EC" 36 | // JWKCurve for ecdsasecp256k1signature2019. 37 | JWKCurve = "secp256k1" 38 | rdfDataSetAlg = "URDNA2015" 39 | ) 40 | 41 | // New an instance of Linked Data Signatures for JWS suite. 42 | func New() *Proof { 43 | p := &Proof{jsonldProcessor: processor.NewProcessor(rdfDataSetAlg)} 44 | p.supportedVMs = []proof.SupportedVerificationMethod{ 45 | { 46 | VerificationMethodType: VerificationMethodType, 47 | KMSKeyType: kms.ECDSASecp256k1TypeIEEEP1363, 48 | JWKKeyType: JWKKeyType, 49 | JWKCurve: JWKCurve, 50 | }, 51 | { 52 | VerificationMethodType: "JsonWebKey2020", 53 | KMSKeyType: kms.ECDSASecp256k1TypeIEEEP1363, 54 | JWKKeyType: JWKKeyType, 55 | JWKCurve: JWKCurve, 56 | RequireJWK: true, 57 | }, 58 | { 59 | VerificationMethodType: VerificationMethodType, 60 | KMSKeyType: kms.ECDSASecp256k1TypeDER, 61 | JWKKeyType: JWKKeyType, 62 | JWKCurve: JWKCurve, 63 | }, 64 | { 65 | VerificationMethodType: "JsonWebKey2020", 66 | KMSKeyType: kms.ECDSASecp256k1TypeDER, 67 | JWKKeyType: JWKKeyType, 68 | JWKCurve: JWKCurve, 69 | RequireJWK: true, 70 | }, 71 | } 72 | 73 | return p 74 | } 75 | 76 | // ProofType return proof type name. 77 | func (s *Proof) ProofType() string { 78 | return ProofType 79 | } 80 | 81 | // SupportedVerificationMethods returns list of verification methods supported by this proof type. 82 | func (s *Proof) SupportedVerificationMethods() []proof.SupportedVerificationMethod { 83 | return s.supportedVMs 84 | } 85 | 86 | // GetCanonicalDocument will return normalized/canonical version of the document. 87 | // EcdsaSecp256k1Signature2019 signature suite uses RDF Dataset Normalization as canonicalization algorithm. 88 | func (s *Proof) GetCanonicalDocument(doc map[string]interface{}, opts ...processor.Opts) ([]byte, error) { 89 | return s.jsonldProcessor.GetCanonicalDocument(doc, opts...) 90 | } 91 | 92 | // GetDigest returns document digest. 93 | func (s *Proof) GetDigest(doc []byte) []byte { 94 | digest := sha256.Sum256(doc) 95 | return digest[:] 96 | } 97 | -------------------------------------------------------------------------------- /presexch/testdata/verified_employee.jwt: -------------------------------------------------------------------------------- 1 | eyJhbGciOiJFUzI1NksiLCJraWQiOiJkaWQ6b3JiOnVBQUE6RWlBbjZqMWMtRGxHdzRBYjA0ODRDa1hzNVJTVGtja2pody1QeTRjVDlHVi13ZyNjNTYyZDMwMi0xNDc4LTQ2YmQtOWQ0Mi0wYjQ3ZGRlN2U4NjMifQ.eyJleHAiOjE3MDc5ODg0MTIsImlhdCI6MTY3NjQ1MjQxMiwiaXNzIjoiZGlkOm9yYjp1QUFBOkVpQW42ajFjLURsR3c0QWIwNDg0Q2tYczVSU1RrY2tqaHctUHk0Y1Q5R1Ytd2ciLCJqdGkiOiJ1cm46dXVpZDo4NzI0ODIwMi0xZmY3LTRiZWItOTU0OS1lNDZmNzg3MjUzYzIiLCJuYmYiOjE2NzY0NTI0MTIsInN1YiI6ImRpZDppb246RWlEanFUUFlrTnB5VGp4NjhKRWxFa2pOa2Zpd0R6TjZ4Z05WNUhEZTN0OHM1ZzpleUprWld4MFlTSTZleUp3WVhSamFHVnpJanBiZXlKaFkzUnBiMjRpT2lKaFpHUXRjSFZpYkdsakxXdGxlWE1pTENKd2RXSnNhV05MWlhseklqcGJleUpwWkNJNklrZHRhbFIwWW5GeExUY3hVbWhNVkVsR2J6RkhNREJKTm1kdVZrVmxjWFZGVDBvNGQyUTBTMHhVVG5NaUxDSndkV0pzYVdOTFpYbEtkMnNpT25zaVkzSjJJam9pUldReU5UVXhPU0lzSW10MGVTSTZJazlMVUNJc0luZ2lPaUpGZEc1WVJrczJiREZUT0dOVFVtMVhVWE5vWWtwdGIyOTViMGhTZGpaNGEwMVdOVXR1ZFhOaWFXbHZJbjBzSW5CMWNuQnZjMlZ6SWpwYkltRjFkR2hsYm5ScFkyRjBhVzl1SWl3aVlYTnpaWEowYVc5dVRXVjBhRzlrSWwwc0luUjVjR1VpT2lKS2MyOXVWMlZpUzJWNU1qQXlNQ0o5WFgxZExDSjFjR1JoZEdWRGIyMXRhWFJ0Wlc1MElqb2lSV2xEVVdKMVNubE9RMVZSVEhkeFRtZDJjbTB3VDB0QmF6aE5MVE40VEZOQlQwdG9SR3ROUlRobVpXcE9VU0o5TENKemRXWm1hWGhFWVhSaElqcDdJbVJsYkhSaFNHRnphQ0k2SWtWcFFUWnRTVTFGVTJ4a1VURmFla2xIYTJGeWJ6Z3hVM1ZDTkZOSk56WTJaamQ2VDFWV1ZsZDNaRXN5V2xFaUxDSnlaV052ZG1WeWVVTnZiVzFwZEcxbGJuUWlPaUpGYVVGdE4xWm9ja0kyZVdRNFoyd3haVGRVVFdGQ0xVSXRNemxvWlMxNVZWRjZaVTA1VW1nNWJHZE5jV1ozSW4wc0luUjVjR1VpT2lKamNtVmhkR1VpZlEiLCJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vdzNpZC5vcmcvdmMvc3RhdHVzLWxpc3QvMjAyMS92MSIsImh0dHBzOi8vdzNjLWNjZy5naXRodWIuaW8vbGRzLWp3czIwMjAvY29udGV4dHMvbGRzLWp3czIwMjAtdjEuanNvbiJdLCJjcmVkZW50aWFsU3RhdHVzIjp7ImlkIjoidXJuOnV1aWQ6YjkzOTM5MWUtYzgzNy00NWUzLTgyYjQtZjczOTBkNDVkN2M2Iiwic3RhdHVzTGlzdENyZWRlbnRpYWwiOiJodHRwOi8vbG9jYWxob3N0OjgwNzUvaXNzdWVyL3Byb2ZpbGVzL2JhbmtfaXNzdWVyL2NyZWRlbnRpYWxzL3N0YXR1cy8xIiwic3RhdHVzTGlzdEluZGV4IjoiMyIsInN0YXR1c1B1cnBvc2UiOiJyZXZvY2F0aW9uIiwidHlwZSI6IlN0YXR1c0xpc3QyMDIxRW50cnkifSwiY3JlZGVudGlhbFN1YmplY3QiOnsiZGlzcGxheU5hbWUiOiJKb2huIERvZSIsImdpdmVuTmFtZSI6IkpvaG4iLCJpZCI6ImRpZDppb246RWlEanFUUFlrTnB5VGp4NjhKRWxFa2pOa2Zpd0R6TjZ4Z05WNUhEZTN0OHM1ZzpleUprWld4MFlTSTZleUp3WVhSamFHVnpJanBiZXlKaFkzUnBiMjRpT2lKaFpHUXRjSFZpYkdsakxXdGxlWE1pTENKd2RXSnNhV05MWlhseklqcGJleUpwWkNJNklrZHRhbFIwWW5GeExUY3hVbWhNVkVsR2J6RkhNREJKTm1kdVZrVmxjWFZGVDBvNGQyUTBTMHhVVG5NaUxDSndkV0pzYVdOTFpYbEtkMnNpT25zaVkzSjJJam9pUldReU5UVXhPU0lzSW10MGVTSTZJazlMVUNJc0luZ2lPaUpGZEc1WVJrczJiREZUT0dOVFVtMVhVWE5vWWtwdGIyOTViMGhTZGpaNGEwMVdOVXR1ZFhOaWFXbHZJbjBzSW5CMWNuQnZjMlZ6SWpwYkltRjFkR2hsYm5ScFkyRjBhVzl1SWl3aVlYTnpaWEowYVc5dVRXVjBhRzlrSWwwc0luUjVjR1VpT2lKS2MyOXVWMlZpUzJWNU1qQXlNQ0o5WFgxZExDSjFjR1JoZEdWRGIyMXRhWFJ0Wlc1MElqb2lSV2xEVVdKMVNubE9RMVZSVEhkeFRtZDJjbTB3VDB0QmF6aE5MVE40VEZOQlQwdG9SR3ROUlRobVpXcE9VU0o5TENKemRXWm1hWGhFWVhSaElqcDdJbVJsYkhSaFNHRnphQ0k2SWtWcFFUWnRTVTFGVTJ4a1VURmFla2xIYTJGeWJ6Z3hVM1ZDTkZOSk56WTJaamQ2VDFWV1ZsZDNaRXN5V2xFaUxDSnlaV052ZG1WeWVVTnZiVzFwZEcxbGJuUWlPaUpGYVVGdE4xWm9ja0kyZVdRNFoyd3haVGRVVFdGQ0xVSXRNemxvWlMxNVZWRjZaVTA1VW1nNWJHZE5jV1ozSW4wc0luUjVjR1VpT2lKamNtVmhkR1VpZlEiLCJqb2JUaXRsZSI6IlNvZnR3YXJlIERldmVsb3BlciIsIm1haWwiOiJqb2huLmRvZUBmb28uYmFyIiwicGhvdG8iOiJiYXNlNjRwaG90byIsInByZWZlcnJlZExhbmd1YWdlIjoiRW5nbGlzaCIsInN1cm5hbWUiOiJEb2UifSwiZXhwaXJhdGlvbkRhdGUiOiIyMDI0LTAyLTE1VDA5OjEzOjMyWiIsImlkIjoidXJuOnV1aWQ6ODcyNDgyMDItMWZmNy00YmViLTk1NDktZTQ2Zjc4NzI1M2MyIiwiaXNzdWFuY2VEYXRlIjoiMjAyMy0wMi0xNVQwOToxMzozMloiLCJpc3N1ZXIiOnsiaWQiOiJkaWQ6b3JiOnVBQUE6RWlBbjZqMWMtRGxHdzRBYjA0ODRDa1hzNVJTVGtja2pody1QeTRjVDlHVi13ZyIsIm5hbWUiOiJCYW5rIElzc3VlciJ9LCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIiwiVmVyaWZpZWRFbXBsb3llZSJdfX0.nJrBSrJs93rNbq_vx9RxW7z71JH0fQ4q_d1vHYt512WkCFksCYC60GByOkTL5onzhhqFooBNMlDkOmKojqgeVA --------------------------------------------------------------------------------