
Security News
Happy Birthday, Shai-Hulud
It has been one year since Shai-Hulud made its first appearance on npm.
zk-password
Advanced tools
Zero-Knowledge password verification using Noir + Poseidon2 hash + Barretenberg.
This library allows clients to prove knowledge of a password without revealing it to the backend using zk-SNARKs.
npm install zk-password @aztec/bb.js@0.84.0 @noir-lang/noir_js@1.0.0-beta.6 argon2-browser@1.18.0
✅ No need to manually compile Noir —
zk_password.jsonis already bundled.
import { ZkPassword } from 'zk-password';
const zk = await ZkPassword.init();
const password = 'secret_password';
const userTag = 'user@example.com';
const { passwordHash, salt } = await zk.register(password, userTag);
// Send this to backend:
fetch('/api/register', {
method: 'POST',
body: JSON.stringify({
user_tag: userTag,
password_hash: passwordHash,
salt,
}),
});
// First, request salt + nonce from backend
const userTag = 'user@example.com';
const res = await fetch(`/api/login-init?user_tag=${userTag}`);
const { salt, nonce } = await res.json();
const zk = await ZkPassword.init();
const result = await zk.login('secret_password', userTag, salt, nonce);
// Send this to backend for verification:
fetch('/api/login-complete', {
method: 'POST',
body: JSON.stringify({
user_tag: userTag,
proof: result.proof,
publicSignals: result.publicSignals,
}),
});
import { verifyProof } from 'zk-password';
app.post('/api/login-complete', async (req, res) => {
const { user_tag, proof, publicSignals } = req.body;
const isValid = await verifyProof(proof);
if (!isValid) return res.status(400).json({ error: 'Invalid ZK proof' });
const user = await db.findUser(user_tag);
if (!user || user.password_hash !== publicSignals.password_hash) {
return res.status(401).json({ error: 'Unauthorized' });
}
if (await db.isNullifierUsed(publicSignals.nullifier_out)) {
return res.status(409).json({ error: 'Replay detected' });
}
await db.markNullifier(publicSignals.nullifier_out);
// Issue access/refresh tokens as needed
const tokens = generateTokens(user.id);
res.json(tokens);
});
User enters password and userTag (e.g., email or username).
Client generates a random salt (16 bytes).
Derives password hash with Argon2: preimage = Argon2(password, salt)
Computes:
tagHash = Poseidon(userTag)password_hash = Poseidon(preimage, tagHash)Sends the following JSON to the backend for storage:
{
"password_hash": "...",
"salt": "...",
"user_tag": "..."
}
Backend stores:
user_tag — acts as identifier.password_hash — later compared with value in proof.salt — returned to client during login.userTag{
"salt": "...",
"nonce": "..."
}
Client inputs password, reuses salt, и получает nonce
Computes:
preimage = Argon2(password, salt)tagHash = Poseidon(userTag)password_hash = Poseidon(preimage, tagHash)nullifier_out = Poseidon(preimage, nonce, tagHash)Generates zk-proof using Noir
Sends proof + public signals:
{
"proof": {
"proof": ["0x...", "0x..."],
...
},
"publicSignals": {
"password_hash": "...",
"session_nonce": "...",
"nullifier_out": "..."
},
"user_tag": "..."
}
Backend then:
verifyProof(proof)password_hash against storednullifier_outThreat: Attacker could precompute hashes for known passwords (rainbow table).
Why it fails:
password_hash is derived from Argon2(password, salt) + Poseidon, making precomputation infeasible.Threat: If an attacker gains access to the backend DB and obtains password_hash and salt.
Why it fails:
password_hash alone is not sufficient to log in.preimage).Threat: Reusing an old valid proof to re-authenticate.
Why it fails:
nonce (timestamp or UUID).nullifier_out binds proof to that session.nullifier_out to detect reuse.Threat: Generating a valid proof without knowing the password.
Why it fails:
UltraHonkBackend guarantees zk soundness and security.▶️ Running the Example
To test the library in a real browser environment:
cd example npm install npm run dev
This will start a Vite development server.You can interact with the zk-password functionality via the provided HTML form at http://localhost:5173.
Apache License 2.0
Copyright 2025 Igor Peregudov
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
FAQs
Zero-Knowledge password authentication using Noir and Poseidon2.
The npm package zk-password receives a total of 1 weekly downloads. As such, zk-password popularity was classified as not popular.
We found that zk-password demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.

Security News
It has been one year since Shai-Hulud made its first appearance on npm.

Research
/Security News
Operators behind PolinRider used a compromised GitHub account to plant malware in four development versions of a Packagist package with 700,000+ downloads.

Security News
GitHub Actions now supports cache-mode, a least-privilege control on the Actions cache aimed at the cache poisoning technique behind recent compromises.