Skip to content

Commit 74dea0b

Browse files
authored
Merge pull request #631 from smallstep/mariano/mackms-tags
Allow to retry with no tags when loading privatekeys
2 parents 3a8464f + d01bccd commit 74dea0b

File tree

2 files changed

+87
-5
lines changed

2 files changed

+87
-5
lines changed

kms/mackms/mackms.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,28 @@ type keyAttributes struct {
5252
label string
5353
tag string
5454
hash []byte
55+
retry bool
5556
useSecureEnclave bool
5657
useBiometrics bool
5758
sigAlgorithm apiv1.SignatureAlgorithm
5859
keySize int
5960
}
6061

62+
// retryAttributes returns the original URI attributes used to get a private
63+
// key, but only if they are different that the ones set. It will return nil, if
64+
// they are the same. The only attribute that can change is the tag. This method
65+
// would return the tag empty if it was set using the default value.
66+
func (k *keyAttributes) retryAttributes() *keyAttributes {
67+
if !k.retry {
68+
return nil
69+
}
70+
return &keyAttributes{
71+
label: k.label,
72+
hash: k.hash,
73+
retry: false,
74+
}
75+
}
76+
6177
type keySearchAttributes struct {
6278
label string
6379
tag string
@@ -715,6 +731,12 @@ func getPrivateKey(u *keyAttributes) (*security.SecKeyRef, error) {
715731

716732
var key cf.TypeRef
717733
if err := security.SecItemCopyMatching(query, &key); err != nil {
734+
// If not found retry without the tag if it wasn't set.
735+
if errors.Is(err, security.ErrNotFound) {
736+
if ru := u.retryAttributes(); ru != nil {
737+
return getPrivateKey(ru)
738+
}
739+
}
718740
return nil, fmt.Errorf("macOS SecItemCopyMatching failed: %w", err)
719741
}
720742
return security.NewSecKeyRef(key), nil
@@ -990,6 +1012,7 @@ func parseURI(rawuri string) (*keyAttributes, error) {
9901012
return &keyAttributes{
9911013
label: rawuri,
9921014
tag: DefaultTag,
1015+
retry: true,
9931016
}, nil
9941017
}
9951018

@@ -1006,6 +1029,7 @@ func parseURI(rawuri string) (*keyAttributes, error) {
10061029
return &keyAttributes{
10071030
label: k,
10081031
tag: DefaultTag,
1032+
retry: true,
10091033
}, nil
10101034
}
10111035
}
@@ -1025,6 +1049,7 @@ func parseURI(rawuri string) (*keyAttributes, error) {
10251049
label: label,
10261050
tag: tag,
10271051
hash: u.GetEncoded("hash"),
1052+
retry: !u.Has("tag"),
10281053
useSecureEnclave: u.GetBool("se"),
10291054
useBiometrics: u.GetBool("bio"),
10301055
}, nil

kms/mackms/mackms_test.go

Lines changed: 62 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ func TestMacKMS_GetPublicKey(t *testing.T) {
307307

308308
// Create private keys only
309309
r2 := createPrivateKeyOnly(t, "mackms:label=test-ecdsa", apiv1.ECDSAWithSHA256)
310-
r3 := createPrivateKeyOnly(t, "mackms:label=test-rsa", apiv1.SHA256WithRSA)
310+
r3 := createPrivateKeyOnly(t, "mackms:label=test-rsa;tag=", apiv1.SHA256WithRSA)
311311

312312
t.Cleanup(func() {
313313
assert.NoError(t, kms.DeleteKey(&apiv1.DeleteKeyRequest{
@@ -334,7 +334,8 @@ func TestMacKMS_GetPublicKey(t *testing.T) {
334334
{"ok", &MacKMS{}, args{&apiv1.GetPublicKeyRequest{Name: r1.Name}}, r1.PublicKey, assert.NoError},
335335
{"ok no tag", &MacKMS{}, args{&apiv1.GetPublicKeyRequest{Name: "mackms:label=test-p256;tag="}}, r1.PublicKey, assert.NoError},
336336
{"ok private only ECDSA ", &MacKMS{}, args{&apiv1.GetPublicKeyRequest{Name: "mackms:label=test-ecdsa"}}, r2.PublicKey, assert.NoError},
337-
{"ok private only RSA ", &MacKMS{}, args{&apiv1.GetPublicKeyRequest{Name: r3.Name}}, r3.PublicKey, assert.NoError},
337+
{"ok private only RSA", &MacKMS{}, args{&apiv1.GetPublicKeyRequest{Name: r3.Name}}, r3.PublicKey, assert.NoError},
338+
{"ok private only RSA with retry", &MacKMS{}, args{&apiv1.GetPublicKeyRequest{Name: "mackms:label=test-rsa"}}, r3.PublicKey, assert.NoError},
338339
{"ok no uri", &MacKMS{}, args{&apiv1.GetPublicKeyRequest{Name: "test-p256"}}, r1.PublicKey, assert.NoError},
339340
{"ok uri simple", &MacKMS{}, args{&apiv1.GetPublicKeyRequest{Name: "mackms:test-p256"}}, r1.PublicKey, assert.NoError},
340341
{"ok uri label", &MacKMS{}, args{&apiv1.GetPublicKeyRequest{Name: "mackms:label=test-p256"}}, r1.PublicKey, assert.NoError},
@@ -541,11 +542,12 @@ func Test_parseURI(t *testing.T) {
541542
assertion assert.ErrorAssertionFunc
542543
}{
543544
{"ok", args{"mackms:label=the-label;tag=the-tag;hash=0102abcd"}, &keyAttributes{label: "the-label", tag: "the-tag", hash: []byte{1, 2, 171, 205}}, assert.NoError},
544-
{"ok label", args{"the-label"}, &keyAttributes{label: "the-label", tag: DefaultTag}, assert.NoError},
545-
{"ok label uri", args{"mackms:label=the-label"}, &keyAttributes{label: "the-label", tag: DefaultTag}, assert.NoError},
545+
{"ok label", args{"the-label"}, &keyAttributes{label: "the-label", tag: DefaultTag, retry: true}, assert.NoError},
546+
{"ok label uri", args{"mackms:label=the-label"}, &keyAttributes{label: "the-label", tag: DefaultTag, retry: true}, assert.NoError},
547+
{"ok label uri simple", args{"mackms:the-label"}, &keyAttributes{label: "the-label", tag: DefaultTag, retry: true}, assert.NoError},
546548
{"ok label empty tag", args{"mackms:label=the-label;tag="}, &keyAttributes{label: "the-label", tag: ""}, assert.NoError},
547549
{"ok label empty tag no equal", args{"mackms:label=the-label;tag"}, &keyAttributes{label: "the-label", tag: ""}, assert.NoError},
548-
{"fail parse", args{"mackms:::label=the-label"}, nil, assert.Error},
550+
{"fail parse", args{"mackms:%label=the-label"}, nil, assert.Error},
549551
{"fail missing label", args{"mackms:hash=0102abcd"}, nil, assert.Error},
550552
}
551553
for _, tt := range tests {
@@ -1306,3 +1308,58 @@ func TestMacKMS_SearchKeys(t *testing.T) {
13061308

13071309
assert.Equal(t, expectedHashes, hashes)
13081310
}
1311+
1312+
func Test_keyAttributes_retryAttributes(t *testing.T) {
1313+
type fields struct {
1314+
label string
1315+
tag string
1316+
hash []byte
1317+
retry bool
1318+
}
1319+
1320+
mustFields := func(s string) fields {
1321+
t.Helper()
1322+
u, err := parseURI(s)
1323+
require.NoError(t, err)
1324+
return fields{
1325+
label: u.label,
1326+
tag: u.tag,
1327+
hash: u.hash,
1328+
retry: u.retry,
1329+
}
1330+
}
1331+
1332+
tests := []struct {
1333+
name string
1334+
fields fields
1335+
want *keyAttributes
1336+
}{
1337+
{"with tag", mustFields("mackms:label=label;tag=tag"), nil},
1338+
{"with tag and hash", mustFields("mackms:label=label;hash=FF00;tag=tag"), nil},
1339+
{"with empty tag", mustFields("mackms:label=label;tag="), nil},
1340+
{"with no tag", mustFields("mackms:label=label;hash=FF00"), &keyAttributes{
1341+
label: "label",
1342+
hash: []byte{0xFF, 0x00},
1343+
}},
1344+
{"legacy name only", mustFields("label"), &keyAttributes{
1345+
label: "label",
1346+
}},
1347+
{"legacy with schema", mustFields("mackms:label"), &keyAttributes{
1348+
label: "label",
1349+
}},
1350+
}
1351+
for _, tt := range tests {
1352+
t.Run(tt.name, func(t *testing.T) {
1353+
k := &keyAttributes{
1354+
label: tt.fields.label,
1355+
tag: tt.fields.tag,
1356+
hash: tt.fields.hash,
1357+
retry: tt.fields.retry,
1358+
}
1359+
if tt.name == "with no tag" {
1360+
t.Log("foo")
1361+
}
1362+
assert.Equal(t, tt.want, k.retryAttributes())
1363+
})
1364+
}
1365+
}

0 commit comments

Comments
 (0)