Skip to content
Snippets Groups Projects
Select Git revision
  • d012a172daa89c6ac97aff056b2083f50ba7b58e
  • master default protected
  • renovate/golang.org-x-term-0.x
  • renovate/github.com-fxamacker-cbor-v2-2.x
4 results

main.go

Blame
    • ale's avatar
      064ff8e9
      Fix compatibility issue with YubiKey 5 · 064ff8e9
      ale authored
      For some reason, even though the default values for RK and UV
      should be 'false', passing the MakeCredentialOptions causes a
      failure with YubiKeys >= 5 ("failed to make credential:
      unsupported option"), so let's just avoid touching the default
      credential options.
      064ff8e9
      History
      Fix compatibility issue with YubiKey 5
      ale authored
      For some reason, even though the default values for RK and UV
      should be 'false', passing the MakeCredentialOptions causes a
      failure with YubiKeys >= 5 ("failed to make credential:
      unsupported option"), so let's just avoid touching the default
      credential options.
    main.go 2.31 KiB
    package main
    
    import (
    	"encoding/base64"
    	"errors"
    	"flag"
    	"fmt"
    	"log"
    	"os"
    
    	"github.com/duo-labs/webauthn/protocol/webauthncose"
    	"github.com/fxamacker/cbor/v2"
    	"github.com/keys-pub/go-libfido2"
    	"golang.org/x/term"
    )
    
    var (
    	forceDevicePath = flag.String("device", "", "force usage of a specific HID device (default: autodetect)")
    	username        = flag.String("username", os.Getenv("USER"), "username")
    	rpID            = flag.String("rpid", "", "WebAuthN relying party identifier")
    )
    
    func readPIN() (string, error) {
    	fmt.Printf("\rPIN> ")
    	pw, err := term.ReadPassword(int(os.Stdin.Fd()))
    	fmt.Printf("\n")
    	return string(pw), err
    }
    
    func fido2ToCOSE(pk []byte) []byte {
    	var parsed webauthncose.EC2PublicKeyData
    	parsed.KeyType = int64(webauthncose.EllipticKey)
    	parsed.Algorithm = int64(webauthncose.AlgES256)
    	parsed.Curve = 0
    	parsed.XCoord = pk[:32]
    	parsed.YCoord = pk[32:]
    	data, _ := cbor.Marshal(&parsed)
    	return data
    }
    
    func main() {
    	log.SetFlags(0)
    	flag.Parse()
    
    	if *rpID == "" {
    		log.Fatal("must specify --rpid")
    	}
    
    	devicePath := *forceDevicePath
    	if devicePath == "" {
    		locs, err := libfido2.DeviceLocations()
    		if err != nil {
    			log.Fatal("error enumerating devices: %v", err)
    		}
    		if len(locs) == 0 {
    			log.Fatal("error: no FIDO2 devices found")
    		}
    		log.Printf("using %s %s", locs[0].Manufacturer, locs[0].Product)
    		devicePath = locs[0].Path
    	}
    
    	device, err := libfido2.NewDevice(devicePath)
    	if err != nil {
    		log.Fatal("error opening device: %v", err)
    	}
    
    	// Generate pseudorandom challenge and user ID.
    	challenge := libfido2.RandBytes(32)
    	userID := libfido2.RandBytes(32)
    
    	fmt.Println("touch the device (you may be asked for a pin first)......")
    
    	var pin string
    	var attest *libfido2.Attestation
    	for {
    		attest, err = device.MakeCredential(
    			challenge,
    			libfido2.RelyingParty{
    				ID:   *rpID,
    				Name: *rpID,
    			},
    			libfido2.User{
    				ID:   userID,
    				Name: *username,
    			},
    			libfido2.ES256,
    			pin,
    			nil,
    		)
    		if errors.Is(err, libfido2.ErrPinRequired) {
    			pin, err = readPIN()
    			if err != nil {
    				log.Fatalf("error reading PIN: %v", err)
    			}
    			continue
    		}
    		if err != nil {
    			log.Fatal(err)
    		}
    		break
    	}
    
    	fmt.Printf(
    		"key_handle: \"%s\"\npublic_key: \"%s\"\n",
    		base64.StdEncoding.EncodeToString(attest.CredentialID),
    		base64.StdEncoding.EncodeToString(fido2ToCOSE(attest.PubKey)),
    	)
    
    }