Skip to content

Commit c4593f5

Browse files
authored
Merge pull request #552 from smallstep/herman/mackms-search
Add `SearchKeys` functionality to MacKMS
2 parents 4a2695a + 606fcd9 commit c4593f5

File tree

6 files changed

+373
-0
lines changed

6 files changed

+373
-0
lines changed

internal/darwin/corefoundation/core_foundation_darwin.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ const (
3333
nilCFData C.CFDataRef = 0
3434
nilCFString C.CFStringRef = 0
3535
nilCFDictionary C.CFDictionaryRef = 0
36+
nilCFArray C.CFArrayRef = 0
3637
nilCFError C.CFErrorRef = 0
3738
nilCFType C.CFTypeRef = 0
3839
)
@@ -45,10 +46,15 @@ func Release(ref TypeReferer) {
4546
C.CFRelease(ref.TypeRef())
4647
}
4748

49+
func Retain(ref TypeReferer) {
50+
C.CFRetain(ref.TypeRef())
51+
}
52+
4853
type CFTypeRef = C.CFTypeRef
4954
type CFStringRef = C.CFStringRef
5055
type CFErrorRef = C.CFErrorRef
5156
type CFDictionaryRef = C.CFDictionaryRef
57+
type CFArrayRef = C.CFArrayRef
5258
type CFDataRef = C.CFDataRef
5359

5460
type TypeRef C.CFTypeRef
@@ -167,6 +173,28 @@ func NewDictionaryRef(ref TypeRef) *DictionaryRef {
167173
func (v *DictionaryRef) Release() { Release(v) }
168174
func (v *DictionaryRef) TypeRef() CFTypeRef { return C.CFTypeRef(v.Value) }
169175

176+
type ArrayRef struct {
177+
Value C.CFArrayRef
178+
}
179+
180+
func NewArrayRef(ref TypeRef) *ArrayRef {
181+
return &ArrayRef{
182+
Value: C.CFArrayRef(ref),
183+
}
184+
}
185+
186+
func (v *ArrayRef) Release() { Release(v) }
187+
func (v *ArrayRef) TypeRef() CFTypeRef { return C.CFTypeRef(v.Value) }
188+
189+
func (v *ArrayRef) Len() int {
190+
return int(C.CFArrayGetCount(v.Value))
191+
}
192+
193+
func (v *ArrayRef) Get(index int) TypeRef {
194+
item := C.CFArrayGetValueAtIndex(v.Value, C.CFIndex(index))
195+
return TypeRef(item)
196+
}
197+
170198
//nolint:errname // type name matches original name
171199
type ErrorRef C.CFErrorRef
172200

internal/darwin/security/security_darwin.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,11 @@ var (
8080
KSecClassIdentity = cf.TypeRef(C.kSecClassIdentity)
8181
KSecMatchLimit = cf.TypeRef(C.kSecMatchLimit)
8282
KSecMatchLimitOne = cf.TypeRef(C.kSecMatchLimitOne)
83+
KSecMatchLimitAll = cf.TypeRef(C.kSecMatchLimitAll)
8384
KSecPublicKeyAttrs = cf.TypeRef(C.kSecPublicKeyAttrs)
8485
KSecPrivateKeyAttrs = cf.TypeRef(C.kSecPrivateKeyAttrs)
8586
KSecReturnRef = cf.TypeRef(C.kSecReturnRef)
87+
KSecReturnAttributes = cf.TypeRef(C.kSecReturnAttributes)
8688
KSecValueRef = cf.TypeRef(C.kSecValueRef)
8789
KSecValueData = cf.TypeRef(C.kSecValueData)
8890
)
@@ -138,6 +140,20 @@ const (
138140
KSecAccessControlOr = SecAccessControlCreateFlags(C.kSecAccessControlOr)
139141
)
140142

143+
type SecKeychainItemRef struct {
144+
Value C.SecKeychainItemRef
145+
}
146+
147+
func NewSecKeychainItemRef(ref cf.TypeRef) *SecKeychainItemRef {
148+
return &SecKeychainItemRef{
149+
Value: C.SecKeychainItemRef(ref),
150+
}
151+
}
152+
153+
func (v *SecKeychainItemRef) Release() { cf.Release(v) }
154+
func (v *SecKeychainItemRef) TypeRef() cf.CFTypeRef { return cf.CFTypeRef(v.Value) }
155+
func (v *SecKeychainItemRef) Retain() { cf.Retain(v) }
156+
141157
type SecKeyRef struct {
142158
Value C.SecKeyRef
143159
}
@@ -150,6 +166,7 @@ func NewSecKeyRef(ref cf.TypeRef) *SecKeyRef {
150166

151167
func (v *SecKeyRef) Release() { cf.Release(v) }
152168
func (v *SecKeyRef) TypeRef() cf.CFTypeRef { return cf.CFTypeRef(v.Value) }
169+
func (v *SecKeyRef) Retain() { cf.Retain(v) }
153170

154171
type SecCertificateRef struct {
155172
Value C.SecCertificateRef
@@ -309,6 +326,54 @@ func GetSecAttrApplicationLabel(v *cf.DictionaryRef) []byte {
309326
)
310327
}
311328

329+
func GetSecAttrApplicationTag(v *cf.DictionaryRef) string {
330+
data := C.CFDataRef(C.CFDictionaryGetValue(C.CFDictionaryRef(v.Value), unsafe.Pointer(C.kSecAttrApplicationTag)))
331+
return string(C.GoBytes(
332+
unsafe.Pointer(C.CFDataGetBytePtr(data)),
333+
C.int(C.CFDataGetLength(data)),
334+
))
335+
}
336+
337+
func GetSecAttrLabel(v *cf.DictionaryRef) (label string) {
338+
ref := C.CFStringRef(C.CFDictionaryGetValue(C.CFDictionaryRef(v.Value), unsafe.Pointer(C.kSecAttrLabel)))
339+
if cstr := C.CFStringGetCStringPtr(ref, C.kCFStringEncodingUTF8); cstr != nil {
340+
label = C.GoString(cstr)
341+
}
342+
return label
343+
}
344+
345+
func GetSecAttrTokenID(v *cf.DictionaryRef) (tokenID string) {
346+
ref := C.CFStringRef(C.CFDictionaryGetValue(C.CFDictionaryRef(v.Value), unsafe.Pointer(C.kSecAttrTokenID)))
347+
if cstr := C.CFStringGetCStringPtr(ref, C.kCFStringEncodingUTF8); cstr != nil {
348+
tokenID = C.GoString(cstr)
349+
}
350+
return tokenID
351+
}
352+
353+
func GetSecAttrAccessControl(v *cf.DictionaryRef) *SecAccessControlRef {
354+
var keyAttributes unsafe.Pointer
355+
tokenID := GetSecAttrTokenID(v)
356+
if tokenID == "com.apple.setoken" {
357+
keyAttributes = C.CFDictionaryGetValue(C.CFDictionaryRef(v.Value), unsafe.Pointer(C.kSecPrivateKeyAttrs))
358+
} else {
359+
keyAttributes = C.CFDictionaryGetValue(C.CFDictionaryRef(v.Value), unsafe.Pointer(C.kSecPublicKeyAttrs))
360+
}
361+
if keyAttributes == nil {
362+
return nil
363+
}
364+
365+
dv := C.CFDictionaryGetValue(C.CFDictionaryRef(keyAttributes), unsafe.Pointer(C.kSecAttrAccessControl))
366+
if dv == nil {
367+
return nil
368+
}
369+
370+
ref := &SecAccessControlRef{
371+
ref: C.SecAccessControlRef(dv),
372+
}
373+
374+
return ref
375+
}
376+
312377
func GetSecValueData(v *cf.DictionaryRef) []byte {
313378
data := C.CFDataRef(C.CFDictionaryGetValue(C.CFDictionaryRef(v.Value), unsafe.Pointer(C.kSecValueData)))
314379
return C.GoBytes(

kms/apiv1/options.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,18 @@ type KeyManager interface {
1818
Close() error
1919
}
2020

21+
// SearchableKeyManager is an optional interface for KMS implementations
22+
// that support searching for keys based on certain attributes.
23+
//
24+
// # Experimental
25+
//
26+
// Notice: This API is EXPERIMENTAL and may be changed or removed in a later
27+
// release.
28+
type SearchableKeyManager interface {
29+
KeyManager
30+
SearchKeys(req *SearchKeysRequest) (*SearchKeysResponse, error)
31+
}
32+
2133
// Decrypter is an interface implemented by KMSes that are used
2234
// in operations that require decryption
2335
type Decrypter interface {

kms/apiv1/requests.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,24 @@ type CreateKeyResponse struct {
178178
CreateSignerRequest CreateSignerRequest
179179
}
180180

181+
// SearchKeysRequest is the request for the SearchKeys method. It takes
182+
// a Query string with the attributes to match when searching the
183+
// KMS.
184+
type SearchKeysRequest struct {
185+
Query string
186+
}
187+
188+
// SearchKeyResult is a single result returned from the SearchKeys
189+
// method.
190+
type SearchKeyResult CreateKeyResponse
191+
192+
// SearchKeysResponse is the response for the SearchKeys method. It
193+
// wraps a slice of SearchKeyResult structs. The Results slice can
194+
// be empty in case no key was found for the search query.
195+
type SearchKeysResponse struct {
196+
Results []SearchKeyResult
197+
}
198+
181199
// CreateSignerRequest is the parameter used in the kms.CreateSigner method.
182200
type CreateSignerRequest struct {
183201
Signer crypto.Signer

0 commit comments

Comments
 (0)