diff --git a/README.md b/README.md index ca8e2f0..0cdc069 100644 --- a/README.md +++ b/README.md @@ -35,12 +35,12 @@ This repository contains the **JavaScript** implementions of different OpenPAYGO Server-side tasks include -- generating OpenPAYGO tokens +- generating OpenPAYGO tokens (normal and extended) - decoding OpenPAYGO metrics payloads Device side tasks -- decoding OpenPAYGO tokens +- decoding OpenPAYGO tokens (normal and extended) - generating OpenPAYGO metrics payloads ## Installation diff --git a/src/encoder.js b/src/encoder.js index 7c9f795..435ba80 100644 --- a/src/encoder.js +++ b/src/encoder.js @@ -84,6 +84,7 @@ class OpenPAYGOTokenEncoder { for (let i = 0; i < newCount - 1; i++) { currentToken = shared.genNextToken(currentToken, key) } + let finalToken = shared.putBaseInToken(currentToken, tokenBase) if (restrictDigitSet) { @@ -107,11 +108,12 @@ class OpenPAYGOTokenEncoder { restrictDigitSet = false, }) { const startingBaseCode = sharedExtended.getTokenBase(startingCode) - const tokenBase = this.#encodeBase(startingBaseCode, value) + const tokenBase = this.#encodeBaseExtended(startingBaseCode, value) + let currentToken = sharedExtended.putBaseInToken(startingCode, tokenBase) const newCount = this.#getNewCount(count, mode) - for (let i = 0; i < newCount - 1; i++) { + for (let i = 0; i < newCount - 2; i++) { currentToken = sharedExtended.genNextToken(currentToken, key) } let finalToken = sharedExtended.putBaseInToken(currentToken, tokenBase) @@ -135,6 +137,15 @@ class OpenPAYGOTokenEncoder { return value + baseCode } + #encodeBaseExtended(baseCode, value) { + baseCode = BigInt(baseCode) + value = BigInt(value) + if (value + baseCode > 999999n) { + return value + baseCode - 1000000 + } + return value + baseCode + } + #getNewCount(count, mode) { let newCount const currCountOdd = count % 2 diff --git a/src/token.js b/src/token.js index ce85069..0771a20 100644 --- a/src/token.js +++ b/src/token.js @@ -34,7 +34,6 @@ class OpenPAYGOTokenShared { let hash = this.genHash({ key: key, msg: duplicatedToken, - asByte: true, }) return this.convertHash2Token(hash) @@ -98,16 +97,18 @@ class OpenPAYGOTokenShared { class OpenPAYGOTokenSharedExtended { static MAX_BASE_EXTENDED = 999999 - static MAX_ACTIVATION_VALUE_EXTENDED = 999999 - static TOKEN_VALUE_OFFSET_EXTENDED = 1000000 + static MAX_ACTIVATION_VALUE_EXTENDED = 999999n + static TOKEN_VALUE_OFFSET_EXTENDED = 1000000n constructor() {} static getTokenBase(code) { - return code % this.TOKEN_VALUE_OFFSET_EXTENDED + return BigInt(code) % this.TOKEN_VALUE_OFFSET_EXTENDED } static putBaseInToken(token, tokenBase) { + token = BigInt(token) + tokenBase = BigInt(tokenBase) if (tokenBase > this.MAX_BASE_EXTENDED) { throw new Error("Invalid token base value") } @@ -115,14 +116,13 @@ class OpenPAYGOTokenSharedExtended { } static genNextToken(prev_token, key) { - const conformedToken = Buffer.alloc(4) // Allocate buffer of 4 bytes - conformedToken.writeUInt32BE(prev_token, 0) // Write the integer value into buffer as big-endian + prev_token = BigInt(prev_token) + const conformedToken = Buffer.alloc(8) // Allocate buffer of 8 bytes + conformedToken.writeBigUInt64BE(prev_token, 0) // Write the integer value into buffer as big-endian - // Duplicate the buffer by concatenating it with itself - const duplicatedToken = Buffer.concat([conformedToken, conformedToken]) let hash = this.genHash({ key: key, - msg: duplicatedToken, + msg: conformedToken, asByte: true, }) @@ -130,27 +130,17 @@ class OpenPAYGOTokenSharedExtended { } static convertHash2Token(hash) { - // convert hash from hex - - const hashBuffer = bigintConversion.hexToBuf(hash, true) - - const dView = new DataView(hashBuffer) - - // take first 4 btypes - const hash_msb = dView.getUint32(0) // as big endian - // take last 4 bytes - const hash_lsb = dView.getUint32(4) // as big endian - - const hashUnit = (hash_msb ^ hash_lsb) >>> 0 - const token = this.convertTo29_5_bits(hashUnit) + const hashUint = bigintConversion.hexToBigint(hash) + const token = this.convertTo_40_bits(hashUint) return token } - static convertTo29_5_bits(hash_uint) { + static convertTo_40_bits(hash_uint) { // port from original lib - const mask = ((1 << (64 - 24 + 1)) - 1) << 24 - let temp = (hash_uint & mask) >>> 24 - if (temp > 999999999999) temp = temp - 99511627777 + // const mask = ((1 << (64 - 24 + 1)) - 1) << 24 + const mask = 36893488147402326016n + let temp = (hash_uint & mask) >> 24n + if (temp > 999999999999n) temp = temp - 99511627777n return temp } @@ -187,6 +177,14 @@ function bitArrayFromInt(number, bitLength) { return bitArray } +function bitArrayFromBigInt(number, bitLength) { + let bitArray = [] + for (let i = 0; i < bitLength; i++) { + bitArray.push((number & (1n << BigInt(bitLength - 1 - i))) !== 0n) + } + return bitArray +} + function bitArrayToInt(bit_array) { let num = 0 for (let bit of bit_array) { @@ -209,14 +207,20 @@ function convertFrom4digitToken(token) { function convertToNdigitToken(token, digit = 4) { // token should be a number data type let restrictedDigitToken = "" - let bitArray = bitArrayFromInt(token, digit * 2) + let bitArray + if (typeof token === "bigint") { + bitArray = bitArrayFromBigInt(token, digit * 2) + } else { + bitArray = bitArrayFromInt(token, digit * 2) + } for (let i = 0; i < digit; i++) { restrictedDigitToken += ( bitArrayToInt(bitArray.slice(i * 2, i * 2 + 2)) + 1 ).toString() } - return parseInt(restrictedDigitToken, 10) + + return restrictedDigitToken } module.exports = { diff --git a/test/encoder.test.js b/test/encoder.test.js index 33018f3..4218e15 100644 --- a/test/encoder.test.js +++ b/test/encoder.test.js @@ -1,10 +1,10 @@ const sample = require("./sample_tokens.json") const Encoder = require("../src/encoder").OpenPAYGOTokenEncoder +const shared = require("../src/token").OpenPAYGOTokenShared describe("OpenPAYGOTokenEncoder test", () => { test("generateToken", () => { const encoder = new Encoder() - sample.forEach((s) => { const data = s try { @@ -15,14 +15,12 @@ describe("OpenPAYGOTokenEncoder test", () => { startingCode: data.starting_code, restrictDigitSet: data.restricted_digit_set, value: data.value_raw, - extendToken: false, + extendToken: data.extended_token, }) - expect(finalToken).toBe(data.token) } catch (err) { - if (!err.message.includes("The value provided is too high.")) { - throw err - } + expect(data.extended_token).toBe(false) + expect(data.value_raw > shared.MAX_ACTIVATION_VALUE).toBe(true) } }) }) diff --git a/test/sample_tokens.json b/test/sample_tokens.json index 73a4a39..d5fa2de 100644 --- a/test/sample_tokens.json +++ b/test/sample_tokens.json @@ -7,8 +7,20 @@ "restricted_digit_set": false, "token_type": 1, "value_raw": 1, + "extended_token": false, "token": "380589011" }, + { + "serial_number": "TEST220000001", + "starting_code": 516959010, + "key": "bc41ec9530f6dac86b1a29ab82edc5fb", + "token_count": 4, + "restricted_digit_set": false, + "token_type": 1, + "value_raw": 1, + "extended_token": true, + "token": "584694959011" + }, { "serial_number": "TEST220000001", "starting_code": 516959010, @@ -17,8 +29,20 @@ "restricted_digit_set": false, "token_type": 1, "value_raw": 2, + "extended_token": false, "token": "283675012" }, + { + "serial_number": "TEST220000001", + "starting_code": 516959010, + "key": "bc41ec9530f6dac86b1a29ab82edc5fb", + "token_count": 6, + "restricted_digit_set": false, + "token_type": 1, + "value_raw": 2, + "extended_token": true, + "token": "093055959012" + }, { "serial_number": "TEST220000001", "starting_code": 516959010, @@ -27,8 +51,20 @@ "restricted_digit_set": false, "token_type": 1, "value_raw": 5, + "extended_token": false, "token": "034254015" }, + { + "serial_number": "TEST220000001", + "starting_code": 516959010, + "key": "bc41ec9530f6dac86b1a29ab82edc5fb", + "token_count": 8, + "restricted_digit_set": false, + "token_type": 1, + "value_raw": 5, + "extended_token": true, + "token": "158793959015" + }, { "serial_number": "TEST220000001", "starting_code": 516959010, @@ -37,8 +73,20 @@ "restricted_digit_set": false, "token_type": 1, "value_raw": 995, + "extended_token": false, "token": "409152005" }, + { + "serial_number": "TEST220000001", + "starting_code": 516959010, + "key": "bc41ec9530f6dac86b1a29ab82edc5fb", + "token_count": 10, + "restricted_digit_set": false, + "token_type": 1, + "value_raw": 995, + "extended_token": true, + "token": "346418960005" + }, { "serial_number": "TEST220000001", "starting_code": 516959010, @@ -47,8 +95,20 @@ "restricted_digit_set": false, "token_type": 1, "value_raw": 998, + "extended_token": false, "token": "071763008" }, + { + "serial_number": "TEST220000001", + "starting_code": 516959010, + "key": "bc41ec9530f6dac86b1a29ab82edc5fb", + "token_count": 12, + "restricted_digit_set": false, + "token_type": 1, + "value_raw": 998, + "extended_token": true, + "token": "738674960008" + }, { "serial_number": "TEST220000001", "starting_code": 516959010, @@ -57,6 +117,7 @@ "restricted_digit_set": false, "token_type": 1, "value_raw": 999, + "extended_token": false, "token": "814704009" }, { @@ -65,19 +126,32 @@ "key": "bc41ec9530f6dac86b1a29ab82edc5fb", "token_count": 14, "restricted_digit_set": false, + "token_type": 1, + "value_raw": 999, + "extended_token": true, + "token": "036504960009" + }, + { + "serial_number": "TEST220000001", + "starting_code": 516959010, + "key": "bc41ec9530f6dac86b1a29ab82edc5fb", + "token_count": 16, + "restricted_digit_set": false, "token_type": 2, "value_raw": 1, - "token": "141465011" + "extended_token": false, + "token": "084661011" }, { "serial_number": "TEST220000001", "starting_code": 516959010, "key": "bc41ec9530f6dac86b1a29ab82edc5fb", - "token_count": 16, + "token_count": 17, "restricted_digit_set": false, "token_type": 2, - "value_raw": 2, - "token": "448320012" + "value_raw": 1, + "extended_token": true, + "token": "342484959011" }, { "serial_number": "TEST220000001", @@ -86,8 +160,20 @@ "token_count": 18, "restricted_digit_set": false, "token_type": 2, - "value_raw": 5, - "token": "730651015" + "value_raw": 2, + "extended_token": false, + "token": "569243012" + }, + { + "serial_number": "TEST220000001", + "starting_code": 516959010, + "key": "bc41ec9530f6dac86b1a29ab82edc5fb", + "token_count": 19, + "restricted_digit_set": false, + "token_type": 2, + "value_raw": 2, + "extended_token": true, + "token": "033302959012" }, { "serial_number": "TEST220000001", @@ -96,8 +182,20 @@ "token_count": 20, "restricted_digit_set": false, "token_type": 2, - "value_raw": 995, - "token": "132820005" + "value_raw": 5, + "extended_token": false, + "token": "953870015" + }, + { + "serial_number": "TEST220000001", + "starting_code": 516959010, + "key": "bc41ec9530f6dac86b1a29ab82edc5fb", + "token_count": 21, + "restricted_digit_set": false, + "token_type": 2, + "value_raw": 5, + "extended_token": true, + "token": "858173959015" }, { "serial_number": "TEST220000001", @@ -106,8 +204,20 @@ "token_count": 22, "restricted_digit_set": false, "token_type": 2, - "value_raw": 998, - "token": "146345008" + "value_raw": 995, + "extended_token": false, + "token": "949090005" + }, + { + "serial_number": "TEST220000001", + "starting_code": 516959010, + "key": "bc41ec9530f6dac86b1a29ab82edc5fb", + "token_count": 23, + "restricted_digit_set": false, + "token_type": 2, + "value_raw": 995, + "extended_token": true, + "token": "398002960005" }, { "serial_number": "TEST220000001", @@ -116,8 +226,42 @@ "token_count": 24, "restricted_digit_set": false, "token_type": 2, + "value_raw": 998, + "extended_token": false, + "token": "574433008" + }, + { + "serial_number": "TEST220000001", + "starting_code": 516959010, + "key": "bc41ec9530f6dac86b1a29ab82edc5fb", + "token_count": 25, + "restricted_digit_set": false, + "token_type": 2, + "value_raw": 998, + "extended_token": true, + "token": "434362960008" + }, + { + "serial_number": "TEST220000001", + "starting_code": 516959010, + "key": "bc41ec9530f6dac86b1a29ab82edc5fb", + "token_count": 26, + "restricted_digit_set": false, + "token_type": 2, + "value_raw": 999, + "extended_token": false, + "token": "017663009" + }, + { + "serial_number": "TEST220000001", + "starting_code": 516959010, + "key": "bc41ec9530f6dac86b1a29ab82edc5fb", + "token_count": 27, + "restricted_digit_set": false, + "token_type": 2, "value_raw": 999, - "token": "386863009" + "extended_token": true, + "token": "998074960009" }, { "serial_number": "TEST240000002", @@ -127,8 +271,20 @@ "restricted_digit_set": true, "token_type": 1, "value_raw": 1, + "extended_token": false, "token": "413441444234331" }, + { + "serial_number": "TEST240000002", + "starting_code": 432435255, + "key": "dac86b1a29ab82edc5fbbc41ec9530f6", + "token_count": 4, + "restricted_digit_set": true, + "token_type": 1, + "value_raw": 1, + "extended_token": true, + "token": "12224323334223111431" + }, { "serial_number": "TEST240000002", "starting_code": 432435255, @@ -137,8 +293,20 @@ "restricted_digit_set": true, "token_type": 1, "value_raw": 2, + "extended_token": false, "token": "431131331113332" }, + { + "serial_number": "TEST240000002", + "starting_code": 432435255, + "key": "dac86b1a29ab82edc5fbbc41ec9530f6", + "token_count": 6, + "restricted_digit_set": true, + "token_type": 1, + "value_raw": 2, + "extended_token": true, + "token": "32413234412423321432" + }, { "serial_number": "TEST240000002", "starting_code": 432435255, @@ -147,8 +315,20 @@ "restricted_digit_set": true, "token_type": 1, "value_raw": 5, + "extended_token": false, "token": "423424444232241" }, + { + "serial_number": "TEST240000002", + "starting_code": 432435255, + "key": "dac86b1a29ab82edc5fbbc41ec9530f6", + "token_count": 8, + "restricted_digit_set": true, + "token_type": 1, + "value_raw": 5, + "extended_token": true, + "token": "42243244223242414441" + }, { "serial_number": "TEST240000002", "starting_code": 432435255, @@ -157,8 +337,20 @@ "restricted_digit_set": true, "token_type": 1, "value_raw": 995, + "extended_token": false, "token": "422313413112333" }, + { + "serial_number": "TEST240000002", + "starting_code": 432435255, + "key": "dac86b1a29ab82edc5fbbc41ec9530f6", + "token_count": 10, + "restricted_digit_set": true, + "token_type": 1, + "value_raw": 995, + "extended_token": true, + "token": "22321432323221313233" + }, { "serial_number": "TEST240000002", "starting_code": 432435255, @@ -167,8 +359,20 @@ "restricted_digit_set": true, "token_type": 1, "value_raw": 998, + "extended_token": false, "token": "231434142221342" }, + { + "serial_number": "TEST240000002", + "starting_code": 432435255, + "key": "dac86b1a29ab82edc5fbbc41ec9530f6", + "token_count": 12, + "restricted_digit_set": true, + "token_type": 1, + "value_raw": 998, + "extended_token": true, + "token": "21443212141334233242" + }, { "serial_number": "TEST240000002", "starting_code": 432435255, @@ -177,6 +381,7 @@ "restricted_digit_set": true, "token_type": 1, "value_raw": 999, + "extended_token": false, "token": "242313431134143" }, { @@ -185,19 +390,32 @@ "key": "dac86b1a29ab82edc5fbbc41ec9530f6", "token_count": 14, "restricted_digit_set": true, + "token_type": 1, + "value_raw": 999, + "extended_token": true, + "token": "13342341143431434243" + }, + { + "serial_number": "TEST240000002", + "starting_code": 432435255, + "key": "dac86b1a29ab82edc5fbbc41ec9530f6", + "token_count": 16, + "restricted_digit_set": true, "token_type": 2, "value_raw": 1, - "token": "113434333414311" + "extended_token": false, + "token": "322233323341431" }, { "serial_number": "TEST240000002", "starting_code": 432435255, "key": "dac86b1a29ab82edc5fbbc41ec9530f6", - "token_count": 16, + "token_count": 17, "restricted_digit_set": true, "token_type": 2, - "value_raw": 2, - "token": "414212121322332" + "value_raw": 1, + "extended_token": true, + "token": "43123241414334323431" }, { "serial_number": "TEST240000002", @@ -206,8 +424,20 @@ "token_count": 18, "restricted_digit_set": true, "token_type": 2, - "value_raw": 5, - "token": "413424224321241" + "value_raw": 2, + "extended_token": false, + "token": "314441232332312" + }, + { + "serial_number": "TEST240000002", + "starting_code": 432435255, + "key": "dac86b1a29ab82edc5fbbc41ec9530f6", + "token_count": 19, + "restricted_digit_set": true, + "token_type": 2, + "value_raw": 2, + "extended_token": true, + "token": "14211341114322122432" }, { "serial_number": "TEST240000002", @@ -216,8 +446,20 @@ "token_count": 20, "restricted_digit_set": true, "token_type": 2, - "value_raw": 995, - "token": "342124322343233" + "value_raw": 5, + "extended_token": false, + "token": "224414221443441" + }, + { + "serial_number": "TEST240000002", + "starting_code": 432435255, + "key": "dac86b1a29ab82edc5fbbc41ec9530f6", + "token_count": 21, + "restricted_digit_set": true, + "token_type": 2, + "value_raw": 5, + "extended_token": true, + "token": "42321323214121133441" }, { "serial_number": "TEST240000002", @@ -226,8 +468,20 @@ "token_count": 22, "restricted_digit_set": true, "token_type": 2, - "value_raw": 998, - "token": "211422314241142" + "value_raw": 995, + "extended_token": false, + "token": "234142213341113" + }, + { + "serial_number": "TEST240000002", + "starting_code": 432435255, + "key": "dac86b1a29ab82edc5fbbc41ec9530f6", + "token_count": 23, + "restricted_digit_set": true, + "token_type": 2, + "value_raw": 995, + "extended_token": true, + "token": "42424443333433421233" }, { "serial_number": "TEST240000002", @@ -236,7 +490,41 @@ "token_count": 24, "restricted_digit_set": true, "token_type": 2, + "value_raw": 998, + "extended_token": false, + "token": "433132413232242" + }, + { + "serial_number": "TEST240000002", + "starting_code": 432435255, + "key": "dac86b1a29ab82edc5fbbc41ec9530f6", + "token_count": 25, + "restricted_digit_set": true, + "token_type": 2, + "value_raw": 998, + "extended_token": true, + "token": "34423331124414212242" + }, + { + "serial_number": "TEST240000002", + "starting_code": 432435255, + "key": "dac86b1a29ab82edc5fbbc41ec9530f6", + "token_count": 26, + "restricted_digit_set": true, + "token_type": 2, + "value_raw": 999, + "extended_token": false, + "token": "243142412213343" + }, + { + "serial_number": "TEST240000002", + "starting_code": 432435255, + "key": "dac86b1a29ab82edc5fbbc41ec9530f6", + "token_count": 27, + "restricted_digit_set": true, + "token_type": 2, "value_raw": 999, - "token": "331233113332423" + "extended_token": true, + "token": "14341211233234443243" } ] diff --git a/test/token.test.js b/test/token.test.js index e047eba..285a9ba 100644 --- a/test/token.test.js +++ b/test/token.test.js @@ -32,6 +32,6 @@ describe("OpenPAYGOTokenShared test", () => { test("OpenPAYGOTokenShared convertToNdigitToken", () => { const token = 854849256 - expect(tokenLib.convertToNdigitToken(token, 15)).toBe(413441444234331) + expect(tokenLib.convertToNdigitToken(token, 15)).toBe("413441444234331") }) })