{"record":{"id":"769da834f9772abd","repo":"ory/hydra","slug":"errunknownhashalgorithm","errorCode":"ErrUnknownHashAlgorithm","errorMessage":"unknown hash algorithm","messagePattern":"unknown hash algorithm","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"oryx/hasherx/hash_comparator.go","lineNumber":18,"sourceCode":"package hasherx\n\nimport (\n\t\"context\"\n\t\"crypto/subtle\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"math\"\n\t\"regexp\"\n\t\"strings\"\n\n\t\"github.com/pkg/errors\"\n\t\"golang.org/x/crypto/argon2\"\n\t\"golang.org/x/crypto/bcrypt\"\n\t\"golang.org/x/crypto/pbkdf2\"\n)\n\nvar ErrUnknownHashAlgorithm = errors.New(\"unknown hash algorithm\")\n\n// Compare the given password with the given hash.\nfunc Compare(ctx context.Context, password []byte, hash []byte) error {\n\tswitch {\n\tcase IsBcryptHash(hash):\n\t\treturn CompareBcrypt(ctx, password, hash)\n\tcase IsArgon2idHash(hash):\n\t\treturn CompareArgon2id(ctx, password, hash)\n\tcase IsArgon2iHash(hash):\n\t\treturn CompareArgon2i(ctx, password, hash)\n\tcase IsPbkdf2Hash(hash):\n\t\treturn ComparePbkdf2(ctx, password, hash)\n\tdefault:\n\t\treturn errors.WithStack(ErrUnknownHashAlgorithm)\n\t}\n}\n\nfunc CompareBcrypt(_ context.Context, password []byte, hash []byte) error {","sourceCodeStart":1,"sourceCodeEnd":36,"githubUrl":"https://github.com/ory/hydra/blob/4174065ffb052799890f7480f5360a877a67ffc1/oryx/hasherx/hash_comparator.go#L1-L36","documentation":"hasherx.Compare dispatches on the hash encoding prefix ($argon2id$, $argon2i$, $2a$ bcrypt, $pbkdf2$ etc.). If the hash's algorithm identifier is none of the recognized kinds, Compare returns ErrUnknownHashAlgorithm because it has no comparator for that scheme.","triggerScenarios":"Calling Compare(ctx, password, hash) where hash carries an unrecognized identifier (e.g. $scrypt$, a bare SHA-256 hex string, or a truncated/garbled prefix), so the prefix-based switch falls to the default branch.","commonSituations":"Importing users from another system using an unsupported algorithm, hashes damaged by truncation or base64 re-encoding, hand-crafted test fixtures, or hashes stored without their algorithm prefix.","solutions":["Store hashes produced by a supported scheme (bcrypt, argon2id, argon2i, pbkdf2) with their full standard prefix intact.","Check the stored hash string starts with a supported prefix ($2a/$2b/$2y, $argon2id$, $argon2i$, $pbkdf2) before calling Compare.","Re-hash imported credentials with a supported hasher on next login (progressive migration) instead of comparing them directly.","Fix storage/encoding steps that strip or mangle the prefix (e.g. URL-safe base64 rewrites)."],"exampleFix":"// before\nif !strings.HasPrefix(string(storedHash), \"$2\") { /* raw sha256 hex */ }\nerr := hasherx.Compare(ctx, pw, storedHash) // -> unknown hash algorithm\n// after\nhash, _ := hasherx.BcryptHasher{}.Generate(ctx, pw) // or argon2id hasher\nerr := hasherx.Compare(ctx, pw, hash)","handlingStrategy":"validation","validationCode":"func knownHashFormat(hash []byte) bool {\n  p := string(hash)\n  return strings.HasPrefix(p, \"$2a$\") || strings.HasPrefix(p, \"$2b$\") ||\n    strings.HasPrefix(p, \"$2y$\") || strings.HasPrefix(p, \"$argon2id$\") ||\n    strings.HasPrefix(p, \"$argon2i$\") || strings.HasPrefix(p, \"$pbkdf2\")\n}\n// guard before Compare","typeGuard":"func isSupportedHash(b []byte) bool { return knownHashFormat(b) }","tryCatchPattern":"if err := hasherx.Compare(ctx, pw, hash); err != nil {\n  if errors.Is(err, hasherx.ErrUnknownHashAlgorithm) {\n    // flag for re-hash/migration instead of failing auth outright\n    return migrateAndRehash(user, pw)\n  }\n  return err\n}","preventionTips":["Only store hashes generated with supported algorithms","Preserve the full hash string including its algorithm prefix in the DB","Re-hash imported credentials to a supported scheme","Add a startup check that fixture/config hashes parse"],"tags":["passwords","hashing","crypto","migration"],"backgroundTag":"unsupported-hash-algorithm","analyzedSha":"4174065ffb052799890f7480f5360a877a67ffc1","analyzedAt":"2026-09-03T14:52:41.581Z","contentChangedAt":"2026-09-03T14:52:41.581Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}