InvalidCanisterSignature conflates an untrusted root key with a malformed signature
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 144
- Forks
- 84
- Avg merge
- 20h 4m
- Merged PRs (30d)
- 1
Description
DelegationError::InvalidCanisterSignature reports 16 distinct conditions, only one of which is
the certificate's BLS trust check. That makes it impossible for a caller to tell "I do not have the
root key this signature is anchored to" from "this signature is malformed", and the two need
opposite handling.
Where this bites
A delegation chain issued by a non-mainnet Internet Identity (a local replica, a testnet) starts
with a canister signature whose certificate only BLS-verifies against that network's root key. A
client that resolves no network — deriving a principal, signing a delegation offline — has no root
key to check it against, and the provider's root key is not derivable from the chain.
Such a client wants to accept the chain while still rejecting a corrupt one. Today it cannot:
match DelegatedIdentity::new(from_key, session, chain) {
Err(DelegationError::InvalidCanisterSignature(_)) => {
// Untrusted root key, or malformed CBOR, or a certified-data mismatch,
// or a canister outside the delegated ranges, or a missing sig leaf.
// No way to tell, short of matching on the message string.
}
...
}
In 0.49.1, verify_cert_bls is the only trust-root site; the other 15 are in extract_bls_key,
resolve_cert_key and verify_canister_sig and all indicate corruption.
Why a client cannot work around it cleanly
Of the verification procedure in the interface spec, only step 3 needs a root key. Steps 1, 2, 4
and 5 — the signature CBOR decodes, the certificate decodes, certified_data matches
reconstruct(sig.tree), and the tree carries a leaf for this payload — do not. But
verify_canister_sig is pub(crate) and takes the root key, so there is no way to run the
root-key-independent part alone. The only options are matching on the error string, or
reimplementing those steps against ic-certification and serde_cbor.
Possible shapes
- A separate variant for the trust decision, e.g.
DelegationError::UntrustedRootKey, leaving
InvalidCanisterSignaturefor structural failures. - A constructor taking
Option<&[u8]>for the root key, verifying everything and skipping only
BLS when it isNone. - Making the root-key-independent verification public in some form.
Any of the three would remove the need for a client to duplicate certificate handling.
Context
Worked around in dfinity/icp-cli#767 by reimplementing steps 1, 2, 4 and 5 and classifying links by
signing-key type (SPKI OID 1.3.6.1.4.1.56387.1.2) rather than by error variant. Happy to send a PR
here for whichever shape you prefer.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Read the canister-signature verification path, especially verify_cert_bls, extract_bls_key, resolve_cert_key, and verify_canister_sig; the issue identifies these as the trust-root and structural-failure sites. Compare the three proposed API shapes and the interface-spec verification steps, then determine how to preserve rejection of malformed certificates while distinguishing an unavailable or untrusted root key. Done means callers can distinguish the trust decision from structural corruption without duplicating certificate handling.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- backend-api-design
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100