Skip to content

Commit

Permalink
v0.3.19: improve dfu and mfu enum rename (#149)
Browse files Browse the repository at this point in the history
  • Loading branch information
taichunmin authored Oct 30, 2024
2 parents 9d6808b + 3afcf2b commit 43bf522
Show file tree
Hide file tree
Showing 10 changed files with 986 additions and 579 deletions.
48 changes: 24 additions & 24 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.18",
"version": "0.3.19",
"bugs": {
"url": "https://github.com/taichunmin/chameleon-ultra.js/issues"
},
Expand All @@ -20,7 +20,7 @@
}
],
"dependencies": {
"@taichunmin/buffer": "^0.13.7",
"@taichunmin/buffer": "^0.13.9",
"@taichunmin/crc": "^0.0.14",
"debug": "^4.3.7",
"jszip": "^3.10.1",
Expand All @@ -34,10 +34,10 @@
"@types/debug": "^4.1.12",
"@types/finalhandler": "^1.2.3",
"@types/html-minifier": "^4.0.5",
"@types/jest": "^29.5.13",
"@types/jest": "^29.5.14",
"@types/livereload": "^0.9.5",
"@types/lodash": "^4.17.7",
"@types/node": "^22.5.5",
"@types/lodash": "^4.17.13",
"@types/node": "^22.8.4",
"@types/pug": "^2.0.10",
"@types/serve-static": "^1.15.7",
"@types/uglify-js": "^3.17.5",
Expand All @@ -52,9 +52,9 @@
"eslint": "^8.57.0",
"eslint-config-love": "^43",
"eslint-config-standard": "^17.1.0",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-local-rules": "^3.0.2",
"eslint-plugin-n": "^17.10.2",
"eslint-plugin-n": "^17.11.1",
"eslint-plugin-promise": "^7.1.0",
"eslint-plugin-pug": "^1.2.5",
"eslint-plugin-tsdoc": "^0.3.0",
Expand All @@ -72,13 +72,13 @@
"supports-color": "^9.4.0",
"ts-jest": "^29.2.5",
"ts-node": "^10.9.2",
"tsup": "^8.3.0",
"tsx": "^4.19.1",
"typedoc": "^0.26.7",
"typedoc-plugin-mdn-links": "^3.3.1",
"tsup": "^8.3.5",
"tsx": "^4.19.2",
"typedoc": "^0.26.10",
"typedoc-plugin-mdn-links": "^3.3.5",
"typedoc-plugin-missing-exports": "^3.0.0",
"typedoc-plugin-rename-defaults": "^0.7.1",
"typescript": "^5.6.2",
"typescript": "^5.6.3",
"utility-types": "^3.11.0"
},
"exports": {
Expand All @@ -88,54 +88,54 @@
"import": "./dist/index.d.mts",
"require": "./dist/index.d.ts"
},
"script": "./dist/index.global.js",
"import": "./dist/index.mjs",
"require": "./dist/index.js",
"script": "./dist/index.global.js"
"require": "./dist/index.js"
},
"./Crypto1": {
"types": {
"import": "./dist/Crypto1.d.mts",
"require": "./dist/Crypto1.d.ts"
},
"script": "./dist/Crypto1.global.js",
"import": "./dist/Crypto1.mjs",
"require": "./dist/Crypto1.js",
"script": "./dist/Crypto1.global.js"
"require": "./dist/Crypto1.js"
},
"./plugin/DfuZip": {
"types": {
"import": "./dist/plugin/DfuZip.d.mts",
"require": "./dist/plugin/DfuZip.d.ts"
},
"script": "./dist/plugin/DfuZip.global.js",
"import": "./dist/plugin/DfuZip.mjs",
"require": "./dist/plugin/DfuZip.js",
"script": "./dist/plugin/DfuZip.global.js"
"require": "./dist/plugin/DfuZip.js"
},
"./plugin/SerialPortAdapter": {
"types": {
"import": "./dist/plugin/SerialPortAdapter.d.mts",
"require": "./dist/plugin/SerialPortAdapter.d.ts"
},
"script": "./dist/plugin/SerialPortAdapter.global.js",
"import": "./dist/plugin/SerialPortAdapter.mjs",
"require": "./dist/plugin/SerialPortAdapter.js",
"script": "./dist/plugin/SerialPortAdapter.global.js"
"require": "./dist/plugin/SerialPortAdapter.js"
},
"./plugin/WebbleAdapter": {
"types": {
"import": "./dist/plugin/WebbleAdapter.d.mts",
"require": "./dist/plugin/WebbleAdapter.d.ts"
},
"script": "./dist/plugin/WebbleAdapter.global.js",
"import": "./dist/plugin/WebbleAdapter.mjs",
"require": "./dist/plugin/WebbleAdapter.js",
"script": "./dist/plugin/WebbleAdapter.global.js"
"require": "./dist/plugin/WebbleAdapter.js"
},
"./plugin/WebserialAdapter": {
"types": {
"import": "./dist/plugin/WebserialAdapter.d.mts",
"require": "./dist/plugin/WebserialAdapter.d.ts"
},
"script": "./dist/plugin/WebserialAdapter.global.js",
"import": "./dist/plugin/WebserialAdapter.mjs",
"require": "./dist/plugin/WebserialAdapter.js",
"script": "./dist/plugin/WebserialAdapter.global.js"
"require": "./dist/plugin/WebserialAdapter.js"
}
},
"keywords": [
Expand Down
1 change: 0 additions & 1 deletion pug/src/dfu.pug
Original file line number Diff line number Diff line change
Expand Up @@ -275,4 +275,3 @@ block script
},
},
})

10 changes: 5 additions & 5 deletions pug/src/mfkey32.pug
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ block script
uid: toHex(tag1.uid),
}
tag2.tagType = (tag2.atqa === '0002' && tag2.sak === '18') ? 1 : 0
console.log(tag2)
ultra.emitter.emit('debug', 'web', `tag2 = ${JSON.stringify(tag2)}`)
_.merge(this.ss, tag2)
Swal.close()
} catch (err) {
Expand All @@ -185,7 +185,7 @@ block script
const { ultra } = this
try {
const { atqa, sak, slot, tagType, uid } = this.ss
console.log({ atqa, sak, slot, tagType, uid })
ultra.emitter.emit('debug', 'web', JSON.stringify({ atqa, sak, slot, tagType, uid }))
const slotName = await ultra.cmdSlotGetFreqName(slot, FreqType.HF) ?? '(no name)'
const msg1 = `The hf data of slot ${slot + 1} "${slotName}" will be REPLACE! Continue?`
if (!await this.confirm(msg1, 'Yes', 'Cancel')) return
Expand All @@ -210,7 +210,7 @@ block script
block0[tag.uid.length] = tag.uid.xor() // bcc for magic card
block0[tag.uid.length + 1] = tag.sak[0] // sak
tag.atqa.copy(block0, tag.uid.length + 2) // atqa
console.log(`block0 = ${block0.toString('hex')}`)
ultra.emitter.emit('debug', 'web', `block0 = ${block0.toString('hex')}`)
await ultra.cmdMf1EmuWriteBlock(0, block0) // set block0
await ultra.cmdSlotSaveSettings()
await Swal.fire({ icon: 'success', title: 'Emulate successfully!' })
Expand Down Expand Up @@ -246,7 +246,7 @@ block script
text: `Retrive logs: ${logs.length} / ${logCnt}`,
}))
await this.sleep(0) // 等待 UI 更新
console.log(logs)
ultra.emitter.emit('debug', 'web', `logs = ${JSON.stringify(logs)}`)

// calc how many mfkey32v2 need to be run
const bufToHex = buf => buf?.toString('hex').toUpperCase()
Expand All @@ -272,7 +272,7 @@ block script
key: this.mfkey32v2(r0.uid, r0, r1),
}], _.isEqual))
} catch (err) {
console.log(err)
ultra.emitter.emit('error', err)
}
recoverCnt++
await this.sleep(0) // 等待 UI 更新
Expand Down
2 changes: 1 addition & 1 deletion pug/src/mifare1k.pug
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ block content
.col.px-1: button.btn.btn-block.btn-outline-primary(@click="btnExportDump") #[i.fa.fa-fw.fa-floppy-o] Export
button.btn.btn-sm.btn-block.btn-outline-danger.mb-2(@click="btnResetDump") #[i.fa.mr-1.fa-repeat] Reset to empty dump
.card-body.px-3.pt-3.pb-2.letter-spacing-n1px.border-top
h6.card-title Anit Collision
h6.card-title Anti Collision
.input-group.input-group-sm.mb-2.was-validated
.input-group-prepend: span.input-group-text.justify-content-center UID
input.form-control(pattern="[\\dA-Fa-f]{8}([\\dA-Fa-f]{6})?([\\dA-Fa-f]{6})?", maxlength="20", placeholder="Hex format of UID", required, v-model="ss.uid")
Expand Down
80 changes: 29 additions & 51 deletions src/ChameleonUltra.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ import {
isValidFreqType,
Mf1KeyType,
MfuCmd,
MfuTagType,
MfuVerToMfuTagType,
NxpMfuType,
MfuVerToNxpMfuType,
RespStatus,
TagType,
} from './enums'
Expand Down Expand Up @@ -296,15 +296,6 @@ export class ChameleonUltra {
return this?.port?.isDfu?.() ?? false
}

/**
* Return true if DFU use slip encode/decode.
* @group DFU Related
* @see Please refer to [SLIP](https://en.wikipedia.org/wiki/Serial_Line_Internet_Protocol) and [nRF5 SDK: SLIP library](https://docs.nordicsemi.com/bundle/sdk_nrf5_v17.1.0/page/lib_slip.html) for more information.
*/
isSlip (): boolean {
return this?.port?.isSlip?.() ?? false
}

/**
* Send a buffer to device.
* @param buf - The buffer to be sent to device.
Expand Down Expand Up @@ -2939,15 +2930,15 @@ export class ChameleonUltra {
* @example
* ```js
* async function run (ultra) {
* const { MfuTagTypeName } = await import('https://cdn.jsdelivr.net/npm/chameleon-ultra.js@0/+esm')
* const MfuTagType = await ultra.mfuDetectTagType()
* console.log(`tagType = ${MfuTagTypeName.get(MfuTagType)}`)
* const { NxpMfuTypeName } = await import('https://cdn.jsdelivr.net/npm/chameleon-ultra.js@0/+esm')
* const NxpMfuType = await ultra.mfuDetectTagType()
* console.log(`tagType = ${NxpMfuTypeName.get(NxpMfuType)}`)
* }
*
* await run(vm.ultra) // you can run in DevTools of https://taichunmin.idv.tw/chameleon-ultra.js/test.html
* ```
*/
async mfuDetectTagType (): Promise<MfuTagType> {
async mfuDetectTagType (): Promise<NxpMfuType> {
const timeout = 500
const tags = await this.cmdHf14aScan()
if (tags.length > 1) throw new Error('More than one tag detected.')
Expand All @@ -2958,11 +2949,11 @@ export class ChameleonUltra {

if (tag.uid[0] === 0x05) { // Infinition MY-D tests, Exam high nibble
const nib = tag.uid[1] >>> 4
if (nib === 1) return MfuTagType.MY_D
else if (nib === 2) return MfuTagType.MY_D_NFC
else if (nib === 3) return MfuTagType.MY_D_MOVE
else if (nib === 7) return MfuTagType.MY_D_MOVE_LEAN
else return MfuTagType.UNKNOWN
if (nib === 1) return NxpMfuType.MY_D
else if (nib === 2) return NxpMfuType.MY_D_NFC
else if (nib === 3) return NxpMfuType.MY_D_MOVE
else if (nib === 7) return NxpMfuType.MY_D_MOVE_LEAN
else return NxpMfuType.UNKNOWN
}

// try GET_VERSION cmd
Expand All @@ -2977,16 +2968,16 @@ export class ChameleonUltra {

if (Buffer.isBuffer(ver1)) {
if (ver1.length === 10) {
let tagType: MfuTagType | undefined
tagType = MfuVerToMfuTagType.get(toUpperHex(ver1.subarray(0, 8)))
let tagType: NxpMfuType | undefined
tagType = MfuVerToNxpMfuType.get(toUpperHex(ver1.subarray(0, 8)))
if (!_.isNil(tagType)) return tagType
tagType = MfuVerToMfuTagType.get(toUpperHex(ver1.subarray(0, 7)))
tagType = MfuVerToNxpMfuType.get(toUpperHex(ver1.subarray(0, 7)))
if (!_.isNil(tagType)) return tagType
if (ver1[2] === 0x04) return MfuTagType.NTAG
if (ver1[2] === 0x03) return MfuTagType.UL_EV1
} else if (ver1.length === 1) return MfuTagType.UL_C
else if (ver1.length === 0) return MfuTagType.UL
else return MfuTagType.UNKNOWN
if (ver1[2] === 0x04) return NxpMfuType.NTAG
if (ver1[2] === 0x03) return NxpMfuType.UL_EV1
} else if (ver1.length === 1) return NxpMfuType.UL_C
else if (ver1.length === 0) return NxpMfuType.UL
else return NxpMfuType.UNKNOWN
}

// try TDES_AUTH cmd (should has resp if it is a Ultralight-C)
Expand All @@ -2997,19 +2988,19 @@ export class ChameleonUltra {
timeout,
}).catch(err => { this.#emitErr(err) })
// console.log(`auth1 = ${auth1?.toString('hex')}`)
if (Buffer.isBuffer(auth1)) return MfuTagType.UL_C
if (Buffer.isBuffer(auth1)) return NxpMfuType.UL_C

// try read page 0x26 (should error if it is a Ultralight)
const read1 = await this.mfuReadPages({ start: 0x26, timeout }).catch(err => { this.#emitErr(err) })
// console.log(`read1 = ${read1?.toString('hex')}`)
if ((read1?.length ?? 0) === 0) return MfuTagType.UL
if ((read1?.length ?? 0) === 0) return NxpMfuType.UL

// try read page 0x30 (should error if it is a ntag203)
const read2 = await this.mfuReadPages({ start: 0x30, timeout }).catch(err => { this.#emitErr(err) })
// console.log(`read2 = ${read2?.toString('hex')}`)
if ((read2?.length ?? 0) === 0) return MfuTagType.NTAG_203
if ((read2?.length ?? 0) === 0) return NxpMfuType.NTAG_203

return MfuTagType.UNKNOWN
return NxpMfuType.UNKNOWN
}

/**
Expand Down Expand Up @@ -3793,28 +3784,14 @@ export class ChameleonUltra {
* await run(vm.ultra) // you can run in DevTools of https://taichunmin.idv.tw/chameleon-ultra.js/test.html
* ```
*/
async cmdDfuGetMtu (): Promise<number> {
async cmdDfuGetMtu (): Promise<number | undefined> {
if (!this.isConnected()) await this.connect()
if (!this.isDfu()) throw new Error('Please enter DFU mode first.')
const op = DfuOp.MTU_GET
const readResp = await this.#createReadRespFn({ op })
await this.#sendBuffer(Buffer.pack('<B', op))
const respData = (await readResp()).data
const mtu = respData.length >= 2 ? respData.readUInt16LE(0) : 21
return this.isSlip() ? Math.trunc((mtu - 1) / 2) : mtu // slip encode/decode
}

/**
* Write selected object.
* @param buf - Data.
* @group DFU Related
* @see Please refer to {@link https://docs.nordicsemi.com/bundle/sdk_nrf5_v17.1.0/page/lib_dfu_transport.html | nRF5 SDK: DFU Protocol} for more infomation.
*/
async cmdDfuWriteObject (buf: Buffer): Promise<void> {
if (!this.isConnected()) await this.connect()
if (!this.isDfu()) throw new Error('Please enter DFU mode first.')
const op = DfuOp.OBJECT_WRITE
await this.#sendBuffer(Buffer.pack(`<B${buf.length}s`, op, buf))
return respData.length < 2 ? undefined : respData.readUInt16LE(0)
}

/**
Expand Down Expand Up @@ -3956,6 +3933,7 @@ export class ChameleonUltra {
*/
async dfuUpdateObject (type: DfuObjType, buf: Buffer): Promise<void> {
if (!isValidDfuObjType(type)) throw new TypeError('Invalid type')
if (_.isNil(this.port?.dfuWriteObject)) throw new Error('this.port.dfuWriteObject is not implemented')
const emitProgress = (offset: number): void => {
this.emitter.emit('progress', {
func: 'dfuUpdateObject',
Expand All @@ -3975,12 +3953,12 @@ export class ChameleonUltra {
Object.assign(uploaded, await this.cmdDfuSelectObject(type))
}
emitProgress(uploaded.offset)
const mtu = await this.cmdDfuGetMtu() - 1
const mtu = await this.cmdDfuGetMtu()
while (uploaded.offset < buf.length) {
buf1 = buf.subarray(uploaded.offset).subarray(0, uploaded.maxSize)
await this.cmdDfuCreateObject(type, buf1.length)
// write object
for (const buf2 of buf1.chunk(mtu)) await this.cmdDfuWriteObject(buf2)
await this.port.dfuWriteObject(buf1, mtu)
// check crc
const crc2 = { offset: uploaded.offset + buf1.length, crc32: crc32(buf1, uploaded.crc32) }
crc1 = await this.cmdDfuGetObjectCrc()
Expand Down Expand Up @@ -4204,9 +4182,9 @@ class DfuRxSink implements UnderlyingSink<Buffer> {
}

export interface ChameleonSerialPort<I extends Uint8Array = Uint8Array, O extends Uint8Array = Uint8Array> {
dfuWriteObject?: (buf: Buffer, mtu?: number) => Promise<void>
isDfu?: () => boolean
isOpen?: () => boolean
isSlip?: () => boolean
readable: ReadableStream<I> | null
writable: WritableStream<O> | null
}
Expand Down
Loading

0 comments on commit 43bf522

Please sign in to comment.