Rule
publicly-derivable-encryption-secret
A published digest determines the scalar of a registered curve point through resolved native calls.
Specimen 20 of 96 · Audit-default ordinary rules
- Severity
- high
- Confidence
- varies
- Maturity
- experimental
- Selection
- Audit default
- Policy intent
- not required
- Release
- v0.1.1
- Engine facts used
resolved-callsprivate-provenancepublic-sinksdirect-hash-inputsexact-invocation-operands
Selection and scope#
Selected by the default audit profile, so an ordinary scan runs it.
./.nightshade/node_modules/.bin/nightshade ./contracts --detectors publicly-derivable-encryption-secret
./.nightshade/node_modules/.bin/nightshade detector show publicly-derivable-encryption-secretThe first command runs only this rule; --detectors takes a comma-separated list. The second prints the rule's metadata from your installed version.
Severity is review priority. It does not express exploitability or impact. Confidence is detector metadata, not a probability. All built-in rules are experimental.
Compiler scope: the engine supports the pinned profiles 0.28.0, 0.29.0, 0.30.0, 0.31.1, 0.33.0-rc.2, 0.34.0, 0.35.0-rc.1 and 0.35.0, with ZKIR2 by default. Support for this rule can be narrower; the rule documentation below states any narrower scope.
Corpus checks#
Synthetic corpus checks for this rule in the 0.1.1 proof run. A check counts only with compiler acceptance and complete analysis.
| Positive detected | Corrected quiet | Lookalike quiet | Other negative quiet | Incomplete |
|---|---|---|---|---|
| 1/1 | 1/1 | 1/1 | 0/0 | 0 |
No deviations were recorded for this rule.
These are behavior checks on synthetic fixtures, not a precision or recall estimate. See evaluation.
Rule documentation#
From docs/detectors/publicly-derivable-encryption-secret.md at v0.1.1.
Availability: default-on, high severity / high confidence in the structural relationship. Select with --detectors publicly-derivable-encryption-secret.
Description#
A public digest can also determine the scalar used for a registered curve point. If the application treats that point as an encryption key, review whether its scalar was intended to remain private. The finding proves the modeled derivation relationship; it does not establish encryption use or application intent.
Example#
An owned synthetic derivation:
pure circuit makePoint(seed: Bytes<32>): JubjubPoint {
const digest = persistentHash<Vector<1, Bytes<32>>>([seed]);
return ecMulGenerator(degradeToTransient(digest));
}
export circuit register(): [] {
const seed = keyMaterial();
accountId = disclose(persistentHash<Vector<1, Bytes<32>>>([seed]));
registeredPoint = disclose(makePoint(seed));
}The published digest supplies the same degradeToTransient input used in the point derivation. Separating hash domains or using independent secret material breaks this exact relationship.
Scenario and evidence#
The rule checks canonical resolved native calls, typed operands and straight-line helper bodies, including immutable local aliases. Provider names, source notices, commits and file fingerprints confer no role. The supported boundary is compiler 0.31.1, language 0.23.0 and ZKIR2 with its Field-based curve APIs. Different native profiles, unavailable recipe proofs and exhausted applicability budgets produce rule-scoped incomplete coverage when reached.
A finding also requires the same exact immutable raw witness operand, a public persistentHash<Vector<1, Bytes<32>>> digest in an on-chain sink, an observed successful derivation invocation, and its exact result as the last whole-root JubjubPoint ledger write on one complete modeled outcome. Invocation arguments and results are recorded separately. Provenance sets, declaration names, flow strings and wildcard storage addresses never prove equality.
Last-write checking rejects overwritten registrations. Committed checkpoint prefixes are separate outcomes. An impossible entire proof supplies no successful prefix. The optional observations retain the baseline engine and original budgets; missing invocation/digest evidence is explicitly incomplete.
Recommendation#
Separate public-identifier and private-key hash domains, or use independent secret material. Confirm whether the registered point is a cryptographic key before treating the structural finding as a vulnerability.
Known false positives and limits#
An intentionally public scalar or a curve point used for another purpose can match. Encryption/ciphertext use, intended secrecy and exploitability are not proven. Conditional/loop derivation bodies, arbitrary curve arithmetic, transformed seeds, Map/struct registrations and cross-entry key lifecycles are outside this proof boundary. Unknown reached recipe bodies are incomplete, not certified safe. Independent witness invocations, different hash types/domains, discarded points, exported-return-only digests and known constant-point derivations are quiet.
Validation#
Owned compiler-valid positive, domain-separated and independent-secret controls exercise instruction/reference and cached/uncached evidence. Tests retain nested and repeated calls, caller guards, state invalidation, checkpoint prefixes, exact typed hash distinctions and explicit resource failures. These checks do not establish independent application recall.
Suppress a match#
If a match is intentional, suppress it on the line before with the exact rule ID and a reason of at least eight characters:
// nightshade-disable-next-line publicly-derivable-encryption-secret -- Reason this match is intentionalDiagnostics cannot be suppressed, and an unmatched directive for a selected rule makes analysis incomplete. See configuration for fingerprint suppressions and baselines.