From ac022b21224d8668cb57f03da13001eca0932ef7 Mon Sep 17 00:00:00 2001
From: ale <ale@incal.net>
Date: Fri, 3 Dec 2021 10:37:31 +0000
Subject: [PATCH] Use more robust U2F -> COSE conversion method

---
 go.mod                                 |  1 +
 ldap/compositetypes/composite_types.go | 69 +++++++++++++++++++-------
 2 files changed, 52 insertions(+), 18 deletions(-)

diff --git a/go.mod b/go.mod
index c7ae8a8..5a4c312 100644
--- a/go.mod
+++ b/go.mod
@@ -11,6 +11,7 @@ require (
 	github.com/coreos/go-systemd/v22 v22.3.2
 	github.com/duo-labs/webauthn v0.0.0-20200714211715-1daaee874e43
 	github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594
+	github.com/fxamacker/cbor/v2 v2.2.0
 	github.com/go-asn1-ber/asn1-ber v1.5.3
 	github.com/go-ldap/ldap/v3 v3.4.1
 	github.com/gofrs/flock v0.8.0 // indirect
diff --git a/ldap/compositetypes/composite_types.go b/ldap/compositetypes/composite_types.go
index 812da55..263f6d6 100644
--- a/ldap/compositetypes/composite_types.go
+++ b/ldap/compositetypes/composite_types.go
@@ -23,7 +23,9 @@ import (
 	"fmt"
 	"strings"
 
+	"github.com/duo-labs/webauthn/protocol/webauthncose"
 	"github.com/duo-labs/webauthn/webauthn"
+	"github.com/fxamacker/cbor/v2"
 )
 
 // AppSpecificPassword stores information on an application-specific
@@ -152,11 +154,11 @@ func UnmarshalU2FRegistration(s string) (*U2FRegistration, error) {
 	}, nil
 }
 
-// ParseU2FRegistrationFromStrings parses the legacy U2F format used
+// ParseLegacyU2FRegistrationFromStrings parses the legacy U2F format used
 // in manual key specifications etc. which consists of a
 // base64(url)-encoded key handle, and a hex-encoded public key (in
 // legacy U2F format).
-func ParseU2FRegistrationFromStrings(keyHandle, publicKey string) (*U2FRegistration, error) {
+func ParseLegacyU2FRegistrationFromStrings(keyHandle, publicKey string) (*U2FRegistration, error) {
 	// U2F key handles are base64(url)-encoded (no trailing =s).
 	kh, err := base64.RawURLEncoding.DecodeString(keyHandle)
 	if err != nil {
@@ -176,6 +178,26 @@ func ParseU2FRegistrationFromStrings(keyHandle, publicKey string) (*U2FRegistrat
 	}, nil
 }
 
+// ParseU2FRegistrationFromStrings parses the U2F registration format
+// used in manual key specifications that is used by Fido2-aware
+// programs such as pamu2fcfg >= 1.0.0. Both parameters are
+// base64-encoded, public key should be in COSE format.
+func ParseU2FRegistrationFromStrings(keyHandle, publicKey string) (*U2FRegistration, error) {
+	kh, err := base64.StdEncoding.DecodeString(keyHandle)
+	if err != nil {
+		return nil, fmt.Errorf("error decoding key handle: %w", err)
+	}
+	pk, err := base64.StdEncoding.DecodeString(publicKey)
+	if err != nil {
+		return nil, fmt.Errorf("error decoding public key: %w", err)
+	}
+
+	return &U2FRegistration{
+		PublicKey: pk,
+		KeyHandle: kh,
+	}, nil
+}
+
 // Decode returns a u2f.Registration object with the decoded public
 // key ready for use in verification.
 func (r *U2FRegistration) Decode() (webauthn.Credential, error) {
@@ -185,21 +207,32 @@ func (r *U2FRegistration) Decode() (webauthn.Credential, error) {
 	}, nil
 }
 
+// Convert a legacy U2F public key to COSE format.
 func u2fToCOSE(pk []byte) []byte {
-	x := pk[1:33]
-	y := pk[33:]
-
-	out := []byte{
-		0xa4,
-		0x01, 0x02,
-		0x03, 0x26,
-		0x21, 0x58, 0x20,
-	}
-	out = append(out, x...)
-	out = append(out, []byte{
-		0x22, 0x58, 0x20,
-	}...)
-	out = append(out, y...)
-
-	return out
+	var key webauthncose.EC2PublicKeyData
+	key.KeyType = int64(webauthncose.EllipticKey)
+	key.Algorithm = int64(webauthncose.AlgES256)
+	key.XCoord = pk[1:33]
+	key.YCoord = pk[33:]
+	data, _ := cbor.Marshal(&key) // nolint: errcheck
+	return data
 }
+
+// Faster, but more questionable, implementation of the above:
+//
+// func u2fToCOSE(pk []byte) []byte {
+// 	x := pk[1:33]
+// 	y := pk[33:]
+// 	out := []byte{
+// 		0xa4,
+// 		0x01, 0x02,
+// 		0x03, 0x26,
+// 		0x21, 0x58, 0x20,
+// 	}
+// 	out = append(out, x...)
+// 	out = append(out, []byte{
+// 		0x22, 0x58, 0x20,
+// 	}...)
+// 	out = append(out, y...)
+// 	return out
+// }
-- 
GitLab