# Go Keychain

A library for accessing the Keychain for macOS and iOS in Go (golang).

Requires macOS 10.9 or greater and iOS 8 or greater.

```go
import "github.com/keybase/go-keychain"
```


## 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)
```
