Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

gpg: add APIs for subkey interactions, revocations and key signing. #18

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
246 changes: 245 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ $ curl \
https://vault.example.com/v1/gpg/keys/my-imported-key
```

### Read key
### Read key by name

This endpoint returns information about a named GPG key.

Expand All @@ -117,6 +117,39 @@ $ curl \
#### Sample response


```json
{
"data": {
"exportable": false,
"fingerprint": "b0b7e7ca0e4ba1a631d15196ef3331150a45bc4d",
"public_key": "-----BEGIN PGP PUBLIC KEY BLOCK-----\n\nxsBNBFmZ6QQBCAC5QSHMKe6M9S2G9REo3sJuDPX2lm4ZMULXCvwcVekPYyUFWYI8\n...\nnTruSryJ4xYCydiJ1xkTedrkVxhh7hJKHA==\n=4fdy\n-----END PGP PUBLIC KEY BLOCK-----"
}
}
```

### Read key by fingerprint

This endpoint returns information about a key associated with the specified fingerprint.

| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `GET` | `/gpg/keys/id/:ID` | `200 application/json` |

#### Parameters

- `ID` `(string: <required>)` – Specifies the fingerprint of the key to read. This is specified as part of the URL.

#### Sample request

```
$ curl \
--header "X-Vault-Token: ..." \
https://vault.example.com/v1/gpg/keys/id/b0b7e7ca0e4ba1a631d15196ef3331150a45bc4d
```

#### Sample response


```json
{
"data": {
Expand Down Expand Up @@ -175,6 +208,138 @@ $ curl \
https://vault.example.com/v1/gpg/keys/my-key
```

### Create subkey

This endpoint creates a new RSA gpg subkey associated with a named key.

| Method | Path | Produces |
| :------- | :------------------------------ | :---------------------- |
| `POST` | `/gpg/subkeys/:name` | `200 application/json` |

#### Parameters

- `name` `(string: <required>)` – Specifies the name of the key to which subkey will be added. This is specified as part of the URL.

- `key_bits` `(int: 2048)` – Specifies the number of bits of the generated GPG subkey to use.

- `canSign` `(bool: false)` – Specifies if the subkey can be used for signing.

- `canEncrypt` `(bool: true)` – Specifies if the subkey can be used for encryption.

#### Sample Payload

```json
{
"canSign": true,
"canEncrypt": true,
"key_bits": 4096
}
```

#### Sample request

```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
https://vault.example.com/v1/gpg/subkeys/my-key
```

#### Sample response

```json
{
"data": {
"subkey-id": "b0b7e7ca0e4ba1a631d15196ef3331150a45bc4d",
"name": "my-key",
}
}
```

### Read subkey by fingerprint

This endpoint returns information about a specific subkey of a named key referenced using it's fingerprint.

| Method | Path | Produces |
| :------- | :----------------------------- | :--------------------- |
| `GET` | `/gpg/subkeys/:name/:subkeyID` | `200 application/json` |

#### Parameters

- `name` `(string: <required>)` – Specifies the name of the key to read. This is specified as part of the URL.

- `subkeyID` `(string: <required>)` – Specifies the fingerprint of the subkey to read. This is specified as part of the URL.

#### Sample request

```
$ curl \
--header "X-Vault-Token: ..." \
https://vault.example.com/v1/gpg/keys/my-key/b0b7e7ca0e4ba1a631d15196ef3331150a45bc4d
```

#### Sample response

```json
{
"data": {
"exportable": false,
"subkey": "-----BEGIN PGP PUBLIC KEY BLOCK-----\n\nxsBNBFmZ6QQBCAC5QSHMKe6M9S2G9REo3sJuDPX2lm4ZMULXCvwcVekPYyUFWYI8\n...\nnTruSryJ4xYCydiJ1xkTedrkVxhh7hJKHA==\n=4fdy\n-----END PGP PUBLIC KEY BLOCK-----"
}
}
```

### List keys

This endpoint returns a list of subkeys of a named GPG key. Only the fingerprint of the subkeys are returned.

| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `LIST` | `/gpg/subkeys/:name` | `200 application/json` |

#### Sample request

```
$ curl \
--header "X-Vault-Token: ..." \
--request LIST \
https://vault.example.com/v1/gpg/subkeys/my-key
```

#### Sample response

```json
{
"data": {
"keys": ["b0b7e7ca0e4ba1a631d15196ef3331150a45bc4d", "20b7e7fa124ba1a631d15196ef3331150a45bc4d"]
}
}
```

### Delete subkey

This endpoint deletes a subkey of a named GPG key referenced using it's fingerprint.

| Method | Path | Produces |
| :------- | :------------------------------ | :--------------------- |
| `DELETE` | `/gpg/subkeys/:name/:subkeyID` | `204 (empty body)` |

#### Parameters

- `name` `(string: <required>)` – Specifies the name of the key to delete. This is specified as part of the URL.

- `subkeyID` `(string: <required>)` – Specifies the fingerprint of the subkey to read. This is specified as part of the URL.

#### Sample request

```
$ curl \
--header "X-Vault-Token: ..." \
--request DELETE \
https://vault.example.com/v1/gpg/subkeys/my-key/b0b7e7ca0e4ba1a631d15196ef3331150a45bc4d
```

### Export key

This endpoint returns the named GPG key ASCII-armored.
Expand Down Expand Up @@ -419,3 +584,82 @@ $ curl \
}
}
```

### Revoke key or subkey

This endpoint revokes a named key or a specific subkey of a named key.

| Method | Path | Produces |
| :------- | :------------------------------ | :---------------------- |
| `POST` | `/gpg/revoke/:name/:subkeyID` | `204 (empty body)` |

#### Parameters

- `name` `(string: <required>)` – Specifies the name of the key to which subkey will ne added. This is specified as part of the URL.

- `subkeyID` `(string: "")` – Specifies the fingerprint of the subkey to be revoked. This is optional and specified as a part of the URL..

- `reasonCode` `(int: 2048)` – Specifies the uint8 reason code for key revocation as per RFC4880.

- `reasonText` `(string: "")` – Specifies the comment associated with the reason for revoking the key/subkey.

#### Sample Payload

```json
{
"reasonCode": 2,
"reasonText": "Key is compromised.",
}
```

#### Sample request for revoking key

```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
https://vault.example.com/v1/gpg/revoke/my-key
```

#### Sample request for revoking subkey

```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
https://vault.example.com/v1/gpg/revoke/my-key/b0b7e7ca0e4ba1a631d15196ef3331150a45bc4d
```

### Sign a key stored in vault

This endpoint signs a key stored on vault with another key to show trust in the signed key.

| Method | Path | Produces |
| :------- | :------------------------------ | :---------------------- |
| `POST` | `/gpg/signKey/:signedKey` | `204 (empty body)` |

#### Parameters

- `name` `(string: <required>)` – Specifies the name of the key that will be used for signing.

- `signedKey` `(string: "")` – Specifies the name of the key that needs to be signed. This is specified as a part of the URL.

#### Sample Payload

```json
{
"name": "signer-key",
}
```

#### Sample request for signing a key

```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
https://vault.example.com/v1/gpg/signkey/my-key
```
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ require (
github.com/hashicorp/vault/sdk v0.1.10
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c
)

replace golang.org/x/crypto => github.com/syadav2015/crypto v0.0.0-20190304101048-6881110aac18
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/syadav2015/crypto v0.0.0-20190304101048-6881110aac18 h1:j7cBfzrR6zkHXRzekBcD9SdKGL1/BdQ9MBJivqHqMrA=
github.com/syadav2015/crypto v0.0.0-20190304101048-6881110aac18/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c h1:Vj5n4GlwjmQteupaxJ9+0FNOmBrHfq7vN4btdGoDZgI=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
Expand Down
7 changes: 7 additions & 0 deletions gpg/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package gpg

import (
"context"
"sync"

"github.com/hashicorp/vault/sdk/framework"
"github.com/hashicorp/vault/sdk/logical"
Expand All @@ -23,9 +24,14 @@ func Backend() *backend {
Help: backendHelp,
Paths: []*framework.Path{
pathKeys(&b),
pathKeysByFingerprint(&b),
pathListKeys(&b),
pathListSubkeys(&b),
pathRevoke(&b),
pathSubkeys(&b),
pathExportKeys(&b),
pathSign(&b),
pathSignKey(&b),
pathVerify(&b),
pathDecrypt(&b),
pathShowSessionKey(&b),
Expand All @@ -43,6 +49,7 @@ func Backend() *backend {

type backend struct {
*framework.Backend
lock sync.RWMutex
}

const backendHelp = `
Expand Down
3 changes: 3 additions & 0 deletions gpg/path_decrypt.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,10 @@ func (b *backend) pathDecryptWrite(ctx context.Context, req *logical.Request, da
return logical.ErrorResponse(fmt.Sprintf("unsupported encoding format %s; must be \"base64\" or \"ascii-armor\"", format)), nil
}

// Acquire a read lock before the read operation.
b.lock.RLock()
keyEntry, err := b.key(ctx, req.Storage, data.Get("name").(string))
b.lock.RUnlock()
if err != nil {
return nil, err
}
Expand Down
3 changes: 3 additions & 0 deletions gpg/path_export.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ func pathExportKeys(b *backend) *framework.Path {

func (b *backend) pathExportKeyRead(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
name := data.Get("name").(string)
// Acquire a read lock before the read operation.
b.lock.RLock()
entry, err := b.key(ctx, req.Storage, name)
b.lock.RUnlock()
if err != nil {
return nil, err
}
Expand Down
Loading