diff --git a/.gitignore b/.gitignore index 3270fe4d..0ad5cf73 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ !src/**/build/ src/jsTest/generated src/jsMain/generated +webpack.config.d/ diff --git a/README.md b/README.md index 43aa412b..2535f445 100644 --- a/README.md +++ b/README.md @@ -96,13 +96,21 @@ Sample data objects are provided in `SampleData`, with special thanks to Christi ## Usage (JS) The build result of this library for JS is a module in UMD format, located under `build/distributions/hcert-kotlin.js`. This script runs in a web browser environment and can be used in the following way (see [demo.html](demo.html)). +In addition, we also (experimentally) support node as target environment (also based on a bundled UMD) by passing the `node` flag to gradle (see the [sample node project](node-demo)). -Build the module either for development or production: +Build the module either for development or production for a **browser** target: ``` ./gradlew jsBrowserDevelopmentWebpack ./gradlew jsBrowserProductionWebpack ``` +Build the module either for development or production (**NodeJS** target): +``` +./gradlew -Pnode jsBrowserDevelopmentWebpack +./gradlew -Pnode jsBrowserProductionWebpack +``` + + To verify a single QR code content: ```JavaScript @@ -613,7 +621,7 @@ See these links for details: ## Changelog Version NEXT: - - Fix constructors and overloads fro Java callers + - Fix constructors and overloads for Java callers - Update dependencies: - Common: - Kotlin: 1.5.31 @@ -632,7 +640,13 @@ Version NEXT: - node-inspect-extracted: 1.0.8 - ajv (JSON schema validator): 8.6.3 - ajv-formats: 2.1.1 - - JS: Switch to upstream cose-js 0.7.0 (deprecates forked version) + - JS: + - Switch to upstream cose-js 0.7.0 (deprecates forked version) + - Fix deprecated calls to `Buffer` constructor (possibly not all calls yet) + - Switch intermediate (=node) output from CommonJS to UMD + - Experimental NodeJS support + - Enable outputting a bundled js module (UMD) targeting NodeJS if desired + - the Gradle [npm-publish](https://github.com/mpetuska/npm-publish) plugin does not work as desired Version 1.3.2: - Export `SignedDataDownloader` to JS diff --git a/build.gradle.kts b/build.gradle.kts index 135416dd..873d4da3 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -104,13 +104,18 @@ kotlin { } } webpackTask { + val currentTemplate = if (project.hasProperty("node")) "node" else "browser" + val templateSrc = "${projectDir.absolutePath}/webpack-templates/patch-$currentTemplate.js" + + //we want to overwrite, else we could end up in a messy state if we switch profiles + File(templateSrc).copyTo(File("${projectDir.absolutePath}/webpack.config.d/patch.js"), overwrite = true) output.library = "hcert" output.libraryTarget = "umd" } } binaries.executable() - useCommonJs() } + sourceSets { val commonShared by creating { sourceSets { kotlin.srcDir(customSrcDirs.commonShared) } @@ -153,31 +158,41 @@ kotlin { dependsOn(jvmDataGenMain) } - val jsMain by getting { + val jsNode by creating { sourceSets { kotlin.srcDir(customSrcDirs.jsMainGenerated) } dependencies { implementation(npm("pako", Versions.js.pako)) implementation(npm("pkijs", Versions.js.pkijs)) implementation(npm("cose-js", Versions.js.cose, generateExternals = false)) + implementation(npm("cbor", Versions.js.cbor)) + implementation(npm("fast-sha256", Versions.js.sha256, generateExternals = true)) + implementation(npm("bignumber.js", Versions.js.bignumber)) + implementation(npm("elliptic", Versions.js.elliptic)) + implementation(npm("node-rsa", Versions.js.rsa)) + implementation(npm("ajv", Versions.js.ajv)) + implementation(npm("ajv-formats", Versions.js.`ajv-formats`)) + implementation(npm("@nuintun/qrcode", Versions.js.qrcode)) + } + } + + val jsBrowser by creating { + dependencies { implementation(npm("crypto-browserify", Versions.js.`crypto-browserify`)) implementation(npm("stream-browserify", Versions.js.`stream-browserify`)) implementation(npm("util", Versions.js.util)) implementation(npm("buffer", Versions.js.buffer)) implementation(npm("process", Versions.js.process)) - implementation(npm("cbor", Versions.js.cbor)) implementation(npm("node-inspect-extracted", Versions.js.`node-inspect-extracted`)) - implementation(npm("bignumber.js", Versions.js.bignumber)) - implementation(npm("fast-sha256", Versions.js.sha256, generateExternals = true)) implementation(npm("url", Versions.js.url)) - implementation(npm("elliptic", Versions.js.elliptic)) - implementation(npm("node-rsa", Versions.js.rsa)) implementation(npm("constants-browserify", Versions.js.`constants-browserify`)) implementation(npm("assert", Versions.js.assert)) - implementation(npm("ajv", Versions.js.ajv)) - implementation(npm("ajv-formats", Versions.js.`ajv-formats`)) - implementation(npm("@nuintun/qrcode", Versions.js.qrcode)) } } + val jsMain by getting { + dependsOn(jsNode) + if (!project.hasProperty("node")) dependsOn(jsBrowser) + } + val jsTest by getting { sourceSets { kotlin.srcDir(customSrcDirs.jsTestGenerated) } } @@ -221,12 +236,12 @@ tasks { val baseFile = File(basePath) baseFile.walkBottomUp().filter { !it.isDirectory }.filterNot { it.extension == "png" } .filterNot { it.extension == "jpg" }.forEach { - val encodeBase64 = - de.undercouch.gradle.tasks.download.org.apache.commons.codec.binary.Base64.encodeBase64(it.readBytes()) - val key = it.absolutePath.substring(baseFile.absolutePath.length + 1) - val safeKey = key.replace("\$", "\\\$").replace("\\", "/") - w.write("m[\"$safeKey\"] = \"${String(encodeBase64)}\"\n") - } + val encodeBase64 = + de.undercouch.gradle.tasks.download.org.apache.commons.codec.binary.Base64.encodeBase64(it.readBytes()) + val key = it.absolutePath.substring(baseFile.absolutePath.length + 1) + val safeKey = key.replace("\$", "\\\$").replace("\\", "/") + w.write("m[\"$safeKey\"] = \"${String(encodeBase64)}\"\n") + } w.write("}\noverride fun get(key:String) = m[key]\n") w.write("override fun allResourceNames() = m.keys.sorted()\n}") } @@ -275,4 +290,4 @@ publishing { } } } -} +} \ No newline at end of file diff --git a/node-demo/index.js b/node-demo/index.js new file mode 100644 index 00000000..603b40e2 --- /dev/null +++ b/node-demo/index.js @@ -0,0 +1,22 @@ +const hcert = require("../build/distributions/hcert-node") + +const qr = "HC1:NCFTW2H:7*I06R3W/J:O6:P4QB3+7RKFVJWV66UBCE//UXDT:*ML-4D.NBXR+SRHMNIY6EB8I595+6UY9-+0DPIO6C5%0SBHN-OWKCJ6BLC2M.M/NPKZ4F3WNHEIE6IO26LB8:F4:JVUGVY8*EKCLQ..QCSTS+F$:0PON:.MND4Z0I9:GU.LBJQ7/2IJPR:PAJFO80NN0TRO1IB:44:N2336-:KC6M*2N*41C42CA5KCD555O/A46F6ST1JJ9D0:.MMLH2/G9A7ZX4DCL*010LGDFI$MUD82QXSVH6R.CLIL:T4Q3129HXB8WZI8RASDE1LL9:9NQDC/O3X3G+A:2U5VP:IE+EMG40R53CG9J3JE1KB KJA5*$4GW54%LJBIWKE*HBX+4MNEIAD$3NR E228Z9SS4E R3HUMH3J%-B6DRO3T7GJBU6O URY858P0TR8MDJ$6VL8+7B5$G CIKIPS2CPVDK%K6+N0GUG+TG+RB5JGOU55HXDR.TL-N75Y0NHQTZ3XNQMTF/ZHYBQ$8IR9MIQHOSV%9K5-7%ZQ/.15I0*-J8AVD0N0/0USH.3" + +const pemCert = + "-----BEGIN CERTIFICATE-----\n" + + "MIIBvTCCAWOgAwIBAgIKAXk8i88OleLsuTAKBggqhkjOPQQDAjA2MRYwFAYDVQQD\n" + + "DA1BVCBER0MgQ1NDQSAxMQswCQYDVQQGEwJBVDEPMA0GA1UECgwGQk1TR1BLMB4X\n" + + "DTIxMDUwNTEyNDEwNloXDTIzMDUwNTEyNDEwNlowPTERMA8GA1UEAwwIQVQgRFND\n" + + "IDExCzAJBgNVBAYTAkFUMQ8wDQYDVQQKDAZCTVNHUEsxCjAIBgNVBAUTATEwWTAT\n" + + "BgcqhkjOPQIBBggqhkjOPQMBBwNCAASt1Vz1rRuW1HqObUE9MDe7RzIk1gq4XW5G\n" + + "TyHuHTj5cFEn2Rge37+hINfCZZcozpwQKdyaporPUP1TE7UWl0F3o1IwUDAOBgNV\n" + + "HQ8BAf8EBAMCB4AwHQYDVR0OBBYEFO49y1ISb6cvXshLcp8UUp9VoGLQMB8GA1Ud\n" + + "IwQYMBaAFP7JKEOflGEvef2iMdtopsetwGGeMAoGCCqGSM49BAMCA0gAMEUCIQDG\n" + + "2opotWG8tJXN84ZZqT6wUBz9KF8D+z9NukYvnUEQ3QIgdBLFSTSiDt0UJaDF6St2\n" + + "bkUQuVHW6fQbONd731/M4nc=\n" + + "-----END CERTIFICATE-----" + +const verifier = new hcert.VerifierDirect([pemCert]); + + +console.debug(verifier.verify(qr)) \ No newline at end of file diff --git a/node-demo/package.json b/node-demo/package.json new file mode 100644 index 00000000..1e023baf --- /dev/null +++ b/node-demo/package.json @@ -0,0 +1,12 @@ +{ + "name": "nodetest", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC" +} diff --git a/src/jsMain/kotlin/ehn/techiop/hcert/kotlin/chain/Extensions.kt b/src/jsMain/kotlin/ehn/techiop/hcert/kotlin/chain/Extensions.kt index 63b65a25..d3f9495e 100644 --- a/src/jsMain/kotlin/ehn/techiop/hcert/kotlin/chain/Extensions.kt +++ b/src/jsMain/kotlin/ehn/techiop/hcert/kotlin/chain/Extensions.kt @@ -31,7 +31,7 @@ fun ByteArray.toUint8ClampedArray(): Uint8ClampedArray { return Uint8ClampedArray(toTypedArray()) } -fun ByteArray.toBuffer(): Buffer = Buffer(toUint8Array()) +fun ByteArray.toBuffer(): Buffer = Buffer.from(toUint8Array()) fun Uint8Array.toByteArray(): ByteArray { return ByteArray(this.length) { this[it] } diff --git a/src/jsMain/kotlin/external/crypto/index.module_bn.js.kt b/src/jsMain/kotlin/external/crypto/index.module_bn.js.kt index 461c8b81..a675bd0f 100644 --- a/src/jsMain/kotlin/external/crypto/index.module_bn.js.kt +++ b/src/jsMain/kotlin/external/crypto/index.module_bn.js.kt @@ -12,7 +12,7 @@ external interface `T$2` { var b: BN var gcd: BN } - +@JsNonModule @JsModule("bn.js") open external class BN { constructor(number: Number, base: Number = definedExternally, endian: String = definedExternally) diff --git a/src/jsMain/kotlin/external/crypto/index.module_node-rsa.kt b/src/jsMain/kotlin/external/crypto/index.module_node-rsa.kt index 3d3ba68d..be6b5a98 100644 --- a/src/jsMain/kotlin/external/crypto/index.module_node-rsa.kt +++ b/src/jsMain/kotlin/external/crypto/index.module_node-rsa.kt @@ -4,7 +4,7 @@ "RETURN_TYPE_MISMATCH_ON_OVERRIDE", "CONFLICTING_OVERLOADS" ) - +@JsNonModule @JsModule("node-rsa") open external class NodeRSA { constructor(key: KeyBits = definedExternally) diff --git a/webpack.config.d/patch.js b/webpack-templates/patch-browser.js similarity index 100% rename from webpack.config.d/patch.js rename to webpack-templates/patch-browser.js diff --git a/webpack-templates/patch-node.js b/webpack-templates/patch-node.js new file mode 100644 index 00000000..145c19c1 --- /dev/null +++ b/webpack-templates/patch-node.js @@ -0,0 +1,7 @@ +config.target="node" +config.output.filename="hcert-node.js" +config.performance = { + maxEntrypointSize: 512000 * 5, + maxAssetSize: 512000 * 5 +} +config.resolve.fallback = {"node-inspect-extracted":false} \ No newline at end of file