+9
-3
src/pds.js
+9
-3
src/pds.js
···
118
118
encodeHead(parts, 3, bytes.length)
119
119
parts.push(...bytes)
120
120
} else if (val instanceof CID) {
121
-
// CID - encode with CBOR tag 42 + 0x00 prefix
121
+
// CID links in DAG-CBOR use tag 42 + 0x00 multibase prefix
122
+
// The 0x00 prefix indicates "identity" multibase (raw bytes)
122
123
parts.push(0xd8, CBOR_TAG_CID)
123
124
encodeHead(parts, 2, val.bytes.length + 1) // +1 for 0x00 prefix
124
-
parts.push(0x00) // multibase identity prefix
125
+
parts.push(0x00)
125
126
parts.push(...val.bytes)
126
127
} else if (val instanceof Uint8Array) {
127
128
// Regular byte string
···
132
133
for (const item of val) encode(item)
133
134
} else if (typeof val === 'object') {
134
135
// DAG-CBOR: sort keys by length first, then lexicographically
136
+
// (differs from standard CBOR which sorts lexicographically only)
135
137
const keys = Object.keys(val).filter(k => val[k] !== undefined)
136
138
keys.sort((a, b) => {
137
139
if (a.length !== b.length) return a.length - b.length
···
378
380
)
379
381
const sig = new Uint8Array(signature)
380
382
381
-
// Low-S normalization: if S > N/2, replace S with N - S
382
383
const r = sig.slice(0, 32)
383
384
const s = sig.slice(32, 64)
384
385
const sBigInt = bytesToBigInt(s)
385
386
387
+
// Low-S normalization: Bitcoin/ATProto require S <= N/2 to prevent
388
+
// signature malleability (two valid signatures for same message)
386
389
if (sBigInt > P256_N_DIV_2) {
387
390
const newS = P256_N - sBigInt
388
391
const newSBytes = bigIntToBytes(newS, 32)
···
499
502
}
500
503
}
501
504
505
+
// MST depth = leading zeros in SHA-256 hash / 2
506
+
// This creates a probabilistic tree where ~50% of keys are at depth 0,
507
+
// ~25% at depth 1, etc., giving O(log n) lookups
502
508
const depth = Math.floor(zeros / 2)
503
509
keyDepthCache.set(key, depth)
504
510
return depth