Skip to content

Commit

Permalink
Merge pull request #109 from smlx/add-gpg-encrypt-slots
Browse files Browse the repository at this point in the history
feat: multiple decrypt slots for multiple touch policies
  • Loading branch information
smlx authored Apr 10, 2022
2 parents ca4123a + 61904dd commit a2d7c1f
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 31 deletions.
6 changes: 3 additions & 3 deletions cmd/piv-agent/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ type SetupCmd struct {
Card string `kong:"help='Specify a smart card device'"`
ResetSecurityKey bool `kong:"help='Overwrite any existing keys'"`
PIN uint64 `kong:"help='Set the PIN/PUK of the device (6-8 digits). Will be prompted interactively if not provided.'"`
SigningKeys []string `kong:"default='cached,always,never',enum='cached,always,never',help='Generate signing keys with various touch policies (cached,always,never)'"`
DecryptingKey bool `kong:"default='true',help='Generate a decrypting key (default true)'"`
SigningKeys []string `kong:"default='cached,always,never',enum='cached,always,never',help='Generate signing keys with various touch policies (default cached,always,never)'"`
DecryptingKeys []string `kong:"default='cached,always,never',enum='cached,always,never',help='Generate a decrypting key (default cached,always,never)'"`
}

func interactivePIN() (uint64, error) {
Expand Down Expand Up @@ -61,7 +61,7 @@ func (cmd *SetupCmd) Run() error {
return fmt.Errorf("couldn't get security key: %v", err)
}
err = k.Setup(strconv.FormatUint(cmd.PIN, 10), version,
cmd.ResetSecurityKey, cmd.SigningKeys, cmd.DecryptingKey)
cmd.ResetSecurityKey, cmd.SigningKeys, cmd.DecryptingKeys)
if errors.Is(err, securitykey.ErrKeySetUp) {
return fmt.Errorf("--reset-security-key not specified: %w", err)
}
Expand Down
12 changes: 6 additions & 6 deletions cmd/piv-agent/setupslots.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ import (

// SetupSlotsCmd represents the setup command.
type SetupSlotsCmd struct {
Card string `kong:"help='Specify a smart card device'"`
ResetSlots bool `kong:"help='Overwrite existing keys in the targeted slots'"`
PIN uint64 `kong:"help='The PIN/PUK of the device (6-8 digits). Will be prompted interactively if not provided.'"`
SigningKeys []string `kong:"required,enum='cached,always,never',help='Set up slots for signing keys with various touch policies (possible values cached,always,never)'"`
DecryptingKey bool `kong:"default='false',help='Set up slot for a decrypting key (default false)'"`
Card string `kong:"help='Specify a smart card device'"`
ResetSlots bool `kong:"help='Overwrite existing keys in the targeted slots'"`
PIN uint64 `kong:"help='The PIN/PUK of the device (6-8 digits). Will be prompted interactively if not provided.'"`
SigningKeys []string `kong:"required,enum='cached,always,never',help='Set up slots for signing keys with various touch policies (possible values cached,always,never)'"`
DecryptingKeys []string `kong:"required,enum='cached,always,never',help='Set up slot for a decrypting key (possible values cached,always,never)'"`
}

// Run the setup-slot command to configure a slot on a security key.
Expand All @@ -35,7 +35,7 @@ func (cmd *SetupSlotsCmd) Run() error {
return fmt.Errorf("couldn't get security key: %v", err)
}
err = k.SetupSlots(strconv.FormatUint(cmd.PIN, 10), version, cmd.ResetSlots,
cmd.SigningKeys, cmd.DecryptingKey)
cmd.SigningKeys, cmd.DecryptingKeys)
if errors.Is(err, securitykey.ErrKeySetUp) {
return fmt.Errorf("--reset-slots not specified: %w", err)
}
Expand Down
29 changes: 15 additions & 14 deletions internal/securitykey/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ import (
// and reset is false.
var ErrKeySetUp = errors.New("security key already set up")

// checkSlotSetUp checks if the provided slot is set up, returning true if the slot
// is set up and false otherwise.
// checkSlotSetUp checks if the provided slot is set up, returning true if the
// slot is set up and false otherwise.
func (k *SecurityKey) checkSlotSetUp(s SlotSpec) (bool, error) {
_, err := k.yubikey.Certificate(s.Slot)
if err == nil {
Expand All @@ -30,10 +30,10 @@ func (k *SecurityKey) checkSlotSetUp(s SlotSpec) (bool, error) {
return false, fmt.Errorf("couldn't check slot certificate: %v", err)
}

// checkSlotsSetUp checks if the provided slots are set up returning true if any of
// the slots are set up, and false otherwise.
// checkSlotsSetUp checks if the provided slots are set up returning true if
// any of the slots are set up, and false otherwise.
func (k *SecurityKey) checkSlotsSetUp(signingKeys []string,
decryptingKey bool) (bool, error) {
decryptingKeys []string) (bool, error) {
for _, p := range signingKeys {
setUp, err := k.checkSlotSetUp(defaultSignSlots[p])
if err != nil {
Expand All @@ -43,8 +43,8 @@ func (k *SecurityKey) checkSlotsSetUp(signingKeys []string,
return true, nil
}
}
if decryptingKey {
setUp, err := k.checkSlotSetUp(defaultDecryptSlots["never"])
for _, p := range decryptingKeys {
setUp, err := k.checkSlotSetUp(defaultDecryptSlots[p])
if err != nil {
return false, err
}
Expand All @@ -57,10 +57,10 @@ func (k *SecurityKey) checkSlotsSetUp(signingKeys []string,

// Setup configures the SecurityKey to work with piv-agent.
func (k *SecurityKey) Setup(pin, version string, reset bool,
signingKeys []string, decryptingKey bool) error {
signingKeys []string, decryptingKeys []string) error {
var err error
if !reset {
setUp, err := k.checkSlotsSetUp(signingKeys, decryptingKey)
setUp, err := k.checkSlotsSetUp(signingKeys, decryptingKeys)
if err != nil {
return fmt.Errorf("couldn't check slots: %v", err)
}
Expand Down Expand Up @@ -96,15 +96,16 @@ func (k *SecurityKey) Setup(pin, version string, reset bool,
for _, p := range signingKeys {
err := k.configureSlot(mgmtKey, defaultSignSlots[p], version)
if err != nil {
return fmt.Errorf("couldn't configure slot %v: %v", defaultSignSlots[p], err)
return fmt.Errorf("couldn't configure slot %v: %v",
defaultSignSlots[p], err)
}
}
// setup decrypt key
if decryptingKey {
err := k.configureSlot(mgmtKey, defaultDecryptSlots["never"], version)
// setup decrypt keys
for _, p := range decryptingKeys {
err := k.configureSlot(mgmtKey, defaultDecryptSlots[p], version)
if err != nil {
return fmt.Errorf("couldn't configure slot %v: %v",
defaultDecryptSlots["cached"], err)
defaultDecryptSlots[p], err)
}
}
return nil
Expand Down
12 changes: 6 additions & 6 deletions internal/securitykey/setupslots.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import "fmt"
// SetupSlots configures slots on the security key without resetting it
// completely.
func (k *SecurityKey) SetupSlots(pin, version string, reset bool,
signingKeys []string, decryptingKey bool) error {
signingKeys []string, decryptingKeys []string) error {
var err error
if !reset {
setUp, err := k.checkSlotsSetUp(signingKeys, decryptingKey)
setUp, err := k.checkSlotsSetUp(signingKeys, decryptingKeys)
if err != nil {
return fmt.Errorf("couldn't check slots: %v", err)
}
Expand All @@ -31,12 +31,12 @@ func (k *SecurityKey) SetupSlots(pin, version string, reset bool,
}
}
// setup decrypt key
if decryptingKey {
err := k.configureSlot(*metadata.ManagementKey,
defaultDecryptSlots["never"], version)
for _, p := range decryptingKeys {
err := k.configureSlot(*metadata.ManagementKey, defaultDecryptSlots[p],
version)
if err != nil {
return fmt.Errorf("couldn't configure slot %v: %v",
defaultDecryptSlots["cached"], err)
defaultDecryptSlots[p], err)
}
}
return nil
Expand Down
12 changes: 10 additions & 2 deletions internal/securitykey/slotspec.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,12 @@ var defaultSignSlots = map[string]SlotSpec{
"never": {piv.SlotCardAuthentication, piv.TouchPolicyNever},
}

var alwaysDecryptSlot, _ = piv.RetiredKeyManagementSlot(0x82)
var neverDecryptSlot, _ = piv.RetiredKeyManagementSlot(0x83)

// defaultDecryptSlots represents the slot specifications for decrypting
// operations.
// operations. By using additional "retired" slots we can enable multiple touch
// policies for decrypt.
var defaultDecryptSlots = map[string]SlotSpec{
// Slot 9d: Key Management
// This certificate and its associated private key is used for encryption for
Expand All @@ -45,5 +49,9 @@ var defaultDecryptSlots = map[string]SlotSpec{
// private key operations. Once the PIN has been provided successfully,
// multiple private key operations may be performed without additional
// cardholder consent.
"never": {piv.SlotKeyManagement, piv.TouchPolicyNever},
"cached": {piv.SlotKeyManagement, piv.TouchPolicyCached},
// "Retired" key management slot with an "always" touch policy.
"always": {alwaysDecryptSlot, piv.TouchPolicyAlways},
// "Retired" key management slot with a "never" touch policy.
"never": {neverDecryptSlot, piv.TouchPolicyNever},
}

0 comments on commit a2d7c1f

Please sign in to comment.