Package integrity
rusty-js-pm-integrity computes the hashes behind npm integrity checking: SHA-1/256/512 digests, base64 and hex codecs, and verify_sri / verify_shasum, which compare a downloaded tarball's hash against the one the registry promised. It reports a match or mismatch; whether that aborts the install is decided in the package manager one layer up.
On every npm install, the registry hands back a dist object for each package: a tarball URL, an integrity string like sha512-oJ...==, and sometimes a legacy shasum. Before the bytes land in node_modules, something recomputes the hash of the downloaded tarball and compares it to what the registry promised. If they disagree, the install aborts.
In Cruft, that code is rusty-js-pm-integrity. This page is about what the crate computes and, just as important, what it does not decide.
Alpha.rusty-js-pm-integrity(Cruft 0.0.10) is a crate that replaces the crates.io crypto stack npm tooling leans on (thesha1/sha2/ssri-style hashing and encoding crates). It has not been externally audited. Treat its properties as young except where this page cites an in-tree test. Do not rely on it against adversarial input in production.
Arithmetic here, policy in the package manager
"Integrity checking" is two things. There is the arithmetic that turns tarball bytes into a digest and compares it to an expected value, and there is the policy that decides which hash to trust, whether a mismatch is fatal, and whether a missing hash is allowed.
rusty-js-pm-integrity is only the arithmetic. It is a pure, allocation-simple library: bytes in, digest out, Ok(()) or an error. It holds no network connection, reads no packument, and makes no install decision. The policy lives one crate up, in rusty-js-pm (the package manager). That boundary matters, because most of what reads as "integrity" is policy, and this crate is deliberately not where policy lives.
What it computes
The whole surface is one module. It gives the package manager:
- RFC 4648 codecs:
decode_base64/encode_base64anddecode_hex/encode_hex_lower, delegated to the siblingrusty_js_basencrate. SRI strings are base64; legacy shasums are hex. - FIPS 180-4 digests:
sha1_digest(20 bytes),sha256_digest(32),sha512_digest(64). Each is the textbook block loop with the standard round constants (K256,K512are in the source). verify_sri(bytes, sri): parses a Subresource Integrity string, decodes the base64 expected digest, recomputes, and compares.verify_shasum(bytes, shasum): the legacy path, hex-decodes the expected SHA-1 and compares.canonical_sha256_hex: a convenience hex digest used elsewhere in the PM.
Known-answer tests back these. The SHA-1, SHA-256, and SHA-512 digests are checked against the published FIPS empty-string and "abc" vectors; the base64 and hex codecs are checked on both valid decodes and malformed-input rejection; and the two verify paths are exercised on matching and non-matching input.
verify_sri only knows sha512
An SRI verifier could handle the whole SRI grammar (sha256, sha512, multiple hashes, option strings). This one does not. verify_sri splits on the first -, and the match has exactly one real arm, "sha512". Anything else, including "sha256", returns IntegrityError::UnsupportedAlgorithm. That is a defensible narrowing (npm's integrity field is sha512 in practice), but it is a narrowing. It covers the npm-registry happy path and is not a general ssri replacement. If a packument ever advertised a sha256 SRI, this crate would refuse it rather than verify it.
SHA-1 is present, and it is the dangerous one
verify_shasum computes SHA-1, which is collision-broken. The crate ships it on purpose, because old registry metadata still carries dist.shasum and nothing else. SHA-1's presence here is not itself the risk. The risk is when the package manager chooses to fall back to it. If an attacker who can edit packument metadata strips the strong integrity field, they could force the install onto the weak SHA-1 path, which they can forge.
That decision is policy, so it is not in this crate. The package manager (rusty-js-pm) handles it: a SHA-1-only dist.shasum (no integrity) is refused by default with a WeakLegacyShasum error, and the legacy verifier runs only behind an explicit CRUFT_PM_ALLOW_SHA1_SHASUM=1 opt-in. Focused tests cover both the default refusal and the opt-in verification. The downgrade is closed at the layer that owns the choice, and verify_shasum remains here only as the arithmetic the opt-in path calls.
Computing a mismatch is separate from aborting the install
verify_sri returning Err only matters if some caller treats that Err as fatal before extracting the tarball. That end-to-end property (download, verify, abort, with nothing written on mismatch) is the security claim that matters, and it is not provable from this crate alone.
It lives in rusty-js-pm. There, a mismatched sha512 SRI on a real gzipped tarball produces an integrity error and the staging directory is never created. So "a tampered package is rejected before it hits disk" is a tested property, but the test for it lives in the PM crate, not here.
Limitations
- This crate decides nothing. Every real integrity guarantee (which hash is authoritative, mismatch aborts, weak-hash refusal) is enforced in
rusty-js-pm, not here. Reading only this crate tells you the digests are correct, not that the install is safe. - Comparison is plain byte-equality, not constant-time. Both
verify_sriandverify_shasumcompare with==on slices. For integrity verification this is acceptable, because the expected digest is public registry metadata, not a secret, so there is no timing oracle to leak. Reusing these helpers to compare a secret (an HMAC, a token) breaks that assumption. They are not written for that. - The verifier is narrow.
verify_srihandles sha512 only; no sha256, no multi-hash SRI, no SRI options. It is the npm path, not the SRI spec. - Integrity does not authenticate the source. Even a passing sha512 check only proves the bytes match the hash in the same
distobject. An attacker who controls the packument controls both thetarballURL and the matching hash, and there is no registry-host pinning or SSRF egress control on the tarball URL (tracked separately as the tarball-substitution finding). Integrity is tamper-detection against a trusted metadata channel, not proof the package is genuine. - Maturity. The crates.io stack this replaces (
sha1,sha2,ssri) is battle-tested, has full SRI-grammar support, and in the case of the RustCrypto digests has been reviewed and fuzzed for years. This crate is small, with known-answer tests for the standard empty-string andabcvectors per algorithm. The math is standard and the KATs pass, but passing those vectors is a much smaller claim than audited and fuzzed.