Skip to content

Commit b53d4ad

Browse files
Implement OpenSSL encryption/decryption validation tests for Azure Linux CBL-Mariner (#3832)
1 parent 14155b2 commit b53d4ad

File tree

3 files changed

+173
-0
lines changed

3 files changed

+173
-0
lines changed

lisa/tools/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@
9090
from .ntttcp import Ntttcp
9191
from .nvidiasmi import NvidiaSmi
9292
from .nvmecli import Nvmecli
93+
from .openssl import OpenSSL
9394
from .parted import Parted
9495
from .perf import Perf
9596
from .pgrep import Pgrep, ProcessInfo
@@ -222,6 +223,7 @@
222223
"Ntttcp",
223224
"NvidiaSmi",
224225
"Nvmecli",
226+
"OpenSSL",
225227
"Parted",
226228
"Perf",
227229
"Pidof",

lisa/tools/openssl.py

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# Copyright (c) Microsoft Corporation.
2+
# Licensed under the MIT license.
3+
4+
import shlex
5+
from typing import TYPE_CHECKING
6+
7+
from lisa.executable import Tool
8+
9+
if TYPE_CHECKING:
10+
from lisa.operating_system import Posix
11+
12+
13+
class OpenSSL(Tool):
14+
"""
15+
OpenSSL tool for encryption and decryption operations.
16+
"""
17+
18+
@property
19+
def command(self) -> str:
20+
return "openssl"
21+
22+
@property
23+
def can_install(self) -> bool:
24+
return True
25+
26+
def encrypt(
27+
self,
28+
plaintext: str,
29+
hex_key: str,
30+
hex_iv: str,
31+
algorithm: str = "aes-256-cbc",
32+
) -> str:
33+
"""
34+
Encrypt the plaintext using the specified key and IV,
35+
and return the base64 encoded ciphertext.
36+
"""
37+
return self._run_with_piped_input(
38+
plaintext,
39+
f"enc -{algorithm} -K '{hex_key}' -iv '{hex_iv}' -base64 -A",
40+
expected_exit_code_failure_message="Failed to encrypt data with OpenSSL.",
41+
)
42+
43+
def decrypt(
44+
self,
45+
ciphertext: str,
46+
hex_key: str,
47+
hex_iv: str,
48+
algorithm: str = "aes-256-cbc",
49+
) -> str:
50+
"""
51+
This method decrypts the ciphertext using the specified
52+
key and IV, and returns the plaintext.
53+
Decrypt the ciphertext using the specified
54+
key and IV, and return the plaintext.
55+
"""
56+
return self._run_with_piped_input(
57+
ciphertext,
58+
f"enc -d -{algorithm} -K '{hex_key}' -iv '{hex_iv}' -base64 -A",
59+
expected_exit_code_failure_message="Failed to decrypt data with OpenSSL.",
60+
)
61+
62+
def _run_with_piped_input(
63+
self,
64+
piped_input_cmd: str,
65+
openssl_cmd: str,
66+
expected_exit_code: int = 0,
67+
expected_exit_code_failure_message: str = "",
68+
) -> str:
69+
"""
70+
Execute OpenSSL command with piped input and validate results.
71+
72+
Args:
73+
piped_input_cmd: The input string to pipe to OpenSSL
74+
openssl_cmd: The OpenSSL command to execute
75+
expected_exit_code: Expected exit code from command (default: 0)
76+
expected_exit_code_failure_message:
77+
Message to display if the command fails with an unexpected exit code
78+
79+
Returns:
80+
The stripped stdout from the command
81+
82+
Raises:
83+
LisaException: When the command fails
84+
or returns unexpected exit code
85+
"""
86+
sanitized_input = shlex.quote(piped_input_cmd)
87+
cmd = f"printf '%s' {sanitized_input} | {self.command} {openssl_cmd}"
88+
result = self.node.execute(
89+
cmd,
90+
shell=True,
91+
expected_exit_code=expected_exit_code,
92+
expected_exit_code_failure_message=expected_exit_code_failure_message,
93+
)
94+
return result.stdout.strip()
95+
96+
def _install(self) -> bool:
97+
"""
98+
Install OpenSSL on the node if
99+
it is not already installed.
100+
"""
101+
posix_os: Posix = self.node.os # type: ignore
102+
posix_os.install_packages([self])
103+
return self._check_exists()
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Copyright (c) Microsoft Corporation.
2+
# Licensed under the MIT license.
3+
4+
import json
5+
6+
from assertpy import assert_that
7+
8+
from lisa import Logger, Node, TestCaseMetadata, TestSuite, TestSuiteMetadata
9+
from lisa.tools import OpenSSL
10+
11+
12+
@TestSuiteMetadata(
13+
area="security",
14+
category="functional",
15+
description="""
16+
Tests the functionality of OpenSSL, including encryption and decryption
17+
operations. Validates that OpenSSL can successfully encrypt plaintext data
18+
and decrypt it back to its original form using generated keys and IVs.
19+
""",
20+
)
21+
class OpenSSLTestSuite(TestSuite):
22+
"""
23+
Test suite for OpenSSL functionality.
24+
"""
25+
26+
@TestCaseMetadata(
27+
description="""
28+
Verifies basic OpenSSL encryption and decryption behavior by generating
29+
a random key and IV, encrypting various types of plaintext, and
30+
decrypting them back to their original form.
31+
""",
32+
priority=2,
33+
)
34+
def verify_openssl_basic(self, log: Logger, node: Node) -> None:
35+
self._openssl_test_encrypt_decrypt(log, node)
36+
37+
def _openssl_test_encrypt_decrypt(self, log: Logger, node: Node) -> None:
38+
"""
39+
Tests OpenSSL encryption and decryption functionality.
40+
This function generates a random key and IV, encrypts various types of
41+
"""
42+
43+
# Key and IV for encryption and decryption.
44+
openssl = node.tools[OpenSSL]
45+
key_hex = openssl.run(
46+
"rand -hex 32",
47+
expected_exit_code=0,
48+
).stdout.strip()
49+
iv_hex = openssl.run(
50+
"rand -hex 16",
51+
expected_exit_code=0,
52+
).stdout.strip()
53+
# Test with different data types and sizes
54+
test_data = [
55+
"cool", # Short string
56+
"A" * 1024, # Longer string
57+
"Special chars: !@#$%^&*()", # Special characters
58+
json.dumps({"resourceId": "test123"}), # JSON Azure resource data
59+
]
60+
61+
for plaintext in test_data:
62+
# Encrypt and decrypt the plaintext
63+
log.debug(f"Output plaintext: {plaintext}")
64+
encrypted_data = openssl.encrypt(plaintext, key_hex, iv_hex)
65+
decrypted_data = openssl.decrypt(encrypted_data, key_hex, iv_hex)
66+
assert_that(plaintext).described_as(
67+
"Plaintext and decrypted data do not match"
68+
).is_equal_to(decrypted_data)

0 commit comments

Comments
 (0)