4.3 KiB
inline-hint
(no submodules)
Purpose
Implements DTP-25 (drive-plugin-chunks-inline-sig). Provides the signed InlineHint shape used by FileLocation.storageHints entries with kind: 'inline', the Ed25519 signing helper that produces them, the verifier that the drive runs before returning inline bytes, and the legacy-detection / upgrade helpers that let pre-DTP-25 hints (no sig field) round-trip safely. The signing input is the canonical concatenation fileId ‖ bytes, which binds the signature to one file so a replay onto a different fileId would fail verification.
Features
InlineHint—{ kind: 'inline'; bytes: Uint8Array; sig: Uint8Array }for the DTP-25 wire format.LegacyInlineHint—{ kind: 'inline'; bytes: Uint8Array }for backward-compat reads.signInlineHint— Ed25519-sign overfileId ‖ bytesunder the drive's secret key.verifyInlineHint— verify and return bytes; throwsCHUNKS.INLINE_SIG_INVALIDon failure (catch arm covers verifier exceptions too).isLegacyInlineHint— type-guard distinguishing legacy from signed shapes.upgradeLegacyInlineHint— sign a legacy hint with the drive's key so the next write rewrites it asInlineHint.inlineSigningInput— exposes the canonical concatenation so other layers can re-derive the bytes that are signed.- Cause-chain preservation: verifier exceptions are wrapped as
ChunksErrorwith the original error attached ascause.
Usage: Basic Example
import {
signInlineHint,
verifyInlineHint,
} from "@suaveplan/drive-plugin-chunks";
import { sha256 } from "@suaveplan/crypto-hash";
const fileId = sha256(plaintext);
const hint = signInlineHint(fileId, plaintext, driveSecretKey);
// later, on read:
const bytes = verifyInlineHint(hint, fileId, drivePublicKey);
console.log(bytes.byteLength);
Usage: Advanced Example
import {
isLegacyInlineHint,
upgradeLegacyInlineHint,
verifyInlineHint,
type AnyInlineHint,
} from "@suaveplan/drive-plugin-chunks";
function readInline(
hint: AnyInlineHint,
fileId: Uint8Array,
driveSecretKey: Uint8Array,
drivePublicKey: Uint8Array,
onLegacyWarning: () => void
): Uint8Array {
if (isLegacyInlineHint(hint)) {
onLegacyWarning(); // emit drive:inline:legacy
const upgraded = upgradeLegacyInlineHint(
hint,
fileId,
driveSecretKey
);
// Persist `upgraded` on the next write; for the current read we
// can return the bytes directly because legacy hints predate the
// sig requirement.
return upgraded.bytes;
}
return verifyInlineHint(hint, fileId, drivePublicKey);
}
API Reference
signInlineHint(fileId: Uint8Array, bytes: Uint8Array, driveSecretKey: Uint8Array): InlineHint— Ed25519-signfileId ‖ bytesand return the signed hint.verifyInlineHint(hint: InlineHint, fileId: Uint8Array, drivePublicKey: Uint8Array): Uint8Array— verify and returnhint.bytes. ThrowsChunksError(CHUNKS.INLINE_SIG_INVALID, ...)if the signature does not verify or the verifier raises.isLegacyInlineHint(hint: AnyInlineHint): hint is LegacyInlineHint— true when the hint omitssig.upgradeLegacyInlineHint(legacy: LegacyInlineHint, fileId: Uint8Array, driveSecretKey: Uint8Array): InlineHint— sign the legacy bytes under the drive's key.inlineSigningInput(fileId: Uint8Array, bytes: Uint8Array): Uint8Array— canonical signing input.InlineHint,LegacyInlineHint,AnyInlineHint,StorageHint,FileLocation— wire types.
Implementation Notes
The verifier uses @noble/curves Ed25519 via @suaveplan/crypto-curve/ed25519. verify() is implemented to never throw on malformed inputs in the noble path, but the catch arm exists for engine-substitution scenarios where a custom verifier may throw — the injectable verifyFn pattern is intentionally not surfaced here because the chunks plugin always uses noble. The CHUNKS.INLINE_SIG_INVALID code is registered through defineCodes("CHUNKS", ...) in error-codes.ts. Domain separation is provided by including fileId in the signing input; this is sufficient because every inline-hint context already has a unique fileId, so the signature cannot be replayed across files even if an attacker controls one inline body.