Skip to content

Commit

Permalink
Merge pull request #43 from cashapp/yoav.use-metadata-to-store-manage…
Browse files Browse the repository at this point in the history
…ment-key

Store PIV management key in Yubikey metadata (PIN protected)
  • Loading branch information
yoavamit authored Mar 19, 2024
2 parents 822d59d + 7844aea commit 5adebcf
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 13 deletions.
14 changes: 5 additions & 9 deletions cmd/pivit/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,11 @@ func commandGenerate(slot string, isP256, selfSign, generateCsr, assumeYes bool,
return errors.Wrap(err, "get pin")
}

managementKey := deriveManagementKey(pin)
managementKey, err := utils.GetOrSetManagementKey(yk, pin)
if err != nil {
return errors.Wrap(err, "failed to use management key")
}

algorithm := piv.AlgorithmEC384
if isP256 {
algorithm = piv.AlgorithmEC256
Expand Down Expand Up @@ -128,14 +132,6 @@ func commandGenerate(slot string, isP256, selfSign, generateCsr, assumeYes bool,
return nil
}

func deriveManagementKey(pin string) *[24]byte {
hash := crypto.SHA256.New()
sha1 := hash.Sum([]byte(pin))
var mk [24]byte
copy(mk[:], sha1[:24])
return &mk
}

func randomSerial() (*big.Int, error) {
max := new(big.Int)
// at most 20 bytes (160 bits) long
Expand Down
6 changes: 4 additions & 2 deletions cmd/pivit/import.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,10 @@ func commandImport(file string, first bool, slot string) error {
return errors.Wrap(err, "get pin")
}

managementKey := deriveManagementKey(pin)

managementKey, err := utils.GetOrSetManagementKey(yk, pin)
if err != nil {
return errors.Wrap(err, "failed to use management key")
}
return ImportCertificate(cert, yk, managementKey, slot)
}

Expand Down
11 changes: 10 additions & 1 deletion cmd/pivit/reset.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,19 @@ func commandReset() error {
return errors.Wrap(err, "failed to change pin")
}

newManagementKey := deriveManagementKey(newPin)
newManagementKey, err := utils.RandomManagementKey()
if err != nil {
return errors.Wrap(err, "failed to generate random management key")
}

if err = yk.SetManagementKey(piv.DefaultManagementKey, *newManagementKey); err != nil {
return errors.Wrap(err, "set new management key")
}
if err = yk.SetMetadata(*newManagementKey, &piv.Metadata{
ManagementKey: newManagementKey,
}); err != nil {
return errors.Wrap(err, "failed to store new management key")
}

randomPuk, err := rand.Int(rand.Reader, big.NewInt(100_000_000))
if err != nil {
Expand Down
59 changes: 58 additions & 1 deletion cmd/pivit/utils/utils.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
package utils

import (
"crypto"
"crypto/rand"
"crypto/sha1"
"crypto/x509"
"encoding/hex"
"fmt"
"strings"

"github.com/pkg/errors"

"github.com/go-piv/piv-go/piv"
"github.com/manifoldco/promptui"
)
Expand Down Expand Up @@ -48,7 +52,7 @@ func Confirm(label string) (bool, error) {
}
result, err := prompt.Run()

return (strings.ToLower(result) == "y"), err
return strings.ToLower(result) == "y", err
}

// GetSlot returns a piv.Slot slot. Defaults to 9e
Expand All @@ -66,3 +70,56 @@ func GetSlot(slot string) piv.Slot {
return piv.SlotCardAuthentication
}
}

// RandomManagementKey returns a *[24]byte slice filled with random byte values
func RandomManagementKey() (*[24]byte, error) {
mk := make([]byte, 24)
if _, err := rand.Reader.Read(mk); err != nil {
return nil, err
}
return (*[24]byte)(mk), nil
}

// DeriveManagementKey returns the first 24 bytes of the SHA256 checksum of the given pin
func DeriveManagementKey(pin string) *[24]byte {
hash := crypto.SHA256.New()
checksum := hash.Sum([]byte(pin))
var mk [24]byte
copy(mk[:], checksum[:24])
return &mk
}

// GetOrSetManagementKey returns the management key from the PIV metadata section.
// If it's not found, it derives the management key from the PIN, and will then:
// 1. create a new random management key
// 2. set it as the new management key
// 3. store it in the PIV metadata section
// 4. return the newly set management key
func GetOrSetManagementKey(yk *piv.YubiKey, pin string) (*[24]byte, error) {
var newManagementKey *[24]byte
metadata, err := yk.Metadata(pin)
if err != nil {
return nil, err
}
if metadata.ManagementKey == nil {
oldManagementKey := DeriveManagementKey(pin)
randomManagementKey, err := RandomManagementKey()
if err != nil {
return nil, errors.Wrap(err, "failed to generate random management key")
}

if err = yk.SetManagementKey(*oldManagementKey, *randomManagementKey); err != nil {
return nil, errors.Wrap(err, "set new management key")
}
if err = yk.SetMetadata(*randomManagementKey, &piv.Metadata{
ManagementKey: randomManagementKey,
}); err != nil {
return nil, errors.Wrap(err, "failed to store new management key")
}

newManagementKey = randomManagementKey
} else {
newManagementKey = metadata.ManagementKey
}
return newManagementKey, nil
}
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
Expand All @@ -60,6 +62,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
Expand Down

0 comments on commit 5adebcf

Please sign in to comment.