diff --git a/protocol/assertion.go b/protocol/assertion.go index f93b5c78..1e6501de 100644 --- a/protocol/assertion.go +++ b/protocol/assertion.go @@ -100,7 +100,7 @@ func ParseCredentialRequestResponseBody(body io.Reader) (*ParsedCredentialAssert // Follow the remaining steps outlined in §7.2 Verifying an authentication assertion // (https://www.w3.org/TR/webauthn/#verifying-assertion) and return an error if there // is a failure during each step. -func (p *ParsedCredentialAssertionData) Verify(storedChallenge string, relyingPartyID, relyingPartyOrigin, appID string, verifyUser bool, credentialBytes []byte) error { +func (p *ParsedCredentialAssertionData) Verify(storedChallenge string, relyingPartyID, relyingPartyOrigin, appID string, verifyUser bool, credentialBytes []byte, challengeOffset uint) error { // Steps 4 through 6 in verifying the assertion data (https://www.w3.org/TR/webauthn/#verifying-assertion) are // "assertive" steps, i.e "Let JSONtext be the result of running UTF-8 decode on the value of cData." @@ -108,7 +108,7 @@ func (p *ParsedCredentialAssertionData) Verify(storedChallenge string, relyingPa // Handle steps 7 through 10 of assertion by verifying stored data against the Collected Client Data // returned by the authenticator - validError := p.Response.CollectedClientData.Verify(storedChallenge, AssertCeremony, relyingPartyOrigin) + validError := p.Response.CollectedClientData.Verify(storedChallenge, AssertCeremony, relyingPartyOrigin, challengeOffset) if validError != nil { return validError } diff --git a/protocol/assertion_test.go b/protocol/assertion_test.go index 8ef2f1a7..1c5ce6cc 100644 --- a/protocol/assertion_test.go +++ b/protocol/assertion_test.go @@ -173,7 +173,7 @@ func TestParsedCredentialAssertionData_Verify(t *testing.T) { Raw: tt.fields.Raw, } - if err := p.Verify(tt.args.storedChallenge.String(), tt.args.relyingPartyID, tt.args.relyingPartyOrigin, "", tt.args.verifyUser, tt.args.credentialBytes); (err != nil) != tt.wantErr { + if err := p.Verify(tt.args.storedChallenge.String(), tt.args.relyingPartyID, tt.args.relyingPartyOrigin, "", tt.args.verifyUser, tt.args.credentialBytes, 0); (err != nil) != tt.wantErr { t.Errorf("ParsedCredentialAssertionData.Verify() error = %v, wantErr %v", err, tt.wantErr) } }) diff --git a/protocol/attestation_test.go b/protocol/attestation_test.go index 85dd7eb0..3af13bf6 100644 --- a/protocol/attestation_test.go +++ b/protocol/attestation_test.go @@ -30,7 +30,7 @@ func TestAttestationVerify(t *testing.T) { pcc.Response = *parsedAttestationResponse // Test Base Verification - err = pcc.Verify(options.Response.Challenge.String(), false, options.Response.RelyingParty.ID, options.Response.RelyingParty.Name) + err = pcc.Verify(options.Response.Challenge.String(), false, options.Response.RelyingParty.ID, options.Response.RelyingParty.Name, 0) if err != nil { t.Fatalf("Not valid: %+v (%s)", err, err.(*Error).DevInfo) } diff --git a/protocol/client.go b/protocol/client.go index 0fcbe23c..7b1d5389 100644 --- a/protocol/client.go +++ b/protocol/client.go @@ -59,7 +59,7 @@ func FullyQualifiedOrigin(u *url.URL) string { // new credential and steps 7 through 10 of verifying an authentication assertion // See https://www.w3.org/TR/webauthn/#registering-a-new-credential // and https://www.w3.org/TR/webauthn/#verifying-assertion -func (c *CollectedClientData) Verify(storedChallenge string, ceremony CeremonyType, relyingPartyOrigin string) error { +func (c *CollectedClientData) Verify(storedChallenge string, ceremony CeremonyType, relyingPartyOrigin string, challengeOffset uint) error { // Registration Step 3. Verify that the value of C.type is webauthn.create. @@ -76,7 +76,7 @@ func (c *CollectedClientData) Verify(storedChallenge string, ceremony CeremonyTy // passed to the get() call. challenge := c.Challenge - if subtle.ConstantTimeCompare([]byte(storedChallenge), []byte(challenge)) != 1 { + if subtle.ConstantTimeCompare([]byte(storedChallenge)[challengeOffset:], []byte(challenge)[challengeOffset:]) != 1 { err := ErrVerification.WithDetails("Error validating challenge") return err.WithInfo(fmt.Sprintf("Expected b Value: %#v\nReceived b: %#v\n", storedChallenge, challenge)) } diff --git a/protocol/client_test.go b/protocol/client_test.go index 6e770158..4b0b4630 100644 --- a/protocol/client_test.go +++ b/protocol/client_test.go @@ -26,7 +26,7 @@ func TestVerifyCollectedClientData(t *testing.T) { var storedChallenge = newChallenge originURL, _ := url.Parse(ccd.Origin) - err = ccd.Verify(storedChallenge.String(), ccd.Type, FullyQualifiedOrigin(originURL)) + err = ccd.Verify(storedChallenge.String(), ccd.Type, FullyQualifiedOrigin(originURL), 0) if err != nil { t.Fatalf("error verifying challenge: expected %#v got %#v", Challenge(ccd.Challenge), storedChallenge) } @@ -43,7 +43,7 @@ func TestVerifyCollectedClientDataIncorrectChallenge(t *testing.T) { t.Fatalf("error creating challenge: %s", err) } storedChallenge := Challenge(bogusChallenge) - err = ccd.Verify(storedChallenge.String(), ccd.Type, ccd.Origin) + err = ccd.Verify(storedChallenge.String(), ccd.Type, ccd.Origin, 0) if err == nil { t.Fatalf("error expected but not received. expected %#v got %#v", Challenge(ccd.Challenge), storedChallenge) } diff --git a/protocol/credential.go b/protocol/credential.go index d39c3894..ccfdba73 100644 --- a/protocol/credential.go +++ b/protocol/credential.go @@ -106,10 +106,10 @@ func ParseCredentialCreationResponseBody(body io.Reader) (*ParsedCredentialCreat // Verifies the Client and Attestation data as laid out by §7.1. Registering a new credential // https://www.w3.org/TR/webauthn/#registering-a-new-credential -func (pcc *ParsedCredentialCreationData) Verify(storedChallenge string, verifyUser bool, relyingPartyID, relyingPartyOrigin string) error { +func (pcc *ParsedCredentialCreationData) Verify(storedChallenge string, verifyUser bool, relyingPartyID, relyingPartyOrigin string, challengeOffset uint) error { // Handles steps 3 through 6 - Verifying the Client Data against the Relying Party's stored data - verifyError := pcc.Response.CollectedClientData.Verify(storedChallenge, CreateCeremony, relyingPartyOrigin) + verifyError := pcc.Response.CollectedClientData.Verify(storedChallenge, CreateCeremony, relyingPartyOrigin, challengeOffset) if verifyError != nil { return verifyError } diff --git a/protocol/credential_test.go b/protocol/credential_test.go index a7bd26ee..e4e7fa33 100644 --- a/protocol/credential_test.go +++ b/protocol/credential_test.go @@ -236,7 +236,7 @@ func TestParsedCredentialCreationData_Verify(t *testing.T) { Response: tt.fields.Response, Raw: tt.fields.Raw, } - if err := pcc.Verify(tt.args.storedChallenge.String(), tt.args.verifyUser, tt.args.relyingPartyID, tt.args.relyingPartyOrigin); (err != nil) != tt.wantErr { + if err := pcc.Verify(tt.args.storedChallenge.String(), tt.args.verifyUser, tt.args.relyingPartyID, tt.args.relyingPartyOrigin, 0); (err != nil) != tt.wantErr { t.Errorf("ParsedCredentialCreationData.Verify() error = %+v, wantErr %v", err, tt.wantErr) } }) diff --git a/webauthn/login.go b/webauthn/login.go index f23fa79f..18efde69 100644 --- a/webauthn/login.go +++ b/webauthn/login.go @@ -224,7 +224,7 @@ func (webauthn *WebAuthn) validateLogin(user User, session SessionData, parsedRe } // Handle steps 4 through 16 - validError := parsedResponse.Verify(session.Challenge[challengeOffset:], rpID, rpOrigin, appID, shouldVerifyUser, loginCredential.PublicKey) + validError := parsedResponse.Verify(session.Challenge, rpID, rpOrigin, appID, shouldVerifyUser, loginCredential.PublicKey, challengeOffset) if validError != nil { return nil, validError } diff --git a/webauthn/registration.go b/webauthn/registration.go index 8acb82f8..25607766 100644 --- a/webauthn/registration.go +++ b/webauthn/registration.go @@ -152,7 +152,7 @@ func (webauthn *WebAuthn) CreateCredential(user User, session SessionData, parse shouldVerifyUser := session.UserVerification == protocol.VerificationRequired - invalidErr := parsedResponse.Verify(session.Challenge, shouldVerifyUser, webauthn.Config.RPID, webauthn.Config.RPOrigin) + invalidErr := parsedResponse.Verify(session.Challenge, shouldVerifyUser, webauthn.Config.RPID, webauthn.Config.RPOrigin, 0) if invalidErr != nil { return nil, invalidErr }