Merge pull request #370 from smallstep/yubi-management-key

Make the YubiKey management key configurable.
This commit is contained in:
Mariano Cano 2020-09-17 16:15:24 -07:00 committed by GitHub
commit 4f3b24af8f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 61 additions and 18 deletions

View File

@ -9,6 +9,7 @@ import (
"crypto/sha1" "crypto/sha1"
"crypto/x509" "crypto/x509"
"crypto/x509/pkix" "crypto/x509/pkix"
"encoding/hex"
"encoding/pem" "encoding/pem"
"flag" "flag"
"fmt" "fmt"
@ -27,18 +28,23 @@ import (
_ "github.com/smallstep/certificates/kms/yubikey" _ "github.com/smallstep/certificates/kms/yubikey"
) )
// Config is a mapping of the cli flags.
type Config struct { type Config struct {
RootOnly bool RootOnly bool
RootSlot string RootSlot string
CrtSlot string CrtSlot string
RootFile string RootFile string
KeyFile string KeyFile string
Pin string Pin string
Force bool ManagementKey string
Force bool
} }
// Validate checks the flags in the config.
func (c *Config) Validate() error { func (c *Config) Validate() error {
switch { switch {
case c.ManagementKey != "" && len(c.ManagementKey) != 48:
return errors.New("flag `--management-key` must be 48 hexadecimal characters (24 bytes)")
case c.RootFile != "" && c.KeyFile == "": case c.RootFile != "" && c.KeyFile == "":
return errors.New("flag `--root` requires flag `--key`") return errors.New("flag `--root` requires flag `--key`")
case c.KeyFile != "" && c.RootFile == "": case c.KeyFile != "" && c.RootFile == "":
@ -56,12 +62,18 @@ func (c *Config) Validate() error {
if c.RootOnly { if c.RootOnly {
c.CrtSlot = "" c.CrtSlot = ""
} }
if c.ManagementKey != "" {
if _, err := hex.DecodeString(c.ManagementKey); err != nil {
return errors.Wrap(err, "flag `--management-key` is not valid")
}
}
return nil return nil
} }
} }
func main() { func main() {
var c Config var c Config
flag.StringVar(&c.ManagementKey, "management-key", "", `Management key to use in hexadecimal format. (default "010203040506070801020304050607080102030405060708")`)
flag.BoolVar(&c.RootOnly, "root-only", false, "Slot only the root certificate and sign and intermediate.") flag.BoolVar(&c.RootOnly, "root-only", false, "Slot only the root certificate and sign and intermediate.")
flag.StringVar(&c.RootSlot, "root-slot", "9a", "Slot to store the root certificate.") flag.StringVar(&c.RootSlot, "root-slot", "9a", "Slot to store the root certificate.")
flag.StringVar(&c.CrtSlot, "crt-slot", "9c", "Slot to store the intermediate certificate.") flag.StringVar(&c.CrtSlot, "crt-slot", "9c", "Slot to store the intermediate certificate.")
@ -82,8 +94,9 @@ func main() {
c.Pin = string(pin) c.Pin = string(pin)
k, err := kms.New(context.Background(), apiv1.Options{ k, err := kms.New(context.Background(), apiv1.Options{
Type: string(apiv1.YubiKey), Type: string(apiv1.YubiKey),
Pin: c.Pin, Pin: c.Pin,
ManagementKey: c.ManagementKey,
}) })
if err != nil { if err != nil {
fatal(err) fatal(err)
@ -109,7 +122,11 @@ func main() {
} }
func fatal(err error) { func fatal(err error) {
fmt.Fprintln(os.Stderr, err) if os.Getenv("STEPDEBUG") == "1" {
fmt.Fprintf(os.Stderr, "%+v\n", err)
} else {
fmt.Fprintln(os.Stderr, err)
}
os.Exit(1) os.Exit(1)
} }

View File

@ -19,11 +19,12 @@ type KeyManager interface {
// CertificateManager is the interface implemented by the KMS that can load and // CertificateManager is the interface implemented by the KMS that can load and
// store x509.Certificates. // store x509.Certificates.
type CertificateManager interface { type CertificateManager interface {
LoadCerticate(req *LoadCertificateRequest) (*x509.Certificate, error) LoadCertificate(req *LoadCertificateRequest) (*x509.Certificate, error)
StoreCertificate(req *StoreCertificateRequest) error StoreCertificate(req *StoreCertificateRequest) error
} }
// ErrNotImplemented // ErrNotImplemented is the type of error returned if an operation is not
// implemented.
type ErrNotImplemented struct { type ErrNotImplemented struct {
msg string msg string
} }
@ -53,6 +54,7 @@ const (
YubiKey Type = "yubikey" YubiKey Type = "yubikey"
) )
// Options are the KMS options. They represent the kms object in the ca.json.
type Options struct { type Options struct {
// The type of the KMS to use. // The type of the KMS to use.
Type string `json:"type"` Type string `json:"type"`
@ -66,6 +68,15 @@ type Options struct {
// Pin used to access the PKCS11 module. // Pin used to access the PKCS11 module.
Pin string `json:"pin"` Pin string `json:"pin"`
// ManagementKey used in YubiKeys. Default management key is the hexadecimal
// string 010203040506070801020304050607080102030405060708:
// []byte{
// 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
// 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
// 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
// }
ManagementKey string `json:"managementKey"`
// Region to use in AmazonKMS. // Region to use in AmazonKMS.
Region string `json:"region"` Region string `json:"region"`

View File

@ -6,6 +6,7 @@ import (
"context" "context"
"crypto" "crypto"
"crypto/x509" "crypto/x509"
"encoding/hex"
"net/url" "net/url"
"strings" "strings"
@ -16,13 +17,26 @@ import (
// YubiKey implements the KMS interface on a YubiKey. // YubiKey implements the KMS interface on a YubiKey.
type YubiKey struct { type YubiKey struct {
yk *piv.YubiKey yk *piv.YubiKey
pin string pin string
managementKey [24]byte
} }
// New initializes a new YubiKey. // New initializes a new YubiKey.
// TODO(mariano): only one card is currently supported. // TODO(mariano): only one card is currently supported.
func New(ctx context.Context, opts apiv1.Options) (*YubiKey, error) { func New(ctx context.Context, opts apiv1.Options) (*YubiKey, error) {
managementKey := piv.DefaultManagementKey
if opts.ManagementKey != "" {
b, err := hex.DecodeString(opts.ManagementKey)
if err != nil {
return nil, errors.Wrap(err, "error decoding managementKey")
}
if len(b) != 24 {
return nil, errors.New("invalid managementKey: length is not 24 bytes")
}
copy(managementKey[:], b[:24])
}
cards, err := piv.Cards() cards, err := piv.Cards()
if err != nil { if err != nil {
return nil, err return nil, err
@ -37,8 +51,9 @@ func New(ctx context.Context, opts apiv1.Options) (*YubiKey, error) {
} }
return &YubiKey{ return &YubiKey{
yk: yk, yk: yk,
pin: opts.Pin, pin: opts.Pin,
managementKey: managementKey,
}, nil }, nil
} }
@ -76,7 +91,7 @@ func (k *YubiKey) StoreCertificate(req *apiv1.StoreCertificateRequest) error {
return err return err
} }
err = k.yk.SetCertificate(piv.DefaultManagementKey, slot, req.Certificate) err = k.yk.SetCertificate(k.managementKey, slot, req.Certificate)
if err != nil { if err != nil {
return errors.Wrap(err, "error storing certificate") return errors.Wrap(err, "error storing certificate")
} }
@ -110,7 +125,7 @@ func (k *YubiKey) CreateKey(req *apiv1.CreateKeyRequest) (*apiv1.CreateKeyRespon
return nil, err return nil, err
} }
pub, err := k.yk.GenerateKey(piv.DefaultManagementKey, slot, piv.Key{ pub, err := k.yk.GenerateKey(k.managementKey, slot, piv.Key{
Algorithm: alg, Algorithm: alg,
PINPolicy: piv.PINPolicyAlways, PINPolicy: piv.PINPolicyAlways,
TouchPolicy: piv.TouchPolicyNever, TouchPolicy: piv.TouchPolicyNever,