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