• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

taichunmin / chameleon-ultra.js / 14702796126

28 Apr 2025 07:40AM UTC coverage: 63.737% (-0.4%) from 64.116%
14702796126

push

github

web-flow
v0.3.27: MF1_HARDNESTED_ACQUIRE (#186)

516 of 1188 branches covered (43.43%)

Branch coverage included in aggregate %.

3 of 15 new or added lines in 3 files covered. (20.0%)

1623 of 2168 relevant lines covered (74.86%)

11257218.75 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.65
/src/ResponseDecoder.ts
1
import { Buffer } from '@taichunmin/buffer'
4✔
2
import _ from 'lodash'
4✔
3
import { type Class } from 'utility-types'
4

5
import {
6
  type AnimationMode,
7
  type ButtonAction,
8
  type DarksideStatus,
9
  type Mf1EmuWriteMode,
10
  type Mf1PrngType,
11
  type TagType,
12
} from './enums'
13

14
function bufUnpackToClass <T> (buf: Buffer, format: string, Type: Class<T>): T {
15
  return new Type(...buf.unpack<ConstructorParameters<typeof Type>>(format))
60✔
16
}
17

18
export class SlotInfo {
4✔
19
  hfTagType: TagType
20
  lfTagType: TagType
21

22
  constructor (hf: TagType, lf: TagType) {
23
    this.hfTagType = hf
16✔
24
    this.lfTagType = lf
16✔
25
  }
26

27
  static fromCmd1019 (buf: Buffer): SlotInfo[] {
28
    bufIsLenOrFail(buf, 32, 'buf')
6✔
29
    return _.map(buf.chunk(4), chunk => bufUnpackToClass(chunk, '!HH', SlotInfo))
16✔
30
  }
31
}
32

33
export class SlotFreqIsEnable {
4✔
34
  hf: boolean
35
  lf: boolean
36

37
  constructor (hf: boolean, lf: boolean) {
38
    ;[this.hf, this.lf] = _.map([hf, lf], Boolean)
16✔
39
  }
40

41
  static fromCmd1023 (buf: Buffer): SlotFreqIsEnable[] {
42
    bufIsLenOrFail(buf, 16, 'buf')
6✔
43
    return _.map(buf.chunk(2), chunk => bufUnpackToClass(chunk, '!??', SlotFreqIsEnable))
16✔
44
  }
45
}
46

47
export class BatteryInfo {
4✔
48
  voltage: number
49
  level: number
50

51
  constructor (voltage: number, level: number) {
52
    ;[this.voltage, this.level] = [voltage, level]
2✔
53
  }
54

55
  static fromCmd1025 (buf: Buffer): BatteryInfo {
56
    bufIsLenOrFail(buf, 3, 'buf')
6✔
57
    return bufUnpackToClass(buf, '!HB', BatteryInfo)
2✔
58
  }
59
}
60

61
export class DeviceSettings {
4✔
62
  version: number // version of setting
63
  animation: AnimationMode
64
  buttonPressAction: ButtonAction[]
65
  buttonLongPressAction: ButtonAction[]
66
  blePairingMode: boolean
67
  blePairingKey: string
68

69
  constructor (
70
    version: number,
71
    animation: AnimationMode,
72
    btnPressA: ButtonAction,
73
    btnPressB: ButtonAction,
74
    btnLongPressA: ButtonAction,
75
    btnLongPressB: ButtonAction,
76
    blePairingMode: boolean,
77
    blePairingKey: string
78
  ) {
79
    this.version = version
2✔
80
    this.animation = animation
2✔
81
    this.buttonPressAction = [btnPressA, btnPressB]
2✔
82
    this.buttonLongPressAction = [btnLongPressA, btnLongPressB]
2✔
83
    this.blePairingMode = Boolean(blePairingMode)
2✔
84
    this.blePairingKey = blePairingKey
2✔
85
  }
86

87
  static fromCmd1034 (buf: Buffer): DeviceSettings {
88
    bufIsLenOrFail(buf, 13, 'buf')
6✔
89
    return bufUnpackToClass(buf, '!6B?6s', DeviceSettings)
2✔
90
  }
91
}
92

93
/**
94
 * Class for Hf14aAntiColl Decoding.
95
 */
96
export class Hf14aAntiColl {
4✔
97
  uid: Buffer
98
  atqa: Buffer
99
  sak: Buffer
100
  ats: Buffer
101

102
  constructor (uid: Buffer, atqa: Buffer, sak: Buffer, ats: Buffer) {
103
    ;[this.uid, this.atqa, this.sak, this.ats] = [uid, atqa, sak, ats]
6✔
104
  }
105

106
  static fromBuffer (buf: Buffer): Hf14aAntiColl {
107
    // uidlen[1]|uid[uidlen]|atqa[2]|sak[1]|atslen[1]|ats[atslen]
108
    const uidLen = buf[0]
10✔
109
    if (buf.length < uidLen + 4) throw new Error('invalid length of uid')
10✔
110
    const atsLen = buf[uidLen + 4]
8✔
111
    if (buf.length < uidLen + atsLen + 5) throw new Error('invalid length of ats')
8✔
112
    return bufUnpackToClass(buf, `!${uidLen + 1}p2ss${atsLen + 1}p`, Hf14aAntiColl)
6✔
113
  }
114

115
  static fromCmd2000 (buf: Buffer): Hf14aAntiColl[] {
116
    if (!Buffer.isBuffer(buf)) throw new TypeError('buf must be a Buffer')
6✔
117
    const tags: Hf14aAntiColl[] = []
4✔
118
    while (buf.length > 0) {
4✔
119
      const tag = Hf14aAntiColl.fromBuffer(buf)
4✔
120
      buf = buf.subarray(tag.uid.length + tag.ats.length + 5)
4✔
121
      tags.push(tag)
4✔
122
    }
123
    return tags
4✔
124
  }
125
}
126

127
export class Mf1AcquireStaticNestedRes {
4✔
128
  uid: Buffer
129
  atks: Array<{ nt1: Buffer, nt2: Buffer }>
130

131
  constructor (uid: Buffer, atks: Array<{ nt1: Buffer, nt2: Buffer }>) {
132
    ;[this.uid, this.atks] = [uid, atks]
2✔
133
  }
134

135
  static fromCmd2003 (buf: Buffer): Mf1AcquireStaticNestedRes {
136
    if (!Buffer.isBuffer(buf)) throw new TypeError('buf must be a Buffer')
4✔
137
    return new Mf1AcquireStaticNestedRes(
2✔
138
      buf.subarray(0, 4), // uid
139
      _.map(buf.subarray(4).chunk(8), chunk => ({
4✔
140
        nt1: chunk.subarray(0, 4),
141
        nt2: chunk.subarray(4, 8),
142
      })), // atks
143
    )
144
  }
145
}
146

147
export class Mf1DarksideRes {
4✔
148
  status: DarksideStatus
149
  uid?: Buffer
150
  nt?: Buffer
151
  par?: Buffer
152
  ks?: Buffer
153
  nr?: Buffer
154
  ar?: Buffer
155

156
  constructor (
157
    status: DarksideStatus,
158
    uid?: Buffer,
159
    nt?: Buffer,
160
    par?: Buffer,
161
    ks?: Buffer,
162
    nr?: Buffer,
163
    ar?: Buffer
164
  ) {
165
    this.status = status
4✔
166
    this.uid = uid
4✔
167
    this.nt = nt
4✔
168
    this.par = par
4✔
169
    this.ks = ks
4✔
170
    this.nr = nr
4✔
171
    this.ar = ar
4✔
172
  }
173

174
  static fromCmd2004 (buf: Buffer): Mf1DarksideRes {
175
    if (!Buffer.isBuffer(buf) || !_.includes([1, 33], buf.length)) throw new TypeError('buf must be a 1 or 33 bytes Buffer.')
8✔
176
    return bufUnpackToClass(buf, buf.length === 1 ? '!B' : '!B4s4s8s8s4s4s', Mf1DarksideRes)
4✔
177
  }
178
}
179

180
export class Mf1NtDistanceRes {
4✔
181
  uid: Buffer
182
  dist: Buffer
183

184
  constructor (uid: Buffer, dist: Buffer) {
185
    ;[this.uid, this.dist] = [uid, dist]
2✔
186
  }
187

188
  static fromCmd2005 (buf: Buffer): Mf1NtDistanceRes {
189
    bufIsLenOrFail(buf, 8, 'buf')
6✔
190
    return bufUnpackToClass(buf, '!4s4s', Mf1NtDistanceRes)
2✔
191
  }
192
}
193

194
/** Answer the random number parameters required for Nested attack */
195
export class Mf1NestedRes {
4✔
196
  nt1: number // Unblocked explicitly random number
197
  nt2: number // Random number of nested verification encryption
198
  par: number // The 3 parity bit of nested verification encryption
199

200
  constructor (nt1: number, nt2: number, par: number) {
201
    ;[this.nt1, this.nt2, this.par] = [nt1, nt2, par]
4✔
202
  }
203

204
  static fromCmd2006 (buf: Buffer): Mf1NestedRes[] {
205
    if (!Buffer.isBuffer(buf)) throw new TypeError('buf must be a Buffer.')
4✔
206
    return _.map(buf.chunk(9), chunk => bufUnpackToClass(chunk, '!IIB', Mf1NestedRes))
4✔
207
  }
208
}
209

210
export class Mf1CheckKeysOfSectorsRes {
4✔
211
  found: Buffer
212
  sectorKeys: Array<Buffer | null>
213

214
  constructor (found: Buffer, sectorKeys: Buffer[]) {
215
    this.found = found
6✔
216
    this.sectorKeys = _.times(80, i => found.readBitMSB(i) === 1 ? sectorKeys[i] : null)
480✔
217
  }
218

219
  static fromCmd2012 (buf: Buffer): Mf1CheckKeysOfSectorsRes {
220
    bufIsLenOrFail(buf, 490, 'buf')
10✔
221
    return new Mf1CheckKeysOfSectorsRes(
6✔
222
      buf.subarray(0, 10), // found
223
      buf.subarray(10).chunk(6), // sectorKeys
224
    )
225
  }
226
}
227

228
export interface Hf14aTagInfo {
229
  antiColl: Hf14aAntiColl
230
  nxpTypeBySak?: string
231
  prngType?: Mf1PrngType
232
}
233

234
export class Mf1DetectionLog {
4✔
235
  block: number
236
  isKeyB: boolean
237
  isNested: boolean
238
  uid: Buffer
239
  nt: Buffer
240
  nr: Buffer
241
  ar: Buffer
242

243
  constructor (
244
    block: number,
245
    flags: Buffer,
246
    uid: Buffer,
247
    nt: Buffer,
248
    nr: Buffer,
249
    ar: Buffer
250
  ) {
251
    this.block = block
6✔
252
    this.isKeyB = flags.readBitLSB(0) === 1
6✔
253
    this.isNested = flags.readBitLSB(1) === 1
6✔
254
    this.uid = uid
6✔
255
    this.nt = nt
6✔
256
    this.nr = nr
6✔
257
    this.ar = ar
6✔
258
  }
259

260
  static fromBuffer (buf: Buffer): Mf1DetectionLog {
261
    bufIsLenOrFail(buf, 18, 'buf')
10✔
262
    return bufUnpackToClass(buf, '!Bs4s4s4s4s', Mf1DetectionLog)
6✔
263
  }
264

265
  static fromCmd4006 (buf: Buffer): Mf1DetectionLog[] {
266
    if (!Buffer.isBuffer(buf)) throw new TypeError('buf must be a Buffer.')
4✔
267
    return _.map(buf.chunk(18), Mf1DetectionLog.fromBuffer)
2✔
268
  }
269
}
270

271
export class Mf1EmuSettings {
4✔
272
  detection: boolean
273
  gen1a: boolean
274
  gen2: boolean
275
  antiColl: boolean
276
  write: Mf1EmuWriteMode
277

278
  constructor (detection: boolean, gen1a: boolean, gen2: boolean, antiColl: boolean, write: Mf1EmuWriteMode) {
279
    this.detection = detection
2✔
280
    this.gen1a = gen1a
2✔
281
    this.gen2 = gen2
2✔
282
    this.antiColl = antiColl
2✔
283
    this.write = write
2✔
284
  }
285

286
  static fromCmd4009 (buf: Buffer): Mf1EmuSettings {
287
    bufIsLenOrFail(buf, 5, 'buf')
4✔
288
    return bufUnpackToClass(buf, '!4?B', Mf1EmuSettings)
2✔
289
  }
290
}
291

292
export interface Mf1EmuData {
293
  antiColl: Hf14aAntiColl
294
  settings: Mf1EmuSettings
295
  body: Buffer
296
}
297

298
export interface SlotSettings {
299
  config: { activated: number }
300
  group: Array<{
301
    hfIsEnable: boolean
302
    hfTagType: TagType
303
    lfIsEnable: boolean
304
    lfTagType: TagType
305
  }>
306
}
307

308
function bufIsLenOrFail (buf: Buffer, len: number, name: string): void {
309
  if (Buffer.isBuffer(buf) && buf.length === len) return
54✔
310
  throw new TypeError(`${name} must be a ${len} ${['byte', 'bytes'][+(len > 1)]} Buffer.`)
30✔
311
}
312

313
/** Answer the random number parameters required for Hard Nested attack */
314
export class Mf1AcquireHardNestedRes {
4✔
315
  nt: number // tag nonce of nested verification encryption
316
  ntEnc: number // encrypted tag nonce of nested verification encryption
317
  par: number // The 8 parity bit of nested verification encryption
318

319
  constructor (nt: number, ntEnc: number, par: number) {
NEW
320
    ;[this.nt, this.ntEnc, this.par] = [nt, ntEnc, par]
×
321
  }
322

323
  static fromCmd2013 (buf: Buffer): Mf1AcquireHardNestedRes[] {
NEW
324
    if (!Buffer.isBuffer(buf)) throw new TypeError('buf must be a Buffer.')
×
NEW
325
    return _.map(buf.chunk(9), chunk => bufUnpackToClass(chunk, '!IIB', Mf1AcquireHardNestedRes))
×
326
  }
327
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc