Skip to content

Commit

Permalink
port: fix failing test + support extended token (#8)
Browse files Browse the repository at this point in the history
  • Loading branch information
beesaferoot authored Jul 8, 2024
1 parent 4fba179 commit 9c8f91f
Show file tree
Hide file tree
Showing 4 changed files with 173 additions and 21 deletions.
54 changes: 52 additions & 2 deletions src/encoder.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

const TokenTypes = require('./constants').TokenTypes
const shared = require('./token').OpenPAYGOTokenShared
const sharedExtended = require('./token').OpenPAYGOTokenSharedExtended

class OpenPAYGOTokenEncoder {
constructor() {}
Expand Down Expand Up @@ -29,7 +30,10 @@ class OpenPAYGOTokenEncoder {
throw new Error("'value' argument is undefined.")
}
value = Math.round(value * valueDivider)
if (value > shared.MAX_ACTIVATION_VALUE) {
const maxValue = extendToken
? sharedExtended.MAX_ACTIVATION_VALUE_EXTENDED
: shared.MAX_ACTIVATION_VALUE
if (value > maxValue) {
throw new Error('The value provided is too high.')
}
} else if (value !== undefined) {
Expand All @@ -44,6 +48,16 @@ class OpenPAYGOTokenEncoder {
}
}

if (extendToken) {
return this.generateExtendedToken({
startingCode: startingCode,
key: secretKeyHex,
count: count,
value: value,
mode: tokenType,
restrictDigitSet: restrictDigitSet,
})
}
return this.generateStandardToken({
startingCode: startingCode,
key: secretKeyHex,
Expand Down Expand Up @@ -73,7 +87,10 @@ class OpenPAYGOTokenEncoder {
let finalToken = shared.putBaseInToken(currentToken, tokenBase)

if (restrictDigitSet) {
finalToken = shared.convertToNdigitToken(finalToken)
finalToken = shared.convertToNdigitToken(
finalToken,
15
)
finalToken = String(finalToken).padStart(15, '0')
} else {
finalToken = String(finalToken).padStart(9, '0')
Expand All @@ -84,6 +101,39 @@ class OpenPAYGOTokenEncoder {
}
}

generateExtendedToken({
startingCode = undefined,
key = undefined,
value = undefined,
count = undefined,
mode = TokenTypes.ADD_TIME,
restrictDigitSet = false,
}) {
const startingBaseCode = sharedExtended.getTokenBase(startingCode)
const tokenBase = this.encodeBase(startingBaseCode, value)
let currentToken = sharedExtended.putBaseInToken(
startingCode,
tokenBase
)
const newCount = this.getNewCount(count, mode)

for (let i = 0; i < newCount - 1; i++) {
currentToken = sharedExtended.genNextToken(currentToken, key)
}
let finalToken = sharedExtended.putBaseInToken(currentToken, tokenBase)

if (restrictDigitSet) {
finalToken = sharedExtended.convertToNdigitToken(finalToken)
finalToken = String(finalToken).padStart(20, '0')
} else {
finalToken = String(finalToken).padStart(12, '0')
}
return {
newCount,
finalToken,
}
}

encodeBase(baseCode, value) {
if (value + baseCode > 999) {
return value + baseCode - 1000
Expand Down
133 changes: 115 additions & 18 deletions src/token.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,32 +70,104 @@ class OpenPAYGOTokenShared {
}

static convertFrom4digitToken(token) {
// 4 - digit token value
let bitArray = []
for (let digit of token.toString()) {
digit = Number(digit) - 1
const tempArr = bitArrayFromInt(digit, 2)
bitArray = bitArray.concat(tempArr)
}
return bitArrayToInt(bitArray)
return convertFrom4digitToken(token)
}

static genStartingCode(key) {
const hash = this.genHash({ key: key, msg: key })
return this.convertHash2Token(hash)
}

static convertToNdigitToken(token, digit = 4) {
// token should be a number data type
let restrictedDigitToken = ''
let bitArray = bitArrayFromInt(token, digit * 2)
static convertToNdigitToken(token, digit = 15) {
return convertToNdigitToken(token, digit)
}

static genHash({ key, msg }) {
let buf
if (typeof key === 'object') {
buf = key
} else {
buf = bigintConversion.hexToBuf(key)
}
const arrayBuffer = buf.buffer.slice(
buf.byteOffset,
buf.byteOffset + buf.byteLength
)
const uint32Array = new Uint32Array(arrayBuffer)
const hash = siphash24.hash_hex(uint32Array, msg)

return hash
}
}

class OpenPAYGOTokenSharedExtended {
static MAX_BASE_EXTENDED = 999999
static MAX_ACTIVATION_VALUE_EXTENDED = 999999
static TOKEN_VALUE_OFFSET_EXTENDED = 1000000

for (let i = 0; i < digit; i++) {
restrictedDigitToken += bitArrayToInt(
bitArray.slice(i * 2, i * 2 + 2)
).toString()
constructor() {}

static getTokenBase(code) {
return code % this.TOKEN_VALUE_OFFSET_EXTENDED
}

static putBaseInToken(token, tokenBase) {
if (tokenBase > this.MAX_BASE_EXTENDED) {
throw new Error('Invalid token base value')
}
return Number(restrictedDigitToken)
return token - this.getTokenBase(token) + tokenBase
}

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

// Duplicate the buffer by concatenating it with itself
const duplicatedToken = Buffer.concat([conformedToken, conformedToken])
let hash = this.genHash({
key: key,
msg: duplicatedToken,
asByte: true,
})

return this.convertHash2Token(hash)
}

static convertHash2Token(hash) {
// convert hash from hex

const hashBuffer = bigintConversion.hexToBuf(hash)

const dView = new DataView(
hashBuffer.buffer,
hashBuffer.byteOffset,
hashBuffer.byteLength
)

// 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)
return token
}

static convertTo29_5_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
return temp
}

static convertFrom4digitToken(token) {
return convertFrom4digitToken(token)
}

static convertToNdigitToken(token, digit = 20) {
return convertToNdigitToken(token, digit)
}

static genHash({ key, msg }) {
Expand All @@ -119,7 +191,7 @@ class OpenPAYGOTokenShared {
function bitArrayFromInt(number, bitLength) {
let bitArray = []
for (let i = 0; i < bitLength; i++) {
bitArray.push(number & (1 << (bitLength - 1 - i)))
bitArray.push((number & (1 << (bitLength - 1 - i))) !== 0)
}
return bitArray
}
Expand All @@ -132,6 +204,31 @@ function bitArrayToInt(bit_array) {
return num
}

function convertFrom4digitToken(token) {
// 4 - digit token value
let bitArray = []
for (let digit of token.toString()) {
digit = Number(digit) - 1
const tempArr = bitArrayFromInt(digit, 2)
bitArray = bitArray.concat(tempArr)
}
return bitArrayToInt(bitArray)
}

function convertToNdigitToken(token, digit = 4) {
// token should be a number data type
let restrictedDigitToken = ''
let 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)
}

module.exports = {
OpenPAYGOTokenShared: OpenPAYGOTokenShared,
OpenPAYGOTokenSharedExtended: OpenPAYGOTokenSharedExtended,
}
2 changes: 1 addition & 1 deletion test/encoder.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ describe('OpenPAYGOTokenEncoder test', () => {

sample.forEach((s) => {
const data = s

try {
const { finalToken } = encoder.generateToken({
tokenType: data.token_type,
Expand All @@ -16,6 +15,7 @@ describe('OpenPAYGOTokenEncoder test', () => {
startingCode: data.starting_code,
restrictDigitSet: data.restricted_digit_set,
value: data.value_raw,
extendToken: false,
})

expect(finalToken).toBe(data.token)
Expand Down
5 changes: 5 additions & 0 deletions test/token.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,9 @@ describe('OpenPAYGOTokenShared test', () => {
)
expect(newToken).toBe(117642353)
})

test('OpenPAYGOTokenShared convertToNdigitToken', () => {
const token = 854849256
expect(tokenLib.convertToNdigitToken(token, 15)).toBe(413441444234331)
})
})

0 comments on commit 9c8f91f

Please sign in to comment.