Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 5 additions & 17 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,21 @@ jobs:
runs-on: ${{ matrix.os }}
name: Tests (${{ matrix.os }})
steps:
- uses: actions/checkout@v2.3.3
- uses: actions/setup-go@v2.1.2
- uses: actions/checkout@v2
- uses: actions/setup-go@v2
with:
go-version: '^1.15.0'
- name: Tests
run: "go test -race -coverprofile='coverage.txt' -covermode=atomic -v ./gpg/"
- uses: codecov/codecov-action@v1.0.13
- uses: codecov/codecov-action@v1
with:
token: ${{ secrets.CODECOV_TOKEN }}
static_analysis:
runs-on: ubuntu-20.04
name: Run static analysis and linting
steps:
- uses: actions/checkout@v2.3.3
- uses: actions/setup-go@v2.1.2
- uses: actions/checkout@v2
- uses: actions/setup-go@v2
with:
go-version: '^1.15.0'
- name: Go fmt
Expand All @@ -39,15 +39,3 @@ jobs:
run: go run honnef.co/go/tools/cmd/staticcheck -checks 'all,-ST1000' ./...
- name: Gosec
run: go run github.com/securego/gosec/cmd/gosec -exclude=G104 ./...
build_script:
runs-on: ubuntu-20.04
name: Build across all supported architectures

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you decide building on all architectures was slow or unreliable? Or not supported by the library?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Too slow, and we don't release the built binaries anyway, so...

steps:
- uses: actions/[email protected]
- uses: actions/[email protected]
with:
go-version: '^1.15.0'
- name: Build
run: ./scripts/build.sh
- name: Display checksum
run: find ./pkg/ -name '*.sha256sum' -exec cat {} \;
2 changes: 1 addition & 1 deletion .github/workflows/CodeQL_Analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:

steps:
- name: Checkout repository
uses: actions/checkout@v2.3.3
uses: actions/checkout@v2
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.
Expand Down
60 changes: 6 additions & 54 deletions docs/http-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Unless otherwise specified, when we say "key" here, assume that it is a master k
* [List Subkeys](#list-subkeys)
* [Delete Subkey](#delete-subkey)
* [Sign Data with Subkey](#sign-data-with-subkey)
* [Verify Signed Data with Subkey](#verify-signed-data-with-subkey)

## Master Keys

Expand Down Expand Up @@ -228,6 +229,8 @@ named master key and the specified hash algorithm.
- `base64`
- `ascii-armor`

- `expires` `(int: 31536000)` – Specifies the number of seconds from the creation time (now) after which the signature expires. If the number is zero, then the signature never expires.

- `input` `(string: <required>)` – Specifies the **base64 encoded** input data.

#### Sample payload
Expand Down Expand Up @@ -549,59 +552,8 @@ $ curl \

### Sign Data with Subkey

This endpoint returns a signature of the given input data using the given subkey associated with the given master key, and the specified hash algorithm.

| Method | Path | Produces |
| :------- | :---------------------------------------------- | :--------------------- |
| `POST` | `/gpg/sign/:name/subkeys/:key_id/(/:algorithm)` | `200 application/json` |

#### Parameters

- `name` `(string: <required>)` – Specifies the name of the master key with which the subkey is associated. This is specified as part of the URL.

- `key_id` `(string: <required>)` – Specifies the Key ID of the subkey. This is specified as part of the URL.

- `algorithm` `(string: "sha2-256")` – Specifies the hash algorithm to use. This can also be specified as part of the URL.
Valid algorithms are:

- `sha2-224`
- `sha2-256`
- `sha2-384`
- `sha2-512`

- `format` `(string: "base64")` – Specifies the encoding format for the returned signature. Valid encoding format are:

- `base64`
- `ascii-armor`

- `input` `(string: <required>)` – Specifies the **base64 encoded** input data.

- `expires` `(int: 31536000)` – Specifies the number of seconds from the creation time (now) after which the signature expires. If the number is zero, then the signature never expires.

#### Sample payload

```json
{
"input": "QWxwYWNhCg=="
}
```

#### Sample request

```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
https://vault.example.com/v1/gpg/keys/my-key/subkeys/6D0A9151F25B6B85
```
Use [Sign Data](#sign-data) to sign data with the _first_ available signing subkey.

#### Sample response
### Verify Signed Data with Subkey

```json
{
"data": {
"signature": "wsBcBAABCgAQBQJZme+7CRBr/Ej4JtFtLAAA8QcIACLtMWlH5860njpQsJZDIzH3T4mz2397lsd9/hsFDAQXEimuLKWmNdJsTEWXKGx1fvW+r6LEPs8HOLdzOMz2tq6M0WvgzHeWOFdEYmCapUlS68m0GnSFHIAFkq2fMVFHdTTmiLNuZwd+meEPL48hUO8QoGZLhS9IO+xOIisJWP+YIfiZBhmqhz0nVX3CnIzDZWAeJCE9TFGPHjFVNHXKN/IA+pdY4ntU1VOxmKCDqtu6qOrFR3ZghJBrDpDqiMHYmnJZ2AGPDVPKoAorvrLkR7eXNX71yRcutqohqS+xt6nGak2OF7UKwgj5bjk1y44lROFi8aVW4LEX7Jmt+2qwWBg="
}
}
```
Use [Verify Signed Data](#verify-signed-data) to verify data signed with a subkey.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ require (
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc
)

replace golang.org/x/crypto => github.com/trishankatdatadog/crypto v0.0.0-20201003183309-2dc30c1240f0
replace golang.org/x/crypto => github.com/ProtonMail/crypto v0.0.0-20201016191319-576ad9c42ffa
17 changes: 2 additions & 15 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/ProtonMail/crypto v0.0.0-20201016191319-576ad9c42ffa h1:RnTRazSOTTBw0S2D0dK6pbC0uEYtHFdO3YQo8lI6coc=
github.com/ProtonMail/crypto v0.0.0-20201016191319-576ad9c42ffa/go.mod h1:Pxr7w4gA2ikI4sWyYwEffm+oew1WAJHzG1SiDpQMkrI=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310 h1:BUAU3CGlLvorLI26FmByPp2eC2qla6E1Tw+scpcg/to=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
Expand Down Expand Up @@ -117,22 +119,7 @@ github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/trishankatdatadog/crypto v0.0.0-20200930184313-2fa05e1d64ba h1:kwvi9bzYoclqN4C0A+DmemdxArNxXYYMzYsyAkoAK/k=
github.com/trishankatdatadog/crypto v0.0.0-20200930184313-2fa05e1d64ba/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
github.com/trishankatdatadog/crypto v0.0.0-20200930202336-a58df677f9f7 h1:KJEkUMVn4RDdWMYeKSYirNcE3vnzAWgUZqxJGtwp/ck=
github.com/trishankatdatadog/crypto v0.0.0-20200930202336-a58df677f9f7/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
github.com/trishankatdatadog/crypto v0.0.0-20201001021522-2667566a2840 h1:QHtS9qYK36/EyFUyg/wsmX1oYahupaygrTcUa+c4znM=
github.com/trishankatdatadog/crypto v0.0.0-20201001021522-2667566a2840/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
github.com/trishankatdatadog/crypto v0.0.0-20201001134537-b3053ccd948b h1:fM/kxj4xD+BJVMKDxY6tRYBHi62bgjxX2umZX8fiRgs=
github.com/trishankatdatadog/crypto v0.0.0-20201001134537-b3053ccd948b/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
github.com/trishankatdatadog/crypto v0.0.0-20201003032211-b00c698fd90d h1:DjFrb66g7w/EqpIaCHXrpDuw7C/zSQ1WPzqjKClexYQ=
github.com/trishankatdatadog/crypto v0.0.0-20201003032211-b00c698fd90d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
github.com/trishankatdatadog/crypto v0.0.0-20201003183309-2dc30c1240f0 h1:6HHfrwzwho5oBfTZ4fRP+m6nbwvTUtQr6nksvMfkKts=
github.com/trishankatdatadog/crypto v0.0.0-20201003183309-2dc30c1240f0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
Expand Down
1 change: 0 additions & 1 deletion gpg/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ func Backend() *backend {
Help: backendHelp,
Paths: []*framework.Path{
// List more specific subkey routes first.
pathSubkeysSign(&b),
pathSubkeysRD(&b),
pathSubkeysCL(&b),
pathKeys(&b),
Expand Down
92 changes: 52 additions & 40 deletions gpg/backend_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,19 +53,24 @@ func TestBackend_Signing(t *testing.T) {
b, storage := getTestBackend(t)

keyData := map[string]interface{}{
"real_name": "Vault",
"email": "[email protected]",
"comment": "Comment",
"key_bits": 4096,
"exportable": true,
"real_name": "Vault",
"email": "[email protected]",
"comment": "Comment",
"key_bits": 2048,
}
base64InputData := "bXkgc2VjcmV0IGRhdGEK"
otherBase64InputData := "c29tZSBvdGhlciBkYXRhCg=="
masterName := "test"
testAccStepCreateKey(t, b, storage, masterName, keyData, false)
// NOTE: choose expiration time long enough that the key does not expire by the time we are done creating it.
sigExpiresAfterSeconds := 6
keyExpiresAfterSeconds := 2 * sigExpiresAfterSeconds

// NOTE: every test uses a separate master key so that parallel tests do not affect each other.
t.Run("signing with master key", func(t *testing.T) {
signature := testAccStepSign(t, b, storage, masterName, base64InputData)
masterName := "master1"
testAccStepCreateKey(t, b, storage, masterName, keyData, false)
signature := testAccStepSign(t, b, storage, masterName, map[string]interface{}{
"input": base64InputData,
})
testAccStepVerify(t, b, storage, masterName, map[string]interface{}{
"input": base64InputData,
"signature": signature,
Expand All @@ -77,10 +82,12 @@ func TestBackend_Signing(t *testing.T) {
})

t.Run("signing with subkey", func(t *testing.T) {
masterName := "master2"
testAccStepCreateKey(t, b, storage, masterName, keyData, false)
subkeyRespData := testAccStepCreateSubkey(t, b, storage, masterName, map[string]interface{}{})
subkeyID := subkeyRespData["key_id"].(string)
testAccStepReadSubkey(t, b, storage, masterName, subkeyID)
signature := testAccStepSignWithSubkey(t, b, storage, masterName, subkeyID, map[string]interface{}{
signature := testAccStepSign(t, b, storage, masterName, map[string]interface{}{
"input": base64InputData,
})
testAccStepVerify(t, b, storage, masterName, map[string]interface{}{
Expand All @@ -93,24 +100,44 @@ func TestBackend_Signing(t *testing.T) {
}, false)
})

t.Run("signing after expiration", func(t *testing.T) {
expireAfterSeconds := 1
subkeyRespData := testAccStepCreateSubkey(t, b, storage, masterName, map[string]interface{}{
"expires": expireAfterSeconds, // 1 second
t.Run("verification after key expiration", func(t *testing.T) {
masterName := "master3"
testAccStepCreateKey(t, b, storage, masterName, keyData, false)
testAccStepCreateSubkey(t, b, storage, masterName, map[string]interface{}{
"expires": keyExpiresAfterSeconds,
})
subkeyID := subkeyRespData["key_id"].(string)
testAccStepReadSubkey(t, b, storage, masterName, subkeyID)
signature := testAccStepSignWithSubkey(t, b, storage, masterName, subkeyID, map[string]interface{}{
signature := testAccStepSign(t, b, storage, masterName, map[string]interface{}{
"input": base64InputData,
"expires": expireAfterSeconds,
"expires": 0, // signature does not expire
})
testAccStepVerify(t, b, storage, masterName, map[string]interface{}{
"input": base64InputData,
"signature": signature,
}, true)
// Sleep for long enough that the subkey *should have* expired
time.Sleep(time.Duration(keyExpiresAfterSeconds) * time.Second)
testAccStepVerify(t, b, storage, masterName, map[string]interface{}{
"input": base64InputData,
"signature": signature,
}, false)
})

// TODO
t.Skipf("re-enable this test once keys and/or signatures expire")

t.Run("verification after sig expiration", func(t *testing.T) {
masterName := "master4"
testAccStepCreateKey(t, b, storage, masterName, keyData, false)
testAccStepCreateSubkey(t, b, storage, masterName, map[string]interface{}{
"expires": 0, // subkey does not expire
})
signature := testAccStepSign(t, b, storage, masterName, map[string]interface{}{
"input": base64InputData,
"expires": sigExpiresAfterSeconds,
})
testAccStepVerify(t, b, storage, masterName, map[string]interface{}{
"input": base64InputData,
"signature": signature,
}, true)
// Sleep for long enough that the subkey and the signature *should have* expired
time.Sleep(time.Duration(3*expireAfterSeconds) * time.Second)

time.Sleep(time.Duration(sigExpiresAfterSeconds) * time.Second)
testAccStepVerify(t, b, storage, masterName, map[string]interface{}{
"input": base64InputData,
"signature": signature,
Expand Down Expand Up @@ -298,10 +325,10 @@ func testAccStepCreateSubkey(t *testing.T, b logical.Backend, s logical.Storage,
return resp.Data
}

func testAccStepReadSubkey(t *testing.T, b logical.Backend, s logical.Storage, masterName string, subkeyName string) {
func testAccStepReadSubkey(t *testing.T, b logical.Backend, s logical.Storage, masterName string, subkeyID string) {
resp, err := b.HandleRequest(context.Background(), &logical.Request{
Operation: logical.ReadOperation,
Path: "keys/" + masterName + "/subkeys/" + subkeyName,
Path: "keys/" + masterName + "/subkeys/" + subkeyID,
Storage: s,
})
if err != nil {
Expand All @@ -320,25 +347,10 @@ func testAccStepReadSubkey(t *testing.T, b logical.Backend, s logical.Storage, m
}
}

func testAccStepSign(t *testing.T, b logical.Backend, s logical.Storage, masterName string, base64Input string) string {
func testAccStepSign(t *testing.T, b logical.Backend, s logical.Storage, masterName string, signData map[string]interface{}) string {
resp, err := b.HandleRequest(context.Background(), &logical.Request{
Operation: logical.UpdateOperation,
Path: "sign/" + masterName,
Data: map[string]interface{}{
"input": base64Input,
},
Storage: s,
})
if err != nil {
t.Error(err)
}
return resp.Data["signature"].(string)
}

func testAccStepSignWithSubkey(t *testing.T, b logical.Backend, s logical.Storage, masterName string, subkeyName string, signData map[string]interface{}) string {
resp, err := b.HandleRequest(context.Background(), &logical.Request{
Operation: logical.UpdateOperation,
Path: "sign/" + masterName + "/subkeys/" + subkeyName,
Data: signData,
Storage: s,
})
Expand Down
Loading