slackhq/nebula · error
key was not 32 bytes, is invalid ECDSA P256 private key
Error message
key was not 32 bytes, is invalid ECDSA P256 private key
What it means
The PEM block was recognized as an ECDSA P256 signing private key (ECDSAP256PrivateKeyBanner), but the decoded bytes are not exactly 32 bytes (the P-256 scalar size). The key body is truncated, padded, or malformed even though the banner is correct, so the library rejects it.
Source
Thrown at cert/pem.go:271
k, r := pem.Decode(b)
if k == nil {
return nil, r, 0, fmt.Errorf("input did not contain a valid PEM encoded block")
}
var curve Curve
switch k.Type {
case EncryptedEd25519PrivateKeyBanner:
return nil, nil, Curve_CURVE25519, ErrPrivateKeyEncrypted
case EncryptedECDSAP256PrivateKeyBanner:
return nil, nil, Curve_P256, ErrPrivateKeyEncrypted
case Ed25519PrivateKeyBanner:
curve = Curve_CURVE25519
if len(k.Bytes) != ed25519.PrivateKeySize {
return nil, r, 0, fmt.Errorf("key was not %d bytes, is invalid Ed25519 private key", ed25519.PrivateKeySize)
}
case ECDSAP256PrivateKeyBanner:
curve = Curve_P256
if len(k.Bytes) != 32 {
return nil, r, 0, fmt.Errorf("key was not 32 bytes, is invalid ECDSA P256 private key")
}
default:
return nil, r, 0, fmt.Errorf("bytes did not contain a proper Ed25519/ECDSA private key banner")
}
return k.Bytes, r, curve, nil
}
View on GitHub (pinned to dd8f660c0a)
Solutions
- Emit exactly the 32-byte raw scalar under the ECDSA P256 banner (use nebula-cert to generate)
- Strip DER/ASN.1 wrapping: extract the raw 32-byte private scalar before PEM-encoding
- Verify len(decoded bytes) == 32 before calling the API
Example fix
// before: DER-encoded key pasted raw block.Bytes = derBytes // e.g. 121 bytes // after block.Bytes = rawScalar // exactly 32 bytes (d parameter)
Defensive patterns
Strategy: validation
Validate before calling
blk, _ := pem.Decode(data)
if blk != nil && blk.Type == "NEBULA ECDSA P256 SIGNING PRIVATE KEY" && len(blk.Bytes) != 32 {
return fmt.Errorf("P256 signing key must be a raw 32-byte scalar, got %d bytes (DER-wrapped?)", len(blk.Bytes))
} Type guard
func isValidP256SigningKey(b []byte) bool {
blk, _ := pem.Decode(b)
return blk != nil && blk.Type == "NEBULA ECDSA P256 SIGNING PRIVATE KEY" && len(blk.Bytes) == 32
} Try / catch
key, _, _, err := nebula.UnmarshalSigningPrivateKeyFromPEM(raw)
if err != nil {
return fmt.Errorf("P256 signing key body must be exactly 32 bytes: %w", err)
} Prevention
- Do not paste openssl/PKCS#8 DER key material under a nebula banner
- Extract the raw scalar (d) before PEM-encoding P256 keys
- Prefer nebula-cert generated keys over converted external keys
When it happens
Trigger: Call UnmarshalSigningPrivateKeyFromPEM with a block of type ECDSAP256PrivateKeyBanner whose k.Bytes length != 32 — e.g. DER-wrapped key material, a truncated scalar, or extra bytes appended.
Common situations: Exporting a P256 key from another tool in SEC1/PKCS#8 DER form (longer than 32 bytes) and pasting it under the nebula banner, or copy/paste truncation of the base64 body.
Related errors
- key was not 32 bytes, is invalid ECDSA P256 private key
- key was not %d bytes, is invalid Ed25519 private key
- key was not %d bytes, is invalid %s public key
- key was not %d bytes, is invalid %s private key
- bytes did not contain a proper Ed25519/ECDSA private key ban
AI-assisted analysis of slackhq/nebula@dd8f660c0a (2026-09-03).
Data as JSON: /api/errors/03c20ef6c2acbd35.
Report an issue: GitHub.