Files
Elydora-Open-Source/packages/server/src/utils/tsa.ts
T
雷电芽衣 bc6983a62e chore(oss): audit the server package and resync the SDK mirrors
Split the 657-line operation service into eor, submit, and verify
modules, add the AGENT_REVOKED and KEY_RETIRED codes the Backend already
emits, and condense every multi-line comment. Mirror Node, Python, and
Go at 2.1.0 with the deep-health 503, non-idempotent retry, rotation
grace, and path-encoding fixes. Document which routes and response
fields the self-hosted server does not provide.

Claude-Session: https://claude.ai/code/session_01Agoc9bRVE9KL7mmWtey9Vq
2026-09-03 20:48:20 -04:00

62 lines
2.4 KiB
TypeScript

/** RFC 3161 Timestamping Authority (TSA) client utilities. */
/** Default public TSA endpoint (Sectigo / Comodo). */
export const DEFAULT_TSA_URL = 'http://timestamp.sectigo.com';
/** Build a minimal RFC 3161 TimeStampReq (DER-encoded, 59 bytes). */
export function buildTimeStampReq(sha256Hash: Uint8Array): Uint8Array {
if (sha256Hash.length !== 32) {
throw new Error(`Expected 32-byte SHA-256 hash, got ${sha256Hash.length}`);
}
// DER prefix (24 bytes): everything before the 32-byte hash
const prefix = new Uint8Array([
0x30, 0x39, // SEQUENCE (57 bytes)
0x02, 0x01, 0x01, // INTEGER 1 (version v1)
0x30, 0x31, // SEQUENCE (49 bytes): MessageImprint
0x30, 0x0d, // SEQUENCE (13 bytes): AlgorithmIdentifier
0x06, 0x09, // OID (9 bytes)
0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, // SHA-256 (2.16.840.1.101.3.4.2.1)
0x05, 0x00, // NULL (parameters)
0x04, 0x20, // OCTET STRING (32 bytes)
]);
// DER suffix (3 bytes): certReq = TRUE
const suffix = new Uint8Array([0x01, 0x01, 0xff]);
// Assemble: prefix + hash + suffix = 24 + 32 + 3 = 59 bytes
const req = new Uint8Array(prefix.length + sha256Hash.length + suffix.length);
req.set(prefix);
req.set(sha256Hash, prefix.length);
req.set(suffix, prefix.length + sha256Hash.length);
return req;
}
/** Submit a SHA-256 hash to a TSA and return the raw DER TimeStampResp. */
export async function requestTimestamp(
sha256Hash: Uint8Array,
tsaUrl: string = DEFAULT_TSA_URL,
): Promise<Uint8Array> {
const body = buildTimeStampReq(sha256Hash);
const response = await fetch(tsaUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/timestamp-query',
},
body: body as unknown as BodyInit,
});
if (!response.ok) {
throw new Error(`TSA request failed: ${response.status} ${response.statusText}`);
}
const contentType = response.headers.get('Content-Type') ?? '';
if (!contentType.includes('application/timestamp-reply')) {
throw new Error(`Unexpected TSA response content-type: ${contentType}`);
}
return new Uint8Array(await response.arrayBuffer());
}