Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: webauthn level 3 #232

Merged
merged 1 commit into from
Apr 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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) {
james-d-elliott marked this conversation as resolved.
Show resolved Hide resolved
oRoot := make([]byte, base64.StdEncoding.DecodedLen(len(MDSRoot)))

nRoot, err := base64.StdEncoding.Decode(oRoot, []byte(MDSRoot))
Expand Down
2 changes: 1 addition & 1 deletion protocol/assertion.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ func (p *ParsedCredentialAssertionData) Verify(storedChallenge string, relyingPa
sigData := append(p.Raw.AssertionResponse.AuthenticatorData, clientDataHash[:]...)

var (
key interface{}
key any
err error
)

Expand Down
6 changes: 3 additions & 3 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
4 changes: 2 additions & 2 deletions protocol/attestation.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,10 @@ 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[AttestationFormat]attestationFormatValidationHandler)

Expand Down
20 changes: 10 additions & 10 deletions protocol/attestation_androidkey.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,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 @@ -48,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 @@ -163,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
4 changes: 2 additions & 2 deletions protocol/attestation_apple.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@ func init() {
// }
//
// Specification: §8.8. Apple Anonymous Attestation Statement Format (https://www.w3.org/TR/webauthn/#sctn-apple-anonymous-attestation)
func verifyAppleFormat(att AttestationObject, clientDataHash []byte) (string, []interface{}, error) {
func verifyAppleFormat(att AttestationObject, clientDataHash []byte) (string, []any, error) {
// Step 1. Verify that attStmt is valid CBOR conforming to the syntax defined
// above and perform CBOR decoding on it to extract the contained fields.

// 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
6 changes: 2 additions & 4 deletions protocol/attestation_apple_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func Test_verifyAppleFormat(t *testing.T) {
name string
args args
want string
want1 []interface{}
want1 []any
wantErr bool
}{
{
Expand All @@ -42,12 +42,10 @@ func Test_verifyAppleFormat(t *testing.T) {
t.Errorf("verifyAppleFormat() error = %v, wantErr %v", err, tt.wantErr)
return
}

if got != tt.want {
t.Errorf("verifyAppleFormat() got = %v, want %v", got, tt.want)
}
//if !reflect.DeepEqual(got1, tt.want1) {
// t.Errorf("verifyPackedFormat() got1 = %v, want %v", got1, tt.want1)
//}
})
}
}
Expand Down
10 changes: 5 additions & 5 deletions protocol/attestation_packed.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func init() {
// }
//
// Specification: §8.2. Packed Attestation Statement Format (https://www.w3.org/TR/webauthn/#sctn-packed-attestation)
func verifyPackedFormat(att AttestationObject, clientDataHash []byte) (string, []interface{}, error) {
func verifyPackedFormat(att AttestationObject, clientDataHash []byte) (string, []any, error) {
// Step 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 @@ -53,7 +53,7 @@ func verifyPackedFormat(att AttestationObject, clientDataHash []byte) (string, [
}

// Step 2. If x5c is present, this indicates that the attestation type is not ECDAA.
x5c, x509present := att.AttStatement["x5c"].([]interface{})
x5c, x509present := att.AttStatement["x5c"].([]any)
if x509present {
// Handle Basic Attestation steps for the x509 Certificate
return handleBasicAttestation(sig, clientDataHash, att.RawAuthData, att.AuthData.AttData.AAGUID, alg, x5c)
Expand All @@ -72,7 +72,7 @@ func verifyPackedFormat(att AttestationObject, clientDataHash []byte) (string, [
}

// Handle the attestation steps laid out in
func handleBasicAttestation(signature, clientDataHash, authData, aaguid []byte, alg int64, x5c []interface{}) (string, []interface{}, error) {
func handleBasicAttestation(signature, clientDataHash, authData, aaguid []byte, alg int64, x5c []any) (string, []any, error) {
// Step 2.1. Verify that sig is a valid signature over the concatenation of authenticatorData
// and clientDataHash using the attestation public key in attestnCert with the algorithm specified in alg.
for _, c := range x5c {
Expand Down Expand Up @@ -199,11 +199,11 @@ func handleBasicAttestation(signature, clientDataHash, authData, aaguid []byte,
return string(metadata.BasicFull), x5c, nil
}

func handleECDAAAttestation(signature, clientDataHash, ecdaaKeyID []byte) (string, []interface{}, error) {
func handleECDAAAttestation(signature, clientDataHash, ecdaaKeyID []byte) (string, []any, error) {
return "Packed (ECDAA)", nil, ErrNotSpecImplemented
}

func handleSelfAttestation(alg int64, pubKey, authData, clientDataHash, signature []byte) (string, []interface{}, error) {
func handleSelfAttestation(alg int64, pubKey, authData, clientDataHash, signature []byte) (string, []any, error) {
// §4.1 Validate that alg matches the algorithm of the credentialPublicKey in authenticatorData.

// §4.2 Verify that sig is a valid signature over the concatenation of authenticatorData and
Expand Down
2 changes: 1 addition & 1 deletion protocol/attestation_packed_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func Test_verifyPackedFormat(t *testing.T) {
name string
args args
want string
want1 []interface{}
want1 []any
wantErr bool
}{
{
Expand Down
22 changes: 11 additions & 11 deletions protocol/attestation_safetynet.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ func init() {
}

type SafetyNetResponse struct {
Nonce string `json:"nonce"`
TimestampMs int64 `json:"timestampMs"`
ApkPackageName string `json:"apkPackageName"`
ApkDigestSha256 string `json:"apkDigestSha256"`
CtsProfileMatch bool `json:"ctsProfileMatch"`
ApkCertificateDigestSha256 []interface{} `json:"apkCertificateDigestSha256"`
BasicIntegrity bool `json:"basicIntegrity"`
Nonce string `json:"nonce"`
TimestampMs int64 `json:"timestampMs"`
ApkPackageName string `json:"apkPackageName"`
ApkDigestSha256 string `json:"apkDigestSha256"`
CtsProfileMatch bool `json:"ctsProfileMatch"`
ApkCertificateDigestSha256 []any `json:"apkCertificateDigestSha256"`
BasicIntegrity bool `json:"basicIntegrity"`
}

// Thanks to @koesie10 and @herrjemand for outlining how to support this type really well
Expand All @@ -40,7 +40,7 @@ type SafetyNetResponse struct {
// authenticators SHOULD make use of the Android Key Attestation when available, even if the SafetyNet API is also present.
//
// Specification: §8.5. Android SafetyNet Attestation Statement Format (https://www.w3.org/TR/webauthn/#sctn-android-safetynet-attestation)
func verifySafetyNetFormat(att AttestationObject, clientDataHash []byte) (string, []interface{}, error) {
func verifySafetyNetFormat(att AttestationObject, clientDataHash []byte) (string, []any, error) {
// The syntax of an Android Attestation statement is defined as follows:
// $$attStmtType //= (
// fmt: "android-safetynet",
Expand Down Expand Up @@ -73,8 +73,8 @@ func verifySafetyNetFormat(att AttestationObject, clientDataHash []byte) (string
return "", nil, ErrAttestationFormat.WithDetails("Unable to find the SafetyNet response")
}

token, err := jwt.Parse(string(response), func(token *jwt.Token) (interface{}, error) {
chain := token.Header["x5c"].([]interface{})
token, err := jwt.Parse(string(response), func(token *jwt.Token) (any, error) {
chain := token.Header["x5c"].([]any)

o := make([]byte, base64.StdEncoding.DecodedLen(len(chain[0].(string))))

Expand Down Expand Up @@ -108,7 +108,7 @@ func verifySafetyNetFormat(att AttestationObject, clientDataHash []byte) (string
}

// §8.5.4 Let attestationCert be the attestation certificate (https://www.w3.org/TR/webauthn/#attestation-certificate)
certChain := token.Header["x5c"].([]interface{})
certChain := token.Header["x5c"].([]any)
l := make([]byte, base64.StdEncoding.DecodedLen(len(certChain[0].(string))))

n, err := base64.StdEncoding.Decode(l, []byte(certChain[0].(string)))
Expand Down
4 changes: 3 additions & 1 deletion protocol/attestation_safetynet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func Test_verifySafetyNetFormat(t *testing.T) {
name string
args args
want string
want1 []interface{}
want1 []any
wantErr bool
}{
{
Expand All @@ -43,9 +43,11 @@ func Test_verifySafetyNetFormat(t *testing.T) {
t.Errorf("verifySafetyNetFormat() error = %v, wantErr %v", err, tt.wantErr)
return
}

if got != tt.want {
t.Errorf("verifySafetyNetFormat() got = %v, want %v", got, tt.want)
}

if !reflect.DeepEqual(got1, tt.want1) {
t.Errorf("verifySafetyNetFormat() got1 = %v, want %v", got1, tt.want1)
}
Expand Down
3 changes: 3 additions & 0 deletions protocol/attestation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,13 @@ func TestAttestationVerify(t *testing.T) {
if err := json.Unmarshal([]byte(testAttestationOptions[i]), &options); err != nil {
t.Fatal(err)
}

ccr := CredentialCreationResponse{}

if err := json.Unmarshal([]byte(testAttestationResponses[i]), &ccr); err != nil {
t.Fatal(err)
}

var pcc ParsedCredentialCreationData
pcc.ID, pcc.RawID, pcc.Type, pcc.ClientExtensionResults = ccr.ID, ccr.RawID, ccr.Type, ccr.ClientExtensionResults
pcc.Raw = ccr
Expand Down
4 changes: 2 additions & 2 deletions protocol/attestation_tpm.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ func init() {
RegisterAttestationFormat(AttestationFormatTPM, verifyTPMFormat)
}

func verifyTPMFormat(att AttestationObject, clientDataHash []byte) (string, []interface{}, error) {
func verifyTPMFormat(att AttestationObject, clientDataHash []byte) (string, []any, error) {
// Given the verification procedure inputs attStmt, authenticatorData
// and clientDataHash, the verification procedure is as follows

Expand All @@ -42,7 +42,7 @@ func verifyTPMFormat(att AttestationObject, clientDataHash []byte) (string, []in

coseAlg := webauthncose.COSEAlgorithmIdentifier(alg)

x5c, x509present := att.AttStatement["x5c"].([]interface{})
x5c, x509present := att.AttStatement["x5c"].([]any)
james-d-elliott marked this conversation as resolved.
Show resolved Hide resolved
if !x509present {
// Handle Basic Attestation steps for the x509 Certificate
return "", nil, ErrNotImplemented
Expand Down
Loading
Loading