Class: Mifare::DESFire

Inherits:
ISO144434 show all
Defined in:
lib/mifare/des_fire.rb

Defined Under Namespace

Classes: CARD_VERSION, FILE_PERMISSION, FILE_SETTING, KEY_SETTING

Constant Summary collapse

CMD_DES_AUTH =

Security Related Commands

0x1A
CMD_AES_AUTH =

Authenticate with DES, 2K3DES, 3K3DES key

0xAA
CMD_GET_KEY_SETTING =

Authenticate with AES-128 key

0x45
CMD_CHANGE_KEY_SETTING =

Gets information on the PICC and application master key settings. In addition it returns the maximum number of keys which can be stored within the selected application.

0x54
CMD_GET_KEY_VERSION =

Changes the master key settings on PICC and application level.

0x64
CMD_CHANGE_KEY =

Reads out the current key version of any key stored on the PICC.

0xC4
CMD_CREATE_APP =

PICC Level Commands

0xCA
CMD_DELETE_APP =

Creates new applications on the PICC.

0xDA
CMD_GET_APP_IDS =

Permanently deactivates applications on the PICC.

0x6A
CMD_SELECT_APP =

Returns the Application IDentifiers of all applications on a PICC.

0x5A
CMD_GET_CARD_VERSION =

Selects one specific application for further access.

0x60
CMD_FORMAT_CARD =

Returns manufacturing related data of the PICC.

0xFC
CMD_GET_FILE_IDS =

Application Level Commands

0x6F
CMD_GET_FILE_SETTING =

Returns the File IDentifiers of all active files within the currently selected application.

0xF5
CMD_CHANGE_FILE_SETTING =

Get information on the properties of a specific file.

0x5F
CMD_CREATE_STD_DATA_FILE =

Changes the access parameters of an existing file.

0xCD
CMD_CREATE_BACKUP_DATA_FILE =

Creates files for the storage of plain unformatted user data within an existing application on the PICC.

0xCB
CMD_CREATE_VALUE_FILE =

Creates files for the storage of plain unformatted user data within an existing application on the PICC, additionally supporting the feature of an integrated backup mechanism.

0xCC
CMD_CREATE_LINEAR_RECORD_FILE =

Creates files for the storage and manipulation of 32bit signed integer values within an existing application on the PICC.

0xC1
CMD_CREATE_CYCLIC_RECORD_FILE =

Creates files for multiple storage of structural similar data, for example for loyalty programs, within an existing application on the PICC. Once the file is filled completely with data records, further writing to the file is not possible unless it is cleared.

0xC0
CMD_DELETE_FILE =

Creates files for multiple storage of structural similar data within an existing application on the PICC. Once the file is filled completely with data records, the PICC automatically overwrites the oldest record with the latest written one.

0xDF
CMD_READ_DATA =

Data Manipulation Commands

0xBD
CMD_WRITE_DATA =

Reads data from Standard Data Files or Backup Data Files.

0x3D
CMD_GET_VALUE =

Writes data to Standard Data Files or Backup Data Files.

0x6C
CMD_CREDIT =

Reads the currently stored value from Value Files.

0x0C
CMD_DEBIT =

Increases a value stored in a Value File.

0xDC
CMD_LIMITED_CREDIT =

Decreases a value stored in a Value File.

0x1C
CMD_WRITE_RECORD =

Allows a limited increase of a value stored in a Value File without having full Credit permissions to the file.

0x3B
CMD_READ_RECORDS =

Writes data to a record in a Cyclic or Linear Record File.

0xBB
CMD_CLEAR_RECORD_FILE =

Reads out a set of complete records from a Cyclic or Linear Record File.

0xEB
CMD_COMMIT_TRANSACTION =

Resets a Cyclic or Linear Record File to empty state.

0xC7
CMD_ABORT_TRANSACTION =

Validates all previous write access’ on Backup Data Files, Value Files and Record Files within one application.

0xA7
ST_SUCCESS =

Status code returned by DESFire

0x00
ST_NO_CHANGES =
0x0C
ST_OUT_OF_MEMORY =
0x0E
ST_ILLEGAL_COMMAND =
0x1C
ST_INTEGRITY_ERROR =
0x1E
ST_KEY_NOT_EXIST =
0x40
ST_WRONG_COMMAND_LEN =
0x7E
ST_PERMISSION_DENIED =
0x9D
ST_INCORRECT_PARAM =
0x9E
ST_APP_NOT_FOUND =
0xA0
ST_APPL_INTEGRITY_ERROR =
0xA1
ST_AUTHENTICATION_ERROR =
0xAE
ST_ADDITIONAL_FRAME =
0xAF
ST_BOUNDARY_ERROR =
0xBE
ST_PICC_INTEGRITY_ERROR =
0xC1
ST_COMMAND_ABORTED =
0xCA
ST_PICC_DISABLED_ERROR =
0xCD
ST_COUNT_ERROR =
0xCE
ST_DUPLICATE_ERROR =
0xDE
ST_EEPROM_ERROR =
0xEE
ST_FILE_NOT_FOUND =
0xF0
ST_FILE_INTEGRITY_ERROR =
0xF1
KEY_TYPE =
{'des-ede-cbc' => 0x00, 'des-ede3-cbc' => 0x40, 'aes-128-cbc' => 0x80}
FILE_TYPE =
{
  std_data_file: 0x00, backup_data_file: 0x01, value_file: 0x02,
  linear_record_file: 0x03, cyclic_record_file: 0x04
}
FILE_COMMUNICATION =
{plain: 0x00, mac: 0x01, encrypt: 0x03}

Constants inherited from ISO144434

ISO144434::CMD_ADDITIONAL_FRAME, ISO144434::CMD_DESELECT, ISO144434::CMD_PPS, ISO144434::CMD_RATS, ISO144434::CMD_SUCCESS, ISO144434::FSCI_to_FSC

Instance Attribute Summary collapse

Attributes inherited from PICC

#sak, #uid

Instance Method Summary collapse

Methods inherited from ISO144434

#halt, #resume_communication, #select

Methods inherited from PICC

#halt, #resume_communication

Constructor Details

#initialize(pcd, uid, sak) ⇒ DESFire

Returns a new instance of DESFire.



160
161
162
163
164
165
# File 'lib/mifare/des_fire.rb', line 160

def initialize(pcd, uid, sak)
  super
  invalid_auth
  @cmac_buffer = []
  @selected_app = false
end

Instance Attribute Details

#selected_appObject (readonly)

Returns the value of attribute selected_app.



158
159
160
# File 'lib/mifare/des_fire.rb', line 158

def selected_app
  @selected_app
end

Instance Method Details

#abort_transactionObject



579
580
581
# File 'lib/mifare/des_fire.rb', line 579

def abort_transaction
  transceive(cmd: CMD_ABORT_TRANSACTION, tx: :cmac, rx: :cmac, expect: ST_SUCCESS)
end

#app_exist?(id) ⇒ Boolean

Returns:

  • (Boolean)


327
328
329
# File 'lib/mifare/des_fire.rb', line 327

def app_exist?(id)
  get_app_ids.include?(id)
end

#auth(key_number, auth_key) ⇒ Object



267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
# File 'lib/mifare/des_fire.rb', line 267

def auth(key_number, auth_key)
  cmd = (auth_key.type == :des) ? CMD_DES_AUTH : CMD_AES_AUTH
  auth_key.clear_iv

  # Ask for authentication
  received_data = transceive(cmd: cmd, data: key_number, expect: ST_ADDITIONAL_FRAME)

  # Receive challenge from DESFire
  challenge = auth_key.decrypt(received_data)
  challenge_rot = challenge.rotate

  # Generate random number and encrypt it with rotated challenge
  random_number = SecureRandom.random_bytes(received_data.size).bytes
  response = auth_key.encrypt(random_number + challenge_rot)

  # Send challenge response
  received_data = transceive(cmd: CMD_ADDITIONAL_FRAME, data: response, expect: ST_SUCCESS)

  # Check if verification matches rotated random_number
  verification = auth_key.decrypt(received_data)

  if random_number.rotate != verification
    halt
    @authed = false

    raise ReceiptIntegrityError, 'Authentication Failed'
  end

  # Generate session key from generated random number(RndA) and challenge(RndB)
  session_key = random_number[0..3] + challenge[0..3]

  if auth_key.key_size > 8
    if auth_key.cipher_suite == 'des-ede-cbc'
      session_key.concat(random_number[4..7] + challenge[4..7])
    elsif auth_key.cipher_suite == 'des-ede3-cbc'
      session_key.concat(random_number[6..9] + challenge[6..9])
      session_key.concat(random_number[12..15] + challenge[12..15])
    elsif auth_key.cipher_suite == 'aes-128-cbc'
      session_key.concat(random_number[12..15] + challenge[12..15])
    end
  end

  @session_key = Key.new(auth_key.type, session_key)
  @session_key.generate_cmac_subkeys
  @authed = key_number

  authed?
end

#authed?Boolean

Returns:

  • (Boolean)


167
168
169
# File 'lib/mifare/des_fire.rb', line 167

def authed?
  @authed.is_a? Numeric
end

#change_file_setting(id, file_setting) ⇒ Object



458
459
460
461
462
463
464
# File 'lib/mifare/des_fire.rb', line 458

def change_file_setting(id, file_setting)
  buffer = []
  buffer.append_uint(FILE_COMMUNICATION.fetch(file_setting.communication), 1)
  buffer.append_uint(file_setting.permission.to_uint, 2)

  transceive(cmd: CMD_CHANGE_FILE_SETTING, plain_data: id, data: buffer, tx: :encrypt, rx: :cmac, expect: ST_SUCCESS)
end

#change_key(key_number, new_key, curr_key = nil) ⇒ Object



375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
# File 'lib/mifare/des_fire.rb', line 375

def change_key(key_number, new_key, curr_key = nil)
  raise UnauthenticatedError unless @authed
  raise UnexpectedDataError, 'Invalid key number' if key_number > 13
  
  cryptogram = new_key.key

  same_key = (key_number == @authed)

  # Only Master Key can change its key type
  key_number |= KEY_TYPE.fetch(new_key.cipher_suite) if @selected_app == 0

  # XOR new key if we're using different one
  unless same_key
    cryptogram = cryptogram.zip(curr_key.key).map{|x, y| x ^ y }
  end

  # AES stores key version separately
  if new_key.type == :aes
    cryptogram.append_uint(new_key.version, 1)
  end

  cryptogram.append_uint(crc32([CMD_CHANGE_KEY, key_number], cryptogram), 4)

  unless same_key
    cryptogram.append_uint(crc32(new_key.key), 4)
  end

  # Encrypt cryptogram
  buffer = [key_number] + @session_key.encrypt(cryptogram)

  # Change current used key will revoke authentication
  invalid_auth if same_key

  transceive(cmd: CMD_CHANGE_KEY, data: buffer, rx: :cmac, expect: ST_SUCCESS)
end

#change_key_setting(key_setting) ⇒ Object



419
420
421
422
423
# File 'lib/mifare/des_fire.rb', line 419

def change_key_setting(key_setting)
  raise UnauthenticatedError unless @authed

  transceive(cmd: CMD_CHANGE_KEY_SETTING, data: key_setting.to_uint, tx: :encrypt, rx: :cmac, expect: ST_SUCCESS)
end

#clear_record(id) ⇒ Object



571
572
573
# File 'lib/mifare/des_fire.rb', line 571

def clear_record(id)
  transceive(cmd: CMD_CLEAR_RECORD_FILE, data: id, tx: :cmac, rx: :cmac, expect: ST_SUCCESS)
end

#commit_transactionObject



575
576
577
# File 'lib/mifare/des_fire.rb', line 575

def commit_transaction
  transceive(cmd: CMD_COMMIT_TRANSACTION, tx: :cmac, rx: :cmac, expect: ST_SUCCESS)
end

#create_app(id, key_setting, key_count, cipher_suite) ⇒ Object



338
339
340
341
342
343
344
345
# File 'lib/mifare/des_fire.rb', line 338

def create_app(id, key_setting, key_count, cipher_suite)
  raise UnauthenticatedError unless @authed
  raise UnexpectedDataError, 'An application can only hold up to 14 keys.' if key_count > 14

  buffer = convert_app_id(id) + [key_setting.to_uint, KEY_TYPE.fetch(cipher_suite) | key_count]

  transceive(cmd: CMD_CREATE_APP, data: buffer, tx: :cmac, rx: :cmac, expect: ST_SUCCESS)
end

#create_file(id, file_setting) ⇒ Object



466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
# File 'lib/mifare/des_fire.rb', line 466

def create_file(id, file_setting)
  buffer = [id]
  buffer.append_uint(FILE_COMMUNICATION.fetch(file_setting.communication), 1)
  buffer.append_uint(file_setting.permission.to_uint, 2)

  case file_setting.type
  when :std_data_file, :backup_data_file
    buffer.append_uint(file_setting.size, 3) # PICC will allocate n * 32 bytes memory internally
  when :value_file
    buffer.append_sint(file_setting.lower_limit, 4)
    buffer.append_sint(file_setting.upper_limit, 4)
    buffer.append_sint(file_setting.limited_credit_value, 4)
    buffer.append_uint(file_setting.limited_credit, 1)
  when :linear_record_file, :cyclic_record_file
    buffer.append_uint(file_setting.record_size, 3)
    buffer.append_uint(file_setting.max_record_number, 3)
  end

  cmd = self.class.const_get("CMD_CREATE_#{file_setting.type.to_s.upcase}")

  transceive(cmd: cmd, data: buffer, tx: :cmac, rx: :cmac, expect: ST_SUCCESS)
end

#credit_value(id, delta) ⇒ Object



526
527
528
529
530
531
532
533
# File 'lib/mifare/des_fire.rb', line 526

def credit_value(id, delta)
  raise UnexpectedDataError, 'Negative number is not allowed.' if delta < 0

  buffer = []
  buffer.append_sint(delta, 4)

  write_file(id, CMD_CREDIT, id, buffer)
end

#debit_value(id, delta) ⇒ Object



535
536
537
538
539
540
541
542
# File 'lib/mifare/des_fire.rb', line 535

def debit_value(id, delta)
  raise UnexpectedDataError, 'Negative number is not allowed.' if delta < 0

  buffer = []
  buffer.append_sint(delta, 4)

  write_file(id, CMD_DEBIT, id, buffer)
end

#delete_app(id) ⇒ Object



347
348
349
350
351
# File 'lib/mifare/des_fire.rb', line 347

def delete_app(id)
  raise UnauthenticatedError unless @authed

  transceive(cmd: CMD_DELETE_APP, data: convert_app_id(id), tx: :cmac, rx: :cmac, expect: ST_SUCCESS)
end

#delete_file(id) ⇒ Object



489
490
491
# File 'lib/mifare/des_fire.rb', line 489

def delete_file(id)
  transceive(cmd: CMD_DELETE_FILE, data: id, tx: :cmac, rx: :cmac, expect: ST_SUCCESS)
end

#deselectObject



171
172
173
174
# File 'lib/mifare/des_fire.rb', line 171

def deselect
  super
  invalid_auth
end

#file_exist?(id) ⇒ Boolean

Returns:

  • (Boolean)


429
430
431
# File 'lib/mifare/des_fire.rb', line 429

def file_exist?(id)
  get_file_ids.include?(id)
end

#format_cardObject



363
364
365
366
367
# File 'lib/mifare/des_fire.rb', line 363

def format_card
  raise UnauthenticatedError unless @authed

  transceive(cmd: CMD_FORMAT_CARD, tx: :cmac, rx: :cmac, expect: ST_SUCCESS)
end

#get_app_idsObject



316
317
318
319
320
321
322
323
324
325
# File 'lib/mifare/des_fire.rb', line 316

def get_app_ids
  ids = transceive(cmd: CMD_GET_APP_IDS, tx: :cmac, rx: :cmac, expect: ST_SUCCESS, return_data: true, receive_all: true)

  return ids if ids.empty?

  ids = ids.each_slice(3).to_a
  ids.map do |id|
    id.to_uint
  end
end

#get_card_versionObject



353
354
355
356
357
358
359
360
361
# File 'lib/mifare/des_fire.rb', line 353

def get_card_version
  version = transceive(cmd: CMD_GET_CARD_VERSION, tx: :cmac, rx: :cmac, expect: ST_SUCCESS, receive_all: true)

  CARD_VERSION.new(
    version[0], version[1], version[2], version[3], version[4], 1 << (version[5] / 2), version[6],
    version[7], version[8], version[9], version[10], version[11], 1 << (version[12] / 2), version[13],
    version[14..20], version[21..25], version[26].to_s(16).to_i, 2000 + version[27].to_s(16).to_i
  )
end

#get_file_idsObject



425
426
427
# File 'lib/mifare/des_fire.rb', line 425

def get_file_ids
  transceive(cmd: CMD_GET_FILE_IDS, tx: :cmac, rx: :cmac, expect: ST_SUCCESS, return_data: true)
end

#get_file_setting(id) ⇒ Object



433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
# File 'lib/mifare/des_fire.rb', line 433

def get_file_setting(id)
  received_data = transceive(cmd: CMD_GET_FILE_SETTING, data: id, tx: :cmac, rx: :cmac, expect: ST_SUCCESS)

  file_setting = FILE_SETTING.new
  file_setting.type = FILE_TYPE.key(received_data.shift)
  file_setting.communication = FILE_COMMUNICATION.key(received_data.shift)
  file_setting.permission = FILE_PERMISSION.new.import(received_data.shift(2).to_uint)

  case file_setting.type
  when :std_data_file, :backup_data_file
    file_setting.size = received_data.shift(3).to_uint
  when :value_file
    file_setting.lower_limit = received_data.shift(4).to_uint
    file_setting.upper_limit = received_data.shift(4).to_uint
    file_setting.limited_credit_value = received_data.shift(4).to_uint
    file_setting.limited_credit = received_data.shift & 0x01
  when :linear_record_file, :cyclic_record_file
    file_setting.record_size = received_data.shift(3).to_uint
    file_setting.max_record_number = received_data.shift(3).to_uint
    file_setting.current_record_number = received_data.shift(3).to_uint
  end

  file_setting
end

#get_key_settingObject



411
412
413
414
415
416
417
# File 'lib/mifare/des_fire.rb', line 411

def get_key_setting
  received_data = transceive(cmd: CMD_GET_KEY_SETTING, tx: :cmac, rx: :cmac, expect: ST_SUCCESS)

  { key_setting: KEY_SETTING.new.import(received_data[0]),
    key_count: received_data[1] & 0x0F,
    key_type: KEY_TYPE.key(received_data[1] & 0xF0) }
end

#get_key_version(key_number) ⇒ Object



369
370
371
372
373
# File 'lib/mifare/des_fire.rb', line 369

def get_key_version(key_number)
  received_data = transceive(cmd: CMD_GET_KEY_VERSION, data: key_number, tx: :cmac, rx: :cmac, expect: ST_SUCCESS)

  received_data[0]
end

#limited_credit_value(id, delta) ⇒ Object



544
545
546
547
548
549
550
551
# File 'lib/mifare/des_fire.rb', line 544

def limited_credit_value(id, delta)
  raise UnexpectedDataError, 'Negative number is not allowed.' if delta < 0

  buffer = []
  buffer.append_sint(delta, 4)

  write_file(id, CMD_LIMITED_CREDIT, id, buffer)
end

#read_data(id, offset, length) ⇒ Object



504
505
506
507
508
509
510
511
# File 'lib/mifare/des_fire.rb', line 504

def read_data(id, offset, length)
  buffer = []
  buffer.append_uint(id, 1)
  buffer.append_uint(offset, 3)
  buffer.append_uint(length, 3)

  read_file(id, CMD_READ_DATA, buffer, length)
end

#read_file(id, cmd, data, length) ⇒ Object



493
494
495
496
497
# File 'lib/mifare/des_fire.rb', line 493

def read_file(id, cmd, data, length)
  file_setting = get_file_setting(id)
  length *= file_setting.record_size if file_setting.record_size
  transceive(cmd: cmd, data: data, tx: :cmac, rx: convert_file_communication(file_setting.communication), expect: ST_SUCCESS, receive_all: true, receive_length: length)
end

#read_records(id, offset, length) ⇒ Object



553
554
555
556
557
558
559
560
# File 'lib/mifare/des_fire.rb', line 553

def read_records(id, offset, length)
  buffer = []
  buffer.append_uint(id, 1)
  buffer.append_uint(offset, 3)
  buffer.append_uint(length, 3)

  read_file(id, CMD_READ_RECORDS, buffer, length)
end

#read_value(id) ⇒ Object



522
523
524
# File 'lib/mifare/des_fire.rb', line 522

def read_value(id)
  read_file(id, CMD_GET_VALUE, id, 4).to_sint
end

#select_app(id) ⇒ Object



331
332
333
334
335
336
# File 'lib/mifare/des_fire.rb', line 331

def select_app(id)
  transceive(cmd: CMD_SELECT_APP, data: convert_app_id(id), expect: ST_SUCCESS)

  invalid_auth
  @selected_app = id
end

#transceive(cmd:, plain_data: [], data: [], tx: nil, rx: nil, expect: nil, return_data: nil, receive_all: nil, receive_length: nil) ⇒ Object



176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
# File 'lib/mifare/des_fire.rb', line 176

def transceive(cmd: , plain_data: [], data: [], tx: nil, rx: nil, expect: nil, return_data: nil, receive_all: nil, receive_length: nil)
  # Session key is needed for encryption
  if (tx == :encrypt || rx == :encrypt) && !@authed
    raise UnauthenticatedError
  end

  # Separate objects and be compatable with single byte input
  plain_data = plain_data.is_a?(Array) ? plain_data.dup : [plain_data]
  data = data.is_a?(Array) ? data.dup : [data]

  buffer = [cmd] + plain_data

  if tx == :encrypt
    # Calculate CRC on whole frame
    data.append_uint(crc32(buffer, data), 4)
    # Encrypt data only
    data = @session_key.encrypt(data)
  end

  buffer.concat(data)

  if (tx == :cmac || tx == :send_cmac) && cmd != CMD_ADDITIONAL_FRAME && @authed
    @cmac_buffer = buffer
    cmac = @session_key.calculate_cmac(@cmac_buffer)
    # Only first 8 bytes of CMAC are transmitted
    buffer.concat(cmac[0..7]) if tx == :send_cmac
  end

  received_data = []
  card_status = nil
  loop do
    receive_buffer = super(buffer.shift(@max_inf_size))

    card_status = receive_buffer.shift
    received_data.concat(receive_buffer)

    break if card_status != ST_ADDITIONAL_FRAME || (buffer.empty? && !receive_all)

    buffer.unshift(CMD_ADDITIONAL_FRAME)
  end

  error_msg = check_status_code(card_status)

  unless error_msg.empty?
    invalid_auth
    raise ReceiptStatusError, "0x#{card_status.to_s(16).rjust(2, '0').upcase} - #{error_msg}"
  end

  if expect && expect != card_status
    raise UnexpectedDataError, 'Card status does not match expected value'
  end

  if (rx == :cmac || rx == :send_cmac) && (card_status == ST_SUCCESS || card_status == ST_ADDITIONAL_FRAME) && @authed
    @cmac_buffer = [] if cmd != CMD_ADDITIONAL_FRAME
    @cmac_buffer.concat(received_data) if card_status == ST_ADDITIONAL_FRAME

    if received_data.size >= 8 && card_status == ST_SUCCESS
      received_cmac = received_data.pop(8)
      @cmac_buffer.concat(received_data + [card_status])
      cmac = @session_key.calculate_cmac(@cmac_buffer)
      # Only first 8 bytes of CMAC are transmitted
      if cmac[0..7] != received_cmac
        raise ReceiptIntegrityError
      end
    end
  end

  if rx == :encrypt
    if receive_length.nil?
      raise UnexpectedDataError, 'Lack of receive length for removing padding'
    end
    received_data = @session_key.decrypt(received_data)
    received_data = remove_padding_bytes(received_data, receive_length)
    received_crc = received_data.pop(4).to_uint
    crc = crc32(received_data, card_status)
    if crc != received_crc
      raise ReceiptIntegrityError
    end
  end

  if expect
    if received_data.empty? && !return_data
      return true
    else
      return received_data
    end
  end

  return card_status, received_data
end

#write_data(id, offset, data) ⇒ Object



513
514
515
516
517
518
519
520
# File 'lib/mifare/des_fire.rb', line 513

def write_data(id, offset, data)
  buffer = []
  buffer.append_uint(id, 1)
  buffer.append_uint(offset, 3)
  buffer.append_uint(data.size, 3)

  write_file(id, CMD_WRITE_DATA, buffer, data)
end

#write_file(id, cmd, plain_data, data) ⇒ Object



499
500
501
502
# File 'lib/mifare/des_fire.rb', line 499

def write_file(id, cmd, plain_data, data)
  file_setting = get_file_setting(id)
  transceive(cmd: cmd, plain_data: plain_data, data: data, tx: convert_file_communication(file_setting.communication), rx: :cmac, expect: ST_SUCCESS)
end

#write_record(id, offset, data) ⇒ Object



562
563
564
565
566
567
568
569
# File 'lib/mifare/des_fire.rb', line 562

def write_record(id, offset, data)
  buffer = []
  buffer.append_uint(id, 1)
  buffer.append_uint(offset, 3)
  buffer.append_uint(data.size, 3)

  write_file(id, CMD_WRITE_RECORD, buffer, data)
end