mirror of https://github.com/helm/helm
fix(provenance): support GnuPG keybox (pubring.kbx) keyrings (#32281)
* fix(provenance): support GnuPG keybox (pubring.kbx) keyrings Starting with GnuPG 2.1, file-backed public keyrings can use ~/.gnupg/pubring.kbx instead of the legacy pubring.gpg. Helm only read the legacy format, so chart and plugin verification failed on installations using the file-backed keybox. Make the keyring loader format-aware: - GnuPG keybox (pubring.kbx): extract OpenPGP keyblocks from the keybox container without adding a dependency. Skip ephemeral blobs, matching GnuPG's own read behavior. - ASCII-armored keyrings: load single or concatenated exports. - Legacy binary packet streams (pubring.gpg): retain the existing path. defaultKeyring() falls back to pubring.kbx when pubring.gpg is absent. pubring.gpg keeps precedence when both files exist. This change covers file-backed public keyrings. It does not read the SQLite database used by keyboxd, which needs a separate design. Related to #31836 Signed-off-by: Ruslan Shaydullin <shaydullin.r.d@outlook.com> * fix(provenance): treat only not-exist as keyring absence in defaultKeyring A stat error other than 'not exist' (e.g. a permission problem) meant the file may well be present, but defaultKeyring() skipped past it: an unreadable pubring.gpg silently lost precedence to pubring.kbx, and the surfaced error could point at the wrong file. Treat only fs.ErrNotExist as absence. For any other stat error, return that path unchanged so the real error surfaces when the keyring is opened. Signed-off-by: Ruslan Shaydullin <shaydullin.r.d@outlook.com> --------- Signed-off-by: Ruslan Shaydullin <shaydullin.r.d@outlook.com> Co-authored-by: George Jenkins <gvjenkins@gmail.com>pull/32568/head
parent
e9b85e4de1
commit
67d54fd880
@ -0,0 +1,113 @@
|
||||
/*
|
||||
Copyright The Helm Authors.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package provenance
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// GnuPG 2.1+ can store file-backed public keys in a "keybox" (pubring.kbx),
|
||||
// a container that interleaves OpenPGP keyblocks with GnuPG-specific
|
||||
// metadata. It is not an OpenPGP packet stream, so it cannot be handed to
|
||||
// openpgp.ReadKeyRing directly. A keybox is a sequence of blobs, each starting
|
||||
// with:
|
||||
//
|
||||
// byte 0..3 u32 blob length, big endian, including this header
|
||||
// byte 4 u8 blob type (0 empty, 1 header, 2 OpenPGP, 3 X.509)
|
||||
// byte 5 u8 blob version
|
||||
//
|
||||
// The first blob is a header carrying the "KBXf" magic at offset 8. OpenPGP
|
||||
// blobs (type 2) record where the raw keyblock lives inside the blob:
|
||||
//
|
||||
// byte 8..11 u32 keyblock offset, relative to the blob start
|
||||
// byte 12..15 u32 keyblock length
|
||||
//
|
||||
// Reference: kbx/keybox-blob.c in the GnuPG source tree.
|
||||
const (
|
||||
kbxBlobTypeHeader = 1
|
||||
kbxBlobTypeOpenPGP = 2
|
||||
|
||||
// kbxBlobFlagEphemeral marks a blob GnuPG considers not (yet) part of
|
||||
// the keyring, e.g. written during an interrupted keyserver operation
|
||||
// (KEYBOX_FLAG_BLOB_EPHEMERAL in kbx/keybox.h). GnuPG skips such blobs
|
||||
// on every normal read (kbx/keybox-search.c), and so do we.
|
||||
kbxBlobFlagEphemeral = 0x0002
|
||||
|
||||
// kbxMinBlobLen covers the length and type fields present in every blob.
|
||||
kbxMinBlobLen = 5
|
||||
|
||||
// kbxOpenPGPHeaderLen is how much of an OpenPGP blob header must be
|
||||
// present for the flags, keyblock offset and keyblock length fields to
|
||||
// be readable.
|
||||
kbxOpenPGPHeaderLen = 16
|
||||
)
|
||||
|
||||
// isKeybox reports whether data looks like a GnuPG keybox (pubring.kbx)
|
||||
// image, identified by the "KBXf" magic in the mandatory first header blob.
|
||||
func isKeybox(data []byte) bool {
|
||||
return len(data) >= 12 && data[4] == kbxBlobTypeHeader && string(data[8:12]) == "KBXf"
|
||||
}
|
||||
|
||||
// isArmored reports whether data looks like an ASCII-armored keyring, as
|
||||
// produced by `gpg --export --armor`.
|
||||
func isArmored(data []byte) bool {
|
||||
return bytes.HasPrefix(bytes.TrimSpace(data), []byte("-----BEGIN PGP"))
|
||||
}
|
||||
|
||||
// keyboxPublicKeys extracts the OpenPGP keyblocks embedded in a keybox image
|
||||
// and returns them concatenated, ready for openpgp.ReadKeyRing. Blobs of any
|
||||
// other type (header, X.509, empty) are skipped, as are blobs flagged
|
||||
// ephemeral, which GnuPG itself ignores when reading the keyring. Malformed
|
||||
// input yields an error, never a panic.
|
||||
func keyboxPublicKeys(data []byte) ([]byte, error) {
|
||||
var keyblocks bytes.Buffer
|
||||
for offset := 0; offset < len(data); {
|
||||
rest := data[offset:]
|
||||
if len(rest) < kbxMinBlobLen {
|
||||
return nil, fmt.Errorf("truncated blob header at offset %d", offset)
|
||||
}
|
||||
blobLen := binary.BigEndian.Uint32(rest)
|
||||
if blobLen < kbxMinBlobLen {
|
||||
return nil, fmt.Errorf("invalid blob length %d at offset %d", blobLen, offset)
|
||||
}
|
||||
if uint64(blobLen) > uint64(len(rest)) {
|
||||
return nil, fmt.Errorf("blob at offset %d has length %d exceeding the %d remaining bytes", offset, blobLen, len(rest))
|
||||
}
|
||||
blob := rest[:blobLen]
|
||||
if blob[4] == kbxBlobTypeOpenPGP {
|
||||
if len(blob) < kbxOpenPGPHeaderLen {
|
||||
return nil, fmt.Errorf("OpenPGP blob at offset %d is too short", offset)
|
||||
}
|
||||
flags := binary.BigEndian.Uint16(blob[6:])
|
||||
keyblockOffset := binary.BigEndian.Uint32(blob[8:])
|
||||
keyblockLen := binary.BigEndian.Uint32(blob[12:])
|
||||
if uint64(keyblockOffset)+uint64(keyblockLen) > uint64(len(blob)) {
|
||||
return nil, fmt.Errorf("OpenPGP blob at offset %d has an out-of-range keyblock", offset)
|
||||
}
|
||||
if flags&kbxBlobFlagEphemeral == 0 {
|
||||
keyblocks.Write(blob[keyblockOffset : keyblockOffset+keyblockLen])
|
||||
}
|
||||
}
|
||||
offset += int(blobLen)
|
||||
}
|
||||
if keyblocks.Len() == 0 {
|
||||
return nil, errors.New("keybox contains no OpenPGP keys")
|
||||
}
|
||||
return keyblocks.Bytes(), nil
|
||||
}
|
||||
@ -0,0 +1,188 @@
|
||||
/*
|
||||
Copyright The Helm Authors.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package provenance
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const (
|
||||
// testKeybox is a GnuPG keybox (pubring.kbx) containing the helm-test
|
||||
// public key. Regenerate with testdata/regen-keyring-formats.sh.
|
||||
testKeybox = "testdata/helm-test-key.kbx"
|
||||
|
||||
// testMixedKeybox is a keybox containing the RSA and Ed25519 test keys.
|
||||
testMixedKeybox = "testdata/helm-mixed-keyring.kbx"
|
||||
|
||||
// testArmoredPubfile is the ASCII-armored export of the helm-test key.
|
||||
testArmoredPubfile = "testdata/helm-test-key.asc"
|
||||
|
||||
// testMultiBlockArmored is two concatenated single-key armored exports
|
||||
// (cat key1.asc key2.asc), covering the RSA and Ed25519 test keys.
|
||||
testMultiBlockArmored = "testdata/helm-mixed-keyring.asc"
|
||||
)
|
||||
|
||||
func TestIsKeybox(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
file string
|
||||
want bool
|
||||
}{
|
||||
{"keybox", testKeybox, true},
|
||||
{"mixed keybox", testMixedKeybox, true},
|
||||
{"legacy binary keyring", testPubfile, false},
|
||||
{"armored keyring", testArmoredPubfile, false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
data, err := os.ReadFile(tt.file)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tt.want, isKeybox(data))
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("degenerate inputs", func(t *testing.T) {
|
||||
assert.False(t, isKeybox(nil))
|
||||
assert.False(t, isKeybox([]byte{}))
|
||||
assert.False(t, isKeybox([]byte("KBXf")))
|
||||
assert.False(t, isKeybox([]byte("garbage that is longer than twelve bytes")))
|
||||
})
|
||||
}
|
||||
|
||||
func TestIsArmored(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
file string
|
||||
want bool
|
||||
}{
|
||||
{"armored keyring", testArmoredPubfile, true},
|
||||
{"legacy binary keyring", testPubfile, false},
|
||||
{"keybox", testKeybox, false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
data, err := os.ReadFile(tt.file)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tt.want, isArmored(data))
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("degenerate inputs", func(t *testing.T) {
|
||||
assert.False(t, isArmored(nil))
|
||||
assert.False(t, isArmored([]byte("not a key")))
|
||||
assert.True(t, isArmored([]byte("\n\t -----BEGIN PGP PUBLIC KEY BLOCK-----")))
|
||||
})
|
||||
}
|
||||
|
||||
func TestKeyboxPublicKeys(t *testing.T) {
|
||||
data, err := os.ReadFile(testKeybox)
|
||||
require.NoError(t, err)
|
||||
|
||||
keys, err := keyboxPublicKeys(data)
|
||||
require.NoError(t, err)
|
||||
|
||||
ring, err := openpgp.ReadKeyRing(bytes.NewReader(keys))
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Len(t, ring, 1)
|
||||
_, ok := ring[0].Identities[testKeyName]
|
||||
assert.True(t, ok, "expected keybox to contain %q", testKeyName)
|
||||
}
|
||||
|
||||
func TestKeyboxPublicKeysEphemeral(t *testing.T) {
|
||||
// GnuPG flags in-progress key material as ephemeral (bit 0x0002 of the
|
||||
// blob flags at blob offset 6) and hides it from every normal read; the
|
||||
// parser must do the same.
|
||||
setFlags := func(t *testing.T, data []byte, blobStart int, flags uint16) []byte {
|
||||
t.Helper()
|
||||
require.Equal(t, byte(kbxBlobTypeOpenPGP), data[blobStart+4])
|
||||
mutated := bytes.Clone(data)
|
||||
binary.BigEndian.PutUint16(mutated[blobStart+6:], flags)
|
||||
return mutated
|
||||
}
|
||||
|
||||
t.Run("all blobs ephemeral means no keys", func(t *testing.T) {
|
||||
valid, err := os.ReadFile(testKeybox)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = keyboxPublicKeys(setFlags(t, valid, 32, kbxBlobFlagEphemeral))
|
||||
assert.ErrorContains(t, err, "no OpenPGP keys")
|
||||
})
|
||||
|
||||
t.Run("ephemeral blob is skipped, others kept", func(t *testing.T) {
|
||||
valid, err := os.ReadFile(testMixedKeybox)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Flag only the first OpenPGP blob (the RSA helm-test key).
|
||||
keys, err := keyboxPublicKeys(setFlags(t, valid, 32, kbxBlobFlagEphemeral))
|
||||
require.NoError(t, err)
|
||||
|
||||
ring, err := openpgp.ReadKeyRing(bytes.NewReader(keys))
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Len(t, ring, 1)
|
||||
_, ok := ring[0].Identities[testKeyName]
|
||||
assert.False(t, ok, "expected the ephemeral-flagged %q blob to be skipped", testKeyName)
|
||||
})
|
||||
}
|
||||
|
||||
func TestKeyboxPublicKeysMalformed(t *testing.T) {
|
||||
valid, err := os.ReadFile(testKeybox)
|
||||
require.NoError(t, err)
|
||||
|
||||
// The mutations below rely on the fixture layout: a 32-byte header blob
|
||||
// followed by an OpenPGP blob.
|
||||
const blobStart = 32
|
||||
require.Greater(t, len(valid), blobStart+16)
|
||||
require.Equal(t, byte(kbxBlobTypeOpenPGP), valid[blobStart+4])
|
||||
|
||||
mutate := func(offset int, value uint32) []byte {
|
||||
data := bytes.Clone(valid)
|
||||
binary.BigEndian.PutUint32(data[offset:], value)
|
||||
return data
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
data []byte
|
||||
}{
|
||||
{"header only, no keys", valid[:blobStart]},
|
||||
{"truncated inside blob header", valid[:blobStart+2]},
|
||||
{"truncated inside blob body", valid[:blobStart+16]},
|
||||
{"zero blob length", mutate(blobStart, 0)},
|
||||
{"blob length below minimum", mutate(blobStart, 4)},
|
||||
{"blob length past end of data", mutate(blobStart, uint32(len(valid))+1)},
|
||||
{"keyblock offset out of range", mutate(blobStart+8, uint32(len(valid)))},
|
||||
{"keyblock length out of range", mutate(blobStart+12, uint32(len(valid)))},
|
||||
{"keyblock offset overflow", mutate(blobStart+8, ^uint32(0))},
|
||||
{"keyblock length overflow", mutate(blobStart+12, ^uint32(0))},
|
||||
{"openpgp blob shorter than its header", append(bytes.Clone(valid[:blobStart]), 0, 0, 0, 8, kbxBlobTypeOpenPGP, 1, 0, 0)},
|
||||
{"empty input", nil},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
_, err := keyboxPublicKeys(tt.data)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
mQENBFeWbZ4BCADIsgNRyVBSWJkbH6h3UWWXsA9ce1u+YKvaCYFSjIujKycTAqCC
|
||||
P7qUV2Oj/4E2zUcOD1/8/meDwuTnNTLzdSw4ujtFlKRSw7zelQE6sxvID0KM0pQK
|
||||
7AxDTXsm/7Afd/fg4WNW0/hcbeiNz4TVmSWAnbqeLg8o8eljR5QhTk47H6Glo4hV
|
||||
raeLCKG77qm2qOQ/m38ec+L5n9iUpoZZu1S5RXPUIanV5pLlx2rQsooIQEdJbTRF
|
||||
Iv3+2Nj/56PFVdrw7E5ARqCD4PpzzYV7uj8vKumOp+VivFj95Ze6DFENh1WWy336
|
||||
jQEj0uTolgOYeQ6AdJobwSPUeYXGV6Sf2vwXABEBAAG0XUhlbG0gVGVzdGluZyAo
|
||||
VGhpcyBrZXkgc2hvdWxkIG9ubHkgYmUgdXNlZCBmb3IgdGVzdGluZy4gRE8gTk9U
|
||||
IFRSVVNULikgPGhlbG0tdGVzdGluZ0BoZWxtLnNoPokBNwQTAQoAIQUCV5ZtngIb
|
||||
AwULCQgHAwUVCgkICwUWAgMBAAIeAQIXgAAKCRCEO7+YH8GHYolFCAC0+ejT5dIX
|
||||
0juRl5AdG6XAlEf9IrehkVlo0s5bG1Ucea9jjNRafmmHyMravrxZVI9zGFhaNpxA
|
||||
+McnnWAO8lx5yTgnGW269qvFBsj5n0ItPewILQZjTEgwLYf1oc2qCFpk+8ovBnxZ
|
||||
Kaz3J5QKqU09zLO2yALuI5FTCCUupJ3OGIrKvLJ4H2jsRk/5jgqIgqJotW/4Baza
|
||||
4zemFdQHj9FPvq9sVeW5lyxM48i638GtslIgc0y2Yd/bfiy0tfIkskWz/kS1U6c9
|
||||
Utr3UPwpBqHjXnakmpK7GMGlp5eDXgr63BEdLKxJJWWxIys35fR19XZXcAELrwMh
|
||||
zkTzB/dvI375uQENBFeWbZ4BCADB6a8oiF2royWbvwmujBxSmD68QWziNZbSCuRs
|
||||
M4WM1iy69kspJHRwJEVIE5pkoVfV6tIcwQQ0VkJmq6Pf/pyKeMAXp0u8bp5K0mVE
|
||||
Yio1/adiLM3kX1yleoXLNUb1gNvsk6Kp6BZCUNRmjKlbKxapJKHmL+opX8UoeLeE
|
||||
KSgJNL8jvo3nRgg+wIPnVDoizz7y03F0k4SbgNGdoA/FtZs/vcSrEfzOLgI2RxQS
|
||||
dj/ePkxg9TvQygUSYgAVbYlASw6XMmd/zTjkfD9qa0l3WSejTisn85b611WryDpA
|
||||
iYkYC3GzK8g7S9VwkAbtr79cbG+1djlA4aHASpil+zR12yEhABEBAAGJAR8EGAEK
|
||||
AAkFAleWbZ4CGwwACgkQhDu/mB/Bh2LbNQgAiOOMNzmKzY4A7/ra8ppaH9oL8XIa
|
||||
WvInivOsx22K09PPuPVqi/ooBiRGyRqhdVS4ShPOoOTR5tOsdfPpEMTHnGQ1+jW+
|
||||
Tw5MXv3oMckWw3YEYptnuvon3wT4bOXLr+eYlY4Z1ONs+pAXwMiQ2zXNuKHpA2fR
|
||||
HsF8Wyw57rCVn7K5nQgZCbVxasYlzvFGnmt/itSC5w/AnIvICDOWcoGFKTieqhME
|
||||
IkCyvu+DNrMAumnD2fqF2olsM4IzCEPSMEQqJIGzZTtQTseS8NSDioRGnG1AJQ94
|
||||
BssQVmTh0/hlpoTqXY803lR5wb1fr3RRnkOu+lbEI6AAUj51j5TAAo/1dA==
|
||||
=bvcV
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
mDMEaMzP2xYJKwYBBAHaRw8BAQdAXkL0KKTOZoMaM+6HsrkWv93zmLmnMiIoQvR2
|
||||
F+bjx7G0KEhlbG0gRWQyNTUxOSBUZXN0IDxoZWxtLWVkMjU1MTlAaGVsbS5zaD6I
|
||||
mQQTFgoAQRYhBEy+dUwlWAxsh43nEfuFaBLOL8tkBQJozM/bAhsDBQkFo5qABQsJ
|
||||
CAcCAiICBhUKCQgLAgQWAgMBAh4HAheAAAoJEPuFaBLOL8tktBYA/2cclE0N+BZV
|
||||
Wymk6XfwFMjErM6olWAfwA8DfAGQ55+lAQCqSa+Tkjd4dmToVHcb0QAm+zwklAY4
|
||||
e6qWgGiivn77AA==
|
||||
=wJ7K
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
Binary file not shown.
@ -0,0 +1,30 @@
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
mQENBFeWbZ4BCADIsgNRyVBSWJkbH6h3UWWXsA9ce1u+YKvaCYFSjIujKycTAqCC
|
||||
P7qUV2Oj/4E2zUcOD1/8/meDwuTnNTLzdSw4ujtFlKRSw7zelQE6sxvID0KM0pQK
|
||||
7AxDTXsm/7Afd/fg4WNW0/hcbeiNz4TVmSWAnbqeLg8o8eljR5QhTk47H6Glo4hV
|
||||
raeLCKG77qm2qOQ/m38ec+L5n9iUpoZZu1S5RXPUIanV5pLlx2rQsooIQEdJbTRF
|
||||
Iv3+2Nj/56PFVdrw7E5ARqCD4PpzzYV7uj8vKumOp+VivFj95Ze6DFENh1WWy336
|
||||
jQEj0uTolgOYeQ6AdJobwSPUeYXGV6Sf2vwXABEBAAG0XUhlbG0gVGVzdGluZyAo
|
||||
VGhpcyBrZXkgc2hvdWxkIG9ubHkgYmUgdXNlZCBmb3IgdGVzdGluZy4gRE8gTk9U
|
||||
IFRSVVNULikgPGhlbG0tdGVzdGluZ0BoZWxtLnNoPokBNwQTAQoAIQUCV5ZtngIb
|
||||
AwULCQgHAwUVCgkICwUWAgMBAAIeAQIXgAAKCRCEO7+YH8GHYolFCAC0+ejT5dIX
|
||||
0juRl5AdG6XAlEf9IrehkVlo0s5bG1Ucea9jjNRafmmHyMravrxZVI9zGFhaNpxA
|
||||
+McnnWAO8lx5yTgnGW269qvFBsj5n0ItPewILQZjTEgwLYf1oc2qCFpk+8ovBnxZ
|
||||
Kaz3J5QKqU09zLO2yALuI5FTCCUupJ3OGIrKvLJ4H2jsRk/5jgqIgqJotW/4Baza
|
||||
4zemFdQHj9FPvq9sVeW5lyxM48i638GtslIgc0y2Yd/bfiy0tfIkskWz/kS1U6c9
|
||||
Utr3UPwpBqHjXnakmpK7GMGlp5eDXgr63BEdLKxJJWWxIys35fR19XZXcAELrwMh
|
||||
zkTzB/dvI375uQENBFeWbZ4BCADB6a8oiF2royWbvwmujBxSmD68QWziNZbSCuRs
|
||||
M4WM1iy69kspJHRwJEVIE5pkoVfV6tIcwQQ0VkJmq6Pf/pyKeMAXp0u8bp5K0mVE
|
||||
Yio1/adiLM3kX1yleoXLNUb1gNvsk6Kp6BZCUNRmjKlbKxapJKHmL+opX8UoeLeE
|
||||
KSgJNL8jvo3nRgg+wIPnVDoizz7y03F0k4SbgNGdoA/FtZs/vcSrEfzOLgI2RxQS
|
||||
dj/ePkxg9TvQygUSYgAVbYlASw6XMmd/zTjkfD9qa0l3WSejTisn85b611WryDpA
|
||||
iYkYC3GzK8g7S9VwkAbtr79cbG+1djlA4aHASpil+zR12yEhABEBAAGJAR8EGAEK
|
||||
AAkFAleWbZ4CGwwACgkQhDu/mB/Bh2LbNQgAiOOMNzmKzY4A7/ra8ppaH9oL8XIa
|
||||
WvInivOsx22K09PPuPVqi/ooBiRGyRqhdVS4ShPOoOTR5tOsdfPpEMTHnGQ1+jW+
|
||||
Tw5MXv3oMckWw3YEYptnuvon3wT4bOXLr+eYlY4Z1ONs+pAXwMiQ2zXNuKHpA2fR
|
||||
HsF8Wyw57rCVn7K5nQgZCbVxasYlzvFGnmt/itSC5w/AnIvICDOWcoGFKTieqhME
|
||||
IkCyvu+DNrMAumnD2fqF2olsM4IzCEPSMEQqJIGzZTtQTseS8NSDioRGnG1AJQ94
|
||||
BssQVmTh0/hlpoTqXY803lR5wb1fr3RRnkOu+lbEI6AAUj51j5TAAo/1dA==
|
||||
=bvcV
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
Binary file not shown.
@ -0,0 +1,26 @@
|
||||
#!/bin/sh
|
||||
# Regenerate the file-backed keyring fixtures from the committed binary
|
||||
# keyrings. Requires GnuPG 2.1+ (keybox support).
|
||||
#
|
||||
# helm-test-key.kbx keybox (pubring.kbx) containing the helm-test key
|
||||
# helm-mixed-keyring.kbx keybox containing the RSA and Ed25519 test keys
|
||||
# helm-test-key.asc ASCII-armored export of the helm-test key
|
||||
# helm-mixed-keyring.asc two concatenated single-key armored exports
|
||||
set -e
|
||||
|
||||
GNUPGHOME=$(mktemp -d)
|
||||
export GNUPGHOME
|
||||
chmod 700 "$GNUPGHOME"
|
||||
gpg --batch --no-tty --quiet --import helm-test-key.pub
|
||||
cp "$GNUPGHOME/pubring.kbx" helm-test-key.kbx
|
||||
gpg --batch --no-tty --export --armor helm-testing@helm.sh > helm-test-key.asc
|
||||
rm -rf "$GNUPGHOME"
|
||||
|
||||
GNUPGHOME=$(mktemp -d)
|
||||
export GNUPGHOME
|
||||
chmod 700 "$GNUPGHOME"
|
||||
gpg --batch --no-tty --quiet --import helm-mixed-keyring.pub
|
||||
cp "$GNUPGHOME/pubring.kbx" helm-mixed-keyring.kbx
|
||||
gpg --batch --no-tty --export --armor helm-testing@helm.sh > helm-mixed-keyring.asc
|
||||
gpg --batch --no-tty --export --armor helm-ed25519@helm.sh >> helm-mixed-keyring.asc
|
||||
rm -rf "$GNUPGHOME"
|
||||
Loading…
Reference in new issue