Change `kid` and `dpop` validation

pull/1672/head
Herman Slatman 5 months ago
parent 3f37feae78
commit 9bb1b24bf1
No known key found for this signature in database
GPG Key ID: F4D8A44EA0A75A4F

@ -352,9 +352,9 @@ func dns01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWebK
type wireOidcPayload struct { type wireOidcPayload struct {
// IDToken contains the OIDC identity token // IDToken contains the OIDC identity token
IDToken string `json:"id_token,omitempty"` IDToken string `json:"id_token"`
// KeyAuth ({challenge-token}.{jwk-thumbprint}) // KeyAuth ({challenge-token}.{jwk-thumbprint})
KeyAuth string `json:"keyauth,omitempty"` KeyAuth string `json:"keyauth"`
} }
func wireOIDC01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWebKey, payload []byte) error { func wireOIDC01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWebKey, payload []byte) error {
@ -448,7 +448,7 @@ func wireOIDC01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSO
type wireDpopPayload struct { type wireDpopPayload struct {
// AccessToken is the token generated by wire-server // AccessToken is the token generated by wire-server
AccessToken string `json:"access_token,omitempty"` AccessToken string `json:"access_token"`
} }
func wireDPOP01Validate(ctx context.Context, ch *Challenge, db DB, accountJWK *jose.JSONWebKey, payload []byte) error { func wireDPOP01Validate(ctx context.Context, ch *Challenge, db DB, accountJWK *jose.JSONWebKey, payload []byte) error {
@ -484,13 +484,14 @@ func wireDPOP01Validate(ctx context.Context, ch *Challenge, db DB, accountJWK *j
return WrapErrorISE(err, "invalid Go template registered for 'target'") return WrapErrorISE(err, "invalid Go template registered for 'target'")
} }
params := verifyParams{ params := wireVerifyParams{
token: dpopPayload.AccessToken, token: dpopPayload.AccessToken,
tokenKey: dpopOptions.GetSigningKey(), tokenKey: dpopOptions.GetSigningKey(),
dpopKey: accountJWK, dpopKey: accountJWK.Public(),
dpopKeyID: accountJWK.KeyID,
issuer: issuer, issuer: issuer,
wireID: wireID, wireID: wireID,
challenge: ch, chToken: ch.Token,
t: clock.Now().UTC(), t: clock.Now().UTC(),
} }
_, dpop, err := parseAndVerifyWireAccessToken(params) _, dpop, err := parseAndVerifyWireAccessToken(params)
@ -523,33 +524,34 @@ func wireDPOP01Validate(ctx context.Context, ch *Challenge, db DB, accountJWK *j
return nil return nil
} }
type cnf struct { type wireCnf struct {
Kid string `json:"kid,omitempty"` Kid string `json:"kid"`
} }
type wireAccessToken struct { type wireAccessToken struct {
jose.Claims jose.Claims
Challenge string `json:"chal,omitempty"` Challenge string `json:"chal"`
Cnf *cnf `json:"cnf,omitempty"` Cnf wireCnf `json:"cnf"`
Proof string `json:"proof,omitempty"` Proof string `json:"proof"`
ClientID string `json:"client_id,omitempty"` ClientID string `json:"client_id"`
APIVersion int `json:"api_version,omitempty"` APIVersion int `json:"api_version"`
Scope string `json:"scope,omitempty"` Scope string `json:"scope"`
} }
type wireDpopToken map[string]any type wireDpopToken map[string]any
type verifyParams struct { type wireVerifyParams struct {
token string token string
tokenKey crypto.PublicKey tokenKey crypto.PublicKey
dpopKey *jose.JSONWebKey dpopKey crypto.PublicKey
dpopKeyID string
issuer string issuer string
wireID wire.ID wireID wire.ID
challenge *Challenge chToken string
t time.Time t time.Time
} }
func parseAndVerifyWireAccessToken(v verifyParams) (*wireAccessToken, *wireDpopToken, error) { func parseAndVerifyWireAccessToken(v wireVerifyParams) (*wireAccessToken, *wireDpopToken, error) {
jwt, err := jose.ParseSigned(v.token) jwt, err := jose.ParseSigned(v.token)
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("failed parsing token: %w", err) return nil, nil, fmt.Errorf("failed parsing token: %w", err)
@ -567,17 +569,8 @@ func parseAndVerifyWireAccessToken(v verifyParams) (*wireAccessToken, *wireDpopT
return nil, nil, fmt.Errorf("failed validation: %w", err) return nil, nil, fmt.Errorf("failed validation: %w", err)
} }
rawKid, err := v.dpopKey.Thumbprint(crypto.SHA256) if accessToken.Cnf.Kid != v.dpopKeyID {
if err != nil { return nil, nil, fmt.Errorf("expected kid %q; got %q", v.dpopKeyID, accessToken.Cnf.Kid)
return nil, nil, fmt.Errorf("failed to compute JWK thumbprint")
}
kid := base64.RawURLEncoding.EncodeToString(rawKid)
if accessToken.Cnf == nil {
return nil, nil, errors.New("'cnf' is nil")
}
if accessToken.Cnf.Kid != kid {
return nil, nil, fmt.Errorf("expected kid %q; got %q", kid, accessToken.Cnf.Kid)
} }
if accessToken.ClientID != v.wireID.ClientID { if accessToken.ClientID != v.wireID.ClientID {
return nil, nil, fmt.Errorf("invalid Wire client ID %q", accessToken.ClientID) return nil, nil, fmt.Errorf("invalid Wire client ID %q", accessToken.ClientID)
@ -591,7 +584,7 @@ func parseAndVerifyWireAccessToken(v verifyParams) (*wireAccessToken, *wireDpopT
return nil, nil, fmt.Errorf("invalid Wire DPoP token: %w", err) return nil, nil, fmt.Errorf("invalid Wire DPoP token: %w", err)
} }
var dpopToken wireDpopToken var dpopToken wireDpopToken
if err := dpopJWT.Claims(v.dpopKey.Key, &dpopToken); err != nil { if err := dpopJWT.Claims(v.dpopKey, &dpopToken); err != nil {
return nil, nil, fmt.Errorf("failed validating Wire DPoP token claims: %w", err) return nil, nil, fmt.Errorf("failed validating Wire DPoP token claims: %w", err)
} }
@ -599,7 +592,7 @@ func parseAndVerifyWireAccessToken(v verifyParams) (*wireAccessToken, *wireDpopT
if !ok { if !ok {
return nil, nil, fmt.Errorf("invalid challenge in Wire DPoP token") return nil, nil, fmt.Errorf("invalid challenge in Wire DPoP token")
} }
if challenge != v.challenge.Token { if challenge != v.chToken {
return nil, nil, fmt.Errorf("invalid Wire DPoP challenge %q", challenge) return nil, nil, fmt.Errorf("invalid Wire DPoP challenge %q", challenge)
} }

@ -4332,13 +4332,18 @@ MCowBQYDK2VwAyEAB2IYqBWXAouDt3WcCZgCM3t9gumMEKMlgMsGenSu+fA=
var accountJWK jose.JSONWebKey var accountJWK jose.JSONWebKey
json.Unmarshal(jwkBytes, &accountJWK) json.Unmarshal(jwkBytes, &accountJWK)
at, dpop, err := parseAndVerifyWireAccessToken(verifyParams{ rawKid, err := accountJWK.Thumbprint(crypto.SHA256)
require.NoError(t, err)
accountJWK.KeyID = base64.RawURLEncoding.EncodeToString(rawKid)
at, dpop, err := parseAndVerifyWireAccessToken(wireVerifyParams{
token: token, token: token,
tokenKey: publicKey, tokenKey: publicKey,
dpopKey: &accountJWK, dpopKey: accountJWK.Public(),
dpopKeyID: accountJWK.KeyID,
issuer: issuer, issuer: issuer,
wireID: wireID, wireID: wireID,
challenge: ch, chToken: ch.Token,
t: issuedAt.Add(1 * time.Minute), // set validation time to be one minute after issuance t: issuedAt.Add(1 * time.Minute), // set validation time to be one minute after issuance
}) })
if assert.NoError(t, err) { if assert.NoError(t, err) {

Loading…
Cancel
Save