diff --git a/README.rst b/README.rst index fb0f52959..a4a5e7b65 100644 --- a/README.rst +++ b/README.rst @@ -188,6 +188,22 @@ the example files and pgp key provided with the repository:: This last step will decrypt ``example.yaml`` using the test private key. +Encrypting with GnuPG subkeys +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you want to encrypt with specific GnuPG subkeys, it does not suffice to provide the +exact key ID of the subkey to SOPS, since GnuPG might use *another* subkey instead +to encrypt the file key with. To force GnuPG to use a specific subkey, you need to +append ``!`` to the key's fingerprint. + +.. code:: yaml + + creation_rules: + - pgp: >- + 85D77543B3D624B63CEA9E6DBC17301B491B3F21!, + E60892BB9BD89A69F759A1A0A3D652173B763E8F! + +Please note that this is only passed on correctly to GnuPG since SOPS 3.9.3. Encrypting using age ~~~~~~~~~~~~~~~~~~~~ diff --git a/pgp/keysource.go b/pgp/keysource.go index e1eed580d..1646aceaa 100644 --- a/pgp/keysource.go +++ b/pgp/keysource.go @@ -634,7 +634,13 @@ func gnuPGHome(customPath string) string { // This is mostly used for compatibility reasons, as older versions of GnuPG // do not always like long IDs. func shortenFingerprint(fingerprint string) string { - if offset := len(fingerprint) - 16; offset > 0 { + offset := len(fingerprint) - 16 + // If the fingerprint ends with '!', we must include '!' in the ID *and* the + // 16 hex digits before it. See https://github.com/getsops/sops/issues/1365. + if strings.HasSuffix(fingerprint, "!") { + offset -= 1 + } + if offset > 0 { fingerprint = fingerprint[offset:] } return fingerprint diff --git a/pgp/keysource_test.go b/pgp/keysource_test.go index 58693d72d..28fcfeb8e 100644 --- a/pgp/keysource_test.go +++ b/pgp/keysource_test.go @@ -697,10 +697,23 @@ func Test_gnuPGHome(t *testing.T) { } func Test_shortenFingerprint(t *testing.T) { + // Test with regular fingerprint shortId := shortenFingerprint(mockFingerprint) assert.Equal(t, "9732075EA221A7EA", shortId) assert.Equal(t, shortId, shortenFingerprint(shortId)) + + // Test with forced subkey + shortId = shortenFingerprint(mockFingerprint + "!") + assert.Equal(t, "9732075EA221A7EA!", shortId) + + assert.Equal(t, shortId, shortenFingerprint(shortId)) + + // Make sure that too short IDs are kept + for _, tooShort := range []string{"012345679abcdef", "012345679abcdef!", "123", "123!"} { + shortId = shortenFingerprint(tooShort) + assert.Equal(t, tooShort, shortId) + } } // TODO(hidde): previous tests kept around for now.