From 05db67c8b79fee4a2b76ed054a2df873c1818b5d Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Thu, 6 Aug 2020 16:54:26 +0000 Subject: [PATCH] verify exp --- mockid/api/verify.go | 12 +++++++++--- mockid/mockid_test.go | 30 +++++++++++++++++++++++++++++- xkeypairs/verify.go | 29 ++++++++++++++++++++++++----- 3 files changed, 62 insertions(+), 9 deletions(-) diff --git a/mockid/api/verify.go b/mockid/api/verify.go index 1f89e4b..95223c5 100644 --- a/mockid/api/verify.go +++ b/mockid/api/verify.go @@ -7,11 +7,12 @@ import ( "log" "net/http" "strings" + "time" "git.coolaj86.com/coolaj86/go-mockid/xkeypairs" ) -// VerifyJWT will verify both JWT and uncompressed JWS +// Verify will verify both JWT and uncompressed JWS func Verify(w http.ResponseWriter, r *http.Request) { if "POST" != r.Method { http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed) @@ -55,7 +56,7 @@ func Verify(w http.ResponseWriter, r *http.Request) { return } if err := json.Unmarshal([]byte(protected), &jws.Header); nil != err { - log.Printf("json decode error: %s", err) + log.Printf("json decode header error: %s", err) http.Error(w, "Bad Request: invalid JWS header", http.StatusBadRequest) return } @@ -66,11 +67,16 @@ func Verify(w http.ResponseWriter, r *http.Request) { return } if err := json.Unmarshal([]byte(payload), &jws.Claims); nil != err { - log.Printf("json decode error: %s", err) + log.Printf("json decode claims error: %s", err) http.Error(w, "Bad Request: invalid JWS claims", http.StatusBadRequest) return } + if "false" == r.URL.Query().Get("exp") { + //expf64, _ := jws.Claims["exp"].(float64) + jws.Claims["exp"] = float64(time.Now().Add(5 * time.Minute).Unix()) + } + ok, err := xkeypairs.VerifyClaims(nil, jws) if nil != err { log.Printf("jws verify error: %s", err) diff --git a/mockid/mockid_test.go b/mockid/mockid_test.go index 57f188d..480e344 100644 --- a/mockid/mockid_test.go +++ b/mockid/mockid_test.go @@ -64,11 +64,39 @@ func TestMain(m *testing.M) { //func TestSelfSignWithoutExp(t *testing.T) //func TestSelfSignWithJTIWithoutExp(t *testing.T) -func TestVerifySelfSignedJWT(t *testing.T) { +func TestVerifyExpired(t *testing.T) { jwt := "eyJfc2VlZCI6LTEzMDY3NDU1MDQxNDQsImFsZyI6IlJTMjU2IiwiandrIjp7ImUiOiJBUUFCIiwia2lkIjoiSEZ4ZTlGV1dVc2N3bjltaVozSXNJeWMwMjMtbEJ1UmtvOEJpVV9IRG9KOCIsImt0eSI6IlJTQSIsIm4iOiJ2NUZkSTdYaC0wekxWVEVQZl94ekdIUVpDcEZ2MWR2N2h3eHhrVjctYmxpYmt6LXIxUG9lZ3lQYzFXMjZlWFBvd0xQQXQ3a3dHQnVOdjdMVjh5MEtvMkxOZklaXzRILW54SkJPaWIybXlHOVVfQ29WRDBiM3NBWTdmcDd2QlV1bTBXYVM4R3hZOGtYU0ZOS0VTY0NDNVBpSmFyblNISk1PcUdIVm51YmpsSjl5c1NyNmNsaGpxc0R4dU9qOHpxamF2MUFxek1STWVpRl9CREJsOUFoUGNZSHpHN0JtaXB5UEo2XzBwdWNLTi0tUDZDRk92d05SVGx2ek41RmlRM3VHcy1fMHcwQzVMZWJ6N21BNmJNTFdXc0tRRFBvb3cxallCWHJKdVF1WkZoSmxLMmdidm9ZcV85dWhfLUM1Z3pPZnR4UHBCNnhtY3RfelVaeUdwUUxnQlEiLCJ1c2UiOiJzaWcifSwidHlwIjoiSldUIn0.eyJleHAiOjE1OTY2MTQ3NTYsInN1YiI6ImJhbmFuYXMifQ.qHpzlglOfZMzE3CTNAUXld_wC62JTAJuoQfMaNeFa-XPtYB2Maj8_w3YmRZg_q5S6y9ToCmZ8nWd1kuMheA5qBKOUQeQH47Jts5zWLd0UBckIHo5lK4mk0bUWuiNgr7c9DY6k1DIdFaavyWCXbhFwG0X83qlMhQlPh02dDpCuU78Nn2hF3mZETQKpBIVESYtfeU1Xy3OU_am0kwcN2klLcdweOcrLx_ONfcvAGY3KiIdFiz0ViySAsQ39BiSSvoDYqOOOi41Hky67bnyZQOdalQC_95McTeXApzmGXRUE74Gj-S8c9e5it5d4QZLPaQ1JHzUKz1s7TPvThIn58NA-g" client := srv.Client() urlstr, _ := url.Parse(srv.URL + "/verify") + req := &http.Request{ + Method: "POST", + URL: urlstr, + Header: http.Header{}, + } + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", jwt)) + res, err := client.Do(req) + if nil != err { + t.Error(err) + return + } + data, err := ioutil.ReadAll(res.Body) + if nil != err { + t.Error(err) + return + } + if 200 == res.StatusCode { + log.Printf(string(data)) + t.Error(fmt.Errorf("did not expect successful status code: %d", res.StatusCode)) + return + } +} + +func TestVerifySelfSignedJWT(t *testing.T) { + jwt := "eyJfc2VlZCI6LTEzMDY3NDU1MDQxNDQsImFsZyI6IlJTMjU2IiwiandrIjp7ImUiOiJBUUFCIiwia2lkIjoiSEZ4ZTlGV1dVc2N3bjltaVozSXNJeWMwMjMtbEJ1UmtvOEJpVV9IRG9KOCIsImt0eSI6IlJTQSIsIm4iOiJ2NUZkSTdYaC0wekxWVEVQZl94ekdIUVpDcEZ2MWR2N2h3eHhrVjctYmxpYmt6LXIxUG9lZ3lQYzFXMjZlWFBvd0xQQXQ3a3dHQnVOdjdMVjh5MEtvMkxOZklaXzRILW54SkJPaWIybXlHOVVfQ29WRDBiM3NBWTdmcDd2QlV1bTBXYVM4R3hZOGtYU0ZOS0VTY0NDNVBpSmFyblNISk1PcUdIVm51YmpsSjl5c1NyNmNsaGpxc0R4dU9qOHpxamF2MUFxek1STWVpRl9CREJsOUFoUGNZSHpHN0JtaXB5UEo2XzBwdWNLTi0tUDZDRk92d05SVGx2ek41RmlRM3VHcy1fMHcwQzVMZWJ6N21BNmJNTFdXc0tRRFBvb3cxallCWHJKdVF1WkZoSmxLMmdidm9ZcV85dWhfLUM1Z3pPZnR4UHBCNnhtY3RfelVaeUdwUUxnQlEiLCJ1c2UiOiJzaWcifSwidHlwIjoiSldUIn0.eyJleHAiOjE1OTY2MTQ3NTYsInN1YiI6ImJhbmFuYXMifQ.qHpzlglOfZMzE3CTNAUXld_wC62JTAJuoQfMaNeFa-XPtYB2Maj8_w3YmRZg_q5S6y9ToCmZ8nWd1kuMheA5qBKOUQeQH47Jts5zWLd0UBckIHo5lK4mk0bUWuiNgr7c9DY6k1DIdFaavyWCXbhFwG0X83qlMhQlPh02dDpCuU78Nn2hF3mZETQKpBIVESYtfeU1Xy3OU_am0kwcN2klLcdweOcrLx_ONfcvAGY3KiIdFiz0ViySAsQ39BiSSvoDYqOOOi41Hky67bnyZQOdalQC_95McTeXApzmGXRUE74Gj-S8c9e5it5d4QZLPaQ1JHzUKz1s7TPvThIn58NA-g" + client := srv.Client() + urlstr, _ := url.Parse(srv.URL + "/verify?exp=false") + req := &http.Request{ Method: "POST", URL: urlstr, diff --git a/xkeypairs/verify.go b/xkeypairs/verify.go index 94babfd..5eec7a2 100644 --- a/xkeypairs/verify.go +++ b/xkeypairs/verify.go @@ -13,20 +13,27 @@ import ( "log" "math/big" mathrand "math/rand" + "time" "git.rootprojects.org/root/keypairs" ) func VerifyClaims(pubkey keypairs.PublicKey, jws *JWS) (bool, error) { seed, _ := jws.Header["_seed"].(int64) + seedf64, _ := jws.Header["_seed"].(float64) kty, _ := jws.Header["_kty"].(string) kid, _ := jws.Header["kid"].(string) jwkmap, hasJWK := jws.Header["jwk"].(Object) //var jwk JWK = nil + + if 0 == seed { + seed = int64(seedf64) + } + var pub keypairs.PublicKey = nil if hasJWK { - fmt.Println("Security TODO: did not check jws.Claims[\"sub\"] against 'jwk' thumbprint") - fmt.Println("Security TODO: did not check jws.Claims[\"iss\"]") + log.Println("Security TODO: did not check jws.Claims[\"sub\"] against 'jwk' thumbprint") + log.Println("Security TODO: did not check jws.Claims[\"iss\"]") kty := jwkmap["kty"] var err error if "RSA" == kty { @@ -70,7 +77,20 @@ func VerifyClaims(pubkey keypairs.PublicKey, jws *JWS) (bool, error) { } else { pub = pubkey } - fmt.Println("Security TODO: did not check jws.Claims[\"kid\"] against thumbprint") + log.Println("Security TODO: did not check jws.Claims[\"kid\"] against thumbprint") + } + + jti, _ := jws.Claims["jti"].(string) + expf64, _ := jws.Claims["exp"].(float64) + exp := int64(expf64) + if 0 == exp { + if "" == jti { + return false, errors.New("one of 'jti' or 'exp' must exist for token expiry") + } + } else { + if time.Now().Unix() > exp { + return false, fmt.Errorf("token expired at %d (%s)", exp, time.Unix(exp, 0)) + } } signable := fmt.Sprintf("%s.%s", jws.Protected, jws.Payload) @@ -103,8 +123,6 @@ func Verify(pubkey keypairs.PublicKey, hash []byte, sig []byte) bool { r.SetBytes(sig[0:32]) s := &big.Int{} s.SetBytes(sig[32:]) - fmt.Println("debug: sig len:", len(sig)) - fmt.Println("debug: r, s:", r, s) return ecdsa.Verify(pub, hash, r, s) default: panic("impossible condition: non-rsa/non-ecdsa key") @@ -134,6 +152,7 @@ func genPrivKey(seed int64, kty string) keypairs.PrivateKey { } if maxRetry == i-1 { log.Printf("error: coinflip landed on heads %d times", maxRetry) + // TODO return random / retry error } } }