trailofbits/algo · error
Private key file must be either base64 or exactly 32 raw byt
Error message
Private key file must be either base64 or exactly 32 raw bytes, got {len(data)} bytes What it means
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.
Source
Thrown at library/x25519_pubkey.py:73
if module.params["private_key_path"]:
try:
with open(module.params["private_key_path"], "rb") as f:
data = f.read()
try:
# First attempt: assume file contains base64 text data
# Strip whitespace from edges for text files (safe for base64 strings)
stripped_data = data.strip()
base64.b64decode(stripped_data, validate=True)
priv_b64 = stripped_data.decode()
except (base64.binascii.Error, ValueError):
# Second attempt: assume file contains raw binary data
# CRITICAL: Do NOT strip raw binary data - X25519 keys can contain
# whitespace-like bytes (0x09, 0x0A, etc.) that must be preserved
# Stripping would corrupt the key and cause "got 31 bytes" errors
if len(data) != 32:
module.fail_json(
msg=f"Private key file must be either base64 or exactly 32 raw bytes, got {len(data)} bytes"
)
priv_b64 = base64.b64encode(data).decode()
except OSError as e:
module.fail_json(msg=f"Failed to read private key file: {e}")
else:
priv_b64 = module.params["private_key_b64"]
# Validate input parameters
if not priv_b64:
module.fail_json(msg="No private key provided")
try:
priv_raw = base64.b64decode(priv_b64, validate=True)
except Exception as e:
module.fail_json(msg=f"Invalid base64 private key format: {e}")
if len(priv_raw) != 32:
module.fail_json(msg=f"Private key must decode to exactly 32 bytes, got {len(priv_raw)}")View on GitHub (pinned to 20e22a8715)
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
Example fix
# before: file contains hex key (64 chars) # after: printf '%s' "$HEXKEY" | xxd -r -p > priv.key # 32 raw bytes
Defensive patterns
Strategy: validation
Validate before calling
data = open(path, 'rb').read()
assert len(data) == 32, f'expected 32 raw bytes or base64, got {len(data)}' Type guard
def is_valid_key_file(data: bytes) -> bool:
import base64
try:
return len(base64.b64decode(data, validate=True)) == 32
except Exception:
return len(data) == 32 Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Invalid base64 private key format: {e}
- Private key must decode to exactly 32 bytes, got {len(priv_r
- Failed to read private key file: {e}
- No private key provided
- Failed to write public key file: {e}
AI-assisted analysis of trailofbits/algo@20e22a8715 (2026-08-28).
Data as JSON: /api/errors/ee181b188140688e.
Report an issue: GitHub.