SCT.js is a low-level TypeScript library for Node.js that parses and verifies Signed Certificate Timestamps (SCTs). It provides the core cryptographic tools needed to check SCTs from any source, including those embedded in X.509 certificates or delivered via TLS extensions and OCSP Stapling.
The library is stable and ready for production use, with support for both X.509 and Pre-certificate SCT entries. The test suite for X.509 entries is ported from the original Rust library and has been augmented with a comprehensive, generated test suite for Pre-certificate entries. This new suite covers all success and error cases for multiple signature algorithms, including ECDSA P-256/P-384 and RSA 2048/3072/4096.
Signed Certificate Timestamps (SCTs) are the core of Certificate Transparency, but they can be delivered to a client in several ways. This library is responsible for the verification of an SCT, regardless of how it was acquired. It is the responsibility of the client application to obtain the certificate and its associated SCTs.
There are three primary methods for delivering SCTs:
- Embedded in the Certificate: The SCT is included directly in the final X.509 certificate as an extension (
1.3.6.1.4.1.11129.2.4.2
). This is the most common method. - Via a TLS Extension: The SCT is delivered during the TLS handshake via the
signed_certificate_timestamp
extension. This is useful for certificates issued before the log was trusted, or when the CA is not participating in CT. - Via OCSP Stapling: The SCT is included in a stapled OCSP response, which is sent by the server during the TLS handshake.
This library can verify SCTs from any of these sources, provided you can supply the SCT data and the certificate or pre-certificate data it corresponds to.
npm install @gldywn/sct.js
The library supports two primary verification scenarios: for SCTs embedded in a pre-certificate (most common, found in the final certificate) and for SCTs delivered via TLS extension or OCSP, which are verified against the final X.509 certificate.
This is the most common use case, typically used to verify an SCT that was embedded directly into a certificate during its issuance. To do this, you need both the final certificate (leafCert
) and the certificate of its issuer (issuerCert
). The reconstructPrecert
function is used to create the data structure that the SCT was originally signed against.
For a complete, runnable example, see examples/verify-precert.ts.
import { verifySct, reconstructPrecert, ENTRY_TYPE, Log } from '@gldywn/sct.js';
try {
// The leaf certificate (end-entity) and the issuer certificate both DER-encoded X.509
const leafCert = readTestData('leaf_google-cert.bin', 'precert');
const issuerCert = readTestData('issuer_google-cert.bin', 'precert');
// The SCT embedded within the leaf certificate (extracted from the certificate itself)
const precertSct = readTestData('google-sct0.bin', 'precert');
// Helper function to reconstruct the precertificate data structure to verify the SCT against
const precert = reconstructPrecert(leafCert, issuerCert);
// A list of trusted logs
const trustedLogs: Log[] = [
{
description: 'Fetched Google Test Log',
key: getTrustedLogKey(),
id: createHash('sha256')
.update(getTrustedLogKey().export({ type: 'spki', format: 'der' }))
.digest(),
url: 'test.com',
operated_by: 'Test',
max_merge_delay: 0,
status: 'usable',
},
];
const { log, sct } = verifySct(precertSct, precert, ENTRY_TYPE.PRECERT_ENTRY, Date.now(), trustedLogs);
console.log(`Pre-cert SCT verification successful!`);
console.log(` Log ID: ${log.id.toString('hex')}`);
console.log(` Log: ${log.description}`);
console.log(` Timestamp: ${new Date(Number(sct.timestamp)).toISOString()}`);
} catch (error) {
console.error('Error while verifying pre-certificate SCT:', error);
}
This method is used when the SCT is delivered separately from the certificate, such as in a signed_certificate_timestamp
TLS extension or a stapled OCSP response. In this case, the SCT is verified against the final, DER-encoded X.509 certificate directly, and no reconstruction is needed.
For a complete, runnable example, see examples/verify-x509.ts.
import { verifySct, ENTRY_TYPE, Log } from '@gldywn/sct.js';
try {
// The leaf certificate (end-entity), DER-encoded X.509
const certificate = readTestData('google-cert.bin', 'x509');
// The SCT, obtained either from the "signed_certificate_timestamp" TLS extension or OCSP response
const sct = readTestData('google-sct0.bin', 'x509');
// A list of trusted logs
const trustedLogs: Log[] = [
{
description: "Google 'Pilot' log",
key: jwkFromRawEcdsa(readTestData('google-pilot-pubkey.bin', 'x509')),
id: Buffer.from('a4b90990b418581487bb13a2cc67700a3c359804f91bdfb8e377cd0ec80ddc10', 'hex'),
url: 'ct.googleapis.com/pilot/',
operated_by: 'Google',
max_merge_delay: 86400,
status: 'usable',
},
];
const { log, sct: parsedSct } = verifySct(sct, certificate, ENTRY_TYPE.X509_ENTRY, Date.now(), trustedLogs);
console.log(`X.509 SCT verification successful!`);
console.log(` Log ID: ${log.id.toString('hex')}`);
console.log(` Log: ${log.description}`);
console.log(` Timestamp: ${new Date(Number(parsedSct.timestamp)).toISOString()}`);
} catch (error) {
console.error('Error while verifying X.509 SCT:', error);
}
This project includes a comprehensive test suite to ensure correctness and stability.
The repository includes pre-generated test data. To update the fixtures for pre-certificate tests, run:
npm run test:update-test-data
This command performs the following steps:
- Generates test keys: Creates new key pairs for all supported algorithms.
- Generates pre-cert data: Builds a fresh suite of mock SCTs and test cases.
- Fetches real-world data: Downloads the latest test certificates from Google.
To run the complete test suite:
npm test
SCT.js is distributed under the MIT license.
This library is currently intended for Node.js environments only.
It relies on Node.js built-in modules like crypto
and Buffer
that are not available in web browsers. While it is theoretically possible to use this library in a browser by using polyfills (such as crypto-browserify
and buffer
), this is not officially supported or tested at this time. Future versions may include a browser-compatible bundle.
This library is a full-featured TypeScript port of the excellent rustls/sct.rs library. It is designed to be a 1:1 functional equivalent and includes a faithful port of the original's comprehensive test suite to ensure correctness.