Skip to content

Commit

Permalink
Merge pull request #552 from smallstep/herman/mackms-search
Browse files Browse the repository at this point in the history
Add `SearchKeys` functionality to MacKMS
  • Loading branch information
hslatman authored Jul 29, 2024
2 parents 4a2695a + 606fcd9 commit c4593f5
Show file tree
Hide file tree
Showing 6 changed files with 373 additions and 0 deletions.
28 changes: 28 additions & 0 deletions internal/darwin/corefoundation/core_foundation_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const (
nilCFData C.CFDataRef = 0
nilCFString C.CFStringRef = 0
nilCFDictionary C.CFDictionaryRef = 0
nilCFArray C.CFArrayRef = 0
nilCFError C.CFErrorRef = 0
nilCFType C.CFTypeRef = 0
)
Expand All @@ -45,10 +46,15 @@ func Release(ref TypeReferer) {
C.CFRelease(ref.TypeRef())
}

func Retain(ref TypeReferer) {
C.CFRetain(ref.TypeRef())
}

type CFTypeRef = C.CFTypeRef
type CFStringRef = C.CFStringRef
type CFErrorRef = C.CFErrorRef
type CFDictionaryRef = C.CFDictionaryRef
type CFArrayRef = C.CFArrayRef
type CFDataRef = C.CFDataRef

type TypeRef C.CFTypeRef
Expand Down Expand Up @@ -167,6 +173,28 @@ func NewDictionaryRef(ref TypeRef) *DictionaryRef {
func (v *DictionaryRef) Release() { Release(v) }
func (v *DictionaryRef) TypeRef() CFTypeRef { return C.CFTypeRef(v.Value) }

type ArrayRef struct {
Value C.CFArrayRef
}

func NewArrayRef(ref TypeRef) *ArrayRef {
return &ArrayRef{
Value: C.CFArrayRef(ref),
}
}

func (v *ArrayRef) Release() { Release(v) }
func (v *ArrayRef) TypeRef() CFTypeRef { return C.CFTypeRef(v.Value) }

func (v *ArrayRef) Len() int {
return int(C.CFArrayGetCount(v.Value))
}

func (v *ArrayRef) Get(index int) TypeRef {
item := C.CFArrayGetValueAtIndex(v.Value, C.CFIndex(index))
return TypeRef(item)
}

//nolint:errname // type name matches original name
type ErrorRef C.CFErrorRef

Expand Down
65 changes: 65 additions & 0 deletions internal/darwin/security/security_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,11 @@ var (
KSecClassIdentity = cf.TypeRef(C.kSecClassIdentity)
KSecMatchLimit = cf.TypeRef(C.kSecMatchLimit)
KSecMatchLimitOne = cf.TypeRef(C.kSecMatchLimitOne)
KSecMatchLimitAll = cf.TypeRef(C.kSecMatchLimitAll)
KSecPublicKeyAttrs = cf.TypeRef(C.kSecPublicKeyAttrs)
KSecPrivateKeyAttrs = cf.TypeRef(C.kSecPrivateKeyAttrs)
KSecReturnRef = cf.TypeRef(C.kSecReturnRef)
KSecReturnAttributes = cf.TypeRef(C.kSecReturnAttributes)
KSecValueRef = cf.TypeRef(C.kSecValueRef)
KSecValueData = cf.TypeRef(C.kSecValueData)
)
Expand Down Expand Up @@ -138,6 +140,20 @@ const (
KSecAccessControlOr = SecAccessControlCreateFlags(C.kSecAccessControlOr)
)

type SecKeychainItemRef struct {
Value C.SecKeychainItemRef
}

func NewSecKeychainItemRef(ref cf.TypeRef) *SecKeychainItemRef {
return &SecKeychainItemRef{
Value: C.SecKeychainItemRef(ref),
}
}

func (v *SecKeychainItemRef) Release() { cf.Release(v) }
func (v *SecKeychainItemRef) TypeRef() cf.CFTypeRef { return cf.CFTypeRef(v.Value) }
func (v *SecKeychainItemRef) Retain() { cf.Retain(v) }

type SecKeyRef struct {
Value C.SecKeyRef
}
Expand All @@ -150,6 +166,7 @@ func NewSecKeyRef(ref cf.TypeRef) *SecKeyRef {

func (v *SecKeyRef) Release() { cf.Release(v) }
func (v *SecKeyRef) TypeRef() cf.CFTypeRef { return cf.CFTypeRef(v.Value) }
func (v *SecKeyRef) Retain() { cf.Retain(v) }

type SecCertificateRef struct {
Value C.SecCertificateRef
Expand Down Expand Up @@ -309,6 +326,54 @@ func GetSecAttrApplicationLabel(v *cf.DictionaryRef) []byte {
)
}

func GetSecAttrApplicationTag(v *cf.DictionaryRef) string {
data := C.CFDataRef(C.CFDictionaryGetValue(C.CFDictionaryRef(v.Value), unsafe.Pointer(C.kSecAttrApplicationTag)))
return string(C.GoBytes(
unsafe.Pointer(C.CFDataGetBytePtr(data)),
C.int(C.CFDataGetLength(data)),
))
}

func GetSecAttrLabel(v *cf.DictionaryRef) (label string) {
ref := C.CFStringRef(C.CFDictionaryGetValue(C.CFDictionaryRef(v.Value), unsafe.Pointer(C.kSecAttrLabel)))
if cstr := C.CFStringGetCStringPtr(ref, C.kCFStringEncodingUTF8); cstr != nil {
label = C.GoString(cstr)
}
return label
}

func GetSecAttrTokenID(v *cf.DictionaryRef) (tokenID string) {
ref := C.CFStringRef(C.CFDictionaryGetValue(C.CFDictionaryRef(v.Value), unsafe.Pointer(C.kSecAttrTokenID)))
if cstr := C.CFStringGetCStringPtr(ref, C.kCFStringEncodingUTF8); cstr != nil {
tokenID = C.GoString(cstr)
}
return tokenID
}

func GetSecAttrAccessControl(v *cf.DictionaryRef) *SecAccessControlRef {
var keyAttributes unsafe.Pointer
tokenID := GetSecAttrTokenID(v)
if tokenID == "com.apple.setoken" {
keyAttributes = C.CFDictionaryGetValue(C.CFDictionaryRef(v.Value), unsafe.Pointer(C.kSecPrivateKeyAttrs))
} else {
keyAttributes = C.CFDictionaryGetValue(C.CFDictionaryRef(v.Value), unsafe.Pointer(C.kSecPublicKeyAttrs))
}
if keyAttributes == nil {
return nil
}

dv := C.CFDictionaryGetValue(C.CFDictionaryRef(keyAttributes), unsafe.Pointer(C.kSecAttrAccessControl))
if dv == nil {
return nil
}

ref := &SecAccessControlRef{
ref: C.SecAccessControlRef(dv),
}

return ref
}

func GetSecValueData(v *cf.DictionaryRef) []byte {
data := C.CFDataRef(C.CFDictionaryGetValue(C.CFDictionaryRef(v.Value), unsafe.Pointer(C.kSecValueData)))
return C.GoBytes(
Expand Down
12 changes: 12 additions & 0 deletions kms/apiv1/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,18 @@ type KeyManager interface {
Close() error
}

// SearchableKeyManager is an optional interface for KMS implementations
// that support searching for keys based on certain attributes.
//
// # Experimental
//
// Notice: This API is EXPERIMENTAL and may be changed or removed in a later
// release.
type SearchableKeyManager interface {
KeyManager
SearchKeys(req *SearchKeysRequest) (*SearchKeysResponse, error)
}

// Decrypter is an interface implemented by KMSes that are used
// in operations that require decryption
type Decrypter interface {
Expand Down
18 changes: 18 additions & 0 deletions kms/apiv1/requests.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,24 @@ type CreateKeyResponse struct {
CreateSignerRequest CreateSignerRequest
}

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

// 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 SearchKeyResult structs. The Results slice can
// be empty in case no key was found for the search query.
type SearchKeysResponse struct {
Results []SearchKeyResult
}

// CreateSignerRequest is the parameter used in the kms.CreateSigner method.
type CreateSignerRequest struct {
Signer crypto.Signer
Expand Down
Loading

0 comments on commit c4593f5

Please sign in to comment.