slackhq/nebula · error

cannot parse private key as P256: %w

Error message

cannot parse private key as P256: %w

What it means

VerifyPrivateKey's P256 branch parses the raw private key bytes with crypto/ecdh's P256 NewPrivateKey before deriving the public key. If the bytes are not a valid scalar in the P256 field (wrong length, leading issues, not a real key), the parse fails and this error wraps the underlying reason.

Source

Thrown at cert/cert_v1.go:153

func (c *certificateV1) VerifyPrivateKey(curve Curve, key []byte) error {
	if curve != c.details.curve {
		return fmt.Errorf("curve in cert and private key supplied don't match")
	}
	if c.details.isCA {
		switch curve {
		case Curve_CURVE25519:
			// the call to PublicKey below will panic slice bounds out of range otherwise
			if len(key) != ed25519.PrivateKeySize {
				return fmt.Errorf("key was not 64 bytes, is invalid ed25519 private key")
			}

			if !ed25519.PublicKey(c.details.publicKey).Equal(ed25519.PrivateKey(key).Public()) {
				return fmt.Errorf("public key in cert and private key supplied don't match")
			}
		case Curve_P256:
			privkey, err := ecdh.P256().NewPrivateKey(key)
			if err != nil {
				return fmt.Errorf("cannot parse private key as P256: %w", err)
			}
			pub := privkey.PublicKey().Bytes()
			if !bytes.Equal(pub, c.details.publicKey) {
				return fmt.Errorf("public key in cert and private key supplied don't match")
			}
		default:
			return fmt.Errorf("invalid curve: %s", curve)
		}
		return nil
	}

	var pub []byte
	switch curve {
	case Curve_CURVE25519:
		var err error
		pub, err = curve25519.X25519(key, curve25519.Basepoint)
		if err != nil {
			return err

View on GitHub (pinned to dd8f660c0a)

Solutions

  1. Confirm the certificate's curve matches the key's curve and pass the correct curve constant
  2. Strip PEM armor and decode base64 so only the raw key bytes are passed
  3. Re-issue the keypair/certificate for the intended curve if the key was generated for a different curve
  4. Inspect the wrapped error (%w) to see the exact ecdh parse failure

Example fix

// before
cert.VerifyPrivateKey(key, cert.Curve_P256, ed25519KeyBytes) // cannot parse as P256
// after
p256Key := generateP256KeyRaw() // raw 32-byte P256 scalar
cert.VerifyPrivateKey(key, cert.Curve_P256, p256Key)
Defensive patterns

Strategy: validation

Validate before calling

func isRawP256Key(b []byte) bool {
    _, err := ecdh.P256().NewPrivateKey(b)
    return err == nil
}
if !isRawP256Key(keyBytes) {
    return fmt.Errorf("key bytes are not a valid P256 private key")
}
err := c.VerifyPrivateKey(key, cert.Curve_P256, keyBytes)

Type guard

func isP256Sized(b []byte) bool { return len(b) == 32 }

Try / catch

if err := c.VerifyPrivateKey(key, cert.Curve_P256, keyBytes); err != nil {
    if strings.Contains(err.Error(), "cannot parse private key as P256") {
        return fmt.Errorf("supplied key is not a valid P256 scalar: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Calling (*certificateV1).VerifyPrivateKey with curve=Curve_P256 and key bytes that ecdh.P256().NewPrivateKey rejects (wrong size, all-zero, out of range, or actually an Ed25519 key).

Common situations: Passing an Ed25519 64-byte private key to a P256 certificate; key file contains PEM text not stripped to raw bytes; truncated or corrupted key file; NIST-curve vs Ed25519 confusion in config.

Related errors


AI-assisted analysis of slackhq/nebula@dd8f660c0a (2026-09-03). Data as JSON: /api/errors/68788e993c14964f. Report an issue: GitHub.