Skip to content

Commit

Permalink
v0.3.24: small fix (#171)
Browse files Browse the repository at this point in the history
  • Loading branch information
taichunmin authored Nov 15, 2024
2 parents 630c9a9 + 14294e7 commit 36d680a
Show file tree
Hide file tree
Showing 10 changed files with 77 additions and 47 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"module": "./dist/index.mjs",
"name": "chameleon-ultra.js",
"type": "commonjs",
"version": "0.3.23",
"version": "0.3.24",
"bugs": {
"url": "https://github.com/taichunmin/chameleon-ultra.js/issues"
},
Expand All @@ -20,7 +20,7 @@
}
],
"dependencies": {
"@taichunmin/buffer": "^0.13.10",
"@taichunmin/buffer": "^0.13.11",
"@taichunmin/crc": "^0.0.14",
"debug": "^4.3.7",
"jszip": "^3.10.1",
Expand Down
12 changes: 3 additions & 9 deletions pug/src/lf-em410x.pug
Original file line number Diff line number Diff line change
Expand Up @@ -148,12 +148,10 @@ block script
if (!await this.confirm(msg1, 'Yes', 'Cancel')) return
this.showLoading({ text: 'Emulating tag...' })
await ultra.cmdChangeDeviceMode(DeviceMode.TAG)
await ultra.cmdSlotChangeTagType(slot, TagType.EM410X)
await ultra.cmdSlotResetTagType(slot, TagType.EM410X)
await ultra.cmdSlotSetEnable(slot, FreqType.LF, true)
await ultra.slotChangeTagTypeAndActive(slot, null, TagType.EM410X) // reset slot
if (_.isString(name) && name.length > 0) await ultra.cmdSlotSetFreqName(slot, FreqType.LF, name)
await ultra.cmdSlotSetActive(slot)
await ultra.cmdEm410xSetEmuId(uid)
await ultra.cmdSlotSaveSettings()
await Swal.fire({ icon: 'success', title: 'Emulate success' })
} catch (err) {
ultra.emitter.emit('error', err)
Expand All @@ -170,11 +168,7 @@ block script
if (!await this.confirm(msg1, 'Yes', 'Cancel')) return
this.showLoading({ text: 'Resetting...' })
await ultra.cmdChangeDeviceMode(DeviceMode.TAG)
// reset slot
await ultra.cmdSlotChangeTagType(slot, TagType.EM410X)
await ultra.cmdSlotResetTagType(slot, TagType.EM410X)
await ultra.cmdSlotSetEnable(slot, FreqType.LF, true)
await ultra.cmdSlotSetActive(slot)
await ultra.slotChangeTagTypeAndActive(slot, null, TagType.EM410X) // reset slot
await Swal.fire({ icon: 'success', title: 'Reset success' })
} catch (err) {
ultra.emitter.emit('error', err)
Expand Down
6 changes: 1 addition & 5 deletions pug/src/mfkey32.pug
Original file line number Diff line number Diff line change
Expand Up @@ -191,11 +191,7 @@ block script
if (!await this.confirm(msg1, 'Yes', 'Cancel')) return
this.showLoading({ text: 'Emulating tag...' })
await ultra.cmdChangeDeviceMode(DeviceMode.TAG)
// reset slot
await ultra.cmdSlotChangeTagType(slot, tagType ? TagType.MIFARE_4096 : TagType.MIFARE_1024)
await ultra.cmdSlotResetTagType(slot, tagType ? TagType.MIFARE_4096 : TagType.MIFARE_1024)
await ultra.cmdSlotSetEnable(slot, FreqType.HF, true)
await ultra.cmdSlotSetActive(slot)
await ultra.slotChangeTagTypeAndActive(slot, tagType ? TagType.MIFARE_4096 : TagType.MIFARE_1024) // reset slot
// set anti-coll
const tag = {
atqa: Buffer.from(atqa, 'hex').reverse(),
Expand Down
6 changes: 1 addition & 5 deletions pug/src/mifare-keychain.pug
Original file line number Diff line number Diff line change
Expand Up @@ -457,11 +457,7 @@ block script
sak: Buffer.from(tag.sak, 'hex'),
uid: Buffer.from(tag.uid, 'hex').slice(-4),
}
// reset slot
await ultra.cmdSlotChangeTagType(slot, tag.tagType)
await ultra.cmdSlotResetTagType(slot, tag.tagType)
await ultra.cmdSlotSetEnable(slot, FreqType.HF, true)
await ultra.cmdSlotSetActive(slot)
await ultra.slotChangeTagTypeAndActive(slot, tag.tagType) // reset slot
// set emulated tag
await ultra.cmdMf1SetAntiCollMode(false)
await ultra.cmdHf14aSetAntiCollData(anticoll)
Expand Down
6 changes: 1 addition & 5 deletions pug/src/mifare-xiaomi.pug
Original file line number Diff line number Diff line change
Expand Up @@ -339,11 +339,7 @@ block script
if (!await this.confirm(msg1, 'Yes', 'Cancel')) return
this.showLoading({ text: 'Emulating tag...' })
await ultra.cmdChangeDeviceMode(DeviceMode.TAG)
// reset slot
await ultra.cmdSlotChangeTagType(slot, TagType.MIFARE_1024)
await ultra.cmdSlotResetTagType(slot, TagType.MIFARE_1024)
await ultra.cmdSlotSetEnable(slot, FreqType.HF, true)
await ultra.cmdSlotSetActive(slot)
await ultra.slotChangeTagTypeAndActive(slot, TagType.MIFARE_1024) // reset slot
await ultra.cmdMf1SetAntiCollMode(true)
await ultra.cmdMf1EmuWriteBlock(0, dump.subarray(0, 16)) // set block0
await ultra.cmdSlotSaveSettings()
Expand Down
12 changes: 3 additions & 9 deletions pug/src/mifare1k.pug
Original file line number Diff line number Diff line change
Expand Up @@ -271,11 +271,8 @@ block script
if (!await this.confirm(msg1, 'Yes', 'Cancel')) return
this.showLoading({ text: 'Emulating tag...' })
await ultra.cmdChangeDeviceMode(DeviceMode.TAG)
await ultra.cmdSlotChangeTagType(slot, TagType.MIFARE_1024)
await ultra.cmdSlotResetTagType(slot, TagType.MIFARE_1024)
await ultra.cmdSlotSetEnable(slot, FreqType.HF, true)
await ultra.slotChangeTagTypeAndActive(slot, TagType.MIFARE_1024) // reset slot
if (_.isString(name) && name.length > 0) await ultra.cmdSlotSetFreqName(slot, FreqType.HF, name)
await ultra.cmdSlotSetActive(slot)
await ultra.cmdMf1SetAntiCollMode(antiColl)
await ultra.cmdMf1SetDetectionEnable(detection)
await ultra.cmdMf1SetGen1aMode(gen1a)
Expand All @@ -292,6 +289,7 @@ block script
const sectorData = Buffer.from(dump[i], 'hex')
await ultra.cmdMf1EmuWriteBlock(i << 2, sectorData)
}
await this.cmdSlotSaveSettings()
await Swal.fire({ icon: 'success', title: 'Emulate success' })
} catch (err) {
ultra.emitter.emit('error', err)
Expand All @@ -308,11 +306,7 @@ block script
if (!await this.confirm(msg1, 'Yes', 'Cancel')) return
this.showLoading({ text: 'Resetting...' })
await ultra.cmdChangeDeviceMode(DeviceMode.TAG)
// reset slot
await ultra.cmdSlotChangeTagType(slot, TagType.MIFARE_1024)
await ultra.cmdSlotResetTagType(slot, TagType.MIFARE_1024)
await ultra.cmdSlotSetEnable(slot, FreqType.HF, true)
await ultra.cmdSlotSetActive(slot)
await ultra.slotChangeTagTypeAndActive(slot, TagType.MIFARE_1024) // reset slot
await Swal.fire({ icon: 'success', title: 'Reset success' })
} catch (err) {
ultra.emitter.emit('error', err)
Expand Down
47 changes: 42 additions & 5 deletions src/ChameleonUltra.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {
type DeviceModel,
type DfuFwId,
type DfuFwType,
type FreqType,
type Mf1EmuWriteMode,
type Mf1PrngType,
type Mf1VblockOperator,
Expand All @@ -21,6 +20,7 @@ import {
DfuObjType,
DfuOp,
DfuResCode,
FreqType,
isAnimationMode,
isButtonAction,
isButtonType,
Expand Down Expand Up @@ -367,7 +367,7 @@ export class ChameleonUltra {
if (!this.isConnected()) await this.connect()
const frame = this.isDfu() ? new DfuFrame(buf) : new UltraFrame(buf)
if (!(frame instanceof DfuFrame) || frame.op !== DfuOp.OBJECT_WRITE) this.#debug('send', frame.inspect)
const writer = (this.port?.writable as any)?.getWriter()
const writer = this.port?.writable?.getWriter()
if (_.isNil(writer)) throw new Error('Failed to getWriter(). Did you remember to use adapter plugin?')
await writer.write(buf)
writer.releaseLock()
Expand Down Expand Up @@ -623,6 +623,43 @@ export class ChameleonUltra {
await readResp()
}

/**
* Helper function to change slot to tagType, reset to default tagType data, enable slot, save settings and set active slot.
* @param slot - The target slot.
* @param hfTagType - The hf tagType to be change. If `null`, the hf of slot will be skip.
* @param lfTagType - The lf tagType to be change. If `null`, the lf of slot will be skip.
* @group Slot Related
* @example
* ```js
* async function run (ultra) {
* const { Slot, TagType } = await import('https://cdn.jsdelivr.net/npm/chameleon-ultra.js@0/+esm')
* await ultra.slotChangeTagTypeAndActive(Slot.SLOT_1, TagType.MIFARE_1024, TagType.EM410X)
* }
*
* await run(vm.ultra) // you can run in DevTools of https://taichunmin.idv.tw/chameleon-ultra.js/test.html
* ```
*/
async slotChangeTagTypeAndActive (slot: Slot, hfTagType?: TagType | null, lfTagType?: TagType | null): Promise<void> {
if (!isSlot(slot)) throw new TypeError('Invalid slot')

if (!_.isNil(hfTagType)) {
if (!isTagType(hfTagType) || hfTagType <= TagType.LF_END) throw new TypeError(`Invalid hfTagType = ${hfTagType}`)
await this.cmdSlotChangeTagType(slot, hfTagType)
await this.cmdSlotResetTagType(slot, hfTagType)
await this.cmdSlotSetEnable(slot, FreqType.HF, true)
}

if (!_.isNil(lfTagType)) {
if (!isTagType(lfTagType) || lfTagType > TagType.LF_END) throw new TypeError(`Invalid lfTagType = ${lfTagType}`)
await this.cmdSlotChangeTagType(slot, lfTagType)
await this.cmdSlotResetTagType(slot, lfTagType)
await this.cmdSlotSetEnable(slot, FreqType.LF, true)
}

await this.cmdSlotSaveSettings()
await this.cmdSlotSetActive(slot)
}

/**
* Set the nickname of specified freq type in specified slot.
* @param slot - The slot to be set.
Expand Down Expand Up @@ -4084,7 +4121,7 @@ export class ChameleonUltra {
const uploaded = await this.cmdDfuSelectObject(type)
this.#debug('core', `uploaded = ${JSON.stringify(uploaded)}`)
let buf1 = buf.subarray(0, uploaded.offset)
let crc1 = { offset: buf1.length, crc32: crc32(buf1 as any) }
let crc1 = { offset: buf1.length, crc32: crc32(buf1) }
let crcFailCnt = 0
if (!_.isMatch(uploaded, crc1)) { // abort
this.#debug('core', 'aborted')
Expand All @@ -4099,7 +4136,7 @@ export class ChameleonUltra {
// write object
await this.port.dfuWriteObject(buf1, mtu)
// check crc
const crc2 = { offset: uploaded.offset + buf1.length, crc32: crc32(buf1 as any, uploaded.crc32) }
const crc2 = { offset: uploaded.offset + buf1.length, crc32: crc32(buf1, uploaded.crc32) }
crc1 = await this.cmdDfuGetObjectCrc()
if (!_.isMatch(crc1, crc2)) {
crcFailCnt++
Expand Down Expand Up @@ -4341,7 +4378,7 @@ function mfuCheckRespNakCrc16a (resp: Buffer): Buffer {
if (resp.length === 1 && resp[0] !== 0x0A) throw createErr(RespStatus.HF_ERR_STAT, `received NAK 0x${toUpperHex(resp)}`)
if (resp.length < 3) throw createErr(RespStatus.HF_ERR_CRC, 'unexpected resp')
const data = resp.subarray(0, -2)
if (crc16a(data as any) !== resp.readUInt16LE(data.length)) throw createErr(RespStatus.HF_ERR_CRC, 'invalid crc16a of resp')
if (crc16a(data) !== resp.readUInt16LE(data.length)) throw createErr(RespStatus.HF_ERR_CRC, 'invalid crc16a of resp')
return data
}

Expand Down
17 changes: 17 additions & 0 deletions src/Crypto1.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -270,3 +270,20 @@ test.each([
const actual = await Crypto1.darkside(fnAcquire, fnCheckKey)
expect(actual.toString('hex')).toEqual(opts.expected)
})

test.each([
{ uid: '65535D33', key: '974C262B9278', nt: 'BE2B7B5D', nrEnc: 'B1E1B891', arEnc: '2CF7A248' },
{ uid: '65535D33', key: 'A9AC67832330', nt: '2C198BE4', nrEnc: 'FEDAC6D2', arEnc: 'CF0A3C7E' },
{ uid: '65535D33', key: 'A9AC67832330', nt: 'F73E638F', nrEnc: '4F4F867A', arEnc: '18CCB40B' },
] as const)('tag send nt and use key to verify nrEnc and arEnc', async ({ uid, key, nt, nrEnc, arEnc }) => {
const tag: any = {}
_.merge(tag, _.mapValues({ uid, nt, nrEnc, arEnc }, hex => Buffer.from(hex, 'hex').readUInt32BE(0)))
tag.state = new Crypto1()
tag.state.setLfsr(Buffer.from(key, 'hex').readUIntBE(0, 6))
tag.ks0 = tag.state.lfsrWord(tag.uid ^ tag.nt, 0)
tag.ks1 = tag.state.lfsrWord(tag.nrEnc, 1)
tag.ks2 = tag.state.lfsrWord(0, 0)
tag.ar = (tag.ks2 ^ tag.arEnc) >>> 0
const expected = Crypto1.prngSuccessor(tag.nt, 64)
expect(tag.ar).toEqual(expected)
})
6 changes: 3 additions & 3 deletions src/plugin/WebserialAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,12 @@ export default class WebserialAdapter implements ChameleonPlugin {
ultra.port = {
isOpen: () => this.#isOpen,
isDfu: () => this.#isDfu,
readable: this.port.readable.pipeThrough(new this.#TransformStream(new SlipDecodeTransformer(Buffer1)) as any),
readable: this.port.readable.pipeThrough(new this.#TransformStream(new SlipDecodeTransformer(Buffer1))),
writable: new this.#WritableStream({
write: async (chunk: Buffer) => {
const writer = this.port?.writable?.getWriter()
if (_.isNil(writer)) throw new Error('Failed to getWriter(). Did you remember to use adapter plugin?')
await writer.write(slipEncode(chunk, Buffer1) as any)
await writer.write(slipEncode(chunk, Buffer1))
writer.releaseLock()
},
}),
Expand All @@ -99,7 +99,7 @@ export default class WebserialAdapter implements ChameleonPlugin {
chunk[0] = DfuOp.OBJECT_WRITE
}
chunk.set(buf1, 1)
await writer.write(slipEncode(chunk, Buffer1) as any)
await writer.write(slipEncode(chunk, Buffer1))
}
writer.releaseLock()
},
Expand Down
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1180,10 +1180,10 @@
dependencies:
"@sinonjs/commons" "^3.0.0"

"@taichunmin/buffer@^0.13.10":
version "0.13.10"
resolved "https://registry.yarnpkg.com/@taichunmin/buffer/-/buffer-0.13.10.tgz#e7d14d65a7c8549b49bf93fc0969b25a7009ce68"
integrity sha512-wFounwBY+jt/QUA9CmO+jMfQnSYUtoiRATWkpgvIOGAKwKLZmkLGq0x9avOjVq75QPse5Ph5YBYotv2a+DawKQ==
"@taichunmin/buffer@^0.13.11":
version "0.13.11"
resolved "https://registry.yarnpkg.com/@taichunmin/buffer/-/buffer-0.13.11.tgz#ac99d3ce9d51cc4aeba982e423750607e4a0e358"
integrity sha512-DnBciBfadZLXPruXq32uTJR7fbt4jftlp9mqf1cbIQJRAKyYKkO29GQsCu9IO8HiR12k8A0lkYeihhWqw7xPfQ==
dependencies:
lodash "^4.17.21"

Expand Down

0 comments on commit 36d680a

Please sign in to comment.