Skip to content

Commit 6338f90

Browse files
authored
Merge pull request #884 from smallstep/herman/yubikey-support-aes-management-keys
Add support for AES128 and AES256 YubiKey management keys
2 parents 7b18d04 + 4180afc commit 6338f90

File tree

2 files changed

+42
-7
lines changed

2 files changed

+42
-7
lines changed

kms/yubikey/yubikey.go

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"fmt"
1414
"io"
1515
"net/url"
16+
"slices"
1617
"strconv"
1718
"strings"
1819
"sync"
@@ -164,6 +165,17 @@ func openCard(card string) (pivKey, error) {
164165
return yk, nil
165166
}
166167

168+
// validManagementKeyLengths contains the valid lengths
169+
// a YubiKey management key can have:
170+
// - 16 bytes for AES128
171+
// - 24 bytes for AES192 and DES3
172+
// - 32 bytes for AES256
173+
var validManagementKeyLengths = []int{16, 24, 32}
174+
175+
// maximumManagementKeyLength is the maximum length a
176+
// Yubikey management key can have.
177+
const maximumManagementKeyLength = 32
178+
167179
// New initializes a new YubiKey KMS.
168180
//
169181
// The most common way to open a YubiKey is to add a URI in the options:
@@ -189,7 +201,11 @@ func openCard(card string) (pivKey, error) {
189201
// ones.
190202
func New(_ context.Context, opts apiv1.Options) (*YubiKey, error) {
191203
pin := "123456"
192-
managementKey := piv.DefaultManagementKey
204+
var managementKey [maximumManagementKeyLength]byte
205+
206+
// set the default management key
207+
managementKeyLength := len(piv.DefaultManagementKey)
208+
copy(managementKey[:managementKeyLength], piv.DefaultManagementKey)
193209

194210
var serial string
195211
if opts.URI != "" {
@@ -222,10 +238,11 @@ func New(_ context.Context, opts apiv1.Options) (*YubiKey, error) {
222238
if err != nil {
223239
return nil, errors.Wrap(err, "error decoding management key")
224240
}
225-
if len(b) != 24 {
226-
return nil, errors.New("invalid managementKey: length is not 24 bytes")
241+
managementKeyLength = len(b)
242+
if !slices.Contains(validManagementKeyLengths, managementKeyLength) {
243+
return nil, fmt.Errorf("invalid management key length %d; expected 16, 24 or 32 bytes", managementKeyLength)
227244
}
228-
copy(managementKey, b[:24])
245+
copy(managementKey[:managementKeyLength], b[:managementKeyLength])
229246
}
230247

231248
if opts.Pin != "" {
@@ -266,7 +283,7 @@ func New(_ context.Context, opts apiv1.Options) (*YubiKey, error) {
266283
yk: yk,
267284
pin: pin,
268285
card: card,
269-
managementKey: managementKey,
286+
managementKey: managementKey[:managementKeyLength],
270287
}, nil
271288
}
272289

kms/yubikey/yubikey_test.go

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ func TestRegister(t *testing.T) {
294294
}
295295

296296
func TestNew(t *testing.T) {
297-
ctx := context.Background()
297+
ctx := t.Context()
298298
pOpen := pivOpen
299299
pCards := pivCards
300300
t.Cleanup(func() {
@@ -303,10 +303,14 @@ func TestNew(t *testing.T) {
303303
pivCards = pCards
304304
})
305305

306-
managementKey, err := randutil.Salt(24)
306+
managementKey, err := randutil.Salt(24) // DES3 and AES192
307307
require.NoError(t, err)
308308
managementKeyFile := filepath.Join(t.TempDir(), "management.key")
309309
require.NoError(t, os.WriteFile(managementKeyFile, []byte(hex.EncodeToString(managementKey)), 0600))
310+
aes128ManagementKey, err := randutil.Salt(16)
311+
require.NoError(t, err)
312+
aes256ManagementKey, err := randutil.Salt(32)
313+
require.NoError(t, err)
310314

311315
yk := newStubPivKey(t, ECDSA)
312316

@@ -376,6 +380,20 @@ func TestNew(t *testing.T) {
376380
pivCards = okPivCards
377381
pivOpen = okPivOpen
378382
}, &YubiKey{yk: yk, pin: "123456", card: "Yubico YubiKey OTP+FIDO+CCID", managementKey: managementKey}, false},
383+
{"ok with AES128 management key", args{ctx, apiv1.Options{
384+
URI: fmt.Sprintf("yubikey:management-key=%s?pin-value=123456", hex.EncodeToString(aes128ManagementKey)),
385+
}}, func() {
386+
pivMap = sync.Map{}
387+
pivCards = okPivCards
388+
pivOpen = okPivOpen
389+
}, &YubiKey{yk: yk, pin: "123456", card: "Yubico YubiKey OTP+FIDO+CCID", managementKey: aes128ManagementKey}, false},
390+
{"ok with AES256 management key", args{ctx, apiv1.Options{
391+
URI: fmt.Sprintf("yubikey:management-key=%s?pin-value=123456", hex.EncodeToString(aes256ManagementKey)),
392+
}}, func() {
393+
pivMap = sync.Map{}
394+
pivCards = okPivCards
395+
pivOpen = okPivOpen
396+
}, &YubiKey{yk: yk, pin: "123456", card: "Yubico YubiKey OTP+FIDO+CCID", managementKey: aes256ManagementKey}, false},
379397
{"ok with Pin", args{ctx, apiv1.Options{Pin: "222222"}}, func() {
380398
pivMap = sync.Map{}
381399
pivCards = okPivCards

0 commit comments

Comments
 (0)