{"record":{"id":"ed17955f89d2aa96","repo":"moonD4rk/HackBrowserData","slug":"encrypted-key-unexpected-prefix-got-q-want-q","errorCode":null,"errorMessage":"encrypted_key unexpected prefix: got %q, want %q","messagePattern":"encrypted_key unexpected prefix: got %q, want %q","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"masterkey/retriever_windows.go","lineNumber":39,"sourceCode":"\t\treturn nil, fmt.Errorf(\"read Local State: %w\", err)\n\t}\n\n\tencryptedKey := gjson.GetBytes(data, \"os_crypt.encrypted_key\")\n\tif !encryptedKey.Exists() {\n\t\treturn nil, fmt.Errorf(\"os_crypt.encrypted_key not found in Local State\")\n\t}\n\n\tkeyBytes, err := base64.StdEncoding.DecodeString(encryptedKey.String())\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"base64 decode encrypted_key: %w\", err)\n\t}\n\n\tconst dpapiPrefix = \"DPAPI\"\n\tif len(keyBytes) <= len(dpapiPrefix) {\n\t\treturn nil, fmt.Errorf(\"encrypted_key too short: %d bytes\", len(keyBytes))\n\t}\n\tif string(keyBytes[:len(dpapiPrefix)]) != dpapiPrefix {\n\t\treturn nil, fmt.Errorf(\"encrypted_key unexpected prefix: got %q, want %q\", keyBytes[:len(dpapiPrefix)], dpapiPrefix)\n\t}\n\n\tmasterKey, err := crypto.DecryptDPAPI(keyBytes[len(dpapiPrefix):])\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"DPAPI decrypt: %w\", err)\n\t}\n\treturn masterKey, nil\n}\n\n// DefaultRetrievers wires the Windows tiers: DPAPI for v10, ABE for v20 (Chrome 127+, via reflective\n// injection). Both run — a profile upgraded from pre-v127 mixes v10+v20 and needs both (issue #578).\nfunc DefaultRetrievers() Retrievers {\n\treturn Retrievers{\n\t\tV10: &DPAPIRetriever{},\n\t\tV20: &ABERetriever{},\n\t}\n}\n","sourceCodeStart":21,"sourceCodeEnd":57,"githubUrl":"https://github.com/moonD4rk/HackBrowserData/blob/0503d04d7a8d0379d060268a74f1b149e5a0aad5/masterkey/retriever_windows.go#L21-L57","documentation":"Chrome's os_crypt.encrypted_key blob is defined as the ASCII string 'DPAPI' followed by a DPAPI-protected blob. RetrieveKey checks this magic prefix after base64 decoding and errors if the first 5 bytes differ, because the remaining bytes are about to be handed to DecryptDPAPI and would be meaningless without it.","triggerScenarios":"The decoded encrypted_key is longer than 5 bytes but does not start with the literal bytes 'DPAPI' — i.e. it is not a Chrome DPAPI key blob.","commonSituations":"Pointing at a Firefox/Gecko-based browser's key file or a non-Chromium browser with a different key format; a Chromium fork that changed the prefix; decrypting a value that is actually an AES-GCM v10 cookie ciphertext rather than the master key; hand-crafted test data.","solutions":["Confirm hints.LocalStatePath points to a Chromium-based browser's profile 'Local State' file (Chrome/Edge/Brave), not a Firefox profile.","Verify the field is os_crypt.encrypted_key — do not pass cookie ciphertext or other blobs here.","Check whether the target browser is an unusual Chromium fork with a different os_crypt scheme and route it accordingly.","If the field is genuinely corrupt, re-launch the browser to regenerate it."],"exampleFix":"// before: assuming any blob is a DPAPI key\nmasterKey, err := crypto.DecryptDPAPI(keyBytes)\n// after: verify the magic prefix first\nif !bytes.HasPrefix(keyBytes, []byte(\"DPAPI\")) {\n\treturn nil, fmt.Errorf(\"not a DPAPI key blob\")\n}\nmasterKey, err := crypto.DecryptDPAPI(keyBytes[5:])","handlingStrategy":"validation","validationCode":"raw, _ := base64.StdEncoding.DecodeString(encKey)\nif !bytes.HasPrefix(raw, []byte(\"DPAPI\")) {\n\treturn errors.New(\"not a Chromium os_crypt DPAPI key blob\")\n}","typeGuard":"func isChromiumEncryptedKey(b []byte) bool { return bytes.HasPrefix(b, []byte(\"DPAPI\")) }","tryCatchPattern":"key, err := retriever.RetrieveKey(hints)\nif err != nil && strings.Contains(err.Error(), \"unexpected prefix\") {\n\t// wrong browser/format; route to the correct retriever\n}","preventionTips":["Only feed os_crypt.encrypted_key into DPAPI retrieval — never cookie ciphertext.","Identify browser family (Chromium vs Firefox) before retrieval.","Verify with jq that you read the intended JSON path."],"tags":["dpapi","chromium","validation","windows"],"backgroundTag":"invalid-argument-format","analyzedSha":"0503d04d7a8d0379d060268a74f1b149e5a0aad5","analyzedAt":"2026-09-06T13:38:28.707Z","contentChangedAt":"2026-09-06T13:38:28.707Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}