Skip to content

Commit

Permalink
Add the tsh fido2 attobj command
Browse files Browse the repository at this point in the history
  • Loading branch information
codingllama committed May 5, 2023
1 parent 89c8b9a commit 70ccdf7
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 5 deletions.
104 changes: 99 additions & 5 deletions tool/tsh/fido2.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,30 +15,43 @@
package main

import (
"crypto/x509"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"os"

"github.com/go-webauthn/webauthn/protocol"
"github.com/go-webauthn/webauthn/protocol/webauthncbor"
"github.com/go-webauthn/webauthn/protocol/webauthncose"
"github.com/gravitational/kingpin"
"github.com/gravitational/trace"

wancli "github.com/gravitational/teleport/lib/auth/webauthncli"
)

type fido2Command struct {
diag *fido2DiagCommand
diag *fido2DiagCommand
attobj *fido2AttobjCommand
}

func newFIDO2Command(app *kingpin.Application) *fido2Command {
cmd := &fido2Command{
diag: &fido2DiagCommand{},
root := &fido2Command{
diag: &fido2DiagCommand{},
attobj: &fido2AttobjCommand{},
}

f2 := app.Command("fido2", "FIDO2 commands").Hidden()

diag := f2.Command("diag", "Run FIDO2 diagnostics").Hidden()
cmd.diag.CmdClause = diag
root.diag.CmdClause = diag

return cmd
attObj := f2.Command("attobj", "Parse a stored attestation object").Hidden()
attObj.Arg("att-obj", "Attestation object encoded in base64 standard or RawURL").StringVar(&root.attobj.attObjB64)
root.attobj.CmdClause = attObj

return root
}

type fido2DiagCommand struct {
Expand All @@ -61,3 +74,84 @@ func (_ *fido2DiagCommand) run(cf *CLIConf) error {

return trace.Wrap(err)
}

type fido2AttobjCommand struct {
*kingpin.CmdClause

attObjB64 string
}

func (c *fido2AttobjCommand) run(_ *CLIConf) error {
var aoRaw []byte
for _, enc := range []*base64.Encoding{
base64.StdEncoding,
base64.RawURLEncoding,
} {
var err error
aoRaw, err = enc.DecodeString(c.attObjB64)
if err == nil {
break
}
}
if aoRaw == nil {
return errors.New("failed to decode attestation object")
}

ao := &protocol.AttestationObject{}
if err := webauthncbor.Unmarshal(aoRaw, ao); err != nil {
return fmt.Errorf("attestation object unmarshal: %w", err)
}
if err := ao.AuthData.Unmarshal(ao.RawAuthData); err != nil {
return fmt.Errorf("authdata unmarshal: %w", err)
}

// Print attestation object as JSON.
enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", " ")
if err := enc.Encode(ao); err != nil {
return fmt.Errorf("encode attestation object to JSON: %w", err)
}

// Print public key.
if len(ao.AuthData.AttData.CredentialPublicKey) > 0 {
pubKey, err := webauthncose.ParsePublicKey(ao.AuthData.AttData.CredentialPublicKey)
if err == nil {
fmt.Println("\nAuthData.AttData.public_key:")
if err := enc.Encode(pubKey); err != nil {
return fmt.Errorf("encode public key: %w", err)
}
}
}

// Print attestation certificates.
if x5c, ok := ao.AttStatement["x5c"]; ok {
if x5cArray, ok := x5c.([]interface{}); ok {
for i, certI := range x5cArray {
certDER, ok := certI.([]byte)
if !ok {
continue
}

cert, err := x509.ParseCertificate(certDER)
if err != nil {
continue
}

type niceCert struct {
Raw []byte
Issuer string
Subject string
}

fmt.Printf("\nattStmt.x509[%v]:\n", i)
enc.Encode(niceCert{
Raw: cert.Raw,
Issuer: cert.Issuer.String(),
Subject: cert.Subject.String(),
})
}
}
}

return nil
}
2 changes: 2 additions & 0 deletions tool/tsh/tsh.go
Original file line number Diff line number Diff line change
Expand Up @@ -1300,6 +1300,8 @@ func Run(ctx context.Context, args []string, opts ...cliOption) error {
err = onDaemonStop(&cf)
case f2.diag.FullCommand():
err = f2.diag.run(&cf)
case f2.attobj.FullCommand():
err = f2.attobj.run(&cf)
case tid.diag.FullCommand():
err = tid.diag.run(&cf)
case webauthnwin.diag.FullCommand():
Expand Down

0 comments on commit 70ccdf7

Please sign in to comment.