Geek on Nostr: Just opened a PR hardening how Primal's web app encrypts keys at rest which is a bit ...
Just opened a PR hardening how Primal's web app encrypts keys at rest which is a bit meatier than the one I submitted this morning.
Previously the PIN encrypted an nsec with a single unsalted SHA-256 hash of the PIN and unauthenticated AES-CBC. The PR upgrades this to PBKDF2 (600k iterations, random salt) and AES-256-GCM, which makes brute-forcing a stored blob drastically more expensive and makes wrong PINs fail cleanly. Existing users are migrated automatically on their next unlock, so nobody has to re-enter anything.
It also encrypts the NIP-46 client transport key, which was previously plaintext in localStorage, using a non-extractable WebCrypto key.
https://github.com/PrimalHQ/primal-web-app/pull/211Published at
2026-08-03 00:31:59 CESTEvent JSON
{
"id": "9f6add637266158ba1b60ffb4acdce7e2f415a0598465f68532225a82f6c6bef",
"pubkey": "daa41bedb68591363bf4407f687cb9789cc543ed024bb77c22d2c84d88f54153",
"created_at": 1785709919,
"kind": 1,
"tags": [],
"content": "Just opened a PR hardening how Primal's web app encrypts keys at rest which is a bit meatier than the one I submitted this morning.\r\n\r\nPreviously the PIN encrypted an nsec with a single unsalted SHA-256 hash of the PIN and unauthenticated AES-CBC. The PR upgrades this to PBKDF2 (600k iterations, random salt) and AES-256-GCM, which makes brute-forcing a stored blob drastically more expensive and makes wrong PINs fail cleanly. Existing users are migrated automatically on their next unlock, so nobody has to re-enter anything.\r\n\r\nIt also encrypts the NIP-46 client transport key, which was previously plaintext in localStorage, using a non-extractable WebCrypto key.\r\n\r\nhttps://github.com/PrimalHQ/primal-web-app/pull/211",
"sig": "7e39ba7a689803ad69333ebe4e6c259e00e66c03d9b20ccaba5493584d41b2aef13fd62ada287988ebba9f5bf22888fb92afed85940159ba0486481707571cb6"
}