{"record":{"id":"ee181b188140688e","repo":"trailofbits/algo","slug":"private-key-file-must-be-either-base64-or-exactly","errorCode":null,"errorMessage":"Private key file must be either base64 or exactly 32 raw bytes, got {len(data)} bytes","messagePattern":"Private key file must be either base64 or exactly 32 raw bytes, got (.+?) bytes","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"library/x25519_pubkey.py","lineNumber":73,"sourceCode":"\n    if module.params[\"private_key_path\"]:\n        try:\n            with open(module.params[\"private_key_path\"], \"rb\") as f:\n                data = f.read()\n            try:\n                # First attempt: assume file contains base64 text data\n                # Strip whitespace from edges for text files (safe for base64 strings)\n                stripped_data = data.strip()\n                base64.b64decode(stripped_data, validate=True)\n                priv_b64 = stripped_data.decode()\n            except (base64.binascii.Error, ValueError):\n                # Second attempt: assume file contains raw binary data\n                # CRITICAL: Do NOT strip raw binary data - X25519 keys can contain\n                # whitespace-like bytes (0x09, 0x0A, etc.) that must be preserved\n                # 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)}\")","sourceCodeStart":55,"sourceCodeEnd":91,"githubUrl":"https://github.com/trailofbits/algo/blob/20e22a8715c198b38c01c1ca62d0953b93587a74/library/x25519_pubkey.py#L55-L91","documentation":"The x25519_pubkey module accepts a private key file containing either base64 text or exactly 32 raw bytes. If the file content is not valid base64 (first decode attempt failed) and its byte length is not exactly 32, this error is raised naming the actual byte count. Note raw binary data is deliberately not stripped because X25519 keys may contain whitespace-like bytes.","triggerScenarios":"Passing a private_key_path whose content is, e.g., 31 or 64 raw bytes, a hex-encoded key (64 chars -> not base64 -> len != 32), or a base64 key with trailing newline that failed strict base64 decoding so it fell through to the raw-bytes branch.","commonSituations":"Using `wg genkey` output with a trailing newline where the base64 attempt failed, hex-encoded keys, or copying a key with an extra/missing character.","solutions":["Ensure the file contains exactly the 44-character base64 key (wg genkey format) or exactly 32 raw bytes","Remove trailing whitespace/newline only if the content is base64, not raw binary","Verify length: wc -c on the file should be 44/45 (base64) or 32 (raw)","If you have hex, convert to 32 raw bytes first: xxd -r -p"],"exampleFix":"# before: file contains hex key (64 chars)\n# after: printf '%s' \"$HEXKEY\" | xxd -r -p > priv.key   # 32 raw bytes","handlingStrategy":"validation","validationCode":"data = open(path, 'rb').read()\nassert len(data) == 32, f'expected 32 raw bytes or base64, got {len(data)}'","typeGuard":"def is_valid_key_file(data: bytes) -> bool:\n    import base64\n    try:\n        return len(base64.b64decode(data, validate=True)) == 32\n    except Exception:\n        return len(data) == 32","tryCatchPattern":null,"preventionTips":["Generate keys with wg genkey (44-char base64) or cryptography","Never strip raw binary key files","Check byte count before passing files to the module"],"tags":["x25519","wireguard","key-format","ansible"],"backgroundTag":"invalid-key-encoding","analyzedSha":"20e22a8715c198b38c01c1ca62d0953b93587a74","analyzedAt":"2026-08-28T13:26:02.752Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}