OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-identity commits / b2e7389

auth: SSH fingerprint is real SHA-256, not BLAKE2b mislabeled "SHA256:"
SSHKeyParser.fingerprint used hypercore-crypto.hash (BLAKE2b-256) then prefixed the digest with "SHA256:", so fingerprints never matched ssh-keygen -lf / ssh-add -l / GitHub. The SSH push path still worked (enrollment, the baked authorized_keys --fp=, and git-acl-check all use the same stored value), but Channel C admin approval and every human-facing fingerprint comparison were broken. Switch to sodium-native crypto_hash_sha256 (already a direct dep, used elsewhere in this package; bare- and node-safe). Pin a known-vector assertion equal to `ssh-keygen -lf` output so this can't regress — the prior test only checked the "SHA256:" prefix + determinism, which the BLAKE2b digest also satisfied. Bump 1.0.0 -> 1.0.1. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
b2e7389b69d1352d2c23fb9aedaf70421755389a
by plankatron <fleeky@gmail.com> · 2026-06-09T15:39:34.000Z

Changed files (3) +13 -3

lib/auth/ssh-parser.js +6 -2
……@@ -1,5 +1,5 @@
11import b4a from 'b4a'
2import crypto from 'hypercore-crypto'
2import sodium from 'sodium-native'
33
44/**
55 * SSH Key Parser
……@@ -21,7 +21,11 @@ export class SSHKeyParser {
2121 const parts = sshPublicKey.trim().split(/\s+/)
2222 if (parts.length < 2) throw new Error('Invalid SSH key format')
2323 const keyBlob = b4a.from(parts[1], 'base64')
24 const digest = crypto.hash(keyBlob) // SHA-256
24 // Real SHA-256 — must match `ssh-keygen -lf`. (Previously used
25 // hypercore-crypto.hash, which is BLAKE2b-256, so the digest was
26 // mislabeled "SHA256:" and never matched OpenSSH fingerprints.)
27 const digest = b4a.alloc(sodium.crypto_hash_sha256_BYTES)
28 sodium.crypto_hash_sha256(digest, keyBlob)
2529 // OpenSSH strips '=' padding from base64
2630 return 'SHA256:' + b4a.toString(digest, 'base64').replace(/=+$/, '')
2731 }
package.json +1 -1
……@@ -1,6 +1,6 @@
11{
22 "name": "hyper-identity",
3 "version": "1.0.0",
3 "version": "1.0.1",
44 "description": "Generic P2P identity management with Autobase, ANProto, and multi-method authentication",
55 "type": "module",
66 "main": "index.js",
test/ssh-scan.test.js +6 -0
……@@ -59,6 +59,12 @@ test('SSHKeyParser.fingerprint is deterministic and matches ssh-keygen format',
5959 t.ok(a.startsWith('SHA256:'))
6060 t.absent(a.endsWith('='), 'no base64 padding (ssh-keygen convention)')
6161
62 // Known-vector: MUST equal the real OpenSSH fingerprint (`ssh-keygen -lf`).
63 // Regression guard for the BLAKE2b-mislabeled-as-SHA256 bug — the fp used to
64 // be hypercore-crypto.hash (BLAKE2b-256), which passed the prefix/determinism
65 // checks above but never matched ssh-keygen / ssh-add -l / GitHub.
66 t.is(a, 'SHA256:a32IzTLPV5ODqnTOYvICRbujbXaS/ZXa1ozJbzz/jOs', 'real SHA-256, matches ssh-keygen -lf')
67
6268 // Two different keys produce different fingerprints
6369 const c = SSHKeyParser.fingerprint(RSA_KEY)
6470 t.not(a, c)