Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add SearchKeys functionality to MacKMS #552

Merged
merged 5 commits into from
Jul 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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