diff --git a/.eslintrc.cjs b/.eslintrc.cjs
index 598be3b..ffd2deb 100644
--- a/.eslintrc.cjs
+++ b/.eslintrc.cjs
@@ -36,5 +36,8 @@ module.exports = {
imports: 'always-multiline',
objects: 'always-multiline',
}],
+ '@typescript-eslint/unbound-method': ['error', {
+ ignoreStatic: true,
+ }]
},
}
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index d025473..2ab9c6d 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -30,10 +30,10 @@ jobs:
echo 'secrets=${{ toJSON(secrets) }}'
echo 'steps=${{ toJSON(steps) }}'
echo 'vars=${{ toJSON(vars) }}'
- - uses: actions/checkout@v3
- - uses: actions/setup-node@v3
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
with:
- node-version: 18
+ node-version: 20
cache: 'yarn'
- name: install, lint, test, build
run: |
@@ -51,12 +51,11 @@ jobs:
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
- - uses: actions/setup-node@v3
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
with:
- node-version: 18
+ node-version: 20
cache: 'yarn'
- - uses: actions/configure-pages@v3
- name: install and build
run: |
set -ex
@@ -64,17 +63,18 @@ jobs:
yarn test:ci
yarn build
- name: Coveralls
- uses: coverallsapp/github-action@master
+ uses: coverallsapp/github-action@v2
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
+ - uses: actions/configure-pages@v4
- name: Upload artifact
- uses: actions/upload-pages-artifact@v2
+ uses: actions/upload-pages-artifact@v3
with:
path: './dist'
- name: Deploy to GitHub Pages
id: deployment
- uses: actions/deploy-pages@v2
+ uses: actions/deploy-pages@v4
- name: 發布至 npm
- uses: JS-DevTools/npm-publish@v1
+ uses: JS-DevTools/npm-publish@v3
with:
token: ${{ secrets.NPM_TOKEN }}
diff --git a/.github/workflows/npm-test.yml b/.github/workflows/npm-test.yml
index d11b54d..06fc2a2 100644
--- a/.github/workflows/npm-test.yml
+++ b/.github/workflows/npm-test.yml
@@ -6,11 +6,11 @@ on:
jobs:
test:
runs-on: ubuntu-latest
- container: node:18
+ container: node:20
steps:
- uses: actions/setup-node@v3
with:
- node-version: 18
+ node-version: 20
- name: install and npm test
run: |
set -ex
diff --git a/.npmignore b/.npmignore
index 213c999..9e77bdb 100644
--- a/.npmignore
+++ b/.npmignore
@@ -150,6 +150,7 @@ out
/.rollup.cache
/*.ts
/dist/docs
+/jest.config.cjs
/pages
/pug
/src/example
diff --git a/package.json b/package.json
index 783dcb8..35cf69d 100644
--- a/package.json
+++ b/package.json
@@ -10,7 +10,7 @@
"repository": "git@github.com:taichunmin/chameleon-ultra.js.git",
"type": "module",
"unpkg": "dist/iife/index.min.js",
- "version": "0.2.18",
+ "version": "0.2.19",
"bugs": {
"url": "https://github.com/taichunmin/chameleon-ultra.js/issues"
},
@@ -32,20 +32,20 @@
"@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-terser": "^0.4.4",
- "@rollup/plugin-typescript": "^11.1.5",
- "@tsconfig/node-lts": "^20.1.0",
+ "@rollup/plugin-typescript": "^11.1.6",
+ "@tsconfig/node-lts": "^20.1.1",
"@types/debug": "^4.1.12",
"@types/finalhandler": "^1.2.3",
"@types/html-minifier": "^4.0.5",
"@types/jest": "^29.5.11",
"@types/livereload": "^0.9.5",
"@types/lodash": "^4.14.202",
- "@types/node": "^20.10.5",
+ "@types/node": "^20.11.4",
"@types/pug": "^2.0.10",
"@types/serve-static": "^1.15.5",
"@types/uglify-js": "^3.17.4",
"@types/web-bluetooth": "^0.0.20",
- "@typescript-eslint/eslint-plugin": "^6.15.0",
+ "@typescript-eslint/eslint-plugin": "^6.19.0",
"chokidar": "^3.5.3",
"concurrently": "^8.2.2",
"dayjs": "^1.11.10",
@@ -53,7 +53,7 @@
"eslint": "^8.56.0",
"eslint-config-standard-with-typescript": "^43.0.0",
"eslint-plugin-import": "^2.29.1",
- "eslint-plugin-n": "^16.5.0",
+ "eslint-plugin-n": "^16.6.2",
"eslint-plugin-promise": "^6.1.1",
"eslint-plugin-pug": "^1.2.5",
"finalhandler": "^1.2.0",
@@ -65,7 +65,7 @@
"nodemon": "^3.0.2",
"pug": "^3.0.2",
"rimraf": "^5.0.5",
- "rollup": "^4.9.1",
+ "rollup": "^4.9.5",
"rollup-plugin-dts": "^6.1.0",
"rollup-plugin-polyfill-node": "^0.13.0",
"rollup-plugin-version-injector": "^1.3.3",
@@ -74,10 +74,11 @@
"ts-jest": "^29.1.1",
"tslib": "^2.6.2",
"tsx": "^4.7.0",
- "typedoc": "^0.25.4",
- "typedoc-plugin-mdn-links": "^3.1.8",
- "typedoc-plugin-missing-exports": "^2.1.0",
+ "typedoc": "^0.25.7",
+ "typedoc-plugin-mdn-links": "^3.1.12",
+ "typedoc-plugin-missing-exports": "^2.2.0",
"typedoc-plugin-rename-defaults": "^0.7.0",
+ "typedoc-plugin-zod": "^1.1.2",
"typescript": "^5.3.3",
"uglify-js": "^3.17.4",
"utility-types": "^3.10.0"
diff --git a/pug/src/mifare1k.pug b/pug/src/mifare1k.pug
index 7c1889c..7b1930d 100644
--- a/pug/src/mifare1k.pug
+++ b/pug/src/mifare1k.pug
@@ -86,7 +86,7 @@ block content
.row.mx-n1.mb-2
.col.px-1: button.btn.btn-block.btn-outline-success(@click="btnGen2Read") #[i.fa.fa-fw.fa-upload] Read
.col.px-1: button.btn.btn-block.btn-outline-primary(@click="btnGen2Write") #[i.fa.fa-fw.fa-download] Write
- button.btn.btn-sm.btn-block.btn-outline-info.mb-2(@click="btnKeysGrab") #[i.fa.mr-1.fa-key] Grab keys from Mifare Data
+ button.btn.btn-sm.btn-block.btn-outline-info.mb-2(@click="btnKeysGrab") #[i.fa.mr-1.fa-key] Grab keys from Mifare Dump
button.btn.btn-sm.btn-block.btn-outline-secondary.mb-2(@click="btnKeysReset") #[i.fa.mr-1.fa-fire] Reset to well-known keys
.card-body.px-3.pt-3.pb-2.letter-spacing-n1px.border-top
h6.card-title UID (Chinese Magic Card gen1a)
@@ -94,13 +94,13 @@ block content
.col.px-1: button.btn.btn-block.btn-outline-success(@click="btnGen1aRead") #[i.fa.fa-fw.fa-upload] Read
.col.px-1: button.btn.btn-block.btn-outline-primary(@click="btnGen1aWrite") #[i.fa.fa-fw.fa-download] Write
.card.shadow-sm.mb-2
- h6.card-header.bg-light #[i.fa.fa-id-card.mr-1] Mifare Data
+ h6.card-header.bg-light #[i.fa.fa-id-card.mr-1] Mifare Dump
.card-body.px-3.pt-3.pb-2.letter-spacing-n1px
input.d-none(type="file", ref="cardImport", @change="cardImport?.cb?.($event.target.files[0])")
.row.mx-n1.mb-2
.col.px-1: button.btn.btn-block.btn-outline-success(@click="btnCardImport") #[i.fa.fa-fw.fa-file-code-o] Import
.col.px-1: button.btn.btn-block.btn-outline-primary(@click="btnCardExport") #[i.fa.fa-fw.fa-floppy-o] Export
- button.btn.btn-sm.btn-block.btn-outline-danger.mb-2(@click="btnCardReset") #[i.fa.mr-1.fa-repeat] Reset to empty mifare
+ button.btn.btn-sm.btn-block.btn-outline-danger.mb-2(@click="btnCardReset") #[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
.input-group.input-group-sm.mb-2.was-validated
@@ -120,7 +120,7 @@ block content
input.form-control(pattern="([\\dA-Fa-f]{2})*", placeholder="Hex format of ATS", v-model="ss.ats")
.input-group-append: button.btn.btn-outline-secondary(type="button", @click="ss.ats = ''") #[i.fa.fa-fw.fa-times]
.card-body.px-3.pt-3.pb-2.letter-spacing-n1px.border-top
- h6.card-title Mifare 1k Card Data
+ h6.card-title Mifare 1k Card Dump
.input-group.input-group-sm.mb-2(v-for="i in _.times(16)")
.input-group-prepend: label.input-group-text.justify-content-center.flex-column(style="width: 2rem", :for="`i-toggle-${i}`")
input.my-2(type="checkbox", v-model="ss.toggle[i]", :id="`i-toggle-${i}`")
diff --git a/src/ChameleonUltra.test.ts b/src/ChameleonUltra.test.ts
index 4f6b241..08552d4 100644
--- a/src/ChameleonUltra.test.ts
+++ b/src/ChameleonUltra.test.ts
@@ -1,9 +1,8 @@
+import _ from 'lodash'
import { Buffer } from './buffer'
+import { ChameleonUltra } from './ChameleonUltra'
import BufferMockAdapter from './plugin/BufferMockAdapter'
import {
- ChameleonUltra,
-
- // enum
AnimationMode,
ButtonAction,
ButtonType,
@@ -17,7 +16,7 @@ import {
Mf1VblockOperator,
Slot,
TagType,
-} from './ChameleonUltra'
+} from './enums'
describe('ChameleonUltra with BufferMockAdapter', () => {
let ultra: ChameleonUltra
@@ -1134,7 +1133,7 @@ describe('ChameleonUltra with BufferMockAdapter', () => {
test('#mf1CheckSectorKeys()', async () => {
// arrange
adapter.send.push(Buffer.fromHexString('11ef 07d7 0000 0000 22 00'))
- adapter.send.push(Buffer.fromHexString('11ef 07d7 0000 0000 22 00'))
+ adapter.send.push(Buffer.fromHexString('11ef 07d8 0000 0010 11 000000000000ff078069ffffffffffff 17'))
// act
const keys = Buffer.from('FFFFFFFFFFFF\n000000000000\nA0A1A2A3A4A5\nD3F7D3F7D3F7', 'hex').chunk(6)
@@ -1146,15 +1145,15 @@ describe('ChameleonUltra with BufferMockAdapter', () => {
[Mf1KeyType.KEY_B]: Buffer.fromHexString('FFFFFFFFFFFF'),
})
expect(adapter.recv).toEqual([
- Buffer.fromHexString('11ef 07d7 0000 0008 1a 6103ffffffffffff a2'),
Buffer.fromHexString('11ef 07d7 0000 0008 1a 6003ffffffffffff a3'),
+ Buffer.fromHexString('11ef 07d8 0000 0008 19 6003ffffffffffff a3'),
])
})
test('#mf1ReadSectorByKeys()', async () => {
// arrange
adapter.send.push(Buffer.fromHexString('11ef 07d7 0000 0000 22 00'))
- adapter.send.push(Buffer.fromHexString('11ef 07d7 0000 0000 22 00'))
+ adapter.send.push(Buffer.fromHexString('11ef 07d8 0000 0010 11 000000000000ff078069ffffffffffff 17'))
adapter.send.push(Buffer.fromHexString('11ef 07d8 0000 0010 11 877209e11d0804000392abdef258ec90 10'))
adapter.send.push(Buffer.fromHexString('11ef 07d8 0000 0010 11 00000000000000000000000000000000 00'))
adapter.send.push(Buffer.fromHexString('11ef 07d8 0000 0010 11 00000000000000000000000000000000 00'))
@@ -1175,8 +1174,8 @@ describe('ChameleonUltra with BufferMockAdapter', () => {
success: [true, true, true, true],
})
expect(adapter.recv).toEqual([
- Buffer.fromHexString('11ef 07d7 0000 0008 1a 6103ffffffffffff a2'),
Buffer.fromHexString('11ef 07d7 0000 0008 1a 6003ffffffffffff a3'),
+ Buffer.fromHexString('11ef 07d8 0000 0008 19 6003ffffffffffff a3'),
Buffer.fromHexString('11ef 07d8 0000 0008 19 6100ffffffffffff a5'),
Buffer.fromHexString('11ef 07d8 0000 0008 19 6101ffffffffffff a4'),
Buffer.fromHexString('11ef 07d8 0000 0008 19 6102ffffffffffff a3'),
@@ -1187,7 +1186,7 @@ describe('ChameleonUltra with BufferMockAdapter', () => {
test('#mf1WriteSectorByKeys()', async () => {
// arrange
adapter.send.push(Buffer.fromHexString('11ef 07d7 0000 0000 22 00'))
- adapter.send.push(Buffer.fromHexString('11ef 07d7 0000 0000 22 00'))
+ adapter.send.push(Buffer.fromHexString('11ef 07d8 0000 0010 11 000000000000ff078069ffffffffffff 17'))
adapter.send.push(Buffer.fromHexString('11ef 07d9 0000 0000 20 00'))
adapter.send.push(Buffer.fromHexString('11ef 07d9 0000 0000 20 00'))
adapter.send.push(Buffer.fromHexString('11ef 07d9 0000 0000 20 00'))
@@ -1206,8 +1205,8 @@ describe('ChameleonUltra with BufferMockAdapter', () => {
// assert
expect(actual).toEqual({ success: [true, true, true, true] })
expect(adapter.recv).toEqual([
- Buffer.fromHexString('11ef 07d7 0000 0008 1a 6107ffffffffffff 9e'),
Buffer.fromHexString('11ef 07d7 0000 0008 1a 6007ffffffffffff 9f'),
+ Buffer.fromHexString('11ef 07d8 0000 0008 19 6007ffffffffffff 9f'),
Buffer.fromHexString('11ef 07d9 0000 0018 08 6104ffffffffffff00000000000000000000000000000000 a1'),
Buffer.fromHexString('11ef 07d9 0000 0018 08 6105ffffffffffff00000000000000000000000000000000 a0'),
Buffer.fromHexString('11ef 07d9 0000 0018 08 6106ffffffffffff00000000000000000000000000000000 9f'),
@@ -1229,3 +1228,17 @@ describe('ChameleonUltra with BufferMockAdapter', () => {
expect(actual).toBe(expected)
})
})
+
+test('.mf1TrailerBlockNoOfSector()', async () => {
+ const actual = _.times(40, ChameleonUltra.mf1TrailerBlockNoOfSector)
+ expect(actual).toEqual([
+ // mifare classic 1k
+ 3, 7, 11, 15, 19, 23, 27, 31,
+ 35, 39, 43, 47, 51, 55, 59, 63,
+ // mifare classic 2k
+ 67, 71, 75, 79, 83, 87, 91, 95,
+ 99, 103, 107, 111, 115, 119, 123, 127,
+ // mifare classic 4k
+ 143, 159, 175, 191, 207, 223, 239, 255,
+ ])
+})
diff --git a/src/ChameleonUltra.ts b/src/ChameleonUltra.ts
index f9b93de..0d1b407 100644
--- a/src/ChameleonUltra.ts
+++ b/src/ChameleonUltra.ts
@@ -1,16 +1,48 @@
import _ from 'lodash'
import { Buffer } from './buffer'
-import { createIsEnum, createIsEnumInteger, errToJson, middlewareCompose, sleep, type MiddlewareComposeFn, versionCompare } from './helper'
import { debug as createDebugger, type Debugger } from 'debug'
+import { errToJson, middlewareCompose, sleep, type MiddlewareComposeFn, versionCompare } from './helper'
import { type ReadableStream, type UnderlyingSink, WritableStream } from 'node:stream/web'
import * as Decoder from './ResponseDecoder'
+import {
+ Cmd,
+ Mf1KeyType,
+ RespStatus,
+ type AnimationMode,
+ type ButtonAction,
+ type ButtonType,
+ type DeviceMode,
+ type DeviceModel,
+ type FreqType,
+ type Mf1EmuWriteMode,
+ type Mf1PrngType,
+ type Mf1VblockOperator,
+ type Slot,
+ type TagType,
+
+ isAnimationMode,
+ isButtonAction,
+ isButtonType,
+ isDeviceMode,
+ isMf1EmuWriteMode,
+ isMf1KeyType,
+ isMf1VblockOperator,
+ isSlot,
+ isTagType,
+ isValidFreqType,
+} from './enums'
+
const READ_DEFAULT_TIMEOUT = 5e3
const START_OF_FRAME = new Buffer(2).writeUInt16BE(0x11EF)
const VERSION_SUPPORTED = { gte: '2.0', lt: '3.0' } as const
+function isMf1BlockNo (block: any): boolean {
+ return _.isInteger(block) && block >= 0 && block <= 0xFF
+}
+
function validateMf1BlockKey (block: any, keyType: any, key: any, prefix: string = ''): void {
- if (!_.isSafeInteger(block)) throw new TypeError(`${prefix}block should be a integer`)
+ if (!isMf1BlockNo(block)) throw new TypeError(`${prefix}block should be a integer`)
if (!isMf1KeyType(keyType)) throw new TypeError(`${prefix}keyType should be a Mf1KeyType`)
if (!Buffer.isBuffer(key) || key.length !== 6) throw new TypeError(`${prefix}key should be a Buffer(6)`)
}
@@ -84,7 +116,7 @@ export class ChameleonUltra {
*
*
*