1"use strict"; 2Object.defineProperty(exports, "__esModule", { value: true }); 3exports.SignedCertificateTimestamp = void 0; 4const util_1 = require("../util"); 5const stream_1 = require("../util/stream"); 6class SignedCertificateTimestamp { 7 constructor(options) { 8 this.version = options.version; 9 this.logID = options.logID; 10 this.timestamp = options.timestamp; 11 this.extensions = options.extensions; 12 this.hashAlgorithm = options.hashAlgorithm; 13 this.signatureAlgorithm = options.signatureAlgorithm; 14 this.signature = options.signature; 15 } 16 get datetime() { 17 return new Date(Number(this.timestamp.readBigInt64BE())); 18 } 19 // Returns the hash algorithm used to generate the SCT's signature. 20 // https://www.rfc-editor.org/rfc/rfc5246#section-7.4.1.4.1 21 get algorithm() { 22 switch (this.hashAlgorithm) { 23 case 0: 24 return 'none'; 25 case 1: 26 return 'md5'; 27 case 2: 28 return 'sha1'; 29 case 3: 30 return 'sha224'; 31 case 4: 32 return 'sha256'; 33 case 5: 34 return 'sha384'; 35 case 6: 36 return 'sha512'; 37 default: 38 return 'unknown'; 39 } 40 } 41 verify(preCert, logs) { 42 // Find key for the log reponsible for this signature 43 const log = logs.find((log) => log.logId?.keyId.equals(this.logID)); 44 if (!log?.publicKey?.rawBytes) { 45 throw new Error(`No key found for log: ${this.logID.toString('base64')}`); 46 } 47 const publicKey = util_1.crypto.createPublicKey(log.publicKey.rawBytes); 48 // Assemble the digitally-signed struct (the data over which the signature 49 // was generated). 50 // https://www.rfc-editor.org/rfc/rfc6962#section-3.2 51 const stream = new stream_1.ByteStream(); 52 stream.appendChar(this.version); 53 stream.appendChar(0x00); // SignatureType = certificate_timestamp(0) 54 stream.appendView(this.timestamp); 55 stream.appendUint16(0x01); // LogEntryType = precert_entry(1) 56 stream.appendView(preCert); 57 stream.appendUint16(this.extensions.byteLength); 58 if (this.extensions.byteLength > 0) { 59 stream.appendView(this.extensions); 60 } 61 return util_1.crypto.verifyBlob(stream.buffer, publicKey, this.signature, this.algorithm); 62 } 63 // Parses a SignedCertificateTimestamp from a buffer. SCTs are encoded using 64 // TLS encoding which means the fields and lengths of most fields are 65 // specified as part of the SCT and TLS specs. 66 // https://www.rfc-editor.org/rfc/rfc6962#section-3.2 67 // https://www.rfc-editor.org/rfc/rfc5246#section-7.4.1.4.1 68 static parse(buf) { 69 const stream = new stream_1.ByteStream(buf); 70 // Version - enum { v1(0), (255) } 71 const version = stream.getUint8(); 72 // Log ID - struct { opaque key_id[32]; } 73 const logID = stream.getBlock(32); 74 // Timestamp - uint64 75 const timestamp = stream.getBlock(8); 76 // Extensions - opaque extensions<0..2^16-1>; 77 const extenstionLength = stream.getUint16(); 78 const extensions = stream.getBlock(extenstionLength); 79 // Hash algo - enum { sha256(4), . . . (255) } 80 const hashAlgorithm = stream.getUint8(); 81 // Signature algo - enum { anonymous(0), rsa(1), dsa(2), ecdsa(3), (255) } 82 const signatureAlgorithm = stream.getUint8(); 83 // Signature - opaque signature<0..2^16-1>; 84 const sigLength = stream.getUint16(); 85 const signature = stream.getBlock(sigLength); 86 // Check that we read the entire buffer 87 if (stream.position !== buf.length) { 88 throw new Error('SCT buffer length mismatch'); 89 } 90 return new SignedCertificateTimestamp({ 91 version, 92 logID, 93 timestamp, 94 extensions, 95 hashAlgorithm, 96 signatureAlgorithm, 97 signature, 98 }); 99 } 100} 101exports.SignedCertificateTimestamp = SignedCertificateTimestamp; 102