Skip to content

Commit

Permalink
Return empty slice when no keys are found for the SearchKeys query
Browse files Browse the repository at this point in the history
  • Loading branch information
hslatman committed Jul 26, 2024
1 parent 9c3c6e2 commit 4baf2ce
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 12 deletions.
7 changes: 4 additions & 3 deletions kms/apiv1/requests.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,18 +179,19 @@ type CreateKeyResponse struct {
}

// SearchKeysRequest is the request for the SearchKeys method. It takes
// a single Query string with the attributes to match when searching the
// a Query string with the attributes to match when searching the
// KMS.
type SearchKeysRequest struct {
Query string
}

// SearchKeyResult is a single results returned from the SearchKeys
// SearchKeyResult is a single result returned from the SearchKeys
// method.
type SearchKeyResult CreateKeyResponse

// SearchKeysResponse is the response for the SearchKeys method. It
// wraps a slice of SearchKeyResponse structs.
// wraps a slice of SearchKeyResult structs. The Results slice can
// be empty in case no key was found for the search query.
type SearchKeysResponse struct {
Results []SearchKeyResult
}
Expand Down
29 changes: 21 additions & 8 deletions kms/mackms/mackms.go
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,18 @@ func (*MacKMS) DeleteCertificate(req *apiv1.DeleteCertificateRequest) error {
return nil
}

// SearchKeys searches for keys according to the query URI in the request.
//
// - "" will return all keys managed by the KMS (using the default tag)
// - "mackms:" will return all keys managed by the KMS (using the default tag)
// - "mackms:label=my-label" will return all keys using label "my-label" (and the default tag)
// - "mackms:hash=the-hash" will return all keys having hash "hash" (and the default tag; generally one result)
// - "mackms:tag=my-tag" will search for all keys with "my-tag"
//
// # Experimental
//
// Notice: This API is EXPERIMENTAL and may be changed or removed in a later
// release.
func (k *MacKMS) SearchKeys(req *apiv1.SearchKeysRequest) (*apiv1.SearchKeysResponse, error) {
if req.Query == "" {
return nil, fmt.Errorf("searchKeysRequest 'query' cannot be empty")
Expand Down Expand Up @@ -586,7 +598,7 @@ func (k *MacKMS) SearchKeys(req *apiv1.SearchKeysRequest) (*apiv1.SearchKeysResp
}

// obtain the public key by requesting it, as the current
// representation of the key are just the attributes.
// representation of the key includes just the attributes.
pub, err := k.GetPublicKey(&apiv1.GetPublicKeyRequest{
Name: name.String(),
})
Expand Down Expand Up @@ -724,7 +736,10 @@ func getPrivateKeys(u *keyAttributes) ([]*security.SecKeychainItemRef, error) {
var result cf.TypeRef
err = security.SecItemCopyMatching(query, &result)
if err != nil {
return nil, fmt.Errorf("failed matching: %w", err)
if errors.Is(err, security.ErrNotFound) {
return []*security.SecKeychainItemRef{}, nil
}
return nil, fmt.Errorf("macOS SecItemCopyMatching failed: %w", err)
}

array := cf.NewArrayRef(result)
Expand Down Expand Up @@ -1041,7 +1056,7 @@ func parseSearchURI(rawuri string) (*keyAttributes, error) {
}

// When rawuri is a mackms uri.
u, err := uri.ParseWithScheme(Scheme, rawuri)
u, err := uri.Parse(rawuri)
if err != nil {
return nil, err
}
Expand All @@ -1066,11 +1081,9 @@ func parseSearchURI(rawuri string) (*keyAttributes, error) {
tag = DefaultTag
}
return &keyAttributes{
label: label,
tag: tag,
hash: u.GetEncoded("hash"),
useSecureEnclave: u.GetBool("se"),
useBiometrics: u.GetBool("bio"),
label: label,
tag: tag,
hash: u.GetEncoded("hash"),
}, nil
}

Expand Down
10 changes: 9 additions & 1 deletion kms/mackms/mackms_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1239,7 +1239,15 @@ func TestMacKMS_SearchKeys(t *testing.T) {
require.NoError(t, err)
tag := fmt.Sprintf("com.smallstep.crypto.test.%s", name) // unique tag per test execution

// initialize MacKMS
k := &MacKMS{}

// search by tag; expect 0 keys before the test
got, err := k.SearchKeys(&apiv1.SearchKeysRequest{Query: fmt.Sprintf("mackms:tag=%s", tag)})
require.NoError(t, err)
require.NotNil(t, got)
require.Len(t, got.Results, 0)

key1, err := k.CreateKey(&apiv1.CreateKeyRequest{Name: fmt.Sprintf("mackms:name=test-step-1;label=test-step-1;tag=%s;se=false", tag)})
require.NoError(t, err)
key2, err := k.CreateKey(&apiv1.CreateKeyRequest{Name: fmt.Sprintf("mackms:name=test-step-2;label=test-step-2;tag=%s;se=false", tag)})
Expand All @@ -1258,7 +1266,7 @@ func TestMacKMS_SearchKeys(t *testing.T) {
})

// search by tag
got, err := k.SearchKeys(&apiv1.SearchKeysRequest{Query: fmt.Sprintf("mackms:tag=%s", tag)})
got, err = k.SearchKeys(&apiv1.SearchKeysRequest{Query: fmt.Sprintf("mackms:tag=%s", tag)})
require.NoError(t, err)
require.NotNil(t, got)
require.Len(t, got.Results, 2)
Expand Down

0 comments on commit 4baf2ce

Please sign in to comment.