trailofbits/algo · error

Private key must decode to exactly 32 bytes, got {len(priv_r

Error message

Private key must decode to exactly 32 bytes, got {len(priv_raw)}

What it means

The base64 decoded successfully but did not produce exactly 32 bytes — X25519 private keys must be 32 bytes, so any other length (commonly 33 or 31 from a corrupted key) is rejected before key construction.

Source

Thrown at library/x25519_pubkey.py:91

                        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()
            except OSError:
                existing = None

View on GitHub (pinned to 20e22a8715)

Solutions

  1. Verify decode length in Python: len(base64.b64decode(key)) must equal 32
  2. Regenerate the key with wg genkey or cryptography's X25519PrivateKey.generate()
  3. Check you didn't concatenate public+private key material
  4. Ensure upstream generation writes exactly 32 raw bytes

Example fix

# python check
import base64
raw = base64.b64decode(open('priv.b64').read().strip(), validate=True)
assert len(raw) == 32, len(raw)
Defensive patterns

Strategy: validation

Validate before calling

import base64
raw = base64.b64decode(priv_b64, validate=True)
assert len(raw) == 32, f'X25519 key must be 32 bytes, got {len(raw)}'

Type guard

def is_32byte_key(b64: str) -> bool:
    import base64
    try:
        return len(base64.b64decode(b64, validate=True)) == 32
    except Exception:
        return False

Prevention

When it happens

Trigger: A valid-base64 string encoding 33/31/64 bytes: e.g. a key with one extra character encoded, an Ed25519 or hex key mislabeled as X25519, or double-encoded data.

Common situations: Mixing up key types between tools, encoding a 64-char hex string as base64, or hand-editing a key.

Related errors


AI-assisted analysis of trailofbits/algo@20e22a8715 (2026-08-28). Data as JSON: /api/errors/2b4ed3cf7dd0517c. Report an issue: GitHub.