Skip to content

Commit

Permalink
unwrap before verify
Browse files Browse the repository at this point in the history
  • Loading branch information
TheRealFalcon committed Oct 21, 2024
1 parent ee2a368 commit 5c8743a
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 11 deletions.
26 changes: 22 additions & 4 deletions cloudinit/gpg.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,30 @@ def decrypt(self, data: str, *, require_signature=False) -> str:
data=data,
update_env=self.env,
)
except subp.ProcessExecutionError as e:
if e.exit_code == 2:
except subp.ProcessExecutionError:
# If the message is signed then encrypted (the default),
# the message can't be verified until it's decrypted
try:
stdout, _ = subp.subp(
["gpg", "--unwrap"],
data=data,
update_env=self.env,
decode=False,
)
except subp.ProcessExecutionError as e:
raise GpgVerificationError(
"Signature verification failed"
"Signature verification failed. Could not unwrap."
) from e
try:
subp.subp(
["gpg", "--verify"],
data=stdout,
update_env=self.env,
)
except subp.ProcessExecutionError as e:
raise GpgVerificationError(
"Signature verification failed. Could not verify."
) from e
raise
result = subp.subp(
[
"gpg",
Expand Down
4 changes: 2 additions & 2 deletions cloudinit/subp.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import subprocess
from errno import ENOEXEC
from io import TextIOWrapper
from typing import List, Optional, Union
from typing import List, Literal, Optional, Union

from cloudinit import performance

Expand Down Expand Up @@ -170,7 +170,7 @@ def subp(
capture=True,
shell=False,
logstring=False,
decode="replace",
decode: Literal[False, "strict", "ignore", "replace"] = "replace",
update_env=None,
cwd=None,
timeout=None,
Expand Down
19 changes: 14 additions & 5 deletions tests/integration_tests/userdata/test_pgp.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""Test PGP signed and encrypted userdata."""

from pathlib import Path

import pytest

from cloudinit import subp
Expand All @@ -16,20 +18,19 @@


@pytest.fixture(scope="module")
def gpg_dir(tmp_path_factory):
def gpg_dir(tmp_path_factory: pytest.TempPathFactory):
yield tmp_path_factory.mktemp("gpg_dir")


@pytest.fixture(scope="module")
def public_key(gpg_dir):
def public_key(gpg_dir: Path):
subp.subp(
[
"gpg",
"--homedir",
str(gpg_dir),
"--quick-generate-key",
"--batch",
# "loopback",
"--passphrase",
"",
"signing_user",
Expand Down Expand Up @@ -197,6 +198,14 @@ def _invalidate_key(client, key_path):
)
def test_signed_and_encrypted(pgp_client: IntegrationInstance):
client = pgp_client

client.write_to_file(
"/etc/cloud/cloud.cfg.d/99_pgp.cfg",
"user_data:\n require_signature: true",
)
client.execute("cloud-init clean --logs")
client.restart()

assert client.execute("test -f /var/tmp/signed_and_encrypted")
verify_clean_boot(client)

Expand All @@ -209,7 +218,7 @@ def test_signed_and_encrypted(pgp_client: IntegrationInstance):
assert not client.execute("test -f /var/tmp/signed_and_encrypted")
result = client.execute("cloud-init status --format=json")
assert result.failed
assert "Failed decrypting user data" in result.stdout
assert "Signature verification failed. Could not verify." in result.stdout

# Restore the public key, invalidate the private key, and ensure we fail
client.execute("cp /var/tmp/pub_key /etc/cloud/keys/pub_key")
Expand All @@ -221,7 +230,7 @@ def test_signed_and_encrypted(pgp_client: IntegrationInstance):
assert not client.execute("test -f /var/tmp/signed_and_encrypted")
result = client.execute("cloud-init status --format=json")
assert result.failed
assert "Failed decrypting user data" in result.stdout
assert "Signature verification failed. Could not unwrap." in result.stdout


@pytest.mark.parametrize(
Expand Down

0 comments on commit 5c8743a

Please sign in to comment.