import * as asn1js from "asn1js"; import * as pvutils from "pvutils"; import * as bs from "bytestreamjs"; import * as common from "./common"; import { PublicKeyInfo } from "./PublicKeyInfo"; import * as Schema from "./Schema"; import { AlgorithmIdentifier } from "./AlgorithmIdentifier"; import { Certificate } from "./Certificate"; import { AsnError } from "./errors"; import { PkiObject, PkiObjectParameters } from "./PkiObject"; import { EMPTY_BUFFER, EMPTY_STRING } from "./constants"; import { SignedCertificateTimestampList } from "./SignedCertificateTimestampList"; import { id_SignedCertificateTimestampList } from "./ObjectIdentifiers";
export interface Log { /** * Identifier of the CT Log encoded in BASE-64 format
*/
log_id: string; /** * Public key of the CT Log encoded in BASE-64 format
*/
key: string;
}
export class SignedCertificateTimestamp extends PkiObject implements ISignedCertificateTimestamp {
public version!: number; public logID!: ArrayBuffer; public timestamp!: Date; public extensions!: ArrayBuffer; public hashAlgorithm!: string; public signatureAlgorithm!: string; public signature: asn1js.BaseBlock;
/** * Initializes a new instance of the {@link SignedCertificateTimestamp} class * @param parameters Initialization parameters
*/
constructor(parameters: SignedCertificateTimestampParameters = {}) { super();
if ("stream" in parameters && parameters.stream) { this.fromStream(parameters.stream);
}
if (parameters.schema) { this.fromSchema(parameters.schema);
}
}
/** * Returns default values for all class members * @param memberName String name for a class member * @returns Default value
*/ publicstatic override defaultValues(memberName: typeof VERSION): number; publicstatic override defaultValues(memberName: typeof LOG_ID): ArrayBuffer; publicstatic override defaultValues(memberName: typeof EXTENSIONS): ArrayBuffer; publicstatic override defaultValues(memberName: typeof TIMESTAMP): Date; publicstatic override defaultValues(memberName: typeof HASH_ALGORITHM): string; publicstatic override defaultValues(memberName: typeof SIGNATURE_ALGORITHM): string; publicstatic override defaultValues(memberName: typeof SIGNATURE): Schema.SchemaType; publicstatic override defaultValues(memberName: string): any { switch (memberName) { case VERSION: return0; case LOG_ID: case EXTENSIONS: return EMPTY_BUFFER; case TIMESTAMP: returnnew Date(0); case HASH_ALGORITHM: case SIGNATURE_ALGORITHM: return EMPTY_STRING; case SIGNATURE: returnnew asn1js.Any(); default: returnsuper.defaultValues(memberName);
}
}
public fromSchema(schema: Schema.SchemaType): void { if ((schema instanceof asn1js.RawData) === false) thrownew Error("Object's schema was not verified against input data for SignedCertificateTimestamp");
const seqStream = new bs.SeqStream({
stream: new bs.ByteStream({
buffer: schema.data
})
});
this.fromStream(seqStream);
}
/** * Converts SeqStream data into current class * @param stream
*/ public fromStream(stream: bs.SeqStream): void { const blockLength = stream.getUint16();
this.version = (stream.getBlock(1))[0];
if (this.version === 0) { this.logID = (new Uint8Array(stream.getBlock(32))).buffer.slice(0); this.timestamp = new Date(pvutils.utilFromBase(new Uint8Array(stream.getBlock(8)), 8));
if (blockLength !== (47 + extensionsLength + signatureLength)) { thrownew Error("Object's stream was not correct for SignedCertificateTimestamp");
}
}
}
public toSchema(): asn1js.RawData { const stream = this.toStream();
/** * Verify SignedCertificateTimestamp for specific input data * @param logs Array of objects with information about each CT Log (like here: https://ct.grahamedgecombe.com/logs.json) * @param data Data to verify signature against. Could be encoded Certificate or encoded PreCert * @param dataType Type = 0 (data is encoded Certificate), type = 1 (data is encoded PreCert) * @param crypto Crypto engine
*/
async verify(logs: Log[], data: ArrayBuffer, dataType = 0, crypto = common.getCrypto(true)): Promise<boolean> { //#region Initial variables const logId = pvutils.toBase64(pvutils.arrayBufferToString(this.logID));
let publicKeyBase64 = null;
const stream = new bs.SeqStream(); //#endregion
//#region Found and init public key for (const log of logs) { if (log.log_id === logId) {
publicKeyBase64 = log.key; break;
}
}
if (!publicKeyBase64) { thrownew Error(`Public key not found for CT with logId: ${logId}`);
}
export interface Log { /** * Identifier of the CT Log encoded in BASE-64 format
*/
log_id: string; /** * Public key of the CT Log encoded in BASE-64 format
*/
key: string;
}
/** * Verify SignedCertificateTimestamp for specific certificate content * @param certificate Certificate for which verification would be performed * @param issuerCertificate Certificate of the issuer of target certificate * @param logs Array of objects with information about each CT Log (like here: https://ct.grahamedgecombe.com/logs.json) * @param index Index of SignedCertificateTimestamp inside SignedCertificateTimestampList (for -1 would verify all) * @param crypto Crypto engine * @return Array of verification results
*/
export async function verifySCTsForCertificate(certificate: Certificate, issuerCertificate: Certificate, logs: Log[], index = (-1), crypto = common.getCrypto(true)) {
let parsedValue: SignedCertificateTimestampList | null = null;
const stream = new bs.SeqStream();
//#region Remove certificate extension for (let i = 0; certificate.extensions && i < certificate.extensions.length; i++) { switch (certificate.extensions[i].extnID) { case id_SignedCertificateTimestampList:
{
parsedValue = certificate.extensions[i].parsedValue;
if (!parsedValue || parsedValue.timestamps.length === 0) thrownew Error("Nothing to verify in the certificate");
//#region Check we do have what to verify if (parsedValue === null) thrownew Error("No SignedCertificateTimestampList extension in the specified certificate"); //#endregion
//#region Prepare modifier TBS value const tbs = certificate.encodeTBS().toBER(); //#endregion
//#region Initialize "issuer_key_hash" value const issuerId = await crypto.digest({ name: "SHA-256" }, new Uint8Array(issuerCertificate.subjectPublicKeyInfo.toSchema().toBER(false))); //#endregion
//#region Make final "PreCert" value
stream.appendView(new Uint8Array(issuerId));
stream.appendUint24(tbs.byteLength);
stream.appendView(new Uint8Array(tbs));
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.