From 3a66346e56d4cbcd302a15c21b0a33d1b6bafc8e Mon Sep 17 00:00:00 2001 From: Maksym Hrynenko <108219165+mhrynenko@users.noreply.github.com> Date: Mon, 16 Dec 2024 15:10:31 +0200 Subject: [PATCH] feature: optional expiration lower bound param; expand JWT auth endpoints (#18) * update (docs): adding expiration lower bound param in paths via query params or request body * update (migrations): add expiration_lower_bound column * update (data): handle ExpirationLowerBound field during insert * update (helpers): move selector params in separate structure, use BirthDateFormat as general DateFormat for params, add expiration lower bound handling to build selector and set proof params * update (service): to use refactored helpers package, handle expiration lower bound for verification links and proof params * fix: expiration lower bound default value during Inserting new VerifyUser instance; move default timestamp value in constant * update: Auth middleware to store claims in ctx for future check in endpoints * feature: add authentication for sensitive endpoints * fix: make auth service optional using Enabled config field, set it disabled by default, handle disabled client in helpers authenticates method * fix: build event data in the same way as smart contract does * fix: use user id keccak256 hash in event data * add: extra logs to debug * fix: ExtractEventData without prepending * feature: implement upsert to update params for users with the same user id * add: helper function to check default ZK date * update: use Upsert instead of Get/Insert approach, use helper default zk date for clear code * update: auth version * fix: var-naming linter error --- config.yaml | 1 + .../parameters/expirationLowerBoundParam.yaml | 10 +++ docs/spec/components/schemas/User.yaml | 7 +- ...ification-svc@public@proof-parameters.yaml | 1 + go.mod | 4 +- go.sum | 4 +- .../migrations/006_expirationLowerBound.sql | 4 ++ internal/data/pg/verify_users.go | 57 ++++++++------- internal/data/verify_users.go | 31 +++++---- internal/service/handlers/ctx.go | 29 +++++++- internal/service/handlers/delete_user.go | 15 +++- .../service/handlers/get_proof_parameters.go | 69 ++++++++++--------- internal/service/handlers/helpers/auth.go | 14 ++++ .../service/handlers/helpers/proof_params.go | 67 +++++++++++------- internal/service/handlers/proof_params.go | 28 +++++--- .../service/handlers/proof_params_light.go | 28 +++++--- .../service/handlers/verification_callback.go | 10 ++- .../service/handlers/verification_link.go | 47 ++++++------- .../handlers/verification_link_light.go | 46 ++++++------- internal/service/middlewares/auth.go | 67 +++++++----------- .../service/requests/get_proof_parameters.go | 15 ++-- internal/service/router.go | 11 +-- resources/model_user_attributes.go | 2 + 23 files changed, 333 insertions(+), 234 deletions(-) create mode 100644 docs/spec/components/parameters/expirationLowerBoundParam.yaml create mode 100644 internal/assets/migrations/006_expirationLowerBound.sql create mode 100644 internal/service/handlers/helpers/auth.go diff --git a/config.yaml b/config.yaml index ec4e448..39c6750 100644 --- a/config.yaml +++ b/config.yaml @@ -33,4 +33,5 @@ poseidonsmt_root_verifier: request_timeout: 10s auth: + enabled: false addr: http://rarime-auth \ No newline at end of file diff --git a/docs/spec/components/parameters/expirationLowerBoundParam.yaml b/docs/spec/components/parameters/expirationLowerBoundParam.yaml new file mode 100644 index 0000000..394ab4e --- /dev/null +++ b/docs/spec/components/parameters/expirationLowerBoundParam.yaml @@ -0,0 +1,10 @@ +in: query +name: 'expiration_lower_bound' +required: false +description: | + Param to enable or disable passport expiration lower bound check.\n + - Empty value or `false` - default date is used (52983525027888) + - `true` - encoded current UTC timestamp is used +example: true +schema: + type: boolean \ No newline at end of file diff --git a/docs/spec/components/schemas/User.yaml b/docs/spec/components/schemas/User.yaml index 372c06e..57d55d5 100644 --- a/docs/spec/components/schemas/User.yaml +++ b/docs/spec/components/schemas/User.yaml @@ -30,4 +30,9 @@ allOf: sex: type: boolean example: true - description: "Enable verification of sex param" \ No newline at end of file + description: "Enable verification of sex param" + expiration_lower_bound: + type: boolean + example: true + description: "Enable verification of expiration lower bound param. When nothing (or `false`) set default value is used, + otherwise encoded current UTC timestamp will be stored." diff --git a/docs/spec/paths/integrations@verification-svc@public@proof-parameters.yaml b/docs/spec/paths/integrations@verification-svc@public@proof-parameters.yaml index 1df682f..a64fea1 100644 --- a/docs/spec/paths/integrations@verification-svc@public@proof-parameters.yaml +++ b/docs/spec/paths/integrations@verification-svc@public@proof-parameters.yaml @@ -11,6 +11,7 @@ get: - $ref: '#/components/parameters/typeOfVerificationUniquenessParam' - $ref: '#/components/parameters/nationalityParam' - $ref: '#/components/parameters/eventIdParam' + - $ref: '#/components/parameters/expirationLowerBoundParam' responses: 200: description: Success diff --git a/go.mod b/go.mod index 82cc829..a14b82f 100644 --- a/go.mod +++ b/go.mod @@ -8,10 +8,9 @@ require ( github.com/ethereum/go-ethereum v1.14.12 github.com/go-chi/chi v4.1.2+incompatible github.com/go-ozzo/ozzo-validation/v4 v4.3.0 - github.com/iden3/go-iden3-crypto v0.0.15 github.com/iden3/go-rapidsnark/types v0.0.3 github.com/pkg/errors v0.9.1 - github.com/rarimo/web3-auth-svc v0.1.1-rc0 + github.com/rarimo/web3-auth-svc v0.1.4 github.com/rarimo/zkverifier-kit v1.2.4 github.com/rubenv/sql-migrate v1.7.0 github.com/status-im/keycard-go v0.2.0 @@ -49,6 +48,7 @@ require ( github.com/gorilla/websocket v1.5.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/holiman/uint256 v1.3.1 // indirect + github.com/iden3/go-iden3-crypto v0.0.15 // indirect github.com/iden3/go-rapidsnark/verifier v0.0.5 // indirect github.com/jmoiron/sqlx v1.3.5 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect diff --git a/go.sum b/go.sum index 2fbbc3e..a158f22 100644 --- a/go.sum +++ b/go.sum @@ -1962,8 +1962,8 @@ github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1 github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/rarimo/web3-auth-svc v0.1.1-rc0 h1:Gqz2rU7vUgQGQDUBHw7wnakBkXsUzFsepd+oU5lOLY0= -github.com/rarimo/web3-auth-svc v0.1.1-rc0/go.mod h1:d1SsDcwzSqSPZABy3wMhrVrhNb/JQ40DLcjxTm0KPBQ= +github.com/rarimo/web3-auth-svc v0.1.4 h1:p57MgO4belYkELTVvWRO4979Z1WshujajTnDcjzf2PM= +github.com/rarimo/web3-auth-svc v0.1.4/go.mod h1:d1SsDcwzSqSPZABy3wMhrVrhNb/JQ40DLcjxTm0KPBQ= github.com/rarimo/zkverifier-kit v1.2.4 h1:AJ5ZAyOYOGR2QiDlOA2ul/QMZnjBZ/VzPqLjSIUbZgw= github.com/rarimo/zkverifier-kit v1.2.4/go.mod h1:3YDg5dTkDRr4IdfaDHGYetopd6gS/2SuwSeseYTWwNw= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= diff --git a/internal/assets/migrations/006_expirationLowerBound.sql b/internal/assets/migrations/006_expirationLowerBound.sql new file mode 100644 index 0000000..929368e --- /dev/null +++ b/internal/assets/migrations/006_expirationLowerBound.sql @@ -0,0 +1,4 @@ +-- +migrate Up +ALTER TABLE verify_users ADD COLUMN expiration_lower_bound TEXT NOT NULL DEFAULT '0x303030303030'; +-- +migrate Down +ALTER TABLE verify_users DROP COLUMN expiration_lower_bound; \ No newline at end of file diff --git a/internal/data/pg/verify_users.go b/internal/data/pg/verify_users.go index 60b46de..6adc824 100644 --- a/internal/data/pg/verify_users.go +++ b/internal/data/pg/verify_users.go @@ -6,12 +6,10 @@ import ( "fmt" "time" - "github.com/pkg/errors" - "gitlab.com/distributed_lab/kit/pgdb" - sq "github.com/Masterminds/squirrel" - + "github.com/pkg/errors" "github.com/rarimo/verificator-svc/internal/data" + "gitlab.com/distributed_lab/kit/pgdb" ) type VerifyUsersQ struct { @@ -62,33 +60,42 @@ func (q *VerifyUsersQ) Get() (*data.VerifyUsers, error) { return &result, nil } -func (q *VerifyUsersQ) Insert(VerifyUsers *data.VerifyUsers) error { +func (q *VerifyUsersQ) Upsert(VerifyUsers *data.VerifyUsers) (data.VerifyUsers, error) { + var response data.VerifyUsers proofJSON, err := json.Marshal(VerifyUsers.Proof) if err != nil { - return fmt.Errorf("failed to marshal proof for user %s: %w", VerifyUsers.UserID, err) + return response, fmt.Errorf("failed to marshal proof for user %s: %w", VerifyUsers.UserID, err) } - stmt := sq.Insert(verifyUsersTableName).SetMap(map[string]interface{}{ - "user_id": VerifyUsers.UserID, - "user_id_hash": VerifyUsers.UserIDHash, - "age_lower_bound": VerifyUsers.AgeLowerBound, - "nationality": VerifyUsers.Nationality, - "uniqueness": VerifyUsers.Uniqueness, - "event_id": VerifyUsers.EventId, - "status": VerifyUsers.Status, - "proof": proofJSON, - "sex": VerifyUsers.Sex, - "sex_enable": VerifyUsers.SexEnable, - "nationality_enable": VerifyUsers.NationalityEnable, - "anonymous_id": VerifyUsers.AnonymousID, - "nullifier": VerifyUsers.Nullifier, - }) - - if err = q.db.Exec(stmt); err != nil { - return fmt.Errorf("insert user %+v: %w", VerifyUsers, err) + newData := map[string]interface{}{ + "user_id_hash": VerifyUsers.UserIDHash, + "age_lower_bound": VerifyUsers.AgeLowerBound, + "nationality": VerifyUsers.Nationality, + "uniqueness": VerifyUsers.Uniqueness, + "event_id": VerifyUsers.EventID, + "status": VerifyUsers.Status, + "proof": proofJSON, + "sex": VerifyUsers.Sex, + "sex_enable": VerifyUsers.SexEnable, + "nationality_enable": VerifyUsers.NationalityEnable, + "anonymous_id": VerifyUsers.AnonymousID, + "nullifier": VerifyUsers.Nullifier, + "expiration_lower_bound": VerifyUsers.ExpirationLowerBound, } - return nil + updateStmt, args, _ := sq.Update(" ").SetMap(newData).ToSql() + + newData["user_id"] = VerifyUsers.UserID + + query := sq.Insert(verifyUsersTableName).SetMap(newData). + Suffix("ON CONFLICT (user_id) DO "+updateStmt, args...). + Suffix("RETURNING *") + + if err = q.db.Get(&response, query); err != nil { + return response, errors.Wrap(err, "failed to upsert new row") + } + + return response, nil } func (q *VerifyUsersQ) Update(VerifyUsers *data.VerifyUsers) error { diff --git a/internal/data/verify_users.go b/internal/data/verify_users.go index fb372d5..666244c 100644 --- a/internal/data/verify_users.go +++ b/internal/data/verify_users.go @@ -5,20 +5,21 @@ import ( ) type VerifyUsers struct { - UserID string `db:"user_id"` - UserIDHash string `db:"user_id_hash"` - AgeLowerBound int `db:"age_lower_bound"` - Nationality string `db:"nationality"` - CreatedAt time.Time `db:"created_at"` - Uniqueness bool `db:"uniqueness"` - EventId string `db:"event_id"` - Status string `db:"status"` - Proof []byte `db:"proof"` - Sex string `db:"sex"` - SexEnable bool `db:"sex_enable"` - NationalityEnable bool `db:"nationality_enable"` - AnonymousID string `db:"anonymous_id"` - Nullifier string `db:"nullifier"` + UserID string `db:"user_id"` + UserIDHash string `db:"user_id_hash"` + AgeLowerBound int `db:"age_lower_bound"` + Nationality string `db:"nationality"` + CreatedAt time.Time `db:"created_at"` + Uniqueness bool `db:"uniqueness"` + EventID string `db:"event_id"` + Status string `db:"status"` + Proof []byte `db:"proof"` + Sex string `db:"sex"` + SexEnable bool `db:"sex_enable"` + NationalityEnable bool `db:"nationality_enable"` + AnonymousID string `db:"anonymous_id"` + Nullifier string `db:"nullifier"` + ExpirationLowerBound string `db:"expiration_lower_bound"` } type VerifyUsersQ interface { @@ -27,7 +28,7 @@ type VerifyUsersQ interface { Get() (*VerifyUsers, error) Select() ([]VerifyUsers, error) Update(*VerifyUsers) error - Insert(*VerifyUsers) error + Upsert(*VerifyUsers) (VerifyUsers, error) Delete() error DeleteByID(*VerifyUsers) error diff --git a/internal/service/handlers/ctx.go b/internal/service/handlers/ctx.go index 9308c6c..4ff1246 100644 --- a/internal/service/handlers/ctx.go +++ b/internal/service/handlers/ctx.go @@ -2,10 +2,12 @@ package handlers import ( "context" - "github.com/rarimo/verificator-svc/internal/config" - "github.com/rarimo/verificator-svc/internal/data" "net/http" + "github.com/rarimo/verificator-svc/internal/config" + "github.com/rarimo/verificator-svc/internal/data" + "github.com/rarimo/web3-auth-svc/pkg/auth" + "github.com/rarimo/web3-auth-svc/resources" "gitlab.com/distributed_lab/logan/v3" ) @@ -16,8 +18,9 @@ const ( verifyUserQCtxKey verifiersCtxKey callbackCtxKey - proofParametersCtxKey + userClaimsCtxKey signatureVerificationCtxKey + authCtxKey ) func CtxLog(entry *logan.Entry) func(context.Context) context.Context { @@ -69,3 +72,23 @@ func CtxSignatureVerification(c config.SignatureVerificationConfig) func(context func SignatureVerification(r *http.Request) config.SignatureVerificationConfig { return r.Context().Value(signatureVerificationCtxKey).(config.SignatureVerificationConfig) } + +func CtxUserClaims(claim []resources.Claim) func(context.Context) context.Context { + return func(ctx context.Context) context.Context { + return context.WithValue(ctx, userClaimsCtxKey, claim) + } +} + +func UserClaims(r *http.Request) []resources.Claim { + return r.Context().Value(userClaimsCtxKey).([]resources.Claim) +} + +func CtxAuthClient(auth *auth.Client) func(context.Context) context.Context { + return func(ctx context.Context) context.Context { + return context.WithValue(ctx, authCtxKey, auth) + } +} + +func AuthClient(r *http.Request) *auth.Client { + return r.Context().Value(authCtxKey).(*auth.Client) +} diff --git a/internal/service/handlers/delete_user.go b/internal/service/handlers/delete_user.go index dd58ff5..d253849 100644 --- a/internal/service/handlers/delete_user.go +++ b/internal/service/handlers/delete_user.go @@ -1,19 +1,32 @@ package handlers import ( + "net/http" + + "github.com/rarimo/verificator-svc/internal/service/handlers/helpers" "github.com/rarimo/verificator-svc/internal/service/requests" + "github.com/rarimo/web3-auth-svc/pkg/auth" "gitlab.com/distributed_lab/ape" "gitlab.com/distributed_lab/ape/problems" - "net/http" ) func DeleteUser(w http.ResponseWriter, r *http.Request) { + if !auth.Authenticates(UserClaims(r), auth.AdminGrant) { + ape.RenderErr(w, problems.Unauthorized()) + return + } + userID, err := requests.GetPathUserID(r) if err != nil { ape.RenderErr(w, problems.BadRequest(err)...) return } + if !helpers.Authenticates(AuthClient(r), UserClaims(r), auth.UserGrant(userID)) { + ape.RenderErr(w, problems.Unauthorized()) + return + } + deletedUser, err := VerifyUsersQ(r).WhereID(userID).Get() if err != nil { Log(r).WithError(err).Error("failed to get user by userID") diff --git a/internal/service/handlers/get_proof_parameters.go b/internal/service/handlers/get_proof_parameters.go index 46b75f1..762527b 100644 --- a/internal/service/handlers/get_proof_parameters.go +++ b/internal/service/handlers/get_proof_parameters.go @@ -2,16 +2,19 @@ package handlers import ( "fmt" + "net/http" + "strconv" + "time" + + "github.com/ethereum/go-ethereum/common" "github.com/rarimo/verificator-svc/internal/data" "github.com/rarimo/verificator-svc/internal/service/handlers/helpers" "github.com/rarimo/verificator-svc/internal/service/requests" "github.com/rarimo/verificator-svc/internal/service/responses" "github.com/rarimo/verificator-svc/resources" + "github.com/rarimo/web3-auth-svc/pkg/auth" "gitlab.com/distributed_lab/ape" "gitlab.com/distributed_lab/ape/problems" - "net/http" - "strconv" - "time" ) func GetProofParameters(w http.ResponseWriter, r *http.Request) { @@ -21,11 +24,24 @@ func GetProofParameters(w http.ResponseWriter, r *http.Request) { return } + if !helpers.Authenticates(AuthClient(r), UserClaims(r), auth.UserGrant(userInputs.UserID)) { + ape.RenderErr(w, problems.Unauthorized()) + return + } + var ( IdentityCounterUpperBound int32 TimestampUpperBound = "0" eventID = Verifiers(r).EventID - proofSelector = helpers.CalculateProofSelector(userInputs.Uniqueness, userInputs.AgeLowerBound, userInputs.Nationality, true, true) + proofSelector = helpers.CalculateProofSelector(helpers.SelectorParams{ + Uniqueness: userInputs.Uniqueness, + AgeLowerBound: userInputs.AgeLowerBound, + Nationality: userInputs.Nationality, + SexEnable: true, + NationalityEnable: true, + ExpirationLowerBound: userInputs.ExpirationLowerBound, + }) + expirationLowerBound = helpers.GetExpirationLowerBound(userInputs.ExpirationLowerBound) ) if userInputs.EventID != "" { @@ -38,32 +54,27 @@ func GetProofParameters(w http.ResponseWriter, r *http.Request) { IdentityCounterUpperBound = 1 } - userIdHash, err := helpers.StringToPoseidonHash(userInputs.UserId) - if err != nil { - Log(r).WithError(err).Errorf("failed to convert user with userID [%s] to poseidon hash", userInputs.UserId) - ape.RenderErr(w, problems.InternalError()) - return - } user := &data.VerifyUsers{ - UserID: userInputs.UserId, - UserIDHash: userIdHash, - CreatedAt: time.Now().UTC(), - Status: "not_verified", - Nationality: userInputs.Nationality, - AgeLowerBound: userInputs.AgeLowerBound, - Uniqueness: userInputs.Uniqueness, - Proof: []byte{}, + UserID: userInputs.UserID, + UserIDHash: helpers.BytesToKeccak256Hash(common.HexToAddress(userInputs.UserID).Bytes()), + CreatedAt: time.Now().UTC(), + Status: "not_verified", + Nationality: userInputs.Nationality, + AgeLowerBound: userInputs.AgeLowerBound, + Uniqueness: userInputs.Uniqueness, + Proof: []byte{}, + ExpirationLowerBound: expirationLowerBound, } proofParameters := resources.ParametersAttributes{ - BirthDateLowerBound: "0x303030303030", + BirthDateLowerBound: helpers.DefaultDateHex, BirthDateUpperBound: helpers.CalculateBirthDateHex(userInputs.AgeLowerBound), CallbackUrl: fmt.Sprintf("%s/integrations/verificator-svc/public/callback/%s", Callback(r).URL, user.UserIDHash), CitizenshipMask: helpers.Utf8ToHex(userInputs.Nationality), EventData: user.UserIDHash, EventId: eventID, - ExpirationDateLowerBound: "52983525027888", - ExpirationDateUpperBound: "52983525027888", + ExpirationDateLowerBound: expirationLowerBound, + ExpirationDateUpperBound: helpers.DefaultDateHex, IdentityCounter: 0, IdentityCounterLowerBound: 0, IdentityCounterUpperBound: IdentityCounterUpperBound, @@ -72,22 +83,12 @@ func GetProofParameters(w http.ResponseWriter, r *http.Request) { TimestampUpperBound: TimestampUpperBound, } - existingUser, err := VerifyUsersQ(r).WhereHashID(user.UserIDHash).Get() + dbUser, err := VerifyUsersQ(r).Upsert(user) if err != nil { - Log(r).WithError(err).Errorf("failed to query user with userID [%s]", userIdHash) - ape.RenderErr(w, problems.InternalError()) - return - } - if existingUser != nil { - ape.Render(w, responses.NewProofParametersResponse(*existingUser, proofParameters)) - return - } - - if err = VerifyUsersQ(r).Insert(user); err != nil { - Log(r).WithError(err).Errorf("failed to insert user with userID [%s]", user.UserIDHash) + Log(r).WithError(err).WithField("user", user).Errorf("failed to upsert user with userID [%s]", user.UserIDHash) ape.RenderErr(w, problems.InternalError()) return } - ape.Render(w, responses.NewProofParametersResponse(*user, proofParameters)) + ape.Render(w, responses.NewProofParametersResponse(dbUser, proofParameters)) } diff --git a/internal/service/handlers/helpers/auth.go b/internal/service/handlers/helpers/auth.go new file mode 100644 index 0000000..f76d17c --- /dev/null +++ b/internal/service/handlers/helpers/auth.go @@ -0,0 +1,14 @@ +package helpers + +import ( + "github.com/rarimo/web3-auth-svc/pkg/auth" + "github.com/rarimo/web3-auth-svc/resources" +) + +func Authenticates(client *auth.Client, claims []resources.Claim, grants ...auth.Grant) bool { + if !client.Enabled { + return true + } + + return auth.Authenticates(claims, grants...) +} diff --git a/internal/service/handlers/helpers/proof_params.go b/internal/service/handlers/helpers/proof_params.go index 8a9f395..a6cfbf3 100644 --- a/internal/service/handlers/helpers/proof_params.go +++ b/internal/service/handlers/helpers/proof_params.go @@ -7,7 +7,8 @@ import ( "math/big" "time" - "github.com/iden3/go-iden3-crypto/poseidon" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" zk "github.com/rarimo/zkverifier-kit" "github.com/status-im/keycard-go/hexutils" ) @@ -22,9 +23,19 @@ const ( ExpirationDateUpperbound = 13 BirthDateLowerboundBit = 14 BirthDateUpperboundBit = 15 - BirthDateFormat = "060102" + DateFormat = "060102" + DefaultDateHex = "0x303030303030" ) +type SelectorParams struct { + Uniqueness bool + AgeLowerBound int + Nationality string + SexEnable bool + NationalityEnable bool + ExpirationLowerBound bool +} + func PubSignalsToSha256(pubSignals []string) ([]byte, error) { var hash = sha256.New() for _, pubSignalByte := range pubSignals { @@ -47,15 +58,12 @@ func PubSignalsToSha256(pubSignals []string) ([]byte, error) { return messageHash, nil } -func StringToPoseidonHash(inputString string) (string, error) { - inputBytes := []byte(inputString) - - hash, err := poseidon.HashBytes(inputBytes) - if err != nil { - return "", fmt.Errorf("failde to convert input bytes to hash: %w", err) +func BytesToKeccak256Hash(input []byte) string { + hashInt := new(big.Int).SetBytes(crypto.Keccak256(common.LeftPadBytes(input, 32))) + mask, _ := new(big.Int).SetString("00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16) + result := new(big.Int).And(hashInt, mask) - } - return fmt.Sprintf("0x%s", hex.EncodeToString(hash.Bytes())), nil + return fmt.Sprintf("0x%s", result.Text(16)) } func Utf8ToHex(input string) string { @@ -76,40 +84,49 @@ func DecimalToHexToUtf8(input string) (string, error) { } func CalculateBirthDateHex(ageLowerBound int) string { - allowedBirthDate := time.Now().UTC().AddDate(-ageLowerBound, 0, 0) - formattedDate := []byte(allowedBirthDate.Format(BirthDateFormat)) - hexBirthDateLoweBound := hexutils.BytesToHex(formattedDate) + return FormatDateTime(time.Now().UTC().AddDate(-ageLowerBound, 0, 0)) +} + +func GetExpirationLowerBound(expirationLowerBound bool) string { + if !expirationLowerBound { + return DefaultDateHex + } + + return FormatDateTime(time.Now().UTC()) +} - return fmt.Sprintf("0x%s", hexBirthDateLoweBound) +func FormatDateTime(date time.Time) string { + return fmt.Sprintf("0x%s", hexutils.BytesToHex([]byte(date.Format(DateFormat)))) } func ExtractEventData(getter zk.PubSignalGetter) (string, error) { - userIDHashDecimal, ok := new(big.Int).SetString(getter.Get(zk.EventData), 10) + userIDBig, ok := new(big.Int).SetString(getter.Get(zk.EventData), 10) if !ok { return "", fmt.Errorf("failed to parse event data") } - var userIDHash [32]byte - userIDHashDecimal.FillBytes(userIDHash[:]) - return fmt.Sprintf("0x%s", hex.EncodeToString(userIDHash[:])), nil + return fmt.Sprintf("0x%s", userIDBig.Text(16)), nil } -func CalculateProofSelector(uniqueness bool, ageLowerBound int, nationality string, sexEnable bool, nationalityEnable bool) int { +func CalculateProofSelector(p SelectorParams) int { var bitLine uint32 bitLine |= 1 << NullifierBit - if nationality != "" || nationalityEnable { + if p.Nationality != "" || p.NationalityEnable { bitLine |= 1 << CitizenshipBit } - if sexEnable { + if p.SexEnable { bitLine |= 1 << SexBit } - if ageLowerBound != -1 { + if p.AgeLowerBound != -1 { bitLine |= 1 << BirthDateUpperboundBit } - if uniqueness { + if p.Uniqueness { bitLine |= 1 << TimestampUpperBoundBit bitLine |= 1 << IdentityCounterUpperBoundBit } + if p.ExpirationLowerBound { + bitLine |= 1 << ExpirationDateLowerboundBit + } return int(bitLine) } @@ -139,3 +156,7 @@ func CheckUniqueness(selectorInt int, serviceStartTimestamp, identityTimestampUp return false, nil } + +func IsDefaultZKDate(date string) bool { + return date == DefaultDateHex +} diff --git a/internal/service/handlers/proof_params.go b/internal/service/handlers/proof_params.go index b5ee2fe..31e33fc 100644 --- a/internal/service/handlers/proof_params.go +++ b/internal/service/handlers/proof_params.go @@ -2,14 +2,15 @@ package handlers import ( "fmt" + "net/http" + "strconv" + "github.com/rarimo/verificator-svc/internal/service/handlers/helpers" "github.com/rarimo/verificator-svc/internal/service/requests" "github.com/rarimo/verificator-svc/internal/service/responses" "github.com/rarimo/verificator-svc/resources" "gitlab.com/distributed_lab/ape" "gitlab.com/distributed_lab/ape/problems" - "net/http" - "strconv" ) func GetProofParamsById(w http.ResponseWriter, r *http.Request) { @@ -36,16 +37,23 @@ func GetProofParamsById(w http.ResponseWriter, r *http.Request) { TimestampUpperBound = "0" eventID = Verifiers(r).EventID birthDateUpperBound = helpers.CalculateBirthDateHex(existingUser.AgeLowerBound) - proofSelector = helpers.CalculateProofSelector(existingUser.Uniqueness, existingUser.AgeLowerBound, existingUser.Nationality, existingUser.SexEnable, existingUser.NationalityEnable) - callbackURL = fmt.Sprintf("%s/integrations/verificator-svc/public/callback/%s", Callback(r).URL, userIDHash) + proofSelector = helpers.CalculateProofSelector(helpers.SelectorParams{ + Uniqueness: existingUser.Uniqueness, + AgeLowerBound: existingUser.AgeLowerBound, + Nationality: existingUser.Nationality, + SexEnable: existingUser.SexEnable, + NationalityEnable: existingUser.NationalityEnable, + ExpirationLowerBound: !helpers.IsDefaultZKDate(existingUser.ExpirationLowerBound), // If there is non-default value, selector should be enabled + }) + callbackURL = fmt.Sprintf("%s/integrations/verificator-svc/public/callback/%s", Callback(r).URL, userIDHash) ) - if existingUser.EventId != "" { - eventID = existingUser.EventId + if existingUser.EventID != "" { + eventID = existingUser.EventID } if existingUser.AgeLowerBound == -1 { - birthDateUpperBound = "0x303030303030" + birthDateUpperBound = helpers.DefaultDateHex } if proofSelector&(1<