Skip to content
Snippets Groups Projects
Commit 6fa6e1f2 authored by ale's avatar ale
Browse files

Do not log unused webauthn credential

parent b836909e
No related branches found
No related tags found
1 merge request!34Draft: Webauthn
Pipeline #24751 failed
......@@ -548,18 +548,12 @@ func (s *Server) finishWebAuthnLogin(user *backend.User, sessionData *webauthn.S
wuser := newWebAuthnUser(user)
// Attempt non-legacy validation first.
cred, err := s.webAuthnHandler.ValidateLogin(wuser, *sessionData, parsedResponse)
_, err := s.webAuthnHandler.ValidateLogin(wuser, *sessionData, parsedResponse)
if err != nil {
cred, err = s.compatWebAuthnHandler.ValidateLogin(wuser, *sessionData, parsedResponse)
if err != nil {
return err
}
_, err = s.compatWebAuthnHandler.ValidateLogin(wuser, *sessionData, parsedResponse)
}
// What do we do with this credential?
log.Printf("cred: %v", cred)
return nil
return err
}
func checkPassword(password, hash []byte) bool {
......
......@@ -106,17 +106,12 @@ func UnmarshalEncryptedKey(s string) (*EncryptedKey, error) {
// U2FRegistration stores information on a single WebAuthN/U2F device
// registration.
//
// This type supports both legacy U2F registrations, as well as newer
// WebAuthN registrations. These have different serialization formats.
// The legacy U2F serialized format follows part of the U2F standard
// and just stores 64 bytes of the public key immediately followed by
// the key handle data, with no encoding. Note that the public key
// itself is a serialization of the elliptic curve parameters. The
// newer serialization format is JSON, for extensibility. Marshaling
// always happens in JSON, legacy format is read-only.
// The public key is expected to be in raw COSE format. Note that on
// the wire (i.e. when serialized as JSON) both the public key and the
// key handle are base64-encoded.
//
// The data in U2FRegistration is still encoded, but it can be turned
// into a usable run-time form by calling Decode().
// It is possible to obtain a usable webauthn.Credential object at
// run-time by calling Decode().
type U2FRegistration struct {
KeyHandle []byte `json:"key_handle"`
PublicKey []byte `json:"public_key"`
......@@ -133,6 +128,11 @@ func (r *U2FRegistration) Marshal() string {
return string(data)
}
const (
legacySerializedU2FKeySize = 65
minU2FKeySize = 64
)
// UnmarshalU2FRegistration parses a U2FRegistration from its serialized format.
func UnmarshalU2FRegistration(s string) (*U2FRegistration, error) {
// Try JSON first.
......@@ -143,13 +143,13 @@ func UnmarshalU2FRegistration(s string) (*U2FRegistration, error) {
// Deserialize legacy format, and perform a conversion of the
// public key to COSE format.
if len(s) < 65 {
if len(s) < legacySerializedU2FKeySize {
return nil, errors.New("badly encoded u2f registration")
}
b := []byte(s)
return &U2FRegistration{
PublicKey: u2fToCOSE(b[:65]),
KeyHandle: b[65:],
PublicKey: u2fToCOSE(b[:legacySerializedU2FKeySize]),
KeyHandle: b[legacySerializedU2FKeySize:],
Legacy: true,
}, nil
}
......@@ -171,6 +171,14 @@ func ParseLegacyU2FRegistrationFromStrings(keyHandle, publicKey string) (*U2FReg
return nil, fmt.Errorf("error decoding public key: %w", err)
}
// Simple sanity check for non-empty fields.
if len(kh) == 0 {
return nil, errors.New("missing key handle")
}
if len(pk) < minU2FKeySize {
return nil, errors.New("public key missing or too short")
}
return &U2FRegistration{
PublicKey: u2fToCOSE(pk),
KeyHandle: kh,
......@@ -181,7 +189,7 @@ func ParseLegacyU2FRegistrationFromStrings(keyHandle, publicKey string) (*U2FReg
// 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.
// 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 {
......@@ -192,6 +200,14 @@ func ParseU2FRegistrationFromStrings(keyHandle, publicKey string) (*U2FRegistrat
return nil, fmt.Errorf("error decoding public key: %w", err)
}
// Simple sanity check for non-empty fields.
if len(kh) == 0 {
return nil, errors.New("missing key handle")
}
if len(pk) < minU2FKeySize {
return nil, errors.New("public key missing or too short")
}
return &U2FRegistration{
PublicKey: pk,
KeyHandle: kh,
......@@ -207,17 +223,18 @@ func (r *U2FRegistration) Decode() (webauthn.Credential, error) {
}, nil
}
// Convert a legacy U2F public key to COSE format.
func u2fToCOSE(pk []byte) []byte {
var parsed webauthncose.EC2PublicKeyData
parsed.KeyType = int64(webauthncose.EllipticKey)
parsed.Algorithm = int64(webauthncose.AlgES256)
parsed.XCoord = pk[1:33]
parsed.YCoord = pk[33:]
data, _ := cbor.Marshal(&parsed) // nolint: errcheck
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:
// Faster, but more questionable, implementation of the above:
//
// func u2fToCOSE(pk []byte) []byte {
// x := pk[1:33]
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment