trailofbits/algo · error
Invalid base64 private key format: {e}
Error message
Invalid base64 private key format: {e} What it means
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.
Source
Thrown at library/x25519_pubkey.py:88
# 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)}")
try:
priv_key = x25519.X25519PrivateKey.from_private_bytes(priv_raw)
pub_key = priv_key.public_key()
pub_raw = pub_key.public_bytes(encoding=serialization.Encoding.Raw, format=serialization.PublicFormat.Raw)
pub_b64 = base64.b64encode(pub_raw).decode()
result["public_key"] = pub_b64
if module.params["public_key_path"]:
pub_path = module.params["public_key_path"]
existing = None
try:
with open(pub_path) as f:
existing = f.read().strip()View on GitHub (pinned to 20e22a8715)
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
Example fix
# before
private_key_b64: "{{ lookup('file', path) }}"
# after
private_key_b64: "{{ lookup('file', path) | trim }}" Defensive patterns
Strategy: validation
Validate before calling
import base64
key = key_str.strip().replace('-', '+').replace('_', '/')
key += '=' * (-len(key) % 4)
base64.b64decode(key, validate=True) # raises if still invalid Type guard
def is_base64_key(s: str) -> bool:
import base64
try:
return len(base64.b64decode(s.strip(), validate=True)) == 32
except Exception:
return False Prevention
- Trim whitespace when loading keys from files
- Avoid base64url encodings for WireGuard keys
When it happens
Trigger: Key string with whitespace/newlines, URL-safe base64 (-/_ instead of +/), hex text, or a truncated/copy-paste-corrupted key passed via private_key_b64.
Common situations: 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.
Related errors
- Private key file must be either base64 or exactly 32 raw byt
- Failed to read private key file: {e}
- No private key provided
- Private key must decode to exactly 32 bytes, got {len(priv_r
- Failed to write public key file: {e}
AI-assisted analysis of trailofbits/algo@20e22a8715 (2026-08-28).
Data as JSON: /api/errors/f5c9b588f0445ad0.
Report an issue: GitHub.