Skip to content

DKG register key-length mismatch: kernel emits 32-byte ed25519 dkgPubKey + 64-byte commKey, but DKG.sol requires 64 / 65 #73

Description

@yingyangxu2026

Summary

The dkgPubKey and enclaveCommKey lengths the kernel submits during DKG registration do not match the requirements of DKG.sol::register on the chain side. As a result, any chain running the current dkg/dev contracts cannot complete DKG registration — every validator's register reverts during gas estimation and DKG never bootstraps.

Background: how we ran into this

We were validating the DKG resharing fixes (chain side piplabs/story #838 off-by-one + participants-root, with the companion kernel-side PR #72 "wait for finalization stage") on a 4-node devnet, with the chain on branch test/v190-regress3 and the kernel on dd7eca07 (= #67 + #72).

It peeled apart in layers:

  1. Symptom: on every setup, DKG never bootstrapped — every validator's register reverted during gas estimation and the chain just churned (the DKG round counter kept incrementing but no active round ever appeared).

  2. First layer (code commitment): the initial revert was SGXAttestationReportValidator: Code commitment does not match. That turned out to be a devnet setup-script bug: the GenerateAlloc forge script reverted due to an owner/prank mismatch, but its non-zero exit code was swallowed by a … | tail pipeline, so the script silently reused a several-days-old local-alloc.json and wrote a previous kernel build's code commitment into genesis. Fixed in piplabs/story-cdr-e2e PR #239 (fail-fast on forge failure + force regeneration of the alloc).

  3. Second layer (this issue): with the setup fixed, the setup deployed the current dkg/dev contracts for the first time. The code-commitment error disappeared and was immediately replaced by DKG: DKG public key must be 64 bytes — the kernel↔contract key-length mismatch described here.

  4. Why it went unnoticed: every previously "successful" setup had actually been running pre-#831 contracts (no 64/65 length checks) because of the same GenerateAlloc failure, so the 32-byte ed25519 public key slipped through the whole time. Only after fixing the setup and deploying the up-to-date contracts did this long-masked incompatibility surface.

The mismatch

key kernel submits contract requires result
dkgPubKey 32 bytes (edwards25519 compressed point) == 64 ❌ wrong curve / length
enclaveCommKey 64 bytes (secp256k1 uncompressed, 0x04 prefix stripped) == 65 ❌ missing the prefix byte

Evidence

Contract side (piplabs/story, introduced by #831 9567630), contracts/src/protocol/DKG.sol:

require(enclaveInstanceData.dkgPubKey.length == 64, "DKG: DKG public key must be 64 bytes");
require(enclaveInstanceData.enclaveCommKey.length == 65, "DKG: Enclave communication key must be 65 bytes");

Kernel side, service/dkg_generate_key.go:

edPubBz, err := edPub.MarshalBinary()              // edwards25519 -> 32 bytes
...
reportData, err := calculateReportData(...,
    edPubBz,                           // dkgPubKey      -> 32 bytes
    ecrypto.FromECDSAPub(secpPub)[1:], // enclaveCommKey -> secp256k1 uncompressed (65) minus prefix = 64 bytes
)
...
DkgPubKey:  edPubBz,                           // 32 bytes
CommPubKey: ecrypto.FromECDSAPub(secpPub)[1:], // 64 bytes

Observed devnet log (register, round 3):

err="...execution reverted: DKG: DKG public key must be 64 bytes"
dkg_pub_key=f3cfb902…a109d23   (64 hex = 32 bytes)
comm_pub_key=20516989…b199ea   (128 hex = 64 bytes)

Status of PR #68

PR #68 (feat/impl-tdx-backend) adds length assertions to calculateReportData in service/utils.godkgPubKeySize = 64 / enclaveCommKeySize = 65 (the comment notes these "match the require()s in DKG.sol::register") — but it does not change the actual key generation in dkg_generate_key.go (still edPubBz / secpPub[1:]). So with PR #68 the failure simply moves earlier, to a kernel-side dkgPubKey must be 64 bytes, got 32; the underlying mismatch is unchanged.

Deeper design conflict

The kernel's DKG itself runs on edwards25519 (service/dist_key_gen.go uses SuiteEd25519), so dkgPubKey is inherently 32 bytes, while the contract requires 64. This needs a protocol-level decision:

  • either the contract reverts dkgPubKey back to 32 bytes (keep ed25519), or
  • the DKG public key migrates to a 64-byte representation (e.g. secp256k1 X‖Y).
  • enclaveCommKey is simpler: the kernel just needs to stop stripping the 0x04 prefix ([1:]) to produce the 65 bytes the contract expects.

Impact

  • On any chain running the current dkg/dev contracts, DKG registration always fails and the chain cannot bootstrap.

Ask

Decide the target curve/length for dkgPubKey, and make the kernel's key generation consistent with the 64 / 65-byte requirements of DKG.sol::register.

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions