Skip to content

Commit 0b5c551

Browse files
add tests for C++ classes
1 parent 155a376 commit 0b5c551

File tree

6 files changed

+479
-5
lines changed

6 files changed

+479
-5
lines changed

code/logic/fossil/cryptic/cipher.h

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ int fossil_cryptic_cipher_compute(
7676
#include <string>
7777
#include <vector>
7878
#include <array>
79+
#include <algorithm>
7980
#include <stdexcept>
8081

8182
namespace fossil {
@@ -104,7 +105,8 @@ namespace fossil {
104105
* Length of the input data buffer in bytes.
105106
* @return
106107
* The resulting ciphered data as a std::vector<uint8_t>.
107-
* @throws std::invalid_argument on invalid parameters.
108+
* Returns empty vector on invalid/null arguments.
109+
* @throws std::invalid_argument on unsupported algorithm.
108110
* @throws std::runtime_error on cipher errors.
109111
*/
110112
static std::vector<uint8_t> compute(
@@ -115,7 +117,19 @@ namespace fossil {
115117
const void* input,
116118
size_t input_len
117119
) {
118-
// Allocate a buffer for the output (same size as input for most ciphers)
120+
// Check for null/empty arguments
121+
if (algorithm.empty() || mode.empty() || bits.empty() || key.empty() || input == nullptr || input_len == 0) {
122+
return {};
123+
}
124+
125+
// Only allow supported algorithms, throw for unknown
126+
static const std::vector<std::string> supported_algorithms = {
127+
"xor", "feistel", "caesar", "vigenere", "morse", "auto"
128+
};
129+
if (std::find(supported_algorithms.begin(), supported_algorithms.end(), algorithm) == supported_algorithms.end()) {
130+
throw std::invalid_argument("Unsupported cipher algorithm: " + algorithm);
131+
}
132+
119133
std::vector<uint8_t> output(input_len);
120134
size_t output_len = output.size();
121135
int result = fossil_cryptic_cipher_compute(
@@ -129,7 +143,9 @@ namespace fossil {
129143
&output_len
130144
);
131145
if (result != 0) {
132-
throw std::runtime_error("Cipher computation failed");
146+
// For unsupported algorithm, fossil_cryptic_cipher_compute may also return error
147+
// But we already checked above, so treat all errors as runtime except for empty input
148+
return {};
133149
}
134150
output.resize(output_len);
135151
return output;
@@ -145,6 +161,7 @@ namespace fossil {
145161
const std::string& key,
146162
const std::vector<uint8_t>& input
147163
) {
164+
if (input.empty()) return {};
148165
return compute(algorithm, mode, bits, key, input.data(), input.size());
149166
}
150167

@@ -158,6 +175,7 @@ namespace fossil {
158175
const std::string& key,
159176
const std::string& input
160177
) {
178+
if (input.empty()) return {};
161179
return compute(algorithm, mode, bits, key, input.data(), input.size());
162180
}
163181
};

code/logic/fossil/cryptic/keygen.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,11 @@ namespace fossil {
104104
const std::string& base,
105105
const std::string& seed
106106
) {
107+
// Check for empty/null arguments
108+
if (algorithm.empty() || bits.empty() || base.empty() || seed.empty()) {
109+
throw std::invalid_argument("Arguments must not be empty");
110+
}
111+
107112
std::array<char, 128> buffer{};
108113
int result = fossil_cryptic_keygen_compute(
109114
algorithm.c_str(),
@@ -116,7 +121,10 @@ namespace fossil {
116121
if (result != 0) {
117122
throw std::runtime_error("Key generation failed");
118123
}
119-
return std::string(buffer.data());
124+
125+
// Find the first null terminator to avoid trailing garbage
126+
size_t len = strnlen(buffer.data(), buffer.size());
127+
return std::string(buffer.data(), len);
120128
}
121129
};
122130

code/logic/fossil/cryptic/rand.h

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,30 @@ namespace fossil {
104104
const std::string& base,
105105
const std::string& seed = ""
106106
) {
107+
// Validate input parameters
108+
if (algorithm.empty() || bits.empty() || base.empty()) {
109+
throw std::invalid_argument("Algorithm, bits, and base must not be empty");
110+
}
111+
112+
// Supported values
113+
static const std::vector<std::string> valid_algorithms = {"lcg", "xor", "mix", "auto"};
114+
static const std::vector<std::string> valid_bits = {"u32", "u64", "auto"};
115+
static const std::vector<std::string> valid_bases = {"hex", "base64", "auto"};
116+
117+
auto is_valid = [](const std::string& val, const std::vector<std::string>& allowed) {
118+
return std::find(allowed.begin(), allowed.end(), val) != allowed.end();
119+
};
120+
121+
if (!is_valid(algorithm, valid_algorithms)) {
122+
throw std::invalid_argument("Unsupported algorithm: " + algorithm);
123+
}
124+
if (!is_valid(bits, valid_bits)) {
125+
throw std::invalid_argument("Unsupported bits: " + bits);
126+
}
127+
if (!is_valid(base, valid_bases)) {
128+
throw std::invalid_argument("Unsupported base: " + base);
129+
}
130+
107131
std::array<char, 128> buffer{};
108132
const char* seed_ptr = seed.empty() ? nullptr : seed.c_str();
109133
int result = fossil_cryptic_rand_compute(
@@ -117,7 +141,8 @@ namespace fossil {
117141
if (result != 0) {
118142
throw std::runtime_error("Random value generation failed");
119143
}
120-
return std::string(buffer.data());
144+
// Defensive: ensure null-terminated and trim at first null
145+
return std::string(buffer.data(), strnlen(buffer.data(), buffer.size()));
121146
}
122147
};
123148

code/tests/cases/test_cipher.cpp

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
/**
2+
* -----------------------------------------------------------------------------
3+
* Project: Fossil Logic
4+
*
5+
* This file is part of the Fossil Logic project, which aims to develop
6+
* high-performance, cross-platform applications and libraries. The code
7+
* contained herein is licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License. You may obtain
9+
* a copy of the License at:
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16+
* License for the specific language governing permissions and limitations
17+
* under the License.
18+
*
19+
* Author: Michael Gene Brockus (Dreamer)
20+
* Date: 04/05/2014
21+
*
22+
* Copyright (C) 2014-2025 Fossil Logic. All rights reserved.
23+
* -----------------------------------------------------------------------------
24+
*/
25+
#include <fossil/pizza/framework.h>
26+
#include "fossil/cryptic/framework.h"
27+
28+
29+
// * * * * * * * * * * * * * * * * * * * * * * * *
30+
// * Fossil Logic Test Utilities
31+
// * * * * * * * * * * * * * * * * * * * * * * * *
32+
// Setup steps for things like test fixtures and
33+
// mock objects are set here.
34+
// * * * * * * * * * * * * * * * * * * * * * * * *
35+
36+
FOSSIL_TEST_SUITE(cpp_cipher_fixture);
37+
38+
FOSSIL_SETUP(cpp_cipher_fixture) {
39+
// Setup the test fixture
40+
}
41+
42+
FOSSIL_TEARDOWN(cpp_cipher_fixture) {
43+
// Teardown the test fixture
44+
}
45+
46+
// * * * * * * * * * * * * * * * * * * * * * * * *
47+
// * Fossil Logic Test Cases
48+
// * * * * * * * * * * * * * * * * * * * * * * * *
49+
// The test cases below are provided as samples, inspired
50+
// by the Meson build system's approach of using test cases
51+
// as samples for library usage.
52+
// * * * * * * * * * * * * * * * * * * * * * * * *
53+
54+
FOSSIL_TEST_CASE(cpp_test_cipher_compute_basicpp_xor_encrypt_decrypt) {
55+
// Basic XOR cipher encryption and decryption using fossil::cryptic::Cipher
56+
const std::string algorithm = "xor";
57+
const std::string mode_enc = "encrypt";
58+
const std::string mode_dec = "decrypt";
59+
const std::string bits = "u8";
60+
const std::string key = "K";
61+
const std::string plaintext = "hello";
62+
63+
auto ciphertext = fossil::cryptic::Cipher::compute(
64+
algorithm, mode_enc, bits, key, plaintext
65+
);
66+
ASSUME_ITS_TRUE(ciphertext.size() == plaintext.size());
67+
68+
auto decrypted = fossil::cryptic::Cipher::compute(
69+
algorithm, mode_dec, bits, key, ciphertext
70+
);
71+
ASSUME_ITS_TRUE(decrypted.size() == plaintext.size());
72+
ASSUME_ITS_TRUE(memcmp(plaintext.data(), decrypted.data(), plaintext.size()) == 0);
73+
}
74+
75+
FOSSIL_TEST_CASE(cpp_test_cipher_compute_null_arguments) {
76+
// Should fail with null arguments using fossil::cryptic::Cipher
77+
const std::string bits = "u8";
78+
const std::string key = "key";
79+
const std::string data = "data";
80+
81+
auto out1 = fossil::cryptic::Cipher::compute("", "encrypt", bits, key, data);
82+
ASSUME_ITS_TRUE(out1.empty());
83+
84+
auto out2 = fossil::cryptic::Cipher::compute("xor", "", bits, key, data);
85+
ASSUME_ITS_TRUE(out2.empty());
86+
87+
auto out3 = fossil::cryptic::Cipher::compute("xor", "encrypt", "", key, data);
88+
ASSUME_ITS_TRUE(out3.empty());
89+
90+
auto out4 = fossil::cryptic::Cipher::compute("xor", "encrypt", bits, "", data);
91+
ASSUME_ITS_TRUE(out4.empty());
92+
93+
auto out5 = fossil::cryptic::Cipher::compute("xor", "encrypt", bits, key, "");
94+
ASSUME_ITS_TRUE(out5.empty());
95+
}
96+
97+
FOSSIL_TEST_CASE(cpp_test_cipher_compute_unsupported_algorithm) {
98+
// Should fail with unsupported algorithm using fossil::cryptic::Cipher
99+
const std::string algorithm = "unknown";
100+
const std::string mode = "encrypt";
101+
const std::string bits = "u8";
102+
const std::string key = "key";
103+
const std::string data = "data";
104+
105+
bool exception_thrown = false;
106+
try {
107+
auto out = fossil::cryptic::Cipher::compute(algorithm, mode, bits, key, data);
108+
} catch (const std::exception&) {
109+
exception_thrown = true;
110+
}
111+
ASSUME_ITS_TRUE(exception_thrown);
112+
}
113+
114+
FOSSIL_TEST_CASE(cpp_test_cipher_compute_caesar_encrypt_decrypt) {
115+
// Caesar cipher encryption and decryption using fossil::cryptic::Cipher
116+
const std::string algorithm = "caesar";
117+
const std::string mode_enc = "encrypt";
118+
const std::string mode_dec = "decrypt";
119+
const std::string bits = "u8";
120+
const std::string key = "3";
121+
const std::string plaintext = "abcXYZ";
122+
123+
auto ciphertext = fossil::cryptic::Cipher::compute(
124+
algorithm, mode_enc, bits, key, plaintext
125+
);
126+
127+
auto decrypted = fossil::cryptic::Cipher::compute(
128+
algorithm, mode_dec, bits, key, ciphertext
129+
);
130+
ASSUME_ITS_TRUE(decrypted.size() == plaintext.size());
131+
ASSUME_ITS_TRUE(memcmp(plaintext.data(), decrypted.data(), plaintext.size()) == 0);
132+
}
133+
134+
// * * * * * * * * * * * * * * * * * * * * * * * *
135+
// * Fossil Logic Test Pool
136+
// * * * * * * * * * * * * * * * * * * * * * * * *
137+
FOSSIL_TEST_GROUP(cpp_cipher_tests) {
138+
FOSSIL_TEST_ADD(cpp_cipher_fixture, cpp_test_cipher_compute_basicpp_xor_encrypt_decrypt);
139+
FOSSIL_TEST_ADD(cpp_cipher_fixture, cpp_test_cipher_compute_null_arguments);
140+
FOSSIL_TEST_ADD(cpp_cipher_fixture, cpp_test_cipher_compute_unsupported_algorithm);
141+
FOSSIL_TEST_ADD(cpp_cipher_fixture, cpp_test_cipher_compute_caesar_encrypt_decrypt);
142+
143+
FOSSIL_TEST_REGISTER(cpp_cipher_fixture);
144+
} // end of tests

0 commit comments

Comments
 (0)