Skip to main content

bcore_mutation/operators/
secp256k1.rs

1//! secp256k1 operator set.
2//!
3//! secp256k1 is a pure C library with no Python functional tests and no Boost
4//! unit tests - its tests are C programs (`tests.c`, `tests_exhaustive.c`,
5//! `*_impl.h` test sections) driven by CTest. The operators here therefore
6//! reuse the language-level [`super::common`] set and add C-specific guard
7//! macros, annotations, cleanup calls, and diagnostics to the skip lists.
8
9use super::{build, common, MutationOperator, OperatorSet};
10
11pub struct Secp256k1;
12
13const SECP256K1_SKIP_PREFIXES: &[&str] = &[
14    // Invariant and public API guards.
15    "VERIFY_CHECK",
16    "VERIFY_BITS",
17    "ARG_CHECK",
18    "ARG_CHECK_VOID",
19    "CHECK",
20    "secp256k1_fe_verify",
21    "secp256k1_ge_verify",
22    "secp256k1_gej_verify",
23    "secp256k1_scalar_verify",
24    // Defensive output zeroing and testrand diagnostics.
25    "memset",
26    "printf",
27    "fprintf",
28];
29
30const SECP256K1_SKIP_SUBSTRINGS: &[&str] = &[
31    // VERIFY-style macro variants such as SECP256K1_FE_VERIFY(...).
32    "VERIFY_CHECK",
33    "VERIFY_BITS",
34    "_VERIFY",
35    "ARG_CHECK",
36    // Callback/error reporting and verification annotations.
37    "secp256k1_callback_call",
38    "secp256k1_declassify",
39    "SECP256K1_CHECKMEM_",
40    // Secret/object cleanup functions.
41    "secp256k1_memclear_explicit",
42    "secp256k1_scalar_clear",
43    "secp256k1_ge_clear",
44    "secp256k1_gej_clear",
45    "secp256k1_fe_clear",
46    "secp256k1_sha256_clear",
47    "secp256k1_hmac_sha256_clear",
48    "secp256k1_rfc6979_hmac_sha256_clear",
49    // Conditional zeroing on failure.
50    "secp256k1_memczero",
51    // Field normalization. These only canonicalize the internal limb
52    // representation without changing the represented value, so deleting a
53    // call almost always yields an equivalent (non-useful) mutant. Covers
54    // secp256k1_fe_normalize{,_weak,_var,_to_zero,_to_zero_var}.
55    "secp256k1_fe_normalize",
56    // testrand /dev/urandom file I/O.
57    "fopen(",
58    "fread(",
59    "fclose(",
60];
61
62impl OperatorSet for Secp256k1 {
63    fn regex_operators(&self) -> Result<Vec<MutationOperator>, regex::Error> {
64        build(common::regex_operators())
65    }
66
67    fn security_operators(&self) -> Result<Vec<MutationOperator>, regex::Error> {
68        build(common::security_operators())
69    }
70
71    fn test_operators(&self) -> Result<Vec<MutationOperator>, regex::Error> {
72        build(common::test_operators())
73    }
74
75    fn do_not_mutate_patterns(&self) -> Vec<&'static str> {
76        let mut patterns = common::do_not_mutate_patterns();
77        patterns.extend(SECP256K1_SKIP_PREFIXES);
78        patterns
79    }
80
81    fn do_not_mutate_py_patterns(&self) -> Vec<&'static str> {
82        // secp256k1 has no Python functional test suite.
83        Vec::new()
84    }
85
86    fn do_not_mutate_unit_patterns(&self) -> Vec<&'static str> {
87        vec![
88            "while",
89            "for",
90            "if",
91            "else",
92            "return",
93            "continue",
94            "break",
95            "static",
96            "void",
97            // secp256k1 test harness helpers
98            "CHECK",
99            "VERIFY_CHECK",
100            "run_",
101            "test_",
102            "secp256k1_",
103        ]
104    }
105
106    fn skip_if_contain_patterns(&self) -> Vec<&'static str> {
107        SECP256K1_SKIP_SUBSTRINGS.to_vec()
108    }
109
110    fn test_line_skip_prefixes(&self) -> Vec<&'static str> {
111        let mut prefixes = vec!["assert", "CHECK", "run_", "test_"];
112        prefixes.extend(SECP256K1_SKIP_PREFIXES);
113        prefixes
114    }
115}
116
117#[cfg(test)]
118mod tests {
119    use super::*;
120
121    fn skipped_by_global_secp256k1_lists(line: &str) -> bool {
122        let ops = Secp256k1;
123        let trimmed = line.trim_start();
124
125        ops.do_not_mutate_patterns()
126            .iter()
127            .any(|pattern| trimmed.starts_with(pattern))
128            || ops
129                .skip_if_contain_patterns()
130                .iter()
131                .any(|pattern| line.contains(pattern))
132    }
133
134    #[test]
135    fn skips_verify_and_api_guard_lines() {
136        assert!(skipped_by_global_secp256k1_lists(
137            "    VERIFY_CHECK(r != NULL);"
138        ));
139        assert!(skipped_by_global_secp256k1_lists(
140            "    VERIFY_BITS_128(x, 64);"
141        ));
142        assert!(skipped_by_global_secp256k1_lists(
143            "    SECP256K1_SCALAR_VERIFY (&s);"
144        ));
145        assert!(skipped_by_global_secp256k1_lists(
146            "    ARG_CHECK(ctx != NULL);"
147        ));
148    }
149
150    #[test]
151    fn skips_annotations_cleanup_and_zeroing() {
152        assert!(skipped_by_global_secp256k1_lists(
153            "    secp256k1_callback_call(&ctx->error_callback, \"bad\");"
154        ));
155        assert!(skipped_by_global_secp256k1_lists(
156            "    secp256k1_declassify(ctx, &ret, sizeof(ret));"
157        ));
158        assert!(skipped_by_global_secp256k1_lists(
159            "    SECP256K1_CHECKMEM_CHECK(p, len);"
160        ));
161        assert!(skipped_by_global_secp256k1_lists(
162            "    secp256k1_scalar_clear(&s);"
163        ));
164        assert!(skipped_by_global_secp256k1_lists(
165            "    secp256k1_memczero(sig64, 64, !ret);"
166        ));
167        assert!(skipped_by_global_secp256k1_lists(
168            "    memset(sig64, 0, 64);"
169        ));
170    }
171
172    #[test]
173    fn skips_field_normalization_calls() {
174        assert!(skipped_by_global_secp256k1_lists(
175            "    secp256k1_fe_normalize(&r->x);"
176        ));
177        assert!(skipped_by_global_secp256k1_lists(
178            "    secp256k1_fe_normalize_weak(&r->x);"
179        ));
180        assert!(skipped_by_global_secp256k1_lists(
181            "    secp256k1_fe_normalize_var(&r->x);"
182        ));
183        assert!(skipped_by_global_secp256k1_lists(
184            "    secp256k1_fe_normalize_to_zero_var(&t);"
185        ));
186    }
187
188    #[test]
189    fn skips_preprocessor_comments_and_testrand_diagnostics() {
190        assert!(skipped_by_global_secp256k1_lists("# ifdef VERIFY"));
191        assert!(skipped_by_global_secp256k1_lists(
192            "    fprintf(stderr, \"random seed failure\\n\");"
193        ));
194        assert!(skipped_by_global_secp256k1_lists(
195            "    fp = fopen(\"/dev/urandom\", \"rb\");"
196        ));
197        assert!(skipped_by_global_secp256k1_lists(
198            "    fread(seed, 1, sizeof(seed), fp);"
199        ));
200        assert!(skipped_by_global_secp256k1_lists("    // comment"));
201        assert!(!skipped_by_global_secp256k1_lists("    ret = a + b;"));
202    }
203}