diff --git a/pass/app/Crypto.js b/pass/app/Crypto.js index bb56135fec7cf063b5d3906b12209e1e2faa5ac7..ad70e2fefbe2d8865704728f1087d96c3888130c 100644 --- a/pass/app/Crypto.js +++ b/pass/app/Crypto.js @@ -1,6 +1,9 @@ const config = require("config"); const { pki, random } = require("node-forge"); const crypto = require("crypto"); +const BlindSignature = require("blind-signatures"); +var BigInteger = require("jsbn").BigInteger; +const NodeRSA = require("node-rsa"); class Crypto { #dayjs; @@ -17,24 +20,21 @@ class Crypto { } private_key_get_current() { - let seed_date = this.#dayjs().format(this.#dayjs_format); + let seed_date = this.#dayjs(); return this.#private_key_get(seed_date); } private_key_get_last() { - let seed_date = this.#dayjs() - .subtract(1, "month") - .format(this.#dayjs_format); + let seed_date = this.#dayjs().subtract(1, "month"); return this.#private_key_get(seed_date); } async #private_key_get(seed_date) { - let private_key = {}; - - let cache_key = "private_keys" + seed_date; + let cache_key = "private_key_" + seed_date.format(this.#dayjs_format); // Check if the private key already exists in cache let cached_key = await this.#redis.get(cache_key); + let private_key = new NodeRSA(); if (cached_key === null) { let seed = config.get("crypto.private_key.seed") + seed_date; @@ -43,31 +43,45 @@ class Crypto { prng_instance.seedFileSync = () => seed; let keypair = pki.rsa.generateKeyPair({ - bits: 4096, + bits: config.get("crypto.private_key.bit_length"), prng: prng_instance, workers: -1, }); + let private_key_pem = pki + .privateKeyToPem(keypair.privateKey) + .trim() + .replace(/\r\n/g, "\n"); - private_key = { - n: keypair.privateKey.n.toString(), - e: keypair.privateKey.e.toString(), - d: keypair.privateKey.d.toString(), - p: keypair.privateKey.p.toString(), - q: keypair.privateKey.q.toString(), - dmp1: keypair.privateKey.dP.toString(), - dmq1: keypair.privateKey.dQ.toString(), - coeff: keypair.privateKey.qInv.toString(), - }; + private_key = private_key.importKey( + Buffer.from(private_key_pem, "utf8"), + "private" + ); // Store the key in cache - await this.#redis.set(cache_key, JSON.stringify(private_key)); + await this.#redis.set(cache_key, private_key.exportKey("pkcs8")); } else { - private_key = JSON.parse(await this.#redis.get(cache_key)); + let private_key_data = await this.#redis.get(cache_key); + private_key = private_key.importKey(private_key_data, "pkcs8"); } - return private_key; } + async sign(encrypted_sales_receipts, order_date) { + let private_key = await this.#private_key_get(order_date); + console.log(private_key); + let signed_encrypted_sales_receipts = []; + + for (let i = 0; i < encrypted_sales_receipts.length; i++) { + signed_encrypted_sales_receipts.push( + BlindSignature.sign({ + blinded: new BigInteger(encrypted_sales_receipts[i]), + key: private_key, + }).toString() + ); + } + return signed_encrypted_sales_receipts; + } + /** * Creates an hmac hash for purchase data so we can check it later */ @@ -84,8 +98,8 @@ class Crypto { amount: parseInt(amount), unit_size: parseInt(unit_size), price_per_unit: parseFloat(price_per_unit), - public_key_n: public_key_n, - public_key_e: public_key_e, + public_key_n: new String(public_key_n), + public_key_e: new String(public_key_e), }); let forge = require("node-forge"); let hmac = forge.hmac.create(); @@ -111,8 +125,8 @@ class Crypto { amount: parseInt(amount), unit_size: parseInt(unit_size), price_per_unit: parseFloat(price_per_unit), - public_key_n: public_key_n, - public_key_e: public_key_e, + public_key_n: new String(public_key_n), + public_key_e: new String(public_key_e), }); let forge = require("node-forge"); let hmac = forge.hmac.create(); diff --git a/pass/app/Order.js b/pass/app/Order.js index b6ac4ec33d18afc07e532953342b619d45ab8e2f..bbe4afd192cfce8684928525db16f043c9abdfe1 100644 --- a/pass/app/Order.js +++ b/pass/app/Order.js @@ -1,4 +1,5 @@ const config = require("config"); +const Crypto = require("./Crypto"); const dayjs = require("dayjs"); class Order { @@ -34,12 +35,14 @@ class Order { #unit_size; #price_per_unit; #encrypted_sales_receipts; + #signatures; #payment_completed; #payment_method_link; // Stores a link to an entry of the payment methods payment i.e. PayPal order id /** * Data populated by context */ + #order_date; #create_mode; #redis_client; @@ -48,14 +51,23 @@ class Order { amount, unit_size, price_per_unit, - encrypted_sales_receipts + encrypted_sales_receipts, + signatures ) { - this.#order_id = parseInt(order_id); + this.#order_id = new String(order_id); + this.#order_date = dayjs.unix(this.#order_id.substr(0, 10)); + this.#amount = parseInt(amount); this.#unit_size = parseInt(unit_size); this.#price_per_unit = parseFloat(price_per_unit); this.#encrypted_sales_receipts = encrypted_sales_receipts; + if (signatures) { + this.#signatures = signatures; + } else { + this.#signatures = []; + } + this.#payment_completed = false; this.#create_mode = true; @@ -81,6 +93,10 @@ class Order { return this.#payment_method_link; } + getSignatures() { + return this.#signatures; + } + isPaymentComplete() { return this.#payment_completed; } @@ -89,6 +105,16 @@ class Order { this.#payment_method_link = payment_method_link; } + async signOrder() { + let mgcrypto = new Crypto(); + + let signed_encrypted_sales_receipts = mgcrypto.sign( + this.#encrypted_sales_receipts, + this.#order_date + ); + this.#signatures = await signed_encrypted_sales_receipts; + } + static async LOAD_ORDER_FROM_ID(order_id) { return new Promise((resolve, reject) => { let Redis = require("ioredis"); @@ -106,7 +132,8 @@ class Order { order_data.amount, order_data.unit_size, order_data.price_per_unit, - order_data.encrypted_sales_receipts + JSON.parse(order_data.encrypted_sales_receipts), + JSON.parse(order_data.signatures) ); if (order_data.payment_method_link) { loaded_order.setPaymentMethodLink( @@ -140,7 +167,10 @@ class Order { amount: this.#amount, unit_size: this.#unit_size, price_per_unit: this.#price_per_unit, - encrypted_sales_receipts: this.#encrypted_sales_receipts, + encrypted_sales_receipts: JSON.stringify( + this.#encrypted_sales_receipts + ), + signatures: JSON.stringify(this.#signatures), payment_completed: this.#payment_completed, payment_method_link: JSON.stringify(this.#payment_method_link), }) diff --git a/pass/resources/js/checkout_paypal.js b/pass/resources/js/checkout_paypal.js index cec426ddfdf36a51a8925a3e8a6a311242673f85..91b0eb51aa3256d3e32d4144b2d104466467c04c 100644 --- a/pass/resources/js/checkout_paypal.js +++ b/pass/resources/js/checkout_paypal.js @@ -70,7 +70,7 @@ function execute_payment_paypal(encrypted_sales_receipts) { .then((orderData) => { let paymentEvent = new CustomEvent("payment-complete", { detail: { - orderData, + signatures: orderData.signatures, }, bubbles: true, cancelable: true, diff --git a/pass/routes/checkout/checkout.js b/pass/routes/checkout/checkout.js index 229098e0f192d6de9bc9cac73740458e9d89ed47..1ae76b2602283696e88e5d131f1390e334325c37 100644 --- a/pass/routes/checkout/checkout.js +++ b/pass/routes/checkout/checkout.js @@ -35,8 +35,8 @@ router.get( let crypto = new Crypto(); let private_key = await crypto.private_key_get_current(); params.crypto = { - N: private_key.n, - E: private_key.e, + N: private_key.keyPair.n, + E: private_key.keyPair.e, }; // Generate hmac hash of the payment data so we are able to verify them when the client submits them again @@ -87,6 +87,7 @@ router.use( req.body.public_key_e ) ) { + console.log("Invalid integrity"); return Promise.reject("Integrity is not matching"); } return true; diff --git a/pass/routes/checkout/paypal.js b/pass/routes/checkout/paypal.js index 1cde1f444f24be975081c6115c3cbbcc4faff0a6..79270524e32b1c24d9f8f5ae84507f4786bd8f76 100644 --- a/pass/routes/checkout/paypal.js +++ b/pass/routes/checkout/paypal.js @@ -18,6 +18,7 @@ router.post("/", async (req, res, next) => { req.body.price_per_unit, req.body.encrypted_sales_receipts ); + order .save() .then(() => { @@ -38,10 +39,30 @@ router.post("/", async (req, res, next) => { // capture payment & store order information or fullfill order router.post("/capture", async (req, res) => { Order.LOAD_ORDER_FROM_ID(req.body.order_id).then((loaded_order) => { - let paypal_order_id = loaded_order.getPaymentMethodLink().id; - capturePayment(paypal_order_id).then((captureData) => - res.json(captureData) - ); + loaded_order + .signOrder() + .then(() => { + let paypal_order_id = loaded_order.getPaymentMethodLink().id; + capturePayment(paypal_order_id) + .then((captureData) => { + loaded_order + .save() + .then(() => { + captureData.signatures = loaded_order.getSignatures(); + res.json(captureData); + }) + .catch((error) => { + res.status(400).json({ errors: [{ msg: error }] }); + }); + }) + .catch((error) => { + res.status(400).json({ errors: [{ msg: error }] }); + }); + }) + .catch((error) => { + res.status(400).json({ errors: [{ msg: error }] }); + }); + //res.json(captureData); // TODO: store payment information such as the transaction ID });