diem/diem · error

Invalid public key bytes

Error message

Invalid public key bytes

What it means

The Move interpreter's crypto intrinsics reject public keys that fail validation before constructing an Ed25519PublicKey. validate_public_key checks the 32-byte length and that the Edwards point is not small-order (malleability protection for ed25519's cofactor of 8), so this error means the bytes are not a valid, non-malleable ed25519 public key.

Source

Thrown at language/move-prover/interpreter/crypto/src/lib.rs:63

    }

    let mut bits = [0u8; ED25519_PUBLIC_KEY_LENGTH];
    bits.copy_from_slice(&bytes[..ED25519_PUBLIC_KEY_LENGTH]);

    let compressed = curve25519_dalek::edwards::CompressedEdwardsY(bits);
    let point = match compressed.decompress() {
        None => return false,
        Some(point) => point,
    };

    // Check if the point lies on a small subgroup. This is required
    // when using curves with a small cofactor (in ed25519, cofactor = 8).
    !point.is_small_order()
}

pub fn ed25519_deserialize_public_key(bytes: &[u8]) -> Result<Ed25519PublicKey> {
    if !validate_public_key(bytes) {
        bail!("Invalid public key bytes");
    }
    Ok(Ed25519PublicKey::from_bytes(bytes)?)
}

fn validate_signature(bytes: &[u8]) -> bool {
    if bytes.len() != ED25519_SIGNATURE_LENGTH {
        return false;
    }
    for i in (0..32).rev() {
        match bytes[32 + i].cmp(&L[i]) {
            Ordering::Less => return true,
            Ordering::Greater => return false,
            _ => (),
        }
    }
    // As this stage S == L which implies a non canonical S.
    false
}

View on GitHub (pinned to fc4714a8ea)

Solutions

  1. Validate key bytes are exactly 32 bytes before calling the native
  2. Reject degenerate/small-order keys (e.g. all zeros) at the application boundary
  3. Check serialization/deserialization code for truncation or offset bugs
  4. In Move code, assert `length(key) == 32` before the native call

Example fix

// Move: before
let pk = ed25519_deserialize_public_key(key_bytes);
// after
assert!(length(key_bytes) == 32, E_INVALID_KEY);
let pk = ed25519_deserialize_public_key(key_bytes);
Defensive patterns

Strategy: validation

Validate before calling

// Move
assert!(length(key_bytes) == 32, E_INVALID_KEY);

Type guard

fn is_valid_key_len(bytes: &[u8]) -> bool {
    bytes.len() == 32 && bytes.iter().any(|&b| b != 0)
}

Prevention

When it happens

Trigger: Calling native `ed25519_deserialize_public_key` (via native_signature_ed25519_validate_pubkey or native_signature_ed25519_signature_verification) with bytes of the wrong length or encoding a small-order/degenerate curve point.

Common situations: Truncated or padded keys from bad serialization, all-zero or degenerate keys passed by malicious or buggy Move code, or keys from a non-standard ed25519 implementation.

Related errors


AI-assisted analysis of diem/diem@fc4714a8ea (2026-09-04). Data as JSON: /api/errors/91d15e2d004d171f. Report an issue: GitHub.