From c6db25f013ade12aa2cc337521657b9b545657b8 Mon Sep 17 00:00:00 2001 From: clD11 <23483715+clD11@users.noreply.github.com> Date: Mon, 17 Oct 2022 17:59:08 +0100 Subject: [PATCH 01/13] feat: address cbr review questions --- kafka/signed_blinded_token_issuer_handler.go | 18 +++- server/cron.go | 9 ++ server/db.go | 80 ++++++++++++++-- server/tokens.go | 97 ++++++++++---------- 4 files changed, 145 insertions(+), 59 deletions(-) diff --git a/kafka/signed_blinded_token_issuer_handler.go b/kafka/signed_blinded_token_issuer_handler.go index ec23794e..b4f171b1 100644 --- a/kafka/signed_blinded_token_issuer_handler.go +++ b/kafka/signed_blinded_token_issuer_handler.go @@ -115,10 +115,11 @@ OUTER: // if the issuer is time aware, we need to approve tokens if issuer.Version == 3 && issuer.Buffer > 0 { - // number of tokens per signing key + // Calculate the number of tokens per signing key. + // Given the mod check this should be a multiple of the total tokens in the request. var numT = len(request.Blinded_tokens) / (issuer.Buffer + issuer.Overlap) - // sign tokens with all the keys in buffer+overlap - for i := issuer.Buffer + issuer.Overlap; i > 0; i-- { + for i := 0; i > len(blindedTokens); i += numT { + var ( blindedTokensSlice []*crypto.BlindedToken signingKey *crypto.SigningKey @@ -130,7 +131,16 @@ OUTER: validFrom = issuer.Keys[len(issuer.Keys)-i].StartAt.Format(time.RFC3339) validTo = issuer.Keys[len(issuer.Keys)-i].EndAt.Format(time.RFC3339) - blindedTokensSlice = blindedTokens[(i - numT):i] + // Calculate the next step size to retrieve. Given previous checks end should never + // be greater than the total number of tokens. + end := i + numT + if end > len(blindedTokens) { + return fmt.Errorf("request %s: error invalid token step length", + blindedTokenRequestSet.Request_id) + } + + // Get the next group of tokens and approve + blindedTokensSlice = blindedTokens[i:end] signedTokens, DLEQProof, err := btd.ApproveTokens(blindedTokensSlice, signingKey) if err != nil { // @TODO: If one token fails they will all fail. Assess this behavior diff --git a/server/cron.go b/server/cron.go index b2c4dd31..1653721a 100644 --- a/server/cron.go +++ b/server/cron.go @@ -27,5 +27,14 @@ func (c *Server) SetupCronTasks() { }); err != nil { panic(err) } + if _, err := cron.AddFunc(cadence, func() { + rows, err := c.deleteIssuerKeys("P1M") + if err != nil { + panic(err) + } + c.Logger.Infof("cron: delete issuers keys removed %d", rows) + }); err != nil { + panic(err) + } cron.Start() } diff --git a/server/db.go b/server/db.go index 6937ebba..0e776f01 100644 --- a/server/db.go +++ b/server/db.go @@ -1,6 +1,7 @@ package server import ( + "context" "database/sql" "errors" "fmt" @@ -118,7 +119,7 @@ type RedemptionV2 struct { TTL int64 `json:"TTL"` } -// CacheInterface cach functions +// CacheInterface cache functions type CacheInterface interface { Get(k string) (interface{}, bool) Delete(k string) @@ -397,6 +398,56 @@ func (c *Server) fetchIssuersByCohort(issuerType string, issuerCohort int16) (*[ return &issuers, nil } +func (c *Server) fetchIssuerByType(ctx context.Context, issuerType string) (*Issuer, error) { + if c.caches != nil { + if cached, found := c.caches["issuer"].Get(issuerType); found { + // TODO: check this + return cached.(*Issuer), nil + } + } + + var issuerV3 issuer + err := c.db.GetContext(ctx, &issuerV3, + `SELECT * + FROM v3_issuers + WHERE issuer_type=$1 + ORDER BY expires_at DESC NULLS LAST, created_at DESC`, issuerType) + if err != nil { + return nil, err + } + + convertedIssuer, err := c.convertDBIssuer(issuerV3) + if err != nil { + return nil, err + } + + if convertedIssuer.Keys == nil { + convertedIssuer.Keys = []IssuerKeys{} + } + + var fetchIssuerKeys []issuerKeys + err = c.db.SelectContext(ctx, &fetchIssuerKeys, `SELECT * FROM v3_issuer_keys where issuer_id=$1 + ORDER BY end_at DESC NULLS LAST, start_at DESC`, issuerV3.ID) + if err != nil { + return nil, err + } + + for _, v := range fetchIssuerKeys { + k, err := c.convertDBIssuerKeys(v) + if err != nil { + c.Logger.Error("Failed to convert issuer keys from DB") + return nil, err + } + convertedIssuer.Keys = append(convertedIssuer.Keys, *k) + } + + if c.caches != nil { + c.caches["issuers"].SetDefault(issuerType, issuerV3) + } + + return convertedIssuer, nil +} + func (c *Server) fetchIssuers(issuerType string) (*[]Issuer, error) { if c.caches != nil { if cached, found := c.caches["issuers"].Get(issuerType); found { @@ -564,14 +615,14 @@ func (c *Server) rotateIssuers() error { err = tx.Commit() }() - fetchedIssuers := []issuer{} + var fetchedIssuers []issuer err = tx.Select( &fetchedIssuers, `SELECT * FROM v3_issuers WHERE expires_at IS NOT NULL AND last_rotated_at < NOW() - $1 * INTERVAL '1 day' AND expires_at < NOW() + $1 * INTERVAL '1 day' - AND version >= 2 + AND version <= 2 FOR UPDATE SKIP LOCKED`, cfg.DefaultDaysBeforeExpiry, ) if err != nil { @@ -619,8 +670,8 @@ func (c *Server) rotateIssuersV3() error { fetchedIssuers := []issuer{} - // we need to get all of the v3 issuers that - // 1. are not expired + // we need to get all the v3 issuers that are + // 1. not expired // 2. now is after valid_from // 3. have max(issuer_v3.end_at) < buffer @@ -669,6 +720,21 @@ func (c *Server) rotateIssuersV3() error { return nil } +// deleteIssuerKeys deletes issuers keys that have ended more than the duration ago. +func (c *Server) deleteIssuerKeys(duration string) (int64, error) { + result, err := c.db.Exec(`delete from v3_issuer_keys where end_at < now() - $1::interval`, duration) + if err != nil { + return 0, fmt.Errorf("error deleting v3 issuer keys: %w", err) + } + + rows, err := result.RowsAffected() + if err != nil { + return 0, fmt.Errorf("error deleting v3 issuer keys row affected: %w", err) + } + + return rows, nil +} + // createIssuer - creation of a v3 issuer func (c *Server) createV3Issuer(issuer Issuer) error { defer incrementCounter(createIssuerCounter) @@ -767,8 +833,8 @@ func txPopulateIssuerKeys(logger *logrus.Logger, tx *sqlx.Tx, issuer Issuer) err var keys []issuerKeys var position = 0 - // for i in buffer, create signing keys for each - for ; i < issuer.Buffer; i++ { + // Create signing keys for buffer and overlap + for ; i < issuer.Buffer+issuer.Overlap; i++ { end := new(time.Time) if duration != nil { // start/end, increment every iteration diff --git a/server/tokens.go b/server/tokens.go index 3ecef1aa..5b1ac91c 100644 --- a/server/tokens.go +++ b/server/tokens.go @@ -1,6 +1,7 @@ package server import ( + "database/sql" "encoding/json" "errors" "net/http" @@ -169,13 +170,40 @@ func (c *Server) blindedTokenIssuerHandler(w http.ResponseWriter, r *http.Reques func (c *Server) blindedTokenRedeemHandlerV3(w http.ResponseWriter, r *http.Request) *handlers.AppError { var response blindedTokenRedeemResponse if issuerType := chi.URLParam(r, "type"); issuerType != "" { - issuers, appErr := c.getIssuers(issuerType) - if appErr != nil { - return appErr + + issuer, err := c.fetchIssuerByType(r.Context(), issuerType) + if err != nil { + switch { + case errors.Is(err, sql.ErrNoRows): + return &handlers.AppError{ + Message: "Issuer not found", + Code: 404, + } + default: + c.Logger.WithError(err) + return &handlers.AppError{ + Cause: errors.New("internal server error"), + Message: "Internal server error could not retrieve issuer", + Code: 500, + } + } } - var request blindedTokenRedeemRequest + if issuer.Version != 3 { + return &handlers.AppError{ + Message: "Issuer must be version 3", + Code: http.StatusBadRequest, + } + } + if issuer.ExpiresAt.IsZero() && issuer.ExpiresAt.Before(time.Now()) { + return &handlers.AppError{ + Message: "Issuer has expired", + Code: http.StatusBadRequest, + } + } + + var request blindedTokenRedeemRequest if err := json.NewDecoder(http.MaxBytesReader(w, r.Body, maxRequestSize)).Decode(&request); err != nil { c.Logger.Debug("Could not parse the request body") return handlers.WrapError(err, "Could not parse the request body", 400) @@ -189,63 +217,36 @@ func (c *Server) blindedTokenRedeemHandlerV3(w http.ResponseWriter, r *http.Requ } } - var verified = false - var verifiedIssuer = &Issuer{} - var verifiedCohort = int16(0) - for _, issuer := range *issuers { - if !issuer.ExpiresAt.IsZero() && issuer.ExpiresAt.Before(time.Now()) { - continue - } - - // validate issuer is a v3 issuer - if issuer.Version != 3 { - return &handlers.AppError{ - Message: "Invalid Issuer", - Code: http.StatusBadRequest, - } - } - - // iterate through the keys until we have one that is valid - var signingKey *crypto.SigningKey - for _, k := range issuer.Keys { - if k.StartAt == nil || k.EndAt == nil { - return &handlers.AppError{ - Message: "Issuer has invalid keys for v3", - Code: http.StatusBadRequest, - } - } - - if k.StartAt.Before(time.Now()) && k.EndAt.After(time.Now()) { - signingKey = k.SigningKey - break - } - } - if signingKey == nil { + var signingKey *crypto.SigningKey + for _, k := range issuer.Keys { + if k.StartAt == nil || k.EndAt == nil { return &handlers.AppError{ - Message: "Issuer has no key that corresponds to start < now < end", + Message: "Issuer has invalid keys for v3", Code: http.StatusBadRequest, } } - if err := btd.VerifyTokenRedemption(request.TokenPreimage, request.Signature, request.Payload, []*crypto.SigningKey{signingKey}); err != nil { - verified = false - } else { - verified = true - verifiedIssuer = &issuer - verifiedCohort = issuer.IssuerCohort + if k.StartAt.Before(time.Now()) && k.EndAt.After(time.Now()) { + signingKey = k.SigningKey break } } + if signingKey == nil { + return &handlers.AppError{ + Message: "Issuer has no key that corresponds to start < now < end", + Code: http.StatusBadRequest, + } + } - if !verified { - c.Logger.Debug("Could not verify that the token redemption is valid") + if err := btd.VerifyTokenRedemption(request.TokenPreimage, request.Signature, request.Payload, + []*crypto.SigningKey{signingKey}); err != nil { return &handlers.AppError{ Message: "Could not verify that token redemption is valid", Code: http.StatusBadRequest, } } - if err := c.RedeemToken(verifiedIssuer, request.TokenPreimage, request.Payload); err != nil { + if err := c.RedeemToken(issuer, request.TokenPreimage, request.Payload); err != nil { if errors.Is(err, errDuplicateRedemption) { return &handlers.AppError{ Message: err.Error(), @@ -259,8 +260,9 @@ func (c *Server) blindedTokenRedeemHandlerV3(w http.ResponseWriter, r *http.Requ } } - response = blindedTokenRedeemResponse{verifiedCohort} + response = blindedTokenRedeemResponse{issuer.IssuerCohort} } + return handlers.RenderContent(r.Context(), response, w, http.StatusOK) } @@ -517,7 +519,6 @@ func (c *Server) tokenRouterV2() chi.Router { return r } -// New end point to generated marked tokens func (c *Server) tokenRouterV3() chi.Router { r := chi.NewRouter() if os.Getenv("ENV") == "production" { From 9901e2fec030ab8b433707e28960dc4644d33322 Mon Sep 17 00:00:00 2001 From: clD11 <23483715+clD11@users.noreply.github.com> Date: Tue, 18 Oct 2022 14:54:02 +0100 Subject: [PATCH 02/13] feat: address cbr review questions updated cache --- server/db.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/db.go b/server/db.go index 0e776f01..1fab0893 100644 --- a/server/db.go +++ b/server/db.go @@ -442,7 +442,7 @@ func (c *Server) fetchIssuerByType(ctx context.Context, issuerType string) (*Iss } if c.caches != nil { - c.caches["issuers"].SetDefault(issuerType, issuerV3) + c.caches["issuer"].SetDefault(issuerType, issuerV3) } return convertedIssuer, nil From 860a943aa77e3288552fed9838ad5552780a3cec Mon Sep 17 00:00:00 2001 From: clD11 <23483715+clD11@users.noreply.github.com> Date: Wed, 19 Oct 2022 19:04:31 +0100 Subject: [PATCH 03/13] feat: address cbr review questions added debug logging --- kafka/signed_blinded_token_issuer_handler.go | 6 ++ server/cron.go | 18 ++--- server/server_test.go | 74 ++++++++++++++++++++ 3 files changed, 89 insertions(+), 9 deletions(-) diff --git a/kafka/signed_blinded_token_issuer_handler.go b/kafka/signed_blinded_token_issuer_handler.go index b4f171b1..1eb9d2d5 100644 --- a/kafka/signed_blinded_token_issuer_handler.go +++ b/kafka/signed_blinded_token_issuer_handler.go @@ -26,6 +26,8 @@ func SignedBlindedTokenIssuerHandler(data []byte, producer *kafka.Writer, server issuerError = 2 ) + log.Debug().Msg("starting blinded token processor") + blindedTokenRequestSet, err := avroSchema.DeserializeSigningRequestSet(bytes.NewReader(data)) if err != nil { return fmt.Errorf("request %s: failed avro deserialization: %w", blindedTokenRequestSet.Request_id, err) @@ -33,6 +35,8 @@ func SignedBlindedTokenIssuerHandler(data []byte, producer *kafka.Writer, server logger := log.With().Str("request_id", blindedTokenRequestSet.Request_id).Logger() + logger.Debug().Msg("processing blinded token request for request_id") + var blindedTokenResults []avroSchema.SigningResultV2 if len(blindedTokenRequestSet.Data) > 1 { // NOTE: When we start supporting multiple requests we will need to review @@ -274,6 +278,8 @@ OUTER: blindedTokenRequestSet.Request_id, resultSetBuffer.String(), err) } + logger.Debug().Msg("ending blinded token request processor loop") + err = Emit(producer, resultSetBuffer.Bytes(), log) if err != nil { return fmt.Errorf("request %s: failed to emit results to topic %s: %w", diff --git a/server/cron.go b/server/cron.go index 1653721a..59009a1b 100644 --- a/server/cron.go +++ b/server/cron.go @@ -27,14 +27,14 @@ func (c *Server) SetupCronTasks() { }); err != nil { panic(err) } - if _, err := cron.AddFunc(cadence, func() { - rows, err := c.deleteIssuerKeys("P1M") - if err != nil { - panic(err) - } - c.Logger.Infof("cron: delete issuers keys removed %d", rows) - }); err != nil { - panic(err) - } + //if _, err := cron.AddFunc(cadence, func() { + // rows, err := c.deleteIssuerKeys("P1M") + // if err != nil { + // panic(err) + // } + // c.Logger.Infof("cron: delete issuers keys removed %d", rows) + //}); err != nil { + // panic(err) + //} cron.Start() } diff --git a/server/server_test.go b/server/server_test.go index 50541165..e0a2e575 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -14,6 +14,8 @@ import ( "testing" "time" + "github.com/brave-intl/challenge-bypass-server/utils/ptr" + "github.com/brave-intl/challenge-bypass-server/utils/test" "github.com/brave-intl/bat-go/libs/middleware" @@ -237,6 +239,78 @@ func (suite *ServerTestSuite) TestNewIssueRedeemV2() { suite.Assert().Equal(http.StatusBadRequest, resp.StatusCode, "Expired Issuers should fail") } +func (suite *ServerTestSuite) TestRedeemV3() { + issuer := Issuer{ + Version: 3, + IssuerType: test.RandomString(), + IssuerCohort: 1, + MaxTokens: 1, + ExpiresAt: time.Now().Add(24 * time.Hour), + Buffer: 1, + Overlap: 1, + Duration: ptr.FromString("PT10S"), + ValidFrom: ptr.FromTime(time.Now()), + } + + err := suite.srv.createV3Issuer(issuer) + suite.Require().NoError(err) + + time.Sleep(30 * time.Second) + + err = suite.srv.rotateIssuersV3() + suite.Require().NoError(err) + + //issuerKey, err := suite.srv.GetLatestIssuer(issuer.IssuerType, issuer.IssuerCohort) + //fmt.Println(err) + //suite.Require().NoError(err) + // + //// sign some tokens + // + //data := blindedTokenRedeemRequest{} + // + //payload, err := json.Marshal(data) + //suite.Require().NoError(err) + // + //server := httptest.NewServer(suite.handler) + //defer server.Close() + // + //url := fmt.Sprintf("%s/v3/blindedToken/%s/redemption/", server.URL, *issuerKey.Keys[0].PublicKey) + // + //response, err := suite.request(http.MethodPost, url, bytes.NewBuffer(payload)) + //suite.Require().NoError(err) + // + //suite.Require().Equal(http.StatusOK, response.Status) +} + +func (suite *ServerTestSuite) TestDeleteIssuerKeysV3() { + issuer := Issuer{ + Version: 3, + IssuerType: test.RandomString(), + IssuerCohort: 1, + MaxTokens: 5, + ExpiresAt: time.Now().Add(24 * time.Hour), + Buffer: 4, + Overlap: 0, + Duration: ptr.FromString("PT1S"), + ValidFrom: ptr.FromTime(time.Now()), + } + + err := suite.srv.createV3Issuer(issuer) + suite.Require().NoError(err) + + time.Sleep(2 * time.Second) + + rows, err := suite.srv.deleteIssuerKeys("PT1S") + suite.Require().NoError(err) + + suite.Assert().Equal(1, rows) +} + +func (suite *ServerTestSuite) TestRunRotate() { + err := suite.srv.rotateIssuersV3() + suite.Require().NoError(err) +} + func (suite *ServerTestSuite) request(method string, URL string, payload io.Reader) (*http.Response, error) { var req *http.Request var err error From 354c8391b4541765da92ac796758ec145ac5fe0e Mon Sep 17 00:00:00 2001 From: husobee Date: Wed, 19 Oct 2022 14:55:57 -0400 Subject: [PATCH 04/13] up to length blinded tokens --- kafka/signed_blinded_token_issuer_handler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kafka/signed_blinded_token_issuer_handler.go b/kafka/signed_blinded_token_issuer_handler.go index 1eb9d2d5..77246bd2 100644 --- a/kafka/signed_blinded_token_issuer_handler.go +++ b/kafka/signed_blinded_token_issuer_handler.go @@ -122,7 +122,7 @@ OUTER: // Calculate the number of tokens per signing key. // Given the mod check this should be a multiple of the total tokens in the request. var numT = len(request.Blinded_tokens) / (issuer.Buffer + issuer.Overlap) - for i := 0; i > len(blindedTokens); i += numT { + for i := 0; i < len(blindedTokens); i += numT { var ( blindedTokensSlice []*crypto.BlindedToken From d14d5033dc9b87627c73ac0fa1846b3b18ee8cd0 Mon Sep 17 00:00:00 2001 From: husobee Date: Wed, 19 Oct 2022 15:45:12 -0400 Subject: [PATCH 05/13] adding debugging and tlv2 bugfix --- kafka/main.go | 6 +++-- kafka/signed_blinded_token_issuer_handler.go | 27 +++++++++++++++++--- main.go | 1 + server/db.go | 4 +++ 4 files changed, 32 insertions(+), 6 deletions(-) diff --git a/kafka/main.go b/kafka/main.go index 44f152be..c1d9e5ee 100644 --- a/kafka/main.go +++ b/kafka/main.go @@ -96,9 +96,11 @@ func StartConsumers(providedServer *server.Server, logger *zerolog.Logger) error failureCount++ continue } - logger.Info().Msgf("Processing message for topic %s at offset %d", msg.Topic, msg.Offset) - logger.Info().Msgf("Reader Stats: %#v", consumer.Stats()) + logger.Debug().Msgf("Processing message for topic %s at offset %d", msg.Topic, msg.Offset) + logger.Debug().Msgf("Reader Stats: %#v", consumer.Stats()) + logger.Debug().Msgf("topicMappings: %+v", topicMappings) for _, topicMapping := range topicMappings { + logger.Debug().Msgf("topic: %+v, topicMapping: %+v", msg.Topic, topicMapping.Topic) if msg.Topic == topicMapping.Topic { go func( msg kafka.Message, diff --git a/kafka/signed_blinded_token_issuer_handler.go b/kafka/signed_blinded_token_issuer_handler.go index 77246bd2..16a37f04 100644 --- a/kafka/signed_blinded_token_issuer_handler.go +++ b/kafka/signed_blinded_token_issuer_handler.go @@ -28,6 +28,8 @@ func SignedBlindedTokenIssuerHandler(data []byte, producer *kafka.Writer, server log.Debug().Msg("starting blinded token processor") + log.Info().Msg("deserialize signing request") + blindedTokenRequestSet, err := avroSchema.DeserializeSigningRequestSet(bytes.NewReader(data)) if err != nil { return fmt.Errorf("request %s: failed avro deserialization: %w", blindedTokenRequestSet.Request_id, err) @@ -48,6 +50,7 @@ func SignedBlindedTokenIssuerHandler(data []byte, producer *kafka.Writer, server OUTER: for _, request := range blindedTokenRequestSet.Data { + logger.Debug().Msgf("processing request: %+v", request) if request.Blinded_tokens == nil { logger.Error().Err(errors.New("blinded tokens is empty")).Msg("") blindedTokenResults = append(blindedTokenResults, avroSchema.SigningResultV2{ @@ -60,6 +63,7 @@ OUTER: } // check to see if issuer cohort will overflow + logger.Debug().Msgf("checking request cohort: %+v", request) if request.Issuer_cohort > math.MaxInt16 || request.Issuer_cohort < math.MinInt16 { logger.Error().Msg("invalid cohort") blindedTokenResults = append(blindedTokenResults, avroSchema.SigningResultV2{ @@ -71,6 +75,7 @@ OUTER: break OUTER } + logger.Debug().Msgf("getting latest issuer: %+v - %+v", request.Issuer_type, request.Issuer_cohort) issuer, appErr := server.GetLatestIssuer(request.Issuer_type, int16(request.Issuer_cohort)) if appErr != nil { logger.Error().Err(appErr).Msg("error retrieving issuer") @@ -83,6 +88,7 @@ OUTER: break OUTER } + logger.Debug().Msgf("checking if issuer is version 3: %+v", issuer) // if this is a time aware issuer, make sure the request contains the appropriate number of blinded tokens if issuer.Version == 3 && issuer.Buffer > 0 { if len(request.Blinded_tokens)%(issuer.Buffer+issuer.Overlap) != 0 { @@ -97,10 +103,12 @@ OUTER: } } + logger.Debug().Msgf("checking blinded tokens: %+v", request.Blinded_tokens) var blindedTokens []*crypto.BlindedToken // Iterate over the provided tokens and create data structure from them, // grouping into a slice for approval for _, stringBlindedToken := range request.Blinded_tokens { + logger.Debug().Msgf("blinded token: %+v", stringBlindedToken) blindedToken := crypto.BlindedToken{} err := blindedToken.UnmarshalText([]byte(stringBlindedToken)) if err != nil { @@ -117,13 +125,17 @@ OUTER: blindedTokens = append(blindedTokens, &blindedToken) } + logger.Debug().Msgf("checking if issuer is time aware: %+v - %+v", issuer.Version, issuer.Buffer) // if the issuer is time aware, we need to approve tokens if issuer.Version == 3 && issuer.Buffer > 0 { // Calculate the number of tokens per signing key. // Given the mod check this should be a multiple of the total tokens in the request. var numT = len(request.Blinded_tokens) / (issuer.Buffer + issuer.Overlap) + count := 0 for i := 0; i < len(blindedTokens); i += numT { + count++ + logger.Debug().Msgf("version 3 issuer: %+v , numT: %+v", issuer, numT) var ( blindedTokensSlice []*crypto.BlindedToken signingKey *crypto.SigningKey @@ -131,9 +143,9 @@ OUTER: validTo string ) - signingKey = issuer.Keys[len(issuer.Keys)-i].SigningKey - validFrom = issuer.Keys[len(issuer.Keys)-i].StartAt.Format(time.RFC3339) - validTo = issuer.Keys[len(issuer.Keys)-i].EndAt.Format(time.RFC3339) + signingKey = issuer.Keys[len(issuer.Keys)-count].SigningKey + validFrom = issuer.Keys[len(issuer.Keys)-count].StartAt.Format(time.RFC3339) + validTo = issuer.Keys[len(issuer.Keys)-count].EndAt.Format(time.RFC3339) // Calculate the next step size to retrieve. Given previous checks end should never // be greater than the total number of tokens. @@ -159,6 +171,8 @@ OUTER: break OUTER } + logger.Debug().Msg("marshalling proof") + marshaledDLEQProof, err := DLEQProof.MarshalText() if err != nil { return fmt.Errorf("request %s: could not marshal dleq proof: %w", blindedTokenRequestSet.Request_id, err) @@ -184,6 +198,7 @@ OUTER: marshaledSignedTokens = append(marshaledSignedTokens, string(marshaledToken[:])) } + logger.Debug().Msg("getting public key") publicKey := signingKey.PublicKey() marshaledPublicKey, err := publicKey.MarshalText() if err != nil { @@ -209,6 +224,7 @@ OUTER: signingKey = issuer.Keys[len(issuer.Keys)-1].SigningKey } + logger.Debug().Msgf("approving tokens: %+v", blindedTokens) // @TODO: If one token fails they will all fail. Assess this behavior signedTokens, DLEQProof, err := btd.ApproveTokens(blindedTokens, signingKey) if err != nil { @@ -270,6 +286,7 @@ OUTER: Request_id: blindedTokenRequestSet.Request_id, Data: blindedTokenResults, } + logger.Debug().Msgf("resultSet: %+v", resultSet) var resultSetBuffer bytes.Buffer err = resultSet.Serialize(&resultSetBuffer) @@ -279,12 +296,14 @@ OUTER: } logger.Debug().Msg("ending blinded token request processor loop") - + logger.Debug().Msgf("about to emit: %+v", resultSet) err = Emit(producer, resultSetBuffer.Bytes(), log) if err != nil { + logger.Error().Msgf("failed to emit: %+v", resultSet) return fmt.Errorf("request %s: failed to emit results to topic %s: %w", blindedTokenRequestSet.Request_id, producer.Topic, err) } + logger.Debug().Msgf("emitted: %+v", resultSet) return nil } diff --git a/main.go b/main.go index 5e6b5256..b609a2de 100644 --- a/main.go +++ b/main.go @@ -25,6 +25,7 @@ func main() { if os.Getenv("ENV") != "production" { zerolog.SetGlobalLevel(zerolog.TraceLevel) } + zerolog.SetGlobalLevel(zerolog.TraceLevel) srv := *server.DefaultServer diff --git a/server/db.go b/server/db.go index 1fab0893..5f8ee9f5 100644 --- a/server/db.go +++ b/server/db.go @@ -797,9 +797,11 @@ func txPopulateIssuerKeys(logger *logrus.Logger, tx *sqlx.Tx, issuer Issuer) err err error ) + logger.Debug("checking if v3") if issuer.Version == 3 { // get the duration from the issuer if issuer.Duration != nil { + logger.Debug("making sure duration is not nil") duration, err = timeutils.ParseDuration(*issuer.Duration) if err != nil { return fmt.Errorf("failed to parse issuer duration: %w", err) @@ -828,6 +830,7 @@ func txPopulateIssuerKeys(logger *logrus.Logger, tx *sqlx.Tx, issuer Issuer) err start = &tmp i = len(issuer.Keys) } + logger.Debug("about to make the issuer keys") valueFmtStr := "" @@ -865,6 +868,7 @@ func txPopulateIssuerKeys(logger *logrus.Logger, tx *sqlx.Tx, issuer Issuer) err tx.Rollback() return err } + logger.Infof("iteration key pubkey: %+v", pubKeyTxt) tmpStart := *start tmpEnd := *end From dcd6fc440e047558ef259d263cdf3b02be64b467 Mon Sep 17 00:00:00 2001 From: husobee Date: Mon, 24 Oct 2022 11:06:05 -0400 Subject: [PATCH 06/13] correct v3 issuer rotation query --- server/db.go | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/server/db.go b/server/db.go index 5f8ee9f5..1200e7cb 100644 --- a/server/db.go +++ b/server/db.go @@ -678,18 +678,16 @@ func (c *Server) rotateIssuersV3() error { err = tx.Select( &fetchedIssuers, ` - select - i.issuer_id, i.issuer_type, i.issuer_cohort, i.max_tokens, i.version, - i.buffer, i.valid_from, i.last_rotated_at, i.expires_at, i.duration, - i.created_at - from - v3_issuers i - join v3_issuer_keys ik on (ik.issuer_id = i.issuer_id) - where - i.version = 3 - and i.expires_at is not null and i.expires_at < now() - and greatest(ik.end_at) < now() + i.buffer * i.duration::interval - for update skip locked + select + i.issuer_id, i.issuer_type, i.issuer_cohort, i.max_tokens, i.version,i.buffer, i.valid_from, i.last_rotated_at, i.expires_at, i.duration,i.created_at + from + v3_issuers i + where + i.version = 3 and + i.expires_at is not null and + i.expires_at < now() + and (select max(end_at) from v3_issuer_keys where issuer_id=i.issuer_id) < now() + i.buffer * i.duration::interval + for update skip locked `, ) if err != nil { From e8bcd8b90d92d050b4cd2477f42c56b463d994cc Mon Sep 17 00:00:00 2001 From: husobee Date: Tue, 25 Oct 2022 10:37:32 -0400 Subject: [PATCH 07/13] fixing assertion type in test --- server/server_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/server_test.go b/server/server_test.go index e0a2e575..0fec6bb8 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -303,7 +303,7 @@ func (suite *ServerTestSuite) TestDeleteIssuerKeysV3() { rows, err := suite.srv.deleteIssuerKeys("PT1S") suite.Require().NoError(err) - suite.Assert().Equal(1, rows) + suite.Assert().Equal(int64(1), rows) } func (suite *ServerTestSuite) TestRunRotate() { From 69078978c6bac1f2dafa0d84213f2b8949f7cca5 Mon Sep 17 00:00:00 2001 From: husobee Date: Tue, 25 Oct 2022 19:39:26 -0400 Subject: [PATCH 08/13] enable removal of old tlv2 issuer keys, fixing test to test redemptions remove debug line --- server/cron.go | 18 ++++++------- server/server_test.go | 59 ++++++++++++++++++++++++++----------------- 2 files changed, 45 insertions(+), 32 deletions(-) diff --git a/server/cron.go b/server/cron.go index 59009a1b..1653721a 100644 --- a/server/cron.go +++ b/server/cron.go @@ -27,14 +27,14 @@ func (c *Server) SetupCronTasks() { }); err != nil { panic(err) } - //if _, err := cron.AddFunc(cadence, func() { - // rows, err := c.deleteIssuerKeys("P1M") - // if err != nil { - // panic(err) - // } - // c.Logger.Infof("cron: delete issuers keys removed %d", rows) - //}); err != nil { - // panic(err) - //} + if _, err := cron.AddFunc(cadence, func() { + rows, err := c.deleteIssuerKeys("P1M") + if err != nil { + panic(err) + } + c.Logger.Infof("cron: delete issuers keys removed %d", rows) + }); err != nil { + panic(err) + } cron.Start() } diff --git a/server/server_test.go b/server/server_test.go index 0fec6bb8..ac1b7441 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -14,6 +14,7 @@ import ( "testing" "time" + "github.com/brave-intl/challenge-bypass-server/btd" "github.com/brave-intl/challenge-bypass-server/utils/ptr" "github.com/brave-intl/challenge-bypass-server/utils/test" @@ -240,9 +241,10 @@ func (suite *ServerTestSuite) TestNewIssueRedeemV2() { } func (suite *ServerTestSuite) TestRedeemV3() { + var issuerType = test.RandomString() issuer := Issuer{ Version: 3, - IssuerType: test.RandomString(), + IssuerType: issuerType, IssuerCohort: 1, MaxTokens: 1, ExpiresAt: time.Now().Add(24 * time.Hour), @@ -255,31 +257,42 @@ func (suite *ServerTestSuite) TestRedeemV3() { err := suite.srv.createV3Issuer(issuer) suite.Require().NoError(err) - time.Sleep(30 * time.Second) - err = suite.srv.rotateIssuersV3() suite.Require().NoError(err) - //issuerKey, err := suite.srv.GetLatestIssuer(issuer.IssuerType, issuer.IssuerCohort) - //fmt.Println(err) - //suite.Require().NoError(err) - // - //// sign some tokens - // - //data := blindedTokenRedeemRequest{} - // - //payload, err := json.Marshal(data) - //suite.Require().NoError(err) - // - //server := httptest.NewServer(suite.handler) - //defer server.Close() - // - //url := fmt.Sprintf("%s/v3/blindedToken/%s/redemption/", server.URL, *issuerKey.Keys[0].PublicKey) - // - //response, err := suite.request(http.MethodPost, url, bytes.NewBuffer(payload)) - //suite.Require().NoError(err) - // - //suite.Require().Equal(http.StatusOK, response.Status) + issuerKey, err := suite.srv.GetLatestIssuer(issuer.IssuerType, issuer.IssuerCohort) + + tokens := make([]*crypto.Token, 1) + token, err := crypto.RandomToken() + suite.Require().NoError(err, "Must be able to generate random token") + + blindedToken := token.Blind() + suite.Require().NoError(err, "Must be able to blind token") + + tokens[0] = token + + var blindedTokensSlice = []*crypto.BlindedToken{ + blindedToken, + } + + // sign some tokens + signedTokens, DLEQProof, err := btd.ApproveTokens(blindedTokensSlice, issuerKey.Keys[0].SigningKey) + + unblindedTokens, err := DLEQProof.VerifyAndUnblind(tokens, blindedTokensSlice, signedTokens, issuerKey.Keys[0].SigningKey.PublicKey()) + + msg := "test message" + preimageText, sigText := suite.prepareRedemption(unblindedTokens[0], msg) + + server := httptest.NewServer(suite.handler) + defer server.Close() + + payload := fmt.Sprintf(`{"t":"%s", "signature":"%s", "payload":"%s"}`, preimageText, sigText, msg) + redeemURL := fmt.Sprintf("%s/v3/blindedToken/%s/redemption/", server.URL, issuerType) + + response, err := suite.request(http.MethodPost, redeemURL, bytes.NewBuffer([]byte(payload))) + suite.Require().NoError(err) + + suite.Require().Equal(http.StatusOK, response.Status) } func (suite *ServerTestSuite) TestDeleteIssuerKeysV3() { From b161de8ca3eba52eb942d3692ced337c0d25a2a3 Mon Sep 17 00:00:00 2001 From: clD11 <23483715+clD11@users.noreply.github.com> Date: Wed, 26 Oct 2022 12:25:44 +0100 Subject: [PATCH 09/13] feat: address cbr review questions added integration tests --- server/server_test.go | 77 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/server/server_test.go b/server/server_test.go index ac1b7441..00f2fc9c 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -295,6 +295,83 @@ func (suite *ServerTestSuite) TestRedeemV3() { suite.Require().Equal(http.StatusOK, response.Status) } +func (suite *ServerTestSuite) TestCreateIssuerV3() { + server := httptest.NewServer(suite.handler) + defer server.Close() + + ctx := context.Background() + + request := issuerV3CreateRequest{ + Name: test.RandomString(), + Cohort: 3, + MaxTokens: 10, + ValidFrom: ptr.FromTime(time.Now()), + Duration: "P1M", + Buffer: 10, + Overlap: 2, + } + + payload, err := json.Marshal(request) + suite.Require().NoError(err) + + createIssuerURL := fmt.Sprintf("%s/v3/issuer/", server.URL) + resp, err := suite.request("POST", createIssuerURL, bytes.NewBuffer(payload)) + + suite.Assert().Equal(http.StatusCreated, resp.StatusCode) + + actualIssuer, err := suite.srv.fetchIssuerByType(ctx, request.Name) + suite.Require().NoError(err) + + suite.Assert().Equal(request.Name, actualIssuer.IssuerType) + suite.Assert().Equal(request.Cohort, actualIssuer.IssuerCohort) + suite.Assert().Equal(request.MaxTokens, actualIssuer.MaxTokens) + suite.Assert().Equal(request.MaxTokens, actualIssuer.MaxTokens) + suite.Assert().WithinDuration(*request.ValidFrom, *actualIssuer.ValidFrom, 100*time.Millisecond) + suite.Assert().Equal(request.Duration, *actualIssuer.Duration) + suite.Assert().Equal(request.Buffer, actualIssuer.Buffer) + suite.Assert().Equal(request.Overlap, actualIssuer.Overlap) +} + +func (suite *ServerTestSuite) TestGetIssuerV2() { + server := httptest.NewServer(suite.handler) + defer server.Close() + + var issuerType = test.RandomString() + issuer := Issuer{ + Version: 3, + IssuerType: issuerType, + IssuerCohort: 1, + MaxTokens: 1, + ExpiresAt: time.Now().Add(24 * time.Hour), + Buffer: 1, + Overlap: 1, + Duration: ptr.FromString("PT10S"), + ValidFrom: ptr.FromTime(time.Now()), + } + + err := suite.srv.createV3Issuer(issuer) + suite.Require().NoError(err) + + request := issuerFetchRequestV2{ + issuer.IssuerCohort, + } + + payload, err := json.Marshal(request) + suite.Require().NoError(err) + + url := fmt.Sprintf("%s/v2/issuer/%s", server.URL, issuer.IssuerType) + resp, err := suite.request(http.MethodGet, url, bytes.NewBuffer(payload)) + suite.Require().NoError(err) + + suite.Require().Equal(http.StatusOK, resp.StatusCode) + + var actual issuerResponse + err = json.NewDecoder(resp.Body).Decode(&actual) + suite.Require().NoError(err) + + suite.Assert().Equal(issuer.IssuerType, actual.Name) +} + func (suite *ServerTestSuite) TestDeleteIssuerKeysV3() { issuer := Issuer{ Version: 3, From 95653d566a47d2c64d694f6d46888be5da9fc7d6 Mon Sep 17 00:00:00 2001 From: clD11 <23483715+clD11@users.noreply.github.com> Date: Wed, 26 Oct 2022 12:47:48 +0100 Subject: [PATCH 10/13] feat: address cbr review questions added logging --- server/tokens.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/server/tokens.go b/server/tokens.go index 5b1ac91c..7f0bcd37 100644 --- a/server/tokens.go +++ b/server/tokens.go @@ -180,7 +180,7 @@ func (c *Server) blindedTokenRedeemHandlerV3(w http.ResponseWriter, r *http.Requ Code: 404, } default: - c.Logger.WithError(err) + c.Logger.WithError(err).Error("error fetching issuer") return &handlers.AppError{ Cause: errors.New("internal server error"), Message: "Internal server error could not retrieve issuer", @@ -189,6 +189,9 @@ func (c *Server) blindedTokenRedeemHandlerV3(w http.ResponseWriter, r *http.Requ } } + c.Logger.WithField("issuer", issuer). + Debug("retrieved issuer") + if issuer.Version != 3 { return &handlers.AppError{ Message: "Issuer must be version 3", @@ -240,6 +243,7 @@ func (c *Server) blindedTokenRedeemHandlerV3(w http.ResponseWriter, r *http.Requ if err := btd.VerifyTokenRedemption(request.TokenPreimage, request.Signature, request.Payload, []*crypto.SigningKey{signingKey}); err != nil { + c.Logger.WithError(err).Error("error verifying token") return &handlers.AppError{ Message: "Could not verify that token redemption is valid", Code: http.StatusBadRequest, @@ -247,6 +251,7 @@ func (c *Server) blindedTokenRedeemHandlerV3(w http.ResponseWriter, r *http.Requ } if err := c.RedeemToken(issuer, request.TokenPreimage, request.Payload); err != nil { + c.Logger.WithError(err).Error("error redeeming token") if errors.Is(err, errDuplicateRedemption) { return &handlers.AppError{ Message: err.Error(), From 6d10ce67241cb6c7392e8027c3b9cf13cfc52f48 Mon Sep 17 00:00:00 2001 From: husobee Date: Wed, 26 Oct 2022 08:38:32 -0400 Subject: [PATCH 11/13] fixing test to use appropriate key for signing to test redemption v3 --- server/server_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/server_test.go b/server/server_test.go index ac1b7441..439fb053 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -276,9 +276,9 @@ func (suite *ServerTestSuite) TestRedeemV3() { } // sign some tokens - signedTokens, DLEQProof, err := btd.ApproveTokens(blindedTokensSlice, issuerKey.Keys[0].SigningKey) + signedTokens, DLEQProof, err := btd.ApproveTokens(blindedTokensSlice, issuerKey.Keys[1].SigningKey) - unblindedTokens, err := DLEQProof.VerifyAndUnblind(tokens, blindedTokensSlice, signedTokens, issuerKey.Keys[0].SigningKey.PublicKey()) + unblindedTokens, err := DLEQProof.VerifyAndUnblind(tokens, blindedTokensSlice, signedTokens, issuerKey.Keys[1].SigningKey.PublicKey()) msg := "test message" preimageText, sigText := suite.prepareRedemption(unblindedTokens[0], msg) @@ -292,7 +292,7 @@ func (suite *ServerTestSuite) TestRedeemV3() { response, err := suite.request(http.MethodPost, redeemURL, bytes.NewBuffer([]byte(payload))) suite.Require().NoError(err) - suite.Require().Equal(http.StatusOK, response.Status) + suite.Require().Equal(http.StatusOK, response.StatusCode) } func (suite *ServerTestSuite) TestDeleteIssuerKeysV3() { From 7f3ae91c02f1c28f10c7fe66530f7c862bfee79b Mon Sep 17 00:00:00 2001 From: husobee Date: Wed, 26 Oct 2022 08:48:01 -0400 Subject: [PATCH 12/13] passify codescanning --- server/tokens.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/tokens.go b/server/tokens.go index 7f0bcd37..10b99b41 100644 --- a/server/tokens.go +++ b/server/tokens.go @@ -243,7 +243,7 @@ func (c *Server) blindedTokenRedeemHandlerV3(w http.ResponseWriter, r *http.Requ if err := btd.VerifyTokenRedemption(request.TokenPreimage, request.Signature, request.Payload, []*crypto.SigningKey{signingKey}); err != nil { - c.Logger.WithError(err).Error("error verifying token") + c.Logger.Error("error verifying token") return &handlers.AppError{ Message: "Could not verify that token redemption is valid", Code: http.StatusBadRequest, @@ -251,7 +251,7 @@ func (c *Server) blindedTokenRedeemHandlerV3(w http.ResponseWriter, r *http.Requ } if err := c.RedeemToken(issuer, request.TokenPreimage, request.Payload); err != nil { - c.Logger.WithError(err).Error("error redeeming token") + c.Logger.Error("error redeeming token") if errors.Is(err, errDuplicateRedemption) { return &handlers.AppError{ Message: err.Error(), From 111fb321527d10c2e11b29e05e8890bfbb56565b Mon Sep 17 00:00:00 2001 From: husobee Date: Wed, 26 Oct 2022 12:52:29 -0400 Subject: [PATCH 13/13] explicitly only consider v3 issuers for key expiry removal --- server/db.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/db.go b/server/db.go index 1200e7cb..3a983220 100644 --- a/server/db.go +++ b/server/db.go @@ -718,9 +718,9 @@ func (c *Server) rotateIssuersV3() error { return nil } -// deleteIssuerKeys deletes issuers keys that have ended more than the duration ago. +// deleteIssuerKeys deletes v3 issuers keys that have ended more than the duration ago. func (c *Server) deleteIssuerKeys(duration string) (int64, error) { - result, err := c.db.Exec(`delete from v3_issuer_keys where end_at < now() - $1::interval`, duration) + result, err := c.db.Exec(`delete from v3_issuer_keys where issuer_id in (select issuer_id from v3_issuers where version = 3) and end_at < now() - $1::interval`, duration) if err != nil { return 0, fmt.Errorf("error deleting v3 issuer keys: %w", err) }