verify exp

This commit is contained in:
AJ ONeal 2020-08-06 16:54:26 +00:00
parent ca84b8dbca
commit 05db67c8b7
3 changed files with 62 additions and 9 deletions

View File

@ -7,11 +7,12 @@ import (
"log" "log"
"net/http" "net/http"
"strings" "strings"
"time"
"git.coolaj86.com/coolaj86/go-mockid/xkeypairs" "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) { func Verify(w http.ResponseWriter, r *http.Request) {
if "POST" != r.Method { if "POST" != r.Method {
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed) http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
@ -55,7 +56,7 @@ func Verify(w http.ResponseWriter, r *http.Request) {
return return
} }
if err := json.Unmarshal([]byte(protected), &jws.Header); nil != err { 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) http.Error(w, "Bad Request: invalid JWS header", http.StatusBadRequest)
return return
} }
@ -66,11 +67,16 @@ func Verify(w http.ResponseWriter, r *http.Request) {
return return
} }
if err := json.Unmarshal([]byte(payload), &jws.Claims); nil != err { 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) http.Error(w, "Bad Request: invalid JWS claims", http.StatusBadRequest)
return 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) ok, err := xkeypairs.VerifyClaims(nil, jws)
if nil != err { if nil != err {
log.Printf("jws verify error: %s", err) log.Printf("jws verify error: %s", err)

View File

@ -64,11 +64,39 @@ func TestMain(m *testing.M) {
//func TestSelfSignWithoutExp(t *testing.T) //func TestSelfSignWithoutExp(t *testing.T)
//func TestSelfSignWithJTIWithoutExp(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" jwt := "eyJfc2VlZCI6LTEzMDY3NDU1MDQxNDQsImFsZyI6IlJTMjU2IiwiandrIjp7ImUiOiJBUUFCIiwia2lkIjoiSEZ4ZTlGV1dVc2N3bjltaVozSXNJeWMwMjMtbEJ1UmtvOEJpVV9IRG9KOCIsImt0eSI6IlJTQSIsIm4iOiJ2NUZkSTdYaC0wekxWVEVQZl94ekdIUVpDcEZ2MWR2N2h3eHhrVjctYmxpYmt6LXIxUG9lZ3lQYzFXMjZlWFBvd0xQQXQ3a3dHQnVOdjdMVjh5MEtvMkxOZklaXzRILW54SkJPaWIybXlHOVVfQ29WRDBiM3NBWTdmcDd2QlV1bTBXYVM4R3hZOGtYU0ZOS0VTY0NDNVBpSmFyblNISk1PcUdIVm51YmpsSjl5c1NyNmNsaGpxc0R4dU9qOHpxamF2MUFxek1STWVpRl9CREJsOUFoUGNZSHpHN0JtaXB5UEo2XzBwdWNLTi0tUDZDRk92d05SVGx2ek41RmlRM3VHcy1fMHcwQzVMZWJ6N21BNmJNTFdXc0tRRFBvb3cxallCWHJKdVF1WkZoSmxLMmdidm9ZcV85dWhfLUM1Z3pPZnR4UHBCNnhtY3RfelVaeUdwUUxnQlEiLCJ1c2UiOiJzaWcifSwidHlwIjoiSldUIn0.eyJleHAiOjE1OTY2MTQ3NTYsInN1YiI6ImJhbmFuYXMifQ.qHpzlglOfZMzE3CTNAUXld_wC62JTAJuoQfMaNeFa-XPtYB2Maj8_w3YmRZg_q5S6y9ToCmZ8nWd1kuMheA5qBKOUQeQH47Jts5zWLd0UBckIHo5lK4mk0bUWuiNgr7c9DY6k1DIdFaavyWCXbhFwG0X83qlMhQlPh02dDpCuU78Nn2hF3mZETQKpBIVESYtfeU1Xy3OU_am0kwcN2klLcdweOcrLx_ONfcvAGY3KiIdFiz0ViySAsQ39BiSSvoDYqOOOi41Hky67bnyZQOdalQC_95McTeXApzmGXRUE74Gj-S8c9e5it5d4QZLPaQ1JHzUKz1s7TPvThIn58NA-g"
client := srv.Client() client := srv.Client()
urlstr, _ := url.Parse(srv.URL + "/verify") 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{ req := &http.Request{
Method: "POST", Method: "POST",
URL: urlstr, URL: urlstr,

View File

@ -13,20 +13,27 @@ import (
"log" "log"
"math/big" "math/big"
mathrand "math/rand" mathrand "math/rand"
"time"
"git.rootprojects.org/root/keypairs" "git.rootprojects.org/root/keypairs"
) )
func VerifyClaims(pubkey keypairs.PublicKey, jws *JWS) (bool, error) { func VerifyClaims(pubkey keypairs.PublicKey, jws *JWS) (bool, error) {
seed, _ := jws.Header["_seed"].(int64) seed, _ := jws.Header["_seed"].(int64)
seedf64, _ := jws.Header["_seed"].(float64)
kty, _ := jws.Header["_kty"].(string) kty, _ := jws.Header["_kty"].(string)
kid, _ := jws.Header["kid"].(string) kid, _ := jws.Header["kid"].(string)
jwkmap, hasJWK := jws.Header["jwk"].(Object) jwkmap, hasJWK := jws.Header["jwk"].(Object)
//var jwk JWK = nil //var jwk JWK = nil
if 0 == seed {
seed = int64(seedf64)
}
var pub keypairs.PublicKey = nil var pub keypairs.PublicKey = nil
if hasJWK { if hasJWK {
fmt.Println("Security TODO: did not check jws.Claims[\"sub\"] against 'jwk' thumbprint") log.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[\"iss\"]")
kty := jwkmap["kty"] kty := jwkmap["kty"]
var err error var err error
if "RSA" == kty { if "RSA" == kty {
@ -70,7 +77,20 @@ func VerifyClaims(pubkey keypairs.PublicKey, jws *JWS) (bool, error) {
} else { } else {
pub = pubkey 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) 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]) r.SetBytes(sig[0:32])
s := &big.Int{} s := &big.Int{}
s.SetBytes(sig[32:]) 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) return ecdsa.Verify(pub, hash, r, s)
default: default:
panic("impossible condition: non-rsa/non-ecdsa key") panic("impossible condition: non-rsa/non-ecdsa key")
@ -134,6 +152,7 @@ func genPrivKey(seed int64, kty string) keypairs.PrivateKey {
} }
if maxRetry == i-1 { if maxRetry == i-1 {
log.Printf("error: coinflip landed on heads %d times", maxRetry) log.Printf("error: coinflip landed on heads %d times", maxRetry)
// TODO return random / retry error
} }
} }
} }