Skip to content

Add Nokia SR OS $2y$ bcrypt password support - #6

Merged
antoinekh merged 8 commits into
masterfrom
nokia-bcrypt-password
Aug 19, 2026
Merged

antoinekh merged 8 commits into
masterfrom
nokia-bcrypt-password

Conversation

@antoinekh

Copy link
Copy Markdown
Owner

Adds Nokia SR OS local user passwords as an eighth format, in the Python package, the CLI and the website.

SR OS stores local user passwords as bcrypt, written in config as $2y$10$<22-char salt><31-char digest>. It is a one-way hash, so it follows the contract Cisco type 8 and type 9 established: encrypt hashes, check verifies a candidate password, decrypt raises. On the site it gets Hash and Verify tabs and a One-way badge, and joins the existing Nokia vendor menu.

Known-answer vector, pinned in both test suites:

$2y$10$jBwKMP7r.vf4x1tbThl7Y.iBIgdDpv8WZ4DTgrnNIZdJS97NUorVe   is   lab123

Two behaviours worth knowing

$2a$, $2b$ and $2y$ all verify. They are the same algorithm with different historical markers, so accepting all three is not guesswork. Hashing always emits $2y$, which is what SR OS writes. The recomputed value in check's return tuple preserves whichever prefix you passed in.

The cost is bounded to 4-16, and that bound is load-bearing. bcrypt's cost is an exponent read from the value being checked, so an unbounded cost lets a pasted hash force arbitrarily expensive work. Measured: cost 10 is 0.11s, cost 14 is 1.9s, cost 16 is 8.5s, cost 31 is roughly 583 hours. Without the bound, pasting a $2y$31$ hash into the website would freeze the browser tab indefinitely. This mirrors how juniper8 bounds its iteration count for the same reason. The web implementation also uses bcryptjs's asynchronous API, so the converter's "Working" state can actually paint.

A malformed cost field is rejected rather than silently treated as a mismatch. bcrypt always writes the cost zero-padded to two digits, so $2y$4$... is not a low-cost hash, it is invalid input; reporting "no match" for it would tell the user their password was wrong when it was their input that was malformed.

Dependencies

Two new ones, both necessary: Python 3.13 removed the stdlib crypt module and Web Crypto has no bcrypt, so bcrypt>=4.0 and bcryptjs. Hand-rolling bcrypt would mean implementing Blowfish's key schedule in security-sensitive code, which is not a trade worth making.

Verification

Python 178 tests, web 168 tests, npm run check clean, npm run build succeeds. Both implementations were checked against each other directly: same vector, all three prefixes, byte-identical error messages for every malformed input, and immediate rejection of an out-of-range cost without hashing. The site was verified by hand in a browser, including that a cost-31 hash errors instantly instead of hanging the tab.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 19, 2026 •

Copy link
Copy Markdown

Deploying network-secret with  Cloudflare Pages  Cloudflare Pages

Latest commit: 18a8c3f
Status:⚡️  Build in progress...

View logs

@antoinekh
antoinekh merged commit b80224d into master Aug 19, 2026
4 of 5 checks passed
@antoinekh
antoinekh deleted the nokia-bcrypt-password branch August 19, 2026 14:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant