diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md index ddd99a2a..920dd8cb 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.md +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -12,6 +12,7 @@ assignees: nitro-neal, decentralgabe **To Reproduce** *Steps to reproduce the behavior:* + 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' @@ -24,8 +25,9 @@ assignees: nitro-neal, decentralgabe *If applicable, add screenshots and/or other documentation to help explain your problem.* **Environment (please complete the following information):** - - OS: [e.g. iOS] - - Version [e.g. 22] + +- OS: [e.g. iOS] +- Version [e.g. 22] **Additional context** Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/idea-submission.md b/.github/ISSUE_TEMPLATE/idea-submission.md index 72563498..86733619 100644 --- a/.github/ISSUE_TEMPLATE/idea-submission.md +++ b/.github/ISSUE_TEMPLATE/idea-submission.md @@ -16,7 +16,6 @@ assignees: decentralgabe, nitro-neal **Describe alternatives you've considered** *A clear and concise description of any alternative solutions or features you've considered.* - **Getting it done** *Are you building this yourself? Do you need help? What is the urgency of the feature? What are the next steps?* diff --git a/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md b/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md index 4c8ef8f4..b169f400 100644 --- a/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md +++ b/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md @@ -1,10 +1,13 @@ # Overview + _Include a summary of the change and link to the issue it addresses._ # Description + _Include context, motivation, brief description, and an impact of the change(s). List follow-up tasks here._ # How Has This Been Tested? + _Describe the tests that you ran to verify your changes. Provide instructions for verification._ - [ ] Test A (e.g. Test A - New test that does ... run in ...) @@ -15,9 +18,11 @@ _Describe the tests that you ran to verify your changes. Provide instructions fo Before submitting this PR, please make sure: - [ ] I have read the CONTRIBUTING document. -- [ ] My code is consistent with the rest of the project +- [ ] My code is consistent with the rest of the project - [ ] I have tagged the relevant reviewers and/or interested parties - [ ] I have updated the READMEs and other documentation of affected packages ## References -_Please list relevant documentation (e.g. tech specs, articles, follow up or related work) relevant to this change, and note if the documentation has been updated._ + +_Please list relevant documentation (e.g. tech specs, articles, follow up or related work) relevant to this change, and +note if the documentation has been updated._ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3506e7a9..301cfa4f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ on: workflow_dispatch: jobs: - vulnerability-scan: + vulnerability-scan: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 @@ -29,7 +29,7 @@ jobs: run: go install github.com/magefile/mage - name: Check Vulnerabilities - run: mage -v vuln + run: mage -v vuln build: runs-on: ubuntu-latest steps: @@ -42,7 +42,7 @@ jobs: - name: Install Mage run: go install github.com/magefile/mage - + - name: Build run: mage build diff --git a/.golangci.yaml b/.golangci.yaml index ff85e9d5..3baf6475 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -34,7 +34,7 @@ linters-settings: - name: banned-characters severity: warning disabled: false - arguments: [ "Ω", "Σ", "σ"] + arguments: [ "Ω", "Σ", "σ" ] # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#bare-return - name: bare-return severity: warning @@ -69,7 +69,8 @@ linters-settings: # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#context-as-argument - name: context-as-argument severity: warning - disabled: false + # # TODO re-enable linter when it isn't broken https://github.com/golangci/golangci-lint/issues/3280 + disabled: true arguments: - allowTypesBefore: "*testing.T,*github.com/user/repo/testing.Harness" # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#context-keys-type diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c056b865..f22699fb 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -91,7 +91,8 @@ We advocate an asynchronous, written debate model - so write up your thoughts an ### Continuous Integration -Build and Test cycles are run on every commit to every branch using [GitHub Actions](https://github.com/TBD54566975/ssi-sdk/actions). +Build and Test cycles are run on every commit to every branch +using [GitHub Actions](https://github.com/TBD54566975/ssi-sdk/actions). ## Contribution diff --git a/README.md b/README.md index f43b697e..75f42f70 100644 --- a/README.md +++ b/README.md @@ -114,10 +114,11 @@ contributions for additional examples. - [Decentralized Identifiers Example](example/did) - [Verifiable Credentials Example](example/vc) - Presentation Exchange Examples - - [Applying for an Apartment](example/usecase/apartment_application) - - [Employment Verification with a University Degree](example/usecase/employer_university_flow) - + - [Applying for an Apartment](example/usecase/apartment_application) + - [Employment Verification with a University Degree](example/usecase/employer_university_flow) + To run the examples use the following command + ``` go run example/did/did.go go run example/usecase/apartment_application/apartment_application.go diff --git a/credential/builder.go b/credential/builder.go index a6ff4d2d..d3a338f5 100644 --- a/credential/builder.go +++ b/credential/builder.go @@ -52,7 +52,7 @@ func (vcb *VerifiableCredentialBuilder) Build() (*VerifiableCredential, error) { } if err := vcb.VerifiableCredential.IsValid(); err != nil { - return nil, util.LoggingErrorMsg(err, "credential not ready to be built") + return nil, errors.Wrap(err, "credential not ready to be built") } return vcb.VerifiableCredential, nil diff --git a/credential/exchange/builder.go b/credential/exchange/builder.go index dbf84d0f..c4fd7a37 100644 --- a/credential/exchange/builder.go +++ b/credential/exchange/builder.go @@ -32,7 +32,7 @@ func (pdb *PresentationDefinitionBuilder) Build() (*PresentationDefinition, erro } if err := pdb.PresentationDefinition.IsValid(); err != nil { - return nil, util.LoggingErrorMsg(err, "presentation definition not ready to be built") + return nil, errors.Wrap(err, "presentation definition not ready to be built") } return pdb.PresentationDefinition, nil @@ -165,7 +165,7 @@ func (idb *InputDescriptorBuilder) Build() (*InputDescriptor, error) { } if err := idb.InputDescriptor.IsValid(); err != nil { - return nil, util.LoggingErrorMsg(err, "input descriptor not ready to be built") + return nil, errors.Wrap(err, "input descriptor not ready to be built") } return idb.InputDescriptor, nil @@ -266,7 +266,7 @@ func (psb *PresentationSubmissionBuilder) Build() (*PresentationSubmission, erro } if err := psb.PresentationSubmission.IsValid(); err != nil { - return nil, util.LoggingErrorMsg(err, "presentation submission not ready to be built") + return nil, errors.Wrap(err, "presentation submission not ready to be built") } return psb.PresentationSubmission, nil diff --git a/credential/exchange/builder_test.go b/credential/exchange/builder_test.go index d61c7e21..5420eb15 100644 --- a/credential/exchange/builder_test.go +++ b/credential/exchange/builder_test.go @@ -1,9 +1,9 @@ package exchange import ( + "encoding/json" "testing" - "encoding/json" "github.com/google/uuid" "github.com/stretchr/testify/assert" diff --git a/credential/exchange/request.go b/credential/exchange/request.go index e8ade995..28167326 100644 --- a/credential/exchange/request.go +++ b/credential/exchange/request.go @@ -8,7 +8,6 @@ import ( "github.com/google/uuid" "github.com/lestrrat-go/jwx/jwt" "github.com/pkg/errors" - "github.com/sirupsen/logrus" ) // PresentationRequestType represents wrappers for Presentation Definitions submitted as requests @@ -35,9 +34,7 @@ func BuildPresentationRequest(signer crypto.JWTSigner, pt PresentationRequestTyp case JWTRequest: return BuildJWTPresentationRequest(signer, def, target) default: - err := fmt.Errorf("presentation request type <%s> is not implemented", pt) - logrus.WithError(err).Error() - return nil, err + return nil, fmt.Errorf("presentation request type <%s> is not implemented", pt) } } @@ -57,7 +54,6 @@ func BuildJWTPresentationRequest(signer crypto.JWTSigner, def PresentationDefini func VerifyPresentationRequest(verifier crypto.JWTVerifier, pt PresentationRequestType, request []byte) (*PresentationDefinition, error) { err := fmt.Errorf("cannot verify unsupported presentation request type: %s", pt) if !IsSupportedPresentationRequestType(pt) { - logrus.WithError(err).Error() return nil, err } switch pt { @@ -73,27 +69,19 @@ func VerifyPresentationRequest(verifier crypto.JWTVerifier, pt PresentationReque func VerifyJWTPresentationRequest(verifier crypto.JWTVerifier, request []byte) (*PresentationDefinition, error) { parsed, err := verifier.VerifyAndParseJWT(string(request)) if err != nil { - err := errors.Wrap(err, "could not verify and parse jwt presentation request") - logrus.WithError(err).Error() - return nil, err + return nil, errors.Wrap(err, "could not verify and parse jwt presentation request") } presDefGeneric, ok := parsed.Get(PresentationDefinitionKey) if !ok { - err := fmt.Errorf("presentation definition key<%s> not found in token", PresentationDefinitionKey) - logrus.WithError(err).Error() - return nil, err + return nil, fmt.Errorf("presentation definition key<%s> not found in token", PresentationDefinitionKey) } presDefBytes, err := json.Marshal(presDefGeneric) if err != nil { - err := errors.Wrap(err, "could not marshal token into bytes for presentation definition") - logrus.WithError(err).Error() - return nil, err + return nil, errors.Wrap(err, "could not marshal token into bytes for presentation definition") } var def PresentationDefinition if err := json.Unmarshal(presDefBytes, &def); err != nil { - err := errors.Wrap(err, "could not unmarshal token into presentation definition") - logrus.WithError(err).Error() - return nil, err + return nil, errors.Wrap(err, "could not unmarshal token into presentation definition") } return &def, nil } diff --git a/credential/exchange/schema.go b/credential/exchange/schema.go index 94c49f8e..044a0d2a 100644 --- a/credential/exchange/schema.go +++ b/credential/exchange/schema.go @@ -1,11 +1,9 @@ package exchange import ( + "github.com/TBD54566975/ssi-sdk/schema" "github.com/goccy/go-json" "github.com/pkg/errors" - "github.com/sirupsen/logrus" - - "github.com/TBD54566975/ssi-sdk/schema" ) const ( @@ -29,8 +27,7 @@ func IsValidPresentationDefinition(definition PresentationDefinition) error { return errors.Wrap(err, "could not get presentation definition schema") } if err = schema.IsJSONValidAgainstSchema(string(jsonBytes), s); err != nil { - logrus.WithError(err).Error("presentation definition not valid against schema") - return err + return errors.New("presentation definition not valid against schema") } return nil } @@ -46,8 +43,7 @@ func IsValidPresentationDefinitionEnvelope(definition PresentationDefinitionEnve return errors.Wrap(err, "could not get presentation definition schema") } if err = schema.IsJSONValidAgainstSchema(string(jsonBytes), s); err != nil { - logrus.WithError(err).Error("presentation definition not valid against schema") - return err + return errors.Wrap(err, "presentation definition not valid against schema") } return nil } @@ -68,8 +64,7 @@ func IsValidPresentationSubmission(submission PresentationSubmission) error { return errors.Wrap(err, "could not get presentation submission schema") } if err = schema.IsJSONValidAgainstSchema(string(jsonBytes), s); err != nil { - logrus.WithError(err).Error("submission declaration not valid against schema") - return err + return errors.Wrap(err, "submission declaration not valid against schema") } return nil } @@ -85,8 +80,7 @@ func IsValidDefinitionClaimFormatDesignation(format ClaimFormat) error { return errors.Wrap(err, "could not get claim format schema") } if err = schema.IsJSONValidAgainstSchema(string(jsonBytes), s); err != nil { - logrus.WithError(err).Error("format declaration not valid against schema") - return err + return errors.Wrap(err, "format declaration not valid against schema") } return nil } @@ -102,8 +96,7 @@ func IsValidSubmissionRequirement(requirement SubmissionRequirement) error { return errors.Wrap(err, "could not get submission requirement schema") } if err = schema.IsJSONValidAgainstSchema(string(jsonBytes), s); err != nil { - logrus.WithError(err).Error("submission requirement not valid against schema") - return err + return errors.Wrap(err, "submission requirement not valid against schema") } return nil } @@ -124,8 +117,7 @@ func AreValidSubmissionRequirements(requirements []SubmissionRequirement) error return errors.Wrap(err, "could not get submission requirements schema") } if err = schema.IsJSONValidAgainstSchema(string(jsonBytes), s); err != nil { - logrus.WithError(err).Error("submission requirements not valid against schema") - return err + return errors.Wrap(err, "submission requirements not valid against schema") } return nil } diff --git a/credential/exchange/submission.go b/credential/exchange/submission.go index 624bbd66..0d6290a5 100644 --- a/credential/exchange/submission.go +++ b/credential/exchange/submission.go @@ -6,16 +6,14 @@ import ( "regexp" "strings" + "github.com/TBD54566975/ssi-sdk/credential" + "github.com/TBD54566975/ssi-sdk/credential/signing" "github.com/TBD54566975/ssi-sdk/crypto" + "github.com/TBD54566975/ssi-sdk/util" "github.com/goccy/go-json" "github.com/google/uuid" "github.com/oliveagle/jsonpath" "github.com/pkg/errors" - "github.com/sirupsen/logrus" - - "github.com/TBD54566975/ssi-sdk/credential" - "github.com/TBD54566975/ssi-sdk/credential/signing" - "github.com/TBD54566975/ssi-sdk/util" ) // EmbedTarget describes where a presentation_submission is located in an object model @@ -128,29 +126,24 @@ func (pc *PresentationClaim) GetClaimJSON() (map[string]interface{}, error) { // may include an analog method for LD suites. func BuildPresentationSubmission(signer crypto.JWTSigner, def PresentationDefinition, claims []PresentationClaim, et EmbedTarget) ([]byte, error) { if !IsSupportedEmbedTarget(et) { - err := fmt.Errorf("unsupported presentation submission embed target type: %s", et) - logrus.WithError(err).Error() - return nil, err + return nil, fmt.Errorf("unsupported presentation submission embed target type: %s", et) + } + normalizedClaims, err := normalizePresentationClaims(claims) + if err != nil { + return nil, errors.Wrap(err, "failed to normalize some presentation claims") } - normalizedClaims := normalizePresentationClaims(claims) if len(normalizedClaims) == 0 { - err := errors.New("no claims remain after normalization; cannot continue processing") - logrus.WithError(err).Error() - return nil, err + return nil, errors.New("no claims remain after normalization; cannot continue processing") } switch et { case JWTVPTarget: vpSubmission, err := BuildPresentationSubmissionVP(def, normalizedClaims) if err != nil { - err := errors.Wrap(err, "unable to fulfill presentation definition with given credentials") - logrus.WithError(err).Error() - return nil, err + return nil, errors.Wrap(err, "unable to fulfill presentation definition with given credentials") } return signing.SignVerifiablePresentationJWT(signer, *vpSubmission) default: - err := fmt.Errorf("presentation submission embed target <%s> is not implemented", et) - logrus.WithError(err).Error() - return nil, err + return nil, fmt.Errorf("presentation submission embed target <%s> is not implemented", et) } } @@ -169,8 +162,9 @@ type NormalizedClaim struct { // normalizePresentationClaims takes a set of Presentation Claims and turns them into map[string]interface{} as // go-JSON representations. The claim format and signature algorithm type are noted as well. // This method is greedy, meaning it returns the set of claims it was able to normalize. -func normalizePresentationClaims(claims []PresentationClaim) []NormalizedClaim { +func normalizePresentationClaims(claims []PresentationClaim) ([]NormalizedClaim, error) { var normalizedClaims []NormalizedClaim + errs := util.NewAppendError() for _, claim := range claims { ae := util.NewAppendError() claimJSON, err := claim.GetClaimJSON() @@ -182,7 +176,7 @@ func normalizePresentationClaims(claims []PresentationClaim) []NormalizedClaim { ae.Append(err) } if ae.Error() != nil { - logrus.WithError(ae.Error()).Error("could not normalize claim") + errs.Append(fmt.Errorf("could not normalize claim: %s", ae.Error().Error())) continue } var id string @@ -196,7 +190,7 @@ func normalizePresentationClaims(claims []PresentationClaim) []NormalizedClaim { AlgOrProofType: claim.SignatureAlgorithmOrProofType, }) } - return normalizedClaims + return normalizedClaims, errs.Error() } // processedClaim represents a claim that has been processed for an input descriptor along with relevant @@ -232,35 +226,31 @@ func BuildPresentationSubmissionVP(def PresentationDefinition, claims []Normaliz // keep track of claims we've already added, to avoid duplicates seenClaims := make(map[string]int) for _, id := range def.InputDescriptors { - processedInputDescriptor, err := processInputDescriptor(id, claims) + processedID, err := processInputDescriptor(id, claims) if err != nil { - err := errors.Wrapf(err, "error processing input descriptor: %s", id.ID) - logrus.WithError(err).Error() - return nil, err + return nil, errors.Wrapf(err, "error processing input descriptor: %s", id.ID) } - if processedInputDescriptor == nil { - err := fmt.Errorf("input descrpitor<%s> could not be fulfilled; could not build a valid presentation submission", id.ID) - logrus.WithError(err).Error() - return nil, err + if processedID == nil { + return nil, fmt.Errorf("input descrpitor<%s> could not be fulfilled; could not build a valid presentation submission", id.ID) } // check if claim already exists. if it has, we won't duplicate the claim var currIndex int var claim map[string]interface{} - claimID := processedInputDescriptor.ClaimID + claimID := processedID.ClaimID if seen, ok := seenClaims[claimID]; ok { currIndex = seen } else { currIndex = claimIndex claimIndex++ - claim = processedInputDescriptor.Claim + claim = processedID.Claim seenClaims[claimID] = currIndex } processedClaims = append(processedClaims, processedClaim{ claim: claim, SubmissionDescriptor: SubmissionDescriptor{ - ID: processedInputDescriptor.ID, - Format: processedInputDescriptor.Format, + ID: processedID.ID, + Format: processedID.Format, Path: fmt.Sprintf("$.verifiableCredential[%d]", currIndex), }, }) @@ -273,9 +263,7 @@ func BuildPresentationSubmissionVP(def PresentationDefinition, claims []Normaliz // on the case we've seen the claim, we need to check as to not add a nil claim value if len(claim.claim) > 0 { if err := builder.AddVerifiableCredentials(claim.claim); err != nil { - err := errors.Wrap(err, "could not add claim value to verifiable presentation") - logrus.WithError(err).Error() - return nil, err + return nil, errors.Wrap(err, "could not add claim value to verifiable presentation") } } } @@ -312,20 +300,12 @@ type limitedInputDescriptor struct { // https://identity.foundation/presentation-exchange/#input-evaluation func processInputDescriptor(id InputDescriptor, claims []NormalizedClaim) (*processedInputDescriptor, error) { constraints := id.Constraints - // NOTE(gabe): the specification does not require input descriptors to have a constraint; however, - // without a constraint it is ambiguous what fulfilling an input descriptor means. As such, we fail processing - // until the specification provides more clarity. - // https://github.com/decentralized-identity/presentation-exchange/issues/361 if constraints == nil { - err := fmt.Errorf("unable to process input descriptor without constraints") - logrus.WithError(err).Error() - return nil, err + return nil, fmt.Errorf("unable to process input descriptor without constraints") } fields := constraints.Fields if len(fields) == 0 { - err := fmt.Errorf("unable to process input descriptor without fields: %s", id.ID) - logrus.WithError(err).Error() - return nil, err + return nil, fmt.Errorf("unable to process input descriptor without fields: %s", id.ID) } // bookkeeping to check whether we've fulfilled all required fields, and whether we need to limit disclosure @@ -339,10 +319,8 @@ func processInputDescriptor(id InputDescriptor, claims []NormalizedClaim) (*proc // first, reduce the set of claims that conform with the format required by the input descriptor filteredClaims := filterClaimsByFormat(claims, id.Format) if len(filteredClaims) == 0 { - err := fmt.Errorf("no claims match the required format, and signing alg/proof type requirements "+ + return nil, fmt.Errorf("no claims match the required format, and signing alg/proof type requirements "+ "for input descriptor: %s", id.ID) - logrus.WithError(err).Error() - return nil, err } // for the input descriptor to be successfully processed each field needs to yield a result for a given claim, @@ -383,9 +361,7 @@ func processInputDescriptor(id InputDescriptor, claims []NormalizedClaim) (*proc }, nil } } - err := fmt.Errorf("no claims could fulfill the input descriptor: %s", id.ID) - logrus.WithError(err).Error("could not fulfill input descriptor") - return nil, err + return nil, fmt.Errorf("no claims could fulfill the input descriptor: %s", id.ID) } // filterClaimsByFormat returns a set of claims that comply with a given ClaimFormat according to its @@ -484,21 +460,17 @@ func processInputDescriptorField(field Field, claimData map[string]interface{}) func canProcessDefinition(def PresentationDefinition) error { submissionRequirementsErr := errors.New("submission requirements feature not supported") if len(def.SubmissionRequirements) > 0 { - logrus.WithError(submissionRequirementsErr).Error("submission requirements present") return submissionRequirementsErr } for _, id := range def.InputDescriptors { if id.Constraints != nil { if len(id.Group) > 0 { - logrus.WithError(submissionRequirementsErr).Error("input descriptor group present") return submissionRequirementsErr } if len(id.Constraints.Fields) > 0 { for _, field := range id.Constraints.Fields { if field.Predicate != nil || field.Filter != nil { - err := errors.New("predicate feature not supported") - logrus.WithError(err).Error("predicate and/or filter present") - return err + return errors.New("predicate and filter features not supported") } } } @@ -506,22 +478,16 @@ func canProcessDefinition(def PresentationDefinition) error { } for _, id := range def.InputDescriptors { if hasRelationalConstraint(id.Constraints) { - err := errors.New("relational constraint feature not supported") - logrus.WithError(err).Error() - return err + return errors.New("relational constraint feature not supported") } } for _, id := range def.InputDescriptors { if id.Constraints != nil && id.Constraints.Statuses != nil { - err := errors.New("credential status constraint feature not supported") - logrus.WithError(err).Error() - return err + return errors.New("credential status constraint feature not supported") } } if def.Frame != nil { - err := errors.New("JSON-LD framing feature not supported") - logrus.WithError(err).Error() - return err + return errors.New("JSON-LD framing feature not supported") } return nil } diff --git a/credential/exchange/submission_test.go b/credential/exchange/submission_test.go index 5a2e36c6..64b3c218 100644 --- a/credential/exchange/submission_test.go +++ b/credential/exchange/submission_test.go @@ -89,7 +89,8 @@ func TestBuildPresentationSubmissionVP(t *testing.T) { LDPFormat: LDPVC.Ptr(), SignatureAlgorithmOrProofType: string(cryptosuite.JSONWebSignature2020), } - normalized := normalizePresentationClaims([]PresentationClaim{presentationClaim}) + normalized, err := normalizePresentationClaims([]PresentationClaim{presentationClaim}) + assert.NoError(tt, err) vp, err := BuildPresentationSubmissionVP(def, normalized) assert.NoError(tt, err) assert.NotEmpty(tt, vp) @@ -182,7 +183,8 @@ func TestBuildPresentationSubmissionVP(t *testing.T) { LDPFormat: LDPVC.Ptr(), SignatureAlgorithmOrProofType: string(cryptosuite.JSONWebSignature2020), } - normalized := normalizePresentationClaims([]PresentationClaim{presentationClaim}) + normalized, err := normalizePresentationClaims([]PresentationClaim{presentationClaim}) + assert.NoError(tt, err) vp, err := BuildPresentationSubmissionVP(def, normalized) assert.NoError(tt, err) assert.NotEmpty(tt, vp) @@ -255,7 +257,8 @@ func TestBuildPresentationSubmissionVP(t *testing.T) { SignatureAlgorithmOrProofType: string(crypto.EdDSA), } - normalized := normalizePresentationClaims([]PresentationClaim{presentationClaim, presentationClaimJWT}) + normalized, err := normalizePresentationClaims([]PresentationClaim{presentationClaim, presentationClaimJWT}) + assert.NoError(tt, err) vp, err := BuildPresentationSubmissionVP(def, normalized) assert.NoError(tt, err) assert.NotEmpty(tt, vp) @@ -315,7 +318,8 @@ func TestProcessInputDescriptor(t *testing.T) { LDPFormat: LDPVC.Ptr(), SignatureAlgorithmOrProofType: string(cryptosuite.JSONWebSignature2020), } - normalized := normalizePresentationClaims([]PresentationClaim{presentationClaim}) + normalized, err := normalizePresentationClaims([]PresentationClaim{presentationClaim}) + assert.NoError(tt, err) processed, err := processInputDescriptor(id, normalized) assert.NoError(tt, err) assert.NotEmpty(tt, processed) @@ -345,7 +349,8 @@ func TestProcessInputDescriptor(t *testing.T) { LDPFormat: LDPVC.Ptr(), SignatureAlgorithmOrProofType: string(cryptosuite.JSONWebSignature2020), } - normalized := normalizePresentationClaims([]PresentationClaim{presentationClaim}) + normalized, err := normalizePresentationClaims([]PresentationClaim{presentationClaim}) + assert.NoError(tt, err) processed, err := processInputDescriptor(id, normalized) assert.NoError(tt, err) assert.NotEmpty(tt, processed) @@ -375,8 +380,9 @@ func TestProcessInputDescriptor(t *testing.T) { LDPFormat: LDPVC.Ptr(), SignatureAlgorithmOrProofType: string(cryptosuite.JSONWebSignature2020), } - normalized := normalizePresentationClaims([]PresentationClaim{presentationClaim}) - _, err := processInputDescriptor(id, normalized) + normalized, err := normalizePresentationClaims([]PresentationClaim{presentationClaim}) + assert.NoError(tt, err) + _, err = processInputDescriptor(id, normalized) assert.Error(tt, err) assert.Contains(tt, err.Error(), "no claims could fulfill the input descriptor") }) @@ -406,8 +412,9 @@ func TestProcessInputDescriptor(t *testing.T) { LDPFormat: LDPVC.Ptr(), SignatureAlgorithmOrProofType: string(cryptosuite.JSONWebSignature2020), } - normalized := normalizePresentationClaims([]PresentationClaim{presentationClaim}) - _, err := processInputDescriptor(id, normalized) + normalized, err := normalizePresentationClaims([]PresentationClaim{presentationClaim}) + assert.NoError(tt, err) + _, err = processInputDescriptor(id, normalized) assert.Error(tt, err) assert.Contains(tt, err.Error(), "no claims match the required format, and signing alg/proof type requirements") }) @@ -437,7 +444,8 @@ func TestProcessInputDescriptor(t *testing.T) { LDPFormat: LDPVC.Ptr(), SignatureAlgorithmOrProofType: string(cryptosuite.JSONWebSignature2020), } - normalized := normalizePresentationClaims([]PresentationClaim{presentationClaim}) + normalized, err := normalizePresentationClaims([]PresentationClaim{presentationClaim}) + assert.NoError(tt, err) processed, err := processInputDescriptor(id, normalized) assert.NoError(tt, err) assert.NotEmpty(tt, processed) @@ -479,7 +487,7 @@ func TestCanProcessDefinition(tt *testing.T) { } err := canProcessDefinition(def) assert.Error(tt, err) - assert.Contains(tt, err.Error(), "predicate feature not supported") + assert.Contains(tt, err.Error(), "predicate and filter features not supported") }) tt.Run("With Relational Constraint", func(tt *testing.T) { @@ -685,7 +693,8 @@ func TestNormalizePresentationClaims(t *testing.T) { SignatureAlgorithmOrProofType: string(crypto.EdDSA), } - normalized := normalizePresentationClaims([]PresentationClaim{presentationClaim}) + normalized, err := normalizePresentationClaims([]PresentationClaim{presentationClaim}) + assert.NoError(tt, err) assert.NotEmpty(tt, normalized) assert.True(tt, len(normalized) == 1) assert.NotEmpty(tt, normalized[0].Data) @@ -703,7 +712,8 @@ func TestNormalizePresentationClaims(t *testing.T) { SignatureAlgorithmOrProofType: string(cryptosuite.JSONWebSignature2020), } - normalized := normalizePresentationClaims([]PresentationClaim{presentationClaim}) + normalized, err := normalizePresentationClaims([]PresentationClaim{presentationClaim}) + assert.NoError(tt, err) assert.NotEmpty(tt, normalized) assert.True(tt, len(normalized) == 1) assert.NotEmpty(tt, normalized[0].Data) @@ -721,7 +731,8 @@ func TestNormalizePresentationClaims(t *testing.T) { SignatureAlgorithmOrProofType: string(cryptosuite.JSONWebSignature2020), } - normalized := normalizePresentationClaims([]PresentationClaim{presentationClaim}) + normalized, err := normalizePresentationClaims([]PresentationClaim{presentationClaim}) + assert.NoError(tt, err) assert.NotEmpty(tt, normalized) assert.True(tt, len(normalized) == 1) assert.NotEmpty(tt, normalized[0].Data) diff --git a/credential/exchange/verification.go b/credential/exchange/verification.go index 2706d36e..7c4aeca1 100644 --- a/credential/exchange/verification.go +++ b/credential/exchange/verification.go @@ -2,18 +2,17 @@ package exchange import ( "fmt" - credutil "github.com/TBD54566975/ssi-sdk/credential/util" "strings" - "github.com/TBD54566975/ssi-sdk/crypto" - "github.com/goccy/go-json" - "github.com/oliveagle/jsonpath" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" + credutil "github.com/TBD54566975/ssi-sdk/credential/util" "github.com/TBD54566975/ssi-sdk/credential" "github.com/TBD54566975/ssi-sdk/credential/signing" + "github.com/TBD54566975/ssi-sdk/crypto" "github.com/TBD54566975/ssi-sdk/util" + "github.com/goccy/go-json" + "github.com/oliveagle/jsonpath" + "github.com/pkg/errors" ) // VerifyPresentationSubmission verifies a presentation submission for both signature validity and correctness @@ -23,28 +22,20 @@ import ( // may include an analog method for LD suites. func VerifyPresentationSubmission(verifier crypto.JWTVerifier, et EmbedTarget, def PresentationDefinition, submission []byte) error { if err := canProcessDefinition(def); err != nil { - err := errors.Wrap(err, "feature not supported in processing given presentation definition") - logrus.WithError(err).Error("not able to verify presentation submission") - return err + return errors.Wrap(err, "not able to verify submission; feature not supported") } if !IsSupportedEmbedTarget(et) { - err := fmt.Errorf("unsupported presentation submission embed target type: %s", et) - logrus.WithError(err).Error() - return err + return fmt.Errorf("unsupported presentation submission embed target type: %s", et) } switch et { case JWTVPTarget: vp, err := signing.VerifyVerifiablePresentationJWT(verifier, string(submission)) if err != nil { - err := errors.Wrap(err, "verification of the presentation submission failed") - logrus.WithError(err).Error() - return err + return errors.Wrap(err, "verification of the presentation submission failed") } return VerifyPresentationSubmissionVP(def, *vp) default: - err := fmt.Errorf("presentation submission embed target <%s> is not implemented", et) - logrus.WithError(err).Error() - return err + return fmt.Errorf("presentation submission embed target <%s> is not implemented", et) } } @@ -53,28 +44,20 @@ func VerifyPresentationSubmission(verifier crypto.JWTVerifier, et EmbedTarget, d // TODO(gabe) handle signature validation of submission claims https://github.com/TBD54566975/ssi-sdk/issues/71 func VerifyPresentationSubmissionVP(def PresentationDefinition, vp credential.VerifiablePresentation) error { if err := vp.IsValid(); err != nil { - err := errors.Wrap(err, "presentation submission does not contain a valid VP") - logrus.WithError(err).Error() - return err + return errors.Wrap(err, "presentation submission does not contain a valid VP") } // first, validate the presentation submission in the VP submission, err := toPresentationSubmission(vp.PresentationSubmission) if err != nil { - err := errors.Wrap(err, "unable to parse presentation submission from verifiable presentation") - logrus.WithError(err).Error() - return err + return errors.Wrap(err, "unable to parse presentation submission from verifiable presentation") } - if err := submission.IsValid(); err != nil { - err := errors.Wrap(err, "invalid presentation submission in provided verifiable presentation") - logrus.WithError(err).Error() - return err + if err = submission.IsValid(); err != nil { + return errors.Wrap(err, "invalid presentation submission in provided verifiable presentation") } if submission.DefinitionID != def.ID { - err := fmt.Errorf("mismatched between presentation definition ID<%s> and submission's definition ID<%s>", + return fmt.Errorf("mismatched between presentation definition ID<%s> and submission's definition ID<%s>", def.ID, submission.DefinitionID) - logrus.WithError(err).Error() - return err } // index submission descriptors by id of the input descriptor @@ -86,18 +69,14 @@ func VerifyPresentationSubmissionVP(def PresentationDefinition, vp credential.Ve // turn the vp into JSON so we can use the paths from the submission descriptor to resolve each claim vpJSON, err := util.ToJSONMap(vp) if err != nil { - err := errors.Wrap(err, "could not turn VP into JSON representation") - logrus.WithError(err).Error() - return err + return errors.Wrap(err, "could not turn VP into JSON representation") } // validate each input descriptor is fulfilled for _, inputDescriptor := range def.InputDescriptors { submissionDescriptor, ok := submissionDescriptorLookup[inputDescriptor.ID] if !ok { - err := fmt.Errorf("unfulfilled input descriptor<%s>; submission not valid", inputDescriptor.ID) - logrus.WithError(err).Error() - return err + return fmt.Errorf("unfulfilled input descriptor<%s>; submission not valid", inputDescriptor.ID) } // if the format on the submitted claim does not match the input descriptor, we cannot process @@ -110,24 +89,18 @@ func VerifyPresentationSubmissionVP(def PresentationDefinition, vp credential.Ve // TODO(gabe) support nested paths in presentation submissions // https://github.com/TBD54566975/ssi-sdk/issues/73 if submissionDescriptor.PathNested != nil { - err := fmt.Errorf("submission with nested paths not supported: %s", submissionDescriptor.ID) - logrus.WithError(err).Error() - return err + return fmt.Errorf("submission with nested paths not supported: %s", submissionDescriptor.ID) } // resolve the claim from the JSON path expression in the submission descriptor claim, err := jsonpath.JsonPathLookup(vpJSON, submissionDescriptor.Path) if err != nil { - err := errors.Wrapf(err, "could not resolve claim from submission descriptor<%s> with path: %s", + return errors.Wrapf(err, "could not resolve claim from submission descriptor<%s> with path: %s", submissionDescriptor.ID, submissionDescriptor.Path) - logrus.WithError(err).Error() - return err } submittedClaim, err := credutil.ClaimAsJSON(claim) if err != nil { - err := errors.Wrapf(err, "getting claim as go-json: <%s>", claim) - logrus.WithError(err).Error() - return err + return errors.Wrapf(err, "getting claim as go-json: <%s>", claim) } // verify the submitted claim complies with the input descriptor @@ -140,10 +113,8 @@ func VerifyPresentationSubmissionVP(def PresentationDefinition, vp credential.Ve // TODO(gabe) consider enforcing limited disclosure if present // for each field we need to verify at least one path matches for _, field := range inputDescriptor.Constraints.Fields { - if err := findMatchingPath(submittedClaim, field.Path); err != nil { - err := errors.Wrapf(err, "input descriptor<%s> not fulfilled for field: %s", inputDescriptor.ID, field.ID) - logrus.WithError(err).Error() - return err + if err = findMatchingPath(submittedClaim, field.Path); err != nil { + return errors.Wrapf(err, "input descriptor<%s> not fulfilled for field: %s", inputDescriptor.ID, field.ID) } } } @@ -156,7 +127,7 @@ func toPresentationSubmission(maybePresentationSubmission interface{}) (*Present return nil, err } var submission PresentationSubmission - if err := json.Unmarshal(bytes, &submission); err != nil { + if err = json.Unmarshal(bytes, &submission); err != nil { return nil, err } return &submission, nil @@ -168,7 +139,5 @@ func findMatchingPath(claim interface{}, paths []string) error { return nil } } - err := errors.New("matching path for claim could not be found") - logrus.WithError(err).Error() - return err + return errors.New("matching path for claim could not be found") } diff --git a/credential/manifest/builder.go b/credential/manifest/builder.go index ef3029a8..36f5c529 100644 --- a/credential/manifest/builder.go +++ b/credential/manifest/builder.go @@ -34,7 +34,7 @@ func (cmb *CredentialManifestBuilder) Build() (*CredentialManifest, error) { } if err := cmb.CredentialManifest.IsValid(); err != nil { - return nil, util.LoggingErrorMsg(err, "credential manifest not ready to be built") + return nil, errors.Wrap(err, "credential manifest not ready to be built") } return cmb.CredentialManifest, nil @@ -152,7 +152,7 @@ func (cab *CredentialApplicationBuilder) Build() (*CredentialApplication, error) } if err := cab.CredentialApplication.IsValid(); err != nil { - return nil, util.LoggingErrorMsg(err, "credential application not ready to be built") + return nil, errors.Wrap(err, "credential application not ready to be built") } return cab.CredentialApplication, nil @@ -224,7 +224,7 @@ func (crb *CredentialResponseBuilder) Build() (*CredentialResponse, error) { } if err := crb.CredentialResponse.IsValid(); err != nil { - return nil, util.LoggingErrorMsg(err, "credential response not ready to be built") + return nil, errors.Wrap(err, "credential response not ready to be built") } return crb.CredentialResponse, nil diff --git a/credential/manifest/schema.go b/credential/manifest/schema.go index 09f656bc..9e9f887e 100644 --- a/credential/manifest/schema.go +++ b/credential/manifest/schema.go @@ -1,11 +1,9 @@ package manifest import ( + "github.com/TBD54566975/ssi-sdk/schema" "github.com/goccy/go-json" "github.com/pkg/errors" - "github.com/sirupsen/logrus" - - "github.com/TBD54566975/ssi-sdk/schema" ) const ( @@ -26,8 +24,7 @@ func IsValidCredentialManifest(manifest CredentialManifest) error { return errors.Wrap(err, "could not get credential manifest schema") } if err = schema.IsJSONValidAgainstSchema(string(jsonBytes), s); err != nil { - logrus.WithError(err).Errorf("credential manifest not valid against schema") - return err + return errors.Wrap(err, "credential manifest not valid against schema") } return nil } @@ -43,8 +40,7 @@ func IsValidCredentialApplication(application CredentialApplication) error { return errors.Wrap(err, "could not get credential application schema") } if err = schema.IsJSONValidAgainstSchema(string(jsonBytes), s); err != nil { - logrus.WithError(err).Error("credential application not valid against schema") - return err + return errors.Wrap(err, "credential application not valid against schema") } return nil } @@ -60,8 +56,7 @@ func IsValidCredentialResponse(response CredentialResponse) error { return errors.Wrap(err, "could not get credential response schema") } if err = schema.IsJSONValidAgainstSchema(string(jsonBytes), s); err != nil { - logrus.WithError(err).Error("credential response not valid against schema") - return err + return errors.Wrap(err, "credential response not valid against schema") } return nil } @@ -82,8 +77,7 @@ func AreValidOutputDescriptors(descriptors []OutputDescriptor) error { return errors.Wrap(err, "could not get output descriptors schema") } if err = schema.IsJSONValidAgainstSchema(string(jsonBytes), s); err != nil { - logrus.WithError(err).Error("output descriptors not valid against schema") - return err + return errors.Wrap(err, "output descriptors not valid against schema") } return nil } diff --git a/credential/manifest/testdata/cm-response-example-2.json b/credential/manifest/testdata/cm-response-example-2.json index 51a08b2c..ba9f005f 100644 --- a/credential/manifest/testdata/cm-response-example-2.json +++ b/credential/manifest/testdata/cm-response-example-2.json @@ -5,6 +5,9 @@ "application_id": "b6385066-147c-49d0-9783-261a2154b1fd", "denial": { "reason": "Input descriptors 1 and 3 use non-matching schemas.", - "input_descriptors": ["id-1", "id-3"] + "input_descriptors": [ + "id-1", + "id-3" + ] } } \ No newline at end of file diff --git a/credential/manifest/testdata/full-credential.json b/credential/manifest/testdata/full-credential.json index 1aea106e..231e8d29 100644 --- a/credential/manifest/testdata/full-credential.json +++ b/credential/manifest/testdata/full-credential.json @@ -1,18 +1,18 @@ { - "@context":"https://www.w3.org/2018/credentials/v1", - "id":"https://eu.com/claims/DriversLicense", - "type":[ + "@context": "https://www.w3.org/2018/credentials/v1", + "id": "https://eu.com/claims/DriversLicense", + "type": [ "EUDriversLicense" ], - "issuer":"did:example:123", - "issuanceDate":"2010-01-01T19:73:24Z", - "credentialSubject":{ - "id":"did:example:ebfeb1f712ebc6f1c276e12ec21", - "givenName":"ricky bobby", - "additionalName":"hank hill", - "familyName":"simpson", - "birthDate":"2009-01-03", - "postalAddress":"p sherman 42 wallaby way, sydney", - "taxID":"123" + "issuer": "did:example:123", + "issuanceDate": "2010-01-01T19:73:24Z", + "credentialSubject": { + "id": "did:example:ebfeb1f712ebc6f1c276e12ec21", + "givenName": "ricky bobby", + "additionalName": "hank hill", + "familyName": "simpson", + "birthDate": "2009-01-03", + "postalAddress": "p sherman 42 wallaby way, sydney", + "taxID": "123" } } \ No newline at end of file diff --git a/credential/manifest/testdata/full-manifest.json b/credential/manifest/testdata/full-manifest.json index 9fb27623..1069bf05 100644 --- a/credential/manifest/testdata/full-manifest.json +++ b/credential/manifest/testdata/full-manifest.json @@ -1,89 +1,89 @@ { - "id":"WA-DL-CLASS-A", - "issuer":{ + "id": "WA-DL-CLASS-A", + "issuer": { "id": "did:example:123" }, - "spec_version":"https://identity.foundation/credential-manifest/spec/v1.0.0/", - "output_descriptors":[ + "spec_version": "https://identity.foundation/credential-manifest/spec/v1.0.0/", + "output_descriptors": [ { - "id":"kyc_credential", - "schema":"https://compliance-is-kewl.com/json-schemas/kyc.json" + "id": "kyc_credential", + "schema": "https://compliance-is-kewl.com/json-schemas/kyc.json" } ], - "presentation_definition":{ - "id":"32f54163-7166-48f1-93d8-ff217bdb0653", - "name":"KYC Requirements", - "purpose":"purpose", - "format":{ - "jwt":{ - "alg":[ + "presentation_definition": { + "id": "32f54163-7166-48f1-93d8-ff217bdb0653", + "name": "KYC Requirements", + "purpose": "purpose", + "format": { + "jwt": { + "alg": [ "EdDSA" ] } }, - "input_descriptors":[ + "input_descriptors": [ { - "id":"kycid1", - "name":"Personal Info", - "constraints":{ - "subject_is_issuer":"required", - "fields":[ + "id": "kycid1", + "name": "Personal Info", + "constraints": { + "subject_is_issuer": "required", + "fields": [ { - "id":"givenName", - "path":[ + "id": "givenName", + "path": [ "$.credentialSubject.givenName" ], - "filter":{ - "type":"string", - "pattern":"[a-zA-Z \\-\\.].+" + "filter": { + "type": "string", + "pattern": "[a-zA-Z \\-\\.].+" } }, { - "id":"additionalName", - "path":[ + "id": "additionalName", + "path": [ "$.credentialSubject.additionalName" ], - "filter":{ - "type":"string", - "pattern":"[a-zA-Z \\-\\.].+" + "filter": { + "type": "string", + "pattern": "[a-zA-Z \\-\\.].+" } }, { - "id":"familyName", - "path":[ + "id": "familyName", + "path": [ "$.credentialSubject.familyName" ], - "filter":{ - "type":"string", - "pattern":"[a-zA-Z \\-\\.].+" + "filter": { + "type": "string", + "pattern": "[a-zA-Z \\-\\.].+" } }, { - "id":"birthDate", - "path":[ + "id": "birthDate", + "path": [ "$.credentialSubject.birthDate" ], - "filter":{ - "type":"string", - "format":"date" + "filter": { + "type": "string", + "format": "date" } }, { - "id":"postalAddress", - "path":[ + "id": "postalAddress", + "path": [ "$.credentialSubject.postalAddress" ], - "filter":{ - "type":"string" + "filter": { + "type": "string" } }, { - "id":"taxID", - "path":[ + "id": "taxID", + "path": [ "$.credentialSubject.taxID" ], - "filter":{ - "type":"string" + "filter": { + "type": "string" } } ] diff --git a/credential/rendering/schema.go b/credential/rendering/schema.go index c1018977..0562be65 100644 --- a/credential/rendering/schema.go +++ b/credential/rendering/schema.go @@ -1,11 +1,9 @@ package rendering import ( + "github.com/TBD54566975/ssi-sdk/schema" "github.com/goccy/go-json" "github.com/pkg/errors" - "github.com/sirupsen/logrus" - - "github.com/TBD54566975/ssi-sdk/schema" ) const ( @@ -25,8 +23,7 @@ func IsValidEntityStyle(esd EntityStyleDescriptor) error { return errors.Wrap(err, "could not get entity styles schema") } if err = schema.IsJSONValidAgainstSchema(string(jsonBytes), s); err != nil { - logrus.WithError(err).Error("entity style not valid against schema") - return err + return errors.Wrap(err, "entity style not valid against schema") } return nil } @@ -42,8 +39,7 @@ func IsValidDisplayMappingObject(dmo DisplayMappingObject) error { return errors.Wrap(err, "could not get display mapping object schema") } if err = schema.IsJSONValidAgainstSchema(string(jsonBytes), s); err != nil { - logrus.WithError(err).Error("display mapping object not valid against schema") - return err + return errors.Wrap(err, "display mapping object not valid against schema") } return nil } @@ -59,8 +55,7 @@ func IsValidLabeledDisplayMappingObject(ldmo LabeledDisplayMappingObject) error return errors.Wrap(err, "could not get labeled display mapping object schema") } if err = schema.IsJSONValidAgainstSchema(string(jsonBytes), s); err != nil { - logrus.WithError(err).Error("labeled display mapping object not valid against schema") - return err + return errors.Wrap(err, "labeled display mapping object not valid against schema") } return nil } diff --git a/credential/schema/vcjsonschema.go b/credential/schema/vcjsonschema.go index 92af943c..7f7bcadf 100644 --- a/credential/schema/vcjsonschema.go +++ b/credential/schema/vcjsonschema.go @@ -1,12 +1,10 @@ package schema import ( - "github.com/goccy/go-json" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" - "github.com/TBD54566975/ssi-sdk/credential" "github.com/TBD54566975/ssi-sdk/schema" + "github.com/goccy/go-json" + "github.com/pkg/errors" ) const ( @@ -25,10 +23,8 @@ func StringToVCJSONCredentialSchema(maybeVCJSONCredentialSchema string) (*VCJSON return nil, errors.Wrap(err, "could not marshal vc json schema's schema property") } maybeSchema := string(schemaBytes) - if err := schema.IsValidJSONSchema(maybeSchema); err != nil { - errMsg := "VC JSON Schema did not contain a valid JSON Schema" - logrus.WithError(err).Error(errMsg) - return nil, errors.Wrap(err, errMsg) + if err = schema.IsValidJSONSchema(maybeSchema); err != nil { + return nil, errors.Wrap(err, "vc json schema did not contain a valid JSON Schema") } return &vcs, nil } @@ -41,16 +37,12 @@ func IsValidCredentialSchema(maybeCredentialSchema string) error { return errors.Wrap(err, "could not get known schema for VC JSON Schema") } - if err := schema.IsJSONValidAgainstSchema(maybeCredentialSchema, vcJSONSchemaSchema); err != nil { - errMsg := "credential schema did not validate" - logrus.WithError(err).Error(errMsg) - return errors.Wrap(err, errMsg) + if err = schema.IsJSONValidAgainstSchema(maybeCredentialSchema, vcJSONSchemaSchema); err != nil { + return errors.Wrap(err, "credential schema did not validate") } - if _, err := StringToVCJSONCredentialSchema(maybeCredentialSchema); err != nil { - errMsg := "credential schema not valid" - logrus.WithError(err).Error(errMsg) - return errors.Wrap(err, errMsg) + if _, err = StringToVCJSONCredentialSchema(maybeCredentialSchema); err != nil { + return errors.Wrap(err, "credential schema not valid") } return nil @@ -82,9 +74,8 @@ func IsCredentialValidForSchema(cred credential.VerifiableCredential, s string) return err } subjectJSON := string(subjectBytes) - if err := schema.IsJSONValidAgainstSchema(subjectJSON, s); err != nil { - logrus.WithError(err).Error("credential not valid for schema") - return err + if err = schema.IsJSONValidAgainstSchema(subjectJSON, s); err != nil { + return errors.Wrap(err, "credential not valid for schema") } return nil } diff --git a/credential/signing/jwt.go b/credential/signing/jwt.go index dd4c58b6..89198edc 100644 --- a/credential/signing/jwt.go +++ b/credential/signing/jwt.go @@ -5,15 +5,13 @@ import ( "strconv" "time" + "github.com/TBD54566975/ssi-sdk/credential" "github.com/TBD54566975/ssi-sdk/crypto" "github.com/goccy/go-json" "github.com/google/uuid" "github.com/lestrrat-go/jwx/jwa" "github.com/lestrrat-go/jwx/jwt" "github.com/pkg/errors" - "github.com/sirupsen/logrus" - - "github.com/TBD54566975/ssi-sdk/credential" ) const ( @@ -33,11 +31,11 @@ func SignVerifiableCredentialJWT(signer crypto.JWTSigner, cred credential.Verifi expirationVal := cred.ExpirationDate if expirationVal != "" { var expirationDate = expirationVal - if unixTime, err := rfc3339ToUnix(expirationVal); err == nil { - expirationDate = string(unixTime) - } else { - logrus.WithError(err).Error("could not convert expiration date to unix time") + unixTime, err := rfc3339ToUnix(expirationVal) + if err != nil { + return nil, errors.Wrap(err, "could not convert expiration date to unix time") } + expirationDate = string(unixTime) if err := t.Set(jwt.ExpirationKey, expirationDate); err != nil { return nil, errors.Wrap(err, "could not set exp value") } @@ -51,7 +49,7 @@ func SignVerifiableCredentialJWT(signer crypto.JWTSigner, cred credential.Verifi if unixTime, err := rfc3339ToUnix(cred.IssuanceDate); err == nil { issuanceDate = string(unixTime) } else { - logrus.WithError(err).Error("could not convert iat to unix time; setting to present moment") + // could not convert iat to unix time; setting to present moment issuanceDate = strconv.FormatInt(time.Now().UTC().UnixNano(), 10) } @@ -79,8 +77,7 @@ func SignVerifiableCredentialJWT(signer crypto.JWTSigner, cred credential.Verifi signed, err := jwt.Sign(t, jwa.SignatureAlgorithm(signer.GetSigningAlgorithm()), signer.Key) if err != nil { - logrus.WithError(err).Error("could not sign JWT credential") - return nil, err + return nil, errors.Wrap(err, "could not sign JWT credential") } return signed, nil } @@ -112,10 +109,8 @@ func ParseVerifiableCredentialFromJWT(token string) (*credential.VerifiableCrede return nil, errors.Wrap(err, "could not marshal credential claim") } var cred credential.VerifiableCredential - if err := json.Unmarshal(vcBytes, &cred); err != nil { - errMsg := "could not reconstruct Verifiable Credential" - logrus.WithError(err).Error(errMsg) - return nil, errors.Wrap(err, errMsg) + if err = json.Unmarshal(vcBytes, &cred); err != nil { + return nil, errors.Wrap(err, "could not reconstruct Verifiable Credential") } // parse remaining JWT properties and set in the credential @@ -188,8 +183,7 @@ func SignVerifiablePresentationJWT(signer crypto.JWTSigner, pres credential.Veri signed, err := jwt.Sign(t, jwa.SignatureAlgorithm(signer.GetSigningAlgorithm()), signer.Key) if err != nil { - logrus.WithError(err).Error("could not sign JWT presentation") - return nil, err + return nil, errors.Wrap(err, "could not sign JWT presentation") } return signed, nil } @@ -201,9 +195,7 @@ func SignVerifiablePresentationJWT(signer crypto.JWTSigner, pres credential.Veri // decoded VerifiablePresentation object is returned. func VerifyVerifiablePresentationJWT(verifier crypto.JWTVerifier, token string) (*credential.VerifiablePresentation, error) { if err := verifier.VerifyJWT(token); err != nil { - errMsg := "could not verify JWT and its signature" - logrus.WithError(err).Error(errMsg) - return nil, errors.Wrap(err, errMsg) + return nil, errors.Wrap(err, "could not verify JWT and its signature") } return ParseVerifiablePresentationFromJWT(token) } @@ -226,10 +218,8 @@ func ParseVerifiablePresentationFromJWT(token string) (*credential.VerifiablePre return nil, errors.Wrap(err, "could not marshal vp claim") } var pres credential.VerifiablePresentation - if err := json.Unmarshal(vpBytes, &pres); err != nil { - errMsg := "could not reconstruct Verifiable Presentation" - logrus.WithError(err).Error(errMsg) - return nil, errors.Wrap(err, errMsg) + if err = json.Unmarshal(vpBytes, &pres); err != nil { + return nil, errors.Wrap(err, "could not reconstruct Verifiable Presentation") } // parse remaining JWT properties and set in the presentation diff --git a/credential/status/statuslist2021.go b/credential/status/statuslist2021.go index 3f1b021b..b83f1f37 100644 --- a/credential/status/statuslist2021.go +++ b/credential/status/statuslist2021.go @@ -10,12 +10,10 @@ import ( "strconv" "strings" - "github.com/bits-and-blooms/bitset" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" - "github.com/TBD54566975/ssi-sdk/credential" "github.com/TBD54566975/ssi-sdk/util" + "github.com/bits-and-blooms/bitset" + "github.com/pkg/errors" ) type StatusPurpose string @@ -59,12 +57,12 @@ type StatusList2021Credential struct { func GenerateStatusList2021Credential(id string, issuer string, purpose StatusPurpose, issuedCredentials []credential.VerifiableCredential) (*credential.VerifiableCredential, error) { statusListIndices, err := prepareCredentialsForStatusList(purpose, issuedCredentials) if err != nil { - return nil, util.LoggingErrorMsg(err, "could not generate status list credential") + return nil, errors.Wrap(err, "could not generate status list credential") } bitString, err := bitstringGeneration(statusListIndices) if err != nil { - return nil, util.LoggingErrorMsg(err, "could not generate bitstring for status list credential") + return nil, errors.Wrap(err, "could not generate bitstring for status list credential") } rlc := StatusList2021Credential{ @@ -76,29 +74,29 @@ func GenerateStatusList2021Credential(id string, issuer string, purpose StatusPu builder := credential.NewVerifiableCredentialBuilder() errMsgFragment := "could not generate status list credential: error setting " - if err := builder.SetID(id); err != nil { - return nil, util.LoggingErrorMsg(err, errMsgFragment+"id") + if err = builder.SetID(id); err != nil { + return nil, errors.Wrap(err, errMsgFragment+"id") } - if err := builder.SetIssuer(issuer); err != nil { - return nil, util.LoggingErrorMsg(err, errMsgFragment+"issuer") + if err = builder.SetIssuer(issuer); err != nil { + return nil, errors.Wrap(err, errMsgFragment+"issuer") } - if err := builder.AddContext(StatusList2021Context); err != nil { - return nil, util.LoggingErrorMsg(err, errMsgFragment+"context") + if err = builder.AddContext(StatusList2021Context); err != nil { + return nil, errors.Wrap(err, errMsgFragment+"context") } - if err := builder.AddType(StatusList2021CreddentialType); err != nil { - return nil, util.LoggingErrorMsg(err, errMsgFragment+"type") + if err = builder.AddType(StatusList2021CreddentialType); err != nil { + return nil, errors.Wrap(err, errMsgFragment+"type") } rlcJSON, err := util.ToJSONMap(rlc) if err != nil { - return nil, util.LoggingErrorMsg(err, "could not turn RLC to JSON") + return nil, errors.Wrap(err, "could not turn RLC to JSON") } - if err := builder.SetCredentialSubject(rlcJSON); err != nil { - return nil, util.LoggingErrorMsg(err, errMsgFragment+"subject") + if err = builder.SetCredentialSubject(rlcJSON); err != nil { + return nil, errors.Wrap(err, errMsgFragment+"subject") } statusListCredential, err := builder.Build() if err != nil { - return nil, util.LoggingErrorMsg(err, "could not build status list credential") + return nil, errors.Wrap(err, "could not build status list credential") } return statusListCredential, nil } @@ -152,11 +150,11 @@ func prepareCredentialsForStatusList(purpose StatusPurpose, credentials []creden func getStatusEntry(maybeCredentialStatus interface{}) (*StatusList2021Entry, error) { statusBytes, err := json.Marshal(maybeCredentialStatus) if err != nil { - return nil, util.LoggingErrorMsg(err, "could not marshal credential status property") + return nil, errors.Wrap(err, "could not marshal credential status property") } var statusEntry StatusList2021Entry - if err := json.Unmarshal(statusBytes, &statusEntry); err != nil { - return nil, util.LoggingErrorMsg(err, "could not unmarshal credential status property") + if err = json.Unmarshal(statusBytes, &statusEntry); err != nil { + return nil, errors.Wrap(err, "could not unmarshal credential status property") } return &statusEntry, util.IsValidStruct(statusEntry) } @@ -169,23 +167,19 @@ func bitstringGeneration(statusListCredentialIndices []string) (string, error) { // 1. Let bitstring be a list of bits with a minimum size of 16KB, where each bit is initialized to 0 (zero). b := bitset.New(16 * KB) - if len(statusListCredentialIndices) == 0 { - logrus.Info("creating a status list with no revoked credentials") - } else { - // 2. For each bit in bitstring, if there is a corresponding statusListIndex value in a revoked credential in - // issuedCredentials, set the bit to 1 (one), otherwise set the bit to 0 (zero). - for _, index := range statusListCredentialIndices { - indexInt, err := strconv.Atoi(index) - if indexInt < 0 || err != nil { - return "", fmt.Errorf("invalid status list index value, not a valid positive integer: %s", index) - } - indexValue := uint(indexInt) - if _, ok := duplicateCheck[indexValue]; ok { - return "", fmt.Errorf("duplicate status list index value found: %d", indexValue) - } - duplicateCheck[indexValue] = true - b.Set(indexValue) + // 2. For each bit in bitstring, if there is a corresponding statusListIndex value in a revoked credential in + // issuedCredentials, set the bit to 1 (one), otherwise set the bit to 0 (zero). + for _, index := range statusListCredentialIndices { + indexInt, err := strconv.Atoi(index) + if indexInt < 0 || err != nil { + return "", fmt.Errorf("invalid status list index value, not a valid positive integer: %s", index) + } + indexValue := uint(indexInt) + if _, ok := duplicateCheck[indexValue]; ok { + return "", fmt.Errorf("duplicate status list index value found: %d", indexValue) } + duplicateCheck[indexValue] = true + b.Set(indexValue) } bitstringBinary, err := b.MarshalBinary() @@ -197,11 +191,11 @@ func bitstringGeneration(statusListCredentialIndices []string) (string, error) { // base64-encoding [RFC4648] the result. var buf bytes.Buffer zw := gzip.NewWriter(&buf) - if _, err := zw.Write(bitstringBinary); err != nil { + if _, err = zw.Write(bitstringBinary); err != nil { return "", errors.Wrap(err, "could not compress status list bitstring using GZIP") } - if err := zw.Close(); err != nil { + if err = zw.Close(); err != nil { return "", errors.Wrap(err, "could not close gzip writer") } @@ -276,11 +270,11 @@ func ValidateCredentialInStatusList(credentialToValidate credential.VerifiableCr if err != nil { return false, errors.Wrapf(err, "could not marshal status credential<%s> subject value", statusCredential.ID) } - if err := json.Unmarshal(subjectBytes, &statusCredentialValue); err != nil { + if err = json.Unmarshal(subjectBytes, &statusCredentialValue); err != nil { return false, errors.Wrapf(err, "could not unmarshal status credential<%s> subject value into "+ "StatusList2021Credential", statusCredential.ID) } - if err := util.IsValidStruct(statusCredentialValue); err != nil { + if err = util.IsValidStruct(statusCredentialValue); err != nil { return false, errors.Wrapf(err, "credential<%s> is not a valid status credential", statusCredential.ID) } if statusPurpose != statusCredentialValue.StatusPurpose { diff --git a/crypto/jwk.go b/crypto/jwk.go index 03276e9a..f37c642f 100644 --- a/crypto/jwk.go +++ b/crypto/jwk.go @@ -13,7 +13,6 @@ import ( "github.com/lestrrat-go/jwx/jwk" "github.com/lestrrat-go/jwx/x25519" "github.com/pkg/errors" - "github.com/sirupsen/logrus" ) // PrivateKeyJWK complies with RFC7517 https://datatracker.ietf.org/doc/html/rfc7517 @@ -314,9 +313,7 @@ func jwkKeyFromSECP256k1PrivateKey(key secp256k1.PrivateKey) (jwk.Key, error) { ecdsaPrivKey := key.ToECDSA() secp256k1JWK := jwk.NewECDSAPrivateKey() if err := secp256k1JWK.FromRaw(ecdsaPrivKey); err != nil { - err = errors.Wrap(err, "failed to generate secp256k1 jwk") - logrus.WithError(err).Error("could not extract key from raw private key") - return nil, err + return nil, errors.Wrap(err, "failed to generate secp256k1 jwk") } return secp256k1JWK, nil } @@ -326,9 +323,7 @@ func jwkFromSECP256k1PrivateKey(key secp256k1.PrivateKey) (*PublicKeyJWK, *Priva ecdsaPrivKey := key.ToECDSA() secp256k1JWK := jwk.NewECDSAPrivateKey() if err := secp256k1JWK.FromRaw(ecdsaPrivKey); err != nil { - err = errors.Wrap(err, "failed to generate secp256k1 jwk") - logrus.WithError(err).Error("could not extract key from raw private key") - return nil, nil, err + return nil, nil, errors.Wrap(err, "failed to generate secp256k1 jwk") } kty := secp256k1JWK.KeyType().String() crv := secp256k1JWK.Crv().String() @@ -356,9 +351,7 @@ func jwkKeyFromSECP256k1PublicKey(key secp256k1.PublicKey) (jwk.Key, error) { ecdsaPubKey := key.ToECDSA() secp256k1JWK := jwk.NewECDSAPublicKey() if err := secp256k1JWK.FromRaw(ecdsaPubKey); err != nil { - err = errors.Wrap(err, "failed to generate secp256k1 jwk") - logrus.WithError(err).Error("could not extract key from raw public key") - return nil, err + return nil, errors.Wrap(err, "failed to generate secp256k1 jwk") } return secp256k1JWK, nil } @@ -368,9 +361,7 @@ func jwkFromSECP256k1PublicKey(key secp256k1.PublicKey) (*PublicKeyJWK, error) { ecdsaPubKey := key.ToECDSA() secp256k1JWK := jwk.NewECDSAPublicKey() if err := secp256k1JWK.FromRaw(ecdsaPubKey); err != nil { - err = errors.Wrap(err, "failed to generate secp256k1 jwk") - logrus.WithError(err).Error("could not extract key from raw public key") - return nil, err + return nil, errors.Wrap(err, "failed to generate secp256k1 jwk") } kty := secp256k1JWK.KeyType().String() crv := secp256k1JWK.Crv().String() @@ -388,9 +379,7 @@ func jwkFromSECP256k1PublicKey(key secp256k1.PublicKey) (*PublicKeyJWK, error) { func jwkKeyFromECDSAPrivateKey(key ecdsa.PrivateKey) (jwk.Key, error) { ecdsaKey := jwk.NewECDSAPrivateKey() if err := ecdsaKey.FromRaw(&key); err != nil { - err = errors.Wrap(err, "failed to generate ecdsa jwk") - logrus.WithError(err).Error("could not extract key from raw private key") - return nil, err + return nil, errors.Wrap(err, "failed to generate ecdsa jwk") } return ecdsaKey, nil } @@ -399,9 +388,7 @@ func jwkKeyFromECDSAPrivateKey(key ecdsa.PrivateKey) (jwk.Key, error) { func jwkFromECDSAPrivateKey(key ecdsa.PrivateKey) (*PublicKeyJWK, *PrivateKeyJWK, error) { ecdsaKey := jwk.NewECDSAPrivateKey() if err := ecdsaKey.FromRaw(&key); err != nil { - err = errors.Wrap(err, "failed to generate ecdsa jwk") - logrus.WithError(err).Error("could not extract key from raw private key") - return nil, nil, err + return nil, nil, errors.Wrap(err, "failed to generate ecdsa jwk") } kty := ecdsaKey.KeyType().String() crv := ecdsaKey.Crv().String() @@ -428,9 +415,7 @@ func jwkFromECDSAPrivateKey(key ecdsa.PrivateKey) (*PublicKeyJWK, *PrivateKeyJWK func jwkKeyFromECDSAPublicKey(key ecdsa.PublicKey) (jwk.Key, error) { ecdsaKey := jwk.NewECDSAPublicKey() if err := ecdsaKey.FromRaw(&key); err != nil { - err = errors.Wrap(err, "failed to generate ecdsa jwk") - logrus.WithError(err).Error("could not extract key from raw private key") - return nil, err + return nil, errors.Wrap(err, "failed to generate ecdsa jwk") } return ecdsaKey, nil } @@ -439,9 +424,7 @@ func jwkKeyFromECDSAPublicKey(key ecdsa.PublicKey) (jwk.Key, error) { func jwkFromECDSAPublicKey(key ecdsa.PublicKey) (*PublicKeyJWK, error) { ecdsaKey := jwk.NewECDSAPublicKey() if err := ecdsaKey.FromRaw(&key); err != nil { - err = errors.Wrap(err, "failed to generate ecdsa jwk") - logrus.WithError(err).Error("could not extract key from raw private key") - return nil, err + return nil, errors.Wrap(err, "failed to generate ecdsa jwk") } kty := ecdsaKey.KeyType().String() crv := ecdsaKey.Crv().String() diff --git a/crypto/jwt.go b/crypto/jwt.go index 5130d5e1..465e84b2 100644 --- a/crypto/jwt.go +++ b/crypto/jwt.go @@ -11,7 +11,6 @@ import ( "github.com/lestrrat-go/jwx/jws" "github.com/lestrrat-go/jwx/jwt" "github.com/pkg/errors" - "github.com/sirupsen/logrus" ) // JWTSigner is a struct that contains the key and algorithm used to sign JWTs @@ -163,9 +162,7 @@ func (s *JWTSigner) SignJWT(kvs map[string]interface{}) ([]byte, error) { for k, v := range kvs { if err := t.Set(k, v); err != nil { - err = errors.Wrapf(err, "could not set %s to value: %v", k, v) - logrus.WithError(err).Error("could not sign JWT") - return nil, err + return nil, errors.Wrapf(err, "could not set %s to value: %v", k, v) } } return jwt.Sign(t, jwa.SignatureAlgorithm(s.GetSigningAlgorithm()), s.Key) @@ -175,8 +172,7 @@ func (s *JWTSigner) SignJWT(kvs map[string]interface{}) ([]byte, error) { func (*JWTSigner) ParseJWT(token string) (jwt.Token, error) { parsed, err := jwt.Parse([]byte(token)) if err != nil { - logrus.WithError(err).Error("could not parse JWT") - return nil, err + return nil, errors.Wrap(err, "could not parse JWT") } return parsed, nil } @@ -184,8 +180,7 @@ func (*JWTSigner) ParseJWT(token string) (jwt.Token, error) { // VerifyJWT parses a token given the verifier's known algorithm and key, and returns an error, which is nil upon success func (v *JWTVerifier) VerifyJWT(token string) error { if _, err := jwt.Parse([]byte(token), jwt.WithVerify(jwa.SignatureAlgorithm(v.Algorithm()), v.Key)); err != nil { - logrus.WithError(err).Error("could not verify JWT") - return err + return errors.Wrap(err, "could not verify JWT") } return nil } @@ -194,8 +189,7 @@ func (v *JWTVerifier) VerifyJWT(token string) error { func (*JWTVerifier) ParseJWT(token string) (jwt.Token, error) { parsed, err := jwt.Parse([]byte(token)) if err != nil { - logrus.WithError(err).Error("could not parse JWT") - return nil, err + return nil, errors.Wrap(err, "could not parse JWT") } return parsed, nil } @@ -204,8 +198,7 @@ func (*JWTVerifier) ParseJWT(token string) (jwt.Token, error) { func (*JWTVerifier) ParseJWS(token string) (*jws.Signature, error) { parsed, err := jws.Parse([]byte(token)) if err != nil { - logrus.WithError(err).Error("could not parse JWS") - return nil, err + return nil, errors.Wrap(err, "could not parse JWS") } signatures := parsed.Signatures() if len(signatures) != 1 { @@ -218,8 +211,7 @@ func (*JWTVerifier) ParseJWS(token string) (*jws.Signature, error) { func (v *JWTVerifier) VerifyAndParseJWT(token string) (jwt.Token, error) { parsed, err := jwt.Parse([]byte(token), jwt.WithVerify(jwa.SignatureAlgorithm(v.Algorithm()), v.Key)) if err != nil { - logrus.WithError(err).Error("could not parse and verify JWT") - return nil, err + return nil, errors.Wrap(err, "could not parse and verify JWT") } return parsed, nil } diff --git a/cryptosuite/jsonwebkey2020.go b/cryptosuite/jsonwebkey2020.go index 326d5550..6c3306cb 100644 --- a/cryptosuite/jsonwebkey2020.go +++ b/cryptosuite/jsonwebkey2020.go @@ -4,13 +4,11 @@ import ( gocrypto "crypto" "fmt" + "github.com/TBD54566975/ssi-sdk/crypto" + "github.com/TBD54566975/ssi-sdk/util" "github.com/lestrrat-go/jwx/jwa" "github.com/lestrrat-go/jwx/jws" "github.com/pkg/errors" - "github.com/sirupsen/logrus" - - "github.com/TBD54566975/ssi-sdk/crypto" - "github.com/TBD54566975/ssi-sdk/util" ) type ( @@ -145,8 +143,7 @@ func GenerateX25519JSONWebKey2020() (*JSONWebKey2020, error) { func GenerateSECP256k1JSONWebKey2020() (*JSONWebKey2020, error) { _, privKey, err := crypto.GenerateSECP256k1Key() if err != nil { - logrus.WithError(err).Error("could not generate secp256k1 key") - return nil, err + return nil, errors.Wrap(err, "could not generate secp256k1 key") } return JSONWebKey2020FromPrivateKey(privKey) } @@ -156,8 +153,7 @@ func GenerateSECP256k1JSONWebKey2020() (*JSONWebKey2020, error) { func GenerateP256JSONWebKey2020() (*JSONWebKey2020, error) { _, privKey, err := crypto.GenerateP256Key() if err != nil { - logrus.WithError(err).Error("could not generate p-256 key") - return nil, err + return nil, errors.Wrap(err, "could not generate p-256 key") } return JSONWebKey2020FromPrivateKey(privKey) } @@ -167,8 +163,7 @@ func GenerateP256JSONWebKey2020() (*JSONWebKey2020, error) { func GenerateP384JSONWebKey2020() (*JSONWebKey2020, error) { _, privKey, err := crypto.GenerateP384Key() if err != nil { - logrus.WithError(err).Error("could not generate p-384 key") - return nil, err + return nil, errors.Wrap(err, "could not generate p-384 key") } return JSONWebKey2020FromPrivateKey(privKey) } @@ -250,9 +245,6 @@ type JSONWebKeyVerifier struct { // and an error should it fail. func (v *JSONWebKeyVerifier) Verify(message, signature []byte) error { _, err := jws.Verify(signature, jwa.SignatureAlgorithm(v.Algorithm()), v.Key, jws.WithDetachedPayload(message)) - if err != nil { - logrus.WithError(err).Error("could not verify JWK") - } return err } diff --git a/cryptosuite/jwssignaturesuite.go b/cryptosuite/jwssignaturesuite.go index f0bf33b3..afc42f98 100644 --- a/cryptosuite/jwssignaturesuite.go +++ b/cryptosuite/jwssignaturesuite.go @@ -7,13 +7,11 @@ import ( "fmt" "strings" + "github.com/TBD54566975/ssi-sdk/crypto" + . "github.com/TBD54566975/ssi-sdk/util" "github.com/goccy/go-json" "github.com/google/uuid" "github.com/pkg/errors" - "github.com/sirupsen/logrus" - - "github.com/TBD54566975/ssi-sdk/crypto" - . "github.com/TBD54566975/ssi-sdk/util" ) // https://w3c-ccg.github.io/ld-cryptosuite-registry/#jsonwebsignature2020 @@ -71,9 +69,7 @@ func (j JWSSignatureSuite) Sign(s Signer, p Provable) error { // prepare proof options contexts, err := GetContextsFromProvable(p) if err != nil { - err := errors.Wrap(err, "could not get contexts from provable") - logrus.WithError(err).Error() - return err + return errors.Wrap(err, "could not get contexts from provable") } // make sure the suite's context(s) are included @@ -83,15 +79,13 @@ func (j JWSSignatureSuite) Sign(s Signer, p Provable) error { // 3. tbs value as a result of cvh tbs, err := j.CreateVerifyHash(p, proof, opts) if err != nil { - logrus.WithError(err).Error("create verify hash algorithm failed") - return err + return errors.Wrap(err, "create verify hash algorithm failed") } // 4 & 5. create the signature over the provable data as a JWS signature, err := s.Sign(tbs) if err != nil { - logrus.WithError(err).Error("could not sign provable value") - return err + return errors.Wrap(err, "could not sign provable value") } // set the signature on the proof object and return @@ -105,9 +99,7 @@ func (j JWSSignatureSuite) Verify(v Verifier, p Provable) error { proof := p.GetProof() gotProof, err := FromGenericProof(*proof) if err != nil { - err := errors.Wrap(err, "could not coerce proof into JsonWebSignature2020 proof") - logrus.WithError(err).Error("could not prepare proof for verification") - return err + return errors.Wrap(err, "could not prepare proof for verification; error coercing proof into JsonWebSignature2020 proof") } // remove proof before verifying @@ -123,9 +115,7 @@ func (j JWSSignatureSuite) Verify(v Verifier, p Provable) error { // prepare proof options contexts, err := GetContextsFromProvable(p) if err != nil { - err := errors.Wrap(err, "could not get contexts from provable") - logrus.WithError(err).Error() - return err + return errors.Wrap(err, "could not get contexts from provable") } // make sure the suite's context(s) are included @@ -135,13 +125,11 @@ func (j JWSSignatureSuite) Verify(v Verifier, p Provable) error { // run CVH on both provable and the proof tbv, err := j.CreateVerifyHash(p, gotProof, opts) if err != nil { - logrus.WithError(err).Error("create verify hash algorithm failed") - return err + return errors.Wrap(err, "create verify hash algorithm failed") } if err = v.Verify(tbv, jwsCopy); err != nil { - logrus.WithError(err).Error("could not verify JWS") - return err + return errors.Wrap(err, "could not verify JWS") } return nil } @@ -165,9 +153,7 @@ func (JWSSignatureSuite) Canonicalize(marshaled []byte) (*string, error) { } normalized, err := LDNormalize(generic) if err != nil { - err := errors.Wrap(err, "could not canonicalize provable document") - logrus.WithError(err).Error() - return nil, err + return nil, errors.Wrap(err, "could not canonicalize provable document") } canonicalString := normalized.(string) return &canonicalString, nil @@ -177,59 +163,45 @@ func (j JWSSignatureSuite) CreateVerifyHash(provable Provable, proof crypto.Proo // first, make sure "created" exists in the proof and insert an LD context property for the proof vocabulary preparedProof, err := j.prepareProof(proof, opts) if err != nil { - errMsg := "could not prepare proof for the create verify hash algorithm" - logrus.WithError(err).Error(errMsg) - return nil, errors.Wrap(err, errMsg) + return nil, errors.Wrap(err, "could not prepare proof for the create verify hash algorithm") } // marshal provable to prepare for canonicalizaiton marshaledProvable, err := j.Marshal(provable) if err != nil { - errMsg := "could not marshal provable" - logrus.WithError(err).Error(errMsg) - return nil, errors.Wrap(err, errMsg) + return nil, errors.Wrap(err, "could not marshal provable") } // canonicalize provable using the suite's method canonicalProvable, err := j.Canonicalize(marshaledProvable) if err != nil { - errMsg := "could not canonicalize provable" - logrus.WithError(err).Error(errMsg) - return nil, errors.Wrap(err, errMsg) + return nil, errors.Wrap(err, "could not canonicalize provable") } // marshal proof to prepare for canonicalizaiton marshaledOptions, err := j.Marshal(preparedProof) if err != nil { - errMsg := "could not marshal proof" - logrus.WithError(err).Error(errMsg) - return nil, errors.Wrap(err, errMsg) + return nil, errors.Wrap(err, "could not marshal proof") } // 4.1 canonicalize proof using the suite's method canonicalizedOptions, err := j.Canonicalize(marshaledOptions) if err != nil { - errMsg := "could not canonicalize proof" - logrus.WithError(err).Error(errMsg) - return nil, errors.Wrap(err, errMsg) + return nil, errors.Wrap(err, "could not canonicalize proof") } // 4.2 set output to the result of the hash of the canonicalized options document canonicalizedOptionsBytes := []byte(*canonicalizedOptions) optionsDigest, err := j.Digest(canonicalizedOptionsBytes) if err != nil { - errMsg := "could not take digest of proof" - logrus.WithError(err).Error(errMsg) - return nil, errors.Wrap(err, errMsg) + return nil, errors.Wrap(err, "could not take digest of proof") } // 4.3 hash the canonicalized doc and append it to the output canonicalDoc := []byte(*canonicalProvable) documentDigest, err := j.Digest(canonicalDoc) if err != nil { - errMsg := "could not take digest of provable" - logrus.WithError(err).Error(errMsg) - return nil, errors.Wrap(err, errMsg) + return nil, errors.Wrap(err, "could not take digest of provable") } // 5. return the output @@ -239,9 +211,7 @@ func (j JWSSignatureSuite) CreateVerifyHash(provable Provable, proof crypto.Proo func (j JWSSignatureSuite) Digest(tbd []byte) ([]byte, error) { if j.MessageDigestAlgorithm() != gocrypto.SHA256 { - err := fmt.Errorf("unexpected digest algorithm: %s", j.MessageDigestAlgorithm().String()) - logrus.WithError(err).Error("could not get digest") - return nil, err + return nil, fmt.Errorf("unexpected digest algorithm: %s", j.MessageDigestAlgorithm().String()) } hash := sha256.Sum256(tbd) return hash[:], nil @@ -254,7 +224,7 @@ func (j JWSSignatureSuite) prepareProof(proof crypto.Proof, opts *ProofOptions) } var genericProof map[string]interface{} - if err := json.Unmarshal(proofBytes, &genericProof); err != nil { + if err = json.Unmarshal(proofBytes, &genericProof); err != nil { return nil, err } @@ -294,7 +264,7 @@ func FromGenericProof(p crypto.Proof) (*JSONWebSignature2020Proof, error) { return nil, err } var generic map[string]interface{} - if err := json.Unmarshal(proofBytes, &generic); err != nil { + if err = json.Unmarshal(proofBytes, &generic); err != nil { return nil, err } typeValue, ok := generic["type"].(string) diff --git a/did/context/did-pkh-context.json b/did/context/did-pkh-context.json index 7b3c0a0e..6a5c2951 100644 --- a/did/context/did-pkh-context.json +++ b/did/context/did-pkh-context.json @@ -1,15 +1,15 @@ [ "https://www.w3.org/ns/did/v1", { - "blockchainAccountId": "https://w3id.org/security#blockchainAccountId", - "publicKeyJwk": { - "@id": "https://w3id.org/security#publicKeyJwk", - "@type": "@json" - }, - "Ed25519VerificationKey2018": "https://w3id.org/security#Ed25519VerificationKey2018", - "Ed25519PublicKeyBLAKE2BDigestSize20Base58CheckEncoded2021": "https://w3id.org/security#Ed25519PublicKeyBLAKE2BDigestSize20Base58CheckEncoded2021", - "P256PublicKeyBLAKE2BDigestSize20Base58CheckEncoded2021": "https://w3id.org/security#P256PublicKeyBLAKE2BDigestSize20Base58CheckEncoded2021", - "TezosMethod2021": "https://w3id.org/security#TezosMethod2021", - "EcdsaSecp256k1RecoveryMethod2020": "https://identity.foundation/EcdsaSecp256k1RecoverySignature2020#EcdsaSecp256k1RecoveryMethod2020" + "blockchainAccountId": "https://w3id.org/security#blockchainAccountId", + "publicKeyJwk": { + "@id": "https://w3id.org/security#publicKeyJwk", + "@type": "@json" + }, + "Ed25519VerificationKey2018": "https://w3id.org/security#Ed25519VerificationKey2018", + "Ed25519PublicKeyBLAKE2BDigestSize20Base58CheckEncoded2021": "https://w3id.org/security#Ed25519PublicKeyBLAKE2BDigestSize20Base58CheckEncoded2021", + "P256PublicKeyBLAKE2BDigestSize20Base58CheckEncoded2021": "https://w3id.org/security#P256PublicKeyBLAKE2BDigestSize20Base58CheckEncoded2021", + "TezosMethod2021": "https://w3id.org/security#TezosMethod2021", + "EcdsaSecp256k1RecoveryMethod2020": "https://identity.foundation/EcdsaSecp256k1RecoverySignature2020#EcdsaSecp256k1RecoveryMethod2020" } ] \ No newline at end of file diff --git a/did/did.go b/did/did.go index 76797c25..896c3ea0 100644 --- a/did/did.go +++ b/did/did.go @@ -15,11 +15,11 @@ func (m Method) String() string { return string(m) } -// DID represents functionality common to all DIDs +// DID encapsulates functionality common to all DIDs type DID interface { // IsValid checks if the DID is compliant with its methods definition IsValid() bool - // ToString Returns the string representation of the DID identifier (e.g. did:example:abcd) + // String Returns the string representation of the DID identifier (e.g. did:example:abcd) String() string // Suffix provides the value of the DID without the method prefix Suffix() (string, error) diff --git a/did/key.go b/did/key.go index a906a824..f45a8f85 100644 --- a/did/key.go +++ b/did/key.go @@ -5,10 +5,7 @@ import ( "fmt" "strings" - "github.com/sirupsen/logrus" - "github.com/lestrrat-go/jwx/jwk" - "github.com/mr-tron/base58" "github.com/TBD54566975/ssi-sdk/cryptosuite" @@ -64,28 +61,22 @@ func (DIDKey) Method() Method { // if !ok { ... } func GenerateDIDKey(kt crypto.KeyType) (gocrypto.PrivateKey, *DIDKey, error) { if !isSupportedKeyType(kt) { - err := fmt.Errorf("unsupported did:key type: %s", kt) - logrus.WithError(err).Error() - return nil, nil, err + return nil, nil, fmt.Errorf("unsupported did:key type: %s", kt) } pubKey, privKey, err := crypto.GenerateKeyByKeyType(kt) if err != nil { - errMsg := "could not generate key for did:key" - logrus.WithError(err).Error(errMsg) - return nil, nil, errors.Wrap(err, errMsg) + return nil, nil, errors.Wrap(err, "could not generate key for did:key") } pubKeyBytes, err := crypto.PubKeyToBytes(pubKey) if err != nil { - logrus.WithError(err).Error("could not convert public key to byte") - return nil, nil, err + return nil, nil, errors.Wrap(err, "could not convert public key to byte") } didKey, err := CreateDIDKey(kt, pubKeyBytes) if err != nil { - logrus.WithError(err).Error("could not create DID key") - return nil, nil, err + return nil, nil, errors.Wrap(err, "could not create DID key") } return privKey, didKey, err } @@ -95,23 +86,19 @@ func GenerateDIDKey(kt crypto.KeyType) (gocrypto.PrivateKey, *DIDKey, error) { // A safer method is `GenerateDIDKey` which handles key generation based on the provided key type. func CreateDIDKey(kt crypto.KeyType, publicKey []byte) (*DIDKey, error) { if !isSupportedKeyType(kt) { - err := fmt.Errorf("unsupported did:key type: %s", kt) - logrus.WithError(err).Error() - return nil, err + return nil, fmt.Errorf("unsupported did:key type: %s", kt) } // did:key: multiCodec, err := keyTypeToMultiCodec(kt) if err != nil { - logrus.WithError(err).Errorf("could find mutlicodec for key type<%s> for did:key", kt) - return nil, err + return nil, fmt.Errorf("could find mutlicodec for key type<%s> for did:key", kt) } prefix := varint.ToUvarint(uint64(multiCodec)) codec := append(prefix, publicKey...) encoded, err := multibase.Encode(Base58BTCMultiBase, codec) if err != nil { - logrus.WithError(err).Error("could not encode did:key") - return nil, err + return nil, errors.Wrap(err, "could not encode did:key") } did := DIDKey(fmt.Sprintf("%s:%s", DIDKeyPrefix, encoded)) return &did, nil @@ -124,20 +111,15 @@ func (d DIDKey) Decode() ([]byte, cryptosuite.LDKeyType, crypto.KeyType, error) return nil, "", "", errors.Wrap(err, "could not parse did:key") } if parsed == "" { - err := fmt.Errorf("could not decode did:key value: %s", string(d)) - logrus.WithError(err).Error() - return nil, "", "", err + return nil, "", "", fmt.Errorf("could not decode did:key value: %s", string(d)) } encoding, decoded, err := multibase.Decode(parsed) if err != nil { - logrus.WithError(err).Error("could not decode did:key") - return nil, "", "", err + return nil, "", "", errors.Wrap(err, "could not decode did:key") } if encoding != Base58BTCMultiBase { - err := fmt.Errorf("expected %d encoding but found %d", Base58BTCMultiBase, encoding) - logrus.WithError(err).Error() - return nil, "", "", err + return nil, "", "", fmt.Errorf("expected %d encoding but found %d", Base58BTCMultiBase, encoding) } // n = # bytes for the int, which we expect to be two from our multicodec @@ -146,16 +128,14 @@ func (d DIDKey) Decode() ([]byte, cryptosuite.LDKeyType, crypto.KeyType, error) return nil, "", "", err } if n != 2 { - errMsg := "Error parsing did:key varint" - logrus.Error(errMsg) - return nil, "", "", errors.New(errMsg) + return nil, "", "", errors.New("error parsing did:key varint") } pubKeyBytes := decoded[n:] multiCodecValue := multicodec.Code(multiCodec) ldKeyType, err := codecToLDKeyType(multiCodecValue) if err != nil { - return nil, "", "", errors.Wrap(err, "determining ld key type") + return nil, "", "", errors.Wrap(err, "determining LD key type") } cryptoKeyType, err := codecToKeyType(multiCodecValue) if err != nil { @@ -175,9 +155,7 @@ func codecToLDKeyType(codec multicodec.Code) (cryptosuite.LDKeyType, error) { case P256MultiCodec, P384MultiCodec, P521MultiCodec, RSAMultiCodec: return cryptosuite.JSONWebKey2020Type, nil default: - err := fmt.Errorf("unknown multicodec for did:key: %d", codec) - logrus.WithError(err).Error() - return "", err + return "", fmt.Errorf("unknown multicodec for did:key: %d", codec) } } @@ -193,14 +171,12 @@ func (d DIDKey) Expand() (*DIDDocument, error) { pubKey, keyType, cryptoKeyType, err := d.Decode() if err != nil { - logrus.WithError(err).Error("could not decode did:key") - return nil, err + return nil, errors.Wrap(err, "could not decode did:key") } verificationMethod, err := constructVerificationMethod(id, keyReference, pubKey, keyType, cryptoKeyType) if err != nil { - logrus.WithError(err).Error("could not construct verification method") - return nil, err + return nil, errors.Wrap(err, "could not construct verification method") } verificationMethodSet := []VerificationMethodSet{ @@ -258,16 +234,12 @@ func constructVerificationMethod(id, keyReference string, pubKey []byte, keyType standardJWK, err := jwk.New(cryptoPubKey) if err != nil { - errMsg := "could not expand key of type JsonWebKey2020" - logrus.WithError(err).Error(errMsg) - return nil, errors.Wrap(err, errMsg) + return nil, errors.Wrap(err, "could not expand key of type JsonWebKey2020") } pubKeyJWK, err := crypto.JWKToPublicKeyJWK(standardJWK) if err != nil { - errMsg := "could convert did:key to PublicKeyJWK" - logrus.WithError(err).Error(errMsg) - return nil, errors.Wrap(err, errMsg) + return nil, errors.Wrap(err, "could convert did:key to PublicKeyJWK") } return &VerificationMethod{ diff --git a/did/model.go b/did/model.go index 0427b6ad..b15a0f6d 100644 --- a/did/model.go +++ b/did/model.go @@ -143,8 +143,6 @@ func KeyTypeToLDKeyType(kt crypto.KeyType) (cryptosuite.LDKeyType, error) { case crypto.P256, crypto.P384, crypto.P521, crypto.RSA: return cryptosuite.JSONWebKey2020Type, nil default: - err := fmt.Errorf("unsupported keyType: %+v", kt) - errMsg := fmt.Sprintf("keyType %+v failed to convert to LDKeyType", kt) - return "", util.LoggingErrorMsg(err, errMsg) + return "", fmt.Errorf("keyType %+v failed to convert to LDKeyType", kt) } } diff --git a/did/model_test.go b/did/model_test.go index 77b44582..01a2336e 100644 --- a/did/model_test.go +++ b/did/model_test.go @@ -101,5 +101,5 @@ func TestKeyTypeToLDKeyType(t *testing.T) { _, err = KeyTypeToLDKeyType(crypto.KeyType("bad")) assert.Error(t, err) - assert.Contains(t, err.Error(), "unsupported keyType") + assert.Contains(t, err.Error(), "keyType bad failed to convert to LDKeyType") } diff --git a/did/peer.go b/did/peer.go index 42c818cc..4548a73b 100644 --- a/did/peer.go +++ b/did/peer.go @@ -23,7 +23,6 @@ import ( "github.com/TBD54566975/ssi-sdk/crypto" "github.com/TBD54566975/ssi-sdk/util" "github.com/pkg/errors" - "github.com/sirupsen/logrus" ) type ( @@ -48,7 +47,6 @@ const ( func isPeerDID(did string) bool { r, err := regexp.Compile(PeerDIDRegex) if err != nil { - logrus.WithError(err).Error() // this should never happen return false } return r.MatchString(did) diff --git a/did/pkh.go b/did/pkh.go index 3e36f0a7..9dda365f 100644 --- a/did/pkh.go +++ b/did/pkh.go @@ -64,7 +64,7 @@ func CreateDIDPKH(namespace, reference, address string) (*DIDPKH, error) { did := DIDPKH(fmt.Sprintf("%s:%s:%s:%s", DIDPKHPrefix, namespace, reference, address)) if !IsValidPKH(did) { - return nil, util.LoggingNewError(fmt.Sprintf("PKH DID is not valid: %s", string(did))) + return nil, fmt.Errorf("PKH DID is not valid: %s", string(did)) } return &did, nil @@ -150,17 +150,17 @@ func (d DIDPKH) Expand() (*DIDDocument, error) { verificationMethod, err := constructPKHVerificationMethod(d) if err != nil { - return nil, util.LoggingErrorMsg(err, "could not construct verification method") + return nil, errors.Wrap(err, "could not construct verification method") } knownDIDPKHContextJSON, err := GetDIDPKHContext() if err != nil { - return nil, util.LoggingErrorMsg(err, "could not get known context json") + return nil, errors.Wrap(err, "could not get known context json") } contextJSON, err := util.ToJSONInterface(knownDIDPKHContextJSON) if err != nil { - return nil, util.LoggingErrorMsg(err, "could not convert known context to json") + return nil, errors.Wrap(err, "could not convert known context to json") } verificationMethodSet := []VerificationMethodSet{ @@ -182,17 +182,17 @@ func constructPKHVerificationMethod(did DIDPKH) (*VerificationMethod, error) { if !IsValidPKH(did) { parsed, err := did.Suffix() if err != nil || parsed == "" { - return nil, util.LoggingNewError("PKH DID is not valid") + return nil, errors.Wrap(err, "PKH DID is not valid") } } network, err := GetDIDPKHNetworkForDID(did.String()) if err != nil { - return nil, util.LoggingErrorMsg(err, "could not find network") + return nil, errors.Wrap(err, "could not find network") } verificationType, err := GetVerificationTypeForNetwork(network) if err != nil { - return nil, util.LoggingErrorMsg(err, "could not find verification type") + return nil, errors.Wrap(err, "could not find verification type") } suffix, err := did.Suffix() diff --git a/did/resolver.go b/did/resolver.go index ca0ea945..8b9dcb6c 100644 --- a/did/resolver.go +++ b/did/resolver.go @@ -4,7 +4,6 @@ import ( "fmt" "strings" - "github.com/TBD54566975/ssi-sdk/util" "github.com/pkg/errors" ) @@ -33,7 +32,7 @@ func NewResolver(resolvers ...Resolution) (*Resolver, error) { for _, resolver := range resolvers { method := resolver.Method() if _, ok := r[method]; ok { - return nil, util.LoggingNewError(fmt.Sprintf("duplicate resolver for method: %s", method)) + return nil, fmt.Errorf("duplicate resolver for method: %s", method) } r[method] = resolver methods = append(methods, method) @@ -50,7 +49,7 @@ func (dr Resolver) Resolve(did string, opts ...ResolutionOptions) (*DIDResolutio if resolver, ok := dr.resolvers[method]; ok { return resolver.Resolve(did, opts) } - return nil, util.LoggingNewError(fmt.Sprintf("unsupported method: %s", method)) + return nil, fmt.Errorf("unsupported method: %s", method) } func (dr Resolver) SupportedMethods() []Method { diff --git a/did/util.go b/did/util.go index 2c2008c8..17f9e54e 100644 --- a/did/util.go +++ b/did/util.go @@ -11,7 +11,6 @@ import ( "github.com/multiformats/go-multicodec" "github.com/multiformats/go-varint" "github.com/pkg/errors" - "github.com/sirupsen/logrus" ) // Encodes the public key provided @@ -131,7 +130,5 @@ func keyTypeToMultiCodec(kt crypto.KeyType) (multicodec.Code, error) { case crypto.RSA: return RSAMultiCodec, nil } - err := fmt.Errorf("unknown multicodec for key type: %s", kt) - logrus.WithError(err).Error() - return 0, err + return 0, fmt.Errorf("unknown multicodec for key type: %s", kt) } diff --git a/did/web.go b/did/web.go index b249f4ff..e694cae3 100644 --- a/did/web.go +++ b/did/web.go @@ -8,11 +8,9 @@ import ( "net/url" "strings" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" - "github.com/TBD54566975/ssi-sdk/crypto" "github.com/TBD54566975/ssi-sdk/util" + "github.com/pkg/errors" ) // did:web method specification https://w3c-ccg.github.io/did-method-web/ @@ -56,7 +54,6 @@ func (DIDWeb) Method() Method { func (d DIDWeb) CreateDoc(kt crypto.KeyType, publicKey []byte) (*DIDDocument, error) { ldKeyType, err := KeyTypeToLDKeyType(kt) if err != nil { - logrus.WithError(err).Error() return nil, err } didWebStr := string(d) @@ -64,8 +61,7 @@ func (d DIDWeb) CreateDoc(kt crypto.KeyType, publicKey []byte) (*DIDDocument, er verificationMethod, err := constructVerificationMethod(didWebStr, keyReference, publicKey, ldKeyType, kt) if err != nil { - errMsg := fmt.Sprintf("could not construct verification method for DIDWeb %+v", d) - return nil, util.LoggingErrorMsg(err, errMsg) + return nil, fmt.Errorf("could not construct verification method for DIDWeb %+v", d) } verificationMethodSet := []VerificationMethodSet{ @@ -85,8 +81,7 @@ func (d DIDWeb) CreateDoc(kt crypto.KeyType, publicKey []byte) (*DIDDocument, er func (d DIDWeb) CreateDocBytes(kt crypto.KeyType, publicKey []byte) ([]byte, error) { doc, err := d.CreateDoc(kt, publicKey) if err != nil { - logrus.WithError(err).Errorf("could not create DIDDocument for DIDWeb %+v", d) - return nil, err + return nil, errors.Wrapf(err, "could not create DID Document for did:web DID %+v", d) } return json.Marshal(doc) } @@ -96,25 +91,20 @@ func (d DIDWeb) CreateDocBytes(kt crypto.KeyType, publicKey []byte) ([]byte, err func (d DIDWeb) GetDocURL() (string, error) { // DIDWeb must be prefixed with d:web: if !strings.HasPrefix(string(d), DIDWebPrefix) { - err := fmt.Errorf("did:web DID %+v is missing prefix %s", d, DIDWebPrefix) - logrus.WithError(err).Error() - return "", err + return "", fmt.Errorf("did:web DID %+v is missing prefix %s", d, DIDWebPrefix) } subStrs := strings.Split(string(d), ":") numSubStrs := len(subStrs) if numSubStrs < 3 || len(subStrs[2]) < 1 { - err := fmt.Errorf("d:web DID %+v is missing the required domain", d) - logrus.WithError(err).Error() - return "", err + return "", fmt.Errorf("did:web DID %+v is missing the required domain", d) } // Specification https://w3c-ccg.github.io/did-method-web/#read-resolve // 2. If the domain contains a port percent decode the colon. decodedDomain, err := url.QueryUnescape(subStrs[2]) if err != nil { - errMsg := fmt.Sprintf("url.QueryUnescape failed for subStr %s", subStrs[2]) - return "", util.LoggingErrorMsg(err, errMsg) + return "", errors.Wrapf(err, "url.QueryUnescape failed for subStr %s", subStrs[2]) } // 3. Generate an HTTPS URL to the expected location of the DID document by prepending https://. @@ -128,16 +118,15 @@ func (d DIDWeb) GetDocURL() (string, error) { // https://w3c-ccg.github.io/did-method-web/#optional-path-considerations // Optional Path Considerations var sb strings.Builder - if _, err := sb.WriteString("https://" + decodedDomain + "/"); err != nil { + if _, err = sb.WriteString("https://" + decodedDomain + "/"); err != nil { return "", err } for i := 3; i < numSubStrs; i++ { str, err := url.QueryUnescape(subStrs[i]) if err != nil { - logrus.WithError(err).Errorf("url.QueryUnescape failed for subStr %s", subStrs[i]) - return "", err + return "", errors.Wrapf(err, "url.QueryUnescape failed for subStr %s", subStrs[i]) } - if _, err := sb.WriteString(str + "/"); err != nil { + if _, err = sb.WriteString(str + "/"); err != nil { return "", err } } @@ -167,17 +156,14 @@ func (WebResolver) Resolve(did string, _ ResolutionOptions) (*DIDResolutionResul func (d DIDWeb) Resolve() (*DIDDocument, error) { docBytes, err := d.ResolveDocBytes() if err != nil { - errMsg := fmt.Sprintf("could not resolve did:web DID: %s", d) - return nil, util.LoggingErrorMsg(err, errMsg) + return nil, errors.Wrapf(err, "could not resolve did:web DID: %s", d) } var doc DIDDocument if err = json.Unmarshal(docBytes, &doc); err != nil { - errMsg := fmt.Sprintf("could not resolve with docBytes %s", docBytes) - return nil, util.LoggingErrorMsg(err, errMsg) + return nil, errors.Wrapf(err, "could not resolve with docBytes %s", docBytes) } if doc.ID != d.String() { - errMsg := fmt.Sprintf("doc.ID %s does not match did:web value: %s", doc.ID, d) - return nil, util.LoggingNewError(errMsg) + return nil, fmt.Errorf("doc.ID %s does not match did:web value: %s", doc.ID, d) } return &doc, nil } @@ -188,22 +174,19 @@ func (d DIDWeb) Resolve() (*DIDDocument, error) { func (d DIDWeb) ResolveDocBytes() ([]byte, error) { docURL, err := d.GetDocURL() if err != nil { - logrus.WithError(err).Errorf("could not resolve DIDWeb %+v", d) - return nil, err + return nil, errors.Wrapf(err, "could not resolve DIDWeb %+v", d) } // Specification https://w3c-ccg.github.io/did-method-web/#read-resolve // 6. Perform an HTTP GET request to the URL using an agent that can successfully negotiate a secure HTTPS // connection, which enforces the security requirements as described in 2.5 Security and privacy considerations. resp, err := http.Get(docURL) // #nosec if err != nil { - logrus.WithError(err).Errorf("could not resolve with docURL %+v", docURL) - return nil, err + return nil, errors.Wrapf(err, "could not resolve with docURL %+v", docURL) } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { - logrus.WithError(err).Errorf("could not resolve with response %+v", resp) - return nil, err + return nil, errors.Wrapf(err, "could not resolve with response %+v", resp) } return body, nil } diff --git a/did/web_test.go b/did/web_test.go index d816d93a..6e1fa021 100644 --- a/did/web_test.go +++ b/did/web_test.go @@ -1,9 +1,10 @@ package did import ( - "gopkg.in/h2non/gock.v1" "testing" + "gopkg.in/h2non/gock.v1" + "github.com/TBD54566975/ssi-sdk/crypto" "github.com/stretchr/testify/assert" ) diff --git a/doc/VERSIONING.md b/doc/VERSIONING.md index d1f31392..96f44be3 100644 --- a/doc/VERSIONING.md +++ b/doc/VERSIONING.md @@ -3,37 +3,46 @@ The SSI SDK follows [Semantic Versioning rules](https://semver.org/) *as much as possible.* > Given a version number MAJOR.MINOR.PATCH, increment the: -> +> > 1. MAJOR version when you make incompatible API changes > 2. MINOR version when you add functionality in a backwards compatible manner > 3. PATCH version when you make backwards compatible bug fixes -> +> > Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format. -Changes to APIs — public methods and interfaces — must be backwards compatible. Backwards compatible changes are represented by **minor** and **patch** version updates. Breaking changes: removing or modifying existing public methods or interfaces should be avoided in a non-backwards compatible manner, but when necessary, result in a **major** version change. +Changes to APIs — public methods and interfaces — must be backwards compatible. Backwards compatible changes are +represented by **minor** and **patch** version updates. Breaking changes: removing or modifying existing public methods +or interfaces should be avoided in a non-backwards compatible manner, but when necessary, result in a **major** version +change. --- # Releases -A release is a version-identified distribution of the SSI SDK. Releases can be found on [GitHub’s release page](https://github.com/TBD54566975/ssi-sdk/releases). Releases are always published via GitHub, and reference a specific commit hash. +A release is a version-identified distribution of the SSI SDK. Releases can be found +on [GitHub’s release page](https://github.com/TBD54566975/ssi-sdk/releases). Releases are always published via GitHub, +and reference a specific commit hash. ## Release Stability -Releases are classified into three main buckets: alpha, beta, and stable. +Releases are classified into three main buckets: alpha, beta, and stable. ### Alpha -Alpha releases may include experimental or incomplete changes. They are not considered stable and are mostly useful for developers. Alpha releases may be suffixed with *-alpha*, for example `v0.1.1-alpha`. +Alpha releases may include experimental or incomplete changes. They are not considered stable and are mostly useful for +developers. Alpha releases may be suffixed with *-alpha*, for example `v0.1.1-alpha`. ### Beta -Beta releases include feature complete changes that need to gain more confidence. Confidence is gained through developer and user testing. Beta releases may contain potential bug fixes and patches that need quick adoption. Beta releases may be suffixed with *-beta*, for example, `v0.2.3-beta`. +Beta releases include feature complete changes that need to gain more confidence. Confidence is gained through developer +and user testing. Beta releases may contain potential bug fixes and patches that need quick adoption. Beta releases may +be suffixed with *-beta*, for example, `v0.2.3-beta`. ### Stable -Stable releases have been well tested and carry the highest level of confidence. They are suited for production-level usage. They are not suffixed (e.g. `v0.5.2`). +Stable releases have been well tested and carry the highest level of confidence. They are suited for production-level +usage. They are not suffixed (e.g. `v0.5.2`). --- @@ -41,15 +50,20 @@ Stable releases have been well tested and carry the highest level of confidence. 1. **Who creates releases?** -The project maintainers are responsible for creates and managing releases. If you are interested in a release but there isn’t one, consider referencing a specific commit hash (e.g. `go get github.com/[TBD54566975/ssi-sdk](https://github.com/TBD54566975/ssi-sdk)@`) or reaching out to the team via Discord, the forums, or an issue. +The project maintainers are responsible for creates and managing releases. If you are interested in a release but there +isn’t one, consider referencing a specific commit hash ( +e.g. `go get github.com/[TBD54566975/ssi-sdk](https://github.com/TBD54566975/ssi-sdk)@`) or reaching out to +the team via Discord, the forums, or an issue. 2. **When do we cut a new release?** -Primarily when there are substantive feature changes or when having adoption of a release has some sort of utility — whether to promote additional testing or to advocate for stability such as in the case of a bug fix. +Primarily when there are substantive feature changes or when having adoption of a release has some sort of utility — +whether to promote additional testing or to advocate for stability such as in the case of a bug fix. 3. **How are releases represented?** -Releases can be found on [GitHub’s release page](https://github.com/TBD54566975/ssi-sdk/releases). Release notes highlighting key changes accompany each release. +Releases can be found on [GitHub’s release page](https://github.com/TBD54566975/ssi-sdk/releases). Release notes +highlighting key changes accompany each release. 4. **Which release should I use?** diff --git a/doc/VISION.md b/doc/VISION.md index 477d0f5e..ef25bbf7 100644 --- a/doc/VISION.md +++ b/doc/VISION.md @@ -1,21 +1,44 @@ # Vision -Name `ssi-sdk`, this SDK encapsulates a set of standards related to [Self Sovereign Identity](http://www.lifewithalacrity.com/2016/04/the-path-to-self-soverereign-identity.html). The `ssi-sdk` intends to provide flexible functionality based on a set of standards-based primitives for building decentralized identity applications in a modular manner: with limited dependencies between components. +Name `ssi-sdk`, this SDK encapsulates a set of standards related +to [Self Sovereign Identity](http://www.lifewithalacrity.com/2016/04/the-path-to-self-soverereign-identity.html). +The `ssi-sdk` intends to provide flexible functionality based on a set of standards-based primitives for building +decentralized identity applications in a modular manner: with limited dependencies between components. -Primarily, the SDK serves to support Decentralized Identifiers and Verifiable Credentials and their associated standards. Interacting with Decentralized Identifiers: resolving identifiers, signing, verifying, encrypting, and decrypting data using cryptographic keys found in DID Documents. Interacting with Verifiable Credentials: creating and using data schemas, facilitating credential application, issuance, and exchange. +Primarily, the SDK serves to support Decentralized Identifiers and Verifiable Credentials and their associated +standards. Interacting with Decentralized Identifiers: resolving identifiers, signing, verifying, encrypting, and +decrypting data using cryptographic keys found in DID Documents. Interacting with Verifiable Credentials: creating and +using data schemas, facilitating credential application, issuance, and exchange. -The SSI SDK is closely related to the [SSI Service](https://github.com/TBD54566975/ssi-service), where much of its features are exposed in a service infrastructure. +The SSI SDK is closely related to the [SSI Service](https://github.com/TBD54566975/ssi-service), where much of its +features are exposed in a service infrastructure. # Guiding Principles -The SDK is a core component of Web5 and has a guiding principle to *build pragmatic standards-based software that serves a wide variety of needs*. The software shall not be tied to any specific entity, nor, without good reason, exclude possibilities within the SSI space. Balancing both feature-richness and complexity we must work closely with our users to design software that meets the needs of all who wish to be on Web5. We favor evaluating the addition of features and standards on a case-by-case basis, and looking towards implementations of standards and features that are well-reasoned, with committed developers. Bonus points if there is already demonstrated usage and interoperability. +The SDK is a core component of Web5 and has a guiding principle to *build pragmatic standards-based software that serves +a wide variety of needs*. The software shall not be tied to any specific entity, nor, without good reason, exclude +possibilities within the SSI space. Balancing both feature-richness and complexity we must work closely with our users +to design software that meets the needs of all who wish to be on Web5. We favor evaluating the addition of features and +standards on a case-by-case basis, and looking towards implementations of standards and features that are well-reasoned, +with committed developers. Bonus points if there is already demonstrated usage and interoperability. ## Feature Support -The feature set of the SDK is largely influenced by the standards and specifications in the Decentralized Identity community in aim of advancing the adoption of Self Sovereign Identity. We favor evaluating the addition of features and standards on a case-by-case basis, and looking towards implementations of standards and features that are well-reasoned, with committed developers and use cases. Features that already demonstrated usage and interoperability outside of the project are prime candidates for adoption. +The feature set of the SDK is largely influenced by the standards and specifications in the Decentralized Identity +community in aim of advancing the adoption of Self Sovereign Identity. We favor evaluating the addition of features and +standards on a case-by-case basis, and looking towards implementations of standards and features that are well-reasoned, +with committed developers and use cases. Features that already demonstrated usage and interoperability outside of the +project are prime candidates for adoption. ## Language Support -The SSI ecosystem uses a wide set of tools, languages, and technologies: working across web browsers, mobile applications, backend servers, ledgers, and more. This SDK uses [Go](https://go.dev/) because of its robust cryptographic support, speed, ability to be compiled to [WASM](https://webassembly.org/), and, above all else, simplicity. It is crucial that the code we write is approachable to encourage contribution. Simple and clear is always preferred over clever. +The SSI ecosystem uses a wide set of tools, languages, and technologies: working across web browsers, mobile +applications, backend servers, ledgers, and more. This SDK uses [Go](https://go.dev/) because of its robust +cryptographic support, speed, ability to be compiled to [WASM](https://webassembly.org/), and, above all else, +simplicity. It is crucial that the code we write is approachable to encourage contribution. Simple and clear is always +preferred over clever. -The future is multi-language, and multi-platform. We welcome initiatives for improving multi-language and multi-platform support, and are open to incubating them in our GitHub organization. When future SDKs are developed, it is expected that they follow the same feature set and API as the Go SDK in addition to fulfilling the suite of language interoperability tests. \ No newline at end of file +The future is multi-language, and multi-platform. We welcome initiatives for improving multi-language and multi-platform +support, and are open to incubating them in our GitHub organization. When future SDKs are developed, it is expected that +they follow the same feature set and API as the Go SDK in addition to fulfilling the suite of language interoperability +tests. \ No newline at end of file diff --git a/example/usecase/apartment_application/README.md b/example/usecase/apartment_application/README.md index de1172de..54ed8b77 100644 --- a/example/usecase/apartment_application/README.md +++ b/example/usecase/apartment_application/README.md @@ -1,37 +1,51 @@ # Apartment Age Verification Use Case # Introduction -This is a full example flow of an apartment verifying the age of a potential tenant. The flow goes as follows: -The apartment will create a presentation request that is to be fulfilled by the tenant. The tenant will fulfil the Presentation Request by submitting a Presentation Submission. This presentation submission will contain a verifiable credential that has been previously issued and signed from the government issuer. The tenant will verify that the apartment's presentation request is valid and the apartment will also verify that the tenant's presentation submission is valid. +This is a full example flow of an apartment verifying the age of a potential tenant. The flow goes as follows: -At the end the apartment will verify the authenticity of the presentation submission and will be able to cryptographically verify the birthdate of the tenant. +The apartment will create a presentation request that is to be fulfilled by the tenant. The tenant will fulfil the +Presentation Request by submitting a Presentation Submission. This presentation submission will contain a verifiable +credential that has been previously issued and signed from the government issuer. The tenant will verify that the +apartment's presentation request is valid and the apartment will also verify that the tenant's presentation submission +is valid. +At the end the apartment will verify the authenticity of the presentation submission and will be able to +cryptographically verify the birthdate of the tenant. ## Step 1 + Create new decentralized identities for an apartment, a government agency, and a future tenant. ![ssi-sdk](doc/dids.png) ## Step 2 -Government issuer using the holder’s DID issues credentials claiming age. The government issuer then signs the verifiable credentials to holder claiming age + +Government issuer using the holder’s DID issues credentials claiming age. The government issuer then signs the +verifiable credentials to holder claiming age ![ssi-sdk](doc/issuevc.png) ## Step 3 + Create Presentation Definition from the apartment to the holder which goes into a presentation request. The apartment is saying "here tenant, here is my what information I am requesting from you" ![ssi-sdk](doc/presentationrequest.png) ## Step 4 -The tenant verifies the presentation request from the apartment is valid and then constructs and signs a presentation submission + +The tenant verifies the presentation request from the apartment is valid and then constructs and signs a presentation +submission ![ssi-sdk](doc/presentationsubmission.png) ## Step 5 -The apartment verifies that the presentation submission is valid and then can cryptographically verify that the birthdate of the tenant is authentic. The tenant now has their age verified and they can move into the apartment! 🎉 + +The apartment verifies that the presentation submission is valid and then can cryptographically verify that the +birthdate of the tenant is authentic. The tenant now has their age verified and they can move into the apartment! 🎉 ![ssi-sdk](doc/aptverify.png) -# Running +# Running + Navigate to the ssi-sdk/example/use_cases/apartment_application directory and run ``` @@ -51,21 +65,23 @@ Government: `did:key:z6Mkpfz5uy9bkBLDLQmarYKVmJCDBd6qKxXLF2VRtcwPD74m` **Step 2:** Government issues Verifiable Credential new for tenant verifying birthdate and signs ``` + Verifiable Credential:{ - "@context": [ - "https://www.w3.org/2018/credentials/v1" - ], - "id": "1b81525a-ae7f-4f90-94ef-ebb04e348556", - "type": [ - "VerifiableCredential" - ], - "issuer": "did:key:z6Mkpfz5uy9bkBLDLQmarYKVmJCDBd6qKxXLF2VRtcwPD74m", - "issuanceDate": "2020-01-01T19:23:24Z", - "credentialSubject": { - "birthdate": "1975-01-01", - "id": "did:key:z6Mkj2VE6wby9NzHD6TLd7vmbUCCNXzAhv9Dtio6PcWNnwgn" - } +"@context": [ +"https://www.w3.org/2018/credentials/v1" +], +"id": "1b81525a-ae7f-4f90-94ef-ebb04e348556", +"type": [ +"VerifiableCredential" +], +"issuer": "did:key:z6Mkpfz5uy9bkBLDLQmarYKVmJCDBd6qKxXLF2VRtcwPD74m", +"issuanceDate": "2020-01-01T19:23:24Z", +"credentialSubject": { +"birthdate": "1975-01-01", +"id": "did:key:z6Mkj2VE6wby9NzHD6TLd7vmbUCCNXzAhv9Dtio6PcWNnwgn" } +} + ``` **Step 3:** The apartment creates a presentation request that confirms which information is required from the tenant @@ -73,32 +89,34 @@ Verifiable Credential:{ Presentation Definition that gets added to presentation request: ``` + +{ +"id": "48e93dce-12a0-4cf7-a016-44927c98e4dc", +"input_descriptors": [ { - "id": "48e93dce-12a0-4cf7-a016-44927c98e4dc", - "input_descriptors": [ - { - "id": "birthdate", - "purpose": "Age verification", - "format": { - "jwt_vc": { - "alg": [ - "EdDSA" - ] - } - }, - "constraints": { - "fields": [ - { - "path": [ - "$.credentialSubject.birthdate" - ], - "id": "birthdate" - } - ] - } - } - ] +"id": "birthdate", +"purpose": "Age verification", +"format": { +"jwt_vc": { +"alg": [ +"EdDSA" +] +} +}, +"constraints": { +"fields": [ +{ +"path": [ +"$.credentialSubject.birthdate" +], +"id": "birthdate" +} +] } +} +] +} + ``` **Step 4:** The holder creates a presentation submission to give to the apartment @@ -106,14 +124,18 @@ Presentation Definition that gets added to presentation request: Presentation Claim that gets added to presentation submission: ``` + { - "Credential": null, - "Presentation": null, - "LDPFormat": null, - "Token": "{\"@context\":[\"https://www.w3.org/2018/credentials/v1\"],\"id\":\"1b81525a-ae7f-4f90-94ef-ebb04e348556\",\"type\":[\"VerifiableCredential\"],\"issuer\":\"did:key:z6Mkpfz5uy9bkBLDLQmarYKVmJCDBd6qKxXLF2VRtcwPD74m\",\"issuanceDate\":\"2020-01-01T19:23:24Z\",\"credentialSubject\":{\"birthdate\":\"1975-01-01\",\"id\":\"did:key:z6Mkj2VE6wby9NzHD6TLd7vmbUCCNXzAhv9Dtio6PcWNnwgn\"}}", - "JWTFormat": "jwt_vc", - "SignatureAlgorithmOrProofType": "EdDSA" +"Credential": null, +"Presentation": null, +"LDPFormat": null, +"Token": "{\"@context\":[ +\"https://www.w3.org/2018/credentials/v1\"],\"id\":\"1b81525a-ae7f-4f90-94ef-ebb04e348556\",\"type\":[\"VerifiableCredential\"],\"issuer\":\"did:key:z6Mkpfz5uy9bkBLDLQmarYKVmJCDBd6qKxXLF2VRtcwPD74m\",\"issuanceDate\":\"2020-01-01T19:23:24Z\",\"credentialSubject\":{\"birthdate\":\"1975-01-01\",\"id\":\"did:key:z6Mkj2VE6wby9NzHD6TLd7vmbUCCNXzAhv9Dtio6PcWNnwgn\"}}" +, +"JWTFormat": "jwt_vc", +"SignatureAlgorithmOrProofType": "EdDSA" } + ``` **Step 5:** The apartment verifies that the presentation submission is valid and then can cryptographically verify that the birthdate of the tenant is authentic diff --git a/example/usecase/employer_university_flow/README.md b/example/usecase/employer_university_flow/README.md index abd57f5b..5e2ee308 100644 --- a/example/usecase/employer_university_flow/README.md +++ b/example/usecase/employer_university_flow/README.md @@ -36,7 +36,8 @@ sequenceDiagram ## Steps 1. Holder, Issuer, and Verifier all are granted wallets and they are initialized. -2. Issuer sends a VC to the holder saying they graduated from the University. It has additionally information such as the degree they graduated with. +2. Issuer sends a VC to the holder saying they graduated from the University. It has additionally information such as + the degree they graduated with. 3. Holder will store the VC in their wallet, "owning" the VC. 4. The Verifier will request to validate the Holder graduated from a university using a Presentation Request. 5. The Holder will respond with a Verified Submission, asserting the claim that they graduated from the university. diff --git a/example/usecase/employer_university_flow/pkg/verifier.go b/example/usecase/employer_university_flow/pkg/verifier.go index 692dba2e..84307887 100644 --- a/example/usecase/employer_university_flow/pkg/verifier.go +++ b/example/usecase/employer_university_flow/pkg/verifier.go @@ -2,35 +2,35 @@ package pkg import ( "encoding/json" - "errors" "github.com/TBD54566975/ssi-sdk/credential" "github.com/TBD54566975/ssi-sdk/credential/signing" "github.com/TBD54566975/ssi-sdk/crypto" - "github.com/TBD54566975/ssi-sdk/util" + "github.com/pkg/errors" ) -// ValidateAccess is a very simple validation process against a Presentation Submission It checks: +// ValidateAccess is a very simple validation process against a Presentation Submission +// It checks: // 1. That the VC is valid // 2. That the VC was issued by a trusted entity -func ValidateAccess(verifier crypto.JWTVerifier, data []byte) error { - vp, err := signing.VerifyVerifiablePresentationJWT(verifier, string(data)) +func ValidateAccess(verifier crypto.JWTVerifier, credBytes []byte) error { + vp, err := signing.VerifyVerifiablePresentationJWT(verifier, string(credBytes)) if err != nil { - return util.LoggingErrorMsg(err, "failed to validate VP signature") + return errors.Wrap(err, "failed to validate VP signature") } - if err := vp.IsValid(); err != nil { - return util.LoggingErrorMsg(err, "failed to validate VP") + if err = vp.IsValid(); err != nil { + return errors.Wrap(err, "failed to validate VP") } for _, untypedCredential := range vp.VerifiableCredential { - data, err := json.Marshal(untypedCredential) + credBytes, err = json.Marshal(untypedCredential) if err != nil { - return util.LoggingErrorMsg(err, "could not marshal credential in VP") + return errors.Wrap(err, "could not marshal credential in VP") } var vc credential.VerifiableCredential - if err := json.Unmarshal(data, &vc); err != nil { - return util.LoggingErrorMsg(err, "could not unmarshal credential in VP") + if err = json.Unmarshal(credBytes, &vc); err != nil { + return errors.Wrap(err, "could not unmarshal credential in VP") } // validity check if issuer, ok := vc.CredentialSubject["id"]; !ok || !TrustedEntities.isTrusted(issuer.(string)) { diff --git a/example/usecase/steel_thread/README.md b/example/usecase/steel_thread/README.md index 30f78213..26330479 100644 --- a/example/usecase/steel_thread/README.md +++ b/example/usecase/steel_thread/README.md @@ -1,9 +1,10 @@ # Steel Thread Signing & Verification - # Overview -Annotated steel thread flow for calling out all signing, verification, and key management. This document is focused on the signing and verification of objects. For Steel Thread, this means: Credentials and Credential Manifest request objects. +Annotated steel thread flow for calling out all signing, verification, and key management. This document is focused on +the signing and verification of objects. For Steel Thread, this means: Credentials and Credential Manifest request +objects. ## Setup @@ -12,17 +13,20 @@ We assume there are two parties: 1. Alice, using a wallet, applying for a credential 2. An issuer via the SSI Service, processing credential applications and issuing credentials -Each party has a single DID. Alice and the SSI Service each have a single `[did:key](https://w3c-ccg.github.io/did-method-key/)` DID. Alice stores her DID’s private key in her wallet. The SSI Service stores its private key in the service key store database. +Each party has a single DID. Alice and the SSI Service each have a +single `[did:key](https://w3c-ccg.github.io/did-method-key/)` DID. Alice stores her DID’s private key in her wallet. The +SSI Service stores its private key in the service key store database. ## Actors -For simplicity the DWN is abstracted from the description and diagrams below, since it plays no role in issuing or verifying credentials or credential manifest objects. +For simplicity the DWN is abstracted from the description and diagrams below, since it plays no role in issuing or +verifying credentials or credential manifest objects. -Alice and the Issuer are also abstracted from the diagram. Instead focusing on the software handling signing and verification — Alice’s Wallet and the SSI Service. +Alice and the Issuer are also abstracted from the diagram. Instead focusing on the software handling signing and +verification — Alice’s Wallet and the SSI Service. ![ssi-sdk](doc/steelthread.png) - ## Flow Annotation