summaryrefslogtreecommitdiff
path: root/vendor/github.com/99designs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/99designs')
-rw-r--r--vendor/github.com/99designs/go-keychain/.gitignore24
-rw-r--r--vendor/github.com/99designs/go-keychain/.golangci.yml11
-rw-r--r--vendor/github.com/99designs/go-keychain/.travis.yml20
-rw-r--r--vendor/github.com/99designs/go-keychain/LICENSE22
-rw-r--r--vendor/github.com/99designs/go-keychain/README.md159
-rw-r--r--vendor/github.com/99designs/go-keychain/corefoundation.go359
-rw-r--r--vendor/github.com/99designs/go-keychain/datetime.go68
-rw-r--r--vendor/github.com/99designs/go-keychain/ios.go22
-rw-r--r--vendor/github.com/99designs/go-keychain/keychain.go531
-rw-r--r--vendor/github.com/99designs/go-keychain/macos.go272
-rw-r--r--vendor/github.com/99designs/go-keychain/util.go31
-rw-r--r--vendor/github.com/99designs/keyring/.gitattributes1
-rw-r--r--vendor/github.com/99designs/keyring/.gitignore1
-rw-r--r--vendor/github.com/99designs/keyring/.golangci.yml29
-rw-r--r--vendor/github.com/99designs/keyring/LICENSE22
-rw-r--r--vendor/github.com/99designs/keyring/README.md67
-rw-r--r--vendor/github.com/99designs/keyring/Vagrantfile85
-rw-r--r--vendor/github.com/99designs/keyring/array.go54
-rw-r--r--vendor/github.com/99designs/keyring/config.go58
-rw-r--r--vendor/github.com/99designs/keyring/docker-compose.yml7
-rw-r--r--vendor/github.com/99designs/keyring/file.go180
-rw-r--r--vendor/github.com/99designs/keyring/keychain.go301
-rw-r--r--vendor/github.com/99designs/keyring/keyctl.go327
-rw-r--r--vendor/github.com/99designs/keyring/keyring.go134
-rw-r--r--vendor/github.com/99designs/keyring/kwallet.go237
-rw-r--r--vendor/github.com/99designs/keyring/pass.go166
-rw-r--r--vendor/github.com/99designs/keyring/prompt.go27
-rw-r--r--vendor/github.com/99designs/keyring/secretservice.go293
-rw-r--r--vendor/github.com/99designs/keyring/tilde.go22
-rw-r--r--vendor/github.com/99designs/keyring/wincred.go98
30 files changed, 3628 insertions, 0 deletions
diff --git a/vendor/github.com/99designs/go-keychain/.gitignore b/vendor/github.com/99designs/go-keychain/.gitignore
new file mode 100644
index 0000000..daf913b
--- /dev/null
+++ b/vendor/github.com/99designs/go-keychain/.gitignore
@@ -0,0 +1,24 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
+*.prof
diff --git a/vendor/github.com/99designs/go-keychain/.golangci.yml b/vendor/github.com/99designs/go-keychain/.golangci.yml
new file mode 100644
index 0000000..23aaf43
--- /dev/null
+++ b/vendor/github.com/99designs/go-keychain/.golangci.yml
@@ -0,0 +1,11 @@
+linters-settings:
+ gocritic:
+ disabled-checks:
+ - ifElseChain
+ - elseif
+
+linters:
+ enable:
+ - gofmt
+ - gocritic
+ - unconvert
diff --git a/vendor/github.com/99designs/go-keychain/.travis.yml b/vendor/github.com/99designs/go-keychain/.travis.yml
new file mode 100644
index 0000000..2fba239
--- /dev/null
+++ b/vendor/github.com/99designs/go-keychain/.travis.yml
@@ -0,0 +1,20 @@
+language: go
+
+os:
+ - osx
+ - linux
+
+before_install:
+ - go get golang.org/x/lint/golint
+
+script:
+ - go vet ./...
+ - golint ./...
+ - curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(go env GOPATH)/bin v1.16.0
+ - golangci-lint run
+ - go test -tags skipsecretserviceintegrationtests ./...
+
+go:
+ - 1.10.x
+ - 1.11.x
+ - 1.12.x
diff --git a/vendor/github.com/99designs/go-keychain/LICENSE b/vendor/github.com/99designs/go-keychain/LICENSE
new file mode 100644
index 0000000..2d54c65
--- /dev/null
+++ b/vendor/github.com/99designs/go-keychain/LICENSE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Keybase
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
diff --git a/vendor/github.com/99designs/go-keychain/README.md b/vendor/github.com/99designs/go-keychain/README.md
new file mode 100644
index 0000000..4a9eeb2
--- /dev/null
+++ b/vendor/github.com/99designs/go-keychain/README.md
@@ -0,0 +1,159 @@
+# Go Keychain
+
+[![Travis CI](https://travis-ci.org/keybase/go-keychain.svg?branch=master)](https://travis-ci.org/keybase/go-keychain)
+
+A library for accessing the Keychain for macOS, iOS, and Linux in Go (golang).
+
+Requires macOS 10.9 or greater and iOS 8 or greater. On Linux, communicates to
+a provider of the DBUS SecretService spec like gnome-keyring or ksecretservice.
+
+```go
+import "github.com/keybase/go-keychain"
+```
+
+
+## Mac/iOS Usage
+
+The API is meant to mirror the macOS/iOS Keychain API and is not necessarily idiomatic go.
+
+#### Add Item
+
+```go
+item := keychain.NewItem()
+item.SetSecClass(keychain.SecClassGenericPassword)
+item.SetService("MyService")
+item.SetAccount("gabriel")
+item.SetLabel("A label")
+item.SetAccessGroup("A123456789.group.com.mycorp")
+item.SetData([]byte("toomanysecrets"))
+item.SetSynchronizable(keychain.SynchronizableNo)
+item.SetAccessible(keychain.AccessibleWhenUnlocked)
+err := keychain.AddItem(item)
+
+if err == keychain.ErrorDuplicateItem {
+ // Duplicate
+}
+```
+
+#### Query Item
+
+Query for multiple results, returning attributes:
+
+```go
+query := keychain.NewItem()
+query.SetSecClass(keychain.SecClassGenericPassword)
+query.SetService(service)
+query.SetAccount(account)
+query.SetAccessGroup(accessGroup)
+query.SetMatchLimit(keychain.MatchLimitAll)
+query.SetReturnAttributes(true)
+results, err := keychain.QueryItem(query)
+if err != nil {
+ // Error
+} else {
+ for _, r := range results {
+ fmt.Printf("%#v\n", r)
+ }
+}
+```
+
+Query for a single result, returning data:
+
+```go
+query := keychain.NewItem()
+query.SetSecClass(keychain.SecClassGenericPassword)
+query.SetService(service)
+query.SetAccount(account)
+query.SetAccessGroup(accessGroup)
+query.SetMatchLimit(keychain.MatchLimitOne)
+query.SetReturnData(true)
+results, err := keychain.QueryItem(query)
+if err != nil {
+ // Error
+} else if len(results) != 1 {
+ // Not found
+} else {
+ password := string(results[0].Data)
+}
+```
+
+#### Delete Item
+
+Delete a generic password item with service and account:
+
+```go
+item := keychain.NewItem()
+item.SetSecClass(keychain.SecClassGenericPassword)
+item.SetService(service)
+item.SetAccount(account)
+err := keychain.DeleteItem(item)
+```
+
+### Other
+
+There are some convenience methods for generic password:
+
+```go
+// Create generic password item with service, account, label, password, access group
+item := keychain.NewGenericPassword("MyService", "gabriel", "A label", []byte("toomanysecrets"), "A123456789.group.com.mycorp")
+item.SetSynchronizable(keychain.SynchronizableNo)
+item.SetAccessible(keychain.AccessibleWhenUnlocked)
+err := keychain.AddItem(item)
+if err == keychain.ErrorDuplicateItem {
+ // Duplicate
+}
+
+accounts, err := keychain.GetGenericPasswordAccounts("MyService")
+// Should have 1 account == "gabriel"
+
+err := keychain.DeleteGenericPasswordItem("MyService", "gabriel")
+if err == keychain.ErrorNotFound {
+ // Not found
+}
+```
+
+### OS X
+
+Creating a new keychain and add an item to it:
+
+```go
+
+// Add a new key chain into ~/Application Support/Keychains, with the provided password
+k, err := keychain.NewKeychain("mykeychain.keychain", "my keychain password")
+if err != nil {
+ // Error creating
+}
+
+// Create generic password item with service, account, label, password, access group
+item := keychain.NewGenericPassword("MyService", "gabriel", "A label", []byte("toomanysecrets"), "A123456789.group.com.mycorp")
+item.UseKeychain(k)
+err := keychain.AddItem(item)
+if err != nil {
+ // Error creating
+}
+```
+
+Using a Keychain at path:
+
+```go
+k, err := keychain.NewWithPath("mykeychain.keychain")
+```
+
+Set a trusted applications for item (OS X only):
+
+```go
+item := keychain.NewGenericPassword("MyService", "gabriel", "A label", []byte("toomanysecrets"), "A123456789.group.com.mycorp")
+trustedApplications := []string{"/Applications/Mail.app"}
+item.SetAccess(&keychain.Access{Label: "Mail", TrustedApplications: trustedApplications})
+err := keychain.AddItem(item)
+```
+
+## iOS
+
+Bindable package in `bind`. iOS project in `ios`. Run that project to test iOS.
+
+To re-generate framework:
+
+```
+(cd bind && gomobile bind -target=ios -tags=ios -o ../ios/bind.framework)
+```
diff --git a/vendor/github.com/99designs/go-keychain/corefoundation.go b/vendor/github.com/99designs/go-keychain/corefoundation.go
new file mode 100644
index 0000000..c45ee7a
--- /dev/null
+++ b/vendor/github.com/99designs/go-keychain/corefoundation.go
@@ -0,0 +1,359 @@
+// +build darwin ios
+
+package keychain
+
+/*
+#cgo LDFLAGS: -framework CoreFoundation
+
+#include <CoreFoundation/CoreFoundation.h>
+
+// Can't cast a *uintptr to *unsafe.Pointer in Go, and casting
+// C.CFTypeRef to unsafe.Pointer is unsafe in Go, so have shim functions to
+// do the casting in C (where it's safe).
+
+// We add a suffix to the C functions below, because we copied this
+// file from go-kext, which means that any project that depends on this
+// package and go-kext would run into duplicate symbol errors otherwise.
+//
+// TODO: Move this file into its own package depended on by go-kext
+// and this package.
+
+CFDictionaryRef CFDictionaryCreateSafe2(CFAllocatorRef allocator, const uintptr_t *keys, const uintptr_t *values, CFIndex numValues, const CFDictionaryKeyCallBacks *keyCallBacks, const CFDictionaryValueCallBacks *valueCallBacks) {
+ return CFDictionaryCreate(allocator, (const void **)keys, (const void **)values, numValues, keyCallBacks, valueCallBacks);
+}
+
+CFArrayRef CFArrayCreateSafe2(CFAllocatorRef allocator, const uintptr_t *values, CFIndex numValues, const CFArrayCallBacks *callBacks) {
+ return CFArrayCreate(allocator, (const void **)values, numValues, callBacks);
+}
+*/
+import "C"
+import (
+ "errors"
+ "fmt"
+ "math"
+ "reflect"
+ "unicode/utf8"
+ "unsafe"
+)
+
+// Release releases memory pointed to by a CFTypeRef.
+func Release(ref C.CFTypeRef) {
+ C.CFRelease(ref)
+}
+
+// BytesToCFData will return a CFDataRef and if non-nil, must be released with
+// Release(ref).
+func BytesToCFData(b []byte) (C.CFDataRef, error) {
+ if uint64(len(b)) > math.MaxUint32 {
+ return 0, errors.New("Data is too large")
+ }
+ var p *C.UInt8
+ if len(b) > 0 {
+ p = (*C.UInt8)(&b[0])
+ }
+ cfData := C.CFDataCreate(C.kCFAllocatorDefault, p, C.CFIndex(len(b)))
+ if cfData == 0 {
+ return 0, fmt.Errorf("CFDataCreate failed")
+ }
+ return cfData, nil
+}
+
+// CFDataToBytes converts CFData to bytes.
+func CFDataToBytes(cfData C.CFDataRef) ([]byte, error) {
+ return C.GoBytes(unsafe.Pointer(C.CFDataGetBytePtr(cfData)), C.int(C.CFDataGetLength(cfData))), nil
+}
+
+// MapToCFDictionary will return a CFDictionaryRef and if non-nil, must be
+// released with Release(ref).
+func MapToCFDictionary(m map[C.CFTypeRef]C.CFTypeRef) (C.CFDictionaryRef, error) {
+ var keys, values []C.uintptr_t
+ for key, value := range m {
+ keys = append(keys, C.uintptr_t(key))
+ values = append(values, C.uintptr_t(value))
+ }
+ numValues := len(values)
+ var keysPointer, valuesPointer *C.uintptr_t
+ if numValues > 0 {
+ keysPointer = &keys[0]
+ valuesPointer = &values[0]
+ }
+ cfDict := C.CFDictionaryCreateSafe2(C.kCFAllocatorDefault, keysPointer, valuesPointer, C.CFIndex(numValues),
+ &C.kCFTypeDictionaryKeyCallBacks, &C.kCFTypeDictionaryValueCallBacks) //nolint
+ if cfDict == 0 {
+ return 0, fmt.Errorf("CFDictionaryCreate failed")
+ }
+ return cfDict, nil
+}
+
+// CFDictionaryToMap converts CFDictionaryRef to a map.
+func CFDictionaryToMap(cfDict C.CFDictionaryRef) (m map[C.CFTypeRef]C.CFTypeRef) {
+ count := C.CFDictionaryGetCount(cfDict)
+ if count > 0 {
+ keys := make([]C.CFTypeRef, count)
+ values := make([]C.CFTypeRef, count)
+ C.CFDictionaryGetKeysAndValues(cfDict, (*unsafe.Pointer)(unsafe.Pointer(&keys[0])), (*unsafe.Pointer)(unsafe.Pointer(&values[0])))
+ m = make(map[C.CFTypeRef]C.CFTypeRef, count)
+ for i := C.CFIndex(0); i < count; i++ {
+ m[keys[i]] = values[i]
+ }
+ }
+ return
+}
+
+// StringToCFString will return a CFStringRef and if non-nil, must be released with
+// Release(ref).
+func StringToCFString(s string) (C.CFStringRef, error) {
+ if !utf8.ValidString(s) {
+ return 0, errors.New("Invalid UTF-8 string")
+ }
+ if uint64(len(s)) > math.MaxUint32 {
+ return 0, errors.New("String is too large")
+ }
+
+ bytes := []byte(s)
+ var p *C.UInt8
+ if len(bytes) > 0 {
+ p = (*C.UInt8)(&bytes[0])
+ }
+ return C.CFStringCreateWithBytes(C.kCFAllocatorDefault, p, C.CFIndex(len(s)), C.kCFStringEncodingUTF8, C.false), nil
+}
+
+// CFStringToString converts a CFStringRef to a string.
+func CFStringToString(s C.CFStringRef) string {
+ p := C.CFStringGetCStringPtr(s, C.kCFStringEncodingUTF8)
+ if p != nil {
+ return C.GoString(p)
+ }
+ length := C.CFStringGetLength(s)
+ if length == 0 {
+ return ""
+ }
+ maxBufLen := C.CFStringGetMaximumSizeForEncoding(length, C.kCFStringEncodingUTF8)
+ if maxBufLen == 0 {
+ return ""
+ }
+ buf := make([]byte, maxBufLen)
+ var usedBufLen C.CFIndex
+ _ = C.CFStringGetBytes(s, C.CFRange{0, length}, C.kCFStringEncodingUTF8, C.UInt8(0), C.false, (*C.UInt8)(&buf[0]), maxBufLen, &usedBufLen)
+ return string(buf[:usedBufLen])
+}
+
+// ArrayToCFArray will return a CFArrayRef and if non-nil, must be released with
+// Release(ref).
+func ArrayToCFArray(a []C.CFTypeRef) C.CFArrayRef {
+ var values []C.uintptr_t
+ for _, value := range a {
+ values = append(values, C.uintptr_t(value))
+ }
+ numValues := len(values)
+ var valuesPointer *C.uintptr_t
+ if numValues > 0 {
+ valuesPointer = &values[0]
+ }
+ return C.CFArrayCreateSafe2(C.kCFAllocatorDefault, valuesPointer, C.CFIndex(numValues), &C.kCFTypeArrayCallBacks) //nolint
+}
+
+// CFArrayToArray converts a CFArrayRef to an array of CFTypes.
+func CFArrayToArray(cfArray C.CFArrayRef) (a []C.CFTypeRef) {
+ count := C.CFArrayGetCount(cfArray)
+ if count > 0 {
+ a = make([]C.CFTypeRef, count)
+ C.CFArrayGetValues(cfArray, C.CFRange{0, count}, (*unsafe.Pointer)(unsafe.Pointer(&a[0])))
+ }
+ return
+}
+
+// Convertable knows how to convert an instance to a CFTypeRef.
+type Convertable interface {
+ Convert() (C.CFTypeRef, error)
+}
+
+// ConvertMapToCFDictionary converts a map to a CFDictionary and if non-nil,
+// must be released with Release(ref).
+func ConvertMapToCFDictionary(attr map[string]interface{}) (C.CFDictionaryRef, error) {
+ m := make(map[C.CFTypeRef]C.CFTypeRef)
+ for key, i := range attr {
+ var valueRef C.CFTypeRef
+ switch val := i.(type) {
+ default:
+ return 0, fmt.Errorf("Unsupported value type: %v", reflect.TypeOf(i))
+ case C.CFTypeRef:
+ valueRef = val
+ case bool:
+ if val {
+ valueRef = C.CFTypeRef(C.kCFBooleanTrue)
+ } else {
+ valueRef = C.CFTypeRef(C.kCFBooleanFalse)
+ }
+ case []byte:
+ bytesRef, err := BytesToCFData(val)
+ if err != nil {
+ return 0, err
+ }
+ valueRef = C.CFTypeRef(bytesRef)
+ defer Release(valueRef)
+ case string:
+ stringRef, err := StringToCFString(val)
+ if err != nil {
+ return 0, err
+ }
+ valueRef = C.CFTypeRef(stringRef)
+ defer Release(valueRef)
+ case Convertable:
+ convertedRef, err := val.Convert()
+ if err != nil {
+ return 0, err
+ }
+ valueRef = convertedRef
+ defer Release(valueRef)
+ }
+ keyRef, err := StringToCFString(key)
+ if err != nil {
+ return 0, err
+ }
+ m[C.CFTypeRef(keyRef)] = valueRef
+ defer Release(C.CFTypeRef(keyRef))
+ }
+
+ cfDict, err := MapToCFDictionary(m)
+ if err != nil {
+ return 0, err
+ }
+ return cfDict, nil
+}
+
+// CFTypeDescription returns type string for CFTypeRef.
+func CFTypeDescription(ref C.CFTypeRef) string {
+ typeID := C.CFGetTypeID(ref)
+ typeDesc := C.CFCopyTypeIDDescription(typeID)
+ defer Release(C.CFTypeRef(typeDesc))
+ return CFStringToString(typeDesc)
+}
+
+// Convert converts a CFTypeRef to a go instance.
+func Convert(ref C.CFTypeRef) (interface{}, error) {
+ typeID := C.CFGetTypeID(ref)
+ if typeID == C.CFStringGetTypeID() {
+ return CFStringToString(C.CFStringRef(ref)), nil
+ } else if typeID == C.CFDictionaryGetTypeID() {
+ return ConvertCFDictionary(C.CFDictionaryRef(ref))
+ } else if typeID == C.CFArrayGetTypeID() {
+ arr := CFArrayToArray(C.CFArrayRef(ref))
+ results := make([]interface{}, 0, len(arr))
+ for _, ref := range arr {
+ v, err := Convert(ref)
+ if err != nil {
+ return nil, err
+ }
+ results = append(results, v)
+ }
+ return results, nil
+ } else if typeID == C.CFDataGetTypeID() {
+ b, err := CFDataToBytes(C.CFDataRef(ref))
+ if err != nil {
+ return nil, err
+ }
+ return b, nil
+ } else if typeID == C.CFNumberGetTypeID() {
+ return CFNumberToInterface(C.CFNumberRef(ref)), nil
+ } else if typeID == C.CFBooleanGetTypeID() {
+ if C.CFBooleanGetValue(C.CFBooleanRef(ref)) != 0 {
+ return true, nil
+ }
+ return false, nil
+ }
+
+ return nil, fmt.Errorf("Invalid type: %s", CFTypeDescription(ref))
+}
+
+// ConvertCFDictionary converts a CFDictionary to map (deep).
+func ConvertCFDictionary(d C.CFDictionaryRef) (map[interface{}]interface{}, error) {
+ m := CFDictionaryToMap(d)
+ result := make(map[interface{}]interface{})
+
+ for k, v := range m {
+ gk, err := Convert(k)
+ if err != nil {
+ return nil, err
+ }
+ gv, err := Convert(v)
+ if err != nil {
+ return nil, err
+ }
+ result[gk] = gv
+ }
+ return result, nil
+}
+
+// CFNumberToInterface converts the CFNumberRef to the most appropriate numeric
+// type.
+// This code is from github.com/kballard/go-osx-plist.
+func CFNumberToInterface(cfNumber C.CFNumberRef) interface{} {
+ typ := C.CFNumberGetType(cfNumber)
+ switch typ {
+ case C.kCFNumberSInt8Type:
+ var sint C.SInt8
+ C.CFNumberGetValue(cfNumber, typ, unsafe.Pointer(&sint)) //nolint
+ return int8(sint)
+ case C.kCFNumberSInt16Type:
+ var sint C.SInt16
+ C.CFNumberGetValue(cfNumber, typ, unsafe.Pointer(&sint)) //nolint
+ return int16(sint)
+ case C.kCFNumberSInt32Type:
+ var sint C.SInt32
+ C.CFNumberGetValue(cfNumber, typ, unsafe.Pointer(&sint)) //nolint
+ return int32(sint)
+ case C.kCFNumberSInt64Type:
+ var sint C.SInt64
+ C.CFNumberGetValue(cfNumber, typ, unsafe.Pointer(&sint)) //nolint
+ return int64(sint)
+ case C.kCFNumberFloat32Type:
+ var float C.Float32
+ C.CFNumberGetValue(cfNumber, typ, unsafe.Pointer(&float)) //nolint
+ return float32(float)
+ case C.kCFNumberFloat64Type:
+ var float C.Float64
+ C.CFNumberGetValue(cfNumber, typ, unsafe.Pointer(&float)) //nolint
+ return float64(float)
+ case C.kCFNumberCharType:
+ var char C.char
+ C.CFNumberGetValue(cfNumber, typ, unsafe.Pointer(&char)) //nolint
+ return byte(char)
+ case C.kCFNumberShortType:
+ var short C.short
+ C.CFNumberGetValue(cfNumber, typ, unsafe.Pointer(&short)) //nolint
+ return int16(short)
+ case C.kCFNumberIntType:
+ var i C.int
+ C.CFNumberGetValue(cfNumber, typ, unsafe.Pointer(&i)) //nolint
+ return int32(i)
+ case C.kCFNumberLongType:
+ var long C.long
+ C.CFNumberGetValue(cfNumber, typ, unsafe.Pointer(&long)) //nolint
+ return int(long)
+ case C.kCFNumberLongLongType:
+ // This is the only type that may actually overflow us
+ var longlong C.longlong
+ C.CFNumberGetValue(cfNumber, typ, unsafe.Pointer(&longlong)) //nolint
+ return int64(longlong)
+ case C.kCFNumberFloatType:
+ var float C.float
+ C.CFNumberGetValue(cfNumber, typ, unsafe.Pointer(&float)) //nolint
+ return float32(float)
+ case C.kCFNumberDoubleType:
+ var double C.double
+ C.CFNumberGetValue(cfNumber, typ, unsafe.Pointer(&double)) //nolint
+ return float64(double)
+ case C.kCFNumberCFIndexType:
+ // CFIndex is a long
+ var index C.CFIndex
+ C.CFNumberGetValue(cfNumber, typ, unsafe.Pointer(&index)) //nolint
+ return int(index)
+ case C.kCFNumberNSIntegerType:
+ // We don't have a definition of NSInteger, but we know it's either an int or a long
+ var nsInt C.long
+ C.CFNumberGetValue(cfNumber, typ, unsafe.Pointer(&nsInt)) //nolint
+ return int(nsInt)
+ }
+ panic("Unknown CFNumber type")
+}
diff --git a/vendor/github.com/99designs/go-keychain/datetime.go b/vendor/github.com/99designs/go-keychain/datetime.go
new file mode 100644
index 0000000..8124a6e
--- /dev/null
+++ b/vendor/github.com/99designs/go-keychain/datetime.go
@@ -0,0 +1,68 @@
+// +build darwin ios
+
+package keychain
+
+/*
+#cgo LDFLAGS: -framework CoreFoundation
+
+#include <CoreFoundation/CoreFoundation.h>
+*/
+import "C"
+import (
+ "math"
+ "time"
+)
+
+const nsPerSec = 1000 * 1000 * 1000
+
+// absoluteTimeIntervalSince1970() returns the number of seconds from
+// the Unix epoch (1970-01-01T00:00:00+00:00) to the Core Foundation
+// absolute reference date (2001-01-01T00:00:00+00:00). It should be
+// exactly 978307200.
+func absoluteTimeIntervalSince1970() int64 {
+ return int64(C.kCFAbsoluteTimeIntervalSince1970)
+}
+
+func unixToAbsoluteTime(s int64, ns int64) C.CFAbsoluteTime {
+ // Subtract as int64s first before converting to floating
+ // point to minimize precision loss (assuming the given time
+ // isn't much earlier than the Core Foundation absolute
+ // reference date).
+ abs := s - absoluteTimeIntervalSince1970()
+ return C.CFAbsoluteTime(abs) + C.CFTimeInterval(ns)/nsPerSec
+}
+
+func absoluteTimeToUnix(abs C.CFAbsoluteTime) (int64, int64) {
+ int, frac := math.Modf(float64(abs))
+ return int64(int) + absoluteTimeIntervalSince1970(), int64(frac * nsPerSec)
+}
+
+// TimeToCFDate will convert the given time.Time to a CFDateRef, which
+// must be released with Release(ref).
+func TimeToCFDate(t time.Time) C.CFDateRef {
+ s := t.Unix()
+ ns := int64(t.Nanosecond())
+ abs := unixToAbsoluteTime(s, ns)
+ return C.CFDateCreate(C.kCFAllocatorDefault, abs)
+}
+
+// CFDateToTime will convert the given CFDateRef to a time.Time.
+func CFDateToTime(d C.CFDateRef) time.Time {
+ abs := C.CFDateGetAbsoluteTime(d)
+ s, ns := absoluteTimeToUnix(abs)
+ return time.Unix(s, ns)
+}
+
+// Wrappers around C functions for testing.
+
+func cfDateToAbsoluteTime(d C.CFDateRef) C.CFAbsoluteTime {
+ return C.CFDateGetAbsoluteTime(d)
+}
+
+func absoluteTimeToCFDate(abs C.CFAbsoluteTime) C.CFDateRef {
+ return C.CFDateCreate(C.kCFAllocatorDefault, abs)
+}
+
+func releaseCFDate(d C.CFDateRef) {
+ Release(C.CFTypeRef(d))
+}
diff --git a/vendor/github.com/99designs/go-keychain/ios.go b/vendor/github.com/99designs/go-keychain/ios.go
new file mode 100644
index 0000000..abbaf28
--- /dev/null
+++ b/vendor/github.com/99designs/go-keychain/ios.go
@@ -0,0 +1,22 @@
+// +build darwin,ios
+
+package keychain
+
+/*
+#cgo LDFLAGS: -framework CoreFoundation -framework Security
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <Security/Security.h>
+*/
+import "C"
+
+var AccessibleKey = attrKey(C.CFTypeRef(C.kSecAttrAccessible))
+var accessibleTypeRef = map[Accessible]C.CFTypeRef{
+ AccessibleWhenUnlocked: C.CFTypeRef(C.kSecAttrAccessibleWhenUnlocked),
+ AccessibleAfterFirstUnlock: C.CFTypeRef(C.kSecAttrAccessibleAfterFirstUnlock),
+ AccessibleAlways: C.CFTypeRef(C.kSecAttrAccessibleAlways),
+ AccessibleWhenPasscodeSetThisDeviceOnly: C.CFTypeRef(C.kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly),
+ AccessibleWhenUnlockedThisDeviceOnly: C.CFTypeRef(C.kSecAttrAccessibleWhenUnlockedThisDeviceOnly),
+ AccessibleAfterFirstUnlockThisDeviceOnly: C.CFTypeRef(C.kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly),
+ AccessibleAccessibleAlwaysThisDeviceOnly: C.CFTypeRef(C.kSecAttrAccessibleAlwaysThisDeviceOnly),
+}
diff --git a/vendor/github.com/99designs/go-keychain/keychain.go b/vendor/github.com/99designs/go-keychain/keychain.go
new file mode 100644
index 0000000..f5d02ad
--- /dev/null
+++ b/vendor/github.com/99designs/go-keychain/keychain.go
@@ -0,0 +1,531 @@
+// +build darwin
+
+package keychain
+
+// See https://developer.apple.com/library/ios/documentation/Security/Reference/keychainservices/index.html for the APIs used below.
+
+// Also see https://developer.apple.com/library/ios/documentation/Security/Conceptual/keychainServConcepts/01introduction/introduction.html .
+
+/*
+#cgo LDFLAGS: -framework CoreFoundation -framework Security
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <Security/Security.h>
+*/
+import "C"
+import (
+ "fmt"
+ "time"
+)
+
+// Error defines keychain errors
+type Error int
+
+var (
+ // ErrorUnimplemented corresponds to errSecUnimplemented result code
+ ErrorUnimplemented = Error(C.errSecUnimplemented)
+ // ErrorParam corresponds to errSecParam result code
+ ErrorParam = Error(C.errSecParam)
+ // ErrorAllocate corresponds to errSecAllocate result code
+ ErrorAllocate = Error(C.errSecAllocate)
+ // ErrorNotAvailable corresponds to errSecNotAvailable result code
+ ErrorNotAvailable = Error(C.errSecNotAvailable)
+ // ErrorAuthFailed corresponds to errSecAuthFailed result code
+ ErrorAuthFailed = Error(C.errSecAuthFailed)
+ // ErrorDuplicateItem corresponds to errSecDuplicateItem result code
+ ErrorDuplicateItem = Error(C.errSecDuplicateItem)
+ // ErrorItemNotFound corresponds to errSecItemNotFound result code
+ ErrorItemNotFound = Error(C.errSecItemNotFound)
+ // ErrorInteractionNotAllowed corresponds to errSecInteractionNotAllowed result code
+ ErrorInteractionNotAllowed = Error(C.errSecInteractionNotAllowed)
+ // ErrorDecode corresponds to errSecDecode result code
+ ErrorDecode = Error(C.errSecDecode)
+ // ErrorNoSuchKeychain corresponds to errSecNoSuchKeychain result code
+ ErrorNoSuchKeychain = Error(C.errSecNoSuchKeychain)
+ // ErrorNoAcccessForItem corresponds to errSecNoAccessForItem result code
+ ErrorNoAccessForItem = Error(C.errSecNoAccessForItem)
+)
+
+func checkError(errCode C.OSStatus) error {
+ if errCode == C.errSecSuccess {
+ return nil
+ }
+ return Error(errCode)
+}
+
+func (k Error) Error() (msg string) {
+ // SecCopyErrorMessageString is only available on OSX, so derive manually.
+ // Messages derived from `$ security error $errcode`.
+ switch k {
+ case ErrorUnimplemented:
+ msg = "Function or operation not implemented."
+ case ErrorParam:
+ msg = "One or more parameters passed to the function were not valid."
+ case ErrorAllocate:
+ msg = "Failed to allocate memory."
+ case ErrorNotAvailable:
+ msg = "No keychain is available. You may need to restart your computer."
+ case ErrorAuthFailed:
+ msg = "The user name or passphrase you entered is not correct."
+ case ErrorDuplicateItem:
+ msg = "The specified item already exists in the keychain."
+ case ErrorItemNotFound:
+ msg = "The specified item could not be found in the keychain."
+ case ErrorInteractionNotAllowed:
+ msg = "User interaction is not allowed."
+ case ErrorDecode:
+ msg = "Unable to decode the provided data."
+ case ErrorNoSuchKeychain:
+ msg = "The specified keychain could not be found."
+ case ErrorNoAccessForItem:
+ msg = "The specified item has no access control."
+ default:
+ msg = "Keychain Error."
+ }
+ return fmt.Sprintf("%s (%d)", msg, k)
+}
+
+// SecClass is the items class code
+type SecClass int
+
+// Keychain Item Classes
+var (
+ /*
+ kSecClassGenericPassword item attributes:
+ kSecAttrAccess (OS X only)
+ kSecAttrAccessGroup (iOS; also OS X if kSecAttrSynchronizable specified)
+ kSecAttrAccessible (iOS; also OS X if kSecAttrSynchronizable specified)
+ kSecAttrAccount
+ kSecAttrService
+ */
+ SecClassGenericPassword SecClass = 1
+ SecClassInternetPassword SecClass = 2
+)
+
+// SecClassKey is the key type for SecClass
+var SecClassKey = attrKey(C.CFTypeRef(C.kSecClass))
+var secClassTypeRef = map[SecClass]C.CFTypeRef{
+ SecClassGenericPassword: C.CFTypeRef(C.kSecClassGenericPassword),
+ SecClassInternetPassword: C.CFTypeRef(C.kSecClassInternetPassword),
+}
+
+var (
+ // ServiceKey is for kSecAttrService
+ ServiceKey = attrKey(C.CFTypeRef(C.kSecAttrService))
+ // LabelKey is for kSecAttrLabel
+ LabelKey = attrKey(C.CFTypeRef(C.kSecAttrLabel))
+ // AccountKey is for kSecAttrAccount
+ AccountKey = attrKey(C.CFTypeRef(C.kSecAttrAccount))
+ // AccessGroupKey is for kSecAttrAccessGroup
+ AccessGroupKey = attrKey(C.CFTypeRef(C.kSecAttrAccessGroup))
+ // DataKey is for kSecValueData
+ DataKey = attrKey(C.CFTypeRef(C.kSecValueData))
+ // DescriptionKey is for kSecAttrDescription
+ DescriptionKey = attrKey(C.CFTypeRef(C.kSecAttrDescription))
+ // CreationDateKey is for kSecAttrCreationDate
+ CreationDateKey = attrKey(C.CFTypeRef(C.kSecAttrCreationDate))
+ // ModificationDateKey is for kSecAttrModificationDate
+ ModificationDateKey = attrKey(C.CFTypeRef(C.kSecAttrModificationDate))
+)
+
+// Synchronizable is the items synchronizable status
+type Synchronizable int
+
+const (
+ // SynchronizableDefault is the default setting
+ SynchronizableDefault Synchronizable = 0
+ // SynchronizableAny is for kSecAttrSynchronizableAny
+ SynchronizableAny = 1
+ // SynchronizableYes enables synchronization
+ SynchronizableYes = 2
+ // SynchronizableNo disables synchronization
+ SynchronizableNo = 3
+)
+
+// SynchronizableKey is the key type for Synchronizable
+var SynchronizableKey = attrKey(C.CFTypeRef(C.kSecAttrSynchronizable))
+var syncTypeRef = map[Synchronizable]C.CFTypeRef{
+ SynchronizableAny: C.CFTypeRef(C.kSecAttrSynchronizableAny),
+ SynchronizableYes: C.CFTypeRef(C.kCFBooleanTrue),
+ SynchronizableNo: C.CFTypeRef(C.kCFBooleanFalse),
+}
+
+// Accessible is the items accessibility
+type Accessible int
+
+const (
+ // AccessibleDefault is the default
+ AccessibleDefault Accessible = 0
+ // AccessibleWhenUnlocked is when unlocked
+ AccessibleWhenUnlocked = 1
+ // AccessibleAfterFirstUnlock is after first unlock
+ AccessibleAfterFirstUnlock = 2
+ // AccessibleAlways is always
+ AccessibleAlways = 3
+ // AccessibleWhenPasscodeSetThisDeviceOnly is when passcode is set
+ AccessibleWhenPasscodeSetThisDeviceOnly = 4
+ // AccessibleWhenUnlockedThisDeviceOnly is when unlocked for this device only
+ AccessibleWhenUnlockedThisDeviceOnly = 5
+ // AccessibleAfterFirstUnlockThisDeviceOnly is after first unlock for this device only
+ AccessibleAfterFirstUnlockThisDeviceOnly = 6
+ // AccessibleAccessibleAlwaysThisDeviceOnly is always for this device only
+ AccessibleAccessibleAlwaysThisDeviceOnly = 7
+)
+
+// MatchLimit is whether to limit results on query
+type MatchLimit int
+
+const (
+ // MatchLimitDefault is the default
+ MatchLimitDefault MatchLimit = 0
+ // MatchLimitOne limits to one result
+ MatchLimitOne = 1
+ // MatchLimitAll is no limit
+ MatchLimitAll = 2
+)
+
+// MatchLimitKey is key type for MatchLimit
+var MatchLimitKey = attrKey(C.CFTypeRef(C.kSecMatchLimit))
+var matchTypeRef = map[MatchLimit]C.CFTypeRef{
+ MatchLimitOne: C.CFTypeRef(C.kSecMatchLimitOne),
+ MatchLimitAll: C.CFTypeRef(C.kSecMatchLimitAll),
+}
+
+// ReturnAttributesKey is key type for kSecReturnAttributes
+var ReturnAttributesKey = attrKey(C.CFTypeRef(C.kSecReturnAttributes))
+
+// ReturnDataKey is key type for kSecReturnData
+var ReturnDataKey = attrKey(C.CFTypeRef(C.kSecReturnData))
+
+// ReturnRefKey is key type for kSecReturnRef
+var ReturnRefKey = attrKey(C.CFTypeRef(C.kSecReturnRef))
+
+// Item for adding, querying or deleting.
+type Item struct {
+ // Values can be string, []byte, Convertable or CFTypeRef (constant).
+ attr map[string]interface{}
+}
+
+// SetSecClass sets the security class
+func (k *Item) SetSecClass(sc SecClass) {
+ k.attr[SecClassKey] = secClassTypeRef[sc]
+}
+
+// SetString sets a string attibute for a string key
+func (k *Item) SetString(key string, s string) {
+ if s != "" {
+ k.attr[key] = s
+ } else {
+ delete(k.attr, key)
+ }
+}
+
+// SetService sets the service attribute
+func (k *Item) SetService(s string) {
+ k.SetString(ServiceKey, s)
+}
+
+// SetAccount sets the account attribute
+func (k *Item) SetAccount(a string) {
+ k.SetString(AccountKey, a)
+}
+
+// SetLabel sets the label attribute
+func (k *Item) SetLabel(l string) {
+ k.SetString(LabelKey, l)
+}
+
+// SetDescription sets the description attribute
+func (k *Item) SetDescription(s string) {
+ k.SetString(DescriptionKey, s)
+}
+
+// SetData sets the data attribute
+func (k *Item) SetData(b []byte) {
+ if b != nil {
+ k.attr[DataKey] = b
+ } else {
+ delete(k.attr, DataKey)
+ }
+}
+
+// SetAccessGroup sets the access group attribute
+func (k *Item) SetAccessGroup(ag string) {
+ k.SetString(AccessGroupKey, ag)
+}
+
+// SetSynchronizable sets the synchronizable attribute
+func (k *Item) SetSynchronizable(sync Synchronizable) {
+ if sync != SynchronizableDefault {
+ k.attr[SynchronizableKey] = syncTypeRef[sync]
+ } else {
+ delete(k.attr, SynchronizableKey)
+ }
+}
+
+// SetAccessible sets the accessible attribute
+func (k *Item) SetAccessible(accessible Accessible) {
+ if accessible != AccessibleDefault {
+ k.attr[AccessibleKey] = accessibleTypeRef[accessible]
+ } else {
+ delete(k.attr, AccessibleKey)
+ }
+}
+
+// SetMatchLimit sets the match limit
+func (k *Item) SetMatchLimit(matchLimit MatchLimit) {
+ if matchLimit != MatchLimitDefault {
+ k.attr[MatchLimitKey] = matchTypeRef[matchLimit]
+ } else {
+ delete(k.attr, MatchLimitKey)
+ }
+}
+
+// SetReturnAttributes sets the return value type on query
+func (k *Item) SetReturnAttributes(b bool) {
+ k.attr[ReturnAttributesKey] = b
+}
+
+// SetReturnData enables returning data on query
+func (k *Item) SetReturnData(b bool) {
+ k.attr[ReturnDataKey] = b
+}
+
+// SetReturnRef enables returning references on query
+func (k *Item) SetReturnRef(b bool) {
+ k.attr[ReturnRefKey] = b
+}
+
+// NewItem is a new empty keychain item
+func NewItem() Item {
+ return Item{make(map[string]interface{})}
+}
+
+// NewGenericPassword creates a generic password item with the default keychain. This is a convenience method.
+func NewGenericPassword(service string, account string, label string, data []byte, accessGroup string) Item {
+ item := NewItem()
+ item.SetSecClass(SecClassGenericPassword)
+ item.SetService(service)
+ item.SetAccount(account)
+ item.SetLabel(label)
+ item.SetData(data)
+ item.SetAccessGroup(accessGroup)
+ return item
+}
+
+// AddItem adds a Item to a Keychain
+func AddItem(item Item) error {
+ cfDict, err := ConvertMapToCFDictionary(item.attr)
+ if err != nil {
+ return err
+ }
+ defer Release(C.CFTypeRef(cfDict))
+
+ errCode := C.SecItemAdd(cfDict, nil)
+ err = checkError(errCode)
+ return err
+}
+
+// UpdateItem updates the queryItem with the parameters from updateItem
+func UpdateItem(queryItem Item, updateItem Item) error {
+ cfDict, err := ConvertMapToCFDictionary(queryItem.attr)
+ if err != nil {
+ return err
+ }
+ defer Release(C.CFTypeRef(cfDict))
+ cfDictUpdate, err := ConvertMapToCFDictionary(updateItem.attr)
+ if err != nil {
+ return err
+ }
+ defer Release(C.CFTypeRef(cfDictUpdate))
+ errCode := C.SecItemUpdate(cfDict, cfDictUpdate)
+ err = checkError(errCode)
+ return err
+}
+
+// QueryResult stores all possible results from queries.
+// Not all fields are applicable all the time. Results depend on query.
+type QueryResult struct {
+ Service string
+ Account string
+ AccessGroup string
+ Label string
+ Description string
+ Data []byte
+ CreationDate time.Time
+ ModificationDate time.Time
+}
+
+// QueryItemRef returns query result as CFTypeRef. You must release it when you are done.
+func QueryItemRef(item Item) (C.CFTypeRef, error) {
+ cfDict, err := ConvertMapToCFDictionary(item.attr)
+ if err != nil {
+ return 0, err
+ }
+ defer Release(C.CFTypeRef(cfDict))
+
+ var resultsRef C.CFTypeRef
+ errCode := C.SecItemCopyMatching(cfDict, &resultsRef) //nolint
+ if Error(errCode) == ErrorItemNotFound {
+ return 0, nil
+ }
+ err = checkError(errCode)
+ if err != nil {
+ return 0, err
+ }
+ return resultsRef, nil
+}
+
+// QueryItem returns a list of query results.
+func QueryItem(item Item) ([]QueryResult, error) {
+ resultsRef, err := QueryItemRef(item)
+ if err != nil {
+ return nil, err
+ }
+ if resultsRef == 0 {
+ return nil, nil
+ }
+ defer Release(resultsRef)
+
+ results := make([]QueryResult, 0, 1)
+
+ typeID := C.CFGetTypeID(resultsRef)
+ if typeID == C.CFArrayGetTypeID() {
+ arr := CFArrayToArray(C.CFArrayRef(resultsRef))
+ for _, ref := range arr {
+ elementTypeID := C.CFGetTypeID(ref)
+ if elementTypeID == C.CFDictionaryGetTypeID() {
+ item, err := convertResult(C.CFDictionaryRef(ref))
+ if err != nil {
+ return nil, err
+ }
+ results = append(results, *item)
+ } else {
+ return nil, fmt.Errorf("invalid result type (If you SetReturnRef(true) you should use QueryItemRef directly)")
+ }
+ }
+ } else if typeID == C.CFDictionaryGetTypeID() {
+ item, err := convertResult(C.CFDictionaryRef(resultsRef))
+ if err != nil {
+ return nil, err
+ }
+ results = append(results, *item)
+ } else if typeID == C.CFDataGetTypeID() {
+ b, err := CFDataToBytes(C.CFDataRef(resultsRef))
+ if err != nil {
+ return nil, err
+ }
+ item := QueryResult{Data: b}
+ results = append(results, item)
+ } else {
+ return nil, fmt.Errorf("Invalid result type: %s", CFTypeDescription(resultsRef))
+ }
+
+ return results, nil
+}
+
+func attrKey(ref C.CFTypeRef) string {
+ return CFStringToString(C.CFStringRef(ref))
+}
+
+func convertResult(d C.CFDictionaryRef) (*QueryResult, error) {
+ m := CFDictionaryToMap(d)
+ result := QueryResult{}
+ for k, v := range m {
+ switch attrKey(k) {
+ case ServiceKey:
+ result.Service = CFStringToString(C.CFStringRef(v))
+ case AccountKey:
+ result.Account = CFStringToString(C.CFStringRef(v))
+ case AccessGroupKey:
+ result.AccessGroup = CFStringToString(C.CFStringRef(v))
+ case LabelKey:
+ result.Label = CFStringToString(C.CFStringRef(v))
+ case DescriptionKey:
+ result.Description = CFStringToString(C.CFStringRef(v))
+ case DataKey:
+ b, err := CFDataToBytes(C.CFDataRef(v))
+ if err != nil {
+ return nil, err
+ }
+ result.Data = b
+ case CreationDateKey:
+ result.CreationDate = CFDateToTime(C.CFDateRef(v))
+ case ModificationDateKey:
+ result.ModificationDate = CFDateToTime(C.CFDateRef(v))
+ // default:
+ // fmt.Printf("Unhandled key in conversion: %v = %v\n", cfTypeValue(k), cfTypeValue(v))
+ }
+ }
+ return &result, nil
+}
+
+// DeleteGenericPasswordItem removes a generic password item.
+func DeleteGenericPasswordItem(service string, account string) error {
+ item := NewItem()
+ item.SetSecClass(SecClassGenericPassword)
+ item.SetService(service)
+ item.SetAccount(account)
+ return DeleteItem(item)
+}
+
+// DeleteItem removes a Item
+func DeleteItem(item Item) error {
+ cfDict, err := ConvertMapToCFDictionary(item.attr)
+ if err != nil {
+ return err
+ }
+ defer Release(C.CFTypeRef(cfDict))
+
+ errCode := C.SecItemDelete(cfDict)
+ return checkError(errCode)
+}
+
+// GetAccountsForService is deprecated
+func GetAccountsForService(service string) ([]string, error) {
+ return GetGenericPasswordAccounts(service)
+}
+
+// GetGenericPasswordAccounts returns generic password accounts for service. This is a convenience method.
+func GetGenericPasswordAccounts(service string) ([]string, error) {
+ query := NewItem()
+ query.SetSecClass(SecClassGenericPassword)
+ query.SetService(service)
+ query.SetMatchLimit(MatchLimitAll)
+ query.SetReturnAttributes(true)
+ results, err := QueryItem(query)
+ if err != nil {
+ return nil, err
+ }
+
+ accounts := make([]string, 0, len(results))
+ for _, r := range results {
+ accounts = append(accounts, r.Account)
+ }
+
+ return accounts, nil
+}
+
+// GetGenericPassword returns password data for service and account. This is a convenience method.
+// If item is not found returns nil, nil.
+func GetGenericPassword(service string, account string, label string, accessGroup string) ([]byte, error) {
+ query := NewItem()
+ query.SetSecClass(SecClassGenericPassword)
+ query.SetService(service)
+ query.SetAccount(account)
+ query.SetLabel(label)
+ query.SetAccessGroup(accessGroup)
+ query.SetMatchLimit(MatchLimitOne)
+ query.SetReturnData(true)
+ results, err := QueryItem(query)
+ if err != nil {
+ return nil, err
+ }
+ if len(results) > 1 {
+ return nil, fmt.Errorf("Too many results")
+ }
+ if len(results) == 1 {
+ return results[0].Data, nil
+ }
+ return nil, nil
+}
diff --git a/vendor/github.com/99designs/go-keychain/macos.go b/vendor/github.com/99designs/go-keychain/macos.go
new file mode 100644
index 0000000..4004349
--- /dev/null
+++ b/vendor/github.com/99designs/go-keychain/macos.go
@@ -0,0 +1,272 @@
+// +build darwin,!ios
+
+package keychain
+
+/*
+#cgo LDFLAGS: -framework CoreFoundation -framework Security
+#cgo CFLAGS: -w
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <Security/Security.h>
+*/
+import "C"
+import (
+ "os"
+ "unsafe"
+)
+
+// AccessibleKey is key for kSecAttrAccessible
+var AccessibleKey = attrKey(C.CFTypeRef(C.kSecAttrAccessible))
+var accessibleTypeRef = map[Accessible]C.CFTypeRef{
+ AccessibleWhenUnlocked: C.CFTypeRef(C.kSecAttrAccessibleWhenUnlocked),
+ AccessibleAfterFirstUnlock: C.CFTypeRef(C.kSecAttrAccessibleAfterFirstUnlock),
+ AccessibleAlways: C.CFTypeRef(C.kSecAttrAccessibleAlways),
+ AccessibleWhenUnlockedThisDeviceOnly: C.CFTypeRef(C.kSecAttrAccessibleWhenUnlockedThisDeviceOnly),
+ AccessibleAfterFirstUnlockThisDeviceOnly: C.CFTypeRef(C.kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly),
+ AccessibleAccessibleAlwaysThisDeviceOnly: C.CFTypeRef(C.kSecAttrAccessibleAlwaysThisDeviceOnly),
+
+ // Only available in 10.10
+ //AccessibleWhenPasscodeSetThisDeviceOnly: C.CFTypeRef(C.kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly),
+}
+
+var (
+ // AccessKey is key for kSecAttrAccess
+ AccessKey = attrKey(C.CFTypeRef(C.kSecAttrAccess))
+)
+
+// createAccess creates a SecAccessRef as CFTypeRef.
+// The returned SecAccessRef, if non-nil, must be released via CFRelease.
+func createAccess(label string, trustedApplications []string) (C.CFTypeRef, error) {
+ var err error
+ var labelRef C.CFStringRef
+ if labelRef, err = StringToCFString(label); err != nil {
+ return 0, err
+ }
+ defer C.CFRelease(C.CFTypeRef(labelRef))
+
+ var trustedApplicationsArray C.CFArrayRef
+ if trustedApplications != nil {
+ if len(trustedApplications) > 0 {
+ // Always prepend with empty string which signifies that we
+ // include a NULL application, which means ourselves.
+ trustedApplications = append([]string{""}, trustedApplications...)
+ }
+
+ var trustedApplicationsRefs []C.CFTypeRef
+ for _, trustedApplication := range trustedApplications {
+ trustedApplicationRef, createErr := createTrustedApplication(trustedApplication)
+ if createErr != nil {
+ return 0, createErr
+ }
+ defer C.CFRelease(trustedApplicationRef)
+ trustedApplicationsRefs = append(trustedApplicationsRefs, trustedApplicationRef)
+ }
+
+ trustedApplicationsArray = ArrayToCFArray(trustedApplicationsRefs)
+ defer C.CFRelease(C.CFTypeRef(trustedApplicationsArray))
+ }
+
+ var access C.SecAccessRef
+ errCode := C.SecAccessCreate(labelRef, trustedApplicationsArray, &access) //nolint
+ err = checkError(errCode)
+ if err != nil {
+ return 0, err
+ }
+
+ return C.CFTypeRef(access), nil
+}
+
+// createTrustedApplication creates a SecTrustedApplicationRef as a CFTypeRef.
+// The returned SecTrustedApplicationRef, if non-nil, must be released via CFRelease.
+func createTrustedApplication(trustedApplication string) (C.CFTypeRef, error) {
+ var trustedApplicationCStr *C.char
+ if trustedApplication != "" {
+ trustedApplicationCStr = C.CString(trustedApplication)
+ defer C.free(unsafe.Pointer(trustedApplicationCStr))
+ }
+
+ var trustedApplicationRef C.SecTrustedApplicationRef
+ errCode := C.SecTrustedApplicationCreateFromPath(trustedApplicationCStr, &trustedApplicationRef) //nolint
+ err := checkError(errCode)
+ if err != nil {
+ return 0, err
+ }
+
+ return C.CFTypeRef(trustedApplicationRef), nil
+}
+
+// Access defines whats applications can use the keychain item
+type Access struct {
+ Label string
+ TrustedApplications []string
+}
+
+// Convert converts Access to CFTypeRef.
+// The returned CFTypeRef, if non-nil, must be released via CFRelease.
+func (a Access) Convert() (C.CFTypeRef, error) {
+ return createAccess(a.Label, a.TrustedApplications)
+}
+
+// SetAccess sets Access on Item
+func (k *Item) SetAccess(a *Access) {
+ if a != nil {
+ k.attr[AccessKey] = a
+ } else {
+ delete(k.attr, AccessKey)
+ }
+}
+
+// DeleteItemRef deletes a keychain item reference.
+func DeleteItemRef(ref C.CFTypeRef) error {
+ errCode := C.SecKeychainItemDelete(C.SecKeychainItemRef(ref))
+ return checkError(errCode)
+}
+
+var (
+ // KeychainKey is key for kSecUseKeychain
+ KeychainKey = attrKey(C.CFTypeRef(C.kSecUseKeychain))
+ // MatchSearchListKey is key for kSecMatchSearchList
+ MatchSearchListKey = attrKey(C.CFTypeRef(C.kSecMatchSearchList))
+)
+
+// Keychain represents the path to a specific OSX keychain
+type Keychain struct {
+ path string
+}
+
+// NewKeychain creates a new keychain file with a password
+func NewKeychain(path string, password string) (Keychain, error) {
+ return newKeychain(path, password, false)
+}
+
+// NewKeychainWithPrompt creates a new Keychain and prompts user for password
+func NewKeychainWithPrompt(path string) (Keychain, error) {
+ return newKeychain(path, "", true)
+}
+
+func newKeychain(path, password string, promptUser bool) (Keychain, error) {
+ pathRef := C.CString(path)
+ defer C.free(unsafe.Pointer(pathRef))
+
+ var errCode C.OSStatus
+ var kref C.SecKeychainRef
+
+ if promptUser {
+ errCode = C.SecKeychainCreate(pathRef, C.UInt32(0), nil, C.Boolean(1), 0, &kref) //nolint
+ } else {
+ passwordRef := C.CString(password)
+ defer C.free(unsafe.Pointer(passwordRef))
+ errCode = C.SecKeychainCreate(pathRef, C.UInt32(len(password)), unsafe.Pointer(passwordRef), C.Boolean(0), 0, &kref) //nolint
+ }
+
+ if err := checkError(errCode); err != nil {
+ return Keychain{}, err
+ }
+
+ // TODO: Without passing in kref I get 'One or more parameters passed to the function were not valid (-50)'
+ defer Release(C.CFTypeRef(kref))
+
+ return Keychain{
+ path: path,
+ }, nil
+}
+
+// NewWithPath to use an existing keychain
+func NewWithPath(path string) Keychain {
+ return Keychain{
+ path: path,
+ }
+}
+
+// Status returns the status of the keychain
+func (kc Keychain) Status() error {
+ // returns no error even if it doesn't exist
+ kref, err := openKeychainRef(kc.path)
+ if err != nil {
+ return err
+ }
+ defer C.CFRelease(C.CFTypeRef(kref))
+
+ var status C.SecKeychainStatus
+ return checkError(C.SecKeychainGetStatus(kref, &status))
+}
+
+// The returned SecKeychainRef, if non-nil, must be released via CFRelease.
+func openKeychainRef(path string) (C.SecKeychainRef, error) {
+ pathName := C.CString(path)
+ defer C.free(unsafe.Pointer(pathName))
+
+ var kref C.SecKeychainRef
+ if err := checkError(C.SecKeychainOpen(pathName, &kref)); err != nil { //nolint
+ return 0, err
+ }
+
+ return kref, nil
+}
+
+// UnlockAtPath unlocks keychain at path
+func UnlockAtPath(path string, password string) error {
+ kref, err := openKeychainRef(path)
+ defer Release(C.CFTypeRef(kref))
+ if err != nil {
+ return err
+ }
+ passwordRef := C.CString(password)
+ defer C.free(unsafe.Pointer(passwordRef))
+ return checkError(C.SecKeychainUnlock(kref, C.UInt32(len(password)), unsafe.Pointer(passwordRef), C.Boolean(1)))
+}
+
+// LockAtPath locks keychain at path
+func LockAtPath(path string) error {
+ kref, err := openKeychainRef(path)
+ defer Release(C.CFTypeRef(kref))
+ if err != nil {
+ return err
+ }
+ return checkError(C.SecKeychainLock(kref))
+}
+
+// Delete the Keychain
+func (kc *Keychain) Delete() error {
+ return os.Remove(kc.path)
+}
+
+// Convert Keychain to CFTypeRef.
+// The returned CFTypeRef, if non-nil, must be released via CFRelease.
+func (kc Keychain) Convert() (C.CFTypeRef, error) {
+ keyRef, err := openKeychainRef(kc.path)
+ return C.CFTypeRef(keyRef), err
+}
+
+type keychainArray []Keychain
+
+// Convert the keychainArray to a CFTypeRef.
+// The returned CFTypeRef, if non-nil, must be released via CFRelease.
+func (ka keychainArray) Convert() (C.CFTypeRef, error) {
+ var refs = make([]C.CFTypeRef, len(ka))
+ var err error
+
+ for idx, kc := range ka {
+ if refs[idx], err = kc.Convert(); err != nil {
+ // If we error trying to convert lets release any we converted before
+ for _, ref := range refs {
+ if ref != 0 {
+ Release(ref)
+ }
+ }
+ return 0, err
+ }
+ }
+
+ return C.CFTypeRef(ArrayToCFArray(refs)), nil
+}
+
+// SetMatchSearchList sets match type on keychains
+func (k *Item) SetMatchSearchList(karr ...Keychain) {
+ k.attr[MatchSearchListKey] = keychainArray(karr)
+}
+
+// UseKeychain tells item to use the specified Keychain
+func (k *Item) UseKeychain(kc Keychain) {
+ k.attr[KeychainKey] = kc
+}
diff --git a/vendor/github.com/99designs/go-keychain/util.go b/vendor/github.com/99designs/go-keychain/util.go
new file mode 100644
index 0000000..29cbfc6
--- /dev/null
+++ b/vendor/github.com/99designs/go-keychain/util.go
@@ -0,0 +1,31 @@
+package keychain
+
+import (
+ "crypto/rand"
+ "encoding/base32"
+ "strings"
+)
+
+var randRead = rand.Read
+
+// RandomID returns random ID (base32) string with prefix, using 256 bits as
+// recommended by tptacek: https://gist.github.com/tqbf/be58d2d39690c3b366ad
+func RandomID(prefix string) (string, error) {
+ buf, err := RandBytes(32)
+ if err != nil {
+ return "", err
+ }
+ str := base32.StdEncoding.EncodeToString(buf)
+ str = strings.Replace(str, "=", "", -1)
+ str = prefix + str
+ return str, nil
+}
+
+// RandBytes returns random bytes of length
+func RandBytes(length int) ([]byte, error) {
+ buf := make([]byte, length)
+ if _, err := randRead(buf); err != nil {
+ return nil, err
+ }
+ return buf, nil
+}
diff --git a/vendor/github.com/99designs/keyring/.gitattributes b/vendor/github.com/99designs/keyring/.gitattributes
new file mode 100644
index 0000000..d207b18
--- /dev/null
+++ b/vendor/github.com/99designs/keyring/.gitattributes
@@ -0,0 +1 @@
+*.go text eol=lf
diff --git a/vendor/github.com/99designs/keyring/.gitignore b/vendor/github.com/99designs/keyring/.gitignore
new file mode 100644
index 0000000..8000dd9
--- /dev/null
+++ b/vendor/github.com/99designs/keyring/.gitignore
@@ -0,0 +1 @@
+.vagrant
diff --git a/vendor/github.com/99designs/keyring/.golangci.yml b/vendor/github.com/99designs/keyring/.golangci.yml
new file mode 100644
index 0000000..f83428e
--- /dev/null
+++ b/vendor/github.com/99designs/keyring/.golangci.yml
@@ -0,0 +1,29 @@
+linters:
+ enable:
+ - bodyclose
+ - contextcheck
+ - depguard
+ - durationcheck
+ - dupl
+ - errchkjson
+ - errname
+ - exhaustive
+ - exportloopref
+ - gocritic
+ - gofmt
+ - goimports
+ - makezero
+ - misspell
+ - nakedret
+ - nilerr
+ - nilnil
+ - noctx
+ - prealloc
+ - revive
+ # - rowserrcheck
+ - thelper
+ - tparallel
+ - unconvert
+ - unparam
+ # - wastedassign
+ - whitespace
diff --git a/vendor/github.com/99designs/keyring/LICENSE b/vendor/github.com/99designs/keyring/LICENSE
new file mode 100644
index 0000000..0fe9e46
--- /dev/null
+++ b/vendor/github.com/99designs/keyring/LICENSE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 99designs
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
diff --git a/vendor/github.com/99designs/keyring/README.md b/vendor/github.com/99designs/keyring/README.md
new file mode 100644
index 0000000..629a6aa
--- /dev/null
+++ b/vendor/github.com/99designs/keyring/README.md
@@ -0,0 +1,67 @@
+Keyring
+=======
+[![Build Status](https://github.com/99designs/keyring/workflows/Continuous%20Integration/badge.svg)](https://github.com/99designs/keyring/actions)
+[![Documentation](https://godoc.org/github.com/99designs/keyring?status.svg)](https://godoc.org/github.com/99designs/keyring)
+
+Keyring provides a common interface to a range of secure credential storage services. Originally developed as part of [AWS Vault](https://github.com/99designs/aws-vault), a command line tool for securely managing AWS access from developer workstations.
+
+Currently Keyring supports the following backends
+ * [macOS Keychain](https://support.apple.com/en-au/guide/keychain-access/welcome/mac)
+ * [Windows Credential Manager](https://support.microsoft.com/en-au/help/4026814/windows-accessing-credential-manager)
+ * Secret Service ([Gnome Keyring](https://wiki.gnome.org/Projects/GnomeKeyring), [KWallet](https://kde.org/applications/system/org.kde.kwalletmanager5))
+ * [KWallet](https://kde.org/applications/system/org.kde.kwalletmanager5)
+ * [Pass](https://www.passwordstore.org/)
+ * [Encrypted file (JWT)](https://datatracker.ietf.org/doc/html/rfc7519)
+ * [KeyCtl](https://linux.die.net/man/1/keyctl)
+
+
+## Usage
+
+The short version of how to use keyring is shown below.
+
+```go
+ring, _ := keyring.Open(keyring.Config{
+ ServiceName: "example",
+})
+
+_ = ring.Set(keyring.Item{
+ Key: "foo",
+ Data: []byte("secret-bar"),
+})
+
+i, _ := ring.Get("foo")
+
+fmt.Printf("%s", i.Data)
+```
+
+For more detail on the API please check [the keyring godocs](https://godoc.org/github.com/99designs/keyring)
+
+
+## Testing
+
+[Vagrant](https://www.vagrantup.com/) is used to create linux and windows test environments.
+
+```bash
+# Start vagrant
+vagrant up
+
+# Run go tests on all platforms
+./bin/go-test
+```
+
+
+## Contributing
+
+Contributions to the keyring package are most welcome from engineers of all backgrounds and skill levels. In particular the addition of extra backends across popular operating systems would be appreciated.
+
+This project will adhere to the [Go Community Code of Conduct](https://golang.org/conduct) in the github provided discussion spaces, with the moderators being the 99designs engineering team.
+
+To make a contribution:
+
+ * Fork the repository
+ * Make your changes on the fork
+ * Submit a pull request back to this repo with a clear description of the problem you're solving
+ * Ensure your PR passes all current (and new) tests
+ * Ideally verify that [aws-vault](https://github.com/99designs/aws-vault) works with your changes (optional)
+
+...and we'll do our best to get your work merged in
diff --git a/vendor/github.com/99designs/keyring/Vagrantfile b/vendor/github.com/99designs/keyring/Vagrantfile
new file mode 100644
index 0000000..7d30d2a
--- /dev/null
+++ b/vendor/github.com/99designs/keyring/Vagrantfile
@@ -0,0 +1,85 @@
+Vagrant.configure("2") do |config|
+
+ config.vm.define "linux" do |linux|
+ linux.vm.box = "generic/fedora32"
+
+ linux.vm.provider "virtualbox" do |vb|
+ vb.gui = true
+ vb.memory = 2048
+ vb.cpus = 2
+
+ # VBoxVGA flickers constantly, use vmsvga instead which doesn't have that problem
+ vb.customize ["modifyvm", :id, "--graphicscontroller", "vmsvga"]
+ end
+
+ # mount the project into /keyring
+ linux.vm.synced_folder ".", "/keyring"
+
+ # install gnome desktop and auto login
+ linux.vm.provision "shell", inline: "sudo dnf install -y --exclude='gnome-initial-setup' @gnome-desktop langpacks-en"
+ linux.vm.provision "shell", inline: <<-SHELL
+ sudo sed -i -e 's/\\[daemon\\]/\\[daemon\\]\\nAutomaticLoginEnable=True\\nAutomaticLogin=vagrant\\n/' \
+ /etc/gdm/custom.conf
+ SHELL
+ linux.vm.provision "shell", inline: "sudo systemctl set-default graphical.target"
+ linux.vm.provision "shell", inline: "sudo systemctl isolate graphical.target"
+
+ # set the root password - sometimes prompts show up in gnome needing to install software
+ linux.vm.provision "shell", inline: "echo 'vagrant' | sudo passwd root --stdin"
+
+ # install gnome keyring
+ linux.vm.provision "shell", inline: "sudo dnf install -y gnome-keyring seahorse"
+
+ # install kwallet
+ linux.vm.provision "shell", inline: "sudo dnf install -y kwalletmanager5"
+
+ # install pass
+ linux.vm.provision "shell", inline: "sudo dnf install -y pass"
+
+ # install golang
+ linux.vm.provision "shell", inline: "sudo dnf install -y go"
+ end
+
+
+ config.vm.define "windows" do |windows|
+ windows.vm.box = "StefanScherer/windows_10"
+
+ windows.vm.provider "virtualbox" do |vb|
+ vb.gui = true
+ vb.memory = 2048
+ vb.cpus = 2
+ end
+
+ # mount the project into c:\keyring
+ windows.vm.synced_folder ".", "/keyring"
+
+ # install chocolately
+ windows.vm.provision "shell", privileged: true, inline: <<-SHELL
+ Set-ExecutionPolicy Bypass -Scope Process -Force; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
+ choco feature disable -n=showDownloadProgress
+ SHELL
+
+ # install golang
+ windows.vm.provision "shell", privileged: true, inline: "choco install -y git golang"
+ end
+
+ config.vm.post_up_message = <<-MESSAGE
+ There are 2 vagrant boxes:
+ - linux
+ - OS: Fedora 32 with Gnome Desktop
+ - The keyring directory is mounted at /keyring
+ - Get a shell with 'vagrant ssh linux'
+ - When running go test, you'll need to use the GUI to click "Continue" on the prompts
+ - After provisioning, adjusting the virtualbox GUI window size doesn't cause the resolution to update. A 'vagrant reload linux' solves the problem
+ - windows
+ - OS: Windows 10
+ - The keyring directory is mounted at C:\keyring
+ - Get a shell by starting PowerShell in the GUI
+ - You can run commands remotely using 'vagrant winrm -e windows CMD'. You'll need the -e (elevated privileges) if you want to interact with wincred
+
+ Automated scripts for running go test on vagrant boxes (run these locally):
+ - ./bin/go-test-linux - Run tests on Linux
+ - ./bin/go-test-windows - Run tests on Windows
+ - ./bin/go-test - Run all tests - locally, linux and windows
+ MESSAGE
+end
diff --git a/vendor/github.com/99designs/keyring/array.go b/vendor/github.com/99designs/keyring/array.go
new file mode 100644
index 0000000..3179cb5
--- /dev/null
+++ b/vendor/github.com/99designs/keyring/array.go
@@ -0,0 +1,54 @@
+package keyring
+
+// ArrayKeyring is a mock/non-secure backend that meets the Keyring interface.
+// It is intended to be used to aid unit testing of code that relies on the package.
+// NOTE: Do not use in production code.
+type ArrayKeyring struct {
+ items map[string]Item
+}
+
+// NewArrayKeyring returns an ArrayKeyring, optionally constructed with an initial slice
+// of items.
+func NewArrayKeyring(initial []Item) *ArrayKeyring {
+ kr := &ArrayKeyring{}
+ for _, i := range initial {
+ _ = kr.Set(i)
+ }
+ return kr
+}
+
+// Get returns an Item matching Key.
+func (k *ArrayKeyring) Get(key string) (Item, error) {
+ if i, ok := k.items[key]; ok {
+ return i, nil
+ }
+ return Item{}, ErrKeyNotFound
+}
+
+// Set will store an item on the mock Keyring.
+func (k *ArrayKeyring) Set(i Item) error {
+ if k.items == nil {
+ k.items = map[string]Item{}
+ }
+ k.items[i.Key] = i
+ return nil
+}
+
+// Remove will delete an Item from the Keyring.
+func (k *ArrayKeyring) Remove(key string) error {
+ delete(k.items, key)
+ return nil
+}
+
+// Keys provides a slice of all Item keys on the Keyring.
+func (k *ArrayKeyring) Keys() ([]string, error) {
+ var keys = []string{}
+ for key := range k.items {
+ keys = append(keys, key)
+ }
+ return keys, nil
+}
+
+func (k *ArrayKeyring) GetMetadata(_ string) (Metadata, error) {
+ return Metadata{}, ErrMetadataNeedsCredentials
+}
diff --git a/vendor/github.com/99designs/keyring/config.go b/vendor/github.com/99designs/keyring/config.go
new file mode 100644
index 0000000..590af7c
--- /dev/null
+++ b/vendor/github.com/99designs/keyring/config.go
@@ -0,0 +1,58 @@
+package keyring
+
+// Config contains configuration for keyring.
+type Config struct {
+ // AllowedBackends is a whitelist of backend providers that can be used. Nil means all available.
+ AllowedBackends []BackendType
+
+ // ServiceName is a generic service name that is used by backends that support the concept
+ ServiceName string
+
+ // MacOSKeychainNameKeychainName is the name of the macOS keychain that is used
+ KeychainName string
+
+ // KeychainTrustApplication is whether the calling application should be trusted by default by items
+ KeychainTrustApplication bool
+
+ // KeychainSynchronizable is whether the item can be synchronized to iCloud
+ KeychainSynchronizable bool
+
+ // KeychainAccessibleWhenUnlocked is whether the item is accessible when the device is locked
+ KeychainAccessibleWhenUnlocked bool
+
+ // KeychainPasswordFunc is an optional function used to prompt the user for a password
+ KeychainPasswordFunc PromptFunc
+
+ // FilePasswordFunc is a required function used to prompt the user for a password
+ FilePasswordFunc PromptFunc
+
+ // FileDir is the directory that keyring files are stored in, ~/ is resolved to the users' home dir
+ FileDir string
+
+ // KeyCtlScope is the scope of the kernel keyring (either "user", "session", "process" or "thread")
+ KeyCtlScope string
+
+ // KeyCtlPerm is the permission mask to use for new keys
+ KeyCtlPerm uint32
+
+ // KWalletAppID is the application id for KWallet
+ KWalletAppID string
+
+ // KWalletFolder is the folder for KWallet
+ KWalletFolder string
+
+ // LibSecretCollectionName is the name collection in secret-service
+ LibSecretCollectionName string
+
+ // PassDir is the pass password-store directory, ~/ is resolved to the users' home dir
+ PassDir string
+
+ // PassCmd is the name of the pass executable
+ PassCmd string
+
+ // PassPrefix is a string prefix to prepend to the item path stored in pass
+ PassPrefix string
+
+ // WinCredPrefix is a string prefix to prepend to the key name
+ WinCredPrefix string
+}
diff --git a/vendor/github.com/99designs/keyring/docker-compose.yml b/vendor/github.com/99designs/keyring/docker-compose.yml
new file mode 100644
index 0000000..9020220
--- /dev/null
+++ b/vendor/github.com/99designs/keyring/docker-compose.yml
@@ -0,0 +1,7 @@
+version: "3.9"
+services:
+ keyring:
+ image: golang:1.19
+ volumes:
+ - .:/usr/local/src/keyring
+ working_dir: /usr/local/src/keyring
diff --git a/vendor/github.com/99designs/keyring/file.go b/vendor/github.com/99designs/keyring/file.go
new file mode 100644
index 0000000..0d25b57
--- /dev/null
+++ b/vendor/github.com/99designs/keyring/file.go
@@ -0,0 +1,180 @@
+package keyring
+
+import (
+ "encoding/json"
+ "fmt"
+ "os"
+ "path/filepath"
+ "time"
+
+ jose "github.com/dvsekhvalnov/jose2go"
+ "github.com/mtibben/percent"
+)
+
+func init() {
+ supportedBackends[FileBackend] = opener(func(cfg Config) (Keyring, error) {
+ return &fileKeyring{
+ dir: cfg.FileDir,
+ passwordFunc: cfg.FilePasswordFunc,
+ }, nil
+ })
+}
+
+var filenameEscape = func(s string) string {
+ return percent.Encode(s, "/")
+}
+var filenameUnescape = percent.Decode
+
+type fileKeyring struct {
+ dir string
+ passwordFunc PromptFunc
+ password string
+}
+
+func (k *fileKeyring) resolveDir() (string, error) {
+ if k.dir == "" {
+ return "", fmt.Errorf("No directory provided for file keyring")
+ }
+
+ dir, err := ExpandTilde(k.dir)
+ if err != nil {
+ return "", err
+ }
+
+ stat, err := os.Stat(dir)
+ if os.IsNotExist(err) {
+ err = os.MkdirAll(dir, 0700)
+ } else if err != nil && stat != nil && !stat.IsDir() {
+ err = fmt.Errorf("%s is a file, not a directory", dir)
+ }
+
+ return dir, err
+}
+
+func (k *fileKeyring) unlock() error {
+ dir, err := k.resolveDir()
+ if err != nil {
+ return err
+ }
+
+ if k.password == "" {
+ pwd, err := k.passwordFunc(fmt.Sprintf("Enter passphrase to unlock %q", dir))
+ if err != nil {
+ return err
+ }
+ k.password = pwd
+ }
+
+ return nil
+}
+
+func (k *fileKeyring) Get(key string) (Item, error) {
+ filename, err := k.filename(key)
+ if err != nil {
+ return Item{}, err
+ }
+
+ bytes, err := os.ReadFile(filename)
+ if os.IsNotExist(err) {
+ return Item{}, ErrKeyNotFound
+ } else if err != nil {
+ return Item{}, err
+ }
+
+ if err = k.unlock(); err != nil {
+ return Item{}, err
+ }
+
+ payload, _, err := jose.Decode(string(bytes), k.password)
+ if err != nil {
+ return Item{}, err
+ }
+
+ var decoded Item
+ err = json.Unmarshal([]byte(payload), &decoded)
+
+ return decoded, err
+}
+
+func (k *fileKeyring) GetMetadata(key string) (Metadata, error) {
+ filename, err := k.filename(key)
+ if err != nil {
+ return Metadata{}, err
+ }
+
+ stat, err := os.Stat(filename)
+ if os.IsNotExist(err) {
+ return Metadata{}, ErrKeyNotFound
+ } else if err != nil {
+ return Metadata{}, err
+ }
+
+ // For the File provider, all internal data is encrypted, not just the
+ // credentials. Thus we only have the timestamps. Return a nil *Item.
+ //
+ // If we want to change this ... how portable are extended file attributes
+ // these days? Would it break user expectations of the security model to
+ // leak data into those? I'm hesitant to do so.
+
+ return Metadata{
+ ModificationTime: stat.ModTime(),
+ }, nil
+}
+
+func (k *fileKeyring) Set(i Item) error {
+ bytes, err := json.Marshal(i)
+ if err != nil {
+ return err
+ }
+
+ if err = k.unlock(); err != nil {
+ return err
+ }
+
+ token, err := jose.Encrypt(string(bytes), jose.PBES2_HS256_A128KW, jose.A256GCM, k.password,
+ jose.Headers(map[string]interface{}{
+ "created": time.Now().String(),
+ }))
+ if err != nil {
+ return err
+ }
+
+ filename, err := k.filename(i.Key)
+ if err != nil {
+ return err
+ }
+ return os.WriteFile(filename, []byte(token), 0600)
+}
+
+func (k *fileKeyring) filename(key string) (string, error) {
+ dir, err := k.resolveDir()
+ if err != nil {
+ return "", err
+ }
+
+ return filepath.Join(dir, filenameEscape(key)), nil
+}
+
+func (k *fileKeyring) Remove(key string) error {
+ filename, err := k.filename(key)
+ if err != nil {
+ return err
+ }
+
+ return os.Remove(filename)
+}
+
+func (k *fileKeyring) Keys() ([]string, error) {
+ dir, err := k.resolveDir()
+ if err != nil {
+ return nil, err
+ }
+
+ var keys = []string{}
+ files, _ := os.ReadDir(dir)
+ for _, f := range files {
+ keys = append(keys, filenameUnescape(f.Name()))
+ }
+
+ return keys, nil
+}
diff --git a/vendor/github.com/99designs/keyring/keychain.go b/vendor/github.com/99designs/keyring/keychain.go
new file mode 100644
index 0000000..d4e634e
--- /dev/null
+++ b/vendor/github.com/99designs/keyring/keychain.go
@@ -0,0 +1,301 @@
+//go:build darwin && cgo
+// +build darwin,cgo
+
+package keyring
+
+import (
+ "errors"
+ "fmt"
+
+ gokeychain "github.com/99designs/go-keychain"
+)
+
+type keychain struct {
+ path string
+ service string
+
+ passwordFunc PromptFunc
+
+ isSynchronizable bool
+ isAccessibleWhenUnlocked bool
+ isTrusted bool
+}
+
+func init() {
+ supportedBackends[KeychainBackend] = opener(func(cfg Config) (Keyring, error) {
+ kc := &keychain{
+ service: cfg.ServiceName,
+ passwordFunc: cfg.KeychainPasswordFunc,
+
+ // Set the isAccessibleWhenUnlocked to the boolean value of
+ // KeychainAccessibleWhenUnlocked is a shorthand for setting the accessibility value.
+ // See: https://developer.apple.com/documentation/security/ksecattraccessiblewhenunlocked
+ isAccessibleWhenUnlocked: cfg.KeychainAccessibleWhenUnlocked,
+ }
+ if cfg.KeychainName != "" {
+ kc.path = cfg.KeychainName + ".keychain"
+ }
+ if cfg.KeychainTrustApplication {
+ kc.isTrusted = true
+ }
+ return kc, nil
+ })
+}
+
+func (k *keychain) Get(key string) (Item, error) {
+ query := gokeychain.NewItem()
+ query.SetSecClass(gokeychain.SecClassGenericPassword)
+ query.SetService(k.service)
+ query.SetAccount(key)
+ query.SetMatchLimit(gokeychain.MatchLimitOne)
+ query.SetReturnAttributes(true)
+ query.SetReturnData(true)
+
+ if k.path != "" {
+ // When we are querying, we don't create by default
+ query.SetMatchSearchList(gokeychain.NewWithPath(k.path))
+ }
+
+ debugf("Querying keychain for service=%q, account=%q, keychain=%q", k.service, key, k.path)
+ results, err := gokeychain.QueryItem(query)
+ if err == gokeychain.ErrorItemNotFound || len(results) == 0 {
+ debugf("No results found")
+ return Item{}, ErrKeyNotFound
+ }
+
+ if err != nil {
+ debugf("Error: %#v", err)
+ return Item{}, err
+ }
+
+ item := Item{
+ Key: key,
+ Data: results[0].Data,
+ Label: results[0].Label,
+ Description: results[0].Description,
+ }
+
+ debugf("Found item %q", results[0].Label)
+ return item, nil
+}
+
+func (k *keychain) GetMetadata(key string) (Metadata, error) {
+ query := gokeychain.NewItem()
+ query.SetSecClass(gokeychain.SecClassGenericPassword)
+ query.SetService(k.service)
+ query.SetAccount(key)
+ query.SetMatchLimit(gokeychain.MatchLimitOne)
+ query.SetReturnAttributes(true)
+ query.SetReturnData(false)
+ query.SetReturnRef(true)
+
+ debugf("Querying keychain for metadata of service=%q, account=%q, keychain=%q", k.service, key, k.path)
+ results, err := gokeychain.QueryItem(query)
+ if err == gokeychain.ErrorItemNotFound || len(results) == 0 {
+ debugf("No results found")
+ return Metadata{}, ErrKeyNotFound
+ } else if err != nil {
+ debugf("Error: %#v", err)
+ return Metadata{}, err
+ }
+
+ md := Metadata{
+ Item: &Item{
+ Key: key,
+ Label: results[0].Label,
+ Description: results[0].Description,
+ },
+ ModificationTime: results[0].ModificationDate,
+ }
+
+ debugf("Found metadata for %q", md.Item.Label)
+
+ return md, nil
+}
+
+func (k *keychain) updateItem(kc gokeychain.Keychain, kcItem gokeychain.Item, account string) error {
+ queryItem := gokeychain.NewItem()
+ queryItem.SetSecClass(gokeychain.SecClassGenericPassword)
+ queryItem.SetService(k.service)
+ queryItem.SetAccount(account)
+ queryItem.SetMatchLimit(gokeychain.MatchLimitOne)
+ queryItem.SetReturnAttributes(true)
+
+ if k.path != "" {
+ queryItem.SetMatchSearchList(kc)
+ }
+
+ results, err := gokeychain.QueryItem(queryItem)
+ if err != nil {
+ return fmt.Errorf("Failed to query keychain: %v", err)
+ }
+ if len(results) == 0 {
+ return errors.New("no results")
+ }
+
+ // Don't call SetAccess() as this will cause multiple prompts on update, even when we are not updating the AccessList
+ kcItem.SetAccess(nil)
+
+ if err := gokeychain.UpdateItem(queryItem, kcItem); err != nil {
+ return fmt.Errorf("Failed to update item in keychain: %v", err)
+ }
+
+ return nil
+}
+
+func (k *keychain) Set(item Item) error {
+ var kc gokeychain.Keychain
+
+ // when we are setting a value, we create or open
+ if k.path != "" {
+ var err error
+ kc, err = k.createOrOpen()
+ if err != nil {
+ return err
+ }
+ }
+
+ kcItem := gokeychain.NewItem()
+ kcItem.SetSecClass(gokeychain.SecClassGenericPassword)
+ kcItem.SetService(k.service)
+ kcItem.SetAccount(item.Key)
+ kcItem.SetLabel(item.Label)
+ kcItem.SetDescription(item.Description)
+ kcItem.SetData(item.Data)
+
+ if k.path != "" {
+ kcItem.UseKeychain(kc)
+ }
+
+ if k.isSynchronizable && !item.KeychainNotSynchronizable {
+ kcItem.SetSynchronizable(gokeychain.SynchronizableYes)
+ }
+
+ if k.isAccessibleWhenUnlocked {
+ kcItem.SetAccessible(gokeychain.AccessibleWhenUnlocked)
+ }
+
+ isTrusted := k.isTrusted && !item.KeychainNotTrustApplication
+
+ if isTrusted {
+ debugf("Keychain item trusts keyring")
+ kcItem.SetAccess(&gokeychain.Access{
+ Label: item.Label,
+ TrustedApplications: nil,
+ })
+ } else {
+ debugf("Keychain item doesn't trust keyring")
+ kcItem.SetAccess(&gokeychain.Access{
+ Label: item.Label,
+ TrustedApplications: []string{},
+ })
+ }
+
+ debugf("Adding service=%q, label=%q, account=%q, trusted=%v to osx keychain %q", k.service, item.Label, item.Key, isTrusted, k.path)
+
+ err := gokeychain.AddItem(kcItem)
+
+ if err == gokeychain.ErrorDuplicateItem {
+ debugf("Item already exists, updating")
+ err = k.updateItem(kc, kcItem, item.Key)
+ }
+
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (k *keychain) Remove(key string) error {
+ item := gokeychain.NewItem()
+ item.SetSecClass(gokeychain.SecClassGenericPassword)
+ item.SetService(k.service)
+ item.SetAccount(key)
+
+ if k.path != "" {
+ kc := gokeychain.NewWithPath(k.path)
+
+ if err := kc.Status(); err != nil {
+ if err == gokeychain.ErrorNoSuchKeychain {
+ return ErrKeyNotFound
+ }
+ return err
+ }
+
+ item.SetMatchSearchList(kc)
+ }
+
+ debugf("Removing keychain item service=%q, account=%q, keychain %q", k.service, key, k.path)
+ err := gokeychain.DeleteItem(item)
+ if err == gokeychain.ErrorItemNotFound {
+ return ErrKeyNotFound
+ }
+
+ return err
+}
+
+func (k *keychain) Keys() ([]string, error) {
+ query := gokeychain.NewItem()
+ query.SetSecClass(gokeychain.SecClassGenericPassword)
+ query.SetService(k.service)
+ query.SetMatchLimit(gokeychain.MatchLimitAll)
+ query.SetReturnAttributes(true)
+
+ if k.path != "" {
+ kc := gokeychain.NewWithPath(k.path)
+
+ if err := kc.Status(); err != nil {
+ if err == gokeychain.ErrorNoSuchKeychain {
+ return []string{}, nil
+ }
+ return nil, err
+ }
+
+ query.SetMatchSearchList(kc)
+ }
+
+ debugf("Querying keychain for service=%q, keychain=%q", k.service, k.path)
+ results, err := gokeychain.QueryItem(query)
+ if err != nil {
+ return nil, err
+ }
+
+ debugf("Found %d results", len(results))
+ accountNames := make([]string, len(results))
+ for idx, r := range results {
+ accountNames[idx] = r.Account
+ }
+
+ return accountNames, nil
+}
+
+func (k *keychain) createOrOpen() (gokeychain.Keychain, error) {
+ kc := gokeychain.NewWithPath(k.path)
+
+ debugf("Checking keychain status")
+ err := kc.Status()
+ if err == nil {
+ debugf("Keychain status returned nil, keychain exists")
+ return kc, nil
+ }
+
+ debugf("Keychain status returned error: %v", err)
+
+ if err != gokeychain.ErrorNoSuchKeychain {
+ return gokeychain.Keychain{}, err
+ }
+
+ if k.passwordFunc == nil {
+ debugf("Creating keychain %s with prompt", k.path)
+ return gokeychain.NewKeychainWithPrompt(k.path)
+ }
+
+ passphrase, err := k.passwordFunc("Enter passphrase for keychain")
+ if err != nil {
+ return gokeychain.Keychain{}, err
+ }
+
+ debugf("Creating keychain %s with provided password", k.path)
+ return gokeychain.NewKeychain(k.path, passphrase)
+}
diff --git a/vendor/github.com/99designs/keyring/keyctl.go b/vendor/github.com/99designs/keyring/keyctl.go
new file mode 100644
index 0000000..bd8018a
--- /dev/null
+++ b/vendor/github.com/99designs/keyring/keyctl.go
@@ -0,0 +1,327 @@
+//go:build linux
+// +build linux
+
+package keyring
+
+import (
+ "errors"
+ "fmt"
+ "strings"
+ "syscall"
+ "unsafe"
+
+ "golang.org/x/sys/unix"
+)
+
+//nolint:revive
+const (
+ KEYCTL_PERM_VIEW = uint32(1 << 0)
+ KEYCTL_PERM_READ = uint32(1 << 1)
+ KEYCTL_PERM_WRITE = uint32(1 << 2)
+ KEYCTL_PERM_SEARCH = uint32(1 << 3)
+ KEYCTL_PERM_LINK = uint32(1 << 4)
+ KEYCTL_PERM_SETATTR = uint32(1 << 5)
+ KEYCTL_PERM_ALL = uint32((1 << 6) - 1)
+
+ KEYCTL_PERM_OTHERS = 0
+ KEYCTL_PERM_GROUP = 8
+ KEYCTL_PERM_USER = 16
+ KEYCTL_PERM_PROCESS = 24
+)
+
+// GetPermissions constructs the permission mask from the elements.
+func GetPermissions(process, user, group, others uint32) uint32 {
+ perm := others << KEYCTL_PERM_OTHERS
+ perm |= group << KEYCTL_PERM_GROUP
+ perm |= user << KEYCTL_PERM_USER
+ perm |= process << KEYCTL_PERM_PROCESS
+
+ return perm
+}
+
+// GetKeyringIDForScope get the keyring ID for a given scope.
+func GetKeyringIDForScope(scope string) (int32, error) {
+ ringRef, err := getKeyringForScope(scope)
+ if err != nil {
+ return 0, err
+ }
+ id, err := unix.KeyctlGetKeyringID(int(ringRef), false)
+ return int32(id), err
+}
+
+type keyctlKeyring struct {
+ keyring int32
+ perm uint32
+}
+
+func init() {
+ supportedBackends[KeyCtlBackend] = opener(func(cfg Config) (Keyring, error) {
+ keyring := keyctlKeyring{}
+ if cfg.KeyCtlPerm > 0 {
+ keyring.perm = cfg.KeyCtlPerm
+ }
+
+ parent, err := getKeyringForScope(cfg.KeyCtlScope)
+ if err != nil {
+ return nil, fmt.Errorf("accessing %q keyring failed: %v", cfg.KeyCtlScope, err)
+ }
+
+ // Check for named keyrings
+ keyring.keyring = parent
+ if cfg.ServiceName != "" {
+ namedKeyring, err := keyctlSearch(parent, "keyring", cfg.ServiceName)
+ if err != nil {
+ if !errors.Is(err, syscall.ENOKEY) {
+ return nil, fmt.Errorf("opening named %q keyring failed: %v", cfg.KeyCtlScope, err)
+ }
+
+ // Keyring does not yet exist, create it
+ namedKeyring, err = keyring.createNamedKeyring(parent, cfg.ServiceName)
+ if err != nil {
+ return nil, fmt.Errorf("creating named %q keyring failed: %v", cfg.KeyCtlScope, err)
+ }
+ }
+ keyring.keyring = namedKeyring
+ }
+
+ return &keyring, nil
+ })
+}
+
+func (k *keyctlKeyring) Get(name string) (Item, error) {
+ key, err := keyctlSearch(k.keyring, "user", name)
+ if err != nil {
+ if errors.Is(err, syscall.ENOKEY) {
+ return Item{}, ErrKeyNotFound
+ }
+ return Item{}, err
+ }
+ // data, err := key.Get()
+ data, err := keyctlRead(key)
+ if err != nil {
+ return Item{}, err
+ }
+
+ item := Item{
+ Key: name,
+ Data: data,
+ }
+
+ return item, nil
+}
+
+// GetMetadata for pass returns an error indicating that it's unsupported for this backend.
+// TODO: We can deliver metadata different from the defined ones (e.g. permissions, expire-time, etc).
+func (k *keyctlKeyring) GetMetadata(_ string) (Metadata, error) {
+ return Metadata{}, ErrMetadataNotSupported
+}
+
+func (k *keyctlKeyring) Set(item Item) error {
+ if k.perm == 0 {
+ // Keep the default permissions (alswrv-----v------------)
+ _, err := keyctlAdd(k.keyring, "user", item.Key, item.Data)
+ return err
+ }
+
+ // By default we loose possession of the key in anything above the session keyring.
+ // Together with the default permissions (which cannot be changed during creation) we
+ // cannot change the permissions without possessing the key. Therefore, create the
+ // key in the session keyring, change permissions and then link to the target
+ // keyring and unlink from the intermediate keyring again.
+ key, err := keyctlAdd(unix.KEY_SPEC_SESSION_KEYRING, "user", item.Key, item.Data)
+ if err != nil {
+ return fmt.Errorf("adding key to session failed: %v", err)
+ }
+
+ if err := keyctlSetperm(key, k.perm); err != nil {
+ return fmt.Errorf("setting permission 0x%x failed: %v", k.perm, err)
+ }
+
+ if err := keyctlLink(k.keyring, key); err != nil {
+ return fmt.Errorf("linking key to keyring failed: %v", err)
+ }
+
+ if err := keyctlUnlink(unix.KEY_SPEC_SESSION_KEYRING, key); err != nil {
+ return fmt.Errorf("unlinking key from session failed: %v", err)
+ }
+
+ return nil
+}
+
+func (k *keyctlKeyring) Remove(name string) error {
+ key, err := keyctlSearch(k.keyring, "user", name)
+ if err != nil {
+ return ErrKeyNotFound
+ }
+
+ return keyctlUnlink(k.keyring, key)
+}
+
+func (k *keyctlKeyring) Keys() ([]string, error) {
+ results := []string{}
+
+ data, err := keyctlRead(k.keyring)
+ if err != nil {
+ return nil, fmt.Errorf("reading keyring failed: %v", err)
+ }
+ ids, err := keyctlConvertKeyBuffer(data)
+ if err != nil {
+ return nil, fmt.Errorf("converting raw keylist failed: %v", err)
+ }
+
+ for _, id := range ids {
+ info, err := keyctlDescribe(id)
+ if err != nil {
+ return nil, err
+ }
+ if info["type"] == "user" {
+ results = append(results, info["description"])
+ }
+ }
+
+ return results, nil
+}
+
+func (k *keyctlKeyring) createNamedKeyring(parent int32, name string) (int32, error) {
+ if k.perm == 0 {
+ // Keep the default permissions (alswrv-----v------------)
+ return keyctlAdd(parent, "keyring", name, nil)
+ }
+
+ // By default we loose possession of the keyring in anything above the session keyring.
+ // Together with the default permissions (which cannot be changed during creation) we
+ // cannot change the permissions without possessing the keyring. Therefore, create the
+ // keyring linked to the session keyring, change permissions and then link to the target
+ // keyring and unlink from the intermediate keyring again.
+ keyring, err := keyctlAdd(unix.KEY_SPEC_SESSION_KEYRING, "keyring", name, nil)
+ if err != nil {
+ return 0, fmt.Errorf("creating keyring failed: %v", err)
+ }
+
+ if err := keyctlSetperm(keyring, k.perm); err != nil {
+ return 0, fmt.Errorf("setting permission 0x%x failed: %v", k.perm, err)
+ }
+
+ if err := keyctlLink(k.keyring, keyring); err != nil {
+ return 0, fmt.Errorf("linking keyring failed: %v", err)
+ }
+
+ if err := keyctlUnlink(unix.KEY_SPEC_SESSION_KEYRING, keyring); err != nil {
+ return 0, fmt.Errorf("unlinking keyring from session failed: %v", err)
+ }
+
+ return keyring, nil
+}
+
+func getKeyringForScope(scope string) (int32, error) {
+ switch scope {
+ case "user":
+ return int32(unix.KEY_SPEC_USER_KEYRING), nil
+ case "usersession":
+ return int32(unix.KEY_SPEC_USER_SESSION_KEYRING), nil
+ case "group":
+ // Not yet implemented in the kernel
+ // return int32(unix.KEY_SPEC_GROUP_KEYRING)
+ return 0, fmt.Errorf("scope %q not yet implemented", scope)
+ case "session":
+ return int32(unix.KEY_SPEC_SESSION_KEYRING), nil
+ case "process":
+ return int32(unix.KEY_SPEC_PROCESS_KEYRING), nil
+ case "thread":
+ return int32(unix.KEY_SPEC_THREAD_KEYRING), nil
+ }
+ return 0, fmt.Errorf("unknown scope %q", scope)
+}
+
+func keyctlAdd(parent int32, keytype, key string, data []byte) (int32, error) {
+ id, err := unix.AddKey(keytype, key, data, int(parent))
+ if err != nil {
+ return 0, err
+ }
+ return int32(id), nil
+}
+
+func keyctlSearch(id int32, idtype, name string) (int32, error) {
+ key, err := unix.KeyctlSearch(int(id), idtype, name, 0)
+ if err != nil {
+ return 0, err
+ }
+ return int32(key), nil
+}
+
+func keyctlRead(id int32) ([]byte, error) {
+ var buffer []byte
+
+ for {
+ length, err := unix.KeyctlBuffer(unix.KEYCTL_READ, int(id), buffer, 0)
+ if err != nil {
+ return nil, err
+ }
+
+ // Return the buffer if it was large enough
+ if length <= len(buffer) {
+ return buffer[:length], nil
+ }
+
+ // Next try with a larger buffer
+ buffer = make([]byte, length)
+ }
+}
+
+func keyctlDescribe(id int32) (map[string]string, error) {
+ description, err := unix.KeyctlString(unix.KEYCTL_DESCRIBE, int(id))
+ if err != nil {
+ return nil, err
+ }
+ fields := strings.Split(description, ";")
+ if len(fields) < 1 {
+ return nil, fmt.Errorf("no data")
+ }
+
+ data := make(map[string]string)
+ names := []string{"type", "uid", "gid", "perm"} // according to keyctlDescribe(3) new fields are added at the end
+ data["description"] = fields[len(fields)-1] // according to keyctlDescribe(3) description is always last
+ for i, f := range fields[:len(fields)-1] {
+ if i >= len(names) {
+ // Do not stumble upon unknown fields
+ break
+ }
+ data[names[i]] = f
+ }
+
+ return data, nil
+}
+
+func keyctlLink(parent, child int32) error {
+ _, _, errno := syscall.Syscall(syscall.SYS_KEYCTL, uintptr(unix.KEYCTL_LINK), uintptr(child), uintptr(parent))
+ if errno != 0 {
+ return errno
+ }
+ return nil
+}
+
+func keyctlUnlink(parent, child int32) error {
+ _, _, errno := syscall.Syscall(syscall.SYS_KEYCTL, uintptr(unix.KEYCTL_UNLINK), uintptr(child), uintptr(parent))
+ if errno != 0 {
+ return errno
+ }
+ return nil
+}
+
+func keyctlSetperm(id int32, perm uint32) error {
+ return unix.KeyctlSetperm(int(id), perm)
+}
+
+func keyctlConvertKeyBuffer(buffer []byte) ([]int32, error) {
+ if len(buffer)%4 != 0 {
+ return nil, fmt.Errorf("buffer size %d not a multiple of 4", len(buffer))
+ }
+
+ results := make([]int32, 0, len(buffer)/4)
+ for i := 0; i < len(buffer); i += 4 {
+ // We need to case in host-native endianess here as this is what we get from the kernel.
+ r := *((*int32)(unsafe.Pointer(&buffer[i])))
+ results = append(results, r)
+ }
+ return results, nil
+}
diff --git a/vendor/github.com/99designs/keyring/keyring.go b/vendor/github.com/99designs/keyring/keyring.go
new file mode 100644
index 0000000..12161b7
--- /dev/null
+++ b/vendor/github.com/99designs/keyring/keyring.go
@@ -0,0 +1,134 @@
+// Package keyring provides a uniform API over a range of desktop credential storage engines.
+package keyring
+
+import (
+ "errors"
+ "log"
+ "time"
+)
+
+// BackendType is an identifier for a credential storage service.
+type BackendType string
+
+// All currently supported secure storage backends.
+const (
+ InvalidBackend BackendType = ""
+ SecretServiceBackend BackendType = "secret-service"
+ KeychainBackend BackendType = "keychain"
+ KeyCtlBackend BackendType = "keyctl"
+ KWalletBackend BackendType = "kwallet"
+ WinCredBackend BackendType = "wincred"
+ FileBackend BackendType = "file"
+ PassBackend BackendType = "pass"
+)
+
+// This order makes sure the OS-specific backends
+// are picked over the more generic backends.
+var backendOrder = []BackendType{
+ // Windows
+ WinCredBackend,
+ // MacOS
+ KeychainBackend,
+ // Linux
+ SecretServiceBackend,
+ KWalletBackend,
+ KeyCtlBackend,
+ // General
+ PassBackend,
+ FileBackend,
+}
+
+var supportedBackends = map[BackendType]opener{}
+
+// AvailableBackends provides a slice of all available backend keys on the current OS.
+func AvailableBackends() []BackendType {
+ b := []BackendType{}
+ for _, k := range backendOrder {
+ _, ok := supportedBackends[k]
+ if ok {
+ b = append(b, k)
+ }
+ }
+ return b
+}
+
+type opener func(cfg Config) (Keyring, error)
+
+// Open will open a specific keyring backend.
+func Open(cfg Config) (Keyring, error) {
+ if cfg.AllowedBackends == nil {
+ cfg.AllowedBackends = AvailableBackends()
+ }
+ debugf("Considering backends: %v", cfg.AllowedBackends)
+ for _, backend := range cfg.AllowedBackends {
+ if opener, ok := supportedBackends[backend]; ok {
+ openBackend, err := opener(cfg)
+ if err != nil {
+ debugf("Failed backend %s: %s", backend, err)
+ continue
+ }
+ return openBackend, nil
+ }
+ }
+ return nil, ErrNoAvailImpl
+}
+
+// Item is a thing stored on the keyring.
+type Item struct {
+ Key string
+ Data []byte
+ Label string
+ Description string
+
+ // Backend specific config
+ KeychainNotTrustApplication bool
+ KeychainNotSynchronizable bool
+}
+
+// Metadata is information about a thing stored on the keyring; retrieving
+// metadata must not require authentication. The embedded Item should be
+// filled in with an empty Data field.
+// It's allowed for Item to be a nil pointer, indicating that all we
+// have is the timestamps.
+type Metadata struct {
+ *Item
+ ModificationTime time.Time
+}
+
+// Keyring provides the uniform interface over the underlying backends.
+type Keyring interface {
+ // Returns an Item matching the key or ErrKeyNotFound
+ Get(key string) (Item, error)
+ // Returns the non-secret parts of an Item
+ GetMetadata(key string) (Metadata, error)
+ // Stores an Item on the keyring
+ Set(item Item) error
+ // Removes the item with matching key
+ Remove(key string) error
+ // Provides a slice of all keys stored on the keyring
+ Keys() ([]string, error)
+}
+
+// ErrNoAvailImpl is returned by Open when a backend cannot be found.
+var ErrNoAvailImpl = errors.New("Specified keyring backend not available")
+
+// ErrKeyNotFound is returned by Keyring Get when the item is not on the keyring.
+var ErrKeyNotFound = errors.New("The specified item could not be found in the keyring")
+
+// ErrMetadataNeedsCredentials is returned when Metadata is called against a
+// backend which requires credentials even to see metadata.
+var ErrMetadataNeedsCredentials = errors.New("The keyring backend requires credentials for metadata access")
+
+// ErrMetadataNotSupported is returned when Metadata is not available for the backend.
+var ErrMetadataNotSupported = errors.New("The keyring backend does not support metadata access")
+
+var (
+ // Debug specifies whether to print debugging output.
+ Debug bool
+)
+
+func debugf(pattern string, args ...interface{}) {
+ if Debug {
+ log.Printf("[keyring] "+pattern, args...)
+ }
+}
diff --git a/vendor/github.com/99designs/keyring/kwallet.go b/vendor/github.com/99designs/keyring/kwallet.go
new file mode 100644
index 0000000..771baba
--- /dev/null
+++ b/vendor/github.com/99designs/keyring/kwallet.go
@@ -0,0 +1,237 @@
+//go:build linux
+// +build linux
+
+package keyring
+
+import (
+ "encoding/json"
+ "os"
+
+ "github.com/godbus/dbus"
+)
+
+const (
+ dbusServiceName = "org.kde.kwalletd5"
+ dbusPath = "/modules/kwalletd5"
+)
+
+func init() {
+ if os.Getenv("DISABLE_KWALLET") == "1" {
+ return
+ }
+
+ // silently fail if dbus isn't available
+ _, err := dbus.SessionBus()
+ if err != nil {
+ return
+ }
+
+ supportedBackends[KWalletBackend] = opener(func(cfg Config) (Keyring, error) {
+ if cfg.ServiceName == "" {
+ cfg.ServiceName = "kdewallet"
+ }
+
+ if cfg.KWalletAppID == "" {
+ cfg.KWalletAppID = "keyring"
+ }
+
+ if cfg.KWalletFolder == "" {
+ cfg.KWalletFolder = "keyring"
+ }
+
+ wallet, err := newKwallet()
+ if err != nil {
+ return nil, err
+ }
+
+ ring := &kwalletKeyring{
+ wallet: *wallet,
+ name: cfg.ServiceName,
+ appID: cfg.KWalletAppID,
+ folder: cfg.KWalletFolder,
+ }
+
+ return ring, ring.openWallet()
+ })
+}
+
+type kwalletKeyring struct {
+ wallet kwalletBinding
+ name string
+ handle int32
+ appID string
+ folder string
+}
+
+func (k *kwalletKeyring) openWallet() error {
+ isOpen, err := k.wallet.IsOpen(k.handle)
+ if err != nil {
+ return err
+ }
+
+ if !isOpen {
+ handle, err := k.wallet.Open(k.name, 0, k.appID)
+ if err != nil {
+ return err
+ }
+ k.handle = handle
+ }
+
+ return nil
+}
+
+func (k *kwalletKeyring) Get(key string) (Item, error) {
+ err := k.openWallet()
+ if err != nil {
+ return Item{}, err
+ }
+
+ data, err := k.wallet.ReadEntry(k.handle, k.folder, key, k.appID)
+ if err != nil {
+ return Item{}, err
+ }
+ if len(data) == 0 {
+ return Item{}, ErrKeyNotFound
+ }
+
+ item := Item{}
+ err = json.Unmarshal(data, &item)
+ if err != nil {
+ return Item{}, err
+ }
+
+ return item, nil
+}
+
+// GetMetadata for kwallet returns an error indicating that it's unsupported
+// for this backend.
+//
+// The only APIs found around KWallet are for retrieving content, no indication
+// found in docs for methods to use to retrieve metadata without needing unlock
+// credentials.
+func (k *kwalletKeyring) GetMetadata(_ string) (Metadata, error) {
+ return Metadata{}, ErrMetadataNeedsCredentials
+}
+
+func (k *kwalletKeyring) Set(item Item) error {
+ err := k.openWallet()
+ if err != nil {
+ return err
+ }
+
+ data, err := json.Marshal(item)
+ if err != nil {
+ return err
+ }
+
+ err = k.wallet.WriteEntry(k.handle, k.folder, item.Key, data, k.appID)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (k *kwalletKeyring) Remove(key string) error {
+ err := k.openWallet()
+ if err != nil {
+ return err
+ }
+
+ err = k.wallet.RemoveEntry(k.handle, k.folder, key, k.appID)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (k *kwalletKeyring) Keys() ([]string, error) {
+ err := k.openWallet()
+ if err != nil {
+ return []string{}, err
+ }
+
+ entries, err := k.wallet.EntryList(k.handle, k.folder, k.appID)
+ if err != nil {
+ return []string{}, err
+ }
+
+ return entries, nil
+}
+
+func newKwallet() (*kwalletBinding, error) {
+ conn, err := dbus.SessionBus()
+ if err != nil {
+ return nil, err
+ }
+
+ return &kwalletBinding{
+ conn.Object(dbusServiceName, dbusPath),
+ }, nil
+}
+
+// Dumb Dbus bindings for kwallet bindings with types.
+type kwalletBinding struct {
+ dbus dbus.BusObject
+}
+
+// method bool org.kde.KWallet.isOpen(int handle)
+func (k *kwalletBinding) IsOpen(handle int32) (bool, error) {
+ call := k.dbus.Call("org.kde.KWallet.isOpen", 0, handle)
+ if call.Err != nil {
+ return false, call.Err
+ }
+
+ return call.Body[0].(bool), call.Err
+}
+
+// method int org.kde.KWallet.open(QString wallet, qlonglong wId, QString appid)
+func (k *kwalletBinding) Open(name string, wID int64, appid string) (int32, error) {
+ call := k.dbus.Call("org.kde.KWallet.open", 0, name, wID, appid)
+ if call.Err != nil {
+ return 0, call.Err
+ }
+
+ return call.Body[0].(int32), call.Err
+}
+
+// method QStringList org.kde.KWallet.entryList(int handle, QString folder, QString appid)
+func (k *kwalletBinding) EntryList(handle int32, folder string, appid string) ([]string, error) {
+ call := k.dbus.Call("org.kde.KWallet.entryList", 0, handle, folder, appid)
+ if call.Err != nil {
+ return []string{}, call.Err
+ }
+
+ return call.Body[0].([]string), call.Err
+}
+
+// method int org.kde.KWallet.writeEntry(int handle, QString folder, QString key, QByteArray value, QString appid)
+func (k *kwalletBinding) WriteEntry(handle int32, folder string, key string, value []byte, appid string) error {
+ call := k.dbus.Call("org.kde.KWallet.writeEntry", 0, handle, folder, key, value, appid)
+ if call.Err != nil {
+ return call.Err
+ }
+
+ return call.Err
+}
+
+// method int org.kde.KWallet.removeEntry(int handle, QString folder, QString key, QString appid)
+func (k *kwalletBinding) RemoveEntry(handle int32, folder string, key string, appid string) error {
+ call := k.dbus.Call("org.kde.KWallet.removeEntry", 0, handle, folder, key, appid)
+ if call.Err != nil {
+ return call.Err
+ }
+
+ return call.Err
+}
+
+// method QByteArray org.kde.KWallet.readEntry(int handle, QString folder, QString key, QString appid)
+func (k *kwalletBinding) ReadEntry(handle int32, folder string, key string, appid string) ([]byte, error) {
+ call := k.dbus.Call("org.kde.KWallet.readEntry", 0, handle, folder, key, appid)
+ if call.Err != nil {
+ return []byte{}, call.Err
+ }
+
+ return call.Body[0].([]byte), call.Err
+}
diff --git a/vendor/github.com/99designs/keyring/pass.go b/vendor/github.com/99designs/keyring/pass.go
new file mode 100644
index 0000000..5ca0bf0
--- /dev/null
+++ b/vendor/github.com/99designs/keyring/pass.go
@@ -0,0 +1,166 @@
+//go:build !windows
+// +build !windows
+
+package keyring
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strings"
+)
+
+func init() {
+ supportedBackends[PassBackend] = opener(func(cfg Config) (Keyring, error) {
+ var err error
+
+ pass := &passKeyring{
+ passcmd: cfg.PassCmd,
+ dir: cfg.PassDir,
+ prefix: cfg.PassPrefix,
+ }
+
+ if pass.passcmd == "" {
+ pass.passcmd = "pass"
+ }
+
+ if pass.dir == "" {
+ if passDir, found := os.LookupEnv("PASSWORD_STORE_DIR"); found {
+ pass.dir = passDir
+ } else {
+ homeDir, err := os.UserHomeDir()
+ if err != nil {
+ return nil, err
+ }
+ pass.dir = filepath.Join(homeDir, ".password-store")
+ }
+ }
+
+ pass.dir, err = ExpandTilde(pass.dir)
+ if err != nil {
+ return nil, err
+ }
+
+ // fail if the pass program is not available
+ _, err = exec.LookPath(pass.passcmd)
+ if err != nil {
+ return nil, errors.New("The pass program is not available")
+ }
+
+ return pass, nil
+ })
+}
+
+type passKeyring struct {
+ dir string
+ passcmd string
+ prefix string
+}
+
+func (k *passKeyring) pass(args ...string) *exec.Cmd {
+ cmd := exec.Command(k.passcmd, args...)
+ if k.dir != "" {
+ cmd.Env = append(os.Environ(), fmt.Sprintf("PASSWORD_STORE_DIR=%s", k.dir))
+ }
+ cmd.Stderr = os.Stderr
+
+ return cmd
+}
+
+func (k *passKeyring) Get(key string) (Item, error) {
+ if !k.itemExists(key) {
+ return Item{}, ErrKeyNotFound
+ }
+
+ name := filepath.Join(k.prefix, key)
+ cmd := k.pass("show", name)
+ output, err := cmd.Output()
+ if err != nil {
+ return Item{}, err
+ }
+
+ var decoded Item
+ err = json.Unmarshal(output, &decoded)
+
+ return decoded, err
+}
+
+func (k *passKeyring) GetMetadata(key string) (Metadata, error) {
+ return Metadata{}, nil
+}
+
+func (k *passKeyring) Set(i Item) error {
+ bytes, err := json.Marshal(i)
+ if err != nil {
+ return err
+ }
+
+ name := filepath.Join(k.prefix, i.Key)
+ cmd := k.pass("insert", "-m", "-f", name)
+ cmd.Stdin = strings.NewReader(string(bytes))
+
+ err = cmd.Run()
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (k *passKeyring) Remove(key string) error {
+ if !k.itemExists(key) {
+ return ErrKeyNotFound
+ }
+
+ name := filepath.Join(k.prefix, key)
+ cmd := k.pass("rm", "-f", name)
+ err := cmd.Run()
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (k *passKeyring) itemExists(key string) bool {
+ var path = filepath.Join(k.dir, k.prefix, key+".gpg")
+ _, err := os.Stat(path)
+
+ return err == nil
+}
+
+func (k *passKeyring) Keys() ([]string, error) {
+ var keys = []string{}
+ var path = filepath.Join(k.dir, k.prefix)
+
+ info, err := os.Stat(path)
+ if err != nil {
+ if os.IsNotExist(err) {
+ return keys, nil
+ }
+ return keys, err
+ }
+ if !info.IsDir() {
+ return keys, fmt.Errorf("%s is not a directory", path)
+ }
+
+ err = filepath.Walk(path, func(p string, info os.FileInfo, err error) error {
+ if err != nil {
+ return err
+ }
+
+ if !info.IsDir() && filepath.Ext(p) == ".gpg" {
+ name := strings.TrimPrefix(p, path)
+ if name[0] == os.PathSeparator {
+ name = name[1:]
+ }
+ keys = append(keys, name[:len(name)-4])
+ }
+ return nil
+ })
+
+ return keys, err
+}
diff --git a/vendor/github.com/99designs/keyring/prompt.go b/vendor/github.com/99designs/keyring/prompt.go
new file mode 100644
index 0000000..59ad81c
--- /dev/null
+++ b/vendor/github.com/99designs/keyring/prompt.go
@@ -0,0 +1,27 @@
+package keyring
+
+import (
+ "fmt"
+ "os"
+
+ "golang.org/x/term"
+)
+
+// PromptFunc is a function used to prompt the user for a password.
+type PromptFunc func(string) (string, error)
+
+func TerminalPrompt(prompt string) (string, error) {
+ fmt.Printf("%s: ", prompt)
+ b, err := term.ReadPassword(int(os.Stdin.Fd()))
+ if err != nil {
+ return "", err
+ }
+ fmt.Println()
+ return string(b), nil
+}
+
+func FixedStringPrompt(value string) PromptFunc {
+ return func(_ string) (string, error) {
+ return value, nil
+ }
+}
diff --git a/vendor/github.com/99designs/keyring/secretservice.go b/vendor/github.com/99designs/keyring/secretservice.go
new file mode 100644
index 0000000..2a1f9d9
--- /dev/null
+++ b/vendor/github.com/99designs/keyring/secretservice.go
@@ -0,0 +1,293 @@
+//go:build linux
+// +build linux
+
+package keyring
+
+import (
+ "encoding/hex"
+ "encoding/json"
+ "errors"
+
+ "strings"
+
+ "github.com/godbus/dbus"
+ "github.com/gsterjov/go-libsecret"
+)
+
+func init() {
+ // silently fail if dbus isn't available
+ _, err := dbus.SessionBus()
+ if err != nil {
+ return
+ }
+
+ supportedBackends[SecretServiceBackend] = opener(func(cfg Config) (Keyring, error) {
+ if cfg.ServiceName == "" {
+ cfg.ServiceName = "secret-service"
+ }
+ if cfg.LibSecretCollectionName == "" {
+ cfg.LibSecretCollectionName = cfg.ServiceName
+ }
+
+ service, err := libsecret.NewService()
+ if err != nil {
+ return &secretsKeyring{}, err
+ }
+
+ ring := &secretsKeyring{
+ name: cfg.LibSecretCollectionName,
+ service: service,
+ }
+
+ return ring, ring.openSecrets()
+ })
+}
+
+type secretsKeyring struct {
+ name string
+ service *libsecret.Service
+ collection *libsecret.Collection
+ session *libsecret.Session
+}
+
+var errCollectionNotFound = errors.New("The collection does not exist. Please add a key first")
+
+func decodeKeyringString(src string) string {
+ var dst strings.Builder
+ for i := 0; i < len(src); i++ {
+ if src[i] != '_' {
+ dst.WriteString(string(src[i]))
+ } else {
+ if i+3 > len(src) {
+ return src
+ }
+ hexstring := src[i+1 : i+3]
+ decoded, err := hex.DecodeString(hexstring)
+ if err != nil {
+ return src
+ }
+ dst.Write(decoded)
+ i += 2
+ }
+ }
+ return dst.String()
+}
+
+func (k *secretsKeyring) openSecrets() error {
+ session, err := k.service.Open()
+ if err != nil {
+ return err
+ }
+ k.session = session
+
+ // get the collection if it already exists
+ collections, err := k.service.Collections()
+ if err != nil {
+ return err
+ }
+
+ path := libsecret.DBusPath + "/collection/" + k.name
+
+ for _, collection := range collections {
+ if decodeKeyringString(string(collection.Path())) == path {
+ c := collection // fix variable into the local variable to ensure it's referenced correctly, see https://github.com/kyoh86/exportloopref
+ k.collection = &c
+ return nil
+ }
+ }
+
+ return nil
+}
+
+func (k *secretsKeyring) openCollection() error {
+ if err := k.openSecrets(); err != nil {
+ return err
+ }
+
+ if k.collection == nil {
+ return errCollectionNotFound
+ // return &secretsError{fmt.Sprintf(
+ // "The collection %q does not exist. Please add a key first",
+ // k.name,
+ // )}
+ }
+
+ return nil
+}
+
+func (k *secretsKeyring) Get(key string) (Item, error) {
+ if err := k.openCollection(); err != nil {
+ if err == errCollectionNotFound {
+ return Item{}, ErrKeyNotFound
+ }
+ return Item{}, err
+ }
+
+ items, err := k.collection.SearchItems(key)
+ if err != nil {
+ return Item{}, err
+ }
+
+ if len(items) == 0 {
+ return Item{}, ErrKeyNotFound
+ }
+
+ // use the first item whenever there are multiples
+ // with the same profile name
+ item := items[0]
+
+ locked, err := item.Locked()
+ if err != nil {
+ return Item{}, err
+ }
+
+ if locked {
+ if err := k.service.Unlock(item); err != nil {
+ return Item{}, err
+ }
+ }
+
+ secret, err := item.GetSecret(k.session)
+ if err != nil {
+ return Item{}, err
+ }
+
+ // pack the secret into the item
+ var ret Item
+ if err = json.Unmarshal(secret.Value, &ret); err != nil {
+ return Item{}, err
+ }
+
+ return ret, err
+}
+
+// GetMetadata for libsecret returns an error indicating that it's unsupported
+// for this backend.
+//
+// libsecret actually implements a metadata system which we could use, "Secret
+// Attributes"; I found no indication in documentation of anything like an
+// automatically maintained last-modification timestamp, so to use this we'd
+// need to have a SetMetadata API too. Which we're not yet doing, but feel
+// free to contribute patches.
+func (k *secretsKeyring) GetMetadata(key string) (Metadata, error) {
+ return Metadata{}, ErrMetadataNeedsCredentials
+}
+
+func (k *secretsKeyring) Set(item Item) error {
+ err := k.openSecrets()
+ if err != nil {
+ return err
+ }
+
+ // create the collection if it doesn't already exist
+ if k.collection == nil {
+ collection, err := k.service.CreateCollection(k.name)
+ if err != nil {
+ return err
+ }
+
+ k.collection = collection
+ }
+
+ if err := k.ensureCollectionUnlocked(); err != nil {
+ return err
+ }
+
+ // create the new item
+ data, err := json.Marshal(item)
+ if err != nil {
+ return err
+ }
+
+ secret := libsecret.NewSecret(k.session, []byte{}, data, "application/json")
+
+ if _, err := k.collection.CreateItem(item.Key, secret, true); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (k *secretsKeyring) Remove(key string) error {
+ if err := k.openCollection(); err != nil {
+ if err == errCollectionNotFound {
+ return ErrKeyNotFound
+ }
+ return err
+ }
+
+ items, err := k.collection.SearchItems(key)
+ if err != nil {
+ return err
+ }
+
+ // nothing to delete
+ if len(items) == 0 {
+ return nil
+ }
+
+ // we dont want to delete more than one anyway
+ // so just get the first item found
+ item := items[0]
+
+ locked, err := item.Locked()
+ if err != nil {
+ return err
+ }
+
+ if locked {
+ if err := k.service.Unlock(item); err != nil {
+ return err
+ }
+ }
+
+ if err := item.Delete(); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (k *secretsKeyring) Keys() ([]string, error) {
+ if err := k.openCollection(); err != nil {
+ if err == errCollectionNotFound {
+ return []string{}, nil
+ }
+ return nil, err
+ }
+ if err := k.ensureCollectionUnlocked(); err != nil {
+ return nil, err
+ }
+ items, err := k.collection.Items()
+ if err != nil {
+ return nil, err
+ }
+ keys := []string{}
+ for _, item := range items {
+ label, err := item.Label() // FIXME: err is being silently ignored
+ if err == nil {
+ keys = append(keys, label)
+ }
+ }
+ return keys, nil
+}
+
+// deleteCollection deletes the keyring's collection if it exists. This is mainly to support testing.
+func (k *secretsKeyring) deleteCollection() error {
+ if err := k.openCollection(); err != nil {
+ return err
+ }
+ return k.collection.Delete()
+}
+
+// unlock the collection if it's locked
+func (k *secretsKeyring) ensureCollectionUnlocked() error {
+ locked, err := k.collection.Locked()
+ if err != nil {
+ return err
+ }
+ if !locked {
+ return nil
+ }
+ return k.service.Unlock(k.collection)
+}
diff --git a/vendor/github.com/99designs/keyring/tilde.go b/vendor/github.com/99designs/keyring/tilde.go
new file mode 100644
index 0000000..c1847a6
--- /dev/null
+++ b/vendor/github.com/99designs/keyring/tilde.go
@@ -0,0 +1,22 @@
+package keyring
+
+import (
+ "os"
+ "path/filepath"
+ "strings"
+)
+
+var tildePrefix = string([]rune{'~', filepath.Separator})
+
+// ExpandTilde will expand tilde (~/ or ~\ depending on OS) for the user home directory.
+func ExpandTilde(dir string) (string, error) {
+ if strings.HasPrefix(dir, tildePrefix) {
+ homeDir, err := os.UserHomeDir()
+ if err != nil {
+ return "", err
+ }
+ dir = strings.Replace(dir, "~", homeDir, 1)
+ debugf("Expanded file dir to %s", dir)
+ }
+ return dir, nil
+}
diff --git a/vendor/github.com/99designs/keyring/wincred.go b/vendor/github.com/99designs/keyring/wincred.go
new file mode 100644
index 0000000..2ed43f2
--- /dev/null
+++ b/vendor/github.com/99designs/keyring/wincred.go
@@ -0,0 +1,98 @@
+//go:build windows
+// +build windows
+
+package keyring
+
+import (
+ "strings"
+ "syscall"
+
+ "github.com/danieljoos/wincred"
+)
+
+// ERROR_NOT_FOUND from https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--1000-1299-
+const elementNotFoundError = syscall.Errno(1168)
+
+type windowsKeyring struct {
+ name string
+ prefix string
+}
+
+func init() {
+ supportedBackends[WinCredBackend] = opener(func(cfg Config) (Keyring, error) {
+ name := cfg.ServiceName
+ if name == "" {
+ name = "default"
+ }
+
+ prefix := cfg.WinCredPrefix
+ if prefix == "" {
+ prefix = "keyring"
+ }
+
+ return &windowsKeyring{
+ name: name,
+ prefix: prefix,
+ }, nil
+ })
+}
+
+func (k *windowsKeyring) Get(key string) (Item, error) {
+ cred, err := wincred.GetGenericCredential(k.credentialName(key))
+ if err != nil {
+ if err == elementNotFoundError {
+ return Item{}, ErrKeyNotFound
+ }
+ return Item{}, err
+ }
+
+ item := Item{
+ Key: key,
+ Data: cred.CredentialBlob,
+ }
+
+ return item, nil
+}
+
+// GetMetadata for pass returns an error indicating that it's unsupported
+// for this backend.
+// TODO: This is a stub. Look into whether pass would support metadata in a usable way for keyring.
+func (k *windowsKeyring) GetMetadata(_ string) (Metadata, error) {
+ return Metadata{}, ErrMetadataNotSupported
+}
+
+func (k *windowsKeyring) Set(item Item) error {
+ cred := wincred.NewGenericCredential(k.credentialName(item.Key))
+ cred.CredentialBlob = item.Data
+ return cred.Write()
+}
+
+func (k *windowsKeyring) Remove(key string) error {
+ cred, err := wincred.GetGenericCredential(k.credentialName(key))
+ if err != nil {
+ if err == elementNotFoundError {
+ return ErrKeyNotFound
+ }
+ return err
+ }
+ return cred.Delete()
+}
+
+func (k *windowsKeyring) Keys() ([]string, error) {
+ results := []string{}
+
+ if creds, err := wincred.List(); err == nil {
+ for _, cred := range creds {
+ prefix := k.credentialName("")
+ if strings.HasPrefix(cred.TargetName, prefix) {
+ results = append(results, strings.TrimPrefix(cred.TargetName, prefix))
+ }
+ }
+ }
+
+ return results, nil
+}
+
+func (k *windowsKeyring) credentialName(key string) string {
+ return k.prefix + ":" + k.name + ":" + key
+}