Improved authenticode.js error handling for #4134

Ylian Saint-Hilaire committed Jun 18, 2022 at 08:38 UTC bf128a988c644f43fc2c808b6202f191f3dd1544
1 file changed +85 -67
authenticode.js
+85 -67
@@ -288,77 +288,95 @@ function createAuthenticodeHandler(path) {
288 var derlen = forge.asn1.getBerValueLength(forge.util.createBuffer(pkcs7raw.slice(1, 5))) + 4;
289 if (derlen != pkcs7raw.length) { pkcs7raw = pkcs7raw.slice(0, derlen); }
290
291 - // Decode the signature block
292 - var pkcs7der = forge.asn1.fromDer(forge.util.createBuffer(pkcs7raw));
293 -
294 - // To work around ForgeJS PKCS#7 limitation, this may break PKCS7 verify if ForgeJS adds support for it in the future
295 - // Switch content type from "1.3.6.1.4.1.311.2.1.4" to "1.2.840.113549.1.7.1"
296 - pkcs7der.value[1].value[0].value[2].value[0].value = forge.asn1.oidToDer(forge.pki.oids.data).data;
291 + // Decode the signature block and check that it's valid
292 + var pkcs7der = null, valid = false;
293 + try { pkcs7der = forge.asn1.fromDer(forge.util.createBuffer(pkcs7raw)); } catch (ex) { }
294 + try { valid = ((pkcs7der != null) && (forge.asn1.derToOid(pkcs7der.value[1].value[0].value[2].value[0].value) == "1.3.6.1.4.1.311.2.1.4")); } catch (ex) { }
295 + if (pkcs7der == null) {
296 + // Can't decode the signature
297 + obj.header.sigpos = 0;
298 + obj.header.siglen = 0;
299 + obj.header.signed = false;
300 + } else {
301 + // To work around ForgeJS PKCS#7 limitation, this may break PKCS7 verify if ForgeJS adds support for it in the future
302 + // Switch content type from "1.3.6.1.4.1.311.2.1.4" to "1.2.840.113549.1.7.1"
303 + pkcs7der.value[1].value[0].value[2].value[0].value = forge.asn1.oidToDer(forge.pki.oids.data).data;
304
298 - // Decode the PKCS7 message
299 - var pkcs7 = p7.messageFromAsn1(pkcs7der);
300 - var pkcs7content = pkcs7.rawCapture.content.value[0];
301 -
302 - // Verify a PKCS#7 signature
303 - // Verify is not currently supported in node-forge, but if implemented in the future, this code could work.
304 - //var caStore = forge.pki.createCaStore();
305 - //for (var i in obj.certificates) { caStore.addCertificate(obj.certificates[i]); }
306 - // Return is true if all signatures are valid and chain up to a provided CA
307 - //if (!pkcs7.verify(caStore)) { throw ('Executable file has an invalid signature.'); }
308 -
309 - // Get the signing attributes
310 - obj.signingAttribs = [];
311 - try {
312 - for (var i in pkcs7.rawCapture.authenticatedAttributes) {
313 - if (
314 - (pkcs7.rawCapture.authenticatedAttributes[i].value != null) &&
315 - (pkcs7.rawCapture.authenticatedAttributes[i].value[0] != null) &&
316 - (pkcs7.rawCapture.authenticatedAttributes[i].value[0].value != null) &&
317 - (pkcs7.rawCapture.authenticatedAttributes[i].value[1] != null) &&
318 - (pkcs7.rawCapture.authenticatedAttributes[i].value[1].value != null) &&
319 - (pkcs7.rawCapture.authenticatedAttributes[i].value[1].value[0] != null) &&
320 - (pkcs7.rawCapture.authenticatedAttributes[i].value[1].value[0].value != null) &&
321 - (forge.asn1.derToOid(pkcs7.rawCapture.authenticatedAttributes[i].value[0].value) == obj.Oids.SPC_SP_OPUS_INFO_OBJID)) {
322 - for (var j in pkcs7.rawCapture.authenticatedAttributes[i].value[1].value[0].value) {
305 + // Decode the PKCS7 message
306 + var pkcs7 = null, pkcs7content = null;
307 + try {
308 + pkcs7 = p7.messageFromAsn1(pkcs7der);
309 + pkcs7content = pkcs7.rawCapture.content.value[0];
310 + } catch (ex) { }
311 +
312 + if ((pkcs7 == null) || (pkcs7content == null)) {
313 + // Can't decode the signature
314 + obj.header.sigpos = 0;
315 + obj.header.siglen = 0;
316 + obj.header.signed = false;
317 + } else {
318 + // Verify a PKCS#7 signature
319 + // Verify is not currently supported in node-forge, but if implemented in the future, this code could work.
320 + //var caStore = forge.pki.createCaStore();
321 + //for (var i in obj.certificates) { caStore.addCertificate(obj.certificates[i]); }
322 + // Return is true if all signatures are valid and chain up to a provided CA
323 + //if (!pkcs7.verify(caStore)) { throw ('Executable file has an invalid signature.'); }
324 +
325 + // Get the signing attributes
326 + obj.signingAttribs = [];
327 + try {
328 + for (var i in pkcs7.rawCapture.authenticatedAttributes) {
329 if (
324 - (pkcs7.rawCapture.authenticatedAttributes[i].value[1].value[0].value[j] != null) &&
325 - (pkcs7.rawCapture.authenticatedAttributes[i].value[1].value[0].value[j].value != null) &&
326 - (pkcs7.rawCapture.authenticatedAttributes[i].value[1].value[0].value[j].value[0] != null) &&
327 - (pkcs7.rawCapture.authenticatedAttributes[i].value[1].value[0].value[j].value[0].value != null)
328 - ) {
329 - var v = pkcs7.rawCapture.authenticatedAttributes[i].value[1].value[0].value[j].value[0].value;
330 - if (v.startsWith('http://') || v.startsWith('https://') || ((v.length % 2) == 1)) { obj.signingAttribs.push(v); } else {
331 - var r = ''; // This string value is in UCS2 format, convert it to a normal string.
332 - for (var k = 0; k < v.length; k += 2) { r += String.fromCharCode((v.charCodeAt(k + 8) << 8) + v.charCodeAt(k + 1)); }
333 - obj.signingAttribs.push(r);
330 + (pkcs7.rawCapture.authenticatedAttributes[i].value != null) &&
331 + (pkcs7.rawCapture.authenticatedAttributes[i].value[0] != null) &&
332 + (pkcs7.rawCapture.authenticatedAttributes[i].value[0].value != null) &&
333 + (pkcs7.rawCapture.authenticatedAttributes[i].value[1] != null) &&
334 + (pkcs7.rawCapture.authenticatedAttributes[i].value[1].value != null) &&
335 + (pkcs7.rawCapture.authenticatedAttributes[i].value[1].value[0] != null) &&
336 + (pkcs7.rawCapture.authenticatedAttributes[i].value[1].value[0].value != null) &&
337 + (forge.asn1.derToOid(pkcs7.rawCapture.authenticatedAttributes[i].value[0].value) == obj.Oids.SPC_SP_OPUS_INFO_OBJID)) {
338 + for (var j in pkcs7.rawCapture.authenticatedAttributes[i].value[1].value[0].value) {
339 + if (
340 + (pkcs7.rawCapture.authenticatedAttributes[i].value[1].value[0].value[j] != null) &&
341 + (pkcs7.rawCapture.authenticatedAttributes[i].value[1].value[0].value[j].value != null) &&
342 + (pkcs7.rawCapture.authenticatedAttributes[i].value[1].value[0].value[j].value[0] != null) &&
343 + (pkcs7.rawCapture.authenticatedAttributes[i].value[1].value[0].value[j].value[0].value != null)
344 + ) {
345 + var v = pkcs7.rawCapture.authenticatedAttributes[i].value[1].value[0].value[j].value[0].value;
346 + if (v.startsWith('http://') || v.startsWith('https://') || ((v.length % 2) == 1)) { obj.signingAttribs.push(v); } else {
347 + var r = ''; // This string value is in UCS2 format, convert it to a normal string.
348 + for (var k = 0; k < v.length; k += 2) { r += String.fromCharCode((v.charCodeAt(k + 8) << 8) + v.charCodeAt(k + 1)); }
349 + obj.signingAttribs.push(r);
350 + }
351 + }
352 }
353 }
354 }
355 + } catch (ex) { }
356 +
357 + // Set the certificate chain
358 + obj.certificates = pkcs7.certificates;
359 +
360 + // Set the signature
361 + obj.signature = Buffer.from(pkcs7.rawCapture.signature, 'binary');
362 +
363 + // Get the file hashing algorithm
364 + var hashAlgoOid = forge.asn1.derToOid(pkcs7content.value[1].value[0].value[0].value);
365 + switch (hashAlgoOid) {
366 + case forge.pki.oids.sha256: { obj.fileHashAlgo = 'sha256'; break; }
367 + case forge.pki.oids.sha384: { obj.fileHashAlgo = 'sha384'; break; }
368 + case forge.pki.oids.sha512: { obj.fileHashAlgo = 'sha512'; break; }
369 + case forge.pki.oids.sha224: { obj.fileHashAlgo = 'sha224'; break; }
370 + case forge.pki.oids.md5: { obj.fileHashAlgo = 'md5'; break; }
371 }
338 - }
339 - } catch (ex) { }
340 -
341 - // Set the certificate chain
342 - obj.certificates = pkcs7.certificates;
343 -
344 - // Set the signature
345 - obj.signature = Buffer.from(pkcs7.rawCapture.signature, 'binary');
346 -
347 - // Get the file hashing algorithm
348 - var hashAlgoOid = forge.asn1.derToOid(pkcs7content.value[1].value[0].value[0].value);
349 - switch (hashAlgoOid) {
350 - case forge.pki.oids.sha256: { obj.fileHashAlgo = 'sha256'; break; }
351 - case forge.pki.oids.sha384: { obj.fileHashAlgo = 'sha384'; break; }
352 - case forge.pki.oids.sha512: { obj.fileHashAlgo = 'sha512'; break; }
353 - case forge.pki.oids.sha224: { obj.fileHashAlgo = 'sha224'; break; }
354 - case forge.pki.oids.md5: { obj.fileHashAlgo = 'md5'; break; }
355 - }
372
357 - // Get the signed file hash
358 - obj.fileHashSigned = Buffer.from(pkcs7content.value[1].value[1].value, 'binary')
373 + // Get the signed file hash
374 + obj.fileHashSigned = Buffer.from(pkcs7content.value[1].value[1].value, 'binary')
375
360 - // Compute the actual file hash
361 - if (obj.fileHashAlgo != null) { obj.fileHashActual = obj.getHash(obj.fileHashAlgo); }
376 + // Compute the actual file hash
377 + if (obj.fileHashAlgo != null) { obj.fileHashActual = obj.getHash(obj.fileHashAlgo); }
378 + }
379 + }
380 }
381 return true;
382 }
@@ -486,7 +504,7 @@ function createAuthenticodeHandler(path) {
504 fs.closeSync(output);
505
506 // Indicate we are done
489 - func(null, written);
507 + func(null);
508 });
509 });
510
@@ -1372,7 +1390,7 @@ function createAuthenticodeHandler(path) {
1390
1391 // Close the file
1392 fs.closeSync(output);
1375 - func(null, written);
1393 + func(null);
1394 }
1395
1396 // Save an executable without the signature
@@ -1635,7 +1653,7 @@ function createAuthenticodeHandler(path) {
1653 fs.closeSync(output);
1654
1655 // Indicate success
1638 - func(null, written);
1656 + func(null);
1657 }
1658
1659 function writeExecutableEx(output, p7signature, written, func) {
@@ -1669,7 +1687,7 @@ function createAuthenticodeHandler(path) {
1687 fs.closeSync(output);
1688
1689 // Indicate success
1672 - func(null, written);
1690 + func(null);
1691 }
1692
1693 // Return null if we could not open the file