Repository: go-webauthn/webauthn
Pull request #5 (2022-03-01 05:28:45)

fix: use ctap2 cbor
Co-authored-by: Maximilian Leibl <leibl.maximilian@gmail.com>
# Conflicts:
#	protocol/attestation_u2f.go


Patch:
diff --git a/protocol/authenticator.go b/protocol/authenticator.go
index 38dc0a8..c9e26e4 100644
--- a/protocol/authenticator.go
+++ b/protocol/authenticator.go
@@ -5,7 +5,7 @@ import (
 	"encoding/binary"
 	"fmt"
 
-	"github.com/fxamacker/cbor/v2"
+	"github.com/go-webauthn/webauthn/protocol/webauthncbor"
 )
 
 var minAuthDataLength = 37
@@ -199,8 +199,8 @@ func (a *AuthenticatorData) unmarshalAttestedData(rawAuthData []byte) {
 // Unmarshall the credential's Public Key into CBOR encoding
 func unmarshalCredentialPublicKey(keyBytes []byte) []byte {
 	var m interface{}
-	cbor.Unmarshal(keyBytes, &m)
-	rawBytes, _ := cbor.Marshal(m)
+	webauthncbor.Unmarshal(keyBytes, &m)
+	rawBytes, _ := webauthncbor.Marshal(m)
 	return rawBytes
 }
 
diff --git a/protocol/webauthncbor/webauthncbor.go b/protocol/webauthncbor/webauthncbor.go
new file mode 100644
index 0000000..1aff823
--- /dev/null
+++ b/protocol/webauthncbor/webauthncbor.go
@@ -0,0 +1,30 @@
+package webauthncbor
+
+import "github.com/fxamacker/cbor/v2"
+
+const nestedLevelsAllowed = 4
+
+// ctap2CBORDecMode is the cbor.DecMode following the CTAP2 canonical CBOR encoding form
+// (https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#message-encoding)
+var ctap2CBORDecMode, _ = cbor.DecOptions{
+	DupMapKey:       cbor.DupMapKeyEnforcedAPF,
+	MaxNestedLevels: nestedLevelsAllowed,
+	IndefLength:     cbor.IndefLengthForbidden,
+	TagsMd:          cbor.TagsForbidden,
+}.DecMode()
+
+var ctap2CBOREncMode, _ = cbor.CTAP2EncOptions().EncMode()
+
+// Unmarshal parses the CBOR-encoded data into the value pointed to by v
+// following the CTAP2 canonical CBOR encoding form.
+// (https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#message-encoding)
+func Unmarshal(data []byte, v interface{}) error {
+	return ctap2CBORDecMode.Unmarshal(data, v)
+}
+
+// Marshal encodes the value pointed to by v
+// following the CTAP2 canonical CBOR encoding form.
+// (https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#message-encoding)
+func Marshal(v interface{}) ([]byte, error) {
+	return ctap2CBOREncMode.Marshal(v)
+}
diff --git a/protocol/webauthncose/webauthncose.go b/protocol/webauthncose/webauthncose.go
index 835f0b7..1b1a181 100644
--- a/protocol/webauthncose/webauthncose.go
+++ b/protocol/webauthncose/webauthncose.go
@@ -12,8 +12,9 @@ import (
 	"hash"
 	"math/big"
 
-	"github.com/fxamacker/cbor/v2"
 	"golang.org/x/crypto/ed25519"
+
+	"github.com/go-webauthn/webauthn/protocol/webauthncbor"
 )
 
 // PublicKeyData The public key portion of a Relying Party-specific credential key pair, generated
@@ -159,21 +160,21 @@ func HasherFromCOSEAlg(coseAlg COSEAlgorithmIdentifier) func() hash.Hash {
 // Figure out what kind of COSE material was provided and create the data for the new key
 func ParsePublicKey(keyBytes []byte) (interface{}, error) {
 	pk := PublicKeyData{}
-	cbor.Unmarshal(keyBytes, &pk)
+	webauthncbor.Unmarshal(keyBytes, &pk)
 	switch COSEKeyType(pk.KeyType) {
 	case OctetKey:
 		var o OKPPublicKeyData
-		cbor.Unmarshal(keyBytes, &o)
+		webauthncbor.Unmarshal(keyBytes, &o)
 		o.PublicKeyData = pk
 		return o, nil
 	case EllipticKey:
 		var e EC2PublicKeyData
-		cbor.Unmarshal(keyBytes, &e)
+		webauthncbor.Unmarshal(keyBytes, &e)
 		e.PublicKeyData = pk
 		return e, nil
 	case RSAKey:
 		var r RSAPublicKeyData
-		cbor.Unmarshal(keyBytes, &r)
+		webauthncbor.Unmarshal(keyBytes, &r)
 		r.PublicKeyData = pk
 		return r, nil
 	default:


Test patch:
diff --git a/protocol/assertion_test.go b/protocol/assertion_test.go
index fc178a8..5ff7510 100644
--- a/protocol/assertion_test.go
+++ b/protocol/assertion_test.go
@@ -8,7 +8,7 @@ import (
 	"reflect"
 	"testing"
 
-	"github.com/fxamacker/cbor/v2"
+	"github.com/go-webauthn/webauthn/protocol/webauthncbor"
 )
 
 func TestParseCredentialRequestResponse(t *testing.T) {
@@ -124,10 +124,10 @@ func TestParseCredentialRequestResponse(t *testing.T) {
 					// Unmarshall CredentialPublicKey
 					var pkWant interface{}
 					keyBytesWant := tt.want.Response.AuthenticatorData.AttData.CredentialPublicKey
-					cbor.Unmarshal(keyBytesWant, &pkWant)
+					webauthncbor.Unmarshal(keyBytesWant, &pkWant)
 					var pkGot interface{}
 					keyBytesGot := got.Response.AuthenticatorData.AttData.CredentialPublicKey
-					cbor.Unmarshal(keyBytesGot, &pkGot)
+					webauthncbor.Unmarshal(keyBytesGot, &pkGot)
 					if !reflect.DeepEqual(pkGot, pkWant) {
 						t.Errorf("Response = %+v \n want: %+v", pkGot, pkWant)
 					} else {
diff --git a/protocol/attestation.go b/protocol/attestation.go
index 9362c5f..6e63271 100644
--- a/protocol/attestation.go
+++ b/protocol/attestation.go
@@ -5,7 +5,7 @@ import (
 	"encoding/json"
 	"fmt"
 
-	"github.com/fxamacker/cbor/v2"
+	"github.com/go-webauthn/webauthn/protocol/webauthncbor"
 )
 
 // From §5.2.1 (https://www.w3.org/TR/webauthn/#authenticatorattestationresponse)
@@ -85,7 +85,7 @@ func (ccr *AuthenticatorAttestationResponse) Parse() (*ParsedAttestationResponse
 		return nil, ErrParsingData.WithInfo(err.Error())
 	}
 
-	err = cbor.Unmarshal(ccr.AttestationObject, &p.AttestationObject)
+	err = webauthncbor.Unmarshal(ccr.AttestationObject, &p.AttestationObject)
 	if err != nil {
 		return nil, ErrParsingData.WithInfo(err.Error())
 	}
diff --git a/protocol/attestation_u2f.go b/protocol/attestation_u2f.go
index 44fd349..e5848fc 100644
--- a/protocol/attestation_u2f.go
+++ b/protocol/attestation_u2f.go
@@ -6,7 +6,7 @@ import (
 	"crypto/elliptic"
 	"crypto/x509"
 
-	"github.com/fxamacker/cbor/v2"
+	"github.com/go-webauthn/webauthn/protocol/webauthncbor"
 	"github.com/go-webauthn/webauthn/protocol/webauthncose"
 )
 
@@ -25,7 +25,7 @@ func verifyU2FFormat(att AttestationObject, clientDataHash []byte) (string, []in
 	// Signing procedure step - If the credential public key of the given credential is not of
 	// algorithm -7 ("ES256"), stop and return an error.
 	key := webauthncose.EC2PublicKeyData{}
-	cbor.Unmarshal(att.AuthData.AttData.CredentialPublicKey, &key)
+	webauthncbor.Unmarshal(att.AuthData.AttData.CredentialPublicKey, &key)
 
 	if webauthncose.COSEAlgorithmIdentifier(key.PublicKeyData.Algorithm) != webauthncose.AlgES256 {
 		return u2fAttestationKey, nil, ErrUnsupportedAlgorithm.WithDetails("Non-ES256 Public Key algorithm used")
diff --git a/protocol/credential_test.go b/protocol/credential_test.go
index 6986265..28e65e2 100644
--- a/protocol/credential_test.go
+++ b/protocol/credential_test.go
@@ -8,7 +8,7 @@ import (
 	"reflect"
 	"testing"
 
-	"github.com/fxamacker/cbor/v2"
+	"github.com/go-webauthn/webauthn/protocol/webauthncbor"
 )
 
 func TestParseCredentialCreationResponse(t *testing.T) {
@@ -113,10 +113,10 @@ func TestParseCredentialCreationResponse(t *testing.T) {
 			// Unmarshall CredentialPublicKey
 			var pkWant interface{}
 			keyBytesWant := tt.want.Response.AttestationObject.AuthData.AttData.CredentialPublicKey
-			cbor.Unmarshal(keyBytesWant, &pkWant)
+			webauthncbor.Unmarshal(keyBytesWant, &pkWant)
 			var pkGot interface{}
 			keyBytesGot := got.Response.AttestationObject.AuthData.AttData.CredentialPublicKey
-			cbor.Unmarshal(keyBytesGot, &pkGot)
+			webauthncbor.Unmarshal(keyBytesGot, &pkGot)
 			if !reflect.DeepEqual(pkGot, pkWant) {
 				t.Errorf("Response = %+v \n want: %+v", pkGot, pkWant)
 			}
diff --git a/protocol/webauthncose/webauthncose_test.go b/protocol/webauthncose/webauthncose_test.go
index 480347c..57b28fe 100644
--- a/protocol/webauthncose/webauthncose_test.go
+++ b/protocol/webauthncose/webauthncose_test.go
@@ -4,8 +4,9 @@ import (
 	"crypto/rand"
 	"testing"
 
-	"github.com/fxamacker/cbor/v2"
 	"golang.org/x/crypto/ed25519"
+
+	"github.com/go-webauthn/webauthn/protocol/webauthncbor"
 )
 
 // TestOKPSignatureVerification is a compatibility test to ensure that removing
@@ -58,7 +59,7 @@ MCowBQYDK2VwAyEAe4gQJK3JgtOAuHceO5v45LOZi8fQWDBmAs5NDy/kt4E=
 		},
 	}
 	// Get the CBOR-encoded representation of the OKPPublicKeyData
-	buf, _ := cbor.Marshal(key)
+	buf, _ := webauthncbor.Marshal(key)
 
 	got := DisplayPublicKey(buf)
 	if got != expected {
diff --git a/webauthn/credential_test.go b/webauthn/credential_test.go
index 7284881..8deae1b 100644
--- a/webauthn/credential_test.go
+++ b/webauthn/credential_test.go
@@ -11,14 +11,14 @@ func TestMakeNewCredential(t *testing.T) {
 	type args struct {
 		c *protocol.ParsedCredentialCreationData
 	}
-	tests := []struct {
+
+	var tests []struct {
 		name    string
 		args    args
 		want    *Credential
 		wantErr bool
-	}{
-		// TODO: Add test cases.
 	}
+
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
 			got, err := MakeNewCredential(tt.args.c)