1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
//! Constant-time verification helpers (ctgrind pattern).
//!
//! These helpers wire secret material into Valgrind memcheck's definedness
//! tracking, following the classic ctgrind idea (as used by graviola, aws-lc,
//! and NSS): memory holding secrets is marked *undefined*, so any
//! secret-dependent branch or memory index in the compiled artifact surfaces
//! as a memcheck error when the test harness runs under
//! `valgrind --track-origins=yes --error-exitcode=99`.
//!
//! Without the private `__ctgrind` cargo feature every function here compiles
//! to a no-op; with it, they emit Valgrind client requests via [`crabgrind`]
//! (a safe API — compatible with `#![forbid(unsafe_code)]`). The feature is
//! internal to pakery's CI (`ct.yml`) and must never be enabled in production
//! builds — the double-underscore prefix marks it exempt from semver.
//!
//! # Marking policy
//!
//! Marked secret at their byte boundaries: passwords, scalar/private-key
//! bytes, DH outputs, OPRF outputs, PRKs and derived keys, envelope contents.
//!
//! Declassified (each call site carries a justification comment):
//! - public-key and key-share encodings before wire serialization — public
//! by protocol design;
//! - MAC tags before wire output — public once sent;
//! - ciphertext (e.g. OPAQUE `masked_response`) before wire output;
//! - boolean accept/reject decisions after a `subtle::ct_eq` comparison
//! ([`declassify_choice`]) — the abort/continue outcome is public;
//! - copies of secret-scalar encodings fed to `curve25519-dalek` /
//! `p256` deserializers ([`declassify`] on a temporary copy): their
//! canonicity checks branch on a `subtle::CtOption` discriminant, which
//! memcheck would flag inside the dependency. Canonicity of an honestly
//! generated key is public information, and constant-time verification of
//! dependency-internal primitives is out of scope here (tracked upstream;
//! see SECURITY_TESTING.md, "Constant-time verification"). Where a parse is laundered
//! this way, the *result* of the following group operation is re-marked
//! secret so taint stays end-to-end.
/// Whether the helpers emit real Valgrind client requests.
///
/// True only when the `__ctgrind` feature is on **and** crabgrind was built
/// against real Valgrind headers. Without headers (e.g. a `--all-features`
/// build on macOS or a runner without Valgrind installed) crabgrind falls
/// back to stub headers whose version is the sentinel `0xBEDABEDA`, and
/// every client request would panic at runtime — so the helpers downgrade
/// to no-ops instead. `ct.yml` asserts this returns true (via the harness's
/// `ct_harness_is_armed` test) to rule out a silently disarmed run.
/// Mark `bytes` as secret: Valgrind memcheck treats them as undefined, so any
/// branch or memory index computed from them is reported as an error.
///
/// No-op unless the `__ctgrind` feature is enabled (and, at runtime, the
/// process runs under Valgrind).
/// Declassify `bytes`: mark them as defined (public) for Valgrind memcheck.
///
/// Use only at boundaries where the data is genuinely public (wire output,
/// public-key encodings, MAC tags being sent) — every call site must carry a
/// justification comment. No-op unless the `__ctgrind` feature is enabled.
/// Declassify the boolean outcome of a constant-time comparison.
///
/// The accept/reject decision after a `ct_eq` (MAC verification,
/// identity-point rejection, shared-secret equality) is public: the protocol
/// visibly aborts or continues. Converting `Choice → bool` directly would
/// branch on secret-derived data (memcheck flags both the caller's `if` and
/// `subtle`'s internal `debug_assert!`), so the decision byte is copied to
/// the stack, declassified, and only then read.