From c4e5dafa17e874b2c0b27aafa3ac977644a8ef39 Mon Sep 17 00:00:00 2001 From: zboris12 Date: Tue, 4 Oct 2022 21:37:17 +0900 Subject: [PATCH] Made js files to compiled by closure compiler. --- closure/build.bat | 16 +++ closure/forge-ext.js | 319 ++++++++++++++++++++++++++++++++++++++++++ closure/google-ext.js | 50 +++++++ closure/pdflib-ext.js | 265 +++++++++++++++++++++++++++++++++++ closure/zb-externs.js | 26 +++- zgapdfcryptor.js | 119 ++++++++++++---- zgapdfsigner.js | 93 ++++++++---- 7 files changed, 836 insertions(+), 52 deletions(-) create mode 100644 closure/build.bat create mode 100644 closure/forge-ext.js create mode 100644 closure/google-ext.js create mode 100644 closure/pdflib-ext.js diff --git a/closure/build.bat b/closure/build.bat new file mode 100644 index 0000000..5503f2b --- /dev/null +++ b/closure/build.bat @@ -0,0 +1,16 @@ +@echo off + +set csr=\java\8\jre\bin\java.exe -jar \closure-compiler\closure-compiler-v20220104.jar --charset UTF-8 --compilation_level SIMPLE_OPTIMIZATIONS --warning_level VERBOSE +doskey csr=%csr% $* +set externs=--externs closure\google-ext.js --externs closure\forge-ext.js --externs closure\pdflib-ext.js --externs closure\zb-externs.js + +rem main +set src=. +set jss=--js %src%\zgapdfcryptor.js --js %src%\zgapdfsigner.js +echo $ +set chkj=%%externs%% --checks_only %%jss%% +echo chkj=csr %chkj% +doskey chkj=%csr% %chkj% +set csrj=%%externs%% %%jss%% --js_output_file %src%\dist\zgapdfsigner.min.js +echo csrj=csr %csrj% +doskey csrj=%csr% %csrj% diff --git a/closure/forge-ext.js b/closure/forge-ext.js new file mode 100644 index 0000000..080a54f --- /dev/null +++ b/closure/forge-ext.js @@ -0,0 +1,319 @@ +/** @const */ +var forge = {}; +forge.random = {}; +/** + * @param {number} count + * @return {string} + */ +forge.random.getBytesSync = function(count){}; + +forge.util = {}; +/** @constructor */ +forge.util.ByteStringBuffer = function(){}; +/** + * @return {string} + */ +forge.util.ByteStringBuffer.prototype.getBytes = function(){}; +/** + * @param {string} value + * @return {forge.util.ByteStringBuffer} + */ +forge.util.ByteStringBuffer.prototype.putBytes = function(value){}; +/** + * @param {number} i + * @return {forge.util.ByteStringBuffer} + */ +forge.util.ByteStringBuffer.prototype.putInt24Le = function(i){}; +/** + * @param {number} i + * @return {forge.util.ByteStringBuffer} + */ +forge.util.ByteStringBuffer.prototype.putInt32Le = function(i){}; +/** + * @param {number} b + * @param {number} n + * @return {forge.util.ByteStringBuffer} + */ +forge.util.ByteStringBuffer.prototype.fillWithByte = function(b, n){}; +/** + * @param {number} count + * @return {forge.util.ByteStringBuffer} + */ +forge.util.ByteStringBuffer.prototype.truncate = function(count){}; +/** + * @return {string} + */ +forge.util.ByteStringBuffer.prototype.toHex = function(){}; +/** @type {string} */ +forge.util.ByteStringBuffer.prototype.data; +/** + * @param {string} input + * @param {string=} encoding + * @return {forge.util.ByteStringBuffer} + */ +forge.util.createBuffer = function(input, encoding){}; +/** + * @param {string} hex + * @return {string} + */ +forge.util.hexToBytes = function(hex){}; +/** + * @param {string} value + * @return {string} + */ +forge.util.decodeUtf8 = function(value){}; + +/** @constructor */ +forge.asn1 = function(){}; +/** + * @typedef + * {{ + * strict: (boolean|undefined), + * parseAllBytes: (boolean|undefined), + * decodeBitStrings: (boolean|undefined), + * }} + */ +var DerOption; +/** + * @param {forge.util.ByteStringBuffer|string} bytes + * @param {DerOption=} options + * @return {forge.asn1} + */ +forge.asn1.fromDer = function(bytes, options){}; +/** + * @param {forge.asn1} obj + * @return {forge.util.ByteStringBuffer} + */ +forge.asn1.toDer = function(obj){}; +/** + * @param {number} num + * @return {forge.util.ByteStringBuffer} + */ +forge.asn1.integerToDer = function(num){}; +/** + * @param {string} oid + * @return {forge.util.ByteStringBuffer} + */ +forge.asn1.oidToDer = function(oid){}; +forge.asn1.Type = {}; +/** @type {number} */ +forge.asn1.Type.UTF8; +/** @type {number} */ +forge.asn1.Type.SET; +/** @type {number} */ +forge.asn1.Type.SEQUENCE; +/** @type {number} */ +forge.asn1.Type.BOOLEAN; +/** @type {number} */ +forge.asn1.Type.INTEGER; +/** @type {number} */ +forge.asn1.Type.OID; +/** @type {number} */ +forge.asn1.Type.NULL; +/** @type {number} */ +forge.asn1.Type.OCTETSTRING; +forge.asn1.Class = {}; +/** @type {string} */ +forge.asn1.Class.UNIVERSAL; +/** + * @param {string} tagClass + * @param {number} type + * @param {boolean} constructed + * @param {Array} value + * @param {Object=} options + * @return {forge.asn1} + */ +forge.asn1.create = function(tagClass, type, constructed, value, options){}; +/** @type {Array} */ +forge.asn1.prototype.value; + +/** @constructor */ +const forge_BigInteger = function(){}; +/** + * @param {forge_BigInteger} a + * @return {number} + */ +forge_BigInteger.prototype.compareTo = function(a){}; +/** @constructor */ +const forge_cert = function(){}; +/** @type {forge_key} */ +forge_cert.prototype.publicKey; +/** @type {forge_cert_issuer} */ +forge_cert.prototype.issuer; +/** @constructor */ +const forge_key = function(){}; +/** @type {forge_BigInteger} */ +forge_key.prototype.n; +/** @type {forge_BigInteger} */ +forge_key.prototype.e; +/** @constructor */ +const forge_cert_issuer = function(){}; +/** @type {Array} */ +forge_cert_issuer.prototype.attributes; +/** + * @typedef + * {{ + * valueTagClass: (string|undefined), + * type: (string|undefined), + * value: (string|undefined), + * }} + */ +var forge_cert_attr; +/** + * @typedef + * {{ + * key: (forge_key|undefined), + * certificate: (forge_cert|undefined), + * digestAlgorithm: (string|undefined), + * authenticatedAttributes: (Array|undefined), + * unauthenticatedAttributes: (Array|undefined), + * signature: (string|undefined), + * }} + */ +var forge_signer; + +/** @constructor */ +forge.pkcs7 = function(){}; +/** + * @return {forge.pkcs7} + */ +forge.pkcs7.createSignedData = function(){}; +/** + * @param {forge_cert} cert + */ +forge.pkcs7.prototype.addCertificate = function(cert){}; +/** + * @param {forge_signer} signer + */ +forge.pkcs7.prototype.addSigner = function(signer){}; +/** @type {Array} */ +forge.pkcs7.prototype.signers; +/** @type {Array} */ +forge.pkcs7.prototype.signerInfos; +/** + * @typedef + * {{ + * detached: (boolean|undefined), + * }} + */ +var forge_sign_option; +/** + * @param {forge_sign_option} options + */ +forge.pkcs7.prototype.sign = function(options){}; +/** + * @return {forge.asn1} + */ +forge.pkcs7.prototype.toAsn1 = function(){}; + +/** @constructor */ +forge.pkcs12 = function(){}; +/** + * @param {forge.asn1} obj + * @param {boolean=} strict + * @param {string=} password + * @return {forge.pkcs12} + */ +forge.pkcs12.pkcs12FromAsn1 = function(obj, strict, password){}; +/** + * @typedef + * {{ + * localKeyId: (string|undefined), + * localKeyIdHex: (string|undefined), + * friendlyName: (string|undefined), + * bagType: (string|undefined), + * }} + */ +var P12BagsFilter; +/** + * @typedef + * {{ + * cert: forge_cert, + * key: forge_key, + * }} + */ +var P12Bag; +/** + * @param {P12BagsFilter} filter + * @return {Object>} + */ +forge.pkcs12.prototype.getBags = function(filter){}; + +forge.oids = {}; +/** @type {string} */ +forge.oids.sha256; +forge.pki = {}; +forge.pki.oids = {}; +/** @type {string} */ +forge.pki.oids.certBag; +/** @type {string} */ +forge.pki.oids.pkcs8ShroudedKeyBag; +/** @type {string} */ +forge.pki.oids.sha256; +/** @type {string} */ +forge.pki.oids.contentType; +/** @type {string} */ +forge.pki.oids.data; +/** @type {string} */ +forge.pki.oids.messageDigest; +/** @type {string} */ +forge.pki.oids.signingTime; + +forge.md = {}; +/** @constructor */ +forge.md.digest = function(){}; +/** + * @param {string=} msg + * @param {string=} encoding + * @return {forge.md.digest} + */ +forge.md.digest.prototype.update = function(msg, encoding){}; +/** + * @return {forge.util.ByteStringBuffer} + */ +forge.md.digest.prototype.digest = function(){}; +forge.md.md5 = {}; +forge.md.sha256 = {}; +/** + * @return {forge.md.digest} + */ +forge.md.md5.create = function(){}; +/** + * @return {forge.md.digest} + */ +forge.md.sha256.create = function(){}; + +forge.cipher = {}; +/** @constructor */ +forge.cipher.BlockCipher = function(){}; +/** + * @typedef + * {{ + * iv: (string|undefined), + * additionalData: (string|undefined), + * tagLength: (number|undefined), + * tag: (string|undefined), + * output: (forge.util.ByteStringBuffer|undefined), + * }} + */ +var CipherOptions; +/** + * @param {CipherOptions} options + */ +forge.cipher.BlockCipher.prototype.start = function(options){}; +/** + * @param {forge.util.ByteStringBuffer} input + */ +forge.cipher.BlockCipher.prototype.update = function(input){}; +/** + * @return {boolean} + */ +forge.cipher.BlockCipher.prototype.finish = function(){}; +/** @type {forge.util.ByteStringBuffer} */ +forge.cipher.BlockCipher.prototype.output; +/** + * @param {string} algorithm + * @param {forge.util.ByteStringBuffer} key + * @return {forge.cipher.BlockCipher} + */ +forge.cipher.createCipher = function(algorithm, key) {}; diff --git a/closure/google-ext.js b/closure/google-ext.js new file mode 100644 index 0000000..0e87aca --- /dev/null +++ b/closure/google-ext.js @@ -0,0 +1,50 @@ +/** + * @typedef + * {{ + * contentType: (string|undefined), + * headers: (Object|undefined), + * method: (string|undefined), + * payload: (string|Uint8Array|undefined), + * useIntranet: (boolean|undefined), + * validateHttpsCertificates: (boolean|undefined), + * followRedirects: (boolean|undefined), + * muteHttpExceptions: (boolean|undefined), + * escaping: (boolean|undefined), + * }} + */ +var UrlFetchParams; + +/** + * @constructor + */ +function GoogleUrlFetchApp(){} +/** + * @param {string} url + * @param {UrlFetchParams} params + * @return {HTTPResponse} + */ +GoogleUrlFetchApp.prototype.fetch = function(url, params){}; + +/** + * @const {!GoogleUrlFetchApp} + */ +var UrlFetchApp; + +/** + * @constructor + */ +function HTTPResponse(){} +/** + * @return {GBlob} + */ +HTTPResponse.prototype.getBlob = function(){}; + +/** + * @constructor + */ +function GBlob(){} +/** + * @return {Array} + */ +GBlob.prototype.getBytes = function(){}; + diff --git a/closure/pdflib-ext.js b/closure/pdflib-ext.js new file mode 100644 index 0000000..45e902f --- /dev/null +++ b/closure/pdflib-ext.js @@ -0,0 +1,265 @@ +/** + * @typedef + * {{ + * ignoreEncryption: (boolean|undefined), + * parseSpeed: (number|undefined), + * throwOnInvalidObject: (boolean|undefined), + * }} + */ +var PdfLoadOptions; + +/** @const */ +var PDFLib = {}; + +/** @constructor */ +PDFLib.PDFDocument = function(){}; +/** + * @param {string|Uint8Array|ArrayBuffer} pdf + * @param {PdfLoadOptions=} options + * @return {PDFLib.PDFDocument} + */ +PDFLib.PDFDocument.load = function(pdf, options){}; +/** + * @typedef + * {{ + * useObjectStreams: (boolean|undefined), + * addDefaultPage: (boolean|undefined), + * objectsPerTick: (number|undefined), + * }} + */ +var PdfSaveOptions; +/** + * @param {PdfSaveOptions} options + * @returns {Promise} + */ +PDFLib.PDFDocument.prototype.save = function(options){}; +/** + * @returns {Array} + */ +PDFLib.PDFDocument.prototype.getPages = function(){}; +/** + * @param {ArrayBuffer|Uint8Array|string} png + * @returns {Promise} + */ +PDFLib.PDFDocument.prototype.embedPng = function(png){}; +/** + * @param {ArrayBuffer|Uint8Array|string} jpg + * @returns {Promise} + */ +PDFLib.PDFDocument.prototype.embedJpg = function(jpg){}; +/** @type {PDFLib.PDFCatalog} */ +PDFLib.PDFDocument.prototype.catalog; +/** @type {PDFLib.PDFContext} */ +PDFLib.PDFDocument.prototype.context; + +/** @constructor */ +PDFLib.PDFCatalog = function(){}; +/** + * @param {PDFLib.PDFName} name + * @param {PDFLib.PDFObject} object + */ +PDFLib.PDFCatalog.prototype.set = function(name, object){}; + +/** @constructor */ +PDFLib.PDFPage = function(){}; +/** @type {PDFLib.PDFRef} */ +PDFLib.PDFPage.prototype.ref; +/** @type {PDFLib.PDFPageLeaf} */ +PDFLib.PDFPage.prototype.node; +/** + * @return {PDFLib.Rotation} + */ +PDFLib.PDFPage.prototype.getRotation = function(){}; +/** + * @typedef + * {{ + * width: number, + * height: number, + * }} + */ +var PdfSize; +/** + * @return {PdfSize} + */ +PDFLib.PDFPage.prototype.getSize = function(){}; + +/** @constructor */ +PDFLib.PDFPageLeaf = function(){}; +/** + * @param {PDFLib.PDFName} name + * @param {PDFLib.PDFObject} object + */ +PDFLib.PDFPageLeaf.prototype.set = function(name, object){}; + +/** @constructor */ +PDFLib.PDFRef = function(){}; +/** @type {number} */ +PDFLib.PDFRef.prototype.objectNumber; + +/** @constructor */ +PDFLib.PDFContext = function(){}; +/** + * @typedef + * {{ + * 0: PDFLib.PDFRef, + * 1: PDFLib.PDFObject, + * }} + */ +var PdfObjEntry; +/** @return {Array} */ +PDFLib.PDFContext.prototype.enumerateIndirectObjects = function(){}; +/** @type {Object} */ +PDFLib.PDFContext.prototype.trailerInfo; +/** + * @param {PDFLib.PDFObject} object + * @return {PDFLib.PDFRef} + */ +PDFLib.PDFContext.prototype.register = function(object){}; +/** + * @param {*} literal + * @return {PDFLib.PDFObject} + */ +PDFLib.PDFContext.prototype.obj = function(literal){}; + +/** @constructor */ +PDFLib.PDFObject = function(){}; +/** @type {Map} */ +PDFLib.PDFObject.prototype.dict; +/** @type {Array} */ +PDFLib.PDFObject.prototype.array; + +/** + * @constructor + * @extends {PDFLib.PDFObject} + */ +PDFLib.PDFName = function(){}; +/** + * @param {string} value + * @return {PDFLib.PDFName} + */ +PDFLib.PDFName.of = function(value){}; +/** @type {string} */ +PDFLib.PDFName.prototype.encodedName; +/** @type {number} */ +PDFLib.PDFName.prototype.numberValue; + +/** + * @constructor + * @param {PDFLib.PDFContext} context + */ +PDFLib.PDFArray = function(context){}; +/** + * @param {PDFLib.PDFObject} object + */ +PDFLib.PDFArray.prototype.push = function(object){}; + +/** + * @constructor + * @extends {PDFLib.PDFObject} + */ +PDFLib.PDFString = function(){}; +/** + * @param {string} value + * @return {PDFLib.PDFString} + */ +PDFLib.PDFString.of = function(value){}; +/** + * @param {Date} value + * @return {PDFLib.PDFString} + */ +PDFLib.PDFString.fromDate = function(value){}; + +/** + * @constructor + * @extends {PDFLib.PDFObject} + */ +PDFLib.PDFHexString = function(){}; +/** + * @param {string} value + * @return {PDFLib.PDFHexString} + */ +PDFLib.PDFHexString.of = function(value){}; +/** + * @param {string} value + * @return {PDFLib.PDFHexString} + */ +PDFLib.PDFHexString.fromText = function(value){}; + +/** + * @constructor + * @extends {PDFLib.PDFObject} + */ +PDFLib.PDFNumber = function(){}; +/** + * @param {number} value + * @return {PDFLib.PDFNumber} + */ +PDFLib.PDFNumber.of = function(value){}; + +/** @constructor */ +PDFLib.PDFImage = function(){}; +/** + * @return {PdfSize} + */ +PDFLib.PDFImage.prototype.size = function(){}; +/** @type {PDFLib.PDFRef} */ +PDFLib.PDFImage.prototype.ref; + +/** @constructor */ +PDFLib.PDFFont = function(){}; +/** @type {PDFLib.PDFRef} */ +PDFLib.PDFFont.prototype.ref; +/** @constructor */ +PDFLib.StandardFonts = function(){}; + +PDFLib.RotationTypes = {}; +/** @type {string} */ +PDFLib.RotationTypes.Degrees; +/** @constructor */ +PDFLib.Rotation = function(){}; +/** @type {string} */ +PDFLib.Rotation.prototype.type; +/** + * @param {number} d + * @return {PDFLib.Rotation} + */ +PDFLib.degrees = function(d){}; +/** + * @param {PDFLib.Rotation} rot + * @return {PDFLib.Rotation} + */ +PDFLib.toDegrees = function(rot){}; + +/** @constructor */ +PDFLib.PDFOperator = function(){}; +/** + * @typedef + * {{ + * x: (number|undefined), + * y: (number|undefined), + * width: (number|undefined), + * height: (number|undefined), + * rotate: (PDFLib.Rotation|undefined), + * xSkew: (PDFLib.Rotation|undefined), + * ySkew: (PDFLib.Rotation|undefined), + * }} + */ +var PdfDrawimgOption; +/** + * @param {string} name + * @param {PdfDrawimgOption} options + */ +PDFLib.drawImage = function(name, options){}; + +/** + * @constructor + * @extends {PDFLib.PDFObject} + */ +PDFLib.PDFContentStream = function(){}; +/** + * @param {PDFLib.PDFObject} dict + * @param {Array} operators + * @param {boolean=} encode + * @return {PDFLib.PDFContentStream} + */ +PDFLib.PDFContentStream.of = function(dict, operators, encode){}; diff --git a/closure/zb-externs.js b/closure/zb-externs.js index 87f6907..1b82355 100644 --- a/closure/zb-externs.js +++ b/closure/zb-externs.js @@ -51,7 +51,7 @@ var SignOption; * @typedef * {{ * c: string, - * p: (Array|undefined), + * p: (Array|undefined), * }} */ var PubKeyInfo; @@ -74,7 +74,7 @@ var PubKeyInfo; * @typedef * {{ * mode: Zga.Crypto.Mode, - * permissions: (Array|undefined), + * permissions: (Array|undefined), * userpwd: (string|undefined), * ownerpwd: (string|undefined), * pubkeys: (Array|undefined), @@ -101,3 +101,25 @@ var CFType; * }} */ var RC4LastInfo; + +var Zga = {}; +Zga.Crypto = {}; +/** @enum {number} */ +Zga.Crypto.Mode = { + RC4_40: 0, + RC4_128: 1, + AES_128: 2, + AES_256: 3, +}; +/** + * @constructor + * @param {EncryptOption} encopt + */ +Zga.PdfCryptor = function(encopt){}; +/** + * @param {PDFLib.PDFDocument|Array|Uint8Array|ArrayBuffer|string} pdf + * @param {boolean=} reload + * @return {Promise} + */ +Zga.PdfCryptor.prototype.encryptPdf = function(pdf, reload){}; + diff --git a/zgapdfcryptor.js b/zgapdfcryptor.js index f1eee6b..6064a11 100644 --- a/zgapdfcryptor.js +++ b/zgapdfcryptor.js @@ -14,11 +14,11 @@ z.loadPdf = async function(pdf){ /** @type {PDFLib.PDFDocument} */ var pdfdoc = null; if(pdf.addPage){ - pdfdoc = pdf; + pdfdoc = /** @type {PDFLib.PDFDocument} */(pdf); }else if(Array.isArray(pdf)){ pdfdoc = await PDFLib.PDFDocument.load(new Uint8Array(pdf)); }else{ - pdfdoc = await PDFLib.PDFDocument.load(pdf); + pdfdoc = await PDFLib.PDFDocument.load(/** @type {(ArrayBuffer|Uint8Array|string)} */(pdf)); } return pdfdoc; }; @@ -41,6 +41,7 @@ z.u8arrToRaw = function(uarr){ * @return {Uint8Array} */ z.rawToU8arr = function(raw){ + /** @type {Uint8Array} */ var arr = new Uint8Array(raw.length); for(var i=0; i} */ const CHARS = "\\()".split(""); + /** @type {Array} */ var arr = []; for(var i=0; i} */ var rc4 = null; + /** @type {number} */ var i = 0; + /** @type {number} */ var j = 0; + /** @type {number} */ var t = 0; if(lastinf.enckey != key){ + /** @type {string} */ var k = key.repeat(256 / key.length + 1); rc4 = new Array(256); // Initialize rc4 @@ -148,9 +157,13 @@ z.Crypto = { }else{ rc4 = [].concat(lastinf.enckeyc); } + /** @type {number} */ var len = txt.length; + /** @type {number} */ var a = 0; + /** @type {number} */ var b = 0; + /** @type {string} */ var out = ""; for(i=0; i=} permissions the set of permissions (specify the ones you want to block). - * @param {Zga.Crypto.Mode=} mode + * @param {Array=} permissions the set of permissions (specify the ones you want to block). + * @param {z.Crypto.Mode=} mode * @return {number} */ getUserPermissionCode: function(permissions, mode){ + /** @type {number} */ var protection = 2147422012; // 32 bit: (01111111 11111111 00001111 00111100) if(permissions){ permissions.forEach(function(a_itm){ + /** @type {number} */ var a_p = z.Crypto.Permission[a_itm]; if(a_p){ if(mode > 0 || a_p <= 32){ @@ -198,6 +213,7 @@ z.Crypto = { * @return {string} */ getEncPermissionsString: function(protection){ + /** @type {forge.util.ByteStringBuffer} */ var buff = new forge.util.ByteStringBuffer(); buff.putInt32Le(protection); return buff.getBytes(); @@ -209,6 +225,7 @@ z.Crypto = { * @return {string} MD5 encrypted binary string */ _md5_16: function(str){ + /** @type {forge.md.digest} */ var md = forge.md.md5.create(); md.update(str); return md.digest().getBytes(); @@ -223,13 +240,17 @@ z.Crypto = { */ _AES: function(key, txt){ // padding (RFC 2898, PKCS #5: Password-Based Cryptography Specification Version 2.0) - /** @type {string} */ + /** @type {number} */ var padding = 16 - (txt.length % 16); + /** @type {forge.util.ByteStringBuffer} */ var buff = forge.util.createBuffer(txt); buff.fillWithByte(padding, padding); + /** @type {string} */ var iv = forge.random.getBytesSync(16); + /** @type {forge.util.ByteStringBuffer} */ var key2 = forge.util.createBuffer(key); + /** @type {forge.cipher.BlockCipher} */ var cipher = forge.cipher.createCipher("AES-CBC", key2); cipher.start({iv: iv}); cipher.update(buff); @@ -245,9 +266,13 @@ z.Crypto = { * @return {string} encrypted text */ _AESnopad: function(key, txt) { + /** @type {forge.util.ByteStringBuffer} */ var buff = forge.util.createBuffer(txt); + /** @type {string} */ var iv = String.fromCharCode(0).repeat(16); + /** @type {forge.util.ByteStringBuffer} */ var key2 = forge.util.createBuffer(key); + /** @type {forge.cipher.BlockCipher} */ var cipher = forge.cipher.createCipher("AES-CBC", key2); cipher.start({iv: iv}); cipher.update(buff); @@ -259,18 +284,17 @@ z.Crypto = { z.PdfCryptor = class{ /** - * @constructor * @param {EncryptOption} encopt */ constructor(encopt){ /** @private @type {string} */ this.fileid = ""; /** @private @type {string} */ - this.key = null; - /** @private @type {Array} */ + this.key = ""; + /** @private @type {Array|undefined} */ this.pubkeys = encopt.pubkeys; - /** @private @type {number} */ - this.mode = encopt.mode; + /** @private @type {z.Crypto.Mode} */ + this.mode = /** @type {z.Crypto.Mode} */(encopt.mode); /** @private @type {number} */ this.protection = 0; /** @private @type {string} */ @@ -289,7 +313,7 @@ z.PdfCryptor = class{ this.V = 1; /** @private @type {number} */ this.Length = 0; - /** @private @type {CFType} */ + /** @private @type {?CFType} */ this.CF = null; /** @private @type {string} */ this.SubFilter = ""; @@ -325,7 +349,6 @@ z.PdfCryptor = class{ }; if(this.pubkeys){ - throw new Error("Public key mode is not supported yet."); if(this.mode == z.Crypto.Mode.RC4_40){ // public-Key Security requires at least 128 bit this.mode = z.Crypto.Mode.RC4_128; @@ -333,6 +356,7 @@ z.PdfCryptor = class{ this.Filter = "Adobe.PubSec"; this.StmF = "DefaultCryptFilter"; this.StrF = "DefaultCryptFilter"; + throw new Error("Public key mode is not supported yet."); } if(encopt.userpwd){ @@ -341,6 +365,7 @@ z.PdfCryptor = class{ if(encopt.ownerpwd){ this.ownerpwd = encopt.ownerpwd; }else{ + /** @type {forge.md.digest} */ var md = forge.md.md5.create(); md.update(z.Crypto.getRandomSeed()); this.ownerpwd = md.digest().toHex(); @@ -379,6 +404,8 @@ z.PdfCryptor = class{ this.Recipients = []; } break; + default: + throw new Error("Unknown crypto mode. " + this.mode); } this.protection = z.Crypto.getUserPermissionCode(encopt.permissions, this.mode); @@ -411,7 +438,7 @@ z.PdfCryptor = class{ /** * @param {number} a_num - * @param {*} a_val + * @param {PDFLib.PDFObject} a_val */ var func = function(a_num, a_val){ if(a_val instanceof PDFLib.PDFStream){ @@ -430,7 +457,7 @@ z.PdfCryptor = class{ if(a_val.dict instanceof Map){ /** @type {Iterator} */ var a_es = a_val.dict.entries(); - /** @type {IteratorResult} */ + /** @type {IIterableResult} */ var a_res = a_es.next(); while(!a_res.done){ func(a_num, a_res.value[1]); @@ -438,7 +465,7 @@ z.PdfCryptor = class{ } } }.bind(this); - pdfcont.enumerateIndirectObjects().forEach(function(a_arr){ + pdfcont.enumerateIndirectObjects().forEach(function(/** @type {PdfObjEntry} */a_arr){ func(a_arr[0].objectNumber, a_arr[1]); }); @@ -456,12 +483,17 @@ z.PdfCryptor = class{ */ prepareEncrypt(pdfcont){ if(!pdfcont.trailerInfo.ID){ + /** @type {forge.md.digest} */ var md = forge.md.md5.create(); md.update(z.Crypto.getRandomSeed()); + /** @type {forge.util.ByteStringBuffer} */ var res = md.digest(); + /** @type {string} */ var idhex = res.toHex(); + /** @type {string} */ this.fileid = res.getBytes(); + /** @type {PDFLib.PDFArray} */ var trIds = new PDFLib.PDFArray(pdfcont); trIds.push(PDFLib.PDFHexString.of(idhex)); trIds.push(PDFLib.PDFHexString.of(idhex)); @@ -484,6 +516,7 @@ z.PdfCryptor = class{ if(this.V >= 4){ // A dictionary whose keys shall be crypt filter names and whose values shall be the corresponding crypt filter dictionaries. if(this.CF){ + /** @type {Object} */ var objStmF = { Type: "CryptFilter", }; @@ -492,11 +525,11 @@ z.PdfCryptor = class{ objStmF.CFM = this.CF.CFM; if(this.pubkeys){ /** @type {PDFLib.PDFArray} */ - var recps = new PDFLib.PDFArray(pdfcont); + var recps1 = new PDFLib.PDFArray(pdfcont); this.Recipients.forEach(function(a_ele){ - recps.push(PDFLib.PDFHexString.of(a_ele)); + recps1.push(PDFLib.PDFHexString.of(a_ele)); }); - objStmF.Recipients = recps; + objStmF.Recipients = recps1; if(typeof this.CF.EncryptMetadata == "boolean" && !this.CF.EncryptMetadata){ objStmF.EncryptMetadata = false; }else{ @@ -517,6 +550,7 @@ z.PdfCryptor = class{ objStmF.Length = this.CF.Length; } + /** @type {Object} */ var objCF = { [this.StmF]: pdfcont.obj(objStmF), }; @@ -569,7 +603,9 @@ z.PdfCryptor = class{ * @return {Uint8Array} */ encryptU8arr(num, dat){ + /** @type {string} */ var str = z.u8arrToRaw(dat); + /** @type {string} */ var enc = this._encrypt_data(num, str); return z.rawToU8arr(enc); } @@ -581,7 +617,9 @@ z.PdfCryptor = class{ * @return {string} */ encryptHexstr(num, dat){ + /** @type {string} */ var str = forge.util.hexToBytes(dat); + /** @type {string} */ var enc = this._encrypt_data(num, str); return forge.util.createBuffer(enc).toHex(); } @@ -595,6 +633,7 @@ z.PdfCryptor = class{ * @return {string} object key */ _objectkey(n){ + /** @type {forge.util.ByteStringBuffer} */ var buff = forge.util.createBuffer(this.key); //pack('VXxx', $n) buff.putInt24Le(n); @@ -604,8 +643,10 @@ z.PdfCryptor = class{ buff.putBytes("sAlT"); } + /** @type {forge.md.digest} */ var md = forge.md.md5.create(); md.update(buff.getBytes()); + /** @type {forge.util.ByteStringBuffer} */ var ret = md.digest(); return ret.getBytes().substr(0, Math.min(16, (this.Length / 8) + 5)); } @@ -640,13 +681,19 @@ z.PdfCryptor = class{ * @return {string} U value */ _Uvalue(){ + /** @type {string} */ + var ret = ""; if(this.mode == z.Crypto.Mode.RC4_40){ - return z.Crypto._RC4(this.key, z.Crypto.EncPadding, this.rc4inf); + ret = z.Crypto._RC4(this.key, z.Crypto.EncPadding, this.rc4inf); }else if(this.mode < z.Crypto.Mode.AES_256) { // RC4-128, AES-128 + /** @type {string} */ var tmp = z.Crypto._md5_16(z.Crypto.EncPadding + this.fileid); + /** @type {string} */ var enc = z.Crypto._RC4(this.key, tmp, this.rc4inf); + /** @type {number} */ var len = tmp.length; for(var i=1; i<=19; i++){ + /** @type {string} */ var ek = ""; for(var j=0; j z.Crypto.Mode.RC4_40){ for(var i=0; i<50; i++){ tmp = z.Crypto._md5_16(tmp); } } + /** @type {string} */ var owner_key = tmp.substr(0, this.Length / 8); - var enc = z.Crypto._RC4(owner_key, this.userpwd, this.rc4inf); + ret = z.Crypto._RC4(owner_key, this.userpwd, this.rc4inf); if(this.mode > z.Crypto.Mode.RC4_40){ + /** @type {number} */ var len = owner_key.length; for(var i=1; i<=19; i++){ + /** @type {string} */ var ek = ""; for(var j=0; j z.Crypto.Mode.RC4_40) { for(var i=0; i<50; i++){ diff --git a/zgapdfsigner.js b/zgapdfsigner.js index d039552..bf744b9 100644 --- a/zgapdfsigner.js +++ b/zgapdfsigner.js @@ -18,7 +18,6 @@ z.TSAURLS = { z.PdfSigner = class{ /** - * @constructor * @param {SignOption} signopt */ constructor(signopt){ @@ -26,7 +25,7 @@ z.PdfSigner = class{ this.DEFAULT_BYTE_RANGE_PLACEHOLDER = "**********"; /** @private @type {SignOption} */ this.opt = signopt; - /** @private @type {TsaServiceInfo} */ + /** @private @type {?TsaServiceInfo} */ this.tsainf = null; /** @private @type {number} */ this.siglen = 0; @@ -47,7 +46,7 @@ z.PdfSigner = class{ url: signopt.signdate, }; }else if(signopt.signdate.url){ - this.tsainf = Object.assign({}, signopt.signdate); + this.tsainf = /** @type {TsaServiceInfo} */(Object.assign({}, signopt.signdate)); } } if(this.tsainf){ @@ -150,7 +149,7 @@ z.PdfSigner = class{ * @param {PDFLib.PDFDocument} pdfdoc */ addSignHolder(pdfdoc){ - /** @const {VisualSignature} */ + /** @const {z.VisualSignature} */ const visign = new z.VisualSignature(this.opt.drawinf); /** @const {PDFLib.PDFRef} */ const strmRef = visign.createStream(pdfdoc, this.opt.signame); @@ -170,7 +169,7 @@ z.PdfSigner = class{ bytrng.push(PDFLib.PDFName.of(this.DEFAULT_BYTE_RANGE_PLACEHOLDER)); bytrng.push(PDFLib.PDFName.of(this.DEFAULT_BYTE_RANGE_PLACEHOLDER)); - this.siglen = this.tsainf ? this.tsainf.len : 3322; + this.siglen = /** @type {number} */(this.tsainf ? this.tsainf.len : 3322); this.sigContents = PDFLib.PDFHexString.of("0".repeat(this.siglen)); /** @type {Object} */ @@ -239,13 +238,13 @@ z.PdfSigner = class{ var istgt = false; /** @type {PDFLib.PDFHexString} */ var sigContents = null; - /** @type {Array<*>} */ + /** @type {Array} */ var objarr = pdfdoc.context.enumerateIndirectObjects(); for(var i=objarr.length - 1; i>= 0; i--){ if(objarr[i][1].dict instanceof Map){ - /** @type {Iterator} */ + /** @type {Iterator} */ var es = objarr[i][1].dict.entries(); - /** @type {IteratorResult} */ + /** @type {IIterableResult} */ var res = es.next(); istgt = false; sigContents = null; @@ -272,6 +271,7 @@ z.PdfSigner = class{ } } } + return null; } /** @@ -287,24 +287,35 @@ z.PdfSigner = class{ } // Finds ByteRange information within a given PDF Buffer if one exists + /** @type {Array} */ var byteRangeStrings = pdfstr.match(/\/ByteRange\s*\[{1}\s*(?:(?:\d*|\/\*{10})\s+){3}(?:\d+|\/\*{10}){1}\s*]{1}/g); + /** @type {string|undefined} */ var byteRangePlaceholder = byteRangeStrings.find(function(a_str){ return a_str.includes("/"+this.DEFAULT_BYTE_RANGE_PLACEHOLDER); }.bind(this)); if(!byteRangePlaceholder){ throw new Error("no signature placeholder"); } + /** @type {number} */ var byteRangePos = pdfstr.indexOf(byteRangePlaceholder); + /** @type {number} */ var byteRangeEnd = byteRangePos + byteRangePlaceholder.length; + /** @type {number} */ var contentsTagPos = pdfstr.indexOf('/Contents ', byteRangeEnd); + /** @type {number} */ var placeholderPos = pdfstr.indexOf('<', contentsTagPos); + /** @type {number} */ var placeholderEnd = pdfstr.indexOf('>', placeholderPos); + /** @type {number} */ var placeholderLengthWithBrackets = placeholderEnd + 1 - placeholderPos; + /** @type {number} */ var placeholderLength = placeholderLengthWithBrackets - 2; + /** @type {Array} */ var byteRange = [0, 0, 0, 0]; byteRange[1] = placeholderPos; byteRange[2] = byteRange[1] + placeholderLengthWithBrackets; byteRange[3] = pdfstr.length - byteRange[2]; + /** @type {string} */ var actualByteRange = "/ByteRange [" + byteRange.join(" ") +"]"; actualByteRange += ' '.repeat(byteRangePlaceholder.length - actualByteRange.length); // Replace the /ByteRange placeholder with the actual ByteRange @@ -316,20 +327,26 @@ z.PdfSigner = class{ this.opt.p12cert = z.u8arrToRaw(new Uint8Array(this.opt.p12cert)); } // Convert Buffer P12 to a forge implementation. + /** @type {forge.asn1} */ var p12Asn1 = forge.asn1.fromDer(this.opt.p12cert); + /** @type {forge.pkcs12} */ var p12 = forge.pkcs12.pkcs12FromAsn1(p12Asn1, true, this.opt.pwd); // Extract safe bags by type. // We will need all the certificates and the private key. + /** @type {Object} */ var certBags = p12.getBags({ "bagType": forge.pki.oids.certBag, })[forge.pki.oids.certBag]; + /** @type {Object} */ var keyBags = p12.getBags({ "bagType": forge.pki.oids.pkcs8ShroudedKeyBag, })[forge.pki.oids.pkcs8ShroudedKeyBag]; + /** @type {forge_key} */ var privateKey = keyBags[0].key; // Here comes the actual PKCS#7 signing. + /** @type {forge.pkcs7} */ var p7 = forge.pkcs7.createSignedData(); // Start off by setting the content. p7.content = forge.util.createBuffer(pdfstr); @@ -337,18 +354,22 @@ z.PdfSigner = class{ // Then add all the certificates (-cacerts & -clcerts) // Keep track of the last found client certificate. // This will be the public key that will be bundled in the signature. + /** @type {forge_cert} */ var cert = null; - Object.keys(certBags).forEach(function(a_ele){ - var a_cert = certBags[a_ele].cert; + if(certBags){ + Object.keys(certBags).forEach(function(a_ele){ + /** @type {forge_cert} */ + var a_cert = certBags[a_ele].cert; - p7.addCertificate(a_cert); + p7.addCertificate(a_cert); - // Try to find the certificate that matches the private key. - if(privateKey.n.compareTo(a_cert.publicKey.n) === 0 - && privateKey.e.compareTo(a_cert.publicKey.e) === 0){ - cert = a_cert; - } - }); + // Try to find the certificate that matches the private key. + if(privateKey.n.compareTo(a_cert.publicKey.n) === 0 + && privateKey.e.compareTo(a_cert.publicKey.e) === 0){ + cert = a_cert; + } + }); + } if(cert){ // When converting to asn1, forge will encode the value of issuer to utf8 if the valueTagClass is UTF8. // But the value load from pfx is already utf8 encoded, so the encoding action will break the final signature. @@ -389,6 +410,7 @@ z.PdfSigner = class{ p7.sign({"detached": true}); if(this.tsainf){ + /** @type {forge.asn1} */ var tsatoken = this.queryTsa(p7.signers[0].signature); p7.signerInfos[0].value[6].value[0].value[1] = forge.asn1.create( forge.asn1.Class.UNIVERSAL, @@ -400,6 +422,7 @@ z.PdfSigner = class{ } // Check if the PDF has a good enough placeholder to fit the signature. + /** @type {string} */ var sighex = forge.asn1.toDer(p7.toAsn1()).toHex(); // placeholderLength represents the length of the HEXified symbols but we're // checking the actual lengths. @@ -425,6 +448,7 @@ z.PdfSigner = class{ */ convToPDFString(str){ // Check if there is a multi-bytes char in the string. + /** @type {boolean} */ var flg = false; for(var i=0; i 0xFF){ @@ -441,14 +465,16 @@ z.PdfSigner = class{ /** * @private - * @param {string} signature + * @param {string=} signature * @return {string} */ genTsrData(signature){ // Generate SHA256 hash from signature content for TSA + /** @type {forge.md.digest} */ var md = forge.md.sha256.create(); md.update(signature); // Generate TSA request + /** @type {forge.asn1} */ var asn1Req = forge.asn1.create( forge.asn1.Class.UNIVERSAL, forge.asn1.Type.SEQUENCE, @@ -511,19 +537,25 @@ z.PdfSigner = class{ /** * @private - * @param {string} signature - * @return {Object} + * @param {string=} signature + * @return {forge.asn1} */ queryTsa(signature){ + /** @type {string} */ var tsr = this.genTsrData(signature); + /** @type {Uint8Array} */ var tu8s = z.rawToU8arr(tsr); + /** @type {UrlFetchParams} */ var options = { "method": "POST", "headers": {"Content-Type": "application/timestamp-query"}, "payload": tu8s, }; + /** @type {GBlob} */ var tblob = UrlFetchApp.fetch(this.tsainf.url, options).getBlob(); + /** @type {string} */ var tstr = z.u8arrToRaw(new Uint8Array(tblob.getBytes())); + /** @type {forge.asn1} */ var token = forge.asn1.fromDer(tstr).value[1]; return token; } @@ -541,7 +573,6 @@ z.PdfSigner = class{ z.VisualSignature = class{ /** - * @constructor * @param {SignDrawInfo=} drawinf */ constructor(drawinf){ @@ -549,7 +580,7 @@ z.VisualSignature = class{ this.pgidx = 0; /** @private @type {Array} */ this.rect = [0, 0, 0, 0]; - /** @private @type {SignDrawInfo} */ + /** @private @type {?SignDrawInfo} */ this.drawinf = null; if(drawinf){ @@ -598,21 +629,29 @@ z.VisualSignature = class{ }else{ throw new Error("Page index is overflow to pdf pages."); } + /** @type {PDFLib.Rotation} */ var pgrot = page.getRotation(); pgrot.angle = PDFLib.toDegrees(pgrot) % 360; pgrot.type = PDFLib.RotationTypes.Degrees; + /** @type {PdfSize} */ var pgsz = page.getSize(); + /** @type {SignAreaInfo} */ var areainf = this.calcAreaInf(pgsz, pgrot.angle, this.drawinf.area); // resources object + /** @type {Object} */ var rscObj = {}; /** @type {Array} */ var sigOprs = []; + /** @type {string} */ var imgName = signame ? signame.concat("Img") : "SigImg"; + /** @type {string} */ var fontName = signame ? signame.concat("Font") : "SigFont"; if(this.drawinf.img){ // Get scaled image size + /** @type {PdfSize} */ var imgsz = this.drawinf.img.size(); + /** @type {number} */ var tmp = areainf.w * imgsz.height / imgsz.width; if(tmp <= areainf.h){ areainf.h = tmp; @@ -633,6 +672,7 @@ z.VisualSignature = class{ this.rect = this.calcRect(pgrot.angle, areainf); + /** @type {PDFLib.PDFObject} */ var frmDict = pdfdoc.context.obj({ "Type": "XObject", "Subtype": "Form", @@ -640,6 +680,7 @@ z.VisualSignature = class{ "BBox": [0, 0, areainf.w, areainf.h], "Resources": rscObj, }); + /** @type {PDFLib.PDFContentStream} */ var strm = PDFLib.PDFContentStream.of(frmDict, sigOprs, false); return pdfdoc.context.register(strm); } @@ -648,13 +689,13 @@ z.VisualSignature = class{ * Calculate area informations for drawing signature after rotate * * @private - * @param {Object} pgsz // { width, height } + * @param {PdfSize} pgsz * @param {number} angle * @param {SignAreaInfo} visinf * @return {SignAreaInfo} */ calcAreaInf(pgsz, angle, visinf){ - var ret = Object.assign({}, visinf); + var ret = /** @type {SignAreaInfo} */(Object.assign({}, visinf)); // Calculate position after rotate switch(angle){ case 90: @@ -688,6 +729,7 @@ z.VisualSignature = class{ * @return {Array} */ calcRect(angle, areainf){ + /** @type {Array} */ var rect = [0, 0, 0, 0]; rect[0] = areainf.x; rect[1] = areainf.y; @@ -719,9 +761,10 @@ z.VisualSignature = class{ * @private * @param {PDFLib.Rotation} rot * @param {SignAreaInfo} areainf // { x, y, w, h } - * @return {Object} // { x, y, width, height, rotate, xSkew, ySkew } + * @return {PdfDrawimgOption} */ calcDrawImgInf(rot, areainf){ + /** @type {PdfDrawimgOption} */ var ret = { "x": 0, "y": 0,