Summary
HeaderMap stores headers in a JS Map keyed by Label objects, but Label uses structural equality while Map.get/Map.set use reference equality. As a result:
- (a) the public
header() accessor calls this.headers.get(label) with a freshly built Label, so it always returns undefined;
- (b)
HeaderMap [Equal.symbol] looks up keys from this in that's map by reference, so two structurally-equal HeaderMaps never compare equal;
- (c)
setAlgorithmId followed by setHeader(labelFromInt(1n), ...) inserts two distinct Label objects for logical key 1, leaving two entries in the map.
No functional or security impact today: the security paths (verifyData, algorithmId, keyId) iterate the map manually with Equal.equals and are unaffected, header() is unused, and the duplicate label-1 entries collapse to a single correct entry on CBOR encode (encode re-keys by primitive value). Latent correctness / cleanup.
Affected
packages/evolution/src/cose/Header.ts
- header() (L260-262):
this.headers.get(label) uses reference equality -> always undefined
- [Equal.symbol] (L43-51):
that.headers.get(key) uses reference equality -> HeaderMaps never equal
packages/evolution/src/cose/Key.ts
- EdDSA25519Key.build() (L248-252):
setAlgorithmId(1n) then setHeader(labelFromInt(1n), ...) create two entries at logical key 1
Fix
Stop keying a JS Map by objects with custom equality. Either intern Label instances so structurally-equal labels share a reference, or replace the map lookups (header(), equality, setHeader overwrite) with Equal.equals-based iteration (as the working accessors already do). Making setHeader overwrite an existing logical key also removes the duplicate label-1 entries in build().
Regression test
- given: a HeaderMap built with
setHeader(labelFromText("address"), bytes)
- before fix:
hm.header(labelFromText("address")) returns undefined
- after fix: returns the bytes
Must FAIL on main today and PASS after the fix.
Reference
Reported informally (HeaderMap reference-equality). No live security impact; cleanup for a broken public accessor and a fragile build path.
Summary
HeaderMap stores headers in a JS
Mapkeyed byLabelobjects, butLabeluses structural equality whileMap.get/Map.setuse reference equality. As a result:header()accessor callsthis.headers.get(label)with a freshly builtLabel, so it always returnsundefined;HeaderMap[Equal.symbol]looks up keys fromthisinthat's map by reference, so two structurally-equal HeaderMaps never compare equal;setAlgorithmIdfollowed bysetHeader(labelFromInt(1n), ...)inserts two distinctLabelobjects for logical key 1, leaving two entries in the map.No functional or security impact today: the security paths (
verifyData,algorithmId,keyId) iterate the map manually withEqual.equalsand are unaffected,header()is unused, and the duplicate label-1 entries collapse to a single correct entry on CBOR encode (encode re-keys by primitive value). Latent correctness / cleanup.Affected
packages/evolution/src/cose/Header.ts
this.headers.get(label)uses reference equality -> always undefinedthat.headers.get(key)uses reference equality -> HeaderMaps never equalpackages/evolution/src/cose/Key.ts
setAlgorithmId(1n)thensetHeader(labelFromInt(1n), ...)create two entries at logical key 1Fix
Stop keying a JS
Mapby objects with custom equality. Either internLabelinstances so structurally-equal labels share a reference, or replace the map lookups (header(), equality,setHeaderoverwrite) withEqual.equals-based iteration (as the working accessors already do). MakingsetHeaderoverwrite an existing logical key also removes the duplicate label-1 entries inbuild().Regression test
setHeader(labelFromText("address"), bytes)hm.header(labelFromText("address"))returns undefinedMust FAIL on main today and PASS after the fix.
Reference
Reported informally (HeaderMap reference-equality). No live security impact; cleanup for a broken public accessor and a fragile build path.