{"record":{"id":"f5c9b588f0445ad0","repo":"trailofbits/algo","slug":"invalid-base64-private-key-format-e","errorCode":null,"errorMessage":"Invalid base64 private key format: {e}","messagePattern":"Invalid base64 private key format: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"library/x25519_pubkey.py","lineNumber":88,"sourceCode":"                # Stripping would corrupt the key and cause \"got 31 bytes\" errors\n                if len(data) != 32:\n                    module.fail_json(\n                        msg=f\"Private key file must be either base64 or exactly 32 raw bytes, got {len(data)} bytes\"\n                    )\n                priv_b64 = base64.b64encode(data).decode()\n        except OSError as e:\n            module.fail_json(msg=f\"Failed to read private key file: {e}\")\n    else:\n        priv_b64 = module.params[\"private_key_b64\"]\n\n    # Validate input parameters\n    if not priv_b64:\n        module.fail_json(msg=\"No private key provided\")\n\n    try:\n        priv_raw = base64.b64decode(priv_b64, validate=True)\n    except Exception as e:\n        module.fail_json(msg=f\"Invalid base64 private key format: {e}\")\n\n    if len(priv_raw) != 32:\n        module.fail_json(msg=f\"Private key must decode to exactly 32 bytes, got {len(priv_raw)}\")\n\n    try:\n        priv_key = x25519.X25519PrivateKey.from_private_bytes(priv_raw)\n        pub_key = priv_key.public_key()\n        pub_raw = pub_key.public_bytes(encoding=serialization.Encoding.Raw, format=serialization.PublicFormat.Raw)\n        pub_b64 = base64.b64encode(pub_raw).decode()\n        result[\"public_key\"] = pub_b64\n\n        if module.params[\"public_key_path\"]:\n            pub_path = module.params[\"public_key_path\"]\n            existing = None\n\n            try:\n                with open(pub_path) as f:\n                    existing = f.read().strip()","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/trailofbits/algo/blob/20e22a8715c198b38c01c1ca62d0953b93587a74/library/x25519_pubkey.py#L70-L106","documentation":"The provided base64 private key failed strict decoding (base64.b64decode with validate=True): it contains characters outside the base64 alphabet or its length is not a multiple of 4. The binascii.Error detail is embedded in the message.","triggerScenarios":"Key string with whitespace/newlines, URL-safe base64 (-/_ instead of +/), hex text, or a truncated/copy-paste-corrupted key passed via private_key_b64.","commonSituations":"Reading a key file without stripping the trailing newline, copying keys through a medium that mangled + into space, or using base64url output from another tool.","solutions":["Strip whitespace: private_key_b64 | trim in Jinja, or .strip() in Python","Convert base64url to standard base64 (replace - with +, _ with /) and add padding","Re-copy the key carefully; verify it is 44 chars ending with '=' for a 32-byte key","Regenerate the key pair if provenance is unknown"],"exampleFix":"# before\nprivate_key_b64: \"{{ lookup('file', path) }}\"\n# after\nprivate_key_b64: \"{{ lookup('file', path) | trim }}\"","handlingStrategy":"validation","validationCode":"import base64\nkey = key_str.strip().replace('-', '+').replace('_', '/')\nkey += '=' * (-len(key) % 4)\nbase64.b64decode(key, validate=True)  # raises if still invalid","typeGuard":"def is_base64_key(s: str) -> bool:\n    import base64\n    try:\n        return len(base64.b64decode(s.strip(), validate=True)) == 32\n    except Exception:\n        return False","tryCatchPattern":null,"preventionTips":["Trim whitespace when loading keys from files","Avoid base64url encodings for WireGuard keys"],"tags":["x25519","base64","key-format","ansible"],"backgroundTag":"invalid-base64-encoding","analyzedSha":"20e22a8715c198b38c01c1ca62d0953b93587a74","analyzedAt":"2026-08-28T13:26:02.752Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}