Skip to content

Commit

Permalink
Merge branch 'master' into test-debug-data
Browse files Browse the repository at this point in the history
  • Loading branch information
james-d-elliott authored Apr 29, 2024
2 parents 83c02f6 + c673c3d commit 97d1124
Show file tree
Hide file tree
Showing 40 changed files with 607 additions and 393 deletions.
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/bug-report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ body:
description: What version of the library are you using or which versions do you see the issue in?
multiple: true
options:
- 0.10.1
- 0.10.0
- 0.9.4
- 0.9.3
Expand Down
9 changes: 6 additions & 3 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
name: Go
on: [push, pull_request]
on:
pull_request: {}
push:
branches:
- master
jobs:
build:
name: Build
runs-on: ubuntu-latest
strategy:
matrix:
go:
- '1.18'
- '1.19'
- '1.20'
- '1.21'
- '1.22'
fail-fast: false
steps:
- name: Set up Go ${{ matrix.go }}
Expand Down
6 changes: 2 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,15 @@ minor versions) with a brief transition time (usually 1 patch release of go, for
will likely still support go 1.17 until go 1.21.1 is released).

This library in our opinion handles a critical element of security in a dependent project and we aim to avoid backwards
compatability at the cost of security wherever possible. We also consider this especially important in a language like
compatibility at the cost of security wherever possible. We also consider this especially important in a language like
go where their backwards compatibility when upgrading the compile tools is usually flawless.

This policy means that users who wish to build this with older versions of go may find there are features being used
which are not available in that version. The current intentionally supported versions of go are as follows:

- go 1.22
- go 1.21
- go 1.20
- go 1.19
- go 1.18

## Status

Expand Down Expand Up @@ -69,7 +68,6 @@ package example

import (
"fmt"
"time"

"github.com/go-webauthn/webauthn/webauthn"
)
Expand Down
14 changes: 7 additions & 7 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,20 @@ module github.com/go-webauthn/webauthn
go 1.21

require (
github.com/fxamacker/cbor/v2 v2.5.0
github.com/go-webauthn/x v0.1.6
github.com/golang-jwt/jwt/v5 v5.2.0
github.com/fxamacker/cbor/v2 v2.6.0
github.com/go-webauthn/x v0.1.9
github.com/golang-jwt/jwt/v5 v5.2.1
github.com/google/go-tpm v0.9.0
github.com/google/uuid v1.5.0
github.com/google/uuid v1.6.0
github.com/mitchellh/mapstructure v1.5.0
github.com/stretchr/testify v1.8.4
golang.org/x/crypto v0.17.0
github.com/stretchr/testify v1.9.0
golang.org/x/crypto v0.22.0
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/x448/float16 v0.8.4 // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/sys v0.19.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
28 changes: 14 additions & 14 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fxamacker/cbor/v2 v2.5.0 h1:oHsG0V/Q6E/wqTS2O1Cozzsy69nqCiguo5Q1a1ADivE=
github.com/fxamacker/cbor/v2 v2.5.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
github.com/go-webauthn/x v0.1.6 h1:QNAX+AWeqRt9loE8mULeWJCqhVG5D/jvdmJ47fIWCkQ=
github.com/go-webauthn/x v0.1.6/go.mod h1:W8dFVZ79o4f+nY1eOUICy/uq5dhrRl7mxQkYhXTo0FA=
github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw=
github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/fxamacker/cbor/v2 v2.6.0 h1:sU6J2usfADwWlYDAFhZBQ6TnLFBHxgesMrQfQgk1tWA=
github.com/fxamacker/cbor/v2 v2.6.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/go-webauthn/x v0.1.9 h1:v1oeLmoaa+gPOaZqUdDentu6Rl7HkSSsmOT6gxEQHhE=
github.com/go-webauthn/x v0.1.9/go.mod h1:pJNMlIMP1SU7cN8HNlKJpLEnFHCygLCvaLZ8a1xeoQA=
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/google/go-tpm v0.9.0 h1:sQF6YqWMi+SCXpsmS3fd21oPy/vSddwZry4JnmltHVk=
github.com/google/go-tpm v0.9.0/go.mod h1:FkNVkc6C+IsvDI9Jw1OveJmxGZUUaKxtrpOS47QWKfU=
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
Expand Down
10 changes: 5 additions & 5 deletions metadata/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -548,16 +548,16 @@ type MDSGetEndpointsResponse struct {
func unmarshalMDSBLOB(body []byte, c http.Client) (MetadataBLOBPayload, error) {
var payload MetadataBLOBPayload

token, err := jwt.Parse(string(body), func(token *jwt.Token) (interface{}, error) {
token, err := jwt.Parse(string(body), func(token *jwt.Token) (any, error) {
// 2. If the x5u attribute is present in the JWT Header, then
if _, ok := token.Header["x5u"].([]interface{}); ok {
if _, ok := token.Header["x5u"].(any); ok {
// never seen an x5u here, although it is in the spec
return nil, errors.New("x5u encountered in header of metadata TOC payload")
}
var chain []interface{}
var chain []any
// 3. If the x5u attribute is missing, the chain should be retrieved from the x5c attribute.

if x5c, ok := token.Header["x5c"].([]interface{}); !ok {
if x5c, ok := token.Header["x5c"].([]any); !ok {
// If that attribute is missing as well, Metadata TOC signing trust anchor is considered the TOC signing certificate chain.
chain[0] = MDSRoot
} else {
Expand Down Expand Up @@ -600,7 +600,7 @@ func unmarshalMDSBLOB(body []byte, c http.Client) (MetadataBLOBPayload, error) {
return payload, err
}

func validateChain(chain []interface{}, c http.Client) (bool, error) {
func validateChain(chain []any, c http.Client) (bool, error) {
oRoot := make([]byte, base64.StdEncoding.DecodedLen(len(MDSRoot)))

nRoot, err := base64.StdEncoding.Decode(oRoot, []byte(MDSRoot))
Expand Down
9 changes: 6 additions & 3 deletions protocol/assertion.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@ import (
// credential for login/assertion.
type CredentialAssertionResponse struct {
PublicKeyCredential

AssertionResponse AuthenticatorAssertionResponse `json:"response"`
}

// The ParsedCredentialAssertionData is the parsed CredentialAssertionResponse that has been marshalled into a format
// that allows us to verify the client and authenticator data inside the response.
type ParsedCredentialAssertionData struct {
ParsedPublicKeyCredential

Response ParsedAssertionResponse
Raw CredentialAssertionResponse
}
Expand All @@ -32,6 +34,7 @@ type ParsedCredentialAssertionData struct {
// ParsedAssertionResponse.
type AuthenticatorAssertionResponse struct {
AuthenticatorResponse

AuthenticatorData URLEncodedBase64 `json:"authenticatorData"`
Signature URLEncodedBase64 `json:"signature"`
UserHandle URLEncodedBase64 `json:"userHandle,omitempty"`
Expand Down Expand Up @@ -138,14 +141,14 @@ func (car CredentialAssertionResponse) Parse() (par *ParsedCredentialAssertionDa
// documentation.
//
// Specification: §7.2 Verifying an Authentication Assertion (https://www.w3.org/TR/webauthn/#sctn-verifying-assertion)
func (p *ParsedCredentialAssertionData) Verify(storedChallenge string, relyingPartyID string, relyingPartyOrigins []string, appID string, verifyUser bool, credentialBytes []byte) error {
func (p *ParsedCredentialAssertionData) Verify(storedChallenge string, relyingPartyID string, rpOrigins, rpTopOrigins []string, rpTopOriginsVerify TopOriginVerificationMode, appID string, verifyUser bool, credentialBytes []byte) 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."
// We handle these steps in part as we verify but also beforehand

// 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, relyingPartyOrigins)
validError := p.Response.CollectedClientData.Verify(storedChallenge, AssertCeremony, rpOrigins, rpTopOrigins, rpTopOriginsVerify)
if validError != nil {
return validError
}
Expand Down Expand Up @@ -175,7 +178,7 @@ func (p *ParsedCredentialAssertionData) Verify(storedChallenge string, relyingPa
sigData := append(p.Raw.AssertionResponse.AuthenticatorData, clientDataHash[:]...)

var (
key interface{}
key any
err error
)

Expand Down
8 changes: 4 additions & 4 deletions protocol/assertion_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func TestParseCredentialRequestResponse(t *testing.T) {
Type: "public-key",
},
RawID: byteID,
ClientExtensionResults: map[string]interface{}{
ClientExtensionResults: map[string]any{
"appID": "example.com",
},
},
Expand Down Expand Up @@ -78,7 +78,7 @@ func TestParseCredentialRequestResponse(t *testing.T) {
ID: "AI7D5q2P0LS-Fal9ZT7CHM2N5BLbUunF92T8b6iYC199bO2kagSuU05-5dZGqb1SP0A0lyTWng",
},
RawID: byteID,
ClientExtensionResults: map[string]interface{}{
ClientExtensionResults: map[string]any{
"appID": "example.com",
},
},
Expand Down Expand Up @@ -135,7 +135,7 @@ func TestParseCredentialRequestResponse(t *testing.T) {
assert.Equal(t, tc.expected.Response.CollectedClientData, actual.Response.CollectedClientData)

var (
pkExpected, pkActual interface{}
pkExpected, pkActual any
)

assert.NoError(t, webauthncbor.Unmarshal(tc.expected.Response.AuthenticatorData.AttData.CredentialPublicKey, &pkExpected))
Expand Down Expand Up @@ -180,7 +180,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, nil, TopOriginIgnoreVerificationMode, "", tt.args.verifyUser, tt.args.credentialBytes); (err != nil) != tt.wantErr {
t.Errorf("ParsedCredentialAssertionData.Verify() error = %v, wantErr %v", err, tt.wantErr)
}
})
Expand Down
22 changes: 14 additions & 8 deletions protocol/attestation.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ type AuthenticatorAttestationResponse struct {
// The byte slice of clientDataJSON, which becomes CollectedClientData
AuthenticatorResponse

Transports []string `json:"transports,omitempty"`

AuthenticatorData URLEncodedBase64 `json:"authenticatorData"`

PublicKey URLEncodedBase64 `json:"publicKey"`

PublicKeyAlgorithm int64 `json:"publicKeyAlgorithm"`

// AttestationObject is the byte slice version of attestationObject.
// This attribute contains an attestation object, which is opaque to, and
// cryptographically protected against tampering by, the client. The
Expand All @@ -33,8 +41,6 @@ type AuthenticatorAttestationResponse struct {
// requires to validate the attestation statement, as well as to decode and
// validate the authenticator data along with the JSON-serialized client data.
AttestationObject URLEncodedBase64 `json:"attestationObject"`

Transports []string `json:"transports,omitempty"`
}

// ParsedAttestationResponse is the parsed version of AuthenticatorAttestationResponse.
Expand Down Expand Up @@ -65,16 +71,16 @@ type AttestationObject struct {
// The format of the Attestation data.
Format string `json:"fmt"`
// The attestation statement data sent back if attestation is requested.
AttStatement map[string]interface{} `json:"attStmt,omitempty"`
AttStatement map[string]any `json:"attStmt,omitempty"`
}

type attestationFormatValidationHandler func(AttestationObject, []byte) (string, []interface{}, error)
type attestationFormatValidationHandler func(AttestationObject, []byte) (string, []any, error)

var attestationRegistry = make(map[string]attestationFormatValidationHandler)
var attestationRegistry = make(map[AttestationFormat]attestationFormatValidationHandler)

// RegisterAttestationFormat is a method to register attestation formats with the library. Generally using one of the
// locally registered attestation formats is sufficient.
func RegisterAttestationFormat(format string, handler attestationFormatValidationHandler) {
func RegisterAttestationFormat(format AttestationFormat, handler attestationFormatValidationHandler) {
attestationRegistry[format] = handler
}

Expand Down Expand Up @@ -135,15 +141,15 @@ func (attestationObject *AttestationObject) Verify(relyingPartyID string, client

// But first let's make sure attestation is present. If it isn't, we don't need to handle
// any of the following steps
if attestationObject.Format == "none" {
if AttestationFormat(attestationObject.Format) == AttestationFormatNone {
if len(attestationObject.AttStatement) != 0 {
return ErrAttestationFormat.WithInfo("Attestation format none with attestation present")
}

return nil
}

formatHandler, valid := attestationRegistry[attestationObject.Format]
formatHandler, valid := attestationRegistry[AttestationFormat(attestationObject.Format)]
if !valid {
return ErrAttestationFormat.WithInfo(fmt.Sprintf("Attestation format %s is unsupported", attestationObject.Format))
}
Expand Down
24 changes: 11 additions & 13 deletions protocol/attestation_androidkey.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,8 @@ import (
"github.com/go-webauthn/webauthn/protocol/webauthncose"
)

var androidAttestationKey = "android-key"

func init() {
RegisterAttestationFormat(androidAttestationKey, verifyAndroidKeyFormat)
RegisterAttestationFormat(AttestationFormatAndroidKey, verifyAndroidKeyFormat)
}

// The android-key attestation statement looks like:
Expand All @@ -31,7 +29,7 @@ func init() {
// }
//
// Specification: §8.4. Android Key Attestation Statement Format (https://www.w3.org/TR/webauthn/#sctn-android-key-attestation)
func verifyAndroidKeyFormat(att AttestationObject, clientDataHash []byte) (string, []interface{}, error) {
func verifyAndroidKeyFormat(att AttestationObject, clientDataHash []byte) (string, []any, error) {
// Given the verification procedure inputs attStmt, authenticatorData and clientDataHash, the verification procedure is as follows:
// §8.4.1. Verify that attStmt is valid CBOR conforming to the syntax defined above and perform CBOR decoding on it to extract
// the contained fields.
Expand All @@ -50,7 +48,7 @@ func verifyAndroidKeyFormat(att AttestationObject, clientDataHash []byte) (strin
}

// If x5c is not present, return an error
x5c, x509present := att.AttStatement["x5c"].([]interface{})
x5c, x509present := att.AttStatement["x5c"].([]any)
if !x509present {
// Handle Basic Attestation steps for the x509 Certificate
return "", nil, ErrAttestationFormat.WithDetails("Error retrieving x5c value")
Expand Down Expand Up @@ -165,19 +163,19 @@ type authorizationList struct {
Padding []int `asn1:"tag:6,explicit,set,optional"`
EcCurve int `asn1:"tag:10,explicit,optional"`
RsaPublicExponent int `asn1:"tag:200,explicit,optional"`
RollbackResistance interface{} `asn1:"tag:303,explicit,optional"`
RollbackResistance any `asn1:"tag:303,explicit,optional"`
ActiveDateTime int `asn1:"tag:400,explicit,optional"`
OriginationExpireDateTime int `asn1:"tag:401,explicit,optional"`
UsageExpireDateTime int `asn1:"tag:402,explicit,optional"`
NoAuthRequired interface{} `asn1:"tag:503,explicit,optional"`
NoAuthRequired any `asn1:"tag:503,explicit,optional"`
UserAuthType int `asn1:"tag:504,explicit,optional"`
AuthTimeout int `asn1:"tag:505,explicit,optional"`
AllowWhileOnBody interface{} `asn1:"tag:506,explicit,optional"`
TrustedUserPresenceRequired interface{} `asn1:"tag:507,explicit,optional"`
TrustedConfirmationRequired interface{} `asn1:"tag:508,explicit,optional"`
UnlockedDeviceRequired interface{} `asn1:"tag:509,explicit,optional"`
AllApplications interface{} `asn1:"tag:600,explicit,optional"`
ApplicationID interface{} `asn1:"tag:601,explicit,optional"`
AllowWhileOnBody any `asn1:"tag:506,explicit,optional"`
TrustedUserPresenceRequired any `asn1:"tag:507,explicit,optional"`
TrustedConfirmationRequired any `asn1:"tag:508,explicit,optional"`
UnlockedDeviceRequired any `asn1:"tag:509,explicit,optional"`
AllApplications any `asn1:"tag:600,explicit,optional"`
ApplicationID any `asn1:"tag:601,explicit,optional"`
CreationDateTime int `asn1:"tag:701,explicit,optional"`
Origin int `asn1:"tag:702,explicit,optional"`
RootOfTrust rootOfTrust `asn1:"tag:704,explicit,optional"`
Expand Down
2 changes: 1 addition & 1 deletion protocol/attestation_androidkey_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func TestVerifyAndroidKeyFormat(t *testing.T) {
name string
args args
want string
want1 []interface{}
want1 []any
wantErr bool
}{
{
Expand Down
Loading

0 comments on commit 97d1124

Please sign in to comment.