From 26d7c40e91feb6e58270c2f1d3405500ddf65fdc Mon Sep 17 00:00:00 2001 From: roman_harazha Date: Fri, 16 Feb 2024 18:50:04 +0200 Subject: [PATCH] minor gist data response fix; add claim revocation status --- docs/spec/components/schemas/GistProof.yaml | 2 + .../assets/migrations/003_claim_revoked.sql | 5 + internal/data/claims.go | 2 + internal/data/pg/claims.go | 6 + .../service/api/handlers/create_identity.go | 117 ++++++++++-------- .../service/api/handlers/get_gist_data.go | 1 + resources/model_gist_proof.go | 1 + 7 files changed, 82 insertions(+), 52 deletions(-) create mode 100644 internal/assets/migrations/003_claim_revoked.sql diff --git a/docs/spec/components/schemas/GistProof.yaml b/docs/spec/components/schemas/GistProof.yaml index a5b7675..63a546b 100644 --- a/docs/spec/components/schemas/GistProof.yaml +++ b/docs/spec/components/schemas/GistProof.yaml @@ -17,6 +17,8 @@ properties: type: array items: type: string + index: + type: string value: type: string aux_existence: diff --git a/internal/assets/migrations/003_claim_revoked.sql b/internal/assets/migrations/003_claim_revoked.sql new file mode 100644 index 0000000..3a33a02 --- /dev/null +++ b/internal/assets/migrations/003_claim_revoked.sql @@ -0,0 +1,5 @@ +-- +migrate Up +alter table claims add column revoked boolean default false; + +-- +migrate Down +alter table claims drop column revoked; \ No newline at end of file diff --git a/internal/data/claims.go b/internal/data/claims.go index 5c60778..52f74f3 100644 --- a/internal/data/claims.go +++ b/internal/data/claims.go @@ -7,6 +7,7 @@ type ClaimQ interface { Insert(value Claim) error FilterBy(column string, value any) ClaimQ Get() (*Claim, error) + Select() ([]Claim, error) DeleteByID(id uuid.UUID) error ForUpdate() ClaimQ ResetFilter() ClaimQ @@ -17,4 +18,5 @@ type Claim struct { UserDID string `db:"user_did" structs:"user_did"` IssuerDID string `db:"issuer_did" structs:"issuer_did"` Document string `db:"document" structs:"document"` + Revoked bool `db:"revoked" structs:"revoked"` } diff --git a/internal/data/pg/claims.go b/internal/data/pg/claims.go index 73ee063..199283b 100644 --- a/internal/data/pg/claims.go +++ b/internal/data/pg/claims.go @@ -55,6 +55,12 @@ func (q *claimsQ) Get() (*data.Claim, error) { return &result, err } +func (q *claimsQ) Select() ([]data.Claim, error) { + var result []data.Claim + err := q.db.Select(&result, q.sql) + return result, err +} + func (q *claimsQ) DeleteByID(id uuid.UUID) error { if err := q.db.Exec(sq.Delete(claimsTableName).Where(sq.Eq{"id": id})); err != nil { return err diff --git a/internal/service/api/handlers/create_identity.go b/internal/service/api/handlers/create_identity.go index 1992442..64d0270 100644 --- a/internal/service/api/handlers/create_identity.go +++ b/internal/service/api/handlers/create_identity.go @@ -63,7 +63,10 @@ func CreateIdentity(w http.ResponseWriter, r *http.Request) { iss := Issuer(r) masterQ := MasterQ(r) - claim, err := masterQ.Claim().ResetFilter().FilterBy("user_did", req.Data.ID).Get() + claim, err := masterQ.Claim().ResetFilter(). + FilterBy("user_did", req.Data.ID). + FilterBy("revoked", false). + Get() if err != nil { Log(r).WithError(err).Error("failed to get claim by user DID") ape.RenderErr(w, problems.InternalError()) @@ -87,75 +90,85 @@ func CreateIdentity(w http.ResponseWriter, r *http.Request) { return } - if err := masterQ.Transaction(func(db data.MasterQ) error { - cfg := VerifierConfig(r) + cfg := VerifierConfig(r) - if err := verifySignature(req); err != nil { - ape.RenderErr(w, problems.InternalError()) - return errors.Wrap(err, "failed to verify signature") - } + if err := verifySignature(req); err != nil { + Log(r).WithError(err).Error("failed to verify signature") + ape.RenderErr(w, problems.InternalError()) + return + } - switch algorithms[req.Data.DocumentSOD.Algorithm] { - case SHA1withECDSA: - if err := verifier.VerifyGroth16(req.Data.ZKProof, cfg.VerificationKeys[SHA1]); err != nil { - ape.RenderErr(w, problems.BadRequest(err)...) - return errors.Wrap(err, "failed to verify Groth16") - } - case SHA256withRSA, SHA256withECDSA: - if err := verifier.VerifyGroth16(req.Data.ZKProof, cfg.VerificationKeys[SHA256]); err != nil { - ape.RenderErr(w, problems.BadRequest(err)...) - return errors.Wrap(err, "failed to verify Groth16") - } + switch algorithms[req.Data.DocumentSOD.Algorithm] { + case SHA1withECDSA: + if err := verifier.VerifyGroth16(req.Data.ZKProof, cfg.VerificationKeys[SHA1]); err != nil { + Log(r).WithError(err).Error("failed to verify Groth16") + ape.RenderErr(w, problems.BadRequest(err)...) + return } - - encapsulatedContentBytes, err := hex.DecodeString(req.Data.DocumentSOD.EncapsulatedContent) - if err != nil { - ape.RenderErr(w, problems.InternalError()) - return errors.Wrap(err, "failed to decode hex string") + case SHA256withRSA, SHA256withECDSA: + if err := verifier.VerifyGroth16(req.Data.ZKProof, cfg.VerificationKeys[SHA256]); err != nil { + Log(r).WithError(err).Error("failed to verify Groth16") + ape.RenderErr(w, problems.BadRequest(err)...) + return } + } - encapsulatedData := resources.EncapsulatedData{} - _, err = asn1.Unmarshal(encapsulatedContentBytes, &encapsulatedData) - if err != nil { - ape.RenderErr(w, problems.InternalError()) - return errors.Wrap(err, "failed to unmarshal ASN.1") - } + encapsulatedContentBytes, err := hex.DecodeString(req.Data.DocumentSOD.EncapsulatedContent) + if err != nil { + Log(r).WithError(err).Error("failed to decode hex string") + ape.RenderErr(w, problems.InternalError()) + return + } - if err := validatePubSignals(cfg, req.Data, encapsulatedData.PrivateKey.El1.OctetStr.Bytes); err != nil { - ape.RenderErr(w, problems.BadRequest(err)...) - return errors.Wrap(err, "failed to validate pub signals") - } + encapsulatedData := resources.EncapsulatedData{} + _, err = asn1.Unmarshal(encapsulatedContentBytes, &encapsulatedData) + if err != nil { + Log(r).WithError(err).Error("failed to unmarshal ASN.1") + ape.RenderErr(w, problems.InternalError()) + return + } - if err := validateCert([]byte(req.Data.DocumentSOD.PemFile), cfg.MasterCerts); err != nil { - ape.RenderErr(w, problems.BadRequest(err)...) - return errors.Wrap(err, "failed to validate certificate") - } + if err := validatePubSignals(cfg, req.Data, encapsulatedData.PrivateKey.El1.OctetStr.Bytes); err != nil { + Log(r).WithError(err).Error("failed to validate pub signals") + ape.RenderErr(w, problems.BadRequest(err)...) + return + } - identityExpiration, err := getExpirationTimeFromPubSignals(req.Data.ZKProof.PubSignals) - if err != nil { - ape.RenderErr(w, problems.BadRequest(err)...) - return errors.Wrap(err, "failed to get expiration time") - } + if err := validateCert([]byte(req.Data.DocumentSOD.PemFile), cfg.MasterCerts); err != nil { + Log(r).WithError(err).Error("failed to validate certificate") + ape.RenderErr(w, problems.BadRequest(err)...) + return + } - issuingAuthority, err := strconv.Atoi(req.Data.ZKProof.PubSignals[2]) - if err != nil { - ape.RenderErr(w, problems.InternalError()) - return errors.Wrap(err, "failed to convert string to int") - } + identityExpiration, err := getExpirationTimeFromPubSignals(req.Data.ZKProof.PubSignals) + if err != nil { + Log(r).WithError(err).Error("failed to get expiration time") + ape.RenderErr(w, problems.BadRequest(err)...) + return + } - // check if there is a claim for this document already - claim, err := db.Claim().ResetFilter(). + issuingAuthority, err := strconv.Atoi(req.Data.ZKProof.PubSignals[2]) + if err != nil { + Log(r).WithError(err).Error("failed to convert string to int") + ape.RenderErr(w, problems.InternalError()) + return + } + + if err := masterQ.Transaction(func(db data.MasterQ) error { + // check if there are any claims for this document already + claims, err := db.Claim().ResetFilter(). FilterBy("document", req.Data.DocumentSOD.SignedAttributes). + FilterBy("revoked", false). ForUpdate(). - Get() + Select() if err != nil { ape.RenderErr(w, problems.InternalError()) return errors.Wrap(err, "failed to get claim") } // revoke if so - if claim != nil { - if err := revokeOutdatedClaim(db, iss, claim.ID); err != nil { + for _, claimToRevoke := range claims { + if err := revokeOutdatedClaim(db, iss, claimToRevoke.ID); err != nil { ape.RenderErr(w, problems.InternalError()) return errors.Wrap(err, "failed to revoke outdated claim") } diff --git a/internal/service/api/handlers/get_gist_data.go b/internal/service/api/handlers/get_gist_data.go index fefd6ce..8692272 100644 --- a/internal/service/api/handlers/get_gist_data.go +++ b/internal/service/api/handlers/get_gist_data.go @@ -74,6 +74,7 @@ func newGistDataResponse(userDID string, proof abi.IStateGistProof, root *big.In Root: proof.Root.String(), Existence: proof.Existence, Siblings: siblings, + Index: proof.Index.String(), Value: proof.Value.String(), AuxExistence: proof.AuxExistence, AuxIndex: proof.AuxIndex.String(), diff --git a/resources/model_gist_proof.go b/resources/model_gist_proof.go index ef977c0..37d443d 100644 --- a/resources/model_gist_proof.go +++ b/resources/model_gist_proof.go @@ -9,6 +9,7 @@ type GistProof struct { AuxIndex string `json:"aux_index"` AuxValue string `json:"aux_value"` Existence bool `json:"existence"` + Index string `json:"index"` Root string `json:"root"` Siblings []string `json:"siblings"` Value string `json:"value"`